// nip11.ts
var _fetch;
try {
  _fetch = fetch;
} catch {
}
async function fetchRelayInformation(url) {
  return await (await fetch(url.replace("ws://", "http://").replace("wss://", "https://"), {
    headers: { Accept: "application/nostr+json" }
  })).json();
}

// nip19.ts
import { bytesToHex as bytesToHex2, concatBytes, hexToBytes as hexToBytes2 } from "@noble/hashes/utils";
import { bech32 } from "@scure/base";

// utils.ts
import { bytesToHex, hexToBytes } from "@noble/hashes/utils";
var utf8Decoder = new TextDecoder("utf-8");
var utf8Encoder = new TextEncoder();
function normalizeURL(url) {
  try {
    if (url.indexOf("://") === -1)
      url = "wss://" + url;
    let p = new URL(url);
    p.pathname = p.pathname.replace(/\/+/g, "/");
    if (p.pathname.endsWith("/"))
      p.pathname = p.pathname.slice(0, -1);
    if (p.port === "80" && p.protocol === "ws:" || p.port === "443" && p.protocol === "wss:")
      p.port = "";
    p.searchParams.sort();
    p.hash = "";
    return p.toString();
  } catch (e) {
    throw new Error(`Invalid URL: ${url}`);
  }
}

// nip19.ts
var NostrTypeGuard = {
  isNProfile: (value) => /^nprofile1[a-z\d]+$/.test(value || ""),
  isNEvent: (value) => /^nevent1[a-z\d]+$/.test(value || ""),
  isNAddr: (value) => /^naddr1[a-z\d]+$/.test(value || ""),
  isNSec: (value) => /^nsec1[a-z\d]{58}$/.test(value || ""),
  isNPub: (value) => /^npub1[a-z\d]{58}$/.test(value || ""),
  isNote: (value) => /^note1[a-z\d]+$/.test(value || ""),
  isNcryptsec: (value) => /^ncryptsec1[a-z\d]+$/.test(value || "")
};
var Bech32MaxSize = 5e3;
function decode(code) {
  let { prefix, words } = bech32.decode(code, Bech32MaxSize);
  let data = new Uint8Array(bech32.fromWords(words));
  switch (prefix) {
    case "nprofile": {
      let tlv = parseTLV(data);
      if (!tlv[0]?.[0])
        throw new Error("missing TLV 0 for nprofile");
      if (tlv[0][0].length !== 32)
        throw new Error("TLV 0 should be 32 bytes");
      return {
        type: "nprofile",
        data: {
          pubkey: bytesToHex2(tlv[0][0]),
          relays: tlv[1] ? tlv[1].map((d) => utf8Decoder.decode(d)) : []
        }
      };
    }
    case "nevent": {
      let tlv = parseTLV(data);
      if (!tlv[0]?.[0])
        throw new Error("missing TLV 0 for nevent");
      if (tlv[0][0].length !== 32)
        throw new Error("TLV 0 should be 32 bytes");
      if (tlv[2] && tlv[2][0].length !== 32)
        throw new Error("TLV 2 should be 32 bytes");
      if (tlv[3] && tlv[3][0].length !== 4)
        throw new Error("TLV 3 should be 4 bytes");
      return {
        type: "nevent",
        data: {
          id: bytesToHex2(tlv[0][0]),
          relays: tlv[1] ? tlv[1].map((d) => utf8Decoder.decode(d)) : [],
          author: tlv[2]?.[0] ? bytesToHex2(tlv[2][0]) : void 0,
          kind: tlv[3]?.[0] ? parseInt(bytesToHex2(tlv[3][0]), 16) : void 0
        }
      };
    }
    case "naddr": {
      let tlv = parseTLV(data);
      if (!tlv[0]?.[0])
        throw new Error("missing TLV 0 for naddr");
      if (!tlv[2]?.[0])
        throw new Error("missing TLV 2 for naddr");
      if (tlv[2][0].length !== 32)
        throw new Error("TLV 2 should be 32 bytes");
      if (!tlv[3]?.[0])
        throw new Error("missing TLV 3 for naddr");
      if (tlv[3][0].length !== 4)
        throw new Error("TLV 3 should be 4 bytes");
      return {
        type: "naddr",
        data: {
          identifier: utf8Decoder.decode(tlv[0][0]),
          pubkey: bytesToHex2(tlv[2][0]),
          kind: parseInt(bytesToHex2(tlv[3][0]), 16),
          relays: tlv[1] ? tlv[1].map((d) => utf8Decoder.decode(d)) : []
        }
      };
    }
    case "nsec":
      return { type: prefix, data };
    case "npub":
    case "note":
      return { type: prefix, data: bytesToHex2(data) };
    default:
      throw new Error(`unknown prefix ${prefix}`);
  }
}
function parseTLV(data) {
  let result = {};
  let rest = data;
  while (rest.length > 0) {
    let t = rest[0];
    let l = rest[1];
    let v = rest.slice(2, 2 + l);
    rest = rest.slice(2 + l);
    if (v.length < l)
      throw new Error(`not enough data to read on TLV ${t}`);
    result[t] = result[t] || [];
    result[t].push(v);
  }
  return result;
}

// nip29.ts
var GroupAdminPermission = /* @__PURE__ */ ((GroupAdminPermission2) => {
  GroupAdminPermission2["AddUser"] = "add-user";
  GroupAdminPermission2["EditMetadata"] = "edit-metadata";
  GroupAdminPermission2["DeleteEvent"] = "delete-event";
  GroupAdminPermission2["RemoveUser"] = "remove-user";
  GroupAdminPermission2["AddPermission"] = "add-permission";
  GroupAdminPermission2["RemovePermission"] = "remove-permission";
  GroupAdminPermission2["EditGroupStatus"] = "edit-group-status";
  GroupAdminPermission2["PutUser"] = "put-user";
  GroupAdminPermission2["CreateGroup"] = "create-group";
  GroupAdminPermission2["DeleteGroup"] = "delete-group";
  GroupAdminPermission2["CreateInvite"] = "create-invite";
  return GroupAdminPermission2;
})(GroupAdminPermission || {});
function generateGroupMetadataEventTemplate(group) {
  const tags = [["d", group.metadata.id]];
  group.metadata.name && tags.push(["name", group.metadata.name]);
  group.metadata.picture && tags.push(["picture", group.metadata.picture]);
  group.metadata.about && tags.push(["about", group.metadata.about]);
  group.metadata.isPublic && tags.push(["public"]);
  group.metadata.isOpen && tags.push(["open"]);
  return {
    content: "",
    created_at: Math.floor(Date.now() / 1e3),
    kind: 39e3,
    tags
  };
}
function validateGroupMetadataEvent(event) {
  if (event.kind !== 39e3)
    return false;
  if (!event.pubkey)
    return false;
  const requiredTags = ["d"];
  for (const tag of requiredTags) {
    if (!event.tags.find(([t]) => t == tag))
      return false;
  }
  return true;
}
function generateGroupAdminsEventTemplate(group, admins) {
  const tags = [["d", group.metadata.id]];
  for (const admin of admins) {
    tags.push(["p", admin.pubkey, admin.label || "", ...admin.permissions]);
  }
  return {
    content: "",
    created_at: Math.floor(Date.now() / 1e3),
    kind: 39001,
    tags
  };
}
function validateGroupAdminsEvent(event) {
  if (event.kind !== 39001)
    return false;
  const requiredTags = ["d"];
  for (const tag of requiredTags) {
    if (!event.tags.find(([t]) => t == tag))
      return false;
  }
  for (const [tag, _value, _label, ...permissions] of event.tags) {
    if (tag !== "p")
      continue;
    for (let i = 0; i < permissions.length; i += 1) {
      if (typeof permissions[i] !== "string")
        return false;
      if (!Object.values(GroupAdminPermission).includes(permissions[i]))
        return false;
    }
  }
  return true;
}
function generateGroupMembersEventTemplate(group, members) {
  const tags = [["d", group.metadata.id]];
  for (const member of members) {
    tags.push(["p", member.pubkey, member.label || ""]);
  }
  return {
    content: "",
    created_at: Math.floor(Date.now() / 1e3),
    kind: 39002,
    tags
  };
}
function validateGroupMembersEvent(event) {
  if (event.kind !== 39002)
    return false;
  const requiredTags = ["d"];
  for (const tag of requiredTags) {
    if (!event.tags.find(([t]) => t == tag))
      return false;
  }
  return true;
}
function getNormalizedRelayURLByGroupReference(groupReference) {
  return normalizeURL(groupReference.host);
}
async function fetchRelayInformationByGroupReference(groupReference) {
  const normalizedRelayURL = getNormalizedRelayURLByGroupReference(groupReference);
  return fetchRelayInformation(normalizedRelayURL);
}
async function fetchGroupMetadataEvent({
  pool,
  groupReference,
  relayInformation,
  normalizedRelayURL
}) {
  if (!normalizedRelayURL) {
    normalizedRelayURL = getNormalizedRelayURLByGroupReference(groupReference);
  }
  if (!relayInformation) {
    relayInformation = await fetchRelayInformation(normalizedRelayURL);
  }
  const groupMetadataEvent = await pool.get([normalizedRelayURL], {
    kinds: [39e3],
    authors: [relayInformation.pubkey],
    "#d": [groupReference.id]
  });
  if (!groupMetadataEvent)
    throw new Error(`group '${groupReference.id}' not found on ${normalizedRelayURL}`);
  return groupMetadataEvent;
}
function parseGroupMetadataEvent(event) {
  if (!validateGroupMetadataEvent(event))
    throw new Error("invalid group metadata event");
  const metadata = {
    id: "",
    pubkey: event.pubkey
  };
  for (const [tag, value] of event.tags) {
    switch (tag) {
      case "d":
        metadata.id = value;
        break;
      case "name":
        metadata.name = value;
        break;
      case "picture":
        metadata.picture = value;
        break;
      case "about":
        metadata.about = value;
        break;
      case "public":
        metadata.isPublic = true;
        break;
      case "open":
        metadata.isOpen = true;
        break;
    }
  }
  return metadata;
}
async function fetchGroupAdminsEvent({
  pool,
  groupReference,
  relayInformation,
  normalizedRelayURL
}) {
  if (!normalizedRelayURL) {
    normalizedRelayURL = getNormalizedRelayURLByGroupReference(groupReference);
  }
  if (!relayInformation) {
    relayInformation = await fetchRelayInformation(normalizedRelayURL);
  }
  const groupAdminsEvent = await pool.get([normalizedRelayURL], {
    kinds: [39001],
    authors: [relayInformation.pubkey],
    "#d": [groupReference.id]
  });
  if (!groupAdminsEvent)
    throw new Error(`admins for group '${groupReference.id}' not found on ${normalizedRelayURL}`);
  return groupAdminsEvent;
}
function parseGroupAdminsEvent(event) {
  if (!validateGroupAdminsEvent(event))
    throw new Error("invalid group admins event");
  const admins = [];
  for (const [tag, value, label, ...permissions] of event.tags) {
    if (tag !== "p")
      continue;
    admins.push({
      pubkey: value,
      label,
      permissions
    });
  }
  return admins;
}
async function fetchGroupMembersEvent({
  pool,
  groupReference,
  relayInformation,
  normalizedRelayURL
}) {
  if (!normalizedRelayURL) {
    normalizedRelayURL = getNormalizedRelayURLByGroupReference(groupReference);
  }
  if (!relayInformation) {
    relayInformation = await fetchRelayInformation(normalizedRelayURL);
  }
  const groupMembersEvent = await pool.get([normalizedRelayURL], {
    kinds: [39002],
    authors: [relayInformation.pubkey],
    "#d": [groupReference.id]
  });
  if (!groupMembersEvent)
    throw new Error(`members for group '${groupReference.id}' not found on ${normalizedRelayURL}`);
  return groupMembersEvent;
}
function parseGroupMembersEvent(event) {
  if (!validateGroupMembersEvent(event))
    throw new Error("invalid group members event");
  const members = [];
  for (const [tag, value, label] of event.tags) {
    if (tag !== "p")
      continue;
    members.push({
      pubkey: value,
      label
    });
  }
  return members;
}
async function loadGroup({
  pool,
  groupReference,
  normalizedRelayURL,
  relayInformation
}) {
  if (!normalizedRelayURL) {
    normalizedRelayURL = getNormalizedRelayURLByGroupReference(groupReference);
  }
  if (!relayInformation) {
    relayInformation = await fetchRelayInformation(normalizedRelayURL);
  }
  const metadataEvent = await fetchGroupMetadataEvent({ pool, groupReference, normalizedRelayURL, relayInformation });
  const metadata = parseGroupMetadataEvent(metadataEvent);
  const adminsEvent = await fetchGroupAdminsEvent({ pool, groupReference, normalizedRelayURL, relayInformation });
  const admins = parseGroupAdminsEvent(adminsEvent);
  const membersEvent = await fetchGroupMembersEvent({ pool, groupReference, normalizedRelayURL, relayInformation });
  const members = parseGroupMembersEvent(membersEvent);
  const group = {
    relay: normalizedRelayURL,
    metadata,
    admins,
    members,
    reference: groupReference
  };
  return group;
}
async function loadGroupFromCode(pool, code) {
  const groupReference = parseGroupCode(code);
  if (!groupReference)
    throw new Error("invalid group code");
  return loadGroup({ pool, groupReference });
}
function parseGroupCode(code) {
  if (NostrTypeGuard.isNAddr(code)) {
    try {
      let { data } = decode(code);
      let { relays, identifier } = data;
      if (!relays || relays.length === 0)
        return null;
      let host = relays[0];
      if (host.startsWith("wss://")) {
        host = host.slice(6);
      }
      return { host, id: identifier };
    } catch (err) {
      return null;
    }
  } else if (code.split("'").length === 2) {
    let spl = code.split("'");
    return { host: spl[0], id: spl[1] };
  }
  return null;
}
function encodeGroupReference(gr) {
  const { host, id } = gr;
  const normalizedHost = host.replace(/^(https?:\/\/|wss?:\/\/)/, "");
  return `${normalizedHost}'${id}`;
}
function subscribeRelayGroupsMetadataEvents({
  pool,
  relayURL,
  onError,
  onEvent,
  onConnect
}) {
  let sub;
  const normalizedRelayURL = normalizeURL(relayURL);
  fetchRelayInformation(normalizedRelayURL).then(async (info) => {
    const abstractedRelay = await pool.ensureRelay(normalizedRelayURL);
    onConnect?.();
    sub = abstractedRelay.prepareSubscription(
      [
        {
          kinds: [39e3],
          limit: 50,
          authors: [info.pubkey]
        }
      ],
      {
        onevent(event) {
          onEvent(event);
        }
      }
    );
  }).catch((err) => {
    sub.close();
    onError(err);
  });
  return () => sub.close();
}
export {
  GroupAdminPermission,
  encodeGroupReference,
  fetchGroupAdminsEvent,
  fetchGroupMembersEvent,
  fetchGroupMetadataEvent,
  fetchRelayInformationByGroupReference,
  generateGroupAdminsEventTemplate,
  generateGroupMembersEventTemplate,
  generateGroupMetadataEventTemplate,
  getNormalizedRelayURLByGroupReference,
  loadGroup,
  loadGroupFromCode,
  parseGroupAdminsEvent,
  parseGroupCode,
  parseGroupMembersEvent,
  parseGroupMetadataEvent,
  subscribeRelayGroupsMetadataEvents,
  validateGroupAdminsEvent,
  validateGroupMembersEvent,
  validateGroupMetadataEvent
};
