nostr-tools

2.20.02.21.0
~

Modified (124 files)

Index: package/lib/cjs/abstract-pool.js
===================================================================
--- package/lib/cjs/abstract-pool.js
+++ package/lib/cjs/abstract-pool.js
@@ -27,9 +27,9 @@
 // core.ts
 var verifiedSymbol = Symbol("verified");
 
 // utils.ts
-var import_utils = require("@noble/hashes/utils");
+var import_utils = require("@noble/hashes/utils.js");
 var utf8Decoder = new TextDecoder("utf-8");
 var utf8Encoder = new TextEncoder();
 function normalizeURL(url) {
   try {
Index: package/lib/esm/abstract-pool.js
===================================================================
--- package/lib/esm/abstract-pool.js
+++ package/lib/esm/abstract-pool.js
@@ -1,9 +1,9 @@
 // core.ts
 var verifiedSymbol = Symbol("verified");
 
 // utils.ts
-import { bytesToHex, hexToBytes } from "@noble/hashes/utils";
+import { bytesToHex, hexToBytes } from "@noble/hashes/utils.js";
 var utf8Decoder = new TextDecoder("utf-8");
 var utf8Encoder = new TextEncoder();
 function normalizeURL(url) {
   try {
Index: package/lib/cjs/abstract-relay.js
===================================================================
--- package/lib/cjs/abstract-relay.js
+++ package/lib/cjs/abstract-relay.js
@@ -26,9 +26,9 @@
 });
 module.exports = __toCommonJS(abstract_relay_exports);
 
 // utils.ts
-var import_utils = require("@noble/hashes/utils");
+var import_utils = require("@noble/hashes/utils.js");
 var utf8Decoder = new TextDecoder("utf-8");
 var utf8Encoder = new TextEncoder();
 function normalizeURL(url) {
   try {
Index: package/lib/esm/abstract-relay.js
===================================================================
--- package/lib/esm/abstract-relay.js
+++ package/lib/esm/abstract-relay.js
@@ -1,6 +1,6 @@
 // utils.ts
-import { bytesToHex, hexToBytes } from "@noble/hashes/utils";
+import { bytesToHex, hexToBytes } from "@noble/hashes/utils.js";
 var utf8Decoder = new TextDecoder("utf-8");
 var utf8Encoder = new TextEncoder();
 function normalizeURL(url) {
   try {
Index: package/lib/cjs/index.js
===================================================================
--- package/lib/cjs/index.js
+++ package/lib/cjs/index.js
@@ -64,10 +64,10 @@
 });
 module.exports = __toCommonJS(nostr_tools_exports);
 
 // pure.ts
-var import_secp256k1 = require("@noble/curves/secp256k1");
-var import_utils2 = require("@noble/hashes/utils");
+var import_secp256k1 = require("@noble/curves/secp256k1.js");
+var import_utils2 = require("@noble/hashes/utils.js");
 
 // core.ts
 var verifiedSymbol = Symbol("verified");
 var isRecord = (obj) => obj instanceof Object;
@@ -106,9 +106,9 @@
   });
 }
 
 // pure.ts
-var import_sha256 = require("@noble/hashes/sha256");
+var import_sha2 = require("@noble/hashes/sha2.js");
 
 // utils.ts
 var utils_exports = {};
 __export(utils_exports, {
@@ -122,9 +122,9 @@
   normalizeURL: () => normalizeURL,
   utf8Decoder: () => utf8Decoder,
   utf8Encoder: () => utf8Encoder
 });
-var import_utils = require("@noble/hashes/utils");
+var import_utils = require("@noble/hashes/utils.js");
 var utf8Decoder = new TextDecoder("utf-8");
 var utf8Encoder = new TextEncoder();
 function normalizeURL(url) {
   try {
@@ -241,18 +241,18 @@
 
 // pure.ts
 var JS = class {
   generateSecretKey() {
-    return import_secp256k1.schnorr.utils.randomPrivateKey();
+    return import_secp256k1.schnorr.utils.randomSecretKey();
   }
   getPublicKey(secretKey) {
     return (0, import_utils2.bytesToHex)(import_secp256k1.schnorr.getPublicKey(secretKey));
   }
   finalizeEvent(t, secretKey) {
     const event = t;
     event.pubkey = (0, import_utils2.bytesToHex)(import_secp256k1.schnorr.getPublicKey(secretKey));
     event.id = getEventHash(event);
-    event.sig = (0, import_utils2.bytesToHex)(import_secp256k1.schnorr.sign(getEventHash(event), secretKey));
+    event.sig = (0, import_utils2.bytesToHex)(import_secp256k1.schnorr.sign((0, import_utils2.hexToBytes)(getEventHash(event)), secretKey));
     event[verifiedSymbol] = true;
     return event;
   }
   verifyEvent(event) {
@@ -263,9 +263,9 @@
       event[verifiedSymbol] = false;
       return false;
     }
     try {
-      const valid = import_secp256k1.schnorr.verify(event.sig, hash, event.pubkey);
+      const valid = import_secp256k1.schnorr.verify((0, import_utils2.hexToBytes)(event.sig), (0, import_utils2.hexToBytes)(hash), (0, import_utils2.hexToBytes)(event.pubkey));
       event[verifiedSymbol] = valid;
       return valid;
     } catch (err) {
       event[verifiedSymbol] = false;
@@ -278,9 +278,9 @@
     throw new Error("can't serialize event with wrong or missing properties");
   return JSON.stringify([0, evt.pubkey, evt.created_at, evt.kind, evt.tags, evt.content]);
 }
 function getEventHash(event) {
-  let eventHash = (0, import_sha256.sha256)(utf8Encoder.encode(serializeEvent(event)));
+  let eventHash = (0, import_sha2.sha256)(utf8Encoder.encode(serializeEvent(event)));
   return (0, import_utils2.bytesToHex)(eventHash);
 }
 var i = new JS();
 var generateSecretKey = i.generateSecretKey;
@@ -1434,9 +1434,9 @@
   nprofileEncode: () => nprofileEncode,
   npubEncode: () => npubEncode,
   nsecEncode: () => nsecEncode
 });
-var import_utils6 = require("@noble/hashes/utils");
+var import_utils6 = require("@noble/hashes/utils.js");
 var import_base = require("@scure/base");
 var NostrTypeGuard = {
   isNProfile: (value) => /^nprofile1[a-z\d]+$/.test(value || ""),
   isNEvent: (value) => /^nevent1[a-z\d]+$/.test(value || ""),
@@ -1704,15 +1704,15 @@
 __export(nip04_exports, {
   decrypt: () => decrypt,
   encrypt: () => encrypt
 });
-var import_utils8 = require("@noble/hashes/utils");
-var import_secp256k12 = require("@noble/curves/secp256k1");
-var import_aes = require("@noble/ciphers/aes");
+var import_utils8 = require("@noble/hashes/utils.js");
+var import_secp256k12 = require("@noble/curves/secp256k1.js");
+var import_aes = require("@noble/ciphers/aes.js");
 var import_base2 = require("@scure/base");
 function encrypt(secretKey, pubkey, text) {
-  const privkey = secretKey instanceof Uint8Array ? (0, import_utils8.bytesToHex)(secretKey) : secretKey;
-  const key = import_secp256k12.secp256k1.getSharedSecret(privkey, "02" + pubkey);
+  const privkey = secretKey instanceof Uint8Array ? secretKey : (0, import_utils8.hexToBytes)(secretKey);
+  const key = import_secp256k12.secp256k1.getSharedSecret(privkey, (0, import_utils8.hexToBytes)("02" + pubkey));
   const normalizedKey = getNormalizedX(key);
   let iv = Uint8Array.from((0, import_utils8.randomBytes)(16));
   let plaintext = utf8Encoder.encode(text);
   let ciphertext = (0, import_aes.cbc)(normalizedKey, iv).encrypt(plaintext);
@@ -1720,11 +1720,11 @@
   let ivb64 = import_base2.base64.encode(new Uint8Array(iv.buffer));
   return `${ctb64}?iv=${ivb64}`;
 }
 function decrypt(secretKey, pubkey, data) {
-  const privkey = secretKey instanceof Uint8Array ? (0, import_utils8.bytesToHex)(secretKey) : secretKey;
+  const privkey = secretKey instanceof Uint8Array ? secretKey : (0, import_utils8.hexToBytes)(secretKey);
   let [ctb64, ivb64] = data.split("?iv=");
-  let key = import_secp256k12.secp256k1.getSharedSecret(privkey, "02" + pubkey);
+  let key = import_secp256k12.secp256k1.getSharedSecret(privkey, (0, import_utils8.hexToBytes)("02" + pubkey));
   let normalizedKey = getNormalizedX(key);
   let iv = import_base2.base64.decode(ivb64);
   let ciphertext = import_base2.base64.decode(ctb64);
   let plaintext = (0, import_aes.cbc)(normalizedKey, iv).decrypt(ciphertext);
@@ -1922,10 +1922,10 @@
   fastEventHash: () => fastEventHash,
   getPow: () => getPow,
   minePow: () => minePow
 });
-var import_utils10 = require("@noble/hashes/utils");
-var import_sha2562 = require("@noble/hashes/sha256");
+var import_utils10 = require("@noble/hashes/utils.js");
+var import_sha22 = require("@noble/hashes/sha2.js");
 function getPow(hex) {
   let count = 0;
   for (let i2 = 0; i2 < 64; i2 += 8) {
     const nibble = parseInt(hex.substring(i2, i2 + 8), 16);
@@ -1958,9 +1958,9 @@
   return event;
 }
 function fastEventHash(evt) {
   return (0, import_utils10.bytesToHex)(
-    (0, import_sha2562.sha256)(utf8Encoder.encode(JSON.stringify([0, evt.pubkey, evt.created_at, evt.kind, evt.tags, evt.content])))
+    (0, import_sha22.sha256)(utf8Encoder.encode(JSON.stringify([0, evt.pubkey, evt.created_at, evt.kind, evt.tags, evt.content])))
   );
 }
 
 // nip17.ts
@@ -1991,24 +1991,24 @@
   encrypt: () => encrypt2,
   getConversationKey: () => getConversationKey,
   v2: () => v2
 });
-var import_chacha = require("@noble/ciphers/chacha");
-var import_utils12 = require("@noble/ciphers/utils");
-var import_secp256k13 = require("@noble/curves/secp256k1");
-var import_hkdf = require("@noble/hashes/hkdf");
-var import_hmac = require("@noble/hashes/hmac");
-var import_sha2563 = require("@noble/hashes/sha256");
-var import_utils13 = require("@noble/hashes/utils");
+var import_chacha = require("@noble/ciphers/chacha.js");
+var import_utils12 = require("@noble/ciphers/utils.js");
+var import_secp256k13 = require("@noble/curves/secp256k1.js");
+var import_hkdf = require("@noble/hashes/hkdf.js");
+var import_hmac = require("@noble/hashes/hmac.js");
+var import_sha23 = require("@noble/hashes/sha2.js");
+var import_utils13 = require("@noble/hashes/utils.js");
 var import_base3 = require("@scure/base");
 var minPlaintextSize = 1;
 var maxPlaintextSize = 65535;
 function getConversationKey(privkeyA, pubkeyB) {
-  const sharedX = import_secp256k13.secp256k1.getSharedSecret(privkeyA, "02" + pubkeyB).subarray(1, 33);
-  return (0, import_hkdf.extract)(import_sha2563.sha256, sharedX, "nip44-v2");
+  const sharedX = import_secp256k13.secp256k1.getSharedSecret(privkeyA, (0, import_utils13.hexToBytes)("02" + pubkeyB)).subarray(1, 33);
+  return (0, import_hkdf.extract)(import_sha23.sha256, sharedX, utf8Encoder.encode("nip44-v2"));
 }
 function getMessageKeys(conversationKey, nonce) {
-  const keys = (0, import_hkdf.expand)(import_sha2563.sha256, conversationKey, nonce, 76);
+  const keys = (0, import_hkdf.expand)(import_sha23.sha256, conversationKey, nonce, 76);
   return {
     chacha_key: keys.subarray(0, 32),
     chacha_nonce: keys.subarray(32, 44),
     hmac_key: keys.subarray(44, 76)
@@ -2047,9 +2047,9 @@
 function hmacAad(key, message, aad) {
   if (aad.length !== 32)
     throw new Error("AAD associated data must be 32 bytes");
   const combined = (0, import_utils13.concatBytes)(aad, message);
-  return (0, import_hmac.hmac)(import_sha2563.sha256, key, combined);
+  return (0, import_hmac.hmac)(import_sha23.sha256, key, combined);
 }
 function decodePayload(payload) {
   if (typeof payload !== "string")
     throw new Error("payload must be a valid string");
@@ -2863,10 +2863,10 @@
   Negentropy: () => Negentropy,
   NegentropyStorageVector: () => NegentropyStorageVector,
   NegentropySync: () => NegentropySync
 });
-var import_utils16 = require("@noble/ciphers/utils");
-var import_sha2564 = require("@noble/hashes/sha256");
+var import_utils16 = require("@noble/hashes/utils.js");
+var import_sha24 = require("@noble/hashes/sha2.js");
 var PROTOCOL_VERSION = 97;
 var ID_SIZE = 32;
 var FINGERPRINT_SIZE = 16;
 var Mode = {
@@ -2995,9 +2995,9 @@
   getFingerprint(n) {
     let input = new WrappedBuffer();
     input.extend(this.buf);
     input.extend(encodeVarInt(n));
-    let hash = (0, import_sha2564.sha256)(input.unwrap());
+    let hash = (0, import_sha24.sha256)(input.unwrap());
     return hash.subarray(0, FINGERPRINT_SIZE);
   }
 };
 var NegentropyStorageVector = class {
@@ -3370,10 +3370,10 @@
   validateEventTimestamp: () => validateEventTimestamp,
   validateEventUrlTag: () => validateEventUrlTag,
   validateToken: () => validateToken
 });
-var import_sha2565 = require("@noble/hashes/sha256");
-var import_utils17 = require("@noble/hashes/utils");
+var import_sha25 = require("@noble/hashes/sha2.js");
+var import_utils17 = require("@noble/hashes/utils.js");
 var import_base5 = require("@scure/base");
 var _authorizationScheme = "Nostr ";
 async function getToken(loginUrl, httpMethod, sign, includeAuthorizationScheme = false, payload) {
   const event = {
@@ -3436,9 +3436,9 @@
   }
   return methodTag.length > 0 && methodTag[1].toLowerCase() === method.toLowerCase();
 }
 function hashPayload(payload) {
-  const hash = (0, import_sha2565.sha256)(utf8Encoder.encode(JSON.stringify(payload)));
+  const hash = (0, import_sha25.sha256)(utf8Encoder.encode(JSON.stringify(payload)));
   return (0, import_utils17.bytesToHex)(hash);
 }
 function validateEventPayloadTag(event, payload) {
   const payloadTag = event.tags.find((t) => t[0] === "payload");
Index: package/lib/esm/index.js
===================================================================
--- package/lib/esm/index.js
+++ package/lib/esm/index.js
@@ -4,10 +4,10 @@
     __defProp(target, name, { get: all[name], enumerable: true });
 };
 
 // pure.ts
-import { schnorr } from "@noble/curves/secp256k1";
-import { bytesToHex as bytesToHex2 } from "@noble/hashes/utils";
+import { schnorr } from "@noble/curves/secp256k1.js";
+import { bytesToHex as bytesToHex2, hexToBytes as hexToBytes2 } from "@noble/hashes/utils.js";
 
 // core.ts
 var verifiedSymbol = Symbol("verified");
 var isRecord = (obj) => obj instanceof Object;
@@ -46,9 +46,9 @@
   });
 }
 
 // pure.ts
-import { sha256 } from "@noble/hashes/sha256";
+import { sha256 } from "@noble/hashes/sha2.js";
 
 // utils.ts
 var utils_exports = {};
 __export(utils_exports, {
@@ -62,9 +62,9 @@
   normalizeURL: () => normalizeURL,
   utf8Decoder: () => utf8Decoder,
   utf8Encoder: () => utf8Encoder
 });
-import { bytesToHex, hexToBytes } from "@noble/hashes/utils";
+import { bytesToHex, hexToBytes } from "@noble/hashes/utils.js";
 var utf8Decoder = new TextDecoder("utf-8");
 var utf8Encoder = new TextEncoder();
 function normalizeURL(url) {
   try {
@@ -181,18 +181,18 @@
 
 // pure.ts
 var JS = class {
   generateSecretKey() {
-    return schnorr.utils.randomPrivateKey();
+    return schnorr.utils.randomSecretKey();
   }
   getPublicKey(secretKey) {
     return bytesToHex2(schnorr.getPublicKey(secretKey));
   }
   finalizeEvent(t, secretKey) {
     const event = t;
     event.pubkey = bytesToHex2(schnorr.getPublicKey(secretKey));
     event.id = getEventHash(event);
-    event.sig = bytesToHex2(schnorr.sign(getEventHash(event), secretKey));
+    event.sig = bytesToHex2(schnorr.sign(hexToBytes2(getEventHash(event)), secretKey));
     event[verifiedSymbol] = true;
     return event;
   }
   verifyEvent(event) {
@@ -203,9 +203,9 @@
       event[verifiedSymbol] = false;
       return false;
     }
     try {
-      const valid = schnorr.verify(event.sig, hash, event.pubkey);
+      const valid = schnorr.verify(hexToBytes2(event.sig), hexToBytes2(hash), hexToBytes2(event.pubkey));
       event[verifiedSymbol] = valid;
       return valid;
     } catch (err) {
       event[verifiedSymbol] = false;
@@ -1374,9 +1374,9 @@
   nprofileEncode: () => nprofileEncode,
   npubEncode: () => npubEncode,
   nsecEncode: () => nsecEncode
 });
-import { bytesToHex as bytesToHex3, concatBytes, hexToBytes as hexToBytes2 } from "@noble/hashes/utils";
+import { bytesToHex as bytesToHex3, concatBytes, hexToBytes as hexToBytes3 } from "@noble/hashes/utils.js";
 import { bech32 } from "@scure/base";
 var NostrTypeGuard = {
   isNProfile: (value) => /^nprofile1[a-z\d]+$/.test(value || ""),
   isNEvent: (value) => /^nevent1[a-z\d]+$/.test(value || ""),
@@ -1492,12 +1492,12 @@
 function nsecEncode(key) {
   return encodeBytes("nsec", key);
 }
 function npubEncode(hex) {
-  return encodeBytes("npub", hexToBytes2(hex));
+  return encodeBytes("npub", hexToBytes3(hex));
 }
 function noteEncode(hex) {
-  return encodeBytes("note", hexToBytes2(hex));
+  return encodeBytes("note", hexToBytes3(hex));
 }
 function encodeBech32(prefix, data) {
   let words = bech32.toWords(data);
   return bech32.encode(prefix, words, Bech32MaxSize);
@@ -1506,9 +1506,9 @@
   return encodeBech32(prefix, bytes);
 }
 function nprofileEncode(profile) {
   let data = encodeTLV({
-    0: [hexToBytes2(profile.pubkey)],
+    0: [hexToBytes3(profile.pubkey)],
     1: (profile.relays || []).map((url) => utf8Encoder.encode(url))
   });
   return encodeBech32("nprofile", data);
 }
@@ -1517,11 +1517,11 @@
   if (event.kind !== void 0) {
     kindArray = integerToUint8Array(event.kind);
   }
   let data = encodeTLV({
-    0: [hexToBytes2(event.id)],
+    0: [hexToBytes3(event.id)],
     1: (event.relays || []).map((url) => utf8Encoder.encode(url)),
-    2: event.author ? [hexToBytes2(event.author)] : [],
+    2: event.author ? [hexToBytes3(event.author)] : [],
     3: kindArray ? [new Uint8Array(kindArray)] : []
   });
   return encodeBech32("nevent", data);
 }
@@ -1530,9 +1530,9 @@
   new DataView(kind).setUint32(0, addr.kind, false);
   let data = encodeTLV({
     0: [utf8Encoder.encode(addr.identifier)],
     1: (addr.relays || []).map((url) => utf8Encoder.encode(url)),
-    2: [hexToBytes2(addr.pubkey)],
+    2: [hexToBytes3(addr.pubkey)],
     3: [new Uint8Array(kind)]
   });
   return encodeBech32("naddr", data);
 }
@@ -1644,15 +1644,15 @@
 __export(nip04_exports, {
   decrypt: () => decrypt,
   encrypt: () => encrypt
 });
-import { bytesToHex as bytesToHex4, randomBytes } from "@noble/hashes/utils";
-import { secp256k1 } from "@noble/curves/secp256k1";
-import { cbc } from "@noble/ciphers/aes";
+import { hexToBytes as hexToBytes4, randomBytes } from "@noble/hashes/utils.js";
+import { secp256k1 } from "@noble/curves/secp256k1.js";
+import { cbc } from "@noble/ciphers/aes.js";
 import { base64 } from "@scure/base";
 function encrypt(secretKey, pubkey, text) {
-  const privkey = secretKey instanceof Uint8Array ? bytesToHex4(secretKey) : secretKey;
-  const key = secp256k1.getSharedSecret(privkey, "02" + pubkey);
+  const privkey = secretKey instanceof Uint8Array ? secretKey : hexToBytes4(secretKey);
+  const key = secp256k1.getSharedSecret(privkey, hexToBytes4("02" + pubkey));
   const normalizedKey = getNormalizedX(key);
   let iv = Uint8Array.from(randomBytes(16));
   let plaintext = utf8Encoder.encode(text);
   let ciphertext = cbc(normalizedKey, iv).encrypt(plaintext);
@@ -1660,11 +1660,11 @@
   let ivb64 = base64.encode(new Uint8Array(iv.buffer));
   return `${ctb64}?iv=${ivb64}`;
 }
 function decrypt(secretKey, pubkey, data) {
-  const privkey = secretKey instanceof Uint8Array ? bytesToHex4(secretKey) : secretKey;
+  const privkey = secretKey instanceof Uint8Array ? secretKey : hexToBytes4(secretKey);
   let [ctb64, ivb64] = data.split("?iv=");
-  let key = secp256k1.getSharedSecret(privkey, "02" + pubkey);
+  let key = secp256k1.getSharedSecret(privkey, hexToBytes4("02" + pubkey));
   let normalizedKey = getNormalizedX(key);
   let iv = base64.decode(ivb64);
   let ciphertext = base64.decode(ctb64);
   let plaintext = cbc(normalizedKey, iv).decrypt(ciphertext);
@@ -1862,10 +1862,10 @@
   fastEventHash: () => fastEventHash,
   getPow: () => getPow,
   minePow: () => minePow
 });
-import { bytesToHex as bytesToHex5 } from "@noble/hashes/utils";
-import { sha256 as sha2562 } from "@noble/hashes/sha256";
+import { bytesToHex as bytesToHex4 } from "@noble/hashes/utils.js";
+import { sha256 as sha2562 } from "@noble/hashes/sha2.js";
 function getPow(hex) {
   let count = 0;
   for (let i2 = 0; i2 < 64; i2 += 8) {
     const nibble = parseInt(hex.substring(i2, i2 + 8), 16);
@@ -1897,9 +1897,9 @@
   }
   return event;
 }
 function fastEventHash(evt) {
-  return bytesToHex5(
+  return bytesToHex4(
     sha2562(utf8Encoder.encode(JSON.stringify([0, evt.pubkey, evt.created_at, evt.kind, evt.tags, evt.content])))
   );
 }
 
@@ -1931,21 +1931,21 @@
   encrypt: () => encrypt2,
   getConversationKey: () => getConversationKey,
   v2: () => v2
 });
-import { chacha20 } from "@noble/ciphers/chacha";
-import { equalBytes } from "@noble/ciphers/utils";
-import { secp256k1 as secp256k12 } from "@noble/curves/secp256k1";
-import { extract as hkdf_extract, expand as hkdf_expand } from "@noble/hashes/hkdf";
-import { hmac } from "@noble/hashes/hmac";
-import { sha256 as sha2563 } from "@noble/hashes/sha256";
-import { concatBytes as concatBytes2, randomBytes as randomBytes2 } from "@noble/hashes/utils";
+import { chacha20 } from "@noble/ciphers/chacha.js";
+import { equalBytes } from "@noble/ciphers/utils.js";
+import { secp256k1 as secp256k12 } from "@noble/curves/secp256k1.js";
+import { extract as hkdf_extract, expand as hkdf_expand } from "@noble/hashes/hkdf.js";
+import { hmac } from "@noble/hashes/hmac.js";
+import { sha256 as sha2563 } from "@noble/hashes/sha2.js";
+import { concatBytes as concatBytes2, hexToBytes as hexToBytes5, randomBytes as randomBytes2 } from "@noble/hashes/utils.js";
 import { base64 as base642 } from "@scure/base";
 var minPlaintextSize = 1;
 var maxPlaintextSize = 65535;
 function getConversationKey(privkeyA, pubkeyB) {
-  const sharedX = secp256k12.getSharedSecret(privkeyA, "02" + pubkeyB).subarray(1, 33);
-  return hkdf_extract(sha2563, sharedX, "nip44-v2");
+  const sharedX = secp256k12.getSharedSecret(privkeyA, hexToBytes5("02" + pubkeyB)).subarray(1, 33);
+  return hkdf_extract(sha2563, sharedX, utf8Encoder.encode("nip44-v2"));
 }
 function getMessageKeys(conversationKey, nonce) {
   const keys = hkdf_expand(sha2563, conversationKey, nonce, 76);
   return {
@@ -2803,10 +2803,10 @@
   Negentropy: () => Negentropy,
   NegentropyStorageVector: () => NegentropyStorageVector,
   NegentropySync: () => NegentropySync
 });
-import { bytesToHex as bytesToHex6, hexToBytes as hexToBytes3 } from "@noble/ciphers/utils";
-import { sha256 as sha2564 } from "@noble/hashes/sha256";
+import { bytesToHex as bytesToHex5, hexToBytes as hexToBytes6 } from "@noble/hashes/utils.js";
+import { sha256 as sha2564 } from "@noble/hashes/sha2.js";
 var PROTOCOL_VERSION = 97;
 var ID_SIZE = 32;
 var FINGERPRINT_SIZE = 16;
 var Mode = {
@@ -2949,9 +2949,9 @@
   }
   insert(timestamp, id) {
     if (this.sealed)
       throw Error("already sealed");
-    const idb = hexToBytes3(id);
+    const idb = hexToBytes6(id);
     if (idb.byteLength !== ID_SIZE)
       throw Error("bad id size for added item");
     this.items.push({ timestamp, id: idb });
   }
@@ -3043,12 +3043,12 @@
   initiate() {
     let output = new WrappedBuffer();
     output.extend(new Uint8Array([PROTOCOL_VERSION]));
     this.splitRange(0, this.storage.size(), this._bound(Number.MAX_VALUE), output);
-    return bytesToHex6(output.unwrap());
+    return bytesToHex5(output.unwrap());
   }
   reconcile(queryMsg, onhave, onneed) {
-    const query = new WrappedBuffer(hexToBytes3(queryMsg));
+    const query = new WrappedBuffer(hexToBytes6(queryMsg));
     this.lastTimestampIn = this.lastTimestampOut = 0;
     let fullOutput = new WrappedBuffer();
     fullOutput.extend(new Uint8Array([PROTOCOL_VERSION]));
     let protocolVersion = getByte(query);
@@ -3089,24 +3089,24 @@
         let numIds = decodeVarInt(query);
         let theirElems = {};
         for (let i2 = 0; i2 < numIds; i2++) {
           let e = getBytes(query, ID_SIZE);
-          theirElems[bytesToHex6(e)] = e;
+          theirElems[bytesToHex5(e)] = e;
         }
         skip = true;
         this.storage.iterate(lower, upper, (item) => {
           let k = item.id;
-          const id = bytesToHex6(k);
+          const id = bytesToHex5(k);
           if (!theirElems[id]) {
             onhave?.(id);
           } else {
-            delete theirElems[bytesToHex6(k)];
+            delete theirElems[bytesToHex5(k)];
           }
           return true;
         });
         if (onneed) {
           for (let v of Object.values(theirElems)) {
-            onneed(bytesToHex6(v));
+            onneed(bytesToHex5(v));
           }
         }
       } else {
         throw Error("unexpected mode");
@@ -3122,9 +3122,9 @@
       }
       prevIndex = upper;
       prevBound = currBound;
     }
-    return fullOutput.length === 1 ? null : bytesToHex6(fullOutput.unwrap());
+    return fullOutput.length === 1 ? null : bytesToHex5(fullOutput.unwrap());
   }
   splitRange(lower, upper, upperBound, o) {
     let numElems = upper - lower;
     let buckets = 16;
@@ -3310,10 +3310,10 @@
   validateEventTimestamp: () => validateEventTimestamp,
   validateEventUrlTag: () => validateEventUrlTag,
   validateToken: () => validateToken
 });
-import { sha256 as sha2565 } from "@noble/hashes/sha256";
-import { bytesToHex as bytesToHex7 } from "@noble/hashes/utils";
+import { sha256 as sha2565 } from "@noble/hashes/sha2.js";
+import { bytesToHex as bytesToHex6 } from "@noble/hashes/utils.js";
 import { base64 as base643 } from "@scure/base";
 var _authorizationScheme = "Nostr ";
 async function getToken(loginUrl, httpMethod, sign, includeAuthorizationScheme = false, payload) {
   const event = {
@@ -3377,9 +3377,9 @@
   return methodTag.length > 0 && methodTag[1].toLowerCase() === method.toLowerCase();
 }
 function hashPayload(payload) {
   const hash = sha2565(utf8Encoder.encode(JSON.stringify(payload)));
-  return bytesToHex7(hash);
+  return bytesToHex6(hash);
 }
 function validateEventPayloadTag(event, payload) {
   const payloadTag = event.tags.find((t) => t[0] === "payload");
   if (!payloadTag) {
Index: package/lib/cjs/nip04.js
===================================================================
--- package/lib/cjs/nip04.js
+++ package/lib/cjs/nip04.js
@@ -23,22 +23,22 @@
   decrypt: () => decrypt,
   encrypt: () => encrypt
 });
 module.exports = __toCommonJS(nip04_exports);
-var import_utils2 = require("@noble/hashes/utils");
-var import_secp256k1 = require("@noble/curves/secp256k1");
-var import_aes = require("@noble/ciphers/aes");
+var import_utils2 = require("@noble/hashes/utils.js");
+var import_secp256k1 = require("@noble/curves/secp256k1.js");
+var import_aes = require("@noble/ciphers/aes.js");
 var import_base = require("@scure/base");
 
 // utils.ts
-var import_utils = require("@noble/hashes/utils");
+var import_utils = require("@noble/hashes/utils.js");
 var utf8Decoder = new TextDecoder("utf-8");
 var utf8Encoder = new TextEncoder();
 
 // nip04.ts
 function encrypt(secretKey, pubkey, text) {
-  const privkey = secretKey instanceof Uint8Array ? (0, import_utils2.bytesToHex)(secretKey) : secretKey;
-  const key = import_secp256k1.secp256k1.getSharedSecret(privkey, "02" + pubkey);
+  const privkey = secretKey instanceof Uint8Array ? secretKey : (0, import_utils2.hexToBytes)(secretKey);
+  const key = import_secp256k1.secp256k1.getSharedSecret(privkey, (0, import_utils2.hexToBytes)("02" + pubkey));
   const normalizedKey = getNormalizedX(key);
   let iv = Uint8Array.from((0, import_utils2.randomBytes)(16));
   let plaintext = utf8Encoder.encode(text);
   let ciphertext = (0, import_aes.cbc)(normalizedKey, iv).encrypt(plaintext);
@@ -46,11 +46,11 @@
   let ivb64 = import_base.base64.encode(new Uint8Array(iv.buffer));
   return `${ctb64}?iv=${ivb64}`;
 }
 function decrypt(secretKey, pubkey, data) {
-  const privkey = secretKey instanceof Uint8Array ? (0, import_utils2.bytesToHex)(secretKey) : secretKey;
+  const privkey = secretKey instanceof Uint8Array ? secretKey : (0, import_utils2.hexToBytes)(secretKey);
   let [ctb64, ivb64] = data.split("?iv=");
-  let key = import_secp256k1.secp256k1.getSharedSecret(privkey, "02" + pubkey);
+  let key = import_secp256k1.secp256k1.getSharedSecret(privkey, (0, import_utils2.hexToBytes)("02" + pubkey));
   let normalizedKey = getNormalizedX(key);
   let iv = import_base.base64.decode(ivb64);
   let ciphertext = import_base.base64.decode(ctb64);
   let plaintext = (0, import_aes.cbc)(normalizedKey, iv).decrypt(ciphertext);
Index: package/lib/esm/nip04.js
===================================================================
--- package/lib/esm/nip04.js
+++ package/lib/esm/nip04.js
@@ -1,19 +1,19 @@
 // nip04.ts
-import { bytesToHex as bytesToHex2, randomBytes } from "@noble/hashes/utils";
-import { secp256k1 } from "@noble/curves/secp256k1";
-import { cbc } from "@noble/ciphers/aes";
+import { hexToBytes as hexToBytes2, randomBytes } from "@noble/hashes/utils.js";
+import { secp256k1 } from "@noble/curves/secp256k1.js";
+import { cbc } from "@noble/ciphers/aes.js";
 import { base64 } from "@scure/base";
 
 // utils.ts
-import { bytesToHex, hexToBytes } from "@noble/hashes/utils";
+import { bytesToHex, hexToBytes } from "@noble/hashes/utils.js";
 var utf8Decoder = new TextDecoder("utf-8");
 var utf8Encoder = new TextEncoder();
 
 // nip04.ts
 function encrypt(secretKey, pubkey, text) {
-  const privkey = secretKey instanceof Uint8Array ? bytesToHex2(secretKey) : secretKey;
-  const key = secp256k1.getSharedSecret(privkey, "02" + pubkey);
+  const privkey = secretKey instanceof Uint8Array ? secretKey : hexToBytes2(secretKey);
+  const key = secp256k1.getSharedSecret(privkey, hexToBytes2("02" + pubkey));
   const normalizedKey = getNormalizedX(key);
   let iv = Uint8Array.from(randomBytes(16));
   let plaintext = utf8Encoder.encode(text);
   let ciphertext = cbc(normalizedKey, iv).encrypt(plaintext);
@@ -21,11 +21,11 @@
   let ivb64 = base64.encode(new Uint8Array(iv.buffer));
   return `${ctb64}?iv=${ivb64}`;
 }
 function decrypt(secretKey, pubkey, data) {
-  const privkey = secretKey instanceof Uint8Array ? bytesToHex2(secretKey) : secretKey;
+  const privkey = secretKey instanceof Uint8Array ? secretKey : hexToBytes2(secretKey);
   let [ctb64, ivb64] = data.split("?iv=");
-  let key = secp256k1.getSharedSecret(privkey, "02" + pubkey);
+  let key = secp256k1.getSharedSecret(privkey, hexToBytes2("02" + pubkey));
   let normalizedKey = getNormalizedX(key);
   let iv = base64.decode(ivb64);
   let ciphertext = base64.decode(ctb64);
   let plaintext = cbc(normalizedKey, iv).decrypt(ciphertext);
Index: package/lib/cjs/nip06.js
===================================================================
--- package/lib/cjs/nip06.js
+++ package/lib/cjs/nip06.js
@@ -27,10 +27,10 @@
   privateKeyFromSeedWords: () => privateKeyFromSeedWords,
   validateWords: () => validateWords
 });
 module.exports = __toCommonJS(nip06_exports);
-var import_utils = require("@noble/hashes/utils");
-var import_english = require("@scure/bip39/wordlists/english");
+var import_utils = require("@noble/hashes/utils.js");
+var import_english = require("@scure/bip39/wordlists/english.js");
 var import_bip39 = require("@scure/bip39");
 var import_bip32 = require("@scure/bip32");
 var DERIVATION_PATH = `m/44'/1237'`;
 function privateKeyFromSeedWords(mnemonic, passphrase, accountIndex = 0) {
Index: package/lib/esm/nip06.js
===================================================================
--- package/lib/esm/nip06.js
+++ package/lib/esm/nip06.js
@@ -1,7 +1,7 @@
 // nip06.ts
-import { bytesToHex } from "@noble/hashes/utils";
-import { wordlist } from "@scure/bip39/wordlists/english";
+import { bytesToHex } from "@noble/hashes/utils.js";
+import { wordlist } from "@scure/bip39/wordlists/english.js";
 import { generateMnemonic, mnemonicToSeedSync, validateMnemonic } from "@scure/bip39";
 import { HDKey } from "@scure/bip32";
 var DERIVATION_PATH = `m/44'/1237'`;
 function privateKeyFromSeedWords(mnemonic, passphrase, accountIndex = 0) {
Index: package/lib/cjs/nip13.js
===================================================================
--- package/lib/cjs/nip13.js
+++ package/lib/cjs/nip13.js
@@ -24,13 +24,13 @@
   getPow: () => getPow,
   minePow: () => minePow
 });
 module.exports = __toCommonJS(nip13_exports);
-var import_utils2 = require("@noble/hashes/utils");
-var import_sha256 = require("@noble/hashes/sha256");
+var import_utils2 = require("@noble/hashes/utils.js");
+var import_sha2 = require("@noble/hashes/sha2.js");
 
 // utils.ts
-var import_utils = require("@noble/hashes/utils");
+var import_utils = require("@noble/hashes/utils.js");
 var utf8Decoder = new TextDecoder("utf-8");
 var utf8Encoder = new TextEncoder();
 
 // nip13.ts
@@ -67,7 +67,7 @@
   return event;
 }
 function fastEventHash(evt) {
   return (0, import_utils2.bytesToHex)(
-    (0, import_sha256.sha256)(utf8Encoder.encode(JSON.stringify([0, evt.pubkey, evt.created_at, evt.kind, evt.tags, evt.content])))
+    (0, import_sha2.sha256)(utf8Encoder.encode(JSON.stringify([0, evt.pubkey, evt.created_at, evt.kind, evt.tags, evt.content])))
   );
 }
Index: package/lib/esm/nip13.js
===================================================================
--- package/lib/esm/nip13.js
+++ package/lib/esm/nip13.js
@@ -1,10 +1,10 @@
 // nip13.ts
-import { bytesToHex as bytesToHex2 } from "@noble/hashes/utils";
-import { sha256 } from "@noble/hashes/sha256";
+import { bytesToHex as bytesToHex2 } from "@noble/hashes/utils.js";
+import { sha256 } from "@noble/hashes/sha2.js";
 
 // utils.ts
-import { bytesToHex, hexToBytes } from "@noble/hashes/utils";
+import { bytesToHex, hexToBytes } from "@noble/hashes/utils.js";
 var utf8Decoder = new TextDecoder("utf-8");
 var utf8Encoder = new TextEncoder();
 
 // nip13.ts
Index: package/lib/cjs/nip17.js
===================================================================
--- package/lib/cjs/nip17.js
+++ package/lib/cjs/nip17.js
@@ -27,10 +27,10 @@
 });
 module.exports = __toCommonJS(nip17_exports);
 
 // pure.ts
-var import_secp256k1 = require("@noble/curves/secp256k1");
-var import_utils2 = require("@noble/hashes/utils");
+var import_secp256k1 = require("@noble/curves/secp256k1.js");
+var import_utils2 = require("@noble/hashes/utils.js");
 
 // core.ts
 var verifiedSymbol = Symbol("verified");
 var isRecord = (obj) => obj instanceof Object;
@@ -61,28 +61,28 @@
   return true;
 }
 
 // pure.ts
-var import_sha256 = require("@noble/hashes/sha256");
+var import_sha2 = require("@noble/hashes/sha2.js");
 
 // utils.ts
-var import_utils = require("@noble/hashes/utils");
+var import_utils = require("@noble/hashes/utils.js");
 var utf8Decoder = new TextDecoder("utf-8");
 var utf8Encoder = new TextEncoder();
 
 // pure.ts
 var JS = class {
   generateSecretKey() {
-    return import_secp256k1.schnorr.utils.randomPrivateKey();
+    return import_secp256k1.schnorr.utils.randomSecretKey();
   }
   getPublicKey(secretKey) {
     return (0, import_utils2.bytesToHex)(import_secp256k1.schnorr.getPublicKey(secretKey));
   }
   finalizeEvent(t, secretKey) {
     const event = t;
     event.pubkey = (0, import_utils2.bytesToHex)(import_secp256k1.schnorr.getPublicKey(secretKey));
     event.id = getEventHash(event);
-    event.sig = (0, import_utils2.bytesToHex)(import_secp256k1.schnorr.sign(getEventHash(event), secretKey));
+    event.sig = (0, import_utils2.bytesToHex)(import_secp256k1.schnorr.sign((0, import_utils2.hexToBytes)(getEventHash(event)), secretKey));
     event[verifiedSymbol] = true;
     return event;
   }
   verifyEvent(event) {
@@ -93,9 +93,9 @@
       event[verifiedSymbol] = false;
       return false;
     }
     try {
-      const valid = import_secp256k1.schnorr.verify(event.sig, hash, event.pubkey);
+      const valid = import_secp256k1.schnorr.verify((0, import_utils2.hexToBytes)(event.sig), (0, import_utils2.hexToBytes)(hash), (0, import_utils2.hexToBytes)(event.pubkey));
       event[verifiedSymbol] = valid;
       return valid;
     } catch (err) {
       event[verifiedSymbol] = false;
@@ -108,9 +108,9 @@
     throw new Error("can't serialize event with wrong or missing properties");
   return JSON.stringify([0, evt.pubkey, evt.created_at, evt.kind, evt.tags, evt.content]);
 }
 function getEventHash(event) {
-  let eventHash = (0, import_sha256.sha256)(utf8Encoder.encode(serializeEvent(event)));
+  let eventHash = (0, import_sha2.sha256)(utf8Encoder.encode(serializeEvent(event)));
   return (0, import_utils2.bytesToHex)(eventHash);
 }
 var i = new JS();
 var generateSecretKey = i.generateSecretKey;
@@ -123,24 +123,24 @@
 var PrivateDirectMessage = 14;
 var GiftWrap = 1059;
 
 // nip44.ts
-var import_chacha = require("@noble/ciphers/chacha");
-var import_utils4 = require("@noble/ciphers/utils");
-var import_secp256k12 = require("@noble/curves/secp256k1");
-var import_hkdf = require("@noble/hashes/hkdf");
-var import_hmac = require("@noble/hashes/hmac");
-var import_sha2562 = require("@noble/hashes/sha256");
-var import_utils5 = require("@noble/hashes/utils");
+var import_chacha = require("@noble/ciphers/chacha.js");
+var import_utils4 = require("@noble/ciphers/utils.js");
+var import_secp256k12 = require("@noble/curves/secp256k1.js");
+var import_hkdf = require("@noble/hashes/hkdf.js");
+var import_hmac = require("@noble/hashes/hmac.js");
+var import_sha22 = require("@noble/hashes/sha2.js");
+var import_utils5 = require("@noble/hashes/utils.js");
 var import_base = require("@scure/base");
 var minPlaintextSize = 1;
 var maxPlaintextSize = 65535;
 function getConversationKey(privkeyA, pubkeyB) {
-  const sharedX = import_secp256k12.secp256k1.getSharedSecret(privkeyA, "02" + pubkeyB).subarray(1, 33);
-  return (0, import_hkdf.extract)(import_sha2562.sha256, sharedX, "nip44-v2");
+  const sharedX = import_secp256k12.secp256k1.getSharedSecret(privkeyA, (0, import_utils5.hexToBytes)("02" + pubkeyB)).subarray(1, 33);
+  return (0, import_hkdf.extract)(import_sha22.sha256, sharedX, utf8Encoder.encode("nip44-v2"));
 }
 function getMessageKeys(conversationKey, nonce) {
-  const keys = (0, import_hkdf.expand)(import_sha2562.sha256, conversationKey, nonce, 76);
+  const keys = (0, import_hkdf.expand)(import_sha22.sha256, conversationKey, nonce, 76);
   return {
     chacha_key: keys.subarray(0, 32),
     chacha_nonce: keys.subarray(32, 44),
     hmac_key: keys.subarray(44, 76)
@@ -179,9 +179,9 @@
 function hmacAad(key, message, aad) {
   if (aad.length !== 32)
     throw new Error("AAD associated data must be 32 bytes");
   const combined = (0, import_utils5.concatBytes)(aad, message);
-  return (0, import_hmac.hmac)(import_sha2562.sha256, key, combined);
+  return (0, import_hmac.hmac)(import_sha22.sha256, key, combined);
 }
 function decodePayload(payload) {
   if (typeof payload !== "string")
     throw new Error("payload must be a valid string");
Index: package/lib/esm/nip17.js
===================================================================
--- package/lib/esm/nip17.js
+++ package/lib/esm/nip17.js
@@ -1,7 +1,7 @@
 // pure.ts
-import { schnorr } from "@noble/curves/secp256k1";
-import { bytesToHex as bytesToHex2 } from "@noble/hashes/utils";
+import { schnorr } from "@noble/curves/secp256k1.js";
+import { bytesToHex as bytesToHex2, hexToBytes as hexToBytes2 } from "@noble/hashes/utils.js";
 
 // core.ts
 var verifiedSymbol = Symbol("verified");
 var isRecord = (obj) => obj instanceof Object;
@@ -32,28 +32,28 @@
   return true;
 }
 
 // pure.ts
-import { sha256 } from "@noble/hashes/sha256";
+import { sha256 } from "@noble/hashes/sha2.js";
 
 // utils.ts
-import { bytesToHex, hexToBytes } from "@noble/hashes/utils";
+import { bytesToHex, hexToBytes } from "@noble/hashes/utils.js";
 var utf8Decoder = new TextDecoder("utf-8");
 var utf8Encoder = new TextEncoder();
 
 // pure.ts
 var JS = class {
   generateSecretKey() {
-    return schnorr.utils.randomPrivateKey();
+    return schnorr.utils.randomSecretKey();
   }
   getPublicKey(secretKey) {
     return bytesToHex2(schnorr.getPublicKey(secretKey));
   }
   finalizeEvent(t, secretKey) {
     const event = t;
     event.pubkey = bytesToHex2(schnorr.getPublicKey(secretKey));
     event.id = getEventHash(event);
-    event.sig = bytesToHex2(schnorr.sign(getEventHash(event), secretKey));
+    event.sig = bytesToHex2(schnorr.sign(hexToBytes2(getEventHash(event)), secretKey));
     event[verifiedSymbol] = true;
     return event;
   }
   verifyEvent(event) {
@@ -64,9 +64,9 @@
       event[verifiedSymbol] = false;
       return false;
     }
     try {
-      const valid = schnorr.verify(event.sig, hash, event.pubkey);
+      const valid = schnorr.verify(hexToBytes2(event.sig), hexToBytes2(hash), hexToBytes2(event.pubkey));
       event[verifiedSymbol] = valid;
       return valid;
     } catch (err) {
       event[verifiedSymbol] = false;
@@ -94,21 +94,21 @@
 var PrivateDirectMessage = 14;
 var GiftWrap = 1059;
 
 // nip44.ts
-import { chacha20 } from "@noble/ciphers/chacha";
-import { equalBytes } from "@noble/ciphers/utils";
-import { secp256k1 } from "@noble/curves/secp256k1";
-import { extract as hkdf_extract, expand as hkdf_expand } from "@noble/hashes/hkdf";
-import { hmac } from "@noble/hashes/hmac";
-import { sha256 as sha2562 } from "@noble/hashes/sha256";
-import { concatBytes, randomBytes } from "@noble/hashes/utils";
+import { chacha20 } from "@noble/ciphers/chacha.js";
+import { equalBytes } from "@noble/ciphers/utils.js";
+import { secp256k1 } from "@noble/curves/secp256k1.js";
+import { extract as hkdf_extract, expand as hkdf_expand } from "@noble/hashes/hkdf.js";
+import { hmac } from "@noble/hashes/hmac.js";
+import { sha256 as sha2562 } from "@noble/hashes/sha2.js";
+import { concatBytes, hexToBytes as hexToBytes3, randomBytes } from "@noble/hashes/utils.js";
 import { base64 } from "@scure/base";
 var minPlaintextSize = 1;
 var maxPlaintextSize = 65535;
 function getConversationKey(privkeyA, pubkeyB) {
-  const sharedX = secp256k1.getSharedSecret(privkeyA, "02" + pubkeyB).subarray(1, 33);
-  return hkdf_extract(sha2562, sharedX, "nip44-v2");
+  const sharedX = secp256k1.getSharedSecret(privkeyA, hexToBytes3("02" + pubkeyB)).subarray(1, 33);
+  return hkdf_extract(sha2562, sharedX, utf8Encoder.encode("nip44-v2"));
 }
 function getMessageKeys(conversationKey, nonce) {
   const keys = hkdf_expand(sha2562, conversationKey, nonce, 76);
   return {
Index: package/lib/cjs/nip18.js
===================================================================
--- package/lib/cjs/nip18.js
+++ package/lib/cjs/nip18.js
@@ -26,10 +26,10 @@
 });
 module.exports = __toCommonJS(nip18_exports);
 
 // pure.ts
-var import_secp256k1 = require("@noble/curves/secp256k1");
-var import_utils2 = require("@noble/hashes/utils");
+var import_secp256k1 = require("@noble/curves/secp256k1.js");
+var import_utils2 = require("@noble/hashes/utils.js");
 
 // core.ts
 var verifiedSymbol = Symbol("verified");
 var isRecord = (obj) => obj instanceof Object;
@@ -60,28 +60,28 @@
   return true;
 }
 
 // pure.ts
-var import_sha256 = require("@noble/hashes/sha256");
+var import_sha2 = require("@noble/hashes/sha2.js");
 
 // utils.ts
-var import_utils = require("@noble/hashes/utils");
+var import_utils = require("@noble/hashes/utils.js");
 var utf8Decoder = new TextDecoder("utf-8");
 var utf8Encoder = new TextEncoder();
 
 // pure.ts
 var JS = class {
   generateSecretKey() {
-    return import_secp256k1.schnorr.utils.randomPrivateKey();
+    return import_secp256k1.schnorr.utils.randomSecretKey();
   }
   getPublicKey(secretKey) {
     return (0, import_utils2.bytesToHex)(import_secp256k1.schnorr.getPublicKey(secretKey));
   }
   finalizeEvent(t, secretKey) {
     const event = t;
     event.pubkey = (0, import_utils2.bytesToHex)(import_secp256k1.schnorr.getPublicKey(secretKey));
     event.id = getEventHash(event);
-    event.sig = (0, import_utils2.bytesToHex)(import_secp256k1.schnorr.sign(getEventHash(event), secretKey));
+    event.sig = (0, import_utils2.bytesToHex)(import_secp256k1.schnorr.sign((0, import_utils2.hexToBytes)(getEventHash(event)), secretKey));
     event[verifiedSymbol] = true;
     return event;
   }
   verifyEvent(event) {
@@ -92,9 +92,9 @@
       event[verifiedSymbol] = false;
       return false;
     }
     try {
-      const valid = import_secp256k1.schnorr.verify(event.sig, hash, event.pubkey);
+      const valid = import_secp256k1.schnorr.verify((0, import_utils2.hexToBytes)(event.sig), (0, import_utils2.hexToBytes)(hash), (0, import_utils2.hexToBytes)(event.pubkey));
       event[verifiedSymbol] = valid;
       return valid;
     } catch (err) {
       event[verifiedSymbol] = false;
@@ -107,9 +107,9 @@
     throw new Error("can't serialize event with wrong or missing properties");
   return JSON.stringify([0, evt.pubkey, evt.created_at, evt.kind, evt.tags, evt.content]);
 }
 function getEventHash(event) {
-  let eventHash = (0, import_sha256.sha256)(utf8Encoder.encode(serializeEvent(event)));
+  let eventHash = (0, import_sha2.sha256)(utf8Encoder.encode(serializeEvent(event)));
   return (0, import_utils2.bytesToHex)(eventHash);
 }
 var i = new JS();
 var generateSecretKey = i.generateSecretKey;
Index: package/lib/esm/nip18.js
===================================================================
--- package/lib/esm/nip18.js
+++ package/lib/esm/nip18.js
@@ -1,7 +1,7 @@
 // pure.ts
-import { schnorr } from "@noble/curves/secp256k1";
-import { bytesToHex as bytesToHex2 } from "@noble/hashes/utils";
+import { schnorr } from "@noble/curves/secp256k1.js";
+import { bytesToHex as bytesToHex2, hexToBytes as hexToBytes2 } from "@noble/hashes/utils.js";
 
 // core.ts
 var verifiedSymbol = Symbol("verified");
 var isRecord = (obj) => obj instanceof Object;
@@ -32,28 +32,28 @@
   return true;
 }
 
 // pure.ts
-import { sha256 } from "@noble/hashes/sha256";
+import { sha256 } from "@noble/hashes/sha2.js";
 
 // utils.ts
-import { bytesToHex, hexToBytes } from "@noble/hashes/utils";
+import { bytesToHex, hexToBytes } from "@noble/hashes/utils.js";
 var utf8Decoder = new TextDecoder("utf-8");
 var utf8Encoder = new TextEncoder();
 
 // pure.ts
 var JS = class {
   generateSecretKey() {
-    return schnorr.utils.randomPrivateKey();
+    return schnorr.utils.randomSecretKey();
   }
   getPublicKey(secretKey) {
     return bytesToHex2(schnorr.getPublicKey(secretKey));
   }
   finalizeEvent(t, secretKey) {
     const event = t;
     event.pubkey = bytesToHex2(schnorr.getPublicKey(secretKey));
     event.id = getEventHash(event);
-    event.sig = bytesToHex2(schnorr.sign(getEventHash(event), secretKey));
+    event.sig = bytesToHex2(schnorr.sign(hexToBytes2(getEventHash(event)), secretKey));
     event[verifiedSymbol] = true;
     return event;
   }
   verifyEvent(event) {
@@ -64,9 +64,9 @@
       event[verifiedSymbol] = false;
       return false;
     }
     try {
-      const valid = schnorr.verify(event.sig, hash, event.pubkey);
+      const valid = schnorr.verify(hexToBytes2(event.sig), hexToBytes2(hash), hexToBytes2(event.pubkey));
       event[verifiedSymbol] = valid;
       return valid;
     } catch (err) {
       event[verifiedSymbol] = false;
Index: package/lib/cjs/nip19.js
===================================================================
--- package/lib/cjs/nip19.js
+++ package/lib/cjs/nip19.js
@@ -33,13 +33,13 @@
   npubEncode: () => npubEncode,
   nsecEncode: () => nsecEncode
 });
 module.exports = __toCommonJS(nip19_exports);
-var import_utils2 = require("@noble/hashes/utils");
+var import_utils2 = require("@noble/hashes/utils.js");
 var import_base = require("@scure/base");
 
 // utils.ts
-var import_utils = require("@noble/hashes/utils");
+var import_utils = require("@noble/hashes/utils.js");
 var utf8Decoder = new TextDecoder("utf-8");
 var utf8Encoder = new TextEncoder();
 
 // nip19.ts
Index: package/lib/esm/nip19.js
===================================================================
--- package/lib/esm/nip19.js
+++ package/lib/esm/nip19.js
@@ -1,10 +1,10 @@
 // nip19.ts
-import { bytesToHex as bytesToHex2, concatBytes, hexToBytes as hexToBytes2 } from "@noble/hashes/utils";
+import { bytesToHex as bytesToHex2, concatBytes, hexToBytes as hexToBytes2 } from "@noble/hashes/utils.js";
 import { bech32 } from "@scure/base";
 
 // utils.ts
-import { bytesToHex, hexToBytes } from "@noble/hashes/utils";
+import { bytesToHex, hexToBytes } from "@noble/hashes/utils.js";
 var utf8Decoder = new TextDecoder("utf-8");
 var utf8Encoder = new TextEncoder();
 
 // nip19.ts
Index: package/lib/cjs/nip21.js
===================================================================
--- package/lib/cjs/nip21.js
+++ package/lib/cjs/nip21.js
@@ -26,13 +26,13 @@
 });
 module.exports = __toCommonJS(nip21_exports);
 
 // nip19.ts
-var import_utils2 = require("@noble/hashes/utils");
+var import_utils2 = require("@noble/hashes/utils.js");
 var import_base = require("@scure/base");
 
 // utils.ts
-var import_utils = require("@noble/hashes/utils");
+var import_utils = require("@noble/hashes/utils.js");
 var utf8Decoder = new TextDecoder("utf-8");
 var utf8Encoder = new TextEncoder();
 
 // nip19.ts
Index: package/lib/esm/nip21.js
===================================================================
--- package/lib/esm/nip21.js
+++ package/lib/esm/nip21.js
@@ -1,10 +1,10 @@
 // nip19.ts
-import { bytesToHex as bytesToHex2, concatBytes, hexToBytes as hexToBytes2 } from "@noble/hashes/utils";
+import { bytesToHex as bytesToHex2, concatBytes, hexToBytes as hexToBytes2 } from "@noble/hashes/utils.js";
 import { bech32 } from "@scure/base";
 
 // utils.ts
-import { bytesToHex, hexToBytes } from "@noble/hashes/utils";
+import { bytesToHex, hexToBytes } from "@noble/hashes/utils.js";
 var utf8Decoder = new TextDecoder("utf-8");
 var utf8Encoder = new TextEncoder();
 
 // nip19.ts
Index: package/lib/cjs/nip25.js
===================================================================
--- package/lib/cjs/nip25.js
+++ package/lib/cjs/nip25.js
@@ -25,10 +25,10 @@
 });
 module.exports = __toCommonJS(nip25_exports);
 
 // pure.ts
-var import_secp256k1 = require("@noble/curves/secp256k1");
-var import_utils2 = require("@noble/hashes/utils");
+var import_secp256k1 = require("@noble/curves/secp256k1.js");
+var import_utils2 = require("@noble/hashes/utils.js");
 
 // core.ts
 var verifiedSymbol = Symbol("verified");
 var isRecord = (obj) => obj instanceof Object;
@@ -59,28 +59,28 @@
   return true;
 }
 
 // pure.ts
-var import_sha256 = require("@noble/hashes/sha256");
+var import_sha2 = require("@noble/hashes/sha2.js");
 
 // utils.ts
-var import_utils = require("@noble/hashes/utils");
+var import_utils = require("@noble/hashes/utils.js");
 var utf8Decoder = new TextDecoder("utf-8");
 var utf8Encoder = new TextEncoder();
 
 // pure.ts
 var JS = class {
   generateSecretKey() {
-    return import_secp256k1.schnorr.utils.randomPrivateKey();
+    return import_secp256k1.schnorr.utils.randomSecretKey();
   }
   getPublicKey(secretKey) {
     return (0, import_utils2.bytesToHex)(import_secp256k1.schnorr.getPublicKey(secretKey));
   }
   finalizeEvent(t, secretKey) {
     const event = t;
     event.pubkey = (0, import_utils2.bytesToHex)(import_secp256k1.schnorr.getPublicKey(secretKey));
     event.id = getEventHash(event);
-    event.sig = (0, import_utils2.bytesToHex)(import_secp256k1.schnorr.sign(getEventHash(event), secretKey));
+    event.sig = (0, import_utils2.bytesToHex)(import_secp256k1.schnorr.sign((0, import_utils2.hexToBytes)(getEventHash(event)), secretKey));
     event[verifiedSymbol] = true;
     return event;
   }
   verifyEvent(event) {
@@ -91,9 +91,9 @@
       event[verifiedSymbol] = false;
       return false;
     }
     try {
-      const valid = import_secp256k1.schnorr.verify(event.sig, hash, event.pubkey);
+      const valid = import_secp256k1.schnorr.verify((0, import_utils2.hexToBytes)(event.sig), (0, import_utils2.hexToBytes)(hash), (0, import_utils2.hexToBytes)(event.pubkey));
       event[verifiedSymbol] = valid;
       return valid;
     } catch (err) {
       event[verifiedSymbol] = false;
@@ -106,9 +106,9 @@
     throw new Error("can't serialize event with wrong or missing properties");
   return JSON.stringify([0, evt.pubkey, evt.created_at, evt.kind, evt.tags, evt.content]);
 }
 function getEventHash(event) {
-  let eventHash = (0, import_sha256.sha256)(utf8Encoder.encode(serializeEvent(event)));
+  let eventHash = (0, import_sha2.sha256)(utf8Encoder.encode(serializeEvent(event)));
   return (0, import_utils2.bytesToHex)(eventHash);
 }
 var i = new JS();
 var generateSecretKey = i.generateSecretKey;
Index: package/lib/esm/nip25.js
===================================================================
--- package/lib/esm/nip25.js
+++ package/lib/esm/nip25.js
@@ -1,7 +1,7 @@
 // pure.ts
-import { schnorr } from "@noble/curves/secp256k1";
-import { bytesToHex as bytesToHex2 } from "@noble/hashes/utils";
+import { schnorr } from "@noble/curves/secp256k1.js";
+import { bytesToHex as bytesToHex2, hexToBytes as hexToBytes2 } from "@noble/hashes/utils.js";
 
 // core.ts
 var verifiedSymbol = Symbol("verified");
 var isRecord = (obj) => obj instanceof Object;
@@ -32,28 +32,28 @@
   return true;
 }
 
 // pure.ts
-import { sha256 } from "@noble/hashes/sha256";
+import { sha256 } from "@noble/hashes/sha2.js";
 
 // utils.ts
-import { bytesToHex, hexToBytes } from "@noble/hashes/utils";
+import { bytesToHex, hexToBytes } from "@noble/hashes/utils.js";
 var utf8Decoder = new TextDecoder("utf-8");
 var utf8Encoder = new TextEncoder();
 
 // pure.ts
 var JS = class {
   generateSecretKey() {
-    return schnorr.utils.randomPrivateKey();
+    return schnorr.utils.randomSecretKey();
   }
   getPublicKey(secretKey) {
     return bytesToHex2(schnorr.getPublicKey(secretKey));
   }
   finalizeEvent(t, secretKey) {
     const event = t;
     event.pubkey = bytesToHex2(schnorr.getPublicKey(secretKey));
     event.id = getEventHash(event);
-    event.sig = bytesToHex2(schnorr.sign(getEventHash(event), secretKey));
+    event.sig = bytesToHex2(schnorr.sign(hexToBytes2(getEventHash(event)), secretKey));
     event[verifiedSymbol] = true;
     return event;
   }
   verifyEvent(event) {
@@ -64,9 +64,9 @@
       event[verifiedSymbol] = false;
       return false;
     }
     try {
-      const valid = schnorr.verify(event.sig, hash, event.pubkey);
+      const valid = schnorr.verify(hexToBytes2(event.sig), hexToBytes2(hash), hexToBytes2(event.pubkey));
       event[verifiedSymbol] = valid;
       return valid;
     } catch (err) {
       event[verifiedSymbol] = false;
Index: package/lib/cjs/nip27.js
===================================================================
--- package/lib/cjs/nip27.js
+++ package/lib/cjs/nip27.js
@@ -24,13 +24,13 @@
 });
 module.exports = __toCommonJS(nip27_exports);
 
 // nip19.ts
-var import_utils2 = require("@noble/hashes/utils");
+var import_utils2 = require("@noble/hashes/utils.js");
 var import_base = require("@scure/base");
 
 // utils.ts
-var import_utils = require("@noble/hashes/utils");
+var import_utils = require("@noble/hashes/utils.js");
 var utf8Decoder = new TextDecoder("utf-8");
 var utf8Encoder = new TextEncoder();
 
 // nip19.ts
Index: package/lib/esm/nip27.js
===================================================================
--- package/lib/esm/nip27.js
+++ package/lib/esm/nip27.js
@@ -1,10 +1,10 @@
 // nip19.ts
-import { bytesToHex as bytesToHex2, concatBytes, hexToBytes as hexToBytes2 } from "@noble/hashes/utils";
+import { bytesToHex as bytesToHex2, concatBytes, hexToBytes as hexToBytes2 } from "@noble/hashes/utils.js";
 import { bech32 } from "@scure/base";
 
 // utils.ts
-import { bytesToHex, hexToBytes } from "@noble/hashes/utils";
+import { bytesToHex, hexToBytes } from "@noble/hashes/utils.js";
 var utf8Decoder = new TextDecoder("utf-8");
 var utf8Encoder = new TextEncoder();
 
 // nip19.ts
Index: package/lib/cjs/nip28.js
===================================================================
--- package/lib/cjs/nip28.js
+++ package/lib/cjs/nip28.js
@@ -28,10 +28,10 @@
 });
 module.exports = __toCommonJS(nip28_exports);
 
 // pure.ts
-var import_secp256k1 = require("@noble/curves/secp256k1");
-var import_utils2 = require("@noble/hashes/utils");
+var import_secp256k1 = require("@noble/curves/secp256k1.js");
+var import_utils2 = require("@noble/hashes/utils.js");
 
 // core.ts
 var verifiedSymbol = Symbol("verified");
 var isRecord = (obj) => obj instanceof Object;
@@ -62,28 +62,28 @@
   return true;
 }
 
 // pure.ts
-var import_sha256 = require("@noble/hashes/sha256");
+var import_sha2 = require("@noble/hashes/sha2.js");
 
 // utils.ts
-var import_utils = require("@noble/hashes/utils");
+var import_utils = require("@noble/hashes/utils.js");
 var utf8Decoder = new TextDecoder("utf-8");
 var utf8Encoder = new TextEncoder();
 
 // pure.ts
 var JS = class {
   generateSecretKey() {
-    return import_secp256k1.schnorr.utils.randomPrivateKey();
+    return import_secp256k1.schnorr.utils.randomSecretKey();
   }
   getPublicKey(secretKey) {
     return (0, import_utils2.bytesToHex)(import_secp256k1.schnorr.getPublicKey(secretKey));
   }
   finalizeEvent(t, secretKey) {
     const event = t;
     event.pubkey = (0, import_utils2.bytesToHex)(import_secp256k1.schnorr.getPublicKey(secretKey));
     event.id = getEventHash(event);
-    event.sig = (0, import_utils2.bytesToHex)(import_secp256k1.schnorr.sign(getEventHash(event), secretKey));
+    event.sig = (0, import_utils2.bytesToHex)(import_secp256k1.schnorr.sign((0, import_utils2.hexToBytes)(getEventHash(event)), secretKey));
     event[verifiedSymbol] = true;
     return event;
   }
   verifyEvent(event) {
@@ -94,9 +94,9 @@
       event[verifiedSymbol] = false;
       return false;
     }
     try {
-      const valid = import_secp256k1.schnorr.verify(event.sig, hash, event.pubkey);
+      const valid = import_secp256k1.schnorr.verify((0, import_utils2.hexToBytes)(event.sig), (0, import_utils2.hexToBytes)(hash), (0, import_utils2.hexToBytes)(event.pubkey));
       event[verifiedSymbol] = valid;
       return valid;
     } catch (err) {
       event[verifiedSymbol] = false;
@@ -109,9 +109,9 @@
     throw new Error("can't serialize event with wrong or missing properties");
   return JSON.stringify([0, evt.pubkey, evt.created_at, evt.kind, evt.tags, evt.content]);
 }
 function getEventHash(event) {
-  let eventHash = (0, import_sha256.sha256)(utf8Encoder.encode(serializeEvent(event)));
+  let eventHash = (0, import_sha2.sha256)(utf8Encoder.encode(serializeEvent(event)));
   return (0, import_utils2.bytesToHex)(eventHash);
 }
 var i = new JS();
 var generateSecretKey = i.generateSecretKey;
Index: package/lib/esm/nip28.js
===================================================================
--- package/lib/esm/nip28.js
+++ package/lib/esm/nip28.js
@@ -1,7 +1,7 @@
 // pure.ts
-import { schnorr } from "@noble/curves/secp256k1";
-import { bytesToHex as bytesToHex2 } from "@noble/hashes/utils";
+import { schnorr } from "@noble/curves/secp256k1.js";
+import { bytesToHex as bytesToHex2, hexToBytes as hexToBytes2 } from "@noble/hashes/utils.js";
 
 // core.ts
 var verifiedSymbol = Symbol("verified");
 var isRecord = (obj) => obj instanceof Object;
@@ -32,28 +32,28 @@
   return true;
 }
 
 // pure.ts
-import { sha256 } from "@noble/hashes/sha256";
+import { sha256 } from "@noble/hashes/sha2.js";
 
 // utils.ts
-import { bytesToHex, hexToBytes } from "@noble/hashes/utils";
+import { bytesToHex, hexToBytes } from "@noble/hashes/utils.js";
 var utf8Decoder = new TextDecoder("utf-8");
 var utf8Encoder = new TextEncoder();
 
 // pure.ts
 var JS = class {
   generateSecretKey() {
-    return schnorr.utils.randomPrivateKey();
+    return schnorr.utils.randomSecretKey();
   }
   getPublicKey(secretKey) {
     return bytesToHex2(schnorr.getPublicKey(secretKey));
   }
   finalizeEvent(t, secretKey) {
     const event = t;
     event.pubkey = bytesToHex2(schnorr.getPublicKey(secretKey));
     event.id = getEventHash(event);
-    event.sig = bytesToHex2(schnorr.sign(getEventHash(event), secretKey));
+    event.sig = bytesToHex2(schnorr.sign(hexToBytes2(getEventHash(event)), secretKey));
     event[verifiedSymbol] = true;
     return event;
   }
   verifyEvent(event) {
@@ -64,9 +64,9 @@
       event[verifiedSymbol] = false;
       return false;
     }
     try {
-      const valid = schnorr.verify(event.sig, hash, event.pubkey);
+      const valid = schnorr.verify(hexToBytes2(event.sig), hexToBytes2(hash), hexToBytes2(event.pubkey));
       event[verifiedSymbol] = valid;
       return valid;
     } catch (err) {
       event[verifiedSymbol] = false;
Index: package/lib/cjs/nip29.js
===================================================================
--- package/lib/cjs/nip29.js
+++ package/lib/cjs/nip29.js
@@ -55,13 +55,13 @@
   })).json();
 }
 
 // nip19.ts
-var import_utils2 = require("@noble/hashes/utils");
+var import_utils2 = require("@noble/hashes/utils.js");
 var import_base = require("@scure/base");
 
 // utils.ts
-var import_utils = require("@noble/hashes/utils");
+var import_utils = require("@noble/hashes/utils.js");
 var utf8Decoder = new TextDecoder("utf-8");
 var utf8Encoder = new TextEncoder();
 function normalizeURL(url) {
   try {
Index: package/lib/esm/nip29.js
===================================================================
--- package/lib/esm/nip29.js
+++ package/lib/esm/nip29.js
@@ -10,13 +10,13 @@
   })).json();
 }
 
 // nip19.ts
-import { bytesToHex as bytesToHex2, concatBytes, hexToBytes as hexToBytes2 } from "@noble/hashes/utils";
+import { bytesToHex as bytesToHex2, concatBytes, hexToBytes as hexToBytes2 } from "@noble/hashes/utils.js";
 import { bech32 } from "@scure/base";
 
 // utils.ts
-import { bytesToHex, hexToBytes } from "@noble/hashes/utils";
+import { bytesToHex, hexToBytes } from "@noble/hashes/utils.js";
 var utf8Decoder = new TextDecoder("utf-8");
 var utf8Encoder = new TextEncoder();
 function normalizeURL(url) {
   try {
Index: package/lib/cjs/nip44.js
===================================================================
--- package/lib/cjs/nip44.js
+++ package/lib/cjs/nip44.js
@@ -25,31 +25,31 @@
   getConversationKey: () => getConversationKey,
   v2: () => v2
 });
 module.exports = __toCommonJS(nip44_exports);
-var import_chacha = require("@noble/ciphers/chacha");
-var import_utils2 = require("@noble/ciphers/utils");
-var import_secp256k1 = require("@noble/curves/secp256k1");
-var import_hkdf = require("@noble/hashes/hkdf");
-var import_hmac = require("@noble/hashes/hmac");
-var import_sha256 = require("@noble/hashes/sha256");
-var import_utils3 = require("@noble/hashes/utils");
+var import_chacha = require("@noble/ciphers/chacha.js");
+var import_utils2 = require("@noble/ciphers/utils.js");
+var import_secp256k1 = require("@noble/curves/secp256k1.js");
+var import_hkdf = require("@noble/hashes/hkdf.js");
+var import_hmac = require("@noble/hashes/hmac.js");
+var import_sha2 = require("@noble/hashes/sha2.js");
+var import_utils3 = require("@noble/hashes/utils.js");
 var import_base = require("@scure/base");
 
 // utils.ts
-var import_utils = require("@noble/hashes/utils");
+var import_utils = require("@noble/hashes/utils.js");
 var utf8Decoder = new TextDecoder("utf-8");
 var utf8Encoder = new TextEncoder();
 
 // nip44.ts
 var minPlaintextSize = 1;
 var maxPlaintextSize = 65535;
 function getConversationKey(privkeyA, pubkeyB) {
-  const sharedX = import_secp256k1.secp256k1.getSharedSecret(privkeyA, "02" + pubkeyB).subarray(1, 33);
-  return (0, import_hkdf.extract)(import_sha256.sha256, sharedX, "nip44-v2");
+  const sharedX = import_secp256k1.secp256k1.getSharedSecret(privkeyA, (0, import_utils3.hexToBytes)("02" + pubkeyB)).subarray(1, 33);
+  return (0, import_hkdf.extract)(import_sha2.sha256, sharedX, utf8Encoder.encode("nip44-v2"));
 }
 function getMessageKeys(conversationKey, nonce) {
-  const keys = (0, import_hkdf.expand)(import_sha256.sha256, conversationKey, nonce, 76);
+  const keys = (0, import_hkdf.expand)(import_sha2.sha256, conversationKey, nonce, 76);
   return {
     chacha_key: keys.subarray(0, 32),
     chacha_nonce: keys.subarray(32, 44),
     hmac_key: keys.subarray(44, 76)
@@ -88,9 +88,9 @@
 function hmacAad(key, message, aad) {
   if (aad.length !== 32)
     throw new Error("AAD associated data must be 32 bytes");
   const combined = (0, import_utils3.concatBytes)(aad, message);
-  return (0, import_hmac.hmac)(import_sha256.sha256, key, combined);
+  return (0, import_hmac.hmac)(import_sha2.sha256, key, combined);
 }
 function decodePayload(payload) {
   if (typeof payload !== "string")
     throw new Error("payload must be a valid string");
Index: package/lib/esm/nip44.js
===================================================================
--- package/lib/esm/nip44.js
+++ package/lib/esm/nip44.js
@@ -1,25 +1,25 @@
 // nip44.ts
-import { chacha20 } from "@noble/ciphers/chacha";
-import { equalBytes } from "@noble/ciphers/utils";
-import { secp256k1 } from "@noble/curves/secp256k1";
-import { extract as hkdf_extract, expand as hkdf_expand } from "@noble/hashes/hkdf";
-import { hmac } from "@noble/hashes/hmac";
-import { sha256 } from "@noble/hashes/sha256";
-import { concatBytes, randomBytes } from "@noble/hashes/utils";
+import { chacha20 } from "@noble/ciphers/chacha.js";
+import { equalBytes } from "@noble/ciphers/utils.js";
+import { secp256k1 } from "@noble/curves/secp256k1.js";
+import { extract as hkdf_extract, expand as hkdf_expand } from "@noble/hashes/hkdf.js";
+import { hmac } from "@noble/hashes/hmac.js";
+import { sha256 } from "@noble/hashes/sha2.js";
+import { concatBytes, hexToBytes as hexToBytes2, randomBytes } from "@noble/hashes/utils.js";
 import { base64 } from "@scure/base";
 
 // utils.ts
-import { bytesToHex, hexToBytes } from "@noble/hashes/utils";
+import { bytesToHex, hexToBytes } from "@noble/hashes/utils.js";
 var utf8Decoder = new TextDecoder("utf-8");
 var utf8Encoder = new TextEncoder();
 
 // nip44.ts
 var minPlaintextSize = 1;
 var maxPlaintextSize = 65535;
 function getConversationKey(privkeyA, pubkeyB) {
-  const sharedX = secp256k1.getSharedSecret(privkeyA, "02" + pubkeyB).subarray(1, 33);
-  return hkdf_extract(sha256, sharedX, "nip44-v2");
+  const sharedX = secp256k1.getSharedSecret(privkeyA, hexToBytes2("02" + pubkeyB)).subarray(1, 33);
+  return hkdf_extract(sha256, sharedX, utf8Encoder.encode("nip44-v2"));
 }
 function getMessageKeys(conversationKey, nonce) {
   const keys = hkdf_expand(sha256, conversationKey, nonce, 76);
   return {
Index: package/lib/cjs/nip46.js
===================================================================
--- package/lib/cjs/nip46.js
+++ package/lib/cjs/nip46.js
@@ -32,10 +32,10 @@
 });
 module.exports = __toCommonJS(nip46_exports);
 
 // pure.ts
-var import_secp256k1 = require("@noble/curves/secp256k1");
-var import_utils2 = require("@noble/hashes/utils");
+var import_secp256k1 = require("@noble/curves/secp256k1.js");
+var import_utils2 = require("@noble/hashes/utils.js");
 
 // core.ts
 var verifiedSymbol = Symbol("verified");
 var isRecord = (obj) => obj instanceof Object;
@@ -66,12 +66,12 @@
   return true;
 }
 
 // pure.ts
-var import_sha256 = require("@noble/hashes/sha256");
+var import_sha2 = require("@noble/hashes/sha2.js");
 
 // utils.ts
-var import_utils = require("@noble/hashes/utils");
+var import_utils = require("@noble/hashes/utils.js");
 var utf8Decoder = new TextDecoder("utf-8");
 var utf8Encoder = new TextEncoder();
 function normalizeURL(url) {
   try {
@@ -145,18 +145,18 @@
 
 // pure.ts
 var JS = class {
   generateSecretKey() {
-    return import_secp256k1.schnorr.utils.randomPrivateKey();
+    return import_secp256k1.schnorr.utils.randomSecretKey();
   }
   getPublicKey(secretKey) {
     return (0, import_utils2.bytesToHex)(import_secp256k1.schnorr.getPublicKey(secretKey));
   }
   finalizeEvent(t, secretKey) {
     const event = t;
     event.pubkey = (0, import_utils2.bytesToHex)(import_secp256k1.schnorr.getPublicKey(secretKey));
     event.id = getEventHash(event);
-    event.sig = (0, import_utils2.bytesToHex)(import_secp256k1.schnorr.sign(getEventHash(event), secretKey));
+    event.sig = (0, import_utils2.bytesToHex)(import_secp256k1.schnorr.sign((0, import_utils2.hexToBytes)(getEventHash(event)), secretKey));
     event[verifiedSymbol] = true;
     return event;
   }
   verifyEvent(event) {
@@ -167,9 +167,9 @@
       event[verifiedSymbol] = false;
       return false;
     }
     try {
-      const valid = import_secp256k1.schnorr.verify(event.sig, hash, event.pubkey);
+      const valid = import_secp256k1.schnorr.verify((0, import_utils2.hexToBytes)(event.sig), (0, import_utils2.hexToBytes)(hash), (0, import_utils2.hexToBytes)(event.pubkey));
       event[verifiedSymbol] = valid;
       return valid;
     } catch (err) {
       event[verifiedSymbol] = false;
@@ -182,9 +182,9 @@
     throw new Error("can't serialize event with wrong or missing properties");
   return JSON.stringify([0, evt.pubkey, evt.created_at, evt.kind, evt.tags, evt.content]);
 }
 function getEventHash(event) {
-  let eventHash = (0, import_sha256.sha256)(utf8Encoder.encode(serializeEvent(event)));
+  let eventHash = (0, import_sha2.sha256)(utf8Encoder.encode(serializeEvent(event)));
   return (0, import_utils2.bytesToHex)(eventHash);
 }
 var i = new JS();
 var generateSecretKey = i.generateSecretKey;
@@ -192,24 +192,24 @@
 var finalizeEvent = i.finalizeEvent;
 var verifyEvent = i.verifyEvent;
 
 // nip44.ts
-var import_chacha = require("@noble/ciphers/chacha");
-var import_utils4 = require("@noble/ciphers/utils");
-var import_secp256k12 = require("@noble/curves/secp256k1");
-var import_hkdf = require("@noble/hashes/hkdf");
-var import_hmac = require("@noble/hashes/hmac");
-var import_sha2562 = require("@noble/hashes/sha256");
-var import_utils5 = require("@noble/hashes/utils");
+var import_chacha = require("@noble/ciphers/chacha.js");
+var import_utils4 = require("@noble/ciphers/utils.js");
+var import_secp256k12 = require("@noble/curves/secp256k1.js");
+var import_hkdf = require("@noble/hashes/hkdf.js");
+var import_hmac = require("@noble/hashes/hmac.js");
+var import_sha22 = require("@noble/hashes/sha2.js");
+var import_utils5 = require("@noble/hashes/utils.js");
 var import_base = require("@scure/base");
 var minPlaintextSize = 1;
 var maxPlaintextSize = 65535;
 function getConversationKey(privkeyA, pubkeyB) {
-  const sharedX = import_secp256k12.secp256k1.getSharedSecret(privkeyA, "02" + pubkeyB).subarray(1, 33);
-  return (0, import_hkdf.extract)(import_sha2562.sha256, sharedX, "nip44-v2");
+  const sharedX = import_secp256k12.secp256k1.getSharedSecret(privkeyA, (0, import_utils5.hexToBytes)("02" + pubkeyB)).subarray(1, 33);
+  return (0, import_hkdf.extract)(import_sha22.sha256, sharedX, utf8Encoder.encode("nip44-v2"));
 }
 function getMessageKeys(conversationKey, nonce) {
-  const keys = (0, import_hkdf.expand)(import_sha2562.sha256, conversationKey, nonce, 76);
+  const keys = (0, import_hkdf.expand)(import_sha22.sha256, conversationKey, nonce, 76);
   return {
     chacha_key: keys.subarray(0, 32),
     chacha_nonce: keys.subarray(32, 44),
     hmac_key: keys.subarray(44, 76)
@@ -248,9 +248,9 @@
 function hmacAad(key, message, aad) {
   if (aad.length !== 32)
     throw new Error("AAD associated data must be 32 bytes");
   const combined = (0, import_utils5.concatBytes)(aad, message);
-  return (0, import_hmac.hmac)(import_sha2562.sha256, key, combined);
+  return (0, import_hmac.hmac)(import_sha22.sha256, key, combined);
 }
 function decodePayload(payload) {
   if (typeof payload !== "string")
     throw new Error("payload must be a valid string");
Index: package/lib/esm/nip46.js
===================================================================
--- package/lib/esm/nip46.js
+++ package/lib/esm/nip46.js
@@ -1,7 +1,7 @@
 // pure.ts
-import { schnorr } from "@noble/curves/secp256k1";
-import { bytesToHex as bytesToHex2 } from "@noble/hashes/utils";
+import { schnorr } from "@noble/curves/secp256k1.js";
+import { bytesToHex as bytesToHex2, hexToBytes as hexToBytes2 } from "@noble/hashes/utils.js";
 
 // core.ts
 var verifiedSymbol = Symbol("verified");
 var isRecord = (obj) => obj instanceof Object;
@@ -32,12 +32,12 @@
   return true;
 }
 
 // pure.ts
-import { sha256 } from "@noble/hashes/sha256";
+import { sha256 } from "@noble/hashes/sha2.js";
 
 // utils.ts
-import { bytesToHex, hexToBytes } from "@noble/hashes/utils";
+import { bytesToHex, hexToBytes } from "@noble/hashes/utils.js";
 var utf8Decoder = new TextDecoder("utf-8");
 var utf8Encoder = new TextEncoder();
 function normalizeURL(url) {
   try {
@@ -111,18 +111,18 @@
 
 // pure.ts
 var JS = class {
   generateSecretKey() {
-    return schnorr.utils.randomPrivateKey();
+    return schnorr.utils.randomSecretKey();
   }
   getPublicKey(secretKey) {
     return bytesToHex2(schnorr.getPublicKey(secretKey));
   }
   finalizeEvent(t, secretKey) {
     const event = t;
     event.pubkey = bytesToHex2(schnorr.getPublicKey(secretKey));
     event.id = getEventHash(event);
-    event.sig = bytesToHex2(schnorr.sign(getEventHash(event), secretKey));
+    event.sig = bytesToHex2(schnorr.sign(hexToBytes2(getEventHash(event)), secretKey));
     event[verifiedSymbol] = true;
     return event;
   }
   verifyEvent(event) {
@@ -133,9 +133,9 @@
       event[verifiedSymbol] = false;
       return false;
     }
     try {
-      const valid = schnorr.verify(event.sig, hash, event.pubkey);
+      const valid = schnorr.verify(hexToBytes2(event.sig), hexToBytes2(hash), hexToBytes2(event.pubkey));
       event[verifiedSymbol] = valid;
       return valid;
     } catch (err) {
       event[verifiedSymbol] = false;
@@ -158,21 +158,21 @@
 var finalizeEvent = i.finalizeEvent;
 var verifyEvent = i.verifyEvent;
 
 // nip44.ts
-import { chacha20 } from "@noble/ciphers/chacha";
-import { equalBytes } from "@noble/ciphers/utils";
-import { secp256k1 } from "@noble/curves/secp256k1";
-import { extract as hkdf_extract, expand as hkdf_expand } from "@noble/hashes/hkdf";
-import { hmac } from "@noble/hashes/hmac";
-import { sha256 as sha2562 } from "@noble/hashes/sha256";
-import { concatBytes, randomBytes } from "@noble/hashes/utils";
+import { chacha20 } from "@noble/ciphers/chacha.js";
+import { equalBytes } from "@noble/ciphers/utils.js";
+import { secp256k1 } from "@noble/curves/secp256k1.js";
+import { extract as hkdf_extract, expand as hkdf_expand } from "@noble/hashes/hkdf.js";
+import { hmac } from "@noble/hashes/hmac.js";
+import { sha256 as sha2562 } from "@noble/hashes/sha2.js";
+import { concatBytes, hexToBytes as hexToBytes3, randomBytes } from "@noble/hashes/utils.js";
 import { base64 } from "@scure/base";
 var minPlaintextSize = 1;
 var maxPlaintextSize = 65535;
 function getConversationKey(privkeyA, pubkeyB) {
-  const sharedX = secp256k1.getSharedSecret(privkeyA, "02" + pubkeyB).subarray(1, 33);
-  return hkdf_extract(sha2562, sharedX, "nip44-v2");
+  const sharedX = secp256k1.getSharedSecret(privkeyA, hexToBytes3("02" + pubkeyB)).subarray(1, 33);
+  return hkdf_extract(sha2562, sharedX, utf8Encoder.encode("nip44-v2"));
 }
 function getMessageKeys(conversationKey, nonce) {
   const keys = hkdf_expand(sha2562, conversationKey, nonce, 76);
   return {
Index: package/lib/cjs/nip47.js
===================================================================
--- package/lib/cjs/nip47.js
+++ package/lib/cjs/nip47.js
@@ -25,10 +25,10 @@
 });
 module.exports = __toCommonJS(nip47_exports);
 
 // pure.ts
-var import_secp256k1 = require("@noble/curves/secp256k1");
-var import_utils2 = require("@noble/hashes/utils");
+var import_secp256k1 = require("@noble/curves/secp256k1.js");
+var import_utils2 = require("@noble/hashes/utils.js");
 
 // core.ts
 var verifiedSymbol = Symbol("verified");
 var isRecord = (obj) => obj instanceof Object;
@@ -59,28 +59,28 @@
   return true;
 }
 
 // pure.ts
-var import_sha256 = require("@noble/hashes/sha256");
+var import_sha2 = require("@noble/hashes/sha2.js");
 
 // utils.ts
-var import_utils = require("@noble/hashes/utils");
+var import_utils = require("@noble/hashes/utils.js");
 var utf8Decoder = new TextDecoder("utf-8");
 var utf8Encoder = new TextEncoder();
 
 // pure.ts
 var JS = class {
   generateSecretKey() {
-    return import_secp256k1.schnorr.utils.randomPrivateKey();
+    return import_secp256k1.schnorr.utils.randomSecretKey();
   }
   getPublicKey(secretKey) {
     return (0, import_utils2.bytesToHex)(import_secp256k1.schnorr.getPublicKey(secretKey));
   }
   finalizeEvent(t, secretKey) {
     const event = t;
     event.pubkey = (0, import_utils2.bytesToHex)(import_secp256k1.schnorr.getPublicKey(secretKey));
     event.id = getEventHash(event);
-    event.sig = (0, import_utils2.bytesToHex)(import_secp256k1.schnorr.sign(getEventHash(event), secretKey));
+    event.sig = (0, import_utils2.bytesToHex)(import_secp256k1.schnorr.sign((0, import_utils2.hexToBytes)(getEventHash(event)), secretKey));
     event[verifiedSymbol] = true;
     return event;
   }
   verifyEvent(event) {
@@ -91,9 +91,9 @@
       event[verifiedSymbol] = false;
       return false;
     }
     try {
-      const valid = import_secp256k1.schnorr.verify(event.sig, hash, event.pubkey);
+      const valid = import_secp256k1.schnorr.verify((0, import_utils2.hexToBytes)(event.sig), (0, import_utils2.hexToBytes)(hash), (0, import_utils2.hexToBytes)(event.pubkey));
       event[verifiedSymbol] = valid;
       return valid;
     } catch (err) {
       event[verifiedSymbol] = false;
@@ -106,9 +106,9 @@
     throw new Error("can't serialize event with wrong or missing properties");
   return JSON.stringify([0, evt.pubkey, evt.created_at, evt.kind, evt.tags, evt.content]);
 }
 function getEventHash(event) {
-  let eventHash = (0, import_sha256.sha256)(utf8Encoder.encode(serializeEvent(event)));
+  let eventHash = (0, import_sha2.sha256)(utf8Encoder.encode(serializeEvent(event)));
   return (0, import_utils2.bytesToHex)(eventHash);
 }
 var i = new JS();
 var generateSecretKey = i.generateSecretKey;
@@ -119,15 +119,15 @@
 // kinds.ts
 var NWCWalletRequest = 23194;
 
 // nip04.ts
-var import_utils4 = require("@noble/hashes/utils");
-var import_secp256k12 = require("@noble/curves/secp256k1");
-var import_aes = require("@noble/ciphers/aes");
+var import_utils4 = require("@noble/hashes/utils.js");
+var import_secp256k12 = require("@noble/curves/secp256k1.js");
+var import_aes = require("@noble/ciphers/aes.js");
 var import_base = require("@scure/base");
 function encrypt(secretKey, pubkey, text) {
-  const privkey = secretKey instanceof Uint8Array ? (0, import_utils4.bytesToHex)(secretKey) : secretKey;
-  const key = import_secp256k12.secp256k1.getSharedSecret(privkey, "02" + pubkey);
+  const privkey = secretKey instanceof Uint8Array ? secretKey : (0, import_utils4.hexToBytes)(secretKey);
+  const key = import_secp256k12.secp256k1.getSharedSecret(privkey, (0, import_utils4.hexToBytes)("02" + pubkey));
   const normalizedKey = getNormalizedX(key);
   let iv = Uint8Array.from((0, import_utils4.randomBytes)(16));
   let plaintext = utf8Encoder.encode(text);
   let ciphertext = (0, import_aes.cbc)(normalizedKey, iv).encrypt(plaintext);
Index: package/lib/esm/nip47.js
===================================================================
--- package/lib/esm/nip47.js
+++ package/lib/esm/nip47.js
@@ -1,7 +1,7 @@
 // pure.ts
-import { schnorr } from "@noble/curves/secp256k1";
-import { bytesToHex as bytesToHex2 } from "@noble/hashes/utils";
+import { schnorr } from "@noble/curves/secp256k1.js";
+import { bytesToHex as bytesToHex2, hexToBytes as hexToBytes2 } from "@noble/hashes/utils.js";
 
 // core.ts
 var verifiedSymbol = Symbol("verified");
 var isRecord = (obj) => obj instanceof Object;
@@ -32,28 +32,28 @@
   return true;
 }
 
 // pure.ts
-import { sha256 } from "@noble/hashes/sha256";
+import { sha256 } from "@noble/hashes/sha2.js";
 
 // utils.ts
-import { bytesToHex, hexToBytes } from "@noble/hashes/utils";
+import { bytesToHex, hexToBytes } from "@noble/hashes/utils.js";
 var utf8Decoder = new TextDecoder("utf-8");
 var utf8Encoder = new TextEncoder();
 
 // pure.ts
 var JS = class {
   generateSecretKey() {
-    return schnorr.utils.randomPrivateKey();
+    return schnorr.utils.randomSecretKey();
   }
   getPublicKey(secretKey) {
     return bytesToHex2(schnorr.getPublicKey(secretKey));
   }
   finalizeEvent(t, secretKey) {
     const event = t;
     event.pubkey = bytesToHex2(schnorr.getPublicKey(secretKey));
     event.id = getEventHash(event);
-    event.sig = bytesToHex2(schnorr.sign(getEventHash(event), secretKey));
+    event.sig = bytesToHex2(schnorr.sign(hexToBytes2(getEventHash(event)), secretKey));
     event[verifiedSymbol] = true;
     return event;
   }
   verifyEvent(event) {
@@ -64,9 +64,9 @@
       event[verifiedSymbol] = false;
       return false;
     }
     try {
-      const valid = schnorr.verify(event.sig, hash, event.pubkey);
+      const valid = schnorr.verify(hexToBytes2(event.sig), hexToBytes2(hash), hexToBytes2(event.pubkey));
       event[verifiedSymbol] = valid;
       return valid;
     } catch (err) {
       event[verifiedSymbol] = false;
@@ -92,15 +92,15 @@
 // kinds.ts
 var NWCWalletRequest = 23194;
 
 // nip04.ts
-import { bytesToHex as bytesToHex3, randomBytes } from "@noble/hashes/utils";
-import { secp256k1 } from "@noble/curves/secp256k1";
-import { cbc } from "@noble/ciphers/aes";
+import { hexToBytes as hexToBytes3, randomBytes } from "@noble/hashes/utils.js";
+import { secp256k1 } from "@noble/curves/secp256k1.js";
+import { cbc } from "@noble/ciphers/aes.js";
 import { base64 } from "@scure/base";
 function encrypt(secretKey, pubkey, text) {
-  const privkey = secretKey instanceof Uint8Array ? bytesToHex3(secretKey) : secretKey;
-  const key = secp256k1.getSharedSecret(privkey, "02" + pubkey);
+  const privkey = secretKey instanceof Uint8Array ? secretKey : hexToBytes3(secretKey);
+  const key = secp256k1.getSharedSecret(privkey, hexToBytes3("02" + pubkey));
   const normalizedKey = getNormalizedX(key);
   let iv = Uint8Array.from(randomBytes(16));
   let plaintext = utf8Encoder.encode(text);
   let ciphertext = cbc(normalizedKey, iv).encrypt(plaintext);
Index: package/lib/cjs/nip49.js
===================================================================
--- package/lib/cjs/nip49.js
+++ package/lib/cjs/nip49.js
@@ -23,14 +23,15 @@
   decrypt: () => decrypt,
   encrypt: () => encrypt
 });
 module.exports = __toCommonJS(nip49_exports);
-var import_scrypt = require("@noble/hashes/scrypt");
-var import_chacha = require("@noble/ciphers/chacha");
-var import_utils2 = require("@noble/hashes/utils");
+var import_base2 = require("@scure/base");
+var import_scrypt = require("@noble/hashes/scrypt.js");
+var import_chacha = require("@noble/ciphers/chacha.js");
+var import_utils2 = require("@noble/hashes/utils.js");
 
 // nip19.ts
-var import_utils = require("@noble/hashes/utils");
+var import_utils = require("@noble/hashes/utils.js");
 var import_base = require("@scure/base");
 var Bech32MaxSize = 5e3;
 function encodeBech32(prefix, data) {
   let words = import_base.bech32.toWords(data);
@@ -40,9 +41,8 @@
   return encodeBech32(prefix, bytes);
 }
 
 // nip49.ts
-var import_base2 = require("@scure/base");
 function encrypt(sec, password, logn = 16, ksb = 2) {
   let salt = (0, import_utils2.randomBytes)(16);
   let n = 2 ** logn;
   let key = (0, import_scrypt.scrypt)(password.normalize("NFKC"), salt, { N: n, r: 8, p: 1, dkLen: 32 });
Index: package/lib/esm/nip49.js
===================================================================
--- package/lib/esm/nip49.js
+++ package/lib/esm/nip49.js
@@ -1,11 +1,12 @@
 // nip49.ts
-import { scrypt } from "@noble/hashes/scrypt";
-import { xchacha20poly1305 } from "@noble/ciphers/chacha";
-import { concatBytes as concatBytes2, randomBytes } from "@noble/hashes/utils";
+import { bech32 as bech322 } from "@scure/base";
+import { scrypt } from "@noble/hashes/scrypt.js";
+import { xchacha20poly1305 } from "@noble/ciphers/chacha.js";
+import { concatBytes as concatBytes2, randomBytes } from "@noble/hashes/utils.js";
 
 // nip19.ts
-import { bytesToHex, concatBytes, hexToBytes } from "@noble/hashes/utils";
+import { bytesToHex, concatBytes, hexToBytes } from "@noble/hashes/utils.js";
 import { bech32 } from "@scure/base";
 var Bech32MaxSize = 5e3;
 function encodeBech32(prefix, data) {
   let words = bech32.toWords(data);
@@ -15,9 +16,8 @@
   return encodeBech32(prefix, bytes);
 }
 
 // nip49.ts
-import { bech32 as bech322 } from "@scure/base";
 function encrypt(sec, password, logn = 16, ksb = 2) {
   let salt = randomBytes(16);
   let n = 2 ** logn;
   let key = scrypt(password.normalize("NFKC"), salt, { N: n, r: 8, p: 1, dkLen: 32 });
Index: package/lib/cjs/nip57.js
===================================================================
--- package/lib/cjs/nip57.js
+++ package/lib/cjs/nip57.js
@@ -30,10 +30,10 @@
 module.exports = __toCommonJS(nip57_exports);
 var import_base = require("@scure/base");
 
 // pure.ts
-var import_secp256k1 = require("@noble/curves/secp256k1");
-var import_utils2 = require("@noble/hashes/utils");
+var import_secp256k1 = require("@noble/curves/secp256k1.js");
+var import_utils2 = require("@noble/hashes/utils.js");
 
 // core.ts
 var verifiedSymbol = Symbol("verified");
 var isRecord = (obj) => obj instanceof Object;
@@ -64,28 +64,28 @@
   return true;
 }
 
 // pure.ts
-var import_sha256 = require("@noble/hashes/sha256");
+var import_sha2 = require("@noble/hashes/sha2.js");
 
 // utils.ts
-var import_utils = require("@noble/hashes/utils");
+var import_utils = require("@noble/hashes/utils.js");
 var utf8Decoder = new TextDecoder("utf-8");
 var utf8Encoder = new TextEncoder();
 
 // pure.ts
 var JS = class {
   generateSecretKey() {
-    return import_secp256k1.schnorr.utils.randomPrivateKey();
+    return import_secp256k1.schnorr.utils.randomSecretKey();
   }
   getPublicKey(secretKey) {
     return (0, import_utils2.bytesToHex)(import_secp256k1.schnorr.getPublicKey(secretKey));
   }
   finalizeEvent(t, secretKey) {
     const event = t;
     event.pubkey = (0, import_utils2.bytesToHex)(import_secp256k1.schnorr.getPublicKey(secretKey));
     event.id = getEventHash(event);
-    event.sig = (0, import_utils2.bytesToHex)(import_secp256k1.schnorr.sign(getEventHash(event), secretKey));
+    event.sig = (0, import_utils2.bytesToHex)(import_secp256k1.schnorr.sign((0, import_utils2.hexToBytes)(getEventHash(event)), secretKey));
     event[verifiedSymbol] = true;
     return event;
   }
   verifyEvent(event) {
@@ -96,9 +96,9 @@
       event[verifiedSymbol] = false;
       return false;
     }
     try {
-      const valid = import_secp256k1.schnorr.verify(event.sig, hash, event.pubkey);
+      const valid = import_secp256k1.schnorr.verify((0, import_utils2.hexToBytes)(event.sig), (0, import_utils2.hexToBytes)(hash), (0, import_utils2.hexToBytes)(event.pubkey));
       event[verifiedSymbol] = valid;
       return valid;
     } catch (err) {
       event[verifiedSymbol] = false;
@@ -111,9 +111,9 @@
     throw new Error("can't serialize event with wrong or missing properties");
   return JSON.stringify([0, evt.pubkey, evt.created_at, evt.kind, evt.tags, evt.content]);
 }
 function getEventHash(event) {
-  let eventHash = (0, import_sha256.sha256)(utf8Encoder.encode(serializeEvent(event)));
+  let eventHash = (0, import_sha2.sha256)(utf8Encoder.encode(serializeEvent(event)));
   return (0, import_utils2.bytesToHex)(eventHash);
 }
 var i = new JS();
 var generateSecretKey = i.generateSecretKey;
Index: package/lib/esm/nip57.js
===================================================================
--- package/lib/esm/nip57.js
+++ package/lib/esm/nip57.js
@@ -1,10 +1,10 @@
 // nip57.ts
 import { bech32 } from "@scure/base";
 
 // pure.ts
-import { schnorr } from "@noble/curves/secp256k1";
-import { bytesToHex as bytesToHex2 } from "@noble/hashes/utils";
+import { schnorr } from "@noble/curves/secp256k1.js";
+import { bytesToHex as bytesToHex2, hexToBytes as hexToBytes2 } from "@noble/hashes/utils.js";
 
 // core.ts
 var verifiedSymbol = Symbol("verified");
 var isRecord = (obj) => obj instanceof Object;
@@ -35,28 +35,28 @@
   return true;
 }
 
 // pure.ts
-import { sha256 } from "@noble/hashes/sha256";
+import { sha256 } from "@noble/hashes/sha2.js";
 
 // utils.ts
-import { bytesToHex, hexToBytes } from "@noble/hashes/utils";
+import { bytesToHex, hexToBytes } from "@noble/hashes/utils.js";
 var utf8Decoder = new TextDecoder("utf-8");
 var utf8Encoder = new TextEncoder();
 
 // pure.ts
 var JS = class {
   generateSecretKey() {
-    return schnorr.utils.randomPrivateKey();
+    return schnorr.utils.randomSecretKey();
   }
   getPublicKey(secretKey) {
     return bytesToHex2(schnorr.getPublicKey(secretKey));
   }
   finalizeEvent(t, secretKey) {
     const event = t;
     event.pubkey = bytesToHex2(schnorr.getPublicKey(secretKey));
     event.id = getEventHash(event);
-    event.sig = bytesToHex2(schnorr.sign(getEventHash(event), secretKey));
+    event.sig = bytesToHex2(schnorr.sign(hexToBytes2(getEventHash(event)), secretKey));
     event[verifiedSymbol] = true;
     return event;
   }
   verifyEvent(event) {
@@ -67,9 +67,9 @@
       event[verifiedSymbol] = false;
       return false;
     }
     try {
-      const valid = schnorr.verify(event.sig, hash, event.pubkey);
+      const valid = schnorr.verify(hexToBytes2(event.sig), hexToBytes2(hash), hexToBytes2(event.pubkey));
       event[verifiedSymbol] = valid;
       return valid;
     } catch (err) {
       event[verifiedSymbol] = false;
Index: package/lib/cjs/nip59.js
===================================================================
--- package/lib/cjs/nip59.js
+++ package/lib/cjs/nip59.js
@@ -30,31 +30,31 @@
 });
 module.exports = __toCommonJS(nip59_exports);
 
 // nip44.ts
-var import_chacha = require("@noble/ciphers/chacha");
-var import_utils2 = require("@noble/ciphers/utils");
-var import_secp256k1 = require("@noble/curves/secp256k1");
-var import_hkdf = require("@noble/hashes/hkdf");
-var import_hmac = require("@noble/hashes/hmac");
-var import_sha256 = require("@noble/hashes/sha256");
-var import_utils3 = require("@noble/hashes/utils");
+var import_chacha = require("@noble/ciphers/chacha.js");
+var import_utils2 = require("@noble/ciphers/utils.js");
+var import_secp256k1 = require("@noble/curves/secp256k1.js");
+var import_hkdf = require("@noble/hashes/hkdf.js");
+var import_hmac = require("@noble/hashes/hmac.js");
+var import_sha2 = require("@noble/hashes/sha2.js");
+var import_utils3 = require("@noble/hashes/utils.js");
 var import_base = require("@scure/base");
 
 // utils.ts
-var import_utils = require("@noble/hashes/utils");
+var import_utils = require("@noble/hashes/utils.js");
 var utf8Decoder = new TextDecoder("utf-8");
 var utf8Encoder = new TextEncoder();
 
 // nip44.ts
 var minPlaintextSize = 1;
 var maxPlaintextSize = 65535;
 function getConversationKey(privkeyA, pubkeyB) {
-  const sharedX = import_secp256k1.secp256k1.getSharedSecret(privkeyA, "02" + pubkeyB).subarray(1, 33);
-  return (0, import_hkdf.extract)(import_sha256.sha256, sharedX, "nip44-v2");
+  const sharedX = import_secp256k1.secp256k1.getSharedSecret(privkeyA, (0, import_utils3.hexToBytes)("02" + pubkeyB)).subarray(1, 33);
+  return (0, import_hkdf.extract)(import_sha2.sha256, sharedX, utf8Encoder.encode("nip44-v2"));
 }
 function getMessageKeys(conversationKey, nonce) {
-  const keys = (0, import_hkdf.expand)(import_sha256.sha256, conversationKey, nonce, 76);
+  const keys = (0, import_hkdf.expand)(import_sha2.sha256, conversationKey, nonce, 76);
   return {
     chacha_key: keys.subarray(0, 32),
     chacha_nonce: keys.subarray(32, 44),
     hmac_key: keys.subarray(44, 76)
@@ -93,9 +93,9 @@
 function hmacAad(key, message, aad) {
   if (aad.length !== 32)
     throw new Error("AAD associated data must be 32 bytes");
   const combined = (0, import_utils3.concatBytes)(aad, message);
-  return (0, import_hmac.hmac)(import_sha256.sha256, key, combined);
+  return (0, import_hmac.hmac)(import_sha2.sha256, key, combined);
 }
 function decodePayload(payload) {
   if (typeof payload !== "string")
     throw new Error("payload must be a valid string");
@@ -139,10 +139,10 @@
   return unpad(padded);
 }
 
 // pure.ts
-var import_secp256k12 = require("@noble/curves/secp256k1");
-var import_utils5 = require("@noble/hashes/utils");
+var import_secp256k12 = require("@noble/curves/secp256k1.js");
+var import_utils5 = require("@noble/hashes/utils.js");
 
 // core.ts
 var verifiedSymbol = Symbol("verified");
 var isRecord = (obj) => obj instanceof Object;
@@ -173,21 +173,21 @@
   return true;
 }
 
 // pure.ts
-var import_sha2562 = require("@noble/hashes/sha256");
+var import_sha22 = require("@noble/hashes/sha2.js");
 var JS = class {
   generateSecretKey() {
-    return import_secp256k12.schnorr.utils.randomPrivateKey();
+    return import_secp256k12.schnorr.utils.randomSecretKey();
   }
   getPublicKey(secretKey) {
     return (0, import_utils5.bytesToHex)(import_secp256k12.schnorr.getPublicKey(secretKey));
   }
   finalizeEvent(t, secretKey) {
     const event = t;
     event.pubkey = (0, import_utils5.bytesToHex)(import_secp256k12.schnorr.getPublicKey(secretKey));
     event.id = getEventHash(event);
-    event.sig = (0, import_utils5.bytesToHex)(import_secp256k12.schnorr.sign(getEventHash(event), secretKey));
+    event.sig = (0, import_utils5.bytesToHex)(import_secp256k12.schnorr.sign((0, import_utils5.hexToBytes)(getEventHash(event)), secretKey));
     event[verifiedSymbol] = true;
     return event;
   }
   verifyEvent(event) {
@@ -198,9 +198,9 @@
       event[verifiedSymbol] = false;
       return false;
     }
     try {
-      const valid = import_secp256k12.schnorr.verify(event.sig, hash, event.pubkey);
+      const valid = import_secp256k12.schnorr.verify((0, import_utils5.hexToBytes)(event.sig), (0, import_utils5.hexToBytes)(hash), (0, import_utils5.hexToBytes)(event.pubkey));
       event[verifiedSymbol] = valid;
       return valid;
     } catch (err) {
       event[verifiedSymbol] = false;
@@ -213,9 +213,9 @@
     throw new Error("can't serialize event with wrong or missing properties");
   return JSON.stringify([0, evt.pubkey, evt.created_at, evt.kind, evt.tags, evt.content]);
 }
 function getEventHash(event) {
-  let eventHash = (0, import_sha2562.sha256)(utf8Encoder.encode(serializeEvent(event)));
+  let eventHash = (0, import_sha22.sha256)(utf8Encoder.encode(serializeEvent(event)));
   return (0, import_utils5.bytesToHex)(eventHash);
 }
 var i = new JS();
 var generateSecretKey = i.generateSecretKey;
Index: package/lib/esm/nip59.js
===================================================================
--- package/lib/esm/nip59.js
+++ package/lib/esm/nip59.js
@@ -1,25 +1,25 @@
 // nip44.ts
-import { chacha20 } from "@noble/ciphers/chacha";
-import { equalBytes } from "@noble/ciphers/utils";
-import { secp256k1 } from "@noble/curves/secp256k1";
-import { extract as hkdf_extract, expand as hkdf_expand } from "@noble/hashes/hkdf";
-import { hmac } from "@noble/hashes/hmac";
-import { sha256 } from "@noble/hashes/sha256";
-import { concatBytes, randomBytes } from "@noble/hashes/utils";
+import { chacha20 } from "@noble/ciphers/chacha.js";
+import { equalBytes } from "@noble/ciphers/utils.js";
+import { secp256k1 } from "@noble/curves/secp256k1.js";
+import { extract as hkdf_extract, expand as hkdf_expand } from "@noble/hashes/hkdf.js";
+import { hmac } from "@noble/hashes/hmac.js";
+import { sha256 } from "@noble/hashes/sha2.js";
+import { concatBytes, hexToBytes as hexToBytes2, randomBytes } from "@noble/hashes/utils.js";
 import { base64 } from "@scure/base";
 
 // utils.ts
-import { bytesToHex, hexToBytes } from "@noble/hashes/utils";
+import { bytesToHex, hexToBytes } from "@noble/hashes/utils.js";
 var utf8Decoder = new TextDecoder("utf-8");
 var utf8Encoder = new TextEncoder();
 
 // nip44.ts
 var minPlaintextSize = 1;
 var maxPlaintextSize = 65535;
 function getConversationKey(privkeyA, pubkeyB) {
-  const sharedX = secp256k1.getSharedSecret(privkeyA, "02" + pubkeyB).subarray(1, 33);
-  return hkdf_extract(sha256, sharedX, "nip44-v2");
+  const sharedX = secp256k1.getSharedSecret(privkeyA, hexToBytes2("02" + pubkeyB)).subarray(1, 33);
+  return hkdf_extract(sha256, sharedX, utf8Encoder.encode("nip44-v2"));
 }
 function getMessageKeys(conversationKey, nonce) {
   const keys = hkdf_expand(sha256, conversationKey, nonce, 76);
   return {
@@ -107,10 +107,10 @@
   return unpad(padded);
 }
 
 // pure.ts
-import { schnorr } from "@noble/curves/secp256k1";
-import { bytesToHex as bytesToHex2 } from "@noble/hashes/utils";
+import { schnorr } from "@noble/curves/secp256k1.js";
+import { bytesToHex as bytesToHex2, hexToBytes as hexToBytes3 } from "@noble/hashes/utils.js";
 
 // core.ts
 var verifiedSymbol = Symbol("verified");
 var isRecord = (obj) => obj instanceof Object;
@@ -141,21 +141,21 @@
   return true;
 }
 
 // pure.ts
-import { sha256 as sha2562 } from "@noble/hashes/sha256";
+import { sha256 as sha2562 } from "@noble/hashes/sha2.js";
 var JS = class {
   generateSecretKey() {
-    return schnorr.utils.randomPrivateKey();
+    return schnorr.utils.randomSecretKey();
   }
   getPublicKey(secretKey) {
     return bytesToHex2(schnorr.getPublicKey(secretKey));
   }
   finalizeEvent(t, secretKey) {
     const event = t;
     event.pubkey = bytesToHex2(schnorr.getPublicKey(secretKey));
     event.id = getEventHash(event);
-    event.sig = bytesToHex2(schnorr.sign(getEventHash(event), secretKey));
+    event.sig = bytesToHex2(schnorr.sign(hexToBytes3(getEventHash(event)), secretKey));
     event[verifiedSymbol] = true;
     return event;
   }
   verifyEvent(event) {
@@ -166,9 +166,9 @@
       event[verifiedSymbol] = false;
       return false;
     }
     try {
-      const valid = schnorr.verify(event.sig, hash, event.pubkey);
+      const valid = schnorr.verify(hexToBytes3(event.sig), hexToBytes3(hash), hexToBytes3(event.pubkey));
       event[verifiedSymbol] = valid;
       return valid;
     } catch (err) {
       event[verifiedSymbol] = false;
Index: package/lib/cjs/nip77.js
===================================================================
--- package/lib/cjs/nip77.js
+++ package/lib/cjs/nip77.js
@@ -24,10 +24,10 @@
   NegentropyStorageVector: () => NegentropyStorageVector,
   NegentropySync: () => NegentropySync
 });
 module.exports = __toCommonJS(nip77_exports);
-var import_utils = require("@noble/ciphers/utils");
-var import_sha256 = require("@noble/hashes/sha256");
+var import_utils = require("@noble/hashes/utils.js");
+var import_sha2 = require("@noble/hashes/sha2.js");
 var PROTOCOL_VERSION = 97;
 var ID_SIZE = 32;
 var FINGERPRINT_SIZE = 16;
 var Mode = {
@@ -156,9 +156,9 @@
   getFingerprint(n) {
     let input = new WrappedBuffer();
     input.extend(this.buf);
     input.extend(encodeVarInt(n));
-    let hash = (0, import_sha256.sha256)(input.unwrap());
+    let hash = (0, import_sha2.sha256)(input.unwrap());
     return hash.subarray(0, FINGERPRINT_SIZE);
   }
 };
 var NegentropyStorageVector = class {
Index: package/lib/esm/nip77.js
===================================================================
--- package/lib/esm/nip77.js
+++ package/lib/esm/nip77.js
@@ -1,7 +1,7 @@
 // nip77.ts
-import { bytesToHex, hexToBytes } from "@noble/ciphers/utils";
-import { sha256 } from "@noble/hashes/sha256";
+import { bytesToHex, hexToBytes } from "@noble/hashes/utils.js";
+import { sha256 } from "@noble/hashes/sha2.js";
 var PROTOCOL_VERSION = 97;
 var ID_SIZE = 32;
 var FINGERPRINT_SIZE = 16;
 var Mode = {
Index: package/lib/cjs/nip98.js
===================================================================
--- package/lib/cjs/nip98.js
+++ package/lib/cjs/nip98.js
@@ -31,15 +31,15 @@
   validateEventUrlTag: () => validateEventUrlTag,
   validateToken: () => validateToken
 });
 module.exports = __toCommonJS(nip98_exports);
-var import_sha2562 = require("@noble/hashes/sha256");
-var import_utils4 = require("@noble/hashes/utils");
+var import_sha22 = require("@noble/hashes/sha2.js");
+var import_utils4 = require("@noble/hashes/utils.js");
 var import_base = require("@scure/base");
 
 // pure.ts
-var import_secp256k1 = require("@noble/curves/secp256k1");
-var import_utils2 = require("@noble/hashes/utils");
+var import_secp256k1 = require("@noble/curves/secp256k1.js");
+var import_utils2 = require("@noble/hashes/utils.js");
 
 // core.ts
 var verifiedSymbol = Symbol("verified");
 var isRecord = (obj) => obj instanceof Object;
@@ -70,28 +70,28 @@
   return true;
 }
 
 // pure.ts
-var import_sha256 = require("@noble/hashes/sha256");
+var import_sha2 = require("@noble/hashes/sha2.js");
 
 // utils.ts
-var import_utils = require("@noble/hashes/utils");
+var import_utils = require("@noble/hashes/utils.js");
 var utf8Decoder = new TextDecoder("utf-8");
 var utf8Encoder = new TextEncoder();
 
 // pure.ts
 var JS = class {
   generateSecretKey() {
-    return import_secp256k1.schnorr.utils.randomPrivateKey();
+    return import_secp256k1.schnorr.utils.randomSecretKey();
   }
   getPublicKey(secretKey) {
     return (0, import_utils2.bytesToHex)(import_secp256k1.schnorr.getPublicKey(secretKey));
   }
   finalizeEvent(t, secretKey) {
     const event = t;
     event.pubkey = (0, import_utils2.bytesToHex)(import_secp256k1.schnorr.getPublicKey(secretKey));
     event.id = getEventHash(event);
-    event.sig = (0, import_utils2.bytesToHex)(import_secp256k1.schnorr.sign(getEventHash(event), secretKey));
+    event.sig = (0, import_utils2.bytesToHex)(import_secp256k1.schnorr.sign((0, import_utils2.hexToBytes)(getEventHash(event)), secretKey));
     event[verifiedSymbol] = true;
     return event;
   }
   verifyEvent(event) {
@@ -102,9 +102,9 @@
       event[verifiedSymbol] = false;
       return false;
     }
     try {
-      const valid = import_secp256k1.schnorr.verify(event.sig, hash, event.pubkey);
+      const valid = import_secp256k1.schnorr.verify((0, import_utils2.hexToBytes)(event.sig), (0, import_utils2.hexToBytes)(hash), (0, import_utils2.hexToBytes)(event.pubkey));
       event[verifiedSymbol] = valid;
       return valid;
     } catch (err) {
       event[verifiedSymbol] = false;
@@ -117,9 +117,9 @@
     throw new Error("can't serialize event with wrong or missing properties");
   return JSON.stringify([0, evt.pubkey, evt.created_at, evt.kind, evt.tags, evt.content]);
 }
 function getEventHash(event) {
-  let eventHash = (0, import_sha256.sha256)(utf8Encoder.encode(serializeEvent(event)));
+  let eventHash = (0, import_sha2.sha256)(utf8Encoder.encode(serializeEvent(event)));
   return (0, import_utils2.bytesToHex)(eventHash);
 }
 var i = new JS();
 var generateSecretKey = i.generateSecretKey;
@@ -193,9 +193,9 @@
   }
   return methodTag.length > 0 && methodTag[1].toLowerCase() === method.toLowerCase();
 }
 function hashPayload(payload) {
-  const hash = (0, import_sha2562.sha256)(utf8Encoder.encode(JSON.stringify(payload)));
+  const hash = (0, import_sha22.sha256)(utf8Encoder.encode(JSON.stringify(payload)));
   return (0, import_utils4.bytesToHex)(hash);
 }
 function validateEventPayloadTag(event, payload) {
   const payloadTag = event.tags.find((t) => t[0] === "payload");
Index: package/lib/esm/nip98.js
===================================================================
--- package/lib/esm/nip98.js
+++ package/lib/esm/nip98.js
@@ -1,12 +1,12 @@
 // nip98.ts
-import { sha256 as sha2562 } from "@noble/hashes/sha256";
-import { bytesToHex as bytesToHex3 } from "@noble/hashes/utils";
+import { sha256 as sha2562 } from "@noble/hashes/sha2.js";
+import { bytesToHex as bytesToHex3 } from "@noble/hashes/utils.js";
 import { base64 } from "@scure/base";
 
 // pure.ts
-import { schnorr } from "@noble/curves/secp256k1";
-import { bytesToHex as bytesToHex2 } from "@noble/hashes/utils";
+import { schnorr } from "@noble/curves/secp256k1.js";
+import { bytesToHex as bytesToHex2, hexToBytes as hexToBytes2 } from "@noble/hashes/utils.js";
 
 // core.ts
 var verifiedSymbol = Symbol("verified");
 var isRecord = (obj) => obj instanceof Object;
@@ -37,28 +37,28 @@
   return true;
 }
 
 // pure.ts
-import { sha256 } from "@noble/hashes/sha256";
+import { sha256 } from "@noble/hashes/sha2.js";
 
 // utils.ts
-import { bytesToHex, hexToBytes } from "@noble/hashes/utils";
+import { bytesToHex, hexToBytes } from "@noble/hashes/utils.js";
 var utf8Decoder = new TextDecoder("utf-8");
 var utf8Encoder = new TextEncoder();
 
 // pure.ts
 var JS = class {
   generateSecretKey() {
-    return schnorr.utils.randomPrivateKey();
+    return schnorr.utils.randomSecretKey();
   }
   getPublicKey(secretKey) {
     return bytesToHex2(schnorr.getPublicKey(secretKey));
   }
   finalizeEvent(t, secretKey) {
     const event = t;
     event.pubkey = bytesToHex2(schnorr.getPublicKey(secretKey));
     event.id = getEventHash(event);
-    event.sig = bytesToHex2(schnorr.sign(getEventHash(event), secretKey));
+    event.sig = bytesToHex2(schnorr.sign(hexToBytes2(getEventHash(event)), secretKey));
     event[verifiedSymbol] = true;
     return event;
   }
   verifyEvent(event) {
@@ -69,9 +69,9 @@
       event[verifiedSymbol] = false;
       return false;
     }
     try {
-      const valid = schnorr.verify(event.sig, hash, event.pubkey);
+      const valid = schnorr.verify(hexToBytes2(event.sig), hexToBytes2(hash), hexToBytes2(event.pubkey));
       event[verifiedSymbol] = valid;
       return valid;
     } catch (err) {
       event[verifiedSymbol] = false;
Index: package/lib/cjs/nipb7.js
===================================================================
--- package/lib/cjs/nipb7.js
+++ package/lib/cjs/nipb7.js
@@ -22,12 +22,12 @@
 __export(nipb7_exports, {
   BlossomClient: () => BlossomClient
 });
 module.exports = __toCommonJS(nipb7_exports);
-var import_sha256 = require("@noble/hashes/sha256");
+var import_sha2 = require("@noble/hashes/sha2.js");
 
 // utils.ts
-var import_utils = require("@noble/hashes/utils");
+var import_utils = require("@noble/hashes/utils.js");
 var utf8Decoder = new TextDecoder("utf-8");
 var utf8Encoder = new TextEncoder();
 
 // nipb7.ts
@@ -98,9 +98,9 @@
       throw new Error(`failed to check for ${hash}: ${error}`);
     }
   }
   async uploadBlob(file, contentType) {
-    const hash = (0, import_utils.bytesToHex)((0, import_sha256.sha256)(new Uint8Array(await file.arrayBuffer())));
+    const hash = (0, import_utils.bytesToHex)((0, import_sha2.sha256)(new Uint8Array(await file.arrayBuffer())));
     const actualContentType = contentType || file.type || "application/octet-stream";
     const bd = await this.httpCall(
       "PUT",
       "upload",
Index: package/lib/esm/nipb7.js
===================================================================
--- package/lib/esm/nipb7.js
+++ package/lib/esm/nipb7.js
@@ -1,9 +1,9 @@
 // nipb7.ts
-import { sha256 } from "@noble/hashes/sha256";
+import { sha256 } from "@noble/hashes/sha2.js";
 
 // utils.ts
-import { bytesToHex, hexToBytes } from "@noble/hashes/utils";
+import { bytesToHex, hexToBytes } from "@noble/hashes/utils.js";
 var utf8Decoder = new TextDecoder("utf-8");
 var utf8Encoder = new TextEncoder();
 
 // nipb7.ts
File too large for inline diff
Index: package/lib/cjs/pool.js
===================================================================
--- package/lib/cjs/pool.js
+++ package/lib/cjs/pool.js
@@ -26,10 +26,10 @@
 });
 module.exports = __toCommonJS(pool_exports);
 
 // pure.ts
-var import_secp256k1 = require("@noble/curves/secp256k1");
-var import_utils2 = require("@noble/hashes/utils");
+var import_secp256k1 = require("@noble/curves/secp256k1.js");
+var import_utils2 = require("@noble/hashes/utils.js");
 
 // core.ts
 var verifiedSymbol = Symbol("verified");
 var isRecord = (obj) => obj instanceof Object;
@@ -60,12 +60,12 @@
   return true;
 }
 
 // pure.ts
-var import_sha256 = require("@noble/hashes/sha256");
+var import_sha2 = require("@noble/hashes/sha2.js");
 
 // utils.ts
-var import_utils = require("@noble/hashes/utils");
+var import_utils = require("@noble/hashes/utils.js");
 var utf8Decoder = new TextDecoder("utf-8");
 var utf8Encoder = new TextEncoder();
 function normalizeURL(url) {
   try {
@@ -139,18 +139,18 @@
 
 // pure.ts
 var JS = class {
   generateSecretKey() {
-    return import_secp256k1.schnorr.utils.randomPrivateKey();
+    return import_secp256k1.schnorr.utils.randomSecretKey();
   }
   getPublicKey(secretKey) {
     return (0, import_utils2.bytesToHex)(import_secp256k1.schnorr.getPublicKey(secretKey));
   }
   finalizeEvent(t, secretKey) {
     const event = t;
     event.pubkey = (0, import_utils2.bytesToHex)(import_secp256k1.schnorr.getPublicKey(secretKey));
     event.id = getEventHash(event);
-    event.sig = (0, import_utils2.bytesToHex)(import_secp256k1.schnorr.sign(getEventHash(event), secretKey));
+    event.sig = (0, import_utils2.bytesToHex)(import_secp256k1.schnorr.sign((0, import_utils2.hexToBytes)(getEventHash(event)), secretKey));
     event[verifiedSymbol] = true;
     return event;
   }
   verifyEvent(event) {
@@ -161,9 +161,9 @@
       event[verifiedSymbol] = false;
       return false;
     }
     try {
-      const valid = import_secp256k1.schnorr.verify(event.sig, hash, event.pubkey);
+      const valid = import_secp256k1.schnorr.verify((0, import_utils2.hexToBytes)(event.sig), (0, import_utils2.hexToBytes)(hash), (0, import_utils2.hexToBytes)(event.pubkey));
       event[verifiedSymbol] = valid;
       return valid;
     } catch (err) {
       event[verifiedSymbol] = false;
@@ -176,9 +176,9 @@
     throw new Error("can't serialize event with wrong or missing properties");
   return JSON.stringify([0, evt.pubkey, evt.created_at, evt.kind, evt.tags, evt.content]);
 }
 function getEventHash(event) {
-  let eventHash = (0, import_sha256.sha256)(utf8Encoder.encode(serializeEvent(event)));
+  let eventHash = (0, import_sha2.sha256)(utf8Encoder.encode(serializeEvent(event)));
   return (0, import_utils2.bytesToHex)(eventHash);
 }
 var i = new JS();
 var generateSecretKey = i.generateSecretKey;
Index: package/lib/esm/pool.js
===================================================================
--- package/lib/esm/pool.js
+++ package/lib/esm/pool.js
@@ -1,7 +1,7 @@
 // pure.ts
-import { schnorr } from "@noble/curves/secp256k1";
-import { bytesToHex as bytesToHex2 } from "@noble/hashes/utils";
+import { schnorr } from "@noble/curves/secp256k1.js";
+import { bytesToHex as bytesToHex2, hexToBytes as hexToBytes2 } from "@noble/hashes/utils.js";
 
 // core.ts
 var verifiedSymbol = Symbol("verified");
 var isRecord = (obj) => obj instanceof Object;
@@ -32,12 +32,12 @@
   return true;
 }
 
 // pure.ts
-import { sha256 } from "@noble/hashes/sha256";
+import { sha256 } from "@noble/hashes/sha2.js";
 
 // utils.ts
-import { bytesToHex, hexToBytes } from "@noble/hashes/utils";
+import { bytesToHex, hexToBytes } from "@noble/hashes/utils.js";
 var utf8Decoder = new TextDecoder("utf-8");
 var utf8Encoder = new TextEncoder();
 function normalizeURL(url) {
   try {
@@ -111,18 +111,18 @@
 
 // pure.ts
 var JS = class {
   generateSecretKey() {
-    return schnorr.utils.randomPrivateKey();
+    return schnorr.utils.randomSecretKey();
   }
   getPublicKey(secretKey) {
     return bytesToHex2(schnorr.getPublicKey(secretKey));
   }
   finalizeEvent(t, secretKey) {
     const event = t;
     event.pubkey = bytesToHex2(schnorr.getPublicKey(secretKey));
     event.id = getEventHash(event);
-    event.sig = bytesToHex2(schnorr.sign(getEventHash(event), secretKey));
+    event.sig = bytesToHex2(schnorr.sign(hexToBytes2(getEventHash(event)), secretKey));
     event[verifiedSymbol] = true;
     return event;
   }
   verifyEvent(event) {
@@ -133,9 +133,9 @@
       event[verifiedSymbol] = false;
       return false;
     }
     try {
-      const valid = schnorr.verify(event.sig, hash, event.pubkey);
+      const valid = schnorr.verify(hexToBytes2(event.sig), hexToBytes2(hash), hexToBytes2(event.pubkey));
       event[verifiedSymbol] = valid;
       return valid;
     } catch (err) {
       event[verifiedSymbol] = false;
Index: package/lib/cjs/pure.js
===================================================================
--- package/lib/cjs/pure.js
+++ package/lib/cjs/pure.js
@@ -30,10 +30,10 @@
   verifiedSymbol: () => verifiedSymbol,
   verifyEvent: () => verifyEvent
 });
 module.exports = __toCommonJS(pure_exports);
-var import_secp256k1 = require("@noble/curves/secp256k1");
-var import_utils2 = require("@noble/hashes/utils");
+var import_secp256k1 = require("@noble/curves/secp256k1.js");
+var import_utils2 = require("@noble/hashes/utils.js");
 
 // core.ts
 var verifiedSymbol = Symbol("verified");
 var isRecord = (obj) => obj instanceof Object;
@@ -72,28 +72,28 @@
   });
 }
 
 // pure.ts
-var import_sha256 = require("@noble/hashes/sha256");
+var import_sha2 = require("@noble/hashes/sha2.js");
 
 // utils.ts
-var import_utils = require("@noble/hashes/utils");
+var import_utils = require("@noble/hashes/utils.js");
 var utf8Decoder = new TextDecoder("utf-8");
 var utf8Encoder = new TextEncoder();
 
 // pure.ts
 var JS = class {
   generateSecretKey() {
-    return import_secp256k1.schnorr.utils.randomPrivateKey();
+    return import_secp256k1.schnorr.utils.randomSecretKey();
   }
   getPublicKey(secretKey) {
     return (0, import_utils2.bytesToHex)(import_secp256k1.schnorr.getPublicKey(secretKey));
   }
   finalizeEvent(t, secretKey) {
     const event = t;
     event.pubkey = (0, import_utils2.bytesToHex)(import_secp256k1.schnorr.getPublicKey(secretKey));
     event.id = getEventHash(event);
-    event.sig = (0, import_utils2.bytesToHex)(import_secp256k1.schnorr.sign(getEventHash(event), secretKey));
+    event.sig = (0, import_utils2.bytesToHex)(import_secp256k1.schnorr.sign((0, import_utils2.hexToBytes)(getEventHash(event)), secretKey));
     event[verifiedSymbol] = true;
     return event;
   }
   verifyEvent(event) {
@@ -104,9 +104,9 @@
       event[verifiedSymbol] = false;
       return false;
     }
     try {
-      const valid = import_secp256k1.schnorr.verify(event.sig, hash, event.pubkey);
+      const valid = import_secp256k1.schnorr.verify((0, import_utils2.hexToBytes)(event.sig), (0, import_utils2.hexToBytes)(hash), (0, import_utils2.hexToBytes)(event.pubkey));
       event[verifiedSymbol] = valid;
       return valid;
     } catch (err) {
       event[verifiedSymbol] = false;
@@ -119,9 +119,9 @@
     throw new Error("can't serialize event with wrong or missing properties");
   return JSON.stringify([0, evt.pubkey, evt.created_at, evt.kind, evt.tags, evt.content]);
 }
 function getEventHash(event) {
-  let eventHash = (0, import_sha256.sha256)(utf8Encoder.encode(serializeEvent(event)));
+  let eventHash = (0, import_sha2.sha256)(utf8Encoder.encode(serializeEvent(event)));
   return (0, import_utils2.bytesToHex)(eventHash);
 }
 var i = new JS();
 var generateSecretKey = i.generateSecretKey;
Index: package/lib/esm/pure.js
===================================================================
--- package/lib/esm/pure.js
+++ package/lib/esm/pure.js
@@ -1,7 +1,7 @@
 // pure.ts
-import { schnorr } from "@noble/curves/secp256k1";
-import { bytesToHex as bytesToHex2 } from "@noble/hashes/utils";
+import { schnorr } from "@noble/curves/secp256k1.js";
+import { bytesToHex as bytesToHex2, hexToBytes as hexToBytes2 } from "@noble/hashes/utils.js";
 
 // core.ts
 var verifiedSymbol = Symbol("verified");
 var isRecord = (obj) => obj instanceof Object;
@@ -40,28 +40,28 @@
   });
 }
 
 // pure.ts
-import { sha256 } from "@noble/hashes/sha256";
+import { sha256 } from "@noble/hashes/sha2.js";
 
 // utils.ts
-import { bytesToHex, hexToBytes } from "@noble/hashes/utils";
+import { bytesToHex, hexToBytes } from "@noble/hashes/utils.js";
 var utf8Decoder = new TextDecoder("utf-8");
 var utf8Encoder = new TextEncoder();
 
 // pure.ts
 var JS = class {
   generateSecretKey() {
-    return schnorr.utils.randomPrivateKey();
+    return schnorr.utils.randomSecretKey();
   }
   getPublicKey(secretKey) {
     return bytesToHex2(schnorr.getPublicKey(secretKey));
   }
   finalizeEvent(t, secretKey) {
     const event = t;
     event.pubkey = bytesToHex2(schnorr.getPublicKey(secretKey));
     event.id = getEventHash(event);
-    event.sig = bytesToHex2(schnorr.sign(getEventHash(event), secretKey));
+    event.sig = bytesToHex2(schnorr.sign(hexToBytes2(getEventHash(event)), secretKey));
     event[verifiedSymbol] = true;
     return event;
   }
   verifyEvent(event) {
@@ -72,9 +72,9 @@
       event[verifiedSymbol] = false;
       return false;
     }
     try {
-      const valid = schnorr.verify(event.sig, hash, event.pubkey);
+      const valid = schnorr.verify(hexToBytes2(event.sig), hexToBytes2(hash), hexToBytes2(event.pubkey));
       event[verifiedSymbol] = valid;
       return valid;
     } catch (err) {
       event[verifiedSymbol] = false;
Index: package/lib/cjs/references.js
===================================================================
--- package/lib/cjs/references.js
+++ package/lib/cjs/references.js
@@ -24,13 +24,13 @@
 });
 module.exports = __toCommonJS(references_exports);
 
 // nip19.ts
-var import_utils2 = require("@noble/hashes/utils");
+var import_utils2 = require("@noble/hashes/utils.js");
 var import_base = require("@scure/base");
 
 // utils.ts
-var import_utils = require("@noble/hashes/utils");
+var import_utils = require("@noble/hashes/utils.js");
 var utf8Decoder = new TextDecoder("utf-8");
 var utf8Encoder = new TextEncoder();
 
 // nip19.ts
Index: package/lib/esm/references.js
===================================================================
--- package/lib/esm/references.js
+++ package/lib/esm/references.js
@@ -1,10 +1,10 @@
 // nip19.ts
-import { bytesToHex as bytesToHex2, concatBytes, hexToBytes as hexToBytes2 } from "@noble/hashes/utils";
+import { bytesToHex as bytesToHex2, concatBytes, hexToBytes as hexToBytes2 } from "@noble/hashes/utils.js";
 import { bech32 } from "@scure/base";
 
 // utils.ts
-import { bytesToHex, hexToBytes } from "@noble/hashes/utils";
+import { bytesToHex, hexToBytes } from "@noble/hashes/utils.js";
 var utf8Decoder = new TextDecoder("utf-8");
 var utf8Encoder = new TextEncoder();
 
 // nip19.ts
Index: package/lib/cjs/relay.js
===================================================================
--- package/lib/cjs/relay.js
+++ package/lib/cjs/relay.js
@@ -28,10 +28,10 @@
 });
 module.exports = __toCommonJS(relay_exports);
 
 // pure.ts
-var import_secp256k1 = require("@noble/curves/secp256k1");
-var import_utils2 = require("@noble/hashes/utils");
+var import_secp256k1 = require("@noble/curves/secp256k1.js");
+var import_utils2 = require("@noble/hashes/utils.js");
 
 // core.ts
 var verifiedSymbol = Symbol("verified");
 var isRecord = (obj) => obj instanceof Object;
@@ -62,12 +62,12 @@
   return true;
 }
 
 // pure.ts
-var import_sha256 = require("@noble/hashes/sha256");
+var import_sha2 = require("@noble/hashes/sha2.js");
 
 // utils.ts
-var import_utils = require("@noble/hashes/utils");
+var import_utils = require("@noble/hashes/utils.js");
 var utf8Decoder = new TextDecoder("utf-8");
 var utf8Encoder = new TextEncoder();
 function normalizeURL(url) {
   try {
@@ -141,18 +141,18 @@
 
 // pure.ts
 var JS = class {
   generateSecretKey() {
-    return import_secp256k1.schnorr.utils.randomPrivateKey();
+    return import_secp256k1.schnorr.utils.randomSecretKey();
   }
   getPublicKey(secretKey) {
     return (0, import_utils2.bytesToHex)(import_secp256k1.schnorr.getPublicKey(secretKey));
   }
   finalizeEvent(t, secretKey) {
     const event = t;
     event.pubkey = (0, import_utils2.bytesToHex)(import_secp256k1.schnorr.getPublicKey(secretKey));
     event.id = getEventHash(event);
-    event.sig = (0, import_utils2.bytesToHex)(import_secp256k1.schnorr.sign(getEventHash(event), secretKey));
+    event.sig = (0, import_utils2.bytesToHex)(import_secp256k1.schnorr.sign((0, import_utils2.hexToBytes)(getEventHash(event)), secretKey));
     event[verifiedSymbol] = true;
     return event;
   }
   verifyEvent(event) {
@@ -163,9 +163,9 @@
       event[verifiedSymbol] = false;
       return false;
     }
     try {
-      const valid = import_secp256k1.schnorr.verify(event.sig, hash, event.pubkey);
+      const valid = import_secp256k1.schnorr.verify((0, import_utils2.hexToBytes)(event.sig), (0, import_utils2.hexToBytes)(hash), (0, import_utils2.hexToBytes)(event.pubkey));
       event[verifiedSymbol] = valid;
       return valid;
     } catch (err) {
       event[verifiedSymbol] = false;
@@ -178,9 +178,9 @@
     throw new Error("can't serialize event with wrong or missing properties");
   return JSON.stringify([0, evt.pubkey, evt.created_at, evt.kind, evt.tags, evt.content]);
 }
 function getEventHash(event) {
-  let eventHash = (0, import_sha256.sha256)(utf8Encoder.encode(serializeEvent(event)));
+  let eventHash = (0, import_sha2.sha256)(utf8Encoder.encode(serializeEvent(event)));
   return (0, import_utils2.bytesToHex)(eventHash);
 }
 var i = new JS();
 var generateSecretKey = i.generateSecretKey;
Index: package/lib/esm/relay.js
===================================================================
--- package/lib/esm/relay.js
+++ package/lib/esm/relay.js
@@ -1,7 +1,7 @@
 // pure.ts
-import { schnorr } from "@noble/curves/secp256k1";
-import { bytesToHex as bytesToHex2 } from "@noble/hashes/utils";
+import { schnorr } from "@noble/curves/secp256k1.js";
+import { bytesToHex as bytesToHex2, hexToBytes as hexToBytes2 } from "@noble/hashes/utils.js";
 
 // core.ts
 var verifiedSymbol = Symbol("verified");
 var isRecord = (obj) => obj instanceof Object;
@@ -32,12 +32,12 @@
   return true;
 }
 
 // pure.ts
-import { sha256 } from "@noble/hashes/sha256";
+import { sha256 } from "@noble/hashes/sha2.js";
 
 // utils.ts
-import { bytesToHex, hexToBytes } from "@noble/hashes/utils";
+import { bytesToHex, hexToBytes } from "@noble/hashes/utils.js";
 var utf8Decoder = new TextDecoder("utf-8");
 var utf8Encoder = new TextEncoder();
 function normalizeURL(url) {
   try {
@@ -111,18 +111,18 @@
 
 // pure.ts
 var JS = class {
   generateSecretKey() {
-    return schnorr.utils.randomPrivateKey();
+    return schnorr.utils.randomSecretKey();
   }
   getPublicKey(secretKey) {
     return bytesToHex2(schnorr.getPublicKey(secretKey));
   }
   finalizeEvent(t, secretKey) {
     const event = t;
     event.pubkey = bytesToHex2(schnorr.getPublicKey(secretKey));
     event.id = getEventHash(event);
-    event.sig = bytesToHex2(schnorr.sign(getEventHash(event), secretKey));
+    event.sig = bytesToHex2(schnorr.sign(hexToBytes2(getEventHash(event)), secretKey));
     event[verifiedSymbol] = true;
     return event;
   }
   verifyEvent(event) {
@@ -133,9 +133,9 @@
       event[verifiedSymbol] = false;
       return false;
     }
     try {
-      const valid = schnorr.verify(event.sig, hash, event.pubkey);
+      const valid = schnorr.verify(hexToBytes2(event.sig), hexToBytes2(hash), hexToBytes2(event.pubkey));
       event[verifiedSymbol] = valid;
       return valid;
     } catch (err) {
       event[verifiedSymbol] = false;
Index: package/lib/cjs/signer.js
===================================================================
--- package/lib/cjs/signer.js
+++ package/lib/cjs/signer.js
@@ -24,10 +24,10 @@
 });
 module.exports = __toCommonJS(signer_exports);
 
 // pure.ts
-var import_secp256k1 = require("@noble/curves/secp256k1");
-var import_utils2 = require("@noble/hashes/utils");
+var import_secp256k1 = require("@noble/curves/secp256k1.js");
+var import_utils2 = require("@noble/hashes/utils.js");
 
 // core.ts
 var verifiedSymbol = Symbol("verified");
 var isRecord = (obj) => obj instanceof Object;
@@ -58,28 +58,28 @@
   return true;
 }
 
 // pure.ts
-var import_sha256 = require("@noble/hashes/sha256");
+var import_sha2 = require("@noble/hashes/sha2.js");
 
 // utils.ts
-var import_utils = require("@noble/hashes/utils");
+var import_utils = require("@noble/hashes/utils.js");
 var utf8Decoder = new TextDecoder("utf-8");
 var utf8Encoder = new TextEncoder();
 
 // pure.ts
 var JS = class {
   generateSecretKey() {
-    return import_secp256k1.schnorr.utils.randomPrivateKey();
+    return import_secp256k1.schnorr.utils.randomSecretKey();
   }
   getPublicKey(secretKey) {
     return (0, import_utils2.bytesToHex)(import_secp256k1.schnorr.getPublicKey(secretKey));
   }
   finalizeEvent(t, secretKey) {
     const event = t;
     event.pubkey = (0, import_utils2.bytesToHex)(import_secp256k1.schnorr.getPublicKey(secretKey));
     event.id = getEventHash(event);
-    event.sig = (0, import_utils2.bytesToHex)(import_secp256k1.schnorr.sign(getEventHash(event), secretKey));
+    event.sig = (0, import_utils2.bytesToHex)(import_secp256k1.schnorr.sign((0, import_utils2.hexToBytes)(getEventHash(event)), secretKey));
     event[verifiedSymbol] = true;
     return event;
   }
   verifyEvent(event) {
@@ -90,9 +90,9 @@
       event[verifiedSymbol] = false;
       return false;
     }
     try {
-      const valid = import_secp256k1.schnorr.verify(event.sig, hash, event.pubkey);
+      const valid = import_secp256k1.schnorr.verify((0, import_utils2.hexToBytes)(event.sig), (0, import_utils2.hexToBytes)(hash), (0, import_utils2.hexToBytes)(event.pubkey));
       event[verifiedSymbol] = valid;
       return valid;
     } catch (err) {
       event[verifiedSymbol] = false;
@@ -105,9 +105,9 @@
     throw new Error("can't serialize event with wrong or missing properties");
   return JSON.stringify([0, evt.pubkey, evt.created_at, evt.kind, evt.tags, evt.content]);
 }
 function getEventHash(event) {
-  let eventHash = (0, import_sha256.sha256)(utf8Encoder.encode(serializeEvent(event)));
+  let eventHash = (0, import_sha2.sha256)(utf8Encoder.encode(serializeEvent(event)));
   return (0, import_utils2.bytesToHex)(eventHash);
 }
 var i = new JS();
 var generateSecretKey = i.generateSecretKey;
Index: package/lib/esm/signer.js
===================================================================
--- package/lib/esm/signer.js
+++ package/lib/esm/signer.js
@@ -1,7 +1,7 @@
 // pure.ts
-import { schnorr } from "@noble/curves/secp256k1";
-import { bytesToHex as bytesToHex2 } from "@noble/hashes/utils";
+import { schnorr } from "@noble/curves/secp256k1.js";
+import { bytesToHex as bytesToHex2, hexToBytes as hexToBytes2 } from "@noble/hashes/utils.js";
 
 // core.ts
 var verifiedSymbol = Symbol("verified");
 var isRecord = (obj) => obj instanceof Object;
@@ -32,28 +32,28 @@
   return true;
 }
 
 // pure.ts
-import { sha256 } from "@noble/hashes/sha256";
+import { sha256 } from "@noble/hashes/sha2.js";
 
 // utils.ts
-import { bytesToHex, hexToBytes } from "@noble/hashes/utils";
+import { bytesToHex, hexToBytes } from "@noble/hashes/utils.js";
 var utf8Decoder = new TextDecoder("utf-8");
 var utf8Encoder = new TextEncoder();
 
 // pure.ts
 var JS = class {
   generateSecretKey() {
-    return schnorr.utils.randomPrivateKey();
+    return schnorr.utils.randomSecretKey();
   }
   getPublicKey(secretKey) {
     return bytesToHex2(schnorr.getPublicKey(secretKey));
   }
   finalizeEvent(t, secretKey) {
     const event = t;
     event.pubkey = bytesToHex2(schnorr.getPublicKey(secretKey));
     event.id = getEventHash(event);
-    event.sig = bytesToHex2(schnorr.sign(getEventHash(event), secretKey));
+    event.sig = bytesToHex2(schnorr.sign(hexToBytes2(getEventHash(event)), secretKey));
     event[verifiedSymbol] = true;
     return event;
   }
   verifyEvent(event) {
@@ -64,9 +64,9 @@
       event[verifiedSymbol] = false;
       return false;
     }
     try {
-      const valid = schnorr.verify(event.sig, hash, event.pubkey);
+      const valid = schnorr.verify(hexToBytes2(event.sig), hexToBytes2(hash), hexToBytes2(event.pubkey));
       event[verifiedSymbol] = valid;
       return valid;
     } catch (err) {
       event[verifiedSymbol] = false;
Index: package/lib/cjs/utils.js
===================================================================
--- package/lib/cjs/utils.js
+++ package/lib/cjs/utils.js
@@ -31,9 +31,9 @@
   utf8Decoder: () => utf8Decoder,
   utf8Encoder: () => utf8Encoder
 });
 module.exports = __toCommonJS(utils_exports);
-var import_utils = require("@noble/hashes/utils");
+var import_utils = require("@noble/hashes/utils.js");
 var utf8Decoder = new TextDecoder("utf-8");
 var utf8Encoder = new TextEncoder();
 function normalizeURL(url) {
   try {
Index: package/lib/esm/utils.js
===================================================================
--- package/lib/esm/utils.js
+++ package/lib/esm/utils.js
@@ -1,6 +1,6 @@
 // utils.ts
-import { bytesToHex, hexToBytes } from "@noble/hashes/utils";
+import { bytesToHex, hexToBytes } from "@noble/hashes/utils.js";
 var utf8Decoder = new TextDecoder("utf-8");
 var utf8Encoder = new TextEncoder();
 function normalizeURL(url) {
   try {
Index: package/lib/cjs/wasm.js
===================================================================
--- package/lib/cjs/wasm.js
+++ package/lib/cjs/wasm.js
@@ -29,9 +29,9 @@
   verifiedSymbol: () => verifiedSymbol,
   verifyEvent: () => verifyEvent
 });
 module.exports = __toCommonJS(wasm_exports);
-var import_utils = require("@noble/hashes/utils");
+var import_utils = require("@noble/hashes/utils.js");
 
 // core.ts
 var verifiedSymbol = Symbol("verified");
 var isRecord = (obj) => obj instanceof Object;
Index: package/lib/esm/wasm.js
===================================================================
--- package/lib/esm/wasm.js
+++ package/lib/esm/wasm.js
@@ -1,6 +1,6 @@
 // wasm.ts
-import { bytesToHex } from "@noble/hashes/utils";
+import { bytesToHex } from "@noble/hashes/utils.js";
 
 // core.ts
 var verifiedSymbol = Symbol("verified");
 var isRecord = (obj) => obj instanceof Object;
Index: package/package.json
===================================================================
--- package/package.json
+++ package/package.json
@@ -1,8 +1,8 @@
 {
   "type": "module",
   "name": "nostr-tools",
-  "version": "2.20.0",
+  "version": "2.21.0",
   "description": "Tools for making a Nostr client.",
   "repository": {
     "type": "git",
     "url": "https://github.com/nbd-wtf/nostr-tools.git"
@@ -235,14 +235,14 @@
     }
   },
   "license": "Unlicense",
   "dependencies": {
-    "@noble/ciphers": "^0.5.1",
-    "@noble/curves": "1.2.0",
-    "@noble/hashes": "1.3.1",
-    "@scure/base": "1.1.1",
-    "@scure/bip32": "1.3.1",
-    "@scure/bip39": "1.2.1",
+    "@noble/ciphers": "npm:@jsr/[email protected]",
+    "@noble/curves": "npm:@jsr/[email protected]",
+    "@noble/hashes": "npm:@jsr/[email protected]",
+    "@scure/base": "npm:@jsr/[email protected]",
+    "@scure/bip32": "npm:@jsr/[email protected]",
+    "@scure/bip39": "npm:@jsr/[email protected]",
     "nostr-wasm": "0.1.0"
   },
   "peerDependencies": {
     "typescript": ">=5.0.0"
Index: package/lib/cjs/abstract-pool.js.map
===================================================================
--- package/lib/cjs/abstract-pool.js.map
+++ package/lib/cjs/abstract-pool.js.map
@@ -1,7 +1,7 @@
 {
   "version": 3,
   "sources": ["../../abstract-pool.ts", "../../core.ts", "../../utils.ts", "../../kinds.ts", "../../filter.ts", "../../fakejson.ts", "../../nip42.ts", "../../helpers.ts", "../../abstract-relay.ts"],
-  "sourcesContent": ["/* global WebSocket */\n\nimport {\n  AbstractRelay as AbstractRelay,\n  SubscriptionParams,\n  Subscription,\n  type AbstractRelayConstructorOptions,\n} from './abstract-relay.ts'\nimport { normalizeURL } from './utils.ts'\n\nimport type { Event, EventTemplate, Nostr, VerifiedEvent } from './core.ts'\nimport { type Filter } from './filter.ts'\nimport { alwaysTrue } from './helpers.ts'\n\nexport type SubCloser = { close: (reason?: string) => void }\n\nexport type AbstractPoolConstructorOptions = AbstractRelayConstructorOptions & {\n  // automaticallyAuth takes a relay URL and should return null\n  // in case that relay shouldn't be authenticated against\n  // or a function to sign the AUTH event template otherwise (that function may still throw in case of failure)\n  automaticallyAuth?: (relayURL: string) => null | ((event: EventTemplate) => Promise<VerifiedEvent>)\n}\n\nexport type SubscribeManyParams = Omit<SubscriptionParams, 'onclose'> & {\n  maxWait?: number\n  abort?: AbortSignal\n  onclose?: (reasons: string[]) => void\n  onauth?: (event: EventTemplate) => Promise<VerifiedEvent>\n  id?: string\n  label?: string\n}\n\nexport class AbstractSimplePool {\n  protected relays: Map<string, AbstractRelay> = new Map()\n  public seenOn: Map<string, Set<AbstractRelay>> = new Map()\n  public trackRelays: boolean = false\n\n  public verifyEvent: Nostr['verifyEvent']\n  public enablePing: boolean | undefined\n  public enableReconnect: boolean\n  public automaticallyAuth?: (relayURL: string) => null | ((event: EventTemplate) => Promise<VerifiedEvent>)\n  public trustedRelayURLs: Set<string> = new Set()\n\n  private _WebSocket?: typeof WebSocket\n\n  constructor(opts: AbstractPoolConstructorOptions) {\n    this.verifyEvent = opts.verifyEvent\n    this._WebSocket = opts.websocketImplementation\n    this.enablePing = opts.enablePing\n    this.enableReconnect = opts.enableReconnect || false\n    this.automaticallyAuth = opts.automaticallyAuth\n  }\n\n  async ensureRelay(\n    url: string,\n    params?: {\n      connectionTimeout?: number\n      abort?: AbortSignal\n    },\n  ): Promise<AbstractRelay> {\n    url = normalizeURL(url)\n\n    let relay = this.relays.get(url)\n    if (!relay) {\n      relay = new AbstractRelay(url, {\n        verifyEvent: this.trustedRelayURLs.has(url) ? alwaysTrue : this.verifyEvent,\n        websocketImplementation: this._WebSocket,\n        enablePing: this.enablePing,\n        enableReconnect: this.enableReconnect,\n      })\n      relay.onclose = () => {\n        if (relay && !relay.enableReconnect) {\n          this.relays.delete(url)\n        }\n      }\n      this.relays.set(url, relay)\n    }\n\n    if (this.automaticallyAuth) {\n      const authSignerFn = this.automaticallyAuth(url)\n      if (authSignerFn) {\n        relay.onauth = authSignerFn\n      }\n    }\n\n    await relay.connect({\n      timeout: params?.connectionTimeout,\n      abort: params?.abort,\n    })\n\n    return relay\n  }\n\n  close(relays: string[]) {\n    relays.map(normalizeURL).forEach(url => {\n      this.relays.get(url)?.close()\n      this.relays.delete(url)\n    })\n  }\n\n  subscribe(relays: string[], filter: Filter, params: SubscribeManyParams): SubCloser {\n    const request: { url: string; filter: Filter }[] = []\n    for (let i = 0; i < relays.length; i++) {\n      const url = normalizeURL(relays[i])\n      if (!request.find(r => r.url === url)) {\n        request.push({ url, filter: filter })\n      }\n    }\n\n    return this.subscribeMap(request, params)\n  }\n\n  subscribeMany(relays: string[], filter: Filter, params: SubscribeManyParams): SubCloser {\n    const request: { url: string; filter: Filter }[] = []\n    const uniqUrls: string[] = []\n    for (let i = 0; i < relays.length; i++) {\n      const url = normalizeURL(relays[i])\n      if (uniqUrls.indexOf(url) === -1) {\n        uniqUrls.push(url)\n        request.push({ url, filter: filter })\n      }\n    }\n\n    return this.subscribeMap(request, params)\n  }\n\n  subscribeMap(requests: { url: string; filter: Filter }[], params: SubscribeManyParams): SubCloser {\n    const grouped = new Map<string, Filter[]>()\n    for (const req of requests) {\n      const { url, filter } = req\n      if (!grouped.has(url)) grouped.set(url, [])\n      grouped.get(url)!.push(filter)\n    }\n    const groupedRequests = Array.from(grouped.entries()).map(([url, filters]) => ({ url, filters }))\n\n    if (this.trackRelays) {\n      params.receivedEvent = (relay: AbstractRelay, id: string) => {\n        let set = this.seenOn.get(id)\n        if (!set) {\n          set = new Set()\n          this.seenOn.set(id, set)\n        }\n        set.add(relay)\n      }\n    }\n\n    const _knownIds = new Set<string>()\n    const subs: Subscription[] = []\n\n    // batch all EOSEs into a single\n    const eosesReceived: boolean[] = []\n    let handleEose = (i: number) => {\n      if (eosesReceived[i]) return // do not act twice for the same relay\n      eosesReceived[i] = true\n      if (eosesReceived.filter(a => a).length === groupedRequests.length) {\n        params.oneose?.()\n        handleEose = () => {}\n      }\n    }\n    // batch all closes into a single\n    const closesReceived: string[] = []\n    let handleClose = (i: number, reason: string) => {\n      if (closesReceived[i]) return // do not act twice for the same relay\n      handleEose(i)\n      closesReceived[i] = reason\n      if (closesReceived.filter(a => a).length === groupedRequests.length) {\n        params.onclose?.(closesReceived)\n        handleClose = () => {}\n      }\n    }\n\n    const localAlreadyHaveEventHandler = (id: string) => {\n      if (params.alreadyHaveEvent?.(id)) {\n        return true\n      }\n      const have = _knownIds.has(id)\n      _knownIds.add(id)\n      return have\n    }\n\n    // open a subscription in all given relays\n    const allOpened = Promise.all(\n      groupedRequests.map(async ({ url, filters }, i) => {\n        let relay: AbstractRelay\n        try {\n          relay = await this.ensureRelay(url, {\n            connectionTimeout: params.maxWait ? Math.max(params.maxWait * 0.8, params.maxWait - 1000) : undefined,\n            abort: params.abort,\n          })\n        } catch (err) {\n          handleClose(i, (err as any)?.message || String(err))\n          return\n        }\n\n        let subscription = relay.subscribe(filters, {\n          ...params,\n          oneose: () => handleEose(i),\n          onclose: reason => {\n            if (reason.startsWith('auth-required: ') && params.onauth) {\n              relay\n                .auth(params.onauth)\n                .then(() => {\n                  relay.subscribe(filters, {\n                    ...params,\n                    oneose: () => handleEose(i),\n                    onclose: reason => {\n                      handleClose(i, reason) // the second time we won't try to auth anymore\n                    },\n                    alreadyHaveEvent: localAlreadyHaveEventHandler,\n                    eoseTimeout: params.maxWait,\n                    abort: params.abort,\n                  })\n                })\n                .catch(err => {\n                  handleClose(i, `auth was required and attempted, but failed with: ${err}`)\n                })\n            } else {\n              handleClose(i, reason)\n            }\n          },\n          alreadyHaveEvent: localAlreadyHaveEventHandler,\n          eoseTimeout: params.maxWait,\n          abort: params.abort,\n        })\n\n        subs.push(subscription)\n      }),\n    )\n\n    return {\n      async close(reason?: string) {\n        await allOpened\n        subs.forEach(sub => {\n          sub.close(reason)\n        })\n      },\n    }\n  }\n\n  subscribeEose(\n    relays: string[],\n    filter: Filter,\n    params: Pick<SubscribeManyParams, 'label' | 'id' | 'onevent' | 'onclose' | 'maxWait' | 'onauth'>,\n  ): SubCloser {\n    const subcloser = this.subscribe(relays, filter, {\n      ...params,\n      oneose() {\n        subcloser.close('closed automatically on eose')\n      },\n    })\n    return subcloser\n  }\n\n  subscribeManyEose(\n    relays: string[],\n    filter: Filter,\n    params: Pick<SubscribeManyParams, 'label' | 'id' | 'onevent' | 'onclose' | 'maxWait' | 'onauth'>,\n  ): SubCloser {\n    const subcloser = this.subscribeMany(relays, filter, {\n      ...params,\n      oneose() {\n        subcloser.close('closed automatically on eose')\n      },\n    })\n    return subcloser\n  }\n\n  async querySync(\n    relays: string[],\n    filter: Filter,\n    params?: Pick<SubscribeManyParams, 'label' | 'id' | 'maxWait'>,\n  ): Promise<Event[]> {\n    return new Promise(async resolve => {\n      const events: Event[] = []\n      this.subscribeEose(relays, filter, {\n        ...params,\n        onevent(event: Event) {\n          events.push(event)\n        },\n        onclose(_: string[]) {\n          resolve(events)\n        },\n      })\n    })\n  }\n\n  async get(\n    relays: string[],\n    filter: Filter,\n    params?: Pick<SubscribeManyParams, 'label' | 'id' | 'maxWait'>,\n  ): Promise<Event | null> {\n    filter.limit = 1\n    const events = await this.querySync(relays, filter, params)\n    events.sort((a, b) => b.created_at - a.created_at)\n    return events[0] || null\n  }\n\n  publish(\n    relays: string[],\n    event: Event,\n    options?: { onauth?: (evt: EventTemplate) => Promise<VerifiedEvent> },\n  ): Promise<string>[] {\n    return relays.map(normalizeURL).map(async (url, i, arr) => {\n      if (arr.indexOf(url) !== i) {\n        // duplicate\n        return Promise.reject('duplicate url')\n      }\n\n      let r = await this.ensureRelay(url)\n      return r\n        .publish(event)\n        .catch(async err => {\n          if (err instanceof Error && err.message.startsWith('auth-required: ') && options?.onauth) {\n            await r.auth(options.onauth)\n            return r.publish(event) // retry\n          }\n          throw err\n        })\n        .then(reason => {\n          if (this.trackRelays) {\n            let set = this.seenOn.get(event.id)\n            if (!set) {\n              set = new Set()\n              this.seenOn.set(event.id, set)\n            }\n            set.add(r)\n          }\n          return reason\n        })\n    })\n  }\n\n  listConnectionStatus(): Map<string, boolean> {\n    const map = new Map<string, boolean>()\n    this.relays.forEach((relay, url) => map.set(url, relay.connected))\n\n    return map\n  }\n\n  destroy(): void {\n    this.relays.forEach(conn => conn.close())\n    this.relays = new Map()\n  }\n}\n", "export interface Nostr {\n  generateSecretKey(): Uint8Array\n  getPublicKey(secretKey: Uint8Array): string\n  finalizeEvent(event: EventTemplate, secretKey: Uint8Array): VerifiedEvent\n  verifyEvent(event: Event): event is VerifiedEvent\n}\n\n/** Designates a verified event signature. */\nexport const verifiedSymbol = Symbol('verified')\n\nexport type NostrEvent = {\n  kind: number\n  tags: string[][]\n  content: string\n  created_at: number\n  pubkey: string\n  id: string\n  sig: string\n  [verifiedSymbol]?: boolean\n}\n\nexport type Event = NostrEvent\nexport type EventTemplate = Pick<Event, 'kind' | 'tags' | 'content' | 'created_at'>\nexport type UnsignedEvent = Pick<Event, 'kind' | 'tags' | 'content' | 'created_at' | 'pubkey'>\n\n/** An event whose signature has been verified. */\nexport interface VerifiedEvent extends Event {\n  [verifiedSymbol]: true\n}\n\nconst isRecord = (obj: unknown): obj is Record<string, unknown> => obj instanceof Object\n\nexport function validateEvent<T>(event: T): event is T & UnsignedEvent {\n  if (!isRecord(event)) return false\n  if (typeof event.kind !== 'number') return false\n  if (typeof event.content !== 'string') return false\n  if (typeof event.created_at !== 'number') return false\n  if (typeof event.pubkey !== 'string') return false\n  if (!event.pubkey.match(/^[a-f0-9]{64}$/)) return false\n\n  if (!Array.isArray(event.tags)) return false\n  for (let i = 0; i < event.tags.length; i++) {\n    let tag = event.tags[i]\n    if (!Array.isArray(tag)) return false\n    for (let j = 0; j < tag.length; j++) {\n      if (typeof tag[j] !== 'string') return false\n    }\n  }\n\n  return true\n}\n\n/**\n * Sort events in reverse-chronological order by the `created_at` timestamp,\n * and then by the event `id` (lexicographically) in case of ties.\n * This mutates the array.\n */\nexport function sortEvents(events: Event[]): Event[] {\n  return events.sort((a: NostrEvent, b: NostrEvent): number => {\n    if (a.created_at !== b.created_at) {\n      return b.created_at - a.created_at\n    }\n    return a.id.localeCompare(b.id)\n  })\n}\n", "import type { Event } from './core.ts'\n\nexport const utf8Decoder: TextDecoder = new TextDecoder('utf-8')\nexport const utf8Encoder: TextEncoder = new TextEncoder()\n\nexport { bytesToHex, hexToBytes } from '@noble/hashes/utils'\n\nexport function normalizeURL(url: string): string {\n  try {\n    if (url.indexOf('://') === -1) url = 'wss://' + url\n    let p = new URL(url)\n    if (p.protocol === 'http:') p.protocol = 'ws:'\n    else if (p.protocol === 'https:') p.protocol = 'wss:'\n    p.pathname = p.pathname.replace(/\\/+/g, '/')\n    if (p.pathname.endsWith('/')) p.pathname = p.pathname.slice(0, -1)\n    if ((p.port === '80' && p.protocol === 'ws:') || (p.port === '443' && p.protocol === 'wss:')) p.port = ''\n    p.searchParams.sort()\n    p.hash = ''\n    return p.toString()\n  } catch (e) {\n    throw new Error(`Invalid URL: ${url}`)\n  }\n}\n\nexport function insertEventIntoDescendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return b.created_at - event.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function insertEventIntoAscendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return event.created_at - b.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function binarySearch<T>(arr: T[], compare: (b: T) => number): [number, boolean] {\n  let start = 0\n  let end = arr.length - 1\n\n  while (start <= end) {\n    const mid = Math.floor((start + end) / 2)\n    const cmp = compare(arr[mid])\n\n    if (cmp === 0) {\n      return [mid, true]\n    }\n\n    if (cmp < 0) {\n      end = mid - 1\n    } else {\n      start = mid + 1\n    }\n  }\n\n  return [start, false]\n}\n\nexport class QueueNode<V> {\n  public value: V\n  public next: QueueNode<V> | null = null\n  public prev: QueueNode<V> | null = null\n\n  constructor(message: V) {\n    this.value = message\n  }\n}\n\nexport class Queue<V> {\n  public first: QueueNode<V> | null\n  public last: QueueNode<V> | null\n\n  constructor() {\n    this.first = null\n    this.last = null\n  }\n\n  enqueue(value: V): boolean {\n    const newNode = new QueueNode(value)\n    if (!this.last) {\n      // list is empty\n      this.first = newNode\n      this.last = newNode\n    } else if (this.last === this.first) {\n      // list has a single element\n      this.last = newNode\n      this.last.prev = this.first\n      this.first.next = newNode\n    } else {\n      // list has elements, add as last\n      newNode.prev = this.last\n      this.last.next = newNode\n      this.last = newNode\n    }\n    return true\n  }\n\n  dequeue(): V | null {\n    if (!this.first) return null\n\n    if (this.first === this.last) {\n      const target = this.first\n      this.first = null\n      this.last = null\n      return target.value\n    }\n\n    const target = this.first\n    this.first = target.next\n    if (this.first) {\n      this.first.prev = null // fix: clean up prev pointer\n    }\n\n    return target.value\n  }\n}\n", "import { NostrEvent, validateEvent } from './pure.ts'\n\n/** Events are **regular**, which means they're all expected to be stored by relays. */\nexport function isRegularKind(kind: number): boolean {\n  return kind < 10000 && kind !== 0 && kind !== 3\n}\n\n/** Events are **replaceable**, which means that, for each combination of `pubkey` and `kind`, only the latest event is expected to (SHOULD) be stored by relays, older versions are expected to be discarded. */\nexport function isReplaceableKind(kind: number): boolean {\n  return kind === 0 || kind === 3 || (10000 <= kind && kind < 20000)\n}\n\n/** Events are **ephemeral**, which means they are not expected to be stored by relays. */\nexport function isEphemeralKind(kind: number): boolean {\n  return 20000 <= kind && kind < 30000\n}\n\n/** Events are **addressable**, which means that, for each combination of `pubkey`, `kind` and the `d` tag, only the latest event is expected to be stored by relays, older versions are expected to be discarded. */\nexport function isAddressableKind(kind: number): boolean {\n  return 30000 <= kind && kind < 40000\n}\n\n/** Classification of the event kind. */\nexport type KindClassification = 'regular' | 'replaceable' | 'ephemeral' | 'parameterized' | 'unknown'\n\n/** Determine the classification of this kind of event if known, or `unknown`. */\nexport function classifyKind(kind: number): KindClassification {\n  if (isRegularKind(kind)) return 'regular'\n  if (isReplaceableKind(kind)) return 'replaceable'\n  if (isEphemeralKind(kind)) return 'ephemeral'\n  if (isAddressableKind(kind)) return 'parameterized'\n  return 'unknown'\n}\n\nexport function isKind<T extends number>(event: unknown, kind: T | Array<T>): event is NostrEvent & { kind: T } {\n  const kindAsArray: number[] = kind instanceof Array ? kind : [kind]\n  return (validateEvent(event) && kindAsArray.includes(event.kind)) || false\n}\n\nexport const Metadata = 0\nexport type Metadata = typeof Metadata\nexport const ShortTextNote = 1\nexport type ShortTextNote = typeof ShortTextNote\nexport const RecommendRelay = 2\nexport type RecommendRelay = typeof RecommendRelay\nexport const Contacts = 3\nexport type Contacts = typeof Contacts\nexport const EncryptedDirectMessage = 4\nexport type EncryptedDirectMessage = typeof EncryptedDirectMessage\nexport const EventDeletion = 5\nexport type EventDeletion = typeof EventDeletion\nexport const Repost = 6\nexport type Repost = typeof Repost\nexport const Reaction = 7\nexport type Reaction = typeof Reaction\nexport const BadgeAward = 8\nexport type BadgeAward = typeof BadgeAward\nexport const ChatMessage = 9\nexport type ChatMessage = typeof ChatMessage\nexport const ForumThread = 11\nexport type ForumThread = typeof ForumThread\nexport const Seal = 13\nexport type Seal = typeof Seal\nexport const PrivateDirectMessage = 14\nexport type PrivateDirectMessage = typeof PrivateDirectMessage\nexport const FileMessage = 15\nexport type FileMessage = typeof FileMessage\nexport const GenericRepost = 16\nexport type GenericRepost = typeof GenericRepost\nexport const Photo = 20\nexport type Photo = typeof Photo\nexport const NormalVideo = 21\nexport type NormalVideo = typeof NormalVideo\nexport const ShortVideo = 22\nexport type ShortVideo = typeof ShortVideo\nexport const ChannelCreation = 40\nexport type ChannelCreation = typeof ChannelCreation\nexport const ChannelMetadata = 41\nexport type ChannelMetadata = typeof ChannelMetadata\nexport const ChannelMessage = 42\nexport type ChannelMessage = typeof ChannelMessage\nexport const ChannelHideMessage = 43\nexport type ChannelHideMessage = typeof ChannelHideMessage\nexport const ChannelMuteUser = 44\nexport type ChannelMuteUser = typeof ChannelMuteUser\nexport const OpenTimestamps = 1040\nexport type OpenTimestamps = typeof OpenTimestamps\nexport const GiftWrap = 1059\nexport type GiftWrap = typeof GiftWrap\nexport const Poll = 1068\nexport type Poll = typeof Poll\nexport const FileMetadata = 1063\nexport type FileMetadata = typeof FileMetadata\nexport const Comment = 1111\nexport type Comment = typeof Comment\nexport const LiveChatMessage = 1311\nexport type LiveChatMessage = typeof LiveChatMessage\nexport const Voice = 1222\nexport type Voice = typeof Voice\nexport const VoiceComment = 1244\nexport type VoiceComment = typeof VoiceComment\nexport const ProblemTracker = 1971\nexport type ProblemTracker = typeof ProblemTracker\nexport const Report = 1984\nexport type Report = typeof Report\nexport const Reporting = 1984\nexport type Reporting = typeof Reporting\nexport const Label = 1985\nexport type Label = typeof Label\nexport const CommunityPostApproval = 4550\nexport type CommunityPostApproval = typeof CommunityPostApproval\nexport const JobRequest = 5999\nexport type JobRequest = typeof JobRequest\nexport const JobResult = 6999\nexport type JobResult = typeof JobResult\nexport const JobFeedback = 7000\nexport type JobFeedback = typeof JobFeedback\nexport const ZapGoal = 9041\nexport type ZapGoal = typeof ZapGoal\nexport const ZapRequest = 9734\nexport type ZapRequest = typeof ZapRequest\nexport const Zap = 9735\nexport type Zap = typeof Zap\nexport const Highlights = 9802\nexport type Highlights = typeof Highlights\nexport const PollResponse = 1018\nexport type PollResponse = typeof PollResponse\nexport const Mutelist = 10000\nexport type Mutelist = typeof Mutelist\nexport const Pinlist = 10001\nexport type Pinlist = typeof Pinlist\nexport const RelayList = 10002\nexport type RelayList = typeof RelayList\nexport const BookmarkList = 10003\nexport type BookmarkList = typeof BookmarkList\nexport const CommunitiesList = 10004\nexport type CommunitiesList = typeof CommunitiesList\nexport const PublicChatsList = 10005\nexport type PublicChatsList = typeof PublicChatsList\nexport const BlockedRelaysList = 10006\nexport type BlockedRelaysList = typeof BlockedRelaysList\nexport const SearchRelaysList = 10007\nexport type SearchRelaysList = typeof SearchRelaysList\nexport const FavoriteRelays = 10012\nexport type FavoriteRelays = typeof FavoriteRelays\nexport const InterestsList = 10015\nexport type InterestsList = typeof InterestsList\nexport const UserEmojiList = 10030\nexport type UserEmojiList = typeof UserEmojiList\nexport const DirectMessageRelaysList = 10050\nexport type DirectMessageRelaysList = typeof DirectMessageRelaysList\nexport const FileServerPreference = 10096\nexport type FileServerPreference = typeof FileServerPreference\nexport const BlossomServerList = 10063\nexport type BlossomServerList = typeof BlossomServerList\nexport const NWCWalletInfo = 13194\nexport type NWCWalletInfo = typeof NWCWalletInfo\nexport const LightningPubRPC = 21000\nexport type LightningPubRPC = typeof LightningPubRPC\nexport const ClientAuth = 22242\nexport type ClientAuth = typeof ClientAuth\nexport const NWCWalletRequest = 23194\nexport type NWCWalletRequest = typeof NWCWalletRequest\nexport const NWCWalletResponse = 23195\nexport type NWCWalletResponse = typeof NWCWalletResponse\nexport const NostrConnect = 24133\nexport type NostrConnect = typeof NostrConnect\nexport const HTTPAuth = 27235\nexport type HTTPAuth = typeof HTTPAuth\nexport const Followsets = 30000\nexport type Followsets = typeof Followsets\nexport const Genericlists = 30001\nexport type Genericlists = typeof Genericlists\nexport const Relaysets = 30002\nexport type Relaysets = typeof Relaysets\nexport const Bookmarksets = 30003\nexport type Bookmarksets = typeof Bookmarksets\nexport const Curationsets = 30004\nexport type Curationsets = typeof Curationsets\nexport const ProfileBadges = 30008\nexport type ProfileBadges = typeof ProfileBadges\nexport const BadgeDefinition = 30009\nexport type BadgeDefinition = typeof BadgeDefinition\nexport const Interestsets = 30015\nexport type Interestsets = typeof Interestsets\nexport const CreateOrUpdateStall = 30017\nexport type CreateOrUpdateStall = typeof CreateOrUpdateStall\nexport const CreateOrUpdateProduct = 30018\nexport type CreateOrUpdateProduct = typeof CreateOrUpdateProduct\nexport const LongFormArticle = 30023\nexport type LongFormArticle = typeof LongFormArticle\nexport const DraftLong = 30024\nexport type DraftLong = typeof DraftLong\nexport const Emojisets = 30030\nexport type Emojisets = typeof Emojisets\nexport const Application = 30078\nexport type Application = typeof Application\nexport const LiveEvent = 30311\nexport type LiveEvent = typeof LiveEvent\nexport const UserStatuses = 30315\nexport type UserStatuses = typeof UserStatuses\nexport const ClassifiedListing = 30402\nexport type ClassifiedListing = typeof ClassifiedListing\nexport const DraftClassifiedListing = 30403\nexport type DraftClassifiedListing = typeof DraftClassifiedListing\nexport const Date = 31922\nexport type Date = typeof Date\nexport const Time = 31923\nexport type Time = typeof Time\nexport const Calendar = 31924\nexport type Calendar = typeof Calendar\nexport const CalendarEventRSVP = 31925\nexport type CalendarEventRSVP = typeof CalendarEventRSVP\nexport const RelayReview = 31987\nexport type RelayReview = typeof RelayReview\nexport const Handlerrecommendation = 31989\nexport type Handlerrecommendation = typeof Handlerrecommendation\nexport const Handlerinformation = 31990\nexport type Handlerinformation = typeof Handlerinformation\nexport const CommunityDefinition = 34550\nexport type CommunityDefinition = typeof CommunityDefinition\nexport const GroupMetadata = 39000\nexport type GroupMetadata = typeof GroupMetadata\n", "import { Event } from './core.ts'\nimport { isAddressableKind, isReplaceableKind } from './kinds.ts'\n\nexport type Filter = {\n  ids?: string[]\n  kinds?: number[]\n  authors?: string[]\n  since?: number\n  until?: number\n  limit?: number\n  search?: string\n  [key: `#${string}`]: string[] | undefined\n}\n\nexport function matchFilter(filter: Filter, event: Event): boolean {\n  if (filter.ids && filter.ids.indexOf(event.id) === -1) {\n    return false\n  }\n  if (filter.kinds && filter.kinds.indexOf(event.kind) === -1) {\n    return false\n  }\n  if (filter.authors && filter.authors.indexOf(event.pubkey) === -1) {\n    return false\n  }\n\n  for (let f in filter) {\n    if (f[0] === '#') {\n      let tagName = f.slice(1)\n      let values = filter[`#${tagName}`]\n      if (values && !event.tags.find(([t, v]) => t === f.slice(1) && values!.indexOf(v) !== -1)) return false\n    }\n  }\n\n  if (filter.since && event.created_at < filter.since) return false\n  if (filter.until && event.created_at > filter.until) return false\n\n  return true\n}\n\nexport function matchFilters(filters: Filter[], event: Event): boolean {\n  for (let i = 0; i < filters.length; i++) {\n    if (matchFilter(filters[i], event)) {\n      return true\n    }\n  }\n  return false\n}\n\nexport function mergeFilters(...filters: Filter[]): Filter {\n  let result: Filter = {}\n  for (let i = 0; i < filters.length; i++) {\n    let filter = filters[i]\n    Object.entries(filter).forEach(([property, values]) => {\n      if (property === 'kinds' || property === 'ids' || property === 'authors' || property[0] === '#') {\n        // @ts-ignore\n        result[property] = result[property] || []\n        // @ts-ignore\n        for (let v = 0; v < values.length; v++) {\n          // @ts-ignore\n          let value = values[v]\n          // @ts-ignore\n          if (!result[property].includes(value)) result[property].push(value)\n        }\n      }\n    })\n\n    if (filter.limit && (!result.limit || filter.limit > result.limit)) result.limit = filter.limit\n    if (filter.until && (!result.until || filter.until > result.until)) result.until = filter.until\n    if (filter.since && (!result.since || filter.since < result.since)) result.since = filter.since\n  }\n\n  return result\n}\n\n/**\n * Calculate the intrinsic limit of a filter.\n * This function returns a positive integer, or `Infinity` if there is no intrinsic limit.\n */\nexport function getFilterLimit(filter: Filter): number {\n  if (filter.ids && !filter.ids.length) return 0\n  if (filter.kinds && !filter.kinds.length) return 0\n  if (filter.authors && !filter.authors.length) return 0\n\n  for (const [key, value] of Object.entries(filter)) {\n    if (key[0] === '#' && Array.isArray(value) && !value.length) return 0\n  }\n\n  return Math.min(\n    // The `limit` property creates an artificial limit.\n    Math.max(0, filter.limit ?? Infinity),\n\n    // There can only be one event per `id`.\n    filter.ids?.length ?? Infinity,\n\n    // Replaceable events are limited by the number of authors and kinds.\n    filter.authors?.length && filter.kinds?.every(kind => isReplaceableKind(kind))\n      ? filter.authors.length * filter.kinds.length\n      : Infinity,\n\n    // Parameterized replaceable events are limited by the number of authors, kinds, and \"d\" tags.\n    filter.authors?.length && filter.kinds?.every(kind => isAddressableKind(kind)) && filter['#d']?.length\n      ? filter.authors.length * filter.kinds.length * filter['#d'].length\n      : Infinity,\n  )\n}\n", "export function getHex64(json: string, field: string): string {\n  let len = field.length + 3\n  let idx = json.indexOf(`\"${field}\":`) + len\n  let s = json.slice(idx).indexOf(`\"`) + idx + 1\n  return json.slice(s, s + 64)\n}\n\nexport function getInt(json: string, field: string): number {\n  let len = field.length\n  let idx = json.indexOf(`\"${field}\":`) + len + 3\n  let sliced = json.slice(idx)\n  let end = Math.min(sliced.indexOf(','), sliced.indexOf('}'))\n  return parseInt(sliced.slice(0, end), 10)\n}\n\nexport function getSubscriptionId(json: string): string | null {\n  let idx = json.slice(0, 22).indexOf(`\"EVENT\"`)\n  if (idx === -1) return null\n\n  let pstart = json.slice(idx + 7 + 1).indexOf(`\"`)\n  if (pstart === -1) return null\n  let start = idx + 7 + 1 + pstart\n\n  let pend = json.slice(start + 1, 80).indexOf(`\"`)\n  if (pend === -1) return null\n  let end = start + 1 + pend\n\n  return json.slice(start + 1, end)\n}\n\nexport function matchEventId(json: string, id: string): boolean {\n  return id === getHex64(json, 'id')\n}\n\nexport function matchEventPubkey(json: string, pubkey: string): boolean {\n  return pubkey === getHex64(json, 'pubkey')\n}\n\nexport function matchEventKind(json: string, kind: number): boolean {\n  return kind === getInt(json, 'kind')\n}\n", "import { EventTemplate } from './core.ts'\nimport { ClientAuth } from './kinds.ts'\n\n/**\n * creates an EventTemplate for an AUTH event to be signed.\n */\nexport function makeAuthEvent(relayURL: string, challenge: string): EventTemplate {\n  return {\n    kind: ClientAuth,\n    created_at: Math.floor(Date.now() / 1000),\n    tags: [\n      ['relay', relayURL],\n      ['challenge', challenge],\n    ],\n    content: '',\n  }\n}\n", "import { verifiedSymbol, type Event, type Nostr, VerifiedEvent } from './core.ts'\n\nexport async function yieldThread() {\n  return new Promise<void>((resolve, reject) => {\n    try {\n      // Check if MessageChannel is available\n      if (typeof MessageChannel !== 'undefined') {\n        const ch = new MessageChannel()\n        const handler = () => {\n          // @ts-ignore (typescript thinks this property should be called `removeListener`, but in fact it's `removeEventListener`)\n          ch.port1.removeEventListener('message', handler)\n          resolve()\n        }\n        // @ts-ignore (typescript thinks this property should be called `addListener`, but in fact it's `addEventListener`)\n        ch.port1.addEventListener('message', handler)\n        ch.port2.postMessage(0)\n        ch.port1.start()\n      } else {\n        if (typeof setImmediate !== 'undefined') {\n          setImmediate(resolve)\n        } else if (typeof setTimeout !== 'undefined') {\n          setTimeout(resolve, 0)\n        } else {\n          // Last resort - resolve immediately\n          resolve()\n        }\n      }\n    } catch (e) {\n      console.error('during yield: ', e)\n      reject(e)\n    }\n  })\n}\n\nexport const alwaysTrue: Nostr['verifyEvent'] = (t: Event): t is VerifiedEvent => {\n  t[verifiedSymbol] = true\n  return true\n}\n", "/* global WebSocket */\n\nimport type { Event, EventTemplate, VerifiedEvent, Nostr, NostrEvent } from './core.ts'\nimport { matchFilters, type Filter } from './filter.ts'\nimport { getHex64, getSubscriptionId } from './fakejson.ts'\nimport { Queue, normalizeURL } from './utils.ts'\nimport { makeAuthEvent } from './nip42.ts'\nimport { yieldThread } from './helpers.ts'\n\ntype RelayWebSocket = WebSocket & {\n  ping?(): void\n  on?(event: 'pong', listener: () => void): any\n}\n\nexport type AbstractRelayConstructorOptions = {\n  verifyEvent: Nostr['verifyEvent']\n  websocketImplementation?: typeof WebSocket\n  enablePing?: boolean\n  enableReconnect?: boolean\n}\n\nexport class SendingOnClosedConnection extends Error {\n  constructor(message: string, relay: string) {\n    super(`Tried to send message '${message} on a closed connection to ${relay}.`)\n    this.name = 'SendingOnClosedConnection'\n  }\n}\n\nexport class AbstractRelay {\n  public readonly url: string\n  private _connected: boolean = false\n\n  public onclose: (() => void) | null = null\n  public onnotice: (msg: string) => void = msg => console.debug(`NOTICE from ${this.url}: ${msg}`)\n  public onauth: undefined | ((evt: EventTemplate) => Promise<VerifiedEvent>)\n\n  public baseEoseTimeout: number = 4400\n  public publishTimeout: number = 4400\n  public pingFrequency: number = 29000\n  public pingTimeout: number = 20000\n  public resubscribeBackoff: number[] = [10000, 10000, 10000, 20000, 20000, 30000, 60000]\n  public openSubs: Map<string, Subscription> = new Map()\n  public enablePing: boolean | undefined\n  public enableReconnect: boolean\n  private reconnectTimeoutHandle: ReturnType<typeof setTimeout> | undefined\n  private pingIntervalHandle: ReturnType<typeof setInterval> | undefined\n  private reconnectAttempts: number = 0\n  private closedIntentionally: boolean = false\n\n  private connectionPromise: Promise<void> | undefined\n  private openCountRequests = new Map<string, CountResolver>()\n  private openEventPublishes = new Map<string, EventPublishResolver>()\n  private ws: RelayWebSocket | undefined\n  private incomingMessageQueue = new Queue<string>()\n  private queueRunning = false\n  private challenge: string | undefined\n  private authPromise: Promise<string> | undefined\n  private serial: number = 0\n  private verifyEvent: Nostr['verifyEvent']\n\n  private _WebSocket: typeof WebSocket\n\n  constructor(url: string, opts: AbstractRelayConstructorOptions) {\n    this.url = normalizeURL(url)\n    this.verifyEvent = opts.verifyEvent\n    this._WebSocket = opts.websocketImplementation || WebSocket\n    this.enablePing = opts.enablePing\n    this.enableReconnect = opts.enableReconnect || false\n  }\n\n  static async connect(\n    url: string,\n    opts: AbstractRelayConstructorOptions & Parameters<AbstractRelay['connect']>[0],\n  ): Promise<AbstractRelay> {\n    const relay = new AbstractRelay(url, opts)\n    await relay.connect(opts)\n    return relay\n  }\n\n  private closeAllSubscriptions(reason: string) {\n    for (let [_, sub] of this.openSubs) {\n      sub.close(reason)\n    }\n    this.openSubs.clear()\n\n    for (let [_, ep] of this.openEventPublishes) {\n      ep.reject(new Error(reason))\n    }\n    this.openEventPublishes.clear()\n\n    for (let [_, cr] of this.openCountRequests) {\n      cr.reject(new Error(reason))\n    }\n    this.openCountRequests.clear()\n  }\n\n  public get connected(): boolean {\n    return this._connected\n  }\n\n  private async reconnect(): Promise<void> {\n    const backoff = this.resubscribeBackoff[Math.min(this.reconnectAttempts, this.resubscribeBackoff.length - 1)]\n    this.reconnectAttempts++\n\n    this.reconnectTimeoutHandle = setTimeout(async () => {\n      try {\n        await this.connect()\n      } catch (err) {\n        // this will be called again through onclose/onerror\n      }\n    }, backoff)\n  }\n\n  private handleHardClose(reason: string) {\n    if (this.pingIntervalHandle) {\n      clearInterval(this.pingIntervalHandle)\n      this.pingIntervalHandle = undefined\n    }\n\n    this._connected = false\n    this.connectionPromise = undefined\n\n    const wasIntentional = this.closedIntentionally\n    this.closedIntentionally = false // reset for next time\n\n    this.onclose?.()\n\n    if (this.enableReconnect && !wasIntentional) {\n      this.reconnect()\n    } else {\n      this.closeAllSubscriptions(reason)\n    }\n  }\n\n  public async connect(opts?: { timeout?: number; abort?: AbortSignal }): Promise<void> {\n    let connectionTimeoutHandle: ReturnType<typeof setTimeout> | undefined\n\n    if (this.connectionPromise) return this.connectionPromise\n\n    this.challenge = undefined\n    this.authPromise = undefined\n    this.connectionPromise = new Promise((resolve, reject) => {\n      if (opts?.timeout) {\n        connectionTimeoutHandle = setTimeout(() => {\n          reject('connection timed out')\n          this.connectionPromise = undefined\n          this.onclose?.()\n          this.closeAllSubscriptions('relay connection timed out')\n        }, opts.timeout)\n      }\n\n      if (opts?.abort) {\n        opts.abort.onabort = reject\n      }\n\n      try {\n        this.ws = new this._WebSocket(this.url)\n      } catch (err) {\n        clearTimeout(connectionTimeoutHandle)\n        reject(err)\n        return\n      }\n\n      this.ws.onopen = () => {\n        if (this.reconnectTimeoutHandle) {\n          clearTimeout(this.reconnectTimeoutHandle)\n          this.reconnectTimeoutHandle = undefined\n        }\n        clearTimeout(connectionTimeoutHandle)\n        this._connected = true\n\n        const isReconnection = this.reconnectAttempts > 0\n        this.reconnectAttempts = 0\n\n        // resubscribe to all open subscriptions\n        for (const sub of this.openSubs.values()) {\n          sub.eosed = false\n          if (isReconnection) {\n            for (let f = 0; f < sub.filters.length; f++) {\n              if (sub.lastEmitted) {\n                sub.filters[f].since = sub.lastEmitted + 1\n              }\n            }\n          }\n          sub.fire()\n        }\n\n        if (this.enablePing) {\n          this.pingIntervalHandle = setInterval(() => this.pingpong(), this.pingFrequency)\n        }\n        resolve()\n      }\n\n      this.ws.onerror = ev => {\n        clearTimeout(connectionTimeoutHandle)\n        reject((ev as any).message || 'websocket error')\n        this.handleHardClose('relay connection errored')\n      }\n\n      this.ws.onclose = ev => {\n        clearTimeout(connectionTimeoutHandle)\n        reject((ev as any).message || 'websocket closed')\n        this.handleHardClose('relay connection closed')\n      }\n\n      this.ws.onmessage = this._onmessage.bind(this)\n    })\n\n    return this.connectionPromise\n  }\n\n  private waitForPingPong() {\n    return new Promise(resolve => {\n      // listen for pong\n      ;(this.ws as any).once('pong', () => resolve(true))\n      // send a ping\n      this.ws!.ping!()\n    })\n  }\n\n  private waitForDummyReq() {\n    return new Promise((resolve, reject) => {\n      if (!this.connectionPromise) return reject(new Error(`no connection to ${this.url}, can't ping`))\n\n      // make a dummy request with expected empty eose reply\n      // [\"REQ\", \"_\", {\"ids\":[\"aaaa...aaaa\"], \"limit\": 0}]\n      try {\n        const sub = this.subscribe(\n          [{ ids: ['aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'], limit: 0 }],\n          {\n            label: 'forced-ping',\n            oneose: () => {\n              resolve(true)\n              sub.close()\n            },\n            onclose() {\n              // if we get a CLOSED it's because the relay is alive\n              resolve(true)\n            },\n            eoseTimeout: this.pingTimeout + 1000,\n          },\n        )\n      } catch (err) {\n        reject(err)\n      }\n    })\n  }\n\n  // nodejs requires this magic here to ensure connections are closed when internet goes off and stuff\n  // in browsers it's done automatically. see https://github.com/nbd-wtf/nostr-tools/issues/491\n  private async pingpong() {\n    // if the websocket is connected\n    if (this.ws?.readyState === 1) {\n      // wait for either a ping-pong reply or a timeout\n      const result = await Promise.any([\n        // browsers don't have ping so use a dummy req\n        this.ws && this.ws.ping && (this.ws as any).once ? this.waitForPingPong() : this.waitForDummyReq(),\n        new Promise(res => setTimeout(() => res(false), this.pingTimeout)),\n      ])\n\n      if (!result) {\n        // pingpong closing socket\n        if (this.ws?.readyState === this._WebSocket.OPEN) {\n          this.ws?.close()\n        }\n      }\n    }\n  }\n\n  private async runQueue() {\n    this.queueRunning = true\n    while (true) {\n      if (false === this.handleNext()) {\n        break\n      }\n      await yieldThread()\n    }\n    this.queueRunning = false\n  }\n\n  private handleNext(): undefined | false {\n    const json = this.incomingMessageQueue.dequeue()\n    if (!json) {\n      return false\n    }\n\n    // shortcut EVENT sub\n    const subid = getSubscriptionId(json)\n    if (subid) {\n      const so = this.openSubs.get(subid as string)\n      if (!so) {\n        // this is an EVENT message, but for a subscription we don't have, so just stop here\n        return\n      }\n\n      // this will be called only when this message is a EVENT message for a subscription we have\n      // we do this before parsing the JSON to not have to do that for duplicate events\n      //   since JSON parsing is slow\n      const id = getHex64(json, 'id')\n      const alreadyHave = so.alreadyHaveEvent?.(id)\n\n      // notify any interested client that the relay has this event\n      // (do this after alreadyHaveEvent() because the client may rely on this to answer that)\n      so.receivedEvent?.(this, id)\n\n      if (alreadyHave) {\n        // if we had already seen this event we can just stop here\n        return\n      }\n    }\n\n    try {\n      let data = JSON.parse(json)\n      // we won't do any checks against the data since all failures (i.e. invalid messages from relays)\n      // will naturally be caught by the encompassing try..catch block\n\n      switch (data[0]) {\n        case 'EVENT': {\n          const so = this.openSubs.get(data[1] as string) as Subscription\n          const event = data[2] as NostrEvent\n          if (this.verifyEvent(event) && matchFilters(so.filters, event)) {\n            so.onevent(event)\n          }\n          if (!so.lastEmitted || so.lastEmitted < event.created_at) so.lastEmitted = event.created_at\n          return\n        }\n        case 'COUNT': {\n          const id: string = data[1]\n          const payload = data[2] as { count: number }\n          const cr = this.openCountRequests.get(id) as CountResolver\n          if (cr) {\n            cr.resolve(payload.count)\n            this.openCountRequests.delete(id)\n          }\n          return\n        }\n        case 'EOSE': {\n          const so = this.openSubs.get(data[1] as string)\n          if (!so) return\n          so.receivedEose()\n          return\n        }\n        case 'OK': {\n          const id: string = data[1]\n          const ok: boolean = data[2]\n          const reason: string = data[3]\n          const ep = this.openEventPublishes.get(id) as EventPublishResolver\n          if (ep) {\n            clearTimeout(ep.timeout)\n            if (ok) ep.resolve(reason)\n            else ep.reject(new Error(reason))\n            this.openEventPublishes.delete(id)\n          }\n          return\n        }\n        case 'CLOSED': {\n          const id: string = data[1]\n          const so = this.openSubs.get(id)\n          if (!so) return\n          so.closed = true\n          so.close(data[2] as string)\n          return\n        }\n        case 'NOTICE': {\n          this.onnotice(data[1] as string)\n          return\n        }\n        case 'AUTH': {\n          this.challenge = data[1] as string\n          if (this.onauth) {\n            this.auth(this.onauth)\n          }\n          return\n        }\n        default: {\n          const so = this.openSubs.get(data[1])\n          so?.oncustom?.(data)\n          return\n        }\n      }\n    } catch (err) {\n      return\n    }\n  }\n\n  public async send(message: string) {\n    if (!this.connectionPromise) throw new SendingOnClosedConnection(message, this.url)\n\n    this.connectionPromise.then(() => {\n      this.ws?.send(message)\n    })\n  }\n\n  public async auth(signAuthEvent: (evt: EventTemplate) => Promise<VerifiedEvent>): Promise<string> {\n    const challenge = this.challenge\n    if (!challenge) throw new Error(\"can't perform auth, no challenge was received\")\n    if (this.authPromise) return this.authPromise\n\n    this.authPromise = new Promise<string>(async (resolve, reject) => {\n      try {\n        let evt = await signAuthEvent(makeAuthEvent(this.url, challenge))\n        let timeout = setTimeout(() => {\n          let ep = this.openEventPublishes.get(evt.id) as EventPublishResolver\n          if (ep) {\n            ep.reject(new Error('auth timed out'))\n            this.openEventPublishes.delete(evt.id)\n          }\n        }, this.publishTimeout)\n        this.openEventPublishes.set(evt.id, { resolve, reject, timeout })\n        this.send('[\"AUTH\",' + JSON.stringify(evt) + ']')\n      } catch (err) {\n        console.warn('subscribe auth function failed:', err)\n      }\n    })\n    return this.authPromise\n  }\n\n  public async publish(event: Event): Promise<string> {\n    const ret = new Promise<string>((resolve, reject) => {\n      const timeout = setTimeout(() => {\n        const ep = this.openEventPublishes.get(event.id) as EventPublishResolver\n        if (ep) {\n          ep.reject(new Error('publish timed out'))\n          this.openEventPublishes.delete(event.id)\n        }\n      }, this.publishTimeout)\n      this.openEventPublishes.set(event.id, { resolve, reject, timeout })\n    })\n    this.send('[\"EVENT\",' + JSON.stringify(event) + ']')\n    return ret\n  }\n\n  public async count(filters: Filter[], params: { id?: string | null }): Promise<number> {\n    this.serial++\n    const id = params?.id || 'count:' + this.serial\n    const ret = new Promise<number>((resolve, reject) => {\n      this.openCountRequests.set(id, { resolve, reject })\n    })\n    this.send('[\"COUNT\",\"' + id + '\",' + JSON.stringify(filters).substring(1))\n    return ret\n  }\n\n  public subscribe(\n    filters: Filter[],\n    params: Partial<SubscriptionParams> & { label?: string; id?: string },\n  ): Subscription {\n    const sub = this.prepareSubscription(filters, params)\n    sub.fire()\n\n    if (params.abort) {\n      params.abort.onabort = () => sub.close(String(params.abort!.reason || '<aborted>'))\n    }\n\n    return sub\n  }\n\n  public prepareSubscription(\n    filters: Filter[],\n    params: Partial<SubscriptionParams> & { label?: string; id?: string },\n  ): Subscription {\n    this.serial++\n    const id = params.id || (params.label ? params.label + ':' : 'sub:') + this.serial\n    const subscription = new Subscription(this, id, filters, params)\n    this.openSubs.set(id, subscription)\n    return subscription\n  }\n\n  public close() {\n    this.closedIntentionally = true\n    if (this.reconnectTimeoutHandle) {\n      clearTimeout(this.reconnectTimeoutHandle)\n      this.reconnectTimeoutHandle = undefined\n    }\n    if (this.pingIntervalHandle) {\n      clearInterval(this.pingIntervalHandle)\n      this.pingIntervalHandle = undefined\n    }\n    this.closeAllSubscriptions('relay connection closed by us')\n    this._connected = false\n    this.onclose?.()\n    if (this.ws?.readyState === this._WebSocket.OPEN) {\n      this.ws?.close()\n    }\n  }\n\n  // this is the function assigned to this.ws.onmessage\n  // it's exposed for testing and debugging purposes\n  public _onmessage(ev: MessageEvent<any>) {\n    this.incomingMessageQueue.enqueue(ev.data as string)\n    if (!this.queueRunning) {\n      this.runQueue()\n    }\n  }\n}\n\nexport class Subscription {\n  public readonly relay: AbstractRelay\n  public readonly id: string\n\n  public lastEmitted: number | undefined\n  public closed: boolean = false\n  public eosed: boolean = false\n  public filters: Filter[]\n  public alreadyHaveEvent: ((id: string) => boolean) | undefined\n  public receivedEvent: ((relay: AbstractRelay, id: string) => void) | undefined\n\n  public onevent: (evt: Event) => void\n  public oneose: (() => void) | undefined\n  public onclose: ((reason: string) => void) | undefined\n\n  // will get any messages that have this subscription id as their second item and are not default standard\n  public oncustom: ((msg: string[]) => void) | undefined\n\n  public eoseTimeout: number\n  private eoseTimeoutHandle: ReturnType<typeof setTimeout> | undefined\n\n  constructor(relay: AbstractRelay, id: string, filters: Filter[], params: SubscriptionParams) {\n    if (filters.length === 0) throw new Error(\"subscription can't be created with zero filters\")\n\n    this.relay = relay\n    this.filters = filters\n    this.id = id\n    this.alreadyHaveEvent = params.alreadyHaveEvent\n    this.receivedEvent = params.receivedEvent\n    this.eoseTimeout = params.eoseTimeout || relay.baseEoseTimeout\n\n    this.oneose = params.oneose\n    this.onclose = params.onclose\n    this.onevent =\n      params.onevent ||\n      (event => {\n        console.warn(\n          `onevent() callback not defined for subscription '${this.id}' in relay ${this.relay.url}. event received:`,\n          event,\n        )\n      })\n  }\n\n  public fire() {\n    this.relay.send('[\"REQ\",\"' + this.id + '\",' + JSON.stringify(this.filters).substring(1))\n\n    // only now we start counting the eoseTimeout\n    this.eoseTimeoutHandle = setTimeout(this.receivedEose.bind(this), this.eoseTimeout)\n  }\n\n  public receivedEose() {\n    if (this.eosed) return\n    clearTimeout(this.eoseTimeoutHandle)\n    this.eosed = true\n    this.oneose?.()\n  }\n\n  public close(reason: string = 'closed by caller') {\n    if (!this.closed && this.relay.connected) {\n      // if the connection was closed by the user calling .close() we will send a CLOSE message\n      // otherwise this._open will be already set to false so we will skip this\n      try {\n        this.relay.send('[\"CLOSE\",' + JSON.stringify(this.id) + ']')\n      } catch (err) {\n        if (err instanceof SendingOnClosedConnection) {\n          /* doesn't matter, it's ok */\n        } else {\n          throw err\n        }\n      }\n      this.closed = true\n    }\n    this.relay.openSubs.delete(this.id)\n    this.onclose?.(reason)\n  }\n}\n\nexport type SubscriptionParams = {\n  onevent?: (evt: Event) => void\n  oneose?: () => void\n  onclose?: (reason: string) => void\n  alreadyHaveEvent?: (id: string) => boolean\n  receivedEvent?: (relay: AbstractRelay, id: string) => void\n  eoseTimeout?: number\n  abort?: AbortSignal\n}\n\nexport type CountResolver = {\n  resolve: (count: number) => void\n  reject: (err: Error) => void\n}\n\nexport type EventPublishResolver = {\n  resolve: (reason: string) => void\n  reject: (err: Error) => void\n  timeout: ReturnType<typeof setTimeout>\n}\n"],
+  "sourcesContent": ["/* global WebSocket */\n\nimport {\n  AbstractRelay as AbstractRelay,\n  SubscriptionParams,\n  Subscription,\n  type AbstractRelayConstructorOptions,\n} from './abstract-relay.ts'\nimport { normalizeURL } from './utils.ts'\n\nimport type { Event, EventTemplate, Nostr, VerifiedEvent } from './core.ts'\nimport { type Filter } from './filter.ts'\nimport { alwaysTrue } from './helpers.ts'\n\nexport type SubCloser = { close: (reason?: string) => void }\n\nexport type AbstractPoolConstructorOptions = AbstractRelayConstructorOptions & {\n  // automaticallyAuth takes a relay URL and should return null\n  // in case that relay shouldn't be authenticated against\n  // or a function to sign the AUTH event template otherwise (that function may still throw in case of failure)\n  automaticallyAuth?: (relayURL: string) => null | ((event: EventTemplate) => Promise<VerifiedEvent>)\n}\n\nexport type SubscribeManyParams = Omit<SubscriptionParams, 'onclose'> & {\n  maxWait?: number\n  abort?: AbortSignal\n  onclose?: (reasons: string[]) => void\n  onauth?: (event: EventTemplate) => Promise<VerifiedEvent>\n  id?: string\n  label?: string\n}\n\nexport class AbstractSimplePool {\n  protected relays: Map<string, AbstractRelay> = new Map()\n  public seenOn: Map<string, Set<AbstractRelay>> = new Map()\n  public trackRelays: boolean = false\n\n  public verifyEvent: Nostr['verifyEvent']\n  public enablePing: boolean | undefined\n  public enableReconnect: boolean\n  public automaticallyAuth?: (relayURL: string) => null | ((event: EventTemplate) => Promise<VerifiedEvent>)\n  public trustedRelayURLs: Set<string> = new Set()\n\n  private _WebSocket?: typeof WebSocket\n\n  constructor(opts: AbstractPoolConstructorOptions) {\n    this.verifyEvent = opts.verifyEvent\n    this._WebSocket = opts.websocketImplementation\n    this.enablePing = opts.enablePing\n    this.enableReconnect = opts.enableReconnect || false\n    this.automaticallyAuth = opts.automaticallyAuth\n  }\n\n  async ensureRelay(\n    url: string,\n    params?: {\n      connectionTimeout?: number\n      abort?: AbortSignal\n    },\n  ): Promise<AbstractRelay> {\n    url = normalizeURL(url)\n\n    let relay = this.relays.get(url)\n    if (!relay) {\n      relay = new AbstractRelay(url, {\n        verifyEvent: this.trustedRelayURLs.has(url) ? alwaysTrue : this.verifyEvent,\n        websocketImplementation: this._WebSocket,\n        enablePing: this.enablePing,\n        enableReconnect: this.enableReconnect,\n      })\n      relay.onclose = () => {\n        if (relay && !relay.enableReconnect) {\n          this.relays.delete(url)\n        }\n      }\n      this.relays.set(url, relay)\n    }\n\n    if (this.automaticallyAuth) {\n      const authSignerFn = this.automaticallyAuth(url)\n      if (authSignerFn) {\n        relay.onauth = authSignerFn\n      }\n    }\n\n    await relay.connect({\n      timeout: params?.connectionTimeout,\n      abort: params?.abort,\n    })\n\n    return relay\n  }\n\n  close(relays: string[]) {\n    relays.map(normalizeURL).forEach(url => {\n      this.relays.get(url)?.close()\n      this.relays.delete(url)\n    })\n  }\n\n  subscribe(relays: string[], filter: Filter, params: SubscribeManyParams): SubCloser {\n    const request: { url: string; filter: Filter }[] = []\n    for (let i = 0; i < relays.length; i++) {\n      const url = normalizeURL(relays[i])\n      if (!request.find(r => r.url === url)) {\n        request.push({ url, filter: filter })\n      }\n    }\n\n    return this.subscribeMap(request, params)\n  }\n\n  subscribeMany(relays: string[], filter: Filter, params: SubscribeManyParams): SubCloser {\n    const request: { url: string; filter: Filter }[] = []\n    const uniqUrls: string[] = []\n    for (let i = 0; i < relays.length; i++) {\n      const url = normalizeURL(relays[i])\n      if (uniqUrls.indexOf(url) === -1) {\n        uniqUrls.push(url)\n        request.push({ url, filter: filter })\n      }\n    }\n\n    return this.subscribeMap(request, params)\n  }\n\n  subscribeMap(requests: { url: string; filter: Filter }[], params: SubscribeManyParams): SubCloser {\n    const grouped = new Map<string, Filter[]>()\n    for (const req of requests) {\n      const { url, filter } = req\n      if (!grouped.has(url)) grouped.set(url, [])\n      grouped.get(url)!.push(filter)\n    }\n    const groupedRequests = Array.from(grouped.entries()).map(([url, filters]) => ({ url, filters }))\n\n    if (this.trackRelays) {\n      params.receivedEvent = (relay: AbstractRelay, id: string) => {\n        let set = this.seenOn.get(id)\n        if (!set) {\n          set = new Set()\n          this.seenOn.set(id, set)\n        }\n        set.add(relay)\n      }\n    }\n\n    const _knownIds = new Set<string>()\n    const subs: Subscription[] = []\n\n    // batch all EOSEs into a single\n    const eosesReceived: boolean[] = []\n    let handleEose = (i: number) => {\n      if (eosesReceived[i]) return // do not act twice for the same relay\n      eosesReceived[i] = true\n      if (eosesReceived.filter(a => a).length === groupedRequests.length) {\n        params.oneose?.()\n        handleEose = () => {}\n      }\n    }\n    // batch all closes into a single\n    const closesReceived: string[] = []\n    let handleClose = (i: number, reason: string) => {\n      if (closesReceived[i]) return // do not act twice for the same relay\n      handleEose(i)\n      closesReceived[i] = reason\n      if (closesReceived.filter(a => a).length === groupedRequests.length) {\n        params.onclose?.(closesReceived)\n        handleClose = () => {}\n      }\n    }\n\n    const localAlreadyHaveEventHandler = (id: string) => {\n      if (params.alreadyHaveEvent?.(id)) {\n        return true\n      }\n      const have = _knownIds.has(id)\n      _knownIds.add(id)\n      return have\n    }\n\n    // open a subscription in all given relays\n    const allOpened = Promise.all(\n      groupedRequests.map(async ({ url, filters }, i) => {\n        let relay: AbstractRelay\n        try {\n          relay = await this.ensureRelay(url, {\n            connectionTimeout: params.maxWait ? Math.max(params.maxWait * 0.8, params.maxWait - 1000) : undefined,\n            abort: params.abort,\n          })\n        } catch (err) {\n          handleClose(i, (err as any)?.message || String(err))\n          return\n        }\n\n        let subscription = relay.subscribe(filters, {\n          ...params,\n          oneose: () => handleEose(i),\n          onclose: reason => {\n            if (reason.startsWith('auth-required: ') && params.onauth) {\n              relay\n                .auth(params.onauth)\n                .then(() => {\n                  relay.subscribe(filters, {\n                    ...params,\n                    oneose: () => handleEose(i),\n                    onclose: reason => {\n                      handleClose(i, reason) // the second time we won't try to auth anymore\n                    },\n                    alreadyHaveEvent: localAlreadyHaveEventHandler,\n                    eoseTimeout: params.maxWait,\n                    abort: params.abort,\n                  })\n                })\n                .catch(err => {\n                  handleClose(i, `auth was required and attempted, but failed with: ${err}`)\n                })\n            } else {\n              handleClose(i, reason)\n            }\n          },\n          alreadyHaveEvent: localAlreadyHaveEventHandler,\n          eoseTimeout: params.maxWait,\n          abort: params.abort,\n        })\n\n        subs.push(subscription)\n      }),\n    )\n\n    return {\n      async close(reason?: string) {\n        await allOpened\n        subs.forEach(sub => {\n          sub.close(reason)\n        })\n      },\n    }\n  }\n\n  subscribeEose(\n    relays: string[],\n    filter: Filter,\n    params: Pick<SubscribeManyParams, 'label' | 'id' | 'onevent' | 'onclose' | 'maxWait' | 'onauth'>,\n  ): SubCloser {\n    const subcloser = this.subscribe(relays, filter, {\n      ...params,\n      oneose() {\n        subcloser.close('closed automatically on eose')\n      },\n    })\n    return subcloser\n  }\n\n  subscribeManyEose(\n    relays: string[],\n    filter: Filter,\n    params: Pick<SubscribeManyParams, 'label' | 'id' | 'onevent' | 'onclose' | 'maxWait' | 'onauth'>,\n  ): SubCloser {\n    const subcloser = this.subscribeMany(relays, filter, {\n      ...params,\n      oneose() {\n        subcloser.close('closed automatically on eose')\n      },\n    })\n    return subcloser\n  }\n\n  async querySync(\n    relays: string[],\n    filter: Filter,\n    params?: Pick<SubscribeManyParams, 'label' | 'id' | 'maxWait'>,\n  ): Promise<Event[]> {\n    return new Promise(async resolve => {\n      const events: Event[] = []\n      this.subscribeEose(relays, filter, {\n        ...params,\n        onevent(event: Event) {\n          events.push(event)\n        },\n        onclose(_: string[]) {\n          resolve(events)\n        },\n      })\n    })\n  }\n\n  async get(\n    relays: string[],\n    filter: Filter,\n    params?: Pick<SubscribeManyParams, 'label' | 'id' | 'maxWait'>,\n  ): Promise<Event | null> {\n    filter.limit = 1\n    const events = await this.querySync(relays, filter, params)\n    events.sort((a, b) => b.created_at - a.created_at)\n    return events[0] || null\n  }\n\n  publish(\n    relays: string[],\n    event: Event,\n    options?: { onauth?: (evt: EventTemplate) => Promise<VerifiedEvent> },\n  ): Promise<string>[] {\n    return relays.map(normalizeURL).map(async (url, i, arr) => {\n      if (arr.indexOf(url) !== i) {\n        // duplicate\n        return Promise.reject('duplicate url')\n      }\n\n      let r = await this.ensureRelay(url)\n      return r\n        .publish(event)\n        .catch(async err => {\n          if (err instanceof Error && err.message.startsWith('auth-required: ') && options?.onauth) {\n            await r.auth(options.onauth)\n            return r.publish(event) // retry\n          }\n          throw err\n        })\n        .then(reason => {\n          if (this.trackRelays) {\n            let set = this.seenOn.get(event.id)\n            if (!set) {\n              set = new Set()\n              this.seenOn.set(event.id, set)\n            }\n            set.add(r)\n          }\n          return reason\n        })\n    })\n  }\n\n  listConnectionStatus(): Map<string, boolean> {\n    const map = new Map<string, boolean>()\n    this.relays.forEach((relay, url) => map.set(url, relay.connected))\n\n    return map\n  }\n\n  destroy(): void {\n    this.relays.forEach(conn => conn.close())\n    this.relays = new Map()\n  }\n}\n", "export interface Nostr {\n  generateSecretKey(): Uint8Array\n  getPublicKey(secretKey: Uint8Array): string\n  finalizeEvent(event: EventTemplate, secretKey: Uint8Array): VerifiedEvent\n  verifyEvent(event: Event): event is VerifiedEvent\n}\n\n/** Designates a verified event signature. */\nexport const verifiedSymbol = Symbol('verified')\n\nexport type NostrEvent = {\n  kind: number\n  tags: string[][]\n  content: string\n  created_at: number\n  pubkey: string\n  id: string\n  sig: string\n  [verifiedSymbol]?: boolean\n}\n\nexport type Event = NostrEvent\nexport type EventTemplate = Pick<Event, 'kind' | 'tags' | 'content' | 'created_at'>\nexport type UnsignedEvent = Pick<Event, 'kind' | 'tags' | 'content' | 'created_at' | 'pubkey'>\n\n/** An event whose signature has been verified. */\nexport interface VerifiedEvent extends Event {\n  [verifiedSymbol]: true\n}\n\nconst isRecord = (obj: unknown): obj is Record<string, unknown> => obj instanceof Object\n\nexport function validateEvent<T>(event: T): event is T & UnsignedEvent {\n  if (!isRecord(event)) return false\n  if (typeof event.kind !== 'number') return false\n  if (typeof event.content !== 'string') return false\n  if (typeof event.created_at !== 'number') return false\n  if (typeof event.pubkey !== 'string') return false\n  if (!event.pubkey.match(/^[a-f0-9]{64}$/)) return false\n\n  if (!Array.isArray(event.tags)) return false\n  for (let i = 0; i < event.tags.length; i++) {\n    let tag = event.tags[i]\n    if (!Array.isArray(tag)) return false\n    for (let j = 0; j < tag.length; j++) {\n      if (typeof tag[j] !== 'string') return false\n    }\n  }\n\n  return true\n}\n\n/**\n * Sort events in reverse-chronological order by the `created_at` timestamp,\n * and then by the event `id` (lexicographically) in case of ties.\n * This mutates the array.\n */\nexport function sortEvents(events: Event[]): Event[] {\n  return events.sort((a: NostrEvent, b: NostrEvent): number => {\n    if (a.created_at !== b.created_at) {\n      return b.created_at - a.created_at\n    }\n    return a.id.localeCompare(b.id)\n  })\n}\n", "import type { Event } from './core.ts'\n\nexport const utf8Decoder: TextDecoder = new TextDecoder('utf-8')\nexport const utf8Encoder: TextEncoder = new TextEncoder()\n\nexport { bytesToHex, hexToBytes } from '@noble/hashes/utils.js'\n\nexport function normalizeURL(url: string): string {\n  try {\n    if (url.indexOf('://') === -1) url = 'wss://' + url\n    let p = new URL(url)\n    if (p.protocol === 'http:') p.protocol = 'ws:'\n    else if (p.protocol === 'https:') p.protocol = 'wss:'\n    p.pathname = p.pathname.replace(/\\/+/g, '/')\n    if (p.pathname.endsWith('/')) p.pathname = p.pathname.slice(0, -1)\n    if ((p.port === '80' && p.protocol === 'ws:') || (p.port === '443' && p.protocol === 'wss:')) p.port = ''\n    p.searchParams.sort()\n    p.hash = ''\n    return p.toString()\n  } catch (e) {\n    throw new Error(`Invalid URL: ${url}`)\n  }\n}\n\nexport function insertEventIntoDescendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return b.created_at - event.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function insertEventIntoAscendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return event.created_at - b.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function binarySearch<T>(arr: T[], compare: (b: T) => number): [number, boolean] {\n  let start = 0\n  let end = arr.length - 1\n\n  while (start <= end) {\n    const mid = Math.floor((start + end) / 2)\n    const cmp = compare(arr[mid])\n\n    if (cmp === 0) {\n      return [mid, true]\n    }\n\n    if (cmp < 0) {\n      end = mid - 1\n    } else {\n      start = mid + 1\n    }\n  }\n\n  return [start, false]\n}\n\nexport class QueueNode<V> {\n  public value: V\n  public next: QueueNode<V> | null = null\n  public prev: QueueNode<V> | null = null\n\n  constructor(message: V) {\n    this.value = message\n  }\n}\n\nexport class Queue<V> {\n  public first: QueueNode<V> | null\n  public last: QueueNode<V> | null\n\n  constructor() {\n    this.first = null\n    this.last = null\n  }\n\n  enqueue(value: V): boolean {\n    const newNode = new QueueNode(value)\n    if (!this.last) {\n      // list is empty\n      this.first = newNode\n      this.last = newNode\n    } else if (this.last === this.first) {\n      // list has a single element\n      this.last = newNode\n      this.last.prev = this.first\n      this.first.next = newNode\n    } else {\n      // list has elements, add as last\n      newNode.prev = this.last\n      this.last.next = newNode\n      this.last = newNode\n    }\n    return true\n  }\n\n  dequeue(): V | null {\n    if (!this.first) return null\n\n    if (this.first === this.last) {\n      const target = this.first\n      this.first = null\n      this.last = null\n      return target.value\n    }\n\n    const target = this.first\n    this.first = target.next\n    if (this.first) {\n      this.first.prev = null // fix: clean up prev pointer\n    }\n\n    return target.value\n  }\n}\n", "import { NostrEvent, validateEvent } from './pure.ts'\n\n/** Events are **regular**, which means they're all expected to be stored by relays. */\nexport function isRegularKind(kind: number): boolean {\n  return kind < 10000 && kind !== 0 && kind !== 3\n}\n\n/** Events are **replaceable**, which means that, for each combination of `pubkey` and `kind`, only the latest event is expected to (SHOULD) be stored by relays, older versions are expected to be discarded. */\nexport function isReplaceableKind(kind: number): boolean {\n  return kind === 0 || kind === 3 || (10000 <= kind && kind < 20000)\n}\n\n/** Events are **ephemeral**, which means they are not expected to be stored by relays. */\nexport function isEphemeralKind(kind: number): boolean {\n  return 20000 <= kind && kind < 30000\n}\n\n/** Events are **addressable**, which means that, for each combination of `pubkey`, `kind` and the `d` tag, only the latest event is expected to be stored by relays, older versions are expected to be discarded. */\nexport function isAddressableKind(kind: number): boolean {\n  return 30000 <= kind && kind < 40000\n}\n\n/** Classification of the event kind. */\nexport type KindClassification = 'regular' | 'replaceable' | 'ephemeral' | 'parameterized' | 'unknown'\n\n/** Determine the classification of this kind of event if known, or `unknown`. */\nexport function classifyKind(kind: number): KindClassification {\n  if (isRegularKind(kind)) return 'regular'\n  if (isReplaceableKind(kind)) return 'replaceable'\n  if (isEphemeralKind(kind)) return 'ephemeral'\n  if (isAddressableKind(kind)) return 'parameterized'\n  return 'unknown'\n}\n\nexport function isKind<T extends number>(event: unknown, kind: T | Array<T>): event is NostrEvent & { kind: T } {\n  const kindAsArray: number[] = kind instanceof Array ? kind : [kind]\n  return (validateEvent(event) && kindAsArray.includes(event.kind)) || false\n}\n\nexport const Metadata = 0\nexport type Metadata = typeof Metadata\nexport const ShortTextNote = 1\nexport type ShortTextNote = typeof ShortTextNote\nexport const RecommendRelay = 2\nexport type RecommendRelay = typeof RecommendRelay\nexport const Contacts = 3\nexport type Contacts = typeof Contacts\nexport const EncryptedDirectMessage = 4\nexport type EncryptedDirectMessage = typeof EncryptedDirectMessage\nexport const EventDeletion = 5\nexport type EventDeletion = typeof EventDeletion\nexport const Repost = 6\nexport type Repost = typeof Repost\nexport const Reaction = 7\nexport type Reaction = typeof Reaction\nexport const BadgeAward = 8\nexport type BadgeAward = typeof BadgeAward\nexport const ChatMessage = 9\nexport type ChatMessage = typeof ChatMessage\nexport const ForumThread = 11\nexport type ForumThread = typeof ForumThread\nexport const Seal = 13\nexport type Seal = typeof Seal\nexport const PrivateDirectMessage = 14\nexport type PrivateDirectMessage = typeof PrivateDirectMessage\nexport const FileMessage = 15\nexport type FileMessage = typeof FileMessage\nexport const GenericRepost = 16\nexport type GenericRepost = typeof GenericRepost\nexport const Photo = 20\nexport type Photo = typeof Photo\nexport const NormalVideo = 21\nexport type NormalVideo = typeof NormalVideo\nexport const ShortVideo = 22\nexport type ShortVideo = typeof ShortVideo\nexport const ChannelCreation = 40\nexport type ChannelCreation = typeof ChannelCreation\nexport const ChannelMetadata = 41\nexport type ChannelMetadata = typeof ChannelMetadata\nexport const ChannelMessage = 42\nexport type ChannelMessage = typeof ChannelMessage\nexport const ChannelHideMessage = 43\nexport type ChannelHideMessage = typeof ChannelHideMessage\nexport const ChannelMuteUser = 44\nexport type ChannelMuteUser = typeof ChannelMuteUser\nexport const OpenTimestamps = 1040\nexport type OpenTimestamps = typeof OpenTimestamps\nexport const GiftWrap = 1059\nexport type GiftWrap = typeof GiftWrap\nexport const Poll = 1068\nexport type Poll = typeof Poll\nexport const FileMetadata = 1063\nexport type FileMetadata = typeof FileMetadata\nexport const Comment = 1111\nexport type Comment = typeof Comment\nexport const LiveChatMessage = 1311\nexport type LiveChatMessage = typeof LiveChatMessage\nexport const Voice = 1222\nexport type Voice = typeof Voice\nexport const VoiceComment = 1244\nexport type VoiceComment = typeof VoiceComment\nexport const ProblemTracker = 1971\nexport type ProblemTracker = typeof ProblemTracker\nexport const Report = 1984\nexport type Report = typeof Report\nexport const Reporting = 1984\nexport type Reporting = typeof Reporting\nexport const Label = 1985\nexport type Label = typeof Label\nexport const CommunityPostApproval = 4550\nexport type CommunityPostApproval = typeof CommunityPostApproval\nexport const JobRequest = 5999\nexport type JobRequest = typeof JobRequest\nexport const JobResult = 6999\nexport type JobResult = typeof JobResult\nexport const JobFeedback = 7000\nexport type JobFeedback = typeof JobFeedback\nexport const ZapGoal = 9041\nexport type ZapGoal = typeof ZapGoal\nexport const ZapRequest = 9734\nexport type ZapRequest = typeof ZapRequest\nexport const Zap = 9735\nexport type Zap = typeof Zap\nexport const Highlights = 9802\nexport type Highlights = typeof Highlights\nexport const PollResponse = 1018\nexport type PollResponse = typeof PollResponse\nexport const Mutelist = 10000\nexport type Mutelist = typeof Mutelist\nexport const Pinlist = 10001\nexport type Pinlist = typeof Pinlist\nexport const RelayList = 10002\nexport type RelayList = typeof RelayList\nexport const BookmarkList = 10003\nexport type BookmarkList = typeof BookmarkList\nexport const CommunitiesList = 10004\nexport type CommunitiesList = typeof CommunitiesList\nexport const PublicChatsList = 10005\nexport type PublicChatsList = typeof PublicChatsList\nexport const BlockedRelaysList = 10006\nexport type BlockedRelaysList = typeof BlockedRelaysList\nexport const SearchRelaysList = 10007\nexport type SearchRelaysList = typeof SearchRelaysList\nexport const FavoriteRelays = 10012\nexport type FavoriteRelays = typeof FavoriteRelays\nexport const InterestsList = 10015\nexport type InterestsList = typeof InterestsList\nexport const UserEmojiList = 10030\nexport type UserEmojiList = typeof UserEmojiList\nexport const DirectMessageRelaysList = 10050\nexport type DirectMessageRelaysList = typeof DirectMessageRelaysList\nexport const FileServerPreference = 10096\nexport type FileServerPreference = typeof FileServerPreference\nexport const BlossomServerList = 10063\nexport type BlossomServerList = typeof BlossomServerList\nexport const NWCWalletInfo = 13194\nexport type NWCWalletInfo = typeof NWCWalletInfo\nexport const LightningPubRPC = 21000\nexport type LightningPubRPC = typeof LightningPubRPC\nexport const ClientAuth = 22242\nexport type ClientAuth = typeof ClientAuth\nexport const NWCWalletRequest = 23194\nexport type NWCWalletRequest = typeof NWCWalletRequest\nexport const NWCWalletResponse = 23195\nexport type NWCWalletResponse = typeof NWCWalletResponse\nexport const NostrConnect = 24133\nexport type NostrConnect = typeof NostrConnect\nexport const HTTPAuth = 27235\nexport type HTTPAuth = typeof HTTPAuth\nexport const Followsets = 30000\nexport type Followsets = typeof Followsets\nexport const Genericlists = 30001\nexport type Genericlists = typeof Genericlists\nexport const Relaysets = 30002\nexport type Relaysets = typeof Relaysets\nexport const Bookmarksets = 30003\nexport type Bookmarksets = typeof Bookmarksets\nexport const Curationsets = 30004\nexport type Curationsets = typeof Curationsets\nexport const ProfileBadges = 30008\nexport type ProfileBadges = typeof ProfileBadges\nexport const BadgeDefinition = 30009\nexport type BadgeDefinition = typeof BadgeDefinition\nexport const Interestsets = 30015\nexport type Interestsets = typeof Interestsets\nexport const CreateOrUpdateStall = 30017\nexport type CreateOrUpdateStall = typeof CreateOrUpdateStall\nexport const CreateOrUpdateProduct = 30018\nexport type CreateOrUpdateProduct = typeof CreateOrUpdateProduct\nexport const LongFormArticle = 30023\nexport type LongFormArticle = typeof LongFormArticle\nexport const DraftLong = 30024\nexport type DraftLong = typeof DraftLong\nexport const Emojisets = 30030\nexport type Emojisets = typeof Emojisets\nexport const Application = 30078\nexport type Application = typeof Application\nexport const LiveEvent = 30311\nexport type LiveEvent = typeof LiveEvent\nexport const UserStatuses = 30315\nexport type UserStatuses = typeof UserStatuses\nexport const ClassifiedListing = 30402\nexport type ClassifiedListing = typeof ClassifiedListing\nexport const DraftClassifiedListing = 30403\nexport type DraftClassifiedListing = typeof DraftClassifiedListing\nexport const Date = 31922\nexport type Date = typeof Date\nexport const Time = 31923\nexport type Time = typeof Time\nexport const Calendar = 31924\nexport type Calendar = typeof Calendar\nexport const CalendarEventRSVP = 31925\nexport type CalendarEventRSVP = typeof CalendarEventRSVP\nexport const RelayReview = 31987\nexport type RelayReview = typeof RelayReview\nexport const Handlerrecommendation = 31989\nexport type Handlerrecommendation = typeof Handlerrecommendation\nexport const Handlerinformation = 31990\nexport type Handlerinformation = typeof Handlerinformation\nexport const CommunityDefinition = 34550\nexport type CommunityDefinition = typeof CommunityDefinition\nexport const GroupMetadata = 39000\nexport type GroupMetadata = typeof GroupMetadata\n", "import { Event } from './core.ts'\nimport { isAddressableKind, isReplaceableKind } from './kinds.ts'\n\nexport type Filter = {\n  ids?: string[]\n  kinds?: number[]\n  authors?: string[]\n  since?: number\n  until?: number\n  limit?: number\n  search?: string\n  [key: `#${string}`]: string[] | undefined\n}\n\nexport function matchFilter(filter: Filter, event: Event): boolean {\n  if (filter.ids && filter.ids.indexOf(event.id) === -1) {\n    return false\n  }\n  if (filter.kinds && filter.kinds.indexOf(event.kind) === -1) {\n    return false\n  }\n  if (filter.authors && filter.authors.indexOf(event.pubkey) === -1) {\n    return false\n  }\n\n  for (let f in filter) {\n    if (f[0] === '#') {\n      let tagName = f.slice(1)\n      let values = filter[`#${tagName}`]\n      if (values && !event.tags.find(([t, v]) => t === f.slice(1) && values!.indexOf(v) !== -1)) return false\n    }\n  }\n\n  if (filter.since && event.created_at < filter.since) return false\n  if (filter.until && event.created_at > filter.until) return false\n\n  return true\n}\n\nexport function matchFilters(filters: Filter[], event: Event): boolean {\n  for (let i = 0; i < filters.length; i++) {\n    if (matchFilter(filters[i], event)) {\n      return true\n    }\n  }\n  return false\n}\n\nexport function mergeFilters(...filters: Filter[]): Filter {\n  let result: Filter = {}\n  for (let i = 0; i < filters.length; i++) {\n    let filter = filters[i]\n    Object.entries(filter).forEach(([property, values]) => {\n      if (property === 'kinds' || property === 'ids' || property === 'authors' || property[0] === '#') {\n        // @ts-ignore\n        result[property] = result[property] || []\n        // @ts-ignore\n        for (let v = 0; v < values.length; v++) {\n          // @ts-ignore\n          let value = values[v]\n          // @ts-ignore\n          if (!result[property].includes(value)) result[property].push(value)\n        }\n      }\n    })\n\n    if (filter.limit && (!result.limit || filter.limit > result.limit)) result.limit = filter.limit\n    if (filter.until && (!result.until || filter.until > result.until)) result.until = filter.until\n    if (filter.since && (!result.since || filter.since < result.since)) result.since = filter.since\n  }\n\n  return result\n}\n\n/**\n * Calculate the intrinsic limit of a filter.\n * This function returns a positive integer, or `Infinity` if there is no intrinsic limit.\n */\nexport function getFilterLimit(filter: Filter): number {\n  if (filter.ids && !filter.ids.length) return 0\n  if (filter.kinds && !filter.kinds.length) return 0\n  if (filter.authors && !filter.authors.length) return 0\n\n  for (const [key, value] of Object.entries(filter)) {\n    if (key[0] === '#' && Array.isArray(value) && !value.length) return 0\n  }\n\n  return Math.min(\n    // The `limit` property creates an artificial limit.\n    Math.max(0, filter.limit ?? Infinity),\n\n    // There can only be one event per `id`.\n    filter.ids?.length ?? Infinity,\n\n    // Replaceable events are limited by the number of authors and kinds.\n    filter.authors?.length && filter.kinds?.every(kind => isReplaceableKind(kind))\n      ? filter.authors.length * filter.kinds.length\n      : Infinity,\n\n    // Parameterized replaceable events are limited by the number of authors, kinds, and \"d\" tags.\n    filter.authors?.length && filter.kinds?.every(kind => isAddressableKind(kind)) && filter['#d']?.length\n      ? filter.authors.length * filter.kinds.length * filter['#d'].length\n      : Infinity,\n  )\n}\n", "export function getHex64(json: string, field: string): string {\n  let len = field.length + 3\n  let idx = json.indexOf(`\"${field}\":`) + len\n  let s = json.slice(idx).indexOf(`\"`) + idx + 1\n  return json.slice(s, s + 64)\n}\n\nexport function getInt(json: string, field: string): number {\n  let len = field.length\n  let idx = json.indexOf(`\"${field}\":`) + len + 3\n  let sliced = json.slice(idx)\n  let end = Math.min(sliced.indexOf(','), sliced.indexOf('}'))\n  return parseInt(sliced.slice(0, end), 10)\n}\n\nexport function getSubscriptionId(json: string): string | null {\n  let idx = json.slice(0, 22).indexOf(`\"EVENT\"`)\n  if (idx === -1) return null\n\n  let pstart = json.slice(idx + 7 + 1).indexOf(`\"`)\n  if (pstart === -1) return null\n  let start = idx + 7 + 1 + pstart\n\n  let pend = json.slice(start + 1, 80).indexOf(`\"`)\n  if (pend === -1) return null\n  let end = start + 1 + pend\n\n  return json.slice(start + 1, end)\n}\n\nexport function matchEventId(json: string, id: string): boolean {\n  return id === getHex64(json, 'id')\n}\n\nexport function matchEventPubkey(json: string, pubkey: string): boolean {\n  return pubkey === getHex64(json, 'pubkey')\n}\n\nexport function matchEventKind(json: string, kind: number): boolean {\n  return kind === getInt(json, 'kind')\n}\n", "import { EventTemplate } from './core.ts'\nimport { ClientAuth } from './kinds.ts'\n\n/**\n * creates an EventTemplate for an AUTH event to be signed.\n */\nexport function makeAuthEvent(relayURL: string, challenge: string): EventTemplate {\n  return {\n    kind: ClientAuth,\n    created_at: Math.floor(Date.now() / 1000),\n    tags: [\n      ['relay', relayURL],\n      ['challenge', challenge],\n    ],\n    content: '',\n  }\n}\n", "import { verifiedSymbol, type Event, type Nostr, VerifiedEvent } from './core.ts'\n\nexport async function yieldThread() {\n  return new Promise<void>((resolve, reject) => {\n    try {\n      // Check if MessageChannel is available\n      if (typeof MessageChannel !== 'undefined') {\n        const ch = new MessageChannel()\n        const handler = () => {\n          // @ts-ignore (typescript thinks this property should be called `removeListener`, but in fact it's `removeEventListener`)\n          ch.port1.removeEventListener('message', handler)\n          resolve()\n        }\n        // @ts-ignore (typescript thinks this property should be called `addListener`, but in fact it's `addEventListener`)\n        ch.port1.addEventListener('message', handler)\n        ch.port2.postMessage(0)\n        ch.port1.start()\n      } else {\n        if (typeof setImmediate !== 'undefined') {\n          setImmediate(resolve)\n        } else if (typeof setTimeout !== 'undefined') {\n          setTimeout(resolve, 0)\n        } else {\n          // Last resort - resolve immediately\n          resolve()\n        }\n      }\n    } catch (e) {\n      console.error('during yield: ', e)\n      reject(e)\n    }\n  })\n}\n\nexport const alwaysTrue: Nostr['verifyEvent'] = (t: Event): t is VerifiedEvent => {\n  t[verifiedSymbol] = true\n  return true\n}\n", "/* global WebSocket */\n\nimport type { Event, EventTemplate, VerifiedEvent, Nostr, NostrEvent } from './core.ts'\nimport { matchFilters, type Filter } from './filter.ts'\nimport { getHex64, getSubscriptionId } from './fakejson.ts'\nimport { Queue, normalizeURL } from './utils.ts'\nimport { makeAuthEvent } from './nip42.ts'\nimport { yieldThread } from './helpers.ts'\n\ntype RelayWebSocket = WebSocket & {\n  ping?(): void\n  on?(event: 'pong', listener: () => void): any\n}\n\nexport type AbstractRelayConstructorOptions = {\n  verifyEvent: Nostr['verifyEvent']\n  websocketImplementation?: typeof WebSocket\n  enablePing?: boolean\n  enableReconnect?: boolean\n}\n\nexport class SendingOnClosedConnection extends Error {\n  constructor(message: string, relay: string) {\n    super(`Tried to send message '${message} on a closed connection to ${relay}.`)\n    this.name = 'SendingOnClosedConnection'\n  }\n}\n\nexport class AbstractRelay {\n  public readonly url: string\n  private _connected: boolean = false\n\n  public onclose: (() => void) | null = null\n  public onnotice: (msg: string) => void = msg => console.debug(`NOTICE from ${this.url}: ${msg}`)\n  public onauth: undefined | ((evt: EventTemplate) => Promise<VerifiedEvent>)\n\n  public baseEoseTimeout: number = 4400\n  public publishTimeout: number = 4400\n  public pingFrequency: number = 29000\n  public pingTimeout: number = 20000\n  public resubscribeBackoff: number[] = [10000, 10000, 10000, 20000, 20000, 30000, 60000]\n  public openSubs: Map<string, Subscription> = new Map()\n  public enablePing: boolean | undefined\n  public enableReconnect: boolean\n  private reconnectTimeoutHandle: ReturnType<typeof setTimeout> | undefined\n  private pingIntervalHandle: ReturnType<typeof setInterval> | undefined\n  private reconnectAttempts: number = 0\n  private closedIntentionally: boolean = false\n\n  private connectionPromise: Promise<void> | undefined\n  private openCountRequests = new Map<string, CountResolver>()\n  private openEventPublishes = new Map<string, EventPublishResolver>()\n  private ws: RelayWebSocket | undefined\n  private incomingMessageQueue = new Queue<string>()\n  private queueRunning = false\n  private challenge: string | undefined\n  private authPromise: Promise<string> | undefined\n  private serial: number = 0\n  private verifyEvent: Nostr['verifyEvent']\n\n  private _WebSocket: typeof WebSocket\n\n  constructor(url: string, opts: AbstractRelayConstructorOptions) {\n    this.url = normalizeURL(url)\n    this.verifyEvent = opts.verifyEvent\n    this._WebSocket = opts.websocketImplementation || WebSocket\n    this.enablePing = opts.enablePing\n    this.enableReconnect = opts.enableReconnect || false\n  }\n\n  static async connect(\n    url: string,\n    opts: AbstractRelayConstructorOptions & Parameters<AbstractRelay['connect']>[0],\n  ): Promise<AbstractRelay> {\n    const relay = new AbstractRelay(url, opts)\n    await relay.connect(opts)\n    return relay\n  }\n\n  private closeAllSubscriptions(reason: string) {\n    for (let [_, sub] of this.openSubs) {\n      sub.close(reason)\n    }\n    this.openSubs.clear()\n\n    for (let [_, ep] of this.openEventPublishes) {\n      ep.reject(new Error(reason))\n    }\n    this.openEventPublishes.clear()\n\n    for (let [_, cr] of this.openCountRequests) {\n      cr.reject(new Error(reason))\n    }\n    this.openCountRequests.clear()\n  }\n\n  public get connected(): boolean {\n    return this._connected\n  }\n\n  private async reconnect(): Promise<void> {\n    const backoff = this.resubscribeBackoff[Math.min(this.reconnectAttempts, this.resubscribeBackoff.length - 1)]\n    this.reconnectAttempts++\n\n    this.reconnectTimeoutHandle = setTimeout(async () => {\n      try {\n        await this.connect()\n      } catch (err) {\n        // this will be called again through onclose/onerror\n      }\n    }, backoff)\n  }\n\n  private handleHardClose(reason: string) {\n    if (this.pingIntervalHandle) {\n      clearInterval(this.pingIntervalHandle)\n      this.pingIntervalHandle = undefined\n    }\n\n    this._connected = false\n    this.connectionPromise = undefined\n\n    const wasIntentional = this.closedIntentionally\n    this.closedIntentionally = false // reset for next time\n\n    this.onclose?.()\n\n    if (this.enableReconnect && !wasIntentional) {\n      this.reconnect()\n    } else {\n      this.closeAllSubscriptions(reason)\n    }\n  }\n\n  public async connect(opts?: { timeout?: number; abort?: AbortSignal }): Promise<void> {\n    let connectionTimeoutHandle: ReturnType<typeof setTimeout> | undefined\n\n    if (this.connectionPromise) return this.connectionPromise\n\n    this.challenge = undefined\n    this.authPromise = undefined\n    this.connectionPromise = new Promise((resolve, reject) => {\n      if (opts?.timeout) {\n        connectionTimeoutHandle = setTimeout(() => {\n          reject('connection timed out')\n          this.connectionPromise = undefined\n          this.onclose?.()\n          this.closeAllSubscriptions('relay connection timed out')\n        }, opts.timeout)\n      }\n\n      if (opts?.abort) {\n        opts.abort.onabort = reject\n      }\n\n      try {\n        this.ws = new this._WebSocket(this.url)\n      } catch (err) {\n        clearTimeout(connectionTimeoutHandle)\n        reject(err)\n        return\n      }\n\n      this.ws.onopen = () => {\n        if (this.reconnectTimeoutHandle) {\n          clearTimeout(this.reconnectTimeoutHandle)\n          this.reconnectTimeoutHandle = undefined\n        }\n        clearTimeout(connectionTimeoutHandle)\n        this._connected = true\n\n        const isReconnection = this.reconnectAttempts > 0\n        this.reconnectAttempts = 0\n\n        // resubscribe to all open subscriptions\n        for (const sub of this.openSubs.values()) {\n          sub.eosed = false\n          if (isReconnection) {\n            for (let f = 0; f < sub.filters.length; f++) {\n              if (sub.lastEmitted) {\n                sub.filters[f].since = sub.lastEmitted + 1\n              }\n            }\n          }\n          sub.fire()\n        }\n\n        if (this.enablePing) {\n          this.pingIntervalHandle = setInterval(() => this.pingpong(), this.pingFrequency)\n        }\n        resolve()\n      }\n\n      this.ws.onerror = ev => {\n        clearTimeout(connectionTimeoutHandle)\n        reject((ev as any).message || 'websocket error')\n        this.handleHardClose('relay connection errored')\n      }\n\n      this.ws.onclose = ev => {\n        clearTimeout(connectionTimeoutHandle)\n        reject((ev as any).message || 'websocket closed')\n        this.handleHardClose('relay connection closed')\n      }\n\n      this.ws.onmessage = this._onmessage.bind(this)\n    })\n\n    return this.connectionPromise\n  }\n\n  private waitForPingPong() {\n    return new Promise(resolve => {\n      // listen for pong\n      ;(this.ws as any).once('pong', () => resolve(true))\n      // send a ping\n      this.ws!.ping!()\n    })\n  }\n\n  private waitForDummyReq() {\n    return new Promise((resolve, reject) => {\n      if (!this.connectionPromise) return reject(new Error(`no connection to ${this.url}, can't ping`))\n\n      // make a dummy request with expected empty eose reply\n      // [\"REQ\", \"_\", {\"ids\":[\"aaaa...aaaa\"], \"limit\": 0}]\n      try {\n        const sub = this.subscribe(\n          [{ ids: ['aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'], limit: 0 }],\n          {\n            label: 'forced-ping',\n            oneose: () => {\n              resolve(true)\n              sub.close()\n            },\n            onclose() {\n              // if we get a CLOSED it's because the relay is alive\n              resolve(true)\n            },\n            eoseTimeout: this.pingTimeout + 1000,\n          },\n        )\n      } catch (err) {\n        reject(err)\n      }\n    })\n  }\n\n  // nodejs requires this magic here to ensure connections are closed when internet goes off and stuff\n  // in browsers it's done automatically. see https://github.com/nbd-wtf/nostr-tools/issues/491\n  private async pingpong() {\n    // if the websocket is connected\n    if (this.ws?.readyState === 1) {\n      // wait for either a ping-pong reply or a timeout\n      const result = await Promise.any([\n        // browsers don't have ping so use a dummy req\n        this.ws && this.ws.ping && (this.ws as any).once ? this.waitForPingPong() : this.waitForDummyReq(),\n        new Promise(res => setTimeout(() => res(false), this.pingTimeout)),\n      ])\n\n      if (!result) {\n        // pingpong closing socket\n        if (this.ws?.readyState === this._WebSocket.OPEN) {\n          this.ws?.close()\n        }\n      }\n    }\n  }\n\n  private async runQueue() {\n    this.queueRunning = true\n    while (true) {\n      if (false === this.handleNext()) {\n        break\n      }\n      await yieldThread()\n    }\n    this.queueRunning = false\n  }\n\n  private handleNext(): undefined | false {\n    const json = this.incomingMessageQueue.dequeue()\n    if (!json) {\n      return false\n    }\n\n    // shortcut EVENT sub\n    const subid = getSubscriptionId(json)\n    if (subid) {\n      const so = this.openSubs.get(subid as string)\n      if (!so) {\n        // this is an EVENT message, but for a subscription we don't have, so just stop here\n        return\n      }\n\n      // this will be called only when this message is a EVENT message for a subscription we have\n      // we do this before parsing the JSON to not have to do that for duplicate events\n      //   since JSON parsing is slow\n      const id = getHex64(json, 'id')\n      const alreadyHave = so.alreadyHaveEvent?.(id)\n\n      // notify any interested client that the relay has this event\n      // (do this after alreadyHaveEvent() because the client may rely on this to answer that)\n      so.receivedEvent?.(this, id)\n\n      if (alreadyHave) {\n        // if we had already seen this event we can just stop here\n        return\n      }\n    }\n\n    try {\n      let data = JSON.parse(json)\n      // we won't do any checks against the data since all failures (i.e. invalid messages from relays)\n      // will naturally be caught by the encompassing try..catch block\n\n      switch (data[0]) {\n        case 'EVENT': {\n          const so = this.openSubs.get(data[1] as string) as Subscription\n          const event = data[2] as NostrEvent\n          if (this.verifyEvent(event) && matchFilters(so.filters, event)) {\n            so.onevent(event)\n          }\n          if (!so.lastEmitted || so.lastEmitted < event.created_at) so.lastEmitted = event.created_at\n          return\n        }\n        case 'COUNT': {\n          const id: string = data[1]\n          const payload = data[2] as { count: number }\n          const cr = this.openCountRequests.get(id) as CountResolver\n          if (cr) {\n            cr.resolve(payload.count)\n            this.openCountRequests.delete(id)\n          }\n          return\n        }\n        case 'EOSE': {\n          const so = this.openSubs.get(data[1] as string)\n          if (!so) return\n          so.receivedEose()\n          return\n        }\n        case 'OK': {\n          const id: string = data[1]\n          const ok: boolean = data[2]\n          const reason: string = data[3]\n          const ep = this.openEventPublishes.get(id) as EventPublishResolver\n          if (ep) {\n            clearTimeout(ep.timeout)\n            if (ok) ep.resolve(reason)\n            else ep.reject(new Error(reason))\n            this.openEventPublishes.delete(id)\n          }\n          return\n        }\n        case 'CLOSED': {\n          const id: string = data[1]\n          const so = this.openSubs.get(id)\n          if (!so) return\n          so.closed = true\n          so.close(data[2] as string)\n          return\n        }\n        case 'NOTICE': {\n          this.onnotice(data[1] as string)\n          return\n        }\n        case 'AUTH': {\n          this.challenge = data[1] as string\n          if (this.onauth) {\n            this.auth(this.onauth)\n          }\n          return\n        }\n        default: {\n          const so = this.openSubs.get(data[1])\n          so?.oncustom?.(data)\n          return\n        }\n      }\n    } catch (err) {\n      return\n    }\n  }\n\n  public async send(message: string) {\n    if (!this.connectionPromise) throw new SendingOnClosedConnection(message, this.url)\n\n    this.connectionPromise.then(() => {\n      this.ws?.send(message)\n    })\n  }\n\n  public async auth(signAuthEvent: (evt: EventTemplate) => Promise<VerifiedEvent>): Promise<string> {\n    const challenge = this.challenge\n    if (!challenge) throw new Error(\"can't perform auth, no challenge was received\")\n    if (this.authPromise) return this.authPromise\n\n    this.authPromise = new Promise<string>(async (resolve, reject) => {\n      try {\n        let evt = await signAuthEvent(makeAuthEvent(this.url, challenge))\n        let timeout = setTimeout(() => {\n          let ep = this.openEventPublishes.get(evt.id) as EventPublishResolver\n          if (ep) {\n            ep.reject(new Error('auth timed out'))\n            this.openEventPublishes.delete(evt.id)\n          }\n        }, this.publishTimeout)\n        this.openEventPublishes.set(evt.id, { resolve, reject, timeout })\n        this.send('[\"AUTH\",' + JSON.stringify(evt) + ']')\n      } catch (err) {\n        console.warn('subscribe auth function failed:', err)\n      }\n    })\n    return this.authPromise\n  }\n\n  public async publish(event: Event): Promise<string> {\n    const ret = new Promise<string>((resolve, reject) => {\n      const timeout = setTimeout(() => {\n        const ep = this.openEventPublishes.get(event.id) as EventPublishResolver\n        if (ep) {\n          ep.reject(new Error('publish timed out'))\n          this.openEventPublishes.delete(event.id)\n        }\n      }, this.publishTimeout)\n      this.openEventPublishes.set(event.id, { resolve, reject, timeout })\n    })\n    this.send('[\"EVENT\",' + JSON.stringify(event) + ']')\n    return ret\n  }\n\n  public async count(filters: Filter[], params: { id?: string | null }): Promise<number> {\n    this.serial++\n    const id = params?.id || 'count:' + this.serial\n    const ret = new Promise<number>((resolve, reject) => {\n      this.openCountRequests.set(id, { resolve, reject })\n    })\n    this.send('[\"COUNT\",\"' + id + '\",' + JSON.stringify(filters).substring(1))\n    return ret\n  }\n\n  public subscribe(\n    filters: Filter[],\n    params: Partial<SubscriptionParams> & { label?: string; id?: string },\n  ): Subscription {\n    const sub = this.prepareSubscription(filters, params)\n    sub.fire()\n\n    if (params.abort) {\n      params.abort.onabort = () => sub.close(String(params.abort!.reason || '<aborted>'))\n    }\n\n    return sub\n  }\n\n  public prepareSubscription(\n    filters: Filter[],\n    params: Partial<SubscriptionParams> & { label?: string; id?: string },\n  ): Subscription {\n    this.serial++\n    const id = params.id || (params.label ? params.label + ':' : 'sub:') + this.serial\n    const subscription = new Subscription(this, id, filters, params)\n    this.openSubs.set(id, subscription)\n    return subscription\n  }\n\n  public close() {\n    this.closedIntentionally = true\n    if (this.reconnectTimeoutHandle) {\n      clearTimeout(this.reconnectTimeoutHandle)\n      this.reconnectTimeoutHandle = undefined\n    }\n    if (this.pingIntervalHandle) {\n      clearInterval(this.pingIntervalHandle)\n      this.pingIntervalHandle = undefined\n    }\n    this.closeAllSubscriptions('relay connection closed by us')\n    this._connected = false\n    this.onclose?.()\n    if (this.ws?.readyState === this._WebSocket.OPEN) {\n      this.ws?.close()\n    }\n  }\n\n  // this is the function assigned to this.ws.onmessage\n  // it's exposed for testing and debugging purposes\n  public _onmessage(ev: MessageEvent<any>) {\n    this.incomingMessageQueue.enqueue(ev.data as string)\n    if (!this.queueRunning) {\n      this.runQueue()\n    }\n  }\n}\n\nexport class Subscription {\n  public readonly relay: AbstractRelay\n  public readonly id: string\n\n  public lastEmitted: number | undefined\n  public closed: boolean = false\n  public eosed: boolean = false\n  public filters: Filter[]\n  public alreadyHaveEvent: ((id: string) => boolean) | undefined\n  public receivedEvent: ((relay: AbstractRelay, id: string) => void) | undefined\n\n  public onevent: (evt: Event) => void\n  public oneose: (() => void) | undefined\n  public onclose: ((reason: string) => void) | undefined\n\n  // will get any messages that have this subscription id as their second item and are not default standard\n  public oncustom: ((msg: string[]) => void) | undefined\n\n  public eoseTimeout: number\n  private eoseTimeoutHandle: ReturnType<typeof setTimeout> | undefined\n\n  constructor(relay: AbstractRelay, id: string, filters: Filter[], params: SubscriptionParams) {\n    if (filters.length === 0) throw new Error(\"subscription can't be created with zero filters\")\n\n    this.relay = relay\n    this.filters = filters\n    this.id = id\n    this.alreadyHaveEvent = params.alreadyHaveEvent\n    this.receivedEvent = params.receivedEvent\n    this.eoseTimeout = params.eoseTimeout || relay.baseEoseTimeout\n\n    this.oneose = params.oneose\n    this.onclose = params.onclose\n    this.onevent =\n      params.onevent ||\n      (event => {\n        console.warn(\n          `onevent() callback not defined for subscription '${this.id}' in relay ${this.relay.url}. event received:`,\n          event,\n        )\n      })\n  }\n\n  public fire() {\n    this.relay.send('[\"REQ\",\"' + this.id + '\",' + JSON.stringify(this.filters).substring(1))\n\n    // only now we start counting the eoseTimeout\n    this.eoseTimeoutHandle = setTimeout(this.receivedEose.bind(this), this.eoseTimeout)\n  }\n\n  public receivedEose() {\n    if (this.eosed) return\n    clearTimeout(this.eoseTimeoutHandle)\n    this.eosed = true\n    this.oneose?.()\n  }\n\n  public close(reason: string = 'closed by caller') {\n    if (!this.closed && this.relay.connected) {\n      // if the connection was closed by the user calling .close() we will send a CLOSE message\n      // otherwise this._open will be already set to false so we will skip this\n      try {\n        this.relay.send('[\"CLOSE\",' + JSON.stringify(this.id) + ']')\n      } catch (err) {\n        if (err instanceof SendingOnClosedConnection) {\n          /* doesn't matter, it's ok */\n        } else {\n          throw err\n        }\n      }\n      this.closed = true\n    }\n    this.relay.openSubs.delete(this.id)\n    this.onclose?.(reason)\n  }\n}\n\nexport type SubscriptionParams = {\n  onevent?: (evt: Event) => void\n  oneose?: () => void\n  onclose?: (reason: string) => void\n  alreadyHaveEvent?: (id: string) => boolean\n  receivedEvent?: (relay: AbstractRelay, id: string) => void\n  eoseTimeout?: number\n  abort?: AbortSignal\n}\n\nexport type CountResolver = {\n  resolve: (count: number) => void\n  reject: (err: Error) => void\n}\n\nexport type EventPublishResolver = {\n  resolve: (reason: string) => void\n  reject: (err: Error) => void\n  timeout: ReturnType<typeof setTimeout>\n}\n"],
   "mappings": "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",
   "names": ["target", "reason"]
 }
Index: package/lib/esm/abstract-pool.js.map
===================================================================
--- package/lib/esm/abstract-pool.js.map
+++ package/lib/esm/abstract-pool.js.map
@@ -1,7 +1,7 @@
 {
   "version": 3,
   "sources": ["../../core.ts", "../../utils.ts", "../../kinds.ts", "../../filter.ts", "../../fakejson.ts", "../../nip42.ts", "../../helpers.ts", "../../abstract-relay.ts", "../../abstract-pool.ts"],
-  "sourcesContent": ["export interface Nostr {\n  generateSecretKey(): Uint8Array\n  getPublicKey(secretKey: Uint8Array): string\n  finalizeEvent(event: EventTemplate, secretKey: Uint8Array): VerifiedEvent\n  verifyEvent(event: Event): event is VerifiedEvent\n}\n\n/** Designates a verified event signature. */\nexport const verifiedSymbol = Symbol('verified')\n\nexport type NostrEvent = {\n  kind: number\n  tags: string[][]\n  content: string\n  created_at: number\n  pubkey: string\n  id: string\n  sig: string\n  [verifiedSymbol]?: boolean\n}\n\nexport type Event = NostrEvent\nexport type EventTemplate = Pick<Event, 'kind' | 'tags' | 'content' | 'created_at'>\nexport type UnsignedEvent = Pick<Event, 'kind' | 'tags' | 'content' | 'created_at' | 'pubkey'>\n\n/** An event whose signature has been verified. */\nexport interface VerifiedEvent extends Event {\n  [verifiedSymbol]: true\n}\n\nconst isRecord = (obj: unknown): obj is Record<string, unknown> => obj instanceof Object\n\nexport function validateEvent<T>(event: T): event is T & UnsignedEvent {\n  if (!isRecord(event)) return false\n  if (typeof event.kind !== 'number') return false\n  if (typeof event.content !== 'string') return false\n  if (typeof event.created_at !== 'number') return false\n  if (typeof event.pubkey !== 'string') return false\n  if (!event.pubkey.match(/^[a-f0-9]{64}$/)) return false\n\n  if (!Array.isArray(event.tags)) return false\n  for (let i = 0; i < event.tags.length; i++) {\n    let tag = event.tags[i]\n    if (!Array.isArray(tag)) return false\n    for (let j = 0; j < tag.length; j++) {\n      if (typeof tag[j] !== 'string') return false\n    }\n  }\n\n  return true\n}\n\n/**\n * Sort events in reverse-chronological order by the `created_at` timestamp,\n * and then by the event `id` (lexicographically) in case of ties.\n * This mutates the array.\n */\nexport function sortEvents(events: Event[]): Event[] {\n  return events.sort((a: NostrEvent, b: NostrEvent): number => {\n    if (a.created_at !== b.created_at) {\n      return b.created_at - a.created_at\n    }\n    return a.id.localeCompare(b.id)\n  })\n}\n", "import type { Event } from './core.ts'\n\nexport const utf8Decoder: TextDecoder = new TextDecoder('utf-8')\nexport const utf8Encoder: TextEncoder = new TextEncoder()\n\nexport { bytesToHex, hexToBytes } from '@noble/hashes/utils'\n\nexport function normalizeURL(url: string): string {\n  try {\n    if (url.indexOf('://') === -1) url = 'wss://' + url\n    let p = new URL(url)\n    if (p.protocol === 'http:') p.protocol = 'ws:'\n    else if (p.protocol === 'https:') p.protocol = 'wss:'\n    p.pathname = p.pathname.replace(/\\/+/g, '/')\n    if (p.pathname.endsWith('/')) p.pathname = p.pathname.slice(0, -1)\n    if ((p.port === '80' && p.protocol === 'ws:') || (p.port === '443' && p.protocol === 'wss:')) p.port = ''\n    p.searchParams.sort()\n    p.hash = ''\n    return p.toString()\n  } catch (e) {\n    throw new Error(`Invalid URL: ${url}`)\n  }\n}\n\nexport function insertEventIntoDescendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return b.created_at - event.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function insertEventIntoAscendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return event.created_at - b.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function binarySearch<T>(arr: T[], compare: (b: T) => number): [number, boolean] {\n  let start = 0\n  let end = arr.length - 1\n\n  while (start <= end) {\n    const mid = Math.floor((start + end) / 2)\n    const cmp = compare(arr[mid])\n\n    if (cmp === 0) {\n      return [mid, true]\n    }\n\n    if (cmp < 0) {\n      end = mid - 1\n    } else {\n      start = mid + 1\n    }\n  }\n\n  return [start, false]\n}\n\nexport class QueueNode<V> {\n  public value: V\n  public next: QueueNode<V> | null = null\n  public prev: QueueNode<V> | null = null\n\n  constructor(message: V) {\n    this.value = message\n  }\n}\n\nexport class Queue<V> {\n  public first: QueueNode<V> | null\n  public last: QueueNode<V> | null\n\n  constructor() {\n    this.first = null\n    this.last = null\n  }\n\n  enqueue(value: V): boolean {\n    const newNode = new QueueNode(value)\n    if (!this.last) {\n      // list is empty\n      this.first = newNode\n      this.last = newNode\n    } else if (this.last === this.first) {\n      // list has a single element\n      this.last = newNode\n      this.last.prev = this.first\n      this.first.next = newNode\n    } else {\n      // list has elements, add as last\n      newNode.prev = this.last\n      this.last.next = newNode\n      this.last = newNode\n    }\n    return true\n  }\n\n  dequeue(): V | null {\n    if (!this.first) return null\n\n    if (this.first === this.last) {\n      const target = this.first\n      this.first = null\n      this.last = null\n      return target.value\n    }\n\n    const target = this.first\n    this.first = target.next\n    if (this.first) {\n      this.first.prev = null // fix: clean up prev pointer\n    }\n\n    return target.value\n  }\n}\n", "import { NostrEvent, validateEvent } from './pure.ts'\n\n/** Events are **regular**, which means they're all expected to be stored by relays. */\nexport function isRegularKind(kind: number): boolean {\n  return kind < 10000 && kind !== 0 && kind !== 3\n}\n\n/** Events are **replaceable**, which means that, for each combination of `pubkey` and `kind`, only the latest event is expected to (SHOULD) be stored by relays, older versions are expected to be discarded. */\nexport function isReplaceableKind(kind: number): boolean {\n  return kind === 0 || kind === 3 || (10000 <= kind && kind < 20000)\n}\n\n/** Events are **ephemeral**, which means they are not expected to be stored by relays. */\nexport function isEphemeralKind(kind: number): boolean {\n  return 20000 <= kind && kind < 30000\n}\n\n/** Events are **addressable**, which means that, for each combination of `pubkey`, `kind` and the `d` tag, only the latest event is expected to be stored by relays, older versions are expected to be discarded. */\nexport function isAddressableKind(kind: number): boolean {\n  return 30000 <= kind && kind < 40000\n}\n\n/** Classification of the event kind. */\nexport type KindClassification = 'regular' | 'replaceable' | 'ephemeral' | 'parameterized' | 'unknown'\n\n/** Determine the classification of this kind of event if known, or `unknown`. */\nexport function classifyKind(kind: number): KindClassification {\n  if (isRegularKind(kind)) return 'regular'\n  if (isReplaceableKind(kind)) return 'replaceable'\n  if (isEphemeralKind(kind)) return 'ephemeral'\n  if (isAddressableKind(kind)) return 'parameterized'\n  return 'unknown'\n}\n\nexport function isKind<T extends number>(event: unknown, kind: T | Array<T>): event is NostrEvent & { kind: T } {\n  const kindAsArray: number[] = kind instanceof Array ? kind : [kind]\n  return (validateEvent(event) && kindAsArray.includes(event.kind)) || false\n}\n\nexport const Metadata = 0\nexport type Metadata = typeof Metadata\nexport const ShortTextNote = 1\nexport type ShortTextNote = typeof ShortTextNote\nexport const RecommendRelay = 2\nexport type RecommendRelay = typeof RecommendRelay\nexport const Contacts = 3\nexport type Contacts = typeof Contacts\nexport const EncryptedDirectMessage = 4\nexport type EncryptedDirectMessage = typeof EncryptedDirectMessage\nexport const EventDeletion = 5\nexport type EventDeletion = typeof EventDeletion\nexport const Repost = 6\nexport type Repost = typeof Repost\nexport const Reaction = 7\nexport type Reaction = typeof Reaction\nexport const BadgeAward = 8\nexport type BadgeAward = typeof BadgeAward\nexport const ChatMessage = 9\nexport type ChatMessage = typeof ChatMessage\nexport const ForumThread = 11\nexport type ForumThread = typeof ForumThread\nexport const Seal = 13\nexport type Seal = typeof Seal\nexport const PrivateDirectMessage = 14\nexport type PrivateDirectMessage = typeof PrivateDirectMessage\nexport const FileMessage = 15\nexport type FileMessage = typeof FileMessage\nexport const GenericRepost = 16\nexport type GenericRepost = typeof GenericRepost\nexport const Photo = 20\nexport type Photo = typeof Photo\nexport const NormalVideo = 21\nexport type NormalVideo = typeof NormalVideo\nexport const ShortVideo = 22\nexport type ShortVideo = typeof ShortVideo\nexport const ChannelCreation = 40\nexport type ChannelCreation = typeof ChannelCreation\nexport const ChannelMetadata = 41\nexport type ChannelMetadata = typeof ChannelMetadata\nexport const ChannelMessage = 42\nexport type ChannelMessage = typeof ChannelMessage\nexport const ChannelHideMessage = 43\nexport type ChannelHideMessage = typeof ChannelHideMessage\nexport const ChannelMuteUser = 44\nexport type ChannelMuteUser = typeof ChannelMuteUser\nexport const OpenTimestamps = 1040\nexport type OpenTimestamps = typeof OpenTimestamps\nexport const GiftWrap = 1059\nexport type GiftWrap = typeof GiftWrap\nexport const Poll = 1068\nexport type Poll = typeof Poll\nexport const FileMetadata = 1063\nexport type FileMetadata = typeof FileMetadata\nexport const Comment = 1111\nexport type Comment = typeof Comment\nexport const LiveChatMessage = 1311\nexport type LiveChatMessage = typeof LiveChatMessage\nexport const Voice = 1222\nexport type Voice = typeof Voice\nexport const VoiceComment = 1244\nexport type VoiceComment = typeof VoiceComment\nexport const ProblemTracker = 1971\nexport type ProblemTracker = typeof ProblemTracker\nexport const Report = 1984\nexport type Report = typeof Report\nexport const Reporting = 1984\nexport type Reporting = typeof Reporting\nexport const Label = 1985\nexport type Label = typeof Label\nexport const CommunityPostApproval = 4550\nexport type CommunityPostApproval = typeof CommunityPostApproval\nexport const JobRequest = 5999\nexport type JobRequest = typeof JobRequest\nexport const JobResult = 6999\nexport type JobResult = typeof JobResult\nexport const JobFeedback = 7000\nexport type JobFeedback = typeof JobFeedback\nexport const ZapGoal = 9041\nexport type ZapGoal = typeof ZapGoal\nexport const ZapRequest = 9734\nexport type ZapRequest = typeof ZapRequest\nexport const Zap = 9735\nexport type Zap = typeof Zap\nexport const Highlights = 9802\nexport type Highlights = typeof Highlights\nexport const PollResponse = 1018\nexport type PollResponse = typeof PollResponse\nexport const Mutelist = 10000\nexport type Mutelist = typeof Mutelist\nexport const Pinlist = 10001\nexport type Pinlist = typeof Pinlist\nexport const RelayList = 10002\nexport type RelayList = typeof RelayList\nexport const BookmarkList = 10003\nexport type BookmarkList = typeof BookmarkList\nexport const CommunitiesList = 10004\nexport type CommunitiesList = typeof CommunitiesList\nexport const PublicChatsList = 10005\nexport type PublicChatsList = typeof PublicChatsList\nexport const BlockedRelaysList = 10006\nexport type BlockedRelaysList = typeof BlockedRelaysList\nexport const SearchRelaysList = 10007\nexport type SearchRelaysList = typeof SearchRelaysList\nexport const FavoriteRelays = 10012\nexport type FavoriteRelays = typeof FavoriteRelays\nexport const InterestsList = 10015\nexport type InterestsList = typeof InterestsList\nexport const UserEmojiList = 10030\nexport type UserEmojiList = typeof UserEmojiList\nexport const DirectMessageRelaysList = 10050\nexport type DirectMessageRelaysList = typeof DirectMessageRelaysList\nexport const FileServerPreference = 10096\nexport type FileServerPreference = typeof FileServerPreference\nexport const BlossomServerList = 10063\nexport type BlossomServerList = typeof BlossomServerList\nexport const NWCWalletInfo = 13194\nexport type NWCWalletInfo = typeof NWCWalletInfo\nexport const LightningPubRPC = 21000\nexport type LightningPubRPC = typeof LightningPubRPC\nexport const ClientAuth = 22242\nexport type ClientAuth = typeof ClientAuth\nexport const NWCWalletRequest = 23194\nexport type NWCWalletRequest = typeof NWCWalletRequest\nexport const NWCWalletResponse = 23195\nexport type NWCWalletResponse = typeof NWCWalletResponse\nexport const NostrConnect = 24133\nexport type NostrConnect = typeof NostrConnect\nexport const HTTPAuth = 27235\nexport type HTTPAuth = typeof HTTPAuth\nexport const Followsets = 30000\nexport type Followsets = typeof Followsets\nexport const Genericlists = 30001\nexport type Genericlists = typeof Genericlists\nexport const Relaysets = 30002\nexport type Relaysets = typeof Relaysets\nexport const Bookmarksets = 30003\nexport type Bookmarksets = typeof Bookmarksets\nexport const Curationsets = 30004\nexport type Curationsets = typeof Curationsets\nexport const ProfileBadges = 30008\nexport type ProfileBadges = typeof ProfileBadges\nexport const BadgeDefinition = 30009\nexport type BadgeDefinition = typeof BadgeDefinition\nexport const Interestsets = 30015\nexport type Interestsets = typeof Interestsets\nexport const CreateOrUpdateStall = 30017\nexport type CreateOrUpdateStall = typeof CreateOrUpdateStall\nexport const CreateOrUpdateProduct = 30018\nexport type CreateOrUpdateProduct = typeof CreateOrUpdateProduct\nexport const LongFormArticle = 30023\nexport type LongFormArticle = typeof LongFormArticle\nexport const DraftLong = 30024\nexport type DraftLong = typeof DraftLong\nexport const Emojisets = 30030\nexport type Emojisets = typeof Emojisets\nexport const Application = 30078\nexport type Application = typeof Application\nexport const LiveEvent = 30311\nexport type LiveEvent = typeof LiveEvent\nexport const UserStatuses = 30315\nexport type UserStatuses = typeof UserStatuses\nexport const ClassifiedListing = 30402\nexport type ClassifiedListing = typeof ClassifiedListing\nexport const DraftClassifiedListing = 30403\nexport type DraftClassifiedListing = typeof DraftClassifiedListing\nexport const Date = 31922\nexport type Date = typeof Date\nexport const Time = 31923\nexport type Time = typeof Time\nexport const Calendar = 31924\nexport type Calendar = typeof Calendar\nexport const CalendarEventRSVP = 31925\nexport type CalendarEventRSVP = typeof CalendarEventRSVP\nexport const RelayReview = 31987\nexport type RelayReview = typeof RelayReview\nexport const Handlerrecommendation = 31989\nexport type Handlerrecommendation = typeof Handlerrecommendation\nexport const Handlerinformation = 31990\nexport type Handlerinformation = typeof Handlerinformation\nexport const CommunityDefinition = 34550\nexport type CommunityDefinition = typeof CommunityDefinition\nexport const GroupMetadata = 39000\nexport type GroupMetadata = typeof GroupMetadata\n", "import { Event } from './core.ts'\nimport { isAddressableKind, isReplaceableKind } from './kinds.ts'\n\nexport type Filter = {\n  ids?: string[]\n  kinds?: number[]\n  authors?: string[]\n  since?: number\n  until?: number\n  limit?: number\n  search?: string\n  [key: `#${string}`]: string[] | undefined\n}\n\nexport function matchFilter(filter: Filter, event: Event): boolean {\n  if (filter.ids && filter.ids.indexOf(event.id) === -1) {\n    return false\n  }\n  if (filter.kinds && filter.kinds.indexOf(event.kind) === -1) {\n    return false\n  }\n  if (filter.authors && filter.authors.indexOf(event.pubkey) === -1) {\n    return false\n  }\n\n  for (let f in filter) {\n    if (f[0] === '#') {\n      let tagName = f.slice(1)\n      let values = filter[`#${tagName}`]\n      if (values && !event.tags.find(([t, v]) => t === f.slice(1) && values!.indexOf(v) !== -1)) return false\n    }\n  }\n\n  if (filter.since && event.created_at < filter.since) return false\n  if (filter.until && event.created_at > filter.until) return false\n\n  return true\n}\n\nexport function matchFilters(filters: Filter[], event: Event): boolean {\n  for (let i = 0; i < filters.length; i++) {\n    if (matchFilter(filters[i], event)) {\n      return true\n    }\n  }\n  return false\n}\n\nexport function mergeFilters(...filters: Filter[]): Filter {\n  let result: Filter = {}\n  for (let i = 0; i < filters.length; i++) {\n    let filter = filters[i]\n    Object.entries(filter).forEach(([property, values]) => {\n      if (property === 'kinds' || property === 'ids' || property === 'authors' || property[0] === '#') {\n        // @ts-ignore\n        result[property] = result[property] || []\n        // @ts-ignore\n        for (let v = 0; v < values.length; v++) {\n          // @ts-ignore\n          let value = values[v]\n          // @ts-ignore\n          if (!result[property].includes(value)) result[property].push(value)\n        }\n      }\n    })\n\n    if (filter.limit && (!result.limit || filter.limit > result.limit)) result.limit = filter.limit\n    if (filter.until && (!result.until || filter.until > result.until)) result.until = filter.until\n    if (filter.since && (!result.since || filter.since < result.since)) result.since = filter.since\n  }\n\n  return result\n}\n\n/**\n * Calculate the intrinsic limit of a filter.\n * This function returns a positive integer, or `Infinity` if there is no intrinsic limit.\n */\nexport function getFilterLimit(filter: Filter): number {\n  if (filter.ids && !filter.ids.length) return 0\n  if (filter.kinds && !filter.kinds.length) return 0\n  if (filter.authors && !filter.authors.length) return 0\n\n  for (const [key, value] of Object.entries(filter)) {\n    if (key[0] === '#' && Array.isArray(value) && !value.length) return 0\n  }\n\n  return Math.min(\n    // The `limit` property creates an artificial limit.\n    Math.max(0, filter.limit ?? Infinity),\n\n    // There can only be one event per `id`.\n    filter.ids?.length ?? Infinity,\n\n    // Replaceable events are limited by the number of authors and kinds.\n    filter.authors?.length && filter.kinds?.every(kind => isReplaceableKind(kind))\n      ? filter.authors.length * filter.kinds.length\n      : Infinity,\n\n    // Parameterized replaceable events are limited by the number of authors, kinds, and \"d\" tags.\n    filter.authors?.length && filter.kinds?.every(kind => isAddressableKind(kind)) && filter['#d']?.length\n      ? filter.authors.length * filter.kinds.length * filter['#d'].length\n      : Infinity,\n  )\n}\n", "export function getHex64(json: string, field: string): string {\n  let len = field.length + 3\n  let idx = json.indexOf(`\"${field}\":`) + len\n  let s = json.slice(idx).indexOf(`\"`) + idx + 1\n  return json.slice(s, s + 64)\n}\n\nexport function getInt(json: string, field: string): number {\n  let len = field.length\n  let idx = json.indexOf(`\"${field}\":`) + len + 3\n  let sliced = json.slice(idx)\n  let end = Math.min(sliced.indexOf(','), sliced.indexOf('}'))\n  return parseInt(sliced.slice(0, end), 10)\n}\n\nexport function getSubscriptionId(json: string): string | null {\n  let idx = json.slice(0, 22).indexOf(`\"EVENT\"`)\n  if (idx === -1) return null\n\n  let pstart = json.slice(idx + 7 + 1).indexOf(`\"`)\n  if (pstart === -1) return null\n  let start = idx + 7 + 1 + pstart\n\n  let pend = json.slice(start + 1, 80).indexOf(`\"`)\n  if (pend === -1) return null\n  let end = start + 1 + pend\n\n  return json.slice(start + 1, end)\n}\n\nexport function matchEventId(json: string, id: string): boolean {\n  return id === getHex64(json, 'id')\n}\n\nexport function matchEventPubkey(json: string, pubkey: string): boolean {\n  return pubkey === getHex64(json, 'pubkey')\n}\n\nexport function matchEventKind(json: string, kind: number): boolean {\n  return kind === getInt(json, 'kind')\n}\n", "import { EventTemplate } from './core.ts'\nimport { ClientAuth } from './kinds.ts'\n\n/**\n * creates an EventTemplate for an AUTH event to be signed.\n */\nexport function makeAuthEvent(relayURL: string, challenge: string): EventTemplate {\n  return {\n    kind: ClientAuth,\n    created_at: Math.floor(Date.now() / 1000),\n    tags: [\n      ['relay', relayURL],\n      ['challenge', challenge],\n    ],\n    content: '',\n  }\n}\n", "import { verifiedSymbol, type Event, type Nostr, VerifiedEvent } from './core.ts'\n\nexport async function yieldThread() {\n  return new Promise<void>((resolve, reject) => {\n    try {\n      // Check if MessageChannel is available\n      if (typeof MessageChannel !== 'undefined') {\n        const ch = new MessageChannel()\n        const handler = () => {\n          // @ts-ignore (typescript thinks this property should be called `removeListener`, but in fact it's `removeEventListener`)\n          ch.port1.removeEventListener('message', handler)\n          resolve()\n        }\n        // @ts-ignore (typescript thinks this property should be called `addListener`, but in fact it's `addEventListener`)\n        ch.port1.addEventListener('message', handler)\n        ch.port2.postMessage(0)\n        ch.port1.start()\n      } else {\n        if (typeof setImmediate !== 'undefined') {\n          setImmediate(resolve)\n        } else if (typeof setTimeout !== 'undefined') {\n          setTimeout(resolve, 0)\n        } else {\n          // Last resort - resolve immediately\n          resolve()\n        }\n      }\n    } catch (e) {\n      console.error('during yield: ', e)\n      reject(e)\n    }\n  })\n}\n\nexport const alwaysTrue: Nostr['verifyEvent'] = (t: Event): t is VerifiedEvent => {\n  t[verifiedSymbol] = true\n  return true\n}\n", "/* global WebSocket */\n\nimport type { Event, EventTemplate, VerifiedEvent, Nostr, NostrEvent } from './core.ts'\nimport { matchFilters, type Filter } from './filter.ts'\nimport { getHex64, getSubscriptionId } from './fakejson.ts'\nimport { Queue, normalizeURL } from './utils.ts'\nimport { makeAuthEvent } from './nip42.ts'\nimport { yieldThread } from './helpers.ts'\n\ntype RelayWebSocket = WebSocket & {\n  ping?(): void\n  on?(event: 'pong', listener: () => void): any\n}\n\nexport type AbstractRelayConstructorOptions = {\n  verifyEvent: Nostr['verifyEvent']\n  websocketImplementation?: typeof WebSocket\n  enablePing?: boolean\n  enableReconnect?: boolean\n}\n\nexport class SendingOnClosedConnection extends Error {\n  constructor(message: string, relay: string) {\n    super(`Tried to send message '${message} on a closed connection to ${relay}.`)\n    this.name = 'SendingOnClosedConnection'\n  }\n}\n\nexport class AbstractRelay {\n  public readonly url: string\n  private _connected: boolean = false\n\n  public onclose: (() => void) | null = null\n  public onnotice: (msg: string) => void = msg => console.debug(`NOTICE from ${this.url}: ${msg}`)\n  public onauth: undefined | ((evt: EventTemplate) => Promise<VerifiedEvent>)\n\n  public baseEoseTimeout: number = 4400\n  public publishTimeout: number = 4400\n  public pingFrequency: number = 29000\n  public pingTimeout: number = 20000\n  public resubscribeBackoff: number[] = [10000, 10000, 10000, 20000, 20000, 30000, 60000]\n  public openSubs: Map<string, Subscription> = new Map()\n  public enablePing: boolean | undefined\n  public enableReconnect: boolean\n  private reconnectTimeoutHandle: ReturnType<typeof setTimeout> | undefined\n  private pingIntervalHandle: ReturnType<typeof setInterval> | undefined\n  private reconnectAttempts: number = 0\n  private closedIntentionally: boolean = false\n\n  private connectionPromise: Promise<void> | undefined\n  private openCountRequests = new Map<string, CountResolver>()\n  private openEventPublishes = new Map<string, EventPublishResolver>()\n  private ws: RelayWebSocket | undefined\n  private incomingMessageQueue = new Queue<string>()\n  private queueRunning = false\n  private challenge: string | undefined\n  private authPromise: Promise<string> | undefined\n  private serial: number = 0\n  private verifyEvent: Nostr['verifyEvent']\n\n  private _WebSocket: typeof WebSocket\n\n  constructor(url: string, opts: AbstractRelayConstructorOptions) {\n    this.url = normalizeURL(url)\n    this.verifyEvent = opts.verifyEvent\n    this._WebSocket = opts.websocketImplementation || WebSocket\n    this.enablePing = opts.enablePing\n    this.enableReconnect = opts.enableReconnect || false\n  }\n\n  static async connect(\n    url: string,\n    opts: AbstractRelayConstructorOptions & Parameters<AbstractRelay['connect']>[0],\n  ): Promise<AbstractRelay> {\n    const relay = new AbstractRelay(url, opts)\n    await relay.connect(opts)\n    return relay\n  }\n\n  private closeAllSubscriptions(reason: string) {\n    for (let [_, sub] of this.openSubs) {\n      sub.close(reason)\n    }\n    this.openSubs.clear()\n\n    for (let [_, ep] of this.openEventPublishes) {\n      ep.reject(new Error(reason))\n    }\n    this.openEventPublishes.clear()\n\n    for (let [_, cr] of this.openCountRequests) {\n      cr.reject(new Error(reason))\n    }\n    this.openCountRequests.clear()\n  }\n\n  public get connected(): boolean {\n    return this._connected\n  }\n\n  private async reconnect(): Promise<void> {\n    const backoff = this.resubscribeBackoff[Math.min(this.reconnectAttempts, this.resubscribeBackoff.length - 1)]\n    this.reconnectAttempts++\n\n    this.reconnectTimeoutHandle = setTimeout(async () => {\n      try {\n        await this.connect()\n      } catch (err) {\n        // this will be called again through onclose/onerror\n      }\n    }, backoff)\n  }\n\n  private handleHardClose(reason: string) {\n    if (this.pingIntervalHandle) {\n      clearInterval(this.pingIntervalHandle)\n      this.pingIntervalHandle = undefined\n    }\n\n    this._connected = false\n    this.connectionPromise = undefined\n\n    const wasIntentional = this.closedIntentionally\n    this.closedIntentionally = false // reset for next time\n\n    this.onclose?.()\n\n    if (this.enableReconnect && !wasIntentional) {\n      this.reconnect()\n    } else {\n      this.closeAllSubscriptions(reason)\n    }\n  }\n\n  public async connect(opts?: { timeout?: number; abort?: AbortSignal }): Promise<void> {\n    let connectionTimeoutHandle: ReturnType<typeof setTimeout> | undefined\n\n    if (this.connectionPromise) return this.connectionPromise\n\n    this.challenge = undefined\n    this.authPromise = undefined\n    this.connectionPromise = new Promise((resolve, reject) => {\n      if (opts?.timeout) {\n        connectionTimeoutHandle = setTimeout(() => {\n          reject('connection timed out')\n          this.connectionPromise = undefined\n          this.onclose?.()\n          this.closeAllSubscriptions('relay connection timed out')\n        }, opts.timeout)\n      }\n\n      if (opts?.abort) {\n        opts.abort.onabort = reject\n      }\n\n      try {\n        this.ws = new this._WebSocket(this.url)\n      } catch (err) {\n        clearTimeout(connectionTimeoutHandle)\n        reject(err)\n        return\n      }\n\n      this.ws.onopen = () => {\n        if (this.reconnectTimeoutHandle) {\n          clearTimeout(this.reconnectTimeoutHandle)\n          this.reconnectTimeoutHandle = undefined\n        }\n        clearTimeout(connectionTimeoutHandle)\n        this._connected = true\n\n        const isReconnection = this.reconnectAttempts > 0\n        this.reconnectAttempts = 0\n\n        // resubscribe to all open subscriptions\n        for (const sub of this.openSubs.values()) {\n          sub.eosed = false\n          if (isReconnection) {\n            for (let f = 0; f < sub.filters.length; f++) {\n              if (sub.lastEmitted) {\n                sub.filters[f].since = sub.lastEmitted + 1\n              }\n            }\n          }\n          sub.fire()\n        }\n\n        if (this.enablePing) {\n          this.pingIntervalHandle = setInterval(() => this.pingpong(), this.pingFrequency)\n        }\n        resolve()\n      }\n\n      this.ws.onerror = ev => {\n        clearTimeout(connectionTimeoutHandle)\n        reject((ev as any).message || 'websocket error')\n        this.handleHardClose('relay connection errored')\n      }\n\n      this.ws.onclose = ev => {\n        clearTimeout(connectionTimeoutHandle)\n        reject((ev as any).message || 'websocket closed')\n        this.handleHardClose('relay connection closed')\n      }\n\n      this.ws.onmessage = this._onmessage.bind(this)\n    })\n\n    return this.connectionPromise\n  }\n\n  private waitForPingPong() {\n    return new Promise(resolve => {\n      // listen for pong\n      ;(this.ws as any).once('pong', () => resolve(true))\n      // send a ping\n      this.ws!.ping!()\n    })\n  }\n\n  private waitForDummyReq() {\n    return new Promise((resolve, reject) => {\n      if (!this.connectionPromise) return reject(new Error(`no connection to ${this.url}, can't ping`))\n\n      // make a dummy request with expected empty eose reply\n      // [\"REQ\", \"_\", {\"ids\":[\"aaaa...aaaa\"], \"limit\": 0}]\n      try {\n        const sub = this.subscribe(\n          [{ ids: ['aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'], limit: 0 }],\n          {\n            label: 'forced-ping',\n            oneose: () => {\n              resolve(true)\n              sub.close()\n            },\n            onclose() {\n              // if we get a CLOSED it's because the relay is alive\n              resolve(true)\n            },\n            eoseTimeout: this.pingTimeout + 1000,\n          },\n        )\n      } catch (err) {\n        reject(err)\n      }\n    })\n  }\n\n  // nodejs requires this magic here to ensure connections are closed when internet goes off and stuff\n  // in browsers it's done automatically. see https://github.com/nbd-wtf/nostr-tools/issues/491\n  private async pingpong() {\n    // if the websocket is connected\n    if (this.ws?.readyState === 1) {\n      // wait for either a ping-pong reply or a timeout\n      const result = await Promise.any([\n        // browsers don't have ping so use a dummy req\n        this.ws && this.ws.ping && (this.ws as any).once ? this.waitForPingPong() : this.waitForDummyReq(),\n        new Promise(res => setTimeout(() => res(false), this.pingTimeout)),\n      ])\n\n      if (!result) {\n        // pingpong closing socket\n        if (this.ws?.readyState === this._WebSocket.OPEN) {\n          this.ws?.close()\n        }\n      }\n    }\n  }\n\n  private async runQueue() {\n    this.queueRunning = true\n    while (true) {\n      if (false === this.handleNext()) {\n        break\n      }\n      await yieldThread()\n    }\n    this.queueRunning = false\n  }\n\n  private handleNext(): undefined | false {\n    const json = this.incomingMessageQueue.dequeue()\n    if (!json) {\n      return false\n    }\n\n    // shortcut EVENT sub\n    const subid = getSubscriptionId(json)\n    if (subid) {\n      const so = this.openSubs.get(subid as string)\n      if (!so) {\n        // this is an EVENT message, but for a subscription we don't have, so just stop here\n        return\n      }\n\n      // this will be called only when this message is a EVENT message for a subscription we have\n      // we do this before parsing the JSON to not have to do that for duplicate events\n      //   since JSON parsing is slow\n      const id = getHex64(json, 'id')\n      const alreadyHave = so.alreadyHaveEvent?.(id)\n\n      // notify any interested client that the relay has this event\n      // (do this after alreadyHaveEvent() because the client may rely on this to answer that)\n      so.receivedEvent?.(this, id)\n\n      if (alreadyHave) {\n        // if we had already seen this event we can just stop here\n        return\n      }\n    }\n\n    try {\n      let data = JSON.parse(json)\n      // we won't do any checks against the data since all failures (i.e. invalid messages from relays)\n      // will naturally be caught by the encompassing try..catch block\n\n      switch (data[0]) {\n        case 'EVENT': {\n          const so = this.openSubs.get(data[1] as string) as Subscription\n          const event = data[2] as NostrEvent\n          if (this.verifyEvent(event) && matchFilters(so.filters, event)) {\n            so.onevent(event)\n          }\n          if (!so.lastEmitted || so.lastEmitted < event.created_at) so.lastEmitted = event.created_at\n          return\n        }\n        case 'COUNT': {\n          const id: string = data[1]\n          const payload = data[2] as { count: number }\n          const cr = this.openCountRequests.get(id) as CountResolver\n          if (cr) {\n            cr.resolve(payload.count)\n            this.openCountRequests.delete(id)\n          }\n          return\n        }\n        case 'EOSE': {\n          const so = this.openSubs.get(data[1] as string)\n          if (!so) return\n          so.receivedEose()\n          return\n        }\n        case 'OK': {\n          const id: string = data[1]\n          const ok: boolean = data[2]\n          const reason: string = data[3]\n          const ep = this.openEventPublishes.get(id) as EventPublishResolver\n          if (ep) {\n            clearTimeout(ep.timeout)\n            if (ok) ep.resolve(reason)\n            else ep.reject(new Error(reason))\n            this.openEventPublishes.delete(id)\n          }\n          return\n        }\n        case 'CLOSED': {\n          const id: string = data[1]\n          const so = this.openSubs.get(id)\n          if (!so) return\n          so.closed = true\n          so.close(data[2] as string)\n          return\n        }\n        case 'NOTICE': {\n          this.onnotice(data[1] as string)\n          return\n        }\n        case 'AUTH': {\n          this.challenge = data[1] as string\n          if (this.onauth) {\n            this.auth(this.onauth)\n          }\n          return\n        }\n        default: {\n          const so = this.openSubs.get(data[1])\n          so?.oncustom?.(data)\n          return\n        }\n      }\n    } catch (err) {\n      return\n    }\n  }\n\n  public async send(message: string) {\n    if (!this.connectionPromise) throw new SendingOnClosedConnection(message, this.url)\n\n    this.connectionPromise.then(() => {\n      this.ws?.send(message)\n    })\n  }\n\n  public async auth(signAuthEvent: (evt: EventTemplate) => Promise<VerifiedEvent>): Promise<string> {\n    const challenge = this.challenge\n    if (!challenge) throw new Error(\"can't perform auth, no challenge was received\")\n    if (this.authPromise) return this.authPromise\n\n    this.authPromise = new Promise<string>(async (resolve, reject) => {\n      try {\n        let evt = await signAuthEvent(makeAuthEvent(this.url, challenge))\n        let timeout = setTimeout(() => {\n          let ep = this.openEventPublishes.get(evt.id) as EventPublishResolver\n          if (ep) {\n            ep.reject(new Error('auth timed out'))\n            this.openEventPublishes.delete(evt.id)\n          }\n        }, this.publishTimeout)\n        this.openEventPublishes.set(evt.id, { resolve, reject, timeout })\n        this.send('[\"AUTH\",' + JSON.stringify(evt) + ']')\n      } catch (err) {\n        console.warn('subscribe auth function failed:', err)\n      }\n    })\n    return this.authPromise\n  }\n\n  public async publish(event: Event): Promise<string> {\n    const ret = new Promise<string>((resolve, reject) => {\n      const timeout = setTimeout(() => {\n        const ep = this.openEventPublishes.get(event.id) as EventPublishResolver\n        if (ep) {\n          ep.reject(new Error('publish timed out'))\n          this.openEventPublishes.delete(event.id)\n        }\n      }, this.publishTimeout)\n      this.openEventPublishes.set(event.id, { resolve, reject, timeout })\n    })\n    this.send('[\"EVENT\",' + JSON.stringify(event) + ']')\n    return ret\n  }\n\n  public async count(filters: Filter[], params: { id?: string | null }): Promise<number> {\n    this.serial++\n    const id = params?.id || 'count:' + this.serial\n    const ret = new Promise<number>((resolve, reject) => {\n      this.openCountRequests.set(id, { resolve, reject })\n    })\n    this.send('[\"COUNT\",\"' + id + '\",' + JSON.stringify(filters).substring(1))\n    return ret\n  }\n\n  public subscribe(\n    filters: Filter[],\n    params: Partial<SubscriptionParams> & { label?: string; id?: string },\n  ): Subscription {\n    const sub = this.prepareSubscription(filters, params)\n    sub.fire()\n\n    if (params.abort) {\n      params.abort.onabort = () => sub.close(String(params.abort!.reason || '<aborted>'))\n    }\n\n    return sub\n  }\n\n  public prepareSubscription(\n    filters: Filter[],\n    params: Partial<SubscriptionParams> & { label?: string; id?: string },\n  ): Subscription {\n    this.serial++\n    const id = params.id || (params.label ? params.label + ':' : 'sub:') + this.serial\n    const subscription = new Subscription(this, id, filters, params)\n    this.openSubs.set(id, subscription)\n    return subscription\n  }\n\n  public close() {\n    this.closedIntentionally = true\n    if (this.reconnectTimeoutHandle) {\n      clearTimeout(this.reconnectTimeoutHandle)\n      this.reconnectTimeoutHandle = undefined\n    }\n    if (this.pingIntervalHandle) {\n      clearInterval(this.pingIntervalHandle)\n      this.pingIntervalHandle = undefined\n    }\n    this.closeAllSubscriptions('relay connection closed by us')\n    this._connected = false\n    this.onclose?.()\n    if (this.ws?.readyState === this._WebSocket.OPEN) {\n      this.ws?.close()\n    }\n  }\n\n  // this is the function assigned to this.ws.onmessage\n  // it's exposed for testing and debugging purposes\n  public _onmessage(ev: MessageEvent<any>) {\n    this.incomingMessageQueue.enqueue(ev.data as string)\n    if (!this.queueRunning) {\n      this.runQueue()\n    }\n  }\n}\n\nexport class Subscription {\n  public readonly relay: AbstractRelay\n  public readonly id: string\n\n  public lastEmitted: number | undefined\n  public closed: boolean = false\n  public eosed: boolean = false\n  public filters: Filter[]\n  public alreadyHaveEvent: ((id: string) => boolean) | undefined\n  public receivedEvent: ((relay: AbstractRelay, id: string) => void) | undefined\n\n  public onevent: (evt: Event) => void\n  public oneose: (() => void) | undefined\n  public onclose: ((reason: string) => void) | undefined\n\n  // will get any messages that have this subscription id as their second item and are not default standard\n  public oncustom: ((msg: string[]) => void) | undefined\n\n  public eoseTimeout: number\n  private eoseTimeoutHandle: ReturnType<typeof setTimeout> | undefined\n\n  constructor(relay: AbstractRelay, id: string, filters: Filter[], params: SubscriptionParams) {\n    if (filters.length === 0) throw new Error(\"subscription can't be created with zero filters\")\n\n    this.relay = relay\n    this.filters = filters\n    this.id = id\n    this.alreadyHaveEvent = params.alreadyHaveEvent\n    this.receivedEvent = params.receivedEvent\n    this.eoseTimeout = params.eoseTimeout || relay.baseEoseTimeout\n\n    this.oneose = params.oneose\n    this.onclose = params.onclose\n    this.onevent =\n      params.onevent ||\n      (event => {\n        console.warn(\n          `onevent() callback not defined for subscription '${this.id}' in relay ${this.relay.url}. event received:`,\n          event,\n        )\n      })\n  }\n\n  public fire() {\n    this.relay.send('[\"REQ\",\"' + this.id + '\",' + JSON.stringify(this.filters).substring(1))\n\n    // only now we start counting the eoseTimeout\n    this.eoseTimeoutHandle = setTimeout(this.receivedEose.bind(this), this.eoseTimeout)\n  }\n\n  public receivedEose() {\n    if (this.eosed) return\n    clearTimeout(this.eoseTimeoutHandle)\n    this.eosed = true\n    this.oneose?.()\n  }\n\n  public close(reason: string = 'closed by caller') {\n    if (!this.closed && this.relay.connected) {\n      // if the connection was closed by the user calling .close() we will send a CLOSE message\n      // otherwise this._open will be already set to false so we will skip this\n      try {\n        this.relay.send('[\"CLOSE\",' + JSON.stringify(this.id) + ']')\n      } catch (err) {\n        if (err instanceof SendingOnClosedConnection) {\n          /* doesn't matter, it's ok */\n        } else {\n          throw err\n        }\n      }\n      this.closed = true\n    }\n    this.relay.openSubs.delete(this.id)\n    this.onclose?.(reason)\n  }\n}\n\nexport type SubscriptionParams = {\n  onevent?: (evt: Event) => void\n  oneose?: () => void\n  onclose?: (reason: string) => void\n  alreadyHaveEvent?: (id: string) => boolean\n  receivedEvent?: (relay: AbstractRelay, id: string) => void\n  eoseTimeout?: number\n  abort?: AbortSignal\n}\n\nexport type CountResolver = {\n  resolve: (count: number) => void\n  reject: (err: Error) => void\n}\n\nexport type EventPublishResolver = {\n  resolve: (reason: string) => void\n  reject: (err: Error) => void\n  timeout: ReturnType<typeof setTimeout>\n}\n", "/* global WebSocket */\n\nimport {\n  AbstractRelay as AbstractRelay,\n  SubscriptionParams,\n  Subscription,\n  type AbstractRelayConstructorOptions,\n} from './abstract-relay.ts'\nimport { normalizeURL } from './utils.ts'\n\nimport type { Event, EventTemplate, Nostr, VerifiedEvent } from './core.ts'\nimport { type Filter } from './filter.ts'\nimport { alwaysTrue } from './helpers.ts'\n\nexport type SubCloser = { close: (reason?: string) => void }\n\nexport type AbstractPoolConstructorOptions = AbstractRelayConstructorOptions & {\n  // automaticallyAuth takes a relay URL and should return null\n  // in case that relay shouldn't be authenticated against\n  // or a function to sign the AUTH event template otherwise (that function may still throw in case of failure)\n  automaticallyAuth?: (relayURL: string) => null | ((event: EventTemplate) => Promise<VerifiedEvent>)\n}\n\nexport type SubscribeManyParams = Omit<SubscriptionParams, 'onclose'> & {\n  maxWait?: number\n  abort?: AbortSignal\n  onclose?: (reasons: string[]) => void\n  onauth?: (event: EventTemplate) => Promise<VerifiedEvent>\n  id?: string\n  label?: string\n}\n\nexport class AbstractSimplePool {\n  protected relays: Map<string, AbstractRelay> = new Map()\n  public seenOn: Map<string, Set<AbstractRelay>> = new Map()\n  public trackRelays: boolean = false\n\n  public verifyEvent: Nostr['verifyEvent']\n  public enablePing: boolean | undefined\n  public enableReconnect: boolean\n  public automaticallyAuth?: (relayURL: string) => null | ((event: EventTemplate) => Promise<VerifiedEvent>)\n  public trustedRelayURLs: Set<string> = new Set()\n\n  private _WebSocket?: typeof WebSocket\n\n  constructor(opts: AbstractPoolConstructorOptions) {\n    this.verifyEvent = opts.verifyEvent\n    this._WebSocket = opts.websocketImplementation\n    this.enablePing = opts.enablePing\n    this.enableReconnect = opts.enableReconnect || false\n    this.automaticallyAuth = opts.automaticallyAuth\n  }\n\n  async ensureRelay(\n    url: string,\n    params?: {\n      connectionTimeout?: number\n      abort?: AbortSignal\n    },\n  ): Promise<AbstractRelay> {\n    url = normalizeURL(url)\n\n    let relay = this.relays.get(url)\n    if (!relay) {\n      relay = new AbstractRelay(url, {\n        verifyEvent: this.trustedRelayURLs.has(url) ? alwaysTrue : this.verifyEvent,\n        websocketImplementation: this._WebSocket,\n        enablePing: this.enablePing,\n        enableReconnect: this.enableReconnect,\n      })\n      relay.onclose = () => {\n        if (relay && !relay.enableReconnect) {\n          this.relays.delete(url)\n        }\n      }\n      this.relays.set(url, relay)\n    }\n\n    if (this.automaticallyAuth) {\n      const authSignerFn = this.automaticallyAuth(url)\n      if (authSignerFn) {\n        relay.onauth = authSignerFn\n      }\n    }\n\n    await relay.connect({\n      timeout: params?.connectionTimeout,\n      abort: params?.abort,\n    })\n\n    return relay\n  }\n\n  close(relays: string[]) {\n    relays.map(normalizeURL).forEach(url => {\n      this.relays.get(url)?.close()\n      this.relays.delete(url)\n    })\n  }\n\n  subscribe(relays: string[], filter: Filter, params: SubscribeManyParams): SubCloser {\n    const request: { url: string; filter: Filter }[] = []\n    for (let i = 0; i < relays.length; i++) {\n      const url = normalizeURL(relays[i])\n      if (!request.find(r => r.url === url)) {\n        request.push({ url, filter: filter })\n      }\n    }\n\n    return this.subscribeMap(request, params)\n  }\n\n  subscribeMany(relays: string[], filter: Filter, params: SubscribeManyParams): SubCloser {\n    const request: { url: string; filter: Filter }[] = []\n    const uniqUrls: string[] = []\n    for (let i = 0; i < relays.length; i++) {\n      const url = normalizeURL(relays[i])\n      if (uniqUrls.indexOf(url) === -1) {\n        uniqUrls.push(url)\n        request.push({ url, filter: filter })\n      }\n    }\n\n    return this.subscribeMap(request, params)\n  }\n\n  subscribeMap(requests: { url: string; filter: Filter }[], params: SubscribeManyParams): SubCloser {\n    const grouped = new Map<string, Filter[]>()\n    for (const req of requests) {\n      const { url, filter } = req\n      if (!grouped.has(url)) grouped.set(url, [])\n      grouped.get(url)!.push(filter)\n    }\n    const groupedRequests = Array.from(grouped.entries()).map(([url, filters]) => ({ url, filters }))\n\n    if (this.trackRelays) {\n      params.receivedEvent = (relay: AbstractRelay, id: string) => {\n        let set = this.seenOn.get(id)\n        if (!set) {\n          set = new Set()\n          this.seenOn.set(id, set)\n        }\n        set.add(relay)\n      }\n    }\n\n    const _knownIds = new Set<string>()\n    const subs: Subscription[] = []\n\n    // batch all EOSEs into a single\n    const eosesReceived: boolean[] = []\n    let handleEose = (i: number) => {\n      if (eosesReceived[i]) return // do not act twice for the same relay\n      eosesReceived[i] = true\n      if (eosesReceived.filter(a => a).length === groupedRequests.length) {\n        params.oneose?.()\n        handleEose = () => {}\n      }\n    }\n    // batch all closes into a single\n    const closesReceived: string[] = []\n    let handleClose = (i: number, reason: string) => {\n      if (closesReceived[i]) return // do not act twice for the same relay\n      handleEose(i)\n      closesReceived[i] = reason\n      if (closesReceived.filter(a => a).length === groupedRequests.length) {\n        params.onclose?.(closesReceived)\n        handleClose = () => {}\n      }\n    }\n\n    const localAlreadyHaveEventHandler = (id: string) => {\n      if (params.alreadyHaveEvent?.(id)) {\n        return true\n      }\n      const have = _knownIds.has(id)\n      _knownIds.add(id)\n      return have\n    }\n\n    // open a subscription in all given relays\n    const allOpened = Promise.all(\n      groupedRequests.map(async ({ url, filters }, i) => {\n        let relay: AbstractRelay\n        try {\n          relay = await this.ensureRelay(url, {\n            connectionTimeout: params.maxWait ? Math.max(params.maxWait * 0.8, params.maxWait - 1000) : undefined,\n            abort: params.abort,\n          })\n        } catch (err) {\n          handleClose(i, (err as any)?.message || String(err))\n          return\n        }\n\n        let subscription = relay.subscribe(filters, {\n          ...params,\n          oneose: () => handleEose(i),\n          onclose: reason => {\n            if (reason.startsWith('auth-required: ') && params.onauth) {\n              relay\n                .auth(params.onauth)\n                .then(() => {\n                  relay.subscribe(filters, {\n                    ...params,\n                    oneose: () => handleEose(i),\n                    onclose: reason => {\n                      handleClose(i, reason) // the second time we won't try to auth anymore\n                    },\n                    alreadyHaveEvent: localAlreadyHaveEventHandler,\n                    eoseTimeout: params.maxWait,\n                    abort: params.abort,\n                  })\n                })\n                .catch(err => {\n                  handleClose(i, `auth was required and attempted, but failed with: ${err}`)\n                })\n            } else {\n              handleClose(i, reason)\n            }\n          },\n          alreadyHaveEvent: localAlreadyHaveEventHandler,\n          eoseTimeout: params.maxWait,\n          abort: params.abort,\n        })\n\n        subs.push(subscription)\n      }),\n    )\n\n    return {\n      async close(reason?: string) {\n        await allOpened\n        subs.forEach(sub => {\n          sub.close(reason)\n        })\n      },\n    }\n  }\n\n  subscribeEose(\n    relays: string[],\n    filter: Filter,\n    params: Pick<SubscribeManyParams, 'label' | 'id' | 'onevent' | 'onclose' | 'maxWait' | 'onauth'>,\n  ): SubCloser {\n    const subcloser = this.subscribe(relays, filter, {\n      ...params,\n      oneose() {\n        subcloser.close('closed automatically on eose')\n      },\n    })\n    return subcloser\n  }\n\n  subscribeManyEose(\n    relays: string[],\n    filter: Filter,\n    params: Pick<SubscribeManyParams, 'label' | 'id' | 'onevent' | 'onclose' | 'maxWait' | 'onauth'>,\n  ): SubCloser {\n    const subcloser = this.subscribeMany(relays, filter, {\n      ...params,\n      oneose() {\n        subcloser.close('closed automatically on eose')\n      },\n    })\n    return subcloser\n  }\n\n  async querySync(\n    relays: string[],\n    filter: Filter,\n    params?: Pick<SubscribeManyParams, 'label' | 'id' | 'maxWait'>,\n  ): Promise<Event[]> {\n    return new Promise(async resolve => {\n      const events: Event[] = []\n      this.subscribeEose(relays, filter, {\n        ...params,\n        onevent(event: Event) {\n          events.push(event)\n        },\n        onclose(_: string[]) {\n          resolve(events)\n        },\n      })\n    })\n  }\n\n  async get(\n    relays: string[],\n    filter: Filter,\n    params?: Pick<SubscribeManyParams, 'label' | 'id' | 'maxWait'>,\n  ): Promise<Event | null> {\n    filter.limit = 1\n    const events = await this.querySync(relays, filter, params)\n    events.sort((a, b) => b.created_at - a.created_at)\n    return events[0] || null\n  }\n\n  publish(\n    relays: string[],\n    event: Event,\n    options?: { onauth?: (evt: EventTemplate) => Promise<VerifiedEvent> },\n  ): Promise<string>[] {\n    return relays.map(normalizeURL).map(async (url, i, arr) => {\n      if (arr.indexOf(url) !== i) {\n        // duplicate\n        return Promise.reject('duplicate url')\n      }\n\n      let r = await this.ensureRelay(url)\n      return r\n        .publish(event)\n        .catch(async err => {\n          if (err instanceof Error && err.message.startsWith('auth-required: ') && options?.onauth) {\n            await r.auth(options.onauth)\n            return r.publish(event) // retry\n          }\n          throw err\n        })\n        .then(reason => {\n          if (this.trackRelays) {\n            let set = this.seenOn.get(event.id)\n            if (!set) {\n              set = new Set()\n              this.seenOn.set(event.id, set)\n            }\n            set.add(r)\n          }\n          return reason\n        })\n    })\n  }\n\n  listConnectionStatus(): Map<string, boolean> {\n    const map = new Map<string, boolean>()\n    this.relays.forEach((relay, url) => map.set(url, relay.connected))\n\n    return map\n  }\n\n  destroy(): void {\n    this.relays.forEach(conn => conn.close())\n    this.relays = new Map()\n  }\n}\n"],
+  "sourcesContent": ["export interface Nostr {\n  generateSecretKey(): Uint8Array\n  getPublicKey(secretKey: Uint8Array): string\n  finalizeEvent(event: EventTemplate, secretKey: Uint8Array): VerifiedEvent\n  verifyEvent(event: Event): event is VerifiedEvent\n}\n\n/** Designates a verified event signature. */\nexport const verifiedSymbol = Symbol('verified')\n\nexport type NostrEvent = {\n  kind: number\n  tags: string[][]\n  content: string\n  created_at: number\n  pubkey: string\n  id: string\n  sig: string\n  [verifiedSymbol]?: boolean\n}\n\nexport type Event = NostrEvent\nexport type EventTemplate = Pick<Event, 'kind' | 'tags' | 'content' | 'created_at'>\nexport type UnsignedEvent = Pick<Event, 'kind' | 'tags' | 'content' | 'created_at' | 'pubkey'>\n\n/** An event whose signature has been verified. */\nexport interface VerifiedEvent extends Event {\n  [verifiedSymbol]: true\n}\n\nconst isRecord = (obj: unknown): obj is Record<string, unknown> => obj instanceof Object\n\nexport function validateEvent<T>(event: T): event is T & UnsignedEvent {\n  if (!isRecord(event)) return false\n  if (typeof event.kind !== 'number') return false\n  if (typeof event.content !== 'string') return false\n  if (typeof event.created_at !== 'number') return false\n  if (typeof event.pubkey !== 'string') return false\n  if (!event.pubkey.match(/^[a-f0-9]{64}$/)) return false\n\n  if (!Array.isArray(event.tags)) return false\n  for (let i = 0; i < event.tags.length; i++) {\n    let tag = event.tags[i]\n    if (!Array.isArray(tag)) return false\n    for (let j = 0; j < tag.length; j++) {\n      if (typeof tag[j] !== 'string') return false\n    }\n  }\n\n  return true\n}\n\n/**\n * Sort events in reverse-chronological order by the `created_at` timestamp,\n * and then by the event `id` (lexicographically) in case of ties.\n * This mutates the array.\n */\nexport function sortEvents(events: Event[]): Event[] {\n  return events.sort((a: NostrEvent, b: NostrEvent): number => {\n    if (a.created_at !== b.created_at) {\n      return b.created_at - a.created_at\n    }\n    return a.id.localeCompare(b.id)\n  })\n}\n", "import type { Event } from './core.ts'\n\nexport const utf8Decoder: TextDecoder = new TextDecoder('utf-8')\nexport const utf8Encoder: TextEncoder = new TextEncoder()\n\nexport { bytesToHex, hexToBytes } from '@noble/hashes/utils.js'\n\nexport function normalizeURL(url: string): string {\n  try {\n    if (url.indexOf('://') === -1) url = 'wss://' + url\n    let p = new URL(url)\n    if (p.protocol === 'http:') p.protocol = 'ws:'\n    else if (p.protocol === 'https:') p.protocol = 'wss:'\n    p.pathname = p.pathname.replace(/\\/+/g, '/')\n    if (p.pathname.endsWith('/')) p.pathname = p.pathname.slice(0, -1)\n    if ((p.port === '80' && p.protocol === 'ws:') || (p.port === '443' && p.protocol === 'wss:')) p.port = ''\n    p.searchParams.sort()\n    p.hash = ''\n    return p.toString()\n  } catch (e) {\n    throw new Error(`Invalid URL: ${url}`)\n  }\n}\n\nexport function insertEventIntoDescendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return b.created_at - event.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function insertEventIntoAscendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return event.created_at - b.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function binarySearch<T>(arr: T[], compare: (b: T) => number): [number, boolean] {\n  let start = 0\n  let end = arr.length - 1\n\n  while (start <= end) {\n    const mid = Math.floor((start + end) / 2)\n    const cmp = compare(arr[mid])\n\n    if (cmp === 0) {\n      return [mid, true]\n    }\n\n    if (cmp < 0) {\n      end = mid - 1\n    } else {\n      start = mid + 1\n    }\n  }\n\n  return [start, false]\n}\n\nexport class QueueNode<V> {\n  public value: V\n  public next: QueueNode<V> | null = null\n  public prev: QueueNode<V> | null = null\n\n  constructor(message: V) {\n    this.value = message\n  }\n}\n\nexport class Queue<V> {\n  public first: QueueNode<V> | null\n  public last: QueueNode<V> | null\n\n  constructor() {\n    this.first = null\n    this.last = null\n  }\n\n  enqueue(value: V): boolean {\n    const newNode = new QueueNode(value)\n    if (!this.last) {\n      // list is empty\n      this.first = newNode\n      this.last = newNode\n    } else if (this.last === this.first) {\n      // list has a single element\n      this.last = newNode\n      this.last.prev = this.first\n      this.first.next = newNode\n    } else {\n      // list has elements, add as last\n      newNode.prev = this.last\n      this.last.next = newNode\n      this.last = newNode\n    }\n    return true\n  }\n\n  dequeue(): V | null {\n    if (!this.first) return null\n\n    if (this.first === this.last) {\n      const target = this.first\n      this.first = null\n      this.last = null\n      return target.value\n    }\n\n    const target = this.first\n    this.first = target.next\n    if (this.first) {\n      this.first.prev = null // fix: clean up prev pointer\n    }\n\n    return target.value\n  }\n}\n", "import { NostrEvent, validateEvent } from './pure.ts'\n\n/** Events are **regular**, which means they're all expected to be stored by relays. */\nexport function isRegularKind(kind: number): boolean {\n  return kind < 10000 && kind !== 0 && kind !== 3\n}\n\n/** Events are **replaceable**, which means that, for each combination of `pubkey` and `kind`, only the latest event is expected to (SHOULD) be stored by relays, older versions are expected to be discarded. */\nexport function isReplaceableKind(kind: number): boolean {\n  return kind === 0 || kind === 3 || (10000 <= kind && kind < 20000)\n}\n\n/** Events are **ephemeral**, which means they are not expected to be stored by relays. */\nexport function isEphemeralKind(kind: number): boolean {\n  return 20000 <= kind && kind < 30000\n}\n\n/** Events are **addressable**, which means that, for each combination of `pubkey`, `kind` and the `d` tag, only the latest event is expected to be stored by relays, older versions are expected to be discarded. */\nexport function isAddressableKind(kind: number): boolean {\n  return 30000 <= kind && kind < 40000\n}\n\n/** Classification of the event kind. */\nexport type KindClassification = 'regular' | 'replaceable' | 'ephemeral' | 'parameterized' | 'unknown'\n\n/** Determine the classification of this kind of event if known, or `unknown`. */\nexport function classifyKind(kind: number): KindClassification {\n  if (isRegularKind(kind)) return 'regular'\n  if (isReplaceableKind(kind)) return 'replaceable'\n  if (isEphemeralKind(kind)) return 'ephemeral'\n  if (isAddressableKind(kind)) return 'parameterized'\n  return 'unknown'\n}\n\nexport function isKind<T extends number>(event: unknown, kind: T | Array<T>): event is NostrEvent & { kind: T } {\n  const kindAsArray: number[] = kind instanceof Array ? kind : [kind]\n  return (validateEvent(event) && kindAsArray.includes(event.kind)) || false\n}\n\nexport const Metadata = 0\nexport type Metadata = typeof Metadata\nexport const ShortTextNote = 1\nexport type ShortTextNote = typeof ShortTextNote\nexport const RecommendRelay = 2\nexport type RecommendRelay = typeof RecommendRelay\nexport const Contacts = 3\nexport type Contacts = typeof Contacts\nexport const EncryptedDirectMessage = 4\nexport type EncryptedDirectMessage = typeof EncryptedDirectMessage\nexport const EventDeletion = 5\nexport type EventDeletion = typeof EventDeletion\nexport const Repost = 6\nexport type Repost = typeof Repost\nexport const Reaction = 7\nexport type Reaction = typeof Reaction\nexport const BadgeAward = 8\nexport type BadgeAward = typeof BadgeAward\nexport const ChatMessage = 9\nexport type ChatMessage = typeof ChatMessage\nexport const ForumThread = 11\nexport type ForumThread = typeof ForumThread\nexport const Seal = 13\nexport type Seal = typeof Seal\nexport const PrivateDirectMessage = 14\nexport type PrivateDirectMessage = typeof PrivateDirectMessage\nexport const FileMessage = 15\nexport type FileMessage = typeof FileMessage\nexport const GenericRepost = 16\nexport type GenericRepost = typeof GenericRepost\nexport const Photo = 20\nexport type Photo = typeof Photo\nexport const NormalVideo = 21\nexport type NormalVideo = typeof NormalVideo\nexport const ShortVideo = 22\nexport type ShortVideo = typeof ShortVideo\nexport const ChannelCreation = 40\nexport type ChannelCreation = typeof ChannelCreation\nexport const ChannelMetadata = 41\nexport type ChannelMetadata = typeof ChannelMetadata\nexport const ChannelMessage = 42\nexport type ChannelMessage = typeof ChannelMessage\nexport const ChannelHideMessage = 43\nexport type ChannelHideMessage = typeof ChannelHideMessage\nexport const ChannelMuteUser = 44\nexport type ChannelMuteUser = typeof ChannelMuteUser\nexport const OpenTimestamps = 1040\nexport type OpenTimestamps = typeof OpenTimestamps\nexport const GiftWrap = 1059\nexport type GiftWrap = typeof GiftWrap\nexport const Poll = 1068\nexport type Poll = typeof Poll\nexport const FileMetadata = 1063\nexport type FileMetadata = typeof FileMetadata\nexport const Comment = 1111\nexport type Comment = typeof Comment\nexport const LiveChatMessage = 1311\nexport type LiveChatMessage = typeof LiveChatMessage\nexport const Voice = 1222\nexport type Voice = typeof Voice\nexport const VoiceComment = 1244\nexport type VoiceComment = typeof VoiceComment\nexport const ProblemTracker = 1971\nexport type ProblemTracker = typeof ProblemTracker\nexport const Report = 1984\nexport type Report = typeof Report\nexport const Reporting = 1984\nexport type Reporting = typeof Reporting\nexport const Label = 1985\nexport type Label = typeof Label\nexport const CommunityPostApproval = 4550\nexport type CommunityPostApproval = typeof CommunityPostApproval\nexport const JobRequest = 5999\nexport type JobRequest = typeof JobRequest\nexport const JobResult = 6999\nexport type JobResult = typeof JobResult\nexport const JobFeedback = 7000\nexport type JobFeedback = typeof JobFeedback\nexport const ZapGoal = 9041\nexport type ZapGoal = typeof ZapGoal\nexport const ZapRequest = 9734\nexport type ZapRequest = typeof ZapRequest\nexport const Zap = 9735\nexport type Zap = typeof Zap\nexport const Highlights = 9802\nexport type Highlights = typeof Highlights\nexport const PollResponse = 1018\nexport type PollResponse = typeof PollResponse\nexport const Mutelist = 10000\nexport type Mutelist = typeof Mutelist\nexport const Pinlist = 10001\nexport type Pinlist = typeof Pinlist\nexport const RelayList = 10002\nexport type RelayList = typeof RelayList\nexport const BookmarkList = 10003\nexport type BookmarkList = typeof BookmarkList\nexport const CommunitiesList = 10004\nexport type CommunitiesList = typeof CommunitiesList\nexport const PublicChatsList = 10005\nexport type PublicChatsList = typeof PublicChatsList\nexport const BlockedRelaysList = 10006\nexport type BlockedRelaysList = typeof BlockedRelaysList\nexport const SearchRelaysList = 10007\nexport type SearchRelaysList = typeof SearchRelaysList\nexport const FavoriteRelays = 10012\nexport type FavoriteRelays = typeof FavoriteRelays\nexport const InterestsList = 10015\nexport type InterestsList = typeof InterestsList\nexport const UserEmojiList = 10030\nexport type UserEmojiList = typeof UserEmojiList\nexport const DirectMessageRelaysList = 10050\nexport type DirectMessageRelaysList = typeof DirectMessageRelaysList\nexport const FileServerPreference = 10096\nexport type FileServerPreference = typeof FileServerPreference\nexport const BlossomServerList = 10063\nexport type BlossomServerList = typeof BlossomServerList\nexport const NWCWalletInfo = 13194\nexport type NWCWalletInfo = typeof NWCWalletInfo\nexport const LightningPubRPC = 21000\nexport type LightningPubRPC = typeof LightningPubRPC\nexport const ClientAuth = 22242\nexport type ClientAuth = typeof ClientAuth\nexport const NWCWalletRequest = 23194\nexport type NWCWalletRequest = typeof NWCWalletRequest\nexport const NWCWalletResponse = 23195\nexport type NWCWalletResponse = typeof NWCWalletResponse\nexport const NostrConnect = 24133\nexport type NostrConnect = typeof NostrConnect\nexport const HTTPAuth = 27235\nexport type HTTPAuth = typeof HTTPAuth\nexport const Followsets = 30000\nexport type Followsets = typeof Followsets\nexport const Genericlists = 30001\nexport type Genericlists = typeof Genericlists\nexport const Relaysets = 30002\nexport type Relaysets = typeof Relaysets\nexport const Bookmarksets = 30003\nexport type Bookmarksets = typeof Bookmarksets\nexport const Curationsets = 30004\nexport type Curationsets = typeof Curationsets\nexport const ProfileBadges = 30008\nexport type ProfileBadges = typeof ProfileBadges\nexport const BadgeDefinition = 30009\nexport type BadgeDefinition = typeof BadgeDefinition\nexport const Interestsets = 30015\nexport type Interestsets = typeof Interestsets\nexport const CreateOrUpdateStall = 30017\nexport type CreateOrUpdateStall = typeof CreateOrUpdateStall\nexport const CreateOrUpdateProduct = 30018\nexport type CreateOrUpdateProduct = typeof CreateOrUpdateProduct\nexport const LongFormArticle = 30023\nexport type LongFormArticle = typeof LongFormArticle\nexport const DraftLong = 30024\nexport type DraftLong = typeof DraftLong\nexport const Emojisets = 30030\nexport type Emojisets = typeof Emojisets\nexport const Application = 30078\nexport type Application = typeof Application\nexport const LiveEvent = 30311\nexport type LiveEvent = typeof LiveEvent\nexport const UserStatuses = 30315\nexport type UserStatuses = typeof UserStatuses\nexport const ClassifiedListing = 30402\nexport type ClassifiedListing = typeof ClassifiedListing\nexport const DraftClassifiedListing = 30403\nexport type DraftClassifiedListing = typeof DraftClassifiedListing\nexport const Date = 31922\nexport type Date = typeof Date\nexport const Time = 31923\nexport type Time = typeof Time\nexport const Calendar = 31924\nexport type Calendar = typeof Calendar\nexport const CalendarEventRSVP = 31925\nexport type CalendarEventRSVP = typeof CalendarEventRSVP\nexport const RelayReview = 31987\nexport type RelayReview = typeof RelayReview\nexport const Handlerrecommendation = 31989\nexport type Handlerrecommendation = typeof Handlerrecommendation\nexport const Handlerinformation = 31990\nexport type Handlerinformation = typeof Handlerinformation\nexport const CommunityDefinition = 34550\nexport type CommunityDefinition = typeof CommunityDefinition\nexport const GroupMetadata = 39000\nexport type GroupMetadata = typeof GroupMetadata\n", "import { Event } from './core.ts'\nimport { isAddressableKind, isReplaceableKind } from './kinds.ts'\n\nexport type Filter = {\n  ids?: string[]\n  kinds?: number[]\n  authors?: string[]\n  since?: number\n  until?: number\n  limit?: number\n  search?: string\n  [key: `#${string}`]: string[] | undefined\n}\n\nexport function matchFilter(filter: Filter, event: Event): boolean {\n  if (filter.ids && filter.ids.indexOf(event.id) === -1) {\n    return false\n  }\n  if (filter.kinds && filter.kinds.indexOf(event.kind) === -1) {\n    return false\n  }\n  if (filter.authors && filter.authors.indexOf(event.pubkey) === -1) {\n    return false\n  }\n\n  for (let f in filter) {\n    if (f[0] === '#') {\n      let tagName = f.slice(1)\n      let values = filter[`#${tagName}`]\n      if (values && !event.tags.find(([t, v]) => t === f.slice(1) && values!.indexOf(v) !== -1)) return false\n    }\n  }\n\n  if (filter.since && event.created_at < filter.since) return false\n  if (filter.until && event.created_at > filter.until) return false\n\n  return true\n}\n\nexport function matchFilters(filters: Filter[], event: Event): boolean {\n  for (let i = 0; i < filters.length; i++) {\n    if (matchFilter(filters[i], event)) {\n      return true\n    }\n  }\n  return false\n}\n\nexport function mergeFilters(...filters: Filter[]): Filter {\n  let result: Filter = {}\n  for (let i = 0; i < filters.length; i++) {\n    let filter = filters[i]\n    Object.entries(filter).forEach(([property, values]) => {\n      if (property === 'kinds' || property === 'ids' || property === 'authors' || property[0] === '#') {\n        // @ts-ignore\n        result[property] = result[property] || []\n        // @ts-ignore\n        for (let v = 0; v < values.length; v++) {\n          // @ts-ignore\n          let value = values[v]\n          // @ts-ignore\n          if (!result[property].includes(value)) result[property].push(value)\n        }\n      }\n    })\n\n    if (filter.limit && (!result.limit || filter.limit > result.limit)) result.limit = filter.limit\n    if (filter.until && (!result.until || filter.until > result.until)) result.until = filter.until\n    if (filter.since && (!result.since || filter.since < result.since)) result.since = filter.since\n  }\n\n  return result\n}\n\n/**\n * Calculate the intrinsic limit of a filter.\n * This function returns a positive integer, or `Infinity` if there is no intrinsic limit.\n */\nexport function getFilterLimit(filter: Filter): number {\n  if (filter.ids && !filter.ids.length) return 0\n  if (filter.kinds && !filter.kinds.length) return 0\n  if (filter.authors && !filter.authors.length) return 0\n\n  for (const [key, value] of Object.entries(filter)) {\n    if (key[0] === '#' && Array.isArray(value) && !value.length) return 0\n  }\n\n  return Math.min(\n    // The `limit` property creates an artificial limit.\n    Math.max(0, filter.limit ?? Infinity),\n\n    // There can only be one event per `id`.\n    filter.ids?.length ?? Infinity,\n\n    // Replaceable events are limited by the number of authors and kinds.\n    filter.authors?.length && filter.kinds?.every(kind => isReplaceableKind(kind))\n      ? filter.authors.length * filter.kinds.length\n      : Infinity,\n\n    // Parameterized replaceable events are limited by the number of authors, kinds, and \"d\" tags.\n    filter.authors?.length && filter.kinds?.every(kind => isAddressableKind(kind)) && filter['#d']?.length\n      ? filter.authors.length * filter.kinds.length * filter['#d'].length\n      : Infinity,\n  )\n}\n", "export function getHex64(json: string, field: string): string {\n  let len = field.length + 3\n  let idx = json.indexOf(`\"${field}\":`) + len\n  let s = json.slice(idx).indexOf(`\"`) + idx + 1\n  return json.slice(s, s + 64)\n}\n\nexport function getInt(json: string, field: string): number {\n  let len = field.length\n  let idx = json.indexOf(`\"${field}\":`) + len + 3\n  let sliced = json.slice(idx)\n  let end = Math.min(sliced.indexOf(','), sliced.indexOf('}'))\n  return parseInt(sliced.slice(0, end), 10)\n}\n\nexport function getSubscriptionId(json: string): string | null {\n  let idx = json.slice(0, 22).indexOf(`\"EVENT\"`)\n  if (idx === -1) return null\n\n  let pstart = json.slice(idx + 7 + 1).indexOf(`\"`)\n  if (pstart === -1) return null\n  let start = idx + 7 + 1 + pstart\n\n  let pend = json.slice(start + 1, 80).indexOf(`\"`)\n  if (pend === -1) return null\n  let end = start + 1 + pend\n\n  return json.slice(start + 1, end)\n}\n\nexport function matchEventId(json: string, id: string): boolean {\n  return id === getHex64(json, 'id')\n}\n\nexport function matchEventPubkey(json: string, pubkey: string): boolean {\n  return pubkey === getHex64(json, 'pubkey')\n}\n\nexport function matchEventKind(json: string, kind: number): boolean {\n  return kind === getInt(json, 'kind')\n}\n", "import { EventTemplate } from './core.ts'\nimport { ClientAuth } from './kinds.ts'\n\n/**\n * creates an EventTemplate for an AUTH event to be signed.\n */\nexport function makeAuthEvent(relayURL: string, challenge: string): EventTemplate {\n  return {\n    kind: ClientAuth,\n    created_at: Math.floor(Date.now() / 1000),\n    tags: [\n      ['relay', relayURL],\n      ['challenge', challenge],\n    ],\n    content: '',\n  }\n}\n", "import { verifiedSymbol, type Event, type Nostr, VerifiedEvent } from './core.ts'\n\nexport async function yieldThread() {\n  return new Promise<void>((resolve, reject) => {\n    try {\n      // Check if MessageChannel is available\n      if (typeof MessageChannel !== 'undefined') {\n        const ch = new MessageChannel()\n        const handler = () => {\n          // @ts-ignore (typescript thinks this property should be called `removeListener`, but in fact it's `removeEventListener`)\n          ch.port1.removeEventListener('message', handler)\n          resolve()\n        }\n        // @ts-ignore (typescript thinks this property should be called `addListener`, but in fact it's `addEventListener`)\n        ch.port1.addEventListener('message', handler)\n        ch.port2.postMessage(0)\n        ch.port1.start()\n      } else {\n        if (typeof setImmediate !== 'undefined') {\n          setImmediate(resolve)\n        } else if (typeof setTimeout !== 'undefined') {\n          setTimeout(resolve, 0)\n        } else {\n          // Last resort - resolve immediately\n          resolve()\n        }\n      }\n    } catch (e) {\n      console.error('during yield: ', e)\n      reject(e)\n    }\n  })\n}\n\nexport const alwaysTrue: Nostr['verifyEvent'] = (t: Event): t is VerifiedEvent => {\n  t[verifiedSymbol] = true\n  return true\n}\n", "/* global WebSocket */\n\nimport type { Event, EventTemplate, VerifiedEvent, Nostr, NostrEvent } from './core.ts'\nimport { matchFilters, type Filter } from './filter.ts'\nimport { getHex64, getSubscriptionId } from './fakejson.ts'\nimport { Queue, normalizeURL } from './utils.ts'\nimport { makeAuthEvent } from './nip42.ts'\nimport { yieldThread } from './helpers.ts'\n\ntype RelayWebSocket = WebSocket & {\n  ping?(): void\n  on?(event: 'pong', listener: () => void): any\n}\n\nexport type AbstractRelayConstructorOptions = {\n  verifyEvent: Nostr['verifyEvent']\n  websocketImplementation?: typeof WebSocket\n  enablePing?: boolean\n  enableReconnect?: boolean\n}\n\nexport class SendingOnClosedConnection extends Error {\n  constructor(message: string, relay: string) {\n    super(`Tried to send message '${message} on a closed connection to ${relay}.`)\n    this.name = 'SendingOnClosedConnection'\n  }\n}\n\nexport class AbstractRelay {\n  public readonly url: string\n  private _connected: boolean = false\n\n  public onclose: (() => void) | null = null\n  public onnotice: (msg: string) => void = msg => console.debug(`NOTICE from ${this.url}: ${msg}`)\n  public onauth: undefined | ((evt: EventTemplate) => Promise<VerifiedEvent>)\n\n  public baseEoseTimeout: number = 4400\n  public publishTimeout: number = 4400\n  public pingFrequency: number = 29000\n  public pingTimeout: number = 20000\n  public resubscribeBackoff: number[] = [10000, 10000, 10000, 20000, 20000, 30000, 60000]\n  public openSubs: Map<string, Subscription> = new Map()\n  public enablePing: boolean | undefined\n  public enableReconnect: boolean\n  private reconnectTimeoutHandle: ReturnType<typeof setTimeout> | undefined\n  private pingIntervalHandle: ReturnType<typeof setInterval> | undefined\n  private reconnectAttempts: number = 0\n  private closedIntentionally: boolean = false\n\n  private connectionPromise: Promise<void> | undefined\n  private openCountRequests = new Map<string, CountResolver>()\n  private openEventPublishes = new Map<string, EventPublishResolver>()\n  private ws: RelayWebSocket | undefined\n  private incomingMessageQueue = new Queue<string>()\n  private queueRunning = false\n  private challenge: string | undefined\n  private authPromise: Promise<string> | undefined\n  private serial: number = 0\n  private verifyEvent: Nostr['verifyEvent']\n\n  private _WebSocket: typeof WebSocket\n\n  constructor(url: string, opts: AbstractRelayConstructorOptions) {\n    this.url = normalizeURL(url)\n    this.verifyEvent = opts.verifyEvent\n    this._WebSocket = opts.websocketImplementation || WebSocket\n    this.enablePing = opts.enablePing\n    this.enableReconnect = opts.enableReconnect || false\n  }\n\n  static async connect(\n    url: string,\n    opts: AbstractRelayConstructorOptions & Parameters<AbstractRelay['connect']>[0],\n  ): Promise<AbstractRelay> {\n    const relay = new AbstractRelay(url, opts)\n    await relay.connect(opts)\n    return relay\n  }\n\n  private closeAllSubscriptions(reason: string) {\n    for (let [_, sub] of this.openSubs) {\n      sub.close(reason)\n    }\n    this.openSubs.clear()\n\n    for (let [_, ep] of this.openEventPublishes) {\n      ep.reject(new Error(reason))\n    }\n    this.openEventPublishes.clear()\n\n    for (let [_, cr] of this.openCountRequests) {\n      cr.reject(new Error(reason))\n    }\n    this.openCountRequests.clear()\n  }\n\n  public get connected(): boolean {\n    return this._connected\n  }\n\n  private async reconnect(): Promise<void> {\n    const backoff = this.resubscribeBackoff[Math.min(this.reconnectAttempts, this.resubscribeBackoff.length - 1)]\n    this.reconnectAttempts++\n\n    this.reconnectTimeoutHandle = setTimeout(async () => {\n      try {\n        await this.connect()\n      } catch (err) {\n        // this will be called again through onclose/onerror\n      }\n    }, backoff)\n  }\n\n  private handleHardClose(reason: string) {\n    if (this.pingIntervalHandle) {\n      clearInterval(this.pingIntervalHandle)\n      this.pingIntervalHandle = undefined\n    }\n\n    this._connected = false\n    this.connectionPromise = undefined\n\n    const wasIntentional = this.closedIntentionally\n    this.closedIntentionally = false // reset for next time\n\n    this.onclose?.()\n\n    if (this.enableReconnect && !wasIntentional) {\n      this.reconnect()\n    } else {\n      this.closeAllSubscriptions(reason)\n    }\n  }\n\n  public async connect(opts?: { timeout?: number; abort?: AbortSignal }): Promise<void> {\n    let connectionTimeoutHandle: ReturnType<typeof setTimeout> | undefined\n\n    if (this.connectionPromise) return this.connectionPromise\n\n    this.challenge = undefined\n    this.authPromise = undefined\n    this.connectionPromise = new Promise((resolve, reject) => {\n      if (opts?.timeout) {\n        connectionTimeoutHandle = setTimeout(() => {\n          reject('connection timed out')\n          this.connectionPromise = undefined\n          this.onclose?.()\n          this.closeAllSubscriptions('relay connection timed out')\n        }, opts.timeout)\n      }\n\n      if (opts?.abort) {\n        opts.abort.onabort = reject\n      }\n\n      try {\n        this.ws = new this._WebSocket(this.url)\n      } catch (err) {\n        clearTimeout(connectionTimeoutHandle)\n        reject(err)\n        return\n      }\n\n      this.ws.onopen = () => {\n        if (this.reconnectTimeoutHandle) {\n          clearTimeout(this.reconnectTimeoutHandle)\n          this.reconnectTimeoutHandle = undefined\n        }\n        clearTimeout(connectionTimeoutHandle)\n        this._connected = true\n\n        const isReconnection = this.reconnectAttempts > 0\n        this.reconnectAttempts = 0\n\n        // resubscribe to all open subscriptions\n        for (const sub of this.openSubs.values()) {\n          sub.eosed = false\n          if (isReconnection) {\n            for (let f = 0; f < sub.filters.length; f++) {\n              if (sub.lastEmitted) {\n                sub.filters[f].since = sub.lastEmitted + 1\n              }\n            }\n          }\n          sub.fire()\n        }\n\n        if (this.enablePing) {\n          this.pingIntervalHandle = setInterval(() => this.pingpong(), this.pingFrequency)\n        }\n        resolve()\n      }\n\n      this.ws.onerror = ev => {\n        clearTimeout(connectionTimeoutHandle)\n        reject((ev as any).message || 'websocket error')\n        this.handleHardClose('relay connection errored')\n      }\n\n      this.ws.onclose = ev => {\n        clearTimeout(connectionTimeoutHandle)\n        reject((ev as any).message || 'websocket closed')\n        this.handleHardClose('relay connection closed')\n      }\n\n      this.ws.onmessage = this._onmessage.bind(this)\n    })\n\n    return this.connectionPromise\n  }\n\n  private waitForPingPong() {\n    return new Promise(resolve => {\n      // listen for pong\n      ;(this.ws as any).once('pong', () => resolve(true))\n      // send a ping\n      this.ws!.ping!()\n    })\n  }\n\n  private waitForDummyReq() {\n    return new Promise((resolve, reject) => {\n      if (!this.connectionPromise) return reject(new Error(`no connection to ${this.url}, can't ping`))\n\n      // make a dummy request with expected empty eose reply\n      // [\"REQ\", \"_\", {\"ids\":[\"aaaa...aaaa\"], \"limit\": 0}]\n      try {\n        const sub = this.subscribe(\n          [{ ids: ['aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'], limit: 0 }],\n          {\n            label: 'forced-ping',\n            oneose: () => {\n              resolve(true)\n              sub.close()\n            },\n            onclose() {\n              // if we get a CLOSED it's because the relay is alive\n              resolve(true)\n            },\n            eoseTimeout: this.pingTimeout + 1000,\n          },\n        )\n      } catch (err) {\n        reject(err)\n      }\n    })\n  }\n\n  // nodejs requires this magic here to ensure connections are closed when internet goes off and stuff\n  // in browsers it's done automatically. see https://github.com/nbd-wtf/nostr-tools/issues/491\n  private async pingpong() {\n    // if the websocket is connected\n    if (this.ws?.readyState === 1) {\n      // wait for either a ping-pong reply or a timeout\n      const result = await Promise.any([\n        // browsers don't have ping so use a dummy req\n        this.ws && this.ws.ping && (this.ws as any).once ? this.waitForPingPong() : this.waitForDummyReq(),\n        new Promise(res => setTimeout(() => res(false), this.pingTimeout)),\n      ])\n\n      if (!result) {\n        // pingpong closing socket\n        if (this.ws?.readyState === this._WebSocket.OPEN) {\n          this.ws?.close()\n        }\n      }\n    }\n  }\n\n  private async runQueue() {\n    this.queueRunning = true\n    while (true) {\n      if (false === this.handleNext()) {\n        break\n      }\n      await yieldThread()\n    }\n    this.queueRunning = false\n  }\n\n  private handleNext(): undefined | false {\n    const json = this.incomingMessageQueue.dequeue()\n    if (!json) {\n      return false\n    }\n\n    // shortcut EVENT sub\n    const subid = getSubscriptionId(json)\n    if (subid) {\n      const so = this.openSubs.get(subid as string)\n      if (!so) {\n        // this is an EVENT message, but for a subscription we don't have, so just stop here\n        return\n      }\n\n      // this will be called only when this message is a EVENT message for a subscription we have\n      // we do this before parsing the JSON to not have to do that for duplicate events\n      //   since JSON parsing is slow\n      const id = getHex64(json, 'id')\n      const alreadyHave = so.alreadyHaveEvent?.(id)\n\n      // notify any interested client that the relay has this event\n      // (do this after alreadyHaveEvent() because the client may rely on this to answer that)\n      so.receivedEvent?.(this, id)\n\n      if (alreadyHave) {\n        // if we had already seen this event we can just stop here\n        return\n      }\n    }\n\n    try {\n      let data = JSON.parse(json)\n      // we won't do any checks against the data since all failures (i.e. invalid messages from relays)\n      // will naturally be caught by the encompassing try..catch block\n\n      switch (data[0]) {\n        case 'EVENT': {\n          const so = this.openSubs.get(data[1] as string) as Subscription\n          const event = data[2] as NostrEvent\n          if (this.verifyEvent(event) && matchFilters(so.filters, event)) {\n            so.onevent(event)\n          }\n          if (!so.lastEmitted || so.lastEmitted < event.created_at) so.lastEmitted = event.created_at\n          return\n        }\n        case 'COUNT': {\n          const id: string = data[1]\n          const payload = data[2] as { count: number }\n          const cr = this.openCountRequests.get(id) as CountResolver\n          if (cr) {\n            cr.resolve(payload.count)\n            this.openCountRequests.delete(id)\n          }\n          return\n        }\n        case 'EOSE': {\n          const so = this.openSubs.get(data[1] as string)\n          if (!so) return\n          so.receivedEose()\n          return\n        }\n        case 'OK': {\n          const id: string = data[1]\n          const ok: boolean = data[2]\n          const reason: string = data[3]\n          const ep = this.openEventPublishes.get(id) as EventPublishResolver\n          if (ep) {\n            clearTimeout(ep.timeout)\n            if (ok) ep.resolve(reason)\n            else ep.reject(new Error(reason))\n            this.openEventPublishes.delete(id)\n          }\n          return\n        }\n        case 'CLOSED': {\n          const id: string = data[1]\n          const so = this.openSubs.get(id)\n          if (!so) return\n          so.closed = true\n          so.close(data[2] as string)\n          return\n        }\n        case 'NOTICE': {\n          this.onnotice(data[1] as string)\n          return\n        }\n        case 'AUTH': {\n          this.challenge = data[1] as string\n          if (this.onauth) {\n            this.auth(this.onauth)\n          }\n          return\n        }\n        default: {\n          const so = this.openSubs.get(data[1])\n          so?.oncustom?.(data)\n          return\n        }\n      }\n    } catch (err) {\n      return\n    }\n  }\n\n  public async send(message: string) {\n    if (!this.connectionPromise) throw new SendingOnClosedConnection(message, this.url)\n\n    this.connectionPromise.then(() => {\n      this.ws?.send(message)\n    })\n  }\n\n  public async auth(signAuthEvent: (evt: EventTemplate) => Promise<VerifiedEvent>): Promise<string> {\n    const challenge = this.challenge\n    if (!challenge) throw new Error(\"can't perform auth, no challenge was received\")\n    if (this.authPromise) return this.authPromise\n\n    this.authPromise = new Promise<string>(async (resolve, reject) => {\n      try {\n        let evt = await signAuthEvent(makeAuthEvent(this.url, challenge))\n        let timeout = setTimeout(() => {\n          let ep = this.openEventPublishes.get(evt.id) as EventPublishResolver\n          if (ep) {\n            ep.reject(new Error('auth timed out'))\n            this.openEventPublishes.delete(evt.id)\n          }\n        }, this.publishTimeout)\n        this.openEventPublishes.set(evt.id, { resolve, reject, timeout })\n        this.send('[\"AUTH\",' + JSON.stringify(evt) + ']')\n      } catch (err) {\n        console.warn('subscribe auth function failed:', err)\n      }\n    })\n    return this.authPromise\n  }\n\n  public async publish(event: Event): Promise<string> {\n    const ret = new Promise<string>((resolve, reject) => {\n      const timeout = setTimeout(() => {\n        const ep = this.openEventPublishes.get(event.id) as EventPublishResolver\n        if (ep) {\n          ep.reject(new Error('publish timed out'))\n          this.openEventPublishes.delete(event.id)\n        }\n      }, this.publishTimeout)\n      this.openEventPublishes.set(event.id, { resolve, reject, timeout })\n    })\n    this.send('[\"EVENT\",' + JSON.stringify(event) + ']')\n    return ret\n  }\n\n  public async count(filters: Filter[], params: { id?: string | null }): Promise<number> {\n    this.serial++\n    const id = params?.id || 'count:' + this.serial\n    const ret = new Promise<number>((resolve, reject) => {\n      this.openCountRequests.set(id, { resolve, reject })\n    })\n    this.send('[\"COUNT\",\"' + id + '\",' + JSON.stringify(filters).substring(1))\n    return ret\n  }\n\n  public subscribe(\n    filters: Filter[],\n    params: Partial<SubscriptionParams> & { label?: string; id?: string },\n  ): Subscription {\n    const sub = this.prepareSubscription(filters, params)\n    sub.fire()\n\n    if (params.abort) {\n      params.abort.onabort = () => sub.close(String(params.abort!.reason || '<aborted>'))\n    }\n\n    return sub\n  }\n\n  public prepareSubscription(\n    filters: Filter[],\n    params: Partial<SubscriptionParams> & { label?: string; id?: string },\n  ): Subscription {\n    this.serial++\n    const id = params.id || (params.label ? params.label + ':' : 'sub:') + this.serial\n    const subscription = new Subscription(this, id, filters, params)\n    this.openSubs.set(id, subscription)\n    return subscription\n  }\n\n  public close() {\n    this.closedIntentionally = true\n    if (this.reconnectTimeoutHandle) {\n      clearTimeout(this.reconnectTimeoutHandle)\n      this.reconnectTimeoutHandle = undefined\n    }\n    if (this.pingIntervalHandle) {\n      clearInterval(this.pingIntervalHandle)\n      this.pingIntervalHandle = undefined\n    }\n    this.closeAllSubscriptions('relay connection closed by us')\n    this._connected = false\n    this.onclose?.()\n    if (this.ws?.readyState === this._WebSocket.OPEN) {\n      this.ws?.close()\n    }\n  }\n\n  // this is the function assigned to this.ws.onmessage\n  // it's exposed for testing and debugging purposes\n  public _onmessage(ev: MessageEvent<any>) {\n    this.incomingMessageQueue.enqueue(ev.data as string)\n    if (!this.queueRunning) {\n      this.runQueue()\n    }\n  }\n}\n\nexport class Subscription {\n  public readonly relay: AbstractRelay\n  public readonly id: string\n\n  public lastEmitted: number | undefined\n  public closed: boolean = false\n  public eosed: boolean = false\n  public filters: Filter[]\n  public alreadyHaveEvent: ((id: string) => boolean) | undefined\n  public receivedEvent: ((relay: AbstractRelay, id: string) => void) | undefined\n\n  public onevent: (evt: Event) => void\n  public oneose: (() => void) | undefined\n  public onclose: ((reason: string) => void) | undefined\n\n  // will get any messages that have this subscription id as their second item and are not default standard\n  public oncustom: ((msg: string[]) => void) | undefined\n\n  public eoseTimeout: number\n  private eoseTimeoutHandle: ReturnType<typeof setTimeout> | undefined\n\n  constructor(relay: AbstractRelay, id: string, filters: Filter[], params: SubscriptionParams) {\n    if (filters.length === 0) throw new Error(\"subscription can't be created with zero filters\")\n\n    this.relay = relay\n    this.filters = filters\n    this.id = id\n    this.alreadyHaveEvent = params.alreadyHaveEvent\n    this.receivedEvent = params.receivedEvent\n    this.eoseTimeout = params.eoseTimeout || relay.baseEoseTimeout\n\n    this.oneose = params.oneose\n    this.onclose = params.onclose\n    this.onevent =\n      params.onevent ||\n      (event => {\n        console.warn(\n          `onevent() callback not defined for subscription '${this.id}' in relay ${this.relay.url}. event received:`,\n          event,\n        )\n      })\n  }\n\n  public fire() {\n    this.relay.send('[\"REQ\",\"' + this.id + '\",' + JSON.stringify(this.filters).substring(1))\n\n    // only now we start counting the eoseTimeout\n    this.eoseTimeoutHandle = setTimeout(this.receivedEose.bind(this), this.eoseTimeout)\n  }\n\n  public receivedEose() {\n    if (this.eosed) return\n    clearTimeout(this.eoseTimeoutHandle)\n    this.eosed = true\n    this.oneose?.()\n  }\n\n  public close(reason: string = 'closed by caller') {\n    if (!this.closed && this.relay.connected) {\n      // if the connection was closed by the user calling .close() we will send a CLOSE message\n      // otherwise this._open will be already set to false so we will skip this\n      try {\n        this.relay.send('[\"CLOSE\",' + JSON.stringify(this.id) + ']')\n      } catch (err) {\n        if (err instanceof SendingOnClosedConnection) {\n          /* doesn't matter, it's ok */\n        } else {\n          throw err\n        }\n      }\n      this.closed = true\n    }\n    this.relay.openSubs.delete(this.id)\n    this.onclose?.(reason)\n  }\n}\n\nexport type SubscriptionParams = {\n  onevent?: (evt: Event) => void\n  oneose?: () => void\n  onclose?: (reason: string) => void\n  alreadyHaveEvent?: (id: string) => boolean\n  receivedEvent?: (relay: AbstractRelay, id: string) => void\n  eoseTimeout?: number\n  abort?: AbortSignal\n}\n\nexport type CountResolver = {\n  resolve: (count: number) => void\n  reject: (err: Error) => void\n}\n\nexport type EventPublishResolver = {\n  resolve: (reason: string) => void\n  reject: (err: Error) => void\n  timeout: ReturnType<typeof setTimeout>\n}\n", "/* global WebSocket */\n\nimport {\n  AbstractRelay as AbstractRelay,\n  SubscriptionParams,\n  Subscription,\n  type AbstractRelayConstructorOptions,\n} from './abstract-relay.ts'\nimport { normalizeURL } from './utils.ts'\n\nimport type { Event, EventTemplate, Nostr, VerifiedEvent } from './core.ts'\nimport { type Filter } from './filter.ts'\nimport { alwaysTrue } from './helpers.ts'\n\nexport type SubCloser = { close: (reason?: string) => void }\n\nexport type AbstractPoolConstructorOptions = AbstractRelayConstructorOptions & {\n  // automaticallyAuth takes a relay URL and should return null\n  // in case that relay shouldn't be authenticated against\n  // or a function to sign the AUTH event template otherwise (that function may still throw in case of failure)\n  automaticallyAuth?: (relayURL: string) => null | ((event: EventTemplate) => Promise<VerifiedEvent>)\n}\n\nexport type SubscribeManyParams = Omit<SubscriptionParams, 'onclose'> & {\n  maxWait?: number\n  abort?: AbortSignal\n  onclose?: (reasons: string[]) => void\n  onauth?: (event: EventTemplate) => Promise<VerifiedEvent>\n  id?: string\n  label?: string\n}\n\nexport class AbstractSimplePool {\n  protected relays: Map<string, AbstractRelay> = new Map()\n  public seenOn: Map<string, Set<AbstractRelay>> = new Map()\n  public trackRelays: boolean = false\n\n  public verifyEvent: Nostr['verifyEvent']\n  public enablePing: boolean | undefined\n  public enableReconnect: boolean\n  public automaticallyAuth?: (relayURL: string) => null | ((event: EventTemplate) => Promise<VerifiedEvent>)\n  public trustedRelayURLs: Set<string> = new Set()\n\n  private _WebSocket?: typeof WebSocket\n\n  constructor(opts: AbstractPoolConstructorOptions) {\n    this.verifyEvent = opts.verifyEvent\n    this._WebSocket = opts.websocketImplementation\n    this.enablePing = opts.enablePing\n    this.enableReconnect = opts.enableReconnect || false\n    this.automaticallyAuth = opts.automaticallyAuth\n  }\n\n  async ensureRelay(\n    url: string,\n    params?: {\n      connectionTimeout?: number\n      abort?: AbortSignal\n    },\n  ): Promise<AbstractRelay> {\n    url = normalizeURL(url)\n\n    let relay = this.relays.get(url)\n    if (!relay) {\n      relay = new AbstractRelay(url, {\n        verifyEvent: this.trustedRelayURLs.has(url) ? alwaysTrue : this.verifyEvent,\n        websocketImplementation: this._WebSocket,\n        enablePing: this.enablePing,\n        enableReconnect: this.enableReconnect,\n      })\n      relay.onclose = () => {\n        if (relay && !relay.enableReconnect) {\n          this.relays.delete(url)\n        }\n      }\n      this.relays.set(url, relay)\n    }\n\n    if (this.automaticallyAuth) {\n      const authSignerFn = this.automaticallyAuth(url)\n      if (authSignerFn) {\n        relay.onauth = authSignerFn\n      }\n    }\n\n    await relay.connect({\n      timeout: params?.connectionTimeout,\n      abort: params?.abort,\n    })\n\n    return relay\n  }\n\n  close(relays: string[]) {\n    relays.map(normalizeURL).forEach(url => {\n      this.relays.get(url)?.close()\n      this.relays.delete(url)\n    })\n  }\n\n  subscribe(relays: string[], filter: Filter, params: SubscribeManyParams): SubCloser {\n    const request: { url: string; filter: Filter }[] = []\n    for (let i = 0; i < relays.length; i++) {\n      const url = normalizeURL(relays[i])\n      if (!request.find(r => r.url === url)) {\n        request.push({ url, filter: filter })\n      }\n    }\n\n    return this.subscribeMap(request, params)\n  }\n\n  subscribeMany(relays: string[], filter: Filter, params: SubscribeManyParams): SubCloser {\n    const request: { url: string; filter: Filter }[] = []\n    const uniqUrls: string[] = []\n    for (let i = 0; i < relays.length; i++) {\n      const url = normalizeURL(relays[i])\n      if (uniqUrls.indexOf(url) === -1) {\n        uniqUrls.push(url)\n        request.push({ url, filter: filter })\n      }\n    }\n\n    return this.subscribeMap(request, params)\n  }\n\n  subscribeMap(requests: { url: string; filter: Filter }[], params: SubscribeManyParams): SubCloser {\n    const grouped = new Map<string, Filter[]>()\n    for (const req of requests) {\n      const { url, filter } = req\n      if (!grouped.has(url)) grouped.set(url, [])\n      grouped.get(url)!.push(filter)\n    }\n    const groupedRequests = Array.from(grouped.entries()).map(([url, filters]) => ({ url, filters }))\n\n    if (this.trackRelays) {\n      params.receivedEvent = (relay: AbstractRelay, id: string) => {\n        let set = this.seenOn.get(id)\n        if (!set) {\n          set = new Set()\n          this.seenOn.set(id, set)\n        }\n        set.add(relay)\n      }\n    }\n\n    const _knownIds = new Set<string>()\n    const subs: Subscription[] = []\n\n    // batch all EOSEs into a single\n    const eosesReceived: boolean[] = []\n    let handleEose = (i: number) => {\n      if (eosesReceived[i]) return // do not act twice for the same relay\n      eosesReceived[i] = true\n      if (eosesReceived.filter(a => a).length === groupedRequests.length) {\n        params.oneose?.()\n        handleEose = () => {}\n      }\n    }\n    // batch all closes into a single\n    const closesReceived: string[] = []\n    let handleClose = (i: number, reason: string) => {\n      if (closesReceived[i]) return // do not act twice for the same relay\n      handleEose(i)\n      closesReceived[i] = reason\n      if (closesReceived.filter(a => a).length === groupedRequests.length) {\n        params.onclose?.(closesReceived)\n        handleClose = () => {}\n      }\n    }\n\n    const localAlreadyHaveEventHandler = (id: string) => {\n      if (params.alreadyHaveEvent?.(id)) {\n        return true\n      }\n      const have = _knownIds.has(id)\n      _knownIds.add(id)\n      return have\n    }\n\n    // open a subscription in all given relays\n    const allOpened = Promise.all(\n      groupedRequests.map(async ({ url, filters }, i) => {\n        let relay: AbstractRelay\n        try {\n          relay = await this.ensureRelay(url, {\n            connectionTimeout: params.maxWait ? Math.max(params.maxWait * 0.8, params.maxWait - 1000) : undefined,\n            abort: params.abort,\n          })\n        } catch (err) {\n          handleClose(i, (err as any)?.message || String(err))\n          return\n        }\n\n        let subscription = relay.subscribe(filters, {\n          ...params,\n          oneose: () => handleEose(i),\n          onclose: reason => {\n            if (reason.startsWith('auth-required: ') && params.onauth) {\n              relay\n                .auth(params.onauth)\n                .then(() => {\n                  relay.subscribe(filters, {\n                    ...params,\n                    oneose: () => handleEose(i),\n                    onclose: reason => {\n                      handleClose(i, reason) // the second time we won't try to auth anymore\n                    },\n                    alreadyHaveEvent: localAlreadyHaveEventHandler,\n                    eoseTimeout: params.maxWait,\n                    abort: params.abort,\n                  })\n                })\n                .catch(err => {\n                  handleClose(i, `auth was required and attempted, but failed with: ${err}`)\n                })\n            } else {\n              handleClose(i, reason)\n            }\n          },\n          alreadyHaveEvent: localAlreadyHaveEventHandler,\n          eoseTimeout: params.maxWait,\n          abort: params.abort,\n        })\n\n        subs.push(subscription)\n      }),\n    )\n\n    return {\n      async close(reason?: string) {\n        await allOpened\n        subs.forEach(sub => {\n          sub.close(reason)\n        })\n      },\n    }\n  }\n\n  subscribeEose(\n    relays: string[],\n    filter: Filter,\n    params: Pick<SubscribeManyParams, 'label' | 'id' | 'onevent' | 'onclose' | 'maxWait' | 'onauth'>,\n  ): SubCloser {\n    const subcloser = this.subscribe(relays, filter, {\n      ...params,\n      oneose() {\n        subcloser.close('closed automatically on eose')\n      },\n    })\n    return subcloser\n  }\n\n  subscribeManyEose(\n    relays: string[],\n    filter: Filter,\n    params: Pick<SubscribeManyParams, 'label' | 'id' | 'onevent' | 'onclose' | 'maxWait' | 'onauth'>,\n  ): SubCloser {\n    const subcloser = this.subscribeMany(relays, filter, {\n      ...params,\n      oneose() {\n        subcloser.close('closed automatically on eose')\n      },\n    })\n    return subcloser\n  }\n\n  async querySync(\n    relays: string[],\n    filter: Filter,\n    params?: Pick<SubscribeManyParams, 'label' | 'id' | 'maxWait'>,\n  ): Promise<Event[]> {\n    return new Promise(async resolve => {\n      const events: Event[] = []\n      this.subscribeEose(relays, filter, {\n        ...params,\n        onevent(event: Event) {\n          events.push(event)\n        },\n        onclose(_: string[]) {\n          resolve(events)\n        },\n      })\n    })\n  }\n\n  async get(\n    relays: string[],\n    filter: Filter,\n    params?: Pick<SubscribeManyParams, 'label' | 'id' | 'maxWait'>,\n  ): Promise<Event | null> {\n    filter.limit = 1\n    const events = await this.querySync(relays, filter, params)\n    events.sort((a, b) => b.created_at - a.created_at)\n    return events[0] || null\n  }\n\n  publish(\n    relays: string[],\n    event: Event,\n    options?: { onauth?: (evt: EventTemplate) => Promise<VerifiedEvent> },\n  ): Promise<string>[] {\n    return relays.map(normalizeURL).map(async (url, i, arr) => {\n      if (arr.indexOf(url) !== i) {\n        // duplicate\n        return Promise.reject('duplicate url')\n      }\n\n      let r = await this.ensureRelay(url)\n      return r\n        .publish(event)\n        .catch(async err => {\n          if (err instanceof Error && err.message.startsWith('auth-required: ') && options?.onauth) {\n            await r.auth(options.onauth)\n            return r.publish(event) // retry\n          }\n          throw err\n        })\n        .then(reason => {\n          if (this.trackRelays) {\n            let set = this.seenOn.get(event.id)\n            if (!set) {\n              set = new Set()\n              this.seenOn.set(event.id, set)\n            }\n            set.add(r)\n          }\n          return reason\n        })\n    })\n  }\n\n  listConnectionStatus(): Map<string, boolean> {\n    const map = new Map<string, boolean>()\n    this.relays.forEach((relay, url) => map.set(url, relay.connected))\n\n    return map\n  }\n\n  destroy(): void {\n    this.relays.forEach(conn => conn.close())\n    this.relays = new Map()\n  }\n}\n"],
   "mappings": "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",
   "names": ["target", "reason"]
 }
Index: package/lib/cjs/abstract-relay.js.map
===================================================================
--- package/lib/cjs/abstract-relay.js.map
+++ package/lib/cjs/abstract-relay.js.map
@@ -1,7 +1,7 @@
 {
   "version": 3,
   "sources": ["../../abstract-relay.ts", "../../utils.ts", "../../kinds.ts", "../../filter.ts", "../../fakejson.ts", "../../nip42.ts", "../../helpers.ts"],
-  "sourcesContent": ["/* global WebSocket */\n\nimport type { Event, EventTemplate, VerifiedEvent, Nostr, NostrEvent } from './core.ts'\nimport { matchFilters, type Filter } from './filter.ts'\nimport { getHex64, getSubscriptionId } from './fakejson.ts'\nimport { Queue, normalizeURL } from './utils.ts'\nimport { makeAuthEvent } from './nip42.ts'\nimport { yieldThread } from './helpers.ts'\n\ntype RelayWebSocket = WebSocket & {\n  ping?(): void\n  on?(event: 'pong', listener: () => void): any\n}\n\nexport type AbstractRelayConstructorOptions = {\n  verifyEvent: Nostr['verifyEvent']\n  websocketImplementation?: typeof WebSocket\n  enablePing?: boolean\n  enableReconnect?: boolean\n}\n\nexport class SendingOnClosedConnection extends Error {\n  constructor(message: string, relay: string) {\n    super(`Tried to send message '${message} on a closed connection to ${relay}.`)\n    this.name = 'SendingOnClosedConnection'\n  }\n}\n\nexport class AbstractRelay {\n  public readonly url: string\n  private _connected: boolean = false\n\n  public onclose: (() => void) | null = null\n  public onnotice: (msg: string) => void = msg => console.debug(`NOTICE from ${this.url}: ${msg}`)\n  public onauth: undefined | ((evt: EventTemplate) => Promise<VerifiedEvent>)\n\n  public baseEoseTimeout: number = 4400\n  public publishTimeout: number = 4400\n  public pingFrequency: number = 29000\n  public pingTimeout: number = 20000\n  public resubscribeBackoff: number[] = [10000, 10000, 10000, 20000, 20000, 30000, 60000]\n  public openSubs: Map<string, Subscription> = new Map()\n  public enablePing: boolean | undefined\n  public enableReconnect: boolean\n  private reconnectTimeoutHandle: ReturnType<typeof setTimeout> | undefined\n  private pingIntervalHandle: ReturnType<typeof setInterval> | undefined\n  private reconnectAttempts: number = 0\n  private closedIntentionally: boolean = false\n\n  private connectionPromise: Promise<void> | undefined\n  private openCountRequests = new Map<string, CountResolver>()\n  private openEventPublishes = new Map<string, EventPublishResolver>()\n  private ws: RelayWebSocket | undefined\n  private incomingMessageQueue = new Queue<string>()\n  private queueRunning = false\n  private challenge: string | undefined\n  private authPromise: Promise<string> | undefined\n  private serial: number = 0\n  private verifyEvent: Nostr['verifyEvent']\n\n  private _WebSocket: typeof WebSocket\n\n  constructor(url: string, opts: AbstractRelayConstructorOptions) {\n    this.url = normalizeURL(url)\n    this.verifyEvent = opts.verifyEvent\n    this._WebSocket = opts.websocketImplementation || WebSocket\n    this.enablePing = opts.enablePing\n    this.enableReconnect = opts.enableReconnect || false\n  }\n\n  static async connect(\n    url: string,\n    opts: AbstractRelayConstructorOptions & Parameters<AbstractRelay['connect']>[0],\n  ): Promise<AbstractRelay> {\n    const relay = new AbstractRelay(url, opts)\n    await relay.connect(opts)\n    return relay\n  }\n\n  private closeAllSubscriptions(reason: string) {\n    for (let [_, sub] of this.openSubs) {\n      sub.close(reason)\n    }\n    this.openSubs.clear()\n\n    for (let [_, ep] of this.openEventPublishes) {\n      ep.reject(new Error(reason))\n    }\n    this.openEventPublishes.clear()\n\n    for (let [_, cr] of this.openCountRequests) {\n      cr.reject(new Error(reason))\n    }\n    this.openCountRequests.clear()\n  }\n\n  public get connected(): boolean {\n    return this._connected\n  }\n\n  private async reconnect(): Promise<void> {\n    const backoff = this.resubscribeBackoff[Math.min(this.reconnectAttempts, this.resubscribeBackoff.length - 1)]\n    this.reconnectAttempts++\n\n    this.reconnectTimeoutHandle = setTimeout(async () => {\n      try {\n        await this.connect()\n      } catch (err) {\n        // this will be called again through onclose/onerror\n      }\n    }, backoff)\n  }\n\n  private handleHardClose(reason: string) {\n    if (this.pingIntervalHandle) {\n      clearInterval(this.pingIntervalHandle)\n      this.pingIntervalHandle = undefined\n    }\n\n    this._connected = false\n    this.connectionPromise = undefined\n\n    const wasIntentional = this.closedIntentionally\n    this.closedIntentionally = false // reset for next time\n\n    this.onclose?.()\n\n    if (this.enableReconnect && !wasIntentional) {\n      this.reconnect()\n    } else {\n      this.closeAllSubscriptions(reason)\n    }\n  }\n\n  public async connect(opts?: { timeout?: number; abort?: AbortSignal }): Promise<void> {\n    let connectionTimeoutHandle: ReturnType<typeof setTimeout> | undefined\n\n    if (this.connectionPromise) return this.connectionPromise\n\n    this.challenge = undefined\n    this.authPromise = undefined\n    this.connectionPromise = new Promise((resolve, reject) => {\n      if (opts?.timeout) {\n        connectionTimeoutHandle = setTimeout(() => {\n          reject('connection timed out')\n          this.connectionPromise = undefined\n          this.onclose?.()\n          this.closeAllSubscriptions('relay connection timed out')\n        }, opts.timeout)\n      }\n\n      if (opts?.abort) {\n        opts.abort.onabort = reject\n      }\n\n      try {\n        this.ws = new this._WebSocket(this.url)\n      } catch (err) {\n        clearTimeout(connectionTimeoutHandle)\n        reject(err)\n        return\n      }\n\n      this.ws.onopen = () => {\n        if (this.reconnectTimeoutHandle) {\n          clearTimeout(this.reconnectTimeoutHandle)\n          this.reconnectTimeoutHandle = undefined\n        }\n        clearTimeout(connectionTimeoutHandle)\n        this._connected = true\n\n        const isReconnection = this.reconnectAttempts > 0\n        this.reconnectAttempts = 0\n\n        // resubscribe to all open subscriptions\n        for (const sub of this.openSubs.values()) {\n          sub.eosed = false\n          if (isReconnection) {\n            for (let f = 0; f < sub.filters.length; f++) {\n              if (sub.lastEmitted) {\n                sub.filters[f].since = sub.lastEmitted + 1\n              }\n            }\n          }\n          sub.fire()\n        }\n\n        if (this.enablePing) {\n          this.pingIntervalHandle = setInterval(() => this.pingpong(), this.pingFrequency)\n        }\n        resolve()\n      }\n\n      this.ws.onerror = ev => {\n        clearTimeout(connectionTimeoutHandle)\n        reject((ev as any).message || 'websocket error')\n        this.handleHardClose('relay connection errored')\n      }\n\n      this.ws.onclose = ev => {\n        clearTimeout(connectionTimeoutHandle)\n        reject((ev as any).message || 'websocket closed')\n        this.handleHardClose('relay connection closed')\n      }\n\n      this.ws.onmessage = this._onmessage.bind(this)\n    })\n\n    return this.connectionPromise\n  }\n\n  private waitForPingPong() {\n    return new Promise(resolve => {\n      // listen for pong\n      ;(this.ws as any).once('pong', () => resolve(true))\n      // send a ping\n      this.ws!.ping!()\n    })\n  }\n\n  private waitForDummyReq() {\n    return new Promise((resolve, reject) => {\n      if (!this.connectionPromise) return reject(new Error(`no connection to ${this.url}, can't ping`))\n\n      // make a dummy request with expected empty eose reply\n      // [\"REQ\", \"_\", {\"ids\":[\"aaaa...aaaa\"], \"limit\": 0}]\n      try {\n        const sub = this.subscribe(\n          [{ ids: ['aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'], limit: 0 }],\n          {\n            label: 'forced-ping',\n            oneose: () => {\n              resolve(true)\n              sub.close()\n            },\n            onclose() {\n              // if we get a CLOSED it's because the relay is alive\n              resolve(true)\n            },\n            eoseTimeout: this.pingTimeout + 1000,\n          },\n        )\n      } catch (err) {\n        reject(err)\n      }\n    })\n  }\n\n  // nodejs requires this magic here to ensure connections are closed when internet goes off and stuff\n  // in browsers it's done automatically. see https://github.com/nbd-wtf/nostr-tools/issues/491\n  private async pingpong() {\n    // if the websocket is connected\n    if (this.ws?.readyState === 1) {\n      // wait for either a ping-pong reply or a timeout\n      const result = await Promise.any([\n        // browsers don't have ping so use a dummy req\n        this.ws && this.ws.ping && (this.ws as any).once ? this.waitForPingPong() : this.waitForDummyReq(),\n        new Promise(res => setTimeout(() => res(false), this.pingTimeout)),\n      ])\n\n      if (!result) {\n        // pingpong closing socket\n        if (this.ws?.readyState === this._WebSocket.OPEN) {\n          this.ws?.close()\n        }\n      }\n    }\n  }\n\n  private async runQueue() {\n    this.queueRunning = true\n    while (true) {\n      if (false === this.handleNext()) {\n        break\n      }\n      await yieldThread()\n    }\n    this.queueRunning = false\n  }\n\n  private handleNext(): undefined | false {\n    const json = this.incomingMessageQueue.dequeue()\n    if (!json) {\n      return false\n    }\n\n    // shortcut EVENT sub\n    const subid = getSubscriptionId(json)\n    if (subid) {\n      const so = this.openSubs.get(subid as string)\n      if (!so) {\n        // this is an EVENT message, but for a subscription we don't have, so just stop here\n        return\n      }\n\n      // this will be called only when this message is a EVENT message for a subscription we have\n      // we do this before parsing the JSON to not have to do that for duplicate events\n      //   since JSON parsing is slow\n      const id = getHex64(json, 'id')\n      const alreadyHave = so.alreadyHaveEvent?.(id)\n\n      // notify any interested client that the relay has this event\n      // (do this after alreadyHaveEvent() because the client may rely on this to answer that)\n      so.receivedEvent?.(this, id)\n\n      if (alreadyHave) {\n        // if we had already seen this event we can just stop here\n        return\n      }\n    }\n\n    try {\n      let data = JSON.parse(json)\n      // we won't do any checks against the data since all failures (i.e. invalid messages from relays)\n      // will naturally be caught by the encompassing try..catch block\n\n      switch (data[0]) {\n        case 'EVENT': {\n          const so = this.openSubs.get(data[1] as string) as Subscription\n          const event = data[2] as NostrEvent\n          if (this.verifyEvent(event) && matchFilters(so.filters, event)) {\n            so.onevent(event)\n          }\n          if (!so.lastEmitted || so.lastEmitted < event.created_at) so.lastEmitted = event.created_at\n          return\n        }\n        case 'COUNT': {\n          const id: string = data[1]\n          const payload = data[2] as { count: number }\n          const cr = this.openCountRequests.get(id) as CountResolver\n          if (cr) {\n            cr.resolve(payload.count)\n            this.openCountRequests.delete(id)\n          }\n          return\n        }\n        case 'EOSE': {\n          const so = this.openSubs.get(data[1] as string)\n          if (!so) return\n          so.receivedEose()\n          return\n        }\n        case 'OK': {\n          const id: string = data[1]\n          const ok: boolean = data[2]\n          const reason: string = data[3]\n          const ep = this.openEventPublishes.get(id) as EventPublishResolver\n          if (ep) {\n            clearTimeout(ep.timeout)\n            if (ok) ep.resolve(reason)\n            else ep.reject(new Error(reason))\n            this.openEventPublishes.delete(id)\n          }\n          return\n        }\n        case 'CLOSED': {\n          const id: string = data[1]\n          const so = this.openSubs.get(id)\n          if (!so) return\n          so.closed = true\n          so.close(data[2] as string)\n          return\n        }\n        case 'NOTICE': {\n          this.onnotice(data[1] as string)\n          return\n        }\n        case 'AUTH': {\n          this.challenge = data[1] as string\n          if (this.onauth) {\n            this.auth(this.onauth)\n          }\n          return\n        }\n        default: {\n          const so = this.openSubs.get(data[1])\n          so?.oncustom?.(data)\n          return\n        }\n      }\n    } catch (err) {\n      return\n    }\n  }\n\n  public async send(message: string) {\n    if (!this.connectionPromise) throw new SendingOnClosedConnection(message, this.url)\n\n    this.connectionPromise.then(() => {\n      this.ws?.send(message)\n    })\n  }\n\n  public async auth(signAuthEvent: (evt: EventTemplate) => Promise<VerifiedEvent>): Promise<string> {\n    const challenge = this.challenge\n    if (!challenge) throw new Error(\"can't perform auth, no challenge was received\")\n    if (this.authPromise) return this.authPromise\n\n    this.authPromise = new Promise<string>(async (resolve, reject) => {\n      try {\n        let evt = await signAuthEvent(makeAuthEvent(this.url, challenge))\n        let timeout = setTimeout(() => {\n          let ep = this.openEventPublishes.get(evt.id) as EventPublishResolver\n          if (ep) {\n            ep.reject(new Error('auth timed out'))\n            this.openEventPublishes.delete(evt.id)\n          }\n        }, this.publishTimeout)\n        this.openEventPublishes.set(evt.id, { resolve, reject, timeout })\n        this.send('[\"AUTH\",' + JSON.stringify(evt) + ']')\n      } catch (err) {\n        console.warn('subscribe auth function failed:', err)\n      }\n    })\n    return this.authPromise\n  }\n\n  public async publish(event: Event): Promise<string> {\n    const ret = new Promise<string>((resolve, reject) => {\n      const timeout = setTimeout(() => {\n        const ep = this.openEventPublishes.get(event.id) as EventPublishResolver\n        if (ep) {\n          ep.reject(new Error('publish timed out'))\n          this.openEventPublishes.delete(event.id)\n        }\n      }, this.publishTimeout)\n      this.openEventPublishes.set(event.id, { resolve, reject, timeout })\n    })\n    this.send('[\"EVENT\",' + JSON.stringify(event) + ']')\n    return ret\n  }\n\n  public async count(filters: Filter[], params: { id?: string | null }): Promise<number> {\n    this.serial++\n    const id = params?.id || 'count:' + this.serial\n    const ret = new Promise<number>((resolve, reject) => {\n      this.openCountRequests.set(id, { resolve, reject })\n    })\n    this.send('[\"COUNT\",\"' + id + '\",' + JSON.stringify(filters).substring(1))\n    return ret\n  }\n\n  public subscribe(\n    filters: Filter[],\n    params: Partial<SubscriptionParams> & { label?: string; id?: string },\n  ): Subscription {\n    const sub = this.prepareSubscription(filters, params)\n    sub.fire()\n\n    if (params.abort) {\n      params.abort.onabort = () => sub.close(String(params.abort!.reason || '<aborted>'))\n    }\n\n    return sub\n  }\n\n  public prepareSubscription(\n    filters: Filter[],\n    params: Partial<SubscriptionParams> & { label?: string; id?: string },\n  ): Subscription {\n    this.serial++\n    const id = params.id || (params.label ? params.label + ':' : 'sub:') + this.serial\n    const subscription = new Subscription(this, id, filters, params)\n    this.openSubs.set(id, subscription)\n    return subscription\n  }\n\n  public close() {\n    this.closedIntentionally = true\n    if (this.reconnectTimeoutHandle) {\n      clearTimeout(this.reconnectTimeoutHandle)\n      this.reconnectTimeoutHandle = undefined\n    }\n    if (this.pingIntervalHandle) {\n      clearInterval(this.pingIntervalHandle)\n      this.pingIntervalHandle = undefined\n    }\n    this.closeAllSubscriptions('relay connection closed by us')\n    this._connected = false\n    this.onclose?.()\n    if (this.ws?.readyState === this._WebSocket.OPEN) {\n      this.ws?.close()\n    }\n  }\n\n  // this is the function assigned to this.ws.onmessage\n  // it's exposed for testing and debugging purposes\n  public _onmessage(ev: MessageEvent<any>) {\n    this.incomingMessageQueue.enqueue(ev.data as string)\n    if (!this.queueRunning) {\n      this.runQueue()\n    }\n  }\n}\n\nexport class Subscription {\n  public readonly relay: AbstractRelay\n  public readonly id: string\n\n  public lastEmitted: number | undefined\n  public closed: boolean = false\n  public eosed: boolean = false\n  public filters: Filter[]\n  public alreadyHaveEvent: ((id: string) => boolean) | undefined\n  public receivedEvent: ((relay: AbstractRelay, id: string) => void) | undefined\n\n  public onevent: (evt: Event) => void\n  public oneose: (() => void) | undefined\n  public onclose: ((reason: string) => void) | undefined\n\n  // will get any messages that have this subscription id as their second item and are not default standard\n  public oncustom: ((msg: string[]) => void) | undefined\n\n  public eoseTimeout: number\n  private eoseTimeoutHandle: ReturnType<typeof setTimeout> | undefined\n\n  constructor(relay: AbstractRelay, id: string, filters: Filter[], params: SubscriptionParams) {\n    if (filters.length === 0) throw new Error(\"subscription can't be created with zero filters\")\n\n    this.relay = relay\n    this.filters = filters\n    this.id = id\n    this.alreadyHaveEvent = params.alreadyHaveEvent\n    this.receivedEvent = params.receivedEvent\n    this.eoseTimeout = params.eoseTimeout || relay.baseEoseTimeout\n\n    this.oneose = params.oneose\n    this.onclose = params.onclose\n    this.onevent =\n      params.onevent ||\n      (event => {\n        console.warn(\n          `onevent() callback not defined for subscription '${this.id}' in relay ${this.relay.url}. event received:`,\n          event,\n        )\n      })\n  }\n\n  public fire() {\n    this.relay.send('[\"REQ\",\"' + this.id + '\",' + JSON.stringify(this.filters).substring(1))\n\n    // only now we start counting the eoseTimeout\n    this.eoseTimeoutHandle = setTimeout(this.receivedEose.bind(this), this.eoseTimeout)\n  }\n\n  public receivedEose() {\n    if (this.eosed) return\n    clearTimeout(this.eoseTimeoutHandle)\n    this.eosed = true\n    this.oneose?.()\n  }\n\n  public close(reason: string = 'closed by caller') {\n    if (!this.closed && this.relay.connected) {\n      // if the connection was closed by the user calling .close() we will send a CLOSE message\n      // otherwise this._open will be already set to false so we will skip this\n      try {\n        this.relay.send('[\"CLOSE\",' + JSON.stringify(this.id) + ']')\n      } catch (err) {\n        if (err instanceof SendingOnClosedConnection) {\n          /* doesn't matter, it's ok */\n        } else {\n          throw err\n        }\n      }\n      this.closed = true\n    }\n    this.relay.openSubs.delete(this.id)\n    this.onclose?.(reason)\n  }\n}\n\nexport type SubscriptionParams = {\n  onevent?: (evt: Event) => void\n  oneose?: () => void\n  onclose?: (reason: string) => void\n  alreadyHaveEvent?: (id: string) => boolean\n  receivedEvent?: (relay: AbstractRelay, id: string) => void\n  eoseTimeout?: number\n  abort?: AbortSignal\n}\n\nexport type CountResolver = {\n  resolve: (count: number) => void\n  reject: (err: Error) => void\n}\n\nexport type EventPublishResolver = {\n  resolve: (reason: string) => void\n  reject: (err: Error) => void\n  timeout: ReturnType<typeof setTimeout>\n}\n", "import type { Event } from './core.ts'\n\nexport const utf8Decoder: TextDecoder = new TextDecoder('utf-8')\nexport const utf8Encoder: TextEncoder = new TextEncoder()\n\nexport { bytesToHex, hexToBytes } from '@noble/hashes/utils'\n\nexport function normalizeURL(url: string): string {\n  try {\n    if (url.indexOf('://') === -1) url = 'wss://' + url\n    let p = new URL(url)\n    if (p.protocol === 'http:') p.protocol = 'ws:'\n    else if (p.protocol === 'https:') p.protocol = 'wss:'\n    p.pathname = p.pathname.replace(/\\/+/g, '/')\n    if (p.pathname.endsWith('/')) p.pathname = p.pathname.slice(0, -1)\n    if ((p.port === '80' && p.protocol === 'ws:') || (p.port === '443' && p.protocol === 'wss:')) p.port = ''\n    p.searchParams.sort()\n    p.hash = ''\n    return p.toString()\n  } catch (e) {\n    throw new Error(`Invalid URL: ${url}`)\n  }\n}\n\nexport function insertEventIntoDescendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return b.created_at - event.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function insertEventIntoAscendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return event.created_at - b.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function binarySearch<T>(arr: T[], compare: (b: T) => number): [number, boolean] {\n  let start = 0\n  let end = arr.length - 1\n\n  while (start <= end) {\n    const mid = Math.floor((start + end) / 2)\n    const cmp = compare(arr[mid])\n\n    if (cmp === 0) {\n      return [mid, true]\n    }\n\n    if (cmp < 0) {\n      end = mid - 1\n    } else {\n      start = mid + 1\n    }\n  }\n\n  return [start, false]\n}\n\nexport class QueueNode<V> {\n  public value: V\n  public next: QueueNode<V> | null = null\n  public prev: QueueNode<V> | null = null\n\n  constructor(message: V) {\n    this.value = message\n  }\n}\n\nexport class Queue<V> {\n  public first: QueueNode<V> | null\n  public last: QueueNode<V> | null\n\n  constructor() {\n    this.first = null\n    this.last = null\n  }\n\n  enqueue(value: V): boolean {\n    const newNode = new QueueNode(value)\n    if (!this.last) {\n      // list is empty\n      this.first = newNode\n      this.last = newNode\n    } else if (this.last === this.first) {\n      // list has a single element\n      this.last = newNode\n      this.last.prev = this.first\n      this.first.next = newNode\n    } else {\n      // list has elements, add as last\n      newNode.prev = this.last\n      this.last.next = newNode\n      this.last = newNode\n    }\n    return true\n  }\n\n  dequeue(): V | null {\n    if (!this.first) return null\n\n    if (this.first === this.last) {\n      const target = this.first\n      this.first = null\n      this.last = null\n      return target.value\n    }\n\n    const target = this.first\n    this.first = target.next\n    if (this.first) {\n      this.first.prev = null // fix: clean up prev pointer\n    }\n\n    return target.value\n  }\n}\n", "import { NostrEvent, validateEvent } from './pure.ts'\n\n/** Events are **regular**, which means they're all expected to be stored by relays. */\nexport function isRegularKind(kind: number): boolean {\n  return kind < 10000 && kind !== 0 && kind !== 3\n}\n\n/** Events are **replaceable**, which means that, for each combination of `pubkey` and `kind`, only the latest event is expected to (SHOULD) be stored by relays, older versions are expected to be discarded. */\nexport function isReplaceableKind(kind: number): boolean {\n  return kind === 0 || kind === 3 || (10000 <= kind && kind < 20000)\n}\n\n/** Events are **ephemeral**, which means they are not expected to be stored by relays. */\nexport function isEphemeralKind(kind: number): boolean {\n  return 20000 <= kind && kind < 30000\n}\n\n/** Events are **addressable**, which means that, for each combination of `pubkey`, `kind` and the `d` tag, only the latest event is expected to be stored by relays, older versions are expected to be discarded. */\nexport function isAddressableKind(kind: number): boolean {\n  return 30000 <= kind && kind < 40000\n}\n\n/** Classification of the event kind. */\nexport type KindClassification = 'regular' | 'replaceable' | 'ephemeral' | 'parameterized' | 'unknown'\n\n/** Determine the classification of this kind of event if known, or `unknown`. */\nexport function classifyKind(kind: number): KindClassification {\n  if (isRegularKind(kind)) return 'regular'\n  if (isReplaceableKind(kind)) return 'replaceable'\n  if (isEphemeralKind(kind)) return 'ephemeral'\n  if (isAddressableKind(kind)) return 'parameterized'\n  return 'unknown'\n}\n\nexport function isKind<T extends number>(event: unknown, kind: T | Array<T>): event is NostrEvent & { kind: T } {\n  const kindAsArray: number[] = kind instanceof Array ? kind : [kind]\n  return (validateEvent(event) && kindAsArray.includes(event.kind)) || false\n}\n\nexport const Metadata = 0\nexport type Metadata = typeof Metadata\nexport const ShortTextNote = 1\nexport type ShortTextNote = typeof ShortTextNote\nexport const RecommendRelay = 2\nexport type RecommendRelay = typeof RecommendRelay\nexport const Contacts = 3\nexport type Contacts = typeof Contacts\nexport const EncryptedDirectMessage = 4\nexport type EncryptedDirectMessage = typeof EncryptedDirectMessage\nexport const EventDeletion = 5\nexport type EventDeletion = typeof EventDeletion\nexport const Repost = 6\nexport type Repost = typeof Repost\nexport const Reaction = 7\nexport type Reaction = typeof Reaction\nexport const BadgeAward = 8\nexport type BadgeAward = typeof BadgeAward\nexport const ChatMessage = 9\nexport type ChatMessage = typeof ChatMessage\nexport const ForumThread = 11\nexport type ForumThread = typeof ForumThread\nexport const Seal = 13\nexport type Seal = typeof Seal\nexport const PrivateDirectMessage = 14\nexport type PrivateDirectMessage = typeof PrivateDirectMessage\nexport const FileMessage = 15\nexport type FileMessage = typeof FileMessage\nexport const GenericRepost = 16\nexport type GenericRepost = typeof GenericRepost\nexport const Photo = 20\nexport type Photo = typeof Photo\nexport const NormalVideo = 21\nexport type NormalVideo = typeof NormalVideo\nexport const ShortVideo = 22\nexport type ShortVideo = typeof ShortVideo\nexport const ChannelCreation = 40\nexport type ChannelCreation = typeof ChannelCreation\nexport const ChannelMetadata = 41\nexport type ChannelMetadata = typeof ChannelMetadata\nexport const ChannelMessage = 42\nexport type ChannelMessage = typeof ChannelMessage\nexport const ChannelHideMessage = 43\nexport type ChannelHideMessage = typeof ChannelHideMessage\nexport const ChannelMuteUser = 44\nexport type ChannelMuteUser = typeof ChannelMuteUser\nexport const OpenTimestamps = 1040\nexport type OpenTimestamps = typeof OpenTimestamps\nexport const GiftWrap = 1059\nexport type GiftWrap = typeof GiftWrap\nexport const Poll = 1068\nexport type Poll = typeof Poll\nexport const FileMetadata = 1063\nexport type FileMetadata = typeof FileMetadata\nexport const Comment = 1111\nexport type Comment = typeof Comment\nexport const LiveChatMessage = 1311\nexport type LiveChatMessage = typeof LiveChatMessage\nexport const Voice = 1222\nexport type Voice = typeof Voice\nexport const VoiceComment = 1244\nexport type VoiceComment = typeof VoiceComment\nexport const ProblemTracker = 1971\nexport type ProblemTracker = typeof ProblemTracker\nexport const Report = 1984\nexport type Report = typeof Report\nexport const Reporting = 1984\nexport type Reporting = typeof Reporting\nexport const Label = 1985\nexport type Label = typeof Label\nexport const CommunityPostApproval = 4550\nexport type CommunityPostApproval = typeof CommunityPostApproval\nexport const JobRequest = 5999\nexport type JobRequest = typeof JobRequest\nexport const JobResult = 6999\nexport type JobResult = typeof JobResult\nexport const JobFeedback = 7000\nexport type JobFeedback = typeof JobFeedback\nexport const ZapGoal = 9041\nexport type ZapGoal = typeof ZapGoal\nexport const ZapRequest = 9734\nexport type ZapRequest = typeof ZapRequest\nexport const Zap = 9735\nexport type Zap = typeof Zap\nexport const Highlights = 9802\nexport type Highlights = typeof Highlights\nexport const PollResponse = 1018\nexport type PollResponse = typeof PollResponse\nexport const Mutelist = 10000\nexport type Mutelist = typeof Mutelist\nexport const Pinlist = 10001\nexport type Pinlist = typeof Pinlist\nexport const RelayList = 10002\nexport type RelayList = typeof RelayList\nexport const BookmarkList = 10003\nexport type BookmarkList = typeof BookmarkList\nexport const CommunitiesList = 10004\nexport type CommunitiesList = typeof CommunitiesList\nexport const PublicChatsList = 10005\nexport type PublicChatsList = typeof PublicChatsList\nexport const BlockedRelaysList = 10006\nexport type BlockedRelaysList = typeof BlockedRelaysList\nexport const SearchRelaysList = 10007\nexport type SearchRelaysList = typeof SearchRelaysList\nexport const FavoriteRelays = 10012\nexport type FavoriteRelays = typeof FavoriteRelays\nexport const InterestsList = 10015\nexport type InterestsList = typeof InterestsList\nexport const UserEmojiList = 10030\nexport type UserEmojiList = typeof UserEmojiList\nexport const DirectMessageRelaysList = 10050\nexport type DirectMessageRelaysList = typeof DirectMessageRelaysList\nexport const FileServerPreference = 10096\nexport type FileServerPreference = typeof FileServerPreference\nexport const BlossomServerList = 10063\nexport type BlossomServerList = typeof BlossomServerList\nexport const NWCWalletInfo = 13194\nexport type NWCWalletInfo = typeof NWCWalletInfo\nexport const LightningPubRPC = 21000\nexport type LightningPubRPC = typeof LightningPubRPC\nexport const ClientAuth = 22242\nexport type ClientAuth = typeof ClientAuth\nexport const NWCWalletRequest = 23194\nexport type NWCWalletRequest = typeof NWCWalletRequest\nexport const NWCWalletResponse = 23195\nexport type NWCWalletResponse = typeof NWCWalletResponse\nexport const NostrConnect = 24133\nexport type NostrConnect = typeof NostrConnect\nexport const HTTPAuth = 27235\nexport type HTTPAuth = typeof HTTPAuth\nexport const Followsets = 30000\nexport type Followsets = typeof Followsets\nexport const Genericlists = 30001\nexport type Genericlists = typeof Genericlists\nexport const Relaysets = 30002\nexport type Relaysets = typeof Relaysets\nexport const Bookmarksets = 30003\nexport type Bookmarksets = typeof Bookmarksets\nexport const Curationsets = 30004\nexport type Curationsets = typeof Curationsets\nexport const ProfileBadges = 30008\nexport type ProfileBadges = typeof ProfileBadges\nexport const BadgeDefinition = 30009\nexport type BadgeDefinition = typeof BadgeDefinition\nexport const Interestsets = 30015\nexport type Interestsets = typeof Interestsets\nexport const CreateOrUpdateStall = 30017\nexport type CreateOrUpdateStall = typeof CreateOrUpdateStall\nexport const CreateOrUpdateProduct = 30018\nexport type CreateOrUpdateProduct = typeof CreateOrUpdateProduct\nexport const LongFormArticle = 30023\nexport type LongFormArticle = typeof LongFormArticle\nexport const DraftLong = 30024\nexport type DraftLong = typeof DraftLong\nexport const Emojisets = 30030\nexport type Emojisets = typeof Emojisets\nexport const Application = 30078\nexport type Application = typeof Application\nexport const LiveEvent = 30311\nexport type LiveEvent = typeof LiveEvent\nexport const UserStatuses = 30315\nexport type UserStatuses = typeof UserStatuses\nexport const ClassifiedListing = 30402\nexport type ClassifiedListing = typeof ClassifiedListing\nexport const DraftClassifiedListing = 30403\nexport type DraftClassifiedListing = typeof DraftClassifiedListing\nexport const Date = 31922\nexport type Date = typeof Date\nexport const Time = 31923\nexport type Time = typeof Time\nexport const Calendar = 31924\nexport type Calendar = typeof Calendar\nexport const CalendarEventRSVP = 31925\nexport type CalendarEventRSVP = typeof CalendarEventRSVP\nexport const RelayReview = 31987\nexport type RelayReview = typeof RelayReview\nexport const Handlerrecommendation = 31989\nexport type Handlerrecommendation = typeof Handlerrecommendation\nexport const Handlerinformation = 31990\nexport type Handlerinformation = typeof Handlerinformation\nexport const CommunityDefinition = 34550\nexport type CommunityDefinition = typeof CommunityDefinition\nexport const GroupMetadata = 39000\nexport type GroupMetadata = typeof GroupMetadata\n", "import { Event } from './core.ts'\nimport { isAddressableKind, isReplaceableKind } from './kinds.ts'\n\nexport type Filter = {\n  ids?: string[]\n  kinds?: number[]\n  authors?: string[]\n  since?: number\n  until?: number\n  limit?: number\n  search?: string\n  [key: `#${string}`]: string[] | undefined\n}\n\nexport function matchFilter(filter: Filter, event: Event): boolean {\n  if (filter.ids && filter.ids.indexOf(event.id) === -1) {\n    return false\n  }\n  if (filter.kinds && filter.kinds.indexOf(event.kind) === -1) {\n    return false\n  }\n  if (filter.authors && filter.authors.indexOf(event.pubkey) === -1) {\n    return false\n  }\n\n  for (let f in filter) {\n    if (f[0] === '#') {\n      let tagName = f.slice(1)\n      let values = filter[`#${tagName}`]\n      if (values && !event.tags.find(([t, v]) => t === f.slice(1) && values!.indexOf(v) !== -1)) return false\n    }\n  }\n\n  if (filter.since && event.created_at < filter.since) return false\n  if (filter.until && event.created_at > filter.until) return false\n\n  return true\n}\n\nexport function matchFilters(filters: Filter[], event: Event): boolean {\n  for (let i = 0; i < filters.length; i++) {\n    if (matchFilter(filters[i], event)) {\n      return true\n    }\n  }\n  return false\n}\n\nexport function mergeFilters(...filters: Filter[]): Filter {\n  let result: Filter = {}\n  for (let i = 0; i < filters.length; i++) {\n    let filter = filters[i]\n    Object.entries(filter).forEach(([property, values]) => {\n      if (property === 'kinds' || property === 'ids' || property === 'authors' || property[0] === '#') {\n        // @ts-ignore\n        result[property] = result[property] || []\n        // @ts-ignore\n        for (let v = 0; v < values.length; v++) {\n          // @ts-ignore\n          let value = values[v]\n          // @ts-ignore\n          if (!result[property].includes(value)) result[property].push(value)\n        }\n      }\n    })\n\n    if (filter.limit && (!result.limit || filter.limit > result.limit)) result.limit = filter.limit\n    if (filter.until && (!result.until || filter.until > result.until)) result.until = filter.until\n    if (filter.since && (!result.since || filter.since < result.since)) result.since = filter.since\n  }\n\n  return result\n}\n\n/**\n * Calculate the intrinsic limit of a filter.\n * This function returns a positive integer, or `Infinity` if there is no intrinsic limit.\n */\nexport function getFilterLimit(filter: Filter): number {\n  if (filter.ids && !filter.ids.length) return 0\n  if (filter.kinds && !filter.kinds.length) return 0\n  if (filter.authors && !filter.authors.length) return 0\n\n  for (const [key, value] of Object.entries(filter)) {\n    if (key[0] === '#' && Array.isArray(value) && !value.length) return 0\n  }\n\n  return Math.min(\n    // The `limit` property creates an artificial limit.\n    Math.max(0, filter.limit ?? Infinity),\n\n    // There can only be one event per `id`.\n    filter.ids?.length ?? Infinity,\n\n    // Replaceable events are limited by the number of authors and kinds.\n    filter.authors?.length && filter.kinds?.every(kind => isReplaceableKind(kind))\n      ? filter.authors.length * filter.kinds.length\n      : Infinity,\n\n    // Parameterized replaceable events are limited by the number of authors, kinds, and \"d\" tags.\n    filter.authors?.length && filter.kinds?.every(kind => isAddressableKind(kind)) && filter['#d']?.length\n      ? filter.authors.length * filter.kinds.length * filter['#d'].length\n      : Infinity,\n  )\n}\n", "export function getHex64(json: string, field: string): string {\n  let len = field.length + 3\n  let idx = json.indexOf(`\"${field}\":`) + len\n  let s = json.slice(idx).indexOf(`\"`) + idx + 1\n  return json.slice(s, s + 64)\n}\n\nexport function getInt(json: string, field: string): number {\n  let len = field.length\n  let idx = json.indexOf(`\"${field}\":`) + len + 3\n  let sliced = json.slice(idx)\n  let end = Math.min(sliced.indexOf(','), sliced.indexOf('}'))\n  return parseInt(sliced.slice(0, end), 10)\n}\n\nexport function getSubscriptionId(json: string): string | null {\n  let idx = json.slice(0, 22).indexOf(`\"EVENT\"`)\n  if (idx === -1) return null\n\n  let pstart = json.slice(idx + 7 + 1).indexOf(`\"`)\n  if (pstart === -1) return null\n  let start = idx + 7 + 1 + pstart\n\n  let pend = json.slice(start + 1, 80).indexOf(`\"`)\n  if (pend === -1) return null\n  let end = start + 1 + pend\n\n  return json.slice(start + 1, end)\n}\n\nexport function matchEventId(json: string, id: string): boolean {\n  return id === getHex64(json, 'id')\n}\n\nexport function matchEventPubkey(json: string, pubkey: string): boolean {\n  return pubkey === getHex64(json, 'pubkey')\n}\n\nexport function matchEventKind(json: string, kind: number): boolean {\n  return kind === getInt(json, 'kind')\n}\n", "import { EventTemplate } from './core.ts'\nimport { ClientAuth } from './kinds.ts'\n\n/**\n * creates an EventTemplate for an AUTH event to be signed.\n */\nexport function makeAuthEvent(relayURL: string, challenge: string): EventTemplate {\n  return {\n    kind: ClientAuth,\n    created_at: Math.floor(Date.now() / 1000),\n    tags: [\n      ['relay', relayURL],\n      ['challenge', challenge],\n    ],\n    content: '',\n  }\n}\n", "import { verifiedSymbol, type Event, type Nostr, VerifiedEvent } from './core.ts'\n\nexport async function yieldThread() {\n  return new Promise<void>((resolve, reject) => {\n    try {\n      // Check if MessageChannel is available\n      if (typeof MessageChannel !== 'undefined') {\n        const ch = new MessageChannel()\n        const handler = () => {\n          // @ts-ignore (typescript thinks this property should be called `removeListener`, but in fact it's `removeEventListener`)\n          ch.port1.removeEventListener('message', handler)\n          resolve()\n        }\n        // @ts-ignore (typescript thinks this property should be called `addListener`, but in fact it's `addEventListener`)\n        ch.port1.addEventListener('message', handler)\n        ch.port2.postMessage(0)\n        ch.port1.start()\n      } else {\n        if (typeof setImmediate !== 'undefined') {\n          setImmediate(resolve)\n        } else if (typeof setTimeout !== 'undefined') {\n          setTimeout(resolve, 0)\n        } else {\n          // Last resort - resolve immediately\n          resolve()\n        }\n      }\n    } catch (e) {\n      console.error('during yield: ', e)\n      reject(e)\n    }\n  })\n}\n\nexport const alwaysTrue: Nostr['verifyEvent'] = (t: Event): t is VerifiedEvent => {\n  t[verifiedSymbol] = true\n  return true\n}\n"],
+  "sourcesContent": ["/* global WebSocket */\n\nimport type { Event, EventTemplate, VerifiedEvent, Nostr, NostrEvent } from './core.ts'\nimport { matchFilters, type Filter } from './filter.ts'\nimport { getHex64, getSubscriptionId } from './fakejson.ts'\nimport { Queue, normalizeURL } from './utils.ts'\nimport { makeAuthEvent } from './nip42.ts'\nimport { yieldThread } from './helpers.ts'\n\ntype RelayWebSocket = WebSocket & {\n  ping?(): void\n  on?(event: 'pong', listener: () => void): any\n}\n\nexport type AbstractRelayConstructorOptions = {\n  verifyEvent: Nostr['verifyEvent']\n  websocketImplementation?: typeof WebSocket\n  enablePing?: boolean\n  enableReconnect?: boolean\n}\n\nexport class SendingOnClosedConnection extends Error {\n  constructor(message: string, relay: string) {\n    super(`Tried to send message '${message} on a closed connection to ${relay}.`)\n    this.name = 'SendingOnClosedConnection'\n  }\n}\n\nexport class AbstractRelay {\n  public readonly url: string\n  private _connected: boolean = false\n\n  public onclose: (() => void) | null = null\n  public onnotice: (msg: string) => void = msg => console.debug(`NOTICE from ${this.url}: ${msg}`)\n  public onauth: undefined | ((evt: EventTemplate) => Promise<VerifiedEvent>)\n\n  public baseEoseTimeout: number = 4400\n  public publishTimeout: number = 4400\n  public pingFrequency: number = 29000\n  public pingTimeout: number = 20000\n  public resubscribeBackoff: number[] = [10000, 10000, 10000, 20000, 20000, 30000, 60000]\n  public openSubs: Map<string, Subscription> = new Map()\n  public enablePing: boolean | undefined\n  public enableReconnect: boolean\n  private reconnectTimeoutHandle: ReturnType<typeof setTimeout> | undefined\n  private pingIntervalHandle: ReturnType<typeof setInterval> | undefined\n  private reconnectAttempts: number = 0\n  private closedIntentionally: boolean = false\n\n  private connectionPromise: Promise<void> | undefined\n  private openCountRequests = new Map<string, CountResolver>()\n  private openEventPublishes = new Map<string, EventPublishResolver>()\n  private ws: RelayWebSocket | undefined\n  private incomingMessageQueue = new Queue<string>()\n  private queueRunning = false\n  private challenge: string | undefined\n  private authPromise: Promise<string> | undefined\n  private serial: number = 0\n  private verifyEvent: Nostr['verifyEvent']\n\n  private _WebSocket: typeof WebSocket\n\n  constructor(url: string, opts: AbstractRelayConstructorOptions) {\n    this.url = normalizeURL(url)\n    this.verifyEvent = opts.verifyEvent\n    this._WebSocket = opts.websocketImplementation || WebSocket\n    this.enablePing = opts.enablePing\n    this.enableReconnect = opts.enableReconnect || false\n  }\n\n  static async connect(\n    url: string,\n    opts: AbstractRelayConstructorOptions & Parameters<AbstractRelay['connect']>[0],\n  ): Promise<AbstractRelay> {\n    const relay = new AbstractRelay(url, opts)\n    await relay.connect(opts)\n    return relay\n  }\n\n  private closeAllSubscriptions(reason: string) {\n    for (let [_, sub] of this.openSubs) {\n      sub.close(reason)\n    }\n    this.openSubs.clear()\n\n    for (let [_, ep] of this.openEventPublishes) {\n      ep.reject(new Error(reason))\n    }\n    this.openEventPublishes.clear()\n\n    for (let [_, cr] of this.openCountRequests) {\n      cr.reject(new Error(reason))\n    }\n    this.openCountRequests.clear()\n  }\n\n  public get connected(): boolean {\n    return this._connected\n  }\n\n  private async reconnect(): Promise<void> {\n    const backoff = this.resubscribeBackoff[Math.min(this.reconnectAttempts, this.resubscribeBackoff.length - 1)]\n    this.reconnectAttempts++\n\n    this.reconnectTimeoutHandle = setTimeout(async () => {\n      try {\n        await this.connect()\n      } catch (err) {\n        // this will be called again through onclose/onerror\n      }\n    }, backoff)\n  }\n\n  private handleHardClose(reason: string) {\n    if (this.pingIntervalHandle) {\n      clearInterval(this.pingIntervalHandle)\n      this.pingIntervalHandle = undefined\n    }\n\n    this._connected = false\n    this.connectionPromise = undefined\n\n    const wasIntentional = this.closedIntentionally\n    this.closedIntentionally = false // reset for next time\n\n    this.onclose?.()\n\n    if (this.enableReconnect && !wasIntentional) {\n      this.reconnect()\n    } else {\n      this.closeAllSubscriptions(reason)\n    }\n  }\n\n  public async connect(opts?: { timeout?: number; abort?: AbortSignal }): Promise<void> {\n    let connectionTimeoutHandle: ReturnType<typeof setTimeout> | undefined\n\n    if (this.connectionPromise) return this.connectionPromise\n\n    this.challenge = undefined\n    this.authPromise = undefined\n    this.connectionPromise = new Promise((resolve, reject) => {\n      if (opts?.timeout) {\n        connectionTimeoutHandle = setTimeout(() => {\n          reject('connection timed out')\n          this.connectionPromise = undefined\n          this.onclose?.()\n          this.closeAllSubscriptions('relay connection timed out')\n        }, opts.timeout)\n      }\n\n      if (opts?.abort) {\n        opts.abort.onabort = reject\n      }\n\n      try {\n        this.ws = new this._WebSocket(this.url)\n      } catch (err) {\n        clearTimeout(connectionTimeoutHandle)\n        reject(err)\n        return\n      }\n\n      this.ws.onopen = () => {\n        if (this.reconnectTimeoutHandle) {\n          clearTimeout(this.reconnectTimeoutHandle)\n          this.reconnectTimeoutHandle = undefined\n        }\n        clearTimeout(connectionTimeoutHandle)\n        this._connected = true\n\n        const isReconnection = this.reconnectAttempts > 0\n        this.reconnectAttempts = 0\n\n        // resubscribe to all open subscriptions\n        for (const sub of this.openSubs.values()) {\n          sub.eosed = false\n          if (isReconnection) {\n            for (let f = 0; f < sub.filters.length; f++) {\n              if (sub.lastEmitted) {\n                sub.filters[f].since = sub.lastEmitted + 1\n              }\n            }\n          }\n          sub.fire()\n        }\n\n        if (this.enablePing) {\n          this.pingIntervalHandle = setInterval(() => this.pingpong(), this.pingFrequency)\n        }\n        resolve()\n      }\n\n      this.ws.onerror = ev => {\n        clearTimeout(connectionTimeoutHandle)\n        reject((ev as any).message || 'websocket error')\n        this.handleHardClose('relay connection errored')\n      }\n\n      this.ws.onclose = ev => {\n        clearTimeout(connectionTimeoutHandle)\n        reject((ev as any).message || 'websocket closed')\n        this.handleHardClose('relay connection closed')\n      }\n\n      this.ws.onmessage = this._onmessage.bind(this)\n    })\n\n    return this.connectionPromise\n  }\n\n  private waitForPingPong() {\n    return new Promise(resolve => {\n      // listen for pong\n      ;(this.ws as any).once('pong', () => resolve(true))\n      // send a ping\n      this.ws!.ping!()\n    })\n  }\n\n  private waitForDummyReq() {\n    return new Promise((resolve, reject) => {\n      if (!this.connectionPromise) return reject(new Error(`no connection to ${this.url}, can't ping`))\n\n      // make a dummy request with expected empty eose reply\n      // [\"REQ\", \"_\", {\"ids\":[\"aaaa...aaaa\"], \"limit\": 0}]\n      try {\n        const sub = this.subscribe(\n          [{ ids: ['aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'], limit: 0 }],\n          {\n            label: 'forced-ping',\n            oneose: () => {\n              resolve(true)\n              sub.close()\n            },\n            onclose() {\n              // if we get a CLOSED it's because the relay is alive\n              resolve(true)\n            },\n            eoseTimeout: this.pingTimeout + 1000,\n          },\n        )\n      } catch (err) {\n        reject(err)\n      }\n    })\n  }\n\n  // nodejs requires this magic here to ensure connections are closed when internet goes off and stuff\n  // in browsers it's done automatically. see https://github.com/nbd-wtf/nostr-tools/issues/491\n  private async pingpong() {\n    // if the websocket is connected\n    if (this.ws?.readyState === 1) {\n      // wait for either a ping-pong reply or a timeout\n      const result = await Promise.any([\n        // browsers don't have ping so use a dummy req\n        this.ws && this.ws.ping && (this.ws as any).once ? this.waitForPingPong() : this.waitForDummyReq(),\n        new Promise(res => setTimeout(() => res(false), this.pingTimeout)),\n      ])\n\n      if (!result) {\n        // pingpong closing socket\n        if (this.ws?.readyState === this._WebSocket.OPEN) {\n          this.ws?.close()\n        }\n      }\n    }\n  }\n\n  private async runQueue() {\n    this.queueRunning = true\n    while (true) {\n      if (false === this.handleNext()) {\n        break\n      }\n      await yieldThread()\n    }\n    this.queueRunning = false\n  }\n\n  private handleNext(): undefined | false {\n    const json = this.incomingMessageQueue.dequeue()\n    if (!json) {\n      return false\n    }\n\n    // shortcut EVENT sub\n    const subid = getSubscriptionId(json)\n    if (subid) {\n      const so = this.openSubs.get(subid as string)\n      if (!so) {\n        // this is an EVENT message, but for a subscription we don't have, so just stop here\n        return\n      }\n\n      // this will be called only when this message is a EVENT message for a subscription we have\n      // we do this before parsing the JSON to not have to do that for duplicate events\n      //   since JSON parsing is slow\n      const id = getHex64(json, 'id')\n      const alreadyHave = so.alreadyHaveEvent?.(id)\n\n      // notify any interested client that the relay has this event\n      // (do this after alreadyHaveEvent() because the client may rely on this to answer that)\n      so.receivedEvent?.(this, id)\n\n      if (alreadyHave) {\n        // if we had already seen this event we can just stop here\n        return\n      }\n    }\n\n    try {\n      let data = JSON.parse(json)\n      // we won't do any checks against the data since all failures (i.e. invalid messages from relays)\n      // will naturally be caught by the encompassing try..catch block\n\n      switch (data[0]) {\n        case 'EVENT': {\n          const so = this.openSubs.get(data[1] as string) as Subscription\n          const event = data[2] as NostrEvent\n          if (this.verifyEvent(event) && matchFilters(so.filters, event)) {\n            so.onevent(event)\n          }\n          if (!so.lastEmitted || so.lastEmitted < event.created_at) so.lastEmitted = event.created_at\n          return\n        }\n        case 'COUNT': {\n          const id: string = data[1]\n          const payload = data[2] as { count: number }\n          const cr = this.openCountRequests.get(id) as CountResolver\n          if (cr) {\n            cr.resolve(payload.count)\n            this.openCountRequests.delete(id)\n          }\n          return\n        }\n        case 'EOSE': {\n          const so = this.openSubs.get(data[1] as string)\n          if (!so) return\n          so.receivedEose()\n          return\n        }\n        case 'OK': {\n          const id: string = data[1]\n          const ok: boolean = data[2]\n          const reason: string = data[3]\n          const ep = this.openEventPublishes.get(id) as EventPublishResolver\n          if (ep) {\n            clearTimeout(ep.timeout)\n            if (ok) ep.resolve(reason)\n            else ep.reject(new Error(reason))\n            this.openEventPublishes.delete(id)\n          }\n          return\n        }\n        case 'CLOSED': {\n          const id: string = data[1]\n          const so = this.openSubs.get(id)\n          if (!so) return\n          so.closed = true\n          so.close(data[2] as string)\n          return\n        }\n        case 'NOTICE': {\n          this.onnotice(data[1] as string)\n          return\n        }\n        case 'AUTH': {\n          this.challenge = data[1] as string\n          if (this.onauth) {\n            this.auth(this.onauth)\n          }\n          return\n        }\n        default: {\n          const so = this.openSubs.get(data[1])\n          so?.oncustom?.(data)\n          return\n        }\n      }\n    } catch (err) {\n      return\n    }\n  }\n\n  public async send(message: string) {\n    if (!this.connectionPromise) throw new SendingOnClosedConnection(message, this.url)\n\n    this.connectionPromise.then(() => {\n      this.ws?.send(message)\n    })\n  }\n\n  public async auth(signAuthEvent: (evt: EventTemplate) => Promise<VerifiedEvent>): Promise<string> {\n    const challenge = this.challenge\n    if (!challenge) throw new Error(\"can't perform auth, no challenge was received\")\n    if (this.authPromise) return this.authPromise\n\n    this.authPromise = new Promise<string>(async (resolve, reject) => {\n      try {\n        let evt = await signAuthEvent(makeAuthEvent(this.url, challenge))\n        let timeout = setTimeout(() => {\n          let ep = this.openEventPublishes.get(evt.id) as EventPublishResolver\n          if (ep) {\n            ep.reject(new Error('auth timed out'))\n            this.openEventPublishes.delete(evt.id)\n          }\n        }, this.publishTimeout)\n        this.openEventPublishes.set(evt.id, { resolve, reject, timeout })\n        this.send('[\"AUTH\",' + JSON.stringify(evt) + ']')\n      } catch (err) {\n        console.warn('subscribe auth function failed:', err)\n      }\n    })\n    return this.authPromise\n  }\n\n  public async publish(event: Event): Promise<string> {\n    const ret = new Promise<string>((resolve, reject) => {\n      const timeout = setTimeout(() => {\n        const ep = this.openEventPublishes.get(event.id) as EventPublishResolver\n        if (ep) {\n          ep.reject(new Error('publish timed out'))\n          this.openEventPublishes.delete(event.id)\n        }\n      }, this.publishTimeout)\n      this.openEventPublishes.set(event.id, { resolve, reject, timeout })\n    })\n    this.send('[\"EVENT\",' + JSON.stringify(event) + ']')\n    return ret\n  }\n\n  public async count(filters: Filter[], params: { id?: string | null }): Promise<number> {\n    this.serial++\n    const id = params?.id || 'count:' + this.serial\n    const ret = new Promise<number>((resolve, reject) => {\n      this.openCountRequests.set(id, { resolve, reject })\n    })\n    this.send('[\"COUNT\",\"' + id + '\",' + JSON.stringify(filters).substring(1))\n    return ret\n  }\n\n  public subscribe(\n    filters: Filter[],\n    params: Partial<SubscriptionParams> & { label?: string; id?: string },\n  ): Subscription {\n    const sub = this.prepareSubscription(filters, params)\n    sub.fire()\n\n    if (params.abort) {\n      params.abort.onabort = () => sub.close(String(params.abort!.reason || '<aborted>'))\n    }\n\n    return sub\n  }\n\n  public prepareSubscription(\n    filters: Filter[],\n    params: Partial<SubscriptionParams> & { label?: string; id?: string },\n  ): Subscription {\n    this.serial++\n    const id = params.id || (params.label ? params.label + ':' : 'sub:') + this.serial\n    const subscription = new Subscription(this, id, filters, params)\n    this.openSubs.set(id, subscription)\n    return subscription\n  }\n\n  public close() {\n    this.closedIntentionally = true\n    if (this.reconnectTimeoutHandle) {\n      clearTimeout(this.reconnectTimeoutHandle)\n      this.reconnectTimeoutHandle = undefined\n    }\n    if (this.pingIntervalHandle) {\n      clearInterval(this.pingIntervalHandle)\n      this.pingIntervalHandle = undefined\n    }\n    this.closeAllSubscriptions('relay connection closed by us')\n    this._connected = false\n    this.onclose?.()\n    if (this.ws?.readyState === this._WebSocket.OPEN) {\n      this.ws?.close()\n    }\n  }\n\n  // this is the function assigned to this.ws.onmessage\n  // it's exposed for testing and debugging purposes\n  public _onmessage(ev: MessageEvent<any>) {\n    this.incomingMessageQueue.enqueue(ev.data as string)\n    if (!this.queueRunning) {\n      this.runQueue()\n    }\n  }\n}\n\nexport class Subscription {\n  public readonly relay: AbstractRelay\n  public readonly id: string\n\n  public lastEmitted: number | undefined\n  public closed: boolean = false\n  public eosed: boolean = false\n  public filters: Filter[]\n  public alreadyHaveEvent: ((id: string) => boolean) | undefined\n  public receivedEvent: ((relay: AbstractRelay, id: string) => void) | undefined\n\n  public onevent: (evt: Event) => void\n  public oneose: (() => void) | undefined\n  public onclose: ((reason: string) => void) | undefined\n\n  // will get any messages that have this subscription id as their second item and are not default standard\n  public oncustom: ((msg: string[]) => void) | undefined\n\n  public eoseTimeout: number\n  private eoseTimeoutHandle: ReturnType<typeof setTimeout> | undefined\n\n  constructor(relay: AbstractRelay, id: string, filters: Filter[], params: SubscriptionParams) {\n    if (filters.length === 0) throw new Error(\"subscription can't be created with zero filters\")\n\n    this.relay = relay\n    this.filters = filters\n    this.id = id\n    this.alreadyHaveEvent = params.alreadyHaveEvent\n    this.receivedEvent = params.receivedEvent\n    this.eoseTimeout = params.eoseTimeout || relay.baseEoseTimeout\n\n    this.oneose = params.oneose\n    this.onclose = params.onclose\n    this.onevent =\n      params.onevent ||\n      (event => {\n        console.warn(\n          `onevent() callback not defined for subscription '${this.id}' in relay ${this.relay.url}. event received:`,\n          event,\n        )\n      })\n  }\n\n  public fire() {\n    this.relay.send('[\"REQ\",\"' + this.id + '\",' + JSON.stringify(this.filters).substring(1))\n\n    // only now we start counting the eoseTimeout\n    this.eoseTimeoutHandle = setTimeout(this.receivedEose.bind(this), this.eoseTimeout)\n  }\n\n  public receivedEose() {\n    if (this.eosed) return\n    clearTimeout(this.eoseTimeoutHandle)\n    this.eosed = true\n    this.oneose?.()\n  }\n\n  public close(reason: string = 'closed by caller') {\n    if (!this.closed && this.relay.connected) {\n      // if the connection was closed by the user calling .close() we will send a CLOSE message\n      // otherwise this._open will be already set to false so we will skip this\n      try {\n        this.relay.send('[\"CLOSE\",' + JSON.stringify(this.id) + ']')\n      } catch (err) {\n        if (err instanceof SendingOnClosedConnection) {\n          /* doesn't matter, it's ok */\n        } else {\n          throw err\n        }\n      }\n      this.closed = true\n    }\n    this.relay.openSubs.delete(this.id)\n    this.onclose?.(reason)\n  }\n}\n\nexport type SubscriptionParams = {\n  onevent?: (evt: Event) => void\n  oneose?: () => void\n  onclose?: (reason: string) => void\n  alreadyHaveEvent?: (id: string) => boolean\n  receivedEvent?: (relay: AbstractRelay, id: string) => void\n  eoseTimeout?: number\n  abort?: AbortSignal\n}\n\nexport type CountResolver = {\n  resolve: (count: number) => void\n  reject: (err: Error) => void\n}\n\nexport type EventPublishResolver = {\n  resolve: (reason: string) => void\n  reject: (err: Error) => void\n  timeout: ReturnType<typeof setTimeout>\n}\n", "import type { Event } from './core.ts'\n\nexport const utf8Decoder: TextDecoder = new TextDecoder('utf-8')\nexport const utf8Encoder: TextEncoder = new TextEncoder()\n\nexport { bytesToHex, hexToBytes } from '@noble/hashes/utils.js'\n\nexport function normalizeURL(url: string): string {\n  try {\n    if (url.indexOf('://') === -1) url = 'wss://' + url\n    let p = new URL(url)\n    if (p.protocol === 'http:') p.protocol = 'ws:'\n    else if (p.protocol === 'https:') p.protocol = 'wss:'\n    p.pathname = p.pathname.replace(/\\/+/g, '/')\n    if (p.pathname.endsWith('/')) p.pathname = p.pathname.slice(0, -1)\n    if ((p.port === '80' && p.protocol === 'ws:') || (p.port === '443' && p.protocol === 'wss:')) p.port = ''\n    p.searchParams.sort()\n    p.hash = ''\n    return p.toString()\n  } catch (e) {\n    throw new Error(`Invalid URL: ${url}`)\n  }\n}\n\nexport function insertEventIntoDescendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return b.created_at - event.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function insertEventIntoAscendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return event.created_at - b.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function binarySearch<T>(arr: T[], compare: (b: T) => number): [number, boolean] {\n  let start = 0\n  let end = arr.length - 1\n\n  while (start <= end) {\n    const mid = Math.floor((start + end) / 2)\n    const cmp = compare(arr[mid])\n\n    if (cmp === 0) {\n      return [mid, true]\n    }\n\n    if (cmp < 0) {\n      end = mid - 1\n    } else {\n      start = mid + 1\n    }\n  }\n\n  return [start, false]\n}\n\nexport class QueueNode<V> {\n  public value: V\n  public next: QueueNode<V> | null = null\n  public prev: QueueNode<V> | null = null\n\n  constructor(message: V) {\n    this.value = message\n  }\n}\n\nexport class Queue<V> {\n  public first: QueueNode<V> | null\n  public last: QueueNode<V> | null\n\n  constructor() {\n    this.first = null\n    this.last = null\n  }\n\n  enqueue(value: V): boolean {\n    const newNode = new QueueNode(value)\n    if (!this.last) {\n      // list is empty\n      this.first = newNode\n      this.last = newNode\n    } else if (this.last === this.first) {\n      // list has a single element\n      this.last = newNode\n      this.last.prev = this.first\n      this.first.next = newNode\n    } else {\n      // list has elements, add as last\n      newNode.prev = this.last\n      this.last.next = newNode\n      this.last = newNode\n    }\n    return true\n  }\n\n  dequeue(): V | null {\n    if (!this.first) return null\n\n    if (this.first === this.last) {\n      const target = this.first\n      this.first = null\n      this.last = null\n      return target.value\n    }\n\n    const target = this.first\n    this.first = target.next\n    if (this.first) {\n      this.first.prev = null // fix: clean up prev pointer\n    }\n\n    return target.value\n  }\n}\n", "import { NostrEvent, validateEvent } from './pure.ts'\n\n/** Events are **regular**, which means they're all expected to be stored by relays. */\nexport function isRegularKind(kind: number): boolean {\n  return kind < 10000 && kind !== 0 && kind !== 3\n}\n\n/** Events are **replaceable**, which means that, for each combination of `pubkey` and `kind`, only the latest event is expected to (SHOULD) be stored by relays, older versions are expected to be discarded. */\nexport function isReplaceableKind(kind: number): boolean {\n  return kind === 0 || kind === 3 || (10000 <= kind && kind < 20000)\n}\n\n/** Events are **ephemeral**, which means they are not expected to be stored by relays. */\nexport function isEphemeralKind(kind: number): boolean {\n  return 20000 <= kind && kind < 30000\n}\n\n/** Events are **addressable**, which means that, for each combination of `pubkey`, `kind` and the `d` tag, only the latest event is expected to be stored by relays, older versions are expected to be discarded. */\nexport function isAddressableKind(kind: number): boolean {\n  return 30000 <= kind && kind < 40000\n}\n\n/** Classification of the event kind. */\nexport type KindClassification = 'regular' | 'replaceable' | 'ephemeral' | 'parameterized' | 'unknown'\n\n/** Determine the classification of this kind of event if known, or `unknown`. */\nexport function classifyKind(kind: number): KindClassification {\n  if (isRegularKind(kind)) return 'regular'\n  if (isReplaceableKind(kind)) return 'replaceable'\n  if (isEphemeralKind(kind)) return 'ephemeral'\n  if (isAddressableKind(kind)) return 'parameterized'\n  return 'unknown'\n}\n\nexport function isKind<T extends number>(event: unknown, kind: T | Array<T>): event is NostrEvent & { kind: T } {\n  const kindAsArray: number[] = kind instanceof Array ? kind : [kind]\n  return (validateEvent(event) && kindAsArray.includes(event.kind)) || false\n}\n\nexport const Metadata = 0\nexport type Metadata = typeof Metadata\nexport const ShortTextNote = 1\nexport type ShortTextNote = typeof ShortTextNote\nexport const RecommendRelay = 2\nexport type RecommendRelay = typeof RecommendRelay\nexport const Contacts = 3\nexport type Contacts = typeof Contacts\nexport const EncryptedDirectMessage = 4\nexport type EncryptedDirectMessage = typeof EncryptedDirectMessage\nexport const EventDeletion = 5\nexport type EventDeletion = typeof EventDeletion\nexport const Repost = 6\nexport type Repost = typeof Repost\nexport const Reaction = 7\nexport type Reaction = typeof Reaction\nexport const BadgeAward = 8\nexport type BadgeAward = typeof BadgeAward\nexport const ChatMessage = 9\nexport type ChatMessage = typeof ChatMessage\nexport const ForumThread = 11\nexport type ForumThread = typeof ForumThread\nexport const Seal = 13\nexport type Seal = typeof Seal\nexport const PrivateDirectMessage = 14\nexport type PrivateDirectMessage = typeof PrivateDirectMessage\nexport const FileMessage = 15\nexport type FileMessage = typeof FileMessage\nexport const GenericRepost = 16\nexport type GenericRepost = typeof GenericRepost\nexport const Photo = 20\nexport type Photo = typeof Photo\nexport const NormalVideo = 21\nexport type NormalVideo = typeof NormalVideo\nexport const ShortVideo = 22\nexport type ShortVideo = typeof ShortVideo\nexport const ChannelCreation = 40\nexport type ChannelCreation = typeof ChannelCreation\nexport const ChannelMetadata = 41\nexport type ChannelMetadata = typeof ChannelMetadata\nexport const ChannelMessage = 42\nexport type ChannelMessage = typeof ChannelMessage\nexport const ChannelHideMessage = 43\nexport type ChannelHideMessage = typeof ChannelHideMessage\nexport const ChannelMuteUser = 44\nexport type ChannelMuteUser = typeof ChannelMuteUser\nexport const OpenTimestamps = 1040\nexport type OpenTimestamps = typeof OpenTimestamps\nexport const GiftWrap = 1059\nexport type GiftWrap = typeof GiftWrap\nexport const Poll = 1068\nexport type Poll = typeof Poll\nexport const FileMetadata = 1063\nexport type FileMetadata = typeof FileMetadata\nexport const Comment = 1111\nexport type Comment = typeof Comment\nexport const LiveChatMessage = 1311\nexport type LiveChatMessage = typeof LiveChatMessage\nexport const Voice = 1222\nexport type Voice = typeof Voice\nexport const VoiceComment = 1244\nexport type VoiceComment = typeof VoiceComment\nexport const ProblemTracker = 1971\nexport type ProblemTracker = typeof ProblemTracker\nexport const Report = 1984\nexport type Report = typeof Report\nexport const Reporting = 1984\nexport type Reporting = typeof Reporting\nexport const Label = 1985\nexport type Label = typeof Label\nexport const CommunityPostApproval = 4550\nexport type CommunityPostApproval = typeof CommunityPostApproval\nexport const JobRequest = 5999\nexport type JobRequest = typeof JobRequest\nexport const JobResult = 6999\nexport type JobResult = typeof JobResult\nexport const JobFeedback = 7000\nexport type JobFeedback = typeof JobFeedback\nexport const ZapGoal = 9041\nexport type ZapGoal = typeof ZapGoal\nexport const ZapRequest = 9734\nexport type ZapRequest = typeof ZapRequest\nexport const Zap = 9735\nexport type Zap = typeof Zap\nexport const Highlights = 9802\nexport type Highlights = typeof Highlights\nexport const PollResponse = 1018\nexport type PollResponse = typeof PollResponse\nexport const Mutelist = 10000\nexport type Mutelist = typeof Mutelist\nexport const Pinlist = 10001\nexport type Pinlist = typeof Pinlist\nexport const RelayList = 10002\nexport type RelayList = typeof RelayList\nexport const BookmarkList = 10003\nexport type BookmarkList = typeof BookmarkList\nexport const CommunitiesList = 10004\nexport type CommunitiesList = typeof CommunitiesList\nexport const PublicChatsList = 10005\nexport type PublicChatsList = typeof PublicChatsList\nexport const BlockedRelaysList = 10006\nexport type BlockedRelaysList = typeof BlockedRelaysList\nexport const SearchRelaysList = 10007\nexport type SearchRelaysList = typeof SearchRelaysList\nexport const FavoriteRelays = 10012\nexport type FavoriteRelays = typeof FavoriteRelays\nexport const InterestsList = 10015\nexport type InterestsList = typeof InterestsList\nexport const UserEmojiList = 10030\nexport type UserEmojiList = typeof UserEmojiList\nexport const DirectMessageRelaysList = 10050\nexport type DirectMessageRelaysList = typeof DirectMessageRelaysList\nexport const FileServerPreference = 10096\nexport type FileServerPreference = typeof FileServerPreference\nexport const BlossomServerList = 10063\nexport type BlossomServerList = typeof BlossomServerList\nexport const NWCWalletInfo = 13194\nexport type NWCWalletInfo = typeof NWCWalletInfo\nexport const LightningPubRPC = 21000\nexport type LightningPubRPC = typeof LightningPubRPC\nexport const ClientAuth = 22242\nexport type ClientAuth = typeof ClientAuth\nexport const NWCWalletRequest = 23194\nexport type NWCWalletRequest = typeof NWCWalletRequest\nexport const NWCWalletResponse = 23195\nexport type NWCWalletResponse = typeof NWCWalletResponse\nexport const NostrConnect = 24133\nexport type NostrConnect = typeof NostrConnect\nexport const HTTPAuth = 27235\nexport type HTTPAuth = typeof HTTPAuth\nexport const Followsets = 30000\nexport type Followsets = typeof Followsets\nexport const Genericlists = 30001\nexport type Genericlists = typeof Genericlists\nexport const Relaysets = 30002\nexport type Relaysets = typeof Relaysets\nexport const Bookmarksets = 30003\nexport type Bookmarksets = typeof Bookmarksets\nexport const Curationsets = 30004\nexport type Curationsets = typeof Curationsets\nexport const ProfileBadges = 30008\nexport type ProfileBadges = typeof ProfileBadges\nexport const BadgeDefinition = 30009\nexport type BadgeDefinition = typeof BadgeDefinition\nexport const Interestsets = 30015\nexport type Interestsets = typeof Interestsets\nexport const CreateOrUpdateStall = 30017\nexport type CreateOrUpdateStall = typeof CreateOrUpdateStall\nexport const CreateOrUpdateProduct = 30018\nexport type CreateOrUpdateProduct = typeof CreateOrUpdateProduct\nexport const LongFormArticle = 30023\nexport type LongFormArticle = typeof LongFormArticle\nexport const DraftLong = 30024\nexport type DraftLong = typeof DraftLong\nexport const Emojisets = 30030\nexport type Emojisets = typeof Emojisets\nexport const Application = 30078\nexport type Application = typeof Application\nexport const LiveEvent = 30311\nexport type LiveEvent = typeof LiveEvent\nexport const UserStatuses = 30315\nexport type UserStatuses = typeof UserStatuses\nexport const ClassifiedListing = 30402\nexport type ClassifiedListing = typeof ClassifiedListing\nexport const DraftClassifiedListing = 30403\nexport type DraftClassifiedListing = typeof DraftClassifiedListing\nexport const Date = 31922\nexport type Date = typeof Date\nexport const Time = 31923\nexport type Time = typeof Time\nexport const Calendar = 31924\nexport type Calendar = typeof Calendar\nexport const CalendarEventRSVP = 31925\nexport type CalendarEventRSVP = typeof CalendarEventRSVP\nexport const RelayReview = 31987\nexport type RelayReview = typeof RelayReview\nexport const Handlerrecommendation = 31989\nexport type Handlerrecommendation = typeof Handlerrecommendation\nexport const Handlerinformation = 31990\nexport type Handlerinformation = typeof Handlerinformation\nexport const CommunityDefinition = 34550\nexport type CommunityDefinition = typeof CommunityDefinition\nexport const GroupMetadata = 39000\nexport type GroupMetadata = typeof GroupMetadata\n", "import { Event } from './core.ts'\nimport { isAddressableKind, isReplaceableKind } from './kinds.ts'\n\nexport type Filter = {\n  ids?: string[]\n  kinds?: number[]\n  authors?: string[]\n  since?: number\n  until?: number\n  limit?: number\n  search?: string\n  [key: `#${string}`]: string[] | undefined\n}\n\nexport function matchFilter(filter: Filter, event: Event): boolean {\n  if (filter.ids && filter.ids.indexOf(event.id) === -1) {\n    return false\n  }\n  if (filter.kinds && filter.kinds.indexOf(event.kind) === -1) {\n    return false\n  }\n  if (filter.authors && filter.authors.indexOf(event.pubkey) === -1) {\n    return false\n  }\n\n  for (let f in filter) {\n    if (f[0] === '#') {\n      let tagName = f.slice(1)\n      let values = filter[`#${tagName}`]\n      if (values && !event.tags.find(([t, v]) => t === f.slice(1) && values!.indexOf(v) !== -1)) return false\n    }\n  }\n\n  if (filter.since && event.created_at < filter.since) return false\n  if (filter.until && event.created_at > filter.until) return false\n\n  return true\n}\n\nexport function matchFilters(filters: Filter[], event: Event): boolean {\n  for (let i = 0; i < filters.length; i++) {\n    if (matchFilter(filters[i], event)) {\n      return true\n    }\n  }\n  return false\n}\n\nexport function mergeFilters(...filters: Filter[]): Filter {\n  let result: Filter = {}\n  for (let i = 0; i < filters.length; i++) {\n    let filter = filters[i]\n    Object.entries(filter).forEach(([property, values]) => {\n      if (property === 'kinds' || property === 'ids' || property === 'authors' || property[0] === '#') {\n        // @ts-ignore\n        result[property] = result[property] || []\n        // @ts-ignore\n        for (let v = 0; v < values.length; v++) {\n          // @ts-ignore\n          let value = values[v]\n          // @ts-ignore\n          if (!result[property].includes(value)) result[property].push(value)\n        }\n      }\n    })\n\n    if (filter.limit && (!result.limit || filter.limit > result.limit)) result.limit = filter.limit\n    if (filter.until && (!result.until || filter.until > result.until)) result.until = filter.until\n    if (filter.since && (!result.since || filter.since < result.since)) result.since = filter.since\n  }\n\n  return result\n}\n\n/**\n * Calculate the intrinsic limit of a filter.\n * This function returns a positive integer, or `Infinity` if there is no intrinsic limit.\n */\nexport function getFilterLimit(filter: Filter): number {\n  if (filter.ids && !filter.ids.length) return 0\n  if (filter.kinds && !filter.kinds.length) return 0\n  if (filter.authors && !filter.authors.length) return 0\n\n  for (const [key, value] of Object.entries(filter)) {\n    if (key[0] === '#' && Array.isArray(value) && !value.length) return 0\n  }\n\n  return Math.min(\n    // The `limit` property creates an artificial limit.\n    Math.max(0, filter.limit ?? Infinity),\n\n    // There can only be one event per `id`.\n    filter.ids?.length ?? Infinity,\n\n    // Replaceable events are limited by the number of authors and kinds.\n    filter.authors?.length && filter.kinds?.every(kind => isReplaceableKind(kind))\n      ? filter.authors.length * filter.kinds.length\n      : Infinity,\n\n    // Parameterized replaceable events are limited by the number of authors, kinds, and \"d\" tags.\n    filter.authors?.length && filter.kinds?.every(kind => isAddressableKind(kind)) && filter['#d']?.length\n      ? filter.authors.length * filter.kinds.length * filter['#d'].length\n      : Infinity,\n  )\n}\n", "export function getHex64(json: string, field: string): string {\n  let len = field.length + 3\n  let idx = json.indexOf(`\"${field}\":`) + len\n  let s = json.slice(idx).indexOf(`\"`) + idx + 1\n  return json.slice(s, s + 64)\n}\n\nexport function getInt(json: string, field: string): number {\n  let len = field.length\n  let idx = json.indexOf(`\"${field}\":`) + len + 3\n  let sliced = json.slice(idx)\n  let end = Math.min(sliced.indexOf(','), sliced.indexOf('}'))\n  return parseInt(sliced.slice(0, end), 10)\n}\n\nexport function getSubscriptionId(json: string): string | null {\n  let idx = json.slice(0, 22).indexOf(`\"EVENT\"`)\n  if (idx === -1) return null\n\n  let pstart = json.slice(idx + 7 + 1).indexOf(`\"`)\n  if (pstart === -1) return null\n  let start = idx + 7 + 1 + pstart\n\n  let pend = json.slice(start + 1, 80).indexOf(`\"`)\n  if (pend === -1) return null\n  let end = start + 1 + pend\n\n  return json.slice(start + 1, end)\n}\n\nexport function matchEventId(json: string, id: string): boolean {\n  return id === getHex64(json, 'id')\n}\n\nexport function matchEventPubkey(json: string, pubkey: string): boolean {\n  return pubkey === getHex64(json, 'pubkey')\n}\n\nexport function matchEventKind(json: string, kind: number): boolean {\n  return kind === getInt(json, 'kind')\n}\n", "import { EventTemplate } from './core.ts'\nimport { ClientAuth } from './kinds.ts'\n\n/**\n * creates an EventTemplate for an AUTH event to be signed.\n */\nexport function makeAuthEvent(relayURL: string, challenge: string): EventTemplate {\n  return {\n    kind: ClientAuth,\n    created_at: Math.floor(Date.now() / 1000),\n    tags: [\n      ['relay', relayURL],\n      ['challenge', challenge],\n    ],\n    content: '',\n  }\n}\n", "import { verifiedSymbol, type Event, type Nostr, VerifiedEvent } from './core.ts'\n\nexport async function yieldThread() {\n  return new Promise<void>((resolve, reject) => {\n    try {\n      // Check if MessageChannel is available\n      if (typeof MessageChannel !== 'undefined') {\n        const ch = new MessageChannel()\n        const handler = () => {\n          // @ts-ignore (typescript thinks this property should be called `removeListener`, but in fact it's `removeEventListener`)\n          ch.port1.removeEventListener('message', handler)\n          resolve()\n        }\n        // @ts-ignore (typescript thinks this property should be called `addListener`, but in fact it's `addEventListener`)\n        ch.port1.addEventListener('message', handler)\n        ch.port2.postMessage(0)\n        ch.port1.start()\n      } else {\n        if (typeof setImmediate !== 'undefined') {\n          setImmediate(resolve)\n        } else if (typeof setTimeout !== 'undefined') {\n          setTimeout(resolve, 0)\n        } else {\n          // Last resort - resolve immediately\n          resolve()\n        }\n      }\n    } catch (e) {\n      console.error('during yield: ', e)\n      reject(e)\n    }\n  })\n}\n\nexport const alwaysTrue: Nostr['verifyEvent'] = (t: Event): t is VerifiedEvent => {\n  t[verifiedSymbol] = true\n  return true\n}\n"],
   "mappings": ";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACKA,mBAAuC;AAHhC,IAAM,cAA2B,IAAI,YAAY,OAAO;AACxD,IAAM,cAA2B,IAAI,YAAY;AAIjD,SAAS,aAAa,KAAqB;AAChD,MAAI;AACF,QAAI,IAAI,QAAQ,KAAK,MAAM;AAAI,YAAM,WAAW;AAChD,QAAI,IAAI,IAAI,IAAI,GAAG;AACnB,QAAI,EAAE,aAAa;AAAS,QAAE,WAAW;AAAA,aAChC,EAAE,aAAa;AAAU,QAAE,WAAW;AAC/C,MAAE,WAAW,EAAE,SAAS,QAAQ,QAAQ,GAAG;AAC3C,QAAI,EAAE,SAAS,SAAS,GAAG;AAAG,QAAE,WAAW,EAAE,SAAS,MAAM,GAAG,EAAE;AACjE,QAAK,EAAE,SAAS,QAAQ,EAAE,aAAa,SAAW,EAAE,SAAS,SAAS,EAAE,aAAa;AAAS,QAAE,OAAO;AACvG,MAAE,aAAa,KAAK;AACpB,MAAE,OAAO;AACT,WAAO,EAAE,SAAS;AAAA,EACpB,SAAS,GAAP;AACA,UAAM,IAAI,MAAM,gBAAgB,KAAK;AAAA,EACvC;AACF;AAgDO,IAAM,YAAN,MAAmB;AAAA,EACjB;AAAA,EACA,OAA4B;AAAA,EAC5B,OAA4B;AAAA,EAEnC,YAAY,SAAY;AACtB,SAAK,QAAQ;AAAA,EACf;AACF;AAEO,IAAM,QAAN,MAAe;AAAA,EACb;AAAA,EACA;AAAA,EAEP,cAAc;AACZ,SAAK,QAAQ;AACb,SAAK,OAAO;AAAA,EACd;AAAA,EAEA,QAAQ,OAAmB;AACzB,UAAM,UAAU,IAAI,UAAU,KAAK;AACnC,QAAI,CAAC,KAAK,MAAM;AAEd,WAAK,QAAQ;AACb,WAAK,OAAO;AAAA,IACd,WAAW,KAAK,SAAS,KAAK,OAAO;AAEnC,WAAK,OAAO;AACZ,WAAK,KAAK,OAAO,KAAK;AACtB,WAAK,MAAM,OAAO;AAAA,IACpB,OAAO;AAEL,cAAQ,OAAO,KAAK;AACpB,WAAK,KAAK,OAAO;AACjB,WAAK,OAAO;AAAA,IACd;AACA,WAAO;AAAA,EACT;AAAA,EAEA,UAAoB;AAClB,QAAI,CAAC,KAAK;AAAO,aAAO;AAExB,QAAI,KAAK,UAAU,KAAK,MAAM;AAC5B,YAAMA,UAAS,KAAK;AACpB,WAAK,QAAQ;AACb,WAAK,OAAO;AACZ,aAAOA,QAAO;AAAA,IAChB;AAEA,UAAM,SAAS,KAAK;AACpB,SAAK,QAAQ,OAAO;AACpB,QAAI,KAAK,OAAO;AACd,WAAK,MAAM,OAAO;AAAA,IACpB;AAEA,WAAO,OAAO;AAAA,EAChB;AACF;;;ACgCO,IAAM,aAAa;;;ACjJnB,SAAS,YAAY,QAAgB,OAAuB;AACjE,MAAI,OAAO,OAAO,OAAO,IAAI,QAAQ,MAAM,EAAE,MAAM,IAAI;AACrD,WAAO;AAAA,EACT;AACA,MAAI,OAAO,SAAS,OAAO,MAAM,QAAQ,MAAM,IAAI,MAAM,IAAI;AAC3D,WAAO;AAAA,EACT;AACA,MAAI,OAAO,WAAW,OAAO,QAAQ,QAAQ,MAAM,MAAM,MAAM,IAAI;AACjE,WAAO;AAAA,EACT;AAEA,WAAS,KAAK,QAAQ;AACpB,QAAI,EAAE,OAAO,KAAK;AAChB,UAAI,UAAU,EAAE,MAAM,CAAC;AACvB,UAAI,SAAS,OAAO,IAAI;AACxB,UAAI,UAAU,CAAC,MAAM,KAAK,KAAK,CAAC,CAAC,GAAG,CAAC,MAAM,MAAM,EAAE,MAAM,CAAC,KAAK,OAAQ,QAAQ,CAAC,MAAM,EAAE;AAAG,eAAO;AAAA,IACpG;AAAA,EACF;AAEA,MAAI,OAAO,SAAS,MAAM,aAAa,OAAO;AAAO,WAAO;AAC5D,MAAI,OAAO,SAAS,MAAM,aAAa,OAAO;AAAO,WAAO;AAE5D,SAAO;AACT;AAEO,SAAS,aAAa,SAAmB,OAAuB;AACrE,WAAS,IAAI,GAAG,IAAI,QAAQ,QAAQ,KAAK;AACvC,QAAI,YAAY,QAAQ,IAAI,KAAK,GAAG;AAClC,aAAO;AAAA,IACT;AAAA,EACF;AACA,SAAO;AACT;;;AC9CO,SAAS,SAAS,MAAc,OAAuB;AAC5D,MAAI,MAAM,MAAM,SAAS;AACzB,MAAI,MAAM,KAAK,QAAQ,IAAI,SAAS,IAAI;AACxC,MAAI,IAAI,KAAK,MAAM,GAAG,EAAE,QAAQ,GAAG,IAAI,MAAM;AAC7C,SAAO,KAAK,MAAM,GAAG,IAAI,EAAE;AAC7B;AAUO,SAAS,kBAAkB,MAA6B;AAC7D,MAAI,MAAM,KAAK,MAAM,GAAG,EAAE,EAAE,QAAQ,SAAS;AAC7C,MAAI,QAAQ;AAAI,WAAO;AAEvB,MAAI,SAAS,KAAK,MAAM,MAAM,IAAI,CAAC,EAAE,QAAQ,GAAG;AAChD,MAAI,WAAW;AAAI,WAAO;AAC1B,MAAI,QAAQ,MAAM,IAAI,IAAI;AAE1B,MAAI,OAAO,KAAK,MAAM,QAAQ,GAAG,EAAE,EAAE,QAAQ,GAAG;AAChD,MAAI,SAAS;AAAI,WAAO;AACxB,MAAI,MAAM,QAAQ,IAAI;AAEtB,SAAO,KAAK,MAAM,QAAQ,GAAG,GAAG;AAClC;;;ACtBO,SAAS,cAAc,UAAkB,WAAkC;AAChF,SAAO;AAAA,IACL,MAAM;AAAA,IACN,YAAY,KAAK,MAAM,KAAK,IAAI,IAAI,GAAI;AAAA,IACxC,MAAM;AAAA,MACJ,CAAC,SAAS,QAAQ;AAAA,MAClB,CAAC,aAAa,SAAS;AAAA,IACzB;AAAA,IACA,SAAS;AAAA,EACX;AACF;;;ACdA,eAAsB,cAAc;AAClC,SAAO,IAAI,QAAc,CAAC,SAAS,WAAW;AAC5C,QAAI;AAEF,UAAI,OAAO,mBAAmB,aAAa;AACzC,cAAM,KAAK,IAAI,eAAe;AAC9B,cAAM,UAAU,MAAM;AAEpB,aAAG,MAAM,oBAAoB,WAAW,OAAO;AAC/C,kBAAQ;AAAA,QACV;AAEA,WAAG,MAAM,iBAAiB,WAAW,OAAO;AAC5C,WAAG,MAAM,YAAY,CAAC;AACtB,WAAG,MAAM,MAAM;AAAA,MACjB,OAAO;AACL,YAAI,OAAO,iBAAiB,aAAa;AACvC,uBAAa,OAAO;AAAA,QACtB,WAAW,OAAO,eAAe,aAAa;AAC5C,qBAAW,SAAS,CAAC;AAAA,QACvB,OAAO;AAEL,kBAAQ;AAAA,QACV;AAAA,MACF;AAAA,IACF,SAAS,GAAP;AACA,cAAQ,MAAM,kBAAkB,CAAC;AACjC,aAAO,CAAC;AAAA,IACV;AAAA,EACF,CAAC;AACH;;;ANXO,IAAM,4BAAN,cAAwC,MAAM;AAAA,EACnD,YAAY,SAAiB,OAAe;AAC1C,UAAM,0BAA0B,qCAAqC,QAAQ;AAC7E,SAAK,OAAO;AAAA,EACd;AACF;AAEO,IAAM,gBAAN,MAAoB;AAAA,EACT;AAAA,EACR,aAAsB;AAAA,EAEvB,UAA+B;AAAA,EAC/B,WAAkC,SAAO,QAAQ,MAAM,eAAe,KAAK,QAAQ,KAAK;AAAA,EACxF;AAAA,EAEA,kBAA0B;AAAA,EAC1B,iBAAyB;AAAA,EACzB,gBAAwB;AAAA,EACxB,cAAsB;AAAA,EACtB,qBAA+B,CAAC,KAAO,KAAO,KAAO,KAAO,KAAO,KAAO,GAAK;AAAA,EAC/E,WAAsC,oBAAI,IAAI;AAAA,EAC9C;AAAA,EACA;AAAA,EACC;AAAA,EACA;AAAA,EACA,oBAA4B;AAAA,EAC5B,sBAA+B;AAAA,EAE/B;AAAA,EACA,oBAAoB,oBAAI,IAA2B;AAAA,EACnD,qBAAqB,oBAAI,IAAkC;AAAA,EAC3D;AAAA,EACA,uBAAuB,IAAI,MAAc;AAAA,EACzC,eAAe;AAAA,EACf;AAAA,EACA;AAAA,EACA,SAAiB;AAAA,EACjB;AAAA,EAEA;AAAA,EAER,YAAY,KAAa,MAAuC;AAC9D,SAAK,MAAM,aAAa,GAAG;AAC3B,SAAK,cAAc,KAAK;AACxB,SAAK,aAAa,KAAK,2BAA2B;AAClD,SAAK,aAAa,KAAK;AACvB,SAAK,kBAAkB,KAAK,mBAAmB;AAAA,EACjD;AAAA,EAEA,aAAa,QACX,KACA,MACwB;AACxB,UAAM,QAAQ,IAAI,cAAc,KAAK,IAAI;AACzC,UAAM,MAAM,QAAQ,IAAI;AACxB,WAAO;AAAA,EACT;AAAA,EAEQ,sBAAsB,QAAgB;AAC5C,aAAS,CAAC,GAAG,GAAG,KAAK,KAAK,UAAU;AAClC,UAAI,MAAM,MAAM;AAAA,IAClB;AACA,SAAK,SAAS,MAAM;AAEpB,aAAS,CAAC,GAAG,EAAE,KAAK,KAAK,oBAAoB;AAC3C,SAAG,OAAO,IAAI,MAAM,MAAM,CAAC;AAAA,IAC7B;AACA,SAAK,mBAAmB,MAAM;AAE9B,aAAS,CAAC,GAAG,EAAE,KAAK,KAAK,mBAAmB;AAC1C,SAAG,OAAO,IAAI,MAAM,MAAM,CAAC;AAAA,IAC7B;AACA,SAAK,kBAAkB,MAAM;AAAA,EAC/B;AAAA,EAEA,IAAW,YAAqB;AAC9B,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,MAAc,YAA2B;AACvC,UAAM,UAAU,KAAK,mBAAmB,KAAK,IAAI,KAAK,mBAAmB,KAAK,mBAAmB,SAAS,CAAC;AAC3G,SAAK;AAEL,SAAK,yBAAyB,WAAW,YAAY;AACnD,UAAI;AACF,cAAM,KAAK,QAAQ;AAAA,MACrB,SAAS,KAAP;AAAA,MAEF;AAAA,IACF,GAAG,OAAO;AAAA,EACZ;AAAA,EAEQ,gBAAgB,QAAgB;AACtC,QAAI,KAAK,oBAAoB;AAC3B,oBAAc,KAAK,kBAAkB;AACrC,WAAK,qBAAqB;AAAA,IAC5B;AAEA,SAAK,aAAa;AAClB,SAAK,oBAAoB;AAEzB,UAAM,iBAAiB,KAAK;AAC5B,SAAK,sBAAsB;AAE3B,SAAK,UAAU;AAEf,QAAI,KAAK,mBAAmB,CAAC,gBAAgB;AAC3C,WAAK,UAAU;AAAA,IACjB,OAAO;AACL,WAAK,sBAAsB,MAAM;AAAA,IACnC;AAAA,EACF;AAAA,EAEA,MAAa,QAAQ,MAAiE;AACpF,QAAI;AAEJ,QAAI,KAAK;AAAmB,aAAO,KAAK;AAExC,SAAK,YAAY;AACjB,SAAK,cAAc;AACnB,SAAK,oBAAoB,IAAI,QAAQ,CAAC,SAAS,WAAW;AACxD,UAAI,MAAM,SAAS;AACjB,kCAA0B,WAAW,MAAM;AACzC,iBAAO,sBAAsB;AAC7B,eAAK,oBAAoB;AACzB,eAAK,UAAU;AACf,eAAK,sBAAsB,4BAA4B;AAAA,QACzD,GAAG,KAAK,OAAO;AAAA,MACjB;AAEA,UAAI,MAAM,OAAO;AACf,aAAK,MAAM,UAAU;AAAA,MACvB;AAEA,UAAI;AACF,aAAK,KAAK,IAAI,KAAK,WAAW,KAAK,GAAG;AAAA,MACxC,SAAS,KAAP;AACA,qBAAa,uBAAuB;AACpC,eAAO,GAAG;AACV;AAAA,MACF;AAEA,WAAK,GAAG,SAAS,MAAM;AACrB,YAAI,KAAK,wBAAwB;AAC/B,uBAAa,KAAK,sBAAsB;AACxC,eAAK,yBAAyB;AAAA,QAChC;AACA,qBAAa,uBAAuB;AACpC,aAAK,aAAa;AAElB,cAAM,iBAAiB,KAAK,oBAAoB;AAChD,aAAK,oBAAoB;AAGzB,mBAAW,OAAO,KAAK,SAAS,OAAO,GAAG;AACxC,cAAI,QAAQ;AACZ,cAAI,gBAAgB;AAClB,qBAAS,IAAI,GAAG,IAAI,IAAI,QAAQ,QAAQ,KAAK;AAC3C,kBAAI,IAAI,aAAa;AACnB,oBAAI,QAAQ,GAAG,QAAQ,IAAI,cAAc;AAAA,cAC3C;AAAA,YACF;AAAA,UACF;AACA,cAAI,KAAK;AAAA,QACX;AAEA,YAAI,KAAK,YAAY;AACnB,eAAK,qBAAqB,YAAY,MAAM,KAAK,SAAS,GAAG,KAAK,aAAa;AAAA,QACjF;AACA,gBAAQ;AAAA,MACV;AAEA,WAAK,GAAG,UAAU,QAAM;AACtB,qBAAa,uBAAuB;AACpC,eAAQ,GAAW,WAAW,iBAAiB;AAC/C,aAAK,gBAAgB,0BAA0B;AAAA,MACjD;AAEA,WAAK,GAAG,UAAU,QAAM;AACtB,qBAAa,uBAAuB;AACpC,eAAQ,GAAW,WAAW,kBAAkB;AAChD,aAAK,gBAAgB,yBAAyB;AAAA,MAChD;AAEA,WAAK,GAAG,YAAY,KAAK,WAAW,KAAK,IAAI;AAAA,IAC/C,CAAC;AAED,WAAO,KAAK;AAAA,EACd;AAAA,EAEQ,kBAAkB;AACxB,WAAO,IAAI,QAAQ,aAAW;AAE5B;AAAC,MAAC,KAAK,GAAW,KAAK,QAAQ,MAAM,QAAQ,IAAI,CAAC;AAElD,WAAK,GAAI,KAAM;AAAA,IACjB,CAAC;AAAA,EACH;AAAA,EAEQ,kBAAkB;AACxB,WAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,UAAI,CAAC,KAAK;AAAmB,eAAO,OAAO,IAAI,MAAM,oBAAoB,KAAK,iBAAiB,CAAC;AAIhG,UAAI;AACF,cAAM,MAAM,KAAK;AAAA,UACf,CAAC,EAAE,KAAK,CAAC,kEAAkE,GAAG,OAAO,EAAE,CAAC;AAAA,UACxF;AAAA,YACE,OAAO;AAAA,YACP,QAAQ,MAAM;AACZ,sBAAQ,IAAI;AACZ,kBAAI,MAAM;AAAA,YACZ;AAAA,YACA,UAAU;AAER,sBAAQ,IAAI;AAAA,YACd;AAAA,YACA,aAAa,KAAK,cAAc;AAAA,UAClC;AAAA,QACF;AAAA,MACF,SAAS,KAAP;AACA,eAAO,GAAG;AAAA,MACZ;AAAA,IACF,CAAC;AAAA,EACH;AAAA,EAIA,MAAc,WAAW;AAEvB,QAAI,KAAK,IAAI,eAAe,GAAG;AAE7B,YAAM,SAAS,MAAM,QAAQ,IAAI;AAAA,QAE/B,KAAK,MAAM,KAAK,GAAG,QAAS,KAAK,GAAW,OAAO,KAAK,gBAAgB,IAAI,KAAK,gBAAgB;AAAA,QACjG,IAAI,QAAQ,SAAO,WAAW,MAAM,IAAI,KAAK,GAAG,KAAK,WAAW,CAAC;AAAA,MACnE,CAAC;AAED,UAAI,CAAC,QAAQ;AAEX,YAAI,KAAK,IAAI,eAAe,KAAK,WAAW,MAAM;AAChD,eAAK,IAAI,MAAM;AAAA,QACjB;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA,EAEA,MAAc,WAAW;AACvB,SAAK,eAAe;AACpB,WAAO,MAAM;AACX,UAAI,UAAU,KAAK,WAAW,GAAG;AAC/B;AAAA,MACF;AACA,YAAM,YAAY;AAAA,IACpB;AACA,SAAK,eAAe;AAAA,EACtB;AAAA,EAEQ,aAAgC;AACtC,UAAM,OAAO,KAAK,qBAAqB,QAAQ;AAC/C,QAAI,CAAC,MAAM;AACT,aAAO;AAAA,IACT;AAGA,UAAM,QAAQ,kBAAkB,IAAI;AACpC,QAAI,OAAO;AACT,YAAM,KAAK,KAAK,SAAS,IAAI,KAAe;AAC5C,UAAI,CAAC,IAAI;AAEP;AAAA,MACF;AAKA,YAAM,KAAK,SAAS,MAAM,IAAI;AAC9B,YAAM,cAAc,GAAG,mBAAmB,EAAE;AAI5C,SAAG,gBAAgB,MAAM,EAAE;AAE3B,UAAI,aAAa;AAEf;AAAA,MACF;AAAA,IACF;AAEA,QAAI;AACF,UAAI,OAAO,KAAK,MAAM,IAAI;AAI1B,cAAQ,KAAK,IAAI;AAAA,QACf,KAAK,SAAS;AACZ,gBAAM,KAAK,KAAK,SAAS,IAAI,KAAK,EAAY;AAC9C,gBAAM,QAAQ,KAAK;AACnB,cAAI,KAAK,YAAY,KAAK,KAAK,aAAa,GAAG,SAAS,KAAK,GAAG;AAC9D,eAAG,QAAQ,KAAK;AAAA,UAClB;AACA,cAAI,CAAC,GAAG,eAAe,GAAG,cAAc,MAAM;AAAY,eAAG,cAAc,MAAM;AACjF;AAAA,QACF;AAAA,QACA,KAAK,SAAS;AACZ,gBAAM,KAAa,KAAK;AACxB,gBAAM,UAAU,KAAK;AACrB,gBAAM,KAAK,KAAK,kBAAkB,IAAI,EAAE;AACxC,cAAI,IAAI;AACN,eAAG,QAAQ,QAAQ,KAAK;AACxB,iBAAK,kBAAkB,OAAO,EAAE;AAAA,UAClC;AACA;AAAA,QACF;AAAA,QACA,KAAK,QAAQ;AACX,gBAAM,KAAK,KAAK,SAAS,IAAI,KAAK,EAAY;AAC9C,cAAI,CAAC;AAAI;AACT,aAAG,aAAa;AAChB;AAAA,QACF;AAAA,QACA,KAAK,MAAM;AACT,gBAAM,KAAa,KAAK;AACxB,gBAAM,KAAc,KAAK;AACzB,gBAAM,SAAiB,KAAK;AAC5B,gBAAM,KAAK,KAAK,mBAAmB,IAAI,EAAE;AACzC,cAAI,IAAI;AACN,yBAAa,GAAG,OAAO;AACvB,gBAAI;AAAI,iBAAG,QAAQ,MAAM;AAAA;AACpB,iBAAG,OAAO,IAAI,MAAM,MAAM,CAAC;AAChC,iBAAK,mBAAmB,OAAO,EAAE;AAAA,UACnC;AACA;AAAA,QACF;AAAA,QACA,KAAK,UAAU;AACb,gBAAM,KAAa,KAAK;AACxB,gBAAM,KAAK,KAAK,SAAS,IAAI,EAAE;AAC/B,cAAI,CAAC;AAAI;AACT,aAAG,SAAS;AACZ,aAAG,MAAM,KAAK,EAAY;AAC1B;AAAA,QACF;AAAA,QACA,KAAK,UAAU;AACb,eAAK,SAAS,KAAK,EAAY;AAC/B;AAAA,QACF;AAAA,QACA,KAAK,QAAQ;AACX,eAAK,YAAY,KAAK;AACtB,cAAI,KAAK,QAAQ;AACf,iBAAK,KAAK,KAAK,MAAM;AAAA,UACvB;AACA;AAAA,QACF;AAAA,QACA,SAAS;AACP,gBAAM,KAAK,KAAK,SAAS,IAAI,KAAK,EAAE;AACpC,cAAI,WAAW,IAAI;AACnB;AAAA,QACF;AAAA,MACF;AAAA,IACF,SAAS,KAAP;AACA;AAAA,IACF;AAAA,EACF;AAAA,EAEA,MAAa,KAAK,SAAiB;AACjC,QAAI,CAAC,KAAK;AAAmB,YAAM,IAAI,0BAA0B,SAAS,KAAK,GAAG;AAElF,SAAK,kBAAkB,KAAK,MAAM;AAChC,WAAK,IAAI,KAAK,OAAO;AAAA,IACvB,CAAC;AAAA,EACH;AAAA,EAEA,MAAa,KAAK,eAAgF;AAChG,UAAM,YAAY,KAAK;AACvB,QAAI,CAAC;AAAW,YAAM,IAAI,MAAM,+CAA+C;AAC/E,QAAI,KAAK;AAAa,aAAO,KAAK;AAElC,SAAK,cAAc,IAAI,QAAgB,OAAO,SAAS,WAAW;AAChE,UAAI;AACF,YAAI,MAAM,MAAM,cAAc,cAAc,KAAK,KAAK,SAAS,CAAC;AAChE,YAAI,UAAU,WAAW,MAAM;AAC7B,cAAI,KAAK,KAAK,mBAAmB,IAAI,IAAI,EAAE;AAC3C,cAAI,IAAI;AACN,eAAG,OAAO,IAAI,MAAM,gBAAgB,CAAC;AACrC,iBAAK,mBAAmB,OAAO,IAAI,EAAE;AAAA,UACvC;AAAA,QACF,GAAG,KAAK,cAAc;AACtB,aAAK,mBAAmB,IAAI,IAAI,IAAI,EAAE,SAAS,QAAQ,QAAQ,CAAC;AAChE,aAAK,KAAK,aAAa,KAAK,UAAU,GAAG,IAAI,GAAG;AAAA,MAClD,SAAS,KAAP;AACA,gBAAQ,KAAK,mCAAmC,GAAG;AAAA,MACrD;AAAA,IACF,CAAC;AACD,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,MAAa,QAAQ,OAA+B;AAClD,UAAM,MAAM,IAAI,QAAgB,CAAC,SAAS,WAAW;AACnD,YAAM,UAAU,WAAW,MAAM;AAC/B,cAAM,KAAK,KAAK,mBAAmB,IAAI,MAAM,EAAE;AAC/C,YAAI,IAAI;AACN,aAAG,OAAO,IAAI,MAAM,mBAAmB,CAAC;AACxC,eAAK,mBAAmB,OAAO,MAAM,EAAE;AAAA,QACzC;AAAA,MACF,GAAG,KAAK,cAAc;AACtB,WAAK,mBAAmB,IAAI,MAAM,IAAI,EAAE,SAAS,QAAQ,QAAQ,CAAC;AAAA,IACpE,CAAC;AACD,SAAK,KAAK,cAAc,KAAK,UAAU,KAAK,IAAI,GAAG;AACnD,WAAO;AAAA,EACT;AAAA,EAEA,MAAa,MAAM,SAAmB,QAAiD;AACrF,SAAK;AACL,UAAM,KAAK,QAAQ,MAAM,WAAW,KAAK;AACzC,UAAM,MAAM,IAAI,QAAgB,CAAC,SAAS,WAAW;AACnD,WAAK,kBAAkB,IAAI,IAAI,EAAE,SAAS,OAAO,CAAC;AAAA,IACpD,CAAC;AACD,SAAK,KAAK,eAAe,KAAK,OAAO,KAAK,UAAU,OAAO,EAAE,UAAU,CAAC,CAAC;AACzE,WAAO;AAAA,EACT;AAAA,EAEO,UACL,SACA,QACc;AACd,UAAM,MAAM,KAAK,oBAAoB,SAAS,MAAM;AACpD,QAAI,KAAK;AAET,QAAI,OAAO,OAAO;AAChB,aAAO,MAAM,UAAU,MAAM,IAAI,MAAM,OAAO,OAAO,MAAO,UAAU,WAAW,CAAC;AAAA,IACpF;AAEA,WAAO;AAAA,EACT;AAAA,EAEO,oBACL,SACA,QACc;AACd,SAAK;AACL,UAAM,KAAK,OAAO,OAAO,OAAO,QAAQ,OAAO,QAAQ,MAAM,UAAU,KAAK;AAC5E,UAAM,eAAe,IAAI,aAAa,MAAM,IAAI,SAAS,MAAM;AAC/D,SAAK,SAAS,IAAI,IAAI,YAAY;AAClC,WAAO;AAAA,EACT;AAAA,EAEO,QAAQ;AACb,SAAK,sBAAsB;AAC3B,QAAI,KAAK,wBAAwB;AAC/B,mBAAa,KAAK,sBAAsB;AACxC,WAAK,yBAAyB;AAAA,IAChC;AACA,QAAI,KAAK,oBAAoB;AAC3B,oBAAc,KAAK,kBAAkB;AACrC,WAAK,qBAAqB;AAAA,IAC5B;AACA,SAAK,sBAAsB,+BAA+B;AAC1D,SAAK,aAAa;AAClB,SAAK,UAAU;AACf,QAAI,KAAK,IAAI,eAAe,KAAK,WAAW,MAAM;AAChD,WAAK,IAAI,MAAM;AAAA,IACjB;AAAA,EACF;AAAA,EAIO,WAAW,IAAuB;AACvC,SAAK,qBAAqB,QAAQ,GAAG,IAAc;AACnD,QAAI,CAAC,KAAK,cAAc;AACtB,WAAK,SAAS;AAAA,IAChB;AAAA,EACF;AACF;AAEO,IAAM,eAAN,MAAmB;AAAA,EACR;AAAA,EACA;AAAA,EAET;AAAA,EACA,SAAkB;AAAA,EAClB,QAAiB;AAAA,EACjB;AAAA,EACA;AAAA,EACA;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,EAGA;AAAA,EAEA;AAAA,EACC;AAAA,EAER,YAAY,OAAsB,IAAY,SAAmB,QAA4B;AAC3F,QAAI,QAAQ,WAAW;AAAG,YAAM,IAAI,MAAM,iDAAiD;AAE3F,SAAK,QAAQ;AACb,SAAK,UAAU;AACf,SAAK,KAAK;AACV,SAAK,mBAAmB,OAAO;AAC/B,SAAK,gBAAgB,OAAO;AAC5B,SAAK,cAAc,OAAO,eAAe,MAAM;AAE/C,SAAK,SAAS,OAAO;AACrB,SAAK,UAAU,OAAO;AACtB,SAAK,UACH,OAAO,YACN,WAAS;AACR,cAAQ;AAAA,QACN,oDAAoD,KAAK,gBAAgB,KAAK,MAAM;AAAA,QACpF;AAAA,MACF;AAAA,IACF;AAAA,EACJ;AAAA,EAEO,OAAO;AACZ,SAAK,MAAM,KAAK,aAAa,KAAK,KAAK,OAAO,KAAK,UAAU,KAAK,OAAO,EAAE,UAAU,CAAC,CAAC;AAGvF,SAAK,oBAAoB,WAAW,KAAK,aAAa,KAAK,IAAI,GAAG,KAAK,WAAW;AAAA,EACpF;AAAA,EAEO,eAAe;AACpB,QAAI,KAAK;AAAO;AAChB,iBAAa,KAAK,iBAAiB;AACnC,SAAK,QAAQ;AACb,SAAK,SAAS;AAAA,EAChB;AAAA,EAEO,MAAM,SAAiB,oBAAoB;AAChD,QAAI,CAAC,KAAK,UAAU,KAAK,MAAM,WAAW;AAGxC,UAAI;AACF,aAAK,MAAM,KAAK,cAAc,KAAK,UAAU,KAAK,EAAE,IAAI,GAAG;AAAA,MAC7D,SAAS,KAAP;AACA,YAAI,eAAe,2BAA2B;AAAA,QAE9C,OAAO;AACL,gBAAM;AAAA,QACR;AAAA,MACF;AACA,WAAK,SAAS;AAAA,IAChB;AACA,SAAK,MAAM,SAAS,OAAO,KAAK,EAAE;AAClC,SAAK,UAAU,MAAM;AAAA,EACvB;AACF;",
   "names": ["target"]
 }
Index: package/lib/esm/abstract-relay.js.map
===================================================================
--- package/lib/esm/abstract-relay.js.map
+++ package/lib/esm/abstract-relay.js.map
@@ -1,7 +1,7 @@
 {
   "version": 3,
   "sources": ["../../utils.ts", "../../kinds.ts", "../../filter.ts", "../../fakejson.ts", "../../nip42.ts", "../../helpers.ts", "../../abstract-relay.ts"],
-  "sourcesContent": ["import type { Event } from './core.ts'\n\nexport const utf8Decoder: TextDecoder = new TextDecoder('utf-8')\nexport const utf8Encoder: TextEncoder = new TextEncoder()\n\nexport { bytesToHex, hexToBytes } from '@noble/hashes/utils'\n\nexport function normalizeURL(url: string): string {\n  try {\n    if (url.indexOf('://') === -1) url = 'wss://' + url\n    let p = new URL(url)\n    if (p.protocol === 'http:') p.protocol = 'ws:'\n    else if (p.protocol === 'https:') p.protocol = 'wss:'\n    p.pathname = p.pathname.replace(/\\/+/g, '/')\n    if (p.pathname.endsWith('/')) p.pathname = p.pathname.slice(0, -1)\n    if ((p.port === '80' && p.protocol === 'ws:') || (p.port === '443' && p.protocol === 'wss:')) p.port = ''\n    p.searchParams.sort()\n    p.hash = ''\n    return p.toString()\n  } catch (e) {\n    throw new Error(`Invalid URL: ${url}`)\n  }\n}\n\nexport function insertEventIntoDescendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return b.created_at - event.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function insertEventIntoAscendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return event.created_at - b.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function binarySearch<T>(arr: T[], compare: (b: T) => number): [number, boolean] {\n  let start = 0\n  let end = arr.length - 1\n\n  while (start <= end) {\n    const mid = Math.floor((start + end) / 2)\n    const cmp = compare(arr[mid])\n\n    if (cmp === 0) {\n      return [mid, true]\n    }\n\n    if (cmp < 0) {\n      end = mid - 1\n    } else {\n      start = mid + 1\n    }\n  }\n\n  return [start, false]\n}\n\nexport class QueueNode<V> {\n  public value: V\n  public next: QueueNode<V> | null = null\n  public prev: QueueNode<V> | null = null\n\n  constructor(message: V) {\n    this.value = message\n  }\n}\n\nexport class Queue<V> {\n  public first: QueueNode<V> | null\n  public last: QueueNode<V> | null\n\n  constructor() {\n    this.first = null\n    this.last = null\n  }\n\n  enqueue(value: V): boolean {\n    const newNode = new QueueNode(value)\n    if (!this.last) {\n      // list is empty\n      this.first = newNode\n      this.last = newNode\n    } else if (this.last === this.first) {\n      // list has a single element\n      this.last = newNode\n      this.last.prev = this.first\n      this.first.next = newNode\n    } else {\n      // list has elements, add as last\n      newNode.prev = this.last\n      this.last.next = newNode\n      this.last = newNode\n    }\n    return true\n  }\n\n  dequeue(): V | null {\n    if (!this.first) return null\n\n    if (this.first === this.last) {\n      const target = this.first\n      this.first = null\n      this.last = null\n      return target.value\n    }\n\n    const target = this.first\n    this.first = target.next\n    if (this.first) {\n      this.first.prev = null // fix: clean up prev pointer\n    }\n\n    return target.value\n  }\n}\n", "import { NostrEvent, validateEvent } from './pure.ts'\n\n/** Events are **regular**, which means they're all expected to be stored by relays. */\nexport function isRegularKind(kind: number): boolean {\n  return kind < 10000 && kind !== 0 && kind !== 3\n}\n\n/** Events are **replaceable**, which means that, for each combination of `pubkey` and `kind`, only the latest event is expected to (SHOULD) be stored by relays, older versions are expected to be discarded. */\nexport function isReplaceableKind(kind: number): boolean {\n  return kind === 0 || kind === 3 || (10000 <= kind && kind < 20000)\n}\n\n/** Events are **ephemeral**, which means they are not expected to be stored by relays. */\nexport function isEphemeralKind(kind: number): boolean {\n  return 20000 <= kind && kind < 30000\n}\n\n/** Events are **addressable**, which means that, for each combination of `pubkey`, `kind` and the `d` tag, only the latest event is expected to be stored by relays, older versions are expected to be discarded. */\nexport function isAddressableKind(kind: number): boolean {\n  return 30000 <= kind && kind < 40000\n}\n\n/** Classification of the event kind. */\nexport type KindClassification = 'regular' | 'replaceable' | 'ephemeral' | 'parameterized' | 'unknown'\n\n/** Determine the classification of this kind of event if known, or `unknown`. */\nexport function classifyKind(kind: number): KindClassification {\n  if (isRegularKind(kind)) return 'regular'\n  if (isReplaceableKind(kind)) return 'replaceable'\n  if (isEphemeralKind(kind)) return 'ephemeral'\n  if (isAddressableKind(kind)) return 'parameterized'\n  return 'unknown'\n}\n\nexport function isKind<T extends number>(event: unknown, kind: T | Array<T>): event is NostrEvent & { kind: T } {\n  const kindAsArray: number[] = kind instanceof Array ? kind : [kind]\n  return (validateEvent(event) && kindAsArray.includes(event.kind)) || false\n}\n\nexport const Metadata = 0\nexport type Metadata = typeof Metadata\nexport const ShortTextNote = 1\nexport type ShortTextNote = typeof ShortTextNote\nexport const RecommendRelay = 2\nexport type RecommendRelay = typeof RecommendRelay\nexport const Contacts = 3\nexport type Contacts = typeof Contacts\nexport const EncryptedDirectMessage = 4\nexport type EncryptedDirectMessage = typeof EncryptedDirectMessage\nexport const EventDeletion = 5\nexport type EventDeletion = typeof EventDeletion\nexport const Repost = 6\nexport type Repost = typeof Repost\nexport const Reaction = 7\nexport type Reaction = typeof Reaction\nexport const BadgeAward = 8\nexport type BadgeAward = typeof BadgeAward\nexport const ChatMessage = 9\nexport type ChatMessage = typeof ChatMessage\nexport const ForumThread = 11\nexport type ForumThread = typeof ForumThread\nexport const Seal = 13\nexport type Seal = typeof Seal\nexport const PrivateDirectMessage = 14\nexport type PrivateDirectMessage = typeof PrivateDirectMessage\nexport const FileMessage = 15\nexport type FileMessage = typeof FileMessage\nexport const GenericRepost = 16\nexport type GenericRepost = typeof GenericRepost\nexport const Photo = 20\nexport type Photo = typeof Photo\nexport const NormalVideo = 21\nexport type NormalVideo = typeof NormalVideo\nexport const ShortVideo = 22\nexport type ShortVideo = typeof ShortVideo\nexport const ChannelCreation = 40\nexport type ChannelCreation = typeof ChannelCreation\nexport const ChannelMetadata = 41\nexport type ChannelMetadata = typeof ChannelMetadata\nexport const ChannelMessage = 42\nexport type ChannelMessage = typeof ChannelMessage\nexport const ChannelHideMessage = 43\nexport type ChannelHideMessage = typeof ChannelHideMessage\nexport const ChannelMuteUser = 44\nexport type ChannelMuteUser = typeof ChannelMuteUser\nexport const OpenTimestamps = 1040\nexport type OpenTimestamps = typeof OpenTimestamps\nexport const GiftWrap = 1059\nexport type GiftWrap = typeof GiftWrap\nexport const Poll = 1068\nexport type Poll = typeof Poll\nexport const FileMetadata = 1063\nexport type FileMetadata = typeof FileMetadata\nexport const Comment = 1111\nexport type Comment = typeof Comment\nexport const LiveChatMessage = 1311\nexport type LiveChatMessage = typeof LiveChatMessage\nexport const Voice = 1222\nexport type Voice = typeof Voice\nexport const VoiceComment = 1244\nexport type VoiceComment = typeof VoiceComment\nexport const ProblemTracker = 1971\nexport type ProblemTracker = typeof ProblemTracker\nexport const Report = 1984\nexport type Report = typeof Report\nexport const Reporting = 1984\nexport type Reporting = typeof Reporting\nexport const Label = 1985\nexport type Label = typeof Label\nexport const CommunityPostApproval = 4550\nexport type CommunityPostApproval = typeof CommunityPostApproval\nexport const JobRequest = 5999\nexport type JobRequest = typeof JobRequest\nexport const JobResult = 6999\nexport type JobResult = typeof JobResult\nexport const JobFeedback = 7000\nexport type JobFeedback = typeof JobFeedback\nexport const ZapGoal = 9041\nexport type ZapGoal = typeof ZapGoal\nexport const ZapRequest = 9734\nexport type ZapRequest = typeof ZapRequest\nexport const Zap = 9735\nexport type Zap = typeof Zap\nexport const Highlights = 9802\nexport type Highlights = typeof Highlights\nexport const PollResponse = 1018\nexport type PollResponse = typeof PollResponse\nexport const Mutelist = 10000\nexport type Mutelist = typeof Mutelist\nexport const Pinlist = 10001\nexport type Pinlist = typeof Pinlist\nexport const RelayList = 10002\nexport type RelayList = typeof RelayList\nexport const BookmarkList = 10003\nexport type BookmarkList = typeof BookmarkList\nexport const CommunitiesList = 10004\nexport type CommunitiesList = typeof CommunitiesList\nexport const PublicChatsList = 10005\nexport type PublicChatsList = typeof PublicChatsList\nexport const BlockedRelaysList = 10006\nexport type BlockedRelaysList = typeof BlockedRelaysList\nexport const SearchRelaysList = 10007\nexport type SearchRelaysList = typeof SearchRelaysList\nexport const FavoriteRelays = 10012\nexport type FavoriteRelays = typeof FavoriteRelays\nexport const InterestsList = 10015\nexport type InterestsList = typeof InterestsList\nexport const UserEmojiList = 10030\nexport type UserEmojiList = typeof UserEmojiList\nexport const DirectMessageRelaysList = 10050\nexport type DirectMessageRelaysList = typeof DirectMessageRelaysList\nexport const FileServerPreference = 10096\nexport type FileServerPreference = typeof FileServerPreference\nexport const BlossomServerList = 10063\nexport type BlossomServerList = typeof BlossomServerList\nexport const NWCWalletInfo = 13194\nexport type NWCWalletInfo = typeof NWCWalletInfo\nexport const LightningPubRPC = 21000\nexport type LightningPubRPC = typeof LightningPubRPC\nexport const ClientAuth = 22242\nexport type ClientAuth = typeof ClientAuth\nexport const NWCWalletRequest = 23194\nexport type NWCWalletRequest = typeof NWCWalletRequest\nexport const NWCWalletResponse = 23195\nexport type NWCWalletResponse = typeof NWCWalletResponse\nexport const NostrConnect = 24133\nexport type NostrConnect = typeof NostrConnect\nexport const HTTPAuth = 27235\nexport type HTTPAuth = typeof HTTPAuth\nexport const Followsets = 30000\nexport type Followsets = typeof Followsets\nexport const Genericlists = 30001\nexport type Genericlists = typeof Genericlists\nexport const Relaysets = 30002\nexport type Relaysets = typeof Relaysets\nexport const Bookmarksets = 30003\nexport type Bookmarksets = typeof Bookmarksets\nexport const Curationsets = 30004\nexport type Curationsets = typeof Curationsets\nexport const ProfileBadges = 30008\nexport type ProfileBadges = typeof ProfileBadges\nexport const BadgeDefinition = 30009\nexport type BadgeDefinition = typeof BadgeDefinition\nexport const Interestsets = 30015\nexport type Interestsets = typeof Interestsets\nexport const CreateOrUpdateStall = 30017\nexport type CreateOrUpdateStall = typeof CreateOrUpdateStall\nexport const CreateOrUpdateProduct = 30018\nexport type CreateOrUpdateProduct = typeof CreateOrUpdateProduct\nexport const LongFormArticle = 30023\nexport type LongFormArticle = typeof LongFormArticle\nexport const DraftLong = 30024\nexport type DraftLong = typeof DraftLong\nexport const Emojisets = 30030\nexport type Emojisets = typeof Emojisets\nexport const Application = 30078\nexport type Application = typeof Application\nexport const LiveEvent = 30311\nexport type LiveEvent = typeof LiveEvent\nexport const UserStatuses = 30315\nexport type UserStatuses = typeof UserStatuses\nexport const ClassifiedListing = 30402\nexport type ClassifiedListing = typeof ClassifiedListing\nexport const DraftClassifiedListing = 30403\nexport type DraftClassifiedListing = typeof DraftClassifiedListing\nexport const Date = 31922\nexport type Date = typeof Date\nexport const Time = 31923\nexport type Time = typeof Time\nexport const Calendar = 31924\nexport type Calendar = typeof Calendar\nexport const CalendarEventRSVP = 31925\nexport type CalendarEventRSVP = typeof CalendarEventRSVP\nexport const RelayReview = 31987\nexport type RelayReview = typeof RelayReview\nexport const Handlerrecommendation = 31989\nexport type Handlerrecommendation = typeof Handlerrecommendation\nexport const Handlerinformation = 31990\nexport type Handlerinformation = typeof Handlerinformation\nexport const CommunityDefinition = 34550\nexport type CommunityDefinition = typeof CommunityDefinition\nexport const GroupMetadata = 39000\nexport type GroupMetadata = typeof GroupMetadata\n", "import { Event } from './core.ts'\nimport { isAddressableKind, isReplaceableKind } from './kinds.ts'\n\nexport type Filter = {\n  ids?: string[]\n  kinds?: number[]\n  authors?: string[]\n  since?: number\n  until?: number\n  limit?: number\n  search?: string\n  [key: `#${string}`]: string[] | undefined\n}\n\nexport function matchFilter(filter: Filter, event: Event): boolean {\n  if (filter.ids && filter.ids.indexOf(event.id) === -1) {\n    return false\n  }\n  if (filter.kinds && filter.kinds.indexOf(event.kind) === -1) {\n    return false\n  }\n  if (filter.authors && filter.authors.indexOf(event.pubkey) === -1) {\n    return false\n  }\n\n  for (let f in filter) {\n    if (f[0] === '#') {\n      let tagName = f.slice(1)\n      let values = filter[`#${tagName}`]\n      if (values && !event.tags.find(([t, v]) => t === f.slice(1) && values!.indexOf(v) !== -1)) return false\n    }\n  }\n\n  if (filter.since && event.created_at < filter.since) return false\n  if (filter.until && event.created_at > filter.until) return false\n\n  return true\n}\n\nexport function matchFilters(filters: Filter[], event: Event): boolean {\n  for (let i = 0; i < filters.length; i++) {\n    if (matchFilter(filters[i], event)) {\n      return true\n    }\n  }\n  return false\n}\n\nexport function mergeFilters(...filters: Filter[]): Filter {\n  let result: Filter = {}\n  for (let i = 0; i < filters.length; i++) {\n    let filter = filters[i]\n    Object.entries(filter).forEach(([property, values]) => {\n      if (property === 'kinds' || property === 'ids' || property === 'authors' || property[0] === '#') {\n        // @ts-ignore\n        result[property] = result[property] || []\n        // @ts-ignore\n        for (let v = 0; v < values.length; v++) {\n          // @ts-ignore\n          let value = values[v]\n          // @ts-ignore\n          if (!result[property].includes(value)) result[property].push(value)\n        }\n      }\n    })\n\n    if (filter.limit && (!result.limit || filter.limit > result.limit)) result.limit = filter.limit\n    if (filter.until && (!result.until || filter.until > result.until)) result.until = filter.until\n    if (filter.since && (!result.since || filter.since < result.since)) result.since = filter.since\n  }\n\n  return result\n}\n\n/**\n * Calculate the intrinsic limit of a filter.\n * This function returns a positive integer, or `Infinity` if there is no intrinsic limit.\n */\nexport function getFilterLimit(filter: Filter): number {\n  if (filter.ids && !filter.ids.length) return 0\n  if (filter.kinds && !filter.kinds.length) return 0\n  if (filter.authors && !filter.authors.length) return 0\n\n  for (const [key, value] of Object.entries(filter)) {\n    if (key[0] === '#' && Array.isArray(value) && !value.length) return 0\n  }\n\n  return Math.min(\n    // The `limit` property creates an artificial limit.\n    Math.max(0, filter.limit ?? Infinity),\n\n    // There can only be one event per `id`.\n    filter.ids?.length ?? Infinity,\n\n    // Replaceable events are limited by the number of authors and kinds.\n    filter.authors?.length && filter.kinds?.every(kind => isReplaceableKind(kind))\n      ? filter.authors.length * filter.kinds.length\n      : Infinity,\n\n    // Parameterized replaceable events are limited by the number of authors, kinds, and \"d\" tags.\n    filter.authors?.length && filter.kinds?.every(kind => isAddressableKind(kind)) && filter['#d']?.length\n      ? filter.authors.length * filter.kinds.length * filter['#d'].length\n      : Infinity,\n  )\n}\n", "export function getHex64(json: string, field: string): string {\n  let len = field.length + 3\n  let idx = json.indexOf(`\"${field}\":`) + len\n  let s = json.slice(idx).indexOf(`\"`) + idx + 1\n  return json.slice(s, s + 64)\n}\n\nexport function getInt(json: string, field: string): number {\n  let len = field.length\n  let idx = json.indexOf(`\"${field}\":`) + len + 3\n  let sliced = json.slice(idx)\n  let end = Math.min(sliced.indexOf(','), sliced.indexOf('}'))\n  return parseInt(sliced.slice(0, end), 10)\n}\n\nexport function getSubscriptionId(json: string): string | null {\n  let idx = json.slice(0, 22).indexOf(`\"EVENT\"`)\n  if (idx === -1) return null\n\n  let pstart = json.slice(idx + 7 + 1).indexOf(`\"`)\n  if (pstart === -1) return null\n  let start = idx + 7 + 1 + pstart\n\n  let pend = json.slice(start + 1, 80).indexOf(`\"`)\n  if (pend === -1) return null\n  let end = start + 1 + pend\n\n  return json.slice(start + 1, end)\n}\n\nexport function matchEventId(json: string, id: string): boolean {\n  return id === getHex64(json, 'id')\n}\n\nexport function matchEventPubkey(json: string, pubkey: string): boolean {\n  return pubkey === getHex64(json, 'pubkey')\n}\n\nexport function matchEventKind(json: string, kind: number): boolean {\n  return kind === getInt(json, 'kind')\n}\n", "import { EventTemplate } from './core.ts'\nimport { ClientAuth } from './kinds.ts'\n\n/**\n * creates an EventTemplate for an AUTH event to be signed.\n */\nexport function makeAuthEvent(relayURL: string, challenge: string): EventTemplate {\n  return {\n    kind: ClientAuth,\n    created_at: Math.floor(Date.now() / 1000),\n    tags: [\n      ['relay', relayURL],\n      ['challenge', challenge],\n    ],\n    content: '',\n  }\n}\n", "import { verifiedSymbol, type Event, type Nostr, VerifiedEvent } from './core.ts'\n\nexport async function yieldThread() {\n  return new Promise<void>((resolve, reject) => {\n    try {\n      // Check if MessageChannel is available\n      if (typeof MessageChannel !== 'undefined') {\n        const ch = new MessageChannel()\n        const handler = () => {\n          // @ts-ignore (typescript thinks this property should be called `removeListener`, but in fact it's `removeEventListener`)\n          ch.port1.removeEventListener('message', handler)\n          resolve()\n        }\n        // @ts-ignore (typescript thinks this property should be called `addListener`, but in fact it's `addEventListener`)\n        ch.port1.addEventListener('message', handler)\n        ch.port2.postMessage(0)\n        ch.port1.start()\n      } else {\n        if (typeof setImmediate !== 'undefined') {\n          setImmediate(resolve)\n        } else if (typeof setTimeout !== 'undefined') {\n          setTimeout(resolve, 0)\n        } else {\n          // Last resort - resolve immediately\n          resolve()\n        }\n      }\n    } catch (e) {\n      console.error('during yield: ', e)\n      reject(e)\n    }\n  })\n}\n\nexport const alwaysTrue: Nostr['verifyEvent'] = (t: Event): t is VerifiedEvent => {\n  t[verifiedSymbol] = true\n  return true\n}\n", "/* global WebSocket */\n\nimport type { Event, EventTemplate, VerifiedEvent, Nostr, NostrEvent } from './core.ts'\nimport { matchFilters, type Filter } from './filter.ts'\nimport { getHex64, getSubscriptionId } from './fakejson.ts'\nimport { Queue, normalizeURL } from './utils.ts'\nimport { makeAuthEvent } from './nip42.ts'\nimport { yieldThread } from './helpers.ts'\n\ntype RelayWebSocket = WebSocket & {\n  ping?(): void\n  on?(event: 'pong', listener: () => void): any\n}\n\nexport type AbstractRelayConstructorOptions = {\n  verifyEvent: Nostr['verifyEvent']\n  websocketImplementation?: typeof WebSocket\n  enablePing?: boolean\n  enableReconnect?: boolean\n}\n\nexport class SendingOnClosedConnection extends Error {\n  constructor(message: string, relay: string) {\n    super(`Tried to send message '${message} on a closed connection to ${relay}.`)\n    this.name = 'SendingOnClosedConnection'\n  }\n}\n\nexport class AbstractRelay {\n  public readonly url: string\n  private _connected: boolean = false\n\n  public onclose: (() => void) | null = null\n  public onnotice: (msg: string) => void = msg => console.debug(`NOTICE from ${this.url}: ${msg}`)\n  public onauth: undefined | ((evt: EventTemplate) => Promise<VerifiedEvent>)\n\n  public baseEoseTimeout: number = 4400\n  public publishTimeout: number = 4400\n  public pingFrequency: number = 29000\n  public pingTimeout: number = 20000\n  public resubscribeBackoff: number[] = [10000, 10000, 10000, 20000, 20000, 30000, 60000]\n  public openSubs: Map<string, Subscription> = new Map()\n  public enablePing: boolean | undefined\n  public enableReconnect: boolean\n  private reconnectTimeoutHandle: ReturnType<typeof setTimeout> | undefined\n  private pingIntervalHandle: ReturnType<typeof setInterval> | undefined\n  private reconnectAttempts: number = 0\n  private closedIntentionally: boolean = false\n\n  private connectionPromise: Promise<void> | undefined\n  private openCountRequests = new Map<string, CountResolver>()\n  private openEventPublishes = new Map<string, EventPublishResolver>()\n  private ws: RelayWebSocket | undefined\n  private incomingMessageQueue = new Queue<string>()\n  private queueRunning = false\n  private challenge: string | undefined\n  private authPromise: Promise<string> | undefined\n  private serial: number = 0\n  private verifyEvent: Nostr['verifyEvent']\n\n  private _WebSocket: typeof WebSocket\n\n  constructor(url: string, opts: AbstractRelayConstructorOptions) {\n    this.url = normalizeURL(url)\n    this.verifyEvent = opts.verifyEvent\n    this._WebSocket = opts.websocketImplementation || WebSocket\n    this.enablePing = opts.enablePing\n    this.enableReconnect = opts.enableReconnect || false\n  }\n\n  static async connect(\n    url: string,\n    opts: AbstractRelayConstructorOptions & Parameters<AbstractRelay['connect']>[0],\n  ): Promise<AbstractRelay> {\n    const relay = new AbstractRelay(url, opts)\n    await relay.connect(opts)\n    return relay\n  }\n\n  private closeAllSubscriptions(reason: string) {\n    for (let [_, sub] of this.openSubs) {\n      sub.close(reason)\n    }\n    this.openSubs.clear()\n\n    for (let [_, ep] of this.openEventPublishes) {\n      ep.reject(new Error(reason))\n    }\n    this.openEventPublishes.clear()\n\n    for (let [_, cr] of this.openCountRequests) {\n      cr.reject(new Error(reason))\n    }\n    this.openCountRequests.clear()\n  }\n\n  public get connected(): boolean {\n    return this._connected\n  }\n\n  private async reconnect(): Promise<void> {\n    const backoff = this.resubscribeBackoff[Math.min(this.reconnectAttempts, this.resubscribeBackoff.length - 1)]\n    this.reconnectAttempts++\n\n    this.reconnectTimeoutHandle = setTimeout(async () => {\n      try {\n        await this.connect()\n      } catch (err) {\n        // this will be called again through onclose/onerror\n      }\n    }, backoff)\n  }\n\n  private handleHardClose(reason: string) {\n    if (this.pingIntervalHandle) {\n      clearInterval(this.pingIntervalHandle)\n      this.pingIntervalHandle = undefined\n    }\n\n    this._connected = false\n    this.connectionPromise = undefined\n\n    const wasIntentional = this.closedIntentionally\n    this.closedIntentionally = false // reset for next time\n\n    this.onclose?.()\n\n    if (this.enableReconnect && !wasIntentional) {\n      this.reconnect()\n    } else {\n      this.closeAllSubscriptions(reason)\n    }\n  }\n\n  public async connect(opts?: { timeout?: number; abort?: AbortSignal }): Promise<void> {\n    let connectionTimeoutHandle: ReturnType<typeof setTimeout> | undefined\n\n    if (this.connectionPromise) return this.connectionPromise\n\n    this.challenge = undefined\n    this.authPromise = undefined\n    this.connectionPromise = new Promise((resolve, reject) => {\n      if (opts?.timeout) {\n        connectionTimeoutHandle = setTimeout(() => {\n          reject('connection timed out')\n          this.connectionPromise = undefined\n          this.onclose?.()\n          this.closeAllSubscriptions('relay connection timed out')\n        }, opts.timeout)\n      }\n\n      if (opts?.abort) {\n        opts.abort.onabort = reject\n      }\n\n      try {\n        this.ws = new this._WebSocket(this.url)\n      } catch (err) {\n        clearTimeout(connectionTimeoutHandle)\n        reject(err)\n        return\n      }\n\n      this.ws.onopen = () => {\n        if (this.reconnectTimeoutHandle) {\n          clearTimeout(this.reconnectTimeoutHandle)\n          this.reconnectTimeoutHandle = undefined\n        }\n        clearTimeout(connectionTimeoutHandle)\n        this._connected = true\n\n        const isReconnection = this.reconnectAttempts > 0\n        this.reconnectAttempts = 0\n\n        // resubscribe to all open subscriptions\n        for (const sub of this.openSubs.values()) {\n          sub.eosed = false\n          if (isReconnection) {\n            for (let f = 0; f < sub.filters.length; f++) {\n              if (sub.lastEmitted) {\n                sub.filters[f].since = sub.lastEmitted + 1\n              }\n            }\n          }\n          sub.fire()\n        }\n\n        if (this.enablePing) {\n          this.pingIntervalHandle = setInterval(() => this.pingpong(), this.pingFrequency)\n        }\n        resolve()\n      }\n\n      this.ws.onerror = ev => {\n        clearTimeout(connectionTimeoutHandle)\n        reject((ev as any).message || 'websocket error')\n        this.handleHardClose('relay connection errored')\n      }\n\n      this.ws.onclose = ev => {\n        clearTimeout(connectionTimeoutHandle)\n        reject((ev as any).message || 'websocket closed')\n        this.handleHardClose('relay connection closed')\n      }\n\n      this.ws.onmessage = this._onmessage.bind(this)\n    })\n\n    return this.connectionPromise\n  }\n\n  private waitForPingPong() {\n    return new Promise(resolve => {\n      // listen for pong\n      ;(this.ws as any).once('pong', () => resolve(true))\n      // send a ping\n      this.ws!.ping!()\n    })\n  }\n\n  private waitForDummyReq() {\n    return new Promise((resolve, reject) => {\n      if (!this.connectionPromise) return reject(new Error(`no connection to ${this.url}, can't ping`))\n\n      // make a dummy request with expected empty eose reply\n      // [\"REQ\", \"_\", {\"ids\":[\"aaaa...aaaa\"], \"limit\": 0}]\n      try {\n        const sub = this.subscribe(\n          [{ ids: ['aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'], limit: 0 }],\n          {\n            label: 'forced-ping',\n            oneose: () => {\n              resolve(true)\n              sub.close()\n            },\n            onclose() {\n              // if we get a CLOSED it's because the relay is alive\n              resolve(true)\n            },\n            eoseTimeout: this.pingTimeout + 1000,\n          },\n        )\n      } catch (err) {\n        reject(err)\n      }\n    })\n  }\n\n  // nodejs requires this magic here to ensure connections are closed when internet goes off and stuff\n  // in browsers it's done automatically. see https://github.com/nbd-wtf/nostr-tools/issues/491\n  private async pingpong() {\n    // if the websocket is connected\n    if (this.ws?.readyState === 1) {\n      // wait for either a ping-pong reply or a timeout\n      const result = await Promise.any([\n        // browsers don't have ping so use a dummy req\n        this.ws && this.ws.ping && (this.ws as any).once ? this.waitForPingPong() : this.waitForDummyReq(),\n        new Promise(res => setTimeout(() => res(false), this.pingTimeout)),\n      ])\n\n      if (!result) {\n        // pingpong closing socket\n        if (this.ws?.readyState === this._WebSocket.OPEN) {\n          this.ws?.close()\n        }\n      }\n    }\n  }\n\n  private async runQueue() {\n    this.queueRunning = true\n    while (true) {\n      if (false === this.handleNext()) {\n        break\n      }\n      await yieldThread()\n    }\n    this.queueRunning = false\n  }\n\n  private handleNext(): undefined | false {\n    const json = this.incomingMessageQueue.dequeue()\n    if (!json) {\n      return false\n    }\n\n    // shortcut EVENT sub\n    const subid = getSubscriptionId(json)\n    if (subid) {\n      const so = this.openSubs.get(subid as string)\n      if (!so) {\n        // this is an EVENT message, but for a subscription we don't have, so just stop here\n        return\n      }\n\n      // this will be called only when this message is a EVENT message for a subscription we have\n      // we do this before parsing the JSON to not have to do that for duplicate events\n      //   since JSON parsing is slow\n      const id = getHex64(json, 'id')\n      const alreadyHave = so.alreadyHaveEvent?.(id)\n\n      // notify any interested client that the relay has this event\n      // (do this after alreadyHaveEvent() because the client may rely on this to answer that)\n      so.receivedEvent?.(this, id)\n\n      if (alreadyHave) {\n        // if we had already seen this event we can just stop here\n        return\n      }\n    }\n\n    try {\n      let data = JSON.parse(json)\n      // we won't do any checks against the data since all failures (i.e. invalid messages from relays)\n      // will naturally be caught by the encompassing try..catch block\n\n      switch (data[0]) {\n        case 'EVENT': {\n          const so = this.openSubs.get(data[1] as string) as Subscription\n          const event = data[2] as NostrEvent\n          if (this.verifyEvent(event) && matchFilters(so.filters, event)) {\n            so.onevent(event)\n          }\n          if (!so.lastEmitted || so.lastEmitted < event.created_at) so.lastEmitted = event.created_at\n          return\n        }\n        case 'COUNT': {\n          const id: string = data[1]\n          const payload = data[2] as { count: number }\n          const cr = this.openCountRequests.get(id) as CountResolver\n          if (cr) {\n            cr.resolve(payload.count)\n            this.openCountRequests.delete(id)\n          }\n          return\n        }\n        case 'EOSE': {\n          const so = this.openSubs.get(data[1] as string)\n          if (!so) return\n          so.receivedEose()\n          return\n        }\n        case 'OK': {\n          const id: string = data[1]\n          const ok: boolean = data[2]\n          const reason: string = data[3]\n          const ep = this.openEventPublishes.get(id) as EventPublishResolver\n          if (ep) {\n            clearTimeout(ep.timeout)\n            if (ok) ep.resolve(reason)\n            else ep.reject(new Error(reason))\n            this.openEventPublishes.delete(id)\n          }\n          return\n        }\n        case 'CLOSED': {\n          const id: string = data[1]\n          const so = this.openSubs.get(id)\n          if (!so) return\n          so.closed = true\n          so.close(data[2] as string)\n          return\n        }\n        case 'NOTICE': {\n          this.onnotice(data[1] as string)\n          return\n        }\n        case 'AUTH': {\n          this.challenge = data[1] as string\n          if (this.onauth) {\n            this.auth(this.onauth)\n          }\n          return\n        }\n        default: {\n          const so = this.openSubs.get(data[1])\n          so?.oncustom?.(data)\n          return\n        }\n      }\n    } catch (err) {\n      return\n    }\n  }\n\n  public async send(message: string) {\n    if (!this.connectionPromise) throw new SendingOnClosedConnection(message, this.url)\n\n    this.connectionPromise.then(() => {\n      this.ws?.send(message)\n    })\n  }\n\n  public async auth(signAuthEvent: (evt: EventTemplate) => Promise<VerifiedEvent>): Promise<string> {\n    const challenge = this.challenge\n    if (!challenge) throw new Error(\"can't perform auth, no challenge was received\")\n    if (this.authPromise) return this.authPromise\n\n    this.authPromise = new Promise<string>(async (resolve, reject) => {\n      try {\n        let evt = await signAuthEvent(makeAuthEvent(this.url, challenge))\n        let timeout = setTimeout(() => {\n          let ep = this.openEventPublishes.get(evt.id) as EventPublishResolver\n          if (ep) {\n            ep.reject(new Error('auth timed out'))\n            this.openEventPublishes.delete(evt.id)\n          }\n        }, this.publishTimeout)\n        this.openEventPublishes.set(evt.id, { resolve, reject, timeout })\n        this.send('[\"AUTH\",' + JSON.stringify(evt) + ']')\n      } catch (err) {\n        console.warn('subscribe auth function failed:', err)\n      }\n    })\n    return this.authPromise\n  }\n\n  public async publish(event: Event): Promise<string> {\n    const ret = new Promise<string>((resolve, reject) => {\n      const timeout = setTimeout(() => {\n        const ep = this.openEventPublishes.get(event.id) as EventPublishResolver\n        if (ep) {\n          ep.reject(new Error('publish timed out'))\n          this.openEventPublishes.delete(event.id)\n        }\n      }, this.publishTimeout)\n      this.openEventPublishes.set(event.id, { resolve, reject, timeout })\n    })\n    this.send('[\"EVENT\",' + JSON.stringify(event) + ']')\n    return ret\n  }\n\n  public async count(filters: Filter[], params: { id?: string | null }): Promise<number> {\n    this.serial++\n    const id = params?.id || 'count:' + this.serial\n    const ret = new Promise<number>((resolve, reject) => {\n      this.openCountRequests.set(id, { resolve, reject })\n    })\n    this.send('[\"COUNT\",\"' + id + '\",' + JSON.stringify(filters).substring(1))\n    return ret\n  }\n\n  public subscribe(\n    filters: Filter[],\n    params: Partial<SubscriptionParams> & { label?: string; id?: string },\n  ): Subscription {\n    const sub = this.prepareSubscription(filters, params)\n    sub.fire()\n\n    if (params.abort) {\n      params.abort.onabort = () => sub.close(String(params.abort!.reason || '<aborted>'))\n    }\n\n    return sub\n  }\n\n  public prepareSubscription(\n    filters: Filter[],\n    params: Partial<SubscriptionParams> & { label?: string; id?: string },\n  ): Subscription {\n    this.serial++\n    const id = params.id || (params.label ? params.label + ':' : 'sub:') + this.serial\n    const subscription = new Subscription(this, id, filters, params)\n    this.openSubs.set(id, subscription)\n    return subscription\n  }\n\n  public close() {\n    this.closedIntentionally = true\n    if (this.reconnectTimeoutHandle) {\n      clearTimeout(this.reconnectTimeoutHandle)\n      this.reconnectTimeoutHandle = undefined\n    }\n    if (this.pingIntervalHandle) {\n      clearInterval(this.pingIntervalHandle)\n      this.pingIntervalHandle = undefined\n    }\n    this.closeAllSubscriptions('relay connection closed by us')\n    this._connected = false\n    this.onclose?.()\n    if (this.ws?.readyState === this._WebSocket.OPEN) {\n      this.ws?.close()\n    }\n  }\n\n  // this is the function assigned to this.ws.onmessage\n  // it's exposed for testing and debugging purposes\n  public _onmessage(ev: MessageEvent<any>) {\n    this.incomingMessageQueue.enqueue(ev.data as string)\n    if (!this.queueRunning) {\n      this.runQueue()\n    }\n  }\n}\n\nexport class Subscription {\n  public readonly relay: AbstractRelay\n  public readonly id: string\n\n  public lastEmitted: number | undefined\n  public closed: boolean = false\n  public eosed: boolean = false\n  public filters: Filter[]\n  public alreadyHaveEvent: ((id: string) => boolean) | undefined\n  public receivedEvent: ((relay: AbstractRelay, id: string) => void) | undefined\n\n  public onevent: (evt: Event) => void\n  public oneose: (() => void) | undefined\n  public onclose: ((reason: string) => void) | undefined\n\n  // will get any messages that have this subscription id as their second item and are not default standard\n  public oncustom: ((msg: string[]) => void) | undefined\n\n  public eoseTimeout: number\n  private eoseTimeoutHandle: ReturnType<typeof setTimeout> | undefined\n\n  constructor(relay: AbstractRelay, id: string, filters: Filter[], params: SubscriptionParams) {\n    if (filters.length === 0) throw new Error(\"subscription can't be created with zero filters\")\n\n    this.relay = relay\n    this.filters = filters\n    this.id = id\n    this.alreadyHaveEvent = params.alreadyHaveEvent\n    this.receivedEvent = params.receivedEvent\n    this.eoseTimeout = params.eoseTimeout || relay.baseEoseTimeout\n\n    this.oneose = params.oneose\n    this.onclose = params.onclose\n    this.onevent =\n      params.onevent ||\n      (event => {\n        console.warn(\n          `onevent() callback not defined for subscription '${this.id}' in relay ${this.relay.url}. event received:`,\n          event,\n        )\n      })\n  }\n\n  public fire() {\n    this.relay.send('[\"REQ\",\"' + this.id + '\",' + JSON.stringify(this.filters).substring(1))\n\n    // only now we start counting the eoseTimeout\n    this.eoseTimeoutHandle = setTimeout(this.receivedEose.bind(this), this.eoseTimeout)\n  }\n\n  public receivedEose() {\n    if (this.eosed) return\n    clearTimeout(this.eoseTimeoutHandle)\n    this.eosed = true\n    this.oneose?.()\n  }\n\n  public close(reason: string = 'closed by caller') {\n    if (!this.closed && this.relay.connected) {\n      // if the connection was closed by the user calling .close() we will send a CLOSE message\n      // otherwise this._open will be already set to false so we will skip this\n      try {\n        this.relay.send('[\"CLOSE\",' + JSON.stringify(this.id) + ']')\n      } catch (err) {\n        if (err instanceof SendingOnClosedConnection) {\n          /* doesn't matter, it's ok */\n        } else {\n          throw err\n        }\n      }\n      this.closed = true\n    }\n    this.relay.openSubs.delete(this.id)\n    this.onclose?.(reason)\n  }\n}\n\nexport type SubscriptionParams = {\n  onevent?: (evt: Event) => void\n  oneose?: () => void\n  onclose?: (reason: string) => void\n  alreadyHaveEvent?: (id: string) => boolean\n  receivedEvent?: (relay: AbstractRelay, id: string) => void\n  eoseTimeout?: number\n  abort?: AbortSignal\n}\n\nexport type CountResolver = {\n  resolve: (count: number) => void\n  reject: (err: Error) => void\n}\n\nexport type EventPublishResolver = {\n  resolve: (reason: string) => void\n  reject: (err: Error) => void\n  timeout: ReturnType<typeof setTimeout>\n}\n"],
+  "sourcesContent": ["import type { Event } from './core.ts'\n\nexport const utf8Decoder: TextDecoder = new TextDecoder('utf-8')\nexport const utf8Encoder: TextEncoder = new TextEncoder()\n\nexport { bytesToHex, hexToBytes } from '@noble/hashes/utils.js'\n\nexport function normalizeURL(url: string): string {\n  try {\n    if (url.indexOf('://') === -1) url = 'wss://' + url\n    let p = new URL(url)\n    if (p.protocol === 'http:') p.protocol = 'ws:'\n    else if (p.protocol === 'https:') p.protocol = 'wss:'\n    p.pathname = p.pathname.replace(/\\/+/g, '/')\n    if (p.pathname.endsWith('/')) p.pathname = p.pathname.slice(0, -1)\n    if ((p.port === '80' && p.protocol === 'ws:') || (p.port === '443' && p.protocol === 'wss:')) p.port = ''\n    p.searchParams.sort()\n    p.hash = ''\n    return p.toString()\n  } catch (e) {\n    throw new Error(`Invalid URL: ${url}`)\n  }\n}\n\nexport function insertEventIntoDescendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return b.created_at - event.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function insertEventIntoAscendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return event.created_at - b.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function binarySearch<T>(arr: T[], compare: (b: T) => number): [number, boolean] {\n  let start = 0\n  let end = arr.length - 1\n\n  while (start <= end) {\n    const mid = Math.floor((start + end) / 2)\n    const cmp = compare(arr[mid])\n\n    if (cmp === 0) {\n      return [mid, true]\n    }\n\n    if (cmp < 0) {\n      end = mid - 1\n    } else {\n      start = mid + 1\n    }\n  }\n\n  return [start, false]\n}\n\nexport class QueueNode<V> {\n  public value: V\n  public next: QueueNode<V> | null = null\n  public prev: QueueNode<V> | null = null\n\n  constructor(message: V) {\n    this.value = message\n  }\n}\n\nexport class Queue<V> {\n  public first: QueueNode<V> | null\n  public last: QueueNode<V> | null\n\n  constructor() {\n    this.first = null\n    this.last = null\n  }\n\n  enqueue(value: V): boolean {\n    const newNode = new QueueNode(value)\n    if (!this.last) {\n      // list is empty\n      this.first = newNode\n      this.last = newNode\n    } else if (this.last === this.first) {\n      // list has a single element\n      this.last = newNode\n      this.last.prev = this.first\n      this.first.next = newNode\n    } else {\n      // list has elements, add as last\n      newNode.prev = this.last\n      this.last.next = newNode\n      this.last = newNode\n    }\n    return true\n  }\n\n  dequeue(): V | null {\n    if (!this.first) return null\n\n    if (this.first === this.last) {\n      const target = this.first\n      this.first = null\n      this.last = null\n      return target.value\n    }\n\n    const target = this.first\n    this.first = target.next\n    if (this.first) {\n      this.first.prev = null // fix: clean up prev pointer\n    }\n\n    return target.value\n  }\n}\n", "import { NostrEvent, validateEvent } from './pure.ts'\n\n/** Events are **regular**, which means they're all expected to be stored by relays. */\nexport function isRegularKind(kind: number): boolean {\n  return kind < 10000 && kind !== 0 && kind !== 3\n}\n\n/** Events are **replaceable**, which means that, for each combination of `pubkey` and `kind`, only the latest event is expected to (SHOULD) be stored by relays, older versions are expected to be discarded. */\nexport function isReplaceableKind(kind: number): boolean {\n  return kind === 0 || kind === 3 || (10000 <= kind && kind < 20000)\n}\n\n/** Events are **ephemeral**, which means they are not expected to be stored by relays. */\nexport function isEphemeralKind(kind: number): boolean {\n  return 20000 <= kind && kind < 30000\n}\n\n/** Events are **addressable**, which means that, for each combination of `pubkey`, `kind` and the `d` tag, only the latest event is expected to be stored by relays, older versions are expected to be discarded. */\nexport function isAddressableKind(kind: number): boolean {\n  return 30000 <= kind && kind < 40000\n}\n\n/** Classification of the event kind. */\nexport type KindClassification = 'regular' | 'replaceable' | 'ephemeral' | 'parameterized' | 'unknown'\n\n/** Determine the classification of this kind of event if known, or `unknown`. */\nexport function classifyKind(kind: number): KindClassification {\n  if (isRegularKind(kind)) return 'regular'\n  if (isReplaceableKind(kind)) return 'replaceable'\n  if (isEphemeralKind(kind)) return 'ephemeral'\n  if (isAddressableKind(kind)) return 'parameterized'\n  return 'unknown'\n}\n\nexport function isKind<T extends number>(event: unknown, kind: T | Array<T>): event is NostrEvent & { kind: T } {\n  const kindAsArray: number[] = kind instanceof Array ? kind : [kind]\n  return (validateEvent(event) && kindAsArray.includes(event.kind)) || false\n}\n\nexport const Metadata = 0\nexport type Metadata = typeof Metadata\nexport const ShortTextNote = 1\nexport type ShortTextNote = typeof ShortTextNote\nexport const RecommendRelay = 2\nexport type RecommendRelay = typeof RecommendRelay\nexport const Contacts = 3\nexport type Contacts = typeof Contacts\nexport const EncryptedDirectMessage = 4\nexport type EncryptedDirectMessage = typeof EncryptedDirectMessage\nexport const EventDeletion = 5\nexport type EventDeletion = typeof EventDeletion\nexport const Repost = 6\nexport type Repost = typeof Repost\nexport const Reaction = 7\nexport type Reaction = typeof Reaction\nexport const BadgeAward = 8\nexport type BadgeAward = typeof BadgeAward\nexport const ChatMessage = 9\nexport type ChatMessage = typeof ChatMessage\nexport const ForumThread = 11\nexport type ForumThread = typeof ForumThread\nexport const Seal = 13\nexport type Seal = typeof Seal\nexport const PrivateDirectMessage = 14\nexport type PrivateDirectMessage = typeof PrivateDirectMessage\nexport const FileMessage = 15\nexport type FileMessage = typeof FileMessage\nexport const GenericRepost = 16\nexport type GenericRepost = typeof GenericRepost\nexport const Photo = 20\nexport type Photo = typeof Photo\nexport const NormalVideo = 21\nexport type NormalVideo = typeof NormalVideo\nexport const ShortVideo = 22\nexport type ShortVideo = typeof ShortVideo\nexport const ChannelCreation = 40\nexport type ChannelCreation = typeof ChannelCreation\nexport const ChannelMetadata = 41\nexport type ChannelMetadata = typeof ChannelMetadata\nexport const ChannelMessage = 42\nexport type ChannelMessage = typeof ChannelMessage\nexport const ChannelHideMessage = 43\nexport type ChannelHideMessage = typeof ChannelHideMessage\nexport const ChannelMuteUser = 44\nexport type ChannelMuteUser = typeof ChannelMuteUser\nexport const OpenTimestamps = 1040\nexport type OpenTimestamps = typeof OpenTimestamps\nexport const GiftWrap = 1059\nexport type GiftWrap = typeof GiftWrap\nexport const Poll = 1068\nexport type Poll = typeof Poll\nexport const FileMetadata = 1063\nexport type FileMetadata = typeof FileMetadata\nexport const Comment = 1111\nexport type Comment = typeof Comment\nexport const LiveChatMessage = 1311\nexport type LiveChatMessage = typeof LiveChatMessage\nexport const Voice = 1222\nexport type Voice = typeof Voice\nexport const VoiceComment = 1244\nexport type VoiceComment = typeof VoiceComment\nexport const ProblemTracker = 1971\nexport type ProblemTracker = typeof ProblemTracker\nexport const Report = 1984\nexport type Report = typeof Report\nexport const Reporting = 1984\nexport type Reporting = typeof Reporting\nexport const Label = 1985\nexport type Label = typeof Label\nexport const CommunityPostApproval = 4550\nexport type CommunityPostApproval = typeof CommunityPostApproval\nexport const JobRequest = 5999\nexport type JobRequest = typeof JobRequest\nexport const JobResult = 6999\nexport type JobResult = typeof JobResult\nexport const JobFeedback = 7000\nexport type JobFeedback = typeof JobFeedback\nexport const ZapGoal = 9041\nexport type ZapGoal = typeof ZapGoal\nexport const ZapRequest = 9734\nexport type ZapRequest = typeof ZapRequest\nexport const Zap = 9735\nexport type Zap = typeof Zap\nexport const Highlights = 9802\nexport type Highlights = typeof Highlights\nexport const PollResponse = 1018\nexport type PollResponse = typeof PollResponse\nexport const Mutelist = 10000\nexport type Mutelist = typeof Mutelist\nexport const Pinlist = 10001\nexport type Pinlist = typeof Pinlist\nexport const RelayList = 10002\nexport type RelayList = typeof RelayList\nexport const BookmarkList = 10003\nexport type BookmarkList = typeof BookmarkList\nexport const CommunitiesList = 10004\nexport type CommunitiesList = typeof CommunitiesList\nexport const PublicChatsList = 10005\nexport type PublicChatsList = typeof PublicChatsList\nexport const BlockedRelaysList = 10006\nexport type BlockedRelaysList = typeof BlockedRelaysList\nexport const SearchRelaysList = 10007\nexport type SearchRelaysList = typeof SearchRelaysList\nexport const FavoriteRelays = 10012\nexport type FavoriteRelays = typeof FavoriteRelays\nexport const InterestsList = 10015\nexport type InterestsList = typeof InterestsList\nexport const UserEmojiList = 10030\nexport type UserEmojiList = typeof UserEmojiList\nexport const DirectMessageRelaysList = 10050\nexport type DirectMessageRelaysList = typeof DirectMessageRelaysList\nexport const FileServerPreference = 10096\nexport type FileServerPreference = typeof FileServerPreference\nexport const BlossomServerList = 10063\nexport type BlossomServerList = typeof BlossomServerList\nexport const NWCWalletInfo = 13194\nexport type NWCWalletInfo = typeof NWCWalletInfo\nexport const LightningPubRPC = 21000\nexport type LightningPubRPC = typeof LightningPubRPC\nexport const ClientAuth = 22242\nexport type ClientAuth = typeof ClientAuth\nexport const NWCWalletRequest = 23194\nexport type NWCWalletRequest = typeof NWCWalletRequest\nexport const NWCWalletResponse = 23195\nexport type NWCWalletResponse = typeof NWCWalletResponse\nexport const NostrConnect = 24133\nexport type NostrConnect = typeof NostrConnect\nexport const HTTPAuth = 27235\nexport type HTTPAuth = typeof HTTPAuth\nexport const Followsets = 30000\nexport type Followsets = typeof Followsets\nexport const Genericlists = 30001\nexport type Genericlists = typeof Genericlists\nexport const Relaysets = 30002\nexport type Relaysets = typeof Relaysets\nexport const Bookmarksets = 30003\nexport type Bookmarksets = typeof Bookmarksets\nexport const Curationsets = 30004\nexport type Curationsets = typeof Curationsets\nexport const ProfileBadges = 30008\nexport type ProfileBadges = typeof ProfileBadges\nexport const BadgeDefinition = 30009\nexport type BadgeDefinition = typeof BadgeDefinition\nexport const Interestsets = 30015\nexport type Interestsets = typeof Interestsets\nexport const CreateOrUpdateStall = 30017\nexport type CreateOrUpdateStall = typeof CreateOrUpdateStall\nexport const CreateOrUpdateProduct = 30018\nexport type CreateOrUpdateProduct = typeof CreateOrUpdateProduct\nexport const LongFormArticle = 30023\nexport type LongFormArticle = typeof LongFormArticle\nexport const DraftLong = 30024\nexport type DraftLong = typeof DraftLong\nexport const Emojisets = 30030\nexport type Emojisets = typeof Emojisets\nexport const Application = 30078\nexport type Application = typeof Application\nexport const LiveEvent = 30311\nexport type LiveEvent = typeof LiveEvent\nexport const UserStatuses = 30315\nexport type UserStatuses = typeof UserStatuses\nexport const ClassifiedListing = 30402\nexport type ClassifiedListing = typeof ClassifiedListing\nexport const DraftClassifiedListing = 30403\nexport type DraftClassifiedListing = typeof DraftClassifiedListing\nexport const Date = 31922\nexport type Date = typeof Date\nexport const Time = 31923\nexport type Time = typeof Time\nexport const Calendar = 31924\nexport type Calendar = typeof Calendar\nexport const CalendarEventRSVP = 31925\nexport type CalendarEventRSVP = typeof CalendarEventRSVP\nexport const RelayReview = 31987\nexport type RelayReview = typeof RelayReview\nexport const Handlerrecommendation = 31989\nexport type Handlerrecommendation = typeof Handlerrecommendation\nexport const Handlerinformation = 31990\nexport type Handlerinformation = typeof Handlerinformation\nexport const CommunityDefinition = 34550\nexport type CommunityDefinition = typeof CommunityDefinition\nexport const GroupMetadata = 39000\nexport type GroupMetadata = typeof GroupMetadata\n", "import { Event } from './core.ts'\nimport { isAddressableKind, isReplaceableKind } from './kinds.ts'\n\nexport type Filter = {\n  ids?: string[]\n  kinds?: number[]\n  authors?: string[]\n  since?: number\n  until?: number\n  limit?: number\n  search?: string\n  [key: `#${string}`]: string[] | undefined\n}\n\nexport function matchFilter(filter: Filter, event: Event): boolean {\n  if (filter.ids && filter.ids.indexOf(event.id) === -1) {\n    return false\n  }\n  if (filter.kinds && filter.kinds.indexOf(event.kind) === -1) {\n    return false\n  }\n  if (filter.authors && filter.authors.indexOf(event.pubkey) === -1) {\n    return false\n  }\n\n  for (let f in filter) {\n    if (f[0] === '#') {\n      let tagName = f.slice(1)\n      let values = filter[`#${tagName}`]\n      if (values && !event.tags.find(([t, v]) => t === f.slice(1) && values!.indexOf(v) !== -1)) return false\n    }\n  }\n\n  if (filter.since && event.created_at < filter.since) return false\n  if (filter.until && event.created_at > filter.until) return false\n\n  return true\n}\n\nexport function matchFilters(filters: Filter[], event: Event): boolean {\n  for (let i = 0; i < filters.length; i++) {\n    if (matchFilter(filters[i], event)) {\n      return true\n    }\n  }\n  return false\n}\n\nexport function mergeFilters(...filters: Filter[]): Filter {\n  let result: Filter = {}\n  for (let i = 0; i < filters.length; i++) {\n    let filter = filters[i]\n    Object.entries(filter).forEach(([property, values]) => {\n      if (property === 'kinds' || property === 'ids' || property === 'authors' || property[0] === '#') {\n        // @ts-ignore\n        result[property] = result[property] || []\n        // @ts-ignore\n        for (let v = 0; v < values.length; v++) {\n          // @ts-ignore\n          let value = values[v]\n          // @ts-ignore\n          if (!result[property].includes(value)) result[property].push(value)\n        }\n      }\n    })\n\n    if (filter.limit && (!result.limit || filter.limit > result.limit)) result.limit = filter.limit\n    if (filter.until && (!result.until || filter.until > result.until)) result.until = filter.until\n    if (filter.since && (!result.since || filter.since < result.since)) result.since = filter.since\n  }\n\n  return result\n}\n\n/**\n * Calculate the intrinsic limit of a filter.\n * This function returns a positive integer, or `Infinity` if there is no intrinsic limit.\n */\nexport function getFilterLimit(filter: Filter): number {\n  if (filter.ids && !filter.ids.length) return 0\n  if (filter.kinds && !filter.kinds.length) return 0\n  if (filter.authors && !filter.authors.length) return 0\n\n  for (const [key, value] of Object.entries(filter)) {\n    if (key[0] === '#' && Array.isArray(value) && !value.length) return 0\n  }\n\n  return Math.min(\n    // The `limit` property creates an artificial limit.\n    Math.max(0, filter.limit ?? Infinity),\n\n    // There can only be one event per `id`.\n    filter.ids?.length ?? Infinity,\n\n    // Replaceable events are limited by the number of authors and kinds.\n    filter.authors?.length && filter.kinds?.every(kind => isReplaceableKind(kind))\n      ? filter.authors.length * filter.kinds.length\n      : Infinity,\n\n    // Parameterized replaceable events are limited by the number of authors, kinds, and \"d\" tags.\n    filter.authors?.length && filter.kinds?.every(kind => isAddressableKind(kind)) && filter['#d']?.length\n      ? filter.authors.length * filter.kinds.length * filter['#d'].length\n      : Infinity,\n  )\n}\n", "export function getHex64(json: string, field: string): string {\n  let len = field.length + 3\n  let idx = json.indexOf(`\"${field}\":`) + len\n  let s = json.slice(idx).indexOf(`\"`) + idx + 1\n  return json.slice(s, s + 64)\n}\n\nexport function getInt(json: string, field: string): number {\n  let len = field.length\n  let idx = json.indexOf(`\"${field}\":`) + len + 3\n  let sliced = json.slice(idx)\n  let end = Math.min(sliced.indexOf(','), sliced.indexOf('}'))\n  return parseInt(sliced.slice(0, end), 10)\n}\n\nexport function getSubscriptionId(json: string): string | null {\n  let idx = json.slice(0, 22).indexOf(`\"EVENT\"`)\n  if (idx === -1) return null\n\n  let pstart = json.slice(idx + 7 + 1).indexOf(`\"`)\n  if (pstart === -1) return null\n  let start = idx + 7 + 1 + pstart\n\n  let pend = json.slice(start + 1, 80).indexOf(`\"`)\n  if (pend === -1) return null\n  let end = start + 1 + pend\n\n  return json.slice(start + 1, end)\n}\n\nexport function matchEventId(json: string, id: string): boolean {\n  return id === getHex64(json, 'id')\n}\n\nexport function matchEventPubkey(json: string, pubkey: string): boolean {\n  return pubkey === getHex64(json, 'pubkey')\n}\n\nexport function matchEventKind(json: string, kind: number): boolean {\n  return kind === getInt(json, 'kind')\n}\n", "import { EventTemplate } from './core.ts'\nimport { ClientAuth } from './kinds.ts'\n\n/**\n * creates an EventTemplate for an AUTH event to be signed.\n */\nexport function makeAuthEvent(relayURL: string, challenge: string): EventTemplate {\n  return {\n    kind: ClientAuth,\n    created_at: Math.floor(Date.now() / 1000),\n    tags: [\n      ['relay', relayURL],\n      ['challenge', challenge],\n    ],\n    content: '',\n  }\n}\n", "import { verifiedSymbol, type Event, type Nostr, VerifiedEvent } from './core.ts'\n\nexport async function yieldThread() {\n  return new Promise<void>((resolve, reject) => {\n    try {\n      // Check if MessageChannel is available\n      if (typeof MessageChannel !== 'undefined') {\n        const ch = new MessageChannel()\n        const handler = () => {\n          // @ts-ignore (typescript thinks this property should be called `removeListener`, but in fact it's `removeEventListener`)\n          ch.port1.removeEventListener('message', handler)\n          resolve()\n        }\n        // @ts-ignore (typescript thinks this property should be called `addListener`, but in fact it's `addEventListener`)\n        ch.port1.addEventListener('message', handler)\n        ch.port2.postMessage(0)\n        ch.port1.start()\n      } else {\n        if (typeof setImmediate !== 'undefined') {\n          setImmediate(resolve)\n        } else if (typeof setTimeout !== 'undefined') {\n          setTimeout(resolve, 0)\n        } else {\n          // Last resort - resolve immediately\n          resolve()\n        }\n      }\n    } catch (e) {\n      console.error('during yield: ', e)\n      reject(e)\n    }\n  })\n}\n\nexport const alwaysTrue: Nostr['verifyEvent'] = (t: Event): t is VerifiedEvent => {\n  t[verifiedSymbol] = true\n  return true\n}\n", "/* global WebSocket */\n\nimport type { Event, EventTemplate, VerifiedEvent, Nostr, NostrEvent } from './core.ts'\nimport { matchFilters, type Filter } from './filter.ts'\nimport { getHex64, getSubscriptionId } from './fakejson.ts'\nimport { Queue, normalizeURL } from './utils.ts'\nimport { makeAuthEvent } from './nip42.ts'\nimport { yieldThread } from './helpers.ts'\n\ntype RelayWebSocket = WebSocket & {\n  ping?(): void\n  on?(event: 'pong', listener: () => void): any\n}\n\nexport type AbstractRelayConstructorOptions = {\n  verifyEvent: Nostr['verifyEvent']\n  websocketImplementation?: typeof WebSocket\n  enablePing?: boolean\n  enableReconnect?: boolean\n}\n\nexport class SendingOnClosedConnection extends Error {\n  constructor(message: string, relay: string) {\n    super(`Tried to send message '${message} on a closed connection to ${relay}.`)\n    this.name = 'SendingOnClosedConnection'\n  }\n}\n\nexport class AbstractRelay {\n  public readonly url: string\n  private _connected: boolean = false\n\n  public onclose: (() => void) | null = null\n  public onnotice: (msg: string) => void = msg => console.debug(`NOTICE from ${this.url}: ${msg}`)\n  public onauth: undefined | ((evt: EventTemplate) => Promise<VerifiedEvent>)\n\n  public baseEoseTimeout: number = 4400\n  public publishTimeout: number = 4400\n  public pingFrequency: number = 29000\n  public pingTimeout: number = 20000\n  public resubscribeBackoff: number[] = [10000, 10000, 10000, 20000, 20000, 30000, 60000]\n  public openSubs: Map<string, Subscription> = new Map()\n  public enablePing: boolean | undefined\n  public enableReconnect: boolean\n  private reconnectTimeoutHandle: ReturnType<typeof setTimeout> | undefined\n  private pingIntervalHandle: ReturnType<typeof setInterval> | undefined\n  private reconnectAttempts: number = 0\n  private closedIntentionally: boolean = false\n\n  private connectionPromise: Promise<void> | undefined\n  private openCountRequests = new Map<string, CountResolver>()\n  private openEventPublishes = new Map<string, EventPublishResolver>()\n  private ws: RelayWebSocket | undefined\n  private incomingMessageQueue = new Queue<string>()\n  private queueRunning = false\n  private challenge: string | undefined\n  private authPromise: Promise<string> | undefined\n  private serial: number = 0\n  private verifyEvent: Nostr['verifyEvent']\n\n  private _WebSocket: typeof WebSocket\n\n  constructor(url: string, opts: AbstractRelayConstructorOptions) {\n    this.url = normalizeURL(url)\n    this.verifyEvent = opts.verifyEvent\n    this._WebSocket = opts.websocketImplementation || WebSocket\n    this.enablePing = opts.enablePing\n    this.enableReconnect = opts.enableReconnect || false\n  }\n\n  static async connect(\n    url: string,\n    opts: AbstractRelayConstructorOptions & Parameters<AbstractRelay['connect']>[0],\n  ): Promise<AbstractRelay> {\n    const relay = new AbstractRelay(url, opts)\n    await relay.connect(opts)\n    return relay\n  }\n\n  private closeAllSubscriptions(reason: string) {\n    for (let [_, sub] of this.openSubs) {\n      sub.close(reason)\n    }\n    this.openSubs.clear()\n\n    for (let [_, ep] of this.openEventPublishes) {\n      ep.reject(new Error(reason))\n    }\n    this.openEventPublishes.clear()\n\n    for (let [_, cr] of this.openCountRequests) {\n      cr.reject(new Error(reason))\n    }\n    this.openCountRequests.clear()\n  }\n\n  public get connected(): boolean {\n    return this._connected\n  }\n\n  private async reconnect(): Promise<void> {\n    const backoff = this.resubscribeBackoff[Math.min(this.reconnectAttempts, this.resubscribeBackoff.length - 1)]\n    this.reconnectAttempts++\n\n    this.reconnectTimeoutHandle = setTimeout(async () => {\n      try {\n        await this.connect()\n      } catch (err) {\n        // this will be called again through onclose/onerror\n      }\n    }, backoff)\n  }\n\n  private handleHardClose(reason: string) {\n    if (this.pingIntervalHandle) {\n      clearInterval(this.pingIntervalHandle)\n      this.pingIntervalHandle = undefined\n    }\n\n    this._connected = false\n    this.connectionPromise = undefined\n\n    const wasIntentional = this.closedIntentionally\n    this.closedIntentionally = false // reset for next time\n\n    this.onclose?.()\n\n    if (this.enableReconnect && !wasIntentional) {\n      this.reconnect()\n    } else {\n      this.closeAllSubscriptions(reason)\n    }\n  }\n\n  public async connect(opts?: { timeout?: number; abort?: AbortSignal }): Promise<void> {\n    let connectionTimeoutHandle: ReturnType<typeof setTimeout> | undefined\n\n    if (this.connectionPromise) return this.connectionPromise\n\n    this.challenge = undefined\n    this.authPromise = undefined\n    this.connectionPromise = new Promise((resolve, reject) => {\n      if (opts?.timeout) {\n        connectionTimeoutHandle = setTimeout(() => {\n          reject('connection timed out')\n          this.connectionPromise = undefined\n          this.onclose?.()\n          this.closeAllSubscriptions('relay connection timed out')\n        }, opts.timeout)\n      }\n\n      if (opts?.abort) {\n        opts.abort.onabort = reject\n      }\n\n      try {\n        this.ws = new this._WebSocket(this.url)\n      } catch (err) {\n        clearTimeout(connectionTimeoutHandle)\n        reject(err)\n        return\n      }\n\n      this.ws.onopen = () => {\n        if (this.reconnectTimeoutHandle) {\n          clearTimeout(this.reconnectTimeoutHandle)\n          this.reconnectTimeoutHandle = undefined\n        }\n        clearTimeout(connectionTimeoutHandle)\n        this._connected = true\n\n        const isReconnection = this.reconnectAttempts > 0\n        this.reconnectAttempts = 0\n\n        // resubscribe to all open subscriptions\n        for (const sub of this.openSubs.values()) {\n          sub.eosed = false\n          if (isReconnection) {\n            for (let f = 0; f < sub.filters.length; f++) {\n              if (sub.lastEmitted) {\n                sub.filters[f].since = sub.lastEmitted + 1\n              }\n            }\n          }\n          sub.fire()\n        }\n\n        if (this.enablePing) {\n          this.pingIntervalHandle = setInterval(() => this.pingpong(), this.pingFrequency)\n        }\n        resolve()\n      }\n\n      this.ws.onerror = ev => {\n        clearTimeout(connectionTimeoutHandle)\n        reject((ev as any).message || 'websocket error')\n        this.handleHardClose('relay connection errored')\n      }\n\n      this.ws.onclose = ev => {\n        clearTimeout(connectionTimeoutHandle)\n        reject((ev as any).message || 'websocket closed')\n        this.handleHardClose('relay connection closed')\n      }\n\n      this.ws.onmessage = this._onmessage.bind(this)\n    })\n\n    return this.connectionPromise\n  }\n\n  private waitForPingPong() {\n    return new Promise(resolve => {\n      // listen for pong\n      ;(this.ws as any).once('pong', () => resolve(true))\n      // send a ping\n      this.ws!.ping!()\n    })\n  }\n\n  private waitForDummyReq() {\n    return new Promise((resolve, reject) => {\n      if (!this.connectionPromise) return reject(new Error(`no connection to ${this.url}, can't ping`))\n\n      // make a dummy request with expected empty eose reply\n      // [\"REQ\", \"_\", {\"ids\":[\"aaaa...aaaa\"], \"limit\": 0}]\n      try {\n        const sub = this.subscribe(\n          [{ ids: ['aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'], limit: 0 }],\n          {\n            label: 'forced-ping',\n            oneose: () => {\n              resolve(true)\n              sub.close()\n            },\n            onclose() {\n              // if we get a CLOSED it's because the relay is alive\n              resolve(true)\n            },\n            eoseTimeout: this.pingTimeout + 1000,\n          },\n        )\n      } catch (err) {\n        reject(err)\n      }\n    })\n  }\n\n  // nodejs requires this magic here to ensure connections are closed when internet goes off and stuff\n  // in browsers it's done automatically. see https://github.com/nbd-wtf/nostr-tools/issues/491\n  private async pingpong() {\n    // if the websocket is connected\n    if (this.ws?.readyState === 1) {\n      // wait for either a ping-pong reply or a timeout\n      const result = await Promise.any([\n        // browsers don't have ping so use a dummy req\n        this.ws && this.ws.ping && (this.ws as any).once ? this.waitForPingPong() : this.waitForDummyReq(),\n        new Promise(res => setTimeout(() => res(false), this.pingTimeout)),\n      ])\n\n      if (!result) {\n        // pingpong closing socket\n        if (this.ws?.readyState === this._WebSocket.OPEN) {\n          this.ws?.close()\n        }\n      }\n    }\n  }\n\n  private async runQueue() {\n    this.queueRunning = true\n    while (true) {\n      if (false === this.handleNext()) {\n        break\n      }\n      await yieldThread()\n    }\n    this.queueRunning = false\n  }\n\n  private handleNext(): undefined | false {\n    const json = this.incomingMessageQueue.dequeue()\n    if (!json) {\n      return false\n    }\n\n    // shortcut EVENT sub\n    const subid = getSubscriptionId(json)\n    if (subid) {\n      const so = this.openSubs.get(subid as string)\n      if (!so) {\n        // this is an EVENT message, but for a subscription we don't have, so just stop here\n        return\n      }\n\n      // this will be called only when this message is a EVENT message for a subscription we have\n      // we do this before parsing the JSON to not have to do that for duplicate events\n      //   since JSON parsing is slow\n      const id = getHex64(json, 'id')\n      const alreadyHave = so.alreadyHaveEvent?.(id)\n\n      // notify any interested client that the relay has this event\n      // (do this after alreadyHaveEvent() because the client may rely on this to answer that)\n      so.receivedEvent?.(this, id)\n\n      if (alreadyHave) {\n        // if we had already seen this event we can just stop here\n        return\n      }\n    }\n\n    try {\n      let data = JSON.parse(json)\n      // we won't do any checks against the data since all failures (i.e. invalid messages from relays)\n      // will naturally be caught by the encompassing try..catch block\n\n      switch (data[0]) {\n        case 'EVENT': {\n          const so = this.openSubs.get(data[1] as string) as Subscription\n          const event = data[2] as NostrEvent\n          if (this.verifyEvent(event) && matchFilters(so.filters, event)) {\n            so.onevent(event)\n          }\n          if (!so.lastEmitted || so.lastEmitted < event.created_at) so.lastEmitted = event.created_at\n          return\n        }\n        case 'COUNT': {\n          const id: string = data[1]\n          const payload = data[2] as { count: number }\n          const cr = this.openCountRequests.get(id) as CountResolver\n          if (cr) {\n            cr.resolve(payload.count)\n            this.openCountRequests.delete(id)\n          }\n          return\n        }\n        case 'EOSE': {\n          const so = this.openSubs.get(data[1] as string)\n          if (!so) return\n          so.receivedEose()\n          return\n        }\n        case 'OK': {\n          const id: string = data[1]\n          const ok: boolean = data[2]\n          const reason: string = data[3]\n          const ep = this.openEventPublishes.get(id) as EventPublishResolver\n          if (ep) {\n            clearTimeout(ep.timeout)\n            if (ok) ep.resolve(reason)\n            else ep.reject(new Error(reason))\n            this.openEventPublishes.delete(id)\n          }\n          return\n        }\n        case 'CLOSED': {\n          const id: string = data[1]\n          const so = this.openSubs.get(id)\n          if (!so) return\n          so.closed = true\n          so.close(data[2] as string)\n          return\n        }\n        case 'NOTICE': {\n          this.onnotice(data[1] as string)\n          return\n        }\n        case 'AUTH': {\n          this.challenge = data[1] as string\n          if (this.onauth) {\n            this.auth(this.onauth)\n          }\n          return\n        }\n        default: {\n          const so = this.openSubs.get(data[1])\n          so?.oncustom?.(data)\n          return\n        }\n      }\n    } catch (err) {\n      return\n    }\n  }\n\n  public async send(message: string) {\n    if (!this.connectionPromise) throw new SendingOnClosedConnection(message, this.url)\n\n    this.connectionPromise.then(() => {\n      this.ws?.send(message)\n    })\n  }\n\n  public async auth(signAuthEvent: (evt: EventTemplate) => Promise<VerifiedEvent>): Promise<string> {\n    const challenge = this.challenge\n    if (!challenge) throw new Error(\"can't perform auth, no challenge was received\")\n    if (this.authPromise) return this.authPromise\n\n    this.authPromise = new Promise<string>(async (resolve, reject) => {\n      try {\n        let evt = await signAuthEvent(makeAuthEvent(this.url, challenge))\n        let timeout = setTimeout(() => {\n          let ep = this.openEventPublishes.get(evt.id) as EventPublishResolver\n          if (ep) {\n            ep.reject(new Error('auth timed out'))\n            this.openEventPublishes.delete(evt.id)\n          }\n        }, this.publishTimeout)\n        this.openEventPublishes.set(evt.id, { resolve, reject, timeout })\n        this.send('[\"AUTH\",' + JSON.stringify(evt) + ']')\n      } catch (err) {\n        console.warn('subscribe auth function failed:', err)\n      }\n    })\n    return this.authPromise\n  }\n\n  public async publish(event: Event): Promise<string> {\n    const ret = new Promise<string>((resolve, reject) => {\n      const timeout = setTimeout(() => {\n        const ep = this.openEventPublishes.get(event.id) as EventPublishResolver\n        if (ep) {\n          ep.reject(new Error('publish timed out'))\n          this.openEventPublishes.delete(event.id)\n        }\n      }, this.publishTimeout)\n      this.openEventPublishes.set(event.id, { resolve, reject, timeout })\n    })\n    this.send('[\"EVENT\",' + JSON.stringify(event) + ']')\n    return ret\n  }\n\n  public async count(filters: Filter[], params: { id?: string | null }): Promise<number> {\n    this.serial++\n    const id = params?.id || 'count:' + this.serial\n    const ret = new Promise<number>((resolve, reject) => {\n      this.openCountRequests.set(id, { resolve, reject })\n    })\n    this.send('[\"COUNT\",\"' + id + '\",' + JSON.stringify(filters).substring(1))\n    return ret\n  }\n\n  public subscribe(\n    filters: Filter[],\n    params: Partial<SubscriptionParams> & { label?: string; id?: string },\n  ): Subscription {\n    const sub = this.prepareSubscription(filters, params)\n    sub.fire()\n\n    if (params.abort) {\n      params.abort.onabort = () => sub.close(String(params.abort!.reason || '<aborted>'))\n    }\n\n    return sub\n  }\n\n  public prepareSubscription(\n    filters: Filter[],\n    params: Partial<SubscriptionParams> & { label?: string; id?: string },\n  ): Subscription {\n    this.serial++\n    const id = params.id || (params.label ? params.label + ':' : 'sub:') + this.serial\n    const subscription = new Subscription(this, id, filters, params)\n    this.openSubs.set(id, subscription)\n    return subscription\n  }\n\n  public close() {\n    this.closedIntentionally = true\n    if (this.reconnectTimeoutHandle) {\n      clearTimeout(this.reconnectTimeoutHandle)\n      this.reconnectTimeoutHandle = undefined\n    }\n    if (this.pingIntervalHandle) {\n      clearInterval(this.pingIntervalHandle)\n      this.pingIntervalHandle = undefined\n    }\n    this.closeAllSubscriptions('relay connection closed by us')\n    this._connected = false\n    this.onclose?.()\n    if (this.ws?.readyState === this._WebSocket.OPEN) {\n      this.ws?.close()\n    }\n  }\n\n  // this is the function assigned to this.ws.onmessage\n  // it's exposed for testing and debugging purposes\n  public _onmessage(ev: MessageEvent<any>) {\n    this.incomingMessageQueue.enqueue(ev.data as string)\n    if (!this.queueRunning) {\n      this.runQueue()\n    }\n  }\n}\n\nexport class Subscription {\n  public readonly relay: AbstractRelay\n  public readonly id: string\n\n  public lastEmitted: number | undefined\n  public closed: boolean = false\n  public eosed: boolean = false\n  public filters: Filter[]\n  public alreadyHaveEvent: ((id: string) => boolean) | undefined\n  public receivedEvent: ((relay: AbstractRelay, id: string) => void) | undefined\n\n  public onevent: (evt: Event) => void\n  public oneose: (() => void) | undefined\n  public onclose: ((reason: string) => void) | undefined\n\n  // will get any messages that have this subscription id as their second item and are not default standard\n  public oncustom: ((msg: string[]) => void) | undefined\n\n  public eoseTimeout: number\n  private eoseTimeoutHandle: ReturnType<typeof setTimeout> | undefined\n\n  constructor(relay: AbstractRelay, id: string, filters: Filter[], params: SubscriptionParams) {\n    if (filters.length === 0) throw new Error(\"subscription can't be created with zero filters\")\n\n    this.relay = relay\n    this.filters = filters\n    this.id = id\n    this.alreadyHaveEvent = params.alreadyHaveEvent\n    this.receivedEvent = params.receivedEvent\n    this.eoseTimeout = params.eoseTimeout || relay.baseEoseTimeout\n\n    this.oneose = params.oneose\n    this.onclose = params.onclose\n    this.onevent =\n      params.onevent ||\n      (event => {\n        console.warn(\n          `onevent() callback not defined for subscription '${this.id}' in relay ${this.relay.url}. event received:`,\n          event,\n        )\n      })\n  }\n\n  public fire() {\n    this.relay.send('[\"REQ\",\"' + this.id + '\",' + JSON.stringify(this.filters).substring(1))\n\n    // only now we start counting the eoseTimeout\n    this.eoseTimeoutHandle = setTimeout(this.receivedEose.bind(this), this.eoseTimeout)\n  }\n\n  public receivedEose() {\n    if (this.eosed) return\n    clearTimeout(this.eoseTimeoutHandle)\n    this.eosed = true\n    this.oneose?.()\n  }\n\n  public close(reason: string = 'closed by caller') {\n    if (!this.closed && this.relay.connected) {\n      // if the connection was closed by the user calling .close() we will send a CLOSE message\n      // otherwise this._open will be already set to false so we will skip this\n      try {\n        this.relay.send('[\"CLOSE\",' + JSON.stringify(this.id) + ']')\n      } catch (err) {\n        if (err instanceof SendingOnClosedConnection) {\n          /* doesn't matter, it's ok */\n        } else {\n          throw err\n        }\n      }\n      this.closed = true\n    }\n    this.relay.openSubs.delete(this.id)\n    this.onclose?.(reason)\n  }\n}\n\nexport type SubscriptionParams = {\n  onevent?: (evt: Event) => void\n  oneose?: () => void\n  onclose?: (reason: string) => void\n  alreadyHaveEvent?: (id: string) => boolean\n  receivedEvent?: (relay: AbstractRelay, id: string) => void\n  eoseTimeout?: number\n  abort?: AbortSignal\n}\n\nexport type CountResolver = {\n  resolve: (count: number) => void\n  reject: (err: Error) => void\n}\n\nexport type EventPublishResolver = {\n  resolve: (reason: string) => void\n  reject: (err: Error) => void\n  timeout: ReturnType<typeof setTimeout>\n}\n"],
   "mappings": "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",
   "names": ["target"]
 }
File too large for inline diff
File too large for inline diff
Index: package/lib/cjs/nip04.js.map
===================================================================
--- package/lib/cjs/nip04.js.map
+++ package/lib/cjs/nip04.js.map
@@ -1,7 +1,7 @@
 {
   "version": 3,
   "sources": ["../../nip04.ts", "../../utils.ts"],
-  "sourcesContent": ["import { bytesToHex, randomBytes } from '@noble/hashes/utils'\nimport { secp256k1 } from '@noble/curves/secp256k1'\nimport { cbc } from '@noble/ciphers/aes'\nimport { base64 } from '@scure/base'\n\nimport { utf8Decoder, utf8Encoder } from './utils.ts'\n\nexport function encrypt(secretKey: string | Uint8Array, pubkey: string, text: string): string {\n  const privkey: string = secretKey instanceof Uint8Array ? bytesToHex(secretKey) : secretKey\n  const key = secp256k1.getSharedSecret(privkey, '02' + pubkey)\n  const normalizedKey = getNormalizedX(key)\n\n  let iv = Uint8Array.from(randomBytes(16))\n  let plaintext = utf8Encoder.encode(text)\n\n  let ciphertext = cbc(normalizedKey, iv).encrypt(plaintext)\n\n  let ctb64 = base64.encode(new Uint8Array(ciphertext))\n  let ivb64 = base64.encode(new Uint8Array(iv.buffer))\n\n  return `${ctb64}?iv=${ivb64}`\n}\n\nexport function decrypt(secretKey: string | Uint8Array, pubkey: string, data: string): string {\n  const privkey: string = secretKey instanceof Uint8Array ? bytesToHex(secretKey) : secretKey\n  let [ctb64, ivb64] = data.split('?iv=')\n  let key = secp256k1.getSharedSecret(privkey, '02' + pubkey)\n  let normalizedKey = getNormalizedX(key)\n\n  let iv = base64.decode(ivb64)\n  let ciphertext = base64.decode(ctb64)\n\n  let plaintext = cbc(normalizedKey, iv).decrypt(ciphertext)\n\n  return utf8Decoder.decode(plaintext)\n}\n\nfunction getNormalizedX(key: Uint8Array): Uint8Array {\n  return key.slice(1, 33)\n}\n", "import type { Event } from './core.ts'\n\nexport const utf8Decoder: TextDecoder = new TextDecoder('utf-8')\nexport const utf8Encoder: TextEncoder = new TextEncoder()\n\nexport { bytesToHex, hexToBytes } from '@noble/hashes/utils'\n\nexport function normalizeURL(url: string): string {\n  try {\n    if (url.indexOf('://') === -1) url = 'wss://' + url\n    let p = new URL(url)\n    if (p.protocol === 'http:') p.protocol = 'ws:'\n    else if (p.protocol === 'https:') p.protocol = 'wss:'\n    p.pathname = p.pathname.replace(/\\/+/g, '/')\n    if (p.pathname.endsWith('/')) p.pathname = p.pathname.slice(0, -1)\n    if ((p.port === '80' && p.protocol === 'ws:') || (p.port === '443' && p.protocol === 'wss:')) p.port = ''\n    p.searchParams.sort()\n    p.hash = ''\n    return p.toString()\n  } catch (e) {\n    throw new Error(`Invalid URL: ${url}`)\n  }\n}\n\nexport function insertEventIntoDescendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return b.created_at - event.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function insertEventIntoAscendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return event.created_at - b.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function binarySearch<T>(arr: T[], compare: (b: T) => number): [number, boolean] {\n  let start = 0\n  let end = arr.length - 1\n\n  while (start <= end) {\n    const mid = Math.floor((start + end) / 2)\n    const cmp = compare(arr[mid])\n\n    if (cmp === 0) {\n      return [mid, true]\n    }\n\n    if (cmp < 0) {\n      end = mid - 1\n    } else {\n      start = mid + 1\n    }\n  }\n\n  return [start, false]\n}\n\nexport class QueueNode<V> {\n  public value: V\n  public next: QueueNode<V> | null = null\n  public prev: QueueNode<V> | null = null\n\n  constructor(message: V) {\n    this.value = message\n  }\n}\n\nexport class Queue<V> {\n  public first: QueueNode<V> | null\n  public last: QueueNode<V> | null\n\n  constructor() {\n    this.first = null\n    this.last = null\n  }\n\n  enqueue(value: V): boolean {\n    const newNode = new QueueNode(value)\n    if (!this.last) {\n      // list is empty\n      this.first = newNode\n      this.last = newNode\n    } else if (this.last === this.first) {\n      // list has a single element\n      this.last = newNode\n      this.last.prev = this.first\n      this.first.next = newNode\n    } else {\n      // list has elements, add as last\n      newNode.prev = this.last\n      this.last.next = newNode\n      this.last = newNode\n    }\n    return true\n  }\n\n  dequeue(): V | null {\n    if (!this.first) return null\n\n    if (this.first === this.last) {\n      const target = this.first\n      this.first = null\n      this.last = null\n      return target.value\n    }\n\n    const target = this.first\n    this.first = target.next\n    if (this.first) {\n      this.first.prev = null // fix: clean up prev pointer\n    }\n\n    return target.value\n  }\n}\n"],
-  "mappings": ";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAAAA,gBAAwC;AACxC,uBAA0B;AAC1B,iBAAoB;AACpB,kBAAuB;;;ACEvB,mBAAuC;AAHhC,IAAM,cAA2B,IAAI,YAAY,OAAO;AACxD,IAAM,cAA2B,IAAI,YAAY;;;ADIjD,SAAS,QAAQ,WAAgC,QAAgB,MAAsB;AAC5F,QAAM,UAAkB,qBAAqB,iBAAa,0BAAW,SAAS,IAAI;AAClF,QAAM,MAAM,2BAAU,gBAAgB,SAAS,OAAO,MAAM;AAC5D,QAAM,gBAAgB,eAAe,GAAG;AAExC,MAAI,KAAK,WAAW,SAAK,2BAAY,EAAE,CAAC;AACxC,MAAI,YAAY,YAAY,OAAO,IAAI;AAEvC,MAAI,iBAAa,gBAAI,eAAe,EAAE,EAAE,QAAQ,SAAS;AAEzD,MAAI,QAAQ,mBAAO,OAAO,IAAI,WAAW,UAAU,CAAC;AACpD,MAAI,QAAQ,mBAAO,OAAO,IAAI,WAAW,GAAG,MAAM,CAAC;AAEnD,SAAO,GAAG,YAAY;AACxB;AAEO,SAAS,QAAQ,WAAgC,QAAgB,MAAsB;AAC5F,QAAM,UAAkB,qBAAqB,iBAAa,0BAAW,SAAS,IAAI;AAClF,MAAI,CAAC,OAAO,KAAK,IAAI,KAAK,MAAM,MAAM;AACtC,MAAI,MAAM,2BAAU,gBAAgB,SAAS,OAAO,MAAM;AAC1D,MAAI,gBAAgB,eAAe,GAAG;AAEtC,MAAI,KAAK,mBAAO,OAAO,KAAK;AAC5B,MAAI,aAAa,mBAAO,OAAO,KAAK;AAEpC,MAAI,gBAAY,gBAAI,eAAe,EAAE,EAAE,QAAQ,UAAU;AAEzD,SAAO,YAAY,OAAO,SAAS;AACrC;AAEA,SAAS,eAAe,KAA6B;AACnD,SAAO,IAAI,MAAM,GAAG,EAAE;AACxB;",
+  "sourcesContent": ["import { hexToBytes, randomBytes } from '@noble/hashes/utils.js'\nimport { secp256k1 } from '@noble/curves/secp256k1.js'\nimport { cbc } from '@noble/ciphers/aes.js'\nimport { base64 } from '@scure/base'\n\nimport { utf8Decoder, utf8Encoder } from './utils.ts'\n\nexport function encrypt(secretKey: string | Uint8Array, pubkey: string, text: string): string {\n  const privkey: Uint8Array = secretKey instanceof Uint8Array ? secretKey : hexToBytes(secretKey)\n  const key = secp256k1.getSharedSecret(privkey, hexToBytes('02' + pubkey))\n  const normalizedKey = getNormalizedX(key)\n\n  let iv = Uint8Array.from(randomBytes(16))\n  let plaintext = utf8Encoder.encode(text)\n\n  let ciphertext = cbc(normalizedKey, iv).encrypt(plaintext)\n\n  let ctb64 = base64.encode(new Uint8Array(ciphertext))\n  let ivb64 = base64.encode(new Uint8Array(iv.buffer))\n\n  return `${ctb64}?iv=${ivb64}`\n}\n\nexport function decrypt(secretKey: string | Uint8Array, pubkey: string, data: string): string {\n  const privkey: Uint8Array = secretKey instanceof Uint8Array ? secretKey : hexToBytes(secretKey)\n  let [ctb64, ivb64] = data.split('?iv=')\n  let key = secp256k1.getSharedSecret(privkey, hexToBytes('02' + pubkey))\n  let normalizedKey = getNormalizedX(key)\n\n  let iv = base64.decode(ivb64)\n  let ciphertext = base64.decode(ctb64)\n\n  let plaintext = cbc(normalizedKey, iv).decrypt(ciphertext)\n\n  return utf8Decoder.decode(plaintext)\n}\n\nfunction getNormalizedX(key: Uint8Array): Uint8Array {\n  return key.slice(1, 33)\n}\n", "import type { Event } from './core.ts'\n\nexport const utf8Decoder: TextDecoder = new TextDecoder('utf-8')\nexport const utf8Encoder: TextEncoder = new TextEncoder()\n\nexport { bytesToHex, hexToBytes } from '@noble/hashes/utils.js'\n\nexport function normalizeURL(url: string): string {\n  try {\n    if (url.indexOf('://') === -1) url = 'wss://' + url\n    let p = new URL(url)\n    if (p.protocol === 'http:') p.protocol = 'ws:'\n    else if (p.protocol === 'https:') p.protocol = 'wss:'\n    p.pathname = p.pathname.replace(/\\/+/g, '/')\n    if (p.pathname.endsWith('/')) p.pathname = p.pathname.slice(0, -1)\n    if ((p.port === '80' && p.protocol === 'ws:') || (p.port === '443' && p.protocol === 'wss:')) p.port = ''\n    p.searchParams.sort()\n    p.hash = ''\n    return p.toString()\n  } catch (e) {\n    throw new Error(`Invalid URL: ${url}`)\n  }\n}\n\nexport function insertEventIntoDescendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return b.created_at - event.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function insertEventIntoAscendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return event.created_at - b.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function binarySearch<T>(arr: T[], compare: (b: T) => number): [number, boolean] {\n  let start = 0\n  let end = arr.length - 1\n\n  while (start <= end) {\n    const mid = Math.floor((start + end) / 2)\n    const cmp = compare(arr[mid])\n\n    if (cmp === 0) {\n      return [mid, true]\n    }\n\n    if (cmp < 0) {\n      end = mid - 1\n    } else {\n      start = mid + 1\n    }\n  }\n\n  return [start, false]\n}\n\nexport class QueueNode<V> {\n  public value: V\n  public next: QueueNode<V> | null = null\n  public prev: QueueNode<V> | null = null\n\n  constructor(message: V) {\n    this.value = message\n  }\n}\n\nexport class Queue<V> {\n  public first: QueueNode<V> | null\n  public last: QueueNode<V> | null\n\n  constructor() {\n    this.first = null\n    this.last = null\n  }\n\n  enqueue(value: V): boolean {\n    const newNode = new QueueNode(value)\n    if (!this.last) {\n      // list is empty\n      this.first = newNode\n      this.last = newNode\n    } else if (this.last === this.first) {\n      // list has a single element\n      this.last = newNode\n      this.last.prev = this.first\n      this.first.next = newNode\n    } else {\n      // list has elements, add as last\n      newNode.prev = this.last\n      this.last.next = newNode\n      this.last = newNode\n    }\n    return true\n  }\n\n  dequeue(): V | null {\n    if (!this.first) return null\n\n    if (this.first === this.last) {\n      const target = this.first\n      this.first = null\n      this.last = null\n      return target.value\n    }\n\n    const target = this.first\n    this.first = target.next\n    if (this.first) {\n      this.first.prev = null // fix: clean up prev pointer\n    }\n\n    return target.value\n  }\n}\n"],
+  "mappings": ";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAAAA,gBAAwC;AACxC,uBAA0B;AAC1B,iBAAoB;AACpB,kBAAuB;;;ACEvB,mBAAuC;AAHhC,IAAM,cAA2B,IAAI,YAAY,OAAO;AACxD,IAAM,cAA2B,IAAI,YAAY;;;ADIjD,SAAS,QAAQ,WAAgC,QAAgB,MAAsB;AAC5F,QAAM,UAAsB,qBAAqB,aAAa,gBAAY,0BAAW,SAAS;AAC9F,QAAM,MAAM,2BAAU,gBAAgB,aAAS,0BAAW,OAAO,MAAM,CAAC;AACxE,QAAM,gBAAgB,eAAe,GAAG;AAExC,MAAI,KAAK,WAAW,SAAK,2BAAY,EAAE,CAAC;AACxC,MAAI,YAAY,YAAY,OAAO,IAAI;AAEvC,MAAI,iBAAa,gBAAI,eAAe,EAAE,EAAE,QAAQ,SAAS;AAEzD,MAAI,QAAQ,mBAAO,OAAO,IAAI,WAAW,UAAU,CAAC;AACpD,MAAI,QAAQ,mBAAO,OAAO,IAAI,WAAW,GAAG,MAAM,CAAC;AAEnD,SAAO,GAAG,YAAY;AACxB;AAEO,SAAS,QAAQ,WAAgC,QAAgB,MAAsB;AAC5F,QAAM,UAAsB,qBAAqB,aAAa,gBAAY,0BAAW,SAAS;AAC9F,MAAI,CAAC,OAAO,KAAK,IAAI,KAAK,MAAM,MAAM;AACtC,MAAI,MAAM,2BAAU,gBAAgB,aAAS,0BAAW,OAAO,MAAM,CAAC;AACtE,MAAI,gBAAgB,eAAe,GAAG;AAEtC,MAAI,KAAK,mBAAO,OAAO,KAAK;AAC5B,MAAI,aAAa,mBAAO,OAAO,KAAK;AAEpC,MAAI,gBAAY,gBAAI,eAAe,EAAE,EAAE,QAAQ,UAAU;AAEzD,SAAO,YAAY,OAAO,SAAS;AACrC;AAEA,SAAS,eAAe,KAA6B;AACnD,SAAO,IAAI,MAAM,GAAG,EAAE;AACxB;",
   "names": ["import_utils"]
 }
Index: package/lib/esm/nip04.js.map
===================================================================
--- package/lib/esm/nip04.js.map
+++ package/lib/esm/nip04.js.map
@@ -1,7 +1,7 @@
 {
   "version": 3,
   "sources": ["../../nip04.ts", "../../utils.ts"],
-  "sourcesContent": ["import { bytesToHex, randomBytes } from '@noble/hashes/utils'\nimport { secp256k1 } from '@noble/curves/secp256k1'\nimport { cbc } from '@noble/ciphers/aes'\nimport { base64 } from '@scure/base'\n\nimport { utf8Decoder, utf8Encoder } from './utils.ts'\n\nexport function encrypt(secretKey: string | Uint8Array, pubkey: string, text: string): string {\n  const privkey: string = secretKey instanceof Uint8Array ? bytesToHex(secretKey) : secretKey\n  const key = secp256k1.getSharedSecret(privkey, '02' + pubkey)\n  const normalizedKey = getNormalizedX(key)\n\n  let iv = Uint8Array.from(randomBytes(16))\n  let plaintext = utf8Encoder.encode(text)\n\n  let ciphertext = cbc(normalizedKey, iv).encrypt(plaintext)\n\n  let ctb64 = base64.encode(new Uint8Array(ciphertext))\n  let ivb64 = base64.encode(new Uint8Array(iv.buffer))\n\n  return `${ctb64}?iv=${ivb64}`\n}\n\nexport function decrypt(secretKey: string | Uint8Array, pubkey: string, data: string): string {\n  const privkey: string = secretKey instanceof Uint8Array ? bytesToHex(secretKey) : secretKey\n  let [ctb64, ivb64] = data.split('?iv=')\n  let key = secp256k1.getSharedSecret(privkey, '02' + pubkey)\n  let normalizedKey = getNormalizedX(key)\n\n  let iv = base64.decode(ivb64)\n  let ciphertext = base64.decode(ctb64)\n\n  let plaintext = cbc(normalizedKey, iv).decrypt(ciphertext)\n\n  return utf8Decoder.decode(plaintext)\n}\n\nfunction getNormalizedX(key: Uint8Array): Uint8Array {\n  return key.slice(1, 33)\n}\n", "import type { Event } from './core.ts'\n\nexport const utf8Decoder: TextDecoder = new TextDecoder('utf-8')\nexport const utf8Encoder: TextEncoder = new TextEncoder()\n\nexport { bytesToHex, hexToBytes } from '@noble/hashes/utils'\n\nexport function normalizeURL(url: string): string {\n  try {\n    if (url.indexOf('://') === -1) url = 'wss://' + url\n    let p = new URL(url)\n    if (p.protocol === 'http:') p.protocol = 'ws:'\n    else if (p.protocol === 'https:') p.protocol = 'wss:'\n    p.pathname = p.pathname.replace(/\\/+/g, '/')\n    if (p.pathname.endsWith('/')) p.pathname = p.pathname.slice(0, -1)\n    if ((p.port === '80' && p.protocol === 'ws:') || (p.port === '443' && p.protocol === 'wss:')) p.port = ''\n    p.searchParams.sort()\n    p.hash = ''\n    return p.toString()\n  } catch (e) {\n    throw new Error(`Invalid URL: ${url}`)\n  }\n}\n\nexport function insertEventIntoDescendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return b.created_at - event.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function insertEventIntoAscendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return event.created_at - b.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function binarySearch<T>(arr: T[], compare: (b: T) => number): [number, boolean] {\n  let start = 0\n  let end = arr.length - 1\n\n  while (start <= end) {\n    const mid = Math.floor((start + end) / 2)\n    const cmp = compare(arr[mid])\n\n    if (cmp === 0) {\n      return [mid, true]\n    }\n\n    if (cmp < 0) {\n      end = mid - 1\n    } else {\n      start = mid + 1\n    }\n  }\n\n  return [start, false]\n}\n\nexport class QueueNode<V> {\n  public value: V\n  public next: QueueNode<V> | null = null\n  public prev: QueueNode<V> | null = null\n\n  constructor(message: V) {\n    this.value = message\n  }\n}\n\nexport class Queue<V> {\n  public first: QueueNode<V> | null\n  public last: QueueNode<V> | null\n\n  constructor() {\n    this.first = null\n    this.last = null\n  }\n\n  enqueue(value: V): boolean {\n    const newNode = new QueueNode(value)\n    if (!this.last) {\n      // list is empty\n      this.first = newNode\n      this.last = newNode\n    } else if (this.last === this.first) {\n      // list has a single element\n      this.last = newNode\n      this.last.prev = this.first\n      this.first.next = newNode\n    } else {\n      // list has elements, add as last\n      newNode.prev = this.last\n      this.last.next = newNode\n      this.last = newNode\n    }\n    return true\n  }\n\n  dequeue(): V | null {\n    if (!this.first) return null\n\n    if (this.first === this.last) {\n      const target = this.first\n      this.first = null\n      this.last = null\n      return target.value\n    }\n\n    const target = this.first\n    this.first = target.next\n    if (this.first) {\n      this.first.prev = null // fix: clean up prev pointer\n    }\n\n    return target.value\n  }\n}\n"],
-  "mappings": ";AAAA,SAAS,cAAAA,aAAY,mBAAmB;AACxC,SAAS,iBAAiB;AAC1B,SAAS,WAAW;AACpB,SAAS,cAAc;;;ACEvB,SAAS,YAAY,kBAAkB;AAHhC,IAAM,cAA2B,IAAI,YAAY,OAAO;AACxD,IAAM,cAA2B,IAAI,YAAY;;;ADIjD,SAAS,QAAQ,WAAgC,QAAgB,MAAsB;AAC5F,QAAM,UAAkB,qBAAqB,aAAaC,YAAW,SAAS,IAAI;AAClF,QAAM,MAAM,UAAU,gBAAgB,SAAS,OAAO,MAAM;AAC5D,QAAM,gBAAgB,eAAe,GAAG;AAExC,MAAI,KAAK,WAAW,KAAK,YAAY,EAAE,CAAC;AACxC,MAAI,YAAY,YAAY,OAAO,IAAI;AAEvC,MAAI,aAAa,IAAI,eAAe,EAAE,EAAE,QAAQ,SAAS;AAEzD,MAAI,QAAQ,OAAO,OAAO,IAAI,WAAW,UAAU,CAAC;AACpD,MAAI,QAAQ,OAAO,OAAO,IAAI,WAAW,GAAG,MAAM,CAAC;AAEnD,SAAO,GAAG,YAAY;AACxB;AAEO,SAAS,QAAQ,WAAgC,QAAgB,MAAsB;AAC5F,QAAM,UAAkB,qBAAqB,aAAaA,YAAW,SAAS,IAAI;AAClF,MAAI,CAAC,OAAO,KAAK,IAAI,KAAK,MAAM,MAAM;AACtC,MAAI,MAAM,UAAU,gBAAgB,SAAS,OAAO,MAAM;AAC1D,MAAI,gBAAgB,eAAe,GAAG;AAEtC,MAAI,KAAK,OAAO,OAAO,KAAK;AAC5B,MAAI,aAAa,OAAO,OAAO,KAAK;AAEpC,MAAI,YAAY,IAAI,eAAe,EAAE,EAAE,QAAQ,UAAU;AAEzD,SAAO,YAAY,OAAO,SAAS;AACrC;AAEA,SAAS,eAAe,KAA6B;AACnD,SAAO,IAAI,MAAM,GAAG,EAAE;AACxB;",
-  "names": ["bytesToHex", "bytesToHex"]
+  "sourcesContent": ["import { hexToBytes, randomBytes } from '@noble/hashes/utils.js'\nimport { secp256k1 } from '@noble/curves/secp256k1.js'\nimport { cbc } from '@noble/ciphers/aes.js'\nimport { base64 } from '@scure/base'\n\nimport { utf8Decoder, utf8Encoder } from './utils.ts'\n\nexport function encrypt(secretKey: string | Uint8Array, pubkey: string, text: string): string {\n  const privkey: Uint8Array = secretKey instanceof Uint8Array ? secretKey : hexToBytes(secretKey)\n  const key = secp256k1.getSharedSecret(privkey, hexToBytes('02' + pubkey))\n  const normalizedKey = getNormalizedX(key)\n\n  let iv = Uint8Array.from(randomBytes(16))\n  let plaintext = utf8Encoder.encode(text)\n\n  let ciphertext = cbc(normalizedKey, iv).encrypt(plaintext)\n\n  let ctb64 = base64.encode(new Uint8Array(ciphertext))\n  let ivb64 = base64.encode(new Uint8Array(iv.buffer))\n\n  return `${ctb64}?iv=${ivb64}`\n}\n\nexport function decrypt(secretKey: string | Uint8Array, pubkey: string, data: string): string {\n  const privkey: Uint8Array = secretKey instanceof Uint8Array ? secretKey : hexToBytes(secretKey)\n  let [ctb64, ivb64] = data.split('?iv=')\n  let key = secp256k1.getSharedSecret(privkey, hexToBytes('02' + pubkey))\n  let normalizedKey = getNormalizedX(key)\n\n  let iv = base64.decode(ivb64)\n  let ciphertext = base64.decode(ctb64)\n\n  let plaintext = cbc(normalizedKey, iv).decrypt(ciphertext)\n\n  return utf8Decoder.decode(plaintext)\n}\n\nfunction getNormalizedX(key: Uint8Array): Uint8Array {\n  return key.slice(1, 33)\n}\n", "import type { Event } from './core.ts'\n\nexport const utf8Decoder: TextDecoder = new TextDecoder('utf-8')\nexport const utf8Encoder: TextEncoder = new TextEncoder()\n\nexport { bytesToHex, hexToBytes } from '@noble/hashes/utils.js'\n\nexport function normalizeURL(url: string): string {\n  try {\n    if (url.indexOf('://') === -1) url = 'wss://' + url\n    let p = new URL(url)\n    if (p.protocol === 'http:') p.protocol = 'ws:'\n    else if (p.protocol === 'https:') p.protocol = 'wss:'\n    p.pathname = p.pathname.replace(/\\/+/g, '/')\n    if (p.pathname.endsWith('/')) p.pathname = p.pathname.slice(0, -1)\n    if ((p.port === '80' && p.protocol === 'ws:') || (p.port === '443' && p.protocol === 'wss:')) p.port = ''\n    p.searchParams.sort()\n    p.hash = ''\n    return p.toString()\n  } catch (e) {\n    throw new Error(`Invalid URL: ${url}`)\n  }\n}\n\nexport function insertEventIntoDescendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return b.created_at - event.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function insertEventIntoAscendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return event.created_at - b.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function binarySearch<T>(arr: T[], compare: (b: T) => number): [number, boolean] {\n  let start = 0\n  let end = arr.length - 1\n\n  while (start <= end) {\n    const mid = Math.floor((start + end) / 2)\n    const cmp = compare(arr[mid])\n\n    if (cmp === 0) {\n      return [mid, true]\n    }\n\n    if (cmp < 0) {\n      end = mid - 1\n    } else {\n      start = mid + 1\n    }\n  }\n\n  return [start, false]\n}\n\nexport class QueueNode<V> {\n  public value: V\n  public next: QueueNode<V> | null = null\n  public prev: QueueNode<V> | null = null\n\n  constructor(message: V) {\n    this.value = message\n  }\n}\n\nexport class Queue<V> {\n  public first: QueueNode<V> | null\n  public last: QueueNode<V> | null\n\n  constructor() {\n    this.first = null\n    this.last = null\n  }\n\n  enqueue(value: V): boolean {\n    const newNode = new QueueNode(value)\n    if (!this.last) {\n      // list is empty\n      this.first = newNode\n      this.last = newNode\n    } else if (this.last === this.first) {\n      // list has a single element\n      this.last = newNode\n      this.last.prev = this.first\n      this.first.next = newNode\n    } else {\n      // list has elements, add as last\n      newNode.prev = this.last\n      this.last.next = newNode\n      this.last = newNode\n    }\n    return true\n  }\n\n  dequeue(): V | null {\n    if (!this.first) return null\n\n    if (this.first === this.last) {\n      const target = this.first\n      this.first = null\n      this.last = null\n      return target.value\n    }\n\n    const target = this.first\n    this.first = target.next\n    if (this.first) {\n      this.first.prev = null // fix: clean up prev pointer\n    }\n\n    return target.value\n  }\n}\n"],
+  "mappings": ";AAAA,SAAS,cAAAA,aAAY,mBAAmB;AACxC,SAAS,iBAAiB;AAC1B,SAAS,WAAW;AACpB,SAAS,cAAc;;;ACEvB,SAAS,YAAY,kBAAkB;AAHhC,IAAM,cAA2B,IAAI,YAAY,OAAO;AACxD,IAAM,cAA2B,IAAI,YAAY;;;ADIjD,SAAS,QAAQ,WAAgC,QAAgB,MAAsB;AAC5F,QAAM,UAAsB,qBAAqB,aAAa,YAAYC,YAAW,SAAS;AAC9F,QAAM,MAAM,UAAU,gBAAgB,SAASA,YAAW,OAAO,MAAM,CAAC;AACxE,QAAM,gBAAgB,eAAe,GAAG;AAExC,MAAI,KAAK,WAAW,KAAK,YAAY,EAAE,CAAC;AACxC,MAAI,YAAY,YAAY,OAAO,IAAI;AAEvC,MAAI,aAAa,IAAI,eAAe,EAAE,EAAE,QAAQ,SAAS;AAEzD,MAAI,QAAQ,OAAO,OAAO,IAAI,WAAW,UAAU,CAAC;AACpD,MAAI,QAAQ,OAAO,OAAO,IAAI,WAAW,GAAG,MAAM,CAAC;AAEnD,SAAO,GAAG,YAAY;AACxB;AAEO,SAAS,QAAQ,WAAgC,QAAgB,MAAsB;AAC5F,QAAM,UAAsB,qBAAqB,aAAa,YAAYA,YAAW,SAAS;AAC9F,MAAI,CAAC,OAAO,KAAK,IAAI,KAAK,MAAM,MAAM;AACtC,MAAI,MAAM,UAAU,gBAAgB,SAASA,YAAW,OAAO,MAAM,CAAC;AACtE,MAAI,gBAAgB,eAAe,GAAG;AAEtC,MAAI,KAAK,OAAO,OAAO,KAAK;AAC5B,MAAI,aAAa,OAAO,OAAO,KAAK;AAEpC,MAAI,YAAY,IAAI,eAAe,EAAE,EAAE,QAAQ,UAAU;AAEzD,SAAO,YAAY,OAAO,SAAS;AACrC;AAEA,SAAS,eAAe,KAA6B;AACnD,SAAO,IAAI,MAAM,GAAG,EAAE;AACxB;",
+  "names": ["hexToBytes", "hexToBytes"]
 }
Index: package/lib/cjs/nip06.js.map
===================================================================
--- package/lib/cjs/nip06.js.map
+++ package/lib/cjs/nip06.js.map
@@ -1,7 +1,7 @@
 {
   "version": 3,
   "sources": ["../../nip06.ts"],
-  "sourcesContent": ["import { bytesToHex } from '@noble/hashes/utils'\nimport { wordlist } from '@scure/bip39/wordlists/english'\nimport { generateMnemonic, mnemonicToSeedSync, validateMnemonic } from '@scure/bip39'\nimport { HDKey } from '@scure/bip32'\n\nconst DERIVATION_PATH = `m/44'/1237'`\n\nexport function privateKeyFromSeedWords(mnemonic: string, passphrase?: string, accountIndex = 0): Uint8Array {\n  let root = HDKey.fromMasterSeed(mnemonicToSeedSync(mnemonic, passphrase))\n  let privateKey = root.derive(`${DERIVATION_PATH}/${accountIndex}'/0/0`).privateKey\n  if (!privateKey) throw new Error('could not derive private key')\n  return privateKey\n}\n\nexport function accountFromSeedWords(\n  mnemonic: string,\n  passphrase?: string,\n  accountIndex = 0,\n): {\n  privateKey: Uint8Array\n  publicKey: string\n} {\n  const root = HDKey.fromMasterSeed(mnemonicToSeedSync(mnemonic, passphrase))\n  const seed = root.derive(`${DERIVATION_PATH}/${accountIndex}'/0/0`)\n  const publicKey = bytesToHex(seed.publicKey!.slice(1))\n  const privateKey = seed.privateKey\n  if (!privateKey || !publicKey) {\n    throw new Error('could not derive key pair')\n  }\n  return { privateKey, publicKey }\n}\n\nexport function extendedKeysFromSeedWords(\n  mnemonic: string,\n  passphrase?: string,\n  extendedAccountIndex = 0,\n): {\n  privateExtendedKey: string\n  publicExtendedKey: string\n} {\n  let root = HDKey.fromMasterSeed(mnemonicToSeedSync(mnemonic, passphrase))\n  let seed = root.derive(`${DERIVATION_PATH}/${extendedAccountIndex}'`)\n  let privateExtendedKey = seed.privateExtendedKey\n  let publicExtendedKey = seed.publicExtendedKey\n  if (!privateExtendedKey && !publicExtendedKey) throw new Error('could not derive extended key pair')\n  return { privateExtendedKey, publicExtendedKey }\n}\n\nexport function accountFromExtendedKey(\n  base58key: string,\n  accountIndex = 0,\n): {\n  privateKey?: Uint8Array\n  publicKey: string\n} {\n  let extendedKey = HDKey.fromExtendedKey(base58key)\n  let version = base58key.slice(0, 4)\n  let child = extendedKey.deriveChild(0).deriveChild(accountIndex)\n  let publicKey = bytesToHex(child.publicKey!.slice(1))\n  if (!publicKey) throw new Error('could not derive public key')\n  if (version === 'xprv') {\n    let privateKey = child.privateKey!\n    if (!privateKey) throw new Error('could not derive private key')\n    return { privateKey, publicKey }\n  }\n  return { publicKey }\n}\n\nexport function generateSeedWords(): string {\n  return generateMnemonic(wordlist)\n}\n\nexport function validateWords(words: string): boolean {\n  return validateMnemonic(words, wordlist)\n}\n"],
+  "sourcesContent": ["import { bytesToHex } from '@noble/hashes/utils.js'\nimport { wordlist } from '@scure/bip39/wordlists/english.js'\nimport { generateMnemonic, mnemonicToSeedSync, validateMnemonic } from '@scure/bip39'\nimport { HDKey } from '@scure/bip32'\n\nconst DERIVATION_PATH = `m/44'/1237'`\n\nexport function privateKeyFromSeedWords(mnemonic: string, passphrase?: string, accountIndex = 0): Uint8Array {\n  let root = HDKey.fromMasterSeed(mnemonicToSeedSync(mnemonic, passphrase))\n  let privateKey = root.derive(`${DERIVATION_PATH}/${accountIndex}'/0/0`).privateKey\n  if (!privateKey) throw new Error('could not derive private key')\n  return privateKey\n}\n\nexport function accountFromSeedWords(\n  mnemonic: string,\n  passphrase?: string,\n  accountIndex = 0,\n): {\n  privateKey: Uint8Array\n  publicKey: string\n} {\n  const root = HDKey.fromMasterSeed(mnemonicToSeedSync(mnemonic, passphrase))\n  const seed = root.derive(`${DERIVATION_PATH}/${accountIndex}'/0/0`)\n  const publicKey = bytesToHex(seed.publicKey!.slice(1))\n  const privateKey = seed.privateKey\n  if (!privateKey || !publicKey) {\n    throw new Error('could not derive key pair')\n  }\n  return { privateKey, publicKey }\n}\n\nexport function extendedKeysFromSeedWords(\n  mnemonic: string,\n  passphrase?: string,\n  extendedAccountIndex = 0,\n): {\n  privateExtendedKey: string\n  publicExtendedKey: string\n} {\n  let root = HDKey.fromMasterSeed(mnemonicToSeedSync(mnemonic, passphrase))\n  let seed = root.derive(`${DERIVATION_PATH}/${extendedAccountIndex}'`)\n  let privateExtendedKey = seed.privateExtendedKey\n  let publicExtendedKey = seed.publicExtendedKey\n  if (!privateExtendedKey && !publicExtendedKey) throw new Error('could not derive extended key pair')\n  return { privateExtendedKey, publicExtendedKey }\n}\n\nexport function accountFromExtendedKey(\n  base58key: string,\n  accountIndex = 0,\n): {\n  privateKey?: Uint8Array\n  publicKey: string\n} {\n  let extendedKey = HDKey.fromExtendedKey(base58key)\n  let version = base58key.slice(0, 4)\n  let child = extendedKey.deriveChild(0).deriveChild(accountIndex)\n  let publicKey = bytesToHex(child.publicKey!.slice(1))\n  if (!publicKey) throw new Error('could not derive public key')\n  if (version === 'xprv') {\n    let privateKey = child.privateKey!\n    if (!privateKey) throw new Error('could not derive private key')\n    return { privateKey, publicKey }\n  }\n  return { publicKey }\n}\n\nexport function generateSeedWords(): string {\n  return generateMnemonic(wordlist)\n}\n\nexport function validateWords(words: string): boolean {\n  return validateMnemonic(words, wordlist)\n}\n"],
   "mappings": ";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,mBAA2B;AAC3B,qBAAyB;AACzB,mBAAuE;AACvE,mBAAsB;AAEtB,IAAM,kBAAkB;AAEjB,SAAS,wBAAwB,UAAkB,YAAqB,eAAe,GAAe;AAC3G,MAAI,OAAO,mBAAM,mBAAe,iCAAmB,UAAU,UAAU,CAAC;AACxE,MAAI,aAAa,KAAK,OAAO,GAAG,mBAAmB,mBAAmB,EAAE;AACxE,MAAI,CAAC;AAAY,UAAM,IAAI,MAAM,8BAA8B;AAC/D,SAAO;AACT;AAEO,SAAS,qBACd,UACA,YACA,eAAe,GAIf;AACA,QAAM,OAAO,mBAAM,mBAAe,iCAAmB,UAAU,UAAU,CAAC;AAC1E,QAAM,OAAO,KAAK,OAAO,GAAG,mBAAmB,mBAAmB;AAClE,QAAM,gBAAY,yBAAW,KAAK,UAAW,MAAM,CAAC,CAAC;AACrD,QAAM,aAAa,KAAK;AACxB,MAAI,CAAC,cAAc,CAAC,WAAW;AAC7B,UAAM,IAAI,MAAM,2BAA2B;AAAA,EAC7C;AACA,SAAO,EAAE,YAAY,UAAU;AACjC;AAEO,SAAS,0BACd,UACA,YACA,uBAAuB,GAIvB;AACA,MAAI,OAAO,mBAAM,mBAAe,iCAAmB,UAAU,UAAU,CAAC;AACxE,MAAI,OAAO,KAAK,OAAO,GAAG,mBAAmB,uBAAuB;AACpE,MAAI,qBAAqB,KAAK;AAC9B,MAAI,oBAAoB,KAAK;AAC7B,MAAI,CAAC,sBAAsB,CAAC;AAAmB,UAAM,IAAI,MAAM,oCAAoC;AACnG,SAAO,EAAE,oBAAoB,kBAAkB;AACjD;AAEO,SAAS,uBACd,WACA,eAAe,GAIf;AACA,MAAI,cAAc,mBAAM,gBAAgB,SAAS;AACjD,MAAI,UAAU,UAAU,MAAM,GAAG,CAAC;AAClC,MAAI,QAAQ,YAAY,YAAY,CAAC,EAAE,YAAY,YAAY;AAC/D,MAAI,gBAAY,yBAAW,MAAM,UAAW,MAAM,CAAC,CAAC;AACpD,MAAI,CAAC;AAAW,UAAM,IAAI,MAAM,6BAA6B;AAC7D,MAAI,YAAY,QAAQ;AACtB,QAAI,aAAa,MAAM;AACvB,QAAI,CAAC;AAAY,YAAM,IAAI,MAAM,8BAA8B;AAC/D,WAAO,EAAE,YAAY,UAAU;AAAA,EACjC;AACA,SAAO,EAAE,UAAU;AACrB;AAEO,SAAS,oBAA4B;AAC1C,aAAO,+BAAiB,uBAAQ;AAClC;AAEO,SAAS,cAAc,OAAwB;AACpD,aAAO,+BAAiB,OAAO,uBAAQ;AACzC;",
   "names": []
 }
Index: package/lib/esm/nip06.js.map
===================================================================
--- package/lib/esm/nip06.js.map
+++ package/lib/esm/nip06.js.map
@@ -1,7 +1,7 @@
 {
   "version": 3,
   "sources": ["../../nip06.ts"],
-  "sourcesContent": ["import { bytesToHex } from '@noble/hashes/utils'\nimport { wordlist } from '@scure/bip39/wordlists/english'\nimport { generateMnemonic, mnemonicToSeedSync, validateMnemonic } from '@scure/bip39'\nimport { HDKey } from '@scure/bip32'\n\nconst DERIVATION_PATH = `m/44'/1237'`\n\nexport function privateKeyFromSeedWords(mnemonic: string, passphrase?: string, accountIndex = 0): Uint8Array {\n  let root = HDKey.fromMasterSeed(mnemonicToSeedSync(mnemonic, passphrase))\n  let privateKey = root.derive(`${DERIVATION_PATH}/${accountIndex}'/0/0`).privateKey\n  if (!privateKey) throw new Error('could not derive private key')\n  return privateKey\n}\n\nexport function accountFromSeedWords(\n  mnemonic: string,\n  passphrase?: string,\n  accountIndex = 0,\n): {\n  privateKey: Uint8Array\n  publicKey: string\n} {\n  const root = HDKey.fromMasterSeed(mnemonicToSeedSync(mnemonic, passphrase))\n  const seed = root.derive(`${DERIVATION_PATH}/${accountIndex}'/0/0`)\n  const publicKey = bytesToHex(seed.publicKey!.slice(1))\n  const privateKey = seed.privateKey\n  if (!privateKey || !publicKey) {\n    throw new Error('could not derive key pair')\n  }\n  return { privateKey, publicKey }\n}\n\nexport function extendedKeysFromSeedWords(\n  mnemonic: string,\n  passphrase?: string,\n  extendedAccountIndex = 0,\n): {\n  privateExtendedKey: string\n  publicExtendedKey: string\n} {\n  let root = HDKey.fromMasterSeed(mnemonicToSeedSync(mnemonic, passphrase))\n  let seed = root.derive(`${DERIVATION_PATH}/${extendedAccountIndex}'`)\n  let privateExtendedKey = seed.privateExtendedKey\n  let publicExtendedKey = seed.publicExtendedKey\n  if (!privateExtendedKey && !publicExtendedKey) throw new Error('could not derive extended key pair')\n  return { privateExtendedKey, publicExtendedKey }\n}\n\nexport function accountFromExtendedKey(\n  base58key: string,\n  accountIndex = 0,\n): {\n  privateKey?: Uint8Array\n  publicKey: string\n} {\n  let extendedKey = HDKey.fromExtendedKey(base58key)\n  let version = base58key.slice(0, 4)\n  let child = extendedKey.deriveChild(0).deriveChild(accountIndex)\n  let publicKey = bytesToHex(child.publicKey!.slice(1))\n  if (!publicKey) throw new Error('could not derive public key')\n  if (version === 'xprv') {\n    let privateKey = child.privateKey!\n    if (!privateKey) throw new Error('could not derive private key')\n    return { privateKey, publicKey }\n  }\n  return { publicKey }\n}\n\nexport function generateSeedWords(): string {\n  return generateMnemonic(wordlist)\n}\n\nexport function validateWords(words: string): boolean {\n  return validateMnemonic(words, wordlist)\n}\n"],
+  "sourcesContent": ["import { bytesToHex } from '@noble/hashes/utils.js'\nimport { wordlist } from '@scure/bip39/wordlists/english.js'\nimport { generateMnemonic, mnemonicToSeedSync, validateMnemonic } from '@scure/bip39'\nimport { HDKey } from '@scure/bip32'\n\nconst DERIVATION_PATH = `m/44'/1237'`\n\nexport function privateKeyFromSeedWords(mnemonic: string, passphrase?: string, accountIndex = 0): Uint8Array {\n  let root = HDKey.fromMasterSeed(mnemonicToSeedSync(mnemonic, passphrase))\n  let privateKey = root.derive(`${DERIVATION_PATH}/${accountIndex}'/0/0`).privateKey\n  if (!privateKey) throw new Error('could not derive private key')\n  return privateKey\n}\n\nexport function accountFromSeedWords(\n  mnemonic: string,\n  passphrase?: string,\n  accountIndex = 0,\n): {\n  privateKey: Uint8Array\n  publicKey: string\n} {\n  const root = HDKey.fromMasterSeed(mnemonicToSeedSync(mnemonic, passphrase))\n  const seed = root.derive(`${DERIVATION_PATH}/${accountIndex}'/0/0`)\n  const publicKey = bytesToHex(seed.publicKey!.slice(1))\n  const privateKey = seed.privateKey\n  if (!privateKey || !publicKey) {\n    throw new Error('could not derive key pair')\n  }\n  return { privateKey, publicKey }\n}\n\nexport function extendedKeysFromSeedWords(\n  mnemonic: string,\n  passphrase?: string,\n  extendedAccountIndex = 0,\n): {\n  privateExtendedKey: string\n  publicExtendedKey: string\n} {\n  let root = HDKey.fromMasterSeed(mnemonicToSeedSync(mnemonic, passphrase))\n  let seed = root.derive(`${DERIVATION_PATH}/${extendedAccountIndex}'`)\n  let privateExtendedKey = seed.privateExtendedKey\n  let publicExtendedKey = seed.publicExtendedKey\n  if (!privateExtendedKey && !publicExtendedKey) throw new Error('could not derive extended key pair')\n  return { privateExtendedKey, publicExtendedKey }\n}\n\nexport function accountFromExtendedKey(\n  base58key: string,\n  accountIndex = 0,\n): {\n  privateKey?: Uint8Array\n  publicKey: string\n} {\n  let extendedKey = HDKey.fromExtendedKey(base58key)\n  let version = base58key.slice(0, 4)\n  let child = extendedKey.deriveChild(0).deriveChild(accountIndex)\n  let publicKey = bytesToHex(child.publicKey!.slice(1))\n  if (!publicKey) throw new Error('could not derive public key')\n  if (version === 'xprv') {\n    let privateKey = child.privateKey!\n    if (!privateKey) throw new Error('could not derive private key')\n    return { privateKey, publicKey }\n  }\n  return { publicKey }\n}\n\nexport function generateSeedWords(): string {\n  return generateMnemonic(wordlist)\n}\n\nexport function validateWords(words: string): boolean {\n  return validateMnemonic(words, wordlist)\n}\n"],
   "mappings": ";AAAA,SAAS,kBAAkB;AAC3B,SAAS,gBAAgB;AACzB,SAAS,kBAAkB,oBAAoB,wBAAwB;AACvE,SAAS,aAAa;AAEtB,IAAM,kBAAkB;AAEjB,SAAS,wBAAwB,UAAkB,YAAqB,eAAe,GAAe;AAC3G,MAAI,OAAO,MAAM,eAAe,mBAAmB,UAAU,UAAU,CAAC;AACxE,MAAI,aAAa,KAAK,OAAO,GAAG,mBAAmB,mBAAmB,EAAE;AACxE,MAAI,CAAC;AAAY,UAAM,IAAI,MAAM,8BAA8B;AAC/D,SAAO;AACT;AAEO,SAAS,qBACd,UACA,YACA,eAAe,GAIf;AACA,QAAM,OAAO,MAAM,eAAe,mBAAmB,UAAU,UAAU,CAAC;AAC1E,QAAM,OAAO,KAAK,OAAO,GAAG,mBAAmB,mBAAmB;AAClE,QAAM,YAAY,WAAW,KAAK,UAAW,MAAM,CAAC,CAAC;AACrD,QAAM,aAAa,KAAK;AACxB,MAAI,CAAC,cAAc,CAAC,WAAW;AAC7B,UAAM,IAAI,MAAM,2BAA2B;AAAA,EAC7C;AACA,SAAO,EAAE,YAAY,UAAU;AACjC;AAEO,SAAS,0BACd,UACA,YACA,uBAAuB,GAIvB;AACA,MAAI,OAAO,MAAM,eAAe,mBAAmB,UAAU,UAAU,CAAC;AACxE,MAAI,OAAO,KAAK,OAAO,GAAG,mBAAmB,uBAAuB;AACpE,MAAI,qBAAqB,KAAK;AAC9B,MAAI,oBAAoB,KAAK;AAC7B,MAAI,CAAC,sBAAsB,CAAC;AAAmB,UAAM,IAAI,MAAM,oCAAoC;AACnG,SAAO,EAAE,oBAAoB,kBAAkB;AACjD;AAEO,SAAS,uBACd,WACA,eAAe,GAIf;AACA,MAAI,cAAc,MAAM,gBAAgB,SAAS;AACjD,MAAI,UAAU,UAAU,MAAM,GAAG,CAAC;AAClC,MAAI,QAAQ,YAAY,YAAY,CAAC,EAAE,YAAY,YAAY;AAC/D,MAAI,YAAY,WAAW,MAAM,UAAW,MAAM,CAAC,CAAC;AACpD,MAAI,CAAC;AAAW,UAAM,IAAI,MAAM,6BAA6B;AAC7D,MAAI,YAAY,QAAQ;AACtB,QAAI,aAAa,MAAM;AACvB,QAAI,CAAC;AAAY,YAAM,IAAI,MAAM,8BAA8B;AAC/D,WAAO,EAAE,YAAY,UAAU;AAAA,EACjC;AACA,SAAO,EAAE,UAAU;AACrB;AAEO,SAAS,oBAA4B;AAC1C,SAAO,iBAAiB,QAAQ;AAClC;AAEO,SAAS,cAAc,OAAwB;AACpD,SAAO,iBAAiB,OAAO,QAAQ;AACzC;",
   "names": []
 }
Index: package/lib/cjs/nip13.js.map
===================================================================
--- package/lib/cjs/nip13.js.map
+++ package/lib/cjs/nip13.js.map
@@ -1,7 +1,7 @@
 {
   "version": 3,
   "sources": ["../../nip13.ts", "../../utils.ts"],
-  "sourcesContent": ["import { bytesToHex } from '@noble/hashes/utils'\nimport { type UnsignedEvent, type Event } from './pure.ts'\nimport { sha256 } from '@noble/hashes/sha256'\n\nimport { utf8Encoder } from './utils.ts'\n\n/** Get POW difficulty from a Nostr hex ID. */\nexport function getPow(hex: string): number {\n  let count = 0\n\n  for (let i = 0; i < 64; i += 8) {\n    const nibble = parseInt(hex.substring(i, i + 8), 16)\n    if (nibble === 0) {\n      count += 32\n    } else {\n      count += Math.clz32(nibble)\n      break\n    }\n  }\n\n  return count\n}\n\n/**\n * Mine an event with the desired POW. This function mutates the event.\n * Note that this operation is synchronous and should be run in a worker context to avoid blocking the main thread.\n */\nexport function minePow(unsigned: UnsignedEvent, difficulty: number): Omit<Event, 'sig'> {\n  let count = 0\n\n  const event = unsigned as Omit<Event, 'sig'>\n  const tag = ['nonce', count.toString(), difficulty.toString()]\n\n  event.tags.push(tag)\n\n  while (true) {\n    const now = Math.floor(new Date().getTime() / 1000)\n\n    if (now !== event.created_at) {\n      count = 0\n      event.created_at = now\n    }\n\n    tag[1] = (++count).toString()\n\n    event.id = fastEventHash(event)\n\n    if (getPow(event.id) >= difficulty) {\n      break\n    }\n  }\n\n  return event\n}\n\nexport function fastEventHash(evt: UnsignedEvent): string {\n  return bytesToHex(\n    sha256(utf8Encoder.encode(JSON.stringify([0, evt.pubkey, evt.created_at, evt.kind, evt.tags, evt.content]))),\n  )\n}\n", "import type { Event } from './core.ts'\n\nexport const utf8Decoder: TextDecoder = new TextDecoder('utf-8')\nexport const utf8Encoder: TextEncoder = new TextEncoder()\n\nexport { bytesToHex, hexToBytes } from '@noble/hashes/utils'\n\nexport function normalizeURL(url: string): string {\n  try {\n    if (url.indexOf('://') === -1) url = 'wss://' + url\n    let p = new URL(url)\n    if (p.protocol === 'http:') p.protocol = 'ws:'\n    else if (p.protocol === 'https:') p.protocol = 'wss:'\n    p.pathname = p.pathname.replace(/\\/+/g, '/')\n    if (p.pathname.endsWith('/')) p.pathname = p.pathname.slice(0, -1)\n    if ((p.port === '80' && p.protocol === 'ws:') || (p.port === '443' && p.protocol === 'wss:')) p.port = ''\n    p.searchParams.sort()\n    p.hash = ''\n    return p.toString()\n  } catch (e) {\n    throw new Error(`Invalid URL: ${url}`)\n  }\n}\n\nexport function insertEventIntoDescendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return b.created_at - event.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function insertEventIntoAscendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return event.created_at - b.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function binarySearch<T>(arr: T[], compare: (b: T) => number): [number, boolean] {\n  let start = 0\n  let end = arr.length - 1\n\n  while (start <= end) {\n    const mid = Math.floor((start + end) / 2)\n    const cmp = compare(arr[mid])\n\n    if (cmp === 0) {\n      return [mid, true]\n    }\n\n    if (cmp < 0) {\n      end = mid - 1\n    } else {\n      start = mid + 1\n    }\n  }\n\n  return [start, false]\n}\n\nexport class QueueNode<V> {\n  public value: V\n  public next: QueueNode<V> | null = null\n  public prev: QueueNode<V> | null = null\n\n  constructor(message: V) {\n    this.value = message\n  }\n}\n\nexport class Queue<V> {\n  public first: QueueNode<V> | null\n  public last: QueueNode<V> | null\n\n  constructor() {\n    this.first = null\n    this.last = null\n  }\n\n  enqueue(value: V): boolean {\n    const newNode = new QueueNode(value)\n    if (!this.last) {\n      // list is empty\n      this.first = newNode\n      this.last = newNode\n    } else if (this.last === this.first) {\n      // list has a single element\n      this.last = newNode\n      this.last.prev = this.first\n      this.first.next = newNode\n    } else {\n      // list has elements, add as last\n      newNode.prev = this.last\n      this.last.next = newNode\n      this.last = newNode\n    }\n    return true\n  }\n\n  dequeue(): V | null {\n    if (!this.first) return null\n\n    if (this.first === this.last) {\n      const target = this.first\n      this.first = null\n      this.last = null\n      return target.value\n    }\n\n    const target = this.first\n    this.first = target.next\n    if (this.first) {\n      this.first.prev = null // fix: clean up prev pointer\n    }\n\n    return target.value\n  }\n}\n"],
-  "mappings": ";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAAAA,gBAA2B;AAE3B,oBAAuB;;;ACGvB,mBAAuC;AAHhC,IAAM,cAA2B,IAAI,YAAY,OAAO;AACxD,IAAM,cAA2B,IAAI,YAAY;;;ADIjD,SAAS,OAAO,KAAqB;AAC1C,MAAI,QAAQ;AAEZ,WAAS,IAAI,GAAG,IAAI,IAAI,KAAK,GAAG;AAC9B,UAAM,SAAS,SAAS,IAAI,UAAU,GAAG,IAAI,CAAC,GAAG,EAAE;AACnD,QAAI,WAAW,GAAG;AAChB,eAAS;AAAA,IACX,OAAO;AACL,eAAS,KAAK,MAAM,MAAM;AAC1B;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AACT;AAMO,SAAS,QAAQ,UAAyB,YAAwC;AACvF,MAAI,QAAQ;AAEZ,QAAM,QAAQ;AACd,QAAM,MAAM,CAAC,SAAS,MAAM,SAAS,GAAG,WAAW,SAAS,CAAC;AAE7D,QAAM,KAAK,KAAK,GAAG;AAEnB,SAAO,MAAM;AACX,UAAM,MAAM,KAAK,MAAM,IAAI,KAAK,EAAE,QAAQ,IAAI,GAAI;AAElD,QAAI,QAAQ,MAAM,YAAY;AAC5B,cAAQ;AACR,YAAM,aAAa;AAAA,IACrB;AAEA,QAAI,MAAM,EAAE,OAAO,SAAS;AAE5B,UAAM,KAAK,cAAc,KAAK;AAE9B,QAAI,OAAO,MAAM,EAAE,KAAK,YAAY;AAClC;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AACT;AAEO,SAAS,cAAc,KAA4B;AACxD,aAAO;AAAA,QACL,sBAAO,YAAY,OAAO,KAAK,UAAU,CAAC,GAAG,IAAI,QAAQ,IAAI,YAAY,IAAI,MAAM,IAAI,MAAM,IAAI,OAAO,CAAC,CAAC,CAAC;AAAA,EAC7G;AACF;",
+  "sourcesContent": ["import { bytesToHex } from '@noble/hashes/utils.js'\nimport { type UnsignedEvent, type Event } from './pure.ts'\nimport { sha256 } from '@noble/hashes/sha2.js'\n\nimport { utf8Encoder } from './utils.ts'\n\n/** Get POW difficulty from a Nostr hex ID. */\nexport function getPow(hex: string): number {\n  let count = 0\n\n  for (let i = 0; i < 64; i += 8) {\n    const nibble = parseInt(hex.substring(i, i + 8), 16)\n    if (nibble === 0) {\n      count += 32\n    } else {\n      count += Math.clz32(nibble)\n      break\n    }\n  }\n\n  return count\n}\n\n/**\n * Mine an event with the desired POW. This function mutates the event.\n * Note that this operation is synchronous and should be run in a worker context to avoid blocking the main thread.\n */\nexport function minePow(unsigned: UnsignedEvent, difficulty: number): Omit<Event, 'sig'> {\n  let count = 0\n\n  const event = unsigned as Omit<Event, 'sig'>\n  const tag = ['nonce', count.toString(), difficulty.toString()]\n\n  event.tags.push(tag)\n\n  while (true) {\n    const now = Math.floor(new Date().getTime() / 1000)\n\n    if (now !== event.created_at) {\n      count = 0\n      event.created_at = now\n    }\n\n    tag[1] = (++count).toString()\n\n    event.id = fastEventHash(event)\n\n    if (getPow(event.id) >= difficulty) {\n      break\n    }\n  }\n\n  return event\n}\n\nexport function fastEventHash(evt: UnsignedEvent): string {\n  return bytesToHex(\n    sha256(utf8Encoder.encode(JSON.stringify([0, evt.pubkey, evt.created_at, evt.kind, evt.tags, evt.content]))),\n  )\n}\n", "import type { Event } from './core.ts'\n\nexport const utf8Decoder: TextDecoder = new TextDecoder('utf-8')\nexport const utf8Encoder: TextEncoder = new TextEncoder()\n\nexport { bytesToHex, hexToBytes } from '@noble/hashes/utils.js'\n\nexport function normalizeURL(url: string): string {\n  try {\n    if (url.indexOf('://') === -1) url = 'wss://' + url\n    let p = new URL(url)\n    if (p.protocol === 'http:') p.protocol = 'ws:'\n    else if (p.protocol === 'https:') p.protocol = 'wss:'\n    p.pathname = p.pathname.replace(/\\/+/g, '/')\n    if (p.pathname.endsWith('/')) p.pathname = p.pathname.slice(0, -1)\n    if ((p.port === '80' && p.protocol === 'ws:') || (p.port === '443' && p.protocol === 'wss:')) p.port = ''\n    p.searchParams.sort()\n    p.hash = ''\n    return p.toString()\n  } catch (e) {\n    throw new Error(`Invalid URL: ${url}`)\n  }\n}\n\nexport function insertEventIntoDescendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return b.created_at - event.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function insertEventIntoAscendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return event.created_at - b.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function binarySearch<T>(arr: T[], compare: (b: T) => number): [number, boolean] {\n  let start = 0\n  let end = arr.length - 1\n\n  while (start <= end) {\n    const mid = Math.floor((start + end) / 2)\n    const cmp = compare(arr[mid])\n\n    if (cmp === 0) {\n      return [mid, true]\n    }\n\n    if (cmp < 0) {\n      end = mid - 1\n    } else {\n      start = mid + 1\n    }\n  }\n\n  return [start, false]\n}\n\nexport class QueueNode<V> {\n  public value: V\n  public next: QueueNode<V> | null = null\n  public prev: QueueNode<V> | null = null\n\n  constructor(message: V) {\n    this.value = message\n  }\n}\n\nexport class Queue<V> {\n  public first: QueueNode<V> | null\n  public last: QueueNode<V> | null\n\n  constructor() {\n    this.first = null\n    this.last = null\n  }\n\n  enqueue(value: V): boolean {\n    const newNode = new QueueNode(value)\n    if (!this.last) {\n      // list is empty\n      this.first = newNode\n      this.last = newNode\n    } else if (this.last === this.first) {\n      // list has a single element\n      this.last = newNode\n      this.last.prev = this.first\n      this.first.next = newNode\n    } else {\n      // list has elements, add as last\n      newNode.prev = this.last\n      this.last.next = newNode\n      this.last = newNode\n    }\n    return true\n  }\n\n  dequeue(): V | null {\n    if (!this.first) return null\n\n    if (this.first === this.last) {\n      const target = this.first\n      this.first = null\n      this.last = null\n      return target.value\n    }\n\n    const target = this.first\n    this.first = target.next\n    if (this.first) {\n      this.first.prev = null // fix: clean up prev pointer\n    }\n\n    return target.value\n  }\n}\n"],
+  "mappings": ";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAAAA,gBAA2B;AAE3B,kBAAuB;;;ACGvB,mBAAuC;AAHhC,IAAM,cAA2B,IAAI,YAAY,OAAO;AACxD,IAAM,cAA2B,IAAI,YAAY;;;ADIjD,SAAS,OAAO,KAAqB;AAC1C,MAAI,QAAQ;AAEZ,WAAS,IAAI,GAAG,IAAI,IAAI,KAAK,GAAG;AAC9B,UAAM,SAAS,SAAS,IAAI,UAAU,GAAG,IAAI,CAAC,GAAG,EAAE;AACnD,QAAI,WAAW,GAAG;AAChB,eAAS;AAAA,IACX,OAAO;AACL,eAAS,KAAK,MAAM,MAAM;AAC1B;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AACT;AAMO,SAAS,QAAQ,UAAyB,YAAwC;AACvF,MAAI,QAAQ;AAEZ,QAAM,QAAQ;AACd,QAAM,MAAM,CAAC,SAAS,MAAM,SAAS,GAAG,WAAW,SAAS,CAAC;AAE7D,QAAM,KAAK,KAAK,GAAG;AAEnB,SAAO,MAAM;AACX,UAAM,MAAM,KAAK,MAAM,IAAI,KAAK,EAAE,QAAQ,IAAI,GAAI;AAElD,QAAI,QAAQ,MAAM,YAAY;AAC5B,cAAQ;AACR,YAAM,aAAa;AAAA,IACrB;AAEA,QAAI,MAAM,EAAE,OAAO,SAAS;AAE5B,UAAM,KAAK,cAAc,KAAK;AAE9B,QAAI,OAAO,MAAM,EAAE,KAAK,YAAY;AAClC;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AACT;AAEO,SAAS,cAAc,KAA4B;AACxD,aAAO;AAAA,QACL,oBAAO,YAAY,OAAO,KAAK,UAAU,CAAC,GAAG,IAAI,QAAQ,IAAI,YAAY,IAAI,MAAM,IAAI,MAAM,IAAI,OAAO,CAAC,CAAC,CAAC;AAAA,EAC7G;AACF;",
   "names": ["import_utils"]
 }
Index: package/lib/esm/nip13.js.map
===================================================================
--- package/lib/esm/nip13.js.map
+++ package/lib/esm/nip13.js.map
@@ -1,7 +1,7 @@
 {
   "version": 3,
   "sources": ["../../nip13.ts", "../../utils.ts"],
-  "sourcesContent": ["import { bytesToHex } from '@noble/hashes/utils'\nimport { type UnsignedEvent, type Event } from './pure.ts'\nimport { sha256 } from '@noble/hashes/sha256'\n\nimport { utf8Encoder } from './utils.ts'\n\n/** Get POW difficulty from a Nostr hex ID. */\nexport function getPow(hex: string): number {\n  let count = 0\n\n  for (let i = 0; i < 64; i += 8) {\n    const nibble = parseInt(hex.substring(i, i + 8), 16)\n    if (nibble === 0) {\n      count += 32\n    } else {\n      count += Math.clz32(nibble)\n      break\n    }\n  }\n\n  return count\n}\n\n/**\n * Mine an event with the desired POW. This function mutates the event.\n * Note that this operation is synchronous and should be run in a worker context to avoid blocking the main thread.\n */\nexport function minePow(unsigned: UnsignedEvent, difficulty: number): Omit<Event, 'sig'> {\n  let count = 0\n\n  const event = unsigned as Omit<Event, 'sig'>\n  const tag = ['nonce', count.toString(), difficulty.toString()]\n\n  event.tags.push(tag)\n\n  while (true) {\n    const now = Math.floor(new Date().getTime() / 1000)\n\n    if (now !== event.created_at) {\n      count = 0\n      event.created_at = now\n    }\n\n    tag[1] = (++count).toString()\n\n    event.id = fastEventHash(event)\n\n    if (getPow(event.id) >= difficulty) {\n      break\n    }\n  }\n\n  return event\n}\n\nexport function fastEventHash(evt: UnsignedEvent): string {\n  return bytesToHex(\n    sha256(utf8Encoder.encode(JSON.stringify([0, evt.pubkey, evt.created_at, evt.kind, evt.tags, evt.content]))),\n  )\n}\n", "import type { Event } from './core.ts'\n\nexport const utf8Decoder: TextDecoder = new TextDecoder('utf-8')\nexport const utf8Encoder: TextEncoder = new TextEncoder()\n\nexport { bytesToHex, hexToBytes } from '@noble/hashes/utils'\n\nexport function normalizeURL(url: string): string {\n  try {\n    if (url.indexOf('://') === -1) url = 'wss://' + url\n    let p = new URL(url)\n    if (p.protocol === 'http:') p.protocol = 'ws:'\n    else if (p.protocol === 'https:') p.protocol = 'wss:'\n    p.pathname = p.pathname.replace(/\\/+/g, '/')\n    if (p.pathname.endsWith('/')) p.pathname = p.pathname.slice(0, -1)\n    if ((p.port === '80' && p.protocol === 'ws:') || (p.port === '443' && p.protocol === 'wss:')) p.port = ''\n    p.searchParams.sort()\n    p.hash = ''\n    return p.toString()\n  } catch (e) {\n    throw new Error(`Invalid URL: ${url}`)\n  }\n}\n\nexport function insertEventIntoDescendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return b.created_at - event.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function insertEventIntoAscendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return event.created_at - b.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function binarySearch<T>(arr: T[], compare: (b: T) => number): [number, boolean] {\n  let start = 0\n  let end = arr.length - 1\n\n  while (start <= end) {\n    const mid = Math.floor((start + end) / 2)\n    const cmp = compare(arr[mid])\n\n    if (cmp === 0) {\n      return [mid, true]\n    }\n\n    if (cmp < 0) {\n      end = mid - 1\n    } else {\n      start = mid + 1\n    }\n  }\n\n  return [start, false]\n}\n\nexport class QueueNode<V> {\n  public value: V\n  public next: QueueNode<V> | null = null\n  public prev: QueueNode<V> | null = null\n\n  constructor(message: V) {\n    this.value = message\n  }\n}\n\nexport class Queue<V> {\n  public first: QueueNode<V> | null\n  public last: QueueNode<V> | null\n\n  constructor() {\n    this.first = null\n    this.last = null\n  }\n\n  enqueue(value: V): boolean {\n    const newNode = new QueueNode(value)\n    if (!this.last) {\n      // list is empty\n      this.first = newNode\n      this.last = newNode\n    } else if (this.last === this.first) {\n      // list has a single element\n      this.last = newNode\n      this.last.prev = this.first\n      this.first.next = newNode\n    } else {\n      // list has elements, add as last\n      newNode.prev = this.last\n      this.last.next = newNode\n      this.last = newNode\n    }\n    return true\n  }\n\n  dequeue(): V | null {\n    if (!this.first) return null\n\n    if (this.first === this.last) {\n      const target = this.first\n      this.first = null\n      this.last = null\n      return target.value\n    }\n\n    const target = this.first\n    this.first = target.next\n    if (this.first) {\n      this.first.prev = null // fix: clean up prev pointer\n    }\n\n    return target.value\n  }\n}\n"],
+  "sourcesContent": ["import { bytesToHex } from '@noble/hashes/utils.js'\nimport { type UnsignedEvent, type Event } from './pure.ts'\nimport { sha256 } from '@noble/hashes/sha2.js'\n\nimport { utf8Encoder } from './utils.ts'\n\n/** Get POW difficulty from a Nostr hex ID. */\nexport function getPow(hex: string): number {\n  let count = 0\n\n  for (let i = 0; i < 64; i += 8) {\n    const nibble = parseInt(hex.substring(i, i + 8), 16)\n    if (nibble === 0) {\n      count += 32\n    } else {\n      count += Math.clz32(nibble)\n      break\n    }\n  }\n\n  return count\n}\n\n/**\n * Mine an event with the desired POW. This function mutates the event.\n * Note that this operation is synchronous and should be run in a worker context to avoid blocking the main thread.\n */\nexport function minePow(unsigned: UnsignedEvent, difficulty: number): Omit<Event, 'sig'> {\n  let count = 0\n\n  const event = unsigned as Omit<Event, 'sig'>\n  const tag = ['nonce', count.toString(), difficulty.toString()]\n\n  event.tags.push(tag)\n\n  while (true) {\n    const now = Math.floor(new Date().getTime() / 1000)\n\n    if (now !== event.created_at) {\n      count = 0\n      event.created_at = now\n    }\n\n    tag[1] = (++count).toString()\n\n    event.id = fastEventHash(event)\n\n    if (getPow(event.id) >= difficulty) {\n      break\n    }\n  }\n\n  return event\n}\n\nexport function fastEventHash(evt: UnsignedEvent): string {\n  return bytesToHex(\n    sha256(utf8Encoder.encode(JSON.stringify([0, evt.pubkey, evt.created_at, evt.kind, evt.tags, evt.content]))),\n  )\n}\n", "import type { Event } from './core.ts'\n\nexport const utf8Decoder: TextDecoder = new TextDecoder('utf-8')\nexport const utf8Encoder: TextEncoder = new TextEncoder()\n\nexport { bytesToHex, hexToBytes } from '@noble/hashes/utils.js'\n\nexport function normalizeURL(url: string): string {\n  try {\n    if (url.indexOf('://') === -1) url = 'wss://' + url\n    let p = new URL(url)\n    if (p.protocol === 'http:') p.protocol = 'ws:'\n    else if (p.protocol === 'https:') p.protocol = 'wss:'\n    p.pathname = p.pathname.replace(/\\/+/g, '/')\n    if (p.pathname.endsWith('/')) p.pathname = p.pathname.slice(0, -1)\n    if ((p.port === '80' && p.protocol === 'ws:') || (p.port === '443' && p.protocol === 'wss:')) p.port = ''\n    p.searchParams.sort()\n    p.hash = ''\n    return p.toString()\n  } catch (e) {\n    throw new Error(`Invalid URL: ${url}`)\n  }\n}\n\nexport function insertEventIntoDescendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return b.created_at - event.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function insertEventIntoAscendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return event.created_at - b.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function binarySearch<T>(arr: T[], compare: (b: T) => number): [number, boolean] {\n  let start = 0\n  let end = arr.length - 1\n\n  while (start <= end) {\n    const mid = Math.floor((start + end) / 2)\n    const cmp = compare(arr[mid])\n\n    if (cmp === 0) {\n      return [mid, true]\n    }\n\n    if (cmp < 0) {\n      end = mid - 1\n    } else {\n      start = mid + 1\n    }\n  }\n\n  return [start, false]\n}\n\nexport class QueueNode<V> {\n  public value: V\n  public next: QueueNode<V> | null = null\n  public prev: QueueNode<V> | null = null\n\n  constructor(message: V) {\n    this.value = message\n  }\n}\n\nexport class Queue<V> {\n  public first: QueueNode<V> | null\n  public last: QueueNode<V> | null\n\n  constructor() {\n    this.first = null\n    this.last = null\n  }\n\n  enqueue(value: V): boolean {\n    const newNode = new QueueNode(value)\n    if (!this.last) {\n      // list is empty\n      this.first = newNode\n      this.last = newNode\n    } else if (this.last === this.first) {\n      // list has a single element\n      this.last = newNode\n      this.last.prev = this.first\n      this.first.next = newNode\n    } else {\n      // list has elements, add as last\n      newNode.prev = this.last\n      this.last.next = newNode\n      this.last = newNode\n    }\n    return true\n  }\n\n  dequeue(): V | null {\n    if (!this.first) return null\n\n    if (this.first === this.last) {\n      const target = this.first\n      this.first = null\n      this.last = null\n      return target.value\n    }\n\n    const target = this.first\n    this.first = target.next\n    if (this.first) {\n      this.first.prev = null // fix: clean up prev pointer\n    }\n\n    return target.value\n  }\n}\n"],
   "mappings": ";AAAA,SAAS,cAAAA,mBAAkB;AAE3B,SAAS,cAAc;;;ACGvB,SAAS,YAAY,kBAAkB;AAHhC,IAAM,cAA2B,IAAI,YAAY,OAAO;AACxD,IAAM,cAA2B,IAAI,YAAY;;;ADIjD,SAAS,OAAO,KAAqB;AAC1C,MAAI,QAAQ;AAEZ,WAAS,IAAI,GAAG,IAAI,IAAI,KAAK,GAAG;AAC9B,UAAM,SAAS,SAAS,IAAI,UAAU,GAAG,IAAI,CAAC,GAAG,EAAE;AACnD,QAAI,WAAW,GAAG;AAChB,eAAS;AAAA,IACX,OAAO;AACL,eAAS,KAAK,MAAM,MAAM;AAC1B;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AACT;AAMO,SAAS,QAAQ,UAAyB,YAAwC;AACvF,MAAI,QAAQ;AAEZ,QAAM,QAAQ;AACd,QAAM,MAAM,CAAC,SAAS,MAAM,SAAS,GAAG,WAAW,SAAS,CAAC;AAE7D,QAAM,KAAK,KAAK,GAAG;AAEnB,SAAO,MAAM;AACX,UAAM,MAAM,KAAK,MAAM,IAAI,KAAK,EAAE,QAAQ,IAAI,GAAI;AAElD,QAAI,QAAQ,MAAM,YAAY;AAC5B,cAAQ;AACR,YAAM,aAAa;AAAA,IACrB;AAEA,QAAI,MAAM,EAAE,OAAO,SAAS;AAE5B,UAAM,KAAK,cAAc,KAAK;AAE9B,QAAI,OAAO,MAAM,EAAE,KAAK,YAAY;AAClC;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AACT;AAEO,SAAS,cAAc,KAA4B;AACxD,SAAOC;AAAA,IACL,OAAO,YAAY,OAAO,KAAK,UAAU,CAAC,GAAG,IAAI,QAAQ,IAAI,YAAY,IAAI,MAAM,IAAI,MAAM,IAAI,OAAO,CAAC,CAAC,CAAC;AAAA,EAC7G;AACF;",
   "names": ["bytesToHex", "bytesToHex"]
 }
Index: package/lib/cjs/nip17.js.map
===================================================================
--- package/lib/cjs/nip17.js.map
+++ package/lib/cjs/nip17.js.map
@@ -1,7 +1,7 @@
 {
   "version": 3,
   "sources": ["../../nip17.ts", "../../pure.ts", "../../core.ts", "../../utils.ts", "../../kinds.ts", "../../nip44.ts", "../../nip59.ts"],
-  "sourcesContent": ["import { PrivateDirectMessage } from './kinds.ts'\nimport { EventTemplate, NostrEvent, getPublicKey } from './pure.ts'\nimport * as nip59 from './nip59.ts'\n\ntype Recipient = {\n  publicKey: string\n  relayUrl?: string\n}\n\ntype ReplyTo = {\n  eventId: string\n  relayUrl?: string\n}\n\nfunction createEvent(\n  recipients: Recipient | Recipient[],\n  message: string,\n  conversationTitle?: string,\n  replyTo?: ReplyTo,\n): EventTemplate {\n  const baseEvent: EventTemplate = {\n    created_at: Math.ceil(Date.now() / 1000),\n    kind: PrivateDirectMessage,\n    tags: [],\n    content: message,\n  }\n\n  const recipientsArray = Array.isArray(recipients) ? recipients : [recipients]\n\n  recipientsArray.forEach(({ publicKey, relayUrl }) => {\n    baseEvent.tags.push(relayUrl ? ['p', publicKey, relayUrl] : ['p', publicKey])\n  })\n\n  if (replyTo) {\n    baseEvent.tags.push(['e', replyTo.eventId, replyTo.relayUrl || '', 'reply'])\n  }\n\n  if (conversationTitle) {\n    baseEvent.tags.push(['subject', conversationTitle])\n  }\n\n  return baseEvent\n}\n\nexport function wrapEvent(\n  senderPrivateKey: Uint8Array,\n  recipient: Recipient,\n  message: string,\n  conversationTitle?: string,\n  replyTo?: ReplyTo,\n): NostrEvent {\n  const event = createEvent(recipient, message, conversationTitle, replyTo)\n  return nip59.wrapEvent(event, senderPrivateKey, recipient.publicKey)\n}\n\nexport function wrapManyEvents(\n  senderPrivateKey: Uint8Array,\n  recipients: Recipient[],\n  message: string,\n  conversationTitle?: string,\n  replyTo?: ReplyTo,\n): NostrEvent[] {\n  if (!recipients || recipients.length === 0) {\n    throw new Error('At least one recipient is required.')\n  }\n\n  const senderPublicKey = getPublicKey(senderPrivateKey)\n\n  // wrap the event for the sender and then for each recipient\n  return [{ publicKey: senderPublicKey }, ...recipients].map(recipient =>\n    wrapEvent(senderPrivateKey, recipient, message, conversationTitle, replyTo),\n  )\n}\n\nexport const unwrapEvent = nip59.unwrapEvent\n\nexport const unwrapManyEvents = nip59.unwrapManyEvents\n", "import { schnorr } from '@noble/curves/secp256k1'\nimport { bytesToHex } from '@noble/hashes/utils'\nimport { Nostr, Event, EventTemplate, UnsignedEvent, VerifiedEvent, verifiedSymbol, validateEvent } from './core.ts'\nimport { sha256 } from '@noble/hashes/sha256'\n\nimport { utf8Encoder } from './utils.ts'\n\nclass JS implements Nostr {\n  generateSecretKey(): Uint8Array {\n    return schnorr.utils.randomPrivateKey()\n  }\n  getPublicKey(secretKey: Uint8Array): string {\n    return bytesToHex(schnorr.getPublicKey(secretKey))\n  }\n  finalizeEvent(t: EventTemplate, secretKey: Uint8Array): VerifiedEvent {\n    const event = t as VerifiedEvent\n    event.pubkey = bytesToHex(schnorr.getPublicKey(secretKey))\n    event.id = getEventHash(event)\n    event.sig = bytesToHex(schnorr.sign(getEventHash(event), secretKey))\n    event[verifiedSymbol] = true\n    return event\n  }\n  verifyEvent(event: Event): event is VerifiedEvent {\n    if (typeof event[verifiedSymbol] === 'boolean') return event[verifiedSymbol]\n\n    const hash = getEventHash(event)\n    if (hash !== event.id) {\n      event[verifiedSymbol] = false\n      return false\n    }\n\n    try {\n      const valid = schnorr.verify(event.sig, hash, event.pubkey)\n      event[verifiedSymbol] = valid\n      return valid\n    } catch (err) {\n      event[verifiedSymbol] = false\n      return false\n    }\n  }\n}\n\nexport function serializeEvent(evt: UnsignedEvent): string {\n  if (!validateEvent(evt)) throw new Error(\"can't serialize event with wrong or missing properties\")\n  return JSON.stringify([0, evt.pubkey, evt.created_at, evt.kind, evt.tags, evt.content])\n}\n\nexport function getEventHash(event: UnsignedEvent): string {\n  let eventHash = sha256(utf8Encoder.encode(serializeEvent(event)))\n  return bytesToHex(eventHash)\n}\n\nconst i: JS = new JS()\n\nexport const generateSecretKey = i.generateSecretKey\nexport const getPublicKey = i.getPublicKey\nexport const finalizeEvent = i.finalizeEvent\nexport const verifyEvent = i.verifyEvent\nexport * from './core.ts'\n", "export interface Nostr {\n  generateSecretKey(): Uint8Array\n  getPublicKey(secretKey: Uint8Array): string\n  finalizeEvent(event: EventTemplate, secretKey: Uint8Array): VerifiedEvent\n  verifyEvent(event: Event): event is VerifiedEvent\n}\n\n/** Designates a verified event signature. */\nexport const verifiedSymbol = Symbol('verified')\n\nexport type NostrEvent = {\n  kind: number\n  tags: string[][]\n  content: string\n  created_at: number\n  pubkey: string\n  id: string\n  sig: string\n  [verifiedSymbol]?: boolean\n}\n\nexport type Event = NostrEvent\nexport type EventTemplate = Pick<Event, 'kind' | 'tags' | 'content' | 'created_at'>\nexport type UnsignedEvent = Pick<Event, 'kind' | 'tags' | 'content' | 'created_at' | 'pubkey'>\n\n/** An event whose signature has been verified. */\nexport interface VerifiedEvent extends Event {\n  [verifiedSymbol]: true\n}\n\nconst isRecord = (obj: unknown): obj is Record<string, unknown> => obj instanceof Object\n\nexport function validateEvent<T>(event: T): event is T & UnsignedEvent {\n  if (!isRecord(event)) return false\n  if (typeof event.kind !== 'number') return false\n  if (typeof event.content !== 'string') return false\n  if (typeof event.created_at !== 'number') return false\n  if (typeof event.pubkey !== 'string') return false\n  if (!event.pubkey.match(/^[a-f0-9]{64}$/)) return false\n\n  if (!Array.isArray(event.tags)) return false\n  for (let i = 0; i < event.tags.length; i++) {\n    let tag = event.tags[i]\n    if (!Array.isArray(tag)) return false\n    for (let j = 0; j < tag.length; j++) {\n      if (typeof tag[j] !== 'string') return false\n    }\n  }\n\n  return true\n}\n\n/**\n * Sort events in reverse-chronological order by the `created_at` timestamp,\n * and then by the event `id` (lexicographically) in case of ties.\n * This mutates the array.\n */\nexport function sortEvents(events: Event[]): Event[] {\n  return events.sort((a: NostrEvent, b: NostrEvent): number => {\n    if (a.created_at !== b.created_at) {\n      return b.created_at - a.created_at\n    }\n    return a.id.localeCompare(b.id)\n  })\n}\n", "import type { Event } from './core.ts'\n\nexport const utf8Decoder: TextDecoder = new TextDecoder('utf-8')\nexport const utf8Encoder: TextEncoder = new TextEncoder()\n\nexport { bytesToHex, hexToBytes } from '@noble/hashes/utils'\n\nexport function normalizeURL(url: string): string {\n  try {\n    if (url.indexOf('://') === -1) url = 'wss://' + url\n    let p = new URL(url)\n    if (p.protocol === 'http:') p.protocol = 'ws:'\n    else if (p.protocol === 'https:') p.protocol = 'wss:'\n    p.pathname = p.pathname.replace(/\\/+/g, '/')\n    if (p.pathname.endsWith('/')) p.pathname = p.pathname.slice(0, -1)\n    if ((p.port === '80' && p.protocol === 'ws:') || (p.port === '443' && p.protocol === 'wss:')) p.port = ''\n    p.searchParams.sort()\n    p.hash = ''\n    return p.toString()\n  } catch (e) {\n    throw new Error(`Invalid URL: ${url}`)\n  }\n}\n\nexport function insertEventIntoDescendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return b.created_at - event.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function insertEventIntoAscendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return event.created_at - b.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function binarySearch<T>(arr: T[], compare: (b: T) => number): [number, boolean] {\n  let start = 0\n  let end = arr.length - 1\n\n  while (start <= end) {\n    const mid = Math.floor((start + end) / 2)\n    const cmp = compare(arr[mid])\n\n    if (cmp === 0) {\n      return [mid, true]\n    }\n\n    if (cmp < 0) {\n      end = mid - 1\n    } else {\n      start = mid + 1\n    }\n  }\n\n  return [start, false]\n}\n\nexport class QueueNode<V> {\n  public value: V\n  public next: QueueNode<V> | null = null\n  public prev: QueueNode<V> | null = null\n\n  constructor(message: V) {\n    this.value = message\n  }\n}\n\nexport class Queue<V> {\n  public first: QueueNode<V> | null\n  public last: QueueNode<V> | null\n\n  constructor() {\n    this.first = null\n    this.last = null\n  }\n\n  enqueue(value: V): boolean {\n    const newNode = new QueueNode(value)\n    if (!this.last) {\n      // list is empty\n      this.first = newNode\n      this.last = newNode\n    } else if (this.last === this.first) {\n      // list has a single element\n      this.last = newNode\n      this.last.prev = this.first\n      this.first.next = newNode\n    } else {\n      // list has elements, add as last\n      newNode.prev = this.last\n      this.last.next = newNode\n      this.last = newNode\n    }\n    return true\n  }\n\n  dequeue(): V | null {\n    if (!this.first) return null\n\n    if (this.first === this.last) {\n      const target = this.first\n      this.first = null\n      this.last = null\n      return target.value\n    }\n\n    const target = this.first\n    this.first = target.next\n    if (this.first) {\n      this.first.prev = null // fix: clean up prev pointer\n    }\n\n    return target.value\n  }\n}\n", "import { NostrEvent, validateEvent } from './pure.ts'\n\n/** Events are **regular**, which means they're all expected to be stored by relays. */\nexport function isRegularKind(kind: number): boolean {\n  return kind < 10000 && kind !== 0 && kind !== 3\n}\n\n/** Events are **replaceable**, which means that, for each combination of `pubkey` and `kind`, only the latest event is expected to (SHOULD) be stored by relays, older versions are expected to be discarded. */\nexport function isReplaceableKind(kind: number): boolean {\n  return kind === 0 || kind === 3 || (10000 <= kind && kind < 20000)\n}\n\n/** Events are **ephemeral**, which means they are not expected to be stored by relays. */\nexport function isEphemeralKind(kind: number): boolean {\n  return 20000 <= kind && kind < 30000\n}\n\n/** Events are **addressable**, which means that, for each combination of `pubkey`, `kind` and the `d` tag, only the latest event is expected to be stored by relays, older versions are expected to be discarded. */\nexport function isAddressableKind(kind: number): boolean {\n  return 30000 <= kind && kind < 40000\n}\n\n/** Classification of the event kind. */\nexport type KindClassification = 'regular' | 'replaceable' | 'ephemeral' | 'parameterized' | 'unknown'\n\n/** Determine the classification of this kind of event if known, or `unknown`. */\nexport function classifyKind(kind: number): KindClassification {\n  if (isRegularKind(kind)) return 'regular'\n  if (isReplaceableKind(kind)) return 'replaceable'\n  if (isEphemeralKind(kind)) return 'ephemeral'\n  if (isAddressableKind(kind)) return 'parameterized'\n  return 'unknown'\n}\n\nexport function isKind<T extends number>(event: unknown, kind: T | Array<T>): event is NostrEvent & { kind: T } {\n  const kindAsArray: number[] = kind instanceof Array ? kind : [kind]\n  return (validateEvent(event) && kindAsArray.includes(event.kind)) || false\n}\n\nexport const Metadata = 0\nexport type Metadata = typeof Metadata\nexport const ShortTextNote = 1\nexport type ShortTextNote = typeof ShortTextNote\nexport const RecommendRelay = 2\nexport type RecommendRelay = typeof RecommendRelay\nexport const Contacts = 3\nexport type Contacts = typeof Contacts\nexport const EncryptedDirectMessage = 4\nexport type EncryptedDirectMessage = typeof EncryptedDirectMessage\nexport const EventDeletion = 5\nexport type EventDeletion = typeof EventDeletion\nexport const Repost = 6\nexport type Repost = typeof Repost\nexport const Reaction = 7\nexport type Reaction = typeof Reaction\nexport const BadgeAward = 8\nexport type BadgeAward = typeof BadgeAward\nexport const ChatMessage = 9\nexport type ChatMessage = typeof ChatMessage\nexport const ForumThread = 11\nexport type ForumThread = typeof ForumThread\nexport const Seal = 13\nexport type Seal = typeof Seal\nexport const PrivateDirectMessage = 14\nexport type PrivateDirectMessage = typeof PrivateDirectMessage\nexport const FileMessage = 15\nexport type FileMessage = typeof FileMessage\nexport const GenericRepost = 16\nexport type GenericRepost = typeof GenericRepost\nexport const Photo = 20\nexport type Photo = typeof Photo\nexport const NormalVideo = 21\nexport type NormalVideo = typeof NormalVideo\nexport const ShortVideo = 22\nexport type ShortVideo = typeof ShortVideo\nexport const ChannelCreation = 40\nexport type ChannelCreation = typeof ChannelCreation\nexport const ChannelMetadata = 41\nexport type ChannelMetadata = typeof ChannelMetadata\nexport const ChannelMessage = 42\nexport type ChannelMessage = typeof ChannelMessage\nexport const ChannelHideMessage = 43\nexport type ChannelHideMessage = typeof ChannelHideMessage\nexport const ChannelMuteUser = 44\nexport type ChannelMuteUser = typeof ChannelMuteUser\nexport const OpenTimestamps = 1040\nexport type OpenTimestamps = typeof OpenTimestamps\nexport const GiftWrap = 1059\nexport type GiftWrap = typeof GiftWrap\nexport const Poll = 1068\nexport type Poll = typeof Poll\nexport const FileMetadata = 1063\nexport type FileMetadata = typeof FileMetadata\nexport const Comment = 1111\nexport type Comment = typeof Comment\nexport const LiveChatMessage = 1311\nexport type LiveChatMessage = typeof LiveChatMessage\nexport const Voice = 1222\nexport type Voice = typeof Voice\nexport const VoiceComment = 1244\nexport type VoiceComment = typeof VoiceComment\nexport const ProblemTracker = 1971\nexport type ProblemTracker = typeof ProblemTracker\nexport const Report = 1984\nexport type Report = typeof Report\nexport const Reporting = 1984\nexport type Reporting = typeof Reporting\nexport const Label = 1985\nexport type Label = typeof Label\nexport const CommunityPostApproval = 4550\nexport type CommunityPostApproval = typeof CommunityPostApproval\nexport const JobRequest = 5999\nexport type JobRequest = typeof JobRequest\nexport const JobResult = 6999\nexport type JobResult = typeof JobResult\nexport const JobFeedback = 7000\nexport type JobFeedback = typeof JobFeedback\nexport const ZapGoal = 9041\nexport type ZapGoal = typeof ZapGoal\nexport const ZapRequest = 9734\nexport type ZapRequest = typeof ZapRequest\nexport const Zap = 9735\nexport type Zap = typeof Zap\nexport const Highlights = 9802\nexport type Highlights = typeof Highlights\nexport const PollResponse = 1018\nexport type PollResponse = typeof PollResponse\nexport const Mutelist = 10000\nexport type Mutelist = typeof Mutelist\nexport const Pinlist = 10001\nexport type Pinlist = typeof Pinlist\nexport const RelayList = 10002\nexport type RelayList = typeof RelayList\nexport const BookmarkList = 10003\nexport type BookmarkList = typeof BookmarkList\nexport const CommunitiesList = 10004\nexport type CommunitiesList = typeof CommunitiesList\nexport const PublicChatsList = 10005\nexport type PublicChatsList = typeof PublicChatsList\nexport const BlockedRelaysList = 10006\nexport type BlockedRelaysList = typeof BlockedRelaysList\nexport const SearchRelaysList = 10007\nexport type SearchRelaysList = typeof SearchRelaysList\nexport const FavoriteRelays = 10012\nexport type FavoriteRelays = typeof FavoriteRelays\nexport const InterestsList = 10015\nexport type InterestsList = typeof InterestsList\nexport const UserEmojiList = 10030\nexport type UserEmojiList = typeof UserEmojiList\nexport const DirectMessageRelaysList = 10050\nexport type DirectMessageRelaysList = typeof DirectMessageRelaysList\nexport const FileServerPreference = 10096\nexport type FileServerPreference = typeof FileServerPreference\nexport const BlossomServerList = 10063\nexport type BlossomServerList = typeof BlossomServerList\nexport const NWCWalletInfo = 13194\nexport type NWCWalletInfo = typeof NWCWalletInfo\nexport const LightningPubRPC = 21000\nexport type LightningPubRPC = typeof LightningPubRPC\nexport const ClientAuth = 22242\nexport type ClientAuth = typeof ClientAuth\nexport const NWCWalletRequest = 23194\nexport type NWCWalletRequest = typeof NWCWalletRequest\nexport const NWCWalletResponse = 23195\nexport type NWCWalletResponse = typeof NWCWalletResponse\nexport const NostrConnect = 24133\nexport type NostrConnect = typeof NostrConnect\nexport const HTTPAuth = 27235\nexport type HTTPAuth = typeof HTTPAuth\nexport const Followsets = 30000\nexport type Followsets = typeof Followsets\nexport const Genericlists = 30001\nexport type Genericlists = typeof Genericlists\nexport const Relaysets = 30002\nexport type Relaysets = typeof Relaysets\nexport const Bookmarksets = 30003\nexport type Bookmarksets = typeof Bookmarksets\nexport const Curationsets = 30004\nexport type Curationsets = typeof Curationsets\nexport const ProfileBadges = 30008\nexport type ProfileBadges = typeof ProfileBadges\nexport const BadgeDefinition = 30009\nexport type BadgeDefinition = typeof BadgeDefinition\nexport const Interestsets = 30015\nexport type Interestsets = typeof Interestsets\nexport const CreateOrUpdateStall = 30017\nexport type CreateOrUpdateStall = typeof CreateOrUpdateStall\nexport const CreateOrUpdateProduct = 30018\nexport type CreateOrUpdateProduct = typeof CreateOrUpdateProduct\nexport const LongFormArticle = 30023\nexport type LongFormArticle = typeof LongFormArticle\nexport const DraftLong = 30024\nexport type DraftLong = typeof DraftLong\nexport const Emojisets = 30030\nexport type Emojisets = typeof Emojisets\nexport const Application = 30078\nexport type Application = typeof Application\nexport const LiveEvent = 30311\nexport type LiveEvent = typeof LiveEvent\nexport const UserStatuses = 30315\nexport type UserStatuses = typeof UserStatuses\nexport const ClassifiedListing = 30402\nexport type ClassifiedListing = typeof ClassifiedListing\nexport const DraftClassifiedListing = 30403\nexport type DraftClassifiedListing = typeof DraftClassifiedListing\nexport const Date = 31922\nexport type Date = typeof Date\nexport const Time = 31923\nexport type Time = typeof Time\nexport const Calendar = 31924\nexport type Calendar = typeof Calendar\nexport const CalendarEventRSVP = 31925\nexport type CalendarEventRSVP = typeof CalendarEventRSVP\nexport const RelayReview = 31987\nexport type RelayReview = typeof RelayReview\nexport const Handlerrecommendation = 31989\nexport type Handlerrecommendation = typeof Handlerrecommendation\nexport const Handlerinformation = 31990\nexport type Handlerinformation = typeof Handlerinformation\nexport const CommunityDefinition = 34550\nexport type CommunityDefinition = typeof CommunityDefinition\nexport const GroupMetadata = 39000\nexport type GroupMetadata = typeof GroupMetadata\n", "import { chacha20 } from '@noble/ciphers/chacha'\nimport { equalBytes } from '@noble/ciphers/utils'\nimport { secp256k1 } from '@noble/curves/secp256k1'\nimport { extract as hkdf_extract, expand as hkdf_expand } from '@noble/hashes/hkdf'\nimport { hmac } from '@noble/hashes/hmac'\nimport { sha256 } from '@noble/hashes/sha256'\nimport { concatBytes, randomBytes } from '@noble/hashes/utils'\nimport { base64 } from '@scure/base'\n\nimport { utf8Decoder, utf8Encoder } from './utils.ts'\n\nconst minPlaintextSize = 0x0001 // 1b msg => padded to 32b\nconst maxPlaintextSize = 0xffff // 65535 (64kb-1) => padded to 64kb\n\nexport function getConversationKey(privkeyA: Uint8Array, pubkeyB: string): Uint8Array {\n  const sharedX = secp256k1.getSharedSecret(privkeyA, '02' + pubkeyB).subarray(1, 33)\n  return hkdf_extract(sha256, sharedX, 'nip44-v2')\n}\n\nfunction getMessageKeys(\n  conversationKey: Uint8Array,\n  nonce: Uint8Array,\n): { chacha_key: Uint8Array; chacha_nonce: Uint8Array; hmac_key: Uint8Array } {\n  const keys = hkdf_expand(sha256, conversationKey, nonce, 76)\n  return {\n    chacha_key: keys.subarray(0, 32),\n    chacha_nonce: keys.subarray(32, 44),\n    hmac_key: keys.subarray(44, 76),\n  }\n}\n\nfunction calcPaddedLen(len: number): number {\n  if (!Number.isSafeInteger(len) || len < 1) throw new Error('expected positive integer')\n  if (len <= 32) return 32\n  const nextPower = 1 << (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 > maxPlaintextSize)\n    throw new Error('invalid plaintext size: must be between 1 and 65535 bytes')\n  const arr = new Uint8Array(2)\n  new DataView(arr.buffer).setUint16(0, num, false)\n  return arr\n}\n\nfunction pad(plaintext: string): Uint8Array {\n  const unpadded = utf8Encoder.encode(plaintext)\n  const unpaddedLen = unpadded.length\n  const prefix = 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 unpaddedLen = new DataView(padded.buffer).getUint16(0)\n  const unpadded = padded.subarray(2, 2 + unpaddedLen)\n  if (\n    unpaddedLen < minPlaintextSize ||\n    unpaddedLen > maxPlaintextSize ||\n    unpadded.length !== unpaddedLen ||\n    padded.length !== 2 + calcPaddedLen(unpaddedLen)\n  )\n    throw new Error('invalid padding')\n  return utf8Decoder.decode(unpadded)\n}\n\nfunction hmacAad(key: Uint8Array, message: Uint8Array, aad: Uint8Array): Uint8Array {\n  if (aad.length !== 32) throw new Error('AAD associated data must be 32 bytes')\n  const combined = concatBytes(aad, message)\n  return hmac(sha256, key, combined)\n}\n\n// metadata: always 65b (version: 1b, nonce: 32b, max: 32b)\n// plaintext: 1b to 0xffff\n// padded plaintext: 32b to 0xffff\n// ciphertext: 32b+2 to 0xffff+2\n// raw payload: 99 (65+32+2) to 65603 (65+0xffff+2)\n// compressed payload (base64): 132b to 87472b\nfunction decodePayload(payload: string): { nonce: Uint8Array; ciphertext: Uint8Array; mac: Uint8Array } {\n  if (typeof payload !== 'string') throw new Error('payload must be a valid string')\n  const plen = payload.length\n  if (plen < 132 || plen > 87472) throw new Error('invalid payload length: ' + plen)\n  if (payload[0] === '#') throw new Error('unknown encryption version')\n  let data: Uint8Array\n  try {\n    data = base64.decode(payload)\n  } catch (error) {\n    throw new Error('invalid base64: ' + (error as any).message)\n  }\n  const dlen = data.length\n  if (dlen < 99 || dlen > 65603) throw new Error('invalid data length: ' + dlen)\n  const vers = data[0]\n  if (vers !== 2) throw new Error('unknown encryption version ' + vers)\n  return {\n    nonce: data.subarray(1, 33),\n    ciphertext: data.subarray(33, -32),\n    mac: data.subarray(-32),\n  }\n}\n\nexport function encrypt(plaintext: string, conversationKey: Uint8Array, nonce: Uint8Array = randomBytes(32)): string {\n  const { chacha_key, chacha_nonce, hmac_key } = getMessageKeys(conversationKey, nonce)\n  const padded = pad(plaintext)\n  const ciphertext = chacha20(chacha_key, chacha_nonce, padded)\n  const mac = hmacAad(hmac_key, ciphertext, nonce)\n  return base64.encode(concatBytes(new Uint8Array([2]), nonce, ciphertext, mac))\n}\n\nexport function decrypt(payload: string, conversationKey: Uint8Array): string {\n  const { nonce, ciphertext, mac } = decodePayload(payload)\n  const { chacha_key, chacha_nonce, hmac_key } = getMessageKeys(conversationKey, nonce)\n  const calculatedMac = hmacAad(hmac_key, ciphertext, nonce)\n  if (!equalBytes(calculatedMac, mac)) throw new Error('invalid MAC')\n  const padded = chacha20(chacha_key, chacha_nonce, ciphertext)\n  return unpad(padded)\n}\n\nexport const v2 = {\n  utils: {\n    getConversationKey,\n    calcPaddedLen,\n  },\n  encrypt,\n  decrypt,\n}\n", "import { EventTemplate, UnsignedEvent, NostrEvent } from './core.ts'\nimport { getConversationKey, decrypt, encrypt } from './nip44.ts'\nimport { getEventHash, generateSecretKey, finalizeEvent, getPublicKey } from './pure.ts'\nimport { Seal, GiftWrap } from './kinds.ts'\n\ntype Rumor = UnsignedEvent & { id: string }\n\nconst TWO_DAYS = 2 * 24 * 60 * 60\n\nconst now = () => Math.round(Date.now() / 1000)\nconst randomNow = () => Math.round(now() - Math.random() * TWO_DAYS)\n\nconst nip44ConversationKey = (privateKey: Uint8Array, publicKey: string) => getConversationKey(privateKey, publicKey)\n\nconst nip44Encrypt = (data: EventTemplate, privateKey: Uint8Array, publicKey: string) =>\n  encrypt(JSON.stringify(data), nip44ConversationKey(privateKey, publicKey))\n\nconst nip44Decrypt = (data: NostrEvent, privateKey: Uint8Array) =>\n  JSON.parse(decrypt(data.content, nip44ConversationKey(privateKey, data.pubkey)))\n\nexport function createRumor(event: Partial<UnsignedEvent>, privateKey: Uint8Array): Rumor {\n  const rumor = {\n    created_at: now(),\n    content: '',\n    tags: [],\n    ...event,\n    pubkey: getPublicKey(privateKey),\n  } as any\n\n  rumor.id = getEventHash(rumor)\n\n  return rumor as Rumor\n}\n\nexport function createSeal(rumor: Rumor, privateKey: Uint8Array, recipientPublicKey: string): NostrEvent {\n  return finalizeEvent(\n    {\n      kind: Seal,\n      content: nip44Encrypt(rumor, privateKey, recipientPublicKey),\n      created_at: randomNow(),\n      tags: [],\n    },\n    privateKey,\n  )\n}\n\nexport function createWrap(seal: NostrEvent, recipientPublicKey: string): NostrEvent {\n  const randomKey = generateSecretKey()\n\n  return finalizeEvent(\n    {\n      kind: GiftWrap,\n      content: nip44Encrypt(seal, randomKey, recipientPublicKey),\n      created_at: randomNow(),\n      tags: [['p', recipientPublicKey]],\n    },\n    randomKey,\n  ) as NostrEvent\n}\n\nexport function wrapEvent(\n  event: Partial<UnsignedEvent>,\n  senderPrivateKey: Uint8Array,\n  recipientPublicKey: string,\n): NostrEvent {\n  const rumor = createRumor(event, senderPrivateKey)\n\n  const seal = createSeal(rumor, senderPrivateKey, recipientPublicKey)\n  return createWrap(seal, recipientPublicKey)\n}\n\nexport function wrapManyEvents(\n  event: Partial<UnsignedEvent>,\n  senderPrivateKey: Uint8Array,\n  recipientsPublicKeys: string[],\n): NostrEvent[] {\n  if (!recipientsPublicKeys || recipientsPublicKeys.length === 0) {\n    throw new Error('At least one recipient is required.')\n  }\n\n  const senderPublicKey = getPublicKey(senderPrivateKey)\n\n  const wrappeds = [wrapEvent(event, senderPrivateKey, senderPublicKey)]\n\n  recipientsPublicKeys.forEach(recipientPublicKey => {\n    wrappeds.push(wrapEvent(event, senderPrivateKey, recipientPublicKey))\n  })\n\n  return wrappeds\n}\n\nexport function unwrapEvent(wrap: NostrEvent, recipientPrivateKey: Uint8Array): Rumor {\n  const unwrappedSeal = nip44Decrypt(wrap, recipientPrivateKey)\n  return nip44Decrypt(unwrappedSeal, recipientPrivateKey)\n}\n\nexport function unwrapManyEvents(wrappedEvents: NostrEvent[], recipientPrivateKey: Uint8Array): Rumor[] {\n  let unwrappedEvents: Rumor[] = []\n\n  wrappedEvents.forEach(e => {\n    unwrappedEvents.push(unwrapEvent(e, recipientPrivateKey))\n  })\n\n  unwrappedEvents.sort((a, b) => a.created_at - b.created_at)\n\n  return unwrappedEvents\n}\n"],
-  "mappings": "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",
-  "names": ["unwrapEvent", "unwrapManyEvents", "wrapEvent", "import_utils", "i", "import_utils", "import_secp256k1", "import_sha256", "hkdf_extract", "hkdf_expand", "wrapEvent", "unwrapEvent", "unwrapManyEvents"]
+  "sourcesContent": ["import { PrivateDirectMessage } from './kinds.ts'\nimport { EventTemplate, NostrEvent, getPublicKey } from './pure.ts'\nimport * as nip59 from './nip59.ts'\n\ntype Recipient = {\n  publicKey: string\n  relayUrl?: string\n}\n\ntype ReplyTo = {\n  eventId: string\n  relayUrl?: string\n}\n\nfunction createEvent(\n  recipients: Recipient | Recipient[],\n  message: string,\n  conversationTitle?: string,\n  replyTo?: ReplyTo,\n): EventTemplate {\n  const baseEvent: EventTemplate = {\n    created_at: Math.ceil(Date.now() / 1000),\n    kind: PrivateDirectMessage,\n    tags: [],\n    content: message,\n  }\n\n  const recipientsArray = Array.isArray(recipients) ? recipients : [recipients]\n\n  recipientsArray.forEach(({ publicKey, relayUrl }) => {\n    baseEvent.tags.push(relayUrl ? ['p', publicKey, relayUrl] : ['p', publicKey])\n  })\n\n  if (replyTo) {\n    baseEvent.tags.push(['e', replyTo.eventId, replyTo.relayUrl || '', 'reply'])\n  }\n\n  if (conversationTitle) {\n    baseEvent.tags.push(['subject', conversationTitle])\n  }\n\n  return baseEvent\n}\n\nexport function wrapEvent(\n  senderPrivateKey: Uint8Array,\n  recipient: Recipient,\n  message: string,\n  conversationTitle?: string,\n  replyTo?: ReplyTo,\n): NostrEvent {\n  const event = createEvent(recipient, message, conversationTitle, replyTo)\n  return nip59.wrapEvent(event, senderPrivateKey, recipient.publicKey)\n}\n\nexport function wrapManyEvents(\n  senderPrivateKey: Uint8Array,\n  recipients: Recipient[],\n  message: string,\n  conversationTitle?: string,\n  replyTo?: ReplyTo,\n): NostrEvent[] {\n  if (!recipients || recipients.length === 0) {\n    throw new Error('At least one recipient is required.')\n  }\n\n  const senderPublicKey = getPublicKey(senderPrivateKey)\n\n  // wrap the event for the sender and then for each recipient\n  return [{ publicKey: senderPublicKey }, ...recipients].map(recipient =>\n    wrapEvent(senderPrivateKey, recipient, message, conversationTitle, replyTo),\n  )\n}\n\nexport const unwrapEvent = nip59.unwrapEvent\n\nexport const unwrapManyEvents = nip59.unwrapManyEvents\n", "import { schnorr } from '@noble/curves/secp256k1.js'\nimport { bytesToHex, hexToBytes } from '@noble/hashes/utils.js'\nimport { Nostr, Event, EventTemplate, UnsignedEvent, VerifiedEvent, verifiedSymbol, validateEvent } from './core.ts'\nimport { sha256 } from '@noble/hashes/sha2.js'\n\nimport { utf8Encoder } from './utils.ts'\n\nclass JS implements Nostr {\n  generateSecretKey(): Uint8Array {\n    return schnorr.utils.randomSecretKey()\n  }\n  getPublicKey(secretKey: Uint8Array): string {\n    return bytesToHex(schnorr.getPublicKey(secretKey))\n  }\n  finalizeEvent(t: EventTemplate, secretKey: Uint8Array): VerifiedEvent {\n    const event = t as VerifiedEvent\n    event.pubkey = bytesToHex(schnorr.getPublicKey(secretKey))\n    event.id = getEventHash(event)\n    event.sig = bytesToHex(schnorr.sign(hexToBytes(getEventHash(event)), secretKey))\n    event[verifiedSymbol] = true\n    return event\n  }\n  verifyEvent(event: Event): event is VerifiedEvent {\n    if (typeof event[verifiedSymbol] === 'boolean') return event[verifiedSymbol]\n\n    const hash = getEventHash(event)\n    if (hash !== event.id) {\n      event[verifiedSymbol] = false\n      return false\n    }\n\n    try {\n      const valid = schnorr.verify(hexToBytes(event.sig), hexToBytes(hash), hexToBytes(event.pubkey))\n      event[verifiedSymbol] = valid\n      return valid\n    } catch (err) {\n      event[verifiedSymbol] = false\n      return false\n    }\n  }\n}\n\nexport function serializeEvent(evt: UnsignedEvent): string {\n  if (!validateEvent(evt)) throw new Error(\"can't serialize event with wrong or missing properties\")\n  return JSON.stringify([0, evt.pubkey, evt.created_at, evt.kind, evt.tags, evt.content])\n}\n\nexport function getEventHash(event: UnsignedEvent): string {\n  let eventHash = sha256(utf8Encoder.encode(serializeEvent(event)))\n  return bytesToHex(eventHash)\n}\n\nconst i: JS = new JS()\n\nexport const generateSecretKey = i.generateSecretKey\nexport const getPublicKey = i.getPublicKey\nexport const finalizeEvent = i.finalizeEvent\nexport const verifyEvent = i.verifyEvent\nexport * from './core.ts'\n", "export interface Nostr {\n  generateSecretKey(): Uint8Array\n  getPublicKey(secretKey: Uint8Array): string\n  finalizeEvent(event: EventTemplate, secretKey: Uint8Array): VerifiedEvent\n  verifyEvent(event: Event): event is VerifiedEvent\n}\n\n/** Designates a verified event signature. */\nexport const verifiedSymbol = Symbol('verified')\n\nexport type NostrEvent = {\n  kind: number\n  tags: string[][]\n  content: string\n  created_at: number\n  pubkey: string\n  id: string\n  sig: string\n  [verifiedSymbol]?: boolean\n}\n\nexport type Event = NostrEvent\nexport type EventTemplate = Pick<Event, 'kind' | 'tags' | 'content' | 'created_at'>\nexport type UnsignedEvent = Pick<Event, 'kind' | 'tags' | 'content' | 'created_at' | 'pubkey'>\n\n/** An event whose signature has been verified. */\nexport interface VerifiedEvent extends Event {\n  [verifiedSymbol]: true\n}\n\nconst isRecord = (obj: unknown): obj is Record<string, unknown> => obj instanceof Object\n\nexport function validateEvent<T>(event: T): event is T & UnsignedEvent {\n  if (!isRecord(event)) return false\n  if (typeof event.kind !== 'number') return false\n  if (typeof event.content !== 'string') return false\n  if (typeof event.created_at !== 'number') return false\n  if (typeof event.pubkey !== 'string') return false\n  if (!event.pubkey.match(/^[a-f0-9]{64}$/)) return false\n\n  if (!Array.isArray(event.tags)) return false\n  for (let i = 0; i < event.tags.length; i++) {\n    let tag = event.tags[i]\n    if (!Array.isArray(tag)) return false\n    for (let j = 0; j < tag.length; j++) {\n      if (typeof tag[j] !== 'string') return false\n    }\n  }\n\n  return true\n}\n\n/**\n * Sort events in reverse-chronological order by the `created_at` timestamp,\n * and then by the event `id` (lexicographically) in case of ties.\n * This mutates the array.\n */\nexport function sortEvents(events: Event[]): Event[] {\n  return events.sort((a: NostrEvent, b: NostrEvent): number => {\n    if (a.created_at !== b.created_at) {\n      return b.created_at - a.created_at\n    }\n    return a.id.localeCompare(b.id)\n  })\n}\n", "import type { Event } from './core.ts'\n\nexport const utf8Decoder: TextDecoder = new TextDecoder('utf-8')\nexport const utf8Encoder: TextEncoder = new TextEncoder()\n\nexport { bytesToHex, hexToBytes } from '@noble/hashes/utils.js'\n\nexport function normalizeURL(url: string): string {\n  try {\n    if (url.indexOf('://') === -1) url = 'wss://' + url\n    let p = new URL(url)\n    if (p.protocol === 'http:') p.protocol = 'ws:'\n    else if (p.protocol === 'https:') p.protocol = 'wss:'\n    p.pathname = p.pathname.replace(/\\/+/g, '/')\n    if (p.pathname.endsWith('/')) p.pathname = p.pathname.slice(0, -1)\n    if ((p.port === '80' && p.protocol === 'ws:') || (p.port === '443' && p.protocol === 'wss:')) p.port = ''\n    p.searchParams.sort()\n    p.hash = ''\n    return p.toString()\n  } catch (e) {\n    throw new Error(`Invalid URL: ${url}`)\n  }\n}\n\nexport function insertEventIntoDescendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return b.created_at - event.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function insertEventIntoAscendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return event.created_at - b.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function binarySearch<T>(arr: T[], compare: (b: T) => number): [number, boolean] {\n  let start = 0\n  let end = arr.length - 1\n\n  while (start <= end) {\n    const mid = Math.floor((start + end) / 2)\n    const cmp = compare(arr[mid])\n\n    if (cmp === 0) {\n      return [mid, true]\n    }\n\n    if (cmp < 0) {\n      end = mid - 1\n    } else {\n      start = mid + 1\n    }\n  }\n\n  return [start, false]\n}\n\nexport class QueueNode<V> {\n  public value: V\n  public next: QueueNode<V> | null = null\n  public prev: QueueNode<V> | null = null\n\n  constructor(message: V) {\n    this.value = message\n  }\n}\n\nexport class Queue<V> {\n  public first: QueueNode<V> | null\n  public last: QueueNode<V> | null\n\n  constructor() {\n    this.first = null\n    this.last = null\n  }\n\n  enqueue(value: V): boolean {\n    const newNode = new QueueNode(value)\n    if (!this.last) {\n      // list is empty\n      this.first = newNode\n      this.last = newNode\n    } else if (this.last === this.first) {\n      // list has a single element\n      this.last = newNode\n      this.last.prev = this.first\n      this.first.next = newNode\n    } else {\n      // list has elements, add as last\n      newNode.prev = this.last\n      this.last.next = newNode\n      this.last = newNode\n    }\n    return true\n  }\n\n  dequeue(): V | null {\n    if (!this.first) return null\n\n    if (this.first === this.last) {\n      const target = this.first\n      this.first = null\n      this.last = null\n      return target.value\n    }\n\n    const target = this.first\n    this.first = target.next\n    if (this.first) {\n      this.first.prev = null // fix: clean up prev pointer\n    }\n\n    return target.value\n  }\n}\n", "import { NostrEvent, validateEvent } from './pure.ts'\n\n/** Events are **regular**, which means they're all expected to be stored by relays. */\nexport function isRegularKind(kind: number): boolean {\n  return kind < 10000 && kind !== 0 && kind !== 3\n}\n\n/** Events are **replaceable**, which means that, for each combination of `pubkey` and `kind`, only the latest event is expected to (SHOULD) be stored by relays, older versions are expected to be discarded. */\nexport function isReplaceableKind(kind: number): boolean {\n  return kind === 0 || kind === 3 || (10000 <= kind && kind < 20000)\n}\n\n/** Events are **ephemeral**, which means they are not expected to be stored by relays. */\nexport function isEphemeralKind(kind: number): boolean {\n  return 20000 <= kind && kind < 30000\n}\n\n/** Events are **addressable**, which means that, for each combination of `pubkey`, `kind` and the `d` tag, only the latest event is expected to be stored by relays, older versions are expected to be discarded. */\nexport function isAddressableKind(kind: number): boolean {\n  return 30000 <= kind && kind < 40000\n}\n\n/** Classification of the event kind. */\nexport type KindClassification = 'regular' | 'replaceable' | 'ephemeral' | 'parameterized' | 'unknown'\n\n/** Determine the classification of this kind of event if known, or `unknown`. */\nexport function classifyKind(kind: number): KindClassification {\n  if (isRegularKind(kind)) return 'regular'\n  if (isReplaceableKind(kind)) return 'replaceable'\n  if (isEphemeralKind(kind)) return 'ephemeral'\n  if (isAddressableKind(kind)) return 'parameterized'\n  return 'unknown'\n}\n\nexport function isKind<T extends number>(event: unknown, kind: T | Array<T>): event is NostrEvent & { kind: T } {\n  const kindAsArray: number[] = kind instanceof Array ? kind : [kind]\n  return (validateEvent(event) && kindAsArray.includes(event.kind)) || false\n}\n\nexport const Metadata = 0\nexport type Metadata = typeof Metadata\nexport const ShortTextNote = 1\nexport type ShortTextNote = typeof ShortTextNote\nexport const RecommendRelay = 2\nexport type RecommendRelay = typeof RecommendRelay\nexport const Contacts = 3\nexport type Contacts = typeof Contacts\nexport const EncryptedDirectMessage = 4\nexport type EncryptedDirectMessage = typeof EncryptedDirectMessage\nexport const EventDeletion = 5\nexport type EventDeletion = typeof EventDeletion\nexport const Repost = 6\nexport type Repost = typeof Repost\nexport const Reaction = 7\nexport type Reaction = typeof Reaction\nexport const BadgeAward = 8\nexport type BadgeAward = typeof BadgeAward\nexport const ChatMessage = 9\nexport type ChatMessage = typeof ChatMessage\nexport const ForumThread = 11\nexport type ForumThread = typeof ForumThread\nexport const Seal = 13\nexport type Seal = typeof Seal\nexport const PrivateDirectMessage = 14\nexport type PrivateDirectMessage = typeof PrivateDirectMessage\nexport const FileMessage = 15\nexport type FileMessage = typeof FileMessage\nexport const GenericRepost = 16\nexport type GenericRepost = typeof GenericRepost\nexport const Photo = 20\nexport type Photo = typeof Photo\nexport const NormalVideo = 21\nexport type NormalVideo = typeof NormalVideo\nexport const ShortVideo = 22\nexport type ShortVideo = typeof ShortVideo\nexport const ChannelCreation = 40\nexport type ChannelCreation = typeof ChannelCreation\nexport const ChannelMetadata = 41\nexport type ChannelMetadata = typeof ChannelMetadata\nexport const ChannelMessage = 42\nexport type ChannelMessage = typeof ChannelMessage\nexport const ChannelHideMessage = 43\nexport type ChannelHideMessage = typeof ChannelHideMessage\nexport const ChannelMuteUser = 44\nexport type ChannelMuteUser = typeof ChannelMuteUser\nexport const OpenTimestamps = 1040\nexport type OpenTimestamps = typeof OpenTimestamps\nexport const GiftWrap = 1059\nexport type GiftWrap = typeof GiftWrap\nexport const Poll = 1068\nexport type Poll = typeof Poll\nexport const FileMetadata = 1063\nexport type FileMetadata = typeof FileMetadata\nexport const Comment = 1111\nexport type Comment = typeof Comment\nexport const LiveChatMessage = 1311\nexport type LiveChatMessage = typeof LiveChatMessage\nexport const Voice = 1222\nexport type Voice = typeof Voice\nexport const VoiceComment = 1244\nexport type VoiceComment = typeof VoiceComment\nexport const ProblemTracker = 1971\nexport type ProblemTracker = typeof ProblemTracker\nexport const Report = 1984\nexport type Report = typeof Report\nexport const Reporting = 1984\nexport type Reporting = typeof Reporting\nexport const Label = 1985\nexport type Label = typeof Label\nexport const CommunityPostApproval = 4550\nexport type CommunityPostApproval = typeof CommunityPostApproval\nexport const JobRequest = 5999\nexport type JobRequest = typeof JobRequest\nexport const JobResult = 6999\nexport type JobResult = typeof JobResult\nexport const JobFeedback = 7000\nexport type JobFeedback = typeof JobFeedback\nexport const ZapGoal = 9041\nexport type ZapGoal = typeof ZapGoal\nexport const ZapRequest = 9734\nexport type ZapRequest = typeof ZapRequest\nexport const Zap = 9735\nexport type Zap = typeof Zap\nexport const Highlights = 9802\nexport type Highlights = typeof Highlights\nexport const PollResponse = 1018\nexport type PollResponse = typeof PollResponse\nexport const Mutelist = 10000\nexport type Mutelist = typeof Mutelist\nexport const Pinlist = 10001\nexport type Pinlist = typeof Pinlist\nexport const RelayList = 10002\nexport type RelayList = typeof RelayList\nexport const BookmarkList = 10003\nexport type BookmarkList = typeof BookmarkList\nexport const CommunitiesList = 10004\nexport type CommunitiesList = typeof CommunitiesList\nexport const PublicChatsList = 10005\nexport type PublicChatsList = typeof PublicChatsList\nexport const BlockedRelaysList = 10006\nexport type BlockedRelaysList = typeof BlockedRelaysList\nexport const SearchRelaysList = 10007\nexport type SearchRelaysList = typeof SearchRelaysList\nexport const FavoriteRelays = 10012\nexport type FavoriteRelays = typeof FavoriteRelays\nexport const InterestsList = 10015\nexport type InterestsList = typeof InterestsList\nexport const UserEmojiList = 10030\nexport type UserEmojiList = typeof UserEmojiList\nexport const DirectMessageRelaysList = 10050\nexport type DirectMessageRelaysList = typeof DirectMessageRelaysList\nexport const FileServerPreference = 10096\nexport type FileServerPreference = typeof FileServerPreference\nexport const BlossomServerList = 10063\nexport type BlossomServerList = typeof BlossomServerList\nexport const NWCWalletInfo = 13194\nexport type NWCWalletInfo = typeof NWCWalletInfo\nexport const LightningPubRPC = 21000\nexport type LightningPubRPC = typeof LightningPubRPC\nexport const ClientAuth = 22242\nexport type ClientAuth = typeof ClientAuth\nexport const NWCWalletRequest = 23194\nexport type NWCWalletRequest = typeof NWCWalletRequest\nexport const NWCWalletResponse = 23195\nexport type NWCWalletResponse = typeof NWCWalletResponse\nexport const NostrConnect = 24133\nexport type NostrConnect = typeof NostrConnect\nexport const HTTPAuth = 27235\nexport type HTTPAuth = typeof HTTPAuth\nexport const Followsets = 30000\nexport type Followsets = typeof Followsets\nexport const Genericlists = 30001\nexport type Genericlists = typeof Genericlists\nexport const Relaysets = 30002\nexport type Relaysets = typeof Relaysets\nexport const Bookmarksets = 30003\nexport type Bookmarksets = typeof Bookmarksets\nexport const Curationsets = 30004\nexport type Curationsets = typeof Curationsets\nexport const ProfileBadges = 30008\nexport type ProfileBadges = typeof ProfileBadges\nexport const BadgeDefinition = 30009\nexport type BadgeDefinition = typeof BadgeDefinition\nexport const Interestsets = 30015\nexport type Interestsets = typeof Interestsets\nexport const CreateOrUpdateStall = 30017\nexport type CreateOrUpdateStall = typeof CreateOrUpdateStall\nexport const CreateOrUpdateProduct = 30018\nexport type CreateOrUpdateProduct = typeof CreateOrUpdateProduct\nexport const LongFormArticle = 30023\nexport type LongFormArticle = typeof LongFormArticle\nexport const DraftLong = 30024\nexport type DraftLong = typeof DraftLong\nexport const Emojisets = 30030\nexport type Emojisets = typeof Emojisets\nexport const Application = 30078\nexport type Application = typeof Application\nexport const LiveEvent = 30311\nexport type LiveEvent = typeof LiveEvent\nexport const UserStatuses = 30315\nexport type UserStatuses = typeof UserStatuses\nexport const ClassifiedListing = 30402\nexport type ClassifiedListing = typeof ClassifiedListing\nexport const DraftClassifiedListing = 30403\nexport type DraftClassifiedListing = typeof DraftClassifiedListing\nexport const Date = 31922\nexport type Date = typeof Date\nexport const Time = 31923\nexport type Time = typeof Time\nexport const Calendar = 31924\nexport type Calendar = typeof Calendar\nexport const CalendarEventRSVP = 31925\nexport type CalendarEventRSVP = typeof CalendarEventRSVP\nexport const RelayReview = 31987\nexport type RelayReview = typeof RelayReview\nexport const Handlerrecommendation = 31989\nexport type Handlerrecommendation = typeof Handlerrecommendation\nexport const Handlerinformation = 31990\nexport type Handlerinformation = typeof Handlerinformation\nexport const CommunityDefinition = 34550\nexport type CommunityDefinition = typeof CommunityDefinition\nexport const GroupMetadata = 39000\nexport type GroupMetadata = typeof GroupMetadata\n", "import { chacha20 } from '@noble/ciphers/chacha.js'\nimport { equalBytes } from '@noble/ciphers/utils.js'\nimport { secp256k1 } from '@noble/curves/secp256k1.js'\nimport { extract as hkdf_extract, expand as hkdf_expand } from '@noble/hashes/hkdf.js'\nimport { hmac } from '@noble/hashes/hmac.js'\nimport { sha256 } from '@noble/hashes/sha2.js'\nimport { concatBytes, hexToBytes, randomBytes } from '@noble/hashes/utils.js'\nimport { base64 } from '@scure/base'\n\nimport { utf8Decoder, utf8Encoder } from './utils.ts'\n\nconst minPlaintextSize = 0x0001 // 1b msg => padded to 32b\nconst maxPlaintextSize = 0xffff // 65535 (64kb-1) => padded to 64kb\n\nexport function getConversationKey(privkeyA: Uint8Array, pubkeyB: string): Uint8Array {\n  const sharedX = secp256k1.getSharedSecret(privkeyA, hexToBytes('02' + pubkeyB)).subarray(1, 33)\n  return hkdf_extract(sha256, sharedX, utf8Encoder.encode('nip44-v2'))\n}\n\nfunction getMessageKeys(\n  conversationKey: Uint8Array,\n  nonce: Uint8Array,\n): { chacha_key: Uint8Array; chacha_nonce: Uint8Array; hmac_key: Uint8Array } {\n  const keys = hkdf_expand(sha256, conversationKey, nonce, 76)\n  return {\n    chacha_key: keys.subarray(0, 32),\n    chacha_nonce: keys.subarray(32, 44),\n    hmac_key: keys.subarray(44, 76),\n  }\n}\n\nfunction calcPaddedLen(len: number): number {\n  if (!Number.isSafeInteger(len) || len < 1) throw new Error('expected positive integer')\n  if (len <= 32) return 32\n  const nextPower = 1 << (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 > maxPlaintextSize)\n    throw new Error('invalid plaintext size: must be between 1 and 65535 bytes')\n  const arr = new Uint8Array(2)\n  new DataView(arr.buffer).setUint16(0, num, false)\n  return arr\n}\n\nfunction pad(plaintext: string): Uint8Array {\n  const unpadded = utf8Encoder.encode(plaintext)\n  const unpaddedLen = unpadded.length\n  const prefix = 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 unpaddedLen = new DataView(padded.buffer).getUint16(0)\n  const unpadded = padded.subarray(2, 2 + unpaddedLen)\n  if (\n    unpaddedLen < minPlaintextSize ||\n    unpaddedLen > maxPlaintextSize ||\n    unpadded.length !== unpaddedLen ||\n    padded.length !== 2 + calcPaddedLen(unpaddedLen)\n  )\n    throw new Error('invalid padding')\n  return utf8Decoder.decode(unpadded)\n}\n\nfunction hmacAad(key: Uint8Array, message: Uint8Array, aad: Uint8Array): Uint8Array {\n  if (aad.length !== 32) throw new Error('AAD associated data must be 32 bytes')\n  const combined = concatBytes(aad, message)\n  return hmac(sha256, key, combined)\n}\n\n// metadata: always 65b (version: 1b, nonce: 32b, max: 32b)\n// plaintext: 1b to 0xffff\n// padded plaintext: 32b to 0xffff\n// ciphertext: 32b+2 to 0xffff+2\n// raw payload: 99 (65+32+2) to 65603 (65+0xffff+2)\n// compressed payload (base64): 132b to 87472b\nfunction decodePayload(payload: string): { nonce: Uint8Array; ciphertext: Uint8Array; mac: Uint8Array } {\n  if (typeof payload !== 'string') throw new Error('payload must be a valid string')\n  const plen = payload.length\n  if (plen < 132 || plen > 87472) throw new Error('invalid payload length: ' + plen)\n  if (payload[0] === '#') throw new Error('unknown encryption version')\n  let data: Uint8Array\n  try {\n    data = base64.decode(payload)\n  } catch (error) {\n    throw new Error('invalid base64: ' + (error as any).message)\n  }\n  const dlen = data.length\n  if (dlen < 99 || dlen > 65603) throw new Error('invalid data length: ' + dlen)\n  const vers = data[0]\n  if (vers !== 2) throw new Error('unknown encryption version ' + vers)\n  return {\n    nonce: data.subarray(1, 33),\n    ciphertext: data.subarray(33, -32),\n    mac: data.subarray(-32),\n  }\n}\n\nexport function encrypt(plaintext: string, conversationKey: Uint8Array, nonce: Uint8Array = randomBytes(32)): string {\n  const { chacha_key, chacha_nonce, hmac_key } = getMessageKeys(conversationKey, nonce)\n  const padded = pad(plaintext)\n  const ciphertext = chacha20(chacha_key, chacha_nonce, padded)\n  const mac = hmacAad(hmac_key, ciphertext, nonce)\n  return base64.encode(concatBytes(new Uint8Array([2]), nonce, ciphertext, mac))\n}\n\nexport function decrypt(payload: string, conversationKey: Uint8Array): string {\n  const { nonce, ciphertext, mac } = decodePayload(payload)\n  const { chacha_key, chacha_nonce, hmac_key } = getMessageKeys(conversationKey, nonce)\n  const calculatedMac = hmacAad(hmac_key, ciphertext, nonce)\n  if (!equalBytes(calculatedMac, mac)) throw new Error('invalid MAC')\n  const padded = chacha20(chacha_key, chacha_nonce, ciphertext)\n  return unpad(padded)\n}\n\nexport const v2 = {\n  utils: {\n    getConversationKey,\n    calcPaddedLen,\n  },\n  encrypt,\n  decrypt,\n}\n", "import { EventTemplate, UnsignedEvent, NostrEvent } from './core.ts'\nimport { getConversationKey, decrypt, encrypt } from './nip44.ts'\nimport { getEventHash, generateSecretKey, finalizeEvent, getPublicKey } from './pure.ts'\nimport { Seal, GiftWrap } from './kinds.ts'\n\ntype Rumor = UnsignedEvent & { id: string }\n\nconst TWO_DAYS = 2 * 24 * 60 * 60\n\nconst now = () => Math.round(Date.now() / 1000)\nconst randomNow = () => Math.round(now() - Math.random() * TWO_DAYS)\n\nconst nip44ConversationKey = (privateKey: Uint8Array, publicKey: string) => getConversationKey(privateKey, publicKey)\n\nconst nip44Encrypt = (data: EventTemplate, privateKey: Uint8Array, publicKey: string) =>\n  encrypt(JSON.stringify(data), nip44ConversationKey(privateKey, publicKey))\n\nconst nip44Decrypt = (data: NostrEvent, privateKey: Uint8Array) =>\n  JSON.parse(decrypt(data.content, nip44ConversationKey(privateKey, data.pubkey)))\n\nexport function createRumor(event: Partial<UnsignedEvent>, privateKey: Uint8Array): Rumor {\n  const rumor = {\n    created_at: now(),\n    content: '',\n    tags: [],\n    ...event,\n    pubkey: getPublicKey(privateKey),\n  } as any\n\n  rumor.id = getEventHash(rumor)\n\n  return rumor as Rumor\n}\n\nexport function createSeal(rumor: Rumor, privateKey: Uint8Array, recipientPublicKey: string): NostrEvent {\n  return finalizeEvent(\n    {\n      kind: Seal,\n      content: nip44Encrypt(rumor, privateKey, recipientPublicKey),\n      created_at: randomNow(),\n      tags: [],\n    },\n    privateKey,\n  )\n}\n\nexport function createWrap(seal: NostrEvent, recipientPublicKey: string): NostrEvent {\n  const randomKey = generateSecretKey()\n\n  return finalizeEvent(\n    {\n      kind: GiftWrap,\n      content: nip44Encrypt(seal, randomKey, recipientPublicKey),\n      created_at: randomNow(),\n      tags: [['p', recipientPublicKey]],\n    },\n    randomKey,\n  ) as NostrEvent\n}\n\nexport function wrapEvent(\n  event: Partial<UnsignedEvent>,\n  senderPrivateKey: Uint8Array,\n  recipientPublicKey: string,\n): NostrEvent {\n  const rumor = createRumor(event, senderPrivateKey)\n\n  const seal = createSeal(rumor, senderPrivateKey, recipientPublicKey)\n  return createWrap(seal, recipientPublicKey)\n}\n\nexport function wrapManyEvents(\n  event: Partial<UnsignedEvent>,\n  senderPrivateKey: Uint8Array,\n  recipientsPublicKeys: string[],\n): NostrEvent[] {\n  if (!recipientsPublicKeys || recipientsPublicKeys.length === 0) {\n    throw new Error('At least one recipient is required.')\n  }\n\n  const senderPublicKey = getPublicKey(senderPrivateKey)\n\n  const wrappeds = [wrapEvent(event, senderPrivateKey, senderPublicKey)]\n\n  recipientsPublicKeys.forEach(recipientPublicKey => {\n    wrappeds.push(wrapEvent(event, senderPrivateKey, recipientPublicKey))\n  })\n\n  return wrappeds\n}\n\nexport function unwrapEvent(wrap: NostrEvent, recipientPrivateKey: Uint8Array): Rumor {\n  const unwrappedSeal = nip44Decrypt(wrap, recipientPrivateKey)\n  return nip44Decrypt(unwrappedSeal, recipientPrivateKey)\n}\n\nexport function unwrapManyEvents(wrappedEvents: NostrEvent[], recipientPrivateKey: Uint8Array): Rumor[] {\n  let unwrappedEvents: Rumor[] = []\n\n  wrappedEvents.forEach(e => {\n    unwrappedEvents.push(unwrapEvent(e, recipientPrivateKey))\n  })\n\n  unwrappedEvents.sort((a, b) => a.created_at - b.created_at)\n\n  return unwrappedEvents\n}\n"],
+  "mappings": "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",
+  "names": ["unwrapEvent", "unwrapManyEvents", "wrapEvent", "import_utils", "i", "import_utils", "import_secp256k1", "import_sha2", "hkdf_extract", "hkdf_expand", "wrapEvent", "unwrapEvent", "unwrapManyEvents"]
 }
Index: package/lib/esm/nip17.js.map
===================================================================
--- package/lib/esm/nip17.js.map
+++ package/lib/esm/nip17.js.map
@@ -1,7 +1,7 @@
 {
   "version": 3,
   "sources": ["../../pure.ts", "../../core.ts", "../../utils.ts", "../../kinds.ts", "../../nip44.ts", "../../nip59.ts", "../../nip17.ts"],
-  "sourcesContent": ["import { schnorr } from '@noble/curves/secp256k1'\nimport { bytesToHex } from '@noble/hashes/utils'\nimport { Nostr, Event, EventTemplate, UnsignedEvent, VerifiedEvent, verifiedSymbol, validateEvent } from './core.ts'\nimport { sha256 } from '@noble/hashes/sha256'\n\nimport { utf8Encoder } from './utils.ts'\n\nclass JS implements Nostr {\n  generateSecretKey(): Uint8Array {\n    return schnorr.utils.randomPrivateKey()\n  }\n  getPublicKey(secretKey: Uint8Array): string {\n    return bytesToHex(schnorr.getPublicKey(secretKey))\n  }\n  finalizeEvent(t: EventTemplate, secretKey: Uint8Array): VerifiedEvent {\n    const event = t as VerifiedEvent\n    event.pubkey = bytesToHex(schnorr.getPublicKey(secretKey))\n    event.id = getEventHash(event)\n    event.sig = bytesToHex(schnorr.sign(getEventHash(event), secretKey))\n    event[verifiedSymbol] = true\n    return event\n  }\n  verifyEvent(event: Event): event is VerifiedEvent {\n    if (typeof event[verifiedSymbol] === 'boolean') return event[verifiedSymbol]\n\n    const hash = getEventHash(event)\n    if (hash !== event.id) {\n      event[verifiedSymbol] = false\n      return false\n    }\n\n    try {\n      const valid = schnorr.verify(event.sig, hash, event.pubkey)\n      event[verifiedSymbol] = valid\n      return valid\n    } catch (err) {\n      event[verifiedSymbol] = false\n      return false\n    }\n  }\n}\n\nexport function serializeEvent(evt: UnsignedEvent): string {\n  if (!validateEvent(evt)) throw new Error(\"can't serialize event with wrong or missing properties\")\n  return JSON.stringify([0, evt.pubkey, evt.created_at, evt.kind, evt.tags, evt.content])\n}\n\nexport function getEventHash(event: UnsignedEvent): string {\n  let eventHash = sha256(utf8Encoder.encode(serializeEvent(event)))\n  return bytesToHex(eventHash)\n}\n\nconst i: JS = new JS()\n\nexport const generateSecretKey = i.generateSecretKey\nexport const getPublicKey = i.getPublicKey\nexport const finalizeEvent = i.finalizeEvent\nexport const verifyEvent = i.verifyEvent\nexport * from './core.ts'\n", "export interface Nostr {\n  generateSecretKey(): Uint8Array\n  getPublicKey(secretKey: Uint8Array): string\n  finalizeEvent(event: EventTemplate, secretKey: Uint8Array): VerifiedEvent\n  verifyEvent(event: Event): event is VerifiedEvent\n}\n\n/** Designates a verified event signature. */\nexport const verifiedSymbol = Symbol('verified')\n\nexport type NostrEvent = {\n  kind: number\n  tags: string[][]\n  content: string\n  created_at: number\n  pubkey: string\n  id: string\n  sig: string\n  [verifiedSymbol]?: boolean\n}\n\nexport type Event = NostrEvent\nexport type EventTemplate = Pick<Event, 'kind' | 'tags' | 'content' | 'created_at'>\nexport type UnsignedEvent = Pick<Event, 'kind' | 'tags' | 'content' | 'created_at' | 'pubkey'>\n\n/** An event whose signature has been verified. */\nexport interface VerifiedEvent extends Event {\n  [verifiedSymbol]: true\n}\n\nconst isRecord = (obj: unknown): obj is Record<string, unknown> => obj instanceof Object\n\nexport function validateEvent<T>(event: T): event is T & UnsignedEvent {\n  if (!isRecord(event)) return false\n  if (typeof event.kind !== 'number') return false\n  if (typeof event.content !== 'string') return false\n  if (typeof event.created_at !== 'number') return false\n  if (typeof event.pubkey !== 'string') return false\n  if (!event.pubkey.match(/^[a-f0-9]{64}$/)) return false\n\n  if (!Array.isArray(event.tags)) return false\n  for (let i = 0; i < event.tags.length; i++) {\n    let tag = event.tags[i]\n    if (!Array.isArray(tag)) return false\n    for (let j = 0; j < tag.length; j++) {\n      if (typeof tag[j] !== 'string') return false\n    }\n  }\n\n  return true\n}\n\n/**\n * Sort events in reverse-chronological order by the `created_at` timestamp,\n * and then by the event `id` (lexicographically) in case of ties.\n * This mutates the array.\n */\nexport function sortEvents(events: Event[]): Event[] {\n  return events.sort((a: NostrEvent, b: NostrEvent): number => {\n    if (a.created_at !== b.created_at) {\n      return b.created_at - a.created_at\n    }\n    return a.id.localeCompare(b.id)\n  })\n}\n", "import type { Event } from './core.ts'\n\nexport const utf8Decoder: TextDecoder = new TextDecoder('utf-8')\nexport const utf8Encoder: TextEncoder = new TextEncoder()\n\nexport { bytesToHex, hexToBytes } from '@noble/hashes/utils'\n\nexport function normalizeURL(url: string): string {\n  try {\n    if (url.indexOf('://') === -1) url = 'wss://' + url\n    let p = new URL(url)\n    if (p.protocol === 'http:') p.protocol = 'ws:'\n    else if (p.protocol === 'https:') p.protocol = 'wss:'\n    p.pathname = p.pathname.replace(/\\/+/g, '/')\n    if (p.pathname.endsWith('/')) p.pathname = p.pathname.slice(0, -1)\n    if ((p.port === '80' && p.protocol === 'ws:') || (p.port === '443' && p.protocol === 'wss:')) p.port = ''\n    p.searchParams.sort()\n    p.hash = ''\n    return p.toString()\n  } catch (e) {\n    throw new Error(`Invalid URL: ${url}`)\n  }\n}\n\nexport function insertEventIntoDescendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return b.created_at - event.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function insertEventIntoAscendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return event.created_at - b.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function binarySearch<T>(arr: T[], compare: (b: T) => number): [number, boolean] {\n  let start = 0\n  let end = arr.length - 1\n\n  while (start <= end) {\n    const mid = Math.floor((start + end) / 2)\n    const cmp = compare(arr[mid])\n\n    if (cmp === 0) {\n      return [mid, true]\n    }\n\n    if (cmp < 0) {\n      end = mid - 1\n    } else {\n      start = mid + 1\n    }\n  }\n\n  return [start, false]\n}\n\nexport class QueueNode<V> {\n  public value: V\n  public next: QueueNode<V> | null = null\n  public prev: QueueNode<V> | null = null\n\n  constructor(message: V) {\n    this.value = message\n  }\n}\n\nexport class Queue<V> {\n  public first: QueueNode<V> | null\n  public last: QueueNode<V> | null\n\n  constructor() {\n    this.first = null\n    this.last = null\n  }\n\n  enqueue(value: V): boolean {\n    const newNode = new QueueNode(value)\n    if (!this.last) {\n      // list is empty\n      this.first = newNode\n      this.last = newNode\n    } else if (this.last === this.first) {\n      // list has a single element\n      this.last = newNode\n      this.last.prev = this.first\n      this.first.next = newNode\n    } else {\n      // list has elements, add as last\n      newNode.prev = this.last\n      this.last.next = newNode\n      this.last = newNode\n    }\n    return true\n  }\n\n  dequeue(): V | null {\n    if (!this.first) return null\n\n    if (this.first === this.last) {\n      const target = this.first\n      this.first = null\n      this.last = null\n      return target.value\n    }\n\n    const target = this.first\n    this.first = target.next\n    if (this.first) {\n      this.first.prev = null // fix: clean up prev pointer\n    }\n\n    return target.value\n  }\n}\n", "import { NostrEvent, validateEvent } from './pure.ts'\n\n/** Events are **regular**, which means they're all expected to be stored by relays. */\nexport function isRegularKind(kind: number): boolean {\n  return kind < 10000 && kind !== 0 && kind !== 3\n}\n\n/** Events are **replaceable**, which means that, for each combination of `pubkey` and `kind`, only the latest event is expected to (SHOULD) be stored by relays, older versions are expected to be discarded. */\nexport function isReplaceableKind(kind: number): boolean {\n  return kind === 0 || kind === 3 || (10000 <= kind && kind < 20000)\n}\n\n/** Events are **ephemeral**, which means they are not expected to be stored by relays. */\nexport function isEphemeralKind(kind: number): boolean {\n  return 20000 <= kind && kind < 30000\n}\n\n/** Events are **addressable**, which means that, for each combination of `pubkey`, `kind` and the `d` tag, only the latest event is expected to be stored by relays, older versions are expected to be discarded. */\nexport function isAddressableKind(kind: number): boolean {\n  return 30000 <= kind && kind < 40000\n}\n\n/** Classification of the event kind. */\nexport type KindClassification = 'regular' | 'replaceable' | 'ephemeral' | 'parameterized' | 'unknown'\n\n/** Determine the classification of this kind of event if known, or `unknown`. */\nexport function classifyKind(kind: number): KindClassification {\n  if (isRegularKind(kind)) return 'regular'\n  if (isReplaceableKind(kind)) return 'replaceable'\n  if (isEphemeralKind(kind)) return 'ephemeral'\n  if (isAddressableKind(kind)) return 'parameterized'\n  return 'unknown'\n}\n\nexport function isKind<T extends number>(event: unknown, kind: T | Array<T>): event is NostrEvent & { kind: T } {\n  const kindAsArray: number[] = kind instanceof Array ? kind : [kind]\n  return (validateEvent(event) && kindAsArray.includes(event.kind)) || false\n}\n\nexport const Metadata = 0\nexport type Metadata = typeof Metadata\nexport const ShortTextNote = 1\nexport type ShortTextNote = typeof ShortTextNote\nexport const RecommendRelay = 2\nexport type RecommendRelay = typeof RecommendRelay\nexport const Contacts = 3\nexport type Contacts = typeof Contacts\nexport const EncryptedDirectMessage = 4\nexport type EncryptedDirectMessage = typeof EncryptedDirectMessage\nexport const EventDeletion = 5\nexport type EventDeletion = typeof EventDeletion\nexport const Repost = 6\nexport type Repost = typeof Repost\nexport const Reaction = 7\nexport type Reaction = typeof Reaction\nexport const BadgeAward = 8\nexport type BadgeAward = typeof BadgeAward\nexport const ChatMessage = 9\nexport type ChatMessage = typeof ChatMessage\nexport const ForumThread = 11\nexport type ForumThread = typeof ForumThread\nexport const Seal = 13\nexport type Seal = typeof Seal\nexport const PrivateDirectMessage = 14\nexport type PrivateDirectMessage = typeof PrivateDirectMessage\nexport const FileMessage = 15\nexport type FileMessage = typeof FileMessage\nexport const GenericRepost = 16\nexport type GenericRepost = typeof GenericRepost\nexport const Photo = 20\nexport type Photo = typeof Photo\nexport const NormalVideo = 21\nexport type NormalVideo = typeof NormalVideo\nexport const ShortVideo = 22\nexport type ShortVideo = typeof ShortVideo\nexport const ChannelCreation = 40\nexport type ChannelCreation = typeof ChannelCreation\nexport const ChannelMetadata = 41\nexport type ChannelMetadata = typeof ChannelMetadata\nexport const ChannelMessage = 42\nexport type ChannelMessage = typeof ChannelMessage\nexport const ChannelHideMessage = 43\nexport type ChannelHideMessage = typeof ChannelHideMessage\nexport const ChannelMuteUser = 44\nexport type ChannelMuteUser = typeof ChannelMuteUser\nexport const OpenTimestamps = 1040\nexport type OpenTimestamps = typeof OpenTimestamps\nexport const GiftWrap = 1059\nexport type GiftWrap = typeof GiftWrap\nexport const Poll = 1068\nexport type Poll = typeof Poll\nexport const FileMetadata = 1063\nexport type FileMetadata = typeof FileMetadata\nexport const Comment = 1111\nexport type Comment = typeof Comment\nexport const LiveChatMessage = 1311\nexport type LiveChatMessage = typeof LiveChatMessage\nexport const Voice = 1222\nexport type Voice = typeof Voice\nexport const VoiceComment = 1244\nexport type VoiceComment = typeof VoiceComment\nexport const ProblemTracker = 1971\nexport type ProblemTracker = typeof ProblemTracker\nexport const Report = 1984\nexport type Report = typeof Report\nexport const Reporting = 1984\nexport type Reporting = typeof Reporting\nexport const Label = 1985\nexport type Label = typeof Label\nexport const CommunityPostApproval = 4550\nexport type CommunityPostApproval = typeof CommunityPostApproval\nexport const JobRequest = 5999\nexport type JobRequest = typeof JobRequest\nexport const JobResult = 6999\nexport type JobResult = typeof JobResult\nexport const JobFeedback = 7000\nexport type JobFeedback = typeof JobFeedback\nexport const ZapGoal = 9041\nexport type ZapGoal = typeof ZapGoal\nexport const ZapRequest = 9734\nexport type ZapRequest = typeof ZapRequest\nexport const Zap = 9735\nexport type Zap = typeof Zap\nexport const Highlights = 9802\nexport type Highlights = typeof Highlights\nexport const PollResponse = 1018\nexport type PollResponse = typeof PollResponse\nexport const Mutelist = 10000\nexport type Mutelist = typeof Mutelist\nexport const Pinlist = 10001\nexport type Pinlist = typeof Pinlist\nexport const RelayList = 10002\nexport type RelayList = typeof RelayList\nexport const BookmarkList = 10003\nexport type BookmarkList = typeof BookmarkList\nexport const CommunitiesList = 10004\nexport type CommunitiesList = typeof CommunitiesList\nexport const PublicChatsList = 10005\nexport type PublicChatsList = typeof PublicChatsList\nexport const BlockedRelaysList = 10006\nexport type BlockedRelaysList = typeof BlockedRelaysList\nexport const SearchRelaysList = 10007\nexport type SearchRelaysList = typeof SearchRelaysList\nexport const FavoriteRelays = 10012\nexport type FavoriteRelays = typeof FavoriteRelays\nexport const InterestsList = 10015\nexport type InterestsList = typeof InterestsList\nexport const UserEmojiList = 10030\nexport type UserEmojiList = typeof UserEmojiList\nexport const DirectMessageRelaysList = 10050\nexport type DirectMessageRelaysList = typeof DirectMessageRelaysList\nexport const FileServerPreference = 10096\nexport type FileServerPreference = typeof FileServerPreference\nexport const BlossomServerList = 10063\nexport type BlossomServerList = typeof BlossomServerList\nexport const NWCWalletInfo = 13194\nexport type NWCWalletInfo = typeof NWCWalletInfo\nexport const LightningPubRPC = 21000\nexport type LightningPubRPC = typeof LightningPubRPC\nexport const ClientAuth = 22242\nexport type ClientAuth = typeof ClientAuth\nexport const NWCWalletRequest = 23194\nexport type NWCWalletRequest = typeof NWCWalletRequest\nexport const NWCWalletResponse = 23195\nexport type NWCWalletResponse = typeof NWCWalletResponse\nexport const NostrConnect = 24133\nexport type NostrConnect = typeof NostrConnect\nexport const HTTPAuth = 27235\nexport type HTTPAuth = typeof HTTPAuth\nexport const Followsets = 30000\nexport type Followsets = typeof Followsets\nexport const Genericlists = 30001\nexport type Genericlists = typeof Genericlists\nexport const Relaysets = 30002\nexport type Relaysets = typeof Relaysets\nexport const Bookmarksets = 30003\nexport type Bookmarksets = typeof Bookmarksets\nexport const Curationsets = 30004\nexport type Curationsets = typeof Curationsets\nexport const ProfileBadges = 30008\nexport type ProfileBadges = typeof ProfileBadges\nexport const BadgeDefinition = 30009\nexport type BadgeDefinition = typeof BadgeDefinition\nexport const Interestsets = 30015\nexport type Interestsets = typeof Interestsets\nexport const CreateOrUpdateStall = 30017\nexport type CreateOrUpdateStall = typeof CreateOrUpdateStall\nexport const CreateOrUpdateProduct = 30018\nexport type CreateOrUpdateProduct = typeof CreateOrUpdateProduct\nexport const LongFormArticle = 30023\nexport type LongFormArticle = typeof LongFormArticle\nexport const DraftLong = 30024\nexport type DraftLong = typeof DraftLong\nexport const Emojisets = 30030\nexport type Emojisets = typeof Emojisets\nexport const Application = 30078\nexport type Application = typeof Application\nexport const LiveEvent = 30311\nexport type LiveEvent = typeof LiveEvent\nexport const UserStatuses = 30315\nexport type UserStatuses = typeof UserStatuses\nexport const ClassifiedListing = 30402\nexport type ClassifiedListing = typeof ClassifiedListing\nexport const DraftClassifiedListing = 30403\nexport type DraftClassifiedListing = typeof DraftClassifiedListing\nexport const Date = 31922\nexport type Date = typeof Date\nexport const Time = 31923\nexport type Time = typeof Time\nexport const Calendar = 31924\nexport type Calendar = typeof Calendar\nexport const CalendarEventRSVP = 31925\nexport type CalendarEventRSVP = typeof CalendarEventRSVP\nexport const RelayReview = 31987\nexport type RelayReview = typeof RelayReview\nexport const Handlerrecommendation = 31989\nexport type Handlerrecommendation = typeof Handlerrecommendation\nexport const Handlerinformation = 31990\nexport type Handlerinformation = typeof Handlerinformation\nexport const CommunityDefinition = 34550\nexport type CommunityDefinition = typeof CommunityDefinition\nexport const GroupMetadata = 39000\nexport type GroupMetadata = typeof GroupMetadata\n", "import { chacha20 } from '@noble/ciphers/chacha'\nimport { equalBytes } from '@noble/ciphers/utils'\nimport { secp256k1 } from '@noble/curves/secp256k1'\nimport { extract as hkdf_extract, expand as hkdf_expand } from '@noble/hashes/hkdf'\nimport { hmac } from '@noble/hashes/hmac'\nimport { sha256 } from '@noble/hashes/sha256'\nimport { concatBytes, randomBytes } from '@noble/hashes/utils'\nimport { base64 } from '@scure/base'\n\nimport { utf8Decoder, utf8Encoder } from './utils.ts'\n\nconst minPlaintextSize = 0x0001 // 1b msg => padded to 32b\nconst maxPlaintextSize = 0xffff // 65535 (64kb-1) => padded to 64kb\n\nexport function getConversationKey(privkeyA: Uint8Array, pubkeyB: string): Uint8Array {\n  const sharedX = secp256k1.getSharedSecret(privkeyA, '02' + pubkeyB).subarray(1, 33)\n  return hkdf_extract(sha256, sharedX, 'nip44-v2')\n}\n\nfunction getMessageKeys(\n  conversationKey: Uint8Array,\n  nonce: Uint8Array,\n): { chacha_key: Uint8Array; chacha_nonce: Uint8Array; hmac_key: Uint8Array } {\n  const keys = hkdf_expand(sha256, conversationKey, nonce, 76)\n  return {\n    chacha_key: keys.subarray(0, 32),\n    chacha_nonce: keys.subarray(32, 44),\n    hmac_key: keys.subarray(44, 76),\n  }\n}\n\nfunction calcPaddedLen(len: number): number {\n  if (!Number.isSafeInteger(len) || len < 1) throw new Error('expected positive integer')\n  if (len <= 32) return 32\n  const nextPower = 1 << (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 > maxPlaintextSize)\n    throw new Error('invalid plaintext size: must be between 1 and 65535 bytes')\n  const arr = new Uint8Array(2)\n  new DataView(arr.buffer).setUint16(0, num, false)\n  return arr\n}\n\nfunction pad(plaintext: string): Uint8Array {\n  const unpadded = utf8Encoder.encode(plaintext)\n  const unpaddedLen = unpadded.length\n  const prefix = 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 unpaddedLen = new DataView(padded.buffer).getUint16(0)\n  const unpadded = padded.subarray(2, 2 + unpaddedLen)\n  if (\n    unpaddedLen < minPlaintextSize ||\n    unpaddedLen > maxPlaintextSize ||\n    unpadded.length !== unpaddedLen ||\n    padded.length !== 2 + calcPaddedLen(unpaddedLen)\n  )\n    throw new Error('invalid padding')\n  return utf8Decoder.decode(unpadded)\n}\n\nfunction hmacAad(key: Uint8Array, message: Uint8Array, aad: Uint8Array): Uint8Array {\n  if (aad.length !== 32) throw new Error('AAD associated data must be 32 bytes')\n  const combined = concatBytes(aad, message)\n  return hmac(sha256, key, combined)\n}\n\n// metadata: always 65b (version: 1b, nonce: 32b, max: 32b)\n// plaintext: 1b to 0xffff\n// padded plaintext: 32b to 0xffff\n// ciphertext: 32b+2 to 0xffff+2\n// raw payload: 99 (65+32+2) to 65603 (65+0xffff+2)\n// compressed payload (base64): 132b to 87472b\nfunction decodePayload(payload: string): { nonce: Uint8Array; ciphertext: Uint8Array; mac: Uint8Array } {\n  if (typeof payload !== 'string') throw new Error('payload must be a valid string')\n  const plen = payload.length\n  if (plen < 132 || plen > 87472) throw new Error('invalid payload length: ' + plen)\n  if (payload[0] === '#') throw new Error('unknown encryption version')\n  let data: Uint8Array\n  try {\n    data = base64.decode(payload)\n  } catch (error) {\n    throw new Error('invalid base64: ' + (error as any).message)\n  }\n  const dlen = data.length\n  if (dlen < 99 || dlen > 65603) throw new Error('invalid data length: ' + dlen)\n  const vers = data[0]\n  if (vers !== 2) throw new Error('unknown encryption version ' + vers)\n  return {\n    nonce: data.subarray(1, 33),\n    ciphertext: data.subarray(33, -32),\n    mac: data.subarray(-32),\n  }\n}\n\nexport function encrypt(plaintext: string, conversationKey: Uint8Array, nonce: Uint8Array = randomBytes(32)): string {\n  const { chacha_key, chacha_nonce, hmac_key } = getMessageKeys(conversationKey, nonce)\n  const padded = pad(plaintext)\n  const ciphertext = chacha20(chacha_key, chacha_nonce, padded)\n  const mac = hmacAad(hmac_key, ciphertext, nonce)\n  return base64.encode(concatBytes(new Uint8Array([2]), nonce, ciphertext, mac))\n}\n\nexport function decrypt(payload: string, conversationKey: Uint8Array): string {\n  const { nonce, ciphertext, mac } = decodePayload(payload)\n  const { chacha_key, chacha_nonce, hmac_key } = getMessageKeys(conversationKey, nonce)\n  const calculatedMac = hmacAad(hmac_key, ciphertext, nonce)\n  if (!equalBytes(calculatedMac, mac)) throw new Error('invalid MAC')\n  const padded = chacha20(chacha_key, chacha_nonce, ciphertext)\n  return unpad(padded)\n}\n\nexport const v2 = {\n  utils: {\n    getConversationKey,\n    calcPaddedLen,\n  },\n  encrypt,\n  decrypt,\n}\n", "import { EventTemplate, UnsignedEvent, NostrEvent } from './core.ts'\nimport { getConversationKey, decrypt, encrypt } from './nip44.ts'\nimport { getEventHash, generateSecretKey, finalizeEvent, getPublicKey } from './pure.ts'\nimport { Seal, GiftWrap } from './kinds.ts'\n\ntype Rumor = UnsignedEvent & { id: string }\n\nconst TWO_DAYS = 2 * 24 * 60 * 60\n\nconst now = () => Math.round(Date.now() / 1000)\nconst randomNow = () => Math.round(now() - Math.random() * TWO_DAYS)\n\nconst nip44ConversationKey = (privateKey: Uint8Array, publicKey: string) => getConversationKey(privateKey, publicKey)\n\nconst nip44Encrypt = (data: EventTemplate, privateKey: Uint8Array, publicKey: string) =>\n  encrypt(JSON.stringify(data), nip44ConversationKey(privateKey, publicKey))\n\nconst nip44Decrypt = (data: NostrEvent, privateKey: Uint8Array) =>\n  JSON.parse(decrypt(data.content, nip44ConversationKey(privateKey, data.pubkey)))\n\nexport function createRumor(event: Partial<UnsignedEvent>, privateKey: Uint8Array): Rumor {\n  const rumor = {\n    created_at: now(),\n    content: '',\n    tags: [],\n    ...event,\n    pubkey: getPublicKey(privateKey),\n  } as any\n\n  rumor.id = getEventHash(rumor)\n\n  return rumor as Rumor\n}\n\nexport function createSeal(rumor: Rumor, privateKey: Uint8Array, recipientPublicKey: string): NostrEvent {\n  return finalizeEvent(\n    {\n      kind: Seal,\n      content: nip44Encrypt(rumor, privateKey, recipientPublicKey),\n      created_at: randomNow(),\n      tags: [],\n    },\n    privateKey,\n  )\n}\n\nexport function createWrap(seal: NostrEvent, recipientPublicKey: string): NostrEvent {\n  const randomKey = generateSecretKey()\n\n  return finalizeEvent(\n    {\n      kind: GiftWrap,\n      content: nip44Encrypt(seal, randomKey, recipientPublicKey),\n      created_at: randomNow(),\n      tags: [['p', recipientPublicKey]],\n    },\n    randomKey,\n  ) as NostrEvent\n}\n\nexport function wrapEvent(\n  event: Partial<UnsignedEvent>,\n  senderPrivateKey: Uint8Array,\n  recipientPublicKey: string,\n): NostrEvent {\n  const rumor = createRumor(event, senderPrivateKey)\n\n  const seal = createSeal(rumor, senderPrivateKey, recipientPublicKey)\n  return createWrap(seal, recipientPublicKey)\n}\n\nexport function wrapManyEvents(\n  event: Partial<UnsignedEvent>,\n  senderPrivateKey: Uint8Array,\n  recipientsPublicKeys: string[],\n): NostrEvent[] {\n  if (!recipientsPublicKeys || recipientsPublicKeys.length === 0) {\n    throw new Error('At least one recipient is required.')\n  }\n\n  const senderPublicKey = getPublicKey(senderPrivateKey)\n\n  const wrappeds = [wrapEvent(event, senderPrivateKey, senderPublicKey)]\n\n  recipientsPublicKeys.forEach(recipientPublicKey => {\n    wrappeds.push(wrapEvent(event, senderPrivateKey, recipientPublicKey))\n  })\n\n  return wrappeds\n}\n\nexport function unwrapEvent(wrap: NostrEvent, recipientPrivateKey: Uint8Array): Rumor {\n  const unwrappedSeal = nip44Decrypt(wrap, recipientPrivateKey)\n  return nip44Decrypt(unwrappedSeal, recipientPrivateKey)\n}\n\nexport function unwrapManyEvents(wrappedEvents: NostrEvent[], recipientPrivateKey: Uint8Array): Rumor[] {\n  let unwrappedEvents: Rumor[] = []\n\n  wrappedEvents.forEach(e => {\n    unwrappedEvents.push(unwrapEvent(e, recipientPrivateKey))\n  })\n\n  unwrappedEvents.sort((a, b) => a.created_at - b.created_at)\n\n  return unwrappedEvents\n}\n", "import { PrivateDirectMessage } from './kinds.ts'\nimport { EventTemplate, NostrEvent, getPublicKey } from './pure.ts'\nimport * as nip59 from './nip59.ts'\n\ntype Recipient = {\n  publicKey: string\n  relayUrl?: string\n}\n\ntype ReplyTo = {\n  eventId: string\n  relayUrl?: string\n}\n\nfunction createEvent(\n  recipients: Recipient | Recipient[],\n  message: string,\n  conversationTitle?: string,\n  replyTo?: ReplyTo,\n): EventTemplate {\n  const baseEvent: EventTemplate = {\n    created_at: Math.ceil(Date.now() / 1000),\n    kind: PrivateDirectMessage,\n    tags: [],\n    content: message,\n  }\n\n  const recipientsArray = Array.isArray(recipients) ? recipients : [recipients]\n\n  recipientsArray.forEach(({ publicKey, relayUrl }) => {\n    baseEvent.tags.push(relayUrl ? ['p', publicKey, relayUrl] : ['p', publicKey])\n  })\n\n  if (replyTo) {\n    baseEvent.tags.push(['e', replyTo.eventId, replyTo.relayUrl || '', 'reply'])\n  }\n\n  if (conversationTitle) {\n    baseEvent.tags.push(['subject', conversationTitle])\n  }\n\n  return baseEvent\n}\n\nexport function wrapEvent(\n  senderPrivateKey: Uint8Array,\n  recipient: Recipient,\n  message: string,\n  conversationTitle?: string,\n  replyTo?: ReplyTo,\n): NostrEvent {\n  const event = createEvent(recipient, message, conversationTitle, replyTo)\n  return nip59.wrapEvent(event, senderPrivateKey, recipient.publicKey)\n}\n\nexport function wrapManyEvents(\n  senderPrivateKey: Uint8Array,\n  recipients: Recipient[],\n  message: string,\n  conversationTitle?: string,\n  replyTo?: ReplyTo,\n): NostrEvent[] {\n  if (!recipients || recipients.length === 0) {\n    throw new Error('At least one recipient is required.')\n  }\n\n  const senderPublicKey = getPublicKey(senderPrivateKey)\n\n  // wrap the event for the sender and then for each recipient\n  return [{ publicKey: senderPublicKey }, ...recipients].map(recipient =>\n    wrapEvent(senderPrivateKey, recipient, message, conversationTitle, replyTo),\n  )\n}\n\nexport const unwrapEvent = nip59.unwrapEvent\n\nexport const unwrapManyEvents = nip59.unwrapManyEvents\n"],
-  "mappings": "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",
-  "names": ["bytesToHex", "i", "bytesToHex", "sha256", "sha256", "wrapEvent", "unwrapEvent", "unwrapManyEvents"]
+  "sourcesContent": ["import { schnorr } from '@noble/curves/secp256k1.js'\nimport { bytesToHex, hexToBytes } from '@noble/hashes/utils.js'\nimport { Nostr, Event, EventTemplate, UnsignedEvent, VerifiedEvent, verifiedSymbol, validateEvent } from './core.ts'\nimport { sha256 } from '@noble/hashes/sha2.js'\n\nimport { utf8Encoder } from './utils.ts'\n\nclass JS implements Nostr {\n  generateSecretKey(): Uint8Array {\n    return schnorr.utils.randomSecretKey()\n  }\n  getPublicKey(secretKey: Uint8Array): string {\n    return bytesToHex(schnorr.getPublicKey(secretKey))\n  }\n  finalizeEvent(t: EventTemplate, secretKey: Uint8Array): VerifiedEvent {\n    const event = t as VerifiedEvent\n    event.pubkey = bytesToHex(schnorr.getPublicKey(secretKey))\n    event.id = getEventHash(event)\n    event.sig = bytesToHex(schnorr.sign(hexToBytes(getEventHash(event)), secretKey))\n    event[verifiedSymbol] = true\n    return event\n  }\n  verifyEvent(event: Event): event is VerifiedEvent {\n    if (typeof event[verifiedSymbol] === 'boolean') return event[verifiedSymbol]\n\n    const hash = getEventHash(event)\n    if (hash !== event.id) {\n      event[verifiedSymbol] = false\n      return false\n    }\n\n    try {\n      const valid = schnorr.verify(hexToBytes(event.sig), hexToBytes(hash), hexToBytes(event.pubkey))\n      event[verifiedSymbol] = valid\n      return valid\n    } catch (err) {\n      event[verifiedSymbol] = false\n      return false\n    }\n  }\n}\n\nexport function serializeEvent(evt: UnsignedEvent): string {\n  if (!validateEvent(evt)) throw new Error(\"can't serialize event with wrong or missing properties\")\n  return JSON.stringify([0, evt.pubkey, evt.created_at, evt.kind, evt.tags, evt.content])\n}\n\nexport function getEventHash(event: UnsignedEvent): string {\n  let eventHash = sha256(utf8Encoder.encode(serializeEvent(event)))\n  return bytesToHex(eventHash)\n}\n\nconst i: JS = new JS()\n\nexport const generateSecretKey = i.generateSecretKey\nexport const getPublicKey = i.getPublicKey\nexport const finalizeEvent = i.finalizeEvent\nexport const verifyEvent = i.verifyEvent\nexport * from './core.ts'\n", "export interface Nostr {\n  generateSecretKey(): Uint8Array\n  getPublicKey(secretKey: Uint8Array): string\n  finalizeEvent(event: EventTemplate, secretKey: Uint8Array): VerifiedEvent\n  verifyEvent(event: Event): event is VerifiedEvent\n}\n\n/** Designates a verified event signature. */\nexport const verifiedSymbol = Symbol('verified')\n\nexport type NostrEvent = {\n  kind: number\n  tags: string[][]\n  content: string\n  created_at: number\n  pubkey: string\n  id: string\n  sig: string\n  [verifiedSymbol]?: boolean\n}\n\nexport type Event = NostrEvent\nexport type EventTemplate = Pick<Event, 'kind' | 'tags' | 'content' | 'created_at'>\nexport type UnsignedEvent = Pick<Event, 'kind' | 'tags' | 'content' | 'created_at' | 'pubkey'>\n\n/** An event whose signature has been verified. */\nexport interface VerifiedEvent extends Event {\n  [verifiedSymbol]: true\n}\n\nconst isRecord = (obj: unknown): obj is Record<string, unknown> => obj instanceof Object\n\nexport function validateEvent<T>(event: T): event is T & UnsignedEvent {\n  if (!isRecord(event)) return false\n  if (typeof event.kind !== 'number') return false\n  if (typeof event.content !== 'string') return false\n  if (typeof event.created_at !== 'number') return false\n  if (typeof event.pubkey !== 'string') return false\n  if (!event.pubkey.match(/^[a-f0-9]{64}$/)) return false\n\n  if (!Array.isArray(event.tags)) return false\n  for (let i = 0; i < event.tags.length; i++) {\n    let tag = event.tags[i]\n    if (!Array.isArray(tag)) return false\n    for (let j = 0; j < tag.length; j++) {\n      if (typeof tag[j] !== 'string') return false\n    }\n  }\n\n  return true\n}\n\n/**\n * Sort events in reverse-chronological order by the `created_at` timestamp,\n * and then by the event `id` (lexicographically) in case of ties.\n * This mutates the array.\n */\nexport function sortEvents(events: Event[]): Event[] {\n  return events.sort((a: NostrEvent, b: NostrEvent): number => {\n    if (a.created_at !== b.created_at) {\n      return b.created_at - a.created_at\n    }\n    return a.id.localeCompare(b.id)\n  })\n}\n", "import type { Event } from './core.ts'\n\nexport const utf8Decoder: TextDecoder = new TextDecoder('utf-8')\nexport const utf8Encoder: TextEncoder = new TextEncoder()\n\nexport { bytesToHex, hexToBytes } from '@noble/hashes/utils.js'\n\nexport function normalizeURL(url: string): string {\n  try {\n    if (url.indexOf('://') === -1) url = 'wss://' + url\n    let p = new URL(url)\n    if (p.protocol === 'http:') p.protocol = 'ws:'\n    else if (p.protocol === 'https:') p.protocol = 'wss:'\n    p.pathname = p.pathname.replace(/\\/+/g, '/')\n    if (p.pathname.endsWith('/')) p.pathname = p.pathname.slice(0, -1)\n    if ((p.port === '80' && p.protocol === 'ws:') || (p.port === '443' && p.protocol === 'wss:')) p.port = ''\n    p.searchParams.sort()\n    p.hash = ''\n    return p.toString()\n  } catch (e) {\n    throw new Error(`Invalid URL: ${url}`)\n  }\n}\n\nexport function insertEventIntoDescendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return b.created_at - event.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function insertEventIntoAscendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return event.created_at - b.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function binarySearch<T>(arr: T[], compare: (b: T) => number): [number, boolean] {\n  let start = 0\n  let end = arr.length - 1\n\n  while (start <= end) {\n    const mid = Math.floor((start + end) / 2)\n    const cmp = compare(arr[mid])\n\n    if (cmp === 0) {\n      return [mid, true]\n    }\n\n    if (cmp < 0) {\n      end = mid - 1\n    } else {\n      start = mid + 1\n    }\n  }\n\n  return [start, false]\n}\n\nexport class QueueNode<V> {\n  public value: V\n  public next: QueueNode<V> | null = null\n  public prev: QueueNode<V> | null = null\n\n  constructor(message: V) {\n    this.value = message\n  }\n}\n\nexport class Queue<V> {\n  public first: QueueNode<V> | null\n  public last: QueueNode<V> | null\n\n  constructor() {\n    this.first = null\n    this.last = null\n  }\n\n  enqueue(value: V): boolean {\n    const newNode = new QueueNode(value)\n    if (!this.last) {\n      // list is empty\n      this.first = newNode\n      this.last = newNode\n    } else if (this.last === this.first) {\n      // list has a single element\n      this.last = newNode\n      this.last.prev = this.first\n      this.first.next = newNode\n    } else {\n      // list has elements, add as last\n      newNode.prev = this.last\n      this.last.next = newNode\n      this.last = newNode\n    }\n    return true\n  }\n\n  dequeue(): V | null {\n    if (!this.first) return null\n\n    if (this.first === this.last) {\n      const target = this.first\n      this.first = null\n      this.last = null\n      return target.value\n    }\n\n    const target = this.first\n    this.first = target.next\n    if (this.first) {\n      this.first.prev = null // fix: clean up prev pointer\n    }\n\n    return target.value\n  }\n}\n", "import { NostrEvent, validateEvent } from './pure.ts'\n\n/** Events are **regular**, which means they're all expected to be stored by relays. */\nexport function isRegularKind(kind: number): boolean {\n  return kind < 10000 && kind !== 0 && kind !== 3\n}\n\n/** Events are **replaceable**, which means that, for each combination of `pubkey` and `kind`, only the latest event is expected to (SHOULD) be stored by relays, older versions are expected to be discarded. */\nexport function isReplaceableKind(kind: number): boolean {\n  return kind === 0 || kind === 3 || (10000 <= kind && kind < 20000)\n}\n\n/** Events are **ephemeral**, which means they are not expected to be stored by relays. */\nexport function isEphemeralKind(kind: number): boolean {\n  return 20000 <= kind && kind < 30000\n}\n\n/** Events are **addressable**, which means that, for each combination of `pubkey`, `kind` and the `d` tag, only the latest event is expected to be stored by relays, older versions are expected to be discarded. */\nexport function isAddressableKind(kind: number): boolean {\n  return 30000 <= kind && kind < 40000\n}\n\n/** Classification of the event kind. */\nexport type KindClassification = 'regular' | 'replaceable' | 'ephemeral' | 'parameterized' | 'unknown'\n\n/** Determine the classification of this kind of event if known, or `unknown`. */\nexport function classifyKind(kind: number): KindClassification {\n  if (isRegularKind(kind)) return 'regular'\n  if (isReplaceableKind(kind)) return 'replaceable'\n  if (isEphemeralKind(kind)) return 'ephemeral'\n  if (isAddressableKind(kind)) return 'parameterized'\n  return 'unknown'\n}\n\nexport function isKind<T extends number>(event: unknown, kind: T | Array<T>): event is NostrEvent & { kind: T } {\n  const kindAsArray: number[] = kind instanceof Array ? kind : [kind]\n  return (validateEvent(event) && kindAsArray.includes(event.kind)) || false\n}\n\nexport const Metadata = 0\nexport type Metadata = typeof Metadata\nexport const ShortTextNote = 1\nexport type ShortTextNote = typeof ShortTextNote\nexport const RecommendRelay = 2\nexport type RecommendRelay = typeof RecommendRelay\nexport const Contacts = 3\nexport type Contacts = typeof Contacts\nexport const EncryptedDirectMessage = 4\nexport type EncryptedDirectMessage = typeof EncryptedDirectMessage\nexport const EventDeletion = 5\nexport type EventDeletion = typeof EventDeletion\nexport const Repost = 6\nexport type Repost = typeof Repost\nexport const Reaction = 7\nexport type Reaction = typeof Reaction\nexport const BadgeAward = 8\nexport type BadgeAward = typeof BadgeAward\nexport const ChatMessage = 9\nexport type ChatMessage = typeof ChatMessage\nexport const ForumThread = 11\nexport type ForumThread = typeof ForumThread\nexport const Seal = 13\nexport type Seal = typeof Seal\nexport const PrivateDirectMessage = 14\nexport type PrivateDirectMessage = typeof PrivateDirectMessage\nexport const FileMessage = 15\nexport type FileMessage = typeof FileMessage\nexport const GenericRepost = 16\nexport type GenericRepost = typeof GenericRepost\nexport const Photo = 20\nexport type Photo = typeof Photo\nexport const NormalVideo = 21\nexport type NormalVideo = typeof NormalVideo\nexport const ShortVideo = 22\nexport type ShortVideo = typeof ShortVideo\nexport const ChannelCreation = 40\nexport type ChannelCreation = typeof ChannelCreation\nexport const ChannelMetadata = 41\nexport type ChannelMetadata = typeof ChannelMetadata\nexport const ChannelMessage = 42\nexport type ChannelMessage = typeof ChannelMessage\nexport const ChannelHideMessage = 43\nexport type ChannelHideMessage = typeof ChannelHideMessage\nexport const ChannelMuteUser = 44\nexport type ChannelMuteUser = typeof ChannelMuteUser\nexport const OpenTimestamps = 1040\nexport type OpenTimestamps = typeof OpenTimestamps\nexport const GiftWrap = 1059\nexport type GiftWrap = typeof GiftWrap\nexport const Poll = 1068\nexport type Poll = typeof Poll\nexport const FileMetadata = 1063\nexport type FileMetadata = typeof FileMetadata\nexport const Comment = 1111\nexport type Comment = typeof Comment\nexport const LiveChatMessage = 1311\nexport type LiveChatMessage = typeof LiveChatMessage\nexport const Voice = 1222\nexport type Voice = typeof Voice\nexport const VoiceComment = 1244\nexport type VoiceComment = typeof VoiceComment\nexport const ProblemTracker = 1971\nexport type ProblemTracker = typeof ProblemTracker\nexport const Report = 1984\nexport type Report = typeof Report\nexport const Reporting = 1984\nexport type Reporting = typeof Reporting\nexport const Label = 1985\nexport type Label = typeof Label\nexport const CommunityPostApproval = 4550\nexport type CommunityPostApproval = typeof CommunityPostApproval\nexport const JobRequest = 5999\nexport type JobRequest = typeof JobRequest\nexport const JobResult = 6999\nexport type JobResult = typeof JobResult\nexport const JobFeedback = 7000\nexport type JobFeedback = typeof JobFeedback\nexport const ZapGoal = 9041\nexport type ZapGoal = typeof ZapGoal\nexport const ZapRequest = 9734\nexport type ZapRequest = typeof ZapRequest\nexport const Zap = 9735\nexport type Zap = typeof Zap\nexport const Highlights = 9802\nexport type Highlights = typeof Highlights\nexport const PollResponse = 1018\nexport type PollResponse = typeof PollResponse\nexport const Mutelist = 10000\nexport type Mutelist = typeof Mutelist\nexport const Pinlist = 10001\nexport type Pinlist = typeof Pinlist\nexport const RelayList = 10002\nexport type RelayList = typeof RelayList\nexport const BookmarkList = 10003\nexport type BookmarkList = typeof BookmarkList\nexport const CommunitiesList = 10004\nexport type CommunitiesList = typeof CommunitiesList\nexport const PublicChatsList = 10005\nexport type PublicChatsList = typeof PublicChatsList\nexport const BlockedRelaysList = 10006\nexport type BlockedRelaysList = typeof BlockedRelaysList\nexport const SearchRelaysList = 10007\nexport type SearchRelaysList = typeof SearchRelaysList\nexport const FavoriteRelays = 10012\nexport type FavoriteRelays = typeof FavoriteRelays\nexport const InterestsList = 10015\nexport type InterestsList = typeof InterestsList\nexport const UserEmojiList = 10030\nexport type UserEmojiList = typeof UserEmojiList\nexport const DirectMessageRelaysList = 10050\nexport type DirectMessageRelaysList = typeof DirectMessageRelaysList\nexport const FileServerPreference = 10096\nexport type FileServerPreference = typeof FileServerPreference\nexport const BlossomServerList = 10063\nexport type BlossomServerList = typeof BlossomServerList\nexport const NWCWalletInfo = 13194\nexport type NWCWalletInfo = typeof NWCWalletInfo\nexport const LightningPubRPC = 21000\nexport type LightningPubRPC = typeof LightningPubRPC\nexport const ClientAuth = 22242\nexport type ClientAuth = typeof ClientAuth\nexport const NWCWalletRequest = 23194\nexport type NWCWalletRequest = typeof NWCWalletRequest\nexport const NWCWalletResponse = 23195\nexport type NWCWalletResponse = typeof NWCWalletResponse\nexport const NostrConnect = 24133\nexport type NostrConnect = typeof NostrConnect\nexport const HTTPAuth = 27235\nexport type HTTPAuth = typeof HTTPAuth\nexport const Followsets = 30000\nexport type Followsets = typeof Followsets\nexport const Genericlists = 30001\nexport type Genericlists = typeof Genericlists\nexport const Relaysets = 30002\nexport type Relaysets = typeof Relaysets\nexport const Bookmarksets = 30003\nexport type Bookmarksets = typeof Bookmarksets\nexport const Curationsets = 30004\nexport type Curationsets = typeof Curationsets\nexport const ProfileBadges = 30008\nexport type ProfileBadges = typeof ProfileBadges\nexport const BadgeDefinition = 30009\nexport type BadgeDefinition = typeof BadgeDefinition\nexport const Interestsets = 30015\nexport type Interestsets = typeof Interestsets\nexport const CreateOrUpdateStall = 30017\nexport type CreateOrUpdateStall = typeof CreateOrUpdateStall\nexport const CreateOrUpdateProduct = 30018\nexport type CreateOrUpdateProduct = typeof CreateOrUpdateProduct\nexport const LongFormArticle = 30023\nexport type LongFormArticle = typeof LongFormArticle\nexport const DraftLong = 30024\nexport type DraftLong = typeof DraftLong\nexport const Emojisets = 30030\nexport type Emojisets = typeof Emojisets\nexport const Application = 30078\nexport type Application = typeof Application\nexport const LiveEvent = 30311\nexport type LiveEvent = typeof LiveEvent\nexport const UserStatuses = 30315\nexport type UserStatuses = typeof UserStatuses\nexport const ClassifiedListing = 30402\nexport type ClassifiedListing = typeof ClassifiedListing\nexport const DraftClassifiedListing = 30403\nexport type DraftClassifiedListing = typeof DraftClassifiedListing\nexport const Date = 31922\nexport type Date = typeof Date\nexport const Time = 31923\nexport type Time = typeof Time\nexport const Calendar = 31924\nexport type Calendar = typeof Calendar\nexport const CalendarEventRSVP = 31925\nexport type CalendarEventRSVP = typeof CalendarEventRSVP\nexport const RelayReview = 31987\nexport type RelayReview = typeof RelayReview\nexport const Handlerrecommendation = 31989\nexport type Handlerrecommendation = typeof Handlerrecommendation\nexport const Handlerinformation = 31990\nexport type Handlerinformation = typeof Handlerinformation\nexport const CommunityDefinition = 34550\nexport type CommunityDefinition = typeof CommunityDefinition\nexport const GroupMetadata = 39000\nexport type GroupMetadata = typeof GroupMetadata\n", "import { chacha20 } from '@noble/ciphers/chacha.js'\nimport { equalBytes } from '@noble/ciphers/utils.js'\nimport { secp256k1 } from '@noble/curves/secp256k1.js'\nimport { extract as hkdf_extract, expand as hkdf_expand } from '@noble/hashes/hkdf.js'\nimport { hmac } from '@noble/hashes/hmac.js'\nimport { sha256 } from '@noble/hashes/sha2.js'\nimport { concatBytes, hexToBytes, randomBytes } from '@noble/hashes/utils.js'\nimport { base64 } from '@scure/base'\n\nimport { utf8Decoder, utf8Encoder } from './utils.ts'\n\nconst minPlaintextSize = 0x0001 // 1b msg => padded to 32b\nconst maxPlaintextSize = 0xffff // 65535 (64kb-1) => padded to 64kb\n\nexport function getConversationKey(privkeyA: Uint8Array, pubkeyB: string): Uint8Array {\n  const sharedX = secp256k1.getSharedSecret(privkeyA, hexToBytes('02' + pubkeyB)).subarray(1, 33)\n  return hkdf_extract(sha256, sharedX, utf8Encoder.encode('nip44-v2'))\n}\n\nfunction getMessageKeys(\n  conversationKey: Uint8Array,\n  nonce: Uint8Array,\n): { chacha_key: Uint8Array; chacha_nonce: Uint8Array; hmac_key: Uint8Array } {\n  const keys = hkdf_expand(sha256, conversationKey, nonce, 76)\n  return {\n    chacha_key: keys.subarray(0, 32),\n    chacha_nonce: keys.subarray(32, 44),\n    hmac_key: keys.subarray(44, 76),\n  }\n}\n\nfunction calcPaddedLen(len: number): number {\n  if (!Number.isSafeInteger(len) || len < 1) throw new Error('expected positive integer')\n  if (len <= 32) return 32\n  const nextPower = 1 << (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 > maxPlaintextSize)\n    throw new Error('invalid plaintext size: must be between 1 and 65535 bytes')\n  const arr = new Uint8Array(2)\n  new DataView(arr.buffer).setUint16(0, num, false)\n  return arr\n}\n\nfunction pad(plaintext: string): Uint8Array {\n  const unpadded = utf8Encoder.encode(plaintext)\n  const unpaddedLen = unpadded.length\n  const prefix = 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 unpaddedLen = new DataView(padded.buffer).getUint16(0)\n  const unpadded = padded.subarray(2, 2 + unpaddedLen)\n  if (\n    unpaddedLen < minPlaintextSize ||\n    unpaddedLen > maxPlaintextSize ||\n    unpadded.length !== unpaddedLen ||\n    padded.length !== 2 + calcPaddedLen(unpaddedLen)\n  )\n    throw new Error('invalid padding')\n  return utf8Decoder.decode(unpadded)\n}\n\nfunction hmacAad(key: Uint8Array, message: Uint8Array, aad: Uint8Array): Uint8Array {\n  if (aad.length !== 32) throw new Error('AAD associated data must be 32 bytes')\n  const combined = concatBytes(aad, message)\n  return hmac(sha256, key, combined)\n}\n\n// metadata: always 65b (version: 1b, nonce: 32b, max: 32b)\n// plaintext: 1b to 0xffff\n// padded plaintext: 32b to 0xffff\n// ciphertext: 32b+2 to 0xffff+2\n// raw payload: 99 (65+32+2) to 65603 (65+0xffff+2)\n// compressed payload (base64): 132b to 87472b\nfunction decodePayload(payload: string): { nonce: Uint8Array; ciphertext: Uint8Array; mac: Uint8Array } {\n  if (typeof payload !== 'string') throw new Error('payload must be a valid string')\n  const plen = payload.length\n  if (plen < 132 || plen > 87472) throw new Error('invalid payload length: ' + plen)\n  if (payload[0] === '#') throw new Error('unknown encryption version')\n  let data: Uint8Array\n  try {\n    data = base64.decode(payload)\n  } catch (error) {\n    throw new Error('invalid base64: ' + (error as any).message)\n  }\n  const dlen = data.length\n  if (dlen < 99 || dlen > 65603) throw new Error('invalid data length: ' + dlen)\n  const vers = data[0]\n  if (vers !== 2) throw new Error('unknown encryption version ' + vers)\n  return {\n    nonce: data.subarray(1, 33),\n    ciphertext: data.subarray(33, -32),\n    mac: data.subarray(-32),\n  }\n}\n\nexport function encrypt(plaintext: string, conversationKey: Uint8Array, nonce: Uint8Array = randomBytes(32)): string {\n  const { chacha_key, chacha_nonce, hmac_key } = getMessageKeys(conversationKey, nonce)\n  const padded = pad(plaintext)\n  const ciphertext = chacha20(chacha_key, chacha_nonce, padded)\n  const mac = hmacAad(hmac_key, ciphertext, nonce)\n  return base64.encode(concatBytes(new Uint8Array([2]), nonce, ciphertext, mac))\n}\n\nexport function decrypt(payload: string, conversationKey: Uint8Array): string {\n  const { nonce, ciphertext, mac } = decodePayload(payload)\n  const { chacha_key, chacha_nonce, hmac_key } = getMessageKeys(conversationKey, nonce)\n  const calculatedMac = hmacAad(hmac_key, ciphertext, nonce)\n  if (!equalBytes(calculatedMac, mac)) throw new Error('invalid MAC')\n  const padded = chacha20(chacha_key, chacha_nonce, ciphertext)\n  return unpad(padded)\n}\n\nexport const v2 = {\n  utils: {\n    getConversationKey,\n    calcPaddedLen,\n  },\n  encrypt,\n  decrypt,\n}\n", "import { EventTemplate, UnsignedEvent, NostrEvent } from './core.ts'\nimport { getConversationKey, decrypt, encrypt } from './nip44.ts'\nimport { getEventHash, generateSecretKey, finalizeEvent, getPublicKey } from './pure.ts'\nimport { Seal, GiftWrap } from './kinds.ts'\n\ntype Rumor = UnsignedEvent & { id: string }\n\nconst TWO_DAYS = 2 * 24 * 60 * 60\n\nconst now = () => Math.round(Date.now() / 1000)\nconst randomNow = () => Math.round(now() - Math.random() * TWO_DAYS)\n\nconst nip44ConversationKey = (privateKey: Uint8Array, publicKey: string) => getConversationKey(privateKey, publicKey)\n\nconst nip44Encrypt = (data: EventTemplate, privateKey: Uint8Array, publicKey: string) =>\n  encrypt(JSON.stringify(data), nip44ConversationKey(privateKey, publicKey))\n\nconst nip44Decrypt = (data: NostrEvent, privateKey: Uint8Array) =>\n  JSON.parse(decrypt(data.content, nip44ConversationKey(privateKey, data.pubkey)))\n\nexport function createRumor(event: Partial<UnsignedEvent>, privateKey: Uint8Array): Rumor {\n  const rumor = {\n    created_at: now(),\n    content: '',\n    tags: [],\n    ...event,\n    pubkey: getPublicKey(privateKey),\n  } as any\n\n  rumor.id = getEventHash(rumor)\n\n  return rumor as Rumor\n}\n\nexport function createSeal(rumor: Rumor, privateKey: Uint8Array, recipientPublicKey: string): NostrEvent {\n  return finalizeEvent(\n    {\n      kind: Seal,\n      content: nip44Encrypt(rumor, privateKey, recipientPublicKey),\n      created_at: randomNow(),\n      tags: [],\n    },\n    privateKey,\n  )\n}\n\nexport function createWrap(seal: NostrEvent, recipientPublicKey: string): NostrEvent {\n  const randomKey = generateSecretKey()\n\n  return finalizeEvent(\n    {\n      kind: GiftWrap,\n      content: nip44Encrypt(seal, randomKey, recipientPublicKey),\n      created_at: randomNow(),\n      tags: [['p', recipientPublicKey]],\n    },\n    randomKey,\n  ) as NostrEvent\n}\n\nexport function wrapEvent(\n  event: Partial<UnsignedEvent>,\n  senderPrivateKey: Uint8Array,\n  recipientPublicKey: string,\n): NostrEvent {\n  const rumor = createRumor(event, senderPrivateKey)\n\n  const seal = createSeal(rumor, senderPrivateKey, recipientPublicKey)\n  return createWrap(seal, recipientPublicKey)\n}\n\nexport function wrapManyEvents(\n  event: Partial<UnsignedEvent>,\n  senderPrivateKey: Uint8Array,\n  recipientsPublicKeys: string[],\n): NostrEvent[] {\n  if (!recipientsPublicKeys || recipientsPublicKeys.length === 0) {\n    throw new Error('At least one recipient is required.')\n  }\n\n  const senderPublicKey = getPublicKey(senderPrivateKey)\n\n  const wrappeds = [wrapEvent(event, senderPrivateKey, senderPublicKey)]\n\n  recipientsPublicKeys.forEach(recipientPublicKey => {\n    wrappeds.push(wrapEvent(event, senderPrivateKey, recipientPublicKey))\n  })\n\n  return wrappeds\n}\n\nexport function unwrapEvent(wrap: NostrEvent, recipientPrivateKey: Uint8Array): Rumor {\n  const unwrappedSeal = nip44Decrypt(wrap, recipientPrivateKey)\n  return nip44Decrypt(unwrappedSeal, recipientPrivateKey)\n}\n\nexport function unwrapManyEvents(wrappedEvents: NostrEvent[], recipientPrivateKey: Uint8Array): Rumor[] {\n  let unwrappedEvents: Rumor[] = []\n\n  wrappedEvents.forEach(e => {\n    unwrappedEvents.push(unwrapEvent(e, recipientPrivateKey))\n  })\n\n  unwrappedEvents.sort((a, b) => a.created_at - b.created_at)\n\n  return unwrappedEvents\n}\n", "import { PrivateDirectMessage } from './kinds.ts'\nimport { EventTemplate, NostrEvent, getPublicKey } from './pure.ts'\nimport * as nip59 from './nip59.ts'\n\ntype Recipient = {\n  publicKey: string\n  relayUrl?: string\n}\n\ntype ReplyTo = {\n  eventId: string\n  relayUrl?: string\n}\n\nfunction createEvent(\n  recipients: Recipient | Recipient[],\n  message: string,\n  conversationTitle?: string,\n  replyTo?: ReplyTo,\n): EventTemplate {\n  const baseEvent: EventTemplate = {\n    created_at: Math.ceil(Date.now() / 1000),\n    kind: PrivateDirectMessage,\n    tags: [],\n    content: message,\n  }\n\n  const recipientsArray = Array.isArray(recipients) ? recipients : [recipients]\n\n  recipientsArray.forEach(({ publicKey, relayUrl }) => {\n    baseEvent.tags.push(relayUrl ? ['p', publicKey, relayUrl] : ['p', publicKey])\n  })\n\n  if (replyTo) {\n    baseEvent.tags.push(['e', replyTo.eventId, replyTo.relayUrl || '', 'reply'])\n  }\n\n  if (conversationTitle) {\n    baseEvent.tags.push(['subject', conversationTitle])\n  }\n\n  return baseEvent\n}\n\nexport function wrapEvent(\n  senderPrivateKey: Uint8Array,\n  recipient: Recipient,\n  message: string,\n  conversationTitle?: string,\n  replyTo?: ReplyTo,\n): NostrEvent {\n  const event = createEvent(recipient, message, conversationTitle, replyTo)\n  return nip59.wrapEvent(event, senderPrivateKey, recipient.publicKey)\n}\n\nexport function wrapManyEvents(\n  senderPrivateKey: Uint8Array,\n  recipients: Recipient[],\n  message: string,\n  conversationTitle?: string,\n  replyTo?: ReplyTo,\n): NostrEvent[] {\n  if (!recipients || recipients.length === 0) {\n    throw new Error('At least one recipient is required.')\n  }\n\n  const senderPublicKey = getPublicKey(senderPrivateKey)\n\n  // wrap the event for the sender and then for each recipient\n  return [{ publicKey: senderPublicKey }, ...recipients].map(recipient =>\n    wrapEvent(senderPrivateKey, recipient, message, conversationTitle, replyTo),\n  )\n}\n\nexport const unwrapEvent = nip59.unwrapEvent\n\nexport const unwrapManyEvents = nip59.unwrapManyEvents\n"],
+  "mappings": "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",
+  "names": ["bytesToHex", "hexToBytes", "i", "bytesToHex", "hexToBytes", "sha256", "hexToBytes", "hexToBytes", "sha256", "wrapEvent", "unwrapEvent", "unwrapManyEvents"]
 }
Index: package/lib/cjs/nip18.js.map
===================================================================
--- package/lib/cjs/nip18.js.map
+++ package/lib/cjs/nip18.js.map
@@ -1,7 +1,7 @@
 {
   "version": 3,
   "sources": ["../../nip18.ts", "../../pure.ts", "../../core.ts", "../../utils.ts", "../../kinds.ts"],
-  "sourcesContent": ["import { GenericRepost, Repost, ShortTextNote } from './kinds.ts'\nimport { EventPointer } from './nip19.ts'\nimport { Event, finalizeEvent, verifyEvent } from './pure.ts'\n\nexport type RepostEventTemplate = {\n  /**\n   * Pass only non-nip18 tags if you have to.\n   * Nip18 tags ('e' and 'p' tags pointing to the reposted event) will be added automatically.\n   */\n  tags?: string[][]\n\n  /**\n   * Pass an empty string to NOT include the stringified JSON of the reposted event.\n   * Any other content will be ignored and replaced with the stringified JSON of the reposted event.\n   * @default Stringified JSON of the reposted event\n   */\n  content?: ''\n\n  created_at: number\n}\n\nexport function finishRepostEvent(\n  t: RepostEventTemplate,\n  reposted: Event,\n  relayUrl: string,\n  privateKey: Uint8Array,\n): Event {\n  let kind: Repost | GenericRepost\n  const tags = [...(t.tags ?? []), ['e', reposted.id, relayUrl], ['p', reposted.pubkey]]\n  if (reposted.kind === ShortTextNote) {\n    kind = Repost\n  } else {\n    kind = GenericRepost\n    tags.push(['k', String(reposted.kind)])\n  }\n\n  return finalizeEvent(\n    {\n      kind,\n      tags,\n      content: t.content === '' || reposted.tags?.find(tag => tag[0] === '-') ? '' : JSON.stringify(reposted),\n      created_at: t.created_at,\n    },\n    privateKey,\n  )\n}\n\nexport function getRepostedEventPointer(event: Event): undefined | EventPointer {\n  if (![Repost, GenericRepost].includes(event.kind)) {\n    return undefined\n  }\n\n  let lastETag: undefined | string[]\n  let lastPTag: undefined | string[]\n\n  for (let i = event.tags.length - 1; i >= 0 && (lastETag === undefined || lastPTag === undefined); i--) {\n    const tag = event.tags[i]\n    if (tag.length >= 2) {\n      if (tag[0] === 'e' && lastETag === undefined) {\n        lastETag = tag\n      } else if (tag[0] === 'p' && lastPTag === undefined) {\n        lastPTag = tag\n      }\n    }\n  }\n\n  if (lastETag === undefined) {\n    return undefined\n  }\n\n  return {\n    id: lastETag[1],\n    relays: [lastETag[2], lastPTag?.[2]].filter((x): x is string => typeof x === 'string'),\n    author: lastPTag?.[1],\n  }\n}\n\nexport type GetRepostedEventOptions = {\n  skipVerification?: boolean\n}\n\nexport function getRepostedEvent(event: Event, { skipVerification }: GetRepostedEventOptions = {}): undefined | Event {\n  const pointer = getRepostedEventPointer(event)\n\n  if (pointer === undefined || event.content === '') {\n    return undefined\n  }\n\n  let repostedEvent: undefined | Event\n\n  try {\n    repostedEvent = JSON.parse(event.content) as Event\n  } catch (error) {\n    return undefined\n  }\n\n  if (repostedEvent.id !== pointer.id) {\n    return undefined\n  }\n\n  if (!skipVerification && !verifyEvent(repostedEvent)) {\n    return undefined\n  }\n\n  return repostedEvent\n}\n", "import { schnorr } from '@noble/curves/secp256k1'\nimport { bytesToHex } from '@noble/hashes/utils'\nimport { Nostr, Event, EventTemplate, UnsignedEvent, VerifiedEvent, verifiedSymbol, validateEvent } from './core.ts'\nimport { sha256 } from '@noble/hashes/sha256'\n\nimport { utf8Encoder } from './utils.ts'\n\nclass JS implements Nostr {\n  generateSecretKey(): Uint8Array {\n    return schnorr.utils.randomPrivateKey()\n  }\n  getPublicKey(secretKey: Uint8Array): string {\n    return bytesToHex(schnorr.getPublicKey(secretKey))\n  }\n  finalizeEvent(t: EventTemplate, secretKey: Uint8Array): VerifiedEvent {\n    const event = t as VerifiedEvent\n    event.pubkey = bytesToHex(schnorr.getPublicKey(secretKey))\n    event.id = getEventHash(event)\n    event.sig = bytesToHex(schnorr.sign(getEventHash(event), secretKey))\n    event[verifiedSymbol] = true\n    return event\n  }\n  verifyEvent(event: Event): event is VerifiedEvent {\n    if (typeof event[verifiedSymbol] === 'boolean') return event[verifiedSymbol]\n\n    const hash = getEventHash(event)\n    if (hash !== event.id) {\n      event[verifiedSymbol] = false\n      return false\n    }\n\n    try {\n      const valid = schnorr.verify(event.sig, hash, event.pubkey)\n      event[verifiedSymbol] = valid\n      return valid\n    } catch (err) {\n      event[verifiedSymbol] = false\n      return false\n    }\n  }\n}\n\nexport function serializeEvent(evt: UnsignedEvent): string {\n  if (!validateEvent(evt)) throw new Error(\"can't serialize event with wrong or missing properties\")\n  return JSON.stringify([0, evt.pubkey, evt.created_at, evt.kind, evt.tags, evt.content])\n}\n\nexport function getEventHash(event: UnsignedEvent): string {\n  let eventHash = sha256(utf8Encoder.encode(serializeEvent(event)))\n  return bytesToHex(eventHash)\n}\n\nconst i: JS = new JS()\n\nexport const generateSecretKey = i.generateSecretKey\nexport const getPublicKey = i.getPublicKey\nexport const finalizeEvent = i.finalizeEvent\nexport const verifyEvent = i.verifyEvent\nexport * from './core.ts'\n", "export interface Nostr {\n  generateSecretKey(): Uint8Array\n  getPublicKey(secretKey: Uint8Array): string\n  finalizeEvent(event: EventTemplate, secretKey: Uint8Array): VerifiedEvent\n  verifyEvent(event: Event): event is VerifiedEvent\n}\n\n/** Designates a verified event signature. */\nexport const verifiedSymbol = Symbol('verified')\n\nexport type NostrEvent = {\n  kind: number\n  tags: string[][]\n  content: string\n  created_at: number\n  pubkey: string\n  id: string\n  sig: string\n  [verifiedSymbol]?: boolean\n}\n\nexport type Event = NostrEvent\nexport type EventTemplate = Pick<Event, 'kind' | 'tags' | 'content' | 'created_at'>\nexport type UnsignedEvent = Pick<Event, 'kind' | 'tags' | 'content' | 'created_at' | 'pubkey'>\n\n/** An event whose signature has been verified. */\nexport interface VerifiedEvent extends Event {\n  [verifiedSymbol]: true\n}\n\nconst isRecord = (obj: unknown): obj is Record<string, unknown> => obj instanceof Object\n\nexport function validateEvent<T>(event: T): event is T & UnsignedEvent {\n  if (!isRecord(event)) return false\n  if (typeof event.kind !== 'number') return false\n  if (typeof event.content !== 'string') return false\n  if (typeof event.created_at !== 'number') return false\n  if (typeof event.pubkey !== 'string') return false\n  if (!event.pubkey.match(/^[a-f0-9]{64}$/)) return false\n\n  if (!Array.isArray(event.tags)) return false\n  for (let i = 0; i < event.tags.length; i++) {\n    let tag = event.tags[i]\n    if (!Array.isArray(tag)) return false\n    for (let j = 0; j < tag.length; j++) {\n      if (typeof tag[j] !== 'string') return false\n    }\n  }\n\n  return true\n}\n\n/**\n * Sort events in reverse-chronological order by the `created_at` timestamp,\n * and then by the event `id` (lexicographically) in case of ties.\n * This mutates the array.\n */\nexport function sortEvents(events: Event[]): Event[] {\n  return events.sort((a: NostrEvent, b: NostrEvent): number => {\n    if (a.created_at !== b.created_at) {\n      return b.created_at - a.created_at\n    }\n    return a.id.localeCompare(b.id)\n  })\n}\n", "import type { Event } from './core.ts'\n\nexport const utf8Decoder: TextDecoder = new TextDecoder('utf-8')\nexport const utf8Encoder: TextEncoder = new TextEncoder()\n\nexport { bytesToHex, hexToBytes } from '@noble/hashes/utils'\n\nexport function normalizeURL(url: string): string {\n  try {\n    if (url.indexOf('://') === -1) url = 'wss://' + url\n    let p = new URL(url)\n    if (p.protocol === 'http:') p.protocol = 'ws:'\n    else if (p.protocol === 'https:') p.protocol = 'wss:'\n    p.pathname = p.pathname.replace(/\\/+/g, '/')\n    if (p.pathname.endsWith('/')) p.pathname = p.pathname.slice(0, -1)\n    if ((p.port === '80' && p.protocol === 'ws:') || (p.port === '443' && p.protocol === 'wss:')) p.port = ''\n    p.searchParams.sort()\n    p.hash = ''\n    return p.toString()\n  } catch (e) {\n    throw new Error(`Invalid URL: ${url}`)\n  }\n}\n\nexport function insertEventIntoDescendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return b.created_at - event.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function insertEventIntoAscendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return event.created_at - b.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function binarySearch<T>(arr: T[], compare: (b: T) => number): [number, boolean] {\n  let start = 0\n  let end = arr.length - 1\n\n  while (start <= end) {\n    const mid = Math.floor((start + end) / 2)\n    const cmp = compare(arr[mid])\n\n    if (cmp === 0) {\n      return [mid, true]\n    }\n\n    if (cmp < 0) {\n      end = mid - 1\n    } else {\n      start = mid + 1\n    }\n  }\n\n  return [start, false]\n}\n\nexport class QueueNode<V> {\n  public value: V\n  public next: QueueNode<V> | null = null\n  public prev: QueueNode<V> | null = null\n\n  constructor(message: V) {\n    this.value = message\n  }\n}\n\nexport class Queue<V> {\n  public first: QueueNode<V> | null\n  public last: QueueNode<V> | null\n\n  constructor() {\n    this.first = null\n    this.last = null\n  }\n\n  enqueue(value: V): boolean {\n    const newNode = new QueueNode(value)\n    if (!this.last) {\n      // list is empty\n      this.first = newNode\n      this.last = newNode\n    } else if (this.last === this.first) {\n      // list has a single element\n      this.last = newNode\n      this.last.prev = this.first\n      this.first.next = newNode\n    } else {\n      // list has elements, add as last\n      newNode.prev = this.last\n      this.last.next = newNode\n      this.last = newNode\n    }\n    return true\n  }\n\n  dequeue(): V | null {\n    if (!this.first) return null\n\n    if (this.first === this.last) {\n      const target = this.first\n      this.first = null\n      this.last = null\n      return target.value\n    }\n\n    const target = this.first\n    this.first = target.next\n    if (this.first) {\n      this.first.prev = null // fix: clean up prev pointer\n    }\n\n    return target.value\n  }\n}\n", "import { NostrEvent, validateEvent } from './pure.ts'\n\n/** Events are **regular**, which means they're all expected to be stored by relays. */\nexport function isRegularKind(kind: number): boolean {\n  return kind < 10000 && kind !== 0 && kind !== 3\n}\n\n/** Events are **replaceable**, which means that, for each combination of `pubkey` and `kind`, only the latest event is expected to (SHOULD) be stored by relays, older versions are expected to be discarded. */\nexport function isReplaceableKind(kind: number): boolean {\n  return kind === 0 || kind === 3 || (10000 <= kind && kind < 20000)\n}\n\n/** Events are **ephemeral**, which means they are not expected to be stored by relays. */\nexport function isEphemeralKind(kind: number): boolean {\n  return 20000 <= kind && kind < 30000\n}\n\n/** Events are **addressable**, which means that, for each combination of `pubkey`, `kind` and the `d` tag, only the latest event is expected to be stored by relays, older versions are expected to be discarded. */\nexport function isAddressableKind(kind: number): boolean {\n  return 30000 <= kind && kind < 40000\n}\n\n/** Classification of the event kind. */\nexport type KindClassification = 'regular' | 'replaceable' | 'ephemeral' | 'parameterized' | 'unknown'\n\n/** Determine the classification of this kind of event if known, or `unknown`. */\nexport function classifyKind(kind: number): KindClassification {\n  if (isRegularKind(kind)) return 'regular'\n  if (isReplaceableKind(kind)) return 'replaceable'\n  if (isEphemeralKind(kind)) return 'ephemeral'\n  if (isAddressableKind(kind)) return 'parameterized'\n  return 'unknown'\n}\n\nexport function isKind<T extends number>(event: unknown, kind: T | Array<T>): event is NostrEvent & { kind: T } {\n  const kindAsArray: number[] = kind instanceof Array ? kind : [kind]\n  return (validateEvent(event) && kindAsArray.includes(event.kind)) || false\n}\n\nexport const Metadata = 0\nexport type Metadata = typeof Metadata\nexport const ShortTextNote = 1\nexport type ShortTextNote = typeof ShortTextNote\nexport const RecommendRelay = 2\nexport type RecommendRelay = typeof RecommendRelay\nexport const Contacts = 3\nexport type Contacts = typeof Contacts\nexport const EncryptedDirectMessage = 4\nexport type EncryptedDirectMessage = typeof EncryptedDirectMessage\nexport const EventDeletion = 5\nexport type EventDeletion = typeof EventDeletion\nexport const Repost = 6\nexport type Repost = typeof Repost\nexport const Reaction = 7\nexport type Reaction = typeof Reaction\nexport const BadgeAward = 8\nexport type BadgeAward = typeof BadgeAward\nexport const ChatMessage = 9\nexport type ChatMessage = typeof ChatMessage\nexport const ForumThread = 11\nexport type ForumThread = typeof ForumThread\nexport const Seal = 13\nexport type Seal = typeof Seal\nexport const PrivateDirectMessage = 14\nexport type PrivateDirectMessage = typeof PrivateDirectMessage\nexport const FileMessage = 15\nexport type FileMessage = typeof FileMessage\nexport const GenericRepost = 16\nexport type GenericRepost = typeof GenericRepost\nexport const Photo = 20\nexport type Photo = typeof Photo\nexport const NormalVideo = 21\nexport type NormalVideo = typeof NormalVideo\nexport const ShortVideo = 22\nexport type ShortVideo = typeof ShortVideo\nexport const ChannelCreation = 40\nexport type ChannelCreation = typeof ChannelCreation\nexport const ChannelMetadata = 41\nexport type ChannelMetadata = typeof ChannelMetadata\nexport const ChannelMessage = 42\nexport type ChannelMessage = typeof ChannelMessage\nexport const ChannelHideMessage = 43\nexport type ChannelHideMessage = typeof ChannelHideMessage\nexport const ChannelMuteUser = 44\nexport type ChannelMuteUser = typeof ChannelMuteUser\nexport const OpenTimestamps = 1040\nexport type OpenTimestamps = typeof OpenTimestamps\nexport const GiftWrap = 1059\nexport type GiftWrap = typeof GiftWrap\nexport const Poll = 1068\nexport type Poll = typeof Poll\nexport const FileMetadata = 1063\nexport type FileMetadata = typeof FileMetadata\nexport const Comment = 1111\nexport type Comment = typeof Comment\nexport const LiveChatMessage = 1311\nexport type LiveChatMessage = typeof LiveChatMessage\nexport const Voice = 1222\nexport type Voice = typeof Voice\nexport const VoiceComment = 1244\nexport type VoiceComment = typeof VoiceComment\nexport const ProblemTracker = 1971\nexport type ProblemTracker = typeof ProblemTracker\nexport const Report = 1984\nexport type Report = typeof Report\nexport const Reporting = 1984\nexport type Reporting = typeof Reporting\nexport const Label = 1985\nexport type Label = typeof Label\nexport const CommunityPostApproval = 4550\nexport type CommunityPostApproval = typeof CommunityPostApproval\nexport const JobRequest = 5999\nexport type JobRequest = typeof JobRequest\nexport const JobResult = 6999\nexport type JobResult = typeof JobResult\nexport const JobFeedback = 7000\nexport type JobFeedback = typeof JobFeedback\nexport const ZapGoal = 9041\nexport type ZapGoal = typeof ZapGoal\nexport const ZapRequest = 9734\nexport type ZapRequest = typeof ZapRequest\nexport const Zap = 9735\nexport type Zap = typeof Zap\nexport const Highlights = 9802\nexport type Highlights = typeof Highlights\nexport const PollResponse = 1018\nexport type PollResponse = typeof PollResponse\nexport const Mutelist = 10000\nexport type Mutelist = typeof Mutelist\nexport const Pinlist = 10001\nexport type Pinlist = typeof Pinlist\nexport const RelayList = 10002\nexport type RelayList = typeof RelayList\nexport const BookmarkList = 10003\nexport type BookmarkList = typeof BookmarkList\nexport const CommunitiesList = 10004\nexport type CommunitiesList = typeof CommunitiesList\nexport const PublicChatsList = 10005\nexport type PublicChatsList = typeof PublicChatsList\nexport const BlockedRelaysList = 10006\nexport type BlockedRelaysList = typeof BlockedRelaysList\nexport const SearchRelaysList = 10007\nexport type SearchRelaysList = typeof SearchRelaysList\nexport const FavoriteRelays = 10012\nexport type FavoriteRelays = typeof FavoriteRelays\nexport const InterestsList = 10015\nexport type InterestsList = typeof InterestsList\nexport const UserEmojiList = 10030\nexport type UserEmojiList = typeof UserEmojiList\nexport const DirectMessageRelaysList = 10050\nexport type DirectMessageRelaysList = typeof DirectMessageRelaysList\nexport const FileServerPreference = 10096\nexport type FileServerPreference = typeof FileServerPreference\nexport const BlossomServerList = 10063\nexport type BlossomServerList = typeof BlossomServerList\nexport const NWCWalletInfo = 13194\nexport type NWCWalletInfo = typeof NWCWalletInfo\nexport const LightningPubRPC = 21000\nexport type LightningPubRPC = typeof LightningPubRPC\nexport const ClientAuth = 22242\nexport type ClientAuth = typeof ClientAuth\nexport const NWCWalletRequest = 23194\nexport type NWCWalletRequest = typeof NWCWalletRequest\nexport const NWCWalletResponse = 23195\nexport type NWCWalletResponse = typeof NWCWalletResponse\nexport const NostrConnect = 24133\nexport type NostrConnect = typeof NostrConnect\nexport const HTTPAuth = 27235\nexport type HTTPAuth = typeof HTTPAuth\nexport const Followsets = 30000\nexport type Followsets = typeof Followsets\nexport const Genericlists = 30001\nexport type Genericlists = typeof Genericlists\nexport const Relaysets = 30002\nexport type Relaysets = typeof Relaysets\nexport const Bookmarksets = 30003\nexport type Bookmarksets = typeof Bookmarksets\nexport const Curationsets = 30004\nexport type Curationsets = typeof Curationsets\nexport const ProfileBadges = 30008\nexport type ProfileBadges = typeof ProfileBadges\nexport const BadgeDefinition = 30009\nexport type BadgeDefinition = typeof BadgeDefinition\nexport const Interestsets = 30015\nexport type Interestsets = typeof Interestsets\nexport const CreateOrUpdateStall = 30017\nexport type CreateOrUpdateStall = typeof CreateOrUpdateStall\nexport const CreateOrUpdateProduct = 30018\nexport type CreateOrUpdateProduct = typeof CreateOrUpdateProduct\nexport const LongFormArticle = 30023\nexport type LongFormArticle = typeof LongFormArticle\nexport const DraftLong = 30024\nexport type DraftLong = typeof DraftLong\nexport const Emojisets = 30030\nexport type Emojisets = typeof Emojisets\nexport const Application = 30078\nexport type Application = typeof Application\nexport const LiveEvent = 30311\nexport type LiveEvent = typeof LiveEvent\nexport const UserStatuses = 30315\nexport type UserStatuses = typeof UserStatuses\nexport const ClassifiedListing = 30402\nexport type ClassifiedListing = typeof ClassifiedListing\nexport const DraftClassifiedListing = 30403\nexport type DraftClassifiedListing = typeof DraftClassifiedListing\nexport const Date = 31922\nexport type Date = typeof Date\nexport const Time = 31923\nexport type Time = typeof Time\nexport const Calendar = 31924\nexport type Calendar = typeof Calendar\nexport const CalendarEventRSVP = 31925\nexport type CalendarEventRSVP = typeof CalendarEventRSVP\nexport const RelayReview = 31987\nexport type RelayReview = typeof RelayReview\nexport const Handlerrecommendation = 31989\nexport type Handlerrecommendation = typeof Handlerrecommendation\nexport const Handlerinformation = 31990\nexport type Handlerinformation = typeof Handlerinformation\nexport const CommunityDefinition = 34550\nexport type CommunityDefinition = typeof CommunityDefinition\nexport const GroupMetadata = 39000\nexport type GroupMetadata = typeof GroupMetadata\n"],
-  "mappings": ";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,uBAAwB;AACxB,IAAAA,gBAA2B;;;ACOpB,IAAM,iBAAiB,OAAO,UAAU;AAsB/C,IAAM,WAAW,CAAC,QAAiD,eAAe;AAE3E,SAAS,cAAiB,OAAsC;AACrE,MAAI,CAAC,SAAS,KAAK;AAAG,WAAO;AAC7B,MAAI,OAAO,MAAM,SAAS;AAAU,WAAO;AAC3C,MAAI,OAAO,MAAM,YAAY;AAAU,WAAO;AAC9C,MAAI,OAAO,MAAM,eAAe;AAAU,WAAO;AACjD,MAAI,OAAO,MAAM,WAAW;AAAU,WAAO;AAC7C,MAAI,CAAC,MAAM,OAAO,MAAM,gBAAgB;AAAG,WAAO;AAElD,MAAI,CAAC,MAAM,QAAQ,MAAM,IAAI;AAAG,WAAO;AACvC,WAASC,KAAI,GAAGA,KAAI,MAAM,KAAK,QAAQA,MAAK;AAC1C,QAAI,MAAM,MAAM,KAAKA;AACrB,QAAI,CAAC,MAAM,QAAQ,GAAG;AAAG,aAAO;AAChC,aAAS,IAAI,GAAG,IAAI,IAAI,QAAQ,KAAK;AACnC,UAAI,OAAO,IAAI,OAAO;AAAU,eAAO;AAAA,IACzC;AAAA,EACF;AAEA,SAAO;AACT;;;AD/CA,oBAAuB;;;AEEvB,mBAAuC;AAHhC,IAAM,cAA2B,IAAI,YAAY,OAAO;AACxD,IAAM,cAA2B,IAAI,YAAY;;;AFIxD,IAAM,KAAN,MAA0B;AAAA,EACxB,oBAAgC;AAC9B,WAAO,yBAAQ,MAAM,iBAAiB;AAAA,EACxC;AAAA,EACA,aAAa,WAA+B;AAC1C,eAAO,0BAAW,yBAAQ,aAAa,SAAS,CAAC;AAAA,EACnD;AAAA,EACA,cAAc,GAAkB,WAAsC;AACpE,UAAM,QAAQ;AACd,UAAM,aAAS,0BAAW,yBAAQ,aAAa,SAAS,CAAC;AACzD,UAAM,KAAK,aAAa,KAAK;AAC7B,UAAM,UAAM,0BAAW,yBAAQ,KAAK,aAAa,KAAK,GAAG,SAAS,CAAC;AACnE,UAAM,kBAAkB;AACxB,WAAO;AAAA,EACT;AAAA,EACA,YAAY,OAAsC;AAChD,QAAI,OAAO,MAAM,oBAAoB;AAAW,aAAO,MAAM;AAE7D,UAAM,OAAO,aAAa,KAAK;AAC/B,QAAI,SAAS,MAAM,IAAI;AACrB,YAAM,kBAAkB;AACxB,aAAO;AAAA,IACT;AAEA,QAAI;AACF,YAAM,QAAQ,yBAAQ,OAAO,MAAM,KAAK,MAAM,MAAM,MAAM;AAC1D,YAAM,kBAAkB;AACxB,aAAO;AAAA,IACT,SAAS,KAAP;AACA,YAAM,kBAAkB;AACxB,aAAO;AAAA,IACT;AAAA,EACF;AACF;AAEO,SAAS,eAAe,KAA4B;AACzD,MAAI,CAAC,cAAc,GAAG;AAAG,UAAM,IAAI,MAAM,wDAAwD;AACjG,SAAO,KAAK,UAAU,CAAC,GAAG,IAAI,QAAQ,IAAI,YAAY,IAAI,MAAM,IAAI,MAAM,IAAI,OAAO,CAAC;AACxF;AAEO,SAAS,aAAa,OAA8B;AACzD,MAAI,gBAAY,sBAAO,YAAY,OAAO,eAAe,KAAK,CAAC,CAAC;AAChE,aAAO,0BAAW,SAAS;AAC7B;AAEA,IAAM,IAAQ,IAAI,GAAG;AAEd,IAAM,oBAAoB,EAAE;AAC5B,IAAM,eAAe,EAAE;AACvB,IAAM,gBAAgB,EAAE;AACxB,IAAM,cAAc,EAAE;;;AGhBtB,IAAM,gBAAgB;AAUtB,IAAM,SAAS;AAgBf,IAAM,gBAAgB;;;AJ9CtB,SAAS,kBACd,GACA,UACA,UACA,YACO;AACP,MAAI;AACJ,QAAM,OAAO,CAAC,GAAI,EAAE,QAAQ,CAAC,GAAI,CAAC,KAAK,SAAS,IAAI,QAAQ,GAAG,CAAC,KAAK,SAAS,MAAM,CAAC;AACrF,MAAI,SAAS,SAAS,eAAe;AACnC,WAAO;AAAA,EACT,OAAO;AACL,WAAO;AACP,SAAK,KAAK,CAAC,KAAK,OAAO,SAAS,IAAI,CAAC,CAAC;AAAA,EACxC;AAEA,SAAO;AAAA,IACL;AAAA,MACE;AAAA,MACA;AAAA,MACA,SAAS,EAAE,YAAY,MAAM,SAAS,MAAM,KAAK,SAAO,IAAI,OAAO,GAAG,IAAI,KAAK,KAAK,UAAU,QAAQ;AAAA,MACtG,YAAY,EAAE;AAAA,IAChB;AAAA,IACA;AAAA,EACF;AACF;AAEO,SAAS,wBAAwB,OAAwC;AAC9E,MAAI,CAAC,CAAC,QAAQ,aAAa,EAAE,SAAS,MAAM,IAAI,GAAG;AACjD,WAAO;AAAA,EACT;AAEA,MAAI;AACJ,MAAI;AAEJ,WAASC,KAAI,MAAM,KAAK,SAAS,GAAGA,MAAK,MAAM,aAAa,UAAa,aAAa,SAAYA,MAAK;AACrG,UAAM,MAAM,MAAM,KAAKA;AACvB,QAAI,IAAI,UAAU,GAAG;AACnB,UAAI,IAAI,OAAO,OAAO,aAAa,QAAW;AAC5C,mBAAW;AAAA,MACb,WAAW,IAAI,OAAO,OAAO,aAAa,QAAW;AACnD,mBAAW;AAAA,MACb;AAAA,IACF;AAAA,EACF;AAEA,MAAI,aAAa,QAAW;AAC1B,WAAO;AAAA,EACT;AAEA,SAAO;AAAA,IACL,IAAI,SAAS;AAAA,IACb,QAAQ,CAAC,SAAS,IAAI,WAAW,EAAE,EAAE,OAAO,CAAC,MAAmB,OAAO,MAAM,QAAQ;AAAA,IACrF,QAAQ,WAAW;AAAA,EACrB;AACF;AAMO,SAAS,iBAAiB,OAAc,EAAE,iBAAiB,IAA6B,CAAC,GAAsB;AACpH,QAAM,UAAU,wBAAwB,KAAK;AAE7C,MAAI,YAAY,UAAa,MAAM,YAAY,IAAI;AACjD,WAAO;AAAA,EACT;AAEA,MAAI;AAEJ,MAAI;AACF,oBAAgB,KAAK,MAAM,MAAM,OAAO;AAAA,EAC1C,SAAS,OAAP;AACA,WAAO;AAAA,EACT;AAEA,MAAI,cAAc,OAAO,QAAQ,IAAI;AACnC,WAAO;AAAA,EACT;AAEA,MAAI,CAAC,oBAAoB,CAAC,YAAY,aAAa,GAAG;AACpD,WAAO;AAAA,EACT;AAEA,SAAO;AACT;",
+  "sourcesContent": ["import { GenericRepost, Repost, ShortTextNote } from './kinds.ts'\nimport { EventPointer } from './nip19.ts'\nimport { Event, finalizeEvent, verifyEvent } from './pure.ts'\n\nexport type RepostEventTemplate = {\n  /**\n   * Pass only non-nip18 tags if you have to.\n   * Nip18 tags ('e' and 'p' tags pointing to the reposted event) will be added automatically.\n   */\n  tags?: string[][]\n\n  /**\n   * Pass an empty string to NOT include the stringified JSON of the reposted event.\n   * Any other content will be ignored and replaced with the stringified JSON of the reposted event.\n   * @default Stringified JSON of the reposted event\n   */\n  content?: ''\n\n  created_at: number\n}\n\nexport function finishRepostEvent(\n  t: RepostEventTemplate,\n  reposted: Event,\n  relayUrl: string,\n  privateKey: Uint8Array,\n): Event {\n  let kind: Repost | GenericRepost\n  const tags = [...(t.tags ?? []), ['e', reposted.id, relayUrl], ['p', reposted.pubkey]]\n  if (reposted.kind === ShortTextNote) {\n    kind = Repost\n  } else {\n    kind = GenericRepost\n    tags.push(['k', String(reposted.kind)])\n  }\n\n  return finalizeEvent(\n    {\n      kind,\n      tags,\n      content: t.content === '' || reposted.tags?.find(tag => tag[0] === '-') ? '' : JSON.stringify(reposted),\n      created_at: t.created_at,\n    },\n    privateKey,\n  )\n}\n\nexport function getRepostedEventPointer(event: Event): undefined | EventPointer {\n  if (![Repost, GenericRepost].includes(event.kind)) {\n    return undefined\n  }\n\n  let lastETag: undefined | string[]\n  let lastPTag: undefined | string[]\n\n  for (let i = event.tags.length - 1; i >= 0 && (lastETag === undefined || lastPTag === undefined); i--) {\n    const tag = event.tags[i]\n    if (tag.length >= 2) {\n      if (tag[0] === 'e' && lastETag === undefined) {\n        lastETag = tag\n      } else if (tag[0] === 'p' && lastPTag === undefined) {\n        lastPTag = tag\n      }\n    }\n  }\n\n  if (lastETag === undefined) {\n    return undefined\n  }\n\n  return {\n    id: lastETag[1],\n    relays: [lastETag[2], lastPTag?.[2]].filter((x): x is string => typeof x === 'string'),\n    author: lastPTag?.[1],\n  }\n}\n\nexport type GetRepostedEventOptions = {\n  skipVerification?: boolean\n}\n\nexport function getRepostedEvent(event: Event, { skipVerification }: GetRepostedEventOptions = {}): undefined | Event {\n  const pointer = getRepostedEventPointer(event)\n\n  if (pointer === undefined || event.content === '') {\n    return undefined\n  }\n\n  let repostedEvent: undefined | Event\n\n  try {\n    repostedEvent = JSON.parse(event.content) as Event\n  } catch (error) {\n    return undefined\n  }\n\n  if (repostedEvent.id !== pointer.id) {\n    return undefined\n  }\n\n  if (!skipVerification && !verifyEvent(repostedEvent)) {\n    return undefined\n  }\n\n  return repostedEvent\n}\n", "import { schnorr } from '@noble/curves/secp256k1.js'\nimport { bytesToHex, hexToBytes } from '@noble/hashes/utils.js'\nimport { Nostr, Event, EventTemplate, UnsignedEvent, VerifiedEvent, verifiedSymbol, validateEvent } from './core.ts'\nimport { sha256 } from '@noble/hashes/sha2.js'\n\nimport { utf8Encoder } from './utils.ts'\n\nclass JS implements Nostr {\n  generateSecretKey(): Uint8Array {\n    return schnorr.utils.randomSecretKey()\n  }\n  getPublicKey(secretKey: Uint8Array): string {\n    return bytesToHex(schnorr.getPublicKey(secretKey))\n  }\n  finalizeEvent(t: EventTemplate, secretKey: Uint8Array): VerifiedEvent {\n    const event = t as VerifiedEvent\n    event.pubkey = bytesToHex(schnorr.getPublicKey(secretKey))\n    event.id = getEventHash(event)\n    event.sig = bytesToHex(schnorr.sign(hexToBytes(getEventHash(event)), secretKey))\n    event[verifiedSymbol] = true\n    return event\n  }\n  verifyEvent(event: Event): event is VerifiedEvent {\n    if (typeof event[verifiedSymbol] === 'boolean') return event[verifiedSymbol]\n\n    const hash = getEventHash(event)\n    if (hash !== event.id) {\n      event[verifiedSymbol] = false\n      return false\n    }\n\n    try {\n      const valid = schnorr.verify(hexToBytes(event.sig), hexToBytes(hash), hexToBytes(event.pubkey))\n      event[verifiedSymbol] = valid\n      return valid\n    } catch (err) {\n      event[verifiedSymbol] = false\n      return false\n    }\n  }\n}\n\nexport function serializeEvent(evt: UnsignedEvent): string {\n  if (!validateEvent(evt)) throw new Error(\"can't serialize event with wrong or missing properties\")\n  return JSON.stringify([0, evt.pubkey, evt.created_at, evt.kind, evt.tags, evt.content])\n}\n\nexport function getEventHash(event: UnsignedEvent): string {\n  let eventHash = sha256(utf8Encoder.encode(serializeEvent(event)))\n  return bytesToHex(eventHash)\n}\n\nconst i: JS = new JS()\n\nexport const generateSecretKey = i.generateSecretKey\nexport const getPublicKey = i.getPublicKey\nexport const finalizeEvent = i.finalizeEvent\nexport const verifyEvent = i.verifyEvent\nexport * from './core.ts'\n", "export interface Nostr {\n  generateSecretKey(): Uint8Array\n  getPublicKey(secretKey: Uint8Array): string\n  finalizeEvent(event: EventTemplate, secretKey: Uint8Array): VerifiedEvent\n  verifyEvent(event: Event): event is VerifiedEvent\n}\n\n/** Designates a verified event signature. */\nexport const verifiedSymbol = Symbol('verified')\n\nexport type NostrEvent = {\n  kind: number\n  tags: string[][]\n  content: string\n  created_at: number\n  pubkey: string\n  id: string\n  sig: string\n  [verifiedSymbol]?: boolean\n}\n\nexport type Event = NostrEvent\nexport type EventTemplate = Pick<Event, 'kind' | 'tags' | 'content' | 'created_at'>\nexport type UnsignedEvent = Pick<Event, 'kind' | 'tags' | 'content' | 'created_at' | 'pubkey'>\n\n/** An event whose signature has been verified. */\nexport interface VerifiedEvent extends Event {\n  [verifiedSymbol]: true\n}\n\nconst isRecord = (obj: unknown): obj is Record<string, unknown> => obj instanceof Object\n\nexport function validateEvent<T>(event: T): event is T & UnsignedEvent {\n  if (!isRecord(event)) return false\n  if (typeof event.kind !== 'number') return false\n  if (typeof event.content !== 'string') return false\n  if (typeof event.created_at !== 'number') return false\n  if (typeof event.pubkey !== 'string') return false\n  if (!event.pubkey.match(/^[a-f0-9]{64}$/)) return false\n\n  if (!Array.isArray(event.tags)) return false\n  for (let i = 0; i < event.tags.length; i++) {\n    let tag = event.tags[i]\n    if (!Array.isArray(tag)) return false\n    for (let j = 0; j < tag.length; j++) {\n      if (typeof tag[j] !== 'string') return false\n    }\n  }\n\n  return true\n}\n\n/**\n * Sort events in reverse-chronological order by the `created_at` timestamp,\n * and then by the event `id` (lexicographically) in case of ties.\n * This mutates the array.\n */\nexport function sortEvents(events: Event[]): Event[] {\n  return events.sort((a: NostrEvent, b: NostrEvent): number => {\n    if (a.created_at !== b.created_at) {\n      return b.created_at - a.created_at\n    }\n    return a.id.localeCompare(b.id)\n  })\n}\n", "import type { Event } from './core.ts'\n\nexport const utf8Decoder: TextDecoder = new TextDecoder('utf-8')\nexport const utf8Encoder: TextEncoder = new TextEncoder()\n\nexport { bytesToHex, hexToBytes } from '@noble/hashes/utils.js'\n\nexport function normalizeURL(url: string): string {\n  try {\n    if (url.indexOf('://') === -1) url = 'wss://' + url\n    let p = new URL(url)\n    if (p.protocol === 'http:') p.protocol = 'ws:'\n    else if (p.protocol === 'https:') p.protocol = 'wss:'\n    p.pathname = p.pathname.replace(/\\/+/g, '/')\n    if (p.pathname.endsWith('/')) p.pathname = p.pathname.slice(0, -1)\n    if ((p.port === '80' && p.protocol === 'ws:') || (p.port === '443' && p.protocol === 'wss:')) p.port = ''\n    p.searchParams.sort()\n    p.hash = ''\n    return p.toString()\n  } catch (e) {\n    throw new Error(`Invalid URL: ${url}`)\n  }\n}\n\nexport function insertEventIntoDescendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return b.created_at - event.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function insertEventIntoAscendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return event.created_at - b.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function binarySearch<T>(arr: T[], compare: (b: T) => number): [number, boolean] {\n  let start = 0\n  let end = arr.length - 1\n\n  while (start <= end) {\n    const mid = Math.floor((start + end) / 2)\n    const cmp = compare(arr[mid])\n\n    if (cmp === 0) {\n      return [mid, true]\n    }\n\n    if (cmp < 0) {\n      end = mid - 1\n    } else {\n      start = mid + 1\n    }\n  }\n\n  return [start, false]\n}\n\nexport class QueueNode<V> {\n  public value: V\n  public next: QueueNode<V> | null = null\n  public prev: QueueNode<V> | null = null\n\n  constructor(message: V) {\n    this.value = message\n  }\n}\n\nexport class Queue<V> {\n  public first: QueueNode<V> | null\n  public last: QueueNode<V> | null\n\n  constructor() {\n    this.first = null\n    this.last = null\n  }\n\n  enqueue(value: V): boolean {\n    const newNode = new QueueNode(value)\n    if (!this.last) {\n      // list is empty\n      this.first = newNode\n      this.last = newNode\n    } else if (this.last === this.first) {\n      // list has a single element\n      this.last = newNode\n      this.last.prev = this.first\n      this.first.next = newNode\n    } else {\n      // list has elements, add as last\n      newNode.prev = this.last\n      this.last.next = newNode\n      this.last = newNode\n    }\n    return true\n  }\n\n  dequeue(): V | null {\n    if (!this.first) return null\n\n    if (this.first === this.last) {\n      const target = this.first\n      this.first = null\n      this.last = null\n      return target.value\n    }\n\n    const target = this.first\n    this.first = target.next\n    if (this.first) {\n      this.first.prev = null // fix: clean up prev pointer\n    }\n\n    return target.value\n  }\n}\n", "import { NostrEvent, validateEvent } from './pure.ts'\n\n/** Events are **regular**, which means they're all expected to be stored by relays. */\nexport function isRegularKind(kind: number): boolean {\n  return kind < 10000 && kind !== 0 && kind !== 3\n}\n\n/** Events are **replaceable**, which means that, for each combination of `pubkey` and `kind`, only the latest event is expected to (SHOULD) be stored by relays, older versions are expected to be discarded. */\nexport function isReplaceableKind(kind: number): boolean {\n  return kind === 0 || kind === 3 || (10000 <= kind && kind < 20000)\n}\n\n/** Events are **ephemeral**, which means they are not expected to be stored by relays. */\nexport function isEphemeralKind(kind: number): boolean {\n  return 20000 <= kind && kind < 30000\n}\n\n/** Events are **addressable**, which means that, for each combination of `pubkey`, `kind` and the `d` tag, only the latest event is expected to be stored by relays, older versions are expected to be discarded. */\nexport function isAddressableKind(kind: number): boolean {\n  return 30000 <= kind && kind < 40000\n}\n\n/** Classification of the event kind. */\nexport type KindClassification = 'regular' | 'replaceable' | 'ephemeral' | 'parameterized' | 'unknown'\n\n/** Determine the classification of this kind of event if known, or `unknown`. */\nexport function classifyKind(kind: number): KindClassification {\n  if (isRegularKind(kind)) return 'regular'\n  if (isReplaceableKind(kind)) return 'replaceable'\n  if (isEphemeralKind(kind)) return 'ephemeral'\n  if (isAddressableKind(kind)) return 'parameterized'\n  return 'unknown'\n}\n\nexport function isKind<T extends number>(event: unknown, kind: T | Array<T>): event is NostrEvent & { kind: T } {\n  const kindAsArray: number[] = kind instanceof Array ? kind : [kind]\n  return (validateEvent(event) && kindAsArray.includes(event.kind)) || false\n}\n\nexport const Metadata = 0\nexport type Metadata = typeof Metadata\nexport const ShortTextNote = 1\nexport type ShortTextNote = typeof ShortTextNote\nexport const RecommendRelay = 2\nexport type RecommendRelay = typeof RecommendRelay\nexport const Contacts = 3\nexport type Contacts = typeof Contacts\nexport const EncryptedDirectMessage = 4\nexport type EncryptedDirectMessage = typeof EncryptedDirectMessage\nexport const EventDeletion = 5\nexport type EventDeletion = typeof EventDeletion\nexport const Repost = 6\nexport type Repost = typeof Repost\nexport const Reaction = 7\nexport type Reaction = typeof Reaction\nexport const BadgeAward = 8\nexport type BadgeAward = typeof BadgeAward\nexport const ChatMessage = 9\nexport type ChatMessage = typeof ChatMessage\nexport const ForumThread = 11\nexport type ForumThread = typeof ForumThread\nexport const Seal = 13\nexport type Seal = typeof Seal\nexport const PrivateDirectMessage = 14\nexport type PrivateDirectMessage = typeof PrivateDirectMessage\nexport const FileMessage = 15\nexport type FileMessage = typeof FileMessage\nexport const GenericRepost = 16\nexport type GenericRepost = typeof GenericRepost\nexport const Photo = 20\nexport type Photo = typeof Photo\nexport const NormalVideo = 21\nexport type NormalVideo = typeof NormalVideo\nexport const ShortVideo = 22\nexport type ShortVideo = typeof ShortVideo\nexport const ChannelCreation = 40\nexport type ChannelCreation = typeof ChannelCreation\nexport const ChannelMetadata = 41\nexport type ChannelMetadata = typeof ChannelMetadata\nexport const ChannelMessage = 42\nexport type ChannelMessage = typeof ChannelMessage\nexport const ChannelHideMessage = 43\nexport type ChannelHideMessage = typeof ChannelHideMessage\nexport const ChannelMuteUser = 44\nexport type ChannelMuteUser = typeof ChannelMuteUser\nexport const OpenTimestamps = 1040\nexport type OpenTimestamps = typeof OpenTimestamps\nexport const GiftWrap = 1059\nexport type GiftWrap = typeof GiftWrap\nexport const Poll = 1068\nexport type Poll = typeof Poll\nexport const FileMetadata = 1063\nexport type FileMetadata = typeof FileMetadata\nexport const Comment = 1111\nexport type Comment = typeof Comment\nexport const LiveChatMessage = 1311\nexport type LiveChatMessage = typeof LiveChatMessage\nexport const Voice = 1222\nexport type Voice = typeof Voice\nexport const VoiceComment = 1244\nexport type VoiceComment = typeof VoiceComment\nexport const ProblemTracker = 1971\nexport type ProblemTracker = typeof ProblemTracker\nexport const Report = 1984\nexport type Report = typeof Report\nexport const Reporting = 1984\nexport type Reporting = typeof Reporting\nexport const Label = 1985\nexport type Label = typeof Label\nexport const CommunityPostApproval = 4550\nexport type CommunityPostApproval = typeof CommunityPostApproval\nexport const JobRequest = 5999\nexport type JobRequest = typeof JobRequest\nexport const JobResult = 6999\nexport type JobResult = typeof JobResult\nexport const JobFeedback = 7000\nexport type JobFeedback = typeof JobFeedback\nexport const ZapGoal = 9041\nexport type ZapGoal = typeof ZapGoal\nexport const ZapRequest = 9734\nexport type ZapRequest = typeof ZapRequest\nexport const Zap = 9735\nexport type Zap = typeof Zap\nexport const Highlights = 9802\nexport type Highlights = typeof Highlights\nexport const PollResponse = 1018\nexport type PollResponse = typeof PollResponse\nexport const Mutelist = 10000\nexport type Mutelist = typeof Mutelist\nexport const Pinlist = 10001\nexport type Pinlist = typeof Pinlist\nexport const RelayList = 10002\nexport type RelayList = typeof RelayList\nexport const BookmarkList = 10003\nexport type BookmarkList = typeof BookmarkList\nexport const CommunitiesList = 10004\nexport type CommunitiesList = typeof CommunitiesList\nexport const PublicChatsList = 10005\nexport type PublicChatsList = typeof PublicChatsList\nexport const BlockedRelaysList = 10006\nexport type BlockedRelaysList = typeof BlockedRelaysList\nexport const SearchRelaysList = 10007\nexport type SearchRelaysList = typeof SearchRelaysList\nexport const FavoriteRelays = 10012\nexport type FavoriteRelays = typeof FavoriteRelays\nexport const InterestsList = 10015\nexport type InterestsList = typeof InterestsList\nexport const UserEmojiList = 10030\nexport type UserEmojiList = typeof UserEmojiList\nexport const DirectMessageRelaysList = 10050\nexport type DirectMessageRelaysList = typeof DirectMessageRelaysList\nexport const FileServerPreference = 10096\nexport type FileServerPreference = typeof FileServerPreference\nexport const BlossomServerList = 10063\nexport type BlossomServerList = typeof BlossomServerList\nexport const NWCWalletInfo = 13194\nexport type NWCWalletInfo = typeof NWCWalletInfo\nexport const LightningPubRPC = 21000\nexport type LightningPubRPC = typeof LightningPubRPC\nexport const ClientAuth = 22242\nexport type ClientAuth = typeof ClientAuth\nexport const NWCWalletRequest = 23194\nexport type NWCWalletRequest = typeof NWCWalletRequest\nexport const NWCWalletResponse = 23195\nexport type NWCWalletResponse = typeof NWCWalletResponse\nexport const NostrConnect = 24133\nexport type NostrConnect = typeof NostrConnect\nexport const HTTPAuth = 27235\nexport type HTTPAuth = typeof HTTPAuth\nexport const Followsets = 30000\nexport type Followsets = typeof Followsets\nexport const Genericlists = 30001\nexport type Genericlists = typeof Genericlists\nexport const Relaysets = 30002\nexport type Relaysets = typeof Relaysets\nexport const Bookmarksets = 30003\nexport type Bookmarksets = typeof Bookmarksets\nexport const Curationsets = 30004\nexport type Curationsets = typeof Curationsets\nexport const ProfileBadges = 30008\nexport type ProfileBadges = typeof ProfileBadges\nexport const BadgeDefinition = 30009\nexport type BadgeDefinition = typeof BadgeDefinition\nexport const Interestsets = 30015\nexport type Interestsets = typeof Interestsets\nexport const CreateOrUpdateStall = 30017\nexport type CreateOrUpdateStall = typeof CreateOrUpdateStall\nexport const CreateOrUpdateProduct = 30018\nexport type CreateOrUpdateProduct = typeof CreateOrUpdateProduct\nexport const LongFormArticle = 30023\nexport type LongFormArticle = typeof LongFormArticle\nexport const DraftLong = 30024\nexport type DraftLong = typeof DraftLong\nexport const Emojisets = 30030\nexport type Emojisets = typeof Emojisets\nexport const Application = 30078\nexport type Application = typeof Application\nexport const LiveEvent = 30311\nexport type LiveEvent = typeof LiveEvent\nexport const UserStatuses = 30315\nexport type UserStatuses = typeof UserStatuses\nexport const ClassifiedListing = 30402\nexport type ClassifiedListing = typeof ClassifiedListing\nexport const DraftClassifiedListing = 30403\nexport type DraftClassifiedListing = typeof DraftClassifiedListing\nexport const Date = 31922\nexport type Date = typeof Date\nexport const Time = 31923\nexport type Time = typeof Time\nexport const Calendar = 31924\nexport type Calendar = typeof Calendar\nexport const CalendarEventRSVP = 31925\nexport type CalendarEventRSVP = typeof CalendarEventRSVP\nexport const RelayReview = 31987\nexport type RelayReview = typeof RelayReview\nexport const Handlerrecommendation = 31989\nexport type Handlerrecommendation = typeof Handlerrecommendation\nexport const Handlerinformation = 31990\nexport type Handlerinformation = typeof Handlerinformation\nexport const CommunityDefinition = 34550\nexport type CommunityDefinition = typeof CommunityDefinition\nexport const GroupMetadata = 39000\nexport type GroupMetadata = typeof GroupMetadata\n"],
+  "mappings": "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",
   "names": ["import_utils", "i", "i"]
 }
Index: package/lib/esm/nip18.js.map
===================================================================
--- package/lib/esm/nip18.js.map
+++ package/lib/esm/nip18.js.map
@@ -1,7 +1,7 @@
 {
   "version": 3,
   "sources": ["../../pure.ts", "../../core.ts", "../../utils.ts", "../../kinds.ts", "../../nip18.ts"],
-  "sourcesContent": ["import { schnorr } from '@noble/curves/secp256k1'\nimport { bytesToHex } from '@noble/hashes/utils'\nimport { Nostr, Event, EventTemplate, UnsignedEvent, VerifiedEvent, verifiedSymbol, validateEvent } from './core.ts'\nimport { sha256 } from '@noble/hashes/sha256'\n\nimport { utf8Encoder } from './utils.ts'\n\nclass JS implements Nostr {\n  generateSecretKey(): Uint8Array {\n    return schnorr.utils.randomPrivateKey()\n  }\n  getPublicKey(secretKey: Uint8Array): string {\n    return bytesToHex(schnorr.getPublicKey(secretKey))\n  }\n  finalizeEvent(t: EventTemplate, secretKey: Uint8Array): VerifiedEvent {\n    const event = t as VerifiedEvent\n    event.pubkey = bytesToHex(schnorr.getPublicKey(secretKey))\n    event.id = getEventHash(event)\n    event.sig = bytesToHex(schnorr.sign(getEventHash(event), secretKey))\n    event[verifiedSymbol] = true\n    return event\n  }\n  verifyEvent(event: Event): event is VerifiedEvent {\n    if (typeof event[verifiedSymbol] === 'boolean') return event[verifiedSymbol]\n\n    const hash = getEventHash(event)\n    if (hash !== event.id) {\n      event[verifiedSymbol] = false\n      return false\n    }\n\n    try {\n      const valid = schnorr.verify(event.sig, hash, event.pubkey)\n      event[verifiedSymbol] = valid\n      return valid\n    } catch (err) {\n      event[verifiedSymbol] = false\n      return false\n    }\n  }\n}\n\nexport function serializeEvent(evt: UnsignedEvent): string {\n  if (!validateEvent(evt)) throw new Error(\"can't serialize event with wrong or missing properties\")\n  return JSON.stringify([0, evt.pubkey, evt.created_at, evt.kind, evt.tags, evt.content])\n}\n\nexport function getEventHash(event: UnsignedEvent): string {\n  let eventHash = sha256(utf8Encoder.encode(serializeEvent(event)))\n  return bytesToHex(eventHash)\n}\n\nconst i: JS = new JS()\n\nexport const generateSecretKey = i.generateSecretKey\nexport const getPublicKey = i.getPublicKey\nexport const finalizeEvent = i.finalizeEvent\nexport const verifyEvent = i.verifyEvent\nexport * from './core.ts'\n", "export interface Nostr {\n  generateSecretKey(): Uint8Array\n  getPublicKey(secretKey: Uint8Array): string\n  finalizeEvent(event: EventTemplate, secretKey: Uint8Array): VerifiedEvent\n  verifyEvent(event: Event): event is VerifiedEvent\n}\n\n/** Designates a verified event signature. */\nexport const verifiedSymbol = Symbol('verified')\n\nexport type NostrEvent = {\n  kind: number\n  tags: string[][]\n  content: string\n  created_at: number\n  pubkey: string\n  id: string\n  sig: string\n  [verifiedSymbol]?: boolean\n}\n\nexport type Event = NostrEvent\nexport type EventTemplate = Pick<Event, 'kind' | 'tags' | 'content' | 'created_at'>\nexport type UnsignedEvent = Pick<Event, 'kind' | 'tags' | 'content' | 'created_at' | 'pubkey'>\n\n/** An event whose signature has been verified. */\nexport interface VerifiedEvent extends Event {\n  [verifiedSymbol]: true\n}\n\nconst isRecord = (obj: unknown): obj is Record<string, unknown> => obj instanceof Object\n\nexport function validateEvent<T>(event: T): event is T & UnsignedEvent {\n  if (!isRecord(event)) return false\n  if (typeof event.kind !== 'number') return false\n  if (typeof event.content !== 'string') return false\n  if (typeof event.created_at !== 'number') return false\n  if (typeof event.pubkey !== 'string') return false\n  if (!event.pubkey.match(/^[a-f0-9]{64}$/)) return false\n\n  if (!Array.isArray(event.tags)) return false\n  for (let i = 0; i < event.tags.length; i++) {\n    let tag = event.tags[i]\n    if (!Array.isArray(tag)) return false\n    for (let j = 0; j < tag.length; j++) {\n      if (typeof tag[j] !== 'string') return false\n    }\n  }\n\n  return true\n}\n\n/**\n * Sort events in reverse-chronological order by the `created_at` timestamp,\n * and then by the event `id` (lexicographically) in case of ties.\n * This mutates the array.\n */\nexport function sortEvents(events: Event[]): Event[] {\n  return events.sort((a: NostrEvent, b: NostrEvent): number => {\n    if (a.created_at !== b.created_at) {\n      return b.created_at - a.created_at\n    }\n    return a.id.localeCompare(b.id)\n  })\n}\n", "import type { Event } from './core.ts'\n\nexport const utf8Decoder: TextDecoder = new TextDecoder('utf-8')\nexport const utf8Encoder: TextEncoder = new TextEncoder()\n\nexport { bytesToHex, hexToBytes } from '@noble/hashes/utils'\n\nexport function normalizeURL(url: string): string {\n  try {\n    if (url.indexOf('://') === -1) url = 'wss://' + url\n    let p = new URL(url)\n    if (p.protocol === 'http:') p.protocol = 'ws:'\n    else if (p.protocol === 'https:') p.protocol = 'wss:'\n    p.pathname = p.pathname.replace(/\\/+/g, '/')\n    if (p.pathname.endsWith('/')) p.pathname = p.pathname.slice(0, -1)\n    if ((p.port === '80' && p.protocol === 'ws:') || (p.port === '443' && p.protocol === 'wss:')) p.port = ''\n    p.searchParams.sort()\n    p.hash = ''\n    return p.toString()\n  } catch (e) {\n    throw new Error(`Invalid URL: ${url}`)\n  }\n}\n\nexport function insertEventIntoDescendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return b.created_at - event.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function insertEventIntoAscendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return event.created_at - b.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function binarySearch<T>(arr: T[], compare: (b: T) => number): [number, boolean] {\n  let start = 0\n  let end = arr.length - 1\n\n  while (start <= end) {\n    const mid = Math.floor((start + end) / 2)\n    const cmp = compare(arr[mid])\n\n    if (cmp === 0) {\n      return [mid, true]\n    }\n\n    if (cmp < 0) {\n      end = mid - 1\n    } else {\n      start = mid + 1\n    }\n  }\n\n  return [start, false]\n}\n\nexport class QueueNode<V> {\n  public value: V\n  public next: QueueNode<V> | null = null\n  public prev: QueueNode<V> | null = null\n\n  constructor(message: V) {\n    this.value = message\n  }\n}\n\nexport class Queue<V> {\n  public first: QueueNode<V> | null\n  public last: QueueNode<V> | null\n\n  constructor() {\n    this.first = null\n    this.last = null\n  }\n\n  enqueue(value: V): boolean {\n    const newNode = new QueueNode(value)\n    if (!this.last) {\n      // list is empty\n      this.first = newNode\n      this.last = newNode\n    } else if (this.last === this.first) {\n      // list has a single element\n      this.last = newNode\n      this.last.prev = this.first\n      this.first.next = newNode\n    } else {\n      // list has elements, add as last\n      newNode.prev = this.last\n      this.last.next = newNode\n      this.last = newNode\n    }\n    return true\n  }\n\n  dequeue(): V | null {\n    if (!this.first) return null\n\n    if (this.first === this.last) {\n      const target = this.first\n      this.first = null\n      this.last = null\n      return target.value\n    }\n\n    const target = this.first\n    this.first = target.next\n    if (this.first) {\n      this.first.prev = null // fix: clean up prev pointer\n    }\n\n    return target.value\n  }\n}\n", "import { NostrEvent, validateEvent } from './pure.ts'\n\n/** Events are **regular**, which means they're all expected to be stored by relays. */\nexport function isRegularKind(kind: number): boolean {\n  return kind < 10000 && kind !== 0 && kind !== 3\n}\n\n/** Events are **replaceable**, which means that, for each combination of `pubkey` and `kind`, only the latest event is expected to (SHOULD) be stored by relays, older versions are expected to be discarded. */\nexport function isReplaceableKind(kind: number): boolean {\n  return kind === 0 || kind === 3 || (10000 <= kind && kind < 20000)\n}\n\n/** Events are **ephemeral**, which means they are not expected to be stored by relays. */\nexport function isEphemeralKind(kind: number): boolean {\n  return 20000 <= kind && kind < 30000\n}\n\n/** Events are **addressable**, which means that, for each combination of `pubkey`, `kind` and the `d` tag, only the latest event is expected to be stored by relays, older versions are expected to be discarded. */\nexport function isAddressableKind(kind: number): boolean {\n  return 30000 <= kind && kind < 40000\n}\n\n/** Classification of the event kind. */\nexport type KindClassification = 'regular' | 'replaceable' | 'ephemeral' | 'parameterized' | 'unknown'\n\n/** Determine the classification of this kind of event if known, or `unknown`. */\nexport function classifyKind(kind: number): KindClassification {\n  if (isRegularKind(kind)) return 'regular'\n  if (isReplaceableKind(kind)) return 'replaceable'\n  if (isEphemeralKind(kind)) return 'ephemeral'\n  if (isAddressableKind(kind)) return 'parameterized'\n  return 'unknown'\n}\n\nexport function isKind<T extends number>(event: unknown, kind: T | Array<T>): event is NostrEvent & { kind: T } {\n  const kindAsArray: number[] = kind instanceof Array ? kind : [kind]\n  return (validateEvent(event) && kindAsArray.includes(event.kind)) || false\n}\n\nexport const Metadata = 0\nexport type Metadata = typeof Metadata\nexport const ShortTextNote = 1\nexport type ShortTextNote = typeof ShortTextNote\nexport const RecommendRelay = 2\nexport type RecommendRelay = typeof RecommendRelay\nexport const Contacts = 3\nexport type Contacts = typeof Contacts\nexport const EncryptedDirectMessage = 4\nexport type EncryptedDirectMessage = typeof EncryptedDirectMessage\nexport const EventDeletion = 5\nexport type EventDeletion = typeof EventDeletion\nexport const Repost = 6\nexport type Repost = typeof Repost\nexport const Reaction = 7\nexport type Reaction = typeof Reaction\nexport const BadgeAward = 8\nexport type BadgeAward = typeof BadgeAward\nexport const ChatMessage = 9\nexport type ChatMessage = typeof ChatMessage\nexport const ForumThread = 11\nexport type ForumThread = typeof ForumThread\nexport const Seal = 13\nexport type Seal = typeof Seal\nexport const PrivateDirectMessage = 14\nexport type PrivateDirectMessage = typeof PrivateDirectMessage\nexport const FileMessage = 15\nexport type FileMessage = typeof FileMessage\nexport const GenericRepost = 16\nexport type GenericRepost = typeof GenericRepost\nexport const Photo = 20\nexport type Photo = typeof Photo\nexport const NormalVideo = 21\nexport type NormalVideo = typeof NormalVideo\nexport const ShortVideo = 22\nexport type ShortVideo = typeof ShortVideo\nexport const ChannelCreation = 40\nexport type ChannelCreation = typeof ChannelCreation\nexport const ChannelMetadata = 41\nexport type ChannelMetadata = typeof ChannelMetadata\nexport const ChannelMessage = 42\nexport type ChannelMessage = typeof ChannelMessage\nexport const ChannelHideMessage = 43\nexport type ChannelHideMessage = typeof ChannelHideMessage\nexport const ChannelMuteUser = 44\nexport type ChannelMuteUser = typeof ChannelMuteUser\nexport const OpenTimestamps = 1040\nexport type OpenTimestamps = typeof OpenTimestamps\nexport const GiftWrap = 1059\nexport type GiftWrap = typeof GiftWrap\nexport const Poll = 1068\nexport type Poll = typeof Poll\nexport const FileMetadata = 1063\nexport type FileMetadata = typeof FileMetadata\nexport const Comment = 1111\nexport type Comment = typeof Comment\nexport const LiveChatMessage = 1311\nexport type LiveChatMessage = typeof LiveChatMessage\nexport const Voice = 1222\nexport type Voice = typeof Voice\nexport const VoiceComment = 1244\nexport type VoiceComment = typeof VoiceComment\nexport const ProblemTracker = 1971\nexport type ProblemTracker = typeof ProblemTracker\nexport const Report = 1984\nexport type Report = typeof Report\nexport const Reporting = 1984\nexport type Reporting = typeof Reporting\nexport const Label = 1985\nexport type Label = typeof Label\nexport const CommunityPostApproval = 4550\nexport type CommunityPostApproval = typeof CommunityPostApproval\nexport const JobRequest = 5999\nexport type JobRequest = typeof JobRequest\nexport const JobResult = 6999\nexport type JobResult = typeof JobResult\nexport const JobFeedback = 7000\nexport type JobFeedback = typeof JobFeedback\nexport const ZapGoal = 9041\nexport type ZapGoal = typeof ZapGoal\nexport const ZapRequest = 9734\nexport type ZapRequest = typeof ZapRequest\nexport const Zap = 9735\nexport type Zap = typeof Zap\nexport const Highlights = 9802\nexport type Highlights = typeof Highlights\nexport const PollResponse = 1018\nexport type PollResponse = typeof PollResponse\nexport const Mutelist = 10000\nexport type Mutelist = typeof Mutelist\nexport const Pinlist = 10001\nexport type Pinlist = typeof Pinlist\nexport const RelayList = 10002\nexport type RelayList = typeof RelayList\nexport const BookmarkList = 10003\nexport type BookmarkList = typeof BookmarkList\nexport const CommunitiesList = 10004\nexport type CommunitiesList = typeof CommunitiesList\nexport const PublicChatsList = 10005\nexport type PublicChatsList = typeof PublicChatsList\nexport const BlockedRelaysList = 10006\nexport type BlockedRelaysList = typeof BlockedRelaysList\nexport const SearchRelaysList = 10007\nexport type SearchRelaysList = typeof SearchRelaysList\nexport const FavoriteRelays = 10012\nexport type FavoriteRelays = typeof FavoriteRelays\nexport const InterestsList = 10015\nexport type InterestsList = typeof InterestsList\nexport const UserEmojiList = 10030\nexport type UserEmojiList = typeof UserEmojiList\nexport const DirectMessageRelaysList = 10050\nexport type DirectMessageRelaysList = typeof DirectMessageRelaysList\nexport const FileServerPreference = 10096\nexport type FileServerPreference = typeof FileServerPreference\nexport const BlossomServerList = 10063\nexport type BlossomServerList = typeof BlossomServerList\nexport const NWCWalletInfo = 13194\nexport type NWCWalletInfo = typeof NWCWalletInfo\nexport const LightningPubRPC = 21000\nexport type LightningPubRPC = typeof LightningPubRPC\nexport const ClientAuth = 22242\nexport type ClientAuth = typeof ClientAuth\nexport const NWCWalletRequest = 23194\nexport type NWCWalletRequest = typeof NWCWalletRequest\nexport const NWCWalletResponse = 23195\nexport type NWCWalletResponse = typeof NWCWalletResponse\nexport const NostrConnect = 24133\nexport type NostrConnect = typeof NostrConnect\nexport const HTTPAuth = 27235\nexport type HTTPAuth = typeof HTTPAuth\nexport const Followsets = 30000\nexport type Followsets = typeof Followsets\nexport const Genericlists = 30001\nexport type Genericlists = typeof Genericlists\nexport const Relaysets = 30002\nexport type Relaysets = typeof Relaysets\nexport const Bookmarksets = 30003\nexport type Bookmarksets = typeof Bookmarksets\nexport const Curationsets = 30004\nexport type Curationsets = typeof Curationsets\nexport const ProfileBadges = 30008\nexport type ProfileBadges = typeof ProfileBadges\nexport const BadgeDefinition = 30009\nexport type BadgeDefinition = typeof BadgeDefinition\nexport const Interestsets = 30015\nexport type Interestsets = typeof Interestsets\nexport const CreateOrUpdateStall = 30017\nexport type CreateOrUpdateStall = typeof CreateOrUpdateStall\nexport const CreateOrUpdateProduct = 30018\nexport type CreateOrUpdateProduct = typeof CreateOrUpdateProduct\nexport const LongFormArticle = 30023\nexport type LongFormArticle = typeof LongFormArticle\nexport const DraftLong = 30024\nexport type DraftLong = typeof DraftLong\nexport const Emojisets = 30030\nexport type Emojisets = typeof Emojisets\nexport const Application = 30078\nexport type Application = typeof Application\nexport const LiveEvent = 30311\nexport type LiveEvent = typeof LiveEvent\nexport const UserStatuses = 30315\nexport type UserStatuses = typeof UserStatuses\nexport const ClassifiedListing = 30402\nexport type ClassifiedListing = typeof ClassifiedListing\nexport const DraftClassifiedListing = 30403\nexport type DraftClassifiedListing = typeof DraftClassifiedListing\nexport const Date = 31922\nexport type Date = typeof Date\nexport const Time = 31923\nexport type Time = typeof Time\nexport const Calendar = 31924\nexport type Calendar = typeof Calendar\nexport const CalendarEventRSVP = 31925\nexport type CalendarEventRSVP = typeof CalendarEventRSVP\nexport const RelayReview = 31987\nexport type RelayReview = typeof RelayReview\nexport const Handlerrecommendation = 31989\nexport type Handlerrecommendation = typeof Handlerrecommendation\nexport const Handlerinformation = 31990\nexport type Handlerinformation = typeof Handlerinformation\nexport const CommunityDefinition = 34550\nexport type CommunityDefinition = typeof CommunityDefinition\nexport const GroupMetadata = 39000\nexport type GroupMetadata = typeof GroupMetadata\n", "import { GenericRepost, Repost, ShortTextNote } from './kinds.ts'\nimport { EventPointer } from './nip19.ts'\nimport { Event, finalizeEvent, verifyEvent } from './pure.ts'\n\nexport type RepostEventTemplate = {\n  /**\n   * Pass only non-nip18 tags if you have to.\n   * Nip18 tags ('e' and 'p' tags pointing to the reposted event) will be added automatically.\n   */\n  tags?: string[][]\n\n  /**\n   * Pass an empty string to NOT include the stringified JSON of the reposted event.\n   * Any other content will be ignored and replaced with the stringified JSON of the reposted event.\n   * @default Stringified JSON of the reposted event\n   */\n  content?: ''\n\n  created_at: number\n}\n\nexport function finishRepostEvent(\n  t: RepostEventTemplate,\n  reposted: Event,\n  relayUrl: string,\n  privateKey: Uint8Array,\n): Event {\n  let kind: Repost | GenericRepost\n  const tags = [...(t.tags ?? []), ['e', reposted.id, relayUrl], ['p', reposted.pubkey]]\n  if (reposted.kind === ShortTextNote) {\n    kind = Repost\n  } else {\n    kind = GenericRepost\n    tags.push(['k', String(reposted.kind)])\n  }\n\n  return finalizeEvent(\n    {\n      kind,\n      tags,\n      content: t.content === '' || reposted.tags?.find(tag => tag[0] === '-') ? '' : JSON.stringify(reposted),\n      created_at: t.created_at,\n    },\n    privateKey,\n  )\n}\n\nexport function getRepostedEventPointer(event: Event): undefined | EventPointer {\n  if (![Repost, GenericRepost].includes(event.kind)) {\n    return undefined\n  }\n\n  let lastETag: undefined | string[]\n  let lastPTag: undefined | string[]\n\n  for (let i = event.tags.length - 1; i >= 0 && (lastETag === undefined || lastPTag === undefined); i--) {\n    const tag = event.tags[i]\n    if (tag.length >= 2) {\n      if (tag[0] === 'e' && lastETag === undefined) {\n        lastETag = tag\n      } else if (tag[0] === 'p' && lastPTag === undefined) {\n        lastPTag = tag\n      }\n    }\n  }\n\n  if (lastETag === undefined) {\n    return undefined\n  }\n\n  return {\n    id: lastETag[1],\n    relays: [lastETag[2], lastPTag?.[2]].filter((x): x is string => typeof x === 'string'),\n    author: lastPTag?.[1],\n  }\n}\n\nexport type GetRepostedEventOptions = {\n  skipVerification?: boolean\n}\n\nexport function getRepostedEvent(event: Event, { skipVerification }: GetRepostedEventOptions = {}): undefined | Event {\n  const pointer = getRepostedEventPointer(event)\n\n  if (pointer === undefined || event.content === '') {\n    return undefined\n  }\n\n  let repostedEvent: undefined | Event\n\n  try {\n    repostedEvent = JSON.parse(event.content) as Event\n  } catch (error) {\n    return undefined\n  }\n\n  if (repostedEvent.id !== pointer.id) {\n    return undefined\n  }\n\n  if (!skipVerification && !verifyEvent(repostedEvent)) {\n    return undefined\n  }\n\n  return repostedEvent\n}\n"],
-  "mappings": "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",
-  "names": ["bytesToHex", "i", "bytesToHex", "i"]
+  "sourcesContent": ["import { schnorr } from '@noble/curves/secp256k1.js'\nimport { bytesToHex, hexToBytes } from '@noble/hashes/utils.js'\nimport { Nostr, Event, EventTemplate, UnsignedEvent, VerifiedEvent, verifiedSymbol, validateEvent } from './core.ts'\nimport { sha256 } from '@noble/hashes/sha2.js'\n\nimport { utf8Encoder } from './utils.ts'\n\nclass JS implements Nostr {\n  generateSecretKey(): Uint8Array {\n    return schnorr.utils.randomSecretKey()\n  }\n  getPublicKey(secretKey: Uint8Array): string {\n    return bytesToHex(schnorr.getPublicKey(secretKey))\n  }\n  finalizeEvent(t: EventTemplate, secretKey: Uint8Array): VerifiedEvent {\n    const event = t as VerifiedEvent\n    event.pubkey = bytesToHex(schnorr.getPublicKey(secretKey))\n    event.id = getEventHash(event)\n    event.sig = bytesToHex(schnorr.sign(hexToBytes(getEventHash(event)), secretKey))\n    event[verifiedSymbol] = true\n    return event\n  }\n  verifyEvent(event: Event): event is VerifiedEvent {\n    if (typeof event[verifiedSymbol] === 'boolean') return event[verifiedSymbol]\n\n    const hash = getEventHash(event)\n    if (hash !== event.id) {\n      event[verifiedSymbol] = false\n      return false\n    }\n\n    try {\n      const valid = schnorr.verify(hexToBytes(event.sig), hexToBytes(hash), hexToBytes(event.pubkey))\n      event[verifiedSymbol] = valid\n      return valid\n    } catch (err) {\n      event[verifiedSymbol] = false\n      return false\n    }\n  }\n}\n\nexport function serializeEvent(evt: UnsignedEvent): string {\n  if (!validateEvent(evt)) throw new Error(\"can't serialize event with wrong or missing properties\")\n  return JSON.stringify([0, evt.pubkey, evt.created_at, evt.kind, evt.tags, evt.content])\n}\n\nexport function getEventHash(event: UnsignedEvent): string {\n  let eventHash = sha256(utf8Encoder.encode(serializeEvent(event)))\n  return bytesToHex(eventHash)\n}\n\nconst i: JS = new JS()\n\nexport const generateSecretKey = i.generateSecretKey\nexport const getPublicKey = i.getPublicKey\nexport const finalizeEvent = i.finalizeEvent\nexport const verifyEvent = i.verifyEvent\nexport * from './core.ts'\n", "export interface Nostr {\n  generateSecretKey(): Uint8Array\n  getPublicKey(secretKey: Uint8Array): string\n  finalizeEvent(event: EventTemplate, secretKey: Uint8Array): VerifiedEvent\n  verifyEvent(event: Event): event is VerifiedEvent\n}\n\n/** Designates a verified event signature. */\nexport const verifiedSymbol = Symbol('verified')\n\nexport type NostrEvent = {\n  kind: number\n  tags: string[][]\n  content: string\n  created_at: number\n  pubkey: string\n  id: string\n  sig: string\n  [verifiedSymbol]?: boolean\n}\n\nexport type Event = NostrEvent\nexport type EventTemplate = Pick<Event, 'kind' | 'tags' | 'content' | 'created_at'>\nexport type UnsignedEvent = Pick<Event, 'kind' | 'tags' | 'content' | 'created_at' | 'pubkey'>\n\n/** An event whose signature has been verified. */\nexport interface VerifiedEvent extends Event {\n  [verifiedSymbol]: true\n}\n\nconst isRecord = (obj: unknown): obj is Record<string, unknown> => obj instanceof Object\n\nexport function validateEvent<T>(event: T): event is T & UnsignedEvent {\n  if (!isRecord(event)) return false\n  if (typeof event.kind !== 'number') return false\n  if (typeof event.content !== 'string') return false\n  if (typeof event.created_at !== 'number') return false\n  if (typeof event.pubkey !== 'string') return false\n  if (!event.pubkey.match(/^[a-f0-9]{64}$/)) return false\n\n  if (!Array.isArray(event.tags)) return false\n  for (let i = 0; i < event.tags.length; i++) {\n    let tag = event.tags[i]\n    if (!Array.isArray(tag)) return false\n    for (let j = 0; j < tag.length; j++) {\n      if (typeof tag[j] !== 'string') return false\n    }\n  }\n\n  return true\n}\n\n/**\n * Sort events in reverse-chronological order by the `created_at` timestamp,\n * and then by the event `id` (lexicographically) in case of ties.\n * This mutates the array.\n */\nexport function sortEvents(events: Event[]): Event[] {\n  return events.sort((a: NostrEvent, b: NostrEvent): number => {\n    if (a.created_at !== b.created_at) {\n      return b.created_at - a.created_at\n    }\n    return a.id.localeCompare(b.id)\n  })\n}\n", "import type { Event } from './core.ts'\n\nexport const utf8Decoder: TextDecoder = new TextDecoder('utf-8')\nexport const utf8Encoder: TextEncoder = new TextEncoder()\n\nexport { bytesToHex, hexToBytes } from '@noble/hashes/utils.js'\n\nexport function normalizeURL(url: string): string {\n  try {\n    if (url.indexOf('://') === -1) url = 'wss://' + url\n    let p = new URL(url)\n    if (p.protocol === 'http:') p.protocol = 'ws:'\n    else if (p.protocol === 'https:') p.protocol = 'wss:'\n    p.pathname = p.pathname.replace(/\\/+/g, '/')\n    if (p.pathname.endsWith('/')) p.pathname = p.pathname.slice(0, -1)\n    if ((p.port === '80' && p.protocol === 'ws:') || (p.port === '443' && p.protocol === 'wss:')) p.port = ''\n    p.searchParams.sort()\n    p.hash = ''\n    return p.toString()\n  } catch (e) {\n    throw new Error(`Invalid URL: ${url}`)\n  }\n}\n\nexport function insertEventIntoDescendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return b.created_at - event.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function insertEventIntoAscendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return event.created_at - b.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function binarySearch<T>(arr: T[], compare: (b: T) => number): [number, boolean] {\n  let start = 0\n  let end = arr.length - 1\n\n  while (start <= end) {\n    const mid = Math.floor((start + end) / 2)\n    const cmp = compare(arr[mid])\n\n    if (cmp === 0) {\n      return [mid, true]\n    }\n\n    if (cmp < 0) {\n      end = mid - 1\n    } else {\n      start = mid + 1\n    }\n  }\n\n  return [start, false]\n}\n\nexport class QueueNode<V> {\n  public value: V\n  public next: QueueNode<V> | null = null\n  public prev: QueueNode<V> | null = null\n\n  constructor(message: V) {\n    this.value = message\n  }\n}\n\nexport class Queue<V> {\n  public first: QueueNode<V> | null\n  public last: QueueNode<V> | null\n\n  constructor() {\n    this.first = null\n    this.last = null\n  }\n\n  enqueue(value: V): boolean {\n    const newNode = new QueueNode(value)\n    if (!this.last) {\n      // list is empty\n      this.first = newNode\n      this.last = newNode\n    } else if (this.last === this.first) {\n      // list has a single element\n      this.last = newNode\n      this.last.prev = this.first\n      this.first.next = newNode\n    } else {\n      // list has elements, add as last\n      newNode.prev = this.last\n      this.last.next = newNode\n      this.last = newNode\n    }\n    return true\n  }\n\n  dequeue(): V | null {\n    if (!this.first) return null\n\n    if (this.first === this.last) {\n      const target = this.first\n      this.first = null\n      this.last = null\n      return target.value\n    }\n\n    const target = this.first\n    this.first = target.next\n    if (this.first) {\n      this.first.prev = null // fix: clean up prev pointer\n    }\n\n    return target.value\n  }\n}\n", "import { NostrEvent, validateEvent } from './pure.ts'\n\n/** Events are **regular**, which means they're all expected to be stored by relays. */\nexport function isRegularKind(kind: number): boolean {\n  return kind < 10000 && kind !== 0 && kind !== 3\n}\n\n/** Events are **replaceable**, which means that, for each combination of `pubkey` and `kind`, only the latest event is expected to (SHOULD) be stored by relays, older versions are expected to be discarded. */\nexport function isReplaceableKind(kind: number): boolean {\n  return kind === 0 || kind === 3 || (10000 <= kind && kind < 20000)\n}\n\n/** Events are **ephemeral**, which means they are not expected to be stored by relays. */\nexport function isEphemeralKind(kind: number): boolean {\n  return 20000 <= kind && kind < 30000\n}\n\n/** Events are **addressable**, which means that, for each combination of `pubkey`, `kind` and the `d` tag, only the latest event is expected to be stored by relays, older versions are expected to be discarded. */\nexport function isAddressableKind(kind: number): boolean {\n  return 30000 <= kind && kind < 40000\n}\n\n/** Classification of the event kind. */\nexport type KindClassification = 'regular' | 'replaceable' | 'ephemeral' | 'parameterized' | 'unknown'\n\n/** Determine the classification of this kind of event if known, or `unknown`. */\nexport function classifyKind(kind: number): KindClassification {\n  if (isRegularKind(kind)) return 'regular'\n  if (isReplaceableKind(kind)) return 'replaceable'\n  if (isEphemeralKind(kind)) return 'ephemeral'\n  if (isAddressableKind(kind)) return 'parameterized'\n  return 'unknown'\n}\n\nexport function isKind<T extends number>(event: unknown, kind: T | Array<T>): event is NostrEvent & { kind: T } {\n  const kindAsArray: number[] = kind instanceof Array ? kind : [kind]\n  return (validateEvent(event) && kindAsArray.includes(event.kind)) || false\n}\n\nexport const Metadata = 0\nexport type Metadata = typeof Metadata\nexport const ShortTextNote = 1\nexport type ShortTextNote = typeof ShortTextNote\nexport const RecommendRelay = 2\nexport type RecommendRelay = typeof RecommendRelay\nexport const Contacts = 3\nexport type Contacts = typeof Contacts\nexport const EncryptedDirectMessage = 4\nexport type EncryptedDirectMessage = typeof EncryptedDirectMessage\nexport const EventDeletion = 5\nexport type EventDeletion = typeof EventDeletion\nexport const Repost = 6\nexport type Repost = typeof Repost\nexport const Reaction = 7\nexport type Reaction = typeof Reaction\nexport const BadgeAward = 8\nexport type BadgeAward = typeof BadgeAward\nexport const ChatMessage = 9\nexport type ChatMessage = typeof ChatMessage\nexport const ForumThread = 11\nexport type ForumThread = typeof ForumThread\nexport const Seal = 13\nexport type Seal = typeof Seal\nexport const PrivateDirectMessage = 14\nexport type PrivateDirectMessage = typeof PrivateDirectMessage\nexport const FileMessage = 15\nexport type FileMessage = typeof FileMessage\nexport const GenericRepost = 16\nexport type GenericRepost = typeof GenericRepost\nexport const Photo = 20\nexport type Photo = typeof Photo\nexport const NormalVideo = 21\nexport type NormalVideo = typeof NormalVideo\nexport const ShortVideo = 22\nexport type ShortVideo = typeof ShortVideo\nexport const ChannelCreation = 40\nexport type ChannelCreation = typeof ChannelCreation\nexport const ChannelMetadata = 41\nexport type ChannelMetadata = typeof ChannelMetadata\nexport const ChannelMessage = 42\nexport type ChannelMessage = typeof ChannelMessage\nexport const ChannelHideMessage = 43\nexport type ChannelHideMessage = typeof ChannelHideMessage\nexport const ChannelMuteUser = 44\nexport type ChannelMuteUser = typeof ChannelMuteUser\nexport const OpenTimestamps = 1040\nexport type OpenTimestamps = typeof OpenTimestamps\nexport const GiftWrap = 1059\nexport type GiftWrap = typeof GiftWrap\nexport const Poll = 1068\nexport type Poll = typeof Poll\nexport const FileMetadata = 1063\nexport type FileMetadata = typeof FileMetadata\nexport const Comment = 1111\nexport type Comment = typeof Comment\nexport const LiveChatMessage = 1311\nexport type LiveChatMessage = typeof LiveChatMessage\nexport const Voice = 1222\nexport type Voice = typeof Voice\nexport const VoiceComment = 1244\nexport type VoiceComment = typeof VoiceComment\nexport const ProblemTracker = 1971\nexport type ProblemTracker = typeof ProblemTracker\nexport const Report = 1984\nexport type Report = typeof Report\nexport const Reporting = 1984\nexport type Reporting = typeof Reporting\nexport const Label = 1985\nexport type Label = typeof Label\nexport const CommunityPostApproval = 4550\nexport type CommunityPostApproval = typeof CommunityPostApproval\nexport const JobRequest = 5999\nexport type JobRequest = typeof JobRequest\nexport const JobResult = 6999\nexport type JobResult = typeof JobResult\nexport const JobFeedback = 7000\nexport type JobFeedback = typeof JobFeedback\nexport const ZapGoal = 9041\nexport type ZapGoal = typeof ZapGoal\nexport const ZapRequest = 9734\nexport type ZapRequest = typeof ZapRequest\nexport const Zap = 9735\nexport type Zap = typeof Zap\nexport const Highlights = 9802\nexport type Highlights = typeof Highlights\nexport const PollResponse = 1018\nexport type PollResponse = typeof PollResponse\nexport const Mutelist = 10000\nexport type Mutelist = typeof Mutelist\nexport const Pinlist = 10001\nexport type Pinlist = typeof Pinlist\nexport const RelayList = 10002\nexport type RelayList = typeof RelayList\nexport const BookmarkList = 10003\nexport type BookmarkList = typeof BookmarkList\nexport const CommunitiesList = 10004\nexport type CommunitiesList = typeof CommunitiesList\nexport const PublicChatsList = 10005\nexport type PublicChatsList = typeof PublicChatsList\nexport const BlockedRelaysList = 10006\nexport type BlockedRelaysList = typeof BlockedRelaysList\nexport const SearchRelaysList = 10007\nexport type SearchRelaysList = typeof SearchRelaysList\nexport const FavoriteRelays = 10012\nexport type FavoriteRelays = typeof FavoriteRelays\nexport const InterestsList = 10015\nexport type InterestsList = typeof InterestsList\nexport const UserEmojiList = 10030\nexport type UserEmojiList = typeof UserEmojiList\nexport const DirectMessageRelaysList = 10050\nexport type DirectMessageRelaysList = typeof DirectMessageRelaysList\nexport const FileServerPreference = 10096\nexport type FileServerPreference = typeof FileServerPreference\nexport const BlossomServerList = 10063\nexport type BlossomServerList = typeof BlossomServerList\nexport const NWCWalletInfo = 13194\nexport type NWCWalletInfo = typeof NWCWalletInfo\nexport const LightningPubRPC = 21000\nexport type LightningPubRPC = typeof LightningPubRPC\nexport const ClientAuth = 22242\nexport type ClientAuth = typeof ClientAuth\nexport const NWCWalletRequest = 23194\nexport type NWCWalletRequest = typeof NWCWalletRequest\nexport const NWCWalletResponse = 23195\nexport type NWCWalletResponse = typeof NWCWalletResponse\nexport const NostrConnect = 24133\nexport type NostrConnect = typeof NostrConnect\nexport const HTTPAuth = 27235\nexport type HTTPAuth = typeof HTTPAuth\nexport const Followsets = 30000\nexport type Followsets = typeof Followsets\nexport const Genericlists = 30001\nexport type Genericlists = typeof Genericlists\nexport const Relaysets = 30002\nexport type Relaysets = typeof Relaysets\nexport const Bookmarksets = 30003\nexport type Bookmarksets = typeof Bookmarksets\nexport const Curationsets = 30004\nexport type Curationsets = typeof Curationsets\nexport const ProfileBadges = 30008\nexport type ProfileBadges = typeof ProfileBadges\nexport const BadgeDefinition = 30009\nexport type BadgeDefinition = typeof BadgeDefinition\nexport const Interestsets = 30015\nexport type Interestsets = typeof Interestsets\nexport const CreateOrUpdateStall = 30017\nexport type CreateOrUpdateStall = typeof CreateOrUpdateStall\nexport const CreateOrUpdateProduct = 30018\nexport type CreateOrUpdateProduct = typeof CreateOrUpdateProduct\nexport const LongFormArticle = 30023\nexport type LongFormArticle = typeof LongFormArticle\nexport const DraftLong = 30024\nexport type DraftLong = typeof DraftLong\nexport const Emojisets = 30030\nexport type Emojisets = typeof Emojisets\nexport const Application = 30078\nexport type Application = typeof Application\nexport const LiveEvent = 30311\nexport type LiveEvent = typeof LiveEvent\nexport const UserStatuses = 30315\nexport type UserStatuses = typeof UserStatuses\nexport const ClassifiedListing = 30402\nexport type ClassifiedListing = typeof ClassifiedListing\nexport const DraftClassifiedListing = 30403\nexport type DraftClassifiedListing = typeof DraftClassifiedListing\nexport const Date = 31922\nexport type Date = typeof Date\nexport const Time = 31923\nexport type Time = typeof Time\nexport const Calendar = 31924\nexport type Calendar = typeof Calendar\nexport const CalendarEventRSVP = 31925\nexport type CalendarEventRSVP = typeof CalendarEventRSVP\nexport const RelayReview = 31987\nexport type RelayReview = typeof RelayReview\nexport const Handlerrecommendation = 31989\nexport type Handlerrecommendation = typeof Handlerrecommendation\nexport const Handlerinformation = 31990\nexport type Handlerinformation = typeof Handlerinformation\nexport const CommunityDefinition = 34550\nexport type CommunityDefinition = typeof CommunityDefinition\nexport const GroupMetadata = 39000\nexport type GroupMetadata = typeof GroupMetadata\n", "import { GenericRepost, Repost, ShortTextNote } from './kinds.ts'\nimport { EventPointer } from './nip19.ts'\nimport { Event, finalizeEvent, verifyEvent } from './pure.ts'\n\nexport type RepostEventTemplate = {\n  /**\n   * Pass only non-nip18 tags if you have to.\n   * Nip18 tags ('e' and 'p' tags pointing to the reposted event) will be added automatically.\n   */\n  tags?: string[][]\n\n  /**\n   * Pass an empty string to NOT include the stringified JSON of the reposted event.\n   * Any other content will be ignored and replaced with the stringified JSON of the reposted event.\n   * @default Stringified JSON of the reposted event\n   */\n  content?: ''\n\n  created_at: number\n}\n\nexport function finishRepostEvent(\n  t: RepostEventTemplate,\n  reposted: Event,\n  relayUrl: string,\n  privateKey: Uint8Array,\n): Event {\n  let kind: Repost | GenericRepost\n  const tags = [...(t.tags ?? []), ['e', reposted.id, relayUrl], ['p', reposted.pubkey]]\n  if (reposted.kind === ShortTextNote) {\n    kind = Repost\n  } else {\n    kind = GenericRepost\n    tags.push(['k', String(reposted.kind)])\n  }\n\n  return finalizeEvent(\n    {\n      kind,\n      tags,\n      content: t.content === '' || reposted.tags?.find(tag => tag[0] === '-') ? '' : JSON.stringify(reposted),\n      created_at: t.created_at,\n    },\n    privateKey,\n  )\n}\n\nexport function getRepostedEventPointer(event: Event): undefined | EventPointer {\n  if (![Repost, GenericRepost].includes(event.kind)) {\n    return undefined\n  }\n\n  let lastETag: undefined | string[]\n  let lastPTag: undefined | string[]\n\n  for (let i = event.tags.length - 1; i >= 0 && (lastETag === undefined || lastPTag === undefined); i--) {\n    const tag = event.tags[i]\n    if (tag.length >= 2) {\n      if (tag[0] === 'e' && lastETag === undefined) {\n        lastETag = tag\n      } else if (tag[0] === 'p' && lastPTag === undefined) {\n        lastPTag = tag\n      }\n    }\n  }\n\n  if (lastETag === undefined) {\n    return undefined\n  }\n\n  return {\n    id: lastETag[1],\n    relays: [lastETag[2], lastPTag?.[2]].filter((x): x is string => typeof x === 'string'),\n    author: lastPTag?.[1],\n  }\n}\n\nexport type GetRepostedEventOptions = {\n  skipVerification?: boolean\n}\n\nexport function getRepostedEvent(event: Event, { skipVerification }: GetRepostedEventOptions = {}): undefined | Event {\n  const pointer = getRepostedEventPointer(event)\n\n  if (pointer === undefined || event.content === '') {\n    return undefined\n  }\n\n  let repostedEvent: undefined | Event\n\n  try {\n    repostedEvent = JSON.parse(event.content) as Event\n  } catch (error) {\n    return undefined\n  }\n\n  if (repostedEvent.id !== pointer.id) {\n    return undefined\n  }\n\n  if (!skipVerification && !verifyEvent(repostedEvent)) {\n    return undefined\n  }\n\n  return repostedEvent\n}\n"],
+  "mappings": "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",
+  "names": ["bytesToHex", "hexToBytes", "i", "bytesToHex", "hexToBytes", "i"]
 }
Index: package/lib/cjs/nip19.js.map
===================================================================
--- package/lib/cjs/nip19.js.map
+++ package/lib/cjs/nip19.js.map
@@ -1,7 +1,7 @@
 {
   "version": 3,
   "sources": ["../../nip19.ts", "../../utils.ts"],
-  "sourcesContent": ["import { bytesToHex, concatBytes, hexToBytes } from '@noble/hashes/utils'\nimport { bech32 } from '@scure/base'\n\nimport { utf8Decoder, utf8Encoder } from './utils.ts'\n\nexport type NProfile = `nprofile1${string}`\nexport type NEvent = `nevent1${string}`\nexport type NAddr = `naddr1${string}`\nexport type NSec = `nsec1${string}`\nexport type NPub = `npub1${string}`\nexport type Note = `note1${string}`\nexport type Ncryptsec = `ncryptsec1${string}`\n\nexport const NostrTypeGuard = {\n  isNProfile: (value?: string | null): value is NProfile => /^nprofile1[a-z\\d]+$/.test(value || ''),\n  isNEvent: (value?: string | null): value is NEvent => /^nevent1[a-z\\d]+$/.test(value || ''),\n  isNAddr: (value?: string | null): value is NAddr => /^naddr1[a-z\\d]+$/.test(value || ''),\n  isNSec: (value?: string | null): value is NSec => /^nsec1[a-z\\d]{58}$/.test(value || ''),\n  isNPub: (value?: string | null): value is NPub => /^npub1[a-z\\d]{58}$/.test(value || ''),\n  isNote: (value?: string | null): value is Note => /^note1[a-z\\d]+$/.test(value || ''),\n  isNcryptsec: (value?: string | null): value is Ncryptsec => /^ncryptsec1[a-z\\d]+$/.test(value || ''),\n}\n\nexport const Bech32MaxSize = 5000\n\n/**\n * Bech32 regex.\n * @see https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki#bech32\n */\nexport const BECH32_REGEX = /[\\x21-\\x7E]{1,83}1[023456789acdefghjklmnpqrstuvwxyz]{6,}/\n\nfunction integerToUint8Array(number: number) {\n  // Create a Uint8Array with enough space to hold a 32-bit integer (4 bytes).\n  const uint8Array = new Uint8Array(4)\n\n  // Use bitwise operations to extract the bytes.\n  uint8Array[0] = (number >> 24) & 0xff // Most significant byte (MSB)\n  uint8Array[1] = (number >> 16) & 0xff\n  uint8Array[2] = (number >> 8) & 0xff\n  uint8Array[3] = number & 0xff // Least significant byte (LSB)\n\n  return uint8Array\n}\n\nexport type ProfilePointer = {\n  pubkey: string // hex\n  relays?: string[]\n}\n\nexport type EventPointer = {\n  id: string // hex\n  relays?: string[]\n  author?: string\n  kind?: number\n}\n\nexport type AddressPointer = {\n  identifier: string\n  pubkey: string\n  kind: number\n  relays?: string[]\n}\n\nexport function decodeNostrURI(nip19code: string): ReturnType<typeof decode> | { type: 'invalid'; data: null } {\n  try {\n    if (nip19code.startsWith('nostr:')) nip19code = nip19code.substring(6)\n    return decode(nip19code)\n  } catch (_err) {\n    return { type: 'invalid', data: null }\n  }\n}\n\nexport type DecodedNevent = {\n  type: 'nevent'\n  data: EventPointer\n}\n\nexport type DecodedNprofile = {\n  type: 'nprofile'\n  data: ProfilePointer\n}\n\nexport type DecodedNaddr = {\n  type: 'naddr'\n  data: AddressPointer\n}\n\nexport type DecodedNsec = {\n  type: 'nsec'\n  data: Uint8Array\n}\n\nexport type DecodedNpub = {\n  type: 'npub'\n  data: string\n}\n\nexport type DecodedNote = {\n  type: 'note'\n  data: string\n}\n\nexport type DecodedResult = DecodedNevent | DecodedNprofile | DecodedNaddr | DecodedNpub | DecodedNsec | DecodedNote\n\nexport function decode(nip19: NEvent): DecodedNevent\nexport function decode(nip19: NProfile): DecodedNprofile\nexport function decode(nip19: NAddr): DecodedNaddr\nexport function decode(nip19: NSec): DecodedNsec\nexport function decode(nip19: NPub): DecodedNpub\nexport function decode(nip19: Note): DecodedNote\nexport function decode(code: string): DecodedResult\nexport function decode(code: string): DecodedResult {\n  let { prefix, words } = bech32.decode(code, Bech32MaxSize)\n  let data = new Uint8Array(bech32.fromWords(words))\n\n  switch (prefix) {\n    case 'nprofile': {\n      let tlv = parseTLV(data)\n      if (!tlv[0]?.[0]) throw new Error('missing TLV 0 for nprofile')\n      if (tlv[0][0].length !== 32) throw new Error('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      let tlv = parseTLV(data)\n      if (!tlv[0]?.[0]) throw new Error('missing TLV 0 for nevent')\n      if (tlv[0][0].length !== 32) throw new Error('TLV 0 should be 32 bytes')\n      if (tlv[2] && tlv[2][0].length !== 32) throw new Error('TLV 2 should be 32 bytes')\n      if (tlv[3] && tlv[3][0].length !== 4) throw new Error('TLV 3 should be 4 bytes')\n\n      return {\n        type: 'nevent',\n        data: {\n          id: bytesToHex(tlv[0][0]),\n          relays: tlv[1] ? tlv[1].map(d => utf8Decoder.decode(d)) : [],\n          author: tlv[2]?.[0] ? bytesToHex(tlv[2][0]) : undefined,\n          kind: tlv[3]?.[0] ? parseInt(bytesToHex(tlv[3][0]), 16) : undefined,\n        },\n      }\n    }\n\n    case 'naddr': {\n      let tlv = parseTLV(data)\n      if (!tlv[0]?.[0]) throw new Error('missing TLV 0 for naddr')\n      if (!tlv[2]?.[0]) throw new Error('missing TLV 2 for naddr')\n      if (tlv[2][0].length !== 32) throw new Error('TLV 2 should be 32 bytes')\n      if (!tlv[3]?.[0]) throw new Error('missing TLV 3 for naddr')\n      if (tlv[3][0].length !== 4) throw new Error('TLV 3 should be 4 bytes')\n\n      return {\n        type: 'naddr',\n        data: {\n          identifier: utf8Decoder.decode(tlv[0][0]),\n          pubkey: bytesToHex(tlv[2][0]),\n          kind: parseInt(bytesToHex(tlv[3][0]), 16),\n          relays: tlv[1] ? tlv[1].map(d => utf8Decoder.decode(d)) : [],\n        },\n      }\n    }\n\n    case 'nsec':\n      return { type: prefix, data }\n\n    case 'npub':\n    case 'note':\n      return { type: prefix, data: bytesToHex(data) }\n\n    default:\n      throw new Error(`unknown prefix ${prefix}`)\n  }\n}\n\ntype TLV = { [t: number]: Uint8Array[] }\n\nfunction parseTLV(data: Uint8Array): TLV {\n  let result: TLV = {}\n  let rest = data\n  while (rest.length > 0) {\n    let t = rest[0]\n    let l = rest[1]\n    let v = rest.slice(2, 2 + l)\n    rest = rest.slice(2 + l)\n    if (v.length < l) throw new Error(`not enough data to read on TLV ${t}`)\n    result[t] = result[t] || []\n    result[t].push(v)\n  }\n  return result\n}\n\nexport function nsecEncode(key: Uint8Array): NSec {\n  return encodeBytes('nsec', key)\n}\n\nexport function npubEncode(hex: string): NPub {\n  return encodeBytes('npub', hexToBytes(hex))\n}\n\nexport function noteEncode(hex: string): Note {\n  return encodeBytes('note', hexToBytes(hex))\n}\n\nfunction encodeBech32<Prefix extends string>(prefix: Prefix, data: Uint8Array): `${Prefix}1${string}` {\n  let words = bech32.toWords(data)\n  return bech32.encode(prefix, words, Bech32MaxSize) as `${Prefix}1${string}`\n}\n\nexport function encodeBytes<Prefix extends string>(prefix: Prefix, bytes: Uint8Array): `${Prefix}1${string}` {\n  return encodeBech32(prefix, bytes)\n}\n\nexport function nprofileEncode(profile: ProfilePointer): NProfile {\n  let data = encodeTLV({\n    0: [hexToBytes(profile.pubkey)],\n    1: (profile.relays || []).map(url => utf8Encoder.encode(url)),\n  })\n  return encodeBech32('nprofile', data)\n}\n\nexport function neventEncode(event: EventPointer): NEvent {\n  let kindArray\n  if (event.kind !== undefined) {\n    kindArray = integerToUint8Array(event.kind)\n  }\n\n  let data = encodeTLV({\n    0: [hexToBytes(event.id)],\n    1: (event.relays || []).map(url => utf8Encoder.encode(url)),\n    2: event.author ? [hexToBytes(event.author)] : [],\n    3: kindArray ? [new Uint8Array(kindArray)] : [],\n  })\n\n  return encodeBech32('nevent', data)\n}\n\nexport function naddrEncode(addr: AddressPointer): NAddr {\n  let kind = new ArrayBuffer(4)\n  new DataView(kind).setUint32(0, addr.kind, false)\n\n  let data = encodeTLV({\n    0: [utf8Encoder.encode(addr.identifier)],\n    1: (addr.relays || []).map(url => utf8Encoder.encode(url)),\n    2: [hexToBytes(addr.pubkey)],\n    3: [new Uint8Array(kind)],\n  })\n  return encodeBech32('naddr', data)\n}\n\nfunction encodeTLV(tlv: TLV): Uint8Array {\n  let entries: Uint8Array[] = []\n\n  Object.entries(tlv)\n    .reverse()\n    .forEach(([t, vs]) => {\n      vs.forEach(v => {\n        let entry = new Uint8Array(v.length + 2)\n        entry.set([parseInt(t)], 0)\n        entry.set([v.length], 1)\n        entry.set(v, 2)\n        entries.push(entry)\n      })\n    })\n\n  return concatBytes(...entries)\n}\n", "import type { Event } from './core.ts'\n\nexport const utf8Decoder: TextDecoder = new TextDecoder('utf-8')\nexport const utf8Encoder: TextEncoder = new TextEncoder()\n\nexport { bytesToHex, hexToBytes } from '@noble/hashes/utils'\n\nexport function normalizeURL(url: string): string {\n  try {\n    if (url.indexOf('://') === -1) url = 'wss://' + url\n    let p = new URL(url)\n    if (p.protocol === 'http:') p.protocol = 'ws:'\n    else if (p.protocol === 'https:') p.protocol = 'wss:'\n    p.pathname = p.pathname.replace(/\\/+/g, '/')\n    if (p.pathname.endsWith('/')) p.pathname = p.pathname.slice(0, -1)\n    if ((p.port === '80' && p.protocol === 'ws:') || (p.port === '443' && p.protocol === 'wss:')) p.port = ''\n    p.searchParams.sort()\n    p.hash = ''\n    return p.toString()\n  } catch (e) {\n    throw new Error(`Invalid URL: ${url}`)\n  }\n}\n\nexport function insertEventIntoDescendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return b.created_at - event.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function insertEventIntoAscendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return event.created_at - b.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function binarySearch<T>(arr: T[], compare: (b: T) => number): [number, boolean] {\n  let start = 0\n  let end = arr.length - 1\n\n  while (start <= end) {\n    const mid = Math.floor((start + end) / 2)\n    const cmp = compare(arr[mid])\n\n    if (cmp === 0) {\n      return [mid, true]\n    }\n\n    if (cmp < 0) {\n      end = mid - 1\n    } else {\n      start = mid + 1\n    }\n  }\n\n  return [start, false]\n}\n\nexport class QueueNode<V> {\n  public value: V\n  public next: QueueNode<V> | null = null\n  public prev: QueueNode<V> | null = null\n\n  constructor(message: V) {\n    this.value = message\n  }\n}\n\nexport class Queue<V> {\n  public first: QueueNode<V> | null\n  public last: QueueNode<V> | null\n\n  constructor() {\n    this.first = null\n    this.last = null\n  }\n\n  enqueue(value: V): boolean {\n    const newNode = new QueueNode(value)\n    if (!this.last) {\n      // list is empty\n      this.first = newNode\n      this.last = newNode\n    } else if (this.last === this.first) {\n      // list has a single element\n      this.last = newNode\n      this.last.prev = this.first\n      this.first.next = newNode\n    } else {\n      // list has elements, add as last\n      newNode.prev = this.last\n      this.last.next = newNode\n      this.last = newNode\n    }\n    return true\n  }\n\n  dequeue(): V | null {\n    if (!this.first) return null\n\n    if (this.first === this.last) {\n      const target = this.first\n      this.first = null\n      this.last = null\n      return target.value\n    }\n\n    const target = this.first\n    this.first = target.next\n    if (this.first) {\n      this.first.prev = null // fix: clean up prev pointer\n    }\n\n    return target.value\n  }\n}\n"],
-  "mappings": "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",
+  "sourcesContent": ["import { bytesToHex, concatBytes, hexToBytes } from '@noble/hashes/utils.js'\nimport { bech32 } from '@scure/base'\n\nimport { utf8Decoder, utf8Encoder } from './utils.ts'\n\nexport type NProfile = `nprofile1${string}`\nexport type NEvent = `nevent1${string}`\nexport type NAddr = `naddr1${string}`\nexport type NSec = `nsec1${string}`\nexport type NPub = `npub1${string}`\nexport type Note = `note1${string}`\nexport type Ncryptsec = `ncryptsec1${string}`\n\nexport const NostrTypeGuard = {\n  isNProfile: (value?: string | null): value is NProfile => /^nprofile1[a-z\\d]+$/.test(value || ''),\n  isNEvent: (value?: string | null): value is NEvent => /^nevent1[a-z\\d]+$/.test(value || ''),\n  isNAddr: (value?: string | null): value is NAddr => /^naddr1[a-z\\d]+$/.test(value || ''),\n  isNSec: (value?: string | null): value is NSec => /^nsec1[a-z\\d]{58}$/.test(value || ''),\n  isNPub: (value?: string | null): value is NPub => /^npub1[a-z\\d]{58}$/.test(value || ''),\n  isNote: (value?: string | null): value is Note => /^note1[a-z\\d]+$/.test(value || ''),\n  isNcryptsec: (value?: string | null): value is Ncryptsec => /^ncryptsec1[a-z\\d]+$/.test(value || ''),\n}\n\nexport const Bech32MaxSize = 5000\n\n/**\n * Bech32 regex.\n * @see https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki#bech32\n */\nexport const BECH32_REGEX = /[\\x21-\\x7E]{1,83}1[023456789acdefghjklmnpqrstuvwxyz]{6,}/\n\nfunction integerToUint8Array(number: number) {\n  // Create a Uint8Array with enough space to hold a 32-bit integer (4 bytes).\n  const uint8Array = new Uint8Array(4)\n\n  // Use bitwise operations to extract the bytes.\n  uint8Array[0] = (number >> 24) & 0xff // Most significant byte (MSB)\n  uint8Array[1] = (number >> 16) & 0xff\n  uint8Array[2] = (number >> 8) & 0xff\n  uint8Array[3] = number & 0xff // Least significant byte (LSB)\n\n  return uint8Array\n}\n\nexport type ProfilePointer = {\n  pubkey: string // hex\n  relays?: string[]\n}\n\nexport type EventPointer = {\n  id: string // hex\n  relays?: string[]\n  author?: string\n  kind?: number\n}\n\nexport type AddressPointer = {\n  identifier: string\n  pubkey: string\n  kind: number\n  relays?: string[]\n}\n\nexport function decodeNostrURI(nip19code: string): ReturnType<typeof decode> | { type: 'invalid'; data: null } {\n  try {\n    if (nip19code.startsWith('nostr:')) nip19code = nip19code.substring(6)\n    return decode(nip19code)\n  } catch (_err) {\n    return { type: 'invalid', data: null }\n  }\n}\n\nexport type DecodedNevent = {\n  type: 'nevent'\n  data: EventPointer\n}\n\nexport type DecodedNprofile = {\n  type: 'nprofile'\n  data: ProfilePointer\n}\n\nexport type DecodedNaddr = {\n  type: 'naddr'\n  data: AddressPointer\n}\n\nexport type DecodedNsec = {\n  type: 'nsec'\n  data: Uint8Array\n}\n\nexport type DecodedNpub = {\n  type: 'npub'\n  data: string\n}\n\nexport type DecodedNote = {\n  type: 'note'\n  data: string\n}\n\nexport type DecodedResult = DecodedNevent | DecodedNprofile | DecodedNaddr | DecodedNpub | DecodedNsec | DecodedNote\n\nexport function decode(nip19: NEvent): DecodedNevent\nexport function decode(nip19: NProfile): DecodedNprofile\nexport function decode(nip19: NAddr): DecodedNaddr\nexport function decode(nip19: NSec): DecodedNsec\nexport function decode(nip19: NPub): DecodedNpub\nexport function decode(nip19: Note): DecodedNote\nexport function decode(code: string): DecodedResult\nexport function decode(code: string): DecodedResult {\n  let { prefix, words } = bech32.decode(code as `${string}1${string}`, Bech32MaxSize)\n  let data = new Uint8Array(bech32.fromWords(words))\n\n  switch (prefix) {\n    case 'nprofile': {\n      let tlv = parseTLV(data)\n      if (!tlv[0]?.[0]) throw new Error('missing TLV 0 for nprofile')\n      if (tlv[0][0].length !== 32) throw new Error('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      let tlv = parseTLV(data)\n      if (!tlv[0]?.[0]) throw new Error('missing TLV 0 for nevent')\n      if (tlv[0][0].length !== 32) throw new Error('TLV 0 should be 32 bytes')\n      if (tlv[2] && tlv[2][0].length !== 32) throw new Error('TLV 2 should be 32 bytes')\n      if (tlv[3] && tlv[3][0].length !== 4) throw new Error('TLV 3 should be 4 bytes')\n\n      return {\n        type: 'nevent',\n        data: {\n          id: bytesToHex(tlv[0][0]),\n          relays: tlv[1] ? tlv[1].map(d => utf8Decoder.decode(d)) : [],\n          author: tlv[2]?.[0] ? bytesToHex(tlv[2][0]) : undefined,\n          kind: tlv[3]?.[0] ? parseInt(bytesToHex(tlv[3][0]), 16) : undefined,\n        },\n      }\n    }\n\n    case 'naddr': {\n      let tlv = parseTLV(data)\n      if (!tlv[0]?.[0]) throw new Error('missing TLV 0 for naddr')\n      if (!tlv[2]?.[0]) throw new Error('missing TLV 2 for naddr')\n      if (tlv[2][0].length !== 32) throw new Error('TLV 2 should be 32 bytes')\n      if (!tlv[3]?.[0]) throw new Error('missing TLV 3 for naddr')\n      if (tlv[3][0].length !== 4) throw new Error('TLV 3 should be 4 bytes')\n\n      return {\n        type: 'naddr',\n        data: {\n          identifier: utf8Decoder.decode(tlv[0][0]),\n          pubkey: bytesToHex(tlv[2][0]),\n          kind: parseInt(bytesToHex(tlv[3][0]), 16),\n          relays: tlv[1] ? tlv[1].map(d => utf8Decoder.decode(d)) : [],\n        },\n      }\n    }\n\n    case 'nsec':\n      return { type: prefix, data }\n\n    case 'npub':\n    case 'note':\n      return { type: prefix, data: bytesToHex(data) }\n\n    default:\n      throw new Error(`unknown prefix ${prefix}`)\n  }\n}\n\ntype TLV = { [t: number]: Uint8Array[] }\n\nfunction parseTLV(data: Uint8Array): TLV {\n  let result: TLV = {}\n  let rest = data\n  while (rest.length > 0) {\n    let t = rest[0]\n    let l = rest[1]\n    let v = rest.slice(2, 2 + l)\n    rest = rest.slice(2 + l)\n    if (v.length < l) throw new Error(`not enough data to read on TLV ${t}`)\n    result[t] = result[t] || []\n    result[t].push(v)\n  }\n  return result\n}\n\nexport function nsecEncode(key: Uint8Array): NSec {\n  return encodeBytes('nsec', key)\n}\n\nexport function npubEncode(hex: string): NPub {\n  return encodeBytes('npub', hexToBytes(hex))\n}\n\nexport function noteEncode(hex: string): Note {\n  return encodeBytes('note', hexToBytes(hex))\n}\n\nfunction encodeBech32<Prefix extends string>(prefix: Prefix, data: Uint8Array): `${Prefix}1${string}` {\n  let words = bech32.toWords(data)\n  return bech32.encode(prefix, words, Bech32MaxSize) as `${Prefix}1${string}`\n}\n\nexport function encodeBytes<Prefix extends string>(prefix: Prefix, bytes: Uint8Array): `${Prefix}1${string}` {\n  return encodeBech32(prefix, bytes)\n}\n\nexport function nprofileEncode(profile: ProfilePointer): NProfile {\n  let data = encodeTLV({\n    0: [hexToBytes(profile.pubkey)],\n    1: (profile.relays || []).map(url => utf8Encoder.encode(url)),\n  })\n  return encodeBech32('nprofile', data)\n}\n\nexport function neventEncode(event: EventPointer): NEvent {\n  let kindArray\n  if (event.kind !== undefined) {\n    kindArray = integerToUint8Array(event.kind)\n  }\n\n  let data = encodeTLV({\n    0: [hexToBytes(event.id)],\n    1: (event.relays || []).map(url => utf8Encoder.encode(url)),\n    2: event.author ? [hexToBytes(event.author)] : [],\n    3: kindArray ? [new Uint8Array(kindArray)] : [],\n  })\n\n  return encodeBech32('nevent', data)\n}\n\nexport function naddrEncode(addr: AddressPointer): NAddr {\n  let kind = new ArrayBuffer(4)\n  new DataView(kind).setUint32(0, addr.kind, false)\n\n  let data = encodeTLV({\n    0: [utf8Encoder.encode(addr.identifier)],\n    1: (addr.relays || []).map(url => utf8Encoder.encode(url)),\n    2: [hexToBytes(addr.pubkey)],\n    3: [new Uint8Array(kind)],\n  })\n  return encodeBech32('naddr', data)\n}\n\nfunction encodeTLV(tlv: TLV): Uint8Array {\n  let entries: Uint8Array[] = []\n\n  Object.entries(tlv)\n    .reverse()\n    .forEach(([t, vs]) => {\n      vs.forEach(v => {\n        let entry = new Uint8Array(v.length + 2)\n        entry.set([parseInt(t)], 0)\n        entry.set([v.length], 1)\n        entry.set(v, 2)\n        entries.push(entry)\n      })\n    })\n\n  return concatBytes(...entries)\n}\n", "import type { Event } from './core.ts'\n\nexport const utf8Decoder: TextDecoder = new TextDecoder('utf-8')\nexport const utf8Encoder: TextEncoder = new TextEncoder()\n\nexport { bytesToHex, hexToBytes } from '@noble/hashes/utils.js'\n\nexport function normalizeURL(url: string): string {\n  try {\n    if (url.indexOf('://') === -1) url = 'wss://' + url\n    let p = new URL(url)\n    if (p.protocol === 'http:') p.protocol = 'ws:'\n    else if (p.protocol === 'https:') p.protocol = 'wss:'\n    p.pathname = p.pathname.replace(/\\/+/g, '/')\n    if (p.pathname.endsWith('/')) p.pathname = p.pathname.slice(0, -1)\n    if ((p.port === '80' && p.protocol === 'ws:') || (p.port === '443' && p.protocol === 'wss:')) p.port = ''\n    p.searchParams.sort()\n    p.hash = ''\n    return p.toString()\n  } catch (e) {\n    throw new Error(`Invalid URL: ${url}`)\n  }\n}\n\nexport function insertEventIntoDescendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return b.created_at - event.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function insertEventIntoAscendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return event.created_at - b.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function binarySearch<T>(arr: T[], compare: (b: T) => number): [number, boolean] {\n  let start = 0\n  let end = arr.length - 1\n\n  while (start <= end) {\n    const mid = Math.floor((start + end) / 2)\n    const cmp = compare(arr[mid])\n\n    if (cmp === 0) {\n      return [mid, true]\n    }\n\n    if (cmp < 0) {\n      end = mid - 1\n    } else {\n      start = mid + 1\n    }\n  }\n\n  return [start, false]\n}\n\nexport class QueueNode<V> {\n  public value: V\n  public next: QueueNode<V> | null = null\n  public prev: QueueNode<V> | null = null\n\n  constructor(message: V) {\n    this.value = message\n  }\n}\n\nexport class Queue<V> {\n  public first: QueueNode<V> | null\n  public last: QueueNode<V> | null\n\n  constructor() {\n    this.first = null\n    this.last = null\n  }\n\n  enqueue(value: V): boolean {\n    const newNode = new QueueNode(value)\n    if (!this.last) {\n      // list is empty\n      this.first = newNode\n      this.last = newNode\n    } else if (this.last === this.first) {\n      // list has a single element\n      this.last = newNode\n      this.last.prev = this.first\n      this.first.next = newNode\n    } else {\n      // list has elements, add as last\n      newNode.prev = this.last\n      this.last.next = newNode\n      this.last = newNode\n    }\n    return true\n  }\n\n  dequeue(): V | null {\n    if (!this.first) return null\n\n    if (this.first === this.last) {\n      const target = this.first\n      this.first = null\n      this.last = null\n      return target.value\n    }\n\n    const target = this.first\n    this.first = target.next\n    if (this.first) {\n      this.first.prev = null // fix: clean up prev pointer\n    }\n\n    return target.value\n  }\n}\n"],
+  "mappings": "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",
   "names": ["import_utils"]
 }
Index: package/lib/esm/nip19.js.map
===================================================================
--- package/lib/esm/nip19.js.map
+++ package/lib/esm/nip19.js.map
@@ -1,7 +1,7 @@
 {
   "version": 3,
   "sources": ["../../nip19.ts", "../../utils.ts"],
-  "sourcesContent": ["import { bytesToHex, concatBytes, hexToBytes } from '@noble/hashes/utils'\nimport { bech32 } from '@scure/base'\n\nimport { utf8Decoder, utf8Encoder } from './utils.ts'\n\nexport type NProfile = `nprofile1${string}`\nexport type NEvent = `nevent1${string}`\nexport type NAddr = `naddr1${string}`\nexport type NSec = `nsec1${string}`\nexport type NPub = `npub1${string}`\nexport type Note = `note1${string}`\nexport type Ncryptsec = `ncryptsec1${string}`\n\nexport const NostrTypeGuard = {\n  isNProfile: (value?: string | null): value is NProfile => /^nprofile1[a-z\\d]+$/.test(value || ''),\n  isNEvent: (value?: string | null): value is NEvent => /^nevent1[a-z\\d]+$/.test(value || ''),\n  isNAddr: (value?: string | null): value is NAddr => /^naddr1[a-z\\d]+$/.test(value || ''),\n  isNSec: (value?: string | null): value is NSec => /^nsec1[a-z\\d]{58}$/.test(value || ''),\n  isNPub: (value?: string | null): value is NPub => /^npub1[a-z\\d]{58}$/.test(value || ''),\n  isNote: (value?: string | null): value is Note => /^note1[a-z\\d]+$/.test(value || ''),\n  isNcryptsec: (value?: string | null): value is Ncryptsec => /^ncryptsec1[a-z\\d]+$/.test(value || ''),\n}\n\nexport const Bech32MaxSize = 5000\n\n/**\n * Bech32 regex.\n * @see https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki#bech32\n */\nexport const BECH32_REGEX = /[\\x21-\\x7E]{1,83}1[023456789acdefghjklmnpqrstuvwxyz]{6,}/\n\nfunction integerToUint8Array(number: number) {\n  // Create a Uint8Array with enough space to hold a 32-bit integer (4 bytes).\n  const uint8Array = new Uint8Array(4)\n\n  // Use bitwise operations to extract the bytes.\n  uint8Array[0] = (number >> 24) & 0xff // Most significant byte (MSB)\n  uint8Array[1] = (number >> 16) & 0xff\n  uint8Array[2] = (number >> 8) & 0xff\n  uint8Array[3] = number & 0xff // Least significant byte (LSB)\n\n  return uint8Array\n}\n\nexport type ProfilePointer = {\n  pubkey: string // hex\n  relays?: string[]\n}\n\nexport type EventPointer = {\n  id: string // hex\n  relays?: string[]\n  author?: string\n  kind?: number\n}\n\nexport type AddressPointer = {\n  identifier: string\n  pubkey: string\n  kind: number\n  relays?: string[]\n}\n\nexport function decodeNostrURI(nip19code: string): ReturnType<typeof decode> | { type: 'invalid'; data: null } {\n  try {\n    if (nip19code.startsWith('nostr:')) nip19code = nip19code.substring(6)\n    return decode(nip19code)\n  } catch (_err) {\n    return { type: 'invalid', data: null }\n  }\n}\n\nexport type DecodedNevent = {\n  type: 'nevent'\n  data: EventPointer\n}\n\nexport type DecodedNprofile = {\n  type: 'nprofile'\n  data: ProfilePointer\n}\n\nexport type DecodedNaddr = {\n  type: 'naddr'\n  data: AddressPointer\n}\n\nexport type DecodedNsec = {\n  type: 'nsec'\n  data: Uint8Array\n}\n\nexport type DecodedNpub = {\n  type: 'npub'\n  data: string\n}\n\nexport type DecodedNote = {\n  type: 'note'\n  data: string\n}\n\nexport type DecodedResult = DecodedNevent | DecodedNprofile | DecodedNaddr | DecodedNpub | DecodedNsec | DecodedNote\n\nexport function decode(nip19: NEvent): DecodedNevent\nexport function decode(nip19: NProfile): DecodedNprofile\nexport function decode(nip19: NAddr): DecodedNaddr\nexport function decode(nip19: NSec): DecodedNsec\nexport function decode(nip19: NPub): DecodedNpub\nexport function decode(nip19: Note): DecodedNote\nexport function decode(code: string): DecodedResult\nexport function decode(code: string): DecodedResult {\n  let { prefix, words } = bech32.decode(code, Bech32MaxSize)\n  let data = new Uint8Array(bech32.fromWords(words))\n\n  switch (prefix) {\n    case 'nprofile': {\n      let tlv = parseTLV(data)\n      if (!tlv[0]?.[0]) throw new Error('missing TLV 0 for nprofile')\n      if (tlv[0][0].length !== 32) throw new Error('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      let tlv = parseTLV(data)\n      if (!tlv[0]?.[0]) throw new Error('missing TLV 0 for nevent')\n      if (tlv[0][0].length !== 32) throw new Error('TLV 0 should be 32 bytes')\n      if (tlv[2] && tlv[2][0].length !== 32) throw new Error('TLV 2 should be 32 bytes')\n      if (tlv[3] && tlv[3][0].length !== 4) throw new Error('TLV 3 should be 4 bytes')\n\n      return {\n        type: 'nevent',\n        data: {\n          id: bytesToHex(tlv[0][0]),\n          relays: tlv[1] ? tlv[1].map(d => utf8Decoder.decode(d)) : [],\n          author: tlv[2]?.[0] ? bytesToHex(tlv[2][0]) : undefined,\n          kind: tlv[3]?.[0] ? parseInt(bytesToHex(tlv[3][0]), 16) : undefined,\n        },\n      }\n    }\n\n    case 'naddr': {\n      let tlv = parseTLV(data)\n      if (!tlv[0]?.[0]) throw new Error('missing TLV 0 for naddr')\n      if (!tlv[2]?.[0]) throw new Error('missing TLV 2 for naddr')\n      if (tlv[2][0].length !== 32) throw new Error('TLV 2 should be 32 bytes')\n      if (!tlv[3]?.[0]) throw new Error('missing TLV 3 for naddr')\n      if (tlv[3][0].length !== 4) throw new Error('TLV 3 should be 4 bytes')\n\n      return {\n        type: 'naddr',\n        data: {\n          identifier: utf8Decoder.decode(tlv[0][0]),\n          pubkey: bytesToHex(tlv[2][0]),\n          kind: parseInt(bytesToHex(tlv[3][0]), 16),\n          relays: tlv[1] ? tlv[1].map(d => utf8Decoder.decode(d)) : [],\n        },\n      }\n    }\n\n    case 'nsec':\n      return { type: prefix, data }\n\n    case 'npub':\n    case 'note':\n      return { type: prefix, data: bytesToHex(data) }\n\n    default:\n      throw new Error(`unknown prefix ${prefix}`)\n  }\n}\n\ntype TLV = { [t: number]: Uint8Array[] }\n\nfunction parseTLV(data: Uint8Array): TLV {\n  let result: TLV = {}\n  let rest = data\n  while (rest.length > 0) {\n    let t = rest[0]\n    let l = rest[1]\n    let v = rest.slice(2, 2 + l)\n    rest = rest.slice(2 + l)\n    if (v.length < l) throw new Error(`not enough data to read on TLV ${t}`)\n    result[t] = result[t] || []\n    result[t].push(v)\n  }\n  return result\n}\n\nexport function nsecEncode(key: Uint8Array): NSec {\n  return encodeBytes('nsec', key)\n}\n\nexport function npubEncode(hex: string): NPub {\n  return encodeBytes('npub', hexToBytes(hex))\n}\n\nexport function noteEncode(hex: string): Note {\n  return encodeBytes('note', hexToBytes(hex))\n}\n\nfunction encodeBech32<Prefix extends string>(prefix: Prefix, data: Uint8Array): `${Prefix}1${string}` {\n  let words = bech32.toWords(data)\n  return bech32.encode(prefix, words, Bech32MaxSize) as `${Prefix}1${string}`\n}\n\nexport function encodeBytes<Prefix extends string>(prefix: Prefix, bytes: Uint8Array): `${Prefix}1${string}` {\n  return encodeBech32(prefix, bytes)\n}\n\nexport function nprofileEncode(profile: ProfilePointer): NProfile {\n  let data = encodeTLV({\n    0: [hexToBytes(profile.pubkey)],\n    1: (profile.relays || []).map(url => utf8Encoder.encode(url)),\n  })\n  return encodeBech32('nprofile', data)\n}\n\nexport function neventEncode(event: EventPointer): NEvent {\n  let kindArray\n  if (event.kind !== undefined) {\n    kindArray = integerToUint8Array(event.kind)\n  }\n\n  let data = encodeTLV({\n    0: [hexToBytes(event.id)],\n    1: (event.relays || []).map(url => utf8Encoder.encode(url)),\n    2: event.author ? [hexToBytes(event.author)] : [],\n    3: kindArray ? [new Uint8Array(kindArray)] : [],\n  })\n\n  return encodeBech32('nevent', data)\n}\n\nexport function naddrEncode(addr: AddressPointer): NAddr {\n  let kind = new ArrayBuffer(4)\n  new DataView(kind).setUint32(0, addr.kind, false)\n\n  let data = encodeTLV({\n    0: [utf8Encoder.encode(addr.identifier)],\n    1: (addr.relays || []).map(url => utf8Encoder.encode(url)),\n    2: [hexToBytes(addr.pubkey)],\n    3: [new Uint8Array(kind)],\n  })\n  return encodeBech32('naddr', data)\n}\n\nfunction encodeTLV(tlv: TLV): Uint8Array {\n  let entries: Uint8Array[] = []\n\n  Object.entries(tlv)\n    .reverse()\n    .forEach(([t, vs]) => {\n      vs.forEach(v => {\n        let entry = new Uint8Array(v.length + 2)\n        entry.set([parseInt(t)], 0)\n        entry.set([v.length], 1)\n        entry.set(v, 2)\n        entries.push(entry)\n      })\n    })\n\n  return concatBytes(...entries)\n}\n", "import type { Event } from './core.ts'\n\nexport const utf8Decoder: TextDecoder = new TextDecoder('utf-8')\nexport const utf8Encoder: TextEncoder = new TextEncoder()\n\nexport { bytesToHex, hexToBytes } from '@noble/hashes/utils'\n\nexport function normalizeURL(url: string): string {\n  try {\n    if (url.indexOf('://') === -1) url = 'wss://' + url\n    let p = new URL(url)\n    if (p.protocol === 'http:') p.protocol = 'ws:'\n    else if (p.protocol === 'https:') p.protocol = 'wss:'\n    p.pathname = p.pathname.replace(/\\/+/g, '/')\n    if (p.pathname.endsWith('/')) p.pathname = p.pathname.slice(0, -1)\n    if ((p.port === '80' && p.protocol === 'ws:') || (p.port === '443' && p.protocol === 'wss:')) p.port = ''\n    p.searchParams.sort()\n    p.hash = ''\n    return p.toString()\n  } catch (e) {\n    throw new Error(`Invalid URL: ${url}`)\n  }\n}\n\nexport function insertEventIntoDescendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return b.created_at - event.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function insertEventIntoAscendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return event.created_at - b.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function binarySearch<T>(arr: T[], compare: (b: T) => number): [number, boolean] {\n  let start = 0\n  let end = arr.length - 1\n\n  while (start <= end) {\n    const mid = Math.floor((start + end) / 2)\n    const cmp = compare(arr[mid])\n\n    if (cmp === 0) {\n      return [mid, true]\n    }\n\n    if (cmp < 0) {\n      end = mid - 1\n    } else {\n      start = mid + 1\n    }\n  }\n\n  return [start, false]\n}\n\nexport class QueueNode<V> {\n  public value: V\n  public next: QueueNode<V> | null = null\n  public prev: QueueNode<V> | null = null\n\n  constructor(message: V) {\n    this.value = message\n  }\n}\n\nexport class Queue<V> {\n  public first: QueueNode<V> | null\n  public last: QueueNode<V> | null\n\n  constructor() {\n    this.first = null\n    this.last = null\n  }\n\n  enqueue(value: V): boolean {\n    const newNode = new QueueNode(value)\n    if (!this.last) {\n      // list is empty\n      this.first = newNode\n      this.last = newNode\n    } else if (this.last === this.first) {\n      // list has a single element\n      this.last = newNode\n      this.last.prev = this.first\n      this.first.next = newNode\n    } else {\n      // list has elements, add as last\n      newNode.prev = this.last\n      this.last.next = newNode\n      this.last = newNode\n    }\n    return true\n  }\n\n  dequeue(): V | null {\n    if (!this.first) return null\n\n    if (this.first === this.last) {\n      const target = this.first\n      this.first = null\n      this.last = null\n      return target.value\n    }\n\n    const target = this.first\n    this.first = target.next\n    if (this.first) {\n      this.first.prev = null // fix: clean up prev pointer\n    }\n\n    return target.value\n  }\n}\n"],
-  "mappings": "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",
+  "sourcesContent": ["import { bytesToHex, concatBytes, hexToBytes } from '@noble/hashes/utils.js'\nimport { bech32 } from '@scure/base'\n\nimport { utf8Decoder, utf8Encoder } from './utils.ts'\n\nexport type NProfile = `nprofile1${string}`\nexport type NEvent = `nevent1${string}`\nexport type NAddr = `naddr1${string}`\nexport type NSec = `nsec1${string}`\nexport type NPub = `npub1${string}`\nexport type Note = `note1${string}`\nexport type Ncryptsec = `ncryptsec1${string}`\n\nexport const NostrTypeGuard = {\n  isNProfile: (value?: string | null): value is NProfile => /^nprofile1[a-z\\d]+$/.test(value || ''),\n  isNEvent: (value?: string | null): value is NEvent => /^nevent1[a-z\\d]+$/.test(value || ''),\n  isNAddr: (value?: string | null): value is NAddr => /^naddr1[a-z\\d]+$/.test(value || ''),\n  isNSec: (value?: string | null): value is NSec => /^nsec1[a-z\\d]{58}$/.test(value || ''),\n  isNPub: (value?: string | null): value is NPub => /^npub1[a-z\\d]{58}$/.test(value || ''),\n  isNote: (value?: string | null): value is Note => /^note1[a-z\\d]+$/.test(value || ''),\n  isNcryptsec: (value?: string | null): value is Ncryptsec => /^ncryptsec1[a-z\\d]+$/.test(value || ''),\n}\n\nexport const Bech32MaxSize = 5000\n\n/**\n * Bech32 regex.\n * @see https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki#bech32\n */\nexport const BECH32_REGEX = /[\\x21-\\x7E]{1,83}1[023456789acdefghjklmnpqrstuvwxyz]{6,}/\n\nfunction integerToUint8Array(number: number) {\n  // Create a Uint8Array with enough space to hold a 32-bit integer (4 bytes).\n  const uint8Array = new Uint8Array(4)\n\n  // Use bitwise operations to extract the bytes.\n  uint8Array[0] = (number >> 24) & 0xff // Most significant byte (MSB)\n  uint8Array[1] = (number >> 16) & 0xff\n  uint8Array[2] = (number >> 8) & 0xff\n  uint8Array[3] = number & 0xff // Least significant byte (LSB)\n\n  return uint8Array\n}\n\nexport type ProfilePointer = {\n  pubkey: string // hex\n  relays?: string[]\n}\n\nexport type EventPointer = {\n  id: string // hex\n  relays?: string[]\n  author?: string\n  kind?: number\n}\n\nexport type AddressPointer = {\n  identifier: string\n  pubkey: string\n  kind: number\n  relays?: string[]\n}\n\nexport function decodeNostrURI(nip19code: string): ReturnType<typeof decode> | { type: 'invalid'; data: null } {\n  try {\n    if (nip19code.startsWith('nostr:')) nip19code = nip19code.substring(6)\n    return decode(nip19code)\n  } catch (_err) {\n    return { type: 'invalid', data: null }\n  }\n}\n\nexport type DecodedNevent = {\n  type: 'nevent'\n  data: EventPointer\n}\n\nexport type DecodedNprofile = {\n  type: 'nprofile'\n  data: ProfilePointer\n}\n\nexport type DecodedNaddr = {\n  type: 'naddr'\n  data: AddressPointer\n}\n\nexport type DecodedNsec = {\n  type: 'nsec'\n  data: Uint8Array\n}\n\nexport type DecodedNpub = {\n  type: 'npub'\n  data: string\n}\n\nexport type DecodedNote = {\n  type: 'note'\n  data: string\n}\n\nexport type DecodedResult = DecodedNevent | DecodedNprofile | DecodedNaddr | DecodedNpub | DecodedNsec | DecodedNote\n\nexport function decode(nip19: NEvent): DecodedNevent\nexport function decode(nip19: NProfile): DecodedNprofile\nexport function decode(nip19: NAddr): DecodedNaddr\nexport function decode(nip19: NSec): DecodedNsec\nexport function decode(nip19: NPub): DecodedNpub\nexport function decode(nip19: Note): DecodedNote\nexport function decode(code: string): DecodedResult\nexport function decode(code: string): DecodedResult {\n  let { prefix, words } = bech32.decode(code as `${string}1${string}`, Bech32MaxSize)\n  let data = new Uint8Array(bech32.fromWords(words))\n\n  switch (prefix) {\n    case 'nprofile': {\n      let tlv = parseTLV(data)\n      if (!tlv[0]?.[0]) throw new Error('missing TLV 0 for nprofile')\n      if (tlv[0][0].length !== 32) throw new Error('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      let tlv = parseTLV(data)\n      if (!tlv[0]?.[0]) throw new Error('missing TLV 0 for nevent')\n      if (tlv[0][0].length !== 32) throw new Error('TLV 0 should be 32 bytes')\n      if (tlv[2] && tlv[2][0].length !== 32) throw new Error('TLV 2 should be 32 bytes')\n      if (tlv[3] && tlv[3][0].length !== 4) throw new Error('TLV 3 should be 4 bytes')\n\n      return {\n        type: 'nevent',\n        data: {\n          id: bytesToHex(tlv[0][0]),\n          relays: tlv[1] ? tlv[1].map(d => utf8Decoder.decode(d)) : [],\n          author: tlv[2]?.[0] ? bytesToHex(tlv[2][0]) : undefined,\n          kind: tlv[3]?.[0] ? parseInt(bytesToHex(tlv[3][0]), 16) : undefined,\n        },\n      }\n    }\n\n    case 'naddr': {\n      let tlv = parseTLV(data)\n      if (!tlv[0]?.[0]) throw new Error('missing TLV 0 for naddr')\n      if (!tlv[2]?.[0]) throw new Error('missing TLV 2 for naddr')\n      if (tlv[2][0].length !== 32) throw new Error('TLV 2 should be 32 bytes')\n      if (!tlv[3]?.[0]) throw new Error('missing TLV 3 for naddr')\n      if (tlv[3][0].length !== 4) throw new Error('TLV 3 should be 4 bytes')\n\n      return {\n        type: 'naddr',\n        data: {\n          identifier: utf8Decoder.decode(tlv[0][0]),\n          pubkey: bytesToHex(tlv[2][0]),\n          kind: parseInt(bytesToHex(tlv[3][0]), 16),\n          relays: tlv[1] ? tlv[1].map(d => utf8Decoder.decode(d)) : [],\n        },\n      }\n    }\n\n    case 'nsec':\n      return { type: prefix, data }\n\n    case 'npub':\n    case 'note':\n      return { type: prefix, data: bytesToHex(data) }\n\n    default:\n      throw new Error(`unknown prefix ${prefix}`)\n  }\n}\n\ntype TLV = { [t: number]: Uint8Array[] }\n\nfunction parseTLV(data: Uint8Array): TLV {\n  let result: TLV = {}\n  let rest = data\n  while (rest.length > 0) {\n    let t = rest[0]\n    let l = rest[1]\n    let v = rest.slice(2, 2 + l)\n    rest = rest.slice(2 + l)\n    if (v.length < l) throw new Error(`not enough data to read on TLV ${t}`)\n    result[t] = result[t] || []\n    result[t].push(v)\n  }\n  return result\n}\n\nexport function nsecEncode(key: Uint8Array): NSec {\n  return encodeBytes('nsec', key)\n}\n\nexport function npubEncode(hex: string): NPub {\n  return encodeBytes('npub', hexToBytes(hex))\n}\n\nexport function noteEncode(hex: string): Note {\n  return encodeBytes('note', hexToBytes(hex))\n}\n\nfunction encodeBech32<Prefix extends string>(prefix: Prefix, data: Uint8Array): `${Prefix}1${string}` {\n  let words = bech32.toWords(data)\n  return bech32.encode(prefix, words, Bech32MaxSize) as `${Prefix}1${string}`\n}\n\nexport function encodeBytes<Prefix extends string>(prefix: Prefix, bytes: Uint8Array): `${Prefix}1${string}` {\n  return encodeBech32(prefix, bytes)\n}\n\nexport function nprofileEncode(profile: ProfilePointer): NProfile {\n  let data = encodeTLV({\n    0: [hexToBytes(profile.pubkey)],\n    1: (profile.relays || []).map(url => utf8Encoder.encode(url)),\n  })\n  return encodeBech32('nprofile', data)\n}\n\nexport function neventEncode(event: EventPointer): NEvent {\n  let kindArray\n  if (event.kind !== undefined) {\n    kindArray = integerToUint8Array(event.kind)\n  }\n\n  let data = encodeTLV({\n    0: [hexToBytes(event.id)],\n    1: (event.relays || []).map(url => utf8Encoder.encode(url)),\n    2: event.author ? [hexToBytes(event.author)] : [],\n    3: kindArray ? [new Uint8Array(kindArray)] : [],\n  })\n\n  return encodeBech32('nevent', data)\n}\n\nexport function naddrEncode(addr: AddressPointer): NAddr {\n  let kind = new ArrayBuffer(4)\n  new DataView(kind).setUint32(0, addr.kind, false)\n\n  let data = encodeTLV({\n    0: [utf8Encoder.encode(addr.identifier)],\n    1: (addr.relays || []).map(url => utf8Encoder.encode(url)),\n    2: [hexToBytes(addr.pubkey)],\n    3: [new Uint8Array(kind)],\n  })\n  return encodeBech32('naddr', data)\n}\n\nfunction encodeTLV(tlv: TLV): Uint8Array {\n  let entries: Uint8Array[] = []\n\n  Object.entries(tlv)\n    .reverse()\n    .forEach(([t, vs]) => {\n      vs.forEach(v => {\n        let entry = new Uint8Array(v.length + 2)\n        entry.set([parseInt(t)], 0)\n        entry.set([v.length], 1)\n        entry.set(v, 2)\n        entries.push(entry)\n      })\n    })\n\n  return concatBytes(...entries)\n}\n", "import type { Event } from './core.ts'\n\nexport const utf8Decoder: TextDecoder = new TextDecoder('utf-8')\nexport const utf8Encoder: TextEncoder = new TextEncoder()\n\nexport { bytesToHex, hexToBytes } from '@noble/hashes/utils.js'\n\nexport function normalizeURL(url: string): string {\n  try {\n    if (url.indexOf('://') === -1) url = 'wss://' + url\n    let p = new URL(url)\n    if (p.protocol === 'http:') p.protocol = 'ws:'\n    else if (p.protocol === 'https:') p.protocol = 'wss:'\n    p.pathname = p.pathname.replace(/\\/+/g, '/')\n    if (p.pathname.endsWith('/')) p.pathname = p.pathname.slice(0, -1)\n    if ((p.port === '80' && p.protocol === 'ws:') || (p.port === '443' && p.protocol === 'wss:')) p.port = ''\n    p.searchParams.sort()\n    p.hash = ''\n    return p.toString()\n  } catch (e) {\n    throw new Error(`Invalid URL: ${url}`)\n  }\n}\n\nexport function insertEventIntoDescendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return b.created_at - event.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function insertEventIntoAscendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return event.created_at - b.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function binarySearch<T>(arr: T[], compare: (b: T) => number): [number, boolean] {\n  let start = 0\n  let end = arr.length - 1\n\n  while (start <= end) {\n    const mid = Math.floor((start + end) / 2)\n    const cmp = compare(arr[mid])\n\n    if (cmp === 0) {\n      return [mid, true]\n    }\n\n    if (cmp < 0) {\n      end = mid - 1\n    } else {\n      start = mid + 1\n    }\n  }\n\n  return [start, false]\n}\n\nexport class QueueNode<V> {\n  public value: V\n  public next: QueueNode<V> | null = null\n  public prev: QueueNode<V> | null = null\n\n  constructor(message: V) {\n    this.value = message\n  }\n}\n\nexport class Queue<V> {\n  public first: QueueNode<V> | null\n  public last: QueueNode<V> | null\n\n  constructor() {\n    this.first = null\n    this.last = null\n  }\n\n  enqueue(value: V): boolean {\n    const newNode = new QueueNode(value)\n    if (!this.last) {\n      // list is empty\n      this.first = newNode\n      this.last = newNode\n    } else if (this.last === this.first) {\n      // list has a single element\n      this.last = newNode\n      this.last.prev = this.first\n      this.first.next = newNode\n    } else {\n      // list has elements, add as last\n      newNode.prev = this.last\n      this.last.next = newNode\n      this.last = newNode\n    }\n    return true\n  }\n\n  dequeue(): V | null {\n    if (!this.first) return null\n\n    if (this.first === this.last) {\n      const target = this.first\n      this.first = null\n      this.last = null\n      return target.value\n    }\n\n    const target = this.first\n    this.first = target.next\n    if (this.first) {\n      this.first.prev = null // fix: clean up prev pointer\n    }\n\n    return target.value\n  }\n}\n"],
+  "mappings": "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",
   "names": ["bytesToHex", "hexToBytes", "bytesToHex", "hexToBytes"]
 }
Index: package/lib/cjs/nip21.js.map
===================================================================
--- package/lib/cjs/nip21.js.map
+++ package/lib/cjs/nip21.js.map
@@ -1,7 +1,7 @@
 {
   "version": 3,
   "sources": ["../../nip21.ts", "../../nip19.ts", "../../utils.ts"],
-  "sourcesContent": ["import { AddressPointer, BECH32_REGEX, decode, EventPointer, ProfilePointer } from './nip19.ts'\n\n/** Nostr URI regex, eg `nostr:npub1...` */\nexport const NOSTR_URI_REGEX: RegExp = new RegExp(`nostr:(${BECH32_REGEX.source})`)\n\n/** Test whether the value is a Nostr URI. */\nexport function test(value: unknown): value is `nostr:${string}` {\n  return typeof value === 'string' && new RegExp(`^${NOSTR_URI_REGEX.source}$`).test(value)\n}\n\n/** Parsed Nostr URI data. */\nexport interface NostrURI {\n  /** Full URI including the `nostr:` protocol. */\n  uri: `nostr:${string}`\n  /** The bech32-encoded data (eg `npub1...`). */\n  value: string\n  /** Decoded bech32 string, according to NIP-19. */\n  decoded:\n    | {\n        type: 'nevent'\n        data: EventPointer\n      }\n    | {\n        type: 'nprofile'\n        data: ProfilePointer\n      }\n    | {\n        type: 'naddr'\n        data: AddressPointer\n      }\n    | {\n        type: 'npub'\n        data: string\n      }\n    | {\n        type: 'nsec'\n        data: Uint8Array\n      }\n    | {\n        type: 'note'\n        data: string\n      }\n}\n\n/** Parse and decode a Nostr URI. */\nexport function parse(uri: string): NostrURI {\n  const match = uri.match(new RegExp(`^${NOSTR_URI_REGEX.source}$`))\n  if (!match) throw new Error(`Invalid Nostr URI: ${uri}`)\n  return {\n    uri: match[0] as `nostr:${string}`,\n    value: match[1],\n    decoded: decode(match[1]),\n  }\n}\n", "import { bytesToHex, concatBytes, hexToBytes } from '@noble/hashes/utils'\nimport { bech32 } from '@scure/base'\n\nimport { utf8Decoder, utf8Encoder } from './utils.ts'\n\nexport type NProfile = `nprofile1${string}`\nexport type NEvent = `nevent1${string}`\nexport type NAddr = `naddr1${string}`\nexport type NSec = `nsec1${string}`\nexport type NPub = `npub1${string}`\nexport type Note = `note1${string}`\nexport type Ncryptsec = `ncryptsec1${string}`\n\nexport const NostrTypeGuard = {\n  isNProfile: (value?: string | null): value is NProfile => /^nprofile1[a-z\\d]+$/.test(value || ''),\n  isNEvent: (value?: string | null): value is NEvent => /^nevent1[a-z\\d]+$/.test(value || ''),\n  isNAddr: (value?: string | null): value is NAddr => /^naddr1[a-z\\d]+$/.test(value || ''),\n  isNSec: (value?: string | null): value is NSec => /^nsec1[a-z\\d]{58}$/.test(value || ''),\n  isNPub: (value?: string | null): value is NPub => /^npub1[a-z\\d]{58}$/.test(value || ''),\n  isNote: (value?: string | null): value is Note => /^note1[a-z\\d]+$/.test(value || ''),\n  isNcryptsec: (value?: string | null): value is Ncryptsec => /^ncryptsec1[a-z\\d]+$/.test(value || ''),\n}\n\nexport const Bech32MaxSize = 5000\n\n/**\n * Bech32 regex.\n * @see https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki#bech32\n */\nexport const BECH32_REGEX = /[\\x21-\\x7E]{1,83}1[023456789acdefghjklmnpqrstuvwxyz]{6,}/\n\nfunction integerToUint8Array(number: number) {\n  // Create a Uint8Array with enough space to hold a 32-bit integer (4 bytes).\n  const uint8Array = new Uint8Array(4)\n\n  // Use bitwise operations to extract the bytes.\n  uint8Array[0] = (number >> 24) & 0xff // Most significant byte (MSB)\n  uint8Array[1] = (number >> 16) & 0xff\n  uint8Array[2] = (number >> 8) & 0xff\n  uint8Array[3] = number & 0xff // Least significant byte (LSB)\n\n  return uint8Array\n}\n\nexport type ProfilePointer = {\n  pubkey: string // hex\n  relays?: string[]\n}\n\nexport type EventPointer = {\n  id: string // hex\n  relays?: string[]\n  author?: string\n  kind?: number\n}\n\nexport type AddressPointer = {\n  identifier: string\n  pubkey: string\n  kind: number\n  relays?: string[]\n}\n\nexport function decodeNostrURI(nip19code: string): ReturnType<typeof decode> | { type: 'invalid'; data: null } {\n  try {\n    if (nip19code.startsWith('nostr:')) nip19code = nip19code.substring(6)\n    return decode(nip19code)\n  } catch (_err) {\n    return { type: 'invalid', data: null }\n  }\n}\n\nexport type DecodedNevent = {\n  type: 'nevent'\n  data: EventPointer\n}\n\nexport type DecodedNprofile = {\n  type: 'nprofile'\n  data: ProfilePointer\n}\n\nexport type DecodedNaddr = {\n  type: 'naddr'\n  data: AddressPointer\n}\n\nexport type DecodedNsec = {\n  type: 'nsec'\n  data: Uint8Array\n}\n\nexport type DecodedNpub = {\n  type: 'npub'\n  data: string\n}\n\nexport type DecodedNote = {\n  type: 'note'\n  data: string\n}\n\nexport type DecodedResult = DecodedNevent | DecodedNprofile | DecodedNaddr | DecodedNpub | DecodedNsec | DecodedNote\n\nexport function decode(nip19: NEvent): DecodedNevent\nexport function decode(nip19: NProfile): DecodedNprofile\nexport function decode(nip19: NAddr): DecodedNaddr\nexport function decode(nip19: NSec): DecodedNsec\nexport function decode(nip19: NPub): DecodedNpub\nexport function decode(nip19: Note): DecodedNote\nexport function decode(code: string): DecodedResult\nexport function decode(code: string): DecodedResult {\n  let { prefix, words } = bech32.decode(code, Bech32MaxSize)\n  let data = new Uint8Array(bech32.fromWords(words))\n\n  switch (prefix) {\n    case 'nprofile': {\n      let tlv = parseTLV(data)\n      if (!tlv[0]?.[0]) throw new Error('missing TLV 0 for nprofile')\n      if (tlv[0][0].length !== 32) throw new Error('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      let tlv = parseTLV(data)\n      if (!tlv[0]?.[0]) throw new Error('missing TLV 0 for nevent')\n      if (tlv[0][0].length !== 32) throw new Error('TLV 0 should be 32 bytes')\n      if (tlv[2] && tlv[2][0].length !== 32) throw new Error('TLV 2 should be 32 bytes')\n      if (tlv[3] && tlv[3][0].length !== 4) throw new Error('TLV 3 should be 4 bytes')\n\n      return {\n        type: 'nevent',\n        data: {\n          id: bytesToHex(tlv[0][0]),\n          relays: tlv[1] ? tlv[1].map(d => utf8Decoder.decode(d)) : [],\n          author: tlv[2]?.[0] ? bytesToHex(tlv[2][0]) : undefined,\n          kind: tlv[3]?.[0] ? parseInt(bytesToHex(tlv[3][0]), 16) : undefined,\n        },\n      }\n    }\n\n    case 'naddr': {\n      let tlv = parseTLV(data)\n      if (!tlv[0]?.[0]) throw new Error('missing TLV 0 for naddr')\n      if (!tlv[2]?.[0]) throw new Error('missing TLV 2 for naddr')\n      if (tlv[2][0].length !== 32) throw new Error('TLV 2 should be 32 bytes')\n      if (!tlv[3]?.[0]) throw new Error('missing TLV 3 for naddr')\n      if (tlv[3][0].length !== 4) throw new Error('TLV 3 should be 4 bytes')\n\n      return {\n        type: 'naddr',\n        data: {\n          identifier: utf8Decoder.decode(tlv[0][0]),\n          pubkey: bytesToHex(tlv[2][0]),\n          kind: parseInt(bytesToHex(tlv[3][0]), 16),\n          relays: tlv[1] ? tlv[1].map(d => utf8Decoder.decode(d)) : [],\n        },\n      }\n    }\n\n    case 'nsec':\n      return { type: prefix, data }\n\n    case 'npub':\n    case 'note':\n      return { type: prefix, data: bytesToHex(data) }\n\n    default:\n      throw new Error(`unknown prefix ${prefix}`)\n  }\n}\n\ntype TLV = { [t: number]: Uint8Array[] }\n\nfunction parseTLV(data: Uint8Array): TLV {\n  let result: TLV = {}\n  let rest = data\n  while (rest.length > 0) {\n    let t = rest[0]\n    let l = rest[1]\n    let v = rest.slice(2, 2 + l)\n    rest = rest.slice(2 + l)\n    if (v.length < l) throw new Error(`not enough data to read on TLV ${t}`)\n    result[t] = result[t] || []\n    result[t].push(v)\n  }\n  return result\n}\n\nexport function nsecEncode(key: Uint8Array): NSec {\n  return encodeBytes('nsec', key)\n}\n\nexport function npubEncode(hex: string): NPub {\n  return encodeBytes('npub', hexToBytes(hex))\n}\n\nexport function noteEncode(hex: string): Note {\n  return encodeBytes('note', hexToBytes(hex))\n}\n\nfunction encodeBech32<Prefix extends string>(prefix: Prefix, data: Uint8Array): `${Prefix}1${string}` {\n  let words = bech32.toWords(data)\n  return bech32.encode(prefix, words, Bech32MaxSize) as `${Prefix}1${string}`\n}\n\nexport function encodeBytes<Prefix extends string>(prefix: Prefix, bytes: Uint8Array): `${Prefix}1${string}` {\n  return encodeBech32(prefix, bytes)\n}\n\nexport function nprofileEncode(profile: ProfilePointer): NProfile {\n  let data = encodeTLV({\n    0: [hexToBytes(profile.pubkey)],\n    1: (profile.relays || []).map(url => utf8Encoder.encode(url)),\n  })\n  return encodeBech32('nprofile', data)\n}\n\nexport function neventEncode(event: EventPointer): NEvent {\n  let kindArray\n  if (event.kind !== undefined) {\n    kindArray = integerToUint8Array(event.kind)\n  }\n\n  let data = encodeTLV({\n    0: [hexToBytes(event.id)],\n    1: (event.relays || []).map(url => utf8Encoder.encode(url)),\n    2: event.author ? [hexToBytes(event.author)] : [],\n    3: kindArray ? [new Uint8Array(kindArray)] : [],\n  })\n\n  return encodeBech32('nevent', data)\n}\n\nexport function naddrEncode(addr: AddressPointer): NAddr {\n  let kind = new ArrayBuffer(4)\n  new DataView(kind).setUint32(0, addr.kind, false)\n\n  let data = encodeTLV({\n    0: [utf8Encoder.encode(addr.identifier)],\n    1: (addr.relays || []).map(url => utf8Encoder.encode(url)),\n    2: [hexToBytes(addr.pubkey)],\n    3: [new Uint8Array(kind)],\n  })\n  return encodeBech32('naddr', data)\n}\n\nfunction encodeTLV(tlv: TLV): Uint8Array {\n  let entries: Uint8Array[] = []\n\n  Object.entries(tlv)\n    .reverse()\n    .forEach(([t, vs]) => {\n      vs.forEach(v => {\n        let entry = new Uint8Array(v.length + 2)\n        entry.set([parseInt(t)], 0)\n        entry.set([v.length], 1)\n        entry.set(v, 2)\n        entries.push(entry)\n      })\n    })\n\n  return concatBytes(...entries)\n}\n", "import type { Event } from './core.ts'\n\nexport const utf8Decoder: TextDecoder = new TextDecoder('utf-8')\nexport const utf8Encoder: TextEncoder = new TextEncoder()\n\nexport { bytesToHex, hexToBytes } from '@noble/hashes/utils'\n\nexport function normalizeURL(url: string): string {\n  try {\n    if (url.indexOf('://') === -1) url = 'wss://' + url\n    let p = new URL(url)\n    if (p.protocol === 'http:') p.protocol = 'ws:'\n    else if (p.protocol === 'https:') p.protocol = 'wss:'\n    p.pathname = p.pathname.replace(/\\/+/g, '/')\n    if (p.pathname.endsWith('/')) p.pathname = p.pathname.slice(0, -1)\n    if ((p.port === '80' && p.protocol === 'ws:') || (p.port === '443' && p.protocol === 'wss:')) p.port = ''\n    p.searchParams.sort()\n    p.hash = ''\n    return p.toString()\n  } catch (e) {\n    throw new Error(`Invalid URL: ${url}`)\n  }\n}\n\nexport function insertEventIntoDescendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return b.created_at - event.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function insertEventIntoAscendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return event.created_at - b.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function binarySearch<T>(arr: T[], compare: (b: T) => number): [number, boolean] {\n  let start = 0\n  let end = arr.length - 1\n\n  while (start <= end) {\n    const mid = Math.floor((start + end) / 2)\n    const cmp = compare(arr[mid])\n\n    if (cmp === 0) {\n      return [mid, true]\n    }\n\n    if (cmp < 0) {\n      end = mid - 1\n    } else {\n      start = mid + 1\n    }\n  }\n\n  return [start, false]\n}\n\nexport class QueueNode<V> {\n  public value: V\n  public next: QueueNode<V> | null = null\n  public prev: QueueNode<V> | null = null\n\n  constructor(message: V) {\n    this.value = message\n  }\n}\n\nexport class Queue<V> {\n  public first: QueueNode<V> | null\n  public last: QueueNode<V> | null\n\n  constructor() {\n    this.first = null\n    this.last = null\n  }\n\n  enqueue(value: V): boolean {\n    const newNode = new QueueNode(value)\n    if (!this.last) {\n      // list is empty\n      this.first = newNode\n      this.last = newNode\n    } else if (this.last === this.first) {\n      // list has a single element\n      this.last = newNode\n      this.last.prev = this.first\n      this.first.next = newNode\n    } else {\n      // list has elements, add as last\n      newNode.prev = this.last\n      this.last.next = newNode\n      this.last = newNode\n    }\n    return true\n  }\n\n  dequeue(): V | null {\n    if (!this.first) return null\n\n    if (this.first === this.last) {\n      const target = this.first\n      this.first = null\n      this.last = null\n      return target.value\n    }\n\n    const target = this.first\n    this.first = target.next\n    if (this.first) {\n      this.first.prev = null // fix: clean up prev pointer\n    }\n\n    return target.value\n  }\n}\n"],
-  "mappings": "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",
+  "sourcesContent": ["import { AddressPointer, BECH32_REGEX, decode, EventPointer, ProfilePointer } from './nip19.ts'\n\n/** Nostr URI regex, eg `nostr:npub1...` */\nexport const NOSTR_URI_REGEX: RegExp = new RegExp(`nostr:(${BECH32_REGEX.source})`)\n\n/** Test whether the value is a Nostr URI. */\nexport function test(value: unknown): value is `nostr:${string}` {\n  return typeof value === 'string' && new RegExp(`^${NOSTR_URI_REGEX.source}$`).test(value)\n}\n\n/** Parsed Nostr URI data. */\nexport interface NostrURI {\n  /** Full URI including the `nostr:` protocol. */\n  uri: `nostr:${string}`\n  /** The bech32-encoded data (eg `npub1...`). */\n  value: string\n  /** Decoded bech32 string, according to NIP-19. */\n  decoded:\n    | {\n        type: 'nevent'\n        data: EventPointer\n      }\n    | {\n        type: 'nprofile'\n        data: ProfilePointer\n      }\n    | {\n        type: 'naddr'\n        data: AddressPointer\n      }\n    | {\n        type: 'npub'\n        data: string\n      }\n    | {\n        type: 'nsec'\n        data: Uint8Array\n      }\n    | {\n        type: 'note'\n        data: string\n      }\n}\n\n/** Parse and decode a Nostr URI. */\nexport function parse(uri: string): NostrURI {\n  const match = uri.match(new RegExp(`^${NOSTR_URI_REGEX.source}$`))\n  if (!match) throw new Error(`Invalid Nostr URI: ${uri}`)\n  return {\n    uri: match[0] as `nostr:${string}`,\n    value: match[1],\n    decoded: decode(match[1]),\n  }\n}\n", "import { bytesToHex, concatBytes, hexToBytes } from '@noble/hashes/utils.js'\nimport { bech32 } from '@scure/base'\n\nimport { utf8Decoder, utf8Encoder } from './utils.ts'\n\nexport type NProfile = `nprofile1${string}`\nexport type NEvent = `nevent1${string}`\nexport type NAddr = `naddr1${string}`\nexport type NSec = `nsec1${string}`\nexport type NPub = `npub1${string}`\nexport type Note = `note1${string}`\nexport type Ncryptsec = `ncryptsec1${string}`\n\nexport const NostrTypeGuard = {\n  isNProfile: (value?: string | null): value is NProfile => /^nprofile1[a-z\\d]+$/.test(value || ''),\n  isNEvent: (value?: string | null): value is NEvent => /^nevent1[a-z\\d]+$/.test(value || ''),\n  isNAddr: (value?: string | null): value is NAddr => /^naddr1[a-z\\d]+$/.test(value || ''),\n  isNSec: (value?: string | null): value is NSec => /^nsec1[a-z\\d]{58}$/.test(value || ''),\n  isNPub: (value?: string | null): value is NPub => /^npub1[a-z\\d]{58}$/.test(value || ''),\n  isNote: (value?: string | null): value is Note => /^note1[a-z\\d]+$/.test(value || ''),\n  isNcryptsec: (value?: string | null): value is Ncryptsec => /^ncryptsec1[a-z\\d]+$/.test(value || ''),\n}\n\nexport const Bech32MaxSize = 5000\n\n/**\n * Bech32 regex.\n * @see https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki#bech32\n */\nexport const BECH32_REGEX = /[\\x21-\\x7E]{1,83}1[023456789acdefghjklmnpqrstuvwxyz]{6,}/\n\nfunction integerToUint8Array(number: number) {\n  // Create a Uint8Array with enough space to hold a 32-bit integer (4 bytes).\n  const uint8Array = new Uint8Array(4)\n\n  // Use bitwise operations to extract the bytes.\n  uint8Array[0] = (number >> 24) & 0xff // Most significant byte (MSB)\n  uint8Array[1] = (number >> 16) & 0xff\n  uint8Array[2] = (number >> 8) & 0xff\n  uint8Array[3] = number & 0xff // Least significant byte (LSB)\n\n  return uint8Array\n}\n\nexport type ProfilePointer = {\n  pubkey: string // hex\n  relays?: string[]\n}\n\nexport type EventPointer = {\n  id: string // hex\n  relays?: string[]\n  author?: string\n  kind?: number\n}\n\nexport type AddressPointer = {\n  identifier: string\n  pubkey: string\n  kind: number\n  relays?: string[]\n}\n\nexport function decodeNostrURI(nip19code: string): ReturnType<typeof decode> | { type: 'invalid'; data: null } {\n  try {\n    if (nip19code.startsWith('nostr:')) nip19code = nip19code.substring(6)\n    return decode(nip19code)\n  } catch (_err) {\n    return { type: 'invalid', data: null }\n  }\n}\n\nexport type DecodedNevent = {\n  type: 'nevent'\n  data: EventPointer\n}\n\nexport type DecodedNprofile = {\n  type: 'nprofile'\n  data: ProfilePointer\n}\n\nexport type DecodedNaddr = {\n  type: 'naddr'\n  data: AddressPointer\n}\n\nexport type DecodedNsec = {\n  type: 'nsec'\n  data: Uint8Array\n}\n\nexport type DecodedNpub = {\n  type: 'npub'\n  data: string\n}\n\nexport type DecodedNote = {\n  type: 'note'\n  data: string\n}\n\nexport type DecodedResult = DecodedNevent | DecodedNprofile | DecodedNaddr | DecodedNpub | DecodedNsec | DecodedNote\n\nexport function decode(nip19: NEvent): DecodedNevent\nexport function decode(nip19: NProfile): DecodedNprofile\nexport function decode(nip19: NAddr): DecodedNaddr\nexport function decode(nip19: NSec): DecodedNsec\nexport function decode(nip19: NPub): DecodedNpub\nexport function decode(nip19: Note): DecodedNote\nexport function decode(code: string): DecodedResult\nexport function decode(code: string): DecodedResult {\n  let { prefix, words } = bech32.decode(code as `${string}1${string}`, Bech32MaxSize)\n  let data = new Uint8Array(bech32.fromWords(words))\n\n  switch (prefix) {\n    case 'nprofile': {\n      let tlv = parseTLV(data)\n      if (!tlv[0]?.[0]) throw new Error('missing TLV 0 for nprofile')\n      if (tlv[0][0].length !== 32) throw new Error('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      let tlv = parseTLV(data)\n      if (!tlv[0]?.[0]) throw new Error('missing TLV 0 for nevent')\n      if (tlv[0][0].length !== 32) throw new Error('TLV 0 should be 32 bytes')\n      if (tlv[2] && tlv[2][0].length !== 32) throw new Error('TLV 2 should be 32 bytes')\n      if (tlv[3] && tlv[3][0].length !== 4) throw new Error('TLV 3 should be 4 bytes')\n\n      return {\n        type: 'nevent',\n        data: {\n          id: bytesToHex(tlv[0][0]),\n          relays: tlv[1] ? tlv[1].map(d => utf8Decoder.decode(d)) : [],\n          author: tlv[2]?.[0] ? bytesToHex(tlv[2][0]) : undefined,\n          kind: tlv[3]?.[0] ? parseInt(bytesToHex(tlv[3][0]), 16) : undefined,\n        },\n      }\n    }\n\n    case 'naddr': {\n      let tlv = parseTLV(data)\n      if (!tlv[0]?.[0]) throw new Error('missing TLV 0 for naddr')\n      if (!tlv[2]?.[0]) throw new Error('missing TLV 2 for naddr')\n      if (tlv[2][0].length !== 32) throw new Error('TLV 2 should be 32 bytes')\n      if (!tlv[3]?.[0]) throw new Error('missing TLV 3 for naddr')\n      if (tlv[3][0].length !== 4) throw new Error('TLV 3 should be 4 bytes')\n\n      return {\n        type: 'naddr',\n        data: {\n          identifier: utf8Decoder.decode(tlv[0][0]),\n          pubkey: bytesToHex(tlv[2][0]),\n          kind: parseInt(bytesToHex(tlv[3][0]), 16),\n          relays: tlv[1] ? tlv[1].map(d => utf8Decoder.decode(d)) : [],\n        },\n      }\n    }\n\n    case 'nsec':\n      return { type: prefix, data }\n\n    case 'npub':\n    case 'note':\n      return { type: prefix, data: bytesToHex(data) }\n\n    default:\n      throw new Error(`unknown prefix ${prefix}`)\n  }\n}\n\ntype TLV = { [t: number]: Uint8Array[] }\n\nfunction parseTLV(data: Uint8Array): TLV {\n  let result: TLV = {}\n  let rest = data\n  while (rest.length > 0) {\n    let t = rest[0]\n    let l = rest[1]\n    let v = rest.slice(2, 2 + l)\n    rest = rest.slice(2 + l)\n    if (v.length < l) throw new Error(`not enough data to read on TLV ${t}`)\n    result[t] = result[t] || []\n    result[t].push(v)\n  }\n  return result\n}\n\nexport function nsecEncode(key: Uint8Array): NSec {\n  return encodeBytes('nsec', key)\n}\n\nexport function npubEncode(hex: string): NPub {\n  return encodeBytes('npub', hexToBytes(hex))\n}\n\nexport function noteEncode(hex: string): Note {\n  return encodeBytes('note', hexToBytes(hex))\n}\n\nfunction encodeBech32<Prefix extends string>(prefix: Prefix, data: Uint8Array): `${Prefix}1${string}` {\n  let words = bech32.toWords(data)\n  return bech32.encode(prefix, words, Bech32MaxSize) as `${Prefix}1${string}`\n}\n\nexport function encodeBytes<Prefix extends string>(prefix: Prefix, bytes: Uint8Array): `${Prefix}1${string}` {\n  return encodeBech32(prefix, bytes)\n}\n\nexport function nprofileEncode(profile: ProfilePointer): NProfile {\n  let data = encodeTLV({\n    0: [hexToBytes(profile.pubkey)],\n    1: (profile.relays || []).map(url => utf8Encoder.encode(url)),\n  })\n  return encodeBech32('nprofile', data)\n}\n\nexport function neventEncode(event: EventPointer): NEvent {\n  let kindArray\n  if (event.kind !== undefined) {\n    kindArray = integerToUint8Array(event.kind)\n  }\n\n  let data = encodeTLV({\n    0: [hexToBytes(event.id)],\n    1: (event.relays || []).map(url => utf8Encoder.encode(url)),\n    2: event.author ? [hexToBytes(event.author)] : [],\n    3: kindArray ? [new Uint8Array(kindArray)] : [],\n  })\n\n  return encodeBech32('nevent', data)\n}\n\nexport function naddrEncode(addr: AddressPointer): NAddr {\n  let kind = new ArrayBuffer(4)\n  new DataView(kind).setUint32(0, addr.kind, false)\n\n  let data = encodeTLV({\n    0: [utf8Encoder.encode(addr.identifier)],\n    1: (addr.relays || []).map(url => utf8Encoder.encode(url)),\n    2: [hexToBytes(addr.pubkey)],\n    3: [new Uint8Array(kind)],\n  })\n  return encodeBech32('naddr', data)\n}\n\nfunction encodeTLV(tlv: TLV): Uint8Array {\n  let entries: Uint8Array[] = []\n\n  Object.entries(tlv)\n    .reverse()\n    .forEach(([t, vs]) => {\n      vs.forEach(v => {\n        let entry = new Uint8Array(v.length + 2)\n        entry.set([parseInt(t)], 0)\n        entry.set([v.length], 1)\n        entry.set(v, 2)\n        entries.push(entry)\n      })\n    })\n\n  return concatBytes(...entries)\n}\n", "import type { Event } from './core.ts'\n\nexport const utf8Decoder: TextDecoder = new TextDecoder('utf-8')\nexport const utf8Encoder: TextEncoder = new TextEncoder()\n\nexport { bytesToHex, hexToBytes } from '@noble/hashes/utils.js'\n\nexport function normalizeURL(url: string): string {\n  try {\n    if (url.indexOf('://') === -1) url = 'wss://' + url\n    let p = new URL(url)\n    if (p.protocol === 'http:') p.protocol = 'ws:'\n    else if (p.protocol === 'https:') p.protocol = 'wss:'\n    p.pathname = p.pathname.replace(/\\/+/g, '/')\n    if (p.pathname.endsWith('/')) p.pathname = p.pathname.slice(0, -1)\n    if ((p.port === '80' && p.protocol === 'ws:') || (p.port === '443' && p.protocol === 'wss:')) p.port = ''\n    p.searchParams.sort()\n    p.hash = ''\n    return p.toString()\n  } catch (e) {\n    throw new Error(`Invalid URL: ${url}`)\n  }\n}\n\nexport function insertEventIntoDescendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return b.created_at - event.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function insertEventIntoAscendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return event.created_at - b.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function binarySearch<T>(arr: T[], compare: (b: T) => number): [number, boolean] {\n  let start = 0\n  let end = arr.length - 1\n\n  while (start <= end) {\n    const mid = Math.floor((start + end) / 2)\n    const cmp = compare(arr[mid])\n\n    if (cmp === 0) {\n      return [mid, true]\n    }\n\n    if (cmp < 0) {\n      end = mid - 1\n    } else {\n      start = mid + 1\n    }\n  }\n\n  return [start, false]\n}\n\nexport class QueueNode<V> {\n  public value: V\n  public next: QueueNode<V> | null = null\n  public prev: QueueNode<V> | null = null\n\n  constructor(message: V) {\n    this.value = message\n  }\n}\n\nexport class Queue<V> {\n  public first: QueueNode<V> | null\n  public last: QueueNode<V> | null\n\n  constructor() {\n    this.first = null\n    this.last = null\n  }\n\n  enqueue(value: V): boolean {\n    const newNode = new QueueNode(value)\n    if (!this.last) {\n      // list is empty\n      this.first = newNode\n      this.last = newNode\n    } else if (this.last === this.first) {\n      // list has a single element\n      this.last = newNode\n      this.last.prev = this.first\n      this.first.next = newNode\n    } else {\n      // list has elements, add as last\n      newNode.prev = this.last\n      this.last.next = newNode\n      this.last = newNode\n    }\n    return true\n  }\n\n  dequeue(): V | null {\n    if (!this.first) return null\n\n    if (this.first === this.last) {\n      const target = this.first\n      this.first = null\n      this.last = null\n      return target.value\n    }\n\n    const target = this.first\n    this.first = target.next\n    if (this.first) {\n      this.first.prev = null // fix: clean up prev pointer\n    }\n\n    return target.value\n  }\n}\n"],
+  "mappings": "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",
   "names": ["import_utils"]
 }
Index: package/lib/esm/nip21.js.map
===================================================================
--- package/lib/esm/nip21.js.map
+++ package/lib/esm/nip21.js.map
@@ -1,7 +1,7 @@
 {
   "version": 3,
   "sources": ["../../nip19.ts", "../../utils.ts", "../../nip21.ts"],
-  "sourcesContent": ["import { bytesToHex, concatBytes, hexToBytes } from '@noble/hashes/utils'\nimport { bech32 } from '@scure/base'\n\nimport { utf8Decoder, utf8Encoder } from './utils.ts'\n\nexport type NProfile = `nprofile1${string}`\nexport type NEvent = `nevent1${string}`\nexport type NAddr = `naddr1${string}`\nexport type NSec = `nsec1${string}`\nexport type NPub = `npub1${string}`\nexport type Note = `note1${string}`\nexport type Ncryptsec = `ncryptsec1${string}`\n\nexport const NostrTypeGuard = {\n  isNProfile: (value?: string | null): value is NProfile => /^nprofile1[a-z\\d]+$/.test(value || ''),\n  isNEvent: (value?: string | null): value is NEvent => /^nevent1[a-z\\d]+$/.test(value || ''),\n  isNAddr: (value?: string | null): value is NAddr => /^naddr1[a-z\\d]+$/.test(value || ''),\n  isNSec: (value?: string | null): value is NSec => /^nsec1[a-z\\d]{58}$/.test(value || ''),\n  isNPub: (value?: string | null): value is NPub => /^npub1[a-z\\d]{58}$/.test(value || ''),\n  isNote: (value?: string | null): value is Note => /^note1[a-z\\d]+$/.test(value || ''),\n  isNcryptsec: (value?: string | null): value is Ncryptsec => /^ncryptsec1[a-z\\d]+$/.test(value || ''),\n}\n\nexport const Bech32MaxSize = 5000\n\n/**\n * Bech32 regex.\n * @see https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki#bech32\n */\nexport const BECH32_REGEX = /[\\x21-\\x7E]{1,83}1[023456789acdefghjklmnpqrstuvwxyz]{6,}/\n\nfunction integerToUint8Array(number: number) {\n  // Create a Uint8Array with enough space to hold a 32-bit integer (4 bytes).\n  const uint8Array = new Uint8Array(4)\n\n  // Use bitwise operations to extract the bytes.\n  uint8Array[0] = (number >> 24) & 0xff // Most significant byte (MSB)\n  uint8Array[1] = (number >> 16) & 0xff\n  uint8Array[2] = (number >> 8) & 0xff\n  uint8Array[3] = number & 0xff // Least significant byte (LSB)\n\n  return uint8Array\n}\n\nexport type ProfilePointer = {\n  pubkey: string // hex\n  relays?: string[]\n}\n\nexport type EventPointer = {\n  id: string // hex\n  relays?: string[]\n  author?: string\n  kind?: number\n}\n\nexport type AddressPointer = {\n  identifier: string\n  pubkey: string\n  kind: number\n  relays?: string[]\n}\n\nexport function decodeNostrURI(nip19code: string): ReturnType<typeof decode> | { type: 'invalid'; data: null } {\n  try {\n    if (nip19code.startsWith('nostr:')) nip19code = nip19code.substring(6)\n    return decode(nip19code)\n  } catch (_err) {\n    return { type: 'invalid', data: null }\n  }\n}\n\nexport type DecodedNevent = {\n  type: 'nevent'\n  data: EventPointer\n}\n\nexport type DecodedNprofile = {\n  type: 'nprofile'\n  data: ProfilePointer\n}\n\nexport type DecodedNaddr = {\n  type: 'naddr'\n  data: AddressPointer\n}\n\nexport type DecodedNsec = {\n  type: 'nsec'\n  data: Uint8Array\n}\n\nexport type DecodedNpub = {\n  type: 'npub'\n  data: string\n}\n\nexport type DecodedNote = {\n  type: 'note'\n  data: string\n}\n\nexport type DecodedResult = DecodedNevent | DecodedNprofile | DecodedNaddr | DecodedNpub | DecodedNsec | DecodedNote\n\nexport function decode(nip19: NEvent): DecodedNevent\nexport function decode(nip19: NProfile): DecodedNprofile\nexport function decode(nip19: NAddr): DecodedNaddr\nexport function decode(nip19: NSec): DecodedNsec\nexport function decode(nip19: NPub): DecodedNpub\nexport function decode(nip19: Note): DecodedNote\nexport function decode(code: string): DecodedResult\nexport function decode(code: string): DecodedResult {\n  let { prefix, words } = bech32.decode(code, Bech32MaxSize)\n  let data = new Uint8Array(bech32.fromWords(words))\n\n  switch (prefix) {\n    case 'nprofile': {\n      let tlv = parseTLV(data)\n      if (!tlv[0]?.[0]) throw new Error('missing TLV 0 for nprofile')\n      if (tlv[0][0].length !== 32) throw new Error('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      let tlv = parseTLV(data)\n      if (!tlv[0]?.[0]) throw new Error('missing TLV 0 for nevent')\n      if (tlv[0][0].length !== 32) throw new Error('TLV 0 should be 32 bytes')\n      if (tlv[2] && tlv[2][0].length !== 32) throw new Error('TLV 2 should be 32 bytes')\n      if (tlv[3] && tlv[3][0].length !== 4) throw new Error('TLV 3 should be 4 bytes')\n\n      return {\n        type: 'nevent',\n        data: {\n          id: bytesToHex(tlv[0][0]),\n          relays: tlv[1] ? tlv[1].map(d => utf8Decoder.decode(d)) : [],\n          author: tlv[2]?.[0] ? bytesToHex(tlv[2][0]) : undefined,\n          kind: tlv[3]?.[0] ? parseInt(bytesToHex(tlv[3][0]), 16) : undefined,\n        },\n      }\n    }\n\n    case 'naddr': {\n      let tlv = parseTLV(data)\n      if (!tlv[0]?.[0]) throw new Error('missing TLV 0 for naddr')\n      if (!tlv[2]?.[0]) throw new Error('missing TLV 2 for naddr')\n      if (tlv[2][0].length !== 32) throw new Error('TLV 2 should be 32 bytes')\n      if (!tlv[3]?.[0]) throw new Error('missing TLV 3 for naddr')\n      if (tlv[3][0].length !== 4) throw new Error('TLV 3 should be 4 bytes')\n\n      return {\n        type: 'naddr',\n        data: {\n          identifier: utf8Decoder.decode(tlv[0][0]),\n          pubkey: bytesToHex(tlv[2][0]),\n          kind: parseInt(bytesToHex(tlv[3][0]), 16),\n          relays: tlv[1] ? tlv[1].map(d => utf8Decoder.decode(d)) : [],\n        },\n      }\n    }\n\n    case 'nsec':\n      return { type: prefix, data }\n\n    case 'npub':\n    case 'note':\n      return { type: prefix, data: bytesToHex(data) }\n\n    default:\n      throw new Error(`unknown prefix ${prefix}`)\n  }\n}\n\ntype TLV = { [t: number]: Uint8Array[] }\n\nfunction parseTLV(data: Uint8Array): TLV {\n  let result: TLV = {}\n  let rest = data\n  while (rest.length > 0) {\n    let t = rest[0]\n    let l = rest[1]\n    let v = rest.slice(2, 2 + l)\n    rest = rest.slice(2 + l)\n    if (v.length < l) throw new Error(`not enough data to read on TLV ${t}`)\n    result[t] = result[t] || []\n    result[t].push(v)\n  }\n  return result\n}\n\nexport function nsecEncode(key: Uint8Array): NSec {\n  return encodeBytes('nsec', key)\n}\n\nexport function npubEncode(hex: string): NPub {\n  return encodeBytes('npub', hexToBytes(hex))\n}\n\nexport function noteEncode(hex: string): Note {\n  return encodeBytes('note', hexToBytes(hex))\n}\n\nfunction encodeBech32<Prefix extends string>(prefix: Prefix, data: Uint8Array): `${Prefix}1${string}` {\n  let words = bech32.toWords(data)\n  return bech32.encode(prefix, words, Bech32MaxSize) as `${Prefix}1${string}`\n}\n\nexport function encodeBytes<Prefix extends string>(prefix: Prefix, bytes: Uint8Array): `${Prefix}1${string}` {\n  return encodeBech32(prefix, bytes)\n}\n\nexport function nprofileEncode(profile: ProfilePointer): NProfile {\n  let data = encodeTLV({\n    0: [hexToBytes(profile.pubkey)],\n    1: (profile.relays || []).map(url => utf8Encoder.encode(url)),\n  })\n  return encodeBech32('nprofile', data)\n}\n\nexport function neventEncode(event: EventPointer): NEvent {\n  let kindArray\n  if (event.kind !== undefined) {\n    kindArray = integerToUint8Array(event.kind)\n  }\n\n  let data = encodeTLV({\n    0: [hexToBytes(event.id)],\n    1: (event.relays || []).map(url => utf8Encoder.encode(url)),\n    2: event.author ? [hexToBytes(event.author)] : [],\n    3: kindArray ? [new Uint8Array(kindArray)] : [],\n  })\n\n  return encodeBech32('nevent', data)\n}\n\nexport function naddrEncode(addr: AddressPointer): NAddr {\n  let kind = new ArrayBuffer(4)\n  new DataView(kind).setUint32(0, addr.kind, false)\n\n  let data = encodeTLV({\n    0: [utf8Encoder.encode(addr.identifier)],\n    1: (addr.relays || []).map(url => utf8Encoder.encode(url)),\n    2: [hexToBytes(addr.pubkey)],\n    3: [new Uint8Array(kind)],\n  })\n  return encodeBech32('naddr', data)\n}\n\nfunction encodeTLV(tlv: TLV): Uint8Array {\n  let entries: Uint8Array[] = []\n\n  Object.entries(tlv)\n    .reverse()\n    .forEach(([t, vs]) => {\n      vs.forEach(v => {\n        let entry = new Uint8Array(v.length + 2)\n        entry.set([parseInt(t)], 0)\n        entry.set([v.length], 1)\n        entry.set(v, 2)\n        entries.push(entry)\n      })\n    })\n\n  return concatBytes(...entries)\n}\n", "import type { Event } from './core.ts'\n\nexport const utf8Decoder: TextDecoder = new TextDecoder('utf-8')\nexport const utf8Encoder: TextEncoder = new TextEncoder()\n\nexport { bytesToHex, hexToBytes } from '@noble/hashes/utils'\n\nexport function normalizeURL(url: string): string {\n  try {\n    if (url.indexOf('://') === -1) url = 'wss://' + url\n    let p = new URL(url)\n    if (p.protocol === 'http:') p.protocol = 'ws:'\n    else if (p.protocol === 'https:') p.protocol = 'wss:'\n    p.pathname = p.pathname.replace(/\\/+/g, '/')\n    if (p.pathname.endsWith('/')) p.pathname = p.pathname.slice(0, -1)\n    if ((p.port === '80' && p.protocol === 'ws:') || (p.port === '443' && p.protocol === 'wss:')) p.port = ''\n    p.searchParams.sort()\n    p.hash = ''\n    return p.toString()\n  } catch (e) {\n    throw new Error(`Invalid URL: ${url}`)\n  }\n}\n\nexport function insertEventIntoDescendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return b.created_at - event.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function insertEventIntoAscendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return event.created_at - b.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function binarySearch<T>(arr: T[], compare: (b: T) => number): [number, boolean] {\n  let start = 0\n  let end = arr.length - 1\n\n  while (start <= end) {\n    const mid = Math.floor((start + end) / 2)\n    const cmp = compare(arr[mid])\n\n    if (cmp === 0) {\n      return [mid, true]\n    }\n\n    if (cmp < 0) {\n      end = mid - 1\n    } else {\n      start = mid + 1\n    }\n  }\n\n  return [start, false]\n}\n\nexport class QueueNode<V> {\n  public value: V\n  public next: QueueNode<V> | null = null\n  public prev: QueueNode<V> | null = null\n\n  constructor(message: V) {\n    this.value = message\n  }\n}\n\nexport class Queue<V> {\n  public first: QueueNode<V> | null\n  public last: QueueNode<V> | null\n\n  constructor() {\n    this.first = null\n    this.last = null\n  }\n\n  enqueue(value: V): boolean {\n    const newNode = new QueueNode(value)\n    if (!this.last) {\n      // list is empty\n      this.first = newNode\n      this.last = newNode\n    } else if (this.last === this.first) {\n      // list has a single element\n      this.last = newNode\n      this.last.prev = this.first\n      this.first.next = newNode\n    } else {\n      // list has elements, add as last\n      newNode.prev = this.last\n      this.last.next = newNode\n      this.last = newNode\n    }\n    return true\n  }\n\n  dequeue(): V | null {\n    if (!this.first) return null\n\n    if (this.first === this.last) {\n      const target = this.first\n      this.first = null\n      this.last = null\n      return target.value\n    }\n\n    const target = this.first\n    this.first = target.next\n    if (this.first) {\n      this.first.prev = null // fix: clean up prev pointer\n    }\n\n    return target.value\n  }\n}\n", "import { AddressPointer, BECH32_REGEX, decode, EventPointer, ProfilePointer } from './nip19.ts'\n\n/** Nostr URI regex, eg `nostr:npub1...` */\nexport const NOSTR_URI_REGEX: RegExp = new RegExp(`nostr:(${BECH32_REGEX.source})`)\n\n/** Test whether the value is a Nostr URI. */\nexport function test(value: unknown): value is `nostr:${string}` {\n  return typeof value === 'string' && new RegExp(`^${NOSTR_URI_REGEX.source}$`).test(value)\n}\n\n/** Parsed Nostr URI data. */\nexport interface NostrURI {\n  /** Full URI including the `nostr:` protocol. */\n  uri: `nostr:${string}`\n  /** The bech32-encoded data (eg `npub1...`). */\n  value: string\n  /** Decoded bech32 string, according to NIP-19. */\n  decoded:\n    | {\n        type: 'nevent'\n        data: EventPointer\n      }\n    | {\n        type: 'nprofile'\n        data: ProfilePointer\n      }\n    | {\n        type: 'naddr'\n        data: AddressPointer\n      }\n    | {\n        type: 'npub'\n        data: string\n      }\n    | {\n        type: 'nsec'\n        data: Uint8Array\n      }\n    | {\n        type: 'note'\n        data: string\n      }\n}\n\n/** Parse and decode a Nostr URI. */\nexport function parse(uri: string): NostrURI {\n  const match = uri.match(new RegExp(`^${NOSTR_URI_REGEX.source}$`))\n  if (!match) throw new Error(`Invalid Nostr URI: ${uri}`)\n  return {\n    uri: match[0] as `nostr:${string}`,\n    value: match[1],\n    decoded: decode(match[1]),\n  }\n}\n"],
-  "mappings": "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",
+  "sourcesContent": ["import { bytesToHex, concatBytes, hexToBytes } from '@noble/hashes/utils.js'\nimport { bech32 } from '@scure/base'\n\nimport { utf8Decoder, utf8Encoder } from './utils.ts'\n\nexport type NProfile = `nprofile1${string}`\nexport type NEvent = `nevent1${string}`\nexport type NAddr = `naddr1${string}`\nexport type NSec = `nsec1${string}`\nexport type NPub = `npub1${string}`\nexport type Note = `note1${string}`\nexport type Ncryptsec = `ncryptsec1${string}`\n\nexport const NostrTypeGuard = {\n  isNProfile: (value?: string | null): value is NProfile => /^nprofile1[a-z\\d]+$/.test(value || ''),\n  isNEvent: (value?: string | null): value is NEvent => /^nevent1[a-z\\d]+$/.test(value || ''),\n  isNAddr: (value?: string | null): value is NAddr => /^naddr1[a-z\\d]+$/.test(value || ''),\n  isNSec: (value?: string | null): value is NSec => /^nsec1[a-z\\d]{58}$/.test(value || ''),\n  isNPub: (value?: string | null): value is NPub => /^npub1[a-z\\d]{58}$/.test(value || ''),\n  isNote: (value?: string | null): value is Note => /^note1[a-z\\d]+$/.test(value || ''),\n  isNcryptsec: (value?: string | null): value is Ncryptsec => /^ncryptsec1[a-z\\d]+$/.test(value || ''),\n}\n\nexport const Bech32MaxSize = 5000\n\n/**\n * Bech32 regex.\n * @see https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki#bech32\n */\nexport const BECH32_REGEX = /[\\x21-\\x7E]{1,83}1[023456789acdefghjklmnpqrstuvwxyz]{6,}/\n\nfunction integerToUint8Array(number: number) {\n  // Create a Uint8Array with enough space to hold a 32-bit integer (4 bytes).\n  const uint8Array = new Uint8Array(4)\n\n  // Use bitwise operations to extract the bytes.\n  uint8Array[0] = (number >> 24) & 0xff // Most significant byte (MSB)\n  uint8Array[1] = (number >> 16) & 0xff\n  uint8Array[2] = (number >> 8) & 0xff\n  uint8Array[3] = number & 0xff // Least significant byte (LSB)\n\n  return uint8Array\n}\n\nexport type ProfilePointer = {\n  pubkey: string // hex\n  relays?: string[]\n}\n\nexport type EventPointer = {\n  id: string // hex\n  relays?: string[]\n  author?: string\n  kind?: number\n}\n\nexport type AddressPointer = {\n  identifier: string\n  pubkey: string\n  kind: number\n  relays?: string[]\n}\n\nexport function decodeNostrURI(nip19code: string): ReturnType<typeof decode> | { type: 'invalid'; data: null } {\n  try {\n    if (nip19code.startsWith('nostr:')) nip19code = nip19code.substring(6)\n    return decode(nip19code)\n  } catch (_err) {\n    return { type: 'invalid', data: null }\n  }\n}\n\nexport type DecodedNevent = {\n  type: 'nevent'\n  data: EventPointer\n}\n\nexport type DecodedNprofile = {\n  type: 'nprofile'\n  data: ProfilePointer\n}\n\nexport type DecodedNaddr = {\n  type: 'naddr'\n  data: AddressPointer\n}\n\nexport type DecodedNsec = {\n  type: 'nsec'\n  data: Uint8Array\n}\n\nexport type DecodedNpub = {\n  type: 'npub'\n  data: string\n}\n\nexport type DecodedNote = {\n  type: 'note'\n  data: string\n}\n\nexport type DecodedResult = DecodedNevent | DecodedNprofile | DecodedNaddr | DecodedNpub | DecodedNsec | DecodedNote\n\nexport function decode(nip19: NEvent): DecodedNevent\nexport function decode(nip19: NProfile): DecodedNprofile\nexport function decode(nip19: NAddr): DecodedNaddr\nexport function decode(nip19: NSec): DecodedNsec\nexport function decode(nip19: NPub): DecodedNpub\nexport function decode(nip19: Note): DecodedNote\nexport function decode(code: string): DecodedResult\nexport function decode(code: string): DecodedResult {\n  let { prefix, words } = bech32.decode(code as `${string}1${string}`, Bech32MaxSize)\n  let data = new Uint8Array(bech32.fromWords(words))\n\n  switch (prefix) {\n    case 'nprofile': {\n      let tlv = parseTLV(data)\n      if (!tlv[0]?.[0]) throw new Error('missing TLV 0 for nprofile')\n      if (tlv[0][0].length !== 32) throw new Error('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      let tlv = parseTLV(data)\n      if (!tlv[0]?.[0]) throw new Error('missing TLV 0 for nevent')\n      if (tlv[0][0].length !== 32) throw new Error('TLV 0 should be 32 bytes')\n      if (tlv[2] && tlv[2][0].length !== 32) throw new Error('TLV 2 should be 32 bytes')\n      if (tlv[3] && tlv[3][0].length !== 4) throw new Error('TLV 3 should be 4 bytes')\n\n      return {\n        type: 'nevent',\n        data: {\n          id: bytesToHex(tlv[0][0]),\n          relays: tlv[1] ? tlv[1].map(d => utf8Decoder.decode(d)) : [],\n          author: tlv[2]?.[0] ? bytesToHex(tlv[2][0]) : undefined,\n          kind: tlv[3]?.[0] ? parseInt(bytesToHex(tlv[3][0]), 16) : undefined,\n        },\n      }\n    }\n\n    case 'naddr': {\n      let tlv = parseTLV(data)\n      if (!tlv[0]?.[0]) throw new Error('missing TLV 0 for naddr')\n      if (!tlv[2]?.[0]) throw new Error('missing TLV 2 for naddr')\n      if (tlv[2][0].length !== 32) throw new Error('TLV 2 should be 32 bytes')\n      if (!tlv[3]?.[0]) throw new Error('missing TLV 3 for naddr')\n      if (tlv[3][0].length !== 4) throw new Error('TLV 3 should be 4 bytes')\n\n      return {\n        type: 'naddr',\n        data: {\n          identifier: utf8Decoder.decode(tlv[0][0]),\n          pubkey: bytesToHex(tlv[2][0]),\n          kind: parseInt(bytesToHex(tlv[3][0]), 16),\n          relays: tlv[1] ? tlv[1].map(d => utf8Decoder.decode(d)) : [],\n        },\n      }\n    }\n\n    case 'nsec':\n      return { type: prefix, data }\n\n    case 'npub':\n    case 'note':\n      return { type: prefix, data: bytesToHex(data) }\n\n    default:\n      throw new Error(`unknown prefix ${prefix}`)\n  }\n}\n\ntype TLV = { [t: number]: Uint8Array[] }\n\nfunction parseTLV(data: Uint8Array): TLV {\n  let result: TLV = {}\n  let rest = data\n  while (rest.length > 0) {\n    let t = rest[0]\n    let l = rest[1]\n    let v = rest.slice(2, 2 + l)\n    rest = rest.slice(2 + l)\n    if (v.length < l) throw new Error(`not enough data to read on TLV ${t}`)\n    result[t] = result[t] || []\n    result[t].push(v)\n  }\n  return result\n}\n\nexport function nsecEncode(key: Uint8Array): NSec {\n  return encodeBytes('nsec', key)\n}\n\nexport function npubEncode(hex: string): NPub {\n  return encodeBytes('npub', hexToBytes(hex))\n}\n\nexport function noteEncode(hex: string): Note {\n  return encodeBytes('note', hexToBytes(hex))\n}\n\nfunction encodeBech32<Prefix extends string>(prefix: Prefix, data: Uint8Array): `${Prefix}1${string}` {\n  let words = bech32.toWords(data)\n  return bech32.encode(prefix, words, Bech32MaxSize) as `${Prefix}1${string}`\n}\n\nexport function encodeBytes<Prefix extends string>(prefix: Prefix, bytes: Uint8Array): `${Prefix}1${string}` {\n  return encodeBech32(prefix, bytes)\n}\n\nexport function nprofileEncode(profile: ProfilePointer): NProfile {\n  let data = encodeTLV({\n    0: [hexToBytes(profile.pubkey)],\n    1: (profile.relays || []).map(url => utf8Encoder.encode(url)),\n  })\n  return encodeBech32('nprofile', data)\n}\n\nexport function neventEncode(event: EventPointer): NEvent {\n  let kindArray\n  if (event.kind !== undefined) {\n    kindArray = integerToUint8Array(event.kind)\n  }\n\n  let data = encodeTLV({\n    0: [hexToBytes(event.id)],\n    1: (event.relays || []).map(url => utf8Encoder.encode(url)),\n    2: event.author ? [hexToBytes(event.author)] : [],\n    3: kindArray ? [new Uint8Array(kindArray)] : [],\n  })\n\n  return encodeBech32('nevent', data)\n}\n\nexport function naddrEncode(addr: AddressPointer): NAddr {\n  let kind = new ArrayBuffer(4)\n  new DataView(kind).setUint32(0, addr.kind, false)\n\n  let data = encodeTLV({\n    0: [utf8Encoder.encode(addr.identifier)],\n    1: (addr.relays || []).map(url => utf8Encoder.encode(url)),\n    2: [hexToBytes(addr.pubkey)],\n    3: [new Uint8Array(kind)],\n  })\n  return encodeBech32('naddr', data)\n}\n\nfunction encodeTLV(tlv: TLV): Uint8Array {\n  let entries: Uint8Array[] = []\n\n  Object.entries(tlv)\n    .reverse()\n    .forEach(([t, vs]) => {\n      vs.forEach(v => {\n        let entry = new Uint8Array(v.length + 2)\n        entry.set([parseInt(t)], 0)\n        entry.set([v.length], 1)\n        entry.set(v, 2)\n        entries.push(entry)\n      })\n    })\n\n  return concatBytes(...entries)\n}\n", "import type { Event } from './core.ts'\n\nexport const utf8Decoder: TextDecoder = new TextDecoder('utf-8')\nexport const utf8Encoder: TextEncoder = new TextEncoder()\n\nexport { bytesToHex, hexToBytes } from '@noble/hashes/utils.js'\n\nexport function normalizeURL(url: string): string {\n  try {\n    if (url.indexOf('://') === -1) url = 'wss://' + url\n    let p = new URL(url)\n    if (p.protocol === 'http:') p.protocol = 'ws:'\n    else if (p.protocol === 'https:') p.protocol = 'wss:'\n    p.pathname = p.pathname.replace(/\\/+/g, '/')\n    if (p.pathname.endsWith('/')) p.pathname = p.pathname.slice(0, -1)\n    if ((p.port === '80' && p.protocol === 'ws:') || (p.port === '443' && p.protocol === 'wss:')) p.port = ''\n    p.searchParams.sort()\n    p.hash = ''\n    return p.toString()\n  } catch (e) {\n    throw new Error(`Invalid URL: ${url}`)\n  }\n}\n\nexport function insertEventIntoDescendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return b.created_at - event.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function insertEventIntoAscendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return event.created_at - b.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function binarySearch<T>(arr: T[], compare: (b: T) => number): [number, boolean] {\n  let start = 0\n  let end = arr.length - 1\n\n  while (start <= end) {\n    const mid = Math.floor((start + end) / 2)\n    const cmp = compare(arr[mid])\n\n    if (cmp === 0) {\n      return [mid, true]\n    }\n\n    if (cmp < 0) {\n      end = mid - 1\n    } else {\n      start = mid + 1\n    }\n  }\n\n  return [start, false]\n}\n\nexport class QueueNode<V> {\n  public value: V\n  public next: QueueNode<V> | null = null\n  public prev: QueueNode<V> | null = null\n\n  constructor(message: V) {\n    this.value = message\n  }\n}\n\nexport class Queue<V> {\n  public first: QueueNode<V> | null\n  public last: QueueNode<V> | null\n\n  constructor() {\n    this.first = null\n    this.last = null\n  }\n\n  enqueue(value: V): boolean {\n    const newNode = new QueueNode(value)\n    if (!this.last) {\n      // list is empty\n      this.first = newNode\n      this.last = newNode\n    } else if (this.last === this.first) {\n      // list has a single element\n      this.last = newNode\n      this.last.prev = this.first\n      this.first.next = newNode\n    } else {\n      // list has elements, add as last\n      newNode.prev = this.last\n      this.last.next = newNode\n      this.last = newNode\n    }\n    return true\n  }\n\n  dequeue(): V | null {\n    if (!this.first) return null\n\n    if (this.first === this.last) {\n      const target = this.first\n      this.first = null\n      this.last = null\n      return target.value\n    }\n\n    const target = this.first\n    this.first = target.next\n    if (this.first) {\n      this.first.prev = null // fix: clean up prev pointer\n    }\n\n    return target.value\n  }\n}\n", "import { AddressPointer, BECH32_REGEX, decode, EventPointer, ProfilePointer } from './nip19.ts'\n\n/** Nostr URI regex, eg `nostr:npub1...` */\nexport const NOSTR_URI_REGEX: RegExp = new RegExp(`nostr:(${BECH32_REGEX.source})`)\n\n/** Test whether the value is a Nostr URI. */\nexport function test(value: unknown): value is `nostr:${string}` {\n  return typeof value === 'string' && new RegExp(`^${NOSTR_URI_REGEX.source}$`).test(value)\n}\n\n/** Parsed Nostr URI data. */\nexport interface NostrURI {\n  /** Full URI including the `nostr:` protocol. */\n  uri: `nostr:${string}`\n  /** The bech32-encoded data (eg `npub1...`). */\n  value: string\n  /** Decoded bech32 string, according to NIP-19. */\n  decoded:\n    | {\n        type: 'nevent'\n        data: EventPointer\n      }\n    | {\n        type: 'nprofile'\n        data: ProfilePointer\n      }\n    | {\n        type: 'naddr'\n        data: AddressPointer\n      }\n    | {\n        type: 'npub'\n        data: string\n      }\n    | {\n        type: 'nsec'\n        data: Uint8Array\n      }\n    | {\n        type: 'note'\n        data: string\n      }\n}\n\n/** Parse and decode a Nostr URI. */\nexport function parse(uri: string): NostrURI {\n  const match = uri.match(new RegExp(`^${NOSTR_URI_REGEX.source}$`))\n  if (!match) throw new Error(`Invalid Nostr URI: ${uri}`)\n  return {\n    uri: match[0] as `nostr:${string}`,\n    value: match[1],\n    decoded: decode(match[1]),\n  }\n}\n"],
+  "mappings": "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",
   "names": ["bytesToHex", "hexToBytes", "bytesToHex"]
 }
Index: package/lib/cjs/nip25.js.map
===================================================================
--- package/lib/cjs/nip25.js.map
+++ package/lib/cjs/nip25.js.map
@@ -1,7 +1,7 @@
 {
   "version": 3,
   "sources": ["../../nip25.ts", "../../pure.ts", "../../core.ts", "../../utils.ts", "../../kinds.ts"],
-  "sourcesContent": ["import { Event, finalizeEvent } from './pure.ts'\nimport { Reaction } from './kinds.ts'\n\nimport type { EventPointer } from './nip19.ts'\n\nexport type ReactionEventTemplate = {\n  /**\n   * Pass only non-nip25 tags if you have to. Nip25 tags ('e' and 'p' tags from reacted event) will be added automatically.\n   */\n  tags?: string[][]\n\n  /**\n   * @default '+'\n   */\n  content?: string\n\n  created_at: number\n}\n\nexport function finishReactionEvent(t: ReactionEventTemplate, reacted: Event, privateKey: Uint8Array): Event {\n  const inheritedTags = reacted.tags.filter(tag => tag.length >= 2 && (tag[0] === 'e' || tag[0] === 'p'))\n\n  return finalizeEvent(\n    {\n      ...t,\n      kind: Reaction,\n      tags: [...(t.tags ?? []), ...inheritedTags, ['e', reacted.id], ['p', reacted.pubkey]],\n      content: t.content ?? '+',\n    },\n    privateKey,\n  )\n}\n\nexport function getReactedEventPointer(event: Event): undefined | EventPointer {\n  if (event.kind !== Reaction) {\n    return undefined\n  }\n\n  let lastETag: undefined | string[]\n  let lastPTag: undefined | string[]\n\n  for (let i = event.tags.length - 1; i >= 0 && (lastETag === undefined || lastPTag === undefined); i--) {\n    const tag = event.tags[i]\n    if (tag.length >= 2) {\n      if (tag[0] === 'e' && lastETag === undefined) {\n        lastETag = tag\n      } else if (tag[0] === 'p' && lastPTag === undefined) {\n        lastPTag = tag\n      }\n    }\n  }\n\n  if (lastETag === undefined || lastPTag === undefined) {\n    return undefined\n  }\n\n  return {\n    id: lastETag[1],\n    relays: [lastETag[2], lastPTag[2]].filter(x => x !== undefined),\n    author: lastPTag[1],\n  }\n}\n", "import { schnorr } from '@noble/curves/secp256k1'\nimport { bytesToHex } from '@noble/hashes/utils'\nimport { Nostr, Event, EventTemplate, UnsignedEvent, VerifiedEvent, verifiedSymbol, validateEvent } from './core.ts'\nimport { sha256 } from '@noble/hashes/sha256'\n\nimport { utf8Encoder } from './utils.ts'\n\nclass JS implements Nostr {\n  generateSecretKey(): Uint8Array {\n    return schnorr.utils.randomPrivateKey()\n  }\n  getPublicKey(secretKey: Uint8Array): string {\n    return bytesToHex(schnorr.getPublicKey(secretKey))\n  }\n  finalizeEvent(t: EventTemplate, secretKey: Uint8Array): VerifiedEvent {\n    const event = t as VerifiedEvent\n    event.pubkey = bytesToHex(schnorr.getPublicKey(secretKey))\n    event.id = getEventHash(event)\n    event.sig = bytesToHex(schnorr.sign(getEventHash(event), secretKey))\n    event[verifiedSymbol] = true\n    return event\n  }\n  verifyEvent(event: Event): event is VerifiedEvent {\n    if (typeof event[verifiedSymbol] === 'boolean') return event[verifiedSymbol]\n\n    const hash = getEventHash(event)\n    if (hash !== event.id) {\n      event[verifiedSymbol] = false\n      return false\n    }\n\n    try {\n      const valid = schnorr.verify(event.sig, hash, event.pubkey)\n      event[verifiedSymbol] = valid\n      return valid\n    } catch (err) {\n      event[verifiedSymbol] = false\n      return false\n    }\n  }\n}\n\nexport function serializeEvent(evt: UnsignedEvent): string {\n  if (!validateEvent(evt)) throw new Error(\"can't serialize event with wrong or missing properties\")\n  return JSON.stringify([0, evt.pubkey, evt.created_at, evt.kind, evt.tags, evt.content])\n}\n\nexport function getEventHash(event: UnsignedEvent): string {\n  let eventHash = sha256(utf8Encoder.encode(serializeEvent(event)))\n  return bytesToHex(eventHash)\n}\n\nconst i: JS = new JS()\n\nexport const generateSecretKey = i.generateSecretKey\nexport const getPublicKey = i.getPublicKey\nexport const finalizeEvent = i.finalizeEvent\nexport const verifyEvent = i.verifyEvent\nexport * from './core.ts'\n", "export interface Nostr {\n  generateSecretKey(): Uint8Array\n  getPublicKey(secretKey: Uint8Array): string\n  finalizeEvent(event: EventTemplate, secretKey: Uint8Array): VerifiedEvent\n  verifyEvent(event: Event): event is VerifiedEvent\n}\n\n/** Designates a verified event signature. */\nexport const verifiedSymbol = Symbol('verified')\n\nexport type NostrEvent = {\n  kind: number\n  tags: string[][]\n  content: string\n  created_at: number\n  pubkey: string\n  id: string\n  sig: string\n  [verifiedSymbol]?: boolean\n}\n\nexport type Event = NostrEvent\nexport type EventTemplate = Pick<Event, 'kind' | 'tags' | 'content' | 'created_at'>\nexport type UnsignedEvent = Pick<Event, 'kind' | 'tags' | 'content' | 'created_at' | 'pubkey'>\n\n/** An event whose signature has been verified. */\nexport interface VerifiedEvent extends Event {\n  [verifiedSymbol]: true\n}\n\nconst isRecord = (obj: unknown): obj is Record<string, unknown> => obj instanceof Object\n\nexport function validateEvent<T>(event: T): event is T & UnsignedEvent {\n  if (!isRecord(event)) return false\n  if (typeof event.kind !== 'number') return false\n  if (typeof event.content !== 'string') return false\n  if (typeof event.created_at !== 'number') return false\n  if (typeof event.pubkey !== 'string') return false\n  if (!event.pubkey.match(/^[a-f0-9]{64}$/)) return false\n\n  if (!Array.isArray(event.tags)) return false\n  for (let i = 0; i < event.tags.length; i++) {\n    let tag = event.tags[i]\n    if (!Array.isArray(tag)) return false\n    for (let j = 0; j < tag.length; j++) {\n      if (typeof tag[j] !== 'string') return false\n    }\n  }\n\n  return true\n}\n\n/**\n * Sort events in reverse-chronological order by the `created_at` timestamp,\n * and then by the event `id` (lexicographically) in case of ties.\n * This mutates the array.\n */\nexport function sortEvents(events: Event[]): Event[] {\n  return events.sort((a: NostrEvent, b: NostrEvent): number => {\n    if (a.created_at !== b.created_at) {\n      return b.created_at - a.created_at\n    }\n    return a.id.localeCompare(b.id)\n  })\n}\n", "import type { Event } from './core.ts'\n\nexport const utf8Decoder: TextDecoder = new TextDecoder('utf-8')\nexport const utf8Encoder: TextEncoder = new TextEncoder()\n\nexport { bytesToHex, hexToBytes } from '@noble/hashes/utils'\n\nexport function normalizeURL(url: string): string {\n  try {\n    if (url.indexOf('://') === -1) url = 'wss://' + url\n    let p = new URL(url)\n    if (p.protocol === 'http:') p.protocol = 'ws:'\n    else if (p.protocol === 'https:') p.protocol = 'wss:'\n    p.pathname = p.pathname.replace(/\\/+/g, '/')\n    if (p.pathname.endsWith('/')) p.pathname = p.pathname.slice(0, -1)\n    if ((p.port === '80' && p.protocol === 'ws:') || (p.port === '443' && p.protocol === 'wss:')) p.port = ''\n    p.searchParams.sort()\n    p.hash = ''\n    return p.toString()\n  } catch (e) {\n    throw new Error(`Invalid URL: ${url}`)\n  }\n}\n\nexport function insertEventIntoDescendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return b.created_at - event.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function insertEventIntoAscendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return event.created_at - b.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function binarySearch<T>(arr: T[], compare: (b: T) => number): [number, boolean] {\n  let start = 0\n  let end = arr.length - 1\n\n  while (start <= end) {\n    const mid = Math.floor((start + end) / 2)\n    const cmp = compare(arr[mid])\n\n    if (cmp === 0) {\n      return [mid, true]\n    }\n\n    if (cmp < 0) {\n      end = mid - 1\n    } else {\n      start = mid + 1\n    }\n  }\n\n  return [start, false]\n}\n\nexport class QueueNode<V> {\n  public value: V\n  public next: QueueNode<V> | null = null\n  public prev: QueueNode<V> | null = null\n\n  constructor(message: V) {\n    this.value = message\n  }\n}\n\nexport class Queue<V> {\n  public first: QueueNode<V> | null\n  public last: QueueNode<V> | null\n\n  constructor() {\n    this.first = null\n    this.last = null\n  }\n\n  enqueue(value: V): boolean {\n    const newNode = new QueueNode(value)\n    if (!this.last) {\n      // list is empty\n      this.first = newNode\n      this.last = newNode\n    } else if (this.last === this.first) {\n      // list has a single element\n      this.last = newNode\n      this.last.prev = this.first\n      this.first.next = newNode\n    } else {\n      // list has elements, add as last\n      newNode.prev = this.last\n      this.last.next = newNode\n      this.last = newNode\n    }\n    return true\n  }\n\n  dequeue(): V | null {\n    if (!this.first) return null\n\n    if (this.first === this.last) {\n      const target = this.first\n      this.first = null\n      this.last = null\n      return target.value\n    }\n\n    const target = this.first\n    this.first = target.next\n    if (this.first) {\n      this.first.prev = null // fix: clean up prev pointer\n    }\n\n    return target.value\n  }\n}\n", "import { NostrEvent, validateEvent } from './pure.ts'\n\n/** Events are **regular**, which means they're all expected to be stored by relays. */\nexport function isRegularKind(kind: number): boolean {\n  return kind < 10000 && kind !== 0 && kind !== 3\n}\n\n/** Events are **replaceable**, which means that, for each combination of `pubkey` and `kind`, only the latest event is expected to (SHOULD) be stored by relays, older versions are expected to be discarded. */\nexport function isReplaceableKind(kind: number): boolean {\n  return kind === 0 || kind === 3 || (10000 <= kind && kind < 20000)\n}\n\n/** Events are **ephemeral**, which means they are not expected to be stored by relays. */\nexport function isEphemeralKind(kind: number): boolean {\n  return 20000 <= kind && kind < 30000\n}\n\n/** Events are **addressable**, which means that, for each combination of `pubkey`, `kind` and the `d` tag, only the latest event is expected to be stored by relays, older versions are expected to be discarded. */\nexport function isAddressableKind(kind: number): boolean {\n  return 30000 <= kind && kind < 40000\n}\n\n/** Classification of the event kind. */\nexport type KindClassification = 'regular' | 'replaceable' | 'ephemeral' | 'parameterized' | 'unknown'\n\n/** Determine the classification of this kind of event if known, or `unknown`. */\nexport function classifyKind(kind: number): KindClassification {\n  if (isRegularKind(kind)) return 'regular'\n  if (isReplaceableKind(kind)) return 'replaceable'\n  if (isEphemeralKind(kind)) return 'ephemeral'\n  if (isAddressableKind(kind)) return 'parameterized'\n  return 'unknown'\n}\n\nexport function isKind<T extends number>(event: unknown, kind: T | Array<T>): event is NostrEvent & { kind: T } {\n  const kindAsArray: number[] = kind instanceof Array ? kind : [kind]\n  return (validateEvent(event) && kindAsArray.includes(event.kind)) || false\n}\n\nexport const Metadata = 0\nexport type Metadata = typeof Metadata\nexport const ShortTextNote = 1\nexport type ShortTextNote = typeof ShortTextNote\nexport const RecommendRelay = 2\nexport type RecommendRelay = typeof RecommendRelay\nexport const Contacts = 3\nexport type Contacts = typeof Contacts\nexport const EncryptedDirectMessage = 4\nexport type EncryptedDirectMessage = typeof EncryptedDirectMessage\nexport const EventDeletion = 5\nexport type EventDeletion = typeof EventDeletion\nexport const Repost = 6\nexport type Repost = typeof Repost\nexport const Reaction = 7\nexport type Reaction = typeof Reaction\nexport const BadgeAward = 8\nexport type BadgeAward = typeof BadgeAward\nexport const ChatMessage = 9\nexport type ChatMessage = typeof ChatMessage\nexport const ForumThread = 11\nexport type ForumThread = typeof ForumThread\nexport const Seal = 13\nexport type Seal = typeof Seal\nexport const PrivateDirectMessage = 14\nexport type PrivateDirectMessage = typeof PrivateDirectMessage\nexport const FileMessage = 15\nexport type FileMessage = typeof FileMessage\nexport const GenericRepost = 16\nexport type GenericRepost = typeof GenericRepost\nexport const Photo = 20\nexport type Photo = typeof Photo\nexport const NormalVideo = 21\nexport type NormalVideo = typeof NormalVideo\nexport const ShortVideo = 22\nexport type ShortVideo = typeof ShortVideo\nexport const ChannelCreation = 40\nexport type ChannelCreation = typeof ChannelCreation\nexport const ChannelMetadata = 41\nexport type ChannelMetadata = typeof ChannelMetadata\nexport const ChannelMessage = 42\nexport type ChannelMessage = typeof ChannelMessage\nexport const ChannelHideMessage = 43\nexport type ChannelHideMessage = typeof ChannelHideMessage\nexport const ChannelMuteUser = 44\nexport type ChannelMuteUser = typeof ChannelMuteUser\nexport const OpenTimestamps = 1040\nexport type OpenTimestamps = typeof OpenTimestamps\nexport const GiftWrap = 1059\nexport type GiftWrap = typeof GiftWrap\nexport const Poll = 1068\nexport type Poll = typeof Poll\nexport const FileMetadata = 1063\nexport type FileMetadata = typeof FileMetadata\nexport const Comment = 1111\nexport type Comment = typeof Comment\nexport const LiveChatMessage = 1311\nexport type LiveChatMessage = typeof LiveChatMessage\nexport const Voice = 1222\nexport type Voice = typeof Voice\nexport const VoiceComment = 1244\nexport type VoiceComment = typeof VoiceComment\nexport const ProblemTracker = 1971\nexport type ProblemTracker = typeof ProblemTracker\nexport const Report = 1984\nexport type Report = typeof Report\nexport const Reporting = 1984\nexport type Reporting = typeof Reporting\nexport const Label = 1985\nexport type Label = typeof Label\nexport const CommunityPostApproval = 4550\nexport type CommunityPostApproval = typeof CommunityPostApproval\nexport const JobRequest = 5999\nexport type JobRequest = typeof JobRequest\nexport const JobResult = 6999\nexport type JobResult = typeof JobResult\nexport const JobFeedback = 7000\nexport type JobFeedback = typeof JobFeedback\nexport const ZapGoal = 9041\nexport type ZapGoal = typeof ZapGoal\nexport const ZapRequest = 9734\nexport type ZapRequest = typeof ZapRequest\nexport const Zap = 9735\nexport type Zap = typeof Zap\nexport const Highlights = 9802\nexport type Highlights = typeof Highlights\nexport const PollResponse = 1018\nexport type PollResponse = typeof PollResponse\nexport const Mutelist = 10000\nexport type Mutelist = typeof Mutelist\nexport const Pinlist = 10001\nexport type Pinlist = typeof Pinlist\nexport const RelayList = 10002\nexport type RelayList = typeof RelayList\nexport const BookmarkList = 10003\nexport type BookmarkList = typeof BookmarkList\nexport const CommunitiesList = 10004\nexport type CommunitiesList = typeof CommunitiesList\nexport const PublicChatsList = 10005\nexport type PublicChatsList = typeof PublicChatsList\nexport const BlockedRelaysList = 10006\nexport type BlockedRelaysList = typeof BlockedRelaysList\nexport const SearchRelaysList = 10007\nexport type SearchRelaysList = typeof SearchRelaysList\nexport const FavoriteRelays = 10012\nexport type FavoriteRelays = typeof FavoriteRelays\nexport const InterestsList = 10015\nexport type InterestsList = typeof InterestsList\nexport const UserEmojiList = 10030\nexport type UserEmojiList = typeof UserEmojiList\nexport const DirectMessageRelaysList = 10050\nexport type DirectMessageRelaysList = typeof DirectMessageRelaysList\nexport const FileServerPreference = 10096\nexport type FileServerPreference = typeof FileServerPreference\nexport const BlossomServerList = 10063\nexport type BlossomServerList = typeof BlossomServerList\nexport const NWCWalletInfo = 13194\nexport type NWCWalletInfo = typeof NWCWalletInfo\nexport const LightningPubRPC = 21000\nexport type LightningPubRPC = typeof LightningPubRPC\nexport const ClientAuth = 22242\nexport type ClientAuth = typeof ClientAuth\nexport const NWCWalletRequest = 23194\nexport type NWCWalletRequest = typeof NWCWalletRequest\nexport const NWCWalletResponse = 23195\nexport type NWCWalletResponse = typeof NWCWalletResponse\nexport const NostrConnect = 24133\nexport type NostrConnect = typeof NostrConnect\nexport const HTTPAuth = 27235\nexport type HTTPAuth = typeof HTTPAuth\nexport const Followsets = 30000\nexport type Followsets = typeof Followsets\nexport const Genericlists = 30001\nexport type Genericlists = typeof Genericlists\nexport const Relaysets = 30002\nexport type Relaysets = typeof Relaysets\nexport const Bookmarksets = 30003\nexport type Bookmarksets = typeof Bookmarksets\nexport const Curationsets = 30004\nexport type Curationsets = typeof Curationsets\nexport const ProfileBadges = 30008\nexport type ProfileBadges = typeof ProfileBadges\nexport const BadgeDefinition = 30009\nexport type BadgeDefinition = typeof BadgeDefinition\nexport const Interestsets = 30015\nexport type Interestsets = typeof Interestsets\nexport const CreateOrUpdateStall = 30017\nexport type CreateOrUpdateStall = typeof CreateOrUpdateStall\nexport const CreateOrUpdateProduct = 30018\nexport type CreateOrUpdateProduct = typeof CreateOrUpdateProduct\nexport const LongFormArticle = 30023\nexport type LongFormArticle = typeof LongFormArticle\nexport const DraftLong = 30024\nexport type DraftLong = typeof DraftLong\nexport const Emojisets = 30030\nexport type Emojisets = typeof Emojisets\nexport const Application = 30078\nexport type Application = typeof Application\nexport const LiveEvent = 30311\nexport type LiveEvent = typeof LiveEvent\nexport const UserStatuses = 30315\nexport type UserStatuses = typeof UserStatuses\nexport const ClassifiedListing = 30402\nexport type ClassifiedListing = typeof ClassifiedListing\nexport const DraftClassifiedListing = 30403\nexport type DraftClassifiedListing = typeof DraftClassifiedListing\nexport const Date = 31922\nexport type Date = typeof Date\nexport const Time = 31923\nexport type Time = typeof Time\nexport const Calendar = 31924\nexport type Calendar = typeof Calendar\nexport const CalendarEventRSVP = 31925\nexport type CalendarEventRSVP = typeof CalendarEventRSVP\nexport const RelayReview = 31987\nexport type RelayReview = typeof RelayReview\nexport const Handlerrecommendation = 31989\nexport type Handlerrecommendation = typeof Handlerrecommendation\nexport const Handlerinformation = 31990\nexport type Handlerinformation = typeof Handlerinformation\nexport const CommunityDefinition = 34550\nexport type CommunityDefinition = typeof CommunityDefinition\nexport const GroupMetadata = 39000\nexport type GroupMetadata = typeof GroupMetadata\n"],
-  "mappings": "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",
+  "sourcesContent": ["import { Event, finalizeEvent } from './pure.ts'\nimport { Reaction } from './kinds.ts'\n\nimport type { EventPointer } from './nip19.ts'\n\nexport type ReactionEventTemplate = {\n  /**\n   * Pass only non-nip25 tags if you have to. Nip25 tags ('e' and 'p' tags from reacted event) will be added automatically.\n   */\n  tags?: string[][]\n\n  /**\n   * @default '+'\n   */\n  content?: string\n\n  created_at: number\n}\n\nexport function finishReactionEvent(t: ReactionEventTemplate, reacted: Event, privateKey: Uint8Array): Event {\n  const inheritedTags = reacted.tags.filter(tag => tag.length >= 2 && (tag[0] === 'e' || tag[0] === 'p'))\n\n  return finalizeEvent(\n    {\n      ...t,\n      kind: Reaction,\n      tags: [...(t.tags ?? []), ...inheritedTags, ['e', reacted.id], ['p', reacted.pubkey]],\n      content: t.content ?? '+',\n    },\n    privateKey,\n  )\n}\n\nexport function getReactedEventPointer(event: Event): undefined | EventPointer {\n  if (event.kind !== Reaction) {\n    return undefined\n  }\n\n  let lastETag: undefined | string[]\n  let lastPTag: undefined | string[]\n\n  for (let i = event.tags.length - 1; i >= 0 && (lastETag === undefined || lastPTag === undefined); i--) {\n    const tag = event.tags[i]\n    if (tag.length >= 2) {\n      if (tag[0] === 'e' && lastETag === undefined) {\n        lastETag = tag\n      } else if (tag[0] === 'p' && lastPTag === undefined) {\n        lastPTag = tag\n      }\n    }\n  }\n\n  if (lastETag === undefined || lastPTag === undefined) {\n    return undefined\n  }\n\n  return {\n    id: lastETag[1],\n    relays: [lastETag[2], lastPTag[2]].filter(x => x !== undefined),\n    author: lastPTag[1],\n  }\n}\n", "import { schnorr } from '@noble/curves/secp256k1.js'\nimport { bytesToHex, hexToBytes } from '@noble/hashes/utils.js'\nimport { Nostr, Event, EventTemplate, UnsignedEvent, VerifiedEvent, verifiedSymbol, validateEvent } from './core.ts'\nimport { sha256 } from '@noble/hashes/sha2.js'\n\nimport { utf8Encoder } from './utils.ts'\n\nclass JS implements Nostr {\n  generateSecretKey(): Uint8Array {\n    return schnorr.utils.randomSecretKey()\n  }\n  getPublicKey(secretKey: Uint8Array): string {\n    return bytesToHex(schnorr.getPublicKey(secretKey))\n  }\n  finalizeEvent(t: EventTemplate, secretKey: Uint8Array): VerifiedEvent {\n    const event = t as VerifiedEvent\n    event.pubkey = bytesToHex(schnorr.getPublicKey(secretKey))\n    event.id = getEventHash(event)\n    event.sig = bytesToHex(schnorr.sign(hexToBytes(getEventHash(event)), secretKey))\n    event[verifiedSymbol] = true\n    return event\n  }\n  verifyEvent(event: Event): event is VerifiedEvent {\n    if (typeof event[verifiedSymbol] === 'boolean') return event[verifiedSymbol]\n\n    const hash = getEventHash(event)\n    if (hash !== event.id) {\n      event[verifiedSymbol] = false\n      return false\n    }\n\n    try {\n      const valid = schnorr.verify(hexToBytes(event.sig), hexToBytes(hash), hexToBytes(event.pubkey))\n      event[verifiedSymbol] = valid\n      return valid\n    } catch (err) {\n      event[verifiedSymbol] = false\n      return false\n    }\n  }\n}\n\nexport function serializeEvent(evt: UnsignedEvent): string {\n  if (!validateEvent(evt)) throw new Error(\"can't serialize event with wrong or missing properties\")\n  return JSON.stringify([0, evt.pubkey, evt.created_at, evt.kind, evt.tags, evt.content])\n}\n\nexport function getEventHash(event: UnsignedEvent): string {\n  let eventHash = sha256(utf8Encoder.encode(serializeEvent(event)))\n  return bytesToHex(eventHash)\n}\n\nconst i: JS = new JS()\n\nexport const generateSecretKey = i.generateSecretKey\nexport const getPublicKey = i.getPublicKey\nexport const finalizeEvent = i.finalizeEvent\nexport const verifyEvent = i.verifyEvent\nexport * from './core.ts'\n", "export interface Nostr {\n  generateSecretKey(): Uint8Array\n  getPublicKey(secretKey: Uint8Array): string\n  finalizeEvent(event: EventTemplate, secretKey: Uint8Array): VerifiedEvent\n  verifyEvent(event: Event): event is VerifiedEvent\n}\n\n/** Designates a verified event signature. */\nexport const verifiedSymbol = Symbol('verified')\n\nexport type NostrEvent = {\n  kind: number\n  tags: string[][]\n  content: string\n  created_at: number\n  pubkey: string\n  id: string\n  sig: string\n  [verifiedSymbol]?: boolean\n}\n\nexport type Event = NostrEvent\nexport type EventTemplate = Pick<Event, 'kind' | 'tags' | 'content' | 'created_at'>\nexport type UnsignedEvent = Pick<Event, 'kind' | 'tags' | 'content' | 'created_at' | 'pubkey'>\n\n/** An event whose signature has been verified. */\nexport interface VerifiedEvent extends Event {\n  [verifiedSymbol]: true\n}\n\nconst isRecord = (obj: unknown): obj is Record<string, unknown> => obj instanceof Object\n\nexport function validateEvent<T>(event: T): event is T & UnsignedEvent {\n  if (!isRecord(event)) return false\n  if (typeof event.kind !== 'number') return false\n  if (typeof event.content !== 'string') return false\n  if (typeof event.created_at !== 'number') return false\n  if (typeof event.pubkey !== 'string') return false\n  if (!event.pubkey.match(/^[a-f0-9]{64}$/)) return false\n\n  if (!Array.isArray(event.tags)) return false\n  for (let i = 0; i < event.tags.length; i++) {\n    let tag = event.tags[i]\n    if (!Array.isArray(tag)) return false\n    for (let j = 0; j < tag.length; j++) {\n      if (typeof tag[j] !== 'string') return false\n    }\n  }\n\n  return true\n}\n\n/**\n * Sort events in reverse-chronological order by the `created_at` timestamp,\n * and then by the event `id` (lexicographically) in case of ties.\n * This mutates the array.\n */\nexport function sortEvents(events: Event[]): Event[] {\n  return events.sort((a: NostrEvent, b: NostrEvent): number => {\n    if (a.created_at !== b.created_at) {\n      return b.created_at - a.created_at\n    }\n    return a.id.localeCompare(b.id)\n  })\n}\n", "import type { Event } from './core.ts'\n\nexport const utf8Decoder: TextDecoder = new TextDecoder('utf-8')\nexport const utf8Encoder: TextEncoder = new TextEncoder()\n\nexport { bytesToHex, hexToBytes } from '@noble/hashes/utils.js'\n\nexport function normalizeURL(url: string): string {\n  try {\n    if (url.indexOf('://') === -1) url = 'wss://' + url\n    let p = new URL(url)\n    if (p.protocol === 'http:') p.protocol = 'ws:'\n    else if (p.protocol === 'https:') p.protocol = 'wss:'\n    p.pathname = p.pathname.replace(/\\/+/g, '/')\n    if (p.pathname.endsWith('/')) p.pathname = p.pathname.slice(0, -1)\n    if ((p.port === '80' && p.protocol === 'ws:') || (p.port === '443' && p.protocol === 'wss:')) p.port = ''\n    p.searchParams.sort()\n    p.hash = ''\n    return p.toString()\n  } catch (e) {\n    throw new Error(`Invalid URL: ${url}`)\n  }\n}\n\nexport function insertEventIntoDescendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return b.created_at - event.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function insertEventIntoAscendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return event.created_at - b.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function binarySearch<T>(arr: T[], compare: (b: T) => number): [number, boolean] {\n  let start = 0\n  let end = arr.length - 1\n\n  while (start <= end) {\n    const mid = Math.floor((start + end) / 2)\n    const cmp = compare(arr[mid])\n\n    if (cmp === 0) {\n      return [mid, true]\n    }\n\n    if (cmp < 0) {\n      end = mid - 1\n    } else {\n      start = mid + 1\n    }\n  }\n\n  return [start, false]\n}\n\nexport class QueueNode<V> {\n  public value: V\n  public next: QueueNode<V> | null = null\n  public prev: QueueNode<V> | null = null\n\n  constructor(message: V) {\n    this.value = message\n  }\n}\n\nexport class Queue<V> {\n  public first: QueueNode<V> | null\n  public last: QueueNode<V> | null\n\n  constructor() {\n    this.first = null\n    this.last = null\n  }\n\n  enqueue(value: V): boolean {\n    const newNode = new QueueNode(value)\n    if (!this.last) {\n      // list is empty\n      this.first = newNode\n      this.last = newNode\n    } else if (this.last === this.first) {\n      // list has a single element\n      this.last = newNode\n      this.last.prev = this.first\n      this.first.next = newNode\n    } else {\n      // list has elements, add as last\n      newNode.prev = this.last\n      this.last.next = newNode\n      this.last = newNode\n    }\n    return true\n  }\n\n  dequeue(): V | null {\n    if (!this.first) return null\n\n    if (this.first === this.last) {\n      const target = this.first\n      this.first = null\n      this.last = null\n      return target.value\n    }\n\n    const target = this.first\n    this.first = target.next\n    if (this.first) {\n      this.first.prev = null // fix: clean up prev pointer\n    }\n\n    return target.value\n  }\n}\n", "import { NostrEvent, validateEvent } from './pure.ts'\n\n/** Events are **regular**, which means they're all expected to be stored by relays. */\nexport function isRegularKind(kind: number): boolean {\n  return kind < 10000 && kind !== 0 && kind !== 3\n}\n\n/** Events are **replaceable**, which means that, for each combination of `pubkey` and `kind`, only the latest event is expected to (SHOULD) be stored by relays, older versions are expected to be discarded. */\nexport function isReplaceableKind(kind: number): boolean {\n  return kind === 0 || kind === 3 || (10000 <= kind && kind < 20000)\n}\n\n/** Events are **ephemeral**, which means they are not expected to be stored by relays. */\nexport function isEphemeralKind(kind: number): boolean {\n  return 20000 <= kind && kind < 30000\n}\n\n/** Events are **addressable**, which means that, for each combination of `pubkey`, `kind` and the `d` tag, only the latest event is expected to be stored by relays, older versions are expected to be discarded. */\nexport function isAddressableKind(kind: number): boolean {\n  return 30000 <= kind && kind < 40000\n}\n\n/** Classification of the event kind. */\nexport type KindClassification = 'regular' | 'replaceable' | 'ephemeral' | 'parameterized' | 'unknown'\n\n/** Determine the classification of this kind of event if known, or `unknown`. */\nexport function classifyKind(kind: number): KindClassification {\n  if (isRegularKind(kind)) return 'regular'\n  if (isReplaceableKind(kind)) return 'replaceable'\n  if (isEphemeralKind(kind)) return 'ephemeral'\n  if (isAddressableKind(kind)) return 'parameterized'\n  return 'unknown'\n}\n\nexport function isKind<T extends number>(event: unknown, kind: T | Array<T>): event is NostrEvent & { kind: T } {\n  const kindAsArray: number[] = kind instanceof Array ? kind : [kind]\n  return (validateEvent(event) && kindAsArray.includes(event.kind)) || false\n}\n\nexport const Metadata = 0\nexport type Metadata = typeof Metadata\nexport const ShortTextNote = 1\nexport type ShortTextNote = typeof ShortTextNote\nexport const RecommendRelay = 2\nexport type RecommendRelay = typeof RecommendRelay\nexport const Contacts = 3\nexport type Contacts = typeof Contacts\nexport const EncryptedDirectMessage = 4\nexport type EncryptedDirectMessage = typeof EncryptedDirectMessage\nexport const EventDeletion = 5\nexport type EventDeletion = typeof EventDeletion\nexport const Repost = 6\nexport type Repost = typeof Repost\nexport const Reaction = 7\nexport type Reaction = typeof Reaction\nexport const BadgeAward = 8\nexport type BadgeAward = typeof BadgeAward\nexport const ChatMessage = 9\nexport type ChatMessage = typeof ChatMessage\nexport const ForumThread = 11\nexport type ForumThread = typeof ForumThread\nexport const Seal = 13\nexport type Seal = typeof Seal\nexport const PrivateDirectMessage = 14\nexport type PrivateDirectMessage = typeof PrivateDirectMessage\nexport const FileMessage = 15\nexport type FileMessage = typeof FileMessage\nexport const GenericRepost = 16\nexport type GenericRepost = typeof GenericRepost\nexport const Photo = 20\nexport type Photo = typeof Photo\nexport const NormalVideo = 21\nexport type NormalVideo = typeof NormalVideo\nexport const ShortVideo = 22\nexport type ShortVideo = typeof ShortVideo\nexport const ChannelCreation = 40\nexport type ChannelCreation = typeof ChannelCreation\nexport const ChannelMetadata = 41\nexport type ChannelMetadata = typeof ChannelMetadata\nexport const ChannelMessage = 42\nexport type ChannelMessage = typeof ChannelMessage\nexport const ChannelHideMessage = 43\nexport type ChannelHideMessage = typeof ChannelHideMessage\nexport const ChannelMuteUser = 44\nexport type ChannelMuteUser = typeof ChannelMuteUser\nexport const OpenTimestamps = 1040\nexport type OpenTimestamps = typeof OpenTimestamps\nexport const GiftWrap = 1059\nexport type GiftWrap = typeof GiftWrap\nexport const Poll = 1068\nexport type Poll = typeof Poll\nexport const FileMetadata = 1063\nexport type FileMetadata = typeof FileMetadata\nexport const Comment = 1111\nexport type Comment = typeof Comment\nexport const LiveChatMessage = 1311\nexport type LiveChatMessage = typeof LiveChatMessage\nexport const Voice = 1222\nexport type Voice = typeof Voice\nexport const VoiceComment = 1244\nexport type VoiceComment = typeof VoiceComment\nexport const ProblemTracker = 1971\nexport type ProblemTracker = typeof ProblemTracker\nexport const Report = 1984\nexport type Report = typeof Report\nexport const Reporting = 1984\nexport type Reporting = typeof Reporting\nexport const Label = 1985\nexport type Label = typeof Label\nexport const CommunityPostApproval = 4550\nexport type CommunityPostApproval = typeof CommunityPostApproval\nexport const JobRequest = 5999\nexport type JobRequest = typeof JobRequest\nexport const JobResult = 6999\nexport type JobResult = typeof JobResult\nexport const JobFeedback = 7000\nexport type JobFeedback = typeof JobFeedback\nexport const ZapGoal = 9041\nexport type ZapGoal = typeof ZapGoal\nexport const ZapRequest = 9734\nexport type ZapRequest = typeof ZapRequest\nexport const Zap = 9735\nexport type Zap = typeof Zap\nexport const Highlights = 9802\nexport type Highlights = typeof Highlights\nexport const PollResponse = 1018\nexport type PollResponse = typeof PollResponse\nexport const Mutelist = 10000\nexport type Mutelist = typeof Mutelist\nexport const Pinlist = 10001\nexport type Pinlist = typeof Pinlist\nexport const RelayList = 10002\nexport type RelayList = typeof RelayList\nexport const BookmarkList = 10003\nexport type BookmarkList = typeof BookmarkList\nexport const CommunitiesList = 10004\nexport type CommunitiesList = typeof CommunitiesList\nexport const PublicChatsList = 10005\nexport type PublicChatsList = typeof PublicChatsList\nexport const BlockedRelaysList = 10006\nexport type BlockedRelaysList = typeof BlockedRelaysList\nexport const SearchRelaysList = 10007\nexport type SearchRelaysList = typeof SearchRelaysList\nexport const FavoriteRelays = 10012\nexport type FavoriteRelays = typeof FavoriteRelays\nexport const InterestsList = 10015\nexport type InterestsList = typeof InterestsList\nexport const UserEmojiList = 10030\nexport type UserEmojiList = typeof UserEmojiList\nexport const DirectMessageRelaysList = 10050\nexport type DirectMessageRelaysList = typeof DirectMessageRelaysList\nexport const FileServerPreference = 10096\nexport type FileServerPreference = typeof FileServerPreference\nexport const BlossomServerList = 10063\nexport type BlossomServerList = typeof BlossomServerList\nexport const NWCWalletInfo = 13194\nexport type NWCWalletInfo = typeof NWCWalletInfo\nexport const LightningPubRPC = 21000\nexport type LightningPubRPC = typeof LightningPubRPC\nexport const ClientAuth = 22242\nexport type ClientAuth = typeof ClientAuth\nexport const NWCWalletRequest = 23194\nexport type NWCWalletRequest = typeof NWCWalletRequest\nexport const NWCWalletResponse = 23195\nexport type NWCWalletResponse = typeof NWCWalletResponse\nexport const NostrConnect = 24133\nexport type NostrConnect = typeof NostrConnect\nexport const HTTPAuth = 27235\nexport type HTTPAuth = typeof HTTPAuth\nexport const Followsets = 30000\nexport type Followsets = typeof Followsets\nexport const Genericlists = 30001\nexport type Genericlists = typeof Genericlists\nexport const Relaysets = 30002\nexport type Relaysets = typeof Relaysets\nexport const Bookmarksets = 30003\nexport type Bookmarksets = typeof Bookmarksets\nexport const Curationsets = 30004\nexport type Curationsets = typeof Curationsets\nexport const ProfileBadges = 30008\nexport type ProfileBadges = typeof ProfileBadges\nexport const BadgeDefinition = 30009\nexport type BadgeDefinition = typeof BadgeDefinition\nexport const Interestsets = 30015\nexport type Interestsets = typeof Interestsets\nexport const CreateOrUpdateStall = 30017\nexport type CreateOrUpdateStall = typeof CreateOrUpdateStall\nexport const CreateOrUpdateProduct = 30018\nexport type CreateOrUpdateProduct = typeof CreateOrUpdateProduct\nexport const LongFormArticle = 30023\nexport type LongFormArticle = typeof LongFormArticle\nexport const DraftLong = 30024\nexport type DraftLong = typeof DraftLong\nexport const Emojisets = 30030\nexport type Emojisets = typeof Emojisets\nexport const Application = 30078\nexport type Application = typeof Application\nexport const LiveEvent = 30311\nexport type LiveEvent = typeof LiveEvent\nexport const UserStatuses = 30315\nexport type UserStatuses = typeof UserStatuses\nexport const ClassifiedListing = 30402\nexport type ClassifiedListing = typeof ClassifiedListing\nexport const DraftClassifiedListing = 30403\nexport type DraftClassifiedListing = typeof DraftClassifiedListing\nexport const Date = 31922\nexport type Date = typeof Date\nexport const Time = 31923\nexport type Time = typeof Time\nexport const Calendar = 31924\nexport type Calendar = typeof Calendar\nexport const CalendarEventRSVP = 31925\nexport type CalendarEventRSVP = typeof CalendarEventRSVP\nexport const RelayReview = 31987\nexport type RelayReview = typeof RelayReview\nexport const Handlerrecommendation = 31989\nexport type Handlerrecommendation = typeof Handlerrecommendation\nexport const Handlerinformation = 31990\nexport type Handlerinformation = typeof Handlerinformation\nexport const CommunityDefinition = 34550\nexport type CommunityDefinition = typeof CommunityDefinition\nexport const GroupMetadata = 39000\nexport type GroupMetadata = typeof GroupMetadata\n"],
+  "mappings": "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",
   "names": ["import_utils", "i", "i"]
 }
Index: package/lib/esm/nip25.js.map
===================================================================
--- package/lib/esm/nip25.js.map
+++ package/lib/esm/nip25.js.map
@@ -1,7 +1,7 @@
 {
   "version": 3,
   "sources": ["../../pure.ts", "../../core.ts", "../../utils.ts", "../../kinds.ts", "../../nip25.ts"],
-  "sourcesContent": ["import { schnorr } from '@noble/curves/secp256k1'\nimport { bytesToHex } from '@noble/hashes/utils'\nimport { Nostr, Event, EventTemplate, UnsignedEvent, VerifiedEvent, verifiedSymbol, validateEvent } from './core.ts'\nimport { sha256 } from '@noble/hashes/sha256'\n\nimport { utf8Encoder } from './utils.ts'\n\nclass JS implements Nostr {\n  generateSecretKey(): Uint8Array {\n    return schnorr.utils.randomPrivateKey()\n  }\n  getPublicKey(secretKey: Uint8Array): string {\n    return bytesToHex(schnorr.getPublicKey(secretKey))\n  }\n  finalizeEvent(t: EventTemplate, secretKey: Uint8Array): VerifiedEvent {\n    const event = t as VerifiedEvent\n    event.pubkey = bytesToHex(schnorr.getPublicKey(secretKey))\n    event.id = getEventHash(event)\n    event.sig = bytesToHex(schnorr.sign(getEventHash(event), secretKey))\n    event[verifiedSymbol] = true\n    return event\n  }\n  verifyEvent(event: Event): event is VerifiedEvent {\n    if (typeof event[verifiedSymbol] === 'boolean') return event[verifiedSymbol]\n\n    const hash = getEventHash(event)\n    if (hash !== event.id) {\n      event[verifiedSymbol] = false\n      return false\n    }\n\n    try {\n      const valid = schnorr.verify(event.sig, hash, event.pubkey)\n      event[verifiedSymbol] = valid\n      return valid\n    } catch (err) {\n      event[verifiedSymbol] = false\n      return false\n    }\n  }\n}\n\nexport function serializeEvent(evt: UnsignedEvent): string {\n  if (!validateEvent(evt)) throw new Error(\"can't serialize event with wrong or missing properties\")\n  return JSON.stringify([0, evt.pubkey, evt.created_at, evt.kind, evt.tags, evt.content])\n}\n\nexport function getEventHash(event: UnsignedEvent): string {\n  let eventHash = sha256(utf8Encoder.encode(serializeEvent(event)))\n  return bytesToHex(eventHash)\n}\n\nconst i: JS = new JS()\n\nexport const generateSecretKey = i.generateSecretKey\nexport const getPublicKey = i.getPublicKey\nexport const finalizeEvent = i.finalizeEvent\nexport const verifyEvent = i.verifyEvent\nexport * from './core.ts'\n", "export interface Nostr {\n  generateSecretKey(): Uint8Array\n  getPublicKey(secretKey: Uint8Array): string\n  finalizeEvent(event: EventTemplate, secretKey: Uint8Array): VerifiedEvent\n  verifyEvent(event: Event): event is VerifiedEvent\n}\n\n/** Designates a verified event signature. */\nexport const verifiedSymbol = Symbol('verified')\n\nexport type NostrEvent = {\n  kind: number\n  tags: string[][]\n  content: string\n  created_at: number\n  pubkey: string\n  id: string\n  sig: string\n  [verifiedSymbol]?: boolean\n}\n\nexport type Event = NostrEvent\nexport type EventTemplate = Pick<Event, 'kind' | 'tags' | 'content' | 'created_at'>\nexport type UnsignedEvent = Pick<Event, 'kind' | 'tags' | 'content' | 'created_at' | 'pubkey'>\n\n/** An event whose signature has been verified. */\nexport interface VerifiedEvent extends Event {\n  [verifiedSymbol]: true\n}\n\nconst isRecord = (obj: unknown): obj is Record<string, unknown> => obj instanceof Object\n\nexport function validateEvent<T>(event: T): event is T & UnsignedEvent {\n  if (!isRecord(event)) return false\n  if (typeof event.kind !== 'number') return false\n  if (typeof event.content !== 'string') return false\n  if (typeof event.created_at !== 'number') return false\n  if (typeof event.pubkey !== 'string') return false\n  if (!event.pubkey.match(/^[a-f0-9]{64}$/)) return false\n\n  if (!Array.isArray(event.tags)) return false\n  for (let i = 0; i < event.tags.length; i++) {\n    let tag = event.tags[i]\n    if (!Array.isArray(tag)) return false\n    for (let j = 0; j < tag.length; j++) {\n      if (typeof tag[j] !== 'string') return false\n    }\n  }\n\n  return true\n}\n\n/**\n * Sort events in reverse-chronological order by the `created_at` timestamp,\n * and then by the event `id` (lexicographically) in case of ties.\n * This mutates the array.\n */\nexport function sortEvents(events: Event[]): Event[] {\n  return events.sort((a: NostrEvent, b: NostrEvent): number => {\n    if (a.created_at !== b.created_at) {\n      return b.created_at - a.created_at\n    }\n    return a.id.localeCompare(b.id)\n  })\n}\n", "import type { Event } from './core.ts'\n\nexport const utf8Decoder: TextDecoder = new TextDecoder('utf-8')\nexport const utf8Encoder: TextEncoder = new TextEncoder()\n\nexport { bytesToHex, hexToBytes } from '@noble/hashes/utils'\n\nexport function normalizeURL(url: string): string {\n  try {\n    if (url.indexOf('://') === -1) url = 'wss://' + url\n    let p = new URL(url)\n    if (p.protocol === 'http:') p.protocol = 'ws:'\n    else if (p.protocol === 'https:') p.protocol = 'wss:'\n    p.pathname = p.pathname.replace(/\\/+/g, '/')\n    if (p.pathname.endsWith('/')) p.pathname = p.pathname.slice(0, -1)\n    if ((p.port === '80' && p.protocol === 'ws:') || (p.port === '443' && p.protocol === 'wss:')) p.port = ''\n    p.searchParams.sort()\n    p.hash = ''\n    return p.toString()\n  } catch (e) {\n    throw new Error(`Invalid URL: ${url}`)\n  }\n}\n\nexport function insertEventIntoDescendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return b.created_at - event.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function insertEventIntoAscendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return event.created_at - b.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function binarySearch<T>(arr: T[], compare: (b: T) => number): [number, boolean] {\n  let start = 0\n  let end = arr.length - 1\n\n  while (start <= end) {\n    const mid = Math.floor((start + end) / 2)\n    const cmp = compare(arr[mid])\n\n    if (cmp === 0) {\n      return [mid, true]\n    }\n\n    if (cmp < 0) {\n      end = mid - 1\n    } else {\n      start = mid + 1\n    }\n  }\n\n  return [start, false]\n}\n\nexport class QueueNode<V> {\n  public value: V\n  public next: QueueNode<V> | null = null\n  public prev: QueueNode<V> | null = null\n\n  constructor(message: V) {\n    this.value = message\n  }\n}\n\nexport class Queue<V> {\n  public first: QueueNode<V> | null\n  public last: QueueNode<V> | null\n\n  constructor() {\n    this.first = null\n    this.last = null\n  }\n\n  enqueue(value: V): boolean {\n    const newNode = new QueueNode(value)\n    if (!this.last) {\n      // list is empty\n      this.first = newNode\n      this.last = newNode\n    } else if (this.last === this.first) {\n      // list has a single element\n      this.last = newNode\n      this.last.prev = this.first\n      this.first.next = newNode\n    } else {\n      // list has elements, add as last\n      newNode.prev = this.last\n      this.last.next = newNode\n      this.last = newNode\n    }\n    return true\n  }\n\n  dequeue(): V | null {\n    if (!this.first) return null\n\n    if (this.first === this.last) {\n      const target = this.first\n      this.first = null\n      this.last = null\n      return target.value\n    }\n\n    const target = this.first\n    this.first = target.next\n    if (this.first) {\n      this.first.prev = null // fix: clean up prev pointer\n    }\n\n    return target.value\n  }\n}\n", "import { NostrEvent, validateEvent } from './pure.ts'\n\n/** Events are **regular**, which means they're all expected to be stored by relays. */\nexport function isRegularKind(kind: number): boolean {\n  return kind < 10000 && kind !== 0 && kind !== 3\n}\n\n/** Events are **replaceable**, which means that, for each combination of `pubkey` and `kind`, only the latest event is expected to (SHOULD) be stored by relays, older versions are expected to be discarded. */\nexport function isReplaceableKind(kind: number): boolean {\n  return kind === 0 || kind === 3 || (10000 <= kind && kind < 20000)\n}\n\n/** Events are **ephemeral**, which means they are not expected to be stored by relays. */\nexport function isEphemeralKind(kind: number): boolean {\n  return 20000 <= kind && kind < 30000\n}\n\n/** Events are **addressable**, which means that, for each combination of `pubkey`, `kind` and the `d` tag, only the latest event is expected to be stored by relays, older versions are expected to be discarded. */\nexport function isAddressableKind(kind: number): boolean {\n  return 30000 <= kind && kind < 40000\n}\n\n/** Classification of the event kind. */\nexport type KindClassification = 'regular' | 'replaceable' | 'ephemeral' | 'parameterized' | 'unknown'\n\n/** Determine the classification of this kind of event if known, or `unknown`. */\nexport function classifyKind(kind: number): KindClassification {\n  if (isRegularKind(kind)) return 'regular'\n  if (isReplaceableKind(kind)) return 'replaceable'\n  if (isEphemeralKind(kind)) return 'ephemeral'\n  if (isAddressableKind(kind)) return 'parameterized'\n  return 'unknown'\n}\n\nexport function isKind<T extends number>(event: unknown, kind: T | Array<T>): event is NostrEvent & { kind: T } {\n  const kindAsArray: number[] = kind instanceof Array ? kind : [kind]\n  return (validateEvent(event) && kindAsArray.includes(event.kind)) || false\n}\n\nexport const Metadata = 0\nexport type Metadata = typeof Metadata\nexport const ShortTextNote = 1\nexport type ShortTextNote = typeof ShortTextNote\nexport const RecommendRelay = 2\nexport type RecommendRelay = typeof RecommendRelay\nexport const Contacts = 3\nexport type Contacts = typeof Contacts\nexport const EncryptedDirectMessage = 4\nexport type EncryptedDirectMessage = typeof EncryptedDirectMessage\nexport const EventDeletion = 5\nexport type EventDeletion = typeof EventDeletion\nexport const Repost = 6\nexport type Repost = typeof Repost\nexport const Reaction = 7\nexport type Reaction = typeof Reaction\nexport const BadgeAward = 8\nexport type BadgeAward = typeof BadgeAward\nexport const ChatMessage = 9\nexport type ChatMessage = typeof ChatMessage\nexport const ForumThread = 11\nexport type ForumThread = typeof ForumThread\nexport const Seal = 13\nexport type Seal = typeof Seal\nexport const PrivateDirectMessage = 14\nexport type PrivateDirectMessage = typeof PrivateDirectMessage\nexport const FileMessage = 15\nexport type FileMessage = typeof FileMessage\nexport const GenericRepost = 16\nexport type GenericRepost = typeof GenericRepost\nexport const Photo = 20\nexport type Photo = typeof Photo\nexport const NormalVideo = 21\nexport type NormalVideo = typeof NormalVideo\nexport const ShortVideo = 22\nexport type ShortVideo = typeof ShortVideo\nexport const ChannelCreation = 40\nexport type ChannelCreation = typeof ChannelCreation\nexport const ChannelMetadata = 41\nexport type ChannelMetadata = typeof ChannelMetadata\nexport const ChannelMessage = 42\nexport type ChannelMessage = typeof ChannelMessage\nexport const ChannelHideMessage = 43\nexport type ChannelHideMessage = typeof ChannelHideMessage\nexport const ChannelMuteUser = 44\nexport type ChannelMuteUser = typeof ChannelMuteUser\nexport const OpenTimestamps = 1040\nexport type OpenTimestamps = typeof OpenTimestamps\nexport const GiftWrap = 1059\nexport type GiftWrap = typeof GiftWrap\nexport const Poll = 1068\nexport type Poll = typeof Poll\nexport const FileMetadata = 1063\nexport type FileMetadata = typeof FileMetadata\nexport const Comment = 1111\nexport type Comment = typeof Comment\nexport const LiveChatMessage = 1311\nexport type LiveChatMessage = typeof LiveChatMessage\nexport const Voice = 1222\nexport type Voice = typeof Voice\nexport const VoiceComment = 1244\nexport type VoiceComment = typeof VoiceComment\nexport const ProblemTracker = 1971\nexport type ProblemTracker = typeof ProblemTracker\nexport const Report = 1984\nexport type Report = typeof Report\nexport const Reporting = 1984\nexport type Reporting = typeof Reporting\nexport const Label = 1985\nexport type Label = typeof Label\nexport const CommunityPostApproval = 4550\nexport type CommunityPostApproval = typeof CommunityPostApproval\nexport const JobRequest = 5999\nexport type JobRequest = typeof JobRequest\nexport const JobResult = 6999\nexport type JobResult = typeof JobResult\nexport const JobFeedback = 7000\nexport type JobFeedback = typeof JobFeedback\nexport const ZapGoal = 9041\nexport type ZapGoal = typeof ZapGoal\nexport const ZapRequest = 9734\nexport type ZapRequest = typeof ZapRequest\nexport const Zap = 9735\nexport type Zap = typeof Zap\nexport const Highlights = 9802\nexport type Highlights = typeof Highlights\nexport const PollResponse = 1018\nexport type PollResponse = typeof PollResponse\nexport const Mutelist = 10000\nexport type Mutelist = typeof Mutelist\nexport const Pinlist = 10001\nexport type Pinlist = typeof Pinlist\nexport const RelayList = 10002\nexport type RelayList = typeof RelayList\nexport const BookmarkList = 10003\nexport type BookmarkList = typeof BookmarkList\nexport const CommunitiesList = 10004\nexport type CommunitiesList = typeof CommunitiesList\nexport const PublicChatsList = 10005\nexport type PublicChatsList = typeof PublicChatsList\nexport const BlockedRelaysList = 10006\nexport type BlockedRelaysList = typeof BlockedRelaysList\nexport const SearchRelaysList = 10007\nexport type SearchRelaysList = typeof SearchRelaysList\nexport const FavoriteRelays = 10012\nexport type FavoriteRelays = typeof FavoriteRelays\nexport const InterestsList = 10015\nexport type InterestsList = typeof InterestsList\nexport const UserEmojiList = 10030\nexport type UserEmojiList = typeof UserEmojiList\nexport const DirectMessageRelaysList = 10050\nexport type DirectMessageRelaysList = typeof DirectMessageRelaysList\nexport const FileServerPreference = 10096\nexport type FileServerPreference = typeof FileServerPreference\nexport const BlossomServerList = 10063\nexport type BlossomServerList = typeof BlossomServerList\nexport const NWCWalletInfo = 13194\nexport type NWCWalletInfo = typeof NWCWalletInfo\nexport const LightningPubRPC = 21000\nexport type LightningPubRPC = typeof LightningPubRPC\nexport const ClientAuth = 22242\nexport type ClientAuth = typeof ClientAuth\nexport const NWCWalletRequest = 23194\nexport type NWCWalletRequest = typeof NWCWalletRequest\nexport const NWCWalletResponse = 23195\nexport type NWCWalletResponse = typeof NWCWalletResponse\nexport const NostrConnect = 24133\nexport type NostrConnect = typeof NostrConnect\nexport const HTTPAuth = 27235\nexport type HTTPAuth = typeof HTTPAuth\nexport const Followsets = 30000\nexport type Followsets = typeof Followsets\nexport const Genericlists = 30001\nexport type Genericlists = typeof Genericlists\nexport const Relaysets = 30002\nexport type Relaysets = typeof Relaysets\nexport const Bookmarksets = 30003\nexport type Bookmarksets = typeof Bookmarksets\nexport const Curationsets = 30004\nexport type Curationsets = typeof Curationsets\nexport const ProfileBadges = 30008\nexport type ProfileBadges = typeof ProfileBadges\nexport const BadgeDefinition = 30009\nexport type BadgeDefinition = typeof BadgeDefinition\nexport const Interestsets = 30015\nexport type Interestsets = typeof Interestsets\nexport const CreateOrUpdateStall = 30017\nexport type CreateOrUpdateStall = typeof CreateOrUpdateStall\nexport const CreateOrUpdateProduct = 30018\nexport type CreateOrUpdateProduct = typeof CreateOrUpdateProduct\nexport const LongFormArticle = 30023\nexport type LongFormArticle = typeof LongFormArticle\nexport const DraftLong = 30024\nexport type DraftLong = typeof DraftLong\nexport const Emojisets = 30030\nexport type Emojisets = typeof Emojisets\nexport const Application = 30078\nexport type Application = typeof Application\nexport const LiveEvent = 30311\nexport type LiveEvent = typeof LiveEvent\nexport const UserStatuses = 30315\nexport type UserStatuses = typeof UserStatuses\nexport const ClassifiedListing = 30402\nexport type ClassifiedListing = typeof ClassifiedListing\nexport const DraftClassifiedListing = 30403\nexport type DraftClassifiedListing = typeof DraftClassifiedListing\nexport const Date = 31922\nexport type Date = typeof Date\nexport const Time = 31923\nexport type Time = typeof Time\nexport const Calendar = 31924\nexport type Calendar = typeof Calendar\nexport const CalendarEventRSVP = 31925\nexport type CalendarEventRSVP = typeof CalendarEventRSVP\nexport const RelayReview = 31987\nexport type RelayReview = typeof RelayReview\nexport const Handlerrecommendation = 31989\nexport type Handlerrecommendation = typeof Handlerrecommendation\nexport const Handlerinformation = 31990\nexport type Handlerinformation = typeof Handlerinformation\nexport const CommunityDefinition = 34550\nexport type CommunityDefinition = typeof CommunityDefinition\nexport const GroupMetadata = 39000\nexport type GroupMetadata = typeof GroupMetadata\n", "import { Event, finalizeEvent } from './pure.ts'\nimport { Reaction } from './kinds.ts'\n\nimport type { EventPointer } from './nip19.ts'\n\nexport type ReactionEventTemplate = {\n  /**\n   * Pass only non-nip25 tags if you have to. Nip25 tags ('e' and 'p' tags from reacted event) will be added automatically.\n   */\n  tags?: string[][]\n\n  /**\n   * @default '+'\n   */\n  content?: string\n\n  created_at: number\n}\n\nexport function finishReactionEvent(t: ReactionEventTemplate, reacted: Event, privateKey: Uint8Array): Event {\n  const inheritedTags = reacted.tags.filter(tag => tag.length >= 2 && (tag[0] === 'e' || tag[0] === 'p'))\n\n  return finalizeEvent(\n    {\n      ...t,\n      kind: Reaction,\n      tags: [...(t.tags ?? []), ...inheritedTags, ['e', reacted.id], ['p', reacted.pubkey]],\n      content: t.content ?? '+',\n    },\n    privateKey,\n  )\n}\n\nexport function getReactedEventPointer(event: Event): undefined | EventPointer {\n  if (event.kind !== Reaction) {\n    return undefined\n  }\n\n  let lastETag: undefined | string[]\n  let lastPTag: undefined | string[]\n\n  for (let i = event.tags.length - 1; i >= 0 && (lastETag === undefined || lastPTag === undefined); i--) {\n    const tag = event.tags[i]\n    if (tag.length >= 2) {\n      if (tag[0] === 'e' && lastETag === undefined) {\n        lastETag = tag\n      } else if (tag[0] === 'p' && lastPTag === undefined) {\n        lastPTag = tag\n      }\n    }\n  }\n\n  if (lastETag === undefined || lastPTag === undefined) {\n    return undefined\n  }\n\n  return {\n    id: lastETag[1],\n    relays: [lastETag[2], lastPTag[2]].filter(x => x !== undefined),\n    author: lastPTag[1],\n  }\n}\n"],
-  "mappings": "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",
-  "names": ["bytesToHex", "i", "bytesToHex", "i"]
+  "sourcesContent": ["import { schnorr } from '@noble/curves/secp256k1.js'\nimport { bytesToHex, hexToBytes } from '@noble/hashes/utils.js'\nimport { Nostr, Event, EventTemplate, UnsignedEvent, VerifiedEvent, verifiedSymbol, validateEvent } from './core.ts'\nimport { sha256 } from '@noble/hashes/sha2.js'\n\nimport { utf8Encoder } from './utils.ts'\n\nclass JS implements Nostr {\n  generateSecretKey(): Uint8Array {\n    return schnorr.utils.randomSecretKey()\n  }\n  getPublicKey(secretKey: Uint8Array): string {\n    return bytesToHex(schnorr.getPublicKey(secretKey))\n  }\n  finalizeEvent(t: EventTemplate, secretKey: Uint8Array): VerifiedEvent {\n    const event = t as VerifiedEvent\n    event.pubkey = bytesToHex(schnorr.getPublicKey(secretKey))\n    event.id = getEventHash(event)\n    event.sig = bytesToHex(schnorr.sign(hexToBytes(getEventHash(event)), secretKey))\n    event[verifiedSymbol] = true\n    return event\n  }\n  verifyEvent(event: Event): event is VerifiedEvent {\n    if (typeof event[verifiedSymbol] === 'boolean') return event[verifiedSymbol]\n\n    const hash = getEventHash(event)\n    if (hash !== event.id) {\n      event[verifiedSymbol] = false\n      return false\n    }\n\n    try {\n      const valid = schnorr.verify(hexToBytes(event.sig), hexToBytes(hash), hexToBytes(event.pubkey))\n      event[verifiedSymbol] = valid\n      return valid\n    } catch (err) {\n      event[verifiedSymbol] = false\n      return false\n    }\n  }\n}\n\nexport function serializeEvent(evt: UnsignedEvent): string {\n  if (!validateEvent(evt)) throw new Error(\"can't serialize event with wrong or missing properties\")\n  return JSON.stringify([0, evt.pubkey, evt.created_at, evt.kind, evt.tags, evt.content])\n}\n\nexport function getEventHash(event: UnsignedEvent): string {\n  let eventHash = sha256(utf8Encoder.encode(serializeEvent(event)))\n  return bytesToHex(eventHash)\n}\n\nconst i: JS = new JS()\n\nexport const generateSecretKey = i.generateSecretKey\nexport const getPublicKey = i.getPublicKey\nexport const finalizeEvent = i.finalizeEvent\nexport const verifyEvent = i.verifyEvent\nexport * from './core.ts'\n", "export interface Nostr {\n  generateSecretKey(): Uint8Array\n  getPublicKey(secretKey: Uint8Array): string\n  finalizeEvent(event: EventTemplate, secretKey: Uint8Array): VerifiedEvent\n  verifyEvent(event: Event): event is VerifiedEvent\n}\n\n/** Designates a verified event signature. */\nexport const verifiedSymbol = Symbol('verified')\n\nexport type NostrEvent = {\n  kind: number\n  tags: string[][]\n  content: string\n  created_at: number\n  pubkey: string\n  id: string\n  sig: string\n  [verifiedSymbol]?: boolean\n}\n\nexport type Event = NostrEvent\nexport type EventTemplate = Pick<Event, 'kind' | 'tags' | 'content' | 'created_at'>\nexport type UnsignedEvent = Pick<Event, 'kind' | 'tags' | 'content' | 'created_at' | 'pubkey'>\n\n/** An event whose signature has been verified. */\nexport interface VerifiedEvent extends Event {\n  [verifiedSymbol]: true\n}\n\nconst isRecord = (obj: unknown): obj is Record<string, unknown> => obj instanceof Object\n\nexport function validateEvent<T>(event: T): event is T & UnsignedEvent {\n  if (!isRecord(event)) return false\n  if (typeof event.kind !== 'number') return false\n  if (typeof event.content !== 'string') return false\n  if (typeof event.created_at !== 'number') return false\n  if (typeof event.pubkey !== 'string') return false\n  if (!event.pubkey.match(/^[a-f0-9]{64}$/)) return false\n\n  if (!Array.isArray(event.tags)) return false\n  for (let i = 0; i < event.tags.length; i++) {\n    let tag = event.tags[i]\n    if (!Array.isArray(tag)) return false\n    for (let j = 0; j < tag.length; j++) {\n      if (typeof tag[j] !== 'string') return false\n    }\n  }\n\n  return true\n}\n\n/**\n * Sort events in reverse-chronological order by the `created_at` timestamp,\n * and then by the event `id` (lexicographically) in case of ties.\n * This mutates the array.\n */\nexport function sortEvents(events: Event[]): Event[] {\n  return events.sort((a: NostrEvent, b: NostrEvent): number => {\n    if (a.created_at !== b.created_at) {\n      return b.created_at - a.created_at\n    }\n    return a.id.localeCompare(b.id)\n  })\n}\n", "import type { Event } from './core.ts'\n\nexport const utf8Decoder: TextDecoder = new TextDecoder('utf-8')\nexport const utf8Encoder: TextEncoder = new TextEncoder()\n\nexport { bytesToHex, hexToBytes } from '@noble/hashes/utils.js'\n\nexport function normalizeURL(url: string): string {\n  try {\n    if (url.indexOf('://') === -1) url = 'wss://' + url\n    let p = new URL(url)\n    if (p.protocol === 'http:') p.protocol = 'ws:'\n    else if (p.protocol === 'https:') p.protocol = 'wss:'\n    p.pathname = p.pathname.replace(/\\/+/g, '/')\n    if (p.pathname.endsWith('/')) p.pathname = p.pathname.slice(0, -1)\n    if ((p.port === '80' && p.protocol === 'ws:') || (p.port === '443' && p.protocol === 'wss:')) p.port = ''\n    p.searchParams.sort()\n    p.hash = ''\n    return p.toString()\n  } catch (e) {\n    throw new Error(`Invalid URL: ${url}`)\n  }\n}\n\nexport function insertEventIntoDescendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return b.created_at - event.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function insertEventIntoAscendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return event.created_at - b.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function binarySearch<T>(arr: T[], compare: (b: T) => number): [number, boolean] {\n  let start = 0\n  let end = arr.length - 1\n\n  while (start <= end) {\n    const mid = Math.floor((start + end) / 2)\n    const cmp = compare(arr[mid])\n\n    if (cmp === 0) {\n      return [mid, true]\n    }\n\n    if (cmp < 0) {\n      end = mid - 1\n    } else {\n      start = mid + 1\n    }\n  }\n\n  return [start, false]\n}\n\nexport class QueueNode<V> {\n  public value: V\n  public next: QueueNode<V> | null = null\n  public prev: QueueNode<V> | null = null\n\n  constructor(message: V) {\n    this.value = message\n  }\n}\n\nexport class Queue<V> {\n  public first: QueueNode<V> | null\n  public last: QueueNode<V> | null\n\n  constructor() {\n    this.first = null\n    this.last = null\n  }\n\n  enqueue(value: V): boolean {\n    const newNode = new QueueNode(value)\n    if (!this.last) {\n      // list is empty\n      this.first = newNode\n      this.last = newNode\n    } else if (this.last === this.first) {\n      // list has a single element\n      this.last = newNode\n      this.last.prev = this.first\n      this.first.next = newNode\n    } else {\n      // list has elements, add as last\n      newNode.prev = this.last\n      this.last.next = newNode\n      this.last = newNode\n    }\n    return true\n  }\n\n  dequeue(): V | null {\n    if (!this.first) return null\n\n    if (this.first === this.last) {\n      const target = this.first\n      this.first = null\n      this.last = null\n      return target.value\n    }\n\n    const target = this.first\n    this.first = target.next\n    if (this.first) {\n      this.first.prev = null // fix: clean up prev pointer\n    }\n\n    return target.value\n  }\n}\n", "import { NostrEvent, validateEvent } from './pure.ts'\n\n/** Events are **regular**, which means they're all expected to be stored by relays. */\nexport function isRegularKind(kind: number): boolean {\n  return kind < 10000 && kind !== 0 && kind !== 3\n}\n\n/** Events are **replaceable**, which means that, for each combination of `pubkey` and `kind`, only the latest event is expected to (SHOULD) be stored by relays, older versions are expected to be discarded. */\nexport function isReplaceableKind(kind: number): boolean {\n  return kind === 0 || kind === 3 || (10000 <= kind && kind < 20000)\n}\n\n/** Events are **ephemeral**, which means they are not expected to be stored by relays. */\nexport function isEphemeralKind(kind: number): boolean {\n  return 20000 <= kind && kind < 30000\n}\n\n/** Events are **addressable**, which means that, for each combination of `pubkey`, `kind` and the `d` tag, only the latest event is expected to be stored by relays, older versions are expected to be discarded. */\nexport function isAddressableKind(kind: number): boolean {\n  return 30000 <= kind && kind < 40000\n}\n\n/** Classification of the event kind. */\nexport type KindClassification = 'regular' | 'replaceable' | 'ephemeral' | 'parameterized' | 'unknown'\n\n/** Determine the classification of this kind of event if known, or `unknown`. */\nexport function classifyKind(kind: number): KindClassification {\n  if (isRegularKind(kind)) return 'regular'\n  if (isReplaceableKind(kind)) return 'replaceable'\n  if (isEphemeralKind(kind)) return 'ephemeral'\n  if (isAddressableKind(kind)) return 'parameterized'\n  return 'unknown'\n}\n\nexport function isKind<T extends number>(event: unknown, kind: T | Array<T>): event is NostrEvent & { kind: T } {\n  const kindAsArray: number[] = kind instanceof Array ? kind : [kind]\n  return (validateEvent(event) && kindAsArray.includes(event.kind)) || false\n}\n\nexport const Metadata = 0\nexport type Metadata = typeof Metadata\nexport const ShortTextNote = 1\nexport type ShortTextNote = typeof ShortTextNote\nexport const RecommendRelay = 2\nexport type RecommendRelay = typeof RecommendRelay\nexport const Contacts = 3\nexport type Contacts = typeof Contacts\nexport const EncryptedDirectMessage = 4\nexport type EncryptedDirectMessage = typeof EncryptedDirectMessage\nexport const EventDeletion = 5\nexport type EventDeletion = typeof EventDeletion\nexport const Repost = 6\nexport type Repost = typeof Repost\nexport const Reaction = 7\nexport type Reaction = typeof Reaction\nexport const BadgeAward = 8\nexport type BadgeAward = typeof BadgeAward\nexport const ChatMessage = 9\nexport type ChatMessage = typeof ChatMessage\nexport const ForumThread = 11\nexport type ForumThread = typeof ForumThread\nexport const Seal = 13\nexport type Seal = typeof Seal\nexport const PrivateDirectMessage = 14\nexport type PrivateDirectMessage = typeof PrivateDirectMessage\nexport const FileMessage = 15\nexport type FileMessage = typeof FileMessage\nexport const GenericRepost = 16\nexport type GenericRepost = typeof GenericRepost\nexport const Photo = 20\nexport type Photo = typeof Photo\nexport const NormalVideo = 21\nexport type NormalVideo = typeof NormalVideo\nexport const ShortVideo = 22\nexport type ShortVideo = typeof ShortVideo\nexport const ChannelCreation = 40\nexport type ChannelCreation = typeof ChannelCreation\nexport const ChannelMetadata = 41\nexport type ChannelMetadata = typeof ChannelMetadata\nexport const ChannelMessage = 42\nexport type ChannelMessage = typeof ChannelMessage\nexport const ChannelHideMessage = 43\nexport type ChannelHideMessage = typeof ChannelHideMessage\nexport const ChannelMuteUser = 44\nexport type ChannelMuteUser = typeof ChannelMuteUser\nexport const OpenTimestamps = 1040\nexport type OpenTimestamps = typeof OpenTimestamps\nexport const GiftWrap = 1059\nexport type GiftWrap = typeof GiftWrap\nexport const Poll = 1068\nexport type Poll = typeof Poll\nexport const FileMetadata = 1063\nexport type FileMetadata = typeof FileMetadata\nexport const Comment = 1111\nexport type Comment = typeof Comment\nexport const LiveChatMessage = 1311\nexport type LiveChatMessage = typeof LiveChatMessage\nexport const Voice = 1222\nexport type Voice = typeof Voice\nexport const VoiceComment = 1244\nexport type VoiceComment = typeof VoiceComment\nexport const ProblemTracker = 1971\nexport type ProblemTracker = typeof ProblemTracker\nexport const Report = 1984\nexport type Report = typeof Report\nexport const Reporting = 1984\nexport type Reporting = typeof Reporting\nexport const Label = 1985\nexport type Label = typeof Label\nexport const CommunityPostApproval = 4550\nexport type CommunityPostApproval = typeof CommunityPostApproval\nexport const JobRequest = 5999\nexport type JobRequest = typeof JobRequest\nexport const JobResult = 6999\nexport type JobResult = typeof JobResult\nexport const JobFeedback = 7000\nexport type JobFeedback = typeof JobFeedback\nexport const ZapGoal = 9041\nexport type ZapGoal = typeof ZapGoal\nexport const ZapRequest = 9734\nexport type ZapRequest = typeof ZapRequest\nexport const Zap = 9735\nexport type Zap = typeof Zap\nexport const Highlights = 9802\nexport type Highlights = typeof Highlights\nexport const PollResponse = 1018\nexport type PollResponse = typeof PollResponse\nexport const Mutelist = 10000\nexport type Mutelist = typeof Mutelist\nexport const Pinlist = 10001\nexport type Pinlist = typeof Pinlist\nexport const RelayList = 10002\nexport type RelayList = typeof RelayList\nexport const BookmarkList = 10003\nexport type BookmarkList = typeof BookmarkList\nexport const CommunitiesList = 10004\nexport type CommunitiesList = typeof CommunitiesList\nexport const PublicChatsList = 10005\nexport type PublicChatsList = typeof PublicChatsList\nexport const BlockedRelaysList = 10006\nexport type BlockedRelaysList = typeof BlockedRelaysList\nexport const SearchRelaysList = 10007\nexport type SearchRelaysList = typeof SearchRelaysList\nexport const FavoriteRelays = 10012\nexport type FavoriteRelays = typeof FavoriteRelays\nexport const InterestsList = 10015\nexport type InterestsList = typeof InterestsList\nexport const UserEmojiList = 10030\nexport type UserEmojiList = typeof UserEmojiList\nexport const DirectMessageRelaysList = 10050\nexport type DirectMessageRelaysList = typeof DirectMessageRelaysList\nexport const FileServerPreference = 10096\nexport type FileServerPreference = typeof FileServerPreference\nexport const BlossomServerList = 10063\nexport type BlossomServerList = typeof BlossomServerList\nexport const NWCWalletInfo = 13194\nexport type NWCWalletInfo = typeof NWCWalletInfo\nexport const LightningPubRPC = 21000\nexport type LightningPubRPC = typeof LightningPubRPC\nexport const ClientAuth = 22242\nexport type ClientAuth = typeof ClientAuth\nexport const NWCWalletRequest = 23194\nexport type NWCWalletRequest = typeof NWCWalletRequest\nexport const NWCWalletResponse = 23195\nexport type NWCWalletResponse = typeof NWCWalletResponse\nexport const NostrConnect = 24133\nexport type NostrConnect = typeof NostrConnect\nexport const HTTPAuth = 27235\nexport type HTTPAuth = typeof HTTPAuth\nexport const Followsets = 30000\nexport type Followsets = typeof Followsets\nexport const Genericlists = 30001\nexport type Genericlists = typeof Genericlists\nexport const Relaysets = 30002\nexport type Relaysets = typeof Relaysets\nexport const Bookmarksets = 30003\nexport type Bookmarksets = typeof Bookmarksets\nexport const Curationsets = 30004\nexport type Curationsets = typeof Curationsets\nexport const ProfileBadges = 30008\nexport type ProfileBadges = typeof ProfileBadges\nexport const BadgeDefinition = 30009\nexport type BadgeDefinition = typeof BadgeDefinition\nexport const Interestsets = 30015\nexport type Interestsets = typeof Interestsets\nexport const CreateOrUpdateStall = 30017\nexport type CreateOrUpdateStall = typeof CreateOrUpdateStall\nexport const CreateOrUpdateProduct = 30018\nexport type CreateOrUpdateProduct = typeof CreateOrUpdateProduct\nexport const LongFormArticle = 30023\nexport type LongFormArticle = typeof LongFormArticle\nexport const DraftLong = 30024\nexport type DraftLong = typeof DraftLong\nexport const Emojisets = 30030\nexport type Emojisets = typeof Emojisets\nexport const Application = 30078\nexport type Application = typeof Application\nexport const LiveEvent = 30311\nexport type LiveEvent = typeof LiveEvent\nexport const UserStatuses = 30315\nexport type UserStatuses = typeof UserStatuses\nexport const ClassifiedListing = 30402\nexport type ClassifiedListing = typeof ClassifiedListing\nexport const DraftClassifiedListing = 30403\nexport type DraftClassifiedListing = typeof DraftClassifiedListing\nexport const Date = 31922\nexport type Date = typeof Date\nexport const Time = 31923\nexport type Time = typeof Time\nexport const Calendar = 31924\nexport type Calendar = typeof Calendar\nexport const CalendarEventRSVP = 31925\nexport type CalendarEventRSVP = typeof CalendarEventRSVP\nexport const RelayReview = 31987\nexport type RelayReview = typeof RelayReview\nexport const Handlerrecommendation = 31989\nexport type Handlerrecommendation = typeof Handlerrecommendation\nexport const Handlerinformation = 31990\nexport type Handlerinformation = typeof Handlerinformation\nexport const CommunityDefinition = 34550\nexport type CommunityDefinition = typeof CommunityDefinition\nexport const GroupMetadata = 39000\nexport type GroupMetadata = typeof GroupMetadata\n", "import { Event, finalizeEvent } from './pure.ts'\nimport { Reaction } from './kinds.ts'\n\nimport type { EventPointer } from './nip19.ts'\n\nexport type ReactionEventTemplate = {\n  /**\n   * Pass only non-nip25 tags if you have to. Nip25 tags ('e' and 'p' tags from reacted event) will be added automatically.\n   */\n  tags?: string[][]\n\n  /**\n   * @default '+'\n   */\n  content?: string\n\n  created_at: number\n}\n\nexport function finishReactionEvent(t: ReactionEventTemplate, reacted: Event, privateKey: Uint8Array): Event {\n  const inheritedTags = reacted.tags.filter(tag => tag.length >= 2 && (tag[0] === 'e' || tag[0] === 'p'))\n\n  return finalizeEvent(\n    {\n      ...t,\n      kind: Reaction,\n      tags: [...(t.tags ?? []), ...inheritedTags, ['e', reacted.id], ['p', reacted.pubkey]],\n      content: t.content ?? '+',\n    },\n    privateKey,\n  )\n}\n\nexport function getReactedEventPointer(event: Event): undefined | EventPointer {\n  if (event.kind !== Reaction) {\n    return undefined\n  }\n\n  let lastETag: undefined | string[]\n  let lastPTag: undefined | string[]\n\n  for (let i = event.tags.length - 1; i >= 0 && (lastETag === undefined || lastPTag === undefined); i--) {\n    const tag = event.tags[i]\n    if (tag.length >= 2) {\n      if (tag[0] === 'e' && lastETag === undefined) {\n        lastETag = tag\n      } else if (tag[0] === 'p' && lastPTag === undefined) {\n        lastPTag = tag\n      }\n    }\n  }\n\n  if (lastETag === undefined || lastPTag === undefined) {\n    return undefined\n  }\n\n  return {\n    id: lastETag[1],\n    relays: [lastETag[2], lastPTag[2]].filter(x => x !== undefined),\n    author: lastPTag[1],\n  }\n}\n"],
+  "mappings": "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",
+  "names": ["bytesToHex", "hexToBytes", "i", "bytesToHex", "hexToBytes", "i"]
 }
Index: package/lib/cjs/nip27.js.map
===================================================================
--- package/lib/cjs/nip27.js.map
+++ package/lib/cjs/nip27.js.map
@@ -1,7 +1,7 @@
 {
   "version": 3,
   "sources": ["../../nip27.ts", "../../nip19.ts", "../../utils.ts"],
-  "sourcesContent": ["import { NostrEvent } from './core.ts'\nimport { AddressPointer, EventPointer, ProfilePointer, decode } from './nip19.ts'\n\nexport type Block =\n  | {\n      type: 'text'\n      text: string\n    }\n  | {\n      type: 'reference'\n      pointer: ProfilePointer | AddressPointer | EventPointer\n    }\n  | {\n      type: 'url'\n      url: string\n    }\n  | {\n      type: 'relay'\n      url: string\n    }\n  | {\n      type: 'image'\n      url: string\n    }\n  | {\n      type: 'video'\n      url: string\n    }\n  | {\n      type: 'audio'\n      url: string\n    }\n  | {\n      type: 'emoji'\n      shortcode: string\n      url: string\n    }\n  | {\n      type: 'hashtag'\n      value: string\n    }\n\nconst noCharacter = /\\W/m\nconst noURLCharacter = /[^\\w\\/] |[^\\w\\/]$|$|,| /m\nconst MAX_HASHTAG_LENGTH = 42\n\nexport function* parse(content: string | NostrEvent): Iterable<Block> {\n  let emojis: { type: 'emoji'; shortcode: string; url: string }[] = []\n  if (typeof content !== 'string') {\n    for (let i = 0; i < content.tags.length; i++) {\n      const tag = content.tags[i]\n      if (tag[0] === 'emoji' && tag.length >= 3) {\n        emojis.push({ type: 'emoji', shortcode: tag[1], url: tag[2] })\n      }\n    }\n    content = content.content\n  }\n\n  const max = content.length\n  let prevIndex = 0\n  let index = 0\n  mainloop: while (index < max) {\n    const u = content.indexOf(':', index)\n    const h = content.indexOf('#', index)\n    if (u === -1 && h === -1) {\n      // reached end\n      break mainloop\n    }\n\n    if (u === -1 || (h >= 0 && h < u)) {\n      // parse hashtag\n      if (h === 0 || content[h - 1] === ' ') {\n        const m = content.slice(h + 1, h + MAX_HASHTAG_LENGTH).match(noCharacter)\n        const end = m ? h + 1 + m.index! : max\n        yield { type: 'text', text: content.slice(prevIndex, h) }\n        yield { type: 'hashtag', value: content.slice(h + 1, end) }\n        index = end\n        prevIndex = index\n        continue mainloop\n      }\n\n      // ignore this, it is nothing\n      index = h + 1\n      continue mainloop\n    }\n\n    // otherwise parse things that have an \":\"\n    if (content.slice(u - 5, u) === 'nostr') {\n      const m = content.slice(u + 60).match(noCharacter)\n      const end = m ? u + 60 + m.index! : max\n      try {\n        let pointer: ProfilePointer | AddressPointer | EventPointer\n        let { data, type } = decode(content.slice(u + 1, end))\n\n        switch (type) {\n          case 'npub':\n            pointer = { pubkey: data } as ProfilePointer\n            break\n          case 'note':\n            pointer = { id: data } as EventPointer\n            break\n          case 'nsec':\n            // ignore this, treat it as not a valid uri\n            index = end + 1\n            continue\n          default:\n            pointer = data as any\n        }\n\n        if (prevIndex !== u - 5) {\n          yield { type: 'text', text: content.slice(prevIndex, u - 5) }\n        }\n        yield { type: 'reference', pointer }\n        index = end\n        prevIndex = index\n        continue mainloop\n      } catch (_err) {\n        // ignore this, not a valid nostr uri\n        index = u + 1\n        continue mainloop\n      }\n    } else if (content.slice(u - 5, u) === 'https' || content.slice(u - 4, u) === 'http') {\n      const m = content.slice(u + 4).match(noURLCharacter)\n      const end = m ? u + 4 + m.index! : max\n      const prefixLen = content[u - 1] === 's' ? 5 : 4\n      try {\n        let url = new URL(content.slice(u - prefixLen, end))\n        if (url.hostname.indexOf('.') === -1) {\n          throw new Error('invalid url')\n        }\n\n        if (prevIndex !== u - prefixLen) {\n          yield { type: 'text', text: content.slice(prevIndex, u - prefixLen) }\n        }\n\n        if (/\\.(png|jpe?g|gif|webp|heic|svg)$/i.test(url.pathname)) {\n          yield { type: 'image', url: url.toString() }\n          index = end\n          prevIndex = index\n          continue mainloop\n        }\n        if (/\\.(mp4|avi|webm|mkv|mov)$/i.test(url.pathname)) {\n          yield { type: 'video', url: url.toString() }\n          index = end\n          prevIndex = index\n          continue mainloop\n        }\n        if (/\\.(mp3|aac|ogg|opus|wav|flac)$/i.test(url.pathname)) {\n          yield { type: 'audio', url: url.toString() }\n          index = end\n          prevIndex = index\n          continue mainloop\n        }\n\n        yield { type: 'url', url: url.toString() }\n        index = end\n        prevIndex = index\n        continue mainloop\n      } catch (_err) {\n        // ignore this, not a valid url\n        index = end + 1\n        continue mainloop\n      }\n    } else if (content.slice(u - 3, u) === 'wss' || content.slice(u - 2, u) === 'ws') {\n      const m = content.slice(u + 4).match(noURLCharacter)\n      const end = m ? u + 4 + m.index! : max\n      const prefixLen = content[u - 1] === 's' ? 3 : 2\n      try {\n        let url = new URL(content.slice(u - prefixLen, end))\n        if (url.hostname.indexOf('.') === -1) {\n          throw new Error('invalid ws url')\n        }\n\n        if (prevIndex !== u - prefixLen) {\n          yield { type: 'text', text: content.slice(prevIndex, u - prefixLen) }\n        }\n        yield { type: 'relay', url: url.toString() }\n        index = end\n        prevIndex = index\n        continue mainloop\n      } catch (_err) {\n        // ignore this, not a valid url\n        index = end + 1\n        continue mainloop\n      }\n    } else {\n      // try to parse an emoji shortcode\n      for (let e = 0; e < emojis.length; e++) {\n        const emoji = emojis[e]\n        if (\n          content[u + emoji.shortcode.length + 1] === ':' &&\n          content.slice(u + 1, u + emoji.shortcode.length + 1) === emoji.shortcode\n        ) {\n          // found an emoji\n          if (prevIndex !== u) {\n            yield { type: 'text', text: content.slice(prevIndex, u) }\n          }\n          yield emoji\n          index = u + emoji.shortcode.length + 2\n          prevIndex = index\n          continue mainloop\n        }\n      }\n\n      // ignore this, it is nothing\n      index = u + 1\n      continue mainloop\n    }\n  }\n\n  if (prevIndex !== max) {\n    yield { type: 'text', text: content.slice(prevIndex) }\n  }\n}\n", "import { bytesToHex, concatBytes, hexToBytes } from '@noble/hashes/utils'\nimport { bech32 } from '@scure/base'\n\nimport { utf8Decoder, utf8Encoder } from './utils.ts'\n\nexport type NProfile = `nprofile1${string}`\nexport type NEvent = `nevent1${string}`\nexport type NAddr = `naddr1${string}`\nexport type NSec = `nsec1${string}`\nexport type NPub = `npub1${string}`\nexport type Note = `note1${string}`\nexport type Ncryptsec = `ncryptsec1${string}`\n\nexport const NostrTypeGuard = {\n  isNProfile: (value?: string | null): value is NProfile => /^nprofile1[a-z\\d]+$/.test(value || ''),\n  isNEvent: (value?: string | null): value is NEvent => /^nevent1[a-z\\d]+$/.test(value || ''),\n  isNAddr: (value?: string | null): value is NAddr => /^naddr1[a-z\\d]+$/.test(value || ''),\n  isNSec: (value?: string | null): value is NSec => /^nsec1[a-z\\d]{58}$/.test(value || ''),\n  isNPub: (value?: string | null): value is NPub => /^npub1[a-z\\d]{58}$/.test(value || ''),\n  isNote: (value?: string | null): value is Note => /^note1[a-z\\d]+$/.test(value || ''),\n  isNcryptsec: (value?: string | null): value is Ncryptsec => /^ncryptsec1[a-z\\d]+$/.test(value || ''),\n}\n\nexport const Bech32MaxSize = 5000\n\n/**\n * Bech32 regex.\n * @see https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki#bech32\n */\nexport const BECH32_REGEX = /[\\x21-\\x7E]{1,83}1[023456789acdefghjklmnpqrstuvwxyz]{6,}/\n\nfunction integerToUint8Array(number: number) {\n  // Create a Uint8Array with enough space to hold a 32-bit integer (4 bytes).\n  const uint8Array = new Uint8Array(4)\n\n  // Use bitwise operations to extract the bytes.\n  uint8Array[0] = (number >> 24) & 0xff // Most significant byte (MSB)\n  uint8Array[1] = (number >> 16) & 0xff\n  uint8Array[2] = (number >> 8) & 0xff\n  uint8Array[3] = number & 0xff // Least significant byte (LSB)\n\n  return uint8Array\n}\n\nexport type ProfilePointer = {\n  pubkey: string // hex\n  relays?: string[]\n}\n\nexport type EventPointer = {\n  id: string // hex\n  relays?: string[]\n  author?: string\n  kind?: number\n}\n\nexport type AddressPointer = {\n  identifier: string\n  pubkey: string\n  kind: number\n  relays?: string[]\n}\n\nexport function decodeNostrURI(nip19code: string): ReturnType<typeof decode> | { type: 'invalid'; data: null } {\n  try {\n    if (nip19code.startsWith('nostr:')) nip19code = nip19code.substring(6)\n    return decode(nip19code)\n  } catch (_err) {\n    return { type: 'invalid', data: null }\n  }\n}\n\nexport type DecodedNevent = {\n  type: 'nevent'\n  data: EventPointer\n}\n\nexport type DecodedNprofile = {\n  type: 'nprofile'\n  data: ProfilePointer\n}\n\nexport type DecodedNaddr = {\n  type: 'naddr'\n  data: AddressPointer\n}\n\nexport type DecodedNsec = {\n  type: 'nsec'\n  data: Uint8Array\n}\n\nexport type DecodedNpub = {\n  type: 'npub'\n  data: string\n}\n\nexport type DecodedNote = {\n  type: 'note'\n  data: string\n}\n\nexport type DecodedResult = DecodedNevent | DecodedNprofile | DecodedNaddr | DecodedNpub | DecodedNsec | DecodedNote\n\nexport function decode(nip19: NEvent): DecodedNevent\nexport function decode(nip19: NProfile): DecodedNprofile\nexport function decode(nip19: NAddr): DecodedNaddr\nexport function decode(nip19: NSec): DecodedNsec\nexport function decode(nip19: NPub): DecodedNpub\nexport function decode(nip19: Note): DecodedNote\nexport function decode(code: string): DecodedResult\nexport function decode(code: string): DecodedResult {\n  let { prefix, words } = bech32.decode(code, Bech32MaxSize)\n  let data = new Uint8Array(bech32.fromWords(words))\n\n  switch (prefix) {\n    case 'nprofile': {\n      let tlv = parseTLV(data)\n      if (!tlv[0]?.[0]) throw new Error('missing TLV 0 for nprofile')\n      if (tlv[0][0].length !== 32) throw new Error('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      let tlv = parseTLV(data)\n      if (!tlv[0]?.[0]) throw new Error('missing TLV 0 for nevent')\n      if (tlv[0][0].length !== 32) throw new Error('TLV 0 should be 32 bytes')\n      if (tlv[2] && tlv[2][0].length !== 32) throw new Error('TLV 2 should be 32 bytes')\n      if (tlv[3] && tlv[3][0].length !== 4) throw new Error('TLV 3 should be 4 bytes')\n\n      return {\n        type: 'nevent',\n        data: {\n          id: bytesToHex(tlv[0][0]),\n          relays: tlv[1] ? tlv[1].map(d => utf8Decoder.decode(d)) : [],\n          author: tlv[2]?.[0] ? bytesToHex(tlv[2][0]) : undefined,\n          kind: tlv[3]?.[0] ? parseInt(bytesToHex(tlv[3][0]), 16) : undefined,\n        },\n      }\n    }\n\n    case 'naddr': {\n      let tlv = parseTLV(data)\n      if (!tlv[0]?.[0]) throw new Error('missing TLV 0 for naddr')\n      if (!tlv[2]?.[0]) throw new Error('missing TLV 2 for naddr')\n      if (tlv[2][0].length !== 32) throw new Error('TLV 2 should be 32 bytes')\n      if (!tlv[3]?.[0]) throw new Error('missing TLV 3 for naddr')\n      if (tlv[3][0].length !== 4) throw new Error('TLV 3 should be 4 bytes')\n\n      return {\n        type: 'naddr',\n        data: {\n          identifier: utf8Decoder.decode(tlv[0][0]),\n          pubkey: bytesToHex(tlv[2][0]),\n          kind: parseInt(bytesToHex(tlv[3][0]), 16),\n          relays: tlv[1] ? tlv[1].map(d => utf8Decoder.decode(d)) : [],\n        },\n      }\n    }\n\n    case 'nsec':\n      return { type: prefix, data }\n\n    case 'npub':\n    case 'note':\n      return { type: prefix, data: bytesToHex(data) }\n\n    default:\n      throw new Error(`unknown prefix ${prefix}`)\n  }\n}\n\ntype TLV = { [t: number]: Uint8Array[] }\n\nfunction parseTLV(data: Uint8Array): TLV {\n  let result: TLV = {}\n  let rest = data\n  while (rest.length > 0) {\n    let t = rest[0]\n    let l = rest[1]\n    let v = rest.slice(2, 2 + l)\n    rest = rest.slice(2 + l)\n    if (v.length < l) throw new Error(`not enough data to read on TLV ${t}`)\n    result[t] = result[t] || []\n    result[t].push(v)\n  }\n  return result\n}\n\nexport function nsecEncode(key: Uint8Array): NSec {\n  return encodeBytes('nsec', key)\n}\n\nexport function npubEncode(hex: string): NPub {\n  return encodeBytes('npub', hexToBytes(hex))\n}\n\nexport function noteEncode(hex: string): Note {\n  return encodeBytes('note', hexToBytes(hex))\n}\n\nfunction encodeBech32<Prefix extends string>(prefix: Prefix, data: Uint8Array): `${Prefix}1${string}` {\n  let words = bech32.toWords(data)\n  return bech32.encode(prefix, words, Bech32MaxSize) as `${Prefix}1${string}`\n}\n\nexport function encodeBytes<Prefix extends string>(prefix: Prefix, bytes: Uint8Array): `${Prefix}1${string}` {\n  return encodeBech32(prefix, bytes)\n}\n\nexport function nprofileEncode(profile: ProfilePointer): NProfile {\n  let data = encodeTLV({\n    0: [hexToBytes(profile.pubkey)],\n    1: (profile.relays || []).map(url => utf8Encoder.encode(url)),\n  })\n  return encodeBech32('nprofile', data)\n}\n\nexport function neventEncode(event: EventPointer): NEvent {\n  let kindArray\n  if (event.kind !== undefined) {\n    kindArray = integerToUint8Array(event.kind)\n  }\n\n  let data = encodeTLV({\n    0: [hexToBytes(event.id)],\n    1: (event.relays || []).map(url => utf8Encoder.encode(url)),\n    2: event.author ? [hexToBytes(event.author)] : [],\n    3: kindArray ? [new Uint8Array(kindArray)] : [],\n  })\n\n  return encodeBech32('nevent', data)\n}\n\nexport function naddrEncode(addr: AddressPointer): NAddr {\n  let kind = new ArrayBuffer(4)\n  new DataView(kind).setUint32(0, addr.kind, false)\n\n  let data = encodeTLV({\n    0: [utf8Encoder.encode(addr.identifier)],\n    1: (addr.relays || []).map(url => utf8Encoder.encode(url)),\n    2: [hexToBytes(addr.pubkey)],\n    3: [new Uint8Array(kind)],\n  })\n  return encodeBech32('naddr', data)\n}\n\nfunction encodeTLV(tlv: TLV): Uint8Array {\n  let entries: Uint8Array[] = []\n\n  Object.entries(tlv)\n    .reverse()\n    .forEach(([t, vs]) => {\n      vs.forEach(v => {\n        let entry = new Uint8Array(v.length + 2)\n        entry.set([parseInt(t)], 0)\n        entry.set([v.length], 1)\n        entry.set(v, 2)\n        entries.push(entry)\n      })\n    })\n\n  return concatBytes(...entries)\n}\n", "import type { Event } from './core.ts'\n\nexport const utf8Decoder: TextDecoder = new TextDecoder('utf-8')\nexport const utf8Encoder: TextEncoder = new TextEncoder()\n\nexport { bytesToHex, hexToBytes } from '@noble/hashes/utils'\n\nexport function normalizeURL(url: string): string {\n  try {\n    if (url.indexOf('://') === -1) url = 'wss://' + url\n    let p = new URL(url)\n    if (p.protocol === 'http:') p.protocol = 'ws:'\n    else if (p.protocol === 'https:') p.protocol = 'wss:'\n    p.pathname = p.pathname.replace(/\\/+/g, '/')\n    if (p.pathname.endsWith('/')) p.pathname = p.pathname.slice(0, -1)\n    if ((p.port === '80' && p.protocol === 'ws:') || (p.port === '443' && p.protocol === 'wss:')) p.port = ''\n    p.searchParams.sort()\n    p.hash = ''\n    return p.toString()\n  } catch (e) {\n    throw new Error(`Invalid URL: ${url}`)\n  }\n}\n\nexport function insertEventIntoDescendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return b.created_at - event.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function insertEventIntoAscendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return event.created_at - b.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function binarySearch<T>(arr: T[], compare: (b: T) => number): [number, boolean] {\n  let start = 0\n  let end = arr.length - 1\n\n  while (start <= end) {\n    const mid = Math.floor((start + end) / 2)\n    const cmp = compare(arr[mid])\n\n    if (cmp === 0) {\n      return [mid, true]\n    }\n\n    if (cmp < 0) {\n      end = mid - 1\n    } else {\n      start = mid + 1\n    }\n  }\n\n  return [start, false]\n}\n\nexport class QueueNode<V> {\n  public value: V\n  public next: QueueNode<V> | null = null\n  public prev: QueueNode<V> | null = null\n\n  constructor(message: V) {\n    this.value = message\n  }\n}\n\nexport class Queue<V> {\n  public first: QueueNode<V> | null\n  public last: QueueNode<V> | null\n\n  constructor() {\n    this.first = null\n    this.last = null\n  }\n\n  enqueue(value: V): boolean {\n    const newNode = new QueueNode(value)\n    if (!this.last) {\n      // list is empty\n      this.first = newNode\n      this.last = newNode\n    } else if (this.last === this.first) {\n      // list has a single element\n      this.last = newNode\n      this.last.prev = this.first\n      this.first.next = newNode\n    } else {\n      // list has elements, add as last\n      newNode.prev = this.last\n      this.last.next = newNode\n      this.last = newNode\n    }\n    return true\n  }\n\n  dequeue(): V | null {\n    if (!this.first) return null\n\n    if (this.first === this.last) {\n      const target = this.first\n      this.first = null\n      this.last = null\n      return target.value\n    }\n\n    const target = this.first\n    this.first = target.next\n    if (this.first) {\n      this.first.prev = null // fix: clean up prev pointer\n    }\n\n    return target.value\n  }\n}\n"],
-  "mappings": "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",
+  "sourcesContent": ["import { NostrEvent } from './core.ts'\nimport { AddressPointer, EventPointer, ProfilePointer, decode } from './nip19.ts'\n\nexport type Block =\n  | {\n      type: 'text'\n      text: string\n    }\n  | {\n      type: 'reference'\n      pointer: ProfilePointer | AddressPointer | EventPointer\n    }\n  | {\n      type: 'url'\n      url: string\n    }\n  | {\n      type: 'relay'\n      url: string\n    }\n  | {\n      type: 'image'\n      url: string\n    }\n  | {\n      type: 'video'\n      url: string\n    }\n  | {\n      type: 'audio'\n      url: string\n    }\n  | {\n      type: 'emoji'\n      shortcode: string\n      url: string\n    }\n  | {\n      type: 'hashtag'\n      value: string\n    }\n\nconst noCharacter = /\\W/m\nconst noURLCharacter = /[^\\w\\/] |[^\\w\\/]$|$|,| /m\nconst MAX_HASHTAG_LENGTH = 42\n\nexport function* parse(content: string | NostrEvent): Iterable<Block> {\n  let emojis: { type: 'emoji'; shortcode: string; url: string }[] = []\n  if (typeof content !== 'string') {\n    for (let i = 0; i < content.tags.length; i++) {\n      const tag = content.tags[i]\n      if (tag[0] === 'emoji' && tag.length >= 3) {\n        emojis.push({ type: 'emoji', shortcode: tag[1], url: tag[2] })\n      }\n    }\n    content = content.content\n  }\n\n  const max = content.length\n  let prevIndex = 0\n  let index = 0\n  mainloop: while (index < max) {\n    const u = content.indexOf(':', index)\n    const h = content.indexOf('#', index)\n    if (u === -1 && h === -1) {\n      // reached end\n      break mainloop\n    }\n\n    if (u === -1 || (h >= 0 && h < u)) {\n      // parse hashtag\n      if (h === 0 || content[h - 1] === ' ') {\n        const m = content.slice(h + 1, h + MAX_HASHTAG_LENGTH).match(noCharacter)\n        const end = m ? h + 1 + m.index! : max\n        yield { type: 'text', text: content.slice(prevIndex, h) }\n        yield { type: 'hashtag', value: content.slice(h + 1, end) }\n        index = end\n        prevIndex = index\n        continue mainloop\n      }\n\n      // ignore this, it is nothing\n      index = h + 1\n      continue mainloop\n    }\n\n    // otherwise parse things that have an \":\"\n    if (content.slice(u - 5, u) === 'nostr') {\n      const m = content.slice(u + 60).match(noCharacter)\n      const end = m ? u + 60 + m.index! : max\n      try {\n        let pointer: ProfilePointer | AddressPointer | EventPointer\n        let { data, type } = decode(content.slice(u + 1, end))\n\n        switch (type) {\n          case 'npub':\n            pointer = { pubkey: data } as ProfilePointer\n            break\n          case 'note':\n            pointer = { id: data } as EventPointer\n            break\n          case 'nsec':\n            // ignore this, treat it as not a valid uri\n            index = end + 1\n            continue\n          default:\n            pointer = data as any\n        }\n\n        if (prevIndex !== u - 5) {\n          yield { type: 'text', text: content.slice(prevIndex, u - 5) }\n        }\n        yield { type: 'reference', pointer }\n        index = end\n        prevIndex = index\n        continue mainloop\n      } catch (_err) {\n        // ignore this, not a valid nostr uri\n        index = u + 1\n        continue mainloop\n      }\n    } else if (content.slice(u - 5, u) === 'https' || content.slice(u - 4, u) === 'http') {\n      const m = content.slice(u + 4).match(noURLCharacter)\n      const end = m ? u + 4 + m.index! : max\n      const prefixLen = content[u - 1] === 's' ? 5 : 4\n      try {\n        let url = new URL(content.slice(u - prefixLen, end))\n        if (url.hostname.indexOf('.') === -1) {\n          throw new Error('invalid url')\n        }\n\n        if (prevIndex !== u - prefixLen) {\n          yield { type: 'text', text: content.slice(prevIndex, u - prefixLen) }\n        }\n\n        if (/\\.(png|jpe?g|gif|webp|heic|svg)$/i.test(url.pathname)) {\n          yield { type: 'image', url: url.toString() }\n          index = end\n          prevIndex = index\n          continue mainloop\n        }\n        if (/\\.(mp4|avi|webm|mkv|mov)$/i.test(url.pathname)) {\n          yield { type: 'video', url: url.toString() }\n          index = end\n          prevIndex = index\n          continue mainloop\n        }\n        if (/\\.(mp3|aac|ogg|opus|wav|flac)$/i.test(url.pathname)) {\n          yield { type: 'audio', url: url.toString() }\n          index = end\n          prevIndex = index\n          continue mainloop\n        }\n\n        yield { type: 'url', url: url.toString() }\n        index = end\n        prevIndex = index\n        continue mainloop\n      } catch (_err) {\n        // ignore this, not a valid url\n        index = end + 1\n        continue mainloop\n      }\n    } else if (content.slice(u - 3, u) === 'wss' || content.slice(u - 2, u) === 'ws') {\n      const m = content.slice(u + 4).match(noURLCharacter)\n      const end = m ? u + 4 + m.index! : max\n      const prefixLen = content[u - 1] === 's' ? 3 : 2\n      try {\n        let url = new URL(content.slice(u - prefixLen, end))\n        if (url.hostname.indexOf('.') === -1) {\n          throw new Error('invalid ws url')\n        }\n\n        if (prevIndex !== u - prefixLen) {\n          yield { type: 'text', text: content.slice(prevIndex, u - prefixLen) }\n        }\n        yield { type: 'relay', url: url.toString() }\n        index = end\n        prevIndex = index\n        continue mainloop\n      } catch (_err) {\n        // ignore this, not a valid url\n        index = end + 1\n        continue mainloop\n      }\n    } else {\n      // try to parse an emoji shortcode\n      for (let e = 0; e < emojis.length; e++) {\n        const emoji = emojis[e]\n        if (\n          content[u + emoji.shortcode.length + 1] === ':' &&\n          content.slice(u + 1, u + emoji.shortcode.length + 1) === emoji.shortcode\n        ) {\n          // found an emoji\n          if (prevIndex !== u) {\n            yield { type: 'text', text: content.slice(prevIndex, u) }\n          }\n          yield emoji\n          index = u + emoji.shortcode.length + 2\n          prevIndex = index\n          continue mainloop\n        }\n      }\n\n      // ignore this, it is nothing\n      index = u + 1\n      continue mainloop\n    }\n  }\n\n  if (prevIndex !== max) {\n    yield { type: 'text', text: content.slice(prevIndex) }\n  }\n}\n", "import { bytesToHex, concatBytes, hexToBytes } from '@noble/hashes/utils.js'\nimport { bech32 } from '@scure/base'\n\nimport { utf8Decoder, utf8Encoder } from './utils.ts'\n\nexport type NProfile = `nprofile1${string}`\nexport type NEvent = `nevent1${string}`\nexport type NAddr = `naddr1${string}`\nexport type NSec = `nsec1${string}`\nexport type NPub = `npub1${string}`\nexport type Note = `note1${string}`\nexport type Ncryptsec = `ncryptsec1${string}`\n\nexport const NostrTypeGuard = {\n  isNProfile: (value?: string | null): value is NProfile => /^nprofile1[a-z\\d]+$/.test(value || ''),\n  isNEvent: (value?: string | null): value is NEvent => /^nevent1[a-z\\d]+$/.test(value || ''),\n  isNAddr: (value?: string | null): value is NAddr => /^naddr1[a-z\\d]+$/.test(value || ''),\n  isNSec: (value?: string | null): value is NSec => /^nsec1[a-z\\d]{58}$/.test(value || ''),\n  isNPub: (value?: string | null): value is NPub => /^npub1[a-z\\d]{58}$/.test(value || ''),\n  isNote: (value?: string | null): value is Note => /^note1[a-z\\d]+$/.test(value || ''),\n  isNcryptsec: (value?: string | null): value is Ncryptsec => /^ncryptsec1[a-z\\d]+$/.test(value || ''),\n}\n\nexport const Bech32MaxSize = 5000\n\n/**\n * Bech32 regex.\n * @see https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki#bech32\n */\nexport const BECH32_REGEX = /[\\x21-\\x7E]{1,83}1[023456789acdefghjklmnpqrstuvwxyz]{6,}/\n\nfunction integerToUint8Array(number: number) {\n  // Create a Uint8Array with enough space to hold a 32-bit integer (4 bytes).\n  const uint8Array = new Uint8Array(4)\n\n  // Use bitwise operations to extract the bytes.\n  uint8Array[0] = (number >> 24) & 0xff // Most significant byte (MSB)\n  uint8Array[1] = (number >> 16) & 0xff\n  uint8Array[2] = (number >> 8) & 0xff\n  uint8Array[3] = number & 0xff // Least significant byte (LSB)\n\n  return uint8Array\n}\n\nexport type ProfilePointer = {\n  pubkey: string // hex\n  relays?: string[]\n}\n\nexport type EventPointer = {\n  id: string // hex\n  relays?: string[]\n  author?: string\n  kind?: number\n}\n\nexport type AddressPointer = {\n  identifier: string\n  pubkey: string\n  kind: number\n  relays?: string[]\n}\n\nexport function decodeNostrURI(nip19code: string): ReturnType<typeof decode> | { type: 'invalid'; data: null } {\n  try {\n    if (nip19code.startsWith('nostr:')) nip19code = nip19code.substring(6)\n    return decode(nip19code)\n  } catch (_err) {\n    return { type: 'invalid', data: null }\n  }\n}\n\nexport type DecodedNevent = {\n  type: 'nevent'\n  data: EventPointer\n}\n\nexport type DecodedNprofile = {\n  type: 'nprofile'\n  data: ProfilePointer\n}\n\nexport type DecodedNaddr = {\n  type: 'naddr'\n  data: AddressPointer\n}\n\nexport type DecodedNsec = {\n  type: 'nsec'\n  data: Uint8Array\n}\n\nexport type DecodedNpub = {\n  type: 'npub'\n  data: string\n}\n\nexport type DecodedNote = {\n  type: 'note'\n  data: string\n}\n\nexport type DecodedResult = DecodedNevent | DecodedNprofile | DecodedNaddr | DecodedNpub | DecodedNsec | DecodedNote\n\nexport function decode(nip19: NEvent): DecodedNevent\nexport function decode(nip19: NProfile): DecodedNprofile\nexport function decode(nip19: NAddr): DecodedNaddr\nexport function decode(nip19: NSec): DecodedNsec\nexport function decode(nip19: NPub): DecodedNpub\nexport function decode(nip19: Note): DecodedNote\nexport function decode(code: string): DecodedResult\nexport function decode(code: string): DecodedResult {\n  let { prefix, words } = bech32.decode(code as `${string}1${string}`, Bech32MaxSize)\n  let data = new Uint8Array(bech32.fromWords(words))\n\n  switch (prefix) {\n    case 'nprofile': {\n      let tlv = parseTLV(data)\n      if (!tlv[0]?.[0]) throw new Error('missing TLV 0 for nprofile')\n      if (tlv[0][0].length !== 32) throw new Error('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      let tlv = parseTLV(data)\n      if (!tlv[0]?.[0]) throw new Error('missing TLV 0 for nevent')\n      if (tlv[0][0].length !== 32) throw new Error('TLV 0 should be 32 bytes')\n      if (tlv[2] && tlv[2][0].length !== 32) throw new Error('TLV 2 should be 32 bytes')\n      if (tlv[3] && tlv[3][0].length !== 4) throw new Error('TLV 3 should be 4 bytes')\n\n      return {\n        type: 'nevent',\n        data: {\n          id: bytesToHex(tlv[0][0]),\n          relays: tlv[1] ? tlv[1].map(d => utf8Decoder.decode(d)) : [],\n          author: tlv[2]?.[0] ? bytesToHex(tlv[2][0]) : undefined,\n          kind: tlv[3]?.[0] ? parseInt(bytesToHex(tlv[3][0]), 16) : undefined,\n        },\n      }\n    }\n\n    case 'naddr': {\n      let tlv = parseTLV(data)\n      if (!tlv[0]?.[0]) throw new Error('missing TLV 0 for naddr')\n      if (!tlv[2]?.[0]) throw new Error('missing TLV 2 for naddr')\n      if (tlv[2][0].length !== 32) throw new Error('TLV 2 should be 32 bytes')\n      if (!tlv[3]?.[0]) throw new Error('missing TLV 3 for naddr')\n      if (tlv[3][0].length !== 4) throw new Error('TLV 3 should be 4 bytes')\n\n      return {\n        type: 'naddr',\n        data: {\n          identifier: utf8Decoder.decode(tlv[0][0]),\n          pubkey: bytesToHex(tlv[2][0]),\n          kind: parseInt(bytesToHex(tlv[3][0]), 16),\n          relays: tlv[1] ? tlv[1].map(d => utf8Decoder.decode(d)) : [],\n        },\n      }\n    }\n\n    case 'nsec':\n      return { type: prefix, data }\n\n    case 'npub':\n    case 'note':\n      return { type: prefix, data: bytesToHex(data) }\n\n    default:\n      throw new Error(`unknown prefix ${prefix}`)\n  }\n}\n\ntype TLV = { [t: number]: Uint8Array[] }\n\nfunction parseTLV(data: Uint8Array): TLV {\n  let result: TLV = {}\n  let rest = data\n  while (rest.length > 0) {\n    let t = rest[0]\n    let l = rest[1]\n    let v = rest.slice(2, 2 + l)\n    rest = rest.slice(2 + l)\n    if (v.length < l) throw new Error(`not enough data to read on TLV ${t}`)\n    result[t] = result[t] || []\n    result[t].push(v)\n  }\n  return result\n}\n\nexport function nsecEncode(key: Uint8Array): NSec {\n  return encodeBytes('nsec', key)\n}\n\nexport function npubEncode(hex: string): NPub {\n  return encodeBytes('npub', hexToBytes(hex))\n}\n\nexport function noteEncode(hex: string): Note {\n  return encodeBytes('note', hexToBytes(hex))\n}\n\nfunction encodeBech32<Prefix extends string>(prefix: Prefix, data: Uint8Array): `${Prefix}1${string}` {\n  let words = bech32.toWords(data)\n  return bech32.encode(prefix, words, Bech32MaxSize) as `${Prefix}1${string}`\n}\n\nexport function encodeBytes<Prefix extends string>(prefix: Prefix, bytes: Uint8Array): `${Prefix}1${string}` {\n  return encodeBech32(prefix, bytes)\n}\n\nexport function nprofileEncode(profile: ProfilePointer): NProfile {\n  let data = encodeTLV({\n    0: [hexToBytes(profile.pubkey)],\n    1: (profile.relays || []).map(url => utf8Encoder.encode(url)),\n  })\n  return encodeBech32('nprofile', data)\n}\n\nexport function neventEncode(event: EventPointer): NEvent {\n  let kindArray\n  if (event.kind !== undefined) {\n    kindArray = integerToUint8Array(event.kind)\n  }\n\n  let data = encodeTLV({\n    0: [hexToBytes(event.id)],\n    1: (event.relays || []).map(url => utf8Encoder.encode(url)),\n    2: event.author ? [hexToBytes(event.author)] : [],\n    3: kindArray ? [new Uint8Array(kindArray)] : [],\n  })\n\n  return encodeBech32('nevent', data)\n}\n\nexport function naddrEncode(addr: AddressPointer): NAddr {\n  let kind = new ArrayBuffer(4)\n  new DataView(kind).setUint32(0, addr.kind, false)\n\n  let data = encodeTLV({\n    0: [utf8Encoder.encode(addr.identifier)],\n    1: (addr.relays || []).map(url => utf8Encoder.encode(url)),\n    2: [hexToBytes(addr.pubkey)],\n    3: [new Uint8Array(kind)],\n  })\n  return encodeBech32('naddr', data)\n}\n\nfunction encodeTLV(tlv: TLV): Uint8Array {\n  let entries: Uint8Array[] = []\n\n  Object.entries(tlv)\n    .reverse()\n    .forEach(([t, vs]) => {\n      vs.forEach(v => {\n        let entry = new Uint8Array(v.length + 2)\n        entry.set([parseInt(t)], 0)\n        entry.set([v.length], 1)\n        entry.set(v, 2)\n        entries.push(entry)\n      })\n    })\n\n  return concatBytes(...entries)\n}\n", "import type { Event } from './core.ts'\n\nexport const utf8Decoder: TextDecoder = new TextDecoder('utf-8')\nexport const utf8Encoder: TextEncoder = new TextEncoder()\n\nexport { bytesToHex, hexToBytes } from '@noble/hashes/utils.js'\n\nexport function normalizeURL(url: string): string {\n  try {\n    if (url.indexOf('://') === -1) url = 'wss://' + url\n    let p = new URL(url)\n    if (p.protocol === 'http:') p.protocol = 'ws:'\n    else if (p.protocol === 'https:') p.protocol = 'wss:'\n    p.pathname = p.pathname.replace(/\\/+/g, '/')\n    if (p.pathname.endsWith('/')) p.pathname = p.pathname.slice(0, -1)\n    if ((p.port === '80' && p.protocol === 'ws:') || (p.port === '443' && p.protocol === 'wss:')) p.port = ''\n    p.searchParams.sort()\n    p.hash = ''\n    return p.toString()\n  } catch (e) {\n    throw new Error(`Invalid URL: ${url}`)\n  }\n}\n\nexport function insertEventIntoDescendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return b.created_at - event.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function insertEventIntoAscendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return event.created_at - b.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function binarySearch<T>(arr: T[], compare: (b: T) => number): [number, boolean] {\n  let start = 0\n  let end = arr.length - 1\n\n  while (start <= end) {\n    const mid = Math.floor((start + end) / 2)\n    const cmp = compare(arr[mid])\n\n    if (cmp === 0) {\n      return [mid, true]\n    }\n\n    if (cmp < 0) {\n      end = mid - 1\n    } else {\n      start = mid + 1\n    }\n  }\n\n  return [start, false]\n}\n\nexport class QueueNode<V> {\n  public value: V\n  public next: QueueNode<V> | null = null\n  public prev: QueueNode<V> | null = null\n\n  constructor(message: V) {\n    this.value = message\n  }\n}\n\nexport class Queue<V> {\n  public first: QueueNode<V> | null\n  public last: QueueNode<V> | null\n\n  constructor() {\n    this.first = null\n    this.last = null\n  }\n\n  enqueue(value: V): boolean {\n    const newNode = new QueueNode(value)\n    if (!this.last) {\n      // list is empty\n      this.first = newNode\n      this.last = newNode\n    } else if (this.last === this.first) {\n      // list has a single element\n      this.last = newNode\n      this.last.prev = this.first\n      this.first.next = newNode\n    } else {\n      // list has elements, add as last\n      newNode.prev = this.last\n      this.last.next = newNode\n      this.last = newNode\n    }\n    return true\n  }\n\n  dequeue(): V | null {\n    if (!this.first) return null\n\n    if (this.first === this.last) {\n      const target = this.first\n      this.first = null\n      this.last = null\n      return target.value\n    }\n\n    const target = this.first\n    this.first = target.next\n    if (this.first) {\n      this.first.prev = null // fix: clean up prev pointer\n    }\n\n    return target.value\n  }\n}\n"],
+  "mappings": "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",
   "names": ["import_utils"]
 }
Index: package/lib/esm/nip27.js.map
===================================================================
--- package/lib/esm/nip27.js.map
+++ package/lib/esm/nip27.js.map
@@ -1,7 +1,7 @@
 {
   "version": 3,
   "sources": ["../../nip19.ts", "../../utils.ts", "../../nip27.ts"],
-  "sourcesContent": ["import { bytesToHex, concatBytes, hexToBytes } from '@noble/hashes/utils'\nimport { bech32 } from '@scure/base'\n\nimport { utf8Decoder, utf8Encoder } from './utils.ts'\n\nexport type NProfile = `nprofile1${string}`\nexport type NEvent = `nevent1${string}`\nexport type NAddr = `naddr1${string}`\nexport type NSec = `nsec1${string}`\nexport type NPub = `npub1${string}`\nexport type Note = `note1${string}`\nexport type Ncryptsec = `ncryptsec1${string}`\n\nexport const NostrTypeGuard = {\n  isNProfile: (value?: string | null): value is NProfile => /^nprofile1[a-z\\d]+$/.test(value || ''),\n  isNEvent: (value?: string | null): value is NEvent => /^nevent1[a-z\\d]+$/.test(value || ''),\n  isNAddr: (value?: string | null): value is NAddr => /^naddr1[a-z\\d]+$/.test(value || ''),\n  isNSec: (value?: string | null): value is NSec => /^nsec1[a-z\\d]{58}$/.test(value || ''),\n  isNPub: (value?: string | null): value is NPub => /^npub1[a-z\\d]{58}$/.test(value || ''),\n  isNote: (value?: string | null): value is Note => /^note1[a-z\\d]+$/.test(value || ''),\n  isNcryptsec: (value?: string | null): value is Ncryptsec => /^ncryptsec1[a-z\\d]+$/.test(value || ''),\n}\n\nexport const Bech32MaxSize = 5000\n\n/**\n * Bech32 regex.\n * @see https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki#bech32\n */\nexport const BECH32_REGEX = /[\\x21-\\x7E]{1,83}1[023456789acdefghjklmnpqrstuvwxyz]{6,}/\n\nfunction integerToUint8Array(number: number) {\n  // Create a Uint8Array with enough space to hold a 32-bit integer (4 bytes).\n  const uint8Array = new Uint8Array(4)\n\n  // Use bitwise operations to extract the bytes.\n  uint8Array[0] = (number >> 24) & 0xff // Most significant byte (MSB)\n  uint8Array[1] = (number >> 16) & 0xff\n  uint8Array[2] = (number >> 8) & 0xff\n  uint8Array[3] = number & 0xff // Least significant byte (LSB)\n\n  return uint8Array\n}\n\nexport type ProfilePointer = {\n  pubkey: string // hex\n  relays?: string[]\n}\n\nexport type EventPointer = {\n  id: string // hex\n  relays?: string[]\n  author?: string\n  kind?: number\n}\n\nexport type AddressPointer = {\n  identifier: string\n  pubkey: string\n  kind: number\n  relays?: string[]\n}\n\nexport function decodeNostrURI(nip19code: string): ReturnType<typeof decode> | { type: 'invalid'; data: null } {\n  try {\n    if (nip19code.startsWith('nostr:')) nip19code = nip19code.substring(6)\n    return decode(nip19code)\n  } catch (_err) {\n    return { type: 'invalid', data: null }\n  }\n}\n\nexport type DecodedNevent = {\n  type: 'nevent'\n  data: EventPointer\n}\n\nexport type DecodedNprofile = {\n  type: 'nprofile'\n  data: ProfilePointer\n}\n\nexport type DecodedNaddr = {\n  type: 'naddr'\n  data: AddressPointer\n}\n\nexport type DecodedNsec = {\n  type: 'nsec'\n  data: Uint8Array\n}\n\nexport type DecodedNpub = {\n  type: 'npub'\n  data: string\n}\n\nexport type DecodedNote = {\n  type: 'note'\n  data: string\n}\n\nexport type DecodedResult = DecodedNevent | DecodedNprofile | DecodedNaddr | DecodedNpub | DecodedNsec | DecodedNote\n\nexport function decode(nip19: NEvent): DecodedNevent\nexport function decode(nip19: NProfile): DecodedNprofile\nexport function decode(nip19: NAddr): DecodedNaddr\nexport function decode(nip19: NSec): DecodedNsec\nexport function decode(nip19: NPub): DecodedNpub\nexport function decode(nip19: Note): DecodedNote\nexport function decode(code: string): DecodedResult\nexport function decode(code: string): DecodedResult {\n  let { prefix, words } = bech32.decode(code, Bech32MaxSize)\n  let data = new Uint8Array(bech32.fromWords(words))\n\n  switch (prefix) {\n    case 'nprofile': {\n      let tlv = parseTLV(data)\n      if (!tlv[0]?.[0]) throw new Error('missing TLV 0 for nprofile')\n      if (tlv[0][0].length !== 32) throw new Error('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      let tlv = parseTLV(data)\n      if (!tlv[0]?.[0]) throw new Error('missing TLV 0 for nevent')\n      if (tlv[0][0].length !== 32) throw new Error('TLV 0 should be 32 bytes')\n      if (tlv[2] && tlv[2][0].length !== 32) throw new Error('TLV 2 should be 32 bytes')\n      if (tlv[3] && tlv[3][0].length !== 4) throw new Error('TLV 3 should be 4 bytes')\n\n      return {\n        type: 'nevent',\n        data: {\n          id: bytesToHex(tlv[0][0]),\n          relays: tlv[1] ? tlv[1].map(d => utf8Decoder.decode(d)) : [],\n          author: tlv[2]?.[0] ? bytesToHex(tlv[2][0]) : undefined,\n          kind: tlv[3]?.[0] ? parseInt(bytesToHex(tlv[3][0]), 16) : undefined,\n        },\n      }\n    }\n\n    case 'naddr': {\n      let tlv = parseTLV(data)\n      if (!tlv[0]?.[0]) throw new Error('missing TLV 0 for naddr')\n      if (!tlv[2]?.[0]) throw new Error('missing TLV 2 for naddr')\n      if (tlv[2][0].length !== 32) throw new Error('TLV 2 should be 32 bytes')\n      if (!tlv[3]?.[0]) throw new Error('missing TLV 3 for naddr')\n      if (tlv[3][0].length !== 4) throw new Error('TLV 3 should be 4 bytes')\n\n      return {\n        type: 'naddr',\n        data: {\n          identifier: utf8Decoder.decode(tlv[0][0]),\n          pubkey: bytesToHex(tlv[2][0]),\n          kind: parseInt(bytesToHex(tlv[3][0]), 16),\n          relays: tlv[1] ? tlv[1].map(d => utf8Decoder.decode(d)) : [],\n        },\n      }\n    }\n\n    case 'nsec':\n      return { type: prefix, data }\n\n    case 'npub':\n    case 'note':\n      return { type: prefix, data: bytesToHex(data) }\n\n    default:\n      throw new Error(`unknown prefix ${prefix}`)\n  }\n}\n\ntype TLV = { [t: number]: Uint8Array[] }\n\nfunction parseTLV(data: Uint8Array): TLV {\n  let result: TLV = {}\n  let rest = data\n  while (rest.length > 0) {\n    let t = rest[0]\n    let l = rest[1]\n    let v = rest.slice(2, 2 + l)\n    rest = rest.slice(2 + l)\n    if (v.length < l) throw new Error(`not enough data to read on TLV ${t}`)\n    result[t] = result[t] || []\n    result[t].push(v)\n  }\n  return result\n}\n\nexport function nsecEncode(key: Uint8Array): NSec {\n  return encodeBytes('nsec', key)\n}\n\nexport function npubEncode(hex: string): NPub {\n  return encodeBytes('npub', hexToBytes(hex))\n}\n\nexport function noteEncode(hex: string): Note {\n  return encodeBytes('note', hexToBytes(hex))\n}\n\nfunction encodeBech32<Prefix extends string>(prefix: Prefix, data: Uint8Array): `${Prefix}1${string}` {\n  let words = bech32.toWords(data)\n  return bech32.encode(prefix, words, Bech32MaxSize) as `${Prefix}1${string}`\n}\n\nexport function encodeBytes<Prefix extends string>(prefix: Prefix, bytes: Uint8Array): `${Prefix}1${string}` {\n  return encodeBech32(prefix, bytes)\n}\n\nexport function nprofileEncode(profile: ProfilePointer): NProfile {\n  let data = encodeTLV({\n    0: [hexToBytes(profile.pubkey)],\n    1: (profile.relays || []).map(url => utf8Encoder.encode(url)),\n  })\n  return encodeBech32('nprofile', data)\n}\n\nexport function neventEncode(event: EventPointer): NEvent {\n  let kindArray\n  if (event.kind !== undefined) {\n    kindArray = integerToUint8Array(event.kind)\n  }\n\n  let data = encodeTLV({\n    0: [hexToBytes(event.id)],\n    1: (event.relays || []).map(url => utf8Encoder.encode(url)),\n    2: event.author ? [hexToBytes(event.author)] : [],\n    3: kindArray ? [new Uint8Array(kindArray)] : [],\n  })\n\n  return encodeBech32('nevent', data)\n}\n\nexport function naddrEncode(addr: AddressPointer): NAddr {\n  let kind = new ArrayBuffer(4)\n  new DataView(kind).setUint32(0, addr.kind, false)\n\n  let data = encodeTLV({\n    0: [utf8Encoder.encode(addr.identifier)],\n    1: (addr.relays || []).map(url => utf8Encoder.encode(url)),\n    2: [hexToBytes(addr.pubkey)],\n    3: [new Uint8Array(kind)],\n  })\n  return encodeBech32('naddr', data)\n}\n\nfunction encodeTLV(tlv: TLV): Uint8Array {\n  let entries: Uint8Array[] = []\n\n  Object.entries(tlv)\n    .reverse()\n    .forEach(([t, vs]) => {\n      vs.forEach(v => {\n        let entry = new Uint8Array(v.length + 2)\n        entry.set([parseInt(t)], 0)\n        entry.set([v.length], 1)\n        entry.set(v, 2)\n        entries.push(entry)\n      })\n    })\n\n  return concatBytes(...entries)\n}\n", "import type { Event } from './core.ts'\n\nexport const utf8Decoder: TextDecoder = new TextDecoder('utf-8')\nexport const utf8Encoder: TextEncoder = new TextEncoder()\n\nexport { bytesToHex, hexToBytes } from '@noble/hashes/utils'\n\nexport function normalizeURL(url: string): string {\n  try {\n    if (url.indexOf('://') === -1) url = 'wss://' + url\n    let p = new URL(url)\n    if (p.protocol === 'http:') p.protocol = 'ws:'\n    else if (p.protocol === 'https:') p.protocol = 'wss:'\n    p.pathname = p.pathname.replace(/\\/+/g, '/')\n    if (p.pathname.endsWith('/')) p.pathname = p.pathname.slice(0, -1)\n    if ((p.port === '80' && p.protocol === 'ws:') || (p.port === '443' && p.protocol === 'wss:')) p.port = ''\n    p.searchParams.sort()\n    p.hash = ''\n    return p.toString()\n  } catch (e) {\n    throw new Error(`Invalid URL: ${url}`)\n  }\n}\n\nexport function insertEventIntoDescendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return b.created_at - event.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function insertEventIntoAscendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return event.created_at - b.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function binarySearch<T>(arr: T[], compare: (b: T) => number): [number, boolean] {\n  let start = 0\n  let end = arr.length - 1\n\n  while (start <= end) {\n    const mid = Math.floor((start + end) / 2)\n    const cmp = compare(arr[mid])\n\n    if (cmp === 0) {\n      return [mid, true]\n    }\n\n    if (cmp < 0) {\n      end = mid - 1\n    } else {\n      start = mid + 1\n    }\n  }\n\n  return [start, false]\n}\n\nexport class QueueNode<V> {\n  public value: V\n  public next: QueueNode<V> | null = null\n  public prev: QueueNode<V> | null = null\n\n  constructor(message: V) {\n    this.value = message\n  }\n}\n\nexport class Queue<V> {\n  public first: QueueNode<V> | null\n  public last: QueueNode<V> | null\n\n  constructor() {\n    this.first = null\n    this.last = null\n  }\n\n  enqueue(value: V): boolean {\n    const newNode = new QueueNode(value)\n    if (!this.last) {\n      // list is empty\n      this.first = newNode\n      this.last = newNode\n    } else if (this.last === this.first) {\n      // list has a single element\n      this.last = newNode\n      this.last.prev = this.first\n      this.first.next = newNode\n    } else {\n      // list has elements, add as last\n      newNode.prev = this.last\n      this.last.next = newNode\n      this.last = newNode\n    }\n    return true\n  }\n\n  dequeue(): V | null {\n    if (!this.first) return null\n\n    if (this.first === this.last) {\n      const target = this.first\n      this.first = null\n      this.last = null\n      return target.value\n    }\n\n    const target = this.first\n    this.first = target.next\n    if (this.first) {\n      this.first.prev = null // fix: clean up prev pointer\n    }\n\n    return target.value\n  }\n}\n", "import { NostrEvent } from './core.ts'\nimport { AddressPointer, EventPointer, ProfilePointer, decode } from './nip19.ts'\n\nexport type Block =\n  | {\n      type: 'text'\n      text: string\n    }\n  | {\n      type: 'reference'\n      pointer: ProfilePointer | AddressPointer | EventPointer\n    }\n  | {\n      type: 'url'\n      url: string\n    }\n  | {\n      type: 'relay'\n      url: string\n    }\n  | {\n      type: 'image'\n      url: string\n    }\n  | {\n      type: 'video'\n      url: string\n    }\n  | {\n      type: 'audio'\n      url: string\n    }\n  | {\n      type: 'emoji'\n      shortcode: string\n      url: string\n    }\n  | {\n      type: 'hashtag'\n      value: string\n    }\n\nconst noCharacter = /\\W/m\nconst noURLCharacter = /[^\\w\\/] |[^\\w\\/]$|$|,| /m\nconst MAX_HASHTAG_LENGTH = 42\n\nexport function* parse(content: string | NostrEvent): Iterable<Block> {\n  let emojis: { type: 'emoji'; shortcode: string; url: string }[] = []\n  if (typeof content !== 'string') {\n    for (let i = 0; i < content.tags.length; i++) {\n      const tag = content.tags[i]\n      if (tag[0] === 'emoji' && tag.length >= 3) {\n        emojis.push({ type: 'emoji', shortcode: tag[1], url: tag[2] })\n      }\n    }\n    content = content.content\n  }\n\n  const max = content.length\n  let prevIndex = 0\n  let index = 0\n  mainloop: while (index < max) {\n    const u = content.indexOf(':', index)\n    const h = content.indexOf('#', index)\n    if (u === -1 && h === -1) {\n      // reached end\n      break mainloop\n    }\n\n    if (u === -1 || (h >= 0 && h < u)) {\n      // parse hashtag\n      if (h === 0 || content[h - 1] === ' ') {\n        const m = content.slice(h + 1, h + MAX_HASHTAG_LENGTH).match(noCharacter)\n        const end = m ? h + 1 + m.index! : max\n        yield { type: 'text', text: content.slice(prevIndex, h) }\n        yield { type: 'hashtag', value: content.slice(h + 1, end) }\n        index = end\n        prevIndex = index\n        continue mainloop\n      }\n\n      // ignore this, it is nothing\n      index = h + 1\n      continue mainloop\n    }\n\n    // otherwise parse things that have an \":\"\n    if (content.slice(u - 5, u) === 'nostr') {\n      const m = content.slice(u + 60).match(noCharacter)\n      const end = m ? u + 60 + m.index! : max\n      try {\n        let pointer: ProfilePointer | AddressPointer | EventPointer\n        let { data, type } = decode(content.slice(u + 1, end))\n\n        switch (type) {\n          case 'npub':\n            pointer = { pubkey: data } as ProfilePointer\n            break\n          case 'note':\n            pointer = { id: data } as EventPointer\n            break\n          case 'nsec':\n            // ignore this, treat it as not a valid uri\n            index = end + 1\n            continue\n          default:\n            pointer = data as any\n        }\n\n        if (prevIndex !== u - 5) {\n          yield { type: 'text', text: content.slice(prevIndex, u - 5) }\n        }\n        yield { type: 'reference', pointer }\n        index = end\n        prevIndex = index\n        continue mainloop\n      } catch (_err) {\n        // ignore this, not a valid nostr uri\n        index = u + 1\n        continue mainloop\n      }\n    } else if (content.slice(u - 5, u) === 'https' || content.slice(u - 4, u) === 'http') {\n      const m = content.slice(u + 4).match(noURLCharacter)\n      const end = m ? u + 4 + m.index! : max\n      const prefixLen = content[u - 1] === 's' ? 5 : 4\n      try {\n        let url = new URL(content.slice(u - prefixLen, end))\n        if (url.hostname.indexOf('.') === -1) {\n          throw new Error('invalid url')\n        }\n\n        if (prevIndex !== u - prefixLen) {\n          yield { type: 'text', text: content.slice(prevIndex, u - prefixLen) }\n        }\n\n        if (/\\.(png|jpe?g|gif|webp|heic|svg)$/i.test(url.pathname)) {\n          yield { type: 'image', url: url.toString() }\n          index = end\n          prevIndex = index\n          continue mainloop\n        }\n        if (/\\.(mp4|avi|webm|mkv|mov)$/i.test(url.pathname)) {\n          yield { type: 'video', url: url.toString() }\n          index = end\n          prevIndex = index\n          continue mainloop\n        }\n        if (/\\.(mp3|aac|ogg|opus|wav|flac)$/i.test(url.pathname)) {\n          yield { type: 'audio', url: url.toString() }\n          index = end\n          prevIndex = index\n          continue mainloop\n        }\n\n        yield { type: 'url', url: url.toString() }\n        index = end\n        prevIndex = index\n        continue mainloop\n      } catch (_err) {\n        // ignore this, not a valid url\n        index = end + 1\n        continue mainloop\n      }\n    } else if (content.slice(u - 3, u) === 'wss' || content.slice(u - 2, u) === 'ws') {\n      const m = content.slice(u + 4).match(noURLCharacter)\n      const end = m ? u + 4 + m.index! : max\n      const prefixLen = content[u - 1] === 's' ? 3 : 2\n      try {\n        let url = new URL(content.slice(u - prefixLen, end))\n        if (url.hostname.indexOf('.') === -1) {\n          throw new Error('invalid ws url')\n        }\n\n        if (prevIndex !== u - prefixLen) {\n          yield { type: 'text', text: content.slice(prevIndex, u - prefixLen) }\n        }\n        yield { type: 'relay', url: url.toString() }\n        index = end\n        prevIndex = index\n        continue mainloop\n      } catch (_err) {\n        // ignore this, not a valid url\n        index = end + 1\n        continue mainloop\n      }\n    } else {\n      // try to parse an emoji shortcode\n      for (let e = 0; e < emojis.length; e++) {\n        const emoji = emojis[e]\n        if (\n          content[u + emoji.shortcode.length + 1] === ':' &&\n          content.slice(u + 1, u + emoji.shortcode.length + 1) === emoji.shortcode\n        ) {\n          // found an emoji\n          if (prevIndex !== u) {\n            yield { type: 'text', text: content.slice(prevIndex, u) }\n          }\n          yield emoji\n          index = u + emoji.shortcode.length + 2\n          prevIndex = index\n          continue mainloop\n        }\n      }\n\n      // ignore this, it is nothing\n      index = u + 1\n      continue mainloop\n    }\n  }\n\n  if (prevIndex !== max) {\n    yield { type: 'text', text: content.slice(prevIndex) }\n  }\n}\n"],
-  "mappings": "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",
+  "sourcesContent": ["import { bytesToHex, concatBytes, hexToBytes } from '@noble/hashes/utils.js'\nimport { bech32 } from '@scure/base'\n\nimport { utf8Decoder, utf8Encoder } from './utils.ts'\n\nexport type NProfile = `nprofile1${string}`\nexport type NEvent = `nevent1${string}`\nexport type NAddr = `naddr1${string}`\nexport type NSec = `nsec1${string}`\nexport type NPub = `npub1${string}`\nexport type Note = `note1${string}`\nexport type Ncryptsec = `ncryptsec1${string}`\n\nexport const NostrTypeGuard = {\n  isNProfile: (value?: string | null): value is NProfile => /^nprofile1[a-z\\d]+$/.test(value || ''),\n  isNEvent: (value?: string | null): value is NEvent => /^nevent1[a-z\\d]+$/.test(value || ''),\n  isNAddr: (value?: string | null): value is NAddr => /^naddr1[a-z\\d]+$/.test(value || ''),\n  isNSec: (value?: string | null): value is NSec => /^nsec1[a-z\\d]{58}$/.test(value || ''),\n  isNPub: (value?: string | null): value is NPub => /^npub1[a-z\\d]{58}$/.test(value || ''),\n  isNote: (value?: string | null): value is Note => /^note1[a-z\\d]+$/.test(value || ''),\n  isNcryptsec: (value?: string | null): value is Ncryptsec => /^ncryptsec1[a-z\\d]+$/.test(value || ''),\n}\n\nexport const Bech32MaxSize = 5000\n\n/**\n * Bech32 regex.\n * @see https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki#bech32\n */\nexport const BECH32_REGEX = /[\\x21-\\x7E]{1,83}1[023456789acdefghjklmnpqrstuvwxyz]{6,}/\n\nfunction integerToUint8Array(number: number) {\n  // Create a Uint8Array with enough space to hold a 32-bit integer (4 bytes).\n  const uint8Array = new Uint8Array(4)\n\n  // Use bitwise operations to extract the bytes.\n  uint8Array[0] = (number >> 24) & 0xff // Most significant byte (MSB)\n  uint8Array[1] = (number >> 16) & 0xff\n  uint8Array[2] = (number >> 8) & 0xff\n  uint8Array[3] = number & 0xff // Least significant byte (LSB)\n\n  return uint8Array\n}\n\nexport type ProfilePointer = {\n  pubkey: string // hex\n  relays?: string[]\n}\n\nexport type EventPointer = {\n  id: string // hex\n  relays?: string[]\n  author?: string\n  kind?: number\n}\n\nexport type AddressPointer = {\n  identifier: string\n  pubkey: string\n  kind: number\n  relays?: string[]\n}\n\nexport function decodeNostrURI(nip19code: string): ReturnType<typeof decode> | { type: 'invalid'; data: null } {\n  try {\n    if (nip19code.startsWith('nostr:')) nip19code = nip19code.substring(6)\n    return decode(nip19code)\n  } catch (_err) {\n    return { type: 'invalid', data: null }\n  }\n}\n\nexport type DecodedNevent = {\n  type: 'nevent'\n  data: EventPointer\n}\n\nexport type DecodedNprofile = {\n  type: 'nprofile'\n  data: ProfilePointer\n}\n\nexport type DecodedNaddr = {\n  type: 'naddr'\n  data: AddressPointer\n}\n\nexport type DecodedNsec = {\n  type: 'nsec'\n  data: Uint8Array\n}\n\nexport type DecodedNpub = {\n  type: 'npub'\n  data: string\n}\n\nexport type DecodedNote = {\n  type: 'note'\n  data: string\n}\n\nexport type DecodedResult = DecodedNevent | DecodedNprofile | DecodedNaddr | DecodedNpub | DecodedNsec | DecodedNote\n\nexport function decode(nip19: NEvent): DecodedNevent\nexport function decode(nip19: NProfile): DecodedNprofile\nexport function decode(nip19: NAddr): DecodedNaddr\nexport function decode(nip19: NSec): DecodedNsec\nexport function decode(nip19: NPub): DecodedNpub\nexport function decode(nip19: Note): DecodedNote\nexport function decode(code: string): DecodedResult\nexport function decode(code: string): DecodedResult {\n  let { prefix, words } = bech32.decode(code as `${string}1${string}`, Bech32MaxSize)\n  let data = new Uint8Array(bech32.fromWords(words))\n\n  switch (prefix) {\n    case 'nprofile': {\n      let tlv = parseTLV(data)\n      if (!tlv[0]?.[0]) throw new Error('missing TLV 0 for nprofile')\n      if (tlv[0][0].length !== 32) throw new Error('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      let tlv = parseTLV(data)\n      if (!tlv[0]?.[0]) throw new Error('missing TLV 0 for nevent')\n      if (tlv[0][0].length !== 32) throw new Error('TLV 0 should be 32 bytes')\n      if (tlv[2] && tlv[2][0].length !== 32) throw new Error('TLV 2 should be 32 bytes')\n      if (tlv[3] && tlv[3][0].length !== 4) throw new Error('TLV 3 should be 4 bytes')\n\n      return {\n        type: 'nevent',\n        data: {\n          id: bytesToHex(tlv[0][0]),\n          relays: tlv[1] ? tlv[1].map(d => utf8Decoder.decode(d)) : [],\n          author: tlv[2]?.[0] ? bytesToHex(tlv[2][0]) : undefined,\n          kind: tlv[3]?.[0] ? parseInt(bytesToHex(tlv[3][0]), 16) : undefined,\n        },\n      }\n    }\n\n    case 'naddr': {\n      let tlv = parseTLV(data)\n      if (!tlv[0]?.[0]) throw new Error('missing TLV 0 for naddr')\n      if (!tlv[2]?.[0]) throw new Error('missing TLV 2 for naddr')\n      if (tlv[2][0].length !== 32) throw new Error('TLV 2 should be 32 bytes')\n      if (!tlv[3]?.[0]) throw new Error('missing TLV 3 for naddr')\n      if (tlv[3][0].length !== 4) throw new Error('TLV 3 should be 4 bytes')\n\n      return {\n        type: 'naddr',\n        data: {\n          identifier: utf8Decoder.decode(tlv[0][0]),\n          pubkey: bytesToHex(tlv[2][0]),\n          kind: parseInt(bytesToHex(tlv[3][0]), 16),\n          relays: tlv[1] ? tlv[1].map(d => utf8Decoder.decode(d)) : [],\n        },\n      }\n    }\n\n    case 'nsec':\n      return { type: prefix, data }\n\n    case 'npub':\n    case 'note':\n      return { type: prefix, data: bytesToHex(data) }\n\n    default:\n      throw new Error(`unknown prefix ${prefix}`)\n  }\n}\n\ntype TLV = { [t: number]: Uint8Array[] }\n\nfunction parseTLV(data: Uint8Array): TLV {\n  let result: TLV = {}\n  let rest = data\n  while (rest.length > 0) {\n    let t = rest[0]\n    let l = rest[1]\n    let v = rest.slice(2, 2 + l)\n    rest = rest.slice(2 + l)\n    if (v.length < l) throw new Error(`not enough data to read on TLV ${t}`)\n    result[t] = result[t] || []\n    result[t].push(v)\n  }\n  return result\n}\n\nexport function nsecEncode(key: Uint8Array): NSec {\n  return encodeBytes('nsec', key)\n}\n\nexport function npubEncode(hex: string): NPub {\n  return encodeBytes('npub', hexToBytes(hex))\n}\n\nexport function noteEncode(hex: string): Note {\n  return encodeBytes('note', hexToBytes(hex))\n}\n\nfunction encodeBech32<Prefix extends string>(prefix: Prefix, data: Uint8Array): `${Prefix}1${string}` {\n  let words = bech32.toWords(data)\n  return bech32.encode(prefix, words, Bech32MaxSize) as `${Prefix}1${string}`\n}\n\nexport function encodeBytes<Prefix extends string>(prefix: Prefix, bytes: Uint8Array): `${Prefix}1${string}` {\n  return encodeBech32(prefix, bytes)\n}\n\nexport function nprofileEncode(profile: ProfilePointer): NProfile {\n  let data = encodeTLV({\n    0: [hexToBytes(profile.pubkey)],\n    1: (profile.relays || []).map(url => utf8Encoder.encode(url)),\n  })\n  return encodeBech32('nprofile', data)\n}\n\nexport function neventEncode(event: EventPointer): NEvent {\n  let kindArray\n  if (event.kind !== undefined) {\n    kindArray = integerToUint8Array(event.kind)\n  }\n\n  let data = encodeTLV({\n    0: [hexToBytes(event.id)],\n    1: (event.relays || []).map(url => utf8Encoder.encode(url)),\n    2: event.author ? [hexToBytes(event.author)] : [],\n    3: kindArray ? [new Uint8Array(kindArray)] : [],\n  })\n\n  return encodeBech32('nevent', data)\n}\n\nexport function naddrEncode(addr: AddressPointer): NAddr {\n  let kind = new ArrayBuffer(4)\n  new DataView(kind).setUint32(0, addr.kind, false)\n\n  let data = encodeTLV({\n    0: [utf8Encoder.encode(addr.identifier)],\n    1: (addr.relays || []).map(url => utf8Encoder.encode(url)),\n    2: [hexToBytes(addr.pubkey)],\n    3: [new Uint8Array(kind)],\n  })\n  return encodeBech32('naddr', data)\n}\n\nfunction encodeTLV(tlv: TLV): Uint8Array {\n  let entries: Uint8Array[] = []\n\n  Object.entries(tlv)\n    .reverse()\n    .forEach(([t, vs]) => {\n      vs.forEach(v => {\n        let entry = new Uint8Array(v.length + 2)\n        entry.set([parseInt(t)], 0)\n        entry.set([v.length], 1)\n        entry.set(v, 2)\n        entries.push(entry)\n      })\n    })\n\n  return concatBytes(...entries)\n}\n", "import type { Event } from './core.ts'\n\nexport const utf8Decoder: TextDecoder = new TextDecoder('utf-8')\nexport const utf8Encoder: TextEncoder = new TextEncoder()\n\nexport { bytesToHex, hexToBytes } from '@noble/hashes/utils.js'\n\nexport function normalizeURL(url: string): string {\n  try {\n    if (url.indexOf('://') === -1) url = 'wss://' + url\n    let p = new URL(url)\n    if (p.protocol === 'http:') p.protocol = 'ws:'\n    else if (p.protocol === 'https:') p.protocol = 'wss:'\n    p.pathname = p.pathname.replace(/\\/+/g, '/')\n    if (p.pathname.endsWith('/')) p.pathname = p.pathname.slice(0, -1)\n    if ((p.port === '80' && p.protocol === 'ws:') || (p.port === '443' && p.protocol === 'wss:')) p.port = ''\n    p.searchParams.sort()\n    p.hash = ''\n    return p.toString()\n  } catch (e) {\n    throw new Error(`Invalid URL: ${url}`)\n  }\n}\n\nexport function insertEventIntoDescendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return b.created_at - event.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function insertEventIntoAscendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return event.created_at - b.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function binarySearch<T>(arr: T[], compare: (b: T) => number): [number, boolean] {\n  let start = 0\n  let end = arr.length - 1\n\n  while (start <= end) {\n    const mid = Math.floor((start + end) / 2)\n    const cmp = compare(arr[mid])\n\n    if (cmp === 0) {\n      return [mid, true]\n    }\n\n    if (cmp < 0) {\n      end = mid - 1\n    } else {\n      start = mid + 1\n    }\n  }\n\n  return [start, false]\n}\n\nexport class QueueNode<V> {\n  public value: V\n  public next: QueueNode<V> | null = null\n  public prev: QueueNode<V> | null = null\n\n  constructor(message: V) {\n    this.value = message\n  }\n}\n\nexport class Queue<V> {\n  public first: QueueNode<V> | null\n  public last: QueueNode<V> | null\n\n  constructor() {\n    this.first = null\n    this.last = null\n  }\n\n  enqueue(value: V): boolean {\n    const newNode = new QueueNode(value)\n    if (!this.last) {\n      // list is empty\n      this.first = newNode\n      this.last = newNode\n    } else if (this.last === this.first) {\n      // list has a single element\n      this.last = newNode\n      this.last.prev = this.first\n      this.first.next = newNode\n    } else {\n      // list has elements, add as last\n      newNode.prev = this.last\n      this.last.next = newNode\n      this.last = newNode\n    }\n    return true\n  }\n\n  dequeue(): V | null {\n    if (!this.first) return null\n\n    if (this.first === this.last) {\n      const target = this.first\n      this.first = null\n      this.last = null\n      return target.value\n    }\n\n    const target = this.first\n    this.first = target.next\n    if (this.first) {\n      this.first.prev = null // fix: clean up prev pointer\n    }\n\n    return target.value\n  }\n}\n", "import { NostrEvent } from './core.ts'\nimport { AddressPointer, EventPointer, ProfilePointer, decode } from './nip19.ts'\n\nexport type Block =\n  | {\n      type: 'text'\n      text: string\n    }\n  | {\n      type: 'reference'\n      pointer: ProfilePointer | AddressPointer | EventPointer\n    }\n  | {\n      type: 'url'\n      url: string\n    }\n  | {\n      type: 'relay'\n      url: string\n    }\n  | {\n      type: 'image'\n      url: string\n    }\n  | {\n      type: 'video'\n      url: string\n    }\n  | {\n      type: 'audio'\n      url: string\n    }\n  | {\n      type: 'emoji'\n      shortcode: string\n      url: string\n    }\n  | {\n      type: 'hashtag'\n      value: string\n    }\n\nconst noCharacter = /\\W/m\nconst noURLCharacter = /[^\\w\\/] |[^\\w\\/]$|$|,| /m\nconst MAX_HASHTAG_LENGTH = 42\n\nexport function* parse(content: string | NostrEvent): Iterable<Block> {\n  let emojis: { type: 'emoji'; shortcode: string; url: string }[] = []\n  if (typeof content !== 'string') {\n    for (let i = 0; i < content.tags.length; i++) {\n      const tag = content.tags[i]\n      if (tag[0] === 'emoji' && tag.length >= 3) {\n        emojis.push({ type: 'emoji', shortcode: tag[1], url: tag[2] })\n      }\n    }\n    content = content.content\n  }\n\n  const max = content.length\n  let prevIndex = 0\n  let index = 0\n  mainloop: while (index < max) {\n    const u = content.indexOf(':', index)\n    const h = content.indexOf('#', index)\n    if (u === -1 && h === -1) {\n      // reached end\n      break mainloop\n    }\n\n    if (u === -1 || (h >= 0 && h < u)) {\n      // parse hashtag\n      if (h === 0 || content[h - 1] === ' ') {\n        const m = content.slice(h + 1, h + MAX_HASHTAG_LENGTH).match(noCharacter)\n        const end = m ? h + 1 + m.index! : max\n        yield { type: 'text', text: content.slice(prevIndex, h) }\n        yield { type: 'hashtag', value: content.slice(h + 1, end) }\n        index = end\n        prevIndex = index\n        continue mainloop\n      }\n\n      // ignore this, it is nothing\n      index = h + 1\n      continue mainloop\n    }\n\n    // otherwise parse things that have an \":\"\n    if (content.slice(u - 5, u) === 'nostr') {\n      const m = content.slice(u + 60).match(noCharacter)\n      const end = m ? u + 60 + m.index! : max\n      try {\n        let pointer: ProfilePointer | AddressPointer | EventPointer\n        let { data, type } = decode(content.slice(u + 1, end))\n\n        switch (type) {\n          case 'npub':\n            pointer = { pubkey: data } as ProfilePointer\n            break\n          case 'note':\n            pointer = { id: data } as EventPointer\n            break\n          case 'nsec':\n            // ignore this, treat it as not a valid uri\n            index = end + 1\n            continue\n          default:\n            pointer = data as any\n        }\n\n        if (prevIndex !== u - 5) {\n          yield { type: 'text', text: content.slice(prevIndex, u - 5) }\n        }\n        yield { type: 'reference', pointer }\n        index = end\n        prevIndex = index\n        continue mainloop\n      } catch (_err) {\n        // ignore this, not a valid nostr uri\n        index = u + 1\n        continue mainloop\n      }\n    } else if (content.slice(u - 5, u) === 'https' || content.slice(u - 4, u) === 'http') {\n      const m = content.slice(u + 4).match(noURLCharacter)\n      const end = m ? u + 4 + m.index! : max\n      const prefixLen = content[u - 1] === 's' ? 5 : 4\n      try {\n        let url = new URL(content.slice(u - prefixLen, end))\n        if (url.hostname.indexOf('.') === -1) {\n          throw new Error('invalid url')\n        }\n\n        if (prevIndex !== u - prefixLen) {\n          yield { type: 'text', text: content.slice(prevIndex, u - prefixLen) }\n        }\n\n        if (/\\.(png|jpe?g|gif|webp|heic|svg)$/i.test(url.pathname)) {\n          yield { type: 'image', url: url.toString() }\n          index = end\n          prevIndex = index\n          continue mainloop\n        }\n        if (/\\.(mp4|avi|webm|mkv|mov)$/i.test(url.pathname)) {\n          yield { type: 'video', url: url.toString() }\n          index = end\n          prevIndex = index\n          continue mainloop\n        }\n        if (/\\.(mp3|aac|ogg|opus|wav|flac)$/i.test(url.pathname)) {\n          yield { type: 'audio', url: url.toString() }\n          index = end\n          prevIndex = index\n          continue mainloop\n        }\n\n        yield { type: 'url', url: url.toString() }\n        index = end\n        prevIndex = index\n        continue mainloop\n      } catch (_err) {\n        // ignore this, not a valid url\n        index = end + 1\n        continue mainloop\n      }\n    } else if (content.slice(u - 3, u) === 'wss' || content.slice(u - 2, u) === 'ws') {\n      const m = content.slice(u + 4).match(noURLCharacter)\n      const end = m ? u + 4 + m.index! : max\n      const prefixLen = content[u - 1] === 's' ? 3 : 2\n      try {\n        let url = new URL(content.slice(u - prefixLen, end))\n        if (url.hostname.indexOf('.') === -1) {\n          throw new Error('invalid ws url')\n        }\n\n        if (prevIndex !== u - prefixLen) {\n          yield { type: 'text', text: content.slice(prevIndex, u - prefixLen) }\n        }\n        yield { type: 'relay', url: url.toString() }\n        index = end\n        prevIndex = index\n        continue mainloop\n      } catch (_err) {\n        // ignore this, not a valid url\n        index = end + 1\n        continue mainloop\n      }\n    } else {\n      // try to parse an emoji shortcode\n      for (let e = 0; e < emojis.length; e++) {\n        const emoji = emojis[e]\n        if (\n          content[u + emoji.shortcode.length + 1] === ':' &&\n          content.slice(u + 1, u + emoji.shortcode.length + 1) === emoji.shortcode\n        ) {\n          // found an emoji\n          if (prevIndex !== u) {\n            yield { type: 'text', text: content.slice(prevIndex, u) }\n          }\n          yield emoji\n          index = u + emoji.shortcode.length + 2\n          prevIndex = index\n          continue mainloop\n        }\n      }\n\n      // ignore this, it is nothing\n      index = u + 1\n      continue mainloop\n    }\n  }\n\n  if (prevIndex !== max) {\n    yield { type: 'text', text: content.slice(prevIndex) }\n  }\n}\n"],
+  "mappings": "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",
   "names": ["bytesToHex", "hexToBytes", "bytesToHex"]
 }
Index: package/lib/cjs/nip28.js.map
===================================================================
--- package/lib/cjs/nip28.js.map
+++ package/lib/cjs/nip28.js.map
@@ -1,7 +1,7 @@
 {
   "version": 3,
   "sources": ["../../nip28.ts", "../../pure.ts", "../../core.ts", "../../utils.ts", "../../kinds.ts"],
-  "sourcesContent": ["import { Event, finalizeEvent } from './pure.ts'\nimport {\n  ChannelCreation,\n  ChannelHideMessage,\n  ChannelMessage,\n  ChannelMetadata as KindChannelMetadata,\n  ChannelMuteUser,\n} from './kinds.ts'\n\nexport interface ChannelMetadata {\n  name: string\n  about: string\n  picture: string\n}\n\nexport interface ChannelCreateEventTemplate {\n  /* JSON string containing ChannelMetadata as defined for Kind 40 and 41 in nip-28. */\n  content: string | ChannelMetadata\n  created_at: number\n  tags?: string[][]\n}\n\nexport interface ChannelMetadataEventTemplate {\n  channel_create_event_id: string\n  /* JSON string containing ChannelMetadata as defined for Kind 40 and 41 in nip-28. */\n  content: string | ChannelMetadata\n  created_at: number\n  tags?: string[][]\n}\n\nexport interface ChannelMessageEventTemplate {\n  channel_create_event_id: string\n  reply_to_channel_message_event_id?: string\n  relay_url: string\n  content: string\n  created_at: number\n  tags?: string[][]\n}\n\nexport interface ChannelHideMessageEventTemplate {\n  channel_message_event_id: string\n  content: string | { reason: string }\n  created_at: number\n  tags?: string[][]\n}\n\nexport interface ChannelMuteUserEventTemplate {\n  content: string | { reason: string }\n  created_at: number\n  pubkey_to_mute: string\n  tags?: string[][]\n}\n\nexport const channelCreateEvent = (t: ChannelCreateEventTemplate, privateKey: Uint8Array): Event | undefined => {\n  let content: string\n  if (typeof t.content === 'object') {\n    content = JSON.stringify(t.content)\n  } else if (typeof t.content === 'string') {\n    content = t.content\n  } else {\n    return undefined\n  }\n\n  return finalizeEvent(\n    {\n      kind: ChannelCreation,\n      tags: [...(t.tags ?? [])],\n      content: content,\n      created_at: t.created_at,\n    },\n    privateKey,\n  )\n}\n\nexport const channelMetadataEvent = (t: ChannelMetadataEventTemplate, privateKey: Uint8Array): Event | undefined => {\n  let content: string\n  if (typeof t.content === 'object') {\n    content = JSON.stringify(t.content)\n  } else if (typeof t.content === 'string') {\n    content = t.content\n  } else {\n    return undefined\n  }\n\n  return finalizeEvent(\n    {\n      kind: KindChannelMetadata,\n      tags: [['e', t.channel_create_event_id], ...(t.tags ?? [])],\n      content: content,\n      created_at: t.created_at,\n    },\n    privateKey,\n  )\n}\n\nexport const channelMessageEvent = (t: ChannelMessageEventTemplate, privateKey: Uint8Array): Event => {\n  const tags = [['e', t.channel_create_event_id, t.relay_url, 'root']]\n\n  if (t.reply_to_channel_message_event_id) {\n    tags.push(['e', t.reply_to_channel_message_event_id, t.relay_url, 'reply'])\n  }\n\n  return finalizeEvent(\n    {\n      kind: ChannelMessage,\n      tags: [...tags, ...(t.tags ?? [])],\n      content: t.content,\n      created_at: t.created_at,\n    },\n    privateKey,\n  )\n}\n\n/* \"e\" tag should be the kind 42 event to hide */\nexport const channelHideMessageEvent = (\n  t: ChannelHideMessageEventTemplate,\n  privateKey: Uint8Array,\n): Event | undefined => {\n  let content: string\n  if (typeof t.content === 'object') {\n    content = JSON.stringify(t.content)\n  } else if (typeof t.content === 'string') {\n    content = t.content\n  } else {\n    return undefined\n  }\n\n  return finalizeEvent(\n    {\n      kind: ChannelHideMessage,\n      tags: [['e', t.channel_message_event_id], ...(t.tags ?? [])],\n      content: content,\n      created_at: t.created_at,\n    },\n    privateKey,\n  )\n}\n\nexport const channelMuteUserEvent = (t: ChannelMuteUserEventTemplate, privateKey: Uint8Array): Event | undefined => {\n  let content: string\n  if (typeof t.content === 'object') {\n    content = JSON.stringify(t.content)\n  } else if (typeof t.content === 'string') {\n    content = t.content\n  } else {\n    return undefined\n  }\n\n  return finalizeEvent(\n    {\n      kind: ChannelMuteUser,\n      tags: [['p', t.pubkey_to_mute], ...(t.tags ?? [])],\n      content: content,\n      created_at: t.created_at,\n    },\n    privateKey,\n  )\n}\n", "import { schnorr } from '@noble/curves/secp256k1'\nimport { bytesToHex } from '@noble/hashes/utils'\nimport { Nostr, Event, EventTemplate, UnsignedEvent, VerifiedEvent, verifiedSymbol, validateEvent } from './core.ts'\nimport { sha256 } from '@noble/hashes/sha256'\n\nimport { utf8Encoder } from './utils.ts'\n\nclass JS implements Nostr {\n  generateSecretKey(): Uint8Array {\n    return schnorr.utils.randomPrivateKey()\n  }\n  getPublicKey(secretKey: Uint8Array): string {\n    return bytesToHex(schnorr.getPublicKey(secretKey))\n  }\n  finalizeEvent(t: EventTemplate, secretKey: Uint8Array): VerifiedEvent {\n    const event = t as VerifiedEvent\n    event.pubkey = bytesToHex(schnorr.getPublicKey(secretKey))\n    event.id = getEventHash(event)\n    event.sig = bytesToHex(schnorr.sign(getEventHash(event), secretKey))\n    event[verifiedSymbol] = true\n    return event\n  }\n  verifyEvent(event: Event): event is VerifiedEvent {\n    if (typeof event[verifiedSymbol] === 'boolean') return event[verifiedSymbol]\n\n    const hash = getEventHash(event)\n    if (hash !== event.id) {\n      event[verifiedSymbol] = false\n      return false\n    }\n\n    try {\n      const valid = schnorr.verify(event.sig, hash, event.pubkey)\n      event[verifiedSymbol] = valid\n      return valid\n    } catch (err) {\n      event[verifiedSymbol] = false\n      return false\n    }\n  }\n}\n\nexport function serializeEvent(evt: UnsignedEvent): string {\n  if (!validateEvent(evt)) throw new Error(\"can't serialize event with wrong or missing properties\")\n  return JSON.stringify([0, evt.pubkey, evt.created_at, evt.kind, evt.tags, evt.content])\n}\n\nexport function getEventHash(event: UnsignedEvent): string {\n  let eventHash = sha256(utf8Encoder.encode(serializeEvent(event)))\n  return bytesToHex(eventHash)\n}\n\nconst i: JS = new JS()\n\nexport const generateSecretKey = i.generateSecretKey\nexport const getPublicKey = i.getPublicKey\nexport const finalizeEvent = i.finalizeEvent\nexport const verifyEvent = i.verifyEvent\nexport * from './core.ts'\n", "export interface Nostr {\n  generateSecretKey(): Uint8Array\n  getPublicKey(secretKey: Uint8Array): string\n  finalizeEvent(event: EventTemplate, secretKey: Uint8Array): VerifiedEvent\n  verifyEvent(event: Event): event is VerifiedEvent\n}\n\n/** Designates a verified event signature. */\nexport const verifiedSymbol = Symbol('verified')\n\nexport type NostrEvent = {\n  kind: number\n  tags: string[][]\n  content: string\n  created_at: number\n  pubkey: string\n  id: string\n  sig: string\n  [verifiedSymbol]?: boolean\n}\n\nexport type Event = NostrEvent\nexport type EventTemplate = Pick<Event, 'kind' | 'tags' | 'content' | 'created_at'>\nexport type UnsignedEvent = Pick<Event, 'kind' | 'tags' | 'content' | 'created_at' | 'pubkey'>\n\n/** An event whose signature has been verified. */\nexport interface VerifiedEvent extends Event {\n  [verifiedSymbol]: true\n}\n\nconst isRecord = (obj: unknown): obj is Record<string, unknown> => obj instanceof Object\n\nexport function validateEvent<T>(event: T): event is T & UnsignedEvent {\n  if (!isRecord(event)) return false\n  if (typeof event.kind !== 'number') return false\n  if (typeof event.content !== 'string') return false\n  if (typeof event.created_at !== 'number') return false\n  if (typeof event.pubkey !== 'string') return false\n  if (!event.pubkey.match(/^[a-f0-9]{64}$/)) return false\n\n  if (!Array.isArray(event.tags)) return false\n  for (let i = 0; i < event.tags.length; i++) {\n    let tag = event.tags[i]\n    if (!Array.isArray(tag)) return false\n    for (let j = 0; j < tag.length; j++) {\n      if (typeof tag[j] !== 'string') return false\n    }\n  }\n\n  return true\n}\n\n/**\n * Sort events in reverse-chronological order by the `created_at` timestamp,\n * and then by the event `id` (lexicographically) in case of ties.\n * This mutates the array.\n */\nexport function sortEvents(events: Event[]): Event[] {\n  return events.sort((a: NostrEvent, b: NostrEvent): number => {\n    if (a.created_at !== b.created_at) {\n      return b.created_at - a.created_at\n    }\n    return a.id.localeCompare(b.id)\n  })\n}\n", "import type { Event } from './core.ts'\n\nexport const utf8Decoder: TextDecoder = new TextDecoder('utf-8')\nexport const utf8Encoder: TextEncoder = new TextEncoder()\n\nexport { bytesToHex, hexToBytes } from '@noble/hashes/utils'\n\nexport function normalizeURL(url: string): string {\n  try {\n    if (url.indexOf('://') === -1) url = 'wss://' + url\n    let p = new URL(url)\n    if (p.protocol === 'http:') p.protocol = 'ws:'\n    else if (p.protocol === 'https:') p.protocol = 'wss:'\n    p.pathname = p.pathname.replace(/\\/+/g, '/')\n    if (p.pathname.endsWith('/')) p.pathname = p.pathname.slice(0, -1)\n    if ((p.port === '80' && p.protocol === 'ws:') || (p.port === '443' && p.protocol === 'wss:')) p.port = ''\n    p.searchParams.sort()\n    p.hash = ''\n    return p.toString()\n  } catch (e) {\n    throw new Error(`Invalid URL: ${url}`)\n  }\n}\n\nexport function insertEventIntoDescendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return b.created_at - event.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function insertEventIntoAscendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return event.created_at - b.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function binarySearch<T>(arr: T[], compare: (b: T) => number): [number, boolean] {\n  let start = 0\n  let end = arr.length - 1\n\n  while (start <= end) {\n    const mid = Math.floor((start + end) / 2)\n    const cmp = compare(arr[mid])\n\n    if (cmp === 0) {\n      return [mid, true]\n    }\n\n    if (cmp < 0) {\n      end = mid - 1\n    } else {\n      start = mid + 1\n    }\n  }\n\n  return [start, false]\n}\n\nexport class QueueNode<V> {\n  public value: V\n  public next: QueueNode<V> | null = null\n  public prev: QueueNode<V> | null = null\n\n  constructor(message: V) {\n    this.value = message\n  }\n}\n\nexport class Queue<V> {\n  public first: QueueNode<V> | null\n  public last: QueueNode<V> | null\n\n  constructor() {\n    this.first = null\n    this.last = null\n  }\n\n  enqueue(value: V): boolean {\n    const newNode = new QueueNode(value)\n    if (!this.last) {\n      // list is empty\n      this.first = newNode\n      this.last = newNode\n    } else if (this.last === this.first) {\n      // list has a single element\n      this.last = newNode\n      this.last.prev = this.first\n      this.first.next = newNode\n    } else {\n      // list has elements, add as last\n      newNode.prev = this.last\n      this.last.next = newNode\n      this.last = newNode\n    }\n    return true\n  }\n\n  dequeue(): V | null {\n    if (!this.first) return null\n\n    if (this.first === this.last) {\n      const target = this.first\n      this.first = null\n      this.last = null\n      return target.value\n    }\n\n    const target = this.first\n    this.first = target.next\n    if (this.first) {\n      this.first.prev = null // fix: clean up prev pointer\n    }\n\n    return target.value\n  }\n}\n", "import { NostrEvent, validateEvent } from './pure.ts'\n\n/** Events are **regular**, which means they're all expected to be stored by relays. */\nexport function isRegularKind(kind: number): boolean {\n  return kind < 10000 && kind !== 0 && kind !== 3\n}\n\n/** Events are **replaceable**, which means that, for each combination of `pubkey` and `kind`, only the latest event is expected to (SHOULD) be stored by relays, older versions are expected to be discarded. */\nexport function isReplaceableKind(kind: number): boolean {\n  return kind === 0 || kind === 3 || (10000 <= kind && kind < 20000)\n}\n\n/** Events are **ephemeral**, which means they are not expected to be stored by relays. */\nexport function isEphemeralKind(kind: number): boolean {\n  return 20000 <= kind && kind < 30000\n}\n\n/** Events are **addressable**, which means that, for each combination of `pubkey`, `kind` and the `d` tag, only the latest event is expected to be stored by relays, older versions are expected to be discarded. */\nexport function isAddressableKind(kind: number): boolean {\n  return 30000 <= kind && kind < 40000\n}\n\n/** Classification of the event kind. */\nexport type KindClassification = 'regular' | 'replaceable' | 'ephemeral' | 'parameterized' | 'unknown'\n\n/** Determine the classification of this kind of event if known, or `unknown`. */\nexport function classifyKind(kind: number): KindClassification {\n  if (isRegularKind(kind)) return 'regular'\n  if (isReplaceableKind(kind)) return 'replaceable'\n  if (isEphemeralKind(kind)) return 'ephemeral'\n  if (isAddressableKind(kind)) return 'parameterized'\n  return 'unknown'\n}\n\nexport function isKind<T extends number>(event: unknown, kind: T | Array<T>): event is NostrEvent & { kind: T } {\n  const kindAsArray: number[] = kind instanceof Array ? kind : [kind]\n  return (validateEvent(event) && kindAsArray.includes(event.kind)) || false\n}\n\nexport const Metadata = 0\nexport type Metadata = typeof Metadata\nexport const ShortTextNote = 1\nexport type ShortTextNote = typeof ShortTextNote\nexport const RecommendRelay = 2\nexport type RecommendRelay = typeof RecommendRelay\nexport const Contacts = 3\nexport type Contacts = typeof Contacts\nexport const EncryptedDirectMessage = 4\nexport type EncryptedDirectMessage = typeof EncryptedDirectMessage\nexport const EventDeletion = 5\nexport type EventDeletion = typeof EventDeletion\nexport const Repost = 6\nexport type Repost = typeof Repost\nexport const Reaction = 7\nexport type Reaction = typeof Reaction\nexport const BadgeAward = 8\nexport type BadgeAward = typeof BadgeAward\nexport const ChatMessage = 9\nexport type ChatMessage = typeof ChatMessage\nexport const ForumThread = 11\nexport type ForumThread = typeof ForumThread\nexport const Seal = 13\nexport type Seal = typeof Seal\nexport const PrivateDirectMessage = 14\nexport type PrivateDirectMessage = typeof PrivateDirectMessage\nexport const FileMessage = 15\nexport type FileMessage = typeof FileMessage\nexport const GenericRepost = 16\nexport type GenericRepost = typeof GenericRepost\nexport const Photo = 20\nexport type Photo = typeof Photo\nexport const NormalVideo = 21\nexport type NormalVideo = typeof NormalVideo\nexport const ShortVideo = 22\nexport type ShortVideo = typeof ShortVideo\nexport const ChannelCreation = 40\nexport type ChannelCreation = typeof ChannelCreation\nexport const ChannelMetadata = 41\nexport type ChannelMetadata = typeof ChannelMetadata\nexport const ChannelMessage = 42\nexport type ChannelMessage = typeof ChannelMessage\nexport const ChannelHideMessage = 43\nexport type ChannelHideMessage = typeof ChannelHideMessage\nexport const ChannelMuteUser = 44\nexport type ChannelMuteUser = typeof ChannelMuteUser\nexport const OpenTimestamps = 1040\nexport type OpenTimestamps = typeof OpenTimestamps\nexport const GiftWrap = 1059\nexport type GiftWrap = typeof GiftWrap\nexport const Poll = 1068\nexport type Poll = typeof Poll\nexport const FileMetadata = 1063\nexport type FileMetadata = typeof FileMetadata\nexport const Comment = 1111\nexport type Comment = typeof Comment\nexport const LiveChatMessage = 1311\nexport type LiveChatMessage = typeof LiveChatMessage\nexport const Voice = 1222\nexport type Voice = typeof Voice\nexport const VoiceComment = 1244\nexport type VoiceComment = typeof VoiceComment\nexport const ProblemTracker = 1971\nexport type ProblemTracker = typeof ProblemTracker\nexport const Report = 1984\nexport type Report = typeof Report\nexport const Reporting = 1984\nexport type Reporting = typeof Reporting\nexport const Label = 1985\nexport type Label = typeof Label\nexport const CommunityPostApproval = 4550\nexport type CommunityPostApproval = typeof CommunityPostApproval\nexport const JobRequest = 5999\nexport type JobRequest = typeof JobRequest\nexport const JobResult = 6999\nexport type JobResult = typeof JobResult\nexport const JobFeedback = 7000\nexport type JobFeedback = typeof JobFeedback\nexport const ZapGoal = 9041\nexport type ZapGoal = typeof ZapGoal\nexport const ZapRequest = 9734\nexport type ZapRequest = typeof ZapRequest\nexport const Zap = 9735\nexport type Zap = typeof Zap\nexport const Highlights = 9802\nexport type Highlights = typeof Highlights\nexport const PollResponse = 1018\nexport type PollResponse = typeof PollResponse\nexport const Mutelist = 10000\nexport type Mutelist = typeof Mutelist\nexport const Pinlist = 10001\nexport type Pinlist = typeof Pinlist\nexport const RelayList = 10002\nexport type RelayList = typeof RelayList\nexport const BookmarkList = 10003\nexport type BookmarkList = typeof BookmarkList\nexport const CommunitiesList = 10004\nexport type CommunitiesList = typeof CommunitiesList\nexport const PublicChatsList = 10005\nexport type PublicChatsList = typeof PublicChatsList\nexport const BlockedRelaysList = 10006\nexport type BlockedRelaysList = typeof BlockedRelaysList\nexport const SearchRelaysList = 10007\nexport type SearchRelaysList = typeof SearchRelaysList\nexport const FavoriteRelays = 10012\nexport type FavoriteRelays = typeof FavoriteRelays\nexport const InterestsList = 10015\nexport type InterestsList = typeof InterestsList\nexport const UserEmojiList = 10030\nexport type UserEmojiList = typeof UserEmojiList\nexport const DirectMessageRelaysList = 10050\nexport type DirectMessageRelaysList = typeof DirectMessageRelaysList\nexport const FileServerPreference = 10096\nexport type FileServerPreference = typeof FileServerPreference\nexport const BlossomServerList = 10063\nexport type BlossomServerList = typeof BlossomServerList\nexport const NWCWalletInfo = 13194\nexport type NWCWalletInfo = typeof NWCWalletInfo\nexport const LightningPubRPC = 21000\nexport type LightningPubRPC = typeof LightningPubRPC\nexport const ClientAuth = 22242\nexport type ClientAuth = typeof ClientAuth\nexport const NWCWalletRequest = 23194\nexport type NWCWalletRequest = typeof NWCWalletRequest\nexport const NWCWalletResponse = 23195\nexport type NWCWalletResponse = typeof NWCWalletResponse\nexport const NostrConnect = 24133\nexport type NostrConnect = typeof NostrConnect\nexport const HTTPAuth = 27235\nexport type HTTPAuth = typeof HTTPAuth\nexport const Followsets = 30000\nexport type Followsets = typeof Followsets\nexport const Genericlists = 30001\nexport type Genericlists = typeof Genericlists\nexport const Relaysets = 30002\nexport type Relaysets = typeof Relaysets\nexport const Bookmarksets = 30003\nexport type Bookmarksets = typeof Bookmarksets\nexport const Curationsets = 30004\nexport type Curationsets = typeof Curationsets\nexport const ProfileBadges = 30008\nexport type ProfileBadges = typeof ProfileBadges\nexport const BadgeDefinition = 30009\nexport type BadgeDefinition = typeof BadgeDefinition\nexport const Interestsets = 30015\nexport type Interestsets = typeof Interestsets\nexport const CreateOrUpdateStall = 30017\nexport type CreateOrUpdateStall = typeof CreateOrUpdateStall\nexport const CreateOrUpdateProduct = 30018\nexport type CreateOrUpdateProduct = typeof CreateOrUpdateProduct\nexport const LongFormArticle = 30023\nexport type LongFormArticle = typeof LongFormArticle\nexport const DraftLong = 30024\nexport type DraftLong = typeof DraftLong\nexport const Emojisets = 30030\nexport type Emojisets = typeof Emojisets\nexport const Application = 30078\nexport type Application = typeof Application\nexport const LiveEvent = 30311\nexport type LiveEvent = typeof LiveEvent\nexport const UserStatuses = 30315\nexport type UserStatuses = typeof UserStatuses\nexport const ClassifiedListing = 30402\nexport type ClassifiedListing = typeof ClassifiedListing\nexport const DraftClassifiedListing = 30403\nexport type DraftClassifiedListing = typeof DraftClassifiedListing\nexport const Date = 31922\nexport type Date = typeof Date\nexport const Time = 31923\nexport type Time = typeof Time\nexport const Calendar = 31924\nexport type Calendar = typeof Calendar\nexport const CalendarEventRSVP = 31925\nexport type CalendarEventRSVP = typeof CalendarEventRSVP\nexport const RelayReview = 31987\nexport type RelayReview = typeof RelayReview\nexport const Handlerrecommendation = 31989\nexport type Handlerrecommendation = typeof Handlerrecommendation\nexport const Handlerinformation = 31990\nexport type Handlerinformation = typeof Handlerinformation\nexport const CommunityDefinition = 34550\nexport type CommunityDefinition = typeof CommunityDefinition\nexport const GroupMetadata = 39000\nexport type GroupMetadata = typeof GroupMetadata\n"],
-  "mappings": "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",
+  "sourcesContent": ["import { Event, finalizeEvent } from './pure.ts'\nimport {\n  ChannelCreation,\n  ChannelHideMessage,\n  ChannelMessage,\n  ChannelMetadata as KindChannelMetadata,\n  ChannelMuteUser,\n} from './kinds.ts'\n\nexport interface ChannelMetadata {\n  name: string\n  about: string\n  picture: string\n}\n\nexport interface ChannelCreateEventTemplate {\n  /* JSON string containing ChannelMetadata as defined for Kind 40 and 41 in nip-28. */\n  content: string | ChannelMetadata\n  created_at: number\n  tags?: string[][]\n}\n\nexport interface ChannelMetadataEventTemplate {\n  channel_create_event_id: string\n  /* JSON string containing ChannelMetadata as defined for Kind 40 and 41 in nip-28. */\n  content: string | ChannelMetadata\n  created_at: number\n  tags?: string[][]\n}\n\nexport interface ChannelMessageEventTemplate {\n  channel_create_event_id: string\n  reply_to_channel_message_event_id?: string\n  relay_url: string\n  content: string\n  created_at: number\n  tags?: string[][]\n}\n\nexport interface ChannelHideMessageEventTemplate {\n  channel_message_event_id: string\n  content: string | { reason: string }\n  created_at: number\n  tags?: string[][]\n}\n\nexport interface ChannelMuteUserEventTemplate {\n  content: string | { reason: string }\n  created_at: number\n  pubkey_to_mute: string\n  tags?: string[][]\n}\n\nexport const channelCreateEvent = (t: ChannelCreateEventTemplate, privateKey: Uint8Array): Event | undefined => {\n  let content: string\n  if (typeof t.content === 'object') {\n    content = JSON.stringify(t.content)\n  } else if (typeof t.content === 'string') {\n    content = t.content\n  } else {\n    return undefined\n  }\n\n  return finalizeEvent(\n    {\n      kind: ChannelCreation,\n      tags: [...(t.tags ?? [])],\n      content: content,\n      created_at: t.created_at,\n    },\n    privateKey,\n  )\n}\n\nexport const channelMetadataEvent = (t: ChannelMetadataEventTemplate, privateKey: Uint8Array): Event | undefined => {\n  let content: string\n  if (typeof t.content === 'object') {\n    content = JSON.stringify(t.content)\n  } else if (typeof t.content === 'string') {\n    content = t.content\n  } else {\n    return undefined\n  }\n\n  return finalizeEvent(\n    {\n      kind: KindChannelMetadata,\n      tags: [['e', t.channel_create_event_id], ...(t.tags ?? [])],\n      content: content,\n      created_at: t.created_at,\n    },\n    privateKey,\n  )\n}\n\nexport const channelMessageEvent = (t: ChannelMessageEventTemplate, privateKey: Uint8Array): Event => {\n  const tags = [['e', t.channel_create_event_id, t.relay_url, 'root']]\n\n  if (t.reply_to_channel_message_event_id) {\n    tags.push(['e', t.reply_to_channel_message_event_id, t.relay_url, 'reply'])\n  }\n\n  return finalizeEvent(\n    {\n      kind: ChannelMessage,\n      tags: [...tags, ...(t.tags ?? [])],\n      content: t.content,\n      created_at: t.created_at,\n    },\n    privateKey,\n  )\n}\n\n/* \"e\" tag should be the kind 42 event to hide */\nexport const channelHideMessageEvent = (\n  t: ChannelHideMessageEventTemplate,\n  privateKey: Uint8Array,\n): Event | undefined => {\n  let content: string\n  if (typeof t.content === 'object') {\n    content = JSON.stringify(t.content)\n  } else if (typeof t.content === 'string') {\n    content = t.content\n  } else {\n    return undefined\n  }\n\n  return finalizeEvent(\n    {\n      kind: ChannelHideMessage,\n      tags: [['e', t.channel_message_event_id], ...(t.tags ?? [])],\n      content: content,\n      created_at: t.created_at,\n    },\n    privateKey,\n  )\n}\n\nexport const channelMuteUserEvent = (t: ChannelMuteUserEventTemplate, privateKey: Uint8Array): Event | undefined => {\n  let content: string\n  if (typeof t.content === 'object') {\n    content = JSON.stringify(t.content)\n  } else if (typeof t.content === 'string') {\n    content = t.content\n  } else {\n    return undefined\n  }\n\n  return finalizeEvent(\n    {\n      kind: ChannelMuteUser,\n      tags: [['p', t.pubkey_to_mute], ...(t.tags ?? [])],\n      content: content,\n      created_at: t.created_at,\n    },\n    privateKey,\n  )\n}\n", "import { schnorr } from '@noble/curves/secp256k1.js'\nimport { bytesToHex, hexToBytes } from '@noble/hashes/utils.js'\nimport { Nostr, Event, EventTemplate, UnsignedEvent, VerifiedEvent, verifiedSymbol, validateEvent } from './core.ts'\nimport { sha256 } from '@noble/hashes/sha2.js'\n\nimport { utf8Encoder } from './utils.ts'\n\nclass JS implements Nostr {\n  generateSecretKey(): Uint8Array {\n    return schnorr.utils.randomSecretKey()\n  }\n  getPublicKey(secretKey: Uint8Array): string {\n    return bytesToHex(schnorr.getPublicKey(secretKey))\n  }\n  finalizeEvent(t: EventTemplate, secretKey: Uint8Array): VerifiedEvent {\n    const event = t as VerifiedEvent\n    event.pubkey = bytesToHex(schnorr.getPublicKey(secretKey))\n    event.id = getEventHash(event)\n    event.sig = bytesToHex(schnorr.sign(hexToBytes(getEventHash(event)), secretKey))\n    event[verifiedSymbol] = true\n    return event\n  }\n  verifyEvent(event: Event): event is VerifiedEvent {\n    if (typeof event[verifiedSymbol] === 'boolean') return event[verifiedSymbol]\n\n    const hash = getEventHash(event)\n    if (hash !== event.id) {\n      event[verifiedSymbol] = false\n      return false\n    }\n\n    try {\n      const valid = schnorr.verify(hexToBytes(event.sig), hexToBytes(hash), hexToBytes(event.pubkey))\n      event[verifiedSymbol] = valid\n      return valid\n    } catch (err) {\n      event[verifiedSymbol] = false\n      return false\n    }\n  }\n}\n\nexport function serializeEvent(evt: UnsignedEvent): string {\n  if (!validateEvent(evt)) throw new Error(\"can't serialize event with wrong or missing properties\")\n  return JSON.stringify([0, evt.pubkey, evt.created_at, evt.kind, evt.tags, evt.content])\n}\n\nexport function getEventHash(event: UnsignedEvent): string {\n  let eventHash = sha256(utf8Encoder.encode(serializeEvent(event)))\n  return bytesToHex(eventHash)\n}\n\nconst i: JS = new JS()\n\nexport const generateSecretKey = i.generateSecretKey\nexport const getPublicKey = i.getPublicKey\nexport const finalizeEvent = i.finalizeEvent\nexport const verifyEvent = i.verifyEvent\nexport * from './core.ts'\n", "export interface Nostr {\n  generateSecretKey(): Uint8Array\n  getPublicKey(secretKey: Uint8Array): string\n  finalizeEvent(event: EventTemplate, secretKey: Uint8Array): VerifiedEvent\n  verifyEvent(event: Event): event is VerifiedEvent\n}\n\n/** Designates a verified event signature. */\nexport const verifiedSymbol = Symbol('verified')\n\nexport type NostrEvent = {\n  kind: number\n  tags: string[][]\n  content: string\n  created_at: number\n  pubkey: string\n  id: string\n  sig: string\n  [verifiedSymbol]?: boolean\n}\n\nexport type Event = NostrEvent\nexport type EventTemplate = Pick<Event, 'kind' | 'tags' | 'content' | 'created_at'>\nexport type UnsignedEvent = Pick<Event, 'kind' | 'tags' | 'content' | 'created_at' | 'pubkey'>\n\n/** An event whose signature has been verified. */\nexport interface VerifiedEvent extends Event {\n  [verifiedSymbol]: true\n}\n\nconst isRecord = (obj: unknown): obj is Record<string, unknown> => obj instanceof Object\n\nexport function validateEvent<T>(event: T): event is T & UnsignedEvent {\n  if (!isRecord(event)) return false\n  if (typeof event.kind !== 'number') return false\n  if (typeof event.content !== 'string') return false\n  if (typeof event.created_at !== 'number') return false\n  if (typeof event.pubkey !== 'string') return false\n  if (!event.pubkey.match(/^[a-f0-9]{64}$/)) return false\n\n  if (!Array.isArray(event.tags)) return false\n  for (let i = 0; i < event.tags.length; i++) {\n    let tag = event.tags[i]\n    if (!Array.isArray(tag)) return false\n    for (let j = 0; j < tag.length; j++) {\n      if (typeof tag[j] !== 'string') return false\n    }\n  }\n\n  return true\n}\n\n/**\n * Sort events in reverse-chronological order by the `created_at` timestamp,\n * and then by the event `id` (lexicographically) in case of ties.\n * This mutates the array.\n */\nexport function sortEvents(events: Event[]): Event[] {\n  return events.sort((a: NostrEvent, b: NostrEvent): number => {\n    if (a.created_at !== b.created_at) {\n      return b.created_at - a.created_at\n    }\n    return a.id.localeCompare(b.id)\n  })\n}\n", "import type { Event } from './core.ts'\n\nexport const utf8Decoder: TextDecoder = new TextDecoder('utf-8')\nexport const utf8Encoder: TextEncoder = new TextEncoder()\n\nexport { bytesToHex, hexToBytes } from '@noble/hashes/utils.js'\n\nexport function normalizeURL(url: string): string {\n  try {\n    if (url.indexOf('://') === -1) url = 'wss://' + url\n    let p = new URL(url)\n    if (p.protocol === 'http:') p.protocol = 'ws:'\n    else if (p.protocol === 'https:') p.protocol = 'wss:'\n    p.pathname = p.pathname.replace(/\\/+/g, '/')\n    if (p.pathname.endsWith('/')) p.pathname = p.pathname.slice(0, -1)\n    if ((p.port === '80' && p.protocol === 'ws:') || (p.port === '443' && p.protocol === 'wss:')) p.port = ''\n    p.searchParams.sort()\n    p.hash = ''\n    return p.toString()\n  } catch (e) {\n    throw new Error(`Invalid URL: ${url}`)\n  }\n}\n\nexport function insertEventIntoDescendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return b.created_at - event.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function insertEventIntoAscendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return event.created_at - b.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function binarySearch<T>(arr: T[], compare: (b: T) => number): [number, boolean] {\n  let start = 0\n  let end = arr.length - 1\n\n  while (start <= end) {\n    const mid = Math.floor((start + end) / 2)\n    const cmp = compare(arr[mid])\n\n    if (cmp === 0) {\n      return [mid, true]\n    }\n\n    if (cmp < 0) {\n      end = mid - 1\n    } else {\n      start = mid + 1\n    }\n  }\n\n  return [start, false]\n}\n\nexport class QueueNode<V> {\n  public value: V\n  public next: QueueNode<V> | null = null\n  public prev: QueueNode<V> | null = null\n\n  constructor(message: V) {\n    this.value = message\n  }\n}\n\nexport class Queue<V> {\n  public first: QueueNode<V> | null\n  public last: QueueNode<V> | null\n\n  constructor() {\n    this.first = null\n    this.last = null\n  }\n\n  enqueue(value: V): boolean {\n    const newNode = new QueueNode(value)\n    if (!this.last) {\n      // list is empty\n      this.first = newNode\n      this.last = newNode\n    } else if (this.last === this.first) {\n      // list has a single element\n      this.last = newNode\n      this.last.prev = this.first\n      this.first.next = newNode\n    } else {\n      // list has elements, add as last\n      newNode.prev = this.last\n      this.last.next = newNode\n      this.last = newNode\n    }\n    return true\n  }\n\n  dequeue(): V | null {\n    if (!this.first) return null\n\n    if (this.first === this.last) {\n      const target = this.first\n      this.first = null\n      this.last = null\n      return target.value\n    }\n\n    const target = this.first\n    this.first = target.next\n    if (this.first) {\n      this.first.prev = null // fix: clean up prev pointer\n    }\n\n    return target.value\n  }\n}\n", "import { NostrEvent, validateEvent } from './pure.ts'\n\n/** Events are **regular**, which means they're all expected to be stored by relays. */\nexport function isRegularKind(kind: number): boolean {\n  return kind < 10000 && kind !== 0 && kind !== 3\n}\n\n/** Events are **replaceable**, which means that, for each combination of `pubkey` and `kind`, only the latest event is expected to (SHOULD) be stored by relays, older versions are expected to be discarded. */\nexport function isReplaceableKind(kind: number): boolean {\n  return kind === 0 || kind === 3 || (10000 <= kind && kind < 20000)\n}\n\n/** Events are **ephemeral**, which means they are not expected to be stored by relays. */\nexport function isEphemeralKind(kind: number): boolean {\n  return 20000 <= kind && kind < 30000\n}\n\n/** Events are **addressable**, which means that, for each combination of `pubkey`, `kind` and the `d` tag, only the latest event is expected to be stored by relays, older versions are expected to be discarded. */\nexport function isAddressableKind(kind: number): boolean {\n  return 30000 <= kind && kind < 40000\n}\n\n/** Classification of the event kind. */\nexport type KindClassification = 'regular' | 'replaceable' | 'ephemeral' | 'parameterized' | 'unknown'\n\n/** Determine the classification of this kind of event if known, or `unknown`. */\nexport function classifyKind(kind: number): KindClassification {\n  if (isRegularKind(kind)) return 'regular'\n  if (isReplaceableKind(kind)) return 'replaceable'\n  if (isEphemeralKind(kind)) return 'ephemeral'\n  if (isAddressableKind(kind)) return 'parameterized'\n  return 'unknown'\n}\n\nexport function isKind<T extends number>(event: unknown, kind: T | Array<T>): event is NostrEvent & { kind: T } {\n  const kindAsArray: number[] = kind instanceof Array ? kind : [kind]\n  return (validateEvent(event) && kindAsArray.includes(event.kind)) || false\n}\n\nexport const Metadata = 0\nexport type Metadata = typeof Metadata\nexport const ShortTextNote = 1\nexport type ShortTextNote = typeof ShortTextNote\nexport const RecommendRelay = 2\nexport type RecommendRelay = typeof RecommendRelay\nexport const Contacts = 3\nexport type Contacts = typeof Contacts\nexport const EncryptedDirectMessage = 4\nexport type EncryptedDirectMessage = typeof EncryptedDirectMessage\nexport const EventDeletion = 5\nexport type EventDeletion = typeof EventDeletion\nexport const Repost = 6\nexport type Repost = typeof Repost\nexport const Reaction = 7\nexport type Reaction = typeof Reaction\nexport const BadgeAward = 8\nexport type BadgeAward = typeof BadgeAward\nexport const ChatMessage = 9\nexport type ChatMessage = typeof ChatMessage\nexport const ForumThread = 11\nexport type ForumThread = typeof ForumThread\nexport const Seal = 13\nexport type Seal = typeof Seal\nexport const PrivateDirectMessage = 14\nexport type PrivateDirectMessage = typeof PrivateDirectMessage\nexport const FileMessage = 15\nexport type FileMessage = typeof FileMessage\nexport const GenericRepost = 16\nexport type GenericRepost = typeof GenericRepost\nexport const Photo = 20\nexport type Photo = typeof Photo\nexport const NormalVideo = 21\nexport type NormalVideo = typeof NormalVideo\nexport const ShortVideo = 22\nexport type ShortVideo = typeof ShortVideo\nexport const ChannelCreation = 40\nexport type ChannelCreation = typeof ChannelCreation\nexport const ChannelMetadata = 41\nexport type ChannelMetadata = typeof ChannelMetadata\nexport const ChannelMessage = 42\nexport type ChannelMessage = typeof ChannelMessage\nexport const ChannelHideMessage = 43\nexport type ChannelHideMessage = typeof ChannelHideMessage\nexport const ChannelMuteUser = 44\nexport type ChannelMuteUser = typeof ChannelMuteUser\nexport const OpenTimestamps = 1040\nexport type OpenTimestamps = typeof OpenTimestamps\nexport const GiftWrap = 1059\nexport type GiftWrap = typeof GiftWrap\nexport const Poll = 1068\nexport type Poll = typeof Poll\nexport const FileMetadata = 1063\nexport type FileMetadata = typeof FileMetadata\nexport const Comment = 1111\nexport type Comment = typeof Comment\nexport const LiveChatMessage = 1311\nexport type LiveChatMessage = typeof LiveChatMessage\nexport const Voice = 1222\nexport type Voice = typeof Voice\nexport const VoiceComment = 1244\nexport type VoiceComment = typeof VoiceComment\nexport const ProblemTracker = 1971\nexport type ProblemTracker = typeof ProblemTracker\nexport const Report = 1984\nexport type Report = typeof Report\nexport const Reporting = 1984\nexport type Reporting = typeof Reporting\nexport const Label = 1985\nexport type Label = typeof Label\nexport const CommunityPostApproval = 4550\nexport type CommunityPostApproval = typeof CommunityPostApproval\nexport const JobRequest = 5999\nexport type JobRequest = typeof JobRequest\nexport const JobResult = 6999\nexport type JobResult = typeof JobResult\nexport const JobFeedback = 7000\nexport type JobFeedback = typeof JobFeedback\nexport const ZapGoal = 9041\nexport type ZapGoal = typeof ZapGoal\nexport const ZapRequest = 9734\nexport type ZapRequest = typeof ZapRequest\nexport const Zap = 9735\nexport type Zap = typeof Zap\nexport const Highlights = 9802\nexport type Highlights = typeof Highlights\nexport const PollResponse = 1018\nexport type PollResponse = typeof PollResponse\nexport const Mutelist = 10000\nexport type Mutelist = typeof Mutelist\nexport const Pinlist = 10001\nexport type Pinlist = typeof Pinlist\nexport const RelayList = 10002\nexport type RelayList = typeof RelayList\nexport const BookmarkList = 10003\nexport type BookmarkList = typeof BookmarkList\nexport const CommunitiesList = 10004\nexport type CommunitiesList = typeof CommunitiesList\nexport const PublicChatsList = 10005\nexport type PublicChatsList = typeof PublicChatsList\nexport const BlockedRelaysList = 10006\nexport type BlockedRelaysList = typeof BlockedRelaysList\nexport const SearchRelaysList = 10007\nexport type SearchRelaysList = typeof SearchRelaysList\nexport const FavoriteRelays = 10012\nexport type FavoriteRelays = typeof FavoriteRelays\nexport const InterestsList = 10015\nexport type InterestsList = typeof InterestsList\nexport const UserEmojiList = 10030\nexport type UserEmojiList = typeof UserEmojiList\nexport const DirectMessageRelaysList = 10050\nexport type DirectMessageRelaysList = typeof DirectMessageRelaysList\nexport const FileServerPreference = 10096\nexport type FileServerPreference = typeof FileServerPreference\nexport const BlossomServerList = 10063\nexport type BlossomServerList = typeof BlossomServerList\nexport const NWCWalletInfo = 13194\nexport type NWCWalletInfo = typeof NWCWalletInfo\nexport const LightningPubRPC = 21000\nexport type LightningPubRPC = typeof LightningPubRPC\nexport const ClientAuth = 22242\nexport type ClientAuth = typeof ClientAuth\nexport const NWCWalletRequest = 23194\nexport type NWCWalletRequest = typeof NWCWalletRequest\nexport const NWCWalletResponse = 23195\nexport type NWCWalletResponse = typeof NWCWalletResponse\nexport const NostrConnect = 24133\nexport type NostrConnect = typeof NostrConnect\nexport const HTTPAuth = 27235\nexport type HTTPAuth = typeof HTTPAuth\nexport const Followsets = 30000\nexport type Followsets = typeof Followsets\nexport const Genericlists = 30001\nexport type Genericlists = typeof Genericlists\nexport const Relaysets = 30002\nexport type Relaysets = typeof Relaysets\nexport const Bookmarksets = 30003\nexport type Bookmarksets = typeof Bookmarksets\nexport const Curationsets = 30004\nexport type Curationsets = typeof Curationsets\nexport const ProfileBadges = 30008\nexport type ProfileBadges = typeof ProfileBadges\nexport const BadgeDefinition = 30009\nexport type BadgeDefinition = typeof BadgeDefinition\nexport const Interestsets = 30015\nexport type Interestsets = typeof Interestsets\nexport const CreateOrUpdateStall = 30017\nexport type CreateOrUpdateStall = typeof CreateOrUpdateStall\nexport const CreateOrUpdateProduct = 30018\nexport type CreateOrUpdateProduct = typeof CreateOrUpdateProduct\nexport const LongFormArticle = 30023\nexport type LongFormArticle = typeof LongFormArticle\nexport const DraftLong = 30024\nexport type DraftLong = typeof DraftLong\nexport const Emojisets = 30030\nexport type Emojisets = typeof Emojisets\nexport const Application = 30078\nexport type Application = typeof Application\nexport const LiveEvent = 30311\nexport type LiveEvent = typeof LiveEvent\nexport const UserStatuses = 30315\nexport type UserStatuses = typeof UserStatuses\nexport const ClassifiedListing = 30402\nexport type ClassifiedListing = typeof ClassifiedListing\nexport const DraftClassifiedListing = 30403\nexport type DraftClassifiedListing = typeof DraftClassifiedListing\nexport const Date = 31922\nexport type Date = typeof Date\nexport const Time = 31923\nexport type Time = typeof Time\nexport const Calendar = 31924\nexport type Calendar = typeof Calendar\nexport const CalendarEventRSVP = 31925\nexport type CalendarEventRSVP = typeof CalendarEventRSVP\nexport const RelayReview = 31987\nexport type RelayReview = typeof RelayReview\nexport const Handlerrecommendation = 31989\nexport type Handlerrecommendation = typeof Handlerrecommendation\nexport const Handlerinformation = 31990\nexport type Handlerinformation = typeof Handlerinformation\nexport const CommunityDefinition = 34550\nexport type CommunityDefinition = typeof CommunityDefinition\nexport const GroupMetadata = 39000\nexport type GroupMetadata = typeof GroupMetadata\n"],
+  "mappings": "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",
   "names": ["import_utils", "i"]
 }
Index: package/lib/esm/nip28.js.map
===================================================================
--- package/lib/esm/nip28.js.map
+++ package/lib/esm/nip28.js.map
@@ -1,7 +1,7 @@
 {
   "version": 3,
   "sources": ["../../pure.ts", "../../core.ts", "../../utils.ts", "../../kinds.ts", "../../nip28.ts"],
-  "sourcesContent": ["import { schnorr } from '@noble/curves/secp256k1'\nimport { bytesToHex } from '@noble/hashes/utils'\nimport { Nostr, Event, EventTemplate, UnsignedEvent, VerifiedEvent, verifiedSymbol, validateEvent } from './core.ts'\nimport { sha256 } from '@noble/hashes/sha256'\n\nimport { utf8Encoder } from './utils.ts'\n\nclass JS implements Nostr {\n  generateSecretKey(): Uint8Array {\n    return schnorr.utils.randomPrivateKey()\n  }\n  getPublicKey(secretKey: Uint8Array): string {\n    return bytesToHex(schnorr.getPublicKey(secretKey))\n  }\n  finalizeEvent(t: EventTemplate, secretKey: Uint8Array): VerifiedEvent {\n    const event = t as VerifiedEvent\n    event.pubkey = bytesToHex(schnorr.getPublicKey(secretKey))\n    event.id = getEventHash(event)\n    event.sig = bytesToHex(schnorr.sign(getEventHash(event), secretKey))\n    event[verifiedSymbol] = true\n    return event\n  }\n  verifyEvent(event: Event): event is VerifiedEvent {\n    if (typeof event[verifiedSymbol] === 'boolean') return event[verifiedSymbol]\n\n    const hash = getEventHash(event)\n    if (hash !== event.id) {\n      event[verifiedSymbol] = false\n      return false\n    }\n\n    try {\n      const valid = schnorr.verify(event.sig, hash, event.pubkey)\n      event[verifiedSymbol] = valid\n      return valid\n    } catch (err) {\n      event[verifiedSymbol] = false\n      return false\n    }\n  }\n}\n\nexport function serializeEvent(evt: UnsignedEvent): string {\n  if (!validateEvent(evt)) throw new Error(\"can't serialize event with wrong or missing properties\")\n  return JSON.stringify([0, evt.pubkey, evt.created_at, evt.kind, evt.tags, evt.content])\n}\n\nexport function getEventHash(event: UnsignedEvent): string {\n  let eventHash = sha256(utf8Encoder.encode(serializeEvent(event)))\n  return bytesToHex(eventHash)\n}\n\nconst i: JS = new JS()\n\nexport const generateSecretKey = i.generateSecretKey\nexport const getPublicKey = i.getPublicKey\nexport const finalizeEvent = i.finalizeEvent\nexport const verifyEvent = i.verifyEvent\nexport * from './core.ts'\n", "export interface Nostr {\n  generateSecretKey(): Uint8Array\n  getPublicKey(secretKey: Uint8Array): string\n  finalizeEvent(event: EventTemplate, secretKey: Uint8Array): VerifiedEvent\n  verifyEvent(event: Event): event is VerifiedEvent\n}\n\n/** Designates a verified event signature. */\nexport const verifiedSymbol = Symbol('verified')\n\nexport type NostrEvent = {\n  kind: number\n  tags: string[][]\n  content: string\n  created_at: number\n  pubkey: string\n  id: string\n  sig: string\n  [verifiedSymbol]?: boolean\n}\n\nexport type Event = NostrEvent\nexport type EventTemplate = Pick<Event, 'kind' | 'tags' | 'content' | 'created_at'>\nexport type UnsignedEvent = Pick<Event, 'kind' | 'tags' | 'content' | 'created_at' | 'pubkey'>\n\n/** An event whose signature has been verified. */\nexport interface VerifiedEvent extends Event {\n  [verifiedSymbol]: true\n}\n\nconst isRecord = (obj: unknown): obj is Record<string, unknown> => obj instanceof Object\n\nexport function validateEvent<T>(event: T): event is T & UnsignedEvent {\n  if (!isRecord(event)) return false\n  if (typeof event.kind !== 'number') return false\n  if (typeof event.content !== 'string') return false\n  if (typeof event.created_at !== 'number') return false\n  if (typeof event.pubkey !== 'string') return false\n  if (!event.pubkey.match(/^[a-f0-9]{64}$/)) return false\n\n  if (!Array.isArray(event.tags)) return false\n  for (let i = 0; i < event.tags.length; i++) {\n    let tag = event.tags[i]\n    if (!Array.isArray(tag)) return false\n    for (let j = 0; j < tag.length; j++) {\n      if (typeof tag[j] !== 'string') return false\n    }\n  }\n\n  return true\n}\n\n/**\n * Sort events in reverse-chronological order by the `created_at` timestamp,\n * and then by the event `id` (lexicographically) in case of ties.\n * This mutates the array.\n */\nexport function sortEvents(events: Event[]): Event[] {\n  return events.sort((a: NostrEvent, b: NostrEvent): number => {\n    if (a.created_at !== b.created_at) {\n      return b.created_at - a.created_at\n    }\n    return a.id.localeCompare(b.id)\n  })\n}\n", "import type { Event } from './core.ts'\n\nexport const utf8Decoder: TextDecoder = new TextDecoder('utf-8')\nexport const utf8Encoder: TextEncoder = new TextEncoder()\n\nexport { bytesToHex, hexToBytes } from '@noble/hashes/utils'\n\nexport function normalizeURL(url: string): string {\n  try {\n    if (url.indexOf('://') === -1) url = 'wss://' + url\n    let p = new URL(url)\n    if (p.protocol === 'http:') p.protocol = 'ws:'\n    else if (p.protocol === 'https:') p.protocol = 'wss:'\n    p.pathname = p.pathname.replace(/\\/+/g, '/')\n    if (p.pathname.endsWith('/')) p.pathname = p.pathname.slice(0, -1)\n    if ((p.port === '80' && p.protocol === 'ws:') || (p.port === '443' && p.protocol === 'wss:')) p.port = ''\n    p.searchParams.sort()\n    p.hash = ''\n    return p.toString()\n  } catch (e) {\n    throw new Error(`Invalid URL: ${url}`)\n  }\n}\n\nexport function insertEventIntoDescendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return b.created_at - event.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function insertEventIntoAscendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return event.created_at - b.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function binarySearch<T>(arr: T[], compare: (b: T) => number): [number, boolean] {\n  let start = 0\n  let end = arr.length - 1\n\n  while (start <= end) {\n    const mid = Math.floor((start + end) / 2)\n    const cmp = compare(arr[mid])\n\n    if (cmp === 0) {\n      return [mid, true]\n    }\n\n    if (cmp < 0) {\n      end = mid - 1\n    } else {\n      start = mid + 1\n    }\n  }\n\n  return [start, false]\n}\n\nexport class QueueNode<V> {\n  public value: V\n  public next: QueueNode<V> | null = null\n  public prev: QueueNode<V> | null = null\n\n  constructor(message: V) {\n    this.value = message\n  }\n}\n\nexport class Queue<V> {\n  public first: QueueNode<V> | null\n  public last: QueueNode<V> | null\n\n  constructor() {\n    this.first = null\n    this.last = null\n  }\n\n  enqueue(value: V): boolean {\n    const newNode = new QueueNode(value)\n    if (!this.last) {\n      // list is empty\n      this.first = newNode\n      this.last = newNode\n    } else if (this.last === this.first) {\n      // list has a single element\n      this.last = newNode\n      this.last.prev = this.first\n      this.first.next = newNode\n    } else {\n      // list has elements, add as last\n      newNode.prev = this.last\n      this.last.next = newNode\n      this.last = newNode\n    }\n    return true\n  }\n\n  dequeue(): V | null {\n    if (!this.first) return null\n\n    if (this.first === this.last) {\n      const target = this.first\n      this.first = null\n      this.last = null\n      return target.value\n    }\n\n    const target = this.first\n    this.first = target.next\n    if (this.first) {\n      this.first.prev = null // fix: clean up prev pointer\n    }\n\n    return target.value\n  }\n}\n", "import { NostrEvent, validateEvent } from './pure.ts'\n\n/** Events are **regular**, which means they're all expected to be stored by relays. */\nexport function isRegularKind(kind: number): boolean {\n  return kind < 10000 && kind !== 0 && kind !== 3\n}\n\n/** Events are **replaceable**, which means that, for each combination of `pubkey` and `kind`, only the latest event is expected to (SHOULD) be stored by relays, older versions are expected to be discarded. */\nexport function isReplaceableKind(kind: number): boolean {\n  return kind === 0 || kind === 3 || (10000 <= kind && kind < 20000)\n}\n\n/** Events are **ephemeral**, which means they are not expected to be stored by relays. */\nexport function isEphemeralKind(kind: number): boolean {\n  return 20000 <= kind && kind < 30000\n}\n\n/** Events are **addressable**, which means that, for each combination of `pubkey`, `kind` and the `d` tag, only the latest event is expected to be stored by relays, older versions are expected to be discarded. */\nexport function isAddressableKind(kind: number): boolean {\n  return 30000 <= kind && kind < 40000\n}\n\n/** Classification of the event kind. */\nexport type KindClassification = 'regular' | 'replaceable' | 'ephemeral' | 'parameterized' | 'unknown'\n\n/** Determine the classification of this kind of event if known, or `unknown`. */\nexport function classifyKind(kind: number): KindClassification {\n  if (isRegularKind(kind)) return 'regular'\n  if (isReplaceableKind(kind)) return 'replaceable'\n  if (isEphemeralKind(kind)) return 'ephemeral'\n  if (isAddressableKind(kind)) return 'parameterized'\n  return 'unknown'\n}\n\nexport function isKind<T extends number>(event: unknown, kind: T | Array<T>): event is NostrEvent & { kind: T } {\n  const kindAsArray: number[] = kind instanceof Array ? kind : [kind]\n  return (validateEvent(event) && kindAsArray.includes(event.kind)) || false\n}\n\nexport const Metadata = 0\nexport type Metadata = typeof Metadata\nexport const ShortTextNote = 1\nexport type ShortTextNote = typeof ShortTextNote\nexport const RecommendRelay = 2\nexport type RecommendRelay = typeof RecommendRelay\nexport const Contacts = 3\nexport type Contacts = typeof Contacts\nexport const EncryptedDirectMessage = 4\nexport type EncryptedDirectMessage = typeof EncryptedDirectMessage\nexport const EventDeletion = 5\nexport type EventDeletion = typeof EventDeletion\nexport const Repost = 6\nexport type Repost = typeof Repost\nexport const Reaction = 7\nexport type Reaction = typeof Reaction\nexport const BadgeAward = 8\nexport type BadgeAward = typeof BadgeAward\nexport const ChatMessage = 9\nexport type ChatMessage = typeof ChatMessage\nexport const ForumThread = 11\nexport type ForumThread = typeof ForumThread\nexport const Seal = 13\nexport type Seal = typeof Seal\nexport const PrivateDirectMessage = 14\nexport type PrivateDirectMessage = typeof PrivateDirectMessage\nexport const FileMessage = 15\nexport type FileMessage = typeof FileMessage\nexport const GenericRepost = 16\nexport type GenericRepost = typeof GenericRepost\nexport const Photo = 20\nexport type Photo = typeof Photo\nexport const NormalVideo = 21\nexport type NormalVideo = typeof NormalVideo\nexport const ShortVideo = 22\nexport type ShortVideo = typeof ShortVideo\nexport const ChannelCreation = 40\nexport type ChannelCreation = typeof ChannelCreation\nexport const ChannelMetadata = 41\nexport type ChannelMetadata = typeof ChannelMetadata\nexport const ChannelMessage = 42\nexport type ChannelMessage = typeof ChannelMessage\nexport const ChannelHideMessage = 43\nexport type ChannelHideMessage = typeof ChannelHideMessage\nexport const ChannelMuteUser = 44\nexport type ChannelMuteUser = typeof ChannelMuteUser\nexport const OpenTimestamps = 1040\nexport type OpenTimestamps = typeof OpenTimestamps\nexport const GiftWrap = 1059\nexport type GiftWrap = typeof GiftWrap\nexport const Poll = 1068\nexport type Poll = typeof Poll\nexport const FileMetadata = 1063\nexport type FileMetadata = typeof FileMetadata\nexport const Comment = 1111\nexport type Comment = typeof Comment\nexport const LiveChatMessage = 1311\nexport type LiveChatMessage = typeof LiveChatMessage\nexport const Voice = 1222\nexport type Voice = typeof Voice\nexport const VoiceComment = 1244\nexport type VoiceComment = typeof VoiceComment\nexport const ProblemTracker = 1971\nexport type ProblemTracker = typeof ProblemTracker\nexport const Report = 1984\nexport type Report = typeof Report\nexport const Reporting = 1984\nexport type Reporting = typeof Reporting\nexport const Label = 1985\nexport type Label = typeof Label\nexport const CommunityPostApproval = 4550\nexport type CommunityPostApproval = typeof CommunityPostApproval\nexport const JobRequest = 5999\nexport type JobRequest = typeof JobRequest\nexport const JobResult = 6999\nexport type JobResult = typeof JobResult\nexport const JobFeedback = 7000\nexport type JobFeedback = typeof JobFeedback\nexport const ZapGoal = 9041\nexport type ZapGoal = typeof ZapGoal\nexport const ZapRequest = 9734\nexport type ZapRequest = typeof ZapRequest\nexport const Zap = 9735\nexport type Zap = typeof Zap\nexport const Highlights = 9802\nexport type Highlights = typeof Highlights\nexport const PollResponse = 1018\nexport type PollResponse = typeof PollResponse\nexport const Mutelist = 10000\nexport type Mutelist = typeof Mutelist\nexport const Pinlist = 10001\nexport type Pinlist = typeof Pinlist\nexport const RelayList = 10002\nexport type RelayList = typeof RelayList\nexport const BookmarkList = 10003\nexport type BookmarkList = typeof BookmarkList\nexport const CommunitiesList = 10004\nexport type CommunitiesList = typeof CommunitiesList\nexport const PublicChatsList = 10005\nexport type PublicChatsList = typeof PublicChatsList\nexport const BlockedRelaysList = 10006\nexport type BlockedRelaysList = typeof BlockedRelaysList\nexport const SearchRelaysList = 10007\nexport type SearchRelaysList = typeof SearchRelaysList\nexport const FavoriteRelays = 10012\nexport type FavoriteRelays = typeof FavoriteRelays\nexport const InterestsList = 10015\nexport type InterestsList = typeof InterestsList\nexport const UserEmojiList = 10030\nexport type UserEmojiList = typeof UserEmojiList\nexport const DirectMessageRelaysList = 10050\nexport type DirectMessageRelaysList = typeof DirectMessageRelaysList\nexport const FileServerPreference = 10096\nexport type FileServerPreference = typeof FileServerPreference\nexport const BlossomServerList = 10063\nexport type BlossomServerList = typeof BlossomServerList\nexport const NWCWalletInfo = 13194\nexport type NWCWalletInfo = typeof NWCWalletInfo\nexport const LightningPubRPC = 21000\nexport type LightningPubRPC = typeof LightningPubRPC\nexport const ClientAuth = 22242\nexport type ClientAuth = typeof ClientAuth\nexport const NWCWalletRequest = 23194\nexport type NWCWalletRequest = typeof NWCWalletRequest\nexport const NWCWalletResponse = 23195\nexport type NWCWalletResponse = typeof NWCWalletResponse\nexport const NostrConnect = 24133\nexport type NostrConnect = typeof NostrConnect\nexport const HTTPAuth = 27235\nexport type HTTPAuth = typeof HTTPAuth\nexport const Followsets = 30000\nexport type Followsets = typeof Followsets\nexport const Genericlists = 30001\nexport type Genericlists = typeof Genericlists\nexport const Relaysets = 30002\nexport type Relaysets = typeof Relaysets\nexport const Bookmarksets = 30003\nexport type Bookmarksets = typeof Bookmarksets\nexport const Curationsets = 30004\nexport type Curationsets = typeof Curationsets\nexport const ProfileBadges = 30008\nexport type ProfileBadges = typeof ProfileBadges\nexport const BadgeDefinition = 30009\nexport type BadgeDefinition = typeof BadgeDefinition\nexport const Interestsets = 30015\nexport type Interestsets = typeof Interestsets\nexport const CreateOrUpdateStall = 30017\nexport type CreateOrUpdateStall = typeof CreateOrUpdateStall\nexport const CreateOrUpdateProduct = 30018\nexport type CreateOrUpdateProduct = typeof CreateOrUpdateProduct\nexport const LongFormArticle = 30023\nexport type LongFormArticle = typeof LongFormArticle\nexport const DraftLong = 30024\nexport type DraftLong = typeof DraftLong\nexport const Emojisets = 30030\nexport type Emojisets = typeof Emojisets\nexport const Application = 30078\nexport type Application = typeof Application\nexport const LiveEvent = 30311\nexport type LiveEvent = typeof LiveEvent\nexport const UserStatuses = 30315\nexport type UserStatuses = typeof UserStatuses\nexport const ClassifiedListing = 30402\nexport type ClassifiedListing = typeof ClassifiedListing\nexport const DraftClassifiedListing = 30403\nexport type DraftClassifiedListing = typeof DraftClassifiedListing\nexport const Date = 31922\nexport type Date = typeof Date\nexport const Time = 31923\nexport type Time = typeof Time\nexport const Calendar = 31924\nexport type Calendar = typeof Calendar\nexport const CalendarEventRSVP = 31925\nexport type CalendarEventRSVP = typeof CalendarEventRSVP\nexport const RelayReview = 31987\nexport type RelayReview = typeof RelayReview\nexport const Handlerrecommendation = 31989\nexport type Handlerrecommendation = typeof Handlerrecommendation\nexport const Handlerinformation = 31990\nexport type Handlerinformation = typeof Handlerinformation\nexport const CommunityDefinition = 34550\nexport type CommunityDefinition = typeof CommunityDefinition\nexport const GroupMetadata = 39000\nexport type GroupMetadata = typeof GroupMetadata\n", "import { Event, finalizeEvent } from './pure.ts'\nimport {\n  ChannelCreation,\n  ChannelHideMessage,\n  ChannelMessage,\n  ChannelMetadata as KindChannelMetadata,\n  ChannelMuteUser,\n} from './kinds.ts'\n\nexport interface ChannelMetadata {\n  name: string\n  about: string\n  picture: string\n}\n\nexport interface ChannelCreateEventTemplate {\n  /* JSON string containing ChannelMetadata as defined for Kind 40 and 41 in nip-28. */\n  content: string | ChannelMetadata\n  created_at: number\n  tags?: string[][]\n}\n\nexport interface ChannelMetadataEventTemplate {\n  channel_create_event_id: string\n  /* JSON string containing ChannelMetadata as defined for Kind 40 and 41 in nip-28. */\n  content: string | ChannelMetadata\n  created_at: number\n  tags?: string[][]\n}\n\nexport interface ChannelMessageEventTemplate {\n  channel_create_event_id: string\n  reply_to_channel_message_event_id?: string\n  relay_url: string\n  content: string\n  created_at: number\n  tags?: string[][]\n}\n\nexport interface ChannelHideMessageEventTemplate {\n  channel_message_event_id: string\n  content: string | { reason: string }\n  created_at: number\n  tags?: string[][]\n}\n\nexport interface ChannelMuteUserEventTemplate {\n  content: string | { reason: string }\n  created_at: number\n  pubkey_to_mute: string\n  tags?: string[][]\n}\n\nexport const channelCreateEvent = (t: ChannelCreateEventTemplate, privateKey: Uint8Array): Event | undefined => {\n  let content: string\n  if (typeof t.content === 'object') {\n    content = JSON.stringify(t.content)\n  } else if (typeof t.content === 'string') {\n    content = t.content\n  } else {\n    return undefined\n  }\n\n  return finalizeEvent(\n    {\n      kind: ChannelCreation,\n      tags: [...(t.tags ?? [])],\n      content: content,\n      created_at: t.created_at,\n    },\n    privateKey,\n  )\n}\n\nexport const channelMetadataEvent = (t: ChannelMetadataEventTemplate, privateKey: Uint8Array): Event | undefined => {\n  let content: string\n  if (typeof t.content === 'object') {\n    content = JSON.stringify(t.content)\n  } else if (typeof t.content === 'string') {\n    content = t.content\n  } else {\n    return undefined\n  }\n\n  return finalizeEvent(\n    {\n      kind: KindChannelMetadata,\n      tags: [['e', t.channel_create_event_id], ...(t.tags ?? [])],\n      content: content,\n      created_at: t.created_at,\n    },\n    privateKey,\n  )\n}\n\nexport const channelMessageEvent = (t: ChannelMessageEventTemplate, privateKey: Uint8Array): Event => {\n  const tags = [['e', t.channel_create_event_id, t.relay_url, 'root']]\n\n  if (t.reply_to_channel_message_event_id) {\n    tags.push(['e', t.reply_to_channel_message_event_id, t.relay_url, 'reply'])\n  }\n\n  return finalizeEvent(\n    {\n      kind: ChannelMessage,\n      tags: [...tags, ...(t.tags ?? [])],\n      content: t.content,\n      created_at: t.created_at,\n    },\n    privateKey,\n  )\n}\n\n/* \"e\" tag should be the kind 42 event to hide */\nexport const channelHideMessageEvent = (\n  t: ChannelHideMessageEventTemplate,\n  privateKey: Uint8Array,\n): Event | undefined => {\n  let content: string\n  if (typeof t.content === 'object') {\n    content = JSON.stringify(t.content)\n  } else if (typeof t.content === 'string') {\n    content = t.content\n  } else {\n    return undefined\n  }\n\n  return finalizeEvent(\n    {\n      kind: ChannelHideMessage,\n      tags: [['e', t.channel_message_event_id], ...(t.tags ?? [])],\n      content: content,\n      created_at: t.created_at,\n    },\n    privateKey,\n  )\n}\n\nexport const channelMuteUserEvent = (t: ChannelMuteUserEventTemplate, privateKey: Uint8Array): Event | undefined => {\n  let content: string\n  if (typeof t.content === 'object') {\n    content = JSON.stringify(t.content)\n  } else if (typeof t.content === 'string') {\n    content = t.content\n  } else {\n    return undefined\n  }\n\n  return finalizeEvent(\n    {\n      kind: ChannelMuteUser,\n      tags: [['p', t.pubkey_to_mute], ...(t.tags ?? [])],\n      content: content,\n      created_at: t.created_at,\n    },\n    privateKey,\n  )\n}\n"],
-  "mappings": ";AAAA,SAAS,eAAe;AACxB,SAAS,cAAAA,mBAAkB;;;ACOpB,IAAM,iBAAiB,OAAO,UAAU;AAsB/C,IAAM,WAAW,CAAC,QAAiD,eAAe;AAE3E,SAAS,cAAiB,OAAsC;AACrE,MAAI,CAAC,SAAS,KAAK;AAAG,WAAO;AAC7B,MAAI,OAAO,MAAM,SAAS;AAAU,WAAO;AAC3C,MAAI,OAAO,MAAM,YAAY;AAAU,WAAO;AAC9C,MAAI,OAAO,MAAM,eAAe;AAAU,WAAO;AACjD,MAAI,OAAO,MAAM,WAAW;AAAU,WAAO;AAC7C,MAAI,CAAC,MAAM,OAAO,MAAM,gBAAgB;AAAG,WAAO;AAElD,MAAI,CAAC,MAAM,QAAQ,MAAM,IAAI;AAAG,WAAO;AACvC,WAASC,KAAI,GAAGA,KAAI,MAAM,KAAK,QAAQA,MAAK;AAC1C,QAAI,MAAM,MAAM,KAAKA;AACrB,QAAI,CAAC,MAAM,QAAQ,GAAG;AAAG,aAAO;AAChC,aAAS,IAAI,GAAG,IAAI,IAAI,QAAQ,KAAK;AACnC,UAAI,OAAO,IAAI,OAAO;AAAU,eAAO;AAAA,IACzC;AAAA,EACF;AAEA,SAAO;AACT;;;AD/CA,SAAS,cAAc;;;AEEvB,SAAS,YAAY,kBAAkB;AAHhC,IAAM,cAA2B,IAAI,YAAY,OAAO;AACxD,IAAM,cAA2B,IAAI,YAAY;;;AFIxD,IAAM,KAAN,MAA0B;AAAA,EACxB,oBAAgC;AAC9B,WAAO,QAAQ,MAAM,iBAAiB;AAAA,EACxC;AAAA,EACA,aAAa,WAA+B;AAC1C,WAAOC,YAAW,QAAQ,aAAa,SAAS,CAAC;AAAA,EACnD;AAAA,EACA,cAAc,GAAkB,WAAsC;AACpE,UAAM,QAAQ;AACd,UAAM,SAASA,YAAW,QAAQ,aAAa,SAAS,CAAC;AACzD,UAAM,KAAK,aAAa,KAAK;AAC7B,UAAM,MAAMA,YAAW,QAAQ,KAAK,aAAa,KAAK,GAAG,SAAS,CAAC;AACnE,UAAM,kBAAkB;AACxB,WAAO;AAAA,EACT;AAAA,EACA,YAAY,OAAsC;AAChD,QAAI,OAAO,MAAM,oBAAoB;AAAW,aAAO,MAAM;AAE7D,UAAM,OAAO,aAAa,KAAK;AAC/B,QAAI,SAAS,MAAM,IAAI;AACrB,YAAM,kBAAkB;AACxB,aAAO;AAAA,IACT;AAEA,QAAI;AACF,YAAM,QAAQ,QAAQ,OAAO,MAAM,KAAK,MAAM,MAAM,MAAM;AAC1D,YAAM,kBAAkB;AACxB,aAAO;AAAA,IACT,SAAS,KAAP;AACA,YAAM,kBAAkB;AACxB,aAAO;AAAA,IACT;AAAA,EACF;AACF;AAEO,SAAS,eAAe,KAA4B;AACzD,MAAI,CAAC,cAAc,GAAG;AAAG,UAAM,IAAI,MAAM,wDAAwD;AACjG,SAAO,KAAK,UAAU,CAAC,GAAG,IAAI,QAAQ,IAAI,YAAY,IAAI,MAAM,IAAI,MAAM,IAAI,OAAO,CAAC;AACxF;AAEO,SAAS,aAAa,OAA8B;AACzD,MAAI,YAAY,OAAO,YAAY,OAAO,eAAe,KAAK,CAAC,CAAC;AAChE,SAAOA,YAAW,SAAS;AAC7B;AAEA,IAAM,IAAQ,IAAI,GAAG;AAEd,IAAM,oBAAoB,EAAE;AAC5B,IAAM,eAAe,EAAE;AACvB,IAAM,gBAAgB,EAAE;AACxB,IAAM,cAAc,EAAE;;;AGkBtB,IAAM,kBAAkB;AAExB,IAAM,kBAAkB;AAExB,IAAM,iBAAiB;AAEvB,IAAM,qBAAqB;AAE3B,IAAM,kBAAkB;;;AC9BxB,IAAM,qBAAqB,CAAC,GAA+B,eAA8C;AAC9G,MAAI;AACJ,MAAI,OAAO,EAAE,YAAY,UAAU;AACjC,cAAU,KAAK,UAAU,EAAE,OAAO;AAAA,EACpC,WAAW,OAAO,EAAE,YAAY,UAAU;AACxC,cAAU,EAAE;AAAA,EACd,OAAO;AACL,WAAO;AAAA,EACT;AAEA,SAAO;AAAA,IACL;AAAA,MACE,MAAM;AAAA,MACN,MAAM,CAAC,GAAI,EAAE,QAAQ,CAAC,CAAE;AAAA,MACxB;AAAA,MACA,YAAY,EAAE;AAAA,IAChB;AAAA,IACA;AAAA,EACF;AACF;AAEO,IAAM,uBAAuB,CAAC,GAAiC,eAA8C;AAClH,MAAI;AACJ,MAAI,OAAO,EAAE,YAAY,UAAU;AACjC,cAAU,KAAK,UAAU,EAAE,OAAO;AAAA,EACpC,WAAW,OAAO,EAAE,YAAY,UAAU;AACxC,cAAU,EAAE;AAAA,EACd,OAAO;AACL,WAAO;AAAA,EACT;AAEA,SAAO;AAAA,IACL;AAAA,MACE,MAAM;AAAA,MACN,MAAM,CAAC,CAAC,KAAK,EAAE,uBAAuB,GAAG,GAAI,EAAE,QAAQ,CAAC,CAAE;AAAA,MAC1D;AAAA,MACA,YAAY,EAAE;AAAA,IAChB;AAAA,IACA;AAAA,EACF;AACF;AAEO,IAAM,sBAAsB,CAAC,GAAgC,eAAkC;AACpG,QAAM,OAAO,CAAC,CAAC,KAAK,EAAE,yBAAyB,EAAE,WAAW,MAAM,CAAC;AAEnE,MAAI,EAAE,mCAAmC;AACvC,SAAK,KAAK,CAAC,KAAK,EAAE,mCAAmC,EAAE,WAAW,OAAO,CAAC;AAAA,EAC5E;AAEA,SAAO;AAAA,IACL;AAAA,MACE,MAAM;AAAA,MACN,MAAM,CAAC,GAAG,MAAM,GAAI,EAAE,QAAQ,CAAC,CAAE;AAAA,MACjC,SAAS,EAAE;AAAA,MACX,YAAY,EAAE;AAAA,IAChB;AAAA,IACA;AAAA,EACF;AACF;AAGO,IAAM,0BAA0B,CACrC,GACA,eACsB;AACtB,MAAI;AACJ,MAAI,OAAO,EAAE,YAAY,UAAU;AACjC,cAAU,KAAK,UAAU,EAAE,OAAO;AAAA,EACpC,WAAW,OAAO,EAAE,YAAY,UAAU;AACxC,cAAU,EAAE;AAAA,EACd,OAAO;AACL,WAAO;AAAA,EACT;AAEA,SAAO;AAAA,IACL;AAAA,MACE,MAAM;AAAA,MACN,MAAM,CAAC,CAAC,KAAK,EAAE,wBAAwB,GAAG,GAAI,EAAE,QAAQ,CAAC,CAAE;AAAA,MAC3D;AAAA,MACA,YAAY,EAAE;AAAA,IAChB;AAAA,IACA;AAAA,EACF;AACF;AAEO,IAAM,uBAAuB,CAAC,GAAiC,eAA8C;AAClH,MAAI;AACJ,MAAI,OAAO,EAAE,YAAY,UAAU;AACjC,cAAU,KAAK,UAAU,EAAE,OAAO;AAAA,EACpC,WAAW,OAAO,EAAE,YAAY,UAAU;AACxC,cAAU,EAAE;AAAA,EACd,OAAO;AACL,WAAO;AAAA,EACT;AAEA,SAAO;AAAA,IACL;AAAA,MACE,MAAM;AAAA,MACN,MAAM,CAAC,CAAC,KAAK,EAAE,cAAc,GAAG,GAAI,EAAE,QAAQ,CAAC,CAAE;AAAA,MACjD;AAAA,MACA,YAAY,EAAE;AAAA,IAChB;AAAA,IACA;AAAA,EACF;AACF;",
-  "names": ["bytesToHex", "i", "bytesToHex"]
+  "sourcesContent": ["import { schnorr } from '@noble/curves/secp256k1.js'\nimport { bytesToHex, hexToBytes } from '@noble/hashes/utils.js'\nimport { Nostr, Event, EventTemplate, UnsignedEvent, VerifiedEvent, verifiedSymbol, validateEvent } from './core.ts'\nimport { sha256 } from '@noble/hashes/sha2.js'\n\nimport { utf8Encoder } from './utils.ts'\n\nclass JS implements Nostr {\n  generateSecretKey(): Uint8Array {\n    return schnorr.utils.randomSecretKey()\n  }\n  getPublicKey(secretKey: Uint8Array): string {\n    return bytesToHex(schnorr.getPublicKey(secretKey))\n  }\n  finalizeEvent(t: EventTemplate, secretKey: Uint8Array): VerifiedEvent {\n    const event = t as VerifiedEvent\n    event.pubkey = bytesToHex(schnorr.getPublicKey(secretKey))\n    event.id = getEventHash(event)\n    event.sig = bytesToHex(schnorr.sign(hexToBytes(getEventHash(event)), secretKey))\n    event[verifiedSymbol] = true\n    return event\n  }\n  verifyEvent(event: Event): event is VerifiedEvent {\n    if (typeof event[verifiedSymbol] === 'boolean') return event[verifiedSymbol]\n\n    const hash = getEventHash(event)\n    if (hash !== event.id) {\n      event[verifiedSymbol] = false\n      return false\n    }\n\n    try {\n      const valid = schnorr.verify(hexToBytes(event.sig), hexToBytes(hash), hexToBytes(event.pubkey))\n      event[verifiedSymbol] = valid\n      return valid\n    } catch (err) {\n      event[verifiedSymbol] = false\n      return false\n    }\n  }\n}\n\nexport function serializeEvent(evt: UnsignedEvent): string {\n  if (!validateEvent(evt)) throw new Error(\"can't serialize event with wrong or missing properties\")\n  return JSON.stringify([0, evt.pubkey, evt.created_at, evt.kind, evt.tags, evt.content])\n}\n\nexport function getEventHash(event: UnsignedEvent): string {\n  let eventHash = sha256(utf8Encoder.encode(serializeEvent(event)))\n  return bytesToHex(eventHash)\n}\n\nconst i: JS = new JS()\n\nexport const generateSecretKey = i.generateSecretKey\nexport const getPublicKey = i.getPublicKey\nexport const finalizeEvent = i.finalizeEvent\nexport const verifyEvent = i.verifyEvent\nexport * from './core.ts'\n", "export interface Nostr {\n  generateSecretKey(): Uint8Array\n  getPublicKey(secretKey: Uint8Array): string\n  finalizeEvent(event: EventTemplate, secretKey: Uint8Array): VerifiedEvent\n  verifyEvent(event: Event): event is VerifiedEvent\n}\n\n/** Designates a verified event signature. */\nexport const verifiedSymbol = Symbol('verified')\n\nexport type NostrEvent = {\n  kind: number\n  tags: string[][]\n  content: string\n  created_at: number\n  pubkey: string\n  id: string\n  sig: string\n  [verifiedSymbol]?: boolean\n}\n\nexport type Event = NostrEvent\nexport type EventTemplate = Pick<Event, 'kind' | 'tags' | 'content' | 'created_at'>\nexport type UnsignedEvent = Pick<Event, 'kind' | 'tags' | 'content' | 'created_at' | 'pubkey'>\n\n/** An event whose signature has been verified. */\nexport interface VerifiedEvent extends Event {\n  [verifiedSymbol]: true\n}\n\nconst isRecord = (obj: unknown): obj is Record<string, unknown> => obj instanceof Object\n\nexport function validateEvent<T>(event: T): event is T & UnsignedEvent {\n  if (!isRecord(event)) return false\n  if (typeof event.kind !== 'number') return false\n  if (typeof event.content !== 'string') return false\n  if (typeof event.created_at !== 'number') return false\n  if (typeof event.pubkey !== 'string') return false\n  if (!event.pubkey.match(/^[a-f0-9]{64}$/)) return false\n\n  if (!Array.isArray(event.tags)) return false\n  for (let i = 0; i < event.tags.length; i++) {\n    let tag = event.tags[i]\n    if (!Array.isArray(tag)) return false\n    for (let j = 0; j < tag.length; j++) {\n      if (typeof tag[j] !== 'string') return false\n    }\n  }\n\n  return true\n}\n\n/**\n * Sort events in reverse-chronological order by the `created_at` timestamp,\n * and then by the event `id` (lexicographically) in case of ties.\n * This mutates the array.\n */\nexport function sortEvents(events: Event[]): Event[] {\n  return events.sort((a: NostrEvent, b: NostrEvent): number => {\n    if (a.created_at !== b.created_at) {\n      return b.created_at - a.created_at\n    }\n    return a.id.localeCompare(b.id)\n  })\n}\n", "import type { Event } from './core.ts'\n\nexport const utf8Decoder: TextDecoder = new TextDecoder('utf-8')\nexport const utf8Encoder: TextEncoder = new TextEncoder()\n\nexport { bytesToHex, hexToBytes } from '@noble/hashes/utils.js'\n\nexport function normalizeURL(url: string): string {\n  try {\n    if (url.indexOf('://') === -1) url = 'wss://' + url\n    let p = new URL(url)\n    if (p.protocol === 'http:') p.protocol = 'ws:'\n    else if (p.protocol === 'https:') p.protocol = 'wss:'\n    p.pathname = p.pathname.replace(/\\/+/g, '/')\n    if (p.pathname.endsWith('/')) p.pathname = p.pathname.slice(0, -1)\n    if ((p.port === '80' && p.protocol === 'ws:') || (p.port === '443' && p.protocol === 'wss:')) p.port = ''\n    p.searchParams.sort()\n    p.hash = ''\n    return p.toString()\n  } catch (e) {\n    throw new Error(`Invalid URL: ${url}`)\n  }\n}\n\nexport function insertEventIntoDescendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return b.created_at - event.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function insertEventIntoAscendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return event.created_at - b.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function binarySearch<T>(arr: T[], compare: (b: T) => number): [number, boolean] {\n  let start = 0\n  let end = arr.length - 1\n\n  while (start <= end) {\n    const mid = Math.floor((start + end) / 2)\n    const cmp = compare(arr[mid])\n\n    if (cmp === 0) {\n      return [mid, true]\n    }\n\n    if (cmp < 0) {\n      end = mid - 1\n    } else {\n      start = mid + 1\n    }\n  }\n\n  return [start, false]\n}\n\nexport class QueueNode<V> {\n  public value: V\n  public next: QueueNode<V> | null = null\n  public prev: QueueNode<V> | null = null\n\n  constructor(message: V) {\n    this.value = message\n  }\n}\n\nexport class Queue<V> {\n  public first: QueueNode<V> | null\n  public last: QueueNode<V> | null\n\n  constructor() {\n    this.first = null\n    this.last = null\n  }\n\n  enqueue(value: V): boolean {\n    const newNode = new QueueNode(value)\n    if (!this.last) {\n      // list is empty\n      this.first = newNode\n      this.last = newNode\n    } else if (this.last === this.first) {\n      // list has a single element\n      this.last = newNode\n      this.last.prev = this.first\n      this.first.next = newNode\n    } else {\n      // list has elements, add as last\n      newNode.prev = this.last\n      this.last.next = newNode\n      this.last = newNode\n    }\n    return true\n  }\n\n  dequeue(): V | null {\n    if (!this.first) return null\n\n    if (this.first === this.last) {\n      const target = this.first\n      this.first = null\n      this.last = null\n      return target.value\n    }\n\n    const target = this.first\n    this.first = target.next\n    if (this.first) {\n      this.first.prev = null // fix: clean up prev pointer\n    }\n\n    return target.value\n  }\n}\n", "import { NostrEvent, validateEvent } from './pure.ts'\n\n/** Events are **regular**, which means they're all expected to be stored by relays. */\nexport function isRegularKind(kind: number): boolean {\n  return kind < 10000 && kind !== 0 && kind !== 3\n}\n\n/** Events are **replaceable**, which means that, for each combination of `pubkey` and `kind`, only the latest event is expected to (SHOULD) be stored by relays, older versions are expected to be discarded. */\nexport function isReplaceableKind(kind: number): boolean {\n  return kind === 0 || kind === 3 || (10000 <= kind && kind < 20000)\n}\n\n/** Events are **ephemeral**, which means they are not expected to be stored by relays. */\nexport function isEphemeralKind(kind: number): boolean {\n  return 20000 <= kind && kind < 30000\n}\n\n/** Events are **addressable**, which means that, for each combination of `pubkey`, `kind` and the `d` tag, only the latest event is expected to be stored by relays, older versions are expected to be discarded. */\nexport function isAddressableKind(kind: number): boolean {\n  return 30000 <= kind && kind < 40000\n}\n\n/** Classification of the event kind. */\nexport type KindClassification = 'regular' | 'replaceable' | 'ephemeral' | 'parameterized' | 'unknown'\n\n/** Determine the classification of this kind of event if known, or `unknown`. */\nexport function classifyKind(kind: number): KindClassification {\n  if (isRegularKind(kind)) return 'regular'\n  if (isReplaceableKind(kind)) return 'replaceable'\n  if (isEphemeralKind(kind)) return 'ephemeral'\n  if (isAddressableKind(kind)) return 'parameterized'\n  return 'unknown'\n}\n\nexport function isKind<T extends number>(event: unknown, kind: T | Array<T>): event is NostrEvent & { kind: T } {\n  const kindAsArray: number[] = kind instanceof Array ? kind : [kind]\n  return (validateEvent(event) && kindAsArray.includes(event.kind)) || false\n}\n\nexport const Metadata = 0\nexport type Metadata = typeof Metadata\nexport const ShortTextNote = 1\nexport type ShortTextNote = typeof ShortTextNote\nexport const RecommendRelay = 2\nexport type RecommendRelay = typeof RecommendRelay\nexport const Contacts = 3\nexport type Contacts = typeof Contacts\nexport const EncryptedDirectMessage = 4\nexport type EncryptedDirectMessage = typeof EncryptedDirectMessage\nexport const EventDeletion = 5\nexport type EventDeletion = typeof EventDeletion\nexport const Repost = 6\nexport type Repost = typeof Repost\nexport const Reaction = 7\nexport type Reaction = typeof Reaction\nexport const BadgeAward = 8\nexport type BadgeAward = typeof BadgeAward\nexport const ChatMessage = 9\nexport type ChatMessage = typeof ChatMessage\nexport const ForumThread = 11\nexport type ForumThread = typeof ForumThread\nexport const Seal = 13\nexport type Seal = typeof Seal\nexport const PrivateDirectMessage = 14\nexport type PrivateDirectMessage = typeof PrivateDirectMessage\nexport const FileMessage = 15\nexport type FileMessage = typeof FileMessage\nexport const GenericRepost = 16\nexport type GenericRepost = typeof GenericRepost\nexport const Photo = 20\nexport type Photo = typeof Photo\nexport const NormalVideo = 21\nexport type NormalVideo = typeof NormalVideo\nexport const ShortVideo = 22\nexport type ShortVideo = typeof ShortVideo\nexport const ChannelCreation = 40\nexport type ChannelCreation = typeof ChannelCreation\nexport const ChannelMetadata = 41\nexport type ChannelMetadata = typeof ChannelMetadata\nexport const ChannelMessage = 42\nexport type ChannelMessage = typeof ChannelMessage\nexport const ChannelHideMessage = 43\nexport type ChannelHideMessage = typeof ChannelHideMessage\nexport const ChannelMuteUser = 44\nexport type ChannelMuteUser = typeof ChannelMuteUser\nexport const OpenTimestamps = 1040\nexport type OpenTimestamps = typeof OpenTimestamps\nexport const GiftWrap = 1059\nexport type GiftWrap = typeof GiftWrap\nexport const Poll = 1068\nexport type Poll = typeof Poll\nexport const FileMetadata = 1063\nexport type FileMetadata = typeof FileMetadata\nexport const Comment = 1111\nexport type Comment = typeof Comment\nexport const LiveChatMessage = 1311\nexport type LiveChatMessage = typeof LiveChatMessage\nexport const Voice = 1222\nexport type Voice = typeof Voice\nexport const VoiceComment = 1244\nexport type VoiceComment = typeof VoiceComment\nexport const ProblemTracker = 1971\nexport type ProblemTracker = typeof ProblemTracker\nexport const Report = 1984\nexport type Report = typeof Report\nexport const Reporting = 1984\nexport type Reporting = typeof Reporting\nexport const Label = 1985\nexport type Label = typeof Label\nexport const CommunityPostApproval = 4550\nexport type CommunityPostApproval = typeof CommunityPostApproval\nexport const JobRequest = 5999\nexport type JobRequest = typeof JobRequest\nexport const JobResult = 6999\nexport type JobResult = typeof JobResult\nexport const JobFeedback = 7000\nexport type JobFeedback = typeof JobFeedback\nexport const ZapGoal = 9041\nexport type ZapGoal = typeof ZapGoal\nexport const ZapRequest = 9734\nexport type ZapRequest = typeof ZapRequest\nexport const Zap = 9735\nexport type Zap = typeof Zap\nexport const Highlights = 9802\nexport type Highlights = typeof Highlights\nexport const PollResponse = 1018\nexport type PollResponse = typeof PollResponse\nexport const Mutelist = 10000\nexport type Mutelist = typeof Mutelist\nexport const Pinlist = 10001\nexport type Pinlist = typeof Pinlist\nexport const RelayList = 10002\nexport type RelayList = typeof RelayList\nexport const BookmarkList = 10003\nexport type BookmarkList = typeof BookmarkList\nexport const CommunitiesList = 10004\nexport type CommunitiesList = typeof CommunitiesList\nexport const PublicChatsList = 10005\nexport type PublicChatsList = typeof PublicChatsList\nexport const BlockedRelaysList = 10006\nexport type BlockedRelaysList = typeof BlockedRelaysList\nexport const SearchRelaysList = 10007\nexport type SearchRelaysList = typeof SearchRelaysList\nexport const FavoriteRelays = 10012\nexport type FavoriteRelays = typeof FavoriteRelays\nexport const InterestsList = 10015\nexport type InterestsList = typeof InterestsList\nexport const UserEmojiList = 10030\nexport type UserEmojiList = typeof UserEmojiList\nexport const DirectMessageRelaysList = 10050\nexport type DirectMessageRelaysList = typeof DirectMessageRelaysList\nexport const FileServerPreference = 10096\nexport type FileServerPreference = typeof FileServerPreference\nexport const BlossomServerList = 10063\nexport type BlossomServerList = typeof BlossomServerList\nexport const NWCWalletInfo = 13194\nexport type NWCWalletInfo = typeof NWCWalletInfo\nexport const LightningPubRPC = 21000\nexport type LightningPubRPC = typeof LightningPubRPC\nexport const ClientAuth = 22242\nexport type ClientAuth = typeof ClientAuth\nexport const NWCWalletRequest = 23194\nexport type NWCWalletRequest = typeof NWCWalletRequest\nexport const NWCWalletResponse = 23195\nexport type NWCWalletResponse = typeof NWCWalletResponse\nexport const NostrConnect = 24133\nexport type NostrConnect = typeof NostrConnect\nexport const HTTPAuth = 27235\nexport type HTTPAuth = typeof HTTPAuth\nexport const Followsets = 30000\nexport type Followsets = typeof Followsets\nexport const Genericlists = 30001\nexport type Genericlists = typeof Genericlists\nexport const Relaysets = 30002\nexport type Relaysets = typeof Relaysets\nexport const Bookmarksets = 30003\nexport type Bookmarksets = typeof Bookmarksets\nexport const Curationsets = 30004\nexport type Curationsets = typeof Curationsets\nexport const ProfileBadges = 30008\nexport type ProfileBadges = typeof ProfileBadges\nexport const BadgeDefinition = 30009\nexport type BadgeDefinition = typeof BadgeDefinition\nexport const Interestsets = 30015\nexport type Interestsets = typeof Interestsets\nexport const CreateOrUpdateStall = 30017\nexport type CreateOrUpdateStall = typeof CreateOrUpdateStall\nexport const CreateOrUpdateProduct = 30018\nexport type CreateOrUpdateProduct = typeof CreateOrUpdateProduct\nexport const LongFormArticle = 30023\nexport type LongFormArticle = typeof LongFormArticle\nexport const DraftLong = 30024\nexport type DraftLong = typeof DraftLong\nexport const Emojisets = 30030\nexport type Emojisets = typeof Emojisets\nexport const Application = 30078\nexport type Application = typeof Application\nexport const LiveEvent = 30311\nexport type LiveEvent = typeof LiveEvent\nexport const UserStatuses = 30315\nexport type UserStatuses = typeof UserStatuses\nexport const ClassifiedListing = 30402\nexport type ClassifiedListing = typeof ClassifiedListing\nexport const DraftClassifiedListing = 30403\nexport type DraftClassifiedListing = typeof DraftClassifiedListing\nexport const Date = 31922\nexport type Date = typeof Date\nexport const Time = 31923\nexport type Time = typeof Time\nexport const Calendar = 31924\nexport type Calendar = typeof Calendar\nexport const CalendarEventRSVP = 31925\nexport type CalendarEventRSVP = typeof CalendarEventRSVP\nexport const RelayReview = 31987\nexport type RelayReview = typeof RelayReview\nexport const Handlerrecommendation = 31989\nexport type Handlerrecommendation = typeof Handlerrecommendation\nexport const Handlerinformation = 31990\nexport type Handlerinformation = typeof Handlerinformation\nexport const CommunityDefinition = 34550\nexport type CommunityDefinition = typeof CommunityDefinition\nexport const GroupMetadata = 39000\nexport type GroupMetadata = typeof GroupMetadata\n", "import { Event, finalizeEvent } from './pure.ts'\nimport {\n  ChannelCreation,\n  ChannelHideMessage,\n  ChannelMessage,\n  ChannelMetadata as KindChannelMetadata,\n  ChannelMuteUser,\n} from './kinds.ts'\n\nexport interface ChannelMetadata {\n  name: string\n  about: string\n  picture: string\n}\n\nexport interface ChannelCreateEventTemplate {\n  /* JSON string containing ChannelMetadata as defined for Kind 40 and 41 in nip-28. */\n  content: string | ChannelMetadata\n  created_at: number\n  tags?: string[][]\n}\n\nexport interface ChannelMetadataEventTemplate {\n  channel_create_event_id: string\n  /* JSON string containing ChannelMetadata as defined for Kind 40 and 41 in nip-28. */\n  content: string | ChannelMetadata\n  created_at: number\n  tags?: string[][]\n}\n\nexport interface ChannelMessageEventTemplate {\n  channel_create_event_id: string\n  reply_to_channel_message_event_id?: string\n  relay_url: string\n  content: string\n  created_at: number\n  tags?: string[][]\n}\n\nexport interface ChannelHideMessageEventTemplate {\n  channel_message_event_id: string\n  content: string | { reason: string }\n  created_at: number\n  tags?: string[][]\n}\n\nexport interface ChannelMuteUserEventTemplate {\n  content: string | { reason: string }\n  created_at: number\n  pubkey_to_mute: string\n  tags?: string[][]\n}\n\nexport const channelCreateEvent = (t: ChannelCreateEventTemplate, privateKey: Uint8Array): Event | undefined => {\n  let content: string\n  if (typeof t.content === 'object') {\n    content = JSON.stringify(t.content)\n  } else if (typeof t.content === 'string') {\n    content = t.content\n  } else {\n    return undefined\n  }\n\n  return finalizeEvent(\n    {\n      kind: ChannelCreation,\n      tags: [...(t.tags ?? [])],\n      content: content,\n      created_at: t.created_at,\n    },\n    privateKey,\n  )\n}\n\nexport const channelMetadataEvent = (t: ChannelMetadataEventTemplate, privateKey: Uint8Array): Event | undefined => {\n  let content: string\n  if (typeof t.content === 'object') {\n    content = JSON.stringify(t.content)\n  } else if (typeof t.content === 'string') {\n    content = t.content\n  } else {\n    return undefined\n  }\n\n  return finalizeEvent(\n    {\n      kind: KindChannelMetadata,\n      tags: [['e', t.channel_create_event_id], ...(t.tags ?? [])],\n      content: content,\n      created_at: t.created_at,\n    },\n    privateKey,\n  )\n}\n\nexport const channelMessageEvent = (t: ChannelMessageEventTemplate, privateKey: Uint8Array): Event => {\n  const tags = [['e', t.channel_create_event_id, t.relay_url, 'root']]\n\n  if (t.reply_to_channel_message_event_id) {\n    tags.push(['e', t.reply_to_channel_message_event_id, t.relay_url, 'reply'])\n  }\n\n  return finalizeEvent(\n    {\n      kind: ChannelMessage,\n      tags: [...tags, ...(t.tags ?? [])],\n      content: t.content,\n      created_at: t.created_at,\n    },\n    privateKey,\n  )\n}\n\n/* \"e\" tag should be the kind 42 event to hide */\nexport const channelHideMessageEvent = (\n  t: ChannelHideMessageEventTemplate,\n  privateKey: Uint8Array,\n): Event | undefined => {\n  let content: string\n  if (typeof t.content === 'object') {\n    content = JSON.stringify(t.content)\n  } else if (typeof t.content === 'string') {\n    content = t.content\n  } else {\n    return undefined\n  }\n\n  return finalizeEvent(\n    {\n      kind: ChannelHideMessage,\n      tags: [['e', t.channel_message_event_id], ...(t.tags ?? [])],\n      content: content,\n      created_at: t.created_at,\n    },\n    privateKey,\n  )\n}\n\nexport const channelMuteUserEvent = (t: ChannelMuteUserEventTemplate, privateKey: Uint8Array): Event | undefined => {\n  let content: string\n  if (typeof t.content === 'object') {\n    content = JSON.stringify(t.content)\n  } else if (typeof t.content === 'string') {\n    content = t.content\n  } else {\n    return undefined\n  }\n\n  return finalizeEvent(\n    {\n      kind: ChannelMuteUser,\n      tags: [['p', t.pubkey_to_mute], ...(t.tags ?? [])],\n      content: content,\n      created_at: t.created_at,\n    },\n    privateKey,\n  )\n}\n"],
+  "mappings": "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",
+  "names": ["bytesToHex", "hexToBytes", "i", "bytesToHex", "hexToBytes"]
 }
Index: package/lib/cjs/nip29.js.map
===================================================================
--- package/lib/cjs/nip29.js.map
+++ package/lib/cjs/nip29.js.map
@@ -1,7 +1,7 @@
 {
   "version": 3,
   "sources": ["../../nip29.ts", "../../nip11.ts", "../../nip19.ts", "../../utils.ts"],
-  "sourcesContent": ["import { AbstractSimplePool } from './abstract-pool.ts'\nimport { Subscription } from './abstract-relay.ts'\nimport type { Event, EventTemplate } from './core.ts'\nimport { fetchRelayInformation, RelayInformation } from './nip11.ts'\nimport { decode, NostrTypeGuard } from './nip19.ts'\nimport { normalizeURL } from './utils.ts'\n\n/**\n * Represents a NIP29 group.\n */\nexport type Group = {\n  relay: string\n  metadata: GroupMetadata\n  admins?: GroupAdmin[]\n  members?: GroupMember[]\n  reference: GroupReference\n}\n\n/**\n * Represents the metadata for a NIP29 group.\n */\nexport type GroupMetadata = {\n  id: string\n  pubkey: string\n  name?: string\n  picture?: string\n  about?: string\n  isPublic?: boolean\n  isOpen?: boolean\n}\n\n/**\n * Represents a NIP29 group reference.\n */\nexport type GroupReference = {\n  id: string\n  host: string\n}\n\n/**\n * Represents a NIP29 group member.\n */\nexport type GroupMember = {\n  pubkey: string\n  label?: string\n}\n\n/**\n * Represents a NIP29 group admin.\n */\nexport type GroupAdmin = {\n  pubkey: string\n  label?: string\n  permissions: GroupAdminPermission[]\n}\n\n/**\n * Represents the permissions that a NIP29 group admin can have.\n */\nexport enum GroupAdminPermission {\n  /** @deprecated use PutUser instead */\n  AddUser = 'add-user',\n  EditMetadata = 'edit-metadata',\n  DeleteEvent = 'delete-event',\n  RemoveUser = 'remove-user',\n  /** @deprecated removed from NIP */\n  AddPermission = 'add-permission',\n  /** @deprecated removed from NIP */\n  RemovePermission = 'remove-permission',\n  /** @deprecated removed from NIP */\n  EditGroupStatus = 'edit-group-status',\n  PutUser = 'put-user',\n  CreateGroup = 'create-group',\n  DeleteGroup = 'delete-group',\n  CreateInvite = 'create-invite',\n}\n\n/**\n * Generates a group metadata event template.\n *\n * @param group - The group object.\n * @returns An event template with the generated group metadata that can be signed later.\n */\nexport function generateGroupMetadataEventTemplate(group: Group): EventTemplate {\n  const tags: string[][] = [['d', group.metadata.id]]\n  group.metadata.name && tags.push(['name', group.metadata.name])\n  group.metadata.picture && tags.push(['picture', group.metadata.picture])\n  group.metadata.about && tags.push(['about', group.metadata.about])\n  group.metadata.isPublic && tags.push(['public'])\n  group.metadata.isOpen && tags.push(['open'])\n\n  return {\n    content: '',\n    created_at: Math.floor(Date.now() / 1000),\n    kind: 39000,\n    tags,\n  }\n}\n\n/**\n * Validates a group metadata event.\n *\n * @param event - The event to validate.\n * @returns A boolean indicating whether the event is valid.\n */\nexport function validateGroupMetadataEvent(event: Event): boolean {\n  if (event.kind !== 39000) return false\n\n  if (!event.pubkey) return false\n\n  const requiredTags = ['d'] as const\n  for (const tag of requiredTags) {\n    if (!event.tags.find(([t]) => t == tag)) return false\n  }\n\n  return true\n}\n\n/**\n * Generates an event template for group admins.\n *\n * @param group - The group object.\n * @param admins - An array of group admins.\n * @returns The generated event template with the group admins that can be signed later.\n */\nexport function generateGroupAdminsEventTemplate(group: Group, admins: GroupAdmin[]): EventTemplate {\n  const tags: string[][] = [['d', group.metadata.id]]\n  for (const admin of admins) {\n    tags.push(['p', admin.pubkey, admin.label || '', ...admin.permissions])\n  }\n\n  return {\n    content: '',\n    created_at: Math.floor(Date.now() / 1000),\n    kind: 39001,\n    tags,\n  }\n}\n\n/**\n * Validates a group admins event.\n *\n * @param event - The event to validate.\n * @returns True if the event is valid, false otherwise.\n */\nexport function validateGroupAdminsEvent(event: Event): boolean {\n  if (event.kind !== 39001) return false\n\n  const requiredTags = ['d'] as const\n  for (const tag of requiredTags) {\n    if (!event.tags.find(([t]) => t == tag)) return false\n  }\n\n  // validate permissions\n  for (const [tag, _value, _label, ...permissions] of event.tags) {\n    if (tag !== 'p') continue\n\n    for (let i = 0; i < permissions.length; i += 1) {\n      if (typeof permissions[i] !== 'string') return false\n\n      // validate permission name from the GroupAdminPermission enum\n      if (!Object.values(GroupAdminPermission).includes(permissions[i] as GroupAdminPermission)) return false\n    }\n  }\n\n  return true\n}\n\n/**\n * Generates an event template for a group with its members.\n *\n * @param group - The group object.\n * @param members - An array of group members.\n * @returns The generated event template with the group members that can be signed later.\n */\nexport function generateGroupMembersEventTemplate(group: Group, members: GroupMember[]): EventTemplate {\n  const tags: string[][] = [['d', group.metadata.id]]\n  for (const member of members) {\n    tags.push(['p', member.pubkey, member.label || ''])\n  }\n\n  return {\n    content: '',\n    created_at: Math.floor(Date.now() / 1000),\n    kind: 39002,\n    tags,\n  }\n}\n\n/**\n * Validates a group members event.\n *\n * @param event - The event to validate.\n * @returns Returns `true` if the event is a valid group members event, `false` otherwise.\n */\nexport function validateGroupMembersEvent(event: Event): boolean {\n  if (event.kind !== 39002) return false\n\n  const requiredTags = ['d'] as const\n  for (const tag of requiredTags) {\n    if (!event.tags.find(([t]) => t == tag)) return false\n  }\n\n  return true\n}\n\n/**\n * Returns the normalized relay URL based on the provided group reference.\n *\n * @param groupReference - The group reference object containing the host.\n * @returns The normalized relay URL.\n */\nexport function getNormalizedRelayURLByGroupReference(groupReference: GroupReference): string {\n  return normalizeURL(groupReference.host)\n}\n\n/**\n * Fetches relay information by group reference.\n *\n * @param groupReference The group reference.\n * @returns A promise that resolves to the relay information.\n */\nexport async function fetchRelayInformationByGroupReference(groupReference: GroupReference): Promise<RelayInformation> {\n  const normalizedRelayURL = getNormalizedRelayURLByGroupReference(groupReference)\n\n  return fetchRelayInformation(normalizedRelayURL)\n}\n\n/**\n * Fetches the group metadata event from the specified pool.\n * If the normalizedRelayURL is not provided, it will be obtained using the groupReference.\n * If the relayInformation is not provided, it will be fetched using the normalizedRelayURL.\n *\n * @param {Object} options - The options object.\n * @param {AbstractSimplePool} options.pool - The pool to fetch the group metadata event from.\n * @param {GroupReference} options.groupReference - The reference to the group.\n * @param {string} [options.normalizedRelayURL] - The normalized URL of the relay.\n * @param {RelayInformation} [options.relayInformation] - The relay information object.\n * @returns {Promise<Event>} The group metadata event that can be parsed later to get the group metadata object.\n * @throws {Error} If the group is not found on the specified relay.\n */\nexport async function fetchGroupMetadataEvent({\n  pool,\n  groupReference,\n  relayInformation,\n  normalizedRelayURL,\n}: {\n  pool: AbstractSimplePool\n  groupReference: GroupReference\n  normalizedRelayURL?: string\n  relayInformation?: RelayInformation\n}): Promise<Event> {\n  if (!normalizedRelayURL) {\n    normalizedRelayURL = getNormalizedRelayURLByGroupReference(groupReference)\n  }\n\n  if (!relayInformation) {\n    relayInformation = await fetchRelayInformation(normalizedRelayURL)\n  }\n\n  const groupMetadataEvent = await pool.get([normalizedRelayURL], {\n    kinds: [39000],\n    authors: [relayInformation.pubkey],\n    '#d': [groupReference.id],\n  })\n\n  if (!groupMetadataEvent) throw new Error(`group '${groupReference.id}' not found on ${normalizedRelayURL}`)\n\n  return groupMetadataEvent\n}\n\n/**\n * Parses a group metadata event and returns the corresponding GroupMetadata object.\n *\n * @param event - The event to parse.\n * @returns The parsed GroupMetadata object.\n * @throws An error if the group metadata event is invalid.\n */\nexport function parseGroupMetadataEvent(event: Event): GroupMetadata {\n  if (!validateGroupMetadataEvent(event)) throw new Error('invalid group metadata event')\n\n  const metadata: GroupMetadata = {\n    id: '',\n    pubkey: event.pubkey,\n  }\n\n  for (const [tag, value] of event.tags) {\n    switch (tag) {\n      case 'd':\n        metadata.id = value\n        break\n      case 'name':\n        metadata.name = value\n        break\n      case 'picture':\n        metadata.picture = value\n        break\n      case 'about':\n        metadata.about = value\n        break\n      case 'public':\n        metadata.isPublic = true\n        break\n      case 'open':\n        metadata.isOpen = true\n        break\n    }\n  }\n\n  return metadata\n}\n\n/**\n * Fetches the group admins event from the specified pool.\n * If the normalizedRelayURL is not provided, it will be obtained from the groupReference.\n * If the relayInformation is not provided, it will be fetched using the normalizedRelayURL.\n *\n * @param {Object} options - The options object.\n * @param {AbstractSimplePool} options.pool - The pool to fetch the group admins event from.\n * @param {GroupReference} options.groupReference - The reference to the group.\n * @param {string} [options.normalizedRelayURL] - The normalized relay URL.\n * @param {RelayInformation} [options.relayInformation] - The relay information.\n * @returns {Promise<Event>} The group admins event that can be parsed later to get the group admins object.\n * @throws {Error} If the group admins event is not found on the specified relay.\n */\nexport async function fetchGroupAdminsEvent({\n  pool,\n  groupReference,\n  relayInformation,\n  normalizedRelayURL,\n}: {\n  pool: AbstractSimplePool\n  groupReference: GroupReference\n  normalizedRelayURL?: string\n  relayInformation?: RelayInformation\n}): Promise<Event> {\n  if (!normalizedRelayURL) {\n    normalizedRelayURL = getNormalizedRelayURLByGroupReference(groupReference)\n  }\n\n  if (!relayInformation) {\n    relayInformation = await fetchRelayInformation(normalizedRelayURL)\n  }\n\n  const groupAdminsEvent = await pool.get([normalizedRelayURL], {\n    kinds: [39001],\n    authors: [relayInformation.pubkey],\n    '#d': [groupReference.id],\n  })\n\n  if (!groupAdminsEvent) throw new Error(`admins for group '${groupReference.id}' not found on ${normalizedRelayURL}`)\n\n  return groupAdminsEvent\n}\n\n/**\n * Parses a group admins event and returns an array of GroupAdmin objects.\n *\n * @param event - The event to parse.\n * @returns An array of GroupAdmin objects.\n * @throws Throws an error if the group admins event is invalid.\n */\nexport function parseGroupAdminsEvent(event: Event): GroupAdmin[] {\n  if (!validateGroupAdminsEvent(event)) throw new Error('invalid group admins event')\n\n  const admins: GroupAdmin[] = []\n\n  for (const [tag, value, label, ...permissions] of event.tags) {\n    if (tag !== 'p') continue\n\n    admins.push({\n      pubkey: value,\n      label,\n      permissions: permissions as GroupAdminPermission[],\n    })\n  }\n\n  return admins\n}\n\n/**\n * Fetches the group members event from the specified relay.\n * If the normalizedRelayURL is not provided, it will be obtained using the groupReference.\n * If the relayInformation is not provided, it will be fetched using the normalizedRelayURL.\n *\n * @param {Object} options - The options object.\n * @param {AbstractSimplePool} options.pool - The pool object.\n * @param {GroupReference} options.groupReference - The group reference object.\n * @param {string} [options.normalizedRelayURL] - The normalized relay URL.\n * @param {RelayInformation} [options.relayInformation] - The relay information object.\n * @returns {Promise<Event>} The group members event that can be parsed later to get the group members object.\n * @throws {Error} If the group members event is not found.\n */\nexport async function fetchGroupMembersEvent({\n  pool,\n  groupReference,\n  relayInformation,\n  normalizedRelayURL,\n}: {\n  pool: AbstractSimplePool\n  groupReference: GroupReference\n  normalizedRelayURL?: string\n  relayInformation?: RelayInformation\n}): Promise<Event> {\n  if (!normalizedRelayURL) {\n    normalizedRelayURL = getNormalizedRelayURLByGroupReference(groupReference)\n  }\n\n  if (!relayInformation) {\n    relayInformation = await fetchRelayInformation(normalizedRelayURL)\n  }\n\n  const groupMembersEvent = await pool.get([normalizedRelayURL], {\n    kinds: [39002],\n    authors: [relayInformation.pubkey],\n    '#d': [groupReference.id],\n  })\n\n  if (!groupMembersEvent) throw new Error(`members for group '${groupReference.id}' not found on ${normalizedRelayURL}`)\n\n  return groupMembersEvent\n}\n\n/**\n * Parses a group members event and returns an array of GroupMember objects.\n * @param event - The event to parse.\n * @returns An array of GroupMember objects.\n * @throws Throws an error if the group members event is invalid.\n */\nexport function parseGroupMembersEvent(event: Event): GroupMember[] {\n  if (!validateGroupMembersEvent(event)) throw new Error('invalid group members event')\n\n  const members: GroupMember[] = []\n\n  for (const [tag, value, label] of event.tags) {\n    if (tag !== 'p') continue\n\n    members.push({\n      pubkey: value,\n      label,\n    })\n  }\n\n  return members\n}\n\n/**\n * Fetches and parses the group metadata event, group admins event, and group members event from the specified pool.\n * If the normalized relay URL is not provided, it will be obtained using the group reference.\n * If the relay information is not provided, it will be fetched using the normalized relay URL.\n *\n * @param {Object} options - The options for loading the group.\n * @param {AbstractSimplePool} options.pool - The pool to load the group from.\n * @param {GroupReference} options.groupReference - The reference of the group to load.\n * @param {string} [options.normalizedRelayURL] - The normalized URL of the relay to use.\n * @param {RelayInformation} [options.relayInformation] - The relay information to use.\n * @returns {Promise<Group>} A promise that resolves to the loaded group.\n */\nexport async function loadGroup({\n  pool,\n  groupReference,\n  normalizedRelayURL,\n  relayInformation,\n}: {\n  pool: AbstractSimplePool\n  groupReference: GroupReference\n  normalizedRelayURL?: string\n  relayInformation?: RelayInformation\n}): Promise<Group> {\n  if (!normalizedRelayURL) {\n    normalizedRelayURL = getNormalizedRelayURLByGroupReference(groupReference)\n  }\n\n  if (!relayInformation) {\n    relayInformation = await fetchRelayInformation(normalizedRelayURL)\n  }\n\n  const metadataEvent = await fetchGroupMetadataEvent({ pool, groupReference, normalizedRelayURL, relayInformation })\n  const metadata = parseGroupMetadataEvent(metadataEvent)\n\n  const adminsEvent = await fetchGroupAdminsEvent({ pool, groupReference, normalizedRelayURL, relayInformation })\n  const admins = parseGroupAdminsEvent(adminsEvent)\n\n  const membersEvent = await fetchGroupMembersEvent({ pool, groupReference, normalizedRelayURL, relayInformation })\n  const members = parseGroupMembersEvent(membersEvent)\n\n  const group: Group = {\n    relay: normalizedRelayURL,\n    metadata,\n    admins,\n    members,\n    reference: groupReference,\n  }\n\n  return group\n}\n\n/**\n * Loads a group from the specified pool using the provided group code.\n *\n * @param {AbstractSimplePool} pool - The pool to load the group from.\n * @param {string} code - The code representing the group.\n * @returns {Promise<Group>} - A promise that resolves to the loaded group.\n * @throws {Error} - If the group code is invalid.\n */\nexport async function loadGroupFromCode(pool: AbstractSimplePool, code: string): Promise<Group> {\n  const groupReference = parseGroupCode(code)\n\n  if (!groupReference) throw new Error('invalid group code')\n\n  return loadGroup({ pool, groupReference })\n}\n\n/**\n * Parses a group code and returns a GroupReference object.\n *\n * @param code The group code to parse.\n * @returns A GroupReference object if the code is valid, otherwise null.\n */\nexport function parseGroupCode(code: string): null | GroupReference {\n  if (NostrTypeGuard.isNAddr(code)) {\n    try {\n      let { data } = decode(code)\n\n      let { relays, identifier } = data\n      if (!relays || relays.length === 0) return null\n\n      let host = relays![0]\n      if (host.startsWith('wss://')) {\n        host = host.slice(6)\n      }\n      return { host, id: identifier }\n    } catch (err) {\n      return null\n    }\n  } else if (code.split(\"'\").length === 2) {\n    let spl = code.split(\"'\")\n    return { host: spl[0], id: spl[1] }\n  }\n\n  return null\n}\n\n/**\n * Encodes a group reference into a string.\n *\n * @param gr - The group reference to encode.\n * @returns The encoded group reference as a string.\n */\nexport function encodeGroupReference(gr: GroupReference): string {\n  const { host, id } = gr\n  const normalizedHost = host.replace(/^(https?:\\/\\/|wss?:\\/\\/)/, '')\n\n  return `${normalizedHost}'${id}`\n}\n\n/**\n * Subscribes to relay groups metadata events and calls the provided event handler function\n * when an event is received.\n *\n * @param {Object} options - The options for subscribing to relay groups metadata events.\n * @param {AbstractSimplePool} options.pool - The pool to subscribe to.\n * @param {string} options.relayURL - The URL of the relay.\n * @param {Function} options.onError - The error handler function.\n * @param {Function} options.onEvent - The event handler function.\n * @param {Function} [options.onConnect] - The connect handler function.\n * @returns {Function} - A function to close the subscription\n */\nexport function subscribeRelayGroupsMetadataEvents({\n  pool,\n  relayURL,\n  onError,\n  onEvent,\n  onConnect,\n}: {\n  pool: AbstractSimplePool\n  relayURL: string\n  onError: (err: Error) => void\n  onEvent: (event: Event) => void\n  onConnect?: () => void\n}): () => void {\n  let sub: Subscription\n\n  const normalizedRelayURL = normalizeURL(relayURL)\n\n  fetchRelayInformation(normalizedRelayURL)\n    .then(async info => {\n      const abstractedRelay = await pool.ensureRelay(normalizedRelayURL)\n\n      onConnect?.()\n\n      sub = abstractedRelay.prepareSubscription(\n        [\n          {\n            kinds: [39000],\n            limit: 50,\n            authors: [info.pubkey],\n          },\n        ],\n        {\n          onevent(event: Event) {\n            onEvent(event)\n          },\n        },\n      )\n    })\n    .catch(err => {\n      sub.close()\n\n      onError(err)\n    })\n\n  return () => sub.close()\n}\n", "var _fetch: any\n\ntry {\n  _fetch = fetch\n} catch {}\n\nexport function useFetchImplementation(fetchImplementation: any): void {\n  _fetch = fetchImplementation\n}\n\nexport async function fetchRelayInformation(url: string): Promise<RelayInformation> {\n  return (await (\n    await fetch(url.replace('ws://', 'http://').replace('wss://', 'https://'), {\n      headers: { Accept: 'application/nostr+json' },\n    })\n  ).json()) as RelayInformation\n}\n\n/**\n * ## Relay Information Document\n\n * Relays may provide server metadata to clients to inform\n * them of capabilities, administrative contacts, and\n * various server attributes. This is made available as a\n * JSON document over HTTP, on the same URI as the relay's\n * websocket.\n\n * Any field may be omitted, and clients MUST ignore any\n * additional fields they do not understand. Relays MUST\n * accept CORS requests by sending\n * `Access-Control-Allow-Origin`,\n * `Access-Control-Allow-Headers`, and\n * `Access-Control-Allow-Methods` headers.\n * @param name string identifying relay\n * @param description string with detailed information\n * @param pubkey administrative contact pubkey\n * @param contact: administrative alternate contact\n * @param supported_nips a list of NIP numbers supported by\n * the relay\n * @param software identifying relay software URL\n * @param version string version identifier\n */\nexport interface BasicRelayInformation {\n  // string identifying relay\n  name: string\n  description: string\n  pubkey: string\n  contact: string\n  supported_nips: number[]\n  software: string\n  version: string\n  // limitation?: Limitations<A, P>\n}\n\n/**\n *  * ## Extra Fields\n\n *  * ### Server Limitations\n\n * These are limitations imposed by the relay on clients.\n * Your client should expect that requests which exceed\n * these practical_ limitations are rejected or fail immediately.\n * @param max_message_length this is the maximum number of\n * bytes for incoming JSON that the relay will attempt to\n * decode and act upon. When you send large subscriptions,\n * you will be limited by this value. It also effectively\n * limits the maximum size of any event. Value is calculated\n * from `[` to `]` and is after UTF-8 serialization (so some\n * unicode characters will cost 2-3 bytes). It is equal to\n * the maximum size of the WebSocket message frame.\n * @param max_subscriptions total number of subscriptions\n * that may be active on a single websocket connection to\n * this relay. It's possible that authenticated clients with\n * a (paid) relationship to the relay may have higher limits.\n * @param max_filters maximum number of filter values in\n * each subscription. Must be one or higher.\n * @param max_limit the relay server will clamp each\n * filter's `limit` value to this number.\n * This means the client won't be able to get more than this\n * number of events from a single subscription filter. This\n * clamping is typically done silently by the relay, but\n * with this number, you can know that there are additional\n * results if you narrowed your filter's time range or other\n * parameters.\n * @param max_subid_length maximum length of subscription id as a\n * string.\n * @param min_prefix for `authors` and `ids` filters which\n * are to match against a hex prefix, you must provide at\n * least this many hex digits in the prefix.\n * @param max_event_tags in any event, this is the maximum\n * number of elements in the `tags` list.\n * @param max_content_length maximum number of characters in\n * the `content` field of any event. This is a count of\n * unicode characters. After serializing into JSON it may be\n * larger (in bytes), and is still subject to the\n * max_message_length`, if defined.\n * @param min_pow_difficulty new events will require at\n * least this difficulty of PoW, based on [NIP-13](13.md),\n * or they will be rejected by this server.\n * @param auth_required this relay requires [NIP-42](42.md)\n * authentication to happen before a new connection may\n * perform any other action. Even if set to False,\n * authentication may be required for specific actions.\n * @param restricted_writes: this relay requires some kind\n * of condition to be fulfilled in order to accept events\n * (not necessarily, but including\n * @param payment_required this relay requires payment\n * before a new connection may perform any action.\n * @param created_at_lower_limit: 'created_at' lower limit\n * @param created_at_upper_limit: 'created_at' upper limit\n */\nexport interface Limitations {\n  max_message_length: number\n  max_subscriptions: number\n  max_filters: number\n  max_limit: number\n  max_subid_length: number\n  min_prefix: number\n  max_event_tags: number\n  max_content_length: number\n  min_pow_difficulty: number\n  auth_required: boolean\n  payment_required: boolean\n  created_at_lower_limit: number\n  created_at_upper_limit: number\n  restricted_writes: boolean\n}\n\nexport interface RetentionDetails {\n  kinds: (number | number[])[]\n  time?: number | null\n  count?: number | null\n}\ntype AnyRetentionDetails = RetentionDetails\n/**\n * ### Event Retention\n\n * There may be a cost associated with storing data forever,\n * so relays may wish to state retention times. The values\n * stated here are defaults for unauthenticated users and\n * visitors. Paid users would likely have other policies.\n\n * Retention times are given in seconds, with `null`\n * indicating infinity. If zero is provided, this means the\n * event will not be stored at all, and preferably an error\n * will be provided when those are received.\n * ```json\n{\n...\n  \"retention\": [\n    { \"kinds\": [0, 1, [5, 7], [40, 49]], \"time\": 3600 },\n    { \"kinds\": [[40000, 49999]], \"time\": 100 },\n    { \"kinds\": [[30000, 39999]], \"count\": 1000 },\n    { \"time\": 3600, \"count\": 10000 }\n  ]\n...\n}\n```\n * @param retention is a list of specifications: each will\n * apply to either all kinds, or a subset of kinds. Ranges\n * may be specified for the kind field as a tuple of\n * inclusive start and end values. Events of indicated kind\n * (or all) are then limited to a `count` and/or time\n * period.\n\n * It is possible to effectively blacklist Nostr-based\n * protocols that rely on a specific `kind` number, by\n * giving a retention time of zero for those `kind` values.\n * While that is unfortunate, it does allow clients to\n * discover servers that will support their protocol quickly\n * via a single HTTP fetch.\n\n * There is no need to specify retention times for\n * _ephemeral events_ as defined in [NIP-16](16.md) since\n * they are not retained.\n */\nexport interface Retention {\n  retention: AnyRetentionDetails[]\n}\n\n/**\n * Some relays may be governed by the arbitrary laws of a\n * nation state. This may limit what content can be stored\n * in cleartext on those relays. All clients are encouraged\n * to use encryption to work around this limitation.\n\n * It is not possible to describe the limitations of each\n * country's laws and policies which themselves are\n * typically vague and constantly shifting.\n\n * Therefore, this field allows the relay operator to\n * indicate which countries' laws might end up being\n * enforced on them, and then indirectly on their users'\n * content.\n\n * Users should be able to avoid relays in countries they\n * don't like, and/or select relays in more favourable\n * zones. Exposing this flexibility is up to the client\n * software.\n\n * @param relay_countries a list of two-level ISO country\n * codes (ISO 3166-1 alpha-2) whose laws and policies may\n * affect this relay. `EU` may be used for European Union\n * countries.\n\n * Remember that a relay may be hosted in a country which is\n * not the country of the legal entities who own the relay,\n * so it's very likely a number of countries are involved.\n */\nexport interface ContentLimitations {\n  relay_countries: string[]\n}\n\n/**\n * ### Community Preferences\n\n * For public text notes at least, a relay may try to foster\n * a local community. This would encourage users to follow\n * the global feed on that relay, in addition to their usual\n * individual follows. To support this goal, relays MAY\n * specify some of the following values.\n\n * @param language_tags  is an ordered list of [IETF\n * language\n * tags](https://en.wikipedia.org/wiki/IETF_language_tag\n * indicating the major languages spoken on the relay.\n * @param tags is a list of limitations on the topics to be\n * discussed. For example `sfw-only` indicates that only\n * \"Safe For Work\" content is encouraged on this relay. This\n * relies on assumptions of what the \"work\" \"community\"\n * feels \"safe\" talking about. In time, a common set of tags\n * may emerge that allow users to find relays that suit\n * their needs, and client software will be able to parse\n * these tags easily. The `bitcoin-only` tag indicates that\n * any _altcoin_, _\"crypto\"_ or _blockchain_ comments will\n * be ridiculed without mercy.\n * @param posting_policy is a link to a human-readable page\n * which specifies the community policies for the relay. In\n * cases where `sfw-only` is True, it's important to link to\n * a page which gets into the specifics of your posting\n * policy.\n\n * The `description` field should be used to describe your\n * community goals and values, in brief. The\n * `posting_policy` is for additional detail and legal\n * terms. Use the `tags` field to signify limitations on\n * content, or topics to be discussed, which could be\n * machine processed by appropriate client software.\n */\nexport interface CommunityPreferences {\n  language_tags: string[]\n  tags: string[]\n  posting_policy: string\n}\n\nexport interface Amount {\n  amount: number\n  unit: 'msat'\n}\nexport interface PublicationAmount extends Amount {\n  kinds: number[]\n}\nexport interface Subscription extends Amount {\n  period: number\n}\nexport interface Fees {\n  admission: Amount[]\n  subscription: Subscription[]\n  publication: PublicationAmount[]\n}\n/**\n * Relays that require payments may want to expose their fee\n * schedules.\n */\nexport interface PayToRelay {\n  payments_url: string\n  fees: Fees\n}\n\n/**\n * A URL pointing to an image to be used as an icon for the\n * relay. Recommended to be squared in shape.\n */\nexport interface Icon {\n  icon: string\n}\n\nexport type RelayInformation = BasicRelayInformation &\n  Partial<Retention> & {\n    limitation?: Partial<Limitations>\n  } & Partial<ContentLimitations> &\n  Partial<CommunityPreferences> &\n  Partial<PayToRelay> &\n  Partial<Icon>\n", "import { bytesToHex, concatBytes, hexToBytes } from '@noble/hashes/utils'\nimport { bech32 } from '@scure/base'\n\nimport { utf8Decoder, utf8Encoder } from './utils.ts'\n\nexport type NProfile = `nprofile1${string}`\nexport type NEvent = `nevent1${string}`\nexport type NAddr = `naddr1${string}`\nexport type NSec = `nsec1${string}`\nexport type NPub = `npub1${string}`\nexport type Note = `note1${string}`\nexport type Ncryptsec = `ncryptsec1${string}`\n\nexport const NostrTypeGuard = {\n  isNProfile: (value?: string | null): value is NProfile => /^nprofile1[a-z\\d]+$/.test(value || ''),\n  isNEvent: (value?: string | null): value is NEvent => /^nevent1[a-z\\d]+$/.test(value || ''),\n  isNAddr: (value?: string | null): value is NAddr => /^naddr1[a-z\\d]+$/.test(value || ''),\n  isNSec: (value?: string | null): value is NSec => /^nsec1[a-z\\d]{58}$/.test(value || ''),\n  isNPub: (value?: string | null): value is NPub => /^npub1[a-z\\d]{58}$/.test(value || ''),\n  isNote: (value?: string | null): value is Note => /^note1[a-z\\d]+$/.test(value || ''),\n  isNcryptsec: (value?: string | null): value is Ncryptsec => /^ncryptsec1[a-z\\d]+$/.test(value || ''),\n}\n\nexport const Bech32MaxSize = 5000\n\n/**\n * Bech32 regex.\n * @see https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki#bech32\n */\nexport const BECH32_REGEX = /[\\x21-\\x7E]{1,83}1[023456789acdefghjklmnpqrstuvwxyz]{6,}/\n\nfunction integerToUint8Array(number: number) {\n  // Create a Uint8Array with enough space to hold a 32-bit integer (4 bytes).\n  const uint8Array = new Uint8Array(4)\n\n  // Use bitwise operations to extract the bytes.\n  uint8Array[0] = (number >> 24) & 0xff // Most significant byte (MSB)\n  uint8Array[1] = (number >> 16) & 0xff\n  uint8Array[2] = (number >> 8) & 0xff\n  uint8Array[3] = number & 0xff // Least significant byte (LSB)\n\n  return uint8Array\n}\n\nexport type ProfilePointer = {\n  pubkey: string // hex\n  relays?: string[]\n}\n\nexport type EventPointer = {\n  id: string // hex\n  relays?: string[]\n  author?: string\n  kind?: number\n}\n\nexport type AddressPointer = {\n  identifier: string\n  pubkey: string\n  kind: number\n  relays?: string[]\n}\n\nexport function decodeNostrURI(nip19code: string): ReturnType<typeof decode> | { type: 'invalid'; data: null } {\n  try {\n    if (nip19code.startsWith('nostr:')) nip19code = nip19code.substring(6)\n    return decode(nip19code)\n  } catch (_err) {\n    return { type: 'invalid', data: null }\n  }\n}\n\nexport type DecodedNevent = {\n  type: 'nevent'\n  data: EventPointer\n}\n\nexport type DecodedNprofile = {\n  type: 'nprofile'\n  data: ProfilePointer\n}\n\nexport type DecodedNaddr = {\n  type: 'naddr'\n  data: AddressPointer\n}\n\nexport type DecodedNsec = {\n  type: 'nsec'\n  data: Uint8Array\n}\n\nexport type DecodedNpub = {\n  type: 'npub'\n  data: string\n}\n\nexport type DecodedNote = {\n  type: 'note'\n  data: string\n}\n\nexport type DecodedResult = DecodedNevent | DecodedNprofile | DecodedNaddr | DecodedNpub | DecodedNsec | DecodedNote\n\nexport function decode(nip19: NEvent): DecodedNevent\nexport function decode(nip19: NProfile): DecodedNprofile\nexport function decode(nip19: NAddr): DecodedNaddr\nexport function decode(nip19: NSec): DecodedNsec\nexport function decode(nip19: NPub): DecodedNpub\nexport function decode(nip19: Note): DecodedNote\nexport function decode(code: string): DecodedResult\nexport function decode(code: string): DecodedResult {\n  let { prefix, words } = bech32.decode(code, Bech32MaxSize)\n  let data = new Uint8Array(bech32.fromWords(words))\n\n  switch (prefix) {\n    case 'nprofile': {\n      let tlv = parseTLV(data)\n      if (!tlv[0]?.[0]) throw new Error('missing TLV 0 for nprofile')\n      if (tlv[0][0].length !== 32) throw new Error('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      let tlv = parseTLV(data)\n      if (!tlv[0]?.[0]) throw new Error('missing TLV 0 for nevent')\n      if (tlv[0][0].length !== 32) throw new Error('TLV 0 should be 32 bytes')\n      if (tlv[2] && tlv[2][0].length !== 32) throw new Error('TLV 2 should be 32 bytes')\n      if (tlv[3] && tlv[3][0].length !== 4) throw new Error('TLV 3 should be 4 bytes')\n\n      return {\n        type: 'nevent',\n        data: {\n          id: bytesToHex(tlv[0][0]),\n          relays: tlv[1] ? tlv[1].map(d => utf8Decoder.decode(d)) : [],\n          author: tlv[2]?.[0] ? bytesToHex(tlv[2][0]) : undefined,\n          kind: tlv[3]?.[0] ? parseInt(bytesToHex(tlv[3][0]), 16) : undefined,\n        },\n      }\n    }\n\n    case 'naddr': {\n      let tlv = parseTLV(data)\n      if (!tlv[0]?.[0]) throw new Error('missing TLV 0 for naddr')\n      if (!tlv[2]?.[0]) throw new Error('missing TLV 2 for naddr')\n      if (tlv[2][0].length !== 32) throw new Error('TLV 2 should be 32 bytes')\n      if (!tlv[3]?.[0]) throw new Error('missing TLV 3 for naddr')\n      if (tlv[3][0].length !== 4) throw new Error('TLV 3 should be 4 bytes')\n\n      return {\n        type: 'naddr',\n        data: {\n          identifier: utf8Decoder.decode(tlv[0][0]),\n          pubkey: bytesToHex(tlv[2][0]),\n          kind: parseInt(bytesToHex(tlv[3][0]), 16),\n          relays: tlv[1] ? tlv[1].map(d => utf8Decoder.decode(d)) : [],\n        },\n      }\n    }\n\n    case 'nsec':\n      return { type: prefix, data }\n\n    case 'npub':\n    case 'note':\n      return { type: prefix, data: bytesToHex(data) }\n\n    default:\n      throw new Error(`unknown prefix ${prefix}`)\n  }\n}\n\ntype TLV = { [t: number]: Uint8Array[] }\n\nfunction parseTLV(data: Uint8Array): TLV {\n  let result: TLV = {}\n  let rest = data\n  while (rest.length > 0) {\n    let t = rest[0]\n    let l = rest[1]\n    let v = rest.slice(2, 2 + l)\n    rest = rest.slice(2 + l)\n    if (v.length < l) throw new Error(`not enough data to read on TLV ${t}`)\n    result[t] = result[t] || []\n    result[t].push(v)\n  }\n  return result\n}\n\nexport function nsecEncode(key: Uint8Array): NSec {\n  return encodeBytes('nsec', key)\n}\n\nexport function npubEncode(hex: string): NPub {\n  return encodeBytes('npub', hexToBytes(hex))\n}\n\nexport function noteEncode(hex: string): Note {\n  return encodeBytes('note', hexToBytes(hex))\n}\n\nfunction encodeBech32<Prefix extends string>(prefix: Prefix, data: Uint8Array): `${Prefix}1${string}` {\n  let words = bech32.toWords(data)\n  return bech32.encode(prefix, words, Bech32MaxSize) as `${Prefix}1${string}`\n}\n\nexport function encodeBytes<Prefix extends string>(prefix: Prefix, bytes: Uint8Array): `${Prefix}1${string}` {\n  return encodeBech32(prefix, bytes)\n}\n\nexport function nprofileEncode(profile: ProfilePointer): NProfile {\n  let data = encodeTLV({\n    0: [hexToBytes(profile.pubkey)],\n    1: (profile.relays || []).map(url => utf8Encoder.encode(url)),\n  })\n  return encodeBech32('nprofile', data)\n}\n\nexport function neventEncode(event: EventPointer): NEvent {\n  let kindArray\n  if (event.kind !== undefined) {\n    kindArray = integerToUint8Array(event.kind)\n  }\n\n  let data = encodeTLV({\n    0: [hexToBytes(event.id)],\n    1: (event.relays || []).map(url => utf8Encoder.encode(url)),\n    2: event.author ? [hexToBytes(event.author)] : [],\n    3: kindArray ? [new Uint8Array(kindArray)] : [],\n  })\n\n  return encodeBech32('nevent', data)\n}\n\nexport function naddrEncode(addr: AddressPointer): NAddr {\n  let kind = new ArrayBuffer(4)\n  new DataView(kind).setUint32(0, addr.kind, false)\n\n  let data = encodeTLV({\n    0: [utf8Encoder.encode(addr.identifier)],\n    1: (addr.relays || []).map(url => utf8Encoder.encode(url)),\n    2: [hexToBytes(addr.pubkey)],\n    3: [new Uint8Array(kind)],\n  })\n  return encodeBech32('naddr', data)\n}\n\nfunction encodeTLV(tlv: TLV): Uint8Array {\n  let entries: Uint8Array[] = []\n\n  Object.entries(tlv)\n    .reverse()\n    .forEach(([t, vs]) => {\n      vs.forEach(v => {\n        let entry = new Uint8Array(v.length + 2)\n        entry.set([parseInt(t)], 0)\n        entry.set([v.length], 1)\n        entry.set(v, 2)\n        entries.push(entry)\n      })\n    })\n\n  return concatBytes(...entries)\n}\n", "import type { Event } from './core.ts'\n\nexport const utf8Decoder: TextDecoder = new TextDecoder('utf-8')\nexport const utf8Encoder: TextEncoder = new TextEncoder()\n\nexport { bytesToHex, hexToBytes } from '@noble/hashes/utils'\n\nexport function normalizeURL(url: string): string {\n  try {\n    if (url.indexOf('://') === -1) url = 'wss://' + url\n    let p = new URL(url)\n    if (p.protocol === 'http:') p.protocol = 'ws:'\n    else if (p.protocol === 'https:') p.protocol = 'wss:'\n    p.pathname = p.pathname.replace(/\\/+/g, '/')\n    if (p.pathname.endsWith('/')) p.pathname = p.pathname.slice(0, -1)\n    if ((p.port === '80' && p.protocol === 'ws:') || (p.port === '443' && p.protocol === 'wss:')) p.port = ''\n    p.searchParams.sort()\n    p.hash = ''\n    return p.toString()\n  } catch (e) {\n    throw new Error(`Invalid URL: ${url}`)\n  }\n}\n\nexport function insertEventIntoDescendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return b.created_at - event.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function insertEventIntoAscendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return event.created_at - b.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function binarySearch<T>(arr: T[], compare: (b: T) => number): [number, boolean] {\n  let start = 0\n  let end = arr.length - 1\n\n  while (start <= end) {\n    const mid = Math.floor((start + end) / 2)\n    const cmp = compare(arr[mid])\n\n    if (cmp === 0) {\n      return [mid, true]\n    }\n\n    if (cmp < 0) {\n      end = mid - 1\n    } else {\n      start = mid + 1\n    }\n  }\n\n  return [start, false]\n}\n\nexport class QueueNode<V> {\n  public value: V\n  public next: QueueNode<V> | null = null\n  public prev: QueueNode<V> | null = null\n\n  constructor(message: V) {\n    this.value = message\n  }\n}\n\nexport class Queue<V> {\n  public first: QueueNode<V> | null\n  public last: QueueNode<V> | null\n\n  constructor() {\n    this.first = null\n    this.last = null\n  }\n\n  enqueue(value: V): boolean {\n    const newNode = new QueueNode(value)\n    if (!this.last) {\n      // list is empty\n      this.first = newNode\n      this.last = newNode\n    } else if (this.last === this.first) {\n      // list has a single element\n      this.last = newNode\n      this.last.prev = this.first\n      this.first.next = newNode\n    } else {\n      // list has elements, add as last\n      newNode.prev = this.last\n      this.last.next = newNode\n      this.last = newNode\n    }\n    return true\n  }\n\n  dequeue(): V | null {\n    if (!this.first) return null\n\n    if (this.first === this.last) {\n      const target = this.first\n      this.first = null\n      this.last = null\n      return target.value\n    }\n\n    const target = this.first\n    this.first = target.next\n    if (this.first) {\n      this.first.prev = null // fix: clean up prev pointer\n    }\n\n    return target.value\n  }\n}\n"],
-  "mappings": "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",
+  "sourcesContent": ["import { AbstractSimplePool } from './abstract-pool.ts'\nimport { Subscription } from './abstract-relay.ts'\nimport type { Event, EventTemplate } from './core.ts'\nimport { fetchRelayInformation, RelayInformation } from './nip11.ts'\nimport { decode, NostrTypeGuard } from './nip19.ts'\nimport { normalizeURL } from './utils.ts'\n\n/**\n * Represents a NIP29 group.\n */\nexport type Group = {\n  relay: string\n  metadata: GroupMetadata\n  admins?: GroupAdmin[]\n  members?: GroupMember[]\n  reference: GroupReference\n}\n\n/**\n * Represents the metadata for a NIP29 group.\n */\nexport type GroupMetadata = {\n  id: string\n  pubkey: string\n  name?: string\n  picture?: string\n  about?: string\n  isPublic?: boolean\n  isOpen?: boolean\n}\n\n/**\n * Represents a NIP29 group reference.\n */\nexport type GroupReference = {\n  id: string\n  host: string\n}\n\n/**\n * Represents a NIP29 group member.\n */\nexport type GroupMember = {\n  pubkey: string\n  label?: string\n}\n\n/**\n * Represents a NIP29 group admin.\n */\nexport type GroupAdmin = {\n  pubkey: string\n  label?: string\n  permissions: GroupAdminPermission[]\n}\n\n/**\n * Represents the permissions that a NIP29 group admin can have.\n */\nexport enum GroupAdminPermission {\n  /** @deprecated use PutUser instead */\n  AddUser = 'add-user',\n  EditMetadata = 'edit-metadata',\n  DeleteEvent = 'delete-event',\n  RemoveUser = 'remove-user',\n  /** @deprecated removed from NIP */\n  AddPermission = 'add-permission',\n  /** @deprecated removed from NIP */\n  RemovePermission = 'remove-permission',\n  /** @deprecated removed from NIP */\n  EditGroupStatus = 'edit-group-status',\n  PutUser = 'put-user',\n  CreateGroup = 'create-group',\n  DeleteGroup = 'delete-group',\n  CreateInvite = 'create-invite',\n}\n\n/**\n * Generates a group metadata event template.\n *\n * @param group - The group object.\n * @returns An event template with the generated group metadata that can be signed later.\n */\nexport function generateGroupMetadataEventTemplate(group: Group): EventTemplate {\n  const tags: string[][] = [['d', group.metadata.id]]\n  group.metadata.name && tags.push(['name', group.metadata.name])\n  group.metadata.picture && tags.push(['picture', group.metadata.picture])\n  group.metadata.about && tags.push(['about', group.metadata.about])\n  group.metadata.isPublic && tags.push(['public'])\n  group.metadata.isOpen && tags.push(['open'])\n\n  return {\n    content: '',\n    created_at: Math.floor(Date.now() / 1000),\n    kind: 39000,\n    tags,\n  }\n}\n\n/**\n * Validates a group metadata event.\n *\n * @param event - The event to validate.\n * @returns A boolean indicating whether the event is valid.\n */\nexport function validateGroupMetadataEvent(event: Event): boolean {\n  if (event.kind !== 39000) return false\n\n  if (!event.pubkey) return false\n\n  const requiredTags = ['d'] as const\n  for (const tag of requiredTags) {\n    if (!event.tags.find(([t]) => t == tag)) return false\n  }\n\n  return true\n}\n\n/**\n * Generates an event template for group admins.\n *\n * @param group - The group object.\n * @param admins - An array of group admins.\n * @returns The generated event template with the group admins that can be signed later.\n */\nexport function generateGroupAdminsEventTemplate(group: Group, admins: GroupAdmin[]): EventTemplate {\n  const tags: string[][] = [['d', group.metadata.id]]\n  for (const admin of admins) {\n    tags.push(['p', admin.pubkey, admin.label || '', ...admin.permissions])\n  }\n\n  return {\n    content: '',\n    created_at: Math.floor(Date.now() / 1000),\n    kind: 39001,\n    tags,\n  }\n}\n\n/**\n * Validates a group admins event.\n *\n * @param event - The event to validate.\n * @returns True if the event is valid, false otherwise.\n */\nexport function validateGroupAdminsEvent(event: Event): boolean {\n  if (event.kind !== 39001) return false\n\n  const requiredTags = ['d'] as const\n  for (const tag of requiredTags) {\n    if (!event.tags.find(([t]) => t == tag)) return false\n  }\n\n  // validate permissions\n  for (const [tag, _value, _label, ...permissions] of event.tags) {\n    if (tag !== 'p') continue\n\n    for (let i = 0; i < permissions.length; i += 1) {\n      if (typeof permissions[i] !== 'string') return false\n\n      // validate permission name from the GroupAdminPermission enum\n      if (!Object.values(GroupAdminPermission).includes(permissions[i] as GroupAdminPermission)) return false\n    }\n  }\n\n  return true\n}\n\n/**\n * Generates an event template for a group with its members.\n *\n * @param group - The group object.\n * @param members - An array of group members.\n * @returns The generated event template with the group members that can be signed later.\n */\nexport function generateGroupMembersEventTemplate(group: Group, members: GroupMember[]): EventTemplate {\n  const tags: string[][] = [['d', group.metadata.id]]\n  for (const member of members) {\n    tags.push(['p', member.pubkey, member.label || ''])\n  }\n\n  return {\n    content: '',\n    created_at: Math.floor(Date.now() / 1000),\n    kind: 39002,\n    tags,\n  }\n}\n\n/**\n * Validates a group members event.\n *\n * @param event - The event to validate.\n * @returns Returns `true` if the event is a valid group members event, `false` otherwise.\n */\nexport function validateGroupMembersEvent(event: Event): boolean {\n  if (event.kind !== 39002) return false\n\n  const requiredTags = ['d'] as const\n  for (const tag of requiredTags) {\n    if (!event.tags.find(([t]) => t == tag)) return false\n  }\n\n  return true\n}\n\n/**\n * Returns the normalized relay URL based on the provided group reference.\n *\n * @param groupReference - The group reference object containing the host.\n * @returns The normalized relay URL.\n */\nexport function getNormalizedRelayURLByGroupReference(groupReference: GroupReference): string {\n  return normalizeURL(groupReference.host)\n}\n\n/**\n * Fetches relay information by group reference.\n *\n * @param groupReference The group reference.\n * @returns A promise that resolves to the relay information.\n */\nexport async function fetchRelayInformationByGroupReference(groupReference: GroupReference): Promise<RelayInformation> {\n  const normalizedRelayURL = getNormalizedRelayURLByGroupReference(groupReference)\n\n  return fetchRelayInformation(normalizedRelayURL)\n}\n\n/**\n * Fetches the group metadata event from the specified pool.\n * If the normalizedRelayURL is not provided, it will be obtained using the groupReference.\n * If the relayInformation is not provided, it will be fetched using the normalizedRelayURL.\n *\n * @param {Object} options - The options object.\n * @param {AbstractSimplePool} options.pool - The pool to fetch the group metadata event from.\n * @param {GroupReference} options.groupReference - The reference to the group.\n * @param {string} [options.normalizedRelayURL] - The normalized URL of the relay.\n * @param {RelayInformation} [options.relayInformation] - The relay information object.\n * @returns {Promise<Event>} The group metadata event that can be parsed later to get the group metadata object.\n * @throws {Error} If the group is not found on the specified relay.\n */\nexport async function fetchGroupMetadataEvent({\n  pool,\n  groupReference,\n  relayInformation,\n  normalizedRelayURL,\n}: {\n  pool: AbstractSimplePool\n  groupReference: GroupReference\n  normalizedRelayURL?: string\n  relayInformation?: RelayInformation\n}): Promise<Event> {\n  if (!normalizedRelayURL) {\n    normalizedRelayURL = getNormalizedRelayURLByGroupReference(groupReference)\n  }\n\n  if (!relayInformation) {\n    relayInformation = await fetchRelayInformation(normalizedRelayURL)\n  }\n\n  const groupMetadataEvent = await pool.get([normalizedRelayURL], {\n    kinds: [39000],\n    authors: [relayInformation.pubkey],\n    '#d': [groupReference.id],\n  })\n\n  if (!groupMetadataEvent) throw new Error(`group '${groupReference.id}' not found on ${normalizedRelayURL}`)\n\n  return groupMetadataEvent\n}\n\n/**\n * Parses a group metadata event and returns the corresponding GroupMetadata object.\n *\n * @param event - The event to parse.\n * @returns The parsed GroupMetadata object.\n * @throws An error if the group metadata event is invalid.\n */\nexport function parseGroupMetadataEvent(event: Event): GroupMetadata {\n  if (!validateGroupMetadataEvent(event)) throw new Error('invalid group metadata event')\n\n  const metadata: GroupMetadata = {\n    id: '',\n    pubkey: event.pubkey,\n  }\n\n  for (const [tag, value] of event.tags) {\n    switch (tag) {\n      case 'd':\n        metadata.id = value\n        break\n      case 'name':\n        metadata.name = value\n        break\n      case 'picture':\n        metadata.picture = value\n        break\n      case 'about':\n        metadata.about = value\n        break\n      case 'public':\n        metadata.isPublic = true\n        break\n      case 'open':\n        metadata.isOpen = true\n        break\n    }\n  }\n\n  return metadata\n}\n\n/**\n * Fetches the group admins event from the specified pool.\n * If the normalizedRelayURL is not provided, it will be obtained from the groupReference.\n * If the relayInformation is not provided, it will be fetched using the normalizedRelayURL.\n *\n * @param {Object} options - The options object.\n * @param {AbstractSimplePool} options.pool - The pool to fetch the group admins event from.\n * @param {GroupReference} options.groupReference - The reference to the group.\n * @param {string} [options.normalizedRelayURL] - The normalized relay URL.\n * @param {RelayInformation} [options.relayInformation] - The relay information.\n * @returns {Promise<Event>} The group admins event that can be parsed later to get the group admins object.\n * @throws {Error} If the group admins event is not found on the specified relay.\n */\nexport async function fetchGroupAdminsEvent({\n  pool,\n  groupReference,\n  relayInformation,\n  normalizedRelayURL,\n}: {\n  pool: AbstractSimplePool\n  groupReference: GroupReference\n  normalizedRelayURL?: string\n  relayInformation?: RelayInformation\n}): Promise<Event> {\n  if (!normalizedRelayURL) {\n    normalizedRelayURL = getNormalizedRelayURLByGroupReference(groupReference)\n  }\n\n  if (!relayInformation) {\n    relayInformation = await fetchRelayInformation(normalizedRelayURL)\n  }\n\n  const groupAdminsEvent = await pool.get([normalizedRelayURL], {\n    kinds: [39001],\n    authors: [relayInformation.pubkey],\n    '#d': [groupReference.id],\n  })\n\n  if (!groupAdminsEvent) throw new Error(`admins for group '${groupReference.id}' not found on ${normalizedRelayURL}`)\n\n  return groupAdminsEvent\n}\n\n/**\n * Parses a group admins event and returns an array of GroupAdmin objects.\n *\n * @param event - The event to parse.\n * @returns An array of GroupAdmin objects.\n * @throws Throws an error if the group admins event is invalid.\n */\nexport function parseGroupAdminsEvent(event: Event): GroupAdmin[] {\n  if (!validateGroupAdminsEvent(event)) throw new Error('invalid group admins event')\n\n  const admins: GroupAdmin[] = []\n\n  for (const [tag, value, label, ...permissions] of event.tags) {\n    if (tag !== 'p') continue\n\n    admins.push({\n      pubkey: value,\n      label,\n      permissions: permissions as GroupAdminPermission[],\n    })\n  }\n\n  return admins\n}\n\n/**\n * Fetches the group members event from the specified relay.\n * If the normalizedRelayURL is not provided, it will be obtained using the groupReference.\n * If the relayInformation is not provided, it will be fetched using the normalizedRelayURL.\n *\n * @param {Object} options - The options object.\n * @param {AbstractSimplePool} options.pool - The pool object.\n * @param {GroupReference} options.groupReference - The group reference object.\n * @param {string} [options.normalizedRelayURL] - The normalized relay URL.\n * @param {RelayInformation} [options.relayInformation] - The relay information object.\n * @returns {Promise<Event>} The group members event that can be parsed later to get the group members object.\n * @throws {Error} If the group members event is not found.\n */\nexport async function fetchGroupMembersEvent({\n  pool,\n  groupReference,\n  relayInformation,\n  normalizedRelayURL,\n}: {\n  pool: AbstractSimplePool\n  groupReference: GroupReference\n  normalizedRelayURL?: string\n  relayInformation?: RelayInformation\n}): Promise<Event> {\n  if (!normalizedRelayURL) {\n    normalizedRelayURL = getNormalizedRelayURLByGroupReference(groupReference)\n  }\n\n  if (!relayInformation) {\n    relayInformation = await fetchRelayInformation(normalizedRelayURL)\n  }\n\n  const groupMembersEvent = await pool.get([normalizedRelayURL], {\n    kinds: [39002],\n    authors: [relayInformation.pubkey],\n    '#d': [groupReference.id],\n  })\n\n  if (!groupMembersEvent) throw new Error(`members for group '${groupReference.id}' not found on ${normalizedRelayURL}`)\n\n  return groupMembersEvent\n}\n\n/**\n * Parses a group members event and returns an array of GroupMember objects.\n * @param event - The event to parse.\n * @returns An array of GroupMember objects.\n * @throws Throws an error if the group members event is invalid.\n */\nexport function parseGroupMembersEvent(event: Event): GroupMember[] {\n  if (!validateGroupMembersEvent(event)) throw new Error('invalid group members event')\n\n  const members: GroupMember[] = []\n\n  for (const [tag, value, label] of event.tags) {\n    if (tag !== 'p') continue\n\n    members.push({\n      pubkey: value,\n      label,\n    })\n  }\n\n  return members\n}\n\n/**\n * Fetches and parses the group metadata event, group admins event, and group members event from the specified pool.\n * If the normalized relay URL is not provided, it will be obtained using the group reference.\n * If the relay information is not provided, it will be fetched using the normalized relay URL.\n *\n * @param {Object} options - The options for loading the group.\n * @param {AbstractSimplePool} options.pool - The pool to load the group from.\n * @param {GroupReference} options.groupReference - The reference of the group to load.\n * @param {string} [options.normalizedRelayURL] - The normalized URL of the relay to use.\n * @param {RelayInformation} [options.relayInformation] - The relay information to use.\n * @returns {Promise<Group>} A promise that resolves to the loaded group.\n */\nexport async function loadGroup({\n  pool,\n  groupReference,\n  normalizedRelayURL,\n  relayInformation,\n}: {\n  pool: AbstractSimplePool\n  groupReference: GroupReference\n  normalizedRelayURL?: string\n  relayInformation?: RelayInformation\n}): Promise<Group> {\n  if (!normalizedRelayURL) {\n    normalizedRelayURL = getNormalizedRelayURLByGroupReference(groupReference)\n  }\n\n  if (!relayInformation) {\n    relayInformation = await fetchRelayInformation(normalizedRelayURL)\n  }\n\n  const metadataEvent = await fetchGroupMetadataEvent({ pool, groupReference, normalizedRelayURL, relayInformation })\n  const metadata = parseGroupMetadataEvent(metadataEvent)\n\n  const adminsEvent = await fetchGroupAdminsEvent({ pool, groupReference, normalizedRelayURL, relayInformation })\n  const admins = parseGroupAdminsEvent(adminsEvent)\n\n  const membersEvent = await fetchGroupMembersEvent({ pool, groupReference, normalizedRelayURL, relayInformation })\n  const members = parseGroupMembersEvent(membersEvent)\n\n  const group: Group = {\n    relay: normalizedRelayURL,\n    metadata,\n    admins,\n    members,\n    reference: groupReference,\n  }\n\n  return group\n}\n\n/**\n * Loads a group from the specified pool using the provided group code.\n *\n * @param {AbstractSimplePool} pool - The pool to load the group from.\n * @param {string} code - The code representing the group.\n * @returns {Promise<Group>} - A promise that resolves to the loaded group.\n * @throws {Error} - If the group code is invalid.\n */\nexport async function loadGroupFromCode(pool: AbstractSimplePool, code: string): Promise<Group> {\n  const groupReference = parseGroupCode(code)\n\n  if (!groupReference) throw new Error('invalid group code')\n\n  return loadGroup({ pool, groupReference })\n}\n\n/**\n * Parses a group code and returns a GroupReference object.\n *\n * @param code The group code to parse.\n * @returns A GroupReference object if the code is valid, otherwise null.\n */\nexport function parseGroupCode(code: string): null | GroupReference {\n  if (NostrTypeGuard.isNAddr(code)) {\n    try {\n      let { data } = decode(code)\n\n      let { relays, identifier } = data\n      if (!relays || relays.length === 0) return null\n\n      let host = relays![0]\n      if (host.startsWith('wss://')) {\n        host = host.slice(6)\n      }\n      return { host, id: identifier }\n    } catch (err) {\n      return null\n    }\n  } else if (code.split(\"'\").length === 2) {\n    let spl = code.split(\"'\")\n    return { host: spl[0], id: spl[1] }\n  }\n\n  return null\n}\n\n/**\n * Encodes a group reference into a string.\n *\n * @param gr - The group reference to encode.\n * @returns The encoded group reference as a string.\n */\nexport function encodeGroupReference(gr: GroupReference): string {\n  const { host, id } = gr\n  const normalizedHost = host.replace(/^(https?:\\/\\/|wss?:\\/\\/)/, '')\n\n  return `${normalizedHost}'${id}`\n}\n\n/**\n * Subscribes to relay groups metadata events and calls the provided event handler function\n * when an event is received.\n *\n * @param {Object} options - The options for subscribing to relay groups metadata events.\n * @param {AbstractSimplePool} options.pool - The pool to subscribe to.\n * @param {string} options.relayURL - The URL of the relay.\n * @param {Function} options.onError - The error handler function.\n * @param {Function} options.onEvent - The event handler function.\n * @param {Function} [options.onConnect] - The connect handler function.\n * @returns {Function} - A function to close the subscription\n */\nexport function subscribeRelayGroupsMetadataEvents({\n  pool,\n  relayURL,\n  onError,\n  onEvent,\n  onConnect,\n}: {\n  pool: AbstractSimplePool\n  relayURL: string\n  onError: (err: Error) => void\n  onEvent: (event: Event) => void\n  onConnect?: () => void\n}): () => void {\n  let sub: Subscription\n\n  const normalizedRelayURL = normalizeURL(relayURL)\n\n  fetchRelayInformation(normalizedRelayURL)\n    .then(async info => {\n      const abstractedRelay = await pool.ensureRelay(normalizedRelayURL)\n\n      onConnect?.()\n\n      sub = abstractedRelay.prepareSubscription(\n        [\n          {\n            kinds: [39000],\n            limit: 50,\n            authors: [info.pubkey],\n          },\n        ],\n        {\n          onevent(event: Event) {\n            onEvent(event)\n          },\n        },\n      )\n    })\n    .catch(err => {\n      sub.close()\n\n      onError(err)\n    })\n\n  return () => sub.close()\n}\n", "var _fetch: any\n\ntry {\n  _fetch = fetch\n} catch {}\n\nexport function useFetchImplementation(fetchImplementation: any): void {\n  _fetch = fetchImplementation\n}\n\nexport async function fetchRelayInformation(url: string): Promise<RelayInformation> {\n  return (await (\n    await fetch(url.replace('ws://', 'http://').replace('wss://', 'https://'), {\n      headers: { Accept: 'application/nostr+json' },\n    })\n  ).json()) as RelayInformation\n}\n\n/**\n * ## Relay Information Document\n\n * Relays may provide server metadata to clients to inform\n * them of capabilities, administrative contacts, and\n * various server attributes. This is made available as a\n * JSON document over HTTP, on the same URI as the relay's\n * websocket.\n\n * Any field may be omitted, and clients MUST ignore any\n * additional fields they do not understand. Relays MUST\n * accept CORS requests by sending\n * `Access-Control-Allow-Origin`,\n * `Access-Control-Allow-Headers`, and\n * `Access-Control-Allow-Methods` headers.\n * @param name string identifying relay\n * @param description string with detailed information\n * @param pubkey administrative contact pubkey\n * @param contact: administrative alternate contact\n * @param supported_nips a list of NIP numbers supported by\n * the relay\n * @param software identifying relay software URL\n * @param version string version identifier\n */\nexport interface BasicRelayInformation {\n  // string identifying relay\n  name: string\n  description: string\n  pubkey: string\n  contact: string\n  supported_nips: number[]\n  software: string\n  version: string\n  // limitation?: Limitations<A, P>\n}\n\n/**\n *  * ## Extra Fields\n\n *  * ### Server Limitations\n\n * These are limitations imposed by the relay on clients.\n * Your client should expect that requests which exceed\n * these practical_ limitations are rejected or fail immediately.\n * @param max_message_length this is the maximum number of\n * bytes for incoming JSON that the relay will attempt to\n * decode and act upon. When you send large subscriptions,\n * you will be limited by this value. It also effectively\n * limits the maximum size of any event. Value is calculated\n * from `[` to `]` and is after UTF-8 serialization (so some\n * unicode characters will cost 2-3 bytes). It is equal to\n * the maximum size of the WebSocket message frame.\n * @param max_subscriptions total number of subscriptions\n * that may be active on a single websocket connection to\n * this relay. It's possible that authenticated clients with\n * a (paid) relationship to the relay may have higher limits.\n * @param max_filters maximum number of filter values in\n * each subscription. Must be one or higher.\n * @param max_limit the relay server will clamp each\n * filter's `limit` value to this number.\n * This means the client won't be able to get more than this\n * number of events from a single subscription filter. This\n * clamping is typically done silently by the relay, but\n * with this number, you can know that there are additional\n * results if you narrowed your filter's time range or other\n * parameters.\n * @param max_subid_length maximum length of subscription id as a\n * string.\n * @param min_prefix for `authors` and `ids` filters which\n * are to match against a hex prefix, you must provide at\n * least this many hex digits in the prefix.\n * @param max_event_tags in any event, this is the maximum\n * number of elements in the `tags` list.\n * @param max_content_length maximum number of characters in\n * the `content` field of any event. This is a count of\n * unicode characters. After serializing into JSON it may be\n * larger (in bytes), and is still subject to the\n * max_message_length`, if defined.\n * @param min_pow_difficulty new events will require at\n * least this difficulty of PoW, based on [NIP-13](13.md),\n * or they will be rejected by this server.\n * @param auth_required this relay requires [NIP-42](42.md)\n * authentication to happen before a new connection may\n * perform any other action. Even if set to False,\n * authentication may be required for specific actions.\n * @param restricted_writes: this relay requires some kind\n * of condition to be fulfilled in order to accept events\n * (not necessarily, but including\n * @param payment_required this relay requires payment\n * before a new connection may perform any action.\n * @param created_at_lower_limit: 'created_at' lower limit\n * @param created_at_upper_limit: 'created_at' upper limit\n */\nexport interface Limitations {\n  max_message_length: number\n  max_subscriptions: number\n  max_filters: number\n  max_limit: number\n  max_subid_length: number\n  min_prefix: number\n  max_event_tags: number\n  max_content_length: number\n  min_pow_difficulty: number\n  auth_required: boolean\n  payment_required: boolean\n  created_at_lower_limit: number\n  created_at_upper_limit: number\n  restricted_writes: boolean\n}\n\nexport interface RetentionDetails {\n  kinds: (number | number[])[]\n  time?: number | null\n  count?: number | null\n}\ntype AnyRetentionDetails = RetentionDetails\n/**\n * ### Event Retention\n\n * There may be a cost associated with storing data forever,\n * so relays may wish to state retention times. The values\n * stated here are defaults for unauthenticated users and\n * visitors. Paid users would likely have other policies.\n\n * Retention times are given in seconds, with `null`\n * indicating infinity. If zero is provided, this means the\n * event will not be stored at all, and preferably an error\n * will be provided when those are received.\n * ```json\n{\n...\n  \"retention\": [\n    { \"kinds\": [0, 1, [5, 7], [40, 49]], \"time\": 3600 },\n    { \"kinds\": [[40000, 49999]], \"time\": 100 },\n    { \"kinds\": [[30000, 39999]], \"count\": 1000 },\n    { \"time\": 3600, \"count\": 10000 }\n  ]\n...\n}\n```\n * @param retention is a list of specifications: each will\n * apply to either all kinds, or a subset of kinds. Ranges\n * may be specified for the kind field as a tuple of\n * inclusive start and end values. Events of indicated kind\n * (or all) are then limited to a `count` and/or time\n * period.\n\n * It is possible to effectively blacklist Nostr-based\n * protocols that rely on a specific `kind` number, by\n * giving a retention time of zero for those `kind` values.\n * While that is unfortunate, it does allow clients to\n * discover servers that will support their protocol quickly\n * via a single HTTP fetch.\n\n * There is no need to specify retention times for\n * _ephemeral events_ as defined in [NIP-16](16.md) since\n * they are not retained.\n */\nexport interface Retention {\n  retention: AnyRetentionDetails[]\n}\n\n/**\n * Some relays may be governed by the arbitrary laws of a\n * nation state. This may limit what content can be stored\n * in cleartext on those relays. All clients are encouraged\n * to use encryption to work around this limitation.\n\n * It is not possible to describe the limitations of each\n * country's laws and policies which themselves are\n * typically vague and constantly shifting.\n\n * Therefore, this field allows the relay operator to\n * indicate which countries' laws might end up being\n * enforced on them, and then indirectly on their users'\n * content.\n\n * Users should be able to avoid relays in countries they\n * don't like, and/or select relays in more favourable\n * zones. Exposing this flexibility is up to the client\n * software.\n\n * @param relay_countries a list of two-level ISO country\n * codes (ISO 3166-1 alpha-2) whose laws and policies may\n * affect this relay. `EU` may be used for European Union\n * countries.\n\n * Remember that a relay may be hosted in a country which is\n * not the country of the legal entities who own the relay,\n * so it's very likely a number of countries are involved.\n */\nexport interface ContentLimitations {\n  relay_countries: string[]\n}\n\n/**\n * ### Community Preferences\n\n * For public text notes at least, a relay may try to foster\n * a local community. This would encourage users to follow\n * the global feed on that relay, in addition to their usual\n * individual follows. To support this goal, relays MAY\n * specify some of the following values.\n\n * @param language_tags  is an ordered list of [IETF\n * language\n * tags](https://en.wikipedia.org/wiki/IETF_language_tag\n * indicating the major languages spoken on the relay.\n * @param tags is a list of limitations on the topics to be\n * discussed. For example `sfw-only` indicates that only\n * \"Safe For Work\" content is encouraged on this relay. This\n * relies on assumptions of what the \"work\" \"community\"\n * feels \"safe\" talking about. In time, a common set of tags\n * may emerge that allow users to find relays that suit\n * their needs, and client software will be able to parse\n * these tags easily. The `bitcoin-only` tag indicates that\n * any _altcoin_, _\"crypto\"_ or _blockchain_ comments will\n * be ridiculed without mercy.\n * @param posting_policy is a link to a human-readable page\n * which specifies the community policies for the relay. In\n * cases where `sfw-only` is True, it's important to link to\n * a page which gets into the specifics of your posting\n * policy.\n\n * The `description` field should be used to describe your\n * community goals and values, in brief. The\n * `posting_policy` is for additional detail and legal\n * terms. Use the `tags` field to signify limitations on\n * content, or topics to be discussed, which could be\n * machine processed by appropriate client software.\n */\nexport interface CommunityPreferences {\n  language_tags: string[]\n  tags: string[]\n  posting_policy: string\n}\n\nexport interface Amount {\n  amount: number\n  unit: 'msat'\n}\nexport interface PublicationAmount extends Amount {\n  kinds: number[]\n}\nexport interface Subscription extends Amount {\n  period: number\n}\nexport interface Fees {\n  admission: Amount[]\n  subscription: Subscription[]\n  publication: PublicationAmount[]\n}\n/**\n * Relays that require payments may want to expose their fee\n * schedules.\n */\nexport interface PayToRelay {\n  payments_url: string\n  fees: Fees\n}\n\n/**\n * A URL pointing to an image to be used as an icon for the\n * relay. Recommended to be squared in shape.\n */\nexport interface Icon {\n  icon: string\n}\n\nexport type RelayInformation = BasicRelayInformation &\n  Partial<Retention> & {\n    limitation?: Partial<Limitations>\n  } & Partial<ContentLimitations> &\n  Partial<CommunityPreferences> &\n  Partial<PayToRelay> &\n  Partial<Icon>\n", "import { bytesToHex, concatBytes, hexToBytes } from '@noble/hashes/utils.js'\nimport { bech32 } from '@scure/base'\n\nimport { utf8Decoder, utf8Encoder } from './utils.ts'\n\nexport type NProfile = `nprofile1${string}`\nexport type NEvent = `nevent1${string}`\nexport type NAddr = `naddr1${string}`\nexport type NSec = `nsec1${string}`\nexport type NPub = `npub1${string}`\nexport type Note = `note1${string}`\nexport type Ncryptsec = `ncryptsec1${string}`\n\nexport const NostrTypeGuard = {\n  isNProfile: (value?: string | null): value is NProfile => /^nprofile1[a-z\\d]+$/.test(value || ''),\n  isNEvent: (value?: string | null): value is NEvent => /^nevent1[a-z\\d]+$/.test(value || ''),\n  isNAddr: (value?: string | null): value is NAddr => /^naddr1[a-z\\d]+$/.test(value || ''),\n  isNSec: (value?: string | null): value is NSec => /^nsec1[a-z\\d]{58}$/.test(value || ''),\n  isNPub: (value?: string | null): value is NPub => /^npub1[a-z\\d]{58}$/.test(value || ''),\n  isNote: (value?: string | null): value is Note => /^note1[a-z\\d]+$/.test(value || ''),\n  isNcryptsec: (value?: string | null): value is Ncryptsec => /^ncryptsec1[a-z\\d]+$/.test(value || ''),\n}\n\nexport const Bech32MaxSize = 5000\n\n/**\n * Bech32 regex.\n * @see https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki#bech32\n */\nexport const BECH32_REGEX = /[\\x21-\\x7E]{1,83}1[023456789acdefghjklmnpqrstuvwxyz]{6,}/\n\nfunction integerToUint8Array(number: number) {\n  // Create a Uint8Array with enough space to hold a 32-bit integer (4 bytes).\n  const uint8Array = new Uint8Array(4)\n\n  // Use bitwise operations to extract the bytes.\n  uint8Array[0] = (number >> 24) & 0xff // Most significant byte (MSB)\n  uint8Array[1] = (number >> 16) & 0xff\n  uint8Array[2] = (number >> 8) & 0xff\n  uint8Array[3] = number & 0xff // Least significant byte (LSB)\n\n  return uint8Array\n}\n\nexport type ProfilePointer = {\n  pubkey: string // hex\n  relays?: string[]\n}\n\nexport type EventPointer = {\n  id: string // hex\n  relays?: string[]\n  author?: string\n  kind?: number\n}\n\nexport type AddressPointer = {\n  identifier: string\n  pubkey: string\n  kind: number\n  relays?: string[]\n}\n\nexport function decodeNostrURI(nip19code: string): ReturnType<typeof decode> | { type: 'invalid'; data: null } {\n  try {\n    if (nip19code.startsWith('nostr:')) nip19code = nip19code.substring(6)\n    return decode(nip19code)\n  } catch (_err) {\n    return { type: 'invalid', data: null }\n  }\n}\n\nexport type DecodedNevent = {\n  type: 'nevent'\n  data: EventPointer\n}\n\nexport type DecodedNprofile = {\n  type: 'nprofile'\n  data: ProfilePointer\n}\n\nexport type DecodedNaddr = {\n  type: 'naddr'\n  data: AddressPointer\n}\n\nexport type DecodedNsec = {\n  type: 'nsec'\n  data: Uint8Array\n}\n\nexport type DecodedNpub = {\n  type: 'npub'\n  data: string\n}\n\nexport type DecodedNote = {\n  type: 'note'\n  data: string\n}\n\nexport type DecodedResult = DecodedNevent | DecodedNprofile | DecodedNaddr | DecodedNpub | DecodedNsec | DecodedNote\n\nexport function decode(nip19: NEvent): DecodedNevent\nexport function decode(nip19: NProfile): DecodedNprofile\nexport function decode(nip19: NAddr): DecodedNaddr\nexport function decode(nip19: NSec): DecodedNsec\nexport function decode(nip19: NPub): DecodedNpub\nexport function decode(nip19: Note): DecodedNote\nexport function decode(code: string): DecodedResult\nexport function decode(code: string): DecodedResult {\n  let { prefix, words } = bech32.decode(code as `${string}1${string}`, Bech32MaxSize)\n  let data = new Uint8Array(bech32.fromWords(words))\n\n  switch (prefix) {\n    case 'nprofile': {\n      let tlv = parseTLV(data)\n      if (!tlv[0]?.[0]) throw new Error('missing TLV 0 for nprofile')\n      if (tlv[0][0].length !== 32) throw new Error('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      let tlv = parseTLV(data)\n      if (!tlv[0]?.[0]) throw new Error('missing TLV 0 for nevent')\n      if (tlv[0][0].length !== 32) throw new Error('TLV 0 should be 32 bytes')\n      if (tlv[2] && tlv[2][0].length !== 32) throw new Error('TLV 2 should be 32 bytes')\n      if (tlv[3] && tlv[3][0].length !== 4) throw new Error('TLV 3 should be 4 bytes')\n\n      return {\n        type: 'nevent',\n        data: {\n          id: bytesToHex(tlv[0][0]),\n          relays: tlv[1] ? tlv[1].map(d => utf8Decoder.decode(d)) : [],\n          author: tlv[2]?.[0] ? bytesToHex(tlv[2][0]) : undefined,\n          kind: tlv[3]?.[0] ? parseInt(bytesToHex(tlv[3][0]), 16) : undefined,\n        },\n      }\n    }\n\n    case 'naddr': {\n      let tlv = parseTLV(data)\n      if (!tlv[0]?.[0]) throw new Error('missing TLV 0 for naddr')\n      if (!tlv[2]?.[0]) throw new Error('missing TLV 2 for naddr')\n      if (tlv[2][0].length !== 32) throw new Error('TLV 2 should be 32 bytes')\n      if (!tlv[3]?.[0]) throw new Error('missing TLV 3 for naddr')\n      if (tlv[3][0].length !== 4) throw new Error('TLV 3 should be 4 bytes')\n\n      return {\n        type: 'naddr',\n        data: {\n          identifier: utf8Decoder.decode(tlv[0][0]),\n          pubkey: bytesToHex(tlv[2][0]),\n          kind: parseInt(bytesToHex(tlv[3][0]), 16),\n          relays: tlv[1] ? tlv[1].map(d => utf8Decoder.decode(d)) : [],\n        },\n      }\n    }\n\n    case 'nsec':\n      return { type: prefix, data }\n\n    case 'npub':\n    case 'note':\n      return { type: prefix, data: bytesToHex(data) }\n\n    default:\n      throw new Error(`unknown prefix ${prefix}`)\n  }\n}\n\ntype TLV = { [t: number]: Uint8Array[] }\n\nfunction parseTLV(data: Uint8Array): TLV {\n  let result: TLV = {}\n  let rest = data\n  while (rest.length > 0) {\n    let t = rest[0]\n    let l = rest[1]\n    let v = rest.slice(2, 2 + l)\n    rest = rest.slice(2 + l)\n    if (v.length < l) throw new Error(`not enough data to read on TLV ${t}`)\n    result[t] = result[t] || []\n    result[t].push(v)\n  }\n  return result\n}\n\nexport function nsecEncode(key: Uint8Array): NSec {\n  return encodeBytes('nsec', key)\n}\n\nexport function npubEncode(hex: string): NPub {\n  return encodeBytes('npub', hexToBytes(hex))\n}\n\nexport function noteEncode(hex: string): Note {\n  return encodeBytes('note', hexToBytes(hex))\n}\n\nfunction encodeBech32<Prefix extends string>(prefix: Prefix, data: Uint8Array): `${Prefix}1${string}` {\n  let words = bech32.toWords(data)\n  return bech32.encode(prefix, words, Bech32MaxSize) as `${Prefix}1${string}`\n}\n\nexport function encodeBytes<Prefix extends string>(prefix: Prefix, bytes: Uint8Array): `${Prefix}1${string}` {\n  return encodeBech32(prefix, bytes)\n}\n\nexport function nprofileEncode(profile: ProfilePointer): NProfile {\n  let data = encodeTLV({\n    0: [hexToBytes(profile.pubkey)],\n    1: (profile.relays || []).map(url => utf8Encoder.encode(url)),\n  })\n  return encodeBech32('nprofile', data)\n}\n\nexport function neventEncode(event: EventPointer): NEvent {\n  let kindArray\n  if (event.kind !== undefined) {\n    kindArray = integerToUint8Array(event.kind)\n  }\n\n  let data = encodeTLV({\n    0: [hexToBytes(event.id)],\n    1: (event.relays || []).map(url => utf8Encoder.encode(url)),\n    2: event.author ? [hexToBytes(event.author)] : [],\n    3: kindArray ? [new Uint8Array(kindArray)] : [],\n  })\n\n  return encodeBech32('nevent', data)\n}\n\nexport function naddrEncode(addr: AddressPointer): NAddr {\n  let kind = new ArrayBuffer(4)\n  new DataView(kind).setUint32(0, addr.kind, false)\n\n  let data = encodeTLV({\n    0: [utf8Encoder.encode(addr.identifier)],\n    1: (addr.relays || []).map(url => utf8Encoder.encode(url)),\n    2: [hexToBytes(addr.pubkey)],\n    3: [new Uint8Array(kind)],\n  })\n  return encodeBech32('naddr', data)\n}\n\nfunction encodeTLV(tlv: TLV): Uint8Array {\n  let entries: Uint8Array[] = []\n\n  Object.entries(tlv)\n    .reverse()\n    .forEach(([t, vs]) => {\n      vs.forEach(v => {\n        let entry = new Uint8Array(v.length + 2)\n        entry.set([parseInt(t)], 0)\n        entry.set([v.length], 1)\n        entry.set(v, 2)\n        entries.push(entry)\n      })\n    })\n\n  return concatBytes(...entries)\n}\n", "import type { Event } from './core.ts'\n\nexport const utf8Decoder: TextDecoder = new TextDecoder('utf-8')\nexport const utf8Encoder: TextEncoder = new TextEncoder()\n\nexport { bytesToHex, hexToBytes } from '@noble/hashes/utils.js'\n\nexport function normalizeURL(url: string): string {\n  try {\n    if (url.indexOf('://') === -1) url = 'wss://' + url\n    let p = new URL(url)\n    if (p.protocol === 'http:') p.protocol = 'ws:'\n    else if (p.protocol === 'https:') p.protocol = 'wss:'\n    p.pathname = p.pathname.replace(/\\/+/g, '/')\n    if (p.pathname.endsWith('/')) p.pathname = p.pathname.slice(0, -1)\n    if ((p.port === '80' && p.protocol === 'ws:') || (p.port === '443' && p.protocol === 'wss:')) p.port = ''\n    p.searchParams.sort()\n    p.hash = ''\n    return p.toString()\n  } catch (e) {\n    throw new Error(`Invalid URL: ${url}`)\n  }\n}\n\nexport function insertEventIntoDescendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return b.created_at - event.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function insertEventIntoAscendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return event.created_at - b.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function binarySearch<T>(arr: T[], compare: (b: T) => number): [number, boolean] {\n  let start = 0\n  let end = arr.length - 1\n\n  while (start <= end) {\n    const mid = Math.floor((start + end) / 2)\n    const cmp = compare(arr[mid])\n\n    if (cmp === 0) {\n      return [mid, true]\n    }\n\n    if (cmp < 0) {\n      end = mid - 1\n    } else {\n      start = mid + 1\n    }\n  }\n\n  return [start, false]\n}\n\nexport class QueueNode<V> {\n  public value: V\n  public next: QueueNode<V> | null = null\n  public prev: QueueNode<V> | null = null\n\n  constructor(message: V) {\n    this.value = message\n  }\n}\n\nexport class Queue<V> {\n  public first: QueueNode<V> | null\n  public last: QueueNode<V> | null\n\n  constructor() {\n    this.first = null\n    this.last = null\n  }\n\n  enqueue(value: V): boolean {\n    const newNode = new QueueNode(value)\n    if (!this.last) {\n      // list is empty\n      this.first = newNode\n      this.last = newNode\n    } else if (this.last === this.first) {\n      // list has a single element\n      this.last = newNode\n      this.last.prev = this.first\n      this.first.next = newNode\n    } else {\n      // list has elements, add as last\n      newNode.prev = this.last\n      this.last.next = newNode\n      this.last = newNode\n    }\n    return true\n  }\n\n  dequeue(): V | null {\n    if (!this.first) return null\n\n    if (this.first === this.last) {\n      const target = this.first\n      this.first = null\n      this.last = null\n      return target.value\n    }\n\n    const target = this.first\n    this.first = target.next\n    if (this.first) {\n      this.first.prev = null // fix: clean up prev pointer\n    }\n\n    return target.value\n  }\n}\n"],
+  "mappings": "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",
   "names": ["import_utils", "GroupAdminPermission"]
 }
Index: package/lib/esm/nip29.js.map
===================================================================
--- package/lib/esm/nip29.js.map
+++ package/lib/esm/nip29.js.map
@@ -1,7 +1,7 @@
 {
   "version": 3,
   "sources": ["../../nip11.ts", "../../nip19.ts", "../../utils.ts", "../../nip29.ts"],
-  "sourcesContent": ["var _fetch: any\n\ntry {\n  _fetch = fetch\n} catch {}\n\nexport function useFetchImplementation(fetchImplementation: any): void {\n  _fetch = fetchImplementation\n}\n\nexport async function fetchRelayInformation(url: string): Promise<RelayInformation> {\n  return (await (\n    await fetch(url.replace('ws://', 'http://').replace('wss://', 'https://'), {\n      headers: { Accept: 'application/nostr+json' },\n    })\n  ).json()) as RelayInformation\n}\n\n/**\n * ## Relay Information Document\n\n * Relays may provide server metadata to clients to inform\n * them of capabilities, administrative contacts, and\n * various server attributes. This is made available as a\n * JSON document over HTTP, on the same URI as the relay's\n * websocket.\n\n * Any field may be omitted, and clients MUST ignore any\n * additional fields they do not understand. Relays MUST\n * accept CORS requests by sending\n * `Access-Control-Allow-Origin`,\n * `Access-Control-Allow-Headers`, and\n * `Access-Control-Allow-Methods` headers.\n * @param name string identifying relay\n * @param description string with detailed information\n * @param pubkey administrative contact pubkey\n * @param contact: administrative alternate contact\n * @param supported_nips a list of NIP numbers supported by\n * the relay\n * @param software identifying relay software URL\n * @param version string version identifier\n */\nexport interface BasicRelayInformation {\n  // string identifying relay\n  name: string\n  description: string\n  pubkey: string\n  contact: string\n  supported_nips: number[]\n  software: string\n  version: string\n  // limitation?: Limitations<A, P>\n}\n\n/**\n *  * ## Extra Fields\n\n *  * ### Server Limitations\n\n * These are limitations imposed by the relay on clients.\n * Your client should expect that requests which exceed\n * these practical_ limitations are rejected or fail immediately.\n * @param max_message_length this is the maximum number of\n * bytes for incoming JSON that the relay will attempt to\n * decode and act upon. When you send large subscriptions,\n * you will be limited by this value. It also effectively\n * limits the maximum size of any event. Value is calculated\n * from `[` to `]` and is after UTF-8 serialization (so some\n * unicode characters will cost 2-3 bytes). It is equal to\n * the maximum size of the WebSocket message frame.\n * @param max_subscriptions total number of subscriptions\n * that may be active on a single websocket connection to\n * this relay. It's possible that authenticated clients with\n * a (paid) relationship to the relay may have higher limits.\n * @param max_filters maximum number of filter values in\n * each subscription. Must be one or higher.\n * @param max_limit the relay server will clamp each\n * filter's `limit` value to this number.\n * This means the client won't be able to get more than this\n * number of events from a single subscription filter. This\n * clamping is typically done silently by the relay, but\n * with this number, you can know that there are additional\n * results if you narrowed your filter's time range or other\n * parameters.\n * @param max_subid_length maximum length of subscription id as a\n * string.\n * @param min_prefix for `authors` and `ids` filters which\n * are to match against a hex prefix, you must provide at\n * least this many hex digits in the prefix.\n * @param max_event_tags in any event, this is the maximum\n * number of elements in the `tags` list.\n * @param max_content_length maximum number of characters in\n * the `content` field of any event. This is a count of\n * unicode characters. After serializing into JSON it may be\n * larger (in bytes), and is still subject to the\n * max_message_length`, if defined.\n * @param min_pow_difficulty new events will require at\n * least this difficulty of PoW, based on [NIP-13](13.md),\n * or they will be rejected by this server.\n * @param auth_required this relay requires [NIP-42](42.md)\n * authentication to happen before a new connection may\n * perform any other action. Even if set to False,\n * authentication may be required for specific actions.\n * @param restricted_writes: this relay requires some kind\n * of condition to be fulfilled in order to accept events\n * (not necessarily, but including\n * @param payment_required this relay requires payment\n * before a new connection may perform any action.\n * @param created_at_lower_limit: 'created_at' lower limit\n * @param created_at_upper_limit: 'created_at' upper limit\n */\nexport interface Limitations {\n  max_message_length: number\n  max_subscriptions: number\n  max_filters: number\n  max_limit: number\n  max_subid_length: number\n  min_prefix: number\n  max_event_tags: number\n  max_content_length: number\n  min_pow_difficulty: number\n  auth_required: boolean\n  payment_required: boolean\n  created_at_lower_limit: number\n  created_at_upper_limit: number\n  restricted_writes: boolean\n}\n\nexport interface RetentionDetails {\n  kinds: (number | number[])[]\n  time?: number | null\n  count?: number | null\n}\ntype AnyRetentionDetails = RetentionDetails\n/**\n * ### Event Retention\n\n * There may be a cost associated with storing data forever,\n * so relays may wish to state retention times. The values\n * stated here are defaults for unauthenticated users and\n * visitors. Paid users would likely have other policies.\n\n * Retention times are given in seconds, with `null`\n * indicating infinity. If zero is provided, this means the\n * event will not be stored at all, and preferably an error\n * will be provided when those are received.\n * ```json\n{\n...\n  \"retention\": [\n    { \"kinds\": [0, 1, [5, 7], [40, 49]], \"time\": 3600 },\n    { \"kinds\": [[40000, 49999]], \"time\": 100 },\n    { \"kinds\": [[30000, 39999]], \"count\": 1000 },\n    { \"time\": 3600, \"count\": 10000 }\n  ]\n...\n}\n```\n * @param retention is a list of specifications: each will\n * apply to either all kinds, or a subset of kinds. Ranges\n * may be specified for the kind field as a tuple of\n * inclusive start and end values. Events of indicated kind\n * (or all) are then limited to a `count` and/or time\n * period.\n\n * It is possible to effectively blacklist Nostr-based\n * protocols that rely on a specific `kind` number, by\n * giving a retention time of zero for those `kind` values.\n * While that is unfortunate, it does allow clients to\n * discover servers that will support their protocol quickly\n * via a single HTTP fetch.\n\n * There is no need to specify retention times for\n * _ephemeral events_ as defined in [NIP-16](16.md) since\n * they are not retained.\n */\nexport interface Retention {\n  retention: AnyRetentionDetails[]\n}\n\n/**\n * Some relays may be governed by the arbitrary laws of a\n * nation state. This may limit what content can be stored\n * in cleartext on those relays. All clients are encouraged\n * to use encryption to work around this limitation.\n\n * It is not possible to describe the limitations of each\n * country's laws and policies which themselves are\n * typically vague and constantly shifting.\n\n * Therefore, this field allows the relay operator to\n * indicate which countries' laws might end up being\n * enforced on them, and then indirectly on their users'\n * content.\n\n * Users should be able to avoid relays in countries they\n * don't like, and/or select relays in more favourable\n * zones. Exposing this flexibility is up to the client\n * software.\n\n * @param relay_countries a list of two-level ISO country\n * codes (ISO 3166-1 alpha-2) whose laws and policies may\n * affect this relay. `EU` may be used for European Union\n * countries.\n\n * Remember that a relay may be hosted in a country which is\n * not the country of the legal entities who own the relay,\n * so it's very likely a number of countries are involved.\n */\nexport interface ContentLimitations {\n  relay_countries: string[]\n}\n\n/**\n * ### Community Preferences\n\n * For public text notes at least, a relay may try to foster\n * a local community. This would encourage users to follow\n * the global feed on that relay, in addition to their usual\n * individual follows. To support this goal, relays MAY\n * specify some of the following values.\n\n * @param language_tags  is an ordered list of [IETF\n * language\n * tags](https://en.wikipedia.org/wiki/IETF_language_tag\n * indicating the major languages spoken on the relay.\n * @param tags is a list of limitations on the topics to be\n * discussed. For example `sfw-only` indicates that only\n * \"Safe For Work\" content is encouraged on this relay. This\n * relies on assumptions of what the \"work\" \"community\"\n * feels \"safe\" talking about. In time, a common set of tags\n * may emerge that allow users to find relays that suit\n * their needs, and client software will be able to parse\n * these tags easily. The `bitcoin-only` tag indicates that\n * any _altcoin_, _\"crypto\"_ or _blockchain_ comments will\n * be ridiculed without mercy.\n * @param posting_policy is a link to a human-readable page\n * which specifies the community policies for the relay. In\n * cases where `sfw-only` is True, it's important to link to\n * a page which gets into the specifics of your posting\n * policy.\n\n * The `description` field should be used to describe your\n * community goals and values, in brief. The\n * `posting_policy` is for additional detail and legal\n * terms. Use the `tags` field to signify limitations on\n * content, or topics to be discussed, which could be\n * machine processed by appropriate client software.\n */\nexport interface CommunityPreferences {\n  language_tags: string[]\n  tags: string[]\n  posting_policy: string\n}\n\nexport interface Amount {\n  amount: number\n  unit: 'msat'\n}\nexport interface PublicationAmount extends Amount {\n  kinds: number[]\n}\nexport interface Subscription extends Amount {\n  period: number\n}\nexport interface Fees {\n  admission: Amount[]\n  subscription: Subscription[]\n  publication: PublicationAmount[]\n}\n/**\n * Relays that require payments may want to expose their fee\n * schedules.\n */\nexport interface PayToRelay {\n  payments_url: string\n  fees: Fees\n}\n\n/**\n * A URL pointing to an image to be used as an icon for the\n * relay. Recommended to be squared in shape.\n */\nexport interface Icon {\n  icon: string\n}\n\nexport type RelayInformation = BasicRelayInformation &\n  Partial<Retention> & {\n    limitation?: Partial<Limitations>\n  } & Partial<ContentLimitations> &\n  Partial<CommunityPreferences> &\n  Partial<PayToRelay> &\n  Partial<Icon>\n", "import { bytesToHex, concatBytes, hexToBytes } from '@noble/hashes/utils'\nimport { bech32 } from '@scure/base'\n\nimport { utf8Decoder, utf8Encoder } from './utils.ts'\n\nexport type NProfile = `nprofile1${string}`\nexport type NEvent = `nevent1${string}`\nexport type NAddr = `naddr1${string}`\nexport type NSec = `nsec1${string}`\nexport type NPub = `npub1${string}`\nexport type Note = `note1${string}`\nexport type Ncryptsec = `ncryptsec1${string}`\n\nexport const NostrTypeGuard = {\n  isNProfile: (value?: string | null): value is NProfile => /^nprofile1[a-z\\d]+$/.test(value || ''),\n  isNEvent: (value?: string | null): value is NEvent => /^nevent1[a-z\\d]+$/.test(value || ''),\n  isNAddr: (value?: string | null): value is NAddr => /^naddr1[a-z\\d]+$/.test(value || ''),\n  isNSec: (value?: string | null): value is NSec => /^nsec1[a-z\\d]{58}$/.test(value || ''),\n  isNPub: (value?: string | null): value is NPub => /^npub1[a-z\\d]{58}$/.test(value || ''),\n  isNote: (value?: string | null): value is Note => /^note1[a-z\\d]+$/.test(value || ''),\n  isNcryptsec: (value?: string | null): value is Ncryptsec => /^ncryptsec1[a-z\\d]+$/.test(value || ''),\n}\n\nexport const Bech32MaxSize = 5000\n\n/**\n * Bech32 regex.\n * @see https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki#bech32\n */\nexport const BECH32_REGEX = /[\\x21-\\x7E]{1,83}1[023456789acdefghjklmnpqrstuvwxyz]{6,}/\n\nfunction integerToUint8Array(number: number) {\n  // Create a Uint8Array with enough space to hold a 32-bit integer (4 bytes).\n  const uint8Array = new Uint8Array(4)\n\n  // Use bitwise operations to extract the bytes.\n  uint8Array[0] = (number >> 24) & 0xff // Most significant byte (MSB)\n  uint8Array[1] = (number >> 16) & 0xff\n  uint8Array[2] = (number >> 8) & 0xff\n  uint8Array[3] = number & 0xff // Least significant byte (LSB)\n\n  return uint8Array\n}\n\nexport type ProfilePointer = {\n  pubkey: string // hex\n  relays?: string[]\n}\n\nexport type EventPointer = {\n  id: string // hex\n  relays?: string[]\n  author?: string\n  kind?: number\n}\n\nexport type AddressPointer = {\n  identifier: string\n  pubkey: string\n  kind: number\n  relays?: string[]\n}\n\nexport function decodeNostrURI(nip19code: string): ReturnType<typeof decode> | { type: 'invalid'; data: null } {\n  try {\n    if (nip19code.startsWith('nostr:')) nip19code = nip19code.substring(6)\n    return decode(nip19code)\n  } catch (_err) {\n    return { type: 'invalid', data: null }\n  }\n}\n\nexport type DecodedNevent = {\n  type: 'nevent'\n  data: EventPointer\n}\n\nexport type DecodedNprofile = {\n  type: 'nprofile'\n  data: ProfilePointer\n}\n\nexport type DecodedNaddr = {\n  type: 'naddr'\n  data: AddressPointer\n}\n\nexport type DecodedNsec = {\n  type: 'nsec'\n  data: Uint8Array\n}\n\nexport type DecodedNpub = {\n  type: 'npub'\n  data: string\n}\n\nexport type DecodedNote = {\n  type: 'note'\n  data: string\n}\n\nexport type DecodedResult = DecodedNevent | DecodedNprofile | DecodedNaddr | DecodedNpub | DecodedNsec | DecodedNote\n\nexport function decode(nip19: NEvent): DecodedNevent\nexport function decode(nip19: NProfile): DecodedNprofile\nexport function decode(nip19: NAddr): DecodedNaddr\nexport function decode(nip19: NSec): DecodedNsec\nexport function decode(nip19: NPub): DecodedNpub\nexport function decode(nip19: Note): DecodedNote\nexport function decode(code: string): DecodedResult\nexport function decode(code: string): DecodedResult {\n  let { prefix, words } = bech32.decode(code, Bech32MaxSize)\n  let data = new Uint8Array(bech32.fromWords(words))\n\n  switch (prefix) {\n    case 'nprofile': {\n      let tlv = parseTLV(data)\n      if (!tlv[0]?.[0]) throw new Error('missing TLV 0 for nprofile')\n      if (tlv[0][0].length !== 32) throw new Error('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      let tlv = parseTLV(data)\n      if (!tlv[0]?.[0]) throw new Error('missing TLV 0 for nevent')\n      if (tlv[0][0].length !== 32) throw new Error('TLV 0 should be 32 bytes')\n      if (tlv[2] && tlv[2][0].length !== 32) throw new Error('TLV 2 should be 32 bytes')\n      if (tlv[3] && tlv[3][0].length !== 4) throw new Error('TLV 3 should be 4 bytes')\n\n      return {\n        type: 'nevent',\n        data: {\n          id: bytesToHex(tlv[0][0]),\n          relays: tlv[1] ? tlv[1].map(d => utf8Decoder.decode(d)) : [],\n          author: tlv[2]?.[0] ? bytesToHex(tlv[2][0]) : undefined,\n          kind: tlv[3]?.[0] ? parseInt(bytesToHex(tlv[3][0]), 16) : undefined,\n        },\n      }\n    }\n\n    case 'naddr': {\n      let tlv = parseTLV(data)\n      if (!tlv[0]?.[0]) throw new Error('missing TLV 0 for naddr')\n      if (!tlv[2]?.[0]) throw new Error('missing TLV 2 for naddr')\n      if (tlv[2][0].length !== 32) throw new Error('TLV 2 should be 32 bytes')\n      if (!tlv[3]?.[0]) throw new Error('missing TLV 3 for naddr')\n      if (tlv[3][0].length !== 4) throw new Error('TLV 3 should be 4 bytes')\n\n      return {\n        type: 'naddr',\n        data: {\n          identifier: utf8Decoder.decode(tlv[0][0]),\n          pubkey: bytesToHex(tlv[2][0]),\n          kind: parseInt(bytesToHex(tlv[3][0]), 16),\n          relays: tlv[1] ? tlv[1].map(d => utf8Decoder.decode(d)) : [],\n        },\n      }\n    }\n\n    case 'nsec':\n      return { type: prefix, data }\n\n    case 'npub':\n    case 'note':\n      return { type: prefix, data: bytesToHex(data) }\n\n    default:\n      throw new Error(`unknown prefix ${prefix}`)\n  }\n}\n\ntype TLV = { [t: number]: Uint8Array[] }\n\nfunction parseTLV(data: Uint8Array): TLV {\n  let result: TLV = {}\n  let rest = data\n  while (rest.length > 0) {\n    let t = rest[0]\n    let l = rest[1]\n    let v = rest.slice(2, 2 + l)\n    rest = rest.slice(2 + l)\n    if (v.length < l) throw new Error(`not enough data to read on TLV ${t}`)\n    result[t] = result[t] || []\n    result[t].push(v)\n  }\n  return result\n}\n\nexport function nsecEncode(key: Uint8Array): NSec {\n  return encodeBytes('nsec', key)\n}\n\nexport function npubEncode(hex: string): NPub {\n  return encodeBytes('npub', hexToBytes(hex))\n}\n\nexport function noteEncode(hex: string): Note {\n  return encodeBytes('note', hexToBytes(hex))\n}\n\nfunction encodeBech32<Prefix extends string>(prefix: Prefix, data: Uint8Array): `${Prefix}1${string}` {\n  let words = bech32.toWords(data)\n  return bech32.encode(prefix, words, Bech32MaxSize) as `${Prefix}1${string}`\n}\n\nexport function encodeBytes<Prefix extends string>(prefix: Prefix, bytes: Uint8Array): `${Prefix}1${string}` {\n  return encodeBech32(prefix, bytes)\n}\n\nexport function nprofileEncode(profile: ProfilePointer): NProfile {\n  let data = encodeTLV({\n    0: [hexToBytes(profile.pubkey)],\n    1: (profile.relays || []).map(url => utf8Encoder.encode(url)),\n  })\n  return encodeBech32('nprofile', data)\n}\n\nexport function neventEncode(event: EventPointer): NEvent {\n  let kindArray\n  if (event.kind !== undefined) {\n    kindArray = integerToUint8Array(event.kind)\n  }\n\n  let data = encodeTLV({\n    0: [hexToBytes(event.id)],\n    1: (event.relays || []).map(url => utf8Encoder.encode(url)),\n    2: event.author ? [hexToBytes(event.author)] : [],\n    3: kindArray ? [new Uint8Array(kindArray)] : [],\n  })\n\n  return encodeBech32('nevent', data)\n}\n\nexport function naddrEncode(addr: AddressPointer): NAddr {\n  let kind = new ArrayBuffer(4)\n  new DataView(kind).setUint32(0, addr.kind, false)\n\n  let data = encodeTLV({\n    0: [utf8Encoder.encode(addr.identifier)],\n    1: (addr.relays || []).map(url => utf8Encoder.encode(url)),\n    2: [hexToBytes(addr.pubkey)],\n    3: [new Uint8Array(kind)],\n  })\n  return encodeBech32('naddr', data)\n}\n\nfunction encodeTLV(tlv: TLV): Uint8Array {\n  let entries: Uint8Array[] = []\n\n  Object.entries(tlv)\n    .reverse()\n    .forEach(([t, vs]) => {\n      vs.forEach(v => {\n        let entry = new Uint8Array(v.length + 2)\n        entry.set([parseInt(t)], 0)\n        entry.set([v.length], 1)\n        entry.set(v, 2)\n        entries.push(entry)\n      })\n    })\n\n  return concatBytes(...entries)\n}\n", "import type { Event } from './core.ts'\n\nexport const utf8Decoder: TextDecoder = new TextDecoder('utf-8')\nexport const utf8Encoder: TextEncoder = new TextEncoder()\n\nexport { bytesToHex, hexToBytes } from '@noble/hashes/utils'\n\nexport function normalizeURL(url: string): string {\n  try {\n    if (url.indexOf('://') === -1) url = 'wss://' + url\n    let p = new URL(url)\n    if (p.protocol === 'http:') p.protocol = 'ws:'\n    else if (p.protocol === 'https:') p.protocol = 'wss:'\n    p.pathname = p.pathname.replace(/\\/+/g, '/')\n    if (p.pathname.endsWith('/')) p.pathname = p.pathname.slice(0, -1)\n    if ((p.port === '80' && p.protocol === 'ws:') || (p.port === '443' && p.protocol === 'wss:')) p.port = ''\n    p.searchParams.sort()\n    p.hash = ''\n    return p.toString()\n  } catch (e) {\n    throw new Error(`Invalid URL: ${url}`)\n  }\n}\n\nexport function insertEventIntoDescendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return b.created_at - event.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function insertEventIntoAscendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return event.created_at - b.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function binarySearch<T>(arr: T[], compare: (b: T) => number): [number, boolean] {\n  let start = 0\n  let end = arr.length - 1\n\n  while (start <= end) {\n    const mid = Math.floor((start + end) / 2)\n    const cmp = compare(arr[mid])\n\n    if (cmp === 0) {\n      return [mid, true]\n    }\n\n    if (cmp < 0) {\n      end = mid - 1\n    } else {\n      start = mid + 1\n    }\n  }\n\n  return [start, false]\n}\n\nexport class QueueNode<V> {\n  public value: V\n  public next: QueueNode<V> | null = null\n  public prev: QueueNode<V> | null = null\n\n  constructor(message: V) {\n    this.value = message\n  }\n}\n\nexport class Queue<V> {\n  public first: QueueNode<V> | null\n  public last: QueueNode<V> | null\n\n  constructor() {\n    this.first = null\n    this.last = null\n  }\n\n  enqueue(value: V): boolean {\n    const newNode = new QueueNode(value)\n    if (!this.last) {\n      // list is empty\n      this.first = newNode\n      this.last = newNode\n    } else if (this.last === this.first) {\n      // list has a single element\n      this.last = newNode\n      this.last.prev = this.first\n      this.first.next = newNode\n    } else {\n      // list has elements, add as last\n      newNode.prev = this.last\n      this.last.next = newNode\n      this.last = newNode\n    }\n    return true\n  }\n\n  dequeue(): V | null {\n    if (!this.first) return null\n\n    if (this.first === this.last) {\n      const target = this.first\n      this.first = null\n      this.last = null\n      return target.value\n    }\n\n    const target = this.first\n    this.first = target.next\n    if (this.first) {\n      this.first.prev = null // fix: clean up prev pointer\n    }\n\n    return target.value\n  }\n}\n", "import { AbstractSimplePool } from './abstract-pool.ts'\nimport { Subscription } from './abstract-relay.ts'\nimport type { Event, EventTemplate } from './core.ts'\nimport { fetchRelayInformation, RelayInformation } from './nip11.ts'\nimport { decode, NostrTypeGuard } from './nip19.ts'\nimport { normalizeURL } from './utils.ts'\n\n/**\n * Represents a NIP29 group.\n */\nexport type Group = {\n  relay: string\n  metadata: GroupMetadata\n  admins?: GroupAdmin[]\n  members?: GroupMember[]\n  reference: GroupReference\n}\n\n/**\n * Represents the metadata for a NIP29 group.\n */\nexport type GroupMetadata = {\n  id: string\n  pubkey: string\n  name?: string\n  picture?: string\n  about?: string\n  isPublic?: boolean\n  isOpen?: boolean\n}\n\n/**\n * Represents a NIP29 group reference.\n */\nexport type GroupReference = {\n  id: string\n  host: string\n}\n\n/**\n * Represents a NIP29 group member.\n */\nexport type GroupMember = {\n  pubkey: string\n  label?: string\n}\n\n/**\n * Represents a NIP29 group admin.\n */\nexport type GroupAdmin = {\n  pubkey: string\n  label?: string\n  permissions: GroupAdminPermission[]\n}\n\n/**\n * Represents the permissions that a NIP29 group admin can have.\n */\nexport enum GroupAdminPermission {\n  /** @deprecated use PutUser instead */\n  AddUser = 'add-user',\n  EditMetadata = 'edit-metadata',\n  DeleteEvent = 'delete-event',\n  RemoveUser = 'remove-user',\n  /** @deprecated removed from NIP */\n  AddPermission = 'add-permission',\n  /** @deprecated removed from NIP */\n  RemovePermission = 'remove-permission',\n  /** @deprecated removed from NIP */\n  EditGroupStatus = 'edit-group-status',\n  PutUser = 'put-user',\n  CreateGroup = 'create-group',\n  DeleteGroup = 'delete-group',\n  CreateInvite = 'create-invite',\n}\n\n/**\n * Generates a group metadata event template.\n *\n * @param group - The group object.\n * @returns An event template with the generated group metadata that can be signed later.\n */\nexport function generateGroupMetadataEventTemplate(group: Group): EventTemplate {\n  const tags: string[][] = [['d', group.metadata.id]]\n  group.metadata.name && tags.push(['name', group.metadata.name])\n  group.metadata.picture && tags.push(['picture', group.metadata.picture])\n  group.metadata.about && tags.push(['about', group.metadata.about])\n  group.metadata.isPublic && tags.push(['public'])\n  group.metadata.isOpen && tags.push(['open'])\n\n  return {\n    content: '',\n    created_at: Math.floor(Date.now() / 1000),\n    kind: 39000,\n    tags,\n  }\n}\n\n/**\n * Validates a group metadata event.\n *\n * @param event - The event to validate.\n * @returns A boolean indicating whether the event is valid.\n */\nexport function validateGroupMetadataEvent(event: Event): boolean {\n  if (event.kind !== 39000) return false\n\n  if (!event.pubkey) return false\n\n  const requiredTags = ['d'] as const\n  for (const tag of requiredTags) {\n    if (!event.tags.find(([t]) => t == tag)) return false\n  }\n\n  return true\n}\n\n/**\n * Generates an event template for group admins.\n *\n * @param group - The group object.\n * @param admins - An array of group admins.\n * @returns The generated event template with the group admins that can be signed later.\n */\nexport function generateGroupAdminsEventTemplate(group: Group, admins: GroupAdmin[]): EventTemplate {\n  const tags: string[][] = [['d', group.metadata.id]]\n  for (const admin of admins) {\n    tags.push(['p', admin.pubkey, admin.label || '', ...admin.permissions])\n  }\n\n  return {\n    content: '',\n    created_at: Math.floor(Date.now() / 1000),\n    kind: 39001,\n    tags,\n  }\n}\n\n/**\n * Validates a group admins event.\n *\n * @param event - The event to validate.\n * @returns True if the event is valid, false otherwise.\n */\nexport function validateGroupAdminsEvent(event: Event): boolean {\n  if (event.kind !== 39001) return false\n\n  const requiredTags = ['d'] as const\n  for (const tag of requiredTags) {\n    if (!event.tags.find(([t]) => t == tag)) return false\n  }\n\n  // validate permissions\n  for (const [tag, _value, _label, ...permissions] of event.tags) {\n    if (tag !== 'p') continue\n\n    for (let i = 0; i < permissions.length; i += 1) {\n      if (typeof permissions[i] !== 'string') return false\n\n      // validate permission name from the GroupAdminPermission enum\n      if (!Object.values(GroupAdminPermission).includes(permissions[i] as GroupAdminPermission)) return false\n    }\n  }\n\n  return true\n}\n\n/**\n * Generates an event template for a group with its members.\n *\n * @param group - The group object.\n * @param members - An array of group members.\n * @returns The generated event template with the group members that can be signed later.\n */\nexport function generateGroupMembersEventTemplate(group: Group, members: GroupMember[]): EventTemplate {\n  const tags: string[][] = [['d', group.metadata.id]]\n  for (const member of members) {\n    tags.push(['p', member.pubkey, member.label || ''])\n  }\n\n  return {\n    content: '',\n    created_at: Math.floor(Date.now() / 1000),\n    kind: 39002,\n    tags,\n  }\n}\n\n/**\n * Validates a group members event.\n *\n * @param event - The event to validate.\n * @returns Returns `true` if the event is a valid group members event, `false` otherwise.\n */\nexport function validateGroupMembersEvent(event: Event): boolean {\n  if (event.kind !== 39002) return false\n\n  const requiredTags = ['d'] as const\n  for (const tag of requiredTags) {\n    if (!event.tags.find(([t]) => t == tag)) return false\n  }\n\n  return true\n}\n\n/**\n * Returns the normalized relay URL based on the provided group reference.\n *\n * @param groupReference - The group reference object containing the host.\n * @returns The normalized relay URL.\n */\nexport function getNormalizedRelayURLByGroupReference(groupReference: GroupReference): string {\n  return normalizeURL(groupReference.host)\n}\n\n/**\n * Fetches relay information by group reference.\n *\n * @param groupReference The group reference.\n * @returns A promise that resolves to the relay information.\n */\nexport async function fetchRelayInformationByGroupReference(groupReference: GroupReference): Promise<RelayInformation> {\n  const normalizedRelayURL = getNormalizedRelayURLByGroupReference(groupReference)\n\n  return fetchRelayInformation(normalizedRelayURL)\n}\n\n/**\n * Fetches the group metadata event from the specified pool.\n * If the normalizedRelayURL is not provided, it will be obtained using the groupReference.\n * If the relayInformation is not provided, it will be fetched using the normalizedRelayURL.\n *\n * @param {Object} options - The options object.\n * @param {AbstractSimplePool} options.pool - The pool to fetch the group metadata event from.\n * @param {GroupReference} options.groupReference - The reference to the group.\n * @param {string} [options.normalizedRelayURL] - The normalized URL of the relay.\n * @param {RelayInformation} [options.relayInformation] - The relay information object.\n * @returns {Promise<Event>} The group metadata event that can be parsed later to get the group metadata object.\n * @throws {Error} If the group is not found on the specified relay.\n */\nexport async function fetchGroupMetadataEvent({\n  pool,\n  groupReference,\n  relayInformation,\n  normalizedRelayURL,\n}: {\n  pool: AbstractSimplePool\n  groupReference: GroupReference\n  normalizedRelayURL?: string\n  relayInformation?: RelayInformation\n}): Promise<Event> {\n  if (!normalizedRelayURL) {\n    normalizedRelayURL = getNormalizedRelayURLByGroupReference(groupReference)\n  }\n\n  if (!relayInformation) {\n    relayInformation = await fetchRelayInformation(normalizedRelayURL)\n  }\n\n  const groupMetadataEvent = await pool.get([normalizedRelayURL], {\n    kinds: [39000],\n    authors: [relayInformation.pubkey],\n    '#d': [groupReference.id],\n  })\n\n  if (!groupMetadataEvent) throw new Error(`group '${groupReference.id}' not found on ${normalizedRelayURL}`)\n\n  return groupMetadataEvent\n}\n\n/**\n * Parses a group metadata event and returns the corresponding GroupMetadata object.\n *\n * @param event - The event to parse.\n * @returns The parsed GroupMetadata object.\n * @throws An error if the group metadata event is invalid.\n */\nexport function parseGroupMetadataEvent(event: Event): GroupMetadata {\n  if (!validateGroupMetadataEvent(event)) throw new Error('invalid group metadata event')\n\n  const metadata: GroupMetadata = {\n    id: '',\n    pubkey: event.pubkey,\n  }\n\n  for (const [tag, value] of event.tags) {\n    switch (tag) {\n      case 'd':\n        metadata.id = value\n        break\n      case 'name':\n        metadata.name = value\n        break\n      case 'picture':\n        metadata.picture = value\n        break\n      case 'about':\n        metadata.about = value\n        break\n      case 'public':\n        metadata.isPublic = true\n        break\n      case 'open':\n        metadata.isOpen = true\n        break\n    }\n  }\n\n  return metadata\n}\n\n/**\n * Fetches the group admins event from the specified pool.\n * If the normalizedRelayURL is not provided, it will be obtained from the groupReference.\n * If the relayInformation is not provided, it will be fetched using the normalizedRelayURL.\n *\n * @param {Object} options - The options object.\n * @param {AbstractSimplePool} options.pool - The pool to fetch the group admins event from.\n * @param {GroupReference} options.groupReference - The reference to the group.\n * @param {string} [options.normalizedRelayURL] - The normalized relay URL.\n * @param {RelayInformation} [options.relayInformation] - The relay information.\n * @returns {Promise<Event>} The group admins event that can be parsed later to get the group admins object.\n * @throws {Error} If the group admins event is not found on the specified relay.\n */\nexport async function fetchGroupAdminsEvent({\n  pool,\n  groupReference,\n  relayInformation,\n  normalizedRelayURL,\n}: {\n  pool: AbstractSimplePool\n  groupReference: GroupReference\n  normalizedRelayURL?: string\n  relayInformation?: RelayInformation\n}): Promise<Event> {\n  if (!normalizedRelayURL) {\n    normalizedRelayURL = getNormalizedRelayURLByGroupReference(groupReference)\n  }\n\n  if (!relayInformation) {\n    relayInformation = await fetchRelayInformation(normalizedRelayURL)\n  }\n\n  const groupAdminsEvent = await pool.get([normalizedRelayURL], {\n    kinds: [39001],\n    authors: [relayInformation.pubkey],\n    '#d': [groupReference.id],\n  })\n\n  if (!groupAdminsEvent) throw new Error(`admins for group '${groupReference.id}' not found on ${normalizedRelayURL}`)\n\n  return groupAdminsEvent\n}\n\n/**\n * Parses a group admins event and returns an array of GroupAdmin objects.\n *\n * @param event - The event to parse.\n * @returns An array of GroupAdmin objects.\n * @throws Throws an error if the group admins event is invalid.\n */\nexport function parseGroupAdminsEvent(event: Event): GroupAdmin[] {\n  if (!validateGroupAdminsEvent(event)) throw new Error('invalid group admins event')\n\n  const admins: GroupAdmin[] = []\n\n  for (const [tag, value, label, ...permissions] of event.tags) {\n    if (tag !== 'p') continue\n\n    admins.push({\n      pubkey: value,\n      label,\n      permissions: permissions as GroupAdminPermission[],\n    })\n  }\n\n  return admins\n}\n\n/**\n * Fetches the group members event from the specified relay.\n * If the normalizedRelayURL is not provided, it will be obtained using the groupReference.\n * If the relayInformation is not provided, it will be fetched using the normalizedRelayURL.\n *\n * @param {Object} options - The options object.\n * @param {AbstractSimplePool} options.pool - The pool object.\n * @param {GroupReference} options.groupReference - The group reference object.\n * @param {string} [options.normalizedRelayURL] - The normalized relay URL.\n * @param {RelayInformation} [options.relayInformation] - The relay information object.\n * @returns {Promise<Event>} The group members event that can be parsed later to get the group members object.\n * @throws {Error} If the group members event is not found.\n */\nexport async function fetchGroupMembersEvent({\n  pool,\n  groupReference,\n  relayInformation,\n  normalizedRelayURL,\n}: {\n  pool: AbstractSimplePool\n  groupReference: GroupReference\n  normalizedRelayURL?: string\n  relayInformation?: RelayInformation\n}): Promise<Event> {\n  if (!normalizedRelayURL) {\n    normalizedRelayURL = getNormalizedRelayURLByGroupReference(groupReference)\n  }\n\n  if (!relayInformation) {\n    relayInformation = await fetchRelayInformation(normalizedRelayURL)\n  }\n\n  const groupMembersEvent = await pool.get([normalizedRelayURL], {\n    kinds: [39002],\n    authors: [relayInformation.pubkey],\n    '#d': [groupReference.id],\n  })\n\n  if (!groupMembersEvent) throw new Error(`members for group '${groupReference.id}' not found on ${normalizedRelayURL}`)\n\n  return groupMembersEvent\n}\n\n/**\n * Parses a group members event and returns an array of GroupMember objects.\n * @param event - The event to parse.\n * @returns An array of GroupMember objects.\n * @throws Throws an error if the group members event is invalid.\n */\nexport function parseGroupMembersEvent(event: Event): GroupMember[] {\n  if (!validateGroupMembersEvent(event)) throw new Error('invalid group members event')\n\n  const members: GroupMember[] = []\n\n  for (const [tag, value, label] of event.tags) {\n    if (tag !== 'p') continue\n\n    members.push({\n      pubkey: value,\n      label,\n    })\n  }\n\n  return members\n}\n\n/**\n * Fetches and parses the group metadata event, group admins event, and group members event from the specified pool.\n * If the normalized relay URL is not provided, it will be obtained using the group reference.\n * If the relay information is not provided, it will be fetched using the normalized relay URL.\n *\n * @param {Object} options - The options for loading the group.\n * @param {AbstractSimplePool} options.pool - The pool to load the group from.\n * @param {GroupReference} options.groupReference - The reference of the group to load.\n * @param {string} [options.normalizedRelayURL] - The normalized URL of the relay to use.\n * @param {RelayInformation} [options.relayInformation] - The relay information to use.\n * @returns {Promise<Group>} A promise that resolves to the loaded group.\n */\nexport async function loadGroup({\n  pool,\n  groupReference,\n  normalizedRelayURL,\n  relayInformation,\n}: {\n  pool: AbstractSimplePool\n  groupReference: GroupReference\n  normalizedRelayURL?: string\n  relayInformation?: RelayInformation\n}): Promise<Group> {\n  if (!normalizedRelayURL) {\n    normalizedRelayURL = getNormalizedRelayURLByGroupReference(groupReference)\n  }\n\n  if (!relayInformation) {\n    relayInformation = await fetchRelayInformation(normalizedRelayURL)\n  }\n\n  const metadataEvent = await fetchGroupMetadataEvent({ pool, groupReference, normalizedRelayURL, relayInformation })\n  const metadata = parseGroupMetadataEvent(metadataEvent)\n\n  const adminsEvent = await fetchGroupAdminsEvent({ pool, groupReference, normalizedRelayURL, relayInformation })\n  const admins = parseGroupAdminsEvent(adminsEvent)\n\n  const membersEvent = await fetchGroupMembersEvent({ pool, groupReference, normalizedRelayURL, relayInformation })\n  const members = parseGroupMembersEvent(membersEvent)\n\n  const group: Group = {\n    relay: normalizedRelayURL,\n    metadata,\n    admins,\n    members,\n    reference: groupReference,\n  }\n\n  return group\n}\n\n/**\n * Loads a group from the specified pool using the provided group code.\n *\n * @param {AbstractSimplePool} pool - The pool to load the group from.\n * @param {string} code - The code representing the group.\n * @returns {Promise<Group>} - A promise that resolves to the loaded group.\n * @throws {Error} - If the group code is invalid.\n */\nexport async function loadGroupFromCode(pool: AbstractSimplePool, code: string): Promise<Group> {\n  const groupReference = parseGroupCode(code)\n\n  if (!groupReference) throw new Error('invalid group code')\n\n  return loadGroup({ pool, groupReference })\n}\n\n/**\n * Parses a group code and returns a GroupReference object.\n *\n * @param code The group code to parse.\n * @returns A GroupReference object if the code is valid, otherwise null.\n */\nexport function parseGroupCode(code: string): null | GroupReference {\n  if (NostrTypeGuard.isNAddr(code)) {\n    try {\n      let { data } = decode(code)\n\n      let { relays, identifier } = data\n      if (!relays || relays.length === 0) return null\n\n      let host = relays![0]\n      if (host.startsWith('wss://')) {\n        host = host.slice(6)\n      }\n      return { host, id: identifier }\n    } catch (err) {\n      return null\n    }\n  } else if (code.split(\"'\").length === 2) {\n    let spl = code.split(\"'\")\n    return { host: spl[0], id: spl[1] }\n  }\n\n  return null\n}\n\n/**\n * Encodes a group reference into a string.\n *\n * @param gr - The group reference to encode.\n * @returns The encoded group reference as a string.\n */\nexport function encodeGroupReference(gr: GroupReference): string {\n  const { host, id } = gr\n  const normalizedHost = host.replace(/^(https?:\\/\\/|wss?:\\/\\/)/, '')\n\n  return `${normalizedHost}'${id}`\n}\n\n/**\n * Subscribes to relay groups metadata events and calls the provided event handler function\n * when an event is received.\n *\n * @param {Object} options - The options for subscribing to relay groups metadata events.\n * @param {AbstractSimplePool} options.pool - The pool to subscribe to.\n * @param {string} options.relayURL - The URL of the relay.\n * @param {Function} options.onError - The error handler function.\n * @param {Function} options.onEvent - The event handler function.\n * @param {Function} [options.onConnect] - The connect handler function.\n * @returns {Function} - A function to close the subscription\n */\nexport function subscribeRelayGroupsMetadataEvents({\n  pool,\n  relayURL,\n  onError,\n  onEvent,\n  onConnect,\n}: {\n  pool: AbstractSimplePool\n  relayURL: string\n  onError: (err: Error) => void\n  onEvent: (event: Event) => void\n  onConnect?: () => void\n}): () => void {\n  let sub: Subscription\n\n  const normalizedRelayURL = normalizeURL(relayURL)\n\n  fetchRelayInformation(normalizedRelayURL)\n    .then(async info => {\n      const abstractedRelay = await pool.ensureRelay(normalizedRelayURL)\n\n      onConnect?.()\n\n      sub = abstractedRelay.prepareSubscription(\n        [\n          {\n            kinds: [39000],\n            limit: 50,\n            authors: [info.pubkey],\n          },\n        ],\n        {\n          onevent(event: Event) {\n            onEvent(event)\n          },\n        },\n      )\n    })\n    .catch(err => {\n      sub.close()\n\n      onError(err)\n    })\n\n  return () => sub.close()\n}\n"],
-  "mappings": "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",
+  "sourcesContent": ["var _fetch: any\n\ntry {\n  _fetch = fetch\n} catch {}\n\nexport function useFetchImplementation(fetchImplementation: any): void {\n  _fetch = fetchImplementation\n}\n\nexport async function fetchRelayInformation(url: string): Promise<RelayInformation> {\n  return (await (\n    await fetch(url.replace('ws://', 'http://').replace('wss://', 'https://'), {\n      headers: { Accept: 'application/nostr+json' },\n    })\n  ).json()) as RelayInformation\n}\n\n/**\n * ## Relay Information Document\n\n * Relays may provide server metadata to clients to inform\n * them of capabilities, administrative contacts, and\n * various server attributes. This is made available as a\n * JSON document over HTTP, on the same URI as the relay's\n * websocket.\n\n * Any field may be omitted, and clients MUST ignore any\n * additional fields they do not understand. Relays MUST\n * accept CORS requests by sending\n * `Access-Control-Allow-Origin`,\n * `Access-Control-Allow-Headers`, and\n * `Access-Control-Allow-Methods` headers.\n * @param name string identifying relay\n * @param description string with detailed information\n * @param pubkey administrative contact pubkey\n * @param contact: administrative alternate contact\n * @param supported_nips a list of NIP numbers supported by\n * the relay\n * @param software identifying relay software URL\n * @param version string version identifier\n */\nexport interface BasicRelayInformation {\n  // string identifying relay\n  name: string\n  description: string\n  pubkey: string\n  contact: string\n  supported_nips: number[]\n  software: string\n  version: string\n  // limitation?: Limitations<A, P>\n}\n\n/**\n *  * ## Extra Fields\n\n *  * ### Server Limitations\n\n * These are limitations imposed by the relay on clients.\n * Your client should expect that requests which exceed\n * these practical_ limitations are rejected or fail immediately.\n * @param max_message_length this is the maximum number of\n * bytes for incoming JSON that the relay will attempt to\n * decode and act upon. When you send large subscriptions,\n * you will be limited by this value. It also effectively\n * limits the maximum size of any event. Value is calculated\n * from `[` to `]` and is after UTF-8 serialization (so some\n * unicode characters will cost 2-3 bytes). It is equal to\n * the maximum size of the WebSocket message frame.\n * @param max_subscriptions total number of subscriptions\n * that may be active on a single websocket connection to\n * this relay. It's possible that authenticated clients with\n * a (paid) relationship to the relay may have higher limits.\n * @param max_filters maximum number of filter values in\n * each subscription. Must be one or higher.\n * @param max_limit the relay server will clamp each\n * filter's `limit` value to this number.\n * This means the client won't be able to get more than this\n * number of events from a single subscription filter. This\n * clamping is typically done silently by the relay, but\n * with this number, you can know that there are additional\n * results if you narrowed your filter's time range or other\n * parameters.\n * @param max_subid_length maximum length of subscription id as a\n * string.\n * @param min_prefix for `authors` and `ids` filters which\n * are to match against a hex prefix, you must provide at\n * least this many hex digits in the prefix.\n * @param max_event_tags in any event, this is the maximum\n * number of elements in the `tags` list.\n * @param max_content_length maximum number of characters in\n * the `content` field of any event. This is a count of\n * unicode characters. After serializing into JSON it may be\n * larger (in bytes), and is still subject to the\n * max_message_length`, if defined.\n * @param min_pow_difficulty new events will require at\n * least this difficulty of PoW, based on [NIP-13](13.md),\n * or they will be rejected by this server.\n * @param auth_required this relay requires [NIP-42](42.md)\n * authentication to happen before a new connection may\n * perform any other action. Even if set to False,\n * authentication may be required for specific actions.\n * @param restricted_writes: this relay requires some kind\n * of condition to be fulfilled in order to accept events\n * (not necessarily, but including\n * @param payment_required this relay requires payment\n * before a new connection may perform any action.\n * @param created_at_lower_limit: 'created_at' lower limit\n * @param created_at_upper_limit: 'created_at' upper limit\n */\nexport interface Limitations {\n  max_message_length: number\n  max_subscriptions: number\n  max_filters: number\n  max_limit: number\n  max_subid_length: number\n  min_prefix: number\n  max_event_tags: number\n  max_content_length: number\n  min_pow_difficulty: number\n  auth_required: boolean\n  payment_required: boolean\n  created_at_lower_limit: number\n  created_at_upper_limit: number\n  restricted_writes: boolean\n}\n\nexport interface RetentionDetails {\n  kinds: (number | number[])[]\n  time?: number | null\n  count?: number | null\n}\ntype AnyRetentionDetails = RetentionDetails\n/**\n * ### Event Retention\n\n * There may be a cost associated with storing data forever,\n * so relays may wish to state retention times. The values\n * stated here are defaults for unauthenticated users and\n * visitors. Paid users would likely have other policies.\n\n * Retention times are given in seconds, with `null`\n * indicating infinity. If zero is provided, this means the\n * event will not be stored at all, and preferably an error\n * will be provided when those are received.\n * ```json\n{\n...\n  \"retention\": [\n    { \"kinds\": [0, 1, [5, 7], [40, 49]], \"time\": 3600 },\n    { \"kinds\": [[40000, 49999]], \"time\": 100 },\n    { \"kinds\": [[30000, 39999]], \"count\": 1000 },\n    { \"time\": 3600, \"count\": 10000 }\n  ]\n...\n}\n```\n * @param retention is a list of specifications: each will\n * apply to either all kinds, or a subset of kinds. Ranges\n * may be specified for the kind field as a tuple of\n * inclusive start and end values. Events of indicated kind\n * (or all) are then limited to a `count` and/or time\n * period.\n\n * It is possible to effectively blacklist Nostr-based\n * protocols that rely on a specific `kind` number, by\n * giving a retention time of zero for those `kind` values.\n * While that is unfortunate, it does allow clients to\n * discover servers that will support their protocol quickly\n * via a single HTTP fetch.\n\n * There is no need to specify retention times for\n * _ephemeral events_ as defined in [NIP-16](16.md) since\n * they are not retained.\n */\nexport interface Retention {\n  retention: AnyRetentionDetails[]\n}\n\n/**\n * Some relays may be governed by the arbitrary laws of a\n * nation state. This may limit what content can be stored\n * in cleartext on those relays. All clients are encouraged\n * to use encryption to work around this limitation.\n\n * It is not possible to describe the limitations of each\n * country's laws and policies which themselves are\n * typically vague and constantly shifting.\n\n * Therefore, this field allows the relay operator to\n * indicate which countries' laws might end up being\n * enforced on them, and then indirectly on their users'\n * content.\n\n * Users should be able to avoid relays in countries they\n * don't like, and/or select relays in more favourable\n * zones. Exposing this flexibility is up to the client\n * software.\n\n * @param relay_countries a list of two-level ISO country\n * codes (ISO 3166-1 alpha-2) whose laws and policies may\n * affect this relay. `EU` may be used for European Union\n * countries.\n\n * Remember that a relay may be hosted in a country which is\n * not the country of the legal entities who own the relay,\n * so it's very likely a number of countries are involved.\n */\nexport interface ContentLimitations {\n  relay_countries: string[]\n}\n\n/**\n * ### Community Preferences\n\n * For public text notes at least, a relay may try to foster\n * a local community. This would encourage users to follow\n * the global feed on that relay, in addition to their usual\n * individual follows. To support this goal, relays MAY\n * specify some of the following values.\n\n * @param language_tags  is an ordered list of [IETF\n * language\n * tags](https://en.wikipedia.org/wiki/IETF_language_tag\n * indicating the major languages spoken on the relay.\n * @param tags is a list of limitations on the topics to be\n * discussed. For example `sfw-only` indicates that only\n * \"Safe For Work\" content is encouraged on this relay. This\n * relies on assumptions of what the \"work\" \"community\"\n * feels \"safe\" talking about. In time, a common set of tags\n * may emerge that allow users to find relays that suit\n * their needs, and client software will be able to parse\n * these tags easily. The `bitcoin-only` tag indicates that\n * any _altcoin_, _\"crypto\"_ or _blockchain_ comments will\n * be ridiculed without mercy.\n * @param posting_policy is a link to a human-readable page\n * which specifies the community policies for the relay. In\n * cases where `sfw-only` is True, it's important to link to\n * a page which gets into the specifics of your posting\n * policy.\n\n * The `description` field should be used to describe your\n * community goals and values, in brief. The\n * `posting_policy` is for additional detail and legal\n * terms. Use the `tags` field to signify limitations on\n * content, or topics to be discussed, which could be\n * machine processed by appropriate client software.\n */\nexport interface CommunityPreferences {\n  language_tags: string[]\n  tags: string[]\n  posting_policy: string\n}\n\nexport interface Amount {\n  amount: number\n  unit: 'msat'\n}\nexport interface PublicationAmount extends Amount {\n  kinds: number[]\n}\nexport interface Subscription extends Amount {\n  period: number\n}\nexport interface Fees {\n  admission: Amount[]\n  subscription: Subscription[]\n  publication: PublicationAmount[]\n}\n/**\n * Relays that require payments may want to expose their fee\n * schedules.\n */\nexport interface PayToRelay {\n  payments_url: string\n  fees: Fees\n}\n\n/**\n * A URL pointing to an image to be used as an icon for the\n * relay. Recommended to be squared in shape.\n */\nexport interface Icon {\n  icon: string\n}\n\nexport type RelayInformation = BasicRelayInformation &\n  Partial<Retention> & {\n    limitation?: Partial<Limitations>\n  } & Partial<ContentLimitations> &\n  Partial<CommunityPreferences> &\n  Partial<PayToRelay> &\n  Partial<Icon>\n", "import { bytesToHex, concatBytes, hexToBytes } from '@noble/hashes/utils.js'\nimport { bech32 } from '@scure/base'\n\nimport { utf8Decoder, utf8Encoder } from './utils.ts'\n\nexport type NProfile = `nprofile1${string}`\nexport type NEvent = `nevent1${string}`\nexport type NAddr = `naddr1${string}`\nexport type NSec = `nsec1${string}`\nexport type NPub = `npub1${string}`\nexport type Note = `note1${string}`\nexport type Ncryptsec = `ncryptsec1${string}`\n\nexport const NostrTypeGuard = {\n  isNProfile: (value?: string | null): value is NProfile => /^nprofile1[a-z\\d]+$/.test(value || ''),\n  isNEvent: (value?: string | null): value is NEvent => /^nevent1[a-z\\d]+$/.test(value || ''),\n  isNAddr: (value?: string | null): value is NAddr => /^naddr1[a-z\\d]+$/.test(value || ''),\n  isNSec: (value?: string | null): value is NSec => /^nsec1[a-z\\d]{58}$/.test(value || ''),\n  isNPub: (value?: string | null): value is NPub => /^npub1[a-z\\d]{58}$/.test(value || ''),\n  isNote: (value?: string | null): value is Note => /^note1[a-z\\d]+$/.test(value || ''),\n  isNcryptsec: (value?: string | null): value is Ncryptsec => /^ncryptsec1[a-z\\d]+$/.test(value || ''),\n}\n\nexport const Bech32MaxSize = 5000\n\n/**\n * Bech32 regex.\n * @see https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki#bech32\n */\nexport const BECH32_REGEX = /[\\x21-\\x7E]{1,83}1[023456789acdefghjklmnpqrstuvwxyz]{6,}/\n\nfunction integerToUint8Array(number: number) {\n  // Create a Uint8Array with enough space to hold a 32-bit integer (4 bytes).\n  const uint8Array = new Uint8Array(4)\n\n  // Use bitwise operations to extract the bytes.\n  uint8Array[0] = (number >> 24) & 0xff // Most significant byte (MSB)\n  uint8Array[1] = (number >> 16) & 0xff\n  uint8Array[2] = (number >> 8) & 0xff\n  uint8Array[3] = number & 0xff // Least significant byte (LSB)\n\n  return uint8Array\n}\n\nexport type ProfilePointer = {\n  pubkey: string // hex\n  relays?: string[]\n}\n\nexport type EventPointer = {\n  id: string // hex\n  relays?: string[]\n  author?: string\n  kind?: number\n}\n\nexport type AddressPointer = {\n  identifier: string\n  pubkey: string\n  kind: number\n  relays?: string[]\n}\n\nexport function decodeNostrURI(nip19code: string): ReturnType<typeof decode> | { type: 'invalid'; data: null } {\n  try {\n    if (nip19code.startsWith('nostr:')) nip19code = nip19code.substring(6)\n    return decode(nip19code)\n  } catch (_err) {\n    return { type: 'invalid', data: null }\n  }\n}\n\nexport type DecodedNevent = {\n  type: 'nevent'\n  data: EventPointer\n}\n\nexport type DecodedNprofile = {\n  type: 'nprofile'\n  data: ProfilePointer\n}\n\nexport type DecodedNaddr = {\n  type: 'naddr'\n  data: AddressPointer\n}\n\nexport type DecodedNsec = {\n  type: 'nsec'\n  data: Uint8Array\n}\n\nexport type DecodedNpub = {\n  type: 'npub'\n  data: string\n}\n\nexport type DecodedNote = {\n  type: 'note'\n  data: string\n}\n\nexport type DecodedResult = DecodedNevent | DecodedNprofile | DecodedNaddr | DecodedNpub | DecodedNsec | DecodedNote\n\nexport function decode(nip19: NEvent): DecodedNevent\nexport function decode(nip19: NProfile): DecodedNprofile\nexport function decode(nip19: NAddr): DecodedNaddr\nexport function decode(nip19: NSec): DecodedNsec\nexport function decode(nip19: NPub): DecodedNpub\nexport function decode(nip19: Note): DecodedNote\nexport function decode(code: string): DecodedResult\nexport function decode(code: string): DecodedResult {\n  let { prefix, words } = bech32.decode(code as `${string}1${string}`, Bech32MaxSize)\n  let data = new Uint8Array(bech32.fromWords(words))\n\n  switch (prefix) {\n    case 'nprofile': {\n      let tlv = parseTLV(data)\n      if (!tlv[0]?.[0]) throw new Error('missing TLV 0 for nprofile')\n      if (tlv[0][0].length !== 32) throw new Error('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      let tlv = parseTLV(data)\n      if (!tlv[0]?.[0]) throw new Error('missing TLV 0 for nevent')\n      if (tlv[0][0].length !== 32) throw new Error('TLV 0 should be 32 bytes')\n      if (tlv[2] && tlv[2][0].length !== 32) throw new Error('TLV 2 should be 32 bytes')\n      if (tlv[3] && tlv[3][0].length !== 4) throw new Error('TLV 3 should be 4 bytes')\n\n      return {\n        type: 'nevent',\n        data: {\n          id: bytesToHex(tlv[0][0]),\n          relays: tlv[1] ? tlv[1].map(d => utf8Decoder.decode(d)) : [],\n          author: tlv[2]?.[0] ? bytesToHex(tlv[2][0]) : undefined,\n          kind: tlv[3]?.[0] ? parseInt(bytesToHex(tlv[3][0]), 16) : undefined,\n        },\n      }\n    }\n\n    case 'naddr': {\n      let tlv = parseTLV(data)\n      if (!tlv[0]?.[0]) throw new Error('missing TLV 0 for naddr')\n      if (!tlv[2]?.[0]) throw new Error('missing TLV 2 for naddr')\n      if (tlv[2][0].length !== 32) throw new Error('TLV 2 should be 32 bytes')\n      if (!tlv[3]?.[0]) throw new Error('missing TLV 3 for naddr')\n      if (tlv[3][0].length !== 4) throw new Error('TLV 3 should be 4 bytes')\n\n      return {\n        type: 'naddr',\n        data: {\n          identifier: utf8Decoder.decode(tlv[0][0]),\n          pubkey: bytesToHex(tlv[2][0]),\n          kind: parseInt(bytesToHex(tlv[3][0]), 16),\n          relays: tlv[1] ? tlv[1].map(d => utf8Decoder.decode(d)) : [],\n        },\n      }\n    }\n\n    case 'nsec':\n      return { type: prefix, data }\n\n    case 'npub':\n    case 'note':\n      return { type: prefix, data: bytesToHex(data) }\n\n    default:\n      throw new Error(`unknown prefix ${prefix}`)\n  }\n}\n\ntype TLV = { [t: number]: Uint8Array[] }\n\nfunction parseTLV(data: Uint8Array): TLV {\n  let result: TLV = {}\n  let rest = data\n  while (rest.length > 0) {\n    let t = rest[0]\n    let l = rest[1]\n    let v = rest.slice(2, 2 + l)\n    rest = rest.slice(2 + l)\n    if (v.length < l) throw new Error(`not enough data to read on TLV ${t}`)\n    result[t] = result[t] || []\n    result[t].push(v)\n  }\n  return result\n}\n\nexport function nsecEncode(key: Uint8Array): NSec {\n  return encodeBytes('nsec', key)\n}\n\nexport function npubEncode(hex: string): NPub {\n  return encodeBytes('npub', hexToBytes(hex))\n}\n\nexport function noteEncode(hex: string): Note {\n  return encodeBytes('note', hexToBytes(hex))\n}\n\nfunction encodeBech32<Prefix extends string>(prefix: Prefix, data: Uint8Array): `${Prefix}1${string}` {\n  let words = bech32.toWords(data)\n  return bech32.encode(prefix, words, Bech32MaxSize) as `${Prefix}1${string}`\n}\n\nexport function encodeBytes<Prefix extends string>(prefix: Prefix, bytes: Uint8Array): `${Prefix}1${string}` {\n  return encodeBech32(prefix, bytes)\n}\n\nexport function nprofileEncode(profile: ProfilePointer): NProfile {\n  let data = encodeTLV({\n    0: [hexToBytes(profile.pubkey)],\n    1: (profile.relays || []).map(url => utf8Encoder.encode(url)),\n  })\n  return encodeBech32('nprofile', data)\n}\n\nexport function neventEncode(event: EventPointer): NEvent {\n  let kindArray\n  if (event.kind !== undefined) {\n    kindArray = integerToUint8Array(event.kind)\n  }\n\n  let data = encodeTLV({\n    0: [hexToBytes(event.id)],\n    1: (event.relays || []).map(url => utf8Encoder.encode(url)),\n    2: event.author ? [hexToBytes(event.author)] : [],\n    3: kindArray ? [new Uint8Array(kindArray)] : [],\n  })\n\n  return encodeBech32('nevent', data)\n}\n\nexport function naddrEncode(addr: AddressPointer): NAddr {\n  let kind = new ArrayBuffer(4)\n  new DataView(kind).setUint32(0, addr.kind, false)\n\n  let data = encodeTLV({\n    0: [utf8Encoder.encode(addr.identifier)],\n    1: (addr.relays || []).map(url => utf8Encoder.encode(url)),\n    2: [hexToBytes(addr.pubkey)],\n    3: [new Uint8Array(kind)],\n  })\n  return encodeBech32('naddr', data)\n}\n\nfunction encodeTLV(tlv: TLV): Uint8Array {\n  let entries: Uint8Array[] = []\n\n  Object.entries(tlv)\n    .reverse()\n    .forEach(([t, vs]) => {\n      vs.forEach(v => {\n        let entry = new Uint8Array(v.length + 2)\n        entry.set([parseInt(t)], 0)\n        entry.set([v.length], 1)\n        entry.set(v, 2)\n        entries.push(entry)\n      })\n    })\n\n  return concatBytes(...entries)\n}\n", "import type { Event } from './core.ts'\n\nexport const utf8Decoder: TextDecoder = new TextDecoder('utf-8')\nexport const utf8Encoder: TextEncoder = new TextEncoder()\n\nexport { bytesToHex, hexToBytes } from '@noble/hashes/utils.js'\n\nexport function normalizeURL(url: string): string {\n  try {\n    if (url.indexOf('://') === -1) url = 'wss://' + url\n    let p = new URL(url)\n    if (p.protocol === 'http:') p.protocol = 'ws:'\n    else if (p.protocol === 'https:') p.protocol = 'wss:'\n    p.pathname = p.pathname.replace(/\\/+/g, '/')\n    if (p.pathname.endsWith('/')) p.pathname = p.pathname.slice(0, -1)\n    if ((p.port === '80' && p.protocol === 'ws:') || (p.port === '443' && p.protocol === 'wss:')) p.port = ''\n    p.searchParams.sort()\n    p.hash = ''\n    return p.toString()\n  } catch (e) {\n    throw new Error(`Invalid URL: ${url}`)\n  }\n}\n\nexport function insertEventIntoDescendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return b.created_at - event.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function insertEventIntoAscendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return event.created_at - b.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function binarySearch<T>(arr: T[], compare: (b: T) => number): [number, boolean] {\n  let start = 0\n  let end = arr.length - 1\n\n  while (start <= end) {\n    const mid = Math.floor((start + end) / 2)\n    const cmp = compare(arr[mid])\n\n    if (cmp === 0) {\n      return [mid, true]\n    }\n\n    if (cmp < 0) {\n      end = mid - 1\n    } else {\n      start = mid + 1\n    }\n  }\n\n  return [start, false]\n}\n\nexport class QueueNode<V> {\n  public value: V\n  public next: QueueNode<V> | null = null\n  public prev: QueueNode<V> | null = null\n\n  constructor(message: V) {\n    this.value = message\n  }\n}\n\nexport class Queue<V> {\n  public first: QueueNode<V> | null\n  public last: QueueNode<V> | null\n\n  constructor() {\n    this.first = null\n    this.last = null\n  }\n\n  enqueue(value: V): boolean {\n    const newNode = new QueueNode(value)\n    if (!this.last) {\n      // list is empty\n      this.first = newNode\n      this.last = newNode\n    } else if (this.last === this.first) {\n      // list has a single element\n      this.last = newNode\n      this.last.prev = this.first\n      this.first.next = newNode\n    } else {\n      // list has elements, add as last\n      newNode.prev = this.last\n      this.last.next = newNode\n      this.last = newNode\n    }\n    return true\n  }\n\n  dequeue(): V | null {\n    if (!this.first) return null\n\n    if (this.first === this.last) {\n      const target = this.first\n      this.first = null\n      this.last = null\n      return target.value\n    }\n\n    const target = this.first\n    this.first = target.next\n    if (this.first) {\n      this.first.prev = null // fix: clean up prev pointer\n    }\n\n    return target.value\n  }\n}\n", "import { AbstractSimplePool } from './abstract-pool.ts'\nimport { Subscription } from './abstract-relay.ts'\nimport type { Event, EventTemplate } from './core.ts'\nimport { fetchRelayInformation, RelayInformation } from './nip11.ts'\nimport { decode, NostrTypeGuard } from './nip19.ts'\nimport { normalizeURL } from './utils.ts'\n\n/**\n * Represents a NIP29 group.\n */\nexport type Group = {\n  relay: string\n  metadata: GroupMetadata\n  admins?: GroupAdmin[]\n  members?: GroupMember[]\n  reference: GroupReference\n}\n\n/**\n * Represents the metadata for a NIP29 group.\n */\nexport type GroupMetadata = {\n  id: string\n  pubkey: string\n  name?: string\n  picture?: string\n  about?: string\n  isPublic?: boolean\n  isOpen?: boolean\n}\n\n/**\n * Represents a NIP29 group reference.\n */\nexport type GroupReference = {\n  id: string\n  host: string\n}\n\n/**\n * Represents a NIP29 group member.\n */\nexport type GroupMember = {\n  pubkey: string\n  label?: string\n}\n\n/**\n * Represents a NIP29 group admin.\n */\nexport type GroupAdmin = {\n  pubkey: string\n  label?: string\n  permissions: GroupAdminPermission[]\n}\n\n/**\n * Represents the permissions that a NIP29 group admin can have.\n */\nexport enum GroupAdminPermission {\n  /** @deprecated use PutUser instead */\n  AddUser = 'add-user',\n  EditMetadata = 'edit-metadata',\n  DeleteEvent = 'delete-event',\n  RemoveUser = 'remove-user',\n  /** @deprecated removed from NIP */\n  AddPermission = 'add-permission',\n  /** @deprecated removed from NIP */\n  RemovePermission = 'remove-permission',\n  /** @deprecated removed from NIP */\n  EditGroupStatus = 'edit-group-status',\n  PutUser = 'put-user',\n  CreateGroup = 'create-group',\n  DeleteGroup = 'delete-group',\n  CreateInvite = 'create-invite',\n}\n\n/**\n * Generates a group metadata event template.\n *\n * @param group - The group object.\n * @returns An event template with the generated group metadata that can be signed later.\n */\nexport function generateGroupMetadataEventTemplate(group: Group): EventTemplate {\n  const tags: string[][] = [['d', group.metadata.id]]\n  group.metadata.name && tags.push(['name', group.metadata.name])\n  group.metadata.picture && tags.push(['picture', group.metadata.picture])\n  group.metadata.about && tags.push(['about', group.metadata.about])\n  group.metadata.isPublic && tags.push(['public'])\n  group.metadata.isOpen && tags.push(['open'])\n\n  return {\n    content: '',\n    created_at: Math.floor(Date.now() / 1000),\n    kind: 39000,\n    tags,\n  }\n}\n\n/**\n * Validates a group metadata event.\n *\n * @param event - The event to validate.\n * @returns A boolean indicating whether the event is valid.\n */\nexport function validateGroupMetadataEvent(event: Event): boolean {\n  if (event.kind !== 39000) return false\n\n  if (!event.pubkey) return false\n\n  const requiredTags = ['d'] as const\n  for (const tag of requiredTags) {\n    if (!event.tags.find(([t]) => t == tag)) return false\n  }\n\n  return true\n}\n\n/**\n * Generates an event template for group admins.\n *\n * @param group - The group object.\n * @param admins - An array of group admins.\n * @returns The generated event template with the group admins that can be signed later.\n */\nexport function generateGroupAdminsEventTemplate(group: Group, admins: GroupAdmin[]): EventTemplate {\n  const tags: string[][] = [['d', group.metadata.id]]\n  for (const admin of admins) {\n    tags.push(['p', admin.pubkey, admin.label || '', ...admin.permissions])\n  }\n\n  return {\n    content: '',\n    created_at: Math.floor(Date.now() / 1000),\n    kind: 39001,\n    tags,\n  }\n}\n\n/**\n * Validates a group admins event.\n *\n * @param event - The event to validate.\n * @returns True if the event is valid, false otherwise.\n */\nexport function validateGroupAdminsEvent(event: Event): boolean {\n  if (event.kind !== 39001) return false\n\n  const requiredTags = ['d'] as const\n  for (const tag of requiredTags) {\n    if (!event.tags.find(([t]) => t == tag)) return false\n  }\n\n  // validate permissions\n  for (const [tag, _value, _label, ...permissions] of event.tags) {\n    if (tag !== 'p') continue\n\n    for (let i = 0; i < permissions.length; i += 1) {\n      if (typeof permissions[i] !== 'string') return false\n\n      // validate permission name from the GroupAdminPermission enum\n      if (!Object.values(GroupAdminPermission).includes(permissions[i] as GroupAdminPermission)) return false\n    }\n  }\n\n  return true\n}\n\n/**\n * Generates an event template for a group with its members.\n *\n * @param group - The group object.\n * @param members - An array of group members.\n * @returns The generated event template with the group members that can be signed later.\n */\nexport function generateGroupMembersEventTemplate(group: Group, members: GroupMember[]): EventTemplate {\n  const tags: string[][] = [['d', group.metadata.id]]\n  for (const member of members) {\n    tags.push(['p', member.pubkey, member.label || ''])\n  }\n\n  return {\n    content: '',\n    created_at: Math.floor(Date.now() / 1000),\n    kind: 39002,\n    tags,\n  }\n}\n\n/**\n * Validates a group members event.\n *\n * @param event - The event to validate.\n * @returns Returns `true` if the event is a valid group members event, `false` otherwise.\n */\nexport function validateGroupMembersEvent(event: Event): boolean {\n  if (event.kind !== 39002) return false\n\n  const requiredTags = ['d'] as const\n  for (const tag of requiredTags) {\n    if (!event.tags.find(([t]) => t == tag)) return false\n  }\n\n  return true\n}\n\n/**\n * Returns the normalized relay URL based on the provided group reference.\n *\n * @param groupReference - The group reference object containing the host.\n * @returns The normalized relay URL.\n */\nexport function getNormalizedRelayURLByGroupReference(groupReference: GroupReference): string {\n  return normalizeURL(groupReference.host)\n}\n\n/**\n * Fetches relay information by group reference.\n *\n * @param groupReference The group reference.\n * @returns A promise that resolves to the relay information.\n */\nexport async function fetchRelayInformationByGroupReference(groupReference: GroupReference): Promise<RelayInformation> {\n  const normalizedRelayURL = getNormalizedRelayURLByGroupReference(groupReference)\n\n  return fetchRelayInformation(normalizedRelayURL)\n}\n\n/**\n * Fetches the group metadata event from the specified pool.\n * If the normalizedRelayURL is not provided, it will be obtained using the groupReference.\n * If the relayInformation is not provided, it will be fetched using the normalizedRelayURL.\n *\n * @param {Object} options - The options object.\n * @param {AbstractSimplePool} options.pool - The pool to fetch the group metadata event from.\n * @param {GroupReference} options.groupReference - The reference to the group.\n * @param {string} [options.normalizedRelayURL] - The normalized URL of the relay.\n * @param {RelayInformation} [options.relayInformation] - The relay information object.\n * @returns {Promise<Event>} The group metadata event that can be parsed later to get the group metadata object.\n * @throws {Error} If the group is not found on the specified relay.\n */\nexport async function fetchGroupMetadataEvent({\n  pool,\n  groupReference,\n  relayInformation,\n  normalizedRelayURL,\n}: {\n  pool: AbstractSimplePool\n  groupReference: GroupReference\n  normalizedRelayURL?: string\n  relayInformation?: RelayInformation\n}): Promise<Event> {\n  if (!normalizedRelayURL) {\n    normalizedRelayURL = getNormalizedRelayURLByGroupReference(groupReference)\n  }\n\n  if (!relayInformation) {\n    relayInformation = await fetchRelayInformation(normalizedRelayURL)\n  }\n\n  const groupMetadataEvent = await pool.get([normalizedRelayURL], {\n    kinds: [39000],\n    authors: [relayInformation.pubkey],\n    '#d': [groupReference.id],\n  })\n\n  if (!groupMetadataEvent) throw new Error(`group '${groupReference.id}' not found on ${normalizedRelayURL}`)\n\n  return groupMetadataEvent\n}\n\n/**\n * Parses a group metadata event and returns the corresponding GroupMetadata object.\n *\n * @param event - The event to parse.\n * @returns The parsed GroupMetadata object.\n * @throws An error if the group metadata event is invalid.\n */\nexport function parseGroupMetadataEvent(event: Event): GroupMetadata {\n  if (!validateGroupMetadataEvent(event)) throw new Error('invalid group metadata event')\n\n  const metadata: GroupMetadata = {\n    id: '',\n    pubkey: event.pubkey,\n  }\n\n  for (const [tag, value] of event.tags) {\n    switch (tag) {\n      case 'd':\n        metadata.id = value\n        break\n      case 'name':\n        metadata.name = value\n        break\n      case 'picture':\n        metadata.picture = value\n        break\n      case 'about':\n        metadata.about = value\n        break\n      case 'public':\n        metadata.isPublic = true\n        break\n      case 'open':\n        metadata.isOpen = true\n        break\n    }\n  }\n\n  return metadata\n}\n\n/**\n * Fetches the group admins event from the specified pool.\n * If the normalizedRelayURL is not provided, it will be obtained from the groupReference.\n * If the relayInformation is not provided, it will be fetched using the normalizedRelayURL.\n *\n * @param {Object} options - The options object.\n * @param {AbstractSimplePool} options.pool - The pool to fetch the group admins event from.\n * @param {GroupReference} options.groupReference - The reference to the group.\n * @param {string} [options.normalizedRelayURL] - The normalized relay URL.\n * @param {RelayInformation} [options.relayInformation] - The relay information.\n * @returns {Promise<Event>} The group admins event that can be parsed later to get the group admins object.\n * @throws {Error} If the group admins event is not found on the specified relay.\n */\nexport async function fetchGroupAdminsEvent({\n  pool,\n  groupReference,\n  relayInformation,\n  normalizedRelayURL,\n}: {\n  pool: AbstractSimplePool\n  groupReference: GroupReference\n  normalizedRelayURL?: string\n  relayInformation?: RelayInformation\n}): Promise<Event> {\n  if (!normalizedRelayURL) {\n    normalizedRelayURL = getNormalizedRelayURLByGroupReference(groupReference)\n  }\n\n  if (!relayInformation) {\n    relayInformation = await fetchRelayInformation(normalizedRelayURL)\n  }\n\n  const groupAdminsEvent = await pool.get([normalizedRelayURL], {\n    kinds: [39001],\n    authors: [relayInformation.pubkey],\n    '#d': [groupReference.id],\n  })\n\n  if (!groupAdminsEvent) throw new Error(`admins for group '${groupReference.id}' not found on ${normalizedRelayURL}`)\n\n  return groupAdminsEvent\n}\n\n/**\n * Parses a group admins event and returns an array of GroupAdmin objects.\n *\n * @param event - The event to parse.\n * @returns An array of GroupAdmin objects.\n * @throws Throws an error if the group admins event is invalid.\n */\nexport function parseGroupAdminsEvent(event: Event): GroupAdmin[] {\n  if (!validateGroupAdminsEvent(event)) throw new Error('invalid group admins event')\n\n  const admins: GroupAdmin[] = []\n\n  for (const [tag, value, label, ...permissions] of event.tags) {\n    if (tag !== 'p') continue\n\n    admins.push({\n      pubkey: value,\n      label,\n      permissions: permissions as GroupAdminPermission[],\n    })\n  }\n\n  return admins\n}\n\n/**\n * Fetches the group members event from the specified relay.\n * If the normalizedRelayURL is not provided, it will be obtained using the groupReference.\n * If the relayInformation is not provided, it will be fetched using the normalizedRelayURL.\n *\n * @param {Object} options - The options object.\n * @param {AbstractSimplePool} options.pool - The pool object.\n * @param {GroupReference} options.groupReference - The group reference object.\n * @param {string} [options.normalizedRelayURL] - The normalized relay URL.\n * @param {RelayInformation} [options.relayInformation] - The relay information object.\n * @returns {Promise<Event>} The group members event that can be parsed later to get the group members object.\n * @throws {Error} If the group members event is not found.\n */\nexport async function fetchGroupMembersEvent({\n  pool,\n  groupReference,\n  relayInformation,\n  normalizedRelayURL,\n}: {\n  pool: AbstractSimplePool\n  groupReference: GroupReference\n  normalizedRelayURL?: string\n  relayInformation?: RelayInformation\n}): Promise<Event> {\n  if (!normalizedRelayURL) {\n    normalizedRelayURL = getNormalizedRelayURLByGroupReference(groupReference)\n  }\n\n  if (!relayInformation) {\n    relayInformation = await fetchRelayInformation(normalizedRelayURL)\n  }\n\n  const groupMembersEvent = await pool.get([normalizedRelayURL], {\n    kinds: [39002],\n    authors: [relayInformation.pubkey],\n    '#d': [groupReference.id],\n  })\n\n  if (!groupMembersEvent) throw new Error(`members for group '${groupReference.id}' not found on ${normalizedRelayURL}`)\n\n  return groupMembersEvent\n}\n\n/**\n * Parses a group members event and returns an array of GroupMember objects.\n * @param event - The event to parse.\n * @returns An array of GroupMember objects.\n * @throws Throws an error if the group members event is invalid.\n */\nexport function parseGroupMembersEvent(event: Event): GroupMember[] {\n  if (!validateGroupMembersEvent(event)) throw new Error('invalid group members event')\n\n  const members: GroupMember[] = []\n\n  for (const [tag, value, label] of event.tags) {\n    if (tag !== 'p') continue\n\n    members.push({\n      pubkey: value,\n      label,\n    })\n  }\n\n  return members\n}\n\n/**\n * Fetches and parses the group metadata event, group admins event, and group members event from the specified pool.\n * If the normalized relay URL is not provided, it will be obtained using the group reference.\n * If the relay information is not provided, it will be fetched using the normalized relay URL.\n *\n * @param {Object} options - The options for loading the group.\n * @param {AbstractSimplePool} options.pool - The pool to load the group from.\n * @param {GroupReference} options.groupReference - The reference of the group to load.\n * @param {string} [options.normalizedRelayURL] - The normalized URL of the relay to use.\n * @param {RelayInformation} [options.relayInformation] - The relay information to use.\n * @returns {Promise<Group>} A promise that resolves to the loaded group.\n */\nexport async function loadGroup({\n  pool,\n  groupReference,\n  normalizedRelayURL,\n  relayInformation,\n}: {\n  pool: AbstractSimplePool\n  groupReference: GroupReference\n  normalizedRelayURL?: string\n  relayInformation?: RelayInformation\n}): Promise<Group> {\n  if (!normalizedRelayURL) {\n    normalizedRelayURL = getNormalizedRelayURLByGroupReference(groupReference)\n  }\n\n  if (!relayInformation) {\n    relayInformation = await fetchRelayInformation(normalizedRelayURL)\n  }\n\n  const metadataEvent = await fetchGroupMetadataEvent({ pool, groupReference, normalizedRelayURL, relayInformation })\n  const metadata = parseGroupMetadataEvent(metadataEvent)\n\n  const adminsEvent = await fetchGroupAdminsEvent({ pool, groupReference, normalizedRelayURL, relayInformation })\n  const admins = parseGroupAdminsEvent(adminsEvent)\n\n  const membersEvent = await fetchGroupMembersEvent({ pool, groupReference, normalizedRelayURL, relayInformation })\n  const members = parseGroupMembersEvent(membersEvent)\n\n  const group: Group = {\n    relay: normalizedRelayURL,\n    metadata,\n    admins,\n    members,\n    reference: groupReference,\n  }\n\n  return group\n}\n\n/**\n * Loads a group from the specified pool using the provided group code.\n *\n * @param {AbstractSimplePool} pool - The pool to load the group from.\n * @param {string} code - The code representing the group.\n * @returns {Promise<Group>} - A promise that resolves to the loaded group.\n * @throws {Error} - If the group code is invalid.\n */\nexport async function loadGroupFromCode(pool: AbstractSimplePool, code: string): Promise<Group> {\n  const groupReference = parseGroupCode(code)\n\n  if (!groupReference) throw new Error('invalid group code')\n\n  return loadGroup({ pool, groupReference })\n}\n\n/**\n * Parses a group code and returns a GroupReference object.\n *\n * @param code The group code to parse.\n * @returns A GroupReference object if the code is valid, otherwise null.\n */\nexport function parseGroupCode(code: string): null | GroupReference {\n  if (NostrTypeGuard.isNAddr(code)) {\n    try {\n      let { data } = decode(code)\n\n      let { relays, identifier } = data\n      if (!relays || relays.length === 0) return null\n\n      let host = relays![0]\n      if (host.startsWith('wss://')) {\n        host = host.slice(6)\n      }\n      return { host, id: identifier }\n    } catch (err) {\n      return null\n    }\n  } else if (code.split(\"'\").length === 2) {\n    let spl = code.split(\"'\")\n    return { host: spl[0], id: spl[1] }\n  }\n\n  return null\n}\n\n/**\n * Encodes a group reference into a string.\n *\n * @param gr - The group reference to encode.\n * @returns The encoded group reference as a string.\n */\nexport function encodeGroupReference(gr: GroupReference): string {\n  const { host, id } = gr\n  const normalizedHost = host.replace(/^(https?:\\/\\/|wss?:\\/\\/)/, '')\n\n  return `${normalizedHost}'${id}`\n}\n\n/**\n * Subscribes to relay groups metadata events and calls the provided event handler function\n * when an event is received.\n *\n * @param {Object} options - The options for subscribing to relay groups metadata events.\n * @param {AbstractSimplePool} options.pool - The pool to subscribe to.\n * @param {string} options.relayURL - The URL of the relay.\n * @param {Function} options.onError - The error handler function.\n * @param {Function} options.onEvent - The event handler function.\n * @param {Function} [options.onConnect] - The connect handler function.\n * @returns {Function} - A function to close the subscription\n */\nexport function subscribeRelayGroupsMetadataEvents({\n  pool,\n  relayURL,\n  onError,\n  onEvent,\n  onConnect,\n}: {\n  pool: AbstractSimplePool\n  relayURL: string\n  onError: (err: Error) => void\n  onEvent: (event: Event) => void\n  onConnect?: () => void\n}): () => void {\n  let sub: Subscription\n\n  const normalizedRelayURL = normalizeURL(relayURL)\n\n  fetchRelayInformation(normalizedRelayURL)\n    .then(async info => {\n      const abstractedRelay = await pool.ensureRelay(normalizedRelayURL)\n\n      onConnect?.()\n\n      sub = abstractedRelay.prepareSubscription(\n        [\n          {\n            kinds: [39000],\n            limit: 50,\n            authors: [info.pubkey],\n          },\n        ],\n        {\n          onevent(event: Event) {\n            onEvent(event)\n          },\n        },\n      )\n    })\n    .catch(err => {\n      sub.close()\n\n      onError(err)\n    })\n\n  return () => sub.close()\n}\n"],
+  "mappings": "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",
   "names": ["bytesToHex", "hexToBytes", "bytesToHex", "GroupAdminPermission"]
 }
Index: package/lib/cjs/nip44.js.map
===================================================================
--- package/lib/cjs/nip44.js.map
+++ package/lib/cjs/nip44.js.map
@@ -1,7 +1,7 @@
 {
   "version": 3,
   "sources": ["../../nip44.ts", "../../utils.ts"],
-  "sourcesContent": ["import { chacha20 } from '@noble/ciphers/chacha'\nimport { equalBytes } from '@noble/ciphers/utils'\nimport { secp256k1 } from '@noble/curves/secp256k1'\nimport { extract as hkdf_extract, expand as hkdf_expand } from '@noble/hashes/hkdf'\nimport { hmac } from '@noble/hashes/hmac'\nimport { sha256 } from '@noble/hashes/sha256'\nimport { concatBytes, randomBytes } from '@noble/hashes/utils'\nimport { base64 } from '@scure/base'\n\nimport { utf8Decoder, utf8Encoder } from './utils.ts'\n\nconst minPlaintextSize = 0x0001 // 1b msg => padded to 32b\nconst maxPlaintextSize = 0xffff // 65535 (64kb-1) => padded to 64kb\n\nexport function getConversationKey(privkeyA: Uint8Array, pubkeyB: string): Uint8Array {\n  const sharedX = secp256k1.getSharedSecret(privkeyA, '02' + pubkeyB).subarray(1, 33)\n  return hkdf_extract(sha256, sharedX, 'nip44-v2')\n}\n\nfunction getMessageKeys(\n  conversationKey: Uint8Array,\n  nonce: Uint8Array,\n): { chacha_key: Uint8Array; chacha_nonce: Uint8Array; hmac_key: Uint8Array } {\n  const keys = hkdf_expand(sha256, conversationKey, nonce, 76)\n  return {\n    chacha_key: keys.subarray(0, 32),\n    chacha_nonce: keys.subarray(32, 44),\n    hmac_key: keys.subarray(44, 76),\n  }\n}\n\nfunction calcPaddedLen(len: number): number {\n  if (!Number.isSafeInteger(len) || len < 1) throw new Error('expected positive integer')\n  if (len <= 32) return 32\n  const nextPower = 1 << (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 > maxPlaintextSize)\n    throw new Error('invalid plaintext size: must be between 1 and 65535 bytes')\n  const arr = new Uint8Array(2)\n  new DataView(arr.buffer).setUint16(0, num, false)\n  return arr\n}\n\nfunction pad(plaintext: string): Uint8Array {\n  const unpadded = utf8Encoder.encode(plaintext)\n  const unpaddedLen = unpadded.length\n  const prefix = 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 unpaddedLen = new DataView(padded.buffer).getUint16(0)\n  const unpadded = padded.subarray(2, 2 + unpaddedLen)\n  if (\n    unpaddedLen < minPlaintextSize ||\n    unpaddedLen > maxPlaintextSize ||\n    unpadded.length !== unpaddedLen ||\n    padded.length !== 2 + calcPaddedLen(unpaddedLen)\n  )\n    throw new Error('invalid padding')\n  return utf8Decoder.decode(unpadded)\n}\n\nfunction hmacAad(key: Uint8Array, message: Uint8Array, aad: Uint8Array): Uint8Array {\n  if (aad.length !== 32) throw new Error('AAD associated data must be 32 bytes')\n  const combined = concatBytes(aad, message)\n  return hmac(sha256, key, combined)\n}\n\n// metadata: always 65b (version: 1b, nonce: 32b, max: 32b)\n// plaintext: 1b to 0xffff\n// padded plaintext: 32b to 0xffff\n// ciphertext: 32b+2 to 0xffff+2\n// raw payload: 99 (65+32+2) to 65603 (65+0xffff+2)\n// compressed payload (base64): 132b to 87472b\nfunction decodePayload(payload: string): { nonce: Uint8Array; ciphertext: Uint8Array; mac: Uint8Array } {\n  if (typeof payload !== 'string') throw new Error('payload must be a valid string')\n  const plen = payload.length\n  if (plen < 132 || plen > 87472) throw new Error('invalid payload length: ' + plen)\n  if (payload[0] === '#') throw new Error('unknown encryption version')\n  let data: Uint8Array\n  try {\n    data = base64.decode(payload)\n  } catch (error) {\n    throw new Error('invalid base64: ' + (error as any).message)\n  }\n  const dlen = data.length\n  if (dlen < 99 || dlen > 65603) throw new Error('invalid data length: ' + dlen)\n  const vers = data[0]\n  if (vers !== 2) throw new Error('unknown encryption version ' + vers)\n  return {\n    nonce: data.subarray(1, 33),\n    ciphertext: data.subarray(33, -32),\n    mac: data.subarray(-32),\n  }\n}\n\nexport function encrypt(plaintext: string, conversationKey: Uint8Array, nonce: Uint8Array = randomBytes(32)): string {\n  const { chacha_key, chacha_nonce, hmac_key } = getMessageKeys(conversationKey, nonce)\n  const padded = pad(plaintext)\n  const ciphertext = chacha20(chacha_key, chacha_nonce, padded)\n  const mac = hmacAad(hmac_key, ciphertext, nonce)\n  return base64.encode(concatBytes(new Uint8Array([2]), nonce, ciphertext, mac))\n}\n\nexport function decrypt(payload: string, conversationKey: Uint8Array): string {\n  const { nonce, ciphertext, mac } = decodePayload(payload)\n  const { chacha_key, chacha_nonce, hmac_key } = getMessageKeys(conversationKey, nonce)\n  const calculatedMac = hmacAad(hmac_key, ciphertext, nonce)\n  if (!equalBytes(calculatedMac, mac)) throw new Error('invalid MAC')\n  const padded = chacha20(chacha_key, chacha_nonce, ciphertext)\n  return unpad(padded)\n}\n\nexport const v2 = {\n  utils: {\n    getConversationKey,\n    calcPaddedLen,\n  },\n  encrypt,\n  decrypt,\n}\n", "import type { Event } from './core.ts'\n\nexport const utf8Decoder: TextDecoder = new TextDecoder('utf-8')\nexport const utf8Encoder: TextEncoder = new TextEncoder()\n\nexport { bytesToHex, hexToBytes } from '@noble/hashes/utils'\n\nexport function normalizeURL(url: string): string {\n  try {\n    if (url.indexOf('://') === -1) url = 'wss://' + url\n    let p = new URL(url)\n    if (p.protocol === 'http:') p.protocol = 'ws:'\n    else if (p.protocol === 'https:') p.protocol = 'wss:'\n    p.pathname = p.pathname.replace(/\\/+/g, '/')\n    if (p.pathname.endsWith('/')) p.pathname = p.pathname.slice(0, -1)\n    if ((p.port === '80' && p.protocol === 'ws:') || (p.port === '443' && p.protocol === 'wss:')) p.port = ''\n    p.searchParams.sort()\n    p.hash = ''\n    return p.toString()\n  } catch (e) {\n    throw new Error(`Invalid URL: ${url}`)\n  }\n}\n\nexport function insertEventIntoDescendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return b.created_at - event.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function insertEventIntoAscendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return event.created_at - b.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function binarySearch<T>(arr: T[], compare: (b: T) => number): [number, boolean] {\n  let start = 0\n  let end = arr.length - 1\n\n  while (start <= end) {\n    const mid = Math.floor((start + end) / 2)\n    const cmp = compare(arr[mid])\n\n    if (cmp === 0) {\n      return [mid, true]\n    }\n\n    if (cmp < 0) {\n      end = mid - 1\n    } else {\n      start = mid + 1\n    }\n  }\n\n  return [start, false]\n}\n\nexport class QueueNode<V> {\n  public value: V\n  public next: QueueNode<V> | null = null\n  public prev: QueueNode<V> | null = null\n\n  constructor(message: V) {\n    this.value = message\n  }\n}\n\nexport class Queue<V> {\n  public first: QueueNode<V> | null\n  public last: QueueNode<V> | null\n\n  constructor() {\n    this.first = null\n    this.last = null\n  }\n\n  enqueue(value: V): boolean {\n    const newNode = new QueueNode(value)\n    if (!this.last) {\n      // list is empty\n      this.first = newNode\n      this.last = newNode\n    } else if (this.last === this.first) {\n      // list has a single element\n      this.last = newNode\n      this.last.prev = this.first\n      this.first.next = newNode\n    } else {\n      // list has elements, add as last\n      newNode.prev = this.last\n      this.last.next = newNode\n      this.last = newNode\n    }\n    return true\n  }\n\n  dequeue(): V | null {\n    if (!this.first) return null\n\n    if (this.first === this.last) {\n      const target = this.first\n      this.first = null\n      this.last = null\n      return target.value\n    }\n\n    const target = this.first\n    this.first = target.next\n    if (this.first) {\n      this.first.prev = null // fix: clean up prev pointer\n    }\n\n    return target.value\n  }\n}\n"],
-  "mappings": "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",
+  "sourcesContent": ["import { chacha20 } from '@noble/ciphers/chacha.js'\nimport { equalBytes } from '@noble/ciphers/utils.js'\nimport { secp256k1 } from '@noble/curves/secp256k1.js'\nimport { extract as hkdf_extract, expand as hkdf_expand } from '@noble/hashes/hkdf.js'\nimport { hmac } from '@noble/hashes/hmac.js'\nimport { sha256 } from '@noble/hashes/sha2.js'\nimport { concatBytes, hexToBytes, randomBytes } from '@noble/hashes/utils.js'\nimport { base64 } from '@scure/base'\n\nimport { utf8Decoder, utf8Encoder } from './utils.ts'\n\nconst minPlaintextSize = 0x0001 // 1b msg => padded to 32b\nconst maxPlaintextSize = 0xffff // 65535 (64kb-1) => padded to 64kb\n\nexport function getConversationKey(privkeyA: Uint8Array, pubkeyB: string): Uint8Array {\n  const sharedX = secp256k1.getSharedSecret(privkeyA, hexToBytes('02' + pubkeyB)).subarray(1, 33)\n  return hkdf_extract(sha256, sharedX, utf8Encoder.encode('nip44-v2'))\n}\n\nfunction getMessageKeys(\n  conversationKey: Uint8Array,\n  nonce: Uint8Array,\n): { chacha_key: Uint8Array; chacha_nonce: Uint8Array; hmac_key: Uint8Array } {\n  const keys = hkdf_expand(sha256, conversationKey, nonce, 76)\n  return {\n    chacha_key: keys.subarray(0, 32),\n    chacha_nonce: keys.subarray(32, 44),\n    hmac_key: keys.subarray(44, 76),\n  }\n}\n\nfunction calcPaddedLen(len: number): number {\n  if (!Number.isSafeInteger(len) || len < 1) throw new Error('expected positive integer')\n  if (len <= 32) return 32\n  const nextPower = 1 << (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 > maxPlaintextSize)\n    throw new Error('invalid plaintext size: must be between 1 and 65535 bytes')\n  const arr = new Uint8Array(2)\n  new DataView(arr.buffer).setUint16(0, num, false)\n  return arr\n}\n\nfunction pad(plaintext: string): Uint8Array {\n  const unpadded = utf8Encoder.encode(plaintext)\n  const unpaddedLen = unpadded.length\n  const prefix = 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 unpaddedLen = new DataView(padded.buffer).getUint16(0)\n  const unpadded = padded.subarray(2, 2 + unpaddedLen)\n  if (\n    unpaddedLen < minPlaintextSize ||\n    unpaddedLen > maxPlaintextSize ||\n    unpadded.length !== unpaddedLen ||\n    padded.length !== 2 + calcPaddedLen(unpaddedLen)\n  )\n    throw new Error('invalid padding')\n  return utf8Decoder.decode(unpadded)\n}\n\nfunction hmacAad(key: Uint8Array, message: Uint8Array, aad: Uint8Array): Uint8Array {\n  if (aad.length !== 32) throw new Error('AAD associated data must be 32 bytes')\n  const combined = concatBytes(aad, message)\n  return hmac(sha256, key, combined)\n}\n\n// metadata: always 65b (version: 1b, nonce: 32b, max: 32b)\n// plaintext: 1b to 0xffff\n// padded plaintext: 32b to 0xffff\n// ciphertext: 32b+2 to 0xffff+2\n// raw payload: 99 (65+32+2) to 65603 (65+0xffff+2)\n// compressed payload (base64): 132b to 87472b\nfunction decodePayload(payload: string): { nonce: Uint8Array; ciphertext: Uint8Array; mac: Uint8Array } {\n  if (typeof payload !== 'string') throw new Error('payload must be a valid string')\n  const plen = payload.length\n  if (plen < 132 || plen > 87472) throw new Error('invalid payload length: ' + plen)\n  if (payload[0] === '#') throw new Error('unknown encryption version')\n  let data: Uint8Array\n  try {\n    data = base64.decode(payload)\n  } catch (error) {\n    throw new Error('invalid base64: ' + (error as any).message)\n  }\n  const dlen = data.length\n  if (dlen < 99 || dlen > 65603) throw new Error('invalid data length: ' + dlen)\n  const vers = data[0]\n  if (vers !== 2) throw new Error('unknown encryption version ' + vers)\n  return {\n    nonce: data.subarray(1, 33),\n    ciphertext: data.subarray(33, -32),\n    mac: data.subarray(-32),\n  }\n}\n\nexport function encrypt(plaintext: string, conversationKey: Uint8Array, nonce: Uint8Array = randomBytes(32)): string {\n  const { chacha_key, chacha_nonce, hmac_key } = getMessageKeys(conversationKey, nonce)\n  const padded = pad(plaintext)\n  const ciphertext = chacha20(chacha_key, chacha_nonce, padded)\n  const mac = hmacAad(hmac_key, ciphertext, nonce)\n  return base64.encode(concatBytes(new Uint8Array([2]), nonce, ciphertext, mac))\n}\n\nexport function decrypt(payload: string, conversationKey: Uint8Array): string {\n  const { nonce, ciphertext, mac } = decodePayload(payload)\n  const { chacha_key, chacha_nonce, hmac_key } = getMessageKeys(conversationKey, nonce)\n  const calculatedMac = hmacAad(hmac_key, ciphertext, nonce)\n  if (!equalBytes(calculatedMac, mac)) throw new Error('invalid MAC')\n  const padded = chacha20(chacha_key, chacha_nonce, ciphertext)\n  return unpad(padded)\n}\n\nexport const v2 = {\n  utils: {\n    getConversationKey,\n    calcPaddedLen,\n  },\n  encrypt,\n  decrypt,\n}\n", "import type { Event } from './core.ts'\n\nexport const utf8Decoder: TextDecoder = new TextDecoder('utf-8')\nexport const utf8Encoder: TextEncoder = new TextEncoder()\n\nexport { bytesToHex, hexToBytes } from '@noble/hashes/utils.js'\n\nexport function normalizeURL(url: string): string {\n  try {\n    if (url.indexOf('://') === -1) url = 'wss://' + url\n    let p = new URL(url)\n    if (p.protocol === 'http:') p.protocol = 'ws:'\n    else if (p.protocol === 'https:') p.protocol = 'wss:'\n    p.pathname = p.pathname.replace(/\\/+/g, '/')\n    if (p.pathname.endsWith('/')) p.pathname = p.pathname.slice(0, -1)\n    if ((p.port === '80' && p.protocol === 'ws:') || (p.port === '443' && p.protocol === 'wss:')) p.port = ''\n    p.searchParams.sort()\n    p.hash = ''\n    return p.toString()\n  } catch (e) {\n    throw new Error(`Invalid URL: ${url}`)\n  }\n}\n\nexport function insertEventIntoDescendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return b.created_at - event.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function insertEventIntoAscendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return event.created_at - b.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function binarySearch<T>(arr: T[], compare: (b: T) => number): [number, boolean] {\n  let start = 0\n  let end = arr.length - 1\n\n  while (start <= end) {\n    const mid = Math.floor((start + end) / 2)\n    const cmp = compare(arr[mid])\n\n    if (cmp === 0) {\n      return [mid, true]\n    }\n\n    if (cmp < 0) {\n      end = mid - 1\n    } else {\n      start = mid + 1\n    }\n  }\n\n  return [start, false]\n}\n\nexport class QueueNode<V> {\n  public value: V\n  public next: QueueNode<V> | null = null\n  public prev: QueueNode<V> | null = null\n\n  constructor(message: V) {\n    this.value = message\n  }\n}\n\nexport class Queue<V> {\n  public first: QueueNode<V> | null\n  public last: QueueNode<V> | null\n\n  constructor() {\n    this.first = null\n    this.last = null\n  }\n\n  enqueue(value: V): boolean {\n    const newNode = new QueueNode(value)\n    if (!this.last) {\n      // list is empty\n      this.first = newNode\n      this.last = newNode\n    } else if (this.last === this.first) {\n      // list has a single element\n      this.last = newNode\n      this.last.prev = this.first\n      this.first.next = newNode\n    } else {\n      // list has elements, add as last\n      newNode.prev = this.last\n      this.last.next = newNode\n      this.last = newNode\n    }\n    return true\n  }\n\n  dequeue(): V | null {\n    if (!this.first) return null\n\n    if (this.first === this.last) {\n      const target = this.first\n      this.first = null\n      this.last = null\n      return target.value\n    }\n\n    const target = this.first\n    this.first = target.next\n    if (this.first) {\n      this.first.prev = null // fix: clean up prev pointer\n    }\n\n    return target.value\n  }\n}\n"],
+  "mappings": "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",
   "names": ["import_utils", "hkdf_extract", "hkdf_expand"]
 }
Index: package/lib/esm/nip44.js.map
===================================================================
--- package/lib/esm/nip44.js.map
+++ package/lib/esm/nip44.js.map
@@ -1,7 +1,7 @@
 {
   "version": 3,
   "sources": ["../../nip44.ts", "../../utils.ts"],
-  "sourcesContent": ["import { chacha20 } from '@noble/ciphers/chacha'\nimport { equalBytes } from '@noble/ciphers/utils'\nimport { secp256k1 } from '@noble/curves/secp256k1'\nimport { extract as hkdf_extract, expand as hkdf_expand } from '@noble/hashes/hkdf'\nimport { hmac } from '@noble/hashes/hmac'\nimport { sha256 } from '@noble/hashes/sha256'\nimport { concatBytes, randomBytes } from '@noble/hashes/utils'\nimport { base64 } from '@scure/base'\n\nimport { utf8Decoder, utf8Encoder } from './utils.ts'\n\nconst minPlaintextSize = 0x0001 // 1b msg => padded to 32b\nconst maxPlaintextSize = 0xffff // 65535 (64kb-1) => padded to 64kb\n\nexport function getConversationKey(privkeyA: Uint8Array, pubkeyB: string): Uint8Array {\n  const sharedX = secp256k1.getSharedSecret(privkeyA, '02' + pubkeyB).subarray(1, 33)\n  return hkdf_extract(sha256, sharedX, 'nip44-v2')\n}\n\nfunction getMessageKeys(\n  conversationKey: Uint8Array,\n  nonce: Uint8Array,\n): { chacha_key: Uint8Array; chacha_nonce: Uint8Array; hmac_key: Uint8Array } {\n  const keys = hkdf_expand(sha256, conversationKey, nonce, 76)\n  return {\n    chacha_key: keys.subarray(0, 32),\n    chacha_nonce: keys.subarray(32, 44),\n    hmac_key: keys.subarray(44, 76),\n  }\n}\n\nfunction calcPaddedLen(len: number): number {\n  if (!Number.isSafeInteger(len) || len < 1) throw new Error('expected positive integer')\n  if (len <= 32) return 32\n  const nextPower = 1 << (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 > maxPlaintextSize)\n    throw new Error('invalid plaintext size: must be between 1 and 65535 bytes')\n  const arr = new Uint8Array(2)\n  new DataView(arr.buffer).setUint16(0, num, false)\n  return arr\n}\n\nfunction pad(plaintext: string): Uint8Array {\n  const unpadded = utf8Encoder.encode(plaintext)\n  const unpaddedLen = unpadded.length\n  const prefix = 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 unpaddedLen = new DataView(padded.buffer).getUint16(0)\n  const unpadded = padded.subarray(2, 2 + unpaddedLen)\n  if (\n    unpaddedLen < minPlaintextSize ||\n    unpaddedLen > maxPlaintextSize ||\n    unpadded.length !== unpaddedLen ||\n    padded.length !== 2 + calcPaddedLen(unpaddedLen)\n  )\n    throw new Error('invalid padding')\n  return utf8Decoder.decode(unpadded)\n}\n\nfunction hmacAad(key: Uint8Array, message: Uint8Array, aad: Uint8Array): Uint8Array {\n  if (aad.length !== 32) throw new Error('AAD associated data must be 32 bytes')\n  const combined = concatBytes(aad, message)\n  return hmac(sha256, key, combined)\n}\n\n// metadata: always 65b (version: 1b, nonce: 32b, max: 32b)\n// plaintext: 1b to 0xffff\n// padded plaintext: 32b to 0xffff\n// ciphertext: 32b+2 to 0xffff+2\n// raw payload: 99 (65+32+2) to 65603 (65+0xffff+2)\n// compressed payload (base64): 132b to 87472b\nfunction decodePayload(payload: string): { nonce: Uint8Array; ciphertext: Uint8Array; mac: Uint8Array } {\n  if (typeof payload !== 'string') throw new Error('payload must be a valid string')\n  const plen = payload.length\n  if (plen < 132 || plen > 87472) throw new Error('invalid payload length: ' + plen)\n  if (payload[0] === '#') throw new Error('unknown encryption version')\n  let data: Uint8Array\n  try {\n    data = base64.decode(payload)\n  } catch (error) {\n    throw new Error('invalid base64: ' + (error as any).message)\n  }\n  const dlen = data.length\n  if (dlen < 99 || dlen > 65603) throw new Error('invalid data length: ' + dlen)\n  const vers = data[0]\n  if (vers !== 2) throw new Error('unknown encryption version ' + vers)\n  return {\n    nonce: data.subarray(1, 33),\n    ciphertext: data.subarray(33, -32),\n    mac: data.subarray(-32),\n  }\n}\n\nexport function encrypt(plaintext: string, conversationKey: Uint8Array, nonce: Uint8Array = randomBytes(32)): string {\n  const { chacha_key, chacha_nonce, hmac_key } = getMessageKeys(conversationKey, nonce)\n  const padded = pad(plaintext)\n  const ciphertext = chacha20(chacha_key, chacha_nonce, padded)\n  const mac = hmacAad(hmac_key, ciphertext, nonce)\n  return base64.encode(concatBytes(new Uint8Array([2]), nonce, ciphertext, mac))\n}\n\nexport function decrypt(payload: string, conversationKey: Uint8Array): string {\n  const { nonce, ciphertext, mac } = decodePayload(payload)\n  const { chacha_key, chacha_nonce, hmac_key } = getMessageKeys(conversationKey, nonce)\n  const calculatedMac = hmacAad(hmac_key, ciphertext, nonce)\n  if (!equalBytes(calculatedMac, mac)) throw new Error('invalid MAC')\n  const padded = chacha20(chacha_key, chacha_nonce, ciphertext)\n  return unpad(padded)\n}\n\nexport const v2 = {\n  utils: {\n    getConversationKey,\n    calcPaddedLen,\n  },\n  encrypt,\n  decrypt,\n}\n", "import type { Event } from './core.ts'\n\nexport const utf8Decoder: TextDecoder = new TextDecoder('utf-8')\nexport const utf8Encoder: TextEncoder = new TextEncoder()\n\nexport { bytesToHex, hexToBytes } from '@noble/hashes/utils'\n\nexport function normalizeURL(url: string): string {\n  try {\n    if (url.indexOf('://') === -1) url = 'wss://' + url\n    let p = new URL(url)\n    if (p.protocol === 'http:') p.protocol = 'ws:'\n    else if (p.protocol === 'https:') p.protocol = 'wss:'\n    p.pathname = p.pathname.replace(/\\/+/g, '/')\n    if (p.pathname.endsWith('/')) p.pathname = p.pathname.slice(0, -1)\n    if ((p.port === '80' && p.protocol === 'ws:') || (p.port === '443' && p.protocol === 'wss:')) p.port = ''\n    p.searchParams.sort()\n    p.hash = ''\n    return p.toString()\n  } catch (e) {\n    throw new Error(`Invalid URL: ${url}`)\n  }\n}\n\nexport function insertEventIntoDescendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return b.created_at - event.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function insertEventIntoAscendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return event.created_at - b.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function binarySearch<T>(arr: T[], compare: (b: T) => number): [number, boolean] {\n  let start = 0\n  let end = arr.length - 1\n\n  while (start <= end) {\n    const mid = Math.floor((start + end) / 2)\n    const cmp = compare(arr[mid])\n\n    if (cmp === 0) {\n      return [mid, true]\n    }\n\n    if (cmp < 0) {\n      end = mid - 1\n    } else {\n      start = mid + 1\n    }\n  }\n\n  return [start, false]\n}\n\nexport class QueueNode<V> {\n  public value: V\n  public next: QueueNode<V> | null = null\n  public prev: QueueNode<V> | null = null\n\n  constructor(message: V) {\n    this.value = message\n  }\n}\n\nexport class Queue<V> {\n  public first: QueueNode<V> | null\n  public last: QueueNode<V> | null\n\n  constructor() {\n    this.first = null\n    this.last = null\n  }\n\n  enqueue(value: V): boolean {\n    const newNode = new QueueNode(value)\n    if (!this.last) {\n      // list is empty\n      this.first = newNode\n      this.last = newNode\n    } else if (this.last === this.first) {\n      // list has a single element\n      this.last = newNode\n      this.last.prev = this.first\n      this.first.next = newNode\n    } else {\n      // list has elements, add as last\n      newNode.prev = this.last\n      this.last.next = newNode\n      this.last = newNode\n    }\n    return true\n  }\n\n  dequeue(): V | null {\n    if (!this.first) return null\n\n    if (this.first === this.last) {\n      const target = this.first\n      this.first = null\n      this.last = null\n      return target.value\n    }\n\n    const target = this.first\n    this.first = target.next\n    if (this.first) {\n      this.first.prev = null // fix: clean up prev pointer\n    }\n\n    return target.value\n  }\n}\n"],
-  "mappings": "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",
-  "names": []
+  "sourcesContent": ["import { chacha20 } from '@noble/ciphers/chacha.js'\nimport { equalBytes } from '@noble/ciphers/utils.js'\nimport { secp256k1 } from '@noble/curves/secp256k1.js'\nimport { extract as hkdf_extract, expand as hkdf_expand } from '@noble/hashes/hkdf.js'\nimport { hmac } from '@noble/hashes/hmac.js'\nimport { sha256 } from '@noble/hashes/sha2.js'\nimport { concatBytes, hexToBytes, randomBytes } from '@noble/hashes/utils.js'\nimport { base64 } from '@scure/base'\n\nimport { utf8Decoder, utf8Encoder } from './utils.ts'\n\nconst minPlaintextSize = 0x0001 // 1b msg => padded to 32b\nconst maxPlaintextSize = 0xffff // 65535 (64kb-1) => padded to 64kb\n\nexport function getConversationKey(privkeyA: Uint8Array, pubkeyB: string): Uint8Array {\n  const sharedX = secp256k1.getSharedSecret(privkeyA, hexToBytes('02' + pubkeyB)).subarray(1, 33)\n  return hkdf_extract(sha256, sharedX, utf8Encoder.encode('nip44-v2'))\n}\n\nfunction getMessageKeys(\n  conversationKey: Uint8Array,\n  nonce: Uint8Array,\n): { chacha_key: Uint8Array; chacha_nonce: Uint8Array; hmac_key: Uint8Array } {\n  const keys = hkdf_expand(sha256, conversationKey, nonce, 76)\n  return {\n    chacha_key: keys.subarray(0, 32),\n    chacha_nonce: keys.subarray(32, 44),\n    hmac_key: keys.subarray(44, 76),\n  }\n}\n\nfunction calcPaddedLen(len: number): number {\n  if (!Number.isSafeInteger(len) || len < 1) throw new Error('expected positive integer')\n  if (len <= 32) return 32\n  const nextPower = 1 << (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 > maxPlaintextSize)\n    throw new Error('invalid plaintext size: must be between 1 and 65535 bytes')\n  const arr = new Uint8Array(2)\n  new DataView(arr.buffer).setUint16(0, num, false)\n  return arr\n}\n\nfunction pad(plaintext: string): Uint8Array {\n  const unpadded = utf8Encoder.encode(plaintext)\n  const unpaddedLen = unpadded.length\n  const prefix = 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 unpaddedLen = new DataView(padded.buffer).getUint16(0)\n  const unpadded = padded.subarray(2, 2 + unpaddedLen)\n  if (\n    unpaddedLen < minPlaintextSize ||\n    unpaddedLen > maxPlaintextSize ||\n    unpadded.length !== unpaddedLen ||\n    padded.length !== 2 + calcPaddedLen(unpaddedLen)\n  )\n    throw new Error('invalid padding')\n  return utf8Decoder.decode(unpadded)\n}\n\nfunction hmacAad(key: Uint8Array, message: Uint8Array, aad: Uint8Array): Uint8Array {\n  if (aad.length !== 32) throw new Error('AAD associated data must be 32 bytes')\n  const combined = concatBytes(aad, message)\n  return hmac(sha256, key, combined)\n}\n\n// metadata: always 65b (version: 1b, nonce: 32b, max: 32b)\n// plaintext: 1b to 0xffff\n// padded plaintext: 32b to 0xffff\n// ciphertext: 32b+2 to 0xffff+2\n// raw payload: 99 (65+32+2) to 65603 (65+0xffff+2)\n// compressed payload (base64): 132b to 87472b\nfunction decodePayload(payload: string): { nonce: Uint8Array; ciphertext: Uint8Array; mac: Uint8Array } {\n  if (typeof payload !== 'string') throw new Error('payload must be a valid string')\n  const plen = payload.length\n  if (plen < 132 || plen > 87472) throw new Error('invalid payload length: ' + plen)\n  if (payload[0] === '#') throw new Error('unknown encryption version')\n  let data: Uint8Array\n  try {\n    data = base64.decode(payload)\n  } catch (error) {\n    throw new Error('invalid base64: ' + (error as any).message)\n  }\n  const dlen = data.length\n  if (dlen < 99 || dlen > 65603) throw new Error('invalid data length: ' + dlen)\n  const vers = data[0]\n  if (vers !== 2) throw new Error('unknown encryption version ' + vers)\n  return {\n    nonce: data.subarray(1, 33),\n    ciphertext: data.subarray(33, -32),\n    mac: data.subarray(-32),\n  }\n}\n\nexport function encrypt(plaintext: string, conversationKey: Uint8Array, nonce: Uint8Array = randomBytes(32)): string {\n  const { chacha_key, chacha_nonce, hmac_key } = getMessageKeys(conversationKey, nonce)\n  const padded = pad(plaintext)\n  const ciphertext = chacha20(chacha_key, chacha_nonce, padded)\n  const mac = hmacAad(hmac_key, ciphertext, nonce)\n  return base64.encode(concatBytes(new Uint8Array([2]), nonce, ciphertext, mac))\n}\n\nexport function decrypt(payload: string, conversationKey: Uint8Array): string {\n  const { nonce, ciphertext, mac } = decodePayload(payload)\n  const { chacha_key, chacha_nonce, hmac_key } = getMessageKeys(conversationKey, nonce)\n  const calculatedMac = hmacAad(hmac_key, ciphertext, nonce)\n  if (!equalBytes(calculatedMac, mac)) throw new Error('invalid MAC')\n  const padded = chacha20(chacha_key, chacha_nonce, ciphertext)\n  return unpad(padded)\n}\n\nexport const v2 = {\n  utils: {\n    getConversationKey,\n    calcPaddedLen,\n  },\n  encrypt,\n  decrypt,\n}\n", "import type { Event } from './core.ts'\n\nexport const utf8Decoder: TextDecoder = new TextDecoder('utf-8')\nexport const utf8Encoder: TextEncoder = new TextEncoder()\n\nexport { bytesToHex, hexToBytes } from '@noble/hashes/utils.js'\n\nexport function normalizeURL(url: string): string {\n  try {\n    if (url.indexOf('://') === -1) url = 'wss://' + url\n    let p = new URL(url)\n    if (p.protocol === 'http:') p.protocol = 'ws:'\n    else if (p.protocol === 'https:') p.protocol = 'wss:'\n    p.pathname = p.pathname.replace(/\\/+/g, '/')\n    if (p.pathname.endsWith('/')) p.pathname = p.pathname.slice(0, -1)\n    if ((p.port === '80' && p.protocol === 'ws:') || (p.port === '443' && p.protocol === 'wss:')) p.port = ''\n    p.searchParams.sort()\n    p.hash = ''\n    return p.toString()\n  } catch (e) {\n    throw new Error(`Invalid URL: ${url}`)\n  }\n}\n\nexport function insertEventIntoDescendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return b.created_at - event.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function insertEventIntoAscendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return event.created_at - b.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function binarySearch<T>(arr: T[], compare: (b: T) => number): [number, boolean] {\n  let start = 0\n  let end = arr.length - 1\n\n  while (start <= end) {\n    const mid = Math.floor((start + end) / 2)\n    const cmp = compare(arr[mid])\n\n    if (cmp === 0) {\n      return [mid, true]\n    }\n\n    if (cmp < 0) {\n      end = mid - 1\n    } else {\n      start = mid + 1\n    }\n  }\n\n  return [start, false]\n}\n\nexport class QueueNode<V> {\n  public value: V\n  public next: QueueNode<V> | null = null\n  public prev: QueueNode<V> | null = null\n\n  constructor(message: V) {\n    this.value = message\n  }\n}\n\nexport class Queue<V> {\n  public first: QueueNode<V> | null\n  public last: QueueNode<V> | null\n\n  constructor() {\n    this.first = null\n    this.last = null\n  }\n\n  enqueue(value: V): boolean {\n    const newNode = new QueueNode(value)\n    if (!this.last) {\n      // list is empty\n      this.first = newNode\n      this.last = newNode\n    } else if (this.last === this.first) {\n      // list has a single element\n      this.last = newNode\n      this.last.prev = this.first\n      this.first.next = newNode\n    } else {\n      // list has elements, add as last\n      newNode.prev = this.last\n      this.last.next = newNode\n      this.last = newNode\n    }\n    return true\n  }\n\n  dequeue(): V | null {\n    if (!this.first) return null\n\n    if (this.first === this.last) {\n      const target = this.first\n      this.first = null\n      this.last = null\n      return target.value\n    }\n\n    const target = this.first\n    this.first = target.next\n    if (this.first) {\n      this.first.prev = null // fix: clean up prev pointer\n    }\n\n    return target.value\n  }\n}\n"],
+  "mappings": "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",
+  "names": ["hexToBytes", "hexToBytes"]
 }
Index: package/lib/cjs/nip46.js.map
===================================================================
--- package/lib/cjs/nip46.js.map
+++ package/lib/cjs/nip46.js.map
@@ -1,7 +1,7 @@
 {
   "version": 3,
   "sources": ["../../nip46.ts", "../../pure.ts", "../../core.ts", "../../utils.ts", "../../nip44.ts", "../../nip05.ts", "../../kinds.ts", "../../filter.ts", "../../fakejson.ts", "../../nip42.ts", "../../helpers.ts", "../../abstract-relay.ts", "../../abstract-pool.ts", "../../pool.ts"],
-  "sourcesContent": ["import { EventTemplate, NostrEvent, VerifiedEvent } from './core.ts'\nimport { generateSecretKey, finalizeEvent, getPublicKey, verifyEvent } from './pure.ts'\nimport { AbstractSimplePool, SubCloser } from './abstract-pool.ts'\nimport { getConversationKey, decrypt, encrypt } from './nip44.ts'\nimport { NIP05_REGEX } from './nip05.ts'\nimport { SimplePool } from './pool.ts'\nimport { Handlerinformation, NostrConnect } from './kinds.ts'\nimport { Signer } from './signer.ts'\n\nvar _fetch: any\n\ntry {\n  _fetch = fetch\n} catch {}\n\nexport function useFetchImplementation(fetchImplementation: any) {\n  _fetch = fetchImplementation\n}\n\nexport const BUNKER_REGEX = /^bunker:\\/\\/([0-9a-f]{64})\\??([?\\/\\w:.=&%-]*)$/\nconst EMAIL_REGEX = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/\n\nexport type BunkerPointer = {\n  relays: string[]\n  pubkey: string\n  secret: null | string\n}\n\nexport function toBunkerURL(bunkerPointer: BunkerPointer): string {\n  let bunkerURL = new URL(`bunker://${bunkerPointer.pubkey}`)\n  bunkerPointer.relays.forEach(relay => {\n    bunkerURL.searchParams.append('relay', relay)\n  })\n  if (bunkerPointer.secret) {\n    bunkerURL.searchParams.set('secret', bunkerPointer.secret)\n  }\n  return bunkerURL.toString()\n}\n\n/** This takes either a bunker:// URL or a [email protected] NIP-05 identifier\n    and returns a BunkerPointer -- or null in case of error */\nexport async function parseBunkerInput(input: string): Promise<BunkerPointer | null> {\n  let match = input.match(BUNKER_REGEX)\n  if (match) {\n    try {\n      const pubkey = match[1]\n      const qs = new URLSearchParams(match[2])\n      return {\n        pubkey,\n        relays: qs.getAll('relay'),\n        secret: qs.get('secret'),\n      }\n    } catch (_err) {\n      /* just move to the next case */\n    }\n  }\n\n  return queryBunkerProfile(input)\n}\n\nexport async function queryBunkerProfile(nip05: string): Promise<BunkerPointer | null> {\n  const match = nip05.match(NIP05_REGEX)\n  if (!match) return null\n\n  const [_, name = '_', domain] = match\n\n  try {\n    const url = `https://${domain}/.well-known/nostr.json?name=${name}`\n    const res = await (await _fetch(url, { redirect: 'error' })).json()\n\n    let pubkey = res.names[name]\n    let relays = res.nip46[pubkey] || []\n\n    return { pubkey, relays, secret: null }\n  } catch (_err) {\n    return null\n  }\n}\n\nexport type NostrConnectParams = {\n  clientPubkey: string\n  relays: string[]\n  secret: string\n  perms?: string[]\n  name?: string\n  url?: string\n  image?: string\n}\n\nexport function createNostrConnectURI(params: NostrConnectParams): string {\n  const queryParams = new URLSearchParams()\n\n  params.relays.forEach(relay => {\n    queryParams.append('relay', relay)\n  })\n\n  queryParams.append('secret', params.secret)\n\n  if (params.perms && params.perms.length > 0) {\n    queryParams.append('perms', params.perms.join(','))\n  }\n  if (params.name) {\n    queryParams.append('name', params.name)\n  }\n  if (params.url) {\n    queryParams.append('url', params.url)\n  }\n  if (params.image) {\n    queryParams.append('image', params.image)\n  }\n\n  return `nostrconnect://${params.clientPubkey}?${queryParams.toString()}`\n}\n\nexport type BunkerSignerParams = {\n  pool?: AbstractSimplePool\n  onauth?: (url: string) => void\n}\n\nexport class BunkerSigner implements Signer {\n  private params: BunkerSignerParams\n  private pool: AbstractSimplePool\n  private subCloser: SubCloser | undefined\n  private isOpen: boolean\n  private serial: number\n  private idPrefix: string\n  private listeners: {\n    [id: string]: {\n      resolve: (_: string) => void\n      reject: (_: string) => void\n    }\n  }\n  private waitingForAuth: { [id: string]: boolean }\n  private secretKey: Uint8Array\n  // If the client initiates the connection, the two variables below can be filled in later.\n  private conversationKey!: Uint8Array\n  public bp!: BunkerPointer\n\n  private cachedPubKey: string | undefined\n\n  /**\n   * Creates a new instance of the Nip46 class.\n   * @param relays - An array of relay addresses.\n   * @param remotePubkey - An optional remote public key. This is the key you want to sign as.\n   * @param secretKey - An optional key pair.\n   */\n  private constructor(clientSecretKey: Uint8Array, params: BunkerSignerParams) {\n    this.params = params\n    this.pool = params.pool || new SimplePool()\n    this.secretKey = clientSecretKey\n    this.isOpen = false\n    this.idPrefix = Math.random().toString(36).substring(7)\n    this.serial = 0\n    this.listeners = {}\n    this.waitingForAuth = {}\n  }\n\n  /**\n   * [Factory Method 1] Creates a Signer using bunker information (bunker:// URL or NIP-05).\n   * This method is used when the public key of the bunker is known in advance.\n   */\n  public static fromBunker(\n    clientSecretKey: Uint8Array,\n    bp: BunkerPointer,\n    params: BunkerSignerParams = {},\n  ): BunkerSigner {\n    if (bp.relays.length === 0) {\n      throw new Error('no relays specified for this bunker')\n    }\n\n    const signer = new BunkerSigner(clientSecretKey, params)\n\n    signer.conversationKey = getConversationKey(clientSecretKey, bp.pubkey)\n    signer.bp = bp\n\n    signer.setupSubscription()\n    return signer\n  }\n\n  /**\n   * [Factory Method 2] Creates a Signer using a nostrconnect:// URI generated by the client.\n   * In this method, the bunker initiates the connection by scanning the URI.\n   */\n  public static async fromURI(\n    clientSecretKey: Uint8Array,\n    connectionURI: string,\n    bunkerParams: BunkerSignerParams = {},\n    maxWaitOrAbort: number | AbortSignal = 300_000,\n  ): Promise<BunkerSigner> {\n    const signer = new BunkerSigner(clientSecretKey, bunkerParams)\n    const uri = new URL(connectionURI)\n    const clientPubkey = getPublicKey(clientSecretKey)\n\n    return new Promise((resolve, reject) => {\n      let success = false\n      const sub = signer.pool.subscribe(\n        uri.searchParams.getAll('relay'),\n        {\n          kinds: [NostrConnect],\n          '#p': [clientPubkey],\n          limit: 0,\n        },\n        {\n          onevent: async (event: NostrEvent) => {\n            try {\n              const tempConvKey = getConversationKey(clientSecretKey, event.pubkey)\n              const decryptedContent = decrypt(event.content, tempConvKey)\n\n              const response = JSON.parse(decryptedContent)\n\n              if (response.result === uri.searchParams.get('secret')) {\n                sub.close()\n\n                signer.bp = {\n                  pubkey: event.pubkey,\n                  relays: uri.searchParams.getAll('relay'),\n                  secret: uri.searchParams.get('secret'),\n                }\n                signer.conversationKey = getConversationKey(clientSecretKey, event.pubkey)\n                signer.setupSubscription()\n\n                success = true\n                await Promise.race([new Promise(resolve => setTimeout(resolve, 1000)), signer.switchRelays()])\n                resolve(signer)\n              }\n            } catch (e) {\n              console.warn('failed to process potential connection event', e)\n            }\n          },\n          onclose: () => {\n            if (!success) reject(new Error('subscription closed before connection was established.'))\n          },\n          maxWait: typeof maxWaitOrAbort === 'number' ? maxWaitOrAbort : undefined,\n          abort: typeof maxWaitOrAbort !== 'number' ? maxWaitOrAbort : undefined,\n        },\n      )\n    })\n  }\n\n  private setupSubscription() {\n    const listeners = this.listeners\n    const waitingForAuth = this.waitingForAuth\n    const convKey = this.conversationKey\n\n    this.subCloser = this.pool.subscribe(\n      this.bp.relays,\n      {\n        kinds: [NostrConnect],\n        authors: [this.bp.pubkey],\n        '#p': [getPublicKey(this.secretKey)],\n        limit: 0,\n      },\n      {\n        onevent: async (event: NostrEvent) => {\n          const o = JSON.parse(decrypt(event.content, convKey))\n          const { id, result, error } = o\n\n          if (result === 'auth_url' && waitingForAuth[id]) {\n            delete waitingForAuth[id]\n\n            if (this.params.onauth) {\n              this.params.onauth(error)\n            } else {\n              console.warn(\n                `nostr-tools/nip46: remote signer ${this.bp.pubkey} tried to send an \"auth_url\"='${error}' but there was no onauth() callback configured.`,\n              )\n            }\n            return\n          }\n\n          let handler = listeners[id]\n          if (handler) {\n            if (error) handler.reject(error)\n            else if (result) handler.resolve(result)\n            delete listeners[id]\n          }\n        },\n        onclose: () => {\n          this.subCloser = undefined\n        },\n      },\n    )\n    this.isOpen = true\n  }\n\n  async switchRelays(): Promise<boolean> {\n    try {\n      const switchResp = await this.sendRequest('switch_relays', [])\n      let relays = JSON.parse(switchResp) as string[] | null\n      if (!relays) return false\n      if (JSON.stringify(relays.sort()) === JSON.stringify(this.bp.relays)) return false\n\n      this.bp.relays = relays\n      let previousCloser = this.subCloser!\n      setTimeout(() => {\n        previousCloser.close()\n      }, 5000)\n\n      this.subCloser = undefined\n      this.setupSubscription()\n      return true\n    } catch {\n      return false\n    }\n  }\n\n  // closes the subscription -- this object can't be used anymore after this\n  async close() {\n    this.isOpen = false\n    this.subCloser!.close()\n  }\n\n  async sendRequest(method: string, params: string[]): Promise<string> {\n    return new Promise(async (resolve, reject) => {\n      try {\n        if (!this.isOpen) throw new Error('this signer is not open anymore, create a new one')\n        if (!this.subCloser) this.setupSubscription()\n\n        this.serial++\n        const id = `${this.idPrefix}-${this.serial}`\n\n        const encryptedContent = encrypt(JSON.stringify({ id, method, params }), this.conversationKey)\n\n        // the request event\n        const verifiedEvent: VerifiedEvent = finalizeEvent(\n          {\n            kind: NostrConnect,\n            tags: [['p', this.bp.pubkey]],\n            content: encryptedContent,\n            created_at: Math.floor(Date.now() / 1000),\n          },\n          this.secretKey,\n        )\n\n        // setup callback listener\n        this.listeners[id] = { resolve, reject }\n        this.waitingForAuth[id] = true\n\n        // publish the event\n        await Promise.any(this.pool.publish(this.bp.relays, verifiedEvent))\n      } catch (err) {\n        reject(err)\n      }\n    })\n  }\n\n  /**\n   * Calls the \"connect\" method on the bunker.\n   * The promise will be rejected if the response is not \"pong\".\n   */\n  async ping(): Promise<void> {\n    let resp = await this.sendRequest('ping', [])\n    if (resp !== 'pong') throw new Error(`result is not pong: ${resp}`)\n  }\n\n  /**\n   * Calls the \"connect\" method on the bunker.\n   */\n  async connect(): Promise<void> {\n    await this.sendRequest('connect', [this.bp.pubkey, this.bp.secret || ''])\n  }\n\n  /**\n   * Calls the \"get_public_key\" method on the bunker.\n   * (before we would return the public key hardcoded in the bunker parameters, but\n   *  that is not correct as that may be the bunker pubkey and the actual signer\n   *  pubkey may be different.)\n   */\n  async getPublicKey(): Promise<string> {\n    if (!this.cachedPubKey) {\n      this.cachedPubKey = await this.sendRequest('get_public_key', [])\n    }\n    return this.cachedPubKey\n  }\n\n  /**\n   * Signs an event using the remote private key.\n   * @param event - The event to sign.\n   * @returns A Promise that resolves to the signed event.\n   */\n  async signEvent(event: EventTemplate): Promise<VerifiedEvent> {\n    let resp = await this.sendRequest('sign_event', [JSON.stringify(event)])\n    let signed: NostrEvent = JSON.parse(resp)\n    if (verifyEvent(signed)) {\n      return signed\n    } else {\n      throw new Error(`event returned from bunker is improperly signed: ${JSON.stringify(signed)}`)\n    }\n  }\n\n  async nip04Encrypt(thirdPartyPubkey: string, plaintext: string): Promise<string> {\n    return await this.sendRequest('nip04_encrypt', [thirdPartyPubkey, plaintext])\n  }\n\n  async nip04Decrypt(thirdPartyPubkey: string, ciphertext: string): Promise<string> {\n    return await this.sendRequest('nip04_decrypt', [thirdPartyPubkey, ciphertext])\n  }\n\n  async nip44Encrypt(thirdPartyPubkey: string, plaintext: string): Promise<string> {\n    return await this.sendRequest('nip44_encrypt', [thirdPartyPubkey, plaintext])\n  }\n\n  async nip44Decrypt(thirdPartyPubkey: string, ciphertext: string): Promise<string> {\n    return await this.sendRequest('nip44_decrypt', [thirdPartyPubkey, ciphertext])\n  }\n}\n\n/**\n * Creates an account with the specified username, domain, and optional email.\n * @param bunkerPubkey - The public key of the bunker to use for the create_account call.\n * @param username - The username for the account.\n * @param domain - The domain for the account.\n * @param email - The optional email for the account.\n * @param localSecretKey - Optionally pass a local secret key that will be used to communicate with the bunker,\n                           this will default to generating a random key.\n * @throws Error if the email is present but invalid.\n * @returns A Promise that resolves to the auth_url that the client should follow to create an account.\n */\nexport async function createAccount(\n  bunker: BunkerProfile,\n  params: BunkerSignerParams,\n  username: string,\n  domain: string,\n  email?: string,\n  localSecretKey: Uint8Array = generateSecretKey(),\n): Promise<BunkerSigner> {\n  if (email && !EMAIL_REGEX.test(email)) throw new Error('invalid email')\n\n  let rpc = BunkerSigner.fromBunker(localSecretKey, bunker.bunkerPointer, params)\n\n  let pubkey = await rpc.sendRequest('create_account', [username, domain, email || ''])\n\n  // once we get the newly created pubkey back, we hijack this signer instance\n  // and turn it into the main instance for this newly created pubkey\n  rpc.bp.pubkey = pubkey\n  await rpc.connect()\n\n  return rpc\n}\n\n/**\n * Fetches info on available providers that announce themselves using NIP-89 events.\n * @returns A promise that resolves to an array of available bunker objects.\n */\nexport async function fetchBunkerProviders(pool: AbstractSimplePool, relays: string[]): Promise<BunkerProfile[]> {\n  const events = await pool.querySync(relays, {\n    kinds: [Handlerinformation],\n    '#k': [NostrConnect.toString()],\n  })\n\n  events.sort((a, b) => b.created_at - a.created_at)\n\n  // validate bunkers by checking their NIP-05 and pubkey\n  // map to a more useful object\n  const validatedBunkers = await Promise.all(\n    events.map(async (event, i) => {\n      try {\n        const content = JSON.parse(event.content)\n\n        // skip duplicates\n        try {\n          if (events.findIndex(ev => JSON.parse(ev.content).nip05 === content.nip05) !== i) return undefined\n        } catch (err) {\n          /***/\n        }\n\n        const bp = await queryBunkerProfile(content.nip05)\n        if (bp && bp.pubkey === event.pubkey && bp.relays.length) {\n          return {\n            bunkerPointer: bp,\n            nip05: content.nip05,\n            domain: content.nip05.split('@')[1],\n            name: content.name || content.display_name,\n            picture: content.picture,\n            about: content.about,\n            website: content.website,\n            local: false,\n          }\n        }\n      } catch (err) {\n        return undefined\n      }\n    }),\n  )\n\n  return validatedBunkers.filter(b => b !== undefined) as BunkerProfile[]\n}\n\nexport type BunkerProfile = {\n  bunkerPointer: BunkerPointer\n  domain: string\n  nip05: string\n  name: string\n  picture: string\n  about: string\n  website: string\n  local: boolean\n}\n", "import { schnorr } from '@noble/curves/secp256k1'\nimport { bytesToHex } from '@noble/hashes/utils'\nimport { Nostr, Event, EventTemplate, UnsignedEvent, VerifiedEvent, verifiedSymbol, validateEvent } from './core.ts'\nimport { sha256 } from '@noble/hashes/sha256'\n\nimport { utf8Encoder } from './utils.ts'\n\nclass JS implements Nostr {\n  generateSecretKey(): Uint8Array {\n    return schnorr.utils.randomPrivateKey()\n  }\n  getPublicKey(secretKey: Uint8Array): string {\n    return bytesToHex(schnorr.getPublicKey(secretKey))\n  }\n  finalizeEvent(t: EventTemplate, secretKey: Uint8Array): VerifiedEvent {\n    const event = t as VerifiedEvent\n    event.pubkey = bytesToHex(schnorr.getPublicKey(secretKey))\n    event.id = getEventHash(event)\n    event.sig = bytesToHex(schnorr.sign(getEventHash(event), secretKey))\n    event[verifiedSymbol] = true\n    return event\n  }\n  verifyEvent(event: Event): event is VerifiedEvent {\n    if (typeof event[verifiedSymbol] === 'boolean') return event[verifiedSymbol]\n\n    const hash = getEventHash(event)\n    if (hash !== event.id) {\n      event[verifiedSymbol] = false\n      return false\n    }\n\n    try {\n      const valid = schnorr.verify(event.sig, hash, event.pubkey)\n      event[verifiedSymbol] = valid\n      return valid\n    } catch (err) {\n      event[verifiedSymbol] = false\n      return false\n    }\n  }\n}\n\nexport function serializeEvent(evt: UnsignedEvent): string {\n  if (!validateEvent(evt)) throw new Error(\"can't serialize event with wrong or missing properties\")\n  return JSON.stringify([0, evt.pubkey, evt.created_at, evt.kind, evt.tags, evt.content])\n}\n\nexport function getEventHash(event: UnsignedEvent): string {\n  let eventHash = sha256(utf8Encoder.encode(serializeEvent(event)))\n  return bytesToHex(eventHash)\n}\n\nconst i: JS = new JS()\n\nexport const generateSecretKey = i.generateSecretKey\nexport const getPublicKey = i.getPublicKey\nexport const finalizeEvent = i.finalizeEvent\nexport const verifyEvent = i.verifyEvent\nexport * from './core.ts'\n", "export interface Nostr {\n  generateSecretKey(): Uint8Array\n  getPublicKey(secretKey: Uint8Array): string\n  finalizeEvent(event: EventTemplate, secretKey: Uint8Array): VerifiedEvent\n  verifyEvent(event: Event): event is VerifiedEvent\n}\n\n/** Designates a verified event signature. */\nexport const verifiedSymbol = Symbol('verified')\n\nexport type NostrEvent = {\n  kind: number\n  tags: string[][]\n  content: string\n  created_at: number\n  pubkey: string\n  id: string\n  sig: string\n  [verifiedSymbol]?: boolean\n}\n\nexport type Event = NostrEvent\nexport type EventTemplate = Pick<Event, 'kind' | 'tags' | 'content' | 'created_at'>\nexport type UnsignedEvent = Pick<Event, 'kind' | 'tags' | 'content' | 'created_at' | 'pubkey'>\n\n/** An event whose signature has been verified. */\nexport interface VerifiedEvent extends Event {\n  [verifiedSymbol]: true\n}\n\nconst isRecord = (obj: unknown): obj is Record<string, unknown> => obj instanceof Object\n\nexport function validateEvent<T>(event: T): event is T & UnsignedEvent {\n  if (!isRecord(event)) return false\n  if (typeof event.kind !== 'number') return false\n  if (typeof event.content !== 'string') return false\n  if (typeof event.created_at !== 'number') return false\n  if (typeof event.pubkey !== 'string') return false\n  if (!event.pubkey.match(/^[a-f0-9]{64}$/)) return false\n\n  if (!Array.isArray(event.tags)) return false\n  for (let i = 0; i < event.tags.length; i++) {\n    let tag = event.tags[i]\n    if (!Array.isArray(tag)) return false\n    for (let j = 0; j < tag.length; j++) {\n      if (typeof tag[j] !== 'string') return false\n    }\n  }\n\n  return true\n}\n\n/**\n * Sort events in reverse-chronological order by the `created_at` timestamp,\n * and then by the event `id` (lexicographically) in case of ties.\n * This mutates the array.\n */\nexport function sortEvents(events: Event[]): Event[] {\n  return events.sort((a: NostrEvent, b: NostrEvent): number => {\n    if (a.created_at !== b.created_at) {\n      return b.created_at - a.created_at\n    }\n    return a.id.localeCompare(b.id)\n  })\n}\n", "import type { Event } from './core.ts'\n\nexport const utf8Decoder: TextDecoder = new TextDecoder('utf-8')\nexport const utf8Encoder: TextEncoder = new TextEncoder()\n\nexport { bytesToHex, hexToBytes } from '@noble/hashes/utils'\n\nexport function normalizeURL(url: string): string {\n  try {\n    if (url.indexOf('://') === -1) url = 'wss://' + url\n    let p = new URL(url)\n    if (p.protocol === 'http:') p.protocol = 'ws:'\n    else if (p.protocol === 'https:') p.protocol = 'wss:'\n    p.pathname = p.pathname.replace(/\\/+/g, '/')\n    if (p.pathname.endsWith('/')) p.pathname = p.pathname.slice(0, -1)\n    if ((p.port === '80' && p.protocol === 'ws:') || (p.port === '443' && p.protocol === 'wss:')) p.port = ''\n    p.searchParams.sort()\n    p.hash = ''\n    return p.toString()\n  } catch (e) {\n    throw new Error(`Invalid URL: ${url}`)\n  }\n}\n\nexport function insertEventIntoDescendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return b.created_at - event.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function insertEventIntoAscendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return event.created_at - b.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function binarySearch<T>(arr: T[], compare: (b: T) => number): [number, boolean] {\n  let start = 0\n  let end = arr.length - 1\n\n  while (start <= end) {\n    const mid = Math.floor((start + end) / 2)\n    const cmp = compare(arr[mid])\n\n    if (cmp === 0) {\n      return [mid, true]\n    }\n\n    if (cmp < 0) {\n      end = mid - 1\n    } else {\n      start = mid + 1\n    }\n  }\n\n  return [start, false]\n}\n\nexport class QueueNode<V> {\n  public value: V\n  public next: QueueNode<V> | null = null\n  public prev: QueueNode<V> | null = null\n\n  constructor(message: V) {\n    this.value = message\n  }\n}\n\nexport class Queue<V> {\n  public first: QueueNode<V> | null\n  public last: QueueNode<V> | null\n\n  constructor() {\n    this.first = null\n    this.last = null\n  }\n\n  enqueue(value: V): boolean {\n    const newNode = new QueueNode(value)\n    if (!this.last) {\n      // list is empty\n      this.first = newNode\n      this.last = newNode\n    } else if (this.last === this.first) {\n      // list has a single element\n      this.last = newNode\n      this.last.prev = this.first\n      this.first.next = newNode\n    } else {\n      // list has elements, add as last\n      newNode.prev = this.last\n      this.last.next = newNode\n      this.last = newNode\n    }\n    return true\n  }\n\n  dequeue(): V | null {\n    if (!this.first) return null\n\n    if (this.first === this.last) {\n      const target = this.first\n      this.first = null\n      this.last = null\n      return target.value\n    }\n\n    const target = this.first\n    this.first = target.next\n    if (this.first) {\n      this.first.prev = null // fix: clean up prev pointer\n    }\n\n    return target.value\n  }\n}\n", "import { chacha20 } from '@noble/ciphers/chacha'\nimport { equalBytes } from '@noble/ciphers/utils'\nimport { secp256k1 } from '@noble/curves/secp256k1'\nimport { extract as hkdf_extract, expand as hkdf_expand } from '@noble/hashes/hkdf'\nimport { hmac } from '@noble/hashes/hmac'\nimport { sha256 } from '@noble/hashes/sha256'\nimport { concatBytes, randomBytes } from '@noble/hashes/utils'\nimport { base64 } from '@scure/base'\n\nimport { utf8Decoder, utf8Encoder } from './utils.ts'\n\nconst minPlaintextSize = 0x0001 // 1b msg => padded to 32b\nconst maxPlaintextSize = 0xffff // 65535 (64kb-1) => padded to 64kb\n\nexport function getConversationKey(privkeyA: Uint8Array, pubkeyB: string): Uint8Array {\n  const sharedX = secp256k1.getSharedSecret(privkeyA, '02' + pubkeyB).subarray(1, 33)\n  return hkdf_extract(sha256, sharedX, 'nip44-v2')\n}\n\nfunction getMessageKeys(\n  conversationKey: Uint8Array,\n  nonce: Uint8Array,\n): { chacha_key: Uint8Array; chacha_nonce: Uint8Array; hmac_key: Uint8Array } {\n  const keys = hkdf_expand(sha256, conversationKey, nonce, 76)\n  return {\n    chacha_key: keys.subarray(0, 32),\n    chacha_nonce: keys.subarray(32, 44),\n    hmac_key: keys.subarray(44, 76),\n  }\n}\n\nfunction calcPaddedLen(len: number): number {\n  if (!Number.isSafeInteger(len) || len < 1) throw new Error('expected positive integer')\n  if (len <= 32) return 32\n  const nextPower = 1 << (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 > maxPlaintextSize)\n    throw new Error('invalid plaintext size: must be between 1 and 65535 bytes')\n  const arr = new Uint8Array(2)\n  new DataView(arr.buffer).setUint16(0, num, false)\n  return arr\n}\n\nfunction pad(plaintext: string): Uint8Array {\n  const unpadded = utf8Encoder.encode(plaintext)\n  const unpaddedLen = unpadded.length\n  const prefix = 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 unpaddedLen = new DataView(padded.buffer).getUint16(0)\n  const unpadded = padded.subarray(2, 2 + unpaddedLen)\n  if (\n    unpaddedLen < minPlaintextSize ||\n    unpaddedLen > maxPlaintextSize ||\n    unpadded.length !== unpaddedLen ||\n    padded.length !== 2 + calcPaddedLen(unpaddedLen)\n  )\n    throw new Error('invalid padding')\n  return utf8Decoder.decode(unpadded)\n}\n\nfunction hmacAad(key: Uint8Array, message: Uint8Array, aad: Uint8Array): Uint8Array {\n  if (aad.length !== 32) throw new Error('AAD associated data must be 32 bytes')\n  const combined = concatBytes(aad, message)\n  return hmac(sha256, key, combined)\n}\n\n// metadata: always 65b (version: 1b, nonce: 32b, max: 32b)\n// plaintext: 1b to 0xffff\n// padded plaintext: 32b to 0xffff\n// ciphertext: 32b+2 to 0xffff+2\n// raw payload: 99 (65+32+2) to 65603 (65+0xffff+2)\n// compressed payload (base64): 132b to 87472b\nfunction decodePayload(payload: string): { nonce: Uint8Array; ciphertext: Uint8Array; mac: Uint8Array } {\n  if (typeof payload !== 'string') throw new Error('payload must be a valid string')\n  const plen = payload.length\n  if (plen < 132 || plen > 87472) throw new Error('invalid payload length: ' + plen)\n  if (payload[0] === '#') throw new Error('unknown encryption version')\n  let data: Uint8Array\n  try {\n    data = base64.decode(payload)\n  } catch (error) {\n    throw new Error('invalid base64: ' + (error as any).message)\n  }\n  const dlen = data.length\n  if (dlen < 99 || dlen > 65603) throw new Error('invalid data length: ' + dlen)\n  const vers = data[0]\n  if (vers !== 2) throw new Error('unknown encryption version ' + vers)\n  return {\n    nonce: data.subarray(1, 33),\n    ciphertext: data.subarray(33, -32),\n    mac: data.subarray(-32),\n  }\n}\n\nexport function encrypt(plaintext: string, conversationKey: Uint8Array, nonce: Uint8Array = randomBytes(32)): string {\n  const { chacha_key, chacha_nonce, hmac_key } = getMessageKeys(conversationKey, nonce)\n  const padded = pad(plaintext)\n  const ciphertext = chacha20(chacha_key, chacha_nonce, padded)\n  const mac = hmacAad(hmac_key, ciphertext, nonce)\n  return base64.encode(concatBytes(new Uint8Array([2]), nonce, ciphertext, mac))\n}\n\nexport function decrypt(payload: string, conversationKey: Uint8Array): string {\n  const { nonce, ciphertext, mac } = decodePayload(payload)\n  const { chacha_key, chacha_nonce, hmac_key } = getMessageKeys(conversationKey, nonce)\n  const calculatedMac = hmacAad(hmac_key, ciphertext, nonce)\n  if (!equalBytes(calculatedMac, mac)) throw new Error('invalid MAC')\n  const padded = chacha20(chacha_key, chacha_nonce, ciphertext)\n  return unpad(padded)\n}\n\nexport const v2 = {\n  utils: {\n    getConversationKey,\n    calcPaddedLen,\n  },\n  encrypt,\n  decrypt,\n}\n", "import { ProfilePointer } from './nip19.ts'\n\nexport type Nip05 = `${string}@${string}`\n\n/**\n * NIP-05 regex. The localpart is optional, and should be assumed to be `_` otherwise.\n *\n * - 0: full match\n * - 1: name (optional)\n * - 2: domain\n */\nexport const NIP05_REGEX = /^(?:([\\w.+-]+)@)?([\\w_-]+(\\.[\\w_-]+)+)$/\nexport const isNip05 = (value?: string | null): value is Nip05 => NIP05_REGEX.test(value || '')\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nlet _fetch: any\n\ntry {\n  _fetch = fetch\n} catch (_) {\n  null\n}\n\nexport function useFetchImplementation(fetchImplementation: unknown) {\n  _fetch = fetchImplementation\n}\n\nexport async function searchDomain(domain: string, query = ''): Promise<{ [name: string]: string }> {\n  try {\n    const url = `https://${domain}/.well-known/nostr.json?name=${query}`\n    const res = await _fetch(url, { redirect: 'manual' })\n    if (res.status !== 200) {\n      throw Error('Wrong response code')\n    }\n    const json = await res.json()\n    return json.names\n  } catch (_) {\n    return {}\n  }\n}\n\nexport async function queryProfile(fullname: string): Promise<ProfilePointer | null> {\n  const match = fullname.match(NIP05_REGEX)\n  if (!match) return null\n\n  const [, name = '_', domain] = match\n\n  try {\n    const url = `https://${domain}/.well-known/nostr.json?name=${name}`\n    const res = await _fetch(url, { redirect: 'manual' })\n    if (res.status !== 200) {\n      throw Error('Wrong response code')\n    }\n    const json = await res.json()\n\n    const pubkey = json.names[name]\n    return pubkey ? { pubkey, relays: json.relays?.[pubkey] } : null\n  } catch (_e) {\n    return null\n  }\n}\n\nexport async function isValid(pubkey: string, nip05: Nip05): Promise<boolean> {\n  const res = await queryProfile(nip05)\n  return res ? res.pubkey === pubkey : false\n}\n", "import { NostrEvent, validateEvent } from './pure.ts'\n\n/** Events are **regular**, which means they're all expected to be stored by relays. */\nexport function isRegularKind(kind: number): boolean {\n  return kind < 10000 && kind !== 0 && kind !== 3\n}\n\n/** Events are **replaceable**, which means that, for each combination of `pubkey` and `kind`, only the latest event is expected to (SHOULD) be stored by relays, older versions are expected to be discarded. */\nexport function isReplaceableKind(kind: number): boolean {\n  return kind === 0 || kind === 3 || (10000 <= kind && kind < 20000)\n}\n\n/** Events are **ephemeral**, which means they are not expected to be stored by relays. */\nexport function isEphemeralKind(kind: number): boolean {\n  return 20000 <= kind && kind < 30000\n}\n\n/** Events are **addressable**, which means that, for each combination of `pubkey`, `kind` and the `d` tag, only the latest event is expected to be stored by relays, older versions are expected to be discarded. */\nexport function isAddressableKind(kind: number): boolean {\n  return 30000 <= kind && kind < 40000\n}\n\n/** Classification of the event kind. */\nexport type KindClassification = 'regular' | 'replaceable' | 'ephemeral' | 'parameterized' | 'unknown'\n\n/** Determine the classification of this kind of event if known, or `unknown`. */\nexport function classifyKind(kind: number): KindClassification {\n  if (isRegularKind(kind)) return 'regular'\n  if (isReplaceableKind(kind)) return 'replaceable'\n  if (isEphemeralKind(kind)) return 'ephemeral'\n  if (isAddressableKind(kind)) return 'parameterized'\n  return 'unknown'\n}\n\nexport function isKind<T extends number>(event: unknown, kind: T | Array<T>): event is NostrEvent & { kind: T } {\n  const kindAsArray: number[] = kind instanceof Array ? kind : [kind]\n  return (validateEvent(event) && kindAsArray.includes(event.kind)) || false\n}\n\nexport const Metadata = 0\nexport type Metadata = typeof Metadata\nexport const ShortTextNote = 1\nexport type ShortTextNote = typeof ShortTextNote\nexport const RecommendRelay = 2\nexport type RecommendRelay = typeof RecommendRelay\nexport const Contacts = 3\nexport type Contacts = typeof Contacts\nexport const EncryptedDirectMessage = 4\nexport type EncryptedDirectMessage = typeof EncryptedDirectMessage\nexport const EventDeletion = 5\nexport type EventDeletion = typeof EventDeletion\nexport const Repost = 6\nexport type Repost = typeof Repost\nexport const Reaction = 7\nexport type Reaction = typeof Reaction\nexport const BadgeAward = 8\nexport type BadgeAward = typeof BadgeAward\nexport const ChatMessage = 9\nexport type ChatMessage = typeof ChatMessage\nexport const ForumThread = 11\nexport type ForumThread = typeof ForumThread\nexport const Seal = 13\nexport type Seal = typeof Seal\nexport const PrivateDirectMessage = 14\nexport type PrivateDirectMessage = typeof PrivateDirectMessage\nexport const FileMessage = 15\nexport type FileMessage = typeof FileMessage\nexport const GenericRepost = 16\nexport type GenericRepost = typeof GenericRepost\nexport const Photo = 20\nexport type Photo = typeof Photo\nexport const NormalVideo = 21\nexport type NormalVideo = typeof NormalVideo\nexport const ShortVideo = 22\nexport type ShortVideo = typeof ShortVideo\nexport const ChannelCreation = 40\nexport type ChannelCreation = typeof ChannelCreation\nexport const ChannelMetadata = 41\nexport type ChannelMetadata = typeof ChannelMetadata\nexport const ChannelMessage = 42\nexport type ChannelMessage = typeof ChannelMessage\nexport const ChannelHideMessage = 43\nexport type ChannelHideMessage = typeof ChannelHideMessage\nexport const ChannelMuteUser = 44\nexport type ChannelMuteUser = typeof ChannelMuteUser\nexport const OpenTimestamps = 1040\nexport type OpenTimestamps = typeof OpenTimestamps\nexport const GiftWrap = 1059\nexport type GiftWrap = typeof GiftWrap\nexport const Poll = 1068\nexport type Poll = typeof Poll\nexport const FileMetadata = 1063\nexport type FileMetadata = typeof FileMetadata\nexport const Comment = 1111\nexport type Comment = typeof Comment\nexport const LiveChatMessage = 1311\nexport type LiveChatMessage = typeof LiveChatMessage\nexport const Voice = 1222\nexport type Voice = typeof Voice\nexport const VoiceComment = 1244\nexport type VoiceComment = typeof VoiceComment\nexport const ProblemTracker = 1971\nexport type ProblemTracker = typeof ProblemTracker\nexport const Report = 1984\nexport type Report = typeof Report\nexport const Reporting = 1984\nexport type Reporting = typeof Reporting\nexport const Label = 1985\nexport type Label = typeof Label\nexport const CommunityPostApproval = 4550\nexport type CommunityPostApproval = typeof CommunityPostApproval\nexport const JobRequest = 5999\nexport type JobRequest = typeof JobRequest\nexport const JobResult = 6999\nexport type JobResult = typeof JobResult\nexport const JobFeedback = 7000\nexport type JobFeedback = typeof JobFeedback\nexport const ZapGoal = 9041\nexport type ZapGoal = typeof ZapGoal\nexport const ZapRequest = 9734\nexport type ZapRequest = typeof ZapRequest\nexport const Zap = 9735\nexport type Zap = typeof Zap\nexport const Highlights = 9802\nexport type Highlights = typeof Highlights\nexport const PollResponse = 1018\nexport type PollResponse = typeof PollResponse\nexport const Mutelist = 10000\nexport type Mutelist = typeof Mutelist\nexport const Pinlist = 10001\nexport type Pinlist = typeof Pinlist\nexport const RelayList = 10002\nexport type RelayList = typeof RelayList\nexport const BookmarkList = 10003\nexport type BookmarkList = typeof BookmarkList\nexport const CommunitiesList = 10004\nexport type CommunitiesList = typeof CommunitiesList\nexport const PublicChatsList = 10005\nexport type PublicChatsList = typeof PublicChatsList\nexport const BlockedRelaysList = 10006\nexport type BlockedRelaysList = typeof BlockedRelaysList\nexport const SearchRelaysList = 10007\nexport type SearchRelaysList = typeof SearchRelaysList\nexport const FavoriteRelays = 10012\nexport type FavoriteRelays = typeof FavoriteRelays\nexport const InterestsList = 10015\nexport type InterestsList = typeof InterestsList\nexport const UserEmojiList = 10030\nexport type UserEmojiList = typeof UserEmojiList\nexport const DirectMessageRelaysList = 10050\nexport type DirectMessageRelaysList = typeof DirectMessageRelaysList\nexport const FileServerPreference = 10096\nexport type FileServerPreference = typeof FileServerPreference\nexport const BlossomServerList = 10063\nexport type BlossomServerList = typeof BlossomServerList\nexport const NWCWalletInfo = 13194\nexport type NWCWalletInfo = typeof NWCWalletInfo\nexport const LightningPubRPC = 21000\nexport type LightningPubRPC = typeof LightningPubRPC\nexport const ClientAuth = 22242\nexport type ClientAuth = typeof ClientAuth\nexport const NWCWalletRequest = 23194\nexport type NWCWalletRequest = typeof NWCWalletRequest\nexport const NWCWalletResponse = 23195\nexport type NWCWalletResponse = typeof NWCWalletResponse\nexport const NostrConnect = 24133\nexport type NostrConnect = typeof NostrConnect\nexport const HTTPAuth = 27235\nexport type HTTPAuth = typeof HTTPAuth\nexport const Followsets = 30000\nexport type Followsets = typeof Followsets\nexport const Genericlists = 30001\nexport type Genericlists = typeof Genericlists\nexport const Relaysets = 30002\nexport type Relaysets = typeof Relaysets\nexport const Bookmarksets = 30003\nexport type Bookmarksets = typeof Bookmarksets\nexport const Curationsets = 30004\nexport type Curationsets = typeof Curationsets\nexport const ProfileBadges = 30008\nexport type ProfileBadges = typeof ProfileBadges\nexport const BadgeDefinition = 30009\nexport type BadgeDefinition = typeof BadgeDefinition\nexport const Interestsets = 30015\nexport type Interestsets = typeof Interestsets\nexport const CreateOrUpdateStall = 30017\nexport type CreateOrUpdateStall = typeof CreateOrUpdateStall\nexport const CreateOrUpdateProduct = 30018\nexport type CreateOrUpdateProduct = typeof CreateOrUpdateProduct\nexport const LongFormArticle = 30023\nexport type LongFormArticle = typeof LongFormArticle\nexport const DraftLong = 30024\nexport type DraftLong = typeof DraftLong\nexport const Emojisets = 30030\nexport type Emojisets = typeof Emojisets\nexport const Application = 30078\nexport type Application = typeof Application\nexport const LiveEvent = 30311\nexport type LiveEvent = typeof LiveEvent\nexport const UserStatuses = 30315\nexport type UserStatuses = typeof UserStatuses\nexport const ClassifiedListing = 30402\nexport type ClassifiedListing = typeof ClassifiedListing\nexport const DraftClassifiedListing = 30403\nexport type DraftClassifiedListing = typeof DraftClassifiedListing\nexport const Date = 31922\nexport type Date = typeof Date\nexport const Time = 31923\nexport type Time = typeof Time\nexport const Calendar = 31924\nexport type Calendar = typeof Calendar\nexport const CalendarEventRSVP = 31925\nexport type CalendarEventRSVP = typeof CalendarEventRSVP\nexport const RelayReview = 31987\nexport type RelayReview = typeof RelayReview\nexport const Handlerrecommendation = 31989\nexport type Handlerrecommendation = typeof Handlerrecommendation\nexport const Handlerinformation = 31990\nexport type Handlerinformation = typeof Handlerinformation\nexport const CommunityDefinition = 34550\nexport type CommunityDefinition = typeof CommunityDefinition\nexport const GroupMetadata = 39000\nexport type GroupMetadata = typeof GroupMetadata\n", "import { Event } from './core.ts'\nimport { isAddressableKind, isReplaceableKind } from './kinds.ts'\n\nexport type Filter = {\n  ids?: string[]\n  kinds?: number[]\n  authors?: string[]\n  since?: number\n  until?: number\n  limit?: number\n  search?: string\n  [key: `#${string}`]: string[] | undefined\n}\n\nexport function matchFilter(filter: Filter, event: Event): boolean {\n  if (filter.ids && filter.ids.indexOf(event.id) === -1) {\n    return false\n  }\n  if (filter.kinds && filter.kinds.indexOf(event.kind) === -1) {\n    return false\n  }\n  if (filter.authors && filter.authors.indexOf(event.pubkey) === -1) {\n    return false\n  }\n\n  for (let f in filter) {\n    if (f[0] === '#') {\n      let tagName = f.slice(1)\n      let values = filter[`#${tagName}`]\n      if (values && !event.tags.find(([t, v]) => t === f.slice(1) && values!.indexOf(v) !== -1)) return false\n    }\n  }\n\n  if (filter.since && event.created_at < filter.since) return false\n  if (filter.until && event.created_at > filter.until) return false\n\n  return true\n}\n\nexport function matchFilters(filters: Filter[], event: Event): boolean {\n  for (let i = 0; i < filters.length; i++) {\n    if (matchFilter(filters[i], event)) {\n      return true\n    }\n  }\n  return false\n}\n\nexport function mergeFilters(...filters: Filter[]): Filter {\n  let result: Filter = {}\n  for (let i = 0; i < filters.length; i++) {\n    let filter = filters[i]\n    Object.entries(filter).forEach(([property, values]) => {\n      if (property === 'kinds' || property === 'ids' || property === 'authors' || property[0] === '#') {\n        // @ts-ignore\n        result[property] = result[property] || []\n        // @ts-ignore\n        for (let v = 0; v < values.length; v++) {\n          // @ts-ignore\n          let value = values[v]\n          // @ts-ignore\n          if (!result[property].includes(value)) result[property].push(value)\n        }\n      }\n    })\n\n    if (filter.limit && (!result.limit || filter.limit > result.limit)) result.limit = filter.limit\n    if (filter.until && (!result.until || filter.until > result.until)) result.until = filter.until\n    if (filter.since && (!result.since || filter.since < result.since)) result.since = filter.since\n  }\n\n  return result\n}\n\n/**\n * Calculate the intrinsic limit of a filter.\n * This function returns a positive integer, or `Infinity` if there is no intrinsic limit.\n */\nexport function getFilterLimit(filter: Filter): number {\n  if (filter.ids && !filter.ids.length) return 0\n  if (filter.kinds && !filter.kinds.length) return 0\n  if (filter.authors && !filter.authors.length) return 0\n\n  for (const [key, value] of Object.entries(filter)) {\n    if (key[0] === '#' && Array.isArray(value) && !value.length) return 0\n  }\n\n  return Math.min(\n    // The `limit` property creates an artificial limit.\n    Math.max(0, filter.limit ?? Infinity),\n\n    // There can only be one event per `id`.\n    filter.ids?.length ?? Infinity,\n\n    // Replaceable events are limited by the number of authors and kinds.\n    filter.authors?.length && filter.kinds?.every(kind => isReplaceableKind(kind))\n      ? filter.authors.length * filter.kinds.length\n      : Infinity,\n\n    // Parameterized replaceable events are limited by the number of authors, kinds, and \"d\" tags.\n    filter.authors?.length && filter.kinds?.every(kind => isAddressableKind(kind)) && filter['#d']?.length\n      ? filter.authors.length * filter.kinds.length * filter['#d'].length\n      : Infinity,\n  )\n}\n", "export function getHex64(json: string, field: string): string {\n  let len = field.length + 3\n  let idx = json.indexOf(`\"${field}\":`) + len\n  let s = json.slice(idx).indexOf(`\"`) + idx + 1\n  return json.slice(s, s + 64)\n}\n\nexport function getInt(json: string, field: string): number {\n  let len = field.length\n  let idx = json.indexOf(`\"${field}\":`) + len + 3\n  let sliced = json.slice(idx)\n  let end = Math.min(sliced.indexOf(','), sliced.indexOf('}'))\n  return parseInt(sliced.slice(0, end), 10)\n}\n\nexport function getSubscriptionId(json: string): string | null {\n  let idx = json.slice(0, 22).indexOf(`\"EVENT\"`)\n  if (idx === -1) return null\n\n  let pstart = json.slice(idx + 7 + 1).indexOf(`\"`)\n  if (pstart === -1) return null\n  let start = idx + 7 + 1 + pstart\n\n  let pend = json.slice(start + 1, 80).indexOf(`\"`)\n  if (pend === -1) return null\n  let end = start + 1 + pend\n\n  return json.slice(start + 1, end)\n}\n\nexport function matchEventId(json: string, id: string): boolean {\n  return id === getHex64(json, 'id')\n}\n\nexport function matchEventPubkey(json: string, pubkey: string): boolean {\n  return pubkey === getHex64(json, 'pubkey')\n}\n\nexport function matchEventKind(json: string, kind: number): boolean {\n  return kind === getInt(json, 'kind')\n}\n", "import { EventTemplate } from './core.ts'\nimport { ClientAuth } from './kinds.ts'\n\n/**\n * creates an EventTemplate for an AUTH event to be signed.\n */\nexport function makeAuthEvent(relayURL: string, challenge: string): EventTemplate {\n  return {\n    kind: ClientAuth,\n    created_at: Math.floor(Date.now() / 1000),\n    tags: [\n      ['relay', relayURL],\n      ['challenge', challenge],\n    ],\n    content: '',\n  }\n}\n", "import { verifiedSymbol, type Event, type Nostr, VerifiedEvent } from './core.ts'\n\nexport async function yieldThread() {\n  return new Promise<void>((resolve, reject) => {\n    try {\n      // Check if MessageChannel is available\n      if (typeof MessageChannel !== 'undefined') {\n        const ch = new MessageChannel()\n        const handler = () => {\n          // @ts-ignore (typescript thinks this property should be called `removeListener`, but in fact it's `removeEventListener`)\n          ch.port1.removeEventListener('message', handler)\n          resolve()\n        }\n        // @ts-ignore (typescript thinks this property should be called `addListener`, but in fact it's `addEventListener`)\n        ch.port1.addEventListener('message', handler)\n        ch.port2.postMessage(0)\n        ch.port1.start()\n      } else {\n        if (typeof setImmediate !== 'undefined') {\n          setImmediate(resolve)\n        } else if (typeof setTimeout !== 'undefined') {\n          setTimeout(resolve, 0)\n        } else {\n          // Last resort - resolve immediately\n          resolve()\n        }\n      }\n    } catch (e) {\n      console.error('during yield: ', e)\n      reject(e)\n    }\n  })\n}\n\nexport const alwaysTrue: Nostr['verifyEvent'] = (t: Event): t is VerifiedEvent => {\n  t[verifiedSymbol] = true\n  return true\n}\n", "/* global WebSocket */\n\nimport type { Event, EventTemplate, VerifiedEvent, Nostr, NostrEvent } from './core.ts'\nimport { matchFilters, type Filter } from './filter.ts'\nimport { getHex64, getSubscriptionId } from './fakejson.ts'\nimport { Queue, normalizeURL } from './utils.ts'\nimport { makeAuthEvent } from './nip42.ts'\nimport { yieldThread } from './helpers.ts'\n\ntype RelayWebSocket = WebSocket & {\n  ping?(): void\n  on?(event: 'pong', listener: () => void): any\n}\n\nexport type AbstractRelayConstructorOptions = {\n  verifyEvent: Nostr['verifyEvent']\n  websocketImplementation?: typeof WebSocket\n  enablePing?: boolean\n  enableReconnect?: boolean\n}\n\nexport class SendingOnClosedConnection extends Error {\n  constructor(message: string, relay: string) {\n    super(`Tried to send message '${message} on a closed connection to ${relay}.`)\n    this.name = 'SendingOnClosedConnection'\n  }\n}\n\nexport class AbstractRelay {\n  public readonly url: string\n  private _connected: boolean = false\n\n  public onclose: (() => void) | null = null\n  public onnotice: (msg: string) => void = msg => console.debug(`NOTICE from ${this.url}: ${msg}`)\n  public onauth: undefined | ((evt: EventTemplate) => Promise<VerifiedEvent>)\n\n  public baseEoseTimeout: number = 4400\n  public publishTimeout: number = 4400\n  public pingFrequency: number = 29000\n  public pingTimeout: number = 20000\n  public resubscribeBackoff: number[] = [10000, 10000, 10000, 20000, 20000, 30000, 60000]\n  public openSubs: Map<string, Subscription> = new Map()\n  public enablePing: boolean | undefined\n  public enableReconnect: boolean\n  private reconnectTimeoutHandle: ReturnType<typeof setTimeout> | undefined\n  private pingIntervalHandle: ReturnType<typeof setInterval> | undefined\n  private reconnectAttempts: number = 0\n  private closedIntentionally: boolean = false\n\n  private connectionPromise: Promise<void> | undefined\n  private openCountRequests = new Map<string, CountResolver>()\n  private openEventPublishes = new Map<string, EventPublishResolver>()\n  private ws: RelayWebSocket | undefined\n  private incomingMessageQueue = new Queue<string>()\n  private queueRunning = false\n  private challenge: string | undefined\n  private authPromise: Promise<string> | undefined\n  private serial: number = 0\n  private verifyEvent: Nostr['verifyEvent']\n\n  private _WebSocket: typeof WebSocket\n\n  constructor(url: string, opts: AbstractRelayConstructorOptions) {\n    this.url = normalizeURL(url)\n    this.verifyEvent = opts.verifyEvent\n    this._WebSocket = opts.websocketImplementation || WebSocket\n    this.enablePing = opts.enablePing\n    this.enableReconnect = opts.enableReconnect || false\n  }\n\n  static async connect(\n    url: string,\n    opts: AbstractRelayConstructorOptions & Parameters<AbstractRelay['connect']>[0],\n  ): Promise<AbstractRelay> {\n    const relay = new AbstractRelay(url, opts)\n    await relay.connect(opts)\n    return relay\n  }\n\n  private closeAllSubscriptions(reason: string) {\n    for (let [_, sub] of this.openSubs) {\n      sub.close(reason)\n    }\n    this.openSubs.clear()\n\n    for (let [_, ep] of this.openEventPublishes) {\n      ep.reject(new Error(reason))\n    }\n    this.openEventPublishes.clear()\n\n    for (let [_, cr] of this.openCountRequests) {\n      cr.reject(new Error(reason))\n    }\n    this.openCountRequests.clear()\n  }\n\n  public get connected(): boolean {\n    return this._connected\n  }\n\n  private async reconnect(): Promise<void> {\n    const backoff = this.resubscribeBackoff[Math.min(this.reconnectAttempts, this.resubscribeBackoff.length - 1)]\n    this.reconnectAttempts++\n\n    this.reconnectTimeoutHandle = setTimeout(async () => {\n      try {\n        await this.connect()\n      } catch (err) {\n        // this will be called again through onclose/onerror\n      }\n    }, backoff)\n  }\n\n  private handleHardClose(reason: string) {\n    if (this.pingIntervalHandle) {\n      clearInterval(this.pingIntervalHandle)\n      this.pingIntervalHandle = undefined\n    }\n\n    this._connected = false\n    this.connectionPromise = undefined\n\n    const wasIntentional = this.closedIntentionally\n    this.closedIntentionally = false // reset for next time\n\n    this.onclose?.()\n\n    if (this.enableReconnect && !wasIntentional) {\n      this.reconnect()\n    } else {\n      this.closeAllSubscriptions(reason)\n    }\n  }\n\n  public async connect(opts?: { timeout?: number; abort?: AbortSignal }): Promise<void> {\n    let connectionTimeoutHandle: ReturnType<typeof setTimeout> | undefined\n\n    if (this.connectionPromise) return this.connectionPromise\n\n    this.challenge = undefined\n    this.authPromise = undefined\n    this.connectionPromise = new Promise((resolve, reject) => {\n      if (opts?.timeout) {\n        connectionTimeoutHandle = setTimeout(() => {\n          reject('connection timed out')\n          this.connectionPromise = undefined\n          this.onclose?.()\n          this.closeAllSubscriptions('relay connection timed out')\n        }, opts.timeout)\n      }\n\n      if (opts?.abort) {\n        opts.abort.onabort = reject\n      }\n\n      try {\n        this.ws = new this._WebSocket(this.url)\n      } catch (err) {\n        clearTimeout(connectionTimeoutHandle)\n        reject(err)\n        return\n      }\n\n      this.ws.onopen = () => {\n        if (this.reconnectTimeoutHandle) {\n          clearTimeout(this.reconnectTimeoutHandle)\n          this.reconnectTimeoutHandle = undefined\n        }\n        clearTimeout(connectionTimeoutHandle)\n        this._connected = true\n\n        const isReconnection = this.reconnectAttempts > 0\n        this.reconnectAttempts = 0\n\n        // resubscribe to all open subscriptions\n        for (const sub of this.openSubs.values()) {\n          sub.eosed = false\n          if (isReconnection) {\n            for (let f = 0; f < sub.filters.length; f++) {\n              if (sub.lastEmitted) {\n                sub.filters[f].since = sub.lastEmitted + 1\n              }\n            }\n          }\n          sub.fire()\n        }\n\n        if (this.enablePing) {\n          this.pingIntervalHandle = setInterval(() => this.pingpong(), this.pingFrequency)\n        }\n        resolve()\n      }\n\n      this.ws.onerror = ev => {\n        clearTimeout(connectionTimeoutHandle)\n        reject((ev as any).message || 'websocket error')\n        this.handleHardClose('relay connection errored')\n      }\n\n      this.ws.onclose = ev => {\n        clearTimeout(connectionTimeoutHandle)\n        reject((ev as any).message || 'websocket closed')\n        this.handleHardClose('relay connection closed')\n      }\n\n      this.ws.onmessage = this._onmessage.bind(this)\n    })\n\n    return this.connectionPromise\n  }\n\n  private waitForPingPong() {\n    return new Promise(resolve => {\n      // listen for pong\n      ;(this.ws as any).once('pong', () => resolve(true))\n      // send a ping\n      this.ws!.ping!()\n    })\n  }\n\n  private waitForDummyReq() {\n    return new Promise((resolve, reject) => {\n      if (!this.connectionPromise) return reject(new Error(`no connection to ${this.url}, can't ping`))\n\n      // make a dummy request with expected empty eose reply\n      // [\"REQ\", \"_\", {\"ids\":[\"aaaa...aaaa\"], \"limit\": 0}]\n      try {\n        const sub = this.subscribe(\n          [{ ids: ['aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'], limit: 0 }],\n          {\n            label: 'forced-ping',\n            oneose: () => {\n              resolve(true)\n              sub.close()\n            },\n            onclose() {\n              // if we get a CLOSED it's because the relay is alive\n              resolve(true)\n            },\n            eoseTimeout: this.pingTimeout + 1000,\n          },\n        )\n      } catch (err) {\n        reject(err)\n      }\n    })\n  }\n\n  // nodejs requires this magic here to ensure connections are closed when internet goes off and stuff\n  // in browsers it's done automatically. see https://github.com/nbd-wtf/nostr-tools/issues/491\n  private async pingpong() {\n    // if the websocket is connected\n    if (this.ws?.readyState === 1) {\n      // wait for either a ping-pong reply or a timeout\n      const result = await Promise.any([\n        // browsers don't have ping so use a dummy req\n        this.ws && this.ws.ping && (this.ws as any).once ? this.waitForPingPong() : this.waitForDummyReq(),\n        new Promise(res => setTimeout(() => res(false), this.pingTimeout)),\n      ])\n\n      if (!result) {\n        // pingpong closing socket\n        if (this.ws?.readyState === this._WebSocket.OPEN) {\n          this.ws?.close()\n        }\n      }\n    }\n  }\n\n  private async runQueue() {\n    this.queueRunning = true\n    while (true) {\n      if (false === this.handleNext()) {\n        break\n      }\n      await yieldThread()\n    }\n    this.queueRunning = false\n  }\n\n  private handleNext(): undefined | false {\n    const json = this.incomingMessageQueue.dequeue()\n    if (!json) {\n      return false\n    }\n\n    // shortcut EVENT sub\n    const subid = getSubscriptionId(json)\n    if (subid) {\n      const so = this.openSubs.get(subid as string)\n      if (!so) {\n        // this is an EVENT message, but for a subscription we don't have, so just stop here\n        return\n      }\n\n      // this will be called only when this message is a EVENT message for a subscription we have\n      // we do this before parsing the JSON to not have to do that for duplicate events\n      //   since JSON parsing is slow\n      const id = getHex64(json, 'id')\n      const alreadyHave = so.alreadyHaveEvent?.(id)\n\n      // notify any interested client that the relay has this event\n      // (do this after alreadyHaveEvent() because the client may rely on this to answer that)\n      so.receivedEvent?.(this, id)\n\n      if (alreadyHave) {\n        // if we had already seen this event we can just stop here\n        return\n      }\n    }\n\n    try {\n      let data = JSON.parse(json)\n      // we won't do any checks against the data since all failures (i.e. invalid messages from relays)\n      // will naturally be caught by the encompassing try..catch block\n\n      switch (data[0]) {\n        case 'EVENT': {\n          const so = this.openSubs.get(data[1] as string) as Subscription\n          const event = data[2] as NostrEvent\n          if (this.verifyEvent(event) && matchFilters(so.filters, event)) {\n            so.onevent(event)\n          }\n          if (!so.lastEmitted || so.lastEmitted < event.created_at) so.lastEmitted = event.created_at\n          return\n        }\n        case 'COUNT': {\n          const id: string = data[1]\n          const payload = data[2] as { count: number }\n          const cr = this.openCountRequests.get(id) as CountResolver\n          if (cr) {\n            cr.resolve(payload.count)\n            this.openCountRequests.delete(id)\n          }\n          return\n        }\n        case 'EOSE': {\n          const so = this.openSubs.get(data[1] as string)\n          if (!so) return\n          so.receivedEose()\n          return\n        }\n        case 'OK': {\n          const id: string = data[1]\n          const ok: boolean = data[2]\n          const reason: string = data[3]\n          const ep = this.openEventPublishes.get(id) as EventPublishResolver\n          if (ep) {\n            clearTimeout(ep.timeout)\n            if (ok) ep.resolve(reason)\n            else ep.reject(new Error(reason))\n            this.openEventPublishes.delete(id)\n          }\n          return\n        }\n        case 'CLOSED': {\n          const id: string = data[1]\n          const so = this.openSubs.get(id)\n          if (!so) return\n          so.closed = true\n          so.close(data[2] as string)\n          return\n        }\n        case 'NOTICE': {\n          this.onnotice(data[1] as string)\n          return\n        }\n        case 'AUTH': {\n          this.challenge = data[1] as string\n          if (this.onauth) {\n            this.auth(this.onauth)\n          }\n          return\n        }\n        default: {\n          const so = this.openSubs.get(data[1])\n          so?.oncustom?.(data)\n          return\n        }\n      }\n    } catch (err) {\n      return\n    }\n  }\n\n  public async send(message: string) {\n    if (!this.connectionPromise) throw new SendingOnClosedConnection(message, this.url)\n\n    this.connectionPromise.then(() => {\n      this.ws?.send(message)\n    })\n  }\n\n  public async auth(signAuthEvent: (evt: EventTemplate) => Promise<VerifiedEvent>): Promise<string> {\n    const challenge = this.challenge\n    if (!challenge) throw new Error(\"can't perform auth, no challenge was received\")\n    if (this.authPromise) return this.authPromise\n\n    this.authPromise = new Promise<string>(async (resolve, reject) => {\n      try {\n        let evt = await signAuthEvent(makeAuthEvent(this.url, challenge))\n        let timeout = setTimeout(() => {\n          let ep = this.openEventPublishes.get(evt.id) as EventPublishResolver\n          if (ep) {\n            ep.reject(new Error('auth timed out'))\n            this.openEventPublishes.delete(evt.id)\n          }\n        }, this.publishTimeout)\n        this.openEventPublishes.set(evt.id, { resolve, reject, timeout })\n        this.send('[\"AUTH\",' + JSON.stringify(evt) + ']')\n      } catch (err) {\n        console.warn('subscribe auth function failed:', err)\n      }\n    })\n    return this.authPromise\n  }\n\n  public async publish(event: Event): Promise<string> {\n    const ret = new Promise<string>((resolve, reject) => {\n      const timeout = setTimeout(() => {\n        const ep = this.openEventPublishes.get(event.id) as EventPublishResolver\n        if (ep) {\n          ep.reject(new Error('publish timed out'))\n          this.openEventPublishes.delete(event.id)\n        }\n      }, this.publishTimeout)\n      this.openEventPublishes.set(event.id, { resolve, reject, timeout })\n    })\n    this.send('[\"EVENT\",' + JSON.stringify(event) + ']')\n    return ret\n  }\n\n  public async count(filters: Filter[], params: { id?: string | null }): Promise<number> {\n    this.serial++\n    const id = params?.id || 'count:' + this.serial\n    const ret = new Promise<number>((resolve, reject) => {\n      this.openCountRequests.set(id, { resolve, reject })\n    })\n    this.send('[\"COUNT\",\"' + id + '\",' + JSON.stringify(filters).substring(1))\n    return ret\n  }\n\n  public subscribe(\n    filters: Filter[],\n    params: Partial<SubscriptionParams> & { label?: string; id?: string },\n  ): Subscription {\n    const sub = this.prepareSubscription(filters, params)\n    sub.fire()\n\n    if (params.abort) {\n      params.abort.onabort = () => sub.close(String(params.abort!.reason || '<aborted>'))\n    }\n\n    return sub\n  }\n\n  public prepareSubscription(\n    filters: Filter[],\n    params: Partial<SubscriptionParams> & { label?: string; id?: string },\n  ): Subscription {\n    this.serial++\n    const id = params.id || (params.label ? params.label + ':' : 'sub:') + this.serial\n    const subscription = new Subscription(this, id, filters, params)\n    this.openSubs.set(id, subscription)\n    return subscription\n  }\n\n  public close() {\n    this.closedIntentionally = true\n    if (this.reconnectTimeoutHandle) {\n      clearTimeout(this.reconnectTimeoutHandle)\n      this.reconnectTimeoutHandle = undefined\n    }\n    if (this.pingIntervalHandle) {\n      clearInterval(this.pingIntervalHandle)\n      this.pingIntervalHandle = undefined\n    }\n    this.closeAllSubscriptions('relay connection closed by us')\n    this._connected = false\n    this.onclose?.()\n    if (this.ws?.readyState === this._WebSocket.OPEN) {\n      this.ws?.close()\n    }\n  }\n\n  // this is the function assigned to this.ws.onmessage\n  // it's exposed for testing and debugging purposes\n  public _onmessage(ev: MessageEvent<any>) {\n    this.incomingMessageQueue.enqueue(ev.data as string)\n    if (!this.queueRunning) {\n      this.runQueue()\n    }\n  }\n}\n\nexport class Subscription {\n  public readonly relay: AbstractRelay\n  public readonly id: string\n\n  public lastEmitted: number | undefined\n  public closed: boolean = false\n  public eosed: boolean = false\n  public filters: Filter[]\n  public alreadyHaveEvent: ((id: string) => boolean) | undefined\n  public receivedEvent: ((relay: AbstractRelay, id: string) => void) | undefined\n\n  public onevent: (evt: Event) => void\n  public oneose: (() => void) | undefined\n  public onclose: ((reason: string) => void) | undefined\n\n  // will get any messages that have this subscription id as their second item and are not default standard\n  public oncustom: ((msg: string[]) => void) | undefined\n\n  public eoseTimeout: number\n  private eoseTimeoutHandle: ReturnType<typeof setTimeout> | undefined\n\n  constructor(relay: AbstractRelay, id: string, filters: Filter[], params: SubscriptionParams) {\n    if (filters.length === 0) throw new Error(\"subscription can't be created with zero filters\")\n\n    this.relay = relay\n    this.filters = filters\n    this.id = id\n    this.alreadyHaveEvent = params.alreadyHaveEvent\n    this.receivedEvent = params.receivedEvent\n    this.eoseTimeout = params.eoseTimeout || relay.baseEoseTimeout\n\n    this.oneose = params.oneose\n    this.onclose = params.onclose\n    this.onevent =\n      params.onevent ||\n      (event => {\n        console.warn(\n          `onevent() callback not defined for subscription '${this.id}' in relay ${this.relay.url}. event received:`,\n          event,\n        )\n      })\n  }\n\n  public fire() {\n    this.relay.send('[\"REQ\",\"' + this.id + '\",' + JSON.stringify(this.filters).substring(1))\n\n    // only now we start counting the eoseTimeout\n    this.eoseTimeoutHandle = setTimeout(this.receivedEose.bind(this), this.eoseTimeout)\n  }\n\n  public receivedEose() {\n    if (this.eosed) return\n    clearTimeout(this.eoseTimeoutHandle)\n    this.eosed = true\n    this.oneose?.()\n  }\n\n  public close(reason: string = 'closed by caller') {\n    if (!this.closed && this.relay.connected) {\n      // if the connection was closed by the user calling .close() we will send a CLOSE message\n      // otherwise this._open will be already set to false so we will skip this\n      try {\n        this.relay.send('[\"CLOSE\",' + JSON.stringify(this.id) + ']')\n      } catch (err) {\n        if (err instanceof SendingOnClosedConnection) {\n          /* doesn't matter, it's ok */\n        } else {\n          throw err\n        }\n      }\n      this.closed = true\n    }\n    this.relay.openSubs.delete(this.id)\n    this.onclose?.(reason)\n  }\n}\n\nexport type SubscriptionParams = {\n  onevent?: (evt: Event) => void\n  oneose?: () => void\n  onclose?: (reason: string) => void\n  alreadyHaveEvent?: (id: string) => boolean\n  receivedEvent?: (relay: AbstractRelay, id: string) => void\n  eoseTimeout?: number\n  abort?: AbortSignal\n}\n\nexport type CountResolver = {\n  resolve: (count: number) => void\n  reject: (err: Error) => void\n}\n\nexport type EventPublishResolver = {\n  resolve: (reason: string) => void\n  reject: (err: Error) => void\n  timeout: ReturnType<typeof setTimeout>\n}\n", "/* global WebSocket */\n\nimport {\n  AbstractRelay as AbstractRelay,\n  SubscriptionParams,\n  Subscription,\n  type AbstractRelayConstructorOptions,\n} from './abstract-relay.ts'\nimport { normalizeURL } from './utils.ts'\n\nimport type { Event, EventTemplate, Nostr, VerifiedEvent } from './core.ts'\nimport { type Filter } from './filter.ts'\nimport { alwaysTrue } from './helpers.ts'\n\nexport type SubCloser = { close: (reason?: string) => void }\n\nexport type AbstractPoolConstructorOptions = AbstractRelayConstructorOptions & {\n  // automaticallyAuth takes a relay URL and should return null\n  // in case that relay shouldn't be authenticated against\n  // or a function to sign the AUTH event template otherwise (that function may still throw in case of failure)\n  automaticallyAuth?: (relayURL: string) => null | ((event: EventTemplate) => Promise<VerifiedEvent>)\n}\n\nexport type SubscribeManyParams = Omit<SubscriptionParams, 'onclose'> & {\n  maxWait?: number\n  abort?: AbortSignal\n  onclose?: (reasons: string[]) => void\n  onauth?: (event: EventTemplate) => Promise<VerifiedEvent>\n  id?: string\n  label?: string\n}\n\nexport class AbstractSimplePool {\n  protected relays: Map<string, AbstractRelay> = new Map()\n  public seenOn: Map<string, Set<AbstractRelay>> = new Map()\n  public trackRelays: boolean = false\n\n  public verifyEvent: Nostr['verifyEvent']\n  public enablePing: boolean | undefined\n  public enableReconnect: boolean\n  public automaticallyAuth?: (relayURL: string) => null | ((event: EventTemplate) => Promise<VerifiedEvent>)\n  public trustedRelayURLs: Set<string> = new Set()\n\n  private _WebSocket?: typeof WebSocket\n\n  constructor(opts: AbstractPoolConstructorOptions) {\n    this.verifyEvent = opts.verifyEvent\n    this._WebSocket = opts.websocketImplementation\n    this.enablePing = opts.enablePing\n    this.enableReconnect = opts.enableReconnect || false\n    this.automaticallyAuth = opts.automaticallyAuth\n  }\n\n  async ensureRelay(\n    url: string,\n    params?: {\n      connectionTimeout?: number\n      abort?: AbortSignal\n    },\n  ): Promise<AbstractRelay> {\n    url = normalizeURL(url)\n\n    let relay = this.relays.get(url)\n    if (!relay) {\n      relay = new AbstractRelay(url, {\n        verifyEvent: this.trustedRelayURLs.has(url) ? alwaysTrue : this.verifyEvent,\n        websocketImplementation: this._WebSocket,\n        enablePing: this.enablePing,\n        enableReconnect: this.enableReconnect,\n      })\n      relay.onclose = () => {\n        if (relay && !relay.enableReconnect) {\n          this.relays.delete(url)\n        }\n      }\n      this.relays.set(url, relay)\n    }\n\n    if (this.automaticallyAuth) {\n      const authSignerFn = this.automaticallyAuth(url)\n      if (authSignerFn) {\n        relay.onauth = authSignerFn\n      }\n    }\n\n    await relay.connect({\n      timeout: params?.connectionTimeout,\n      abort: params?.abort,\n    })\n\n    return relay\n  }\n\n  close(relays: string[]) {\n    relays.map(normalizeURL).forEach(url => {\n      this.relays.get(url)?.close()\n      this.relays.delete(url)\n    })\n  }\n\n  subscribe(relays: string[], filter: Filter, params: SubscribeManyParams): SubCloser {\n    const request: { url: string; filter: Filter }[] = []\n    for (let i = 0; i < relays.length; i++) {\n      const url = normalizeURL(relays[i])\n      if (!request.find(r => r.url === url)) {\n        request.push({ url, filter: filter })\n      }\n    }\n\n    return this.subscribeMap(request, params)\n  }\n\n  subscribeMany(relays: string[], filter: Filter, params: SubscribeManyParams): SubCloser {\n    const request: { url: string; filter: Filter }[] = []\n    const uniqUrls: string[] = []\n    for (let i = 0; i < relays.length; i++) {\n      const url = normalizeURL(relays[i])\n      if (uniqUrls.indexOf(url) === -1) {\n        uniqUrls.push(url)\n        request.push({ url, filter: filter })\n      }\n    }\n\n    return this.subscribeMap(request, params)\n  }\n\n  subscribeMap(requests: { url: string; filter: Filter }[], params: SubscribeManyParams): SubCloser {\n    const grouped = new Map<string, Filter[]>()\n    for (const req of requests) {\n      const { url, filter } = req\n      if (!grouped.has(url)) grouped.set(url, [])\n      grouped.get(url)!.push(filter)\n    }\n    const groupedRequests = Array.from(grouped.entries()).map(([url, filters]) => ({ url, filters }))\n\n    if (this.trackRelays) {\n      params.receivedEvent = (relay: AbstractRelay, id: string) => {\n        let set = this.seenOn.get(id)\n        if (!set) {\n          set = new Set()\n          this.seenOn.set(id, set)\n        }\n        set.add(relay)\n      }\n    }\n\n    const _knownIds = new Set<string>()\n    const subs: Subscription[] = []\n\n    // batch all EOSEs into a single\n    const eosesReceived: boolean[] = []\n    let handleEose = (i: number) => {\n      if (eosesReceived[i]) return // do not act twice for the same relay\n      eosesReceived[i] = true\n      if (eosesReceived.filter(a => a).length === groupedRequests.length) {\n        params.oneose?.()\n        handleEose = () => {}\n      }\n    }\n    // batch all closes into a single\n    const closesReceived: string[] = []\n    let handleClose = (i: number, reason: string) => {\n      if (closesReceived[i]) return // do not act twice for the same relay\n      handleEose(i)\n      closesReceived[i] = reason\n      if (closesReceived.filter(a => a).length === groupedRequests.length) {\n        params.onclose?.(closesReceived)\n        handleClose = () => {}\n      }\n    }\n\n    const localAlreadyHaveEventHandler = (id: string) => {\n      if (params.alreadyHaveEvent?.(id)) {\n        return true\n      }\n      const have = _knownIds.has(id)\n      _knownIds.add(id)\n      return have\n    }\n\n    // open a subscription in all given relays\n    const allOpened = Promise.all(\n      groupedRequests.map(async ({ url, filters }, i) => {\n        let relay: AbstractRelay\n        try {\n          relay = await this.ensureRelay(url, {\n            connectionTimeout: params.maxWait ? Math.max(params.maxWait * 0.8, params.maxWait - 1000) : undefined,\n            abort: params.abort,\n          })\n        } catch (err) {\n          handleClose(i, (err as any)?.message || String(err))\n          return\n        }\n\n        let subscription = relay.subscribe(filters, {\n          ...params,\n          oneose: () => handleEose(i),\n          onclose: reason => {\n            if (reason.startsWith('auth-required: ') && params.onauth) {\n              relay\n                .auth(params.onauth)\n                .then(() => {\n                  relay.subscribe(filters, {\n                    ...params,\n                    oneose: () => handleEose(i),\n                    onclose: reason => {\n                      handleClose(i, reason) // the second time we won't try to auth anymore\n                    },\n                    alreadyHaveEvent: localAlreadyHaveEventHandler,\n                    eoseTimeout: params.maxWait,\n                    abort: params.abort,\n                  })\n                })\n                .catch(err => {\n                  handleClose(i, `auth was required and attempted, but failed with: ${err}`)\n                })\n            } else {\n              handleClose(i, reason)\n            }\n          },\n          alreadyHaveEvent: localAlreadyHaveEventHandler,\n          eoseTimeout: params.maxWait,\n          abort: params.abort,\n        })\n\n        subs.push(subscription)\n      }),\n    )\n\n    return {\n      async close(reason?: string) {\n        await allOpened\n        subs.forEach(sub => {\n          sub.close(reason)\n        })\n      },\n    }\n  }\n\n  subscribeEose(\n    relays: string[],\n    filter: Filter,\n    params: Pick<SubscribeManyParams, 'label' | 'id' | 'onevent' | 'onclose' | 'maxWait' | 'onauth'>,\n  ): SubCloser {\n    const subcloser = this.subscribe(relays, filter, {\n      ...params,\n      oneose() {\n        subcloser.close('closed automatically on eose')\n      },\n    })\n    return subcloser\n  }\n\n  subscribeManyEose(\n    relays: string[],\n    filter: Filter,\n    params: Pick<SubscribeManyParams, 'label' | 'id' | 'onevent' | 'onclose' | 'maxWait' | 'onauth'>,\n  ): SubCloser {\n    const subcloser = this.subscribeMany(relays, filter, {\n      ...params,\n      oneose() {\n        subcloser.close('closed automatically on eose')\n      },\n    })\n    return subcloser\n  }\n\n  async querySync(\n    relays: string[],\n    filter: Filter,\n    params?: Pick<SubscribeManyParams, 'label' | 'id' | 'maxWait'>,\n  ): Promise<Event[]> {\n    return new Promise(async resolve => {\n      const events: Event[] = []\n      this.subscribeEose(relays, filter, {\n        ...params,\n        onevent(event: Event) {\n          events.push(event)\n        },\n        onclose(_: string[]) {\n          resolve(events)\n        },\n      })\n    })\n  }\n\n  async get(\n    relays: string[],\n    filter: Filter,\n    params?: Pick<SubscribeManyParams, 'label' | 'id' | 'maxWait'>,\n  ): Promise<Event | null> {\n    filter.limit = 1\n    const events = await this.querySync(relays, filter, params)\n    events.sort((a, b) => b.created_at - a.created_at)\n    return events[0] || null\n  }\n\n  publish(\n    relays: string[],\n    event: Event,\n    options?: { onauth?: (evt: EventTemplate) => Promise<VerifiedEvent> },\n  ): Promise<string>[] {\n    return relays.map(normalizeURL).map(async (url, i, arr) => {\n      if (arr.indexOf(url) !== i) {\n        // duplicate\n        return Promise.reject('duplicate url')\n      }\n\n      let r = await this.ensureRelay(url)\n      return r\n        .publish(event)\n        .catch(async err => {\n          if (err instanceof Error && err.message.startsWith('auth-required: ') && options?.onauth) {\n            await r.auth(options.onauth)\n            return r.publish(event) // retry\n          }\n          throw err\n        })\n        .then(reason => {\n          if (this.trackRelays) {\n            let set = this.seenOn.get(event.id)\n            if (!set) {\n              set = new Set()\n              this.seenOn.set(event.id, set)\n            }\n            set.add(r)\n          }\n          return reason\n        })\n    })\n  }\n\n  listConnectionStatus(): Map<string, boolean> {\n    const map = new Map<string, boolean>()\n    this.relays.forEach((relay, url) => map.set(url, relay.connected))\n\n    return map\n  }\n\n  destroy(): void {\n    this.relays.forEach(conn => conn.close())\n    this.relays = new Map()\n  }\n}\n", "/* global WebSocket */\n\nimport { verifyEvent } from './pure.ts'\nimport { AbstractSimplePool, type AbstractPoolConstructorOptions } from './abstract-pool.ts'\n\nvar _WebSocket: typeof WebSocket\n\ntry {\n  _WebSocket = WebSocket\n} catch {}\n\nexport function useWebSocketImplementation(websocketImplementation: any) {\n  _WebSocket = websocketImplementation\n}\n\nexport class SimplePool extends AbstractSimplePool {\n  constructor(options?: Pick<AbstractPoolConstructorOptions, 'enablePing' | 'enableReconnect'>) {\n    super({ verifyEvent, websocketImplementation: _WebSocket, ...options })\n  }\n}\n\nexport * from './abstract-pool.ts'\n"],
-  "mappings": "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",
-  "names": ["import_utils", "i", "target", "import_utils", "import_secp256k1", "import_sha256", "hkdf_extract", "hkdf_expand", "i", "i", "reason", "_fetch", "resolve", "i"]
+  "sourcesContent": ["import { EventTemplate, NostrEvent, VerifiedEvent } from './core.ts'\nimport { generateSecretKey, finalizeEvent, getPublicKey, verifyEvent } from './pure.ts'\nimport { AbstractSimplePool, SubCloser } from './abstract-pool.ts'\nimport { getConversationKey, decrypt, encrypt } from './nip44.ts'\nimport { NIP05_REGEX } from './nip05.ts'\nimport { SimplePool } from './pool.ts'\nimport { Handlerinformation, NostrConnect } from './kinds.ts'\nimport { Signer } from './signer.ts'\n\nvar _fetch: any\n\ntry {\n  _fetch = fetch\n} catch {}\n\nexport function useFetchImplementation(fetchImplementation: any) {\n  _fetch = fetchImplementation\n}\n\nexport const BUNKER_REGEX = /^bunker:\\/\\/([0-9a-f]{64})\\??([?\\/\\w:.=&%-]*)$/\nconst EMAIL_REGEX = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/\n\nexport type BunkerPointer = {\n  relays: string[]\n  pubkey: string\n  secret: null | string\n}\n\nexport function toBunkerURL(bunkerPointer: BunkerPointer): string {\n  let bunkerURL = new URL(`bunker://${bunkerPointer.pubkey}`)\n  bunkerPointer.relays.forEach(relay => {\n    bunkerURL.searchParams.append('relay', relay)\n  })\n  if (bunkerPointer.secret) {\n    bunkerURL.searchParams.set('secret', bunkerPointer.secret)\n  }\n  return bunkerURL.toString()\n}\n\n/** This takes either a bunker:// URL or a [email protected] NIP-05 identifier\n    and returns a BunkerPointer -- or null in case of error */\nexport async function parseBunkerInput(input: string): Promise<BunkerPointer | null> {\n  let match = input.match(BUNKER_REGEX)\n  if (match) {\n    try {\n      const pubkey = match[1]\n      const qs = new URLSearchParams(match[2])\n      return {\n        pubkey,\n        relays: qs.getAll('relay'),\n        secret: qs.get('secret'),\n      }\n    } catch (_err) {\n      /* just move to the next case */\n    }\n  }\n\n  return queryBunkerProfile(input)\n}\n\nexport async function queryBunkerProfile(nip05: string): Promise<BunkerPointer | null> {\n  const match = nip05.match(NIP05_REGEX)\n  if (!match) return null\n\n  const [_, name = '_', domain] = match\n\n  try {\n    const url = `https://${domain}/.well-known/nostr.json?name=${name}`\n    const res = await (await _fetch(url, { redirect: 'error' })).json()\n\n    let pubkey = res.names[name]\n    let relays = res.nip46[pubkey] || []\n\n    return { pubkey, relays, secret: null }\n  } catch (_err) {\n    return null\n  }\n}\n\nexport type NostrConnectParams = {\n  clientPubkey: string\n  relays: string[]\n  secret: string\n  perms?: string[]\n  name?: string\n  url?: string\n  image?: string\n}\n\nexport function createNostrConnectURI(params: NostrConnectParams): string {\n  const queryParams = new URLSearchParams()\n\n  params.relays.forEach(relay => {\n    queryParams.append('relay', relay)\n  })\n\n  queryParams.append('secret', params.secret)\n\n  if (params.perms && params.perms.length > 0) {\n    queryParams.append('perms', params.perms.join(','))\n  }\n  if (params.name) {\n    queryParams.append('name', params.name)\n  }\n  if (params.url) {\n    queryParams.append('url', params.url)\n  }\n  if (params.image) {\n    queryParams.append('image', params.image)\n  }\n\n  return `nostrconnect://${params.clientPubkey}?${queryParams.toString()}`\n}\n\nexport type BunkerSignerParams = {\n  pool?: AbstractSimplePool\n  onauth?: (url: string) => void\n}\n\nexport class BunkerSigner implements Signer {\n  private params: BunkerSignerParams\n  private pool: AbstractSimplePool\n  private subCloser: SubCloser | undefined\n  private isOpen: boolean\n  private serial: number\n  private idPrefix: string\n  private listeners: {\n    [id: string]: {\n      resolve: (_: string) => void\n      reject: (_: string) => void\n    }\n  }\n  private waitingForAuth: { [id: string]: boolean }\n  private secretKey: Uint8Array\n  // If the client initiates the connection, the two variables below can be filled in later.\n  private conversationKey!: Uint8Array\n  public bp!: BunkerPointer\n\n  private cachedPubKey: string | undefined\n\n  /**\n   * Creates a new instance of the Nip46 class.\n   * @param relays - An array of relay addresses.\n   * @param remotePubkey - An optional remote public key. This is the key you want to sign as.\n   * @param secretKey - An optional key pair.\n   */\n  private constructor(clientSecretKey: Uint8Array, params: BunkerSignerParams) {\n    this.params = params\n    this.pool = params.pool || new SimplePool()\n    this.secretKey = clientSecretKey\n    this.isOpen = false\n    this.idPrefix = Math.random().toString(36).substring(7)\n    this.serial = 0\n    this.listeners = {}\n    this.waitingForAuth = {}\n  }\n\n  /**\n   * [Factory Method 1] Creates a Signer using bunker information (bunker:// URL or NIP-05).\n   * This method is used when the public key of the bunker is known in advance.\n   */\n  public static fromBunker(\n    clientSecretKey: Uint8Array,\n    bp: BunkerPointer,\n    params: BunkerSignerParams = {},\n  ): BunkerSigner {\n    if (bp.relays.length === 0) {\n      throw new Error('no relays specified for this bunker')\n    }\n\n    const signer = new BunkerSigner(clientSecretKey, params)\n\n    signer.conversationKey = getConversationKey(clientSecretKey, bp.pubkey)\n    signer.bp = bp\n\n    signer.setupSubscription()\n    return signer\n  }\n\n  /**\n   * [Factory Method 2] Creates a Signer using a nostrconnect:// URI generated by the client.\n   * In this method, the bunker initiates the connection by scanning the URI.\n   */\n  public static async fromURI(\n    clientSecretKey: Uint8Array,\n    connectionURI: string,\n    bunkerParams: BunkerSignerParams = {},\n    maxWaitOrAbort: number | AbortSignal = 300_000,\n  ): Promise<BunkerSigner> {\n    const signer = new BunkerSigner(clientSecretKey, bunkerParams)\n    const uri = new URL(connectionURI)\n    const clientPubkey = getPublicKey(clientSecretKey)\n\n    return new Promise((resolve, reject) => {\n      let success = false\n      const sub = signer.pool.subscribe(\n        uri.searchParams.getAll('relay'),\n        {\n          kinds: [NostrConnect],\n          '#p': [clientPubkey],\n          limit: 0,\n        },\n        {\n          onevent: async (event: NostrEvent) => {\n            try {\n              const tempConvKey = getConversationKey(clientSecretKey, event.pubkey)\n              const decryptedContent = decrypt(event.content, tempConvKey)\n\n              const response = JSON.parse(decryptedContent)\n\n              if (response.result === uri.searchParams.get('secret')) {\n                sub.close()\n\n                signer.bp = {\n                  pubkey: event.pubkey,\n                  relays: uri.searchParams.getAll('relay'),\n                  secret: uri.searchParams.get('secret'),\n                }\n                signer.conversationKey = getConversationKey(clientSecretKey, event.pubkey)\n                signer.setupSubscription()\n\n                success = true\n                await Promise.race([new Promise(resolve => setTimeout(resolve, 1000)), signer.switchRelays()])\n                resolve(signer)\n              }\n            } catch (e) {\n              console.warn('failed to process potential connection event', e)\n            }\n          },\n          onclose: () => {\n            if (!success) reject(new Error('subscription closed before connection was established.'))\n          },\n          maxWait: typeof maxWaitOrAbort === 'number' ? maxWaitOrAbort : undefined,\n          abort: typeof maxWaitOrAbort !== 'number' ? maxWaitOrAbort : undefined,\n        },\n      )\n    })\n  }\n\n  private setupSubscription() {\n    const listeners = this.listeners\n    const waitingForAuth = this.waitingForAuth\n    const convKey = this.conversationKey\n\n    this.subCloser = this.pool.subscribe(\n      this.bp.relays,\n      {\n        kinds: [NostrConnect],\n        authors: [this.bp.pubkey],\n        '#p': [getPublicKey(this.secretKey)],\n        limit: 0,\n      },\n      {\n        onevent: async (event: NostrEvent) => {\n          const o = JSON.parse(decrypt(event.content, convKey))\n          const { id, result, error } = o\n\n          if (result === 'auth_url' && waitingForAuth[id]) {\n            delete waitingForAuth[id]\n\n            if (this.params.onauth) {\n              this.params.onauth(error)\n            } else {\n              console.warn(\n                `nostr-tools/nip46: remote signer ${this.bp.pubkey} tried to send an \"auth_url\"='${error}' but there was no onauth() callback configured.`,\n              )\n            }\n            return\n          }\n\n          let handler = listeners[id]\n          if (handler) {\n            if (error) handler.reject(error)\n            else if (result) handler.resolve(result)\n            delete listeners[id]\n          }\n        },\n        onclose: () => {\n          this.subCloser = undefined\n        },\n      },\n    )\n    this.isOpen = true\n  }\n\n  async switchRelays(): Promise<boolean> {\n    try {\n      const switchResp = await this.sendRequest('switch_relays', [])\n      let relays = JSON.parse(switchResp) as string[] | null\n      if (!relays) return false\n      if (JSON.stringify(relays.sort()) === JSON.stringify(this.bp.relays)) return false\n\n      this.bp.relays = relays\n      let previousCloser = this.subCloser!\n      setTimeout(() => {\n        previousCloser.close()\n      }, 5000)\n\n      this.subCloser = undefined\n      this.setupSubscription()\n      return true\n    } catch {\n      return false\n    }\n  }\n\n  // closes the subscription -- this object can't be used anymore after this\n  async close() {\n    this.isOpen = false\n    this.subCloser!.close()\n  }\n\n  async sendRequest(method: string, params: string[]): Promise<string> {\n    return new Promise(async (resolve, reject) => {\n      try {\n        if (!this.isOpen) throw new Error('this signer is not open anymore, create a new one')\n        if (!this.subCloser) this.setupSubscription()\n\n        this.serial++\n        const id = `${this.idPrefix}-${this.serial}`\n\n        const encryptedContent = encrypt(JSON.stringify({ id, method, params }), this.conversationKey)\n\n        // the request event\n        const verifiedEvent: VerifiedEvent = finalizeEvent(\n          {\n            kind: NostrConnect,\n            tags: [['p', this.bp.pubkey]],\n            content: encryptedContent,\n            created_at: Math.floor(Date.now() / 1000),\n          },\n          this.secretKey,\n        )\n\n        // setup callback listener\n        this.listeners[id] = { resolve, reject }\n        this.waitingForAuth[id] = true\n\n        // publish the event\n        await Promise.any(this.pool.publish(this.bp.relays, verifiedEvent))\n      } catch (err) {\n        reject(err)\n      }\n    })\n  }\n\n  /**\n   * Calls the \"connect\" method on the bunker.\n   * The promise will be rejected if the response is not \"pong\".\n   */\n  async ping(): Promise<void> {\n    let resp = await this.sendRequest('ping', [])\n    if (resp !== 'pong') throw new Error(`result is not pong: ${resp}`)\n  }\n\n  /**\n   * Calls the \"connect\" method on the bunker.\n   */\n  async connect(): Promise<void> {\n    await this.sendRequest('connect', [this.bp.pubkey, this.bp.secret || ''])\n  }\n\n  /**\n   * Calls the \"get_public_key\" method on the bunker.\n   * (before we would return the public key hardcoded in the bunker parameters, but\n   *  that is not correct as that may be the bunker pubkey and the actual signer\n   *  pubkey may be different.)\n   */\n  async getPublicKey(): Promise<string> {\n    if (!this.cachedPubKey) {\n      this.cachedPubKey = await this.sendRequest('get_public_key', [])\n    }\n    return this.cachedPubKey\n  }\n\n  /**\n   * Signs an event using the remote private key.\n   * @param event - The event to sign.\n   * @returns A Promise that resolves to the signed event.\n   */\n  async signEvent(event: EventTemplate): Promise<VerifiedEvent> {\n    let resp = await this.sendRequest('sign_event', [JSON.stringify(event)])\n    let signed: NostrEvent = JSON.parse(resp)\n    if (verifyEvent(signed)) {\n      return signed\n    } else {\n      throw new Error(`event returned from bunker is improperly signed: ${JSON.stringify(signed)}`)\n    }\n  }\n\n  async nip04Encrypt(thirdPartyPubkey: string, plaintext: string): Promise<string> {\n    return await this.sendRequest('nip04_encrypt', [thirdPartyPubkey, plaintext])\n  }\n\n  async nip04Decrypt(thirdPartyPubkey: string, ciphertext: string): Promise<string> {\n    return await this.sendRequest('nip04_decrypt', [thirdPartyPubkey, ciphertext])\n  }\n\n  async nip44Encrypt(thirdPartyPubkey: string, plaintext: string): Promise<string> {\n    return await this.sendRequest('nip44_encrypt', [thirdPartyPubkey, plaintext])\n  }\n\n  async nip44Decrypt(thirdPartyPubkey: string, ciphertext: string): Promise<string> {\n    return await this.sendRequest('nip44_decrypt', [thirdPartyPubkey, ciphertext])\n  }\n}\n\n/**\n * Creates an account with the specified username, domain, and optional email.\n * @param bunkerPubkey - The public key of the bunker to use for the create_account call.\n * @param username - The username for the account.\n * @param domain - The domain for the account.\n * @param email - The optional email for the account.\n * @param localSecretKey - Optionally pass a local secret key that will be used to communicate with the bunker,\n                           this will default to generating a random key.\n * @throws Error if the email is present but invalid.\n * @returns A Promise that resolves to the auth_url that the client should follow to create an account.\n */\nexport async function createAccount(\n  bunker: BunkerProfile,\n  params: BunkerSignerParams,\n  username: string,\n  domain: string,\n  email?: string,\n  localSecretKey: Uint8Array = generateSecretKey(),\n): Promise<BunkerSigner> {\n  if (email && !EMAIL_REGEX.test(email)) throw new Error('invalid email')\n\n  let rpc = BunkerSigner.fromBunker(localSecretKey, bunker.bunkerPointer, params)\n\n  let pubkey = await rpc.sendRequest('create_account', [username, domain, email || ''])\n\n  // once we get the newly created pubkey back, we hijack this signer instance\n  // and turn it into the main instance for this newly created pubkey\n  rpc.bp.pubkey = pubkey\n  await rpc.connect()\n\n  return rpc\n}\n\n/**\n * Fetches info on available providers that announce themselves using NIP-89 events.\n * @returns A promise that resolves to an array of available bunker objects.\n */\nexport async function fetchBunkerProviders(pool: AbstractSimplePool, relays: string[]): Promise<BunkerProfile[]> {\n  const events = await pool.querySync(relays, {\n    kinds: [Handlerinformation],\n    '#k': [NostrConnect.toString()],\n  })\n\n  events.sort((a, b) => b.created_at - a.created_at)\n\n  // validate bunkers by checking their NIP-05 and pubkey\n  // map to a more useful object\n  const validatedBunkers = await Promise.all(\n    events.map(async (event, i) => {\n      try {\n        const content = JSON.parse(event.content)\n\n        // skip duplicates\n        try {\n          if (events.findIndex(ev => JSON.parse(ev.content).nip05 === content.nip05) !== i) return undefined\n        } catch (err) {\n          /***/\n        }\n\n        const bp = await queryBunkerProfile(content.nip05)\n        if (bp && bp.pubkey === event.pubkey && bp.relays.length) {\n          return {\n            bunkerPointer: bp,\n            nip05: content.nip05,\n            domain: content.nip05.split('@')[1],\n            name: content.name || content.display_name,\n            picture: content.picture,\n            about: content.about,\n            website: content.website,\n            local: false,\n          }\n        }\n      } catch (err) {\n        return undefined\n      }\n    }),\n  )\n\n  return validatedBunkers.filter(b => b !== undefined) as BunkerProfile[]\n}\n\nexport type BunkerProfile = {\n  bunkerPointer: BunkerPointer\n  domain: string\n  nip05: string\n  name: string\n  picture: string\n  about: string\n  website: string\n  local: boolean\n}\n", "import { schnorr } from '@noble/curves/secp256k1.js'\nimport { bytesToHex, hexToBytes } from '@noble/hashes/utils.js'\nimport { Nostr, Event, EventTemplate, UnsignedEvent, VerifiedEvent, verifiedSymbol, validateEvent } from './core.ts'\nimport { sha256 } from '@noble/hashes/sha2.js'\n\nimport { utf8Encoder } from './utils.ts'\n\nclass JS implements Nostr {\n  generateSecretKey(): Uint8Array {\n    return schnorr.utils.randomSecretKey()\n  }\n  getPublicKey(secretKey: Uint8Array): string {\n    return bytesToHex(schnorr.getPublicKey(secretKey))\n  }\n  finalizeEvent(t: EventTemplate, secretKey: Uint8Array): VerifiedEvent {\n    const event = t as VerifiedEvent\n    event.pubkey = bytesToHex(schnorr.getPublicKey(secretKey))\n    event.id = getEventHash(event)\n    event.sig = bytesToHex(schnorr.sign(hexToBytes(getEventHash(event)), secretKey))\n    event[verifiedSymbol] = true\n    return event\n  }\n  verifyEvent(event: Event): event is VerifiedEvent {\n    if (typeof event[verifiedSymbol] === 'boolean') return event[verifiedSymbol]\n\n    const hash = getEventHash(event)\n    if (hash !== event.id) {\n      event[verifiedSymbol] = false\n      return false\n    }\n\n    try {\n      const valid = schnorr.verify(hexToBytes(event.sig), hexToBytes(hash), hexToBytes(event.pubkey))\n      event[verifiedSymbol] = valid\n      return valid\n    } catch (err) {\n      event[verifiedSymbol] = false\n      return false\n    }\n  }\n}\n\nexport function serializeEvent(evt: UnsignedEvent): string {\n  if (!validateEvent(evt)) throw new Error(\"can't serialize event with wrong or missing properties\")\n  return JSON.stringify([0, evt.pubkey, evt.created_at, evt.kind, evt.tags, evt.content])\n}\n\nexport function getEventHash(event: UnsignedEvent): string {\n  let eventHash = sha256(utf8Encoder.encode(serializeEvent(event)))\n  return bytesToHex(eventHash)\n}\n\nconst i: JS = new JS()\n\nexport const generateSecretKey = i.generateSecretKey\nexport const getPublicKey = i.getPublicKey\nexport const finalizeEvent = i.finalizeEvent\nexport const verifyEvent = i.verifyEvent\nexport * from './core.ts'\n", "export interface Nostr {\n  generateSecretKey(): Uint8Array\n  getPublicKey(secretKey: Uint8Array): string\n  finalizeEvent(event: EventTemplate, secretKey: Uint8Array): VerifiedEvent\n  verifyEvent(event: Event): event is VerifiedEvent\n}\n\n/** Designates a verified event signature. */\nexport const verifiedSymbol = Symbol('verified')\n\nexport type NostrEvent = {\n  kind: number\n  tags: string[][]\n  content: string\n  created_at: number\n  pubkey: string\n  id: string\n  sig: string\n  [verifiedSymbol]?: boolean\n}\n\nexport type Event = NostrEvent\nexport type EventTemplate = Pick<Event, 'kind' | 'tags' | 'content' | 'created_at'>\nexport type UnsignedEvent = Pick<Event, 'kind' | 'tags' | 'content' | 'created_at' | 'pubkey'>\n\n/** An event whose signature has been verified. */\nexport interface VerifiedEvent extends Event {\n  [verifiedSymbol]: true\n}\n\nconst isRecord = (obj: unknown): obj is Record<string, unknown> => obj instanceof Object\n\nexport function validateEvent<T>(event: T): event is T & UnsignedEvent {\n  if (!isRecord(event)) return false\n  if (typeof event.kind !== 'number') return false\n  if (typeof event.content !== 'string') return false\n  if (typeof event.created_at !== 'number') return false\n  if (typeof event.pubkey !== 'string') return false\n  if (!event.pubkey.match(/^[a-f0-9]{64}$/)) return false\n\n  if (!Array.isArray(event.tags)) return false\n  for (let i = 0; i < event.tags.length; i++) {\n    let tag = event.tags[i]\n    if (!Array.isArray(tag)) return false\n    for (let j = 0; j < tag.length; j++) {\n      if (typeof tag[j] !== 'string') return false\n    }\n  }\n\n  return true\n}\n\n/**\n * Sort events in reverse-chronological order by the `created_at` timestamp,\n * and then by the event `id` (lexicographically) in case of ties.\n * This mutates the array.\n */\nexport function sortEvents(events: Event[]): Event[] {\n  return events.sort((a: NostrEvent, b: NostrEvent): number => {\n    if (a.created_at !== b.created_at) {\n      return b.created_at - a.created_at\n    }\n    return a.id.localeCompare(b.id)\n  })\n}\n", "import type { Event } from './core.ts'\n\nexport const utf8Decoder: TextDecoder = new TextDecoder('utf-8')\nexport const utf8Encoder: TextEncoder = new TextEncoder()\n\nexport { bytesToHex, hexToBytes } from '@noble/hashes/utils.js'\n\nexport function normalizeURL(url: string): string {\n  try {\n    if (url.indexOf('://') === -1) url = 'wss://' + url\n    let p = new URL(url)\n    if (p.protocol === 'http:') p.protocol = 'ws:'\n    else if (p.protocol === 'https:') p.protocol = 'wss:'\n    p.pathname = p.pathname.replace(/\\/+/g, '/')\n    if (p.pathname.endsWith('/')) p.pathname = p.pathname.slice(0, -1)\n    if ((p.port === '80' && p.protocol === 'ws:') || (p.port === '443' && p.protocol === 'wss:')) p.port = ''\n    p.searchParams.sort()\n    p.hash = ''\n    return p.toString()\n  } catch (e) {\n    throw new Error(`Invalid URL: ${url}`)\n  }\n}\n\nexport function insertEventIntoDescendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return b.created_at - event.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function insertEventIntoAscendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return event.created_at - b.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function binarySearch<T>(arr: T[], compare: (b: T) => number): [number, boolean] {\n  let start = 0\n  let end = arr.length - 1\n\n  while (start <= end) {\n    const mid = Math.floor((start + end) / 2)\n    const cmp = compare(arr[mid])\n\n    if (cmp === 0) {\n      return [mid, true]\n    }\n\n    if (cmp < 0) {\n      end = mid - 1\n    } else {\n      start = mid + 1\n    }\n  }\n\n  return [start, false]\n}\n\nexport class QueueNode<V> {\n  public value: V\n  public next: QueueNode<V> | null = null\n  public prev: QueueNode<V> | null = null\n\n  constructor(message: V) {\n    this.value = message\n  }\n}\n\nexport class Queue<V> {\n  public first: QueueNode<V> | null\n  public last: QueueNode<V> | null\n\n  constructor() {\n    this.first = null\n    this.last = null\n  }\n\n  enqueue(value: V): boolean {\n    const newNode = new QueueNode(value)\n    if (!this.last) {\n      // list is empty\n      this.first = newNode\n      this.last = newNode\n    } else if (this.last === this.first) {\n      // list has a single element\n      this.last = newNode\n      this.last.prev = this.first\n      this.first.next = newNode\n    } else {\n      // list has elements, add as last\n      newNode.prev = this.last\n      this.last.next = newNode\n      this.last = newNode\n    }\n    return true\n  }\n\n  dequeue(): V | null {\n    if (!this.first) return null\n\n    if (this.first === this.last) {\n      const target = this.first\n      this.first = null\n      this.last = null\n      return target.value\n    }\n\n    const target = this.first\n    this.first = target.next\n    if (this.first) {\n      this.first.prev = null // fix: clean up prev pointer\n    }\n\n    return target.value\n  }\n}\n", "import { chacha20 } from '@noble/ciphers/chacha.js'\nimport { equalBytes } from '@noble/ciphers/utils.js'\nimport { secp256k1 } from '@noble/curves/secp256k1.js'\nimport { extract as hkdf_extract, expand as hkdf_expand } from '@noble/hashes/hkdf.js'\nimport { hmac } from '@noble/hashes/hmac.js'\nimport { sha256 } from '@noble/hashes/sha2.js'\nimport { concatBytes, hexToBytes, randomBytes } from '@noble/hashes/utils.js'\nimport { base64 } from '@scure/base'\n\nimport { utf8Decoder, utf8Encoder } from './utils.ts'\n\nconst minPlaintextSize = 0x0001 // 1b msg => padded to 32b\nconst maxPlaintextSize = 0xffff // 65535 (64kb-1) => padded to 64kb\n\nexport function getConversationKey(privkeyA: Uint8Array, pubkeyB: string): Uint8Array {\n  const sharedX = secp256k1.getSharedSecret(privkeyA, hexToBytes('02' + pubkeyB)).subarray(1, 33)\n  return hkdf_extract(sha256, sharedX, utf8Encoder.encode('nip44-v2'))\n}\n\nfunction getMessageKeys(\n  conversationKey: Uint8Array,\n  nonce: Uint8Array,\n): { chacha_key: Uint8Array; chacha_nonce: Uint8Array; hmac_key: Uint8Array } {\n  const keys = hkdf_expand(sha256, conversationKey, nonce, 76)\n  return {\n    chacha_key: keys.subarray(0, 32),\n    chacha_nonce: keys.subarray(32, 44),\n    hmac_key: keys.subarray(44, 76),\n  }\n}\n\nfunction calcPaddedLen(len: number): number {\n  if (!Number.isSafeInteger(len) || len < 1) throw new Error('expected positive integer')\n  if (len <= 32) return 32\n  const nextPower = 1 << (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 > maxPlaintextSize)\n    throw new Error('invalid plaintext size: must be between 1 and 65535 bytes')\n  const arr = new Uint8Array(2)\n  new DataView(arr.buffer).setUint16(0, num, false)\n  return arr\n}\n\nfunction pad(plaintext: string): Uint8Array {\n  const unpadded = utf8Encoder.encode(plaintext)\n  const unpaddedLen = unpadded.length\n  const prefix = 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 unpaddedLen = new DataView(padded.buffer).getUint16(0)\n  const unpadded = padded.subarray(2, 2 + unpaddedLen)\n  if (\n    unpaddedLen < minPlaintextSize ||\n    unpaddedLen > maxPlaintextSize ||\n    unpadded.length !== unpaddedLen ||\n    padded.length !== 2 + calcPaddedLen(unpaddedLen)\n  )\n    throw new Error('invalid padding')\n  return utf8Decoder.decode(unpadded)\n}\n\nfunction hmacAad(key: Uint8Array, message: Uint8Array, aad: Uint8Array): Uint8Array {\n  if (aad.length !== 32) throw new Error('AAD associated data must be 32 bytes')\n  const combined = concatBytes(aad, message)\n  return hmac(sha256, key, combined)\n}\n\n// metadata: always 65b (version: 1b, nonce: 32b, max: 32b)\n// plaintext: 1b to 0xffff\n// padded plaintext: 32b to 0xffff\n// ciphertext: 32b+2 to 0xffff+2\n// raw payload: 99 (65+32+2) to 65603 (65+0xffff+2)\n// compressed payload (base64): 132b to 87472b\nfunction decodePayload(payload: string): { nonce: Uint8Array; ciphertext: Uint8Array; mac: Uint8Array } {\n  if (typeof payload !== 'string') throw new Error('payload must be a valid string')\n  const plen = payload.length\n  if (plen < 132 || plen > 87472) throw new Error('invalid payload length: ' + plen)\n  if (payload[0] === '#') throw new Error('unknown encryption version')\n  let data: Uint8Array\n  try {\n    data = base64.decode(payload)\n  } catch (error) {\n    throw new Error('invalid base64: ' + (error as any).message)\n  }\n  const dlen = data.length\n  if (dlen < 99 || dlen > 65603) throw new Error('invalid data length: ' + dlen)\n  const vers = data[0]\n  if (vers !== 2) throw new Error('unknown encryption version ' + vers)\n  return {\n    nonce: data.subarray(1, 33),\n    ciphertext: data.subarray(33, -32),\n    mac: data.subarray(-32),\n  }\n}\n\nexport function encrypt(plaintext: string, conversationKey: Uint8Array, nonce: Uint8Array = randomBytes(32)): string {\n  const { chacha_key, chacha_nonce, hmac_key } = getMessageKeys(conversationKey, nonce)\n  const padded = pad(plaintext)\n  const ciphertext = chacha20(chacha_key, chacha_nonce, padded)\n  const mac = hmacAad(hmac_key, ciphertext, nonce)\n  return base64.encode(concatBytes(new Uint8Array([2]), nonce, ciphertext, mac))\n}\n\nexport function decrypt(payload: string, conversationKey: Uint8Array): string {\n  const { nonce, ciphertext, mac } = decodePayload(payload)\n  const { chacha_key, chacha_nonce, hmac_key } = getMessageKeys(conversationKey, nonce)\n  const calculatedMac = hmacAad(hmac_key, ciphertext, nonce)\n  if (!equalBytes(calculatedMac, mac)) throw new Error('invalid MAC')\n  const padded = chacha20(chacha_key, chacha_nonce, ciphertext)\n  return unpad(padded)\n}\n\nexport const v2 = {\n  utils: {\n    getConversationKey,\n    calcPaddedLen,\n  },\n  encrypt,\n  decrypt,\n}\n", "import { ProfilePointer } from './nip19.ts'\n\nexport type Nip05 = `${string}@${string}`\n\n/**\n * NIP-05 regex. The localpart is optional, and should be assumed to be `_` otherwise.\n *\n * - 0: full match\n * - 1: name (optional)\n * - 2: domain\n */\nexport const NIP05_REGEX = /^(?:([\\w.+-]+)@)?([\\w_-]+(\\.[\\w_-]+)+)$/\nexport const isNip05 = (value?: string | null): value is Nip05 => NIP05_REGEX.test(value || '')\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nlet _fetch: any\n\ntry {\n  _fetch = fetch\n} catch (_) {\n  null\n}\n\nexport function useFetchImplementation(fetchImplementation: unknown) {\n  _fetch = fetchImplementation\n}\n\nexport async function searchDomain(domain: string, query = ''): Promise<{ [name: string]: string }> {\n  try {\n    const url = `https://${domain}/.well-known/nostr.json?name=${query}`\n    const res = await _fetch(url, { redirect: 'manual' })\n    if (res.status !== 200) {\n      throw Error('Wrong response code')\n    }\n    const json = await res.json()\n    return json.names\n  } catch (_) {\n    return {}\n  }\n}\n\nexport async function queryProfile(fullname: string): Promise<ProfilePointer | null> {\n  const match = fullname.match(NIP05_REGEX)\n  if (!match) return null\n\n  const [, name = '_', domain] = match\n\n  try {\n    const url = `https://${domain}/.well-known/nostr.json?name=${name}`\n    const res = await _fetch(url, { redirect: 'manual' })\n    if (res.status !== 200) {\n      throw Error('Wrong response code')\n    }\n    const json = await res.json()\n\n    const pubkey = json.names[name]\n    return pubkey ? { pubkey, relays: json.relays?.[pubkey] } : null\n  } catch (_e) {\n    return null\n  }\n}\n\nexport async function isValid(pubkey: string, nip05: Nip05): Promise<boolean> {\n  const res = await queryProfile(nip05)\n  return res ? res.pubkey === pubkey : false\n}\n", "import { NostrEvent, validateEvent } from './pure.ts'\n\n/** Events are **regular**, which means they're all expected to be stored by relays. */\nexport function isRegularKind(kind: number): boolean {\n  return kind < 10000 && kind !== 0 && kind !== 3\n}\n\n/** Events are **replaceable**, which means that, for each combination of `pubkey` and `kind`, only the latest event is expected to (SHOULD) be stored by relays, older versions are expected to be discarded. */\nexport function isReplaceableKind(kind: number): boolean {\n  return kind === 0 || kind === 3 || (10000 <= kind && kind < 20000)\n}\n\n/** Events are **ephemeral**, which means they are not expected to be stored by relays. */\nexport function isEphemeralKind(kind: number): boolean {\n  return 20000 <= kind && kind < 30000\n}\n\n/** Events are **addressable**, which means that, for each combination of `pubkey`, `kind` and the `d` tag, only the latest event is expected to be stored by relays, older versions are expected to be discarded. */\nexport function isAddressableKind(kind: number): boolean {\n  return 30000 <= kind && kind < 40000\n}\n\n/** Classification of the event kind. */\nexport type KindClassification = 'regular' | 'replaceable' | 'ephemeral' | 'parameterized' | 'unknown'\n\n/** Determine the classification of this kind of event if known, or `unknown`. */\nexport function classifyKind(kind: number): KindClassification {\n  if (isRegularKind(kind)) return 'regular'\n  if (isReplaceableKind(kind)) return 'replaceable'\n  if (isEphemeralKind(kind)) return 'ephemeral'\n  if (isAddressableKind(kind)) return 'parameterized'\n  return 'unknown'\n}\n\nexport function isKind<T extends number>(event: unknown, kind: T | Array<T>): event is NostrEvent & { kind: T } {\n  const kindAsArray: number[] = kind instanceof Array ? kind : [kind]\n  return (validateEvent(event) && kindAsArray.includes(event.kind)) || false\n}\n\nexport const Metadata = 0\nexport type Metadata = typeof Metadata\nexport const ShortTextNote = 1\nexport type ShortTextNote = typeof ShortTextNote\nexport const RecommendRelay = 2\nexport type RecommendRelay = typeof RecommendRelay\nexport const Contacts = 3\nexport type Contacts = typeof Contacts\nexport const EncryptedDirectMessage = 4\nexport type EncryptedDirectMessage = typeof EncryptedDirectMessage\nexport const EventDeletion = 5\nexport type EventDeletion = typeof EventDeletion\nexport const Repost = 6\nexport type Repost = typeof Repost\nexport const Reaction = 7\nexport type Reaction = typeof Reaction\nexport const BadgeAward = 8\nexport type BadgeAward = typeof BadgeAward\nexport const ChatMessage = 9\nexport type ChatMessage = typeof ChatMessage\nexport const ForumThread = 11\nexport type ForumThread = typeof ForumThread\nexport const Seal = 13\nexport type Seal = typeof Seal\nexport const PrivateDirectMessage = 14\nexport type PrivateDirectMessage = typeof PrivateDirectMessage\nexport const FileMessage = 15\nexport type FileMessage = typeof FileMessage\nexport const GenericRepost = 16\nexport type GenericRepost = typeof GenericRepost\nexport const Photo = 20\nexport type Photo = typeof Photo\nexport const NormalVideo = 21\nexport type NormalVideo = typeof NormalVideo\nexport const ShortVideo = 22\nexport type ShortVideo = typeof ShortVideo\nexport const ChannelCreation = 40\nexport type ChannelCreation = typeof ChannelCreation\nexport const ChannelMetadata = 41\nexport type ChannelMetadata = typeof ChannelMetadata\nexport const ChannelMessage = 42\nexport type ChannelMessage = typeof ChannelMessage\nexport const ChannelHideMessage = 43\nexport type ChannelHideMessage = typeof ChannelHideMessage\nexport const ChannelMuteUser = 44\nexport type ChannelMuteUser = typeof ChannelMuteUser\nexport const OpenTimestamps = 1040\nexport type OpenTimestamps = typeof OpenTimestamps\nexport const GiftWrap = 1059\nexport type GiftWrap = typeof GiftWrap\nexport const Poll = 1068\nexport type Poll = typeof Poll\nexport const FileMetadata = 1063\nexport type FileMetadata = typeof FileMetadata\nexport const Comment = 1111\nexport type Comment = typeof Comment\nexport const LiveChatMessage = 1311\nexport type LiveChatMessage = typeof LiveChatMessage\nexport const Voice = 1222\nexport type Voice = typeof Voice\nexport const VoiceComment = 1244\nexport type VoiceComment = typeof VoiceComment\nexport const ProblemTracker = 1971\nexport type ProblemTracker = typeof ProblemTracker\nexport const Report = 1984\nexport type Report = typeof Report\nexport const Reporting = 1984\nexport type Reporting = typeof Reporting\nexport const Label = 1985\nexport type Label = typeof Label\nexport const CommunityPostApproval = 4550\nexport type CommunityPostApproval = typeof CommunityPostApproval\nexport const JobRequest = 5999\nexport type JobRequest = typeof JobRequest\nexport const JobResult = 6999\nexport type JobResult = typeof JobResult\nexport const JobFeedback = 7000\nexport type JobFeedback = typeof JobFeedback\nexport const ZapGoal = 9041\nexport type ZapGoal = typeof ZapGoal\nexport const ZapRequest = 9734\nexport type ZapRequest = typeof ZapRequest\nexport const Zap = 9735\nexport type Zap = typeof Zap\nexport const Highlights = 9802\nexport type Highlights = typeof Highlights\nexport const PollResponse = 1018\nexport type PollResponse = typeof PollResponse\nexport const Mutelist = 10000\nexport type Mutelist = typeof Mutelist\nexport const Pinlist = 10001\nexport type Pinlist = typeof Pinlist\nexport const RelayList = 10002\nexport type RelayList = typeof RelayList\nexport const BookmarkList = 10003\nexport type BookmarkList = typeof BookmarkList\nexport const CommunitiesList = 10004\nexport type CommunitiesList = typeof CommunitiesList\nexport const PublicChatsList = 10005\nexport type PublicChatsList = typeof PublicChatsList\nexport const BlockedRelaysList = 10006\nexport type BlockedRelaysList = typeof BlockedRelaysList\nexport const SearchRelaysList = 10007\nexport type SearchRelaysList = typeof SearchRelaysList\nexport const FavoriteRelays = 10012\nexport type FavoriteRelays = typeof FavoriteRelays\nexport const InterestsList = 10015\nexport type InterestsList = typeof InterestsList\nexport const UserEmojiList = 10030\nexport type UserEmojiList = typeof UserEmojiList\nexport const DirectMessageRelaysList = 10050\nexport type DirectMessageRelaysList = typeof DirectMessageRelaysList\nexport const FileServerPreference = 10096\nexport type FileServerPreference = typeof FileServerPreference\nexport const BlossomServerList = 10063\nexport type BlossomServerList = typeof BlossomServerList\nexport const NWCWalletInfo = 13194\nexport type NWCWalletInfo = typeof NWCWalletInfo\nexport const LightningPubRPC = 21000\nexport type LightningPubRPC = typeof LightningPubRPC\nexport const ClientAuth = 22242\nexport type ClientAuth = typeof ClientAuth\nexport const NWCWalletRequest = 23194\nexport type NWCWalletRequest = typeof NWCWalletRequest\nexport const NWCWalletResponse = 23195\nexport type NWCWalletResponse = typeof NWCWalletResponse\nexport const NostrConnect = 24133\nexport type NostrConnect = typeof NostrConnect\nexport const HTTPAuth = 27235\nexport type HTTPAuth = typeof HTTPAuth\nexport const Followsets = 30000\nexport type Followsets = typeof Followsets\nexport const Genericlists = 30001\nexport type Genericlists = typeof Genericlists\nexport const Relaysets = 30002\nexport type Relaysets = typeof Relaysets\nexport const Bookmarksets = 30003\nexport type Bookmarksets = typeof Bookmarksets\nexport const Curationsets = 30004\nexport type Curationsets = typeof Curationsets\nexport const ProfileBadges = 30008\nexport type ProfileBadges = typeof ProfileBadges\nexport const BadgeDefinition = 30009\nexport type BadgeDefinition = typeof BadgeDefinition\nexport const Interestsets = 30015\nexport type Interestsets = typeof Interestsets\nexport const CreateOrUpdateStall = 30017\nexport type CreateOrUpdateStall = typeof CreateOrUpdateStall\nexport const CreateOrUpdateProduct = 30018\nexport type CreateOrUpdateProduct = typeof CreateOrUpdateProduct\nexport const LongFormArticle = 30023\nexport type LongFormArticle = typeof LongFormArticle\nexport const DraftLong = 30024\nexport type DraftLong = typeof DraftLong\nexport const Emojisets = 30030\nexport type Emojisets = typeof Emojisets\nexport const Application = 30078\nexport type Application = typeof Application\nexport const LiveEvent = 30311\nexport type LiveEvent = typeof LiveEvent\nexport const UserStatuses = 30315\nexport type UserStatuses = typeof UserStatuses\nexport const ClassifiedListing = 30402\nexport type ClassifiedListing = typeof ClassifiedListing\nexport const DraftClassifiedListing = 30403\nexport type DraftClassifiedListing = typeof DraftClassifiedListing\nexport const Date = 31922\nexport type Date = typeof Date\nexport const Time = 31923\nexport type Time = typeof Time\nexport const Calendar = 31924\nexport type Calendar = typeof Calendar\nexport const CalendarEventRSVP = 31925\nexport type CalendarEventRSVP = typeof CalendarEventRSVP\nexport const RelayReview = 31987\nexport type RelayReview = typeof RelayReview\nexport const Handlerrecommendation = 31989\nexport type Handlerrecommendation = typeof Handlerrecommendation\nexport const Handlerinformation = 31990\nexport type Handlerinformation = typeof Handlerinformation\nexport const CommunityDefinition = 34550\nexport type CommunityDefinition = typeof CommunityDefinition\nexport const GroupMetadata = 39000\nexport type GroupMetadata = typeof GroupMetadata\n", "import { Event } from './core.ts'\nimport { isAddressableKind, isReplaceableKind } from './kinds.ts'\n\nexport type Filter = {\n  ids?: string[]\n  kinds?: number[]\n  authors?: string[]\n  since?: number\n  until?: number\n  limit?: number\n  search?: string\n  [key: `#${string}`]: string[] | undefined\n}\n\nexport function matchFilter(filter: Filter, event: Event): boolean {\n  if (filter.ids && filter.ids.indexOf(event.id) === -1) {\n    return false\n  }\n  if (filter.kinds && filter.kinds.indexOf(event.kind) === -1) {\n    return false\n  }\n  if (filter.authors && filter.authors.indexOf(event.pubkey) === -1) {\n    return false\n  }\n\n  for (let f in filter) {\n    if (f[0] === '#') {\n      let tagName = f.slice(1)\n      let values = filter[`#${tagName}`]\n      if (values && !event.tags.find(([t, v]) => t === f.slice(1) && values!.indexOf(v) !== -1)) return false\n    }\n  }\n\n  if (filter.since && event.created_at < filter.since) return false\n  if (filter.until && event.created_at > filter.until) return false\n\n  return true\n}\n\nexport function matchFilters(filters: Filter[], event: Event): boolean {\n  for (let i = 0; i < filters.length; i++) {\n    if (matchFilter(filters[i], event)) {\n      return true\n    }\n  }\n  return false\n}\n\nexport function mergeFilters(...filters: Filter[]): Filter {\n  let result: Filter = {}\n  for (let i = 0; i < filters.length; i++) {\n    let filter = filters[i]\n    Object.entries(filter).forEach(([property, values]) => {\n      if (property === 'kinds' || property === 'ids' || property === 'authors' || property[0] === '#') {\n        // @ts-ignore\n        result[property] = result[property] || []\n        // @ts-ignore\n        for (let v = 0; v < values.length; v++) {\n          // @ts-ignore\n          let value = values[v]\n          // @ts-ignore\n          if (!result[property].includes(value)) result[property].push(value)\n        }\n      }\n    })\n\n    if (filter.limit && (!result.limit || filter.limit > result.limit)) result.limit = filter.limit\n    if (filter.until && (!result.until || filter.until > result.until)) result.until = filter.until\n    if (filter.since && (!result.since || filter.since < result.since)) result.since = filter.since\n  }\n\n  return result\n}\n\n/**\n * Calculate the intrinsic limit of a filter.\n * This function returns a positive integer, or `Infinity` if there is no intrinsic limit.\n */\nexport function getFilterLimit(filter: Filter): number {\n  if (filter.ids && !filter.ids.length) return 0\n  if (filter.kinds && !filter.kinds.length) return 0\n  if (filter.authors && !filter.authors.length) return 0\n\n  for (const [key, value] of Object.entries(filter)) {\n    if (key[0] === '#' && Array.isArray(value) && !value.length) return 0\n  }\n\n  return Math.min(\n    // The `limit` property creates an artificial limit.\n    Math.max(0, filter.limit ?? Infinity),\n\n    // There can only be one event per `id`.\n    filter.ids?.length ?? Infinity,\n\n    // Replaceable events are limited by the number of authors and kinds.\n    filter.authors?.length && filter.kinds?.every(kind => isReplaceableKind(kind))\n      ? filter.authors.length * filter.kinds.length\n      : Infinity,\n\n    // Parameterized replaceable events are limited by the number of authors, kinds, and \"d\" tags.\n    filter.authors?.length && filter.kinds?.every(kind => isAddressableKind(kind)) && filter['#d']?.length\n      ? filter.authors.length * filter.kinds.length * filter['#d'].length\n      : Infinity,\n  )\n}\n", "export function getHex64(json: string, field: string): string {\n  let len = field.length + 3\n  let idx = json.indexOf(`\"${field}\":`) + len\n  let s = json.slice(idx).indexOf(`\"`) + idx + 1\n  return json.slice(s, s + 64)\n}\n\nexport function getInt(json: string, field: string): number {\n  let len = field.length\n  let idx = json.indexOf(`\"${field}\":`) + len + 3\n  let sliced = json.slice(idx)\n  let end = Math.min(sliced.indexOf(','), sliced.indexOf('}'))\n  return parseInt(sliced.slice(0, end), 10)\n}\n\nexport function getSubscriptionId(json: string): string | null {\n  let idx = json.slice(0, 22).indexOf(`\"EVENT\"`)\n  if (idx === -1) return null\n\n  let pstart = json.slice(idx + 7 + 1).indexOf(`\"`)\n  if (pstart === -1) return null\n  let start = idx + 7 + 1 + pstart\n\n  let pend = json.slice(start + 1, 80).indexOf(`\"`)\n  if (pend === -1) return null\n  let end = start + 1 + pend\n\n  return json.slice(start + 1, end)\n}\n\nexport function matchEventId(json: string, id: string): boolean {\n  return id === getHex64(json, 'id')\n}\n\nexport function matchEventPubkey(json: string, pubkey: string): boolean {\n  return pubkey === getHex64(json, 'pubkey')\n}\n\nexport function matchEventKind(json: string, kind: number): boolean {\n  return kind === getInt(json, 'kind')\n}\n", "import { EventTemplate } from './core.ts'\nimport { ClientAuth } from './kinds.ts'\n\n/**\n * creates an EventTemplate for an AUTH event to be signed.\n */\nexport function makeAuthEvent(relayURL: string, challenge: string): EventTemplate {\n  return {\n    kind: ClientAuth,\n    created_at: Math.floor(Date.now() / 1000),\n    tags: [\n      ['relay', relayURL],\n      ['challenge', challenge],\n    ],\n    content: '',\n  }\n}\n", "import { verifiedSymbol, type Event, type Nostr, VerifiedEvent } from './core.ts'\n\nexport async function yieldThread() {\n  return new Promise<void>((resolve, reject) => {\n    try {\n      // Check if MessageChannel is available\n      if (typeof MessageChannel !== 'undefined') {\n        const ch = new MessageChannel()\n        const handler = () => {\n          // @ts-ignore (typescript thinks this property should be called `removeListener`, but in fact it's `removeEventListener`)\n          ch.port1.removeEventListener('message', handler)\n          resolve()\n        }\n        // @ts-ignore (typescript thinks this property should be called `addListener`, but in fact it's `addEventListener`)\n        ch.port1.addEventListener('message', handler)\n        ch.port2.postMessage(0)\n        ch.port1.start()\n      } else {\n        if (typeof setImmediate !== 'undefined') {\n          setImmediate(resolve)\n        } else if (typeof setTimeout !== 'undefined') {\n          setTimeout(resolve, 0)\n        } else {\n          // Last resort - resolve immediately\n          resolve()\n        }\n      }\n    } catch (e) {\n      console.error('during yield: ', e)\n      reject(e)\n    }\n  })\n}\n\nexport const alwaysTrue: Nostr['verifyEvent'] = (t: Event): t is VerifiedEvent => {\n  t[verifiedSymbol] = true\n  return true\n}\n", "/* global WebSocket */\n\nimport type { Event, EventTemplate, VerifiedEvent, Nostr, NostrEvent } from './core.ts'\nimport { matchFilters, type Filter } from './filter.ts'\nimport { getHex64, getSubscriptionId } from './fakejson.ts'\nimport { Queue, normalizeURL } from './utils.ts'\nimport { makeAuthEvent } from './nip42.ts'\nimport { yieldThread } from './helpers.ts'\n\ntype RelayWebSocket = WebSocket & {\n  ping?(): void\n  on?(event: 'pong', listener: () => void): any\n}\n\nexport type AbstractRelayConstructorOptions = {\n  verifyEvent: Nostr['verifyEvent']\n  websocketImplementation?: typeof WebSocket\n  enablePing?: boolean\n  enableReconnect?: boolean\n}\n\nexport class SendingOnClosedConnection extends Error {\n  constructor(message: string, relay: string) {\n    super(`Tried to send message '${message} on a closed connection to ${relay}.`)\n    this.name = 'SendingOnClosedConnection'\n  }\n}\n\nexport class AbstractRelay {\n  public readonly url: string\n  private _connected: boolean = false\n\n  public onclose: (() => void) | null = null\n  public onnotice: (msg: string) => void = msg => console.debug(`NOTICE from ${this.url}: ${msg}`)\n  public onauth: undefined | ((evt: EventTemplate) => Promise<VerifiedEvent>)\n\n  public baseEoseTimeout: number = 4400\n  public publishTimeout: number = 4400\n  public pingFrequency: number = 29000\n  public pingTimeout: number = 20000\n  public resubscribeBackoff: number[] = [10000, 10000, 10000, 20000, 20000, 30000, 60000]\n  public openSubs: Map<string, Subscription> = new Map()\n  public enablePing: boolean | undefined\n  public enableReconnect: boolean\n  private reconnectTimeoutHandle: ReturnType<typeof setTimeout> | undefined\n  private pingIntervalHandle: ReturnType<typeof setInterval> | undefined\n  private reconnectAttempts: number = 0\n  private closedIntentionally: boolean = false\n\n  private connectionPromise: Promise<void> | undefined\n  private openCountRequests = new Map<string, CountResolver>()\n  private openEventPublishes = new Map<string, EventPublishResolver>()\n  private ws: RelayWebSocket | undefined\n  private incomingMessageQueue = new Queue<string>()\n  private queueRunning = false\n  private challenge: string | undefined\n  private authPromise: Promise<string> | undefined\n  private serial: number = 0\n  private verifyEvent: Nostr['verifyEvent']\n\n  private _WebSocket: typeof WebSocket\n\n  constructor(url: string, opts: AbstractRelayConstructorOptions) {\n    this.url = normalizeURL(url)\n    this.verifyEvent = opts.verifyEvent\n    this._WebSocket = opts.websocketImplementation || WebSocket\n    this.enablePing = opts.enablePing\n    this.enableReconnect = opts.enableReconnect || false\n  }\n\n  static async connect(\n    url: string,\n    opts: AbstractRelayConstructorOptions & Parameters<AbstractRelay['connect']>[0],\n  ): Promise<AbstractRelay> {\n    const relay = new AbstractRelay(url, opts)\n    await relay.connect(opts)\n    return relay\n  }\n\n  private closeAllSubscriptions(reason: string) {\n    for (let [_, sub] of this.openSubs) {\n      sub.close(reason)\n    }\n    this.openSubs.clear()\n\n    for (let [_, ep] of this.openEventPublishes) {\n      ep.reject(new Error(reason))\n    }\n    this.openEventPublishes.clear()\n\n    for (let [_, cr] of this.openCountRequests) {\n      cr.reject(new Error(reason))\n    }\n    this.openCountRequests.clear()\n  }\n\n  public get connected(): boolean {\n    return this._connected\n  }\n\n  private async reconnect(): Promise<void> {\n    const backoff = this.resubscribeBackoff[Math.min(this.reconnectAttempts, this.resubscribeBackoff.length - 1)]\n    this.reconnectAttempts++\n\n    this.reconnectTimeoutHandle = setTimeout(async () => {\n      try {\n        await this.connect()\n      } catch (err) {\n        // this will be called again through onclose/onerror\n      }\n    }, backoff)\n  }\n\n  private handleHardClose(reason: string) {\n    if (this.pingIntervalHandle) {\n      clearInterval(this.pingIntervalHandle)\n      this.pingIntervalHandle = undefined\n    }\n\n    this._connected = false\n    this.connectionPromise = undefined\n\n    const wasIntentional = this.closedIntentionally\n    this.closedIntentionally = false // reset for next time\n\n    this.onclose?.()\n\n    if (this.enableReconnect && !wasIntentional) {\n      this.reconnect()\n    } else {\n      this.closeAllSubscriptions(reason)\n    }\n  }\n\n  public async connect(opts?: { timeout?: number; abort?: AbortSignal }): Promise<void> {\n    let connectionTimeoutHandle: ReturnType<typeof setTimeout> | undefined\n\n    if (this.connectionPromise) return this.connectionPromise\n\n    this.challenge = undefined\n    this.authPromise = undefined\n    this.connectionPromise = new Promise((resolve, reject) => {\n      if (opts?.timeout) {\n        connectionTimeoutHandle = setTimeout(() => {\n          reject('connection timed out')\n          this.connectionPromise = undefined\n          this.onclose?.()\n          this.closeAllSubscriptions('relay connection timed out')\n        }, opts.timeout)\n      }\n\n      if (opts?.abort) {\n        opts.abort.onabort = reject\n      }\n\n      try {\n        this.ws = new this._WebSocket(this.url)\n      } catch (err) {\n        clearTimeout(connectionTimeoutHandle)\n        reject(err)\n        return\n      }\n\n      this.ws.onopen = () => {\n        if (this.reconnectTimeoutHandle) {\n          clearTimeout(this.reconnectTimeoutHandle)\n          this.reconnectTimeoutHandle = undefined\n        }\n        clearTimeout(connectionTimeoutHandle)\n        this._connected = true\n\n        const isReconnection = this.reconnectAttempts > 0\n        this.reconnectAttempts = 0\n\n        // resubscribe to all open subscriptions\n        for (const sub of this.openSubs.values()) {\n          sub.eosed = false\n          if (isReconnection) {\n            for (let f = 0; f < sub.filters.length; f++) {\n              if (sub.lastEmitted) {\n                sub.filters[f].since = sub.lastEmitted + 1\n              }\n            }\n          }\n          sub.fire()\n        }\n\n        if (this.enablePing) {\n          this.pingIntervalHandle = setInterval(() => this.pingpong(), this.pingFrequency)\n        }\n        resolve()\n      }\n\n      this.ws.onerror = ev => {\n        clearTimeout(connectionTimeoutHandle)\n        reject((ev as any).message || 'websocket error')\n        this.handleHardClose('relay connection errored')\n      }\n\n      this.ws.onclose = ev => {\n        clearTimeout(connectionTimeoutHandle)\n        reject((ev as any).message || 'websocket closed')\n        this.handleHardClose('relay connection closed')\n      }\n\n      this.ws.onmessage = this._onmessage.bind(this)\n    })\n\n    return this.connectionPromise\n  }\n\n  private waitForPingPong() {\n    return new Promise(resolve => {\n      // listen for pong\n      ;(this.ws as any).once('pong', () => resolve(true))\n      // send a ping\n      this.ws!.ping!()\n    })\n  }\n\n  private waitForDummyReq() {\n    return new Promise((resolve, reject) => {\n      if (!this.connectionPromise) return reject(new Error(`no connection to ${this.url}, can't ping`))\n\n      // make a dummy request with expected empty eose reply\n      // [\"REQ\", \"_\", {\"ids\":[\"aaaa...aaaa\"], \"limit\": 0}]\n      try {\n        const sub = this.subscribe(\n          [{ ids: ['aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'], limit: 0 }],\n          {\n            label: 'forced-ping',\n            oneose: () => {\n              resolve(true)\n              sub.close()\n            },\n            onclose() {\n              // if we get a CLOSED it's because the relay is alive\n              resolve(true)\n            },\n            eoseTimeout: this.pingTimeout + 1000,\n          },\n        )\n      } catch (err) {\n        reject(err)\n      }\n    })\n  }\n\n  // nodejs requires this magic here to ensure connections are closed when internet goes off and stuff\n  // in browsers it's done automatically. see https://github.com/nbd-wtf/nostr-tools/issues/491\n  private async pingpong() {\n    // if the websocket is connected\n    if (this.ws?.readyState === 1) {\n      // wait for either a ping-pong reply or a timeout\n      const result = await Promise.any([\n        // browsers don't have ping so use a dummy req\n        this.ws && this.ws.ping && (this.ws as any).once ? this.waitForPingPong() : this.waitForDummyReq(),\n        new Promise(res => setTimeout(() => res(false), this.pingTimeout)),\n      ])\n\n      if (!result) {\n        // pingpong closing socket\n        if (this.ws?.readyState === this._WebSocket.OPEN) {\n          this.ws?.close()\n        }\n      }\n    }\n  }\n\n  private async runQueue() {\n    this.queueRunning = true\n    while (true) {\n      if (false === this.handleNext()) {\n        break\n      }\n      await yieldThread()\n    }\n    this.queueRunning = false\n  }\n\n  private handleNext(): undefined | false {\n    const json = this.incomingMessageQueue.dequeue()\n    if (!json) {\n      return false\n    }\n\n    // shortcut EVENT sub\n    const subid = getSubscriptionId(json)\n    if (subid) {\n      const so = this.openSubs.get(subid as string)\n      if (!so) {\n        // this is an EVENT message, but for a subscription we don't have, so just stop here\n        return\n      }\n\n      // this will be called only when this message is a EVENT message for a subscription we have\n      // we do this before parsing the JSON to not have to do that for duplicate events\n      //   since JSON parsing is slow\n      const id = getHex64(json, 'id')\n      const alreadyHave = so.alreadyHaveEvent?.(id)\n\n      // notify any interested client that the relay has this event\n      // (do this after alreadyHaveEvent() because the client may rely on this to answer that)\n      so.receivedEvent?.(this, id)\n\n      if (alreadyHave) {\n        // if we had already seen this event we can just stop here\n        return\n      }\n    }\n\n    try {\n      let data = JSON.parse(json)\n      // we won't do any checks against the data since all failures (i.e. invalid messages from relays)\n      // will naturally be caught by the encompassing try..catch block\n\n      switch (data[0]) {\n        case 'EVENT': {\n          const so = this.openSubs.get(data[1] as string) as Subscription\n          const event = data[2] as NostrEvent\n          if (this.verifyEvent(event) && matchFilters(so.filters, event)) {\n            so.onevent(event)\n          }\n          if (!so.lastEmitted || so.lastEmitted < event.created_at) so.lastEmitted = event.created_at\n          return\n        }\n        case 'COUNT': {\n          const id: string = data[1]\n          const payload = data[2] as { count: number }\n          const cr = this.openCountRequests.get(id) as CountResolver\n          if (cr) {\n            cr.resolve(payload.count)\n            this.openCountRequests.delete(id)\n          }\n          return\n        }\n        case 'EOSE': {\n          const so = this.openSubs.get(data[1] as string)\n          if (!so) return\n          so.receivedEose()\n          return\n        }\n        case 'OK': {\n          const id: string = data[1]\n          const ok: boolean = data[2]\n          const reason: string = data[3]\n          const ep = this.openEventPublishes.get(id) as EventPublishResolver\n          if (ep) {\n            clearTimeout(ep.timeout)\n            if (ok) ep.resolve(reason)\n            else ep.reject(new Error(reason))\n            this.openEventPublishes.delete(id)\n          }\n          return\n        }\n        case 'CLOSED': {\n          const id: string = data[1]\n          const so = this.openSubs.get(id)\n          if (!so) return\n          so.closed = true\n          so.close(data[2] as string)\n          return\n        }\n        case 'NOTICE': {\n          this.onnotice(data[1] as string)\n          return\n        }\n        case 'AUTH': {\n          this.challenge = data[1] as string\n          if (this.onauth) {\n            this.auth(this.onauth)\n          }\n          return\n        }\n        default: {\n          const so = this.openSubs.get(data[1])\n          so?.oncustom?.(data)\n          return\n        }\n      }\n    } catch (err) {\n      return\n    }\n  }\n\n  public async send(message: string) {\n    if (!this.connectionPromise) throw new SendingOnClosedConnection(message, this.url)\n\n    this.connectionPromise.then(() => {\n      this.ws?.send(message)\n    })\n  }\n\n  public async auth(signAuthEvent: (evt: EventTemplate) => Promise<VerifiedEvent>): Promise<string> {\n    const challenge = this.challenge\n    if (!challenge) throw new Error(\"can't perform auth, no challenge was received\")\n    if (this.authPromise) return this.authPromise\n\n    this.authPromise = new Promise<string>(async (resolve, reject) => {\n      try {\n        let evt = await signAuthEvent(makeAuthEvent(this.url, challenge))\n        let timeout = setTimeout(() => {\n          let ep = this.openEventPublishes.get(evt.id) as EventPublishResolver\n          if (ep) {\n            ep.reject(new Error('auth timed out'))\n            this.openEventPublishes.delete(evt.id)\n          }\n        }, this.publishTimeout)\n        this.openEventPublishes.set(evt.id, { resolve, reject, timeout })\n        this.send('[\"AUTH\",' + JSON.stringify(evt) + ']')\n      } catch (err) {\n        console.warn('subscribe auth function failed:', err)\n      }\n    })\n    return this.authPromise\n  }\n\n  public async publish(event: Event): Promise<string> {\n    const ret = new Promise<string>((resolve, reject) => {\n      const timeout = setTimeout(() => {\n        const ep = this.openEventPublishes.get(event.id) as EventPublishResolver\n        if (ep) {\n          ep.reject(new Error('publish timed out'))\n          this.openEventPublishes.delete(event.id)\n        }\n      }, this.publishTimeout)\n      this.openEventPublishes.set(event.id, { resolve, reject, timeout })\n    })\n    this.send('[\"EVENT\",' + JSON.stringify(event) + ']')\n    return ret\n  }\n\n  public async count(filters: Filter[], params: { id?: string | null }): Promise<number> {\n    this.serial++\n    const id = params?.id || 'count:' + this.serial\n    const ret = new Promise<number>((resolve, reject) => {\n      this.openCountRequests.set(id, { resolve, reject })\n    })\n    this.send('[\"COUNT\",\"' + id + '\",' + JSON.stringify(filters).substring(1))\n    return ret\n  }\n\n  public subscribe(\n    filters: Filter[],\n    params: Partial<SubscriptionParams> & { label?: string; id?: string },\n  ): Subscription {\n    const sub = this.prepareSubscription(filters, params)\n    sub.fire()\n\n    if (params.abort) {\n      params.abort.onabort = () => sub.close(String(params.abort!.reason || '<aborted>'))\n    }\n\n    return sub\n  }\n\n  public prepareSubscription(\n    filters: Filter[],\n    params: Partial<SubscriptionParams> & { label?: string; id?: string },\n  ): Subscription {\n    this.serial++\n    const id = params.id || (params.label ? params.label + ':' : 'sub:') + this.serial\n    const subscription = new Subscription(this, id, filters, params)\n    this.openSubs.set(id, subscription)\n    return subscription\n  }\n\n  public close() {\n    this.closedIntentionally = true\n    if (this.reconnectTimeoutHandle) {\n      clearTimeout(this.reconnectTimeoutHandle)\n      this.reconnectTimeoutHandle = undefined\n    }\n    if (this.pingIntervalHandle) {\n      clearInterval(this.pingIntervalHandle)\n      this.pingIntervalHandle = undefined\n    }\n    this.closeAllSubscriptions('relay connection closed by us')\n    this._connected = false\n    this.onclose?.()\n    if (this.ws?.readyState === this._WebSocket.OPEN) {\n      this.ws?.close()\n    }\n  }\n\n  // this is the function assigned to this.ws.onmessage\n  // it's exposed for testing and debugging purposes\n  public _onmessage(ev: MessageEvent<any>) {\n    this.incomingMessageQueue.enqueue(ev.data as string)\n    if (!this.queueRunning) {\n      this.runQueue()\n    }\n  }\n}\n\nexport class Subscription {\n  public readonly relay: AbstractRelay\n  public readonly id: string\n\n  public lastEmitted: number | undefined\n  public closed: boolean = false\n  public eosed: boolean = false\n  public filters: Filter[]\n  public alreadyHaveEvent: ((id: string) => boolean) | undefined\n  public receivedEvent: ((relay: AbstractRelay, id: string) => void) | undefined\n\n  public onevent: (evt: Event) => void\n  public oneose: (() => void) | undefined\n  public onclose: ((reason: string) => void) | undefined\n\n  // will get any messages that have this subscription id as their second item and are not default standard\n  public oncustom: ((msg: string[]) => void) | undefined\n\n  public eoseTimeout: number\n  private eoseTimeoutHandle: ReturnType<typeof setTimeout> | undefined\n\n  constructor(relay: AbstractRelay, id: string, filters: Filter[], params: SubscriptionParams) {\n    if (filters.length === 0) throw new Error(\"subscription can't be created with zero filters\")\n\n    this.relay = relay\n    this.filters = filters\n    this.id = id\n    this.alreadyHaveEvent = params.alreadyHaveEvent\n    this.receivedEvent = params.receivedEvent\n    this.eoseTimeout = params.eoseTimeout || relay.baseEoseTimeout\n\n    this.oneose = params.oneose\n    this.onclose = params.onclose\n    this.onevent =\n      params.onevent ||\n      (event => {\n        console.warn(\n          `onevent() callback not defined for subscription '${this.id}' in relay ${this.relay.url}. event received:`,\n          event,\n        )\n      })\n  }\n\n  public fire() {\n    this.relay.send('[\"REQ\",\"' + this.id + '\",' + JSON.stringify(this.filters).substring(1))\n\n    // only now we start counting the eoseTimeout\n    this.eoseTimeoutHandle = setTimeout(this.receivedEose.bind(this), this.eoseTimeout)\n  }\n\n  public receivedEose() {\n    if (this.eosed) return\n    clearTimeout(this.eoseTimeoutHandle)\n    this.eosed = true\n    this.oneose?.()\n  }\n\n  public close(reason: string = 'closed by caller') {\n    if (!this.closed && this.relay.connected) {\n      // if the connection was closed by the user calling .close() we will send a CLOSE message\n      // otherwise this._open will be already set to false so we will skip this\n      try {\n        this.relay.send('[\"CLOSE\",' + JSON.stringify(this.id) + ']')\n      } catch (err) {\n        if (err instanceof SendingOnClosedConnection) {\n          /* doesn't matter, it's ok */\n        } else {\n          throw err\n        }\n      }\n      this.closed = true\n    }\n    this.relay.openSubs.delete(this.id)\n    this.onclose?.(reason)\n  }\n}\n\nexport type SubscriptionParams = {\n  onevent?: (evt: Event) => void\n  oneose?: () => void\n  onclose?: (reason: string) => void\n  alreadyHaveEvent?: (id: string) => boolean\n  receivedEvent?: (relay: AbstractRelay, id: string) => void\n  eoseTimeout?: number\n  abort?: AbortSignal\n}\n\nexport type CountResolver = {\n  resolve: (count: number) => void\n  reject: (err: Error) => void\n}\n\nexport type EventPublishResolver = {\n  resolve: (reason: string) => void\n  reject: (err: Error) => void\n  timeout: ReturnType<typeof setTimeout>\n}\n", "/* global WebSocket */\n\nimport {\n  AbstractRelay as AbstractRelay,\n  SubscriptionParams,\n  Subscription,\n  type AbstractRelayConstructorOptions,\n} from './abstract-relay.ts'\nimport { normalizeURL } from './utils.ts'\n\nimport type { Event, EventTemplate, Nostr, VerifiedEvent } from './core.ts'\nimport { type Filter } from './filter.ts'\nimport { alwaysTrue } from './helpers.ts'\n\nexport type SubCloser = { close: (reason?: string) => void }\n\nexport type AbstractPoolConstructorOptions = AbstractRelayConstructorOptions & {\n  // automaticallyAuth takes a relay URL and should return null\n  // in case that relay shouldn't be authenticated against\n  // or a function to sign the AUTH event template otherwise (that function may still throw in case of failure)\n  automaticallyAuth?: (relayURL: string) => null | ((event: EventTemplate) => Promise<VerifiedEvent>)\n}\n\nexport type SubscribeManyParams = Omit<SubscriptionParams, 'onclose'> & {\n  maxWait?: number\n  abort?: AbortSignal\n  onclose?: (reasons: string[]) => void\n  onauth?: (event: EventTemplate) => Promise<VerifiedEvent>\n  id?: string\n  label?: string\n}\n\nexport class AbstractSimplePool {\n  protected relays: Map<string, AbstractRelay> = new Map()\n  public seenOn: Map<string, Set<AbstractRelay>> = new Map()\n  public trackRelays: boolean = false\n\n  public verifyEvent: Nostr['verifyEvent']\n  public enablePing: boolean | undefined\n  public enableReconnect: boolean\n  public automaticallyAuth?: (relayURL: string) => null | ((event: EventTemplate) => Promise<VerifiedEvent>)\n  public trustedRelayURLs: Set<string> = new Set()\n\n  private _WebSocket?: typeof WebSocket\n\n  constructor(opts: AbstractPoolConstructorOptions) {\n    this.verifyEvent = opts.verifyEvent\n    this._WebSocket = opts.websocketImplementation\n    this.enablePing = opts.enablePing\n    this.enableReconnect = opts.enableReconnect || false\n    this.automaticallyAuth = opts.automaticallyAuth\n  }\n\n  async ensureRelay(\n    url: string,\n    params?: {\n      connectionTimeout?: number\n      abort?: AbortSignal\n    },\n  ): Promise<AbstractRelay> {\n    url = normalizeURL(url)\n\n    let relay = this.relays.get(url)\n    if (!relay) {\n      relay = new AbstractRelay(url, {\n        verifyEvent: this.trustedRelayURLs.has(url) ? alwaysTrue : this.verifyEvent,\n        websocketImplementation: this._WebSocket,\n        enablePing: this.enablePing,\n        enableReconnect: this.enableReconnect,\n      })\n      relay.onclose = () => {\n        if (relay && !relay.enableReconnect) {\n          this.relays.delete(url)\n        }\n      }\n      this.relays.set(url, relay)\n    }\n\n    if (this.automaticallyAuth) {\n      const authSignerFn = this.automaticallyAuth(url)\n      if (authSignerFn) {\n        relay.onauth = authSignerFn\n      }\n    }\n\n    await relay.connect({\n      timeout: params?.connectionTimeout,\n      abort: params?.abort,\n    })\n\n    return relay\n  }\n\n  close(relays: string[]) {\n    relays.map(normalizeURL).forEach(url => {\n      this.relays.get(url)?.close()\n      this.relays.delete(url)\n    })\n  }\n\n  subscribe(relays: string[], filter: Filter, params: SubscribeManyParams): SubCloser {\n    const request: { url: string; filter: Filter }[] = []\n    for (let i = 0; i < relays.length; i++) {\n      const url = normalizeURL(relays[i])\n      if (!request.find(r => r.url === url)) {\n        request.push({ url, filter: filter })\n      }\n    }\n\n    return this.subscribeMap(request, params)\n  }\n\n  subscribeMany(relays: string[], filter: Filter, params: SubscribeManyParams): SubCloser {\n    const request: { url: string; filter: Filter }[] = []\n    const uniqUrls: string[] = []\n    for (let i = 0; i < relays.length; i++) {\n      const url = normalizeURL(relays[i])\n      if (uniqUrls.indexOf(url) === -1) {\n        uniqUrls.push(url)\n        request.push({ url, filter: filter })\n      }\n    }\n\n    return this.subscribeMap(request, params)\n  }\n\n  subscribeMap(requests: { url: string; filter: Filter }[], params: SubscribeManyParams): SubCloser {\n    const grouped = new Map<string, Filter[]>()\n    for (const req of requests) {\n      const { url, filter } = req\n      if (!grouped.has(url)) grouped.set(url, [])\n      grouped.get(url)!.push(filter)\n    }\n    const groupedRequests = Array.from(grouped.entries()).map(([url, filters]) => ({ url, filters }))\n\n    if (this.trackRelays) {\n      params.receivedEvent = (relay: AbstractRelay, id: string) => {\n        let set = this.seenOn.get(id)\n        if (!set) {\n          set = new Set()\n          this.seenOn.set(id, set)\n        }\n        set.add(relay)\n      }\n    }\n\n    const _knownIds = new Set<string>()\n    const subs: Subscription[] = []\n\n    // batch all EOSEs into a single\n    const eosesReceived: boolean[] = []\n    let handleEose = (i: number) => {\n      if (eosesReceived[i]) return // do not act twice for the same relay\n      eosesReceived[i] = true\n      if (eosesReceived.filter(a => a).length === groupedRequests.length) {\n        params.oneose?.()\n        handleEose = () => {}\n      }\n    }\n    // batch all closes into a single\n    const closesReceived: string[] = []\n    let handleClose = (i: number, reason: string) => {\n      if (closesReceived[i]) return // do not act twice for the same relay\n      handleEose(i)\n      closesReceived[i] = reason\n      if (closesReceived.filter(a => a).length === groupedRequests.length) {\n        params.onclose?.(closesReceived)\n        handleClose = () => {}\n      }\n    }\n\n    const localAlreadyHaveEventHandler = (id: string) => {\n      if (params.alreadyHaveEvent?.(id)) {\n        return true\n      }\n      const have = _knownIds.has(id)\n      _knownIds.add(id)\n      return have\n    }\n\n    // open a subscription in all given relays\n    const allOpened = Promise.all(\n      groupedRequests.map(async ({ url, filters }, i) => {\n        let relay: AbstractRelay\n        try {\n          relay = await this.ensureRelay(url, {\n            connectionTimeout: params.maxWait ? Math.max(params.maxWait * 0.8, params.maxWait - 1000) : undefined,\n            abort: params.abort,\n          })\n        } catch (err) {\n          handleClose(i, (err as any)?.message || String(err))\n          return\n        }\n\n        let subscription = relay.subscribe(filters, {\n          ...params,\n          oneose: () => handleEose(i),\n          onclose: reason => {\n            if (reason.startsWith('auth-required: ') && params.onauth) {\n              relay\n                .auth(params.onauth)\n                .then(() => {\n                  relay.subscribe(filters, {\n                    ...params,\n                    oneose: () => handleEose(i),\n                    onclose: reason => {\n                      handleClose(i, reason) // the second time we won't try to auth anymore\n                    },\n                    alreadyHaveEvent: localAlreadyHaveEventHandler,\n                    eoseTimeout: params.maxWait,\n                    abort: params.abort,\n                  })\n                })\n                .catch(err => {\n                  handleClose(i, `auth was required and attempted, but failed with: ${err}`)\n                })\n            } else {\n              handleClose(i, reason)\n            }\n          },\n          alreadyHaveEvent: localAlreadyHaveEventHandler,\n          eoseTimeout: params.maxWait,\n          abort: params.abort,\n        })\n\n        subs.push(subscription)\n      }),\n    )\n\n    return {\n      async close(reason?: string) {\n        await allOpened\n        subs.forEach(sub => {\n          sub.close(reason)\n        })\n      },\n    }\n  }\n\n  subscribeEose(\n    relays: string[],\n    filter: Filter,\n    params: Pick<SubscribeManyParams, 'label' | 'id' | 'onevent' | 'onclose' | 'maxWait' | 'onauth'>,\n  ): SubCloser {\n    const subcloser = this.subscribe(relays, filter, {\n      ...params,\n      oneose() {\n        subcloser.close('closed automatically on eose')\n      },\n    })\n    return subcloser\n  }\n\n  subscribeManyEose(\n    relays: string[],\n    filter: Filter,\n    params: Pick<SubscribeManyParams, 'label' | 'id' | 'onevent' | 'onclose' | 'maxWait' | 'onauth'>,\n  ): SubCloser {\n    const subcloser = this.subscribeMany(relays, filter, {\n      ...params,\n      oneose() {\n        subcloser.close('closed automatically on eose')\n      },\n    })\n    return subcloser\n  }\n\n  async querySync(\n    relays: string[],\n    filter: Filter,\n    params?: Pick<SubscribeManyParams, 'label' | 'id' | 'maxWait'>,\n  ): Promise<Event[]> {\n    return new Promise(async resolve => {\n      const events: Event[] = []\n      this.subscribeEose(relays, filter, {\n        ...params,\n        onevent(event: Event) {\n          events.push(event)\n        },\n        onclose(_: string[]) {\n          resolve(events)\n        },\n      })\n    })\n  }\n\n  async get(\n    relays: string[],\n    filter: Filter,\n    params?: Pick<SubscribeManyParams, 'label' | 'id' | 'maxWait'>,\n  ): Promise<Event | null> {\n    filter.limit = 1\n    const events = await this.querySync(relays, filter, params)\n    events.sort((a, b) => b.created_at - a.created_at)\n    return events[0] || null\n  }\n\n  publish(\n    relays: string[],\n    event: Event,\n    options?: { onauth?: (evt: EventTemplate) => Promise<VerifiedEvent> },\n  ): Promise<string>[] {\n    return relays.map(normalizeURL).map(async (url, i, arr) => {\n      if (arr.indexOf(url) !== i) {\n        // duplicate\n        return Promise.reject('duplicate url')\n      }\n\n      let r = await this.ensureRelay(url)\n      return r\n        .publish(event)\n        .catch(async err => {\n          if (err instanceof Error && err.message.startsWith('auth-required: ') && options?.onauth) {\n            await r.auth(options.onauth)\n            return r.publish(event) // retry\n          }\n          throw err\n        })\n        .then(reason => {\n          if (this.trackRelays) {\n            let set = this.seenOn.get(event.id)\n            if (!set) {\n              set = new Set()\n              this.seenOn.set(event.id, set)\n            }\n            set.add(r)\n          }\n          return reason\n        })\n    })\n  }\n\n  listConnectionStatus(): Map<string, boolean> {\n    const map = new Map<string, boolean>()\n    this.relays.forEach((relay, url) => map.set(url, relay.connected))\n\n    return map\n  }\n\n  destroy(): void {\n    this.relays.forEach(conn => conn.close())\n    this.relays = new Map()\n  }\n}\n", "/* global WebSocket */\n\nimport { verifyEvent } from './pure.ts'\nimport { AbstractSimplePool, type AbstractPoolConstructorOptions } from './abstract-pool.ts'\n\nvar _WebSocket: typeof WebSocket\n\ntry {\n  _WebSocket = WebSocket\n} catch {}\n\nexport function useWebSocketImplementation(websocketImplementation: any) {\n  _WebSocket = websocketImplementation\n}\n\nexport class SimplePool extends AbstractSimplePool {\n  constructor(options?: Pick<AbstractPoolConstructorOptions, 'enablePing' | 'enableReconnect'>) {\n    super({ verifyEvent, websocketImplementation: _WebSocket, ...options })\n  }\n}\n\nexport * from './abstract-pool.ts'\n"],
+  "mappings": ";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,uBAAwB;AACxB,IAAAA,gBAAuC;;;ACOhC,IAAM,iBAAiB,OAAO,UAAU;AAsB/C,IAAM,WAAW,CAAC,QAAiD,eAAe;AAE3E,SAAS,cAAiB,OAAsC;AACrE,MAAI,CAAC,SAAS,KAAK;AAAG,WAAO;AAC7B,MAAI,OAAO,MAAM,SAAS;AAAU,WAAO;AAC3C,MAAI,OAAO,MAAM,YAAY;AAAU,WAAO;AAC9C,MAAI,OAAO,MAAM,eAAe;AAAU,WAAO;AACjD,MAAI,OAAO,MAAM,WAAW;AAAU,WAAO;AAC7C,MAAI,CAAC,MAAM,OAAO,MAAM,gBAAgB;AAAG,WAAO;AAElD,MAAI,CAAC,MAAM,QAAQ,MAAM,IAAI;AAAG,WAAO;AACvC,WAASC,KAAI,GAAGA,KAAI,MAAM,KAAK,QAAQA,MAAK;AAC1C,QAAI,MAAM,MAAM,KAAKA;AACrB,QAAI,CAAC,MAAM,QAAQ,GAAG;AAAG,aAAO;AAChC,aAAS,IAAI,GAAG,IAAI,IAAI,QAAQ,KAAK;AACnC,UAAI,OAAO,IAAI,OAAO;AAAU,eAAO;AAAA,IACzC;AAAA,EACF;AAEA,SAAO;AACT;;;AD/CA,kBAAuB;;;AEEvB,mBAAuC;AAHhC,IAAM,cAA2B,IAAI,YAAY,OAAO;AACxD,IAAM,cAA2B,IAAI,YAAY;AAIjD,SAAS,aAAa,KAAqB;AAChD,MAAI;AACF,QAAI,IAAI,QAAQ,KAAK,MAAM;AAAI,YAAM,WAAW;AAChD,QAAI,IAAI,IAAI,IAAI,GAAG;AACnB,QAAI,EAAE,aAAa;AAAS,QAAE,WAAW;AAAA,aAChC,EAAE,aAAa;AAAU,QAAE,WAAW;AAC/C,MAAE,WAAW,EAAE,SAAS,QAAQ,QAAQ,GAAG;AAC3C,QAAI,EAAE,SAAS,SAAS,GAAG;AAAG,QAAE,WAAW,EAAE,SAAS,MAAM,GAAG,EAAE;AACjE,QAAK,EAAE,SAAS,QAAQ,EAAE,aAAa,SAAW,EAAE,SAAS,SAAS,EAAE,aAAa;AAAS,QAAE,OAAO;AACvG,MAAE,aAAa,KAAK;AACpB,MAAE,OAAO;AACT,WAAO,EAAE,SAAS;AAAA,EACpB,SAAS,GAAP;AACA,UAAM,IAAI,MAAM,gBAAgB,KAAK;AAAA,EACvC;AACF;AAgDO,IAAM,YAAN,MAAmB;AAAA,EACjB;AAAA,EACA,OAA4B;AAAA,EAC5B,OAA4B;AAAA,EAEnC,YAAY,SAAY;AACtB,SAAK,QAAQ;AAAA,EACf;AACF;AAEO,IAAM,QAAN,MAAe;AAAA,EACb;AAAA,EACA;AAAA,EAEP,cAAc;AACZ,SAAK,QAAQ;AACb,SAAK,OAAO;AAAA,EACd;AAAA,EAEA,QAAQ,OAAmB;AACzB,UAAM,UAAU,IAAI,UAAU,KAAK;AACnC,QAAI,CAAC,KAAK,MAAM;AAEd,WAAK,QAAQ;AACb,WAAK,OAAO;AAAA,IACd,WAAW,KAAK,SAAS,KAAK,OAAO;AAEnC,WAAK,OAAO;AACZ,WAAK,KAAK,OAAO,KAAK;AACtB,WAAK,MAAM,OAAO;AAAA,IACpB,OAAO;AAEL,cAAQ,OAAO,KAAK;AACpB,WAAK,KAAK,OAAO;AACjB,WAAK,OAAO;AAAA,IACd;AACA,WAAO;AAAA,EACT;AAAA,EAEA,UAAoB;AAClB,QAAI,CAAC,KAAK;AAAO,aAAO;AAExB,QAAI,KAAK,UAAU,KAAK,MAAM;AAC5B,YAAMC,UAAS,KAAK;AACpB,WAAK,QAAQ;AACb,WAAK,OAAO;AACZ,aAAOA,QAAO;AAAA,IAChB;AAEA,UAAM,SAAS,KAAK;AACpB,SAAK,QAAQ,OAAO;AACpB,QAAI,KAAK,OAAO;AACd,WAAK,MAAM,OAAO;AAAA,IACpB;AAEA,WAAO,OAAO;AAAA,EAChB;AACF;;;AFxHA,IAAM,KAAN,MAA0B;AAAA,EACxB,oBAAgC;AAC9B,WAAO,yBAAQ,MAAM,gBAAgB;AAAA,EACvC;AAAA,EACA,aAAa,WAA+B;AAC1C,eAAO,0BAAW,yBAAQ,aAAa,SAAS,CAAC;AAAA,EACnD;AAAA,EACA,cAAc,GAAkB,WAAsC;AACpE,UAAM,QAAQ;AACd,UAAM,aAAS,0BAAW,yBAAQ,aAAa,SAAS,CAAC;AACzD,UAAM,KAAK,aAAa,KAAK;AAC7B,UAAM,UAAM,0BAAW,yBAAQ,SAAK,0BAAW,aAAa,KAAK,CAAC,GAAG,SAAS,CAAC;AAC/E,UAAM,kBAAkB;AACxB,WAAO;AAAA,EACT;AAAA,EACA,YAAY,OAAsC;AAChD,QAAI,OAAO,MAAM,oBAAoB;AAAW,aAAO,MAAM;AAE7D,UAAM,OAAO,aAAa,KAAK;AAC/B,QAAI,SAAS,MAAM,IAAI;AACrB,YAAM,kBAAkB;AACxB,aAAO;AAAA,IACT;AAEA,QAAI;AACF,YAAM,QAAQ,yBAAQ,WAAO,0BAAW,MAAM,GAAG,OAAG,0BAAW,IAAI,OAAG,0BAAW,MAAM,MAAM,CAAC;AAC9F,YAAM,kBAAkB;AACxB,aAAO;AAAA,IACT,SAAS,KAAP;AACA,YAAM,kBAAkB;AACxB,aAAO;AAAA,IACT;AAAA,EACF;AACF;AAEO,SAAS,eAAe,KAA4B;AACzD,MAAI,CAAC,cAAc,GAAG;AAAG,UAAM,IAAI,MAAM,wDAAwD;AACjG,SAAO,KAAK,UAAU,CAAC,GAAG,IAAI,QAAQ,IAAI,YAAY,IAAI,MAAM,IAAI,MAAM,IAAI,OAAO,CAAC;AACxF;AAEO,SAAS,aAAa,OAA8B;AACzD,MAAI,gBAAY,oBAAO,YAAY,OAAO,eAAe,KAAK,CAAC,CAAC;AAChE,aAAO,0BAAW,SAAS;AAC7B;AAEA,IAAM,IAAQ,IAAI,GAAG;AAEd,IAAM,oBAAoB,EAAE;AAC5B,IAAM,eAAe,EAAE;AACvB,IAAM,gBAAgB,EAAE;AACxB,IAAM,cAAc,EAAE;;;AGzD7B,oBAAyB;AACzB,IAAAC,gBAA2B;AAC3B,IAAAC,oBAA0B;AAC1B,kBAA+D;AAC/D,kBAAqB;AACrB,IAAAC,eAAuB;AACvB,IAAAF,gBAAqD;AACrD,kBAAuB;AAIvB,IAAM,mBAAmB;AACzB,IAAM,mBAAmB;AAElB,SAAS,mBAAmB,UAAsB,SAA6B;AACpF,QAAM,UAAU,4BAAU,gBAAgB,cAAU,0BAAW,OAAO,OAAO,CAAC,EAAE,SAAS,GAAG,EAAE;AAC9F,aAAO,YAAAG,SAAa,qBAAQ,SAAS,YAAY,OAAO,UAAU,CAAC;AACrE;AAEA,SAAS,eACP,iBACA,OAC4E;AAC5E,QAAM,WAAO,YAAAC,QAAY,qBAAQ,iBAAiB,OAAO,EAAE;AAC3D,SAAO;AAAA,IACL,YAAY,KAAK,SAAS,GAAG,EAAE;AAAA,IAC/B,cAAc,KAAK,SAAS,IAAI,EAAE;AAAA,IAClC,UAAU,KAAK,SAAS,IAAI,EAAE;AAAA,EAChC;AACF;AAEA,SAAS,cAAc,KAAqB;AAC1C,MAAI,CAAC,OAAO,cAAc,GAAG,KAAK,MAAM;AAAG,UAAM,IAAI,MAAM,2BAA2B;AACtF,MAAI,OAAO;AAAI,WAAO;AACtB,QAAM,YAAY,KAAM,KAAK,MAAM,KAAK,KAAK,MAAM,CAAC,CAAC,IAAI;AACzD,QAAM,QAAQ,aAAa,MAAM,KAAK,YAAY;AAClD,SAAO,SAAS,KAAK,OAAO,MAAM,KAAK,KAAK,IAAI;AAClD;AAEA,SAAS,WAAW,KAAyB;AAC3C,MAAI,CAAC,OAAO,cAAc,GAAG,KAAK,MAAM,oBAAoB,MAAM;AAChE,UAAM,IAAI,MAAM,2DAA2D;AAC7E,QAAM,MAAM,IAAI,WAAW,CAAC;AAC5B,MAAI,SAAS,IAAI,MAAM,EAAE,UAAU,GAAG,KAAK,KAAK;AAChD,SAAO;AACT;AAEA,SAAS,IAAI,WAA+B;AAC1C,QAAM,WAAW,YAAY,OAAO,SAAS;AAC7C,QAAM,cAAc,SAAS;AAC7B,QAAM,SAAS,WAAW,WAAW;AACrC,QAAM,SAAS,IAAI,WAAW,cAAc,WAAW,IAAI,WAAW;AACtE,aAAO,2BAAY,QAAQ,UAAU,MAAM;AAC7C;AAEA,SAAS,MAAM,QAA4B;AACzC,QAAM,cAAc,IAAI,SAAS,OAAO,MAAM,EAAE,UAAU,CAAC;AAC3D,QAAM,WAAW,OAAO,SAAS,GAAG,IAAI,WAAW;AACnD,MACE,cAAc,oBACd,cAAc,oBACd,SAAS,WAAW,eACpB,OAAO,WAAW,IAAI,cAAc,WAAW;AAE/C,UAAM,IAAI,MAAM,iBAAiB;AACnC,SAAO,YAAY,OAAO,QAAQ;AACpC;AAEA,SAAS,QAAQ,KAAiB,SAAqB,KAA6B;AAClF,MAAI,IAAI,WAAW;AAAI,UAAM,IAAI,MAAM,sCAAsC;AAC7E,QAAM,eAAW,2BAAY,KAAK,OAAO;AACzC,aAAO,kBAAK,qBAAQ,KAAK,QAAQ;AACnC;AAQA,SAAS,cAAc,SAAiF;AACtG,MAAI,OAAO,YAAY;AAAU,UAAM,IAAI,MAAM,gCAAgC;AACjF,QAAM,OAAO,QAAQ;AACrB,MAAI,OAAO,OAAO,OAAO;AAAO,UAAM,IAAI,MAAM,6BAA6B,IAAI;AACjF,MAAI,QAAQ,OAAO;AAAK,UAAM,IAAI,MAAM,4BAA4B;AACpE,MAAI;AACJ,MAAI;AACF,WAAO,mBAAO,OAAO,OAAO;AAAA,EAC9B,SAAS,OAAP;AACA,UAAM,IAAI,MAAM,qBAAsB,MAAc,OAAO;AAAA,EAC7D;AACA,QAAM,OAAO,KAAK;AAClB,MAAI,OAAO,MAAM,OAAO;AAAO,UAAM,IAAI,MAAM,0BAA0B,IAAI;AAC7E,QAAM,OAAO,KAAK;AAClB,MAAI,SAAS;AAAG,UAAM,IAAI,MAAM,gCAAgC,IAAI;AACpE,SAAO;AAAA,IACL,OAAO,KAAK,SAAS,GAAG,EAAE;AAAA,IAC1B,YAAY,KAAK,SAAS,IAAI,GAAG;AAAA,IACjC,KAAK,KAAK,SAAS,GAAG;AAAA,EACxB;AACF;AAEO,SAAS,QAAQ,WAAmB,iBAA6B,YAAoB,2BAAY,EAAE,GAAW;AACnH,QAAM,EAAE,YAAY,cAAc,SAAS,IAAI,eAAe,iBAAiB,KAAK;AACpF,QAAM,SAAS,IAAI,SAAS;AAC5B,QAAM,iBAAa,wBAAS,YAAY,cAAc,MAAM;AAC5D,QAAM,MAAM,QAAQ,UAAU,YAAY,KAAK;AAC/C,SAAO,mBAAO,WAAO,2BAAY,IAAI,WAAW,CAAC,CAAC,CAAC,GAAG,OAAO,YAAY,GAAG,CAAC;AAC/E;AAEO,SAAS,QAAQ,SAAiB,iBAAqC;AAC5E,QAAM,EAAE,OAAO,YAAY,IAAI,IAAI,cAAc,OAAO;AACxD,QAAM,EAAE,YAAY,cAAc,SAAS,IAAI,eAAe,iBAAiB,KAAK;AACpF,QAAM,gBAAgB,QAAQ,UAAU,YAAY,KAAK;AACzD,MAAI,KAAC,0BAAW,eAAe,GAAG;AAAG,UAAM,IAAI,MAAM,aAAa;AAClE,QAAM,aAAS,wBAAS,YAAY,cAAc,UAAU;AAC5D,SAAO,MAAM,MAAM;AACrB;;;AC1GO,IAAM,cAAc;AAI3B,IAAI;AAEJ,IAAI;AACF,WAAS;AACX,SAAS,GAAP;AACA;AACF;;;AC0IO,IAAM,aAAa;AAMnB,IAAM,eAAe;AAoDrB,IAAM,qBAAqB;;;AC3M3B,SAAS,YAAY,QAAgB,OAAuB;AACjE,MAAI,OAAO,OAAO,OAAO,IAAI,QAAQ,MAAM,EAAE,MAAM,IAAI;AACrD,WAAO;AAAA,EACT;AACA,MAAI,OAAO,SAAS,OAAO,MAAM,QAAQ,MAAM,IAAI,MAAM,IAAI;AAC3D,WAAO;AAAA,EACT;AACA,MAAI,OAAO,WAAW,OAAO,QAAQ,QAAQ,MAAM,MAAM,MAAM,IAAI;AACjE,WAAO;AAAA,EACT;AAEA,WAAS,KAAK,QAAQ;AACpB,QAAI,EAAE,OAAO,KAAK;AAChB,UAAI,UAAU,EAAE,MAAM,CAAC;AACvB,UAAI,SAAS,OAAO,IAAI;AACxB,UAAI,UAAU,CAAC,MAAM,KAAK,KAAK,CAAC,CAAC,GAAG,CAAC,MAAM,MAAM,EAAE,MAAM,CAAC,KAAK,OAAQ,QAAQ,CAAC,MAAM,EAAE;AAAG,eAAO;AAAA,IACpG;AAAA,EACF;AAEA,MAAI,OAAO,SAAS,MAAM,aAAa,OAAO;AAAO,WAAO;AAC5D,MAAI,OAAO,SAAS,MAAM,aAAa,OAAO;AAAO,WAAO;AAE5D,SAAO;AACT;AAEO,SAAS,aAAa,SAAmB,OAAuB;AACrE,WAASC,KAAI,GAAGA,KAAI,QAAQ,QAAQA,MAAK;AACvC,QAAI,YAAY,QAAQA,KAAI,KAAK,GAAG;AAClC,aAAO;AAAA,IACT;AAAA,EACF;AACA,SAAO;AACT;;;AC9CO,SAAS,SAAS,MAAc,OAAuB;AAC5D,MAAI,MAAM,MAAM,SAAS;AACzB,MAAI,MAAM,KAAK,QAAQ,IAAI,SAAS,IAAI;AACxC,MAAI,IAAI,KAAK,MAAM,GAAG,EAAE,QAAQ,GAAG,IAAI,MAAM;AAC7C,SAAO,KAAK,MAAM,GAAG,IAAI,EAAE;AAC7B;AAUO,SAAS,kBAAkB,MAA6B;AAC7D,MAAI,MAAM,KAAK,MAAM,GAAG,EAAE,EAAE,QAAQ,SAAS;AAC7C,MAAI,QAAQ;AAAI,WAAO;AAEvB,MAAI,SAAS,KAAK,MAAM,MAAM,IAAI,CAAC,EAAE,QAAQ,GAAG;AAChD,MAAI,WAAW;AAAI,WAAO;AAC1B,MAAI,QAAQ,MAAM,IAAI,IAAI;AAE1B,MAAI,OAAO,KAAK,MAAM,QAAQ,GAAG,EAAE,EAAE,QAAQ,GAAG;AAChD,MAAI,SAAS;AAAI,WAAO;AACxB,MAAI,MAAM,QAAQ,IAAI;AAEtB,SAAO,KAAK,MAAM,QAAQ,GAAG,GAAG;AAClC;;;ACtBO,SAAS,cAAc,UAAkB,WAAkC;AAChF,SAAO;AAAA,IACL,MAAM;AAAA,IACN,YAAY,KAAK,MAAM,KAAK,IAAI,IAAI,GAAI;AAAA,IACxC,MAAM;AAAA,MACJ,CAAC,SAAS,QAAQ;AAAA,MAClB,CAAC,aAAa,SAAS;AAAA,IACzB;AAAA,IACA,SAAS;AAAA,EACX;AACF;;;ACdA,eAAsB,cAAc;AAClC,SAAO,IAAI,QAAc,CAAC,SAAS,WAAW;AAC5C,QAAI;AAEF,UAAI,OAAO,mBAAmB,aAAa;AACzC,cAAM,KAAK,IAAI,eAAe;AAC9B,cAAM,UAAU,MAAM;AAEpB,aAAG,MAAM,oBAAoB,WAAW,OAAO;AAC/C,kBAAQ;AAAA,QACV;AAEA,WAAG,MAAM,iBAAiB,WAAW,OAAO;AAC5C,WAAG,MAAM,YAAY,CAAC;AACtB,WAAG,MAAM,MAAM;AAAA,MACjB,OAAO;AACL,YAAI,OAAO,iBAAiB,aAAa;AACvC,uBAAa,OAAO;AAAA,QACtB,WAAW,OAAO,eAAe,aAAa;AAC5C,qBAAW,SAAS,CAAC;AAAA,QACvB,OAAO;AAEL,kBAAQ;AAAA,QACV;AAAA,MACF;AAAA,IACF,SAAS,GAAP;AACA,cAAQ,MAAM,kBAAkB,CAAC;AACjC,aAAO,CAAC;AAAA,IACV;AAAA,EACF,CAAC;AACH;AAEO,IAAM,aAAmC,CAAC,MAAiC;AAChF,IAAE,kBAAkB;AACpB,SAAO;AACT;;;AChBO,IAAM,4BAAN,cAAwC,MAAM;AAAA,EACnD,YAAY,SAAiB,OAAe;AAC1C,UAAM,0BAA0B,qCAAqC,QAAQ;AAC7E,SAAK,OAAO;AAAA,EACd;AACF;AAEO,IAAM,gBAAN,MAAoB;AAAA,EACT;AAAA,EACR,aAAsB;AAAA,EAEvB,UAA+B;AAAA,EAC/B,WAAkC,SAAO,QAAQ,MAAM,eAAe,KAAK,QAAQ,KAAK;AAAA,EACxF;AAAA,EAEA,kBAA0B;AAAA,EAC1B,iBAAyB;AAAA,EACzB,gBAAwB;AAAA,EACxB,cAAsB;AAAA,EACtB,qBAA+B,CAAC,KAAO,KAAO,KAAO,KAAO,KAAO,KAAO,GAAK;AAAA,EAC/E,WAAsC,oBAAI,IAAI;AAAA,EAC9C;AAAA,EACA;AAAA,EACC;AAAA,EACA;AAAA,EACA,oBAA4B;AAAA,EAC5B,sBAA+B;AAAA,EAE/B;AAAA,EACA,oBAAoB,oBAAI,IAA2B;AAAA,EACnD,qBAAqB,oBAAI,IAAkC;AAAA,EAC3D;AAAA,EACA,uBAAuB,IAAI,MAAc;AAAA,EACzC,eAAe;AAAA,EACf;AAAA,EACA;AAAA,EACA,SAAiB;AAAA,EACjB;AAAA,EAEA;AAAA,EAER,YAAY,KAAa,MAAuC;AAC9D,SAAK,MAAM,aAAa,GAAG;AAC3B,SAAK,cAAc,KAAK;AACxB,SAAK,aAAa,KAAK,2BAA2B;AAClD,SAAK,aAAa,KAAK;AACvB,SAAK,kBAAkB,KAAK,mBAAmB;AAAA,EACjD;AAAA,EAEA,aAAa,QACX,KACA,MACwB;AACxB,UAAM,QAAQ,IAAI,cAAc,KAAK,IAAI;AACzC,UAAM,MAAM,QAAQ,IAAI;AACxB,WAAO;AAAA,EACT;AAAA,EAEQ,sBAAsB,QAAgB;AAC5C,aAAS,CAAC,GAAG,GAAG,KAAK,KAAK,UAAU;AAClC,UAAI,MAAM,MAAM;AAAA,IAClB;AACA,SAAK,SAAS,MAAM;AAEpB,aAAS,CAAC,GAAG,EAAE,KAAK,KAAK,oBAAoB;AAC3C,SAAG,OAAO,IAAI,MAAM,MAAM,CAAC;AAAA,IAC7B;AACA,SAAK,mBAAmB,MAAM;AAE9B,aAAS,CAAC,GAAG,EAAE,KAAK,KAAK,mBAAmB;AAC1C,SAAG,OAAO,IAAI,MAAM,MAAM,CAAC;AAAA,IAC7B;AACA,SAAK,kBAAkB,MAAM;AAAA,EAC/B;AAAA,EAEA,IAAW,YAAqB;AAC9B,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,MAAc,YAA2B;AACvC,UAAM,UAAU,KAAK,mBAAmB,KAAK,IAAI,KAAK,mBAAmB,KAAK,mBAAmB,SAAS,CAAC;AAC3G,SAAK;AAEL,SAAK,yBAAyB,WAAW,YAAY;AACnD,UAAI;AACF,cAAM,KAAK,QAAQ;AAAA,MACrB,SAAS,KAAP;AAAA,MAEF;AAAA,IACF,GAAG,OAAO;AAAA,EACZ;AAAA,EAEQ,gBAAgB,QAAgB;AACtC,QAAI,KAAK,oBAAoB;AAC3B,oBAAc,KAAK,kBAAkB;AACrC,WAAK,qBAAqB;AAAA,IAC5B;AAEA,SAAK,aAAa;AAClB,SAAK,oBAAoB;AAEzB,UAAM,iBAAiB,KAAK;AAC5B,SAAK,sBAAsB;AAE3B,SAAK,UAAU;AAEf,QAAI,KAAK,mBAAmB,CAAC,gBAAgB;AAC3C,WAAK,UAAU;AAAA,IACjB,OAAO;AACL,WAAK,sBAAsB,MAAM;AAAA,IACnC;AAAA,EACF;AAAA,EAEA,MAAa,QAAQ,MAAiE;AACpF,QAAI;AAEJ,QAAI,KAAK;AAAmB,aAAO,KAAK;AAExC,SAAK,YAAY;AACjB,SAAK,cAAc;AACnB,SAAK,oBAAoB,IAAI,QAAQ,CAAC,SAAS,WAAW;AACxD,UAAI,MAAM,SAAS;AACjB,kCAA0B,WAAW,MAAM;AACzC,iBAAO,sBAAsB;AAC7B,eAAK,oBAAoB;AACzB,eAAK,UAAU;AACf,eAAK,sBAAsB,4BAA4B;AAAA,QACzD,GAAG,KAAK,OAAO;AAAA,MACjB;AAEA,UAAI,MAAM,OAAO;AACf,aAAK,MAAM,UAAU;AAAA,MACvB;AAEA,UAAI;AACF,aAAK,KAAK,IAAI,KAAK,WAAW,KAAK,GAAG;AAAA,MACxC,SAAS,KAAP;AACA,qBAAa,uBAAuB;AACpC,eAAO,GAAG;AACV;AAAA,MACF;AAEA,WAAK,GAAG,SAAS,MAAM;AACrB,YAAI,KAAK,wBAAwB;AAC/B,uBAAa,KAAK,sBAAsB;AACxC,eAAK,yBAAyB;AAAA,QAChC;AACA,qBAAa,uBAAuB;AACpC,aAAK,aAAa;AAElB,cAAM,iBAAiB,KAAK,oBAAoB;AAChD,aAAK,oBAAoB;AAGzB,mBAAW,OAAO,KAAK,SAAS,OAAO,GAAG;AACxC,cAAI,QAAQ;AACZ,cAAI,gBAAgB;AAClB,qBAAS,IAAI,GAAG,IAAI,IAAI,QAAQ,QAAQ,KAAK;AAC3C,kBAAI,IAAI,aAAa;AACnB,oBAAI,QAAQ,GAAG,QAAQ,IAAI,cAAc;AAAA,cAC3C;AAAA,YACF;AAAA,UACF;AACA,cAAI,KAAK;AAAA,QACX;AAEA,YAAI,KAAK,YAAY;AACnB,eAAK,qBAAqB,YAAY,MAAM,KAAK,SAAS,GAAG,KAAK,aAAa;AAAA,QACjF;AACA,gBAAQ;AAAA,MACV;AAEA,WAAK,GAAG,UAAU,QAAM;AACtB,qBAAa,uBAAuB;AACpC,eAAQ,GAAW,WAAW,iBAAiB;AAC/C,aAAK,gBAAgB,0BAA0B;AAAA,MACjD;AAEA,WAAK,GAAG,UAAU,QAAM;AACtB,qBAAa,uBAAuB;AACpC,eAAQ,GAAW,WAAW,kBAAkB;AAChD,aAAK,gBAAgB,yBAAyB;AAAA,MAChD;AAEA,WAAK,GAAG,YAAY,KAAK,WAAW,KAAK,IAAI;AAAA,IAC/C,CAAC;AAED,WAAO,KAAK;AAAA,EACd;AAAA,EAEQ,kBAAkB;AACxB,WAAO,IAAI,QAAQ,aAAW;AAE5B;AAAC,MAAC,KAAK,GAAW,KAAK,QAAQ,MAAM,QAAQ,IAAI,CAAC;AAElD,WAAK,GAAI,KAAM;AAAA,IACjB,CAAC;AAAA,EACH;AAAA,EAEQ,kBAAkB;AACxB,WAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,UAAI,CAAC,KAAK;AAAmB,eAAO,OAAO,IAAI,MAAM,oBAAoB,KAAK,iBAAiB,CAAC;AAIhG,UAAI;AACF,cAAM,MAAM,KAAK;AAAA,UACf,CAAC,EAAE,KAAK,CAAC,kEAAkE,GAAG,OAAO,EAAE,CAAC;AAAA,UACxF;AAAA,YACE,OAAO;AAAA,YACP,QAAQ,MAAM;AACZ,sBAAQ,IAAI;AACZ,kBAAI,MAAM;AAAA,YACZ;AAAA,YACA,UAAU;AAER,sBAAQ,IAAI;AAAA,YACd;AAAA,YACA,aAAa,KAAK,cAAc;AAAA,UAClC;AAAA,QACF;AAAA,MACF,SAAS,KAAP;AACA,eAAO,GAAG;AAAA,MACZ;AAAA,IACF,CAAC;AAAA,EACH;AAAA,EAIA,MAAc,WAAW;AAEvB,QAAI,KAAK,IAAI,eAAe,GAAG;AAE7B,YAAM,SAAS,MAAM,QAAQ,IAAI;AAAA,QAE/B,KAAK,MAAM,KAAK,GAAG,QAAS,KAAK,GAAW,OAAO,KAAK,gBAAgB,IAAI,KAAK,gBAAgB;AAAA,QACjG,IAAI,QAAQ,SAAO,WAAW,MAAM,IAAI,KAAK,GAAG,KAAK,WAAW,CAAC;AAAA,MACnE,CAAC;AAED,UAAI,CAAC,QAAQ;AAEX,YAAI,KAAK,IAAI,eAAe,KAAK,WAAW,MAAM;AAChD,eAAK,IAAI,MAAM;AAAA,QACjB;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA,EAEA,MAAc,WAAW;AACvB,SAAK,eAAe;AACpB,WAAO,MAAM;AACX,UAAI,UAAU,KAAK,WAAW,GAAG;AAC/B;AAAA,MACF;AACA,YAAM,YAAY;AAAA,IACpB;AACA,SAAK,eAAe;AAAA,EACtB;AAAA,EAEQ,aAAgC;AACtC,UAAM,OAAO,KAAK,qBAAqB,QAAQ;AAC/C,QAAI,CAAC,MAAM;AACT,aAAO;AAAA,IACT;AAGA,UAAM,QAAQ,kBAAkB,IAAI;AACpC,QAAI,OAAO;AACT,YAAM,KAAK,KAAK,SAAS,IAAI,KAAe;AAC5C,UAAI,CAAC,IAAI;AAEP;AAAA,MACF;AAKA,YAAM,KAAK,SAAS,MAAM,IAAI;AAC9B,YAAM,cAAc,GAAG,mBAAmB,EAAE;AAI5C,SAAG,gBAAgB,MAAM,EAAE;AAE3B,UAAI,aAAa;AAEf;AAAA,MACF;AAAA,IACF;AAEA,QAAI;AACF,UAAI,OAAO,KAAK,MAAM,IAAI;AAI1B,cAAQ,KAAK,IAAI;AAAA,QACf,KAAK,SAAS;AACZ,gBAAM,KAAK,KAAK,SAAS,IAAI,KAAK,EAAY;AAC9C,gBAAM,QAAQ,KAAK;AACnB,cAAI,KAAK,YAAY,KAAK,KAAK,aAAa,GAAG,SAAS,KAAK,GAAG;AAC9D,eAAG,QAAQ,KAAK;AAAA,UAClB;AACA,cAAI,CAAC,GAAG,eAAe,GAAG,cAAc,MAAM;AAAY,eAAG,cAAc,MAAM;AACjF;AAAA,QACF;AAAA,QACA,KAAK,SAAS;AACZ,gBAAM,KAAa,KAAK;AACxB,gBAAM,UAAU,KAAK;AACrB,gBAAM,KAAK,KAAK,kBAAkB,IAAI,EAAE;AACxC,cAAI,IAAI;AACN,eAAG,QAAQ,QAAQ,KAAK;AACxB,iBAAK,kBAAkB,OAAO,EAAE;AAAA,UAClC;AACA;AAAA,QACF;AAAA,QACA,KAAK,QAAQ;AACX,gBAAM,KAAK,KAAK,SAAS,IAAI,KAAK,EAAY;AAC9C,cAAI,CAAC;AAAI;AACT,aAAG,aAAa;AAChB;AAAA,QACF;AAAA,QACA,KAAK,MAAM;AACT,gBAAM,KAAa,KAAK;AACxB,gBAAM,KAAc,KAAK;AACzB,gBAAM,SAAiB,KAAK;AAC5B,gBAAM,KAAK,KAAK,mBAAmB,IAAI,EAAE;AACzC,cAAI,IAAI;AACN,yBAAa,GAAG,OAAO;AACvB,gBAAI;AAAI,iBAAG,QAAQ,MAAM;AAAA;AACpB,iBAAG,OAAO,IAAI,MAAM,MAAM,CAAC;AAChC,iBAAK,mBAAmB,OAAO,EAAE;AAAA,UACnC;AACA;AAAA,QACF;AAAA,QACA,KAAK,UAAU;AACb,gBAAM,KAAa,KAAK;AACxB,gBAAM,KAAK,KAAK,SAAS,IAAI,EAAE;AAC/B,cAAI,CAAC;AAAI;AACT,aAAG,SAAS;AACZ,aAAG,MAAM,KAAK,EAAY;AAC1B;AAAA,QACF;AAAA,QACA,KAAK,UAAU;AACb,eAAK,SAAS,KAAK,EAAY;AAC/B;AAAA,QACF;AAAA,QACA,KAAK,QAAQ;AACX,eAAK,YAAY,KAAK;AACtB,cAAI,KAAK,QAAQ;AACf,iBAAK,KAAK,KAAK,MAAM;AAAA,UACvB;AACA;AAAA,QACF;AAAA,QACA,SAAS;AACP,gBAAM,KAAK,KAAK,SAAS,IAAI,KAAK,EAAE;AACpC,cAAI,WAAW,IAAI;AACnB;AAAA,QACF;AAAA,MACF;AAAA,IACF,SAAS,KAAP;AACA;AAAA,IACF;AAAA,EACF;AAAA,EAEA,MAAa,KAAK,SAAiB;AACjC,QAAI,CAAC,KAAK;AAAmB,YAAM,IAAI,0BAA0B,SAAS,KAAK,GAAG;AAElF,SAAK,kBAAkB,KAAK,MAAM;AAChC,WAAK,IAAI,KAAK,OAAO;AAAA,IACvB,CAAC;AAAA,EACH;AAAA,EAEA,MAAa,KAAK,eAAgF;AAChG,UAAM,YAAY,KAAK;AACvB,QAAI,CAAC;AAAW,YAAM,IAAI,MAAM,+CAA+C;AAC/E,QAAI,KAAK;AAAa,aAAO,KAAK;AAElC,SAAK,cAAc,IAAI,QAAgB,OAAO,SAAS,WAAW;AAChE,UAAI;AACF,YAAI,MAAM,MAAM,cAAc,cAAc,KAAK,KAAK,SAAS,CAAC;AAChE,YAAI,UAAU,WAAW,MAAM;AAC7B,cAAI,KAAK,KAAK,mBAAmB,IAAI,IAAI,EAAE;AAC3C,cAAI,IAAI;AACN,eAAG,OAAO,IAAI,MAAM,gBAAgB,CAAC;AACrC,iBAAK,mBAAmB,OAAO,IAAI,EAAE;AAAA,UACvC;AAAA,QACF,GAAG,KAAK,cAAc;AACtB,aAAK,mBAAmB,IAAI,IAAI,IAAI,EAAE,SAAS,QAAQ,QAAQ,CAAC;AAChE,aAAK,KAAK,aAAa,KAAK,UAAU,GAAG,IAAI,GAAG;AAAA,MAClD,SAAS,KAAP;AACA,gBAAQ,KAAK,mCAAmC,GAAG;AAAA,MACrD;AAAA,IACF,CAAC;AACD,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,MAAa,QAAQ,OAA+B;AAClD,UAAM,MAAM,IAAI,QAAgB,CAAC,SAAS,WAAW;AACnD,YAAM,UAAU,WAAW,MAAM;AAC/B,cAAM,KAAK,KAAK,mBAAmB,IAAI,MAAM,EAAE;AAC/C,YAAI,IAAI;AACN,aAAG,OAAO,IAAI,MAAM,mBAAmB,CAAC;AACxC,eAAK,mBAAmB,OAAO,MAAM,EAAE;AAAA,QACzC;AAAA,MACF,GAAG,KAAK,cAAc;AACtB,WAAK,mBAAmB,IAAI,MAAM,IAAI,EAAE,SAAS,QAAQ,QAAQ,CAAC;AAAA,IACpE,CAAC;AACD,SAAK,KAAK,cAAc,KAAK,UAAU,KAAK,IAAI,GAAG;AACnD,WAAO;AAAA,EACT;AAAA,EAEA,MAAa,MAAM,SAAmB,QAAiD;AACrF,SAAK;AACL,UAAM,KAAK,QAAQ,MAAM,WAAW,KAAK;AACzC,UAAM,MAAM,IAAI,QAAgB,CAAC,SAAS,WAAW;AACnD,WAAK,kBAAkB,IAAI,IAAI,EAAE,SAAS,OAAO,CAAC;AAAA,IACpD,CAAC;AACD,SAAK,KAAK,eAAe,KAAK,OAAO,KAAK,UAAU,OAAO,EAAE,UAAU,CAAC,CAAC;AACzE,WAAO;AAAA,EACT;AAAA,EAEO,UACL,SACA,QACc;AACd,UAAM,MAAM,KAAK,oBAAoB,SAAS,MAAM;AACpD,QAAI,KAAK;AAET,QAAI,OAAO,OAAO;AAChB,aAAO,MAAM,UAAU,MAAM,IAAI,MAAM,OAAO,OAAO,MAAO,UAAU,WAAW,CAAC;AAAA,IACpF;AAEA,WAAO;AAAA,EACT;AAAA,EAEO,oBACL,SACA,QACc;AACd,SAAK;AACL,UAAM,KAAK,OAAO,OAAO,OAAO,QAAQ,OAAO,QAAQ,MAAM,UAAU,KAAK;AAC5E,UAAM,eAAe,IAAI,aAAa,MAAM,IAAI,SAAS,MAAM;AAC/D,SAAK,SAAS,IAAI,IAAI,YAAY;AAClC,WAAO;AAAA,EACT;AAAA,EAEO,QAAQ;AACb,SAAK,sBAAsB;AAC3B,QAAI,KAAK,wBAAwB;AAC/B,mBAAa,KAAK,sBAAsB;AACxC,WAAK,yBAAyB;AAAA,IAChC;AACA,QAAI,KAAK,oBAAoB;AAC3B,oBAAc,KAAK,kBAAkB;AACrC,WAAK,qBAAqB;AAAA,IAC5B;AACA,SAAK,sBAAsB,+BAA+B;AAC1D,SAAK,aAAa;AAClB,SAAK,UAAU;AACf,QAAI,KAAK,IAAI,eAAe,KAAK,WAAW,MAAM;AAChD,WAAK,IAAI,MAAM;AAAA,IACjB;AAAA,EACF;AAAA,EAIO,WAAW,IAAuB;AACvC,SAAK,qBAAqB,QAAQ,GAAG,IAAc;AACnD,QAAI,CAAC,KAAK,cAAc;AACtB,WAAK,SAAS;AAAA,IAChB;AAAA,EACF;AACF;AAEO,IAAM,eAAN,MAAmB;AAAA,EACR;AAAA,EACA;AAAA,EAET;AAAA,EACA,SAAkB;AAAA,EAClB,QAAiB;AAAA,EACjB;AAAA,EACA;AAAA,EACA;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,EAGA;AAAA,EAEA;AAAA,EACC;AAAA,EAER,YAAY,OAAsB,IAAY,SAAmB,QAA4B;AAC3F,QAAI,QAAQ,WAAW;AAAG,YAAM,IAAI,MAAM,iDAAiD;AAE3F,SAAK,QAAQ;AACb,SAAK,UAAU;AACf,SAAK,KAAK;AACV,SAAK,mBAAmB,OAAO;AAC/B,SAAK,gBAAgB,OAAO;AAC5B,SAAK,cAAc,OAAO,eAAe,MAAM;AAE/C,SAAK,SAAS,OAAO;AACrB,SAAK,UAAU,OAAO;AACtB,SAAK,UACH,OAAO,YACN,WAAS;AACR,cAAQ;AAAA,QACN,oDAAoD,KAAK,gBAAgB,KAAK,MAAM;AAAA,QACpF;AAAA,MACF;AAAA,IACF;AAAA,EACJ;AAAA,EAEO,OAAO;AACZ,SAAK,MAAM,KAAK,aAAa,KAAK,KAAK,OAAO,KAAK,UAAU,KAAK,OAAO,EAAE,UAAU,CAAC,CAAC;AAGvF,SAAK,oBAAoB,WAAW,KAAK,aAAa,KAAK,IAAI,GAAG,KAAK,WAAW;AAAA,EACpF;AAAA,EAEO,eAAe;AACpB,QAAI,KAAK;AAAO;AAChB,iBAAa,KAAK,iBAAiB;AACnC,SAAK,QAAQ;AACb,SAAK,SAAS;AAAA,EAChB;AAAA,EAEO,MAAM,SAAiB,oBAAoB;AAChD,QAAI,CAAC,KAAK,UAAU,KAAK,MAAM,WAAW;AAGxC,UAAI;AACF,aAAK,MAAM,KAAK,cAAc,KAAK,UAAU,KAAK,EAAE,IAAI,GAAG;AAAA,MAC7D,SAAS,KAAP;AACA,YAAI,eAAe,2BAA2B;AAAA,QAE9C,OAAO;AACL,gBAAM;AAAA,QACR;AAAA,MACF;AACA,WAAK,SAAS;AAAA,IAChB;AACA,SAAK,MAAM,SAAS,OAAO,KAAK,EAAE;AAClC,SAAK,UAAU,MAAM;AAAA,EACvB;AACF;;;AC1hBO,IAAM,qBAAN,MAAyB;AAAA,EACpB,SAAqC,oBAAI,IAAI;AAAA,EAChD,SAA0C,oBAAI,IAAI;AAAA,EAClD,cAAuB;AAAA,EAEvB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,mBAAgC,oBAAI,IAAI;AAAA,EAEvC;AAAA,EAER,YAAY,MAAsC;AAChD,SAAK,cAAc,KAAK;AACxB,SAAK,aAAa,KAAK;AACvB,SAAK,aAAa,KAAK;AACvB,SAAK,kBAAkB,KAAK,mBAAmB;AAC/C,SAAK,oBAAoB,KAAK;AAAA,EAChC;AAAA,EAEA,MAAM,YACJ,KACA,QAIwB;AACxB,UAAM,aAAa,GAAG;AAEtB,QAAI,QAAQ,KAAK,OAAO,IAAI,GAAG;AAC/B,QAAI,CAAC,OAAO;AACV,cAAQ,IAAI,cAAc,KAAK;AAAA,QAC7B,aAAa,KAAK,iBAAiB,IAAI,GAAG,IAAI,aAAa,KAAK;AAAA,QAChE,yBAAyB,KAAK;AAAA,QAC9B,YAAY,KAAK;AAAA,QACjB,iBAAiB,KAAK;AAAA,MACxB,CAAC;AACD,YAAM,UAAU,MAAM;AACpB,YAAI,SAAS,CAAC,MAAM,iBAAiB;AACnC,eAAK,OAAO,OAAO,GAAG;AAAA,QACxB;AAAA,MACF;AACA,WAAK,OAAO,IAAI,KAAK,KAAK;AAAA,IAC5B;AAEA,QAAI,KAAK,mBAAmB;AAC1B,YAAM,eAAe,KAAK,kBAAkB,GAAG;AAC/C,UAAI,cAAc;AAChB,cAAM,SAAS;AAAA,MACjB;AAAA,IACF;AAEA,UAAM,MAAM,QAAQ;AAAA,MAClB,SAAS,QAAQ;AAAA,MACjB,OAAO,QAAQ;AAAA,IACjB,CAAC;AAED,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,QAAkB;AACtB,WAAO,IAAI,YAAY,EAAE,QAAQ,SAAO;AACtC,WAAK,OAAO,IAAI,GAAG,GAAG,MAAM;AAC5B,WAAK,OAAO,OAAO,GAAG;AAAA,IACxB,CAAC;AAAA,EACH;AAAA,EAEA,UAAU,QAAkB,QAAgB,QAAwC;AAClF,UAAM,UAA6C,CAAC;AACpD,aAASC,KAAI,GAAGA,KAAI,OAAO,QAAQA,MAAK;AACtC,YAAM,MAAM,aAAa,OAAOA,GAAE;AAClC,UAAI,CAAC,QAAQ,KAAK,OAAK,EAAE,QAAQ,GAAG,GAAG;AACrC,gBAAQ,KAAK,EAAE,KAAK,OAAe,CAAC;AAAA,MACtC;AAAA,IACF;AAEA,WAAO,KAAK,aAAa,SAAS,MAAM;AAAA,EAC1C;AAAA,EAEA,cAAc,QAAkB,QAAgB,QAAwC;AACtF,UAAM,UAA6C,CAAC;AACpD,UAAM,WAAqB,CAAC;AAC5B,aAASA,KAAI,GAAGA,KAAI,OAAO,QAAQA,MAAK;AACtC,YAAM,MAAM,aAAa,OAAOA,GAAE;AAClC,UAAI,SAAS,QAAQ,GAAG,MAAM,IAAI;AAChC,iBAAS,KAAK,GAAG;AACjB,gBAAQ,KAAK,EAAE,KAAK,OAAe,CAAC;AAAA,MACtC;AAAA,IACF;AAEA,WAAO,KAAK,aAAa,SAAS,MAAM;AAAA,EAC1C;AAAA,EAEA,aAAa,UAA6C,QAAwC;AAChG,UAAM,UAAU,oBAAI,IAAsB;AAC1C,eAAW,OAAO,UAAU;AAC1B,YAAM,EAAE,KAAK,OAAO,IAAI;AACxB,UAAI,CAAC,QAAQ,IAAI,GAAG;AAAG,gBAAQ,IAAI,KAAK,CAAC,CAAC;AAC1C,cAAQ,IAAI,GAAG,EAAG,KAAK,MAAM;AAAA,IAC/B;AACA,UAAM,kBAAkB,MAAM,KAAK,QAAQ,QAAQ,CAAC,EAAE,IAAI,CAAC,CAAC,KAAK,OAAO,OAAO,EAAE,KAAK,QAAQ,EAAE;AAEhG,QAAI,KAAK,aAAa;AACpB,aAAO,gBAAgB,CAAC,OAAsB,OAAe;AAC3D,YAAI,MAAM,KAAK,OAAO,IAAI,EAAE;AAC5B,YAAI,CAAC,KAAK;AACR,gBAAM,oBAAI,IAAI;AACd,eAAK,OAAO,IAAI,IAAI,GAAG;AAAA,QACzB;AACA,YAAI,IAAI,KAAK;AAAA,MACf;AAAA,IACF;AAEA,UAAM,YAAY,oBAAI,IAAY;AAClC,UAAM,OAAuB,CAAC;AAG9B,UAAM,gBAA2B,CAAC;AAClC,QAAI,aAAa,CAACA,OAAc;AAC9B,UAAI,cAAcA;AAAI;AACtB,oBAAcA,MAAK;AACnB,UAAI,cAAc,OAAO,OAAK,CAAC,EAAE,WAAW,gBAAgB,QAAQ;AAClE,eAAO,SAAS;AAChB,qBAAa,MAAM;AAAA,QAAC;AAAA,MACtB;AAAA,IACF;AAEA,UAAM,iBAA2B,CAAC;AAClC,QAAI,cAAc,CAACA,IAAW,WAAmB;AAC/C,UAAI,eAAeA;AAAI;AACvB,iBAAWA,EAAC;AACZ,qBAAeA,MAAK;AACpB,UAAI,eAAe,OAAO,OAAK,CAAC,EAAE,WAAW,gBAAgB,QAAQ;AACnE,eAAO,UAAU,cAAc;AAC/B,sBAAc,MAAM;AAAA,QAAC;AAAA,MACvB;AAAA,IACF;AAEA,UAAM,+BAA+B,CAAC,OAAe;AACnD,UAAI,OAAO,mBAAmB,EAAE,GAAG;AACjC,eAAO;AAAA,MACT;AACA,YAAM,OAAO,UAAU,IAAI,EAAE;AAC7B,gBAAU,IAAI,EAAE;AAChB,aAAO;AAAA,IACT;AAGA,UAAM,YAAY,QAAQ;AAAA,MACxB,gBAAgB,IAAI,OAAO,EAAE,KAAK,QAAQ,GAAGA,OAAM;AACjD,YAAI;AACJ,YAAI;AACF,kBAAQ,MAAM,KAAK,YAAY,KAAK;AAAA,YAClC,mBAAmB,OAAO,UAAU,KAAK,IAAI,OAAO,UAAU,KAAK,OAAO,UAAU,GAAI,IAAI;AAAA,YAC5F,OAAO,OAAO;AAAA,UAChB,CAAC;AAAA,QACH,SAAS,KAAP;AACA,sBAAYA,IAAI,KAAa,WAAW,OAAO,GAAG,CAAC;AACnD;AAAA,QACF;AAEA,YAAI,eAAe,MAAM,UAAU,SAAS;AAAA,UAC1C,GAAG;AAAA,UACH,QAAQ,MAAM,WAAWA,EAAC;AAAA,UAC1B,SAAS,YAAU;AACjB,gBAAI,OAAO,WAAW,iBAAiB,KAAK,OAAO,QAAQ;AACzD,oBACG,KAAK,OAAO,MAAM,EAClB,KAAK,MAAM;AACV,sBAAM,UAAU,SAAS;AAAA,kBACvB,GAAG;AAAA,kBACH,QAAQ,MAAM,WAAWA,EAAC;AAAA,kBAC1B,SAAS,CAAAC,YAAU;AACjB,gCAAYD,IAAGC,OAAM;AAAA,kBACvB;AAAA,kBACA,kBAAkB;AAAA,kBAClB,aAAa,OAAO;AAAA,kBACpB,OAAO,OAAO;AAAA,gBAChB,CAAC;AAAA,cACH,CAAC,EACA,MAAM,SAAO;AACZ,4BAAYD,IAAG,qDAAqD,KAAK;AAAA,cAC3E,CAAC;AAAA,YACL,OAAO;AACL,0BAAYA,IAAG,MAAM;AAAA,YACvB;AAAA,UACF;AAAA,UACA,kBAAkB;AAAA,UAClB,aAAa,OAAO;AAAA,UACpB,OAAO,OAAO;AAAA,QAChB,CAAC;AAED,aAAK,KAAK,YAAY;AAAA,MACxB,CAAC;AAAA,IACH;AAEA,WAAO;AAAA,MACL,MAAM,MAAM,QAAiB;AAC3B,cAAM;AACN,aAAK,QAAQ,SAAO;AAClB,cAAI,MAAM,MAAM;AAAA,QAClB,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AAAA,EAEA,cACE,QACA,QACA,QACW;AACX,UAAM,YAAY,KAAK,UAAU,QAAQ,QAAQ;AAAA,MAC/C,GAAG;AAAA,MACH,SAAS;AACP,kBAAU,MAAM,8BAA8B;AAAA,MAChD;AAAA,IACF,CAAC;AACD,WAAO;AAAA,EACT;AAAA,EAEA,kBACE,QACA,QACA,QACW;AACX,UAAM,YAAY,KAAK,cAAc,QAAQ,QAAQ;AAAA,MACnD,GAAG;AAAA,MACH,SAAS;AACP,kBAAU,MAAM,8BAA8B;AAAA,MAChD;AAAA,IACF,CAAC;AACD,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,UACJ,QACA,QACA,QACkB;AAClB,WAAO,IAAI,QAAQ,OAAM,YAAW;AAClC,YAAM,SAAkB,CAAC;AACzB,WAAK,cAAc,QAAQ,QAAQ;AAAA,QACjC,GAAG;AAAA,QACH,QAAQ,OAAc;AACpB,iBAAO,KAAK,KAAK;AAAA,QACnB;AAAA,QACA,QAAQ,GAAa;AACnB,kBAAQ,MAAM;AAAA,QAChB;AAAA,MACF,CAAC;AAAA,IACH,CAAC;AAAA,EACH;AAAA,EAEA,MAAM,IACJ,QACA,QACA,QACuB;AACvB,WAAO,QAAQ;AACf,UAAM,SAAS,MAAM,KAAK,UAAU,QAAQ,QAAQ,MAAM;AAC1D,WAAO,KAAK,CAAC,GAAG,MAAM,EAAE,aAAa,EAAE,UAAU;AACjD,WAAO,OAAO,MAAM;AAAA,EACtB;AAAA,EAEA,QACE,QACA,OACA,SACmB;AACnB,WAAO,OAAO,IAAI,YAAY,EAAE,IAAI,OAAO,KAAKA,IAAG,QAAQ;AACzD,UAAI,IAAI,QAAQ,GAAG,MAAMA,IAAG;AAE1B,eAAO,QAAQ,OAAO,eAAe;AAAA,MACvC;AAEA,UAAI,IAAI,MAAM,KAAK,YAAY,GAAG;AAClC,aAAO,EACJ,QAAQ,KAAK,EACb,MAAM,OAAM,QAAO;AAClB,YAAI,eAAe,SAAS,IAAI,QAAQ,WAAW,iBAAiB,KAAK,SAAS,QAAQ;AACxF,gBAAM,EAAE,KAAK,QAAQ,MAAM;AAC3B,iBAAO,EAAE,QAAQ,KAAK;AAAA,QACxB;AACA,cAAM;AAAA,MACR,CAAC,EACA,KAAK,YAAU;AACd,YAAI,KAAK,aAAa;AACpB,cAAI,MAAM,KAAK,OAAO,IAAI,MAAM,EAAE;AAClC,cAAI,CAAC,KAAK;AACR,kBAAM,oBAAI,IAAI;AACd,iBAAK,OAAO,IAAI,MAAM,IAAI,GAAG;AAAA,UAC/B;AACA,cAAI,IAAI,CAAC;AAAA,QACX;AACA,eAAO;AAAA,MACT,CAAC;AAAA,IACL,CAAC;AAAA,EACH;AAAA,EAEA,uBAA6C;AAC3C,UAAM,MAAM,oBAAI,IAAqB;AACrC,SAAK,OAAO,QAAQ,CAAC,OAAO,QAAQ,IAAI,IAAI,KAAK,MAAM,SAAS,CAAC;AAEjE,WAAO;AAAA,EACT;AAAA,EAEA,UAAgB;AACd,SAAK,OAAO,QAAQ,UAAQ,KAAK,MAAM,CAAC;AACxC,SAAK,SAAS,oBAAI,IAAI;AAAA,EACxB;AACF;;;AClVA,IAAI;AAEJ,IAAI;AACF,eAAa;AACf,QAAE;AAAO;AAMF,IAAM,aAAN,cAAyB,mBAAmB;AAAA,EACjD,YAAY,SAAkF;AAC5F,UAAM,EAAE,aAAa,yBAAyB,YAAY,GAAG,QAAQ,CAAC;AAAA,EACxE;AACF;;;AbVA,IAAIE;AAEJ,IAAI;AACF,EAAAA,UAAS;AACX,QAAE;AAAO;AAEF,SAAS,uBAAuB,qBAA0B;AAC/D,EAAAA,UAAS;AACX;AAEO,IAAM,eAAe;AAC5B,IAAM,cAAc;AAQb,SAAS,YAAY,eAAsC;AAChE,MAAI,YAAY,IAAI,IAAI,YAAY,cAAc,QAAQ;AAC1D,gBAAc,OAAO,QAAQ,WAAS;AACpC,cAAU,aAAa,OAAO,SAAS,KAAK;AAAA,EAC9C,CAAC;AACD,MAAI,cAAc,QAAQ;AACxB,cAAU,aAAa,IAAI,UAAU,cAAc,MAAM;AAAA,EAC3D;AACA,SAAO,UAAU,SAAS;AAC5B;AAIA,eAAsB,iBAAiB,OAA8C;AACnF,MAAI,QAAQ,MAAM,MAAM,YAAY;AACpC,MAAI,OAAO;AACT,QAAI;AACF,YAAM,SAAS,MAAM;AACrB,YAAM,KAAK,IAAI,gBAAgB,MAAM,EAAE;AACvC,aAAO;AAAA,QACL;AAAA,QACA,QAAQ,GAAG,OAAO,OAAO;AAAA,QACzB,QAAQ,GAAG,IAAI,QAAQ;AAAA,MACzB;AAAA,IACF,SAAS,MAAP;AAAA,IAEF;AAAA,EACF;AAEA,SAAO,mBAAmB,KAAK;AACjC;AAEA,eAAsB,mBAAmB,OAA8C;AACrF,QAAM,QAAQ,MAAM,MAAM,WAAW;AACrC,MAAI,CAAC;AAAO,WAAO;AAEnB,QAAM,CAAC,GAAG,OAAO,KAAK,MAAM,IAAI;AAEhC,MAAI;AACF,UAAM,MAAM,WAAW,sCAAsC;AAC7D,UAAM,MAAM,OAAO,MAAMA,QAAO,KAAK,EAAE,UAAU,QAAQ,CAAC,GAAG,KAAK;AAElE,QAAI,SAAS,IAAI,MAAM;AACvB,QAAI,SAAS,IAAI,MAAM,WAAW,CAAC;AAEnC,WAAO,EAAE,QAAQ,QAAQ,QAAQ,KAAK;AAAA,EACxC,SAAS,MAAP;AACA,WAAO;AAAA,EACT;AACF;AAYO,SAAS,sBAAsB,QAAoC;AACxE,QAAM,cAAc,IAAI,gBAAgB;AAExC,SAAO,OAAO,QAAQ,WAAS;AAC7B,gBAAY,OAAO,SAAS,KAAK;AAAA,EACnC,CAAC;AAED,cAAY,OAAO,UAAU,OAAO,MAAM;AAE1C,MAAI,OAAO,SAAS,OAAO,MAAM,SAAS,GAAG;AAC3C,gBAAY,OAAO,SAAS,OAAO,MAAM,KAAK,GAAG,CAAC;AAAA,EACpD;AACA,MAAI,OAAO,MAAM;AACf,gBAAY,OAAO,QAAQ,OAAO,IAAI;AAAA,EACxC;AACA,MAAI,OAAO,KAAK;AACd,gBAAY,OAAO,OAAO,OAAO,GAAG;AAAA,EACtC;AACA,MAAI,OAAO,OAAO;AAChB,gBAAY,OAAO,SAAS,OAAO,KAAK;AAAA,EAC1C;AAEA,SAAO,kBAAkB,OAAO,gBAAgB,YAAY,SAAS;AACvE;AAOO,IAAM,eAAN,MAAqC;AAAA,EAClC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAMA;AAAA,EACA;AAAA,EAEA;AAAA,EACD;AAAA,EAEC;AAAA,EAQA,YAAY,iBAA6B,QAA4B;AAC3E,SAAK,SAAS;AACd,SAAK,OAAO,OAAO,QAAQ,IAAI,WAAW;AAC1C,SAAK,YAAY;AACjB,SAAK,SAAS;AACd,SAAK,WAAW,KAAK,OAAO,EAAE,SAAS,EAAE,EAAE,UAAU,CAAC;AACtD,SAAK,SAAS;AACd,SAAK,YAAY,CAAC;AAClB,SAAK,iBAAiB,CAAC;AAAA,EACzB;AAAA,EAMA,OAAc,WACZ,iBACA,IACA,SAA6B,CAAC,GAChB;AACd,QAAI,GAAG,OAAO,WAAW,GAAG;AAC1B,YAAM,IAAI,MAAM,qCAAqC;AAAA,IACvD;AAEA,UAAM,SAAS,IAAI,aAAa,iBAAiB,MAAM;AAEvD,WAAO,kBAAkB,mBAAmB,iBAAiB,GAAG,MAAM;AACtE,WAAO,KAAK;AAEZ,WAAO,kBAAkB;AACzB,WAAO;AAAA,EACT;AAAA,EAMA,aAAoB,QAClB,iBACA,eACA,eAAmC,CAAC,GACpC,iBAAuC,KAChB;AACvB,UAAM,SAAS,IAAI,aAAa,iBAAiB,YAAY;AAC7D,UAAM,MAAM,IAAI,IAAI,aAAa;AACjC,UAAM,eAAe,aAAa,eAAe;AAEjD,WAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,UAAI,UAAU;AACd,YAAM,MAAM,OAAO,KAAK;AAAA,QACtB,IAAI,aAAa,OAAO,OAAO;AAAA,QAC/B;AAAA,UACE,OAAO,CAAC,YAAY;AAAA,UACpB,MAAM,CAAC,YAAY;AAAA,UACnB,OAAO;AAAA,QACT;AAAA,QACA;AAAA,UACE,SAAS,OAAO,UAAsB;AACpC,gBAAI;AACF,oBAAM,cAAc,mBAAmB,iBAAiB,MAAM,MAAM;AACpE,oBAAM,mBAAmB,QAAQ,MAAM,SAAS,WAAW;AAE3D,oBAAM,WAAW,KAAK,MAAM,gBAAgB;AAE5C,kBAAI,SAAS,WAAW,IAAI,aAAa,IAAI,QAAQ,GAAG;AACtD,oBAAI,MAAM;AAEV,uBAAO,KAAK;AAAA,kBACV,QAAQ,MAAM;AAAA,kBACd,QAAQ,IAAI,aAAa,OAAO,OAAO;AAAA,kBACvC,QAAQ,IAAI,aAAa,IAAI,QAAQ;AAAA,gBACvC;AACA,uBAAO,kBAAkB,mBAAmB,iBAAiB,MAAM,MAAM;AACzE,uBAAO,kBAAkB;AAEzB,0BAAU;AACV,sBAAM,QAAQ,KAAK,CAAC,IAAI,QAAQ,CAAAC,aAAW,WAAWA,UAAS,GAAI,CAAC,GAAG,OAAO,aAAa,CAAC,CAAC;AAC7F,wBAAQ,MAAM;AAAA,cAChB;AAAA,YACF,SAAS,GAAP;AACA,sBAAQ,KAAK,gDAAgD,CAAC;AAAA,YAChE;AAAA,UACF;AAAA,UACA,SAAS,MAAM;AACb,gBAAI,CAAC;AAAS,qBAAO,IAAI,MAAM,wDAAwD,CAAC;AAAA,UAC1F;AAAA,UACA,SAAS,OAAO,mBAAmB,WAAW,iBAAiB;AAAA,UAC/D,OAAO,OAAO,mBAAmB,WAAW,iBAAiB;AAAA,QAC/D;AAAA,MACF;AAAA,IACF,CAAC;AAAA,EACH;AAAA,EAEQ,oBAAoB;AAC1B,UAAM,YAAY,KAAK;AACvB,UAAM,iBAAiB,KAAK;AAC5B,UAAM,UAAU,KAAK;AAErB,SAAK,YAAY,KAAK,KAAK;AAAA,MACzB,KAAK,GAAG;AAAA,MACR;AAAA,QACE,OAAO,CAAC,YAAY;AAAA,QACpB,SAAS,CAAC,KAAK,GAAG,MAAM;AAAA,QACxB,MAAM,CAAC,aAAa,KAAK,SAAS,CAAC;AAAA,QACnC,OAAO;AAAA,MACT;AAAA,MACA;AAAA,QACE,SAAS,OAAO,UAAsB;AACpC,gBAAM,IAAI,KAAK,MAAM,QAAQ,MAAM,SAAS,OAAO,CAAC;AACpD,gBAAM,EAAE,IAAI,QAAQ,MAAM,IAAI;AAE9B,cAAI,WAAW,cAAc,eAAe,KAAK;AAC/C,mBAAO,eAAe;AAEtB,gBAAI,KAAK,OAAO,QAAQ;AACtB,mBAAK,OAAO,OAAO,KAAK;AAAA,YAC1B,OAAO;AACL,sBAAQ;AAAA,gBACN,oCAAoC,KAAK,GAAG,uCAAuC;AAAA,cACrF;AAAA,YACF;AACA;AAAA,UACF;AAEA,cAAI,UAAU,UAAU;AACxB,cAAI,SAAS;AACX,gBAAI;AAAO,sBAAQ,OAAO,KAAK;AAAA,qBACtB;AAAQ,sBAAQ,QAAQ,MAAM;AACvC,mBAAO,UAAU;AAAA,UACnB;AAAA,QACF;AAAA,QACA,SAAS,MAAM;AACb,eAAK,YAAY;AAAA,QACnB;AAAA,MACF;AAAA,IACF;AACA,SAAK,SAAS;AAAA,EAChB;AAAA,EAEA,MAAM,eAAiC;AACrC,QAAI;AACF,YAAM,aAAa,MAAM,KAAK,YAAY,iBAAiB,CAAC,CAAC;AAC7D,UAAI,SAAS,KAAK,MAAM,UAAU;AAClC,UAAI,CAAC;AAAQ,eAAO;AACpB,UAAI,KAAK,UAAU,OAAO,KAAK,CAAC,MAAM,KAAK,UAAU,KAAK,GAAG,MAAM;AAAG,eAAO;AAE7E,WAAK,GAAG,SAAS;AACjB,UAAI,iBAAiB,KAAK;AAC1B,iBAAW,MAAM;AACf,uBAAe,MAAM;AAAA,MACvB,GAAG,GAAI;AAEP,WAAK,YAAY;AACjB,WAAK,kBAAkB;AACvB,aAAO;AAAA,IACT,QAAE;AACA,aAAO;AAAA,IACT;AAAA,EACF;AAAA,EAGA,MAAM,QAAQ;AACZ,SAAK,SAAS;AACd,SAAK,UAAW,MAAM;AAAA,EACxB;AAAA,EAEA,MAAM,YAAY,QAAgB,QAAmC;AACnE,WAAO,IAAI,QAAQ,OAAO,SAAS,WAAW;AAC5C,UAAI;AACF,YAAI,CAAC,KAAK;AAAQ,gBAAM,IAAI,MAAM,mDAAmD;AACrF,YAAI,CAAC,KAAK;AAAW,eAAK,kBAAkB;AAE5C,aAAK;AACL,cAAM,KAAK,GAAG,KAAK,YAAY,KAAK;AAEpC,cAAM,mBAAmB,QAAQ,KAAK,UAAU,EAAE,IAAI,QAAQ,OAAO,CAAC,GAAG,KAAK,eAAe;AAG7F,cAAM,gBAA+B;AAAA,UACnC;AAAA,YACE,MAAM;AAAA,YACN,MAAM,CAAC,CAAC,KAAK,KAAK,GAAG,MAAM,CAAC;AAAA,YAC5B,SAAS;AAAA,YACT,YAAY,KAAK,MAAM,KAAK,IAAI,IAAI,GAAI;AAAA,UAC1C;AAAA,UACA,KAAK;AAAA,QACP;AAGA,aAAK,UAAU,MAAM,EAAE,SAAS,OAAO;AACvC,aAAK,eAAe,MAAM;AAG1B,cAAM,QAAQ,IAAI,KAAK,KAAK,QAAQ,KAAK,GAAG,QAAQ,aAAa,CAAC;AAAA,MACpE,SAAS,KAAP;AACA,eAAO,GAAG;AAAA,MACZ;AAAA,IACF,CAAC;AAAA,EACH;AAAA,EAMA,MAAM,OAAsB;AAC1B,QAAI,OAAO,MAAM,KAAK,YAAY,QAAQ,CAAC,CAAC;AAC5C,QAAI,SAAS;AAAQ,YAAM,IAAI,MAAM,uBAAuB,MAAM;AAAA,EACpE;AAAA,EAKA,MAAM,UAAyB;AAC7B,UAAM,KAAK,YAAY,WAAW,CAAC,KAAK,GAAG,QAAQ,KAAK,GAAG,UAAU,EAAE,CAAC;AAAA,EAC1E;AAAA,EAQA,MAAM,eAAgC;AACpC,QAAI,CAAC,KAAK,cAAc;AACtB,WAAK,eAAe,MAAM,KAAK,YAAY,kBAAkB,CAAC,CAAC;AAAA,IACjE;AACA,WAAO,KAAK;AAAA,EACd;AAAA,EAOA,MAAM,UAAU,OAA8C;AAC5D,QAAI,OAAO,MAAM,KAAK,YAAY,cAAc,CAAC,KAAK,UAAU,KAAK,CAAC,CAAC;AACvE,QAAI,SAAqB,KAAK,MAAM,IAAI;AACxC,QAAI,YAAY,MAAM,GAAG;AACvB,aAAO;AAAA,IACT,OAAO;AACL,YAAM,IAAI,MAAM,oDAAoD,KAAK,UAAU,MAAM,GAAG;AAAA,IAC9F;AAAA,EACF;AAAA,EAEA,MAAM,aAAa,kBAA0B,WAAoC;AAC/E,WAAO,MAAM,KAAK,YAAY,iBAAiB,CAAC,kBAAkB,SAAS,CAAC;AAAA,EAC9E;AAAA,EAEA,MAAM,aAAa,kBAA0B,YAAqC;AAChF,WAAO,MAAM,KAAK,YAAY,iBAAiB,CAAC,kBAAkB,UAAU,CAAC;AAAA,EAC/E;AAAA,EAEA,MAAM,aAAa,kBAA0B,WAAoC;AAC/E,WAAO,MAAM,KAAK,YAAY,iBAAiB,CAAC,kBAAkB,SAAS,CAAC;AAAA,EAC9E;AAAA,EAEA,MAAM,aAAa,kBAA0B,YAAqC;AAChF,WAAO,MAAM,KAAK,YAAY,iBAAiB,CAAC,kBAAkB,UAAU,CAAC;AAAA,EAC/E;AACF;AAaA,eAAsB,cACpB,QACA,QACA,UACA,QACA,OACA,iBAA6B,kBAAkB,GACxB;AACvB,MAAI,SAAS,CAAC,YAAY,KAAK,KAAK;AAAG,UAAM,IAAI,MAAM,eAAe;AAEtE,MAAI,MAAM,aAAa,WAAW,gBAAgB,OAAO,eAAe,MAAM;AAE9E,MAAI,SAAS,MAAM,IAAI,YAAY,kBAAkB,CAAC,UAAU,QAAQ,SAAS,EAAE,CAAC;AAIpF,MAAI,GAAG,SAAS;AAChB,QAAM,IAAI,QAAQ;AAElB,SAAO;AACT;AAMA,eAAsB,qBAAqB,MAA0B,QAA4C;AAC/G,QAAM,SAAS,MAAM,KAAK,UAAU,QAAQ;AAAA,IAC1C,OAAO,CAAC,kBAAkB;AAAA,IAC1B,MAAM,CAAC,aAAa,SAAS,CAAC;AAAA,EAChC,CAAC;AAED,SAAO,KAAK,CAAC,GAAG,MAAM,EAAE,aAAa,EAAE,UAAU;AAIjD,QAAM,mBAAmB,MAAM,QAAQ;AAAA,IACrC,OAAO,IAAI,OAAO,OAAOC,OAAM;AAC7B,UAAI;AACF,cAAM,UAAU,KAAK,MAAM,MAAM,OAAO;AAGxC,YAAI;AACF,cAAI,OAAO,UAAU,QAAM,KAAK,MAAM,GAAG,OAAO,EAAE,UAAU,QAAQ,KAAK,MAAMA;AAAG,mBAAO;AAAA,QAC3F,SAAS,KAAP;AAAA,QAEF;AAEA,cAAM,KAAK,MAAM,mBAAmB,QAAQ,KAAK;AACjD,YAAI,MAAM,GAAG,WAAW,MAAM,UAAU,GAAG,OAAO,QAAQ;AACxD,iBAAO;AAAA,YACL,eAAe;AAAA,YACf,OAAO,QAAQ;AAAA,YACf,QAAQ,QAAQ,MAAM,MAAM,GAAG,EAAE;AAAA,YACjC,MAAM,QAAQ,QAAQ,QAAQ;AAAA,YAC9B,SAAS,QAAQ;AAAA,YACjB,OAAO,QAAQ;AAAA,YACf,SAAS,QAAQ;AAAA,YACjB,OAAO;AAAA,UACT;AAAA,QACF;AAAA,MACF,SAAS,KAAP;AACA,eAAO;AAAA,MACT;AAAA,IACF,CAAC;AAAA,EACH;AAEA,SAAO,iBAAiB,OAAO,OAAK,MAAM,MAAS;AACrD;",
+  "names": ["import_utils", "i", "target", "import_utils", "import_secp256k1", "import_sha2", "hkdf_extract", "hkdf_expand", "i", "i", "reason", "_fetch", "resolve", "i"]
 }
Index: package/lib/esm/nip46.js.map
===================================================================
--- package/lib/esm/nip46.js.map
+++ package/lib/esm/nip46.js.map
@@ -1,7 +1,7 @@
 {
   "version": 3,
   "sources": ["../../pure.ts", "../../core.ts", "../../utils.ts", "../../nip44.ts", "../../nip05.ts", "../../kinds.ts", "../../filter.ts", "../../fakejson.ts", "../../nip42.ts", "../../helpers.ts", "../../abstract-relay.ts", "../../abstract-pool.ts", "../../pool.ts", "../../nip46.ts"],
-  "sourcesContent": ["import { schnorr } from '@noble/curves/secp256k1'\nimport { bytesToHex } from '@noble/hashes/utils'\nimport { Nostr, Event, EventTemplate, UnsignedEvent, VerifiedEvent, verifiedSymbol, validateEvent } from './core.ts'\nimport { sha256 } from '@noble/hashes/sha256'\n\nimport { utf8Encoder } from './utils.ts'\n\nclass JS implements Nostr {\n  generateSecretKey(): Uint8Array {\n    return schnorr.utils.randomPrivateKey()\n  }\n  getPublicKey(secretKey: Uint8Array): string {\n    return bytesToHex(schnorr.getPublicKey(secretKey))\n  }\n  finalizeEvent(t: EventTemplate, secretKey: Uint8Array): VerifiedEvent {\n    const event = t as VerifiedEvent\n    event.pubkey = bytesToHex(schnorr.getPublicKey(secretKey))\n    event.id = getEventHash(event)\n    event.sig = bytesToHex(schnorr.sign(getEventHash(event), secretKey))\n    event[verifiedSymbol] = true\n    return event\n  }\n  verifyEvent(event: Event): event is VerifiedEvent {\n    if (typeof event[verifiedSymbol] === 'boolean') return event[verifiedSymbol]\n\n    const hash = getEventHash(event)\n    if (hash !== event.id) {\n      event[verifiedSymbol] = false\n      return false\n    }\n\n    try {\n      const valid = schnorr.verify(event.sig, hash, event.pubkey)\n      event[verifiedSymbol] = valid\n      return valid\n    } catch (err) {\n      event[verifiedSymbol] = false\n      return false\n    }\n  }\n}\n\nexport function serializeEvent(evt: UnsignedEvent): string {\n  if (!validateEvent(evt)) throw new Error(\"can't serialize event with wrong or missing properties\")\n  return JSON.stringify([0, evt.pubkey, evt.created_at, evt.kind, evt.tags, evt.content])\n}\n\nexport function getEventHash(event: UnsignedEvent): string {\n  let eventHash = sha256(utf8Encoder.encode(serializeEvent(event)))\n  return bytesToHex(eventHash)\n}\n\nconst i: JS = new JS()\n\nexport const generateSecretKey = i.generateSecretKey\nexport const getPublicKey = i.getPublicKey\nexport const finalizeEvent = i.finalizeEvent\nexport const verifyEvent = i.verifyEvent\nexport * from './core.ts'\n", "export interface Nostr {\n  generateSecretKey(): Uint8Array\n  getPublicKey(secretKey: Uint8Array): string\n  finalizeEvent(event: EventTemplate, secretKey: Uint8Array): VerifiedEvent\n  verifyEvent(event: Event): event is VerifiedEvent\n}\n\n/** Designates a verified event signature. */\nexport const verifiedSymbol = Symbol('verified')\n\nexport type NostrEvent = {\n  kind: number\n  tags: string[][]\n  content: string\n  created_at: number\n  pubkey: string\n  id: string\n  sig: string\n  [verifiedSymbol]?: boolean\n}\n\nexport type Event = NostrEvent\nexport type EventTemplate = Pick<Event, 'kind' | 'tags' | 'content' | 'created_at'>\nexport type UnsignedEvent = Pick<Event, 'kind' | 'tags' | 'content' | 'created_at' | 'pubkey'>\n\n/** An event whose signature has been verified. */\nexport interface VerifiedEvent extends Event {\n  [verifiedSymbol]: true\n}\n\nconst isRecord = (obj: unknown): obj is Record<string, unknown> => obj instanceof Object\n\nexport function validateEvent<T>(event: T): event is T & UnsignedEvent {\n  if (!isRecord(event)) return false\n  if (typeof event.kind !== 'number') return false\n  if (typeof event.content !== 'string') return false\n  if (typeof event.created_at !== 'number') return false\n  if (typeof event.pubkey !== 'string') return false\n  if (!event.pubkey.match(/^[a-f0-9]{64}$/)) return false\n\n  if (!Array.isArray(event.tags)) return false\n  for (let i = 0; i < event.tags.length; i++) {\n    let tag = event.tags[i]\n    if (!Array.isArray(tag)) return false\n    for (let j = 0; j < tag.length; j++) {\n      if (typeof tag[j] !== 'string') return false\n    }\n  }\n\n  return true\n}\n\n/**\n * Sort events in reverse-chronological order by the `created_at` timestamp,\n * and then by the event `id` (lexicographically) in case of ties.\n * This mutates the array.\n */\nexport function sortEvents(events: Event[]): Event[] {\n  return events.sort((a: NostrEvent, b: NostrEvent): number => {\n    if (a.created_at !== b.created_at) {\n      return b.created_at - a.created_at\n    }\n    return a.id.localeCompare(b.id)\n  })\n}\n", "import type { Event } from './core.ts'\n\nexport const utf8Decoder: TextDecoder = new TextDecoder('utf-8')\nexport const utf8Encoder: TextEncoder = new TextEncoder()\n\nexport { bytesToHex, hexToBytes } from '@noble/hashes/utils'\n\nexport function normalizeURL(url: string): string {\n  try {\n    if (url.indexOf('://') === -1) url = 'wss://' + url\n    let p = new URL(url)\n    if (p.protocol === 'http:') p.protocol = 'ws:'\n    else if (p.protocol === 'https:') p.protocol = 'wss:'\n    p.pathname = p.pathname.replace(/\\/+/g, '/')\n    if (p.pathname.endsWith('/')) p.pathname = p.pathname.slice(0, -1)\n    if ((p.port === '80' && p.protocol === 'ws:') || (p.port === '443' && p.protocol === 'wss:')) p.port = ''\n    p.searchParams.sort()\n    p.hash = ''\n    return p.toString()\n  } catch (e) {\n    throw new Error(`Invalid URL: ${url}`)\n  }\n}\n\nexport function insertEventIntoDescendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return b.created_at - event.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function insertEventIntoAscendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return event.created_at - b.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function binarySearch<T>(arr: T[], compare: (b: T) => number): [number, boolean] {\n  let start = 0\n  let end = arr.length - 1\n\n  while (start <= end) {\n    const mid = Math.floor((start + end) / 2)\n    const cmp = compare(arr[mid])\n\n    if (cmp === 0) {\n      return [mid, true]\n    }\n\n    if (cmp < 0) {\n      end = mid - 1\n    } else {\n      start = mid + 1\n    }\n  }\n\n  return [start, false]\n}\n\nexport class QueueNode<V> {\n  public value: V\n  public next: QueueNode<V> | null = null\n  public prev: QueueNode<V> | null = null\n\n  constructor(message: V) {\n    this.value = message\n  }\n}\n\nexport class Queue<V> {\n  public first: QueueNode<V> | null\n  public last: QueueNode<V> | null\n\n  constructor() {\n    this.first = null\n    this.last = null\n  }\n\n  enqueue(value: V): boolean {\n    const newNode = new QueueNode(value)\n    if (!this.last) {\n      // list is empty\n      this.first = newNode\n      this.last = newNode\n    } else if (this.last === this.first) {\n      // list has a single element\n      this.last = newNode\n      this.last.prev = this.first\n      this.first.next = newNode\n    } else {\n      // list has elements, add as last\n      newNode.prev = this.last\n      this.last.next = newNode\n      this.last = newNode\n    }\n    return true\n  }\n\n  dequeue(): V | null {\n    if (!this.first) return null\n\n    if (this.first === this.last) {\n      const target = this.first\n      this.first = null\n      this.last = null\n      return target.value\n    }\n\n    const target = this.first\n    this.first = target.next\n    if (this.first) {\n      this.first.prev = null // fix: clean up prev pointer\n    }\n\n    return target.value\n  }\n}\n", "import { chacha20 } from '@noble/ciphers/chacha'\nimport { equalBytes } from '@noble/ciphers/utils'\nimport { secp256k1 } from '@noble/curves/secp256k1'\nimport { extract as hkdf_extract, expand as hkdf_expand } from '@noble/hashes/hkdf'\nimport { hmac } from '@noble/hashes/hmac'\nimport { sha256 } from '@noble/hashes/sha256'\nimport { concatBytes, randomBytes } from '@noble/hashes/utils'\nimport { base64 } from '@scure/base'\n\nimport { utf8Decoder, utf8Encoder } from './utils.ts'\n\nconst minPlaintextSize = 0x0001 // 1b msg => padded to 32b\nconst maxPlaintextSize = 0xffff // 65535 (64kb-1) => padded to 64kb\n\nexport function getConversationKey(privkeyA: Uint8Array, pubkeyB: string): Uint8Array {\n  const sharedX = secp256k1.getSharedSecret(privkeyA, '02' + pubkeyB).subarray(1, 33)\n  return hkdf_extract(sha256, sharedX, 'nip44-v2')\n}\n\nfunction getMessageKeys(\n  conversationKey: Uint8Array,\n  nonce: Uint8Array,\n): { chacha_key: Uint8Array; chacha_nonce: Uint8Array; hmac_key: Uint8Array } {\n  const keys = hkdf_expand(sha256, conversationKey, nonce, 76)\n  return {\n    chacha_key: keys.subarray(0, 32),\n    chacha_nonce: keys.subarray(32, 44),\n    hmac_key: keys.subarray(44, 76),\n  }\n}\n\nfunction calcPaddedLen(len: number): number {\n  if (!Number.isSafeInteger(len) || len < 1) throw new Error('expected positive integer')\n  if (len <= 32) return 32\n  const nextPower = 1 << (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 > maxPlaintextSize)\n    throw new Error('invalid plaintext size: must be between 1 and 65535 bytes')\n  const arr = new Uint8Array(2)\n  new DataView(arr.buffer).setUint16(0, num, false)\n  return arr\n}\n\nfunction pad(plaintext: string): Uint8Array {\n  const unpadded = utf8Encoder.encode(plaintext)\n  const unpaddedLen = unpadded.length\n  const prefix = 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 unpaddedLen = new DataView(padded.buffer).getUint16(0)\n  const unpadded = padded.subarray(2, 2 + unpaddedLen)\n  if (\n    unpaddedLen < minPlaintextSize ||\n    unpaddedLen > maxPlaintextSize ||\n    unpadded.length !== unpaddedLen ||\n    padded.length !== 2 + calcPaddedLen(unpaddedLen)\n  )\n    throw new Error('invalid padding')\n  return utf8Decoder.decode(unpadded)\n}\n\nfunction hmacAad(key: Uint8Array, message: Uint8Array, aad: Uint8Array): Uint8Array {\n  if (aad.length !== 32) throw new Error('AAD associated data must be 32 bytes')\n  const combined = concatBytes(aad, message)\n  return hmac(sha256, key, combined)\n}\n\n// metadata: always 65b (version: 1b, nonce: 32b, max: 32b)\n// plaintext: 1b to 0xffff\n// padded plaintext: 32b to 0xffff\n// ciphertext: 32b+2 to 0xffff+2\n// raw payload: 99 (65+32+2) to 65603 (65+0xffff+2)\n// compressed payload (base64): 132b to 87472b\nfunction decodePayload(payload: string): { nonce: Uint8Array; ciphertext: Uint8Array; mac: Uint8Array } {\n  if (typeof payload !== 'string') throw new Error('payload must be a valid string')\n  const plen = payload.length\n  if (plen < 132 || plen > 87472) throw new Error('invalid payload length: ' + plen)\n  if (payload[0] === '#') throw new Error('unknown encryption version')\n  let data: Uint8Array\n  try {\n    data = base64.decode(payload)\n  } catch (error) {\n    throw new Error('invalid base64: ' + (error as any).message)\n  }\n  const dlen = data.length\n  if (dlen < 99 || dlen > 65603) throw new Error('invalid data length: ' + dlen)\n  const vers = data[0]\n  if (vers !== 2) throw new Error('unknown encryption version ' + vers)\n  return {\n    nonce: data.subarray(1, 33),\n    ciphertext: data.subarray(33, -32),\n    mac: data.subarray(-32),\n  }\n}\n\nexport function encrypt(plaintext: string, conversationKey: Uint8Array, nonce: Uint8Array = randomBytes(32)): string {\n  const { chacha_key, chacha_nonce, hmac_key } = getMessageKeys(conversationKey, nonce)\n  const padded = pad(plaintext)\n  const ciphertext = chacha20(chacha_key, chacha_nonce, padded)\n  const mac = hmacAad(hmac_key, ciphertext, nonce)\n  return base64.encode(concatBytes(new Uint8Array([2]), nonce, ciphertext, mac))\n}\n\nexport function decrypt(payload: string, conversationKey: Uint8Array): string {\n  const { nonce, ciphertext, mac } = decodePayload(payload)\n  const { chacha_key, chacha_nonce, hmac_key } = getMessageKeys(conversationKey, nonce)\n  const calculatedMac = hmacAad(hmac_key, ciphertext, nonce)\n  if (!equalBytes(calculatedMac, mac)) throw new Error('invalid MAC')\n  const padded = chacha20(chacha_key, chacha_nonce, ciphertext)\n  return unpad(padded)\n}\n\nexport const v2 = {\n  utils: {\n    getConversationKey,\n    calcPaddedLen,\n  },\n  encrypt,\n  decrypt,\n}\n", "import { ProfilePointer } from './nip19.ts'\n\nexport type Nip05 = `${string}@${string}`\n\n/**\n * NIP-05 regex. The localpart is optional, and should be assumed to be `_` otherwise.\n *\n * - 0: full match\n * - 1: name (optional)\n * - 2: domain\n */\nexport const NIP05_REGEX = /^(?:([\\w.+-]+)@)?([\\w_-]+(\\.[\\w_-]+)+)$/\nexport const isNip05 = (value?: string | null): value is Nip05 => NIP05_REGEX.test(value || '')\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nlet _fetch: any\n\ntry {\n  _fetch = fetch\n} catch (_) {\n  null\n}\n\nexport function useFetchImplementation(fetchImplementation: unknown) {\n  _fetch = fetchImplementation\n}\n\nexport async function searchDomain(domain: string, query = ''): Promise<{ [name: string]: string }> {\n  try {\n    const url = `https://${domain}/.well-known/nostr.json?name=${query}`\n    const res = await _fetch(url, { redirect: 'manual' })\n    if (res.status !== 200) {\n      throw Error('Wrong response code')\n    }\n    const json = await res.json()\n    return json.names\n  } catch (_) {\n    return {}\n  }\n}\n\nexport async function queryProfile(fullname: string): Promise<ProfilePointer | null> {\n  const match = fullname.match(NIP05_REGEX)\n  if (!match) return null\n\n  const [, name = '_', domain] = match\n\n  try {\n    const url = `https://${domain}/.well-known/nostr.json?name=${name}`\n    const res = await _fetch(url, { redirect: 'manual' })\n    if (res.status !== 200) {\n      throw Error('Wrong response code')\n    }\n    const json = await res.json()\n\n    const pubkey = json.names[name]\n    return pubkey ? { pubkey, relays: json.relays?.[pubkey] } : null\n  } catch (_e) {\n    return null\n  }\n}\n\nexport async function isValid(pubkey: string, nip05: Nip05): Promise<boolean> {\n  const res = await queryProfile(nip05)\n  return res ? res.pubkey === pubkey : false\n}\n", "import { NostrEvent, validateEvent } from './pure.ts'\n\n/** Events are **regular**, which means they're all expected to be stored by relays. */\nexport function isRegularKind(kind: number): boolean {\n  return kind < 10000 && kind !== 0 && kind !== 3\n}\n\n/** Events are **replaceable**, which means that, for each combination of `pubkey` and `kind`, only the latest event is expected to (SHOULD) be stored by relays, older versions are expected to be discarded. */\nexport function isReplaceableKind(kind: number): boolean {\n  return kind === 0 || kind === 3 || (10000 <= kind && kind < 20000)\n}\n\n/** Events are **ephemeral**, which means they are not expected to be stored by relays. */\nexport function isEphemeralKind(kind: number): boolean {\n  return 20000 <= kind && kind < 30000\n}\n\n/** Events are **addressable**, which means that, for each combination of `pubkey`, `kind` and the `d` tag, only the latest event is expected to be stored by relays, older versions are expected to be discarded. */\nexport function isAddressableKind(kind: number): boolean {\n  return 30000 <= kind && kind < 40000\n}\n\n/** Classification of the event kind. */\nexport type KindClassification = 'regular' | 'replaceable' | 'ephemeral' | 'parameterized' | 'unknown'\n\n/** Determine the classification of this kind of event if known, or `unknown`. */\nexport function classifyKind(kind: number): KindClassification {\n  if (isRegularKind(kind)) return 'regular'\n  if (isReplaceableKind(kind)) return 'replaceable'\n  if (isEphemeralKind(kind)) return 'ephemeral'\n  if (isAddressableKind(kind)) return 'parameterized'\n  return 'unknown'\n}\n\nexport function isKind<T extends number>(event: unknown, kind: T | Array<T>): event is NostrEvent & { kind: T } {\n  const kindAsArray: number[] = kind instanceof Array ? kind : [kind]\n  return (validateEvent(event) && kindAsArray.includes(event.kind)) || false\n}\n\nexport const Metadata = 0\nexport type Metadata = typeof Metadata\nexport const ShortTextNote = 1\nexport type ShortTextNote = typeof ShortTextNote\nexport const RecommendRelay = 2\nexport type RecommendRelay = typeof RecommendRelay\nexport const Contacts = 3\nexport type Contacts = typeof Contacts\nexport const EncryptedDirectMessage = 4\nexport type EncryptedDirectMessage = typeof EncryptedDirectMessage\nexport const EventDeletion = 5\nexport type EventDeletion = typeof EventDeletion\nexport const Repost = 6\nexport type Repost = typeof Repost\nexport const Reaction = 7\nexport type Reaction = typeof Reaction\nexport const BadgeAward = 8\nexport type BadgeAward = typeof BadgeAward\nexport const ChatMessage = 9\nexport type ChatMessage = typeof ChatMessage\nexport const ForumThread = 11\nexport type ForumThread = typeof ForumThread\nexport const Seal = 13\nexport type Seal = typeof Seal\nexport const PrivateDirectMessage = 14\nexport type PrivateDirectMessage = typeof PrivateDirectMessage\nexport const FileMessage = 15\nexport type FileMessage = typeof FileMessage\nexport const GenericRepost = 16\nexport type GenericRepost = typeof GenericRepost\nexport const Photo = 20\nexport type Photo = typeof Photo\nexport const NormalVideo = 21\nexport type NormalVideo = typeof NormalVideo\nexport const ShortVideo = 22\nexport type ShortVideo = typeof ShortVideo\nexport const ChannelCreation = 40\nexport type ChannelCreation = typeof ChannelCreation\nexport const ChannelMetadata = 41\nexport type ChannelMetadata = typeof ChannelMetadata\nexport const ChannelMessage = 42\nexport type ChannelMessage = typeof ChannelMessage\nexport const ChannelHideMessage = 43\nexport type ChannelHideMessage = typeof ChannelHideMessage\nexport const ChannelMuteUser = 44\nexport type ChannelMuteUser = typeof ChannelMuteUser\nexport const OpenTimestamps = 1040\nexport type OpenTimestamps = typeof OpenTimestamps\nexport const GiftWrap = 1059\nexport type GiftWrap = typeof GiftWrap\nexport const Poll = 1068\nexport type Poll = typeof Poll\nexport const FileMetadata = 1063\nexport type FileMetadata = typeof FileMetadata\nexport const Comment = 1111\nexport type Comment = typeof Comment\nexport const LiveChatMessage = 1311\nexport type LiveChatMessage = typeof LiveChatMessage\nexport const Voice = 1222\nexport type Voice = typeof Voice\nexport const VoiceComment = 1244\nexport type VoiceComment = typeof VoiceComment\nexport const ProblemTracker = 1971\nexport type ProblemTracker = typeof ProblemTracker\nexport const Report = 1984\nexport type Report = typeof Report\nexport const Reporting = 1984\nexport type Reporting = typeof Reporting\nexport const Label = 1985\nexport type Label = typeof Label\nexport const CommunityPostApproval = 4550\nexport type CommunityPostApproval = typeof CommunityPostApproval\nexport const JobRequest = 5999\nexport type JobRequest = typeof JobRequest\nexport const JobResult = 6999\nexport type JobResult = typeof JobResult\nexport const JobFeedback = 7000\nexport type JobFeedback = typeof JobFeedback\nexport const ZapGoal = 9041\nexport type ZapGoal = typeof ZapGoal\nexport const ZapRequest = 9734\nexport type ZapRequest = typeof ZapRequest\nexport const Zap = 9735\nexport type Zap = typeof Zap\nexport const Highlights = 9802\nexport type Highlights = typeof Highlights\nexport const PollResponse = 1018\nexport type PollResponse = typeof PollResponse\nexport const Mutelist = 10000\nexport type Mutelist = typeof Mutelist\nexport const Pinlist = 10001\nexport type Pinlist = typeof Pinlist\nexport const RelayList = 10002\nexport type RelayList = typeof RelayList\nexport const BookmarkList = 10003\nexport type BookmarkList = typeof BookmarkList\nexport const CommunitiesList = 10004\nexport type CommunitiesList = typeof CommunitiesList\nexport const PublicChatsList = 10005\nexport type PublicChatsList = typeof PublicChatsList\nexport const BlockedRelaysList = 10006\nexport type BlockedRelaysList = typeof BlockedRelaysList\nexport const SearchRelaysList = 10007\nexport type SearchRelaysList = typeof SearchRelaysList\nexport const FavoriteRelays = 10012\nexport type FavoriteRelays = typeof FavoriteRelays\nexport const InterestsList = 10015\nexport type InterestsList = typeof InterestsList\nexport const UserEmojiList = 10030\nexport type UserEmojiList = typeof UserEmojiList\nexport const DirectMessageRelaysList = 10050\nexport type DirectMessageRelaysList = typeof DirectMessageRelaysList\nexport const FileServerPreference = 10096\nexport type FileServerPreference = typeof FileServerPreference\nexport const BlossomServerList = 10063\nexport type BlossomServerList = typeof BlossomServerList\nexport const NWCWalletInfo = 13194\nexport type NWCWalletInfo = typeof NWCWalletInfo\nexport const LightningPubRPC = 21000\nexport type LightningPubRPC = typeof LightningPubRPC\nexport const ClientAuth = 22242\nexport type ClientAuth = typeof ClientAuth\nexport const NWCWalletRequest = 23194\nexport type NWCWalletRequest = typeof NWCWalletRequest\nexport const NWCWalletResponse = 23195\nexport type NWCWalletResponse = typeof NWCWalletResponse\nexport const NostrConnect = 24133\nexport type NostrConnect = typeof NostrConnect\nexport const HTTPAuth = 27235\nexport type HTTPAuth = typeof HTTPAuth\nexport const Followsets = 30000\nexport type Followsets = typeof Followsets\nexport const Genericlists = 30001\nexport type Genericlists = typeof Genericlists\nexport const Relaysets = 30002\nexport type Relaysets = typeof Relaysets\nexport const Bookmarksets = 30003\nexport type Bookmarksets = typeof Bookmarksets\nexport const Curationsets = 30004\nexport type Curationsets = typeof Curationsets\nexport const ProfileBadges = 30008\nexport type ProfileBadges = typeof ProfileBadges\nexport const BadgeDefinition = 30009\nexport type BadgeDefinition = typeof BadgeDefinition\nexport const Interestsets = 30015\nexport type Interestsets = typeof Interestsets\nexport const CreateOrUpdateStall = 30017\nexport type CreateOrUpdateStall = typeof CreateOrUpdateStall\nexport const CreateOrUpdateProduct = 30018\nexport type CreateOrUpdateProduct = typeof CreateOrUpdateProduct\nexport const LongFormArticle = 30023\nexport type LongFormArticle = typeof LongFormArticle\nexport const DraftLong = 30024\nexport type DraftLong = typeof DraftLong\nexport const Emojisets = 30030\nexport type Emojisets = typeof Emojisets\nexport const Application = 30078\nexport type Application = typeof Application\nexport const LiveEvent = 30311\nexport type LiveEvent = typeof LiveEvent\nexport const UserStatuses = 30315\nexport type UserStatuses = typeof UserStatuses\nexport const ClassifiedListing = 30402\nexport type ClassifiedListing = typeof ClassifiedListing\nexport const DraftClassifiedListing = 30403\nexport type DraftClassifiedListing = typeof DraftClassifiedListing\nexport const Date = 31922\nexport type Date = typeof Date\nexport const Time = 31923\nexport type Time = typeof Time\nexport const Calendar = 31924\nexport type Calendar = typeof Calendar\nexport const CalendarEventRSVP = 31925\nexport type CalendarEventRSVP = typeof CalendarEventRSVP\nexport const RelayReview = 31987\nexport type RelayReview = typeof RelayReview\nexport const Handlerrecommendation = 31989\nexport type Handlerrecommendation = typeof Handlerrecommendation\nexport const Handlerinformation = 31990\nexport type Handlerinformation = typeof Handlerinformation\nexport const CommunityDefinition = 34550\nexport type CommunityDefinition = typeof CommunityDefinition\nexport const GroupMetadata = 39000\nexport type GroupMetadata = typeof GroupMetadata\n", "import { Event } from './core.ts'\nimport { isAddressableKind, isReplaceableKind } from './kinds.ts'\n\nexport type Filter = {\n  ids?: string[]\n  kinds?: number[]\n  authors?: string[]\n  since?: number\n  until?: number\n  limit?: number\n  search?: string\n  [key: `#${string}`]: string[] | undefined\n}\n\nexport function matchFilter(filter: Filter, event: Event): boolean {\n  if (filter.ids && filter.ids.indexOf(event.id) === -1) {\n    return false\n  }\n  if (filter.kinds && filter.kinds.indexOf(event.kind) === -1) {\n    return false\n  }\n  if (filter.authors && filter.authors.indexOf(event.pubkey) === -1) {\n    return false\n  }\n\n  for (let f in filter) {\n    if (f[0] === '#') {\n      let tagName = f.slice(1)\n      let values = filter[`#${tagName}`]\n      if (values && !event.tags.find(([t, v]) => t === f.slice(1) && values!.indexOf(v) !== -1)) return false\n    }\n  }\n\n  if (filter.since && event.created_at < filter.since) return false\n  if (filter.until && event.created_at > filter.until) return false\n\n  return true\n}\n\nexport function matchFilters(filters: Filter[], event: Event): boolean {\n  for (let i = 0; i < filters.length; i++) {\n    if (matchFilter(filters[i], event)) {\n      return true\n    }\n  }\n  return false\n}\n\nexport function mergeFilters(...filters: Filter[]): Filter {\n  let result: Filter = {}\n  for (let i = 0; i < filters.length; i++) {\n    let filter = filters[i]\n    Object.entries(filter).forEach(([property, values]) => {\n      if (property === 'kinds' || property === 'ids' || property === 'authors' || property[0] === '#') {\n        // @ts-ignore\n        result[property] = result[property] || []\n        // @ts-ignore\n        for (let v = 0; v < values.length; v++) {\n          // @ts-ignore\n          let value = values[v]\n          // @ts-ignore\n          if (!result[property].includes(value)) result[property].push(value)\n        }\n      }\n    })\n\n    if (filter.limit && (!result.limit || filter.limit > result.limit)) result.limit = filter.limit\n    if (filter.until && (!result.until || filter.until > result.until)) result.until = filter.until\n    if (filter.since && (!result.since || filter.since < result.since)) result.since = filter.since\n  }\n\n  return result\n}\n\n/**\n * Calculate the intrinsic limit of a filter.\n * This function returns a positive integer, or `Infinity` if there is no intrinsic limit.\n */\nexport function getFilterLimit(filter: Filter): number {\n  if (filter.ids && !filter.ids.length) return 0\n  if (filter.kinds && !filter.kinds.length) return 0\n  if (filter.authors && !filter.authors.length) return 0\n\n  for (const [key, value] of Object.entries(filter)) {\n    if (key[0] === '#' && Array.isArray(value) && !value.length) return 0\n  }\n\n  return Math.min(\n    // The `limit` property creates an artificial limit.\n    Math.max(0, filter.limit ?? Infinity),\n\n    // There can only be one event per `id`.\n    filter.ids?.length ?? Infinity,\n\n    // Replaceable events are limited by the number of authors and kinds.\n    filter.authors?.length && filter.kinds?.every(kind => isReplaceableKind(kind))\n      ? filter.authors.length * filter.kinds.length\n      : Infinity,\n\n    // Parameterized replaceable events are limited by the number of authors, kinds, and \"d\" tags.\n    filter.authors?.length && filter.kinds?.every(kind => isAddressableKind(kind)) && filter['#d']?.length\n      ? filter.authors.length * filter.kinds.length * filter['#d'].length\n      : Infinity,\n  )\n}\n", "export function getHex64(json: string, field: string): string {\n  let len = field.length + 3\n  let idx = json.indexOf(`\"${field}\":`) + len\n  let s = json.slice(idx).indexOf(`\"`) + idx + 1\n  return json.slice(s, s + 64)\n}\n\nexport function getInt(json: string, field: string): number {\n  let len = field.length\n  let idx = json.indexOf(`\"${field}\":`) + len + 3\n  let sliced = json.slice(idx)\n  let end = Math.min(sliced.indexOf(','), sliced.indexOf('}'))\n  return parseInt(sliced.slice(0, end), 10)\n}\n\nexport function getSubscriptionId(json: string): string | null {\n  let idx = json.slice(0, 22).indexOf(`\"EVENT\"`)\n  if (idx === -1) return null\n\n  let pstart = json.slice(idx + 7 + 1).indexOf(`\"`)\n  if (pstart === -1) return null\n  let start = idx + 7 + 1 + pstart\n\n  let pend = json.slice(start + 1, 80).indexOf(`\"`)\n  if (pend === -1) return null\n  let end = start + 1 + pend\n\n  return json.slice(start + 1, end)\n}\n\nexport function matchEventId(json: string, id: string): boolean {\n  return id === getHex64(json, 'id')\n}\n\nexport function matchEventPubkey(json: string, pubkey: string): boolean {\n  return pubkey === getHex64(json, 'pubkey')\n}\n\nexport function matchEventKind(json: string, kind: number): boolean {\n  return kind === getInt(json, 'kind')\n}\n", "import { EventTemplate } from './core.ts'\nimport { ClientAuth } from './kinds.ts'\n\n/**\n * creates an EventTemplate for an AUTH event to be signed.\n */\nexport function makeAuthEvent(relayURL: string, challenge: string): EventTemplate {\n  return {\n    kind: ClientAuth,\n    created_at: Math.floor(Date.now() / 1000),\n    tags: [\n      ['relay', relayURL],\n      ['challenge', challenge],\n    ],\n    content: '',\n  }\n}\n", "import { verifiedSymbol, type Event, type Nostr, VerifiedEvent } from './core.ts'\n\nexport async function yieldThread() {\n  return new Promise<void>((resolve, reject) => {\n    try {\n      // Check if MessageChannel is available\n      if (typeof MessageChannel !== 'undefined') {\n        const ch = new MessageChannel()\n        const handler = () => {\n          // @ts-ignore (typescript thinks this property should be called `removeListener`, but in fact it's `removeEventListener`)\n          ch.port1.removeEventListener('message', handler)\n          resolve()\n        }\n        // @ts-ignore (typescript thinks this property should be called `addListener`, but in fact it's `addEventListener`)\n        ch.port1.addEventListener('message', handler)\n        ch.port2.postMessage(0)\n        ch.port1.start()\n      } else {\n        if (typeof setImmediate !== 'undefined') {\n          setImmediate(resolve)\n        } else if (typeof setTimeout !== 'undefined') {\n          setTimeout(resolve, 0)\n        } else {\n          // Last resort - resolve immediately\n          resolve()\n        }\n      }\n    } catch (e) {\n      console.error('during yield: ', e)\n      reject(e)\n    }\n  })\n}\n\nexport const alwaysTrue: Nostr['verifyEvent'] = (t: Event): t is VerifiedEvent => {\n  t[verifiedSymbol] = true\n  return true\n}\n", "/* global WebSocket */\n\nimport type { Event, EventTemplate, VerifiedEvent, Nostr, NostrEvent } from './core.ts'\nimport { matchFilters, type Filter } from './filter.ts'\nimport { getHex64, getSubscriptionId } from './fakejson.ts'\nimport { Queue, normalizeURL } from './utils.ts'\nimport { makeAuthEvent } from './nip42.ts'\nimport { yieldThread } from './helpers.ts'\n\ntype RelayWebSocket = WebSocket & {\n  ping?(): void\n  on?(event: 'pong', listener: () => void): any\n}\n\nexport type AbstractRelayConstructorOptions = {\n  verifyEvent: Nostr['verifyEvent']\n  websocketImplementation?: typeof WebSocket\n  enablePing?: boolean\n  enableReconnect?: boolean\n}\n\nexport class SendingOnClosedConnection extends Error {\n  constructor(message: string, relay: string) {\n    super(`Tried to send message '${message} on a closed connection to ${relay}.`)\n    this.name = 'SendingOnClosedConnection'\n  }\n}\n\nexport class AbstractRelay {\n  public readonly url: string\n  private _connected: boolean = false\n\n  public onclose: (() => void) | null = null\n  public onnotice: (msg: string) => void = msg => console.debug(`NOTICE from ${this.url}: ${msg}`)\n  public onauth: undefined | ((evt: EventTemplate) => Promise<VerifiedEvent>)\n\n  public baseEoseTimeout: number = 4400\n  public publishTimeout: number = 4400\n  public pingFrequency: number = 29000\n  public pingTimeout: number = 20000\n  public resubscribeBackoff: number[] = [10000, 10000, 10000, 20000, 20000, 30000, 60000]\n  public openSubs: Map<string, Subscription> = new Map()\n  public enablePing: boolean | undefined\n  public enableReconnect: boolean\n  private reconnectTimeoutHandle: ReturnType<typeof setTimeout> | undefined\n  private pingIntervalHandle: ReturnType<typeof setInterval> | undefined\n  private reconnectAttempts: number = 0\n  private closedIntentionally: boolean = false\n\n  private connectionPromise: Promise<void> | undefined\n  private openCountRequests = new Map<string, CountResolver>()\n  private openEventPublishes = new Map<string, EventPublishResolver>()\n  private ws: RelayWebSocket | undefined\n  private incomingMessageQueue = new Queue<string>()\n  private queueRunning = false\n  private challenge: string | undefined\n  private authPromise: Promise<string> | undefined\n  private serial: number = 0\n  private verifyEvent: Nostr['verifyEvent']\n\n  private _WebSocket: typeof WebSocket\n\n  constructor(url: string, opts: AbstractRelayConstructorOptions) {\n    this.url = normalizeURL(url)\n    this.verifyEvent = opts.verifyEvent\n    this._WebSocket = opts.websocketImplementation || WebSocket\n    this.enablePing = opts.enablePing\n    this.enableReconnect = opts.enableReconnect || false\n  }\n\n  static async connect(\n    url: string,\n    opts: AbstractRelayConstructorOptions & Parameters<AbstractRelay['connect']>[0],\n  ): Promise<AbstractRelay> {\n    const relay = new AbstractRelay(url, opts)\n    await relay.connect(opts)\n    return relay\n  }\n\n  private closeAllSubscriptions(reason: string) {\n    for (let [_, sub] of this.openSubs) {\n      sub.close(reason)\n    }\n    this.openSubs.clear()\n\n    for (let [_, ep] of this.openEventPublishes) {\n      ep.reject(new Error(reason))\n    }\n    this.openEventPublishes.clear()\n\n    for (let [_, cr] of this.openCountRequests) {\n      cr.reject(new Error(reason))\n    }\n    this.openCountRequests.clear()\n  }\n\n  public get connected(): boolean {\n    return this._connected\n  }\n\n  private async reconnect(): Promise<void> {\n    const backoff = this.resubscribeBackoff[Math.min(this.reconnectAttempts, this.resubscribeBackoff.length - 1)]\n    this.reconnectAttempts++\n\n    this.reconnectTimeoutHandle = setTimeout(async () => {\n      try {\n        await this.connect()\n      } catch (err) {\n        // this will be called again through onclose/onerror\n      }\n    }, backoff)\n  }\n\n  private handleHardClose(reason: string) {\n    if (this.pingIntervalHandle) {\n      clearInterval(this.pingIntervalHandle)\n      this.pingIntervalHandle = undefined\n    }\n\n    this._connected = false\n    this.connectionPromise = undefined\n\n    const wasIntentional = this.closedIntentionally\n    this.closedIntentionally = false // reset for next time\n\n    this.onclose?.()\n\n    if (this.enableReconnect && !wasIntentional) {\n      this.reconnect()\n    } else {\n      this.closeAllSubscriptions(reason)\n    }\n  }\n\n  public async connect(opts?: { timeout?: number; abort?: AbortSignal }): Promise<void> {\n    let connectionTimeoutHandle: ReturnType<typeof setTimeout> | undefined\n\n    if (this.connectionPromise) return this.connectionPromise\n\n    this.challenge = undefined\n    this.authPromise = undefined\n    this.connectionPromise = new Promise((resolve, reject) => {\n      if (opts?.timeout) {\n        connectionTimeoutHandle = setTimeout(() => {\n          reject('connection timed out')\n          this.connectionPromise = undefined\n          this.onclose?.()\n          this.closeAllSubscriptions('relay connection timed out')\n        }, opts.timeout)\n      }\n\n      if (opts?.abort) {\n        opts.abort.onabort = reject\n      }\n\n      try {\n        this.ws = new this._WebSocket(this.url)\n      } catch (err) {\n        clearTimeout(connectionTimeoutHandle)\n        reject(err)\n        return\n      }\n\n      this.ws.onopen = () => {\n        if (this.reconnectTimeoutHandle) {\n          clearTimeout(this.reconnectTimeoutHandle)\n          this.reconnectTimeoutHandle = undefined\n        }\n        clearTimeout(connectionTimeoutHandle)\n        this._connected = true\n\n        const isReconnection = this.reconnectAttempts > 0\n        this.reconnectAttempts = 0\n\n        // resubscribe to all open subscriptions\n        for (const sub of this.openSubs.values()) {\n          sub.eosed = false\n          if (isReconnection) {\n            for (let f = 0; f < sub.filters.length; f++) {\n              if (sub.lastEmitted) {\n                sub.filters[f].since = sub.lastEmitted + 1\n              }\n            }\n          }\n          sub.fire()\n        }\n\n        if (this.enablePing) {\n          this.pingIntervalHandle = setInterval(() => this.pingpong(), this.pingFrequency)\n        }\n        resolve()\n      }\n\n      this.ws.onerror = ev => {\n        clearTimeout(connectionTimeoutHandle)\n        reject((ev as any).message || 'websocket error')\n        this.handleHardClose('relay connection errored')\n      }\n\n      this.ws.onclose = ev => {\n        clearTimeout(connectionTimeoutHandle)\n        reject((ev as any).message || 'websocket closed')\n        this.handleHardClose('relay connection closed')\n      }\n\n      this.ws.onmessage = this._onmessage.bind(this)\n    })\n\n    return this.connectionPromise\n  }\n\n  private waitForPingPong() {\n    return new Promise(resolve => {\n      // listen for pong\n      ;(this.ws as any).once('pong', () => resolve(true))\n      // send a ping\n      this.ws!.ping!()\n    })\n  }\n\n  private waitForDummyReq() {\n    return new Promise((resolve, reject) => {\n      if (!this.connectionPromise) return reject(new Error(`no connection to ${this.url}, can't ping`))\n\n      // make a dummy request with expected empty eose reply\n      // [\"REQ\", \"_\", {\"ids\":[\"aaaa...aaaa\"], \"limit\": 0}]\n      try {\n        const sub = this.subscribe(\n          [{ ids: ['aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'], limit: 0 }],\n          {\n            label: 'forced-ping',\n            oneose: () => {\n              resolve(true)\n              sub.close()\n            },\n            onclose() {\n              // if we get a CLOSED it's because the relay is alive\n              resolve(true)\n            },\n            eoseTimeout: this.pingTimeout + 1000,\n          },\n        )\n      } catch (err) {\n        reject(err)\n      }\n    })\n  }\n\n  // nodejs requires this magic here to ensure connections are closed when internet goes off and stuff\n  // in browsers it's done automatically. see https://github.com/nbd-wtf/nostr-tools/issues/491\n  private async pingpong() {\n    // if the websocket is connected\n    if (this.ws?.readyState === 1) {\n      // wait for either a ping-pong reply or a timeout\n      const result = await Promise.any([\n        // browsers don't have ping so use a dummy req\n        this.ws && this.ws.ping && (this.ws as any).once ? this.waitForPingPong() : this.waitForDummyReq(),\n        new Promise(res => setTimeout(() => res(false), this.pingTimeout)),\n      ])\n\n      if (!result) {\n        // pingpong closing socket\n        if (this.ws?.readyState === this._WebSocket.OPEN) {\n          this.ws?.close()\n        }\n      }\n    }\n  }\n\n  private async runQueue() {\n    this.queueRunning = true\n    while (true) {\n      if (false === this.handleNext()) {\n        break\n      }\n      await yieldThread()\n    }\n    this.queueRunning = false\n  }\n\n  private handleNext(): undefined | false {\n    const json = this.incomingMessageQueue.dequeue()\n    if (!json) {\n      return false\n    }\n\n    // shortcut EVENT sub\n    const subid = getSubscriptionId(json)\n    if (subid) {\n      const so = this.openSubs.get(subid as string)\n      if (!so) {\n        // this is an EVENT message, but for a subscription we don't have, so just stop here\n        return\n      }\n\n      // this will be called only when this message is a EVENT message for a subscription we have\n      // we do this before parsing the JSON to not have to do that for duplicate events\n      //   since JSON parsing is slow\n      const id = getHex64(json, 'id')\n      const alreadyHave = so.alreadyHaveEvent?.(id)\n\n      // notify any interested client that the relay has this event\n      // (do this after alreadyHaveEvent() because the client may rely on this to answer that)\n      so.receivedEvent?.(this, id)\n\n      if (alreadyHave) {\n        // if we had already seen this event we can just stop here\n        return\n      }\n    }\n\n    try {\n      let data = JSON.parse(json)\n      // we won't do any checks against the data since all failures (i.e. invalid messages from relays)\n      // will naturally be caught by the encompassing try..catch block\n\n      switch (data[0]) {\n        case 'EVENT': {\n          const so = this.openSubs.get(data[1] as string) as Subscription\n          const event = data[2] as NostrEvent\n          if (this.verifyEvent(event) && matchFilters(so.filters, event)) {\n            so.onevent(event)\n          }\n          if (!so.lastEmitted || so.lastEmitted < event.created_at) so.lastEmitted = event.created_at\n          return\n        }\n        case 'COUNT': {\n          const id: string = data[1]\n          const payload = data[2] as { count: number }\n          const cr = this.openCountRequests.get(id) as CountResolver\n          if (cr) {\n            cr.resolve(payload.count)\n            this.openCountRequests.delete(id)\n          }\n          return\n        }\n        case 'EOSE': {\n          const so = this.openSubs.get(data[1] as string)\n          if (!so) return\n          so.receivedEose()\n          return\n        }\n        case 'OK': {\n          const id: string = data[1]\n          const ok: boolean = data[2]\n          const reason: string = data[3]\n          const ep = this.openEventPublishes.get(id) as EventPublishResolver\n          if (ep) {\n            clearTimeout(ep.timeout)\n            if (ok) ep.resolve(reason)\n            else ep.reject(new Error(reason))\n            this.openEventPublishes.delete(id)\n          }\n          return\n        }\n        case 'CLOSED': {\n          const id: string = data[1]\n          const so = this.openSubs.get(id)\n          if (!so) return\n          so.closed = true\n          so.close(data[2] as string)\n          return\n        }\n        case 'NOTICE': {\n          this.onnotice(data[1] as string)\n          return\n        }\n        case 'AUTH': {\n          this.challenge = data[1] as string\n          if (this.onauth) {\n            this.auth(this.onauth)\n          }\n          return\n        }\n        default: {\n          const so = this.openSubs.get(data[1])\n          so?.oncustom?.(data)\n          return\n        }\n      }\n    } catch (err) {\n      return\n    }\n  }\n\n  public async send(message: string) {\n    if (!this.connectionPromise) throw new SendingOnClosedConnection(message, this.url)\n\n    this.connectionPromise.then(() => {\n      this.ws?.send(message)\n    })\n  }\n\n  public async auth(signAuthEvent: (evt: EventTemplate) => Promise<VerifiedEvent>): Promise<string> {\n    const challenge = this.challenge\n    if (!challenge) throw new Error(\"can't perform auth, no challenge was received\")\n    if (this.authPromise) return this.authPromise\n\n    this.authPromise = new Promise<string>(async (resolve, reject) => {\n      try {\n        let evt = await signAuthEvent(makeAuthEvent(this.url, challenge))\n        let timeout = setTimeout(() => {\n          let ep = this.openEventPublishes.get(evt.id) as EventPublishResolver\n          if (ep) {\n            ep.reject(new Error('auth timed out'))\n            this.openEventPublishes.delete(evt.id)\n          }\n        }, this.publishTimeout)\n        this.openEventPublishes.set(evt.id, { resolve, reject, timeout })\n        this.send('[\"AUTH\",' + JSON.stringify(evt) + ']')\n      } catch (err) {\n        console.warn('subscribe auth function failed:', err)\n      }\n    })\n    return this.authPromise\n  }\n\n  public async publish(event: Event): Promise<string> {\n    const ret = new Promise<string>((resolve, reject) => {\n      const timeout = setTimeout(() => {\n        const ep = this.openEventPublishes.get(event.id) as EventPublishResolver\n        if (ep) {\n          ep.reject(new Error('publish timed out'))\n          this.openEventPublishes.delete(event.id)\n        }\n      }, this.publishTimeout)\n      this.openEventPublishes.set(event.id, { resolve, reject, timeout })\n    })\n    this.send('[\"EVENT\",' + JSON.stringify(event) + ']')\n    return ret\n  }\n\n  public async count(filters: Filter[], params: { id?: string | null }): Promise<number> {\n    this.serial++\n    const id = params?.id || 'count:' + this.serial\n    const ret = new Promise<number>((resolve, reject) => {\n      this.openCountRequests.set(id, { resolve, reject })\n    })\n    this.send('[\"COUNT\",\"' + id + '\",' + JSON.stringify(filters).substring(1))\n    return ret\n  }\n\n  public subscribe(\n    filters: Filter[],\n    params: Partial<SubscriptionParams> & { label?: string; id?: string },\n  ): Subscription {\n    const sub = this.prepareSubscription(filters, params)\n    sub.fire()\n\n    if (params.abort) {\n      params.abort.onabort = () => sub.close(String(params.abort!.reason || '<aborted>'))\n    }\n\n    return sub\n  }\n\n  public prepareSubscription(\n    filters: Filter[],\n    params: Partial<SubscriptionParams> & { label?: string; id?: string },\n  ): Subscription {\n    this.serial++\n    const id = params.id || (params.label ? params.label + ':' : 'sub:') + this.serial\n    const subscription = new Subscription(this, id, filters, params)\n    this.openSubs.set(id, subscription)\n    return subscription\n  }\n\n  public close() {\n    this.closedIntentionally = true\n    if (this.reconnectTimeoutHandle) {\n      clearTimeout(this.reconnectTimeoutHandle)\n      this.reconnectTimeoutHandle = undefined\n    }\n    if (this.pingIntervalHandle) {\n      clearInterval(this.pingIntervalHandle)\n      this.pingIntervalHandle = undefined\n    }\n    this.closeAllSubscriptions('relay connection closed by us')\n    this._connected = false\n    this.onclose?.()\n    if (this.ws?.readyState === this._WebSocket.OPEN) {\n      this.ws?.close()\n    }\n  }\n\n  // this is the function assigned to this.ws.onmessage\n  // it's exposed for testing and debugging purposes\n  public _onmessage(ev: MessageEvent<any>) {\n    this.incomingMessageQueue.enqueue(ev.data as string)\n    if (!this.queueRunning) {\n      this.runQueue()\n    }\n  }\n}\n\nexport class Subscription {\n  public readonly relay: AbstractRelay\n  public readonly id: string\n\n  public lastEmitted: number | undefined\n  public closed: boolean = false\n  public eosed: boolean = false\n  public filters: Filter[]\n  public alreadyHaveEvent: ((id: string) => boolean) | undefined\n  public receivedEvent: ((relay: AbstractRelay, id: string) => void) | undefined\n\n  public onevent: (evt: Event) => void\n  public oneose: (() => void) | undefined\n  public onclose: ((reason: string) => void) | undefined\n\n  // will get any messages that have this subscription id as their second item and are not default standard\n  public oncustom: ((msg: string[]) => void) | undefined\n\n  public eoseTimeout: number\n  private eoseTimeoutHandle: ReturnType<typeof setTimeout> | undefined\n\n  constructor(relay: AbstractRelay, id: string, filters: Filter[], params: SubscriptionParams) {\n    if (filters.length === 0) throw new Error(\"subscription can't be created with zero filters\")\n\n    this.relay = relay\n    this.filters = filters\n    this.id = id\n    this.alreadyHaveEvent = params.alreadyHaveEvent\n    this.receivedEvent = params.receivedEvent\n    this.eoseTimeout = params.eoseTimeout || relay.baseEoseTimeout\n\n    this.oneose = params.oneose\n    this.onclose = params.onclose\n    this.onevent =\n      params.onevent ||\n      (event => {\n        console.warn(\n          `onevent() callback not defined for subscription '${this.id}' in relay ${this.relay.url}. event received:`,\n          event,\n        )\n      })\n  }\n\n  public fire() {\n    this.relay.send('[\"REQ\",\"' + this.id + '\",' + JSON.stringify(this.filters).substring(1))\n\n    // only now we start counting the eoseTimeout\n    this.eoseTimeoutHandle = setTimeout(this.receivedEose.bind(this), this.eoseTimeout)\n  }\n\n  public receivedEose() {\n    if (this.eosed) return\n    clearTimeout(this.eoseTimeoutHandle)\n    this.eosed = true\n    this.oneose?.()\n  }\n\n  public close(reason: string = 'closed by caller') {\n    if (!this.closed && this.relay.connected) {\n      // if the connection was closed by the user calling .close() we will send a CLOSE message\n      // otherwise this._open will be already set to false so we will skip this\n      try {\n        this.relay.send('[\"CLOSE\",' + JSON.stringify(this.id) + ']')\n      } catch (err) {\n        if (err instanceof SendingOnClosedConnection) {\n          /* doesn't matter, it's ok */\n        } else {\n          throw err\n        }\n      }\n      this.closed = true\n    }\n    this.relay.openSubs.delete(this.id)\n    this.onclose?.(reason)\n  }\n}\n\nexport type SubscriptionParams = {\n  onevent?: (evt: Event) => void\n  oneose?: () => void\n  onclose?: (reason: string) => void\n  alreadyHaveEvent?: (id: string) => boolean\n  receivedEvent?: (relay: AbstractRelay, id: string) => void\n  eoseTimeout?: number\n  abort?: AbortSignal\n}\n\nexport type CountResolver = {\n  resolve: (count: number) => void\n  reject: (err: Error) => void\n}\n\nexport type EventPublishResolver = {\n  resolve: (reason: string) => void\n  reject: (err: Error) => void\n  timeout: ReturnType<typeof setTimeout>\n}\n", "/* global WebSocket */\n\nimport {\n  AbstractRelay as AbstractRelay,\n  SubscriptionParams,\n  Subscription,\n  type AbstractRelayConstructorOptions,\n} from './abstract-relay.ts'\nimport { normalizeURL } from './utils.ts'\n\nimport type { Event, EventTemplate, Nostr, VerifiedEvent } from './core.ts'\nimport { type Filter } from './filter.ts'\nimport { alwaysTrue } from './helpers.ts'\n\nexport type SubCloser = { close: (reason?: string) => void }\n\nexport type AbstractPoolConstructorOptions = AbstractRelayConstructorOptions & {\n  // automaticallyAuth takes a relay URL and should return null\n  // in case that relay shouldn't be authenticated against\n  // or a function to sign the AUTH event template otherwise (that function may still throw in case of failure)\n  automaticallyAuth?: (relayURL: string) => null | ((event: EventTemplate) => Promise<VerifiedEvent>)\n}\n\nexport type SubscribeManyParams = Omit<SubscriptionParams, 'onclose'> & {\n  maxWait?: number\n  abort?: AbortSignal\n  onclose?: (reasons: string[]) => void\n  onauth?: (event: EventTemplate) => Promise<VerifiedEvent>\n  id?: string\n  label?: string\n}\n\nexport class AbstractSimplePool {\n  protected relays: Map<string, AbstractRelay> = new Map()\n  public seenOn: Map<string, Set<AbstractRelay>> = new Map()\n  public trackRelays: boolean = false\n\n  public verifyEvent: Nostr['verifyEvent']\n  public enablePing: boolean | undefined\n  public enableReconnect: boolean\n  public automaticallyAuth?: (relayURL: string) => null | ((event: EventTemplate) => Promise<VerifiedEvent>)\n  public trustedRelayURLs: Set<string> = new Set()\n\n  private _WebSocket?: typeof WebSocket\n\n  constructor(opts: AbstractPoolConstructorOptions) {\n    this.verifyEvent = opts.verifyEvent\n    this._WebSocket = opts.websocketImplementation\n    this.enablePing = opts.enablePing\n    this.enableReconnect = opts.enableReconnect || false\n    this.automaticallyAuth = opts.automaticallyAuth\n  }\n\n  async ensureRelay(\n    url: string,\n    params?: {\n      connectionTimeout?: number\n      abort?: AbortSignal\n    },\n  ): Promise<AbstractRelay> {\n    url = normalizeURL(url)\n\n    let relay = this.relays.get(url)\n    if (!relay) {\n      relay = new AbstractRelay(url, {\n        verifyEvent: this.trustedRelayURLs.has(url) ? alwaysTrue : this.verifyEvent,\n        websocketImplementation: this._WebSocket,\n        enablePing: this.enablePing,\n        enableReconnect: this.enableReconnect,\n      })\n      relay.onclose = () => {\n        if (relay && !relay.enableReconnect) {\n          this.relays.delete(url)\n        }\n      }\n      this.relays.set(url, relay)\n    }\n\n    if (this.automaticallyAuth) {\n      const authSignerFn = this.automaticallyAuth(url)\n      if (authSignerFn) {\n        relay.onauth = authSignerFn\n      }\n    }\n\n    await relay.connect({\n      timeout: params?.connectionTimeout,\n      abort: params?.abort,\n    })\n\n    return relay\n  }\n\n  close(relays: string[]) {\n    relays.map(normalizeURL).forEach(url => {\n      this.relays.get(url)?.close()\n      this.relays.delete(url)\n    })\n  }\n\n  subscribe(relays: string[], filter: Filter, params: SubscribeManyParams): SubCloser {\n    const request: { url: string; filter: Filter }[] = []\n    for (let i = 0; i < relays.length; i++) {\n      const url = normalizeURL(relays[i])\n      if (!request.find(r => r.url === url)) {\n        request.push({ url, filter: filter })\n      }\n    }\n\n    return this.subscribeMap(request, params)\n  }\n\n  subscribeMany(relays: string[], filter: Filter, params: SubscribeManyParams): SubCloser {\n    const request: { url: string; filter: Filter }[] = []\n    const uniqUrls: string[] = []\n    for (let i = 0; i < relays.length; i++) {\n      const url = normalizeURL(relays[i])\n      if (uniqUrls.indexOf(url) === -1) {\n        uniqUrls.push(url)\n        request.push({ url, filter: filter })\n      }\n    }\n\n    return this.subscribeMap(request, params)\n  }\n\n  subscribeMap(requests: { url: string; filter: Filter }[], params: SubscribeManyParams): SubCloser {\n    const grouped = new Map<string, Filter[]>()\n    for (const req of requests) {\n      const { url, filter } = req\n      if (!grouped.has(url)) grouped.set(url, [])\n      grouped.get(url)!.push(filter)\n    }\n    const groupedRequests = Array.from(grouped.entries()).map(([url, filters]) => ({ url, filters }))\n\n    if (this.trackRelays) {\n      params.receivedEvent = (relay: AbstractRelay, id: string) => {\n        let set = this.seenOn.get(id)\n        if (!set) {\n          set = new Set()\n          this.seenOn.set(id, set)\n        }\n        set.add(relay)\n      }\n    }\n\n    const _knownIds = new Set<string>()\n    const subs: Subscription[] = []\n\n    // batch all EOSEs into a single\n    const eosesReceived: boolean[] = []\n    let handleEose = (i: number) => {\n      if (eosesReceived[i]) return // do not act twice for the same relay\n      eosesReceived[i] = true\n      if (eosesReceived.filter(a => a).length === groupedRequests.length) {\n        params.oneose?.()\n        handleEose = () => {}\n      }\n    }\n    // batch all closes into a single\n    const closesReceived: string[] = []\n    let handleClose = (i: number, reason: string) => {\n      if (closesReceived[i]) return // do not act twice for the same relay\n      handleEose(i)\n      closesReceived[i] = reason\n      if (closesReceived.filter(a => a).length === groupedRequests.length) {\n        params.onclose?.(closesReceived)\n        handleClose = () => {}\n      }\n    }\n\n    const localAlreadyHaveEventHandler = (id: string) => {\n      if (params.alreadyHaveEvent?.(id)) {\n        return true\n      }\n      const have = _knownIds.has(id)\n      _knownIds.add(id)\n      return have\n    }\n\n    // open a subscription in all given relays\n    const allOpened = Promise.all(\n      groupedRequests.map(async ({ url, filters }, i) => {\n        let relay: AbstractRelay\n        try {\n          relay = await this.ensureRelay(url, {\n            connectionTimeout: params.maxWait ? Math.max(params.maxWait * 0.8, params.maxWait - 1000) : undefined,\n            abort: params.abort,\n          })\n        } catch (err) {\n          handleClose(i, (err as any)?.message || String(err))\n          return\n        }\n\n        let subscription = relay.subscribe(filters, {\n          ...params,\n          oneose: () => handleEose(i),\n          onclose: reason => {\n            if (reason.startsWith('auth-required: ') && params.onauth) {\n              relay\n                .auth(params.onauth)\n                .then(() => {\n                  relay.subscribe(filters, {\n                    ...params,\n                    oneose: () => handleEose(i),\n                    onclose: reason => {\n                      handleClose(i, reason) // the second time we won't try to auth anymore\n                    },\n                    alreadyHaveEvent: localAlreadyHaveEventHandler,\n                    eoseTimeout: params.maxWait,\n                    abort: params.abort,\n                  })\n                })\n                .catch(err => {\n                  handleClose(i, `auth was required and attempted, but failed with: ${err}`)\n                })\n            } else {\n              handleClose(i, reason)\n            }\n          },\n          alreadyHaveEvent: localAlreadyHaveEventHandler,\n          eoseTimeout: params.maxWait,\n          abort: params.abort,\n        })\n\n        subs.push(subscription)\n      }),\n    )\n\n    return {\n      async close(reason?: string) {\n        await allOpened\n        subs.forEach(sub => {\n          sub.close(reason)\n        })\n      },\n    }\n  }\n\n  subscribeEose(\n    relays: string[],\n    filter: Filter,\n    params: Pick<SubscribeManyParams, 'label' | 'id' | 'onevent' | 'onclose' | 'maxWait' | 'onauth'>,\n  ): SubCloser {\n    const subcloser = this.subscribe(relays, filter, {\n      ...params,\n      oneose() {\n        subcloser.close('closed automatically on eose')\n      },\n    })\n    return subcloser\n  }\n\n  subscribeManyEose(\n    relays: string[],\n    filter: Filter,\n    params: Pick<SubscribeManyParams, 'label' | 'id' | 'onevent' | 'onclose' | 'maxWait' | 'onauth'>,\n  ): SubCloser {\n    const subcloser = this.subscribeMany(relays, filter, {\n      ...params,\n      oneose() {\n        subcloser.close('closed automatically on eose')\n      },\n    })\n    return subcloser\n  }\n\n  async querySync(\n    relays: string[],\n    filter: Filter,\n    params?: Pick<SubscribeManyParams, 'label' | 'id' | 'maxWait'>,\n  ): Promise<Event[]> {\n    return new Promise(async resolve => {\n      const events: Event[] = []\n      this.subscribeEose(relays, filter, {\n        ...params,\n        onevent(event: Event) {\n          events.push(event)\n        },\n        onclose(_: string[]) {\n          resolve(events)\n        },\n      })\n    })\n  }\n\n  async get(\n    relays: string[],\n    filter: Filter,\n    params?: Pick<SubscribeManyParams, 'label' | 'id' | 'maxWait'>,\n  ): Promise<Event | null> {\n    filter.limit = 1\n    const events = await this.querySync(relays, filter, params)\n    events.sort((a, b) => b.created_at - a.created_at)\n    return events[0] || null\n  }\n\n  publish(\n    relays: string[],\n    event: Event,\n    options?: { onauth?: (evt: EventTemplate) => Promise<VerifiedEvent> },\n  ): Promise<string>[] {\n    return relays.map(normalizeURL).map(async (url, i, arr) => {\n      if (arr.indexOf(url) !== i) {\n        // duplicate\n        return Promise.reject('duplicate url')\n      }\n\n      let r = await this.ensureRelay(url)\n      return r\n        .publish(event)\n        .catch(async err => {\n          if (err instanceof Error && err.message.startsWith('auth-required: ') && options?.onauth) {\n            await r.auth(options.onauth)\n            return r.publish(event) // retry\n          }\n          throw err\n        })\n        .then(reason => {\n          if (this.trackRelays) {\n            let set = this.seenOn.get(event.id)\n            if (!set) {\n              set = new Set()\n              this.seenOn.set(event.id, set)\n            }\n            set.add(r)\n          }\n          return reason\n        })\n    })\n  }\n\n  listConnectionStatus(): Map<string, boolean> {\n    const map = new Map<string, boolean>()\n    this.relays.forEach((relay, url) => map.set(url, relay.connected))\n\n    return map\n  }\n\n  destroy(): void {\n    this.relays.forEach(conn => conn.close())\n    this.relays = new Map()\n  }\n}\n", "/* global WebSocket */\n\nimport { verifyEvent } from './pure.ts'\nimport { AbstractSimplePool, type AbstractPoolConstructorOptions } from './abstract-pool.ts'\n\nvar _WebSocket: typeof WebSocket\n\ntry {\n  _WebSocket = WebSocket\n} catch {}\n\nexport function useWebSocketImplementation(websocketImplementation: any) {\n  _WebSocket = websocketImplementation\n}\n\nexport class SimplePool extends AbstractSimplePool {\n  constructor(options?: Pick<AbstractPoolConstructorOptions, 'enablePing' | 'enableReconnect'>) {\n    super({ verifyEvent, websocketImplementation: _WebSocket, ...options })\n  }\n}\n\nexport * from './abstract-pool.ts'\n", "import { EventTemplate, NostrEvent, VerifiedEvent } from './core.ts'\nimport { generateSecretKey, finalizeEvent, getPublicKey, verifyEvent } from './pure.ts'\nimport { AbstractSimplePool, SubCloser } from './abstract-pool.ts'\nimport { getConversationKey, decrypt, encrypt } from './nip44.ts'\nimport { NIP05_REGEX } from './nip05.ts'\nimport { SimplePool } from './pool.ts'\nimport { Handlerinformation, NostrConnect } from './kinds.ts'\nimport { Signer } from './signer.ts'\n\nvar _fetch: any\n\ntry {\n  _fetch = fetch\n} catch {}\n\nexport function useFetchImplementation(fetchImplementation: any) {\n  _fetch = fetchImplementation\n}\n\nexport const BUNKER_REGEX = /^bunker:\\/\\/([0-9a-f]{64})\\??([?\\/\\w:.=&%-]*)$/\nconst EMAIL_REGEX = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/\n\nexport type BunkerPointer = {\n  relays: string[]\n  pubkey: string\n  secret: null | string\n}\n\nexport function toBunkerURL(bunkerPointer: BunkerPointer): string {\n  let bunkerURL = new URL(`bunker://${bunkerPointer.pubkey}`)\n  bunkerPointer.relays.forEach(relay => {\n    bunkerURL.searchParams.append('relay', relay)\n  })\n  if (bunkerPointer.secret) {\n    bunkerURL.searchParams.set('secret', bunkerPointer.secret)\n  }\n  return bunkerURL.toString()\n}\n\n/** This takes either a bunker:// URL or a [email protected] NIP-05 identifier\n    and returns a BunkerPointer -- or null in case of error */\nexport async function parseBunkerInput(input: string): Promise<BunkerPointer | null> {\n  let match = input.match(BUNKER_REGEX)\n  if (match) {\n    try {\n      const pubkey = match[1]\n      const qs = new URLSearchParams(match[2])\n      return {\n        pubkey,\n        relays: qs.getAll('relay'),\n        secret: qs.get('secret'),\n      }\n    } catch (_err) {\n      /* just move to the next case */\n    }\n  }\n\n  return queryBunkerProfile(input)\n}\n\nexport async function queryBunkerProfile(nip05: string): Promise<BunkerPointer | null> {\n  const match = nip05.match(NIP05_REGEX)\n  if (!match) return null\n\n  const [_, name = '_', domain] = match\n\n  try {\n    const url = `https://${domain}/.well-known/nostr.json?name=${name}`\n    const res = await (await _fetch(url, { redirect: 'error' })).json()\n\n    let pubkey = res.names[name]\n    let relays = res.nip46[pubkey] || []\n\n    return { pubkey, relays, secret: null }\n  } catch (_err) {\n    return null\n  }\n}\n\nexport type NostrConnectParams = {\n  clientPubkey: string\n  relays: string[]\n  secret: string\n  perms?: string[]\n  name?: string\n  url?: string\n  image?: string\n}\n\nexport function createNostrConnectURI(params: NostrConnectParams): string {\n  const queryParams = new URLSearchParams()\n\n  params.relays.forEach(relay => {\n    queryParams.append('relay', relay)\n  })\n\n  queryParams.append('secret', params.secret)\n\n  if (params.perms && params.perms.length > 0) {\n    queryParams.append('perms', params.perms.join(','))\n  }\n  if (params.name) {\n    queryParams.append('name', params.name)\n  }\n  if (params.url) {\n    queryParams.append('url', params.url)\n  }\n  if (params.image) {\n    queryParams.append('image', params.image)\n  }\n\n  return `nostrconnect://${params.clientPubkey}?${queryParams.toString()}`\n}\n\nexport type BunkerSignerParams = {\n  pool?: AbstractSimplePool\n  onauth?: (url: string) => void\n}\n\nexport class BunkerSigner implements Signer {\n  private params: BunkerSignerParams\n  private pool: AbstractSimplePool\n  private subCloser: SubCloser | undefined\n  private isOpen: boolean\n  private serial: number\n  private idPrefix: string\n  private listeners: {\n    [id: string]: {\n      resolve: (_: string) => void\n      reject: (_: string) => void\n    }\n  }\n  private waitingForAuth: { [id: string]: boolean }\n  private secretKey: Uint8Array\n  // If the client initiates the connection, the two variables below can be filled in later.\n  private conversationKey!: Uint8Array\n  public bp!: BunkerPointer\n\n  private cachedPubKey: string | undefined\n\n  /**\n   * Creates a new instance of the Nip46 class.\n   * @param relays - An array of relay addresses.\n   * @param remotePubkey - An optional remote public key. This is the key you want to sign as.\n   * @param secretKey - An optional key pair.\n   */\n  private constructor(clientSecretKey: Uint8Array, params: BunkerSignerParams) {\n    this.params = params\n    this.pool = params.pool || new SimplePool()\n    this.secretKey = clientSecretKey\n    this.isOpen = false\n    this.idPrefix = Math.random().toString(36).substring(7)\n    this.serial = 0\n    this.listeners = {}\n    this.waitingForAuth = {}\n  }\n\n  /**\n   * [Factory Method 1] Creates a Signer using bunker information (bunker:// URL or NIP-05).\n   * This method is used when the public key of the bunker is known in advance.\n   */\n  public static fromBunker(\n    clientSecretKey: Uint8Array,\n    bp: BunkerPointer,\n    params: BunkerSignerParams = {},\n  ): BunkerSigner {\n    if (bp.relays.length === 0) {\n      throw new Error('no relays specified for this bunker')\n    }\n\n    const signer = new BunkerSigner(clientSecretKey, params)\n\n    signer.conversationKey = getConversationKey(clientSecretKey, bp.pubkey)\n    signer.bp = bp\n\n    signer.setupSubscription()\n    return signer\n  }\n\n  /**\n   * [Factory Method 2] Creates a Signer using a nostrconnect:// URI generated by the client.\n   * In this method, the bunker initiates the connection by scanning the URI.\n   */\n  public static async fromURI(\n    clientSecretKey: Uint8Array,\n    connectionURI: string,\n    bunkerParams: BunkerSignerParams = {},\n    maxWaitOrAbort: number | AbortSignal = 300_000,\n  ): Promise<BunkerSigner> {\n    const signer = new BunkerSigner(clientSecretKey, bunkerParams)\n    const uri = new URL(connectionURI)\n    const clientPubkey = getPublicKey(clientSecretKey)\n\n    return new Promise((resolve, reject) => {\n      let success = false\n      const sub = signer.pool.subscribe(\n        uri.searchParams.getAll('relay'),\n        {\n          kinds: [NostrConnect],\n          '#p': [clientPubkey],\n          limit: 0,\n        },\n        {\n          onevent: async (event: NostrEvent) => {\n            try {\n              const tempConvKey = getConversationKey(clientSecretKey, event.pubkey)\n              const decryptedContent = decrypt(event.content, tempConvKey)\n\n              const response = JSON.parse(decryptedContent)\n\n              if (response.result === uri.searchParams.get('secret')) {\n                sub.close()\n\n                signer.bp = {\n                  pubkey: event.pubkey,\n                  relays: uri.searchParams.getAll('relay'),\n                  secret: uri.searchParams.get('secret'),\n                }\n                signer.conversationKey = getConversationKey(clientSecretKey, event.pubkey)\n                signer.setupSubscription()\n\n                success = true\n                await Promise.race([new Promise(resolve => setTimeout(resolve, 1000)), signer.switchRelays()])\n                resolve(signer)\n              }\n            } catch (e) {\n              console.warn('failed to process potential connection event', e)\n            }\n          },\n          onclose: () => {\n            if (!success) reject(new Error('subscription closed before connection was established.'))\n          },\n          maxWait: typeof maxWaitOrAbort === 'number' ? maxWaitOrAbort : undefined,\n          abort: typeof maxWaitOrAbort !== 'number' ? maxWaitOrAbort : undefined,\n        },\n      )\n    })\n  }\n\n  private setupSubscription() {\n    const listeners = this.listeners\n    const waitingForAuth = this.waitingForAuth\n    const convKey = this.conversationKey\n\n    this.subCloser = this.pool.subscribe(\n      this.bp.relays,\n      {\n        kinds: [NostrConnect],\n        authors: [this.bp.pubkey],\n        '#p': [getPublicKey(this.secretKey)],\n        limit: 0,\n      },\n      {\n        onevent: async (event: NostrEvent) => {\n          const o = JSON.parse(decrypt(event.content, convKey))\n          const { id, result, error } = o\n\n          if (result === 'auth_url' && waitingForAuth[id]) {\n            delete waitingForAuth[id]\n\n            if (this.params.onauth) {\n              this.params.onauth(error)\n            } else {\n              console.warn(\n                `nostr-tools/nip46: remote signer ${this.bp.pubkey} tried to send an \"auth_url\"='${error}' but there was no onauth() callback configured.`,\n              )\n            }\n            return\n          }\n\n          let handler = listeners[id]\n          if (handler) {\n            if (error) handler.reject(error)\n            else if (result) handler.resolve(result)\n            delete listeners[id]\n          }\n        },\n        onclose: () => {\n          this.subCloser = undefined\n        },\n      },\n    )\n    this.isOpen = true\n  }\n\n  async switchRelays(): Promise<boolean> {\n    try {\n      const switchResp = await this.sendRequest('switch_relays', [])\n      let relays = JSON.parse(switchResp) as string[] | null\n      if (!relays) return false\n      if (JSON.stringify(relays.sort()) === JSON.stringify(this.bp.relays)) return false\n\n      this.bp.relays = relays\n      let previousCloser = this.subCloser!\n      setTimeout(() => {\n        previousCloser.close()\n      }, 5000)\n\n      this.subCloser = undefined\n      this.setupSubscription()\n      return true\n    } catch {\n      return false\n    }\n  }\n\n  // closes the subscription -- this object can't be used anymore after this\n  async close() {\n    this.isOpen = false\n    this.subCloser!.close()\n  }\n\n  async sendRequest(method: string, params: string[]): Promise<string> {\n    return new Promise(async (resolve, reject) => {\n      try {\n        if (!this.isOpen) throw new Error('this signer is not open anymore, create a new one')\n        if (!this.subCloser) this.setupSubscription()\n\n        this.serial++\n        const id = `${this.idPrefix}-${this.serial}`\n\n        const encryptedContent = encrypt(JSON.stringify({ id, method, params }), this.conversationKey)\n\n        // the request event\n        const verifiedEvent: VerifiedEvent = finalizeEvent(\n          {\n            kind: NostrConnect,\n            tags: [['p', this.bp.pubkey]],\n            content: encryptedContent,\n            created_at: Math.floor(Date.now() / 1000),\n          },\n          this.secretKey,\n        )\n\n        // setup callback listener\n        this.listeners[id] = { resolve, reject }\n        this.waitingForAuth[id] = true\n\n        // publish the event\n        await Promise.any(this.pool.publish(this.bp.relays, verifiedEvent))\n      } catch (err) {\n        reject(err)\n      }\n    })\n  }\n\n  /**\n   * Calls the \"connect\" method on the bunker.\n   * The promise will be rejected if the response is not \"pong\".\n   */\n  async ping(): Promise<void> {\n    let resp = await this.sendRequest('ping', [])\n    if (resp !== 'pong') throw new Error(`result is not pong: ${resp}`)\n  }\n\n  /**\n   * Calls the \"connect\" method on the bunker.\n   */\n  async connect(): Promise<void> {\n    await this.sendRequest('connect', [this.bp.pubkey, this.bp.secret || ''])\n  }\n\n  /**\n   * Calls the \"get_public_key\" method on the bunker.\n   * (before we would return the public key hardcoded in the bunker parameters, but\n   *  that is not correct as that may be the bunker pubkey and the actual signer\n   *  pubkey may be different.)\n   */\n  async getPublicKey(): Promise<string> {\n    if (!this.cachedPubKey) {\n      this.cachedPubKey = await this.sendRequest('get_public_key', [])\n    }\n    return this.cachedPubKey\n  }\n\n  /**\n   * Signs an event using the remote private key.\n   * @param event - The event to sign.\n   * @returns A Promise that resolves to the signed event.\n   */\n  async signEvent(event: EventTemplate): Promise<VerifiedEvent> {\n    let resp = await this.sendRequest('sign_event', [JSON.stringify(event)])\n    let signed: NostrEvent = JSON.parse(resp)\n    if (verifyEvent(signed)) {\n      return signed\n    } else {\n      throw new Error(`event returned from bunker is improperly signed: ${JSON.stringify(signed)}`)\n    }\n  }\n\n  async nip04Encrypt(thirdPartyPubkey: string, plaintext: string): Promise<string> {\n    return await this.sendRequest('nip04_encrypt', [thirdPartyPubkey, plaintext])\n  }\n\n  async nip04Decrypt(thirdPartyPubkey: string, ciphertext: string): Promise<string> {\n    return await this.sendRequest('nip04_decrypt', [thirdPartyPubkey, ciphertext])\n  }\n\n  async nip44Encrypt(thirdPartyPubkey: string, plaintext: string): Promise<string> {\n    return await this.sendRequest('nip44_encrypt', [thirdPartyPubkey, plaintext])\n  }\n\n  async nip44Decrypt(thirdPartyPubkey: string, ciphertext: string): Promise<string> {\n    return await this.sendRequest('nip44_decrypt', [thirdPartyPubkey, ciphertext])\n  }\n}\n\n/**\n * Creates an account with the specified username, domain, and optional email.\n * @param bunkerPubkey - The public key of the bunker to use for the create_account call.\n * @param username - The username for the account.\n * @param domain - The domain for the account.\n * @param email - The optional email for the account.\n * @param localSecretKey - Optionally pass a local secret key that will be used to communicate with the bunker,\n                           this will default to generating a random key.\n * @throws Error if the email is present but invalid.\n * @returns A Promise that resolves to the auth_url that the client should follow to create an account.\n */\nexport async function createAccount(\n  bunker: BunkerProfile,\n  params: BunkerSignerParams,\n  username: string,\n  domain: string,\n  email?: string,\n  localSecretKey: Uint8Array = generateSecretKey(),\n): Promise<BunkerSigner> {\n  if (email && !EMAIL_REGEX.test(email)) throw new Error('invalid email')\n\n  let rpc = BunkerSigner.fromBunker(localSecretKey, bunker.bunkerPointer, params)\n\n  let pubkey = await rpc.sendRequest('create_account', [username, domain, email || ''])\n\n  // once we get the newly created pubkey back, we hijack this signer instance\n  // and turn it into the main instance for this newly created pubkey\n  rpc.bp.pubkey = pubkey\n  await rpc.connect()\n\n  return rpc\n}\n\n/**\n * Fetches info on available providers that announce themselves using NIP-89 events.\n * @returns A promise that resolves to an array of available bunker objects.\n */\nexport async function fetchBunkerProviders(pool: AbstractSimplePool, relays: string[]): Promise<BunkerProfile[]> {\n  const events = await pool.querySync(relays, {\n    kinds: [Handlerinformation],\n    '#k': [NostrConnect.toString()],\n  })\n\n  events.sort((a, b) => b.created_at - a.created_at)\n\n  // validate bunkers by checking their NIP-05 and pubkey\n  // map to a more useful object\n  const validatedBunkers = await Promise.all(\n    events.map(async (event, i) => {\n      try {\n        const content = JSON.parse(event.content)\n\n        // skip duplicates\n        try {\n          if (events.findIndex(ev => JSON.parse(ev.content).nip05 === content.nip05) !== i) return undefined\n        } catch (err) {\n          /***/\n        }\n\n        const bp = await queryBunkerProfile(content.nip05)\n        if (bp && bp.pubkey === event.pubkey && bp.relays.length) {\n          return {\n            bunkerPointer: bp,\n            nip05: content.nip05,\n            domain: content.nip05.split('@')[1],\n            name: content.name || content.display_name,\n            picture: content.picture,\n            about: content.about,\n            website: content.website,\n            local: false,\n          }\n        }\n      } catch (err) {\n        return undefined\n      }\n    }),\n  )\n\n  return validatedBunkers.filter(b => b !== undefined) as BunkerProfile[]\n}\n\nexport type BunkerProfile = {\n  bunkerPointer: BunkerPointer\n  domain: string\n  nip05: string\n  name: string\n  picture: string\n  about: string\n  website: string\n  local: boolean\n}\n"],
-  "mappings": "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",
-  "names": ["bytesToHex", "i", "target", "bytesToHex", "sha256", "sha256", "i", "i", "reason", "_fetch", "resolve", "i"]
+  "sourcesContent": ["import { schnorr } from '@noble/curves/secp256k1.js'\nimport { bytesToHex, hexToBytes } from '@noble/hashes/utils.js'\nimport { Nostr, Event, EventTemplate, UnsignedEvent, VerifiedEvent, verifiedSymbol, validateEvent } from './core.ts'\nimport { sha256 } from '@noble/hashes/sha2.js'\n\nimport { utf8Encoder } from './utils.ts'\n\nclass JS implements Nostr {\n  generateSecretKey(): Uint8Array {\n    return schnorr.utils.randomSecretKey()\n  }\n  getPublicKey(secretKey: Uint8Array): string {\n    return bytesToHex(schnorr.getPublicKey(secretKey))\n  }\n  finalizeEvent(t: EventTemplate, secretKey: Uint8Array): VerifiedEvent {\n    const event = t as VerifiedEvent\n    event.pubkey = bytesToHex(schnorr.getPublicKey(secretKey))\n    event.id = getEventHash(event)\n    event.sig = bytesToHex(schnorr.sign(hexToBytes(getEventHash(event)), secretKey))\n    event[verifiedSymbol] = true\n    return event\n  }\n  verifyEvent(event: Event): event is VerifiedEvent {\n    if (typeof event[verifiedSymbol] === 'boolean') return event[verifiedSymbol]\n\n    const hash = getEventHash(event)\n    if (hash !== event.id) {\n      event[verifiedSymbol] = false\n      return false\n    }\n\n    try {\n      const valid = schnorr.verify(hexToBytes(event.sig), hexToBytes(hash), hexToBytes(event.pubkey))\n      event[verifiedSymbol] = valid\n      return valid\n    } catch (err) {\n      event[verifiedSymbol] = false\n      return false\n    }\n  }\n}\n\nexport function serializeEvent(evt: UnsignedEvent): string {\n  if (!validateEvent(evt)) throw new Error(\"can't serialize event with wrong or missing properties\")\n  return JSON.stringify([0, evt.pubkey, evt.created_at, evt.kind, evt.tags, evt.content])\n}\n\nexport function getEventHash(event: UnsignedEvent): string {\n  let eventHash = sha256(utf8Encoder.encode(serializeEvent(event)))\n  return bytesToHex(eventHash)\n}\n\nconst i: JS = new JS()\n\nexport const generateSecretKey = i.generateSecretKey\nexport const getPublicKey = i.getPublicKey\nexport const finalizeEvent = i.finalizeEvent\nexport const verifyEvent = i.verifyEvent\nexport * from './core.ts'\n", "export interface Nostr {\n  generateSecretKey(): Uint8Array\n  getPublicKey(secretKey: Uint8Array): string\n  finalizeEvent(event: EventTemplate, secretKey: Uint8Array): VerifiedEvent\n  verifyEvent(event: Event): event is VerifiedEvent\n}\n\n/** Designates a verified event signature. */\nexport const verifiedSymbol = Symbol('verified')\n\nexport type NostrEvent = {\n  kind: number\n  tags: string[][]\n  content: string\n  created_at: number\n  pubkey: string\n  id: string\n  sig: string\n  [verifiedSymbol]?: boolean\n}\n\nexport type Event = NostrEvent\nexport type EventTemplate = Pick<Event, 'kind' | 'tags' | 'content' | 'created_at'>\nexport type UnsignedEvent = Pick<Event, 'kind' | 'tags' | 'content' | 'created_at' | 'pubkey'>\n\n/** An event whose signature has been verified. */\nexport interface VerifiedEvent extends Event {\n  [verifiedSymbol]: true\n}\n\nconst isRecord = (obj: unknown): obj is Record<string, unknown> => obj instanceof Object\n\nexport function validateEvent<T>(event: T): event is T & UnsignedEvent {\n  if (!isRecord(event)) return false\n  if (typeof event.kind !== 'number') return false\n  if (typeof event.content !== 'string') return false\n  if (typeof event.created_at !== 'number') return false\n  if (typeof event.pubkey !== 'string') return false\n  if (!event.pubkey.match(/^[a-f0-9]{64}$/)) return false\n\n  if (!Array.isArray(event.tags)) return false\n  for (let i = 0; i < event.tags.length; i++) {\n    let tag = event.tags[i]\n    if (!Array.isArray(tag)) return false\n    for (let j = 0; j < tag.length; j++) {\n      if (typeof tag[j] !== 'string') return false\n    }\n  }\n\n  return true\n}\n\n/**\n * Sort events in reverse-chronological order by the `created_at` timestamp,\n * and then by the event `id` (lexicographically) in case of ties.\n * This mutates the array.\n */\nexport function sortEvents(events: Event[]): Event[] {\n  return events.sort((a: NostrEvent, b: NostrEvent): number => {\n    if (a.created_at !== b.created_at) {\n      return b.created_at - a.created_at\n    }\n    return a.id.localeCompare(b.id)\n  })\n}\n", "import type { Event } from './core.ts'\n\nexport const utf8Decoder: TextDecoder = new TextDecoder('utf-8')\nexport const utf8Encoder: TextEncoder = new TextEncoder()\n\nexport { bytesToHex, hexToBytes } from '@noble/hashes/utils.js'\n\nexport function normalizeURL(url: string): string {\n  try {\n    if (url.indexOf('://') === -1) url = 'wss://' + url\n    let p = new URL(url)\n    if (p.protocol === 'http:') p.protocol = 'ws:'\n    else if (p.protocol === 'https:') p.protocol = 'wss:'\n    p.pathname = p.pathname.replace(/\\/+/g, '/')\n    if (p.pathname.endsWith('/')) p.pathname = p.pathname.slice(0, -1)\n    if ((p.port === '80' && p.protocol === 'ws:') || (p.port === '443' && p.protocol === 'wss:')) p.port = ''\n    p.searchParams.sort()\n    p.hash = ''\n    return p.toString()\n  } catch (e) {\n    throw new Error(`Invalid URL: ${url}`)\n  }\n}\n\nexport function insertEventIntoDescendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return b.created_at - event.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function insertEventIntoAscendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return event.created_at - b.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function binarySearch<T>(arr: T[], compare: (b: T) => number): [number, boolean] {\n  let start = 0\n  let end = arr.length - 1\n\n  while (start <= end) {\n    const mid = Math.floor((start + end) / 2)\n    const cmp = compare(arr[mid])\n\n    if (cmp === 0) {\n      return [mid, true]\n    }\n\n    if (cmp < 0) {\n      end = mid - 1\n    } else {\n      start = mid + 1\n    }\n  }\n\n  return [start, false]\n}\n\nexport class QueueNode<V> {\n  public value: V\n  public next: QueueNode<V> | null = null\n  public prev: QueueNode<V> | null = null\n\n  constructor(message: V) {\n    this.value = message\n  }\n}\n\nexport class Queue<V> {\n  public first: QueueNode<V> | null\n  public last: QueueNode<V> | null\n\n  constructor() {\n    this.first = null\n    this.last = null\n  }\n\n  enqueue(value: V): boolean {\n    const newNode = new QueueNode(value)\n    if (!this.last) {\n      // list is empty\n      this.first = newNode\n      this.last = newNode\n    } else if (this.last === this.first) {\n      // list has a single element\n      this.last = newNode\n      this.last.prev = this.first\n      this.first.next = newNode\n    } else {\n      // list has elements, add as last\n      newNode.prev = this.last\n      this.last.next = newNode\n      this.last = newNode\n    }\n    return true\n  }\n\n  dequeue(): V | null {\n    if (!this.first) return null\n\n    if (this.first === this.last) {\n      const target = this.first\n      this.first = null\n      this.last = null\n      return target.value\n    }\n\n    const target = this.first\n    this.first = target.next\n    if (this.first) {\n      this.first.prev = null // fix: clean up prev pointer\n    }\n\n    return target.value\n  }\n}\n", "import { chacha20 } from '@noble/ciphers/chacha.js'\nimport { equalBytes } from '@noble/ciphers/utils.js'\nimport { secp256k1 } from '@noble/curves/secp256k1.js'\nimport { extract as hkdf_extract, expand as hkdf_expand } from '@noble/hashes/hkdf.js'\nimport { hmac } from '@noble/hashes/hmac.js'\nimport { sha256 } from '@noble/hashes/sha2.js'\nimport { concatBytes, hexToBytes, randomBytes } from '@noble/hashes/utils.js'\nimport { base64 } from '@scure/base'\n\nimport { utf8Decoder, utf8Encoder } from './utils.ts'\n\nconst minPlaintextSize = 0x0001 // 1b msg => padded to 32b\nconst maxPlaintextSize = 0xffff // 65535 (64kb-1) => padded to 64kb\n\nexport function getConversationKey(privkeyA: Uint8Array, pubkeyB: string): Uint8Array {\n  const sharedX = secp256k1.getSharedSecret(privkeyA, hexToBytes('02' + pubkeyB)).subarray(1, 33)\n  return hkdf_extract(sha256, sharedX, utf8Encoder.encode('nip44-v2'))\n}\n\nfunction getMessageKeys(\n  conversationKey: Uint8Array,\n  nonce: Uint8Array,\n): { chacha_key: Uint8Array; chacha_nonce: Uint8Array; hmac_key: Uint8Array } {\n  const keys = hkdf_expand(sha256, conversationKey, nonce, 76)\n  return {\n    chacha_key: keys.subarray(0, 32),\n    chacha_nonce: keys.subarray(32, 44),\n    hmac_key: keys.subarray(44, 76),\n  }\n}\n\nfunction calcPaddedLen(len: number): number {\n  if (!Number.isSafeInteger(len) || len < 1) throw new Error('expected positive integer')\n  if (len <= 32) return 32\n  const nextPower = 1 << (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 > maxPlaintextSize)\n    throw new Error('invalid plaintext size: must be between 1 and 65535 bytes')\n  const arr = new Uint8Array(2)\n  new DataView(arr.buffer).setUint16(0, num, false)\n  return arr\n}\n\nfunction pad(plaintext: string): Uint8Array {\n  const unpadded = utf8Encoder.encode(plaintext)\n  const unpaddedLen = unpadded.length\n  const prefix = 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 unpaddedLen = new DataView(padded.buffer).getUint16(0)\n  const unpadded = padded.subarray(2, 2 + unpaddedLen)\n  if (\n    unpaddedLen < minPlaintextSize ||\n    unpaddedLen > maxPlaintextSize ||\n    unpadded.length !== unpaddedLen ||\n    padded.length !== 2 + calcPaddedLen(unpaddedLen)\n  )\n    throw new Error('invalid padding')\n  return utf8Decoder.decode(unpadded)\n}\n\nfunction hmacAad(key: Uint8Array, message: Uint8Array, aad: Uint8Array): Uint8Array {\n  if (aad.length !== 32) throw new Error('AAD associated data must be 32 bytes')\n  const combined = concatBytes(aad, message)\n  return hmac(sha256, key, combined)\n}\n\n// metadata: always 65b (version: 1b, nonce: 32b, max: 32b)\n// plaintext: 1b to 0xffff\n// padded plaintext: 32b to 0xffff\n// ciphertext: 32b+2 to 0xffff+2\n// raw payload: 99 (65+32+2) to 65603 (65+0xffff+2)\n// compressed payload (base64): 132b to 87472b\nfunction decodePayload(payload: string): { nonce: Uint8Array; ciphertext: Uint8Array; mac: Uint8Array } {\n  if (typeof payload !== 'string') throw new Error('payload must be a valid string')\n  const plen = payload.length\n  if (plen < 132 || plen > 87472) throw new Error('invalid payload length: ' + plen)\n  if (payload[0] === '#') throw new Error('unknown encryption version')\n  let data: Uint8Array\n  try {\n    data = base64.decode(payload)\n  } catch (error) {\n    throw new Error('invalid base64: ' + (error as any).message)\n  }\n  const dlen = data.length\n  if (dlen < 99 || dlen > 65603) throw new Error('invalid data length: ' + dlen)\n  const vers = data[0]\n  if (vers !== 2) throw new Error('unknown encryption version ' + vers)\n  return {\n    nonce: data.subarray(1, 33),\n    ciphertext: data.subarray(33, -32),\n    mac: data.subarray(-32),\n  }\n}\n\nexport function encrypt(plaintext: string, conversationKey: Uint8Array, nonce: Uint8Array = randomBytes(32)): string {\n  const { chacha_key, chacha_nonce, hmac_key } = getMessageKeys(conversationKey, nonce)\n  const padded = pad(plaintext)\n  const ciphertext = chacha20(chacha_key, chacha_nonce, padded)\n  const mac = hmacAad(hmac_key, ciphertext, nonce)\n  return base64.encode(concatBytes(new Uint8Array([2]), nonce, ciphertext, mac))\n}\n\nexport function decrypt(payload: string, conversationKey: Uint8Array): string {\n  const { nonce, ciphertext, mac } = decodePayload(payload)\n  const { chacha_key, chacha_nonce, hmac_key } = getMessageKeys(conversationKey, nonce)\n  const calculatedMac = hmacAad(hmac_key, ciphertext, nonce)\n  if (!equalBytes(calculatedMac, mac)) throw new Error('invalid MAC')\n  const padded = chacha20(chacha_key, chacha_nonce, ciphertext)\n  return unpad(padded)\n}\n\nexport const v2 = {\n  utils: {\n    getConversationKey,\n    calcPaddedLen,\n  },\n  encrypt,\n  decrypt,\n}\n", "import { ProfilePointer } from './nip19.ts'\n\nexport type Nip05 = `${string}@${string}`\n\n/**\n * NIP-05 regex. The localpart is optional, and should be assumed to be `_` otherwise.\n *\n * - 0: full match\n * - 1: name (optional)\n * - 2: domain\n */\nexport const NIP05_REGEX = /^(?:([\\w.+-]+)@)?([\\w_-]+(\\.[\\w_-]+)+)$/\nexport const isNip05 = (value?: string | null): value is Nip05 => NIP05_REGEX.test(value || '')\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nlet _fetch: any\n\ntry {\n  _fetch = fetch\n} catch (_) {\n  null\n}\n\nexport function useFetchImplementation(fetchImplementation: unknown) {\n  _fetch = fetchImplementation\n}\n\nexport async function searchDomain(domain: string, query = ''): Promise<{ [name: string]: string }> {\n  try {\n    const url = `https://${domain}/.well-known/nostr.json?name=${query}`\n    const res = await _fetch(url, { redirect: 'manual' })\n    if (res.status !== 200) {\n      throw Error('Wrong response code')\n    }\n    const json = await res.json()\n    return json.names\n  } catch (_) {\n    return {}\n  }\n}\n\nexport async function queryProfile(fullname: string): Promise<ProfilePointer | null> {\n  const match = fullname.match(NIP05_REGEX)\n  if (!match) return null\n\n  const [, name = '_', domain] = match\n\n  try {\n    const url = `https://${domain}/.well-known/nostr.json?name=${name}`\n    const res = await _fetch(url, { redirect: 'manual' })\n    if (res.status !== 200) {\n      throw Error('Wrong response code')\n    }\n    const json = await res.json()\n\n    const pubkey = json.names[name]\n    return pubkey ? { pubkey, relays: json.relays?.[pubkey] } : null\n  } catch (_e) {\n    return null\n  }\n}\n\nexport async function isValid(pubkey: string, nip05: Nip05): Promise<boolean> {\n  const res = await queryProfile(nip05)\n  return res ? res.pubkey === pubkey : false\n}\n", "import { NostrEvent, validateEvent } from './pure.ts'\n\n/** Events are **regular**, which means they're all expected to be stored by relays. */\nexport function isRegularKind(kind: number): boolean {\n  return kind < 10000 && kind !== 0 && kind !== 3\n}\n\n/** Events are **replaceable**, which means that, for each combination of `pubkey` and `kind`, only the latest event is expected to (SHOULD) be stored by relays, older versions are expected to be discarded. */\nexport function isReplaceableKind(kind: number): boolean {\n  return kind === 0 || kind === 3 || (10000 <= kind && kind < 20000)\n}\n\n/** Events are **ephemeral**, which means they are not expected to be stored by relays. */\nexport function isEphemeralKind(kind: number): boolean {\n  return 20000 <= kind && kind < 30000\n}\n\n/** Events are **addressable**, which means that, for each combination of `pubkey`, `kind` and the `d` tag, only the latest event is expected to be stored by relays, older versions are expected to be discarded. */\nexport function isAddressableKind(kind: number): boolean {\n  return 30000 <= kind && kind < 40000\n}\n\n/** Classification of the event kind. */\nexport type KindClassification = 'regular' | 'replaceable' | 'ephemeral' | 'parameterized' | 'unknown'\n\n/** Determine the classification of this kind of event if known, or `unknown`. */\nexport function classifyKind(kind: number): KindClassification {\n  if (isRegularKind(kind)) return 'regular'\n  if (isReplaceableKind(kind)) return 'replaceable'\n  if (isEphemeralKind(kind)) return 'ephemeral'\n  if (isAddressableKind(kind)) return 'parameterized'\n  return 'unknown'\n}\n\nexport function isKind<T extends number>(event: unknown, kind: T | Array<T>): event is NostrEvent & { kind: T } {\n  const kindAsArray: number[] = kind instanceof Array ? kind : [kind]\n  return (validateEvent(event) && kindAsArray.includes(event.kind)) || false\n}\n\nexport const Metadata = 0\nexport type Metadata = typeof Metadata\nexport const ShortTextNote = 1\nexport type ShortTextNote = typeof ShortTextNote\nexport const RecommendRelay = 2\nexport type RecommendRelay = typeof RecommendRelay\nexport const Contacts = 3\nexport type Contacts = typeof Contacts\nexport const EncryptedDirectMessage = 4\nexport type EncryptedDirectMessage = typeof EncryptedDirectMessage\nexport const EventDeletion = 5\nexport type EventDeletion = typeof EventDeletion\nexport const Repost = 6\nexport type Repost = typeof Repost\nexport const Reaction = 7\nexport type Reaction = typeof Reaction\nexport const BadgeAward = 8\nexport type BadgeAward = typeof BadgeAward\nexport const ChatMessage = 9\nexport type ChatMessage = typeof ChatMessage\nexport const ForumThread = 11\nexport type ForumThread = typeof ForumThread\nexport const Seal = 13\nexport type Seal = typeof Seal\nexport const PrivateDirectMessage = 14\nexport type PrivateDirectMessage = typeof PrivateDirectMessage\nexport const FileMessage = 15\nexport type FileMessage = typeof FileMessage\nexport const GenericRepost = 16\nexport type GenericRepost = typeof GenericRepost\nexport const Photo = 20\nexport type Photo = typeof Photo\nexport const NormalVideo = 21\nexport type NormalVideo = typeof NormalVideo\nexport const ShortVideo = 22\nexport type ShortVideo = typeof ShortVideo\nexport const ChannelCreation = 40\nexport type ChannelCreation = typeof ChannelCreation\nexport const ChannelMetadata = 41\nexport type ChannelMetadata = typeof ChannelMetadata\nexport const ChannelMessage = 42\nexport type ChannelMessage = typeof ChannelMessage\nexport const ChannelHideMessage = 43\nexport type ChannelHideMessage = typeof ChannelHideMessage\nexport const ChannelMuteUser = 44\nexport type ChannelMuteUser = typeof ChannelMuteUser\nexport const OpenTimestamps = 1040\nexport type OpenTimestamps = typeof OpenTimestamps\nexport const GiftWrap = 1059\nexport type GiftWrap = typeof GiftWrap\nexport const Poll = 1068\nexport type Poll = typeof Poll\nexport const FileMetadata = 1063\nexport type FileMetadata = typeof FileMetadata\nexport const Comment = 1111\nexport type Comment = typeof Comment\nexport const LiveChatMessage = 1311\nexport type LiveChatMessage = typeof LiveChatMessage\nexport const Voice = 1222\nexport type Voice = typeof Voice\nexport const VoiceComment = 1244\nexport type VoiceComment = typeof VoiceComment\nexport const ProblemTracker = 1971\nexport type ProblemTracker = typeof ProblemTracker\nexport const Report = 1984\nexport type Report = typeof Report\nexport const Reporting = 1984\nexport type Reporting = typeof Reporting\nexport const Label = 1985\nexport type Label = typeof Label\nexport const CommunityPostApproval = 4550\nexport type CommunityPostApproval = typeof CommunityPostApproval\nexport const JobRequest = 5999\nexport type JobRequest = typeof JobRequest\nexport const JobResult = 6999\nexport type JobResult = typeof JobResult\nexport const JobFeedback = 7000\nexport type JobFeedback = typeof JobFeedback\nexport const ZapGoal = 9041\nexport type ZapGoal = typeof ZapGoal\nexport const ZapRequest = 9734\nexport type ZapRequest = typeof ZapRequest\nexport const Zap = 9735\nexport type Zap = typeof Zap\nexport const Highlights = 9802\nexport type Highlights = typeof Highlights\nexport const PollResponse = 1018\nexport type PollResponse = typeof PollResponse\nexport const Mutelist = 10000\nexport type Mutelist = typeof Mutelist\nexport const Pinlist = 10001\nexport type Pinlist = typeof Pinlist\nexport const RelayList = 10002\nexport type RelayList = typeof RelayList\nexport const BookmarkList = 10003\nexport type BookmarkList = typeof BookmarkList\nexport const CommunitiesList = 10004\nexport type CommunitiesList = typeof CommunitiesList\nexport const PublicChatsList = 10005\nexport type PublicChatsList = typeof PublicChatsList\nexport const BlockedRelaysList = 10006\nexport type BlockedRelaysList = typeof BlockedRelaysList\nexport const SearchRelaysList = 10007\nexport type SearchRelaysList = typeof SearchRelaysList\nexport const FavoriteRelays = 10012\nexport type FavoriteRelays = typeof FavoriteRelays\nexport const InterestsList = 10015\nexport type InterestsList = typeof InterestsList\nexport const UserEmojiList = 10030\nexport type UserEmojiList = typeof UserEmojiList\nexport const DirectMessageRelaysList = 10050\nexport type DirectMessageRelaysList = typeof DirectMessageRelaysList\nexport const FileServerPreference = 10096\nexport type FileServerPreference = typeof FileServerPreference\nexport const BlossomServerList = 10063\nexport type BlossomServerList = typeof BlossomServerList\nexport const NWCWalletInfo = 13194\nexport type NWCWalletInfo = typeof NWCWalletInfo\nexport const LightningPubRPC = 21000\nexport type LightningPubRPC = typeof LightningPubRPC\nexport const ClientAuth = 22242\nexport type ClientAuth = typeof ClientAuth\nexport const NWCWalletRequest = 23194\nexport type NWCWalletRequest = typeof NWCWalletRequest\nexport const NWCWalletResponse = 23195\nexport type NWCWalletResponse = typeof NWCWalletResponse\nexport const NostrConnect = 24133\nexport type NostrConnect = typeof NostrConnect\nexport const HTTPAuth = 27235\nexport type HTTPAuth = typeof HTTPAuth\nexport const Followsets = 30000\nexport type Followsets = typeof Followsets\nexport const Genericlists = 30001\nexport type Genericlists = typeof Genericlists\nexport const Relaysets = 30002\nexport type Relaysets = typeof Relaysets\nexport const Bookmarksets = 30003\nexport type Bookmarksets = typeof Bookmarksets\nexport const Curationsets = 30004\nexport type Curationsets = typeof Curationsets\nexport const ProfileBadges = 30008\nexport type ProfileBadges = typeof ProfileBadges\nexport const BadgeDefinition = 30009\nexport type BadgeDefinition = typeof BadgeDefinition\nexport const Interestsets = 30015\nexport type Interestsets = typeof Interestsets\nexport const CreateOrUpdateStall = 30017\nexport type CreateOrUpdateStall = typeof CreateOrUpdateStall\nexport const CreateOrUpdateProduct = 30018\nexport type CreateOrUpdateProduct = typeof CreateOrUpdateProduct\nexport const LongFormArticle = 30023\nexport type LongFormArticle = typeof LongFormArticle\nexport const DraftLong = 30024\nexport type DraftLong = typeof DraftLong\nexport const Emojisets = 30030\nexport type Emojisets = typeof Emojisets\nexport const Application = 30078\nexport type Application = typeof Application\nexport const LiveEvent = 30311\nexport type LiveEvent = typeof LiveEvent\nexport const UserStatuses = 30315\nexport type UserStatuses = typeof UserStatuses\nexport const ClassifiedListing = 30402\nexport type ClassifiedListing = typeof ClassifiedListing\nexport const DraftClassifiedListing = 30403\nexport type DraftClassifiedListing = typeof DraftClassifiedListing\nexport const Date = 31922\nexport type Date = typeof Date\nexport const Time = 31923\nexport type Time = typeof Time\nexport const Calendar = 31924\nexport type Calendar = typeof Calendar\nexport const CalendarEventRSVP = 31925\nexport type CalendarEventRSVP = typeof CalendarEventRSVP\nexport const RelayReview = 31987\nexport type RelayReview = typeof RelayReview\nexport const Handlerrecommendation = 31989\nexport type Handlerrecommendation = typeof Handlerrecommendation\nexport const Handlerinformation = 31990\nexport type Handlerinformation = typeof Handlerinformation\nexport const CommunityDefinition = 34550\nexport type CommunityDefinition = typeof CommunityDefinition\nexport const GroupMetadata = 39000\nexport type GroupMetadata = typeof GroupMetadata\n", "import { Event } from './core.ts'\nimport { isAddressableKind, isReplaceableKind } from './kinds.ts'\n\nexport type Filter = {\n  ids?: string[]\n  kinds?: number[]\n  authors?: string[]\n  since?: number\n  until?: number\n  limit?: number\n  search?: string\n  [key: `#${string}`]: string[] | undefined\n}\n\nexport function matchFilter(filter: Filter, event: Event): boolean {\n  if (filter.ids && filter.ids.indexOf(event.id) === -1) {\n    return false\n  }\n  if (filter.kinds && filter.kinds.indexOf(event.kind) === -1) {\n    return false\n  }\n  if (filter.authors && filter.authors.indexOf(event.pubkey) === -1) {\n    return false\n  }\n\n  for (let f in filter) {\n    if (f[0] === '#') {\n      let tagName = f.slice(1)\n      let values = filter[`#${tagName}`]\n      if (values && !event.tags.find(([t, v]) => t === f.slice(1) && values!.indexOf(v) !== -1)) return false\n    }\n  }\n\n  if (filter.since && event.created_at < filter.since) return false\n  if (filter.until && event.created_at > filter.until) return false\n\n  return true\n}\n\nexport function matchFilters(filters: Filter[], event: Event): boolean {\n  for (let i = 0; i < filters.length; i++) {\n    if (matchFilter(filters[i], event)) {\n      return true\n    }\n  }\n  return false\n}\n\nexport function mergeFilters(...filters: Filter[]): Filter {\n  let result: Filter = {}\n  for (let i = 0; i < filters.length; i++) {\n    let filter = filters[i]\n    Object.entries(filter).forEach(([property, values]) => {\n      if (property === 'kinds' || property === 'ids' || property === 'authors' || property[0] === '#') {\n        // @ts-ignore\n        result[property] = result[property] || []\n        // @ts-ignore\n        for (let v = 0; v < values.length; v++) {\n          // @ts-ignore\n          let value = values[v]\n          // @ts-ignore\n          if (!result[property].includes(value)) result[property].push(value)\n        }\n      }\n    })\n\n    if (filter.limit && (!result.limit || filter.limit > result.limit)) result.limit = filter.limit\n    if (filter.until && (!result.until || filter.until > result.until)) result.until = filter.until\n    if (filter.since && (!result.since || filter.since < result.since)) result.since = filter.since\n  }\n\n  return result\n}\n\n/**\n * Calculate the intrinsic limit of a filter.\n * This function returns a positive integer, or `Infinity` if there is no intrinsic limit.\n */\nexport function getFilterLimit(filter: Filter): number {\n  if (filter.ids && !filter.ids.length) return 0\n  if (filter.kinds && !filter.kinds.length) return 0\n  if (filter.authors && !filter.authors.length) return 0\n\n  for (const [key, value] of Object.entries(filter)) {\n    if (key[0] === '#' && Array.isArray(value) && !value.length) return 0\n  }\n\n  return Math.min(\n    // The `limit` property creates an artificial limit.\n    Math.max(0, filter.limit ?? Infinity),\n\n    // There can only be one event per `id`.\n    filter.ids?.length ?? Infinity,\n\n    // Replaceable events are limited by the number of authors and kinds.\n    filter.authors?.length && filter.kinds?.every(kind => isReplaceableKind(kind))\n      ? filter.authors.length * filter.kinds.length\n      : Infinity,\n\n    // Parameterized replaceable events are limited by the number of authors, kinds, and \"d\" tags.\n    filter.authors?.length && filter.kinds?.every(kind => isAddressableKind(kind)) && filter['#d']?.length\n      ? filter.authors.length * filter.kinds.length * filter['#d'].length\n      : Infinity,\n  )\n}\n", "export function getHex64(json: string, field: string): string {\n  let len = field.length + 3\n  let idx = json.indexOf(`\"${field}\":`) + len\n  let s = json.slice(idx).indexOf(`\"`) + idx + 1\n  return json.slice(s, s + 64)\n}\n\nexport function getInt(json: string, field: string): number {\n  let len = field.length\n  let idx = json.indexOf(`\"${field}\":`) + len + 3\n  let sliced = json.slice(idx)\n  let end = Math.min(sliced.indexOf(','), sliced.indexOf('}'))\n  return parseInt(sliced.slice(0, end), 10)\n}\n\nexport function getSubscriptionId(json: string): string | null {\n  let idx = json.slice(0, 22).indexOf(`\"EVENT\"`)\n  if (idx === -1) return null\n\n  let pstart = json.slice(idx + 7 + 1).indexOf(`\"`)\n  if (pstart === -1) return null\n  let start = idx + 7 + 1 + pstart\n\n  let pend = json.slice(start + 1, 80).indexOf(`\"`)\n  if (pend === -1) return null\n  let end = start + 1 + pend\n\n  return json.slice(start + 1, end)\n}\n\nexport function matchEventId(json: string, id: string): boolean {\n  return id === getHex64(json, 'id')\n}\n\nexport function matchEventPubkey(json: string, pubkey: string): boolean {\n  return pubkey === getHex64(json, 'pubkey')\n}\n\nexport function matchEventKind(json: string, kind: number): boolean {\n  return kind === getInt(json, 'kind')\n}\n", "import { EventTemplate } from './core.ts'\nimport { ClientAuth } from './kinds.ts'\n\n/**\n * creates an EventTemplate for an AUTH event to be signed.\n */\nexport function makeAuthEvent(relayURL: string, challenge: string): EventTemplate {\n  return {\n    kind: ClientAuth,\n    created_at: Math.floor(Date.now() / 1000),\n    tags: [\n      ['relay', relayURL],\n      ['challenge', challenge],\n    ],\n    content: '',\n  }\n}\n", "import { verifiedSymbol, type Event, type Nostr, VerifiedEvent } from './core.ts'\n\nexport async function yieldThread() {\n  return new Promise<void>((resolve, reject) => {\n    try {\n      // Check if MessageChannel is available\n      if (typeof MessageChannel !== 'undefined') {\n        const ch = new MessageChannel()\n        const handler = () => {\n          // @ts-ignore (typescript thinks this property should be called `removeListener`, but in fact it's `removeEventListener`)\n          ch.port1.removeEventListener('message', handler)\n          resolve()\n        }\n        // @ts-ignore (typescript thinks this property should be called `addListener`, but in fact it's `addEventListener`)\n        ch.port1.addEventListener('message', handler)\n        ch.port2.postMessage(0)\n        ch.port1.start()\n      } else {\n        if (typeof setImmediate !== 'undefined') {\n          setImmediate(resolve)\n        } else if (typeof setTimeout !== 'undefined') {\n          setTimeout(resolve, 0)\n        } else {\n          // Last resort - resolve immediately\n          resolve()\n        }\n      }\n    } catch (e) {\n      console.error('during yield: ', e)\n      reject(e)\n    }\n  })\n}\n\nexport const alwaysTrue: Nostr['verifyEvent'] = (t: Event): t is VerifiedEvent => {\n  t[verifiedSymbol] = true\n  return true\n}\n", "/* global WebSocket */\n\nimport type { Event, EventTemplate, VerifiedEvent, Nostr, NostrEvent } from './core.ts'\nimport { matchFilters, type Filter } from './filter.ts'\nimport { getHex64, getSubscriptionId } from './fakejson.ts'\nimport { Queue, normalizeURL } from './utils.ts'\nimport { makeAuthEvent } from './nip42.ts'\nimport { yieldThread } from './helpers.ts'\n\ntype RelayWebSocket = WebSocket & {\n  ping?(): void\n  on?(event: 'pong', listener: () => void): any\n}\n\nexport type AbstractRelayConstructorOptions = {\n  verifyEvent: Nostr['verifyEvent']\n  websocketImplementation?: typeof WebSocket\n  enablePing?: boolean\n  enableReconnect?: boolean\n}\n\nexport class SendingOnClosedConnection extends Error {\n  constructor(message: string, relay: string) {\n    super(`Tried to send message '${message} on a closed connection to ${relay}.`)\n    this.name = 'SendingOnClosedConnection'\n  }\n}\n\nexport class AbstractRelay {\n  public readonly url: string\n  private _connected: boolean = false\n\n  public onclose: (() => void) | null = null\n  public onnotice: (msg: string) => void = msg => console.debug(`NOTICE from ${this.url}: ${msg}`)\n  public onauth: undefined | ((evt: EventTemplate) => Promise<VerifiedEvent>)\n\n  public baseEoseTimeout: number = 4400\n  public publishTimeout: number = 4400\n  public pingFrequency: number = 29000\n  public pingTimeout: number = 20000\n  public resubscribeBackoff: number[] = [10000, 10000, 10000, 20000, 20000, 30000, 60000]\n  public openSubs: Map<string, Subscription> = new Map()\n  public enablePing: boolean | undefined\n  public enableReconnect: boolean\n  private reconnectTimeoutHandle: ReturnType<typeof setTimeout> | undefined\n  private pingIntervalHandle: ReturnType<typeof setInterval> | undefined\n  private reconnectAttempts: number = 0\n  private closedIntentionally: boolean = false\n\n  private connectionPromise: Promise<void> | undefined\n  private openCountRequests = new Map<string, CountResolver>()\n  private openEventPublishes = new Map<string, EventPublishResolver>()\n  private ws: RelayWebSocket | undefined\n  private incomingMessageQueue = new Queue<string>()\n  private queueRunning = false\n  private challenge: string | undefined\n  private authPromise: Promise<string> | undefined\n  private serial: number = 0\n  private verifyEvent: Nostr['verifyEvent']\n\n  private _WebSocket: typeof WebSocket\n\n  constructor(url: string, opts: AbstractRelayConstructorOptions) {\n    this.url = normalizeURL(url)\n    this.verifyEvent = opts.verifyEvent\n    this._WebSocket = opts.websocketImplementation || WebSocket\n    this.enablePing = opts.enablePing\n    this.enableReconnect = opts.enableReconnect || false\n  }\n\n  static async connect(\n    url: string,\n    opts: AbstractRelayConstructorOptions & Parameters<AbstractRelay['connect']>[0],\n  ): Promise<AbstractRelay> {\n    const relay = new AbstractRelay(url, opts)\n    await relay.connect(opts)\n    return relay\n  }\n\n  private closeAllSubscriptions(reason: string) {\n    for (let [_, sub] of this.openSubs) {\n      sub.close(reason)\n    }\n    this.openSubs.clear()\n\n    for (let [_, ep] of this.openEventPublishes) {\n      ep.reject(new Error(reason))\n    }\n    this.openEventPublishes.clear()\n\n    for (let [_, cr] of this.openCountRequests) {\n      cr.reject(new Error(reason))\n    }\n    this.openCountRequests.clear()\n  }\n\n  public get connected(): boolean {\n    return this._connected\n  }\n\n  private async reconnect(): Promise<void> {\n    const backoff = this.resubscribeBackoff[Math.min(this.reconnectAttempts, this.resubscribeBackoff.length - 1)]\n    this.reconnectAttempts++\n\n    this.reconnectTimeoutHandle = setTimeout(async () => {\n      try {\n        await this.connect()\n      } catch (err) {\n        // this will be called again through onclose/onerror\n      }\n    }, backoff)\n  }\n\n  private handleHardClose(reason: string) {\n    if (this.pingIntervalHandle) {\n      clearInterval(this.pingIntervalHandle)\n      this.pingIntervalHandle = undefined\n    }\n\n    this._connected = false\n    this.connectionPromise = undefined\n\n    const wasIntentional = this.closedIntentionally\n    this.closedIntentionally = false // reset for next time\n\n    this.onclose?.()\n\n    if (this.enableReconnect && !wasIntentional) {\n      this.reconnect()\n    } else {\n      this.closeAllSubscriptions(reason)\n    }\n  }\n\n  public async connect(opts?: { timeout?: number; abort?: AbortSignal }): Promise<void> {\n    let connectionTimeoutHandle: ReturnType<typeof setTimeout> | undefined\n\n    if (this.connectionPromise) return this.connectionPromise\n\n    this.challenge = undefined\n    this.authPromise = undefined\n    this.connectionPromise = new Promise((resolve, reject) => {\n      if (opts?.timeout) {\n        connectionTimeoutHandle = setTimeout(() => {\n          reject('connection timed out')\n          this.connectionPromise = undefined\n          this.onclose?.()\n          this.closeAllSubscriptions('relay connection timed out')\n        }, opts.timeout)\n      }\n\n      if (opts?.abort) {\n        opts.abort.onabort = reject\n      }\n\n      try {\n        this.ws = new this._WebSocket(this.url)\n      } catch (err) {\n        clearTimeout(connectionTimeoutHandle)\n        reject(err)\n        return\n      }\n\n      this.ws.onopen = () => {\n        if (this.reconnectTimeoutHandle) {\n          clearTimeout(this.reconnectTimeoutHandle)\n          this.reconnectTimeoutHandle = undefined\n        }\n        clearTimeout(connectionTimeoutHandle)\n        this._connected = true\n\n        const isReconnection = this.reconnectAttempts > 0\n        this.reconnectAttempts = 0\n\n        // resubscribe to all open subscriptions\n        for (const sub of this.openSubs.values()) {\n          sub.eosed = false\n          if (isReconnection) {\n            for (let f = 0; f < sub.filters.length; f++) {\n              if (sub.lastEmitted) {\n                sub.filters[f].since = sub.lastEmitted + 1\n              }\n            }\n          }\n          sub.fire()\n        }\n\n        if (this.enablePing) {\n          this.pingIntervalHandle = setInterval(() => this.pingpong(), this.pingFrequency)\n        }\n        resolve()\n      }\n\n      this.ws.onerror = ev => {\n        clearTimeout(connectionTimeoutHandle)\n        reject((ev as any).message || 'websocket error')\n        this.handleHardClose('relay connection errored')\n      }\n\n      this.ws.onclose = ev => {\n        clearTimeout(connectionTimeoutHandle)\n        reject((ev as any).message || 'websocket closed')\n        this.handleHardClose('relay connection closed')\n      }\n\n      this.ws.onmessage = this._onmessage.bind(this)\n    })\n\n    return this.connectionPromise\n  }\n\n  private waitForPingPong() {\n    return new Promise(resolve => {\n      // listen for pong\n      ;(this.ws as any).once('pong', () => resolve(true))\n      // send a ping\n      this.ws!.ping!()\n    })\n  }\n\n  private waitForDummyReq() {\n    return new Promise((resolve, reject) => {\n      if (!this.connectionPromise) return reject(new Error(`no connection to ${this.url}, can't ping`))\n\n      // make a dummy request with expected empty eose reply\n      // [\"REQ\", \"_\", {\"ids\":[\"aaaa...aaaa\"], \"limit\": 0}]\n      try {\n        const sub = this.subscribe(\n          [{ ids: ['aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'], limit: 0 }],\n          {\n            label: 'forced-ping',\n            oneose: () => {\n              resolve(true)\n              sub.close()\n            },\n            onclose() {\n              // if we get a CLOSED it's because the relay is alive\n              resolve(true)\n            },\n            eoseTimeout: this.pingTimeout + 1000,\n          },\n        )\n      } catch (err) {\n        reject(err)\n      }\n    })\n  }\n\n  // nodejs requires this magic here to ensure connections are closed when internet goes off and stuff\n  // in browsers it's done automatically. see https://github.com/nbd-wtf/nostr-tools/issues/491\n  private async pingpong() {\n    // if the websocket is connected\n    if (this.ws?.readyState === 1) {\n      // wait for either a ping-pong reply or a timeout\n      const result = await Promise.any([\n        // browsers don't have ping so use a dummy req\n        this.ws && this.ws.ping && (this.ws as any).once ? this.waitForPingPong() : this.waitForDummyReq(),\n        new Promise(res => setTimeout(() => res(false), this.pingTimeout)),\n      ])\n\n      if (!result) {\n        // pingpong closing socket\n        if (this.ws?.readyState === this._WebSocket.OPEN) {\n          this.ws?.close()\n        }\n      }\n    }\n  }\n\n  private async runQueue() {\n    this.queueRunning = true\n    while (true) {\n      if (false === this.handleNext()) {\n        break\n      }\n      await yieldThread()\n    }\n    this.queueRunning = false\n  }\n\n  private handleNext(): undefined | false {\n    const json = this.incomingMessageQueue.dequeue()\n    if (!json) {\n      return false\n    }\n\n    // shortcut EVENT sub\n    const subid = getSubscriptionId(json)\n    if (subid) {\n      const so = this.openSubs.get(subid as string)\n      if (!so) {\n        // this is an EVENT message, but for a subscription we don't have, so just stop here\n        return\n      }\n\n      // this will be called only when this message is a EVENT message for a subscription we have\n      // we do this before parsing the JSON to not have to do that for duplicate events\n      //   since JSON parsing is slow\n      const id = getHex64(json, 'id')\n      const alreadyHave = so.alreadyHaveEvent?.(id)\n\n      // notify any interested client that the relay has this event\n      // (do this after alreadyHaveEvent() because the client may rely on this to answer that)\n      so.receivedEvent?.(this, id)\n\n      if (alreadyHave) {\n        // if we had already seen this event we can just stop here\n        return\n      }\n    }\n\n    try {\n      let data = JSON.parse(json)\n      // we won't do any checks against the data since all failures (i.e. invalid messages from relays)\n      // will naturally be caught by the encompassing try..catch block\n\n      switch (data[0]) {\n        case 'EVENT': {\n          const so = this.openSubs.get(data[1] as string) as Subscription\n          const event = data[2] as NostrEvent\n          if (this.verifyEvent(event) && matchFilters(so.filters, event)) {\n            so.onevent(event)\n          }\n          if (!so.lastEmitted || so.lastEmitted < event.created_at) so.lastEmitted = event.created_at\n          return\n        }\n        case 'COUNT': {\n          const id: string = data[1]\n          const payload = data[2] as { count: number }\n          const cr = this.openCountRequests.get(id) as CountResolver\n          if (cr) {\n            cr.resolve(payload.count)\n            this.openCountRequests.delete(id)\n          }\n          return\n        }\n        case 'EOSE': {\n          const so = this.openSubs.get(data[1] as string)\n          if (!so) return\n          so.receivedEose()\n          return\n        }\n        case 'OK': {\n          const id: string = data[1]\n          const ok: boolean = data[2]\n          const reason: string = data[3]\n          const ep = this.openEventPublishes.get(id) as EventPublishResolver\n          if (ep) {\n            clearTimeout(ep.timeout)\n            if (ok) ep.resolve(reason)\n            else ep.reject(new Error(reason))\n            this.openEventPublishes.delete(id)\n          }\n          return\n        }\n        case 'CLOSED': {\n          const id: string = data[1]\n          const so = this.openSubs.get(id)\n          if (!so) return\n          so.closed = true\n          so.close(data[2] as string)\n          return\n        }\n        case 'NOTICE': {\n          this.onnotice(data[1] as string)\n          return\n        }\n        case 'AUTH': {\n          this.challenge = data[1] as string\n          if (this.onauth) {\n            this.auth(this.onauth)\n          }\n          return\n        }\n        default: {\n          const so = this.openSubs.get(data[1])\n          so?.oncustom?.(data)\n          return\n        }\n      }\n    } catch (err) {\n      return\n    }\n  }\n\n  public async send(message: string) {\n    if (!this.connectionPromise) throw new SendingOnClosedConnection(message, this.url)\n\n    this.connectionPromise.then(() => {\n      this.ws?.send(message)\n    })\n  }\n\n  public async auth(signAuthEvent: (evt: EventTemplate) => Promise<VerifiedEvent>): Promise<string> {\n    const challenge = this.challenge\n    if (!challenge) throw new Error(\"can't perform auth, no challenge was received\")\n    if (this.authPromise) return this.authPromise\n\n    this.authPromise = new Promise<string>(async (resolve, reject) => {\n      try {\n        let evt = await signAuthEvent(makeAuthEvent(this.url, challenge))\n        let timeout = setTimeout(() => {\n          let ep = this.openEventPublishes.get(evt.id) as EventPublishResolver\n          if (ep) {\n            ep.reject(new Error('auth timed out'))\n            this.openEventPublishes.delete(evt.id)\n          }\n        }, this.publishTimeout)\n        this.openEventPublishes.set(evt.id, { resolve, reject, timeout })\n        this.send('[\"AUTH\",' + JSON.stringify(evt) + ']')\n      } catch (err) {\n        console.warn('subscribe auth function failed:', err)\n      }\n    })\n    return this.authPromise\n  }\n\n  public async publish(event: Event): Promise<string> {\n    const ret = new Promise<string>((resolve, reject) => {\n      const timeout = setTimeout(() => {\n        const ep = this.openEventPublishes.get(event.id) as EventPublishResolver\n        if (ep) {\n          ep.reject(new Error('publish timed out'))\n          this.openEventPublishes.delete(event.id)\n        }\n      }, this.publishTimeout)\n      this.openEventPublishes.set(event.id, { resolve, reject, timeout })\n    })\n    this.send('[\"EVENT\",' + JSON.stringify(event) + ']')\n    return ret\n  }\n\n  public async count(filters: Filter[], params: { id?: string | null }): Promise<number> {\n    this.serial++\n    const id = params?.id || 'count:' + this.serial\n    const ret = new Promise<number>((resolve, reject) => {\n      this.openCountRequests.set(id, { resolve, reject })\n    })\n    this.send('[\"COUNT\",\"' + id + '\",' + JSON.stringify(filters).substring(1))\n    return ret\n  }\n\n  public subscribe(\n    filters: Filter[],\n    params: Partial<SubscriptionParams> & { label?: string; id?: string },\n  ): Subscription {\n    const sub = this.prepareSubscription(filters, params)\n    sub.fire()\n\n    if (params.abort) {\n      params.abort.onabort = () => sub.close(String(params.abort!.reason || '<aborted>'))\n    }\n\n    return sub\n  }\n\n  public prepareSubscription(\n    filters: Filter[],\n    params: Partial<SubscriptionParams> & { label?: string; id?: string },\n  ): Subscription {\n    this.serial++\n    const id = params.id || (params.label ? params.label + ':' : 'sub:') + this.serial\n    const subscription = new Subscription(this, id, filters, params)\n    this.openSubs.set(id, subscription)\n    return subscription\n  }\n\n  public close() {\n    this.closedIntentionally = true\n    if (this.reconnectTimeoutHandle) {\n      clearTimeout(this.reconnectTimeoutHandle)\n      this.reconnectTimeoutHandle = undefined\n    }\n    if (this.pingIntervalHandle) {\n      clearInterval(this.pingIntervalHandle)\n      this.pingIntervalHandle = undefined\n    }\n    this.closeAllSubscriptions('relay connection closed by us')\n    this._connected = false\n    this.onclose?.()\n    if (this.ws?.readyState === this._WebSocket.OPEN) {\n      this.ws?.close()\n    }\n  }\n\n  // this is the function assigned to this.ws.onmessage\n  // it's exposed for testing and debugging purposes\n  public _onmessage(ev: MessageEvent<any>) {\n    this.incomingMessageQueue.enqueue(ev.data as string)\n    if (!this.queueRunning) {\n      this.runQueue()\n    }\n  }\n}\n\nexport class Subscription {\n  public readonly relay: AbstractRelay\n  public readonly id: string\n\n  public lastEmitted: number | undefined\n  public closed: boolean = false\n  public eosed: boolean = false\n  public filters: Filter[]\n  public alreadyHaveEvent: ((id: string) => boolean) | undefined\n  public receivedEvent: ((relay: AbstractRelay, id: string) => void) | undefined\n\n  public onevent: (evt: Event) => void\n  public oneose: (() => void) | undefined\n  public onclose: ((reason: string) => void) | undefined\n\n  // will get any messages that have this subscription id as their second item and are not default standard\n  public oncustom: ((msg: string[]) => void) | undefined\n\n  public eoseTimeout: number\n  private eoseTimeoutHandle: ReturnType<typeof setTimeout> | undefined\n\n  constructor(relay: AbstractRelay, id: string, filters: Filter[], params: SubscriptionParams) {\n    if (filters.length === 0) throw new Error(\"subscription can't be created with zero filters\")\n\n    this.relay = relay\n    this.filters = filters\n    this.id = id\n    this.alreadyHaveEvent = params.alreadyHaveEvent\n    this.receivedEvent = params.receivedEvent\n    this.eoseTimeout = params.eoseTimeout || relay.baseEoseTimeout\n\n    this.oneose = params.oneose\n    this.onclose = params.onclose\n    this.onevent =\n      params.onevent ||\n      (event => {\n        console.warn(\n          `onevent() callback not defined for subscription '${this.id}' in relay ${this.relay.url}. event received:`,\n          event,\n        )\n      })\n  }\n\n  public fire() {\n    this.relay.send('[\"REQ\",\"' + this.id + '\",' + JSON.stringify(this.filters).substring(1))\n\n    // only now we start counting the eoseTimeout\n    this.eoseTimeoutHandle = setTimeout(this.receivedEose.bind(this), this.eoseTimeout)\n  }\n\n  public receivedEose() {\n    if (this.eosed) return\n    clearTimeout(this.eoseTimeoutHandle)\n    this.eosed = true\n    this.oneose?.()\n  }\n\n  public close(reason: string = 'closed by caller') {\n    if (!this.closed && this.relay.connected) {\n      // if the connection was closed by the user calling .close() we will send a CLOSE message\n      // otherwise this._open will be already set to false so we will skip this\n      try {\n        this.relay.send('[\"CLOSE\",' + JSON.stringify(this.id) + ']')\n      } catch (err) {\n        if (err instanceof SendingOnClosedConnection) {\n          /* doesn't matter, it's ok */\n        } else {\n          throw err\n        }\n      }\n      this.closed = true\n    }\n    this.relay.openSubs.delete(this.id)\n    this.onclose?.(reason)\n  }\n}\n\nexport type SubscriptionParams = {\n  onevent?: (evt: Event) => void\n  oneose?: () => void\n  onclose?: (reason: string) => void\n  alreadyHaveEvent?: (id: string) => boolean\n  receivedEvent?: (relay: AbstractRelay, id: string) => void\n  eoseTimeout?: number\n  abort?: AbortSignal\n}\n\nexport type CountResolver = {\n  resolve: (count: number) => void\n  reject: (err: Error) => void\n}\n\nexport type EventPublishResolver = {\n  resolve: (reason: string) => void\n  reject: (err: Error) => void\n  timeout: ReturnType<typeof setTimeout>\n}\n", "/* global WebSocket */\n\nimport {\n  AbstractRelay as AbstractRelay,\n  SubscriptionParams,\n  Subscription,\n  type AbstractRelayConstructorOptions,\n} from './abstract-relay.ts'\nimport { normalizeURL } from './utils.ts'\n\nimport type { Event, EventTemplate, Nostr, VerifiedEvent } from './core.ts'\nimport { type Filter } from './filter.ts'\nimport { alwaysTrue } from './helpers.ts'\n\nexport type SubCloser = { close: (reason?: string) => void }\n\nexport type AbstractPoolConstructorOptions = AbstractRelayConstructorOptions & {\n  // automaticallyAuth takes a relay URL and should return null\n  // in case that relay shouldn't be authenticated against\n  // or a function to sign the AUTH event template otherwise (that function may still throw in case of failure)\n  automaticallyAuth?: (relayURL: string) => null | ((event: EventTemplate) => Promise<VerifiedEvent>)\n}\n\nexport type SubscribeManyParams = Omit<SubscriptionParams, 'onclose'> & {\n  maxWait?: number\n  abort?: AbortSignal\n  onclose?: (reasons: string[]) => void\n  onauth?: (event: EventTemplate) => Promise<VerifiedEvent>\n  id?: string\n  label?: string\n}\n\nexport class AbstractSimplePool {\n  protected relays: Map<string, AbstractRelay> = new Map()\n  public seenOn: Map<string, Set<AbstractRelay>> = new Map()\n  public trackRelays: boolean = false\n\n  public verifyEvent: Nostr['verifyEvent']\n  public enablePing: boolean | undefined\n  public enableReconnect: boolean\n  public automaticallyAuth?: (relayURL: string) => null | ((event: EventTemplate) => Promise<VerifiedEvent>)\n  public trustedRelayURLs: Set<string> = new Set()\n\n  private _WebSocket?: typeof WebSocket\n\n  constructor(opts: AbstractPoolConstructorOptions) {\n    this.verifyEvent = opts.verifyEvent\n    this._WebSocket = opts.websocketImplementation\n    this.enablePing = opts.enablePing\n    this.enableReconnect = opts.enableReconnect || false\n    this.automaticallyAuth = opts.automaticallyAuth\n  }\n\n  async ensureRelay(\n    url: string,\n    params?: {\n      connectionTimeout?: number\n      abort?: AbortSignal\n    },\n  ): Promise<AbstractRelay> {\n    url = normalizeURL(url)\n\n    let relay = this.relays.get(url)\n    if (!relay) {\n      relay = new AbstractRelay(url, {\n        verifyEvent: this.trustedRelayURLs.has(url) ? alwaysTrue : this.verifyEvent,\n        websocketImplementation: this._WebSocket,\n        enablePing: this.enablePing,\n        enableReconnect: this.enableReconnect,\n      })\n      relay.onclose = () => {\n        if (relay && !relay.enableReconnect) {\n          this.relays.delete(url)\n        }\n      }\n      this.relays.set(url, relay)\n    }\n\n    if (this.automaticallyAuth) {\n      const authSignerFn = this.automaticallyAuth(url)\n      if (authSignerFn) {\n        relay.onauth = authSignerFn\n      }\n    }\n\n    await relay.connect({\n      timeout: params?.connectionTimeout,\n      abort: params?.abort,\n    })\n\n    return relay\n  }\n\n  close(relays: string[]) {\n    relays.map(normalizeURL).forEach(url => {\n      this.relays.get(url)?.close()\n      this.relays.delete(url)\n    })\n  }\n\n  subscribe(relays: string[], filter: Filter, params: SubscribeManyParams): SubCloser {\n    const request: { url: string; filter: Filter }[] = []\n    for (let i = 0; i < relays.length; i++) {\n      const url = normalizeURL(relays[i])\n      if (!request.find(r => r.url === url)) {\n        request.push({ url, filter: filter })\n      }\n    }\n\n    return this.subscribeMap(request, params)\n  }\n\n  subscribeMany(relays: string[], filter: Filter, params: SubscribeManyParams): SubCloser {\n    const request: { url: string; filter: Filter }[] = []\n    const uniqUrls: string[] = []\n    for (let i = 0; i < relays.length; i++) {\n      const url = normalizeURL(relays[i])\n      if (uniqUrls.indexOf(url) === -1) {\n        uniqUrls.push(url)\n        request.push({ url, filter: filter })\n      }\n    }\n\n    return this.subscribeMap(request, params)\n  }\n\n  subscribeMap(requests: { url: string; filter: Filter }[], params: SubscribeManyParams): SubCloser {\n    const grouped = new Map<string, Filter[]>()\n    for (const req of requests) {\n      const { url, filter } = req\n      if (!grouped.has(url)) grouped.set(url, [])\n      grouped.get(url)!.push(filter)\n    }\n    const groupedRequests = Array.from(grouped.entries()).map(([url, filters]) => ({ url, filters }))\n\n    if (this.trackRelays) {\n      params.receivedEvent = (relay: AbstractRelay, id: string) => {\n        let set = this.seenOn.get(id)\n        if (!set) {\n          set = new Set()\n          this.seenOn.set(id, set)\n        }\n        set.add(relay)\n      }\n    }\n\n    const _knownIds = new Set<string>()\n    const subs: Subscription[] = []\n\n    // batch all EOSEs into a single\n    const eosesReceived: boolean[] = []\n    let handleEose = (i: number) => {\n      if (eosesReceived[i]) return // do not act twice for the same relay\n      eosesReceived[i] = true\n      if (eosesReceived.filter(a => a).length === groupedRequests.length) {\n        params.oneose?.()\n        handleEose = () => {}\n      }\n    }\n    // batch all closes into a single\n    const closesReceived: string[] = []\n    let handleClose = (i: number, reason: string) => {\n      if (closesReceived[i]) return // do not act twice for the same relay\n      handleEose(i)\n      closesReceived[i] = reason\n      if (closesReceived.filter(a => a).length === groupedRequests.length) {\n        params.onclose?.(closesReceived)\n        handleClose = () => {}\n      }\n    }\n\n    const localAlreadyHaveEventHandler = (id: string) => {\n      if (params.alreadyHaveEvent?.(id)) {\n        return true\n      }\n      const have = _knownIds.has(id)\n      _knownIds.add(id)\n      return have\n    }\n\n    // open a subscription in all given relays\n    const allOpened = Promise.all(\n      groupedRequests.map(async ({ url, filters }, i) => {\n        let relay: AbstractRelay\n        try {\n          relay = await this.ensureRelay(url, {\n            connectionTimeout: params.maxWait ? Math.max(params.maxWait * 0.8, params.maxWait - 1000) : undefined,\n            abort: params.abort,\n          })\n        } catch (err) {\n          handleClose(i, (err as any)?.message || String(err))\n          return\n        }\n\n        let subscription = relay.subscribe(filters, {\n          ...params,\n          oneose: () => handleEose(i),\n          onclose: reason => {\n            if (reason.startsWith('auth-required: ') && params.onauth) {\n              relay\n                .auth(params.onauth)\n                .then(() => {\n                  relay.subscribe(filters, {\n                    ...params,\n                    oneose: () => handleEose(i),\n                    onclose: reason => {\n                      handleClose(i, reason) // the second time we won't try to auth anymore\n                    },\n                    alreadyHaveEvent: localAlreadyHaveEventHandler,\n                    eoseTimeout: params.maxWait,\n                    abort: params.abort,\n                  })\n                })\n                .catch(err => {\n                  handleClose(i, `auth was required and attempted, but failed with: ${err}`)\n                })\n            } else {\n              handleClose(i, reason)\n            }\n          },\n          alreadyHaveEvent: localAlreadyHaveEventHandler,\n          eoseTimeout: params.maxWait,\n          abort: params.abort,\n        })\n\n        subs.push(subscription)\n      }),\n    )\n\n    return {\n      async close(reason?: string) {\n        await allOpened\n        subs.forEach(sub => {\n          sub.close(reason)\n        })\n      },\n    }\n  }\n\n  subscribeEose(\n    relays: string[],\n    filter: Filter,\n    params: Pick<SubscribeManyParams, 'label' | 'id' | 'onevent' | 'onclose' | 'maxWait' | 'onauth'>,\n  ): SubCloser {\n    const subcloser = this.subscribe(relays, filter, {\n      ...params,\n      oneose() {\n        subcloser.close('closed automatically on eose')\n      },\n    })\n    return subcloser\n  }\n\n  subscribeManyEose(\n    relays: string[],\n    filter: Filter,\n    params: Pick<SubscribeManyParams, 'label' | 'id' | 'onevent' | 'onclose' | 'maxWait' | 'onauth'>,\n  ): SubCloser {\n    const subcloser = this.subscribeMany(relays, filter, {\n      ...params,\n      oneose() {\n        subcloser.close('closed automatically on eose')\n      },\n    })\n    return subcloser\n  }\n\n  async querySync(\n    relays: string[],\n    filter: Filter,\n    params?: Pick<SubscribeManyParams, 'label' | 'id' | 'maxWait'>,\n  ): Promise<Event[]> {\n    return new Promise(async resolve => {\n      const events: Event[] = []\n      this.subscribeEose(relays, filter, {\n        ...params,\n        onevent(event: Event) {\n          events.push(event)\n        },\n        onclose(_: string[]) {\n          resolve(events)\n        },\n      })\n    })\n  }\n\n  async get(\n    relays: string[],\n    filter: Filter,\n    params?: Pick<SubscribeManyParams, 'label' | 'id' | 'maxWait'>,\n  ): Promise<Event | null> {\n    filter.limit = 1\n    const events = await this.querySync(relays, filter, params)\n    events.sort((a, b) => b.created_at - a.created_at)\n    return events[0] || null\n  }\n\n  publish(\n    relays: string[],\n    event: Event,\n    options?: { onauth?: (evt: EventTemplate) => Promise<VerifiedEvent> },\n  ): Promise<string>[] {\n    return relays.map(normalizeURL).map(async (url, i, arr) => {\n      if (arr.indexOf(url) !== i) {\n        // duplicate\n        return Promise.reject('duplicate url')\n      }\n\n      let r = await this.ensureRelay(url)\n      return r\n        .publish(event)\n        .catch(async err => {\n          if (err instanceof Error && err.message.startsWith('auth-required: ') && options?.onauth) {\n            await r.auth(options.onauth)\n            return r.publish(event) // retry\n          }\n          throw err\n        })\n        .then(reason => {\n          if (this.trackRelays) {\n            let set = this.seenOn.get(event.id)\n            if (!set) {\n              set = new Set()\n              this.seenOn.set(event.id, set)\n            }\n            set.add(r)\n          }\n          return reason\n        })\n    })\n  }\n\n  listConnectionStatus(): Map<string, boolean> {\n    const map = new Map<string, boolean>()\n    this.relays.forEach((relay, url) => map.set(url, relay.connected))\n\n    return map\n  }\n\n  destroy(): void {\n    this.relays.forEach(conn => conn.close())\n    this.relays = new Map()\n  }\n}\n", "/* global WebSocket */\n\nimport { verifyEvent } from './pure.ts'\nimport { AbstractSimplePool, type AbstractPoolConstructorOptions } from './abstract-pool.ts'\n\nvar _WebSocket: typeof WebSocket\n\ntry {\n  _WebSocket = WebSocket\n} catch {}\n\nexport function useWebSocketImplementation(websocketImplementation: any) {\n  _WebSocket = websocketImplementation\n}\n\nexport class SimplePool extends AbstractSimplePool {\n  constructor(options?: Pick<AbstractPoolConstructorOptions, 'enablePing' | 'enableReconnect'>) {\n    super({ verifyEvent, websocketImplementation: _WebSocket, ...options })\n  }\n}\n\nexport * from './abstract-pool.ts'\n", "import { EventTemplate, NostrEvent, VerifiedEvent } from './core.ts'\nimport { generateSecretKey, finalizeEvent, getPublicKey, verifyEvent } from './pure.ts'\nimport { AbstractSimplePool, SubCloser } from './abstract-pool.ts'\nimport { getConversationKey, decrypt, encrypt } from './nip44.ts'\nimport { NIP05_REGEX } from './nip05.ts'\nimport { SimplePool } from './pool.ts'\nimport { Handlerinformation, NostrConnect } from './kinds.ts'\nimport { Signer } from './signer.ts'\n\nvar _fetch: any\n\ntry {\n  _fetch = fetch\n} catch {}\n\nexport function useFetchImplementation(fetchImplementation: any) {\n  _fetch = fetchImplementation\n}\n\nexport const BUNKER_REGEX = /^bunker:\\/\\/([0-9a-f]{64})\\??([?\\/\\w:.=&%-]*)$/\nconst EMAIL_REGEX = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/\n\nexport type BunkerPointer = {\n  relays: string[]\n  pubkey: string\n  secret: null | string\n}\n\nexport function toBunkerURL(bunkerPointer: BunkerPointer): string {\n  let bunkerURL = new URL(`bunker://${bunkerPointer.pubkey}`)\n  bunkerPointer.relays.forEach(relay => {\n    bunkerURL.searchParams.append('relay', relay)\n  })\n  if (bunkerPointer.secret) {\n    bunkerURL.searchParams.set('secret', bunkerPointer.secret)\n  }\n  return bunkerURL.toString()\n}\n\n/** This takes either a bunker:// URL or a [email protected] NIP-05 identifier\n    and returns a BunkerPointer -- or null in case of error */\nexport async function parseBunkerInput(input: string): Promise<BunkerPointer | null> {\n  let match = input.match(BUNKER_REGEX)\n  if (match) {\n    try {\n      const pubkey = match[1]\n      const qs = new URLSearchParams(match[2])\n      return {\n        pubkey,\n        relays: qs.getAll('relay'),\n        secret: qs.get('secret'),\n      }\n    } catch (_err) {\n      /* just move to the next case */\n    }\n  }\n\n  return queryBunkerProfile(input)\n}\n\nexport async function queryBunkerProfile(nip05: string): Promise<BunkerPointer | null> {\n  const match = nip05.match(NIP05_REGEX)\n  if (!match) return null\n\n  const [_, name = '_', domain] = match\n\n  try {\n    const url = `https://${domain}/.well-known/nostr.json?name=${name}`\n    const res = await (await _fetch(url, { redirect: 'error' })).json()\n\n    let pubkey = res.names[name]\n    let relays = res.nip46[pubkey] || []\n\n    return { pubkey, relays, secret: null }\n  } catch (_err) {\n    return null\n  }\n}\n\nexport type NostrConnectParams = {\n  clientPubkey: string\n  relays: string[]\n  secret: string\n  perms?: string[]\n  name?: string\n  url?: string\n  image?: string\n}\n\nexport function createNostrConnectURI(params: NostrConnectParams): string {\n  const queryParams = new URLSearchParams()\n\n  params.relays.forEach(relay => {\n    queryParams.append('relay', relay)\n  })\n\n  queryParams.append('secret', params.secret)\n\n  if (params.perms && params.perms.length > 0) {\n    queryParams.append('perms', params.perms.join(','))\n  }\n  if (params.name) {\n    queryParams.append('name', params.name)\n  }\n  if (params.url) {\n    queryParams.append('url', params.url)\n  }\n  if (params.image) {\n    queryParams.append('image', params.image)\n  }\n\n  return `nostrconnect://${params.clientPubkey}?${queryParams.toString()}`\n}\n\nexport type BunkerSignerParams = {\n  pool?: AbstractSimplePool\n  onauth?: (url: string) => void\n}\n\nexport class BunkerSigner implements Signer {\n  private params: BunkerSignerParams\n  private pool: AbstractSimplePool\n  private subCloser: SubCloser | undefined\n  private isOpen: boolean\n  private serial: number\n  private idPrefix: string\n  private listeners: {\n    [id: string]: {\n      resolve: (_: string) => void\n      reject: (_: string) => void\n    }\n  }\n  private waitingForAuth: { [id: string]: boolean }\n  private secretKey: Uint8Array\n  // If the client initiates the connection, the two variables below can be filled in later.\n  private conversationKey!: Uint8Array\n  public bp!: BunkerPointer\n\n  private cachedPubKey: string | undefined\n\n  /**\n   * Creates a new instance of the Nip46 class.\n   * @param relays - An array of relay addresses.\n   * @param remotePubkey - An optional remote public key. This is the key you want to sign as.\n   * @param secretKey - An optional key pair.\n   */\n  private constructor(clientSecretKey: Uint8Array, params: BunkerSignerParams) {\n    this.params = params\n    this.pool = params.pool || new SimplePool()\n    this.secretKey = clientSecretKey\n    this.isOpen = false\n    this.idPrefix = Math.random().toString(36).substring(7)\n    this.serial = 0\n    this.listeners = {}\n    this.waitingForAuth = {}\n  }\n\n  /**\n   * [Factory Method 1] Creates a Signer using bunker information (bunker:// URL or NIP-05).\n   * This method is used when the public key of the bunker is known in advance.\n   */\n  public static fromBunker(\n    clientSecretKey: Uint8Array,\n    bp: BunkerPointer,\n    params: BunkerSignerParams = {},\n  ): BunkerSigner {\n    if (bp.relays.length === 0) {\n      throw new Error('no relays specified for this bunker')\n    }\n\n    const signer = new BunkerSigner(clientSecretKey, params)\n\n    signer.conversationKey = getConversationKey(clientSecretKey, bp.pubkey)\n    signer.bp = bp\n\n    signer.setupSubscription()\n    return signer\n  }\n\n  /**\n   * [Factory Method 2] Creates a Signer using a nostrconnect:// URI generated by the client.\n   * In this method, the bunker initiates the connection by scanning the URI.\n   */\n  public static async fromURI(\n    clientSecretKey: Uint8Array,\n    connectionURI: string,\n    bunkerParams: BunkerSignerParams = {},\n    maxWaitOrAbort: number | AbortSignal = 300_000,\n  ): Promise<BunkerSigner> {\n    const signer = new BunkerSigner(clientSecretKey, bunkerParams)\n    const uri = new URL(connectionURI)\n    const clientPubkey = getPublicKey(clientSecretKey)\n\n    return new Promise((resolve, reject) => {\n      let success = false\n      const sub = signer.pool.subscribe(\n        uri.searchParams.getAll('relay'),\n        {\n          kinds: [NostrConnect],\n          '#p': [clientPubkey],\n          limit: 0,\n        },\n        {\n          onevent: async (event: NostrEvent) => {\n            try {\n              const tempConvKey = getConversationKey(clientSecretKey, event.pubkey)\n              const decryptedContent = decrypt(event.content, tempConvKey)\n\n              const response = JSON.parse(decryptedContent)\n\n              if (response.result === uri.searchParams.get('secret')) {\n                sub.close()\n\n                signer.bp = {\n                  pubkey: event.pubkey,\n                  relays: uri.searchParams.getAll('relay'),\n                  secret: uri.searchParams.get('secret'),\n                }\n                signer.conversationKey = getConversationKey(clientSecretKey, event.pubkey)\n                signer.setupSubscription()\n\n                success = true\n                await Promise.race([new Promise(resolve => setTimeout(resolve, 1000)), signer.switchRelays()])\n                resolve(signer)\n              }\n            } catch (e) {\n              console.warn('failed to process potential connection event', e)\n            }\n          },\n          onclose: () => {\n            if (!success) reject(new Error('subscription closed before connection was established.'))\n          },\n          maxWait: typeof maxWaitOrAbort === 'number' ? maxWaitOrAbort : undefined,\n          abort: typeof maxWaitOrAbort !== 'number' ? maxWaitOrAbort : undefined,\n        },\n      )\n    })\n  }\n\n  private setupSubscription() {\n    const listeners = this.listeners\n    const waitingForAuth = this.waitingForAuth\n    const convKey = this.conversationKey\n\n    this.subCloser = this.pool.subscribe(\n      this.bp.relays,\n      {\n        kinds: [NostrConnect],\n        authors: [this.bp.pubkey],\n        '#p': [getPublicKey(this.secretKey)],\n        limit: 0,\n      },\n      {\n        onevent: async (event: NostrEvent) => {\n          const o = JSON.parse(decrypt(event.content, convKey))\n          const { id, result, error } = o\n\n          if (result === 'auth_url' && waitingForAuth[id]) {\n            delete waitingForAuth[id]\n\n            if (this.params.onauth) {\n              this.params.onauth(error)\n            } else {\n              console.warn(\n                `nostr-tools/nip46: remote signer ${this.bp.pubkey} tried to send an \"auth_url\"='${error}' but there was no onauth() callback configured.`,\n              )\n            }\n            return\n          }\n\n          let handler = listeners[id]\n          if (handler) {\n            if (error) handler.reject(error)\n            else if (result) handler.resolve(result)\n            delete listeners[id]\n          }\n        },\n        onclose: () => {\n          this.subCloser = undefined\n        },\n      },\n    )\n    this.isOpen = true\n  }\n\n  async switchRelays(): Promise<boolean> {\n    try {\n      const switchResp = await this.sendRequest('switch_relays', [])\n      let relays = JSON.parse(switchResp) as string[] | null\n      if (!relays) return false\n      if (JSON.stringify(relays.sort()) === JSON.stringify(this.bp.relays)) return false\n\n      this.bp.relays = relays\n      let previousCloser = this.subCloser!\n      setTimeout(() => {\n        previousCloser.close()\n      }, 5000)\n\n      this.subCloser = undefined\n      this.setupSubscription()\n      return true\n    } catch {\n      return false\n    }\n  }\n\n  // closes the subscription -- this object can't be used anymore after this\n  async close() {\n    this.isOpen = false\n    this.subCloser!.close()\n  }\n\n  async sendRequest(method: string, params: string[]): Promise<string> {\n    return new Promise(async (resolve, reject) => {\n      try {\n        if (!this.isOpen) throw new Error('this signer is not open anymore, create a new one')\n        if (!this.subCloser) this.setupSubscription()\n\n        this.serial++\n        const id = `${this.idPrefix}-${this.serial}`\n\n        const encryptedContent = encrypt(JSON.stringify({ id, method, params }), this.conversationKey)\n\n        // the request event\n        const verifiedEvent: VerifiedEvent = finalizeEvent(\n          {\n            kind: NostrConnect,\n            tags: [['p', this.bp.pubkey]],\n            content: encryptedContent,\n            created_at: Math.floor(Date.now() / 1000),\n          },\n          this.secretKey,\n        )\n\n        // setup callback listener\n        this.listeners[id] = { resolve, reject }\n        this.waitingForAuth[id] = true\n\n        // publish the event\n        await Promise.any(this.pool.publish(this.bp.relays, verifiedEvent))\n      } catch (err) {\n        reject(err)\n      }\n    })\n  }\n\n  /**\n   * Calls the \"connect\" method on the bunker.\n   * The promise will be rejected if the response is not \"pong\".\n   */\n  async ping(): Promise<void> {\n    let resp = await this.sendRequest('ping', [])\n    if (resp !== 'pong') throw new Error(`result is not pong: ${resp}`)\n  }\n\n  /**\n   * Calls the \"connect\" method on the bunker.\n   */\n  async connect(): Promise<void> {\n    await this.sendRequest('connect', [this.bp.pubkey, this.bp.secret || ''])\n  }\n\n  /**\n   * Calls the \"get_public_key\" method on the bunker.\n   * (before we would return the public key hardcoded in the bunker parameters, but\n   *  that is not correct as that may be the bunker pubkey and the actual signer\n   *  pubkey may be different.)\n   */\n  async getPublicKey(): Promise<string> {\n    if (!this.cachedPubKey) {\n      this.cachedPubKey = await this.sendRequest('get_public_key', [])\n    }\n    return this.cachedPubKey\n  }\n\n  /**\n   * Signs an event using the remote private key.\n   * @param event - The event to sign.\n   * @returns A Promise that resolves to the signed event.\n   */\n  async signEvent(event: EventTemplate): Promise<VerifiedEvent> {\n    let resp = await this.sendRequest('sign_event', [JSON.stringify(event)])\n    let signed: NostrEvent = JSON.parse(resp)\n    if (verifyEvent(signed)) {\n      return signed\n    } else {\n      throw new Error(`event returned from bunker is improperly signed: ${JSON.stringify(signed)}`)\n    }\n  }\n\n  async nip04Encrypt(thirdPartyPubkey: string, plaintext: string): Promise<string> {\n    return await this.sendRequest('nip04_encrypt', [thirdPartyPubkey, plaintext])\n  }\n\n  async nip04Decrypt(thirdPartyPubkey: string, ciphertext: string): Promise<string> {\n    return await this.sendRequest('nip04_decrypt', [thirdPartyPubkey, ciphertext])\n  }\n\n  async nip44Encrypt(thirdPartyPubkey: string, plaintext: string): Promise<string> {\n    return await this.sendRequest('nip44_encrypt', [thirdPartyPubkey, plaintext])\n  }\n\n  async nip44Decrypt(thirdPartyPubkey: string, ciphertext: string): Promise<string> {\n    return await this.sendRequest('nip44_decrypt', [thirdPartyPubkey, ciphertext])\n  }\n}\n\n/**\n * Creates an account with the specified username, domain, and optional email.\n * @param bunkerPubkey - The public key of the bunker to use for the create_account call.\n * @param username - The username for the account.\n * @param domain - The domain for the account.\n * @param email - The optional email for the account.\n * @param localSecretKey - Optionally pass a local secret key that will be used to communicate with the bunker,\n                           this will default to generating a random key.\n * @throws Error if the email is present but invalid.\n * @returns A Promise that resolves to the auth_url that the client should follow to create an account.\n */\nexport async function createAccount(\n  bunker: BunkerProfile,\n  params: BunkerSignerParams,\n  username: string,\n  domain: string,\n  email?: string,\n  localSecretKey: Uint8Array = generateSecretKey(),\n): Promise<BunkerSigner> {\n  if (email && !EMAIL_REGEX.test(email)) throw new Error('invalid email')\n\n  let rpc = BunkerSigner.fromBunker(localSecretKey, bunker.bunkerPointer, params)\n\n  let pubkey = await rpc.sendRequest('create_account', [username, domain, email || ''])\n\n  // once we get the newly created pubkey back, we hijack this signer instance\n  // and turn it into the main instance for this newly created pubkey\n  rpc.bp.pubkey = pubkey\n  await rpc.connect()\n\n  return rpc\n}\n\n/**\n * Fetches info on available providers that announce themselves using NIP-89 events.\n * @returns A promise that resolves to an array of available bunker objects.\n */\nexport async function fetchBunkerProviders(pool: AbstractSimplePool, relays: string[]): Promise<BunkerProfile[]> {\n  const events = await pool.querySync(relays, {\n    kinds: [Handlerinformation],\n    '#k': [NostrConnect.toString()],\n  })\n\n  events.sort((a, b) => b.created_at - a.created_at)\n\n  // validate bunkers by checking their NIP-05 and pubkey\n  // map to a more useful object\n  const validatedBunkers = await Promise.all(\n    events.map(async (event, i) => {\n      try {\n        const content = JSON.parse(event.content)\n\n        // skip duplicates\n        try {\n          if (events.findIndex(ev => JSON.parse(ev.content).nip05 === content.nip05) !== i) return undefined\n        } catch (err) {\n          /***/\n        }\n\n        const bp = await queryBunkerProfile(content.nip05)\n        if (bp && bp.pubkey === event.pubkey && bp.relays.length) {\n          return {\n            bunkerPointer: bp,\n            nip05: content.nip05,\n            domain: content.nip05.split('@')[1],\n            name: content.name || content.display_name,\n            picture: content.picture,\n            about: content.about,\n            website: content.website,\n            local: false,\n          }\n        }\n      } catch (err) {\n        return undefined\n      }\n    }),\n  )\n\n  return validatedBunkers.filter(b => b !== undefined) as BunkerProfile[]\n}\n\nexport type BunkerProfile = {\n  bunkerPointer: BunkerPointer\n  domain: string\n  nip05: string\n  name: string\n  picture: string\n  about: string\n  website: string\n  local: boolean\n}\n"],
+  "mappings": "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",
+  "names": ["bytesToHex", "hexToBytes", "i", "target", "bytesToHex", "hexToBytes", "sha256", "hexToBytes", "hexToBytes", "sha256", "i", "i", "reason", "_fetch", "resolve", "i"]
 }
Index: package/lib/cjs/nip47.js.map
===================================================================
--- package/lib/cjs/nip47.js.map
+++ package/lib/cjs/nip47.js.map
@@ -1,7 +1,7 @@
 {
   "version": 3,
   "sources": ["../../nip47.ts", "../../pure.ts", "../../core.ts", "../../utils.ts", "../../kinds.ts", "../../nip04.ts"],
-  "sourcesContent": ["import { type VerifiedEvent, finalizeEvent } from './pure.ts'\nimport { NWCWalletRequest } from './kinds.ts'\nimport { encrypt } from './nip04.ts'\n\ninterface NWCConnection {\n  pubkey: string\n  relay: string\n  secret: string\n}\n\nexport function parseConnectionString(connectionString: string): NWCConnection {\n  const { host, pathname, searchParams } = new URL(connectionString)\n  const pubkey = pathname || host\n  const relay = searchParams.get('relay')\n  const secret = searchParams.get('secret')\n\n  if (!pubkey || !relay || !secret) {\n    throw new Error('invalid connection string')\n  }\n\n  return { pubkey, relay, secret }\n}\n\nexport async function makeNwcRequestEvent(\n  pubkey: string,\n  secretKey: Uint8Array,\n  invoice: string,\n): Promise<VerifiedEvent> {\n  const content = {\n    method: 'pay_invoice',\n    params: {\n      invoice,\n    },\n  }\n  const encryptedContent = encrypt(secretKey, pubkey, JSON.stringify(content))\n  const eventTemplate = {\n    kind: NWCWalletRequest,\n    created_at: Math.round(Date.now() / 1000),\n    content: encryptedContent,\n    tags: [['p', pubkey]],\n  }\n\n  return finalizeEvent(eventTemplate, secretKey)\n}\n", "import { schnorr } from '@noble/curves/secp256k1'\nimport { bytesToHex } from '@noble/hashes/utils'\nimport { Nostr, Event, EventTemplate, UnsignedEvent, VerifiedEvent, verifiedSymbol, validateEvent } from './core.ts'\nimport { sha256 } from '@noble/hashes/sha256'\n\nimport { utf8Encoder } from './utils.ts'\n\nclass JS implements Nostr {\n  generateSecretKey(): Uint8Array {\n    return schnorr.utils.randomPrivateKey()\n  }\n  getPublicKey(secretKey: Uint8Array): string {\n    return bytesToHex(schnorr.getPublicKey(secretKey))\n  }\n  finalizeEvent(t: EventTemplate, secretKey: Uint8Array): VerifiedEvent {\n    const event = t as VerifiedEvent\n    event.pubkey = bytesToHex(schnorr.getPublicKey(secretKey))\n    event.id = getEventHash(event)\n    event.sig = bytesToHex(schnorr.sign(getEventHash(event), secretKey))\n    event[verifiedSymbol] = true\n    return event\n  }\n  verifyEvent(event: Event): event is VerifiedEvent {\n    if (typeof event[verifiedSymbol] === 'boolean') return event[verifiedSymbol]\n\n    const hash = getEventHash(event)\n    if (hash !== event.id) {\n      event[verifiedSymbol] = false\n      return false\n    }\n\n    try {\n      const valid = schnorr.verify(event.sig, hash, event.pubkey)\n      event[verifiedSymbol] = valid\n      return valid\n    } catch (err) {\n      event[verifiedSymbol] = false\n      return false\n    }\n  }\n}\n\nexport function serializeEvent(evt: UnsignedEvent): string {\n  if (!validateEvent(evt)) throw new Error(\"can't serialize event with wrong or missing properties\")\n  return JSON.stringify([0, evt.pubkey, evt.created_at, evt.kind, evt.tags, evt.content])\n}\n\nexport function getEventHash(event: UnsignedEvent): string {\n  let eventHash = sha256(utf8Encoder.encode(serializeEvent(event)))\n  return bytesToHex(eventHash)\n}\n\nconst i: JS = new JS()\n\nexport const generateSecretKey = i.generateSecretKey\nexport const getPublicKey = i.getPublicKey\nexport const finalizeEvent = i.finalizeEvent\nexport const verifyEvent = i.verifyEvent\nexport * from './core.ts'\n", "export interface Nostr {\n  generateSecretKey(): Uint8Array\n  getPublicKey(secretKey: Uint8Array): string\n  finalizeEvent(event: EventTemplate, secretKey: Uint8Array): VerifiedEvent\n  verifyEvent(event: Event): event is VerifiedEvent\n}\n\n/** Designates a verified event signature. */\nexport const verifiedSymbol = Symbol('verified')\n\nexport type NostrEvent = {\n  kind: number\n  tags: string[][]\n  content: string\n  created_at: number\n  pubkey: string\n  id: string\n  sig: string\n  [verifiedSymbol]?: boolean\n}\n\nexport type Event = NostrEvent\nexport type EventTemplate = Pick<Event, 'kind' | 'tags' | 'content' | 'created_at'>\nexport type UnsignedEvent = Pick<Event, 'kind' | 'tags' | 'content' | 'created_at' | 'pubkey'>\n\n/** An event whose signature has been verified. */\nexport interface VerifiedEvent extends Event {\n  [verifiedSymbol]: true\n}\n\nconst isRecord = (obj: unknown): obj is Record<string, unknown> => obj instanceof Object\n\nexport function validateEvent<T>(event: T): event is T & UnsignedEvent {\n  if (!isRecord(event)) return false\n  if (typeof event.kind !== 'number') return false\n  if (typeof event.content !== 'string') return false\n  if (typeof event.created_at !== 'number') return false\n  if (typeof event.pubkey !== 'string') return false\n  if (!event.pubkey.match(/^[a-f0-9]{64}$/)) return false\n\n  if (!Array.isArray(event.tags)) return false\n  for (let i = 0; i < event.tags.length; i++) {\n    let tag = event.tags[i]\n    if (!Array.isArray(tag)) return false\n    for (let j = 0; j < tag.length; j++) {\n      if (typeof tag[j] !== 'string') return false\n    }\n  }\n\n  return true\n}\n\n/**\n * Sort events in reverse-chronological order by the `created_at` timestamp,\n * and then by the event `id` (lexicographically) in case of ties.\n * This mutates the array.\n */\nexport function sortEvents(events: Event[]): Event[] {\n  return events.sort((a: NostrEvent, b: NostrEvent): number => {\n    if (a.created_at !== b.created_at) {\n      return b.created_at - a.created_at\n    }\n    return a.id.localeCompare(b.id)\n  })\n}\n", "import type { Event } from './core.ts'\n\nexport const utf8Decoder: TextDecoder = new TextDecoder('utf-8')\nexport const utf8Encoder: TextEncoder = new TextEncoder()\n\nexport { bytesToHex, hexToBytes } from '@noble/hashes/utils'\n\nexport function normalizeURL(url: string): string {\n  try {\n    if (url.indexOf('://') === -1) url = 'wss://' + url\n    let p = new URL(url)\n    if (p.protocol === 'http:') p.protocol = 'ws:'\n    else if (p.protocol === 'https:') p.protocol = 'wss:'\n    p.pathname = p.pathname.replace(/\\/+/g, '/')\n    if (p.pathname.endsWith('/')) p.pathname = p.pathname.slice(0, -1)\n    if ((p.port === '80' && p.protocol === 'ws:') || (p.port === '443' && p.protocol === 'wss:')) p.port = ''\n    p.searchParams.sort()\n    p.hash = ''\n    return p.toString()\n  } catch (e) {\n    throw new Error(`Invalid URL: ${url}`)\n  }\n}\n\nexport function insertEventIntoDescendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return b.created_at - event.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function insertEventIntoAscendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return event.created_at - b.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function binarySearch<T>(arr: T[], compare: (b: T) => number): [number, boolean] {\n  let start = 0\n  let end = arr.length - 1\n\n  while (start <= end) {\n    const mid = Math.floor((start + end) / 2)\n    const cmp = compare(arr[mid])\n\n    if (cmp === 0) {\n      return [mid, true]\n    }\n\n    if (cmp < 0) {\n      end = mid - 1\n    } else {\n      start = mid + 1\n    }\n  }\n\n  return [start, false]\n}\n\nexport class QueueNode<V> {\n  public value: V\n  public next: QueueNode<V> | null = null\n  public prev: QueueNode<V> | null = null\n\n  constructor(message: V) {\n    this.value = message\n  }\n}\n\nexport class Queue<V> {\n  public first: QueueNode<V> | null\n  public last: QueueNode<V> | null\n\n  constructor() {\n    this.first = null\n    this.last = null\n  }\n\n  enqueue(value: V): boolean {\n    const newNode = new QueueNode(value)\n    if (!this.last) {\n      // list is empty\n      this.first = newNode\n      this.last = newNode\n    } else if (this.last === this.first) {\n      // list has a single element\n      this.last = newNode\n      this.last.prev = this.first\n      this.first.next = newNode\n    } else {\n      // list has elements, add as last\n      newNode.prev = this.last\n      this.last.next = newNode\n      this.last = newNode\n    }\n    return true\n  }\n\n  dequeue(): V | null {\n    if (!this.first) return null\n\n    if (this.first === this.last) {\n      const target = this.first\n      this.first = null\n      this.last = null\n      return target.value\n    }\n\n    const target = this.first\n    this.first = target.next\n    if (this.first) {\n      this.first.prev = null // fix: clean up prev pointer\n    }\n\n    return target.value\n  }\n}\n", "import { NostrEvent, validateEvent } from './pure.ts'\n\n/** Events are **regular**, which means they're all expected to be stored by relays. */\nexport function isRegularKind(kind: number): boolean {\n  return kind < 10000 && kind !== 0 && kind !== 3\n}\n\n/** Events are **replaceable**, which means that, for each combination of `pubkey` and `kind`, only the latest event is expected to (SHOULD) be stored by relays, older versions are expected to be discarded. */\nexport function isReplaceableKind(kind: number): boolean {\n  return kind === 0 || kind === 3 || (10000 <= kind && kind < 20000)\n}\n\n/** Events are **ephemeral**, which means they are not expected to be stored by relays. */\nexport function isEphemeralKind(kind: number): boolean {\n  return 20000 <= kind && kind < 30000\n}\n\n/** Events are **addressable**, which means that, for each combination of `pubkey`, `kind` and the `d` tag, only the latest event is expected to be stored by relays, older versions are expected to be discarded. */\nexport function isAddressableKind(kind: number): boolean {\n  return 30000 <= kind && kind < 40000\n}\n\n/** Classification of the event kind. */\nexport type KindClassification = 'regular' | 'replaceable' | 'ephemeral' | 'parameterized' | 'unknown'\n\n/** Determine the classification of this kind of event if known, or `unknown`. */\nexport function classifyKind(kind: number): KindClassification {\n  if (isRegularKind(kind)) return 'regular'\n  if (isReplaceableKind(kind)) return 'replaceable'\n  if (isEphemeralKind(kind)) return 'ephemeral'\n  if (isAddressableKind(kind)) return 'parameterized'\n  return 'unknown'\n}\n\nexport function isKind<T extends number>(event: unknown, kind: T | Array<T>): event is NostrEvent & { kind: T } {\n  const kindAsArray: number[] = kind instanceof Array ? kind : [kind]\n  return (validateEvent(event) && kindAsArray.includes(event.kind)) || false\n}\n\nexport const Metadata = 0\nexport type Metadata = typeof Metadata\nexport const ShortTextNote = 1\nexport type ShortTextNote = typeof ShortTextNote\nexport const RecommendRelay = 2\nexport type RecommendRelay = typeof RecommendRelay\nexport const Contacts = 3\nexport type Contacts = typeof Contacts\nexport const EncryptedDirectMessage = 4\nexport type EncryptedDirectMessage = typeof EncryptedDirectMessage\nexport const EventDeletion = 5\nexport type EventDeletion = typeof EventDeletion\nexport const Repost = 6\nexport type Repost = typeof Repost\nexport const Reaction = 7\nexport type Reaction = typeof Reaction\nexport const BadgeAward = 8\nexport type BadgeAward = typeof BadgeAward\nexport const ChatMessage = 9\nexport type ChatMessage = typeof ChatMessage\nexport const ForumThread = 11\nexport type ForumThread = typeof ForumThread\nexport const Seal = 13\nexport type Seal = typeof Seal\nexport const PrivateDirectMessage = 14\nexport type PrivateDirectMessage = typeof PrivateDirectMessage\nexport const FileMessage = 15\nexport type FileMessage = typeof FileMessage\nexport const GenericRepost = 16\nexport type GenericRepost = typeof GenericRepost\nexport const Photo = 20\nexport type Photo = typeof Photo\nexport const NormalVideo = 21\nexport type NormalVideo = typeof NormalVideo\nexport const ShortVideo = 22\nexport type ShortVideo = typeof ShortVideo\nexport const ChannelCreation = 40\nexport type ChannelCreation = typeof ChannelCreation\nexport const ChannelMetadata = 41\nexport type ChannelMetadata = typeof ChannelMetadata\nexport const ChannelMessage = 42\nexport type ChannelMessage = typeof ChannelMessage\nexport const ChannelHideMessage = 43\nexport type ChannelHideMessage = typeof ChannelHideMessage\nexport const ChannelMuteUser = 44\nexport type ChannelMuteUser = typeof ChannelMuteUser\nexport const OpenTimestamps = 1040\nexport type OpenTimestamps = typeof OpenTimestamps\nexport const GiftWrap = 1059\nexport type GiftWrap = typeof GiftWrap\nexport const Poll = 1068\nexport type Poll = typeof Poll\nexport const FileMetadata = 1063\nexport type FileMetadata = typeof FileMetadata\nexport const Comment = 1111\nexport type Comment = typeof Comment\nexport const LiveChatMessage = 1311\nexport type LiveChatMessage = typeof LiveChatMessage\nexport const Voice = 1222\nexport type Voice = typeof Voice\nexport const VoiceComment = 1244\nexport type VoiceComment = typeof VoiceComment\nexport const ProblemTracker = 1971\nexport type ProblemTracker = typeof ProblemTracker\nexport const Report = 1984\nexport type Report = typeof Report\nexport const Reporting = 1984\nexport type Reporting = typeof Reporting\nexport const Label = 1985\nexport type Label = typeof Label\nexport const CommunityPostApproval = 4550\nexport type CommunityPostApproval = typeof CommunityPostApproval\nexport const JobRequest = 5999\nexport type JobRequest = typeof JobRequest\nexport const JobResult = 6999\nexport type JobResult = typeof JobResult\nexport const JobFeedback = 7000\nexport type JobFeedback = typeof JobFeedback\nexport const ZapGoal = 9041\nexport type ZapGoal = typeof ZapGoal\nexport const ZapRequest = 9734\nexport type ZapRequest = typeof ZapRequest\nexport const Zap = 9735\nexport type Zap = typeof Zap\nexport const Highlights = 9802\nexport type Highlights = typeof Highlights\nexport const PollResponse = 1018\nexport type PollResponse = typeof PollResponse\nexport const Mutelist = 10000\nexport type Mutelist = typeof Mutelist\nexport const Pinlist = 10001\nexport type Pinlist = typeof Pinlist\nexport const RelayList = 10002\nexport type RelayList = typeof RelayList\nexport const BookmarkList = 10003\nexport type BookmarkList = typeof BookmarkList\nexport const CommunitiesList = 10004\nexport type CommunitiesList = typeof CommunitiesList\nexport const PublicChatsList = 10005\nexport type PublicChatsList = typeof PublicChatsList\nexport const BlockedRelaysList = 10006\nexport type BlockedRelaysList = typeof BlockedRelaysList\nexport const SearchRelaysList = 10007\nexport type SearchRelaysList = typeof SearchRelaysList\nexport const FavoriteRelays = 10012\nexport type FavoriteRelays = typeof FavoriteRelays\nexport const InterestsList = 10015\nexport type InterestsList = typeof InterestsList\nexport const UserEmojiList = 10030\nexport type UserEmojiList = typeof UserEmojiList\nexport const DirectMessageRelaysList = 10050\nexport type DirectMessageRelaysList = typeof DirectMessageRelaysList\nexport const FileServerPreference = 10096\nexport type FileServerPreference = typeof FileServerPreference\nexport const BlossomServerList = 10063\nexport type BlossomServerList = typeof BlossomServerList\nexport const NWCWalletInfo = 13194\nexport type NWCWalletInfo = typeof NWCWalletInfo\nexport const LightningPubRPC = 21000\nexport type LightningPubRPC = typeof LightningPubRPC\nexport const ClientAuth = 22242\nexport type ClientAuth = typeof ClientAuth\nexport const NWCWalletRequest = 23194\nexport type NWCWalletRequest = typeof NWCWalletRequest\nexport const NWCWalletResponse = 23195\nexport type NWCWalletResponse = typeof NWCWalletResponse\nexport const NostrConnect = 24133\nexport type NostrConnect = typeof NostrConnect\nexport const HTTPAuth = 27235\nexport type HTTPAuth = typeof HTTPAuth\nexport const Followsets = 30000\nexport type Followsets = typeof Followsets\nexport const Genericlists = 30001\nexport type Genericlists = typeof Genericlists\nexport const Relaysets = 30002\nexport type Relaysets = typeof Relaysets\nexport const Bookmarksets = 30003\nexport type Bookmarksets = typeof Bookmarksets\nexport const Curationsets = 30004\nexport type Curationsets = typeof Curationsets\nexport const ProfileBadges = 30008\nexport type ProfileBadges = typeof ProfileBadges\nexport const BadgeDefinition = 30009\nexport type BadgeDefinition = typeof BadgeDefinition\nexport const Interestsets = 30015\nexport type Interestsets = typeof Interestsets\nexport const CreateOrUpdateStall = 30017\nexport type CreateOrUpdateStall = typeof CreateOrUpdateStall\nexport const CreateOrUpdateProduct = 30018\nexport type CreateOrUpdateProduct = typeof CreateOrUpdateProduct\nexport const LongFormArticle = 30023\nexport type LongFormArticle = typeof LongFormArticle\nexport const DraftLong = 30024\nexport type DraftLong = typeof DraftLong\nexport const Emojisets = 30030\nexport type Emojisets = typeof Emojisets\nexport const Application = 30078\nexport type Application = typeof Application\nexport const LiveEvent = 30311\nexport type LiveEvent = typeof LiveEvent\nexport const UserStatuses = 30315\nexport type UserStatuses = typeof UserStatuses\nexport const ClassifiedListing = 30402\nexport type ClassifiedListing = typeof ClassifiedListing\nexport const DraftClassifiedListing = 30403\nexport type DraftClassifiedListing = typeof DraftClassifiedListing\nexport const Date = 31922\nexport type Date = typeof Date\nexport const Time = 31923\nexport type Time = typeof Time\nexport const Calendar = 31924\nexport type Calendar = typeof Calendar\nexport const CalendarEventRSVP = 31925\nexport type CalendarEventRSVP = typeof CalendarEventRSVP\nexport const RelayReview = 31987\nexport type RelayReview = typeof RelayReview\nexport const Handlerrecommendation = 31989\nexport type Handlerrecommendation = typeof Handlerrecommendation\nexport const Handlerinformation = 31990\nexport type Handlerinformation = typeof Handlerinformation\nexport const CommunityDefinition = 34550\nexport type CommunityDefinition = typeof CommunityDefinition\nexport const GroupMetadata = 39000\nexport type GroupMetadata = typeof GroupMetadata\n", "import { bytesToHex, randomBytes } from '@noble/hashes/utils'\nimport { secp256k1 } from '@noble/curves/secp256k1'\nimport { cbc } from '@noble/ciphers/aes'\nimport { base64 } from '@scure/base'\n\nimport { utf8Decoder, utf8Encoder } from './utils.ts'\n\nexport function encrypt(secretKey: string | Uint8Array, pubkey: string, text: string): string {\n  const privkey: string = secretKey instanceof Uint8Array ? bytesToHex(secretKey) : secretKey\n  const key = secp256k1.getSharedSecret(privkey, '02' + pubkey)\n  const normalizedKey = getNormalizedX(key)\n\n  let iv = Uint8Array.from(randomBytes(16))\n  let plaintext = utf8Encoder.encode(text)\n\n  let ciphertext = cbc(normalizedKey, iv).encrypt(plaintext)\n\n  let ctb64 = base64.encode(new Uint8Array(ciphertext))\n  let ivb64 = base64.encode(new Uint8Array(iv.buffer))\n\n  return `${ctb64}?iv=${ivb64}`\n}\n\nexport function decrypt(secretKey: string | Uint8Array, pubkey: string, data: string): string {\n  const privkey: string = secretKey instanceof Uint8Array ? bytesToHex(secretKey) : secretKey\n  let [ctb64, ivb64] = data.split('?iv=')\n  let key = secp256k1.getSharedSecret(privkey, '02' + pubkey)\n  let normalizedKey = getNormalizedX(key)\n\n  let iv = base64.decode(ivb64)\n  let ciphertext = base64.decode(ctb64)\n\n  let plaintext = cbc(normalizedKey, iv).decrypt(ciphertext)\n\n  return utf8Decoder.decode(plaintext)\n}\n\nfunction getNormalizedX(key: Uint8Array): Uint8Array {\n  return key.slice(1, 33)\n}\n"],
-  "mappings": "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",
+  "sourcesContent": ["import { type VerifiedEvent, finalizeEvent } from './pure.ts'\nimport { NWCWalletRequest } from './kinds.ts'\nimport { encrypt } from './nip04.ts'\n\ninterface NWCConnection {\n  pubkey: string\n  relay: string\n  secret: string\n}\n\nexport function parseConnectionString(connectionString: string): NWCConnection {\n  const { host, pathname, searchParams } = new URL(connectionString)\n  const pubkey = pathname || host\n  const relay = searchParams.get('relay')\n  const secret = searchParams.get('secret')\n\n  if (!pubkey || !relay || !secret) {\n    throw new Error('invalid connection string')\n  }\n\n  return { pubkey, relay, secret }\n}\n\nexport async function makeNwcRequestEvent(\n  pubkey: string,\n  secretKey: Uint8Array,\n  invoice: string,\n): Promise<VerifiedEvent> {\n  const content = {\n    method: 'pay_invoice',\n    params: {\n      invoice,\n    },\n  }\n  const encryptedContent = encrypt(secretKey, pubkey, JSON.stringify(content))\n  const eventTemplate = {\n    kind: NWCWalletRequest,\n    created_at: Math.round(Date.now() / 1000),\n    content: encryptedContent,\n    tags: [['p', pubkey]],\n  }\n\n  return finalizeEvent(eventTemplate, secretKey)\n}\n", "import { schnorr } from '@noble/curves/secp256k1.js'\nimport { bytesToHex, hexToBytes } from '@noble/hashes/utils.js'\nimport { Nostr, Event, EventTemplate, UnsignedEvent, VerifiedEvent, verifiedSymbol, validateEvent } from './core.ts'\nimport { sha256 } from '@noble/hashes/sha2.js'\n\nimport { utf8Encoder } from './utils.ts'\n\nclass JS implements Nostr {\n  generateSecretKey(): Uint8Array {\n    return schnorr.utils.randomSecretKey()\n  }\n  getPublicKey(secretKey: Uint8Array): string {\n    return bytesToHex(schnorr.getPublicKey(secretKey))\n  }\n  finalizeEvent(t: EventTemplate, secretKey: Uint8Array): VerifiedEvent {\n    const event = t as VerifiedEvent\n    event.pubkey = bytesToHex(schnorr.getPublicKey(secretKey))\n    event.id = getEventHash(event)\n    event.sig = bytesToHex(schnorr.sign(hexToBytes(getEventHash(event)), secretKey))\n    event[verifiedSymbol] = true\n    return event\n  }\n  verifyEvent(event: Event): event is VerifiedEvent {\n    if (typeof event[verifiedSymbol] === 'boolean') return event[verifiedSymbol]\n\n    const hash = getEventHash(event)\n    if (hash !== event.id) {\n      event[verifiedSymbol] = false\n      return false\n    }\n\n    try {\n      const valid = schnorr.verify(hexToBytes(event.sig), hexToBytes(hash), hexToBytes(event.pubkey))\n      event[verifiedSymbol] = valid\n      return valid\n    } catch (err) {\n      event[verifiedSymbol] = false\n      return false\n    }\n  }\n}\n\nexport function serializeEvent(evt: UnsignedEvent): string {\n  if (!validateEvent(evt)) throw new Error(\"can't serialize event with wrong or missing properties\")\n  return JSON.stringify([0, evt.pubkey, evt.created_at, evt.kind, evt.tags, evt.content])\n}\n\nexport function getEventHash(event: UnsignedEvent): string {\n  let eventHash = sha256(utf8Encoder.encode(serializeEvent(event)))\n  return bytesToHex(eventHash)\n}\n\nconst i: JS = new JS()\n\nexport const generateSecretKey = i.generateSecretKey\nexport const getPublicKey = i.getPublicKey\nexport const finalizeEvent = i.finalizeEvent\nexport const verifyEvent = i.verifyEvent\nexport * from './core.ts'\n", "export interface Nostr {\n  generateSecretKey(): Uint8Array\n  getPublicKey(secretKey: Uint8Array): string\n  finalizeEvent(event: EventTemplate, secretKey: Uint8Array): VerifiedEvent\n  verifyEvent(event: Event): event is VerifiedEvent\n}\n\n/** Designates a verified event signature. */\nexport const verifiedSymbol = Symbol('verified')\n\nexport type NostrEvent = {\n  kind: number\n  tags: string[][]\n  content: string\n  created_at: number\n  pubkey: string\n  id: string\n  sig: string\n  [verifiedSymbol]?: boolean\n}\n\nexport type Event = NostrEvent\nexport type EventTemplate = Pick<Event, 'kind' | 'tags' | 'content' | 'created_at'>\nexport type UnsignedEvent = Pick<Event, 'kind' | 'tags' | 'content' | 'created_at' | 'pubkey'>\n\n/** An event whose signature has been verified. */\nexport interface VerifiedEvent extends Event {\n  [verifiedSymbol]: true\n}\n\nconst isRecord = (obj: unknown): obj is Record<string, unknown> => obj instanceof Object\n\nexport function validateEvent<T>(event: T): event is T & UnsignedEvent {\n  if (!isRecord(event)) return false\n  if (typeof event.kind !== 'number') return false\n  if (typeof event.content !== 'string') return false\n  if (typeof event.created_at !== 'number') return false\n  if (typeof event.pubkey !== 'string') return false\n  if (!event.pubkey.match(/^[a-f0-9]{64}$/)) return false\n\n  if (!Array.isArray(event.tags)) return false\n  for (let i = 0; i < event.tags.length; i++) {\n    let tag = event.tags[i]\n    if (!Array.isArray(tag)) return false\n    for (let j = 0; j < tag.length; j++) {\n      if (typeof tag[j] !== 'string') return false\n    }\n  }\n\n  return true\n}\n\n/**\n * Sort events in reverse-chronological order by the `created_at` timestamp,\n * and then by the event `id` (lexicographically) in case of ties.\n * This mutates the array.\n */\nexport function sortEvents(events: Event[]): Event[] {\n  return events.sort((a: NostrEvent, b: NostrEvent): number => {\n    if (a.created_at !== b.created_at) {\n      return b.created_at - a.created_at\n    }\n    return a.id.localeCompare(b.id)\n  })\n}\n", "import type { Event } from './core.ts'\n\nexport const utf8Decoder: TextDecoder = new TextDecoder('utf-8')\nexport const utf8Encoder: TextEncoder = new TextEncoder()\n\nexport { bytesToHex, hexToBytes } from '@noble/hashes/utils.js'\n\nexport function normalizeURL(url: string): string {\n  try {\n    if (url.indexOf('://') === -1) url = 'wss://' + url\n    let p = new URL(url)\n    if (p.protocol === 'http:') p.protocol = 'ws:'\n    else if (p.protocol === 'https:') p.protocol = 'wss:'\n    p.pathname = p.pathname.replace(/\\/+/g, '/')\n    if (p.pathname.endsWith('/')) p.pathname = p.pathname.slice(0, -1)\n    if ((p.port === '80' && p.protocol === 'ws:') || (p.port === '443' && p.protocol === 'wss:')) p.port = ''\n    p.searchParams.sort()\n    p.hash = ''\n    return p.toString()\n  } catch (e) {\n    throw new Error(`Invalid URL: ${url}`)\n  }\n}\n\nexport function insertEventIntoDescendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return b.created_at - event.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function insertEventIntoAscendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return event.created_at - b.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function binarySearch<T>(arr: T[], compare: (b: T) => number): [number, boolean] {\n  let start = 0\n  let end = arr.length - 1\n\n  while (start <= end) {\n    const mid = Math.floor((start + end) / 2)\n    const cmp = compare(arr[mid])\n\n    if (cmp === 0) {\n      return [mid, true]\n    }\n\n    if (cmp < 0) {\n      end = mid - 1\n    } else {\n      start = mid + 1\n    }\n  }\n\n  return [start, false]\n}\n\nexport class QueueNode<V> {\n  public value: V\n  public next: QueueNode<V> | null = null\n  public prev: QueueNode<V> | null = null\n\n  constructor(message: V) {\n    this.value = message\n  }\n}\n\nexport class Queue<V> {\n  public first: QueueNode<V> | null\n  public last: QueueNode<V> | null\n\n  constructor() {\n    this.first = null\n    this.last = null\n  }\n\n  enqueue(value: V): boolean {\n    const newNode = new QueueNode(value)\n    if (!this.last) {\n      // list is empty\n      this.first = newNode\n      this.last = newNode\n    } else if (this.last === this.first) {\n      // list has a single element\n      this.last = newNode\n      this.last.prev = this.first\n      this.first.next = newNode\n    } else {\n      // list has elements, add as last\n      newNode.prev = this.last\n      this.last.next = newNode\n      this.last = newNode\n    }\n    return true\n  }\n\n  dequeue(): V | null {\n    if (!this.first) return null\n\n    if (this.first === this.last) {\n      const target = this.first\n      this.first = null\n      this.last = null\n      return target.value\n    }\n\n    const target = this.first\n    this.first = target.next\n    if (this.first) {\n      this.first.prev = null // fix: clean up prev pointer\n    }\n\n    return target.value\n  }\n}\n", "import { NostrEvent, validateEvent } from './pure.ts'\n\n/** Events are **regular**, which means they're all expected to be stored by relays. */\nexport function isRegularKind(kind: number): boolean {\n  return kind < 10000 && kind !== 0 && kind !== 3\n}\n\n/** Events are **replaceable**, which means that, for each combination of `pubkey` and `kind`, only the latest event is expected to (SHOULD) be stored by relays, older versions are expected to be discarded. */\nexport function isReplaceableKind(kind: number): boolean {\n  return kind === 0 || kind === 3 || (10000 <= kind && kind < 20000)\n}\n\n/** Events are **ephemeral**, which means they are not expected to be stored by relays. */\nexport function isEphemeralKind(kind: number): boolean {\n  return 20000 <= kind && kind < 30000\n}\n\n/** Events are **addressable**, which means that, for each combination of `pubkey`, `kind` and the `d` tag, only the latest event is expected to be stored by relays, older versions are expected to be discarded. */\nexport function isAddressableKind(kind: number): boolean {\n  return 30000 <= kind && kind < 40000\n}\n\n/** Classification of the event kind. */\nexport type KindClassification = 'regular' | 'replaceable' | 'ephemeral' | 'parameterized' | 'unknown'\n\n/** Determine the classification of this kind of event if known, or `unknown`. */\nexport function classifyKind(kind: number): KindClassification {\n  if (isRegularKind(kind)) return 'regular'\n  if (isReplaceableKind(kind)) return 'replaceable'\n  if (isEphemeralKind(kind)) return 'ephemeral'\n  if (isAddressableKind(kind)) return 'parameterized'\n  return 'unknown'\n}\n\nexport function isKind<T extends number>(event: unknown, kind: T | Array<T>): event is NostrEvent & { kind: T } {\n  const kindAsArray: number[] = kind instanceof Array ? kind : [kind]\n  return (validateEvent(event) && kindAsArray.includes(event.kind)) || false\n}\n\nexport const Metadata = 0\nexport type Metadata = typeof Metadata\nexport const ShortTextNote = 1\nexport type ShortTextNote = typeof ShortTextNote\nexport const RecommendRelay = 2\nexport type RecommendRelay = typeof RecommendRelay\nexport const Contacts = 3\nexport type Contacts = typeof Contacts\nexport const EncryptedDirectMessage = 4\nexport type EncryptedDirectMessage = typeof EncryptedDirectMessage\nexport const EventDeletion = 5\nexport type EventDeletion = typeof EventDeletion\nexport const Repost = 6\nexport type Repost = typeof Repost\nexport const Reaction = 7\nexport type Reaction = typeof Reaction\nexport const BadgeAward = 8\nexport type BadgeAward = typeof BadgeAward\nexport const ChatMessage = 9\nexport type ChatMessage = typeof ChatMessage\nexport const ForumThread = 11\nexport type ForumThread = typeof ForumThread\nexport const Seal = 13\nexport type Seal = typeof Seal\nexport const PrivateDirectMessage = 14\nexport type PrivateDirectMessage = typeof PrivateDirectMessage\nexport const FileMessage = 15\nexport type FileMessage = typeof FileMessage\nexport const GenericRepost = 16\nexport type GenericRepost = typeof GenericRepost\nexport const Photo = 20\nexport type Photo = typeof Photo\nexport const NormalVideo = 21\nexport type NormalVideo = typeof NormalVideo\nexport const ShortVideo = 22\nexport type ShortVideo = typeof ShortVideo\nexport const ChannelCreation = 40\nexport type ChannelCreation = typeof ChannelCreation\nexport const ChannelMetadata = 41\nexport type ChannelMetadata = typeof ChannelMetadata\nexport const ChannelMessage = 42\nexport type ChannelMessage = typeof ChannelMessage\nexport const ChannelHideMessage = 43\nexport type ChannelHideMessage = typeof ChannelHideMessage\nexport const ChannelMuteUser = 44\nexport type ChannelMuteUser = typeof ChannelMuteUser\nexport const OpenTimestamps = 1040\nexport type OpenTimestamps = typeof OpenTimestamps\nexport const GiftWrap = 1059\nexport type GiftWrap = typeof GiftWrap\nexport const Poll = 1068\nexport type Poll = typeof Poll\nexport const FileMetadata = 1063\nexport type FileMetadata = typeof FileMetadata\nexport const Comment = 1111\nexport type Comment = typeof Comment\nexport const LiveChatMessage = 1311\nexport type LiveChatMessage = typeof LiveChatMessage\nexport const Voice = 1222\nexport type Voice = typeof Voice\nexport const VoiceComment = 1244\nexport type VoiceComment = typeof VoiceComment\nexport const ProblemTracker = 1971\nexport type ProblemTracker = typeof ProblemTracker\nexport const Report = 1984\nexport type Report = typeof Report\nexport const Reporting = 1984\nexport type Reporting = typeof Reporting\nexport const Label = 1985\nexport type Label = typeof Label\nexport const CommunityPostApproval = 4550\nexport type CommunityPostApproval = typeof CommunityPostApproval\nexport const JobRequest = 5999\nexport type JobRequest = typeof JobRequest\nexport const JobResult = 6999\nexport type JobResult = typeof JobResult\nexport const JobFeedback = 7000\nexport type JobFeedback = typeof JobFeedback\nexport const ZapGoal = 9041\nexport type ZapGoal = typeof ZapGoal\nexport const ZapRequest = 9734\nexport type ZapRequest = typeof ZapRequest\nexport const Zap = 9735\nexport type Zap = typeof Zap\nexport const Highlights = 9802\nexport type Highlights = typeof Highlights\nexport const PollResponse = 1018\nexport type PollResponse = typeof PollResponse\nexport const Mutelist = 10000\nexport type Mutelist = typeof Mutelist\nexport const Pinlist = 10001\nexport type Pinlist = typeof Pinlist\nexport const RelayList = 10002\nexport type RelayList = typeof RelayList\nexport const BookmarkList = 10003\nexport type BookmarkList = typeof BookmarkList\nexport const CommunitiesList = 10004\nexport type CommunitiesList = typeof CommunitiesList\nexport const PublicChatsList = 10005\nexport type PublicChatsList = typeof PublicChatsList\nexport const BlockedRelaysList = 10006\nexport type BlockedRelaysList = typeof BlockedRelaysList\nexport const SearchRelaysList = 10007\nexport type SearchRelaysList = typeof SearchRelaysList\nexport const FavoriteRelays = 10012\nexport type FavoriteRelays = typeof FavoriteRelays\nexport const InterestsList = 10015\nexport type InterestsList = typeof InterestsList\nexport const UserEmojiList = 10030\nexport type UserEmojiList = typeof UserEmojiList\nexport const DirectMessageRelaysList = 10050\nexport type DirectMessageRelaysList = typeof DirectMessageRelaysList\nexport const FileServerPreference = 10096\nexport type FileServerPreference = typeof FileServerPreference\nexport const BlossomServerList = 10063\nexport type BlossomServerList = typeof BlossomServerList\nexport const NWCWalletInfo = 13194\nexport type NWCWalletInfo = typeof NWCWalletInfo\nexport const LightningPubRPC = 21000\nexport type LightningPubRPC = typeof LightningPubRPC\nexport const ClientAuth = 22242\nexport type ClientAuth = typeof ClientAuth\nexport const NWCWalletRequest = 23194\nexport type NWCWalletRequest = typeof NWCWalletRequest\nexport const NWCWalletResponse = 23195\nexport type NWCWalletResponse = typeof NWCWalletResponse\nexport const NostrConnect = 24133\nexport type NostrConnect = typeof NostrConnect\nexport const HTTPAuth = 27235\nexport type HTTPAuth = typeof HTTPAuth\nexport const Followsets = 30000\nexport type Followsets = typeof Followsets\nexport const Genericlists = 30001\nexport type Genericlists = typeof Genericlists\nexport const Relaysets = 30002\nexport type Relaysets = typeof Relaysets\nexport const Bookmarksets = 30003\nexport type Bookmarksets = typeof Bookmarksets\nexport const Curationsets = 30004\nexport type Curationsets = typeof Curationsets\nexport const ProfileBadges = 30008\nexport type ProfileBadges = typeof ProfileBadges\nexport const BadgeDefinition = 30009\nexport type BadgeDefinition = typeof BadgeDefinition\nexport const Interestsets = 30015\nexport type Interestsets = typeof Interestsets\nexport const CreateOrUpdateStall = 30017\nexport type CreateOrUpdateStall = typeof CreateOrUpdateStall\nexport const CreateOrUpdateProduct = 30018\nexport type CreateOrUpdateProduct = typeof CreateOrUpdateProduct\nexport const LongFormArticle = 30023\nexport type LongFormArticle = typeof LongFormArticle\nexport const DraftLong = 30024\nexport type DraftLong = typeof DraftLong\nexport const Emojisets = 30030\nexport type Emojisets = typeof Emojisets\nexport const Application = 30078\nexport type Application = typeof Application\nexport const LiveEvent = 30311\nexport type LiveEvent = typeof LiveEvent\nexport const UserStatuses = 30315\nexport type UserStatuses = typeof UserStatuses\nexport const ClassifiedListing = 30402\nexport type ClassifiedListing = typeof ClassifiedListing\nexport const DraftClassifiedListing = 30403\nexport type DraftClassifiedListing = typeof DraftClassifiedListing\nexport const Date = 31922\nexport type Date = typeof Date\nexport const Time = 31923\nexport type Time = typeof Time\nexport const Calendar = 31924\nexport type Calendar = typeof Calendar\nexport const CalendarEventRSVP = 31925\nexport type CalendarEventRSVP = typeof CalendarEventRSVP\nexport const RelayReview = 31987\nexport type RelayReview = typeof RelayReview\nexport const Handlerrecommendation = 31989\nexport type Handlerrecommendation = typeof Handlerrecommendation\nexport const Handlerinformation = 31990\nexport type Handlerinformation = typeof Handlerinformation\nexport const CommunityDefinition = 34550\nexport type CommunityDefinition = typeof CommunityDefinition\nexport const GroupMetadata = 39000\nexport type GroupMetadata = typeof GroupMetadata\n", "import { hexToBytes, randomBytes } from '@noble/hashes/utils.js'\nimport { secp256k1 } from '@noble/curves/secp256k1.js'\nimport { cbc } from '@noble/ciphers/aes.js'\nimport { base64 } from '@scure/base'\n\nimport { utf8Decoder, utf8Encoder } from './utils.ts'\n\nexport function encrypt(secretKey: string | Uint8Array, pubkey: string, text: string): string {\n  const privkey: Uint8Array = secretKey instanceof Uint8Array ? secretKey : hexToBytes(secretKey)\n  const key = secp256k1.getSharedSecret(privkey, hexToBytes('02' + pubkey))\n  const normalizedKey = getNormalizedX(key)\n\n  let iv = Uint8Array.from(randomBytes(16))\n  let plaintext = utf8Encoder.encode(text)\n\n  let ciphertext = cbc(normalizedKey, iv).encrypt(plaintext)\n\n  let ctb64 = base64.encode(new Uint8Array(ciphertext))\n  let ivb64 = base64.encode(new Uint8Array(iv.buffer))\n\n  return `${ctb64}?iv=${ivb64}`\n}\n\nexport function decrypt(secretKey: string | Uint8Array, pubkey: string, data: string): string {\n  const privkey: Uint8Array = secretKey instanceof Uint8Array ? secretKey : hexToBytes(secretKey)\n  let [ctb64, ivb64] = data.split('?iv=')\n  let key = secp256k1.getSharedSecret(privkey, hexToBytes('02' + pubkey))\n  let normalizedKey = getNormalizedX(key)\n\n  let iv = base64.decode(ivb64)\n  let ciphertext = base64.decode(ctb64)\n\n  let plaintext = cbc(normalizedKey, iv).decrypt(ciphertext)\n\n  return utf8Decoder.decode(plaintext)\n}\n\nfunction getNormalizedX(key: Uint8Array): Uint8Array {\n  return key.slice(1, 33)\n}\n"],
+  "mappings": "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",
   "names": ["import_utils", "i", "import_utils", "import_secp256k1"]
 }
Index: package/lib/esm/nip47.js.map
===================================================================
--- package/lib/esm/nip47.js.map
+++ package/lib/esm/nip47.js.map
@@ -1,7 +1,7 @@
 {
   "version": 3,
   "sources": ["../../pure.ts", "../../core.ts", "../../utils.ts", "../../kinds.ts", "../../nip04.ts", "../../nip47.ts"],
-  "sourcesContent": ["import { schnorr } from '@noble/curves/secp256k1'\nimport { bytesToHex } from '@noble/hashes/utils'\nimport { Nostr, Event, EventTemplate, UnsignedEvent, VerifiedEvent, verifiedSymbol, validateEvent } from './core.ts'\nimport { sha256 } from '@noble/hashes/sha256'\n\nimport { utf8Encoder } from './utils.ts'\n\nclass JS implements Nostr {\n  generateSecretKey(): Uint8Array {\n    return schnorr.utils.randomPrivateKey()\n  }\n  getPublicKey(secretKey: Uint8Array): string {\n    return bytesToHex(schnorr.getPublicKey(secretKey))\n  }\n  finalizeEvent(t: EventTemplate, secretKey: Uint8Array): VerifiedEvent {\n    const event = t as VerifiedEvent\n    event.pubkey = bytesToHex(schnorr.getPublicKey(secretKey))\n    event.id = getEventHash(event)\n    event.sig = bytesToHex(schnorr.sign(getEventHash(event), secretKey))\n    event[verifiedSymbol] = true\n    return event\n  }\n  verifyEvent(event: Event): event is VerifiedEvent {\n    if (typeof event[verifiedSymbol] === 'boolean') return event[verifiedSymbol]\n\n    const hash = getEventHash(event)\n    if (hash !== event.id) {\n      event[verifiedSymbol] = false\n      return false\n    }\n\n    try {\n      const valid = schnorr.verify(event.sig, hash, event.pubkey)\n      event[verifiedSymbol] = valid\n      return valid\n    } catch (err) {\n      event[verifiedSymbol] = false\n      return false\n    }\n  }\n}\n\nexport function serializeEvent(evt: UnsignedEvent): string {\n  if (!validateEvent(evt)) throw new Error(\"can't serialize event with wrong or missing properties\")\n  return JSON.stringify([0, evt.pubkey, evt.created_at, evt.kind, evt.tags, evt.content])\n}\n\nexport function getEventHash(event: UnsignedEvent): string {\n  let eventHash = sha256(utf8Encoder.encode(serializeEvent(event)))\n  return bytesToHex(eventHash)\n}\n\nconst i: JS = new JS()\n\nexport const generateSecretKey = i.generateSecretKey\nexport const getPublicKey = i.getPublicKey\nexport const finalizeEvent = i.finalizeEvent\nexport const verifyEvent = i.verifyEvent\nexport * from './core.ts'\n", "export interface Nostr {\n  generateSecretKey(): Uint8Array\n  getPublicKey(secretKey: Uint8Array): string\n  finalizeEvent(event: EventTemplate, secretKey: Uint8Array): VerifiedEvent\n  verifyEvent(event: Event): event is VerifiedEvent\n}\n\n/** Designates a verified event signature. */\nexport const verifiedSymbol = Symbol('verified')\n\nexport type NostrEvent = {\n  kind: number\n  tags: string[][]\n  content: string\n  created_at: number\n  pubkey: string\n  id: string\n  sig: string\n  [verifiedSymbol]?: boolean\n}\n\nexport type Event = NostrEvent\nexport type EventTemplate = Pick<Event, 'kind' | 'tags' | 'content' | 'created_at'>\nexport type UnsignedEvent = Pick<Event, 'kind' | 'tags' | 'content' | 'created_at' | 'pubkey'>\n\n/** An event whose signature has been verified. */\nexport interface VerifiedEvent extends Event {\n  [verifiedSymbol]: true\n}\n\nconst isRecord = (obj: unknown): obj is Record<string, unknown> => obj instanceof Object\n\nexport function validateEvent<T>(event: T): event is T & UnsignedEvent {\n  if (!isRecord(event)) return false\n  if (typeof event.kind !== 'number') return false\n  if (typeof event.content !== 'string') return false\n  if (typeof event.created_at !== 'number') return false\n  if (typeof event.pubkey !== 'string') return false\n  if (!event.pubkey.match(/^[a-f0-9]{64}$/)) return false\n\n  if (!Array.isArray(event.tags)) return false\n  for (let i = 0; i < event.tags.length; i++) {\n    let tag = event.tags[i]\n    if (!Array.isArray(tag)) return false\n    for (let j = 0; j < tag.length; j++) {\n      if (typeof tag[j] !== 'string') return false\n    }\n  }\n\n  return true\n}\n\n/**\n * Sort events in reverse-chronological order by the `created_at` timestamp,\n * and then by the event `id` (lexicographically) in case of ties.\n * This mutates the array.\n */\nexport function sortEvents(events: Event[]): Event[] {\n  return events.sort((a: NostrEvent, b: NostrEvent): number => {\n    if (a.created_at !== b.created_at) {\n      return b.created_at - a.created_at\n    }\n    return a.id.localeCompare(b.id)\n  })\n}\n", "import type { Event } from './core.ts'\n\nexport const utf8Decoder: TextDecoder = new TextDecoder('utf-8')\nexport const utf8Encoder: TextEncoder = new TextEncoder()\n\nexport { bytesToHex, hexToBytes } from '@noble/hashes/utils'\n\nexport function normalizeURL(url: string): string {\n  try {\n    if (url.indexOf('://') === -1) url = 'wss://' + url\n    let p = new URL(url)\n    if (p.protocol === 'http:') p.protocol = 'ws:'\n    else if (p.protocol === 'https:') p.protocol = 'wss:'\n    p.pathname = p.pathname.replace(/\\/+/g, '/')\n    if (p.pathname.endsWith('/')) p.pathname = p.pathname.slice(0, -1)\n    if ((p.port === '80' && p.protocol === 'ws:') || (p.port === '443' && p.protocol === 'wss:')) p.port = ''\n    p.searchParams.sort()\n    p.hash = ''\n    return p.toString()\n  } catch (e) {\n    throw new Error(`Invalid URL: ${url}`)\n  }\n}\n\nexport function insertEventIntoDescendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return b.created_at - event.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function insertEventIntoAscendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return event.created_at - b.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function binarySearch<T>(arr: T[], compare: (b: T) => number): [number, boolean] {\n  let start = 0\n  let end = arr.length - 1\n\n  while (start <= end) {\n    const mid = Math.floor((start + end) / 2)\n    const cmp = compare(arr[mid])\n\n    if (cmp === 0) {\n      return [mid, true]\n    }\n\n    if (cmp < 0) {\n      end = mid - 1\n    } else {\n      start = mid + 1\n    }\n  }\n\n  return [start, false]\n}\n\nexport class QueueNode<V> {\n  public value: V\n  public next: QueueNode<V> | null = null\n  public prev: QueueNode<V> | null = null\n\n  constructor(message: V) {\n    this.value = message\n  }\n}\n\nexport class Queue<V> {\n  public first: QueueNode<V> | null\n  public last: QueueNode<V> | null\n\n  constructor() {\n    this.first = null\n    this.last = null\n  }\n\n  enqueue(value: V): boolean {\n    const newNode = new QueueNode(value)\n    if (!this.last) {\n      // list is empty\n      this.first = newNode\n      this.last = newNode\n    } else if (this.last === this.first) {\n      // list has a single element\n      this.last = newNode\n      this.last.prev = this.first\n      this.first.next = newNode\n    } else {\n      // list has elements, add as last\n      newNode.prev = this.last\n      this.last.next = newNode\n      this.last = newNode\n    }\n    return true\n  }\n\n  dequeue(): V | null {\n    if (!this.first) return null\n\n    if (this.first === this.last) {\n      const target = this.first\n      this.first = null\n      this.last = null\n      return target.value\n    }\n\n    const target = this.first\n    this.first = target.next\n    if (this.first) {\n      this.first.prev = null // fix: clean up prev pointer\n    }\n\n    return target.value\n  }\n}\n", "import { NostrEvent, validateEvent } from './pure.ts'\n\n/** Events are **regular**, which means they're all expected to be stored by relays. */\nexport function isRegularKind(kind: number): boolean {\n  return kind < 10000 && kind !== 0 && kind !== 3\n}\n\n/** Events are **replaceable**, which means that, for each combination of `pubkey` and `kind`, only the latest event is expected to (SHOULD) be stored by relays, older versions are expected to be discarded. */\nexport function isReplaceableKind(kind: number): boolean {\n  return kind === 0 || kind === 3 || (10000 <= kind && kind < 20000)\n}\n\n/** Events are **ephemeral**, which means they are not expected to be stored by relays. */\nexport function isEphemeralKind(kind: number): boolean {\n  return 20000 <= kind && kind < 30000\n}\n\n/** Events are **addressable**, which means that, for each combination of `pubkey`, `kind` and the `d` tag, only the latest event is expected to be stored by relays, older versions are expected to be discarded. */\nexport function isAddressableKind(kind: number): boolean {\n  return 30000 <= kind && kind < 40000\n}\n\n/** Classification of the event kind. */\nexport type KindClassification = 'regular' | 'replaceable' | 'ephemeral' | 'parameterized' | 'unknown'\n\n/** Determine the classification of this kind of event if known, or `unknown`. */\nexport function classifyKind(kind: number): KindClassification {\n  if (isRegularKind(kind)) return 'regular'\n  if (isReplaceableKind(kind)) return 'replaceable'\n  if (isEphemeralKind(kind)) return 'ephemeral'\n  if (isAddressableKind(kind)) return 'parameterized'\n  return 'unknown'\n}\n\nexport function isKind<T extends number>(event: unknown, kind: T | Array<T>): event is NostrEvent & { kind: T } {\n  const kindAsArray: number[] = kind instanceof Array ? kind : [kind]\n  return (validateEvent(event) && kindAsArray.includes(event.kind)) || false\n}\n\nexport const Metadata = 0\nexport type Metadata = typeof Metadata\nexport const ShortTextNote = 1\nexport type ShortTextNote = typeof ShortTextNote\nexport const RecommendRelay = 2\nexport type RecommendRelay = typeof RecommendRelay\nexport const Contacts = 3\nexport type Contacts = typeof Contacts\nexport const EncryptedDirectMessage = 4\nexport type EncryptedDirectMessage = typeof EncryptedDirectMessage\nexport const EventDeletion = 5\nexport type EventDeletion = typeof EventDeletion\nexport const Repost = 6\nexport type Repost = typeof Repost\nexport const Reaction = 7\nexport type Reaction = typeof Reaction\nexport const BadgeAward = 8\nexport type BadgeAward = typeof BadgeAward\nexport const ChatMessage = 9\nexport type ChatMessage = typeof ChatMessage\nexport const ForumThread = 11\nexport type ForumThread = typeof ForumThread\nexport const Seal = 13\nexport type Seal = typeof Seal\nexport const PrivateDirectMessage = 14\nexport type PrivateDirectMessage = typeof PrivateDirectMessage\nexport const FileMessage = 15\nexport type FileMessage = typeof FileMessage\nexport const GenericRepost = 16\nexport type GenericRepost = typeof GenericRepost\nexport const Photo = 20\nexport type Photo = typeof Photo\nexport const NormalVideo = 21\nexport type NormalVideo = typeof NormalVideo\nexport const ShortVideo = 22\nexport type ShortVideo = typeof ShortVideo\nexport const ChannelCreation = 40\nexport type ChannelCreation = typeof ChannelCreation\nexport const ChannelMetadata = 41\nexport type ChannelMetadata = typeof ChannelMetadata\nexport const ChannelMessage = 42\nexport type ChannelMessage = typeof ChannelMessage\nexport const ChannelHideMessage = 43\nexport type ChannelHideMessage = typeof ChannelHideMessage\nexport const ChannelMuteUser = 44\nexport type ChannelMuteUser = typeof ChannelMuteUser\nexport const OpenTimestamps = 1040\nexport type OpenTimestamps = typeof OpenTimestamps\nexport const GiftWrap = 1059\nexport type GiftWrap = typeof GiftWrap\nexport const Poll = 1068\nexport type Poll = typeof Poll\nexport const FileMetadata = 1063\nexport type FileMetadata = typeof FileMetadata\nexport const Comment = 1111\nexport type Comment = typeof Comment\nexport const LiveChatMessage = 1311\nexport type LiveChatMessage = typeof LiveChatMessage\nexport const Voice = 1222\nexport type Voice = typeof Voice\nexport const VoiceComment = 1244\nexport type VoiceComment = typeof VoiceComment\nexport const ProblemTracker = 1971\nexport type ProblemTracker = typeof ProblemTracker\nexport const Report = 1984\nexport type Report = typeof Report\nexport const Reporting = 1984\nexport type Reporting = typeof Reporting\nexport const Label = 1985\nexport type Label = typeof Label\nexport const CommunityPostApproval = 4550\nexport type CommunityPostApproval = typeof CommunityPostApproval\nexport const JobRequest = 5999\nexport type JobRequest = typeof JobRequest\nexport const JobResult = 6999\nexport type JobResult = typeof JobResult\nexport const JobFeedback = 7000\nexport type JobFeedback = typeof JobFeedback\nexport const ZapGoal = 9041\nexport type ZapGoal = typeof ZapGoal\nexport const ZapRequest = 9734\nexport type ZapRequest = typeof ZapRequest\nexport const Zap = 9735\nexport type Zap = typeof Zap\nexport const Highlights = 9802\nexport type Highlights = typeof Highlights\nexport const PollResponse = 1018\nexport type PollResponse = typeof PollResponse\nexport const Mutelist = 10000\nexport type Mutelist = typeof Mutelist\nexport const Pinlist = 10001\nexport type Pinlist = typeof Pinlist\nexport const RelayList = 10002\nexport type RelayList = typeof RelayList\nexport const BookmarkList = 10003\nexport type BookmarkList = typeof BookmarkList\nexport const CommunitiesList = 10004\nexport type CommunitiesList = typeof CommunitiesList\nexport const PublicChatsList = 10005\nexport type PublicChatsList = typeof PublicChatsList\nexport const BlockedRelaysList = 10006\nexport type BlockedRelaysList = typeof BlockedRelaysList\nexport const SearchRelaysList = 10007\nexport type SearchRelaysList = typeof SearchRelaysList\nexport const FavoriteRelays = 10012\nexport type FavoriteRelays = typeof FavoriteRelays\nexport const InterestsList = 10015\nexport type InterestsList = typeof InterestsList\nexport const UserEmojiList = 10030\nexport type UserEmojiList = typeof UserEmojiList\nexport const DirectMessageRelaysList = 10050\nexport type DirectMessageRelaysList = typeof DirectMessageRelaysList\nexport const FileServerPreference = 10096\nexport type FileServerPreference = typeof FileServerPreference\nexport const BlossomServerList = 10063\nexport type BlossomServerList = typeof BlossomServerList\nexport const NWCWalletInfo = 13194\nexport type NWCWalletInfo = typeof NWCWalletInfo\nexport const LightningPubRPC = 21000\nexport type LightningPubRPC = typeof LightningPubRPC\nexport const ClientAuth = 22242\nexport type ClientAuth = typeof ClientAuth\nexport const NWCWalletRequest = 23194\nexport type NWCWalletRequest = typeof NWCWalletRequest\nexport const NWCWalletResponse = 23195\nexport type NWCWalletResponse = typeof NWCWalletResponse\nexport const NostrConnect = 24133\nexport type NostrConnect = typeof NostrConnect\nexport const HTTPAuth = 27235\nexport type HTTPAuth = typeof HTTPAuth\nexport const Followsets = 30000\nexport type Followsets = typeof Followsets\nexport const Genericlists = 30001\nexport type Genericlists = typeof Genericlists\nexport const Relaysets = 30002\nexport type Relaysets = typeof Relaysets\nexport const Bookmarksets = 30003\nexport type Bookmarksets = typeof Bookmarksets\nexport const Curationsets = 30004\nexport type Curationsets = typeof Curationsets\nexport const ProfileBadges = 30008\nexport type ProfileBadges = typeof ProfileBadges\nexport const BadgeDefinition = 30009\nexport type BadgeDefinition = typeof BadgeDefinition\nexport const Interestsets = 30015\nexport type Interestsets = typeof Interestsets\nexport const CreateOrUpdateStall = 30017\nexport type CreateOrUpdateStall = typeof CreateOrUpdateStall\nexport const CreateOrUpdateProduct = 30018\nexport type CreateOrUpdateProduct = typeof CreateOrUpdateProduct\nexport const LongFormArticle = 30023\nexport type LongFormArticle = typeof LongFormArticle\nexport const DraftLong = 30024\nexport type DraftLong = typeof DraftLong\nexport const Emojisets = 30030\nexport type Emojisets = typeof Emojisets\nexport const Application = 30078\nexport type Application = typeof Application\nexport const LiveEvent = 30311\nexport type LiveEvent = typeof LiveEvent\nexport const UserStatuses = 30315\nexport type UserStatuses = typeof UserStatuses\nexport const ClassifiedListing = 30402\nexport type ClassifiedListing = typeof ClassifiedListing\nexport const DraftClassifiedListing = 30403\nexport type DraftClassifiedListing = typeof DraftClassifiedListing\nexport const Date = 31922\nexport type Date = typeof Date\nexport const Time = 31923\nexport type Time = typeof Time\nexport const Calendar = 31924\nexport type Calendar = typeof Calendar\nexport const CalendarEventRSVP = 31925\nexport type CalendarEventRSVP = typeof CalendarEventRSVP\nexport const RelayReview = 31987\nexport type RelayReview = typeof RelayReview\nexport const Handlerrecommendation = 31989\nexport type Handlerrecommendation = typeof Handlerrecommendation\nexport const Handlerinformation = 31990\nexport type Handlerinformation = typeof Handlerinformation\nexport const CommunityDefinition = 34550\nexport type CommunityDefinition = typeof CommunityDefinition\nexport const GroupMetadata = 39000\nexport type GroupMetadata = typeof GroupMetadata\n", "import { bytesToHex, randomBytes } from '@noble/hashes/utils'\nimport { secp256k1 } from '@noble/curves/secp256k1'\nimport { cbc } from '@noble/ciphers/aes'\nimport { base64 } from '@scure/base'\n\nimport { utf8Decoder, utf8Encoder } from './utils.ts'\n\nexport function encrypt(secretKey: string | Uint8Array, pubkey: string, text: string): string {\n  const privkey: string = secretKey instanceof Uint8Array ? bytesToHex(secretKey) : secretKey\n  const key = secp256k1.getSharedSecret(privkey, '02' + pubkey)\n  const normalizedKey = getNormalizedX(key)\n\n  let iv = Uint8Array.from(randomBytes(16))\n  let plaintext = utf8Encoder.encode(text)\n\n  let ciphertext = cbc(normalizedKey, iv).encrypt(plaintext)\n\n  let ctb64 = base64.encode(new Uint8Array(ciphertext))\n  let ivb64 = base64.encode(new Uint8Array(iv.buffer))\n\n  return `${ctb64}?iv=${ivb64}`\n}\n\nexport function decrypt(secretKey: string | Uint8Array, pubkey: string, data: string): string {\n  const privkey: string = secretKey instanceof Uint8Array ? bytesToHex(secretKey) : secretKey\n  let [ctb64, ivb64] = data.split('?iv=')\n  let key = secp256k1.getSharedSecret(privkey, '02' + pubkey)\n  let normalizedKey = getNormalizedX(key)\n\n  let iv = base64.decode(ivb64)\n  let ciphertext = base64.decode(ctb64)\n\n  let plaintext = cbc(normalizedKey, iv).decrypt(ciphertext)\n\n  return utf8Decoder.decode(plaintext)\n}\n\nfunction getNormalizedX(key: Uint8Array): Uint8Array {\n  return key.slice(1, 33)\n}\n", "import { type VerifiedEvent, finalizeEvent } from './pure.ts'\nimport { NWCWalletRequest } from './kinds.ts'\nimport { encrypt } from './nip04.ts'\n\ninterface NWCConnection {\n  pubkey: string\n  relay: string\n  secret: string\n}\n\nexport function parseConnectionString(connectionString: string): NWCConnection {\n  const { host, pathname, searchParams } = new URL(connectionString)\n  const pubkey = pathname || host\n  const relay = searchParams.get('relay')\n  const secret = searchParams.get('secret')\n\n  if (!pubkey || !relay || !secret) {\n    throw new Error('invalid connection string')\n  }\n\n  return { pubkey, relay, secret }\n}\n\nexport async function makeNwcRequestEvent(\n  pubkey: string,\n  secretKey: Uint8Array,\n  invoice: string,\n): Promise<VerifiedEvent> {\n  const content = {\n    method: 'pay_invoice',\n    params: {\n      invoice,\n    },\n  }\n  const encryptedContent = encrypt(secretKey, pubkey, JSON.stringify(content))\n  const eventTemplate = {\n    kind: NWCWalletRequest,\n    created_at: Math.round(Date.now() / 1000),\n    content: encryptedContent,\n    tags: [['p', pubkey]],\n  }\n\n  return finalizeEvent(eventTemplate, secretKey)\n}\n"],
-  "mappings": "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",
-  "names": ["bytesToHex", "i", "bytesToHex", "bytesToHex", "bytesToHex"]
+  "sourcesContent": ["import { schnorr } from '@noble/curves/secp256k1.js'\nimport { bytesToHex, hexToBytes } from '@noble/hashes/utils.js'\nimport { Nostr, Event, EventTemplate, UnsignedEvent, VerifiedEvent, verifiedSymbol, validateEvent } from './core.ts'\nimport { sha256 } from '@noble/hashes/sha2.js'\n\nimport { utf8Encoder } from './utils.ts'\n\nclass JS implements Nostr {\n  generateSecretKey(): Uint8Array {\n    return schnorr.utils.randomSecretKey()\n  }\n  getPublicKey(secretKey: Uint8Array): string {\n    return bytesToHex(schnorr.getPublicKey(secretKey))\n  }\n  finalizeEvent(t: EventTemplate, secretKey: Uint8Array): VerifiedEvent {\n    const event = t as VerifiedEvent\n    event.pubkey = bytesToHex(schnorr.getPublicKey(secretKey))\n    event.id = getEventHash(event)\n    event.sig = bytesToHex(schnorr.sign(hexToBytes(getEventHash(event)), secretKey))\n    event[verifiedSymbol] = true\n    return event\n  }\n  verifyEvent(event: Event): event is VerifiedEvent {\n    if (typeof event[verifiedSymbol] === 'boolean') return event[verifiedSymbol]\n\n    const hash = getEventHash(event)\n    if (hash !== event.id) {\n      event[verifiedSymbol] = false\n      return false\n    }\n\n    try {\n      const valid = schnorr.verify(hexToBytes(event.sig), hexToBytes(hash), hexToBytes(event.pubkey))\n      event[verifiedSymbol] = valid\n      return valid\n    } catch (err) {\n      event[verifiedSymbol] = false\n      return false\n    }\n  }\n}\n\nexport function serializeEvent(evt: UnsignedEvent): string {\n  if (!validateEvent(evt)) throw new Error(\"can't serialize event with wrong or missing properties\")\n  return JSON.stringify([0, evt.pubkey, evt.created_at, evt.kind, evt.tags, evt.content])\n}\n\nexport function getEventHash(event: UnsignedEvent): string {\n  let eventHash = sha256(utf8Encoder.encode(serializeEvent(event)))\n  return bytesToHex(eventHash)\n}\n\nconst i: JS = new JS()\n\nexport const generateSecretKey = i.generateSecretKey\nexport const getPublicKey = i.getPublicKey\nexport const finalizeEvent = i.finalizeEvent\nexport const verifyEvent = i.verifyEvent\nexport * from './core.ts'\n", "export interface Nostr {\n  generateSecretKey(): Uint8Array\n  getPublicKey(secretKey: Uint8Array): string\n  finalizeEvent(event: EventTemplate, secretKey: Uint8Array): VerifiedEvent\n  verifyEvent(event: Event): event is VerifiedEvent\n}\n\n/** Designates a verified event signature. */\nexport const verifiedSymbol = Symbol('verified')\n\nexport type NostrEvent = {\n  kind: number\n  tags: string[][]\n  content: string\n  created_at: number\n  pubkey: string\n  id: string\n  sig: string\n  [verifiedSymbol]?: boolean\n}\n\nexport type Event = NostrEvent\nexport type EventTemplate = Pick<Event, 'kind' | 'tags' | 'content' | 'created_at'>\nexport type UnsignedEvent = Pick<Event, 'kind' | 'tags' | 'content' | 'created_at' | 'pubkey'>\n\n/** An event whose signature has been verified. */\nexport interface VerifiedEvent extends Event {\n  [verifiedSymbol]: true\n}\n\nconst isRecord = (obj: unknown): obj is Record<string, unknown> => obj instanceof Object\n\nexport function validateEvent<T>(event: T): event is T & UnsignedEvent {\n  if (!isRecord(event)) return false\n  if (typeof event.kind !== 'number') return false\n  if (typeof event.content !== 'string') return false\n  if (typeof event.created_at !== 'number') return false\n  if (typeof event.pubkey !== 'string') return false\n  if (!event.pubkey.match(/^[a-f0-9]{64}$/)) return false\n\n  if (!Array.isArray(event.tags)) return false\n  for (let i = 0; i < event.tags.length; i++) {\n    let tag = event.tags[i]\n    if (!Array.isArray(tag)) return false\n    for (let j = 0; j < tag.length; j++) {\n      if (typeof tag[j] !== 'string') return false\n    }\n  }\n\n  return true\n}\n\n/**\n * Sort events in reverse-chronological order by the `created_at` timestamp,\n * and then by the event `id` (lexicographically) in case of ties.\n * This mutates the array.\n */\nexport function sortEvents(events: Event[]): Event[] {\n  return events.sort((a: NostrEvent, b: NostrEvent): number => {\n    if (a.created_at !== b.created_at) {\n      return b.created_at - a.created_at\n    }\n    return a.id.localeCompare(b.id)\n  })\n}\n", "import type { Event } from './core.ts'\n\nexport const utf8Decoder: TextDecoder = new TextDecoder('utf-8')\nexport const utf8Encoder: TextEncoder = new TextEncoder()\n\nexport { bytesToHex, hexToBytes } from '@noble/hashes/utils.js'\n\nexport function normalizeURL(url: string): string {\n  try {\n    if (url.indexOf('://') === -1) url = 'wss://' + url\n    let p = new URL(url)\n    if (p.protocol === 'http:') p.protocol = 'ws:'\n    else if (p.protocol === 'https:') p.protocol = 'wss:'\n    p.pathname = p.pathname.replace(/\\/+/g, '/')\n    if (p.pathname.endsWith('/')) p.pathname = p.pathname.slice(0, -1)\n    if ((p.port === '80' && p.protocol === 'ws:') || (p.port === '443' && p.protocol === 'wss:')) p.port = ''\n    p.searchParams.sort()\n    p.hash = ''\n    return p.toString()\n  } catch (e) {\n    throw new Error(`Invalid URL: ${url}`)\n  }\n}\n\nexport function insertEventIntoDescendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return b.created_at - event.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function insertEventIntoAscendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return event.created_at - b.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function binarySearch<T>(arr: T[], compare: (b: T) => number): [number, boolean] {\n  let start = 0\n  let end = arr.length - 1\n\n  while (start <= end) {\n    const mid = Math.floor((start + end) / 2)\n    const cmp = compare(arr[mid])\n\n    if (cmp === 0) {\n      return [mid, true]\n    }\n\n    if (cmp < 0) {\n      end = mid - 1\n    } else {\n      start = mid + 1\n    }\n  }\n\n  return [start, false]\n}\n\nexport class QueueNode<V> {\n  public value: V\n  public next: QueueNode<V> | null = null\n  public prev: QueueNode<V> | null = null\n\n  constructor(message: V) {\n    this.value = message\n  }\n}\n\nexport class Queue<V> {\n  public first: QueueNode<V> | null\n  public last: QueueNode<V> | null\n\n  constructor() {\n    this.first = null\n    this.last = null\n  }\n\n  enqueue(value: V): boolean {\n    const newNode = new QueueNode(value)\n    if (!this.last) {\n      // list is empty\n      this.first = newNode\n      this.last = newNode\n    } else if (this.last === this.first) {\n      // list has a single element\n      this.last = newNode\n      this.last.prev = this.first\n      this.first.next = newNode\n    } else {\n      // list has elements, add as last\n      newNode.prev = this.last\n      this.last.next = newNode\n      this.last = newNode\n    }\n    return true\n  }\n\n  dequeue(): V | null {\n    if (!this.first) return null\n\n    if (this.first === this.last) {\n      const target = this.first\n      this.first = null\n      this.last = null\n      return target.value\n    }\n\n    const target = this.first\n    this.first = target.next\n    if (this.first) {\n      this.first.prev = null // fix: clean up prev pointer\n    }\n\n    return target.value\n  }\n}\n", "import { NostrEvent, validateEvent } from './pure.ts'\n\n/** Events are **regular**, which means they're all expected to be stored by relays. */\nexport function isRegularKind(kind: number): boolean {\n  return kind < 10000 && kind !== 0 && kind !== 3\n}\n\n/** Events are **replaceable**, which means that, for each combination of `pubkey` and `kind`, only the latest event is expected to (SHOULD) be stored by relays, older versions are expected to be discarded. */\nexport function isReplaceableKind(kind: number): boolean {\n  return kind === 0 || kind === 3 || (10000 <= kind && kind < 20000)\n}\n\n/** Events are **ephemeral**, which means they are not expected to be stored by relays. */\nexport function isEphemeralKind(kind: number): boolean {\n  return 20000 <= kind && kind < 30000\n}\n\n/** Events are **addressable**, which means that, for each combination of `pubkey`, `kind` and the `d` tag, only the latest event is expected to be stored by relays, older versions are expected to be discarded. */\nexport function isAddressableKind(kind: number): boolean {\n  return 30000 <= kind && kind < 40000\n}\n\n/** Classification of the event kind. */\nexport type KindClassification = 'regular' | 'replaceable' | 'ephemeral' | 'parameterized' | 'unknown'\n\n/** Determine the classification of this kind of event if known, or `unknown`. */\nexport function classifyKind(kind: number): KindClassification {\n  if (isRegularKind(kind)) return 'regular'\n  if (isReplaceableKind(kind)) return 'replaceable'\n  if (isEphemeralKind(kind)) return 'ephemeral'\n  if (isAddressableKind(kind)) return 'parameterized'\n  return 'unknown'\n}\n\nexport function isKind<T extends number>(event: unknown, kind: T | Array<T>): event is NostrEvent & { kind: T } {\n  const kindAsArray: number[] = kind instanceof Array ? kind : [kind]\n  return (validateEvent(event) && kindAsArray.includes(event.kind)) || false\n}\n\nexport const Metadata = 0\nexport type Metadata = typeof Metadata\nexport const ShortTextNote = 1\nexport type ShortTextNote = typeof ShortTextNote\nexport const RecommendRelay = 2\nexport type RecommendRelay = typeof RecommendRelay\nexport const Contacts = 3\nexport type Contacts = typeof Contacts\nexport const EncryptedDirectMessage = 4\nexport type EncryptedDirectMessage = typeof EncryptedDirectMessage\nexport const EventDeletion = 5\nexport type EventDeletion = typeof EventDeletion\nexport const Repost = 6\nexport type Repost = typeof Repost\nexport const Reaction = 7\nexport type Reaction = typeof Reaction\nexport const BadgeAward = 8\nexport type BadgeAward = typeof BadgeAward\nexport const ChatMessage = 9\nexport type ChatMessage = typeof ChatMessage\nexport const ForumThread = 11\nexport type ForumThread = typeof ForumThread\nexport const Seal = 13\nexport type Seal = typeof Seal\nexport const PrivateDirectMessage = 14\nexport type PrivateDirectMessage = typeof PrivateDirectMessage\nexport const FileMessage = 15\nexport type FileMessage = typeof FileMessage\nexport const GenericRepost = 16\nexport type GenericRepost = typeof GenericRepost\nexport const Photo = 20\nexport type Photo = typeof Photo\nexport const NormalVideo = 21\nexport type NormalVideo = typeof NormalVideo\nexport const ShortVideo = 22\nexport type ShortVideo = typeof ShortVideo\nexport const ChannelCreation = 40\nexport type ChannelCreation = typeof ChannelCreation\nexport const ChannelMetadata = 41\nexport type ChannelMetadata = typeof ChannelMetadata\nexport const ChannelMessage = 42\nexport type ChannelMessage = typeof ChannelMessage\nexport const ChannelHideMessage = 43\nexport type ChannelHideMessage = typeof ChannelHideMessage\nexport const ChannelMuteUser = 44\nexport type ChannelMuteUser = typeof ChannelMuteUser\nexport const OpenTimestamps = 1040\nexport type OpenTimestamps = typeof OpenTimestamps\nexport const GiftWrap = 1059\nexport type GiftWrap = typeof GiftWrap\nexport const Poll = 1068\nexport type Poll = typeof Poll\nexport const FileMetadata = 1063\nexport type FileMetadata = typeof FileMetadata\nexport const Comment = 1111\nexport type Comment = typeof Comment\nexport const LiveChatMessage = 1311\nexport type LiveChatMessage = typeof LiveChatMessage\nexport const Voice = 1222\nexport type Voice = typeof Voice\nexport const VoiceComment = 1244\nexport type VoiceComment = typeof VoiceComment\nexport const ProblemTracker = 1971\nexport type ProblemTracker = typeof ProblemTracker\nexport const Report = 1984\nexport type Report = typeof Report\nexport const Reporting = 1984\nexport type Reporting = typeof Reporting\nexport const Label = 1985\nexport type Label = typeof Label\nexport const CommunityPostApproval = 4550\nexport type CommunityPostApproval = typeof CommunityPostApproval\nexport const JobRequest = 5999\nexport type JobRequest = typeof JobRequest\nexport const JobResult = 6999\nexport type JobResult = typeof JobResult\nexport const JobFeedback = 7000\nexport type JobFeedback = typeof JobFeedback\nexport const ZapGoal = 9041\nexport type ZapGoal = typeof ZapGoal\nexport const ZapRequest = 9734\nexport type ZapRequest = typeof ZapRequest\nexport const Zap = 9735\nexport type Zap = typeof Zap\nexport const Highlights = 9802\nexport type Highlights = typeof Highlights\nexport const PollResponse = 1018\nexport type PollResponse = typeof PollResponse\nexport const Mutelist = 10000\nexport type Mutelist = typeof Mutelist\nexport const Pinlist = 10001\nexport type Pinlist = typeof Pinlist\nexport const RelayList = 10002\nexport type RelayList = typeof RelayList\nexport const BookmarkList = 10003\nexport type BookmarkList = typeof BookmarkList\nexport const CommunitiesList = 10004\nexport type CommunitiesList = typeof CommunitiesList\nexport const PublicChatsList = 10005\nexport type PublicChatsList = typeof PublicChatsList\nexport const BlockedRelaysList = 10006\nexport type BlockedRelaysList = typeof BlockedRelaysList\nexport const SearchRelaysList = 10007\nexport type SearchRelaysList = typeof SearchRelaysList\nexport const FavoriteRelays = 10012\nexport type FavoriteRelays = typeof FavoriteRelays\nexport const InterestsList = 10015\nexport type InterestsList = typeof InterestsList\nexport const UserEmojiList = 10030\nexport type UserEmojiList = typeof UserEmojiList\nexport const DirectMessageRelaysList = 10050\nexport type DirectMessageRelaysList = typeof DirectMessageRelaysList\nexport const FileServerPreference = 10096\nexport type FileServerPreference = typeof FileServerPreference\nexport const BlossomServerList = 10063\nexport type BlossomServerList = typeof BlossomServerList\nexport const NWCWalletInfo = 13194\nexport type NWCWalletInfo = typeof NWCWalletInfo\nexport const LightningPubRPC = 21000\nexport type LightningPubRPC = typeof LightningPubRPC\nexport const ClientAuth = 22242\nexport type ClientAuth = typeof ClientAuth\nexport const NWCWalletRequest = 23194\nexport type NWCWalletRequest = typeof NWCWalletRequest\nexport const NWCWalletResponse = 23195\nexport type NWCWalletResponse = typeof NWCWalletResponse\nexport const NostrConnect = 24133\nexport type NostrConnect = typeof NostrConnect\nexport const HTTPAuth = 27235\nexport type HTTPAuth = typeof HTTPAuth\nexport const Followsets = 30000\nexport type Followsets = typeof Followsets\nexport const Genericlists = 30001\nexport type Genericlists = typeof Genericlists\nexport const Relaysets = 30002\nexport type Relaysets = typeof Relaysets\nexport const Bookmarksets = 30003\nexport type Bookmarksets = typeof Bookmarksets\nexport const Curationsets = 30004\nexport type Curationsets = typeof Curationsets\nexport const ProfileBadges = 30008\nexport type ProfileBadges = typeof ProfileBadges\nexport const BadgeDefinition = 30009\nexport type BadgeDefinition = typeof BadgeDefinition\nexport const Interestsets = 30015\nexport type Interestsets = typeof Interestsets\nexport const CreateOrUpdateStall = 30017\nexport type CreateOrUpdateStall = typeof CreateOrUpdateStall\nexport const CreateOrUpdateProduct = 30018\nexport type CreateOrUpdateProduct = typeof CreateOrUpdateProduct\nexport const LongFormArticle = 30023\nexport type LongFormArticle = typeof LongFormArticle\nexport const DraftLong = 30024\nexport type DraftLong = typeof DraftLong\nexport const Emojisets = 30030\nexport type Emojisets = typeof Emojisets\nexport const Application = 30078\nexport type Application = typeof Application\nexport const LiveEvent = 30311\nexport type LiveEvent = typeof LiveEvent\nexport const UserStatuses = 30315\nexport type UserStatuses = typeof UserStatuses\nexport const ClassifiedListing = 30402\nexport type ClassifiedListing = typeof ClassifiedListing\nexport const DraftClassifiedListing = 30403\nexport type DraftClassifiedListing = typeof DraftClassifiedListing\nexport const Date = 31922\nexport type Date = typeof Date\nexport const Time = 31923\nexport type Time = typeof Time\nexport const Calendar = 31924\nexport type Calendar = typeof Calendar\nexport const CalendarEventRSVP = 31925\nexport type CalendarEventRSVP = typeof CalendarEventRSVP\nexport const RelayReview = 31987\nexport type RelayReview = typeof RelayReview\nexport const Handlerrecommendation = 31989\nexport type Handlerrecommendation = typeof Handlerrecommendation\nexport const Handlerinformation = 31990\nexport type Handlerinformation = typeof Handlerinformation\nexport const CommunityDefinition = 34550\nexport type CommunityDefinition = typeof CommunityDefinition\nexport const GroupMetadata = 39000\nexport type GroupMetadata = typeof GroupMetadata\n", "import { hexToBytes, randomBytes } from '@noble/hashes/utils.js'\nimport { secp256k1 } from '@noble/curves/secp256k1.js'\nimport { cbc } from '@noble/ciphers/aes.js'\nimport { base64 } from '@scure/base'\n\nimport { utf8Decoder, utf8Encoder } from './utils.ts'\n\nexport function encrypt(secretKey: string | Uint8Array, pubkey: string, text: string): string {\n  const privkey: Uint8Array = secretKey instanceof Uint8Array ? secretKey : hexToBytes(secretKey)\n  const key = secp256k1.getSharedSecret(privkey, hexToBytes('02' + pubkey))\n  const normalizedKey = getNormalizedX(key)\n\n  let iv = Uint8Array.from(randomBytes(16))\n  let plaintext = utf8Encoder.encode(text)\n\n  let ciphertext = cbc(normalizedKey, iv).encrypt(plaintext)\n\n  let ctb64 = base64.encode(new Uint8Array(ciphertext))\n  let ivb64 = base64.encode(new Uint8Array(iv.buffer))\n\n  return `${ctb64}?iv=${ivb64}`\n}\n\nexport function decrypt(secretKey: string | Uint8Array, pubkey: string, data: string): string {\n  const privkey: Uint8Array = secretKey instanceof Uint8Array ? secretKey : hexToBytes(secretKey)\n  let [ctb64, ivb64] = data.split('?iv=')\n  let key = secp256k1.getSharedSecret(privkey, hexToBytes('02' + pubkey))\n  let normalizedKey = getNormalizedX(key)\n\n  let iv = base64.decode(ivb64)\n  let ciphertext = base64.decode(ctb64)\n\n  let plaintext = cbc(normalizedKey, iv).decrypt(ciphertext)\n\n  return utf8Decoder.decode(plaintext)\n}\n\nfunction getNormalizedX(key: Uint8Array): Uint8Array {\n  return key.slice(1, 33)\n}\n", "import { type VerifiedEvent, finalizeEvent } from './pure.ts'\nimport { NWCWalletRequest } from './kinds.ts'\nimport { encrypt } from './nip04.ts'\n\ninterface NWCConnection {\n  pubkey: string\n  relay: string\n  secret: string\n}\n\nexport function parseConnectionString(connectionString: string): NWCConnection {\n  const { host, pathname, searchParams } = new URL(connectionString)\n  const pubkey = pathname || host\n  const relay = searchParams.get('relay')\n  const secret = searchParams.get('secret')\n\n  if (!pubkey || !relay || !secret) {\n    throw new Error('invalid connection string')\n  }\n\n  return { pubkey, relay, secret }\n}\n\nexport async function makeNwcRequestEvent(\n  pubkey: string,\n  secretKey: Uint8Array,\n  invoice: string,\n): Promise<VerifiedEvent> {\n  const content = {\n    method: 'pay_invoice',\n    params: {\n      invoice,\n    },\n  }\n  const encryptedContent = encrypt(secretKey, pubkey, JSON.stringify(content))\n  const eventTemplate = {\n    kind: NWCWalletRequest,\n    created_at: Math.round(Date.now() / 1000),\n    content: encryptedContent,\n    tags: [['p', pubkey]],\n  }\n\n  return finalizeEvent(eventTemplate, secretKey)\n}\n"],
+  "mappings": "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",
+  "names": ["bytesToHex", "hexToBytes", "i", "bytesToHex", "hexToBytes", "hexToBytes", "hexToBytes"]
 }
Index: package/lib/cjs/nip49.js.map
===================================================================
--- package/lib/cjs/nip49.js.map
+++ package/lib/cjs/nip49.js.map
@@ -1,7 +1,7 @@
 {
   "version": 3,
   "sources": ["../../nip49.ts", "../../nip19.ts"],
-  "sourcesContent": ["import { scrypt } from '@noble/hashes/scrypt'\nimport { xchacha20poly1305 } from '@noble/ciphers/chacha'\nimport { concatBytes, randomBytes } from '@noble/hashes/utils'\nimport { Bech32MaxSize, Ncryptsec, encodeBytes } from './nip19.ts'\nimport { bech32 } from '@scure/base'\n\nexport function encrypt(\n  sec: Uint8Array,\n  password: string,\n  logn: number = 16,\n  ksb: 0x00 | 0x01 | 0x02 = 0x02,\n): Ncryptsec {\n  let salt = randomBytes(16)\n  let n = 2 ** logn\n  let key = scrypt(password.normalize('NFKC'), salt, { N: n, r: 8, p: 1, dkLen: 32 })\n  let nonce = randomBytes(24)\n  let aad = Uint8Array.from([ksb])\n  let xc2p1 = xchacha20poly1305(key, nonce, aad)\n  let ciphertext = xc2p1.encrypt(sec)\n  let b = concatBytes(Uint8Array.from([0x02]), Uint8Array.from([logn]), salt, nonce, aad, ciphertext)\n  return encodeBytes('ncryptsec', b)\n}\n\nexport function decrypt(ncryptsec: string, password: string): Uint8Array {\n  let { prefix, words } = bech32.decode(ncryptsec, Bech32MaxSize)\n  if (prefix !== 'ncryptsec') {\n    throw new Error(`invalid prefix ${prefix}, expected 'ncryptsec'`)\n  }\n  let b = new Uint8Array(bech32.fromWords(words))\n\n  let version = b[0]\n  if (version !== 0x02) {\n    throw new Error(`invalid version ${version}, expected 0x02`)\n  }\n\n  let logn = b[1]\n  let n = 2 ** logn\n\n  let salt = b.slice(2, 2 + 16)\n  let nonce = b.slice(2 + 16, 2 + 16 + 24)\n  let ksb = b[2 + 16 + 24]\n  let aad = Uint8Array.from([ksb])\n  let ciphertext = b.slice(2 + 16 + 24 + 1)\n\n  let key = scrypt(password.normalize('NFKC'), salt, { N: n, r: 8, p: 1, dkLen: 32 })\n  let xc2p1 = xchacha20poly1305(key, nonce, aad)\n  let sec = xc2p1.decrypt(ciphertext)\n\n  return sec\n}\n", "import { bytesToHex, concatBytes, hexToBytes } from '@noble/hashes/utils'\nimport { bech32 } from '@scure/base'\n\nimport { utf8Decoder, utf8Encoder } from './utils.ts'\n\nexport type NProfile = `nprofile1${string}`\nexport type NEvent = `nevent1${string}`\nexport type NAddr = `naddr1${string}`\nexport type NSec = `nsec1${string}`\nexport type NPub = `npub1${string}`\nexport type Note = `note1${string}`\nexport type Ncryptsec = `ncryptsec1${string}`\n\nexport const NostrTypeGuard = {\n  isNProfile: (value?: string | null): value is NProfile => /^nprofile1[a-z\\d]+$/.test(value || ''),\n  isNEvent: (value?: string | null): value is NEvent => /^nevent1[a-z\\d]+$/.test(value || ''),\n  isNAddr: (value?: string | null): value is NAddr => /^naddr1[a-z\\d]+$/.test(value || ''),\n  isNSec: (value?: string | null): value is NSec => /^nsec1[a-z\\d]{58}$/.test(value || ''),\n  isNPub: (value?: string | null): value is NPub => /^npub1[a-z\\d]{58}$/.test(value || ''),\n  isNote: (value?: string | null): value is Note => /^note1[a-z\\d]+$/.test(value || ''),\n  isNcryptsec: (value?: string | null): value is Ncryptsec => /^ncryptsec1[a-z\\d]+$/.test(value || ''),\n}\n\nexport const Bech32MaxSize = 5000\n\n/**\n * Bech32 regex.\n * @see https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki#bech32\n */\nexport const BECH32_REGEX = /[\\x21-\\x7E]{1,83}1[023456789acdefghjklmnpqrstuvwxyz]{6,}/\n\nfunction integerToUint8Array(number: number) {\n  // Create a Uint8Array with enough space to hold a 32-bit integer (4 bytes).\n  const uint8Array = new Uint8Array(4)\n\n  // Use bitwise operations to extract the bytes.\n  uint8Array[0] = (number >> 24) & 0xff // Most significant byte (MSB)\n  uint8Array[1] = (number >> 16) & 0xff\n  uint8Array[2] = (number >> 8) & 0xff\n  uint8Array[3] = number & 0xff // Least significant byte (LSB)\n\n  return uint8Array\n}\n\nexport type ProfilePointer = {\n  pubkey: string // hex\n  relays?: string[]\n}\n\nexport type EventPointer = {\n  id: string // hex\n  relays?: string[]\n  author?: string\n  kind?: number\n}\n\nexport type AddressPointer = {\n  identifier: string\n  pubkey: string\n  kind: number\n  relays?: string[]\n}\n\nexport function decodeNostrURI(nip19code: string): ReturnType<typeof decode> | { type: 'invalid'; data: null } {\n  try {\n    if (nip19code.startsWith('nostr:')) nip19code = nip19code.substring(6)\n    return decode(nip19code)\n  } catch (_err) {\n    return { type: 'invalid', data: null }\n  }\n}\n\nexport type DecodedNevent = {\n  type: 'nevent'\n  data: EventPointer\n}\n\nexport type DecodedNprofile = {\n  type: 'nprofile'\n  data: ProfilePointer\n}\n\nexport type DecodedNaddr = {\n  type: 'naddr'\n  data: AddressPointer\n}\n\nexport type DecodedNsec = {\n  type: 'nsec'\n  data: Uint8Array\n}\n\nexport type DecodedNpub = {\n  type: 'npub'\n  data: string\n}\n\nexport type DecodedNote = {\n  type: 'note'\n  data: string\n}\n\nexport type DecodedResult = DecodedNevent | DecodedNprofile | DecodedNaddr | DecodedNpub | DecodedNsec | DecodedNote\n\nexport function decode(nip19: NEvent): DecodedNevent\nexport function decode(nip19: NProfile): DecodedNprofile\nexport function decode(nip19: NAddr): DecodedNaddr\nexport function decode(nip19: NSec): DecodedNsec\nexport function decode(nip19: NPub): DecodedNpub\nexport function decode(nip19: Note): DecodedNote\nexport function decode(code: string): DecodedResult\nexport function decode(code: string): DecodedResult {\n  let { prefix, words } = bech32.decode(code, Bech32MaxSize)\n  let data = new Uint8Array(bech32.fromWords(words))\n\n  switch (prefix) {\n    case 'nprofile': {\n      let tlv = parseTLV(data)\n      if (!tlv[0]?.[0]) throw new Error('missing TLV 0 for nprofile')\n      if (tlv[0][0].length !== 32) throw new Error('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      let tlv = parseTLV(data)\n      if (!tlv[0]?.[0]) throw new Error('missing TLV 0 for nevent')\n      if (tlv[0][0].length !== 32) throw new Error('TLV 0 should be 32 bytes')\n      if (tlv[2] && tlv[2][0].length !== 32) throw new Error('TLV 2 should be 32 bytes')\n      if (tlv[3] && tlv[3][0].length !== 4) throw new Error('TLV 3 should be 4 bytes')\n\n      return {\n        type: 'nevent',\n        data: {\n          id: bytesToHex(tlv[0][0]),\n          relays: tlv[1] ? tlv[1].map(d => utf8Decoder.decode(d)) : [],\n          author: tlv[2]?.[0] ? bytesToHex(tlv[2][0]) : undefined,\n          kind: tlv[3]?.[0] ? parseInt(bytesToHex(tlv[3][0]), 16) : undefined,\n        },\n      }\n    }\n\n    case 'naddr': {\n      let tlv = parseTLV(data)\n      if (!tlv[0]?.[0]) throw new Error('missing TLV 0 for naddr')\n      if (!tlv[2]?.[0]) throw new Error('missing TLV 2 for naddr')\n      if (tlv[2][0].length !== 32) throw new Error('TLV 2 should be 32 bytes')\n      if (!tlv[3]?.[0]) throw new Error('missing TLV 3 for naddr')\n      if (tlv[3][0].length !== 4) throw new Error('TLV 3 should be 4 bytes')\n\n      return {\n        type: 'naddr',\n        data: {\n          identifier: utf8Decoder.decode(tlv[0][0]),\n          pubkey: bytesToHex(tlv[2][0]),\n          kind: parseInt(bytesToHex(tlv[3][0]), 16),\n          relays: tlv[1] ? tlv[1].map(d => utf8Decoder.decode(d)) : [],\n        },\n      }\n    }\n\n    case 'nsec':\n      return { type: prefix, data }\n\n    case 'npub':\n    case 'note':\n      return { type: prefix, data: bytesToHex(data) }\n\n    default:\n      throw new Error(`unknown prefix ${prefix}`)\n  }\n}\n\ntype TLV = { [t: number]: Uint8Array[] }\n\nfunction parseTLV(data: Uint8Array): TLV {\n  let result: TLV = {}\n  let rest = data\n  while (rest.length > 0) {\n    let t = rest[0]\n    let l = rest[1]\n    let v = rest.slice(2, 2 + l)\n    rest = rest.slice(2 + l)\n    if (v.length < l) throw new Error(`not enough data to read on TLV ${t}`)\n    result[t] = result[t] || []\n    result[t].push(v)\n  }\n  return result\n}\n\nexport function nsecEncode(key: Uint8Array): NSec {\n  return encodeBytes('nsec', key)\n}\n\nexport function npubEncode(hex: string): NPub {\n  return encodeBytes('npub', hexToBytes(hex))\n}\n\nexport function noteEncode(hex: string): Note {\n  return encodeBytes('note', hexToBytes(hex))\n}\n\nfunction encodeBech32<Prefix extends string>(prefix: Prefix, data: Uint8Array): `${Prefix}1${string}` {\n  let words = bech32.toWords(data)\n  return bech32.encode(prefix, words, Bech32MaxSize) as `${Prefix}1${string}`\n}\n\nexport function encodeBytes<Prefix extends string>(prefix: Prefix, bytes: Uint8Array): `${Prefix}1${string}` {\n  return encodeBech32(prefix, bytes)\n}\n\nexport function nprofileEncode(profile: ProfilePointer): NProfile {\n  let data = encodeTLV({\n    0: [hexToBytes(profile.pubkey)],\n    1: (profile.relays || []).map(url => utf8Encoder.encode(url)),\n  })\n  return encodeBech32('nprofile', data)\n}\n\nexport function neventEncode(event: EventPointer): NEvent {\n  let kindArray\n  if (event.kind !== undefined) {\n    kindArray = integerToUint8Array(event.kind)\n  }\n\n  let data = encodeTLV({\n    0: [hexToBytes(event.id)],\n    1: (event.relays || []).map(url => utf8Encoder.encode(url)),\n    2: event.author ? [hexToBytes(event.author)] : [],\n    3: kindArray ? [new Uint8Array(kindArray)] : [],\n  })\n\n  return encodeBech32('nevent', data)\n}\n\nexport function naddrEncode(addr: AddressPointer): NAddr {\n  let kind = new ArrayBuffer(4)\n  new DataView(kind).setUint32(0, addr.kind, false)\n\n  let data = encodeTLV({\n    0: [utf8Encoder.encode(addr.identifier)],\n    1: (addr.relays || []).map(url => utf8Encoder.encode(url)),\n    2: [hexToBytes(addr.pubkey)],\n    3: [new Uint8Array(kind)],\n  })\n  return encodeBech32('naddr', data)\n}\n\nfunction encodeTLV(tlv: TLV): Uint8Array {\n  let entries: Uint8Array[] = []\n\n  Object.entries(tlv)\n    .reverse()\n    .forEach(([t, vs]) => {\n      vs.forEach(v => {\n        let entry = new Uint8Array(v.length + 2)\n        entry.set([parseInt(t)], 0)\n        entry.set([v.length], 1)\n        entry.set(v, 2)\n        entries.push(entry)\n      })\n    })\n\n  return concatBytes(...entries)\n}\n"],
-  "mappings": ";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,oBAAuB;AACvB,oBAAkC;AAClC,IAAAA,gBAAyC;;;ACFzC,mBAAoD;AACpD,kBAAuB;AAsBhB,IAAM,gBAAgB;AAwL7B,SAAS,aAAoC,QAAgB,MAAyC;AACpG,MAAI,QAAQ,mBAAO,QAAQ,IAAI;AAC/B,SAAO,mBAAO,OAAO,QAAQ,OAAO,aAAa;AACnD;AAEO,SAAS,YAAmC,QAAgB,OAA0C;AAC3G,SAAO,aAAa,QAAQ,KAAK;AACnC;;;ADlNA,IAAAC,eAAuB;AAEhB,SAAS,QACd,KACA,UACA,OAAe,IACf,MAA0B,GACf;AACX,MAAI,WAAO,2BAAY,EAAE;AACzB,MAAI,IAAI,KAAK;AACb,MAAI,UAAM,sBAAO,SAAS,UAAU,MAAM,GAAG,MAAM,EAAE,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,OAAO,GAAG,CAAC;AAClF,MAAI,YAAQ,2BAAY,EAAE;AAC1B,MAAI,MAAM,WAAW,KAAK,CAAC,GAAG,CAAC;AAC/B,MAAI,YAAQ,iCAAkB,KAAK,OAAO,GAAG;AAC7C,MAAI,aAAa,MAAM,QAAQ,GAAG;AAClC,MAAI,QAAI,2BAAY,WAAW,KAAK,CAAC,CAAI,CAAC,GAAG,WAAW,KAAK,CAAC,IAAI,CAAC,GAAG,MAAM,OAAO,KAAK,UAAU;AAClG,SAAO,YAAY,aAAa,CAAC;AACnC;AAEO,SAAS,QAAQ,WAAmB,UAA8B;AACvE,MAAI,EAAE,QAAQ,MAAM,IAAI,oBAAO,OAAO,WAAW,aAAa;AAC9D,MAAI,WAAW,aAAa;AAC1B,UAAM,IAAI,MAAM,kBAAkB,8BAA8B;AAAA,EAClE;AACA,MAAI,IAAI,IAAI,WAAW,oBAAO,UAAU,KAAK,CAAC;AAE9C,MAAI,UAAU,EAAE;AAChB,MAAI,YAAY,GAAM;AACpB,UAAM,IAAI,MAAM,mBAAmB,wBAAwB;AAAA,EAC7D;AAEA,MAAI,OAAO,EAAE;AACb,MAAI,IAAI,KAAK;AAEb,MAAI,OAAO,EAAE,MAAM,GAAG,IAAI,EAAE;AAC5B,MAAI,QAAQ,EAAE,MAAM,IAAI,IAAI,IAAI,KAAK,EAAE;AACvC,MAAI,MAAM,EAAE,IAAI,KAAK;AACrB,MAAI,MAAM,WAAW,KAAK,CAAC,GAAG,CAAC;AAC/B,MAAI,aAAa,EAAE,MAAM,IAAI,KAAK,KAAK,CAAC;AAExC,MAAI,UAAM,sBAAO,SAAS,UAAU,MAAM,GAAG,MAAM,EAAE,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,OAAO,GAAG,CAAC;AAClF,MAAI,YAAQ,iCAAkB,KAAK,OAAO,GAAG;AAC7C,MAAI,MAAM,MAAM,QAAQ,UAAU;AAElC,SAAO;AACT;",
-  "names": ["import_utils", "import_base"]
+  "sourcesContent": ["import { bech32 } from '@scure/base'\nimport { scrypt } from '@noble/hashes/scrypt.js'\nimport { xchacha20poly1305 } from '@noble/ciphers/chacha.js'\nimport { concatBytes, randomBytes } from '@noble/hashes/utils.js'\nimport { Bech32MaxSize, Ncryptsec, encodeBytes } from './nip19.ts'\n\nexport function encrypt(\n  sec: Uint8Array,\n  password: string,\n  logn: number = 16,\n  ksb: 0x00 | 0x01 | 0x02 = 0x02,\n): Ncryptsec {\n  let salt = randomBytes(16)\n  let n = 2 ** logn\n  let key = scrypt(password.normalize('NFKC'), salt, { N: n, r: 8, p: 1, dkLen: 32 })\n  let nonce = randomBytes(24)\n  let aad = Uint8Array.from([ksb])\n  let xc2p1 = xchacha20poly1305(key, nonce, aad)\n  let ciphertext = xc2p1.encrypt(sec)\n  let b = concatBytes(Uint8Array.from([0x02]), Uint8Array.from([logn]), salt, nonce, aad, ciphertext)\n  return encodeBytes('ncryptsec', b)\n}\n\nexport function decrypt(ncryptsec: string, password: string): Uint8Array {\n  let { prefix, words } = bech32.decode(ncryptsec as `${string}1${string}`, Bech32MaxSize)\n  if (prefix !== 'ncryptsec') {\n    throw new Error(`invalid prefix ${prefix}, expected 'ncryptsec'`)\n  }\n  let b = new Uint8Array(bech32.fromWords(words))\n\n  let version = b[0]\n  if (version !== 0x02) {\n    throw new Error(`invalid version ${version}, expected 0x02`)\n  }\n\n  let logn = b[1]\n  let n = 2 ** logn\n\n  let salt = b.slice(2, 2 + 16)\n  let nonce = b.slice(2 + 16, 2 + 16 + 24)\n  let ksb = b[2 + 16 + 24]\n  let aad = Uint8Array.from([ksb])\n  let ciphertext = b.slice(2 + 16 + 24 + 1)\n\n  let key = scrypt(password.normalize('NFKC'), salt, { N: n, r: 8, p: 1, dkLen: 32 })\n  let xc2p1 = xchacha20poly1305(key, nonce, aad)\n  let sec = xc2p1.decrypt(ciphertext)\n\n  return sec\n}\n", "import { bytesToHex, concatBytes, hexToBytes } from '@noble/hashes/utils.js'\nimport { bech32 } from '@scure/base'\n\nimport { utf8Decoder, utf8Encoder } from './utils.ts'\n\nexport type NProfile = `nprofile1${string}`\nexport type NEvent = `nevent1${string}`\nexport type NAddr = `naddr1${string}`\nexport type NSec = `nsec1${string}`\nexport type NPub = `npub1${string}`\nexport type Note = `note1${string}`\nexport type Ncryptsec = `ncryptsec1${string}`\n\nexport const NostrTypeGuard = {\n  isNProfile: (value?: string | null): value is NProfile => /^nprofile1[a-z\\d]+$/.test(value || ''),\n  isNEvent: (value?: string | null): value is NEvent => /^nevent1[a-z\\d]+$/.test(value || ''),\n  isNAddr: (value?: string | null): value is NAddr => /^naddr1[a-z\\d]+$/.test(value || ''),\n  isNSec: (value?: string | null): value is NSec => /^nsec1[a-z\\d]{58}$/.test(value || ''),\n  isNPub: (value?: string | null): value is NPub => /^npub1[a-z\\d]{58}$/.test(value || ''),\n  isNote: (value?: string | null): value is Note => /^note1[a-z\\d]+$/.test(value || ''),\n  isNcryptsec: (value?: string | null): value is Ncryptsec => /^ncryptsec1[a-z\\d]+$/.test(value || ''),\n}\n\nexport const Bech32MaxSize = 5000\n\n/**\n * Bech32 regex.\n * @see https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki#bech32\n */\nexport const BECH32_REGEX = /[\\x21-\\x7E]{1,83}1[023456789acdefghjklmnpqrstuvwxyz]{6,}/\n\nfunction integerToUint8Array(number: number) {\n  // Create a Uint8Array with enough space to hold a 32-bit integer (4 bytes).\n  const uint8Array = new Uint8Array(4)\n\n  // Use bitwise operations to extract the bytes.\n  uint8Array[0] = (number >> 24) & 0xff // Most significant byte (MSB)\n  uint8Array[1] = (number >> 16) & 0xff\n  uint8Array[2] = (number >> 8) & 0xff\n  uint8Array[3] = number & 0xff // Least significant byte (LSB)\n\n  return uint8Array\n}\n\nexport type ProfilePointer = {\n  pubkey: string // hex\n  relays?: string[]\n}\n\nexport type EventPointer = {\n  id: string // hex\n  relays?: string[]\n  author?: string\n  kind?: number\n}\n\nexport type AddressPointer = {\n  identifier: string\n  pubkey: string\n  kind: number\n  relays?: string[]\n}\n\nexport function decodeNostrURI(nip19code: string): ReturnType<typeof decode> | { type: 'invalid'; data: null } {\n  try {\n    if (nip19code.startsWith('nostr:')) nip19code = nip19code.substring(6)\n    return decode(nip19code)\n  } catch (_err) {\n    return { type: 'invalid', data: null }\n  }\n}\n\nexport type DecodedNevent = {\n  type: 'nevent'\n  data: EventPointer\n}\n\nexport type DecodedNprofile = {\n  type: 'nprofile'\n  data: ProfilePointer\n}\n\nexport type DecodedNaddr = {\n  type: 'naddr'\n  data: AddressPointer\n}\n\nexport type DecodedNsec = {\n  type: 'nsec'\n  data: Uint8Array\n}\n\nexport type DecodedNpub = {\n  type: 'npub'\n  data: string\n}\n\nexport type DecodedNote = {\n  type: 'note'\n  data: string\n}\n\nexport type DecodedResult = DecodedNevent | DecodedNprofile | DecodedNaddr | DecodedNpub | DecodedNsec | DecodedNote\n\nexport function decode(nip19: NEvent): DecodedNevent\nexport function decode(nip19: NProfile): DecodedNprofile\nexport function decode(nip19: NAddr): DecodedNaddr\nexport function decode(nip19: NSec): DecodedNsec\nexport function decode(nip19: NPub): DecodedNpub\nexport function decode(nip19: Note): DecodedNote\nexport function decode(code: string): DecodedResult\nexport function decode(code: string): DecodedResult {\n  let { prefix, words } = bech32.decode(code as `${string}1${string}`, Bech32MaxSize)\n  let data = new Uint8Array(bech32.fromWords(words))\n\n  switch (prefix) {\n    case 'nprofile': {\n      let tlv = parseTLV(data)\n      if (!tlv[0]?.[0]) throw new Error('missing TLV 0 for nprofile')\n      if (tlv[0][0].length !== 32) throw new Error('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      let tlv = parseTLV(data)\n      if (!tlv[0]?.[0]) throw new Error('missing TLV 0 for nevent')\n      if (tlv[0][0].length !== 32) throw new Error('TLV 0 should be 32 bytes')\n      if (tlv[2] && tlv[2][0].length !== 32) throw new Error('TLV 2 should be 32 bytes')\n      if (tlv[3] && tlv[3][0].length !== 4) throw new Error('TLV 3 should be 4 bytes')\n\n      return {\n        type: 'nevent',\n        data: {\n          id: bytesToHex(tlv[0][0]),\n          relays: tlv[1] ? tlv[1].map(d => utf8Decoder.decode(d)) : [],\n          author: tlv[2]?.[0] ? bytesToHex(tlv[2][0]) : undefined,\n          kind: tlv[3]?.[0] ? parseInt(bytesToHex(tlv[3][0]), 16) : undefined,\n        },\n      }\n    }\n\n    case 'naddr': {\n      let tlv = parseTLV(data)\n      if (!tlv[0]?.[0]) throw new Error('missing TLV 0 for naddr')\n      if (!tlv[2]?.[0]) throw new Error('missing TLV 2 for naddr')\n      if (tlv[2][0].length !== 32) throw new Error('TLV 2 should be 32 bytes')\n      if (!tlv[3]?.[0]) throw new Error('missing TLV 3 for naddr')\n      if (tlv[3][0].length !== 4) throw new Error('TLV 3 should be 4 bytes')\n\n      return {\n        type: 'naddr',\n        data: {\n          identifier: utf8Decoder.decode(tlv[0][0]),\n          pubkey: bytesToHex(tlv[2][0]),\n          kind: parseInt(bytesToHex(tlv[3][0]), 16),\n          relays: tlv[1] ? tlv[1].map(d => utf8Decoder.decode(d)) : [],\n        },\n      }\n    }\n\n    case 'nsec':\n      return { type: prefix, data }\n\n    case 'npub':\n    case 'note':\n      return { type: prefix, data: bytesToHex(data) }\n\n    default:\n      throw new Error(`unknown prefix ${prefix}`)\n  }\n}\n\ntype TLV = { [t: number]: Uint8Array[] }\n\nfunction parseTLV(data: Uint8Array): TLV {\n  let result: TLV = {}\n  let rest = data\n  while (rest.length > 0) {\n    let t = rest[0]\n    let l = rest[1]\n    let v = rest.slice(2, 2 + l)\n    rest = rest.slice(2 + l)\n    if (v.length < l) throw new Error(`not enough data to read on TLV ${t}`)\n    result[t] = result[t] || []\n    result[t].push(v)\n  }\n  return result\n}\n\nexport function nsecEncode(key: Uint8Array): NSec {\n  return encodeBytes('nsec', key)\n}\n\nexport function npubEncode(hex: string): NPub {\n  return encodeBytes('npub', hexToBytes(hex))\n}\n\nexport function noteEncode(hex: string): Note {\n  return encodeBytes('note', hexToBytes(hex))\n}\n\nfunction encodeBech32<Prefix extends string>(prefix: Prefix, data: Uint8Array): `${Prefix}1${string}` {\n  let words = bech32.toWords(data)\n  return bech32.encode(prefix, words, Bech32MaxSize) as `${Prefix}1${string}`\n}\n\nexport function encodeBytes<Prefix extends string>(prefix: Prefix, bytes: Uint8Array): `${Prefix}1${string}` {\n  return encodeBech32(prefix, bytes)\n}\n\nexport function nprofileEncode(profile: ProfilePointer): NProfile {\n  let data = encodeTLV({\n    0: [hexToBytes(profile.pubkey)],\n    1: (profile.relays || []).map(url => utf8Encoder.encode(url)),\n  })\n  return encodeBech32('nprofile', data)\n}\n\nexport function neventEncode(event: EventPointer): NEvent {\n  let kindArray\n  if (event.kind !== undefined) {\n    kindArray = integerToUint8Array(event.kind)\n  }\n\n  let data = encodeTLV({\n    0: [hexToBytes(event.id)],\n    1: (event.relays || []).map(url => utf8Encoder.encode(url)),\n    2: event.author ? [hexToBytes(event.author)] : [],\n    3: kindArray ? [new Uint8Array(kindArray)] : [],\n  })\n\n  return encodeBech32('nevent', data)\n}\n\nexport function naddrEncode(addr: AddressPointer): NAddr {\n  let kind = new ArrayBuffer(4)\n  new DataView(kind).setUint32(0, addr.kind, false)\n\n  let data = encodeTLV({\n    0: [utf8Encoder.encode(addr.identifier)],\n    1: (addr.relays || []).map(url => utf8Encoder.encode(url)),\n    2: [hexToBytes(addr.pubkey)],\n    3: [new Uint8Array(kind)],\n  })\n  return encodeBech32('naddr', data)\n}\n\nfunction encodeTLV(tlv: TLV): Uint8Array {\n  let entries: Uint8Array[] = []\n\n  Object.entries(tlv)\n    .reverse()\n    .forEach(([t, vs]) => {\n      vs.forEach(v => {\n        let entry = new Uint8Array(v.length + 2)\n        entry.set([parseInt(t)], 0)\n        entry.set([v.length], 1)\n        entry.set(v, 2)\n        entries.push(entry)\n      })\n    })\n\n  return concatBytes(...entries)\n}\n"],
+  "mappings": ";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAAAA,eAAuB;AACvB,oBAAuB;AACvB,oBAAkC;AAClC,IAAAC,gBAAyC;;;ACHzC,mBAAoD;AACpD,kBAAuB;AAsBhB,IAAM,gBAAgB;AAwL7B,SAAS,aAAoC,QAAgB,MAAyC;AACpG,MAAI,QAAQ,mBAAO,QAAQ,IAAI;AAC/B,SAAO,mBAAO,OAAO,QAAQ,OAAO,aAAa;AACnD;AAEO,SAAS,YAAmC,QAAgB,OAA0C;AAC3G,SAAO,aAAa,QAAQ,KAAK;AACnC;;;ADhNO,SAAS,QACd,KACA,UACA,OAAe,IACf,MAA0B,GACf;AACX,MAAI,WAAO,2BAAY,EAAE;AACzB,MAAI,IAAI,KAAK;AACb,MAAI,UAAM,sBAAO,SAAS,UAAU,MAAM,GAAG,MAAM,EAAE,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,OAAO,GAAG,CAAC;AAClF,MAAI,YAAQ,2BAAY,EAAE;AAC1B,MAAI,MAAM,WAAW,KAAK,CAAC,GAAG,CAAC;AAC/B,MAAI,YAAQ,iCAAkB,KAAK,OAAO,GAAG;AAC7C,MAAI,aAAa,MAAM,QAAQ,GAAG;AAClC,MAAI,QAAI,2BAAY,WAAW,KAAK,CAAC,CAAI,CAAC,GAAG,WAAW,KAAK,CAAC,IAAI,CAAC,GAAG,MAAM,OAAO,KAAK,UAAU;AAClG,SAAO,YAAY,aAAa,CAAC;AACnC;AAEO,SAAS,QAAQ,WAAmB,UAA8B;AACvE,MAAI,EAAE,QAAQ,MAAM,IAAI,oBAAO,OAAO,WAAoC,aAAa;AACvF,MAAI,WAAW,aAAa;AAC1B,UAAM,IAAI,MAAM,kBAAkB,8BAA8B;AAAA,EAClE;AACA,MAAI,IAAI,IAAI,WAAW,oBAAO,UAAU,KAAK,CAAC;AAE9C,MAAI,UAAU,EAAE;AAChB,MAAI,YAAY,GAAM;AACpB,UAAM,IAAI,MAAM,mBAAmB,wBAAwB;AAAA,EAC7D;AAEA,MAAI,OAAO,EAAE;AACb,MAAI,IAAI,KAAK;AAEb,MAAI,OAAO,EAAE,MAAM,GAAG,IAAI,EAAE;AAC5B,MAAI,QAAQ,EAAE,MAAM,IAAI,IAAI,IAAI,KAAK,EAAE;AACvC,MAAI,MAAM,EAAE,IAAI,KAAK;AACrB,MAAI,MAAM,WAAW,KAAK,CAAC,GAAG,CAAC;AAC/B,MAAI,aAAa,EAAE,MAAM,IAAI,KAAK,KAAK,CAAC;AAExC,MAAI,UAAM,sBAAO,SAAS,UAAU,MAAM,GAAG,MAAM,EAAE,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,OAAO,GAAG,CAAC;AAClF,MAAI,YAAQ,iCAAkB,KAAK,OAAO,GAAG;AAC7C,MAAI,MAAM,MAAM,QAAQ,UAAU;AAElC,SAAO;AACT;",
+  "names": ["import_base", "import_utils"]
 }
Index: package/lib/esm/nip49.js.map
===================================================================
--- package/lib/esm/nip49.js.map
+++ package/lib/esm/nip49.js.map
@@ -1,7 +1,7 @@
 {
   "version": 3,
   "sources": ["../../nip49.ts", "../../nip19.ts"],
-  "sourcesContent": ["import { scrypt } from '@noble/hashes/scrypt'\nimport { xchacha20poly1305 } from '@noble/ciphers/chacha'\nimport { concatBytes, randomBytes } from '@noble/hashes/utils'\nimport { Bech32MaxSize, Ncryptsec, encodeBytes } from './nip19.ts'\nimport { bech32 } from '@scure/base'\n\nexport function encrypt(\n  sec: Uint8Array,\n  password: string,\n  logn: number = 16,\n  ksb: 0x00 | 0x01 | 0x02 = 0x02,\n): Ncryptsec {\n  let salt = randomBytes(16)\n  let n = 2 ** logn\n  let key = scrypt(password.normalize('NFKC'), salt, { N: n, r: 8, p: 1, dkLen: 32 })\n  let nonce = randomBytes(24)\n  let aad = Uint8Array.from([ksb])\n  let xc2p1 = xchacha20poly1305(key, nonce, aad)\n  let ciphertext = xc2p1.encrypt(sec)\n  let b = concatBytes(Uint8Array.from([0x02]), Uint8Array.from([logn]), salt, nonce, aad, ciphertext)\n  return encodeBytes('ncryptsec', b)\n}\n\nexport function decrypt(ncryptsec: string, password: string): Uint8Array {\n  let { prefix, words } = bech32.decode(ncryptsec, Bech32MaxSize)\n  if (prefix !== 'ncryptsec') {\n    throw new Error(`invalid prefix ${prefix}, expected 'ncryptsec'`)\n  }\n  let b = new Uint8Array(bech32.fromWords(words))\n\n  let version = b[0]\n  if (version !== 0x02) {\n    throw new Error(`invalid version ${version}, expected 0x02`)\n  }\n\n  let logn = b[1]\n  let n = 2 ** logn\n\n  let salt = b.slice(2, 2 + 16)\n  let nonce = b.slice(2 + 16, 2 + 16 + 24)\n  let ksb = b[2 + 16 + 24]\n  let aad = Uint8Array.from([ksb])\n  let ciphertext = b.slice(2 + 16 + 24 + 1)\n\n  let key = scrypt(password.normalize('NFKC'), salt, { N: n, r: 8, p: 1, dkLen: 32 })\n  let xc2p1 = xchacha20poly1305(key, nonce, aad)\n  let sec = xc2p1.decrypt(ciphertext)\n\n  return sec\n}\n", "import { bytesToHex, concatBytes, hexToBytes } from '@noble/hashes/utils'\nimport { bech32 } from '@scure/base'\n\nimport { utf8Decoder, utf8Encoder } from './utils.ts'\n\nexport type NProfile = `nprofile1${string}`\nexport type NEvent = `nevent1${string}`\nexport type NAddr = `naddr1${string}`\nexport type NSec = `nsec1${string}`\nexport type NPub = `npub1${string}`\nexport type Note = `note1${string}`\nexport type Ncryptsec = `ncryptsec1${string}`\n\nexport const NostrTypeGuard = {\n  isNProfile: (value?: string | null): value is NProfile => /^nprofile1[a-z\\d]+$/.test(value || ''),\n  isNEvent: (value?: string | null): value is NEvent => /^nevent1[a-z\\d]+$/.test(value || ''),\n  isNAddr: (value?: string | null): value is NAddr => /^naddr1[a-z\\d]+$/.test(value || ''),\n  isNSec: (value?: string | null): value is NSec => /^nsec1[a-z\\d]{58}$/.test(value || ''),\n  isNPub: (value?: string | null): value is NPub => /^npub1[a-z\\d]{58}$/.test(value || ''),\n  isNote: (value?: string | null): value is Note => /^note1[a-z\\d]+$/.test(value || ''),\n  isNcryptsec: (value?: string | null): value is Ncryptsec => /^ncryptsec1[a-z\\d]+$/.test(value || ''),\n}\n\nexport const Bech32MaxSize = 5000\n\n/**\n * Bech32 regex.\n * @see https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki#bech32\n */\nexport const BECH32_REGEX = /[\\x21-\\x7E]{1,83}1[023456789acdefghjklmnpqrstuvwxyz]{6,}/\n\nfunction integerToUint8Array(number: number) {\n  // Create a Uint8Array with enough space to hold a 32-bit integer (4 bytes).\n  const uint8Array = new Uint8Array(4)\n\n  // Use bitwise operations to extract the bytes.\n  uint8Array[0] = (number >> 24) & 0xff // Most significant byte (MSB)\n  uint8Array[1] = (number >> 16) & 0xff\n  uint8Array[2] = (number >> 8) & 0xff\n  uint8Array[3] = number & 0xff // Least significant byte (LSB)\n\n  return uint8Array\n}\n\nexport type ProfilePointer = {\n  pubkey: string // hex\n  relays?: string[]\n}\n\nexport type EventPointer = {\n  id: string // hex\n  relays?: string[]\n  author?: string\n  kind?: number\n}\n\nexport type AddressPointer = {\n  identifier: string\n  pubkey: string\n  kind: number\n  relays?: string[]\n}\n\nexport function decodeNostrURI(nip19code: string): ReturnType<typeof decode> | { type: 'invalid'; data: null } {\n  try {\n    if (nip19code.startsWith('nostr:')) nip19code = nip19code.substring(6)\n    return decode(nip19code)\n  } catch (_err) {\n    return { type: 'invalid', data: null }\n  }\n}\n\nexport type DecodedNevent = {\n  type: 'nevent'\n  data: EventPointer\n}\n\nexport type DecodedNprofile = {\n  type: 'nprofile'\n  data: ProfilePointer\n}\n\nexport type DecodedNaddr = {\n  type: 'naddr'\n  data: AddressPointer\n}\n\nexport type DecodedNsec = {\n  type: 'nsec'\n  data: Uint8Array\n}\n\nexport type DecodedNpub = {\n  type: 'npub'\n  data: string\n}\n\nexport type DecodedNote = {\n  type: 'note'\n  data: string\n}\n\nexport type DecodedResult = DecodedNevent | DecodedNprofile | DecodedNaddr | DecodedNpub | DecodedNsec | DecodedNote\n\nexport function decode(nip19: NEvent): DecodedNevent\nexport function decode(nip19: NProfile): DecodedNprofile\nexport function decode(nip19: NAddr): DecodedNaddr\nexport function decode(nip19: NSec): DecodedNsec\nexport function decode(nip19: NPub): DecodedNpub\nexport function decode(nip19: Note): DecodedNote\nexport function decode(code: string): DecodedResult\nexport function decode(code: string): DecodedResult {\n  let { prefix, words } = bech32.decode(code, Bech32MaxSize)\n  let data = new Uint8Array(bech32.fromWords(words))\n\n  switch (prefix) {\n    case 'nprofile': {\n      let tlv = parseTLV(data)\n      if (!tlv[0]?.[0]) throw new Error('missing TLV 0 for nprofile')\n      if (tlv[0][0].length !== 32) throw new Error('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      let tlv = parseTLV(data)\n      if (!tlv[0]?.[0]) throw new Error('missing TLV 0 for nevent')\n      if (tlv[0][0].length !== 32) throw new Error('TLV 0 should be 32 bytes')\n      if (tlv[2] && tlv[2][0].length !== 32) throw new Error('TLV 2 should be 32 bytes')\n      if (tlv[3] && tlv[3][0].length !== 4) throw new Error('TLV 3 should be 4 bytes')\n\n      return {\n        type: 'nevent',\n        data: {\n          id: bytesToHex(tlv[0][0]),\n          relays: tlv[1] ? tlv[1].map(d => utf8Decoder.decode(d)) : [],\n          author: tlv[2]?.[0] ? bytesToHex(tlv[2][0]) : undefined,\n          kind: tlv[3]?.[0] ? parseInt(bytesToHex(tlv[3][0]), 16) : undefined,\n        },\n      }\n    }\n\n    case 'naddr': {\n      let tlv = parseTLV(data)\n      if (!tlv[0]?.[0]) throw new Error('missing TLV 0 for naddr')\n      if (!tlv[2]?.[0]) throw new Error('missing TLV 2 for naddr')\n      if (tlv[2][0].length !== 32) throw new Error('TLV 2 should be 32 bytes')\n      if (!tlv[3]?.[0]) throw new Error('missing TLV 3 for naddr')\n      if (tlv[3][0].length !== 4) throw new Error('TLV 3 should be 4 bytes')\n\n      return {\n        type: 'naddr',\n        data: {\n          identifier: utf8Decoder.decode(tlv[0][0]),\n          pubkey: bytesToHex(tlv[2][0]),\n          kind: parseInt(bytesToHex(tlv[3][0]), 16),\n          relays: tlv[1] ? tlv[1].map(d => utf8Decoder.decode(d)) : [],\n        },\n      }\n    }\n\n    case 'nsec':\n      return { type: prefix, data }\n\n    case 'npub':\n    case 'note':\n      return { type: prefix, data: bytesToHex(data) }\n\n    default:\n      throw new Error(`unknown prefix ${prefix}`)\n  }\n}\n\ntype TLV = { [t: number]: Uint8Array[] }\n\nfunction parseTLV(data: Uint8Array): TLV {\n  let result: TLV = {}\n  let rest = data\n  while (rest.length > 0) {\n    let t = rest[0]\n    let l = rest[1]\n    let v = rest.slice(2, 2 + l)\n    rest = rest.slice(2 + l)\n    if (v.length < l) throw new Error(`not enough data to read on TLV ${t}`)\n    result[t] = result[t] || []\n    result[t].push(v)\n  }\n  return result\n}\n\nexport function nsecEncode(key: Uint8Array): NSec {\n  return encodeBytes('nsec', key)\n}\n\nexport function npubEncode(hex: string): NPub {\n  return encodeBytes('npub', hexToBytes(hex))\n}\n\nexport function noteEncode(hex: string): Note {\n  return encodeBytes('note', hexToBytes(hex))\n}\n\nfunction encodeBech32<Prefix extends string>(prefix: Prefix, data: Uint8Array): `${Prefix}1${string}` {\n  let words = bech32.toWords(data)\n  return bech32.encode(prefix, words, Bech32MaxSize) as `${Prefix}1${string}`\n}\n\nexport function encodeBytes<Prefix extends string>(prefix: Prefix, bytes: Uint8Array): `${Prefix}1${string}` {\n  return encodeBech32(prefix, bytes)\n}\n\nexport function nprofileEncode(profile: ProfilePointer): NProfile {\n  let data = encodeTLV({\n    0: [hexToBytes(profile.pubkey)],\n    1: (profile.relays || []).map(url => utf8Encoder.encode(url)),\n  })\n  return encodeBech32('nprofile', data)\n}\n\nexport function neventEncode(event: EventPointer): NEvent {\n  let kindArray\n  if (event.kind !== undefined) {\n    kindArray = integerToUint8Array(event.kind)\n  }\n\n  let data = encodeTLV({\n    0: [hexToBytes(event.id)],\n    1: (event.relays || []).map(url => utf8Encoder.encode(url)),\n    2: event.author ? [hexToBytes(event.author)] : [],\n    3: kindArray ? [new Uint8Array(kindArray)] : [],\n  })\n\n  return encodeBech32('nevent', data)\n}\n\nexport function naddrEncode(addr: AddressPointer): NAddr {\n  let kind = new ArrayBuffer(4)\n  new DataView(kind).setUint32(0, addr.kind, false)\n\n  let data = encodeTLV({\n    0: [utf8Encoder.encode(addr.identifier)],\n    1: (addr.relays || []).map(url => utf8Encoder.encode(url)),\n    2: [hexToBytes(addr.pubkey)],\n    3: [new Uint8Array(kind)],\n  })\n  return encodeBech32('naddr', data)\n}\n\nfunction encodeTLV(tlv: TLV): Uint8Array {\n  let entries: Uint8Array[] = []\n\n  Object.entries(tlv)\n    .reverse()\n    .forEach(([t, vs]) => {\n      vs.forEach(v => {\n        let entry = new Uint8Array(v.length + 2)\n        entry.set([parseInt(t)], 0)\n        entry.set([v.length], 1)\n        entry.set(v, 2)\n        entries.push(entry)\n      })\n    })\n\n  return concatBytes(...entries)\n}\n"],
-  "mappings": ";AAAA,SAAS,cAAc;AACvB,SAAS,yBAAyB;AAClC,SAAS,eAAAA,cAAa,mBAAmB;;;ACFzC,SAAS,YAAY,aAAa,kBAAkB;AACpD,SAAS,cAAc;AAsBhB,IAAM,gBAAgB;AAwL7B,SAAS,aAAoC,QAAgB,MAAyC;AACpG,MAAI,QAAQ,OAAO,QAAQ,IAAI;AAC/B,SAAO,OAAO,OAAO,QAAQ,OAAO,aAAa;AACnD;AAEO,SAAS,YAAmC,QAAgB,OAA0C;AAC3G,SAAO,aAAa,QAAQ,KAAK;AACnC;;;ADlNA,SAAS,UAAAC,eAAc;AAEhB,SAAS,QACd,KACA,UACA,OAAe,IACf,MAA0B,GACf;AACX,MAAI,OAAO,YAAY,EAAE;AACzB,MAAI,IAAI,KAAK;AACb,MAAI,MAAM,OAAO,SAAS,UAAU,MAAM,GAAG,MAAM,EAAE,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,OAAO,GAAG,CAAC;AAClF,MAAI,QAAQ,YAAY,EAAE;AAC1B,MAAI,MAAM,WAAW,KAAK,CAAC,GAAG,CAAC;AAC/B,MAAI,QAAQ,kBAAkB,KAAK,OAAO,GAAG;AAC7C,MAAI,aAAa,MAAM,QAAQ,GAAG;AAClC,MAAI,IAAIC,aAAY,WAAW,KAAK,CAAC,CAAI,CAAC,GAAG,WAAW,KAAK,CAAC,IAAI,CAAC,GAAG,MAAM,OAAO,KAAK,UAAU;AAClG,SAAO,YAAY,aAAa,CAAC;AACnC;AAEO,SAAS,QAAQ,WAAmB,UAA8B;AACvE,MAAI,EAAE,QAAQ,MAAM,IAAID,QAAO,OAAO,WAAW,aAAa;AAC9D,MAAI,WAAW,aAAa;AAC1B,UAAM,IAAI,MAAM,kBAAkB,8BAA8B;AAAA,EAClE;AACA,MAAI,IAAI,IAAI,WAAWA,QAAO,UAAU,KAAK,CAAC;AAE9C,MAAI,UAAU,EAAE;AAChB,MAAI,YAAY,GAAM;AACpB,UAAM,IAAI,MAAM,mBAAmB,wBAAwB;AAAA,EAC7D;AAEA,MAAI,OAAO,EAAE;AACb,MAAI,IAAI,KAAK;AAEb,MAAI,OAAO,EAAE,MAAM,GAAG,IAAI,EAAE;AAC5B,MAAI,QAAQ,EAAE,MAAM,IAAI,IAAI,IAAI,KAAK,EAAE;AACvC,MAAI,MAAM,EAAE,IAAI,KAAK;AACrB,MAAI,MAAM,WAAW,KAAK,CAAC,GAAG,CAAC;AAC/B,MAAI,aAAa,EAAE,MAAM,IAAI,KAAK,KAAK,CAAC;AAExC,MAAI,MAAM,OAAO,SAAS,UAAU,MAAM,GAAG,MAAM,EAAE,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,OAAO,GAAG,CAAC;AAClF,MAAI,QAAQ,kBAAkB,KAAK,OAAO,GAAG;AAC7C,MAAI,MAAM,MAAM,QAAQ,UAAU;AAElC,SAAO;AACT;",
-  "names": ["concatBytes", "bech32", "concatBytes"]
+  "sourcesContent": ["import { bech32 } from '@scure/base'\nimport { scrypt } from '@noble/hashes/scrypt.js'\nimport { xchacha20poly1305 } from '@noble/ciphers/chacha.js'\nimport { concatBytes, randomBytes } from '@noble/hashes/utils.js'\nimport { Bech32MaxSize, Ncryptsec, encodeBytes } from './nip19.ts'\n\nexport function encrypt(\n  sec: Uint8Array,\n  password: string,\n  logn: number = 16,\n  ksb: 0x00 | 0x01 | 0x02 = 0x02,\n): Ncryptsec {\n  let salt = randomBytes(16)\n  let n = 2 ** logn\n  let key = scrypt(password.normalize('NFKC'), salt, { N: n, r: 8, p: 1, dkLen: 32 })\n  let nonce = randomBytes(24)\n  let aad = Uint8Array.from([ksb])\n  let xc2p1 = xchacha20poly1305(key, nonce, aad)\n  let ciphertext = xc2p1.encrypt(sec)\n  let b = concatBytes(Uint8Array.from([0x02]), Uint8Array.from([logn]), salt, nonce, aad, ciphertext)\n  return encodeBytes('ncryptsec', b)\n}\n\nexport function decrypt(ncryptsec: string, password: string): Uint8Array {\n  let { prefix, words } = bech32.decode(ncryptsec as `${string}1${string}`, Bech32MaxSize)\n  if (prefix !== 'ncryptsec') {\n    throw new Error(`invalid prefix ${prefix}, expected 'ncryptsec'`)\n  }\n  let b = new Uint8Array(bech32.fromWords(words))\n\n  let version = b[0]\n  if (version !== 0x02) {\n    throw new Error(`invalid version ${version}, expected 0x02`)\n  }\n\n  let logn = b[1]\n  let n = 2 ** logn\n\n  let salt = b.slice(2, 2 + 16)\n  let nonce = b.slice(2 + 16, 2 + 16 + 24)\n  let ksb = b[2 + 16 + 24]\n  let aad = Uint8Array.from([ksb])\n  let ciphertext = b.slice(2 + 16 + 24 + 1)\n\n  let key = scrypt(password.normalize('NFKC'), salt, { N: n, r: 8, p: 1, dkLen: 32 })\n  let xc2p1 = xchacha20poly1305(key, nonce, aad)\n  let sec = xc2p1.decrypt(ciphertext)\n\n  return sec\n}\n", "import { bytesToHex, concatBytes, hexToBytes } from '@noble/hashes/utils.js'\nimport { bech32 } from '@scure/base'\n\nimport { utf8Decoder, utf8Encoder } from './utils.ts'\n\nexport type NProfile = `nprofile1${string}`\nexport type NEvent = `nevent1${string}`\nexport type NAddr = `naddr1${string}`\nexport type NSec = `nsec1${string}`\nexport type NPub = `npub1${string}`\nexport type Note = `note1${string}`\nexport type Ncryptsec = `ncryptsec1${string}`\n\nexport const NostrTypeGuard = {\n  isNProfile: (value?: string | null): value is NProfile => /^nprofile1[a-z\\d]+$/.test(value || ''),\n  isNEvent: (value?: string | null): value is NEvent => /^nevent1[a-z\\d]+$/.test(value || ''),\n  isNAddr: (value?: string | null): value is NAddr => /^naddr1[a-z\\d]+$/.test(value || ''),\n  isNSec: (value?: string | null): value is NSec => /^nsec1[a-z\\d]{58}$/.test(value || ''),\n  isNPub: (value?: string | null): value is NPub => /^npub1[a-z\\d]{58}$/.test(value || ''),\n  isNote: (value?: string | null): value is Note => /^note1[a-z\\d]+$/.test(value || ''),\n  isNcryptsec: (value?: string | null): value is Ncryptsec => /^ncryptsec1[a-z\\d]+$/.test(value || ''),\n}\n\nexport const Bech32MaxSize = 5000\n\n/**\n * Bech32 regex.\n * @see https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki#bech32\n */\nexport const BECH32_REGEX = /[\\x21-\\x7E]{1,83}1[023456789acdefghjklmnpqrstuvwxyz]{6,}/\n\nfunction integerToUint8Array(number: number) {\n  // Create a Uint8Array with enough space to hold a 32-bit integer (4 bytes).\n  const uint8Array = new Uint8Array(4)\n\n  // Use bitwise operations to extract the bytes.\n  uint8Array[0] = (number >> 24) & 0xff // Most significant byte (MSB)\n  uint8Array[1] = (number >> 16) & 0xff\n  uint8Array[2] = (number >> 8) & 0xff\n  uint8Array[3] = number & 0xff // Least significant byte (LSB)\n\n  return uint8Array\n}\n\nexport type ProfilePointer = {\n  pubkey: string // hex\n  relays?: string[]\n}\n\nexport type EventPointer = {\n  id: string // hex\n  relays?: string[]\n  author?: string\n  kind?: number\n}\n\nexport type AddressPointer = {\n  identifier: string\n  pubkey: string\n  kind: number\n  relays?: string[]\n}\n\nexport function decodeNostrURI(nip19code: string): ReturnType<typeof decode> | { type: 'invalid'; data: null } {\n  try {\n    if (nip19code.startsWith('nostr:')) nip19code = nip19code.substring(6)\n    return decode(nip19code)\n  } catch (_err) {\n    return { type: 'invalid', data: null }\n  }\n}\n\nexport type DecodedNevent = {\n  type: 'nevent'\n  data: EventPointer\n}\n\nexport type DecodedNprofile = {\n  type: 'nprofile'\n  data: ProfilePointer\n}\n\nexport type DecodedNaddr = {\n  type: 'naddr'\n  data: AddressPointer\n}\n\nexport type DecodedNsec = {\n  type: 'nsec'\n  data: Uint8Array\n}\n\nexport type DecodedNpub = {\n  type: 'npub'\n  data: string\n}\n\nexport type DecodedNote = {\n  type: 'note'\n  data: string\n}\n\nexport type DecodedResult = DecodedNevent | DecodedNprofile | DecodedNaddr | DecodedNpub | DecodedNsec | DecodedNote\n\nexport function decode(nip19: NEvent): DecodedNevent\nexport function decode(nip19: NProfile): DecodedNprofile\nexport function decode(nip19: NAddr): DecodedNaddr\nexport function decode(nip19: NSec): DecodedNsec\nexport function decode(nip19: NPub): DecodedNpub\nexport function decode(nip19: Note): DecodedNote\nexport function decode(code: string): DecodedResult\nexport function decode(code: string): DecodedResult {\n  let { prefix, words } = bech32.decode(code as `${string}1${string}`, Bech32MaxSize)\n  let data = new Uint8Array(bech32.fromWords(words))\n\n  switch (prefix) {\n    case 'nprofile': {\n      let tlv = parseTLV(data)\n      if (!tlv[0]?.[0]) throw new Error('missing TLV 0 for nprofile')\n      if (tlv[0][0].length !== 32) throw new Error('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      let tlv = parseTLV(data)\n      if (!tlv[0]?.[0]) throw new Error('missing TLV 0 for nevent')\n      if (tlv[0][0].length !== 32) throw new Error('TLV 0 should be 32 bytes')\n      if (tlv[2] && tlv[2][0].length !== 32) throw new Error('TLV 2 should be 32 bytes')\n      if (tlv[3] && tlv[3][0].length !== 4) throw new Error('TLV 3 should be 4 bytes')\n\n      return {\n        type: 'nevent',\n        data: {\n          id: bytesToHex(tlv[0][0]),\n          relays: tlv[1] ? tlv[1].map(d => utf8Decoder.decode(d)) : [],\n          author: tlv[2]?.[0] ? bytesToHex(tlv[2][0]) : undefined,\n          kind: tlv[3]?.[0] ? parseInt(bytesToHex(tlv[3][0]), 16) : undefined,\n        },\n      }\n    }\n\n    case 'naddr': {\n      let tlv = parseTLV(data)\n      if (!tlv[0]?.[0]) throw new Error('missing TLV 0 for naddr')\n      if (!tlv[2]?.[0]) throw new Error('missing TLV 2 for naddr')\n      if (tlv[2][0].length !== 32) throw new Error('TLV 2 should be 32 bytes')\n      if (!tlv[3]?.[0]) throw new Error('missing TLV 3 for naddr')\n      if (tlv[3][0].length !== 4) throw new Error('TLV 3 should be 4 bytes')\n\n      return {\n        type: 'naddr',\n        data: {\n          identifier: utf8Decoder.decode(tlv[0][0]),\n          pubkey: bytesToHex(tlv[2][0]),\n          kind: parseInt(bytesToHex(tlv[3][0]), 16),\n          relays: tlv[1] ? tlv[1].map(d => utf8Decoder.decode(d)) : [],\n        },\n      }\n    }\n\n    case 'nsec':\n      return { type: prefix, data }\n\n    case 'npub':\n    case 'note':\n      return { type: prefix, data: bytesToHex(data) }\n\n    default:\n      throw new Error(`unknown prefix ${prefix}`)\n  }\n}\n\ntype TLV = { [t: number]: Uint8Array[] }\n\nfunction parseTLV(data: Uint8Array): TLV {\n  let result: TLV = {}\n  let rest = data\n  while (rest.length > 0) {\n    let t = rest[0]\n    let l = rest[1]\n    let v = rest.slice(2, 2 + l)\n    rest = rest.slice(2 + l)\n    if (v.length < l) throw new Error(`not enough data to read on TLV ${t}`)\n    result[t] = result[t] || []\n    result[t].push(v)\n  }\n  return result\n}\n\nexport function nsecEncode(key: Uint8Array): NSec {\n  return encodeBytes('nsec', key)\n}\n\nexport function npubEncode(hex: string): NPub {\n  return encodeBytes('npub', hexToBytes(hex))\n}\n\nexport function noteEncode(hex: string): Note {\n  return encodeBytes('note', hexToBytes(hex))\n}\n\nfunction encodeBech32<Prefix extends string>(prefix: Prefix, data: Uint8Array): `${Prefix}1${string}` {\n  let words = bech32.toWords(data)\n  return bech32.encode(prefix, words, Bech32MaxSize) as `${Prefix}1${string}`\n}\n\nexport function encodeBytes<Prefix extends string>(prefix: Prefix, bytes: Uint8Array): `${Prefix}1${string}` {\n  return encodeBech32(prefix, bytes)\n}\n\nexport function nprofileEncode(profile: ProfilePointer): NProfile {\n  let data = encodeTLV({\n    0: [hexToBytes(profile.pubkey)],\n    1: (profile.relays || []).map(url => utf8Encoder.encode(url)),\n  })\n  return encodeBech32('nprofile', data)\n}\n\nexport function neventEncode(event: EventPointer): NEvent {\n  let kindArray\n  if (event.kind !== undefined) {\n    kindArray = integerToUint8Array(event.kind)\n  }\n\n  let data = encodeTLV({\n    0: [hexToBytes(event.id)],\n    1: (event.relays || []).map(url => utf8Encoder.encode(url)),\n    2: event.author ? [hexToBytes(event.author)] : [],\n    3: kindArray ? [new Uint8Array(kindArray)] : [],\n  })\n\n  return encodeBech32('nevent', data)\n}\n\nexport function naddrEncode(addr: AddressPointer): NAddr {\n  let kind = new ArrayBuffer(4)\n  new DataView(kind).setUint32(0, addr.kind, false)\n\n  let data = encodeTLV({\n    0: [utf8Encoder.encode(addr.identifier)],\n    1: (addr.relays || []).map(url => utf8Encoder.encode(url)),\n    2: [hexToBytes(addr.pubkey)],\n    3: [new Uint8Array(kind)],\n  })\n  return encodeBech32('naddr', data)\n}\n\nfunction encodeTLV(tlv: TLV): Uint8Array {\n  let entries: Uint8Array[] = []\n\n  Object.entries(tlv)\n    .reverse()\n    .forEach(([t, vs]) => {\n      vs.forEach(v => {\n        let entry = new Uint8Array(v.length + 2)\n        entry.set([parseInt(t)], 0)\n        entry.set([v.length], 1)\n        entry.set(v, 2)\n        entries.push(entry)\n      })\n    })\n\n  return concatBytes(...entries)\n}\n"],
+  "mappings": ";AAAA,SAAS,UAAAA,eAAc;AACvB,SAAS,cAAc;AACvB,SAAS,yBAAyB;AAClC,SAAS,eAAAC,cAAa,mBAAmB;;;ACHzC,SAAS,YAAY,aAAa,kBAAkB;AACpD,SAAS,cAAc;AAsBhB,IAAM,gBAAgB;AAwL7B,SAAS,aAAoC,QAAgB,MAAyC;AACpG,MAAI,QAAQ,OAAO,QAAQ,IAAI;AAC/B,SAAO,OAAO,OAAO,QAAQ,OAAO,aAAa;AACnD;AAEO,SAAS,YAAmC,QAAgB,OAA0C;AAC3G,SAAO,aAAa,QAAQ,KAAK;AACnC;;;ADhNO,SAAS,QACd,KACA,UACA,OAAe,IACf,MAA0B,GACf;AACX,MAAI,OAAO,YAAY,EAAE;AACzB,MAAI,IAAI,KAAK;AACb,MAAI,MAAM,OAAO,SAAS,UAAU,MAAM,GAAG,MAAM,EAAE,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,OAAO,GAAG,CAAC;AAClF,MAAI,QAAQ,YAAY,EAAE;AAC1B,MAAI,MAAM,WAAW,KAAK,CAAC,GAAG,CAAC;AAC/B,MAAI,QAAQ,kBAAkB,KAAK,OAAO,GAAG;AAC7C,MAAI,aAAa,MAAM,QAAQ,GAAG;AAClC,MAAI,IAAIC,aAAY,WAAW,KAAK,CAAC,CAAI,CAAC,GAAG,WAAW,KAAK,CAAC,IAAI,CAAC,GAAG,MAAM,OAAO,KAAK,UAAU;AAClG,SAAO,YAAY,aAAa,CAAC;AACnC;AAEO,SAAS,QAAQ,WAAmB,UAA8B;AACvE,MAAI,EAAE,QAAQ,MAAM,IAAIC,QAAO,OAAO,WAAoC,aAAa;AACvF,MAAI,WAAW,aAAa;AAC1B,UAAM,IAAI,MAAM,kBAAkB,8BAA8B;AAAA,EAClE;AACA,MAAI,IAAI,IAAI,WAAWA,QAAO,UAAU,KAAK,CAAC;AAE9C,MAAI,UAAU,EAAE;AAChB,MAAI,YAAY,GAAM;AACpB,UAAM,IAAI,MAAM,mBAAmB,wBAAwB;AAAA,EAC7D;AAEA,MAAI,OAAO,EAAE;AACb,MAAI,IAAI,KAAK;AAEb,MAAI,OAAO,EAAE,MAAM,GAAG,IAAI,EAAE;AAC5B,MAAI,QAAQ,EAAE,MAAM,IAAI,IAAI,IAAI,KAAK,EAAE;AACvC,MAAI,MAAM,EAAE,IAAI,KAAK;AACrB,MAAI,MAAM,WAAW,KAAK,CAAC,GAAG,CAAC;AAC/B,MAAI,aAAa,EAAE,MAAM,IAAI,KAAK,KAAK,CAAC;AAExC,MAAI,MAAM,OAAO,SAAS,UAAU,MAAM,GAAG,MAAM,EAAE,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,OAAO,GAAG,CAAC;AAClF,MAAI,QAAQ,kBAAkB,KAAK,OAAO,GAAG;AAC7C,MAAI,MAAM,MAAM,QAAQ,UAAU;AAElC,SAAO;AACT;",
+  "names": ["bech32", "concatBytes", "concatBytes", "bech32"]
 }
Index: package/lib/cjs/nip57.js.map
===================================================================
--- package/lib/cjs/nip57.js.map
+++ package/lib/cjs/nip57.js.map
@@ -1,7 +1,7 @@
 {
   "version": 3,
   "sources": ["../../nip57.ts", "../../pure.ts", "../../core.ts", "../../utils.ts", "../../kinds.ts"],
-  "sourcesContent": ["import { bech32 } from '@scure/base'\n\nimport { NostrEvent, validateEvent, verifyEvent, type Event, type EventTemplate } from './pure.ts'\nimport { utf8Decoder } from './utils.ts'\nimport { isReplaceableKind, isAddressableKind } from './kinds.ts'\n\nvar _fetch: any\n\ntry {\n  _fetch = fetch\n} catch {}\n\nexport function useFetchImplementation(fetchImplementation: any) {\n  _fetch = fetchImplementation\n}\n\nexport async function getZapEndpoint(metadata: Event): Promise<null | string> {\n  try {\n    let lnurl: string = ''\n    let { lud06, lud16 } = JSON.parse(metadata.content)\n    if (lud16) {\n      let [name, domain] = lud16.split('@')\n      lnurl = new URL(`/.well-known/lnurlp/${name}`, `https://${domain}`).toString()\n    } else if (lud06) {\n      let { words } = bech32.decode(lud06, 1000)\n      let data = bech32.fromWords(words)\n      lnurl = utf8Decoder.decode(data)\n    } else {\n      return null\n    }\n\n    let res = await _fetch(lnurl)\n    let body = await res.json()\n\n    if (body.allowsNostr && body.nostrPubkey) {\n      return body.callback\n    }\n  } catch (err) {\n    /*-*/\n  }\n\n  return null\n}\n\ntype ProfileZap = {\n  pubkey: string\n  amount: number\n  comment?: string\n  relays: string[]\n}\n\ntype EventZap = {\n  event: NostrEvent\n  amount: number\n  comment?: string\n  relays: string[]\n}\n\nexport function makeZapRequest(params: ProfileZap | EventZap): EventTemplate {\n  let zr: EventTemplate = {\n    kind: 9734,\n    created_at: Math.round(Date.now() / 1000),\n    content: params.comment || '',\n    tags: [\n      ['p', 'pubkey' in params ? params.pubkey : params.event.pubkey],\n      ['amount', params.amount.toString()],\n      ['relays', ...params.relays],\n    ],\n  }\n\n  if ('event' in params) {\n    zr.tags.push(['e', params.event.id])\n    if (isReplaceableKind(params.event.kind)) {\n      const a = ['a', `${params.event.kind}:${params.event.pubkey}:`]\n      zr.tags.push(a)\n    } else if (isAddressableKind(params.event.kind)) {\n      let d = params.event.tags.find(([t, v]) => t === 'd' && v)\n      if (!d) throw new Error('d tag not found or is empty')\n      const a = ['a', `${params.event.kind}:${params.event.pubkey}:${d[1]}`]\n      zr.tags.push(a)\n    }\n    zr.tags.push(['k', params.event.kind.toString()])\n  }\n\n  return zr\n}\n\nexport function validateZapRequest(zapRequestString: string): string | null {\n  let zapRequest: Event\n\n  try {\n    zapRequest = JSON.parse(zapRequestString)\n  } catch (err) {\n    return 'Invalid zap request JSON.'\n  }\n\n  if (!validateEvent(zapRequest)) return 'Zap request is not a valid Nostr event.'\n\n  if (!verifyEvent(zapRequest)) return 'Invalid signature on zap request.'\n\n  let p = zapRequest.tags.find(([t, v]) => t === 'p' && v)\n  if (!p) return \"Zap request doesn't have a 'p' tag.\"\n  if (!p[1].match(/^[a-f0-9]{64}$/)) return \"Zap request 'p' tag is not valid hex.\"\n\n  let e = zapRequest.tags.find(([t, v]) => t === 'e' && v)\n  if (e && !e[1].match(/^[a-f0-9]{64}$/)) return \"Zap request 'e' tag is not valid hex.\"\n\n  let relays = zapRequest.tags.find(([t, v]) => t === 'relays' && v)\n  if (!relays) return \"Zap request doesn't have a 'relays' tag.\"\n\n  return null\n}\n\nexport function makeZapReceipt({\n  zapRequest,\n  preimage,\n  bolt11,\n  paidAt,\n}: {\n  zapRequest: string\n  preimage?: string\n  bolt11: string\n  paidAt: Date\n}): EventTemplate {\n  let zr: Event = JSON.parse(zapRequest)\n  let tagsFromZapRequest = zr.tags.filter(([t]) => t === 'e' || t === 'p' || t === 'a')\n\n  let zap: EventTemplate = {\n    kind: 9735,\n    created_at: Math.round(paidAt.getTime() / 1000),\n    content: '',\n    tags: [...tagsFromZapRequest, ['P', zr.pubkey], ['bolt11', bolt11], ['description', zapRequest]],\n  }\n\n  if (preimage) {\n    zap.tags.push(['preimage', preimage])\n  }\n\n  return zap\n}\n\nexport function getSatoshisAmountFromBolt11(bolt11: string): number {\n  if (bolt11.length < 50) {\n    return 0\n  }\n  bolt11 = bolt11.substring(0, 50)\n  const idx = bolt11.lastIndexOf('1')\n  if (idx === -1) {\n    return 0\n  }\n  const hrp = bolt11.substring(0, idx)\n  if (!hrp.startsWith('lnbc')) {\n    return 0\n  }\n  const amount = hrp.substring(4) // equivalent to strings.CutPrefix\n\n  if (amount.length < 1) {\n    return 0\n  }\n\n  // if last character is a digit, then the amount can just be interpreted as BTC\n  const char = amount[amount.length - 1]\n  const digit = char.charCodeAt(0) - '0'.charCodeAt(0)\n  const isDigit = digit >= 0 && digit <= 9\n\n  let cutPoint = amount.length - 1\n  if (isDigit) {\n    cutPoint++\n  }\n\n  if (cutPoint < 1) {\n    return 0\n  }\n\n  const num = parseInt(amount.substring(0, cutPoint))\n\n  switch (char) {\n    case 'm':\n      return num * 100000\n    case 'u':\n      return num * 100\n    case 'n':\n      return num / 10\n    case 'p':\n      return num / 10000\n    default:\n      return num * 100000000\n  }\n}\n", "import { schnorr } from '@noble/curves/secp256k1'\nimport { bytesToHex } from '@noble/hashes/utils'\nimport { Nostr, Event, EventTemplate, UnsignedEvent, VerifiedEvent, verifiedSymbol, validateEvent } from './core.ts'\nimport { sha256 } from '@noble/hashes/sha256'\n\nimport { utf8Encoder } from './utils.ts'\n\nclass JS implements Nostr {\n  generateSecretKey(): Uint8Array {\n    return schnorr.utils.randomPrivateKey()\n  }\n  getPublicKey(secretKey: Uint8Array): string {\n    return bytesToHex(schnorr.getPublicKey(secretKey))\n  }\n  finalizeEvent(t: EventTemplate, secretKey: Uint8Array): VerifiedEvent {\n    const event = t as VerifiedEvent\n    event.pubkey = bytesToHex(schnorr.getPublicKey(secretKey))\n    event.id = getEventHash(event)\n    event.sig = bytesToHex(schnorr.sign(getEventHash(event), secretKey))\n    event[verifiedSymbol] = true\n    return event\n  }\n  verifyEvent(event: Event): event is VerifiedEvent {\n    if (typeof event[verifiedSymbol] === 'boolean') return event[verifiedSymbol]\n\n    const hash = getEventHash(event)\n    if (hash !== event.id) {\n      event[verifiedSymbol] = false\n      return false\n    }\n\n    try {\n      const valid = schnorr.verify(event.sig, hash, event.pubkey)\n      event[verifiedSymbol] = valid\n      return valid\n    } catch (err) {\n      event[verifiedSymbol] = false\n      return false\n    }\n  }\n}\n\nexport function serializeEvent(evt: UnsignedEvent): string {\n  if (!validateEvent(evt)) throw new Error(\"can't serialize event with wrong or missing properties\")\n  return JSON.stringify([0, evt.pubkey, evt.created_at, evt.kind, evt.tags, evt.content])\n}\n\nexport function getEventHash(event: UnsignedEvent): string {\n  let eventHash = sha256(utf8Encoder.encode(serializeEvent(event)))\n  return bytesToHex(eventHash)\n}\n\nconst i: JS = new JS()\n\nexport const generateSecretKey = i.generateSecretKey\nexport const getPublicKey = i.getPublicKey\nexport const finalizeEvent = i.finalizeEvent\nexport const verifyEvent = i.verifyEvent\nexport * from './core.ts'\n", "export interface Nostr {\n  generateSecretKey(): Uint8Array\n  getPublicKey(secretKey: Uint8Array): string\n  finalizeEvent(event: EventTemplate, secretKey: Uint8Array): VerifiedEvent\n  verifyEvent(event: Event): event is VerifiedEvent\n}\n\n/** Designates a verified event signature. */\nexport const verifiedSymbol = Symbol('verified')\n\nexport type NostrEvent = {\n  kind: number\n  tags: string[][]\n  content: string\n  created_at: number\n  pubkey: string\n  id: string\n  sig: string\n  [verifiedSymbol]?: boolean\n}\n\nexport type Event = NostrEvent\nexport type EventTemplate = Pick<Event, 'kind' | 'tags' | 'content' | 'created_at'>\nexport type UnsignedEvent = Pick<Event, 'kind' | 'tags' | 'content' | 'created_at' | 'pubkey'>\n\n/** An event whose signature has been verified. */\nexport interface VerifiedEvent extends Event {\n  [verifiedSymbol]: true\n}\n\nconst isRecord = (obj: unknown): obj is Record<string, unknown> => obj instanceof Object\n\nexport function validateEvent<T>(event: T): event is T & UnsignedEvent {\n  if (!isRecord(event)) return false\n  if (typeof event.kind !== 'number') return false\n  if (typeof event.content !== 'string') return false\n  if (typeof event.created_at !== 'number') return false\n  if (typeof event.pubkey !== 'string') return false\n  if (!event.pubkey.match(/^[a-f0-9]{64}$/)) return false\n\n  if (!Array.isArray(event.tags)) return false\n  for (let i = 0; i < event.tags.length; i++) {\n    let tag = event.tags[i]\n    if (!Array.isArray(tag)) return false\n    for (let j = 0; j < tag.length; j++) {\n      if (typeof tag[j] !== 'string') return false\n    }\n  }\n\n  return true\n}\n\n/**\n * Sort events in reverse-chronological order by the `created_at` timestamp,\n * and then by the event `id` (lexicographically) in case of ties.\n * This mutates the array.\n */\nexport function sortEvents(events: Event[]): Event[] {\n  return events.sort((a: NostrEvent, b: NostrEvent): number => {\n    if (a.created_at !== b.created_at) {\n      return b.created_at - a.created_at\n    }\n    return a.id.localeCompare(b.id)\n  })\n}\n", "import type { Event } from './core.ts'\n\nexport const utf8Decoder: TextDecoder = new TextDecoder('utf-8')\nexport const utf8Encoder: TextEncoder = new TextEncoder()\n\nexport { bytesToHex, hexToBytes } from '@noble/hashes/utils'\n\nexport function normalizeURL(url: string): string {\n  try {\n    if (url.indexOf('://') === -1) url = 'wss://' + url\n    let p = new URL(url)\n    if (p.protocol === 'http:') p.protocol = 'ws:'\n    else if (p.protocol === 'https:') p.protocol = 'wss:'\n    p.pathname = p.pathname.replace(/\\/+/g, '/')\n    if (p.pathname.endsWith('/')) p.pathname = p.pathname.slice(0, -1)\n    if ((p.port === '80' && p.protocol === 'ws:') || (p.port === '443' && p.protocol === 'wss:')) p.port = ''\n    p.searchParams.sort()\n    p.hash = ''\n    return p.toString()\n  } catch (e) {\n    throw new Error(`Invalid URL: ${url}`)\n  }\n}\n\nexport function insertEventIntoDescendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return b.created_at - event.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function insertEventIntoAscendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return event.created_at - b.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function binarySearch<T>(arr: T[], compare: (b: T) => number): [number, boolean] {\n  let start = 0\n  let end = arr.length - 1\n\n  while (start <= end) {\n    const mid = Math.floor((start + end) / 2)\n    const cmp = compare(arr[mid])\n\n    if (cmp === 0) {\n      return [mid, true]\n    }\n\n    if (cmp < 0) {\n      end = mid - 1\n    } else {\n      start = mid + 1\n    }\n  }\n\n  return [start, false]\n}\n\nexport class QueueNode<V> {\n  public value: V\n  public next: QueueNode<V> | null = null\n  public prev: QueueNode<V> | null = null\n\n  constructor(message: V) {\n    this.value = message\n  }\n}\n\nexport class Queue<V> {\n  public first: QueueNode<V> | null\n  public last: QueueNode<V> | null\n\n  constructor() {\n    this.first = null\n    this.last = null\n  }\n\n  enqueue(value: V): boolean {\n    const newNode = new QueueNode(value)\n    if (!this.last) {\n      // list is empty\n      this.first = newNode\n      this.last = newNode\n    } else if (this.last === this.first) {\n      // list has a single element\n      this.last = newNode\n      this.last.prev = this.first\n      this.first.next = newNode\n    } else {\n      // list has elements, add as last\n      newNode.prev = this.last\n      this.last.next = newNode\n      this.last = newNode\n    }\n    return true\n  }\n\n  dequeue(): V | null {\n    if (!this.first) return null\n\n    if (this.first === this.last) {\n      const target = this.first\n      this.first = null\n      this.last = null\n      return target.value\n    }\n\n    const target = this.first\n    this.first = target.next\n    if (this.first) {\n      this.first.prev = null // fix: clean up prev pointer\n    }\n\n    return target.value\n  }\n}\n", "import { NostrEvent, validateEvent } from './pure.ts'\n\n/** Events are **regular**, which means they're all expected to be stored by relays. */\nexport function isRegularKind(kind: number): boolean {\n  return kind < 10000 && kind !== 0 && kind !== 3\n}\n\n/** Events are **replaceable**, which means that, for each combination of `pubkey` and `kind`, only the latest event is expected to (SHOULD) be stored by relays, older versions are expected to be discarded. */\nexport function isReplaceableKind(kind: number): boolean {\n  return kind === 0 || kind === 3 || (10000 <= kind && kind < 20000)\n}\n\n/** Events are **ephemeral**, which means they are not expected to be stored by relays. */\nexport function isEphemeralKind(kind: number): boolean {\n  return 20000 <= kind && kind < 30000\n}\n\n/** Events are **addressable**, which means that, for each combination of `pubkey`, `kind` and the `d` tag, only the latest event is expected to be stored by relays, older versions are expected to be discarded. */\nexport function isAddressableKind(kind: number): boolean {\n  return 30000 <= kind && kind < 40000\n}\n\n/** Classification of the event kind. */\nexport type KindClassification = 'regular' | 'replaceable' | 'ephemeral' | 'parameterized' | 'unknown'\n\n/** Determine the classification of this kind of event if known, or `unknown`. */\nexport function classifyKind(kind: number): KindClassification {\n  if (isRegularKind(kind)) return 'regular'\n  if (isReplaceableKind(kind)) return 'replaceable'\n  if (isEphemeralKind(kind)) return 'ephemeral'\n  if (isAddressableKind(kind)) return 'parameterized'\n  return 'unknown'\n}\n\nexport function isKind<T extends number>(event: unknown, kind: T | Array<T>): event is NostrEvent & { kind: T } {\n  const kindAsArray: number[] = kind instanceof Array ? kind : [kind]\n  return (validateEvent(event) && kindAsArray.includes(event.kind)) || false\n}\n\nexport const Metadata = 0\nexport type Metadata = typeof Metadata\nexport const ShortTextNote = 1\nexport type ShortTextNote = typeof ShortTextNote\nexport const RecommendRelay = 2\nexport type RecommendRelay = typeof RecommendRelay\nexport const Contacts = 3\nexport type Contacts = typeof Contacts\nexport const EncryptedDirectMessage = 4\nexport type EncryptedDirectMessage = typeof EncryptedDirectMessage\nexport const EventDeletion = 5\nexport type EventDeletion = typeof EventDeletion\nexport const Repost = 6\nexport type Repost = typeof Repost\nexport const Reaction = 7\nexport type Reaction = typeof Reaction\nexport const BadgeAward = 8\nexport type BadgeAward = typeof BadgeAward\nexport const ChatMessage = 9\nexport type ChatMessage = typeof ChatMessage\nexport const ForumThread = 11\nexport type ForumThread = typeof ForumThread\nexport const Seal = 13\nexport type Seal = typeof Seal\nexport const PrivateDirectMessage = 14\nexport type PrivateDirectMessage = typeof PrivateDirectMessage\nexport const FileMessage = 15\nexport type FileMessage = typeof FileMessage\nexport const GenericRepost = 16\nexport type GenericRepost = typeof GenericRepost\nexport const Photo = 20\nexport type Photo = typeof Photo\nexport const NormalVideo = 21\nexport type NormalVideo = typeof NormalVideo\nexport const ShortVideo = 22\nexport type ShortVideo = typeof ShortVideo\nexport const ChannelCreation = 40\nexport type ChannelCreation = typeof ChannelCreation\nexport const ChannelMetadata = 41\nexport type ChannelMetadata = typeof ChannelMetadata\nexport const ChannelMessage = 42\nexport type ChannelMessage = typeof ChannelMessage\nexport const ChannelHideMessage = 43\nexport type ChannelHideMessage = typeof ChannelHideMessage\nexport const ChannelMuteUser = 44\nexport type ChannelMuteUser = typeof ChannelMuteUser\nexport const OpenTimestamps = 1040\nexport type OpenTimestamps = typeof OpenTimestamps\nexport const GiftWrap = 1059\nexport type GiftWrap = typeof GiftWrap\nexport const Poll = 1068\nexport type Poll = typeof Poll\nexport const FileMetadata = 1063\nexport type FileMetadata = typeof FileMetadata\nexport const Comment = 1111\nexport type Comment = typeof Comment\nexport const LiveChatMessage = 1311\nexport type LiveChatMessage = typeof LiveChatMessage\nexport const Voice = 1222\nexport type Voice = typeof Voice\nexport const VoiceComment = 1244\nexport type VoiceComment = typeof VoiceComment\nexport const ProblemTracker = 1971\nexport type ProblemTracker = typeof ProblemTracker\nexport const Report = 1984\nexport type Report = typeof Report\nexport const Reporting = 1984\nexport type Reporting = typeof Reporting\nexport const Label = 1985\nexport type Label = typeof Label\nexport const CommunityPostApproval = 4550\nexport type CommunityPostApproval = typeof CommunityPostApproval\nexport const JobRequest = 5999\nexport type JobRequest = typeof JobRequest\nexport const JobResult = 6999\nexport type JobResult = typeof JobResult\nexport const JobFeedback = 7000\nexport type JobFeedback = typeof JobFeedback\nexport const ZapGoal = 9041\nexport type ZapGoal = typeof ZapGoal\nexport const ZapRequest = 9734\nexport type ZapRequest = typeof ZapRequest\nexport const Zap = 9735\nexport type Zap = typeof Zap\nexport const Highlights = 9802\nexport type Highlights = typeof Highlights\nexport const PollResponse = 1018\nexport type PollResponse = typeof PollResponse\nexport const Mutelist = 10000\nexport type Mutelist = typeof Mutelist\nexport const Pinlist = 10001\nexport type Pinlist = typeof Pinlist\nexport const RelayList = 10002\nexport type RelayList = typeof RelayList\nexport const BookmarkList = 10003\nexport type BookmarkList = typeof BookmarkList\nexport const CommunitiesList = 10004\nexport type CommunitiesList = typeof CommunitiesList\nexport const PublicChatsList = 10005\nexport type PublicChatsList = typeof PublicChatsList\nexport const BlockedRelaysList = 10006\nexport type BlockedRelaysList = typeof BlockedRelaysList\nexport const SearchRelaysList = 10007\nexport type SearchRelaysList = typeof SearchRelaysList\nexport const FavoriteRelays = 10012\nexport type FavoriteRelays = typeof FavoriteRelays\nexport const InterestsList = 10015\nexport type InterestsList = typeof InterestsList\nexport const UserEmojiList = 10030\nexport type UserEmojiList = typeof UserEmojiList\nexport const DirectMessageRelaysList = 10050\nexport type DirectMessageRelaysList = typeof DirectMessageRelaysList\nexport const FileServerPreference = 10096\nexport type FileServerPreference = typeof FileServerPreference\nexport const BlossomServerList = 10063\nexport type BlossomServerList = typeof BlossomServerList\nexport const NWCWalletInfo = 13194\nexport type NWCWalletInfo = typeof NWCWalletInfo\nexport const LightningPubRPC = 21000\nexport type LightningPubRPC = typeof LightningPubRPC\nexport const ClientAuth = 22242\nexport type ClientAuth = typeof ClientAuth\nexport const NWCWalletRequest = 23194\nexport type NWCWalletRequest = typeof NWCWalletRequest\nexport const NWCWalletResponse = 23195\nexport type NWCWalletResponse = typeof NWCWalletResponse\nexport const NostrConnect = 24133\nexport type NostrConnect = typeof NostrConnect\nexport const HTTPAuth = 27235\nexport type HTTPAuth = typeof HTTPAuth\nexport const Followsets = 30000\nexport type Followsets = typeof Followsets\nexport const Genericlists = 30001\nexport type Genericlists = typeof Genericlists\nexport const Relaysets = 30002\nexport type Relaysets = typeof Relaysets\nexport const Bookmarksets = 30003\nexport type Bookmarksets = typeof Bookmarksets\nexport const Curationsets = 30004\nexport type Curationsets = typeof Curationsets\nexport const ProfileBadges = 30008\nexport type ProfileBadges = typeof ProfileBadges\nexport const BadgeDefinition = 30009\nexport type BadgeDefinition = typeof BadgeDefinition\nexport const Interestsets = 30015\nexport type Interestsets = typeof Interestsets\nexport const CreateOrUpdateStall = 30017\nexport type CreateOrUpdateStall = typeof CreateOrUpdateStall\nexport const CreateOrUpdateProduct = 30018\nexport type CreateOrUpdateProduct = typeof CreateOrUpdateProduct\nexport const LongFormArticle = 30023\nexport type LongFormArticle = typeof LongFormArticle\nexport const DraftLong = 30024\nexport type DraftLong = typeof DraftLong\nexport const Emojisets = 30030\nexport type Emojisets = typeof Emojisets\nexport const Application = 30078\nexport type Application = typeof Application\nexport const LiveEvent = 30311\nexport type LiveEvent = typeof LiveEvent\nexport const UserStatuses = 30315\nexport type UserStatuses = typeof UserStatuses\nexport const ClassifiedListing = 30402\nexport type ClassifiedListing = typeof ClassifiedListing\nexport const DraftClassifiedListing = 30403\nexport type DraftClassifiedListing = typeof DraftClassifiedListing\nexport const Date = 31922\nexport type Date = typeof Date\nexport const Time = 31923\nexport type Time = typeof Time\nexport const Calendar = 31924\nexport type Calendar = typeof Calendar\nexport const CalendarEventRSVP = 31925\nexport type CalendarEventRSVP = typeof CalendarEventRSVP\nexport const RelayReview = 31987\nexport type RelayReview = typeof RelayReview\nexport const Handlerrecommendation = 31989\nexport type Handlerrecommendation = typeof Handlerrecommendation\nexport const Handlerinformation = 31990\nexport type Handlerinformation = typeof Handlerinformation\nexport const CommunityDefinition = 34550\nexport type CommunityDefinition = typeof CommunityDefinition\nexport const GroupMetadata = 39000\nexport type GroupMetadata = typeof GroupMetadata\n"],
-  "mappings": "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",
+  "sourcesContent": ["import { bech32 } from '@scure/base'\n\nimport { NostrEvent, validateEvent, verifyEvent, type Event, type EventTemplate } from './pure.ts'\nimport { utf8Decoder } from './utils.ts'\nimport { isReplaceableKind, isAddressableKind } from './kinds.ts'\n\nvar _fetch: any\n\ntry {\n  _fetch = fetch\n} catch {}\n\nexport function useFetchImplementation(fetchImplementation: any) {\n  _fetch = fetchImplementation\n}\n\nexport async function getZapEndpoint(metadata: Event): Promise<null | string> {\n  try {\n    let lnurl: string = ''\n    let { lud06, lud16 } = JSON.parse(metadata.content)\n    if (lud16) {\n      let [name, domain] = lud16.split('@')\n      lnurl = new URL(`/.well-known/lnurlp/${name}`, `https://${domain}`).toString()\n    } else if (lud06) {\n      let { words } = bech32.decode(lud06, 1000)\n      let data = bech32.fromWords(words)\n      lnurl = utf8Decoder.decode(data)\n    } else {\n      return null\n    }\n\n    let res = await _fetch(lnurl)\n    let body = await res.json()\n\n    if (body.allowsNostr && body.nostrPubkey) {\n      return body.callback\n    }\n  } catch (err) {\n    /*-*/\n  }\n\n  return null\n}\n\ntype ProfileZap = {\n  pubkey: string\n  amount: number\n  comment?: string\n  relays: string[]\n}\n\ntype EventZap = {\n  event: NostrEvent\n  amount: number\n  comment?: string\n  relays: string[]\n}\n\nexport function makeZapRequest(params: ProfileZap | EventZap): EventTemplate {\n  let zr: EventTemplate = {\n    kind: 9734,\n    created_at: Math.round(Date.now() / 1000),\n    content: params.comment || '',\n    tags: [\n      ['p', 'pubkey' in params ? params.pubkey : params.event.pubkey],\n      ['amount', params.amount.toString()],\n      ['relays', ...params.relays],\n    ],\n  }\n\n  if ('event' in params) {\n    zr.tags.push(['e', params.event.id])\n    if (isReplaceableKind(params.event.kind)) {\n      const a = ['a', `${params.event.kind}:${params.event.pubkey}:`]\n      zr.tags.push(a)\n    } else if (isAddressableKind(params.event.kind)) {\n      let d = params.event.tags.find(([t, v]) => t === 'd' && v)\n      if (!d) throw new Error('d tag not found or is empty')\n      const a = ['a', `${params.event.kind}:${params.event.pubkey}:${d[1]}`]\n      zr.tags.push(a)\n    }\n    zr.tags.push(['k', params.event.kind.toString()])\n  }\n\n  return zr\n}\n\nexport function validateZapRequest(zapRequestString: string): string | null {\n  let zapRequest: Event\n\n  try {\n    zapRequest = JSON.parse(zapRequestString)\n  } catch (err) {\n    return 'Invalid zap request JSON.'\n  }\n\n  if (!validateEvent(zapRequest)) return 'Zap request is not a valid Nostr event.'\n\n  if (!verifyEvent(zapRequest)) return 'Invalid signature on zap request.'\n\n  let p = zapRequest.tags.find(([t, v]) => t === 'p' && v)\n  if (!p) return \"Zap request doesn't have a 'p' tag.\"\n  if (!p[1].match(/^[a-f0-9]{64}$/)) return \"Zap request 'p' tag is not valid hex.\"\n\n  let e = zapRequest.tags.find(([t, v]) => t === 'e' && v)\n  if (e && !e[1].match(/^[a-f0-9]{64}$/)) return \"Zap request 'e' tag is not valid hex.\"\n\n  let relays = zapRequest.tags.find(([t, v]) => t === 'relays' && v)\n  if (!relays) return \"Zap request doesn't have a 'relays' tag.\"\n\n  return null\n}\n\nexport function makeZapReceipt({\n  zapRequest,\n  preimage,\n  bolt11,\n  paidAt,\n}: {\n  zapRequest: string\n  preimage?: string\n  bolt11: string\n  paidAt: Date\n}): EventTemplate {\n  let zr: Event = JSON.parse(zapRequest)\n  let tagsFromZapRequest = zr.tags.filter(([t]) => t === 'e' || t === 'p' || t === 'a')\n\n  let zap: EventTemplate = {\n    kind: 9735,\n    created_at: Math.round(paidAt.getTime() / 1000),\n    content: '',\n    tags: [...tagsFromZapRequest, ['P', zr.pubkey], ['bolt11', bolt11], ['description', zapRequest]],\n  }\n\n  if (preimage) {\n    zap.tags.push(['preimage', preimage])\n  }\n\n  return zap\n}\n\nexport function getSatoshisAmountFromBolt11(bolt11: string): number {\n  if (bolt11.length < 50) {\n    return 0\n  }\n  bolt11 = bolt11.substring(0, 50)\n  const idx = bolt11.lastIndexOf('1')\n  if (idx === -1) {\n    return 0\n  }\n  const hrp = bolt11.substring(0, idx)\n  if (!hrp.startsWith('lnbc')) {\n    return 0\n  }\n  const amount = hrp.substring(4) // equivalent to strings.CutPrefix\n\n  if (amount.length < 1) {\n    return 0\n  }\n\n  // if last character is a digit, then the amount can just be interpreted as BTC\n  const char = amount[amount.length - 1]\n  const digit = char.charCodeAt(0) - '0'.charCodeAt(0)\n  const isDigit = digit >= 0 && digit <= 9\n\n  let cutPoint = amount.length - 1\n  if (isDigit) {\n    cutPoint++\n  }\n\n  if (cutPoint < 1) {\n    return 0\n  }\n\n  const num = parseInt(amount.substring(0, cutPoint))\n\n  switch (char) {\n    case 'm':\n      return num * 100000\n    case 'u':\n      return num * 100\n    case 'n':\n      return num / 10\n    case 'p':\n      return num / 10000\n    default:\n      return num * 100000000\n  }\n}\n", "import { schnorr } from '@noble/curves/secp256k1.js'\nimport { bytesToHex, hexToBytes } from '@noble/hashes/utils.js'\nimport { Nostr, Event, EventTemplate, UnsignedEvent, VerifiedEvent, verifiedSymbol, validateEvent } from './core.ts'\nimport { sha256 } from '@noble/hashes/sha2.js'\n\nimport { utf8Encoder } from './utils.ts'\n\nclass JS implements Nostr {\n  generateSecretKey(): Uint8Array {\n    return schnorr.utils.randomSecretKey()\n  }\n  getPublicKey(secretKey: Uint8Array): string {\n    return bytesToHex(schnorr.getPublicKey(secretKey))\n  }\n  finalizeEvent(t: EventTemplate, secretKey: Uint8Array): VerifiedEvent {\n    const event = t as VerifiedEvent\n    event.pubkey = bytesToHex(schnorr.getPublicKey(secretKey))\n    event.id = getEventHash(event)\n    event.sig = bytesToHex(schnorr.sign(hexToBytes(getEventHash(event)), secretKey))\n    event[verifiedSymbol] = true\n    return event\n  }\n  verifyEvent(event: Event): event is VerifiedEvent {\n    if (typeof event[verifiedSymbol] === 'boolean') return event[verifiedSymbol]\n\n    const hash = getEventHash(event)\n    if (hash !== event.id) {\n      event[verifiedSymbol] = false\n      return false\n    }\n\n    try {\n      const valid = schnorr.verify(hexToBytes(event.sig), hexToBytes(hash), hexToBytes(event.pubkey))\n      event[verifiedSymbol] = valid\n      return valid\n    } catch (err) {\n      event[verifiedSymbol] = false\n      return false\n    }\n  }\n}\n\nexport function serializeEvent(evt: UnsignedEvent): string {\n  if (!validateEvent(evt)) throw new Error(\"can't serialize event with wrong or missing properties\")\n  return JSON.stringify([0, evt.pubkey, evt.created_at, evt.kind, evt.tags, evt.content])\n}\n\nexport function getEventHash(event: UnsignedEvent): string {\n  let eventHash = sha256(utf8Encoder.encode(serializeEvent(event)))\n  return bytesToHex(eventHash)\n}\n\nconst i: JS = new JS()\n\nexport const generateSecretKey = i.generateSecretKey\nexport const getPublicKey = i.getPublicKey\nexport const finalizeEvent = i.finalizeEvent\nexport const verifyEvent = i.verifyEvent\nexport * from './core.ts'\n", "export interface Nostr {\n  generateSecretKey(): Uint8Array\n  getPublicKey(secretKey: Uint8Array): string\n  finalizeEvent(event: EventTemplate, secretKey: Uint8Array): VerifiedEvent\n  verifyEvent(event: Event): event is VerifiedEvent\n}\n\n/** Designates a verified event signature. */\nexport const verifiedSymbol = Symbol('verified')\n\nexport type NostrEvent = {\n  kind: number\n  tags: string[][]\n  content: string\n  created_at: number\n  pubkey: string\n  id: string\n  sig: string\n  [verifiedSymbol]?: boolean\n}\n\nexport type Event = NostrEvent\nexport type EventTemplate = Pick<Event, 'kind' | 'tags' | 'content' | 'created_at'>\nexport type UnsignedEvent = Pick<Event, 'kind' | 'tags' | 'content' | 'created_at' | 'pubkey'>\n\n/** An event whose signature has been verified. */\nexport interface VerifiedEvent extends Event {\n  [verifiedSymbol]: true\n}\n\nconst isRecord = (obj: unknown): obj is Record<string, unknown> => obj instanceof Object\n\nexport function validateEvent<T>(event: T): event is T & UnsignedEvent {\n  if (!isRecord(event)) return false\n  if (typeof event.kind !== 'number') return false\n  if (typeof event.content !== 'string') return false\n  if (typeof event.created_at !== 'number') return false\n  if (typeof event.pubkey !== 'string') return false\n  if (!event.pubkey.match(/^[a-f0-9]{64}$/)) return false\n\n  if (!Array.isArray(event.tags)) return false\n  for (let i = 0; i < event.tags.length; i++) {\n    let tag = event.tags[i]\n    if (!Array.isArray(tag)) return false\n    for (let j = 0; j < tag.length; j++) {\n      if (typeof tag[j] !== 'string') return false\n    }\n  }\n\n  return true\n}\n\n/**\n * Sort events in reverse-chronological order by the `created_at` timestamp,\n * and then by the event `id` (lexicographically) in case of ties.\n * This mutates the array.\n */\nexport function sortEvents(events: Event[]): Event[] {\n  return events.sort((a: NostrEvent, b: NostrEvent): number => {\n    if (a.created_at !== b.created_at) {\n      return b.created_at - a.created_at\n    }\n    return a.id.localeCompare(b.id)\n  })\n}\n", "import type { Event } from './core.ts'\n\nexport const utf8Decoder: TextDecoder = new TextDecoder('utf-8')\nexport const utf8Encoder: TextEncoder = new TextEncoder()\n\nexport { bytesToHex, hexToBytes } from '@noble/hashes/utils.js'\n\nexport function normalizeURL(url: string): string {\n  try {\n    if (url.indexOf('://') === -1) url = 'wss://' + url\n    let p = new URL(url)\n    if (p.protocol === 'http:') p.protocol = 'ws:'\n    else if (p.protocol === 'https:') p.protocol = 'wss:'\n    p.pathname = p.pathname.replace(/\\/+/g, '/')\n    if (p.pathname.endsWith('/')) p.pathname = p.pathname.slice(0, -1)\n    if ((p.port === '80' && p.protocol === 'ws:') || (p.port === '443' && p.protocol === 'wss:')) p.port = ''\n    p.searchParams.sort()\n    p.hash = ''\n    return p.toString()\n  } catch (e) {\n    throw new Error(`Invalid URL: ${url}`)\n  }\n}\n\nexport function insertEventIntoDescendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return b.created_at - event.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function insertEventIntoAscendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return event.created_at - b.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function binarySearch<T>(arr: T[], compare: (b: T) => number): [number, boolean] {\n  let start = 0\n  let end = arr.length - 1\n\n  while (start <= end) {\n    const mid = Math.floor((start + end) / 2)\n    const cmp = compare(arr[mid])\n\n    if (cmp === 0) {\n      return [mid, true]\n    }\n\n    if (cmp < 0) {\n      end = mid - 1\n    } else {\n      start = mid + 1\n    }\n  }\n\n  return [start, false]\n}\n\nexport class QueueNode<V> {\n  public value: V\n  public next: QueueNode<V> | null = null\n  public prev: QueueNode<V> | null = null\n\n  constructor(message: V) {\n    this.value = message\n  }\n}\n\nexport class Queue<V> {\n  public first: QueueNode<V> | null\n  public last: QueueNode<V> | null\n\n  constructor() {\n    this.first = null\n    this.last = null\n  }\n\n  enqueue(value: V): boolean {\n    const newNode = new QueueNode(value)\n    if (!this.last) {\n      // list is empty\n      this.first = newNode\n      this.last = newNode\n    } else if (this.last === this.first) {\n      // list has a single element\n      this.last = newNode\n      this.last.prev = this.first\n      this.first.next = newNode\n    } else {\n      // list has elements, add as last\n      newNode.prev = this.last\n      this.last.next = newNode\n      this.last = newNode\n    }\n    return true\n  }\n\n  dequeue(): V | null {\n    if (!this.first) return null\n\n    if (this.first === this.last) {\n      const target = this.first\n      this.first = null\n      this.last = null\n      return target.value\n    }\n\n    const target = this.first\n    this.first = target.next\n    if (this.first) {\n      this.first.prev = null // fix: clean up prev pointer\n    }\n\n    return target.value\n  }\n}\n", "import { NostrEvent, validateEvent } from './pure.ts'\n\n/** Events are **regular**, which means they're all expected to be stored by relays. */\nexport function isRegularKind(kind: number): boolean {\n  return kind < 10000 && kind !== 0 && kind !== 3\n}\n\n/** Events are **replaceable**, which means that, for each combination of `pubkey` and `kind`, only the latest event is expected to (SHOULD) be stored by relays, older versions are expected to be discarded. */\nexport function isReplaceableKind(kind: number): boolean {\n  return kind === 0 || kind === 3 || (10000 <= kind && kind < 20000)\n}\n\n/** Events are **ephemeral**, which means they are not expected to be stored by relays. */\nexport function isEphemeralKind(kind: number): boolean {\n  return 20000 <= kind && kind < 30000\n}\n\n/** Events are **addressable**, which means that, for each combination of `pubkey`, `kind` and the `d` tag, only the latest event is expected to be stored by relays, older versions are expected to be discarded. */\nexport function isAddressableKind(kind: number): boolean {\n  return 30000 <= kind && kind < 40000\n}\n\n/** Classification of the event kind. */\nexport type KindClassification = 'regular' | 'replaceable' | 'ephemeral' | 'parameterized' | 'unknown'\n\n/** Determine the classification of this kind of event if known, or `unknown`. */\nexport function classifyKind(kind: number): KindClassification {\n  if (isRegularKind(kind)) return 'regular'\n  if (isReplaceableKind(kind)) return 'replaceable'\n  if (isEphemeralKind(kind)) return 'ephemeral'\n  if (isAddressableKind(kind)) return 'parameterized'\n  return 'unknown'\n}\n\nexport function isKind<T extends number>(event: unknown, kind: T | Array<T>): event is NostrEvent & { kind: T } {\n  const kindAsArray: number[] = kind instanceof Array ? kind : [kind]\n  return (validateEvent(event) && kindAsArray.includes(event.kind)) || false\n}\n\nexport const Metadata = 0\nexport type Metadata = typeof Metadata\nexport const ShortTextNote = 1\nexport type ShortTextNote = typeof ShortTextNote\nexport const RecommendRelay = 2\nexport type RecommendRelay = typeof RecommendRelay\nexport const Contacts = 3\nexport type Contacts = typeof Contacts\nexport const EncryptedDirectMessage = 4\nexport type EncryptedDirectMessage = typeof EncryptedDirectMessage\nexport const EventDeletion = 5\nexport type EventDeletion = typeof EventDeletion\nexport const Repost = 6\nexport type Repost = typeof Repost\nexport const Reaction = 7\nexport type Reaction = typeof Reaction\nexport const BadgeAward = 8\nexport type BadgeAward = typeof BadgeAward\nexport const ChatMessage = 9\nexport type ChatMessage = typeof ChatMessage\nexport const ForumThread = 11\nexport type ForumThread = typeof ForumThread\nexport const Seal = 13\nexport type Seal = typeof Seal\nexport const PrivateDirectMessage = 14\nexport type PrivateDirectMessage = typeof PrivateDirectMessage\nexport const FileMessage = 15\nexport type FileMessage = typeof FileMessage\nexport const GenericRepost = 16\nexport type GenericRepost = typeof GenericRepost\nexport const Photo = 20\nexport type Photo = typeof Photo\nexport const NormalVideo = 21\nexport type NormalVideo = typeof NormalVideo\nexport const ShortVideo = 22\nexport type ShortVideo = typeof ShortVideo\nexport const ChannelCreation = 40\nexport type ChannelCreation = typeof ChannelCreation\nexport const ChannelMetadata = 41\nexport type ChannelMetadata = typeof ChannelMetadata\nexport const ChannelMessage = 42\nexport type ChannelMessage = typeof ChannelMessage\nexport const ChannelHideMessage = 43\nexport type ChannelHideMessage = typeof ChannelHideMessage\nexport const ChannelMuteUser = 44\nexport type ChannelMuteUser = typeof ChannelMuteUser\nexport const OpenTimestamps = 1040\nexport type OpenTimestamps = typeof OpenTimestamps\nexport const GiftWrap = 1059\nexport type GiftWrap = typeof GiftWrap\nexport const Poll = 1068\nexport type Poll = typeof Poll\nexport const FileMetadata = 1063\nexport type FileMetadata = typeof FileMetadata\nexport const Comment = 1111\nexport type Comment = typeof Comment\nexport const LiveChatMessage = 1311\nexport type LiveChatMessage = typeof LiveChatMessage\nexport const Voice = 1222\nexport type Voice = typeof Voice\nexport const VoiceComment = 1244\nexport type VoiceComment = typeof VoiceComment\nexport const ProblemTracker = 1971\nexport type ProblemTracker = typeof ProblemTracker\nexport const Report = 1984\nexport type Report = typeof Report\nexport const Reporting = 1984\nexport type Reporting = typeof Reporting\nexport const Label = 1985\nexport type Label = typeof Label\nexport const CommunityPostApproval = 4550\nexport type CommunityPostApproval = typeof CommunityPostApproval\nexport const JobRequest = 5999\nexport type JobRequest = typeof JobRequest\nexport const JobResult = 6999\nexport type JobResult = typeof JobResult\nexport const JobFeedback = 7000\nexport type JobFeedback = typeof JobFeedback\nexport const ZapGoal = 9041\nexport type ZapGoal = typeof ZapGoal\nexport const ZapRequest = 9734\nexport type ZapRequest = typeof ZapRequest\nexport const Zap = 9735\nexport type Zap = typeof Zap\nexport const Highlights = 9802\nexport type Highlights = typeof Highlights\nexport const PollResponse = 1018\nexport type PollResponse = typeof PollResponse\nexport const Mutelist = 10000\nexport type Mutelist = typeof Mutelist\nexport const Pinlist = 10001\nexport type Pinlist = typeof Pinlist\nexport const RelayList = 10002\nexport type RelayList = typeof RelayList\nexport const BookmarkList = 10003\nexport type BookmarkList = typeof BookmarkList\nexport const CommunitiesList = 10004\nexport type CommunitiesList = typeof CommunitiesList\nexport const PublicChatsList = 10005\nexport type PublicChatsList = typeof PublicChatsList\nexport const BlockedRelaysList = 10006\nexport type BlockedRelaysList = typeof BlockedRelaysList\nexport const SearchRelaysList = 10007\nexport type SearchRelaysList = typeof SearchRelaysList\nexport const FavoriteRelays = 10012\nexport type FavoriteRelays = typeof FavoriteRelays\nexport const InterestsList = 10015\nexport type InterestsList = typeof InterestsList\nexport const UserEmojiList = 10030\nexport type UserEmojiList = typeof UserEmojiList\nexport const DirectMessageRelaysList = 10050\nexport type DirectMessageRelaysList = typeof DirectMessageRelaysList\nexport const FileServerPreference = 10096\nexport type FileServerPreference = typeof FileServerPreference\nexport const BlossomServerList = 10063\nexport type BlossomServerList = typeof BlossomServerList\nexport const NWCWalletInfo = 13194\nexport type NWCWalletInfo = typeof NWCWalletInfo\nexport const LightningPubRPC = 21000\nexport type LightningPubRPC = typeof LightningPubRPC\nexport const ClientAuth = 22242\nexport type ClientAuth = typeof ClientAuth\nexport const NWCWalletRequest = 23194\nexport type NWCWalletRequest = typeof NWCWalletRequest\nexport const NWCWalletResponse = 23195\nexport type NWCWalletResponse = typeof NWCWalletResponse\nexport const NostrConnect = 24133\nexport type NostrConnect = typeof NostrConnect\nexport const HTTPAuth = 27235\nexport type HTTPAuth = typeof HTTPAuth\nexport const Followsets = 30000\nexport type Followsets = typeof Followsets\nexport const Genericlists = 30001\nexport type Genericlists = typeof Genericlists\nexport const Relaysets = 30002\nexport type Relaysets = typeof Relaysets\nexport const Bookmarksets = 30003\nexport type Bookmarksets = typeof Bookmarksets\nexport const Curationsets = 30004\nexport type Curationsets = typeof Curationsets\nexport const ProfileBadges = 30008\nexport type ProfileBadges = typeof ProfileBadges\nexport const BadgeDefinition = 30009\nexport type BadgeDefinition = typeof BadgeDefinition\nexport const Interestsets = 30015\nexport type Interestsets = typeof Interestsets\nexport const CreateOrUpdateStall = 30017\nexport type CreateOrUpdateStall = typeof CreateOrUpdateStall\nexport const CreateOrUpdateProduct = 30018\nexport type CreateOrUpdateProduct = typeof CreateOrUpdateProduct\nexport const LongFormArticle = 30023\nexport type LongFormArticle = typeof LongFormArticle\nexport const DraftLong = 30024\nexport type DraftLong = typeof DraftLong\nexport const Emojisets = 30030\nexport type Emojisets = typeof Emojisets\nexport const Application = 30078\nexport type Application = typeof Application\nexport const LiveEvent = 30311\nexport type LiveEvent = typeof LiveEvent\nexport const UserStatuses = 30315\nexport type UserStatuses = typeof UserStatuses\nexport const ClassifiedListing = 30402\nexport type ClassifiedListing = typeof ClassifiedListing\nexport const DraftClassifiedListing = 30403\nexport type DraftClassifiedListing = typeof DraftClassifiedListing\nexport const Date = 31922\nexport type Date = typeof Date\nexport const Time = 31923\nexport type Time = typeof Time\nexport const Calendar = 31924\nexport type Calendar = typeof Calendar\nexport const CalendarEventRSVP = 31925\nexport type CalendarEventRSVP = typeof CalendarEventRSVP\nexport const RelayReview = 31987\nexport type RelayReview = typeof RelayReview\nexport const Handlerrecommendation = 31989\nexport type Handlerrecommendation = typeof Handlerrecommendation\nexport const Handlerinformation = 31990\nexport type Handlerinformation = typeof Handlerinformation\nexport const CommunityDefinition = 34550\nexport type CommunityDefinition = typeof CommunityDefinition\nexport const GroupMetadata = 39000\nexport type GroupMetadata = typeof GroupMetadata\n"],
+  "mappings": "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",
   "names": ["import_utils", "i"]
 }
Index: package/lib/esm/nip57.js.map
===================================================================
--- package/lib/esm/nip57.js.map
+++ package/lib/esm/nip57.js.map
@@ -1,7 +1,7 @@
 {
   "version": 3,
   "sources": ["../../nip57.ts", "../../pure.ts", "../../core.ts", "../../utils.ts", "../../kinds.ts"],
-  "sourcesContent": ["import { bech32 } from '@scure/base'\n\nimport { NostrEvent, validateEvent, verifyEvent, type Event, type EventTemplate } from './pure.ts'\nimport { utf8Decoder } from './utils.ts'\nimport { isReplaceableKind, isAddressableKind } from './kinds.ts'\n\nvar _fetch: any\n\ntry {\n  _fetch = fetch\n} catch {}\n\nexport function useFetchImplementation(fetchImplementation: any) {\n  _fetch = fetchImplementation\n}\n\nexport async function getZapEndpoint(metadata: Event): Promise<null | string> {\n  try {\n    let lnurl: string = ''\n    let { lud06, lud16 } = JSON.parse(metadata.content)\n    if (lud16) {\n      let [name, domain] = lud16.split('@')\n      lnurl = new URL(`/.well-known/lnurlp/${name}`, `https://${domain}`).toString()\n    } else if (lud06) {\n      let { words } = bech32.decode(lud06, 1000)\n      let data = bech32.fromWords(words)\n      lnurl = utf8Decoder.decode(data)\n    } else {\n      return null\n    }\n\n    let res = await _fetch(lnurl)\n    let body = await res.json()\n\n    if (body.allowsNostr && body.nostrPubkey) {\n      return body.callback\n    }\n  } catch (err) {\n    /*-*/\n  }\n\n  return null\n}\n\ntype ProfileZap = {\n  pubkey: string\n  amount: number\n  comment?: string\n  relays: string[]\n}\n\ntype EventZap = {\n  event: NostrEvent\n  amount: number\n  comment?: string\n  relays: string[]\n}\n\nexport function makeZapRequest(params: ProfileZap | EventZap): EventTemplate {\n  let zr: EventTemplate = {\n    kind: 9734,\n    created_at: Math.round(Date.now() / 1000),\n    content: params.comment || '',\n    tags: [\n      ['p', 'pubkey' in params ? params.pubkey : params.event.pubkey],\n      ['amount', params.amount.toString()],\n      ['relays', ...params.relays],\n    ],\n  }\n\n  if ('event' in params) {\n    zr.tags.push(['e', params.event.id])\n    if (isReplaceableKind(params.event.kind)) {\n      const a = ['a', `${params.event.kind}:${params.event.pubkey}:`]\n      zr.tags.push(a)\n    } else if (isAddressableKind(params.event.kind)) {\n      let d = params.event.tags.find(([t, v]) => t === 'd' && v)\n      if (!d) throw new Error('d tag not found or is empty')\n      const a = ['a', `${params.event.kind}:${params.event.pubkey}:${d[1]}`]\n      zr.tags.push(a)\n    }\n    zr.tags.push(['k', params.event.kind.toString()])\n  }\n\n  return zr\n}\n\nexport function validateZapRequest(zapRequestString: string): string | null {\n  let zapRequest: Event\n\n  try {\n    zapRequest = JSON.parse(zapRequestString)\n  } catch (err) {\n    return 'Invalid zap request JSON.'\n  }\n\n  if (!validateEvent(zapRequest)) return 'Zap request is not a valid Nostr event.'\n\n  if (!verifyEvent(zapRequest)) return 'Invalid signature on zap request.'\n\n  let p = zapRequest.tags.find(([t, v]) => t === 'p' && v)\n  if (!p) return \"Zap request doesn't have a 'p' tag.\"\n  if (!p[1].match(/^[a-f0-9]{64}$/)) return \"Zap request 'p' tag is not valid hex.\"\n\n  let e = zapRequest.tags.find(([t, v]) => t === 'e' && v)\n  if (e && !e[1].match(/^[a-f0-9]{64}$/)) return \"Zap request 'e' tag is not valid hex.\"\n\n  let relays = zapRequest.tags.find(([t, v]) => t === 'relays' && v)\n  if (!relays) return \"Zap request doesn't have a 'relays' tag.\"\n\n  return null\n}\n\nexport function makeZapReceipt({\n  zapRequest,\n  preimage,\n  bolt11,\n  paidAt,\n}: {\n  zapRequest: string\n  preimage?: string\n  bolt11: string\n  paidAt: Date\n}): EventTemplate {\n  let zr: Event = JSON.parse(zapRequest)\n  let tagsFromZapRequest = zr.tags.filter(([t]) => t === 'e' || t === 'p' || t === 'a')\n\n  let zap: EventTemplate = {\n    kind: 9735,\n    created_at: Math.round(paidAt.getTime() / 1000),\n    content: '',\n    tags: [...tagsFromZapRequest, ['P', zr.pubkey], ['bolt11', bolt11], ['description', zapRequest]],\n  }\n\n  if (preimage) {\n    zap.tags.push(['preimage', preimage])\n  }\n\n  return zap\n}\n\nexport function getSatoshisAmountFromBolt11(bolt11: string): number {\n  if (bolt11.length < 50) {\n    return 0\n  }\n  bolt11 = bolt11.substring(0, 50)\n  const idx = bolt11.lastIndexOf('1')\n  if (idx === -1) {\n    return 0\n  }\n  const hrp = bolt11.substring(0, idx)\n  if (!hrp.startsWith('lnbc')) {\n    return 0\n  }\n  const amount = hrp.substring(4) // equivalent to strings.CutPrefix\n\n  if (amount.length < 1) {\n    return 0\n  }\n\n  // if last character is a digit, then the amount can just be interpreted as BTC\n  const char = amount[amount.length - 1]\n  const digit = char.charCodeAt(0) - '0'.charCodeAt(0)\n  const isDigit = digit >= 0 && digit <= 9\n\n  let cutPoint = amount.length - 1\n  if (isDigit) {\n    cutPoint++\n  }\n\n  if (cutPoint < 1) {\n    return 0\n  }\n\n  const num = parseInt(amount.substring(0, cutPoint))\n\n  switch (char) {\n    case 'm':\n      return num * 100000\n    case 'u':\n      return num * 100\n    case 'n':\n      return num / 10\n    case 'p':\n      return num / 10000\n    default:\n      return num * 100000000\n  }\n}\n", "import { schnorr } from '@noble/curves/secp256k1'\nimport { bytesToHex } from '@noble/hashes/utils'\nimport { Nostr, Event, EventTemplate, UnsignedEvent, VerifiedEvent, verifiedSymbol, validateEvent } from './core.ts'\nimport { sha256 } from '@noble/hashes/sha256'\n\nimport { utf8Encoder } from './utils.ts'\n\nclass JS implements Nostr {\n  generateSecretKey(): Uint8Array {\n    return schnorr.utils.randomPrivateKey()\n  }\n  getPublicKey(secretKey: Uint8Array): string {\n    return bytesToHex(schnorr.getPublicKey(secretKey))\n  }\n  finalizeEvent(t: EventTemplate, secretKey: Uint8Array): VerifiedEvent {\n    const event = t as VerifiedEvent\n    event.pubkey = bytesToHex(schnorr.getPublicKey(secretKey))\n    event.id = getEventHash(event)\n    event.sig = bytesToHex(schnorr.sign(getEventHash(event), secretKey))\n    event[verifiedSymbol] = true\n    return event\n  }\n  verifyEvent(event: Event): event is VerifiedEvent {\n    if (typeof event[verifiedSymbol] === 'boolean') return event[verifiedSymbol]\n\n    const hash = getEventHash(event)\n    if (hash !== event.id) {\n      event[verifiedSymbol] = false\n      return false\n    }\n\n    try {\n      const valid = schnorr.verify(event.sig, hash, event.pubkey)\n      event[verifiedSymbol] = valid\n      return valid\n    } catch (err) {\n      event[verifiedSymbol] = false\n      return false\n    }\n  }\n}\n\nexport function serializeEvent(evt: UnsignedEvent): string {\n  if (!validateEvent(evt)) throw new Error(\"can't serialize event with wrong or missing properties\")\n  return JSON.stringify([0, evt.pubkey, evt.created_at, evt.kind, evt.tags, evt.content])\n}\n\nexport function getEventHash(event: UnsignedEvent): string {\n  let eventHash = sha256(utf8Encoder.encode(serializeEvent(event)))\n  return bytesToHex(eventHash)\n}\n\nconst i: JS = new JS()\n\nexport const generateSecretKey = i.generateSecretKey\nexport const getPublicKey = i.getPublicKey\nexport const finalizeEvent = i.finalizeEvent\nexport const verifyEvent = i.verifyEvent\nexport * from './core.ts'\n", "export interface Nostr {\n  generateSecretKey(): Uint8Array\n  getPublicKey(secretKey: Uint8Array): string\n  finalizeEvent(event: EventTemplate, secretKey: Uint8Array): VerifiedEvent\n  verifyEvent(event: Event): event is VerifiedEvent\n}\n\n/** Designates a verified event signature. */\nexport const verifiedSymbol = Symbol('verified')\n\nexport type NostrEvent = {\n  kind: number\n  tags: string[][]\n  content: string\n  created_at: number\n  pubkey: string\n  id: string\n  sig: string\n  [verifiedSymbol]?: boolean\n}\n\nexport type Event = NostrEvent\nexport type EventTemplate = Pick<Event, 'kind' | 'tags' | 'content' | 'created_at'>\nexport type UnsignedEvent = Pick<Event, 'kind' | 'tags' | 'content' | 'created_at' | 'pubkey'>\n\n/** An event whose signature has been verified. */\nexport interface VerifiedEvent extends Event {\n  [verifiedSymbol]: true\n}\n\nconst isRecord = (obj: unknown): obj is Record<string, unknown> => obj instanceof Object\n\nexport function validateEvent<T>(event: T): event is T & UnsignedEvent {\n  if (!isRecord(event)) return false\n  if (typeof event.kind !== 'number') return false\n  if (typeof event.content !== 'string') return false\n  if (typeof event.created_at !== 'number') return false\n  if (typeof event.pubkey !== 'string') return false\n  if (!event.pubkey.match(/^[a-f0-9]{64}$/)) return false\n\n  if (!Array.isArray(event.tags)) return false\n  for (let i = 0; i < event.tags.length; i++) {\n    let tag = event.tags[i]\n    if (!Array.isArray(tag)) return false\n    for (let j = 0; j < tag.length; j++) {\n      if (typeof tag[j] !== 'string') return false\n    }\n  }\n\n  return true\n}\n\n/**\n * Sort events in reverse-chronological order by the `created_at` timestamp,\n * and then by the event `id` (lexicographically) in case of ties.\n * This mutates the array.\n */\nexport function sortEvents(events: Event[]): Event[] {\n  return events.sort((a: NostrEvent, b: NostrEvent): number => {\n    if (a.created_at !== b.created_at) {\n      return b.created_at - a.created_at\n    }\n    return a.id.localeCompare(b.id)\n  })\n}\n", "import type { Event } from './core.ts'\n\nexport const utf8Decoder: TextDecoder = new TextDecoder('utf-8')\nexport const utf8Encoder: TextEncoder = new TextEncoder()\n\nexport { bytesToHex, hexToBytes } from '@noble/hashes/utils'\n\nexport function normalizeURL(url: string): string {\n  try {\n    if (url.indexOf('://') === -1) url = 'wss://' + url\n    let p = new URL(url)\n    if (p.protocol === 'http:') p.protocol = 'ws:'\n    else if (p.protocol === 'https:') p.protocol = 'wss:'\n    p.pathname = p.pathname.replace(/\\/+/g, '/')\n    if (p.pathname.endsWith('/')) p.pathname = p.pathname.slice(0, -1)\n    if ((p.port === '80' && p.protocol === 'ws:') || (p.port === '443' && p.protocol === 'wss:')) p.port = ''\n    p.searchParams.sort()\n    p.hash = ''\n    return p.toString()\n  } catch (e) {\n    throw new Error(`Invalid URL: ${url}`)\n  }\n}\n\nexport function insertEventIntoDescendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return b.created_at - event.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function insertEventIntoAscendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return event.created_at - b.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function binarySearch<T>(arr: T[], compare: (b: T) => number): [number, boolean] {\n  let start = 0\n  let end = arr.length - 1\n\n  while (start <= end) {\n    const mid = Math.floor((start + end) / 2)\n    const cmp = compare(arr[mid])\n\n    if (cmp === 0) {\n      return [mid, true]\n    }\n\n    if (cmp < 0) {\n      end = mid - 1\n    } else {\n      start = mid + 1\n    }\n  }\n\n  return [start, false]\n}\n\nexport class QueueNode<V> {\n  public value: V\n  public next: QueueNode<V> | null = null\n  public prev: QueueNode<V> | null = null\n\n  constructor(message: V) {\n    this.value = message\n  }\n}\n\nexport class Queue<V> {\n  public first: QueueNode<V> | null\n  public last: QueueNode<V> | null\n\n  constructor() {\n    this.first = null\n    this.last = null\n  }\n\n  enqueue(value: V): boolean {\n    const newNode = new QueueNode(value)\n    if (!this.last) {\n      // list is empty\n      this.first = newNode\n      this.last = newNode\n    } else if (this.last === this.first) {\n      // list has a single element\n      this.last = newNode\n      this.last.prev = this.first\n      this.first.next = newNode\n    } else {\n      // list has elements, add as last\n      newNode.prev = this.last\n      this.last.next = newNode\n      this.last = newNode\n    }\n    return true\n  }\n\n  dequeue(): V | null {\n    if (!this.first) return null\n\n    if (this.first === this.last) {\n      const target = this.first\n      this.first = null\n      this.last = null\n      return target.value\n    }\n\n    const target = this.first\n    this.first = target.next\n    if (this.first) {\n      this.first.prev = null // fix: clean up prev pointer\n    }\n\n    return target.value\n  }\n}\n", "import { NostrEvent, validateEvent } from './pure.ts'\n\n/** Events are **regular**, which means they're all expected to be stored by relays. */\nexport function isRegularKind(kind: number): boolean {\n  return kind < 10000 && kind !== 0 && kind !== 3\n}\n\n/** Events are **replaceable**, which means that, for each combination of `pubkey` and `kind`, only the latest event is expected to (SHOULD) be stored by relays, older versions are expected to be discarded. */\nexport function isReplaceableKind(kind: number): boolean {\n  return kind === 0 || kind === 3 || (10000 <= kind && kind < 20000)\n}\n\n/** Events are **ephemeral**, which means they are not expected to be stored by relays. */\nexport function isEphemeralKind(kind: number): boolean {\n  return 20000 <= kind && kind < 30000\n}\n\n/** Events are **addressable**, which means that, for each combination of `pubkey`, `kind` and the `d` tag, only the latest event is expected to be stored by relays, older versions are expected to be discarded. */\nexport function isAddressableKind(kind: number): boolean {\n  return 30000 <= kind && kind < 40000\n}\n\n/** Classification of the event kind. */\nexport type KindClassification = 'regular' | 'replaceable' | 'ephemeral' | 'parameterized' | 'unknown'\n\n/** Determine the classification of this kind of event if known, or `unknown`. */\nexport function classifyKind(kind: number): KindClassification {\n  if (isRegularKind(kind)) return 'regular'\n  if (isReplaceableKind(kind)) return 'replaceable'\n  if (isEphemeralKind(kind)) return 'ephemeral'\n  if (isAddressableKind(kind)) return 'parameterized'\n  return 'unknown'\n}\n\nexport function isKind<T extends number>(event: unknown, kind: T | Array<T>): event is NostrEvent & { kind: T } {\n  const kindAsArray: number[] = kind instanceof Array ? kind : [kind]\n  return (validateEvent(event) && kindAsArray.includes(event.kind)) || false\n}\n\nexport const Metadata = 0\nexport type Metadata = typeof Metadata\nexport const ShortTextNote = 1\nexport type ShortTextNote = typeof ShortTextNote\nexport const RecommendRelay = 2\nexport type RecommendRelay = typeof RecommendRelay\nexport const Contacts = 3\nexport type Contacts = typeof Contacts\nexport const EncryptedDirectMessage = 4\nexport type EncryptedDirectMessage = typeof EncryptedDirectMessage\nexport const EventDeletion = 5\nexport type EventDeletion = typeof EventDeletion\nexport const Repost = 6\nexport type Repost = typeof Repost\nexport const Reaction = 7\nexport type Reaction = typeof Reaction\nexport const BadgeAward = 8\nexport type BadgeAward = typeof BadgeAward\nexport const ChatMessage = 9\nexport type ChatMessage = typeof ChatMessage\nexport const ForumThread = 11\nexport type ForumThread = typeof ForumThread\nexport const Seal = 13\nexport type Seal = typeof Seal\nexport const PrivateDirectMessage = 14\nexport type PrivateDirectMessage = typeof PrivateDirectMessage\nexport const FileMessage = 15\nexport type FileMessage = typeof FileMessage\nexport const GenericRepost = 16\nexport type GenericRepost = typeof GenericRepost\nexport const Photo = 20\nexport type Photo = typeof Photo\nexport const NormalVideo = 21\nexport type NormalVideo = typeof NormalVideo\nexport const ShortVideo = 22\nexport type ShortVideo = typeof ShortVideo\nexport const ChannelCreation = 40\nexport type ChannelCreation = typeof ChannelCreation\nexport const ChannelMetadata = 41\nexport type ChannelMetadata = typeof ChannelMetadata\nexport const ChannelMessage = 42\nexport type ChannelMessage = typeof ChannelMessage\nexport const ChannelHideMessage = 43\nexport type ChannelHideMessage = typeof ChannelHideMessage\nexport const ChannelMuteUser = 44\nexport type ChannelMuteUser = typeof ChannelMuteUser\nexport const OpenTimestamps = 1040\nexport type OpenTimestamps = typeof OpenTimestamps\nexport const GiftWrap = 1059\nexport type GiftWrap = typeof GiftWrap\nexport const Poll = 1068\nexport type Poll = typeof Poll\nexport const FileMetadata = 1063\nexport type FileMetadata = typeof FileMetadata\nexport const Comment = 1111\nexport type Comment = typeof Comment\nexport const LiveChatMessage = 1311\nexport type LiveChatMessage = typeof LiveChatMessage\nexport const Voice = 1222\nexport type Voice = typeof Voice\nexport const VoiceComment = 1244\nexport type VoiceComment = typeof VoiceComment\nexport const ProblemTracker = 1971\nexport type ProblemTracker = typeof ProblemTracker\nexport const Report = 1984\nexport type Report = typeof Report\nexport const Reporting = 1984\nexport type Reporting = typeof Reporting\nexport const Label = 1985\nexport type Label = typeof Label\nexport const CommunityPostApproval = 4550\nexport type CommunityPostApproval = typeof CommunityPostApproval\nexport const JobRequest = 5999\nexport type JobRequest = typeof JobRequest\nexport const JobResult = 6999\nexport type JobResult = typeof JobResult\nexport const JobFeedback = 7000\nexport type JobFeedback = typeof JobFeedback\nexport const ZapGoal = 9041\nexport type ZapGoal = typeof ZapGoal\nexport const ZapRequest = 9734\nexport type ZapRequest = typeof ZapRequest\nexport const Zap = 9735\nexport type Zap = typeof Zap\nexport const Highlights = 9802\nexport type Highlights = typeof Highlights\nexport const PollResponse = 1018\nexport type PollResponse = typeof PollResponse\nexport const Mutelist = 10000\nexport type Mutelist = typeof Mutelist\nexport const Pinlist = 10001\nexport type Pinlist = typeof Pinlist\nexport const RelayList = 10002\nexport type RelayList = typeof RelayList\nexport const BookmarkList = 10003\nexport type BookmarkList = typeof BookmarkList\nexport const CommunitiesList = 10004\nexport type CommunitiesList = typeof CommunitiesList\nexport const PublicChatsList = 10005\nexport type PublicChatsList = typeof PublicChatsList\nexport const BlockedRelaysList = 10006\nexport type BlockedRelaysList = typeof BlockedRelaysList\nexport const SearchRelaysList = 10007\nexport type SearchRelaysList = typeof SearchRelaysList\nexport const FavoriteRelays = 10012\nexport type FavoriteRelays = typeof FavoriteRelays\nexport const InterestsList = 10015\nexport type InterestsList = typeof InterestsList\nexport const UserEmojiList = 10030\nexport type UserEmojiList = typeof UserEmojiList\nexport const DirectMessageRelaysList = 10050\nexport type DirectMessageRelaysList = typeof DirectMessageRelaysList\nexport const FileServerPreference = 10096\nexport type FileServerPreference = typeof FileServerPreference\nexport const BlossomServerList = 10063\nexport type BlossomServerList = typeof BlossomServerList\nexport const NWCWalletInfo = 13194\nexport type NWCWalletInfo = typeof NWCWalletInfo\nexport const LightningPubRPC = 21000\nexport type LightningPubRPC = typeof LightningPubRPC\nexport const ClientAuth = 22242\nexport type ClientAuth = typeof ClientAuth\nexport const NWCWalletRequest = 23194\nexport type NWCWalletRequest = typeof NWCWalletRequest\nexport const NWCWalletResponse = 23195\nexport type NWCWalletResponse = typeof NWCWalletResponse\nexport const NostrConnect = 24133\nexport type NostrConnect = typeof NostrConnect\nexport const HTTPAuth = 27235\nexport type HTTPAuth = typeof HTTPAuth\nexport const Followsets = 30000\nexport type Followsets = typeof Followsets\nexport const Genericlists = 30001\nexport type Genericlists = typeof Genericlists\nexport const Relaysets = 30002\nexport type Relaysets = typeof Relaysets\nexport const Bookmarksets = 30003\nexport type Bookmarksets = typeof Bookmarksets\nexport const Curationsets = 30004\nexport type Curationsets = typeof Curationsets\nexport const ProfileBadges = 30008\nexport type ProfileBadges = typeof ProfileBadges\nexport const BadgeDefinition = 30009\nexport type BadgeDefinition = typeof BadgeDefinition\nexport const Interestsets = 30015\nexport type Interestsets = typeof Interestsets\nexport const CreateOrUpdateStall = 30017\nexport type CreateOrUpdateStall = typeof CreateOrUpdateStall\nexport const CreateOrUpdateProduct = 30018\nexport type CreateOrUpdateProduct = typeof CreateOrUpdateProduct\nexport const LongFormArticle = 30023\nexport type LongFormArticle = typeof LongFormArticle\nexport const DraftLong = 30024\nexport type DraftLong = typeof DraftLong\nexport const Emojisets = 30030\nexport type Emojisets = typeof Emojisets\nexport const Application = 30078\nexport type Application = typeof Application\nexport const LiveEvent = 30311\nexport type LiveEvent = typeof LiveEvent\nexport const UserStatuses = 30315\nexport type UserStatuses = typeof UserStatuses\nexport const ClassifiedListing = 30402\nexport type ClassifiedListing = typeof ClassifiedListing\nexport const DraftClassifiedListing = 30403\nexport type DraftClassifiedListing = typeof DraftClassifiedListing\nexport const Date = 31922\nexport type Date = typeof Date\nexport const Time = 31923\nexport type Time = typeof Time\nexport const Calendar = 31924\nexport type Calendar = typeof Calendar\nexport const CalendarEventRSVP = 31925\nexport type CalendarEventRSVP = typeof CalendarEventRSVP\nexport const RelayReview = 31987\nexport type RelayReview = typeof RelayReview\nexport const Handlerrecommendation = 31989\nexport type Handlerrecommendation = typeof Handlerrecommendation\nexport const Handlerinformation = 31990\nexport type Handlerinformation = typeof Handlerinformation\nexport const CommunityDefinition = 34550\nexport type CommunityDefinition = typeof CommunityDefinition\nexport const GroupMetadata = 39000\nexport type GroupMetadata = typeof GroupMetadata\n"],
-  "mappings": "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",
-  "names": ["bytesToHex", "i", "bytesToHex"]
+  "sourcesContent": ["import { bech32 } from '@scure/base'\n\nimport { NostrEvent, validateEvent, verifyEvent, type Event, type EventTemplate } from './pure.ts'\nimport { utf8Decoder } from './utils.ts'\nimport { isReplaceableKind, isAddressableKind } from './kinds.ts'\n\nvar _fetch: any\n\ntry {\n  _fetch = fetch\n} catch {}\n\nexport function useFetchImplementation(fetchImplementation: any) {\n  _fetch = fetchImplementation\n}\n\nexport async function getZapEndpoint(metadata: Event): Promise<null | string> {\n  try {\n    let lnurl: string = ''\n    let { lud06, lud16 } = JSON.parse(metadata.content)\n    if (lud16) {\n      let [name, domain] = lud16.split('@')\n      lnurl = new URL(`/.well-known/lnurlp/${name}`, `https://${domain}`).toString()\n    } else if (lud06) {\n      let { words } = bech32.decode(lud06, 1000)\n      let data = bech32.fromWords(words)\n      lnurl = utf8Decoder.decode(data)\n    } else {\n      return null\n    }\n\n    let res = await _fetch(lnurl)\n    let body = await res.json()\n\n    if (body.allowsNostr && body.nostrPubkey) {\n      return body.callback\n    }\n  } catch (err) {\n    /*-*/\n  }\n\n  return null\n}\n\ntype ProfileZap = {\n  pubkey: string\n  amount: number\n  comment?: string\n  relays: string[]\n}\n\ntype EventZap = {\n  event: NostrEvent\n  amount: number\n  comment?: string\n  relays: string[]\n}\n\nexport function makeZapRequest(params: ProfileZap | EventZap): EventTemplate {\n  let zr: EventTemplate = {\n    kind: 9734,\n    created_at: Math.round(Date.now() / 1000),\n    content: params.comment || '',\n    tags: [\n      ['p', 'pubkey' in params ? params.pubkey : params.event.pubkey],\n      ['amount', params.amount.toString()],\n      ['relays', ...params.relays],\n    ],\n  }\n\n  if ('event' in params) {\n    zr.tags.push(['e', params.event.id])\n    if (isReplaceableKind(params.event.kind)) {\n      const a = ['a', `${params.event.kind}:${params.event.pubkey}:`]\n      zr.tags.push(a)\n    } else if (isAddressableKind(params.event.kind)) {\n      let d = params.event.tags.find(([t, v]) => t === 'd' && v)\n      if (!d) throw new Error('d tag not found or is empty')\n      const a = ['a', `${params.event.kind}:${params.event.pubkey}:${d[1]}`]\n      zr.tags.push(a)\n    }\n    zr.tags.push(['k', params.event.kind.toString()])\n  }\n\n  return zr\n}\n\nexport function validateZapRequest(zapRequestString: string): string | null {\n  let zapRequest: Event\n\n  try {\n    zapRequest = JSON.parse(zapRequestString)\n  } catch (err) {\n    return 'Invalid zap request JSON.'\n  }\n\n  if (!validateEvent(zapRequest)) return 'Zap request is not a valid Nostr event.'\n\n  if (!verifyEvent(zapRequest)) return 'Invalid signature on zap request.'\n\n  let p = zapRequest.tags.find(([t, v]) => t === 'p' && v)\n  if (!p) return \"Zap request doesn't have a 'p' tag.\"\n  if (!p[1].match(/^[a-f0-9]{64}$/)) return \"Zap request 'p' tag is not valid hex.\"\n\n  let e = zapRequest.tags.find(([t, v]) => t === 'e' && v)\n  if (e && !e[1].match(/^[a-f0-9]{64}$/)) return \"Zap request 'e' tag is not valid hex.\"\n\n  let relays = zapRequest.tags.find(([t, v]) => t === 'relays' && v)\n  if (!relays) return \"Zap request doesn't have a 'relays' tag.\"\n\n  return null\n}\n\nexport function makeZapReceipt({\n  zapRequest,\n  preimage,\n  bolt11,\n  paidAt,\n}: {\n  zapRequest: string\n  preimage?: string\n  bolt11: string\n  paidAt: Date\n}): EventTemplate {\n  let zr: Event = JSON.parse(zapRequest)\n  let tagsFromZapRequest = zr.tags.filter(([t]) => t === 'e' || t === 'p' || t === 'a')\n\n  let zap: EventTemplate = {\n    kind: 9735,\n    created_at: Math.round(paidAt.getTime() / 1000),\n    content: '',\n    tags: [...tagsFromZapRequest, ['P', zr.pubkey], ['bolt11', bolt11], ['description', zapRequest]],\n  }\n\n  if (preimage) {\n    zap.tags.push(['preimage', preimage])\n  }\n\n  return zap\n}\n\nexport function getSatoshisAmountFromBolt11(bolt11: string): number {\n  if (bolt11.length < 50) {\n    return 0\n  }\n  bolt11 = bolt11.substring(0, 50)\n  const idx = bolt11.lastIndexOf('1')\n  if (idx === -1) {\n    return 0\n  }\n  const hrp = bolt11.substring(0, idx)\n  if (!hrp.startsWith('lnbc')) {\n    return 0\n  }\n  const amount = hrp.substring(4) // equivalent to strings.CutPrefix\n\n  if (amount.length < 1) {\n    return 0\n  }\n\n  // if last character is a digit, then the amount can just be interpreted as BTC\n  const char = amount[amount.length - 1]\n  const digit = char.charCodeAt(0) - '0'.charCodeAt(0)\n  const isDigit = digit >= 0 && digit <= 9\n\n  let cutPoint = amount.length - 1\n  if (isDigit) {\n    cutPoint++\n  }\n\n  if (cutPoint < 1) {\n    return 0\n  }\n\n  const num = parseInt(amount.substring(0, cutPoint))\n\n  switch (char) {\n    case 'm':\n      return num * 100000\n    case 'u':\n      return num * 100\n    case 'n':\n      return num / 10\n    case 'p':\n      return num / 10000\n    default:\n      return num * 100000000\n  }\n}\n", "import { schnorr } from '@noble/curves/secp256k1.js'\nimport { bytesToHex, hexToBytes } from '@noble/hashes/utils.js'\nimport { Nostr, Event, EventTemplate, UnsignedEvent, VerifiedEvent, verifiedSymbol, validateEvent } from './core.ts'\nimport { sha256 } from '@noble/hashes/sha2.js'\n\nimport { utf8Encoder } from './utils.ts'\n\nclass JS implements Nostr {\n  generateSecretKey(): Uint8Array {\n    return schnorr.utils.randomSecretKey()\n  }\n  getPublicKey(secretKey: Uint8Array): string {\n    return bytesToHex(schnorr.getPublicKey(secretKey))\n  }\n  finalizeEvent(t: EventTemplate, secretKey: Uint8Array): VerifiedEvent {\n    const event = t as VerifiedEvent\n    event.pubkey = bytesToHex(schnorr.getPublicKey(secretKey))\n    event.id = getEventHash(event)\n    event.sig = bytesToHex(schnorr.sign(hexToBytes(getEventHash(event)), secretKey))\n    event[verifiedSymbol] = true\n    return event\n  }\n  verifyEvent(event: Event): event is VerifiedEvent {\n    if (typeof event[verifiedSymbol] === 'boolean') return event[verifiedSymbol]\n\n    const hash = getEventHash(event)\n    if (hash !== event.id) {\n      event[verifiedSymbol] = false\n      return false\n    }\n\n    try {\n      const valid = schnorr.verify(hexToBytes(event.sig), hexToBytes(hash), hexToBytes(event.pubkey))\n      event[verifiedSymbol] = valid\n      return valid\n    } catch (err) {\n      event[verifiedSymbol] = false\n      return false\n    }\n  }\n}\n\nexport function serializeEvent(evt: UnsignedEvent): string {\n  if (!validateEvent(evt)) throw new Error(\"can't serialize event with wrong or missing properties\")\n  return JSON.stringify([0, evt.pubkey, evt.created_at, evt.kind, evt.tags, evt.content])\n}\n\nexport function getEventHash(event: UnsignedEvent): string {\n  let eventHash = sha256(utf8Encoder.encode(serializeEvent(event)))\n  return bytesToHex(eventHash)\n}\n\nconst i: JS = new JS()\n\nexport const generateSecretKey = i.generateSecretKey\nexport const getPublicKey = i.getPublicKey\nexport const finalizeEvent = i.finalizeEvent\nexport const verifyEvent = i.verifyEvent\nexport * from './core.ts'\n", "export interface Nostr {\n  generateSecretKey(): Uint8Array\n  getPublicKey(secretKey: Uint8Array): string\n  finalizeEvent(event: EventTemplate, secretKey: Uint8Array): VerifiedEvent\n  verifyEvent(event: Event): event is VerifiedEvent\n}\n\n/** Designates a verified event signature. */\nexport const verifiedSymbol = Symbol('verified')\n\nexport type NostrEvent = {\n  kind: number\n  tags: string[][]\n  content: string\n  created_at: number\n  pubkey: string\n  id: string\n  sig: string\n  [verifiedSymbol]?: boolean\n}\n\nexport type Event = NostrEvent\nexport type EventTemplate = Pick<Event, 'kind' | 'tags' | 'content' | 'created_at'>\nexport type UnsignedEvent = Pick<Event, 'kind' | 'tags' | 'content' | 'created_at' | 'pubkey'>\n\n/** An event whose signature has been verified. */\nexport interface VerifiedEvent extends Event {\n  [verifiedSymbol]: true\n}\n\nconst isRecord = (obj: unknown): obj is Record<string, unknown> => obj instanceof Object\n\nexport function validateEvent<T>(event: T): event is T & UnsignedEvent {\n  if (!isRecord(event)) return false\n  if (typeof event.kind !== 'number') return false\n  if (typeof event.content !== 'string') return false\n  if (typeof event.created_at !== 'number') return false\n  if (typeof event.pubkey !== 'string') return false\n  if (!event.pubkey.match(/^[a-f0-9]{64}$/)) return false\n\n  if (!Array.isArray(event.tags)) return false\n  for (let i = 0; i < event.tags.length; i++) {\n    let tag = event.tags[i]\n    if (!Array.isArray(tag)) return false\n    for (let j = 0; j < tag.length; j++) {\n      if (typeof tag[j] !== 'string') return false\n    }\n  }\n\n  return true\n}\n\n/**\n * Sort events in reverse-chronological order by the `created_at` timestamp,\n * and then by the event `id` (lexicographically) in case of ties.\n * This mutates the array.\n */\nexport function sortEvents(events: Event[]): Event[] {\n  return events.sort((a: NostrEvent, b: NostrEvent): number => {\n    if (a.created_at !== b.created_at) {\n      return b.created_at - a.created_at\n    }\n    return a.id.localeCompare(b.id)\n  })\n}\n", "import type { Event } from './core.ts'\n\nexport const utf8Decoder: TextDecoder = new TextDecoder('utf-8')\nexport const utf8Encoder: TextEncoder = new TextEncoder()\n\nexport { bytesToHex, hexToBytes } from '@noble/hashes/utils.js'\n\nexport function normalizeURL(url: string): string {\n  try {\n    if (url.indexOf('://') === -1) url = 'wss://' + url\n    let p = new URL(url)\n    if (p.protocol === 'http:') p.protocol = 'ws:'\n    else if (p.protocol === 'https:') p.protocol = 'wss:'\n    p.pathname = p.pathname.replace(/\\/+/g, '/')\n    if (p.pathname.endsWith('/')) p.pathname = p.pathname.slice(0, -1)\n    if ((p.port === '80' && p.protocol === 'ws:') || (p.port === '443' && p.protocol === 'wss:')) p.port = ''\n    p.searchParams.sort()\n    p.hash = ''\n    return p.toString()\n  } catch (e) {\n    throw new Error(`Invalid URL: ${url}`)\n  }\n}\n\nexport function insertEventIntoDescendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return b.created_at - event.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function insertEventIntoAscendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return event.created_at - b.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function binarySearch<T>(arr: T[], compare: (b: T) => number): [number, boolean] {\n  let start = 0\n  let end = arr.length - 1\n\n  while (start <= end) {\n    const mid = Math.floor((start + end) / 2)\n    const cmp = compare(arr[mid])\n\n    if (cmp === 0) {\n      return [mid, true]\n    }\n\n    if (cmp < 0) {\n      end = mid - 1\n    } else {\n      start = mid + 1\n    }\n  }\n\n  return [start, false]\n}\n\nexport class QueueNode<V> {\n  public value: V\n  public next: QueueNode<V> | null = null\n  public prev: QueueNode<V> | null = null\n\n  constructor(message: V) {\n    this.value = message\n  }\n}\n\nexport class Queue<V> {\n  public first: QueueNode<V> | null\n  public last: QueueNode<V> | null\n\n  constructor() {\n    this.first = null\n    this.last = null\n  }\n\n  enqueue(value: V): boolean {\n    const newNode = new QueueNode(value)\n    if (!this.last) {\n      // list is empty\n      this.first = newNode\n      this.last = newNode\n    } else if (this.last === this.first) {\n      // list has a single element\n      this.last = newNode\n      this.last.prev = this.first\n      this.first.next = newNode\n    } else {\n      // list has elements, add as last\n      newNode.prev = this.last\n      this.last.next = newNode\n      this.last = newNode\n    }\n    return true\n  }\n\n  dequeue(): V | null {\n    if (!this.first) return null\n\n    if (this.first === this.last) {\n      const target = this.first\n      this.first = null\n      this.last = null\n      return target.value\n    }\n\n    const target = this.first\n    this.first = target.next\n    if (this.first) {\n      this.first.prev = null // fix: clean up prev pointer\n    }\n\n    return target.value\n  }\n}\n", "import { NostrEvent, validateEvent } from './pure.ts'\n\n/** Events are **regular**, which means they're all expected to be stored by relays. */\nexport function isRegularKind(kind: number): boolean {\n  return kind < 10000 && kind !== 0 && kind !== 3\n}\n\n/** Events are **replaceable**, which means that, for each combination of `pubkey` and `kind`, only the latest event is expected to (SHOULD) be stored by relays, older versions are expected to be discarded. */\nexport function isReplaceableKind(kind: number): boolean {\n  return kind === 0 || kind === 3 || (10000 <= kind && kind < 20000)\n}\n\n/** Events are **ephemeral**, which means they are not expected to be stored by relays. */\nexport function isEphemeralKind(kind: number): boolean {\n  return 20000 <= kind && kind < 30000\n}\n\n/** Events are **addressable**, which means that, for each combination of `pubkey`, `kind` and the `d` tag, only the latest event is expected to be stored by relays, older versions are expected to be discarded. */\nexport function isAddressableKind(kind: number): boolean {\n  return 30000 <= kind && kind < 40000\n}\n\n/** Classification of the event kind. */\nexport type KindClassification = 'regular' | 'replaceable' | 'ephemeral' | 'parameterized' | 'unknown'\n\n/** Determine the classification of this kind of event if known, or `unknown`. */\nexport function classifyKind(kind: number): KindClassification {\n  if (isRegularKind(kind)) return 'regular'\n  if (isReplaceableKind(kind)) return 'replaceable'\n  if (isEphemeralKind(kind)) return 'ephemeral'\n  if (isAddressableKind(kind)) return 'parameterized'\n  return 'unknown'\n}\n\nexport function isKind<T extends number>(event: unknown, kind: T | Array<T>): event is NostrEvent & { kind: T } {\n  const kindAsArray: number[] = kind instanceof Array ? kind : [kind]\n  return (validateEvent(event) && kindAsArray.includes(event.kind)) || false\n}\n\nexport const Metadata = 0\nexport type Metadata = typeof Metadata\nexport const ShortTextNote = 1\nexport type ShortTextNote = typeof ShortTextNote\nexport const RecommendRelay = 2\nexport type RecommendRelay = typeof RecommendRelay\nexport const Contacts = 3\nexport type Contacts = typeof Contacts\nexport const EncryptedDirectMessage = 4\nexport type EncryptedDirectMessage = typeof EncryptedDirectMessage\nexport const EventDeletion = 5\nexport type EventDeletion = typeof EventDeletion\nexport const Repost = 6\nexport type Repost = typeof Repost\nexport const Reaction = 7\nexport type Reaction = typeof Reaction\nexport const BadgeAward = 8\nexport type BadgeAward = typeof BadgeAward\nexport const ChatMessage = 9\nexport type ChatMessage = typeof ChatMessage\nexport const ForumThread = 11\nexport type ForumThread = typeof ForumThread\nexport const Seal = 13\nexport type Seal = typeof Seal\nexport const PrivateDirectMessage = 14\nexport type PrivateDirectMessage = typeof PrivateDirectMessage\nexport const FileMessage = 15\nexport type FileMessage = typeof FileMessage\nexport const GenericRepost = 16\nexport type GenericRepost = typeof GenericRepost\nexport const Photo = 20\nexport type Photo = typeof Photo\nexport const NormalVideo = 21\nexport type NormalVideo = typeof NormalVideo\nexport const ShortVideo = 22\nexport type ShortVideo = typeof ShortVideo\nexport const ChannelCreation = 40\nexport type ChannelCreation = typeof ChannelCreation\nexport const ChannelMetadata = 41\nexport type ChannelMetadata = typeof ChannelMetadata\nexport const ChannelMessage = 42\nexport type ChannelMessage = typeof ChannelMessage\nexport const ChannelHideMessage = 43\nexport type ChannelHideMessage = typeof ChannelHideMessage\nexport const ChannelMuteUser = 44\nexport type ChannelMuteUser = typeof ChannelMuteUser\nexport const OpenTimestamps = 1040\nexport type OpenTimestamps = typeof OpenTimestamps\nexport const GiftWrap = 1059\nexport type GiftWrap = typeof GiftWrap\nexport const Poll = 1068\nexport type Poll = typeof Poll\nexport const FileMetadata = 1063\nexport type FileMetadata = typeof FileMetadata\nexport const Comment = 1111\nexport type Comment = typeof Comment\nexport const LiveChatMessage = 1311\nexport type LiveChatMessage = typeof LiveChatMessage\nexport const Voice = 1222\nexport type Voice = typeof Voice\nexport const VoiceComment = 1244\nexport type VoiceComment = typeof VoiceComment\nexport const ProblemTracker = 1971\nexport type ProblemTracker = typeof ProblemTracker\nexport const Report = 1984\nexport type Report = typeof Report\nexport const Reporting = 1984\nexport type Reporting = typeof Reporting\nexport const Label = 1985\nexport type Label = typeof Label\nexport const CommunityPostApproval = 4550\nexport type CommunityPostApproval = typeof CommunityPostApproval\nexport const JobRequest = 5999\nexport type JobRequest = typeof JobRequest\nexport const JobResult = 6999\nexport type JobResult = typeof JobResult\nexport const JobFeedback = 7000\nexport type JobFeedback = typeof JobFeedback\nexport const ZapGoal = 9041\nexport type ZapGoal = typeof ZapGoal\nexport const ZapRequest = 9734\nexport type ZapRequest = typeof ZapRequest\nexport const Zap = 9735\nexport type Zap = typeof Zap\nexport const Highlights = 9802\nexport type Highlights = typeof Highlights\nexport const PollResponse = 1018\nexport type PollResponse = typeof PollResponse\nexport const Mutelist = 10000\nexport type Mutelist = typeof Mutelist\nexport const Pinlist = 10001\nexport type Pinlist = typeof Pinlist\nexport const RelayList = 10002\nexport type RelayList = typeof RelayList\nexport const BookmarkList = 10003\nexport type BookmarkList = typeof BookmarkList\nexport const CommunitiesList = 10004\nexport type CommunitiesList = typeof CommunitiesList\nexport const PublicChatsList = 10005\nexport type PublicChatsList = typeof PublicChatsList\nexport const BlockedRelaysList = 10006\nexport type BlockedRelaysList = typeof BlockedRelaysList\nexport const SearchRelaysList = 10007\nexport type SearchRelaysList = typeof SearchRelaysList\nexport const FavoriteRelays = 10012\nexport type FavoriteRelays = typeof FavoriteRelays\nexport const InterestsList = 10015\nexport type InterestsList = typeof InterestsList\nexport const UserEmojiList = 10030\nexport type UserEmojiList = typeof UserEmojiList\nexport const DirectMessageRelaysList = 10050\nexport type DirectMessageRelaysList = typeof DirectMessageRelaysList\nexport const FileServerPreference = 10096\nexport type FileServerPreference = typeof FileServerPreference\nexport const BlossomServerList = 10063\nexport type BlossomServerList = typeof BlossomServerList\nexport const NWCWalletInfo = 13194\nexport type NWCWalletInfo = typeof NWCWalletInfo\nexport const LightningPubRPC = 21000\nexport type LightningPubRPC = typeof LightningPubRPC\nexport const ClientAuth = 22242\nexport type ClientAuth = typeof ClientAuth\nexport const NWCWalletRequest = 23194\nexport type NWCWalletRequest = typeof NWCWalletRequest\nexport const NWCWalletResponse = 23195\nexport type NWCWalletResponse = typeof NWCWalletResponse\nexport const NostrConnect = 24133\nexport type NostrConnect = typeof NostrConnect\nexport const HTTPAuth = 27235\nexport type HTTPAuth = typeof HTTPAuth\nexport const Followsets = 30000\nexport type Followsets = typeof Followsets\nexport const Genericlists = 30001\nexport type Genericlists = typeof Genericlists\nexport const Relaysets = 30002\nexport type Relaysets = typeof Relaysets\nexport const Bookmarksets = 30003\nexport type Bookmarksets = typeof Bookmarksets\nexport const Curationsets = 30004\nexport type Curationsets = typeof Curationsets\nexport const ProfileBadges = 30008\nexport type ProfileBadges = typeof ProfileBadges\nexport const BadgeDefinition = 30009\nexport type BadgeDefinition = typeof BadgeDefinition\nexport const Interestsets = 30015\nexport type Interestsets = typeof Interestsets\nexport const CreateOrUpdateStall = 30017\nexport type CreateOrUpdateStall = typeof CreateOrUpdateStall\nexport const CreateOrUpdateProduct = 30018\nexport type CreateOrUpdateProduct = typeof CreateOrUpdateProduct\nexport const LongFormArticle = 30023\nexport type LongFormArticle = typeof LongFormArticle\nexport const DraftLong = 30024\nexport type DraftLong = typeof DraftLong\nexport const Emojisets = 30030\nexport type Emojisets = typeof Emojisets\nexport const Application = 30078\nexport type Application = typeof Application\nexport const LiveEvent = 30311\nexport type LiveEvent = typeof LiveEvent\nexport const UserStatuses = 30315\nexport type UserStatuses = typeof UserStatuses\nexport const ClassifiedListing = 30402\nexport type ClassifiedListing = typeof ClassifiedListing\nexport const DraftClassifiedListing = 30403\nexport type DraftClassifiedListing = typeof DraftClassifiedListing\nexport const Date = 31922\nexport type Date = typeof Date\nexport const Time = 31923\nexport type Time = typeof Time\nexport const Calendar = 31924\nexport type Calendar = typeof Calendar\nexport const CalendarEventRSVP = 31925\nexport type CalendarEventRSVP = typeof CalendarEventRSVP\nexport const RelayReview = 31987\nexport type RelayReview = typeof RelayReview\nexport const Handlerrecommendation = 31989\nexport type Handlerrecommendation = typeof Handlerrecommendation\nexport const Handlerinformation = 31990\nexport type Handlerinformation = typeof Handlerinformation\nexport const CommunityDefinition = 34550\nexport type CommunityDefinition = typeof CommunityDefinition\nexport const GroupMetadata = 39000\nexport type GroupMetadata = typeof GroupMetadata\n"],
+  "mappings": "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",
+  "names": ["bytesToHex", "hexToBytes", "i", "bytesToHex", "hexToBytes"]
 }
Index: package/lib/cjs/nip59.js.map
===================================================================
--- package/lib/cjs/nip59.js.map
+++ package/lib/cjs/nip59.js.map
@@ -1,7 +1,7 @@
 {
   "version": 3,
   "sources": ["../../nip59.ts", "../../nip44.ts", "../../utils.ts", "../../pure.ts", "../../core.ts", "../../kinds.ts"],
-  "sourcesContent": ["import { EventTemplate, UnsignedEvent, NostrEvent } from './core.ts'\nimport { getConversationKey, decrypt, encrypt } from './nip44.ts'\nimport { getEventHash, generateSecretKey, finalizeEvent, getPublicKey } from './pure.ts'\nimport { Seal, GiftWrap } from './kinds.ts'\n\ntype Rumor = UnsignedEvent & { id: string }\n\nconst TWO_DAYS = 2 * 24 * 60 * 60\n\nconst now = () => Math.round(Date.now() / 1000)\nconst randomNow = () => Math.round(now() - Math.random() * TWO_DAYS)\n\nconst nip44ConversationKey = (privateKey: Uint8Array, publicKey: string) => getConversationKey(privateKey, publicKey)\n\nconst nip44Encrypt = (data: EventTemplate, privateKey: Uint8Array, publicKey: string) =>\n  encrypt(JSON.stringify(data), nip44ConversationKey(privateKey, publicKey))\n\nconst nip44Decrypt = (data: NostrEvent, privateKey: Uint8Array) =>\n  JSON.parse(decrypt(data.content, nip44ConversationKey(privateKey, data.pubkey)))\n\nexport function createRumor(event: Partial<UnsignedEvent>, privateKey: Uint8Array): Rumor {\n  const rumor = {\n    created_at: now(),\n    content: '',\n    tags: [],\n    ...event,\n    pubkey: getPublicKey(privateKey),\n  } as any\n\n  rumor.id = getEventHash(rumor)\n\n  return rumor as Rumor\n}\n\nexport function createSeal(rumor: Rumor, privateKey: Uint8Array, recipientPublicKey: string): NostrEvent {\n  return finalizeEvent(\n    {\n      kind: Seal,\n      content: nip44Encrypt(rumor, privateKey, recipientPublicKey),\n      created_at: randomNow(),\n      tags: [],\n    },\n    privateKey,\n  )\n}\n\nexport function createWrap(seal: NostrEvent, recipientPublicKey: string): NostrEvent {\n  const randomKey = generateSecretKey()\n\n  return finalizeEvent(\n    {\n      kind: GiftWrap,\n      content: nip44Encrypt(seal, randomKey, recipientPublicKey),\n      created_at: randomNow(),\n      tags: [['p', recipientPublicKey]],\n    },\n    randomKey,\n  ) as NostrEvent\n}\n\nexport function wrapEvent(\n  event: Partial<UnsignedEvent>,\n  senderPrivateKey: Uint8Array,\n  recipientPublicKey: string,\n): NostrEvent {\n  const rumor = createRumor(event, senderPrivateKey)\n\n  const seal = createSeal(rumor, senderPrivateKey, recipientPublicKey)\n  return createWrap(seal, recipientPublicKey)\n}\n\nexport function wrapManyEvents(\n  event: Partial<UnsignedEvent>,\n  senderPrivateKey: Uint8Array,\n  recipientsPublicKeys: string[],\n): NostrEvent[] {\n  if (!recipientsPublicKeys || recipientsPublicKeys.length === 0) {\n    throw new Error('At least one recipient is required.')\n  }\n\n  const senderPublicKey = getPublicKey(senderPrivateKey)\n\n  const wrappeds = [wrapEvent(event, senderPrivateKey, senderPublicKey)]\n\n  recipientsPublicKeys.forEach(recipientPublicKey => {\n    wrappeds.push(wrapEvent(event, senderPrivateKey, recipientPublicKey))\n  })\n\n  return wrappeds\n}\n\nexport function unwrapEvent(wrap: NostrEvent, recipientPrivateKey: Uint8Array): Rumor {\n  const unwrappedSeal = nip44Decrypt(wrap, recipientPrivateKey)\n  return nip44Decrypt(unwrappedSeal, recipientPrivateKey)\n}\n\nexport function unwrapManyEvents(wrappedEvents: NostrEvent[], recipientPrivateKey: Uint8Array): Rumor[] {\n  let unwrappedEvents: Rumor[] = []\n\n  wrappedEvents.forEach(e => {\n    unwrappedEvents.push(unwrapEvent(e, recipientPrivateKey))\n  })\n\n  unwrappedEvents.sort((a, b) => a.created_at - b.created_at)\n\n  return unwrappedEvents\n}\n", "import { chacha20 } from '@noble/ciphers/chacha'\nimport { equalBytes } from '@noble/ciphers/utils'\nimport { secp256k1 } from '@noble/curves/secp256k1'\nimport { extract as hkdf_extract, expand as hkdf_expand } from '@noble/hashes/hkdf'\nimport { hmac } from '@noble/hashes/hmac'\nimport { sha256 } from '@noble/hashes/sha256'\nimport { concatBytes, randomBytes } from '@noble/hashes/utils'\nimport { base64 } from '@scure/base'\n\nimport { utf8Decoder, utf8Encoder } from './utils.ts'\n\nconst minPlaintextSize = 0x0001 // 1b msg => padded to 32b\nconst maxPlaintextSize = 0xffff // 65535 (64kb-1) => padded to 64kb\n\nexport function getConversationKey(privkeyA: Uint8Array, pubkeyB: string): Uint8Array {\n  const sharedX = secp256k1.getSharedSecret(privkeyA, '02' + pubkeyB).subarray(1, 33)\n  return hkdf_extract(sha256, sharedX, 'nip44-v2')\n}\n\nfunction getMessageKeys(\n  conversationKey: Uint8Array,\n  nonce: Uint8Array,\n): { chacha_key: Uint8Array; chacha_nonce: Uint8Array; hmac_key: Uint8Array } {\n  const keys = hkdf_expand(sha256, conversationKey, nonce, 76)\n  return {\n    chacha_key: keys.subarray(0, 32),\n    chacha_nonce: keys.subarray(32, 44),\n    hmac_key: keys.subarray(44, 76),\n  }\n}\n\nfunction calcPaddedLen(len: number): number {\n  if (!Number.isSafeInteger(len) || len < 1) throw new Error('expected positive integer')\n  if (len <= 32) return 32\n  const nextPower = 1 << (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 > maxPlaintextSize)\n    throw new Error('invalid plaintext size: must be between 1 and 65535 bytes')\n  const arr = new Uint8Array(2)\n  new DataView(arr.buffer).setUint16(0, num, false)\n  return arr\n}\n\nfunction pad(plaintext: string): Uint8Array {\n  const unpadded = utf8Encoder.encode(plaintext)\n  const unpaddedLen = unpadded.length\n  const prefix = 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 unpaddedLen = new DataView(padded.buffer).getUint16(0)\n  const unpadded = padded.subarray(2, 2 + unpaddedLen)\n  if (\n    unpaddedLen < minPlaintextSize ||\n    unpaddedLen > maxPlaintextSize ||\n    unpadded.length !== unpaddedLen ||\n    padded.length !== 2 + calcPaddedLen(unpaddedLen)\n  )\n    throw new Error('invalid padding')\n  return utf8Decoder.decode(unpadded)\n}\n\nfunction hmacAad(key: Uint8Array, message: Uint8Array, aad: Uint8Array): Uint8Array {\n  if (aad.length !== 32) throw new Error('AAD associated data must be 32 bytes')\n  const combined = concatBytes(aad, message)\n  return hmac(sha256, key, combined)\n}\n\n// metadata: always 65b (version: 1b, nonce: 32b, max: 32b)\n// plaintext: 1b to 0xffff\n// padded plaintext: 32b to 0xffff\n// ciphertext: 32b+2 to 0xffff+2\n// raw payload: 99 (65+32+2) to 65603 (65+0xffff+2)\n// compressed payload (base64): 132b to 87472b\nfunction decodePayload(payload: string): { nonce: Uint8Array; ciphertext: Uint8Array; mac: Uint8Array } {\n  if (typeof payload !== 'string') throw new Error('payload must be a valid string')\n  const plen = payload.length\n  if (plen < 132 || plen > 87472) throw new Error('invalid payload length: ' + plen)\n  if (payload[0] === '#') throw new Error('unknown encryption version')\n  let data: Uint8Array\n  try {\n    data = base64.decode(payload)\n  } catch (error) {\n    throw new Error('invalid base64: ' + (error as any).message)\n  }\n  const dlen = data.length\n  if (dlen < 99 || dlen > 65603) throw new Error('invalid data length: ' + dlen)\n  const vers = data[0]\n  if (vers !== 2) throw new Error('unknown encryption version ' + vers)\n  return {\n    nonce: data.subarray(1, 33),\n    ciphertext: data.subarray(33, -32),\n    mac: data.subarray(-32),\n  }\n}\n\nexport function encrypt(plaintext: string, conversationKey: Uint8Array, nonce: Uint8Array = randomBytes(32)): string {\n  const { chacha_key, chacha_nonce, hmac_key } = getMessageKeys(conversationKey, nonce)\n  const padded = pad(plaintext)\n  const ciphertext = chacha20(chacha_key, chacha_nonce, padded)\n  const mac = hmacAad(hmac_key, ciphertext, nonce)\n  return base64.encode(concatBytes(new Uint8Array([2]), nonce, ciphertext, mac))\n}\n\nexport function decrypt(payload: string, conversationKey: Uint8Array): string {\n  const { nonce, ciphertext, mac } = decodePayload(payload)\n  const { chacha_key, chacha_nonce, hmac_key } = getMessageKeys(conversationKey, nonce)\n  const calculatedMac = hmacAad(hmac_key, ciphertext, nonce)\n  if (!equalBytes(calculatedMac, mac)) throw new Error('invalid MAC')\n  const padded = chacha20(chacha_key, chacha_nonce, ciphertext)\n  return unpad(padded)\n}\n\nexport const v2 = {\n  utils: {\n    getConversationKey,\n    calcPaddedLen,\n  },\n  encrypt,\n  decrypt,\n}\n", "import type { Event } from './core.ts'\n\nexport const utf8Decoder: TextDecoder = new TextDecoder('utf-8')\nexport const utf8Encoder: TextEncoder = new TextEncoder()\n\nexport { bytesToHex, hexToBytes } from '@noble/hashes/utils'\n\nexport function normalizeURL(url: string): string {\n  try {\n    if (url.indexOf('://') === -1) url = 'wss://' + url\n    let p = new URL(url)\n    if (p.protocol === 'http:') p.protocol = 'ws:'\n    else if (p.protocol === 'https:') p.protocol = 'wss:'\n    p.pathname = p.pathname.replace(/\\/+/g, '/')\n    if (p.pathname.endsWith('/')) p.pathname = p.pathname.slice(0, -1)\n    if ((p.port === '80' && p.protocol === 'ws:') || (p.port === '443' && p.protocol === 'wss:')) p.port = ''\n    p.searchParams.sort()\n    p.hash = ''\n    return p.toString()\n  } catch (e) {\n    throw new Error(`Invalid URL: ${url}`)\n  }\n}\n\nexport function insertEventIntoDescendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return b.created_at - event.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function insertEventIntoAscendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return event.created_at - b.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function binarySearch<T>(arr: T[], compare: (b: T) => number): [number, boolean] {\n  let start = 0\n  let end = arr.length - 1\n\n  while (start <= end) {\n    const mid = Math.floor((start + end) / 2)\n    const cmp = compare(arr[mid])\n\n    if (cmp === 0) {\n      return [mid, true]\n    }\n\n    if (cmp < 0) {\n      end = mid - 1\n    } else {\n      start = mid + 1\n    }\n  }\n\n  return [start, false]\n}\n\nexport class QueueNode<V> {\n  public value: V\n  public next: QueueNode<V> | null = null\n  public prev: QueueNode<V> | null = null\n\n  constructor(message: V) {\n    this.value = message\n  }\n}\n\nexport class Queue<V> {\n  public first: QueueNode<V> | null\n  public last: QueueNode<V> | null\n\n  constructor() {\n    this.first = null\n    this.last = null\n  }\n\n  enqueue(value: V): boolean {\n    const newNode = new QueueNode(value)\n    if (!this.last) {\n      // list is empty\n      this.first = newNode\n      this.last = newNode\n    } else if (this.last === this.first) {\n      // list has a single element\n      this.last = newNode\n      this.last.prev = this.first\n      this.first.next = newNode\n    } else {\n      // list has elements, add as last\n      newNode.prev = this.last\n      this.last.next = newNode\n      this.last = newNode\n    }\n    return true\n  }\n\n  dequeue(): V | null {\n    if (!this.first) return null\n\n    if (this.first === this.last) {\n      const target = this.first\n      this.first = null\n      this.last = null\n      return target.value\n    }\n\n    const target = this.first\n    this.first = target.next\n    if (this.first) {\n      this.first.prev = null // fix: clean up prev pointer\n    }\n\n    return target.value\n  }\n}\n", "import { schnorr } from '@noble/curves/secp256k1'\nimport { bytesToHex } from '@noble/hashes/utils'\nimport { Nostr, Event, EventTemplate, UnsignedEvent, VerifiedEvent, verifiedSymbol, validateEvent } from './core.ts'\nimport { sha256 } from '@noble/hashes/sha256'\n\nimport { utf8Encoder } from './utils.ts'\n\nclass JS implements Nostr {\n  generateSecretKey(): Uint8Array {\n    return schnorr.utils.randomPrivateKey()\n  }\n  getPublicKey(secretKey: Uint8Array): string {\n    return bytesToHex(schnorr.getPublicKey(secretKey))\n  }\n  finalizeEvent(t: EventTemplate, secretKey: Uint8Array): VerifiedEvent {\n    const event = t as VerifiedEvent\n    event.pubkey = bytesToHex(schnorr.getPublicKey(secretKey))\n    event.id = getEventHash(event)\n    event.sig = bytesToHex(schnorr.sign(getEventHash(event), secretKey))\n    event[verifiedSymbol] = true\n    return event\n  }\n  verifyEvent(event: Event): event is VerifiedEvent {\n    if (typeof event[verifiedSymbol] === 'boolean') return event[verifiedSymbol]\n\n    const hash = getEventHash(event)\n    if (hash !== event.id) {\n      event[verifiedSymbol] = false\n      return false\n    }\n\n    try {\n      const valid = schnorr.verify(event.sig, hash, event.pubkey)\n      event[verifiedSymbol] = valid\n      return valid\n    } catch (err) {\n      event[verifiedSymbol] = false\n      return false\n    }\n  }\n}\n\nexport function serializeEvent(evt: UnsignedEvent): string {\n  if (!validateEvent(evt)) throw new Error(\"can't serialize event with wrong or missing properties\")\n  return JSON.stringify([0, evt.pubkey, evt.created_at, evt.kind, evt.tags, evt.content])\n}\n\nexport function getEventHash(event: UnsignedEvent): string {\n  let eventHash = sha256(utf8Encoder.encode(serializeEvent(event)))\n  return bytesToHex(eventHash)\n}\n\nconst i: JS = new JS()\n\nexport const generateSecretKey = i.generateSecretKey\nexport const getPublicKey = i.getPublicKey\nexport const finalizeEvent = i.finalizeEvent\nexport const verifyEvent = i.verifyEvent\nexport * from './core.ts'\n", "export interface Nostr {\n  generateSecretKey(): Uint8Array\n  getPublicKey(secretKey: Uint8Array): string\n  finalizeEvent(event: EventTemplate, secretKey: Uint8Array): VerifiedEvent\n  verifyEvent(event: Event): event is VerifiedEvent\n}\n\n/** Designates a verified event signature. */\nexport const verifiedSymbol = Symbol('verified')\n\nexport type NostrEvent = {\n  kind: number\n  tags: string[][]\n  content: string\n  created_at: number\n  pubkey: string\n  id: string\n  sig: string\n  [verifiedSymbol]?: boolean\n}\n\nexport type Event = NostrEvent\nexport type EventTemplate = Pick<Event, 'kind' | 'tags' | 'content' | 'created_at'>\nexport type UnsignedEvent = Pick<Event, 'kind' | 'tags' | 'content' | 'created_at' | 'pubkey'>\n\n/** An event whose signature has been verified. */\nexport interface VerifiedEvent extends Event {\n  [verifiedSymbol]: true\n}\n\nconst isRecord = (obj: unknown): obj is Record<string, unknown> => obj instanceof Object\n\nexport function validateEvent<T>(event: T): event is T & UnsignedEvent {\n  if (!isRecord(event)) return false\n  if (typeof event.kind !== 'number') return false\n  if (typeof event.content !== 'string') return false\n  if (typeof event.created_at !== 'number') return false\n  if (typeof event.pubkey !== 'string') return false\n  if (!event.pubkey.match(/^[a-f0-9]{64}$/)) return false\n\n  if (!Array.isArray(event.tags)) return false\n  for (let i = 0; i < event.tags.length; i++) {\n    let tag = event.tags[i]\n    if (!Array.isArray(tag)) return false\n    for (let j = 0; j < tag.length; j++) {\n      if (typeof tag[j] !== 'string') return false\n    }\n  }\n\n  return true\n}\n\n/**\n * Sort events in reverse-chronological order by the `created_at` timestamp,\n * and then by the event `id` (lexicographically) in case of ties.\n * This mutates the array.\n */\nexport function sortEvents(events: Event[]): Event[] {\n  return events.sort((a: NostrEvent, b: NostrEvent): number => {\n    if (a.created_at !== b.created_at) {\n      return b.created_at - a.created_at\n    }\n    return a.id.localeCompare(b.id)\n  })\n}\n", "import { NostrEvent, validateEvent } from './pure.ts'\n\n/** Events are **regular**, which means they're all expected to be stored by relays. */\nexport function isRegularKind(kind: number): boolean {\n  return kind < 10000 && kind !== 0 && kind !== 3\n}\n\n/** Events are **replaceable**, which means that, for each combination of `pubkey` and `kind`, only the latest event is expected to (SHOULD) be stored by relays, older versions are expected to be discarded. */\nexport function isReplaceableKind(kind: number): boolean {\n  return kind === 0 || kind === 3 || (10000 <= kind && kind < 20000)\n}\n\n/** Events are **ephemeral**, which means they are not expected to be stored by relays. */\nexport function isEphemeralKind(kind: number): boolean {\n  return 20000 <= kind && kind < 30000\n}\n\n/** Events are **addressable**, which means that, for each combination of `pubkey`, `kind` and the `d` tag, only the latest event is expected to be stored by relays, older versions are expected to be discarded. */\nexport function isAddressableKind(kind: number): boolean {\n  return 30000 <= kind && kind < 40000\n}\n\n/** Classification of the event kind. */\nexport type KindClassification = 'regular' | 'replaceable' | 'ephemeral' | 'parameterized' | 'unknown'\n\n/** Determine the classification of this kind of event if known, or `unknown`. */\nexport function classifyKind(kind: number): KindClassification {\n  if (isRegularKind(kind)) return 'regular'\n  if (isReplaceableKind(kind)) return 'replaceable'\n  if (isEphemeralKind(kind)) return 'ephemeral'\n  if (isAddressableKind(kind)) return 'parameterized'\n  return 'unknown'\n}\n\nexport function isKind<T extends number>(event: unknown, kind: T | Array<T>): event is NostrEvent & { kind: T } {\n  const kindAsArray: number[] = kind instanceof Array ? kind : [kind]\n  return (validateEvent(event) && kindAsArray.includes(event.kind)) || false\n}\n\nexport const Metadata = 0\nexport type Metadata = typeof Metadata\nexport const ShortTextNote = 1\nexport type ShortTextNote = typeof ShortTextNote\nexport const RecommendRelay = 2\nexport type RecommendRelay = typeof RecommendRelay\nexport const Contacts = 3\nexport type Contacts = typeof Contacts\nexport const EncryptedDirectMessage = 4\nexport type EncryptedDirectMessage = typeof EncryptedDirectMessage\nexport const EventDeletion = 5\nexport type EventDeletion = typeof EventDeletion\nexport const Repost = 6\nexport type Repost = typeof Repost\nexport const Reaction = 7\nexport type Reaction = typeof Reaction\nexport const BadgeAward = 8\nexport type BadgeAward = typeof BadgeAward\nexport const ChatMessage = 9\nexport type ChatMessage = typeof ChatMessage\nexport const ForumThread = 11\nexport type ForumThread = typeof ForumThread\nexport const Seal = 13\nexport type Seal = typeof Seal\nexport const PrivateDirectMessage = 14\nexport type PrivateDirectMessage = typeof PrivateDirectMessage\nexport const FileMessage = 15\nexport type FileMessage = typeof FileMessage\nexport const GenericRepost = 16\nexport type GenericRepost = typeof GenericRepost\nexport const Photo = 20\nexport type Photo = typeof Photo\nexport const NormalVideo = 21\nexport type NormalVideo = typeof NormalVideo\nexport const ShortVideo = 22\nexport type ShortVideo = typeof ShortVideo\nexport const ChannelCreation = 40\nexport type ChannelCreation = typeof ChannelCreation\nexport const ChannelMetadata = 41\nexport type ChannelMetadata = typeof ChannelMetadata\nexport const ChannelMessage = 42\nexport type ChannelMessage = typeof ChannelMessage\nexport const ChannelHideMessage = 43\nexport type ChannelHideMessage = typeof ChannelHideMessage\nexport const ChannelMuteUser = 44\nexport type ChannelMuteUser = typeof ChannelMuteUser\nexport const OpenTimestamps = 1040\nexport type OpenTimestamps = typeof OpenTimestamps\nexport const GiftWrap = 1059\nexport type GiftWrap = typeof GiftWrap\nexport const Poll = 1068\nexport type Poll = typeof Poll\nexport const FileMetadata = 1063\nexport type FileMetadata = typeof FileMetadata\nexport const Comment = 1111\nexport type Comment = typeof Comment\nexport const LiveChatMessage = 1311\nexport type LiveChatMessage = typeof LiveChatMessage\nexport const Voice = 1222\nexport type Voice = typeof Voice\nexport const VoiceComment = 1244\nexport type VoiceComment = typeof VoiceComment\nexport const ProblemTracker = 1971\nexport type ProblemTracker = typeof ProblemTracker\nexport const Report = 1984\nexport type Report = typeof Report\nexport const Reporting = 1984\nexport type Reporting = typeof Reporting\nexport const Label = 1985\nexport type Label = typeof Label\nexport const CommunityPostApproval = 4550\nexport type CommunityPostApproval = typeof CommunityPostApproval\nexport const JobRequest = 5999\nexport type JobRequest = typeof JobRequest\nexport const JobResult = 6999\nexport type JobResult = typeof JobResult\nexport const JobFeedback = 7000\nexport type JobFeedback = typeof JobFeedback\nexport const ZapGoal = 9041\nexport type ZapGoal = typeof ZapGoal\nexport const ZapRequest = 9734\nexport type ZapRequest = typeof ZapRequest\nexport const Zap = 9735\nexport type Zap = typeof Zap\nexport const Highlights = 9802\nexport type Highlights = typeof Highlights\nexport const PollResponse = 1018\nexport type PollResponse = typeof PollResponse\nexport const Mutelist = 10000\nexport type Mutelist = typeof Mutelist\nexport const Pinlist = 10001\nexport type Pinlist = typeof Pinlist\nexport const RelayList = 10002\nexport type RelayList = typeof RelayList\nexport const BookmarkList = 10003\nexport type BookmarkList = typeof BookmarkList\nexport const CommunitiesList = 10004\nexport type CommunitiesList = typeof CommunitiesList\nexport const PublicChatsList = 10005\nexport type PublicChatsList = typeof PublicChatsList\nexport const BlockedRelaysList = 10006\nexport type BlockedRelaysList = typeof BlockedRelaysList\nexport const SearchRelaysList = 10007\nexport type SearchRelaysList = typeof SearchRelaysList\nexport const FavoriteRelays = 10012\nexport type FavoriteRelays = typeof FavoriteRelays\nexport const InterestsList = 10015\nexport type InterestsList = typeof InterestsList\nexport const UserEmojiList = 10030\nexport type UserEmojiList = typeof UserEmojiList\nexport const DirectMessageRelaysList = 10050\nexport type DirectMessageRelaysList = typeof DirectMessageRelaysList\nexport const FileServerPreference = 10096\nexport type FileServerPreference = typeof FileServerPreference\nexport const BlossomServerList = 10063\nexport type BlossomServerList = typeof BlossomServerList\nexport const NWCWalletInfo = 13194\nexport type NWCWalletInfo = typeof NWCWalletInfo\nexport const LightningPubRPC = 21000\nexport type LightningPubRPC = typeof LightningPubRPC\nexport const ClientAuth = 22242\nexport type ClientAuth = typeof ClientAuth\nexport const NWCWalletRequest = 23194\nexport type NWCWalletRequest = typeof NWCWalletRequest\nexport const NWCWalletResponse = 23195\nexport type NWCWalletResponse = typeof NWCWalletResponse\nexport const NostrConnect = 24133\nexport type NostrConnect = typeof NostrConnect\nexport const HTTPAuth = 27235\nexport type HTTPAuth = typeof HTTPAuth\nexport const Followsets = 30000\nexport type Followsets = typeof Followsets\nexport const Genericlists = 30001\nexport type Genericlists = typeof Genericlists\nexport const Relaysets = 30002\nexport type Relaysets = typeof Relaysets\nexport const Bookmarksets = 30003\nexport type Bookmarksets = typeof Bookmarksets\nexport const Curationsets = 30004\nexport type Curationsets = typeof Curationsets\nexport const ProfileBadges = 30008\nexport type ProfileBadges = typeof ProfileBadges\nexport const BadgeDefinition = 30009\nexport type BadgeDefinition = typeof BadgeDefinition\nexport const Interestsets = 30015\nexport type Interestsets = typeof Interestsets\nexport const CreateOrUpdateStall = 30017\nexport type CreateOrUpdateStall = typeof CreateOrUpdateStall\nexport const CreateOrUpdateProduct = 30018\nexport type CreateOrUpdateProduct = typeof CreateOrUpdateProduct\nexport const LongFormArticle = 30023\nexport type LongFormArticle = typeof LongFormArticle\nexport const DraftLong = 30024\nexport type DraftLong = typeof DraftLong\nexport const Emojisets = 30030\nexport type Emojisets = typeof Emojisets\nexport const Application = 30078\nexport type Application = typeof Application\nexport const LiveEvent = 30311\nexport type LiveEvent = typeof LiveEvent\nexport const UserStatuses = 30315\nexport type UserStatuses = typeof UserStatuses\nexport const ClassifiedListing = 30402\nexport type ClassifiedListing = typeof ClassifiedListing\nexport const DraftClassifiedListing = 30403\nexport type DraftClassifiedListing = typeof DraftClassifiedListing\nexport const Date = 31922\nexport type Date = typeof Date\nexport const Time = 31923\nexport type Time = typeof Time\nexport const Calendar = 31924\nexport type Calendar = typeof Calendar\nexport const CalendarEventRSVP = 31925\nexport type CalendarEventRSVP = typeof CalendarEventRSVP\nexport const RelayReview = 31987\nexport type RelayReview = typeof RelayReview\nexport const Handlerrecommendation = 31989\nexport type Handlerrecommendation = typeof Handlerrecommendation\nexport const Handlerinformation = 31990\nexport type Handlerinformation = typeof Handlerinformation\nexport const CommunityDefinition = 34550\nexport type CommunityDefinition = typeof CommunityDefinition\nexport const GroupMetadata = 39000\nexport type GroupMetadata = typeof GroupMetadata\n"],
-  "mappings": "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",
-  "names": ["import_utils", "hkdf_extract", "hkdf_expand", "import_secp256k1", "import_utils", "i", "import_sha256"]
+  "sourcesContent": ["import { EventTemplate, UnsignedEvent, NostrEvent } from './core.ts'\nimport { getConversationKey, decrypt, encrypt } from './nip44.ts'\nimport { getEventHash, generateSecretKey, finalizeEvent, getPublicKey } from './pure.ts'\nimport { Seal, GiftWrap } from './kinds.ts'\n\ntype Rumor = UnsignedEvent & { id: string }\n\nconst TWO_DAYS = 2 * 24 * 60 * 60\n\nconst now = () => Math.round(Date.now() / 1000)\nconst randomNow = () => Math.round(now() - Math.random() * TWO_DAYS)\n\nconst nip44ConversationKey = (privateKey: Uint8Array, publicKey: string) => getConversationKey(privateKey, publicKey)\n\nconst nip44Encrypt = (data: EventTemplate, privateKey: Uint8Array, publicKey: string) =>\n  encrypt(JSON.stringify(data), nip44ConversationKey(privateKey, publicKey))\n\nconst nip44Decrypt = (data: NostrEvent, privateKey: Uint8Array) =>\n  JSON.parse(decrypt(data.content, nip44ConversationKey(privateKey, data.pubkey)))\n\nexport function createRumor(event: Partial<UnsignedEvent>, privateKey: Uint8Array): Rumor {\n  const rumor = {\n    created_at: now(),\n    content: '',\n    tags: [],\n    ...event,\n    pubkey: getPublicKey(privateKey),\n  } as any\n\n  rumor.id = getEventHash(rumor)\n\n  return rumor as Rumor\n}\n\nexport function createSeal(rumor: Rumor, privateKey: Uint8Array, recipientPublicKey: string): NostrEvent {\n  return finalizeEvent(\n    {\n      kind: Seal,\n      content: nip44Encrypt(rumor, privateKey, recipientPublicKey),\n      created_at: randomNow(),\n      tags: [],\n    },\n    privateKey,\n  )\n}\n\nexport function createWrap(seal: NostrEvent, recipientPublicKey: string): NostrEvent {\n  const randomKey = generateSecretKey()\n\n  return finalizeEvent(\n    {\n      kind: GiftWrap,\n      content: nip44Encrypt(seal, randomKey, recipientPublicKey),\n      created_at: randomNow(),\n      tags: [['p', recipientPublicKey]],\n    },\n    randomKey,\n  ) as NostrEvent\n}\n\nexport function wrapEvent(\n  event: Partial<UnsignedEvent>,\n  senderPrivateKey: Uint8Array,\n  recipientPublicKey: string,\n): NostrEvent {\n  const rumor = createRumor(event, senderPrivateKey)\n\n  const seal = createSeal(rumor, senderPrivateKey, recipientPublicKey)\n  return createWrap(seal, recipientPublicKey)\n}\n\nexport function wrapManyEvents(\n  event: Partial<UnsignedEvent>,\n  senderPrivateKey: Uint8Array,\n  recipientsPublicKeys: string[],\n): NostrEvent[] {\n  if (!recipientsPublicKeys || recipientsPublicKeys.length === 0) {\n    throw new Error('At least one recipient is required.')\n  }\n\n  const senderPublicKey = getPublicKey(senderPrivateKey)\n\n  const wrappeds = [wrapEvent(event, senderPrivateKey, senderPublicKey)]\n\n  recipientsPublicKeys.forEach(recipientPublicKey => {\n    wrappeds.push(wrapEvent(event, senderPrivateKey, recipientPublicKey))\n  })\n\n  return wrappeds\n}\n\nexport function unwrapEvent(wrap: NostrEvent, recipientPrivateKey: Uint8Array): Rumor {\n  const unwrappedSeal = nip44Decrypt(wrap, recipientPrivateKey)\n  return nip44Decrypt(unwrappedSeal, recipientPrivateKey)\n}\n\nexport function unwrapManyEvents(wrappedEvents: NostrEvent[], recipientPrivateKey: Uint8Array): Rumor[] {\n  let unwrappedEvents: Rumor[] = []\n\n  wrappedEvents.forEach(e => {\n    unwrappedEvents.push(unwrapEvent(e, recipientPrivateKey))\n  })\n\n  unwrappedEvents.sort((a, b) => a.created_at - b.created_at)\n\n  return unwrappedEvents\n}\n", "import { chacha20 } from '@noble/ciphers/chacha.js'\nimport { equalBytes } from '@noble/ciphers/utils.js'\nimport { secp256k1 } from '@noble/curves/secp256k1.js'\nimport { extract as hkdf_extract, expand as hkdf_expand } from '@noble/hashes/hkdf.js'\nimport { hmac } from '@noble/hashes/hmac.js'\nimport { sha256 } from '@noble/hashes/sha2.js'\nimport { concatBytes, hexToBytes, randomBytes } from '@noble/hashes/utils.js'\nimport { base64 } from '@scure/base'\n\nimport { utf8Decoder, utf8Encoder } from './utils.ts'\n\nconst minPlaintextSize = 0x0001 // 1b msg => padded to 32b\nconst maxPlaintextSize = 0xffff // 65535 (64kb-1) => padded to 64kb\n\nexport function getConversationKey(privkeyA: Uint8Array, pubkeyB: string): Uint8Array {\n  const sharedX = secp256k1.getSharedSecret(privkeyA, hexToBytes('02' + pubkeyB)).subarray(1, 33)\n  return hkdf_extract(sha256, sharedX, utf8Encoder.encode('nip44-v2'))\n}\n\nfunction getMessageKeys(\n  conversationKey: Uint8Array,\n  nonce: Uint8Array,\n): { chacha_key: Uint8Array; chacha_nonce: Uint8Array; hmac_key: Uint8Array } {\n  const keys = hkdf_expand(sha256, conversationKey, nonce, 76)\n  return {\n    chacha_key: keys.subarray(0, 32),\n    chacha_nonce: keys.subarray(32, 44),\n    hmac_key: keys.subarray(44, 76),\n  }\n}\n\nfunction calcPaddedLen(len: number): number {\n  if (!Number.isSafeInteger(len) || len < 1) throw new Error('expected positive integer')\n  if (len <= 32) return 32\n  const nextPower = 1 << (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 > maxPlaintextSize)\n    throw new Error('invalid plaintext size: must be between 1 and 65535 bytes')\n  const arr = new Uint8Array(2)\n  new DataView(arr.buffer).setUint16(0, num, false)\n  return arr\n}\n\nfunction pad(plaintext: string): Uint8Array {\n  const unpadded = utf8Encoder.encode(plaintext)\n  const unpaddedLen = unpadded.length\n  const prefix = 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 unpaddedLen = new DataView(padded.buffer).getUint16(0)\n  const unpadded = padded.subarray(2, 2 + unpaddedLen)\n  if (\n    unpaddedLen < minPlaintextSize ||\n    unpaddedLen > maxPlaintextSize ||\n    unpadded.length !== unpaddedLen ||\n    padded.length !== 2 + calcPaddedLen(unpaddedLen)\n  )\n    throw new Error('invalid padding')\n  return utf8Decoder.decode(unpadded)\n}\n\nfunction hmacAad(key: Uint8Array, message: Uint8Array, aad: Uint8Array): Uint8Array {\n  if (aad.length !== 32) throw new Error('AAD associated data must be 32 bytes')\n  const combined = concatBytes(aad, message)\n  return hmac(sha256, key, combined)\n}\n\n// metadata: always 65b (version: 1b, nonce: 32b, max: 32b)\n// plaintext: 1b to 0xffff\n// padded plaintext: 32b to 0xffff\n// ciphertext: 32b+2 to 0xffff+2\n// raw payload: 99 (65+32+2) to 65603 (65+0xffff+2)\n// compressed payload (base64): 132b to 87472b\nfunction decodePayload(payload: string): { nonce: Uint8Array; ciphertext: Uint8Array; mac: Uint8Array } {\n  if (typeof payload !== 'string') throw new Error('payload must be a valid string')\n  const plen = payload.length\n  if (plen < 132 || plen > 87472) throw new Error('invalid payload length: ' + plen)\n  if (payload[0] === '#') throw new Error('unknown encryption version')\n  let data: Uint8Array\n  try {\n    data = base64.decode(payload)\n  } catch (error) {\n    throw new Error('invalid base64: ' + (error as any).message)\n  }\n  const dlen = data.length\n  if (dlen < 99 || dlen > 65603) throw new Error('invalid data length: ' + dlen)\n  const vers = data[0]\n  if (vers !== 2) throw new Error('unknown encryption version ' + vers)\n  return {\n    nonce: data.subarray(1, 33),\n    ciphertext: data.subarray(33, -32),\n    mac: data.subarray(-32),\n  }\n}\n\nexport function encrypt(plaintext: string, conversationKey: Uint8Array, nonce: Uint8Array = randomBytes(32)): string {\n  const { chacha_key, chacha_nonce, hmac_key } = getMessageKeys(conversationKey, nonce)\n  const padded = pad(plaintext)\n  const ciphertext = chacha20(chacha_key, chacha_nonce, padded)\n  const mac = hmacAad(hmac_key, ciphertext, nonce)\n  return base64.encode(concatBytes(new Uint8Array([2]), nonce, ciphertext, mac))\n}\n\nexport function decrypt(payload: string, conversationKey: Uint8Array): string {\n  const { nonce, ciphertext, mac } = decodePayload(payload)\n  const { chacha_key, chacha_nonce, hmac_key } = getMessageKeys(conversationKey, nonce)\n  const calculatedMac = hmacAad(hmac_key, ciphertext, nonce)\n  if (!equalBytes(calculatedMac, mac)) throw new Error('invalid MAC')\n  const padded = chacha20(chacha_key, chacha_nonce, ciphertext)\n  return unpad(padded)\n}\n\nexport const v2 = {\n  utils: {\n    getConversationKey,\n    calcPaddedLen,\n  },\n  encrypt,\n  decrypt,\n}\n", "import type { Event } from './core.ts'\n\nexport const utf8Decoder: TextDecoder = new TextDecoder('utf-8')\nexport const utf8Encoder: TextEncoder = new TextEncoder()\n\nexport { bytesToHex, hexToBytes } from '@noble/hashes/utils.js'\n\nexport function normalizeURL(url: string): string {\n  try {\n    if (url.indexOf('://') === -1) url = 'wss://' + url\n    let p = new URL(url)\n    if (p.protocol === 'http:') p.protocol = 'ws:'\n    else if (p.protocol === 'https:') p.protocol = 'wss:'\n    p.pathname = p.pathname.replace(/\\/+/g, '/')\n    if (p.pathname.endsWith('/')) p.pathname = p.pathname.slice(0, -1)\n    if ((p.port === '80' && p.protocol === 'ws:') || (p.port === '443' && p.protocol === 'wss:')) p.port = ''\n    p.searchParams.sort()\n    p.hash = ''\n    return p.toString()\n  } catch (e) {\n    throw new Error(`Invalid URL: ${url}`)\n  }\n}\n\nexport function insertEventIntoDescendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return b.created_at - event.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function insertEventIntoAscendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return event.created_at - b.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function binarySearch<T>(arr: T[], compare: (b: T) => number): [number, boolean] {\n  let start = 0\n  let end = arr.length - 1\n\n  while (start <= end) {\n    const mid = Math.floor((start + end) / 2)\n    const cmp = compare(arr[mid])\n\n    if (cmp === 0) {\n      return [mid, true]\n    }\n\n    if (cmp < 0) {\n      end = mid - 1\n    } else {\n      start = mid + 1\n    }\n  }\n\n  return [start, false]\n}\n\nexport class QueueNode<V> {\n  public value: V\n  public next: QueueNode<V> | null = null\n  public prev: QueueNode<V> | null = null\n\n  constructor(message: V) {\n    this.value = message\n  }\n}\n\nexport class Queue<V> {\n  public first: QueueNode<V> | null\n  public last: QueueNode<V> | null\n\n  constructor() {\n    this.first = null\n    this.last = null\n  }\n\n  enqueue(value: V): boolean {\n    const newNode = new QueueNode(value)\n    if (!this.last) {\n      // list is empty\n      this.first = newNode\n      this.last = newNode\n    } else if (this.last === this.first) {\n      // list has a single element\n      this.last = newNode\n      this.last.prev = this.first\n      this.first.next = newNode\n    } else {\n      // list has elements, add as last\n      newNode.prev = this.last\n      this.last.next = newNode\n      this.last = newNode\n    }\n    return true\n  }\n\n  dequeue(): V | null {\n    if (!this.first) return null\n\n    if (this.first === this.last) {\n      const target = this.first\n      this.first = null\n      this.last = null\n      return target.value\n    }\n\n    const target = this.first\n    this.first = target.next\n    if (this.first) {\n      this.first.prev = null // fix: clean up prev pointer\n    }\n\n    return target.value\n  }\n}\n", "import { schnorr } from '@noble/curves/secp256k1.js'\nimport { bytesToHex, hexToBytes } from '@noble/hashes/utils.js'\nimport { Nostr, Event, EventTemplate, UnsignedEvent, VerifiedEvent, verifiedSymbol, validateEvent } from './core.ts'\nimport { sha256 } from '@noble/hashes/sha2.js'\n\nimport { utf8Encoder } from './utils.ts'\n\nclass JS implements Nostr {\n  generateSecretKey(): Uint8Array {\n    return schnorr.utils.randomSecretKey()\n  }\n  getPublicKey(secretKey: Uint8Array): string {\n    return bytesToHex(schnorr.getPublicKey(secretKey))\n  }\n  finalizeEvent(t: EventTemplate, secretKey: Uint8Array): VerifiedEvent {\n    const event = t as VerifiedEvent\n    event.pubkey = bytesToHex(schnorr.getPublicKey(secretKey))\n    event.id = getEventHash(event)\n    event.sig = bytesToHex(schnorr.sign(hexToBytes(getEventHash(event)), secretKey))\n    event[verifiedSymbol] = true\n    return event\n  }\n  verifyEvent(event: Event): event is VerifiedEvent {\n    if (typeof event[verifiedSymbol] === 'boolean') return event[verifiedSymbol]\n\n    const hash = getEventHash(event)\n    if (hash !== event.id) {\n      event[verifiedSymbol] = false\n      return false\n    }\n\n    try {\n      const valid = schnorr.verify(hexToBytes(event.sig), hexToBytes(hash), hexToBytes(event.pubkey))\n      event[verifiedSymbol] = valid\n      return valid\n    } catch (err) {\n      event[verifiedSymbol] = false\n      return false\n    }\n  }\n}\n\nexport function serializeEvent(evt: UnsignedEvent): string {\n  if (!validateEvent(evt)) throw new Error(\"can't serialize event with wrong or missing properties\")\n  return JSON.stringify([0, evt.pubkey, evt.created_at, evt.kind, evt.tags, evt.content])\n}\n\nexport function getEventHash(event: UnsignedEvent): string {\n  let eventHash = sha256(utf8Encoder.encode(serializeEvent(event)))\n  return bytesToHex(eventHash)\n}\n\nconst i: JS = new JS()\n\nexport const generateSecretKey = i.generateSecretKey\nexport const getPublicKey = i.getPublicKey\nexport const finalizeEvent = i.finalizeEvent\nexport const verifyEvent = i.verifyEvent\nexport * from './core.ts'\n", "export interface Nostr {\n  generateSecretKey(): Uint8Array\n  getPublicKey(secretKey: Uint8Array): string\n  finalizeEvent(event: EventTemplate, secretKey: Uint8Array): VerifiedEvent\n  verifyEvent(event: Event): event is VerifiedEvent\n}\n\n/** Designates a verified event signature. */\nexport const verifiedSymbol = Symbol('verified')\n\nexport type NostrEvent = {\n  kind: number\n  tags: string[][]\n  content: string\n  created_at: number\n  pubkey: string\n  id: string\n  sig: string\n  [verifiedSymbol]?: boolean\n}\n\nexport type Event = NostrEvent\nexport type EventTemplate = Pick<Event, 'kind' | 'tags' | 'content' | 'created_at'>\nexport type UnsignedEvent = Pick<Event, 'kind' | 'tags' | 'content' | 'created_at' | 'pubkey'>\n\n/** An event whose signature has been verified. */\nexport interface VerifiedEvent extends Event {\n  [verifiedSymbol]: true\n}\n\nconst isRecord = (obj: unknown): obj is Record<string, unknown> => obj instanceof Object\n\nexport function validateEvent<T>(event: T): event is T & UnsignedEvent {\n  if (!isRecord(event)) return false\n  if (typeof event.kind !== 'number') return false\n  if (typeof event.content !== 'string') return false\n  if (typeof event.created_at !== 'number') return false\n  if (typeof event.pubkey !== 'string') return false\n  if (!event.pubkey.match(/^[a-f0-9]{64}$/)) return false\n\n  if (!Array.isArray(event.tags)) return false\n  for (let i = 0; i < event.tags.length; i++) {\n    let tag = event.tags[i]\n    if (!Array.isArray(tag)) return false\n    for (let j = 0; j < tag.length; j++) {\n      if (typeof tag[j] !== 'string') return false\n    }\n  }\n\n  return true\n}\n\n/**\n * Sort events in reverse-chronological order by the `created_at` timestamp,\n * and then by the event `id` (lexicographically) in case of ties.\n * This mutates the array.\n */\nexport function sortEvents(events: Event[]): Event[] {\n  return events.sort((a: NostrEvent, b: NostrEvent): number => {\n    if (a.created_at !== b.created_at) {\n      return b.created_at - a.created_at\n    }\n    return a.id.localeCompare(b.id)\n  })\n}\n", "import { NostrEvent, validateEvent } from './pure.ts'\n\n/** Events are **regular**, which means they're all expected to be stored by relays. */\nexport function isRegularKind(kind: number): boolean {\n  return kind < 10000 && kind !== 0 && kind !== 3\n}\n\n/** Events are **replaceable**, which means that, for each combination of `pubkey` and `kind`, only the latest event is expected to (SHOULD) be stored by relays, older versions are expected to be discarded. */\nexport function isReplaceableKind(kind: number): boolean {\n  return kind === 0 || kind === 3 || (10000 <= kind && kind < 20000)\n}\n\n/** Events are **ephemeral**, which means they are not expected to be stored by relays. */\nexport function isEphemeralKind(kind: number): boolean {\n  return 20000 <= kind && kind < 30000\n}\n\n/** Events are **addressable**, which means that, for each combination of `pubkey`, `kind` and the `d` tag, only the latest event is expected to be stored by relays, older versions are expected to be discarded. */\nexport function isAddressableKind(kind: number): boolean {\n  return 30000 <= kind && kind < 40000\n}\n\n/** Classification of the event kind. */\nexport type KindClassification = 'regular' | 'replaceable' | 'ephemeral' | 'parameterized' | 'unknown'\n\n/** Determine the classification of this kind of event if known, or `unknown`. */\nexport function classifyKind(kind: number): KindClassification {\n  if (isRegularKind(kind)) return 'regular'\n  if (isReplaceableKind(kind)) return 'replaceable'\n  if (isEphemeralKind(kind)) return 'ephemeral'\n  if (isAddressableKind(kind)) return 'parameterized'\n  return 'unknown'\n}\n\nexport function isKind<T extends number>(event: unknown, kind: T | Array<T>): event is NostrEvent & { kind: T } {\n  const kindAsArray: number[] = kind instanceof Array ? kind : [kind]\n  return (validateEvent(event) && kindAsArray.includes(event.kind)) || false\n}\n\nexport const Metadata = 0\nexport type Metadata = typeof Metadata\nexport const ShortTextNote = 1\nexport type ShortTextNote = typeof ShortTextNote\nexport const RecommendRelay = 2\nexport type RecommendRelay = typeof RecommendRelay\nexport const Contacts = 3\nexport type Contacts = typeof Contacts\nexport const EncryptedDirectMessage = 4\nexport type EncryptedDirectMessage = typeof EncryptedDirectMessage\nexport const EventDeletion = 5\nexport type EventDeletion = typeof EventDeletion\nexport const Repost = 6\nexport type Repost = typeof Repost\nexport const Reaction = 7\nexport type Reaction = typeof Reaction\nexport const BadgeAward = 8\nexport type BadgeAward = typeof BadgeAward\nexport const ChatMessage = 9\nexport type ChatMessage = typeof ChatMessage\nexport const ForumThread = 11\nexport type ForumThread = typeof ForumThread\nexport const Seal = 13\nexport type Seal = typeof Seal\nexport const PrivateDirectMessage = 14\nexport type PrivateDirectMessage = typeof PrivateDirectMessage\nexport const FileMessage = 15\nexport type FileMessage = typeof FileMessage\nexport const GenericRepost = 16\nexport type GenericRepost = typeof GenericRepost\nexport const Photo = 20\nexport type Photo = typeof Photo\nexport const NormalVideo = 21\nexport type NormalVideo = typeof NormalVideo\nexport const ShortVideo = 22\nexport type ShortVideo = typeof ShortVideo\nexport const ChannelCreation = 40\nexport type ChannelCreation = typeof ChannelCreation\nexport const ChannelMetadata = 41\nexport type ChannelMetadata = typeof ChannelMetadata\nexport const ChannelMessage = 42\nexport type ChannelMessage = typeof ChannelMessage\nexport const ChannelHideMessage = 43\nexport type ChannelHideMessage = typeof ChannelHideMessage\nexport const ChannelMuteUser = 44\nexport type ChannelMuteUser = typeof ChannelMuteUser\nexport const OpenTimestamps = 1040\nexport type OpenTimestamps = typeof OpenTimestamps\nexport const GiftWrap = 1059\nexport type GiftWrap = typeof GiftWrap\nexport const Poll = 1068\nexport type Poll = typeof Poll\nexport const FileMetadata = 1063\nexport type FileMetadata = typeof FileMetadata\nexport const Comment = 1111\nexport type Comment = typeof Comment\nexport const LiveChatMessage = 1311\nexport type LiveChatMessage = typeof LiveChatMessage\nexport const Voice = 1222\nexport type Voice = typeof Voice\nexport const VoiceComment = 1244\nexport type VoiceComment = typeof VoiceComment\nexport const ProblemTracker = 1971\nexport type ProblemTracker = typeof ProblemTracker\nexport const Report = 1984\nexport type Report = typeof Report\nexport const Reporting = 1984\nexport type Reporting = typeof Reporting\nexport const Label = 1985\nexport type Label = typeof Label\nexport const CommunityPostApproval = 4550\nexport type CommunityPostApproval = typeof CommunityPostApproval\nexport const JobRequest = 5999\nexport type JobRequest = typeof JobRequest\nexport const JobResult = 6999\nexport type JobResult = typeof JobResult\nexport const JobFeedback = 7000\nexport type JobFeedback = typeof JobFeedback\nexport const ZapGoal = 9041\nexport type ZapGoal = typeof ZapGoal\nexport const ZapRequest = 9734\nexport type ZapRequest = typeof ZapRequest\nexport const Zap = 9735\nexport type Zap = typeof Zap\nexport const Highlights = 9802\nexport type Highlights = typeof Highlights\nexport const PollResponse = 1018\nexport type PollResponse = typeof PollResponse\nexport const Mutelist = 10000\nexport type Mutelist = typeof Mutelist\nexport const Pinlist = 10001\nexport type Pinlist = typeof Pinlist\nexport const RelayList = 10002\nexport type RelayList = typeof RelayList\nexport const BookmarkList = 10003\nexport type BookmarkList = typeof BookmarkList\nexport const CommunitiesList = 10004\nexport type CommunitiesList = typeof CommunitiesList\nexport const PublicChatsList = 10005\nexport type PublicChatsList = typeof PublicChatsList\nexport const BlockedRelaysList = 10006\nexport type BlockedRelaysList = typeof BlockedRelaysList\nexport const SearchRelaysList = 10007\nexport type SearchRelaysList = typeof SearchRelaysList\nexport const FavoriteRelays = 10012\nexport type FavoriteRelays = typeof FavoriteRelays\nexport const InterestsList = 10015\nexport type InterestsList = typeof InterestsList\nexport const UserEmojiList = 10030\nexport type UserEmojiList = typeof UserEmojiList\nexport const DirectMessageRelaysList = 10050\nexport type DirectMessageRelaysList = typeof DirectMessageRelaysList\nexport const FileServerPreference = 10096\nexport type FileServerPreference = typeof FileServerPreference\nexport const BlossomServerList = 10063\nexport type BlossomServerList = typeof BlossomServerList\nexport const NWCWalletInfo = 13194\nexport type NWCWalletInfo = typeof NWCWalletInfo\nexport const LightningPubRPC = 21000\nexport type LightningPubRPC = typeof LightningPubRPC\nexport const ClientAuth = 22242\nexport type ClientAuth = typeof ClientAuth\nexport const NWCWalletRequest = 23194\nexport type NWCWalletRequest = typeof NWCWalletRequest\nexport const NWCWalletResponse = 23195\nexport type NWCWalletResponse = typeof NWCWalletResponse\nexport const NostrConnect = 24133\nexport type NostrConnect = typeof NostrConnect\nexport const HTTPAuth = 27235\nexport type HTTPAuth = typeof HTTPAuth\nexport const Followsets = 30000\nexport type Followsets = typeof Followsets\nexport const Genericlists = 30001\nexport type Genericlists = typeof Genericlists\nexport const Relaysets = 30002\nexport type Relaysets = typeof Relaysets\nexport const Bookmarksets = 30003\nexport type Bookmarksets = typeof Bookmarksets\nexport const Curationsets = 30004\nexport type Curationsets = typeof Curationsets\nexport const ProfileBadges = 30008\nexport type ProfileBadges = typeof ProfileBadges\nexport const BadgeDefinition = 30009\nexport type BadgeDefinition = typeof BadgeDefinition\nexport const Interestsets = 30015\nexport type Interestsets = typeof Interestsets\nexport const CreateOrUpdateStall = 30017\nexport type CreateOrUpdateStall = typeof CreateOrUpdateStall\nexport const CreateOrUpdateProduct = 30018\nexport type CreateOrUpdateProduct = typeof CreateOrUpdateProduct\nexport const LongFormArticle = 30023\nexport type LongFormArticle = typeof LongFormArticle\nexport const DraftLong = 30024\nexport type DraftLong = typeof DraftLong\nexport const Emojisets = 30030\nexport type Emojisets = typeof Emojisets\nexport const Application = 30078\nexport type Application = typeof Application\nexport const LiveEvent = 30311\nexport type LiveEvent = typeof LiveEvent\nexport const UserStatuses = 30315\nexport type UserStatuses = typeof UserStatuses\nexport const ClassifiedListing = 30402\nexport type ClassifiedListing = typeof ClassifiedListing\nexport const DraftClassifiedListing = 30403\nexport type DraftClassifiedListing = typeof DraftClassifiedListing\nexport const Date = 31922\nexport type Date = typeof Date\nexport const Time = 31923\nexport type Time = typeof Time\nexport const Calendar = 31924\nexport type Calendar = typeof Calendar\nexport const CalendarEventRSVP = 31925\nexport type CalendarEventRSVP = typeof CalendarEventRSVP\nexport const RelayReview = 31987\nexport type RelayReview = typeof RelayReview\nexport const Handlerrecommendation = 31989\nexport type Handlerrecommendation = typeof Handlerrecommendation\nexport const Handlerinformation = 31990\nexport type Handlerinformation = typeof Handlerinformation\nexport const CommunityDefinition = 34550\nexport type CommunityDefinition = typeof CommunityDefinition\nexport const GroupMetadata = 39000\nexport type GroupMetadata = typeof GroupMetadata\n"],
+  "mappings": "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",
+  "names": ["import_utils", "hkdf_extract", "hkdf_expand", "import_secp256k1", "import_utils", "i", "import_sha2"]
 }
Index: package/lib/esm/nip59.js.map
===================================================================
--- package/lib/esm/nip59.js.map
+++ package/lib/esm/nip59.js.map
@@ -1,7 +1,7 @@
 {
   "version": 3,
   "sources": ["../../nip44.ts", "../../utils.ts", "../../pure.ts", "../../core.ts", "../../kinds.ts", "../../nip59.ts"],
-  "sourcesContent": ["import { chacha20 } from '@noble/ciphers/chacha'\nimport { equalBytes } from '@noble/ciphers/utils'\nimport { secp256k1 } from '@noble/curves/secp256k1'\nimport { extract as hkdf_extract, expand as hkdf_expand } from '@noble/hashes/hkdf'\nimport { hmac } from '@noble/hashes/hmac'\nimport { sha256 } from '@noble/hashes/sha256'\nimport { concatBytes, randomBytes } from '@noble/hashes/utils'\nimport { base64 } from '@scure/base'\n\nimport { utf8Decoder, utf8Encoder } from './utils.ts'\n\nconst minPlaintextSize = 0x0001 // 1b msg => padded to 32b\nconst maxPlaintextSize = 0xffff // 65535 (64kb-1) => padded to 64kb\n\nexport function getConversationKey(privkeyA: Uint8Array, pubkeyB: string): Uint8Array {\n  const sharedX = secp256k1.getSharedSecret(privkeyA, '02' + pubkeyB).subarray(1, 33)\n  return hkdf_extract(sha256, sharedX, 'nip44-v2')\n}\n\nfunction getMessageKeys(\n  conversationKey: Uint8Array,\n  nonce: Uint8Array,\n): { chacha_key: Uint8Array; chacha_nonce: Uint8Array; hmac_key: Uint8Array } {\n  const keys = hkdf_expand(sha256, conversationKey, nonce, 76)\n  return {\n    chacha_key: keys.subarray(0, 32),\n    chacha_nonce: keys.subarray(32, 44),\n    hmac_key: keys.subarray(44, 76),\n  }\n}\n\nfunction calcPaddedLen(len: number): number {\n  if (!Number.isSafeInteger(len) || len < 1) throw new Error('expected positive integer')\n  if (len <= 32) return 32\n  const nextPower = 1 << (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 > maxPlaintextSize)\n    throw new Error('invalid plaintext size: must be between 1 and 65535 bytes')\n  const arr = new Uint8Array(2)\n  new DataView(arr.buffer).setUint16(0, num, false)\n  return arr\n}\n\nfunction pad(plaintext: string): Uint8Array {\n  const unpadded = utf8Encoder.encode(plaintext)\n  const unpaddedLen = unpadded.length\n  const prefix = 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 unpaddedLen = new DataView(padded.buffer).getUint16(0)\n  const unpadded = padded.subarray(2, 2 + unpaddedLen)\n  if (\n    unpaddedLen < minPlaintextSize ||\n    unpaddedLen > maxPlaintextSize ||\n    unpadded.length !== unpaddedLen ||\n    padded.length !== 2 + calcPaddedLen(unpaddedLen)\n  )\n    throw new Error('invalid padding')\n  return utf8Decoder.decode(unpadded)\n}\n\nfunction hmacAad(key: Uint8Array, message: Uint8Array, aad: Uint8Array): Uint8Array {\n  if (aad.length !== 32) throw new Error('AAD associated data must be 32 bytes')\n  const combined = concatBytes(aad, message)\n  return hmac(sha256, key, combined)\n}\n\n// metadata: always 65b (version: 1b, nonce: 32b, max: 32b)\n// plaintext: 1b to 0xffff\n// padded plaintext: 32b to 0xffff\n// ciphertext: 32b+2 to 0xffff+2\n// raw payload: 99 (65+32+2) to 65603 (65+0xffff+2)\n// compressed payload (base64): 132b to 87472b\nfunction decodePayload(payload: string): { nonce: Uint8Array; ciphertext: Uint8Array; mac: Uint8Array } {\n  if (typeof payload !== 'string') throw new Error('payload must be a valid string')\n  const plen = payload.length\n  if (plen < 132 || plen > 87472) throw new Error('invalid payload length: ' + plen)\n  if (payload[0] === '#') throw new Error('unknown encryption version')\n  let data: Uint8Array\n  try {\n    data = base64.decode(payload)\n  } catch (error) {\n    throw new Error('invalid base64: ' + (error as any).message)\n  }\n  const dlen = data.length\n  if (dlen < 99 || dlen > 65603) throw new Error('invalid data length: ' + dlen)\n  const vers = data[0]\n  if (vers !== 2) throw new Error('unknown encryption version ' + vers)\n  return {\n    nonce: data.subarray(1, 33),\n    ciphertext: data.subarray(33, -32),\n    mac: data.subarray(-32),\n  }\n}\n\nexport function encrypt(plaintext: string, conversationKey: Uint8Array, nonce: Uint8Array = randomBytes(32)): string {\n  const { chacha_key, chacha_nonce, hmac_key } = getMessageKeys(conversationKey, nonce)\n  const padded = pad(plaintext)\n  const ciphertext = chacha20(chacha_key, chacha_nonce, padded)\n  const mac = hmacAad(hmac_key, ciphertext, nonce)\n  return base64.encode(concatBytes(new Uint8Array([2]), nonce, ciphertext, mac))\n}\n\nexport function decrypt(payload: string, conversationKey: Uint8Array): string {\n  const { nonce, ciphertext, mac } = decodePayload(payload)\n  const { chacha_key, chacha_nonce, hmac_key } = getMessageKeys(conversationKey, nonce)\n  const calculatedMac = hmacAad(hmac_key, ciphertext, nonce)\n  if (!equalBytes(calculatedMac, mac)) throw new Error('invalid MAC')\n  const padded = chacha20(chacha_key, chacha_nonce, ciphertext)\n  return unpad(padded)\n}\n\nexport const v2 = {\n  utils: {\n    getConversationKey,\n    calcPaddedLen,\n  },\n  encrypt,\n  decrypt,\n}\n", "import type { Event } from './core.ts'\n\nexport const utf8Decoder: TextDecoder = new TextDecoder('utf-8')\nexport const utf8Encoder: TextEncoder = new TextEncoder()\n\nexport { bytesToHex, hexToBytes } from '@noble/hashes/utils'\n\nexport function normalizeURL(url: string): string {\n  try {\n    if (url.indexOf('://') === -1) url = 'wss://' + url\n    let p = new URL(url)\n    if (p.protocol === 'http:') p.protocol = 'ws:'\n    else if (p.protocol === 'https:') p.protocol = 'wss:'\n    p.pathname = p.pathname.replace(/\\/+/g, '/')\n    if (p.pathname.endsWith('/')) p.pathname = p.pathname.slice(0, -1)\n    if ((p.port === '80' && p.protocol === 'ws:') || (p.port === '443' && p.protocol === 'wss:')) p.port = ''\n    p.searchParams.sort()\n    p.hash = ''\n    return p.toString()\n  } catch (e) {\n    throw new Error(`Invalid URL: ${url}`)\n  }\n}\n\nexport function insertEventIntoDescendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return b.created_at - event.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function insertEventIntoAscendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return event.created_at - b.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function binarySearch<T>(arr: T[], compare: (b: T) => number): [number, boolean] {\n  let start = 0\n  let end = arr.length - 1\n\n  while (start <= end) {\n    const mid = Math.floor((start + end) / 2)\n    const cmp = compare(arr[mid])\n\n    if (cmp === 0) {\n      return [mid, true]\n    }\n\n    if (cmp < 0) {\n      end = mid - 1\n    } else {\n      start = mid + 1\n    }\n  }\n\n  return [start, false]\n}\n\nexport class QueueNode<V> {\n  public value: V\n  public next: QueueNode<V> | null = null\n  public prev: QueueNode<V> | null = null\n\n  constructor(message: V) {\n    this.value = message\n  }\n}\n\nexport class Queue<V> {\n  public first: QueueNode<V> | null\n  public last: QueueNode<V> | null\n\n  constructor() {\n    this.first = null\n    this.last = null\n  }\n\n  enqueue(value: V): boolean {\n    const newNode = new QueueNode(value)\n    if (!this.last) {\n      // list is empty\n      this.first = newNode\n      this.last = newNode\n    } else if (this.last === this.first) {\n      // list has a single element\n      this.last = newNode\n      this.last.prev = this.first\n      this.first.next = newNode\n    } else {\n      // list has elements, add as last\n      newNode.prev = this.last\n      this.last.next = newNode\n      this.last = newNode\n    }\n    return true\n  }\n\n  dequeue(): V | null {\n    if (!this.first) return null\n\n    if (this.first === this.last) {\n      const target = this.first\n      this.first = null\n      this.last = null\n      return target.value\n    }\n\n    const target = this.first\n    this.first = target.next\n    if (this.first) {\n      this.first.prev = null // fix: clean up prev pointer\n    }\n\n    return target.value\n  }\n}\n", "import { schnorr } from '@noble/curves/secp256k1'\nimport { bytesToHex } from '@noble/hashes/utils'\nimport { Nostr, Event, EventTemplate, UnsignedEvent, VerifiedEvent, verifiedSymbol, validateEvent } from './core.ts'\nimport { sha256 } from '@noble/hashes/sha256'\n\nimport { utf8Encoder } from './utils.ts'\n\nclass JS implements Nostr {\n  generateSecretKey(): Uint8Array {\n    return schnorr.utils.randomPrivateKey()\n  }\n  getPublicKey(secretKey: Uint8Array): string {\n    return bytesToHex(schnorr.getPublicKey(secretKey))\n  }\n  finalizeEvent(t: EventTemplate, secretKey: Uint8Array): VerifiedEvent {\n    const event = t as VerifiedEvent\n    event.pubkey = bytesToHex(schnorr.getPublicKey(secretKey))\n    event.id = getEventHash(event)\n    event.sig = bytesToHex(schnorr.sign(getEventHash(event), secretKey))\n    event[verifiedSymbol] = true\n    return event\n  }\n  verifyEvent(event: Event): event is VerifiedEvent {\n    if (typeof event[verifiedSymbol] === 'boolean') return event[verifiedSymbol]\n\n    const hash = getEventHash(event)\n    if (hash !== event.id) {\n      event[verifiedSymbol] = false\n      return false\n    }\n\n    try {\n      const valid = schnorr.verify(event.sig, hash, event.pubkey)\n      event[verifiedSymbol] = valid\n      return valid\n    } catch (err) {\n      event[verifiedSymbol] = false\n      return false\n    }\n  }\n}\n\nexport function serializeEvent(evt: UnsignedEvent): string {\n  if (!validateEvent(evt)) throw new Error(\"can't serialize event with wrong or missing properties\")\n  return JSON.stringify([0, evt.pubkey, evt.created_at, evt.kind, evt.tags, evt.content])\n}\n\nexport function getEventHash(event: UnsignedEvent): string {\n  let eventHash = sha256(utf8Encoder.encode(serializeEvent(event)))\n  return bytesToHex(eventHash)\n}\n\nconst i: JS = new JS()\n\nexport const generateSecretKey = i.generateSecretKey\nexport const getPublicKey = i.getPublicKey\nexport const finalizeEvent = i.finalizeEvent\nexport const verifyEvent = i.verifyEvent\nexport * from './core.ts'\n", "export interface Nostr {\n  generateSecretKey(): Uint8Array\n  getPublicKey(secretKey: Uint8Array): string\n  finalizeEvent(event: EventTemplate, secretKey: Uint8Array): VerifiedEvent\n  verifyEvent(event: Event): event is VerifiedEvent\n}\n\n/** Designates a verified event signature. */\nexport const verifiedSymbol = Symbol('verified')\n\nexport type NostrEvent = {\n  kind: number\n  tags: string[][]\n  content: string\n  created_at: number\n  pubkey: string\n  id: string\n  sig: string\n  [verifiedSymbol]?: boolean\n}\n\nexport type Event = NostrEvent\nexport type EventTemplate = Pick<Event, 'kind' | 'tags' | 'content' | 'created_at'>\nexport type UnsignedEvent = Pick<Event, 'kind' | 'tags' | 'content' | 'created_at' | 'pubkey'>\n\n/** An event whose signature has been verified. */\nexport interface VerifiedEvent extends Event {\n  [verifiedSymbol]: true\n}\n\nconst isRecord = (obj: unknown): obj is Record<string, unknown> => obj instanceof Object\n\nexport function validateEvent<T>(event: T): event is T & UnsignedEvent {\n  if (!isRecord(event)) return false\n  if (typeof event.kind !== 'number') return false\n  if (typeof event.content !== 'string') return false\n  if (typeof event.created_at !== 'number') return false\n  if (typeof event.pubkey !== 'string') return false\n  if (!event.pubkey.match(/^[a-f0-9]{64}$/)) return false\n\n  if (!Array.isArray(event.tags)) return false\n  for (let i = 0; i < event.tags.length; i++) {\n    let tag = event.tags[i]\n    if (!Array.isArray(tag)) return false\n    for (let j = 0; j < tag.length; j++) {\n      if (typeof tag[j] !== 'string') return false\n    }\n  }\n\n  return true\n}\n\n/**\n * Sort events in reverse-chronological order by the `created_at` timestamp,\n * and then by the event `id` (lexicographically) in case of ties.\n * This mutates the array.\n */\nexport function sortEvents(events: Event[]): Event[] {\n  return events.sort((a: NostrEvent, b: NostrEvent): number => {\n    if (a.created_at !== b.created_at) {\n      return b.created_at - a.created_at\n    }\n    return a.id.localeCompare(b.id)\n  })\n}\n", "import { NostrEvent, validateEvent } from './pure.ts'\n\n/** Events are **regular**, which means they're all expected to be stored by relays. */\nexport function isRegularKind(kind: number): boolean {\n  return kind < 10000 && kind !== 0 && kind !== 3\n}\n\n/** Events are **replaceable**, which means that, for each combination of `pubkey` and `kind`, only the latest event is expected to (SHOULD) be stored by relays, older versions are expected to be discarded. */\nexport function isReplaceableKind(kind: number): boolean {\n  return kind === 0 || kind === 3 || (10000 <= kind && kind < 20000)\n}\n\n/** Events are **ephemeral**, which means they are not expected to be stored by relays. */\nexport function isEphemeralKind(kind: number): boolean {\n  return 20000 <= kind && kind < 30000\n}\n\n/** Events are **addressable**, which means that, for each combination of `pubkey`, `kind` and the `d` tag, only the latest event is expected to be stored by relays, older versions are expected to be discarded. */\nexport function isAddressableKind(kind: number): boolean {\n  return 30000 <= kind && kind < 40000\n}\n\n/** Classification of the event kind. */\nexport type KindClassification = 'regular' | 'replaceable' | 'ephemeral' | 'parameterized' | 'unknown'\n\n/** Determine the classification of this kind of event if known, or `unknown`. */\nexport function classifyKind(kind: number): KindClassification {\n  if (isRegularKind(kind)) return 'regular'\n  if (isReplaceableKind(kind)) return 'replaceable'\n  if (isEphemeralKind(kind)) return 'ephemeral'\n  if (isAddressableKind(kind)) return 'parameterized'\n  return 'unknown'\n}\n\nexport function isKind<T extends number>(event: unknown, kind: T | Array<T>): event is NostrEvent & { kind: T } {\n  const kindAsArray: number[] = kind instanceof Array ? kind : [kind]\n  return (validateEvent(event) && kindAsArray.includes(event.kind)) || false\n}\n\nexport const Metadata = 0\nexport type Metadata = typeof Metadata\nexport const ShortTextNote = 1\nexport type ShortTextNote = typeof ShortTextNote\nexport const RecommendRelay = 2\nexport type RecommendRelay = typeof RecommendRelay\nexport const Contacts = 3\nexport type Contacts = typeof Contacts\nexport const EncryptedDirectMessage = 4\nexport type EncryptedDirectMessage = typeof EncryptedDirectMessage\nexport const EventDeletion = 5\nexport type EventDeletion = typeof EventDeletion\nexport const Repost = 6\nexport type Repost = typeof Repost\nexport const Reaction = 7\nexport type Reaction = typeof Reaction\nexport const BadgeAward = 8\nexport type BadgeAward = typeof BadgeAward\nexport const ChatMessage = 9\nexport type ChatMessage = typeof ChatMessage\nexport const ForumThread = 11\nexport type ForumThread = typeof ForumThread\nexport const Seal = 13\nexport type Seal = typeof Seal\nexport const PrivateDirectMessage = 14\nexport type PrivateDirectMessage = typeof PrivateDirectMessage\nexport const FileMessage = 15\nexport type FileMessage = typeof FileMessage\nexport const GenericRepost = 16\nexport type GenericRepost = typeof GenericRepost\nexport const Photo = 20\nexport type Photo = typeof Photo\nexport const NormalVideo = 21\nexport type NormalVideo = typeof NormalVideo\nexport const ShortVideo = 22\nexport type ShortVideo = typeof ShortVideo\nexport const ChannelCreation = 40\nexport type ChannelCreation = typeof ChannelCreation\nexport const ChannelMetadata = 41\nexport type ChannelMetadata = typeof ChannelMetadata\nexport const ChannelMessage = 42\nexport type ChannelMessage = typeof ChannelMessage\nexport const ChannelHideMessage = 43\nexport type ChannelHideMessage = typeof ChannelHideMessage\nexport const ChannelMuteUser = 44\nexport type ChannelMuteUser = typeof ChannelMuteUser\nexport const OpenTimestamps = 1040\nexport type OpenTimestamps = typeof OpenTimestamps\nexport const GiftWrap = 1059\nexport type GiftWrap = typeof GiftWrap\nexport const Poll = 1068\nexport type Poll = typeof Poll\nexport const FileMetadata = 1063\nexport type FileMetadata = typeof FileMetadata\nexport const Comment = 1111\nexport type Comment = typeof Comment\nexport const LiveChatMessage = 1311\nexport type LiveChatMessage = typeof LiveChatMessage\nexport const Voice = 1222\nexport type Voice = typeof Voice\nexport const VoiceComment = 1244\nexport type VoiceComment = typeof VoiceComment\nexport const ProblemTracker = 1971\nexport type ProblemTracker = typeof ProblemTracker\nexport const Report = 1984\nexport type Report = typeof Report\nexport const Reporting = 1984\nexport type Reporting = typeof Reporting\nexport const Label = 1985\nexport type Label = typeof Label\nexport const CommunityPostApproval = 4550\nexport type CommunityPostApproval = typeof CommunityPostApproval\nexport const JobRequest = 5999\nexport type JobRequest = typeof JobRequest\nexport const JobResult = 6999\nexport type JobResult = typeof JobResult\nexport const JobFeedback = 7000\nexport type JobFeedback = typeof JobFeedback\nexport const ZapGoal = 9041\nexport type ZapGoal = typeof ZapGoal\nexport const ZapRequest = 9734\nexport type ZapRequest = typeof ZapRequest\nexport const Zap = 9735\nexport type Zap = typeof Zap\nexport const Highlights = 9802\nexport type Highlights = typeof Highlights\nexport const PollResponse = 1018\nexport type PollResponse = typeof PollResponse\nexport const Mutelist = 10000\nexport type Mutelist = typeof Mutelist\nexport const Pinlist = 10001\nexport type Pinlist = typeof Pinlist\nexport const RelayList = 10002\nexport type RelayList = typeof RelayList\nexport const BookmarkList = 10003\nexport type BookmarkList = typeof BookmarkList\nexport const CommunitiesList = 10004\nexport type CommunitiesList = typeof CommunitiesList\nexport const PublicChatsList = 10005\nexport type PublicChatsList = typeof PublicChatsList\nexport const BlockedRelaysList = 10006\nexport type BlockedRelaysList = typeof BlockedRelaysList\nexport const SearchRelaysList = 10007\nexport type SearchRelaysList = typeof SearchRelaysList\nexport const FavoriteRelays = 10012\nexport type FavoriteRelays = typeof FavoriteRelays\nexport const InterestsList = 10015\nexport type InterestsList = typeof InterestsList\nexport const UserEmojiList = 10030\nexport type UserEmojiList = typeof UserEmojiList\nexport const DirectMessageRelaysList = 10050\nexport type DirectMessageRelaysList = typeof DirectMessageRelaysList\nexport const FileServerPreference = 10096\nexport type FileServerPreference = typeof FileServerPreference\nexport const BlossomServerList = 10063\nexport type BlossomServerList = typeof BlossomServerList\nexport const NWCWalletInfo = 13194\nexport type NWCWalletInfo = typeof NWCWalletInfo\nexport const LightningPubRPC = 21000\nexport type LightningPubRPC = typeof LightningPubRPC\nexport const ClientAuth = 22242\nexport type ClientAuth = typeof ClientAuth\nexport const NWCWalletRequest = 23194\nexport type NWCWalletRequest = typeof NWCWalletRequest\nexport const NWCWalletResponse = 23195\nexport type NWCWalletResponse = typeof NWCWalletResponse\nexport const NostrConnect = 24133\nexport type NostrConnect = typeof NostrConnect\nexport const HTTPAuth = 27235\nexport type HTTPAuth = typeof HTTPAuth\nexport const Followsets = 30000\nexport type Followsets = typeof Followsets\nexport const Genericlists = 30001\nexport type Genericlists = typeof Genericlists\nexport const Relaysets = 30002\nexport type Relaysets = typeof Relaysets\nexport const Bookmarksets = 30003\nexport type Bookmarksets = typeof Bookmarksets\nexport const Curationsets = 30004\nexport type Curationsets = typeof Curationsets\nexport const ProfileBadges = 30008\nexport type ProfileBadges = typeof ProfileBadges\nexport const BadgeDefinition = 30009\nexport type BadgeDefinition = typeof BadgeDefinition\nexport const Interestsets = 30015\nexport type Interestsets = typeof Interestsets\nexport const CreateOrUpdateStall = 30017\nexport type CreateOrUpdateStall = typeof CreateOrUpdateStall\nexport const CreateOrUpdateProduct = 30018\nexport type CreateOrUpdateProduct = typeof CreateOrUpdateProduct\nexport const LongFormArticle = 30023\nexport type LongFormArticle = typeof LongFormArticle\nexport const DraftLong = 30024\nexport type DraftLong = typeof DraftLong\nexport const Emojisets = 30030\nexport type Emojisets = typeof Emojisets\nexport const Application = 30078\nexport type Application = typeof Application\nexport const LiveEvent = 30311\nexport type LiveEvent = typeof LiveEvent\nexport const UserStatuses = 30315\nexport type UserStatuses = typeof UserStatuses\nexport const ClassifiedListing = 30402\nexport type ClassifiedListing = typeof ClassifiedListing\nexport const DraftClassifiedListing = 30403\nexport type DraftClassifiedListing = typeof DraftClassifiedListing\nexport const Date = 31922\nexport type Date = typeof Date\nexport const Time = 31923\nexport type Time = typeof Time\nexport const Calendar = 31924\nexport type Calendar = typeof Calendar\nexport const CalendarEventRSVP = 31925\nexport type CalendarEventRSVP = typeof CalendarEventRSVP\nexport const RelayReview = 31987\nexport type RelayReview = typeof RelayReview\nexport const Handlerrecommendation = 31989\nexport type Handlerrecommendation = typeof Handlerrecommendation\nexport const Handlerinformation = 31990\nexport type Handlerinformation = typeof Handlerinformation\nexport const CommunityDefinition = 34550\nexport type CommunityDefinition = typeof CommunityDefinition\nexport const GroupMetadata = 39000\nexport type GroupMetadata = typeof GroupMetadata\n", "import { EventTemplate, UnsignedEvent, NostrEvent } from './core.ts'\nimport { getConversationKey, decrypt, encrypt } from './nip44.ts'\nimport { getEventHash, generateSecretKey, finalizeEvent, getPublicKey } from './pure.ts'\nimport { Seal, GiftWrap } from './kinds.ts'\n\ntype Rumor = UnsignedEvent & { id: string }\n\nconst TWO_DAYS = 2 * 24 * 60 * 60\n\nconst now = () => Math.round(Date.now() / 1000)\nconst randomNow = () => Math.round(now() - Math.random() * TWO_DAYS)\n\nconst nip44ConversationKey = (privateKey: Uint8Array, publicKey: string) => getConversationKey(privateKey, publicKey)\n\nconst nip44Encrypt = (data: EventTemplate, privateKey: Uint8Array, publicKey: string) =>\n  encrypt(JSON.stringify(data), nip44ConversationKey(privateKey, publicKey))\n\nconst nip44Decrypt = (data: NostrEvent, privateKey: Uint8Array) =>\n  JSON.parse(decrypt(data.content, nip44ConversationKey(privateKey, data.pubkey)))\n\nexport function createRumor(event: Partial<UnsignedEvent>, privateKey: Uint8Array): Rumor {\n  const rumor = {\n    created_at: now(),\n    content: '',\n    tags: [],\n    ...event,\n    pubkey: getPublicKey(privateKey),\n  } as any\n\n  rumor.id = getEventHash(rumor)\n\n  return rumor as Rumor\n}\n\nexport function createSeal(rumor: Rumor, privateKey: Uint8Array, recipientPublicKey: string): NostrEvent {\n  return finalizeEvent(\n    {\n      kind: Seal,\n      content: nip44Encrypt(rumor, privateKey, recipientPublicKey),\n      created_at: randomNow(),\n      tags: [],\n    },\n    privateKey,\n  )\n}\n\nexport function createWrap(seal: NostrEvent, recipientPublicKey: string): NostrEvent {\n  const randomKey = generateSecretKey()\n\n  return finalizeEvent(\n    {\n      kind: GiftWrap,\n      content: nip44Encrypt(seal, randomKey, recipientPublicKey),\n      created_at: randomNow(),\n      tags: [['p', recipientPublicKey]],\n    },\n    randomKey,\n  ) as NostrEvent\n}\n\nexport function wrapEvent(\n  event: Partial<UnsignedEvent>,\n  senderPrivateKey: Uint8Array,\n  recipientPublicKey: string,\n): NostrEvent {\n  const rumor = createRumor(event, senderPrivateKey)\n\n  const seal = createSeal(rumor, senderPrivateKey, recipientPublicKey)\n  return createWrap(seal, recipientPublicKey)\n}\n\nexport function wrapManyEvents(\n  event: Partial<UnsignedEvent>,\n  senderPrivateKey: Uint8Array,\n  recipientsPublicKeys: string[],\n): NostrEvent[] {\n  if (!recipientsPublicKeys || recipientsPublicKeys.length === 0) {\n    throw new Error('At least one recipient is required.')\n  }\n\n  const senderPublicKey = getPublicKey(senderPrivateKey)\n\n  const wrappeds = [wrapEvent(event, senderPrivateKey, senderPublicKey)]\n\n  recipientsPublicKeys.forEach(recipientPublicKey => {\n    wrappeds.push(wrapEvent(event, senderPrivateKey, recipientPublicKey))\n  })\n\n  return wrappeds\n}\n\nexport function unwrapEvent(wrap: NostrEvent, recipientPrivateKey: Uint8Array): Rumor {\n  const unwrappedSeal = nip44Decrypt(wrap, recipientPrivateKey)\n  return nip44Decrypt(unwrappedSeal, recipientPrivateKey)\n}\n\nexport function unwrapManyEvents(wrappedEvents: NostrEvent[], recipientPrivateKey: Uint8Array): Rumor[] {\n  let unwrappedEvents: Rumor[] = []\n\n  wrappedEvents.forEach(e => {\n    unwrappedEvents.push(unwrapEvent(e, recipientPrivateKey))\n  })\n\n  unwrappedEvents.sort((a, b) => a.created_at - b.created_at)\n\n  return unwrappedEvents\n}\n"],
-  "mappings": "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",
-  "names": ["bytesToHex", "i", "sha256", "bytesToHex", "sha256"]
+  "sourcesContent": ["import { chacha20 } from '@noble/ciphers/chacha.js'\nimport { equalBytes } from '@noble/ciphers/utils.js'\nimport { secp256k1 } from '@noble/curves/secp256k1.js'\nimport { extract as hkdf_extract, expand as hkdf_expand } from '@noble/hashes/hkdf.js'\nimport { hmac } from '@noble/hashes/hmac.js'\nimport { sha256 } from '@noble/hashes/sha2.js'\nimport { concatBytes, hexToBytes, randomBytes } from '@noble/hashes/utils.js'\nimport { base64 } from '@scure/base'\n\nimport { utf8Decoder, utf8Encoder } from './utils.ts'\n\nconst minPlaintextSize = 0x0001 // 1b msg => padded to 32b\nconst maxPlaintextSize = 0xffff // 65535 (64kb-1) => padded to 64kb\n\nexport function getConversationKey(privkeyA: Uint8Array, pubkeyB: string): Uint8Array {\n  const sharedX = secp256k1.getSharedSecret(privkeyA, hexToBytes('02' + pubkeyB)).subarray(1, 33)\n  return hkdf_extract(sha256, sharedX, utf8Encoder.encode('nip44-v2'))\n}\n\nfunction getMessageKeys(\n  conversationKey: Uint8Array,\n  nonce: Uint8Array,\n): { chacha_key: Uint8Array; chacha_nonce: Uint8Array; hmac_key: Uint8Array } {\n  const keys = hkdf_expand(sha256, conversationKey, nonce, 76)\n  return {\n    chacha_key: keys.subarray(0, 32),\n    chacha_nonce: keys.subarray(32, 44),\n    hmac_key: keys.subarray(44, 76),\n  }\n}\n\nfunction calcPaddedLen(len: number): number {\n  if (!Number.isSafeInteger(len) || len < 1) throw new Error('expected positive integer')\n  if (len <= 32) return 32\n  const nextPower = 1 << (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 > maxPlaintextSize)\n    throw new Error('invalid plaintext size: must be between 1 and 65535 bytes')\n  const arr = new Uint8Array(2)\n  new DataView(arr.buffer).setUint16(0, num, false)\n  return arr\n}\n\nfunction pad(plaintext: string): Uint8Array {\n  const unpadded = utf8Encoder.encode(plaintext)\n  const unpaddedLen = unpadded.length\n  const prefix = 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 unpaddedLen = new DataView(padded.buffer).getUint16(0)\n  const unpadded = padded.subarray(2, 2 + unpaddedLen)\n  if (\n    unpaddedLen < minPlaintextSize ||\n    unpaddedLen > maxPlaintextSize ||\n    unpadded.length !== unpaddedLen ||\n    padded.length !== 2 + calcPaddedLen(unpaddedLen)\n  )\n    throw new Error('invalid padding')\n  return utf8Decoder.decode(unpadded)\n}\n\nfunction hmacAad(key: Uint8Array, message: Uint8Array, aad: Uint8Array): Uint8Array {\n  if (aad.length !== 32) throw new Error('AAD associated data must be 32 bytes')\n  const combined = concatBytes(aad, message)\n  return hmac(sha256, key, combined)\n}\n\n// metadata: always 65b (version: 1b, nonce: 32b, max: 32b)\n// plaintext: 1b to 0xffff\n// padded plaintext: 32b to 0xffff\n// ciphertext: 32b+2 to 0xffff+2\n// raw payload: 99 (65+32+2) to 65603 (65+0xffff+2)\n// compressed payload (base64): 132b to 87472b\nfunction decodePayload(payload: string): { nonce: Uint8Array; ciphertext: Uint8Array; mac: Uint8Array } {\n  if (typeof payload !== 'string') throw new Error('payload must be a valid string')\n  const plen = payload.length\n  if (plen < 132 || plen > 87472) throw new Error('invalid payload length: ' + plen)\n  if (payload[0] === '#') throw new Error('unknown encryption version')\n  let data: Uint8Array\n  try {\n    data = base64.decode(payload)\n  } catch (error) {\n    throw new Error('invalid base64: ' + (error as any).message)\n  }\n  const dlen = data.length\n  if (dlen < 99 || dlen > 65603) throw new Error('invalid data length: ' + dlen)\n  const vers = data[0]\n  if (vers !== 2) throw new Error('unknown encryption version ' + vers)\n  return {\n    nonce: data.subarray(1, 33),\n    ciphertext: data.subarray(33, -32),\n    mac: data.subarray(-32),\n  }\n}\n\nexport function encrypt(plaintext: string, conversationKey: Uint8Array, nonce: Uint8Array = randomBytes(32)): string {\n  const { chacha_key, chacha_nonce, hmac_key } = getMessageKeys(conversationKey, nonce)\n  const padded = pad(plaintext)\n  const ciphertext = chacha20(chacha_key, chacha_nonce, padded)\n  const mac = hmacAad(hmac_key, ciphertext, nonce)\n  return base64.encode(concatBytes(new Uint8Array([2]), nonce, ciphertext, mac))\n}\n\nexport function decrypt(payload: string, conversationKey: Uint8Array): string {\n  const { nonce, ciphertext, mac } = decodePayload(payload)\n  const { chacha_key, chacha_nonce, hmac_key } = getMessageKeys(conversationKey, nonce)\n  const calculatedMac = hmacAad(hmac_key, ciphertext, nonce)\n  if (!equalBytes(calculatedMac, mac)) throw new Error('invalid MAC')\n  const padded = chacha20(chacha_key, chacha_nonce, ciphertext)\n  return unpad(padded)\n}\n\nexport const v2 = {\n  utils: {\n    getConversationKey,\n    calcPaddedLen,\n  },\n  encrypt,\n  decrypt,\n}\n", "import type { Event } from './core.ts'\n\nexport const utf8Decoder: TextDecoder = new TextDecoder('utf-8')\nexport const utf8Encoder: TextEncoder = new TextEncoder()\n\nexport { bytesToHex, hexToBytes } from '@noble/hashes/utils.js'\n\nexport function normalizeURL(url: string): string {\n  try {\n    if (url.indexOf('://') === -1) url = 'wss://' + url\n    let p = new URL(url)\n    if (p.protocol === 'http:') p.protocol = 'ws:'\n    else if (p.protocol === 'https:') p.protocol = 'wss:'\n    p.pathname = p.pathname.replace(/\\/+/g, '/')\n    if (p.pathname.endsWith('/')) p.pathname = p.pathname.slice(0, -1)\n    if ((p.port === '80' && p.protocol === 'ws:') || (p.port === '443' && p.protocol === 'wss:')) p.port = ''\n    p.searchParams.sort()\n    p.hash = ''\n    return p.toString()\n  } catch (e) {\n    throw new Error(`Invalid URL: ${url}`)\n  }\n}\n\nexport function insertEventIntoDescendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return b.created_at - event.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function insertEventIntoAscendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return event.created_at - b.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function binarySearch<T>(arr: T[], compare: (b: T) => number): [number, boolean] {\n  let start = 0\n  let end = arr.length - 1\n\n  while (start <= end) {\n    const mid = Math.floor((start + end) / 2)\n    const cmp = compare(arr[mid])\n\n    if (cmp === 0) {\n      return [mid, true]\n    }\n\n    if (cmp < 0) {\n      end = mid - 1\n    } else {\n      start = mid + 1\n    }\n  }\n\n  return [start, false]\n}\n\nexport class QueueNode<V> {\n  public value: V\n  public next: QueueNode<V> | null = null\n  public prev: QueueNode<V> | null = null\n\n  constructor(message: V) {\n    this.value = message\n  }\n}\n\nexport class Queue<V> {\n  public first: QueueNode<V> | null\n  public last: QueueNode<V> | null\n\n  constructor() {\n    this.first = null\n    this.last = null\n  }\n\n  enqueue(value: V): boolean {\n    const newNode = new QueueNode(value)\n    if (!this.last) {\n      // list is empty\n      this.first = newNode\n      this.last = newNode\n    } else if (this.last === this.first) {\n      // list has a single element\n      this.last = newNode\n      this.last.prev = this.first\n      this.first.next = newNode\n    } else {\n      // list has elements, add as last\n      newNode.prev = this.last\n      this.last.next = newNode\n      this.last = newNode\n    }\n    return true\n  }\n\n  dequeue(): V | null {\n    if (!this.first) return null\n\n    if (this.first === this.last) {\n      const target = this.first\n      this.first = null\n      this.last = null\n      return target.value\n    }\n\n    const target = this.first\n    this.first = target.next\n    if (this.first) {\n      this.first.prev = null // fix: clean up prev pointer\n    }\n\n    return target.value\n  }\n}\n", "import { schnorr } from '@noble/curves/secp256k1.js'\nimport { bytesToHex, hexToBytes } from '@noble/hashes/utils.js'\nimport { Nostr, Event, EventTemplate, UnsignedEvent, VerifiedEvent, verifiedSymbol, validateEvent } from './core.ts'\nimport { sha256 } from '@noble/hashes/sha2.js'\n\nimport { utf8Encoder } from './utils.ts'\n\nclass JS implements Nostr {\n  generateSecretKey(): Uint8Array {\n    return schnorr.utils.randomSecretKey()\n  }\n  getPublicKey(secretKey: Uint8Array): string {\n    return bytesToHex(schnorr.getPublicKey(secretKey))\n  }\n  finalizeEvent(t: EventTemplate, secretKey: Uint8Array): VerifiedEvent {\n    const event = t as VerifiedEvent\n    event.pubkey = bytesToHex(schnorr.getPublicKey(secretKey))\n    event.id = getEventHash(event)\n    event.sig = bytesToHex(schnorr.sign(hexToBytes(getEventHash(event)), secretKey))\n    event[verifiedSymbol] = true\n    return event\n  }\n  verifyEvent(event: Event): event is VerifiedEvent {\n    if (typeof event[verifiedSymbol] === 'boolean') return event[verifiedSymbol]\n\n    const hash = getEventHash(event)\n    if (hash !== event.id) {\n      event[verifiedSymbol] = false\n      return false\n    }\n\n    try {\n      const valid = schnorr.verify(hexToBytes(event.sig), hexToBytes(hash), hexToBytes(event.pubkey))\n      event[verifiedSymbol] = valid\n      return valid\n    } catch (err) {\n      event[verifiedSymbol] = false\n      return false\n    }\n  }\n}\n\nexport function serializeEvent(evt: UnsignedEvent): string {\n  if (!validateEvent(evt)) throw new Error(\"can't serialize event with wrong or missing properties\")\n  return JSON.stringify([0, evt.pubkey, evt.created_at, evt.kind, evt.tags, evt.content])\n}\n\nexport function getEventHash(event: UnsignedEvent): string {\n  let eventHash = sha256(utf8Encoder.encode(serializeEvent(event)))\n  return bytesToHex(eventHash)\n}\n\nconst i: JS = new JS()\n\nexport const generateSecretKey = i.generateSecretKey\nexport const getPublicKey = i.getPublicKey\nexport const finalizeEvent = i.finalizeEvent\nexport const verifyEvent = i.verifyEvent\nexport * from './core.ts'\n", "export interface Nostr {\n  generateSecretKey(): Uint8Array\n  getPublicKey(secretKey: Uint8Array): string\n  finalizeEvent(event: EventTemplate, secretKey: Uint8Array): VerifiedEvent\n  verifyEvent(event: Event): event is VerifiedEvent\n}\n\n/** Designates a verified event signature. */\nexport const verifiedSymbol = Symbol('verified')\n\nexport type NostrEvent = {\n  kind: number\n  tags: string[][]\n  content: string\n  created_at: number\n  pubkey: string\n  id: string\n  sig: string\n  [verifiedSymbol]?: boolean\n}\n\nexport type Event = NostrEvent\nexport type EventTemplate = Pick<Event, 'kind' | 'tags' | 'content' | 'created_at'>\nexport type UnsignedEvent = Pick<Event, 'kind' | 'tags' | 'content' | 'created_at' | 'pubkey'>\n\n/** An event whose signature has been verified. */\nexport interface VerifiedEvent extends Event {\n  [verifiedSymbol]: true\n}\n\nconst isRecord = (obj: unknown): obj is Record<string, unknown> => obj instanceof Object\n\nexport function validateEvent<T>(event: T): event is T & UnsignedEvent {\n  if (!isRecord(event)) return false\n  if (typeof event.kind !== 'number') return false\n  if (typeof event.content !== 'string') return false\n  if (typeof event.created_at !== 'number') return false\n  if (typeof event.pubkey !== 'string') return false\n  if (!event.pubkey.match(/^[a-f0-9]{64}$/)) return false\n\n  if (!Array.isArray(event.tags)) return false\n  for (let i = 0; i < event.tags.length; i++) {\n    let tag = event.tags[i]\n    if (!Array.isArray(tag)) return false\n    for (let j = 0; j < tag.length; j++) {\n      if (typeof tag[j] !== 'string') return false\n    }\n  }\n\n  return true\n}\n\n/**\n * Sort events in reverse-chronological order by the `created_at` timestamp,\n * and then by the event `id` (lexicographically) in case of ties.\n * This mutates the array.\n */\nexport function sortEvents(events: Event[]): Event[] {\n  return events.sort((a: NostrEvent, b: NostrEvent): number => {\n    if (a.created_at !== b.created_at) {\n      return b.created_at - a.created_at\n    }\n    return a.id.localeCompare(b.id)\n  })\n}\n", "import { NostrEvent, validateEvent } from './pure.ts'\n\n/** Events are **regular**, which means they're all expected to be stored by relays. */\nexport function isRegularKind(kind: number): boolean {\n  return kind < 10000 && kind !== 0 && kind !== 3\n}\n\n/** Events are **replaceable**, which means that, for each combination of `pubkey` and `kind`, only the latest event is expected to (SHOULD) be stored by relays, older versions are expected to be discarded. */\nexport function isReplaceableKind(kind: number): boolean {\n  return kind === 0 || kind === 3 || (10000 <= kind && kind < 20000)\n}\n\n/** Events are **ephemeral**, which means they are not expected to be stored by relays. */\nexport function isEphemeralKind(kind: number): boolean {\n  return 20000 <= kind && kind < 30000\n}\n\n/** Events are **addressable**, which means that, for each combination of `pubkey`, `kind` and the `d` tag, only the latest event is expected to be stored by relays, older versions are expected to be discarded. */\nexport function isAddressableKind(kind: number): boolean {\n  return 30000 <= kind && kind < 40000\n}\n\n/** Classification of the event kind. */\nexport type KindClassification = 'regular' | 'replaceable' | 'ephemeral' | 'parameterized' | 'unknown'\n\n/** Determine the classification of this kind of event if known, or `unknown`. */\nexport function classifyKind(kind: number): KindClassification {\n  if (isRegularKind(kind)) return 'regular'\n  if (isReplaceableKind(kind)) return 'replaceable'\n  if (isEphemeralKind(kind)) return 'ephemeral'\n  if (isAddressableKind(kind)) return 'parameterized'\n  return 'unknown'\n}\n\nexport function isKind<T extends number>(event: unknown, kind: T | Array<T>): event is NostrEvent & { kind: T } {\n  const kindAsArray: number[] = kind instanceof Array ? kind : [kind]\n  return (validateEvent(event) && kindAsArray.includes(event.kind)) || false\n}\n\nexport const Metadata = 0\nexport type Metadata = typeof Metadata\nexport const ShortTextNote = 1\nexport type ShortTextNote = typeof ShortTextNote\nexport const RecommendRelay = 2\nexport type RecommendRelay = typeof RecommendRelay\nexport const Contacts = 3\nexport type Contacts = typeof Contacts\nexport const EncryptedDirectMessage = 4\nexport type EncryptedDirectMessage = typeof EncryptedDirectMessage\nexport const EventDeletion = 5\nexport type EventDeletion = typeof EventDeletion\nexport const Repost = 6\nexport type Repost = typeof Repost\nexport const Reaction = 7\nexport type Reaction = typeof Reaction\nexport const BadgeAward = 8\nexport type BadgeAward = typeof BadgeAward\nexport const ChatMessage = 9\nexport type ChatMessage = typeof ChatMessage\nexport const ForumThread = 11\nexport type ForumThread = typeof ForumThread\nexport const Seal = 13\nexport type Seal = typeof Seal\nexport const PrivateDirectMessage = 14\nexport type PrivateDirectMessage = typeof PrivateDirectMessage\nexport const FileMessage = 15\nexport type FileMessage = typeof FileMessage\nexport const GenericRepost = 16\nexport type GenericRepost = typeof GenericRepost\nexport const Photo = 20\nexport type Photo = typeof Photo\nexport const NormalVideo = 21\nexport type NormalVideo = typeof NormalVideo\nexport const ShortVideo = 22\nexport type ShortVideo = typeof ShortVideo\nexport const ChannelCreation = 40\nexport type ChannelCreation = typeof ChannelCreation\nexport const ChannelMetadata = 41\nexport type ChannelMetadata = typeof ChannelMetadata\nexport const ChannelMessage = 42\nexport type ChannelMessage = typeof ChannelMessage\nexport const ChannelHideMessage = 43\nexport type ChannelHideMessage = typeof ChannelHideMessage\nexport const ChannelMuteUser = 44\nexport type ChannelMuteUser = typeof ChannelMuteUser\nexport const OpenTimestamps = 1040\nexport type OpenTimestamps = typeof OpenTimestamps\nexport const GiftWrap = 1059\nexport type GiftWrap = typeof GiftWrap\nexport const Poll = 1068\nexport type Poll = typeof Poll\nexport const FileMetadata = 1063\nexport type FileMetadata = typeof FileMetadata\nexport const Comment = 1111\nexport type Comment = typeof Comment\nexport const LiveChatMessage = 1311\nexport type LiveChatMessage = typeof LiveChatMessage\nexport const Voice = 1222\nexport type Voice = typeof Voice\nexport const VoiceComment = 1244\nexport type VoiceComment = typeof VoiceComment\nexport const ProblemTracker = 1971\nexport type ProblemTracker = typeof ProblemTracker\nexport const Report = 1984\nexport type Report = typeof Report\nexport const Reporting = 1984\nexport type Reporting = typeof Reporting\nexport const Label = 1985\nexport type Label = typeof Label\nexport const CommunityPostApproval = 4550\nexport type CommunityPostApproval = typeof CommunityPostApproval\nexport const JobRequest = 5999\nexport type JobRequest = typeof JobRequest\nexport const JobResult = 6999\nexport type JobResult = typeof JobResult\nexport const JobFeedback = 7000\nexport type JobFeedback = typeof JobFeedback\nexport const ZapGoal = 9041\nexport type ZapGoal = typeof ZapGoal\nexport const ZapRequest = 9734\nexport type ZapRequest = typeof ZapRequest\nexport const Zap = 9735\nexport type Zap = typeof Zap\nexport const Highlights = 9802\nexport type Highlights = typeof Highlights\nexport const PollResponse = 1018\nexport type PollResponse = typeof PollResponse\nexport const Mutelist = 10000\nexport type Mutelist = typeof Mutelist\nexport const Pinlist = 10001\nexport type Pinlist = typeof Pinlist\nexport const RelayList = 10002\nexport type RelayList = typeof RelayList\nexport const BookmarkList = 10003\nexport type BookmarkList = typeof BookmarkList\nexport const CommunitiesList = 10004\nexport type CommunitiesList = typeof CommunitiesList\nexport const PublicChatsList = 10005\nexport type PublicChatsList = typeof PublicChatsList\nexport const BlockedRelaysList = 10006\nexport type BlockedRelaysList = typeof BlockedRelaysList\nexport const SearchRelaysList = 10007\nexport type SearchRelaysList = typeof SearchRelaysList\nexport const FavoriteRelays = 10012\nexport type FavoriteRelays = typeof FavoriteRelays\nexport const InterestsList = 10015\nexport type InterestsList = typeof InterestsList\nexport const UserEmojiList = 10030\nexport type UserEmojiList = typeof UserEmojiList\nexport const DirectMessageRelaysList = 10050\nexport type DirectMessageRelaysList = typeof DirectMessageRelaysList\nexport const FileServerPreference = 10096\nexport type FileServerPreference = typeof FileServerPreference\nexport const BlossomServerList = 10063\nexport type BlossomServerList = typeof BlossomServerList\nexport const NWCWalletInfo = 13194\nexport type NWCWalletInfo = typeof NWCWalletInfo\nexport const LightningPubRPC = 21000\nexport type LightningPubRPC = typeof LightningPubRPC\nexport const ClientAuth = 22242\nexport type ClientAuth = typeof ClientAuth\nexport const NWCWalletRequest = 23194\nexport type NWCWalletRequest = typeof NWCWalletRequest\nexport const NWCWalletResponse = 23195\nexport type NWCWalletResponse = typeof NWCWalletResponse\nexport const NostrConnect = 24133\nexport type NostrConnect = typeof NostrConnect\nexport const HTTPAuth = 27235\nexport type HTTPAuth = typeof HTTPAuth\nexport const Followsets = 30000\nexport type Followsets = typeof Followsets\nexport const Genericlists = 30001\nexport type Genericlists = typeof Genericlists\nexport const Relaysets = 30002\nexport type Relaysets = typeof Relaysets\nexport const Bookmarksets = 30003\nexport type Bookmarksets = typeof Bookmarksets\nexport const Curationsets = 30004\nexport type Curationsets = typeof Curationsets\nexport const ProfileBadges = 30008\nexport type ProfileBadges = typeof ProfileBadges\nexport const BadgeDefinition = 30009\nexport type BadgeDefinition = typeof BadgeDefinition\nexport const Interestsets = 30015\nexport type Interestsets = typeof Interestsets\nexport const CreateOrUpdateStall = 30017\nexport type CreateOrUpdateStall = typeof CreateOrUpdateStall\nexport const CreateOrUpdateProduct = 30018\nexport type CreateOrUpdateProduct = typeof CreateOrUpdateProduct\nexport const LongFormArticle = 30023\nexport type LongFormArticle = typeof LongFormArticle\nexport const DraftLong = 30024\nexport type DraftLong = typeof DraftLong\nexport const Emojisets = 30030\nexport type Emojisets = typeof Emojisets\nexport const Application = 30078\nexport type Application = typeof Application\nexport const LiveEvent = 30311\nexport type LiveEvent = typeof LiveEvent\nexport const UserStatuses = 30315\nexport type UserStatuses = typeof UserStatuses\nexport const ClassifiedListing = 30402\nexport type ClassifiedListing = typeof ClassifiedListing\nexport const DraftClassifiedListing = 30403\nexport type DraftClassifiedListing = typeof DraftClassifiedListing\nexport const Date = 31922\nexport type Date = typeof Date\nexport const Time = 31923\nexport type Time = typeof Time\nexport const Calendar = 31924\nexport type Calendar = typeof Calendar\nexport const CalendarEventRSVP = 31925\nexport type CalendarEventRSVP = typeof CalendarEventRSVP\nexport const RelayReview = 31987\nexport type RelayReview = typeof RelayReview\nexport const Handlerrecommendation = 31989\nexport type Handlerrecommendation = typeof Handlerrecommendation\nexport const Handlerinformation = 31990\nexport type Handlerinformation = typeof Handlerinformation\nexport const CommunityDefinition = 34550\nexport type CommunityDefinition = typeof CommunityDefinition\nexport const GroupMetadata = 39000\nexport type GroupMetadata = typeof GroupMetadata\n", "import { EventTemplate, UnsignedEvent, NostrEvent } from './core.ts'\nimport { getConversationKey, decrypt, encrypt } from './nip44.ts'\nimport { getEventHash, generateSecretKey, finalizeEvent, getPublicKey } from './pure.ts'\nimport { Seal, GiftWrap } from './kinds.ts'\n\ntype Rumor = UnsignedEvent & { id: string }\n\nconst TWO_DAYS = 2 * 24 * 60 * 60\n\nconst now = () => Math.round(Date.now() / 1000)\nconst randomNow = () => Math.round(now() - Math.random() * TWO_DAYS)\n\nconst nip44ConversationKey = (privateKey: Uint8Array, publicKey: string) => getConversationKey(privateKey, publicKey)\n\nconst nip44Encrypt = (data: EventTemplate, privateKey: Uint8Array, publicKey: string) =>\n  encrypt(JSON.stringify(data), nip44ConversationKey(privateKey, publicKey))\n\nconst nip44Decrypt = (data: NostrEvent, privateKey: Uint8Array) =>\n  JSON.parse(decrypt(data.content, nip44ConversationKey(privateKey, data.pubkey)))\n\nexport function createRumor(event: Partial<UnsignedEvent>, privateKey: Uint8Array): Rumor {\n  const rumor = {\n    created_at: now(),\n    content: '',\n    tags: [],\n    ...event,\n    pubkey: getPublicKey(privateKey),\n  } as any\n\n  rumor.id = getEventHash(rumor)\n\n  return rumor as Rumor\n}\n\nexport function createSeal(rumor: Rumor, privateKey: Uint8Array, recipientPublicKey: string): NostrEvent {\n  return finalizeEvent(\n    {\n      kind: Seal,\n      content: nip44Encrypt(rumor, privateKey, recipientPublicKey),\n      created_at: randomNow(),\n      tags: [],\n    },\n    privateKey,\n  )\n}\n\nexport function createWrap(seal: NostrEvent, recipientPublicKey: string): NostrEvent {\n  const randomKey = generateSecretKey()\n\n  return finalizeEvent(\n    {\n      kind: GiftWrap,\n      content: nip44Encrypt(seal, randomKey, recipientPublicKey),\n      created_at: randomNow(),\n      tags: [['p', recipientPublicKey]],\n    },\n    randomKey,\n  ) as NostrEvent\n}\n\nexport function wrapEvent(\n  event: Partial<UnsignedEvent>,\n  senderPrivateKey: Uint8Array,\n  recipientPublicKey: string,\n): NostrEvent {\n  const rumor = createRumor(event, senderPrivateKey)\n\n  const seal = createSeal(rumor, senderPrivateKey, recipientPublicKey)\n  return createWrap(seal, recipientPublicKey)\n}\n\nexport function wrapManyEvents(\n  event: Partial<UnsignedEvent>,\n  senderPrivateKey: Uint8Array,\n  recipientsPublicKeys: string[],\n): NostrEvent[] {\n  if (!recipientsPublicKeys || recipientsPublicKeys.length === 0) {\n    throw new Error('At least one recipient is required.')\n  }\n\n  const senderPublicKey = getPublicKey(senderPrivateKey)\n\n  const wrappeds = [wrapEvent(event, senderPrivateKey, senderPublicKey)]\n\n  recipientsPublicKeys.forEach(recipientPublicKey => {\n    wrappeds.push(wrapEvent(event, senderPrivateKey, recipientPublicKey))\n  })\n\n  return wrappeds\n}\n\nexport function unwrapEvent(wrap: NostrEvent, recipientPrivateKey: Uint8Array): Rumor {\n  const unwrappedSeal = nip44Decrypt(wrap, recipientPrivateKey)\n  return nip44Decrypt(unwrappedSeal, recipientPrivateKey)\n}\n\nexport function unwrapManyEvents(wrappedEvents: NostrEvent[], recipientPrivateKey: Uint8Array): Rumor[] {\n  let unwrappedEvents: Rumor[] = []\n\n  wrappedEvents.forEach(e => {\n    unwrappedEvents.push(unwrapEvent(e, recipientPrivateKey))\n  })\n\n  unwrappedEvents.sort((a, b) => a.created_at - b.created_at)\n\n  return unwrappedEvents\n}\n"],
+  "mappings": "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",
+  "names": ["hexToBytes", "hexToBytes", "bytesToHex", "hexToBytes", "i", "sha256", "bytesToHex", "hexToBytes", "sha256"]
 }
Index: package/lib/cjs/nip77.js.map
===================================================================
--- package/lib/cjs/nip77.js.map
+++ package/lib/cjs/nip77.js.map
@@ -1,7 +1,7 @@
 {
   "version": 3,
   "sources": ["../../nip77.ts"],
-  "sourcesContent": ["import { bytesToHex, hexToBytes } from '@noble/ciphers/utils'\nimport { Filter } from './filter.ts'\nimport { AbstractRelay, Subscription } from './relay.ts'\nimport { sha256 } from '@noble/hashes/sha256'\n\n// Negentropy implementation by Doug Hoyte\nconst PROTOCOL_VERSION = 0x61 // Version 1\nconst ID_SIZE = 32\nconst FINGERPRINT_SIZE = 16\n\nconst Mode = {\n  Skip: 0,\n  Fingerprint: 1,\n  IdList: 2,\n}\n\nclass WrappedBuffer {\n  _raw: Uint8Array\n  length: number\n\n  constructor(buffer?: Uint8Array | number) {\n    if (typeof buffer === 'number') {\n      this._raw = new Uint8Array(buffer)\n      this.length = 0\n    } else if (buffer instanceof Uint8Array) {\n      this._raw = new Uint8Array(buffer)\n      this.length = buffer.length\n    } else {\n      this._raw = new Uint8Array(512)\n      this.length = 0\n    }\n  }\n\n  unwrap(): Uint8Array {\n    return this._raw.subarray(0, this.length)\n  }\n\n  get capacity(): number {\n    return this._raw.byteLength\n  }\n\n  extend(buf: Uint8Array | WrappedBuffer): void {\n    if (buf instanceof WrappedBuffer) buf = buf.unwrap()\n    if (typeof buf.length !== 'number') throw Error('bad length')\n    const targetSize = buf.length + this.length\n    if (this.capacity < targetSize) {\n      const oldRaw = this._raw\n      const newCapacity = Math.max(this.capacity * 2, targetSize)\n      this._raw = new Uint8Array(newCapacity)\n      this._raw.set(oldRaw)\n    }\n\n    this._raw.set(buf, this.length)\n    this.length += buf.length\n  }\n\n  shift(): number {\n    const first = this._raw[0]\n    this._raw = this._raw.subarray(1)\n    this.length--\n    return first\n  }\n\n  shiftN(n: number = 1): Uint8Array {\n    const firstSubarray = this._raw.subarray(0, n)\n    this._raw = this._raw.subarray(n)\n    this.length -= n\n    return firstSubarray\n  }\n}\n\nfunction decodeVarInt(buf: WrappedBuffer): number {\n  let res = 0\n\n  while (1) {\n    if (buf.length === 0) throw Error('parse ends prematurely')\n    let byte = buf.shift()\n    res = (res << 7) | (byte & 127)\n    if ((byte & 128) === 0) break\n  }\n\n  return res\n}\n\nfunction encodeVarInt(n: number): WrappedBuffer {\n  if (n === 0) return new WrappedBuffer(new Uint8Array([0]))\n\n  let o: number[] = []\n\n  while (n !== 0) {\n    o.push(n & 127)\n    n >>>= 7\n  }\n\n  o.reverse()\n\n  for (let i = 0; i < o.length - 1; i++) o[i] |= 128\n\n  return new WrappedBuffer(new Uint8Array(o))\n}\n\nfunction getByte(buf: WrappedBuffer): number {\n  return getBytes(buf, 1)[0]\n}\n\nfunction getBytes(buf: WrappedBuffer, n: number): Uint8Array {\n  if (buf.length < n) throw Error('parse ends prematurely')\n  return buf.shiftN(n)\n}\n\nclass Accumulator {\n  buf!: Uint8Array\n\n  constructor() {\n    this.setToZero()\n  }\n\n  setToZero(): void {\n    this.buf = new Uint8Array(ID_SIZE)\n  }\n\n  add(otherBuf: Uint8Array): void {\n    let currCarry = 0,\n      nextCarry = 0\n    let p = new DataView(this.buf.buffer)\n    let po = new DataView(otherBuf.buffer)\n\n    for (let i = 0; i < 8; i++) {\n      let offset = i * 4\n      let orig = p.getUint32(offset, true)\n      let otherV = po.getUint32(offset, true)\n\n      let next = orig\n\n      next += currCarry\n      next += otherV\n      if (next > 0xffffffff) nextCarry = 1\n\n      p.setUint32(offset, next & 0xffffffff, true)\n      currCarry = nextCarry\n      nextCarry = 0\n    }\n  }\n\n  negate(): void {\n    let p = new DataView(this.buf.buffer)\n\n    for (let i = 0; i < 8; i++) {\n      let offset = i * 4\n      p.setUint32(offset, ~p.getUint32(offset, true))\n    }\n\n    let one = new Uint8Array(ID_SIZE)\n    one[0] = 1\n    this.add(one)\n  }\n\n  getFingerprint(n: number): Uint8Array {\n    let input = new WrappedBuffer()\n    input.extend(this.buf)\n    input.extend(encodeVarInt(n))\n\n    let hash = sha256(input.unwrap())\n    return hash.subarray(0, FINGERPRINT_SIZE)\n  }\n}\n\nexport class NegentropyStorageVector {\n  items: { timestamp: number; id: Uint8Array }[]\n  sealed: boolean\n\n  constructor() {\n    this.items = []\n    this.sealed = false\n  }\n\n  insert(timestamp: number, id: string): void {\n    if (this.sealed) throw Error('already sealed')\n    const idb = hexToBytes(id)\n    if (idb.byteLength !== ID_SIZE) throw Error('bad id size for added item')\n    this.items.push({ timestamp, id: idb })\n  }\n\n  seal(): void {\n    if (this.sealed) throw Error('already sealed')\n    this.sealed = true\n\n    this.items.sort(itemCompare)\n\n    for (let i = 1; i < this.items.length; i++) {\n      if (itemCompare(this.items[i - 1], this.items[i]) === 0) throw Error('duplicate item inserted')\n    }\n  }\n\n  unseal(): void {\n    this.sealed = false\n  }\n\n  size(): number {\n    this._checkSealed()\n    return this.items.length\n  }\n\n  getItem(i: number): { timestamp: number; id: Uint8Array } {\n    this._checkSealed()\n    if (i >= this.items.length) throw Error('out of range')\n    return this.items[i]\n  }\n\n  iterate(begin: number, end: number, cb: (item: { timestamp: number; id: Uint8Array }, i: number) => boolean): void {\n    this._checkSealed()\n    this._checkBounds(begin, end)\n\n    for (let i = begin; i < end; ++i) {\n      if (!cb(this.items[i], i)) break\n    }\n  }\n\n  findLowerBound(begin: number, end: number, bound: { timestamp: number; id: Uint8Array }): number {\n    this._checkSealed()\n    this._checkBounds(begin, end)\n\n    return this._binarySearch(this.items, begin, end, a => itemCompare(a, bound) < 0)\n  }\n\n  fingerprint(begin: number, end: number): Uint8Array {\n    let out = new Accumulator()\n    out.setToZero()\n\n    this.iterate(begin, end, item => {\n      out.add(item.id)\n      return true\n    })\n\n    return out.getFingerprint(end - begin)\n  }\n\n  _checkSealed(): void {\n    if (!this.sealed) throw Error('not sealed')\n  }\n\n  _checkBounds(begin: number, end: number): void {\n    if (begin > end || end > this.items.length) throw Error('bad range')\n  }\n\n  _binarySearch(\n    arr: { timestamp: number; id: Uint8Array }[],\n    first: number,\n    last: number,\n    cmp: (a: { timestamp: number; id: Uint8Array }) => boolean,\n  ): number {\n    let count = last - first\n\n    while (count > 0) {\n      let it = first\n      let step = Math.floor(count / 2)\n      it += step\n\n      if (cmp(arr[it])) {\n        first = ++it\n        count -= step + 1\n      } else {\n        count = step\n      }\n    }\n\n    return first\n  }\n}\n\nexport class Negentropy {\n  storage: NegentropyStorageVector\n  frameSizeLimit: number\n  lastTimestampIn: number\n  lastTimestampOut: number\n\n  constructor(storage: NegentropyStorageVector, frameSizeLimit: number = 60_000) {\n    if (frameSizeLimit < 4096) throw Error('frameSizeLimit too small')\n\n    this.storage = storage\n    this.frameSizeLimit = frameSizeLimit\n\n    this.lastTimestampIn = 0\n    this.lastTimestampOut = 0\n  }\n\n  _bound(timestamp: number, id?: Uint8Array): { timestamp: number; id: Uint8Array } {\n    return { timestamp, id: id || new Uint8Array(0) }\n  }\n\n  initiate(): string {\n    let output = new WrappedBuffer()\n    output.extend(new Uint8Array([PROTOCOL_VERSION]))\n    this.splitRange(0, this.storage.size(), this._bound(Number.MAX_VALUE), output)\n    return bytesToHex(output.unwrap())\n  }\n\n  reconcile(queryMsg: string, onhave?: (id: string) => void, onneed?: (id: string) => void): string | null {\n    const query = new WrappedBuffer(hexToBytes(queryMsg))\n\n    this.lastTimestampIn = this.lastTimestampOut = 0 // reset for each message\n\n    let fullOutput = new WrappedBuffer()\n    fullOutput.extend(new Uint8Array([PROTOCOL_VERSION]))\n\n    let protocolVersion = getByte(query)\n    if (protocolVersion < 0x60 || protocolVersion > 0x6f) throw Error('invalid negentropy protocol version byte')\n    if (protocolVersion !== PROTOCOL_VERSION) {\n      throw Error('unsupported negentropy protocol version requested: ' + (protocolVersion - 0x60))\n    }\n\n    let storageSize = this.storage.size()\n    let prevBound = this._bound(0)\n    let prevIndex = 0\n    let skip = false\n\n    while (query.length !== 0) {\n      let o = new WrappedBuffer()\n\n      let doSkip = () => {\n        if (skip) {\n          skip = false\n          o.extend(this.encodeBound(prevBound))\n          o.extend(encodeVarInt(Mode.Skip))\n        }\n      }\n\n      let currBound = this.decodeBound(query)\n      let mode = decodeVarInt(query)\n\n      let lower = prevIndex\n      let upper = this.storage.findLowerBound(prevIndex, storageSize, currBound)\n\n      if (mode === Mode.Skip) {\n        skip = true\n      } else if (mode === Mode.Fingerprint) {\n        let theirFingerprint = getBytes(query, FINGERPRINT_SIZE)\n        let ourFingerprint = this.storage.fingerprint(lower, upper)\n\n        if (compareUint8Array(theirFingerprint, ourFingerprint) !== 0) {\n          doSkip()\n          this.splitRange(lower, upper, currBound, o)\n        } else {\n          skip = true\n        }\n      } else if (mode === Mode.IdList) {\n        let numIds = decodeVarInt(query)\n\n        let theirElems: { [key: string]: Uint8Array } = {} // stringified Uint8Array -> original Uint8Array (or hex)\n        for (let i = 0; i < numIds; i++) {\n          let e = getBytes(query, ID_SIZE)\n          theirElems[bytesToHex(e)] = e\n        }\n\n        skip = true\n        this.storage.iterate(lower, upper, item => {\n          let k = item.id\n          const id = bytesToHex(k)\n\n          if (!theirElems[id]) {\n            // ID exists on our side, but not their side\n            onhave?.(id)\n          } else {\n            // ID exists on both sides\n            delete theirElems[bytesToHex(k)]\n          }\n\n          return true\n        })\n\n        if (onneed) {\n          for (let v of Object.values(theirElems)) {\n            // ID exists on their side, but not our side\n            onneed(bytesToHex(v))\n          }\n        }\n      } else {\n        throw Error('unexpected mode')\n      }\n\n      if (this.exceededFrameSizeLimit(fullOutput.length + o.length)) {\n        // frameSizeLimit exceeded: stop range processing and return a fingerprint for the remaining range\n        let remainingFingerprint = this.storage.fingerprint(upper, storageSize)\n\n        fullOutput.extend(this.encodeBound(this._bound(Number.MAX_VALUE)))\n        fullOutput.extend(encodeVarInt(Mode.Fingerprint))\n        fullOutput.extend(remainingFingerprint)\n        break\n      } else {\n        fullOutput.extend(o)\n      }\n\n      prevIndex = upper\n      prevBound = currBound\n    }\n\n    return fullOutput.length === 1 ? null : bytesToHex(fullOutput.unwrap())\n  }\n\n  splitRange(lower: number, upper: number, upperBound: { timestamp: number; id: Uint8Array }, o: WrappedBuffer) {\n    let numElems = upper - lower\n    let buckets = 16\n\n    if (numElems < buckets * 2) {\n      o.extend(this.encodeBound(upperBound))\n      o.extend(encodeVarInt(Mode.IdList))\n\n      o.extend(encodeVarInt(numElems))\n      this.storage.iterate(lower, upper, item => {\n        o.extend(item.id)\n        return true\n      })\n    } else {\n      let itemsPerBucket = Math.floor(numElems / buckets)\n      let bucketsWithExtra = numElems % buckets\n      let curr = lower\n\n      for (let i = 0; i < buckets; i++) {\n        let bucketSize = itemsPerBucket + (i < bucketsWithExtra ? 1 : 0)\n        let ourFingerprint = this.storage.fingerprint(curr, curr + bucketSize)\n        curr += bucketSize\n\n        let nextBound: { timestamp: number; id: Uint8Array }\n\n        if (curr === upper) {\n          nextBound = upperBound\n        } else {\n          let prevItem: { timestamp: number; id: Uint8Array } | undefined\n          let currItem: { timestamp: number; id: Uint8Array } | undefined\n\n          this.storage.iterate(curr - 1, curr + 1, (item, index) => {\n            if (index === curr - 1) prevItem = item\n            else currItem = item\n            return true\n          })\n\n          nextBound = this.getMinimalBound(prevItem!, currItem!)\n        }\n\n        o.extend(this.encodeBound(nextBound))\n        o.extend(encodeVarInt(Mode.Fingerprint))\n        o.extend(ourFingerprint)\n      }\n    }\n  }\n\n  exceededFrameSizeLimit(n: number): boolean {\n    return n > this.frameSizeLimit - 200\n  }\n\n  // Decoding\n  decodeTimestampIn(encoded: WrappedBuffer): number {\n    let timestamp = decodeVarInt(encoded)\n    timestamp = timestamp === 0 ? Number.MAX_VALUE : timestamp - 1\n    if (this.lastTimestampIn === Number.MAX_VALUE || timestamp === Number.MAX_VALUE) {\n      this.lastTimestampIn = Number.MAX_VALUE\n      return Number.MAX_VALUE\n    }\n    timestamp += this.lastTimestampIn\n    this.lastTimestampIn = timestamp\n    return timestamp\n  }\n\n  decodeBound(encoded: WrappedBuffer): { timestamp: number; id: Uint8Array } {\n    let timestamp = this.decodeTimestampIn(encoded)\n    let len = decodeVarInt(encoded)\n    if (len > ID_SIZE) throw Error('bound key too long')\n    let id = getBytes(encoded, len)\n    return { timestamp, id }\n  }\n\n  // Encoding\n  encodeTimestampOut(timestamp: number): WrappedBuffer {\n    if (timestamp === Number.MAX_VALUE) {\n      this.lastTimestampOut = Number.MAX_VALUE\n      return encodeVarInt(0)\n    }\n\n    let temp = timestamp\n    timestamp -= this.lastTimestampOut\n    this.lastTimestampOut = temp\n    return encodeVarInt(timestamp + 1)\n  }\n\n  encodeBound(key: { timestamp: number; id: Uint8Array }): WrappedBuffer {\n    let output = new WrappedBuffer()\n\n    output.extend(this.encodeTimestampOut(key.timestamp))\n    output.extend(encodeVarInt(key.id.length))\n    output.extend(key.id)\n\n    return output\n  }\n\n  getMinimalBound(\n    prev: { timestamp: number; id: Uint8Array },\n    curr: { timestamp: number; id: Uint8Array },\n  ): { timestamp: number; id: Uint8Array } {\n    if (curr.timestamp !== prev.timestamp) {\n      return this._bound(curr.timestamp)\n    } else {\n      let sharedPrefixBytes = 0\n      let currKey = curr.id\n      let prevKey = prev.id\n\n      for (let i = 0; i < ID_SIZE; i++) {\n        if (currKey[i] !== prevKey[i]) break\n        sharedPrefixBytes++\n      }\n\n      return this._bound(curr.timestamp, curr.id.subarray(0, sharedPrefixBytes + 1))\n    }\n  }\n}\n\nfunction compareUint8Array(a: Uint8Array, b: Uint8Array): number {\n  for (let i = 0; i < a.byteLength; i++) {\n    if (a[i] < b[i]) return -1\n    if (a[i] > b[i]) return 1\n  }\n\n  if (a.byteLength > b.byteLength) return 1\n  if (a.byteLength < b.byteLength) return -1\n\n  return 0\n}\n\nfunction itemCompare(a: { timestamp: number; id: Uint8Array }, b: { timestamp: number; id: Uint8Array }): number {\n  if (a.timestamp === b.timestamp) {\n    return compareUint8Array(a.id, b.id)\n  }\n\n  return a.timestamp - b.timestamp\n}\n\nexport class NegentropySync {\n  relay: AbstractRelay\n  storage: NegentropyStorageVector\n  private neg: Negentropy\n  private filter: Filter\n  private subscription: Subscription\n  private onhave?: (id: string) => void\n  private onneed?: (id: string) => void\n\n  constructor(\n    relay: AbstractRelay,\n    storage: NegentropyStorageVector,\n    filter: Filter,\n    params: {\n      label?: string\n      onhave?: (id: string) => void\n      onneed?: (id: string) => void\n      onclose?: (errReason?: string) => void\n    } = {},\n  ) {\n    this.relay = relay\n    this.storage = storage\n    this.neg = new Negentropy(storage)\n    this.onhave = params.onhave\n    this.onneed = params.onneed\n    this.filter = filter\n\n    // we prepare a subscription with an empty filter, but it will not be used\n    this.subscription = this.relay.prepareSubscription([{}], { label: params.label || 'negentropy' })\n    this.subscription.oncustom = (data: string[]) => {\n      switch (data[0]) {\n        case 'NEG-MSG': {\n          if (data.length < 3) {\n            console.warn(`got invalid NEG-MSG from ${this.relay.url}: ${data}`)\n          }\n          try {\n            const response = this.neg.reconcile(data[2], this.onhave, this.onneed)\n            if (response) {\n              this.relay.send(`[\"NEG-MSG\", \"${this.subscription.id}\", \"${response}\"]`)\n            } else {\n              this.close()\n              params.onclose?.()\n            }\n          } catch (error) {\n            console.error('negentropy reconcile error:', error)\n            params?.onclose?.(`reconcile error: ${error}`)\n          }\n          break\n        }\n        case 'NEG-CLOSE': {\n          const reason = data[2]\n          console.warn('negentropy error:', reason)\n          params.onclose?.(reason)\n          break\n        }\n        case 'NEG-ERR': {\n          params.onclose?.()\n        }\n      }\n    }\n  }\n\n  async start(): Promise<void> {\n    const initMsg = this.neg.initiate()\n    this.relay.send(`[\"NEG-OPEN\",\"${this.subscription.id}\",${JSON.stringify(this.filter)},\"${initMsg}\"]`)\n  }\n\n  close(): void {\n    this.relay.send(`[\"NEG-CLOSE\",\"${this.subscription.id}\"]`)\n    this.subscription.close()\n  }\n}\n"],
-  "mappings": "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",
+  "sourcesContent": ["import { bytesToHex, hexToBytes } from '@noble/hashes/utils.js'\nimport { Filter } from './filter.ts'\nimport { AbstractRelay, Subscription } from './relay.ts'\nimport { sha256 } from '@noble/hashes/sha2.js'\n\n// Negentropy implementation by Doug Hoyte\nconst PROTOCOL_VERSION = 0x61 // Version 1\nconst ID_SIZE = 32\nconst FINGERPRINT_SIZE = 16\n\nconst Mode = {\n  Skip: 0,\n  Fingerprint: 1,\n  IdList: 2,\n}\n\nclass WrappedBuffer {\n  _raw: Uint8Array\n  length: number\n\n  constructor(buffer?: Uint8Array | number) {\n    if (typeof buffer === 'number') {\n      this._raw = new Uint8Array(buffer)\n      this.length = 0\n    } else if (buffer instanceof Uint8Array) {\n      this._raw = new Uint8Array(buffer)\n      this.length = buffer.length\n    } else {\n      this._raw = new Uint8Array(512)\n      this.length = 0\n    }\n  }\n\n  unwrap(): Uint8Array {\n    return this._raw.subarray(0, this.length)\n  }\n\n  get capacity(): number {\n    return this._raw.byteLength\n  }\n\n  extend(buf: Uint8Array | WrappedBuffer): void {\n    if (buf instanceof WrappedBuffer) buf = buf.unwrap()\n    if (typeof buf.length !== 'number') throw Error('bad length')\n    const targetSize = buf.length + this.length\n    if (this.capacity < targetSize) {\n      const oldRaw = this._raw\n      const newCapacity = Math.max(this.capacity * 2, targetSize)\n      this._raw = new Uint8Array(newCapacity)\n      this._raw.set(oldRaw)\n    }\n\n    this._raw.set(buf, this.length)\n    this.length += buf.length\n  }\n\n  shift(): number {\n    const first = this._raw[0]\n    this._raw = this._raw.subarray(1)\n    this.length--\n    return first\n  }\n\n  shiftN(n: number = 1): Uint8Array {\n    const firstSubarray = this._raw.subarray(0, n)\n    this._raw = this._raw.subarray(n)\n    this.length -= n\n    return firstSubarray\n  }\n}\n\nfunction decodeVarInt(buf: WrappedBuffer): number {\n  let res = 0\n\n  while (1) {\n    if (buf.length === 0) throw Error('parse ends prematurely')\n    let byte = buf.shift()\n    res = (res << 7) | (byte & 127)\n    if ((byte & 128) === 0) break\n  }\n\n  return res\n}\n\nfunction encodeVarInt(n: number): WrappedBuffer {\n  if (n === 0) return new WrappedBuffer(new Uint8Array([0]))\n\n  let o: number[] = []\n\n  while (n !== 0) {\n    o.push(n & 127)\n    n >>>= 7\n  }\n\n  o.reverse()\n\n  for (let i = 0; i < o.length - 1; i++) o[i] |= 128\n\n  return new WrappedBuffer(new Uint8Array(o))\n}\n\nfunction getByte(buf: WrappedBuffer): number {\n  return getBytes(buf, 1)[0]\n}\n\nfunction getBytes(buf: WrappedBuffer, n: number): Uint8Array {\n  if (buf.length < n) throw Error('parse ends prematurely')\n  return buf.shiftN(n)\n}\n\nclass Accumulator {\n  buf!: Uint8Array\n\n  constructor() {\n    this.setToZero()\n  }\n\n  setToZero(): void {\n    this.buf = new Uint8Array(ID_SIZE)\n  }\n\n  add(otherBuf: Uint8Array): void {\n    let currCarry = 0,\n      nextCarry = 0\n    let p = new DataView(this.buf.buffer)\n    let po = new DataView(otherBuf.buffer)\n\n    for (let i = 0; i < 8; i++) {\n      let offset = i * 4\n      let orig = p.getUint32(offset, true)\n      let otherV = po.getUint32(offset, true)\n\n      let next = orig\n\n      next += currCarry\n      next += otherV\n      if (next > 0xffffffff) nextCarry = 1\n\n      p.setUint32(offset, next & 0xffffffff, true)\n      currCarry = nextCarry\n      nextCarry = 0\n    }\n  }\n\n  negate(): void {\n    let p = new DataView(this.buf.buffer)\n\n    for (let i = 0; i < 8; i++) {\n      let offset = i * 4\n      p.setUint32(offset, ~p.getUint32(offset, true))\n    }\n\n    let one = new Uint8Array(ID_SIZE)\n    one[0] = 1\n    this.add(one)\n  }\n\n  getFingerprint(n: number): Uint8Array {\n    let input = new WrappedBuffer()\n    input.extend(this.buf)\n    input.extend(encodeVarInt(n))\n\n    let hash = sha256(input.unwrap())\n    return hash.subarray(0, FINGERPRINT_SIZE)\n  }\n}\n\nexport class NegentropyStorageVector {\n  items: { timestamp: number; id: Uint8Array }[]\n  sealed: boolean\n\n  constructor() {\n    this.items = []\n    this.sealed = false\n  }\n\n  insert(timestamp: number, id: string): void {\n    if (this.sealed) throw Error('already sealed')\n    const idb = hexToBytes(id)\n    if (idb.byteLength !== ID_SIZE) throw Error('bad id size for added item')\n    this.items.push({ timestamp, id: idb })\n  }\n\n  seal(): void {\n    if (this.sealed) throw Error('already sealed')\n    this.sealed = true\n\n    this.items.sort(itemCompare)\n\n    for (let i = 1; i < this.items.length; i++) {\n      if (itemCompare(this.items[i - 1], this.items[i]) === 0) throw Error('duplicate item inserted')\n    }\n  }\n\n  unseal(): void {\n    this.sealed = false\n  }\n\n  size(): number {\n    this._checkSealed()\n    return this.items.length\n  }\n\n  getItem(i: number): { timestamp: number; id: Uint8Array } {\n    this._checkSealed()\n    if (i >= this.items.length) throw Error('out of range')\n    return this.items[i]\n  }\n\n  iterate(begin: number, end: number, cb: (item: { timestamp: number; id: Uint8Array }, i: number) => boolean): void {\n    this._checkSealed()\n    this._checkBounds(begin, end)\n\n    for (let i = begin; i < end; ++i) {\n      if (!cb(this.items[i], i)) break\n    }\n  }\n\n  findLowerBound(begin: number, end: number, bound: { timestamp: number; id: Uint8Array }): number {\n    this._checkSealed()\n    this._checkBounds(begin, end)\n\n    return this._binarySearch(this.items, begin, end, a => itemCompare(a, bound) < 0)\n  }\n\n  fingerprint(begin: number, end: number): Uint8Array {\n    let out = new Accumulator()\n    out.setToZero()\n\n    this.iterate(begin, end, item => {\n      out.add(item.id)\n      return true\n    })\n\n    return out.getFingerprint(end - begin)\n  }\n\n  _checkSealed(): void {\n    if (!this.sealed) throw Error('not sealed')\n  }\n\n  _checkBounds(begin: number, end: number): void {\n    if (begin > end || end > this.items.length) throw Error('bad range')\n  }\n\n  _binarySearch(\n    arr: { timestamp: number; id: Uint8Array }[],\n    first: number,\n    last: number,\n    cmp: (a: { timestamp: number; id: Uint8Array }) => boolean,\n  ): number {\n    let count = last - first\n\n    while (count > 0) {\n      let it = first\n      let step = Math.floor(count / 2)\n      it += step\n\n      if (cmp(arr[it])) {\n        first = ++it\n        count -= step + 1\n      } else {\n        count = step\n      }\n    }\n\n    return first\n  }\n}\n\nexport class Negentropy {\n  storage: NegentropyStorageVector\n  frameSizeLimit: number\n  lastTimestampIn: number\n  lastTimestampOut: number\n\n  constructor(storage: NegentropyStorageVector, frameSizeLimit: number = 60_000) {\n    if (frameSizeLimit < 4096) throw Error('frameSizeLimit too small')\n\n    this.storage = storage\n    this.frameSizeLimit = frameSizeLimit\n\n    this.lastTimestampIn = 0\n    this.lastTimestampOut = 0\n  }\n\n  _bound(timestamp: number, id?: Uint8Array): { timestamp: number; id: Uint8Array } {\n    return { timestamp, id: id || new Uint8Array(0) }\n  }\n\n  initiate(): string {\n    let output = new WrappedBuffer()\n    output.extend(new Uint8Array([PROTOCOL_VERSION]))\n    this.splitRange(0, this.storage.size(), this._bound(Number.MAX_VALUE), output)\n    return bytesToHex(output.unwrap())\n  }\n\n  reconcile(queryMsg: string, onhave?: (id: string) => void, onneed?: (id: string) => void): string | null {\n    const query = new WrappedBuffer(hexToBytes(queryMsg))\n\n    this.lastTimestampIn = this.lastTimestampOut = 0 // reset for each message\n\n    let fullOutput = new WrappedBuffer()\n    fullOutput.extend(new Uint8Array([PROTOCOL_VERSION]))\n\n    let protocolVersion = getByte(query)\n    if (protocolVersion < 0x60 || protocolVersion > 0x6f) throw Error('invalid negentropy protocol version byte')\n    if (protocolVersion !== PROTOCOL_VERSION) {\n      throw Error('unsupported negentropy protocol version requested: ' + (protocolVersion - 0x60))\n    }\n\n    let storageSize = this.storage.size()\n    let prevBound = this._bound(0)\n    let prevIndex = 0\n    let skip = false\n\n    while (query.length !== 0) {\n      let o = new WrappedBuffer()\n\n      let doSkip = () => {\n        if (skip) {\n          skip = false\n          o.extend(this.encodeBound(prevBound))\n          o.extend(encodeVarInt(Mode.Skip))\n        }\n      }\n\n      let currBound = this.decodeBound(query)\n      let mode = decodeVarInt(query)\n\n      let lower = prevIndex\n      let upper = this.storage.findLowerBound(prevIndex, storageSize, currBound)\n\n      if (mode === Mode.Skip) {\n        skip = true\n      } else if (mode === Mode.Fingerprint) {\n        let theirFingerprint = getBytes(query, FINGERPRINT_SIZE)\n        let ourFingerprint = this.storage.fingerprint(lower, upper)\n\n        if (compareUint8Array(theirFingerprint, ourFingerprint) !== 0) {\n          doSkip()\n          this.splitRange(lower, upper, currBound, o)\n        } else {\n          skip = true\n        }\n      } else if (mode === Mode.IdList) {\n        let numIds = decodeVarInt(query)\n\n        let theirElems: { [key: string]: Uint8Array } = {} // stringified Uint8Array -> original Uint8Array (or hex)\n        for (let i = 0; i < numIds; i++) {\n          let e = getBytes(query, ID_SIZE)\n          theirElems[bytesToHex(e)] = e\n        }\n\n        skip = true\n        this.storage.iterate(lower, upper, item => {\n          let k = item.id\n          const id = bytesToHex(k)\n\n          if (!theirElems[id]) {\n            // ID exists on our side, but not their side\n            onhave?.(id)\n          } else {\n            // ID exists on both sides\n            delete theirElems[bytesToHex(k)]\n          }\n\n          return true\n        })\n\n        if (onneed) {\n          for (let v of Object.values(theirElems)) {\n            // ID exists on their side, but not our side\n            onneed(bytesToHex(v))\n          }\n        }\n      } else {\n        throw Error('unexpected mode')\n      }\n\n      if (this.exceededFrameSizeLimit(fullOutput.length + o.length)) {\n        // frameSizeLimit exceeded: stop range processing and return a fingerprint for the remaining range\n        let remainingFingerprint = this.storage.fingerprint(upper, storageSize)\n\n        fullOutput.extend(this.encodeBound(this._bound(Number.MAX_VALUE)))\n        fullOutput.extend(encodeVarInt(Mode.Fingerprint))\n        fullOutput.extend(remainingFingerprint)\n        break\n      } else {\n        fullOutput.extend(o)\n      }\n\n      prevIndex = upper\n      prevBound = currBound\n    }\n\n    return fullOutput.length === 1 ? null : bytesToHex(fullOutput.unwrap())\n  }\n\n  splitRange(lower: number, upper: number, upperBound: { timestamp: number; id: Uint8Array }, o: WrappedBuffer) {\n    let numElems = upper - lower\n    let buckets = 16\n\n    if (numElems < buckets * 2) {\n      o.extend(this.encodeBound(upperBound))\n      o.extend(encodeVarInt(Mode.IdList))\n\n      o.extend(encodeVarInt(numElems))\n      this.storage.iterate(lower, upper, item => {\n        o.extend(item.id)\n        return true\n      })\n    } else {\n      let itemsPerBucket = Math.floor(numElems / buckets)\n      let bucketsWithExtra = numElems % buckets\n      let curr = lower\n\n      for (let i = 0; i < buckets; i++) {\n        let bucketSize = itemsPerBucket + (i < bucketsWithExtra ? 1 : 0)\n        let ourFingerprint = this.storage.fingerprint(curr, curr + bucketSize)\n        curr += bucketSize\n\n        let nextBound: { timestamp: number; id: Uint8Array }\n\n        if (curr === upper) {\n          nextBound = upperBound\n        } else {\n          let prevItem: { timestamp: number; id: Uint8Array } | undefined\n          let currItem: { timestamp: number; id: Uint8Array } | undefined\n\n          this.storage.iterate(curr - 1, curr + 1, (item, index) => {\n            if (index === curr - 1) prevItem = item\n            else currItem = item\n            return true\n          })\n\n          nextBound = this.getMinimalBound(prevItem!, currItem!)\n        }\n\n        o.extend(this.encodeBound(nextBound))\n        o.extend(encodeVarInt(Mode.Fingerprint))\n        o.extend(ourFingerprint)\n      }\n    }\n  }\n\n  exceededFrameSizeLimit(n: number): boolean {\n    return n > this.frameSizeLimit - 200\n  }\n\n  // Decoding\n  decodeTimestampIn(encoded: WrappedBuffer): number {\n    let timestamp = decodeVarInt(encoded)\n    timestamp = timestamp === 0 ? Number.MAX_VALUE : timestamp - 1\n    if (this.lastTimestampIn === Number.MAX_VALUE || timestamp === Number.MAX_VALUE) {\n      this.lastTimestampIn = Number.MAX_VALUE\n      return Number.MAX_VALUE\n    }\n    timestamp += this.lastTimestampIn\n    this.lastTimestampIn = timestamp\n    return timestamp\n  }\n\n  decodeBound(encoded: WrappedBuffer): { timestamp: number; id: Uint8Array } {\n    let timestamp = this.decodeTimestampIn(encoded)\n    let len = decodeVarInt(encoded)\n    if (len > ID_SIZE) throw Error('bound key too long')\n    let id = getBytes(encoded, len)\n    return { timestamp, id }\n  }\n\n  // Encoding\n  encodeTimestampOut(timestamp: number): WrappedBuffer {\n    if (timestamp === Number.MAX_VALUE) {\n      this.lastTimestampOut = Number.MAX_VALUE\n      return encodeVarInt(0)\n    }\n\n    let temp = timestamp\n    timestamp -= this.lastTimestampOut\n    this.lastTimestampOut = temp\n    return encodeVarInt(timestamp + 1)\n  }\n\n  encodeBound(key: { timestamp: number; id: Uint8Array }): WrappedBuffer {\n    let output = new WrappedBuffer()\n\n    output.extend(this.encodeTimestampOut(key.timestamp))\n    output.extend(encodeVarInt(key.id.length))\n    output.extend(key.id)\n\n    return output\n  }\n\n  getMinimalBound(\n    prev: { timestamp: number; id: Uint8Array },\n    curr: { timestamp: number; id: Uint8Array },\n  ): { timestamp: number; id: Uint8Array } {\n    if (curr.timestamp !== prev.timestamp) {\n      return this._bound(curr.timestamp)\n    } else {\n      let sharedPrefixBytes = 0\n      let currKey = curr.id\n      let prevKey = prev.id\n\n      for (let i = 0; i < ID_SIZE; i++) {\n        if (currKey[i] !== prevKey[i]) break\n        sharedPrefixBytes++\n      }\n\n      return this._bound(curr.timestamp, curr.id.subarray(0, sharedPrefixBytes + 1))\n    }\n  }\n}\n\nfunction compareUint8Array(a: Uint8Array, b: Uint8Array): number {\n  for (let i = 0; i < a.byteLength; i++) {\n    if (a[i] < b[i]) return -1\n    if (a[i] > b[i]) return 1\n  }\n\n  if (a.byteLength > b.byteLength) return 1\n  if (a.byteLength < b.byteLength) return -1\n\n  return 0\n}\n\nfunction itemCompare(a: { timestamp: number; id: Uint8Array }, b: { timestamp: number; id: Uint8Array }): number {\n  if (a.timestamp === b.timestamp) {\n    return compareUint8Array(a.id, b.id)\n  }\n\n  return a.timestamp - b.timestamp\n}\n\nexport class NegentropySync {\n  relay: AbstractRelay\n  storage: NegentropyStorageVector\n  private neg: Negentropy\n  private filter: Filter\n  private subscription: Subscription\n  private onhave?: (id: string) => void\n  private onneed?: (id: string) => void\n\n  constructor(\n    relay: AbstractRelay,\n    storage: NegentropyStorageVector,\n    filter: Filter,\n    params: {\n      label?: string\n      onhave?: (id: string) => void\n      onneed?: (id: string) => void\n      onclose?: (errReason?: string) => void\n    } = {},\n  ) {\n    this.relay = relay\n    this.storage = storage\n    this.neg = new Negentropy(storage)\n    this.onhave = params.onhave\n    this.onneed = params.onneed\n    this.filter = filter\n\n    // we prepare a subscription with an empty filter, but it will not be used\n    this.subscription = this.relay.prepareSubscription([{}], { label: params.label || 'negentropy' })\n    this.subscription.oncustom = (data: string[]) => {\n      switch (data[0]) {\n        case 'NEG-MSG': {\n          if (data.length < 3) {\n            console.warn(`got invalid NEG-MSG from ${this.relay.url}: ${data}`)\n          }\n          try {\n            const response = this.neg.reconcile(data[2], this.onhave, this.onneed)\n            if (response) {\n              this.relay.send(`[\"NEG-MSG\", \"${this.subscription.id}\", \"${response}\"]`)\n            } else {\n              this.close()\n              params.onclose?.()\n            }\n          } catch (error) {\n            console.error('negentropy reconcile error:', error)\n            params?.onclose?.(`reconcile error: ${error}`)\n          }\n          break\n        }\n        case 'NEG-CLOSE': {\n          const reason = data[2]\n          console.warn('negentropy error:', reason)\n          params.onclose?.(reason)\n          break\n        }\n        case 'NEG-ERR': {\n          params.onclose?.()\n        }\n      }\n    }\n  }\n\n  async start(): Promise<void> {\n    const initMsg = this.neg.initiate()\n    this.relay.send(`[\"NEG-OPEN\",\"${this.subscription.id}\",${JSON.stringify(this.filter)},\"${initMsg}\"]`)\n  }\n\n  close(): void {\n    this.relay.send(`[\"NEG-CLOSE\",\"${this.subscription.id}\"]`)\n    this.subscription.close()\n  }\n}\n"],
+  "mappings": "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",
   "names": []
 }
Index: package/lib/esm/nip77.js.map
===================================================================
--- package/lib/esm/nip77.js.map
+++ package/lib/esm/nip77.js.map
@@ -1,7 +1,7 @@
 {
   "version": 3,
   "sources": ["../../nip77.ts"],
-  "sourcesContent": ["import { bytesToHex, hexToBytes } from '@noble/ciphers/utils'\nimport { Filter } from './filter.ts'\nimport { AbstractRelay, Subscription } from './relay.ts'\nimport { sha256 } from '@noble/hashes/sha256'\n\n// Negentropy implementation by Doug Hoyte\nconst PROTOCOL_VERSION = 0x61 // Version 1\nconst ID_SIZE = 32\nconst FINGERPRINT_SIZE = 16\n\nconst Mode = {\n  Skip: 0,\n  Fingerprint: 1,\n  IdList: 2,\n}\n\nclass WrappedBuffer {\n  _raw: Uint8Array\n  length: number\n\n  constructor(buffer?: Uint8Array | number) {\n    if (typeof buffer === 'number') {\n      this._raw = new Uint8Array(buffer)\n      this.length = 0\n    } else if (buffer instanceof Uint8Array) {\n      this._raw = new Uint8Array(buffer)\n      this.length = buffer.length\n    } else {\n      this._raw = new Uint8Array(512)\n      this.length = 0\n    }\n  }\n\n  unwrap(): Uint8Array {\n    return this._raw.subarray(0, this.length)\n  }\n\n  get capacity(): number {\n    return this._raw.byteLength\n  }\n\n  extend(buf: Uint8Array | WrappedBuffer): void {\n    if (buf instanceof WrappedBuffer) buf = buf.unwrap()\n    if (typeof buf.length !== 'number') throw Error('bad length')\n    const targetSize = buf.length + this.length\n    if (this.capacity < targetSize) {\n      const oldRaw = this._raw\n      const newCapacity = Math.max(this.capacity * 2, targetSize)\n      this._raw = new Uint8Array(newCapacity)\n      this._raw.set(oldRaw)\n    }\n\n    this._raw.set(buf, this.length)\n    this.length += buf.length\n  }\n\n  shift(): number {\n    const first = this._raw[0]\n    this._raw = this._raw.subarray(1)\n    this.length--\n    return first\n  }\n\n  shiftN(n: number = 1): Uint8Array {\n    const firstSubarray = this._raw.subarray(0, n)\n    this._raw = this._raw.subarray(n)\n    this.length -= n\n    return firstSubarray\n  }\n}\n\nfunction decodeVarInt(buf: WrappedBuffer): number {\n  let res = 0\n\n  while (1) {\n    if (buf.length === 0) throw Error('parse ends prematurely')\n    let byte = buf.shift()\n    res = (res << 7) | (byte & 127)\n    if ((byte & 128) === 0) break\n  }\n\n  return res\n}\n\nfunction encodeVarInt(n: number): WrappedBuffer {\n  if (n === 0) return new WrappedBuffer(new Uint8Array([0]))\n\n  let o: number[] = []\n\n  while (n !== 0) {\n    o.push(n & 127)\n    n >>>= 7\n  }\n\n  o.reverse()\n\n  for (let i = 0; i < o.length - 1; i++) o[i] |= 128\n\n  return new WrappedBuffer(new Uint8Array(o))\n}\n\nfunction getByte(buf: WrappedBuffer): number {\n  return getBytes(buf, 1)[0]\n}\n\nfunction getBytes(buf: WrappedBuffer, n: number): Uint8Array {\n  if (buf.length < n) throw Error('parse ends prematurely')\n  return buf.shiftN(n)\n}\n\nclass Accumulator {\n  buf!: Uint8Array\n\n  constructor() {\n    this.setToZero()\n  }\n\n  setToZero(): void {\n    this.buf = new Uint8Array(ID_SIZE)\n  }\n\n  add(otherBuf: Uint8Array): void {\n    let currCarry = 0,\n      nextCarry = 0\n    let p = new DataView(this.buf.buffer)\n    let po = new DataView(otherBuf.buffer)\n\n    for (let i = 0; i < 8; i++) {\n      let offset = i * 4\n      let orig = p.getUint32(offset, true)\n      let otherV = po.getUint32(offset, true)\n\n      let next = orig\n\n      next += currCarry\n      next += otherV\n      if (next > 0xffffffff) nextCarry = 1\n\n      p.setUint32(offset, next & 0xffffffff, true)\n      currCarry = nextCarry\n      nextCarry = 0\n    }\n  }\n\n  negate(): void {\n    let p = new DataView(this.buf.buffer)\n\n    for (let i = 0; i < 8; i++) {\n      let offset = i * 4\n      p.setUint32(offset, ~p.getUint32(offset, true))\n    }\n\n    let one = new Uint8Array(ID_SIZE)\n    one[0] = 1\n    this.add(one)\n  }\n\n  getFingerprint(n: number): Uint8Array {\n    let input = new WrappedBuffer()\n    input.extend(this.buf)\n    input.extend(encodeVarInt(n))\n\n    let hash = sha256(input.unwrap())\n    return hash.subarray(0, FINGERPRINT_SIZE)\n  }\n}\n\nexport class NegentropyStorageVector {\n  items: { timestamp: number; id: Uint8Array }[]\n  sealed: boolean\n\n  constructor() {\n    this.items = []\n    this.sealed = false\n  }\n\n  insert(timestamp: number, id: string): void {\n    if (this.sealed) throw Error('already sealed')\n    const idb = hexToBytes(id)\n    if (idb.byteLength !== ID_SIZE) throw Error('bad id size for added item')\n    this.items.push({ timestamp, id: idb })\n  }\n\n  seal(): void {\n    if (this.sealed) throw Error('already sealed')\n    this.sealed = true\n\n    this.items.sort(itemCompare)\n\n    for (let i = 1; i < this.items.length; i++) {\n      if (itemCompare(this.items[i - 1], this.items[i]) === 0) throw Error('duplicate item inserted')\n    }\n  }\n\n  unseal(): void {\n    this.sealed = false\n  }\n\n  size(): number {\n    this._checkSealed()\n    return this.items.length\n  }\n\n  getItem(i: number): { timestamp: number; id: Uint8Array } {\n    this._checkSealed()\n    if (i >= this.items.length) throw Error('out of range')\n    return this.items[i]\n  }\n\n  iterate(begin: number, end: number, cb: (item: { timestamp: number; id: Uint8Array }, i: number) => boolean): void {\n    this._checkSealed()\n    this._checkBounds(begin, end)\n\n    for (let i = begin; i < end; ++i) {\n      if (!cb(this.items[i], i)) break\n    }\n  }\n\n  findLowerBound(begin: number, end: number, bound: { timestamp: number; id: Uint8Array }): number {\n    this._checkSealed()\n    this._checkBounds(begin, end)\n\n    return this._binarySearch(this.items, begin, end, a => itemCompare(a, bound) < 0)\n  }\n\n  fingerprint(begin: number, end: number): Uint8Array {\n    let out = new Accumulator()\n    out.setToZero()\n\n    this.iterate(begin, end, item => {\n      out.add(item.id)\n      return true\n    })\n\n    return out.getFingerprint(end - begin)\n  }\n\n  _checkSealed(): void {\n    if (!this.sealed) throw Error('not sealed')\n  }\n\n  _checkBounds(begin: number, end: number): void {\n    if (begin > end || end > this.items.length) throw Error('bad range')\n  }\n\n  _binarySearch(\n    arr: { timestamp: number; id: Uint8Array }[],\n    first: number,\n    last: number,\n    cmp: (a: { timestamp: number; id: Uint8Array }) => boolean,\n  ): number {\n    let count = last - first\n\n    while (count > 0) {\n      let it = first\n      let step = Math.floor(count / 2)\n      it += step\n\n      if (cmp(arr[it])) {\n        first = ++it\n        count -= step + 1\n      } else {\n        count = step\n      }\n    }\n\n    return first\n  }\n}\n\nexport class Negentropy {\n  storage: NegentropyStorageVector\n  frameSizeLimit: number\n  lastTimestampIn: number\n  lastTimestampOut: number\n\n  constructor(storage: NegentropyStorageVector, frameSizeLimit: number = 60_000) {\n    if (frameSizeLimit < 4096) throw Error('frameSizeLimit too small')\n\n    this.storage = storage\n    this.frameSizeLimit = frameSizeLimit\n\n    this.lastTimestampIn = 0\n    this.lastTimestampOut = 0\n  }\n\n  _bound(timestamp: number, id?: Uint8Array): { timestamp: number; id: Uint8Array } {\n    return { timestamp, id: id || new Uint8Array(0) }\n  }\n\n  initiate(): string {\n    let output = new WrappedBuffer()\n    output.extend(new Uint8Array([PROTOCOL_VERSION]))\n    this.splitRange(0, this.storage.size(), this._bound(Number.MAX_VALUE), output)\n    return bytesToHex(output.unwrap())\n  }\n\n  reconcile(queryMsg: string, onhave?: (id: string) => void, onneed?: (id: string) => void): string | null {\n    const query = new WrappedBuffer(hexToBytes(queryMsg))\n\n    this.lastTimestampIn = this.lastTimestampOut = 0 // reset for each message\n\n    let fullOutput = new WrappedBuffer()\n    fullOutput.extend(new Uint8Array([PROTOCOL_VERSION]))\n\n    let protocolVersion = getByte(query)\n    if (protocolVersion < 0x60 || protocolVersion > 0x6f) throw Error('invalid negentropy protocol version byte')\n    if (protocolVersion !== PROTOCOL_VERSION) {\n      throw Error('unsupported negentropy protocol version requested: ' + (protocolVersion - 0x60))\n    }\n\n    let storageSize = this.storage.size()\n    let prevBound = this._bound(0)\n    let prevIndex = 0\n    let skip = false\n\n    while (query.length !== 0) {\n      let o = new WrappedBuffer()\n\n      let doSkip = () => {\n        if (skip) {\n          skip = false\n          o.extend(this.encodeBound(prevBound))\n          o.extend(encodeVarInt(Mode.Skip))\n        }\n      }\n\n      let currBound = this.decodeBound(query)\n      let mode = decodeVarInt(query)\n\n      let lower = prevIndex\n      let upper = this.storage.findLowerBound(prevIndex, storageSize, currBound)\n\n      if (mode === Mode.Skip) {\n        skip = true\n      } else if (mode === Mode.Fingerprint) {\n        let theirFingerprint = getBytes(query, FINGERPRINT_SIZE)\n        let ourFingerprint = this.storage.fingerprint(lower, upper)\n\n        if (compareUint8Array(theirFingerprint, ourFingerprint) !== 0) {\n          doSkip()\n          this.splitRange(lower, upper, currBound, o)\n        } else {\n          skip = true\n        }\n      } else if (mode === Mode.IdList) {\n        let numIds = decodeVarInt(query)\n\n        let theirElems: { [key: string]: Uint8Array } = {} // stringified Uint8Array -> original Uint8Array (or hex)\n        for (let i = 0; i < numIds; i++) {\n          let e = getBytes(query, ID_SIZE)\n          theirElems[bytesToHex(e)] = e\n        }\n\n        skip = true\n        this.storage.iterate(lower, upper, item => {\n          let k = item.id\n          const id = bytesToHex(k)\n\n          if (!theirElems[id]) {\n            // ID exists on our side, but not their side\n            onhave?.(id)\n          } else {\n            // ID exists on both sides\n            delete theirElems[bytesToHex(k)]\n          }\n\n          return true\n        })\n\n        if (onneed) {\n          for (let v of Object.values(theirElems)) {\n            // ID exists on their side, but not our side\n            onneed(bytesToHex(v))\n          }\n        }\n      } else {\n        throw Error('unexpected mode')\n      }\n\n      if (this.exceededFrameSizeLimit(fullOutput.length + o.length)) {\n        // frameSizeLimit exceeded: stop range processing and return a fingerprint for the remaining range\n        let remainingFingerprint = this.storage.fingerprint(upper, storageSize)\n\n        fullOutput.extend(this.encodeBound(this._bound(Number.MAX_VALUE)))\n        fullOutput.extend(encodeVarInt(Mode.Fingerprint))\n        fullOutput.extend(remainingFingerprint)\n        break\n      } else {\n        fullOutput.extend(o)\n      }\n\n      prevIndex = upper\n      prevBound = currBound\n    }\n\n    return fullOutput.length === 1 ? null : bytesToHex(fullOutput.unwrap())\n  }\n\n  splitRange(lower: number, upper: number, upperBound: { timestamp: number; id: Uint8Array }, o: WrappedBuffer) {\n    let numElems = upper - lower\n    let buckets = 16\n\n    if (numElems < buckets * 2) {\n      o.extend(this.encodeBound(upperBound))\n      o.extend(encodeVarInt(Mode.IdList))\n\n      o.extend(encodeVarInt(numElems))\n      this.storage.iterate(lower, upper, item => {\n        o.extend(item.id)\n        return true\n      })\n    } else {\n      let itemsPerBucket = Math.floor(numElems / buckets)\n      let bucketsWithExtra = numElems % buckets\n      let curr = lower\n\n      for (let i = 0; i < buckets; i++) {\n        let bucketSize = itemsPerBucket + (i < bucketsWithExtra ? 1 : 0)\n        let ourFingerprint = this.storage.fingerprint(curr, curr + bucketSize)\n        curr += bucketSize\n\n        let nextBound: { timestamp: number; id: Uint8Array }\n\n        if (curr === upper) {\n          nextBound = upperBound\n        } else {\n          let prevItem: { timestamp: number; id: Uint8Array } | undefined\n          let currItem: { timestamp: number; id: Uint8Array } | undefined\n\n          this.storage.iterate(curr - 1, curr + 1, (item, index) => {\n            if (index === curr - 1) prevItem = item\n            else currItem = item\n            return true\n          })\n\n          nextBound = this.getMinimalBound(prevItem!, currItem!)\n        }\n\n        o.extend(this.encodeBound(nextBound))\n        o.extend(encodeVarInt(Mode.Fingerprint))\n        o.extend(ourFingerprint)\n      }\n    }\n  }\n\n  exceededFrameSizeLimit(n: number): boolean {\n    return n > this.frameSizeLimit - 200\n  }\n\n  // Decoding\n  decodeTimestampIn(encoded: WrappedBuffer): number {\n    let timestamp = decodeVarInt(encoded)\n    timestamp = timestamp === 0 ? Number.MAX_VALUE : timestamp - 1\n    if (this.lastTimestampIn === Number.MAX_VALUE || timestamp === Number.MAX_VALUE) {\n      this.lastTimestampIn = Number.MAX_VALUE\n      return Number.MAX_VALUE\n    }\n    timestamp += this.lastTimestampIn\n    this.lastTimestampIn = timestamp\n    return timestamp\n  }\n\n  decodeBound(encoded: WrappedBuffer): { timestamp: number; id: Uint8Array } {\n    let timestamp = this.decodeTimestampIn(encoded)\n    let len = decodeVarInt(encoded)\n    if (len > ID_SIZE) throw Error('bound key too long')\n    let id = getBytes(encoded, len)\n    return { timestamp, id }\n  }\n\n  // Encoding\n  encodeTimestampOut(timestamp: number): WrappedBuffer {\n    if (timestamp === Number.MAX_VALUE) {\n      this.lastTimestampOut = Number.MAX_VALUE\n      return encodeVarInt(0)\n    }\n\n    let temp = timestamp\n    timestamp -= this.lastTimestampOut\n    this.lastTimestampOut = temp\n    return encodeVarInt(timestamp + 1)\n  }\n\n  encodeBound(key: { timestamp: number; id: Uint8Array }): WrappedBuffer {\n    let output = new WrappedBuffer()\n\n    output.extend(this.encodeTimestampOut(key.timestamp))\n    output.extend(encodeVarInt(key.id.length))\n    output.extend(key.id)\n\n    return output\n  }\n\n  getMinimalBound(\n    prev: { timestamp: number; id: Uint8Array },\n    curr: { timestamp: number; id: Uint8Array },\n  ): { timestamp: number; id: Uint8Array } {\n    if (curr.timestamp !== prev.timestamp) {\n      return this._bound(curr.timestamp)\n    } else {\n      let sharedPrefixBytes = 0\n      let currKey = curr.id\n      let prevKey = prev.id\n\n      for (let i = 0; i < ID_SIZE; i++) {\n        if (currKey[i] !== prevKey[i]) break\n        sharedPrefixBytes++\n      }\n\n      return this._bound(curr.timestamp, curr.id.subarray(0, sharedPrefixBytes + 1))\n    }\n  }\n}\n\nfunction compareUint8Array(a: Uint8Array, b: Uint8Array): number {\n  for (let i = 0; i < a.byteLength; i++) {\n    if (a[i] < b[i]) return -1\n    if (a[i] > b[i]) return 1\n  }\n\n  if (a.byteLength > b.byteLength) return 1\n  if (a.byteLength < b.byteLength) return -1\n\n  return 0\n}\n\nfunction itemCompare(a: { timestamp: number; id: Uint8Array }, b: { timestamp: number; id: Uint8Array }): number {\n  if (a.timestamp === b.timestamp) {\n    return compareUint8Array(a.id, b.id)\n  }\n\n  return a.timestamp - b.timestamp\n}\n\nexport class NegentropySync {\n  relay: AbstractRelay\n  storage: NegentropyStorageVector\n  private neg: Negentropy\n  private filter: Filter\n  private subscription: Subscription\n  private onhave?: (id: string) => void\n  private onneed?: (id: string) => void\n\n  constructor(\n    relay: AbstractRelay,\n    storage: NegentropyStorageVector,\n    filter: Filter,\n    params: {\n      label?: string\n      onhave?: (id: string) => void\n      onneed?: (id: string) => void\n      onclose?: (errReason?: string) => void\n    } = {},\n  ) {\n    this.relay = relay\n    this.storage = storage\n    this.neg = new Negentropy(storage)\n    this.onhave = params.onhave\n    this.onneed = params.onneed\n    this.filter = filter\n\n    // we prepare a subscription with an empty filter, but it will not be used\n    this.subscription = this.relay.prepareSubscription([{}], { label: params.label || 'negentropy' })\n    this.subscription.oncustom = (data: string[]) => {\n      switch (data[0]) {\n        case 'NEG-MSG': {\n          if (data.length < 3) {\n            console.warn(`got invalid NEG-MSG from ${this.relay.url}: ${data}`)\n          }\n          try {\n            const response = this.neg.reconcile(data[2], this.onhave, this.onneed)\n            if (response) {\n              this.relay.send(`[\"NEG-MSG\", \"${this.subscription.id}\", \"${response}\"]`)\n            } else {\n              this.close()\n              params.onclose?.()\n            }\n          } catch (error) {\n            console.error('negentropy reconcile error:', error)\n            params?.onclose?.(`reconcile error: ${error}`)\n          }\n          break\n        }\n        case 'NEG-CLOSE': {\n          const reason = data[2]\n          console.warn('negentropy error:', reason)\n          params.onclose?.(reason)\n          break\n        }\n        case 'NEG-ERR': {\n          params.onclose?.()\n        }\n      }\n    }\n  }\n\n  async start(): Promise<void> {\n    const initMsg = this.neg.initiate()\n    this.relay.send(`[\"NEG-OPEN\",\"${this.subscription.id}\",${JSON.stringify(this.filter)},\"${initMsg}\"]`)\n  }\n\n  close(): void {\n    this.relay.send(`[\"NEG-CLOSE\",\"${this.subscription.id}\"]`)\n    this.subscription.close()\n  }\n}\n"],
+  "sourcesContent": ["import { bytesToHex, hexToBytes } from '@noble/hashes/utils.js'\nimport { Filter } from './filter.ts'\nimport { AbstractRelay, Subscription } from './relay.ts'\nimport { sha256 } from '@noble/hashes/sha2.js'\n\n// Negentropy implementation by Doug Hoyte\nconst PROTOCOL_VERSION = 0x61 // Version 1\nconst ID_SIZE = 32\nconst FINGERPRINT_SIZE = 16\n\nconst Mode = {\n  Skip: 0,\n  Fingerprint: 1,\n  IdList: 2,\n}\n\nclass WrappedBuffer {\n  _raw: Uint8Array\n  length: number\n\n  constructor(buffer?: Uint8Array | number) {\n    if (typeof buffer === 'number') {\n      this._raw = new Uint8Array(buffer)\n      this.length = 0\n    } else if (buffer instanceof Uint8Array) {\n      this._raw = new Uint8Array(buffer)\n      this.length = buffer.length\n    } else {\n      this._raw = new Uint8Array(512)\n      this.length = 0\n    }\n  }\n\n  unwrap(): Uint8Array {\n    return this._raw.subarray(0, this.length)\n  }\n\n  get capacity(): number {\n    return this._raw.byteLength\n  }\n\n  extend(buf: Uint8Array | WrappedBuffer): void {\n    if (buf instanceof WrappedBuffer) buf = buf.unwrap()\n    if (typeof buf.length !== 'number') throw Error('bad length')\n    const targetSize = buf.length + this.length\n    if (this.capacity < targetSize) {\n      const oldRaw = this._raw\n      const newCapacity = Math.max(this.capacity * 2, targetSize)\n      this._raw = new Uint8Array(newCapacity)\n      this._raw.set(oldRaw)\n    }\n\n    this._raw.set(buf, this.length)\n    this.length += buf.length\n  }\n\n  shift(): number {\n    const first = this._raw[0]\n    this._raw = this._raw.subarray(1)\n    this.length--\n    return first\n  }\n\n  shiftN(n: number = 1): Uint8Array {\n    const firstSubarray = this._raw.subarray(0, n)\n    this._raw = this._raw.subarray(n)\n    this.length -= n\n    return firstSubarray\n  }\n}\n\nfunction decodeVarInt(buf: WrappedBuffer): number {\n  let res = 0\n\n  while (1) {\n    if (buf.length === 0) throw Error('parse ends prematurely')\n    let byte = buf.shift()\n    res = (res << 7) | (byte & 127)\n    if ((byte & 128) === 0) break\n  }\n\n  return res\n}\n\nfunction encodeVarInt(n: number): WrappedBuffer {\n  if (n === 0) return new WrappedBuffer(new Uint8Array([0]))\n\n  let o: number[] = []\n\n  while (n !== 0) {\n    o.push(n & 127)\n    n >>>= 7\n  }\n\n  o.reverse()\n\n  for (let i = 0; i < o.length - 1; i++) o[i] |= 128\n\n  return new WrappedBuffer(new Uint8Array(o))\n}\n\nfunction getByte(buf: WrappedBuffer): number {\n  return getBytes(buf, 1)[0]\n}\n\nfunction getBytes(buf: WrappedBuffer, n: number): Uint8Array {\n  if (buf.length < n) throw Error('parse ends prematurely')\n  return buf.shiftN(n)\n}\n\nclass Accumulator {\n  buf!: Uint8Array\n\n  constructor() {\n    this.setToZero()\n  }\n\n  setToZero(): void {\n    this.buf = new Uint8Array(ID_SIZE)\n  }\n\n  add(otherBuf: Uint8Array): void {\n    let currCarry = 0,\n      nextCarry = 0\n    let p = new DataView(this.buf.buffer)\n    let po = new DataView(otherBuf.buffer)\n\n    for (let i = 0; i < 8; i++) {\n      let offset = i * 4\n      let orig = p.getUint32(offset, true)\n      let otherV = po.getUint32(offset, true)\n\n      let next = orig\n\n      next += currCarry\n      next += otherV\n      if (next > 0xffffffff) nextCarry = 1\n\n      p.setUint32(offset, next & 0xffffffff, true)\n      currCarry = nextCarry\n      nextCarry = 0\n    }\n  }\n\n  negate(): void {\n    let p = new DataView(this.buf.buffer)\n\n    for (let i = 0; i < 8; i++) {\n      let offset = i * 4\n      p.setUint32(offset, ~p.getUint32(offset, true))\n    }\n\n    let one = new Uint8Array(ID_SIZE)\n    one[0] = 1\n    this.add(one)\n  }\n\n  getFingerprint(n: number): Uint8Array {\n    let input = new WrappedBuffer()\n    input.extend(this.buf)\n    input.extend(encodeVarInt(n))\n\n    let hash = sha256(input.unwrap())\n    return hash.subarray(0, FINGERPRINT_SIZE)\n  }\n}\n\nexport class NegentropyStorageVector {\n  items: { timestamp: number; id: Uint8Array }[]\n  sealed: boolean\n\n  constructor() {\n    this.items = []\n    this.sealed = false\n  }\n\n  insert(timestamp: number, id: string): void {\n    if (this.sealed) throw Error('already sealed')\n    const idb = hexToBytes(id)\n    if (idb.byteLength !== ID_SIZE) throw Error('bad id size for added item')\n    this.items.push({ timestamp, id: idb })\n  }\n\n  seal(): void {\n    if (this.sealed) throw Error('already sealed')\n    this.sealed = true\n\n    this.items.sort(itemCompare)\n\n    for (let i = 1; i < this.items.length; i++) {\n      if (itemCompare(this.items[i - 1], this.items[i]) === 0) throw Error('duplicate item inserted')\n    }\n  }\n\n  unseal(): void {\n    this.sealed = false\n  }\n\n  size(): number {\n    this._checkSealed()\n    return this.items.length\n  }\n\n  getItem(i: number): { timestamp: number; id: Uint8Array } {\n    this._checkSealed()\n    if (i >= this.items.length) throw Error('out of range')\n    return this.items[i]\n  }\n\n  iterate(begin: number, end: number, cb: (item: { timestamp: number; id: Uint8Array }, i: number) => boolean): void {\n    this._checkSealed()\n    this._checkBounds(begin, end)\n\n    for (let i = begin; i < end; ++i) {\n      if (!cb(this.items[i], i)) break\n    }\n  }\n\n  findLowerBound(begin: number, end: number, bound: { timestamp: number; id: Uint8Array }): number {\n    this._checkSealed()\n    this._checkBounds(begin, end)\n\n    return this._binarySearch(this.items, begin, end, a => itemCompare(a, bound) < 0)\n  }\n\n  fingerprint(begin: number, end: number): Uint8Array {\n    let out = new Accumulator()\n    out.setToZero()\n\n    this.iterate(begin, end, item => {\n      out.add(item.id)\n      return true\n    })\n\n    return out.getFingerprint(end - begin)\n  }\n\n  _checkSealed(): void {\n    if (!this.sealed) throw Error('not sealed')\n  }\n\n  _checkBounds(begin: number, end: number): void {\n    if (begin > end || end > this.items.length) throw Error('bad range')\n  }\n\n  _binarySearch(\n    arr: { timestamp: number; id: Uint8Array }[],\n    first: number,\n    last: number,\n    cmp: (a: { timestamp: number; id: Uint8Array }) => boolean,\n  ): number {\n    let count = last - first\n\n    while (count > 0) {\n      let it = first\n      let step = Math.floor(count / 2)\n      it += step\n\n      if (cmp(arr[it])) {\n        first = ++it\n        count -= step + 1\n      } else {\n        count = step\n      }\n    }\n\n    return first\n  }\n}\n\nexport class Negentropy {\n  storage: NegentropyStorageVector\n  frameSizeLimit: number\n  lastTimestampIn: number\n  lastTimestampOut: number\n\n  constructor(storage: NegentropyStorageVector, frameSizeLimit: number = 60_000) {\n    if (frameSizeLimit < 4096) throw Error('frameSizeLimit too small')\n\n    this.storage = storage\n    this.frameSizeLimit = frameSizeLimit\n\n    this.lastTimestampIn = 0\n    this.lastTimestampOut = 0\n  }\n\n  _bound(timestamp: number, id?: Uint8Array): { timestamp: number; id: Uint8Array } {\n    return { timestamp, id: id || new Uint8Array(0) }\n  }\n\n  initiate(): string {\n    let output = new WrappedBuffer()\n    output.extend(new Uint8Array([PROTOCOL_VERSION]))\n    this.splitRange(0, this.storage.size(), this._bound(Number.MAX_VALUE), output)\n    return bytesToHex(output.unwrap())\n  }\n\n  reconcile(queryMsg: string, onhave?: (id: string) => void, onneed?: (id: string) => void): string | null {\n    const query = new WrappedBuffer(hexToBytes(queryMsg))\n\n    this.lastTimestampIn = this.lastTimestampOut = 0 // reset for each message\n\n    let fullOutput = new WrappedBuffer()\n    fullOutput.extend(new Uint8Array([PROTOCOL_VERSION]))\n\n    let protocolVersion = getByte(query)\n    if (protocolVersion < 0x60 || protocolVersion > 0x6f) throw Error('invalid negentropy protocol version byte')\n    if (protocolVersion !== PROTOCOL_VERSION) {\n      throw Error('unsupported negentropy protocol version requested: ' + (protocolVersion - 0x60))\n    }\n\n    let storageSize = this.storage.size()\n    let prevBound = this._bound(0)\n    let prevIndex = 0\n    let skip = false\n\n    while (query.length !== 0) {\n      let o = new WrappedBuffer()\n\n      let doSkip = () => {\n        if (skip) {\n          skip = false\n          o.extend(this.encodeBound(prevBound))\n          o.extend(encodeVarInt(Mode.Skip))\n        }\n      }\n\n      let currBound = this.decodeBound(query)\n      let mode = decodeVarInt(query)\n\n      let lower = prevIndex\n      let upper = this.storage.findLowerBound(prevIndex, storageSize, currBound)\n\n      if (mode === Mode.Skip) {\n        skip = true\n      } else if (mode === Mode.Fingerprint) {\n        let theirFingerprint = getBytes(query, FINGERPRINT_SIZE)\n        let ourFingerprint = this.storage.fingerprint(lower, upper)\n\n        if (compareUint8Array(theirFingerprint, ourFingerprint) !== 0) {\n          doSkip()\n          this.splitRange(lower, upper, currBound, o)\n        } else {\n          skip = true\n        }\n      } else if (mode === Mode.IdList) {\n        let numIds = decodeVarInt(query)\n\n        let theirElems: { [key: string]: Uint8Array } = {} // stringified Uint8Array -> original Uint8Array (or hex)\n        for (let i = 0; i < numIds; i++) {\n          let e = getBytes(query, ID_SIZE)\n          theirElems[bytesToHex(e)] = e\n        }\n\n        skip = true\n        this.storage.iterate(lower, upper, item => {\n          let k = item.id\n          const id = bytesToHex(k)\n\n          if (!theirElems[id]) {\n            // ID exists on our side, but not their side\n            onhave?.(id)\n          } else {\n            // ID exists on both sides\n            delete theirElems[bytesToHex(k)]\n          }\n\n          return true\n        })\n\n        if (onneed) {\n          for (let v of Object.values(theirElems)) {\n            // ID exists on their side, but not our side\n            onneed(bytesToHex(v))\n          }\n        }\n      } else {\n        throw Error('unexpected mode')\n      }\n\n      if (this.exceededFrameSizeLimit(fullOutput.length + o.length)) {\n        // frameSizeLimit exceeded: stop range processing and return a fingerprint for the remaining range\n        let remainingFingerprint = this.storage.fingerprint(upper, storageSize)\n\n        fullOutput.extend(this.encodeBound(this._bound(Number.MAX_VALUE)))\n        fullOutput.extend(encodeVarInt(Mode.Fingerprint))\n        fullOutput.extend(remainingFingerprint)\n        break\n      } else {\n        fullOutput.extend(o)\n      }\n\n      prevIndex = upper\n      prevBound = currBound\n    }\n\n    return fullOutput.length === 1 ? null : bytesToHex(fullOutput.unwrap())\n  }\n\n  splitRange(lower: number, upper: number, upperBound: { timestamp: number; id: Uint8Array }, o: WrappedBuffer) {\n    let numElems = upper - lower\n    let buckets = 16\n\n    if (numElems < buckets * 2) {\n      o.extend(this.encodeBound(upperBound))\n      o.extend(encodeVarInt(Mode.IdList))\n\n      o.extend(encodeVarInt(numElems))\n      this.storage.iterate(lower, upper, item => {\n        o.extend(item.id)\n        return true\n      })\n    } else {\n      let itemsPerBucket = Math.floor(numElems / buckets)\n      let bucketsWithExtra = numElems % buckets\n      let curr = lower\n\n      for (let i = 0; i < buckets; i++) {\n        let bucketSize = itemsPerBucket + (i < bucketsWithExtra ? 1 : 0)\n        let ourFingerprint = this.storage.fingerprint(curr, curr + bucketSize)\n        curr += bucketSize\n\n        let nextBound: { timestamp: number; id: Uint8Array }\n\n        if (curr === upper) {\n          nextBound = upperBound\n        } else {\n          let prevItem: { timestamp: number; id: Uint8Array } | undefined\n          let currItem: { timestamp: number; id: Uint8Array } | undefined\n\n          this.storage.iterate(curr - 1, curr + 1, (item, index) => {\n            if (index === curr - 1) prevItem = item\n            else currItem = item\n            return true\n          })\n\n          nextBound = this.getMinimalBound(prevItem!, currItem!)\n        }\n\n        o.extend(this.encodeBound(nextBound))\n        o.extend(encodeVarInt(Mode.Fingerprint))\n        o.extend(ourFingerprint)\n      }\n    }\n  }\n\n  exceededFrameSizeLimit(n: number): boolean {\n    return n > this.frameSizeLimit - 200\n  }\n\n  // Decoding\n  decodeTimestampIn(encoded: WrappedBuffer): number {\n    let timestamp = decodeVarInt(encoded)\n    timestamp = timestamp === 0 ? Number.MAX_VALUE : timestamp - 1\n    if (this.lastTimestampIn === Number.MAX_VALUE || timestamp === Number.MAX_VALUE) {\n      this.lastTimestampIn = Number.MAX_VALUE\n      return Number.MAX_VALUE\n    }\n    timestamp += this.lastTimestampIn\n    this.lastTimestampIn = timestamp\n    return timestamp\n  }\n\n  decodeBound(encoded: WrappedBuffer): { timestamp: number; id: Uint8Array } {\n    let timestamp = this.decodeTimestampIn(encoded)\n    let len = decodeVarInt(encoded)\n    if (len > ID_SIZE) throw Error('bound key too long')\n    let id = getBytes(encoded, len)\n    return { timestamp, id }\n  }\n\n  // Encoding\n  encodeTimestampOut(timestamp: number): WrappedBuffer {\n    if (timestamp === Number.MAX_VALUE) {\n      this.lastTimestampOut = Number.MAX_VALUE\n      return encodeVarInt(0)\n    }\n\n    let temp = timestamp\n    timestamp -= this.lastTimestampOut\n    this.lastTimestampOut = temp\n    return encodeVarInt(timestamp + 1)\n  }\n\n  encodeBound(key: { timestamp: number; id: Uint8Array }): WrappedBuffer {\n    let output = new WrappedBuffer()\n\n    output.extend(this.encodeTimestampOut(key.timestamp))\n    output.extend(encodeVarInt(key.id.length))\n    output.extend(key.id)\n\n    return output\n  }\n\n  getMinimalBound(\n    prev: { timestamp: number; id: Uint8Array },\n    curr: { timestamp: number; id: Uint8Array },\n  ): { timestamp: number; id: Uint8Array } {\n    if (curr.timestamp !== prev.timestamp) {\n      return this._bound(curr.timestamp)\n    } else {\n      let sharedPrefixBytes = 0\n      let currKey = curr.id\n      let prevKey = prev.id\n\n      for (let i = 0; i < ID_SIZE; i++) {\n        if (currKey[i] !== prevKey[i]) break\n        sharedPrefixBytes++\n      }\n\n      return this._bound(curr.timestamp, curr.id.subarray(0, sharedPrefixBytes + 1))\n    }\n  }\n}\n\nfunction compareUint8Array(a: Uint8Array, b: Uint8Array): number {\n  for (let i = 0; i < a.byteLength; i++) {\n    if (a[i] < b[i]) return -1\n    if (a[i] > b[i]) return 1\n  }\n\n  if (a.byteLength > b.byteLength) return 1\n  if (a.byteLength < b.byteLength) return -1\n\n  return 0\n}\n\nfunction itemCompare(a: { timestamp: number; id: Uint8Array }, b: { timestamp: number; id: Uint8Array }): number {\n  if (a.timestamp === b.timestamp) {\n    return compareUint8Array(a.id, b.id)\n  }\n\n  return a.timestamp - b.timestamp\n}\n\nexport class NegentropySync {\n  relay: AbstractRelay\n  storage: NegentropyStorageVector\n  private neg: Negentropy\n  private filter: Filter\n  private subscription: Subscription\n  private onhave?: (id: string) => void\n  private onneed?: (id: string) => void\n\n  constructor(\n    relay: AbstractRelay,\n    storage: NegentropyStorageVector,\n    filter: Filter,\n    params: {\n      label?: string\n      onhave?: (id: string) => void\n      onneed?: (id: string) => void\n      onclose?: (errReason?: string) => void\n    } = {},\n  ) {\n    this.relay = relay\n    this.storage = storage\n    this.neg = new Negentropy(storage)\n    this.onhave = params.onhave\n    this.onneed = params.onneed\n    this.filter = filter\n\n    // we prepare a subscription with an empty filter, but it will not be used\n    this.subscription = this.relay.prepareSubscription([{}], { label: params.label || 'negentropy' })\n    this.subscription.oncustom = (data: string[]) => {\n      switch (data[0]) {\n        case 'NEG-MSG': {\n          if (data.length < 3) {\n            console.warn(`got invalid NEG-MSG from ${this.relay.url}: ${data}`)\n          }\n          try {\n            const response = this.neg.reconcile(data[2], this.onhave, this.onneed)\n            if (response) {\n              this.relay.send(`[\"NEG-MSG\", \"${this.subscription.id}\", \"${response}\"]`)\n            } else {\n              this.close()\n              params.onclose?.()\n            }\n          } catch (error) {\n            console.error('negentropy reconcile error:', error)\n            params?.onclose?.(`reconcile error: ${error}`)\n          }\n          break\n        }\n        case 'NEG-CLOSE': {\n          const reason = data[2]\n          console.warn('negentropy error:', reason)\n          params.onclose?.(reason)\n          break\n        }\n        case 'NEG-ERR': {\n          params.onclose?.()\n        }\n      }\n    }\n  }\n\n  async start(): Promise<void> {\n    const initMsg = this.neg.initiate()\n    this.relay.send(`[\"NEG-OPEN\",\"${this.subscription.id}\",${JSON.stringify(this.filter)},\"${initMsg}\"]`)\n  }\n\n  close(): void {\n    this.relay.send(`[\"NEG-CLOSE\",\"${this.subscription.id}\"]`)\n    this.subscription.close()\n  }\n}\n"],
   "mappings": "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",
   "names": []
 }
Index: package/lib/cjs/nip98.js.map
===================================================================
--- package/lib/cjs/nip98.js.map
+++ package/lib/cjs/nip98.js.map
@@ -1,7 +1,7 @@
 {
   "version": 3,
   "sources": ["../../nip98.ts", "../../pure.ts", "../../core.ts", "../../utils.ts", "../../kinds.ts"],
-  "sourcesContent": ["import { sha256 } from '@noble/hashes/sha256'\nimport { bytesToHex } from '@noble/hashes/utils'\nimport { base64 } from '@scure/base'\n\nimport { HTTPAuth } from './kinds.ts'\nimport { Event, EventTemplate, verifyEvent } from './pure.ts'\nimport { utf8Decoder, utf8Encoder } from './utils.ts'\n\nconst _authorizationScheme = 'Nostr '\n\n/**\n * Generate token for NIP-98 flow.\n *\n * @example\n * const sign = window.nostr.signEvent\n * await nip98.getToken('https://example.com/login', 'post', (e) => sign(e), true)\n */\nexport async function getToken(\n  loginUrl: string,\n  httpMethod: string,\n  sign: (e: EventTemplate) => Promise<Event> | Event,\n  includeAuthorizationScheme: boolean = false,\n  payload?: Record<string, any>,\n): Promise<string> {\n  const event: EventTemplate = {\n    kind: HTTPAuth,\n    tags: [\n      ['u', loginUrl],\n      ['method', httpMethod],\n    ],\n    created_at: Math.round(new Date().getTime() / 1000),\n    content: '',\n  }\n\n  if (payload) {\n    event.tags.push(['payload', hashPayload(payload)])\n  }\n\n  const signedEvent = await sign(event)\n  const authorizationScheme = includeAuthorizationScheme ? _authorizationScheme : ''\n\n  return authorizationScheme + base64.encode(utf8Encoder.encode(JSON.stringify(signedEvent)))\n}\n\n/**\n * Validate token for NIP-98 flow.\n *\n * @example\n * await nip98.validateToken('Nostr base64token', 'https://example.com/login', 'post')\n */\nexport async function validateToken(token: string, url: string, method: string): Promise<boolean> {\n  const event = await unpackEventFromToken(token).catch(error => {\n    throw error\n  })\n\n  const valid = await validateEvent(event, url, method).catch(error => {\n    throw error\n  })\n\n  return valid\n}\n\n/**\n * Unpacks an event from a token.\n *\n * @param token - The token to unpack.\n * @returns A promise that resolves to the unpacked event.\n * @throws {Error} If the token is missing, invalid, or cannot be parsed.\n */\nexport async function unpackEventFromToken(token: string): Promise<Event> {\n  if (!token) {\n    throw new Error('Missing token')\n  }\n\n  token = token.replace(_authorizationScheme, '')\n\n  const eventB64 = utf8Decoder.decode(base64.decode(token))\n  if (!eventB64 || eventB64.length === 0 || !eventB64.startsWith('{')) {\n    throw new Error('Invalid token')\n  }\n\n  const event = JSON.parse(eventB64) as Event\n\n  return event\n}\n\n/**\n * Validates the timestamp of an event.\n * @param event - The event object to validate.\n * @returns A boolean indicating whether the event timestamp is within the last 60 seconds.\n */\nexport function validateEventTimestamp(event: Event): boolean {\n  if (!event.created_at) {\n    return false\n  }\n\n  return Math.round(new Date().getTime() / 1000) - event.created_at < 60\n}\n\n/**\n * Validates the kind of an event.\n * @param event The event to validate.\n * @returns A boolean indicating whether the event kind is valid.\n */\nexport function validateEventKind(event: Event): boolean {\n  return event.kind === HTTPAuth\n}\n\n/**\n * Validates if the given URL matches the URL tag of the event.\n * @param event - The event object.\n * @param url - The URL to validate.\n * @returns A boolean indicating whether the URL is valid or not.\n */\nexport function validateEventUrlTag(event: Event, url: string): boolean {\n  const urlTag = event.tags.find(t => t[0] === 'u')\n\n  if (!urlTag) {\n    return false\n  }\n\n  return urlTag.length > 0 && urlTag[1] === url\n}\n\n/**\n * Validates if the given event has a method tag that matches the specified method.\n * @param event - The event to validate.\n * @param method - The method to match against the method tag.\n * @returns A boolean indicating whether the event has a matching method tag.\n */\nexport function validateEventMethodTag(event: Event, method: string): boolean {\n  const methodTag = event.tags.find(t => t[0] === 'method')\n\n  if (!methodTag) {\n    return false\n  }\n\n  return methodTag.length > 0 && methodTag[1].toLowerCase() === method.toLowerCase()\n}\n\n/**\n * Calculates the hash of a payload.\n * @param payload - The payload to be hashed.\n * @returns The hash value as a string.\n */\nexport function hashPayload(payload: any): string {\n  const hash = sha256(utf8Encoder.encode(JSON.stringify(payload)))\n  return bytesToHex(hash)\n}\n\n/**\n * Validates the event payload tag against the provided payload.\n * @param event The event object.\n * @param payload The payload to validate.\n * @returns A boolean indicating whether the payload tag is valid.\n */\nexport function validateEventPayloadTag(event: Event, payload: any): boolean {\n  const payloadTag = event.tags.find(t => t[0] === 'payload')\n\n  if (!payloadTag) {\n    return false\n  }\n\n  const payloadHash = hashPayload(payload)\n  return payloadTag.length > 0 && payloadTag[1] === payloadHash\n}\n\n/**\n * Validates a Nostr event for the NIP-98 flow.\n *\n * @param event - The Nostr event to validate.\n * @param url - The URL associated with the event.\n * @param method - The HTTP method associated with the event.\n * @param body - The request body associated with the event (optional).\n * @returns A promise that resolves to a boolean indicating whether the event is valid.\n * @throws An error if the event is invalid.\n */\nexport async function validateEvent(event: Event, url: string, method: string, body?: any): Promise<boolean> {\n  if (!verifyEvent(event)) {\n    throw new Error('Invalid nostr event, signature invalid')\n  }\n\n  if (!validateEventKind(event)) {\n    throw new Error('Invalid nostr event, kind invalid')\n  }\n\n  if (!validateEventTimestamp(event)) {\n    throw new Error('Invalid nostr event, created_at timestamp invalid')\n  }\n\n  if (!validateEventUrlTag(event, url)) {\n    throw new Error('Invalid nostr event, url tag invalid')\n  }\n\n  if (!validateEventMethodTag(event, method)) {\n    throw new Error('Invalid nostr event, method tag invalid')\n  }\n\n  if (Boolean(body) && typeof body === 'object' && Object.keys(body).length > 0) {\n    if (!validateEventPayloadTag(event, body)) {\n      throw new Error('Invalid nostr event, payload tag does not match request body hash')\n    }\n  }\n\n  return true\n}\n", "import { schnorr } from '@noble/curves/secp256k1'\nimport { bytesToHex } from '@noble/hashes/utils'\nimport { Nostr, Event, EventTemplate, UnsignedEvent, VerifiedEvent, verifiedSymbol, validateEvent } from './core.ts'\nimport { sha256 } from '@noble/hashes/sha256'\n\nimport { utf8Encoder } from './utils.ts'\n\nclass JS implements Nostr {\n  generateSecretKey(): Uint8Array {\n    return schnorr.utils.randomPrivateKey()\n  }\n  getPublicKey(secretKey: Uint8Array): string {\n    return bytesToHex(schnorr.getPublicKey(secretKey))\n  }\n  finalizeEvent(t: EventTemplate, secretKey: Uint8Array): VerifiedEvent {\n    const event = t as VerifiedEvent\n    event.pubkey = bytesToHex(schnorr.getPublicKey(secretKey))\n    event.id = getEventHash(event)\n    event.sig = bytesToHex(schnorr.sign(getEventHash(event), secretKey))\n    event[verifiedSymbol] = true\n    return event\n  }\n  verifyEvent(event: Event): event is VerifiedEvent {\n    if (typeof event[verifiedSymbol] === 'boolean') return event[verifiedSymbol]\n\n    const hash = getEventHash(event)\n    if (hash !== event.id) {\n      event[verifiedSymbol] = false\n      return false\n    }\n\n    try {\n      const valid = schnorr.verify(event.sig, hash, event.pubkey)\n      event[verifiedSymbol] = valid\n      return valid\n    } catch (err) {\n      event[verifiedSymbol] = false\n      return false\n    }\n  }\n}\n\nexport function serializeEvent(evt: UnsignedEvent): string {\n  if (!validateEvent(evt)) throw new Error(\"can't serialize event with wrong or missing properties\")\n  return JSON.stringify([0, evt.pubkey, evt.created_at, evt.kind, evt.tags, evt.content])\n}\n\nexport function getEventHash(event: UnsignedEvent): string {\n  let eventHash = sha256(utf8Encoder.encode(serializeEvent(event)))\n  return bytesToHex(eventHash)\n}\n\nconst i: JS = new JS()\n\nexport const generateSecretKey = i.generateSecretKey\nexport const getPublicKey = i.getPublicKey\nexport const finalizeEvent = i.finalizeEvent\nexport const verifyEvent = i.verifyEvent\nexport * from './core.ts'\n", "export interface Nostr {\n  generateSecretKey(): Uint8Array\n  getPublicKey(secretKey: Uint8Array): string\n  finalizeEvent(event: EventTemplate, secretKey: Uint8Array): VerifiedEvent\n  verifyEvent(event: Event): event is VerifiedEvent\n}\n\n/** Designates a verified event signature. */\nexport const verifiedSymbol = Symbol('verified')\n\nexport type NostrEvent = {\n  kind: number\n  tags: string[][]\n  content: string\n  created_at: number\n  pubkey: string\n  id: string\n  sig: string\n  [verifiedSymbol]?: boolean\n}\n\nexport type Event = NostrEvent\nexport type EventTemplate = Pick<Event, 'kind' | 'tags' | 'content' | 'created_at'>\nexport type UnsignedEvent = Pick<Event, 'kind' | 'tags' | 'content' | 'created_at' | 'pubkey'>\n\n/** An event whose signature has been verified. */\nexport interface VerifiedEvent extends Event {\n  [verifiedSymbol]: true\n}\n\nconst isRecord = (obj: unknown): obj is Record<string, unknown> => obj instanceof Object\n\nexport function validateEvent<T>(event: T): event is T & UnsignedEvent {\n  if (!isRecord(event)) return false\n  if (typeof event.kind !== 'number') return false\n  if (typeof event.content !== 'string') return false\n  if (typeof event.created_at !== 'number') return false\n  if (typeof event.pubkey !== 'string') return false\n  if (!event.pubkey.match(/^[a-f0-9]{64}$/)) return false\n\n  if (!Array.isArray(event.tags)) return false\n  for (let i = 0; i < event.tags.length; i++) {\n    let tag = event.tags[i]\n    if (!Array.isArray(tag)) return false\n    for (let j = 0; j < tag.length; j++) {\n      if (typeof tag[j] !== 'string') return false\n    }\n  }\n\n  return true\n}\n\n/**\n * Sort events in reverse-chronological order by the `created_at` timestamp,\n * and then by the event `id` (lexicographically) in case of ties.\n * This mutates the array.\n */\nexport function sortEvents(events: Event[]): Event[] {\n  return events.sort((a: NostrEvent, b: NostrEvent): number => {\n    if (a.created_at !== b.created_at) {\n      return b.created_at - a.created_at\n    }\n    return a.id.localeCompare(b.id)\n  })\n}\n", "import type { Event } from './core.ts'\n\nexport const utf8Decoder: TextDecoder = new TextDecoder('utf-8')\nexport const utf8Encoder: TextEncoder = new TextEncoder()\n\nexport { bytesToHex, hexToBytes } from '@noble/hashes/utils'\n\nexport function normalizeURL(url: string): string {\n  try {\n    if (url.indexOf('://') === -1) url = 'wss://' + url\n    let p = new URL(url)\n    if (p.protocol === 'http:') p.protocol = 'ws:'\n    else if (p.protocol === 'https:') p.protocol = 'wss:'\n    p.pathname = p.pathname.replace(/\\/+/g, '/')\n    if (p.pathname.endsWith('/')) p.pathname = p.pathname.slice(0, -1)\n    if ((p.port === '80' && p.protocol === 'ws:') || (p.port === '443' && p.protocol === 'wss:')) p.port = ''\n    p.searchParams.sort()\n    p.hash = ''\n    return p.toString()\n  } catch (e) {\n    throw new Error(`Invalid URL: ${url}`)\n  }\n}\n\nexport function insertEventIntoDescendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return b.created_at - event.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function insertEventIntoAscendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return event.created_at - b.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function binarySearch<T>(arr: T[], compare: (b: T) => number): [number, boolean] {\n  let start = 0\n  let end = arr.length - 1\n\n  while (start <= end) {\n    const mid = Math.floor((start + end) / 2)\n    const cmp = compare(arr[mid])\n\n    if (cmp === 0) {\n      return [mid, true]\n    }\n\n    if (cmp < 0) {\n      end = mid - 1\n    } else {\n      start = mid + 1\n    }\n  }\n\n  return [start, false]\n}\n\nexport class QueueNode<V> {\n  public value: V\n  public next: QueueNode<V> | null = null\n  public prev: QueueNode<V> | null = null\n\n  constructor(message: V) {\n    this.value = message\n  }\n}\n\nexport class Queue<V> {\n  public first: QueueNode<V> | null\n  public last: QueueNode<V> | null\n\n  constructor() {\n    this.first = null\n    this.last = null\n  }\n\n  enqueue(value: V): boolean {\n    const newNode = new QueueNode(value)\n    if (!this.last) {\n      // list is empty\n      this.first = newNode\n      this.last = newNode\n    } else if (this.last === this.first) {\n      // list has a single element\n      this.last = newNode\n      this.last.prev = this.first\n      this.first.next = newNode\n    } else {\n      // list has elements, add as last\n      newNode.prev = this.last\n      this.last.next = newNode\n      this.last = newNode\n    }\n    return true\n  }\n\n  dequeue(): V | null {\n    if (!this.first) return null\n\n    if (this.first === this.last) {\n      const target = this.first\n      this.first = null\n      this.last = null\n      return target.value\n    }\n\n    const target = this.first\n    this.first = target.next\n    if (this.first) {\n      this.first.prev = null // fix: clean up prev pointer\n    }\n\n    return target.value\n  }\n}\n", "import { NostrEvent, validateEvent } from './pure.ts'\n\n/** Events are **regular**, which means they're all expected to be stored by relays. */\nexport function isRegularKind(kind: number): boolean {\n  return kind < 10000 && kind !== 0 && kind !== 3\n}\n\n/** Events are **replaceable**, which means that, for each combination of `pubkey` and `kind`, only the latest event is expected to (SHOULD) be stored by relays, older versions are expected to be discarded. */\nexport function isReplaceableKind(kind: number): boolean {\n  return kind === 0 || kind === 3 || (10000 <= kind && kind < 20000)\n}\n\n/** Events are **ephemeral**, which means they are not expected to be stored by relays. */\nexport function isEphemeralKind(kind: number): boolean {\n  return 20000 <= kind && kind < 30000\n}\n\n/** Events are **addressable**, which means that, for each combination of `pubkey`, `kind` and the `d` tag, only the latest event is expected to be stored by relays, older versions are expected to be discarded. */\nexport function isAddressableKind(kind: number): boolean {\n  return 30000 <= kind && kind < 40000\n}\n\n/** Classification of the event kind. */\nexport type KindClassification = 'regular' | 'replaceable' | 'ephemeral' | 'parameterized' | 'unknown'\n\n/** Determine the classification of this kind of event if known, or `unknown`. */\nexport function classifyKind(kind: number): KindClassification {\n  if (isRegularKind(kind)) return 'regular'\n  if (isReplaceableKind(kind)) return 'replaceable'\n  if (isEphemeralKind(kind)) return 'ephemeral'\n  if (isAddressableKind(kind)) return 'parameterized'\n  return 'unknown'\n}\n\nexport function isKind<T extends number>(event: unknown, kind: T | Array<T>): event is NostrEvent & { kind: T } {\n  const kindAsArray: number[] = kind instanceof Array ? kind : [kind]\n  return (validateEvent(event) && kindAsArray.includes(event.kind)) || false\n}\n\nexport const Metadata = 0\nexport type Metadata = typeof Metadata\nexport const ShortTextNote = 1\nexport type ShortTextNote = typeof ShortTextNote\nexport const RecommendRelay = 2\nexport type RecommendRelay = typeof RecommendRelay\nexport const Contacts = 3\nexport type Contacts = typeof Contacts\nexport const EncryptedDirectMessage = 4\nexport type EncryptedDirectMessage = typeof EncryptedDirectMessage\nexport const EventDeletion = 5\nexport type EventDeletion = typeof EventDeletion\nexport const Repost = 6\nexport type Repost = typeof Repost\nexport const Reaction = 7\nexport type Reaction = typeof Reaction\nexport const BadgeAward = 8\nexport type BadgeAward = typeof BadgeAward\nexport const ChatMessage = 9\nexport type ChatMessage = typeof ChatMessage\nexport const ForumThread = 11\nexport type ForumThread = typeof ForumThread\nexport const Seal = 13\nexport type Seal = typeof Seal\nexport const PrivateDirectMessage = 14\nexport type PrivateDirectMessage = typeof PrivateDirectMessage\nexport const FileMessage = 15\nexport type FileMessage = typeof FileMessage\nexport const GenericRepost = 16\nexport type GenericRepost = typeof GenericRepost\nexport const Photo = 20\nexport type Photo = typeof Photo\nexport const NormalVideo = 21\nexport type NormalVideo = typeof NormalVideo\nexport const ShortVideo = 22\nexport type ShortVideo = typeof ShortVideo\nexport const ChannelCreation = 40\nexport type ChannelCreation = typeof ChannelCreation\nexport const ChannelMetadata = 41\nexport type ChannelMetadata = typeof ChannelMetadata\nexport const ChannelMessage = 42\nexport type ChannelMessage = typeof ChannelMessage\nexport const ChannelHideMessage = 43\nexport type ChannelHideMessage = typeof ChannelHideMessage\nexport const ChannelMuteUser = 44\nexport type ChannelMuteUser = typeof ChannelMuteUser\nexport const OpenTimestamps = 1040\nexport type OpenTimestamps = typeof OpenTimestamps\nexport const GiftWrap = 1059\nexport type GiftWrap = typeof GiftWrap\nexport const Poll = 1068\nexport type Poll = typeof Poll\nexport const FileMetadata = 1063\nexport type FileMetadata = typeof FileMetadata\nexport const Comment = 1111\nexport type Comment = typeof Comment\nexport const LiveChatMessage = 1311\nexport type LiveChatMessage = typeof LiveChatMessage\nexport const Voice = 1222\nexport type Voice = typeof Voice\nexport const VoiceComment = 1244\nexport type VoiceComment = typeof VoiceComment\nexport const ProblemTracker = 1971\nexport type ProblemTracker = typeof ProblemTracker\nexport const Report = 1984\nexport type Report = typeof Report\nexport const Reporting = 1984\nexport type Reporting = typeof Reporting\nexport const Label = 1985\nexport type Label = typeof Label\nexport const CommunityPostApproval = 4550\nexport type CommunityPostApproval = typeof CommunityPostApproval\nexport const JobRequest = 5999\nexport type JobRequest = typeof JobRequest\nexport const JobResult = 6999\nexport type JobResult = typeof JobResult\nexport const JobFeedback = 7000\nexport type JobFeedback = typeof JobFeedback\nexport const ZapGoal = 9041\nexport type ZapGoal = typeof ZapGoal\nexport const ZapRequest = 9734\nexport type ZapRequest = typeof ZapRequest\nexport const Zap = 9735\nexport type Zap = typeof Zap\nexport const Highlights = 9802\nexport type Highlights = typeof Highlights\nexport const PollResponse = 1018\nexport type PollResponse = typeof PollResponse\nexport const Mutelist = 10000\nexport type Mutelist = typeof Mutelist\nexport const Pinlist = 10001\nexport type Pinlist = typeof Pinlist\nexport const RelayList = 10002\nexport type RelayList = typeof RelayList\nexport const BookmarkList = 10003\nexport type BookmarkList = typeof BookmarkList\nexport const CommunitiesList = 10004\nexport type CommunitiesList = typeof CommunitiesList\nexport const PublicChatsList = 10005\nexport type PublicChatsList = typeof PublicChatsList\nexport const BlockedRelaysList = 10006\nexport type BlockedRelaysList = typeof BlockedRelaysList\nexport const SearchRelaysList = 10007\nexport type SearchRelaysList = typeof SearchRelaysList\nexport const FavoriteRelays = 10012\nexport type FavoriteRelays = typeof FavoriteRelays\nexport const InterestsList = 10015\nexport type InterestsList = typeof InterestsList\nexport const UserEmojiList = 10030\nexport type UserEmojiList = typeof UserEmojiList\nexport const DirectMessageRelaysList = 10050\nexport type DirectMessageRelaysList = typeof DirectMessageRelaysList\nexport const FileServerPreference = 10096\nexport type FileServerPreference = typeof FileServerPreference\nexport const BlossomServerList = 10063\nexport type BlossomServerList = typeof BlossomServerList\nexport const NWCWalletInfo = 13194\nexport type NWCWalletInfo = typeof NWCWalletInfo\nexport const LightningPubRPC = 21000\nexport type LightningPubRPC = typeof LightningPubRPC\nexport const ClientAuth = 22242\nexport type ClientAuth = typeof ClientAuth\nexport const NWCWalletRequest = 23194\nexport type NWCWalletRequest = typeof NWCWalletRequest\nexport const NWCWalletResponse = 23195\nexport type NWCWalletResponse = typeof NWCWalletResponse\nexport const NostrConnect = 24133\nexport type NostrConnect = typeof NostrConnect\nexport const HTTPAuth = 27235\nexport type HTTPAuth = typeof HTTPAuth\nexport const Followsets = 30000\nexport type Followsets = typeof Followsets\nexport const Genericlists = 30001\nexport type Genericlists = typeof Genericlists\nexport const Relaysets = 30002\nexport type Relaysets = typeof Relaysets\nexport const Bookmarksets = 30003\nexport type Bookmarksets = typeof Bookmarksets\nexport const Curationsets = 30004\nexport type Curationsets = typeof Curationsets\nexport const ProfileBadges = 30008\nexport type ProfileBadges = typeof ProfileBadges\nexport const BadgeDefinition = 30009\nexport type BadgeDefinition = typeof BadgeDefinition\nexport const Interestsets = 30015\nexport type Interestsets = typeof Interestsets\nexport const CreateOrUpdateStall = 30017\nexport type CreateOrUpdateStall = typeof CreateOrUpdateStall\nexport const CreateOrUpdateProduct = 30018\nexport type CreateOrUpdateProduct = typeof CreateOrUpdateProduct\nexport const LongFormArticle = 30023\nexport type LongFormArticle = typeof LongFormArticle\nexport const DraftLong = 30024\nexport type DraftLong = typeof DraftLong\nexport const Emojisets = 30030\nexport type Emojisets = typeof Emojisets\nexport const Application = 30078\nexport type Application = typeof Application\nexport const LiveEvent = 30311\nexport type LiveEvent = typeof LiveEvent\nexport const UserStatuses = 30315\nexport type UserStatuses = typeof UserStatuses\nexport const ClassifiedListing = 30402\nexport type ClassifiedListing = typeof ClassifiedListing\nexport const DraftClassifiedListing = 30403\nexport type DraftClassifiedListing = typeof DraftClassifiedListing\nexport const Date = 31922\nexport type Date = typeof Date\nexport const Time = 31923\nexport type Time = typeof Time\nexport const Calendar = 31924\nexport type Calendar = typeof Calendar\nexport const CalendarEventRSVP = 31925\nexport type CalendarEventRSVP = typeof CalendarEventRSVP\nexport const RelayReview = 31987\nexport type RelayReview = typeof RelayReview\nexport const Handlerrecommendation = 31989\nexport type Handlerrecommendation = typeof Handlerrecommendation\nexport const Handlerinformation = 31990\nexport type Handlerinformation = typeof Handlerinformation\nexport const CommunityDefinition = 34550\nexport type CommunityDefinition = typeof CommunityDefinition\nexport const GroupMetadata = 39000\nexport type GroupMetadata = typeof GroupMetadata\n"],
-  "mappings": "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",
-  "names": ["validateEvent", "import_sha256", "import_utils", "import_utils", "i", "validateEvent"]
+  "sourcesContent": ["import { sha256 } from '@noble/hashes/sha2.js'\nimport { bytesToHex } from '@noble/hashes/utils.js'\nimport { base64 } from '@scure/base'\n\nimport { HTTPAuth } from './kinds.ts'\nimport { Event, EventTemplate, verifyEvent } from './pure.ts'\nimport { utf8Decoder, utf8Encoder } from './utils.ts'\n\nconst _authorizationScheme = 'Nostr '\n\n/**\n * Generate token for NIP-98 flow.\n *\n * @example\n * const sign = window.nostr.signEvent\n * await nip98.getToken('https://example.com/login', 'post', (e) => sign(e), true)\n */\nexport async function getToken(\n  loginUrl: string,\n  httpMethod: string,\n  sign: (e: EventTemplate) => Promise<Event> | Event,\n  includeAuthorizationScheme: boolean = false,\n  payload?: Record<string, any>,\n): Promise<string> {\n  const event: EventTemplate = {\n    kind: HTTPAuth,\n    tags: [\n      ['u', loginUrl],\n      ['method', httpMethod],\n    ],\n    created_at: Math.round(new Date().getTime() / 1000),\n    content: '',\n  }\n\n  if (payload) {\n    event.tags.push(['payload', hashPayload(payload)])\n  }\n\n  const signedEvent = await sign(event)\n  const authorizationScheme = includeAuthorizationScheme ? _authorizationScheme : ''\n\n  return authorizationScheme + base64.encode(utf8Encoder.encode(JSON.stringify(signedEvent)))\n}\n\n/**\n * Validate token for NIP-98 flow.\n *\n * @example\n * await nip98.validateToken('Nostr base64token', 'https://example.com/login', 'post')\n */\nexport async function validateToken(token: string, url: string, method: string): Promise<boolean> {\n  const event = await unpackEventFromToken(token).catch(error => {\n    throw error\n  })\n\n  const valid = await validateEvent(event, url, method).catch(error => {\n    throw error\n  })\n\n  return valid\n}\n\n/**\n * Unpacks an event from a token.\n *\n * @param token - The token to unpack.\n * @returns A promise that resolves to the unpacked event.\n * @throws {Error} If the token is missing, invalid, or cannot be parsed.\n */\nexport async function unpackEventFromToken(token: string): Promise<Event> {\n  if (!token) {\n    throw new Error('Missing token')\n  }\n\n  token = token.replace(_authorizationScheme, '')\n\n  const eventB64 = utf8Decoder.decode(base64.decode(token))\n  if (!eventB64 || eventB64.length === 0 || !eventB64.startsWith('{')) {\n    throw new Error('Invalid token')\n  }\n\n  const event = JSON.parse(eventB64) as Event\n\n  return event\n}\n\n/**\n * Validates the timestamp of an event.\n * @param event - The event object to validate.\n * @returns A boolean indicating whether the event timestamp is within the last 60 seconds.\n */\nexport function validateEventTimestamp(event: Event): boolean {\n  if (!event.created_at) {\n    return false\n  }\n\n  return Math.round(new Date().getTime() / 1000) - event.created_at < 60\n}\n\n/**\n * Validates the kind of an event.\n * @param event The event to validate.\n * @returns A boolean indicating whether the event kind is valid.\n */\nexport function validateEventKind(event: Event): boolean {\n  return event.kind === HTTPAuth\n}\n\n/**\n * Validates if the given URL matches the URL tag of the event.\n * @param event - The event object.\n * @param url - The URL to validate.\n * @returns A boolean indicating whether the URL is valid or not.\n */\nexport function validateEventUrlTag(event: Event, url: string): boolean {\n  const urlTag = event.tags.find(t => t[0] === 'u')\n\n  if (!urlTag) {\n    return false\n  }\n\n  return urlTag.length > 0 && urlTag[1] === url\n}\n\n/**\n * Validates if the given event has a method tag that matches the specified method.\n * @param event - The event to validate.\n * @param method - The method to match against the method tag.\n * @returns A boolean indicating whether the event has a matching method tag.\n */\nexport function validateEventMethodTag(event: Event, method: string): boolean {\n  const methodTag = event.tags.find(t => t[0] === 'method')\n\n  if (!methodTag) {\n    return false\n  }\n\n  return methodTag.length > 0 && methodTag[1].toLowerCase() === method.toLowerCase()\n}\n\n/**\n * Calculates the hash of a payload.\n * @param payload - The payload to be hashed.\n * @returns The hash value as a string.\n */\nexport function hashPayload(payload: any): string {\n  const hash = sha256(utf8Encoder.encode(JSON.stringify(payload)))\n  return bytesToHex(hash)\n}\n\n/**\n * Validates the event payload tag against the provided payload.\n * @param event The event object.\n * @param payload The payload to validate.\n * @returns A boolean indicating whether the payload tag is valid.\n */\nexport function validateEventPayloadTag(event: Event, payload: any): boolean {\n  const payloadTag = event.tags.find(t => t[0] === 'payload')\n\n  if (!payloadTag) {\n    return false\n  }\n\n  const payloadHash = hashPayload(payload)\n  return payloadTag.length > 0 && payloadTag[1] === payloadHash\n}\n\n/**\n * Validates a Nostr event for the NIP-98 flow.\n *\n * @param event - The Nostr event to validate.\n * @param url - The URL associated with the event.\n * @param method - The HTTP method associated with the event.\n * @param body - The request body associated with the event (optional).\n * @returns A promise that resolves to a boolean indicating whether the event is valid.\n * @throws An error if the event is invalid.\n */\nexport async function validateEvent(event: Event, url: string, method: string, body?: any): Promise<boolean> {\n  if (!verifyEvent(event)) {\n    throw new Error('Invalid nostr event, signature invalid')\n  }\n\n  if (!validateEventKind(event)) {\n    throw new Error('Invalid nostr event, kind invalid')\n  }\n\n  if (!validateEventTimestamp(event)) {\n    throw new Error('Invalid nostr event, created_at timestamp invalid')\n  }\n\n  if (!validateEventUrlTag(event, url)) {\n    throw new Error('Invalid nostr event, url tag invalid')\n  }\n\n  if (!validateEventMethodTag(event, method)) {\n    throw new Error('Invalid nostr event, method tag invalid')\n  }\n\n  if (Boolean(body) && typeof body === 'object' && Object.keys(body).length > 0) {\n    if (!validateEventPayloadTag(event, body)) {\n      throw new Error('Invalid nostr event, payload tag does not match request body hash')\n    }\n  }\n\n  return true\n}\n", "import { schnorr } from '@noble/curves/secp256k1.js'\nimport { bytesToHex, hexToBytes } from '@noble/hashes/utils.js'\nimport { Nostr, Event, EventTemplate, UnsignedEvent, VerifiedEvent, verifiedSymbol, validateEvent } from './core.ts'\nimport { sha256 } from '@noble/hashes/sha2.js'\n\nimport { utf8Encoder } from './utils.ts'\n\nclass JS implements Nostr {\n  generateSecretKey(): Uint8Array {\n    return schnorr.utils.randomSecretKey()\n  }\n  getPublicKey(secretKey: Uint8Array): string {\n    return bytesToHex(schnorr.getPublicKey(secretKey))\n  }\n  finalizeEvent(t: EventTemplate, secretKey: Uint8Array): VerifiedEvent {\n    const event = t as VerifiedEvent\n    event.pubkey = bytesToHex(schnorr.getPublicKey(secretKey))\n    event.id = getEventHash(event)\n    event.sig = bytesToHex(schnorr.sign(hexToBytes(getEventHash(event)), secretKey))\n    event[verifiedSymbol] = true\n    return event\n  }\n  verifyEvent(event: Event): event is VerifiedEvent {\n    if (typeof event[verifiedSymbol] === 'boolean') return event[verifiedSymbol]\n\n    const hash = getEventHash(event)\n    if (hash !== event.id) {\n      event[verifiedSymbol] = false\n      return false\n    }\n\n    try {\n      const valid = schnorr.verify(hexToBytes(event.sig), hexToBytes(hash), hexToBytes(event.pubkey))\n      event[verifiedSymbol] = valid\n      return valid\n    } catch (err) {\n      event[verifiedSymbol] = false\n      return false\n    }\n  }\n}\n\nexport function serializeEvent(evt: UnsignedEvent): string {\n  if (!validateEvent(evt)) throw new Error(\"can't serialize event with wrong or missing properties\")\n  return JSON.stringify([0, evt.pubkey, evt.created_at, evt.kind, evt.tags, evt.content])\n}\n\nexport function getEventHash(event: UnsignedEvent): string {\n  let eventHash = sha256(utf8Encoder.encode(serializeEvent(event)))\n  return bytesToHex(eventHash)\n}\n\nconst i: JS = new JS()\n\nexport const generateSecretKey = i.generateSecretKey\nexport const getPublicKey = i.getPublicKey\nexport const finalizeEvent = i.finalizeEvent\nexport const verifyEvent = i.verifyEvent\nexport * from './core.ts'\n", "export interface Nostr {\n  generateSecretKey(): Uint8Array\n  getPublicKey(secretKey: Uint8Array): string\n  finalizeEvent(event: EventTemplate, secretKey: Uint8Array): VerifiedEvent\n  verifyEvent(event: Event): event is VerifiedEvent\n}\n\n/** Designates a verified event signature. */\nexport const verifiedSymbol = Symbol('verified')\n\nexport type NostrEvent = {\n  kind: number\n  tags: string[][]\n  content: string\n  created_at: number\n  pubkey: string\n  id: string\n  sig: string\n  [verifiedSymbol]?: boolean\n}\n\nexport type Event = NostrEvent\nexport type EventTemplate = Pick<Event, 'kind' | 'tags' | 'content' | 'created_at'>\nexport type UnsignedEvent = Pick<Event, 'kind' | 'tags' | 'content' | 'created_at' | 'pubkey'>\n\n/** An event whose signature has been verified. */\nexport interface VerifiedEvent extends Event {\n  [verifiedSymbol]: true\n}\n\nconst isRecord = (obj: unknown): obj is Record<string, unknown> => obj instanceof Object\n\nexport function validateEvent<T>(event: T): event is T & UnsignedEvent {\n  if (!isRecord(event)) return false\n  if (typeof event.kind !== 'number') return false\n  if (typeof event.content !== 'string') return false\n  if (typeof event.created_at !== 'number') return false\n  if (typeof event.pubkey !== 'string') return false\n  if (!event.pubkey.match(/^[a-f0-9]{64}$/)) return false\n\n  if (!Array.isArray(event.tags)) return false\n  for (let i = 0; i < event.tags.length; i++) {\n    let tag = event.tags[i]\n    if (!Array.isArray(tag)) return false\n    for (let j = 0; j < tag.length; j++) {\n      if (typeof tag[j] !== 'string') return false\n    }\n  }\n\n  return true\n}\n\n/**\n * Sort events in reverse-chronological order by the `created_at` timestamp,\n * and then by the event `id` (lexicographically) in case of ties.\n * This mutates the array.\n */\nexport function sortEvents(events: Event[]): Event[] {\n  return events.sort((a: NostrEvent, b: NostrEvent): number => {\n    if (a.created_at !== b.created_at) {\n      return b.created_at - a.created_at\n    }\n    return a.id.localeCompare(b.id)\n  })\n}\n", "import type { Event } from './core.ts'\n\nexport const utf8Decoder: TextDecoder = new TextDecoder('utf-8')\nexport const utf8Encoder: TextEncoder = new TextEncoder()\n\nexport { bytesToHex, hexToBytes } from '@noble/hashes/utils.js'\n\nexport function normalizeURL(url: string): string {\n  try {\n    if (url.indexOf('://') === -1) url = 'wss://' + url\n    let p = new URL(url)\n    if (p.protocol === 'http:') p.protocol = 'ws:'\n    else if (p.protocol === 'https:') p.protocol = 'wss:'\n    p.pathname = p.pathname.replace(/\\/+/g, '/')\n    if (p.pathname.endsWith('/')) p.pathname = p.pathname.slice(0, -1)\n    if ((p.port === '80' && p.protocol === 'ws:') || (p.port === '443' && p.protocol === 'wss:')) p.port = ''\n    p.searchParams.sort()\n    p.hash = ''\n    return p.toString()\n  } catch (e) {\n    throw new Error(`Invalid URL: ${url}`)\n  }\n}\n\nexport function insertEventIntoDescendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return b.created_at - event.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function insertEventIntoAscendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return event.created_at - b.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function binarySearch<T>(arr: T[], compare: (b: T) => number): [number, boolean] {\n  let start = 0\n  let end = arr.length - 1\n\n  while (start <= end) {\n    const mid = Math.floor((start + end) / 2)\n    const cmp = compare(arr[mid])\n\n    if (cmp === 0) {\n      return [mid, true]\n    }\n\n    if (cmp < 0) {\n      end = mid - 1\n    } else {\n      start = mid + 1\n    }\n  }\n\n  return [start, false]\n}\n\nexport class QueueNode<V> {\n  public value: V\n  public next: QueueNode<V> | null = null\n  public prev: QueueNode<V> | null = null\n\n  constructor(message: V) {\n    this.value = message\n  }\n}\n\nexport class Queue<V> {\n  public first: QueueNode<V> | null\n  public last: QueueNode<V> | null\n\n  constructor() {\n    this.first = null\n    this.last = null\n  }\n\n  enqueue(value: V): boolean {\n    const newNode = new QueueNode(value)\n    if (!this.last) {\n      // list is empty\n      this.first = newNode\n      this.last = newNode\n    } else if (this.last === this.first) {\n      // list has a single element\n      this.last = newNode\n      this.last.prev = this.first\n      this.first.next = newNode\n    } else {\n      // list has elements, add as last\n      newNode.prev = this.last\n      this.last.next = newNode\n      this.last = newNode\n    }\n    return true\n  }\n\n  dequeue(): V | null {\n    if (!this.first) return null\n\n    if (this.first === this.last) {\n      const target = this.first\n      this.first = null\n      this.last = null\n      return target.value\n    }\n\n    const target = this.first\n    this.first = target.next\n    if (this.first) {\n      this.first.prev = null // fix: clean up prev pointer\n    }\n\n    return target.value\n  }\n}\n", "import { NostrEvent, validateEvent } from './pure.ts'\n\n/** Events are **regular**, which means they're all expected to be stored by relays. */\nexport function isRegularKind(kind: number): boolean {\n  return kind < 10000 && kind !== 0 && kind !== 3\n}\n\n/** Events are **replaceable**, which means that, for each combination of `pubkey` and `kind`, only the latest event is expected to (SHOULD) be stored by relays, older versions are expected to be discarded. */\nexport function isReplaceableKind(kind: number): boolean {\n  return kind === 0 || kind === 3 || (10000 <= kind && kind < 20000)\n}\n\n/** Events are **ephemeral**, which means they are not expected to be stored by relays. */\nexport function isEphemeralKind(kind: number): boolean {\n  return 20000 <= kind && kind < 30000\n}\n\n/** Events are **addressable**, which means that, for each combination of `pubkey`, `kind` and the `d` tag, only the latest event is expected to be stored by relays, older versions are expected to be discarded. */\nexport function isAddressableKind(kind: number): boolean {\n  return 30000 <= kind && kind < 40000\n}\n\n/** Classification of the event kind. */\nexport type KindClassification = 'regular' | 'replaceable' | 'ephemeral' | 'parameterized' | 'unknown'\n\n/** Determine the classification of this kind of event if known, or `unknown`. */\nexport function classifyKind(kind: number): KindClassification {\n  if (isRegularKind(kind)) return 'regular'\n  if (isReplaceableKind(kind)) return 'replaceable'\n  if (isEphemeralKind(kind)) return 'ephemeral'\n  if (isAddressableKind(kind)) return 'parameterized'\n  return 'unknown'\n}\n\nexport function isKind<T extends number>(event: unknown, kind: T | Array<T>): event is NostrEvent & { kind: T } {\n  const kindAsArray: number[] = kind instanceof Array ? kind : [kind]\n  return (validateEvent(event) && kindAsArray.includes(event.kind)) || false\n}\n\nexport const Metadata = 0\nexport type Metadata = typeof Metadata\nexport const ShortTextNote = 1\nexport type ShortTextNote = typeof ShortTextNote\nexport const RecommendRelay = 2\nexport type RecommendRelay = typeof RecommendRelay\nexport const Contacts = 3\nexport type Contacts = typeof Contacts\nexport const EncryptedDirectMessage = 4\nexport type EncryptedDirectMessage = typeof EncryptedDirectMessage\nexport const EventDeletion = 5\nexport type EventDeletion = typeof EventDeletion\nexport const Repost = 6\nexport type Repost = typeof Repost\nexport const Reaction = 7\nexport type Reaction = typeof Reaction\nexport const BadgeAward = 8\nexport type BadgeAward = typeof BadgeAward\nexport const ChatMessage = 9\nexport type ChatMessage = typeof ChatMessage\nexport const ForumThread = 11\nexport type ForumThread = typeof ForumThread\nexport const Seal = 13\nexport type Seal = typeof Seal\nexport const PrivateDirectMessage = 14\nexport type PrivateDirectMessage = typeof PrivateDirectMessage\nexport const FileMessage = 15\nexport type FileMessage = typeof FileMessage\nexport const GenericRepost = 16\nexport type GenericRepost = typeof GenericRepost\nexport const Photo = 20\nexport type Photo = typeof Photo\nexport const NormalVideo = 21\nexport type NormalVideo = typeof NormalVideo\nexport const ShortVideo = 22\nexport type ShortVideo = typeof ShortVideo\nexport const ChannelCreation = 40\nexport type ChannelCreation = typeof ChannelCreation\nexport const ChannelMetadata = 41\nexport type ChannelMetadata = typeof ChannelMetadata\nexport const ChannelMessage = 42\nexport type ChannelMessage = typeof ChannelMessage\nexport const ChannelHideMessage = 43\nexport type ChannelHideMessage = typeof ChannelHideMessage\nexport const ChannelMuteUser = 44\nexport type ChannelMuteUser = typeof ChannelMuteUser\nexport const OpenTimestamps = 1040\nexport type OpenTimestamps = typeof OpenTimestamps\nexport const GiftWrap = 1059\nexport type GiftWrap = typeof GiftWrap\nexport const Poll = 1068\nexport type Poll = typeof Poll\nexport const FileMetadata = 1063\nexport type FileMetadata = typeof FileMetadata\nexport const Comment = 1111\nexport type Comment = typeof Comment\nexport const LiveChatMessage = 1311\nexport type LiveChatMessage = typeof LiveChatMessage\nexport const Voice = 1222\nexport type Voice = typeof Voice\nexport const VoiceComment = 1244\nexport type VoiceComment = typeof VoiceComment\nexport const ProblemTracker = 1971\nexport type ProblemTracker = typeof ProblemTracker\nexport const Report = 1984\nexport type Report = typeof Report\nexport const Reporting = 1984\nexport type Reporting = typeof Reporting\nexport const Label = 1985\nexport type Label = typeof Label\nexport const CommunityPostApproval = 4550\nexport type CommunityPostApproval = typeof CommunityPostApproval\nexport const JobRequest = 5999\nexport type JobRequest = typeof JobRequest\nexport const JobResult = 6999\nexport type JobResult = typeof JobResult\nexport const JobFeedback = 7000\nexport type JobFeedback = typeof JobFeedback\nexport const ZapGoal = 9041\nexport type ZapGoal = typeof ZapGoal\nexport const ZapRequest = 9734\nexport type ZapRequest = typeof ZapRequest\nexport const Zap = 9735\nexport type Zap = typeof Zap\nexport const Highlights = 9802\nexport type Highlights = typeof Highlights\nexport const PollResponse = 1018\nexport type PollResponse = typeof PollResponse\nexport const Mutelist = 10000\nexport type Mutelist = typeof Mutelist\nexport const Pinlist = 10001\nexport type Pinlist = typeof Pinlist\nexport const RelayList = 10002\nexport type RelayList = typeof RelayList\nexport const BookmarkList = 10003\nexport type BookmarkList = typeof BookmarkList\nexport const CommunitiesList = 10004\nexport type CommunitiesList = typeof CommunitiesList\nexport const PublicChatsList = 10005\nexport type PublicChatsList = typeof PublicChatsList\nexport const BlockedRelaysList = 10006\nexport type BlockedRelaysList = typeof BlockedRelaysList\nexport const SearchRelaysList = 10007\nexport type SearchRelaysList = typeof SearchRelaysList\nexport const FavoriteRelays = 10012\nexport type FavoriteRelays = typeof FavoriteRelays\nexport const InterestsList = 10015\nexport type InterestsList = typeof InterestsList\nexport const UserEmojiList = 10030\nexport type UserEmojiList = typeof UserEmojiList\nexport const DirectMessageRelaysList = 10050\nexport type DirectMessageRelaysList = typeof DirectMessageRelaysList\nexport const FileServerPreference = 10096\nexport type FileServerPreference = typeof FileServerPreference\nexport const BlossomServerList = 10063\nexport type BlossomServerList = typeof BlossomServerList\nexport const NWCWalletInfo = 13194\nexport type NWCWalletInfo = typeof NWCWalletInfo\nexport const LightningPubRPC = 21000\nexport type LightningPubRPC = typeof LightningPubRPC\nexport const ClientAuth = 22242\nexport type ClientAuth = typeof ClientAuth\nexport const NWCWalletRequest = 23194\nexport type NWCWalletRequest = typeof NWCWalletRequest\nexport const NWCWalletResponse = 23195\nexport type NWCWalletResponse = typeof NWCWalletResponse\nexport const NostrConnect = 24133\nexport type NostrConnect = typeof NostrConnect\nexport const HTTPAuth = 27235\nexport type HTTPAuth = typeof HTTPAuth\nexport const Followsets = 30000\nexport type Followsets = typeof Followsets\nexport const Genericlists = 30001\nexport type Genericlists = typeof Genericlists\nexport const Relaysets = 30002\nexport type Relaysets = typeof Relaysets\nexport const Bookmarksets = 30003\nexport type Bookmarksets = typeof Bookmarksets\nexport const Curationsets = 30004\nexport type Curationsets = typeof Curationsets\nexport const ProfileBadges = 30008\nexport type ProfileBadges = typeof ProfileBadges\nexport const BadgeDefinition = 30009\nexport type BadgeDefinition = typeof BadgeDefinition\nexport const Interestsets = 30015\nexport type Interestsets = typeof Interestsets\nexport const CreateOrUpdateStall = 30017\nexport type CreateOrUpdateStall = typeof CreateOrUpdateStall\nexport const CreateOrUpdateProduct = 30018\nexport type CreateOrUpdateProduct = typeof CreateOrUpdateProduct\nexport const LongFormArticle = 30023\nexport type LongFormArticle = typeof LongFormArticle\nexport const DraftLong = 30024\nexport type DraftLong = typeof DraftLong\nexport const Emojisets = 30030\nexport type Emojisets = typeof Emojisets\nexport const Application = 30078\nexport type Application = typeof Application\nexport const LiveEvent = 30311\nexport type LiveEvent = typeof LiveEvent\nexport const UserStatuses = 30315\nexport type UserStatuses = typeof UserStatuses\nexport const ClassifiedListing = 30402\nexport type ClassifiedListing = typeof ClassifiedListing\nexport const DraftClassifiedListing = 30403\nexport type DraftClassifiedListing = typeof DraftClassifiedListing\nexport const Date = 31922\nexport type Date = typeof Date\nexport const Time = 31923\nexport type Time = typeof Time\nexport const Calendar = 31924\nexport type Calendar = typeof Calendar\nexport const CalendarEventRSVP = 31925\nexport type CalendarEventRSVP = typeof CalendarEventRSVP\nexport const RelayReview = 31987\nexport type RelayReview = typeof RelayReview\nexport const Handlerrecommendation = 31989\nexport type Handlerrecommendation = typeof Handlerrecommendation\nexport const Handlerinformation = 31990\nexport type Handlerinformation = typeof Handlerinformation\nexport const CommunityDefinition = 34550\nexport type CommunityDefinition = typeof CommunityDefinition\nexport const GroupMetadata = 39000\nexport type GroupMetadata = typeof GroupMetadata\n"],
+  "mappings": ";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,uBAAAA;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAAAC,eAAuB;AACvB,IAAAC,gBAA2B;AAC3B,kBAAuB;;;ACFvB,uBAAwB;AACxB,IAAAC,gBAAuC;;;ACOhC,IAAM,iBAAiB,OAAO,UAAU;AAsB/C,IAAM,WAAW,CAAC,QAAiD,eAAe;AAE3E,SAAS,cAAiB,OAAsC;AACrE,MAAI,CAAC,SAAS,KAAK;AAAG,WAAO;AAC7B,MAAI,OAAO,MAAM,SAAS;AAAU,WAAO;AAC3C,MAAI,OAAO,MAAM,YAAY;AAAU,WAAO;AAC9C,MAAI,OAAO,MAAM,eAAe;AAAU,WAAO;AACjD,MAAI,OAAO,MAAM,WAAW;AAAU,WAAO;AAC7C,MAAI,CAAC,MAAM,OAAO,MAAM,gBAAgB;AAAG,WAAO;AAElD,MAAI,CAAC,MAAM,QAAQ,MAAM,IAAI;AAAG,WAAO;AACvC,WAASC,KAAI,GAAGA,KAAI,MAAM,KAAK,QAAQA,MAAK;AAC1C,QAAI,MAAM,MAAM,KAAKA;AACrB,QAAI,CAAC,MAAM,QAAQ,GAAG;AAAG,aAAO;AAChC,aAAS,IAAI,GAAG,IAAI,IAAI,QAAQ,KAAK;AACnC,UAAI,OAAO,IAAI,OAAO;AAAU,eAAO;AAAA,IACzC;AAAA,EACF;AAEA,SAAO;AACT;;;AD/CA,kBAAuB;;;AEEvB,mBAAuC;AAHhC,IAAM,cAA2B,IAAI,YAAY,OAAO;AACxD,IAAM,cAA2B,IAAI,YAAY;;;AFIxD,IAAM,KAAN,MAA0B;AAAA,EACxB,oBAAgC;AAC9B,WAAO,yBAAQ,MAAM,gBAAgB;AAAA,EACvC;AAAA,EACA,aAAa,WAA+B;AAC1C,eAAO,0BAAW,yBAAQ,aAAa,SAAS,CAAC;AAAA,EACnD;AAAA,EACA,cAAc,GAAkB,WAAsC;AACpE,UAAM,QAAQ;AACd,UAAM,aAAS,0BAAW,yBAAQ,aAAa,SAAS,CAAC;AACzD,UAAM,KAAK,aAAa,KAAK;AAC7B,UAAM,UAAM,0BAAW,yBAAQ,SAAK,0BAAW,aAAa,KAAK,CAAC,GAAG,SAAS,CAAC;AAC/E,UAAM,kBAAkB;AACxB,WAAO;AAAA,EACT;AAAA,EACA,YAAY,OAAsC;AAChD,QAAI,OAAO,MAAM,oBAAoB;AAAW,aAAO,MAAM;AAE7D,UAAM,OAAO,aAAa,KAAK;AAC/B,QAAI,SAAS,MAAM,IAAI;AACrB,YAAM,kBAAkB;AACxB,aAAO;AAAA,IACT;AAEA,QAAI;AACF,YAAM,QAAQ,yBAAQ,WAAO,0BAAW,MAAM,GAAG,OAAG,0BAAW,IAAI,OAAG,0BAAW,MAAM,MAAM,CAAC;AAC9F,YAAM,kBAAkB;AACxB,aAAO;AAAA,IACT,SAAS,KAAP;AACA,YAAM,kBAAkB;AACxB,aAAO;AAAA,IACT;AAAA,EACF;AACF;AAEO,SAAS,eAAe,KAA4B;AACzD,MAAI,CAAC,cAAc,GAAG;AAAG,UAAM,IAAI,MAAM,wDAAwD;AACjG,SAAO,KAAK,UAAU,CAAC,GAAG,IAAI,QAAQ,IAAI,YAAY,IAAI,MAAM,IAAI,MAAM,IAAI,OAAO,CAAC;AACxF;AAEO,SAAS,aAAa,OAA8B;AACzD,MAAI,gBAAY,oBAAO,YAAY,OAAO,eAAe,KAAK,CAAC,CAAC;AAChE,aAAO,0BAAW,SAAS;AAC7B;AAEA,IAAM,IAAQ,IAAI,GAAG;AAEd,IAAM,oBAAoB,EAAE;AAC5B,IAAM,eAAe,EAAE;AACvB,IAAM,gBAAgB,EAAE;AACxB,IAAM,cAAc,EAAE;;;AG8GtB,IAAM,WAAW;;;AJ/JxB,IAAM,uBAAuB;AAS7B,eAAsB,SACpB,UACA,YACA,MACA,6BAAsC,OACtC,SACiB;AACjB,QAAM,QAAuB;AAAA,IAC3B,MAAM;AAAA,IACN,MAAM;AAAA,MACJ,CAAC,KAAK,QAAQ;AAAA,MACd,CAAC,UAAU,UAAU;AAAA,IACvB;AAAA,IACA,YAAY,KAAK,MAAM,IAAI,KAAK,EAAE,QAAQ,IAAI,GAAI;AAAA,IAClD,SAAS;AAAA,EACX;AAEA,MAAI,SAAS;AACX,UAAM,KAAK,KAAK,CAAC,WAAW,YAAY,OAAO,CAAC,CAAC;AAAA,EACnD;AAEA,QAAM,cAAc,MAAM,KAAK,KAAK;AACpC,QAAM,sBAAsB,6BAA6B,uBAAuB;AAEhF,SAAO,sBAAsB,mBAAO,OAAO,YAAY,OAAO,KAAK,UAAU,WAAW,CAAC,CAAC;AAC5F;AAQA,eAAsB,cAAc,OAAe,KAAa,QAAkC;AAChG,QAAM,QAAQ,MAAM,qBAAqB,KAAK,EAAE,MAAM,WAAS;AAC7D,UAAM;AAAA,EACR,CAAC;AAED,QAAM,QAAQ,MAAMC,eAAc,OAAO,KAAK,MAAM,EAAE,MAAM,WAAS;AACnE,UAAM;AAAA,EACR,CAAC;AAED,SAAO;AACT;AASA,eAAsB,qBAAqB,OAA+B;AACxE,MAAI,CAAC,OAAO;AACV,UAAM,IAAI,MAAM,eAAe;AAAA,EACjC;AAEA,UAAQ,MAAM,QAAQ,sBAAsB,EAAE;AAE9C,QAAM,WAAW,YAAY,OAAO,mBAAO,OAAO,KAAK,CAAC;AACxD,MAAI,CAAC,YAAY,SAAS,WAAW,KAAK,CAAC,SAAS,WAAW,GAAG,GAAG;AACnE,UAAM,IAAI,MAAM,eAAe;AAAA,EACjC;AAEA,QAAM,QAAQ,KAAK,MAAM,QAAQ;AAEjC,SAAO;AACT;AAOO,SAAS,uBAAuB,OAAuB;AAC5D,MAAI,CAAC,MAAM,YAAY;AACrB,WAAO;AAAA,EACT;AAEA,SAAO,KAAK,MAAM,IAAI,KAAK,EAAE,QAAQ,IAAI,GAAI,IAAI,MAAM,aAAa;AACtE;AAOO,SAAS,kBAAkB,OAAuB;AACvD,SAAO,MAAM,SAAS;AACxB;AAQO,SAAS,oBAAoB,OAAc,KAAsB;AACtE,QAAM,SAAS,MAAM,KAAK,KAAK,OAAK,EAAE,OAAO,GAAG;AAEhD,MAAI,CAAC,QAAQ;AACX,WAAO;AAAA,EACT;AAEA,SAAO,OAAO,SAAS,KAAK,OAAO,OAAO;AAC5C;AAQO,SAAS,uBAAuB,OAAc,QAAyB;AAC5E,QAAM,YAAY,MAAM,KAAK,KAAK,OAAK,EAAE,OAAO,QAAQ;AAExD,MAAI,CAAC,WAAW;AACd,WAAO;AAAA,EACT;AAEA,SAAO,UAAU,SAAS,KAAK,UAAU,GAAG,YAAY,MAAM,OAAO,YAAY;AACnF;AAOO,SAAS,YAAY,SAAsB;AAChD,QAAM,WAAO,qBAAO,YAAY,OAAO,KAAK,UAAU,OAAO,CAAC,CAAC;AAC/D,aAAO,0BAAW,IAAI;AACxB;AAQO,SAAS,wBAAwB,OAAc,SAAuB;AAC3E,QAAM,aAAa,MAAM,KAAK,KAAK,OAAK,EAAE,OAAO,SAAS;AAE1D,MAAI,CAAC,YAAY;AACf,WAAO;AAAA,EACT;AAEA,QAAM,cAAc,YAAY,OAAO;AACvC,SAAO,WAAW,SAAS,KAAK,WAAW,OAAO;AACpD;AAYA,eAAsBA,eAAc,OAAc,KAAa,QAAgB,MAA8B;AAC3G,MAAI,CAAC,YAAY,KAAK,GAAG;AACvB,UAAM,IAAI,MAAM,wCAAwC;AAAA,EAC1D;AAEA,MAAI,CAAC,kBAAkB,KAAK,GAAG;AAC7B,UAAM,IAAI,MAAM,mCAAmC;AAAA,EACrD;AAEA,MAAI,CAAC,uBAAuB,KAAK,GAAG;AAClC,UAAM,IAAI,MAAM,mDAAmD;AAAA,EACrE;AAEA,MAAI,CAAC,oBAAoB,OAAO,GAAG,GAAG;AACpC,UAAM,IAAI,MAAM,sCAAsC;AAAA,EACxD;AAEA,MAAI,CAAC,uBAAuB,OAAO,MAAM,GAAG;AAC1C,UAAM,IAAI,MAAM,yCAAyC;AAAA,EAC3D;AAEA,MAAI,QAAQ,IAAI,KAAK,OAAO,SAAS,YAAY,OAAO,KAAK,IAAI,EAAE,SAAS,GAAG;AAC7E,QAAI,CAAC,wBAAwB,OAAO,IAAI,GAAG;AACzC,YAAM,IAAI,MAAM,mEAAmE;AAAA,IACrF;AAAA,EACF;AAEA,SAAO;AACT;",
+  "names": ["validateEvent", "import_sha2", "import_utils", "import_utils", "i", "validateEvent"]
 }
Index: package/lib/esm/nip98.js.map
===================================================================
--- package/lib/esm/nip98.js.map
+++ package/lib/esm/nip98.js.map
@@ -1,7 +1,7 @@
 {
   "version": 3,
   "sources": ["../../nip98.ts", "../../pure.ts", "../../core.ts", "../../utils.ts", "../../kinds.ts"],
-  "sourcesContent": ["import { sha256 } from '@noble/hashes/sha256'\nimport { bytesToHex } from '@noble/hashes/utils'\nimport { base64 } from '@scure/base'\n\nimport { HTTPAuth } from './kinds.ts'\nimport { Event, EventTemplate, verifyEvent } from './pure.ts'\nimport { utf8Decoder, utf8Encoder } from './utils.ts'\n\nconst _authorizationScheme = 'Nostr '\n\n/**\n * Generate token for NIP-98 flow.\n *\n * @example\n * const sign = window.nostr.signEvent\n * await nip98.getToken('https://example.com/login', 'post', (e) => sign(e), true)\n */\nexport async function getToken(\n  loginUrl: string,\n  httpMethod: string,\n  sign: (e: EventTemplate) => Promise<Event> | Event,\n  includeAuthorizationScheme: boolean = false,\n  payload?: Record<string, any>,\n): Promise<string> {\n  const event: EventTemplate = {\n    kind: HTTPAuth,\n    tags: [\n      ['u', loginUrl],\n      ['method', httpMethod],\n    ],\n    created_at: Math.round(new Date().getTime() / 1000),\n    content: '',\n  }\n\n  if (payload) {\n    event.tags.push(['payload', hashPayload(payload)])\n  }\n\n  const signedEvent = await sign(event)\n  const authorizationScheme = includeAuthorizationScheme ? _authorizationScheme : ''\n\n  return authorizationScheme + base64.encode(utf8Encoder.encode(JSON.stringify(signedEvent)))\n}\n\n/**\n * Validate token for NIP-98 flow.\n *\n * @example\n * await nip98.validateToken('Nostr base64token', 'https://example.com/login', 'post')\n */\nexport async function validateToken(token: string, url: string, method: string): Promise<boolean> {\n  const event = await unpackEventFromToken(token).catch(error => {\n    throw error\n  })\n\n  const valid = await validateEvent(event, url, method).catch(error => {\n    throw error\n  })\n\n  return valid\n}\n\n/**\n * Unpacks an event from a token.\n *\n * @param token - The token to unpack.\n * @returns A promise that resolves to the unpacked event.\n * @throws {Error} If the token is missing, invalid, or cannot be parsed.\n */\nexport async function unpackEventFromToken(token: string): Promise<Event> {\n  if (!token) {\n    throw new Error('Missing token')\n  }\n\n  token = token.replace(_authorizationScheme, '')\n\n  const eventB64 = utf8Decoder.decode(base64.decode(token))\n  if (!eventB64 || eventB64.length === 0 || !eventB64.startsWith('{')) {\n    throw new Error('Invalid token')\n  }\n\n  const event = JSON.parse(eventB64) as Event\n\n  return event\n}\n\n/**\n * Validates the timestamp of an event.\n * @param event - The event object to validate.\n * @returns A boolean indicating whether the event timestamp is within the last 60 seconds.\n */\nexport function validateEventTimestamp(event: Event): boolean {\n  if (!event.created_at) {\n    return false\n  }\n\n  return Math.round(new Date().getTime() / 1000) - event.created_at < 60\n}\n\n/**\n * Validates the kind of an event.\n * @param event The event to validate.\n * @returns A boolean indicating whether the event kind is valid.\n */\nexport function validateEventKind(event: Event): boolean {\n  return event.kind === HTTPAuth\n}\n\n/**\n * Validates if the given URL matches the URL tag of the event.\n * @param event - The event object.\n * @param url - The URL to validate.\n * @returns A boolean indicating whether the URL is valid or not.\n */\nexport function validateEventUrlTag(event: Event, url: string): boolean {\n  const urlTag = event.tags.find(t => t[0] === 'u')\n\n  if (!urlTag) {\n    return false\n  }\n\n  return urlTag.length > 0 && urlTag[1] === url\n}\n\n/**\n * Validates if the given event has a method tag that matches the specified method.\n * @param event - The event to validate.\n * @param method - The method to match against the method tag.\n * @returns A boolean indicating whether the event has a matching method tag.\n */\nexport function validateEventMethodTag(event: Event, method: string): boolean {\n  const methodTag = event.tags.find(t => t[0] === 'method')\n\n  if (!methodTag) {\n    return false\n  }\n\n  return methodTag.length > 0 && methodTag[1].toLowerCase() === method.toLowerCase()\n}\n\n/**\n * Calculates the hash of a payload.\n * @param payload - The payload to be hashed.\n * @returns The hash value as a string.\n */\nexport function hashPayload(payload: any): string {\n  const hash = sha256(utf8Encoder.encode(JSON.stringify(payload)))\n  return bytesToHex(hash)\n}\n\n/**\n * Validates the event payload tag against the provided payload.\n * @param event The event object.\n * @param payload The payload to validate.\n * @returns A boolean indicating whether the payload tag is valid.\n */\nexport function validateEventPayloadTag(event: Event, payload: any): boolean {\n  const payloadTag = event.tags.find(t => t[0] === 'payload')\n\n  if (!payloadTag) {\n    return false\n  }\n\n  const payloadHash = hashPayload(payload)\n  return payloadTag.length > 0 && payloadTag[1] === payloadHash\n}\n\n/**\n * Validates a Nostr event for the NIP-98 flow.\n *\n * @param event - The Nostr event to validate.\n * @param url - The URL associated with the event.\n * @param method - The HTTP method associated with the event.\n * @param body - The request body associated with the event (optional).\n * @returns A promise that resolves to a boolean indicating whether the event is valid.\n * @throws An error if the event is invalid.\n */\nexport async function validateEvent(event: Event, url: string, method: string, body?: any): Promise<boolean> {\n  if (!verifyEvent(event)) {\n    throw new Error('Invalid nostr event, signature invalid')\n  }\n\n  if (!validateEventKind(event)) {\n    throw new Error('Invalid nostr event, kind invalid')\n  }\n\n  if (!validateEventTimestamp(event)) {\n    throw new Error('Invalid nostr event, created_at timestamp invalid')\n  }\n\n  if (!validateEventUrlTag(event, url)) {\n    throw new Error('Invalid nostr event, url tag invalid')\n  }\n\n  if (!validateEventMethodTag(event, method)) {\n    throw new Error('Invalid nostr event, method tag invalid')\n  }\n\n  if (Boolean(body) && typeof body === 'object' && Object.keys(body).length > 0) {\n    if (!validateEventPayloadTag(event, body)) {\n      throw new Error('Invalid nostr event, payload tag does not match request body hash')\n    }\n  }\n\n  return true\n}\n", "import { schnorr } from '@noble/curves/secp256k1'\nimport { bytesToHex } from '@noble/hashes/utils'\nimport { Nostr, Event, EventTemplate, UnsignedEvent, VerifiedEvent, verifiedSymbol, validateEvent } from './core.ts'\nimport { sha256 } from '@noble/hashes/sha256'\n\nimport { utf8Encoder } from './utils.ts'\n\nclass JS implements Nostr {\n  generateSecretKey(): Uint8Array {\n    return schnorr.utils.randomPrivateKey()\n  }\n  getPublicKey(secretKey: Uint8Array): string {\n    return bytesToHex(schnorr.getPublicKey(secretKey))\n  }\n  finalizeEvent(t: EventTemplate, secretKey: Uint8Array): VerifiedEvent {\n    const event = t as VerifiedEvent\n    event.pubkey = bytesToHex(schnorr.getPublicKey(secretKey))\n    event.id = getEventHash(event)\n    event.sig = bytesToHex(schnorr.sign(getEventHash(event), secretKey))\n    event[verifiedSymbol] = true\n    return event\n  }\n  verifyEvent(event: Event): event is VerifiedEvent {\n    if (typeof event[verifiedSymbol] === 'boolean') return event[verifiedSymbol]\n\n    const hash = getEventHash(event)\n    if (hash !== event.id) {\n      event[verifiedSymbol] = false\n      return false\n    }\n\n    try {\n      const valid = schnorr.verify(event.sig, hash, event.pubkey)\n      event[verifiedSymbol] = valid\n      return valid\n    } catch (err) {\n      event[verifiedSymbol] = false\n      return false\n    }\n  }\n}\n\nexport function serializeEvent(evt: UnsignedEvent): string {\n  if (!validateEvent(evt)) throw new Error(\"can't serialize event with wrong or missing properties\")\n  return JSON.stringify([0, evt.pubkey, evt.created_at, evt.kind, evt.tags, evt.content])\n}\n\nexport function getEventHash(event: UnsignedEvent): string {\n  let eventHash = sha256(utf8Encoder.encode(serializeEvent(event)))\n  return bytesToHex(eventHash)\n}\n\nconst i: JS = new JS()\n\nexport const generateSecretKey = i.generateSecretKey\nexport const getPublicKey = i.getPublicKey\nexport const finalizeEvent = i.finalizeEvent\nexport const verifyEvent = i.verifyEvent\nexport * from './core.ts'\n", "export interface Nostr {\n  generateSecretKey(): Uint8Array\n  getPublicKey(secretKey: Uint8Array): string\n  finalizeEvent(event: EventTemplate, secretKey: Uint8Array): VerifiedEvent\n  verifyEvent(event: Event): event is VerifiedEvent\n}\n\n/** Designates a verified event signature. */\nexport const verifiedSymbol = Symbol('verified')\n\nexport type NostrEvent = {\n  kind: number\n  tags: string[][]\n  content: string\n  created_at: number\n  pubkey: string\n  id: string\n  sig: string\n  [verifiedSymbol]?: boolean\n}\n\nexport type Event = NostrEvent\nexport type EventTemplate = Pick<Event, 'kind' | 'tags' | 'content' | 'created_at'>\nexport type UnsignedEvent = Pick<Event, 'kind' | 'tags' | 'content' | 'created_at' | 'pubkey'>\n\n/** An event whose signature has been verified. */\nexport interface VerifiedEvent extends Event {\n  [verifiedSymbol]: true\n}\n\nconst isRecord = (obj: unknown): obj is Record<string, unknown> => obj instanceof Object\n\nexport function validateEvent<T>(event: T): event is T & UnsignedEvent {\n  if (!isRecord(event)) return false\n  if (typeof event.kind !== 'number') return false\n  if (typeof event.content !== 'string') return false\n  if (typeof event.created_at !== 'number') return false\n  if (typeof event.pubkey !== 'string') return false\n  if (!event.pubkey.match(/^[a-f0-9]{64}$/)) return false\n\n  if (!Array.isArray(event.tags)) return false\n  for (let i = 0; i < event.tags.length; i++) {\n    let tag = event.tags[i]\n    if (!Array.isArray(tag)) return false\n    for (let j = 0; j < tag.length; j++) {\n      if (typeof tag[j] !== 'string') return false\n    }\n  }\n\n  return true\n}\n\n/**\n * Sort events in reverse-chronological order by the `created_at` timestamp,\n * and then by the event `id` (lexicographically) in case of ties.\n * This mutates the array.\n */\nexport function sortEvents(events: Event[]): Event[] {\n  return events.sort((a: NostrEvent, b: NostrEvent): number => {\n    if (a.created_at !== b.created_at) {\n      return b.created_at - a.created_at\n    }\n    return a.id.localeCompare(b.id)\n  })\n}\n", "import type { Event } from './core.ts'\n\nexport const utf8Decoder: TextDecoder = new TextDecoder('utf-8')\nexport const utf8Encoder: TextEncoder = new TextEncoder()\n\nexport { bytesToHex, hexToBytes } from '@noble/hashes/utils'\n\nexport function normalizeURL(url: string): string {\n  try {\n    if (url.indexOf('://') === -1) url = 'wss://' + url\n    let p = new URL(url)\n    if (p.protocol === 'http:') p.protocol = 'ws:'\n    else if (p.protocol === 'https:') p.protocol = 'wss:'\n    p.pathname = p.pathname.replace(/\\/+/g, '/')\n    if (p.pathname.endsWith('/')) p.pathname = p.pathname.slice(0, -1)\n    if ((p.port === '80' && p.protocol === 'ws:') || (p.port === '443' && p.protocol === 'wss:')) p.port = ''\n    p.searchParams.sort()\n    p.hash = ''\n    return p.toString()\n  } catch (e) {\n    throw new Error(`Invalid URL: ${url}`)\n  }\n}\n\nexport function insertEventIntoDescendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return b.created_at - event.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function insertEventIntoAscendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return event.created_at - b.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function binarySearch<T>(arr: T[], compare: (b: T) => number): [number, boolean] {\n  let start = 0\n  let end = arr.length - 1\n\n  while (start <= end) {\n    const mid = Math.floor((start + end) / 2)\n    const cmp = compare(arr[mid])\n\n    if (cmp === 0) {\n      return [mid, true]\n    }\n\n    if (cmp < 0) {\n      end = mid - 1\n    } else {\n      start = mid + 1\n    }\n  }\n\n  return [start, false]\n}\n\nexport class QueueNode<V> {\n  public value: V\n  public next: QueueNode<V> | null = null\n  public prev: QueueNode<V> | null = null\n\n  constructor(message: V) {\n    this.value = message\n  }\n}\n\nexport class Queue<V> {\n  public first: QueueNode<V> | null\n  public last: QueueNode<V> | null\n\n  constructor() {\n    this.first = null\n    this.last = null\n  }\n\n  enqueue(value: V): boolean {\n    const newNode = new QueueNode(value)\n    if (!this.last) {\n      // list is empty\n      this.first = newNode\n      this.last = newNode\n    } else if (this.last === this.first) {\n      // list has a single element\n      this.last = newNode\n      this.last.prev = this.first\n      this.first.next = newNode\n    } else {\n      // list has elements, add as last\n      newNode.prev = this.last\n      this.last.next = newNode\n      this.last = newNode\n    }\n    return true\n  }\n\n  dequeue(): V | null {\n    if (!this.first) return null\n\n    if (this.first === this.last) {\n      const target = this.first\n      this.first = null\n      this.last = null\n      return target.value\n    }\n\n    const target = this.first\n    this.first = target.next\n    if (this.first) {\n      this.first.prev = null // fix: clean up prev pointer\n    }\n\n    return target.value\n  }\n}\n", "import { NostrEvent, validateEvent } from './pure.ts'\n\n/** Events are **regular**, which means they're all expected to be stored by relays. */\nexport function isRegularKind(kind: number): boolean {\n  return kind < 10000 && kind !== 0 && kind !== 3\n}\n\n/** Events are **replaceable**, which means that, for each combination of `pubkey` and `kind`, only the latest event is expected to (SHOULD) be stored by relays, older versions are expected to be discarded. */\nexport function isReplaceableKind(kind: number): boolean {\n  return kind === 0 || kind === 3 || (10000 <= kind && kind < 20000)\n}\n\n/** Events are **ephemeral**, which means they are not expected to be stored by relays. */\nexport function isEphemeralKind(kind: number): boolean {\n  return 20000 <= kind && kind < 30000\n}\n\n/** Events are **addressable**, which means that, for each combination of `pubkey`, `kind` and the `d` tag, only the latest event is expected to be stored by relays, older versions are expected to be discarded. */\nexport function isAddressableKind(kind: number): boolean {\n  return 30000 <= kind && kind < 40000\n}\n\n/** Classification of the event kind. */\nexport type KindClassification = 'regular' | 'replaceable' | 'ephemeral' | 'parameterized' | 'unknown'\n\n/** Determine the classification of this kind of event if known, or `unknown`. */\nexport function classifyKind(kind: number): KindClassification {\n  if (isRegularKind(kind)) return 'regular'\n  if (isReplaceableKind(kind)) return 'replaceable'\n  if (isEphemeralKind(kind)) return 'ephemeral'\n  if (isAddressableKind(kind)) return 'parameterized'\n  return 'unknown'\n}\n\nexport function isKind<T extends number>(event: unknown, kind: T | Array<T>): event is NostrEvent & { kind: T } {\n  const kindAsArray: number[] = kind instanceof Array ? kind : [kind]\n  return (validateEvent(event) && kindAsArray.includes(event.kind)) || false\n}\n\nexport const Metadata = 0\nexport type Metadata = typeof Metadata\nexport const ShortTextNote = 1\nexport type ShortTextNote = typeof ShortTextNote\nexport const RecommendRelay = 2\nexport type RecommendRelay = typeof RecommendRelay\nexport const Contacts = 3\nexport type Contacts = typeof Contacts\nexport const EncryptedDirectMessage = 4\nexport type EncryptedDirectMessage = typeof EncryptedDirectMessage\nexport const EventDeletion = 5\nexport type EventDeletion = typeof EventDeletion\nexport const Repost = 6\nexport type Repost = typeof Repost\nexport const Reaction = 7\nexport type Reaction = typeof Reaction\nexport const BadgeAward = 8\nexport type BadgeAward = typeof BadgeAward\nexport const ChatMessage = 9\nexport type ChatMessage = typeof ChatMessage\nexport const ForumThread = 11\nexport type ForumThread = typeof ForumThread\nexport const Seal = 13\nexport type Seal = typeof Seal\nexport const PrivateDirectMessage = 14\nexport type PrivateDirectMessage = typeof PrivateDirectMessage\nexport const FileMessage = 15\nexport type FileMessage = typeof FileMessage\nexport const GenericRepost = 16\nexport type GenericRepost = typeof GenericRepost\nexport const Photo = 20\nexport type Photo = typeof Photo\nexport const NormalVideo = 21\nexport type NormalVideo = typeof NormalVideo\nexport const ShortVideo = 22\nexport type ShortVideo = typeof ShortVideo\nexport const ChannelCreation = 40\nexport type ChannelCreation = typeof ChannelCreation\nexport const ChannelMetadata = 41\nexport type ChannelMetadata = typeof ChannelMetadata\nexport const ChannelMessage = 42\nexport type ChannelMessage = typeof ChannelMessage\nexport const ChannelHideMessage = 43\nexport type ChannelHideMessage = typeof ChannelHideMessage\nexport const ChannelMuteUser = 44\nexport type ChannelMuteUser = typeof ChannelMuteUser\nexport const OpenTimestamps = 1040\nexport type OpenTimestamps = typeof OpenTimestamps\nexport const GiftWrap = 1059\nexport type GiftWrap = typeof GiftWrap\nexport const Poll = 1068\nexport type Poll = typeof Poll\nexport const FileMetadata = 1063\nexport type FileMetadata = typeof FileMetadata\nexport const Comment = 1111\nexport type Comment = typeof Comment\nexport const LiveChatMessage = 1311\nexport type LiveChatMessage = typeof LiveChatMessage\nexport const Voice = 1222\nexport type Voice = typeof Voice\nexport const VoiceComment = 1244\nexport type VoiceComment = typeof VoiceComment\nexport const ProblemTracker = 1971\nexport type ProblemTracker = typeof ProblemTracker\nexport const Report = 1984\nexport type Report = typeof Report\nexport const Reporting = 1984\nexport type Reporting = typeof Reporting\nexport const Label = 1985\nexport type Label = typeof Label\nexport const CommunityPostApproval = 4550\nexport type CommunityPostApproval = typeof CommunityPostApproval\nexport const JobRequest = 5999\nexport type JobRequest = typeof JobRequest\nexport const JobResult = 6999\nexport type JobResult = typeof JobResult\nexport const JobFeedback = 7000\nexport type JobFeedback = typeof JobFeedback\nexport const ZapGoal = 9041\nexport type ZapGoal = typeof ZapGoal\nexport const ZapRequest = 9734\nexport type ZapRequest = typeof ZapRequest\nexport const Zap = 9735\nexport type Zap = typeof Zap\nexport const Highlights = 9802\nexport type Highlights = typeof Highlights\nexport const PollResponse = 1018\nexport type PollResponse = typeof PollResponse\nexport const Mutelist = 10000\nexport type Mutelist = typeof Mutelist\nexport const Pinlist = 10001\nexport type Pinlist = typeof Pinlist\nexport const RelayList = 10002\nexport type RelayList = typeof RelayList\nexport const BookmarkList = 10003\nexport type BookmarkList = typeof BookmarkList\nexport const CommunitiesList = 10004\nexport type CommunitiesList = typeof CommunitiesList\nexport const PublicChatsList = 10005\nexport type PublicChatsList = typeof PublicChatsList\nexport const BlockedRelaysList = 10006\nexport type BlockedRelaysList = typeof BlockedRelaysList\nexport const SearchRelaysList = 10007\nexport type SearchRelaysList = typeof SearchRelaysList\nexport const FavoriteRelays = 10012\nexport type FavoriteRelays = typeof FavoriteRelays\nexport const InterestsList = 10015\nexport type InterestsList = typeof InterestsList\nexport const UserEmojiList = 10030\nexport type UserEmojiList = typeof UserEmojiList\nexport const DirectMessageRelaysList = 10050\nexport type DirectMessageRelaysList = typeof DirectMessageRelaysList\nexport const FileServerPreference = 10096\nexport type FileServerPreference = typeof FileServerPreference\nexport const BlossomServerList = 10063\nexport type BlossomServerList = typeof BlossomServerList\nexport const NWCWalletInfo = 13194\nexport type NWCWalletInfo = typeof NWCWalletInfo\nexport const LightningPubRPC = 21000\nexport type LightningPubRPC = typeof LightningPubRPC\nexport const ClientAuth = 22242\nexport type ClientAuth = typeof ClientAuth\nexport const NWCWalletRequest = 23194\nexport type NWCWalletRequest = typeof NWCWalletRequest\nexport const NWCWalletResponse = 23195\nexport type NWCWalletResponse = typeof NWCWalletResponse\nexport const NostrConnect = 24133\nexport type NostrConnect = typeof NostrConnect\nexport const HTTPAuth = 27235\nexport type HTTPAuth = typeof HTTPAuth\nexport const Followsets = 30000\nexport type Followsets = typeof Followsets\nexport const Genericlists = 30001\nexport type Genericlists = typeof Genericlists\nexport const Relaysets = 30002\nexport type Relaysets = typeof Relaysets\nexport const Bookmarksets = 30003\nexport type Bookmarksets = typeof Bookmarksets\nexport const Curationsets = 30004\nexport type Curationsets = typeof Curationsets\nexport const ProfileBadges = 30008\nexport type ProfileBadges = typeof ProfileBadges\nexport const BadgeDefinition = 30009\nexport type BadgeDefinition = typeof BadgeDefinition\nexport const Interestsets = 30015\nexport type Interestsets = typeof Interestsets\nexport const CreateOrUpdateStall = 30017\nexport type CreateOrUpdateStall = typeof CreateOrUpdateStall\nexport const CreateOrUpdateProduct = 30018\nexport type CreateOrUpdateProduct = typeof CreateOrUpdateProduct\nexport const LongFormArticle = 30023\nexport type LongFormArticle = typeof LongFormArticle\nexport const DraftLong = 30024\nexport type DraftLong = typeof DraftLong\nexport const Emojisets = 30030\nexport type Emojisets = typeof Emojisets\nexport const Application = 30078\nexport type Application = typeof Application\nexport const LiveEvent = 30311\nexport type LiveEvent = typeof LiveEvent\nexport const UserStatuses = 30315\nexport type UserStatuses = typeof UserStatuses\nexport const ClassifiedListing = 30402\nexport type ClassifiedListing = typeof ClassifiedListing\nexport const DraftClassifiedListing = 30403\nexport type DraftClassifiedListing = typeof DraftClassifiedListing\nexport const Date = 31922\nexport type Date = typeof Date\nexport const Time = 31923\nexport type Time = typeof Time\nexport const Calendar = 31924\nexport type Calendar = typeof Calendar\nexport const CalendarEventRSVP = 31925\nexport type CalendarEventRSVP = typeof CalendarEventRSVP\nexport const RelayReview = 31987\nexport type RelayReview = typeof RelayReview\nexport const Handlerrecommendation = 31989\nexport type Handlerrecommendation = typeof Handlerrecommendation\nexport const Handlerinformation = 31990\nexport type Handlerinformation = typeof Handlerinformation\nexport const CommunityDefinition = 34550\nexport type CommunityDefinition = typeof CommunityDefinition\nexport const GroupMetadata = 39000\nexport type GroupMetadata = typeof GroupMetadata\n"],
-  "mappings": "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",
-  "names": ["sha256", "bytesToHex", "bytesToHex", "i", "bytesToHex", "validateEvent", "sha256", "bytesToHex"]
+  "sourcesContent": ["import { sha256 } from '@noble/hashes/sha2.js'\nimport { bytesToHex } from '@noble/hashes/utils.js'\nimport { base64 } from '@scure/base'\n\nimport { HTTPAuth } from './kinds.ts'\nimport { Event, EventTemplate, verifyEvent } from './pure.ts'\nimport { utf8Decoder, utf8Encoder } from './utils.ts'\n\nconst _authorizationScheme = 'Nostr '\n\n/**\n * Generate token for NIP-98 flow.\n *\n * @example\n * const sign = window.nostr.signEvent\n * await nip98.getToken('https://example.com/login', 'post', (e) => sign(e), true)\n */\nexport async function getToken(\n  loginUrl: string,\n  httpMethod: string,\n  sign: (e: EventTemplate) => Promise<Event> | Event,\n  includeAuthorizationScheme: boolean = false,\n  payload?: Record<string, any>,\n): Promise<string> {\n  const event: EventTemplate = {\n    kind: HTTPAuth,\n    tags: [\n      ['u', loginUrl],\n      ['method', httpMethod],\n    ],\n    created_at: Math.round(new Date().getTime() / 1000),\n    content: '',\n  }\n\n  if (payload) {\n    event.tags.push(['payload', hashPayload(payload)])\n  }\n\n  const signedEvent = await sign(event)\n  const authorizationScheme = includeAuthorizationScheme ? _authorizationScheme : ''\n\n  return authorizationScheme + base64.encode(utf8Encoder.encode(JSON.stringify(signedEvent)))\n}\n\n/**\n * Validate token for NIP-98 flow.\n *\n * @example\n * await nip98.validateToken('Nostr base64token', 'https://example.com/login', 'post')\n */\nexport async function validateToken(token: string, url: string, method: string): Promise<boolean> {\n  const event = await unpackEventFromToken(token).catch(error => {\n    throw error\n  })\n\n  const valid = await validateEvent(event, url, method).catch(error => {\n    throw error\n  })\n\n  return valid\n}\n\n/**\n * Unpacks an event from a token.\n *\n * @param token - The token to unpack.\n * @returns A promise that resolves to the unpacked event.\n * @throws {Error} If the token is missing, invalid, or cannot be parsed.\n */\nexport async function unpackEventFromToken(token: string): Promise<Event> {\n  if (!token) {\n    throw new Error('Missing token')\n  }\n\n  token = token.replace(_authorizationScheme, '')\n\n  const eventB64 = utf8Decoder.decode(base64.decode(token))\n  if (!eventB64 || eventB64.length === 0 || !eventB64.startsWith('{')) {\n    throw new Error('Invalid token')\n  }\n\n  const event = JSON.parse(eventB64) as Event\n\n  return event\n}\n\n/**\n * Validates the timestamp of an event.\n * @param event - The event object to validate.\n * @returns A boolean indicating whether the event timestamp is within the last 60 seconds.\n */\nexport function validateEventTimestamp(event: Event): boolean {\n  if (!event.created_at) {\n    return false\n  }\n\n  return Math.round(new Date().getTime() / 1000) - event.created_at < 60\n}\n\n/**\n * Validates the kind of an event.\n * @param event The event to validate.\n * @returns A boolean indicating whether the event kind is valid.\n */\nexport function validateEventKind(event: Event): boolean {\n  return event.kind === HTTPAuth\n}\n\n/**\n * Validates if the given URL matches the URL tag of the event.\n * @param event - The event object.\n * @param url - The URL to validate.\n * @returns A boolean indicating whether the URL is valid or not.\n */\nexport function validateEventUrlTag(event: Event, url: string): boolean {\n  const urlTag = event.tags.find(t => t[0] === 'u')\n\n  if (!urlTag) {\n    return false\n  }\n\n  return urlTag.length > 0 && urlTag[1] === url\n}\n\n/**\n * Validates if the given event has a method tag that matches the specified method.\n * @param event - The event to validate.\n * @param method - The method to match against the method tag.\n * @returns A boolean indicating whether the event has a matching method tag.\n */\nexport function validateEventMethodTag(event: Event, method: string): boolean {\n  const methodTag = event.tags.find(t => t[0] === 'method')\n\n  if (!methodTag) {\n    return false\n  }\n\n  return methodTag.length > 0 && methodTag[1].toLowerCase() === method.toLowerCase()\n}\n\n/**\n * Calculates the hash of a payload.\n * @param payload - The payload to be hashed.\n * @returns The hash value as a string.\n */\nexport function hashPayload(payload: any): string {\n  const hash = sha256(utf8Encoder.encode(JSON.stringify(payload)))\n  return bytesToHex(hash)\n}\n\n/**\n * Validates the event payload tag against the provided payload.\n * @param event The event object.\n * @param payload The payload to validate.\n * @returns A boolean indicating whether the payload tag is valid.\n */\nexport function validateEventPayloadTag(event: Event, payload: any): boolean {\n  const payloadTag = event.tags.find(t => t[0] === 'payload')\n\n  if (!payloadTag) {\n    return false\n  }\n\n  const payloadHash = hashPayload(payload)\n  return payloadTag.length > 0 && payloadTag[1] === payloadHash\n}\n\n/**\n * Validates a Nostr event for the NIP-98 flow.\n *\n * @param event - The Nostr event to validate.\n * @param url - The URL associated with the event.\n * @param method - The HTTP method associated with the event.\n * @param body - The request body associated with the event (optional).\n * @returns A promise that resolves to a boolean indicating whether the event is valid.\n * @throws An error if the event is invalid.\n */\nexport async function validateEvent(event: Event, url: string, method: string, body?: any): Promise<boolean> {\n  if (!verifyEvent(event)) {\n    throw new Error('Invalid nostr event, signature invalid')\n  }\n\n  if (!validateEventKind(event)) {\n    throw new Error('Invalid nostr event, kind invalid')\n  }\n\n  if (!validateEventTimestamp(event)) {\n    throw new Error('Invalid nostr event, created_at timestamp invalid')\n  }\n\n  if (!validateEventUrlTag(event, url)) {\n    throw new Error('Invalid nostr event, url tag invalid')\n  }\n\n  if (!validateEventMethodTag(event, method)) {\n    throw new Error('Invalid nostr event, method tag invalid')\n  }\n\n  if (Boolean(body) && typeof body === 'object' && Object.keys(body).length > 0) {\n    if (!validateEventPayloadTag(event, body)) {\n      throw new Error('Invalid nostr event, payload tag does not match request body hash')\n    }\n  }\n\n  return true\n}\n", "import { schnorr } from '@noble/curves/secp256k1.js'\nimport { bytesToHex, hexToBytes } from '@noble/hashes/utils.js'\nimport { Nostr, Event, EventTemplate, UnsignedEvent, VerifiedEvent, verifiedSymbol, validateEvent } from './core.ts'\nimport { sha256 } from '@noble/hashes/sha2.js'\n\nimport { utf8Encoder } from './utils.ts'\n\nclass JS implements Nostr {\n  generateSecretKey(): Uint8Array {\n    return schnorr.utils.randomSecretKey()\n  }\n  getPublicKey(secretKey: Uint8Array): string {\n    return bytesToHex(schnorr.getPublicKey(secretKey))\n  }\n  finalizeEvent(t: EventTemplate, secretKey: Uint8Array): VerifiedEvent {\n    const event = t as VerifiedEvent\n    event.pubkey = bytesToHex(schnorr.getPublicKey(secretKey))\n    event.id = getEventHash(event)\n    event.sig = bytesToHex(schnorr.sign(hexToBytes(getEventHash(event)), secretKey))\n    event[verifiedSymbol] = true\n    return event\n  }\n  verifyEvent(event: Event): event is VerifiedEvent {\n    if (typeof event[verifiedSymbol] === 'boolean') return event[verifiedSymbol]\n\n    const hash = getEventHash(event)\n    if (hash !== event.id) {\n      event[verifiedSymbol] = false\n      return false\n    }\n\n    try {\n      const valid = schnorr.verify(hexToBytes(event.sig), hexToBytes(hash), hexToBytes(event.pubkey))\n      event[verifiedSymbol] = valid\n      return valid\n    } catch (err) {\n      event[verifiedSymbol] = false\n      return false\n    }\n  }\n}\n\nexport function serializeEvent(evt: UnsignedEvent): string {\n  if (!validateEvent(evt)) throw new Error(\"can't serialize event with wrong or missing properties\")\n  return JSON.stringify([0, evt.pubkey, evt.created_at, evt.kind, evt.tags, evt.content])\n}\n\nexport function getEventHash(event: UnsignedEvent): string {\n  let eventHash = sha256(utf8Encoder.encode(serializeEvent(event)))\n  return bytesToHex(eventHash)\n}\n\nconst i: JS = new JS()\n\nexport const generateSecretKey = i.generateSecretKey\nexport const getPublicKey = i.getPublicKey\nexport const finalizeEvent = i.finalizeEvent\nexport const verifyEvent = i.verifyEvent\nexport * from './core.ts'\n", "export interface Nostr {\n  generateSecretKey(): Uint8Array\n  getPublicKey(secretKey: Uint8Array): string\n  finalizeEvent(event: EventTemplate, secretKey: Uint8Array): VerifiedEvent\n  verifyEvent(event: Event): event is VerifiedEvent\n}\n\n/** Designates a verified event signature. */\nexport const verifiedSymbol = Symbol('verified')\n\nexport type NostrEvent = {\n  kind: number\n  tags: string[][]\n  content: string\n  created_at: number\n  pubkey: string\n  id: string\n  sig: string\n  [verifiedSymbol]?: boolean\n}\n\nexport type Event = NostrEvent\nexport type EventTemplate = Pick<Event, 'kind' | 'tags' | 'content' | 'created_at'>\nexport type UnsignedEvent = Pick<Event, 'kind' | 'tags' | 'content' | 'created_at' | 'pubkey'>\n\n/** An event whose signature has been verified. */\nexport interface VerifiedEvent extends Event {\n  [verifiedSymbol]: true\n}\n\nconst isRecord = (obj: unknown): obj is Record<string, unknown> => obj instanceof Object\n\nexport function validateEvent<T>(event: T): event is T & UnsignedEvent {\n  if (!isRecord(event)) return false\n  if (typeof event.kind !== 'number') return false\n  if (typeof event.content !== 'string') return false\n  if (typeof event.created_at !== 'number') return false\n  if (typeof event.pubkey !== 'string') return false\n  if (!event.pubkey.match(/^[a-f0-9]{64}$/)) return false\n\n  if (!Array.isArray(event.tags)) return false\n  for (let i = 0; i < event.tags.length; i++) {\n    let tag = event.tags[i]\n    if (!Array.isArray(tag)) return false\n    for (let j = 0; j < tag.length; j++) {\n      if (typeof tag[j] !== 'string') return false\n    }\n  }\n\n  return true\n}\n\n/**\n * Sort events in reverse-chronological order by the `created_at` timestamp,\n * and then by the event `id` (lexicographically) in case of ties.\n * This mutates the array.\n */\nexport function sortEvents(events: Event[]): Event[] {\n  return events.sort((a: NostrEvent, b: NostrEvent): number => {\n    if (a.created_at !== b.created_at) {\n      return b.created_at - a.created_at\n    }\n    return a.id.localeCompare(b.id)\n  })\n}\n", "import type { Event } from './core.ts'\n\nexport const utf8Decoder: TextDecoder = new TextDecoder('utf-8')\nexport const utf8Encoder: TextEncoder = new TextEncoder()\n\nexport { bytesToHex, hexToBytes } from '@noble/hashes/utils.js'\n\nexport function normalizeURL(url: string): string {\n  try {\n    if (url.indexOf('://') === -1) url = 'wss://' + url\n    let p = new URL(url)\n    if (p.protocol === 'http:') p.protocol = 'ws:'\n    else if (p.protocol === 'https:') p.protocol = 'wss:'\n    p.pathname = p.pathname.replace(/\\/+/g, '/')\n    if (p.pathname.endsWith('/')) p.pathname = p.pathname.slice(0, -1)\n    if ((p.port === '80' && p.protocol === 'ws:') || (p.port === '443' && p.protocol === 'wss:')) p.port = ''\n    p.searchParams.sort()\n    p.hash = ''\n    return p.toString()\n  } catch (e) {\n    throw new Error(`Invalid URL: ${url}`)\n  }\n}\n\nexport function insertEventIntoDescendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return b.created_at - event.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function insertEventIntoAscendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return event.created_at - b.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function binarySearch<T>(arr: T[], compare: (b: T) => number): [number, boolean] {\n  let start = 0\n  let end = arr.length - 1\n\n  while (start <= end) {\n    const mid = Math.floor((start + end) / 2)\n    const cmp = compare(arr[mid])\n\n    if (cmp === 0) {\n      return [mid, true]\n    }\n\n    if (cmp < 0) {\n      end = mid - 1\n    } else {\n      start = mid + 1\n    }\n  }\n\n  return [start, false]\n}\n\nexport class QueueNode<V> {\n  public value: V\n  public next: QueueNode<V> | null = null\n  public prev: QueueNode<V> | null = null\n\n  constructor(message: V) {\n    this.value = message\n  }\n}\n\nexport class Queue<V> {\n  public first: QueueNode<V> | null\n  public last: QueueNode<V> | null\n\n  constructor() {\n    this.first = null\n    this.last = null\n  }\n\n  enqueue(value: V): boolean {\n    const newNode = new QueueNode(value)\n    if (!this.last) {\n      // list is empty\n      this.first = newNode\n      this.last = newNode\n    } else if (this.last === this.first) {\n      // list has a single element\n      this.last = newNode\n      this.last.prev = this.first\n      this.first.next = newNode\n    } else {\n      // list has elements, add as last\n      newNode.prev = this.last\n      this.last.next = newNode\n      this.last = newNode\n    }\n    return true\n  }\n\n  dequeue(): V | null {\n    if (!this.first) return null\n\n    if (this.first === this.last) {\n      const target = this.first\n      this.first = null\n      this.last = null\n      return target.value\n    }\n\n    const target = this.first\n    this.first = target.next\n    if (this.first) {\n      this.first.prev = null // fix: clean up prev pointer\n    }\n\n    return target.value\n  }\n}\n", "import { NostrEvent, validateEvent } from './pure.ts'\n\n/** Events are **regular**, which means they're all expected to be stored by relays. */\nexport function isRegularKind(kind: number): boolean {\n  return kind < 10000 && kind !== 0 && kind !== 3\n}\n\n/** Events are **replaceable**, which means that, for each combination of `pubkey` and `kind`, only the latest event is expected to (SHOULD) be stored by relays, older versions are expected to be discarded. */\nexport function isReplaceableKind(kind: number): boolean {\n  return kind === 0 || kind === 3 || (10000 <= kind && kind < 20000)\n}\n\n/** Events are **ephemeral**, which means they are not expected to be stored by relays. */\nexport function isEphemeralKind(kind: number): boolean {\n  return 20000 <= kind && kind < 30000\n}\n\n/** Events are **addressable**, which means that, for each combination of `pubkey`, `kind` and the `d` tag, only the latest event is expected to be stored by relays, older versions are expected to be discarded. */\nexport function isAddressableKind(kind: number): boolean {\n  return 30000 <= kind && kind < 40000\n}\n\n/** Classification of the event kind. */\nexport type KindClassification = 'regular' | 'replaceable' | 'ephemeral' | 'parameterized' | 'unknown'\n\n/** Determine the classification of this kind of event if known, or `unknown`. */\nexport function classifyKind(kind: number): KindClassification {\n  if (isRegularKind(kind)) return 'regular'\n  if (isReplaceableKind(kind)) return 'replaceable'\n  if (isEphemeralKind(kind)) return 'ephemeral'\n  if (isAddressableKind(kind)) return 'parameterized'\n  return 'unknown'\n}\n\nexport function isKind<T extends number>(event: unknown, kind: T | Array<T>): event is NostrEvent & { kind: T } {\n  const kindAsArray: number[] = kind instanceof Array ? kind : [kind]\n  return (validateEvent(event) && kindAsArray.includes(event.kind)) || false\n}\n\nexport const Metadata = 0\nexport type Metadata = typeof Metadata\nexport const ShortTextNote = 1\nexport type ShortTextNote = typeof ShortTextNote\nexport const RecommendRelay = 2\nexport type RecommendRelay = typeof RecommendRelay\nexport const Contacts = 3\nexport type Contacts = typeof Contacts\nexport const EncryptedDirectMessage = 4\nexport type EncryptedDirectMessage = typeof EncryptedDirectMessage\nexport const EventDeletion = 5\nexport type EventDeletion = typeof EventDeletion\nexport const Repost = 6\nexport type Repost = typeof Repost\nexport const Reaction = 7\nexport type Reaction = typeof Reaction\nexport const BadgeAward = 8\nexport type BadgeAward = typeof BadgeAward\nexport const ChatMessage = 9\nexport type ChatMessage = typeof ChatMessage\nexport const ForumThread = 11\nexport type ForumThread = typeof ForumThread\nexport const Seal = 13\nexport type Seal = typeof Seal\nexport const PrivateDirectMessage = 14\nexport type PrivateDirectMessage = typeof PrivateDirectMessage\nexport const FileMessage = 15\nexport type FileMessage = typeof FileMessage\nexport const GenericRepost = 16\nexport type GenericRepost = typeof GenericRepost\nexport const Photo = 20\nexport type Photo = typeof Photo\nexport const NormalVideo = 21\nexport type NormalVideo = typeof NormalVideo\nexport const ShortVideo = 22\nexport type ShortVideo = typeof ShortVideo\nexport const ChannelCreation = 40\nexport type ChannelCreation = typeof ChannelCreation\nexport const ChannelMetadata = 41\nexport type ChannelMetadata = typeof ChannelMetadata\nexport const ChannelMessage = 42\nexport type ChannelMessage = typeof ChannelMessage\nexport const ChannelHideMessage = 43\nexport type ChannelHideMessage = typeof ChannelHideMessage\nexport const ChannelMuteUser = 44\nexport type ChannelMuteUser = typeof ChannelMuteUser\nexport const OpenTimestamps = 1040\nexport type OpenTimestamps = typeof OpenTimestamps\nexport const GiftWrap = 1059\nexport type GiftWrap = typeof GiftWrap\nexport const Poll = 1068\nexport type Poll = typeof Poll\nexport const FileMetadata = 1063\nexport type FileMetadata = typeof FileMetadata\nexport const Comment = 1111\nexport type Comment = typeof Comment\nexport const LiveChatMessage = 1311\nexport type LiveChatMessage = typeof LiveChatMessage\nexport const Voice = 1222\nexport type Voice = typeof Voice\nexport const VoiceComment = 1244\nexport type VoiceComment = typeof VoiceComment\nexport const ProblemTracker = 1971\nexport type ProblemTracker = typeof ProblemTracker\nexport const Report = 1984\nexport type Report = typeof Report\nexport const Reporting = 1984\nexport type Reporting = typeof Reporting\nexport const Label = 1985\nexport type Label = typeof Label\nexport const CommunityPostApproval = 4550\nexport type CommunityPostApproval = typeof CommunityPostApproval\nexport const JobRequest = 5999\nexport type JobRequest = typeof JobRequest\nexport const JobResult = 6999\nexport type JobResult = typeof JobResult\nexport const JobFeedback = 7000\nexport type JobFeedback = typeof JobFeedback\nexport const ZapGoal = 9041\nexport type ZapGoal = typeof ZapGoal\nexport const ZapRequest = 9734\nexport type ZapRequest = typeof ZapRequest\nexport const Zap = 9735\nexport type Zap = typeof Zap\nexport const Highlights = 9802\nexport type Highlights = typeof Highlights\nexport const PollResponse = 1018\nexport type PollResponse = typeof PollResponse\nexport const Mutelist = 10000\nexport type Mutelist = typeof Mutelist\nexport const Pinlist = 10001\nexport type Pinlist = typeof Pinlist\nexport const RelayList = 10002\nexport type RelayList = typeof RelayList\nexport const BookmarkList = 10003\nexport type BookmarkList = typeof BookmarkList\nexport const CommunitiesList = 10004\nexport type CommunitiesList = typeof CommunitiesList\nexport const PublicChatsList = 10005\nexport type PublicChatsList = typeof PublicChatsList\nexport const BlockedRelaysList = 10006\nexport type BlockedRelaysList = typeof BlockedRelaysList\nexport const SearchRelaysList = 10007\nexport type SearchRelaysList = typeof SearchRelaysList\nexport const FavoriteRelays = 10012\nexport type FavoriteRelays = typeof FavoriteRelays\nexport const InterestsList = 10015\nexport type InterestsList = typeof InterestsList\nexport const UserEmojiList = 10030\nexport type UserEmojiList = typeof UserEmojiList\nexport const DirectMessageRelaysList = 10050\nexport type DirectMessageRelaysList = typeof DirectMessageRelaysList\nexport const FileServerPreference = 10096\nexport type FileServerPreference = typeof FileServerPreference\nexport const BlossomServerList = 10063\nexport type BlossomServerList = typeof BlossomServerList\nexport const NWCWalletInfo = 13194\nexport type NWCWalletInfo = typeof NWCWalletInfo\nexport const LightningPubRPC = 21000\nexport type LightningPubRPC = typeof LightningPubRPC\nexport const ClientAuth = 22242\nexport type ClientAuth = typeof ClientAuth\nexport const NWCWalletRequest = 23194\nexport type NWCWalletRequest = typeof NWCWalletRequest\nexport const NWCWalletResponse = 23195\nexport type NWCWalletResponse = typeof NWCWalletResponse\nexport const NostrConnect = 24133\nexport type NostrConnect = typeof NostrConnect\nexport const HTTPAuth = 27235\nexport type HTTPAuth = typeof HTTPAuth\nexport const Followsets = 30000\nexport type Followsets = typeof Followsets\nexport const Genericlists = 30001\nexport type Genericlists = typeof Genericlists\nexport const Relaysets = 30002\nexport type Relaysets = typeof Relaysets\nexport const Bookmarksets = 30003\nexport type Bookmarksets = typeof Bookmarksets\nexport const Curationsets = 30004\nexport type Curationsets = typeof Curationsets\nexport const ProfileBadges = 30008\nexport type ProfileBadges = typeof ProfileBadges\nexport const BadgeDefinition = 30009\nexport type BadgeDefinition = typeof BadgeDefinition\nexport const Interestsets = 30015\nexport type Interestsets = typeof Interestsets\nexport const CreateOrUpdateStall = 30017\nexport type CreateOrUpdateStall = typeof CreateOrUpdateStall\nexport const CreateOrUpdateProduct = 30018\nexport type CreateOrUpdateProduct = typeof CreateOrUpdateProduct\nexport const LongFormArticle = 30023\nexport type LongFormArticle = typeof LongFormArticle\nexport const DraftLong = 30024\nexport type DraftLong = typeof DraftLong\nexport const Emojisets = 30030\nexport type Emojisets = typeof Emojisets\nexport const Application = 30078\nexport type Application = typeof Application\nexport const LiveEvent = 30311\nexport type LiveEvent = typeof LiveEvent\nexport const UserStatuses = 30315\nexport type UserStatuses = typeof UserStatuses\nexport const ClassifiedListing = 30402\nexport type ClassifiedListing = typeof ClassifiedListing\nexport const DraftClassifiedListing = 30403\nexport type DraftClassifiedListing = typeof DraftClassifiedListing\nexport const Date = 31922\nexport type Date = typeof Date\nexport const Time = 31923\nexport type Time = typeof Time\nexport const Calendar = 31924\nexport type Calendar = typeof Calendar\nexport const CalendarEventRSVP = 31925\nexport type CalendarEventRSVP = typeof CalendarEventRSVP\nexport const RelayReview = 31987\nexport type RelayReview = typeof RelayReview\nexport const Handlerrecommendation = 31989\nexport type Handlerrecommendation = typeof Handlerrecommendation\nexport const Handlerinformation = 31990\nexport type Handlerinformation = typeof Handlerinformation\nexport const CommunityDefinition = 34550\nexport type CommunityDefinition = typeof CommunityDefinition\nexport const GroupMetadata = 39000\nexport type GroupMetadata = typeof GroupMetadata\n"],
+  "mappings": "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",
+  "names": ["sha256", "bytesToHex", "bytesToHex", "hexToBytes", "i", "bytesToHex", "hexToBytes", "validateEvent", "sha256", "bytesToHex"]
 }
Index: package/lib/cjs/nipb7.js.map
===================================================================
--- package/lib/cjs/nipb7.js.map
+++ package/lib/cjs/nipb7.js.map
@@ -1,7 +1,7 @@
 {
   "version": 3,
   "sources": ["../../nipb7.ts", "../../utils.ts"],
-  "sourcesContent": ["import { sha256 } from '@noble/hashes/sha256'\nimport { EventTemplate } from './core.ts'\nimport { Signer } from './signer.ts'\nimport { bytesToHex } from './utils.ts'\n\nexport type BlobDescriptor = {\n  url: string\n  sha256: string\n  size: number\n  type: string\n  uploaded: number\n}\n\nexport class BlossomClient {\n  private mediaserver: string\n  private signer: Signer\n\n  constructor(mediaserver: string, signer: Signer) {\n    if (!mediaserver.startsWith('http')) {\n      mediaserver = 'https://' + mediaserver\n    }\n    this.mediaserver = mediaserver.replace(/\\/$/, '') + '/'\n    this.signer = signer\n  }\n\n  private async httpCall(\n    method: string,\n    url: string,\n    contentType?: string,\n    addAuthorization?: () => Promise<string>,\n    body?: File | Blob,\n    result?: any,\n  ): Promise<any> {\n    const headers: { [_: string]: string } = {}\n\n    if (contentType) {\n      headers['Content-Type'] = contentType\n    }\n\n    if (addAuthorization) {\n      const auth = await addAuthorization()\n      if (auth) {\n        headers['Authorization'] = auth\n      }\n    }\n\n    const response = await fetch(this.mediaserver + url, {\n      method,\n      headers,\n      body,\n    })\n\n    if (response.status >= 300) {\n      const reason = response.headers.get('X-Reason') || response.statusText\n      throw new Error(`${url} returned an error (${response.status}): ${reason}`)\n    }\n\n    if (result !== null && response.headers.get('content-type')?.includes('application/json')) {\n      return await response.json()\n    }\n\n    return response\n  }\n\n  private async authorizationHeader(modify?: (event: EventTemplate) => void): Promise<string> {\n    const now = Math.floor(Date.now() / 1000)\n    const event: EventTemplate = {\n      created_at: now,\n      kind: 24242,\n      content: 'blossom stuff',\n      tags: [['expiration', String(now + 60)]],\n    }\n\n    if (modify) {\n      modify(event)\n    }\n\n    try {\n      const signedEvent = await this.signer.signEvent(event)\n      const eventJson = JSON.stringify(signedEvent)\n      return 'Nostr ' + btoa(eventJson)\n    } catch (error) {\n      return ''\n    }\n  }\n\n  private isValid32ByteHex(hash: string): boolean {\n    return /^[a-f0-9]{64}$/i.test(hash)\n  }\n\n  async check(hash: string): Promise<void> {\n    if (!this.isValid32ByteHex(hash)) {\n      throw new Error(`${hash} is not a valid 32-byte hex string`)\n    }\n\n    try {\n      await this.httpCall('HEAD', hash)\n    } catch (error) {\n      throw new Error(`failed to check for ${hash}: ${error}`)\n    }\n  }\n\n  async uploadBlob(file: File | Blob, contentType?: string): Promise<BlobDescriptor> {\n    const hash = bytesToHex(sha256(new Uint8Array(await file.arrayBuffer())))\n    const actualContentType = contentType || file.type || 'application/octet-stream'\n\n    const bd = await this.httpCall(\n      'PUT',\n      'upload',\n      actualContentType,\n      () =>\n        this.authorizationHeader(evt => {\n          evt.tags.push(['t', 'upload'])\n          evt.tags.push(['x', hash])\n        }),\n      file,\n      {},\n    )\n\n    return bd\n  }\n\n  async uploadFile(file: File): Promise<BlobDescriptor> {\n    return this.uploadBlob(file, file.type)\n  }\n\n  async download(hash: string): Promise<ArrayBuffer> {\n    if (!this.isValid32ByteHex(hash)) {\n      throw new Error(`${hash} is not a valid 32-byte hex string`)\n    }\n\n    const authHeader = await this.authorizationHeader(evt => {\n      evt.tags.push(['t', 'get'])\n      evt.tags.push(['x', hash])\n    })\n\n    const response = await fetch(this.mediaserver + hash, {\n      method: 'GET',\n      headers: {\n        Authorization: authHeader,\n      },\n    })\n\n    if (response.status >= 300) {\n      throw new Error(`${hash} is not present in ${this.mediaserver}: ${response.status}`)\n    }\n\n    return await response.arrayBuffer()\n  }\n\n  async downloadAsBlob(hash: string): Promise<Blob> {\n    const arrayBuffer = await this.download(hash)\n    return new Blob([arrayBuffer])\n  }\n\n  async list(): Promise<BlobDescriptor[]> {\n    const pubkey = await this.signer.getPublicKey()\n\n    if (!this.isValid32ByteHex(pubkey)) {\n      throw new Error(`pubkey ${pubkey} is not valid`)\n    }\n\n    try {\n      const bds = await this.httpCall(\n        'GET',\n        `list/${pubkey}`,\n        undefined,\n        () =>\n          this.authorizationHeader(evt => {\n            evt.tags.push(['t', 'list'])\n          }),\n        undefined,\n        [],\n      )\n      return bds\n    } catch (error) {\n      throw new Error(`failed to list blobs: ${error}`)\n    }\n  }\n\n  async delete(hash: string): Promise<void> {\n    if (!this.isValid32ByteHex(hash)) {\n      throw new Error(`${hash} is not a valid 32-byte hex string`)\n    }\n\n    try {\n      await this.httpCall(\n        'DELETE',\n        hash,\n        undefined,\n        () =>\n          this.authorizationHeader(evt => {\n            evt.tags.push(['t', 'delete'])\n            evt.tags.push(['x', hash])\n          }),\n        undefined,\n        null,\n      )\n    } catch (error) {\n      throw new Error(`failed to delete ${hash}: ${error}`)\n    }\n  }\n}\n", "import type { Event } from './core.ts'\n\nexport const utf8Decoder: TextDecoder = new TextDecoder('utf-8')\nexport const utf8Encoder: TextEncoder = new TextEncoder()\n\nexport { bytesToHex, hexToBytes } from '@noble/hashes/utils'\n\nexport function normalizeURL(url: string): string {\n  try {\n    if (url.indexOf('://') === -1) url = 'wss://' + url\n    let p = new URL(url)\n    if (p.protocol === 'http:') p.protocol = 'ws:'\n    else if (p.protocol === 'https:') p.protocol = 'wss:'\n    p.pathname = p.pathname.replace(/\\/+/g, '/')\n    if (p.pathname.endsWith('/')) p.pathname = p.pathname.slice(0, -1)\n    if ((p.port === '80' && p.protocol === 'ws:') || (p.port === '443' && p.protocol === 'wss:')) p.port = ''\n    p.searchParams.sort()\n    p.hash = ''\n    return p.toString()\n  } catch (e) {\n    throw new Error(`Invalid URL: ${url}`)\n  }\n}\n\nexport function insertEventIntoDescendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return b.created_at - event.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function insertEventIntoAscendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return event.created_at - b.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function binarySearch<T>(arr: T[], compare: (b: T) => number): [number, boolean] {\n  let start = 0\n  let end = arr.length - 1\n\n  while (start <= end) {\n    const mid = Math.floor((start + end) / 2)\n    const cmp = compare(arr[mid])\n\n    if (cmp === 0) {\n      return [mid, true]\n    }\n\n    if (cmp < 0) {\n      end = mid - 1\n    } else {\n      start = mid + 1\n    }\n  }\n\n  return [start, false]\n}\n\nexport class QueueNode<V> {\n  public value: V\n  public next: QueueNode<V> | null = null\n  public prev: QueueNode<V> | null = null\n\n  constructor(message: V) {\n    this.value = message\n  }\n}\n\nexport class Queue<V> {\n  public first: QueueNode<V> | null\n  public last: QueueNode<V> | null\n\n  constructor() {\n    this.first = null\n    this.last = null\n  }\n\n  enqueue(value: V): boolean {\n    const newNode = new QueueNode(value)\n    if (!this.last) {\n      // list is empty\n      this.first = newNode\n      this.last = newNode\n    } else if (this.last === this.first) {\n      // list has a single element\n      this.last = newNode\n      this.last.prev = this.first\n      this.first.next = newNode\n    } else {\n      // list has elements, add as last\n      newNode.prev = this.last\n      this.last.next = newNode\n      this.last = newNode\n    }\n    return true\n  }\n\n  dequeue(): V | null {\n    if (!this.first) return null\n\n    if (this.first === this.last) {\n      const target = this.first\n      this.first = null\n      this.last = null\n      return target.value\n    }\n\n    const target = this.first\n    this.first = target.next\n    if (this.first) {\n      this.first.prev = null // fix: clean up prev pointer\n    }\n\n    return target.value\n  }\n}\n"],
-  "mappings": "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",
+  "sourcesContent": ["import { sha256 } from '@noble/hashes/sha2.js'\nimport { EventTemplate } from './core.ts'\nimport { Signer } from './signer.ts'\nimport { bytesToHex } from './utils.ts'\n\nexport type BlobDescriptor = {\n  url: string\n  sha256: string\n  size: number\n  type: string\n  uploaded: number\n}\n\nexport class BlossomClient {\n  private mediaserver: string\n  private signer: Signer\n\n  constructor(mediaserver: string, signer: Signer) {\n    if (!mediaserver.startsWith('http')) {\n      mediaserver = 'https://' + mediaserver\n    }\n    this.mediaserver = mediaserver.replace(/\\/$/, '') + '/'\n    this.signer = signer\n  }\n\n  private async httpCall(\n    method: string,\n    url: string,\n    contentType?: string,\n    addAuthorization?: () => Promise<string>,\n    body?: File | Blob,\n    result?: any,\n  ): Promise<any> {\n    const headers: { [_: string]: string } = {}\n\n    if (contentType) {\n      headers['Content-Type'] = contentType\n    }\n\n    if (addAuthorization) {\n      const auth = await addAuthorization()\n      if (auth) {\n        headers['Authorization'] = auth\n      }\n    }\n\n    const response = await fetch(this.mediaserver + url, {\n      method,\n      headers,\n      body,\n    })\n\n    if (response.status >= 300) {\n      const reason = response.headers.get('X-Reason') || response.statusText\n      throw new Error(`${url} returned an error (${response.status}): ${reason}`)\n    }\n\n    if (result !== null && response.headers.get('content-type')?.includes('application/json')) {\n      return await response.json()\n    }\n\n    return response\n  }\n\n  private async authorizationHeader(modify?: (event: EventTemplate) => void): Promise<string> {\n    const now = Math.floor(Date.now() / 1000)\n    const event: EventTemplate = {\n      created_at: now,\n      kind: 24242,\n      content: 'blossom stuff',\n      tags: [['expiration', String(now + 60)]],\n    }\n\n    if (modify) {\n      modify(event)\n    }\n\n    try {\n      const signedEvent = await this.signer.signEvent(event)\n      const eventJson = JSON.stringify(signedEvent)\n      return 'Nostr ' + btoa(eventJson)\n    } catch (error) {\n      return ''\n    }\n  }\n\n  private isValid32ByteHex(hash: string): boolean {\n    return /^[a-f0-9]{64}$/i.test(hash)\n  }\n\n  async check(hash: string): Promise<void> {\n    if (!this.isValid32ByteHex(hash)) {\n      throw new Error(`${hash} is not a valid 32-byte hex string`)\n    }\n\n    try {\n      await this.httpCall('HEAD', hash)\n    } catch (error) {\n      throw new Error(`failed to check for ${hash}: ${error}`)\n    }\n  }\n\n  async uploadBlob(file: File | Blob, contentType?: string): Promise<BlobDescriptor> {\n    const hash = bytesToHex(sha256(new Uint8Array(await file.arrayBuffer())))\n    const actualContentType = contentType || file.type || 'application/octet-stream'\n\n    const bd = await this.httpCall(\n      'PUT',\n      'upload',\n      actualContentType,\n      () =>\n        this.authorizationHeader(evt => {\n          evt.tags.push(['t', 'upload'])\n          evt.tags.push(['x', hash])\n        }),\n      file,\n      {},\n    )\n\n    return bd\n  }\n\n  async uploadFile(file: File): Promise<BlobDescriptor> {\n    return this.uploadBlob(file, file.type)\n  }\n\n  async download(hash: string): Promise<ArrayBuffer> {\n    if (!this.isValid32ByteHex(hash)) {\n      throw new Error(`${hash} is not a valid 32-byte hex string`)\n    }\n\n    const authHeader = await this.authorizationHeader(evt => {\n      evt.tags.push(['t', 'get'])\n      evt.tags.push(['x', hash])\n    })\n\n    const response = await fetch(this.mediaserver + hash, {\n      method: 'GET',\n      headers: {\n        Authorization: authHeader,\n      },\n    })\n\n    if (response.status >= 300) {\n      throw new Error(`${hash} is not present in ${this.mediaserver}: ${response.status}`)\n    }\n\n    return await response.arrayBuffer()\n  }\n\n  async downloadAsBlob(hash: string): Promise<Blob> {\n    const arrayBuffer = await this.download(hash)\n    return new Blob([arrayBuffer])\n  }\n\n  async list(): Promise<BlobDescriptor[]> {\n    const pubkey = await this.signer.getPublicKey()\n\n    if (!this.isValid32ByteHex(pubkey)) {\n      throw new Error(`pubkey ${pubkey} is not valid`)\n    }\n\n    try {\n      const bds = await this.httpCall(\n        'GET',\n        `list/${pubkey}`,\n        undefined,\n        () =>\n          this.authorizationHeader(evt => {\n            evt.tags.push(['t', 'list'])\n          }),\n        undefined,\n        [],\n      )\n      return bds\n    } catch (error) {\n      throw new Error(`failed to list blobs: ${error}`)\n    }\n  }\n\n  async delete(hash: string): Promise<void> {\n    if (!this.isValid32ByteHex(hash)) {\n      throw new Error(`${hash} is not a valid 32-byte hex string`)\n    }\n\n    try {\n      await this.httpCall(\n        'DELETE',\n        hash,\n        undefined,\n        () =>\n          this.authorizationHeader(evt => {\n            evt.tags.push(['t', 'delete'])\n            evt.tags.push(['x', hash])\n          }),\n        undefined,\n        null,\n      )\n    } catch (error) {\n      throw new Error(`failed to delete ${hash}: ${error}`)\n    }\n  }\n}\n", "import type { Event } from './core.ts'\n\nexport const utf8Decoder: TextDecoder = new TextDecoder('utf-8')\nexport const utf8Encoder: TextEncoder = new TextEncoder()\n\nexport { bytesToHex, hexToBytes } from '@noble/hashes/utils.js'\n\nexport function normalizeURL(url: string): string {\n  try {\n    if (url.indexOf('://') === -1) url = 'wss://' + url\n    let p = new URL(url)\n    if (p.protocol === 'http:') p.protocol = 'ws:'\n    else if (p.protocol === 'https:') p.protocol = 'wss:'\n    p.pathname = p.pathname.replace(/\\/+/g, '/')\n    if (p.pathname.endsWith('/')) p.pathname = p.pathname.slice(0, -1)\n    if ((p.port === '80' && p.protocol === 'ws:') || (p.port === '443' && p.protocol === 'wss:')) p.port = ''\n    p.searchParams.sort()\n    p.hash = ''\n    return p.toString()\n  } catch (e) {\n    throw new Error(`Invalid URL: ${url}`)\n  }\n}\n\nexport function insertEventIntoDescendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return b.created_at - event.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function insertEventIntoAscendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return event.created_at - b.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function binarySearch<T>(arr: T[], compare: (b: T) => number): [number, boolean] {\n  let start = 0\n  let end = arr.length - 1\n\n  while (start <= end) {\n    const mid = Math.floor((start + end) / 2)\n    const cmp = compare(arr[mid])\n\n    if (cmp === 0) {\n      return [mid, true]\n    }\n\n    if (cmp < 0) {\n      end = mid - 1\n    } else {\n      start = mid + 1\n    }\n  }\n\n  return [start, false]\n}\n\nexport class QueueNode<V> {\n  public value: V\n  public next: QueueNode<V> | null = null\n  public prev: QueueNode<V> | null = null\n\n  constructor(message: V) {\n    this.value = message\n  }\n}\n\nexport class Queue<V> {\n  public first: QueueNode<V> | null\n  public last: QueueNode<V> | null\n\n  constructor() {\n    this.first = null\n    this.last = null\n  }\n\n  enqueue(value: V): boolean {\n    const newNode = new QueueNode(value)\n    if (!this.last) {\n      // list is empty\n      this.first = newNode\n      this.last = newNode\n    } else if (this.last === this.first) {\n      // list has a single element\n      this.last = newNode\n      this.last.prev = this.first\n      this.first.next = newNode\n    } else {\n      // list has elements, add as last\n      newNode.prev = this.last\n      this.last.next = newNode\n      this.last = newNode\n    }\n    return true\n  }\n\n  dequeue(): V | null {\n    if (!this.first) return null\n\n    if (this.first === this.last) {\n      const target = this.first\n      this.first = null\n      this.last = null\n      return target.value\n    }\n\n    const target = this.first\n    this.first = target.next\n    if (this.first) {\n      this.first.prev = null // fix: clean up prev pointer\n    }\n\n    return target.value\n  }\n}\n"],
+  "mappings": "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",
   "names": []
 }
Index: package/lib/esm/nipb7.js.map
===================================================================
--- package/lib/esm/nipb7.js.map
+++ package/lib/esm/nipb7.js.map
@@ -1,7 +1,7 @@
 {
   "version": 3,
   "sources": ["../../nipb7.ts", "../../utils.ts"],
-  "sourcesContent": ["import { sha256 } from '@noble/hashes/sha256'\nimport { EventTemplate } from './core.ts'\nimport { Signer } from './signer.ts'\nimport { bytesToHex } from './utils.ts'\n\nexport type BlobDescriptor = {\n  url: string\n  sha256: string\n  size: number\n  type: string\n  uploaded: number\n}\n\nexport class BlossomClient {\n  private mediaserver: string\n  private signer: Signer\n\n  constructor(mediaserver: string, signer: Signer) {\n    if (!mediaserver.startsWith('http')) {\n      mediaserver = 'https://' + mediaserver\n    }\n    this.mediaserver = mediaserver.replace(/\\/$/, '') + '/'\n    this.signer = signer\n  }\n\n  private async httpCall(\n    method: string,\n    url: string,\n    contentType?: string,\n    addAuthorization?: () => Promise<string>,\n    body?: File | Blob,\n    result?: any,\n  ): Promise<any> {\n    const headers: { [_: string]: string } = {}\n\n    if (contentType) {\n      headers['Content-Type'] = contentType\n    }\n\n    if (addAuthorization) {\n      const auth = await addAuthorization()\n      if (auth) {\n        headers['Authorization'] = auth\n      }\n    }\n\n    const response = await fetch(this.mediaserver + url, {\n      method,\n      headers,\n      body,\n    })\n\n    if (response.status >= 300) {\n      const reason = response.headers.get('X-Reason') || response.statusText\n      throw new Error(`${url} returned an error (${response.status}): ${reason}`)\n    }\n\n    if (result !== null && response.headers.get('content-type')?.includes('application/json')) {\n      return await response.json()\n    }\n\n    return response\n  }\n\n  private async authorizationHeader(modify?: (event: EventTemplate) => void): Promise<string> {\n    const now = Math.floor(Date.now() / 1000)\n    const event: EventTemplate = {\n      created_at: now,\n      kind: 24242,\n      content: 'blossom stuff',\n      tags: [['expiration', String(now + 60)]],\n    }\n\n    if (modify) {\n      modify(event)\n    }\n\n    try {\n      const signedEvent = await this.signer.signEvent(event)\n      const eventJson = JSON.stringify(signedEvent)\n      return 'Nostr ' + btoa(eventJson)\n    } catch (error) {\n      return ''\n    }\n  }\n\n  private isValid32ByteHex(hash: string): boolean {\n    return /^[a-f0-9]{64}$/i.test(hash)\n  }\n\n  async check(hash: string): Promise<void> {\n    if (!this.isValid32ByteHex(hash)) {\n      throw new Error(`${hash} is not a valid 32-byte hex string`)\n    }\n\n    try {\n      await this.httpCall('HEAD', hash)\n    } catch (error) {\n      throw new Error(`failed to check for ${hash}: ${error}`)\n    }\n  }\n\n  async uploadBlob(file: File | Blob, contentType?: string): Promise<BlobDescriptor> {\n    const hash = bytesToHex(sha256(new Uint8Array(await file.arrayBuffer())))\n    const actualContentType = contentType || file.type || 'application/octet-stream'\n\n    const bd = await this.httpCall(\n      'PUT',\n      'upload',\n      actualContentType,\n      () =>\n        this.authorizationHeader(evt => {\n          evt.tags.push(['t', 'upload'])\n          evt.tags.push(['x', hash])\n        }),\n      file,\n      {},\n    )\n\n    return bd\n  }\n\n  async uploadFile(file: File): Promise<BlobDescriptor> {\n    return this.uploadBlob(file, file.type)\n  }\n\n  async download(hash: string): Promise<ArrayBuffer> {\n    if (!this.isValid32ByteHex(hash)) {\n      throw new Error(`${hash} is not a valid 32-byte hex string`)\n    }\n\n    const authHeader = await this.authorizationHeader(evt => {\n      evt.tags.push(['t', 'get'])\n      evt.tags.push(['x', hash])\n    })\n\n    const response = await fetch(this.mediaserver + hash, {\n      method: 'GET',\n      headers: {\n        Authorization: authHeader,\n      },\n    })\n\n    if (response.status >= 300) {\n      throw new Error(`${hash} is not present in ${this.mediaserver}: ${response.status}`)\n    }\n\n    return await response.arrayBuffer()\n  }\n\n  async downloadAsBlob(hash: string): Promise<Blob> {\n    const arrayBuffer = await this.download(hash)\n    return new Blob([arrayBuffer])\n  }\n\n  async list(): Promise<BlobDescriptor[]> {\n    const pubkey = await this.signer.getPublicKey()\n\n    if (!this.isValid32ByteHex(pubkey)) {\n      throw new Error(`pubkey ${pubkey} is not valid`)\n    }\n\n    try {\n      const bds = await this.httpCall(\n        'GET',\n        `list/${pubkey}`,\n        undefined,\n        () =>\n          this.authorizationHeader(evt => {\n            evt.tags.push(['t', 'list'])\n          }),\n        undefined,\n        [],\n      )\n      return bds\n    } catch (error) {\n      throw new Error(`failed to list blobs: ${error}`)\n    }\n  }\n\n  async delete(hash: string): Promise<void> {\n    if (!this.isValid32ByteHex(hash)) {\n      throw new Error(`${hash} is not a valid 32-byte hex string`)\n    }\n\n    try {\n      await this.httpCall(\n        'DELETE',\n        hash,\n        undefined,\n        () =>\n          this.authorizationHeader(evt => {\n            evt.tags.push(['t', 'delete'])\n            evt.tags.push(['x', hash])\n          }),\n        undefined,\n        null,\n      )\n    } catch (error) {\n      throw new Error(`failed to delete ${hash}: ${error}`)\n    }\n  }\n}\n", "import type { Event } from './core.ts'\n\nexport const utf8Decoder: TextDecoder = new TextDecoder('utf-8')\nexport const utf8Encoder: TextEncoder = new TextEncoder()\n\nexport { bytesToHex, hexToBytes } from '@noble/hashes/utils'\n\nexport function normalizeURL(url: string): string {\n  try {\n    if (url.indexOf('://') === -1) url = 'wss://' + url\n    let p = new URL(url)\n    if (p.protocol === 'http:') p.protocol = 'ws:'\n    else if (p.protocol === 'https:') p.protocol = 'wss:'\n    p.pathname = p.pathname.replace(/\\/+/g, '/')\n    if (p.pathname.endsWith('/')) p.pathname = p.pathname.slice(0, -1)\n    if ((p.port === '80' && p.protocol === 'ws:') || (p.port === '443' && p.protocol === 'wss:')) p.port = ''\n    p.searchParams.sort()\n    p.hash = ''\n    return p.toString()\n  } catch (e) {\n    throw new Error(`Invalid URL: ${url}`)\n  }\n}\n\nexport function insertEventIntoDescendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return b.created_at - event.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function insertEventIntoAscendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return event.created_at - b.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function binarySearch<T>(arr: T[], compare: (b: T) => number): [number, boolean] {\n  let start = 0\n  let end = arr.length - 1\n\n  while (start <= end) {\n    const mid = Math.floor((start + end) / 2)\n    const cmp = compare(arr[mid])\n\n    if (cmp === 0) {\n      return [mid, true]\n    }\n\n    if (cmp < 0) {\n      end = mid - 1\n    } else {\n      start = mid + 1\n    }\n  }\n\n  return [start, false]\n}\n\nexport class QueueNode<V> {\n  public value: V\n  public next: QueueNode<V> | null = null\n  public prev: QueueNode<V> | null = null\n\n  constructor(message: V) {\n    this.value = message\n  }\n}\n\nexport class Queue<V> {\n  public first: QueueNode<V> | null\n  public last: QueueNode<V> | null\n\n  constructor() {\n    this.first = null\n    this.last = null\n  }\n\n  enqueue(value: V): boolean {\n    const newNode = new QueueNode(value)\n    if (!this.last) {\n      // list is empty\n      this.first = newNode\n      this.last = newNode\n    } else if (this.last === this.first) {\n      // list has a single element\n      this.last = newNode\n      this.last.prev = this.first\n      this.first.next = newNode\n    } else {\n      // list has elements, add as last\n      newNode.prev = this.last\n      this.last.next = newNode\n      this.last = newNode\n    }\n    return true\n  }\n\n  dequeue(): V | null {\n    if (!this.first) return null\n\n    if (this.first === this.last) {\n      const target = this.first\n      this.first = null\n      this.last = null\n      return target.value\n    }\n\n    const target = this.first\n    this.first = target.next\n    if (this.first) {\n      this.first.prev = null // fix: clean up prev pointer\n    }\n\n    return target.value\n  }\n}\n"],
+  "sourcesContent": ["import { sha256 } from '@noble/hashes/sha2.js'\nimport { EventTemplate } from './core.ts'\nimport { Signer } from './signer.ts'\nimport { bytesToHex } from './utils.ts'\n\nexport type BlobDescriptor = {\n  url: string\n  sha256: string\n  size: number\n  type: string\n  uploaded: number\n}\n\nexport class BlossomClient {\n  private mediaserver: string\n  private signer: Signer\n\n  constructor(mediaserver: string, signer: Signer) {\n    if (!mediaserver.startsWith('http')) {\n      mediaserver = 'https://' + mediaserver\n    }\n    this.mediaserver = mediaserver.replace(/\\/$/, '') + '/'\n    this.signer = signer\n  }\n\n  private async httpCall(\n    method: string,\n    url: string,\n    contentType?: string,\n    addAuthorization?: () => Promise<string>,\n    body?: File | Blob,\n    result?: any,\n  ): Promise<any> {\n    const headers: { [_: string]: string } = {}\n\n    if (contentType) {\n      headers['Content-Type'] = contentType\n    }\n\n    if (addAuthorization) {\n      const auth = await addAuthorization()\n      if (auth) {\n        headers['Authorization'] = auth\n      }\n    }\n\n    const response = await fetch(this.mediaserver + url, {\n      method,\n      headers,\n      body,\n    })\n\n    if (response.status >= 300) {\n      const reason = response.headers.get('X-Reason') || response.statusText\n      throw new Error(`${url} returned an error (${response.status}): ${reason}`)\n    }\n\n    if (result !== null && response.headers.get('content-type')?.includes('application/json')) {\n      return await response.json()\n    }\n\n    return response\n  }\n\n  private async authorizationHeader(modify?: (event: EventTemplate) => void): Promise<string> {\n    const now = Math.floor(Date.now() / 1000)\n    const event: EventTemplate = {\n      created_at: now,\n      kind: 24242,\n      content: 'blossom stuff',\n      tags: [['expiration', String(now + 60)]],\n    }\n\n    if (modify) {\n      modify(event)\n    }\n\n    try {\n      const signedEvent = await this.signer.signEvent(event)\n      const eventJson = JSON.stringify(signedEvent)\n      return 'Nostr ' + btoa(eventJson)\n    } catch (error) {\n      return ''\n    }\n  }\n\n  private isValid32ByteHex(hash: string): boolean {\n    return /^[a-f0-9]{64}$/i.test(hash)\n  }\n\n  async check(hash: string): Promise<void> {\n    if (!this.isValid32ByteHex(hash)) {\n      throw new Error(`${hash} is not a valid 32-byte hex string`)\n    }\n\n    try {\n      await this.httpCall('HEAD', hash)\n    } catch (error) {\n      throw new Error(`failed to check for ${hash}: ${error}`)\n    }\n  }\n\n  async uploadBlob(file: File | Blob, contentType?: string): Promise<BlobDescriptor> {\n    const hash = bytesToHex(sha256(new Uint8Array(await file.arrayBuffer())))\n    const actualContentType = contentType || file.type || 'application/octet-stream'\n\n    const bd = await this.httpCall(\n      'PUT',\n      'upload',\n      actualContentType,\n      () =>\n        this.authorizationHeader(evt => {\n          evt.tags.push(['t', 'upload'])\n          evt.tags.push(['x', hash])\n        }),\n      file,\n      {},\n    )\n\n    return bd\n  }\n\n  async uploadFile(file: File): Promise<BlobDescriptor> {\n    return this.uploadBlob(file, file.type)\n  }\n\n  async download(hash: string): Promise<ArrayBuffer> {\n    if (!this.isValid32ByteHex(hash)) {\n      throw new Error(`${hash} is not a valid 32-byte hex string`)\n    }\n\n    const authHeader = await this.authorizationHeader(evt => {\n      evt.tags.push(['t', 'get'])\n      evt.tags.push(['x', hash])\n    })\n\n    const response = await fetch(this.mediaserver + hash, {\n      method: 'GET',\n      headers: {\n        Authorization: authHeader,\n      },\n    })\n\n    if (response.status >= 300) {\n      throw new Error(`${hash} is not present in ${this.mediaserver}: ${response.status}`)\n    }\n\n    return await response.arrayBuffer()\n  }\n\n  async downloadAsBlob(hash: string): Promise<Blob> {\n    const arrayBuffer = await this.download(hash)\n    return new Blob([arrayBuffer])\n  }\n\n  async list(): Promise<BlobDescriptor[]> {\n    const pubkey = await this.signer.getPublicKey()\n\n    if (!this.isValid32ByteHex(pubkey)) {\n      throw new Error(`pubkey ${pubkey} is not valid`)\n    }\n\n    try {\n      const bds = await this.httpCall(\n        'GET',\n        `list/${pubkey}`,\n        undefined,\n        () =>\n          this.authorizationHeader(evt => {\n            evt.tags.push(['t', 'list'])\n          }),\n        undefined,\n        [],\n      )\n      return bds\n    } catch (error) {\n      throw new Error(`failed to list blobs: ${error}`)\n    }\n  }\n\n  async delete(hash: string): Promise<void> {\n    if (!this.isValid32ByteHex(hash)) {\n      throw new Error(`${hash} is not a valid 32-byte hex string`)\n    }\n\n    try {\n      await this.httpCall(\n        'DELETE',\n        hash,\n        undefined,\n        () =>\n          this.authorizationHeader(evt => {\n            evt.tags.push(['t', 'delete'])\n            evt.tags.push(['x', hash])\n          }),\n        undefined,\n        null,\n      )\n    } catch (error) {\n      throw new Error(`failed to delete ${hash}: ${error}`)\n    }\n  }\n}\n", "import type { Event } from './core.ts'\n\nexport const utf8Decoder: TextDecoder = new TextDecoder('utf-8')\nexport const utf8Encoder: TextEncoder = new TextEncoder()\n\nexport { bytesToHex, hexToBytes } from '@noble/hashes/utils.js'\n\nexport function normalizeURL(url: string): string {\n  try {\n    if (url.indexOf('://') === -1) url = 'wss://' + url\n    let p = new URL(url)\n    if (p.protocol === 'http:') p.protocol = 'ws:'\n    else if (p.protocol === 'https:') p.protocol = 'wss:'\n    p.pathname = p.pathname.replace(/\\/+/g, '/')\n    if (p.pathname.endsWith('/')) p.pathname = p.pathname.slice(0, -1)\n    if ((p.port === '80' && p.protocol === 'ws:') || (p.port === '443' && p.protocol === 'wss:')) p.port = ''\n    p.searchParams.sort()\n    p.hash = ''\n    return p.toString()\n  } catch (e) {\n    throw new Error(`Invalid URL: ${url}`)\n  }\n}\n\nexport function insertEventIntoDescendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return b.created_at - event.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function insertEventIntoAscendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return event.created_at - b.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function binarySearch<T>(arr: T[], compare: (b: T) => number): [number, boolean] {\n  let start = 0\n  let end = arr.length - 1\n\n  while (start <= end) {\n    const mid = Math.floor((start + end) / 2)\n    const cmp = compare(arr[mid])\n\n    if (cmp === 0) {\n      return [mid, true]\n    }\n\n    if (cmp < 0) {\n      end = mid - 1\n    } else {\n      start = mid + 1\n    }\n  }\n\n  return [start, false]\n}\n\nexport class QueueNode<V> {\n  public value: V\n  public next: QueueNode<V> | null = null\n  public prev: QueueNode<V> | null = null\n\n  constructor(message: V) {\n    this.value = message\n  }\n}\n\nexport class Queue<V> {\n  public first: QueueNode<V> | null\n  public last: QueueNode<V> | null\n\n  constructor() {\n    this.first = null\n    this.last = null\n  }\n\n  enqueue(value: V): boolean {\n    const newNode = new QueueNode(value)\n    if (!this.last) {\n      // list is empty\n      this.first = newNode\n      this.last = newNode\n    } else if (this.last === this.first) {\n      // list has a single element\n      this.last = newNode\n      this.last.prev = this.first\n      this.first.next = newNode\n    } else {\n      // list has elements, add as last\n      newNode.prev = this.last\n      this.last.next = newNode\n      this.last = newNode\n    }\n    return true\n  }\n\n  dequeue(): V | null {\n    if (!this.first) return null\n\n    if (this.first === this.last) {\n      const target = this.first\n      this.first = null\n      this.last = null\n      return target.value\n    }\n\n    const target = this.first\n    this.first = target.next\n    if (this.first) {\n      this.first.prev = null // fix: clean up prev pointer\n    }\n\n    return target.value\n  }\n}\n"],
   "mappings": "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",
   "names": []
 }
File too large for inline diff
Index: package/lib/cjs/pool.js.map
===================================================================
--- package/lib/cjs/pool.js.map
+++ package/lib/cjs/pool.js.map
@@ -1,7 +1,7 @@
 {
   "version": 3,
   "sources": ["../../pool.ts", "../../pure.ts", "../../core.ts", "../../utils.ts", "../../kinds.ts", "../../filter.ts", "../../fakejson.ts", "../../nip42.ts", "../../helpers.ts", "../../abstract-relay.ts", "../../abstract-pool.ts"],
-  "sourcesContent": ["/* global WebSocket */\n\nimport { verifyEvent } from './pure.ts'\nimport { AbstractSimplePool, type AbstractPoolConstructorOptions } from './abstract-pool.ts'\n\nvar _WebSocket: typeof WebSocket\n\ntry {\n  _WebSocket = WebSocket\n} catch {}\n\nexport function useWebSocketImplementation(websocketImplementation: any) {\n  _WebSocket = websocketImplementation\n}\n\nexport class SimplePool extends AbstractSimplePool {\n  constructor(options?: Pick<AbstractPoolConstructorOptions, 'enablePing' | 'enableReconnect'>) {\n    super({ verifyEvent, websocketImplementation: _WebSocket, ...options })\n  }\n}\n\nexport * from './abstract-pool.ts'\n", "import { schnorr } from '@noble/curves/secp256k1'\nimport { bytesToHex } from '@noble/hashes/utils'\nimport { Nostr, Event, EventTemplate, UnsignedEvent, VerifiedEvent, verifiedSymbol, validateEvent } from './core.ts'\nimport { sha256 } from '@noble/hashes/sha256'\n\nimport { utf8Encoder } from './utils.ts'\n\nclass JS implements Nostr {\n  generateSecretKey(): Uint8Array {\n    return schnorr.utils.randomPrivateKey()\n  }\n  getPublicKey(secretKey: Uint8Array): string {\n    return bytesToHex(schnorr.getPublicKey(secretKey))\n  }\n  finalizeEvent(t: EventTemplate, secretKey: Uint8Array): VerifiedEvent {\n    const event = t as VerifiedEvent\n    event.pubkey = bytesToHex(schnorr.getPublicKey(secretKey))\n    event.id = getEventHash(event)\n    event.sig = bytesToHex(schnorr.sign(getEventHash(event), secretKey))\n    event[verifiedSymbol] = true\n    return event\n  }\n  verifyEvent(event: Event): event is VerifiedEvent {\n    if (typeof event[verifiedSymbol] === 'boolean') return event[verifiedSymbol]\n\n    const hash = getEventHash(event)\n    if (hash !== event.id) {\n      event[verifiedSymbol] = false\n      return false\n    }\n\n    try {\n      const valid = schnorr.verify(event.sig, hash, event.pubkey)\n      event[verifiedSymbol] = valid\n      return valid\n    } catch (err) {\n      event[verifiedSymbol] = false\n      return false\n    }\n  }\n}\n\nexport function serializeEvent(evt: UnsignedEvent): string {\n  if (!validateEvent(evt)) throw new Error(\"can't serialize event with wrong or missing properties\")\n  return JSON.stringify([0, evt.pubkey, evt.created_at, evt.kind, evt.tags, evt.content])\n}\n\nexport function getEventHash(event: UnsignedEvent): string {\n  let eventHash = sha256(utf8Encoder.encode(serializeEvent(event)))\n  return bytesToHex(eventHash)\n}\n\nconst i: JS = new JS()\n\nexport const generateSecretKey = i.generateSecretKey\nexport const getPublicKey = i.getPublicKey\nexport const finalizeEvent = i.finalizeEvent\nexport const verifyEvent = i.verifyEvent\nexport * from './core.ts'\n", "export interface Nostr {\n  generateSecretKey(): Uint8Array\n  getPublicKey(secretKey: Uint8Array): string\n  finalizeEvent(event: EventTemplate, secretKey: Uint8Array): VerifiedEvent\n  verifyEvent(event: Event): event is VerifiedEvent\n}\n\n/** Designates a verified event signature. */\nexport const verifiedSymbol = Symbol('verified')\n\nexport type NostrEvent = {\n  kind: number\n  tags: string[][]\n  content: string\n  created_at: number\n  pubkey: string\n  id: string\n  sig: string\n  [verifiedSymbol]?: boolean\n}\n\nexport type Event = NostrEvent\nexport type EventTemplate = Pick<Event, 'kind' | 'tags' | 'content' | 'created_at'>\nexport type UnsignedEvent = Pick<Event, 'kind' | 'tags' | 'content' | 'created_at' | 'pubkey'>\n\n/** An event whose signature has been verified. */\nexport interface VerifiedEvent extends Event {\n  [verifiedSymbol]: true\n}\n\nconst isRecord = (obj: unknown): obj is Record<string, unknown> => obj instanceof Object\n\nexport function validateEvent<T>(event: T): event is T & UnsignedEvent {\n  if (!isRecord(event)) return false\n  if (typeof event.kind !== 'number') return false\n  if (typeof event.content !== 'string') return false\n  if (typeof event.created_at !== 'number') return false\n  if (typeof event.pubkey !== 'string') return false\n  if (!event.pubkey.match(/^[a-f0-9]{64}$/)) return false\n\n  if (!Array.isArray(event.tags)) return false\n  for (let i = 0; i < event.tags.length; i++) {\n    let tag = event.tags[i]\n    if (!Array.isArray(tag)) return false\n    for (let j = 0; j < tag.length; j++) {\n      if (typeof tag[j] !== 'string') return false\n    }\n  }\n\n  return true\n}\n\n/**\n * Sort events in reverse-chronological order by the `created_at` timestamp,\n * and then by the event `id` (lexicographically) in case of ties.\n * This mutates the array.\n */\nexport function sortEvents(events: Event[]): Event[] {\n  return events.sort((a: NostrEvent, b: NostrEvent): number => {\n    if (a.created_at !== b.created_at) {\n      return b.created_at - a.created_at\n    }\n    return a.id.localeCompare(b.id)\n  })\n}\n", "import type { Event } from './core.ts'\n\nexport const utf8Decoder: TextDecoder = new TextDecoder('utf-8')\nexport const utf8Encoder: TextEncoder = new TextEncoder()\n\nexport { bytesToHex, hexToBytes } from '@noble/hashes/utils'\n\nexport function normalizeURL(url: string): string {\n  try {\n    if (url.indexOf('://') === -1) url = 'wss://' + url\n    let p = new URL(url)\n    if (p.protocol === 'http:') p.protocol = 'ws:'\n    else if (p.protocol === 'https:') p.protocol = 'wss:'\n    p.pathname = p.pathname.replace(/\\/+/g, '/')\n    if (p.pathname.endsWith('/')) p.pathname = p.pathname.slice(0, -1)\n    if ((p.port === '80' && p.protocol === 'ws:') || (p.port === '443' && p.protocol === 'wss:')) p.port = ''\n    p.searchParams.sort()\n    p.hash = ''\n    return p.toString()\n  } catch (e) {\n    throw new Error(`Invalid URL: ${url}`)\n  }\n}\n\nexport function insertEventIntoDescendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return b.created_at - event.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function insertEventIntoAscendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return event.created_at - b.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function binarySearch<T>(arr: T[], compare: (b: T) => number): [number, boolean] {\n  let start = 0\n  let end = arr.length - 1\n\n  while (start <= end) {\n    const mid = Math.floor((start + end) / 2)\n    const cmp = compare(arr[mid])\n\n    if (cmp === 0) {\n      return [mid, true]\n    }\n\n    if (cmp < 0) {\n      end = mid - 1\n    } else {\n      start = mid + 1\n    }\n  }\n\n  return [start, false]\n}\n\nexport class QueueNode<V> {\n  public value: V\n  public next: QueueNode<V> | null = null\n  public prev: QueueNode<V> | null = null\n\n  constructor(message: V) {\n    this.value = message\n  }\n}\n\nexport class Queue<V> {\n  public first: QueueNode<V> | null\n  public last: QueueNode<V> | null\n\n  constructor() {\n    this.first = null\n    this.last = null\n  }\n\n  enqueue(value: V): boolean {\n    const newNode = new QueueNode(value)\n    if (!this.last) {\n      // list is empty\n      this.first = newNode\n      this.last = newNode\n    } else if (this.last === this.first) {\n      // list has a single element\n      this.last = newNode\n      this.last.prev = this.first\n      this.first.next = newNode\n    } else {\n      // list has elements, add as last\n      newNode.prev = this.last\n      this.last.next = newNode\n      this.last = newNode\n    }\n    return true\n  }\n\n  dequeue(): V | null {\n    if (!this.first) return null\n\n    if (this.first === this.last) {\n      const target = this.first\n      this.first = null\n      this.last = null\n      return target.value\n    }\n\n    const target = this.first\n    this.first = target.next\n    if (this.first) {\n      this.first.prev = null // fix: clean up prev pointer\n    }\n\n    return target.value\n  }\n}\n", "import { NostrEvent, validateEvent } from './pure.ts'\n\n/** Events are **regular**, which means they're all expected to be stored by relays. */\nexport function isRegularKind(kind: number): boolean {\n  return kind < 10000 && kind !== 0 && kind !== 3\n}\n\n/** Events are **replaceable**, which means that, for each combination of `pubkey` and `kind`, only the latest event is expected to (SHOULD) be stored by relays, older versions are expected to be discarded. */\nexport function isReplaceableKind(kind: number): boolean {\n  return kind === 0 || kind === 3 || (10000 <= kind && kind < 20000)\n}\n\n/** Events are **ephemeral**, which means they are not expected to be stored by relays. */\nexport function isEphemeralKind(kind: number): boolean {\n  return 20000 <= kind && kind < 30000\n}\n\n/** Events are **addressable**, which means that, for each combination of `pubkey`, `kind` and the `d` tag, only the latest event is expected to be stored by relays, older versions are expected to be discarded. */\nexport function isAddressableKind(kind: number): boolean {\n  return 30000 <= kind && kind < 40000\n}\n\n/** Classification of the event kind. */\nexport type KindClassification = 'regular' | 'replaceable' | 'ephemeral' | 'parameterized' | 'unknown'\n\n/** Determine the classification of this kind of event if known, or `unknown`. */\nexport function classifyKind(kind: number): KindClassification {\n  if (isRegularKind(kind)) return 'regular'\n  if (isReplaceableKind(kind)) return 'replaceable'\n  if (isEphemeralKind(kind)) return 'ephemeral'\n  if (isAddressableKind(kind)) return 'parameterized'\n  return 'unknown'\n}\n\nexport function isKind<T extends number>(event: unknown, kind: T | Array<T>): event is NostrEvent & { kind: T } {\n  const kindAsArray: number[] = kind instanceof Array ? kind : [kind]\n  return (validateEvent(event) && kindAsArray.includes(event.kind)) || false\n}\n\nexport const Metadata = 0\nexport type Metadata = typeof Metadata\nexport const ShortTextNote = 1\nexport type ShortTextNote = typeof ShortTextNote\nexport const RecommendRelay = 2\nexport type RecommendRelay = typeof RecommendRelay\nexport const Contacts = 3\nexport type Contacts = typeof Contacts\nexport const EncryptedDirectMessage = 4\nexport type EncryptedDirectMessage = typeof EncryptedDirectMessage\nexport const EventDeletion = 5\nexport type EventDeletion = typeof EventDeletion\nexport const Repost = 6\nexport type Repost = typeof Repost\nexport const Reaction = 7\nexport type Reaction = typeof Reaction\nexport const BadgeAward = 8\nexport type BadgeAward = typeof BadgeAward\nexport const ChatMessage = 9\nexport type ChatMessage = typeof ChatMessage\nexport const ForumThread = 11\nexport type ForumThread = typeof ForumThread\nexport const Seal = 13\nexport type Seal = typeof Seal\nexport const PrivateDirectMessage = 14\nexport type PrivateDirectMessage = typeof PrivateDirectMessage\nexport const FileMessage = 15\nexport type FileMessage = typeof FileMessage\nexport const GenericRepost = 16\nexport type GenericRepost = typeof GenericRepost\nexport const Photo = 20\nexport type Photo = typeof Photo\nexport const NormalVideo = 21\nexport type NormalVideo = typeof NormalVideo\nexport const ShortVideo = 22\nexport type ShortVideo = typeof ShortVideo\nexport const ChannelCreation = 40\nexport type ChannelCreation = typeof ChannelCreation\nexport const ChannelMetadata = 41\nexport type ChannelMetadata = typeof ChannelMetadata\nexport const ChannelMessage = 42\nexport type ChannelMessage = typeof ChannelMessage\nexport const ChannelHideMessage = 43\nexport type ChannelHideMessage = typeof ChannelHideMessage\nexport const ChannelMuteUser = 44\nexport type ChannelMuteUser = typeof ChannelMuteUser\nexport const OpenTimestamps = 1040\nexport type OpenTimestamps = typeof OpenTimestamps\nexport const GiftWrap = 1059\nexport type GiftWrap = typeof GiftWrap\nexport const Poll = 1068\nexport type Poll = typeof Poll\nexport const FileMetadata = 1063\nexport type FileMetadata = typeof FileMetadata\nexport const Comment = 1111\nexport type Comment = typeof Comment\nexport const LiveChatMessage = 1311\nexport type LiveChatMessage = typeof LiveChatMessage\nexport const Voice = 1222\nexport type Voice = typeof Voice\nexport const VoiceComment = 1244\nexport type VoiceComment = typeof VoiceComment\nexport const ProblemTracker = 1971\nexport type ProblemTracker = typeof ProblemTracker\nexport const Report = 1984\nexport type Report = typeof Report\nexport const Reporting = 1984\nexport type Reporting = typeof Reporting\nexport const Label = 1985\nexport type Label = typeof Label\nexport const CommunityPostApproval = 4550\nexport type CommunityPostApproval = typeof CommunityPostApproval\nexport const JobRequest = 5999\nexport type JobRequest = typeof JobRequest\nexport const JobResult = 6999\nexport type JobResult = typeof JobResult\nexport const JobFeedback = 7000\nexport type JobFeedback = typeof JobFeedback\nexport const ZapGoal = 9041\nexport type ZapGoal = typeof ZapGoal\nexport const ZapRequest = 9734\nexport type ZapRequest = typeof ZapRequest\nexport const Zap = 9735\nexport type Zap = typeof Zap\nexport const Highlights = 9802\nexport type Highlights = typeof Highlights\nexport const PollResponse = 1018\nexport type PollResponse = typeof PollResponse\nexport const Mutelist = 10000\nexport type Mutelist = typeof Mutelist\nexport const Pinlist = 10001\nexport type Pinlist = typeof Pinlist\nexport const RelayList = 10002\nexport type RelayList = typeof RelayList\nexport const BookmarkList = 10003\nexport type BookmarkList = typeof BookmarkList\nexport const CommunitiesList = 10004\nexport type CommunitiesList = typeof CommunitiesList\nexport const PublicChatsList = 10005\nexport type PublicChatsList = typeof PublicChatsList\nexport const BlockedRelaysList = 10006\nexport type BlockedRelaysList = typeof BlockedRelaysList\nexport const SearchRelaysList = 10007\nexport type SearchRelaysList = typeof SearchRelaysList\nexport const FavoriteRelays = 10012\nexport type FavoriteRelays = typeof FavoriteRelays\nexport const InterestsList = 10015\nexport type InterestsList = typeof InterestsList\nexport const UserEmojiList = 10030\nexport type UserEmojiList = typeof UserEmojiList\nexport const DirectMessageRelaysList = 10050\nexport type DirectMessageRelaysList = typeof DirectMessageRelaysList\nexport const FileServerPreference = 10096\nexport type FileServerPreference = typeof FileServerPreference\nexport const BlossomServerList = 10063\nexport type BlossomServerList = typeof BlossomServerList\nexport const NWCWalletInfo = 13194\nexport type NWCWalletInfo = typeof NWCWalletInfo\nexport const LightningPubRPC = 21000\nexport type LightningPubRPC = typeof LightningPubRPC\nexport const ClientAuth = 22242\nexport type ClientAuth = typeof ClientAuth\nexport const NWCWalletRequest = 23194\nexport type NWCWalletRequest = typeof NWCWalletRequest\nexport const NWCWalletResponse = 23195\nexport type NWCWalletResponse = typeof NWCWalletResponse\nexport const NostrConnect = 24133\nexport type NostrConnect = typeof NostrConnect\nexport const HTTPAuth = 27235\nexport type HTTPAuth = typeof HTTPAuth\nexport const Followsets = 30000\nexport type Followsets = typeof Followsets\nexport const Genericlists = 30001\nexport type Genericlists = typeof Genericlists\nexport const Relaysets = 30002\nexport type Relaysets = typeof Relaysets\nexport const Bookmarksets = 30003\nexport type Bookmarksets = typeof Bookmarksets\nexport const Curationsets = 30004\nexport type Curationsets = typeof Curationsets\nexport const ProfileBadges = 30008\nexport type ProfileBadges = typeof ProfileBadges\nexport const BadgeDefinition = 30009\nexport type BadgeDefinition = typeof BadgeDefinition\nexport const Interestsets = 30015\nexport type Interestsets = typeof Interestsets\nexport const CreateOrUpdateStall = 30017\nexport type CreateOrUpdateStall = typeof CreateOrUpdateStall\nexport const CreateOrUpdateProduct = 30018\nexport type CreateOrUpdateProduct = typeof CreateOrUpdateProduct\nexport const LongFormArticle = 30023\nexport type LongFormArticle = typeof LongFormArticle\nexport const DraftLong = 30024\nexport type DraftLong = typeof DraftLong\nexport const Emojisets = 30030\nexport type Emojisets = typeof Emojisets\nexport const Application = 30078\nexport type Application = typeof Application\nexport const LiveEvent = 30311\nexport type LiveEvent = typeof LiveEvent\nexport const UserStatuses = 30315\nexport type UserStatuses = typeof UserStatuses\nexport const ClassifiedListing = 30402\nexport type ClassifiedListing = typeof ClassifiedListing\nexport const DraftClassifiedListing = 30403\nexport type DraftClassifiedListing = typeof DraftClassifiedListing\nexport const Date = 31922\nexport type Date = typeof Date\nexport const Time = 31923\nexport type Time = typeof Time\nexport const Calendar = 31924\nexport type Calendar = typeof Calendar\nexport const CalendarEventRSVP = 31925\nexport type CalendarEventRSVP = typeof CalendarEventRSVP\nexport const RelayReview = 31987\nexport type RelayReview = typeof RelayReview\nexport const Handlerrecommendation = 31989\nexport type Handlerrecommendation = typeof Handlerrecommendation\nexport const Handlerinformation = 31990\nexport type Handlerinformation = typeof Handlerinformation\nexport const CommunityDefinition = 34550\nexport type CommunityDefinition = typeof CommunityDefinition\nexport const GroupMetadata = 39000\nexport type GroupMetadata = typeof GroupMetadata\n", "import { Event } from './core.ts'\nimport { isAddressableKind, isReplaceableKind } from './kinds.ts'\n\nexport type Filter = {\n  ids?: string[]\n  kinds?: number[]\n  authors?: string[]\n  since?: number\n  until?: number\n  limit?: number\n  search?: string\n  [key: `#${string}`]: string[] | undefined\n}\n\nexport function matchFilter(filter: Filter, event: Event): boolean {\n  if (filter.ids && filter.ids.indexOf(event.id) === -1) {\n    return false\n  }\n  if (filter.kinds && filter.kinds.indexOf(event.kind) === -1) {\n    return false\n  }\n  if (filter.authors && filter.authors.indexOf(event.pubkey) === -1) {\n    return false\n  }\n\n  for (let f in filter) {\n    if (f[0] === '#') {\n      let tagName = f.slice(1)\n      let values = filter[`#${tagName}`]\n      if (values && !event.tags.find(([t, v]) => t === f.slice(1) && values!.indexOf(v) !== -1)) return false\n    }\n  }\n\n  if (filter.since && event.created_at < filter.since) return false\n  if (filter.until && event.created_at > filter.until) return false\n\n  return true\n}\n\nexport function matchFilters(filters: Filter[], event: Event): boolean {\n  for (let i = 0; i < filters.length; i++) {\n    if (matchFilter(filters[i], event)) {\n      return true\n    }\n  }\n  return false\n}\n\nexport function mergeFilters(...filters: Filter[]): Filter {\n  let result: Filter = {}\n  for (let i = 0; i < filters.length; i++) {\n    let filter = filters[i]\n    Object.entries(filter).forEach(([property, values]) => {\n      if (property === 'kinds' || property === 'ids' || property === 'authors' || property[0] === '#') {\n        // @ts-ignore\n        result[property] = result[property] || []\n        // @ts-ignore\n        for (let v = 0; v < values.length; v++) {\n          // @ts-ignore\n          let value = values[v]\n          // @ts-ignore\n          if (!result[property].includes(value)) result[property].push(value)\n        }\n      }\n    })\n\n    if (filter.limit && (!result.limit || filter.limit > result.limit)) result.limit = filter.limit\n    if (filter.until && (!result.until || filter.until > result.until)) result.until = filter.until\n    if (filter.since && (!result.since || filter.since < result.since)) result.since = filter.since\n  }\n\n  return result\n}\n\n/**\n * Calculate the intrinsic limit of a filter.\n * This function returns a positive integer, or `Infinity` if there is no intrinsic limit.\n */\nexport function getFilterLimit(filter: Filter): number {\n  if (filter.ids && !filter.ids.length) return 0\n  if (filter.kinds && !filter.kinds.length) return 0\n  if (filter.authors && !filter.authors.length) return 0\n\n  for (const [key, value] of Object.entries(filter)) {\n    if (key[0] === '#' && Array.isArray(value) && !value.length) return 0\n  }\n\n  return Math.min(\n    // The `limit` property creates an artificial limit.\n    Math.max(0, filter.limit ?? Infinity),\n\n    // There can only be one event per `id`.\n    filter.ids?.length ?? Infinity,\n\n    // Replaceable events are limited by the number of authors and kinds.\n    filter.authors?.length && filter.kinds?.every(kind => isReplaceableKind(kind))\n      ? filter.authors.length * filter.kinds.length\n      : Infinity,\n\n    // Parameterized replaceable events are limited by the number of authors, kinds, and \"d\" tags.\n    filter.authors?.length && filter.kinds?.every(kind => isAddressableKind(kind)) && filter['#d']?.length\n      ? filter.authors.length * filter.kinds.length * filter['#d'].length\n      : Infinity,\n  )\n}\n", "export function getHex64(json: string, field: string): string {\n  let len = field.length + 3\n  let idx = json.indexOf(`\"${field}\":`) + len\n  let s = json.slice(idx).indexOf(`\"`) + idx + 1\n  return json.slice(s, s + 64)\n}\n\nexport function getInt(json: string, field: string): number {\n  let len = field.length\n  let idx = json.indexOf(`\"${field}\":`) + len + 3\n  let sliced = json.slice(idx)\n  let end = Math.min(sliced.indexOf(','), sliced.indexOf('}'))\n  return parseInt(sliced.slice(0, end), 10)\n}\n\nexport function getSubscriptionId(json: string): string | null {\n  let idx = json.slice(0, 22).indexOf(`\"EVENT\"`)\n  if (idx === -1) return null\n\n  let pstart = json.slice(idx + 7 + 1).indexOf(`\"`)\n  if (pstart === -1) return null\n  let start = idx + 7 + 1 + pstart\n\n  let pend = json.slice(start + 1, 80).indexOf(`\"`)\n  if (pend === -1) return null\n  let end = start + 1 + pend\n\n  return json.slice(start + 1, end)\n}\n\nexport function matchEventId(json: string, id: string): boolean {\n  return id === getHex64(json, 'id')\n}\n\nexport function matchEventPubkey(json: string, pubkey: string): boolean {\n  return pubkey === getHex64(json, 'pubkey')\n}\n\nexport function matchEventKind(json: string, kind: number): boolean {\n  return kind === getInt(json, 'kind')\n}\n", "import { EventTemplate } from './core.ts'\nimport { ClientAuth } from './kinds.ts'\n\n/**\n * creates an EventTemplate for an AUTH event to be signed.\n */\nexport function makeAuthEvent(relayURL: string, challenge: string): EventTemplate {\n  return {\n    kind: ClientAuth,\n    created_at: Math.floor(Date.now() / 1000),\n    tags: [\n      ['relay', relayURL],\n      ['challenge', challenge],\n    ],\n    content: '',\n  }\n}\n", "import { verifiedSymbol, type Event, type Nostr, VerifiedEvent } from './core.ts'\n\nexport async function yieldThread() {\n  return new Promise<void>((resolve, reject) => {\n    try {\n      // Check if MessageChannel is available\n      if (typeof MessageChannel !== 'undefined') {\n        const ch = new MessageChannel()\n        const handler = () => {\n          // @ts-ignore (typescript thinks this property should be called `removeListener`, but in fact it's `removeEventListener`)\n          ch.port1.removeEventListener('message', handler)\n          resolve()\n        }\n        // @ts-ignore (typescript thinks this property should be called `addListener`, but in fact it's `addEventListener`)\n        ch.port1.addEventListener('message', handler)\n        ch.port2.postMessage(0)\n        ch.port1.start()\n      } else {\n        if (typeof setImmediate !== 'undefined') {\n          setImmediate(resolve)\n        } else if (typeof setTimeout !== 'undefined') {\n          setTimeout(resolve, 0)\n        } else {\n          // Last resort - resolve immediately\n          resolve()\n        }\n      }\n    } catch (e) {\n      console.error('during yield: ', e)\n      reject(e)\n    }\n  })\n}\n\nexport const alwaysTrue: Nostr['verifyEvent'] = (t: Event): t is VerifiedEvent => {\n  t[verifiedSymbol] = true\n  return true\n}\n", "/* global WebSocket */\n\nimport type { Event, EventTemplate, VerifiedEvent, Nostr, NostrEvent } from './core.ts'\nimport { matchFilters, type Filter } from './filter.ts'\nimport { getHex64, getSubscriptionId } from './fakejson.ts'\nimport { Queue, normalizeURL } from './utils.ts'\nimport { makeAuthEvent } from './nip42.ts'\nimport { yieldThread } from './helpers.ts'\n\ntype RelayWebSocket = WebSocket & {\n  ping?(): void\n  on?(event: 'pong', listener: () => void): any\n}\n\nexport type AbstractRelayConstructorOptions = {\n  verifyEvent: Nostr['verifyEvent']\n  websocketImplementation?: typeof WebSocket\n  enablePing?: boolean\n  enableReconnect?: boolean\n}\n\nexport class SendingOnClosedConnection extends Error {\n  constructor(message: string, relay: string) {\n    super(`Tried to send message '${message} on a closed connection to ${relay}.`)\n    this.name = 'SendingOnClosedConnection'\n  }\n}\n\nexport class AbstractRelay {\n  public readonly url: string\n  private _connected: boolean = false\n\n  public onclose: (() => void) | null = null\n  public onnotice: (msg: string) => void = msg => console.debug(`NOTICE from ${this.url}: ${msg}`)\n  public onauth: undefined | ((evt: EventTemplate) => Promise<VerifiedEvent>)\n\n  public baseEoseTimeout: number = 4400\n  public publishTimeout: number = 4400\n  public pingFrequency: number = 29000\n  public pingTimeout: number = 20000\n  public resubscribeBackoff: number[] = [10000, 10000, 10000, 20000, 20000, 30000, 60000]\n  public openSubs: Map<string, Subscription> = new Map()\n  public enablePing: boolean | undefined\n  public enableReconnect: boolean\n  private reconnectTimeoutHandle: ReturnType<typeof setTimeout> | undefined\n  private pingIntervalHandle: ReturnType<typeof setInterval> | undefined\n  private reconnectAttempts: number = 0\n  private closedIntentionally: boolean = false\n\n  private connectionPromise: Promise<void> | undefined\n  private openCountRequests = new Map<string, CountResolver>()\n  private openEventPublishes = new Map<string, EventPublishResolver>()\n  private ws: RelayWebSocket | undefined\n  private incomingMessageQueue = new Queue<string>()\n  private queueRunning = false\n  private challenge: string | undefined\n  private authPromise: Promise<string> | undefined\n  private serial: number = 0\n  private verifyEvent: Nostr['verifyEvent']\n\n  private _WebSocket: typeof WebSocket\n\n  constructor(url: string, opts: AbstractRelayConstructorOptions) {\n    this.url = normalizeURL(url)\n    this.verifyEvent = opts.verifyEvent\n    this._WebSocket = opts.websocketImplementation || WebSocket\n    this.enablePing = opts.enablePing\n    this.enableReconnect = opts.enableReconnect || false\n  }\n\n  static async connect(\n    url: string,\n    opts: AbstractRelayConstructorOptions & Parameters<AbstractRelay['connect']>[0],\n  ): Promise<AbstractRelay> {\n    const relay = new AbstractRelay(url, opts)\n    await relay.connect(opts)\n    return relay\n  }\n\n  private closeAllSubscriptions(reason: string) {\n    for (let [_, sub] of this.openSubs) {\n      sub.close(reason)\n    }\n    this.openSubs.clear()\n\n    for (let [_, ep] of this.openEventPublishes) {\n      ep.reject(new Error(reason))\n    }\n    this.openEventPublishes.clear()\n\n    for (let [_, cr] of this.openCountRequests) {\n      cr.reject(new Error(reason))\n    }\n    this.openCountRequests.clear()\n  }\n\n  public get connected(): boolean {\n    return this._connected\n  }\n\n  private async reconnect(): Promise<void> {\n    const backoff = this.resubscribeBackoff[Math.min(this.reconnectAttempts, this.resubscribeBackoff.length - 1)]\n    this.reconnectAttempts++\n\n    this.reconnectTimeoutHandle = setTimeout(async () => {\n      try {\n        await this.connect()\n      } catch (err) {\n        // this will be called again through onclose/onerror\n      }\n    }, backoff)\n  }\n\n  private handleHardClose(reason: string) {\n    if (this.pingIntervalHandle) {\n      clearInterval(this.pingIntervalHandle)\n      this.pingIntervalHandle = undefined\n    }\n\n    this._connected = false\n    this.connectionPromise = undefined\n\n    const wasIntentional = this.closedIntentionally\n    this.closedIntentionally = false // reset for next time\n\n    this.onclose?.()\n\n    if (this.enableReconnect && !wasIntentional) {\n      this.reconnect()\n    } else {\n      this.closeAllSubscriptions(reason)\n    }\n  }\n\n  public async connect(opts?: { timeout?: number; abort?: AbortSignal }): Promise<void> {\n    let connectionTimeoutHandle: ReturnType<typeof setTimeout> | undefined\n\n    if (this.connectionPromise) return this.connectionPromise\n\n    this.challenge = undefined\n    this.authPromise = undefined\n    this.connectionPromise = new Promise((resolve, reject) => {\n      if (opts?.timeout) {\n        connectionTimeoutHandle = setTimeout(() => {\n          reject('connection timed out')\n          this.connectionPromise = undefined\n          this.onclose?.()\n          this.closeAllSubscriptions('relay connection timed out')\n        }, opts.timeout)\n      }\n\n      if (opts?.abort) {\n        opts.abort.onabort = reject\n      }\n\n      try {\n        this.ws = new this._WebSocket(this.url)\n      } catch (err) {\n        clearTimeout(connectionTimeoutHandle)\n        reject(err)\n        return\n      }\n\n      this.ws.onopen = () => {\n        if (this.reconnectTimeoutHandle) {\n          clearTimeout(this.reconnectTimeoutHandle)\n          this.reconnectTimeoutHandle = undefined\n        }\n        clearTimeout(connectionTimeoutHandle)\n        this._connected = true\n\n        const isReconnection = this.reconnectAttempts > 0\n        this.reconnectAttempts = 0\n\n        // resubscribe to all open subscriptions\n        for (const sub of this.openSubs.values()) {\n          sub.eosed = false\n          if (isReconnection) {\n            for (let f = 0; f < sub.filters.length; f++) {\n              if (sub.lastEmitted) {\n                sub.filters[f].since = sub.lastEmitted + 1\n              }\n            }\n          }\n          sub.fire()\n        }\n\n        if (this.enablePing) {\n          this.pingIntervalHandle = setInterval(() => this.pingpong(), this.pingFrequency)\n        }\n        resolve()\n      }\n\n      this.ws.onerror = ev => {\n        clearTimeout(connectionTimeoutHandle)\n        reject((ev as any).message || 'websocket error')\n        this.handleHardClose('relay connection errored')\n      }\n\n      this.ws.onclose = ev => {\n        clearTimeout(connectionTimeoutHandle)\n        reject((ev as any).message || 'websocket closed')\n        this.handleHardClose('relay connection closed')\n      }\n\n      this.ws.onmessage = this._onmessage.bind(this)\n    })\n\n    return this.connectionPromise\n  }\n\n  private waitForPingPong() {\n    return new Promise(resolve => {\n      // listen for pong\n      ;(this.ws as any).once('pong', () => resolve(true))\n      // send a ping\n      this.ws!.ping!()\n    })\n  }\n\n  private waitForDummyReq() {\n    return new Promise((resolve, reject) => {\n      if (!this.connectionPromise) return reject(new Error(`no connection to ${this.url}, can't ping`))\n\n      // make a dummy request with expected empty eose reply\n      // [\"REQ\", \"_\", {\"ids\":[\"aaaa...aaaa\"], \"limit\": 0}]\n      try {\n        const sub = this.subscribe(\n          [{ ids: ['aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'], limit: 0 }],\n          {\n            label: 'forced-ping',\n            oneose: () => {\n              resolve(true)\n              sub.close()\n            },\n            onclose() {\n              // if we get a CLOSED it's because the relay is alive\n              resolve(true)\n            },\n            eoseTimeout: this.pingTimeout + 1000,\n          },\n        )\n      } catch (err) {\n        reject(err)\n      }\n    })\n  }\n\n  // nodejs requires this magic here to ensure connections are closed when internet goes off and stuff\n  // in browsers it's done automatically. see https://github.com/nbd-wtf/nostr-tools/issues/491\n  private async pingpong() {\n    // if the websocket is connected\n    if (this.ws?.readyState === 1) {\n      // wait for either a ping-pong reply or a timeout\n      const result = await Promise.any([\n        // browsers don't have ping so use a dummy req\n        this.ws && this.ws.ping && (this.ws as any).once ? this.waitForPingPong() : this.waitForDummyReq(),\n        new Promise(res => setTimeout(() => res(false), this.pingTimeout)),\n      ])\n\n      if (!result) {\n        // pingpong closing socket\n        if (this.ws?.readyState === this._WebSocket.OPEN) {\n          this.ws?.close()\n        }\n      }\n    }\n  }\n\n  private async runQueue() {\n    this.queueRunning = true\n    while (true) {\n      if (false === this.handleNext()) {\n        break\n      }\n      await yieldThread()\n    }\n    this.queueRunning = false\n  }\n\n  private handleNext(): undefined | false {\n    const json = this.incomingMessageQueue.dequeue()\n    if (!json) {\n      return false\n    }\n\n    // shortcut EVENT sub\n    const subid = getSubscriptionId(json)\n    if (subid) {\n      const so = this.openSubs.get(subid as string)\n      if (!so) {\n        // this is an EVENT message, but for a subscription we don't have, so just stop here\n        return\n      }\n\n      // this will be called only when this message is a EVENT message for a subscription we have\n      // we do this before parsing the JSON to not have to do that for duplicate events\n      //   since JSON parsing is slow\n      const id = getHex64(json, 'id')\n      const alreadyHave = so.alreadyHaveEvent?.(id)\n\n      // notify any interested client that the relay has this event\n      // (do this after alreadyHaveEvent() because the client may rely on this to answer that)\n      so.receivedEvent?.(this, id)\n\n      if (alreadyHave) {\n        // if we had already seen this event we can just stop here\n        return\n      }\n    }\n\n    try {\n      let data = JSON.parse(json)\n      // we won't do any checks against the data since all failures (i.e. invalid messages from relays)\n      // will naturally be caught by the encompassing try..catch block\n\n      switch (data[0]) {\n        case 'EVENT': {\n          const so = this.openSubs.get(data[1] as string) as Subscription\n          const event = data[2] as NostrEvent\n          if (this.verifyEvent(event) && matchFilters(so.filters, event)) {\n            so.onevent(event)\n          }\n          if (!so.lastEmitted || so.lastEmitted < event.created_at) so.lastEmitted = event.created_at\n          return\n        }\n        case 'COUNT': {\n          const id: string = data[1]\n          const payload = data[2] as { count: number }\n          const cr = this.openCountRequests.get(id) as CountResolver\n          if (cr) {\n            cr.resolve(payload.count)\n            this.openCountRequests.delete(id)\n          }\n          return\n        }\n        case 'EOSE': {\n          const so = this.openSubs.get(data[1] as string)\n          if (!so) return\n          so.receivedEose()\n          return\n        }\n        case 'OK': {\n          const id: string = data[1]\n          const ok: boolean = data[2]\n          const reason: string = data[3]\n          const ep = this.openEventPublishes.get(id) as EventPublishResolver\n          if (ep) {\n            clearTimeout(ep.timeout)\n            if (ok) ep.resolve(reason)\n            else ep.reject(new Error(reason))\n            this.openEventPublishes.delete(id)\n          }\n          return\n        }\n        case 'CLOSED': {\n          const id: string = data[1]\n          const so = this.openSubs.get(id)\n          if (!so) return\n          so.closed = true\n          so.close(data[2] as string)\n          return\n        }\n        case 'NOTICE': {\n          this.onnotice(data[1] as string)\n          return\n        }\n        case 'AUTH': {\n          this.challenge = data[1] as string\n          if (this.onauth) {\n            this.auth(this.onauth)\n          }\n          return\n        }\n        default: {\n          const so = this.openSubs.get(data[1])\n          so?.oncustom?.(data)\n          return\n        }\n      }\n    } catch (err) {\n      return\n    }\n  }\n\n  public async send(message: string) {\n    if (!this.connectionPromise) throw new SendingOnClosedConnection(message, this.url)\n\n    this.connectionPromise.then(() => {\n      this.ws?.send(message)\n    })\n  }\n\n  public async auth(signAuthEvent: (evt: EventTemplate) => Promise<VerifiedEvent>): Promise<string> {\n    const challenge = this.challenge\n    if (!challenge) throw new Error(\"can't perform auth, no challenge was received\")\n    if (this.authPromise) return this.authPromise\n\n    this.authPromise = new Promise<string>(async (resolve, reject) => {\n      try {\n        let evt = await signAuthEvent(makeAuthEvent(this.url, challenge))\n        let timeout = setTimeout(() => {\n          let ep = this.openEventPublishes.get(evt.id) as EventPublishResolver\n          if (ep) {\n            ep.reject(new Error('auth timed out'))\n            this.openEventPublishes.delete(evt.id)\n          }\n        }, this.publishTimeout)\n        this.openEventPublishes.set(evt.id, { resolve, reject, timeout })\n        this.send('[\"AUTH\",' + JSON.stringify(evt) + ']')\n      } catch (err) {\n        console.warn('subscribe auth function failed:', err)\n      }\n    })\n    return this.authPromise\n  }\n\n  public async publish(event: Event): Promise<string> {\n    const ret = new Promise<string>((resolve, reject) => {\n      const timeout = setTimeout(() => {\n        const ep = this.openEventPublishes.get(event.id) as EventPublishResolver\n        if (ep) {\n          ep.reject(new Error('publish timed out'))\n          this.openEventPublishes.delete(event.id)\n        }\n      }, this.publishTimeout)\n      this.openEventPublishes.set(event.id, { resolve, reject, timeout })\n    })\n    this.send('[\"EVENT\",' + JSON.stringify(event) + ']')\n    return ret\n  }\n\n  public async count(filters: Filter[], params: { id?: string | null }): Promise<number> {\n    this.serial++\n    const id = params?.id || 'count:' + this.serial\n    const ret = new Promise<number>((resolve, reject) => {\n      this.openCountRequests.set(id, { resolve, reject })\n    })\n    this.send('[\"COUNT\",\"' + id + '\",' + JSON.stringify(filters).substring(1))\n    return ret\n  }\n\n  public subscribe(\n    filters: Filter[],\n    params: Partial<SubscriptionParams> & { label?: string; id?: string },\n  ): Subscription {\n    const sub = this.prepareSubscription(filters, params)\n    sub.fire()\n\n    if (params.abort) {\n      params.abort.onabort = () => sub.close(String(params.abort!.reason || '<aborted>'))\n    }\n\n    return sub\n  }\n\n  public prepareSubscription(\n    filters: Filter[],\n    params: Partial<SubscriptionParams> & { label?: string; id?: string },\n  ): Subscription {\n    this.serial++\n    const id = params.id || (params.label ? params.label + ':' : 'sub:') + this.serial\n    const subscription = new Subscription(this, id, filters, params)\n    this.openSubs.set(id, subscription)\n    return subscription\n  }\n\n  public close() {\n    this.closedIntentionally = true\n    if (this.reconnectTimeoutHandle) {\n      clearTimeout(this.reconnectTimeoutHandle)\n      this.reconnectTimeoutHandle = undefined\n    }\n    if (this.pingIntervalHandle) {\n      clearInterval(this.pingIntervalHandle)\n      this.pingIntervalHandle = undefined\n    }\n    this.closeAllSubscriptions('relay connection closed by us')\n    this._connected = false\n    this.onclose?.()\n    if (this.ws?.readyState === this._WebSocket.OPEN) {\n      this.ws?.close()\n    }\n  }\n\n  // this is the function assigned to this.ws.onmessage\n  // it's exposed for testing and debugging purposes\n  public _onmessage(ev: MessageEvent<any>) {\n    this.incomingMessageQueue.enqueue(ev.data as string)\n    if (!this.queueRunning) {\n      this.runQueue()\n    }\n  }\n}\n\nexport class Subscription {\n  public readonly relay: AbstractRelay\n  public readonly id: string\n\n  public lastEmitted: number | undefined\n  public closed: boolean = false\n  public eosed: boolean = false\n  public filters: Filter[]\n  public alreadyHaveEvent: ((id: string) => boolean) | undefined\n  public receivedEvent: ((relay: AbstractRelay, id: string) => void) | undefined\n\n  public onevent: (evt: Event) => void\n  public oneose: (() => void) | undefined\n  public onclose: ((reason: string) => void) | undefined\n\n  // will get any messages that have this subscription id as their second item and are not default standard\n  public oncustom: ((msg: string[]) => void) | undefined\n\n  public eoseTimeout: number\n  private eoseTimeoutHandle: ReturnType<typeof setTimeout> | undefined\n\n  constructor(relay: AbstractRelay, id: string, filters: Filter[], params: SubscriptionParams) {\n    if (filters.length === 0) throw new Error(\"subscription can't be created with zero filters\")\n\n    this.relay = relay\n    this.filters = filters\n    this.id = id\n    this.alreadyHaveEvent = params.alreadyHaveEvent\n    this.receivedEvent = params.receivedEvent\n    this.eoseTimeout = params.eoseTimeout || relay.baseEoseTimeout\n\n    this.oneose = params.oneose\n    this.onclose = params.onclose\n    this.onevent =\n      params.onevent ||\n      (event => {\n        console.warn(\n          `onevent() callback not defined for subscription '${this.id}' in relay ${this.relay.url}. event received:`,\n          event,\n        )\n      })\n  }\n\n  public fire() {\n    this.relay.send('[\"REQ\",\"' + this.id + '\",' + JSON.stringify(this.filters).substring(1))\n\n    // only now we start counting the eoseTimeout\n    this.eoseTimeoutHandle = setTimeout(this.receivedEose.bind(this), this.eoseTimeout)\n  }\n\n  public receivedEose() {\n    if (this.eosed) return\n    clearTimeout(this.eoseTimeoutHandle)\n    this.eosed = true\n    this.oneose?.()\n  }\n\n  public close(reason: string = 'closed by caller') {\n    if (!this.closed && this.relay.connected) {\n      // if the connection was closed by the user calling .close() we will send a CLOSE message\n      // otherwise this._open will be already set to false so we will skip this\n      try {\n        this.relay.send('[\"CLOSE\",' + JSON.stringify(this.id) + ']')\n      } catch (err) {\n        if (err instanceof SendingOnClosedConnection) {\n          /* doesn't matter, it's ok */\n        } else {\n          throw err\n        }\n      }\n      this.closed = true\n    }\n    this.relay.openSubs.delete(this.id)\n    this.onclose?.(reason)\n  }\n}\n\nexport type SubscriptionParams = {\n  onevent?: (evt: Event) => void\n  oneose?: () => void\n  onclose?: (reason: string) => void\n  alreadyHaveEvent?: (id: string) => boolean\n  receivedEvent?: (relay: AbstractRelay, id: string) => void\n  eoseTimeout?: number\n  abort?: AbortSignal\n}\n\nexport type CountResolver = {\n  resolve: (count: number) => void\n  reject: (err: Error) => void\n}\n\nexport type EventPublishResolver = {\n  resolve: (reason: string) => void\n  reject: (err: Error) => void\n  timeout: ReturnType<typeof setTimeout>\n}\n", "/* global WebSocket */\n\nimport {\n  AbstractRelay as AbstractRelay,\n  SubscriptionParams,\n  Subscription,\n  type AbstractRelayConstructorOptions,\n} from './abstract-relay.ts'\nimport { normalizeURL } from './utils.ts'\n\nimport type { Event, EventTemplate, Nostr, VerifiedEvent } from './core.ts'\nimport { type Filter } from './filter.ts'\nimport { alwaysTrue } from './helpers.ts'\n\nexport type SubCloser = { close: (reason?: string) => void }\n\nexport type AbstractPoolConstructorOptions = AbstractRelayConstructorOptions & {\n  // automaticallyAuth takes a relay URL and should return null\n  // in case that relay shouldn't be authenticated against\n  // or a function to sign the AUTH event template otherwise (that function may still throw in case of failure)\n  automaticallyAuth?: (relayURL: string) => null | ((event: EventTemplate) => Promise<VerifiedEvent>)\n}\n\nexport type SubscribeManyParams = Omit<SubscriptionParams, 'onclose'> & {\n  maxWait?: number\n  abort?: AbortSignal\n  onclose?: (reasons: string[]) => void\n  onauth?: (event: EventTemplate) => Promise<VerifiedEvent>\n  id?: string\n  label?: string\n}\n\nexport class AbstractSimplePool {\n  protected relays: Map<string, AbstractRelay> = new Map()\n  public seenOn: Map<string, Set<AbstractRelay>> = new Map()\n  public trackRelays: boolean = false\n\n  public verifyEvent: Nostr['verifyEvent']\n  public enablePing: boolean | undefined\n  public enableReconnect: boolean\n  public automaticallyAuth?: (relayURL: string) => null | ((event: EventTemplate) => Promise<VerifiedEvent>)\n  public trustedRelayURLs: Set<string> = new Set()\n\n  private _WebSocket?: typeof WebSocket\n\n  constructor(opts: AbstractPoolConstructorOptions) {\n    this.verifyEvent = opts.verifyEvent\n    this._WebSocket = opts.websocketImplementation\n    this.enablePing = opts.enablePing\n    this.enableReconnect = opts.enableReconnect || false\n    this.automaticallyAuth = opts.automaticallyAuth\n  }\n\n  async ensureRelay(\n    url: string,\n    params?: {\n      connectionTimeout?: number\n      abort?: AbortSignal\n    },\n  ): Promise<AbstractRelay> {\n    url = normalizeURL(url)\n\n    let relay = this.relays.get(url)\n    if (!relay) {\n      relay = new AbstractRelay(url, {\n        verifyEvent: this.trustedRelayURLs.has(url) ? alwaysTrue : this.verifyEvent,\n        websocketImplementation: this._WebSocket,\n        enablePing: this.enablePing,\n        enableReconnect: this.enableReconnect,\n      })\n      relay.onclose = () => {\n        if (relay && !relay.enableReconnect) {\n          this.relays.delete(url)\n        }\n      }\n      this.relays.set(url, relay)\n    }\n\n    if (this.automaticallyAuth) {\n      const authSignerFn = this.automaticallyAuth(url)\n      if (authSignerFn) {\n        relay.onauth = authSignerFn\n      }\n    }\n\n    await relay.connect({\n      timeout: params?.connectionTimeout,\n      abort: params?.abort,\n    })\n\n    return relay\n  }\n\n  close(relays: string[]) {\n    relays.map(normalizeURL).forEach(url => {\n      this.relays.get(url)?.close()\n      this.relays.delete(url)\n    })\n  }\n\n  subscribe(relays: string[], filter: Filter, params: SubscribeManyParams): SubCloser {\n    const request: { url: string; filter: Filter }[] = []\n    for (let i = 0; i < relays.length; i++) {\n      const url = normalizeURL(relays[i])\n      if (!request.find(r => r.url === url)) {\n        request.push({ url, filter: filter })\n      }\n    }\n\n    return this.subscribeMap(request, params)\n  }\n\n  subscribeMany(relays: string[], filter: Filter, params: SubscribeManyParams): SubCloser {\n    const request: { url: string; filter: Filter }[] = []\n    const uniqUrls: string[] = []\n    for (let i = 0; i < relays.length; i++) {\n      const url = normalizeURL(relays[i])\n      if (uniqUrls.indexOf(url) === -1) {\n        uniqUrls.push(url)\n        request.push({ url, filter: filter })\n      }\n    }\n\n    return this.subscribeMap(request, params)\n  }\n\n  subscribeMap(requests: { url: string; filter: Filter }[], params: SubscribeManyParams): SubCloser {\n    const grouped = new Map<string, Filter[]>()\n    for (const req of requests) {\n      const { url, filter } = req\n      if (!grouped.has(url)) grouped.set(url, [])\n      grouped.get(url)!.push(filter)\n    }\n    const groupedRequests = Array.from(grouped.entries()).map(([url, filters]) => ({ url, filters }))\n\n    if (this.trackRelays) {\n      params.receivedEvent = (relay: AbstractRelay, id: string) => {\n        let set = this.seenOn.get(id)\n        if (!set) {\n          set = new Set()\n          this.seenOn.set(id, set)\n        }\n        set.add(relay)\n      }\n    }\n\n    const _knownIds = new Set<string>()\n    const subs: Subscription[] = []\n\n    // batch all EOSEs into a single\n    const eosesReceived: boolean[] = []\n    let handleEose = (i: number) => {\n      if (eosesReceived[i]) return // do not act twice for the same relay\n      eosesReceived[i] = true\n      if (eosesReceived.filter(a => a).length === groupedRequests.length) {\n        params.oneose?.()\n        handleEose = () => {}\n      }\n    }\n    // batch all closes into a single\n    const closesReceived: string[] = []\n    let handleClose = (i: number, reason: string) => {\n      if (closesReceived[i]) return // do not act twice for the same relay\n      handleEose(i)\n      closesReceived[i] = reason\n      if (closesReceived.filter(a => a).length === groupedRequests.length) {\n        params.onclose?.(closesReceived)\n        handleClose = () => {}\n      }\n    }\n\n    const localAlreadyHaveEventHandler = (id: string) => {\n      if (params.alreadyHaveEvent?.(id)) {\n        return true\n      }\n      const have = _knownIds.has(id)\n      _knownIds.add(id)\n      return have\n    }\n\n    // open a subscription in all given relays\n    const allOpened = Promise.all(\n      groupedRequests.map(async ({ url, filters }, i) => {\n        let relay: AbstractRelay\n        try {\n          relay = await this.ensureRelay(url, {\n            connectionTimeout: params.maxWait ? Math.max(params.maxWait * 0.8, params.maxWait - 1000) : undefined,\n            abort: params.abort,\n          })\n        } catch (err) {\n          handleClose(i, (err as any)?.message || String(err))\n          return\n        }\n\n        let subscription = relay.subscribe(filters, {\n          ...params,\n          oneose: () => handleEose(i),\n          onclose: reason => {\n            if (reason.startsWith('auth-required: ') && params.onauth) {\n              relay\n                .auth(params.onauth)\n                .then(() => {\n                  relay.subscribe(filters, {\n                    ...params,\n                    oneose: () => handleEose(i),\n                    onclose: reason => {\n                      handleClose(i, reason) // the second time we won't try to auth anymore\n                    },\n                    alreadyHaveEvent: localAlreadyHaveEventHandler,\n                    eoseTimeout: params.maxWait,\n                    abort: params.abort,\n                  })\n                })\n                .catch(err => {\n                  handleClose(i, `auth was required and attempted, but failed with: ${err}`)\n                })\n            } else {\n              handleClose(i, reason)\n            }\n          },\n          alreadyHaveEvent: localAlreadyHaveEventHandler,\n          eoseTimeout: params.maxWait,\n          abort: params.abort,\n        })\n\n        subs.push(subscription)\n      }),\n    )\n\n    return {\n      async close(reason?: string) {\n        await allOpened\n        subs.forEach(sub => {\n          sub.close(reason)\n        })\n      },\n    }\n  }\n\n  subscribeEose(\n    relays: string[],\n    filter: Filter,\n    params: Pick<SubscribeManyParams, 'label' | 'id' | 'onevent' | 'onclose' | 'maxWait' | 'onauth'>,\n  ): SubCloser {\n    const subcloser = this.subscribe(relays, filter, {\n      ...params,\n      oneose() {\n        subcloser.close('closed automatically on eose')\n      },\n    })\n    return subcloser\n  }\n\n  subscribeManyEose(\n    relays: string[],\n    filter: Filter,\n    params: Pick<SubscribeManyParams, 'label' | 'id' | 'onevent' | 'onclose' | 'maxWait' | 'onauth'>,\n  ): SubCloser {\n    const subcloser = this.subscribeMany(relays, filter, {\n      ...params,\n      oneose() {\n        subcloser.close('closed automatically on eose')\n      },\n    })\n    return subcloser\n  }\n\n  async querySync(\n    relays: string[],\n    filter: Filter,\n    params?: Pick<SubscribeManyParams, 'label' | 'id' | 'maxWait'>,\n  ): Promise<Event[]> {\n    return new Promise(async resolve => {\n      const events: Event[] = []\n      this.subscribeEose(relays, filter, {\n        ...params,\n        onevent(event: Event) {\n          events.push(event)\n        },\n        onclose(_: string[]) {\n          resolve(events)\n        },\n      })\n    })\n  }\n\n  async get(\n    relays: string[],\n    filter: Filter,\n    params?: Pick<SubscribeManyParams, 'label' | 'id' | 'maxWait'>,\n  ): Promise<Event | null> {\n    filter.limit = 1\n    const events = await this.querySync(relays, filter, params)\n    events.sort((a, b) => b.created_at - a.created_at)\n    return events[0] || null\n  }\n\n  publish(\n    relays: string[],\n    event: Event,\n    options?: { onauth?: (evt: EventTemplate) => Promise<VerifiedEvent> },\n  ): Promise<string>[] {\n    return relays.map(normalizeURL).map(async (url, i, arr) => {\n      if (arr.indexOf(url) !== i) {\n        // duplicate\n        return Promise.reject('duplicate url')\n      }\n\n      let r = await this.ensureRelay(url)\n      return r\n        .publish(event)\n        .catch(async err => {\n          if (err instanceof Error && err.message.startsWith('auth-required: ') && options?.onauth) {\n            await r.auth(options.onauth)\n            return r.publish(event) // retry\n          }\n          throw err\n        })\n        .then(reason => {\n          if (this.trackRelays) {\n            let set = this.seenOn.get(event.id)\n            if (!set) {\n              set = new Set()\n              this.seenOn.set(event.id, set)\n            }\n            set.add(r)\n          }\n          return reason\n        })\n    })\n  }\n\n  listConnectionStatus(): Map<string, boolean> {\n    const map = new Map<string, boolean>()\n    this.relays.forEach((relay, url) => map.set(url, relay.connected))\n\n    return map\n  }\n\n  destroy(): void {\n    this.relays.forEach(conn => conn.close())\n    this.relays = new Map()\n  }\n}\n"],
-  "mappings": "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",
+  "sourcesContent": ["/* global WebSocket */\n\nimport { verifyEvent } from './pure.ts'\nimport { AbstractSimplePool, type AbstractPoolConstructorOptions } from './abstract-pool.ts'\n\nvar _WebSocket: typeof WebSocket\n\ntry {\n  _WebSocket = WebSocket\n} catch {}\n\nexport function useWebSocketImplementation(websocketImplementation: any) {\n  _WebSocket = websocketImplementation\n}\n\nexport class SimplePool extends AbstractSimplePool {\n  constructor(options?: Pick<AbstractPoolConstructorOptions, 'enablePing' | 'enableReconnect'>) {\n    super({ verifyEvent, websocketImplementation: _WebSocket, ...options })\n  }\n}\n\nexport * from './abstract-pool.ts'\n", "import { schnorr } from '@noble/curves/secp256k1.js'\nimport { bytesToHex, hexToBytes } from '@noble/hashes/utils.js'\nimport { Nostr, Event, EventTemplate, UnsignedEvent, VerifiedEvent, verifiedSymbol, validateEvent } from './core.ts'\nimport { sha256 } from '@noble/hashes/sha2.js'\n\nimport { utf8Encoder } from './utils.ts'\n\nclass JS implements Nostr {\n  generateSecretKey(): Uint8Array {\n    return schnorr.utils.randomSecretKey()\n  }\n  getPublicKey(secretKey: Uint8Array): string {\n    return bytesToHex(schnorr.getPublicKey(secretKey))\n  }\n  finalizeEvent(t: EventTemplate, secretKey: Uint8Array): VerifiedEvent {\n    const event = t as VerifiedEvent\n    event.pubkey = bytesToHex(schnorr.getPublicKey(secretKey))\n    event.id = getEventHash(event)\n    event.sig = bytesToHex(schnorr.sign(hexToBytes(getEventHash(event)), secretKey))\n    event[verifiedSymbol] = true\n    return event\n  }\n  verifyEvent(event: Event): event is VerifiedEvent {\n    if (typeof event[verifiedSymbol] === 'boolean') return event[verifiedSymbol]\n\n    const hash = getEventHash(event)\n    if (hash !== event.id) {\n      event[verifiedSymbol] = false\n      return false\n    }\n\n    try {\n      const valid = schnorr.verify(hexToBytes(event.sig), hexToBytes(hash), hexToBytes(event.pubkey))\n      event[verifiedSymbol] = valid\n      return valid\n    } catch (err) {\n      event[verifiedSymbol] = false\n      return false\n    }\n  }\n}\n\nexport function serializeEvent(evt: UnsignedEvent): string {\n  if (!validateEvent(evt)) throw new Error(\"can't serialize event with wrong or missing properties\")\n  return JSON.stringify([0, evt.pubkey, evt.created_at, evt.kind, evt.tags, evt.content])\n}\n\nexport function getEventHash(event: UnsignedEvent): string {\n  let eventHash = sha256(utf8Encoder.encode(serializeEvent(event)))\n  return bytesToHex(eventHash)\n}\n\nconst i: JS = new JS()\n\nexport const generateSecretKey = i.generateSecretKey\nexport const getPublicKey = i.getPublicKey\nexport const finalizeEvent = i.finalizeEvent\nexport const verifyEvent = i.verifyEvent\nexport * from './core.ts'\n", "export interface Nostr {\n  generateSecretKey(): Uint8Array\n  getPublicKey(secretKey: Uint8Array): string\n  finalizeEvent(event: EventTemplate, secretKey: Uint8Array): VerifiedEvent\n  verifyEvent(event: Event): event is VerifiedEvent\n}\n\n/** Designates a verified event signature. */\nexport const verifiedSymbol = Symbol('verified')\n\nexport type NostrEvent = {\n  kind: number\n  tags: string[][]\n  content: string\n  created_at: number\n  pubkey: string\n  id: string\n  sig: string\n  [verifiedSymbol]?: boolean\n}\n\nexport type Event = NostrEvent\nexport type EventTemplate = Pick<Event, 'kind' | 'tags' | 'content' | 'created_at'>\nexport type UnsignedEvent = Pick<Event, 'kind' | 'tags' | 'content' | 'created_at' | 'pubkey'>\n\n/** An event whose signature has been verified. */\nexport interface VerifiedEvent extends Event {\n  [verifiedSymbol]: true\n}\n\nconst isRecord = (obj: unknown): obj is Record<string, unknown> => obj instanceof Object\n\nexport function validateEvent<T>(event: T): event is T & UnsignedEvent {\n  if (!isRecord(event)) return false\n  if (typeof event.kind !== 'number') return false\n  if (typeof event.content !== 'string') return false\n  if (typeof event.created_at !== 'number') return false\n  if (typeof event.pubkey !== 'string') return false\n  if (!event.pubkey.match(/^[a-f0-9]{64}$/)) return false\n\n  if (!Array.isArray(event.tags)) return false\n  for (let i = 0; i < event.tags.length; i++) {\n    let tag = event.tags[i]\n    if (!Array.isArray(tag)) return false\n    for (let j = 0; j < tag.length; j++) {\n      if (typeof tag[j] !== 'string') return false\n    }\n  }\n\n  return true\n}\n\n/**\n * Sort events in reverse-chronological order by the `created_at` timestamp,\n * and then by the event `id` (lexicographically) in case of ties.\n * This mutates the array.\n */\nexport function sortEvents(events: Event[]): Event[] {\n  return events.sort((a: NostrEvent, b: NostrEvent): number => {\n    if (a.created_at !== b.created_at) {\n      return b.created_at - a.created_at\n    }\n    return a.id.localeCompare(b.id)\n  })\n}\n", "import type { Event } from './core.ts'\n\nexport const utf8Decoder: TextDecoder = new TextDecoder('utf-8')\nexport const utf8Encoder: TextEncoder = new TextEncoder()\n\nexport { bytesToHex, hexToBytes } from '@noble/hashes/utils.js'\n\nexport function normalizeURL(url: string): string {\n  try {\n    if (url.indexOf('://') === -1) url = 'wss://' + url\n    let p = new URL(url)\n    if (p.protocol === 'http:') p.protocol = 'ws:'\n    else if (p.protocol === 'https:') p.protocol = 'wss:'\n    p.pathname = p.pathname.replace(/\\/+/g, '/')\n    if (p.pathname.endsWith('/')) p.pathname = p.pathname.slice(0, -1)\n    if ((p.port === '80' && p.protocol === 'ws:') || (p.port === '443' && p.protocol === 'wss:')) p.port = ''\n    p.searchParams.sort()\n    p.hash = ''\n    return p.toString()\n  } catch (e) {\n    throw new Error(`Invalid URL: ${url}`)\n  }\n}\n\nexport function insertEventIntoDescendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return b.created_at - event.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function insertEventIntoAscendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return event.created_at - b.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function binarySearch<T>(arr: T[], compare: (b: T) => number): [number, boolean] {\n  let start = 0\n  let end = arr.length - 1\n\n  while (start <= end) {\n    const mid = Math.floor((start + end) / 2)\n    const cmp = compare(arr[mid])\n\n    if (cmp === 0) {\n      return [mid, true]\n    }\n\n    if (cmp < 0) {\n      end = mid - 1\n    } else {\n      start = mid + 1\n    }\n  }\n\n  return [start, false]\n}\n\nexport class QueueNode<V> {\n  public value: V\n  public next: QueueNode<V> | null = null\n  public prev: QueueNode<V> | null = null\n\n  constructor(message: V) {\n    this.value = message\n  }\n}\n\nexport class Queue<V> {\n  public first: QueueNode<V> | null\n  public last: QueueNode<V> | null\n\n  constructor() {\n    this.first = null\n    this.last = null\n  }\n\n  enqueue(value: V): boolean {\n    const newNode = new QueueNode(value)\n    if (!this.last) {\n      // list is empty\n      this.first = newNode\n      this.last = newNode\n    } else if (this.last === this.first) {\n      // list has a single element\n      this.last = newNode\n      this.last.prev = this.first\n      this.first.next = newNode\n    } else {\n      // list has elements, add as last\n      newNode.prev = this.last\n      this.last.next = newNode\n      this.last = newNode\n    }\n    return true\n  }\n\n  dequeue(): V | null {\n    if (!this.first) return null\n\n    if (this.first === this.last) {\n      const target = this.first\n      this.first = null\n      this.last = null\n      return target.value\n    }\n\n    const target = this.first\n    this.first = target.next\n    if (this.first) {\n      this.first.prev = null // fix: clean up prev pointer\n    }\n\n    return target.value\n  }\n}\n", "import { NostrEvent, validateEvent } from './pure.ts'\n\n/** Events are **regular**, which means they're all expected to be stored by relays. */\nexport function isRegularKind(kind: number): boolean {\n  return kind < 10000 && kind !== 0 && kind !== 3\n}\n\n/** Events are **replaceable**, which means that, for each combination of `pubkey` and `kind`, only the latest event is expected to (SHOULD) be stored by relays, older versions are expected to be discarded. */\nexport function isReplaceableKind(kind: number): boolean {\n  return kind === 0 || kind === 3 || (10000 <= kind && kind < 20000)\n}\n\n/** Events are **ephemeral**, which means they are not expected to be stored by relays. */\nexport function isEphemeralKind(kind: number): boolean {\n  return 20000 <= kind && kind < 30000\n}\n\n/** Events are **addressable**, which means that, for each combination of `pubkey`, `kind` and the `d` tag, only the latest event is expected to be stored by relays, older versions are expected to be discarded. */\nexport function isAddressableKind(kind: number): boolean {\n  return 30000 <= kind && kind < 40000\n}\n\n/** Classification of the event kind. */\nexport type KindClassification = 'regular' | 'replaceable' | 'ephemeral' | 'parameterized' | 'unknown'\n\n/** Determine the classification of this kind of event if known, or `unknown`. */\nexport function classifyKind(kind: number): KindClassification {\n  if (isRegularKind(kind)) return 'regular'\n  if (isReplaceableKind(kind)) return 'replaceable'\n  if (isEphemeralKind(kind)) return 'ephemeral'\n  if (isAddressableKind(kind)) return 'parameterized'\n  return 'unknown'\n}\n\nexport function isKind<T extends number>(event: unknown, kind: T | Array<T>): event is NostrEvent & { kind: T } {\n  const kindAsArray: number[] = kind instanceof Array ? kind : [kind]\n  return (validateEvent(event) && kindAsArray.includes(event.kind)) || false\n}\n\nexport const Metadata = 0\nexport type Metadata = typeof Metadata\nexport const ShortTextNote = 1\nexport type ShortTextNote = typeof ShortTextNote\nexport const RecommendRelay = 2\nexport type RecommendRelay = typeof RecommendRelay\nexport const Contacts = 3\nexport type Contacts = typeof Contacts\nexport const EncryptedDirectMessage = 4\nexport type EncryptedDirectMessage = typeof EncryptedDirectMessage\nexport const EventDeletion = 5\nexport type EventDeletion = typeof EventDeletion\nexport const Repost = 6\nexport type Repost = typeof Repost\nexport const Reaction = 7\nexport type Reaction = typeof Reaction\nexport const BadgeAward = 8\nexport type BadgeAward = typeof BadgeAward\nexport const ChatMessage = 9\nexport type ChatMessage = typeof ChatMessage\nexport const ForumThread = 11\nexport type ForumThread = typeof ForumThread\nexport const Seal = 13\nexport type Seal = typeof Seal\nexport const PrivateDirectMessage = 14\nexport type PrivateDirectMessage = typeof PrivateDirectMessage\nexport const FileMessage = 15\nexport type FileMessage = typeof FileMessage\nexport const GenericRepost = 16\nexport type GenericRepost = typeof GenericRepost\nexport const Photo = 20\nexport type Photo = typeof Photo\nexport const NormalVideo = 21\nexport type NormalVideo = typeof NormalVideo\nexport const ShortVideo = 22\nexport type ShortVideo = typeof ShortVideo\nexport const ChannelCreation = 40\nexport type ChannelCreation = typeof ChannelCreation\nexport const ChannelMetadata = 41\nexport type ChannelMetadata = typeof ChannelMetadata\nexport const ChannelMessage = 42\nexport type ChannelMessage = typeof ChannelMessage\nexport const ChannelHideMessage = 43\nexport type ChannelHideMessage = typeof ChannelHideMessage\nexport const ChannelMuteUser = 44\nexport type ChannelMuteUser = typeof ChannelMuteUser\nexport const OpenTimestamps = 1040\nexport type OpenTimestamps = typeof OpenTimestamps\nexport const GiftWrap = 1059\nexport type GiftWrap = typeof GiftWrap\nexport const Poll = 1068\nexport type Poll = typeof Poll\nexport const FileMetadata = 1063\nexport type FileMetadata = typeof FileMetadata\nexport const Comment = 1111\nexport type Comment = typeof Comment\nexport const LiveChatMessage = 1311\nexport type LiveChatMessage = typeof LiveChatMessage\nexport const Voice = 1222\nexport type Voice = typeof Voice\nexport const VoiceComment = 1244\nexport type VoiceComment = typeof VoiceComment\nexport const ProblemTracker = 1971\nexport type ProblemTracker = typeof ProblemTracker\nexport const Report = 1984\nexport type Report = typeof Report\nexport const Reporting = 1984\nexport type Reporting = typeof Reporting\nexport const Label = 1985\nexport type Label = typeof Label\nexport const CommunityPostApproval = 4550\nexport type CommunityPostApproval = typeof CommunityPostApproval\nexport const JobRequest = 5999\nexport type JobRequest = typeof JobRequest\nexport const JobResult = 6999\nexport type JobResult = typeof JobResult\nexport const JobFeedback = 7000\nexport type JobFeedback = typeof JobFeedback\nexport const ZapGoal = 9041\nexport type ZapGoal = typeof ZapGoal\nexport const ZapRequest = 9734\nexport type ZapRequest = typeof ZapRequest\nexport const Zap = 9735\nexport type Zap = typeof Zap\nexport const Highlights = 9802\nexport type Highlights = typeof Highlights\nexport const PollResponse = 1018\nexport type PollResponse = typeof PollResponse\nexport const Mutelist = 10000\nexport type Mutelist = typeof Mutelist\nexport const Pinlist = 10001\nexport type Pinlist = typeof Pinlist\nexport const RelayList = 10002\nexport type RelayList = typeof RelayList\nexport const BookmarkList = 10003\nexport type BookmarkList = typeof BookmarkList\nexport const CommunitiesList = 10004\nexport type CommunitiesList = typeof CommunitiesList\nexport const PublicChatsList = 10005\nexport type PublicChatsList = typeof PublicChatsList\nexport const BlockedRelaysList = 10006\nexport type BlockedRelaysList = typeof BlockedRelaysList\nexport const SearchRelaysList = 10007\nexport type SearchRelaysList = typeof SearchRelaysList\nexport const FavoriteRelays = 10012\nexport type FavoriteRelays = typeof FavoriteRelays\nexport const InterestsList = 10015\nexport type InterestsList = typeof InterestsList\nexport const UserEmojiList = 10030\nexport type UserEmojiList = typeof UserEmojiList\nexport const DirectMessageRelaysList = 10050\nexport type DirectMessageRelaysList = typeof DirectMessageRelaysList\nexport const FileServerPreference = 10096\nexport type FileServerPreference = typeof FileServerPreference\nexport const BlossomServerList = 10063\nexport type BlossomServerList = typeof BlossomServerList\nexport const NWCWalletInfo = 13194\nexport type NWCWalletInfo = typeof NWCWalletInfo\nexport const LightningPubRPC = 21000\nexport type LightningPubRPC = typeof LightningPubRPC\nexport const ClientAuth = 22242\nexport type ClientAuth = typeof ClientAuth\nexport const NWCWalletRequest = 23194\nexport type NWCWalletRequest = typeof NWCWalletRequest\nexport const NWCWalletResponse = 23195\nexport type NWCWalletResponse = typeof NWCWalletResponse\nexport const NostrConnect = 24133\nexport type NostrConnect = typeof NostrConnect\nexport const HTTPAuth = 27235\nexport type HTTPAuth = typeof HTTPAuth\nexport const Followsets = 30000\nexport type Followsets = typeof Followsets\nexport const Genericlists = 30001\nexport type Genericlists = typeof Genericlists\nexport const Relaysets = 30002\nexport type Relaysets = typeof Relaysets\nexport const Bookmarksets = 30003\nexport type Bookmarksets = typeof Bookmarksets\nexport const Curationsets = 30004\nexport type Curationsets = typeof Curationsets\nexport const ProfileBadges = 30008\nexport type ProfileBadges = typeof ProfileBadges\nexport const BadgeDefinition = 30009\nexport type BadgeDefinition = typeof BadgeDefinition\nexport const Interestsets = 30015\nexport type Interestsets = typeof Interestsets\nexport const CreateOrUpdateStall = 30017\nexport type CreateOrUpdateStall = typeof CreateOrUpdateStall\nexport const CreateOrUpdateProduct = 30018\nexport type CreateOrUpdateProduct = typeof CreateOrUpdateProduct\nexport const LongFormArticle = 30023\nexport type LongFormArticle = typeof LongFormArticle\nexport const DraftLong = 30024\nexport type DraftLong = typeof DraftLong\nexport const Emojisets = 30030\nexport type Emojisets = typeof Emojisets\nexport const Application = 30078\nexport type Application = typeof Application\nexport const LiveEvent = 30311\nexport type LiveEvent = typeof LiveEvent\nexport const UserStatuses = 30315\nexport type UserStatuses = typeof UserStatuses\nexport const ClassifiedListing = 30402\nexport type ClassifiedListing = typeof ClassifiedListing\nexport const DraftClassifiedListing = 30403\nexport type DraftClassifiedListing = typeof DraftClassifiedListing\nexport const Date = 31922\nexport type Date = typeof Date\nexport const Time = 31923\nexport type Time = typeof Time\nexport const Calendar = 31924\nexport type Calendar = typeof Calendar\nexport const CalendarEventRSVP = 31925\nexport type CalendarEventRSVP = typeof CalendarEventRSVP\nexport const RelayReview = 31987\nexport type RelayReview = typeof RelayReview\nexport const Handlerrecommendation = 31989\nexport type Handlerrecommendation = typeof Handlerrecommendation\nexport const Handlerinformation = 31990\nexport type Handlerinformation = typeof Handlerinformation\nexport const CommunityDefinition = 34550\nexport type CommunityDefinition = typeof CommunityDefinition\nexport const GroupMetadata = 39000\nexport type GroupMetadata = typeof GroupMetadata\n", "import { Event } from './core.ts'\nimport { isAddressableKind, isReplaceableKind } from './kinds.ts'\n\nexport type Filter = {\n  ids?: string[]\n  kinds?: number[]\n  authors?: string[]\n  since?: number\n  until?: number\n  limit?: number\n  search?: string\n  [key: `#${string}`]: string[] | undefined\n}\n\nexport function matchFilter(filter: Filter, event: Event): boolean {\n  if (filter.ids && filter.ids.indexOf(event.id) === -1) {\n    return false\n  }\n  if (filter.kinds && filter.kinds.indexOf(event.kind) === -1) {\n    return false\n  }\n  if (filter.authors && filter.authors.indexOf(event.pubkey) === -1) {\n    return false\n  }\n\n  for (let f in filter) {\n    if (f[0] === '#') {\n      let tagName = f.slice(1)\n      let values = filter[`#${tagName}`]\n      if (values && !event.tags.find(([t, v]) => t === f.slice(1) && values!.indexOf(v) !== -1)) return false\n    }\n  }\n\n  if (filter.since && event.created_at < filter.since) return false\n  if (filter.until && event.created_at > filter.until) return false\n\n  return true\n}\n\nexport function matchFilters(filters: Filter[], event: Event): boolean {\n  for (let i = 0; i < filters.length; i++) {\n    if (matchFilter(filters[i], event)) {\n      return true\n    }\n  }\n  return false\n}\n\nexport function mergeFilters(...filters: Filter[]): Filter {\n  let result: Filter = {}\n  for (let i = 0; i < filters.length; i++) {\n    let filter = filters[i]\n    Object.entries(filter).forEach(([property, values]) => {\n      if (property === 'kinds' || property === 'ids' || property === 'authors' || property[0] === '#') {\n        // @ts-ignore\n        result[property] = result[property] || []\n        // @ts-ignore\n        for (let v = 0; v < values.length; v++) {\n          // @ts-ignore\n          let value = values[v]\n          // @ts-ignore\n          if (!result[property].includes(value)) result[property].push(value)\n        }\n      }\n    })\n\n    if (filter.limit && (!result.limit || filter.limit > result.limit)) result.limit = filter.limit\n    if (filter.until && (!result.until || filter.until > result.until)) result.until = filter.until\n    if (filter.since && (!result.since || filter.since < result.since)) result.since = filter.since\n  }\n\n  return result\n}\n\n/**\n * Calculate the intrinsic limit of a filter.\n * This function returns a positive integer, or `Infinity` if there is no intrinsic limit.\n */\nexport function getFilterLimit(filter: Filter): number {\n  if (filter.ids && !filter.ids.length) return 0\n  if (filter.kinds && !filter.kinds.length) return 0\n  if (filter.authors && !filter.authors.length) return 0\n\n  for (const [key, value] of Object.entries(filter)) {\n    if (key[0] === '#' && Array.isArray(value) && !value.length) return 0\n  }\n\n  return Math.min(\n    // The `limit` property creates an artificial limit.\n    Math.max(0, filter.limit ?? Infinity),\n\n    // There can only be one event per `id`.\n    filter.ids?.length ?? Infinity,\n\n    // Replaceable events are limited by the number of authors and kinds.\n    filter.authors?.length && filter.kinds?.every(kind => isReplaceableKind(kind))\n      ? filter.authors.length * filter.kinds.length\n      : Infinity,\n\n    // Parameterized replaceable events are limited by the number of authors, kinds, and \"d\" tags.\n    filter.authors?.length && filter.kinds?.every(kind => isAddressableKind(kind)) && filter['#d']?.length\n      ? filter.authors.length * filter.kinds.length * filter['#d'].length\n      : Infinity,\n  )\n}\n", "export function getHex64(json: string, field: string): string {\n  let len = field.length + 3\n  let idx = json.indexOf(`\"${field}\":`) + len\n  let s = json.slice(idx).indexOf(`\"`) + idx + 1\n  return json.slice(s, s + 64)\n}\n\nexport function getInt(json: string, field: string): number {\n  let len = field.length\n  let idx = json.indexOf(`\"${field}\":`) + len + 3\n  let sliced = json.slice(idx)\n  let end = Math.min(sliced.indexOf(','), sliced.indexOf('}'))\n  return parseInt(sliced.slice(0, end), 10)\n}\n\nexport function getSubscriptionId(json: string): string | null {\n  let idx = json.slice(0, 22).indexOf(`\"EVENT\"`)\n  if (idx === -1) return null\n\n  let pstart = json.slice(idx + 7 + 1).indexOf(`\"`)\n  if (pstart === -1) return null\n  let start = idx + 7 + 1 + pstart\n\n  let pend = json.slice(start + 1, 80).indexOf(`\"`)\n  if (pend === -1) return null\n  let end = start + 1 + pend\n\n  return json.slice(start + 1, end)\n}\n\nexport function matchEventId(json: string, id: string): boolean {\n  return id === getHex64(json, 'id')\n}\n\nexport function matchEventPubkey(json: string, pubkey: string): boolean {\n  return pubkey === getHex64(json, 'pubkey')\n}\n\nexport function matchEventKind(json: string, kind: number): boolean {\n  return kind === getInt(json, 'kind')\n}\n", "import { EventTemplate } from './core.ts'\nimport { ClientAuth } from './kinds.ts'\n\n/**\n * creates an EventTemplate for an AUTH event to be signed.\n */\nexport function makeAuthEvent(relayURL: string, challenge: string): EventTemplate {\n  return {\n    kind: ClientAuth,\n    created_at: Math.floor(Date.now() / 1000),\n    tags: [\n      ['relay', relayURL],\n      ['challenge', challenge],\n    ],\n    content: '',\n  }\n}\n", "import { verifiedSymbol, type Event, type Nostr, VerifiedEvent } from './core.ts'\n\nexport async function yieldThread() {\n  return new Promise<void>((resolve, reject) => {\n    try {\n      // Check if MessageChannel is available\n      if (typeof MessageChannel !== 'undefined') {\n        const ch = new MessageChannel()\n        const handler = () => {\n          // @ts-ignore (typescript thinks this property should be called `removeListener`, but in fact it's `removeEventListener`)\n          ch.port1.removeEventListener('message', handler)\n          resolve()\n        }\n        // @ts-ignore (typescript thinks this property should be called `addListener`, but in fact it's `addEventListener`)\n        ch.port1.addEventListener('message', handler)\n        ch.port2.postMessage(0)\n        ch.port1.start()\n      } else {\n        if (typeof setImmediate !== 'undefined') {\n          setImmediate(resolve)\n        } else if (typeof setTimeout !== 'undefined') {\n          setTimeout(resolve, 0)\n        } else {\n          // Last resort - resolve immediately\n          resolve()\n        }\n      }\n    } catch (e) {\n      console.error('during yield: ', e)\n      reject(e)\n    }\n  })\n}\n\nexport const alwaysTrue: Nostr['verifyEvent'] = (t: Event): t is VerifiedEvent => {\n  t[verifiedSymbol] = true\n  return true\n}\n", "/* global WebSocket */\n\nimport type { Event, EventTemplate, VerifiedEvent, Nostr, NostrEvent } from './core.ts'\nimport { matchFilters, type Filter } from './filter.ts'\nimport { getHex64, getSubscriptionId } from './fakejson.ts'\nimport { Queue, normalizeURL } from './utils.ts'\nimport { makeAuthEvent } from './nip42.ts'\nimport { yieldThread } from './helpers.ts'\n\ntype RelayWebSocket = WebSocket & {\n  ping?(): void\n  on?(event: 'pong', listener: () => void): any\n}\n\nexport type AbstractRelayConstructorOptions = {\n  verifyEvent: Nostr['verifyEvent']\n  websocketImplementation?: typeof WebSocket\n  enablePing?: boolean\n  enableReconnect?: boolean\n}\n\nexport class SendingOnClosedConnection extends Error {\n  constructor(message: string, relay: string) {\n    super(`Tried to send message '${message} on a closed connection to ${relay}.`)\n    this.name = 'SendingOnClosedConnection'\n  }\n}\n\nexport class AbstractRelay {\n  public readonly url: string\n  private _connected: boolean = false\n\n  public onclose: (() => void) | null = null\n  public onnotice: (msg: string) => void = msg => console.debug(`NOTICE from ${this.url}: ${msg}`)\n  public onauth: undefined | ((evt: EventTemplate) => Promise<VerifiedEvent>)\n\n  public baseEoseTimeout: number = 4400\n  public publishTimeout: number = 4400\n  public pingFrequency: number = 29000\n  public pingTimeout: number = 20000\n  public resubscribeBackoff: number[] = [10000, 10000, 10000, 20000, 20000, 30000, 60000]\n  public openSubs: Map<string, Subscription> = new Map()\n  public enablePing: boolean | undefined\n  public enableReconnect: boolean\n  private reconnectTimeoutHandle: ReturnType<typeof setTimeout> | undefined\n  private pingIntervalHandle: ReturnType<typeof setInterval> | undefined\n  private reconnectAttempts: number = 0\n  private closedIntentionally: boolean = false\n\n  private connectionPromise: Promise<void> | undefined\n  private openCountRequests = new Map<string, CountResolver>()\n  private openEventPublishes = new Map<string, EventPublishResolver>()\n  private ws: RelayWebSocket | undefined\n  private incomingMessageQueue = new Queue<string>()\n  private queueRunning = false\n  private challenge: string | undefined\n  private authPromise: Promise<string> | undefined\n  private serial: number = 0\n  private verifyEvent: Nostr['verifyEvent']\n\n  private _WebSocket: typeof WebSocket\n\n  constructor(url: string, opts: AbstractRelayConstructorOptions) {\n    this.url = normalizeURL(url)\n    this.verifyEvent = opts.verifyEvent\n    this._WebSocket = opts.websocketImplementation || WebSocket\n    this.enablePing = opts.enablePing\n    this.enableReconnect = opts.enableReconnect || false\n  }\n\n  static async connect(\n    url: string,\n    opts: AbstractRelayConstructorOptions & Parameters<AbstractRelay['connect']>[0],\n  ): Promise<AbstractRelay> {\n    const relay = new AbstractRelay(url, opts)\n    await relay.connect(opts)\n    return relay\n  }\n\n  private closeAllSubscriptions(reason: string) {\n    for (let [_, sub] of this.openSubs) {\n      sub.close(reason)\n    }\n    this.openSubs.clear()\n\n    for (let [_, ep] of this.openEventPublishes) {\n      ep.reject(new Error(reason))\n    }\n    this.openEventPublishes.clear()\n\n    for (let [_, cr] of this.openCountRequests) {\n      cr.reject(new Error(reason))\n    }\n    this.openCountRequests.clear()\n  }\n\n  public get connected(): boolean {\n    return this._connected\n  }\n\n  private async reconnect(): Promise<void> {\n    const backoff = this.resubscribeBackoff[Math.min(this.reconnectAttempts, this.resubscribeBackoff.length - 1)]\n    this.reconnectAttempts++\n\n    this.reconnectTimeoutHandle = setTimeout(async () => {\n      try {\n        await this.connect()\n      } catch (err) {\n        // this will be called again through onclose/onerror\n      }\n    }, backoff)\n  }\n\n  private handleHardClose(reason: string) {\n    if (this.pingIntervalHandle) {\n      clearInterval(this.pingIntervalHandle)\n      this.pingIntervalHandle = undefined\n    }\n\n    this._connected = false\n    this.connectionPromise = undefined\n\n    const wasIntentional = this.closedIntentionally\n    this.closedIntentionally = false // reset for next time\n\n    this.onclose?.()\n\n    if (this.enableReconnect && !wasIntentional) {\n      this.reconnect()\n    } else {\n      this.closeAllSubscriptions(reason)\n    }\n  }\n\n  public async connect(opts?: { timeout?: number; abort?: AbortSignal }): Promise<void> {\n    let connectionTimeoutHandle: ReturnType<typeof setTimeout> | undefined\n\n    if (this.connectionPromise) return this.connectionPromise\n\n    this.challenge = undefined\n    this.authPromise = undefined\n    this.connectionPromise = new Promise((resolve, reject) => {\n      if (opts?.timeout) {\n        connectionTimeoutHandle = setTimeout(() => {\n          reject('connection timed out')\n          this.connectionPromise = undefined\n          this.onclose?.()\n          this.closeAllSubscriptions('relay connection timed out')\n        }, opts.timeout)\n      }\n\n      if (opts?.abort) {\n        opts.abort.onabort = reject\n      }\n\n      try {\n        this.ws = new this._WebSocket(this.url)\n      } catch (err) {\n        clearTimeout(connectionTimeoutHandle)\n        reject(err)\n        return\n      }\n\n      this.ws.onopen = () => {\n        if (this.reconnectTimeoutHandle) {\n          clearTimeout(this.reconnectTimeoutHandle)\n          this.reconnectTimeoutHandle = undefined\n        }\n        clearTimeout(connectionTimeoutHandle)\n        this._connected = true\n\n        const isReconnection = this.reconnectAttempts > 0\n        this.reconnectAttempts = 0\n\n        // resubscribe to all open subscriptions\n        for (const sub of this.openSubs.values()) {\n          sub.eosed = false\n          if (isReconnection) {\n            for (let f = 0; f < sub.filters.length; f++) {\n              if (sub.lastEmitted) {\n                sub.filters[f].since = sub.lastEmitted + 1\n              }\n            }\n          }\n          sub.fire()\n        }\n\n        if (this.enablePing) {\n          this.pingIntervalHandle = setInterval(() => this.pingpong(), this.pingFrequency)\n        }\n        resolve()\n      }\n\n      this.ws.onerror = ev => {\n        clearTimeout(connectionTimeoutHandle)\n        reject((ev as any).message || 'websocket error')\n        this.handleHardClose('relay connection errored')\n      }\n\n      this.ws.onclose = ev => {\n        clearTimeout(connectionTimeoutHandle)\n        reject((ev as any).message || 'websocket closed')\n        this.handleHardClose('relay connection closed')\n      }\n\n      this.ws.onmessage = this._onmessage.bind(this)\n    })\n\n    return this.connectionPromise\n  }\n\n  private waitForPingPong() {\n    return new Promise(resolve => {\n      // listen for pong\n      ;(this.ws as any).once('pong', () => resolve(true))\n      // send a ping\n      this.ws!.ping!()\n    })\n  }\n\n  private waitForDummyReq() {\n    return new Promise((resolve, reject) => {\n      if (!this.connectionPromise) return reject(new Error(`no connection to ${this.url}, can't ping`))\n\n      // make a dummy request with expected empty eose reply\n      // [\"REQ\", \"_\", {\"ids\":[\"aaaa...aaaa\"], \"limit\": 0}]\n      try {\n        const sub = this.subscribe(\n          [{ ids: ['aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'], limit: 0 }],\n          {\n            label: 'forced-ping',\n            oneose: () => {\n              resolve(true)\n              sub.close()\n            },\n            onclose() {\n              // if we get a CLOSED it's because the relay is alive\n              resolve(true)\n            },\n            eoseTimeout: this.pingTimeout + 1000,\n          },\n        )\n      } catch (err) {\n        reject(err)\n      }\n    })\n  }\n\n  // nodejs requires this magic here to ensure connections are closed when internet goes off and stuff\n  // in browsers it's done automatically. see https://github.com/nbd-wtf/nostr-tools/issues/491\n  private async pingpong() {\n    // if the websocket is connected\n    if (this.ws?.readyState === 1) {\n      // wait for either a ping-pong reply or a timeout\n      const result = await Promise.any([\n        // browsers don't have ping so use a dummy req\n        this.ws && this.ws.ping && (this.ws as any).once ? this.waitForPingPong() : this.waitForDummyReq(),\n        new Promise(res => setTimeout(() => res(false), this.pingTimeout)),\n      ])\n\n      if (!result) {\n        // pingpong closing socket\n        if (this.ws?.readyState === this._WebSocket.OPEN) {\n          this.ws?.close()\n        }\n      }\n    }\n  }\n\n  private async runQueue() {\n    this.queueRunning = true\n    while (true) {\n      if (false === this.handleNext()) {\n        break\n      }\n      await yieldThread()\n    }\n    this.queueRunning = false\n  }\n\n  private handleNext(): undefined | false {\n    const json = this.incomingMessageQueue.dequeue()\n    if (!json) {\n      return false\n    }\n\n    // shortcut EVENT sub\n    const subid = getSubscriptionId(json)\n    if (subid) {\n      const so = this.openSubs.get(subid as string)\n      if (!so) {\n        // this is an EVENT message, but for a subscription we don't have, so just stop here\n        return\n      }\n\n      // this will be called only when this message is a EVENT message for a subscription we have\n      // we do this before parsing the JSON to not have to do that for duplicate events\n      //   since JSON parsing is slow\n      const id = getHex64(json, 'id')\n      const alreadyHave = so.alreadyHaveEvent?.(id)\n\n      // notify any interested client that the relay has this event\n      // (do this after alreadyHaveEvent() because the client may rely on this to answer that)\n      so.receivedEvent?.(this, id)\n\n      if (alreadyHave) {\n        // if we had already seen this event we can just stop here\n        return\n      }\n    }\n\n    try {\n      let data = JSON.parse(json)\n      // we won't do any checks against the data since all failures (i.e. invalid messages from relays)\n      // will naturally be caught by the encompassing try..catch block\n\n      switch (data[0]) {\n        case 'EVENT': {\n          const so = this.openSubs.get(data[1] as string) as Subscription\n          const event = data[2] as NostrEvent\n          if (this.verifyEvent(event) && matchFilters(so.filters, event)) {\n            so.onevent(event)\n          }\n          if (!so.lastEmitted || so.lastEmitted < event.created_at) so.lastEmitted = event.created_at\n          return\n        }\n        case 'COUNT': {\n          const id: string = data[1]\n          const payload = data[2] as { count: number }\n          const cr = this.openCountRequests.get(id) as CountResolver\n          if (cr) {\n            cr.resolve(payload.count)\n            this.openCountRequests.delete(id)\n          }\n          return\n        }\n        case 'EOSE': {\n          const so = this.openSubs.get(data[1] as string)\n          if (!so) return\n          so.receivedEose()\n          return\n        }\n        case 'OK': {\n          const id: string = data[1]\n          const ok: boolean = data[2]\n          const reason: string = data[3]\n          const ep = this.openEventPublishes.get(id) as EventPublishResolver\n          if (ep) {\n            clearTimeout(ep.timeout)\n            if (ok) ep.resolve(reason)\n            else ep.reject(new Error(reason))\n            this.openEventPublishes.delete(id)\n          }\n          return\n        }\n        case 'CLOSED': {\n          const id: string = data[1]\n          const so = this.openSubs.get(id)\n          if (!so) return\n          so.closed = true\n          so.close(data[2] as string)\n          return\n        }\n        case 'NOTICE': {\n          this.onnotice(data[1] as string)\n          return\n        }\n        case 'AUTH': {\n          this.challenge = data[1] as string\n          if (this.onauth) {\n            this.auth(this.onauth)\n          }\n          return\n        }\n        default: {\n          const so = this.openSubs.get(data[1])\n          so?.oncustom?.(data)\n          return\n        }\n      }\n    } catch (err) {\n      return\n    }\n  }\n\n  public async send(message: string) {\n    if (!this.connectionPromise) throw new SendingOnClosedConnection(message, this.url)\n\n    this.connectionPromise.then(() => {\n      this.ws?.send(message)\n    })\n  }\n\n  public async auth(signAuthEvent: (evt: EventTemplate) => Promise<VerifiedEvent>): Promise<string> {\n    const challenge = this.challenge\n    if (!challenge) throw new Error(\"can't perform auth, no challenge was received\")\n    if (this.authPromise) return this.authPromise\n\n    this.authPromise = new Promise<string>(async (resolve, reject) => {\n      try {\n        let evt = await signAuthEvent(makeAuthEvent(this.url, challenge))\n        let timeout = setTimeout(() => {\n          let ep = this.openEventPublishes.get(evt.id) as EventPublishResolver\n          if (ep) {\n            ep.reject(new Error('auth timed out'))\n            this.openEventPublishes.delete(evt.id)\n          }\n        }, this.publishTimeout)\n        this.openEventPublishes.set(evt.id, { resolve, reject, timeout })\n        this.send('[\"AUTH\",' + JSON.stringify(evt) + ']')\n      } catch (err) {\n        console.warn('subscribe auth function failed:', err)\n      }\n    })\n    return this.authPromise\n  }\n\n  public async publish(event: Event): Promise<string> {\n    const ret = new Promise<string>((resolve, reject) => {\n      const timeout = setTimeout(() => {\n        const ep = this.openEventPublishes.get(event.id) as EventPublishResolver\n        if (ep) {\n          ep.reject(new Error('publish timed out'))\n          this.openEventPublishes.delete(event.id)\n        }\n      }, this.publishTimeout)\n      this.openEventPublishes.set(event.id, { resolve, reject, timeout })\n    })\n    this.send('[\"EVENT\",' + JSON.stringify(event) + ']')\n    return ret\n  }\n\n  public async count(filters: Filter[], params: { id?: string | null }): Promise<number> {\n    this.serial++\n    const id = params?.id || 'count:' + this.serial\n    const ret = new Promise<number>((resolve, reject) => {\n      this.openCountRequests.set(id, { resolve, reject })\n    })\n    this.send('[\"COUNT\",\"' + id + '\",' + JSON.stringify(filters).substring(1))\n    return ret\n  }\n\n  public subscribe(\n    filters: Filter[],\n    params: Partial<SubscriptionParams> & { label?: string; id?: string },\n  ): Subscription {\n    const sub = this.prepareSubscription(filters, params)\n    sub.fire()\n\n    if (params.abort) {\n      params.abort.onabort = () => sub.close(String(params.abort!.reason || '<aborted>'))\n    }\n\n    return sub\n  }\n\n  public prepareSubscription(\n    filters: Filter[],\n    params: Partial<SubscriptionParams> & { label?: string; id?: string },\n  ): Subscription {\n    this.serial++\n    const id = params.id || (params.label ? params.label + ':' : 'sub:') + this.serial\n    const subscription = new Subscription(this, id, filters, params)\n    this.openSubs.set(id, subscription)\n    return subscription\n  }\n\n  public close() {\n    this.closedIntentionally = true\n    if (this.reconnectTimeoutHandle) {\n      clearTimeout(this.reconnectTimeoutHandle)\n      this.reconnectTimeoutHandle = undefined\n    }\n    if (this.pingIntervalHandle) {\n      clearInterval(this.pingIntervalHandle)\n      this.pingIntervalHandle = undefined\n    }\n    this.closeAllSubscriptions('relay connection closed by us')\n    this._connected = false\n    this.onclose?.()\n    if (this.ws?.readyState === this._WebSocket.OPEN) {\n      this.ws?.close()\n    }\n  }\n\n  // this is the function assigned to this.ws.onmessage\n  // it's exposed for testing and debugging purposes\n  public _onmessage(ev: MessageEvent<any>) {\n    this.incomingMessageQueue.enqueue(ev.data as string)\n    if (!this.queueRunning) {\n      this.runQueue()\n    }\n  }\n}\n\nexport class Subscription {\n  public readonly relay: AbstractRelay\n  public readonly id: string\n\n  public lastEmitted: number | undefined\n  public closed: boolean = false\n  public eosed: boolean = false\n  public filters: Filter[]\n  public alreadyHaveEvent: ((id: string) => boolean) | undefined\n  public receivedEvent: ((relay: AbstractRelay, id: string) => void) | undefined\n\n  public onevent: (evt: Event) => void\n  public oneose: (() => void) | undefined\n  public onclose: ((reason: string) => void) | undefined\n\n  // will get any messages that have this subscription id as their second item and are not default standard\n  public oncustom: ((msg: string[]) => void) | undefined\n\n  public eoseTimeout: number\n  private eoseTimeoutHandle: ReturnType<typeof setTimeout> | undefined\n\n  constructor(relay: AbstractRelay, id: string, filters: Filter[], params: SubscriptionParams) {\n    if (filters.length === 0) throw new Error(\"subscription can't be created with zero filters\")\n\n    this.relay = relay\n    this.filters = filters\n    this.id = id\n    this.alreadyHaveEvent = params.alreadyHaveEvent\n    this.receivedEvent = params.receivedEvent\n    this.eoseTimeout = params.eoseTimeout || relay.baseEoseTimeout\n\n    this.oneose = params.oneose\n    this.onclose = params.onclose\n    this.onevent =\n      params.onevent ||\n      (event => {\n        console.warn(\n          `onevent() callback not defined for subscription '${this.id}' in relay ${this.relay.url}. event received:`,\n          event,\n        )\n      })\n  }\n\n  public fire() {\n    this.relay.send('[\"REQ\",\"' + this.id + '\",' + JSON.stringify(this.filters).substring(1))\n\n    // only now we start counting the eoseTimeout\n    this.eoseTimeoutHandle = setTimeout(this.receivedEose.bind(this), this.eoseTimeout)\n  }\n\n  public receivedEose() {\n    if (this.eosed) return\n    clearTimeout(this.eoseTimeoutHandle)\n    this.eosed = true\n    this.oneose?.()\n  }\n\n  public close(reason: string = 'closed by caller') {\n    if (!this.closed && this.relay.connected) {\n      // if the connection was closed by the user calling .close() we will send a CLOSE message\n      // otherwise this._open will be already set to false so we will skip this\n      try {\n        this.relay.send('[\"CLOSE\",' + JSON.stringify(this.id) + ']')\n      } catch (err) {\n        if (err instanceof SendingOnClosedConnection) {\n          /* doesn't matter, it's ok */\n        } else {\n          throw err\n        }\n      }\n      this.closed = true\n    }\n    this.relay.openSubs.delete(this.id)\n    this.onclose?.(reason)\n  }\n}\n\nexport type SubscriptionParams = {\n  onevent?: (evt: Event) => void\n  oneose?: () => void\n  onclose?: (reason: string) => void\n  alreadyHaveEvent?: (id: string) => boolean\n  receivedEvent?: (relay: AbstractRelay, id: string) => void\n  eoseTimeout?: number\n  abort?: AbortSignal\n}\n\nexport type CountResolver = {\n  resolve: (count: number) => void\n  reject: (err: Error) => void\n}\n\nexport type EventPublishResolver = {\n  resolve: (reason: string) => void\n  reject: (err: Error) => void\n  timeout: ReturnType<typeof setTimeout>\n}\n", "/* global WebSocket */\n\nimport {\n  AbstractRelay as AbstractRelay,\n  SubscriptionParams,\n  Subscription,\n  type AbstractRelayConstructorOptions,\n} from './abstract-relay.ts'\nimport { normalizeURL } from './utils.ts'\n\nimport type { Event, EventTemplate, Nostr, VerifiedEvent } from './core.ts'\nimport { type Filter } from './filter.ts'\nimport { alwaysTrue } from './helpers.ts'\n\nexport type SubCloser = { close: (reason?: string) => void }\n\nexport type AbstractPoolConstructorOptions = AbstractRelayConstructorOptions & {\n  // automaticallyAuth takes a relay URL and should return null\n  // in case that relay shouldn't be authenticated against\n  // or a function to sign the AUTH event template otherwise (that function may still throw in case of failure)\n  automaticallyAuth?: (relayURL: string) => null | ((event: EventTemplate) => Promise<VerifiedEvent>)\n}\n\nexport type SubscribeManyParams = Omit<SubscriptionParams, 'onclose'> & {\n  maxWait?: number\n  abort?: AbortSignal\n  onclose?: (reasons: string[]) => void\n  onauth?: (event: EventTemplate) => Promise<VerifiedEvent>\n  id?: string\n  label?: string\n}\n\nexport class AbstractSimplePool {\n  protected relays: Map<string, AbstractRelay> = new Map()\n  public seenOn: Map<string, Set<AbstractRelay>> = new Map()\n  public trackRelays: boolean = false\n\n  public verifyEvent: Nostr['verifyEvent']\n  public enablePing: boolean | undefined\n  public enableReconnect: boolean\n  public automaticallyAuth?: (relayURL: string) => null | ((event: EventTemplate) => Promise<VerifiedEvent>)\n  public trustedRelayURLs: Set<string> = new Set()\n\n  private _WebSocket?: typeof WebSocket\n\n  constructor(opts: AbstractPoolConstructorOptions) {\n    this.verifyEvent = opts.verifyEvent\n    this._WebSocket = opts.websocketImplementation\n    this.enablePing = opts.enablePing\n    this.enableReconnect = opts.enableReconnect || false\n    this.automaticallyAuth = opts.automaticallyAuth\n  }\n\n  async ensureRelay(\n    url: string,\n    params?: {\n      connectionTimeout?: number\n      abort?: AbortSignal\n    },\n  ): Promise<AbstractRelay> {\n    url = normalizeURL(url)\n\n    let relay = this.relays.get(url)\n    if (!relay) {\n      relay = new AbstractRelay(url, {\n        verifyEvent: this.trustedRelayURLs.has(url) ? alwaysTrue : this.verifyEvent,\n        websocketImplementation: this._WebSocket,\n        enablePing: this.enablePing,\n        enableReconnect: this.enableReconnect,\n      })\n      relay.onclose = () => {\n        if (relay && !relay.enableReconnect) {\n          this.relays.delete(url)\n        }\n      }\n      this.relays.set(url, relay)\n    }\n\n    if (this.automaticallyAuth) {\n      const authSignerFn = this.automaticallyAuth(url)\n      if (authSignerFn) {\n        relay.onauth = authSignerFn\n      }\n    }\n\n    await relay.connect({\n      timeout: params?.connectionTimeout,\n      abort: params?.abort,\n    })\n\n    return relay\n  }\n\n  close(relays: string[]) {\n    relays.map(normalizeURL).forEach(url => {\n      this.relays.get(url)?.close()\n      this.relays.delete(url)\n    })\n  }\n\n  subscribe(relays: string[], filter: Filter, params: SubscribeManyParams): SubCloser {\n    const request: { url: string; filter: Filter }[] = []\n    for (let i = 0; i < relays.length; i++) {\n      const url = normalizeURL(relays[i])\n      if (!request.find(r => r.url === url)) {\n        request.push({ url, filter: filter })\n      }\n    }\n\n    return this.subscribeMap(request, params)\n  }\n\n  subscribeMany(relays: string[], filter: Filter, params: SubscribeManyParams): SubCloser {\n    const request: { url: string; filter: Filter }[] = []\n    const uniqUrls: string[] = []\n    for (let i = 0; i < relays.length; i++) {\n      const url = normalizeURL(relays[i])\n      if (uniqUrls.indexOf(url) === -1) {\n        uniqUrls.push(url)\n        request.push({ url, filter: filter })\n      }\n    }\n\n    return this.subscribeMap(request, params)\n  }\n\n  subscribeMap(requests: { url: string; filter: Filter }[], params: SubscribeManyParams): SubCloser {\n    const grouped = new Map<string, Filter[]>()\n    for (const req of requests) {\n      const { url, filter } = req\n      if (!grouped.has(url)) grouped.set(url, [])\n      grouped.get(url)!.push(filter)\n    }\n    const groupedRequests = Array.from(grouped.entries()).map(([url, filters]) => ({ url, filters }))\n\n    if (this.trackRelays) {\n      params.receivedEvent = (relay: AbstractRelay, id: string) => {\n        let set = this.seenOn.get(id)\n        if (!set) {\n          set = new Set()\n          this.seenOn.set(id, set)\n        }\n        set.add(relay)\n      }\n    }\n\n    const _knownIds = new Set<string>()\n    const subs: Subscription[] = []\n\n    // batch all EOSEs into a single\n    const eosesReceived: boolean[] = []\n    let handleEose = (i: number) => {\n      if (eosesReceived[i]) return // do not act twice for the same relay\n      eosesReceived[i] = true\n      if (eosesReceived.filter(a => a).length === groupedRequests.length) {\n        params.oneose?.()\n        handleEose = () => {}\n      }\n    }\n    // batch all closes into a single\n    const closesReceived: string[] = []\n    let handleClose = (i: number, reason: string) => {\n      if (closesReceived[i]) return // do not act twice for the same relay\n      handleEose(i)\n      closesReceived[i] = reason\n      if (closesReceived.filter(a => a).length === groupedRequests.length) {\n        params.onclose?.(closesReceived)\n        handleClose = () => {}\n      }\n    }\n\n    const localAlreadyHaveEventHandler = (id: string) => {\n      if (params.alreadyHaveEvent?.(id)) {\n        return true\n      }\n      const have = _knownIds.has(id)\n      _knownIds.add(id)\n      return have\n    }\n\n    // open a subscription in all given relays\n    const allOpened = Promise.all(\n      groupedRequests.map(async ({ url, filters }, i) => {\n        let relay: AbstractRelay\n        try {\n          relay = await this.ensureRelay(url, {\n            connectionTimeout: params.maxWait ? Math.max(params.maxWait * 0.8, params.maxWait - 1000) : undefined,\n            abort: params.abort,\n          })\n        } catch (err) {\n          handleClose(i, (err as any)?.message || String(err))\n          return\n        }\n\n        let subscription = relay.subscribe(filters, {\n          ...params,\n          oneose: () => handleEose(i),\n          onclose: reason => {\n            if (reason.startsWith('auth-required: ') && params.onauth) {\n              relay\n                .auth(params.onauth)\n                .then(() => {\n                  relay.subscribe(filters, {\n                    ...params,\n                    oneose: () => handleEose(i),\n                    onclose: reason => {\n                      handleClose(i, reason) // the second time we won't try to auth anymore\n                    },\n                    alreadyHaveEvent: localAlreadyHaveEventHandler,\n                    eoseTimeout: params.maxWait,\n                    abort: params.abort,\n                  })\n                })\n                .catch(err => {\n                  handleClose(i, `auth was required and attempted, but failed with: ${err}`)\n                })\n            } else {\n              handleClose(i, reason)\n            }\n          },\n          alreadyHaveEvent: localAlreadyHaveEventHandler,\n          eoseTimeout: params.maxWait,\n          abort: params.abort,\n        })\n\n        subs.push(subscription)\n      }),\n    )\n\n    return {\n      async close(reason?: string) {\n        await allOpened\n        subs.forEach(sub => {\n          sub.close(reason)\n        })\n      },\n    }\n  }\n\n  subscribeEose(\n    relays: string[],\n    filter: Filter,\n    params: Pick<SubscribeManyParams, 'label' | 'id' | 'onevent' | 'onclose' | 'maxWait' | 'onauth'>,\n  ): SubCloser {\n    const subcloser = this.subscribe(relays, filter, {\n      ...params,\n      oneose() {\n        subcloser.close('closed automatically on eose')\n      },\n    })\n    return subcloser\n  }\n\n  subscribeManyEose(\n    relays: string[],\n    filter: Filter,\n    params: Pick<SubscribeManyParams, 'label' | 'id' | 'onevent' | 'onclose' | 'maxWait' | 'onauth'>,\n  ): SubCloser {\n    const subcloser = this.subscribeMany(relays, filter, {\n      ...params,\n      oneose() {\n        subcloser.close('closed automatically on eose')\n      },\n    })\n    return subcloser\n  }\n\n  async querySync(\n    relays: string[],\n    filter: Filter,\n    params?: Pick<SubscribeManyParams, 'label' | 'id' | 'maxWait'>,\n  ): Promise<Event[]> {\n    return new Promise(async resolve => {\n      const events: Event[] = []\n      this.subscribeEose(relays, filter, {\n        ...params,\n        onevent(event: Event) {\n          events.push(event)\n        },\n        onclose(_: string[]) {\n          resolve(events)\n        },\n      })\n    })\n  }\n\n  async get(\n    relays: string[],\n    filter: Filter,\n    params?: Pick<SubscribeManyParams, 'label' | 'id' | 'maxWait'>,\n  ): Promise<Event | null> {\n    filter.limit = 1\n    const events = await this.querySync(relays, filter, params)\n    events.sort((a, b) => b.created_at - a.created_at)\n    return events[0] || null\n  }\n\n  publish(\n    relays: string[],\n    event: Event,\n    options?: { onauth?: (evt: EventTemplate) => Promise<VerifiedEvent> },\n  ): Promise<string>[] {\n    return relays.map(normalizeURL).map(async (url, i, arr) => {\n      if (arr.indexOf(url) !== i) {\n        // duplicate\n        return Promise.reject('duplicate url')\n      }\n\n      let r = await this.ensureRelay(url)\n      return r\n        .publish(event)\n        .catch(async err => {\n          if (err instanceof Error && err.message.startsWith('auth-required: ') && options?.onauth) {\n            await r.auth(options.onauth)\n            return r.publish(event) // retry\n          }\n          throw err\n        })\n        .then(reason => {\n          if (this.trackRelays) {\n            let set = this.seenOn.get(event.id)\n            if (!set) {\n              set = new Set()\n              this.seenOn.set(event.id, set)\n            }\n            set.add(r)\n          }\n          return reason\n        })\n    })\n  }\n\n  listConnectionStatus(): Map<string, boolean> {\n    const map = new Map<string, boolean>()\n    this.relays.forEach((relay, url) => map.set(url, relay.connected))\n\n    return map\n  }\n\n  destroy(): void {\n    this.relays.forEach(conn => conn.close())\n    this.relays = new Map()\n  }\n}\n"],
+  "mappings": "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",
   "names": ["import_utils", "i", "target", "i", "i", "reason"]
 }
Index: package/lib/esm/pool.js.map
===================================================================
--- package/lib/esm/pool.js.map
+++ package/lib/esm/pool.js.map
@@ -1,7 +1,7 @@
 {
   "version": 3,
   "sources": ["../../pure.ts", "../../core.ts", "../../utils.ts", "../../kinds.ts", "../../filter.ts", "../../fakejson.ts", "../../nip42.ts", "../../helpers.ts", "../../abstract-relay.ts", "../../abstract-pool.ts", "../../pool.ts"],
-  "sourcesContent": ["import { schnorr } from '@noble/curves/secp256k1'\nimport { bytesToHex } from '@noble/hashes/utils'\nimport { Nostr, Event, EventTemplate, UnsignedEvent, VerifiedEvent, verifiedSymbol, validateEvent } from './core.ts'\nimport { sha256 } from '@noble/hashes/sha256'\n\nimport { utf8Encoder } from './utils.ts'\n\nclass JS implements Nostr {\n  generateSecretKey(): Uint8Array {\n    return schnorr.utils.randomPrivateKey()\n  }\n  getPublicKey(secretKey: Uint8Array): string {\n    return bytesToHex(schnorr.getPublicKey(secretKey))\n  }\n  finalizeEvent(t: EventTemplate, secretKey: Uint8Array): VerifiedEvent {\n    const event = t as VerifiedEvent\n    event.pubkey = bytesToHex(schnorr.getPublicKey(secretKey))\n    event.id = getEventHash(event)\n    event.sig = bytesToHex(schnorr.sign(getEventHash(event), secretKey))\n    event[verifiedSymbol] = true\n    return event\n  }\n  verifyEvent(event: Event): event is VerifiedEvent {\n    if (typeof event[verifiedSymbol] === 'boolean') return event[verifiedSymbol]\n\n    const hash = getEventHash(event)\n    if (hash !== event.id) {\n      event[verifiedSymbol] = false\n      return false\n    }\n\n    try {\n      const valid = schnorr.verify(event.sig, hash, event.pubkey)\n      event[verifiedSymbol] = valid\n      return valid\n    } catch (err) {\n      event[verifiedSymbol] = false\n      return false\n    }\n  }\n}\n\nexport function serializeEvent(evt: UnsignedEvent): string {\n  if (!validateEvent(evt)) throw new Error(\"can't serialize event with wrong or missing properties\")\n  return JSON.stringify([0, evt.pubkey, evt.created_at, evt.kind, evt.tags, evt.content])\n}\n\nexport function getEventHash(event: UnsignedEvent): string {\n  let eventHash = sha256(utf8Encoder.encode(serializeEvent(event)))\n  return bytesToHex(eventHash)\n}\n\nconst i: JS = new JS()\n\nexport const generateSecretKey = i.generateSecretKey\nexport const getPublicKey = i.getPublicKey\nexport const finalizeEvent = i.finalizeEvent\nexport const verifyEvent = i.verifyEvent\nexport * from './core.ts'\n", "export interface Nostr {\n  generateSecretKey(): Uint8Array\n  getPublicKey(secretKey: Uint8Array): string\n  finalizeEvent(event: EventTemplate, secretKey: Uint8Array): VerifiedEvent\n  verifyEvent(event: Event): event is VerifiedEvent\n}\n\n/** Designates a verified event signature. */\nexport const verifiedSymbol = Symbol('verified')\n\nexport type NostrEvent = {\n  kind: number\n  tags: string[][]\n  content: string\n  created_at: number\n  pubkey: string\n  id: string\n  sig: string\n  [verifiedSymbol]?: boolean\n}\n\nexport type Event = NostrEvent\nexport type EventTemplate = Pick<Event, 'kind' | 'tags' | 'content' | 'created_at'>\nexport type UnsignedEvent = Pick<Event, 'kind' | 'tags' | 'content' | 'created_at' | 'pubkey'>\n\n/** An event whose signature has been verified. */\nexport interface VerifiedEvent extends Event {\n  [verifiedSymbol]: true\n}\n\nconst isRecord = (obj: unknown): obj is Record<string, unknown> => obj instanceof Object\n\nexport function validateEvent<T>(event: T): event is T & UnsignedEvent {\n  if (!isRecord(event)) return false\n  if (typeof event.kind !== 'number') return false\n  if (typeof event.content !== 'string') return false\n  if (typeof event.created_at !== 'number') return false\n  if (typeof event.pubkey !== 'string') return false\n  if (!event.pubkey.match(/^[a-f0-9]{64}$/)) return false\n\n  if (!Array.isArray(event.tags)) return false\n  for (let i = 0; i < event.tags.length; i++) {\n    let tag = event.tags[i]\n    if (!Array.isArray(tag)) return false\n    for (let j = 0; j < tag.length; j++) {\n      if (typeof tag[j] !== 'string') return false\n    }\n  }\n\n  return true\n}\n\n/**\n * Sort events in reverse-chronological order by the `created_at` timestamp,\n * and then by the event `id` (lexicographically) in case of ties.\n * This mutates the array.\n */\nexport function sortEvents(events: Event[]): Event[] {\n  return events.sort((a: NostrEvent, b: NostrEvent): number => {\n    if (a.created_at !== b.created_at) {\n      return b.created_at - a.created_at\n    }\n    return a.id.localeCompare(b.id)\n  })\n}\n", "import type { Event } from './core.ts'\n\nexport const utf8Decoder: TextDecoder = new TextDecoder('utf-8')\nexport const utf8Encoder: TextEncoder = new TextEncoder()\n\nexport { bytesToHex, hexToBytes } from '@noble/hashes/utils'\n\nexport function normalizeURL(url: string): string {\n  try {\n    if (url.indexOf('://') === -1) url = 'wss://' + url\n    let p = new URL(url)\n    if (p.protocol === 'http:') p.protocol = 'ws:'\n    else if (p.protocol === 'https:') p.protocol = 'wss:'\n    p.pathname = p.pathname.replace(/\\/+/g, '/')\n    if (p.pathname.endsWith('/')) p.pathname = p.pathname.slice(0, -1)\n    if ((p.port === '80' && p.protocol === 'ws:') || (p.port === '443' && p.protocol === 'wss:')) p.port = ''\n    p.searchParams.sort()\n    p.hash = ''\n    return p.toString()\n  } catch (e) {\n    throw new Error(`Invalid URL: ${url}`)\n  }\n}\n\nexport function insertEventIntoDescendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return b.created_at - event.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function insertEventIntoAscendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return event.created_at - b.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function binarySearch<T>(arr: T[], compare: (b: T) => number): [number, boolean] {\n  let start = 0\n  let end = arr.length - 1\n\n  while (start <= end) {\n    const mid = Math.floor((start + end) / 2)\n    const cmp = compare(arr[mid])\n\n    if (cmp === 0) {\n      return [mid, true]\n    }\n\n    if (cmp < 0) {\n      end = mid - 1\n    } else {\n      start = mid + 1\n    }\n  }\n\n  return [start, false]\n}\n\nexport class QueueNode<V> {\n  public value: V\n  public next: QueueNode<V> | null = null\n  public prev: QueueNode<V> | null = null\n\n  constructor(message: V) {\n    this.value = message\n  }\n}\n\nexport class Queue<V> {\n  public first: QueueNode<V> | null\n  public last: QueueNode<V> | null\n\n  constructor() {\n    this.first = null\n    this.last = null\n  }\n\n  enqueue(value: V): boolean {\n    const newNode = new QueueNode(value)\n    if (!this.last) {\n      // list is empty\n      this.first = newNode\n      this.last = newNode\n    } else if (this.last === this.first) {\n      // list has a single element\n      this.last = newNode\n      this.last.prev = this.first\n      this.first.next = newNode\n    } else {\n      // list has elements, add as last\n      newNode.prev = this.last\n      this.last.next = newNode\n      this.last = newNode\n    }\n    return true\n  }\n\n  dequeue(): V | null {\n    if (!this.first) return null\n\n    if (this.first === this.last) {\n      const target = this.first\n      this.first = null\n      this.last = null\n      return target.value\n    }\n\n    const target = this.first\n    this.first = target.next\n    if (this.first) {\n      this.first.prev = null // fix: clean up prev pointer\n    }\n\n    return target.value\n  }\n}\n", "import { NostrEvent, validateEvent } from './pure.ts'\n\n/** Events are **regular**, which means they're all expected to be stored by relays. */\nexport function isRegularKind(kind: number): boolean {\n  return kind < 10000 && kind !== 0 && kind !== 3\n}\n\n/** Events are **replaceable**, which means that, for each combination of `pubkey` and `kind`, only the latest event is expected to (SHOULD) be stored by relays, older versions are expected to be discarded. */\nexport function isReplaceableKind(kind: number): boolean {\n  return kind === 0 || kind === 3 || (10000 <= kind && kind < 20000)\n}\n\n/** Events are **ephemeral**, which means they are not expected to be stored by relays. */\nexport function isEphemeralKind(kind: number): boolean {\n  return 20000 <= kind && kind < 30000\n}\n\n/** Events are **addressable**, which means that, for each combination of `pubkey`, `kind` and the `d` tag, only the latest event is expected to be stored by relays, older versions are expected to be discarded. */\nexport function isAddressableKind(kind: number): boolean {\n  return 30000 <= kind && kind < 40000\n}\n\n/** Classification of the event kind. */\nexport type KindClassification = 'regular' | 'replaceable' | 'ephemeral' | 'parameterized' | 'unknown'\n\n/** Determine the classification of this kind of event if known, or `unknown`. */\nexport function classifyKind(kind: number): KindClassification {\n  if (isRegularKind(kind)) return 'regular'\n  if (isReplaceableKind(kind)) return 'replaceable'\n  if (isEphemeralKind(kind)) return 'ephemeral'\n  if (isAddressableKind(kind)) return 'parameterized'\n  return 'unknown'\n}\n\nexport function isKind<T extends number>(event: unknown, kind: T | Array<T>): event is NostrEvent & { kind: T } {\n  const kindAsArray: number[] = kind instanceof Array ? kind : [kind]\n  return (validateEvent(event) && kindAsArray.includes(event.kind)) || false\n}\n\nexport const Metadata = 0\nexport type Metadata = typeof Metadata\nexport const ShortTextNote = 1\nexport type ShortTextNote = typeof ShortTextNote\nexport const RecommendRelay = 2\nexport type RecommendRelay = typeof RecommendRelay\nexport const Contacts = 3\nexport type Contacts = typeof Contacts\nexport const EncryptedDirectMessage = 4\nexport type EncryptedDirectMessage = typeof EncryptedDirectMessage\nexport const EventDeletion = 5\nexport type EventDeletion = typeof EventDeletion\nexport const Repost = 6\nexport type Repost = typeof Repost\nexport const Reaction = 7\nexport type Reaction = typeof Reaction\nexport const BadgeAward = 8\nexport type BadgeAward = typeof BadgeAward\nexport const ChatMessage = 9\nexport type ChatMessage = typeof ChatMessage\nexport const ForumThread = 11\nexport type ForumThread = typeof ForumThread\nexport const Seal = 13\nexport type Seal = typeof Seal\nexport const PrivateDirectMessage = 14\nexport type PrivateDirectMessage = typeof PrivateDirectMessage\nexport const FileMessage = 15\nexport type FileMessage = typeof FileMessage\nexport const GenericRepost = 16\nexport type GenericRepost = typeof GenericRepost\nexport const Photo = 20\nexport type Photo = typeof Photo\nexport const NormalVideo = 21\nexport type NormalVideo = typeof NormalVideo\nexport const ShortVideo = 22\nexport type ShortVideo = typeof ShortVideo\nexport const ChannelCreation = 40\nexport type ChannelCreation = typeof ChannelCreation\nexport const ChannelMetadata = 41\nexport type ChannelMetadata = typeof ChannelMetadata\nexport const ChannelMessage = 42\nexport type ChannelMessage = typeof ChannelMessage\nexport const ChannelHideMessage = 43\nexport type ChannelHideMessage = typeof ChannelHideMessage\nexport const ChannelMuteUser = 44\nexport type ChannelMuteUser = typeof ChannelMuteUser\nexport const OpenTimestamps = 1040\nexport type OpenTimestamps = typeof OpenTimestamps\nexport const GiftWrap = 1059\nexport type GiftWrap = typeof GiftWrap\nexport const Poll = 1068\nexport type Poll = typeof Poll\nexport const FileMetadata = 1063\nexport type FileMetadata = typeof FileMetadata\nexport const Comment = 1111\nexport type Comment = typeof Comment\nexport const LiveChatMessage = 1311\nexport type LiveChatMessage = typeof LiveChatMessage\nexport const Voice = 1222\nexport type Voice = typeof Voice\nexport const VoiceComment = 1244\nexport type VoiceComment = typeof VoiceComment\nexport const ProblemTracker = 1971\nexport type ProblemTracker = typeof ProblemTracker\nexport const Report = 1984\nexport type Report = typeof Report\nexport const Reporting = 1984\nexport type Reporting = typeof Reporting\nexport const Label = 1985\nexport type Label = typeof Label\nexport const CommunityPostApproval = 4550\nexport type CommunityPostApproval = typeof CommunityPostApproval\nexport const JobRequest = 5999\nexport type JobRequest = typeof JobRequest\nexport const JobResult = 6999\nexport type JobResult = typeof JobResult\nexport const JobFeedback = 7000\nexport type JobFeedback = typeof JobFeedback\nexport const ZapGoal = 9041\nexport type ZapGoal = typeof ZapGoal\nexport const ZapRequest = 9734\nexport type ZapRequest = typeof ZapRequest\nexport const Zap = 9735\nexport type Zap = typeof Zap\nexport const Highlights = 9802\nexport type Highlights = typeof Highlights\nexport const PollResponse = 1018\nexport type PollResponse = typeof PollResponse\nexport const Mutelist = 10000\nexport type Mutelist = typeof Mutelist\nexport const Pinlist = 10001\nexport type Pinlist = typeof Pinlist\nexport const RelayList = 10002\nexport type RelayList = typeof RelayList\nexport const BookmarkList = 10003\nexport type BookmarkList = typeof BookmarkList\nexport const CommunitiesList = 10004\nexport type CommunitiesList = typeof CommunitiesList\nexport const PublicChatsList = 10005\nexport type PublicChatsList = typeof PublicChatsList\nexport const BlockedRelaysList = 10006\nexport type BlockedRelaysList = typeof BlockedRelaysList\nexport const SearchRelaysList = 10007\nexport type SearchRelaysList = typeof SearchRelaysList\nexport const FavoriteRelays = 10012\nexport type FavoriteRelays = typeof FavoriteRelays\nexport const InterestsList = 10015\nexport type InterestsList = typeof InterestsList\nexport const UserEmojiList = 10030\nexport type UserEmojiList = typeof UserEmojiList\nexport const DirectMessageRelaysList = 10050\nexport type DirectMessageRelaysList = typeof DirectMessageRelaysList\nexport const FileServerPreference = 10096\nexport type FileServerPreference = typeof FileServerPreference\nexport const BlossomServerList = 10063\nexport type BlossomServerList = typeof BlossomServerList\nexport const NWCWalletInfo = 13194\nexport type NWCWalletInfo = typeof NWCWalletInfo\nexport const LightningPubRPC = 21000\nexport type LightningPubRPC = typeof LightningPubRPC\nexport const ClientAuth = 22242\nexport type ClientAuth = typeof ClientAuth\nexport const NWCWalletRequest = 23194\nexport type NWCWalletRequest = typeof NWCWalletRequest\nexport const NWCWalletResponse = 23195\nexport type NWCWalletResponse = typeof NWCWalletResponse\nexport const NostrConnect = 24133\nexport type NostrConnect = typeof NostrConnect\nexport const HTTPAuth = 27235\nexport type HTTPAuth = typeof HTTPAuth\nexport const Followsets = 30000\nexport type Followsets = typeof Followsets\nexport const Genericlists = 30001\nexport type Genericlists = typeof Genericlists\nexport const Relaysets = 30002\nexport type Relaysets = typeof Relaysets\nexport const Bookmarksets = 30003\nexport type Bookmarksets = typeof Bookmarksets\nexport const Curationsets = 30004\nexport type Curationsets = typeof Curationsets\nexport const ProfileBadges = 30008\nexport type ProfileBadges = typeof ProfileBadges\nexport const BadgeDefinition = 30009\nexport type BadgeDefinition = typeof BadgeDefinition\nexport const Interestsets = 30015\nexport type Interestsets = typeof Interestsets\nexport const CreateOrUpdateStall = 30017\nexport type CreateOrUpdateStall = typeof CreateOrUpdateStall\nexport const CreateOrUpdateProduct = 30018\nexport type CreateOrUpdateProduct = typeof CreateOrUpdateProduct\nexport const LongFormArticle = 30023\nexport type LongFormArticle = typeof LongFormArticle\nexport const DraftLong = 30024\nexport type DraftLong = typeof DraftLong\nexport const Emojisets = 30030\nexport type Emojisets = typeof Emojisets\nexport const Application = 30078\nexport type Application = typeof Application\nexport const LiveEvent = 30311\nexport type LiveEvent = typeof LiveEvent\nexport const UserStatuses = 30315\nexport type UserStatuses = typeof UserStatuses\nexport const ClassifiedListing = 30402\nexport type ClassifiedListing = typeof ClassifiedListing\nexport const DraftClassifiedListing = 30403\nexport type DraftClassifiedListing = typeof DraftClassifiedListing\nexport const Date = 31922\nexport type Date = typeof Date\nexport const Time = 31923\nexport type Time = typeof Time\nexport const Calendar = 31924\nexport type Calendar = typeof Calendar\nexport const CalendarEventRSVP = 31925\nexport type CalendarEventRSVP = typeof CalendarEventRSVP\nexport const RelayReview = 31987\nexport type RelayReview = typeof RelayReview\nexport const Handlerrecommendation = 31989\nexport type Handlerrecommendation = typeof Handlerrecommendation\nexport const Handlerinformation = 31990\nexport type Handlerinformation = typeof Handlerinformation\nexport const CommunityDefinition = 34550\nexport type CommunityDefinition = typeof CommunityDefinition\nexport const GroupMetadata = 39000\nexport type GroupMetadata = typeof GroupMetadata\n", "import { Event } from './core.ts'\nimport { isAddressableKind, isReplaceableKind } from './kinds.ts'\n\nexport type Filter = {\n  ids?: string[]\n  kinds?: number[]\n  authors?: string[]\n  since?: number\n  until?: number\n  limit?: number\n  search?: string\n  [key: `#${string}`]: string[] | undefined\n}\n\nexport function matchFilter(filter: Filter, event: Event): boolean {\n  if (filter.ids && filter.ids.indexOf(event.id) === -1) {\n    return false\n  }\n  if (filter.kinds && filter.kinds.indexOf(event.kind) === -1) {\n    return false\n  }\n  if (filter.authors && filter.authors.indexOf(event.pubkey) === -1) {\n    return false\n  }\n\n  for (let f in filter) {\n    if (f[0] === '#') {\n      let tagName = f.slice(1)\n      let values = filter[`#${tagName}`]\n      if (values && !event.tags.find(([t, v]) => t === f.slice(1) && values!.indexOf(v) !== -1)) return false\n    }\n  }\n\n  if (filter.since && event.created_at < filter.since) return false\n  if (filter.until && event.created_at > filter.until) return false\n\n  return true\n}\n\nexport function matchFilters(filters: Filter[], event: Event): boolean {\n  for (let i = 0; i < filters.length; i++) {\n    if (matchFilter(filters[i], event)) {\n      return true\n    }\n  }\n  return false\n}\n\nexport function mergeFilters(...filters: Filter[]): Filter {\n  let result: Filter = {}\n  for (let i = 0; i < filters.length; i++) {\n    let filter = filters[i]\n    Object.entries(filter).forEach(([property, values]) => {\n      if (property === 'kinds' || property === 'ids' || property === 'authors' || property[0] === '#') {\n        // @ts-ignore\n        result[property] = result[property] || []\n        // @ts-ignore\n        for (let v = 0; v < values.length; v++) {\n          // @ts-ignore\n          let value = values[v]\n          // @ts-ignore\n          if (!result[property].includes(value)) result[property].push(value)\n        }\n      }\n    })\n\n    if (filter.limit && (!result.limit || filter.limit > result.limit)) result.limit = filter.limit\n    if (filter.until && (!result.until || filter.until > result.until)) result.until = filter.until\n    if (filter.since && (!result.since || filter.since < result.since)) result.since = filter.since\n  }\n\n  return result\n}\n\n/**\n * Calculate the intrinsic limit of a filter.\n * This function returns a positive integer, or `Infinity` if there is no intrinsic limit.\n */\nexport function getFilterLimit(filter: Filter): number {\n  if (filter.ids && !filter.ids.length) return 0\n  if (filter.kinds && !filter.kinds.length) return 0\n  if (filter.authors && !filter.authors.length) return 0\n\n  for (const [key, value] of Object.entries(filter)) {\n    if (key[0] === '#' && Array.isArray(value) && !value.length) return 0\n  }\n\n  return Math.min(\n    // The `limit` property creates an artificial limit.\n    Math.max(0, filter.limit ?? Infinity),\n\n    // There can only be one event per `id`.\n    filter.ids?.length ?? Infinity,\n\n    // Replaceable events are limited by the number of authors and kinds.\n    filter.authors?.length && filter.kinds?.every(kind => isReplaceableKind(kind))\n      ? filter.authors.length * filter.kinds.length\n      : Infinity,\n\n    // Parameterized replaceable events are limited by the number of authors, kinds, and \"d\" tags.\n    filter.authors?.length && filter.kinds?.every(kind => isAddressableKind(kind)) && filter['#d']?.length\n      ? filter.authors.length * filter.kinds.length * filter['#d'].length\n      : Infinity,\n  )\n}\n", "export function getHex64(json: string, field: string): string {\n  let len = field.length + 3\n  let idx = json.indexOf(`\"${field}\":`) + len\n  let s = json.slice(idx).indexOf(`\"`) + idx + 1\n  return json.slice(s, s + 64)\n}\n\nexport function getInt(json: string, field: string): number {\n  let len = field.length\n  let idx = json.indexOf(`\"${field}\":`) + len + 3\n  let sliced = json.slice(idx)\n  let end = Math.min(sliced.indexOf(','), sliced.indexOf('}'))\n  return parseInt(sliced.slice(0, end), 10)\n}\n\nexport function getSubscriptionId(json: string): string | null {\n  let idx = json.slice(0, 22).indexOf(`\"EVENT\"`)\n  if (idx === -1) return null\n\n  let pstart = json.slice(idx + 7 + 1).indexOf(`\"`)\n  if (pstart === -1) return null\n  let start = idx + 7 + 1 + pstart\n\n  let pend = json.slice(start + 1, 80).indexOf(`\"`)\n  if (pend === -1) return null\n  let end = start + 1 + pend\n\n  return json.slice(start + 1, end)\n}\n\nexport function matchEventId(json: string, id: string): boolean {\n  return id === getHex64(json, 'id')\n}\n\nexport function matchEventPubkey(json: string, pubkey: string): boolean {\n  return pubkey === getHex64(json, 'pubkey')\n}\n\nexport function matchEventKind(json: string, kind: number): boolean {\n  return kind === getInt(json, 'kind')\n}\n", "import { EventTemplate } from './core.ts'\nimport { ClientAuth } from './kinds.ts'\n\n/**\n * creates an EventTemplate for an AUTH event to be signed.\n */\nexport function makeAuthEvent(relayURL: string, challenge: string): EventTemplate {\n  return {\n    kind: ClientAuth,\n    created_at: Math.floor(Date.now() / 1000),\n    tags: [\n      ['relay', relayURL],\n      ['challenge', challenge],\n    ],\n    content: '',\n  }\n}\n", "import { verifiedSymbol, type Event, type Nostr, VerifiedEvent } from './core.ts'\n\nexport async function yieldThread() {\n  return new Promise<void>((resolve, reject) => {\n    try {\n      // Check if MessageChannel is available\n      if (typeof MessageChannel !== 'undefined') {\n        const ch = new MessageChannel()\n        const handler = () => {\n          // @ts-ignore (typescript thinks this property should be called `removeListener`, but in fact it's `removeEventListener`)\n          ch.port1.removeEventListener('message', handler)\n          resolve()\n        }\n        // @ts-ignore (typescript thinks this property should be called `addListener`, but in fact it's `addEventListener`)\n        ch.port1.addEventListener('message', handler)\n        ch.port2.postMessage(0)\n        ch.port1.start()\n      } else {\n        if (typeof setImmediate !== 'undefined') {\n          setImmediate(resolve)\n        } else if (typeof setTimeout !== 'undefined') {\n          setTimeout(resolve, 0)\n        } else {\n          // Last resort - resolve immediately\n          resolve()\n        }\n      }\n    } catch (e) {\n      console.error('during yield: ', e)\n      reject(e)\n    }\n  })\n}\n\nexport const alwaysTrue: Nostr['verifyEvent'] = (t: Event): t is VerifiedEvent => {\n  t[verifiedSymbol] = true\n  return true\n}\n", "/* global WebSocket */\n\nimport type { Event, EventTemplate, VerifiedEvent, Nostr, NostrEvent } from './core.ts'\nimport { matchFilters, type Filter } from './filter.ts'\nimport { getHex64, getSubscriptionId } from './fakejson.ts'\nimport { Queue, normalizeURL } from './utils.ts'\nimport { makeAuthEvent } from './nip42.ts'\nimport { yieldThread } from './helpers.ts'\n\ntype RelayWebSocket = WebSocket & {\n  ping?(): void\n  on?(event: 'pong', listener: () => void): any\n}\n\nexport type AbstractRelayConstructorOptions = {\n  verifyEvent: Nostr['verifyEvent']\n  websocketImplementation?: typeof WebSocket\n  enablePing?: boolean\n  enableReconnect?: boolean\n}\n\nexport class SendingOnClosedConnection extends Error {\n  constructor(message: string, relay: string) {\n    super(`Tried to send message '${message} on a closed connection to ${relay}.`)\n    this.name = 'SendingOnClosedConnection'\n  }\n}\n\nexport class AbstractRelay {\n  public readonly url: string\n  private _connected: boolean = false\n\n  public onclose: (() => void) | null = null\n  public onnotice: (msg: string) => void = msg => console.debug(`NOTICE from ${this.url}: ${msg}`)\n  public onauth: undefined | ((evt: EventTemplate) => Promise<VerifiedEvent>)\n\n  public baseEoseTimeout: number = 4400\n  public publishTimeout: number = 4400\n  public pingFrequency: number = 29000\n  public pingTimeout: number = 20000\n  public resubscribeBackoff: number[] = [10000, 10000, 10000, 20000, 20000, 30000, 60000]\n  public openSubs: Map<string, Subscription> = new Map()\n  public enablePing: boolean | undefined\n  public enableReconnect: boolean\n  private reconnectTimeoutHandle: ReturnType<typeof setTimeout> | undefined\n  private pingIntervalHandle: ReturnType<typeof setInterval> | undefined\n  private reconnectAttempts: number = 0\n  private closedIntentionally: boolean = false\n\n  private connectionPromise: Promise<void> | undefined\n  private openCountRequests = new Map<string, CountResolver>()\n  private openEventPublishes = new Map<string, EventPublishResolver>()\n  private ws: RelayWebSocket | undefined\n  private incomingMessageQueue = new Queue<string>()\n  private queueRunning = false\n  private challenge: string | undefined\n  private authPromise: Promise<string> | undefined\n  private serial: number = 0\n  private verifyEvent: Nostr['verifyEvent']\n\n  private _WebSocket: typeof WebSocket\n\n  constructor(url: string, opts: AbstractRelayConstructorOptions) {\n    this.url = normalizeURL(url)\n    this.verifyEvent = opts.verifyEvent\n    this._WebSocket = opts.websocketImplementation || WebSocket\n    this.enablePing = opts.enablePing\n    this.enableReconnect = opts.enableReconnect || false\n  }\n\n  static async connect(\n    url: string,\n    opts: AbstractRelayConstructorOptions & Parameters<AbstractRelay['connect']>[0],\n  ): Promise<AbstractRelay> {\n    const relay = new AbstractRelay(url, opts)\n    await relay.connect(opts)\n    return relay\n  }\n\n  private closeAllSubscriptions(reason: string) {\n    for (let [_, sub] of this.openSubs) {\n      sub.close(reason)\n    }\n    this.openSubs.clear()\n\n    for (let [_, ep] of this.openEventPublishes) {\n      ep.reject(new Error(reason))\n    }\n    this.openEventPublishes.clear()\n\n    for (let [_, cr] of this.openCountRequests) {\n      cr.reject(new Error(reason))\n    }\n    this.openCountRequests.clear()\n  }\n\n  public get connected(): boolean {\n    return this._connected\n  }\n\n  private async reconnect(): Promise<void> {\n    const backoff = this.resubscribeBackoff[Math.min(this.reconnectAttempts, this.resubscribeBackoff.length - 1)]\n    this.reconnectAttempts++\n\n    this.reconnectTimeoutHandle = setTimeout(async () => {\n      try {\n        await this.connect()\n      } catch (err) {\n        // this will be called again through onclose/onerror\n      }\n    }, backoff)\n  }\n\n  private handleHardClose(reason: string) {\n    if (this.pingIntervalHandle) {\n      clearInterval(this.pingIntervalHandle)\n      this.pingIntervalHandle = undefined\n    }\n\n    this._connected = false\n    this.connectionPromise = undefined\n\n    const wasIntentional = this.closedIntentionally\n    this.closedIntentionally = false // reset for next time\n\n    this.onclose?.()\n\n    if (this.enableReconnect && !wasIntentional) {\n      this.reconnect()\n    } else {\n      this.closeAllSubscriptions(reason)\n    }\n  }\n\n  public async connect(opts?: { timeout?: number; abort?: AbortSignal }): Promise<void> {\n    let connectionTimeoutHandle: ReturnType<typeof setTimeout> | undefined\n\n    if (this.connectionPromise) return this.connectionPromise\n\n    this.challenge = undefined\n    this.authPromise = undefined\n    this.connectionPromise = new Promise((resolve, reject) => {\n      if (opts?.timeout) {\n        connectionTimeoutHandle = setTimeout(() => {\n          reject('connection timed out')\n          this.connectionPromise = undefined\n          this.onclose?.()\n          this.closeAllSubscriptions('relay connection timed out')\n        }, opts.timeout)\n      }\n\n      if (opts?.abort) {\n        opts.abort.onabort = reject\n      }\n\n      try {\n        this.ws = new this._WebSocket(this.url)\n      } catch (err) {\n        clearTimeout(connectionTimeoutHandle)\n        reject(err)\n        return\n      }\n\n      this.ws.onopen = () => {\n        if (this.reconnectTimeoutHandle) {\n          clearTimeout(this.reconnectTimeoutHandle)\n          this.reconnectTimeoutHandle = undefined\n        }\n        clearTimeout(connectionTimeoutHandle)\n        this._connected = true\n\n        const isReconnection = this.reconnectAttempts > 0\n        this.reconnectAttempts = 0\n\n        // resubscribe to all open subscriptions\n        for (const sub of this.openSubs.values()) {\n          sub.eosed = false\n          if (isReconnection) {\n            for (let f = 0; f < sub.filters.length; f++) {\n              if (sub.lastEmitted) {\n                sub.filters[f].since = sub.lastEmitted + 1\n              }\n            }\n          }\n          sub.fire()\n        }\n\n        if (this.enablePing) {\n          this.pingIntervalHandle = setInterval(() => this.pingpong(), this.pingFrequency)\n        }\n        resolve()\n      }\n\n      this.ws.onerror = ev => {\n        clearTimeout(connectionTimeoutHandle)\n        reject((ev as any).message || 'websocket error')\n        this.handleHardClose('relay connection errored')\n      }\n\n      this.ws.onclose = ev => {\n        clearTimeout(connectionTimeoutHandle)\n        reject((ev as any).message || 'websocket closed')\n        this.handleHardClose('relay connection closed')\n      }\n\n      this.ws.onmessage = this._onmessage.bind(this)\n    })\n\n    return this.connectionPromise\n  }\n\n  private waitForPingPong() {\n    return new Promise(resolve => {\n      // listen for pong\n      ;(this.ws as any).once('pong', () => resolve(true))\n      // send a ping\n      this.ws!.ping!()\n    })\n  }\n\n  private waitForDummyReq() {\n    return new Promise((resolve, reject) => {\n      if (!this.connectionPromise) return reject(new Error(`no connection to ${this.url}, can't ping`))\n\n      // make a dummy request with expected empty eose reply\n      // [\"REQ\", \"_\", {\"ids\":[\"aaaa...aaaa\"], \"limit\": 0}]\n      try {\n        const sub = this.subscribe(\n          [{ ids: ['aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'], limit: 0 }],\n          {\n            label: 'forced-ping',\n            oneose: () => {\n              resolve(true)\n              sub.close()\n            },\n            onclose() {\n              // if we get a CLOSED it's because the relay is alive\n              resolve(true)\n            },\n            eoseTimeout: this.pingTimeout + 1000,\n          },\n        )\n      } catch (err) {\n        reject(err)\n      }\n    })\n  }\n\n  // nodejs requires this magic here to ensure connections are closed when internet goes off and stuff\n  // in browsers it's done automatically. see https://github.com/nbd-wtf/nostr-tools/issues/491\n  private async pingpong() {\n    // if the websocket is connected\n    if (this.ws?.readyState === 1) {\n      // wait for either a ping-pong reply or a timeout\n      const result = await Promise.any([\n        // browsers don't have ping so use a dummy req\n        this.ws && this.ws.ping && (this.ws as any).once ? this.waitForPingPong() : this.waitForDummyReq(),\n        new Promise(res => setTimeout(() => res(false), this.pingTimeout)),\n      ])\n\n      if (!result) {\n        // pingpong closing socket\n        if (this.ws?.readyState === this._WebSocket.OPEN) {\n          this.ws?.close()\n        }\n      }\n    }\n  }\n\n  private async runQueue() {\n    this.queueRunning = true\n    while (true) {\n      if (false === this.handleNext()) {\n        break\n      }\n      await yieldThread()\n    }\n    this.queueRunning = false\n  }\n\n  private handleNext(): undefined | false {\n    const json = this.incomingMessageQueue.dequeue()\n    if (!json) {\n      return false\n    }\n\n    // shortcut EVENT sub\n    const subid = getSubscriptionId(json)\n    if (subid) {\n      const so = this.openSubs.get(subid as string)\n      if (!so) {\n        // this is an EVENT message, but for a subscription we don't have, so just stop here\n        return\n      }\n\n      // this will be called only when this message is a EVENT message for a subscription we have\n      // we do this before parsing the JSON to not have to do that for duplicate events\n      //   since JSON parsing is slow\n      const id = getHex64(json, 'id')\n      const alreadyHave = so.alreadyHaveEvent?.(id)\n\n      // notify any interested client that the relay has this event\n      // (do this after alreadyHaveEvent() because the client may rely on this to answer that)\n      so.receivedEvent?.(this, id)\n\n      if (alreadyHave) {\n        // if we had already seen this event we can just stop here\n        return\n      }\n    }\n\n    try {\n      let data = JSON.parse(json)\n      // we won't do any checks against the data since all failures (i.e. invalid messages from relays)\n      // will naturally be caught by the encompassing try..catch block\n\n      switch (data[0]) {\n        case 'EVENT': {\n          const so = this.openSubs.get(data[1] as string) as Subscription\n          const event = data[2] as NostrEvent\n          if (this.verifyEvent(event) && matchFilters(so.filters, event)) {\n            so.onevent(event)\n          }\n          if (!so.lastEmitted || so.lastEmitted < event.created_at) so.lastEmitted = event.created_at\n          return\n        }\n        case 'COUNT': {\n          const id: string = data[1]\n          const payload = data[2] as { count: number }\n          const cr = this.openCountRequests.get(id) as CountResolver\n          if (cr) {\n            cr.resolve(payload.count)\n            this.openCountRequests.delete(id)\n          }\n          return\n        }\n        case 'EOSE': {\n          const so = this.openSubs.get(data[1] as string)\n          if (!so) return\n          so.receivedEose()\n          return\n        }\n        case 'OK': {\n          const id: string = data[1]\n          const ok: boolean = data[2]\n          const reason: string = data[3]\n          const ep = this.openEventPublishes.get(id) as EventPublishResolver\n          if (ep) {\n            clearTimeout(ep.timeout)\n            if (ok) ep.resolve(reason)\n            else ep.reject(new Error(reason))\n            this.openEventPublishes.delete(id)\n          }\n          return\n        }\n        case 'CLOSED': {\n          const id: string = data[1]\n          const so = this.openSubs.get(id)\n          if (!so) return\n          so.closed = true\n          so.close(data[2] as string)\n          return\n        }\n        case 'NOTICE': {\n          this.onnotice(data[1] as string)\n          return\n        }\n        case 'AUTH': {\n          this.challenge = data[1] as string\n          if (this.onauth) {\n            this.auth(this.onauth)\n          }\n          return\n        }\n        default: {\n          const so = this.openSubs.get(data[1])\n          so?.oncustom?.(data)\n          return\n        }\n      }\n    } catch (err) {\n      return\n    }\n  }\n\n  public async send(message: string) {\n    if (!this.connectionPromise) throw new SendingOnClosedConnection(message, this.url)\n\n    this.connectionPromise.then(() => {\n      this.ws?.send(message)\n    })\n  }\n\n  public async auth(signAuthEvent: (evt: EventTemplate) => Promise<VerifiedEvent>): Promise<string> {\n    const challenge = this.challenge\n    if (!challenge) throw new Error(\"can't perform auth, no challenge was received\")\n    if (this.authPromise) return this.authPromise\n\n    this.authPromise = new Promise<string>(async (resolve, reject) => {\n      try {\n        let evt = await signAuthEvent(makeAuthEvent(this.url, challenge))\n        let timeout = setTimeout(() => {\n          let ep = this.openEventPublishes.get(evt.id) as EventPublishResolver\n          if (ep) {\n            ep.reject(new Error('auth timed out'))\n            this.openEventPublishes.delete(evt.id)\n          }\n        }, this.publishTimeout)\n        this.openEventPublishes.set(evt.id, { resolve, reject, timeout })\n        this.send('[\"AUTH\",' + JSON.stringify(evt) + ']')\n      } catch (err) {\n        console.warn('subscribe auth function failed:', err)\n      }\n    })\n    return this.authPromise\n  }\n\n  public async publish(event: Event): Promise<string> {\n    const ret = new Promise<string>((resolve, reject) => {\n      const timeout = setTimeout(() => {\n        const ep = this.openEventPublishes.get(event.id) as EventPublishResolver\n        if (ep) {\n          ep.reject(new Error('publish timed out'))\n          this.openEventPublishes.delete(event.id)\n        }\n      }, this.publishTimeout)\n      this.openEventPublishes.set(event.id, { resolve, reject, timeout })\n    })\n    this.send('[\"EVENT\",' + JSON.stringify(event) + ']')\n    return ret\n  }\n\n  public async count(filters: Filter[], params: { id?: string | null }): Promise<number> {\n    this.serial++\n    const id = params?.id || 'count:' + this.serial\n    const ret = new Promise<number>((resolve, reject) => {\n      this.openCountRequests.set(id, { resolve, reject })\n    })\n    this.send('[\"COUNT\",\"' + id + '\",' + JSON.stringify(filters).substring(1))\n    return ret\n  }\n\n  public subscribe(\n    filters: Filter[],\n    params: Partial<SubscriptionParams> & { label?: string; id?: string },\n  ): Subscription {\n    const sub = this.prepareSubscription(filters, params)\n    sub.fire()\n\n    if (params.abort) {\n      params.abort.onabort = () => sub.close(String(params.abort!.reason || '<aborted>'))\n    }\n\n    return sub\n  }\n\n  public prepareSubscription(\n    filters: Filter[],\n    params: Partial<SubscriptionParams> & { label?: string; id?: string },\n  ): Subscription {\n    this.serial++\n    const id = params.id || (params.label ? params.label + ':' : 'sub:') + this.serial\n    const subscription = new Subscription(this, id, filters, params)\n    this.openSubs.set(id, subscription)\n    return subscription\n  }\n\n  public close() {\n    this.closedIntentionally = true\n    if (this.reconnectTimeoutHandle) {\n      clearTimeout(this.reconnectTimeoutHandle)\n      this.reconnectTimeoutHandle = undefined\n    }\n    if (this.pingIntervalHandle) {\n      clearInterval(this.pingIntervalHandle)\n      this.pingIntervalHandle = undefined\n    }\n    this.closeAllSubscriptions('relay connection closed by us')\n    this._connected = false\n    this.onclose?.()\n    if (this.ws?.readyState === this._WebSocket.OPEN) {\n      this.ws?.close()\n    }\n  }\n\n  // this is the function assigned to this.ws.onmessage\n  // it's exposed for testing and debugging purposes\n  public _onmessage(ev: MessageEvent<any>) {\n    this.incomingMessageQueue.enqueue(ev.data as string)\n    if (!this.queueRunning) {\n      this.runQueue()\n    }\n  }\n}\n\nexport class Subscription {\n  public readonly relay: AbstractRelay\n  public readonly id: string\n\n  public lastEmitted: number | undefined\n  public closed: boolean = false\n  public eosed: boolean = false\n  public filters: Filter[]\n  public alreadyHaveEvent: ((id: string) => boolean) | undefined\n  public receivedEvent: ((relay: AbstractRelay, id: string) => void) | undefined\n\n  public onevent: (evt: Event) => void\n  public oneose: (() => void) | undefined\n  public onclose: ((reason: string) => void) | undefined\n\n  // will get any messages that have this subscription id as their second item and are not default standard\n  public oncustom: ((msg: string[]) => void) | undefined\n\n  public eoseTimeout: number\n  private eoseTimeoutHandle: ReturnType<typeof setTimeout> | undefined\n\n  constructor(relay: AbstractRelay, id: string, filters: Filter[], params: SubscriptionParams) {\n    if (filters.length === 0) throw new Error(\"subscription can't be created with zero filters\")\n\n    this.relay = relay\n    this.filters = filters\n    this.id = id\n    this.alreadyHaveEvent = params.alreadyHaveEvent\n    this.receivedEvent = params.receivedEvent\n    this.eoseTimeout = params.eoseTimeout || relay.baseEoseTimeout\n\n    this.oneose = params.oneose\n    this.onclose = params.onclose\n    this.onevent =\n      params.onevent ||\n      (event => {\n        console.warn(\n          `onevent() callback not defined for subscription '${this.id}' in relay ${this.relay.url}. event received:`,\n          event,\n        )\n      })\n  }\n\n  public fire() {\n    this.relay.send('[\"REQ\",\"' + this.id + '\",' + JSON.stringify(this.filters).substring(1))\n\n    // only now we start counting the eoseTimeout\n    this.eoseTimeoutHandle = setTimeout(this.receivedEose.bind(this), this.eoseTimeout)\n  }\n\n  public receivedEose() {\n    if (this.eosed) return\n    clearTimeout(this.eoseTimeoutHandle)\n    this.eosed = true\n    this.oneose?.()\n  }\n\n  public close(reason: string = 'closed by caller') {\n    if (!this.closed && this.relay.connected) {\n      // if the connection was closed by the user calling .close() we will send a CLOSE message\n      // otherwise this._open will be already set to false so we will skip this\n      try {\n        this.relay.send('[\"CLOSE\",' + JSON.stringify(this.id) + ']')\n      } catch (err) {\n        if (err instanceof SendingOnClosedConnection) {\n          /* doesn't matter, it's ok */\n        } else {\n          throw err\n        }\n      }\n      this.closed = true\n    }\n    this.relay.openSubs.delete(this.id)\n    this.onclose?.(reason)\n  }\n}\n\nexport type SubscriptionParams = {\n  onevent?: (evt: Event) => void\n  oneose?: () => void\n  onclose?: (reason: string) => void\n  alreadyHaveEvent?: (id: string) => boolean\n  receivedEvent?: (relay: AbstractRelay, id: string) => void\n  eoseTimeout?: number\n  abort?: AbortSignal\n}\n\nexport type CountResolver = {\n  resolve: (count: number) => void\n  reject: (err: Error) => void\n}\n\nexport type EventPublishResolver = {\n  resolve: (reason: string) => void\n  reject: (err: Error) => void\n  timeout: ReturnType<typeof setTimeout>\n}\n", "/* global WebSocket */\n\nimport {\n  AbstractRelay as AbstractRelay,\n  SubscriptionParams,\n  Subscription,\n  type AbstractRelayConstructorOptions,\n} from './abstract-relay.ts'\nimport { normalizeURL } from './utils.ts'\n\nimport type { Event, EventTemplate, Nostr, VerifiedEvent } from './core.ts'\nimport { type Filter } from './filter.ts'\nimport { alwaysTrue } from './helpers.ts'\n\nexport type SubCloser = { close: (reason?: string) => void }\n\nexport type AbstractPoolConstructorOptions = AbstractRelayConstructorOptions & {\n  // automaticallyAuth takes a relay URL and should return null\n  // in case that relay shouldn't be authenticated against\n  // or a function to sign the AUTH event template otherwise (that function may still throw in case of failure)\n  automaticallyAuth?: (relayURL: string) => null | ((event: EventTemplate) => Promise<VerifiedEvent>)\n}\n\nexport type SubscribeManyParams = Omit<SubscriptionParams, 'onclose'> & {\n  maxWait?: number\n  abort?: AbortSignal\n  onclose?: (reasons: string[]) => void\n  onauth?: (event: EventTemplate) => Promise<VerifiedEvent>\n  id?: string\n  label?: string\n}\n\nexport class AbstractSimplePool {\n  protected relays: Map<string, AbstractRelay> = new Map()\n  public seenOn: Map<string, Set<AbstractRelay>> = new Map()\n  public trackRelays: boolean = false\n\n  public verifyEvent: Nostr['verifyEvent']\n  public enablePing: boolean | undefined\n  public enableReconnect: boolean\n  public automaticallyAuth?: (relayURL: string) => null | ((event: EventTemplate) => Promise<VerifiedEvent>)\n  public trustedRelayURLs: Set<string> = new Set()\n\n  private _WebSocket?: typeof WebSocket\n\n  constructor(opts: AbstractPoolConstructorOptions) {\n    this.verifyEvent = opts.verifyEvent\n    this._WebSocket = opts.websocketImplementation\n    this.enablePing = opts.enablePing\n    this.enableReconnect = opts.enableReconnect || false\n    this.automaticallyAuth = opts.automaticallyAuth\n  }\n\n  async ensureRelay(\n    url: string,\n    params?: {\n      connectionTimeout?: number\n      abort?: AbortSignal\n    },\n  ): Promise<AbstractRelay> {\n    url = normalizeURL(url)\n\n    let relay = this.relays.get(url)\n    if (!relay) {\n      relay = new AbstractRelay(url, {\n        verifyEvent: this.trustedRelayURLs.has(url) ? alwaysTrue : this.verifyEvent,\n        websocketImplementation: this._WebSocket,\n        enablePing: this.enablePing,\n        enableReconnect: this.enableReconnect,\n      })\n      relay.onclose = () => {\n        if (relay && !relay.enableReconnect) {\n          this.relays.delete(url)\n        }\n      }\n      this.relays.set(url, relay)\n    }\n\n    if (this.automaticallyAuth) {\n      const authSignerFn = this.automaticallyAuth(url)\n      if (authSignerFn) {\n        relay.onauth = authSignerFn\n      }\n    }\n\n    await relay.connect({\n      timeout: params?.connectionTimeout,\n      abort: params?.abort,\n    })\n\n    return relay\n  }\n\n  close(relays: string[]) {\n    relays.map(normalizeURL).forEach(url => {\n      this.relays.get(url)?.close()\n      this.relays.delete(url)\n    })\n  }\n\n  subscribe(relays: string[], filter: Filter, params: SubscribeManyParams): SubCloser {\n    const request: { url: string; filter: Filter }[] = []\n    for (let i = 0; i < relays.length; i++) {\n      const url = normalizeURL(relays[i])\n      if (!request.find(r => r.url === url)) {\n        request.push({ url, filter: filter })\n      }\n    }\n\n    return this.subscribeMap(request, params)\n  }\n\n  subscribeMany(relays: string[], filter: Filter, params: SubscribeManyParams): SubCloser {\n    const request: { url: string; filter: Filter }[] = []\n    const uniqUrls: string[] = []\n    for (let i = 0; i < relays.length; i++) {\n      const url = normalizeURL(relays[i])\n      if (uniqUrls.indexOf(url) === -1) {\n        uniqUrls.push(url)\n        request.push({ url, filter: filter })\n      }\n    }\n\n    return this.subscribeMap(request, params)\n  }\n\n  subscribeMap(requests: { url: string; filter: Filter }[], params: SubscribeManyParams): SubCloser {\n    const grouped = new Map<string, Filter[]>()\n    for (const req of requests) {\n      const { url, filter } = req\n      if (!grouped.has(url)) grouped.set(url, [])\n      grouped.get(url)!.push(filter)\n    }\n    const groupedRequests = Array.from(grouped.entries()).map(([url, filters]) => ({ url, filters }))\n\n    if (this.trackRelays) {\n      params.receivedEvent = (relay: AbstractRelay, id: string) => {\n        let set = this.seenOn.get(id)\n        if (!set) {\n          set = new Set()\n          this.seenOn.set(id, set)\n        }\n        set.add(relay)\n      }\n    }\n\n    const _knownIds = new Set<string>()\n    const subs: Subscription[] = []\n\n    // batch all EOSEs into a single\n    const eosesReceived: boolean[] = []\n    let handleEose = (i: number) => {\n      if (eosesReceived[i]) return // do not act twice for the same relay\n      eosesReceived[i] = true\n      if (eosesReceived.filter(a => a).length === groupedRequests.length) {\n        params.oneose?.()\n        handleEose = () => {}\n      }\n    }\n    // batch all closes into a single\n    const closesReceived: string[] = []\n    let handleClose = (i: number, reason: string) => {\n      if (closesReceived[i]) return // do not act twice for the same relay\n      handleEose(i)\n      closesReceived[i] = reason\n      if (closesReceived.filter(a => a).length === groupedRequests.length) {\n        params.onclose?.(closesReceived)\n        handleClose = () => {}\n      }\n    }\n\n    const localAlreadyHaveEventHandler = (id: string) => {\n      if (params.alreadyHaveEvent?.(id)) {\n        return true\n      }\n      const have = _knownIds.has(id)\n      _knownIds.add(id)\n      return have\n    }\n\n    // open a subscription in all given relays\n    const allOpened = Promise.all(\n      groupedRequests.map(async ({ url, filters }, i) => {\n        let relay: AbstractRelay\n        try {\n          relay = await this.ensureRelay(url, {\n            connectionTimeout: params.maxWait ? Math.max(params.maxWait * 0.8, params.maxWait - 1000) : undefined,\n            abort: params.abort,\n          })\n        } catch (err) {\n          handleClose(i, (err as any)?.message || String(err))\n          return\n        }\n\n        let subscription = relay.subscribe(filters, {\n          ...params,\n          oneose: () => handleEose(i),\n          onclose: reason => {\n            if (reason.startsWith('auth-required: ') && params.onauth) {\n              relay\n                .auth(params.onauth)\n                .then(() => {\n                  relay.subscribe(filters, {\n                    ...params,\n                    oneose: () => handleEose(i),\n                    onclose: reason => {\n                      handleClose(i, reason) // the second time we won't try to auth anymore\n                    },\n                    alreadyHaveEvent: localAlreadyHaveEventHandler,\n                    eoseTimeout: params.maxWait,\n                    abort: params.abort,\n                  })\n                })\n                .catch(err => {\n                  handleClose(i, `auth was required and attempted, but failed with: ${err}`)\n                })\n            } else {\n              handleClose(i, reason)\n            }\n          },\n          alreadyHaveEvent: localAlreadyHaveEventHandler,\n          eoseTimeout: params.maxWait,\n          abort: params.abort,\n        })\n\n        subs.push(subscription)\n      }),\n    )\n\n    return {\n      async close(reason?: string) {\n        await allOpened\n        subs.forEach(sub => {\n          sub.close(reason)\n        })\n      },\n    }\n  }\n\n  subscribeEose(\n    relays: string[],\n    filter: Filter,\n    params: Pick<SubscribeManyParams, 'label' | 'id' | 'onevent' | 'onclose' | 'maxWait' | 'onauth'>,\n  ): SubCloser {\n    const subcloser = this.subscribe(relays, filter, {\n      ...params,\n      oneose() {\n        subcloser.close('closed automatically on eose')\n      },\n    })\n    return subcloser\n  }\n\n  subscribeManyEose(\n    relays: string[],\n    filter: Filter,\n    params: Pick<SubscribeManyParams, 'label' | 'id' | 'onevent' | 'onclose' | 'maxWait' | 'onauth'>,\n  ): SubCloser {\n    const subcloser = this.subscribeMany(relays, filter, {\n      ...params,\n      oneose() {\n        subcloser.close('closed automatically on eose')\n      },\n    })\n    return subcloser\n  }\n\n  async querySync(\n    relays: string[],\n    filter: Filter,\n    params?: Pick<SubscribeManyParams, 'label' | 'id' | 'maxWait'>,\n  ): Promise<Event[]> {\n    return new Promise(async resolve => {\n      const events: Event[] = []\n      this.subscribeEose(relays, filter, {\n        ...params,\n        onevent(event: Event) {\n          events.push(event)\n        },\n        onclose(_: string[]) {\n          resolve(events)\n        },\n      })\n    })\n  }\n\n  async get(\n    relays: string[],\n    filter: Filter,\n    params?: Pick<SubscribeManyParams, 'label' | 'id' | 'maxWait'>,\n  ): Promise<Event | null> {\n    filter.limit = 1\n    const events = await this.querySync(relays, filter, params)\n    events.sort((a, b) => b.created_at - a.created_at)\n    return events[0] || null\n  }\n\n  publish(\n    relays: string[],\n    event: Event,\n    options?: { onauth?: (evt: EventTemplate) => Promise<VerifiedEvent> },\n  ): Promise<string>[] {\n    return relays.map(normalizeURL).map(async (url, i, arr) => {\n      if (arr.indexOf(url) !== i) {\n        // duplicate\n        return Promise.reject('duplicate url')\n      }\n\n      let r = await this.ensureRelay(url)\n      return r\n        .publish(event)\n        .catch(async err => {\n          if (err instanceof Error && err.message.startsWith('auth-required: ') && options?.onauth) {\n            await r.auth(options.onauth)\n            return r.publish(event) // retry\n          }\n          throw err\n        })\n        .then(reason => {\n          if (this.trackRelays) {\n            let set = this.seenOn.get(event.id)\n            if (!set) {\n              set = new Set()\n              this.seenOn.set(event.id, set)\n            }\n            set.add(r)\n          }\n          return reason\n        })\n    })\n  }\n\n  listConnectionStatus(): Map<string, boolean> {\n    const map = new Map<string, boolean>()\n    this.relays.forEach((relay, url) => map.set(url, relay.connected))\n\n    return map\n  }\n\n  destroy(): void {\n    this.relays.forEach(conn => conn.close())\n    this.relays = new Map()\n  }\n}\n", "/* global WebSocket */\n\nimport { verifyEvent } from './pure.ts'\nimport { AbstractSimplePool, type AbstractPoolConstructorOptions } from './abstract-pool.ts'\n\nvar _WebSocket: typeof WebSocket\n\ntry {\n  _WebSocket = WebSocket\n} catch {}\n\nexport function useWebSocketImplementation(websocketImplementation: any) {\n  _WebSocket = websocketImplementation\n}\n\nexport class SimplePool extends AbstractSimplePool {\n  constructor(options?: Pick<AbstractPoolConstructorOptions, 'enablePing' | 'enableReconnect'>) {\n    super({ verifyEvent, websocketImplementation: _WebSocket, ...options })\n  }\n}\n\nexport * from './abstract-pool.ts'\n"],
-  "mappings": "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",
-  "names": ["bytesToHex", "i", "target", "bytesToHex", "i", "i", "reason"]
+  "sourcesContent": ["import { schnorr } from '@noble/curves/secp256k1.js'\nimport { bytesToHex, hexToBytes } from '@noble/hashes/utils.js'\nimport { Nostr, Event, EventTemplate, UnsignedEvent, VerifiedEvent, verifiedSymbol, validateEvent } from './core.ts'\nimport { sha256 } from '@noble/hashes/sha2.js'\n\nimport { utf8Encoder } from './utils.ts'\n\nclass JS implements Nostr {\n  generateSecretKey(): Uint8Array {\n    return schnorr.utils.randomSecretKey()\n  }\n  getPublicKey(secretKey: Uint8Array): string {\n    return bytesToHex(schnorr.getPublicKey(secretKey))\n  }\n  finalizeEvent(t: EventTemplate, secretKey: Uint8Array): VerifiedEvent {\n    const event = t as VerifiedEvent\n    event.pubkey = bytesToHex(schnorr.getPublicKey(secretKey))\n    event.id = getEventHash(event)\n    event.sig = bytesToHex(schnorr.sign(hexToBytes(getEventHash(event)), secretKey))\n    event[verifiedSymbol] = true\n    return event\n  }\n  verifyEvent(event: Event): event is VerifiedEvent {\n    if (typeof event[verifiedSymbol] === 'boolean') return event[verifiedSymbol]\n\n    const hash = getEventHash(event)\n    if (hash !== event.id) {\n      event[verifiedSymbol] = false\n      return false\n    }\n\n    try {\n      const valid = schnorr.verify(hexToBytes(event.sig), hexToBytes(hash), hexToBytes(event.pubkey))\n      event[verifiedSymbol] = valid\n      return valid\n    } catch (err) {\n      event[verifiedSymbol] = false\n      return false\n    }\n  }\n}\n\nexport function serializeEvent(evt: UnsignedEvent): string {\n  if (!validateEvent(evt)) throw new Error(\"can't serialize event with wrong or missing properties\")\n  return JSON.stringify([0, evt.pubkey, evt.created_at, evt.kind, evt.tags, evt.content])\n}\n\nexport function getEventHash(event: UnsignedEvent): string {\n  let eventHash = sha256(utf8Encoder.encode(serializeEvent(event)))\n  return bytesToHex(eventHash)\n}\n\nconst i: JS = new JS()\n\nexport const generateSecretKey = i.generateSecretKey\nexport const getPublicKey = i.getPublicKey\nexport const finalizeEvent = i.finalizeEvent\nexport const verifyEvent = i.verifyEvent\nexport * from './core.ts'\n", "export interface Nostr {\n  generateSecretKey(): Uint8Array\n  getPublicKey(secretKey: Uint8Array): string\n  finalizeEvent(event: EventTemplate, secretKey: Uint8Array): VerifiedEvent\n  verifyEvent(event: Event): event is VerifiedEvent\n}\n\n/** Designates a verified event signature. */\nexport const verifiedSymbol = Symbol('verified')\n\nexport type NostrEvent = {\n  kind: number\n  tags: string[][]\n  content: string\n  created_at: number\n  pubkey: string\n  id: string\n  sig: string\n  [verifiedSymbol]?: boolean\n}\n\nexport type Event = NostrEvent\nexport type EventTemplate = Pick<Event, 'kind' | 'tags' | 'content' | 'created_at'>\nexport type UnsignedEvent = Pick<Event, 'kind' | 'tags' | 'content' | 'created_at' | 'pubkey'>\n\n/** An event whose signature has been verified. */\nexport interface VerifiedEvent extends Event {\n  [verifiedSymbol]: true\n}\n\nconst isRecord = (obj: unknown): obj is Record<string, unknown> => obj instanceof Object\n\nexport function validateEvent<T>(event: T): event is T & UnsignedEvent {\n  if (!isRecord(event)) return false\n  if (typeof event.kind !== 'number') return false\n  if (typeof event.content !== 'string') return false\n  if (typeof event.created_at !== 'number') return false\n  if (typeof event.pubkey !== 'string') return false\n  if (!event.pubkey.match(/^[a-f0-9]{64}$/)) return false\n\n  if (!Array.isArray(event.tags)) return false\n  for (let i = 0; i < event.tags.length; i++) {\n    let tag = event.tags[i]\n    if (!Array.isArray(tag)) return false\n    for (let j = 0; j < tag.length; j++) {\n      if (typeof tag[j] !== 'string') return false\n    }\n  }\n\n  return true\n}\n\n/**\n * Sort events in reverse-chronological order by the `created_at` timestamp,\n * and then by the event `id` (lexicographically) in case of ties.\n * This mutates the array.\n */\nexport function sortEvents(events: Event[]): Event[] {\n  return events.sort((a: NostrEvent, b: NostrEvent): number => {\n    if (a.created_at !== b.created_at) {\n      return b.created_at - a.created_at\n    }\n    return a.id.localeCompare(b.id)\n  })\n}\n", "import type { Event } from './core.ts'\n\nexport const utf8Decoder: TextDecoder = new TextDecoder('utf-8')\nexport const utf8Encoder: TextEncoder = new TextEncoder()\n\nexport { bytesToHex, hexToBytes } from '@noble/hashes/utils.js'\n\nexport function normalizeURL(url: string): string {\n  try {\n    if (url.indexOf('://') === -1) url = 'wss://' + url\n    let p = new URL(url)\n    if (p.protocol === 'http:') p.protocol = 'ws:'\n    else if (p.protocol === 'https:') p.protocol = 'wss:'\n    p.pathname = p.pathname.replace(/\\/+/g, '/')\n    if (p.pathname.endsWith('/')) p.pathname = p.pathname.slice(0, -1)\n    if ((p.port === '80' && p.protocol === 'ws:') || (p.port === '443' && p.protocol === 'wss:')) p.port = ''\n    p.searchParams.sort()\n    p.hash = ''\n    return p.toString()\n  } catch (e) {\n    throw new Error(`Invalid URL: ${url}`)\n  }\n}\n\nexport function insertEventIntoDescendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return b.created_at - event.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function insertEventIntoAscendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return event.created_at - b.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function binarySearch<T>(arr: T[], compare: (b: T) => number): [number, boolean] {\n  let start = 0\n  let end = arr.length - 1\n\n  while (start <= end) {\n    const mid = Math.floor((start + end) / 2)\n    const cmp = compare(arr[mid])\n\n    if (cmp === 0) {\n      return [mid, true]\n    }\n\n    if (cmp < 0) {\n      end = mid - 1\n    } else {\n      start = mid + 1\n    }\n  }\n\n  return [start, false]\n}\n\nexport class QueueNode<V> {\n  public value: V\n  public next: QueueNode<V> | null = null\n  public prev: QueueNode<V> | null = null\n\n  constructor(message: V) {\n    this.value = message\n  }\n}\n\nexport class Queue<V> {\n  public first: QueueNode<V> | null\n  public last: QueueNode<V> | null\n\n  constructor() {\n    this.first = null\n    this.last = null\n  }\n\n  enqueue(value: V): boolean {\n    const newNode = new QueueNode(value)\n    if (!this.last) {\n      // list is empty\n      this.first = newNode\n      this.last = newNode\n    } else if (this.last === this.first) {\n      // list has a single element\n      this.last = newNode\n      this.last.prev = this.first\n      this.first.next = newNode\n    } else {\n      // list has elements, add as last\n      newNode.prev = this.last\n      this.last.next = newNode\n      this.last = newNode\n    }\n    return true\n  }\n\n  dequeue(): V | null {\n    if (!this.first) return null\n\n    if (this.first === this.last) {\n      const target = this.first\n      this.first = null\n      this.last = null\n      return target.value\n    }\n\n    const target = this.first\n    this.first = target.next\n    if (this.first) {\n      this.first.prev = null // fix: clean up prev pointer\n    }\n\n    return target.value\n  }\n}\n", "import { NostrEvent, validateEvent } from './pure.ts'\n\n/** Events are **regular**, which means they're all expected to be stored by relays. */\nexport function isRegularKind(kind: number): boolean {\n  return kind < 10000 && kind !== 0 && kind !== 3\n}\n\n/** Events are **replaceable**, which means that, for each combination of `pubkey` and `kind`, only the latest event is expected to (SHOULD) be stored by relays, older versions are expected to be discarded. */\nexport function isReplaceableKind(kind: number): boolean {\n  return kind === 0 || kind === 3 || (10000 <= kind && kind < 20000)\n}\n\n/** Events are **ephemeral**, which means they are not expected to be stored by relays. */\nexport function isEphemeralKind(kind: number): boolean {\n  return 20000 <= kind && kind < 30000\n}\n\n/** Events are **addressable**, which means that, for each combination of `pubkey`, `kind` and the `d` tag, only the latest event is expected to be stored by relays, older versions are expected to be discarded. */\nexport function isAddressableKind(kind: number): boolean {\n  return 30000 <= kind && kind < 40000\n}\n\n/** Classification of the event kind. */\nexport type KindClassification = 'regular' | 'replaceable' | 'ephemeral' | 'parameterized' | 'unknown'\n\n/** Determine the classification of this kind of event if known, or `unknown`. */\nexport function classifyKind(kind: number): KindClassification {\n  if (isRegularKind(kind)) return 'regular'\n  if (isReplaceableKind(kind)) return 'replaceable'\n  if (isEphemeralKind(kind)) return 'ephemeral'\n  if (isAddressableKind(kind)) return 'parameterized'\n  return 'unknown'\n}\n\nexport function isKind<T extends number>(event: unknown, kind: T | Array<T>): event is NostrEvent & { kind: T } {\n  const kindAsArray: number[] = kind instanceof Array ? kind : [kind]\n  return (validateEvent(event) && kindAsArray.includes(event.kind)) || false\n}\n\nexport const Metadata = 0\nexport type Metadata = typeof Metadata\nexport const ShortTextNote = 1\nexport type ShortTextNote = typeof ShortTextNote\nexport const RecommendRelay = 2\nexport type RecommendRelay = typeof RecommendRelay\nexport const Contacts = 3\nexport type Contacts = typeof Contacts\nexport const EncryptedDirectMessage = 4\nexport type EncryptedDirectMessage = typeof EncryptedDirectMessage\nexport const EventDeletion = 5\nexport type EventDeletion = typeof EventDeletion\nexport const Repost = 6\nexport type Repost = typeof Repost\nexport const Reaction = 7\nexport type Reaction = typeof Reaction\nexport const BadgeAward = 8\nexport type BadgeAward = typeof BadgeAward\nexport const ChatMessage = 9\nexport type ChatMessage = typeof ChatMessage\nexport const ForumThread = 11\nexport type ForumThread = typeof ForumThread\nexport const Seal = 13\nexport type Seal = typeof Seal\nexport const PrivateDirectMessage = 14\nexport type PrivateDirectMessage = typeof PrivateDirectMessage\nexport const FileMessage = 15\nexport type FileMessage = typeof FileMessage\nexport const GenericRepost = 16\nexport type GenericRepost = typeof GenericRepost\nexport const Photo = 20\nexport type Photo = typeof Photo\nexport const NormalVideo = 21\nexport type NormalVideo = typeof NormalVideo\nexport const ShortVideo = 22\nexport type ShortVideo = typeof ShortVideo\nexport const ChannelCreation = 40\nexport type ChannelCreation = typeof ChannelCreation\nexport const ChannelMetadata = 41\nexport type ChannelMetadata = typeof ChannelMetadata\nexport const ChannelMessage = 42\nexport type ChannelMessage = typeof ChannelMessage\nexport const ChannelHideMessage = 43\nexport type ChannelHideMessage = typeof ChannelHideMessage\nexport const ChannelMuteUser = 44\nexport type ChannelMuteUser = typeof ChannelMuteUser\nexport const OpenTimestamps = 1040\nexport type OpenTimestamps = typeof OpenTimestamps\nexport const GiftWrap = 1059\nexport type GiftWrap = typeof GiftWrap\nexport const Poll = 1068\nexport type Poll = typeof Poll\nexport const FileMetadata = 1063\nexport type FileMetadata = typeof FileMetadata\nexport const Comment = 1111\nexport type Comment = typeof Comment\nexport const LiveChatMessage = 1311\nexport type LiveChatMessage = typeof LiveChatMessage\nexport const Voice = 1222\nexport type Voice = typeof Voice\nexport const VoiceComment = 1244\nexport type VoiceComment = typeof VoiceComment\nexport const ProblemTracker = 1971\nexport type ProblemTracker = typeof ProblemTracker\nexport const Report = 1984\nexport type Report = typeof Report\nexport const Reporting = 1984\nexport type Reporting = typeof Reporting\nexport const Label = 1985\nexport type Label = typeof Label\nexport const CommunityPostApproval = 4550\nexport type CommunityPostApproval = typeof CommunityPostApproval\nexport const JobRequest = 5999\nexport type JobRequest = typeof JobRequest\nexport const JobResult = 6999\nexport type JobResult = typeof JobResult\nexport const JobFeedback = 7000\nexport type JobFeedback = typeof JobFeedback\nexport const ZapGoal = 9041\nexport type ZapGoal = typeof ZapGoal\nexport const ZapRequest = 9734\nexport type ZapRequest = typeof ZapRequest\nexport const Zap = 9735\nexport type Zap = typeof Zap\nexport const Highlights = 9802\nexport type Highlights = typeof Highlights\nexport const PollResponse = 1018\nexport type PollResponse = typeof PollResponse\nexport const Mutelist = 10000\nexport type Mutelist = typeof Mutelist\nexport const Pinlist = 10001\nexport type Pinlist = typeof Pinlist\nexport const RelayList = 10002\nexport type RelayList = typeof RelayList\nexport const BookmarkList = 10003\nexport type BookmarkList = typeof BookmarkList\nexport const CommunitiesList = 10004\nexport type CommunitiesList = typeof CommunitiesList\nexport const PublicChatsList = 10005\nexport type PublicChatsList = typeof PublicChatsList\nexport const BlockedRelaysList = 10006\nexport type BlockedRelaysList = typeof BlockedRelaysList\nexport const SearchRelaysList = 10007\nexport type SearchRelaysList = typeof SearchRelaysList\nexport const FavoriteRelays = 10012\nexport type FavoriteRelays = typeof FavoriteRelays\nexport const InterestsList = 10015\nexport type InterestsList = typeof InterestsList\nexport const UserEmojiList = 10030\nexport type UserEmojiList = typeof UserEmojiList\nexport const DirectMessageRelaysList = 10050\nexport type DirectMessageRelaysList = typeof DirectMessageRelaysList\nexport const FileServerPreference = 10096\nexport type FileServerPreference = typeof FileServerPreference\nexport const BlossomServerList = 10063\nexport type BlossomServerList = typeof BlossomServerList\nexport const NWCWalletInfo = 13194\nexport type NWCWalletInfo = typeof NWCWalletInfo\nexport const LightningPubRPC = 21000\nexport type LightningPubRPC = typeof LightningPubRPC\nexport const ClientAuth = 22242\nexport type ClientAuth = typeof ClientAuth\nexport const NWCWalletRequest = 23194\nexport type NWCWalletRequest = typeof NWCWalletRequest\nexport const NWCWalletResponse = 23195\nexport type NWCWalletResponse = typeof NWCWalletResponse\nexport const NostrConnect = 24133\nexport type NostrConnect = typeof NostrConnect\nexport const HTTPAuth = 27235\nexport type HTTPAuth = typeof HTTPAuth\nexport const Followsets = 30000\nexport type Followsets = typeof Followsets\nexport const Genericlists = 30001\nexport type Genericlists = typeof Genericlists\nexport const Relaysets = 30002\nexport type Relaysets = typeof Relaysets\nexport const Bookmarksets = 30003\nexport type Bookmarksets = typeof Bookmarksets\nexport const Curationsets = 30004\nexport type Curationsets = typeof Curationsets\nexport const ProfileBadges = 30008\nexport type ProfileBadges = typeof ProfileBadges\nexport const BadgeDefinition = 30009\nexport type BadgeDefinition = typeof BadgeDefinition\nexport const Interestsets = 30015\nexport type Interestsets = typeof Interestsets\nexport const CreateOrUpdateStall = 30017\nexport type CreateOrUpdateStall = typeof CreateOrUpdateStall\nexport const CreateOrUpdateProduct = 30018\nexport type CreateOrUpdateProduct = typeof CreateOrUpdateProduct\nexport const LongFormArticle = 30023\nexport type LongFormArticle = typeof LongFormArticle\nexport const DraftLong = 30024\nexport type DraftLong = typeof DraftLong\nexport const Emojisets = 30030\nexport type Emojisets = typeof Emojisets\nexport const Application = 30078\nexport type Application = typeof Application\nexport const LiveEvent = 30311\nexport type LiveEvent = typeof LiveEvent\nexport const UserStatuses = 30315\nexport type UserStatuses = typeof UserStatuses\nexport const ClassifiedListing = 30402\nexport type ClassifiedListing = typeof ClassifiedListing\nexport const DraftClassifiedListing = 30403\nexport type DraftClassifiedListing = typeof DraftClassifiedListing\nexport const Date = 31922\nexport type Date = typeof Date\nexport const Time = 31923\nexport type Time = typeof Time\nexport const Calendar = 31924\nexport type Calendar = typeof Calendar\nexport const CalendarEventRSVP = 31925\nexport type CalendarEventRSVP = typeof CalendarEventRSVP\nexport const RelayReview = 31987\nexport type RelayReview = typeof RelayReview\nexport const Handlerrecommendation = 31989\nexport type Handlerrecommendation = typeof Handlerrecommendation\nexport const Handlerinformation = 31990\nexport type Handlerinformation = typeof Handlerinformation\nexport const CommunityDefinition = 34550\nexport type CommunityDefinition = typeof CommunityDefinition\nexport const GroupMetadata = 39000\nexport type GroupMetadata = typeof GroupMetadata\n", "import { Event } from './core.ts'\nimport { isAddressableKind, isReplaceableKind } from './kinds.ts'\n\nexport type Filter = {\n  ids?: string[]\n  kinds?: number[]\n  authors?: string[]\n  since?: number\n  until?: number\n  limit?: number\n  search?: string\n  [key: `#${string}`]: string[] | undefined\n}\n\nexport function matchFilter(filter: Filter, event: Event): boolean {\n  if (filter.ids && filter.ids.indexOf(event.id) === -1) {\n    return false\n  }\n  if (filter.kinds && filter.kinds.indexOf(event.kind) === -1) {\n    return false\n  }\n  if (filter.authors && filter.authors.indexOf(event.pubkey) === -1) {\n    return false\n  }\n\n  for (let f in filter) {\n    if (f[0] === '#') {\n      let tagName = f.slice(1)\n      let values = filter[`#${tagName}`]\n      if (values && !event.tags.find(([t, v]) => t === f.slice(1) && values!.indexOf(v) !== -1)) return false\n    }\n  }\n\n  if (filter.since && event.created_at < filter.since) return false\n  if (filter.until && event.created_at > filter.until) return false\n\n  return true\n}\n\nexport function matchFilters(filters: Filter[], event: Event): boolean {\n  for (let i = 0; i < filters.length; i++) {\n    if (matchFilter(filters[i], event)) {\n      return true\n    }\n  }\n  return false\n}\n\nexport function mergeFilters(...filters: Filter[]): Filter {\n  let result: Filter = {}\n  for (let i = 0; i < filters.length; i++) {\n    let filter = filters[i]\n    Object.entries(filter).forEach(([property, values]) => {\n      if (property === 'kinds' || property === 'ids' || property === 'authors' || property[0] === '#') {\n        // @ts-ignore\n        result[property] = result[property] || []\n        // @ts-ignore\n        for (let v = 0; v < values.length; v++) {\n          // @ts-ignore\n          let value = values[v]\n          // @ts-ignore\n          if (!result[property].includes(value)) result[property].push(value)\n        }\n      }\n    })\n\n    if (filter.limit && (!result.limit || filter.limit > result.limit)) result.limit = filter.limit\n    if (filter.until && (!result.until || filter.until > result.until)) result.until = filter.until\n    if (filter.since && (!result.since || filter.since < result.since)) result.since = filter.since\n  }\n\n  return result\n}\n\n/**\n * Calculate the intrinsic limit of a filter.\n * This function returns a positive integer, or `Infinity` if there is no intrinsic limit.\n */\nexport function getFilterLimit(filter: Filter): number {\n  if (filter.ids && !filter.ids.length) return 0\n  if (filter.kinds && !filter.kinds.length) return 0\n  if (filter.authors && !filter.authors.length) return 0\n\n  for (const [key, value] of Object.entries(filter)) {\n    if (key[0] === '#' && Array.isArray(value) && !value.length) return 0\n  }\n\n  return Math.min(\n    // The `limit` property creates an artificial limit.\n    Math.max(0, filter.limit ?? Infinity),\n\n    // There can only be one event per `id`.\n    filter.ids?.length ?? Infinity,\n\n    // Replaceable events are limited by the number of authors and kinds.\n    filter.authors?.length && filter.kinds?.every(kind => isReplaceableKind(kind))\n      ? filter.authors.length * filter.kinds.length\n      : Infinity,\n\n    // Parameterized replaceable events are limited by the number of authors, kinds, and \"d\" tags.\n    filter.authors?.length && filter.kinds?.every(kind => isAddressableKind(kind)) && filter['#d']?.length\n      ? filter.authors.length * filter.kinds.length * filter['#d'].length\n      : Infinity,\n  )\n}\n", "export function getHex64(json: string, field: string): string {\n  let len = field.length + 3\n  let idx = json.indexOf(`\"${field}\":`) + len\n  let s = json.slice(idx).indexOf(`\"`) + idx + 1\n  return json.slice(s, s + 64)\n}\n\nexport function getInt(json: string, field: string): number {\n  let len = field.length\n  let idx = json.indexOf(`\"${field}\":`) + len + 3\n  let sliced = json.slice(idx)\n  let end = Math.min(sliced.indexOf(','), sliced.indexOf('}'))\n  return parseInt(sliced.slice(0, end), 10)\n}\n\nexport function getSubscriptionId(json: string): string | null {\n  let idx = json.slice(0, 22).indexOf(`\"EVENT\"`)\n  if (idx === -1) return null\n\n  let pstart = json.slice(idx + 7 + 1).indexOf(`\"`)\n  if (pstart === -1) return null\n  let start = idx + 7 + 1 + pstart\n\n  let pend = json.slice(start + 1, 80).indexOf(`\"`)\n  if (pend === -1) return null\n  let end = start + 1 + pend\n\n  return json.slice(start + 1, end)\n}\n\nexport function matchEventId(json: string, id: string): boolean {\n  return id === getHex64(json, 'id')\n}\n\nexport function matchEventPubkey(json: string, pubkey: string): boolean {\n  return pubkey === getHex64(json, 'pubkey')\n}\n\nexport function matchEventKind(json: string, kind: number): boolean {\n  return kind === getInt(json, 'kind')\n}\n", "import { EventTemplate } from './core.ts'\nimport { ClientAuth } from './kinds.ts'\n\n/**\n * creates an EventTemplate for an AUTH event to be signed.\n */\nexport function makeAuthEvent(relayURL: string, challenge: string): EventTemplate {\n  return {\n    kind: ClientAuth,\n    created_at: Math.floor(Date.now() / 1000),\n    tags: [\n      ['relay', relayURL],\n      ['challenge', challenge],\n    ],\n    content: '',\n  }\n}\n", "import { verifiedSymbol, type Event, type Nostr, VerifiedEvent } from './core.ts'\n\nexport async function yieldThread() {\n  return new Promise<void>((resolve, reject) => {\n    try {\n      // Check if MessageChannel is available\n      if (typeof MessageChannel !== 'undefined') {\n        const ch = new MessageChannel()\n        const handler = () => {\n          // @ts-ignore (typescript thinks this property should be called `removeListener`, but in fact it's `removeEventListener`)\n          ch.port1.removeEventListener('message', handler)\n          resolve()\n        }\n        // @ts-ignore (typescript thinks this property should be called `addListener`, but in fact it's `addEventListener`)\n        ch.port1.addEventListener('message', handler)\n        ch.port2.postMessage(0)\n        ch.port1.start()\n      } else {\n        if (typeof setImmediate !== 'undefined') {\n          setImmediate(resolve)\n        } else if (typeof setTimeout !== 'undefined') {\n          setTimeout(resolve, 0)\n        } else {\n          // Last resort - resolve immediately\n          resolve()\n        }\n      }\n    } catch (e) {\n      console.error('during yield: ', e)\n      reject(e)\n    }\n  })\n}\n\nexport const alwaysTrue: Nostr['verifyEvent'] = (t: Event): t is VerifiedEvent => {\n  t[verifiedSymbol] = true\n  return true\n}\n", "/* global WebSocket */\n\nimport type { Event, EventTemplate, VerifiedEvent, Nostr, NostrEvent } from './core.ts'\nimport { matchFilters, type Filter } from './filter.ts'\nimport { getHex64, getSubscriptionId } from './fakejson.ts'\nimport { Queue, normalizeURL } from './utils.ts'\nimport { makeAuthEvent } from './nip42.ts'\nimport { yieldThread } from './helpers.ts'\n\ntype RelayWebSocket = WebSocket & {\n  ping?(): void\n  on?(event: 'pong', listener: () => void): any\n}\n\nexport type AbstractRelayConstructorOptions = {\n  verifyEvent: Nostr['verifyEvent']\n  websocketImplementation?: typeof WebSocket\n  enablePing?: boolean\n  enableReconnect?: boolean\n}\n\nexport class SendingOnClosedConnection extends Error {\n  constructor(message: string, relay: string) {\n    super(`Tried to send message '${message} on a closed connection to ${relay}.`)\n    this.name = 'SendingOnClosedConnection'\n  }\n}\n\nexport class AbstractRelay {\n  public readonly url: string\n  private _connected: boolean = false\n\n  public onclose: (() => void) | null = null\n  public onnotice: (msg: string) => void = msg => console.debug(`NOTICE from ${this.url}: ${msg}`)\n  public onauth: undefined | ((evt: EventTemplate) => Promise<VerifiedEvent>)\n\n  public baseEoseTimeout: number = 4400\n  public publishTimeout: number = 4400\n  public pingFrequency: number = 29000\n  public pingTimeout: number = 20000\n  public resubscribeBackoff: number[] = [10000, 10000, 10000, 20000, 20000, 30000, 60000]\n  public openSubs: Map<string, Subscription> = new Map()\n  public enablePing: boolean | undefined\n  public enableReconnect: boolean\n  private reconnectTimeoutHandle: ReturnType<typeof setTimeout> | undefined\n  private pingIntervalHandle: ReturnType<typeof setInterval> | undefined\n  private reconnectAttempts: number = 0\n  private closedIntentionally: boolean = false\n\n  private connectionPromise: Promise<void> | undefined\n  private openCountRequests = new Map<string, CountResolver>()\n  private openEventPublishes = new Map<string, EventPublishResolver>()\n  private ws: RelayWebSocket | undefined\n  private incomingMessageQueue = new Queue<string>()\n  private queueRunning = false\n  private challenge: string | undefined\n  private authPromise: Promise<string> | undefined\n  private serial: number = 0\n  private verifyEvent: Nostr['verifyEvent']\n\n  private _WebSocket: typeof WebSocket\n\n  constructor(url: string, opts: AbstractRelayConstructorOptions) {\n    this.url = normalizeURL(url)\n    this.verifyEvent = opts.verifyEvent\n    this._WebSocket = opts.websocketImplementation || WebSocket\n    this.enablePing = opts.enablePing\n    this.enableReconnect = opts.enableReconnect || false\n  }\n\n  static async connect(\n    url: string,\n    opts: AbstractRelayConstructorOptions & Parameters<AbstractRelay['connect']>[0],\n  ): Promise<AbstractRelay> {\n    const relay = new AbstractRelay(url, opts)\n    await relay.connect(opts)\n    return relay\n  }\n\n  private closeAllSubscriptions(reason: string) {\n    for (let [_, sub] of this.openSubs) {\n      sub.close(reason)\n    }\n    this.openSubs.clear()\n\n    for (let [_, ep] of this.openEventPublishes) {\n      ep.reject(new Error(reason))\n    }\n    this.openEventPublishes.clear()\n\n    for (let [_, cr] of this.openCountRequests) {\n      cr.reject(new Error(reason))\n    }\n    this.openCountRequests.clear()\n  }\n\n  public get connected(): boolean {\n    return this._connected\n  }\n\n  private async reconnect(): Promise<void> {\n    const backoff = this.resubscribeBackoff[Math.min(this.reconnectAttempts, this.resubscribeBackoff.length - 1)]\n    this.reconnectAttempts++\n\n    this.reconnectTimeoutHandle = setTimeout(async () => {\n      try {\n        await this.connect()\n      } catch (err) {\n        // this will be called again through onclose/onerror\n      }\n    }, backoff)\n  }\n\n  private handleHardClose(reason: string) {\n    if (this.pingIntervalHandle) {\n      clearInterval(this.pingIntervalHandle)\n      this.pingIntervalHandle = undefined\n    }\n\n    this._connected = false\n    this.connectionPromise = undefined\n\n    const wasIntentional = this.closedIntentionally\n    this.closedIntentionally = false // reset for next time\n\n    this.onclose?.()\n\n    if (this.enableReconnect && !wasIntentional) {\n      this.reconnect()\n    } else {\n      this.closeAllSubscriptions(reason)\n    }\n  }\n\n  public async connect(opts?: { timeout?: number; abort?: AbortSignal }): Promise<void> {\n    let connectionTimeoutHandle: ReturnType<typeof setTimeout> | undefined\n\n    if (this.connectionPromise) return this.connectionPromise\n\n    this.challenge = undefined\n    this.authPromise = undefined\n    this.connectionPromise = new Promise((resolve, reject) => {\n      if (opts?.timeout) {\n        connectionTimeoutHandle = setTimeout(() => {\n          reject('connection timed out')\n          this.connectionPromise = undefined\n          this.onclose?.()\n          this.closeAllSubscriptions('relay connection timed out')\n        }, opts.timeout)\n      }\n\n      if (opts?.abort) {\n        opts.abort.onabort = reject\n      }\n\n      try {\n        this.ws = new this._WebSocket(this.url)\n      } catch (err) {\n        clearTimeout(connectionTimeoutHandle)\n        reject(err)\n        return\n      }\n\n      this.ws.onopen = () => {\n        if (this.reconnectTimeoutHandle) {\n          clearTimeout(this.reconnectTimeoutHandle)\n          this.reconnectTimeoutHandle = undefined\n        }\n        clearTimeout(connectionTimeoutHandle)\n        this._connected = true\n\n        const isReconnection = this.reconnectAttempts > 0\n        this.reconnectAttempts = 0\n\n        // resubscribe to all open subscriptions\n        for (const sub of this.openSubs.values()) {\n          sub.eosed = false\n          if (isReconnection) {\n            for (let f = 0; f < sub.filters.length; f++) {\n              if (sub.lastEmitted) {\n                sub.filters[f].since = sub.lastEmitted + 1\n              }\n            }\n          }\n          sub.fire()\n        }\n\n        if (this.enablePing) {\n          this.pingIntervalHandle = setInterval(() => this.pingpong(), this.pingFrequency)\n        }\n        resolve()\n      }\n\n      this.ws.onerror = ev => {\n        clearTimeout(connectionTimeoutHandle)\n        reject((ev as any).message || 'websocket error')\n        this.handleHardClose('relay connection errored')\n      }\n\n      this.ws.onclose = ev => {\n        clearTimeout(connectionTimeoutHandle)\n        reject((ev as any).message || 'websocket closed')\n        this.handleHardClose('relay connection closed')\n      }\n\n      this.ws.onmessage = this._onmessage.bind(this)\n    })\n\n    return this.connectionPromise\n  }\n\n  private waitForPingPong() {\n    return new Promise(resolve => {\n      // listen for pong\n      ;(this.ws as any).once('pong', () => resolve(true))\n      // send a ping\n      this.ws!.ping!()\n    })\n  }\n\n  private waitForDummyReq() {\n    return new Promise((resolve, reject) => {\n      if (!this.connectionPromise) return reject(new Error(`no connection to ${this.url}, can't ping`))\n\n      // make a dummy request with expected empty eose reply\n      // [\"REQ\", \"_\", {\"ids\":[\"aaaa...aaaa\"], \"limit\": 0}]\n      try {\n        const sub = this.subscribe(\n          [{ ids: ['aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'], limit: 0 }],\n          {\n            label: 'forced-ping',\n            oneose: () => {\n              resolve(true)\n              sub.close()\n            },\n            onclose() {\n              // if we get a CLOSED it's because the relay is alive\n              resolve(true)\n            },\n            eoseTimeout: this.pingTimeout + 1000,\n          },\n        )\n      } catch (err) {\n        reject(err)\n      }\n    })\n  }\n\n  // nodejs requires this magic here to ensure connections are closed when internet goes off and stuff\n  // in browsers it's done automatically. see https://github.com/nbd-wtf/nostr-tools/issues/491\n  private async pingpong() {\n    // if the websocket is connected\n    if (this.ws?.readyState === 1) {\n      // wait for either a ping-pong reply or a timeout\n      const result = await Promise.any([\n        // browsers don't have ping so use a dummy req\n        this.ws && this.ws.ping && (this.ws as any).once ? this.waitForPingPong() : this.waitForDummyReq(),\n        new Promise(res => setTimeout(() => res(false), this.pingTimeout)),\n      ])\n\n      if (!result) {\n        // pingpong closing socket\n        if (this.ws?.readyState === this._WebSocket.OPEN) {\n          this.ws?.close()\n        }\n      }\n    }\n  }\n\n  private async runQueue() {\n    this.queueRunning = true\n    while (true) {\n      if (false === this.handleNext()) {\n        break\n      }\n      await yieldThread()\n    }\n    this.queueRunning = false\n  }\n\n  private handleNext(): undefined | false {\n    const json = this.incomingMessageQueue.dequeue()\n    if (!json) {\n      return false\n    }\n\n    // shortcut EVENT sub\n    const subid = getSubscriptionId(json)\n    if (subid) {\n      const so = this.openSubs.get(subid as string)\n      if (!so) {\n        // this is an EVENT message, but for a subscription we don't have, so just stop here\n        return\n      }\n\n      // this will be called only when this message is a EVENT message for a subscription we have\n      // we do this before parsing the JSON to not have to do that for duplicate events\n      //   since JSON parsing is slow\n      const id = getHex64(json, 'id')\n      const alreadyHave = so.alreadyHaveEvent?.(id)\n\n      // notify any interested client that the relay has this event\n      // (do this after alreadyHaveEvent() because the client may rely on this to answer that)\n      so.receivedEvent?.(this, id)\n\n      if (alreadyHave) {\n        // if we had already seen this event we can just stop here\n        return\n      }\n    }\n\n    try {\n      let data = JSON.parse(json)\n      // we won't do any checks against the data since all failures (i.e. invalid messages from relays)\n      // will naturally be caught by the encompassing try..catch block\n\n      switch (data[0]) {\n        case 'EVENT': {\n          const so = this.openSubs.get(data[1] as string) as Subscription\n          const event = data[2] as NostrEvent\n          if (this.verifyEvent(event) && matchFilters(so.filters, event)) {\n            so.onevent(event)\n          }\n          if (!so.lastEmitted || so.lastEmitted < event.created_at) so.lastEmitted = event.created_at\n          return\n        }\n        case 'COUNT': {\n          const id: string = data[1]\n          const payload = data[2] as { count: number }\n          const cr = this.openCountRequests.get(id) as CountResolver\n          if (cr) {\n            cr.resolve(payload.count)\n            this.openCountRequests.delete(id)\n          }\n          return\n        }\n        case 'EOSE': {\n          const so = this.openSubs.get(data[1] as string)\n          if (!so) return\n          so.receivedEose()\n          return\n        }\n        case 'OK': {\n          const id: string = data[1]\n          const ok: boolean = data[2]\n          const reason: string = data[3]\n          const ep = this.openEventPublishes.get(id) as EventPublishResolver\n          if (ep) {\n            clearTimeout(ep.timeout)\n            if (ok) ep.resolve(reason)\n            else ep.reject(new Error(reason))\n            this.openEventPublishes.delete(id)\n          }\n          return\n        }\n        case 'CLOSED': {\n          const id: string = data[1]\n          const so = this.openSubs.get(id)\n          if (!so) return\n          so.closed = true\n          so.close(data[2] as string)\n          return\n        }\n        case 'NOTICE': {\n          this.onnotice(data[1] as string)\n          return\n        }\n        case 'AUTH': {\n          this.challenge = data[1] as string\n          if (this.onauth) {\n            this.auth(this.onauth)\n          }\n          return\n        }\n        default: {\n          const so = this.openSubs.get(data[1])\n          so?.oncustom?.(data)\n          return\n        }\n      }\n    } catch (err) {\n      return\n    }\n  }\n\n  public async send(message: string) {\n    if (!this.connectionPromise) throw new SendingOnClosedConnection(message, this.url)\n\n    this.connectionPromise.then(() => {\n      this.ws?.send(message)\n    })\n  }\n\n  public async auth(signAuthEvent: (evt: EventTemplate) => Promise<VerifiedEvent>): Promise<string> {\n    const challenge = this.challenge\n    if (!challenge) throw new Error(\"can't perform auth, no challenge was received\")\n    if (this.authPromise) return this.authPromise\n\n    this.authPromise = new Promise<string>(async (resolve, reject) => {\n      try {\n        let evt = await signAuthEvent(makeAuthEvent(this.url, challenge))\n        let timeout = setTimeout(() => {\n          let ep = this.openEventPublishes.get(evt.id) as EventPublishResolver\n          if (ep) {\n            ep.reject(new Error('auth timed out'))\n            this.openEventPublishes.delete(evt.id)\n          }\n        }, this.publishTimeout)\n        this.openEventPublishes.set(evt.id, { resolve, reject, timeout })\n        this.send('[\"AUTH\",' + JSON.stringify(evt) + ']')\n      } catch (err) {\n        console.warn('subscribe auth function failed:', err)\n      }\n    })\n    return this.authPromise\n  }\n\n  public async publish(event: Event): Promise<string> {\n    const ret = new Promise<string>((resolve, reject) => {\n      const timeout = setTimeout(() => {\n        const ep = this.openEventPublishes.get(event.id) as EventPublishResolver\n        if (ep) {\n          ep.reject(new Error('publish timed out'))\n          this.openEventPublishes.delete(event.id)\n        }\n      }, this.publishTimeout)\n      this.openEventPublishes.set(event.id, { resolve, reject, timeout })\n    })\n    this.send('[\"EVENT\",' + JSON.stringify(event) + ']')\n    return ret\n  }\n\n  public async count(filters: Filter[], params: { id?: string | null }): Promise<number> {\n    this.serial++\n    const id = params?.id || 'count:' + this.serial\n    const ret = new Promise<number>((resolve, reject) => {\n      this.openCountRequests.set(id, { resolve, reject })\n    })\n    this.send('[\"COUNT\",\"' + id + '\",' + JSON.stringify(filters).substring(1))\n    return ret\n  }\n\n  public subscribe(\n    filters: Filter[],\n    params: Partial<SubscriptionParams> & { label?: string; id?: string },\n  ): Subscription {\n    const sub = this.prepareSubscription(filters, params)\n    sub.fire()\n\n    if (params.abort) {\n      params.abort.onabort = () => sub.close(String(params.abort!.reason || '<aborted>'))\n    }\n\n    return sub\n  }\n\n  public prepareSubscription(\n    filters: Filter[],\n    params: Partial<SubscriptionParams> & { label?: string; id?: string },\n  ): Subscription {\n    this.serial++\n    const id = params.id || (params.label ? params.label + ':' : 'sub:') + this.serial\n    const subscription = new Subscription(this, id, filters, params)\n    this.openSubs.set(id, subscription)\n    return subscription\n  }\n\n  public close() {\n    this.closedIntentionally = true\n    if (this.reconnectTimeoutHandle) {\n      clearTimeout(this.reconnectTimeoutHandle)\n      this.reconnectTimeoutHandle = undefined\n    }\n    if (this.pingIntervalHandle) {\n      clearInterval(this.pingIntervalHandle)\n      this.pingIntervalHandle = undefined\n    }\n    this.closeAllSubscriptions('relay connection closed by us')\n    this._connected = false\n    this.onclose?.()\n    if (this.ws?.readyState === this._WebSocket.OPEN) {\n      this.ws?.close()\n    }\n  }\n\n  // this is the function assigned to this.ws.onmessage\n  // it's exposed for testing and debugging purposes\n  public _onmessage(ev: MessageEvent<any>) {\n    this.incomingMessageQueue.enqueue(ev.data as string)\n    if (!this.queueRunning) {\n      this.runQueue()\n    }\n  }\n}\n\nexport class Subscription {\n  public readonly relay: AbstractRelay\n  public readonly id: string\n\n  public lastEmitted: number | undefined\n  public closed: boolean = false\n  public eosed: boolean = false\n  public filters: Filter[]\n  public alreadyHaveEvent: ((id: string) => boolean) | undefined\n  public receivedEvent: ((relay: AbstractRelay, id: string) => void) | undefined\n\n  public onevent: (evt: Event) => void\n  public oneose: (() => void) | undefined\n  public onclose: ((reason: string) => void) | undefined\n\n  // will get any messages that have this subscription id as their second item and are not default standard\n  public oncustom: ((msg: string[]) => void) | undefined\n\n  public eoseTimeout: number\n  private eoseTimeoutHandle: ReturnType<typeof setTimeout> | undefined\n\n  constructor(relay: AbstractRelay, id: string, filters: Filter[], params: SubscriptionParams) {\n    if (filters.length === 0) throw new Error(\"subscription can't be created with zero filters\")\n\n    this.relay = relay\n    this.filters = filters\n    this.id = id\n    this.alreadyHaveEvent = params.alreadyHaveEvent\n    this.receivedEvent = params.receivedEvent\n    this.eoseTimeout = params.eoseTimeout || relay.baseEoseTimeout\n\n    this.oneose = params.oneose\n    this.onclose = params.onclose\n    this.onevent =\n      params.onevent ||\n      (event => {\n        console.warn(\n          `onevent() callback not defined for subscription '${this.id}' in relay ${this.relay.url}. event received:`,\n          event,\n        )\n      })\n  }\n\n  public fire() {\n    this.relay.send('[\"REQ\",\"' + this.id + '\",' + JSON.stringify(this.filters).substring(1))\n\n    // only now we start counting the eoseTimeout\n    this.eoseTimeoutHandle = setTimeout(this.receivedEose.bind(this), this.eoseTimeout)\n  }\n\n  public receivedEose() {\n    if (this.eosed) return\n    clearTimeout(this.eoseTimeoutHandle)\n    this.eosed = true\n    this.oneose?.()\n  }\n\n  public close(reason: string = 'closed by caller') {\n    if (!this.closed && this.relay.connected) {\n      // if the connection was closed by the user calling .close() we will send a CLOSE message\n      // otherwise this._open will be already set to false so we will skip this\n      try {\n        this.relay.send('[\"CLOSE\",' + JSON.stringify(this.id) + ']')\n      } catch (err) {\n        if (err instanceof SendingOnClosedConnection) {\n          /* doesn't matter, it's ok */\n        } else {\n          throw err\n        }\n      }\n      this.closed = true\n    }\n    this.relay.openSubs.delete(this.id)\n    this.onclose?.(reason)\n  }\n}\n\nexport type SubscriptionParams = {\n  onevent?: (evt: Event) => void\n  oneose?: () => void\n  onclose?: (reason: string) => void\n  alreadyHaveEvent?: (id: string) => boolean\n  receivedEvent?: (relay: AbstractRelay, id: string) => void\n  eoseTimeout?: number\n  abort?: AbortSignal\n}\n\nexport type CountResolver = {\n  resolve: (count: number) => void\n  reject: (err: Error) => void\n}\n\nexport type EventPublishResolver = {\n  resolve: (reason: string) => void\n  reject: (err: Error) => void\n  timeout: ReturnType<typeof setTimeout>\n}\n", "/* global WebSocket */\n\nimport {\n  AbstractRelay as AbstractRelay,\n  SubscriptionParams,\n  Subscription,\n  type AbstractRelayConstructorOptions,\n} from './abstract-relay.ts'\nimport { normalizeURL } from './utils.ts'\n\nimport type { Event, EventTemplate, Nostr, VerifiedEvent } from './core.ts'\nimport { type Filter } from './filter.ts'\nimport { alwaysTrue } from './helpers.ts'\n\nexport type SubCloser = { close: (reason?: string) => void }\n\nexport type AbstractPoolConstructorOptions = AbstractRelayConstructorOptions & {\n  // automaticallyAuth takes a relay URL and should return null\n  // in case that relay shouldn't be authenticated against\n  // or a function to sign the AUTH event template otherwise (that function may still throw in case of failure)\n  automaticallyAuth?: (relayURL: string) => null | ((event: EventTemplate) => Promise<VerifiedEvent>)\n}\n\nexport type SubscribeManyParams = Omit<SubscriptionParams, 'onclose'> & {\n  maxWait?: number\n  abort?: AbortSignal\n  onclose?: (reasons: string[]) => void\n  onauth?: (event: EventTemplate) => Promise<VerifiedEvent>\n  id?: string\n  label?: string\n}\n\nexport class AbstractSimplePool {\n  protected relays: Map<string, AbstractRelay> = new Map()\n  public seenOn: Map<string, Set<AbstractRelay>> = new Map()\n  public trackRelays: boolean = false\n\n  public verifyEvent: Nostr['verifyEvent']\n  public enablePing: boolean | undefined\n  public enableReconnect: boolean\n  public automaticallyAuth?: (relayURL: string) => null | ((event: EventTemplate) => Promise<VerifiedEvent>)\n  public trustedRelayURLs: Set<string> = new Set()\n\n  private _WebSocket?: typeof WebSocket\n\n  constructor(opts: AbstractPoolConstructorOptions) {\n    this.verifyEvent = opts.verifyEvent\n    this._WebSocket = opts.websocketImplementation\n    this.enablePing = opts.enablePing\n    this.enableReconnect = opts.enableReconnect || false\n    this.automaticallyAuth = opts.automaticallyAuth\n  }\n\n  async ensureRelay(\n    url: string,\n    params?: {\n      connectionTimeout?: number\n      abort?: AbortSignal\n    },\n  ): Promise<AbstractRelay> {\n    url = normalizeURL(url)\n\n    let relay = this.relays.get(url)\n    if (!relay) {\n      relay = new AbstractRelay(url, {\n        verifyEvent: this.trustedRelayURLs.has(url) ? alwaysTrue : this.verifyEvent,\n        websocketImplementation: this._WebSocket,\n        enablePing: this.enablePing,\n        enableReconnect: this.enableReconnect,\n      })\n      relay.onclose = () => {\n        if (relay && !relay.enableReconnect) {\n          this.relays.delete(url)\n        }\n      }\n      this.relays.set(url, relay)\n    }\n\n    if (this.automaticallyAuth) {\n      const authSignerFn = this.automaticallyAuth(url)\n      if (authSignerFn) {\n        relay.onauth = authSignerFn\n      }\n    }\n\n    await relay.connect({\n      timeout: params?.connectionTimeout,\n      abort: params?.abort,\n    })\n\n    return relay\n  }\n\n  close(relays: string[]) {\n    relays.map(normalizeURL).forEach(url => {\n      this.relays.get(url)?.close()\n      this.relays.delete(url)\n    })\n  }\n\n  subscribe(relays: string[], filter: Filter, params: SubscribeManyParams): SubCloser {\n    const request: { url: string; filter: Filter }[] = []\n    for (let i = 0; i < relays.length; i++) {\n      const url = normalizeURL(relays[i])\n      if (!request.find(r => r.url === url)) {\n        request.push({ url, filter: filter })\n      }\n    }\n\n    return this.subscribeMap(request, params)\n  }\n\n  subscribeMany(relays: string[], filter: Filter, params: SubscribeManyParams): SubCloser {\n    const request: { url: string; filter: Filter }[] = []\n    const uniqUrls: string[] = []\n    for (let i = 0; i < relays.length; i++) {\n      const url = normalizeURL(relays[i])\n      if (uniqUrls.indexOf(url) === -1) {\n        uniqUrls.push(url)\n        request.push({ url, filter: filter })\n      }\n    }\n\n    return this.subscribeMap(request, params)\n  }\n\n  subscribeMap(requests: { url: string; filter: Filter }[], params: SubscribeManyParams): SubCloser {\n    const grouped = new Map<string, Filter[]>()\n    for (const req of requests) {\n      const { url, filter } = req\n      if (!grouped.has(url)) grouped.set(url, [])\n      grouped.get(url)!.push(filter)\n    }\n    const groupedRequests = Array.from(grouped.entries()).map(([url, filters]) => ({ url, filters }))\n\n    if (this.trackRelays) {\n      params.receivedEvent = (relay: AbstractRelay, id: string) => {\n        let set = this.seenOn.get(id)\n        if (!set) {\n          set = new Set()\n          this.seenOn.set(id, set)\n        }\n        set.add(relay)\n      }\n    }\n\n    const _knownIds = new Set<string>()\n    const subs: Subscription[] = []\n\n    // batch all EOSEs into a single\n    const eosesReceived: boolean[] = []\n    let handleEose = (i: number) => {\n      if (eosesReceived[i]) return // do not act twice for the same relay\n      eosesReceived[i] = true\n      if (eosesReceived.filter(a => a).length === groupedRequests.length) {\n        params.oneose?.()\n        handleEose = () => {}\n      }\n    }\n    // batch all closes into a single\n    const closesReceived: string[] = []\n    let handleClose = (i: number, reason: string) => {\n      if (closesReceived[i]) return // do not act twice for the same relay\n      handleEose(i)\n      closesReceived[i] = reason\n      if (closesReceived.filter(a => a).length === groupedRequests.length) {\n        params.onclose?.(closesReceived)\n        handleClose = () => {}\n      }\n    }\n\n    const localAlreadyHaveEventHandler = (id: string) => {\n      if (params.alreadyHaveEvent?.(id)) {\n        return true\n      }\n      const have = _knownIds.has(id)\n      _knownIds.add(id)\n      return have\n    }\n\n    // open a subscription in all given relays\n    const allOpened = Promise.all(\n      groupedRequests.map(async ({ url, filters }, i) => {\n        let relay: AbstractRelay\n        try {\n          relay = await this.ensureRelay(url, {\n            connectionTimeout: params.maxWait ? Math.max(params.maxWait * 0.8, params.maxWait - 1000) : undefined,\n            abort: params.abort,\n          })\n        } catch (err) {\n          handleClose(i, (err as any)?.message || String(err))\n          return\n        }\n\n        let subscription = relay.subscribe(filters, {\n          ...params,\n          oneose: () => handleEose(i),\n          onclose: reason => {\n            if (reason.startsWith('auth-required: ') && params.onauth) {\n              relay\n                .auth(params.onauth)\n                .then(() => {\n                  relay.subscribe(filters, {\n                    ...params,\n                    oneose: () => handleEose(i),\n                    onclose: reason => {\n                      handleClose(i, reason) // the second time we won't try to auth anymore\n                    },\n                    alreadyHaveEvent: localAlreadyHaveEventHandler,\n                    eoseTimeout: params.maxWait,\n                    abort: params.abort,\n                  })\n                })\n                .catch(err => {\n                  handleClose(i, `auth was required and attempted, but failed with: ${err}`)\n                })\n            } else {\n              handleClose(i, reason)\n            }\n          },\n          alreadyHaveEvent: localAlreadyHaveEventHandler,\n          eoseTimeout: params.maxWait,\n          abort: params.abort,\n        })\n\n        subs.push(subscription)\n      }),\n    )\n\n    return {\n      async close(reason?: string) {\n        await allOpened\n        subs.forEach(sub => {\n          sub.close(reason)\n        })\n      },\n    }\n  }\n\n  subscribeEose(\n    relays: string[],\n    filter: Filter,\n    params: Pick<SubscribeManyParams, 'label' | 'id' | 'onevent' | 'onclose' | 'maxWait' | 'onauth'>,\n  ): SubCloser {\n    const subcloser = this.subscribe(relays, filter, {\n      ...params,\n      oneose() {\n        subcloser.close('closed automatically on eose')\n      },\n    })\n    return subcloser\n  }\n\n  subscribeManyEose(\n    relays: string[],\n    filter: Filter,\n    params: Pick<SubscribeManyParams, 'label' | 'id' | 'onevent' | 'onclose' | 'maxWait' | 'onauth'>,\n  ): SubCloser {\n    const subcloser = this.subscribeMany(relays, filter, {\n      ...params,\n      oneose() {\n        subcloser.close('closed automatically on eose')\n      },\n    })\n    return subcloser\n  }\n\n  async querySync(\n    relays: string[],\n    filter: Filter,\n    params?: Pick<SubscribeManyParams, 'label' | 'id' | 'maxWait'>,\n  ): Promise<Event[]> {\n    return new Promise(async resolve => {\n      const events: Event[] = []\n      this.subscribeEose(relays, filter, {\n        ...params,\n        onevent(event: Event) {\n          events.push(event)\n        },\n        onclose(_: string[]) {\n          resolve(events)\n        },\n      })\n    })\n  }\n\n  async get(\n    relays: string[],\n    filter: Filter,\n    params?: Pick<SubscribeManyParams, 'label' | 'id' | 'maxWait'>,\n  ): Promise<Event | null> {\n    filter.limit = 1\n    const events = await this.querySync(relays, filter, params)\n    events.sort((a, b) => b.created_at - a.created_at)\n    return events[0] || null\n  }\n\n  publish(\n    relays: string[],\n    event: Event,\n    options?: { onauth?: (evt: EventTemplate) => Promise<VerifiedEvent> },\n  ): Promise<string>[] {\n    return relays.map(normalizeURL).map(async (url, i, arr) => {\n      if (arr.indexOf(url) !== i) {\n        // duplicate\n        return Promise.reject('duplicate url')\n      }\n\n      let r = await this.ensureRelay(url)\n      return r\n        .publish(event)\n        .catch(async err => {\n          if (err instanceof Error && err.message.startsWith('auth-required: ') && options?.onauth) {\n            await r.auth(options.onauth)\n            return r.publish(event) // retry\n          }\n          throw err\n        })\n        .then(reason => {\n          if (this.trackRelays) {\n            let set = this.seenOn.get(event.id)\n            if (!set) {\n              set = new Set()\n              this.seenOn.set(event.id, set)\n            }\n            set.add(r)\n          }\n          return reason\n        })\n    })\n  }\n\n  listConnectionStatus(): Map<string, boolean> {\n    const map = new Map<string, boolean>()\n    this.relays.forEach((relay, url) => map.set(url, relay.connected))\n\n    return map\n  }\n\n  destroy(): void {\n    this.relays.forEach(conn => conn.close())\n    this.relays = new Map()\n  }\n}\n", "/* global WebSocket */\n\nimport { verifyEvent } from './pure.ts'\nimport { AbstractSimplePool, type AbstractPoolConstructorOptions } from './abstract-pool.ts'\n\nvar _WebSocket: typeof WebSocket\n\ntry {\n  _WebSocket = WebSocket\n} catch {}\n\nexport function useWebSocketImplementation(websocketImplementation: any) {\n  _WebSocket = websocketImplementation\n}\n\nexport class SimplePool extends AbstractSimplePool {\n  constructor(options?: Pick<AbstractPoolConstructorOptions, 'enablePing' | 'enableReconnect'>) {\n    super({ verifyEvent, websocketImplementation: _WebSocket, ...options })\n  }\n}\n\nexport * from './abstract-pool.ts'\n"],
+  "mappings": "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",
+  "names": ["bytesToHex", "hexToBytes", "i", "target", "bytesToHex", "hexToBytes", "i", "i", "reason"]
 }
Index: package/lib/cjs/pure.js.map
===================================================================
--- package/lib/cjs/pure.js.map
+++ package/lib/cjs/pure.js.map
@@ -1,7 +1,7 @@
 {
   "version": 3,
   "sources": ["../../pure.ts", "../../core.ts", "../../utils.ts"],
-  "sourcesContent": ["import { schnorr } from '@noble/curves/secp256k1'\nimport { bytesToHex } from '@noble/hashes/utils'\nimport { Nostr, Event, EventTemplate, UnsignedEvent, VerifiedEvent, verifiedSymbol, validateEvent } from './core.ts'\nimport { sha256 } from '@noble/hashes/sha256'\n\nimport { utf8Encoder } from './utils.ts'\n\nclass JS implements Nostr {\n  generateSecretKey(): Uint8Array {\n    return schnorr.utils.randomPrivateKey()\n  }\n  getPublicKey(secretKey: Uint8Array): string {\n    return bytesToHex(schnorr.getPublicKey(secretKey))\n  }\n  finalizeEvent(t: EventTemplate, secretKey: Uint8Array): VerifiedEvent {\n    const event = t as VerifiedEvent\n    event.pubkey = bytesToHex(schnorr.getPublicKey(secretKey))\n    event.id = getEventHash(event)\n    event.sig = bytesToHex(schnorr.sign(getEventHash(event), secretKey))\n    event[verifiedSymbol] = true\n    return event\n  }\n  verifyEvent(event: Event): event is VerifiedEvent {\n    if (typeof event[verifiedSymbol] === 'boolean') return event[verifiedSymbol]\n\n    const hash = getEventHash(event)\n    if (hash !== event.id) {\n      event[verifiedSymbol] = false\n      return false\n    }\n\n    try {\n      const valid = schnorr.verify(event.sig, hash, event.pubkey)\n      event[verifiedSymbol] = valid\n      return valid\n    } catch (err) {\n      event[verifiedSymbol] = false\n      return false\n    }\n  }\n}\n\nexport function serializeEvent(evt: UnsignedEvent): string {\n  if (!validateEvent(evt)) throw new Error(\"can't serialize event with wrong or missing properties\")\n  return JSON.stringify([0, evt.pubkey, evt.created_at, evt.kind, evt.tags, evt.content])\n}\n\nexport function getEventHash(event: UnsignedEvent): string {\n  let eventHash = sha256(utf8Encoder.encode(serializeEvent(event)))\n  return bytesToHex(eventHash)\n}\n\nconst i: JS = new JS()\n\nexport const generateSecretKey = i.generateSecretKey\nexport const getPublicKey = i.getPublicKey\nexport const finalizeEvent = i.finalizeEvent\nexport const verifyEvent = i.verifyEvent\nexport * from './core.ts'\n", "export interface Nostr {\n  generateSecretKey(): Uint8Array\n  getPublicKey(secretKey: Uint8Array): string\n  finalizeEvent(event: EventTemplate, secretKey: Uint8Array): VerifiedEvent\n  verifyEvent(event: Event): event is VerifiedEvent\n}\n\n/** Designates a verified event signature. */\nexport const verifiedSymbol = Symbol('verified')\n\nexport type NostrEvent = {\n  kind: number\n  tags: string[][]\n  content: string\n  created_at: number\n  pubkey: string\n  id: string\n  sig: string\n  [verifiedSymbol]?: boolean\n}\n\nexport type Event = NostrEvent\nexport type EventTemplate = Pick<Event, 'kind' | 'tags' | 'content' | 'created_at'>\nexport type UnsignedEvent = Pick<Event, 'kind' | 'tags' | 'content' | 'created_at' | 'pubkey'>\n\n/** An event whose signature has been verified. */\nexport interface VerifiedEvent extends Event {\n  [verifiedSymbol]: true\n}\n\nconst isRecord = (obj: unknown): obj is Record<string, unknown> => obj instanceof Object\n\nexport function validateEvent<T>(event: T): event is T & UnsignedEvent {\n  if (!isRecord(event)) return false\n  if (typeof event.kind !== 'number') return false\n  if (typeof event.content !== 'string') return false\n  if (typeof event.created_at !== 'number') return false\n  if (typeof event.pubkey !== 'string') return false\n  if (!event.pubkey.match(/^[a-f0-9]{64}$/)) return false\n\n  if (!Array.isArray(event.tags)) return false\n  for (let i = 0; i < event.tags.length; i++) {\n    let tag = event.tags[i]\n    if (!Array.isArray(tag)) return false\n    for (let j = 0; j < tag.length; j++) {\n      if (typeof tag[j] !== 'string') return false\n    }\n  }\n\n  return true\n}\n\n/**\n * Sort events in reverse-chronological order by the `created_at` timestamp,\n * and then by the event `id` (lexicographically) in case of ties.\n * This mutates the array.\n */\nexport function sortEvents(events: Event[]): Event[] {\n  return events.sort((a: NostrEvent, b: NostrEvent): number => {\n    if (a.created_at !== b.created_at) {\n      return b.created_at - a.created_at\n    }\n    return a.id.localeCompare(b.id)\n  })\n}\n", "import type { Event } from './core.ts'\n\nexport const utf8Decoder: TextDecoder = new TextDecoder('utf-8')\nexport const utf8Encoder: TextEncoder = new TextEncoder()\n\nexport { bytesToHex, hexToBytes } from '@noble/hashes/utils'\n\nexport function normalizeURL(url: string): string {\n  try {\n    if (url.indexOf('://') === -1) url = 'wss://' + url\n    let p = new URL(url)\n    if (p.protocol === 'http:') p.protocol = 'ws:'\n    else if (p.protocol === 'https:') p.protocol = 'wss:'\n    p.pathname = p.pathname.replace(/\\/+/g, '/')\n    if (p.pathname.endsWith('/')) p.pathname = p.pathname.slice(0, -1)\n    if ((p.port === '80' && p.protocol === 'ws:') || (p.port === '443' && p.protocol === 'wss:')) p.port = ''\n    p.searchParams.sort()\n    p.hash = ''\n    return p.toString()\n  } catch (e) {\n    throw new Error(`Invalid URL: ${url}`)\n  }\n}\n\nexport function insertEventIntoDescendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return b.created_at - event.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function insertEventIntoAscendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return event.created_at - b.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function binarySearch<T>(arr: T[], compare: (b: T) => number): [number, boolean] {\n  let start = 0\n  let end = arr.length - 1\n\n  while (start <= end) {\n    const mid = Math.floor((start + end) / 2)\n    const cmp = compare(arr[mid])\n\n    if (cmp === 0) {\n      return [mid, true]\n    }\n\n    if (cmp < 0) {\n      end = mid - 1\n    } else {\n      start = mid + 1\n    }\n  }\n\n  return [start, false]\n}\n\nexport class QueueNode<V> {\n  public value: V\n  public next: QueueNode<V> | null = null\n  public prev: QueueNode<V> | null = null\n\n  constructor(message: V) {\n    this.value = message\n  }\n}\n\nexport class Queue<V> {\n  public first: QueueNode<V> | null\n  public last: QueueNode<V> | null\n\n  constructor() {\n    this.first = null\n    this.last = null\n  }\n\n  enqueue(value: V): boolean {\n    const newNode = new QueueNode(value)\n    if (!this.last) {\n      // list is empty\n      this.first = newNode\n      this.last = newNode\n    } else if (this.last === this.first) {\n      // list has a single element\n      this.last = newNode\n      this.last.prev = this.first\n      this.first.next = newNode\n    } else {\n      // list has elements, add as last\n      newNode.prev = this.last\n      this.last.next = newNode\n      this.last = newNode\n    }\n    return true\n  }\n\n  dequeue(): V | null {\n    if (!this.first) return null\n\n    if (this.first === this.last) {\n      const target = this.first\n      this.first = null\n      this.last = null\n      return target.value\n    }\n\n    const target = this.first\n    this.first = target.next\n    if (this.first) {\n      this.first.prev = null // fix: clean up prev pointer\n    }\n\n    return target.value\n  }\n}\n"],
-  "mappings": "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",
+  "sourcesContent": ["import { schnorr } from '@noble/curves/secp256k1.js'\nimport { bytesToHex, hexToBytes } from '@noble/hashes/utils.js'\nimport { Nostr, Event, EventTemplate, UnsignedEvent, VerifiedEvent, verifiedSymbol, validateEvent } from './core.ts'\nimport { sha256 } from '@noble/hashes/sha2.js'\n\nimport { utf8Encoder } from './utils.ts'\n\nclass JS implements Nostr {\n  generateSecretKey(): Uint8Array {\n    return schnorr.utils.randomSecretKey()\n  }\n  getPublicKey(secretKey: Uint8Array): string {\n    return bytesToHex(schnorr.getPublicKey(secretKey))\n  }\n  finalizeEvent(t: EventTemplate, secretKey: Uint8Array): VerifiedEvent {\n    const event = t as VerifiedEvent\n    event.pubkey = bytesToHex(schnorr.getPublicKey(secretKey))\n    event.id = getEventHash(event)\n    event.sig = bytesToHex(schnorr.sign(hexToBytes(getEventHash(event)), secretKey))\n    event[verifiedSymbol] = true\n    return event\n  }\n  verifyEvent(event: Event): event is VerifiedEvent {\n    if (typeof event[verifiedSymbol] === 'boolean') return event[verifiedSymbol]\n\n    const hash = getEventHash(event)\n    if (hash !== event.id) {\n      event[verifiedSymbol] = false\n      return false\n    }\n\n    try {\n      const valid = schnorr.verify(hexToBytes(event.sig), hexToBytes(hash), hexToBytes(event.pubkey))\n      event[verifiedSymbol] = valid\n      return valid\n    } catch (err) {\n      event[verifiedSymbol] = false\n      return false\n    }\n  }\n}\n\nexport function serializeEvent(evt: UnsignedEvent): string {\n  if (!validateEvent(evt)) throw new Error(\"can't serialize event with wrong or missing properties\")\n  return JSON.stringify([0, evt.pubkey, evt.created_at, evt.kind, evt.tags, evt.content])\n}\n\nexport function getEventHash(event: UnsignedEvent): string {\n  let eventHash = sha256(utf8Encoder.encode(serializeEvent(event)))\n  return bytesToHex(eventHash)\n}\n\nconst i: JS = new JS()\n\nexport const generateSecretKey = i.generateSecretKey\nexport const getPublicKey = i.getPublicKey\nexport const finalizeEvent = i.finalizeEvent\nexport const verifyEvent = i.verifyEvent\nexport * from './core.ts'\n", "export interface Nostr {\n  generateSecretKey(): Uint8Array\n  getPublicKey(secretKey: Uint8Array): string\n  finalizeEvent(event: EventTemplate, secretKey: Uint8Array): VerifiedEvent\n  verifyEvent(event: Event): event is VerifiedEvent\n}\n\n/** Designates a verified event signature. */\nexport const verifiedSymbol = Symbol('verified')\n\nexport type NostrEvent = {\n  kind: number\n  tags: string[][]\n  content: string\n  created_at: number\n  pubkey: string\n  id: string\n  sig: string\n  [verifiedSymbol]?: boolean\n}\n\nexport type Event = NostrEvent\nexport type EventTemplate = Pick<Event, 'kind' | 'tags' | 'content' | 'created_at'>\nexport type UnsignedEvent = Pick<Event, 'kind' | 'tags' | 'content' | 'created_at' | 'pubkey'>\n\n/** An event whose signature has been verified. */\nexport interface VerifiedEvent extends Event {\n  [verifiedSymbol]: true\n}\n\nconst isRecord = (obj: unknown): obj is Record<string, unknown> => obj instanceof Object\n\nexport function validateEvent<T>(event: T): event is T & UnsignedEvent {\n  if (!isRecord(event)) return false\n  if (typeof event.kind !== 'number') return false\n  if (typeof event.content !== 'string') return false\n  if (typeof event.created_at !== 'number') return false\n  if (typeof event.pubkey !== 'string') return false\n  if (!event.pubkey.match(/^[a-f0-9]{64}$/)) return false\n\n  if (!Array.isArray(event.tags)) return false\n  for (let i = 0; i < event.tags.length; i++) {\n    let tag = event.tags[i]\n    if (!Array.isArray(tag)) return false\n    for (let j = 0; j < tag.length; j++) {\n      if (typeof tag[j] !== 'string') return false\n    }\n  }\n\n  return true\n}\n\n/**\n * Sort events in reverse-chronological order by the `created_at` timestamp,\n * and then by the event `id` (lexicographically) in case of ties.\n * This mutates the array.\n */\nexport function sortEvents(events: Event[]): Event[] {\n  return events.sort((a: NostrEvent, b: NostrEvent): number => {\n    if (a.created_at !== b.created_at) {\n      return b.created_at - a.created_at\n    }\n    return a.id.localeCompare(b.id)\n  })\n}\n", "import type { Event } from './core.ts'\n\nexport const utf8Decoder: TextDecoder = new TextDecoder('utf-8')\nexport const utf8Encoder: TextEncoder = new TextEncoder()\n\nexport { bytesToHex, hexToBytes } from '@noble/hashes/utils.js'\n\nexport function normalizeURL(url: string): string {\n  try {\n    if (url.indexOf('://') === -1) url = 'wss://' + url\n    let p = new URL(url)\n    if (p.protocol === 'http:') p.protocol = 'ws:'\n    else if (p.protocol === 'https:') p.protocol = 'wss:'\n    p.pathname = p.pathname.replace(/\\/+/g, '/')\n    if (p.pathname.endsWith('/')) p.pathname = p.pathname.slice(0, -1)\n    if ((p.port === '80' && p.protocol === 'ws:') || (p.port === '443' && p.protocol === 'wss:')) p.port = ''\n    p.searchParams.sort()\n    p.hash = ''\n    return p.toString()\n  } catch (e) {\n    throw new Error(`Invalid URL: ${url}`)\n  }\n}\n\nexport function insertEventIntoDescendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return b.created_at - event.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function insertEventIntoAscendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return event.created_at - b.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function binarySearch<T>(arr: T[], compare: (b: T) => number): [number, boolean] {\n  let start = 0\n  let end = arr.length - 1\n\n  while (start <= end) {\n    const mid = Math.floor((start + end) / 2)\n    const cmp = compare(arr[mid])\n\n    if (cmp === 0) {\n      return [mid, true]\n    }\n\n    if (cmp < 0) {\n      end = mid - 1\n    } else {\n      start = mid + 1\n    }\n  }\n\n  return [start, false]\n}\n\nexport class QueueNode<V> {\n  public value: V\n  public next: QueueNode<V> | null = null\n  public prev: QueueNode<V> | null = null\n\n  constructor(message: V) {\n    this.value = message\n  }\n}\n\nexport class Queue<V> {\n  public first: QueueNode<V> | null\n  public last: QueueNode<V> | null\n\n  constructor() {\n    this.first = null\n    this.last = null\n  }\n\n  enqueue(value: V): boolean {\n    const newNode = new QueueNode(value)\n    if (!this.last) {\n      // list is empty\n      this.first = newNode\n      this.last = newNode\n    } else if (this.last === this.first) {\n      // list has a single element\n      this.last = newNode\n      this.last.prev = this.first\n      this.first.next = newNode\n    } else {\n      // list has elements, add as last\n      newNode.prev = this.last\n      this.last.next = newNode\n      this.last = newNode\n    }\n    return true\n  }\n\n  dequeue(): V | null {\n    if (!this.first) return null\n\n    if (this.first === this.last) {\n      const target = this.first\n      this.first = null\n      this.last = null\n      return target.value\n    }\n\n    const target = this.first\n    this.first = target.next\n    if (this.first) {\n      this.first.prev = null // fix: clean up prev pointer\n    }\n\n    return target.value\n  }\n}\n"],
+  "mappings": "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",
   "names": ["import_utils", "i"]
 }
Index: package/lib/esm/pure.js.map
===================================================================
--- package/lib/esm/pure.js.map
+++ package/lib/esm/pure.js.map
@@ -1,7 +1,7 @@
 {
   "version": 3,
   "sources": ["../../pure.ts", "../../core.ts", "../../utils.ts"],
-  "sourcesContent": ["import { schnorr } from '@noble/curves/secp256k1'\nimport { bytesToHex } from '@noble/hashes/utils'\nimport { Nostr, Event, EventTemplate, UnsignedEvent, VerifiedEvent, verifiedSymbol, validateEvent } from './core.ts'\nimport { sha256 } from '@noble/hashes/sha256'\n\nimport { utf8Encoder } from './utils.ts'\n\nclass JS implements Nostr {\n  generateSecretKey(): Uint8Array {\n    return schnorr.utils.randomPrivateKey()\n  }\n  getPublicKey(secretKey: Uint8Array): string {\n    return bytesToHex(schnorr.getPublicKey(secretKey))\n  }\n  finalizeEvent(t: EventTemplate, secretKey: Uint8Array): VerifiedEvent {\n    const event = t as VerifiedEvent\n    event.pubkey = bytesToHex(schnorr.getPublicKey(secretKey))\n    event.id = getEventHash(event)\n    event.sig = bytesToHex(schnorr.sign(getEventHash(event), secretKey))\n    event[verifiedSymbol] = true\n    return event\n  }\n  verifyEvent(event: Event): event is VerifiedEvent {\n    if (typeof event[verifiedSymbol] === 'boolean') return event[verifiedSymbol]\n\n    const hash = getEventHash(event)\n    if (hash !== event.id) {\n      event[verifiedSymbol] = false\n      return false\n    }\n\n    try {\n      const valid = schnorr.verify(event.sig, hash, event.pubkey)\n      event[verifiedSymbol] = valid\n      return valid\n    } catch (err) {\n      event[verifiedSymbol] = false\n      return false\n    }\n  }\n}\n\nexport function serializeEvent(evt: UnsignedEvent): string {\n  if (!validateEvent(evt)) throw new Error(\"can't serialize event with wrong or missing properties\")\n  return JSON.stringify([0, evt.pubkey, evt.created_at, evt.kind, evt.tags, evt.content])\n}\n\nexport function getEventHash(event: UnsignedEvent): string {\n  let eventHash = sha256(utf8Encoder.encode(serializeEvent(event)))\n  return bytesToHex(eventHash)\n}\n\nconst i: JS = new JS()\n\nexport const generateSecretKey = i.generateSecretKey\nexport const getPublicKey = i.getPublicKey\nexport const finalizeEvent = i.finalizeEvent\nexport const verifyEvent = i.verifyEvent\nexport * from './core.ts'\n", "export interface Nostr {\n  generateSecretKey(): Uint8Array\n  getPublicKey(secretKey: Uint8Array): string\n  finalizeEvent(event: EventTemplate, secretKey: Uint8Array): VerifiedEvent\n  verifyEvent(event: Event): event is VerifiedEvent\n}\n\n/** Designates a verified event signature. */\nexport const verifiedSymbol = Symbol('verified')\n\nexport type NostrEvent = {\n  kind: number\n  tags: string[][]\n  content: string\n  created_at: number\n  pubkey: string\n  id: string\n  sig: string\n  [verifiedSymbol]?: boolean\n}\n\nexport type Event = NostrEvent\nexport type EventTemplate = Pick<Event, 'kind' | 'tags' | 'content' | 'created_at'>\nexport type UnsignedEvent = Pick<Event, 'kind' | 'tags' | 'content' | 'created_at' | 'pubkey'>\n\n/** An event whose signature has been verified. */\nexport interface VerifiedEvent extends Event {\n  [verifiedSymbol]: true\n}\n\nconst isRecord = (obj: unknown): obj is Record<string, unknown> => obj instanceof Object\n\nexport function validateEvent<T>(event: T): event is T & UnsignedEvent {\n  if (!isRecord(event)) return false\n  if (typeof event.kind !== 'number') return false\n  if (typeof event.content !== 'string') return false\n  if (typeof event.created_at !== 'number') return false\n  if (typeof event.pubkey !== 'string') return false\n  if (!event.pubkey.match(/^[a-f0-9]{64}$/)) return false\n\n  if (!Array.isArray(event.tags)) return false\n  for (let i = 0; i < event.tags.length; i++) {\n    let tag = event.tags[i]\n    if (!Array.isArray(tag)) return false\n    for (let j = 0; j < tag.length; j++) {\n      if (typeof tag[j] !== 'string') return false\n    }\n  }\n\n  return true\n}\n\n/**\n * Sort events in reverse-chronological order by the `created_at` timestamp,\n * and then by the event `id` (lexicographically) in case of ties.\n * This mutates the array.\n */\nexport function sortEvents(events: Event[]): Event[] {\n  return events.sort((a: NostrEvent, b: NostrEvent): number => {\n    if (a.created_at !== b.created_at) {\n      return b.created_at - a.created_at\n    }\n    return a.id.localeCompare(b.id)\n  })\n}\n", "import type { Event } from './core.ts'\n\nexport const utf8Decoder: TextDecoder = new TextDecoder('utf-8')\nexport const utf8Encoder: TextEncoder = new TextEncoder()\n\nexport { bytesToHex, hexToBytes } from '@noble/hashes/utils'\n\nexport function normalizeURL(url: string): string {\n  try {\n    if (url.indexOf('://') === -1) url = 'wss://' + url\n    let p = new URL(url)\n    if (p.protocol === 'http:') p.protocol = 'ws:'\n    else if (p.protocol === 'https:') p.protocol = 'wss:'\n    p.pathname = p.pathname.replace(/\\/+/g, '/')\n    if (p.pathname.endsWith('/')) p.pathname = p.pathname.slice(0, -1)\n    if ((p.port === '80' && p.protocol === 'ws:') || (p.port === '443' && p.protocol === 'wss:')) p.port = ''\n    p.searchParams.sort()\n    p.hash = ''\n    return p.toString()\n  } catch (e) {\n    throw new Error(`Invalid URL: ${url}`)\n  }\n}\n\nexport function insertEventIntoDescendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return b.created_at - event.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function insertEventIntoAscendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return event.created_at - b.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function binarySearch<T>(arr: T[], compare: (b: T) => number): [number, boolean] {\n  let start = 0\n  let end = arr.length - 1\n\n  while (start <= end) {\n    const mid = Math.floor((start + end) / 2)\n    const cmp = compare(arr[mid])\n\n    if (cmp === 0) {\n      return [mid, true]\n    }\n\n    if (cmp < 0) {\n      end = mid - 1\n    } else {\n      start = mid + 1\n    }\n  }\n\n  return [start, false]\n}\n\nexport class QueueNode<V> {\n  public value: V\n  public next: QueueNode<V> | null = null\n  public prev: QueueNode<V> | null = null\n\n  constructor(message: V) {\n    this.value = message\n  }\n}\n\nexport class Queue<V> {\n  public first: QueueNode<V> | null\n  public last: QueueNode<V> | null\n\n  constructor() {\n    this.first = null\n    this.last = null\n  }\n\n  enqueue(value: V): boolean {\n    const newNode = new QueueNode(value)\n    if (!this.last) {\n      // list is empty\n      this.first = newNode\n      this.last = newNode\n    } else if (this.last === this.first) {\n      // list has a single element\n      this.last = newNode\n      this.last.prev = this.first\n      this.first.next = newNode\n    } else {\n      // list has elements, add as last\n      newNode.prev = this.last\n      this.last.next = newNode\n      this.last = newNode\n    }\n    return true\n  }\n\n  dequeue(): V | null {\n    if (!this.first) return null\n\n    if (this.first === this.last) {\n      const target = this.first\n      this.first = null\n      this.last = null\n      return target.value\n    }\n\n    const target = this.first\n    this.first = target.next\n    if (this.first) {\n      this.first.prev = null // fix: clean up prev pointer\n    }\n\n    return target.value\n  }\n}\n"],
-  "mappings": ";AAAA,SAAS,eAAe;AACxB,SAAS,cAAAA,mBAAkB;;;ACOpB,IAAM,iBAAiB,OAAO,UAAU;AAsB/C,IAAM,WAAW,CAAC,QAAiD,eAAe;AAE3E,SAAS,cAAiB,OAAsC;AACrE,MAAI,CAAC,SAAS,KAAK;AAAG,WAAO;AAC7B,MAAI,OAAO,MAAM,SAAS;AAAU,WAAO;AAC3C,MAAI,OAAO,MAAM,YAAY;AAAU,WAAO;AAC9C,MAAI,OAAO,MAAM,eAAe;AAAU,WAAO;AACjD,MAAI,OAAO,MAAM,WAAW;AAAU,WAAO;AAC7C,MAAI,CAAC,MAAM,OAAO,MAAM,gBAAgB;AAAG,WAAO;AAElD,MAAI,CAAC,MAAM,QAAQ,MAAM,IAAI;AAAG,WAAO;AACvC,WAASC,KAAI,GAAGA,KAAI,MAAM,KAAK,QAAQA,MAAK;AAC1C,QAAI,MAAM,MAAM,KAAKA;AACrB,QAAI,CAAC,MAAM,QAAQ,GAAG;AAAG,aAAO;AAChC,aAAS,IAAI,GAAG,IAAI,IAAI,QAAQ,KAAK;AACnC,UAAI,OAAO,IAAI,OAAO;AAAU,eAAO;AAAA,IACzC;AAAA,EACF;AAEA,SAAO;AACT;AAOO,SAAS,WAAW,QAA0B;AACnD,SAAO,OAAO,KAAK,CAAC,GAAe,MAA0B;AAC3D,QAAI,EAAE,eAAe,EAAE,YAAY;AACjC,aAAO,EAAE,aAAa,EAAE;AAAA,IAC1B;AACA,WAAO,EAAE,GAAG,cAAc,EAAE,EAAE;AAAA,EAChC,CAAC;AACH;;;AD7DA,SAAS,cAAc;;;AEEvB,SAAS,YAAY,kBAAkB;AAHhC,IAAM,cAA2B,IAAI,YAAY,OAAO;AACxD,IAAM,cAA2B,IAAI,YAAY;;;AFIxD,IAAM,KAAN,MAA0B;AAAA,EACxB,oBAAgC;AAC9B,WAAO,QAAQ,MAAM,iBAAiB;AAAA,EACxC;AAAA,EACA,aAAa,WAA+B;AAC1C,WAAOC,YAAW,QAAQ,aAAa,SAAS,CAAC;AAAA,EACnD;AAAA,EACA,cAAc,GAAkB,WAAsC;AACpE,UAAM,QAAQ;AACd,UAAM,SAASA,YAAW,QAAQ,aAAa,SAAS,CAAC;AACzD,UAAM,KAAK,aAAa,KAAK;AAC7B,UAAM,MAAMA,YAAW,QAAQ,KAAK,aAAa,KAAK,GAAG,SAAS,CAAC;AACnE,UAAM,kBAAkB;AACxB,WAAO;AAAA,EACT;AAAA,EACA,YAAY,OAAsC;AAChD,QAAI,OAAO,MAAM,oBAAoB;AAAW,aAAO,MAAM;AAE7D,UAAM,OAAO,aAAa,KAAK;AAC/B,QAAI,SAAS,MAAM,IAAI;AACrB,YAAM,kBAAkB;AACxB,aAAO;AAAA,IACT;AAEA,QAAI;AACF,YAAM,QAAQ,QAAQ,OAAO,MAAM,KAAK,MAAM,MAAM,MAAM;AAC1D,YAAM,kBAAkB;AACxB,aAAO;AAAA,IACT,SAAS,KAAP;AACA,YAAM,kBAAkB;AACxB,aAAO;AAAA,IACT;AAAA,EACF;AACF;AAEO,SAAS,eAAe,KAA4B;AACzD,MAAI,CAAC,cAAc,GAAG;AAAG,UAAM,IAAI,MAAM,wDAAwD;AACjG,SAAO,KAAK,UAAU,CAAC,GAAG,IAAI,QAAQ,IAAI,YAAY,IAAI,MAAM,IAAI,MAAM,IAAI,OAAO,CAAC;AACxF;AAEO,SAAS,aAAa,OAA8B;AACzD,MAAI,YAAY,OAAO,YAAY,OAAO,eAAe,KAAK,CAAC,CAAC;AAChE,SAAOA,YAAW,SAAS;AAC7B;AAEA,IAAM,IAAQ,IAAI,GAAG;AAEd,IAAM,oBAAoB,EAAE;AAC5B,IAAM,eAAe,EAAE;AACvB,IAAM,gBAAgB,EAAE;AACxB,IAAM,cAAc,EAAE;",
-  "names": ["bytesToHex", "i", "bytesToHex"]
+  "sourcesContent": ["import { schnorr } from '@noble/curves/secp256k1.js'\nimport { bytesToHex, hexToBytes } from '@noble/hashes/utils.js'\nimport { Nostr, Event, EventTemplate, UnsignedEvent, VerifiedEvent, verifiedSymbol, validateEvent } from './core.ts'\nimport { sha256 } from '@noble/hashes/sha2.js'\n\nimport { utf8Encoder } from './utils.ts'\n\nclass JS implements Nostr {\n  generateSecretKey(): Uint8Array {\n    return schnorr.utils.randomSecretKey()\n  }\n  getPublicKey(secretKey: Uint8Array): string {\n    return bytesToHex(schnorr.getPublicKey(secretKey))\n  }\n  finalizeEvent(t: EventTemplate, secretKey: Uint8Array): VerifiedEvent {\n    const event = t as VerifiedEvent\n    event.pubkey = bytesToHex(schnorr.getPublicKey(secretKey))\n    event.id = getEventHash(event)\n    event.sig = bytesToHex(schnorr.sign(hexToBytes(getEventHash(event)), secretKey))\n    event[verifiedSymbol] = true\n    return event\n  }\n  verifyEvent(event: Event): event is VerifiedEvent {\n    if (typeof event[verifiedSymbol] === 'boolean') return event[verifiedSymbol]\n\n    const hash = getEventHash(event)\n    if (hash !== event.id) {\n      event[verifiedSymbol] = false\n      return false\n    }\n\n    try {\n      const valid = schnorr.verify(hexToBytes(event.sig), hexToBytes(hash), hexToBytes(event.pubkey))\n      event[verifiedSymbol] = valid\n      return valid\n    } catch (err) {\n      event[verifiedSymbol] = false\n      return false\n    }\n  }\n}\n\nexport function serializeEvent(evt: UnsignedEvent): string {\n  if (!validateEvent(evt)) throw new Error(\"can't serialize event with wrong or missing properties\")\n  return JSON.stringify([0, evt.pubkey, evt.created_at, evt.kind, evt.tags, evt.content])\n}\n\nexport function getEventHash(event: UnsignedEvent): string {\n  let eventHash = sha256(utf8Encoder.encode(serializeEvent(event)))\n  return bytesToHex(eventHash)\n}\n\nconst i: JS = new JS()\n\nexport const generateSecretKey = i.generateSecretKey\nexport const getPublicKey = i.getPublicKey\nexport const finalizeEvent = i.finalizeEvent\nexport const verifyEvent = i.verifyEvent\nexport * from './core.ts'\n", "export interface Nostr {\n  generateSecretKey(): Uint8Array\n  getPublicKey(secretKey: Uint8Array): string\n  finalizeEvent(event: EventTemplate, secretKey: Uint8Array): VerifiedEvent\n  verifyEvent(event: Event): event is VerifiedEvent\n}\n\n/** Designates a verified event signature. */\nexport const verifiedSymbol = Symbol('verified')\n\nexport type NostrEvent = {\n  kind: number\n  tags: string[][]\n  content: string\n  created_at: number\n  pubkey: string\n  id: string\n  sig: string\n  [verifiedSymbol]?: boolean\n}\n\nexport type Event = NostrEvent\nexport type EventTemplate = Pick<Event, 'kind' | 'tags' | 'content' | 'created_at'>\nexport type UnsignedEvent = Pick<Event, 'kind' | 'tags' | 'content' | 'created_at' | 'pubkey'>\n\n/** An event whose signature has been verified. */\nexport interface VerifiedEvent extends Event {\n  [verifiedSymbol]: true\n}\n\nconst isRecord = (obj: unknown): obj is Record<string, unknown> => obj instanceof Object\n\nexport function validateEvent<T>(event: T): event is T & UnsignedEvent {\n  if (!isRecord(event)) return false\n  if (typeof event.kind !== 'number') return false\n  if (typeof event.content !== 'string') return false\n  if (typeof event.created_at !== 'number') return false\n  if (typeof event.pubkey !== 'string') return false\n  if (!event.pubkey.match(/^[a-f0-9]{64}$/)) return false\n\n  if (!Array.isArray(event.tags)) return false\n  for (let i = 0; i < event.tags.length; i++) {\n    let tag = event.tags[i]\n    if (!Array.isArray(tag)) return false\n    for (let j = 0; j < tag.length; j++) {\n      if (typeof tag[j] !== 'string') return false\n    }\n  }\n\n  return true\n}\n\n/**\n * Sort events in reverse-chronological order by the `created_at` timestamp,\n * and then by the event `id` (lexicographically) in case of ties.\n * This mutates the array.\n */\nexport function sortEvents(events: Event[]): Event[] {\n  return events.sort((a: NostrEvent, b: NostrEvent): number => {\n    if (a.created_at !== b.created_at) {\n      return b.created_at - a.created_at\n    }\n    return a.id.localeCompare(b.id)\n  })\n}\n", "import type { Event } from './core.ts'\n\nexport const utf8Decoder: TextDecoder = new TextDecoder('utf-8')\nexport const utf8Encoder: TextEncoder = new TextEncoder()\n\nexport { bytesToHex, hexToBytes } from '@noble/hashes/utils.js'\n\nexport function normalizeURL(url: string): string {\n  try {\n    if (url.indexOf('://') === -1) url = 'wss://' + url\n    let p = new URL(url)\n    if (p.protocol === 'http:') p.protocol = 'ws:'\n    else if (p.protocol === 'https:') p.protocol = 'wss:'\n    p.pathname = p.pathname.replace(/\\/+/g, '/')\n    if (p.pathname.endsWith('/')) p.pathname = p.pathname.slice(0, -1)\n    if ((p.port === '80' && p.protocol === 'ws:') || (p.port === '443' && p.protocol === 'wss:')) p.port = ''\n    p.searchParams.sort()\n    p.hash = ''\n    return p.toString()\n  } catch (e) {\n    throw new Error(`Invalid URL: ${url}`)\n  }\n}\n\nexport function insertEventIntoDescendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return b.created_at - event.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function insertEventIntoAscendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return event.created_at - b.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function binarySearch<T>(arr: T[], compare: (b: T) => number): [number, boolean] {\n  let start = 0\n  let end = arr.length - 1\n\n  while (start <= end) {\n    const mid = Math.floor((start + end) / 2)\n    const cmp = compare(arr[mid])\n\n    if (cmp === 0) {\n      return [mid, true]\n    }\n\n    if (cmp < 0) {\n      end = mid - 1\n    } else {\n      start = mid + 1\n    }\n  }\n\n  return [start, false]\n}\n\nexport class QueueNode<V> {\n  public value: V\n  public next: QueueNode<V> | null = null\n  public prev: QueueNode<V> | null = null\n\n  constructor(message: V) {\n    this.value = message\n  }\n}\n\nexport class Queue<V> {\n  public first: QueueNode<V> | null\n  public last: QueueNode<V> | null\n\n  constructor() {\n    this.first = null\n    this.last = null\n  }\n\n  enqueue(value: V): boolean {\n    const newNode = new QueueNode(value)\n    if (!this.last) {\n      // list is empty\n      this.first = newNode\n      this.last = newNode\n    } else if (this.last === this.first) {\n      // list has a single element\n      this.last = newNode\n      this.last.prev = this.first\n      this.first.next = newNode\n    } else {\n      // list has elements, add as last\n      newNode.prev = this.last\n      this.last.next = newNode\n      this.last = newNode\n    }\n    return true\n  }\n\n  dequeue(): V | null {\n    if (!this.first) return null\n\n    if (this.first === this.last) {\n      const target = this.first\n      this.first = null\n      this.last = null\n      return target.value\n    }\n\n    const target = this.first\n    this.first = target.next\n    if (this.first) {\n      this.first.prev = null // fix: clean up prev pointer\n    }\n\n    return target.value\n  }\n}\n"],
+  "mappings": "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",
+  "names": ["bytesToHex", "hexToBytes", "i", "bytesToHex", "hexToBytes"]
 }
Index: package/lib/cjs/references.js.map
===================================================================
--- package/lib/cjs/references.js.map
+++ package/lib/cjs/references.js.map
@@ -1,7 +1,7 @@
 {
   "version": 3,
   "sources": ["../../references.ts", "../../nip19.ts", "../../utils.ts"],
-  "sourcesContent": ["import { decode, type AddressPointer, type ProfilePointer, type EventPointer } from './nip19.ts'\n\nimport type { Event } from './core.ts'\n\ntype Reference = {\n  text: string\n  profile?: ProfilePointer\n  event?: EventPointer\n  address?: AddressPointer\n}\n\nconst mentionRegex = /\\bnostr:((note|npub|naddr|nevent|nprofile)1\\w+)\\b|#\\[(\\d+)\\]/g\n\nexport function parseReferences(evt: Event): Reference[] {\n  let references: Reference[] = []\n  for (let ref of evt.content.matchAll(mentionRegex)) {\n    if (ref[2]) {\n      // it's a NIP-27 mention\n      try {\n        let { type, data } = decode(ref[1])\n        switch (type) {\n          case 'npub': {\n            references.push({\n              text: ref[0],\n              profile: { pubkey: data as string, relays: [] },\n            })\n            break\n          }\n          case 'nprofile': {\n            references.push({\n              text: ref[0],\n              profile: data as ProfilePointer,\n            })\n            break\n          }\n          case 'note': {\n            references.push({\n              text: ref[0],\n              event: { id: data as string, relays: [] },\n            })\n            break\n          }\n          case 'nevent': {\n            references.push({\n              text: ref[0],\n              event: data as EventPointer,\n            })\n            break\n          }\n          case 'naddr': {\n            references.push({\n              text: ref[0],\n              address: data as AddressPointer,\n            })\n            break\n          }\n        }\n      } catch (err) {\n        /***/\n      }\n    } else if (ref[3]) {\n      // it's a NIP-10 mention\n      let idx = parseInt(ref[3], 10)\n      let tag = evt.tags[idx]\n      if (!tag) continue\n\n      switch (tag[0]) {\n        case 'p': {\n          references.push({\n            text: ref[0],\n            profile: { pubkey: tag[1], relays: tag[2] ? [tag[2]] : [] },\n          })\n          break\n        }\n        case 'e': {\n          references.push({\n            text: ref[0],\n            event: { id: tag[1], relays: tag[2] ? [tag[2]] : [] },\n          })\n          break\n        }\n        case 'a': {\n          try {\n            let [kind, pubkey, identifier] = tag[1].split(':')\n            references.push({\n              text: ref[0],\n              address: {\n                identifier,\n                pubkey,\n                kind: parseInt(kind, 10),\n                relays: tag[2] ? [tag[2]] : [],\n              },\n            })\n          } catch (err) {\n            /***/\n          }\n          break\n        }\n      }\n    }\n  }\n\n  return references\n}\n", "import { bytesToHex, concatBytes, hexToBytes } from '@noble/hashes/utils'\nimport { bech32 } from '@scure/base'\n\nimport { utf8Decoder, utf8Encoder } from './utils.ts'\n\nexport type NProfile = `nprofile1${string}`\nexport type NEvent = `nevent1${string}`\nexport type NAddr = `naddr1${string}`\nexport type NSec = `nsec1${string}`\nexport type NPub = `npub1${string}`\nexport type Note = `note1${string}`\nexport type Ncryptsec = `ncryptsec1${string}`\n\nexport const NostrTypeGuard = {\n  isNProfile: (value?: string | null): value is NProfile => /^nprofile1[a-z\\d]+$/.test(value || ''),\n  isNEvent: (value?: string | null): value is NEvent => /^nevent1[a-z\\d]+$/.test(value || ''),\n  isNAddr: (value?: string | null): value is NAddr => /^naddr1[a-z\\d]+$/.test(value || ''),\n  isNSec: (value?: string | null): value is NSec => /^nsec1[a-z\\d]{58}$/.test(value || ''),\n  isNPub: (value?: string | null): value is NPub => /^npub1[a-z\\d]{58}$/.test(value || ''),\n  isNote: (value?: string | null): value is Note => /^note1[a-z\\d]+$/.test(value || ''),\n  isNcryptsec: (value?: string | null): value is Ncryptsec => /^ncryptsec1[a-z\\d]+$/.test(value || ''),\n}\n\nexport const Bech32MaxSize = 5000\n\n/**\n * Bech32 regex.\n * @see https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki#bech32\n */\nexport const BECH32_REGEX = /[\\x21-\\x7E]{1,83}1[023456789acdefghjklmnpqrstuvwxyz]{6,}/\n\nfunction integerToUint8Array(number: number) {\n  // Create a Uint8Array with enough space to hold a 32-bit integer (4 bytes).\n  const uint8Array = new Uint8Array(4)\n\n  // Use bitwise operations to extract the bytes.\n  uint8Array[0] = (number >> 24) & 0xff // Most significant byte (MSB)\n  uint8Array[1] = (number >> 16) & 0xff\n  uint8Array[2] = (number >> 8) & 0xff\n  uint8Array[3] = number & 0xff // Least significant byte (LSB)\n\n  return uint8Array\n}\n\nexport type ProfilePointer = {\n  pubkey: string // hex\n  relays?: string[]\n}\n\nexport type EventPointer = {\n  id: string // hex\n  relays?: string[]\n  author?: string\n  kind?: number\n}\n\nexport type AddressPointer = {\n  identifier: string\n  pubkey: string\n  kind: number\n  relays?: string[]\n}\n\nexport function decodeNostrURI(nip19code: string): ReturnType<typeof decode> | { type: 'invalid'; data: null } {\n  try {\n    if (nip19code.startsWith('nostr:')) nip19code = nip19code.substring(6)\n    return decode(nip19code)\n  } catch (_err) {\n    return { type: 'invalid', data: null }\n  }\n}\n\nexport type DecodedNevent = {\n  type: 'nevent'\n  data: EventPointer\n}\n\nexport type DecodedNprofile = {\n  type: 'nprofile'\n  data: ProfilePointer\n}\n\nexport type DecodedNaddr = {\n  type: 'naddr'\n  data: AddressPointer\n}\n\nexport type DecodedNsec = {\n  type: 'nsec'\n  data: Uint8Array\n}\n\nexport type DecodedNpub = {\n  type: 'npub'\n  data: string\n}\n\nexport type DecodedNote = {\n  type: 'note'\n  data: string\n}\n\nexport type DecodedResult = DecodedNevent | DecodedNprofile | DecodedNaddr | DecodedNpub | DecodedNsec | DecodedNote\n\nexport function decode(nip19: NEvent): DecodedNevent\nexport function decode(nip19: NProfile): DecodedNprofile\nexport function decode(nip19: NAddr): DecodedNaddr\nexport function decode(nip19: NSec): DecodedNsec\nexport function decode(nip19: NPub): DecodedNpub\nexport function decode(nip19: Note): DecodedNote\nexport function decode(code: string): DecodedResult\nexport function decode(code: string): DecodedResult {\n  let { prefix, words } = bech32.decode(code, Bech32MaxSize)\n  let data = new Uint8Array(bech32.fromWords(words))\n\n  switch (prefix) {\n    case 'nprofile': {\n      let tlv = parseTLV(data)\n      if (!tlv[0]?.[0]) throw new Error('missing TLV 0 for nprofile')\n      if (tlv[0][0].length !== 32) throw new Error('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      let tlv = parseTLV(data)\n      if (!tlv[0]?.[0]) throw new Error('missing TLV 0 for nevent')\n      if (tlv[0][0].length !== 32) throw new Error('TLV 0 should be 32 bytes')\n      if (tlv[2] && tlv[2][0].length !== 32) throw new Error('TLV 2 should be 32 bytes')\n      if (tlv[3] && tlv[3][0].length !== 4) throw new Error('TLV 3 should be 4 bytes')\n\n      return {\n        type: 'nevent',\n        data: {\n          id: bytesToHex(tlv[0][0]),\n          relays: tlv[1] ? tlv[1].map(d => utf8Decoder.decode(d)) : [],\n          author: tlv[2]?.[0] ? bytesToHex(tlv[2][0]) : undefined,\n          kind: tlv[3]?.[0] ? parseInt(bytesToHex(tlv[3][0]), 16) : undefined,\n        },\n      }\n    }\n\n    case 'naddr': {\n      let tlv = parseTLV(data)\n      if (!tlv[0]?.[0]) throw new Error('missing TLV 0 for naddr')\n      if (!tlv[2]?.[0]) throw new Error('missing TLV 2 for naddr')\n      if (tlv[2][0].length !== 32) throw new Error('TLV 2 should be 32 bytes')\n      if (!tlv[3]?.[0]) throw new Error('missing TLV 3 for naddr')\n      if (tlv[3][0].length !== 4) throw new Error('TLV 3 should be 4 bytes')\n\n      return {\n        type: 'naddr',\n        data: {\n          identifier: utf8Decoder.decode(tlv[0][0]),\n          pubkey: bytesToHex(tlv[2][0]),\n          kind: parseInt(bytesToHex(tlv[3][0]), 16),\n          relays: tlv[1] ? tlv[1].map(d => utf8Decoder.decode(d)) : [],\n        },\n      }\n    }\n\n    case 'nsec':\n      return { type: prefix, data }\n\n    case 'npub':\n    case 'note':\n      return { type: prefix, data: bytesToHex(data) }\n\n    default:\n      throw new Error(`unknown prefix ${prefix}`)\n  }\n}\n\ntype TLV = { [t: number]: Uint8Array[] }\n\nfunction parseTLV(data: Uint8Array): TLV {\n  let result: TLV = {}\n  let rest = data\n  while (rest.length > 0) {\n    let t = rest[0]\n    let l = rest[1]\n    let v = rest.slice(2, 2 + l)\n    rest = rest.slice(2 + l)\n    if (v.length < l) throw new Error(`not enough data to read on TLV ${t}`)\n    result[t] = result[t] || []\n    result[t].push(v)\n  }\n  return result\n}\n\nexport function nsecEncode(key: Uint8Array): NSec {\n  return encodeBytes('nsec', key)\n}\n\nexport function npubEncode(hex: string): NPub {\n  return encodeBytes('npub', hexToBytes(hex))\n}\n\nexport function noteEncode(hex: string): Note {\n  return encodeBytes('note', hexToBytes(hex))\n}\n\nfunction encodeBech32<Prefix extends string>(prefix: Prefix, data: Uint8Array): `${Prefix}1${string}` {\n  let words = bech32.toWords(data)\n  return bech32.encode(prefix, words, Bech32MaxSize) as `${Prefix}1${string}`\n}\n\nexport function encodeBytes<Prefix extends string>(prefix: Prefix, bytes: Uint8Array): `${Prefix}1${string}` {\n  return encodeBech32(prefix, bytes)\n}\n\nexport function nprofileEncode(profile: ProfilePointer): NProfile {\n  let data = encodeTLV({\n    0: [hexToBytes(profile.pubkey)],\n    1: (profile.relays || []).map(url => utf8Encoder.encode(url)),\n  })\n  return encodeBech32('nprofile', data)\n}\n\nexport function neventEncode(event: EventPointer): NEvent {\n  let kindArray\n  if (event.kind !== undefined) {\n    kindArray = integerToUint8Array(event.kind)\n  }\n\n  let data = encodeTLV({\n    0: [hexToBytes(event.id)],\n    1: (event.relays || []).map(url => utf8Encoder.encode(url)),\n    2: event.author ? [hexToBytes(event.author)] : [],\n    3: kindArray ? [new Uint8Array(kindArray)] : [],\n  })\n\n  return encodeBech32('nevent', data)\n}\n\nexport function naddrEncode(addr: AddressPointer): NAddr {\n  let kind = new ArrayBuffer(4)\n  new DataView(kind).setUint32(0, addr.kind, false)\n\n  let data = encodeTLV({\n    0: [utf8Encoder.encode(addr.identifier)],\n    1: (addr.relays || []).map(url => utf8Encoder.encode(url)),\n    2: [hexToBytes(addr.pubkey)],\n    3: [new Uint8Array(kind)],\n  })\n  return encodeBech32('naddr', data)\n}\n\nfunction encodeTLV(tlv: TLV): Uint8Array {\n  let entries: Uint8Array[] = []\n\n  Object.entries(tlv)\n    .reverse()\n    .forEach(([t, vs]) => {\n      vs.forEach(v => {\n        let entry = new Uint8Array(v.length + 2)\n        entry.set([parseInt(t)], 0)\n        entry.set([v.length], 1)\n        entry.set(v, 2)\n        entries.push(entry)\n      })\n    })\n\n  return concatBytes(...entries)\n}\n", "import type { Event } from './core.ts'\n\nexport const utf8Decoder: TextDecoder = new TextDecoder('utf-8')\nexport const utf8Encoder: TextEncoder = new TextEncoder()\n\nexport { bytesToHex, hexToBytes } from '@noble/hashes/utils'\n\nexport function normalizeURL(url: string): string {\n  try {\n    if (url.indexOf('://') === -1) url = 'wss://' + url\n    let p = new URL(url)\n    if (p.protocol === 'http:') p.protocol = 'ws:'\n    else if (p.protocol === 'https:') p.protocol = 'wss:'\n    p.pathname = p.pathname.replace(/\\/+/g, '/')\n    if (p.pathname.endsWith('/')) p.pathname = p.pathname.slice(0, -1)\n    if ((p.port === '80' && p.protocol === 'ws:') || (p.port === '443' && p.protocol === 'wss:')) p.port = ''\n    p.searchParams.sort()\n    p.hash = ''\n    return p.toString()\n  } catch (e) {\n    throw new Error(`Invalid URL: ${url}`)\n  }\n}\n\nexport function insertEventIntoDescendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return b.created_at - event.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function insertEventIntoAscendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return event.created_at - b.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function binarySearch<T>(arr: T[], compare: (b: T) => number): [number, boolean] {\n  let start = 0\n  let end = arr.length - 1\n\n  while (start <= end) {\n    const mid = Math.floor((start + end) / 2)\n    const cmp = compare(arr[mid])\n\n    if (cmp === 0) {\n      return [mid, true]\n    }\n\n    if (cmp < 0) {\n      end = mid - 1\n    } else {\n      start = mid + 1\n    }\n  }\n\n  return [start, false]\n}\n\nexport class QueueNode<V> {\n  public value: V\n  public next: QueueNode<V> | null = null\n  public prev: QueueNode<V> | null = null\n\n  constructor(message: V) {\n    this.value = message\n  }\n}\n\nexport class Queue<V> {\n  public first: QueueNode<V> | null\n  public last: QueueNode<V> | null\n\n  constructor() {\n    this.first = null\n    this.last = null\n  }\n\n  enqueue(value: V): boolean {\n    const newNode = new QueueNode(value)\n    if (!this.last) {\n      // list is empty\n      this.first = newNode\n      this.last = newNode\n    } else if (this.last === this.first) {\n      // list has a single element\n      this.last = newNode\n      this.last.prev = this.first\n      this.first.next = newNode\n    } else {\n      // list has elements, add as last\n      newNode.prev = this.last\n      this.last.next = newNode\n      this.last = newNode\n    }\n    return true\n  }\n\n  dequeue(): V | null {\n    if (!this.first) return null\n\n    if (this.first === this.last) {\n      const target = this.first\n      this.first = null\n      this.last = null\n      return target.value\n    }\n\n    const target = this.first\n    this.first = target.next\n    if (this.first) {\n      this.first.prev = null // fix: clean up prev pointer\n    }\n\n    return target.value\n  }\n}\n"],
-  "mappings": "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",
+  "sourcesContent": ["import { decode, type AddressPointer, type ProfilePointer, type EventPointer } from './nip19.ts'\n\nimport type { Event } from './core.ts'\n\ntype Reference = {\n  text: string\n  profile?: ProfilePointer\n  event?: EventPointer\n  address?: AddressPointer\n}\n\nconst mentionRegex = /\\bnostr:((note|npub|naddr|nevent|nprofile)1\\w+)\\b|#\\[(\\d+)\\]/g\n\nexport function parseReferences(evt: Event): Reference[] {\n  let references: Reference[] = []\n  for (let ref of evt.content.matchAll(mentionRegex)) {\n    if (ref[2]) {\n      // it's a NIP-27 mention\n      try {\n        let { type, data } = decode(ref[1])\n        switch (type) {\n          case 'npub': {\n            references.push({\n              text: ref[0],\n              profile: { pubkey: data as string, relays: [] },\n            })\n            break\n          }\n          case 'nprofile': {\n            references.push({\n              text: ref[0],\n              profile: data as ProfilePointer,\n            })\n            break\n          }\n          case 'note': {\n            references.push({\n              text: ref[0],\n              event: { id: data as string, relays: [] },\n            })\n            break\n          }\n          case 'nevent': {\n            references.push({\n              text: ref[0],\n              event: data as EventPointer,\n            })\n            break\n          }\n          case 'naddr': {\n            references.push({\n              text: ref[0],\n              address: data as AddressPointer,\n            })\n            break\n          }\n        }\n      } catch (err) {\n        /***/\n      }\n    } else if (ref[3]) {\n      // it's a NIP-10 mention\n      let idx = parseInt(ref[3], 10)\n      let tag = evt.tags[idx]\n      if (!tag) continue\n\n      switch (tag[0]) {\n        case 'p': {\n          references.push({\n            text: ref[0],\n            profile: { pubkey: tag[1], relays: tag[2] ? [tag[2]] : [] },\n          })\n          break\n        }\n        case 'e': {\n          references.push({\n            text: ref[0],\n            event: { id: tag[1], relays: tag[2] ? [tag[2]] : [] },\n          })\n          break\n        }\n        case 'a': {\n          try {\n            let [kind, pubkey, identifier] = tag[1].split(':')\n            references.push({\n              text: ref[0],\n              address: {\n                identifier,\n                pubkey,\n                kind: parseInt(kind, 10),\n                relays: tag[2] ? [tag[2]] : [],\n              },\n            })\n          } catch (err) {\n            /***/\n          }\n          break\n        }\n      }\n    }\n  }\n\n  return references\n}\n", "import { bytesToHex, concatBytes, hexToBytes } from '@noble/hashes/utils.js'\nimport { bech32 } from '@scure/base'\n\nimport { utf8Decoder, utf8Encoder } from './utils.ts'\n\nexport type NProfile = `nprofile1${string}`\nexport type NEvent = `nevent1${string}`\nexport type NAddr = `naddr1${string}`\nexport type NSec = `nsec1${string}`\nexport type NPub = `npub1${string}`\nexport type Note = `note1${string}`\nexport type Ncryptsec = `ncryptsec1${string}`\n\nexport const NostrTypeGuard = {\n  isNProfile: (value?: string | null): value is NProfile => /^nprofile1[a-z\\d]+$/.test(value || ''),\n  isNEvent: (value?: string | null): value is NEvent => /^nevent1[a-z\\d]+$/.test(value || ''),\n  isNAddr: (value?: string | null): value is NAddr => /^naddr1[a-z\\d]+$/.test(value || ''),\n  isNSec: (value?: string | null): value is NSec => /^nsec1[a-z\\d]{58}$/.test(value || ''),\n  isNPub: (value?: string | null): value is NPub => /^npub1[a-z\\d]{58}$/.test(value || ''),\n  isNote: (value?: string | null): value is Note => /^note1[a-z\\d]+$/.test(value || ''),\n  isNcryptsec: (value?: string | null): value is Ncryptsec => /^ncryptsec1[a-z\\d]+$/.test(value || ''),\n}\n\nexport const Bech32MaxSize = 5000\n\n/**\n * Bech32 regex.\n * @see https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki#bech32\n */\nexport const BECH32_REGEX = /[\\x21-\\x7E]{1,83}1[023456789acdefghjklmnpqrstuvwxyz]{6,}/\n\nfunction integerToUint8Array(number: number) {\n  // Create a Uint8Array with enough space to hold a 32-bit integer (4 bytes).\n  const uint8Array = new Uint8Array(4)\n\n  // Use bitwise operations to extract the bytes.\n  uint8Array[0] = (number >> 24) & 0xff // Most significant byte (MSB)\n  uint8Array[1] = (number >> 16) & 0xff\n  uint8Array[2] = (number >> 8) & 0xff\n  uint8Array[3] = number & 0xff // Least significant byte (LSB)\n\n  return uint8Array\n}\n\nexport type ProfilePointer = {\n  pubkey: string // hex\n  relays?: string[]\n}\n\nexport type EventPointer = {\n  id: string // hex\n  relays?: string[]\n  author?: string\n  kind?: number\n}\n\nexport type AddressPointer = {\n  identifier: string\n  pubkey: string\n  kind: number\n  relays?: string[]\n}\n\nexport function decodeNostrURI(nip19code: string): ReturnType<typeof decode> | { type: 'invalid'; data: null } {\n  try {\n    if (nip19code.startsWith('nostr:')) nip19code = nip19code.substring(6)\n    return decode(nip19code)\n  } catch (_err) {\n    return { type: 'invalid', data: null }\n  }\n}\n\nexport type DecodedNevent = {\n  type: 'nevent'\n  data: EventPointer\n}\n\nexport type DecodedNprofile = {\n  type: 'nprofile'\n  data: ProfilePointer\n}\n\nexport type DecodedNaddr = {\n  type: 'naddr'\n  data: AddressPointer\n}\n\nexport type DecodedNsec = {\n  type: 'nsec'\n  data: Uint8Array\n}\n\nexport type DecodedNpub = {\n  type: 'npub'\n  data: string\n}\n\nexport type DecodedNote = {\n  type: 'note'\n  data: string\n}\n\nexport type DecodedResult = DecodedNevent | DecodedNprofile | DecodedNaddr | DecodedNpub | DecodedNsec | DecodedNote\n\nexport function decode(nip19: NEvent): DecodedNevent\nexport function decode(nip19: NProfile): DecodedNprofile\nexport function decode(nip19: NAddr): DecodedNaddr\nexport function decode(nip19: NSec): DecodedNsec\nexport function decode(nip19: NPub): DecodedNpub\nexport function decode(nip19: Note): DecodedNote\nexport function decode(code: string): DecodedResult\nexport function decode(code: string): DecodedResult {\n  let { prefix, words } = bech32.decode(code as `${string}1${string}`, Bech32MaxSize)\n  let data = new Uint8Array(bech32.fromWords(words))\n\n  switch (prefix) {\n    case 'nprofile': {\n      let tlv = parseTLV(data)\n      if (!tlv[0]?.[0]) throw new Error('missing TLV 0 for nprofile')\n      if (tlv[0][0].length !== 32) throw new Error('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      let tlv = parseTLV(data)\n      if (!tlv[0]?.[0]) throw new Error('missing TLV 0 for nevent')\n      if (tlv[0][0].length !== 32) throw new Error('TLV 0 should be 32 bytes')\n      if (tlv[2] && tlv[2][0].length !== 32) throw new Error('TLV 2 should be 32 bytes')\n      if (tlv[3] && tlv[3][0].length !== 4) throw new Error('TLV 3 should be 4 bytes')\n\n      return {\n        type: 'nevent',\n        data: {\n          id: bytesToHex(tlv[0][0]),\n          relays: tlv[1] ? tlv[1].map(d => utf8Decoder.decode(d)) : [],\n          author: tlv[2]?.[0] ? bytesToHex(tlv[2][0]) : undefined,\n          kind: tlv[3]?.[0] ? parseInt(bytesToHex(tlv[3][0]), 16) : undefined,\n        },\n      }\n    }\n\n    case 'naddr': {\n      let tlv = parseTLV(data)\n      if (!tlv[0]?.[0]) throw new Error('missing TLV 0 for naddr')\n      if (!tlv[2]?.[0]) throw new Error('missing TLV 2 for naddr')\n      if (tlv[2][0].length !== 32) throw new Error('TLV 2 should be 32 bytes')\n      if (!tlv[3]?.[0]) throw new Error('missing TLV 3 for naddr')\n      if (tlv[3][0].length !== 4) throw new Error('TLV 3 should be 4 bytes')\n\n      return {\n        type: 'naddr',\n        data: {\n          identifier: utf8Decoder.decode(tlv[0][0]),\n          pubkey: bytesToHex(tlv[2][0]),\n          kind: parseInt(bytesToHex(tlv[3][0]), 16),\n          relays: tlv[1] ? tlv[1].map(d => utf8Decoder.decode(d)) : [],\n        },\n      }\n    }\n\n    case 'nsec':\n      return { type: prefix, data }\n\n    case 'npub':\n    case 'note':\n      return { type: prefix, data: bytesToHex(data) }\n\n    default:\n      throw new Error(`unknown prefix ${prefix}`)\n  }\n}\n\ntype TLV = { [t: number]: Uint8Array[] }\n\nfunction parseTLV(data: Uint8Array): TLV {\n  let result: TLV = {}\n  let rest = data\n  while (rest.length > 0) {\n    let t = rest[0]\n    let l = rest[1]\n    let v = rest.slice(2, 2 + l)\n    rest = rest.slice(2 + l)\n    if (v.length < l) throw new Error(`not enough data to read on TLV ${t}`)\n    result[t] = result[t] || []\n    result[t].push(v)\n  }\n  return result\n}\n\nexport function nsecEncode(key: Uint8Array): NSec {\n  return encodeBytes('nsec', key)\n}\n\nexport function npubEncode(hex: string): NPub {\n  return encodeBytes('npub', hexToBytes(hex))\n}\n\nexport function noteEncode(hex: string): Note {\n  return encodeBytes('note', hexToBytes(hex))\n}\n\nfunction encodeBech32<Prefix extends string>(prefix: Prefix, data: Uint8Array): `${Prefix}1${string}` {\n  let words = bech32.toWords(data)\n  return bech32.encode(prefix, words, Bech32MaxSize) as `${Prefix}1${string}`\n}\n\nexport function encodeBytes<Prefix extends string>(prefix: Prefix, bytes: Uint8Array): `${Prefix}1${string}` {\n  return encodeBech32(prefix, bytes)\n}\n\nexport function nprofileEncode(profile: ProfilePointer): NProfile {\n  let data = encodeTLV({\n    0: [hexToBytes(profile.pubkey)],\n    1: (profile.relays || []).map(url => utf8Encoder.encode(url)),\n  })\n  return encodeBech32('nprofile', data)\n}\n\nexport function neventEncode(event: EventPointer): NEvent {\n  let kindArray\n  if (event.kind !== undefined) {\n    kindArray = integerToUint8Array(event.kind)\n  }\n\n  let data = encodeTLV({\n    0: [hexToBytes(event.id)],\n    1: (event.relays || []).map(url => utf8Encoder.encode(url)),\n    2: event.author ? [hexToBytes(event.author)] : [],\n    3: kindArray ? [new Uint8Array(kindArray)] : [],\n  })\n\n  return encodeBech32('nevent', data)\n}\n\nexport function naddrEncode(addr: AddressPointer): NAddr {\n  let kind = new ArrayBuffer(4)\n  new DataView(kind).setUint32(0, addr.kind, false)\n\n  let data = encodeTLV({\n    0: [utf8Encoder.encode(addr.identifier)],\n    1: (addr.relays || []).map(url => utf8Encoder.encode(url)),\n    2: [hexToBytes(addr.pubkey)],\n    3: [new Uint8Array(kind)],\n  })\n  return encodeBech32('naddr', data)\n}\n\nfunction encodeTLV(tlv: TLV): Uint8Array {\n  let entries: Uint8Array[] = []\n\n  Object.entries(tlv)\n    .reverse()\n    .forEach(([t, vs]) => {\n      vs.forEach(v => {\n        let entry = new Uint8Array(v.length + 2)\n        entry.set([parseInt(t)], 0)\n        entry.set([v.length], 1)\n        entry.set(v, 2)\n        entries.push(entry)\n      })\n    })\n\n  return concatBytes(...entries)\n}\n", "import type { Event } from './core.ts'\n\nexport const utf8Decoder: TextDecoder = new TextDecoder('utf-8')\nexport const utf8Encoder: TextEncoder = new TextEncoder()\n\nexport { bytesToHex, hexToBytes } from '@noble/hashes/utils.js'\n\nexport function normalizeURL(url: string): string {\n  try {\n    if (url.indexOf('://') === -1) url = 'wss://' + url\n    let p = new URL(url)\n    if (p.protocol === 'http:') p.protocol = 'ws:'\n    else if (p.protocol === 'https:') p.protocol = 'wss:'\n    p.pathname = p.pathname.replace(/\\/+/g, '/')\n    if (p.pathname.endsWith('/')) p.pathname = p.pathname.slice(0, -1)\n    if ((p.port === '80' && p.protocol === 'ws:') || (p.port === '443' && p.protocol === 'wss:')) p.port = ''\n    p.searchParams.sort()\n    p.hash = ''\n    return p.toString()\n  } catch (e) {\n    throw new Error(`Invalid URL: ${url}`)\n  }\n}\n\nexport function insertEventIntoDescendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return b.created_at - event.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function insertEventIntoAscendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return event.created_at - b.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function binarySearch<T>(arr: T[], compare: (b: T) => number): [number, boolean] {\n  let start = 0\n  let end = arr.length - 1\n\n  while (start <= end) {\n    const mid = Math.floor((start + end) / 2)\n    const cmp = compare(arr[mid])\n\n    if (cmp === 0) {\n      return [mid, true]\n    }\n\n    if (cmp < 0) {\n      end = mid - 1\n    } else {\n      start = mid + 1\n    }\n  }\n\n  return [start, false]\n}\n\nexport class QueueNode<V> {\n  public value: V\n  public next: QueueNode<V> | null = null\n  public prev: QueueNode<V> | null = null\n\n  constructor(message: V) {\n    this.value = message\n  }\n}\n\nexport class Queue<V> {\n  public first: QueueNode<V> | null\n  public last: QueueNode<V> | null\n\n  constructor() {\n    this.first = null\n    this.last = null\n  }\n\n  enqueue(value: V): boolean {\n    const newNode = new QueueNode(value)\n    if (!this.last) {\n      // list is empty\n      this.first = newNode\n      this.last = newNode\n    } else if (this.last === this.first) {\n      // list has a single element\n      this.last = newNode\n      this.last.prev = this.first\n      this.first.next = newNode\n    } else {\n      // list has elements, add as last\n      newNode.prev = this.last\n      this.last.next = newNode\n      this.last = newNode\n    }\n    return true\n  }\n\n  dequeue(): V | null {\n    if (!this.first) return null\n\n    if (this.first === this.last) {\n      const target = this.first\n      this.first = null\n      this.last = null\n      return target.value\n    }\n\n    const target = this.first\n    this.first = target.next\n    if (this.first) {\n      this.first.prev = null // fix: clean up prev pointer\n    }\n\n    return target.value\n  }\n}\n"],
+  "mappings": "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",
   "names": ["import_utils"]
 }
Index: package/lib/esm/references.js.map
===================================================================
--- package/lib/esm/references.js.map
+++ package/lib/esm/references.js.map
@@ -1,7 +1,7 @@
 {
   "version": 3,
   "sources": ["../../nip19.ts", "../../utils.ts", "../../references.ts"],
-  "sourcesContent": ["import { bytesToHex, concatBytes, hexToBytes } from '@noble/hashes/utils'\nimport { bech32 } from '@scure/base'\n\nimport { utf8Decoder, utf8Encoder } from './utils.ts'\n\nexport type NProfile = `nprofile1${string}`\nexport type NEvent = `nevent1${string}`\nexport type NAddr = `naddr1${string}`\nexport type NSec = `nsec1${string}`\nexport type NPub = `npub1${string}`\nexport type Note = `note1${string}`\nexport type Ncryptsec = `ncryptsec1${string}`\n\nexport const NostrTypeGuard = {\n  isNProfile: (value?: string | null): value is NProfile => /^nprofile1[a-z\\d]+$/.test(value || ''),\n  isNEvent: (value?: string | null): value is NEvent => /^nevent1[a-z\\d]+$/.test(value || ''),\n  isNAddr: (value?: string | null): value is NAddr => /^naddr1[a-z\\d]+$/.test(value || ''),\n  isNSec: (value?: string | null): value is NSec => /^nsec1[a-z\\d]{58}$/.test(value || ''),\n  isNPub: (value?: string | null): value is NPub => /^npub1[a-z\\d]{58}$/.test(value || ''),\n  isNote: (value?: string | null): value is Note => /^note1[a-z\\d]+$/.test(value || ''),\n  isNcryptsec: (value?: string | null): value is Ncryptsec => /^ncryptsec1[a-z\\d]+$/.test(value || ''),\n}\n\nexport const Bech32MaxSize = 5000\n\n/**\n * Bech32 regex.\n * @see https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki#bech32\n */\nexport const BECH32_REGEX = /[\\x21-\\x7E]{1,83}1[023456789acdefghjklmnpqrstuvwxyz]{6,}/\n\nfunction integerToUint8Array(number: number) {\n  // Create a Uint8Array with enough space to hold a 32-bit integer (4 bytes).\n  const uint8Array = new Uint8Array(4)\n\n  // Use bitwise operations to extract the bytes.\n  uint8Array[0] = (number >> 24) & 0xff // Most significant byte (MSB)\n  uint8Array[1] = (number >> 16) & 0xff\n  uint8Array[2] = (number >> 8) & 0xff\n  uint8Array[3] = number & 0xff // Least significant byte (LSB)\n\n  return uint8Array\n}\n\nexport type ProfilePointer = {\n  pubkey: string // hex\n  relays?: string[]\n}\n\nexport type EventPointer = {\n  id: string // hex\n  relays?: string[]\n  author?: string\n  kind?: number\n}\n\nexport type AddressPointer = {\n  identifier: string\n  pubkey: string\n  kind: number\n  relays?: string[]\n}\n\nexport function decodeNostrURI(nip19code: string): ReturnType<typeof decode> | { type: 'invalid'; data: null } {\n  try {\n    if (nip19code.startsWith('nostr:')) nip19code = nip19code.substring(6)\n    return decode(nip19code)\n  } catch (_err) {\n    return { type: 'invalid', data: null }\n  }\n}\n\nexport type DecodedNevent = {\n  type: 'nevent'\n  data: EventPointer\n}\n\nexport type DecodedNprofile = {\n  type: 'nprofile'\n  data: ProfilePointer\n}\n\nexport type DecodedNaddr = {\n  type: 'naddr'\n  data: AddressPointer\n}\n\nexport type DecodedNsec = {\n  type: 'nsec'\n  data: Uint8Array\n}\n\nexport type DecodedNpub = {\n  type: 'npub'\n  data: string\n}\n\nexport type DecodedNote = {\n  type: 'note'\n  data: string\n}\n\nexport type DecodedResult = DecodedNevent | DecodedNprofile | DecodedNaddr | DecodedNpub | DecodedNsec | DecodedNote\n\nexport function decode(nip19: NEvent): DecodedNevent\nexport function decode(nip19: NProfile): DecodedNprofile\nexport function decode(nip19: NAddr): DecodedNaddr\nexport function decode(nip19: NSec): DecodedNsec\nexport function decode(nip19: NPub): DecodedNpub\nexport function decode(nip19: Note): DecodedNote\nexport function decode(code: string): DecodedResult\nexport function decode(code: string): DecodedResult {\n  let { prefix, words } = bech32.decode(code, Bech32MaxSize)\n  let data = new Uint8Array(bech32.fromWords(words))\n\n  switch (prefix) {\n    case 'nprofile': {\n      let tlv = parseTLV(data)\n      if (!tlv[0]?.[0]) throw new Error('missing TLV 0 for nprofile')\n      if (tlv[0][0].length !== 32) throw new Error('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      let tlv = parseTLV(data)\n      if (!tlv[0]?.[0]) throw new Error('missing TLV 0 for nevent')\n      if (tlv[0][0].length !== 32) throw new Error('TLV 0 should be 32 bytes')\n      if (tlv[2] && tlv[2][0].length !== 32) throw new Error('TLV 2 should be 32 bytes')\n      if (tlv[3] && tlv[3][0].length !== 4) throw new Error('TLV 3 should be 4 bytes')\n\n      return {\n        type: 'nevent',\n        data: {\n          id: bytesToHex(tlv[0][0]),\n          relays: tlv[1] ? tlv[1].map(d => utf8Decoder.decode(d)) : [],\n          author: tlv[2]?.[0] ? bytesToHex(tlv[2][0]) : undefined,\n          kind: tlv[3]?.[0] ? parseInt(bytesToHex(tlv[3][0]), 16) : undefined,\n        },\n      }\n    }\n\n    case 'naddr': {\n      let tlv = parseTLV(data)\n      if (!tlv[0]?.[0]) throw new Error('missing TLV 0 for naddr')\n      if (!tlv[2]?.[0]) throw new Error('missing TLV 2 for naddr')\n      if (tlv[2][0].length !== 32) throw new Error('TLV 2 should be 32 bytes')\n      if (!tlv[3]?.[0]) throw new Error('missing TLV 3 for naddr')\n      if (tlv[3][0].length !== 4) throw new Error('TLV 3 should be 4 bytes')\n\n      return {\n        type: 'naddr',\n        data: {\n          identifier: utf8Decoder.decode(tlv[0][0]),\n          pubkey: bytesToHex(tlv[2][0]),\n          kind: parseInt(bytesToHex(tlv[3][0]), 16),\n          relays: tlv[1] ? tlv[1].map(d => utf8Decoder.decode(d)) : [],\n        },\n      }\n    }\n\n    case 'nsec':\n      return { type: prefix, data }\n\n    case 'npub':\n    case 'note':\n      return { type: prefix, data: bytesToHex(data) }\n\n    default:\n      throw new Error(`unknown prefix ${prefix}`)\n  }\n}\n\ntype TLV = { [t: number]: Uint8Array[] }\n\nfunction parseTLV(data: Uint8Array): TLV {\n  let result: TLV = {}\n  let rest = data\n  while (rest.length > 0) {\n    let t = rest[0]\n    let l = rest[1]\n    let v = rest.slice(2, 2 + l)\n    rest = rest.slice(2 + l)\n    if (v.length < l) throw new Error(`not enough data to read on TLV ${t}`)\n    result[t] = result[t] || []\n    result[t].push(v)\n  }\n  return result\n}\n\nexport function nsecEncode(key: Uint8Array): NSec {\n  return encodeBytes('nsec', key)\n}\n\nexport function npubEncode(hex: string): NPub {\n  return encodeBytes('npub', hexToBytes(hex))\n}\n\nexport function noteEncode(hex: string): Note {\n  return encodeBytes('note', hexToBytes(hex))\n}\n\nfunction encodeBech32<Prefix extends string>(prefix: Prefix, data: Uint8Array): `${Prefix}1${string}` {\n  let words = bech32.toWords(data)\n  return bech32.encode(prefix, words, Bech32MaxSize) as `${Prefix}1${string}`\n}\n\nexport function encodeBytes<Prefix extends string>(prefix: Prefix, bytes: Uint8Array): `${Prefix}1${string}` {\n  return encodeBech32(prefix, bytes)\n}\n\nexport function nprofileEncode(profile: ProfilePointer): NProfile {\n  let data = encodeTLV({\n    0: [hexToBytes(profile.pubkey)],\n    1: (profile.relays || []).map(url => utf8Encoder.encode(url)),\n  })\n  return encodeBech32('nprofile', data)\n}\n\nexport function neventEncode(event: EventPointer): NEvent {\n  let kindArray\n  if (event.kind !== undefined) {\n    kindArray = integerToUint8Array(event.kind)\n  }\n\n  let data = encodeTLV({\n    0: [hexToBytes(event.id)],\n    1: (event.relays || []).map(url => utf8Encoder.encode(url)),\n    2: event.author ? [hexToBytes(event.author)] : [],\n    3: kindArray ? [new Uint8Array(kindArray)] : [],\n  })\n\n  return encodeBech32('nevent', data)\n}\n\nexport function naddrEncode(addr: AddressPointer): NAddr {\n  let kind = new ArrayBuffer(4)\n  new DataView(kind).setUint32(0, addr.kind, false)\n\n  let data = encodeTLV({\n    0: [utf8Encoder.encode(addr.identifier)],\n    1: (addr.relays || []).map(url => utf8Encoder.encode(url)),\n    2: [hexToBytes(addr.pubkey)],\n    3: [new Uint8Array(kind)],\n  })\n  return encodeBech32('naddr', data)\n}\n\nfunction encodeTLV(tlv: TLV): Uint8Array {\n  let entries: Uint8Array[] = []\n\n  Object.entries(tlv)\n    .reverse()\n    .forEach(([t, vs]) => {\n      vs.forEach(v => {\n        let entry = new Uint8Array(v.length + 2)\n        entry.set([parseInt(t)], 0)\n        entry.set([v.length], 1)\n        entry.set(v, 2)\n        entries.push(entry)\n      })\n    })\n\n  return concatBytes(...entries)\n}\n", "import type { Event } from './core.ts'\n\nexport const utf8Decoder: TextDecoder = new TextDecoder('utf-8')\nexport const utf8Encoder: TextEncoder = new TextEncoder()\n\nexport { bytesToHex, hexToBytes } from '@noble/hashes/utils'\n\nexport function normalizeURL(url: string): string {\n  try {\n    if (url.indexOf('://') === -1) url = 'wss://' + url\n    let p = new URL(url)\n    if (p.protocol === 'http:') p.protocol = 'ws:'\n    else if (p.protocol === 'https:') p.protocol = 'wss:'\n    p.pathname = p.pathname.replace(/\\/+/g, '/')\n    if (p.pathname.endsWith('/')) p.pathname = p.pathname.slice(0, -1)\n    if ((p.port === '80' && p.protocol === 'ws:') || (p.port === '443' && p.protocol === 'wss:')) p.port = ''\n    p.searchParams.sort()\n    p.hash = ''\n    return p.toString()\n  } catch (e) {\n    throw new Error(`Invalid URL: ${url}`)\n  }\n}\n\nexport function insertEventIntoDescendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return b.created_at - event.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function insertEventIntoAscendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return event.created_at - b.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function binarySearch<T>(arr: T[], compare: (b: T) => number): [number, boolean] {\n  let start = 0\n  let end = arr.length - 1\n\n  while (start <= end) {\n    const mid = Math.floor((start + end) / 2)\n    const cmp = compare(arr[mid])\n\n    if (cmp === 0) {\n      return [mid, true]\n    }\n\n    if (cmp < 0) {\n      end = mid - 1\n    } else {\n      start = mid + 1\n    }\n  }\n\n  return [start, false]\n}\n\nexport class QueueNode<V> {\n  public value: V\n  public next: QueueNode<V> | null = null\n  public prev: QueueNode<V> | null = null\n\n  constructor(message: V) {\n    this.value = message\n  }\n}\n\nexport class Queue<V> {\n  public first: QueueNode<V> | null\n  public last: QueueNode<V> | null\n\n  constructor() {\n    this.first = null\n    this.last = null\n  }\n\n  enqueue(value: V): boolean {\n    const newNode = new QueueNode(value)\n    if (!this.last) {\n      // list is empty\n      this.first = newNode\n      this.last = newNode\n    } else if (this.last === this.first) {\n      // list has a single element\n      this.last = newNode\n      this.last.prev = this.first\n      this.first.next = newNode\n    } else {\n      // list has elements, add as last\n      newNode.prev = this.last\n      this.last.next = newNode\n      this.last = newNode\n    }\n    return true\n  }\n\n  dequeue(): V | null {\n    if (!this.first) return null\n\n    if (this.first === this.last) {\n      const target = this.first\n      this.first = null\n      this.last = null\n      return target.value\n    }\n\n    const target = this.first\n    this.first = target.next\n    if (this.first) {\n      this.first.prev = null // fix: clean up prev pointer\n    }\n\n    return target.value\n  }\n}\n", "import { decode, type AddressPointer, type ProfilePointer, type EventPointer } from './nip19.ts'\n\nimport type { Event } from './core.ts'\n\ntype Reference = {\n  text: string\n  profile?: ProfilePointer\n  event?: EventPointer\n  address?: AddressPointer\n}\n\nconst mentionRegex = /\\bnostr:((note|npub|naddr|nevent|nprofile)1\\w+)\\b|#\\[(\\d+)\\]/g\n\nexport function parseReferences(evt: Event): Reference[] {\n  let references: Reference[] = []\n  for (let ref of evt.content.matchAll(mentionRegex)) {\n    if (ref[2]) {\n      // it's a NIP-27 mention\n      try {\n        let { type, data } = decode(ref[1])\n        switch (type) {\n          case 'npub': {\n            references.push({\n              text: ref[0],\n              profile: { pubkey: data as string, relays: [] },\n            })\n            break\n          }\n          case 'nprofile': {\n            references.push({\n              text: ref[0],\n              profile: data as ProfilePointer,\n            })\n            break\n          }\n          case 'note': {\n            references.push({\n              text: ref[0],\n              event: { id: data as string, relays: [] },\n            })\n            break\n          }\n          case 'nevent': {\n            references.push({\n              text: ref[0],\n              event: data as EventPointer,\n            })\n            break\n          }\n          case 'naddr': {\n            references.push({\n              text: ref[0],\n              address: data as AddressPointer,\n            })\n            break\n          }\n        }\n      } catch (err) {\n        /***/\n      }\n    } else if (ref[3]) {\n      // it's a NIP-10 mention\n      let idx = parseInt(ref[3], 10)\n      let tag = evt.tags[idx]\n      if (!tag) continue\n\n      switch (tag[0]) {\n        case 'p': {\n          references.push({\n            text: ref[0],\n            profile: { pubkey: tag[1], relays: tag[2] ? [tag[2]] : [] },\n          })\n          break\n        }\n        case 'e': {\n          references.push({\n            text: ref[0],\n            event: { id: tag[1], relays: tag[2] ? [tag[2]] : [] },\n          })\n          break\n        }\n        case 'a': {\n          try {\n            let [kind, pubkey, identifier] = tag[1].split(':')\n            references.push({\n              text: ref[0],\n              address: {\n                identifier,\n                pubkey,\n                kind: parseInt(kind, 10),\n                relays: tag[2] ? [tag[2]] : [],\n              },\n            })\n          } catch (err) {\n            /***/\n          }\n          break\n        }\n      }\n    }\n  }\n\n  return references\n}\n"],
-  "mappings": "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",
+  "sourcesContent": ["import { bytesToHex, concatBytes, hexToBytes } from '@noble/hashes/utils.js'\nimport { bech32 } from '@scure/base'\n\nimport { utf8Decoder, utf8Encoder } from './utils.ts'\n\nexport type NProfile = `nprofile1${string}`\nexport type NEvent = `nevent1${string}`\nexport type NAddr = `naddr1${string}`\nexport type NSec = `nsec1${string}`\nexport type NPub = `npub1${string}`\nexport type Note = `note1${string}`\nexport type Ncryptsec = `ncryptsec1${string}`\n\nexport const NostrTypeGuard = {\n  isNProfile: (value?: string | null): value is NProfile => /^nprofile1[a-z\\d]+$/.test(value || ''),\n  isNEvent: (value?: string | null): value is NEvent => /^nevent1[a-z\\d]+$/.test(value || ''),\n  isNAddr: (value?: string | null): value is NAddr => /^naddr1[a-z\\d]+$/.test(value || ''),\n  isNSec: (value?: string | null): value is NSec => /^nsec1[a-z\\d]{58}$/.test(value || ''),\n  isNPub: (value?: string | null): value is NPub => /^npub1[a-z\\d]{58}$/.test(value || ''),\n  isNote: (value?: string | null): value is Note => /^note1[a-z\\d]+$/.test(value || ''),\n  isNcryptsec: (value?: string | null): value is Ncryptsec => /^ncryptsec1[a-z\\d]+$/.test(value || ''),\n}\n\nexport const Bech32MaxSize = 5000\n\n/**\n * Bech32 regex.\n * @see https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki#bech32\n */\nexport const BECH32_REGEX = /[\\x21-\\x7E]{1,83}1[023456789acdefghjklmnpqrstuvwxyz]{6,}/\n\nfunction integerToUint8Array(number: number) {\n  // Create a Uint8Array with enough space to hold a 32-bit integer (4 bytes).\n  const uint8Array = new Uint8Array(4)\n\n  // Use bitwise operations to extract the bytes.\n  uint8Array[0] = (number >> 24) & 0xff // Most significant byte (MSB)\n  uint8Array[1] = (number >> 16) & 0xff\n  uint8Array[2] = (number >> 8) & 0xff\n  uint8Array[3] = number & 0xff // Least significant byte (LSB)\n\n  return uint8Array\n}\n\nexport type ProfilePointer = {\n  pubkey: string // hex\n  relays?: string[]\n}\n\nexport type EventPointer = {\n  id: string // hex\n  relays?: string[]\n  author?: string\n  kind?: number\n}\n\nexport type AddressPointer = {\n  identifier: string\n  pubkey: string\n  kind: number\n  relays?: string[]\n}\n\nexport function decodeNostrURI(nip19code: string): ReturnType<typeof decode> | { type: 'invalid'; data: null } {\n  try {\n    if (nip19code.startsWith('nostr:')) nip19code = nip19code.substring(6)\n    return decode(nip19code)\n  } catch (_err) {\n    return { type: 'invalid', data: null }\n  }\n}\n\nexport type DecodedNevent = {\n  type: 'nevent'\n  data: EventPointer\n}\n\nexport type DecodedNprofile = {\n  type: 'nprofile'\n  data: ProfilePointer\n}\n\nexport type DecodedNaddr = {\n  type: 'naddr'\n  data: AddressPointer\n}\n\nexport type DecodedNsec = {\n  type: 'nsec'\n  data: Uint8Array\n}\n\nexport type DecodedNpub = {\n  type: 'npub'\n  data: string\n}\n\nexport type DecodedNote = {\n  type: 'note'\n  data: string\n}\n\nexport type DecodedResult = DecodedNevent | DecodedNprofile | DecodedNaddr | DecodedNpub | DecodedNsec | DecodedNote\n\nexport function decode(nip19: NEvent): DecodedNevent\nexport function decode(nip19: NProfile): DecodedNprofile\nexport function decode(nip19: NAddr): DecodedNaddr\nexport function decode(nip19: NSec): DecodedNsec\nexport function decode(nip19: NPub): DecodedNpub\nexport function decode(nip19: Note): DecodedNote\nexport function decode(code: string): DecodedResult\nexport function decode(code: string): DecodedResult {\n  let { prefix, words } = bech32.decode(code as `${string}1${string}`, Bech32MaxSize)\n  let data = new Uint8Array(bech32.fromWords(words))\n\n  switch (prefix) {\n    case 'nprofile': {\n      let tlv = parseTLV(data)\n      if (!tlv[0]?.[0]) throw new Error('missing TLV 0 for nprofile')\n      if (tlv[0][0].length !== 32) throw new Error('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      let tlv = parseTLV(data)\n      if (!tlv[0]?.[0]) throw new Error('missing TLV 0 for nevent')\n      if (tlv[0][0].length !== 32) throw new Error('TLV 0 should be 32 bytes')\n      if (tlv[2] && tlv[2][0].length !== 32) throw new Error('TLV 2 should be 32 bytes')\n      if (tlv[3] && tlv[3][0].length !== 4) throw new Error('TLV 3 should be 4 bytes')\n\n      return {\n        type: 'nevent',\n        data: {\n          id: bytesToHex(tlv[0][0]),\n          relays: tlv[1] ? tlv[1].map(d => utf8Decoder.decode(d)) : [],\n          author: tlv[2]?.[0] ? bytesToHex(tlv[2][0]) : undefined,\n          kind: tlv[3]?.[0] ? parseInt(bytesToHex(tlv[3][0]), 16) : undefined,\n        },\n      }\n    }\n\n    case 'naddr': {\n      let tlv = parseTLV(data)\n      if (!tlv[0]?.[0]) throw new Error('missing TLV 0 for naddr')\n      if (!tlv[2]?.[0]) throw new Error('missing TLV 2 for naddr')\n      if (tlv[2][0].length !== 32) throw new Error('TLV 2 should be 32 bytes')\n      if (!tlv[3]?.[0]) throw new Error('missing TLV 3 for naddr')\n      if (tlv[3][0].length !== 4) throw new Error('TLV 3 should be 4 bytes')\n\n      return {\n        type: 'naddr',\n        data: {\n          identifier: utf8Decoder.decode(tlv[0][0]),\n          pubkey: bytesToHex(tlv[2][0]),\n          kind: parseInt(bytesToHex(tlv[3][0]), 16),\n          relays: tlv[1] ? tlv[1].map(d => utf8Decoder.decode(d)) : [],\n        },\n      }\n    }\n\n    case 'nsec':\n      return { type: prefix, data }\n\n    case 'npub':\n    case 'note':\n      return { type: prefix, data: bytesToHex(data) }\n\n    default:\n      throw new Error(`unknown prefix ${prefix}`)\n  }\n}\n\ntype TLV = { [t: number]: Uint8Array[] }\n\nfunction parseTLV(data: Uint8Array): TLV {\n  let result: TLV = {}\n  let rest = data\n  while (rest.length > 0) {\n    let t = rest[0]\n    let l = rest[1]\n    let v = rest.slice(2, 2 + l)\n    rest = rest.slice(2 + l)\n    if (v.length < l) throw new Error(`not enough data to read on TLV ${t}`)\n    result[t] = result[t] || []\n    result[t].push(v)\n  }\n  return result\n}\n\nexport function nsecEncode(key: Uint8Array): NSec {\n  return encodeBytes('nsec', key)\n}\n\nexport function npubEncode(hex: string): NPub {\n  return encodeBytes('npub', hexToBytes(hex))\n}\n\nexport function noteEncode(hex: string): Note {\n  return encodeBytes('note', hexToBytes(hex))\n}\n\nfunction encodeBech32<Prefix extends string>(prefix: Prefix, data: Uint8Array): `${Prefix}1${string}` {\n  let words = bech32.toWords(data)\n  return bech32.encode(prefix, words, Bech32MaxSize) as `${Prefix}1${string}`\n}\n\nexport function encodeBytes<Prefix extends string>(prefix: Prefix, bytes: Uint8Array): `${Prefix}1${string}` {\n  return encodeBech32(prefix, bytes)\n}\n\nexport function nprofileEncode(profile: ProfilePointer): NProfile {\n  let data = encodeTLV({\n    0: [hexToBytes(profile.pubkey)],\n    1: (profile.relays || []).map(url => utf8Encoder.encode(url)),\n  })\n  return encodeBech32('nprofile', data)\n}\n\nexport function neventEncode(event: EventPointer): NEvent {\n  let kindArray\n  if (event.kind !== undefined) {\n    kindArray = integerToUint8Array(event.kind)\n  }\n\n  let data = encodeTLV({\n    0: [hexToBytes(event.id)],\n    1: (event.relays || []).map(url => utf8Encoder.encode(url)),\n    2: event.author ? [hexToBytes(event.author)] : [],\n    3: kindArray ? [new Uint8Array(kindArray)] : [],\n  })\n\n  return encodeBech32('nevent', data)\n}\n\nexport function naddrEncode(addr: AddressPointer): NAddr {\n  let kind = new ArrayBuffer(4)\n  new DataView(kind).setUint32(0, addr.kind, false)\n\n  let data = encodeTLV({\n    0: [utf8Encoder.encode(addr.identifier)],\n    1: (addr.relays || []).map(url => utf8Encoder.encode(url)),\n    2: [hexToBytes(addr.pubkey)],\n    3: [new Uint8Array(kind)],\n  })\n  return encodeBech32('naddr', data)\n}\n\nfunction encodeTLV(tlv: TLV): Uint8Array {\n  let entries: Uint8Array[] = []\n\n  Object.entries(tlv)\n    .reverse()\n    .forEach(([t, vs]) => {\n      vs.forEach(v => {\n        let entry = new Uint8Array(v.length + 2)\n        entry.set([parseInt(t)], 0)\n        entry.set([v.length], 1)\n        entry.set(v, 2)\n        entries.push(entry)\n      })\n    })\n\n  return concatBytes(...entries)\n}\n", "import type { Event } from './core.ts'\n\nexport const utf8Decoder: TextDecoder = new TextDecoder('utf-8')\nexport const utf8Encoder: TextEncoder = new TextEncoder()\n\nexport { bytesToHex, hexToBytes } from '@noble/hashes/utils.js'\n\nexport function normalizeURL(url: string): string {\n  try {\n    if (url.indexOf('://') === -1) url = 'wss://' + url\n    let p = new URL(url)\n    if (p.protocol === 'http:') p.protocol = 'ws:'\n    else if (p.protocol === 'https:') p.protocol = 'wss:'\n    p.pathname = p.pathname.replace(/\\/+/g, '/')\n    if (p.pathname.endsWith('/')) p.pathname = p.pathname.slice(0, -1)\n    if ((p.port === '80' && p.protocol === 'ws:') || (p.port === '443' && p.protocol === 'wss:')) p.port = ''\n    p.searchParams.sort()\n    p.hash = ''\n    return p.toString()\n  } catch (e) {\n    throw new Error(`Invalid URL: ${url}`)\n  }\n}\n\nexport function insertEventIntoDescendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return b.created_at - event.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function insertEventIntoAscendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return event.created_at - b.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function binarySearch<T>(arr: T[], compare: (b: T) => number): [number, boolean] {\n  let start = 0\n  let end = arr.length - 1\n\n  while (start <= end) {\n    const mid = Math.floor((start + end) / 2)\n    const cmp = compare(arr[mid])\n\n    if (cmp === 0) {\n      return [mid, true]\n    }\n\n    if (cmp < 0) {\n      end = mid - 1\n    } else {\n      start = mid + 1\n    }\n  }\n\n  return [start, false]\n}\n\nexport class QueueNode<V> {\n  public value: V\n  public next: QueueNode<V> | null = null\n  public prev: QueueNode<V> | null = null\n\n  constructor(message: V) {\n    this.value = message\n  }\n}\n\nexport class Queue<V> {\n  public first: QueueNode<V> | null\n  public last: QueueNode<V> | null\n\n  constructor() {\n    this.first = null\n    this.last = null\n  }\n\n  enqueue(value: V): boolean {\n    const newNode = new QueueNode(value)\n    if (!this.last) {\n      // list is empty\n      this.first = newNode\n      this.last = newNode\n    } else if (this.last === this.first) {\n      // list has a single element\n      this.last = newNode\n      this.last.prev = this.first\n      this.first.next = newNode\n    } else {\n      // list has elements, add as last\n      newNode.prev = this.last\n      this.last.next = newNode\n      this.last = newNode\n    }\n    return true\n  }\n\n  dequeue(): V | null {\n    if (!this.first) return null\n\n    if (this.first === this.last) {\n      const target = this.first\n      this.first = null\n      this.last = null\n      return target.value\n    }\n\n    const target = this.first\n    this.first = target.next\n    if (this.first) {\n      this.first.prev = null // fix: clean up prev pointer\n    }\n\n    return target.value\n  }\n}\n", "import { decode, type AddressPointer, type ProfilePointer, type EventPointer } from './nip19.ts'\n\nimport type { Event } from './core.ts'\n\ntype Reference = {\n  text: string\n  profile?: ProfilePointer\n  event?: EventPointer\n  address?: AddressPointer\n}\n\nconst mentionRegex = /\\bnostr:((note|npub|naddr|nevent|nprofile)1\\w+)\\b|#\\[(\\d+)\\]/g\n\nexport function parseReferences(evt: Event): Reference[] {\n  let references: Reference[] = []\n  for (let ref of evt.content.matchAll(mentionRegex)) {\n    if (ref[2]) {\n      // it's a NIP-27 mention\n      try {\n        let { type, data } = decode(ref[1])\n        switch (type) {\n          case 'npub': {\n            references.push({\n              text: ref[0],\n              profile: { pubkey: data as string, relays: [] },\n            })\n            break\n          }\n          case 'nprofile': {\n            references.push({\n              text: ref[0],\n              profile: data as ProfilePointer,\n            })\n            break\n          }\n          case 'note': {\n            references.push({\n              text: ref[0],\n              event: { id: data as string, relays: [] },\n            })\n            break\n          }\n          case 'nevent': {\n            references.push({\n              text: ref[0],\n              event: data as EventPointer,\n            })\n            break\n          }\n          case 'naddr': {\n            references.push({\n              text: ref[0],\n              address: data as AddressPointer,\n            })\n            break\n          }\n        }\n      } catch (err) {\n        /***/\n      }\n    } else if (ref[3]) {\n      // it's a NIP-10 mention\n      let idx = parseInt(ref[3], 10)\n      let tag = evt.tags[idx]\n      if (!tag) continue\n\n      switch (tag[0]) {\n        case 'p': {\n          references.push({\n            text: ref[0],\n            profile: { pubkey: tag[1], relays: tag[2] ? [tag[2]] : [] },\n          })\n          break\n        }\n        case 'e': {\n          references.push({\n            text: ref[0],\n            event: { id: tag[1], relays: tag[2] ? [tag[2]] : [] },\n          })\n          break\n        }\n        case 'a': {\n          try {\n            let [kind, pubkey, identifier] = tag[1].split(':')\n            references.push({\n              text: ref[0],\n              address: {\n                identifier,\n                pubkey,\n                kind: parseInt(kind, 10),\n                relays: tag[2] ? [tag[2]] : [],\n              },\n            })\n          } catch (err) {\n            /***/\n          }\n          break\n        }\n      }\n    }\n  }\n\n  return references\n}\n"],
+  "mappings": "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",
   "names": ["bytesToHex", "hexToBytes", "bytesToHex"]
 }
Index: package/lib/cjs/relay.js.map
===================================================================
--- package/lib/cjs/relay.js.map
+++ package/lib/cjs/relay.js.map
@@ -1,7 +1,7 @@
 {
   "version": 3,
   "sources": ["../../relay.ts", "../../pure.ts", "../../core.ts", "../../utils.ts", "../../kinds.ts", "../../filter.ts", "../../fakejson.ts", "../../nip42.ts", "../../helpers.ts", "../../abstract-relay.ts"],
-  "sourcesContent": ["/* global WebSocket */\n\nimport { verifyEvent } from './pure.ts'\nimport { AbstractRelay, type AbstractRelayConstructorOptions } from './abstract-relay.ts'\n\nvar _WebSocket: typeof WebSocket\n\ntry {\n  _WebSocket = WebSocket\n} catch {}\n\nexport function useWebSocketImplementation(websocketImplementation: any) {\n  _WebSocket = websocketImplementation\n}\n\nexport class Relay extends AbstractRelay {\n  constructor(url: string, options?: Pick<AbstractRelayConstructorOptions, 'enablePing' | 'enableReconnect'>) {\n    super(url, { verifyEvent, websocketImplementation: _WebSocket, ...options })\n  }\n\n  static async connect(\n    url: string,\n    options?: Pick<AbstractRelayConstructorOptions, 'enablePing' | 'enableReconnect'>,\n  ): Promise<Relay> {\n    const relay = new Relay(url, options)\n    await relay.connect()\n    return relay\n  }\n}\n\nexport type RelayRecord = Record<string, { read: boolean; write: boolean }>\n\nexport * from './abstract-relay.ts'\n", "import { schnorr } from '@noble/curves/secp256k1'\nimport { bytesToHex } from '@noble/hashes/utils'\nimport { Nostr, Event, EventTemplate, UnsignedEvent, VerifiedEvent, verifiedSymbol, validateEvent } from './core.ts'\nimport { sha256 } from '@noble/hashes/sha256'\n\nimport { utf8Encoder } from './utils.ts'\n\nclass JS implements Nostr {\n  generateSecretKey(): Uint8Array {\n    return schnorr.utils.randomPrivateKey()\n  }\n  getPublicKey(secretKey: Uint8Array): string {\n    return bytesToHex(schnorr.getPublicKey(secretKey))\n  }\n  finalizeEvent(t: EventTemplate, secretKey: Uint8Array): VerifiedEvent {\n    const event = t as VerifiedEvent\n    event.pubkey = bytesToHex(schnorr.getPublicKey(secretKey))\n    event.id = getEventHash(event)\n    event.sig = bytesToHex(schnorr.sign(getEventHash(event), secretKey))\n    event[verifiedSymbol] = true\n    return event\n  }\n  verifyEvent(event: Event): event is VerifiedEvent {\n    if (typeof event[verifiedSymbol] === 'boolean') return event[verifiedSymbol]\n\n    const hash = getEventHash(event)\n    if (hash !== event.id) {\n      event[verifiedSymbol] = false\n      return false\n    }\n\n    try {\n      const valid = schnorr.verify(event.sig, hash, event.pubkey)\n      event[verifiedSymbol] = valid\n      return valid\n    } catch (err) {\n      event[verifiedSymbol] = false\n      return false\n    }\n  }\n}\n\nexport function serializeEvent(evt: UnsignedEvent): string {\n  if (!validateEvent(evt)) throw new Error(\"can't serialize event with wrong or missing properties\")\n  return JSON.stringify([0, evt.pubkey, evt.created_at, evt.kind, evt.tags, evt.content])\n}\n\nexport function getEventHash(event: UnsignedEvent): string {\n  let eventHash = sha256(utf8Encoder.encode(serializeEvent(event)))\n  return bytesToHex(eventHash)\n}\n\nconst i: JS = new JS()\n\nexport const generateSecretKey = i.generateSecretKey\nexport const getPublicKey = i.getPublicKey\nexport const finalizeEvent = i.finalizeEvent\nexport const verifyEvent = i.verifyEvent\nexport * from './core.ts'\n", "export interface Nostr {\n  generateSecretKey(): Uint8Array\n  getPublicKey(secretKey: Uint8Array): string\n  finalizeEvent(event: EventTemplate, secretKey: Uint8Array): VerifiedEvent\n  verifyEvent(event: Event): event is VerifiedEvent\n}\n\n/** Designates a verified event signature. */\nexport const verifiedSymbol = Symbol('verified')\n\nexport type NostrEvent = {\n  kind: number\n  tags: string[][]\n  content: string\n  created_at: number\n  pubkey: string\n  id: string\n  sig: string\n  [verifiedSymbol]?: boolean\n}\n\nexport type Event = NostrEvent\nexport type EventTemplate = Pick<Event, 'kind' | 'tags' | 'content' | 'created_at'>\nexport type UnsignedEvent = Pick<Event, 'kind' | 'tags' | 'content' | 'created_at' | 'pubkey'>\n\n/** An event whose signature has been verified. */\nexport interface VerifiedEvent extends Event {\n  [verifiedSymbol]: true\n}\n\nconst isRecord = (obj: unknown): obj is Record<string, unknown> => obj instanceof Object\n\nexport function validateEvent<T>(event: T): event is T & UnsignedEvent {\n  if (!isRecord(event)) return false\n  if (typeof event.kind !== 'number') return false\n  if (typeof event.content !== 'string') return false\n  if (typeof event.created_at !== 'number') return false\n  if (typeof event.pubkey !== 'string') return false\n  if (!event.pubkey.match(/^[a-f0-9]{64}$/)) return false\n\n  if (!Array.isArray(event.tags)) return false\n  for (let i = 0; i < event.tags.length; i++) {\n    let tag = event.tags[i]\n    if (!Array.isArray(tag)) return false\n    for (let j = 0; j < tag.length; j++) {\n      if (typeof tag[j] !== 'string') return false\n    }\n  }\n\n  return true\n}\n\n/**\n * Sort events in reverse-chronological order by the `created_at` timestamp,\n * and then by the event `id` (lexicographically) in case of ties.\n * This mutates the array.\n */\nexport function sortEvents(events: Event[]): Event[] {\n  return events.sort((a: NostrEvent, b: NostrEvent): number => {\n    if (a.created_at !== b.created_at) {\n      return b.created_at - a.created_at\n    }\n    return a.id.localeCompare(b.id)\n  })\n}\n", "import type { Event } from './core.ts'\n\nexport const utf8Decoder: TextDecoder = new TextDecoder('utf-8')\nexport const utf8Encoder: TextEncoder = new TextEncoder()\n\nexport { bytesToHex, hexToBytes } from '@noble/hashes/utils'\n\nexport function normalizeURL(url: string): string {\n  try {\n    if (url.indexOf('://') === -1) url = 'wss://' + url\n    let p = new URL(url)\n    if (p.protocol === 'http:') p.protocol = 'ws:'\n    else if (p.protocol === 'https:') p.protocol = 'wss:'\n    p.pathname = p.pathname.replace(/\\/+/g, '/')\n    if (p.pathname.endsWith('/')) p.pathname = p.pathname.slice(0, -1)\n    if ((p.port === '80' && p.protocol === 'ws:') || (p.port === '443' && p.protocol === 'wss:')) p.port = ''\n    p.searchParams.sort()\n    p.hash = ''\n    return p.toString()\n  } catch (e) {\n    throw new Error(`Invalid URL: ${url}`)\n  }\n}\n\nexport function insertEventIntoDescendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return b.created_at - event.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function insertEventIntoAscendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return event.created_at - b.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function binarySearch<T>(arr: T[], compare: (b: T) => number): [number, boolean] {\n  let start = 0\n  let end = arr.length - 1\n\n  while (start <= end) {\n    const mid = Math.floor((start + end) / 2)\n    const cmp = compare(arr[mid])\n\n    if (cmp === 0) {\n      return [mid, true]\n    }\n\n    if (cmp < 0) {\n      end = mid - 1\n    } else {\n      start = mid + 1\n    }\n  }\n\n  return [start, false]\n}\n\nexport class QueueNode<V> {\n  public value: V\n  public next: QueueNode<V> | null = null\n  public prev: QueueNode<V> | null = null\n\n  constructor(message: V) {\n    this.value = message\n  }\n}\n\nexport class Queue<V> {\n  public first: QueueNode<V> | null\n  public last: QueueNode<V> | null\n\n  constructor() {\n    this.first = null\n    this.last = null\n  }\n\n  enqueue(value: V): boolean {\n    const newNode = new QueueNode(value)\n    if (!this.last) {\n      // list is empty\n      this.first = newNode\n      this.last = newNode\n    } else if (this.last === this.first) {\n      // list has a single element\n      this.last = newNode\n      this.last.prev = this.first\n      this.first.next = newNode\n    } else {\n      // list has elements, add as last\n      newNode.prev = this.last\n      this.last.next = newNode\n      this.last = newNode\n    }\n    return true\n  }\n\n  dequeue(): V | null {\n    if (!this.first) return null\n\n    if (this.first === this.last) {\n      const target = this.first\n      this.first = null\n      this.last = null\n      return target.value\n    }\n\n    const target = this.first\n    this.first = target.next\n    if (this.first) {\n      this.first.prev = null // fix: clean up prev pointer\n    }\n\n    return target.value\n  }\n}\n", "import { NostrEvent, validateEvent } from './pure.ts'\n\n/** Events are **regular**, which means they're all expected to be stored by relays. */\nexport function isRegularKind(kind: number): boolean {\n  return kind < 10000 && kind !== 0 && kind !== 3\n}\n\n/** Events are **replaceable**, which means that, for each combination of `pubkey` and `kind`, only the latest event is expected to (SHOULD) be stored by relays, older versions are expected to be discarded. */\nexport function isReplaceableKind(kind: number): boolean {\n  return kind === 0 || kind === 3 || (10000 <= kind && kind < 20000)\n}\n\n/** Events are **ephemeral**, which means they are not expected to be stored by relays. */\nexport function isEphemeralKind(kind: number): boolean {\n  return 20000 <= kind && kind < 30000\n}\n\n/** Events are **addressable**, which means that, for each combination of `pubkey`, `kind` and the `d` tag, only the latest event is expected to be stored by relays, older versions are expected to be discarded. */\nexport function isAddressableKind(kind: number): boolean {\n  return 30000 <= kind && kind < 40000\n}\n\n/** Classification of the event kind. */\nexport type KindClassification = 'regular' | 'replaceable' | 'ephemeral' | 'parameterized' | 'unknown'\n\n/** Determine the classification of this kind of event if known, or `unknown`. */\nexport function classifyKind(kind: number): KindClassification {\n  if (isRegularKind(kind)) return 'regular'\n  if (isReplaceableKind(kind)) return 'replaceable'\n  if (isEphemeralKind(kind)) return 'ephemeral'\n  if (isAddressableKind(kind)) return 'parameterized'\n  return 'unknown'\n}\n\nexport function isKind<T extends number>(event: unknown, kind: T | Array<T>): event is NostrEvent & { kind: T } {\n  const kindAsArray: number[] = kind instanceof Array ? kind : [kind]\n  return (validateEvent(event) && kindAsArray.includes(event.kind)) || false\n}\n\nexport const Metadata = 0\nexport type Metadata = typeof Metadata\nexport const ShortTextNote = 1\nexport type ShortTextNote = typeof ShortTextNote\nexport const RecommendRelay = 2\nexport type RecommendRelay = typeof RecommendRelay\nexport const Contacts = 3\nexport type Contacts = typeof Contacts\nexport const EncryptedDirectMessage = 4\nexport type EncryptedDirectMessage = typeof EncryptedDirectMessage\nexport const EventDeletion = 5\nexport type EventDeletion = typeof EventDeletion\nexport const Repost = 6\nexport type Repost = typeof Repost\nexport const Reaction = 7\nexport type Reaction = typeof Reaction\nexport const BadgeAward = 8\nexport type BadgeAward = typeof BadgeAward\nexport const ChatMessage = 9\nexport type ChatMessage = typeof ChatMessage\nexport const ForumThread = 11\nexport type ForumThread = typeof ForumThread\nexport const Seal = 13\nexport type Seal = typeof Seal\nexport const PrivateDirectMessage = 14\nexport type PrivateDirectMessage = typeof PrivateDirectMessage\nexport const FileMessage = 15\nexport type FileMessage = typeof FileMessage\nexport const GenericRepost = 16\nexport type GenericRepost = typeof GenericRepost\nexport const Photo = 20\nexport type Photo = typeof Photo\nexport const NormalVideo = 21\nexport type NormalVideo = typeof NormalVideo\nexport const ShortVideo = 22\nexport type ShortVideo = typeof ShortVideo\nexport const ChannelCreation = 40\nexport type ChannelCreation = typeof ChannelCreation\nexport const ChannelMetadata = 41\nexport type ChannelMetadata = typeof ChannelMetadata\nexport const ChannelMessage = 42\nexport type ChannelMessage = typeof ChannelMessage\nexport const ChannelHideMessage = 43\nexport type ChannelHideMessage = typeof ChannelHideMessage\nexport const ChannelMuteUser = 44\nexport type ChannelMuteUser = typeof ChannelMuteUser\nexport const OpenTimestamps = 1040\nexport type OpenTimestamps = typeof OpenTimestamps\nexport const GiftWrap = 1059\nexport type GiftWrap = typeof GiftWrap\nexport const Poll = 1068\nexport type Poll = typeof Poll\nexport const FileMetadata = 1063\nexport type FileMetadata = typeof FileMetadata\nexport const Comment = 1111\nexport type Comment = typeof Comment\nexport const LiveChatMessage = 1311\nexport type LiveChatMessage = typeof LiveChatMessage\nexport const Voice = 1222\nexport type Voice = typeof Voice\nexport const VoiceComment = 1244\nexport type VoiceComment = typeof VoiceComment\nexport const ProblemTracker = 1971\nexport type ProblemTracker = typeof ProblemTracker\nexport const Report = 1984\nexport type Report = typeof Report\nexport const Reporting = 1984\nexport type Reporting = typeof Reporting\nexport const Label = 1985\nexport type Label = typeof Label\nexport const CommunityPostApproval = 4550\nexport type CommunityPostApproval = typeof CommunityPostApproval\nexport const JobRequest = 5999\nexport type JobRequest = typeof JobRequest\nexport const JobResult = 6999\nexport type JobResult = typeof JobResult\nexport const JobFeedback = 7000\nexport type JobFeedback = typeof JobFeedback\nexport const ZapGoal = 9041\nexport type ZapGoal = typeof ZapGoal\nexport const ZapRequest = 9734\nexport type ZapRequest = typeof ZapRequest\nexport const Zap = 9735\nexport type Zap = typeof Zap\nexport const Highlights = 9802\nexport type Highlights = typeof Highlights\nexport const PollResponse = 1018\nexport type PollResponse = typeof PollResponse\nexport const Mutelist = 10000\nexport type Mutelist = typeof Mutelist\nexport const Pinlist = 10001\nexport type Pinlist = typeof Pinlist\nexport const RelayList = 10002\nexport type RelayList = typeof RelayList\nexport const BookmarkList = 10003\nexport type BookmarkList = typeof BookmarkList\nexport const CommunitiesList = 10004\nexport type CommunitiesList = typeof CommunitiesList\nexport const PublicChatsList = 10005\nexport type PublicChatsList = typeof PublicChatsList\nexport const BlockedRelaysList = 10006\nexport type BlockedRelaysList = typeof BlockedRelaysList\nexport const SearchRelaysList = 10007\nexport type SearchRelaysList = typeof SearchRelaysList\nexport const FavoriteRelays = 10012\nexport type FavoriteRelays = typeof FavoriteRelays\nexport const InterestsList = 10015\nexport type InterestsList = typeof InterestsList\nexport const UserEmojiList = 10030\nexport type UserEmojiList = typeof UserEmojiList\nexport const DirectMessageRelaysList = 10050\nexport type DirectMessageRelaysList = typeof DirectMessageRelaysList\nexport const FileServerPreference = 10096\nexport type FileServerPreference = typeof FileServerPreference\nexport const BlossomServerList = 10063\nexport type BlossomServerList = typeof BlossomServerList\nexport const NWCWalletInfo = 13194\nexport type NWCWalletInfo = typeof NWCWalletInfo\nexport const LightningPubRPC = 21000\nexport type LightningPubRPC = typeof LightningPubRPC\nexport const ClientAuth = 22242\nexport type ClientAuth = typeof ClientAuth\nexport const NWCWalletRequest = 23194\nexport type NWCWalletRequest = typeof NWCWalletRequest\nexport const NWCWalletResponse = 23195\nexport type NWCWalletResponse = typeof NWCWalletResponse\nexport const NostrConnect = 24133\nexport type NostrConnect = typeof NostrConnect\nexport const HTTPAuth = 27235\nexport type HTTPAuth = typeof HTTPAuth\nexport const Followsets = 30000\nexport type Followsets = typeof Followsets\nexport const Genericlists = 30001\nexport type Genericlists = typeof Genericlists\nexport const Relaysets = 30002\nexport type Relaysets = typeof Relaysets\nexport const Bookmarksets = 30003\nexport type Bookmarksets = typeof Bookmarksets\nexport const Curationsets = 30004\nexport type Curationsets = typeof Curationsets\nexport const ProfileBadges = 30008\nexport type ProfileBadges = typeof ProfileBadges\nexport const BadgeDefinition = 30009\nexport type BadgeDefinition = typeof BadgeDefinition\nexport const Interestsets = 30015\nexport type Interestsets = typeof Interestsets\nexport const CreateOrUpdateStall = 30017\nexport type CreateOrUpdateStall = typeof CreateOrUpdateStall\nexport const CreateOrUpdateProduct = 30018\nexport type CreateOrUpdateProduct = typeof CreateOrUpdateProduct\nexport const LongFormArticle = 30023\nexport type LongFormArticle = typeof LongFormArticle\nexport const DraftLong = 30024\nexport type DraftLong = typeof DraftLong\nexport const Emojisets = 30030\nexport type Emojisets = typeof Emojisets\nexport const Application = 30078\nexport type Application = typeof Application\nexport const LiveEvent = 30311\nexport type LiveEvent = typeof LiveEvent\nexport const UserStatuses = 30315\nexport type UserStatuses = typeof UserStatuses\nexport const ClassifiedListing = 30402\nexport type ClassifiedListing = typeof ClassifiedListing\nexport const DraftClassifiedListing = 30403\nexport type DraftClassifiedListing = typeof DraftClassifiedListing\nexport const Date = 31922\nexport type Date = typeof Date\nexport const Time = 31923\nexport type Time = typeof Time\nexport const Calendar = 31924\nexport type Calendar = typeof Calendar\nexport const CalendarEventRSVP = 31925\nexport type CalendarEventRSVP = typeof CalendarEventRSVP\nexport const RelayReview = 31987\nexport type RelayReview = typeof RelayReview\nexport const Handlerrecommendation = 31989\nexport type Handlerrecommendation = typeof Handlerrecommendation\nexport const Handlerinformation = 31990\nexport type Handlerinformation = typeof Handlerinformation\nexport const CommunityDefinition = 34550\nexport type CommunityDefinition = typeof CommunityDefinition\nexport const GroupMetadata = 39000\nexport type GroupMetadata = typeof GroupMetadata\n", "import { Event } from './core.ts'\nimport { isAddressableKind, isReplaceableKind } from './kinds.ts'\n\nexport type Filter = {\n  ids?: string[]\n  kinds?: number[]\n  authors?: string[]\n  since?: number\n  until?: number\n  limit?: number\n  search?: string\n  [key: `#${string}`]: string[] | undefined\n}\n\nexport function matchFilter(filter: Filter, event: Event): boolean {\n  if (filter.ids && filter.ids.indexOf(event.id) === -1) {\n    return false\n  }\n  if (filter.kinds && filter.kinds.indexOf(event.kind) === -1) {\n    return false\n  }\n  if (filter.authors && filter.authors.indexOf(event.pubkey) === -1) {\n    return false\n  }\n\n  for (let f in filter) {\n    if (f[0] === '#') {\n      let tagName = f.slice(1)\n      let values = filter[`#${tagName}`]\n      if (values && !event.tags.find(([t, v]) => t === f.slice(1) && values!.indexOf(v) !== -1)) return false\n    }\n  }\n\n  if (filter.since && event.created_at < filter.since) return false\n  if (filter.until && event.created_at > filter.until) return false\n\n  return true\n}\n\nexport function matchFilters(filters: Filter[], event: Event): boolean {\n  for (let i = 0; i < filters.length; i++) {\n    if (matchFilter(filters[i], event)) {\n      return true\n    }\n  }\n  return false\n}\n\nexport function mergeFilters(...filters: Filter[]): Filter {\n  let result: Filter = {}\n  for (let i = 0; i < filters.length; i++) {\n    let filter = filters[i]\n    Object.entries(filter).forEach(([property, values]) => {\n      if (property === 'kinds' || property === 'ids' || property === 'authors' || property[0] === '#') {\n        // @ts-ignore\n        result[property] = result[property] || []\n        // @ts-ignore\n        for (let v = 0; v < values.length; v++) {\n          // @ts-ignore\n          let value = values[v]\n          // @ts-ignore\n          if (!result[property].includes(value)) result[property].push(value)\n        }\n      }\n    })\n\n    if (filter.limit && (!result.limit || filter.limit > result.limit)) result.limit = filter.limit\n    if (filter.until && (!result.until || filter.until > result.until)) result.until = filter.until\n    if (filter.since && (!result.since || filter.since < result.since)) result.since = filter.since\n  }\n\n  return result\n}\n\n/**\n * Calculate the intrinsic limit of a filter.\n * This function returns a positive integer, or `Infinity` if there is no intrinsic limit.\n */\nexport function getFilterLimit(filter: Filter): number {\n  if (filter.ids && !filter.ids.length) return 0\n  if (filter.kinds && !filter.kinds.length) return 0\n  if (filter.authors && !filter.authors.length) return 0\n\n  for (const [key, value] of Object.entries(filter)) {\n    if (key[0] === '#' && Array.isArray(value) && !value.length) return 0\n  }\n\n  return Math.min(\n    // The `limit` property creates an artificial limit.\n    Math.max(0, filter.limit ?? Infinity),\n\n    // There can only be one event per `id`.\n    filter.ids?.length ?? Infinity,\n\n    // Replaceable events are limited by the number of authors and kinds.\n    filter.authors?.length && filter.kinds?.every(kind => isReplaceableKind(kind))\n      ? filter.authors.length * filter.kinds.length\n      : Infinity,\n\n    // Parameterized replaceable events are limited by the number of authors, kinds, and \"d\" tags.\n    filter.authors?.length && filter.kinds?.every(kind => isAddressableKind(kind)) && filter['#d']?.length\n      ? filter.authors.length * filter.kinds.length * filter['#d'].length\n      : Infinity,\n  )\n}\n", "export function getHex64(json: string, field: string): string {\n  let len = field.length + 3\n  let idx = json.indexOf(`\"${field}\":`) + len\n  let s = json.slice(idx).indexOf(`\"`) + idx + 1\n  return json.slice(s, s + 64)\n}\n\nexport function getInt(json: string, field: string): number {\n  let len = field.length\n  let idx = json.indexOf(`\"${field}\":`) + len + 3\n  let sliced = json.slice(idx)\n  let end = Math.min(sliced.indexOf(','), sliced.indexOf('}'))\n  return parseInt(sliced.slice(0, end), 10)\n}\n\nexport function getSubscriptionId(json: string): string | null {\n  let idx = json.slice(0, 22).indexOf(`\"EVENT\"`)\n  if (idx === -1) return null\n\n  let pstart = json.slice(idx + 7 + 1).indexOf(`\"`)\n  if (pstart === -1) return null\n  let start = idx + 7 + 1 + pstart\n\n  let pend = json.slice(start + 1, 80).indexOf(`\"`)\n  if (pend === -1) return null\n  let end = start + 1 + pend\n\n  return json.slice(start + 1, end)\n}\n\nexport function matchEventId(json: string, id: string): boolean {\n  return id === getHex64(json, 'id')\n}\n\nexport function matchEventPubkey(json: string, pubkey: string): boolean {\n  return pubkey === getHex64(json, 'pubkey')\n}\n\nexport function matchEventKind(json: string, kind: number): boolean {\n  return kind === getInt(json, 'kind')\n}\n", "import { EventTemplate } from './core.ts'\nimport { ClientAuth } from './kinds.ts'\n\n/**\n * creates an EventTemplate for an AUTH event to be signed.\n */\nexport function makeAuthEvent(relayURL: string, challenge: string): EventTemplate {\n  return {\n    kind: ClientAuth,\n    created_at: Math.floor(Date.now() / 1000),\n    tags: [\n      ['relay', relayURL],\n      ['challenge', challenge],\n    ],\n    content: '',\n  }\n}\n", "import { verifiedSymbol, type Event, type Nostr, VerifiedEvent } from './core.ts'\n\nexport async function yieldThread() {\n  return new Promise<void>((resolve, reject) => {\n    try {\n      // Check if MessageChannel is available\n      if (typeof MessageChannel !== 'undefined') {\n        const ch = new MessageChannel()\n        const handler = () => {\n          // @ts-ignore (typescript thinks this property should be called `removeListener`, but in fact it's `removeEventListener`)\n          ch.port1.removeEventListener('message', handler)\n          resolve()\n        }\n        // @ts-ignore (typescript thinks this property should be called `addListener`, but in fact it's `addEventListener`)\n        ch.port1.addEventListener('message', handler)\n        ch.port2.postMessage(0)\n        ch.port1.start()\n      } else {\n        if (typeof setImmediate !== 'undefined') {\n          setImmediate(resolve)\n        } else if (typeof setTimeout !== 'undefined') {\n          setTimeout(resolve, 0)\n        } else {\n          // Last resort - resolve immediately\n          resolve()\n        }\n      }\n    } catch (e) {\n      console.error('during yield: ', e)\n      reject(e)\n    }\n  })\n}\n\nexport const alwaysTrue: Nostr['verifyEvent'] = (t: Event): t is VerifiedEvent => {\n  t[verifiedSymbol] = true\n  return true\n}\n", "/* global WebSocket */\n\nimport type { Event, EventTemplate, VerifiedEvent, Nostr, NostrEvent } from './core.ts'\nimport { matchFilters, type Filter } from './filter.ts'\nimport { getHex64, getSubscriptionId } from './fakejson.ts'\nimport { Queue, normalizeURL } from './utils.ts'\nimport { makeAuthEvent } from './nip42.ts'\nimport { yieldThread } from './helpers.ts'\n\ntype RelayWebSocket = WebSocket & {\n  ping?(): void\n  on?(event: 'pong', listener: () => void): any\n}\n\nexport type AbstractRelayConstructorOptions = {\n  verifyEvent: Nostr['verifyEvent']\n  websocketImplementation?: typeof WebSocket\n  enablePing?: boolean\n  enableReconnect?: boolean\n}\n\nexport class SendingOnClosedConnection extends Error {\n  constructor(message: string, relay: string) {\n    super(`Tried to send message '${message} on a closed connection to ${relay}.`)\n    this.name = 'SendingOnClosedConnection'\n  }\n}\n\nexport class AbstractRelay {\n  public readonly url: string\n  private _connected: boolean = false\n\n  public onclose: (() => void) | null = null\n  public onnotice: (msg: string) => void = msg => console.debug(`NOTICE from ${this.url}: ${msg}`)\n  public onauth: undefined | ((evt: EventTemplate) => Promise<VerifiedEvent>)\n\n  public baseEoseTimeout: number = 4400\n  public publishTimeout: number = 4400\n  public pingFrequency: number = 29000\n  public pingTimeout: number = 20000\n  public resubscribeBackoff: number[] = [10000, 10000, 10000, 20000, 20000, 30000, 60000]\n  public openSubs: Map<string, Subscription> = new Map()\n  public enablePing: boolean | undefined\n  public enableReconnect: boolean\n  private reconnectTimeoutHandle: ReturnType<typeof setTimeout> | undefined\n  private pingIntervalHandle: ReturnType<typeof setInterval> | undefined\n  private reconnectAttempts: number = 0\n  private closedIntentionally: boolean = false\n\n  private connectionPromise: Promise<void> | undefined\n  private openCountRequests = new Map<string, CountResolver>()\n  private openEventPublishes = new Map<string, EventPublishResolver>()\n  private ws: RelayWebSocket | undefined\n  private incomingMessageQueue = new Queue<string>()\n  private queueRunning = false\n  private challenge: string | undefined\n  private authPromise: Promise<string> | undefined\n  private serial: number = 0\n  private verifyEvent: Nostr['verifyEvent']\n\n  private _WebSocket: typeof WebSocket\n\n  constructor(url: string, opts: AbstractRelayConstructorOptions) {\n    this.url = normalizeURL(url)\n    this.verifyEvent = opts.verifyEvent\n    this._WebSocket = opts.websocketImplementation || WebSocket\n    this.enablePing = opts.enablePing\n    this.enableReconnect = opts.enableReconnect || false\n  }\n\n  static async connect(\n    url: string,\n    opts: AbstractRelayConstructorOptions & Parameters<AbstractRelay['connect']>[0],\n  ): Promise<AbstractRelay> {\n    const relay = new AbstractRelay(url, opts)\n    await relay.connect(opts)\n    return relay\n  }\n\n  private closeAllSubscriptions(reason: string) {\n    for (let [_, sub] of this.openSubs) {\n      sub.close(reason)\n    }\n    this.openSubs.clear()\n\n    for (let [_, ep] of this.openEventPublishes) {\n      ep.reject(new Error(reason))\n    }\n    this.openEventPublishes.clear()\n\n    for (let [_, cr] of this.openCountRequests) {\n      cr.reject(new Error(reason))\n    }\n    this.openCountRequests.clear()\n  }\n\n  public get connected(): boolean {\n    return this._connected\n  }\n\n  private async reconnect(): Promise<void> {\n    const backoff = this.resubscribeBackoff[Math.min(this.reconnectAttempts, this.resubscribeBackoff.length - 1)]\n    this.reconnectAttempts++\n\n    this.reconnectTimeoutHandle = setTimeout(async () => {\n      try {\n        await this.connect()\n      } catch (err) {\n        // this will be called again through onclose/onerror\n      }\n    }, backoff)\n  }\n\n  private handleHardClose(reason: string) {\n    if (this.pingIntervalHandle) {\n      clearInterval(this.pingIntervalHandle)\n      this.pingIntervalHandle = undefined\n    }\n\n    this._connected = false\n    this.connectionPromise = undefined\n\n    const wasIntentional = this.closedIntentionally\n    this.closedIntentionally = false // reset for next time\n\n    this.onclose?.()\n\n    if (this.enableReconnect && !wasIntentional) {\n      this.reconnect()\n    } else {\n      this.closeAllSubscriptions(reason)\n    }\n  }\n\n  public async connect(opts?: { timeout?: number; abort?: AbortSignal }): Promise<void> {\n    let connectionTimeoutHandle: ReturnType<typeof setTimeout> | undefined\n\n    if (this.connectionPromise) return this.connectionPromise\n\n    this.challenge = undefined\n    this.authPromise = undefined\n    this.connectionPromise = new Promise((resolve, reject) => {\n      if (opts?.timeout) {\n        connectionTimeoutHandle = setTimeout(() => {\n          reject('connection timed out')\n          this.connectionPromise = undefined\n          this.onclose?.()\n          this.closeAllSubscriptions('relay connection timed out')\n        }, opts.timeout)\n      }\n\n      if (opts?.abort) {\n        opts.abort.onabort = reject\n      }\n\n      try {\n        this.ws = new this._WebSocket(this.url)\n      } catch (err) {\n        clearTimeout(connectionTimeoutHandle)\n        reject(err)\n        return\n      }\n\n      this.ws.onopen = () => {\n        if (this.reconnectTimeoutHandle) {\n          clearTimeout(this.reconnectTimeoutHandle)\n          this.reconnectTimeoutHandle = undefined\n        }\n        clearTimeout(connectionTimeoutHandle)\n        this._connected = true\n\n        const isReconnection = this.reconnectAttempts > 0\n        this.reconnectAttempts = 0\n\n        // resubscribe to all open subscriptions\n        for (const sub of this.openSubs.values()) {\n          sub.eosed = false\n          if (isReconnection) {\n            for (let f = 0; f < sub.filters.length; f++) {\n              if (sub.lastEmitted) {\n                sub.filters[f].since = sub.lastEmitted + 1\n              }\n            }\n          }\n          sub.fire()\n        }\n\n        if (this.enablePing) {\n          this.pingIntervalHandle = setInterval(() => this.pingpong(), this.pingFrequency)\n        }\n        resolve()\n      }\n\n      this.ws.onerror = ev => {\n        clearTimeout(connectionTimeoutHandle)\n        reject((ev as any).message || 'websocket error')\n        this.handleHardClose('relay connection errored')\n      }\n\n      this.ws.onclose = ev => {\n        clearTimeout(connectionTimeoutHandle)\n        reject((ev as any).message || 'websocket closed')\n        this.handleHardClose('relay connection closed')\n      }\n\n      this.ws.onmessage = this._onmessage.bind(this)\n    })\n\n    return this.connectionPromise\n  }\n\n  private waitForPingPong() {\n    return new Promise(resolve => {\n      // listen for pong\n      ;(this.ws as any).once('pong', () => resolve(true))\n      // send a ping\n      this.ws!.ping!()\n    })\n  }\n\n  private waitForDummyReq() {\n    return new Promise((resolve, reject) => {\n      if (!this.connectionPromise) return reject(new Error(`no connection to ${this.url}, can't ping`))\n\n      // make a dummy request with expected empty eose reply\n      // [\"REQ\", \"_\", {\"ids\":[\"aaaa...aaaa\"], \"limit\": 0}]\n      try {\n        const sub = this.subscribe(\n          [{ ids: ['aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'], limit: 0 }],\n          {\n            label: 'forced-ping',\n            oneose: () => {\n              resolve(true)\n              sub.close()\n            },\n            onclose() {\n              // if we get a CLOSED it's because the relay is alive\n              resolve(true)\n            },\n            eoseTimeout: this.pingTimeout + 1000,\n          },\n        )\n      } catch (err) {\n        reject(err)\n      }\n    })\n  }\n\n  // nodejs requires this magic here to ensure connections are closed when internet goes off and stuff\n  // in browsers it's done automatically. see https://github.com/nbd-wtf/nostr-tools/issues/491\n  private async pingpong() {\n    // if the websocket is connected\n    if (this.ws?.readyState === 1) {\n      // wait for either a ping-pong reply or a timeout\n      const result = await Promise.any([\n        // browsers don't have ping so use a dummy req\n        this.ws && this.ws.ping && (this.ws as any).once ? this.waitForPingPong() : this.waitForDummyReq(),\n        new Promise(res => setTimeout(() => res(false), this.pingTimeout)),\n      ])\n\n      if (!result) {\n        // pingpong closing socket\n        if (this.ws?.readyState === this._WebSocket.OPEN) {\n          this.ws?.close()\n        }\n      }\n    }\n  }\n\n  private async runQueue() {\n    this.queueRunning = true\n    while (true) {\n      if (false === this.handleNext()) {\n        break\n      }\n      await yieldThread()\n    }\n    this.queueRunning = false\n  }\n\n  private handleNext(): undefined | false {\n    const json = this.incomingMessageQueue.dequeue()\n    if (!json) {\n      return false\n    }\n\n    // shortcut EVENT sub\n    const subid = getSubscriptionId(json)\n    if (subid) {\n      const so = this.openSubs.get(subid as string)\n      if (!so) {\n        // this is an EVENT message, but for a subscription we don't have, so just stop here\n        return\n      }\n\n      // this will be called only when this message is a EVENT message for a subscription we have\n      // we do this before parsing the JSON to not have to do that for duplicate events\n      //   since JSON parsing is slow\n      const id = getHex64(json, 'id')\n      const alreadyHave = so.alreadyHaveEvent?.(id)\n\n      // notify any interested client that the relay has this event\n      // (do this after alreadyHaveEvent() because the client may rely on this to answer that)\n      so.receivedEvent?.(this, id)\n\n      if (alreadyHave) {\n        // if we had already seen this event we can just stop here\n        return\n      }\n    }\n\n    try {\n      let data = JSON.parse(json)\n      // we won't do any checks against the data since all failures (i.e. invalid messages from relays)\n      // will naturally be caught by the encompassing try..catch block\n\n      switch (data[0]) {\n        case 'EVENT': {\n          const so = this.openSubs.get(data[1] as string) as Subscription\n          const event = data[2] as NostrEvent\n          if (this.verifyEvent(event) && matchFilters(so.filters, event)) {\n            so.onevent(event)\n          }\n          if (!so.lastEmitted || so.lastEmitted < event.created_at) so.lastEmitted = event.created_at\n          return\n        }\n        case 'COUNT': {\n          const id: string = data[1]\n          const payload = data[2] as { count: number }\n          const cr = this.openCountRequests.get(id) as CountResolver\n          if (cr) {\n            cr.resolve(payload.count)\n            this.openCountRequests.delete(id)\n          }\n          return\n        }\n        case 'EOSE': {\n          const so = this.openSubs.get(data[1] as string)\n          if (!so) return\n          so.receivedEose()\n          return\n        }\n        case 'OK': {\n          const id: string = data[1]\n          const ok: boolean = data[2]\n          const reason: string = data[3]\n          const ep = this.openEventPublishes.get(id) as EventPublishResolver\n          if (ep) {\n            clearTimeout(ep.timeout)\n            if (ok) ep.resolve(reason)\n            else ep.reject(new Error(reason))\n            this.openEventPublishes.delete(id)\n          }\n          return\n        }\n        case 'CLOSED': {\n          const id: string = data[1]\n          const so = this.openSubs.get(id)\n          if (!so) return\n          so.closed = true\n          so.close(data[2] as string)\n          return\n        }\n        case 'NOTICE': {\n          this.onnotice(data[1] as string)\n          return\n        }\n        case 'AUTH': {\n          this.challenge = data[1] as string\n          if (this.onauth) {\n            this.auth(this.onauth)\n          }\n          return\n        }\n        default: {\n          const so = this.openSubs.get(data[1])\n          so?.oncustom?.(data)\n          return\n        }\n      }\n    } catch (err) {\n      return\n    }\n  }\n\n  public async send(message: string) {\n    if (!this.connectionPromise) throw new SendingOnClosedConnection(message, this.url)\n\n    this.connectionPromise.then(() => {\n      this.ws?.send(message)\n    })\n  }\n\n  public async auth(signAuthEvent: (evt: EventTemplate) => Promise<VerifiedEvent>): Promise<string> {\n    const challenge = this.challenge\n    if (!challenge) throw new Error(\"can't perform auth, no challenge was received\")\n    if (this.authPromise) return this.authPromise\n\n    this.authPromise = new Promise<string>(async (resolve, reject) => {\n      try {\n        let evt = await signAuthEvent(makeAuthEvent(this.url, challenge))\n        let timeout = setTimeout(() => {\n          let ep = this.openEventPublishes.get(evt.id) as EventPublishResolver\n          if (ep) {\n            ep.reject(new Error('auth timed out'))\n            this.openEventPublishes.delete(evt.id)\n          }\n        }, this.publishTimeout)\n        this.openEventPublishes.set(evt.id, { resolve, reject, timeout })\n        this.send('[\"AUTH\",' + JSON.stringify(evt) + ']')\n      } catch (err) {\n        console.warn('subscribe auth function failed:', err)\n      }\n    })\n    return this.authPromise\n  }\n\n  public async publish(event: Event): Promise<string> {\n    const ret = new Promise<string>((resolve, reject) => {\n      const timeout = setTimeout(() => {\n        const ep = this.openEventPublishes.get(event.id) as EventPublishResolver\n        if (ep) {\n          ep.reject(new Error('publish timed out'))\n          this.openEventPublishes.delete(event.id)\n        }\n      }, this.publishTimeout)\n      this.openEventPublishes.set(event.id, { resolve, reject, timeout })\n    })\n    this.send('[\"EVENT\",' + JSON.stringify(event) + ']')\n    return ret\n  }\n\n  public async count(filters: Filter[], params: { id?: string | null }): Promise<number> {\n    this.serial++\n    const id = params?.id || 'count:' + this.serial\n    const ret = new Promise<number>((resolve, reject) => {\n      this.openCountRequests.set(id, { resolve, reject })\n    })\n    this.send('[\"COUNT\",\"' + id + '\",' + JSON.stringify(filters).substring(1))\n    return ret\n  }\n\n  public subscribe(\n    filters: Filter[],\n    params: Partial<SubscriptionParams> & { label?: string; id?: string },\n  ): Subscription {\n    const sub = this.prepareSubscription(filters, params)\n    sub.fire()\n\n    if (params.abort) {\n      params.abort.onabort = () => sub.close(String(params.abort!.reason || '<aborted>'))\n    }\n\n    return sub\n  }\n\n  public prepareSubscription(\n    filters: Filter[],\n    params: Partial<SubscriptionParams> & { label?: string; id?: string },\n  ): Subscription {\n    this.serial++\n    const id = params.id || (params.label ? params.label + ':' : 'sub:') + this.serial\n    const subscription = new Subscription(this, id, filters, params)\n    this.openSubs.set(id, subscription)\n    return subscription\n  }\n\n  public close() {\n    this.closedIntentionally = true\n    if (this.reconnectTimeoutHandle) {\n      clearTimeout(this.reconnectTimeoutHandle)\n      this.reconnectTimeoutHandle = undefined\n    }\n    if (this.pingIntervalHandle) {\n      clearInterval(this.pingIntervalHandle)\n      this.pingIntervalHandle = undefined\n    }\n    this.closeAllSubscriptions('relay connection closed by us')\n    this._connected = false\n    this.onclose?.()\n    if (this.ws?.readyState === this._WebSocket.OPEN) {\n      this.ws?.close()\n    }\n  }\n\n  // this is the function assigned to this.ws.onmessage\n  // it's exposed for testing and debugging purposes\n  public _onmessage(ev: MessageEvent<any>) {\n    this.incomingMessageQueue.enqueue(ev.data as string)\n    if (!this.queueRunning) {\n      this.runQueue()\n    }\n  }\n}\n\nexport class Subscription {\n  public readonly relay: AbstractRelay\n  public readonly id: string\n\n  public lastEmitted: number | undefined\n  public closed: boolean = false\n  public eosed: boolean = false\n  public filters: Filter[]\n  public alreadyHaveEvent: ((id: string) => boolean) | undefined\n  public receivedEvent: ((relay: AbstractRelay, id: string) => void) | undefined\n\n  public onevent: (evt: Event) => void\n  public oneose: (() => void) | undefined\n  public onclose: ((reason: string) => void) | undefined\n\n  // will get any messages that have this subscription id as their second item and are not default standard\n  public oncustom: ((msg: string[]) => void) | undefined\n\n  public eoseTimeout: number\n  private eoseTimeoutHandle: ReturnType<typeof setTimeout> | undefined\n\n  constructor(relay: AbstractRelay, id: string, filters: Filter[], params: SubscriptionParams) {\n    if (filters.length === 0) throw new Error(\"subscription can't be created with zero filters\")\n\n    this.relay = relay\n    this.filters = filters\n    this.id = id\n    this.alreadyHaveEvent = params.alreadyHaveEvent\n    this.receivedEvent = params.receivedEvent\n    this.eoseTimeout = params.eoseTimeout || relay.baseEoseTimeout\n\n    this.oneose = params.oneose\n    this.onclose = params.onclose\n    this.onevent =\n      params.onevent ||\n      (event => {\n        console.warn(\n          `onevent() callback not defined for subscription '${this.id}' in relay ${this.relay.url}. event received:`,\n          event,\n        )\n      })\n  }\n\n  public fire() {\n    this.relay.send('[\"REQ\",\"' + this.id + '\",' + JSON.stringify(this.filters).substring(1))\n\n    // only now we start counting the eoseTimeout\n    this.eoseTimeoutHandle = setTimeout(this.receivedEose.bind(this), this.eoseTimeout)\n  }\n\n  public receivedEose() {\n    if (this.eosed) return\n    clearTimeout(this.eoseTimeoutHandle)\n    this.eosed = true\n    this.oneose?.()\n  }\n\n  public close(reason: string = 'closed by caller') {\n    if (!this.closed && this.relay.connected) {\n      // if the connection was closed by the user calling .close() we will send a CLOSE message\n      // otherwise this._open will be already set to false so we will skip this\n      try {\n        this.relay.send('[\"CLOSE\",' + JSON.stringify(this.id) + ']')\n      } catch (err) {\n        if (err instanceof SendingOnClosedConnection) {\n          /* doesn't matter, it's ok */\n        } else {\n          throw err\n        }\n      }\n      this.closed = true\n    }\n    this.relay.openSubs.delete(this.id)\n    this.onclose?.(reason)\n  }\n}\n\nexport type SubscriptionParams = {\n  onevent?: (evt: Event) => void\n  oneose?: () => void\n  onclose?: (reason: string) => void\n  alreadyHaveEvent?: (id: string) => boolean\n  receivedEvent?: (relay: AbstractRelay, id: string) => void\n  eoseTimeout?: number\n  abort?: AbortSignal\n}\n\nexport type CountResolver = {\n  resolve: (count: number) => void\n  reject: (err: Error) => void\n}\n\nexport type EventPublishResolver = {\n  resolve: (reason: string) => void\n  reject: (err: Error) => void\n  timeout: ReturnType<typeof setTimeout>\n}\n"],
-  "mappings": "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",
+  "sourcesContent": ["/* global WebSocket */\n\nimport { verifyEvent } from './pure.ts'\nimport { AbstractRelay, type AbstractRelayConstructorOptions } from './abstract-relay.ts'\n\nvar _WebSocket: typeof WebSocket\n\ntry {\n  _WebSocket = WebSocket\n} catch {}\n\nexport function useWebSocketImplementation(websocketImplementation: any) {\n  _WebSocket = websocketImplementation\n}\n\nexport class Relay extends AbstractRelay {\n  constructor(url: string, options?: Pick<AbstractRelayConstructorOptions, 'enablePing' | 'enableReconnect'>) {\n    super(url, { verifyEvent, websocketImplementation: _WebSocket, ...options })\n  }\n\n  static async connect(\n    url: string,\n    options?: Pick<AbstractRelayConstructorOptions, 'enablePing' | 'enableReconnect'>,\n  ): Promise<Relay> {\n    const relay = new Relay(url, options)\n    await relay.connect()\n    return relay\n  }\n}\n\nexport type RelayRecord = Record<string, { read: boolean; write: boolean }>\n\nexport * from './abstract-relay.ts'\n", "import { schnorr } from '@noble/curves/secp256k1.js'\nimport { bytesToHex, hexToBytes } from '@noble/hashes/utils.js'\nimport { Nostr, Event, EventTemplate, UnsignedEvent, VerifiedEvent, verifiedSymbol, validateEvent } from './core.ts'\nimport { sha256 } from '@noble/hashes/sha2.js'\n\nimport { utf8Encoder } from './utils.ts'\n\nclass JS implements Nostr {\n  generateSecretKey(): Uint8Array {\n    return schnorr.utils.randomSecretKey()\n  }\n  getPublicKey(secretKey: Uint8Array): string {\n    return bytesToHex(schnorr.getPublicKey(secretKey))\n  }\n  finalizeEvent(t: EventTemplate, secretKey: Uint8Array): VerifiedEvent {\n    const event = t as VerifiedEvent\n    event.pubkey = bytesToHex(schnorr.getPublicKey(secretKey))\n    event.id = getEventHash(event)\n    event.sig = bytesToHex(schnorr.sign(hexToBytes(getEventHash(event)), secretKey))\n    event[verifiedSymbol] = true\n    return event\n  }\n  verifyEvent(event: Event): event is VerifiedEvent {\n    if (typeof event[verifiedSymbol] === 'boolean') return event[verifiedSymbol]\n\n    const hash = getEventHash(event)\n    if (hash !== event.id) {\n      event[verifiedSymbol] = false\n      return false\n    }\n\n    try {\n      const valid = schnorr.verify(hexToBytes(event.sig), hexToBytes(hash), hexToBytes(event.pubkey))\n      event[verifiedSymbol] = valid\n      return valid\n    } catch (err) {\n      event[verifiedSymbol] = false\n      return false\n    }\n  }\n}\n\nexport function serializeEvent(evt: UnsignedEvent): string {\n  if (!validateEvent(evt)) throw new Error(\"can't serialize event with wrong or missing properties\")\n  return JSON.stringify([0, evt.pubkey, evt.created_at, evt.kind, evt.tags, evt.content])\n}\n\nexport function getEventHash(event: UnsignedEvent): string {\n  let eventHash = sha256(utf8Encoder.encode(serializeEvent(event)))\n  return bytesToHex(eventHash)\n}\n\nconst i: JS = new JS()\n\nexport const generateSecretKey = i.generateSecretKey\nexport const getPublicKey = i.getPublicKey\nexport const finalizeEvent = i.finalizeEvent\nexport const verifyEvent = i.verifyEvent\nexport * from './core.ts'\n", "export interface Nostr {\n  generateSecretKey(): Uint8Array\n  getPublicKey(secretKey: Uint8Array): string\n  finalizeEvent(event: EventTemplate, secretKey: Uint8Array): VerifiedEvent\n  verifyEvent(event: Event): event is VerifiedEvent\n}\n\n/** Designates a verified event signature. */\nexport const verifiedSymbol = Symbol('verified')\n\nexport type NostrEvent = {\n  kind: number\n  tags: string[][]\n  content: string\n  created_at: number\n  pubkey: string\n  id: string\n  sig: string\n  [verifiedSymbol]?: boolean\n}\n\nexport type Event = NostrEvent\nexport type EventTemplate = Pick<Event, 'kind' | 'tags' | 'content' | 'created_at'>\nexport type UnsignedEvent = Pick<Event, 'kind' | 'tags' | 'content' | 'created_at' | 'pubkey'>\n\n/** An event whose signature has been verified. */\nexport interface VerifiedEvent extends Event {\n  [verifiedSymbol]: true\n}\n\nconst isRecord = (obj: unknown): obj is Record<string, unknown> => obj instanceof Object\n\nexport function validateEvent<T>(event: T): event is T & UnsignedEvent {\n  if (!isRecord(event)) return false\n  if (typeof event.kind !== 'number') return false\n  if (typeof event.content !== 'string') return false\n  if (typeof event.created_at !== 'number') return false\n  if (typeof event.pubkey !== 'string') return false\n  if (!event.pubkey.match(/^[a-f0-9]{64}$/)) return false\n\n  if (!Array.isArray(event.tags)) return false\n  for (let i = 0; i < event.tags.length; i++) {\n    let tag = event.tags[i]\n    if (!Array.isArray(tag)) return false\n    for (let j = 0; j < tag.length; j++) {\n      if (typeof tag[j] !== 'string') return false\n    }\n  }\n\n  return true\n}\n\n/**\n * Sort events in reverse-chronological order by the `created_at` timestamp,\n * and then by the event `id` (lexicographically) in case of ties.\n * This mutates the array.\n */\nexport function sortEvents(events: Event[]): Event[] {\n  return events.sort((a: NostrEvent, b: NostrEvent): number => {\n    if (a.created_at !== b.created_at) {\n      return b.created_at - a.created_at\n    }\n    return a.id.localeCompare(b.id)\n  })\n}\n", "import type { Event } from './core.ts'\n\nexport const utf8Decoder: TextDecoder = new TextDecoder('utf-8')\nexport const utf8Encoder: TextEncoder = new TextEncoder()\n\nexport { bytesToHex, hexToBytes } from '@noble/hashes/utils.js'\n\nexport function normalizeURL(url: string): string {\n  try {\n    if (url.indexOf('://') === -1) url = 'wss://' + url\n    let p = new URL(url)\n    if (p.protocol === 'http:') p.protocol = 'ws:'\n    else if (p.protocol === 'https:') p.protocol = 'wss:'\n    p.pathname = p.pathname.replace(/\\/+/g, '/')\n    if (p.pathname.endsWith('/')) p.pathname = p.pathname.slice(0, -1)\n    if ((p.port === '80' && p.protocol === 'ws:') || (p.port === '443' && p.protocol === 'wss:')) p.port = ''\n    p.searchParams.sort()\n    p.hash = ''\n    return p.toString()\n  } catch (e) {\n    throw new Error(`Invalid URL: ${url}`)\n  }\n}\n\nexport function insertEventIntoDescendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return b.created_at - event.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function insertEventIntoAscendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return event.created_at - b.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function binarySearch<T>(arr: T[], compare: (b: T) => number): [number, boolean] {\n  let start = 0\n  let end = arr.length - 1\n\n  while (start <= end) {\n    const mid = Math.floor((start + end) / 2)\n    const cmp = compare(arr[mid])\n\n    if (cmp === 0) {\n      return [mid, true]\n    }\n\n    if (cmp < 0) {\n      end = mid - 1\n    } else {\n      start = mid + 1\n    }\n  }\n\n  return [start, false]\n}\n\nexport class QueueNode<V> {\n  public value: V\n  public next: QueueNode<V> | null = null\n  public prev: QueueNode<V> | null = null\n\n  constructor(message: V) {\n    this.value = message\n  }\n}\n\nexport class Queue<V> {\n  public first: QueueNode<V> | null\n  public last: QueueNode<V> | null\n\n  constructor() {\n    this.first = null\n    this.last = null\n  }\n\n  enqueue(value: V): boolean {\n    const newNode = new QueueNode(value)\n    if (!this.last) {\n      // list is empty\n      this.first = newNode\n      this.last = newNode\n    } else if (this.last === this.first) {\n      // list has a single element\n      this.last = newNode\n      this.last.prev = this.first\n      this.first.next = newNode\n    } else {\n      // list has elements, add as last\n      newNode.prev = this.last\n      this.last.next = newNode\n      this.last = newNode\n    }\n    return true\n  }\n\n  dequeue(): V | null {\n    if (!this.first) return null\n\n    if (this.first === this.last) {\n      const target = this.first\n      this.first = null\n      this.last = null\n      return target.value\n    }\n\n    const target = this.first\n    this.first = target.next\n    if (this.first) {\n      this.first.prev = null // fix: clean up prev pointer\n    }\n\n    return target.value\n  }\n}\n", "import { NostrEvent, validateEvent } from './pure.ts'\n\n/** Events are **regular**, which means they're all expected to be stored by relays. */\nexport function isRegularKind(kind: number): boolean {\n  return kind < 10000 && kind !== 0 && kind !== 3\n}\n\n/** Events are **replaceable**, which means that, for each combination of `pubkey` and `kind`, only the latest event is expected to (SHOULD) be stored by relays, older versions are expected to be discarded. */\nexport function isReplaceableKind(kind: number): boolean {\n  return kind === 0 || kind === 3 || (10000 <= kind && kind < 20000)\n}\n\n/** Events are **ephemeral**, which means they are not expected to be stored by relays. */\nexport function isEphemeralKind(kind: number): boolean {\n  return 20000 <= kind && kind < 30000\n}\n\n/** Events are **addressable**, which means that, for each combination of `pubkey`, `kind` and the `d` tag, only the latest event is expected to be stored by relays, older versions are expected to be discarded. */\nexport function isAddressableKind(kind: number): boolean {\n  return 30000 <= kind && kind < 40000\n}\n\n/** Classification of the event kind. */\nexport type KindClassification = 'regular' | 'replaceable' | 'ephemeral' | 'parameterized' | 'unknown'\n\n/** Determine the classification of this kind of event if known, or `unknown`. */\nexport function classifyKind(kind: number): KindClassification {\n  if (isRegularKind(kind)) return 'regular'\n  if (isReplaceableKind(kind)) return 'replaceable'\n  if (isEphemeralKind(kind)) return 'ephemeral'\n  if (isAddressableKind(kind)) return 'parameterized'\n  return 'unknown'\n}\n\nexport function isKind<T extends number>(event: unknown, kind: T | Array<T>): event is NostrEvent & { kind: T } {\n  const kindAsArray: number[] = kind instanceof Array ? kind : [kind]\n  return (validateEvent(event) && kindAsArray.includes(event.kind)) || false\n}\n\nexport const Metadata = 0\nexport type Metadata = typeof Metadata\nexport const ShortTextNote = 1\nexport type ShortTextNote = typeof ShortTextNote\nexport const RecommendRelay = 2\nexport type RecommendRelay = typeof RecommendRelay\nexport const Contacts = 3\nexport type Contacts = typeof Contacts\nexport const EncryptedDirectMessage = 4\nexport type EncryptedDirectMessage = typeof EncryptedDirectMessage\nexport const EventDeletion = 5\nexport type EventDeletion = typeof EventDeletion\nexport const Repost = 6\nexport type Repost = typeof Repost\nexport const Reaction = 7\nexport type Reaction = typeof Reaction\nexport const BadgeAward = 8\nexport type BadgeAward = typeof BadgeAward\nexport const ChatMessage = 9\nexport type ChatMessage = typeof ChatMessage\nexport const ForumThread = 11\nexport type ForumThread = typeof ForumThread\nexport const Seal = 13\nexport type Seal = typeof Seal\nexport const PrivateDirectMessage = 14\nexport type PrivateDirectMessage = typeof PrivateDirectMessage\nexport const FileMessage = 15\nexport type FileMessage = typeof FileMessage\nexport const GenericRepost = 16\nexport type GenericRepost = typeof GenericRepost\nexport const Photo = 20\nexport type Photo = typeof Photo\nexport const NormalVideo = 21\nexport type NormalVideo = typeof NormalVideo\nexport const ShortVideo = 22\nexport type ShortVideo = typeof ShortVideo\nexport const ChannelCreation = 40\nexport type ChannelCreation = typeof ChannelCreation\nexport const ChannelMetadata = 41\nexport type ChannelMetadata = typeof ChannelMetadata\nexport const ChannelMessage = 42\nexport type ChannelMessage = typeof ChannelMessage\nexport const ChannelHideMessage = 43\nexport type ChannelHideMessage = typeof ChannelHideMessage\nexport const ChannelMuteUser = 44\nexport type ChannelMuteUser = typeof ChannelMuteUser\nexport const OpenTimestamps = 1040\nexport type OpenTimestamps = typeof OpenTimestamps\nexport const GiftWrap = 1059\nexport type GiftWrap = typeof GiftWrap\nexport const Poll = 1068\nexport type Poll = typeof Poll\nexport const FileMetadata = 1063\nexport type FileMetadata = typeof FileMetadata\nexport const Comment = 1111\nexport type Comment = typeof Comment\nexport const LiveChatMessage = 1311\nexport type LiveChatMessage = typeof LiveChatMessage\nexport const Voice = 1222\nexport type Voice = typeof Voice\nexport const VoiceComment = 1244\nexport type VoiceComment = typeof VoiceComment\nexport const ProblemTracker = 1971\nexport type ProblemTracker = typeof ProblemTracker\nexport const Report = 1984\nexport type Report = typeof Report\nexport const Reporting = 1984\nexport type Reporting = typeof Reporting\nexport const Label = 1985\nexport type Label = typeof Label\nexport const CommunityPostApproval = 4550\nexport type CommunityPostApproval = typeof CommunityPostApproval\nexport const JobRequest = 5999\nexport type JobRequest = typeof JobRequest\nexport const JobResult = 6999\nexport type JobResult = typeof JobResult\nexport const JobFeedback = 7000\nexport type JobFeedback = typeof JobFeedback\nexport const ZapGoal = 9041\nexport type ZapGoal = typeof ZapGoal\nexport const ZapRequest = 9734\nexport type ZapRequest = typeof ZapRequest\nexport const Zap = 9735\nexport type Zap = typeof Zap\nexport const Highlights = 9802\nexport type Highlights = typeof Highlights\nexport const PollResponse = 1018\nexport type PollResponse = typeof PollResponse\nexport const Mutelist = 10000\nexport type Mutelist = typeof Mutelist\nexport const Pinlist = 10001\nexport type Pinlist = typeof Pinlist\nexport const RelayList = 10002\nexport type RelayList = typeof RelayList\nexport const BookmarkList = 10003\nexport type BookmarkList = typeof BookmarkList\nexport const CommunitiesList = 10004\nexport type CommunitiesList = typeof CommunitiesList\nexport const PublicChatsList = 10005\nexport type PublicChatsList = typeof PublicChatsList\nexport const BlockedRelaysList = 10006\nexport type BlockedRelaysList = typeof BlockedRelaysList\nexport const SearchRelaysList = 10007\nexport type SearchRelaysList = typeof SearchRelaysList\nexport const FavoriteRelays = 10012\nexport type FavoriteRelays = typeof FavoriteRelays\nexport const InterestsList = 10015\nexport type InterestsList = typeof InterestsList\nexport const UserEmojiList = 10030\nexport type UserEmojiList = typeof UserEmojiList\nexport const DirectMessageRelaysList = 10050\nexport type DirectMessageRelaysList = typeof DirectMessageRelaysList\nexport const FileServerPreference = 10096\nexport type FileServerPreference = typeof FileServerPreference\nexport const BlossomServerList = 10063\nexport type BlossomServerList = typeof BlossomServerList\nexport const NWCWalletInfo = 13194\nexport type NWCWalletInfo = typeof NWCWalletInfo\nexport const LightningPubRPC = 21000\nexport type LightningPubRPC = typeof LightningPubRPC\nexport const ClientAuth = 22242\nexport type ClientAuth = typeof ClientAuth\nexport const NWCWalletRequest = 23194\nexport type NWCWalletRequest = typeof NWCWalletRequest\nexport const NWCWalletResponse = 23195\nexport type NWCWalletResponse = typeof NWCWalletResponse\nexport const NostrConnect = 24133\nexport type NostrConnect = typeof NostrConnect\nexport const HTTPAuth = 27235\nexport type HTTPAuth = typeof HTTPAuth\nexport const Followsets = 30000\nexport type Followsets = typeof Followsets\nexport const Genericlists = 30001\nexport type Genericlists = typeof Genericlists\nexport const Relaysets = 30002\nexport type Relaysets = typeof Relaysets\nexport const Bookmarksets = 30003\nexport type Bookmarksets = typeof Bookmarksets\nexport const Curationsets = 30004\nexport type Curationsets = typeof Curationsets\nexport const ProfileBadges = 30008\nexport type ProfileBadges = typeof ProfileBadges\nexport const BadgeDefinition = 30009\nexport type BadgeDefinition = typeof BadgeDefinition\nexport const Interestsets = 30015\nexport type Interestsets = typeof Interestsets\nexport const CreateOrUpdateStall = 30017\nexport type CreateOrUpdateStall = typeof CreateOrUpdateStall\nexport const CreateOrUpdateProduct = 30018\nexport type CreateOrUpdateProduct = typeof CreateOrUpdateProduct\nexport const LongFormArticle = 30023\nexport type LongFormArticle = typeof LongFormArticle\nexport const DraftLong = 30024\nexport type DraftLong = typeof DraftLong\nexport const Emojisets = 30030\nexport type Emojisets = typeof Emojisets\nexport const Application = 30078\nexport type Application = typeof Application\nexport const LiveEvent = 30311\nexport type LiveEvent = typeof LiveEvent\nexport const UserStatuses = 30315\nexport type UserStatuses = typeof UserStatuses\nexport const ClassifiedListing = 30402\nexport type ClassifiedListing = typeof ClassifiedListing\nexport const DraftClassifiedListing = 30403\nexport type DraftClassifiedListing = typeof DraftClassifiedListing\nexport const Date = 31922\nexport type Date = typeof Date\nexport const Time = 31923\nexport type Time = typeof Time\nexport const Calendar = 31924\nexport type Calendar = typeof Calendar\nexport const CalendarEventRSVP = 31925\nexport type CalendarEventRSVP = typeof CalendarEventRSVP\nexport const RelayReview = 31987\nexport type RelayReview = typeof RelayReview\nexport const Handlerrecommendation = 31989\nexport type Handlerrecommendation = typeof Handlerrecommendation\nexport const Handlerinformation = 31990\nexport type Handlerinformation = typeof Handlerinformation\nexport const CommunityDefinition = 34550\nexport type CommunityDefinition = typeof CommunityDefinition\nexport const GroupMetadata = 39000\nexport type GroupMetadata = typeof GroupMetadata\n", "import { Event } from './core.ts'\nimport { isAddressableKind, isReplaceableKind } from './kinds.ts'\n\nexport type Filter = {\n  ids?: string[]\n  kinds?: number[]\n  authors?: string[]\n  since?: number\n  until?: number\n  limit?: number\n  search?: string\n  [key: `#${string}`]: string[] | undefined\n}\n\nexport function matchFilter(filter: Filter, event: Event): boolean {\n  if (filter.ids && filter.ids.indexOf(event.id) === -1) {\n    return false\n  }\n  if (filter.kinds && filter.kinds.indexOf(event.kind) === -1) {\n    return false\n  }\n  if (filter.authors && filter.authors.indexOf(event.pubkey) === -1) {\n    return false\n  }\n\n  for (let f in filter) {\n    if (f[0] === '#') {\n      let tagName = f.slice(1)\n      let values = filter[`#${tagName}`]\n      if (values && !event.tags.find(([t, v]) => t === f.slice(1) && values!.indexOf(v) !== -1)) return false\n    }\n  }\n\n  if (filter.since && event.created_at < filter.since) return false\n  if (filter.until && event.created_at > filter.until) return false\n\n  return true\n}\n\nexport function matchFilters(filters: Filter[], event: Event): boolean {\n  for (let i = 0; i < filters.length; i++) {\n    if (matchFilter(filters[i], event)) {\n      return true\n    }\n  }\n  return false\n}\n\nexport function mergeFilters(...filters: Filter[]): Filter {\n  let result: Filter = {}\n  for (let i = 0; i < filters.length; i++) {\n    let filter = filters[i]\n    Object.entries(filter).forEach(([property, values]) => {\n      if (property === 'kinds' || property === 'ids' || property === 'authors' || property[0] === '#') {\n        // @ts-ignore\n        result[property] = result[property] || []\n        // @ts-ignore\n        for (let v = 0; v < values.length; v++) {\n          // @ts-ignore\n          let value = values[v]\n          // @ts-ignore\n          if (!result[property].includes(value)) result[property].push(value)\n        }\n      }\n    })\n\n    if (filter.limit && (!result.limit || filter.limit > result.limit)) result.limit = filter.limit\n    if (filter.until && (!result.until || filter.until > result.until)) result.until = filter.until\n    if (filter.since && (!result.since || filter.since < result.since)) result.since = filter.since\n  }\n\n  return result\n}\n\n/**\n * Calculate the intrinsic limit of a filter.\n * This function returns a positive integer, or `Infinity` if there is no intrinsic limit.\n */\nexport function getFilterLimit(filter: Filter): number {\n  if (filter.ids && !filter.ids.length) return 0\n  if (filter.kinds && !filter.kinds.length) return 0\n  if (filter.authors && !filter.authors.length) return 0\n\n  for (const [key, value] of Object.entries(filter)) {\n    if (key[0] === '#' && Array.isArray(value) && !value.length) return 0\n  }\n\n  return Math.min(\n    // The `limit` property creates an artificial limit.\n    Math.max(0, filter.limit ?? Infinity),\n\n    // There can only be one event per `id`.\n    filter.ids?.length ?? Infinity,\n\n    // Replaceable events are limited by the number of authors and kinds.\n    filter.authors?.length && filter.kinds?.every(kind => isReplaceableKind(kind))\n      ? filter.authors.length * filter.kinds.length\n      : Infinity,\n\n    // Parameterized replaceable events are limited by the number of authors, kinds, and \"d\" tags.\n    filter.authors?.length && filter.kinds?.every(kind => isAddressableKind(kind)) && filter['#d']?.length\n      ? filter.authors.length * filter.kinds.length * filter['#d'].length\n      : Infinity,\n  )\n}\n", "export function getHex64(json: string, field: string): string {\n  let len = field.length + 3\n  let idx = json.indexOf(`\"${field}\":`) + len\n  let s = json.slice(idx).indexOf(`\"`) + idx + 1\n  return json.slice(s, s + 64)\n}\n\nexport function getInt(json: string, field: string): number {\n  let len = field.length\n  let idx = json.indexOf(`\"${field}\":`) + len + 3\n  let sliced = json.slice(idx)\n  let end = Math.min(sliced.indexOf(','), sliced.indexOf('}'))\n  return parseInt(sliced.slice(0, end), 10)\n}\n\nexport function getSubscriptionId(json: string): string | null {\n  let idx = json.slice(0, 22).indexOf(`\"EVENT\"`)\n  if (idx === -1) return null\n\n  let pstart = json.slice(idx + 7 + 1).indexOf(`\"`)\n  if (pstart === -1) return null\n  let start = idx + 7 + 1 + pstart\n\n  let pend = json.slice(start + 1, 80).indexOf(`\"`)\n  if (pend === -1) return null\n  let end = start + 1 + pend\n\n  return json.slice(start + 1, end)\n}\n\nexport function matchEventId(json: string, id: string): boolean {\n  return id === getHex64(json, 'id')\n}\n\nexport function matchEventPubkey(json: string, pubkey: string): boolean {\n  return pubkey === getHex64(json, 'pubkey')\n}\n\nexport function matchEventKind(json: string, kind: number): boolean {\n  return kind === getInt(json, 'kind')\n}\n", "import { EventTemplate } from './core.ts'\nimport { ClientAuth } from './kinds.ts'\n\n/**\n * creates an EventTemplate for an AUTH event to be signed.\n */\nexport function makeAuthEvent(relayURL: string, challenge: string): EventTemplate {\n  return {\n    kind: ClientAuth,\n    created_at: Math.floor(Date.now() / 1000),\n    tags: [\n      ['relay', relayURL],\n      ['challenge', challenge],\n    ],\n    content: '',\n  }\n}\n", "import { verifiedSymbol, type Event, type Nostr, VerifiedEvent } from './core.ts'\n\nexport async function yieldThread() {\n  return new Promise<void>((resolve, reject) => {\n    try {\n      // Check if MessageChannel is available\n      if (typeof MessageChannel !== 'undefined') {\n        const ch = new MessageChannel()\n        const handler = () => {\n          // @ts-ignore (typescript thinks this property should be called `removeListener`, but in fact it's `removeEventListener`)\n          ch.port1.removeEventListener('message', handler)\n          resolve()\n        }\n        // @ts-ignore (typescript thinks this property should be called `addListener`, but in fact it's `addEventListener`)\n        ch.port1.addEventListener('message', handler)\n        ch.port2.postMessage(0)\n        ch.port1.start()\n      } else {\n        if (typeof setImmediate !== 'undefined') {\n          setImmediate(resolve)\n        } else if (typeof setTimeout !== 'undefined') {\n          setTimeout(resolve, 0)\n        } else {\n          // Last resort - resolve immediately\n          resolve()\n        }\n      }\n    } catch (e) {\n      console.error('during yield: ', e)\n      reject(e)\n    }\n  })\n}\n\nexport const alwaysTrue: Nostr['verifyEvent'] = (t: Event): t is VerifiedEvent => {\n  t[verifiedSymbol] = true\n  return true\n}\n", "/* global WebSocket */\n\nimport type { Event, EventTemplate, VerifiedEvent, Nostr, NostrEvent } from './core.ts'\nimport { matchFilters, type Filter } from './filter.ts'\nimport { getHex64, getSubscriptionId } from './fakejson.ts'\nimport { Queue, normalizeURL } from './utils.ts'\nimport { makeAuthEvent } from './nip42.ts'\nimport { yieldThread } from './helpers.ts'\n\ntype RelayWebSocket = WebSocket & {\n  ping?(): void\n  on?(event: 'pong', listener: () => void): any\n}\n\nexport type AbstractRelayConstructorOptions = {\n  verifyEvent: Nostr['verifyEvent']\n  websocketImplementation?: typeof WebSocket\n  enablePing?: boolean\n  enableReconnect?: boolean\n}\n\nexport class SendingOnClosedConnection extends Error {\n  constructor(message: string, relay: string) {\n    super(`Tried to send message '${message} on a closed connection to ${relay}.`)\n    this.name = 'SendingOnClosedConnection'\n  }\n}\n\nexport class AbstractRelay {\n  public readonly url: string\n  private _connected: boolean = false\n\n  public onclose: (() => void) | null = null\n  public onnotice: (msg: string) => void = msg => console.debug(`NOTICE from ${this.url}: ${msg}`)\n  public onauth: undefined | ((evt: EventTemplate) => Promise<VerifiedEvent>)\n\n  public baseEoseTimeout: number = 4400\n  public publishTimeout: number = 4400\n  public pingFrequency: number = 29000\n  public pingTimeout: number = 20000\n  public resubscribeBackoff: number[] = [10000, 10000, 10000, 20000, 20000, 30000, 60000]\n  public openSubs: Map<string, Subscription> = new Map()\n  public enablePing: boolean | undefined\n  public enableReconnect: boolean\n  private reconnectTimeoutHandle: ReturnType<typeof setTimeout> | undefined\n  private pingIntervalHandle: ReturnType<typeof setInterval> | undefined\n  private reconnectAttempts: number = 0\n  private closedIntentionally: boolean = false\n\n  private connectionPromise: Promise<void> | undefined\n  private openCountRequests = new Map<string, CountResolver>()\n  private openEventPublishes = new Map<string, EventPublishResolver>()\n  private ws: RelayWebSocket | undefined\n  private incomingMessageQueue = new Queue<string>()\n  private queueRunning = false\n  private challenge: string | undefined\n  private authPromise: Promise<string> | undefined\n  private serial: number = 0\n  private verifyEvent: Nostr['verifyEvent']\n\n  private _WebSocket: typeof WebSocket\n\n  constructor(url: string, opts: AbstractRelayConstructorOptions) {\n    this.url = normalizeURL(url)\n    this.verifyEvent = opts.verifyEvent\n    this._WebSocket = opts.websocketImplementation || WebSocket\n    this.enablePing = opts.enablePing\n    this.enableReconnect = opts.enableReconnect || false\n  }\n\n  static async connect(\n    url: string,\n    opts: AbstractRelayConstructorOptions & Parameters<AbstractRelay['connect']>[0],\n  ): Promise<AbstractRelay> {\n    const relay = new AbstractRelay(url, opts)\n    await relay.connect(opts)\n    return relay\n  }\n\n  private closeAllSubscriptions(reason: string) {\n    for (let [_, sub] of this.openSubs) {\n      sub.close(reason)\n    }\n    this.openSubs.clear()\n\n    for (let [_, ep] of this.openEventPublishes) {\n      ep.reject(new Error(reason))\n    }\n    this.openEventPublishes.clear()\n\n    for (let [_, cr] of this.openCountRequests) {\n      cr.reject(new Error(reason))\n    }\n    this.openCountRequests.clear()\n  }\n\n  public get connected(): boolean {\n    return this._connected\n  }\n\n  private async reconnect(): Promise<void> {\n    const backoff = this.resubscribeBackoff[Math.min(this.reconnectAttempts, this.resubscribeBackoff.length - 1)]\n    this.reconnectAttempts++\n\n    this.reconnectTimeoutHandle = setTimeout(async () => {\n      try {\n        await this.connect()\n      } catch (err) {\n        // this will be called again through onclose/onerror\n      }\n    }, backoff)\n  }\n\n  private handleHardClose(reason: string) {\n    if (this.pingIntervalHandle) {\n      clearInterval(this.pingIntervalHandle)\n      this.pingIntervalHandle = undefined\n    }\n\n    this._connected = false\n    this.connectionPromise = undefined\n\n    const wasIntentional = this.closedIntentionally\n    this.closedIntentionally = false // reset for next time\n\n    this.onclose?.()\n\n    if (this.enableReconnect && !wasIntentional) {\n      this.reconnect()\n    } else {\n      this.closeAllSubscriptions(reason)\n    }\n  }\n\n  public async connect(opts?: { timeout?: number; abort?: AbortSignal }): Promise<void> {\n    let connectionTimeoutHandle: ReturnType<typeof setTimeout> | undefined\n\n    if (this.connectionPromise) return this.connectionPromise\n\n    this.challenge = undefined\n    this.authPromise = undefined\n    this.connectionPromise = new Promise((resolve, reject) => {\n      if (opts?.timeout) {\n        connectionTimeoutHandle = setTimeout(() => {\n          reject('connection timed out')\n          this.connectionPromise = undefined\n          this.onclose?.()\n          this.closeAllSubscriptions('relay connection timed out')\n        }, opts.timeout)\n      }\n\n      if (opts?.abort) {\n        opts.abort.onabort = reject\n      }\n\n      try {\n        this.ws = new this._WebSocket(this.url)\n      } catch (err) {\n        clearTimeout(connectionTimeoutHandle)\n        reject(err)\n        return\n      }\n\n      this.ws.onopen = () => {\n        if (this.reconnectTimeoutHandle) {\n          clearTimeout(this.reconnectTimeoutHandle)\n          this.reconnectTimeoutHandle = undefined\n        }\n        clearTimeout(connectionTimeoutHandle)\n        this._connected = true\n\n        const isReconnection = this.reconnectAttempts > 0\n        this.reconnectAttempts = 0\n\n        // resubscribe to all open subscriptions\n        for (const sub of this.openSubs.values()) {\n          sub.eosed = false\n          if (isReconnection) {\n            for (let f = 0; f < sub.filters.length; f++) {\n              if (sub.lastEmitted) {\n                sub.filters[f].since = sub.lastEmitted + 1\n              }\n            }\n          }\n          sub.fire()\n        }\n\n        if (this.enablePing) {\n          this.pingIntervalHandle = setInterval(() => this.pingpong(), this.pingFrequency)\n        }\n        resolve()\n      }\n\n      this.ws.onerror = ev => {\n        clearTimeout(connectionTimeoutHandle)\n        reject((ev as any).message || 'websocket error')\n        this.handleHardClose('relay connection errored')\n      }\n\n      this.ws.onclose = ev => {\n        clearTimeout(connectionTimeoutHandle)\n        reject((ev as any).message || 'websocket closed')\n        this.handleHardClose('relay connection closed')\n      }\n\n      this.ws.onmessage = this._onmessage.bind(this)\n    })\n\n    return this.connectionPromise\n  }\n\n  private waitForPingPong() {\n    return new Promise(resolve => {\n      // listen for pong\n      ;(this.ws as any).once('pong', () => resolve(true))\n      // send a ping\n      this.ws!.ping!()\n    })\n  }\n\n  private waitForDummyReq() {\n    return new Promise((resolve, reject) => {\n      if (!this.connectionPromise) return reject(new Error(`no connection to ${this.url}, can't ping`))\n\n      // make a dummy request with expected empty eose reply\n      // [\"REQ\", \"_\", {\"ids\":[\"aaaa...aaaa\"], \"limit\": 0}]\n      try {\n        const sub = this.subscribe(\n          [{ ids: ['aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'], limit: 0 }],\n          {\n            label: 'forced-ping',\n            oneose: () => {\n              resolve(true)\n              sub.close()\n            },\n            onclose() {\n              // if we get a CLOSED it's because the relay is alive\n              resolve(true)\n            },\n            eoseTimeout: this.pingTimeout + 1000,\n          },\n        )\n      } catch (err) {\n        reject(err)\n      }\n    })\n  }\n\n  // nodejs requires this magic here to ensure connections are closed when internet goes off and stuff\n  // in browsers it's done automatically. see https://github.com/nbd-wtf/nostr-tools/issues/491\n  private async pingpong() {\n    // if the websocket is connected\n    if (this.ws?.readyState === 1) {\n      // wait for either a ping-pong reply or a timeout\n      const result = await Promise.any([\n        // browsers don't have ping so use a dummy req\n        this.ws && this.ws.ping && (this.ws as any).once ? this.waitForPingPong() : this.waitForDummyReq(),\n        new Promise(res => setTimeout(() => res(false), this.pingTimeout)),\n      ])\n\n      if (!result) {\n        // pingpong closing socket\n        if (this.ws?.readyState === this._WebSocket.OPEN) {\n          this.ws?.close()\n        }\n      }\n    }\n  }\n\n  private async runQueue() {\n    this.queueRunning = true\n    while (true) {\n      if (false === this.handleNext()) {\n        break\n      }\n      await yieldThread()\n    }\n    this.queueRunning = false\n  }\n\n  private handleNext(): undefined | false {\n    const json = this.incomingMessageQueue.dequeue()\n    if (!json) {\n      return false\n    }\n\n    // shortcut EVENT sub\n    const subid = getSubscriptionId(json)\n    if (subid) {\n      const so = this.openSubs.get(subid as string)\n      if (!so) {\n        // this is an EVENT message, but for a subscription we don't have, so just stop here\n        return\n      }\n\n      // this will be called only when this message is a EVENT message for a subscription we have\n      // we do this before parsing the JSON to not have to do that for duplicate events\n      //   since JSON parsing is slow\n      const id = getHex64(json, 'id')\n      const alreadyHave = so.alreadyHaveEvent?.(id)\n\n      // notify any interested client that the relay has this event\n      // (do this after alreadyHaveEvent() because the client may rely on this to answer that)\n      so.receivedEvent?.(this, id)\n\n      if (alreadyHave) {\n        // if we had already seen this event we can just stop here\n        return\n      }\n    }\n\n    try {\n      let data = JSON.parse(json)\n      // we won't do any checks against the data since all failures (i.e. invalid messages from relays)\n      // will naturally be caught by the encompassing try..catch block\n\n      switch (data[0]) {\n        case 'EVENT': {\n          const so = this.openSubs.get(data[1] as string) as Subscription\n          const event = data[2] as NostrEvent\n          if (this.verifyEvent(event) && matchFilters(so.filters, event)) {\n            so.onevent(event)\n          }\n          if (!so.lastEmitted || so.lastEmitted < event.created_at) so.lastEmitted = event.created_at\n          return\n        }\n        case 'COUNT': {\n          const id: string = data[1]\n          const payload = data[2] as { count: number }\n          const cr = this.openCountRequests.get(id) as CountResolver\n          if (cr) {\n            cr.resolve(payload.count)\n            this.openCountRequests.delete(id)\n          }\n          return\n        }\n        case 'EOSE': {\n          const so = this.openSubs.get(data[1] as string)\n          if (!so) return\n          so.receivedEose()\n          return\n        }\n        case 'OK': {\n          const id: string = data[1]\n          const ok: boolean = data[2]\n          const reason: string = data[3]\n          const ep = this.openEventPublishes.get(id) as EventPublishResolver\n          if (ep) {\n            clearTimeout(ep.timeout)\n            if (ok) ep.resolve(reason)\n            else ep.reject(new Error(reason))\n            this.openEventPublishes.delete(id)\n          }\n          return\n        }\n        case 'CLOSED': {\n          const id: string = data[1]\n          const so = this.openSubs.get(id)\n          if (!so) return\n          so.closed = true\n          so.close(data[2] as string)\n          return\n        }\n        case 'NOTICE': {\n          this.onnotice(data[1] as string)\n          return\n        }\n        case 'AUTH': {\n          this.challenge = data[1] as string\n          if (this.onauth) {\n            this.auth(this.onauth)\n          }\n          return\n        }\n        default: {\n          const so = this.openSubs.get(data[1])\n          so?.oncustom?.(data)\n          return\n        }\n      }\n    } catch (err) {\n      return\n    }\n  }\n\n  public async send(message: string) {\n    if (!this.connectionPromise) throw new SendingOnClosedConnection(message, this.url)\n\n    this.connectionPromise.then(() => {\n      this.ws?.send(message)\n    })\n  }\n\n  public async auth(signAuthEvent: (evt: EventTemplate) => Promise<VerifiedEvent>): Promise<string> {\n    const challenge = this.challenge\n    if (!challenge) throw new Error(\"can't perform auth, no challenge was received\")\n    if (this.authPromise) return this.authPromise\n\n    this.authPromise = new Promise<string>(async (resolve, reject) => {\n      try {\n        let evt = await signAuthEvent(makeAuthEvent(this.url, challenge))\n        let timeout = setTimeout(() => {\n          let ep = this.openEventPublishes.get(evt.id) as EventPublishResolver\n          if (ep) {\n            ep.reject(new Error('auth timed out'))\n            this.openEventPublishes.delete(evt.id)\n          }\n        }, this.publishTimeout)\n        this.openEventPublishes.set(evt.id, { resolve, reject, timeout })\n        this.send('[\"AUTH\",' + JSON.stringify(evt) + ']')\n      } catch (err) {\n        console.warn('subscribe auth function failed:', err)\n      }\n    })\n    return this.authPromise\n  }\n\n  public async publish(event: Event): Promise<string> {\n    const ret = new Promise<string>((resolve, reject) => {\n      const timeout = setTimeout(() => {\n        const ep = this.openEventPublishes.get(event.id) as EventPublishResolver\n        if (ep) {\n          ep.reject(new Error('publish timed out'))\n          this.openEventPublishes.delete(event.id)\n        }\n      }, this.publishTimeout)\n      this.openEventPublishes.set(event.id, { resolve, reject, timeout })\n    })\n    this.send('[\"EVENT\",' + JSON.stringify(event) + ']')\n    return ret\n  }\n\n  public async count(filters: Filter[], params: { id?: string | null }): Promise<number> {\n    this.serial++\n    const id = params?.id || 'count:' + this.serial\n    const ret = new Promise<number>((resolve, reject) => {\n      this.openCountRequests.set(id, { resolve, reject })\n    })\n    this.send('[\"COUNT\",\"' + id + '\",' + JSON.stringify(filters).substring(1))\n    return ret\n  }\n\n  public subscribe(\n    filters: Filter[],\n    params: Partial<SubscriptionParams> & { label?: string; id?: string },\n  ): Subscription {\n    const sub = this.prepareSubscription(filters, params)\n    sub.fire()\n\n    if (params.abort) {\n      params.abort.onabort = () => sub.close(String(params.abort!.reason || '<aborted>'))\n    }\n\n    return sub\n  }\n\n  public prepareSubscription(\n    filters: Filter[],\n    params: Partial<SubscriptionParams> & { label?: string; id?: string },\n  ): Subscription {\n    this.serial++\n    const id = params.id || (params.label ? params.label + ':' : 'sub:') + this.serial\n    const subscription = new Subscription(this, id, filters, params)\n    this.openSubs.set(id, subscription)\n    return subscription\n  }\n\n  public close() {\n    this.closedIntentionally = true\n    if (this.reconnectTimeoutHandle) {\n      clearTimeout(this.reconnectTimeoutHandle)\n      this.reconnectTimeoutHandle = undefined\n    }\n    if (this.pingIntervalHandle) {\n      clearInterval(this.pingIntervalHandle)\n      this.pingIntervalHandle = undefined\n    }\n    this.closeAllSubscriptions('relay connection closed by us')\n    this._connected = false\n    this.onclose?.()\n    if (this.ws?.readyState === this._WebSocket.OPEN) {\n      this.ws?.close()\n    }\n  }\n\n  // this is the function assigned to this.ws.onmessage\n  // it's exposed for testing and debugging purposes\n  public _onmessage(ev: MessageEvent<any>) {\n    this.incomingMessageQueue.enqueue(ev.data as string)\n    if (!this.queueRunning) {\n      this.runQueue()\n    }\n  }\n}\n\nexport class Subscription {\n  public readonly relay: AbstractRelay\n  public readonly id: string\n\n  public lastEmitted: number | undefined\n  public closed: boolean = false\n  public eosed: boolean = false\n  public filters: Filter[]\n  public alreadyHaveEvent: ((id: string) => boolean) | undefined\n  public receivedEvent: ((relay: AbstractRelay, id: string) => void) | undefined\n\n  public onevent: (evt: Event) => void\n  public oneose: (() => void) | undefined\n  public onclose: ((reason: string) => void) | undefined\n\n  // will get any messages that have this subscription id as their second item and are not default standard\n  public oncustom: ((msg: string[]) => void) | undefined\n\n  public eoseTimeout: number\n  private eoseTimeoutHandle: ReturnType<typeof setTimeout> | undefined\n\n  constructor(relay: AbstractRelay, id: string, filters: Filter[], params: SubscriptionParams) {\n    if (filters.length === 0) throw new Error(\"subscription can't be created with zero filters\")\n\n    this.relay = relay\n    this.filters = filters\n    this.id = id\n    this.alreadyHaveEvent = params.alreadyHaveEvent\n    this.receivedEvent = params.receivedEvent\n    this.eoseTimeout = params.eoseTimeout || relay.baseEoseTimeout\n\n    this.oneose = params.oneose\n    this.onclose = params.onclose\n    this.onevent =\n      params.onevent ||\n      (event => {\n        console.warn(\n          `onevent() callback not defined for subscription '${this.id}' in relay ${this.relay.url}. event received:`,\n          event,\n        )\n      })\n  }\n\n  public fire() {\n    this.relay.send('[\"REQ\",\"' + this.id + '\",' + JSON.stringify(this.filters).substring(1))\n\n    // only now we start counting the eoseTimeout\n    this.eoseTimeoutHandle = setTimeout(this.receivedEose.bind(this), this.eoseTimeout)\n  }\n\n  public receivedEose() {\n    if (this.eosed) return\n    clearTimeout(this.eoseTimeoutHandle)\n    this.eosed = true\n    this.oneose?.()\n  }\n\n  public close(reason: string = 'closed by caller') {\n    if (!this.closed && this.relay.connected) {\n      // if the connection was closed by the user calling .close() we will send a CLOSE message\n      // otherwise this._open will be already set to false so we will skip this\n      try {\n        this.relay.send('[\"CLOSE\",' + JSON.stringify(this.id) + ']')\n      } catch (err) {\n        if (err instanceof SendingOnClosedConnection) {\n          /* doesn't matter, it's ok */\n        } else {\n          throw err\n        }\n      }\n      this.closed = true\n    }\n    this.relay.openSubs.delete(this.id)\n    this.onclose?.(reason)\n  }\n}\n\nexport type SubscriptionParams = {\n  onevent?: (evt: Event) => void\n  oneose?: () => void\n  onclose?: (reason: string) => void\n  alreadyHaveEvent?: (id: string) => boolean\n  receivedEvent?: (relay: AbstractRelay, id: string) => void\n  eoseTimeout?: number\n  abort?: AbortSignal\n}\n\nexport type CountResolver = {\n  resolve: (count: number) => void\n  reject: (err: Error) => void\n}\n\nexport type EventPublishResolver = {\n  resolve: (reason: string) => void\n  reject: (err: Error) => void\n  timeout: ReturnType<typeof setTimeout>\n}\n"],
+  "mappings": "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",
   "names": ["import_utils", "i", "target", "i"]
 }
Index: package/lib/esm/relay.js.map
===================================================================
--- package/lib/esm/relay.js.map
+++ package/lib/esm/relay.js.map
@@ -1,7 +1,7 @@
 {
   "version": 3,
   "sources": ["../../pure.ts", "../../core.ts", "../../utils.ts", "../../kinds.ts", "../../filter.ts", "../../fakejson.ts", "../../nip42.ts", "../../helpers.ts", "../../abstract-relay.ts", "../../relay.ts"],
-  "sourcesContent": ["import { schnorr } from '@noble/curves/secp256k1'\nimport { bytesToHex } from '@noble/hashes/utils'\nimport { Nostr, Event, EventTemplate, UnsignedEvent, VerifiedEvent, verifiedSymbol, validateEvent } from './core.ts'\nimport { sha256 } from '@noble/hashes/sha256'\n\nimport { utf8Encoder } from './utils.ts'\n\nclass JS implements Nostr {\n  generateSecretKey(): Uint8Array {\n    return schnorr.utils.randomPrivateKey()\n  }\n  getPublicKey(secretKey: Uint8Array): string {\n    return bytesToHex(schnorr.getPublicKey(secretKey))\n  }\n  finalizeEvent(t: EventTemplate, secretKey: Uint8Array): VerifiedEvent {\n    const event = t as VerifiedEvent\n    event.pubkey = bytesToHex(schnorr.getPublicKey(secretKey))\n    event.id = getEventHash(event)\n    event.sig = bytesToHex(schnorr.sign(getEventHash(event), secretKey))\n    event[verifiedSymbol] = true\n    return event\n  }\n  verifyEvent(event: Event): event is VerifiedEvent {\n    if (typeof event[verifiedSymbol] === 'boolean') return event[verifiedSymbol]\n\n    const hash = getEventHash(event)\n    if (hash !== event.id) {\n      event[verifiedSymbol] = false\n      return false\n    }\n\n    try {\n      const valid = schnorr.verify(event.sig, hash, event.pubkey)\n      event[verifiedSymbol] = valid\n      return valid\n    } catch (err) {\n      event[verifiedSymbol] = false\n      return false\n    }\n  }\n}\n\nexport function serializeEvent(evt: UnsignedEvent): string {\n  if (!validateEvent(evt)) throw new Error(\"can't serialize event with wrong or missing properties\")\n  return JSON.stringify([0, evt.pubkey, evt.created_at, evt.kind, evt.tags, evt.content])\n}\n\nexport function getEventHash(event: UnsignedEvent): string {\n  let eventHash = sha256(utf8Encoder.encode(serializeEvent(event)))\n  return bytesToHex(eventHash)\n}\n\nconst i: JS = new JS()\n\nexport const generateSecretKey = i.generateSecretKey\nexport const getPublicKey = i.getPublicKey\nexport const finalizeEvent = i.finalizeEvent\nexport const verifyEvent = i.verifyEvent\nexport * from './core.ts'\n", "export interface Nostr {\n  generateSecretKey(): Uint8Array\n  getPublicKey(secretKey: Uint8Array): string\n  finalizeEvent(event: EventTemplate, secretKey: Uint8Array): VerifiedEvent\n  verifyEvent(event: Event): event is VerifiedEvent\n}\n\n/** Designates a verified event signature. */\nexport const verifiedSymbol = Symbol('verified')\n\nexport type NostrEvent = {\n  kind: number\n  tags: string[][]\n  content: string\n  created_at: number\n  pubkey: string\n  id: string\n  sig: string\n  [verifiedSymbol]?: boolean\n}\n\nexport type Event = NostrEvent\nexport type EventTemplate = Pick<Event, 'kind' | 'tags' | 'content' | 'created_at'>\nexport type UnsignedEvent = Pick<Event, 'kind' | 'tags' | 'content' | 'created_at' | 'pubkey'>\n\n/** An event whose signature has been verified. */\nexport interface VerifiedEvent extends Event {\n  [verifiedSymbol]: true\n}\n\nconst isRecord = (obj: unknown): obj is Record<string, unknown> => obj instanceof Object\n\nexport function validateEvent<T>(event: T): event is T & UnsignedEvent {\n  if (!isRecord(event)) return false\n  if (typeof event.kind !== 'number') return false\n  if (typeof event.content !== 'string') return false\n  if (typeof event.created_at !== 'number') return false\n  if (typeof event.pubkey !== 'string') return false\n  if (!event.pubkey.match(/^[a-f0-9]{64}$/)) return false\n\n  if (!Array.isArray(event.tags)) return false\n  for (let i = 0; i < event.tags.length; i++) {\n    let tag = event.tags[i]\n    if (!Array.isArray(tag)) return false\n    for (let j = 0; j < tag.length; j++) {\n      if (typeof tag[j] !== 'string') return false\n    }\n  }\n\n  return true\n}\n\n/**\n * Sort events in reverse-chronological order by the `created_at` timestamp,\n * and then by the event `id` (lexicographically) in case of ties.\n * This mutates the array.\n */\nexport function sortEvents(events: Event[]): Event[] {\n  return events.sort((a: NostrEvent, b: NostrEvent): number => {\n    if (a.created_at !== b.created_at) {\n      return b.created_at - a.created_at\n    }\n    return a.id.localeCompare(b.id)\n  })\n}\n", "import type { Event } from './core.ts'\n\nexport const utf8Decoder: TextDecoder = new TextDecoder('utf-8')\nexport const utf8Encoder: TextEncoder = new TextEncoder()\n\nexport { bytesToHex, hexToBytes } from '@noble/hashes/utils'\n\nexport function normalizeURL(url: string): string {\n  try {\n    if (url.indexOf('://') === -1) url = 'wss://' + url\n    let p = new URL(url)\n    if (p.protocol === 'http:') p.protocol = 'ws:'\n    else if (p.protocol === 'https:') p.protocol = 'wss:'\n    p.pathname = p.pathname.replace(/\\/+/g, '/')\n    if (p.pathname.endsWith('/')) p.pathname = p.pathname.slice(0, -1)\n    if ((p.port === '80' && p.protocol === 'ws:') || (p.port === '443' && p.protocol === 'wss:')) p.port = ''\n    p.searchParams.sort()\n    p.hash = ''\n    return p.toString()\n  } catch (e) {\n    throw new Error(`Invalid URL: ${url}`)\n  }\n}\n\nexport function insertEventIntoDescendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return b.created_at - event.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function insertEventIntoAscendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return event.created_at - b.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function binarySearch<T>(arr: T[], compare: (b: T) => number): [number, boolean] {\n  let start = 0\n  let end = arr.length - 1\n\n  while (start <= end) {\n    const mid = Math.floor((start + end) / 2)\n    const cmp = compare(arr[mid])\n\n    if (cmp === 0) {\n      return [mid, true]\n    }\n\n    if (cmp < 0) {\n      end = mid - 1\n    } else {\n      start = mid + 1\n    }\n  }\n\n  return [start, false]\n}\n\nexport class QueueNode<V> {\n  public value: V\n  public next: QueueNode<V> | null = null\n  public prev: QueueNode<V> | null = null\n\n  constructor(message: V) {\n    this.value = message\n  }\n}\n\nexport class Queue<V> {\n  public first: QueueNode<V> | null\n  public last: QueueNode<V> | null\n\n  constructor() {\n    this.first = null\n    this.last = null\n  }\n\n  enqueue(value: V): boolean {\n    const newNode = new QueueNode(value)\n    if (!this.last) {\n      // list is empty\n      this.first = newNode\n      this.last = newNode\n    } else if (this.last === this.first) {\n      // list has a single element\n      this.last = newNode\n      this.last.prev = this.first\n      this.first.next = newNode\n    } else {\n      // list has elements, add as last\n      newNode.prev = this.last\n      this.last.next = newNode\n      this.last = newNode\n    }\n    return true\n  }\n\n  dequeue(): V | null {\n    if (!this.first) return null\n\n    if (this.first === this.last) {\n      const target = this.first\n      this.first = null\n      this.last = null\n      return target.value\n    }\n\n    const target = this.first\n    this.first = target.next\n    if (this.first) {\n      this.first.prev = null // fix: clean up prev pointer\n    }\n\n    return target.value\n  }\n}\n", "import { NostrEvent, validateEvent } from './pure.ts'\n\n/** Events are **regular**, which means they're all expected to be stored by relays. */\nexport function isRegularKind(kind: number): boolean {\n  return kind < 10000 && kind !== 0 && kind !== 3\n}\n\n/** Events are **replaceable**, which means that, for each combination of `pubkey` and `kind`, only the latest event is expected to (SHOULD) be stored by relays, older versions are expected to be discarded. */\nexport function isReplaceableKind(kind: number): boolean {\n  return kind === 0 || kind === 3 || (10000 <= kind && kind < 20000)\n}\n\n/** Events are **ephemeral**, which means they are not expected to be stored by relays. */\nexport function isEphemeralKind(kind: number): boolean {\n  return 20000 <= kind && kind < 30000\n}\n\n/** Events are **addressable**, which means that, for each combination of `pubkey`, `kind` and the `d` tag, only the latest event is expected to be stored by relays, older versions are expected to be discarded. */\nexport function isAddressableKind(kind: number): boolean {\n  return 30000 <= kind && kind < 40000\n}\n\n/** Classification of the event kind. */\nexport type KindClassification = 'regular' | 'replaceable' | 'ephemeral' | 'parameterized' | 'unknown'\n\n/** Determine the classification of this kind of event if known, or `unknown`. */\nexport function classifyKind(kind: number): KindClassification {\n  if (isRegularKind(kind)) return 'regular'\n  if (isReplaceableKind(kind)) return 'replaceable'\n  if (isEphemeralKind(kind)) return 'ephemeral'\n  if (isAddressableKind(kind)) return 'parameterized'\n  return 'unknown'\n}\n\nexport function isKind<T extends number>(event: unknown, kind: T | Array<T>): event is NostrEvent & { kind: T } {\n  const kindAsArray: number[] = kind instanceof Array ? kind : [kind]\n  return (validateEvent(event) && kindAsArray.includes(event.kind)) || false\n}\n\nexport const Metadata = 0\nexport type Metadata = typeof Metadata\nexport const ShortTextNote = 1\nexport type ShortTextNote = typeof ShortTextNote\nexport const RecommendRelay = 2\nexport type RecommendRelay = typeof RecommendRelay\nexport const Contacts = 3\nexport type Contacts = typeof Contacts\nexport const EncryptedDirectMessage = 4\nexport type EncryptedDirectMessage = typeof EncryptedDirectMessage\nexport const EventDeletion = 5\nexport type EventDeletion = typeof EventDeletion\nexport const Repost = 6\nexport type Repost = typeof Repost\nexport const Reaction = 7\nexport type Reaction = typeof Reaction\nexport const BadgeAward = 8\nexport type BadgeAward = typeof BadgeAward\nexport const ChatMessage = 9\nexport type ChatMessage = typeof ChatMessage\nexport const ForumThread = 11\nexport type ForumThread = typeof ForumThread\nexport const Seal = 13\nexport type Seal = typeof Seal\nexport const PrivateDirectMessage = 14\nexport type PrivateDirectMessage = typeof PrivateDirectMessage\nexport const FileMessage = 15\nexport type FileMessage = typeof FileMessage\nexport const GenericRepost = 16\nexport type GenericRepost = typeof GenericRepost\nexport const Photo = 20\nexport type Photo = typeof Photo\nexport const NormalVideo = 21\nexport type NormalVideo = typeof NormalVideo\nexport const ShortVideo = 22\nexport type ShortVideo = typeof ShortVideo\nexport const ChannelCreation = 40\nexport type ChannelCreation = typeof ChannelCreation\nexport const ChannelMetadata = 41\nexport type ChannelMetadata = typeof ChannelMetadata\nexport const ChannelMessage = 42\nexport type ChannelMessage = typeof ChannelMessage\nexport const ChannelHideMessage = 43\nexport type ChannelHideMessage = typeof ChannelHideMessage\nexport const ChannelMuteUser = 44\nexport type ChannelMuteUser = typeof ChannelMuteUser\nexport const OpenTimestamps = 1040\nexport type OpenTimestamps = typeof OpenTimestamps\nexport const GiftWrap = 1059\nexport type GiftWrap = typeof GiftWrap\nexport const Poll = 1068\nexport type Poll = typeof Poll\nexport const FileMetadata = 1063\nexport type FileMetadata = typeof FileMetadata\nexport const Comment = 1111\nexport type Comment = typeof Comment\nexport const LiveChatMessage = 1311\nexport type LiveChatMessage = typeof LiveChatMessage\nexport const Voice = 1222\nexport type Voice = typeof Voice\nexport const VoiceComment = 1244\nexport type VoiceComment = typeof VoiceComment\nexport const ProblemTracker = 1971\nexport type ProblemTracker = typeof ProblemTracker\nexport const Report = 1984\nexport type Report = typeof Report\nexport const Reporting = 1984\nexport type Reporting = typeof Reporting\nexport const Label = 1985\nexport type Label = typeof Label\nexport const CommunityPostApproval = 4550\nexport type CommunityPostApproval = typeof CommunityPostApproval\nexport const JobRequest = 5999\nexport type JobRequest = typeof JobRequest\nexport const JobResult = 6999\nexport type JobResult = typeof JobResult\nexport const JobFeedback = 7000\nexport type JobFeedback = typeof JobFeedback\nexport const ZapGoal = 9041\nexport type ZapGoal = typeof ZapGoal\nexport const ZapRequest = 9734\nexport type ZapRequest = typeof ZapRequest\nexport const Zap = 9735\nexport type Zap = typeof Zap\nexport const Highlights = 9802\nexport type Highlights = typeof Highlights\nexport const PollResponse = 1018\nexport type PollResponse = typeof PollResponse\nexport const Mutelist = 10000\nexport type Mutelist = typeof Mutelist\nexport const Pinlist = 10001\nexport type Pinlist = typeof Pinlist\nexport const RelayList = 10002\nexport type RelayList = typeof RelayList\nexport const BookmarkList = 10003\nexport type BookmarkList = typeof BookmarkList\nexport const CommunitiesList = 10004\nexport type CommunitiesList = typeof CommunitiesList\nexport const PublicChatsList = 10005\nexport type PublicChatsList = typeof PublicChatsList\nexport const BlockedRelaysList = 10006\nexport type BlockedRelaysList = typeof BlockedRelaysList\nexport const SearchRelaysList = 10007\nexport type SearchRelaysList = typeof SearchRelaysList\nexport const FavoriteRelays = 10012\nexport type FavoriteRelays = typeof FavoriteRelays\nexport const InterestsList = 10015\nexport type InterestsList = typeof InterestsList\nexport const UserEmojiList = 10030\nexport type UserEmojiList = typeof UserEmojiList\nexport const DirectMessageRelaysList = 10050\nexport type DirectMessageRelaysList = typeof DirectMessageRelaysList\nexport const FileServerPreference = 10096\nexport type FileServerPreference = typeof FileServerPreference\nexport const BlossomServerList = 10063\nexport type BlossomServerList = typeof BlossomServerList\nexport const NWCWalletInfo = 13194\nexport type NWCWalletInfo = typeof NWCWalletInfo\nexport const LightningPubRPC = 21000\nexport type LightningPubRPC = typeof LightningPubRPC\nexport const ClientAuth = 22242\nexport type ClientAuth = typeof ClientAuth\nexport const NWCWalletRequest = 23194\nexport type NWCWalletRequest = typeof NWCWalletRequest\nexport const NWCWalletResponse = 23195\nexport type NWCWalletResponse = typeof NWCWalletResponse\nexport const NostrConnect = 24133\nexport type NostrConnect = typeof NostrConnect\nexport const HTTPAuth = 27235\nexport type HTTPAuth = typeof HTTPAuth\nexport const Followsets = 30000\nexport type Followsets = typeof Followsets\nexport const Genericlists = 30001\nexport type Genericlists = typeof Genericlists\nexport const Relaysets = 30002\nexport type Relaysets = typeof Relaysets\nexport const Bookmarksets = 30003\nexport type Bookmarksets = typeof Bookmarksets\nexport const Curationsets = 30004\nexport type Curationsets = typeof Curationsets\nexport const ProfileBadges = 30008\nexport type ProfileBadges = typeof ProfileBadges\nexport const BadgeDefinition = 30009\nexport type BadgeDefinition = typeof BadgeDefinition\nexport const Interestsets = 30015\nexport type Interestsets = typeof Interestsets\nexport const CreateOrUpdateStall = 30017\nexport type CreateOrUpdateStall = typeof CreateOrUpdateStall\nexport const CreateOrUpdateProduct = 30018\nexport type CreateOrUpdateProduct = typeof CreateOrUpdateProduct\nexport const LongFormArticle = 30023\nexport type LongFormArticle = typeof LongFormArticle\nexport const DraftLong = 30024\nexport type DraftLong = typeof DraftLong\nexport const Emojisets = 30030\nexport type Emojisets = typeof Emojisets\nexport const Application = 30078\nexport type Application = typeof Application\nexport const LiveEvent = 30311\nexport type LiveEvent = typeof LiveEvent\nexport const UserStatuses = 30315\nexport type UserStatuses = typeof UserStatuses\nexport const ClassifiedListing = 30402\nexport type ClassifiedListing = typeof ClassifiedListing\nexport const DraftClassifiedListing = 30403\nexport type DraftClassifiedListing = typeof DraftClassifiedListing\nexport const Date = 31922\nexport type Date = typeof Date\nexport const Time = 31923\nexport type Time = typeof Time\nexport const Calendar = 31924\nexport type Calendar = typeof Calendar\nexport const CalendarEventRSVP = 31925\nexport type CalendarEventRSVP = typeof CalendarEventRSVP\nexport const RelayReview = 31987\nexport type RelayReview = typeof RelayReview\nexport const Handlerrecommendation = 31989\nexport type Handlerrecommendation = typeof Handlerrecommendation\nexport const Handlerinformation = 31990\nexport type Handlerinformation = typeof Handlerinformation\nexport const CommunityDefinition = 34550\nexport type CommunityDefinition = typeof CommunityDefinition\nexport const GroupMetadata = 39000\nexport type GroupMetadata = typeof GroupMetadata\n", "import { Event } from './core.ts'\nimport { isAddressableKind, isReplaceableKind } from './kinds.ts'\n\nexport type Filter = {\n  ids?: string[]\n  kinds?: number[]\n  authors?: string[]\n  since?: number\n  until?: number\n  limit?: number\n  search?: string\n  [key: `#${string}`]: string[] | undefined\n}\n\nexport function matchFilter(filter: Filter, event: Event): boolean {\n  if (filter.ids && filter.ids.indexOf(event.id) === -1) {\n    return false\n  }\n  if (filter.kinds && filter.kinds.indexOf(event.kind) === -1) {\n    return false\n  }\n  if (filter.authors && filter.authors.indexOf(event.pubkey) === -1) {\n    return false\n  }\n\n  for (let f in filter) {\n    if (f[0] === '#') {\n      let tagName = f.slice(1)\n      let values = filter[`#${tagName}`]\n      if (values && !event.tags.find(([t, v]) => t === f.slice(1) && values!.indexOf(v) !== -1)) return false\n    }\n  }\n\n  if (filter.since && event.created_at < filter.since) return false\n  if (filter.until && event.created_at > filter.until) return false\n\n  return true\n}\n\nexport function matchFilters(filters: Filter[], event: Event): boolean {\n  for (let i = 0; i < filters.length; i++) {\n    if (matchFilter(filters[i], event)) {\n      return true\n    }\n  }\n  return false\n}\n\nexport function mergeFilters(...filters: Filter[]): Filter {\n  let result: Filter = {}\n  for (let i = 0; i < filters.length; i++) {\n    let filter = filters[i]\n    Object.entries(filter).forEach(([property, values]) => {\n      if (property === 'kinds' || property === 'ids' || property === 'authors' || property[0] === '#') {\n        // @ts-ignore\n        result[property] = result[property] || []\n        // @ts-ignore\n        for (let v = 0; v < values.length; v++) {\n          // @ts-ignore\n          let value = values[v]\n          // @ts-ignore\n          if (!result[property].includes(value)) result[property].push(value)\n        }\n      }\n    })\n\n    if (filter.limit && (!result.limit || filter.limit > result.limit)) result.limit = filter.limit\n    if (filter.until && (!result.until || filter.until > result.until)) result.until = filter.until\n    if (filter.since && (!result.since || filter.since < result.since)) result.since = filter.since\n  }\n\n  return result\n}\n\n/**\n * Calculate the intrinsic limit of a filter.\n * This function returns a positive integer, or `Infinity` if there is no intrinsic limit.\n */\nexport function getFilterLimit(filter: Filter): number {\n  if (filter.ids && !filter.ids.length) return 0\n  if (filter.kinds && !filter.kinds.length) return 0\n  if (filter.authors && !filter.authors.length) return 0\n\n  for (const [key, value] of Object.entries(filter)) {\n    if (key[0] === '#' && Array.isArray(value) && !value.length) return 0\n  }\n\n  return Math.min(\n    // The `limit` property creates an artificial limit.\n    Math.max(0, filter.limit ?? Infinity),\n\n    // There can only be one event per `id`.\n    filter.ids?.length ?? Infinity,\n\n    // Replaceable events are limited by the number of authors and kinds.\n    filter.authors?.length && filter.kinds?.every(kind => isReplaceableKind(kind))\n      ? filter.authors.length * filter.kinds.length\n      : Infinity,\n\n    // Parameterized replaceable events are limited by the number of authors, kinds, and \"d\" tags.\n    filter.authors?.length && filter.kinds?.every(kind => isAddressableKind(kind)) && filter['#d']?.length\n      ? filter.authors.length * filter.kinds.length * filter['#d'].length\n      : Infinity,\n  )\n}\n", "export function getHex64(json: string, field: string): string {\n  let len = field.length + 3\n  let idx = json.indexOf(`\"${field}\":`) + len\n  let s = json.slice(idx).indexOf(`\"`) + idx + 1\n  return json.slice(s, s + 64)\n}\n\nexport function getInt(json: string, field: string): number {\n  let len = field.length\n  let idx = json.indexOf(`\"${field}\":`) + len + 3\n  let sliced = json.slice(idx)\n  let end = Math.min(sliced.indexOf(','), sliced.indexOf('}'))\n  return parseInt(sliced.slice(0, end), 10)\n}\n\nexport function getSubscriptionId(json: string): string | null {\n  let idx = json.slice(0, 22).indexOf(`\"EVENT\"`)\n  if (idx === -1) return null\n\n  let pstart = json.slice(idx + 7 + 1).indexOf(`\"`)\n  if (pstart === -1) return null\n  let start = idx + 7 + 1 + pstart\n\n  let pend = json.slice(start + 1, 80).indexOf(`\"`)\n  if (pend === -1) return null\n  let end = start + 1 + pend\n\n  return json.slice(start + 1, end)\n}\n\nexport function matchEventId(json: string, id: string): boolean {\n  return id === getHex64(json, 'id')\n}\n\nexport function matchEventPubkey(json: string, pubkey: string): boolean {\n  return pubkey === getHex64(json, 'pubkey')\n}\n\nexport function matchEventKind(json: string, kind: number): boolean {\n  return kind === getInt(json, 'kind')\n}\n", "import { EventTemplate } from './core.ts'\nimport { ClientAuth } from './kinds.ts'\n\n/**\n * creates an EventTemplate for an AUTH event to be signed.\n */\nexport function makeAuthEvent(relayURL: string, challenge: string): EventTemplate {\n  return {\n    kind: ClientAuth,\n    created_at: Math.floor(Date.now() / 1000),\n    tags: [\n      ['relay', relayURL],\n      ['challenge', challenge],\n    ],\n    content: '',\n  }\n}\n", "import { verifiedSymbol, type Event, type Nostr, VerifiedEvent } from './core.ts'\n\nexport async function yieldThread() {\n  return new Promise<void>((resolve, reject) => {\n    try {\n      // Check if MessageChannel is available\n      if (typeof MessageChannel !== 'undefined') {\n        const ch = new MessageChannel()\n        const handler = () => {\n          // @ts-ignore (typescript thinks this property should be called `removeListener`, but in fact it's `removeEventListener`)\n          ch.port1.removeEventListener('message', handler)\n          resolve()\n        }\n        // @ts-ignore (typescript thinks this property should be called `addListener`, but in fact it's `addEventListener`)\n        ch.port1.addEventListener('message', handler)\n        ch.port2.postMessage(0)\n        ch.port1.start()\n      } else {\n        if (typeof setImmediate !== 'undefined') {\n          setImmediate(resolve)\n        } else if (typeof setTimeout !== 'undefined') {\n          setTimeout(resolve, 0)\n        } else {\n          // Last resort - resolve immediately\n          resolve()\n        }\n      }\n    } catch (e) {\n      console.error('during yield: ', e)\n      reject(e)\n    }\n  })\n}\n\nexport const alwaysTrue: Nostr['verifyEvent'] = (t: Event): t is VerifiedEvent => {\n  t[verifiedSymbol] = true\n  return true\n}\n", "/* global WebSocket */\n\nimport type { Event, EventTemplate, VerifiedEvent, Nostr, NostrEvent } from './core.ts'\nimport { matchFilters, type Filter } from './filter.ts'\nimport { getHex64, getSubscriptionId } from './fakejson.ts'\nimport { Queue, normalizeURL } from './utils.ts'\nimport { makeAuthEvent } from './nip42.ts'\nimport { yieldThread } from './helpers.ts'\n\ntype RelayWebSocket = WebSocket & {\n  ping?(): void\n  on?(event: 'pong', listener: () => void): any\n}\n\nexport type AbstractRelayConstructorOptions = {\n  verifyEvent: Nostr['verifyEvent']\n  websocketImplementation?: typeof WebSocket\n  enablePing?: boolean\n  enableReconnect?: boolean\n}\n\nexport class SendingOnClosedConnection extends Error {\n  constructor(message: string, relay: string) {\n    super(`Tried to send message '${message} on a closed connection to ${relay}.`)\n    this.name = 'SendingOnClosedConnection'\n  }\n}\n\nexport class AbstractRelay {\n  public readonly url: string\n  private _connected: boolean = false\n\n  public onclose: (() => void) | null = null\n  public onnotice: (msg: string) => void = msg => console.debug(`NOTICE from ${this.url}: ${msg}`)\n  public onauth: undefined | ((evt: EventTemplate) => Promise<VerifiedEvent>)\n\n  public baseEoseTimeout: number = 4400\n  public publishTimeout: number = 4400\n  public pingFrequency: number = 29000\n  public pingTimeout: number = 20000\n  public resubscribeBackoff: number[] = [10000, 10000, 10000, 20000, 20000, 30000, 60000]\n  public openSubs: Map<string, Subscription> = new Map()\n  public enablePing: boolean | undefined\n  public enableReconnect: boolean\n  private reconnectTimeoutHandle: ReturnType<typeof setTimeout> | undefined\n  private pingIntervalHandle: ReturnType<typeof setInterval> | undefined\n  private reconnectAttempts: number = 0\n  private closedIntentionally: boolean = false\n\n  private connectionPromise: Promise<void> | undefined\n  private openCountRequests = new Map<string, CountResolver>()\n  private openEventPublishes = new Map<string, EventPublishResolver>()\n  private ws: RelayWebSocket | undefined\n  private incomingMessageQueue = new Queue<string>()\n  private queueRunning = false\n  private challenge: string | undefined\n  private authPromise: Promise<string> | undefined\n  private serial: number = 0\n  private verifyEvent: Nostr['verifyEvent']\n\n  private _WebSocket: typeof WebSocket\n\n  constructor(url: string, opts: AbstractRelayConstructorOptions) {\n    this.url = normalizeURL(url)\n    this.verifyEvent = opts.verifyEvent\n    this._WebSocket = opts.websocketImplementation || WebSocket\n    this.enablePing = opts.enablePing\n    this.enableReconnect = opts.enableReconnect || false\n  }\n\n  static async connect(\n    url: string,\n    opts: AbstractRelayConstructorOptions & Parameters<AbstractRelay['connect']>[0],\n  ): Promise<AbstractRelay> {\n    const relay = new AbstractRelay(url, opts)\n    await relay.connect(opts)\n    return relay\n  }\n\n  private closeAllSubscriptions(reason: string) {\n    for (let [_, sub] of this.openSubs) {\n      sub.close(reason)\n    }\n    this.openSubs.clear()\n\n    for (let [_, ep] of this.openEventPublishes) {\n      ep.reject(new Error(reason))\n    }\n    this.openEventPublishes.clear()\n\n    for (let [_, cr] of this.openCountRequests) {\n      cr.reject(new Error(reason))\n    }\n    this.openCountRequests.clear()\n  }\n\n  public get connected(): boolean {\n    return this._connected\n  }\n\n  private async reconnect(): Promise<void> {\n    const backoff = this.resubscribeBackoff[Math.min(this.reconnectAttempts, this.resubscribeBackoff.length - 1)]\n    this.reconnectAttempts++\n\n    this.reconnectTimeoutHandle = setTimeout(async () => {\n      try {\n        await this.connect()\n      } catch (err) {\n        // this will be called again through onclose/onerror\n      }\n    }, backoff)\n  }\n\n  private handleHardClose(reason: string) {\n    if (this.pingIntervalHandle) {\n      clearInterval(this.pingIntervalHandle)\n      this.pingIntervalHandle = undefined\n    }\n\n    this._connected = false\n    this.connectionPromise = undefined\n\n    const wasIntentional = this.closedIntentionally\n    this.closedIntentionally = false // reset for next time\n\n    this.onclose?.()\n\n    if (this.enableReconnect && !wasIntentional) {\n      this.reconnect()\n    } else {\n      this.closeAllSubscriptions(reason)\n    }\n  }\n\n  public async connect(opts?: { timeout?: number; abort?: AbortSignal }): Promise<void> {\n    let connectionTimeoutHandle: ReturnType<typeof setTimeout> | undefined\n\n    if (this.connectionPromise) return this.connectionPromise\n\n    this.challenge = undefined\n    this.authPromise = undefined\n    this.connectionPromise = new Promise((resolve, reject) => {\n      if (opts?.timeout) {\n        connectionTimeoutHandle = setTimeout(() => {\n          reject('connection timed out')\n          this.connectionPromise = undefined\n          this.onclose?.()\n          this.closeAllSubscriptions('relay connection timed out')\n        }, opts.timeout)\n      }\n\n      if (opts?.abort) {\n        opts.abort.onabort = reject\n      }\n\n      try {\n        this.ws = new this._WebSocket(this.url)\n      } catch (err) {\n        clearTimeout(connectionTimeoutHandle)\n        reject(err)\n        return\n      }\n\n      this.ws.onopen = () => {\n        if (this.reconnectTimeoutHandle) {\n          clearTimeout(this.reconnectTimeoutHandle)\n          this.reconnectTimeoutHandle = undefined\n        }\n        clearTimeout(connectionTimeoutHandle)\n        this._connected = true\n\n        const isReconnection = this.reconnectAttempts > 0\n        this.reconnectAttempts = 0\n\n        // resubscribe to all open subscriptions\n        for (const sub of this.openSubs.values()) {\n          sub.eosed = false\n          if (isReconnection) {\n            for (let f = 0; f < sub.filters.length; f++) {\n              if (sub.lastEmitted) {\n                sub.filters[f].since = sub.lastEmitted + 1\n              }\n            }\n          }\n          sub.fire()\n        }\n\n        if (this.enablePing) {\n          this.pingIntervalHandle = setInterval(() => this.pingpong(), this.pingFrequency)\n        }\n        resolve()\n      }\n\n      this.ws.onerror = ev => {\n        clearTimeout(connectionTimeoutHandle)\n        reject((ev as any).message || 'websocket error')\n        this.handleHardClose('relay connection errored')\n      }\n\n      this.ws.onclose = ev => {\n        clearTimeout(connectionTimeoutHandle)\n        reject((ev as any).message || 'websocket closed')\n        this.handleHardClose('relay connection closed')\n      }\n\n      this.ws.onmessage = this._onmessage.bind(this)\n    })\n\n    return this.connectionPromise\n  }\n\n  private waitForPingPong() {\n    return new Promise(resolve => {\n      // listen for pong\n      ;(this.ws as any).once('pong', () => resolve(true))\n      // send a ping\n      this.ws!.ping!()\n    })\n  }\n\n  private waitForDummyReq() {\n    return new Promise((resolve, reject) => {\n      if (!this.connectionPromise) return reject(new Error(`no connection to ${this.url}, can't ping`))\n\n      // make a dummy request with expected empty eose reply\n      // [\"REQ\", \"_\", {\"ids\":[\"aaaa...aaaa\"], \"limit\": 0}]\n      try {\n        const sub = this.subscribe(\n          [{ ids: ['aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'], limit: 0 }],\n          {\n            label: 'forced-ping',\n            oneose: () => {\n              resolve(true)\n              sub.close()\n            },\n            onclose() {\n              // if we get a CLOSED it's because the relay is alive\n              resolve(true)\n            },\n            eoseTimeout: this.pingTimeout + 1000,\n          },\n        )\n      } catch (err) {\n        reject(err)\n      }\n    })\n  }\n\n  // nodejs requires this magic here to ensure connections are closed when internet goes off and stuff\n  // in browsers it's done automatically. see https://github.com/nbd-wtf/nostr-tools/issues/491\n  private async pingpong() {\n    // if the websocket is connected\n    if (this.ws?.readyState === 1) {\n      // wait for either a ping-pong reply or a timeout\n      const result = await Promise.any([\n        // browsers don't have ping so use a dummy req\n        this.ws && this.ws.ping && (this.ws as any).once ? this.waitForPingPong() : this.waitForDummyReq(),\n        new Promise(res => setTimeout(() => res(false), this.pingTimeout)),\n      ])\n\n      if (!result) {\n        // pingpong closing socket\n        if (this.ws?.readyState === this._WebSocket.OPEN) {\n          this.ws?.close()\n        }\n      }\n    }\n  }\n\n  private async runQueue() {\n    this.queueRunning = true\n    while (true) {\n      if (false === this.handleNext()) {\n        break\n      }\n      await yieldThread()\n    }\n    this.queueRunning = false\n  }\n\n  private handleNext(): undefined | false {\n    const json = this.incomingMessageQueue.dequeue()\n    if (!json) {\n      return false\n    }\n\n    // shortcut EVENT sub\n    const subid = getSubscriptionId(json)\n    if (subid) {\n      const so = this.openSubs.get(subid as string)\n      if (!so) {\n        // this is an EVENT message, but for a subscription we don't have, so just stop here\n        return\n      }\n\n      // this will be called only when this message is a EVENT message for a subscription we have\n      // we do this before parsing the JSON to not have to do that for duplicate events\n      //   since JSON parsing is slow\n      const id = getHex64(json, 'id')\n      const alreadyHave = so.alreadyHaveEvent?.(id)\n\n      // notify any interested client that the relay has this event\n      // (do this after alreadyHaveEvent() because the client may rely on this to answer that)\n      so.receivedEvent?.(this, id)\n\n      if (alreadyHave) {\n        // if we had already seen this event we can just stop here\n        return\n      }\n    }\n\n    try {\n      let data = JSON.parse(json)\n      // we won't do any checks against the data since all failures (i.e. invalid messages from relays)\n      // will naturally be caught by the encompassing try..catch block\n\n      switch (data[0]) {\n        case 'EVENT': {\n          const so = this.openSubs.get(data[1] as string) as Subscription\n          const event = data[2] as NostrEvent\n          if (this.verifyEvent(event) && matchFilters(so.filters, event)) {\n            so.onevent(event)\n          }\n          if (!so.lastEmitted || so.lastEmitted < event.created_at) so.lastEmitted = event.created_at\n          return\n        }\n        case 'COUNT': {\n          const id: string = data[1]\n          const payload = data[2] as { count: number }\n          const cr = this.openCountRequests.get(id) as CountResolver\n          if (cr) {\n            cr.resolve(payload.count)\n            this.openCountRequests.delete(id)\n          }\n          return\n        }\n        case 'EOSE': {\n          const so = this.openSubs.get(data[1] as string)\n          if (!so) return\n          so.receivedEose()\n          return\n        }\n        case 'OK': {\n          const id: string = data[1]\n          const ok: boolean = data[2]\n          const reason: string = data[3]\n          const ep = this.openEventPublishes.get(id) as EventPublishResolver\n          if (ep) {\n            clearTimeout(ep.timeout)\n            if (ok) ep.resolve(reason)\n            else ep.reject(new Error(reason))\n            this.openEventPublishes.delete(id)\n          }\n          return\n        }\n        case 'CLOSED': {\n          const id: string = data[1]\n          const so = this.openSubs.get(id)\n          if (!so) return\n          so.closed = true\n          so.close(data[2] as string)\n          return\n        }\n        case 'NOTICE': {\n          this.onnotice(data[1] as string)\n          return\n        }\n        case 'AUTH': {\n          this.challenge = data[1] as string\n          if (this.onauth) {\n            this.auth(this.onauth)\n          }\n          return\n        }\n        default: {\n          const so = this.openSubs.get(data[1])\n          so?.oncustom?.(data)\n          return\n        }\n      }\n    } catch (err) {\n      return\n    }\n  }\n\n  public async send(message: string) {\n    if (!this.connectionPromise) throw new SendingOnClosedConnection(message, this.url)\n\n    this.connectionPromise.then(() => {\n      this.ws?.send(message)\n    })\n  }\n\n  public async auth(signAuthEvent: (evt: EventTemplate) => Promise<VerifiedEvent>): Promise<string> {\n    const challenge = this.challenge\n    if (!challenge) throw new Error(\"can't perform auth, no challenge was received\")\n    if (this.authPromise) return this.authPromise\n\n    this.authPromise = new Promise<string>(async (resolve, reject) => {\n      try {\n        let evt = await signAuthEvent(makeAuthEvent(this.url, challenge))\n        let timeout = setTimeout(() => {\n          let ep = this.openEventPublishes.get(evt.id) as EventPublishResolver\n          if (ep) {\n            ep.reject(new Error('auth timed out'))\n            this.openEventPublishes.delete(evt.id)\n          }\n        }, this.publishTimeout)\n        this.openEventPublishes.set(evt.id, { resolve, reject, timeout })\n        this.send('[\"AUTH\",' + JSON.stringify(evt) + ']')\n      } catch (err) {\n        console.warn('subscribe auth function failed:', err)\n      }\n    })\n    return this.authPromise\n  }\n\n  public async publish(event: Event): Promise<string> {\n    const ret = new Promise<string>((resolve, reject) => {\n      const timeout = setTimeout(() => {\n        const ep = this.openEventPublishes.get(event.id) as EventPublishResolver\n        if (ep) {\n          ep.reject(new Error('publish timed out'))\n          this.openEventPublishes.delete(event.id)\n        }\n      }, this.publishTimeout)\n      this.openEventPublishes.set(event.id, { resolve, reject, timeout })\n    })\n    this.send('[\"EVENT\",' + JSON.stringify(event) + ']')\n    return ret\n  }\n\n  public async count(filters: Filter[], params: { id?: string | null }): Promise<number> {\n    this.serial++\n    const id = params?.id || 'count:' + this.serial\n    const ret = new Promise<number>((resolve, reject) => {\n      this.openCountRequests.set(id, { resolve, reject })\n    })\n    this.send('[\"COUNT\",\"' + id + '\",' + JSON.stringify(filters).substring(1))\n    return ret\n  }\n\n  public subscribe(\n    filters: Filter[],\n    params: Partial<SubscriptionParams> & { label?: string; id?: string },\n  ): Subscription {\n    const sub = this.prepareSubscription(filters, params)\n    sub.fire()\n\n    if (params.abort) {\n      params.abort.onabort = () => sub.close(String(params.abort!.reason || '<aborted>'))\n    }\n\n    return sub\n  }\n\n  public prepareSubscription(\n    filters: Filter[],\n    params: Partial<SubscriptionParams> & { label?: string; id?: string },\n  ): Subscription {\n    this.serial++\n    const id = params.id || (params.label ? params.label + ':' : 'sub:') + this.serial\n    const subscription = new Subscription(this, id, filters, params)\n    this.openSubs.set(id, subscription)\n    return subscription\n  }\n\n  public close() {\n    this.closedIntentionally = true\n    if (this.reconnectTimeoutHandle) {\n      clearTimeout(this.reconnectTimeoutHandle)\n      this.reconnectTimeoutHandle = undefined\n    }\n    if (this.pingIntervalHandle) {\n      clearInterval(this.pingIntervalHandle)\n      this.pingIntervalHandle = undefined\n    }\n    this.closeAllSubscriptions('relay connection closed by us')\n    this._connected = false\n    this.onclose?.()\n    if (this.ws?.readyState === this._WebSocket.OPEN) {\n      this.ws?.close()\n    }\n  }\n\n  // this is the function assigned to this.ws.onmessage\n  // it's exposed for testing and debugging purposes\n  public _onmessage(ev: MessageEvent<any>) {\n    this.incomingMessageQueue.enqueue(ev.data as string)\n    if (!this.queueRunning) {\n      this.runQueue()\n    }\n  }\n}\n\nexport class Subscription {\n  public readonly relay: AbstractRelay\n  public readonly id: string\n\n  public lastEmitted: number | undefined\n  public closed: boolean = false\n  public eosed: boolean = false\n  public filters: Filter[]\n  public alreadyHaveEvent: ((id: string) => boolean) | undefined\n  public receivedEvent: ((relay: AbstractRelay, id: string) => void) | undefined\n\n  public onevent: (evt: Event) => void\n  public oneose: (() => void) | undefined\n  public onclose: ((reason: string) => void) | undefined\n\n  // will get any messages that have this subscription id as their second item and are not default standard\n  public oncustom: ((msg: string[]) => void) | undefined\n\n  public eoseTimeout: number\n  private eoseTimeoutHandle: ReturnType<typeof setTimeout> | undefined\n\n  constructor(relay: AbstractRelay, id: string, filters: Filter[], params: SubscriptionParams) {\n    if (filters.length === 0) throw new Error(\"subscription can't be created with zero filters\")\n\n    this.relay = relay\n    this.filters = filters\n    this.id = id\n    this.alreadyHaveEvent = params.alreadyHaveEvent\n    this.receivedEvent = params.receivedEvent\n    this.eoseTimeout = params.eoseTimeout || relay.baseEoseTimeout\n\n    this.oneose = params.oneose\n    this.onclose = params.onclose\n    this.onevent =\n      params.onevent ||\n      (event => {\n        console.warn(\n          `onevent() callback not defined for subscription '${this.id}' in relay ${this.relay.url}. event received:`,\n          event,\n        )\n      })\n  }\n\n  public fire() {\n    this.relay.send('[\"REQ\",\"' + this.id + '\",' + JSON.stringify(this.filters).substring(1))\n\n    // only now we start counting the eoseTimeout\n    this.eoseTimeoutHandle = setTimeout(this.receivedEose.bind(this), this.eoseTimeout)\n  }\n\n  public receivedEose() {\n    if (this.eosed) return\n    clearTimeout(this.eoseTimeoutHandle)\n    this.eosed = true\n    this.oneose?.()\n  }\n\n  public close(reason: string = 'closed by caller') {\n    if (!this.closed && this.relay.connected) {\n      // if the connection was closed by the user calling .close() we will send a CLOSE message\n      // otherwise this._open will be already set to false so we will skip this\n      try {\n        this.relay.send('[\"CLOSE\",' + JSON.stringify(this.id) + ']')\n      } catch (err) {\n        if (err instanceof SendingOnClosedConnection) {\n          /* doesn't matter, it's ok */\n        } else {\n          throw err\n        }\n      }\n      this.closed = true\n    }\n    this.relay.openSubs.delete(this.id)\n    this.onclose?.(reason)\n  }\n}\n\nexport type SubscriptionParams = {\n  onevent?: (evt: Event) => void\n  oneose?: () => void\n  onclose?: (reason: string) => void\n  alreadyHaveEvent?: (id: string) => boolean\n  receivedEvent?: (relay: AbstractRelay, id: string) => void\n  eoseTimeout?: number\n  abort?: AbortSignal\n}\n\nexport type CountResolver = {\n  resolve: (count: number) => void\n  reject: (err: Error) => void\n}\n\nexport type EventPublishResolver = {\n  resolve: (reason: string) => void\n  reject: (err: Error) => void\n  timeout: ReturnType<typeof setTimeout>\n}\n", "/* global WebSocket */\n\nimport { verifyEvent } from './pure.ts'\nimport { AbstractRelay, type AbstractRelayConstructorOptions } from './abstract-relay.ts'\n\nvar _WebSocket: typeof WebSocket\n\ntry {\n  _WebSocket = WebSocket\n} catch {}\n\nexport function useWebSocketImplementation(websocketImplementation: any) {\n  _WebSocket = websocketImplementation\n}\n\nexport class Relay extends AbstractRelay {\n  constructor(url: string, options?: Pick<AbstractRelayConstructorOptions, 'enablePing' | 'enableReconnect'>) {\n    super(url, { verifyEvent, websocketImplementation: _WebSocket, ...options })\n  }\n\n  static async connect(\n    url: string,\n    options?: Pick<AbstractRelayConstructorOptions, 'enablePing' | 'enableReconnect'>,\n  ): Promise<Relay> {\n    const relay = new Relay(url, options)\n    await relay.connect()\n    return relay\n  }\n}\n\nexport type RelayRecord = Record<string, { read: boolean; write: boolean }>\n\nexport * from './abstract-relay.ts'\n"],
-  "mappings": "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",
-  "names": ["bytesToHex", "i", "target", "bytesToHex", "i"]
+  "sourcesContent": ["import { schnorr } from '@noble/curves/secp256k1.js'\nimport { bytesToHex, hexToBytes } from '@noble/hashes/utils.js'\nimport { Nostr, Event, EventTemplate, UnsignedEvent, VerifiedEvent, verifiedSymbol, validateEvent } from './core.ts'\nimport { sha256 } from '@noble/hashes/sha2.js'\n\nimport { utf8Encoder } from './utils.ts'\n\nclass JS implements Nostr {\n  generateSecretKey(): Uint8Array {\n    return schnorr.utils.randomSecretKey()\n  }\n  getPublicKey(secretKey: Uint8Array): string {\n    return bytesToHex(schnorr.getPublicKey(secretKey))\n  }\n  finalizeEvent(t: EventTemplate, secretKey: Uint8Array): VerifiedEvent {\n    const event = t as VerifiedEvent\n    event.pubkey = bytesToHex(schnorr.getPublicKey(secretKey))\n    event.id = getEventHash(event)\n    event.sig = bytesToHex(schnorr.sign(hexToBytes(getEventHash(event)), secretKey))\n    event[verifiedSymbol] = true\n    return event\n  }\n  verifyEvent(event: Event): event is VerifiedEvent {\n    if (typeof event[verifiedSymbol] === 'boolean') return event[verifiedSymbol]\n\n    const hash = getEventHash(event)\n    if (hash !== event.id) {\n      event[verifiedSymbol] = false\n      return false\n    }\n\n    try {\n      const valid = schnorr.verify(hexToBytes(event.sig), hexToBytes(hash), hexToBytes(event.pubkey))\n      event[verifiedSymbol] = valid\n      return valid\n    } catch (err) {\n      event[verifiedSymbol] = false\n      return false\n    }\n  }\n}\n\nexport function serializeEvent(evt: UnsignedEvent): string {\n  if (!validateEvent(evt)) throw new Error(\"can't serialize event with wrong or missing properties\")\n  return JSON.stringify([0, evt.pubkey, evt.created_at, evt.kind, evt.tags, evt.content])\n}\n\nexport function getEventHash(event: UnsignedEvent): string {\n  let eventHash = sha256(utf8Encoder.encode(serializeEvent(event)))\n  return bytesToHex(eventHash)\n}\n\nconst i: JS = new JS()\n\nexport const generateSecretKey = i.generateSecretKey\nexport const getPublicKey = i.getPublicKey\nexport const finalizeEvent = i.finalizeEvent\nexport const verifyEvent = i.verifyEvent\nexport * from './core.ts'\n", "export interface Nostr {\n  generateSecretKey(): Uint8Array\n  getPublicKey(secretKey: Uint8Array): string\n  finalizeEvent(event: EventTemplate, secretKey: Uint8Array): VerifiedEvent\n  verifyEvent(event: Event): event is VerifiedEvent\n}\n\n/** Designates a verified event signature. */\nexport const verifiedSymbol = Symbol('verified')\n\nexport type NostrEvent = {\n  kind: number\n  tags: string[][]\n  content: string\n  created_at: number\n  pubkey: string\n  id: string\n  sig: string\n  [verifiedSymbol]?: boolean\n}\n\nexport type Event = NostrEvent\nexport type EventTemplate = Pick<Event, 'kind' | 'tags' | 'content' | 'created_at'>\nexport type UnsignedEvent = Pick<Event, 'kind' | 'tags' | 'content' | 'created_at' | 'pubkey'>\n\n/** An event whose signature has been verified. */\nexport interface VerifiedEvent extends Event {\n  [verifiedSymbol]: true\n}\n\nconst isRecord = (obj: unknown): obj is Record<string, unknown> => obj instanceof Object\n\nexport function validateEvent<T>(event: T): event is T & UnsignedEvent {\n  if (!isRecord(event)) return false\n  if (typeof event.kind !== 'number') return false\n  if (typeof event.content !== 'string') return false\n  if (typeof event.created_at !== 'number') return false\n  if (typeof event.pubkey !== 'string') return false\n  if (!event.pubkey.match(/^[a-f0-9]{64}$/)) return false\n\n  if (!Array.isArray(event.tags)) return false\n  for (let i = 0; i < event.tags.length; i++) {\n    let tag = event.tags[i]\n    if (!Array.isArray(tag)) return false\n    for (let j = 0; j < tag.length; j++) {\n      if (typeof tag[j] !== 'string') return false\n    }\n  }\n\n  return true\n}\n\n/**\n * Sort events in reverse-chronological order by the `created_at` timestamp,\n * and then by the event `id` (lexicographically) in case of ties.\n * This mutates the array.\n */\nexport function sortEvents(events: Event[]): Event[] {\n  return events.sort((a: NostrEvent, b: NostrEvent): number => {\n    if (a.created_at !== b.created_at) {\n      return b.created_at - a.created_at\n    }\n    return a.id.localeCompare(b.id)\n  })\n}\n", "import type { Event } from './core.ts'\n\nexport const utf8Decoder: TextDecoder = new TextDecoder('utf-8')\nexport const utf8Encoder: TextEncoder = new TextEncoder()\n\nexport { bytesToHex, hexToBytes } from '@noble/hashes/utils.js'\n\nexport function normalizeURL(url: string): string {\n  try {\n    if (url.indexOf('://') === -1) url = 'wss://' + url\n    let p = new URL(url)\n    if (p.protocol === 'http:') p.protocol = 'ws:'\n    else if (p.protocol === 'https:') p.protocol = 'wss:'\n    p.pathname = p.pathname.replace(/\\/+/g, '/')\n    if (p.pathname.endsWith('/')) p.pathname = p.pathname.slice(0, -1)\n    if ((p.port === '80' && p.protocol === 'ws:') || (p.port === '443' && p.protocol === 'wss:')) p.port = ''\n    p.searchParams.sort()\n    p.hash = ''\n    return p.toString()\n  } catch (e) {\n    throw new Error(`Invalid URL: ${url}`)\n  }\n}\n\nexport function insertEventIntoDescendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return b.created_at - event.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function insertEventIntoAscendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return event.created_at - b.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function binarySearch<T>(arr: T[], compare: (b: T) => number): [number, boolean] {\n  let start = 0\n  let end = arr.length - 1\n\n  while (start <= end) {\n    const mid = Math.floor((start + end) / 2)\n    const cmp = compare(arr[mid])\n\n    if (cmp === 0) {\n      return [mid, true]\n    }\n\n    if (cmp < 0) {\n      end = mid - 1\n    } else {\n      start = mid + 1\n    }\n  }\n\n  return [start, false]\n}\n\nexport class QueueNode<V> {\n  public value: V\n  public next: QueueNode<V> | null = null\n  public prev: QueueNode<V> | null = null\n\n  constructor(message: V) {\n    this.value = message\n  }\n}\n\nexport class Queue<V> {\n  public first: QueueNode<V> | null\n  public last: QueueNode<V> | null\n\n  constructor() {\n    this.first = null\n    this.last = null\n  }\n\n  enqueue(value: V): boolean {\n    const newNode = new QueueNode(value)\n    if (!this.last) {\n      // list is empty\n      this.first = newNode\n      this.last = newNode\n    } else if (this.last === this.first) {\n      // list has a single element\n      this.last = newNode\n      this.last.prev = this.first\n      this.first.next = newNode\n    } else {\n      // list has elements, add as last\n      newNode.prev = this.last\n      this.last.next = newNode\n      this.last = newNode\n    }\n    return true\n  }\n\n  dequeue(): V | null {\n    if (!this.first) return null\n\n    if (this.first === this.last) {\n      const target = this.first\n      this.first = null\n      this.last = null\n      return target.value\n    }\n\n    const target = this.first\n    this.first = target.next\n    if (this.first) {\n      this.first.prev = null // fix: clean up prev pointer\n    }\n\n    return target.value\n  }\n}\n", "import { NostrEvent, validateEvent } from './pure.ts'\n\n/** Events are **regular**, which means they're all expected to be stored by relays. */\nexport function isRegularKind(kind: number): boolean {\n  return kind < 10000 && kind !== 0 && kind !== 3\n}\n\n/** Events are **replaceable**, which means that, for each combination of `pubkey` and `kind`, only the latest event is expected to (SHOULD) be stored by relays, older versions are expected to be discarded. */\nexport function isReplaceableKind(kind: number): boolean {\n  return kind === 0 || kind === 3 || (10000 <= kind && kind < 20000)\n}\n\n/** Events are **ephemeral**, which means they are not expected to be stored by relays. */\nexport function isEphemeralKind(kind: number): boolean {\n  return 20000 <= kind && kind < 30000\n}\n\n/** Events are **addressable**, which means that, for each combination of `pubkey`, `kind` and the `d` tag, only the latest event is expected to be stored by relays, older versions are expected to be discarded. */\nexport function isAddressableKind(kind: number): boolean {\n  return 30000 <= kind && kind < 40000\n}\n\n/** Classification of the event kind. */\nexport type KindClassification = 'regular' | 'replaceable' | 'ephemeral' | 'parameterized' | 'unknown'\n\n/** Determine the classification of this kind of event if known, or `unknown`. */\nexport function classifyKind(kind: number): KindClassification {\n  if (isRegularKind(kind)) return 'regular'\n  if (isReplaceableKind(kind)) return 'replaceable'\n  if (isEphemeralKind(kind)) return 'ephemeral'\n  if (isAddressableKind(kind)) return 'parameterized'\n  return 'unknown'\n}\n\nexport function isKind<T extends number>(event: unknown, kind: T | Array<T>): event is NostrEvent & { kind: T } {\n  const kindAsArray: number[] = kind instanceof Array ? kind : [kind]\n  return (validateEvent(event) && kindAsArray.includes(event.kind)) || false\n}\n\nexport const Metadata = 0\nexport type Metadata = typeof Metadata\nexport const ShortTextNote = 1\nexport type ShortTextNote = typeof ShortTextNote\nexport const RecommendRelay = 2\nexport type RecommendRelay = typeof RecommendRelay\nexport const Contacts = 3\nexport type Contacts = typeof Contacts\nexport const EncryptedDirectMessage = 4\nexport type EncryptedDirectMessage = typeof EncryptedDirectMessage\nexport const EventDeletion = 5\nexport type EventDeletion = typeof EventDeletion\nexport const Repost = 6\nexport type Repost = typeof Repost\nexport const Reaction = 7\nexport type Reaction = typeof Reaction\nexport const BadgeAward = 8\nexport type BadgeAward = typeof BadgeAward\nexport const ChatMessage = 9\nexport type ChatMessage = typeof ChatMessage\nexport const ForumThread = 11\nexport type ForumThread = typeof ForumThread\nexport const Seal = 13\nexport type Seal = typeof Seal\nexport const PrivateDirectMessage = 14\nexport type PrivateDirectMessage = typeof PrivateDirectMessage\nexport const FileMessage = 15\nexport type FileMessage = typeof FileMessage\nexport const GenericRepost = 16\nexport type GenericRepost = typeof GenericRepost\nexport const Photo = 20\nexport type Photo = typeof Photo\nexport const NormalVideo = 21\nexport type NormalVideo = typeof NormalVideo\nexport const ShortVideo = 22\nexport type ShortVideo = typeof ShortVideo\nexport const ChannelCreation = 40\nexport type ChannelCreation = typeof ChannelCreation\nexport const ChannelMetadata = 41\nexport type ChannelMetadata = typeof ChannelMetadata\nexport const ChannelMessage = 42\nexport type ChannelMessage = typeof ChannelMessage\nexport const ChannelHideMessage = 43\nexport type ChannelHideMessage = typeof ChannelHideMessage\nexport const ChannelMuteUser = 44\nexport type ChannelMuteUser = typeof ChannelMuteUser\nexport const OpenTimestamps = 1040\nexport type OpenTimestamps = typeof OpenTimestamps\nexport const GiftWrap = 1059\nexport type GiftWrap = typeof GiftWrap\nexport const Poll = 1068\nexport type Poll = typeof Poll\nexport const FileMetadata = 1063\nexport type FileMetadata = typeof FileMetadata\nexport const Comment = 1111\nexport type Comment = typeof Comment\nexport const LiveChatMessage = 1311\nexport type LiveChatMessage = typeof LiveChatMessage\nexport const Voice = 1222\nexport type Voice = typeof Voice\nexport const VoiceComment = 1244\nexport type VoiceComment = typeof VoiceComment\nexport const ProblemTracker = 1971\nexport type ProblemTracker = typeof ProblemTracker\nexport const Report = 1984\nexport type Report = typeof Report\nexport const Reporting = 1984\nexport type Reporting = typeof Reporting\nexport const Label = 1985\nexport type Label = typeof Label\nexport const CommunityPostApproval = 4550\nexport type CommunityPostApproval = typeof CommunityPostApproval\nexport const JobRequest = 5999\nexport type JobRequest = typeof JobRequest\nexport const JobResult = 6999\nexport type JobResult = typeof JobResult\nexport const JobFeedback = 7000\nexport type JobFeedback = typeof JobFeedback\nexport const ZapGoal = 9041\nexport type ZapGoal = typeof ZapGoal\nexport const ZapRequest = 9734\nexport type ZapRequest = typeof ZapRequest\nexport const Zap = 9735\nexport type Zap = typeof Zap\nexport const Highlights = 9802\nexport type Highlights = typeof Highlights\nexport const PollResponse = 1018\nexport type PollResponse = typeof PollResponse\nexport const Mutelist = 10000\nexport type Mutelist = typeof Mutelist\nexport const Pinlist = 10001\nexport type Pinlist = typeof Pinlist\nexport const RelayList = 10002\nexport type RelayList = typeof RelayList\nexport const BookmarkList = 10003\nexport type BookmarkList = typeof BookmarkList\nexport const CommunitiesList = 10004\nexport type CommunitiesList = typeof CommunitiesList\nexport const PublicChatsList = 10005\nexport type PublicChatsList = typeof PublicChatsList\nexport const BlockedRelaysList = 10006\nexport type BlockedRelaysList = typeof BlockedRelaysList\nexport const SearchRelaysList = 10007\nexport type SearchRelaysList = typeof SearchRelaysList\nexport const FavoriteRelays = 10012\nexport type FavoriteRelays = typeof FavoriteRelays\nexport const InterestsList = 10015\nexport type InterestsList = typeof InterestsList\nexport const UserEmojiList = 10030\nexport type UserEmojiList = typeof UserEmojiList\nexport const DirectMessageRelaysList = 10050\nexport type DirectMessageRelaysList = typeof DirectMessageRelaysList\nexport const FileServerPreference = 10096\nexport type FileServerPreference = typeof FileServerPreference\nexport const BlossomServerList = 10063\nexport type BlossomServerList = typeof BlossomServerList\nexport const NWCWalletInfo = 13194\nexport type NWCWalletInfo = typeof NWCWalletInfo\nexport const LightningPubRPC = 21000\nexport type LightningPubRPC = typeof LightningPubRPC\nexport const ClientAuth = 22242\nexport type ClientAuth = typeof ClientAuth\nexport const NWCWalletRequest = 23194\nexport type NWCWalletRequest = typeof NWCWalletRequest\nexport const NWCWalletResponse = 23195\nexport type NWCWalletResponse = typeof NWCWalletResponse\nexport const NostrConnect = 24133\nexport type NostrConnect = typeof NostrConnect\nexport const HTTPAuth = 27235\nexport type HTTPAuth = typeof HTTPAuth\nexport const Followsets = 30000\nexport type Followsets = typeof Followsets\nexport const Genericlists = 30001\nexport type Genericlists = typeof Genericlists\nexport const Relaysets = 30002\nexport type Relaysets = typeof Relaysets\nexport const Bookmarksets = 30003\nexport type Bookmarksets = typeof Bookmarksets\nexport const Curationsets = 30004\nexport type Curationsets = typeof Curationsets\nexport const ProfileBadges = 30008\nexport type ProfileBadges = typeof ProfileBadges\nexport const BadgeDefinition = 30009\nexport type BadgeDefinition = typeof BadgeDefinition\nexport const Interestsets = 30015\nexport type Interestsets = typeof Interestsets\nexport const CreateOrUpdateStall = 30017\nexport type CreateOrUpdateStall = typeof CreateOrUpdateStall\nexport const CreateOrUpdateProduct = 30018\nexport type CreateOrUpdateProduct = typeof CreateOrUpdateProduct\nexport const LongFormArticle = 30023\nexport type LongFormArticle = typeof LongFormArticle\nexport const DraftLong = 30024\nexport type DraftLong = typeof DraftLong\nexport const Emojisets = 30030\nexport type Emojisets = typeof Emojisets\nexport const Application = 30078\nexport type Application = typeof Application\nexport const LiveEvent = 30311\nexport type LiveEvent = typeof LiveEvent\nexport const UserStatuses = 30315\nexport type UserStatuses = typeof UserStatuses\nexport const ClassifiedListing = 30402\nexport type ClassifiedListing = typeof ClassifiedListing\nexport const DraftClassifiedListing = 30403\nexport type DraftClassifiedListing = typeof DraftClassifiedListing\nexport const Date = 31922\nexport type Date = typeof Date\nexport const Time = 31923\nexport type Time = typeof Time\nexport const Calendar = 31924\nexport type Calendar = typeof Calendar\nexport const CalendarEventRSVP = 31925\nexport type CalendarEventRSVP = typeof CalendarEventRSVP\nexport const RelayReview = 31987\nexport type RelayReview = typeof RelayReview\nexport const Handlerrecommendation = 31989\nexport type Handlerrecommendation = typeof Handlerrecommendation\nexport const Handlerinformation = 31990\nexport type Handlerinformation = typeof Handlerinformation\nexport const CommunityDefinition = 34550\nexport type CommunityDefinition = typeof CommunityDefinition\nexport const GroupMetadata = 39000\nexport type GroupMetadata = typeof GroupMetadata\n", "import { Event } from './core.ts'\nimport { isAddressableKind, isReplaceableKind } from './kinds.ts'\n\nexport type Filter = {\n  ids?: string[]\n  kinds?: number[]\n  authors?: string[]\n  since?: number\n  until?: number\n  limit?: number\n  search?: string\n  [key: `#${string}`]: string[] | undefined\n}\n\nexport function matchFilter(filter: Filter, event: Event): boolean {\n  if (filter.ids && filter.ids.indexOf(event.id) === -1) {\n    return false\n  }\n  if (filter.kinds && filter.kinds.indexOf(event.kind) === -1) {\n    return false\n  }\n  if (filter.authors && filter.authors.indexOf(event.pubkey) === -1) {\n    return false\n  }\n\n  for (let f in filter) {\n    if (f[0] === '#') {\n      let tagName = f.slice(1)\n      let values = filter[`#${tagName}`]\n      if (values && !event.tags.find(([t, v]) => t === f.slice(1) && values!.indexOf(v) !== -1)) return false\n    }\n  }\n\n  if (filter.since && event.created_at < filter.since) return false\n  if (filter.until && event.created_at > filter.until) return false\n\n  return true\n}\n\nexport function matchFilters(filters: Filter[], event: Event): boolean {\n  for (let i = 0; i < filters.length; i++) {\n    if (matchFilter(filters[i], event)) {\n      return true\n    }\n  }\n  return false\n}\n\nexport function mergeFilters(...filters: Filter[]): Filter {\n  let result: Filter = {}\n  for (let i = 0; i < filters.length; i++) {\n    let filter = filters[i]\n    Object.entries(filter).forEach(([property, values]) => {\n      if (property === 'kinds' || property === 'ids' || property === 'authors' || property[0] === '#') {\n        // @ts-ignore\n        result[property] = result[property] || []\n        // @ts-ignore\n        for (let v = 0; v < values.length; v++) {\n          // @ts-ignore\n          let value = values[v]\n          // @ts-ignore\n          if (!result[property].includes(value)) result[property].push(value)\n        }\n      }\n    })\n\n    if (filter.limit && (!result.limit || filter.limit > result.limit)) result.limit = filter.limit\n    if (filter.until && (!result.until || filter.until > result.until)) result.until = filter.until\n    if (filter.since && (!result.since || filter.since < result.since)) result.since = filter.since\n  }\n\n  return result\n}\n\n/**\n * Calculate the intrinsic limit of a filter.\n * This function returns a positive integer, or `Infinity` if there is no intrinsic limit.\n */\nexport function getFilterLimit(filter: Filter): number {\n  if (filter.ids && !filter.ids.length) return 0\n  if (filter.kinds && !filter.kinds.length) return 0\n  if (filter.authors && !filter.authors.length) return 0\n\n  for (const [key, value] of Object.entries(filter)) {\n    if (key[0] === '#' && Array.isArray(value) && !value.length) return 0\n  }\n\n  return Math.min(\n    // The `limit` property creates an artificial limit.\n    Math.max(0, filter.limit ?? Infinity),\n\n    // There can only be one event per `id`.\n    filter.ids?.length ?? Infinity,\n\n    // Replaceable events are limited by the number of authors and kinds.\n    filter.authors?.length && filter.kinds?.every(kind => isReplaceableKind(kind))\n      ? filter.authors.length * filter.kinds.length\n      : Infinity,\n\n    // Parameterized replaceable events are limited by the number of authors, kinds, and \"d\" tags.\n    filter.authors?.length && filter.kinds?.every(kind => isAddressableKind(kind)) && filter['#d']?.length\n      ? filter.authors.length * filter.kinds.length * filter['#d'].length\n      : Infinity,\n  )\n}\n", "export function getHex64(json: string, field: string): string {\n  let len = field.length + 3\n  let idx = json.indexOf(`\"${field}\":`) + len\n  let s = json.slice(idx).indexOf(`\"`) + idx + 1\n  return json.slice(s, s + 64)\n}\n\nexport function getInt(json: string, field: string): number {\n  let len = field.length\n  let idx = json.indexOf(`\"${field}\":`) + len + 3\n  let sliced = json.slice(idx)\n  let end = Math.min(sliced.indexOf(','), sliced.indexOf('}'))\n  return parseInt(sliced.slice(0, end), 10)\n}\n\nexport function getSubscriptionId(json: string): string | null {\n  let idx = json.slice(0, 22).indexOf(`\"EVENT\"`)\n  if (idx === -1) return null\n\n  let pstart = json.slice(idx + 7 + 1).indexOf(`\"`)\n  if (pstart === -1) return null\n  let start = idx + 7 + 1 + pstart\n\n  let pend = json.slice(start + 1, 80).indexOf(`\"`)\n  if (pend === -1) return null\n  let end = start + 1 + pend\n\n  return json.slice(start + 1, end)\n}\n\nexport function matchEventId(json: string, id: string): boolean {\n  return id === getHex64(json, 'id')\n}\n\nexport function matchEventPubkey(json: string, pubkey: string): boolean {\n  return pubkey === getHex64(json, 'pubkey')\n}\n\nexport function matchEventKind(json: string, kind: number): boolean {\n  return kind === getInt(json, 'kind')\n}\n", "import { EventTemplate } from './core.ts'\nimport { ClientAuth } from './kinds.ts'\n\n/**\n * creates an EventTemplate for an AUTH event to be signed.\n */\nexport function makeAuthEvent(relayURL: string, challenge: string): EventTemplate {\n  return {\n    kind: ClientAuth,\n    created_at: Math.floor(Date.now() / 1000),\n    tags: [\n      ['relay', relayURL],\n      ['challenge', challenge],\n    ],\n    content: '',\n  }\n}\n", "import { verifiedSymbol, type Event, type Nostr, VerifiedEvent } from './core.ts'\n\nexport async function yieldThread() {\n  return new Promise<void>((resolve, reject) => {\n    try {\n      // Check if MessageChannel is available\n      if (typeof MessageChannel !== 'undefined') {\n        const ch = new MessageChannel()\n        const handler = () => {\n          // @ts-ignore (typescript thinks this property should be called `removeListener`, but in fact it's `removeEventListener`)\n          ch.port1.removeEventListener('message', handler)\n          resolve()\n        }\n        // @ts-ignore (typescript thinks this property should be called `addListener`, but in fact it's `addEventListener`)\n        ch.port1.addEventListener('message', handler)\n        ch.port2.postMessage(0)\n        ch.port1.start()\n      } else {\n        if (typeof setImmediate !== 'undefined') {\n          setImmediate(resolve)\n        } else if (typeof setTimeout !== 'undefined') {\n          setTimeout(resolve, 0)\n        } else {\n          // Last resort - resolve immediately\n          resolve()\n        }\n      }\n    } catch (e) {\n      console.error('during yield: ', e)\n      reject(e)\n    }\n  })\n}\n\nexport const alwaysTrue: Nostr['verifyEvent'] = (t: Event): t is VerifiedEvent => {\n  t[verifiedSymbol] = true\n  return true\n}\n", "/* global WebSocket */\n\nimport type { Event, EventTemplate, VerifiedEvent, Nostr, NostrEvent } from './core.ts'\nimport { matchFilters, type Filter } from './filter.ts'\nimport { getHex64, getSubscriptionId } from './fakejson.ts'\nimport { Queue, normalizeURL } from './utils.ts'\nimport { makeAuthEvent } from './nip42.ts'\nimport { yieldThread } from './helpers.ts'\n\ntype RelayWebSocket = WebSocket & {\n  ping?(): void\n  on?(event: 'pong', listener: () => void): any\n}\n\nexport type AbstractRelayConstructorOptions = {\n  verifyEvent: Nostr['verifyEvent']\n  websocketImplementation?: typeof WebSocket\n  enablePing?: boolean\n  enableReconnect?: boolean\n}\n\nexport class SendingOnClosedConnection extends Error {\n  constructor(message: string, relay: string) {\n    super(`Tried to send message '${message} on a closed connection to ${relay}.`)\n    this.name = 'SendingOnClosedConnection'\n  }\n}\n\nexport class AbstractRelay {\n  public readonly url: string\n  private _connected: boolean = false\n\n  public onclose: (() => void) | null = null\n  public onnotice: (msg: string) => void = msg => console.debug(`NOTICE from ${this.url}: ${msg}`)\n  public onauth: undefined | ((evt: EventTemplate) => Promise<VerifiedEvent>)\n\n  public baseEoseTimeout: number = 4400\n  public publishTimeout: number = 4400\n  public pingFrequency: number = 29000\n  public pingTimeout: number = 20000\n  public resubscribeBackoff: number[] = [10000, 10000, 10000, 20000, 20000, 30000, 60000]\n  public openSubs: Map<string, Subscription> = new Map()\n  public enablePing: boolean | undefined\n  public enableReconnect: boolean\n  private reconnectTimeoutHandle: ReturnType<typeof setTimeout> | undefined\n  private pingIntervalHandle: ReturnType<typeof setInterval> | undefined\n  private reconnectAttempts: number = 0\n  private closedIntentionally: boolean = false\n\n  private connectionPromise: Promise<void> | undefined\n  private openCountRequests = new Map<string, CountResolver>()\n  private openEventPublishes = new Map<string, EventPublishResolver>()\n  private ws: RelayWebSocket | undefined\n  private incomingMessageQueue = new Queue<string>()\n  private queueRunning = false\n  private challenge: string | undefined\n  private authPromise: Promise<string> | undefined\n  private serial: number = 0\n  private verifyEvent: Nostr['verifyEvent']\n\n  private _WebSocket: typeof WebSocket\n\n  constructor(url: string, opts: AbstractRelayConstructorOptions) {\n    this.url = normalizeURL(url)\n    this.verifyEvent = opts.verifyEvent\n    this._WebSocket = opts.websocketImplementation || WebSocket\n    this.enablePing = opts.enablePing\n    this.enableReconnect = opts.enableReconnect || false\n  }\n\n  static async connect(\n    url: string,\n    opts: AbstractRelayConstructorOptions & Parameters<AbstractRelay['connect']>[0],\n  ): Promise<AbstractRelay> {\n    const relay = new AbstractRelay(url, opts)\n    await relay.connect(opts)\n    return relay\n  }\n\n  private closeAllSubscriptions(reason: string) {\n    for (let [_, sub] of this.openSubs) {\n      sub.close(reason)\n    }\n    this.openSubs.clear()\n\n    for (let [_, ep] of this.openEventPublishes) {\n      ep.reject(new Error(reason))\n    }\n    this.openEventPublishes.clear()\n\n    for (let [_, cr] of this.openCountRequests) {\n      cr.reject(new Error(reason))\n    }\n    this.openCountRequests.clear()\n  }\n\n  public get connected(): boolean {\n    return this._connected\n  }\n\n  private async reconnect(): Promise<void> {\n    const backoff = this.resubscribeBackoff[Math.min(this.reconnectAttempts, this.resubscribeBackoff.length - 1)]\n    this.reconnectAttempts++\n\n    this.reconnectTimeoutHandle = setTimeout(async () => {\n      try {\n        await this.connect()\n      } catch (err) {\n        // this will be called again through onclose/onerror\n      }\n    }, backoff)\n  }\n\n  private handleHardClose(reason: string) {\n    if (this.pingIntervalHandle) {\n      clearInterval(this.pingIntervalHandle)\n      this.pingIntervalHandle = undefined\n    }\n\n    this._connected = false\n    this.connectionPromise = undefined\n\n    const wasIntentional = this.closedIntentionally\n    this.closedIntentionally = false // reset for next time\n\n    this.onclose?.()\n\n    if (this.enableReconnect && !wasIntentional) {\n      this.reconnect()\n    } else {\n      this.closeAllSubscriptions(reason)\n    }\n  }\n\n  public async connect(opts?: { timeout?: number; abort?: AbortSignal }): Promise<void> {\n    let connectionTimeoutHandle: ReturnType<typeof setTimeout> | undefined\n\n    if (this.connectionPromise) return this.connectionPromise\n\n    this.challenge = undefined\n    this.authPromise = undefined\n    this.connectionPromise = new Promise((resolve, reject) => {\n      if (opts?.timeout) {\n        connectionTimeoutHandle = setTimeout(() => {\n          reject('connection timed out')\n          this.connectionPromise = undefined\n          this.onclose?.()\n          this.closeAllSubscriptions('relay connection timed out')\n        }, opts.timeout)\n      }\n\n      if (opts?.abort) {\n        opts.abort.onabort = reject\n      }\n\n      try {\n        this.ws = new this._WebSocket(this.url)\n      } catch (err) {\n        clearTimeout(connectionTimeoutHandle)\n        reject(err)\n        return\n      }\n\n      this.ws.onopen = () => {\n        if (this.reconnectTimeoutHandle) {\n          clearTimeout(this.reconnectTimeoutHandle)\n          this.reconnectTimeoutHandle = undefined\n        }\n        clearTimeout(connectionTimeoutHandle)\n        this._connected = true\n\n        const isReconnection = this.reconnectAttempts > 0\n        this.reconnectAttempts = 0\n\n        // resubscribe to all open subscriptions\n        for (const sub of this.openSubs.values()) {\n          sub.eosed = false\n          if (isReconnection) {\n            for (let f = 0; f < sub.filters.length; f++) {\n              if (sub.lastEmitted) {\n                sub.filters[f].since = sub.lastEmitted + 1\n              }\n            }\n          }\n          sub.fire()\n        }\n\n        if (this.enablePing) {\n          this.pingIntervalHandle = setInterval(() => this.pingpong(), this.pingFrequency)\n        }\n        resolve()\n      }\n\n      this.ws.onerror = ev => {\n        clearTimeout(connectionTimeoutHandle)\n        reject((ev as any).message || 'websocket error')\n        this.handleHardClose('relay connection errored')\n      }\n\n      this.ws.onclose = ev => {\n        clearTimeout(connectionTimeoutHandle)\n        reject((ev as any).message || 'websocket closed')\n        this.handleHardClose('relay connection closed')\n      }\n\n      this.ws.onmessage = this._onmessage.bind(this)\n    })\n\n    return this.connectionPromise\n  }\n\n  private waitForPingPong() {\n    return new Promise(resolve => {\n      // listen for pong\n      ;(this.ws as any).once('pong', () => resolve(true))\n      // send a ping\n      this.ws!.ping!()\n    })\n  }\n\n  private waitForDummyReq() {\n    return new Promise((resolve, reject) => {\n      if (!this.connectionPromise) return reject(new Error(`no connection to ${this.url}, can't ping`))\n\n      // make a dummy request with expected empty eose reply\n      // [\"REQ\", \"_\", {\"ids\":[\"aaaa...aaaa\"], \"limit\": 0}]\n      try {\n        const sub = this.subscribe(\n          [{ ids: ['aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'], limit: 0 }],\n          {\n            label: 'forced-ping',\n            oneose: () => {\n              resolve(true)\n              sub.close()\n            },\n            onclose() {\n              // if we get a CLOSED it's because the relay is alive\n              resolve(true)\n            },\n            eoseTimeout: this.pingTimeout + 1000,\n          },\n        )\n      } catch (err) {\n        reject(err)\n      }\n    })\n  }\n\n  // nodejs requires this magic here to ensure connections are closed when internet goes off and stuff\n  // in browsers it's done automatically. see https://github.com/nbd-wtf/nostr-tools/issues/491\n  private async pingpong() {\n    // if the websocket is connected\n    if (this.ws?.readyState === 1) {\n      // wait for either a ping-pong reply or a timeout\n      const result = await Promise.any([\n        // browsers don't have ping so use a dummy req\n        this.ws && this.ws.ping && (this.ws as any).once ? this.waitForPingPong() : this.waitForDummyReq(),\n        new Promise(res => setTimeout(() => res(false), this.pingTimeout)),\n      ])\n\n      if (!result) {\n        // pingpong closing socket\n        if (this.ws?.readyState === this._WebSocket.OPEN) {\n          this.ws?.close()\n        }\n      }\n    }\n  }\n\n  private async runQueue() {\n    this.queueRunning = true\n    while (true) {\n      if (false === this.handleNext()) {\n        break\n      }\n      await yieldThread()\n    }\n    this.queueRunning = false\n  }\n\n  private handleNext(): undefined | false {\n    const json = this.incomingMessageQueue.dequeue()\n    if (!json) {\n      return false\n    }\n\n    // shortcut EVENT sub\n    const subid = getSubscriptionId(json)\n    if (subid) {\n      const so = this.openSubs.get(subid as string)\n      if (!so) {\n        // this is an EVENT message, but for a subscription we don't have, so just stop here\n        return\n      }\n\n      // this will be called only when this message is a EVENT message for a subscription we have\n      // we do this before parsing the JSON to not have to do that for duplicate events\n      //   since JSON parsing is slow\n      const id = getHex64(json, 'id')\n      const alreadyHave = so.alreadyHaveEvent?.(id)\n\n      // notify any interested client that the relay has this event\n      // (do this after alreadyHaveEvent() because the client may rely on this to answer that)\n      so.receivedEvent?.(this, id)\n\n      if (alreadyHave) {\n        // if we had already seen this event we can just stop here\n        return\n      }\n    }\n\n    try {\n      let data = JSON.parse(json)\n      // we won't do any checks against the data since all failures (i.e. invalid messages from relays)\n      // will naturally be caught by the encompassing try..catch block\n\n      switch (data[0]) {\n        case 'EVENT': {\n          const so = this.openSubs.get(data[1] as string) as Subscription\n          const event = data[2] as NostrEvent\n          if (this.verifyEvent(event) && matchFilters(so.filters, event)) {\n            so.onevent(event)\n          }\n          if (!so.lastEmitted || so.lastEmitted < event.created_at) so.lastEmitted = event.created_at\n          return\n        }\n        case 'COUNT': {\n          const id: string = data[1]\n          const payload = data[2] as { count: number }\n          const cr = this.openCountRequests.get(id) as CountResolver\n          if (cr) {\n            cr.resolve(payload.count)\n            this.openCountRequests.delete(id)\n          }\n          return\n        }\n        case 'EOSE': {\n          const so = this.openSubs.get(data[1] as string)\n          if (!so) return\n          so.receivedEose()\n          return\n        }\n        case 'OK': {\n          const id: string = data[1]\n          const ok: boolean = data[2]\n          const reason: string = data[3]\n          const ep = this.openEventPublishes.get(id) as EventPublishResolver\n          if (ep) {\n            clearTimeout(ep.timeout)\n            if (ok) ep.resolve(reason)\n            else ep.reject(new Error(reason))\n            this.openEventPublishes.delete(id)\n          }\n          return\n        }\n        case 'CLOSED': {\n          const id: string = data[1]\n          const so = this.openSubs.get(id)\n          if (!so) return\n          so.closed = true\n          so.close(data[2] as string)\n          return\n        }\n        case 'NOTICE': {\n          this.onnotice(data[1] as string)\n          return\n        }\n        case 'AUTH': {\n          this.challenge = data[1] as string\n          if (this.onauth) {\n            this.auth(this.onauth)\n          }\n          return\n        }\n        default: {\n          const so = this.openSubs.get(data[1])\n          so?.oncustom?.(data)\n          return\n        }\n      }\n    } catch (err) {\n      return\n    }\n  }\n\n  public async send(message: string) {\n    if (!this.connectionPromise) throw new SendingOnClosedConnection(message, this.url)\n\n    this.connectionPromise.then(() => {\n      this.ws?.send(message)\n    })\n  }\n\n  public async auth(signAuthEvent: (evt: EventTemplate) => Promise<VerifiedEvent>): Promise<string> {\n    const challenge = this.challenge\n    if (!challenge) throw new Error(\"can't perform auth, no challenge was received\")\n    if (this.authPromise) return this.authPromise\n\n    this.authPromise = new Promise<string>(async (resolve, reject) => {\n      try {\n        let evt = await signAuthEvent(makeAuthEvent(this.url, challenge))\n        let timeout = setTimeout(() => {\n          let ep = this.openEventPublishes.get(evt.id) as EventPublishResolver\n          if (ep) {\n            ep.reject(new Error('auth timed out'))\n            this.openEventPublishes.delete(evt.id)\n          }\n        }, this.publishTimeout)\n        this.openEventPublishes.set(evt.id, { resolve, reject, timeout })\n        this.send('[\"AUTH\",' + JSON.stringify(evt) + ']')\n      } catch (err) {\n        console.warn('subscribe auth function failed:', err)\n      }\n    })\n    return this.authPromise\n  }\n\n  public async publish(event: Event): Promise<string> {\n    const ret = new Promise<string>((resolve, reject) => {\n      const timeout = setTimeout(() => {\n        const ep = this.openEventPublishes.get(event.id) as EventPublishResolver\n        if (ep) {\n          ep.reject(new Error('publish timed out'))\n          this.openEventPublishes.delete(event.id)\n        }\n      }, this.publishTimeout)\n      this.openEventPublishes.set(event.id, { resolve, reject, timeout })\n    })\n    this.send('[\"EVENT\",' + JSON.stringify(event) + ']')\n    return ret\n  }\n\n  public async count(filters: Filter[], params: { id?: string | null }): Promise<number> {\n    this.serial++\n    const id = params?.id || 'count:' + this.serial\n    const ret = new Promise<number>((resolve, reject) => {\n      this.openCountRequests.set(id, { resolve, reject })\n    })\n    this.send('[\"COUNT\",\"' + id + '\",' + JSON.stringify(filters).substring(1))\n    return ret\n  }\n\n  public subscribe(\n    filters: Filter[],\n    params: Partial<SubscriptionParams> & { label?: string; id?: string },\n  ): Subscription {\n    const sub = this.prepareSubscription(filters, params)\n    sub.fire()\n\n    if (params.abort) {\n      params.abort.onabort = () => sub.close(String(params.abort!.reason || '<aborted>'))\n    }\n\n    return sub\n  }\n\n  public prepareSubscription(\n    filters: Filter[],\n    params: Partial<SubscriptionParams> & { label?: string; id?: string },\n  ): Subscription {\n    this.serial++\n    const id = params.id || (params.label ? params.label + ':' : 'sub:') + this.serial\n    const subscription = new Subscription(this, id, filters, params)\n    this.openSubs.set(id, subscription)\n    return subscription\n  }\n\n  public close() {\n    this.closedIntentionally = true\n    if (this.reconnectTimeoutHandle) {\n      clearTimeout(this.reconnectTimeoutHandle)\n      this.reconnectTimeoutHandle = undefined\n    }\n    if (this.pingIntervalHandle) {\n      clearInterval(this.pingIntervalHandle)\n      this.pingIntervalHandle = undefined\n    }\n    this.closeAllSubscriptions('relay connection closed by us')\n    this._connected = false\n    this.onclose?.()\n    if (this.ws?.readyState === this._WebSocket.OPEN) {\n      this.ws?.close()\n    }\n  }\n\n  // this is the function assigned to this.ws.onmessage\n  // it's exposed for testing and debugging purposes\n  public _onmessage(ev: MessageEvent<any>) {\n    this.incomingMessageQueue.enqueue(ev.data as string)\n    if (!this.queueRunning) {\n      this.runQueue()\n    }\n  }\n}\n\nexport class Subscription {\n  public readonly relay: AbstractRelay\n  public readonly id: string\n\n  public lastEmitted: number | undefined\n  public closed: boolean = false\n  public eosed: boolean = false\n  public filters: Filter[]\n  public alreadyHaveEvent: ((id: string) => boolean) | undefined\n  public receivedEvent: ((relay: AbstractRelay, id: string) => void) | undefined\n\n  public onevent: (evt: Event) => void\n  public oneose: (() => void) | undefined\n  public onclose: ((reason: string) => void) | undefined\n\n  // will get any messages that have this subscription id as their second item and are not default standard\n  public oncustom: ((msg: string[]) => void) | undefined\n\n  public eoseTimeout: number\n  private eoseTimeoutHandle: ReturnType<typeof setTimeout> | undefined\n\n  constructor(relay: AbstractRelay, id: string, filters: Filter[], params: SubscriptionParams) {\n    if (filters.length === 0) throw new Error(\"subscription can't be created with zero filters\")\n\n    this.relay = relay\n    this.filters = filters\n    this.id = id\n    this.alreadyHaveEvent = params.alreadyHaveEvent\n    this.receivedEvent = params.receivedEvent\n    this.eoseTimeout = params.eoseTimeout || relay.baseEoseTimeout\n\n    this.oneose = params.oneose\n    this.onclose = params.onclose\n    this.onevent =\n      params.onevent ||\n      (event => {\n        console.warn(\n          `onevent() callback not defined for subscription '${this.id}' in relay ${this.relay.url}. event received:`,\n          event,\n        )\n      })\n  }\n\n  public fire() {\n    this.relay.send('[\"REQ\",\"' + this.id + '\",' + JSON.stringify(this.filters).substring(1))\n\n    // only now we start counting the eoseTimeout\n    this.eoseTimeoutHandle = setTimeout(this.receivedEose.bind(this), this.eoseTimeout)\n  }\n\n  public receivedEose() {\n    if (this.eosed) return\n    clearTimeout(this.eoseTimeoutHandle)\n    this.eosed = true\n    this.oneose?.()\n  }\n\n  public close(reason: string = 'closed by caller') {\n    if (!this.closed && this.relay.connected) {\n      // if the connection was closed by the user calling .close() we will send a CLOSE message\n      // otherwise this._open will be already set to false so we will skip this\n      try {\n        this.relay.send('[\"CLOSE\",' + JSON.stringify(this.id) + ']')\n      } catch (err) {\n        if (err instanceof SendingOnClosedConnection) {\n          /* doesn't matter, it's ok */\n        } else {\n          throw err\n        }\n      }\n      this.closed = true\n    }\n    this.relay.openSubs.delete(this.id)\n    this.onclose?.(reason)\n  }\n}\n\nexport type SubscriptionParams = {\n  onevent?: (evt: Event) => void\n  oneose?: () => void\n  onclose?: (reason: string) => void\n  alreadyHaveEvent?: (id: string) => boolean\n  receivedEvent?: (relay: AbstractRelay, id: string) => void\n  eoseTimeout?: number\n  abort?: AbortSignal\n}\n\nexport type CountResolver = {\n  resolve: (count: number) => void\n  reject: (err: Error) => void\n}\n\nexport type EventPublishResolver = {\n  resolve: (reason: string) => void\n  reject: (err: Error) => void\n  timeout: ReturnType<typeof setTimeout>\n}\n", "/* global WebSocket */\n\nimport { verifyEvent } from './pure.ts'\nimport { AbstractRelay, type AbstractRelayConstructorOptions } from './abstract-relay.ts'\n\nvar _WebSocket: typeof WebSocket\n\ntry {\n  _WebSocket = WebSocket\n} catch {}\n\nexport function useWebSocketImplementation(websocketImplementation: any) {\n  _WebSocket = websocketImplementation\n}\n\nexport class Relay extends AbstractRelay {\n  constructor(url: string, options?: Pick<AbstractRelayConstructorOptions, 'enablePing' | 'enableReconnect'>) {\n    super(url, { verifyEvent, websocketImplementation: _WebSocket, ...options })\n  }\n\n  static async connect(\n    url: string,\n    options?: Pick<AbstractRelayConstructorOptions, 'enablePing' | 'enableReconnect'>,\n  ): Promise<Relay> {\n    const relay = new Relay(url, options)\n    await relay.connect()\n    return relay\n  }\n}\n\nexport type RelayRecord = Record<string, { read: boolean; write: boolean }>\n\nexport * from './abstract-relay.ts'\n"],
+  "mappings": "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",
+  "names": ["bytesToHex", "hexToBytes", "i", "target", "bytesToHex", "hexToBytes", "i"]
 }
Index: package/lib/cjs/signer.js.map
===================================================================
--- package/lib/cjs/signer.js.map
+++ package/lib/cjs/signer.js.map
@@ -1,7 +1,7 @@
 {
   "version": 3,
   "sources": ["../../signer.ts", "../../pure.ts", "../../core.ts", "../../utils.ts"],
-  "sourcesContent": ["import { EventTemplate, VerifiedEvent } from './core.ts'\nimport { finalizeEvent, getPublicKey } from './pure.ts'\n\nexport interface Signer {\n  getPublicKey(): Promise<string>\n  signEvent(event: EventTemplate): Promise<VerifiedEvent>\n}\n\nexport class PlainKeySigner implements Signer {\n  private secretKey: Uint8Array\n\n  constructor(secretKey: Uint8Array) {\n    this.secretKey = secretKey\n  }\n\n  async getPublicKey(): Promise<string> {\n    return getPublicKey(this.secretKey)\n  }\n\n  async signEvent(event: EventTemplate): Promise<VerifiedEvent> {\n    return finalizeEvent(event, this.secretKey)\n  }\n}\n", "import { schnorr } from '@noble/curves/secp256k1'\nimport { bytesToHex } from '@noble/hashes/utils'\nimport { Nostr, Event, EventTemplate, UnsignedEvent, VerifiedEvent, verifiedSymbol, validateEvent } from './core.ts'\nimport { sha256 } from '@noble/hashes/sha256'\n\nimport { utf8Encoder } from './utils.ts'\n\nclass JS implements Nostr {\n  generateSecretKey(): Uint8Array {\n    return schnorr.utils.randomPrivateKey()\n  }\n  getPublicKey(secretKey: Uint8Array): string {\n    return bytesToHex(schnorr.getPublicKey(secretKey))\n  }\n  finalizeEvent(t: EventTemplate, secretKey: Uint8Array): VerifiedEvent {\n    const event = t as VerifiedEvent\n    event.pubkey = bytesToHex(schnorr.getPublicKey(secretKey))\n    event.id = getEventHash(event)\n    event.sig = bytesToHex(schnorr.sign(getEventHash(event), secretKey))\n    event[verifiedSymbol] = true\n    return event\n  }\n  verifyEvent(event: Event): event is VerifiedEvent {\n    if (typeof event[verifiedSymbol] === 'boolean') return event[verifiedSymbol]\n\n    const hash = getEventHash(event)\n    if (hash !== event.id) {\n      event[verifiedSymbol] = false\n      return false\n    }\n\n    try {\n      const valid = schnorr.verify(event.sig, hash, event.pubkey)\n      event[verifiedSymbol] = valid\n      return valid\n    } catch (err) {\n      event[verifiedSymbol] = false\n      return false\n    }\n  }\n}\n\nexport function serializeEvent(evt: UnsignedEvent): string {\n  if (!validateEvent(evt)) throw new Error(\"can't serialize event with wrong or missing properties\")\n  return JSON.stringify([0, evt.pubkey, evt.created_at, evt.kind, evt.tags, evt.content])\n}\n\nexport function getEventHash(event: UnsignedEvent): string {\n  let eventHash = sha256(utf8Encoder.encode(serializeEvent(event)))\n  return bytesToHex(eventHash)\n}\n\nconst i: JS = new JS()\n\nexport const generateSecretKey = i.generateSecretKey\nexport const getPublicKey = i.getPublicKey\nexport const finalizeEvent = i.finalizeEvent\nexport const verifyEvent = i.verifyEvent\nexport * from './core.ts'\n", "export interface Nostr {\n  generateSecretKey(): Uint8Array\n  getPublicKey(secretKey: Uint8Array): string\n  finalizeEvent(event: EventTemplate, secretKey: Uint8Array): VerifiedEvent\n  verifyEvent(event: Event): event is VerifiedEvent\n}\n\n/** Designates a verified event signature. */\nexport const verifiedSymbol = Symbol('verified')\n\nexport type NostrEvent = {\n  kind: number\n  tags: string[][]\n  content: string\n  created_at: number\n  pubkey: string\n  id: string\n  sig: string\n  [verifiedSymbol]?: boolean\n}\n\nexport type Event = NostrEvent\nexport type EventTemplate = Pick<Event, 'kind' | 'tags' | 'content' | 'created_at'>\nexport type UnsignedEvent = Pick<Event, 'kind' | 'tags' | 'content' | 'created_at' | 'pubkey'>\n\n/** An event whose signature has been verified. */\nexport interface VerifiedEvent extends Event {\n  [verifiedSymbol]: true\n}\n\nconst isRecord = (obj: unknown): obj is Record<string, unknown> => obj instanceof Object\n\nexport function validateEvent<T>(event: T): event is T & UnsignedEvent {\n  if (!isRecord(event)) return false\n  if (typeof event.kind !== 'number') return false\n  if (typeof event.content !== 'string') return false\n  if (typeof event.created_at !== 'number') return false\n  if (typeof event.pubkey !== 'string') return false\n  if (!event.pubkey.match(/^[a-f0-9]{64}$/)) return false\n\n  if (!Array.isArray(event.tags)) return false\n  for (let i = 0; i < event.tags.length; i++) {\n    let tag = event.tags[i]\n    if (!Array.isArray(tag)) return false\n    for (let j = 0; j < tag.length; j++) {\n      if (typeof tag[j] !== 'string') return false\n    }\n  }\n\n  return true\n}\n\n/**\n * Sort events in reverse-chronological order by the `created_at` timestamp,\n * and then by the event `id` (lexicographically) in case of ties.\n * This mutates the array.\n */\nexport function sortEvents(events: Event[]): Event[] {\n  return events.sort((a: NostrEvent, b: NostrEvent): number => {\n    if (a.created_at !== b.created_at) {\n      return b.created_at - a.created_at\n    }\n    return a.id.localeCompare(b.id)\n  })\n}\n", "import type { Event } from './core.ts'\n\nexport const utf8Decoder: TextDecoder = new TextDecoder('utf-8')\nexport const utf8Encoder: TextEncoder = new TextEncoder()\n\nexport { bytesToHex, hexToBytes } from '@noble/hashes/utils'\n\nexport function normalizeURL(url: string): string {\n  try {\n    if (url.indexOf('://') === -1) url = 'wss://' + url\n    let p = new URL(url)\n    if (p.protocol === 'http:') p.protocol = 'ws:'\n    else if (p.protocol === 'https:') p.protocol = 'wss:'\n    p.pathname = p.pathname.replace(/\\/+/g, '/')\n    if (p.pathname.endsWith('/')) p.pathname = p.pathname.slice(0, -1)\n    if ((p.port === '80' && p.protocol === 'ws:') || (p.port === '443' && p.protocol === 'wss:')) p.port = ''\n    p.searchParams.sort()\n    p.hash = ''\n    return p.toString()\n  } catch (e) {\n    throw new Error(`Invalid URL: ${url}`)\n  }\n}\n\nexport function insertEventIntoDescendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return b.created_at - event.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function insertEventIntoAscendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return event.created_at - b.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function binarySearch<T>(arr: T[], compare: (b: T) => number): [number, boolean] {\n  let start = 0\n  let end = arr.length - 1\n\n  while (start <= end) {\n    const mid = Math.floor((start + end) / 2)\n    const cmp = compare(arr[mid])\n\n    if (cmp === 0) {\n      return [mid, true]\n    }\n\n    if (cmp < 0) {\n      end = mid - 1\n    } else {\n      start = mid + 1\n    }\n  }\n\n  return [start, false]\n}\n\nexport class QueueNode<V> {\n  public value: V\n  public next: QueueNode<V> | null = null\n  public prev: QueueNode<V> | null = null\n\n  constructor(message: V) {\n    this.value = message\n  }\n}\n\nexport class Queue<V> {\n  public first: QueueNode<V> | null\n  public last: QueueNode<V> | null\n\n  constructor() {\n    this.first = null\n    this.last = null\n  }\n\n  enqueue(value: V): boolean {\n    const newNode = new QueueNode(value)\n    if (!this.last) {\n      // list is empty\n      this.first = newNode\n      this.last = newNode\n    } else if (this.last === this.first) {\n      // list has a single element\n      this.last = newNode\n      this.last.prev = this.first\n      this.first.next = newNode\n    } else {\n      // list has elements, add as last\n      newNode.prev = this.last\n      this.last.next = newNode\n      this.last = newNode\n    }\n    return true\n  }\n\n  dequeue(): V | null {\n    if (!this.first) return null\n\n    if (this.first === this.last) {\n      const target = this.first\n      this.first = null\n      this.last = null\n      return target.value\n    }\n\n    const target = this.first\n    this.first = target.next\n    if (this.first) {\n      this.first.prev = null // fix: clean up prev pointer\n    }\n\n    return target.value\n  }\n}\n"],
-  "mappings": ";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,uBAAwB;AACxB,IAAAA,gBAA2B;;;ACOpB,IAAM,iBAAiB,OAAO,UAAU;AAsB/C,IAAM,WAAW,CAAC,QAAiD,eAAe;AAE3E,SAAS,cAAiB,OAAsC;AACrE,MAAI,CAAC,SAAS,KAAK;AAAG,WAAO;AAC7B,MAAI,OAAO,MAAM,SAAS;AAAU,WAAO;AAC3C,MAAI,OAAO,MAAM,YAAY;AAAU,WAAO;AAC9C,MAAI,OAAO,MAAM,eAAe;AAAU,WAAO;AACjD,MAAI,OAAO,MAAM,WAAW;AAAU,WAAO;AAC7C,MAAI,CAAC,MAAM,OAAO,MAAM,gBAAgB;AAAG,WAAO;AAElD,MAAI,CAAC,MAAM,QAAQ,MAAM,IAAI;AAAG,WAAO;AACvC,WAASC,KAAI,GAAGA,KAAI,MAAM,KAAK,QAAQA,MAAK;AAC1C,QAAI,MAAM,MAAM,KAAKA;AACrB,QAAI,CAAC,MAAM,QAAQ,GAAG;AAAG,aAAO;AAChC,aAAS,IAAI,GAAG,IAAI,IAAI,QAAQ,KAAK;AACnC,UAAI,OAAO,IAAI,OAAO;AAAU,eAAO;AAAA,IACzC;AAAA,EACF;AAEA,SAAO;AACT;;;AD/CA,oBAAuB;;;AEEvB,mBAAuC;AAHhC,IAAM,cAA2B,IAAI,YAAY,OAAO;AACxD,IAAM,cAA2B,IAAI,YAAY;;;AFIxD,IAAM,KAAN,MAA0B;AAAA,EACxB,oBAAgC;AAC9B,WAAO,yBAAQ,MAAM,iBAAiB;AAAA,EACxC;AAAA,EACA,aAAa,WAA+B;AAC1C,eAAO,0BAAW,yBAAQ,aAAa,SAAS,CAAC;AAAA,EACnD;AAAA,EACA,cAAc,GAAkB,WAAsC;AACpE,UAAM,QAAQ;AACd,UAAM,aAAS,0BAAW,yBAAQ,aAAa,SAAS,CAAC;AACzD,UAAM,KAAK,aAAa,KAAK;AAC7B,UAAM,UAAM,0BAAW,yBAAQ,KAAK,aAAa,KAAK,GAAG,SAAS,CAAC;AACnE,UAAM,kBAAkB;AACxB,WAAO;AAAA,EACT;AAAA,EACA,YAAY,OAAsC;AAChD,QAAI,OAAO,MAAM,oBAAoB;AAAW,aAAO,MAAM;AAE7D,UAAM,OAAO,aAAa,KAAK;AAC/B,QAAI,SAAS,MAAM,IAAI;AACrB,YAAM,kBAAkB;AACxB,aAAO;AAAA,IACT;AAEA,QAAI;AACF,YAAM,QAAQ,yBAAQ,OAAO,MAAM,KAAK,MAAM,MAAM,MAAM;AAC1D,YAAM,kBAAkB;AACxB,aAAO;AAAA,IACT,SAAS,KAAP;AACA,YAAM,kBAAkB;AACxB,aAAO;AAAA,IACT;AAAA,EACF;AACF;AAEO,SAAS,eAAe,KAA4B;AACzD,MAAI,CAAC,cAAc,GAAG;AAAG,UAAM,IAAI,MAAM,wDAAwD;AACjG,SAAO,KAAK,UAAU,CAAC,GAAG,IAAI,QAAQ,IAAI,YAAY,IAAI,MAAM,IAAI,MAAM,IAAI,OAAO,CAAC;AACxF;AAEO,SAAS,aAAa,OAA8B;AACzD,MAAI,gBAAY,sBAAO,YAAY,OAAO,eAAe,KAAK,CAAC,CAAC;AAChE,aAAO,0BAAW,SAAS;AAC7B;AAEA,IAAM,IAAQ,IAAI,GAAG;AAEd,IAAM,oBAAoB,EAAE;AAC5B,IAAM,eAAe,EAAE;AACvB,IAAM,gBAAgB,EAAE;AACxB,IAAM,cAAc,EAAE;;;ADjDtB,IAAM,iBAAN,MAAuC;AAAA,EACpC;AAAA,EAER,YAAY,WAAuB;AACjC,SAAK,YAAY;AAAA,EACnB;AAAA,EAEA,MAAM,eAAgC;AACpC,WAAO,aAAa,KAAK,SAAS;AAAA,EACpC;AAAA,EAEA,MAAM,UAAU,OAA8C;AAC5D,WAAO,cAAc,OAAO,KAAK,SAAS;AAAA,EAC5C;AACF;",
+  "sourcesContent": ["import { EventTemplate, VerifiedEvent } from './core.ts'\nimport { finalizeEvent, getPublicKey } from './pure.ts'\n\nexport interface Signer {\n  getPublicKey(): Promise<string>\n  signEvent(event: EventTemplate): Promise<VerifiedEvent>\n}\n\nexport class PlainKeySigner implements Signer {\n  private secretKey: Uint8Array\n\n  constructor(secretKey: Uint8Array) {\n    this.secretKey = secretKey\n  }\n\n  async getPublicKey(): Promise<string> {\n    return getPublicKey(this.secretKey)\n  }\n\n  async signEvent(event: EventTemplate): Promise<VerifiedEvent> {\n    return finalizeEvent(event, this.secretKey)\n  }\n}\n", "import { schnorr } from '@noble/curves/secp256k1.js'\nimport { bytesToHex, hexToBytes } from '@noble/hashes/utils.js'\nimport { Nostr, Event, EventTemplate, UnsignedEvent, VerifiedEvent, verifiedSymbol, validateEvent } from './core.ts'\nimport { sha256 } from '@noble/hashes/sha2.js'\n\nimport { utf8Encoder } from './utils.ts'\n\nclass JS implements Nostr {\n  generateSecretKey(): Uint8Array {\n    return schnorr.utils.randomSecretKey()\n  }\n  getPublicKey(secretKey: Uint8Array): string {\n    return bytesToHex(schnorr.getPublicKey(secretKey))\n  }\n  finalizeEvent(t: EventTemplate, secretKey: Uint8Array): VerifiedEvent {\n    const event = t as VerifiedEvent\n    event.pubkey = bytesToHex(schnorr.getPublicKey(secretKey))\n    event.id = getEventHash(event)\n    event.sig = bytesToHex(schnorr.sign(hexToBytes(getEventHash(event)), secretKey))\n    event[verifiedSymbol] = true\n    return event\n  }\n  verifyEvent(event: Event): event is VerifiedEvent {\n    if (typeof event[verifiedSymbol] === 'boolean') return event[verifiedSymbol]\n\n    const hash = getEventHash(event)\n    if (hash !== event.id) {\n      event[verifiedSymbol] = false\n      return false\n    }\n\n    try {\n      const valid = schnorr.verify(hexToBytes(event.sig), hexToBytes(hash), hexToBytes(event.pubkey))\n      event[verifiedSymbol] = valid\n      return valid\n    } catch (err) {\n      event[verifiedSymbol] = false\n      return false\n    }\n  }\n}\n\nexport function serializeEvent(evt: UnsignedEvent): string {\n  if (!validateEvent(evt)) throw new Error(\"can't serialize event with wrong or missing properties\")\n  return JSON.stringify([0, evt.pubkey, evt.created_at, evt.kind, evt.tags, evt.content])\n}\n\nexport function getEventHash(event: UnsignedEvent): string {\n  let eventHash = sha256(utf8Encoder.encode(serializeEvent(event)))\n  return bytesToHex(eventHash)\n}\n\nconst i: JS = new JS()\n\nexport const generateSecretKey = i.generateSecretKey\nexport const getPublicKey = i.getPublicKey\nexport const finalizeEvent = i.finalizeEvent\nexport const verifyEvent = i.verifyEvent\nexport * from './core.ts'\n", "export interface Nostr {\n  generateSecretKey(): Uint8Array\n  getPublicKey(secretKey: Uint8Array): string\n  finalizeEvent(event: EventTemplate, secretKey: Uint8Array): VerifiedEvent\n  verifyEvent(event: Event): event is VerifiedEvent\n}\n\n/** Designates a verified event signature. */\nexport const verifiedSymbol = Symbol('verified')\n\nexport type NostrEvent = {\n  kind: number\n  tags: string[][]\n  content: string\n  created_at: number\n  pubkey: string\n  id: string\n  sig: string\n  [verifiedSymbol]?: boolean\n}\n\nexport type Event = NostrEvent\nexport type EventTemplate = Pick<Event, 'kind' | 'tags' | 'content' | 'created_at'>\nexport type UnsignedEvent = Pick<Event, 'kind' | 'tags' | 'content' | 'created_at' | 'pubkey'>\n\n/** An event whose signature has been verified. */\nexport interface VerifiedEvent extends Event {\n  [verifiedSymbol]: true\n}\n\nconst isRecord = (obj: unknown): obj is Record<string, unknown> => obj instanceof Object\n\nexport function validateEvent<T>(event: T): event is T & UnsignedEvent {\n  if (!isRecord(event)) return false\n  if (typeof event.kind !== 'number') return false\n  if (typeof event.content !== 'string') return false\n  if (typeof event.created_at !== 'number') return false\n  if (typeof event.pubkey !== 'string') return false\n  if (!event.pubkey.match(/^[a-f0-9]{64}$/)) return false\n\n  if (!Array.isArray(event.tags)) return false\n  for (let i = 0; i < event.tags.length; i++) {\n    let tag = event.tags[i]\n    if (!Array.isArray(tag)) return false\n    for (let j = 0; j < tag.length; j++) {\n      if (typeof tag[j] !== 'string') return false\n    }\n  }\n\n  return true\n}\n\n/**\n * Sort events in reverse-chronological order by the `created_at` timestamp,\n * and then by the event `id` (lexicographically) in case of ties.\n * This mutates the array.\n */\nexport function sortEvents(events: Event[]): Event[] {\n  return events.sort((a: NostrEvent, b: NostrEvent): number => {\n    if (a.created_at !== b.created_at) {\n      return b.created_at - a.created_at\n    }\n    return a.id.localeCompare(b.id)\n  })\n}\n", "import type { Event } from './core.ts'\n\nexport const utf8Decoder: TextDecoder = new TextDecoder('utf-8')\nexport const utf8Encoder: TextEncoder = new TextEncoder()\n\nexport { bytesToHex, hexToBytes } from '@noble/hashes/utils.js'\n\nexport function normalizeURL(url: string): string {\n  try {\n    if (url.indexOf('://') === -1) url = 'wss://' + url\n    let p = new URL(url)\n    if (p.protocol === 'http:') p.protocol = 'ws:'\n    else if (p.protocol === 'https:') p.protocol = 'wss:'\n    p.pathname = p.pathname.replace(/\\/+/g, '/')\n    if (p.pathname.endsWith('/')) p.pathname = p.pathname.slice(0, -1)\n    if ((p.port === '80' && p.protocol === 'ws:') || (p.port === '443' && p.protocol === 'wss:')) p.port = ''\n    p.searchParams.sort()\n    p.hash = ''\n    return p.toString()\n  } catch (e) {\n    throw new Error(`Invalid URL: ${url}`)\n  }\n}\n\nexport function insertEventIntoDescendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return b.created_at - event.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function insertEventIntoAscendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return event.created_at - b.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function binarySearch<T>(arr: T[], compare: (b: T) => number): [number, boolean] {\n  let start = 0\n  let end = arr.length - 1\n\n  while (start <= end) {\n    const mid = Math.floor((start + end) / 2)\n    const cmp = compare(arr[mid])\n\n    if (cmp === 0) {\n      return [mid, true]\n    }\n\n    if (cmp < 0) {\n      end = mid - 1\n    } else {\n      start = mid + 1\n    }\n  }\n\n  return [start, false]\n}\n\nexport class QueueNode<V> {\n  public value: V\n  public next: QueueNode<V> | null = null\n  public prev: QueueNode<V> | null = null\n\n  constructor(message: V) {\n    this.value = message\n  }\n}\n\nexport class Queue<V> {\n  public first: QueueNode<V> | null\n  public last: QueueNode<V> | null\n\n  constructor() {\n    this.first = null\n    this.last = null\n  }\n\n  enqueue(value: V): boolean {\n    const newNode = new QueueNode(value)\n    if (!this.last) {\n      // list is empty\n      this.first = newNode\n      this.last = newNode\n    } else if (this.last === this.first) {\n      // list has a single element\n      this.last = newNode\n      this.last.prev = this.first\n      this.first.next = newNode\n    } else {\n      // list has elements, add as last\n      newNode.prev = this.last\n      this.last.next = newNode\n      this.last = newNode\n    }\n    return true\n  }\n\n  dequeue(): V | null {\n    if (!this.first) return null\n\n    if (this.first === this.last) {\n      const target = this.first\n      this.first = null\n      this.last = null\n      return target.value\n    }\n\n    const target = this.first\n    this.first = target.next\n    if (this.first) {\n      this.first.prev = null // fix: clean up prev pointer\n    }\n\n    return target.value\n  }\n}\n"],
+  "mappings": "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",
   "names": ["import_utils", "i"]
 }
Index: package/lib/esm/signer.js.map
===================================================================
--- package/lib/esm/signer.js.map
+++ package/lib/esm/signer.js.map
@@ -1,7 +1,7 @@
 {
   "version": 3,
   "sources": ["../../pure.ts", "../../core.ts", "../../utils.ts", "../../signer.ts"],
-  "sourcesContent": ["import { schnorr } from '@noble/curves/secp256k1'\nimport { bytesToHex } from '@noble/hashes/utils'\nimport { Nostr, Event, EventTemplate, UnsignedEvent, VerifiedEvent, verifiedSymbol, validateEvent } from './core.ts'\nimport { sha256 } from '@noble/hashes/sha256'\n\nimport { utf8Encoder } from './utils.ts'\n\nclass JS implements Nostr {\n  generateSecretKey(): Uint8Array {\n    return schnorr.utils.randomPrivateKey()\n  }\n  getPublicKey(secretKey: Uint8Array): string {\n    return bytesToHex(schnorr.getPublicKey(secretKey))\n  }\n  finalizeEvent(t: EventTemplate, secretKey: Uint8Array): VerifiedEvent {\n    const event = t as VerifiedEvent\n    event.pubkey = bytesToHex(schnorr.getPublicKey(secretKey))\n    event.id = getEventHash(event)\n    event.sig = bytesToHex(schnorr.sign(getEventHash(event), secretKey))\n    event[verifiedSymbol] = true\n    return event\n  }\n  verifyEvent(event: Event): event is VerifiedEvent {\n    if (typeof event[verifiedSymbol] === 'boolean') return event[verifiedSymbol]\n\n    const hash = getEventHash(event)\n    if (hash !== event.id) {\n      event[verifiedSymbol] = false\n      return false\n    }\n\n    try {\n      const valid = schnorr.verify(event.sig, hash, event.pubkey)\n      event[verifiedSymbol] = valid\n      return valid\n    } catch (err) {\n      event[verifiedSymbol] = false\n      return false\n    }\n  }\n}\n\nexport function serializeEvent(evt: UnsignedEvent): string {\n  if (!validateEvent(evt)) throw new Error(\"can't serialize event with wrong or missing properties\")\n  return JSON.stringify([0, evt.pubkey, evt.created_at, evt.kind, evt.tags, evt.content])\n}\n\nexport function getEventHash(event: UnsignedEvent): string {\n  let eventHash = sha256(utf8Encoder.encode(serializeEvent(event)))\n  return bytesToHex(eventHash)\n}\n\nconst i: JS = new JS()\n\nexport const generateSecretKey = i.generateSecretKey\nexport const getPublicKey = i.getPublicKey\nexport const finalizeEvent = i.finalizeEvent\nexport const verifyEvent = i.verifyEvent\nexport * from './core.ts'\n", "export interface Nostr {\n  generateSecretKey(): Uint8Array\n  getPublicKey(secretKey: Uint8Array): string\n  finalizeEvent(event: EventTemplate, secretKey: Uint8Array): VerifiedEvent\n  verifyEvent(event: Event): event is VerifiedEvent\n}\n\n/** Designates a verified event signature. */\nexport const verifiedSymbol = Symbol('verified')\n\nexport type NostrEvent = {\n  kind: number\n  tags: string[][]\n  content: string\n  created_at: number\n  pubkey: string\n  id: string\n  sig: string\n  [verifiedSymbol]?: boolean\n}\n\nexport type Event = NostrEvent\nexport type EventTemplate = Pick<Event, 'kind' | 'tags' | 'content' | 'created_at'>\nexport type UnsignedEvent = Pick<Event, 'kind' | 'tags' | 'content' | 'created_at' | 'pubkey'>\n\n/** An event whose signature has been verified. */\nexport interface VerifiedEvent extends Event {\n  [verifiedSymbol]: true\n}\n\nconst isRecord = (obj: unknown): obj is Record<string, unknown> => obj instanceof Object\n\nexport function validateEvent<T>(event: T): event is T & UnsignedEvent {\n  if (!isRecord(event)) return false\n  if (typeof event.kind !== 'number') return false\n  if (typeof event.content !== 'string') return false\n  if (typeof event.created_at !== 'number') return false\n  if (typeof event.pubkey !== 'string') return false\n  if (!event.pubkey.match(/^[a-f0-9]{64}$/)) return false\n\n  if (!Array.isArray(event.tags)) return false\n  for (let i = 0; i < event.tags.length; i++) {\n    let tag = event.tags[i]\n    if (!Array.isArray(tag)) return false\n    for (let j = 0; j < tag.length; j++) {\n      if (typeof tag[j] !== 'string') return false\n    }\n  }\n\n  return true\n}\n\n/**\n * Sort events in reverse-chronological order by the `created_at` timestamp,\n * and then by the event `id` (lexicographically) in case of ties.\n * This mutates the array.\n */\nexport function sortEvents(events: Event[]): Event[] {\n  return events.sort((a: NostrEvent, b: NostrEvent): number => {\n    if (a.created_at !== b.created_at) {\n      return b.created_at - a.created_at\n    }\n    return a.id.localeCompare(b.id)\n  })\n}\n", "import type { Event } from './core.ts'\n\nexport const utf8Decoder: TextDecoder = new TextDecoder('utf-8')\nexport const utf8Encoder: TextEncoder = new TextEncoder()\n\nexport { bytesToHex, hexToBytes } from '@noble/hashes/utils'\n\nexport function normalizeURL(url: string): string {\n  try {\n    if (url.indexOf('://') === -1) url = 'wss://' + url\n    let p = new URL(url)\n    if (p.protocol === 'http:') p.protocol = 'ws:'\n    else if (p.protocol === 'https:') p.protocol = 'wss:'\n    p.pathname = p.pathname.replace(/\\/+/g, '/')\n    if (p.pathname.endsWith('/')) p.pathname = p.pathname.slice(0, -1)\n    if ((p.port === '80' && p.protocol === 'ws:') || (p.port === '443' && p.protocol === 'wss:')) p.port = ''\n    p.searchParams.sort()\n    p.hash = ''\n    return p.toString()\n  } catch (e) {\n    throw new Error(`Invalid URL: ${url}`)\n  }\n}\n\nexport function insertEventIntoDescendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return b.created_at - event.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function insertEventIntoAscendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return event.created_at - b.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function binarySearch<T>(arr: T[], compare: (b: T) => number): [number, boolean] {\n  let start = 0\n  let end = arr.length - 1\n\n  while (start <= end) {\n    const mid = Math.floor((start + end) / 2)\n    const cmp = compare(arr[mid])\n\n    if (cmp === 0) {\n      return [mid, true]\n    }\n\n    if (cmp < 0) {\n      end = mid - 1\n    } else {\n      start = mid + 1\n    }\n  }\n\n  return [start, false]\n}\n\nexport class QueueNode<V> {\n  public value: V\n  public next: QueueNode<V> | null = null\n  public prev: QueueNode<V> | null = null\n\n  constructor(message: V) {\n    this.value = message\n  }\n}\n\nexport class Queue<V> {\n  public first: QueueNode<V> | null\n  public last: QueueNode<V> | null\n\n  constructor() {\n    this.first = null\n    this.last = null\n  }\n\n  enqueue(value: V): boolean {\n    const newNode = new QueueNode(value)\n    if (!this.last) {\n      // list is empty\n      this.first = newNode\n      this.last = newNode\n    } else if (this.last === this.first) {\n      // list has a single element\n      this.last = newNode\n      this.last.prev = this.first\n      this.first.next = newNode\n    } else {\n      // list has elements, add as last\n      newNode.prev = this.last\n      this.last.next = newNode\n      this.last = newNode\n    }\n    return true\n  }\n\n  dequeue(): V | null {\n    if (!this.first) return null\n\n    if (this.first === this.last) {\n      const target = this.first\n      this.first = null\n      this.last = null\n      return target.value\n    }\n\n    const target = this.first\n    this.first = target.next\n    if (this.first) {\n      this.first.prev = null // fix: clean up prev pointer\n    }\n\n    return target.value\n  }\n}\n", "import { EventTemplate, VerifiedEvent } from './core.ts'\nimport { finalizeEvent, getPublicKey } from './pure.ts'\n\nexport interface Signer {\n  getPublicKey(): Promise<string>\n  signEvent(event: EventTemplate): Promise<VerifiedEvent>\n}\n\nexport class PlainKeySigner implements Signer {\n  private secretKey: Uint8Array\n\n  constructor(secretKey: Uint8Array) {\n    this.secretKey = secretKey\n  }\n\n  async getPublicKey(): Promise<string> {\n    return getPublicKey(this.secretKey)\n  }\n\n  async signEvent(event: EventTemplate): Promise<VerifiedEvent> {\n    return finalizeEvent(event, this.secretKey)\n  }\n}\n"],
-  "mappings": ";AAAA,SAAS,eAAe;AACxB,SAAS,cAAAA,mBAAkB;;;ACOpB,IAAM,iBAAiB,OAAO,UAAU;AAsB/C,IAAM,WAAW,CAAC,QAAiD,eAAe;AAE3E,SAAS,cAAiB,OAAsC;AACrE,MAAI,CAAC,SAAS,KAAK;AAAG,WAAO;AAC7B,MAAI,OAAO,MAAM,SAAS;AAAU,WAAO;AAC3C,MAAI,OAAO,MAAM,YAAY;AAAU,WAAO;AAC9C,MAAI,OAAO,MAAM,eAAe;AAAU,WAAO;AACjD,MAAI,OAAO,MAAM,WAAW;AAAU,WAAO;AAC7C,MAAI,CAAC,MAAM,OAAO,MAAM,gBAAgB;AAAG,WAAO;AAElD,MAAI,CAAC,MAAM,QAAQ,MAAM,IAAI;AAAG,WAAO;AACvC,WAASC,KAAI,GAAGA,KAAI,MAAM,KAAK,QAAQA,MAAK;AAC1C,QAAI,MAAM,MAAM,KAAKA;AACrB,QAAI,CAAC,MAAM,QAAQ,GAAG;AAAG,aAAO;AAChC,aAAS,IAAI,GAAG,IAAI,IAAI,QAAQ,KAAK;AACnC,UAAI,OAAO,IAAI,OAAO;AAAU,eAAO;AAAA,IACzC;AAAA,EACF;AAEA,SAAO;AACT;;;AD/CA,SAAS,cAAc;;;AEEvB,SAAS,YAAY,kBAAkB;AAHhC,IAAM,cAA2B,IAAI,YAAY,OAAO;AACxD,IAAM,cAA2B,IAAI,YAAY;;;AFIxD,IAAM,KAAN,MAA0B;AAAA,EACxB,oBAAgC;AAC9B,WAAO,QAAQ,MAAM,iBAAiB;AAAA,EACxC;AAAA,EACA,aAAa,WAA+B;AAC1C,WAAOC,YAAW,QAAQ,aAAa,SAAS,CAAC;AAAA,EACnD;AAAA,EACA,cAAc,GAAkB,WAAsC;AACpE,UAAM,QAAQ;AACd,UAAM,SAASA,YAAW,QAAQ,aAAa,SAAS,CAAC;AACzD,UAAM,KAAK,aAAa,KAAK;AAC7B,UAAM,MAAMA,YAAW,QAAQ,KAAK,aAAa,KAAK,GAAG,SAAS,CAAC;AACnE,UAAM,kBAAkB;AACxB,WAAO;AAAA,EACT;AAAA,EACA,YAAY,OAAsC;AAChD,QAAI,OAAO,MAAM,oBAAoB;AAAW,aAAO,MAAM;AAE7D,UAAM,OAAO,aAAa,KAAK;AAC/B,QAAI,SAAS,MAAM,IAAI;AACrB,YAAM,kBAAkB;AACxB,aAAO;AAAA,IACT;AAEA,QAAI;AACF,YAAM,QAAQ,QAAQ,OAAO,MAAM,KAAK,MAAM,MAAM,MAAM;AAC1D,YAAM,kBAAkB;AACxB,aAAO;AAAA,IACT,SAAS,KAAP;AACA,YAAM,kBAAkB;AACxB,aAAO;AAAA,IACT;AAAA,EACF;AACF;AAEO,SAAS,eAAe,KAA4B;AACzD,MAAI,CAAC,cAAc,GAAG;AAAG,UAAM,IAAI,MAAM,wDAAwD;AACjG,SAAO,KAAK,UAAU,CAAC,GAAG,IAAI,QAAQ,IAAI,YAAY,IAAI,MAAM,IAAI,MAAM,IAAI,OAAO,CAAC;AACxF;AAEO,SAAS,aAAa,OAA8B;AACzD,MAAI,YAAY,OAAO,YAAY,OAAO,eAAe,KAAK,CAAC,CAAC;AAChE,SAAOA,YAAW,SAAS;AAC7B;AAEA,IAAM,IAAQ,IAAI,GAAG;AAEd,IAAM,oBAAoB,EAAE;AAC5B,IAAM,eAAe,EAAE;AACvB,IAAM,gBAAgB,EAAE;AACxB,IAAM,cAAc,EAAE;;;AGjDtB,IAAM,iBAAN,MAAuC;AAAA,EACpC;AAAA,EAER,YAAY,WAAuB;AACjC,SAAK,YAAY;AAAA,EACnB;AAAA,EAEA,MAAM,eAAgC;AACpC,WAAO,aAAa,KAAK,SAAS;AAAA,EACpC;AAAA,EAEA,MAAM,UAAU,OAA8C;AAC5D,WAAO,cAAc,OAAO,KAAK,SAAS;AAAA,EAC5C;AACF;",
-  "names": ["bytesToHex", "i", "bytesToHex"]
+  "sourcesContent": ["import { schnorr } from '@noble/curves/secp256k1.js'\nimport { bytesToHex, hexToBytes } from '@noble/hashes/utils.js'\nimport { Nostr, Event, EventTemplate, UnsignedEvent, VerifiedEvent, verifiedSymbol, validateEvent } from './core.ts'\nimport { sha256 } from '@noble/hashes/sha2.js'\n\nimport { utf8Encoder } from './utils.ts'\n\nclass JS implements Nostr {\n  generateSecretKey(): Uint8Array {\n    return schnorr.utils.randomSecretKey()\n  }\n  getPublicKey(secretKey: Uint8Array): string {\n    return bytesToHex(schnorr.getPublicKey(secretKey))\n  }\n  finalizeEvent(t: EventTemplate, secretKey: Uint8Array): VerifiedEvent {\n    const event = t as VerifiedEvent\n    event.pubkey = bytesToHex(schnorr.getPublicKey(secretKey))\n    event.id = getEventHash(event)\n    event.sig = bytesToHex(schnorr.sign(hexToBytes(getEventHash(event)), secretKey))\n    event[verifiedSymbol] = true\n    return event\n  }\n  verifyEvent(event: Event): event is VerifiedEvent {\n    if (typeof event[verifiedSymbol] === 'boolean') return event[verifiedSymbol]\n\n    const hash = getEventHash(event)\n    if (hash !== event.id) {\n      event[verifiedSymbol] = false\n      return false\n    }\n\n    try {\n      const valid = schnorr.verify(hexToBytes(event.sig), hexToBytes(hash), hexToBytes(event.pubkey))\n      event[verifiedSymbol] = valid\n      return valid\n    } catch (err) {\n      event[verifiedSymbol] = false\n      return false\n    }\n  }\n}\n\nexport function serializeEvent(evt: UnsignedEvent): string {\n  if (!validateEvent(evt)) throw new Error(\"can't serialize event with wrong or missing properties\")\n  return JSON.stringify([0, evt.pubkey, evt.created_at, evt.kind, evt.tags, evt.content])\n}\n\nexport function getEventHash(event: UnsignedEvent): string {\n  let eventHash = sha256(utf8Encoder.encode(serializeEvent(event)))\n  return bytesToHex(eventHash)\n}\n\nconst i: JS = new JS()\n\nexport const generateSecretKey = i.generateSecretKey\nexport const getPublicKey = i.getPublicKey\nexport const finalizeEvent = i.finalizeEvent\nexport const verifyEvent = i.verifyEvent\nexport * from './core.ts'\n", "export interface Nostr {\n  generateSecretKey(): Uint8Array\n  getPublicKey(secretKey: Uint8Array): string\n  finalizeEvent(event: EventTemplate, secretKey: Uint8Array): VerifiedEvent\n  verifyEvent(event: Event): event is VerifiedEvent\n}\n\n/** Designates a verified event signature. */\nexport const verifiedSymbol = Symbol('verified')\n\nexport type NostrEvent = {\n  kind: number\n  tags: string[][]\n  content: string\n  created_at: number\n  pubkey: string\n  id: string\n  sig: string\n  [verifiedSymbol]?: boolean\n}\n\nexport type Event = NostrEvent\nexport type EventTemplate = Pick<Event, 'kind' | 'tags' | 'content' | 'created_at'>\nexport type UnsignedEvent = Pick<Event, 'kind' | 'tags' | 'content' | 'created_at' | 'pubkey'>\n\n/** An event whose signature has been verified. */\nexport interface VerifiedEvent extends Event {\n  [verifiedSymbol]: true\n}\n\nconst isRecord = (obj: unknown): obj is Record<string, unknown> => obj instanceof Object\n\nexport function validateEvent<T>(event: T): event is T & UnsignedEvent {\n  if (!isRecord(event)) return false\n  if (typeof event.kind !== 'number') return false\n  if (typeof event.content !== 'string') return false\n  if (typeof event.created_at !== 'number') return false\n  if (typeof event.pubkey !== 'string') return false\n  if (!event.pubkey.match(/^[a-f0-9]{64}$/)) return false\n\n  if (!Array.isArray(event.tags)) return false\n  for (let i = 0; i < event.tags.length; i++) {\n    let tag = event.tags[i]\n    if (!Array.isArray(tag)) return false\n    for (let j = 0; j < tag.length; j++) {\n      if (typeof tag[j] !== 'string') return false\n    }\n  }\n\n  return true\n}\n\n/**\n * Sort events in reverse-chronological order by the `created_at` timestamp,\n * and then by the event `id` (lexicographically) in case of ties.\n * This mutates the array.\n */\nexport function sortEvents(events: Event[]): Event[] {\n  return events.sort((a: NostrEvent, b: NostrEvent): number => {\n    if (a.created_at !== b.created_at) {\n      return b.created_at - a.created_at\n    }\n    return a.id.localeCompare(b.id)\n  })\n}\n", "import type { Event } from './core.ts'\n\nexport const utf8Decoder: TextDecoder = new TextDecoder('utf-8')\nexport const utf8Encoder: TextEncoder = new TextEncoder()\n\nexport { bytesToHex, hexToBytes } from '@noble/hashes/utils.js'\n\nexport function normalizeURL(url: string): string {\n  try {\n    if (url.indexOf('://') === -1) url = 'wss://' + url\n    let p = new URL(url)\n    if (p.protocol === 'http:') p.protocol = 'ws:'\n    else if (p.protocol === 'https:') p.protocol = 'wss:'\n    p.pathname = p.pathname.replace(/\\/+/g, '/')\n    if (p.pathname.endsWith('/')) p.pathname = p.pathname.slice(0, -1)\n    if ((p.port === '80' && p.protocol === 'ws:') || (p.port === '443' && p.protocol === 'wss:')) p.port = ''\n    p.searchParams.sort()\n    p.hash = ''\n    return p.toString()\n  } catch (e) {\n    throw new Error(`Invalid URL: ${url}`)\n  }\n}\n\nexport function insertEventIntoDescendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return b.created_at - event.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function insertEventIntoAscendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return event.created_at - b.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function binarySearch<T>(arr: T[], compare: (b: T) => number): [number, boolean] {\n  let start = 0\n  let end = arr.length - 1\n\n  while (start <= end) {\n    const mid = Math.floor((start + end) / 2)\n    const cmp = compare(arr[mid])\n\n    if (cmp === 0) {\n      return [mid, true]\n    }\n\n    if (cmp < 0) {\n      end = mid - 1\n    } else {\n      start = mid + 1\n    }\n  }\n\n  return [start, false]\n}\n\nexport class QueueNode<V> {\n  public value: V\n  public next: QueueNode<V> | null = null\n  public prev: QueueNode<V> | null = null\n\n  constructor(message: V) {\n    this.value = message\n  }\n}\n\nexport class Queue<V> {\n  public first: QueueNode<V> | null\n  public last: QueueNode<V> | null\n\n  constructor() {\n    this.first = null\n    this.last = null\n  }\n\n  enqueue(value: V): boolean {\n    const newNode = new QueueNode(value)\n    if (!this.last) {\n      // list is empty\n      this.first = newNode\n      this.last = newNode\n    } else if (this.last === this.first) {\n      // list has a single element\n      this.last = newNode\n      this.last.prev = this.first\n      this.first.next = newNode\n    } else {\n      // list has elements, add as last\n      newNode.prev = this.last\n      this.last.next = newNode\n      this.last = newNode\n    }\n    return true\n  }\n\n  dequeue(): V | null {\n    if (!this.first) return null\n\n    if (this.first === this.last) {\n      const target = this.first\n      this.first = null\n      this.last = null\n      return target.value\n    }\n\n    const target = this.first\n    this.first = target.next\n    if (this.first) {\n      this.first.prev = null // fix: clean up prev pointer\n    }\n\n    return target.value\n  }\n}\n", "import { EventTemplate, VerifiedEvent } from './core.ts'\nimport { finalizeEvent, getPublicKey } from './pure.ts'\n\nexport interface Signer {\n  getPublicKey(): Promise<string>\n  signEvent(event: EventTemplate): Promise<VerifiedEvent>\n}\n\nexport class PlainKeySigner implements Signer {\n  private secretKey: Uint8Array\n\n  constructor(secretKey: Uint8Array) {\n    this.secretKey = secretKey\n  }\n\n  async getPublicKey(): Promise<string> {\n    return getPublicKey(this.secretKey)\n  }\n\n  async signEvent(event: EventTemplate): Promise<VerifiedEvent> {\n    return finalizeEvent(event, this.secretKey)\n  }\n}\n"],
+  "mappings": "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",
+  "names": ["bytesToHex", "hexToBytes", "i", "bytesToHex", "hexToBytes"]
 }
Index: package/lib/cjs/utils.js.map
===================================================================
--- package/lib/cjs/utils.js.map
+++ package/lib/cjs/utils.js.map
@@ -1,7 +1,7 @@
 {
   "version": 3,
   "sources": ["../../utils.ts"],
-  "sourcesContent": ["import type { Event } from './core.ts'\n\nexport const utf8Decoder: TextDecoder = new TextDecoder('utf-8')\nexport const utf8Encoder: TextEncoder = new TextEncoder()\n\nexport { bytesToHex, hexToBytes } from '@noble/hashes/utils'\n\nexport function normalizeURL(url: string): string {\n  try {\n    if (url.indexOf('://') === -1) url = 'wss://' + url\n    let p = new URL(url)\n    if (p.protocol === 'http:') p.protocol = 'ws:'\n    else if (p.protocol === 'https:') p.protocol = 'wss:'\n    p.pathname = p.pathname.replace(/\\/+/g, '/')\n    if (p.pathname.endsWith('/')) p.pathname = p.pathname.slice(0, -1)\n    if ((p.port === '80' && p.protocol === 'ws:') || (p.port === '443' && p.protocol === 'wss:')) p.port = ''\n    p.searchParams.sort()\n    p.hash = ''\n    return p.toString()\n  } catch (e) {\n    throw new Error(`Invalid URL: ${url}`)\n  }\n}\n\nexport function insertEventIntoDescendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return b.created_at - event.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function insertEventIntoAscendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return event.created_at - b.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function binarySearch<T>(arr: T[], compare: (b: T) => number): [number, boolean] {\n  let start = 0\n  let end = arr.length - 1\n\n  while (start <= end) {\n    const mid = Math.floor((start + end) / 2)\n    const cmp = compare(arr[mid])\n\n    if (cmp === 0) {\n      return [mid, true]\n    }\n\n    if (cmp < 0) {\n      end = mid - 1\n    } else {\n      start = mid + 1\n    }\n  }\n\n  return [start, false]\n}\n\nexport class QueueNode<V> {\n  public value: V\n  public next: QueueNode<V> | null = null\n  public prev: QueueNode<V> | null = null\n\n  constructor(message: V) {\n    this.value = message\n  }\n}\n\nexport class Queue<V> {\n  public first: QueueNode<V> | null\n  public last: QueueNode<V> | null\n\n  constructor() {\n    this.first = null\n    this.last = null\n  }\n\n  enqueue(value: V): boolean {\n    const newNode = new QueueNode(value)\n    if (!this.last) {\n      // list is empty\n      this.first = newNode\n      this.last = newNode\n    } else if (this.last === this.first) {\n      // list has a single element\n      this.last = newNode\n      this.last.prev = this.first\n      this.first.next = newNode\n    } else {\n      // list has elements, add as last\n      newNode.prev = this.last\n      this.last.next = newNode\n      this.last = newNode\n    }\n    return true\n  }\n\n  dequeue(): V | null {\n    if (!this.first) return null\n\n    if (this.first === this.last) {\n      const target = this.first\n      this.first = null\n      this.last = null\n      return target.value\n    }\n\n    const target = this.first\n    this.first = target.next\n    if (this.first) {\n      this.first.prev = null // fix: clean up prev pointer\n    }\n\n    return target.value\n  }\n}\n"],
+  "sourcesContent": ["import type { Event } from './core.ts'\n\nexport const utf8Decoder: TextDecoder = new TextDecoder('utf-8')\nexport const utf8Encoder: TextEncoder = new TextEncoder()\n\nexport { bytesToHex, hexToBytes } from '@noble/hashes/utils.js'\n\nexport function normalizeURL(url: string): string {\n  try {\n    if (url.indexOf('://') === -1) url = 'wss://' + url\n    let p = new URL(url)\n    if (p.protocol === 'http:') p.protocol = 'ws:'\n    else if (p.protocol === 'https:') p.protocol = 'wss:'\n    p.pathname = p.pathname.replace(/\\/+/g, '/')\n    if (p.pathname.endsWith('/')) p.pathname = p.pathname.slice(0, -1)\n    if ((p.port === '80' && p.protocol === 'ws:') || (p.port === '443' && p.protocol === 'wss:')) p.port = ''\n    p.searchParams.sort()\n    p.hash = ''\n    return p.toString()\n  } catch (e) {\n    throw new Error(`Invalid URL: ${url}`)\n  }\n}\n\nexport function insertEventIntoDescendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return b.created_at - event.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function insertEventIntoAscendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return event.created_at - b.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function binarySearch<T>(arr: T[], compare: (b: T) => number): [number, boolean] {\n  let start = 0\n  let end = arr.length - 1\n\n  while (start <= end) {\n    const mid = Math.floor((start + end) / 2)\n    const cmp = compare(arr[mid])\n\n    if (cmp === 0) {\n      return [mid, true]\n    }\n\n    if (cmp < 0) {\n      end = mid - 1\n    } else {\n      start = mid + 1\n    }\n  }\n\n  return [start, false]\n}\n\nexport class QueueNode<V> {\n  public value: V\n  public next: QueueNode<V> | null = null\n  public prev: QueueNode<V> | null = null\n\n  constructor(message: V) {\n    this.value = message\n  }\n}\n\nexport class Queue<V> {\n  public first: QueueNode<V> | null\n  public last: QueueNode<V> | null\n\n  constructor() {\n    this.first = null\n    this.last = null\n  }\n\n  enqueue(value: V): boolean {\n    const newNode = new QueueNode(value)\n    if (!this.last) {\n      // list is empty\n      this.first = newNode\n      this.last = newNode\n    } else if (this.last === this.first) {\n      // list has a single element\n      this.last = newNode\n      this.last.prev = this.first\n      this.first.next = newNode\n    } else {\n      // list has elements, add as last\n      newNode.prev = this.last\n      this.last.next = newNode\n      this.last = newNode\n    }\n    return true\n  }\n\n  dequeue(): V | null {\n    if (!this.first) return null\n\n    if (this.first === this.last) {\n      const target = this.first\n      this.first = null\n      this.last = null\n      return target.value\n    }\n\n    const target = this.first\n    this.first = target.next\n    if (this.first) {\n      this.first.prev = null // fix: clean up prev pointer\n    }\n\n    return target.value\n  }\n}\n"],
   "mappings": "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",
   "names": ["target"]
 }
Index: package/lib/esm/utils.js.map
===================================================================
--- package/lib/esm/utils.js.map
+++ package/lib/esm/utils.js.map
@@ -1,7 +1,7 @@
 {
   "version": 3,
   "sources": ["../../utils.ts"],
-  "sourcesContent": ["import type { Event } from './core.ts'\n\nexport const utf8Decoder: TextDecoder = new TextDecoder('utf-8')\nexport const utf8Encoder: TextEncoder = new TextEncoder()\n\nexport { bytesToHex, hexToBytes } from '@noble/hashes/utils'\n\nexport function normalizeURL(url: string): string {\n  try {\n    if (url.indexOf('://') === -1) url = 'wss://' + url\n    let p = new URL(url)\n    if (p.protocol === 'http:') p.protocol = 'ws:'\n    else if (p.protocol === 'https:') p.protocol = 'wss:'\n    p.pathname = p.pathname.replace(/\\/+/g, '/')\n    if (p.pathname.endsWith('/')) p.pathname = p.pathname.slice(0, -1)\n    if ((p.port === '80' && p.protocol === 'ws:') || (p.port === '443' && p.protocol === 'wss:')) p.port = ''\n    p.searchParams.sort()\n    p.hash = ''\n    return p.toString()\n  } catch (e) {\n    throw new Error(`Invalid URL: ${url}`)\n  }\n}\n\nexport function insertEventIntoDescendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return b.created_at - event.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function insertEventIntoAscendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return event.created_at - b.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function binarySearch<T>(arr: T[], compare: (b: T) => number): [number, boolean] {\n  let start = 0\n  let end = arr.length - 1\n\n  while (start <= end) {\n    const mid = Math.floor((start + end) / 2)\n    const cmp = compare(arr[mid])\n\n    if (cmp === 0) {\n      return [mid, true]\n    }\n\n    if (cmp < 0) {\n      end = mid - 1\n    } else {\n      start = mid + 1\n    }\n  }\n\n  return [start, false]\n}\n\nexport class QueueNode<V> {\n  public value: V\n  public next: QueueNode<V> | null = null\n  public prev: QueueNode<V> | null = null\n\n  constructor(message: V) {\n    this.value = message\n  }\n}\n\nexport class Queue<V> {\n  public first: QueueNode<V> | null\n  public last: QueueNode<V> | null\n\n  constructor() {\n    this.first = null\n    this.last = null\n  }\n\n  enqueue(value: V): boolean {\n    const newNode = new QueueNode(value)\n    if (!this.last) {\n      // list is empty\n      this.first = newNode\n      this.last = newNode\n    } else if (this.last === this.first) {\n      // list has a single element\n      this.last = newNode\n      this.last.prev = this.first\n      this.first.next = newNode\n    } else {\n      // list has elements, add as last\n      newNode.prev = this.last\n      this.last.next = newNode\n      this.last = newNode\n    }\n    return true\n  }\n\n  dequeue(): V | null {\n    if (!this.first) return null\n\n    if (this.first === this.last) {\n      const target = this.first\n      this.first = null\n      this.last = null\n      return target.value\n    }\n\n    const target = this.first\n    this.first = target.next\n    if (this.first) {\n      this.first.prev = null // fix: clean up prev pointer\n    }\n\n    return target.value\n  }\n}\n"],
+  "sourcesContent": ["import type { Event } from './core.ts'\n\nexport const utf8Decoder: TextDecoder = new TextDecoder('utf-8')\nexport const utf8Encoder: TextEncoder = new TextEncoder()\n\nexport { bytesToHex, hexToBytes } from '@noble/hashes/utils.js'\n\nexport function normalizeURL(url: string): string {\n  try {\n    if (url.indexOf('://') === -1) url = 'wss://' + url\n    let p = new URL(url)\n    if (p.protocol === 'http:') p.protocol = 'ws:'\n    else if (p.protocol === 'https:') p.protocol = 'wss:'\n    p.pathname = p.pathname.replace(/\\/+/g, '/')\n    if (p.pathname.endsWith('/')) p.pathname = p.pathname.slice(0, -1)\n    if ((p.port === '80' && p.protocol === 'ws:') || (p.port === '443' && p.protocol === 'wss:')) p.port = ''\n    p.searchParams.sort()\n    p.hash = ''\n    return p.toString()\n  } catch (e) {\n    throw new Error(`Invalid URL: ${url}`)\n  }\n}\n\nexport function insertEventIntoDescendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return b.created_at - event.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function insertEventIntoAscendingList(sortedArray: Event[], event: Event): Event[] {\n  const [idx, found] = binarySearch(sortedArray, b => {\n    if (event.id === b.id) return 0\n    if (event.created_at === b.created_at) return -1\n    return event.created_at - b.created_at\n  })\n  if (!found) {\n    sortedArray.splice(idx, 0, event)\n  }\n  return sortedArray\n}\n\nexport function binarySearch<T>(arr: T[], compare: (b: T) => number): [number, boolean] {\n  let start = 0\n  let end = arr.length - 1\n\n  while (start <= end) {\n    const mid = Math.floor((start + end) / 2)\n    const cmp = compare(arr[mid])\n\n    if (cmp === 0) {\n      return [mid, true]\n    }\n\n    if (cmp < 0) {\n      end = mid - 1\n    } else {\n      start = mid + 1\n    }\n  }\n\n  return [start, false]\n}\n\nexport class QueueNode<V> {\n  public value: V\n  public next: QueueNode<V> | null = null\n  public prev: QueueNode<V> | null = null\n\n  constructor(message: V) {\n    this.value = message\n  }\n}\n\nexport class Queue<V> {\n  public first: QueueNode<V> | null\n  public last: QueueNode<V> | null\n\n  constructor() {\n    this.first = null\n    this.last = null\n  }\n\n  enqueue(value: V): boolean {\n    const newNode = new QueueNode(value)\n    if (!this.last) {\n      // list is empty\n      this.first = newNode\n      this.last = newNode\n    } else if (this.last === this.first) {\n      // list has a single element\n      this.last = newNode\n      this.last.prev = this.first\n      this.first.next = newNode\n    } else {\n      // list has elements, add as last\n      newNode.prev = this.last\n      this.last.next = newNode\n      this.last = newNode\n    }\n    return true\n  }\n\n  dequeue(): V | null {\n    if (!this.first) return null\n\n    if (this.first === this.last) {\n      const target = this.first\n      this.first = null\n      this.last = null\n      return target.value\n    }\n\n    const target = this.first\n    this.first = target.next\n    if (this.first) {\n      this.first.prev = null // fix: clean up prev pointer\n    }\n\n    return target.value\n  }\n}\n"],
   "mappings": "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",
   "names": ["target"]
 }
Index: package/lib/cjs/wasm.js.map
===================================================================
--- package/lib/cjs/wasm.js.map
+++ package/lib/cjs/wasm.js.map
@@ -1,7 +1,7 @@
 {
   "version": 3,
   "sources": ["../../wasm.ts", "../../core.ts"],
-  "sourcesContent": ["import { bytesToHex } from '@noble/hashes/utils'\nimport { Nostr as NostrWasm } from 'nostr-wasm'\nimport { EventTemplate, Event, Nostr, VerifiedEvent, verifiedSymbol } from './core.ts'\n\nlet nw: NostrWasm\n\nexport function setNostrWasm(x: NostrWasm) {\n  nw = x\n}\n\nclass Wasm implements Nostr {\n  generateSecretKey(): Uint8Array {\n    return nw.generateSecretKey()\n  }\n  getPublicKey(secretKey: Uint8Array): string {\n    return bytesToHex(nw.getPublicKey(secretKey))\n  }\n  finalizeEvent(t: EventTemplate, secretKey: Uint8Array): VerifiedEvent {\n    nw.finalizeEvent(t as any, secretKey)\n    return t as VerifiedEvent\n  }\n  verifyEvent(event: Event): event is VerifiedEvent {\n    try {\n      nw.verifyEvent(event)\n      event[verifiedSymbol] = true\n      return true\n    } catch (err) {\n      return false\n    }\n  }\n}\n\nconst i: Wasm = new Wasm()\nexport const generateSecretKey = i.generateSecretKey\nexport const getPublicKey = i.getPublicKey\nexport const finalizeEvent = i.finalizeEvent\nexport const verifyEvent = i.verifyEvent\nexport * from './core.ts'\n", "export interface Nostr {\n  generateSecretKey(): Uint8Array\n  getPublicKey(secretKey: Uint8Array): string\n  finalizeEvent(event: EventTemplate, secretKey: Uint8Array): VerifiedEvent\n  verifyEvent(event: Event): event is VerifiedEvent\n}\n\n/** Designates a verified event signature. */\nexport const verifiedSymbol = Symbol('verified')\n\nexport type NostrEvent = {\n  kind: number\n  tags: string[][]\n  content: string\n  created_at: number\n  pubkey: string\n  id: string\n  sig: string\n  [verifiedSymbol]?: boolean\n}\n\nexport type Event = NostrEvent\nexport type EventTemplate = Pick<Event, 'kind' | 'tags' | 'content' | 'created_at'>\nexport type UnsignedEvent = Pick<Event, 'kind' | 'tags' | 'content' | 'created_at' | 'pubkey'>\n\n/** An event whose signature has been verified. */\nexport interface VerifiedEvent extends Event {\n  [verifiedSymbol]: true\n}\n\nconst isRecord = (obj: unknown): obj is Record<string, unknown> => obj instanceof Object\n\nexport function validateEvent<T>(event: T): event is T & UnsignedEvent {\n  if (!isRecord(event)) return false\n  if (typeof event.kind !== 'number') return false\n  if (typeof event.content !== 'string') return false\n  if (typeof event.created_at !== 'number') return false\n  if (typeof event.pubkey !== 'string') return false\n  if (!event.pubkey.match(/^[a-f0-9]{64}$/)) return false\n\n  if (!Array.isArray(event.tags)) return false\n  for (let i = 0; i < event.tags.length; i++) {\n    let tag = event.tags[i]\n    if (!Array.isArray(tag)) return false\n    for (let j = 0; j < tag.length; j++) {\n      if (typeof tag[j] !== 'string') return false\n    }\n  }\n\n  return true\n}\n\n/**\n * Sort events in reverse-chronological order by the `created_at` timestamp,\n * and then by the event `id` (lexicographically) in case of ties.\n * This mutates the array.\n */\nexport function sortEvents(events: Event[]): Event[] {\n  return events.sort((a: NostrEvent, b: NostrEvent): number => {\n    if (a.created_at !== b.created_at) {\n      return b.created_at - a.created_at\n    }\n    return a.id.localeCompare(b.id)\n  })\n}\n"],
+  "sourcesContent": ["import { bytesToHex } from '@noble/hashes/utils.js'\nimport { Nostr as NostrWasm } from 'nostr-wasm'\nimport { EventTemplate, Event, Nostr, VerifiedEvent, verifiedSymbol } from './core.ts'\n\nlet nw: NostrWasm\n\nexport function setNostrWasm(x: NostrWasm) {\n  nw = x\n}\n\nclass Wasm implements Nostr {\n  generateSecretKey(): Uint8Array {\n    return nw.generateSecretKey()\n  }\n  getPublicKey(secretKey: Uint8Array): string {\n    return bytesToHex(nw.getPublicKey(secretKey))\n  }\n  finalizeEvent(t: EventTemplate, secretKey: Uint8Array): VerifiedEvent {\n    nw.finalizeEvent(t as any, secretKey)\n    return t as VerifiedEvent\n  }\n  verifyEvent(event: Event): event is VerifiedEvent {\n    try {\n      nw.verifyEvent(event)\n      event[verifiedSymbol] = true\n      return true\n    } catch (err) {\n      return false\n    }\n  }\n}\n\nconst i: Wasm = new Wasm()\nexport const generateSecretKey = i.generateSecretKey\nexport const getPublicKey = i.getPublicKey\nexport const finalizeEvent = i.finalizeEvent\nexport const verifyEvent = i.verifyEvent\nexport * from './core.ts'\n", "export interface Nostr {\n  generateSecretKey(): Uint8Array\n  getPublicKey(secretKey: Uint8Array): string\n  finalizeEvent(event: EventTemplate, secretKey: Uint8Array): VerifiedEvent\n  verifyEvent(event: Event): event is VerifiedEvent\n}\n\n/** Designates a verified event signature. */\nexport const verifiedSymbol = Symbol('verified')\n\nexport type NostrEvent = {\n  kind: number\n  tags: string[][]\n  content: string\n  created_at: number\n  pubkey: string\n  id: string\n  sig: string\n  [verifiedSymbol]?: boolean\n}\n\nexport type Event = NostrEvent\nexport type EventTemplate = Pick<Event, 'kind' | 'tags' | 'content' | 'created_at'>\nexport type UnsignedEvent = Pick<Event, 'kind' | 'tags' | 'content' | 'created_at' | 'pubkey'>\n\n/** An event whose signature has been verified. */\nexport interface VerifiedEvent extends Event {\n  [verifiedSymbol]: true\n}\n\nconst isRecord = (obj: unknown): obj is Record<string, unknown> => obj instanceof Object\n\nexport function validateEvent<T>(event: T): event is T & UnsignedEvent {\n  if (!isRecord(event)) return false\n  if (typeof event.kind !== 'number') return false\n  if (typeof event.content !== 'string') return false\n  if (typeof event.created_at !== 'number') return false\n  if (typeof event.pubkey !== 'string') return false\n  if (!event.pubkey.match(/^[a-f0-9]{64}$/)) return false\n\n  if (!Array.isArray(event.tags)) return false\n  for (let i = 0; i < event.tags.length; i++) {\n    let tag = event.tags[i]\n    if (!Array.isArray(tag)) return false\n    for (let j = 0; j < tag.length; j++) {\n      if (typeof tag[j] !== 'string') return false\n    }\n  }\n\n  return true\n}\n\n/**\n * Sort events in reverse-chronological order by the `created_at` timestamp,\n * and then by the event `id` (lexicographically) in case of ties.\n * This mutates the array.\n */\nexport function sortEvents(events: Event[]): Event[] {\n  return events.sort((a: NostrEvent, b: NostrEvent): number => {\n    if (a.created_at !== b.created_at) {\n      return b.created_at - a.created_at\n    }\n    return a.id.localeCompare(b.id)\n  })\n}\n"],
   "mappings": ";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,mBAA2B;;;ACQpB,IAAM,iBAAiB,OAAO,UAAU;AAsB/C,IAAM,WAAW,CAAC,QAAiD,eAAe;AAE3E,SAAS,cAAiB,OAAsC;AACrE,MAAI,CAAC,SAAS,KAAK;AAAG,WAAO;AAC7B,MAAI,OAAO,MAAM,SAAS;AAAU,WAAO;AAC3C,MAAI,OAAO,MAAM,YAAY;AAAU,WAAO;AAC9C,MAAI,OAAO,MAAM,eAAe;AAAU,WAAO;AACjD,MAAI,OAAO,MAAM,WAAW;AAAU,WAAO;AAC7C,MAAI,CAAC,MAAM,OAAO,MAAM,gBAAgB;AAAG,WAAO;AAElD,MAAI,CAAC,MAAM,QAAQ,MAAM,IAAI;AAAG,WAAO;AACvC,WAASA,KAAI,GAAGA,KAAI,MAAM,KAAK,QAAQA,MAAK;AAC1C,QAAI,MAAM,MAAM,KAAKA;AACrB,QAAI,CAAC,MAAM,QAAQ,GAAG;AAAG,aAAO;AAChC,aAAS,IAAI,GAAG,IAAI,IAAI,QAAQ,KAAK;AACnC,UAAI,OAAO,IAAI,OAAO;AAAU,eAAO;AAAA,IACzC;AAAA,EACF;AAEA,SAAO;AACT;AAOO,SAAS,WAAW,QAA0B;AACnD,SAAO,OAAO,KAAK,CAAC,GAAe,MAA0B;AAC3D,QAAI,EAAE,eAAe,EAAE,YAAY;AACjC,aAAO,EAAE,aAAa,EAAE;AAAA,IAC1B;AACA,WAAO,EAAE,GAAG,cAAc,EAAE,EAAE;AAAA,EAChC,CAAC;AACH;;;AD5DA,IAAI;AAEG,SAAS,aAAa,GAAc;AACzC,OAAK;AACP;AAEA,IAAM,OAAN,MAA4B;AAAA,EAC1B,oBAAgC;AAC9B,WAAO,GAAG,kBAAkB;AAAA,EAC9B;AAAA,EACA,aAAa,WAA+B;AAC1C,eAAO,yBAAW,GAAG,aAAa,SAAS,CAAC;AAAA,EAC9C;AAAA,EACA,cAAc,GAAkB,WAAsC;AACpE,OAAG,cAAc,GAAU,SAAS;AACpC,WAAO;AAAA,EACT;AAAA,EACA,YAAY,OAAsC;AAChD,QAAI;AACF,SAAG,YAAY,KAAK;AACpB,YAAM,kBAAkB;AACxB,aAAO;AAAA,IACT,SAAS,KAAP;AACA,aAAO;AAAA,IACT;AAAA,EACF;AACF;AAEA,IAAM,IAAU,IAAI,KAAK;AAClB,IAAM,oBAAoB,EAAE;AAC5B,IAAM,eAAe,EAAE;AACvB,IAAM,gBAAgB,EAAE;AACxB,IAAM,cAAc,EAAE;",
   "names": ["i"]
 }
Index: package/lib/esm/wasm.js.map
===================================================================
--- package/lib/esm/wasm.js.map
+++ package/lib/esm/wasm.js.map
@@ -1,7 +1,7 @@
 {
   "version": 3,
   "sources": ["../../wasm.ts", "../../core.ts"],
-  "sourcesContent": ["import { bytesToHex } from '@noble/hashes/utils'\nimport { Nostr as NostrWasm } from 'nostr-wasm'\nimport { EventTemplate, Event, Nostr, VerifiedEvent, verifiedSymbol } from './core.ts'\n\nlet nw: NostrWasm\n\nexport function setNostrWasm(x: NostrWasm) {\n  nw = x\n}\n\nclass Wasm implements Nostr {\n  generateSecretKey(): Uint8Array {\n    return nw.generateSecretKey()\n  }\n  getPublicKey(secretKey: Uint8Array): string {\n    return bytesToHex(nw.getPublicKey(secretKey))\n  }\n  finalizeEvent(t: EventTemplate, secretKey: Uint8Array): VerifiedEvent {\n    nw.finalizeEvent(t as any, secretKey)\n    return t as VerifiedEvent\n  }\n  verifyEvent(event: Event): event is VerifiedEvent {\n    try {\n      nw.verifyEvent(event)\n      event[verifiedSymbol] = true\n      return true\n    } catch (err) {\n      return false\n    }\n  }\n}\n\nconst i: Wasm = new Wasm()\nexport const generateSecretKey = i.generateSecretKey\nexport const getPublicKey = i.getPublicKey\nexport const finalizeEvent = i.finalizeEvent\nexport const verifyEvent = i.verifyEvent\nexport * from './core.ts'\n", "export interface Nostr {\n  generateSecretKey(): Uint8Array\n  getPublicKey(secretKey: Uint8Array): string\n  finalizeEvent(event: EventTemplate, secretKey: Uint8Array): VerifiedEvent\n  verifyEvent(event: Event): event is VerifiedEvent\n}\n\n/** Designates a verified event signature. */\nexport const verifiedSymbol = Symbol('verified')\n\nexport type NostrEvent = {\n  kind: number\n  tags: string[][]\n  content: string\n  created_at: number\n  pubkey: string\n  id: string\n  sig: string\n  [verifiedSymbol]?: boolean\n}\n\nexport type Event = NostrEvent\nexport type EventTemplate = Pick<Event, 'kind' | 'tags' | 'content' | 'created_at'>\nexport type UnsignedEvent = Pick<Event, 'kind' | 'tags' | 'content' | 'created_at' | 'pubkey'>\n\n/** An event whose signature has been verified. */\nexport interface VerifiedEvent extends Event {\n  [verifiedSymbol]: true\n}\n\nconst isRecord = (obj: unknown): obj is Record<string, unknown> => obj instanceof Object\n\nexport function validateEvent<T>(event: T): event is T & UnsignedEvent {\n  if (!isRecord(event)) return false\n  if (typeof event.kind !== 'number') return false\n  if (typeof event.content !== 'string') return false\n  if (typeof event.created_at !== 'number') return false\n  if (typeof event.pubkey !== 'string') return false\n  if (!event.pubkey.match(/^[a-f0-9]{64}$/)) return false\n\n  if (!Array.isArray(event.tags)) return false\n  for (let i = 0; i < event.tags.length; i++) {\n    let tag = event.tags[i]\n    if (!Array.isArray(tag)) return false\n    for (let j = 0; j < tag.length; j++) {\n      if (typeof tag[j] !== 'string') return false\n    }\n  }\n\n  return true\n}\n\n/**\n * Sort events in reverse-chronological order by the `created_at` timestamp,\n * and then by the event `id` (lexicographically) in case of ties.\n * This mutates the array.\n */\nexport function sortEvents(events: Event[]): Event[] {\n  return events.sort((a: NostrEvent, b: NostrEvent): number => {\n    if (a.created_at !== b.created_at) {\n      return b.created_at - a.created_at\n    }\n    return a.id.localeCompare(b.id)\n  })\n}\n"],
+  "sourcesContent": ["import { bytesToHex } from '@noble/hashes/utils.js'\nimport { Nostr as NostrWasm } from 'nostr-wasm'\nimport { EventTemplate, Event, Nostr, VerifiedEvent, verifiedSymbol } from './core.ts'\n\nlet nw: NostrWasm\n\nexport function setNostrWasm(x: NostrWasm) {\n  nw = x\n}\n\nclass Wasm implements Nostr {\n  generateSecretKey(): Uint8Array {\n    return nw.generateSecretKey()\n  }\n  getPublicKey(secretKey: Uint8Array): string {\n    return bytesToHex(nw.getPublicKey(secretKey))\n  }\n  finalizeEvent(t: EventTemplate, secretKey: Uint8Array): VerifiedEvent {\n    nw.finalizeEvent(t as any, secretKey)\n    return t as VerifiedEvent\n  }\n  verifyEvent(event: Event): event is VerifiedEvent {\n    try {\n      nw.verifyEvent(event)\n      event[verifiedSymbol] = true\n      return true\n    } catch (err) {\n      return false\n    }\n  }\n}\n\nconst i: Wasm = new Wasm()\nexport const generateSecretKey = i.generateSecretKey\nexport const getPublicKey = i.getPublicKey\nexport const finalizeEvent = i.finalizeEvent\nexport const verifyEvent = i.verifyEvent\nexport * from './core.ts'\n", "export interface Nostr {\n  generateSecretKey(): Uint8Array\n  getPublicKey(secretKey: Uint8Array): string\n  finalizeEvent(event: EventTemplate, secretKey: Uint8Array): VerifiedEvent\n  verifyEvent(event: Event): event is VerifiedEvent\n}\n\n/** Designates a verified event signature. */\nexport const verifiedSymbol = Symbol('verified')\n\nexport type NostrEvent = {\n  kind: number\n  tags: string[][]\n  content: string\n  created_at: number\n  pubkey: string\n  id: string\n  sig: string\n  [verifiedSymbol]?: boolean\n}\n\nexport type Event = NostrEvent\nexport type EventTemplate = Pick<Event, 'kind' | 'tags' | 'content' | 'created_at'>\nexport type UnsignedEvent = Pick<Event, 'kind' | 'tags' | 'content' | 'created_at' | 'pubkey'>\n\n/** An event whose signature has been verified. */\nexport interface VerifiedEvent extends Event {\n  [verifiedSymbol]: true\n}\n\nconst isRecord = (obj: unknown): obj is Record<string, unknown> => obj instanceof Object\n\nexport function validateEvent<T>(event: T): event is T & UnsignedEvent {\n  if (!isRecord(event)) return false\n  if (typeof event.kind !== 'number') return false\n  if (typeof event.content !== 'string') return false\n  if (typeof event.created_at !== 'number') return false\n  if (typeof event.pubkey !== 'string') return false\n  if (!event.pubkey.match(/^[a-f0-9]{64}$/)) return false\n\n  if (!Array.isArray(event.tags)) return false\n  for (let i = 0; i < event.tags.length; i++) {\n    let tag = event.tags[i]\n    if (!Array.isArray(tag)) return false\n    for (let j = 0; j < tag.length; j++) {\n      if (typeof tag[j] !== 'string') return false\n    }\n  }\n\n  return true\n}\n\n/**\n * Sort events in reverse-chronological order by the `created_at` timestamp,\n * and then by the event `id` (lexicographically) in case of ties.\n * This mutates the array.\n */\nexport function sortEvents(events: Event[]): Event[] {\n  return events.sort((a: NostrEvent, b: NostrEvent): number => {\n    if (a.created_at !== b.created_at) {\n      return b.created_at - a.created_at\n    }\n    return a.id.localeCompare(b.id)\n  })\n}\n"],
   "mappings": ";AAAA,SAAS,kBAAkB;;;ACQpB,IAAM,iBAAiB,OAAO,UAAU;AAsB/C,IAAM,WAAW,CAAC,QAAiD,eAAe;AAE3E,SAAS,cAAiB,OAAsC;AACrE,MAAI,CAAC,SAAS,KAAK;AAAG,WAAO;AAC7B,MAAI,OAAO,MAAM,SAAS;AAAU,WAAO;AAC3C,MAAI,OAAO,MAAM,YAAY;AAAU,WAAO;AAC9C,MAAI,OAAO,MAAM,eAAe;AAAU,WAAO;AACjD,MAAI,OAAO,MAAM,WAAW;AAAU,WAAO;AAC7C,MAAI,CAAC,MAAM,OAAO,MAAM,gBAAgB;AAAG,WAAO;AAElD,MAAI,CAAC,MAAM,QAAQ,MAAM,IAAI;AAAG,WAAO;AACvC,WAASA,KAAI,GAAGA,KAAI,MAAM,KAAK,QAAQA,MAAK;AAC1C,QAAI,MAAM,MAAM,KAAKA;AACrB,QAAI,CAAC,MAAM,QAAQ,GAAG;AAAG,aAAO;AAChC,aAAS,IAAI,GAAG,IAAI,IAAI,QAAQ,KAAK;AACnC,UAAI,OAAO,IAAI,OAAO;AAAU,eAAO;AAAA,IACzC;AAAA,EACF;AAEA,SAAO;AACT;AAOO,SAAS,WAAW,QAA0B;AACnD,SAAO,OAAO,KAAK,CAAC,GAAe,MAA0B;AAC3D,QAAI,EAAE,eAAe,EAAE,YAAY;AACjC,aAAO,EAAE,aAAa,EAAE;AAAA,IAC1B;AACA,WAAO,EAAE,GAAG,cAAc,EAAE,EAAE;AAAA,EAChC,CAAC;AACH;;;AD5DA,IAAI;AAEG,SAAS,aAAa,GAAc;AACzC,OAAK;AACP;AAEA,IAAM,OAAN,MAA4B;AAAA,EAC1B,oBAAgC;AAC9B,WAAO,GAAG,kBAAkB;AAAA,EAC9B;AAAA,EACA,aAAa,WAA+B;AAC1C,WAAO,WAAW,GAAG,aAAa,SAAS,CAAC;AAAA,EAC9C;AAAA,EACA,cAAc,GAAkB,WAAsC;AACpE,OAAG,cAAc,GAAU,SAAS;AACpC,WAAO;AAAA,EACT;AAAA,EACA,YAAY,OAAsC;AAChD,QAAI;AACF,SAAG,YAAY,KAAK;AACpB,YAAM,kBAAkB;AACxB,aAAO;AAAA,IACT,SAAS,KAAP;AACA,aAAO;AAAA,IACT;AAAA,EACF;AACF;AAEA,IAAM,IAAU,IAAI,KAAK;AAClB,IAAM,oBAAoB,EAAE;AAC5B,IAAM,eAAe,EAAE;AACvB,IAAM,gBAAgB,EAAE;AACxB,IAAM,cAAc,EAAE;",
   "names": ["i"]
 }
Index: package/lib/types/utils.d.ts
===================================================================
--- package/lib/types/utils.d.ts
+++ package/lib/types/utils.d.ts
@@ -1,8 +1,8 @@
 import type { Event } from './core.ts';
 export declare const utf8Decoder: TextDecoder;
 export declare const utf8Encoder: TextEncoder;
-export { bytesToHex, hexToBytes } from '@noble/hashes/utils';
+export { bytesToHex, hexToBytes } from '@noble/hashes/utils.js';
 export declare function normalizeURL(url: string): string;
 export declare function insertEventIntoDescendingList(sortedArray: Event[], event: Event): Event[];
 export declare function insertEventIntoAscendingList(sortedArray: Event[], event: Event): Event[];
 export declare function binarySearch<T>(arr: T[], compare: (b: T) => number): [number, boolean];