"use strict"; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // index.ts var nostr_tools_exports = {}; __export(nostr_tools_exports, { Kind: () => Kind, SimplePool: () => SimplePool, eventsGenerator: () => eventsGenerator, finishEvent: () => finishEvent, fj: () => fakejson_exports, generatePrivateKey: () => generatePrivateKey, getBlankEvent: () => getBlankEvent, getEventHash: () => getEventHash, getPublicKey: () => getPublicKey, getSignature: () => getSignature, matchFilter: () => matchFilter, matchFilters: () => matchFilters, mergeFilters: () => mergeFilters, nip04: () => nip04_exports, nip05: () => nip05_exports, nip06: () => nip06_exports, nip10: () => nip10_exports, nip13: () => nip13_exports, nip18: () => nip18_exports, nip19: () => nip19_exports, nip21: () => nip21_exports, nip25: () => nip25_exports, nip26: () => nip26_exports, nip27: () => nip27_exports, nip28: () => nip28_exports, nip39: () => nip39_exports, nip42: () => nip42_exports, nip44: () => nip44_exports, nip47: () => nip47_exports, nip57: () => nip57_exports, nip98: () => nip98_exports, parseReferences: () => parseReferences, relayInit: () => relayInit, serializeEvent: () => serializeEvent, signEvent: () => signEvent, utils: () => utils_exports, validateEvent: () => validateEvent, verifiedSymbol: () => verifiedSymbol, verifySignature: () => verifySignature }); module.exports = __toCommonJS(nostr_tools_exports); // keys.ts var import_secp256k1 = require("@noble/curves/secp256k1"); var import_utils = require("@noble/hashes/utils"); function generatePrivateKey() { return (0, import_utils.bytesToHex)(import_secp256k1.schnorr.utils.randomPrivateKey()); } function getPublicKey(privateKey) { return (0, import_utils.bytesToHex)(import_secp256k1.schnorr.getPublicKey(privateKey)); } // event.ts var import_secp256k12 = require("@noble/curves/secp256k1"); var import_sha256 = require("@noble/hashes/sha256"); var import_utils2 = require("@noble/hashes/utils"); // utils.ts var utils_exports = {}; __export(utils_exports, { MessageNode: () => MessageNode, MessageQueue: () => MessageQueue, insertEventIntoAscendingList: () => insertEventIntoAscendingList, insertEventIntoDescendingList: () => insertEventIntoDescendingList, normalizeURL: () => normalizeURL, utf8Decoder: () => utf8Decoder, utf8Encoder: () => utf8Encoder }); var utf8Decoder = new TextDecoder("utf-8"); var utf8Encoder = new TextEncoder(); function normalizeURL(url) { let p = new URL(url); p.pathname = p.pathname.replace(/\/+/g, "/"); if (p.pathname.endsWith("/")) p.pathname = p.pathname.slice(0, -1); if (p.port === "80" && p.protocol === "ws:" || p.port === "443" && p.protocol === "wss:") p.port = ""; p.searchParams.sort(); p.hash = ""; return p.toString(); } function insertEventIntoDescendingList(sortedArray, event) { let start = 0; let end = sortedArray.length - 1; let midPoint; let position = start; if (end < 0) { position = 0; } else if (event.created_at < sortedArray[end].created_at) { position = end + 1; } else if (event.created_at >= sortedArray[start].created_at) { position = start; } else while (true) { if (end <= start + 1) { position = end; break; } midPoint = Math.floor(start + (end - start) / 2); if (sortedArray[midPoint].created_at > event.created_at) { start = midPoint; } else if (sortedArray[midPoint].created_at < event.created_at) { end = midPoint; } else { position = midPoint; break; } } if (sortedArray[position]?.id !== event.id) { return [...sortedArray.slice(0, position), event, ...sortedArray.slice(position)]; } return sortedArray; } function insertEventIntoAscendingList(sortedArray, event) { let start = 0; let end = sortedArray.length - 1; let midPoint; let position = start; if (end < 0) { position = 0; } else if (event.created_at > sortedArray[end].created_at) { position = end + 1; } else if (event.created_at <= sortedArray[start].created_at) { position = start; } else while (true) { if (end <= start + 1) { position = end; break; } midPoint = Math.floor(start + (end - start) / 2); if (sortedArray[midPoint].created_at < event.created_at) { start = midPoint; } else if (sortedArray[midPoint].created_at > event.created_at) { end = midPoint; } else { position = midPoint; break; } } if (sortedArray[position]?.id !== event.id) { return [...sortedArray.slice(0, position), event, ...sortedArray.slice(position)]; } return sortedArray; } var MessageNode = class { _value; _next; get value() { return this._value; } set value(message) { this._value = message; } get next() { return this._next; } set next(node) { this._next = node; } constructor(message) { this._value = message; this._next = null; } }; var MessageQueue = class { _first; _last; get first() { return this._first; } set first(messageNode) { this._first = messageNode; } get last() { return this._last; } set last(messageNode) { this._last = messageNode; } _size; get size() { return this._size; } set size(v) { this._size = v; } constructor() { this._first = null; this._last = null; this._size = 0; } enqueue(message) { const newNode = new MessageNode(message); if (this._size === 0 || !this._last) { this._first = newNode; this._last = newNode; } else { this._last.next = newNode; this._last = newNode; } this._size++; return true; } dequeue() { if (this._size === 0 || !this._first) return null; let prev = this._first; this._first = prev.next; prev.next = null; this._size--; return prev.value; } }; // event.ts var verifiedSymbol = Symbol("verified"); var Kind = /* @__PURE__ */ ((Kind3) => { Kind3[Kind3["Metadata"] = 0] = "Metadata"; Kind3[Kind3["Text"] = 1] = "Text"; Kind3[Kind3["RecommendRelay"] = 2] = "RecommendRelay"; Kind3[Kind3["Contacts"] = 3] = "Contacts"; Kind3[Kind3["EncryptedDirectMessage"] = 4] = "EncryptedDirectMessage"; Kind3[Kind3["EventDeletion"] = 5] = "EventDeletion"; Kind3[Kind3["Repost"] = 6] = "Repost"; Kind3[Kind3["Reaction"] = 7] = "Reaction"; Kind3[Kind3["BadgeAward"] = 8] = "BadgeAward"; Kind3[Kind3["ChannelCreation"] = 40] = "ChannelCreation"; Kind3[Kind3["ChannelMetadata"] = 41] = "ChannelMetadata"; Kind3[Kind3["ChannelMessage"] = 42] = "ChannelMessage"; Kind3[Kind3["ChannelHideMessage"] = 43] = "ChannelHideMessage"; Kind3[Kind3["ChannelMuteUser"] = 44] = "ChannelMuteUser"; Kind3[Kind3["Blank"] = 255] = "Blank"; Kind3[Kind3["Report"] = 1984] = "Report"; Kind3[Kind3["ZapRequest"] = 9734] = "ZapRequest"; Kind3[Kind3["Zap"] = 9735] = "Zap"; Kind3[Kind3["RelayList"] = 10002] = "RelayList"; Kind3[Kind3["ClientAuth"] = 22242] = "ClientAuth"; Kind3[Kind3["NwcRequest"] = 23194] = "NwcRequest"; Kind3[Kind3["HttpAuth"] = 27235] = "HttpAuth"; Kind3[Kind3["ProfileBadge"] = 30008] = "ProfileBadge"; Kind3[Kind3["BadgeDefinition"] = 30009] = "BadgeDefinition"; Kind3[Kind3["Article"] = 30023] = "Article"; Kind3[Kind3["FileMetadata"] = 1063] = "FileMetadata"; return Kind3; })(Kind || {}); function getBlankEvent(kind = 255 /* Blank */) { return { kind, content: "", tags: [], created_at: 0 }; } function finishEvent(t, privateKey) { const event = t; event.pubkey = getPublicKey(privateKey); event.id = getEventHash(event); event.sig = getSignature(event, privateKey); event[verifiedSymbol] = true; return event; } function serializeEvent(evt) { if (!validateEvent(evt)) 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))); return (0, import_utils2.bytesToHex)(eventHash); } var isRecord = (obj) => obj instanceof Object; function validateEvent(event) { if (!isRecord(event)) return false; if (typeof event.kind !== "number") return false; if (typeof event.content !== "string") return false; if (typeof event.created_at !== "number") return false; if (typeof event.pubkey !== "string") return false; if (!event.pubkey.match(/^[a-f0-9]{64}$/)) return false; if (!Array.isArray(event.tags)) return false; for (let i = 0; i < event.tags.length; i++) { let tag = event.tags[i]; if (!Array.isArray(tag)) return false; for (let j = 0; j < tag.length; j++) { if (typeof tag[j] === "object") return false; } } return true; } function verifySignature(event) { if (typeof event[verifiedSymbol] === "boolean") return event[verifiedSymbol]; const hash = getEventHash(event); if (hash !== event.id) { return event[verifiedSymbol] = false; } try { return event[verifiedSymbol] = import_secp256k12.schnorr.verify(event.sig, hash, event.pubkey); } catch (err) { return event[verifiedSymbol] = false; } } function signEvent(event, key) { console.warn( "nostr-tools: `signEvent` is deprecated and will be removed or changed in the future. Please use `getSignature` instead." ); return getSignature(event, key); } function getSignature(event, key) { return (0, import_utils2.bytesToHex)(import_secp256k12.schnorr.sign(getEventHash(event), key)); } // filter.ts function matchFilter(filter, event) { if (filter.ids && filter.ids.indexOf(event.id) === -1) { if (!filter.ids.some((prefix) => event.id.startsWith(prefix))) { return false; } } if (filter.kinds && filter.kinds.indexOf(event.kind) === -1) return false; if (filter.authors && filter.authors.indexOf(event.pubkey) === -1) { if (!filter.authors.some((prefix) => event.pubkey.startsWith(prefix))) { return false; } } for (let f in filter) { if (f[0] === "#") { let tagName = f.slice(1); let values = filter[`#${tagName}`]; if (values && !event.tags.find(([t, v]) => t === f.slice(1) && values.indexOf(v) !== -1)) return false; } } if (filter.since && event.created_at < filter.since) return false; if (filter.until && event.created_at > filter.until) return false; return true; } function matchFilters(filters, event) { for (let i = 0; i < filters.length; i++) { if (matchFilter(filters[i], event)) return true; } return false; } function mergeFilters(...filters) { let result = {}; for (let i = 0; i < filters.length; i++) { let filter = filters[i]; Object.entries(filter).forEach(([property, values]) => { if (property === "kinds" || property === "ids" || property === "authors" || property[0] === "#") { result[property] = result[property] || []; for (let v = 0; v < values.length; v++) { let value = values[v]; if (!result[property].includes(value)) result[property].push(value); } } }); if (filter.limit && (!result.limit || filter.limit > result.limit)) result.limit = filter.limit; if (filter.until && (!result.until || filter.until > result.until)) result.until = filter.until; if (filter.since && (!result.since || filter.since < result.since)) result.since = filter.since; } return result; } // fakejson.ts var fakejson_exports = {}; __export(fakejson_exports, { getHex64: () => getHex64, getInt: () => getInt, getSubscriptionId: () => getSubscriptionId, matchEventId: () => matchEventId, matchEventKind: () => matchEventKind, matchEventPubkey: () => matchEventPubkey }); function getHex64(json, field) { let len = field.length + 3; let idx = json.indexOf(`"${field}":`) + len; let s = json.slice(idx).indexOf(`"`) + idx + 1; return json.slice(s, s + 64); } function getInt(json, field) { let len = field.length; let idx = json.indexOf(`"${field}":`) + len + 3; let sliced = json.slice(idx); let end = Math.min(sliced.indexOf(","), sliced.indexOf("}")); return parseInt(sliced.slice(0, end), 10); } function getSubscriptionId(json) { let idx = json.slice(0, 22).indexOf(`"EVENT"`); if (idx === -1) return null; let pstart = json.slice(idx + 7 + 1).indexOf(`"`); if (pstart === -1) return null; let start = idx + 7 + 1 + pstart; let pend = json.slice(start + 1, 80).indexOf(`"`); if (pend === -1) return null; let end = start + 1 + pend; return json.slice(start + 1, end); } function matchEventId(json, id) { return id === getHex64(json, "id"); } function matchEventPubkey(json, pubkey) { return pubkey === getHex64(json, "pubkey"); } function matchEventKind(json, kind) { return kind === getInt(json, "kind"); } // relay.ts var newListeners = () => ({ connect: [], disconnect: [], error: [], notice: [], auth: [] }); function relayInit(url, options = {}) { let { listTimeout = 3e3, getTimeout = 3e3, countTimeout = 3e3 } = options; var ws; var openSubs = {}; var listeners = newListeners(); var subListeners = {}; var pubListeners = {}; var connectionPromise; async function connectRelay() { if (connectionPromise) return connectionPromise; connectionPromise = new Promise((resolve, reject) => { try { ws = new WebSocket(url); } catch (err) { reject(err); } ws.onopen = () => { listeners.connect.forEach((cb) => cb()); resolve(); }; ws.onerror = () => { connectionPromise = void 0; listeners.error.forEach((cb) => cb()); reject(); }; ws.onclose = async () => { connectionPromise = void 0; listeners.disconnect.forEach((cb) => cb()); }; let incomingMessageQueue = new MessageQueue(); let handleNextInterval; ws.onmessage = (e) => { incomingMessageQueue.enqueue(e.data); if (!handleNextInterval) { handleNextInterval = setInterval(handleNext, 0); } }; function handleNext() { if (incomingMessageQueue.size === 0) { clearInterval(handleNextInterval); handleNextInterval = null; return; } var json = incomingMessageQueue.dequeue(); if (!json) return; let subid = getSubscriptionId(json); if (subid) { let so = openSubs[subid]; if (so && so.alreadyHaveEvent && so.alreadyHaveEvent(getHex64(json, "id"), url)) { return; } } try { let data = JSON.parse(json); switch (data[0]) { case "EVENT": { let id2 = data[1]; let event = data[2]; if (validateEvent(event) && openSubs[id2] && (openSubs[id2].skipVerification || verifySignature(event)) && matchFilters(openSubs[id2].filters, event)) { openSubs[id2]; (subListeners[id2]?.event || []).forEach((cb) => cb(event)); } return; } case "COUNT": let id = data[1]; let payload = data[2]; if (openSubs[id]) { ; (subListeners[id]?.count || []).forEach((cb) => cb(payload)); } return; case "EOSE": { let id2 = data[1]; if (id2 in subListeners) { subListeners[id2].eose.forEach((cb) => cb()); subListeners[id2].eose = []; } return; } case "OK": { let id2 = data[1]; let ok = data[2]; let reason = data[3] || ""; if (id2 in pubListeners) { let { resolve: resolve2, reject: reject2 } = pubListeners[id2]; if (ok) resolve2(null); else reject2(new Error(reason)); } return; } case "NOTICE": let notice = data[1]; listeners.notice.forEach((cb) => cb(notice)); return; case "AUTH": { let challenge = data[1]; listeners.auth?.forEach((cb) => cb(challenge)); return; } } } catch (err) { return; } } }); return connectionPromise; } function connected() { return ws?.readyState === 1; } async function connect() { if (connected()) return; await connectRelay(); } async function trySend(params) { let msg = JSON.stringify(params); if (!connected()) { await new Promise((resolve) => setTimeout(resolve, 1e3)); if (!connected()) { return; } } try { ws.send(msg); } catch (err) { console.log(err); } } const sub = (filters, { verb = "REQ", skipVerification = false, alreadyHaveEvent = null, id = Math.random().toString().slice(2) } = {}) => { let subid = id; openSubs[subid] = { id: subid, filters, skipVerification, alreadyHaveEvent }; trySend([verb, subid, ...filters]); let subscription = { sub: (newFilters, newOpts = {}) => sub(newFilters || filters, { skipVerification: newOpts.skipVerification || skipVerification, alreadyHaveEvent: newOpts.alreadyHaveEvent || alreadyHaveEvent, id: subid }), unsub: () => { delete openSubs[subid]; delete subListeners[subid]; trySend(["CLOSE", subid]); }, on: (type, cb) => { subListeners[subid] = subListeners[subid] || { event: [], count: [], eose: [] }; subListeners[subid][type].push(cb); }, off: (type, cb) => { let listeners2 = subListeners[subid]; let idx = listeners2[type].indexOf(cb); if (idx >= 0) listeners2[type].splice(idx, 1); }, get events() { return eventsGenerator(subscription); } }; return subscription; }; function _publishEvent(event, type) { return new Promise((resolve, reject) => { if (!event.id) { reject(new Error(`event ${event} has no id`)); return; } let id = event.id; trySend([type, event]); pubListeners[id] = { resolve, reject }; }); } return { url, sub, on: (type, cb) => { listeners[type].push(cb); if (type === "connect" && ws?.readyState === 1) { ; cb(); } }, off: (type, cb) => { let index = listeners[type].indexOf(cb); if (index !== -1) listeners[type].splice(index, 1); }, list: (filters, opts) => new Promise((resolve) => { let s = sub(filters, opts); let events = []; let timeout = setTimeout(() => { s.unsub(); resolve(events); }, listTimeout); s.on("eose", () => { s.unsub(); clearTimeout(timeout); resolve(events); }); s.on("event", (event) => { events.push(event); }); }), get: (filter, opts) => new Promise((resolve) => { let s = sub([filter], opts); let timeout = setTimeout(() => { s.unsub(); resolve(null); }, getTimeout); s.on("event", (event) => { s.unsub(); clearTimeout(timeout); resolve(event); }); }), count: (filters) => new Promise((resolve) => { let s = sub(filters, { ...sub, verb: "COUNT" }); let timeout = setTimeout(() => { s.unsub(); resolve(null); }, countTimeout); s.on("count", (event) => { s.unsub(); clearTimeout(timeout); resolve(event); }); }), async publish(event) { await _publishEvent(event, "EVENT"); }, async auth(event) { await _publishEvent(event, "AUTH"); }, connect, close() { listeners = newListeners(); subListeners = {}; pubListeners = {}; if (ws?.readyState === WebSocket.OPEN) { ws.close(); } }, get status() { return ws?.readyState ?? 3; } }; } async function* eventsGenerator(sub) { let nextResolve; const eventQueue = []; const pushToQueue = (event) => { if (nextResolve) { nextResolve(event); nextResolve = void 0; } else { eventQueue.push(event); } }; sub.on("event", pushToQueue); try { while (true) { if (eventQueue.length > 0) { yield eventQueue.shift(); } else { const event = await new Promise((resolve) => { nextResolve = resolve; }); yield event; } } } finally { sub.off("event", pushToQueue); } } // pool.ts var SimplePool = class { _conn; _seenOn = {}; batchedByKey = {}; eoseSubTimeout; getTimeout; seenOnEnabled = true; batchInterval = 100; constructor(options = {}) { this._conn = {}; this.eoseSubTimeout = options.eoseSubTimeout || 3400; this.getTimeout = options.getTimeout || 3400; this.seenOnEnabled = options.seenOnEnabled !== false; this.batchInterval = options.batchInterval || 100; } close(relays) { relays.forEach((url) => { let relay = this._conn[normalizeURL(url)]; if (relay) relay.close(); }); } async ensureRelay(url) { const nm = normalizeURL(url); if (!this._conn[nm]) { this._conn[nm] = relayInit(nm, { getTimeout: this.getTimeout * 0.9, listTimeout: this.getTimeout * 0.9 }); } const relay = this._conn[nm]; await relay.connect(); return relay; } sub(relays, filters, opts) { let _knownIds = /* @__PURE__ */ new Set(); let modifiedOpts = { ...opts || {} }; modifiedOpts.alreadyHaveEvent = (id, url) => { if (opts?.alreadyHaveEvent?.(id, url)) { return true; } if (this.seenOnEnabled) { let set = this._seenOn[id] || /* @__PURE__ */ new Set(); set.add(url); this._seenOn[id] = set; } return _knownIds.has(id); }; let subs = []; let eventListeners = /* @__PURE__ */ new Set(); let eoseListeners = /* @__PURE__ */ new Set(); let eosesMissing = relays.length; let eoseSent = false; let eoseTimeout = setTimeout( () => { eoseSent = true; for (let cb of eoseListeners.values()) cb(); }, opts?.eoseSubTimeout || this.eoseSubTimeout ); relays.filter((r, i, a) => a.indexOf(r) === i).forEach(async (relay) => { let r; try { r = await this.ensureRelay(relay); } catch (err) { handleEose(); return; } if (!r) return; let s = r.sub(filters, modifiedOpts); s.on("event", (event) => { _knownIds.add(event.id); for (let cb of eventListeners.values()) cb(event); }); s.on("eose", () => { if (eoseSent) return; handleEose(); }); subs.push(s); function handleEose() { eosesMissing--; if (eosesMissing === 0) { clearTimeout(eoseTimeout); for (let cb of eoseListeners.values()) cb(); } } }); let greaterSub = { sub(filters2, opts2) { subs.forEach((sub) => sub.sub(filters2, opts2)); return greaterSub; }, unsub() { subs.forEach((sub) => sub.unsub()); }, on(type, cb) { if (type === "event") { eventListeners.add(cb); } else if (type === "eose") { eoseListeners.add(cb); } }, off(type, cb) { if (type === "event") { eventListeners.delete(cb); } else if (type === "eose") eoseListeners.delete(cb); }, get events() { return eventsGenerator(greaterSub); } }; return greaterSub; } get(relays, filter, opts) { return new Promise((resolve) => { let sub = this.sub(relays, [filter], opts); let timeout = setTimeout(() => { sub.unsub(); resolve(null); }, this.getTimeout); sub.on("event", (event) => { resolve(event); clearTimeout(timeout); sub.unsub(); }); }); } list(relays, filters, opts) { return new Promise((resolve) => { let events = []; let sub = this.sub(relays, filters, opts); sub.on("event", (event) => { events.push(event); }); sub.on("eose", () => { sub.unsub(); resolve(events); }); }); } batchedList(batchKey, relays, filters) { return new Promise((resolve) => { if (!this.batchedByKey[batchKey]) { this.batchedByKey[batchKey] = [ { filters, relays, resolve, events: [] } ]; setTimeout(() => { Object.keys(this.batchedByKey).forEach(async (batchKey2) => { const batchedRequests = this.batchedByKey[batchKey2]; const filters2 = []; const relays2 = []; batchedRequests.forEach((br) => { filters2.push(...br.filters); relays2.push(...br.relays); }); const sub = this.sub(relays2, [mergeFilters(...filters2)]); sub.on("event", (event) => { batchedRequests.forEach((br) => matchFilters(br.filters, event) && br.events.push(event)); }); sub.on("eose", () => { sub.unsub(); batchedRequests.forEach((br) => br.resolve(br.events)); }); delete this.batchedByKey[batchKey2]; }); }, this.batchInterval); } else { this.batchedByKey[batchKey].push({ filters, relays, resolve, events: [] }); } }); } publish(relays, event) { return relays.map(async (relay) => { let r = await this.ensureRelay(relay); return r.publish(event); }); } seenOn(id) { return Array.from(this._seenOn[id]?.values?.() || []); } }; // nip19.ts var nip19_exports = {}; __export(nip19_exports, { BECH32_REGEX: () => BECH32_REGEX, decode: () => decode, naddrEncode: () => naddrEncode, neventEncode: () => neventEncode, noteEncode: () => noteEncode, nprofileEncode: () => nprofileEncode, npubEncode: () => npubEncode, nrelayEncode: () => nrelayEncode, nsecEncode: () => nsecEncode }); var import_utils6 = require("@noble/hashes/utils"); var import_base = require("@scure/base"); var Bech32MaxSize = 5e3; var BECH32_REGEX = /[\x21-\x7E]{1,83}1[023456789acdefghjklmnpqrstuvwxyz]{6,}/; function integerToUint8Array(number) { const uint8Array = new Uint8Array(4); uint8Array[0] = number >> 24 & 255; uint8Array[1] = number >> 16 & 255; uint8Array[2] = number >> 8 & 255; uint8Array[3] = number & 255; return uint8Array; } function decode(nip19) { let { prefix, words } = import_base.bech32.decode(nip19, Bech32MaxSize); let data = new Uint8Array(import_base.bech32.fromWords(words)); switch (prefix) { case "nprofile": { let tlv = parseTLV(data); if (!tlv[0]?.[0]) throw new Error("missing TLV 0 for nprofile"); if (tlv[0][0].length !== 32) throw new Error("TLV 0 should be 32 bytes"); return { type: "nprofile", data: { pubkey: (0, import_utils6.bytesToHex)(tlv[0][0]), relays: tlv[1] ? tlv[1].map((d) => utf8Decoder.decode(d)) : [] } }; } case "nevent": { let tlv = parseTLV(data); if (!tlv[0]?.[0]) throw new Error("missing TLV 0 for nevent"); if (tlv[0][0].length !== 32) throw new Error("TLV 0 should be 32 bytes"); if (tlv[2] && tlv[2][0].length !== 32) throw new Error("TLV 2 should be 32 bytes"); if (tlv[3] && tlv[3][0].length !== 4) throw new Error("TLV 3 should be 4 bytes"); return { type: "nevent", data: { id: (0, import_utils6.bytesToHex)(tlv[0][0]), relays: tlv[1] ? tlv[1].map((d) => utf8Decoder.decode(d)) : [], author: tlv[2]?.[0] ? (0, import_utils6.bytesToHex)(tlv[2][0]) : void 0, kind: tlv[3]?.[0] ? parseInt((0, import_utils6.bytesToHex)(tlv[3][0]), 16) : void 0 } }; } case "naddr": { let tlv = parseTLV(data); if (!tlv[0]?.[0]) throw new Error("missing TLV 0 for naddr"); if (!tlv[2]?.[0]) throw new Error("missing TLV 2 for naddr"); if (tlv[2][0].length !== 32) throw new Error("TLV 2 should be 32 bytes"); if (!tlv[3]?.[0]) throw new Error("missing TLV 3 for naddr"); if (tlv[3][0].length !== 4) throw new Error("TLV 3 should be 4 bytes"); return { type: "naddr", data: { identifier: utf8Decoder.decode(tlv[0][0]), pubkey: (0, import_utils6.bytesToHex)(tlv[2][0]), kind: parseInt((0, import_utils6.bytesToHex)(tlv[3][0]), 16), relays: tlv[1] ? tlv[1].map((d) => utf8Decoder.decode(d)) : [] } }; } case "nrelay": { let tlv = parseTLV(data); if (!tlv[0]?.[0]) throw new Error("missing TLV 0 for nrelay"); return { type: "nrelay", data: utf8Decoder.decode(tlv[0][0]) }; } case "nsec": case "npub": case "note": return { type: prefix, data: (0, import_utils6.bytesToHex)(data) }; default: throw new Error(`unknown prefix ${prefix}`); } } function parseTLV(data) { let result = {}; let rest = data; while (rest.length > 0) { let t = rest[0]; let l = rest[1]; if (!l) throw new Error(`malformed TLV ${t}`); let v = rest.slice(2, 2 + l); rest = rest.slice(2 + l); if (v.length < l) throw new Error(`not enough data to read on TLV ${t}`); result[t] = result[t] || []; result[t].push(v); } return result; } function nsecEncode(hex) { return encodeBytes("nsec", hex); } function npubEncode(hex) { return encodeBytes("npub", hex); } function noteEncode(hex) { return encodeBytes("note", hex); } function encodeBech32(prefix, data) { let words = import_base.bech32.toWords(data); return import_base.bech32.encode(prefix, words, Bech32MaxSize); } function encodeBytes(prefix, hex) { let data = (0, import_utils6.hexToBytes)(hex); return encodeBech32(prefix, data); } function nprofileEncode(profile) { let data = encodeTLV({ 0: [(0, import_utils6.hexToBytes)(profile.pubkey)], 1: (profile.relays || []).map((url) => utf8Encoder.encode(url)) }); return encodeBech32("nprofile", data); } function neventEncode(event) { let kindArray; if (event.kind != void 0) { kindArray = integerToUint8Array(event.kind); } let data = encodeTLV({ 0: [(0, import_utils6.hexToBytes)(event.id)], 1: (event.relays || []).map((url) => utf8Encoder.encode(url)), 2: event.author ? [(0, import_utils6.hexToBytes)(event.author)] : [], 3: kindArray ? [new Uint8Array(kindArray)] : [] }); return encodeBech32("nevent", data); } function naddrEncode(addr) { let kind = new ArrayBuffer(4); 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: [(0, import_utils6.hexToBytes)(addr.pubkey)], 3: [new Uint8Array(kind)] }); return encodeBech32("naddr", data); } function nrelayEncode(url) { let data = encodeTLV({ 0: [utf8Encoder.encode(url)] }); return encodeBech32("nrelay", data); } function encodeTLV(tlv) { let entries = []; Object.entries(tlv).forEach(([t, vs]) => { vs.forEach((v) => { let entry = new Uint8Array(v.length + 2); entry.set([parseInt(t)], 0); entry.set([v.length], 1); entry.set(v, 2); entries.push(entry); }); }); return (0, import_utils6.concatBytes)(...entries); } // references.ts var mentionRegex = /\bnostr:((note|npub|naddr|nevent|nprofile)1\w+)\b|#\[(\d+)\]/g; function parseReferences(evt) { let references = []; for (let ref of evt.content.matchAll(mentionRegex)) { if (ref[2]) { try { let { type, data } = decode(ref[1]); switch (type) { case "npub": { references.push({ text: ref[0], profile: { pubkey: data, relays: [] } }); break; } case "nprofile": { references.push({ text: ref[0], profile: data }); break; } case "note": { references.push({ text: ref[0], event: { id: data, relays: [] } }); break; } case "nevent": { references.push({ text: ref[0], event: data }); break; } case "naddr": { references.push({ text: ref[0], address: data }); break; } } } catch (err) { } } else if (ref[3]) { let idx = parseInt(ref[3], 10); let tag = evt.tags[idx]; if (!tag) continue; switch (tag[0]) { case "p": { references.push({ text: ref[0], profile: { pubkey: tag[1], relays: tag[2] ? [tag[2]] : [] } }); break; } case "e": { references.push({ text: ref[0], event: { id: tag[1], relays: tag[2] ? [tag[2]] : [] } }); break; } case "a": { try { let [kind, pubkey, identifier] = tag[1].split(":"); references.push({ text: ref[0], address: { identifier, pubkey, kind: parseInt(kind, 10), relays: tag[2] ? [tag[2]] : [] } }); } catch (err) { } break; } } } } return references; } // nip04.ts var nip04_exports = {}; __export(nip04_exports, { decrypt: () => decrypt, encrypt: () => encrypt }); var import_utils8 = require("@noble/hashes/utils"); var import_secp256k13 = require("@noble/curves/secp256k1"); var import_base2 = require("@scure/base"); if (typeof crypto !== "undefined" && !crypto.subtle && crypto.webcrypto) { crypto.subtle = crypto.webcrypto.subtle; } async function encrypt(privkey, pubkey, text) { const key = import_secp256k13.secp256k1.getSharedSecret(privkey, "02" + pubkey); const normalizedKey = getNormalizedX(key); let iv = Uint8Array.from((0, import_utils8.randomBytes)(16)); let plaintext = utf8Encoder.encode(text); let cryptoKey = await crypto.subtle.importKey("raw", normalizedKey, { name: "AES-CBC" }, false, ["encrypt"]); let ciphertext = await crypto.subtle.encrypt({ name: "AES-CBC", iv }, cryptoKey, plaintext); let ctb64 = import_base2.base64.encode(new Uint8Array(ciphertext)); let ivb64 = import_base2.base64.encode(new Uint8Array(iv.buffer)); return `${ctb64}?iv=${ivb64}`; } async function decrypt(privkey, pubkey, data) { let [ctb64, ivb64] = data.split("?iv="); let key = import_secp256k13.secp256k1.getSharedSecret(privkey, "02" + pubkey); let normalizedKey = getNormalizedX(key); let cryptoKey = await crypto.subtle.importKey("raw", normalizedKey, { name: "AES-CBC" }, false, ["decrypt"]); let ciphertext = import_base2.base64.decode(ctb64); let iv = import_base2.base64.decode(ivb64); let plaintext = await crypto.subtle.decrypt({ name: "AES-CBC", iv }, cryptoKey, ciphertext); let text = utf8Decoder.decode(plaintext); return text; } function getNormalizedX(key) { return key.slice(1, 33); } // nip05.ts var nip05_exports = {}; __export(nip05_exports, { NIP05_REGEX: () => NIP05_REGEX, queryProfile: () => queryProfile, searchDomain: () => searchDomain, useFetchImplementation: () => useFetchImplementation }); var NIP05_REGEX = /^(?:([\w.+-]+)@)?([\w.-]+)$/; var _fetch; try { _fetch = fetch; } catch { } function useFetchImplementation(fetchImplementation) { _fetch = fetchImplementation; } async function searchDomain(domain, query = "") { try { let res = await (await _fetch(`https://${domain}/.well-known/nostr.json?name=${query}`)).json(); return res.names; } catch (_) { return {}; } } async function queryProfile(fullname) { const match = fullname.match(NIP05_REGEX); if (!match) return null; const [_, name = "_", domain] = match; try { const res = await _fetch(`https://${domain}/.well-known/nostr.json?name=${name}`); const { names, relays } = parseNIP05Result(await res.json()); const pubkey = names[name]; return pubkey ? { pubkey, relays: relays?.[pubkey] } : null; } catch (_e) { return null; } } function parseNIP05Result(json) { const result = { names: {} }; for (const [name, pubkey] of Object.entries(json.names)) { if (typeof name === "string" && typeof pubkey === "string") { result.names[name] = pubkey; } } if (json.relays) { result.relays = {}; for (const [pubkey, relays] of Object.entries(json.relays)) { if (typeof pubkey === "string" && Array.isArray(relays)) { result.relays[pubkey] = relays.filter((relay) => typeof relay === "string"); } } } return result; } // nip06.ts var nip06_exports = {}; __export(nip06_exports, { generateSeedWords: () => generateSeedWords, privateKeyFromSeedWords: () => privateKeyFromSeedWords, validateWords: () => validateWords }); var import_utils10 = require("@noble/hashes/utils"); var import_english = require("@scure/bip39/wordlists/english"); var import_bip39 = require("@scure/bip39"); var import_bip32 = require("@scure/bip32"); function privateKeyFromSeedWords(mnemonic, passphrase) { let root = import_bip32.HDKey.fromMasterSeed((0, import_bip39.mnemonicToSeedSync)(mnemonic, passphrase)); let privateKey = root.derive(`m/44'/1237'/0'/0/0`).privateKey; if (!privateKey) throw new Error("could not derive private key"); return (0, import_utils10.bytesToHex)(privateKey); } function generateSeedWords() { return (0, import_bip39.generateMnemonic)(import_english.wordlist); } function validateWords(words) { return (0, import_bip39.validateMnemonic)(words, import_english.wordlist); } // nip10.ts var nip10_exports = {}; __export(nip10_exports, { parse: () => parse }); function parse(event) { const result = { reply: void 0, root: void 0, mentions: [], profiles: [] }; const eTags = []; for (const tag of event.tags) { if (tag[0] === "e" && tag[1]) { eTags.push(tag); } if (tag[0] === "p" && tag[1]) { result.profiles.push({ pubkey: tag[1], relays: tag[2] ? [tag[2]] : [] }); } } for (let eTagIndex = 0; eTagIndex < eTags.length; eTagIndex++) { const eTag = eTags[eTagIndex]; const [_, eTagEventId, eTagRelayUrl, eTagMarker] = eTag; const eventPointer = { id: eTagEventId, relays: eTagRelayUrl ? [eTagRelayUrl] : [] }; const isFirstETag = eTagIndex === 0; const isLastETag = eTagIndex === eTags.length - 1; if (eTagMarker === "root") { result.root = eventPointer; continue; } if (eTagMarker === "reply") { result.reply = eventPointer; continue; } if (eTagMarker === "mention") { result.mentions.push(eventPointer); continue; } if (isFirstETag) { result.root = eventPointer; continue; } if (isLastETag) { result.reply = eventPointer; continue; } result.mentions.push(eventPointer); } return result; } // nip13.ts var nip13_exports = {}; __export(nip13_exports, { getPow: () => getPow, minePow: () => minePow }); function getPow(hex) { let count = 0; for (let i = 0; i < hex.length; i++) { const nibble = parseInt(hex[i], 16); if (nibble === 0) { count += 4; } else { count += Math.clz32(nibble) - 28; break; } } return count; } function minePow(unsigned, difficulty) { let count = 0; const event = unsigned; const tag = ["nonce", count.toString(), difficulty.toString()]; event.tags.push(tag); while (true) { const now = Math.floor(new Date().getTime() / 1e3); if (now !== event.created_at) { count = 0; event.created_at = now; } tag[1] = (++count).toString(); event.id = getEventHash(event); if (getPow(event.id) >= difficulty) { break; } } return event; } // nip18.ts var nip18_exports = {}; __export(nip18_exports, { finishRepostEvent: () => finishRepostEvent, getRepostedEvent: () => getRepostedEvent, getRepostedEventPointer: () => getRepostedEventPointer }); function finishRepostEvent(t, reposted, relayUrl, privateKey) { return finishEvent( { kind: 6 /* Repost */, tags: [...t.tags ?? [], ["e", reposted.id, relayUrl], ["p", reposted.pubkey]], content: t.content === "" ? "" : JSON.stringify(reposted), created_at: t.created_at }, privateKey ); } function getRepostedEventPointer(event) { if (event.kind !== 6 /* Repost */) { return void 0; } let lastETag; let lastPTag; for (let i = event.tags.length - 1; i >= 0 && (lastETag === void 0 || lastPTag === void 0); i--) { const tag = event.tags[i]; if (tag.length >= 2) { if (tag[0] === "e" && lastETag === void 0) { lastETag = tag; } else if (tag[0] === "p" && lastPTag === void 0) { lastPTag = tag; } } } if (lastETag === void 0) { return void 0; } return { id: lastETag[1], relays: [lastETag[2], lastPTag?.[2]].filter((x) => typeof x === "string"), author: lastPTag?.[1] }; } function getRepostedEvent(event, { skipVerification } = {}) { const pointer = getRepostedEventPointer(event); if (pointer === void 0 || event.content === "") { return void 0; } let repostedEvent; try { repostedEvent = JSON.parse(event.content); } catch (error) { return void 0; } if (repostedEvent.id !== pointer.id) { return void 0; } if (!skipVerification && !verifySignature(repostedEvent)) { return void 0; } return repostedEvent; } // nip21.ts var nip21_exports = {}; __export(nip21_exports, { NOSTR_URI_REGEX: () => NOSTR_URI_REGEX, parse: () => parse2, test: () => test }); var NOSTR_URI_REGEX = new RegExp(`nostr:(${BECH32_REGEX.source})`); function test(value) { return typeof value === "string" && new RegExp(`^${NOSTR_URI_REGEX.source}$`).test(value); } function parse2(uri) { const match = uri.match(new RegExp(`^${NOSTR_URI_REGEX.source}$`)); if (!match) throw new Error(`Invalid Nostr URI: ${uri}`); return { uri: match[0], value: match[1], decoded: decode(match[1]) }; } // nip25.ts var nip25_exports = {}; __export(nip25_exports, { finishReactionEvent: () => finishReactionEvent, getReactedEventPointer: () => getReactedEventPointer }); function finishReactionEvent(t, reacted, privateKey) { const inheritedTags = reacted.tags.filter((tag) => tag.length >= 2 && (tag[0] === "e" || tag[0] === "p")); return finishEvent( { ...t, kind: 7 /* Reaction */, tags: [...t.tags ?? [], ...inheritedTags, ["e", reacted.id], ["p", reacted.pubkey]], content: t.content ?? "+" }, privateKey ); } function getReactedEventPointer(event) { if (event.kind !== 7 /* Reaction */) { return void 0; } let lastETag; let lastPTag; for (let i = event.tags.length - 1; i >= 0 && (lastETag === void 0 || lastPTag === void 0); i--) { const tag = event.tags[i]; if (tag.length >= 2) { if (tag[0] === "e" && lastETag === void 0) { lastETag = tag; } else if (tag[0] === "p" && lastPTag === void 0) { lastPTag = tag; } } } if (lastETag === void 0 || lastPTag === void 0) { return void 0; } return { id: lastETag[1], relays: [lastETag[2], lastPTag[2]].filter((x) => x !== void 0), author: lastPTag[1] }; } // nip26.ts var nip26_exports = {}; __export(nip26_exports, { createDelegation: () => createDelegation, getDelegator: () => getDelegator }); var import_secp256k14 = require("@noble/curves/secp256k1"); var import_utils11 = require("@noble/hashes/utils"); var import_sha2562 = require("@noble/hashes/sha256"); function createDelegation(privateKey, parameters) { let conditions = []; if ((parameters.kind || -1) >= 0) conditions.push(`kind=${parameters.kind}`); if (parameters.until) conditions.push(`created_at<${parameters.until}`); if (parameters.since) conditions.push(`created_at>${parameters.since}`); let cond = conditions.join("&"); if (cond === "") throw new Error("refusing to create a delegation without any conditions"); let sighash = (0, import_sha2562.sha256)(utf8Encoder.encode(`nostr:delegation:${parameters.pubkey}:${cond}`)); let sig = (0, import_utils11.bytesToHex)(import_secp256k14.schnorr.sign(sighash, privateKey)); return { from: getPublicKey(privateKey), to: parameters.pubkey, cond, sig }; } function getDelegator(event) { let tag = event.tags.find((tag2) => tag2[0] === "delegation" && tag2.length >= 4); if (!tag) return null; let pubkey = tag[1]; let cond = tag[2]; let sig = tag[3]; let conditions = cond.split("&"); for (let i = 0; i < conditions.length; i++) { let [key, operator, value] = conditions[i].split(/\b/); if (key === "kind" && operator === "=" && event.kind === parseInt(value)) continue; else if (key === "created_at" && operator === "<" && event.created_at < parseInt(value)) continue; else if (key === "created_at" && operator === ">" && event.created_at > parseInt(value)) continue; else return null; } let sighash = (0, import_sha2562.sha256)(utf8Encoder.encode(`nostr:delegation:${event.pubkey}:${cond}`)); if (!import_secp256k14.schnorr.verify(sig, sighash, pubkey)) return null; return pubkey; } // nip27.ts var nip27_exports = {}; __export(nip27_exports, { matchAll: () => matchAll, regex: () => regex, replaceAll: () => replaceAll }); var regex = () => new RegExp(`\\b${NOSTR_URI_REGEX.source}\\b`, "g"); function* matchAll(content) { const matches = content.matchAll(regex()); for (const match of matches) { try { const [uri, value] = match; yield { uri, value, decoded: decode(value), start: match.index, end: match.index + uri.length }; } catch (_e) { } } } function replaceAll(content, replacer) { return content.replaceAll(regex(), (uri, value) => { return replacer({ uri, value, decoded: decode(value) }); }); } // nip28.ts var nip28_exports = {}; __export(nip28_exports, { channelCreateEvent: () => channelCreateEvent, channelHideMessageEvent: () => channelHideMessageEvent, channelMessageEvent: () => channelMessageEvent, channelMetadataEvent: () => channelMetadataEvent, channelMuteUserEvent: () => channelMuteUserEvent }); var channelCreateEvent = (t, privateKey) => { let content; if (typeof t.content === "object") { content = JSON.stringify(t.content); } else if (typeof t.content === "string") { content = t.content; } else { return void 0; } return finishEvent( { kind: 40 /* ChannelCreation */, tags: [...t.tags ?? []], content, created_at: t.created_at }, privateKey ); }; var channelMetadataEvent = (t, privateKey) => { let content; if (typeof t.content === "object") { content = JSON.stringify(t.content); } else if (typeof t.content === "string") { content = t.content; } else { return void 0; } return finishEvent( { kind: 41 /* ChannelMetadata */, tags: [["e", t.channel_create_event_id], ...t.tags ?? []], content, created_at: t.created_at }, privateKey ); }; var channelMessageEvent = (t, privateKey) => { const tags = [["e", t.channel_create_event_id, t.relay_url, "root"]]; if (t.reply_to_channel_message_event_id) { tags.push(["e", t.reply_to_channel_message_event_id, t.relay_url, "reply"]); } return finishEvent( { kind: 42 /* ChannelMessage */, tags: [...tags, ...t.tags ?? []], content: t.content, created_at: t.created_at }, privateKey ); }; var channelHideMessageEvent = (t, privateKey) => { let content; if (typeof t.content === "object") { content = JSON.stringify(t.content); } else if (typeof t.content === "string") { content = t.content; } else { return void 0; } return finishEvent( { kind: 43 /* ChannelHideMessage */, tags: [["e", t.channel_message_event_id], ...t.tags ?? []], content, created_at: t.created_at }, privateKey ); }; var channelMuteUserEvent = (t, privateKey) => { let content; if (typeof t.content === "object") { content = JSON.stringify(t.content); } else if (typeof t.content === "string") { content = t.content; } else { return void 0; } return finishEvent( { kind: 44 /* ChannelMuteUser */, tags: [["p", t.pubkey_to_mute], ...t.tags ?? []], content, created_at: t.created_at }, privateKey ); }; // nip39.ts var nip39_exports = {}; __export(nip39_exports, { useFetchImplementation: () => useFetchImplementation2, validateGithub: () => validateGithub }); var _fetch2; try { _fetch2 = fetch; } catch { } function useFetchImplementation2(fetchImplementation) { _fetch2 = fetchImplementation; } async function validateGithub(pubkey, username, proof) { try { let res = await (await _fetch2(`https://gist.github.com/${username}/${proof}/raw`)).text(); return res === `Verifying that I control the following Nostr public key: ${pubkey}`; } catch (_) { return false; } } // nip42.ts var nip42_exports = {}; __export(nip42_exports, { authenticate: () => authenticate }); var authenticate = async ({ challenge, relay, sign }) => { const e = { kind: 22242 /* ClientAuth */, created_at: Math.floor(Date.now() / 1e3), tags: [ ["relay", relay.url], ["challenge", challenge] ], content: "" }; return relay.auth(await sign(e)); }; // nip44.ts var nip44_exports = {}; __export(nip44_exports, { decrypt: () => decrypt2, encrypt: () => encrypt2, utils: () => utils }); var import_chacha = require("@noble/ciphers/chacha"); var import_utils13 = require("@noble/ciphers/utils"); var import_secp256k15 = 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_utils14 = require("@noble/hashes/utils"); var import_base3 = require("@scure/base"); var utils = { v2: { maxPlaintextSize: 65536 - 128, minCiphertextSize: 100, maxCiphertextSize: 102400, getConversationKey(privkeyA, pubkeyB) { const key = import_secp256k15.secp256k1.getSharedSecret(privkeyA, "02" + pubkeyB); return key.subarray(1, 33); }, getMessageKeys(conversationKey, salt) { const keys = (0, import_hkdf.hkdf)(import_sha2563.sha256, conversationKey, salt, "nip44-v2", 76); return { encryption: keys.subarray(0, 32), nonce: keys.subarray(32, 44), auth: keys.subarray(44, 76) }; }, calcPadding(len) { if (!Number.isSafeInteger(len) || len < 0) throw new Error("expected positive integer"); if (len <= 32) return 32; const nextpower = 1 << Math.floor(Math.log2(len - 1)) + 1; const chunk = nextpower <= 256 ? 32 : nextpower / 8; return chunk * (Math.floor((len - 1) / chunk) + 1); }, pad(unpadded) { const unpaddedB = utf8Encoder.encode(unpadded); const len = unpaddedB.length; if (len < 1 || len >= utils.v2.maxPlaintextSize) throw new Error("invalid plaintext length: must be between 1b and 64KB"); const paddedLen = utils.v2.calcPadding(len); const zeros = new Uint8Array(paddedLen - len); const lenBuf = new Uint8Array(2); new DataView(lenBuf.buffer).setUint16(0, len); return (0, import_utils14.concatBytes)(lenBuf, unpaddedB, zeros); }, unpad(padded) { const unpaddedLen = new DataView(padded.buffer).getUint16(0); const unpadded = padded.subarray(2, 2 + unpaddedLen); if (unpaddedLen === 0 || unpadded.length !== unpaddedLen || padded.length !== 2 + utils.v2.calcPadding(unpaddedLen)) throw new Error("invalid padding"); return utf8Decoder.decode(unpadded); } } }; function encrypt2(key, plaintext, options = {}) { const version = options.version ?? 2; if (version !== 2) throw new Error("unknown encryption version " + version); const salt = options.salt ?? (0, import_utils14.randomBytes)(32); (0, import_utils13.ensureBytes)(salt, 32); const keys = utils.v2.getMessageKeys(key, salt); const padded = utils.v2.pad(plaintext); const ciphertext = (0, import_chacha.chacha20)(keys.encryption, keys.nonce, padded); const mac = (0, import_hmac.hmac)(import_sha2563.sha256, keys.auth, ciphertext); return import_base3.base64.encode((0, import_utils14.concatBytes)(new Uint8Array([version]), salt, ciphertext, mac)); } function decrypt2(key, ciphertext) { const u = utils.v2; (0, import_utils13.ensureBytes)(key, 32); const clen = ciphertext.length; if (clen < u.minCiphertextSize || clen >= u.maxCiphertextSize) throw new Error("invalid ciphertext length: " + clen); if (ciphertext[0] === "#") throw new Error("unknown encryption version"); let data; try { data = import_base3.base64.decode(ciphertext); } catch (error) { throw new Error("invalid base64: " + error.message); } const vers = data.subarray(0, 1)[0]; if (vers !== 2) throw new Error("unknown encryption version " + vers); const salt = data.subarray(1, 33); const ciphertext_ = data.subarray(33, -32); const mac = data.subarray(-32); const keys = u.getMessageKeys(key, salt); const calculatedMac = (0, import_hmac.hmac)(import_sha2563.sha256, keys.auth, ciphertext_); if (!(0, import_utils13.equalBytes)(calculatedMac, mac)) throw new Error("invalid MAC"); const padded = (0, import_chacha.chacha20)(keys.encryption, keys.nonce, ciphertext_); return u.unpad(padded); } // nip47.ts var nip47_exports = {}; __export(nip47_exports, { makeNwcRequestEvent: () => makeNwcRequestEvent, parseConnectionString: () => parseConnectionString }); function parseConnectionString(connectionString) { const { pathname, searchParams } = new URL(connectionString); const pubkey = pathname; const relay = searchParams.get("relay"); const secret = searchParams.get("secret"); if (!pubkey || !relay || !secret) { throw new Error("invalid connection string"); } return { pubkey, relay, secret }; } async function makeNwcRequestEvent({ pubkey, secret, invoice }) { const content = { method: "pay_invoice", params: { invoice } }; const encryptedContent = await encrypt(secret, pubkey, JSON.stringify(content)); const eventTemplate = { kind: 23194 /* NwcRequest */, created_at: Math.round(Date.now() / 1e3), content: encryptedContent, tags: [["p", pubkey]] }; return finishEvent(eventTemplate, secret); } // nip57.ts var nip57_exports = {}; __export(nip57_exports, { getZapEndpoint: () => getZapEndpoint, makeZapReceipt: () => makeZapReceipt, makeZapRequest: () => makeZapRequest, useFetchImplementation: () => useFetchImplementation3, validateZapRequest: () => validateZapRequest }); var import_base4 = require("@scure/base"); var _fetch3; try { _fetch3 = fetch; } catch { } function useFetchImplementation3(fetchImplementation) { _fetch3 = fetchImplementation; } async function getZapEndpoint(metadata) { try { let lnurl = ""; let { lud06, lud16 } = JSON.parse(metadata.content); if (lud06) { let { words } = import_base4.bech32.decode(lud06, 1e3); let data = import_base4.bech32.fromWords(words); lnurl = utf8Decoder.decode(data); } else if (lud16) { let [name, domain] = lud16.split("@"); lnurl = `https://${domain}/.well-known/lnurlp/${name}`; } else { return null; } let res = await _fetch3(lnurl); let body = await res.json(); if (body.allowsNostr && body.nostrPubkey) { return body.callback; } } catch (err) { } return null; } function makeZapRequest({ profile, event, amount, relays, comment = "" }) { if (!amount) throw new Error("amount not given"); if (!profile) throw new Error("profile not given"); let zr = { kind: 9734, created_at: Math.round(Date.now() / 1e3), content: comment, tags: [ ["p", profile], ["amount", amount.toString()], ["relays", ...relays] ] }; if (event) { zr.tags.push(["e", event]); } return zr; } function validateZapRequest(zapRequestString) { let zapRequest; try { zapRequest = JSON.parse(zapRequestString); } catch (err) { return "Invalid zap request JSON."; } if (!validateEvent(zapRequest)) return "Zap request is not a valid Nostr event."; if (!verifySignature(zapRequest)) return "Invalid signature on zap request."; let p = zapRequest.tags.find(([t, v]) => t === "p" && v); if (!p) return "Zap request doesn't have a 'p' tag."; if (!p[1].match(/^[a-f0-9]{64}$/)) return "Zap request 'p' tag is not valid hex."; let e = zapRequest.tags.find(([t, v]) => t === "e" && v); if (e && !e[1].match(/^[a-f0-9]{64}$/)) return "Zap request 'e' tag is not valid hex."; let relays = zapRequest.tags.find(([t, v]) => t === "relays" && v); if (!relays) return "Zap request doesn't have a 'relays' tag."; return null; } function makeZapReceipt({ zapRequest, preimage, bolt11, paidAt }) { let zr = JSON.parse(zapRequest); let tagsFromZapRequest = zr.tags.filter(([t]) => t === "e" || t === "p" || t === "a"); let zap = { kind: 9735, created_at: Math.round(paidAt.getTime() / 1e3), content: "", tags: [...tagsFromZapRequest, ["bolt11", bolt11], ["description", zapRequest]] }; if (preimage) { zap.tags.push(["preimage", preimage]); } return zap; } // nip98.ts var nip98_exports = {}; __export(nip98_exports, { getToken: () => getToken, unpackEventFromToken: () => unpackEventFromToken, validateEvent: () => validateEvent2, validateToken: () => validateToken }); var import_base5 = require("@scure/base"); var _authorizationScheme = "Nostr "; async function getToken(loginUrl, httpMethod, sign, includeAuthorizationScheme = false) { if (!loginUrl || !httpMethod) throw new Error("Missing loginUrl or httpMethod"); const event = getBlankEvent(27235 /* HttpAuth */); event.tags = [ ["u", loginUrl], ["method", httpMethod] ]; event.created_at = Math.round(new Date().getTime() / 1e3); const signedEvent = await sign(event); const authorizationScheme = includeAuthorizationScheme ? _authorizationScheme : ""; return authorizationScheme + import_base5.base64.encode(utf8Encoder.encode(JSON.stringify(signedEvent))); } async function validateToken(token, url, method) { const event = await unpackEventFromToken(token).catch((error) => { throw error; }); const valid = await validateEvent2(event, url, method).catch((error) => { throw error; }); return valid; } async function unpackEventFromToken(token) { if (!token) { throw new Error("Missing token"); } token = token.replace(_authorizationScheme, ""); const eventB64 = utf8Decoder.decode(import_base5.base64.decode(token)); if (!eventB64 || eventB64.length === 0 || !eventB64.startsWith("{")) { throw new Error("Invalid token"); } const event = JSON.parse(eventB64); return event; } async function validateEvent2(event, url, method) { if (!event) { throw new Error("Invalid nostr event"); } if (!verifySignature(event)) { throw new Error("Invalid nostr event, signature invalid"); } if (event.kind !== 27235 /* HttpAuth */) { throw new Error("Invalid nostr event, kind invalid"); } if (!event.created_at) { throw new Error("Invalid nostr event, created_at invalid"); } if (Math.round(new Date().getTime() / 1e3) - event.created_at > 60) { throw new Error("Invalid nostr event, expired"); } const urlTag = event.tags.find((t) => t[0] === "u"); if (urlTag?.length !== 1 && urlTag?.[1] !== url) { throw new Error("Invalid nostr event, url tag invalid"); } const methodTag = event.tags.find((t) => t[0] === "method"); if (methodTag?.length !== 1 && methodTag?.[1].toLowerCase() !== method.toLowerCase()) { throw new Error("Invalid nostr event, method tag invalid"); } return true; }