"use strict";
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __commonJS = (cb, mod) => function __require() {
  return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
};
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 __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
  // If the importer is in node compatibility mode or this is not an ESM
  // file that has been converted to a CommonJS file using a Babel-
  // compatible transform (i.e. "__esModule" has not been set), then set
  // "default" to the CommonJS "module.exports" for node compatibility.
  isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
  mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);

// ../node_modules/eventemitter3/index.js
var require_eventemitter3 = __commonJS({
  "../node_modules/eventemitter3/index.js"(exports, module2) {
    "use strict";
    var has = Object.prototype.hasOwnProperty;
    var prefix = "~";
    function Events() {
    }
    if (Object.create) {
      Events.prototype = /* @__PURE__ */ Object.create(null);
      if (!new Events().__proto__)
        prefix = false;
    }
    function EE(fn, context, once) {
      this.fn = fn;
      this.context = context;
      this.once = once || false;
    }
    function addListener(emitter, event, fn, context, once) {
      if (typeof fn !== "function") {
        throw new TypeError("The listener must be a function");
      }
      var listener = new EE(fn, context || emitter, once), evt = prefix ? prefix + event : event;
      if (!emitter._events[evt])
        emitter._events[evt] = listener, emitter._eventsCount++;
      else if (!emitter._events[evt].fn)
        emitter._events[evt].push(listener);
      else
        emitter._events[evt] = [emitter._events[evt], listener];
      return emitter;
    }
    function clearEvent(emitter, evt) {
      if (--emitter._eventsCount === 0)
        emitter._events = new Events();
      else
        delete emitter._events[evt];
    }
    function EventEmitter11() {
      this._events = new Events();
      this._eventsCount = 0;
    }
    EventEmitter11.prototype.eventNames = function eventNames() {
      var names = [], events, name;
      if (this._eventsCount === 0)
        return names;
      for (name in events = this._events) {
        if (has.call(events, name))
          names.push(prefix ? name.slice(1) : name);
      }
      if (Object.getOwnPropertySymbols) {
        return names.concat(Object.getOwnPropertySymbols(events));
      }
      return names;
    };
    EventEmitter11.prototype.listeners = function listeners(event) {
      var evt = prefix ? prefix + event : event, handlers = this._events[evt];
      if (!handlers)
        return [];
      if (handlers.fn)
        return [handlers.fn];
      for (var i = 0, l = handlers.length, ee = new Array(l); i < l; i++) {
        ee[i] = handlers[i].fn;
      }
      return ee;
    };
    EventEmitter11.prototype.listenerCount = function listenerCount(event) {
      var evt = prefix ? prefix + event : event, listeners = this._events[evt];
      if (!listeners)
        return 0;
      if (listeners.fn)
        return 1;
      return listeners.length;
    };
    EventEmitter11.prototype.emit = function emit(event, a1, a2, a3, a4, a5) {
      var evt = prefix ? prefix + event : event;
      if (!this._events[evt])
        return false;
      var listeners = this._events[evt], len = arguments.length, args, i;
      if (listeners.fn) {
        if (listeners.once)
          this.removeListener(event, listeners.fn, void 0, true);
        switch (len) {
          case 1:
            return listeners.fn.call(listeners.context), true;
          case 2:
            return listeners.fn.call(listeners.context, a1), true;
          case 3:
            return listeners.fn.call(listeners.context, a1, a2), true;
          case 4:
            return listeners.fn.call(listeners.context, a1, a2, a3), true;
          case 5:
            return listeners.fn.call(listeners.context, a1, a2, a3, a4), true;
          case 6:
            return listeners.fn.call(listeners.context, a1, a2, a3, a4, a5), true;
        }
        for (i = 1, args = new Array(len - 1); i < len; i++) {
          args[i - 1] = arguments[i];
        }
        listeners.fn.apply(listeners.context, args);
      } else {
        var length = listeners.length, j;
        for (i = 0; i < length; i++) {
          if (listeners[i].once)
            this.removeListener(event, listeners[i].fn, void 0, true);
          switch (len) {
            case 1:
              listeners[i].fn.call(listeners[i].context);
              break;
            case 2:
              listeners[i].fn.call(listeners[i].context, a1);
              break;
            case 3:
              listeners[i].fn.call(listeners[i].context, a1, a2);
              break;
            case 4:
              listeners[i].fn.call(listeners[i].context, a1, a2, a3);
              break;
            default:
              if (!args)
                for (j = 1, args = new Array(len - 1); j < len; j++) {
                  args[j - 1] = arguments[j];
                }
              listeners[i].fn.apply(listeners[i].context, args);
          }
        }
      }
      return true;
    };
    EventEmitter11.prototype.on = function on(event, fn, context) {
      return addListener(this, event, fn, context, false);
    };
    EventEmitter11.prototype.once = function once(event, fn, context) {
      return addListener(this, event, fn, context, true);
    };
    EventEmitter11.prototype.removeListener = function removeListener(event, fn, context, once) {
      var evt = prefix ? prefix + event : event;
      if (!this._events[evt])
        return this;
      if (!fn) {
        clearEvent(this, evt);
        return this;
      }
      var listeners = this._events[evt];
      if (listeners.fn) {
        if (listeners.fn === fn && (!once || listeners.once) && (!context || listeners.context === context)) {
          clearEvent(this, evt);
        }
      } else {
        for (var i = 0, events = [], length = listeners.length; i < length; i++) {
          if (listeners[i].fn !== fn || once && !listeners[i].once || context && listeners[i].context !== context) {
            events.push(listeners[i]);
          }
        }
        if (events.length)
          this._events[evt] = events.length === 1 ? events[0] : events;
        else
          clearEvent(this, evt);
      }
      return this;
    };
    EventEmitter11.prototype.removeAllListeners = function removeAllListeners(event) {
      var evt;
      if (event) {
        evt = prefix ? prefix + event : event;
        if (this._events[evt])
          clearEvent(this, evt);
      } else {
        this._events = new Events();
        this._eventsCount = 0;
      }
      return this;
    };
    EventEmitter11.prototype.off = EventEmitter11.prototype.removeListener;
    EventEmitter11.prototype.addListener = EventEmitter11.prototype.on;
    EventEmitter11.prefixed = prefix;
    EventEmitter11.EventEmitter = EventEmitter11;
    if ("undefined" !== typeof module2) {
      module2.exports = EventEmitter11;
    }
  }
});

// src/index.ts
var src_exports = {};
__export(src_exports, {
  NDKAppHandlerEvent: () => NDKAppHandlerEvent,
  NDKArticle: () => NDKArticle,
  NDKDVMJobFeedback: () => NDKDVMJobFeedback,
  NDKDVMJobResult: () => NDKDVMJobResult,
  NDKDVMRequest: () => NDKDVMRequest,
  NDKDvmJobFeedbackStatus: () => NDKDvmJobFeedbackStatus,
  NDKEvent: () => NDKEvent,
  NDKHighlight: () => NDKHighlight,
  NDKKind: () => NDKKind,
  NDKList: () => NDKList,
  NDKListKinds: () => NDKListKinds,
  NDKNip07Signer: () => NDKNip07Signer,
  NDKNip46Backend: () => NDKNip46Backend,
  NDKNip46Signer: () => NDKNip46Signer,
  NDKNostrRpc: () => NDKNostrRpc,
  NDKPrivateKeySigner: () => NDKPrivateKeySigner,
  NDKRelay: () => NDKRelay,
  NDKRelayList: () => NDKRelayList,
  NDKRelaySet: () => NDKRelaySet,
  NDKRelayStatus: () => NDKRelayStatus,
  NDKRepost: () => NDKRepost,
  NDKSubscription: () => NDKSubscription,
  NDKSubscriptionCacheUsage: () => NDKSubscriptionCacheUsage,
  NDKTranscriptionDVM: () => NDKTranscriptionDVM,
  NDKUser: () => NDKUser,
  calculateGroupableId: () => calculateGroupableId,
  default: () => NDK,
  defaultOpts: () => defaultOpts,
  mergeFilters: () => mergeFilters,
  profileFromEvent: () => profileFromEvent,
  serializeProfile: () => serializeProfile,
  zapInvoiceFromEvent: () => zapInvoiceFromEvent
});
module.exports = __toCommonJS(src_exports);

// src/user/index.ts
var import_nostr_tools8 = require("nostr-tools");

// src/events/index.ts
var import_tseep4 = require("tseep");
var import_nostr_tools7 = require("nostr-tools");

// src/relay/index.ts
var import_debug = __toESM(require("debug"));
var import_tseep2 = require("tseep");

// src/relay/connectivity.ts
var import_nostr_tools = require("nostr-tools");
var NDKRelayConnectivity = class {
  ndkRelay;
  _status;
  relay;
  connectedAt;
  _connectionStats = {
    attempts: 0,
    success: 0,
    durations: []
  };
  debug;
  constructor(ndkRelay) {
    this.ndkRelay = ndkRelay;
    this._status = 3 /* DISCONNECTED */;
    this.relay = (0, import_nostr_tools.relayInit)(this.ndkRelay.url);
    this.debug = this.ndkRelay.debug.extend("connectivity");
    this.relay.on("notice", (notice) => this.handleNotice(notice));
  }
  async connect() {
    const connectHandler = () => {
      this.updateConnectionStats.connected();
      this._status = 1 /* CONNECTED */;
      this.ndkRelay.emit("connect");
    };
    const disconnectHandler = () => {
      this.updateConnectionStats.disconnected();
      if (this._status === 1 /* CONNECTED */) {
        this._status = 3 /* DISCONNECTED */;
        this.handleReconnection();
      }
      this.ndkRelay.emit("disconnect");
    };
    try {
      this.updateConnectionStats.attempt();
      this._status = 0 /* CONNECTING */;
      this.relay.off("connect", connectHandler);
      this.relay.off("disconnect", disconnectHandler);
      this.relay.on("connect", connectHandler);
      this.relay.on("disconnect", disconnectHandler);
      await this.relay.connect();
    } catch (e) {
      this.debug("Failed to connect", e);
      this._status = 3 /* DISCONNECTED */;
      throw e;
    }
  }
  disconnect() {
    this._status = 2 /* DISCONNECTING */;
    this.relay.close();
  }
  get status() {
    return this._status;
  }
  isAvailable() {
    return this._status === 1 /* CONNECTED */;
  }
  /**
   * Evaluates the connection stats to determine if the relay is flapping.
   */
  isFlapping() {
    const durations = this._connectionStats.durations;
    if (durations.length % 3 !== 0)
      return false;
    const sum = durations.reduce((a, b) => a + b, 0);
    const avg = sum / durations.length;
    const variance = durations.map((x) => Math.pow(x - avg, 2)).reduce((a, b) => a + b, 0) / durations.length;
    const stdDev = Math.sqrt(variance);
    const isFlapping = stdDev < 1e3;
    return isFlapping;
  }
  async handleNotice(notice) {
    if (notice.includes("oo many") || notice.includes("aximum")) {
      this.disconnect();
      setTimeout(() => this.connect(), 2e3);
      this.debug(this.relay.url, "Relay complaining?", notice);
    }
    this.ndkRelay.emit("notice", this, notice);
  }
  /**
   * Called when the relay is unexpectedly disconnected.
   */
  handleReconnection(attempt = 0) {
    if (this.isFlapping()) {
      this.ndkRelay.emit("flapping", this, this._connectionStats);
      this._status = 5 /* FLAPPING */;
      return;
    }
    const reconnectDelay = this.connectedAt ? Math.max(0, 6e4 - (Date.now() - this.connectedAt)) : 0;
    setTimeout(() => {
      this._status = 4 /* RECONNECTING */;
      this.connect().then(() => {
        this.debug("Reconnected");
      }).catch((err) => {
        this.debug("Reconnect failed", err);
        if (attempt < 5) {
          setTimeout(() => {
            this.handleReconnection(attempt + 1);
          }, 6e4);
        } else {
          this.debug("Reconnect failed after 5 attempts");
        }
      });
    }, reconnectDelay);
  }
  /**
   * Utility functions to update the connection stats.
   */
  updateConnectionStats = {
    connected: () => {
      this._connectionStats.success++;
      this._connectionStats.connectedAt = Date.now();
    },
    disconnected: () => {
      if (this._connectionStats.connectedAt) {
        this._connectionStats.durations.push(
          Date.now() - this._connectionStats.connectedAt
        );
        if (this._connectionStats.durations.length > 100) {
          this._connectionStats.durations.shift();
        }
      }
      this._connectionStats.connectedAt = void 0;
    },
    attempt: () => {
      this._connectionStats.attempts++;
    }
  };
  /**
   * Returns the connection stats.
   */
  get connectionStats() {
    return this._connectionStats;
  }
};

// src/relay/publisher.ts
var NDKRelayPublisher = class {
  ndkRelay;
  constructor(ndkRelay) {
    this.ndkRelay = ndkRelay;
  }
  /**
   * Published an event to the relay; if the relay is not connected, it will
   * wait for the relay to connect before publishing the event.
   *
   * If the relay does not connect within the timeout, the publish operation
   * will fail.
   * @param event  The event to publish
   * @param timeoutMs  The timeout for the publish operation in milliseconds
   * @returns A promise that resolves when the event has been published or rejects if the operation times out
   */
  async publish(event, timeoutMs = 2500) {
    const publishWhenConnected = () => {
      return new Promise((resolve, reject) => {
        try {
          this.publishEvent(event, timeoutMs).then((result) => resolve(result)).catch((err) => reject(err));
        } catch (err) {
          reject(err);
        }
      });
    };
    const timeoutPromise = new Promise((_, reject) => {
      setTimeout(() => reject(new Error("Timeout")), timeoutMs);
    });
    const onConnectHandler = () => {
      publishWhenConnected().then((result) => connectResolve(result)).catch((err) => connectReject(err));
    };
    let connectResolve;
    let connectReject;
    if (this.ndkRelay.status === 1 /* CONNECTED */) {
      return Promise.race([publishWhenConnected(), timeoutPromise]);
    } else {
      return Promise.race([
        new Promise((resolve, reject) => {
          connectResolve = resolve;
          connectReject = reject;
          this.ndkRelay.once("connect", onConnectHandler);
        }),
        timeoutPromise
      ]).finally(() => {
        this.ndkRelay.removeListener("connect", onConnectHandler);
      });
    }
  }
  async publishEvent(event, timeoutMs) {
    const nostrEvent = await event.toNostrEvent();
    const publish = this.ndkRelay.connectivity.relay.publish(nostrEvent);
    let publishTimeout;
    const publishPromise = new Promise((resolve, reject) => {
      publish.then(() => {
        clearTimeout(publishTimeout);
        this.ndkRelay.emit("published", event);
        resolve(true);
      }).catch((err) => {
        clearTimeout(publishTimeout);
        this.ndkRelay.debug("Publish failed", err, event.id);
        this.ndkRelay.emit("publish:failed", event, err);
        reject(err);
      });
    });
    if (!timeoutMs || event.isEphemeral()) {
      return publishPromise;
    }
    const timeoutPromise = new Promise((_, reject) => {
      publishTimeout = setTimeout(() => {
        this.ndkRelay.debug("Publish timed out", event.rawEvent());
        this.ndkRelay.emit("publish:failed", event, "Timeout");
        reject(new Error("Publish operation timed out"));
      }, timeoutMs);
    });
    return Promise.race([publishPromise, timeoutPromise]);
  }
};

// src/relay/subscriptions.ts
var import_tseep = require("tseep");
var import_nostr_tools3 = require("nostr-tools");

// src/subscription/grouping.ts
function calculateGroupableId(filters) {
  const elements = [];
  for (const filter of filters) {
    const hasTimeConstraints = filter.since || filter.until;
    if (hasTimeConstraints)
      return null;
    const keys = Object.keys(filter || {}).sort().join("-");
    elements.push(keys);
  }
  return elements.join("|");
}
function mergeFilters(filters) {
  const result = {};
  filters.forEach((filter) => {
    Object.entries(filter).forEach(([key, value]) => {
      if (Array.isArray(value)) {
        if (result[key] === void 0) {
          result[key] = [...value];
        } else {
          result[key] = Array.from(/* @__PURE__ */ new Set([...result[key], ...value]));
        }
      } else {
        result[key] = value;
      }
    });
  });
  return result;
}

// src/subscription/utils.ts
var import_nostr_tools2 = require("nostr-tools");
function queryFullyFilled(subscription) {
  if (filterIncludesIds(subscription.filter)) {
    if (resultHasAllRequestedIds(subscription)) {
      return true;
    }
  }
  return false;
}
function compareFilter(filter1, filter2) {
  if (Object.keys(filter1).length !== Object.keys(filter2).length)
    return false;
  for (const [key, value] of Object.entries(filter1)) {
    const valuesInFilter2 = filter2[key];
    if (!valuesInFilter2)
      return false;
    if (Array.isArray(value) && Array.isArray(valuesInFilter2)) {
      const v = value;
      for (const valueInFilter2 of valuesInFilter2) {
        const val = valueInFilter2;
        if (!v.includes(val)) {
          return false;
        }
      }
    } else {
      if (valuesInFilter2 !== value)
        return false;
    }
  }
  return true;
}
function filterIncludesIds(filter) {
  return !!filter["ids"];
}
function resultHasAllRequestedIds(subscription) {
  const ids = subscription.filter["ids"];
  return !!ids && ids.length === subscription.eventFirstSeen.size;
}
function generateSubId(subscriptions, filters) {
  const subIds = subscriptions.map((sub) => sub.subId).filter(Boolean);
  const subIdParts = [];
  const filterNonKindKeys = /* @__PURE__ */ new Set();
  const filterKinds = /* @__PURE__ */ new Set();
  if (subIds.length > 0) {
    subIdParts.push(Array.from(new Set(subIds)).join(","));
  } else {
    for (const filter of filters) {
      for (const key of Object.keys(filter)) {
        if (key === "kinds") {
          filter.kinds?.forEach((k) => filterKinds.add(k));
        } else {
          filterNonKindKeys.add(key);
        }
      }
    }
    if (filterKinds.size > 0) {
      subIdParts.push("kinds:" + Array.from(filterKinds).join(","));
    }
    if (filterNonKindKeys.size > 0) {
      subIdParts.push(Array.from(filterNonKindKeys).join(","));
    }
  }
  if (subIds.length !== 1) {
    subIdParts.push(Math.floor(Math.random() * 999).toString());
  }
  return subIdParts.join("-");
}
function filterFromId(id) {
  let decoded;
  if (id.match(NIP33_A_REGEX)) {
    const [kind, pubkey, identifier] = id.split(":");
    const filter = {
      authors: [pubkey],
      kinds: [parseInt(kind)]
    };
    if (identifier) {
      filter["#d"] = [identifier];
    }
    return filter;
  }
  try {
    decoded = import_nostr_tools2.nip19.decode(id);
    switch (decoded.type) {
      case "nevent":
        return { ids: [decoded.data.id] };
      case "note":
        return { ids: [decoded.data] };
      case "naddr":
        return {
          authors: [decoded.data.pubkey],
          "#d": [decoded.data.identifier],
          kinds: [decoded.data.kind]
        };
    }
  } catch (e) {
  }
  return { ids: [id] };
}
function isNip33AValue(value) {
  return value.match(NIP33_A_REGEX) !== null;
}
var NIP33_A_REGEX = /^(\d+):([0-9A-Fa-f]+)(?::(.*))?$/;
function relaysFromBech32(bech322) {
  try {
    const decoded = import_nostr_tools2.nip19.decode(bech322);
    if (["naddr", "nevent"].includes(decoded?.type)) {
      const data = decoded.data;
      if (data?.relays) {
        return data.relays.map((r) => new NDKRelay(r));
      }
    }
  } catch (e) {
  }
  return [];
}

// src/relay/subscriptions.ts
var NDKGroupedSubscriptions = class extends import_tseep.EventEmitter {
  subscriptions;
  req;
  debug;
  constructor(subscriptions, debug4) {
    super();
    this.subscriptions = subscriptions;
    this.debug = debug4 || this.subscriptions[0].subscription.debug.extend("grouped");
    for (const subscription of subscriptions) {
      this.handleSubscriptionClosure(subscription);
    }
  }
  /**
   * Adds a subscription to this group.
   * @param subscription
   */
  addSubscription(subscription) {
    this.subscriptions.push(subscription);
    this.handleSubscriptionClosure(subscription);
  }
  eventReceived(event) {
    for (const subscription of this.subscriptions) {
      subscription.eventReceived(event);
    }
  }
  eoseReceived(relay) {
    const subscriptionsToInform = Array.from(this.subscriptions);
    subscriptionsToInform.forEach(async (subscription) => {
      subscription.subscription.eoseReceived(relay);
    });
  }
  handleSubscriptionClosure(subscription) {
    subscription.subscription.on("close", () => {
      const index = this.subscriptions.findIndex(
        (i) => i.subscription === subscription.subscription
      );
      this.subscriptions.splice(index, 1);
      if (this.subscriptions.length <= 0) {
        this.emit("close");
      }
    });
  }
  /**
   * Maps each subscription through a transformation function.
   * @param fn - The transformation function.
   * @returns A new array with each subscription transformed by fn.
   */
  map(fn) {
    return this.subscriptions.map(fn);
  }
  [Symbol.iterator]() {
    let index = 0;
    const subscriptions = this.subscriptions;
    return {
      next() {
        if (index < subscriptions.length) {
          return { value: subscriptions[index++], done: false };
        } else {
          return { value: null, done: true };
        }
      }
    };
  }
};
var NDKSubscriptionFilters = class {
  subscription;
  filters = [];
  ndkRelay;
  constructor(subscription, filters, ndkRelay) {
    this.subscription = subscription;
    this.filters = filters;
    this.ndkRelay = ndkRelay;
  }
  eventReceived(event) {
    if (!this.eventMatchesLocalFilter(event))
      return;
    this.subscription.eventReceived(event, this.ndkRelay, false);
  }
  eventMatchesLocalFilter(event) {
    const rawEvent = event.rawEvent();
    return this.filters.some((filter) => (0, import_nostr_tools3.matchFilter)(filter, rawEvent));
  }
};
function findMatchingActiveSubscriptions(activeSubscriptions, filters) {
  if (activeSubscriptions.length !== filters.length)
    return false;
  for (let i = 0; i < activeSubscriptions.length; i++) {
    if (!compareFilter(activeSubscriptions[i], filters[i])) {
      break;
    }
    return activeSubscriptions[i];
  }
  return void 0;
}
var NDKRelaySubscriptions = class {
  ndkRelay;
  delayedItems = /* @__PURE__ */ new Map();
  delayedTimers = /* @__PURE__ */ new Map();
  /**
   * Active subscriptions this relay is connected to
   */
  activeSubscriptions = /* @__PURE__ */ new Map();
  activeSubscriptionsByGroupId = /* @__PURE__ */ new Map();
  debug;
  groupingDebug;
  conn;
  constructor(ndkRelay) {
    this.ndkRelay = ndkRelay;
    this.conn = ndkRelay.connectivity;
    this.debug = ndkRelay.debug.extend("subscriptions");
    this.groupingDebug = ndkRelay.debug.extend("grouping");
  }
  /**
   * Creates or queues a subscription to the relay.
   */
  subscribe(subscription, filters) {
    const groupableId = calculateGroupableId(filters);
    const subscriptionFilters = new NDKSubscriptionFilters(
      subscription,
      filters,
      this.ndkRelay
    );
    if (!groupableId || !subscription.isGroupable()) {
      this.groupingDebug("No groupable ID for filters", filters);
      this.executeSubscriptions(
        groupableId,
        // hacky
        new NDKGroupedSubscriptions([subscriptionFilters]),
        filters
      );
      return;
    }
    const activeSubscriptions = this.activeSubscriptionsByGroupId.get(groupableId);
    if (activeSubscriptions) {
      const matchingSubscription = findMatchingActiveSubscriptions(
        activeSubscriptions.filters,
        filters
      );
      if (matchingSubscription) {
        const activeSubscription = this.activeSubscriptions.get(activeSubscriptions.sub);
        activeSubscription?.addSubscription(
          new NDKSubscriptionFilters(subscription, filters, this.ndkRelay)
        );
        return;
      }
    }
    let delayedItem = this.delayedItems.get(groupableId);
    if (!delayedItem) {
      delayedItem = new NDKGroupedSubscriptions([subscriptionFilters]);
      this.delayedItems.set(groupableId, delayedItem);
      delayedItem.once("close", () => {
        const delayedItem2 = this.delayedItems.get(groupableId);
        if (!delayedItem2)
          return;
        this.delayedItems.delete(groupableId);
      });
    } else {
      delayedItem.addSubscription(subscriptionFilters);
    }
    const timeout = setTimeout(() => {
      this.executeGroup(groupableId, subscription);
    }, subscription.opts.groupableDelay);
    if (this.delayedTimers.has(groupableId)) {
      this.delayedTimers.get(groupableId).push(timeout);
    } else {
      this.delayedTimers.set(groupableId, [timeout]);
    }
  }
  /**
   * Executes a delayed subscription via its groupable ID.
   * @param groupableId
   */
  executeGroup(groupableId, triggeredBy) {
    const delayedItem = this.delayedItems.get(groupableId);
    this.delayedItems.delete(groupableId);
    const timeouts = this.delayedTimers.get(groupableId);
    this.delayedTimers.delete(groupableId);
    if (timeouts) {
      for (const timeout of timeouts) {
        clearTimeout(timeout);
      }
    }
    if (delayedItem) {
      const filterCount = delayedItem.subscriptions[0].filters.length;
      const mergedFilters = [];
      for (let i = 0; i < filterCount; i++) {
        const allFiltersAtIndex = delayedItem.map((di) => di.filters[i]);
        mergedFilters.push(mergeFilters(allFiltersAtIndex));
      }
      this.executeSubscriptions(groupableId, delayedItem, mergedFilters);
    }
  }
  /**
   * Executes one or more subscriptions.
   *
   * If the relay is not connected, subscriptions will be queued
   * until the relay connects.
   *
   * @param groupableId
   * @param subscriptionFilters
   * @param mergedFilters
   */
  executeSubscriptions(groupableId, groupedSubscriptions, mergedFilters) {
    if (this.conn.isAvailable()) {
      this.executeSubscriptionsConnected(groupableId, groupedSubscriptions, mergedFilters);
    } else {
      const connectedListener = () => {
        this.debug("new relay coming online for active subscription", {
          relay: this.ndkRelay.url,
          mergeFilters
        });
        this.executeSubscriptionsConnected(
          groupableId,
          groupedSubscriptions,
          mergedFilters
        );
      };
      this.ndkRelay.once("connect", connectedListener);
      groupedSubscriptions.once("close", () => {
        this.ndkRelay.removeListener("connect", connectedListener);
      });
    }
  }
  /**
   * Executes one or more subscriptions.
   *
   * When there are more than one subscription, results
   * will be sent to the right subscription
   *
   * @param subscriptions
   * @param filters The filters as they should be sent to the relay
   */
  executeSubscriptionsConnected(groupableId, groupedSubscriptions, mergedFilters) {
    const subscriptions = [];
    for (const { subscription } of groupedSubscriptions) {
      subscriptions.push(subscription);
    }
    const subId = generateSubId(subscriptions, mergedFilters);
    groupedSubscriptions.req = mergedFilters;
    const sub = this.conn.relay.sub(mergedFilters, { id: subId });
    this.activeSubscriptions.set(sub, groupedSubscriptions);
    if (groupableId) {
      this.activeSubscriptionsByGroupId.set(groupableId, { filters: mergedFilters, sub });
    }
    sub.on("event", (event) => {
      const e = new NDKEvent(void 0, event);
      e.relay = this.ndkRelay;
      const subFilters = this.activeSubscriptions.get(sub);
      subFilters?.eventReceived(e);
    });
    sub.on("eose", () => {
      const subFilters = this.activeSubscriptions.get(sub);
      subFilters?.eoseReceived(this.ndkRelay);
    });
    groupedSubscriptions.once("close", () => {
      sub.unsub();
      this.activeSubscriptions.delete(sub);
      if (groupableId) {
        this.activeSubscriptionsByGroupId.delete(groupableId);
      }
    });
    return sub;
  }
  executedFilters() {
    const ret = /* @__PURE__ */ new Map();
    for (const [, groupedSubscriptions] of this.activeSubscriptions) {
      ret.set(
        groupedSubscriptions.req,
        groupedSubscriptions.map((sub) => sub.subscription)
      );
    }
    return ret;
  }
};

// src/relay/index.ts
var NDKRelayStatus = /* @__PURE__ */ ((NDKRelayStatus2) => {
  NDKRelayStatus2[NDKRelayStatus2["CONNECTING"] = 0] = "CONNECTING";
  NDKRelayStatus2[NDKRelayStatus2["CONNECTED"] = 1] = "CONNECTED";
  NDKRelayStatus2[NDKRelayStatus2["DISCONNECTING"] = 2] = "DISCONNECTING";
  NDKRelayStatus2[NDKRelayStatus2["DISCONNECTED"] = 3] = "DISCONNECTED";
  NDKRelayStatus2[NDKRelayStatus2["RECONNECTING"] = 4] = "RECONNECTING";
  NDKRelayStatus2[NDKRelayStatus2["FLAPPING"] = 5] = "FLAPPING";
  return NDKRelayStatus2;
})(NDKRelayStatus || {});
var NDKRelay = class extends import_tseep2.EventEmitter {
  url;
  scores;
  connectivity;
  subs;
  publisher;
  complaining = false;
  debug;
  constructor(url) {
    super();
    this.url = url;
    this.scores = /* @__PURE__ */ new Map();
    this.debug = (0, import_debug.default)(`ndk:relay:${url}`);
    this.connectivity = new NDKRelayConnectivity(this);
    this.subs = new NDKRelaySubscriptions(this);
    this.publisher = new NDKRelayPublisher(this);
  }
  get status() {
    return this.connectivity.status;
  }
  get connectionStats() {
    return this.connectivity.connectionStats;
  }
  /**
   * Connects to the relay.
   */
  async connect() {
    return this.connectivity.connect();
  }
  /**
   * Disconnects from the relay.
   */
  disconnect() {
    this.connectivity.disconnect();
  }
  /**
   * Queues or executes the subscription of a specific set of filters
   * within this relay.
   *
   * @param subscription NDKSubscription this filters belong to.
   * @param filters Filters to execute
   */
  subscribe(subscription, filters) {
    this.subs.subscribe(subscription, filters);
  }
  /**
   * Publishes an event to the relay with an optional timeout.
   *
   * If the relay is not connected, the event will be published when the relay connects,
   * unless the timeout is reached before the relay connects.
   *
   * @param event The event to publish
   * @param timeoutMs The timeout for the publish operation in milliseconds
   * @returns A promise that resolves when the event has been published or rejects if the operation times out
   */
  async publish(event, timeoutMs = 2500) {
    return this.publisher.publish(event, timeoutMs);
  }
  /**
   * Called when this relay has responded with an event but
   * wasn't the fastest one.
   * @param timeDiffInMs The time difference in ms between the fastest and this relay in milliseconds
   */
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
  scoreSlowerEvent(timeDiffInMs) {
  }
  /** @deprecated Use referenceTags instead. */
  tagReference(marker) {
    const tag = ["r", this.url];
    if (marker) {
      tag.push(marker);
    }
    return tag;
  }
  referenceTags() {
    return [["r", this.url]];
  }
  activeSubscriptions() {
    return this.subs.executedFilters();
  }
};

// src/relay/sets/index.ts
var NDKRelaySet = class {
  relays;
  debug;
  ndk;
  constructor(relays, ndk) {
    this.relays = relays;
    this.ndk = ndk;
    this.debug = ndk.debug.extend("relayset");
  }
  /**
   * Adds a relay to this set.
   */
  addRelay(relay) {
    this.relays.add(relay);
  }
  /**
   * Creates a relay set from a list of relay URLs.
   *
   * If no connection to the relay is found in the pool it will temporarily
   * connect to it.
   *
   * @param relayUrls - list of relay URLs to include in this set
   * @param ndk
   * @returns NDKRelaySet
   */
  static fromRelayUrls(relayUrls, ndk) {
    const relays = /* @__PURE__ */ new Set();
    for (const url of relayUrls) {
      const relay = ndk.pool.relays.get(url);
      if (relay) {
        relays.add(relay);
      } else {
        const temporaryRelay = new NDKRelay(url);
        ndk.pool.useTemporaryRelay(temporaryRelay);
        relays.add(temporaryRelay);
      }
    }
    return new NDKRelaySet(new Set(relays), ndk);
  }
  /**
   * Publish an event to all relays in this set. Returns the number of relays that have received the event.
   * @param event
   * @param timeoutMs - timeout in milliseconds for each publish operation and connection operation
   * @returns A set where the event was successfully published to
   */
  async publish(event, timeoutMs) {
    const publishedToRelays = /* @__PURE__ */ new Set();
    const isEphemeral2 = event.isEphemeral();
    const promises = Array.from(this.relays).map((relay) => {
      return new Promise((resolve) => {
        relay.publish(event, timeoutMs).then(() => {
          publishedToRelays.add(relay);
          resolve();
        }).catch((err) => {
          if (!isEphemeral2) {
            this.debug("error publishing to relay", {
              relay: relay.url,
              err
            });
          }
          resolve();
        });
      });
    });
    await Promise.all(promises);
    if (publishedToRelays.size === 0) {
      if (!isEphemeral2) {
        throw new Error("No relay was able to receive the event");
      }
    }
    return publishedToRelays;
  }
  size() {
    return this.relays.size;
  }
};

// src/relay/sets/calculate.ts
function calculateRelaySetFromEvent(ndk, event) {
  const relays = /* @__PURE__ */ new Set();
  ndk.pool?.relays.forEach((relay) => relays.add(relay));
  return new NDKRelaySet(relays, ndk);
}
function getWriteRelaysFor(ndk, author) {
  if (!ndk.outboxTracker)
    return void 0;
  return ndk.outboxTracker.data.get(author)?.writeRelays;
}
function calculateRelaySetsFromFilter(ndk, filters) {
  const result = /* @__PURE__ */ new Map();
  const authors = /* @__PURE__ */ new Set();
  filters.forEach((filter) => {
    if (filter.authors) {
      filter.authors.forEach((author) => authors.add(author));
    }
  });
  if (authors.size > 0) {
    const authorToRelaysMap = /* @__PURE__ */ new Map();
    for (const author of authors) {
      const userWriteRelays = getWriteRelaysFor(ndk, author);
      if (userWriteRelays && userWriteRelays.size > 0) {
        ndk.debug(`Adding ${userWriteRelays.size} relays for ${author}`);
        userWriteRelays.forEach((relay) => {
          const authorsInRelay = authorToRelaysMap.get(relay) || [];
          authorsInRelay.push(author);
          authorToRelaysMap.set(relay, authorsInRelay);
        });
      } else {
        ndk.explicitRelayUrls?.forEach((relay) => {
          const authorsInRelay = authorToRelaysMap.get(relay) || [];
          authorsInRelay.push(author);
          authorToRelaysMap.set(relay, authorsInRelay);
        });
      }
    }
    for (const relayUrl of authorToRelaysMap.keys()) {
      result.set(relayUrl, []);
    }
    for (const filter of filters) {
      if (filter.authors) {
        for (const [relayUrl, authors2] of authorToRelaysMap.entries()) {
          result.set(relayUrl, [...result.get(relayUrl), { ...filter, authors: authors2 }]);
        }
      } else {
        for (const relayUrl of authorToRelaysMap.keys()) {
          result.set(relayUrl, [...result.get(relayUrl), filter]);
        }
      }
    }
  } else {
    ndk.explicitRelayUrls?.forEach((relay) => {
      result.set(relay, filters);
    });
  }
  return result;
}
function calculateRelaySetsFromFilters(ndk, filters) {
  return calculateRelaySetsFromFilter(ndk, filters);
}

// src/zap/index.ts
var import_base = require("@scure/base");
var import_tseep3 = require("tseep");
var import_nostr_tools4 = require("nostr-tools");
var DEFAULT_RELAYS = [
  "wss://nos.lol",
  "wss://relay.nostr.band",
  "wss://relay.f7z.io",
  "wss://relay.damus.io",
  "wss://nostr.mom",
  "wss://no.str.cr"
];
var Zap = class extends import_tseep3.EventEmitter {
  ndk;
  zappedEvent;
  zappedUser;
  constructor(args) {
    super();
    this.ndk = args.ndk;
    this.zappedEvent = args.zappedEvent;
    this.zappedUser = args.zappedUser || this.ndk.getUser({ hexpubkey: this.zappedEvent?.pubkey });
  }
  async getZapEndpoint() {
    let lud06;
    let lud16;
    let zapEndpoint;
    let zapEndpointCallback;
    if (this.zappedUser) {
      if (!this.zappedUser.profile) {
        await this.zappedUser.fetchProfile();
      }
      lud06 = (this.zappedUser.profile || {}).lud06;
      lud16 = (this.zappedUser.profile || {}).lud16;
    }
    if (lud16 && !lud16.startsWith("LNURL")) {
      const [name, domain] = lud16.split("@");
      zapEndpoint = `https://${domain}/.well-known/lnurlp/${name}`;
    } else if (lud06) {
      const { words } = import_base.bech32.decode(lud06, 1e3);
      const data = import_base.bech32.fromWords(words);
      const utf8Decoder = new TextDecoder("utf-8");
      zapEndpoint = utf8Decoder.decode(data);
    }
    if (!zapEndpoint) {
      throw new Error("No zap endpoint found");
    }
    const response = await fetch(zapEndpoint);
    const body = await response.json();
    if (body?.allowsNostr && (body?.nostrPubkey || body?.nostrPubKey)) {
      zapEndpointCallback = body.callback;
    }
    return zapEndpointCallback;
  }
  /**
   * Generates a kind:9734 zap request and returns the payment request
   * @param amount amount to zap in millisatoshis
   * @param comment optional comment to include in the zap request
   * @param extraTags optional extra tags to include in the zap request
   * @param relays optional relays to ask zapper to publish the zap to
   * @returns the payment request
   */
  async createZapRequest(amount, comment, extraTags, relays, signer) {
    const zapEndpoint = await this.getZapEndpoint();
    if (!zapEndpoint) {
      throw new Error("No zap endpoint found");
    }
    if (!this.zappedEvent && !this.zappedUser)
      throw new Error("No zapped event or user found");
    const zapRequest = import_nostr_tools4.nip57.makeZapRequest({
      profile: this.zappedUser.hexpubkey,
      // set the event to null since nostr-tools doesn't support nip-33 zaps
      event: null,
      amount,
      comment: comment || "",
      relays: relays ?? this.relays()
    });
    if (this.zappedEvent) {
      const tags = this.zappedEvent.referenceTags();
      zapRequest.tags.push(...tags);
    }
    zapRequest.tags.push(["lnurl", zapEndpoint]);
    const zapRequestEvent = new NDKEvent(this.ndk, zapRequest);
    if (extraTags) {
      zapRequestEvent.tags = zapRequestEvent.tags.concat(extraTags);
    }
    await zapRequestEvent.sign(signer);
    const zapRequestNostrEvent = await zapRequestEvent.toNostrEvent();
    const response = await fetch(
      `${zapEndpoint}?` + new URLSearchParams({
        amount: amount.toString(),
        nostr: JSON.stringify(zapRequestNostrEvent)
      })
    );
    const body = await response.json();
    return body.pr;
  }
  /**
   * @returns the relays to use for the zap request
   */
  relays() {
    let r = [];
    if (this.ndk?.pool?.relays) {
      r = this.ndk.pool.urls();
    }
    if (!r.length) {
      r = DEFAULT_RELAYS;
    }
    return r;
  }
};

// src/events/content-tagger.ts
var import_nostr_tools5 = require("nostr-tools");
function mergeTags(tags1, tags2) {
  const tagMap = /* @__PURE__ */ new Map();
  const generateKey = (tag) => tag.join(",");
  const isContained = (smaller, larger) => {
    return smaller.every((value, index) => value === larger[index]);
  };
  const processTag = (tag) => {
    for (let [key, existingTag] of tagMap) {
      if (isContained(existingTag, tag) || isContained(tag, existingTag)) {
        if (tag.length >= existingTag.length) {
          tagMap.set(key, tag);
        }
        return;
      }
    }
    tagMap.set(generateKey(tag), tag);
  };
  tags1.concat(tags2).forEach(processTag);
  return Array.from(tagMap.values());
}
async function generateContentTags(content, tags = []) {
  const tagRegex = /(@|nostr:)(npub|nprofile|note|nevent|naddr)[a-zA-Z0-9]+/g;
  const hashtagRegex = /#(\w+)/g;
  let promises = [];
  const addTagIfNew = (t) => {
    if (!tags.find((t2) => t2[0] === t[0] && t2[1] === t[1])) {
      tags.push(t);
    }
  };
  content = content.replace(tagRegex, (tag) => {
    try {
      const entity = tag.split(/(@|nostr:)/)[2];
      const { type, data } = import_nostr_tools5.nip19.decode(entity);
      let t;
      switch (type) {
        case "npub":
          t = ["p", data];
          break;
        case "nprofile":
          t = ["p", data.pubkey];
          break;
        case "note":
          promises.push(
            new Promise(async (resolve) => {
              addTagIfNew([
                "e",
                data,
                await maybeGetEventRelayUrl(entity),
                "mention"
              ]);
              resolve();
            })
          );
          break;
        case "nevent":
          promises.push(
            new Promise(async (resolve) => {
              let { id, relays, author } = data;
              if (!relays || relays.length === 0) {
                relays = [await maybeGetEventRelayUrl(entity)];
              }
              addTagIfNew(["e", id, relays[0], "mention"]);
              if (author)
                addTagIfNew(["p", author]);
              resolve();
            })
          );
          break;
        case "naddr":
          promises.push(
            new Promise(async (resolve) => {
              const id = [data.kind, data.pubkey, data.identifier].join(":");
              let relays = data.relays ?? [];
              if (relays.length === 0) {
                relays = [await maybeGetEventRelayUrl(entity)];
              }
              addTagIfNew(["a", id, relays[0], "mention"]);
              addTagIfNew(["p", data.pubkey]);
              resolve();
            })
          );
          break;
        default:
          return tag;
      }
      if (t)
        addTagIfNew(t);
      return `nostr:${entity}`;
    } catch (error) {
      return tag;
    }
  });
  await Promise.all(promises);
  content = content.replace(hashtagRegex, (tag, word) => {
    const t = ["t", word];
    if (!tags.find((t2) => t2[0] === t[0] && t2[1] === t[1])) {
      tags.push(t);
    }
    return tag;
  });
  return { content, tags };
}
async function maybeGetEventRelayUrl(nip19Id) {
  return "";
}

// src/events/kind.ts
function isReplaceable() {
  if (this.kind === void 0)
    throw new Error("Kind not set");
  return this.kind >= 1e4 && this.kind < 2e4;
}
function isEphemeral() {
  if (this.kind === void 0)
    throw new Error("Kind not set");
  return this.kind >= 2e4 && this.kind < 3e4;
}
function isParamReplaceable() {
  if (this.kind === void 0)
    throw new Error("Kind not set");
  return this.kind >= 3e4 && this.kind < 4e4;
}

// src/events/kinds/index.ts
var NDKKind = /* @__PURE__ */ ((NDKKind2) => {
  NDKKind2[NDKKind2["Metadata"] = 0] = "Metadata";
  NDKKind2[NDKKind2["Text"] = 1] = "Text";
  NDKKind2[NDKKind2["RecommendRelay"] = 2] = "RecommendRelay";
  NDKKind2[NDKKind2["Contacts"] = 3] = "Contacts";
  NDKKind2[NDKKind2["EncryptedDirectMessage"] = 4] = "EncryptedDirectMessage";
  NDKKind2[NDKKind2["EventDeletion"] = 5] = "EventDeletion";
  NDKKind2[NDKKind2["Repost"] = 6] = "Repost";
  NDKKind2[NDKKind2["Reaction"] = 7] = "Reaction";
  NDKKind2[NDKKind2["BadgeAward"] = 8] = "BadgeAward";
  NDKKind2[NDKKind2["GroupNote"] = 9] = "GroupNote";
  NDKKind2[NDKKind2["GroupChat"] = 11] = "GroupChat";
  NDKKind2[NDKKind2["GenericRepost"] = 16] = "GenericRepost";
  NDKKind2[NDKKind2["ChannelCreation"] = 40] = "ChannelCreation";
  NDKKind2[NDKKind2["ChannelMetadata"] = 41] = "ChannelMetadata";
  NDKKind2[NDKKind2["ChannelMessage"] = 42] = "ChannelMessage";
  NDKKind2[NDKKind2["ChannelHideMessage"] = 43] = "ChannelHideMessage";
  NDKKind2[NDKKind2["ChannelMuteUser"] = 44] = "ChannelMuteUser";
  NDKKind2[NDKKind2["Report"] = 1984] = "Report";
  NDKKind2[NDKKind2["Label"] = 1985] = "Label";
  NDKKind2[NDKKind2["DVMReqTextExtraction"] = 5e3] = "DVMReqTextExtraction";
  NDKKind2[NDKKind2["DVMReqTextSummarization"] = 5001] = "DVMReqTextSummarization";
  NDKKind2[NDKKind2["DVMReqTextTranslation"] = 5002] = "DVMReqTextTranslation";
  NDKKind2[NDKKind2["DVMReqTextGeneration"] = 5050] = "DVMReqTextGeneration";
  NDKKind2[NDKKind2["DVMReqImageGeneration"] = 5100] = "DVMReqImageGeneration";
  NDKKind2[NDKKind2["DVMReqDiscoveryNostrContent"] = 5300] = "DVMReqDiscoveryNostrContent";
  NDKKind2[NDKKind2["DVMReqDiscoveryNostrPeople"] = 5301] = "DVMReqDiscoveryNostrPeople";
  NDKKind2[NDKKind2["DVMReqTimestamping"] = 5900] = "DVMReqTimestamping";
  NDKKind2[NDKKind2["DVMJobFeedback"] = 7e3] = "DVMJobFeedback";
  NDKKind2[NDKKind2["MuteList"] = 1e4] = "MuteList";
  NDKKind2[NDKKind2["PinList"] = 10001] = "PinList";
  NDKKind2[NDKKind2["RelayList"] = 10002] = "RelayList";
  NDKKind2[NDKKind2["BookmarkList"] = 10003] = "BookmarkList";
  NDKKind2[NDKKind2["CommunityList"] = 10004] = "CommunityList";
  NDKKind2[NDKKind2["PublicChatList"] = 10005] = "PublicChatList";
  NDKKind2[NDKKind2["BlockRelayList"] = 10006] = "BlockRelayList";
  NDKKind2[NDKKind2["SearchRelayList"] = 10007] = "SearchRelayList";
  NDKKind2[NDKKind2["InterestList"] = 10015] = "InterestList";
  NDKKind2[NDKKind2["EmojiList"] = 10030] = "EmojiList";
  NDKKind2[NDKKind2["FollowSet"] = 3e4] = "FollowSet";
  NDKKind2[NDKKind2["CategorizedPeopleList"] = 3e4 /* FollowSet */] = "CategorizedPeopleList";
  NDKKind2[NDKKind2["CategorizedBookmarkList"] = 30001] = "CategorizedBookmarkList";
  NDKKind2[NDKKind2["RelaySet"] = 30002] = "RelaySet";
  NDKKind2[NDKKind2["CategorizedRelayList"] = 30002 /* RelaySet */] = "CategorizedRelayList";
  NDKKind2[NDKKind2["BookmarkSet"] = 30003] = "BookmarkSet";
  NDKKind2[NDKKind2["CurationSet"] = 30004] = "CurationSet";
  NDKKind2[NDKKind2["InterestSet"] = 30015] = "InterestSet";
  NDKKind2[NDKKind2["InterestsList"] = 30015 /* InterestSet */] = "InterestsList";
  NDKKind2[NDKKind2["EmojiSet"] = 30030] = "EmojiSet";
  NDKKind2[NDKKind2["HighlightSet"] = 39802] = "HighlightSet";
  NDKKind2[NDKKind2["CategorizedHighlightList"] = 39802 /* HighlightSet */] = "CategorizedHighlightList";
  NDKKind2[NDKKind2["ZapRequest"] = 9734] = "ZapRequest";
  NDKKind2[NDKKind2["Zap"] = 9735] = "Zap";
  NDKKind2[NDKKind2["Highlight"] = 9802] = "Highlight";
  NDKKind2[NDKKind2["ClientAuth"] = 22242] = "ClientAuth";
  NDKKind2[NDKKind2["NostrConnect"] = 24133] = "NostrConnect";
  NDKKind2[NDKKind2["ProfileBadge"] = 30008] = "ProfileBadge";
  NDKKind2[NDKKind2["BadgeDefinition"] = 30009] = "BadgeDefinition";
  NDKKind2[NDKKind2["MarketStall"] = 30017] = "MarketStall";
  NDKKind2[NDKKind2["MarketProduct"] = 30018] = "MarketProduct";
  NDKKind2[NDKKind2["Article"] = 30023] = "Article";
  NDKKind2[NDKKind2["AppSpecificData"] = 30078] = "AppSpecificData";
  NDKKind2[NDKKind2["Classified"] = 30402] = "Classified";
  NDKKind2[NDKKind2["AppRecommendation"] = 31989] = "AppRecommendation";
  NDKKind2[NDKKind2["AppHandler"] = 31990] = "AppHandler";
  return NDKKind2;
})(NDKKind || {});
var NDKListKinds = [
  1e4 /* MuteList */,
  10001 /* PinList */,
  10002 /* RelayList */,
  10003 /* BookmarkList */,
  10004 /* CommunityList */,
  10005 /* PublicChatList */,
  10006 /* BlockRelayList */,
  10007 /* SearchRelayList */,
  10015 /* InterestList */,
  10030 /* EmojiList */,
  3e4 /* FollowSet */,
  30003 /* BookmarkSet */,
  30001 /* CategorizedBookmarkList */,
  // Backwards compatibility
  30002 /* RelaySet */,
  30004 /* CurationSet */,
  30015 /* InterestSet */,
  30030 /* EmojiSet */,
  39802 /* HighlightSet */
];

// src/events/nip04.ts
async function encrypt(recipient, signer) {
  if (!this.ndk)
    throw new Error("No NDK instance found!");
  if (!signer) {
    await this.ndk.assertSigner();
    signer = this.ndk.signer;
  }
  if (!recipient) {
    const pTags = this.getMatchingTags("p");
    if (pTags.length !== 1) {
      throw new Error(
        "No recipient could be determined and no explicit recipient was provided"
      );
    }
    recipient = this.ndk.getUser({ hexpubkey: pTags[0][1] });
  }
  this.content = await signer?.encrypt(recipient, this.content);
}
async function decrypt(sender, signer) {
  if (!this.ndk)
    throw new Error("No NDK instance found!");
  if (!signer) {
    await this.ndk.assertSigner();
    signer = this.ndk.signer;
  }
  if (!sender) {
    sender = this.author;
  }
  this.content = await signer?.decrypt(sender, this.content);
}

// src/events/nip19.ts
var import_nostr_tools6 = require("nostr-tools");
function encode() {
  if (this.isParamReplaceable()) {
    return import_nostr_tools6.nip19.naddrEncode({
      kind: this.kind,
      pubkey: this.pubkey,
      identifier: this.replaceableDTag(),
      relays: this.relay ? [this.relay.url] : []
    });
  } else if (this.relay) {
    return import_nostr_tools6.nip19.neventEncode({
      id: this.tagId(),
      relays: [this.relay.url],
      author: this.pubkey
    });
  } else {
    return import_nostr_tools6.nip19.noteEncode(this.tagId());
  }
}

// src/events/repost.ts
async function repost(publish = true, signer) {
  if (!signer && publish) {
    if (!this.ndk)
      throw new Error("No NDK instance found");
    this.ndk.assertSigner();
    signer = this.ndk.signer;
  }
  const e = new NDKEvent(this.ndk, {
    kind: getKind(this),
    content: ""
  });
  e.tag(this);
  if (e.kind === 16 /* GenericRepost */) {
    e.tags.push(["k", `${this.kind}`]);
  }
  if (signer)
    await e.sign(signer);
  if (publish)
    await e.publish();
  return e;
}
function getKind(event) {
  if (event.kind === 1) {
    return 6 /* Repost */;
  }
  return 16 /* GenericRepost */;
}

// src/events/index.ts
var NDKEvent = class extends import_tseep4.EventEmitter {
  ndk;
  created_at;
  content = "";
  tags = [];
  kind;
  id = "";
  sig;
  pubkey = "";
  _author = void 0;
  /**
   * The relay that this event was first received from.
   */
  relay;
  constructor(ndk, event) {
    super();
    this.ndk = ndk;
    this.created_at = event?.created_at;
    this.content = event?.content || "";
    this.tags = event?.tags || [];
    this.id = event?.id || "";
    this.sig = event?.sig;
    this.pubkey = event?.pubkey || "";
    this.kind = event?.kind;
  }
  /**
   * Returns the event as is.
   */
  rawEvent() {
    return {
      created_at: this.created_at,
      content: this.content,
      tags: this.tags,
      kind: this.kind,
      pubkey: this.pubkey,
      id: this.id,
      sig: this.sig
    };
  }
  set author(user) {
    this.pubkey = user.hexpubkey;
    this._author = void 0;
  }
  /**
   * Returns an NDKUser for the author of the event.
   */
  get author() {
    if (this._author)
      return this._author;
    if (!this.ndk)
      throw new Error("No NDK instance found");
    const user = this.ndk.getUser({ hexpubkey: this.pubkey });
    this._author = user;
    return user;
  }
  tag(userOrEvent, marker) {
    const skipAuthorTag = userOrEvent?.pubkey === this.pubkey;
    const tags = userOrEvent.referenceTags(marker, skipAuthorTag);
    this.tags = mergeTags(this.tags, tags);
    if (userOrEvent instanceof NDKEvent) {
      for (const pTag of userOrEvent.getMatchingTags("p")) {
        if (pTag[1] === this.pubkey)
          continue;
        if (this.tags.find((t) => t[0] === "p" && t[1] === pTag[1]))
          continue;
        this.tags.push(["p", pTag[1]]);
      }
    }
  }
  /**
   * Return a NostrEvent object, trying to fill in missing fields
   * when possible, adding tags when necessary.
   * @param pubkey {string} The pubkey of the user who the event belongs to.
   * @returns {Promise<NostrEvent>} A promise that resolves to a NostrEvent.
   */
  async toNostrEvent(pubkey) {
    if (!pubkey && this.pubkey === "") {
      const user = await this.ndk?.signer?.user();
      this.pubkey = user?.hexpubkey || "";
    }
    if (!this.created_at)
      this.created_at = Math.floor(Date.now() / 1e3);
    const nostrEvent = this.rawEvent();
    const { content, tags } = await this.generateTags();
    nostrEvent.content = content || "";
    nostrEvent.tags = tags;
    try {
      this.id = (0, import_nostr_tools7.getEventHash)(nostrEvent);
    } catch (e) {
    }
    if (this.id)
      nostrEvent.id = this.id;
    if (this.sig)
      nostrEvent.sig = this.sig;
    return nostrEvent;
  }
  isReplaceable = isReplaceable.bind(this);
  isEphemeral = isEphemeral.bind(this);
  isParamReplaceable = isParamReplaceable.bind(this);
  /**
   * Encodes a bech32 id.
   *
   * @returns {string} - Encoded naddr, note or nevent.
   */
  encode = encode.bind(this);
  encrypt = encrypt.bind(this);
  decrypt = decrypt.bind(this);
  /**
   * Get all tags with the given name
   * @param tagName {string} The name of the tag to search for
   * @returns {NDKTag[]} An array of the matching tags
   */
  getMatchingTags(tagName) {
    return this.tags.filter((tag) => tag[0] === tagName);
  }
  /**
   * Get the first tag with the given name
   * @param tagName Tag name to search for
   * @returns The value of the first tag with the given name, or undefined if no such tag exists
   */
  tagValue(tagName) {
    const tags = this.getMatchingTags(tagName);
    if (tags.length === 0)
      return void 0;
    return tags[0][1];
  }
  /**
   * Remove all tags with the given name (e.g. "d", "a", "p")
   * @param tagName Tag name to search for and remove
   * @returns {void}
   */
  removeTag(tagName) {
    this.tags = this.tags.filter((tag) => tag[0] !== tagName);
  }
  /**
   * Sign the event if a signer is present.
   *
   * It will generate tags.
   * Repleacable events will have their created_at field set to the current time.
   * @param signer {NDKSigner} The NDKSigner to use to sign the event
   * @returns {Promise<string>} A Promise that resolves to the signature of the signed event.
   */
  async sign(signer) {
    if (!signer) {
      this.ndk?.assertSigner();
      signer = this.ndk.signer;
    } else {
      this.author = await signer.user();
    }
    await this.generateTags();
    if (this.isReplaceable()) {
      this.created_at = Math.floor(Date.now() / 1e3);
    }
    const nostrEvent = await this.toNostrEvent();
    this.sig = await signer.sign(nostrEvent);
    return this.sig;
  }
  /**
   * Attempt to sign and then publish an NDKEvent to a given relaySet.
   * If no relaySet is provided, the relaySet will be calculated by NDK.
   * @param relaySet {NDKRelaySet} The relaySet to publish the even to.
   * @returns A promise that resolves to the relays the event was published to.
   */
  async publish(relaySet, timeoutMs) {
    if (!this.sig)
      await this.sign();
    if (!this.ndk)
      throw new Error("NDKEvent must be associated with an NDK instance to publish");
    if (!relaySet) {
      relaySet = this.ndk.devWriteRelaySet || calculateRelaySetFromEvent(this.ndk, this);
    }
    return relaySet.publish(this, timeoutMs);
  }
  /**
   * Generates tags for users, notes, and other events tagged in content.
   * Will also generate random "d" tag for parameterized replaceable events where needed.
   * @returns {ContentTag} The tags and content of the event.
   */
  async generateTags() {
    let tags = [];
    const g = await generateContentTags(this.content, this.tags);
    const content = g.content;
    tags = g.tags;
    if (this.kind && this.isParamReplaceable()) {
      const dTag = this.getMatchingTags("d")[0];
      if (!dTag) {
        const str = [...Array(16)].map(() => Math.random().toString(36)[2]).join("");
        tags.push(["d", str]);
      }
    }
    if (this.ndk?.client && !this.tagValue("client")) {
      tags.push(["client", this.ndk.client]);
    }
    return { content: content || "", tags };
  }
  muted() {
    const authorMutedEntry = this.ndk?.mutedIds.get(this.pubkey);
    if (authorMutedEntry && authorMutedEntry === "p")
      return "author";
    const eventTagReference = this.tagReference();
    const eventMutedEntry = this.ndk?.mutedIds.get(eventTagReference[1]);
    if (eventMutedEntry && eventMutedEntry === eventTagReference[0])
      return "event";
    return null;
  }
  /**
   * Returns the "d" tag of a parameterized replaceable event or throws an error if the event isn't
   * a parameterized replaceable event.
   * @returns {string} the "d" tag of the event.
   */
  replaceableDTag() {
    if (this.kind && this.kind >= 3e4 && this.kind <= 4e4) {
      const dTag = this.getMatchingTags("d")[0];
      const dTagId = dTag ? dTag[1] : "";
      return dTagId;
    }
    throw new Error("Event is not a parameterized replaceable event");
  }
  /**
   * Provides a deduplication key for the event.
   *
   * For kinds 0, 3, 10k-20k this will be the event <kind>:<pubkey>
   * For kinds 30k-40k this will be the event <kind>:<pubkey>:<d-tag>
   * For all other kinds this will be the event id
   */
  deduplicationKey() {
    if (this.kind === 0 || this.kind === 3 || this.kind && this.kind >= 1e4 && this.kind < 2e4) {
      return `${this.kind}:${this.pubkey}`;
    } else {
      return this.tagId();
    }
  }
  /**
   * Returns the id of the event or, if it's a parameterized event, the generated id of the event using "d" tag, pubkey, and kind.
   * @returns {string} The id
   */
  tagId() {
    if (this.isParamReplaceable()) {
      return this.tagAddress();
    }
    return this.id;
  }
  /**
   * Returns the "reference" value ("<kind>:<author-pubkey>:<d-tag>") for this replaceable event.
   * @returns {string} The id
   */
  tagAddress() {
    if (!this.isParamReplaceable()) {
      throw new Error("This must only be called on replaceable events");
    }
    const dTagId = this.replaceableDTag();
    return `${this.kind}:${this.pubkey}:${dTagId}`;
  }
  /**
   * Get the tag that can be used to reference this event from another event.
   *
   * Consider using referenceTags() instead (unless you have a good reason to use this)
   *
   * @example
   *     event = new NDKEvent(ndk, { kind: 30000, pubkey: 'pubkey', tags: [ ["d", "d-code"] ] });
   *     event.tagReference(); // ["a", "30000:pubkey:d-code"]
   *
   *     event = new NDKEvent(ndk, { kind: 1, pubkey: 'pubkey', id: "eventid" });
   *     event.tagReference(); // ["e", "eventid"]
   * @returns {NDKTag} The NDKTag object referencing this event
   */
  tagReference(marker) {
    let tag;
    if (this.isParamReplaceable()) {
      tag = ["a", this.tagAddress()];
    } else {
      tag = ["e", this.tagId()];
    }
    if (this.relay) {
      tag.push(this.relay.url);
    } else {
      tag.push("");
    }
    if (marker) {
      tag.push(marker);
    }
    return tag;
  }
  /**
   * Get the tags that can be used to reference this event from another event
   * @param marker The marker to use in the tag
   * @example
   *     event = new NDKEvent(ndk, { kind: 30000, pubkey: 'pubkey', tags: [ ["d", "d-code"] ] });
   *     event.referenceTags(); // [["a", "30000:pubkey:d-code"], ["e", "parent-id"]]
   *
   *     event = new NDKEvent(ndk, { kind: 1, pubkey: 'pubkey', id: "eventid" });
   *     event.referenceTags(); // [["e", "parent-id"]]
   * @returns {NDKTag} The NDKTag object referencing this event
   */
  referenceTags(marker, skipAuthorTag) {
    let tags = [];
    if (this.isParamReplaceable()) {
      tags = [
        ["a", this.tagAddress()],
        ["e", this.id]
      ];
    } else {
      tags = [["e", this.id]];
    }
    if (this.relay?.url) {
      tags = tags.map((tag) => {
        tag.push(this.relay?.url);
        return tag;
      });
    } else if (marker) {
      tags = tags.map((tag) => {
        tag.push("");
        return tag;
      });
    }
    if (marker) {
      tags.forEach((tag) => tag.push(marker));
    }
    if (!skipAuthorTag)
      tags.push(...this.author.referenceTags());
    return tags;
  }
  /**
   * Provides the filter that will return matching events for this event.
   *
   * @example
   *    event = new NDKEvent(ndk, { kind: 30000, pubkey: 'pubkey', tags: [ ["d", "d-code"] ] });
   *    event.filter(); // { "#a": ["30000:pubkey:d-code"] }
   * @example
   *    event = new NDKEvent(ndk, { kind: 1, pubkey: 'pubkey', id: "eventid" });
   *    event.filter(); // { "#e": ["eventid"] }
   *
   * @returns The filter that will return matching events for this event
   */
  filter() {
    if (this.isParamReplaceable()) {
      return { "#a": [this.tagId()] };
    } else {
      return { "#e": [this.tagId()] };
    }
  }
  /**
   * Create a zap request for an existing event
   *
   * @param amount The amount to zap in millisatoshis
   * @param comment A comment to add to the zap request
   * @param extraTags Extra tags to add to the zap request
   * @param recipient The zap recipient (optional for events)
   * @param signer The signer to use (will default to the NDK instance's signer)
   */
  async zap(amount, comment, extraTags, recipient, signer) {
    if (!this.ndk)
      throw new Error("No NDK instance found");
    if (!signer) {
      this.ndk.assertSigner();
    }
    const zap = new Zap({
      ndk: this.ndk,
      zappedEvent: this,
      zappedUser: recipient
    });
    const relays = Array.from(this.ndk.pool.relays.keys());
    const paymentRequest = await zap.createZapRequest(
      amount,
      comment,
      extraTags,
      relays,
      signer
    );
    return paymentRequest;
  }
  /**
   * Generates a deletion event of the current event
   *
   * @param reason The reason for the deletion
   * @returns The deletion event
   */
  async delete(reason) {
    if (!this.ndk)
      throw new Error("No NDK instance found");
    this.ndk.assertSigner();
    const e = new NDKEvent(this.ndk, {
      kind: 5 /* EventDeletion */,
      content: reason || ""
    });
    e.tag(this);
    await e.publish();
    return e;
  }
  /**
   * NIP-18 reposting event.
   *
   * @param publish Whether to publish the reposted event automatically
   * @param signer The signer to use for signing the reposted event
   * @returns The reposted event
   *
   * @function
   */
  repost = repost.bind(this);
  /**
   * React to an existing event
   *
   * @param content The content of the reaction
   */
  async react(content) {
    if (!this.ndk)
      throw new Error("No NDK instance found");
    this.ndk.assertSigner();
    const e = new NDKEvent(this.ndk, {
      kind: 7 /* Reaction */,
      content
    });
    e.tag(this);
    await e.publish();
    return e;
  }
};

// src/events/kinds/NDKRelayList.ts
var READ_MARKER = "read";
var WRITE_MARKER = "write";
var NDKRelayList = class extends NDKEvent {
  constructor(ndk, rawEvent) {
    super(ndk, rawEvent);
    this.kind ??= 10002 /* RelayList */;
  }
  static from(ndkEvent) {
    return new NDKRelayList(ndkEvent.ndk, ndkEvent.rawEvent());
  }
  get readRelayUrls() {
    return this.getMatchingTags("r").filter((tag) => !tag[2] || tag[2] && tag[2] === READ_MARKER).map((tag) => tag[1]);
  }
  set readRelayUrls(relays) {
    for (const relay of relays) {
      this.tags.push(["r", relay, READ_MARKER]);
    }
  }
  get writeRelayUrls() {
    return this.getMatchingTags("r").filter((tag) => !tag[2] || tag[2] && tag[2] === WRITE_MARKER).map((tag) => tag[1]);
  }
  set writeRelayUrls(relays) {
    for (const relay of relays) {
      this.tags.push(["r", relay, WRITE_MARKER]);
    }
  }
  get bothRelayUrls() {
    return this.getMatchingTags("r").filter((tag) => !tag[2]).map((tag) => tag[1]);
  }
  set bothRelayUrls(relays) {
    for (const relay of relays) {
      this.tags.push(["r", relay]);
    }
  }
  get relays() {
    return this.getMatchingTags("r").map((tag) => tag[1]);
  }
};

// src/subscription/index.ts
var import_tseep5 = require("tseep");
var NDKSubscriptionCacheUsage = /* @__PURE__ */ ((NDKSubscriptionCacheUsage2) => {
  NDKSubscriptionCacheUsage2["ONLY_CACHE"] = "ONLY_CACHE";
  NDKSubscriptionCacheUsage2["CACHE_FIRST"] = "CACHE_FIRST";
  NDKSubscriptionCacheUsage2["PARALLEL"] = "PARALLEL";
  NDKSubscriptionCacheUsage2["ONLY_RELAY"] = "ONLY_RELAY";
  return NDKSubscriptionCacheUsage2;
})(NDKSubscriptionCacheUsage || {});
var defaultOpts = {
  closeOnEose: false,
  cacheUsage: "CACHE_FIRST" /* CACHE_FIRST */,
  groupable: true,
  groupableDelay: 100
};
var NDKSubscription = class extends import_tseep5.EventEmitter {
  subId;
  filters;
  opts;
  pool;
  /**
   * Tracks the filters as they are executed on each relay
   */
  relayFilters;
  relaySet;
  ndk;
  debug;
  eoseDebug;
  /**
   * Events that have been seen by the subscription, with the time they were first seen.
   */
  eventFirstSeen = /* @__PURE__ */ new Map();
  /**
   * Relays that have sent an EOSE.
   */
  eosesSeen = /* @__PURE__ */ new Set();
  /**
   * Events that have been seen by the subscription per relay.
   */
  eventsPerRelay = /* @__PURE__ */ new Map();
  /**
   * The time the last event was received by the subscription.
   * This is used to calculate when EOSE should be emitted.
   */
  lastEventReceivedAt;
  internalId;
  constructor(ndk, filters, opts, relaySet, subId) {
    super();
    this.ndk = ndk;
    this.pool = opts?.pool || ndk.pool;
    this.opts = { ...defaultOpts, ...opts || {} };
    this.filters = filters instanceof Array ? filters : [filters];
    this.subId = subId || opts?.subId;
    this.internalId = Math.random().toString(36).substring(7);
    this.relaySet = relaySet;
    this.debug = ndk.debug.extend(`subscription[${opts?.subId ?? this.internalId}]`);
    5;
    this.eoseDebug = this.debug.extend("eose");
    if (!this.opts.closeOnEose) {
      this.debug(
        `Creating a permanent subscription`,
        this.opts,
        JSON.stringify(this.filters)
      );
    }
    if (this.opts.cacheUsage === "ONLY_CACHE" /* ONLY_CACHE */ && !this.opts.closeOnEose) {
      throw new Error("Cannot use cache-only options with a persistent subscription");
    }
  }
  /**
   * Provides access to the first filter of the subscription for
   * backwards compatibility.
   */
  get filter() {
    return this.filters[0];
  }
  isGroupable() {
    return this.opts?.groupable || false;
  }
  shouldQueryCache() {
    return this.opts?.cacheUsage !== "ONLY_RELAY" /* ONLY_RELAY */;
  }
  shouldQueryRelays() {
    return this.opts?.cacheUsage !== "ONLY_CACHE" /* ONLY_CACHE */;
  }
  shouldWaitForCache() {
    return (
      // Must want to close on EOSE; subscriptions
      // that want to receive further updates must
      // always hit the relay
      this.opts.closeOnEose && // Cache adapter must claim to be fast
      !!this.ndk.cacheAdapter?.locking && // If explicitly told to run in parallel, then
      // we should not wait for the cache
      this.opts.cacheUsage !== "PARALLEL" /* PARALLEL */
    );
  }
  /**
   * Start the subscription. This is the main method that should be called
   * after creating a subscription.
   */
  async start() {
    let cachePromise;
    if (this.shouldQueryCache()) {
      cachePromise = this.startWithCache();
      if (this.shouldWaitForCache()) {
        await cachePromise;
        if (queryFullyFilled(this)) {
          this.emit("eose", this);
          return;
        }
      }
    }
    if (this.shouldQueryRelays()) {
      this.startWithRelays();
    } else {
      this.emit("eose", this);
    }
    return;
  }
  stop() {
    this.emit("close", this);
  }
  /**
   * @returns Whether the subscription has an authors filter.
   */
  hasAuthorsFilter() {
    return this.filters.some((f) => f.authors?.length);
  }
  async startWithCache() {
    if (this.ndk.cacheAdapter?.query) {
      const promise = this.ndk.cacheAdapter.query(this);
      if (this.ndk.cacheAdapter.locking) {
        await promise;
      }
    }
  }
  /**
   * Send REQ to relays
   */
  startWithRelays() {
    if (!this.relaySet) {
      this.relayFilters = calculateRelaySetsFromFilters(this.ndk, this.filters);
    } else {
      this.relayFilters = /* @__PURE__ */ new Map();
      for (const relay of this.relaySet.relays) {
        this.relayFilters.set(relay.url, this.filters);
      }
    }
    for (const [relayUrl, filters] of this.relayFilters) {
      const relay = this.pool.getRelay(relayUrl);
      relay.subscribe(this, filters);
    }
  }
  // EVENT handling
  /**
   * Called when an event is received from a relay or the cache
   * @param event
   * @param relay
   * @param fromCache Whether the event was received from the cache
   */
  eventReceived(event, relay, fromCache = false) {
    if (relay)
      event.relay = relay;
    if (!relay)
      relay = event.relay;
    if (!fromCache && relay) {
      let events = this.eventsPerRelay.get(relay);
      if (!events) {
        events = /* @__PURE__ */ new Set();
        this.eventsPerRelay.set(relay, events);
      }
      events.add(event.id);
      const eventAlreadySeen = this.eventFirstSeen.has(event.id);
      if (eventAlreadySeen) {
        const timeSinceFirstSeen = Date.now() - (this.eventFirstSeen.get(event.id) || 0);
        relay.scoreSlowerEvent(timeSinceFirstSeen);
        this.emit("event:dup", event, relay, timeSinceFirstSeen, this);
        return;
      }
      if (this.ndk.cacheAdapter) {
        this.ndk.cacheAdapter.setEvent(event, this.filters[0], relay);
      }
      this.eventFirstSeen.set(event.id, Date.now());
    } else {
      this.eventFirstSeen.set(event.id, 0);
    }
    if (!event.ndk)
      event.ndk = this.ndk;
    this.emit("event", event, relay, this);
    this.lastEventReceivedAt = Date.now();
  }
  // EOSE handling
  eoseTimeout;
  eoseReceived(relay) {
    this.eosesSeen.add(relay);
    this.eoseDebug(`received from ${relay.url}`);
    let lastEventSeen = this.lastEventReceivedAt ? Date.now() - this.lastEventReceivedAt : void 0;
    const hasSeenAllEoses = this.eosesSeen.size === this.relayFilters?.size;
    const queryFilled = queryFullyFilled(this);
    if (queryFilled) {
      this.emit("eose");
      this.eoseDebug(`Query fully filled`);
      if (this.opts?.closeOnEose) {
        this.stop();
      } else {
      }
    } else if (hasSeenAllEoses) {
      this.emit("eose");
      this.eoseDebug(`All EOSEs seen`);
      if (this.opts?.closeOnEose) {
        this.stop();
      } else {
      }
    } else {
      let timeToWaitForNextEose = 1e3;
      const percentageOfRelaysThatHaveSentEose = this.eosesSeen.size / this.relayFilters.size;
      if (this.eosesSeen.size >= 2 && percentageOfRelaysThatHaveSentEose >= 0.5) {
        timeToWaitForNextEose = timeToWaitForNextEose * (1 - percentageOfRelaysThatHaveSentEose);
        if (this.eoseTimeout) {
          clearTimeout(this.eoseTimeout);
        }
        const sendEoseTimeout = () => {
          lastEventSeen = this.lastEventReceivedAt ? Date.now() - this.lastEventReceivedAt : void 0;
          if (lastEventSeen !== void 0 && lastEventSeen < 20) {
            this.eoseTimeout = setTimeout(sendEoseTimeout, timeToWaitForNextEose);
          } else {
            this.emit("eose");
            if (this.opts?.closeOnEose)
              this.stop();
          }
        };
        this.eoseTimeout = setTimeout(sendEoseTimeout, timeToWaitForNextEose);
      }
    }
  }
};

// src/user/follows.ts
async function follows(opts, outbox) {
  if (!this.ndk)
    throw new Error("NDK not set");
  const contactListEvent = Array.from(
    await this.ndk.fetchEvents(
      {
        kinds: [3],
        authors: [this.pubkey]
      },
      opts || { groupable: false }
    )
  )[0];
  if (contactListEvent) {
    const pubkeys = /* @__PURE__ */ new Set();
    contactListEvent.tags.forEach((tag) => {
      if (tag[0] === "p") {
        try {
          pubkeys.add(tag[1]);
          if (outbox) {
            this.ndk?.outboxTracker?.trackUsers([tag[1]]);
          }
        } catch (e) {
        }
      }
    });
    return [...pubkeys].reduce((acc, pubkey) => {
      const user = new NDKUser({ pubkey });
      user.ndk = this.ndk;
      acc.add(user);
      return acc;
    }, /* @__PURE__ */ new Set());
  }
  return /* @__PURE__ */ new Set();
}

// src/user/profile.ts
function profileFromEvent(event) {
  const profile = {};
  const payload = JSON.parse(event.content);
  Object.keys(payload).forEach((key) => {
    switch (key) {
      case "name":
        profile.name = payload.name;
        break;
      case "display_name":
        profile.displayName = payload.display_name;
        break;
      case "image":
      case "picture":
        profile.image = payload.image || payload.picture;
        break;
      case "banner":
        profile.banner = payload.banner;
        break;
      case "bio":
        profile.bio = payload.bio;
        break;
      case "nip05":
        profile.nip05 = payload.nip05;
        break;
      case "lud06":
        profile.lud06 = payload.lud06;
        break;
      case "lud16":
        profile.lud16 = payload.lud16;
        break;
      case "about":
        profile.about = payload.about;
        break;
      case "zapService":
        profile.zapService = payload.zapService;
        break;
      case "website":
        profile.website = payload.website;
        break;
      default:
        profile[key] = payload[key];
        break;
    }
  });
  return profile;
}
function serializeProfile(profile) {
  const payload = {};
  for (const [key, val] of Object.entries(profile)) {
    switch (key) {
      case "username":
      case "name":
        payload.name = val;
        break;
      case "displayName":
        payload.display_name = val;
        break;
      case "image":
      case "picture":
        payload.picture = val;
        break;
      case "bio":
      case "about":
        payload.about = val;
        break;
      default:
        payload[key] = val;
        break;
    }
  }
  return JSON.stringify(payload);
}

// src/events/kinds/lists/index.ts
var NDKList = class extends NDKEvent {
  _encryptedTags;
  /**
   * Stores the number of bytes the content was before decryption
   * to expire the cache when the content changes.
   */
  encryptedTagsLength;
  constructor(ndk, rawEvent) {
    super(ndk, rawEvent);
    this.kind ??= 30001 /* CategorizedBookmarkList */;
  }
  /**
   * Wrap a NDKEvent into a NDKList
   */
  static from(ndkEvent) {
    return new NDKList(ndkEvent.ndk, ndkEvent.rawEvent());
  }
  /**
   * Returns the title of the list. Falls back on fetching the name tag value.
   */
  get title() {
    const titleTag = this.tagValue("title") || this.tagValue("name");
    if (this.kind === 3 /* Contacts */ && !titleTag) {
      return "Contacts";
    } else if (this.kind === 1e4 /* MuteList */ && !titleTag) {
      return "Mute";
    } else if (this.kind === 10001 /* PinList */ && !titleTag) {
      return "Pinned Notes";
    } else if (this.kind === 10002 /* RelayList */ && !titleTag) {
      return "Relay Metadata";
    } else if (this.kind === 10003 /* BookmarkList */ && !titleTag) {
      return "Bookmarks";
    } else if (this.kind === 10004 /* CommunityList */ && !titleTag) {
      return "Communities";
    } else if (this.kind === 10005 /* PublicChatList */ && !titleTag) {
      return "Public Chats";
    } else if (this.kind === 10006 /* BlockRelayList */ && !titleTag) {
      return "Blocked Relays";
    } else if (this.kind === 10007 /* SearchRelayList */ && !titleTag) {
      return "Search Relays";
    } else if (this.kind === 10015 /* InterestList */ && !titleTag) {
      return "Interests";
    } else if (this.kind === 10030 /* EmojiList */ && !titleTag) {
      return "Emojis";
    } else {
      return titleTag ?? this.tagValue("d");
    }
  }
  /**
   * Sets the title of the list.
   */
  set title(title) {
    this.removeTag("title");
    this.removeTag("name");
    if (title) {
      this.tags.push(["title", title]);
    } else {
      throw new Error("Title cannot be empty");
    }
  }
  /**
   * Returns the name of the list.
   * @deprecated Please use "title" instead.
   */
  get name() {
    const nameTag = this.tagValue("name");
    if (this.kind === 3 /* Contacts */ && !nameTag) {
      return "Contacts";
    } else if (this.kind === 1e4 /* MuteList */ && !nameTag) {
      return "Mute";
    } else if (this.kind === 10001 /* PinList */ && !nameTag) {
      return "Pinned Notes";
    } else if (this.kind === 10002 /* RelayList */ && !nameTag) {
      return "Relay Metadata";
    } else if (this.kind === 10003 /* BookmarkList */ && !nameTag) {
      return "Bookmarks";
    } else if (this.kind === 10004 /* CommunityList */ && !nameTag) {
      return "Communities";
    } else if (this.kind === 10005 /* PublicChatList */ && !nameTag) {
      return "Public Chats";
    } else if (this.kind === 10006 /* BlockRelayList */ && !nameTag) {
      return "Blocked Relays";
    } else if (this.kind === 10007 /* SearchRelayList */ && !nameTag) {
      return "Search Relays";
    } else if (this.kind === 10015 /* InterestList */ && !nameTag) {
      return "Interests";
    } else if (this.kind === 10030 /* EmojiList */ && !nameTag) {
      return "Emojis";
    } else {
      return nameTag ?? this.tagValue("d");
    }
  }
  /**
   * Sets the name of the list.
   * @deprecated Please use "title" instead. This method will use the `title` tag instead.
   */
  set name(name) {
    this.removeTag("name");
    if (name) {
      this.tags.push(["title", name]);
    } else {
      throw new Error("Name cannot be empty");
    }
  }
  /**
   * Returns the description of the list.
   */
  get description() {
    return this.tagValue("description");
  }
  /**
   * Sets the description of the list.
   */
  set description(name) {
    if (name) {
      this.tags.push(["description", name]);
    } else {
      this.removeTag("description");
    }
  }
  isEncryptedTagsCacheValid() {
    return !!(this._encryptedTags && this.encryptedTagsLength === this.content.length);
  }
  /**
   * Returns the decrypted content of the list.
   */
  async encryptedTags(useCache = true) {
    if (useCache && this.isEncryptedTagsCacheValid())
      return this._encryptedTags;
    if (!this.ndk)
      throw new Error("NDK instance not set");
    if (!this.ndk.signer)
      throw new Error("NDK signer not set");
    const user = await this.ndk.signer.user();
    try {
      if (this.content.length > 0) {
        try {
          const decryptedContent = await this.ndk.signer.decrypt(user, this.content);
          const a = JSON.parse(decryptedContent);
          if (a && a[0]) {
            this.encryptedTagsLength = this.content.length;
            return this._encryptedTags = a;
          }
          this.encryptedTagsLength = this.content.length;
          return this._encryptedTags = [];
        } catch (e) {
          console.log(`error decrypting ${this.content}`);
        }
      }
    } catch (e) {
    }
    return [];
  }
  /**
   * This method can be overriden to validate that a tag is valid for this list.
   *
   * (i.e. the NDKPersonList can validate that items are NDKUser instances)
   */
  validateTag(tagValue) {
    return true;
  }
  /**
   * Returns the unecrypted items in this list.
   */
  get items() {
    return this.tags.filter((t) => {
      return ![
        "d",
        "L",
        "l",
        "title",
        "name",
        "description",
        "summary",
        "image",
        "thumb",
        "alt",
        "expiration",
        "subject"
      ].includes(t[0]);
    });
  }
  /**
   * Adds a new item to the list.
   * @param relay Relay to add
   * @param mark Optional mark to add to the item
   * @param encrypted Whether to encrypt the item
   */
  async addItem(item, mark = void 0, encrypted = false) {
    if (!this.ndk)
      throw new Error("NDK instance not set");
    if (!this.ndk.signer)
      throw new Error("NDK signer not set");
    let tags;
    if (item instanceof NDKEvent) {
      tags = item.referenceTags();
    } else if (item instanceof NDKUser) {
      tags = item.referenceTags();
    } else if (item instanceof NDKRelay) {
      tags = item.referenceTags();
    } else if (Array.isArray(item)) {
      tags = [item];
    } else {
      throw new Error("Invalid object type");
    }
    if (mark)
      tags[0].push(mark);
    if (encrypted) {
      const user = await this.ndk.signer.user();
      const currentList = await this.encryptedTags();
      currentList.push(...tags);
      this._encryptedTags = currentList;
      this.encryptedTagsLength = this.content.length;
      this.content = JSON.stringify(currentList);
      await this.encrypt(user);
    } else {
      this.tags.push(...tags);
    }
    this.created_at = Math.floor(Date.now() / 1e3);
    this.emit("change");
  }
  /**
   * Removes an item from the list.
   *
   * @param index The index of the item to remove.
   * @param encrypted Whether to remove from the encrypted list or not.
   */
  async removeItem(index, encrypted) {
    if (!this.ndk)
      throw new Error("NDK instance not set");
    if (!this.ndk.signer)
      throw new Error("NDK signer not set");
    if (encrypted) {
      const user = await this.ndk.signer.user();
      const currentList = await this.encryptedTags();
      currentList.splice(index, 1);
      this._encryptedTags = currentList;
      this.encryptedTagsLength = this.content.length;
      this.content = JSON.stringify(currentList);
      await this.encrypt(user);
    } else {
      this.tags.splice(index, 1);
    }
    this.created_at = Math.floor(Date.now() / 1e3);
    this.emit("change");
    return this;
  }
};
var lists_default = NDKList;

// src/user/pin.ts
async function pin(event, pinEvent, publish) {
  const kind = 10001 /* PinList */;
  if (!this.ndk)
    throw new Error("No NDK instance found");
  this.ndk.assertSigner();
  if (!pinEvent) {
    const events = await this.ndk.fetchEvents(
      { kinds: [kind], authors: [this.pubkey] },
      { cacheUsage: "ONLY_RELAY" /* ONLY_RELAY */ }
    );
    if (events.size > 0) {
      pinEvent = lists_default.from(Array.from(events)[0]);
    } else {
      pinEvent = new NDKEvent(this.ndk, {
        kind
      });
    }
  }
  pinEvent.tag(event);
  if (publish) {
    await pinEvent.publish();
  }
  return pinEvent;
}

// src/user/index.ts
var NDKUser = class {
  ndk;
  profile;
  _npub;
  _pubkey;
  relayUrls = [];
  constructor(opts) {
    if (opts.npub)
      this._npub = opts.npub;
    if (opts.hexpubkey)
      this._pubkey = opts.hexpubkey;
    if (opts.pubkey)
      this._pubkey = opts.pubkey;
    if (opts.relayUrls) {
      this.relayUrls = opts.relayUrls;
    }
  }
  get npub() {
    if (!this._npub) {
      if (!this._pubkey)
        throw new Error("hexpubkey not set");
      this._npub = import_nostr_tools8.nip19.npubEncode(this.pubkey);
    }
    return this._npub;
  }
  set npub(npub) {
    this._npub = npub;
  }
  /**
   * Get the user's hexpubkey
   * @returns {Hexpubkey} The user's hexpubkey
   *
   * @deprecated Use `pubkey` instead
   */
  get hexpubkey() {
    return this.pubkey;
  }
  /**
   * Set the user's hexpubkey
   * @param pubkey {Hexpubkey} The user's hexpubkey
   * @deprecated Use `pubkey` instead
   */
  set hexpubkey(pubkey) {
    this._pubkey = pubkey;
  }
  /**
   * Get the user's pubkey
   * @returns {string} The user's pubkey
   */
  get pubkey() {
    if (!this._pubkey) {
      if (!this._npub)
        throw new Error("npub not set");
      this._pubkey = import_nostr_tools8.nip19.decode(this.npub).data;
    }
    return this._pubkey;
  }
  /**
   * Set the user's pubkey
   * @param pubkey {string} The user's pubkey
   */
  set pubkey(pubkey) {
    this._pubkey = pubkey;
  }
  /**
   * Instantiate an NDKUser from a NIP-05 string
   * @param nip05Id {string} The user's NIP-05
   * @returns {NDKUser | undefined} An NDKUser if one is found for the given NIP-05, undefined otherwise.
   */
  static async fromNip05(nip05Id) {
    const profile = await import_nostr_tools8.nip05.queryProfile(nip05Id);
    if (profile) {
      return new NDKUser({
        pubkey: profile.pubkey,
        relayUrls: profile.relays
      });
    }
  }
  /**
   * Fetch a user's profile
   * @param opts {NDKSubscriptionOptions} A set of NDKSubscriptionOptions
   * @returns User Profile
   */
  async fetchProfile(opts) {
    if (!this.ndk)
      throw new Error("NDK not set");
    if (!this.profile)
      this.profile = {};
    let setMetadataEvents = null;
    if (this.ndk.cacheAdapter && this.ndk.cacheAdapter.fetchProfile && opts?.cacheUsage !== "ONLY_RELAY" /* ONLY_RELAY */) {
      const profile = await this.ndk.cacheAdapter.fetchProfile(this.pubkey);
      if (profile) {
        this.profile = profile;
        return profile;
      }
    }
    if (!opts && // if no options have been set
    this.ndk.cacheAdapter && // and we have a cache
    this.ndk.cacheAdapter.locking) {
      setMetadataEvents = await this.ndk.fetchEvents(
        {
          kinds: [0],
          authors: [this.pubkey]
        },
        {
          cacheUsage: "ONLY_CACHE" /* ONLY_CACHE */,
          closeOnEose: true,
          groupable: false
        }
      );
      opts = {
        cacheUsage: "ONLY_RELAY" /* ONLY_RELAY */,
        closeOnEose: true,
        groupable: true,
        groupableDelay: 250
      };
    }
    if (!setMetadataEvents || setMetadataEvents.size === 0) {
      setMetadataEvents = await this.ndk.fetchEvents(
        {
          kinds: [0],
          authors: [this.pubkey]
        },
        opts
      );
    }
    const sortedSetMetadataEvents = Array.from(setMetadataEvents).sort(
      (a, b) => a.created_at - b.created_at
    );
    if (sortedSetMetadataEvents.length === 0)
      return null;
    this.profile = profileFromEvent(sortedSetMetadataEvents[0]);
    if (this.profile && this.ndk.cacheAdapter && this.ndk.cacheAdapter.saveProfile) {
      this.ndk.cacheAdapter.saveProfile(this.pubkey, this.profile);
    }
    return this.profile;
  }
  /**
   * Returns a set of users that this user follows.
   */
  follows = follows.bind(this);
  /**
   * Pins a user or an event
   */
  pin = pin.bind(this);
  /**
   * Returns a set of relay list events for a user.
   * @returns {Promise<Set<NDKEvent>>} A set of NDKEvents returned for the given user.
   */
  async relayList() {
    if (!this.ndk)
      throw new Error("NDK not set");
    const pool = this.ndk.outboxPool || this.ndk.pool;
    const set = /* @__PURE__ */ new Set();
    for (const relay of pool.relays.values())
      set.add(relay);
    const relaySet = new NDKRelaySet(set, this.ndk);
    const event = await this.ndk.fetchEvent(
      {
        kinds: [10002],
        authors: [this.pubkey]
      },
      {
        closeOnEose: true,
        pool,
        groupable: true,
        subId: `relay-list-${this.pubkey.slice(0, 6)}`
      },
      relaySet
    );
    if (event)
      return NDKRelayList.from(event);
    return await this.relayListFromKind3();
  }
  async relayListFromKind3() {
    if (!this.ndk)
      throw new Error("NDK not set");
    const followList = await this.ndk.fetchEvent({
      kinds: [3],
      authors: [this.pubkey]
    });
    if (followList) {
      try {
        const content = JSON.parse(followList.content);
        const relayList = new NDKRelayList(this.ndk);
        const readRelays = /* @__PURE__ */ new Set();
        const writeRelays = /* @__PURE__ */ new Set();
        for (const [key, config] of Object.entries(content)) {
          if (!config) {
            readRelays.add(key);
            writeRelays.add(key);
          } else {
            const relayConfig = config;
            if (relayConfig.write)
              writeRelays.add(key);
            if (relayConfig.read)
              readRelays.add(key);
          }
        }
        relayList.readRelayUrls = Array.from(readRelays);
        relayList.writeRelayUrls = Array.from(writeRelays);
        return relayList;
      } catch (e) {
      }
    }
    return void 0;
  }
  /** @deprecated Use referenceTags instead. */
  /**
   * Get the tag that can be used to reference this user in an event
   * @returns {NDKTag} an NDKTag
   */
  tagReference() {
    return ["p", this.pubkey];
  }
  /**
   * Get the tags that can be used to reference this user in an event
   * @returns {NDKTag[]} an array of NDKTag
   */
  referenceTags(marker) {
    const tag = [["p", this.pubkey]];
    if (!marker)
      return tag;
    tag[0].push("", marker);
    return tag;
  }
  /**
   * Publishes the current profile.
   */
  async publish() {
    if (!this.ndk)
      throw new Error("No NDK instance found");
    if (!this.profile)
      throw new Error("No profile available");
    this.ndk.assertSigner();
    const event = new NDKEvent(this.ndk, {
      kind: 0,
      content: serializeProfile(this.profile)
    });
    await event.publish();
  }
  /**
   * Add a follow to this user's contact list
   *
   * @param newFollow {NDKUser} The user to follow
   * @param currentFollowList {Set<NDKUser>} The current follow list
   * @returns {Promise<boolean>} True if the follow was added, false if the follow already exists
   */
  async follow(newFollow, currentFollowList) {
    if (!this.ndk)
      throw new Error("No NDK instance found");
    this.ndk.assertSigner();
    if (!currentFollowList) {
      currentFollowList = await this.follows();
    }
    if (currentFollowList.has(newFollow)) {
      return false;
    }
    currentFollowList.add(newFollow);
    const event = new NDKEvent(this.ndk, {
      kind: 3 /* Contacts */
    });
    for (const follow of currentFollowList) {
      event.tag(follow);
    }
    await event.publish();
    return true;
  }
  /**
   * Validate a user's NIP-05 identifier (usually fetched from their kind:0 profile data)
   *
   * @param nip05Id The NIP-05 string to validate
   * @returns {Promise<boolean | null>} True if the NIP-05 is found and matches this user's pubkey,
   * False if the NIP-05 is found but doesn't match this user's pubkey,
   * null if the NIP-05 isn't found on the domain or we're unable to verify (because of network issues, etc.)
   */
  async validateNip05(nip05Id) {
    if (!this.ndk)
      throw new Error("No NDK instance found");
    const profilePointer = await import_nostr_tools8.nip05.queryProfile(nip05Id);
    if (profilePointer === null)
      return null;
    return profilePointer.pubkey === this.pubkey;
  }
  /**
   * Zap a user
   *
   * @param amount The amount to zap in millisatoshis
   * @param comment A comment to add to the zap request
   * @param extraTags Extra tags to add to the zap request
   * @param signer The signer to use (will default to the NDK instance's signer)
   */
  async zap(amount, comment, extraTags, signer) {
    if (!this.ndk)
      throw new Error("No NDK instance found");
    if (!signer) {
      this.ndk.assertSigner();
    }
    const zap = new Zap({
      ndk: this.ndk,
      zappedUser: this
    });
    const relays = Array.from(this.ndk.pool.relays.keys());
    const paymentRequest = await zap.createZapRequest(
      amount,
      comment,
      extraTags,
      relays,
      signer
    );
    return paymentRequest;
  }
};

// src/events/kinds/article.ts
var NDKArticle = class extends NDKEvent {
  constructor(ndk, rawEvent) {
    super(ndk, rawEvent);
    this.kind = 30023 /* Article */;
  }
  /**
   * Creates a NDKArticle from an existing NDKEvent.
   *
   * @param event NDKEvent to create the NDKArticle from.
   * @returns NDKArticle
   */
  static from(event) {
    return new NDKArticle(event.ndk, event.rawEvent());
  }
  /**
   * Getter for the article title.
   *
   * @returns {string | undefined} - The article title if available, otherwise undefined.
   */
  get title() {
    return this.tagValue("title");
  }
  /**
   * Setter for the article title.
   *
   * @param {string | undefined} title - The title to set for the article.
   */
  set title(title) {
    this.removeTag("title");
    if (title)
      this.tags.push(["title", title]);
  }
  /**
   * Getter for the article image.
   *
   * @returns {string | undefined} - The article image if available, otherwise undefined.
   */
  get image() {
    return this.tagValue("image");
  }
  /**
   * Setter for the article image.
   *
   * @param {string | undefined} image - The image to set for the article.
   */
  set image(image) {
    this.removeTag("image");
    if (image)
      this.tags.push(["image", image]);
  }
  get summary() {
    return this.tagValue("summary");
  }
  /**
   * Getter for the article's publication timestamp.
   *
   * @returns {number | undefined} - The Unix timestamp of when the article was published or undefined.
   */
  get published_at() {
    const tag = this.tagValue("published_at");
    if (tag) {
      return parseInt(tag);
    }
    return void 0;
  }
  /**
   * Setter for the article's publication timestamp.
   *
   * @param {number | undefined} timestamp - The Unix timestamp to set for the article's publication date.
   */
  set published_at(timestamp) {
    this.removeTag("published_at");
    if (timestamp !== void 0) {
      this.tags.push(["published_at", timestamp.toString()]);
    }
  }
  /**
   * Generates content tags for the article.
   *
   * This method first checks and sets the publication date if not available,
   * and then generates content tags based on the base NDKEvent class.
   *
   * @returns {ContentTag} - The generated content tags.
   */
  async generateTags() {
    super.generateTags();
    if (!this.published_at) {
      this.published_at = this.created_at;
    }
    return super.generateTags();
  }
  /**
   * Getter for the article's URL.
   *
   * @returns {string | undefined} - The article's URL if available, otherwise undefined.
   */
  get url() {
    return this.tagValue("url");
  }
  /**
   * Setter for the article's URL.
   *
   * @param {string | undefined} url - The URL to set for the article.
   */
  set url(url) {
    if (url) {
      this.tags.push(["url", url]);
    } else {
      this.removeTag("url");
    }
  }
};

// src/events/kinds/highlight.ts
var import_nostr_tools9 = require("nostr-tools");
var NDKHighlight = class extends NDKEvent {
  _article;
  constructor(ndk, rawEvent) {
    super(ndk, rawEvent);
    this.kind ??= 9802 /* Highlight */;
  }
  static from(event) {
    return new NDKHighlight(event.ndk, event.rawEvent());
  }
  get url() {
    return this.tagValue("r");
  }
  /**
   * Context tag.
   */
  set context(context) {
    if (context === void 0) {
      this.tags = this.tags.filter(([tag, value]) => tag !== "context");
    } else {
      this.tags = this.tags.filter(([tag, value]) => tag !== "context");
      this.tags.push(["context", context]);
    }
  }
  get context() {
    return this.tags.find(([tag, value]) => tag === "context")?.[1] ?? void 0;
  }
  /**
   * Will return the article URL or NDKEvent if they have already been
   * set (it won't attempt to load remote events)
   */
  get article() {
    return this._article;
  }
  /**
   * Article the highlight is coming from.
   *
   * @param article Article URL or NDKEvent.
   */
  set article(article) {
    this._article = article;
    if (typeof article === "string") {
      this.tags.push(["r", article]);
    } else {
      this.tag(article);
    }
  }
  getArticleTag() {
    return this.getMatchingTags("a")[0] || this.getMatchingTags("e")[0] || this.getMatchingTags("r")[0];
  }
  async getArticle() {
    if (this._article !== void 0)
      return this._article;
    let taggedBech32;
    const articleTag = this.getArticleTag();
    if (!articleTag)
      return void 0;
    switch (articleTag[0]) {
      case "a":
        const [kind, pubkey, identifier] = articleTag[1].split(":");
        taggedBech32 = import_nostr_tools9.nip19.naddrEncode({ kind: parseInt(kind), pubkey, identifier });
        break;
      case "e":
        taggedBech32 = import_nostr_tools9.nip19.noteEncode(articleTag[1]);
        break;
      case "r":
        this._article = articleTag[1];
        break;
    }
    if (taggedBech32) {
      let a = await this.ndk?.fetchEvent(taggedBech32);
      if (a) {
        if (a.kind === 30023 /* Article */) {
          a = NDKArticle.from(a);
        }
        this._article = a;
      }
    }
    return this._article;
  }
};

// src/events/kinds/dvm/NDKDVMJobFeedback.ts
var NDKDvmJobFeedbackStatus = /* @__PURE__ */ ((NDKDvmJobFeedbackStatus2) => {
  NDKDvmJobFeedbackStatus2["Processing"] = "processing";
  NDKDvmJobFeedbackStatus2["Success"] = "success";
  NDKDvmJobFeedbackStatus2["Scheduled"] = "scheduled";
  NDKDvmJobFeedbackStatus2["PayReq"] = "payment_required";
  return NDKDvmJobFeedbackStatus2;
})(NDKDvmJobFeedbackStatus || {});
var NDKDVMJobFeedback = class extends NDKEvent {
  constructor(ndk, event) {
    super(ndk, event);
    this.kind ??= 7e3 /* DVMJobFeedback */;
  }
  static from(event) {
    return new NDKDVMJobFeedback(event.ndk, event.rawEvent());
  }
  get status() {
    return this.tagValue("status");
  }
  set status(status) {
    this.removeTag("status");
    if (status !== void 0) {
      this.tags.push(["status", status]);
    }
  }
};

// src/events/kinds/dvm/NDKDVMJobResult.ts
var NDKDVMJobResult = class extends NDKEvent {
  constructor(ndk, event) {
    super(ndk, event);
  }
  static from(event) {
    return new NDKDVMJobResult(event.ndk, event.rawEvent());
  }
  setAmount(msat, invoice) {
    this.removeTag("amount");
    const tag = ["amount", msat.toString()];
    if (invoice)
      tag.push(invoice);
    this.tags.push(tag);
  }
  set result(result) {
    if (result === void 0) {
      this.content = "";
    } else {
      this.content = result;
    }
  }
  get result() {
    if (this.content === "") {
      return void 0;
    }
    return this.content;
  }
  set status(status) {
    this.removeTag("status");
    if (status !== void 0) {
      this.tags.push(["status", status]);
    }
  }
  get status() {
    return this.tagValue("status");
  }
  get jobRequestId() {
    for (const eTag of this.getMatchingTags("e")) {
      if (eTag[2] === "job")
        return eTag[1];
    }
    if (this.jobRequest)
      return this.jobRequest.id;
    return this.tagValue("e");
  }
  set jobRequest(event) {
    this.removeTag("request");
    if (event) {
      this.kind = event.kind + 1e3;
      this.tags.push(["request", JSON.stringify(event.rawEvent())]);
      this.tag(event);
    }
  }
  get jobRequest() {
    const tag = this.tagValue("request");
    if (tag === void 0) {
      return void 0;
    }
    return new NDKEvent(this.ndk, JSON.parse(tag));
  }
};

// src/events/kinds/dvm/NDKDVMRequest.ts
var NDKDVMRequest = class extends NDKEvent {
  constructor(ndk, event) {
    super(ndk, event);
    if (ndk) {
      this.tags.push(["relays", ...ndk.pool.urls()]);
    }
  }
  static from(event) {
    return new NDKDVMRequest(event.ndk, event.rawEvent());
  }
  /**
   * Create a new job feedback for this request
   */
  createFeedback(status) {
    const feedback = new NDKDVMJobFeedback(this.ndk);
    feedback.status = status;
    feedback.tag(this, "job");
    return feedback;
  }
  /**
   * Create a new result event for this request
   */
  createResult(data) {
    const event = new NDKDVMJobResult(this.ndk, data);
    event.jobRequest = this;
    return event;
  }
  set bid(msatAmount) {
    if (msatAmount === void 0) {
      this.removeTag("bid");
    } else {
      this.tags.push(["bid", msatAmount.toString()]);
    }
  }
  get bid() {
    const v = this.tagValue("bid");
    if (v === void 0)
      return void 0;
    return parseInt(v);
  }
  /**
   * Adds a new input to the job
   * @param args The arguments to the input
   */
  addInput(...args) {
    this.tags.push(["i", ...args]);
  }
  /**
   * Adds a new parameter to the job
   */
  addParam(...args) {
    this.tags.push(["param", ...args]);
  }
  set output(output) {
    if (output === void 0) {
      this.removeTag("output");
    } else {
      if (typeof output === "string")
        output = [output];
      this.tags.push(["output", ...output]);
    }
  }
  get output() {
    const outputTag = this.getMatchingTags("output")[0];
    return outputTag ? outputTag.slice(1) : void 0;
  }
  get params() {
    const paramTags = this.getMatchingTags("param");
    return paramTags.map((t) => t.slice(1));
  }
  getParam(name) {
    const paramTag = this.getMatchingTags("param").find((t) => t[1] === name);
    return paramTag ? paramTag[2] : void 0;
  }
};

// src/events/kinds/dvm/NDKTranscriptionDVM.ts
var NDKTranscriptionDVM = class extends NDKDVMRequest {
  constructor(ndk, event) {
    super(ndk, event);
    this.kind = 5e3 /* DVMReqTextExtraction */;
  }
  static from(event) {
    return new NDKTranscriptionDVM(event.ndk, event.rawEvent());
  }
  /**
   * Returns the original source of the transcription
   */
  get url() {
    const inputTags = this.getMatchingTags("i");
    if (inputTags.length !== 1) {
      return void 0;
    }
    return inputTags[0][1];
  }
  /**
   * Getter for the title tag
   */
  get title() {
    return this.tagValue("title");
  }
  /**
   * Setter for the title tag
   */
  set title(value) {
    this.removeTag("title");
    if (value) {
      this.tags.push(["title", value]);
    }
  }
  /**
   * Getter for the image tag
   */
  get image() {
    return this.tagValue("image");
  }
  /**
   * Setter for the image tag
   */
  set image(value) {
    this.removeTag("image");
    if (value) {
      this.tags.push(["image", value]);
    }
  }
};

// src/events/kinds/repost.ts
var NDKRepost = class extends NDKEvent {
  _repostedEvents;
  constructor(ndk, rawEvent) {
    super(ndk, rawEvent);
  }
  static from(event) {
    return new NDKRepost(event.ndk, event.rawEvent());
  }
  /**
   * Returns all reposted events by the current event.
   *
   * @param klass Optional class to convert the events to.
   * @returns
   */
  async repostedEvents(klass, opts) {
    const items = [];
    if (!this.ndk)
      throw new Error("NDK instance not set");
    if (this._repostedEvents !== void 0)
      return this._repostedEvents;
    for (const eventId of this.repostedEventIds()) {
      const filter = filterForId(eventId);
      const event = await this.ndk.fetchEvent(filter, opts);
      if (event) {
        items.push(klass ? klass.from(event) : event);
      }
    }
    return items;
  }
  /**
   * Returns the reposted event IDs.
   */
  repostedEventIds() {
    return this.tags.filter((t) => t[0] === "e" || t[0] === "a").map((t) => t[1]);
  }
};
function filterForId(id) {
  if (id.match(/:/)) {
    const [kind, pubkey, identifier] = id.split(":");
    return {
      kinds: [parseInt(kind)],
      authors: [pubkey],
      "#d": [identifier]
    };
  } else {
    return { ids: [id] };
  }
}

// src/events/kinds/nip89/NDKAppHandler.ts
var NDKAppHandlerEvent = class extends NDKEvent {
  profile;
  constructor(ndk, rawEvent) {
    super(ndk, rawEvent);
    this.kind ??= 31990 /* AppHandler */;
  }
  static from(ndkEvent) {
    return new NDKAppHandlerEvent(ndkEvent.ndk, ndkEvent.rawEvent());
  }
  /**
   * Fetches app handler information
   * If no app information is available on the kind:31990,
   * we fetch the event's author's profile and return that instead.
   */
  async fetchProfile() {
    if (this.profile === void 0 && this.content.length > 0) {
      try {
        const profile = JSON.parse(this.content);
        if (profile && profile.name) {
          return profile;
        } else {
          this.profile = null;
        }
      } catch (e) {
        this.profile = null;
      }
    }
    return new Promise((resolve, reject) => {
      const author = this.author;
      author.fetchProfile().then(() => {
        resolve(author.profile);
      }).catch(reject);
    });
  }
};

// src/signers/nip07/index.ts
var import_debug2 = __toESM(require("debug"));
var NDKNip07Signer = class {
  _userPromise;
  nip04Queue = [];
  nip04Processing = false;
  debug;
  waitTimeout;
  /**
   * @param waitTimeout - The timeout in milliseconds to wait for the NIP-07 to become available
   */
  constructor(waitTimeout = 1e3) {
    this.debug = (0, import_debug2.default)("ndk:nip07");
    this.waitTimeout = waitTimeout;
  }
  async blockUntilReady() {
    await this.waitForExtension();
    const pubkey = await window.nostr.getPublicKey();
    if (!pubkey) {
      throw new Error("User rejected access");
    }
    return new NDKUser({ hexpubkey: pubkey });
  }
  /**
   * Getter for the user property.
   * @returns The NDKUser instance.
   */
  async user() {
    if (!this._userPromise) {
      this._userPromise = this.blockUntilReady();
    }
    return this._userPromise;
  }
  /**
   * Signs the given Nostr event.
   * @param event - The Nostr event to be signed.
   * @returns The signature of the signed event.
   * @throws Error if the NIP-07 is not available on the window object.
   */
  async sign(event) {
    await this.waitForExtension();
    const signedEvent = await window.nostr.signEvent(event);
    return signedEvent.sig;
  }
  async relays() {
    await this.waitForExtension();
    const relays = await window.nostr.getRelays?.() || {};
    const activeRelays = [];
    for (const url in Object.keys(relays)) {
      if (relays[url].read && relays[url].write) {
        activeRelays.push(url);
      }
    }
    return activeRelays.map((url) => new NDKRelay(url));
  }
  async encrypt(recipient, value) {
    await this.waitForExtension();
    const recipientHexPubKey = recipient.hexpubkey;
    return this.queueNip04("encrypt", recipientHexPubKey, value);
  }
  async decrypt(sender, value) {
    await this.waitForExtension();
    const senderHexPubKey = sender.hexpubkey;
    return this.queueNip04("decrypt", senderHexPubKey, value);
  }
  async queueNip04(type, counterpartyHexpubkey, value) {
    return new Promise((resolve, reject) => {
      this.nip04Queue.push({
        type,
        counterpartyHexpubkey,
        value,
        resolve,
        reject
      });
      if (!this.nip04Processing) {
        this.processNip04Queue();
      }
    });
  }
  async processNip04Queue(item, retries = 0) {
    if (!item && this.nip04Queue.length === 0) {
      this.nip04Processing = false;
      return;
    }
    this.nip04Processing = true;
    const { type, counterpartyHexpubkey, value, resolve, reject } = item || this.nip04Queue.shift();
    this.debug("Processing encryption queue item", {
      type,
      counterpartyHexpubkey,
      value
    });
    try {
      let result;
      if (type === "encrypt") {
        result = await window.nostr.nip04.encrypt(counterpartyHexpubkey, value);
      } else {
        result = await window.nostr.nip04.decrypt(counterpartyHexpubkey, value);
      }
      resolve(result);
    } catch (error) {
      if (error.message && error.message.includes("call already executing")) {
        if (retries < 5) {
          this.debug("Retrying encryption queue item", {
            type,
            counterpartyHexpubkey,
            value,
            retries
          });
          setTimeout(() => {
            this.processNip04Queue(item, retries + 1);
          }, 50 * retries);
          return;
        }
      }
      reject(error);
    }
    this.processNip04Queue();
  }
  waitForExtension() {
    return new Promise((resolve, reject) => {
      if (window.nostr) {
        resolve();
        return;
      }
      let timerId;
      const intervalId = setInterval(() => {
        if (window.nostr) {
          clearTimeout(timerId);
          clearInterval(intervalId);
          resolve();
        }
      }, 100);
      timerId = setTimeout(() => {
        clearInterval(intervalId);
        reject(new Error("NIP-07 extension not available"));
      }, this.waitTimeout);
    });
  }
};

// src/signers/nip46/backend/index.ts
var import_nostr_tools11 = require("nostr-tools");

// src/signers/private-key/index.ts
var import_nostr_tools10 = require("nostr-tools");
var NDKPrivateKeySigner = class {
  _user;
  privateKey;
  constructor(privateKey) {
    if (privateKey) {
      this.privateKey = privateKey;
      this._user = new NDKUser({
        hexpubkey: (0, import_nostr_tools10.getPublicKey)(this.privateKey)
      });
    }
  }
  static generate() {
    const privateKey = (0, import_nostr_tools10.generatePrivateKey)();
    return new NDKPrivateKeySigner(privateKey);
  }
  async blockUntilReady() {
    if (!this._user) {
      throw new Error("NDKUser not initialized");
    }
    return this._user;
  }
  async user() {
    await this.blockUntilReady();
    return this._user;
  }
  async sign(event) {
    if (!this.privateKey) {
      throw Error("Attempted to sign without a private key");
    }
    return (0, import_nostr_tools10.getSignature)(event, this.privateKey);
  }
  async encrypt(recipient, value) {
    if (!this.privateKey) {
      throw Error("Attempted to encrypt without a private key");
    }
    const recipientHexPubKey = recipient.hexpubkey;
    return await import_nostr_tools10.nip04.encrypt(this.privateKey, recipientHexPubKey, value);
  }
  async decrypt(sender, value) {
    if (!this.privateKey) {
      throw Error("Attempted to decrypt without a private key");
    }
    const senderHexPubKey = sender.hexpubkey;
    return await import_nostr_tools10.nip04.decrypt(this.privateKey, senderHexPubKey, value);
  }
};

// src/signers/nip46/rpc.ts
var import_tseep6 = require("tseep");
var NDKNostrRpc = class extends import_tseep6.EventEmitter {
  ndk;
  signer;
  debug;
  constructor(ndk, signer, debug4) {
    super();
    this.ndk = ndk;
    this.signer = signer;
    this.debug = debug4.extend("rpc");
  }
  /**
   * Subscribe to a filter. This function will resolve once the subscription is ready.
   */
  subscribe(filter) {
    const sub = this.ndk.subscribe(filter, {
      closeOnEose: false,
      groupable: false
    });
    sub.on("event", async (event) => {
      try {
        const parsedEvent = await this.parseEvent(event);
        if (parsedEvent.method) {
          this.emit("request", parsedEvent);
        } else {
          this.emit(`response-${parsedEvent.id}`, parsedEvent);
        }
      } catch (e) {
        this.debug("error parsing event", e, event);
      }
    });
    return new Promise((resolve, reject) => {
      sub.on("eose", () => resolve(sub));
    });
  }
  async parseEvent(event) {
    const remoteUser = this.ndk.getUser({ hexpubkey: event.pubkey });
    remoteUser.ndk = this.ndk;
    const decryptedContent = await this.signer.decrypt(remoteUser, event.content);
    const parsedContent = JSON.parse(decryptedContent);
    const { id, method, params, result, error } = parsedContent;
    if (method) {
      return { id, pubkey: event.pubkey, method, params, event };
    } else {
      return { id, result, error, event };
    }
  }
  async sendResponse(id, remotePubkey, result, kind = 24133, error) {
    const res = { id, result };
    if (error) {
      res.error = error;
    }
    const localUser = await this.signer.user();
    const remoteUser = this.ndk.getUser({ hexpubkey: remotePubkey });
    const event = new NDKEvent(this.ndk, {
      kind,
      content: JSON.stringify(res),
      tags: [["p", remotePubkey]],
      pubkey: localUser.hexpubkey
    });
    event.content = await this.signer.encrypt(remoteUser, event.content);
    await event.sign(this.signer);
    await event.publish();
  }
  /**
   * Sends a request.
   * @param remotePubkey
   * @param method
   * @param params
   * @param kind
   * @param id
   */
  async sendRequest(remotePubkey, method, params = [], kind = 24133, cb) {
    const id = Math.random().toString(36).substring(7);
    const localUser = await this.signer.user();
    const remoteUser = this.ndk.getUser({ hexpubkey: remotePubkey });
    const request = { id, method, params };
    const promise = new Promise((resolve) => {
      const responseHandler = (response) => {
        if (response.result === "auth_url") {
          this.once(`response-${id}`, responseHandler);
          this.emit("authUrl", response.error);
        } else if (cb) {
          cb(response);
        }
      };
      this.once(`response-${id}`, responseHandler);
    });
    const event = new NDKEvent(this.ndk, {
      kind,
      content: JSON.stringify(request),
      tags: [["p", remotePubkey]],
      pubkey: localUser.hexpubkey
    });
    event.content = await this.signer.encrypt(remoteUser, event.content);
    await event.sign(this.signer);
    this.debug(`sending ${method} request to`, remotePubkey);
    await event.publish();
    return promise;
  }
};

// src/signers/nip46/backend/connect.ts
var ConnectEventHandlingStrategy = class {
  async handle(backend, id, remotePubkey, params) {
    const [pubkey, token] = params;
    const debug4 = backend.debug.extend("connect");
    debug4(`connection request from ${pubkey}`);
    if (token && backend.applyToken) {
      debug4(`applying token`);
      await backend.applyToken(pubkey, token);
    }
    if (await backend.pubkeyAllowed({ id, pubkey, method: "connect", params: token })) {
      debug4(`connection request from ${pubkey} allowed`);
      return "ack";
    } else {
      debug4(`connection request from ${pubkey} rejected`);
    }
    return void 0;
  }
};

// src/signers/nip46/backend/get-public-key.ts
var GetPublicKeyHandlingStrategy = class {
  async handle(backend, id, remotePubkey, params) {
    return backend.localUser?.pubkey;
  }
};

// src/signers/nip46/backend/nip04-decrypt.ts
var Nip04DecryptHandlingStrategy = class {
  async handle(backend, id, remotePubkey, params) {
    const [senderPubkey, payload] = params;
    const senderUser = new NDKUser({ hexpubkey: senderPubkey });
    const decryptedPayload = await decrypt2(backend, id, remotePubkey, senderUser, payload);
    return JSON.stringify([decryptedPayload]);
  }
};
async function decrypt2(backend, id, remotePubkey, senderUser, payload) {
  if (!await backend.pubkeyAllowed({
    id,
    pubkey: remotePubkey,
    method: "decrypt",
    params: payload
  })) {
    backend.debug(`decrypt request from ${remotePubkey} rejected`);
    return void 0;
  }
  return await backend.signer.decrypt(senderUser, payload);
}

// src/signers/nip46/backend/nip04-encrypt.ts
var Nip04EncryptHandlingStrategy = class {
  async handle(backend, id, remotePubkey, params) {
    const [recipientPubkey, payload] = params;
    const recipientUser = new NDKUser({ hexpubkey: recipientPubkey });
    const encryptedPayload = await encrypt2(backend, id, remotePubkey, recipientUser, payload);
    return encryptedPayload;
  }
};
async function encrypt2(backend, id, remotePubkey, recipientUser, payload) {
  if (!await backend.pubkeyAllowed({
    id,
    pubkey: remotePubkey,
    method: "encrypt",
    params: payload
  })) {
    backend.debug(`encrypt request from ${remotePubkey} rejected`);
    return void 0;
  }
  return await backend.signer.encrypt(recipientUser, payload);
}

// src/signers/nip46/backend/sign-event.ts
var SignEventHandlingStrategy = class {
  async handle(backend, id, remotePubkey, params) {
    const event = await signEvent(backend, id, remotePubkey, params);
    if (!event)
      return void 0;
    return JSON.stringify(await event.toNostrEvent());
  }
};
async function signEvent(backend, id, remotePubkey, params) {
  const [eventString] = params;
  backend.debug(`sign event request from ${remotePubkey}`);
  const event = new NDKEvent(backend.ndk, JSON.parse(eventString));
  backend.debug("event to sign", event.rawEvent());
  if (!await backend.pubkeyAllowed({
    id,
    pubkey: remotePubkey,
    method: "sign_event",
    params: event
  })) {
    backend.debug(`sign event request from ${remotePubkey} rejected`);
    return void 0;
  }
  backend.debug(`sign event request from ${remotePubkey} allowed`);
  await event.sign(backend.signer);
  return event;
}

// src/signers/nip46/backend/index.ts
var NDKNip46Backend = class {
  ndk;
  signer;
  localUser;
  debug;
  rpc;
  permitCallback;
  /**
   * @param ndk The NDK instance to use
   * @param privateKey The private key of the npub that wants to be published as
   */
  constructor(ndk, privateKey, permitCallback) {
    this.ndk = ndk;
    this.signer = new NDKPrivateKeySigner(privateKey);
    this.debug = ndk.debug.extend("nip46:backend");
    this.rpc = new NDKNostrRpc(ndk, this.signer, this.debug);
    this.permitCallback = permitCallback;
  }
  /**
   * This method starts the backend, which will start listening for incoming
   * requests.
   */
  async start() {
    this.localUser = await this.signer.user();
    const sub = this.ndk.subscribe(
      {
        kinds: [24133],
        "#p": [this.localUser.hexpubkey]
      },
      { closeOnEose: false }
    );
    sub.on("event", (e) => this.handleIncomingEvent(e));
  }
  handlers = {
    connect: new ConnectEventHandlingStrategy(),
    sign_event: new SignEventHandlingStrategy(),
    nip04_encrypt: new Nip04EncryptHandlingStrategy(),
    nip04_decrypt: new Nip04DecryptHandlingStrategy(),
    get_public_key: new GetPublicKeyHandlingStrategy()
  };
  /**
   * Enables the user to set a custom strategy for handling incoming events.
   * @param method - The method to set the strategy for
   * @param strategy - The strategy to set
   */
  setStrategy(method, strategy) {
    this.handlers[method] = strategy;
  }
  /**
   * Overload this method to apply tokens, which can
   * wrap permission sets to be applied to a pubkey.
   * @param pubkey public key to apply token to
   * @param token token to apply
   */
  async applyToken(pubkey, token) {
    throw new Error("connection token not supported");
  }
  async handleIncomingEvent(event) {
    const { id, method, params } = await this.rpc.parseEvent(event);
    const remotePubkey = event.pubkey;
    let response;
    this.debug("incoming event", { id, method, params });
    if (!(0, import_nostr_tools11.verifySignature)(event.rawEvent())) {
      this.debug("invalid signature", event.rawEvent());
      return;
    }
    const strategy = this.handlers[method];
    if (strategy) {
      try {
        response = await strategy.handle(this, id, remotePubkey, params);
      } catch (e) {
        this.debug("error handling event", e, { id, method, params });
        this.rpc.sendResponse(id, remotePubkey, "error", void 0, e.message);
      }
    } else {
      this.debug("unsupported method", { method, params });
    }
    if (response) {
      this.debug(`sending response to ${remotePubkey}`, response);
      this.rpc.sendResponse(id, remotePubkey, response);
    } else {
      this.rpc.sendResponse(id, remotePubkey, "error", void 0, "Not authorized");
    }
  }
  /**
   * This method should be overriden by the user to allow or reject incoming
   * connections.
   */
  async pubkeyAllowed(params) {
    return this.permitCallback(params);
  }
};

// ../node_modules/eventemitter3/index.mjs
var import_index6 = __toESM(require_eventemitter3(), 1);
var eventemitter3_default = import_index6.default;

// src/signers/nip46/index.ts
var NDKNip46Signer = class extends eventemitter3_default {
  ndk;
  remoteUser;
  remotePubkey;
  token;
  localSigner;
  nip05;
  rpc;
  debug;
  /**
   * @param ndk - The NDK instance to use
   * @param tokenOrRemoteUser - The public key, or a connection token, of the npub that wants to be published as
   * @param localSigner - The signer that will be used to request events to be signed
   */
  constructor(ndk, tokenOrRemoteUser, localSigner) {
    super();
    let remotePubkey;
    let token;
    if (tokenOrRemoteUser.includes("#")) {
      const parts = tokenOrRemoteUser.split("#");
      remotePubkey = new NDKUser({ npub: parts[0] }).pubkey;
      token = parts[1];
    } else if (tokenOrRemoteUser.startsWith("npub")) {
      remotePubkey = new NDKUser({
        npub: tokenOrRemoteUser
      }).pubkey;
    } else if (tokenOrRemoteUser.match(/\./)) {
      this.nip05 = tokenOrRemoteUser;
    } else {
      remotePubkey = tokenOrRemoteUser;
    }
    this.ndk = ndk;
    if (remotePubkey)
      this.remotePubkey = remotePubkey;
    this.token = token;
    this.debug = ndk.debug.extend("nip46:signer");
    this.remoteUser = new NDKUser({ pubkey: remotePubkey });
    if (!localSigner) {
      this.localSigner = NDKPrivateKeySigner.generate();
    } else {
      this.localSigner = localSigner;
    }
    this.rpc = new NDKNostrRpc(ndk, this.localSigner, this.debug);
    this.rpc.on("authUrl", (...props) => {
      this.emit("authUrl", ...props);
    });
  }
  /**
   * Get the user that is being published as
   */
  async user() {
    return this.remoteUser;
  }
  async blockUntilReady() {
    const localUser = await this.localSigner.user();
    const remoteUser = this.ndk.getUser({ pubkey: this.remotePubkey });
    if (this.nip05 && !this.remotePubkey) {
      const remoteUser2 = NDKUser.fromNip05(this.nip05).then((user) => {
        if (user) {
          this.remoteUser = user;
          this.remotePubkey = user.pubkey;
        }
      });
    }
    if (!this.remotePubkey) {
      throw new Error("Remote pubkey not set");
    }
    await this.rpc.subscribe({
      kinds: [24133],
      "#p": [localUser.pubkey]
    });
    return new Promise((resolve, reject) => {
      setTimeout(() => {
        const connectParams = [localUser.pubkey];
        if (this.token) {
          connectParams.push(this.token);
        }
        this.rpc.sendRequest(
          this.remotePubkey,
          "connect",
          connectParams,
          24133,
          (response) => {
            if (response.result === "ack") {
              resolve(remoteUser);
            } else {
              reject(response.error);
            }
          }
        );
      }, 100);
    });
  }
  async encrypt(recipient, value) {
    this.debug("asking for encryption");
    const promise = new Promise((resolve, reject) => {
      this.rpc.sendRequest(
        this.remotePubkey,
        "nip04_encrypt",
        [recipient.pubkey, value],
        24133,
        (response) => {
          if (!response.error) {
            resolve(response.result);
          } else {
            reject(response.error);
          }
        }
      );
    });
    return promise;
  }
  async decrypt(sender, value) {
    this.debug("asking for decryption");
    const promise = new Promise((resolve, reject) => {
      this.rpc.sendRequest(
        this.remotePubkey,
        "nip04_decrypt",
        [sender.pubkey, value],
        24133,
        (response) => {
          if (!response.error) {
            const value2 = JSON.parse(response.result);
            resolve(value2[0]);
          } else {
            reject(response.error);
          }
        }
      );
    });
    return promise;
  }
  async sign(event) {
    this.debug("asking for a signature");
    const promise = new Promise((resolve, reject) => {
      this.rpc.sendRequest(
        this.remotePubkey,
        "sign_event",
        [JSON.stringify(event)],
        24133,
        (response) => {
          this.debug("got a response", response);
          if (!response.error) {
            const json = JSON.parse(response.result);
            resolve(json.sig);
          } else {
            reject(response.error);
          }
        }
      );
    });
    return promise;
  }
};

// src/ndk/index.ts
var import_debug3 = __toESM(require("debug"));
var import_tseep9 = require("tseep");

// src/events/dedup.ts
function dedup(event1, event2) {
  if (event1.created_at > event2.created_at) {
    return event1;
  }
  return event2;
}

// src/outbox/tracker.ts
var import_tseep7 = require("tseep");
var import_typescript_lru_cache = require("typescript-lru-cache");
var OutboxItem = class {
  /**
   * Type of item
   */
  type;
  /**
   * The relay URLs that are of interest to this item
   */
  relayUrlScores;
  readRelays;
  writeRelays;
  constructor(type) {
    this.type = type;
    this.relayUrlScores = /* @__PURE__ */ new Map();
    this.readRelays = /* @__PURE__ */ new Set();
    this.writeRelays = /* @__PURE__ */ new Set();
  }
};
var OutboxTracker = class extends import_tseep7.EventEmitter {
  data;
  ndk;
  debug;
  constructor(ndk) {
    super();
    this.ndk = ndk;
    this.debug = ndk.debug.extend("outbox-tracker");
    this.data = new import_typescript_lru_cache.LRUCache({
      maxSize: 1e5,
      entryExpirationTimeInMS: 5e3
    });
  }
  trackUsers(items) {
    for (const item of items) {
      const itemKey = getKeyFromItem(item);
      if (this.data.has(itemKey))
        continue;
      const outboxItem = this.track(item, "user");
      const user = item instanceof NDKUser ? item : new NDKUser({ hexpubkey: item });
      user.ndk = this.ndk;
      user.relayList().then((relayList) => {
        if (relayList) {
          outboxItem.readRelays = new Set(relayList.readRelayUrls);
          outboxItem.writeRelays = new Set(relayList.writeRelayUrls);
          for (const relayUrl of outboxItem.readRelays) {
            if (this.ndk.pool.blacklistRelayUrls.has(relayUrl)) {
              this.debug(`removing blacklisted relay ${relayUrl} from read relays`);
              outboxItem.readRelays.delete(relayUrl);
            }
          }
          for (const relayUrl of outboxItem.writeRelays) {
            if (this.ndk.pool.blacklistRelayUrls.has(relayUrl)) {
              this.debug(`removing blacklisted relay ${relayUrl} from write relays`);
              outboxItem.writeRelays.delete(relayUrl);
            }
          }
          this.data.set(itemKey, outboxItem);
          this.debug(
            `Adding ${outboxItem.readRelays.size} read relays and ${outboxItem.writeRelays.size} write relays for ${user.hexpubkey}`
          );
        }
      });
    }
  }
  /**
   *
   * @param key
   * @param score
   */
  track(item, type) {
    const key = getKeyFromItem(item);
    type ??= getTypeFromItem(item);
    let outboxItem = this.data.get(key);
    if (!outboxItem)
      outboxItem = new OutboxItem(type);
    this.data.set(key, outboxItem);
    return outboxItem;
  }
};
function getKeyFromItem(item) {
  if (item instanceof NDKUser) {
    return item.hexpubkey;
  } else {
    return item;
  }
}
function getTypeFromItem(item) {
  if (item instanceof NDKUser) {
    return "user";
  } else {
    return "kind";
  }
}

// src/relay/pool/index.ts
var import_tseep8 = require("tseep");
var NDKPool = class extends import_tseep8.EventEmitter {
  // TODO: This should probably be an LRU cache
  relays = /* @__PURE__ */ new Map();
  blacklistRelayUrls;
  debug;
  temporaryRelayTimers = /* @__PURE__ */ new Map();
  flappingRelays = /* @__PURE__ */ new Set();
  // A map to store timeouts for each flapping relay.
  backoffTimes = /* @__PURE__ */ new Map();
  constructor(relayUrls = [], blacklistedRelayUrls = [], ndk, debug4) {
    super();
    this.debug = debug4 ?? ndk.debug.extend("pool");
    for (const relayUrl of relayUrls) {
      const relay = new NDKRelay(relayUrl);
      this.addRelay(relay, false);
    }
    this.blacklistRelayUrls = new Set(blacklistedRelayUrls);
  }
  /**
   * Adds a relay to the pool, and sets a timer to remove it if it is not used within the specified time.
   * @param relay - The relay to add to the pool.
   * @param removeIfUnusedAfter - The time in milliseconds to wait before removing the relay from the pool after it is no longer used.
   */
  useTemporaryRelay(relay, removeIfUnusedAfter = 6e5) {
    const relayAlreadyInPool = this.relays.has(relay.url);
    if (!relayAlreadyInPool) {
      this.addRelay(relay);
    }
    const existingTimer = this.temporaryRelayTimers.get(relay.url);
    if (existingTimer) {
      clearTimeout(existingTimer);
    }
    if (!relayAlreadyInPool || existingTimer) {
      const timer = setTimeout(() => {
        this.removeRelay(relay.url);
      }, removeIfUnusedAfter);
      this.temporaryRelayTimers.set(relay.url, timer);
    }
  }
  /**
   * Adds a relay to the pool.
   *
   * @param relay - The relay to add to the pool.
   * @param connect - Whether or not to connect to the relay.
   */
  addRelay(relay, connect = true) {
    const relayUrl = relay.url;
    if (this.blacklistRelayUrls?.has(relayUrl)) {
      this.debug(`Relay ${relayUrl} is blacklisted`);
      return;
    }
    relay.on("notice", async (relay2, notice) => this.emit("notice", relay2, notice));
    relay.on("connect", () => this.handleRelayConnect(relayUrl));
    relay.on("disconnect", async () => this.emit("relay:disconnect", relay));
    relay.on("flapping", () => this.handleFlapping(relay));
    this.relays.set(relayUrl, relay);
    if (connect) {
      relay.connect().catch((e) => {
        this.debug(`Failed to connect to relay ${relayUrl}`, e);
      });
    }
  }
  /**
   * Removes a relay from the pool.
   * @param relayUrl - The URL of the relay to remove.
   * @returns {boolean} True if the relay was removed, false if it was not found.
   */
  removeRelay(relayUrl) {
    const relay = this.relays.get(relayUrl);
    if (relay) {
      relay.disconnect();
      this.relays.delete(relayUrl);
      this.emit("relay:disconnect", relay);
      return true;
    }
    const existingTimer = this.temporaryRelayTimers.get(relayUrl);
    if (existingTimer) {
      clearTimeout(existingTimer);
      this.temporaryRelayTimers.delete(relayUrl);
    }
    return false;
  }
  /**
   * Fetches a relay from the pool, or creates a new one if it does not exist.
   *
   * New relays will be attempted to be connected.
   */
  getRelay(url) {
    let relay = this.relays.get(url);
    if (!relay) {
      relay = new NDKRelay(url);
      this.addRelay(relay);
    }
    return relay;
  }
  handleRelayConnect(relayUrl) {
    this.debug(`Relay ${relayUrl} connected`);
    this.emit("relay:connect", this.relays.get(relayUrl));
    if (this.stats().connected === this.relays.size) {
      this.emit("connect");
    }
  }
  /**
   * Attempts to establish a connection to each relay in the pool.
   *
   * @async
   * @param {number} [timeoutMs] - Optional timeout in milliseconds for each connection attempt.
   * @returns {Promise<void>} A promise that resolves when all connection attempts have completed.
   * @throws {Error} If any of the connection attempts result in an error or timeout.
   */
  async connect(timeoutMs) {
    const promises = [];
    this.debug(
      `Connecting to ${this.relays.size} relays${timeoutMs ? `, timeout ${timeoutMs}...` : ""}`
    );
    for (const relay of this.relays.values()) {
      if (timeoutMs) {
        const timeoutPromise = new Promise((_, reject) => {
          setTimeout(() => reject(`Timed out after ${timeoutMs}ms`), timeoutMs);
        });
        promises.push(
          Promise.race([relay.connect(), timeoutPromise]).catch((e) => {
            this.debug(
              `Failed to connect to relay ${relay.url}: ${e ?? "No reason specified"}`
            );
          })
        );
      } else {
        promises.push(relay.connect());
      }
    }
    if (timeoutMs) {
      setTimeout(() => {
        const allConnected = this.stats().connected === this.relays.size;
        const someConnected = this.stats().connected > 0;
        if (!allConnected && someConnected) {
          this.emit("connect");
        }
      }, timeoutMs);
    }
    await Promise.all(promises);
  }
  checkOnFlappingRelays() {
    const flappingRelaysCount = this.flappingRelays.size;
    const totalRelays = this.relays.size;
    if (flappingRelaysCount / totalRelays >= 0.8) {
      for (const relayUrl of this.flappingRelays) {
        this.backoffTimes.set(relayUrl, 0);
      }
    }
  }
  handleFlapping(relay) {
    this.debug(`Relay ${relay.url} is flapping`);
    let currentBackoff = this.backoffTimes.get(relay.url) || 5e3;
    currentBackoff = currentBackoff * 2;
    this.backoffTimes.set(relay.url, currentBackoff);
    this.debug(`Backoff time for ${relay.url} is ${currentBackoff}ms`);
    setTimeout(() => {
      this.debug(`Attempting to reconnect to ${relay.url}`);
      relay.connect();
      this.checkOnFlappingRelays();
    }, currentBackoff);
    relay.disconnect();
    this.emit("flapping", relay);
  }
  size() {
    return this.relays.size;
  }
  /**
   * Returns the status of each relay in the pool.
   * @returns {NDKPoolStats} An object containing the number of relays in each status.
   */
  stats() {
    const stats = {
      total: 0,
      connected: 0,
      disconnected: 0,
      connecting: 0
    };
    for (const relay of this.relays.values()) {
      stats.total++;
      if (relay.status === 1 /* CONNECTED */) {
        stats.connected++;
      } else if (relay.status === 3 /* DISCONNECTED */) {
        stats.disconnected++;
      } else if (relay.status === 0 /* CONNECTING */) {
        stats.connecting++;
      }
    }
    return stats;
  }
  connectedRelays() {
    return Array.from(this.relays.values()).filter(
      (relay) => relay.status === 1 /* CONNECTED */
    );
  }
  /**
   * Get a list of all relay urls in the pool.
   */
  urls() {
    return Array.from(this.relays.keys());
  }
};

// src/relay/sets/utils.ts
function correctRelaySet(relaySet, pool) {
  const connectedRelays = pool.connectedRelays();
  const includesConnectedRelay = Array.from(relaySet.relays).some((relay) => {
    return connectedRelays.map((r) => r.url).includes(relay.url);
  });
  if (!includesConnectedRelay) {
    for (const relay of connectedRelays) {
      relaySet.addRelay(relay);
    }
  }
  if (connectedRelays.length === 0) {
    for (const relay of pool.relays.values()) {
      relaySet.addRelay(relay);
    }
  }
  return relaySet;
}

// src/ndk/index.ts
var DEFAULT_OUTBOX_RELAYS = ["wss://purplepag.es", "wss://relay.snort.social"];
var DEFAULT_BLACKLISTED_RELAYS = [
  "wss://brb.io"
  // BRB
];
var NDK = class extends import_tseep9.EventEmitter {
  explicitRelayUrls;
  pool;
  outboxPool;
  _signer;
  _activeUser;
  cacheAdapter;
  debug;
  devWriteRelaySet;
  outboxTracker;
  mutedIds;
  client;
  autoConnectUserRelays = true;
  autoFetchUserMutelist = true;
  constructor(opts = {}) {
    super();
    this.debug = opts.debug || (0, import_debug3.default)("ndk");
    this.explicitRelayUrls = opts.explicitRelayUrls;
    this.pool = new NDKPool(opts.explicitRelayUrls || [], opts.blacklistRelayUrls, this);
    this.debug(`Starting with explicit relays: ${JSON.stringify(this.explicitRelayUrls)}`);
    this.autoConnectUserRelays = opts.autoConnectUserRelays ?? true;
    this.autoFetchUserMutelist = opts.autoFetchUserMutelist ?? true;
    this.client = opts.client;
    if (opts.enableOutboxModel) {
      this.outboxPool = new NDKPool(
        opts.outboxRelayUrls || DEFAULT_OUTBOX_RELAYS,
        opts.blacklistRelayUrls || DEFAULT_BLACKLISTED_RELAYS,
        this,
        this.debug.extend("outbox-pool")
      );
      this.outboxTracker = new OutboxTracker(this);
    }
    this.signer = opts.signer;
    this.cacheAdapter = opts.cacheAdapter;
    this.mutedIds = opts.mutedIds || /* @__PURE__ */ new Map();
    if (opts.devWriteRelayUrls) {
      this.devWriteRelaySet = NDKRelaySet.fromRelayUrls(opts.devWriteRelayUrls, this);
    }
  }
  toJSON() {
    return { relayCount: this.pool.relays.size }.toString();
  }
  get activeUser() {
    return this._activeUser;
  }
  /**
   * Sets the active user for this NDK instance, typically this will be
   * called when assigning a signer to the NDK instance.
   *
   * This function will automatically connect to the user's relays if
   * `autoConnectUserRelays` is set to true.
   *
   * It will also fetch the user's mutelist if `autoFetchUserMutelist` is set to true.
   */
  set activeUser(user) {
    const differentUser = this._activeUser !== user;
    this._activeUser = user;
    if (user && differentUser) {
      const connectToUserRelays = async (user2) => {
        const relayList = await user2.relayList();
        if (!relayList) {
          this.debug("No relay list found for user", { npub: user2.npub });
          return;
        }
        this.debug("Connecting to user relays", {
          npub: user2.npub,
          relays: relayList.relays
        });
        for (const url of relayList.relays) {
          let relay = this.pool.relays.get(url);
          if (!relay) {
            relay = new NDKRelay(url);
            this.pool.addRelay(relay);
          }
        }
      };
      const fetchUserMuteList = async (user2) => {
        const muteLists = await this.fetchEvents([
          { kinds: [1e4 /* MuteList */], authors: [user2.pubkey] },
          {
            kinds: [3e4 /* FollowSet */],
            authors: [user2.pubkey],
            "#d": ["mute"],
            limit: 1
          }
        ]);
        if (!muteLists) {
          this.debug("No mute list found for user", { npub: user2.npub });
          return;
        }
        for (const muteList of muteLists) {
          const list = lists_default.from(muteList);
          for (const item of list.items) {
            this.mutedIds.set(item[1], item[0]);
          }
        }
      };
      const userFunctions = [];
      if (this.autoConnectUserRelays)
        userFunctions.push(connectToUserRelays);
      if (this.autoFetchUserMutelist)
        userFunctions.push(fetchUserMuteList);
      const runUserFunctions = async (user2) => {
        for (const fn of userFunctions) {
          await fn(user2);
        }
      };
      const pool = this.outboxPool || this.pool;
      if (pool.connectedRelays.length > 0) {
        runUserFunctions(user);
      } else {
        this.debug("Waiting for connection to main relays");
        pool.once("relay:connect", (relay) => {
          this.debug("New relay came online", relay);
          runUserFunctions(user);
        });
      }
    } else if (!user) {
      this.mutedIds = /* @__PURE__ */ new Map();
    }
  }
  get signer() {
    return this._signer;
  }
  set signer(newSigner) {
    this._signer = newSigner;
    this.debug(`setting signer`, this.autoConnectUserRelays);
    newSigner?.user().then((user) => {
      user.ndk = this;
      this.activeUser = user;
    });
  }
  /**
   * Connect to relays with optional timeout.
   * If the timeout is reached, the connection will be continued to be established in the background.
   */
  async connect(timeoutMs) {
    if (this._signer && this.autoConnectUserRelays) {
      this.debug("Attempting to connect to user relays specified by signer");
      if (this._signer.relays) {
        const relays = await this._signer.relays();
        relays.forEach((relay) => this.pool.addRelay(relay));
      }
    }
    const connections = [this.pool.connect(timeoutMs)];
    if (this.outboxPool) {
      connections.push(this.outboxPool.connect(timeoutMs));
    }
    this.debug("Connecting to relays", { timeoutMs });
    return Promise.allSettled(connections).then(() => {
    });
  }
  /**
   * Get a NDKUser object
   *
   * @param opts
   * @returns
   */
  getUser(opts) {
    const user = new NDKUser(opts);
    user.ndk = this;
    return user;
  }
  /**
   * Create a new subscription. Subscriptions automatically start, you can make them automatically close when all relays send back an EOSE by setting `opts.closeOnEose` to `true`)
   *
   * @param filters
   * @param opts
   * @param relaySet explicit relay set to use
   * @param autoStart automatically start the subscription
   * @returns NDKSubscription
   */
  subscribe(filters, opts, relaySet, autoStart = true) {
    const subscription = new NDKSubscription(this, filters, opts, relaySet);
    if (relaySet) {
      for (const relay of relaySet.relays) {
        this.pool.useTemporaryRelay(relay);
      }
    }
    if (this.outboxPool && subscription.hasAuthorsFilter()) {
      const authors = subscription.filters.filter((filter) => filter.authors && filter.authors?.length > 0).map((filter) => filter.authors).flat();
      this.outboxTracker?.trackUsers(authors);
    }
    if (autoStart)
      subscription.start();
    return subscription;
  }
  /**
   * Publish an event to a relay
   * @param event event to publish
   * @param relaySet explicit relay set to use
   * @param timeoutMs timeout in milliseconds to wait for the event to be published
   * @returns The relays the event was published to
   *
   * @deprecated Use `event.publish()` instead
   */
  async publish(event, relaySet, timeoutMs) {
    this.debug("Deprecated: Use `event.publish()` instead");
    return event.publish(relaySet, timeoutMs);
  }
  /**
   * Fetch a single event.
   *
   * @param idOrFilter event id in bech32 format or filter
   * @param opts subscription options
   * @param relaySet explicit relay set to use
   */
  async fetchEvent(idOrFilter, opts, relaySet) {
    let filter;
    if (!relaySet && typeof idOrFilter === "string") {
      if (!isNip33AValue(idOrFilter)) {
        const relays = relaysFromBech32(idOrFilter);
        if (relays.length > 0) {
          relaySet = new NDKRelaySet(new Set(relays), this);
          relaySet = correctRelaySet(relaySet, this.pool);
        }
      }
    }
    if (typeof idOrFilter === "string") {
      filter = filterFromId(idOrFilter);
    } else {
      filter = idOrFilter;
    }
    if (!filter) {
      throw new Error(`Invalid filter: ${JSON.stringify(idOrFilter)}`);
    }
    return new Promise((resolve) => {
      const s = this.subscribe(
        filter,
        { ...opts || {}, closeOnEose: true },
        relaySet,
        false
      );
      s.on("event", (event) => {
        event.ndk = this;
        resolve(event);
      });
      s.on("eose", () => {
        resolve(null);
      });
      s.start();
    });
  }
  /**
   * Fetch events
   */
  async fetchEvents(filters, opts, relaySet) {
    return new Promise((resolve) => {
      const events = /* @__PURE__ */ new Map();
      const relaySetSubscription = this.subscribe(
        filters,
        { ...opts || {}, closeOnEose: true },
        relaySet,
        false
      );
      const onEvent = (event) => {
        const dedupKey = event.deduplicationKey();
        const existingEvent = events.get(dedupKey);
        if (existingEvent) {
          event = dedup(existingEvent, event);
        }
        event.ndk = this;
        events.set(dedupKey, event);
      };
      relaySetSubscription.on("event", onEvent);
      relaySetSubscription.on("event:dup", onEvent);
      relaySetSubscription.on("eose", () => {
        resolve(new Set(events.values()));
      });
      relaySetSubscription.start();
    });
  }
  /**
   * Ensures that a signer is available to sign an event.
   */
  assertSigner() {
    if (!this.signer) {
      this.emit("signerRequired");
      throw new Error("Signer required");
    }
  }
};

// src/zap/invoice.ts
var import_light_bolt11_decoder = require("light-bolt11-decoder");
function zapInvoiceFromEvent(event) {
  const description = event.getMatchingTags("description")[0];
  const bolt11 = event.getMatchingTags("bolt11")[0];
  let decodedInvoice;
  let zapRequest;
  if (!description || !bolt11 || !bolt11[1]) {
    return null;
  }
  try {
    let zapRequestPayload = description[1];
    if (zapRequestPayload.startsWith("%")) {
      zapRequestPayload = decodeURIComponent(zapRequestPayload);
    }
    if (zapRequestPayload === "") {
      return null;
    }
    zapRequest = JSON.parse(zapRequestPayload);
    decodedInvoice = (0, import_light_bolt11_decoder.decode)(bolt11[1]);
  } catch (e) {
    return null;
  }
  const amountSection = decodedInvoice.sections.find((s) => s.name === "amount");
  if (!amountSection) {
    return null;
  }
  const amount = parseInt(amountSection.value);
  if (!amount) {
    return null;
  }
  const content = zapRequest.content;
  const sender = zapRequest.pubkey;
  const recipientTag = event.getMatchingTags("p")[0];
  const recipient = recipientTag[1];
  let zappedEvent = event.getMatchingTags("e")[0];
  if (!zappedEvent) {
    zappedEvent = event.getMatchingTags("a")[0];
  }
  const zappedEventId = zappedEvent ? zappedEvent[1] : void 0;
  const zapInvoice = {
    id: event.id,
    zapper: event.pubkey,
    zappee: sender,
    zapped: recipient,
    zappedEvent: zappedEventId,
    amount,
    comment: content
  };
  return zapInvoice;
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
  NDKAppHandlerEvent,
  NDKArticle,
  NDKDVMJobFeedback,
  NDKDVMJobResult,
  NDKDVMRequest,
  NDKDvmJobFeedbackStatus,
  NDKEvent,
  NDKHighlight,
  NDKKind,
  NDKList,
  NDKListKinds,
  NDKNip07Signer,
  NDKNip46Backend,
  NDKNip46Signer,
  NDKNostrRpc,
  NDKPrivateKeySigner,
  NDKRelay,
  NDKRelayList,
  NDKRelaySet,
  NDKRelayStatus,
  NDKRepost,
  NDKSubscription,
  NDKSubscriptionCacheUsage,
  NDKTranscriptionDVM,
  NDKUser,
  calculateGroupableId,
  defaultOpts,
  mergeFilters,
  profileFromEvent,
  serializeProfile,
  zapInvoiceFromEvent
});
