var __defProp = Object.defineProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; // pure.ts import { schnorr } from "@noble/curves/secp256k1"; import { bytesToHex as bytesToHex2 } from "@noble/hashes/utils"; // core.ts var verifiedSymbol = Symbol("verified"); 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 i2 = 0; i2 < event.tags.length; i2++) { let tag = event.tags[i2]; if (!Array.isArray(tag)) return false; for (let j = 0; j < tag.length; j++) { if (typeof tag[j] !== "string") return false; } } return true; } function sortEvents(events) { return events.sort((a, b) => { if (a.created_at !== b.created_at) { return b.created_at - a.created_at; } return a.id.localeCompare(b.id); }); } // pure.ts import { sha256 } from "@noble/hashes/sha256"; // utils.ts var utils_exports = {}; __export(utils_exports, { Queue: () => Queue, QueueNode: () => QueueNode, binarySearch: () => binarySearch, bytesToHex: () => bytesToHex, hexToBytes: () => hexToBytes, insertEventIntoAscendingList: () => insertEventIntoAscendingList, insertEventIntoDescendingList: () => insertEventIntoDescendingList, normalizeURL: () => normalizeURL, utf8Decoder: () => utf8Decoder, utf8Encoder: () => utf8Encoder }); import { bytesToHex, hexToBytes } from "@noble/hashes/utils"; var utf8Decoder = new TextDecoder("utf-8"); var utf8Encoder = new TextEncoder(); function normalizeURL(url) { try { if (url.indexOf("://") === -1) url = "wss://" + 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(); } catch (e) { throw new Error(`Invalid URL: ${url}`); } } function insertEventIntoDescendingList(sortedArray, event) { const [idx, found] = binarySearch(sortedArray, (b) => { if (event.id === b.id) return 0; if (event.created_at === b.created_at) return -1; return b.created_at - event.created_at; }); if (!found) { sortedArray.splice(idx, 0, event); } return sortedArray; } function insertEventIntoAscendingList(sortedArray, event) { const [idx, found] = binarySearch(sortedArray, (b) => { if (event.id === b.id) return 0; if (event.created_at === b.created_at) return -1; return event.created_at - b.created_at; }); if (!found) { sortedArray.splice(idx, 0, event); } return sortedArray; } function binarySearch(arr, compare) { let start = 0; let end = arr.length - 1; while (start <= end) { const mid = Math.floor((start + end) / 2); const cmp = compare(arr[mid]); if (cmp === 0) { return [mid, true]; } if (cmp < 0) { end = mid - 1; } else { start = mid + 1; } } return [start, false]; } var QueueNode = class { value; next = null; prev = null; constructor(message) { this.value = message; } }; var Queue = class { first; last; constructor() { this.first = null; this.last = null; } enqueue(value) { const newNode = new QueueNode(value); if (!this.last) { this.first = newNode; this.last = newNode; } else if (this.last === this.first) { this.last = newNode; this.last.prev = this.first; this.first.next = newNode; } else { newNode.prev = this.last; this.last.next = newNode; this.last = newNode; } return true; } dequeue() { if (!this.first) return null; if (this.first === this.last) { const target2 = this.first; this.first = null; this.last = null; return target2.value; } const target = this.first; this.first = target.next; if (this.first) { this.first.prev = null; } return target.value; } }; // pure.ts var JS = class { generateSecretKey() { return schnorr.utils.randomPrivateKey(); } 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[verifiedSymbol] = true; return event; } verifyEvent(event) { if (typeof event[verifiedSymbol] === "boolean") return event[verifiedSymbol]; const hash = getEventHash(event); if (hash !== event.id) { event[verifiedSymbol] = false; return false; } try { const valid = schnorr.verify(event.sig, hash, event.pubkey); event[verifiedSymbol] = valid; return valid; } catch (err) { event[verifiedSymbol] = false; return false; } } }; 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 = sha256(utf8Encoder.encode(serializeEvent(event))); return bytesToHex2(eventHash); } var i = new JS(); var generateSecretKey = i.generateSecretKey; var getPublicKey = i.getPublicKey; var finalizeEvent = i.finalizeEvent; var verifyEvent = i.verifyEvent; // kinds.ts var kinds_exports = {}; __export(kinds_exports, { Application: () => Application, BadgeAward: () => BadgeAward, BadgeDefinition: () => BadgeDefinition, BlockedRelaysList: () => BlockedRelaysList, BookmarkList: () => BookmarkList, Bookmarksets: () => Bookmarksets, Calendar: () => Calendar, CalendarEventRSVP: () => CalendarEventRSVP, ChannelCreation: () => ChannelCreation, ChannelHideMessage: () => ChannelHideMessage, ChannelMessage: () => ChannelMessage, ChannelMetadata: () => ChannelMetadata, ChannelMuteUser: () => ChannelMuteUser, ClassifiedListing: () => ClassifiedListing, ClientAuth: () => ClientAuth, CommunitiesList: () => CommunitiesList, CommunityDefinition: () => CommunityDefinition, CommunityPostApproval: () => CommunityPostApproval, Contacts: () => Contacts, CreateOrUpdateProduct: () => CreateOrUpdateProduct, CreateOrUpdateStall: () => CreateOrUpdateStall, Curationsets: () => Curationsets, Date: () => Date2, DirectMessageRelaysList: () => DirectMessageRelaysList, DraftClassifiedListing: () => DraftClassifiedListing, DraftLong: () => DraftLong, Emojisets: () => Emojisets, EncryptedDirectMessage: () => EncryptedDirectMessage, EventDeletion: () => EventDeletion, FileMetadata: () => FileMetadata, FileServerPreference: () => FileServerPreference, Followsets: () => Followsets, GenericRepost: () => GenericRepost, Genericlists: () => Genericlists, GiftWrap: () => GiftWrap, HTTPAuth: () => HTTPAuth, Handlerinformation: () => Handlerinformation, Handlerrecommendation: () => Handlerrecommendation, Highlights: () => Highlights, InterestsList: () => InterestsList, Interestsets: () => Interestsets, JobFeedback: () => JobFeedback, JobRequest: () => JobRequest, JobResult: () => JobResult, Label: () => Label, LightningPubRPC: () => LightningPubRPC, LiveChatMessage: () => LiveChatMessage, LiveEvent: () => LiveEvent, LongFormArticle: () => LongFormArticle, Metadata: () => Metadata, Mutelist: () => Mutelist, NWCWalletInfo: () => NWCWalletInfo, NWCWalletRequest: () => NWCWalletRequest, NWCWalletResponse: () => NWCWalletResponse, NostrConnect: () => NostrConnect, OpenTimestamps: () => OpenTimestamps, Pinlist: () => Pinlist, PrivateDirectMessage: () => PrivateDirectMessage, ProblemTracker: () => ProblemTracker, ProfileBadges: () => ProfileBadges, PublicChatsList: () => PublicChatsList, Reaction: () => Reaction, RecommendRelay: () => RecommendRelay, RelayList: () => RelayList, Relaysets: () => Relaysets, Report: () => Report, Reporting: () => Reporting, Repost: () => Repost, Seal: () => Seal, SearchRelaysList: () => SearchRelaysList, ShortTextNote: () => ShortTextNote, Time: () => Time, UserEmojiList: () => UserEmojiList, UserStatuses: () => UserStatuses, Zap: () => Zap, ZapGoal: () => ZapGoal, ZapRequest: () => ZapRequest, classifyKind: () => classifyKind, isAddressableKind: () => isAddressableKind, isEphemeralKind: () => isEphemeralKind, isKind: () => isKind, isRegularKind: () => isRegularKind, isReplaceableKind: () => isReplaceableKind }); function isRegularKind(kind) { return 1e3 <= kind && kind < 1e4 || [1, 2, 4, 5, 6, 7, 8, 16, 40, 41, 42, 43, 44].includes(kind); } function isReplaceableKind(kind) { return [0, 3].includes(kind) || 1e4 <= kind && kind < 2e4; } function isEphemeralKind(kind) { return 2e4 <= kind && kind < 3e4; } function isAddressableKind(kind) { return 3e4 <= kind && kind < 4e4; } function classifyKind(kind) { if (isRegularKind(kind)) return "regular"; if (isReplaceableKind(kind)) return "replaceable"; if (isEphemeralKind(kind)) return "ephemeral"; if (isAddressableKind(kind)) return "parameterized"; return "unknown"; } function isKind(event, kind) { const kindAsArray = kind instanceof Array ? kind : [kind]; return validateEvent(event) && kindAsArray.includes(event.kind) || false; } var Metadata = 0; var ShortTextNote = 1; var RecommendRelay = 2; var Contacts = 3; var EncryptedDirectMessage = 4; var EventDeletion = 5; var Repost = 6; var Reaction = 7; var BadgeAward = 8; var Seal = 13; var PrivateDirectMessage = 14; var GenericRepost = 16; var ChannelCreation = 40; var ChannelMetadata = 41; var ChannelMessage = 42; var ChannelHideMessage = 43; var ChannelMuteUser = 44; var OpenTimestamps = 1040; var GiftWrap = 1059; var FileMetadata = 1063; var LiveChatMessage = 1311; var ProblemTracker = 1971; var Report = 1984; var Reporting = 1984; var Label = 1985; var CommunityPostApproval = 4550; var JobRequest = 5999; var JobResult = 6999; var JobFeedback = 7e3; var ZapGoal = 9041; var ZapRequest = 9734; var Zap = 9735; var Highlights = 9802; var Mutelist = 1e4; var Pinlist = 10001; var RelayList = 10002; var BookmarkList = 10003; var CommunitiesList = 10004; var PublicChatsList = 10005; var BlockedRelaysList = 10006; var SearchRelaysList = 10007; var InterestsList = 10015; var UserEmojiList = 10030; var DirectMessageRelaysList = 10050; var FileServerPreference = 10096; var NWCWalletInfo = 13194; var LightningPubRPC = 21e3; var ClientAuth = 22242; var NWCWalletRequest = 23194; var NWCWalletResponse = 23195; var NostrConnect = 24133; var HTTPAuth = 27235; var Followsets = 3e4; var Genericlists = 30001; var Relaysets = 30002; var Bookmarksets = 30003; var Curationsets = 30004; var ProfileBadges = 30008; var BadgeDefinition = 30009; var Interestsets = 30015; var CreateOrUpdateStall = 30017; var CreateOrUpdateProduct = 30018; var LongFormArticle = 30023; var DraftLong = 30024; var Emojisets = 30030; var Application = 30078; var LiveEvent = 30311; var UserStatuses = 30315; var ClassifiedListing = 30402; var DraftClassifiedListing = 30403; var Date2 = 31922; var Time = 31923; var Calendar = 31924; var CalendarEventRSVP = 31925; var Handlerrecommendation = 31989; var Handlerinformation = 31990; var CommunityDefinition = 34550; // filter.ts function matchFilter(filter, event) { if (filter.ids && filter.ids.indexOf(event.id) === -1) { return false; } if (filter.kinds && filter.kinds.indexOf(event.kind) === -1) { return false; } if (filter.authors && filter.authors.indexOf(event.pubkey) === -1) { 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 i2 = 0; i2 < filters.length; i2++) { if (matchFilter(filters[i2], event)) { return true; } } return false; } function mergeFilters(...filters) { let result = {}; for (let i2 = 0; i2 < filters.length; i2++) { let filter = filters[i2]; 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; } function getFilterLimit(filter) { if (filter.ids && !filter.ids.length) return 0; if (filter.kinds && !filter.kinds.length) return 0; if (filter.authors && !filter.authors.length) return 0; for (const [key, value] of Object.entries(filter)) { if (key[0] === "#" && Array.isArray(value) && !value.length) return 0; } return Math.min( Math.max(0, filter.limit ?? Infinity), filter.ids?.length ?? Infinity, filter.authors?.length && filter.kinds?.every((kind) => isReplaceableKind(kind)) ? filter.authors.length * filter.kinds.length : Infinity, filter.authors?.length && filter.kinds?.every((kind) => isAddressableKind(kind)) && filter["#d"]?.length ? filter.authors.length * filter.kinds.length * filter["#d"].length : Infinity ); } // 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"); } // nip42.ts var nip42_exports = {}; __export(nip42_exports, { makeAuthEvent: () => makeAuthEvent }); function makeAuthEvent(relayURL, challenge) { return { kind: ClientAuth, created_at: Math.floor(Date.now() / 1e3), tags: [ ["relay", relayURL], ["challenge", challenge] ], content: "" }; } // helpers.ts async function yieldThread() { return new Promise((resolve) => { const ch = new MessageChannel(); const handler = () => { ch.port1.removeEventListener("message", handler); resolve(); }; ch.port1.addEventListener("message", handler); ch.port2.postMessage(0); ch.port1.start(); }); } var alwaysTrue = (t) => { t[verifiedSymbol] = true; return true; }; // abstract-relay.ts var SendingOnClosedConnection = class extends Error { constructor(message, relay) { super(`Tried to send message '${message} on a closed connection to ${relay}.`); this.name = "SendingOnClosedConnection"; } }; var AbstractRelay = class { url; _connected = false; onclose = null; onnotice = (msg) => console.debug(`NOTICE from ${this.url}: ${msg}`); baseEoseTimeout = 4400; connectionTimeout = 4400; publishTimeout = 4400; pingFrequency = 2e4; pingTimeout = 2e4; openSubs = /* @__PURE__ */ new Map(); enablePing; connectionTimeoutHandle; connectionPromise; openCountRequests = /* @__PURE__ */ new Map(); openEventPublishes = /* @__PURE__ */ new Map(); ws; incomingMessageQueue = new Queue(); queueRunning = false; challenge; authPromise; serial = 0; verifyEvent; _WebSocket; constructor(url, opts) { this.url = normalizeURL(url); this.verifyEvent = opts.verifyEvent; this._WebSocket = opts.websocketImplementation || WebSocket; this.enablePing = opts.enablePing; } static async connect(url, opts) { const relay = new AbstractRelay(url, opts); await relay.connect(); return relay; } closeAllSubscriptions(reason) { for (let [_, sub] of this.openSubs) { sub.close(reason); } this.openSubs.clear(); for (let [_, ep] of this.openEventPublishes) { ep.reject(new Error(reason)); } this.openEventPublishes.clear(); for (let [_, cr] of this.openCountRequests) { cr.reject(new Error(reason)); } this.openCountRequests.clear(); } get connected() { return this._connected; } async connect() { if (this.connectionPromise) return this.connectionPromise; this.challenge = void 0; this.authPromise = void 0; this.connectionPromise = new Promise((resolve, reject) => { this.connectionTimeoutHandle = setTimeout(() => { reject("connection timed out"); this.connectionPromise = void 0; this.onclose?.(); this.closeAllSubscriptions("relay connection timed out"); }, this.connectionTimeout); try { this.ws = new this._WebSocket(this.url); } catch (err) { clearTimeout(this.connectionTimeoutHandle); reject(err); return; } this.ws.onopen = () => { clearTimeout(this.connectionTimeoutHandle); this._connected = true; if (this.enablePing) { this.pingpong(); } resolve(); }; this.ws.onerror = (ev) => { clearTimeout(this.connectionTimeoutHandle); reject(ev.message || "websocket error"); this._connected = false; this.connectionPromise = void 0; this.onclose?.(); this.closeAllSubscriptions("relay connection errored"); }; this.ws.onclose = (ev) => { clearTimeout(this.connectionTimeoutHandle); reject(ev.message || "websocket closed"); this._connected = false; this.connectionPromise = void 0; this.onclose?.(); this.closeAllSubscriptions("relay connection closed"); }; this.ws.onmessage = this._onmessage.bind(this); }); return this.connectionPromise; } async waitForPingPong() { return new Promise((res, err) => { ; this.ws && this.ws.on && this.ws.on("pong", () => res(true)) || err("ws can't listen for pong"); this.ws && this.ws.ping && this.ws.ping(); }); } async waitForDummyReq() { return new Promise((resolve, _) => { const sub = this.subscribe([{ ids: ["a".repeat(64)] }], { oneose: () => { sub.close(); resolve(true); }, eoseTimeout: this.pingTimeout + 1e3 }); }); } async pingpong() { if (this.ws?.readyState === 1) { const result = await Promise.any([ this.ws && this.ws.ping && this.ws.on ? this.waitForPingPong() : this.waitForDummyReq(), new Promise((res) => setTimeout(() => res(false), this.pingTimeout)) ]); if (result) { setTimeout(() => this.pingpong(), this.pingFrequency); } else { this.closeAllSubscriptions("pingpong timed out"); this._connected = false; this.onclose?.(); this.ws?.close(); } } } async runQueue() { this.queueRunning = true; while (true) { if (false === this.handleNext()) { break; } await yieldThread(); } this.queueRunning = false; } handleNext() { const json = this.incomingMessageQueue.dequeue(); if (!json) { return false; } const subid = getSubscriptionId(json); if (subid) { const so = this.openSubs.get(subid); if (!so) { return; } const id = getHex64(json, "id"); const alreadyHave = so.alreadyHaveEvent?.(id); so.receivedEvent?.(this, id); if (alreadyHave) { return; } } try { let data = JSON.parse(json); switch (data[0]) { case "EVENT": { const so = this.openSubs.get(data[1]); const event = data[2]; if (this.verifyEvent(event) && matchFilters(so.filters, event)) { so.onevent(event); } return; } case "COUNT": { const id = data[1]; const payload = data[2]; const cr = this.openCountRequests.get(id); if (cr) { cr.resolve(payload.count); this.openCountRequests.delete(id); } return; } case "EOSE": { const so = this.openSubs.get(data[1]); if (!so) return; so.receivedEose(); return; } case "OK": { const id = data[1]; const ok = data[2]; const reason = data[3]; const ep = this.openEventPublishes.get(id); if (ep) { clearTimeout(ep.timeout); if (ok) ep.resolve(reason); else ep.reject(new Error(reason)); this.openEventPublishes.delete(id); } return; } case "CLOSED": { const id = data[1]; const so = this.openSubs.get(id); if (!so) return; so.closed = true; so.close(data[2]); return; } case "NOTICE": this.onnotice(data[1]); return; case "AUTH": { this.challenge = data[1]; return; } } } catch (err) { return; } } async send(message) { if (!this.connectionPromise) throw new SendingOnClosedConnection(message, this.url); this.connectionPromise.then(() => { this.ws?.send(message); }); } async auth(signAuthEvent) { const challenge = this.challenge; if (!challenge) throw new Error("can't perform auth, no challenge was received"); if (this.authPromise) return this.authPromise; this.authPromise = new Promise(async (resolve, reject) => { try { let evt = await signAuthEvent(makeAuthEvent(this.url, challenge)); let timeout = setTimeout(() => { let ep = this.openEventPublishes.get(evt.id); if (ep) { ep.reject(new Error("auth timed out")); this.openEventPublishes.delete(evt.id); } }, this.publishTimeout); this.openEventPublishes.set(evt.id, { resolve, reject, timeout }); this.send('["AUTH",' + JSON.stringify(evt) + "]"); } catch (err) { console.warn("subscribe auth function failed:", err); } }); return this.authPromise; } async publish(event) { const ret = new Promise((resolve, reject) => { const timeout = setTimeout(() => { const ep = this.openEventPublishes.get(event.id); if (ep) { ep.reject(new Error("publish timed out")); this.openEventPublishes.delete(event.id); } }, this.publishTimeout); this.openEventPublishes.set(event.id, { resolve, reject, timeout }); }); this.send('["EVENT",' + JSON.stringify(event) + "]"); return ret; } async count(filters, params) { this.serial++; const id = params?.id || "count:" + this.serial; const ret = new Promise((resolve, reject) => { this.openCountRequests.set(id, { resolve, reject }); }); this.send('["COUNT","' + id + '",' + JSON.stringify(filters).substring(1)); return ret; } subscribe(filters, params) { const subscription = this.prepareSubscription(filters, params); subscription.fire(); return subscription; } prepareSubscription(filters, params) { this.serial++; const id = params.id || (params.label ? params.label + ":" : "sub:") + this.serial; const subscription = new Subscription(this, id, filters, params); this.openSubs.set(id, subscription); return subscription; } close() { this.closeAllSubscriptions("relay connection closed by us"); this._connected = false; this.onclose?.(); this.ws?.close(); } _onmessage(ev) { this.incomingMessageQueue.enqueue(ev.data); if (!this.queueRunning) { this.runQueue(); } } }; var Subscription = class { relay; id; closed = false; eosed = false; filters; alreadyHaveEvent; receivedEvent; onevent; oneose; onclose; eoseTimeout; eoseTimeoutHandle; constructor(relay, id, filters, params) { this.relay = relay; this.filters = filters; this.id = id; this.alreadyHaveEvent = params.alreadyHaveEvent; this.receivedEvent = params.receivedEvent; this.eoseTimeout = params.eoseTimeout || relay.baseEoseTimeout; this.oneose = params.oneose; this.onclose = params.onclose; this.onevent = params.onevent || ((event) => { console.warn( `onevent() callback not defined for subscription '${this.id}' in relay ${this.relay.url}. event received:`, event ); }); } fire() { this.relay.send('["REQ","' + this.id + '",' + JSON.stringify(this.filters).substring(1)); this.eoseTimeoutHandle = setTimeout(this.receivedEose.bind(this), this.eoseTimeout); } receivedEose() { if (this.eosed) return; clearTimeout(this.eoseTimeoutHandle); this.eosed = true; this.oneose?.(); } close(reason = "closed by caller") { if (!this.closed && this.relay.connected) { try { this.relay.send('["CLOSE",' + JSON.stringify(this.id) + "]"); } catch (err) { if (err instanceof SendingOnClosedConnection) { } else { throw err; } } this.closed = true; } this.relay.openSubs.delete(this.id); this.onclose?.(reason); } }; // relay.ts var _WebSocket; try { _WebSocket = WebSocket; } catch { } var Relay = class extends AbstractRelay { constructor(url) { super(url, { verifyEvent, websocketImplementation: _WebSocket }); } static async connect(url) { const relay = new Relay(url); await relay.connect(); return relay; } }; // abstract-pool.ts var AbstractSimplePool = class { relays = /* @__PURE__ */ new Map(); seenOn = /* @__PURE__ */ new Map(); trackRelays = false; verifyEvent; enablePing; trustedRelayURLs = /* @__PURE__ */ new Set(); _WebSocket; constructor(opts) { this.verifyEvent = opts.verifyEvent; this._WebSocket = opts.websocketImplementation; this.enablePing = opts.enablePing; } async ensureRelay(url, params) { url = normalizeURL(url); let relay = this.relays.get(url); if (!relay) { relay = new AbstractRelay(url, { verifyEvent: this.trustedRelayURLs.has(url) ? alwaysTrue : this.verifyEvent, websocketImplementation: this._WebSocket, enablePing: this.enablePing }); relay.onclose = () => { this.relays.delete(url); }; if (params?.connectionTimeout) relay.connectionTimeout = params.connectionTimeout; this.relays.set(url, relay); } await relay.connect(); return relay; } close(relays) { relays.map(normalizeURL).forEach((url) => { this.relays.get(url)?.close(); this.relays.delete(url); }); } subscribe(relays, filter, params) { params.onauth = params.onauth || params.doauth; const request = []; for (let i2 = 0; i2 < relays.length; i2++) { const url = normalizeURL(relays[i2]); if (!request.find((r) => r.url === url)) { request.push({ url, filter }); } } return this.subscribeMap(request, params); } subscribeMany(relays, filter, params) { params.onauth = params.onauth || params.doauth; const request = []; const uniqUrls = []; for (let i2 = 0; i2 < relays.length; i2++) { const url = normalizeURL(relays[i2]); if (uniqUrls.indexOf(url) === -1) { uniqUrls.push(url); request.push({ url, filter }); } } return this.subscribeMap(request, params); } subscribeMap(requests, params) { params.onauth = params.onauth || params.doauth; const grouped = /* @__PURE__ */ new Map(); for (const req of requests) { const { url, filter } = req; if (!grouped.has(url)) grouped.set(url, []); grouped.get(url).push(filter); } const groupedRequests = Array.from(grouped.entries()).map(([url, filters]) => ({ url, filters })); if (this.trackRelays) { params.receivedEvent = (relay, id) => { let set = this.seenOn.get(id); if (!set) { set = /* @__PURE__ */ new Set(); this.seenOn.set(id, set); } set.add(relay); }; } const _knownIds = /* @__PURE__ */ new Set(); const subs = []; const eosesReceived = []; let handleEose = (i2) => { if (eosesReceived[i2]) return; eosesReceived[i2] = true; if (eosesReceived.filter((a) => a).length === requests.length) { params.oneose?.(); handleEose = () => { }; } }; const closesReceived = []; let handleClose = (i2, reason) => { if (closesReceived[i2]) return; handleEose(i2); closesReceived[i2] = reason; if (closesReceived.filter((a) => a).length === requests.length) { params.onclose?.(closesReceived); handleClose = () => { }; } }; const localAlreadyHaveEventHandler = (id) => { if (params.alreadyHaveEvent?.(id)) { return true; } const have = _knownIds.has(id); _knownIds.add(id); return have; }; const allOpened = Promise.all( groupedRequests.map(async ({ url, filters }, i2) => { let relay; try { relay = await this.ensureRelay(url, { connectionTimeout: params.maxWait ? Math.max(params.maxWait * 0.8, params.maxWait - 1e3) : void 0 }); } catch (err) { handleClose(i2, err?.message || String(err)); return; } let subscription = relay.subscribe(filters, { ...params, oneose: () => handleEose(i2), onclose: (reason) => { if (reason.startsWith("auth-required: ") && params.onauth) { relay.auth(params.onauth).then(() => { relay.subscribe(filters, { ...params, oneose: () => handleEose(i2), onclose: (reason2) => { handleClose(i2, reason2); }, alreadyHaveEvent: localAlreadyHaveEventHandler, eoseTimeout: params.maxWait }); }).catch((err) => { handleClose(i2, `auth was required and attempted, but failed with: ${err}`); }); } else { handleClose(i2, reason); } }, alreadyHaveEvent: localAlreadyHaveEventHandler, eoseTimeout: params.maxWait }); subs.push(subscription); }) ); return { async close(reason) { await allOpened; subs.forEach((sub) => { sub.close(reason); }); } }; } subscribeEose(relays, filter, params) { params.onauth = params.onauth || params.doauth; const subcloser = this.subscribe(relays, filter, { ...params, oneose() { subcloser.close("closed automatically on eose"); } }); return subcloser; } subscribeManyEose(relays, filter, params) { params.onauth = params.onauth || params.doauth; const subcloser = this.subscribeMany(relays, filter, { ...params, oneose() { subcloser.close("closed automatically on eose"); } }); return subcloser; } async querySync(relays, filter, params) { return new Promise(async (resolve) => { const events = []; this.subscribeEose(relays, filter, { ...params, onevent(event) { events.push(event); }, onclose(_) { resolve(events); } }); }); } async get(relays, filter, params) { filter.limit = 1; const events = await this.querySync(relays, filter, params); events.sort((a, b) => b.created_at - a.created_at); return events[0] || null; } publish(relays, event, options) { return relays.map(normalizeURL).map(async (url, i2, arr) => { if (arr.indexOf(url) !== i2) { return Promise.reject("duplicate url"); } let r = await this.ensureRelay(url); return r.publish(event).catch(async (err) => { if (err instanceof Error && err.message.startsWith("auth-required: ") && options?.onauth) { await r.auth(options.onauth); return r.publish(event); } throw err; }).then((reason) => { if (this.trackRelays) { let set = this.seenOn.get(event.id); if (!set) { set = /* @__PURE__ */ new Set(); this.seenOn.set(event.id, set); } set.add(r); } return reason; }); }); } listConnectionStatus() { const map = /* @__PURE__ */ new Map(); this.relays.forEach((relay, url) => map.set(url, relay.connected)); return map; } destroy() { this.relays.forEach((conn) => conn.close()); this.relays = /* @__PURE__ */ new Map(); } }; // pool.ts var _WebSocket2; try { _WebSocket2 = WebSocket; } catch { } var SimplePool = class extends AbstractSimplePool { constructor(options) { super({ verifyEvent, websocketImplementation: _WebSocket2, ...options }); } }; // nip19.ts var nip19_exports = {}; __export(nip19_exports, { BECH32_REGEX: () => BECH32_REGEX, Bech32MaxSize: () => Bech32MaxSize, NostrTypeGuard: () => NostrTypeGuard, decode: () => decode, decodeNostrURI: () => decodeNostrURI, encodeBytes: () => encodeBytes, naddrEncode: () => naddrEncode, neventEncode: () => neventEncode, noteEncode: () => noteEncode, nprofileEncode: () => nprofileEncode, npubEncode: () => npubEncode, nsecEncode: () => nsecEncode }); import { bytesToHex as bytesToHex3, concatBytes, hexToBytes as hexToBytes2 } from "@noble/hashes/utils"; import { bech32 } from "@scure/base"; var NostrTypeGuard = { isNProfile: (value) => /^nprofile1[a-z\d]+$/.test(value || ""), isNEvent: (value) => /^nevent1[a-z\d]+$/.test(value || ""), isNAddr: (value) => /^naddr1[a-z\d]+$/.test(value || ""), isNSec: (value) => /^nsec1[a-z\d]{58}$/.test(value || ""), isNPub: (value) => /^npub1[a-z\d]{58}$/.test(value || ""), isNote: (value) => /^note1[a-z\d]+$/.test(value || ""), isNcryptsec: (value) => /^ncryptsec1[a-z\d]+$/.test(value || "") }; 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 decodeNostrURI(nip19code) { try { if (nip19code.startsWith("nostr:")) nip19code = nip19code.substring(6); return decode(nip19code); } catch (_err) { return { type: "invalid", data: null }; } } function decode(code) { let { prefix, words } = bech32.decode(code, Bech32MaxSize); let data = new Uint8Array(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: bytesToHex3(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: bytesToHex3(tlv[0][0]), relays: tlv[1] ? tlv[1].map((d) => utf8Decoder.decode(d)) : [], author: tlv[2]?.[0] ? bytesToHex3(tlv[2][0]) : void 0, kind: tlv[3]?.[0] ? parseInt(bytesToHex3(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: bytesToHex3(tlv[2][0]), kind: parseInt(bytesToHex3(tlv[3][0]), 16), relays: tlv[1] ? tlv[1].map((d) => utf8Decoder.decode(d)) : [] } }; } case "nsec": return { type: prefix, data }; case "npub": case "note": return { type: prefix, data: bytesToHex3(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]; 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(key) { return encodeBytes("nsec", key); } function npubEncode(hex) { return encodeBytes("npub", hexToBytes2(hex)); } function noteEncode(hex) { return encodeBytes("note", hexToBytes2(hex)); } function encodeBech32(prefix, data) { let words = bech32.toWords(data); return bech32.encode(prefix, words, Bech32MaxSize); } function encodeBytes(prefix, bytes) { return encodeBech32(prefix, bytes); } function nprofileEncode(profile) { let data = encodeTLV({ 0: [hexToBytes2(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: [hexToBytes2(event.id)], 1: (event.relays || []).map((url) => utf8Encoder.encode(url)), 2: event.author ? [hexToBytes2(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: [hexToBytes2(addr.pubkey)], 3: [new Uint8Array(kind)] }); return encodeBech32("naddr", data); } function encodeTLV(tlv) { let entries = []; Object.entries(tlv).reverse().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 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 }); import { bytesToHex as bytesToHex4, randomBytes } from "@noble/hashes/utils"; import { secp256k1 } from "@noble/curves/secp256k1"; import { cbc } from "@noble/ciphers/aes"; 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 normalizedKey = getNormalizedX(key); let iv = Uint8Array.from(randomBytes(16)); let plaintext = utf8Encoder.encode(text); let ciphertext = cbc(normalizedKey, iv).encrypt(plaintext); let ctb64 = base64.encode(new Uint8Array(ciphertext)); 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; let [ctb64, ivb64] = data.split("?iv="); let key = secp256k1.getSharedSecret(privkey, "02" + pubkey); let normalizedKey = getNormalizedX(key); let iv = base64.decode(ivb64); let ciphertext = base64.decode(ctb64); let plaintext = cbc(normalizedKey, iv).decrypt(ciphertext); return utf8Decoder.decode(plaintext); } function getNormalizedX(key) { return key.slice(1, 33); } // nip05.ts var nip05_exports = {}; __export(nip05_exports, { NIP05_REGEX: () => NIP05_REGEX, isNip05: () => isNip05, isValid: () => isValid, queryProfile: () => queryProfile, searchDomain: () => searchDomain, useFetchImplementation: () => useFetchImplementation }); var NIP05_REGEX = /^(?:([\w.+-]+)@)?([\w_-]+(\.[\w_-]+)+)$/; var isNip05 = (value) => NIP05_REGEX.test(value || ""); var _fetch; try { _fetch = fetch; } catch (_) { null; } function useFetchImplementation(fetchImplementation) { _fetch = fetchImplementation; } async function searchDomain(domain, query = "") { try { const url = `https://${domain}/.well-known/nostr.json?name=${query}`; const res = await _fetch(url, { redirect: "manual" }); if (res.status !== 200) { throw Error("Wrong response code"); } const json = await res.json(); return json.names; } catch (_) { return {}; } } async function queryProfile(fullname) { const match = fullname.match(NIP05_REGEX); if (!match) return null; const [, name = "_", domain] = match; try { const url = `https://${domain}/.well-known/nostr.json?name=${name}`; const res = await _fetch(url, { redirect: "manual" }); if (res.status !== 200) { throw Error("Wrong response code"); } const json = await res.json(); const pubkey = json.names[name]; return pubkey ? { pubkey, relays: json.relays?.[pubkey] } : null; } catch (_e) { return null; } } async function isValid(pubkey, nip05) { const res = await queryProfile(nip05); return res ? res.pubkey === pubkey : false; } // nip10.ts var nip10_exports = {}; __export(nip10_exports, { parse: () => parse }); function parse(event) { const result = { reply: void 0, root: void 0, mentions: [], profiles: [], quotes: [] }; let maybeParent; let maybeRoot; for (let i2 = event.tags.length - 1; i2 >= 0; i2--) { const tag = event.tags[i2]; if (tag[0] === "e" && tag[1]) { const [_, eTagEventId, eTagRelayUrl, eTagMarker, eTagAuthor] = tag; const eventPointer = { id: eTagEventId, relays: eTagRelayUrl ? [eTagRelayUrl] : [], author: eTagAuthor }; if (eTagMarker === "root") { result.root = eventPointer; continue; } if (eTagMarker === "reply") { result.reply = eventPointer; continue; } if (eTagMarker === "mention") { result.mentions.push(eventPointer); continue; } if (!maybeParent) { maybeParent = eventPointer; } else { maybeRoot = eventPointer; } result.mentions.push(eventPointer); continue; } if (tag[0] === "q" && tag[1]) { const [_, eTagEventId, eTagRelayUrl] = tag; result.quotes.push({ id: eTagEventId, relays: eTagRelayUrl ? [eTagRelayUrl] : [] }); } if (tag[0] === "p" && tag[1]) { result.profiles.push({ pubkey: tag[1], relays: tag[2] ? [tag[2]] : [] }); continue; } } if (!result.root) { result.root = maybeRoot || maybeParent || result.reply; } if (!result.reply) { result.reply = maybeParent || result.root; } ; [result.reply, result.root].forEach((ref) => { if (!ref) return; let idx = result.mentions.indexOf(ref); if (idx !== -1) { result.mentions.splice(idx, 1); } if (ref.author) { let author = result.profiles.find((p) => p.pubkey === ref.author); if (author && author.relays) { if (!ref.relays) { ref.relays = []; } author.relays.forEach((url) => { if (ref.relays?.indexOf(url) === -1) ref.relays.push(url); }); author.relays = ref.relays; } } }); result.mentions.forEach((ref) => { if (ref.author) { let author = result.profiles.find((p) => p.pubkey === ref.author); if (author && author.relays) { if (!ref.relays) { ref.relays = []; } author.relays.forEach((url) => { if (ref.relays.indexOf(url) === -1) ref.relays.push(url); }); author.relays = ref.relays; } } }); return result; } // nip11.ts var nip11_exports = {}; __export(nip11_exports, { fetchRelayInformation: () => fetchRelayInformation, useFetchImplementation: () => useFetchImplementation2 }); var _fetch2; try { _fetch2 = fetch; } catch { } function useFetchImplementation2(fetchImplementation) { _fetch2 = fetchImplementation; } async function fetchRelayInformation(url) { return await (await fetch(url.replace("ws://", "http://").replace("wss://", "https://"), { headers: { Accept: "application/nostr+json" } })).json(); } // nip13.ts var nip13_exports = {}; __export(nip13_exports, { fastEventHash: () => fastEventHash, getPow: () => getPow, minePow: () => minePow }); import { bytesToHex as bytesToHex5 } from "@noble/hashes/utils"; import { sha256 as sha2562 } from "@noble/hashes/sha256"; function getPow(hex) { let count = 0; for (let i2 = 0; i2 < 64; i2 += 8) { const nibble = parseInt(hex.substring(i2, i2 + 8), 16); if (nibble === 0) { count += 32; } else { count += Math.clz32(nibble); 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 now2 = Math.floor(new Date().getTime() / 1e3); if (now2 !== event.created_at) { count = 0; event.created_at = now2; } tag[1] = (++count).toString(); event.id = fastEventHash(event); if (getPow(event.id) >= difficulty) { break; } } return event; } function fastEventHash(evt) { return bytesToHex5( sha2562(utf8Encoder.encode(JSON.stringify([0, evt.pubkey, evt.created_at, evt.kind, evt.tags, evt.content]))) ); } // nip17.ts var nip17_exports = {}; __export(nip17_exports, { unwrapEvent: () => unwrapEvent2, unwrapManyEvents: () => unwrapManyEvents2, wrapEvent: () => wrapEvent2, wrapManyEvents: () => wrapManyEvents2 }); // nip59.ts var nip59_exports = {}; __export(nip59_exports, { createRumor: () => createRumor, createSeal: () => createSeal, createWrap: () => createWrap, unwrapEvent: () => unwrapEvent, unwrapManyEvents: () => unwrapManyEvents, wrapEvent: () => wrapEvent, wrapManyEvents: () => wrapManyEvents }); // nip44.ts var nip44_exports = {}; __export(nip44_exports, { decrypt: () => decrypt2, 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 { 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"); } function getMessageKeys(conversationKey, nonce) { const keys = hkdf_expand(sha2563, conversationKey, nonce, 76); return { chacha_key: keys.subarray(0, 32), chacha_nonce: keys.subarray(32, 44), hmac_key: keys.subarray(44, 76) }; } function calcPaddedLen(len) { if (!Number.isSafeInteger(len) || len < 1) 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); } function writeU16BE(num) { if (!Number.isSafeInteger(num) || num < minPlaintextSize || num > maxPlaintextSize) throw new Error("invalid plaintext size: must be between 1 and 65535 bytes"); const arr = new Uint8Array(2); new DataView(arr.buffer).setUint16(0, num, false); return arr; } function pad(plaintext) { const unpadded = utf8Encoder.encode(plaintext); const unpaddedLen = unpadded.length; const prefix = writeU16BE(unpaddedLen); const suffix = new Uint8Array(calcPaddedLen(unpaddedLen) - unpaddedLen); return concatBytes2(prefix, unpadded, suffix); } function unpad(padded) { const unpaddedLen = new DataView(padded.buffer).getUint16(0); const unpadded = padded.subarray(2, 2 + unpaddedLen); if (unpaddedLen < minPlaintextSize || unpaddedLen > maxPlaintextSize || unpadded.length !== unpaddedLen || padded.length !== 2 + calcPaddedLen(unpaddedLen)) throw new Error("invalid padding"); return utf8Decoder.decode(unpadded); } function hmacAad(key, message, aad) { if (aad.length !== 32) throw new Error("AAD associated data must be 32 bytes"); const combined = concatBytes2(aad, message); return hmac(sha2563, key, combined); } function decodePayload(payload) { if (typeof payload !== "string") throw new Error("payload must be a valid string"); const plen = payload.length; if (plen < 132 || plen > 87472) throw new Error("invalid payload length: " + plen); if (payload[0] === "#") throw new Error("unknown encryption version"); let data; try { data = base642.decode(payload); } catch (error) { throw new Error("invalid base64: " + error.message); } const dlen = data.length; if (dlen < 99 || dlen > 65603) throw new Error("invalid data length: " + dlen); const vers = data[0]; if (vers !== 2) throw new Error("unknown encryption version " + vers); return { nonce: data.subarray(1, 33), ciphertext: data.subarray(33, -32), mac: data.subarray(-32) }; } function encrypt2(plaintext, conversationKey, nonce = randomBytes2(32)) { const { chacha_key, chacha_nonce, hmac_key } = getMessageKeys(conversationKey, nonce); const padded = pad(plaintext); const ciphertext = chacha20(chacha_key, chacha_nonce, padded); const mac = hmacAad(hmac_key, ciphertext, nonce); return base642.encode(concatBytes2(new Uint8Array([2]), nonce, ciphertext, mac)); } function decrypt2(payload, conversationKey) { const { nonce, ciphertext, mac } = decodePayload(payload); const { chacha_key, chacha_nonce, hmac_key } = getMessageKeys(conversationKey, nonce); const calculatedMac = hmacAad(hmac_key, ciphertext, nonce); if (!equalBytes(calculatedMac, mac)) throw new Error("invalid MAC"); const padded = chacha20(chacha_key, chacha_nonce, ciphertext); return unpad(padded); } var v2 = { utils: { getConversationKey, calcPaddedLen }, encrypt: encrypt2, decrypt: decrypt2 }; // nip59.ts var TWO_DAYS = 2 * 24 * 60 * 60; var now = () => Math.round(Date.now() / 1e3); var randomNow = () => Math.round(now() - Math.random() * TWO_DAYS); var nip44ConversationKey = (privateKey, publicKey) => getConversationKey(privateKey, publicKey); var nip44Encrypt = (data, privateKey, publicKey) => encrypt2(JSON.stringify(data), nip44ConversationKey(privateKey, publicKey)); var nip44Decrypt = (data, privateKey) => JSON.parse(decrypt2(data.content, nip44ConversationKey(privateKey, data.pubkey))); function createRumor(event, privateKey) { const rumor = { created_at: now(), content: "", tags: [], ...event, pubkey: getPublicKey(privateKey) }; rumor.id = getEventHash(rumor); return rumor; } function createSeal(rumor, privateKey, recipientPublicKey) { return finalizeEvent( { kind: Seal, content: nip44Encrypt(rumor, privateKey, recipientPublicKey), created_at: randomNow(), tags: [] }, privateKey ); } function createWrap(seal, recipientPublicKey) { const randomKey = generateSecretKey(); return finalizeEvent( { kind: GiftWrap, content: nip44Encrypt(seal, randomKey, recipientPublicKey), created_at: randomNow(), tags: [["p", recipientPublicKey]] }, randomKey ); } function wrapEvent(event, senderPrivateKey, recipientPublicKey) { const rumor = createRumor(event, senderPrivateKey); const seal = createSeal(rumor, senderPrivateKey, recipientPublicKey); return createWrap(seal, recipientPublicKey); } function wrapManyEvents(event, senderPrivateKey, recipientsPublicKeys) { if (!recipientsPublicKeys || recipientsPublicKeys.length === 0) { throw new Error("At least one recipient is required."); } const senderPublicKey = getPublicKey(senderPrivateKey); const wrappeds = [wrapEvent(event, senderPrivateKey, senderPublicKey)]; recipientsPublicKeys.forEach((recipientPublicKey) => { wrappeds.push(wrapEvent(event, senderPrivateKey, recipientPublicKey)); }); return wrappeds; } function unwrapEvent(wrap, recipientPrivateKey) { const unwrappedSeal = nip44Decrypt(wrap, recipientPrivateKey); return nip44Decrypt(unwrappedSeal, recipientPrivateKey); } function unwrapManyEvents(wrappedEvents, recipientPrivateKey) { let unwrappedEvents = []; wrappedEvents.forEach((e) => { unwrappedEvents.push(unwrapEvent(e, recipientPrivateKey)); }); unwrappedEvents.sort((a, b) => a.created_at - b.created_at); return unwrappedEvents; } // nip17.ts function createEvent(recipients, message, conversationTitle, replyTo) { const baseEvent = { created_at: Math.ceil(Date.now() / 1e3), kind: PrivateDirectMessage, tags: [], content: message }; const recipientsArray = Array.isArray(recipients) ? recipients : [recipients]; recipientsArray.forEach(({ publicKey, relayUrl }) => { baseEvent.tags.push(relayUrl ? ["p", publicKey, relayUrl] : ["p", publicKey]); }); if (replyTo) { baseEvent.tags.push(["e", replyTo.eventId, replyTo.relayUrl || "", "reply"]); } if (conversationTitle) { baseEvent.tags.push(["subject", conversationTitle]); } return baseEvent; } function wrapEvent2(senderPrivateKey, recipient, message, conversationTitle, replyTo) { const event = createEvent(recipient, message, conversationTitle, replyTo); return wrapEvent(event, senderPrivateKey, recipient.publicKey); } function wrapManyEvents2(senderPrivateKey, recipients, message, conversationTitle, replyTo) { if (!recipients || recipients.length === 0) { throw new Error("At least one recipient is required."); } const senderPublicKey = getPublicKey(senderPrivateKey); return [{ publicKey: senderPublicKey }, ...recipients].map( (recipient) => wrapEvent2(senderPrivateKey, recipient, message, conversationTitle, replyTo) ); } var unwrapEvent2 = unwrapEvent; var unwrapManyEvents2 = unwrapManyEvents; // nip18.ts var nip18_exports = {}; __export(nip18_exports, { finishRepostEvent: () => finishRepostEvent, getRepostedEvent: () => getRepostedEvent, getRepostedEventPointer: () => getRepostedEventPointer }); function finishRepostEvent(t, reposted, relayUrl, privateKey) { let kind; const tags = [...t.tags ?? [], ["e", reposted.id, relayUrl], ["p", reposted.pubkey]]; if (reposted.kind === ShortTextNote) { kind = Repost; } else { kind = GenericRepost; tags.push(["k", String(reposted.kind)]); } return finalizeEvent( { kind, tags, content: t.content === "" || reposted.tags?.find((tag) => tag[0] === "-") ? "" : JSON.stringify(reposted), created_at: t.created_at }, privateKey ); } function getRepostedEventPointer(event) { if (![Repost, GenericRepost].includes(event.kind)) { return void 0; } let lastETag; let lastPTag; for (let i2 = event.tags.length - 1; i2 >= 0 && (lastETag === void 0 || lastPTag === void 0); i2--) { const tag = event.tags[i2]; 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 && !verifyEvent(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 finalizeEvent( { ...t, kind: Reaction, tags: [...t.tags ?? [], ...inheritedTags, ["e", reacted.id], ["p", reacted.pubkey]], content: t.content ?? "+" }, privateKey ); } function getReactedEventPointer(event) { if (event.kind !== Reaction) { return void 0; } let lastETag; let lastPTag; for (let i2 = event.tags.length - 1; i2 >= 0 && (lastETag === void 0 || lastPTag === void 0); i2--) { const tag = event.tags[i2]; 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] }; } // nip27.ts var nip27_exports = {}; __export(nip27_exports, { parse: () => parse3 }); var noCharacter = /\W/m; var noURLCharacter = /\W |\W$|$|,| /m; function* parse3(content) { const max = content.length; let prevIndex = 0; let index = 0; while (index < max) { let u = content.indexOf(":", index); if (u === -1) { break; } if (content.substring(u - 5, u) === "nostr") { const m = content.substring(u + 60).match(noCharacter); const end = m ? u + 60 + m.index : max; try { let pointer; let { data, type } = decode(content.substring(u + 1, end)); switch (type) { case "npub": pointer = { pubkey: data }; break; case "nsec": case "note": index = end + 1; continue; default: pointer = data; } if (prevIndex !== u - 5) { yield { type: "text", text: content.substring(prevIndex, u - 5) }; } yield { type: "reference", pointer }; index = end; prevIndex = index; continue; } catch (_err) { index = u + 1; continue; } } else if (content.substring(u - 5, u) === "https" || content.substring(u - 4, u) === "http") { const m = content.substring(u + 4).match(noURLCharacter); const end = m ? u + 4 + m.index : max; const prefixLen = content[u - 1] === "s" ? 5 : 4; try { let url = new URL(content.substring(u - prefixLen, end)); if (url.hostname.indexOf(".") === -1) { throw new Error("invalid url"); } if (prevIndex !== u - prefixLen) { yield { type: "text", text: content.substring(prevIndex, u - prefixLen) }; } if (/\.(png|jpe?g|gif|webp)$/i.test(url.pathname)) { yield { type: "image", url: url.toString() }; index = end; prevIndex = index; continue; } if (/\.(mp4|avi|webm|mkv)$/i.test(url.pathname)) { yield { type: "video", url: url.toString() }; index = end; prevIndex = index; continue; } if (/\.(mp3|aac|ogg|opus)$/i.test(url.pathname)) { yield { type: "audio", url: url.toString() }; index = end; prevIndex = index; continue; } yield { type: "url", url: url.toString() }; index = end; prevIndex = index; continue; } catch (_err) { index = end + 1; continue; } } else if (content.substring(u - 3, u) === "wss" || content.substring(u - 2, u) === "ws") { const m = content.substring(u + 4).match(noURLCharacter); const end = m ? u + 4 + m.index : max; const prefixLen = content[u - 1] === "s" ? 3 : 2; try { let url = new URL(content.substring(u - prefixLen, end)); if (url.hostname.indexOf(".") === -1) { throw new Error("invalid ws url"); } if (prevIndex !== u - prefixLen) { yield { type: "text", text: content.substring(prevIndex, u - prefixLen) }; } yield { type: "relay", url: url.toString() }; index = end; prevIndex = index; continue; } catch (_err) { index = end + 1; continue; } } else { index = u + 1; continue; } } if (prevIndex !== max) { yield { type: "text", text: content.substring(prevIndex) }; } } // 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 finalizeEvent( { kind: 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 finalizeEvent( { kind: 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 finalizeEvent( { kind: 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 finalizeEvent( { kind: 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 finalizeEvent( { kind: ChannelMuteUser, tags: [["p", t.pubkey_to_mute], ...t.tags ?? []], content, created_at: t.created_at }, privateKey ); }; // nip30.ts var nip30_exports = {}; __export(nip30_exports, { EMOJI_SHORTCODE_REGEX: () => EMOJI_SHORTCODE_REGEX, matchAll: () => matchAll, regex: () => regex, replaceAll: () => replaceAll }); var EMOJI_SHORTCODE_REGEX = /:(\w+):/; var regex = () => new RegExp(`\\B${EMOJI_SHORTCODE_REGEX.source}\\B`, "g"); function* matchAll(content) { const matches = content.matchAll(regex()); for (const match of matches) { try { const [shortcode, name] = match; yield { shortcode, name, start: match.index, end: match.index + shortcode.length }; } catch (_e) { } } } function replaceAll(content, replacer) { return content.replaceAll(regex(), (shortcode, name) => { return replacer({ shortcode, name }); }); } // nip39.ts var nip39_exports = {}; __export(nip39_exports, { useFetchImplementation: () => useFetchImplementation3, validateGithub: () => validateGithub }); var _fetch3; try { _fetch3 = fetch; } catch { } function useFetchImplementation3(fetchImplementation) { _fetch3 = fetchImplementation; } async function validateGithub(pubkey, username, proof) { try { let res = await (await _fetch3(`https://gist.github.com/${username}/${proof}/raw`)).text(); return res === `Verifying that I control the following Nostr public key: ${pubkey}`; } catch (_) { return false; } } // nip47.ts var nip47_exports = {}; __export(nip47_exports, { makeNwcRequestEvent: () => makeNwcRequestEvent, parseConnectionString: () => parseConnectionString }); function parseConnectionString(connectionString) { const { host, pathname, searchParams } = new URL(connectionString); const pubkey = pathname || host; 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, secretKey, invoice) { const content = { method: "pay_invoice", params: { invoice } }; const encryptedContent = encrypt(secretKey, pubkey, JSON.stringify(content)); const eventTemplate = { kind: NWCWalletRequest, created_at: Math.round(Date.now() / 1e3), content: encryptedContent, tags: [["p", pubkey]] }; return finalizeEvent(eventTemplate, secretKey); } // nip54.ts var nip54_exports = {}; __export(nip54_exports, { normalizeIdentifier: () => normalizeIdentifier }); function normalizeIdentifier(name) { name = name.trim().toLowerCase(); name = name.normalize("NFKC"); return Array.from(name).map((char) => { if (/\p{Letter}/u.test(char) || /\p{Number}/u.test(char)) { return char; } return "-"; }).join(""); } // nip57.ts var nip57_exports = {}; __export(nip57_exports, { getSatoshisAmountFromBolt11: () => getSatoshisAmountFromBolt11, getZapEndpoint: () => getZapEndpoint, makeZapReceipt: () => makeZapReceipt, makeZapRequest: () => makeZapRequest, useFetchImplementation: () => useFetchImplementation4, validateZapRequest: () => validateZapRequest }); import { bech32 as bech322 } from "@scure/base"; var _fetch4; try { _fetch4 = fetch; } catch { } function useFetchImplementation4(fetchImplementation) { _fetch4 = fetchImplementation; } async function getZapEndpoint(metadata) { try { let lnurl = ""; let { lud06, lud16 } = JSON.parse(metadata.content); if (lud06) { let { words } = bech322.decode(lud06, 1e3); let data = bech322.fromWords(words); lnurl = utf8Decoder.decode(data); } else if (lud16) { let [name, domain] = lud16.split("@"); lnurl = new URL(`/.well-known/lnurlp/${name}`, `https://${domain}`).toString(); } else { return null; } let res = await _fetch4(lnurl); let body = await res.json(); if (body.allowsNostr && body.nostrPubkey) { return body.callback; } } catch (err) { } return null; } function makeZapRequest(params) { let zr = { kind: 9734, created_at: Math.round(Date.now() / 1e3), content: params.comment || "", tags: [ ["p", "pubkey" in params ? params.pubkey : params.event.pubkey], ["amount", params.amount.toString()], ["relays", ...params.relays] ] }; if ("event" in params) { zr.tags.push(["e", params.event.id]); if (isReplaceableKind(params.event.kind)) { const a = ["a", `${params.event.kind}:${params.event.pubkey}:`]; zr.tags.push(a); } else if (isAddressableKind(params.event.kind)) { let d = params.event.tags.find(([t, v]) => t === "d" && v); if (!d) throw new Error("d tag not found or is empty"); const a = ["a", `${params.event.kind}:${params.event.pubkey}:${d[1]}`]; zr.tags.push(a); } zr.tags.push(["k", params.event.kind.toString()]); } 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 (!verifyEvent(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, ["P", zr.pubkey], ["bolt11", bolt11], ["description", zapRequest]] }; if (preimage) { zap.tags.push(["preimage", preimage]); } return zap; } function getSatoshisAmountFromBolt11(bolt11) { if (bolt11.length < 50) { return 0; } bolt11 = bolt11.substring(0, 50); const idx = bolt11.lastIndexOf("1"); if (idx === -1) { return 0; } const hrp = bolt11.substring(0, idx); if (!hrp.startsWith("lnbc")) { return 0; } const amount = hrp.substring(4); if (amount.length < 1) { return 0; } const char = amount[amount.length - 1]; const digit = char.charCodeAt(0) - "0".charCodeAt(0); const isDigit = digit >= 0 && digit <= 9; let cutPoint = amount.length - 1; if (isDigit) { cutPoint++; } if (cutPoint < 1) { return 0; } const num = parseInt(amount.substring(0, cutPoint)); switch (char) { case "m": return num * 1e5; case "u": return num * 100; case "n": return num / 10; case "p": return num / 1e4; default: return num * 1e8; } } // nip98.ts var nip98_exports = {}; __export(nip98_exports, { getToken: () => getToken, hashPayload: () => hashPayload, unpackEventFromToken: () => unpackEventFromToken, validateEvent: () => validateEvent2, validateEventKind: () => validateEventKind, validateEventMethodTag: () => validateEventMethodTag, validateEventPayloadTag: () => validateEventPayloadTag, validateEventTimestamp: () => validateEventTimestamp, validateEventUrlTag: () => validateEventUrlTag, validateToken: () => validateToken }); import { sha256 as sha2564 } from "@noble/hashes/sha256"; import { bytesToHex as bytesToHex6 } from "@noble/hashes/utils"; import { base64 as base643 } from "@scure/base"; var _authorizationScheme = "Nostr "; async function getToken(loginUrl, httpMethod, sign, includeAuthorizationScheme = false, payload) { const event = { kind: HTTPAuth, tags: [ ["u", loginUrl], ["method", httpMethod] ], created_at: Math.round(new Date().getTime() / 1e3), content: "" }; if (payload) { event.tags.push(["payload", hashPayload(payload)]); } const signedEvent = await sign(event); const authorizationScheme = includeAuthorizationScheme ? _authorizationScheme : ""; return authorizationScheme + base643.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(base643.decode(token)); if (!eventB64 || eventB64.length === 0 || !eventB64.startsWith("{")) { throw new Error("Invalid token"); } const event = JSON.parse(eventB64); return event; } function validateEventTimestamp(event) { if (!event.created_at) { return false; } return Math.round(new Date().getTime() / 1e3) - event.created_at < 60; } function validateEventKind(event) { return event.kind === HTTPAuth; } function validateEventUrlTag(event, url) { const urlTag = event.tags.find((t) => t[0] === "u"); if (!urlTag) { return false; } return urlTag.length > 0 && urlTag[1] === url; } function validateEventMethodTag(event, method) { const methodTag = event.tags.find((t) => t[0] === "method"); if (!methodTag) { return false; } return methodTag.length > 0 && methodTag[1].toLowerCase() === method.toLowerCase(); } function hashPayload(payload) { const hash = sha2564(utf8Encoder.encode(JSON.stringify(payload))); return bytesToHex6(hash); } function validateEventPayloadTag(event, payload) { const payloadTag = event.tags.find((t) => t[0] === "payload"); if (!payloadTag) { return false; } const payloadHash = hashPayload(payload); return payloadTag.length > 0 && payloadTag[1] === payloadHash; } async function validateEvent2(event, url, method, body) { if (!verifyEvent(event)) { throw new Error("Invalid nostr event, signature invalid"); } if (!validateEventKind(event)) { throw new Error("Invalid nostr event, kind invalid"); } if (!validateEventTimestamp(event)) { throw new Error("Invalid nostr event, created_at timestamp invalid"); } if (!validateEventUrlTag(event, url)) { throw new Error("Invalid nostr event, url tag invalid"); } if (!validateEventMethodTag(event, method)) { throw new Error("Invalid nostr event, method tag invalid"); } if (Boolean(body) && typeof body === "object" && Object.keys(body).length > 0) { if (!validateEventPayloadTag(event, body)) { throw new Error("Invalid nostr event, payload tag does not match request body hash"); } } return true; } export { Relay, SimplePool, finalizeEvent, fakejson_exports as fj, generateSecretKey, getEventHash, getFilterLimit, getPublicKey, kinds_exports as kinds, matchFilter, matchFilters, mergeFilters, nip04_exports as nip04, nip05_exports as nip05, nip10_exports as nip10, nip11_exports as nip11, nip13_exports as nip13, nip17_exports as nip17, nip18_exports as nip18, nip19_exports as nip19, nip21_exports as nip21, nip25_exports as nip25, nip27_exports as nip27, nip28_exports as nip28, nip30_exports as nip30, nip39_exports as nip39, nip42_exports as nip42, nip44_exports as nip44, nip47_exports as nip47, nip54_exports as nip54, nip57_exports as nip57, nip59_exports as nip59, nip98_exports as nip98, parseReferences, serializeEvent, sortEvents, utils_exports as utils, validateEvent, verifiedSymbol, verifyEvent };