nostr-tools
Version:
Tools for making a Nostr client.
1,621 lines (1,606 loc) • 123 kB
JavaScript
"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// index.ts
var nostr_tools_exports = {};
__export(nostr_tools_exports, {
Relay: () => Relay,
SimplePool: () => SimplePool,
finalizeEvent: () => finalizeEvent,
fj: () => fakejson_exports,
generateSecretKey: () => generateSecretKey,
getEventHash: () => getEventHash,
getFilterLimit: () => getFilterLimit,
getPublicKey: () => getPublicKey,
kinds: () => kinds_exports,
matchFilter: () => matchFilter,
matchFilters: () => matchFilters,
mergeFilters: () => mergeFilters,
nip04: () => nip04_exports,
nip05: () => nip05_exports,
nip10: () => nip10_exports,
nip11: () => nip11_exports,
nip13: () => nip13_exports,
nip17: () => nip17_exports,
nip18: () => nip18_exports,
nip19: () => nip19_exports,
nip21: () => nip21_exports,
nip22: () => nip22_exports,
nip25: () => nip25_exports,
nip27: () => nip27_exports,
nip28: () => nip28_exports,
nip30: () => nip30_exports,
nip39: () => nip39_exports,
nip42: () => nip42_exports,
nip44: () => nip44_exports,
nip47: () => nip47_exports,
nip54: () => nip54_exports,
nip57: () => nip57_exports,
nip59: () => nip59_exports,
nip77: () => nip77_exports,
nip98: () => nip98_exports,
parseReferences: () => parseReferences,
serializeEvent: () => serializeEvent,
sortEvents: () => sortEvents,
utils: () => utils_exports,
validateEvent: () => validateEvent,
verifiedSymbol: () => verifiedSymbol,
verifyEvent: () => verifyEvent
});
module.exports = __toCommonJS(nostr_tools_exports);
// pure.ts
var import_secp256k1 = require("@noble/curves/secp256k1.js");
var import_utils2 = require("@noble/hashes/utils.js");
// core.ts
var verifiedSymbol = Symbol("verified");
var isRecord = (obj) => obj instanceof Object;
function validateEvent(event) {
if (!isRecord(event))
return false;
if (typeof event.kind !== "number")
return false;
if (typeof event.content !== "string")
return false;
if (typeof event.created_at !== "number")
return false;
if (typeof event.pubkey !== "string")
return false;
if (!event.pubkey.match(/^[a-f0-9]{64}$/))
return false;
if (!Array.isArray(event.tags))
return false;
for (let i2 = 0; i2 < event.tags.length; i2++) {
let tag = event.tags[i2];
if (!Array.isArray(tag))
return false;
for (let j = 0; j < tag.length; j++) {
if (typeof tag[j] !== "string")
return false;
}
}
return true;
}
function sortEvents(events) {
return events.sort((a, b) => {
if (a.created_at !== b.created_at) {
return b.created_at - a.created_at;
}
return a.id.localeCompare(b.id);
});
}
// pure.ts
var import_sha2 = require("@noble/hashes/sha2.js");
// utils.ts
var utils_exports = {};
__export(utils_exports, {
binarySearch: () => binarySearch,
bytesToHex: () => import_utils.bytesToHex,
hexToBytes: () => import_utils.hexToBytes,
insertEventIntoAscendingList: () => insertEventIntoAscendingList,
insertEventIntoDescendingList: () => insertEventIntoDescendingList,
mergeReverseSortedLists: () => mergeReverseSortedLists,
normalizeURL: () => normalizeURL,
utf8Decoder: () => utf8Decoder,
utf8Encoder: () => utf8Encoder
});
var import_utils = require("@noble/hashes/utils.js");
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);
if (p.protocol === "http:")
p.protocol = "ws:";
else if (p.protocol === "https:")
p.protocol = "wss:";
p.pathname = p.pathname.replace(/\/+/g, "/");
if (p.pathname.endsWith("/"))
p.pathname = p.pathname.slice(0, -1);
if (p.port === "80" && p.protocol === "ws:" || p.port === "443" && p.protocol === "wss:")
p.port = "";
p.searchParams.sort();
p.hash = "";
return p.toString();
} catch (e) {
throw new Error(`Invalid URL: ${url}`);
}
}
function insertEventIntoDescendingList(sortedArray, event) {
const [idx, found] = binarySearch(sortedArray, (b) => {
if (event.id === b.id)
return 0;
if (event.created_at === b.created_at)
return -1;
return b.created_at - event.created_at;
});
if (!found) {
sortedArray.splice(idx, 0, event);
}
return sortedArray;
}
function insertEventIntoAscendingList(sortedArray, event) {
const [idx, found] = binarySearch(sortedArray, (b) => {
if (event.id === b.id)
return 0;
if (event.created_at === b.created_at)
return -1;
return event.created_at - b.created_at;
});
if (!found) {
sortedArray.splice(idx, 0, event);
}
return sortedArray;
}
function binarySearch(arr, compare) {
let start = 0;
let end = arr.length - 1;
while (start <= end) {
const mid = Math.floor((start + end) / 2);
const cmp = compare(arr[mid]);
if (cmp === 0) {
return [mid, true];
}
if (cmp < 0) {
end = mid - 1;
} else {
start = mid + 1;
}
}
return [start, false];
}
function mergeReverseSortedLists(list1, list2) {
const result = new Array(list1.length + list2.length);
result.length = 0;
let i1 = 0;
let i2 = 0;
let sameTimestampIds = [];
while (i1 < list1.length && i2 < list2.length) {
let next;
if (list1[i1]?.created_at > list2[i2]?.created_at) {
next = list1[i1];
i1++;
} else {
next = list2[i2];
i2++;
}
if (result.length > 0 && result[result.length - 1].created_at === next.created_at) {
if (sameTimestampIds.includes(next.id))
continue;
} else {
sameTimestampIds.length = 0;
}
result.push(next);
sameTimestampIds.push(next.id);
}
while (i1 < list1.length) {
const next = list1[i1];
i1++;
if (result.length > 0 && result[result.length - 1].created_at === next.created_at) {
if (sameTimestampIds.includes(next.id))
continue;
} else {
sameTimestampIds.length = 0;
}
result.push(next);
sameTimestampIds.push(next.id);
}
while (i2 < list2.length) {
const next = list2[i2];
i2++;
if (result.length > 0 && result[result.length - 1].created_at === next.created_at) {
if (sameTimestampIds.includes(next.id))
continue;
} else {
sameTimestampIds.length = 0;
}
result.push(next);
sameTimestampIds.push(next.id);
}
return result;
}
// pure.ts
var JS = class {
generateSecretKey() {
return import_secp256k1.schnorr.utils.randomSecretKey();
}
getPublicKey(secretKey) {
return (0, import_utils2.bytesToHex)(import_secp256k1.schnorr.getPublicKey(secretKey));
}
finalizeEvent(t, secretKey) {
const event = t;
event.pubkey = (0, import_utils2.bytesToHex)(import_secp256k1.schnorr.getPublicKey(secretKey));
event.id = getEventHash(event);
event.sig = (0, import_utils2.bytesToHex)(import_secp256k1.schnorr.sign((0, import_utils2.hexToBytes)(getEventHash(event)), secretKey));
event[verifiedSymbol] = true;
return event;
}
verifyEvent(event) {
if (typeof event[verifiedSymbol] === "boolean")
return event[verifiedSymbol];
try {
const hash = getEventHash(event);
if (hash !== event.id) {
event[verifiedSymbol] = false;
return false;
}
const valid = import_secp256k1.schnorr.verify((0, import_utils2.hexToBytes)(event.sig), (0, import_utils2.hexToBytes)(hash), (0, import_utils2.hexToBytes)(event.pubkey));
event[verifiedSymbol] = valid;
return valid;
} catch (err) {
event[verifiedSymbol] = false;
return false;
}
}
};
function serializeEvent(evt) {
if (!validateEvent(evt))
throw new Error("can't serialize event with wrong or missing properties");
return JSON.stringify([0, evt.pubkey, evt.created_at, evt.kind, evt.tags, evt.content]);
}
function getEventHash(event) {
let eventHash = (0, import_sha2.sha256)(utf8Encoder.encode(serializeEvent(event)));
return (0, import_utils2.bytesToHex)(eventHash);
}
var i = new JS();
var generateSecretKey = i.generateSecretKey;
var getPublicKey = i.getPublicKey;
var finalizeEvent = i.finalizeEvent;
var verifyEvent = i.verifyEvent;
// kinds.ts
var kinds_exports = {};
__export(kinds_exports, {
AIEmbeddings: () => AIEmbeddings,
AppCurationSet: () => AppCurationSet,
Application: () => Application,
AuthoredPodcasts: () => AuthoredPodcasts,
BadgeAward: () => BadgeAward,
BadgeDefinition: () => BadgeDefinition,
Bid: () => Bid,
BidConfirmation: () => BidConfirmation,
BlobsAuth: () => BlobsAuth,
BlockedRelaysList: () => BlockedRelaysList,
BlossomServerList: () => BlossomServerList,
BookmarkList: () => BookmarkList,
Bookmarksets: () => Bookmarksets,
Calendar: () => Calendar,
CalendarEventRSVP: () => CalendarEventRSVP,
CashuMintAnnouncement: () => CashuMintAnnouncement,
CashuWalletEvent: () => CashuWalletEvent,
CashuWalletHistory: () => CashuWalletHistory,
CashuWalletTokens: () => CashuWalletTokens,
ChannelCreation: () => ChannelCreation,
ChannelHideMessage: () => ChannelHideMessage,
ChannelMessage: () => ChannelMessage,
ChannelMetadata: () => ChannelMetadata,
ChannelMuteUser: () => ChannelMuteUser,
ChatMessage: () => ChatMessage,
Chess: () => Chess,
ClassifiedListing: () => ClassifiedListing,
ClientAuth: () => ClientAuth,
CodeSnippet: () => CodeSnippet,
CoinjoinPool: () => CoinjoinPool,
Comment: () => Comment,
CommunitiesList: () => CommunitiesList,
CommunityDefinition: () => CommunityDefinition,
CommunityPostApproval: () => CommunityPostApproval,
ConferenceEvent: () => ConferenceEvent,
Contacts: () => Contacts,
CreateOrUpdateProduct: () => CreateOrUpdateProduct,
CreateOrUpdateStall: () => CreateOrUpdateStall,
CuratedVideoSets: () => CuratedVideoSets,
Curationsets: () => Curationsets,
Date: () => Date2,
DecoupledEncryptionKeyDistribution: () => DecoupledEncryptionKeyDistribution,
DecoupledKeyAnnouncement: () => DecoupledKeyAnnouncement,
DecoupledKeyClientAnnouncement: () => DecoupledKeyClientAnnouncement,
DirectMessageRelaysList: () => DirectMessageRelaysList,
DraftClassifiedListing: () => DraftClassifiedListing,
DraftEvent: () => DraftEvent,
DraftLong: () => DraftLong,
Emojisets: () => Emojisets,
EncryptedDirectMessage: () => EncryptedDirectMessage,
EventDeletion: () => EventDeletion,
FavoritePodcasts: () => FavoritePodcasts,
FavoriteRelays: () => FavoriteRelays,
FedimintAnnouncement: () => FedimintAnnouncement,
Feed: () => Feed,
FileMessage: () => FileMessage,
FileMetadata: () => FileMetadata,
FileServerPreference: () => FileServerPreference,
Followsets: () => Followsets,
ForumThread: () => ForumThread,
GenericRepost: () => GenericRepost,
Genericlists: () => Genericlists,
GeocacheListing: () => GeocacheListing,
GeocacheLog: () => GeocacheLog,
GeocacheLogEntry: () => GeocacheLogEntry,
GeocacheProofOfFind: () => GeocacheProofOfFind,
GiftWrap: () => GiftWrap,
GitPullRequest: () => GitPullRequest,
GitPullRequestUpdate: () => GitPullRequestUpdate,
GoodWikiAuthorList: () => GoodWikiAuthorList,
GoodWikiRelayList: () => GoodWikiRelayList,
GroupMetadata: () => GroupMetadata,
HTTPAuth: () => HTTPAuth,
Handlerinformation: () => Handlerinformation,
Handlerrecommendation: () => Handlerrecommendation,
Highlights: () => Highlights,
InteractiveRoom: () => InteractiveRoom,
InterestsList: () => InterestsList,
Interestsets: () => Interestsets,
Issue: () => Issue,
JobFeedback: () => JobFeedback,
JobRequest: () => JobRequest,
JobResult: () => JobResult,
Label: () => Label,
LegacyNsiteFile: () => LegacyNsiteFile,
LightningPubRPC: () => LightningPubRPC,
LinkSet: () => LinkSet,
LiveChatMessage: () => LiveChatMessage,
LiveEvent: () => LiveEvent,
LongFormArticle: () => LongFormArticle,
MarketplaceUI: () => MarketplaceUI,
MediaFollows: () => MediaFollows,
MediaStarterPacks: () => MediaStarterPacks,
MergeRequests: () => MergeRequests,
Metadata: () => Metadata,
ModularArticleContent: () => ModularArticleContent,
ModularArticleHeader: () => ModularArticleHeader,
MuteSets: () => MuteSets,
Mutelist: () => Mutelist,
NWCWalletInfo: () => NWCWalletInfo,
NWCWalletRequest: () => NWCWalletRequest,
NWCWalletResponse: () => NWCWalletResponse,
NormalVideo: () => NormalVideo,
NostrConnect: () => NostrConnect,
NsiteNamed: () => NsiteNamed,
NsiteRoot: () => NsiteRoot,
NutZap: () => NutZap,
NutZapInfo: () => NutZapInfo,
OpenTimestamps: () => OpenTimestamps,
Patch: () => Patch,
PeerToPeerOrderEvents: () => PeerToPeerOrderEvents,
Photo: () => Photo,
Pinlist: () => Pinlist,
PodcastEpisode: () => PodcastEpisode,
PodcastMetadata: () => PodcastMetadata,
Poll: () => Poll,
PollResponse: () => PollResponse,
PrivateDirectMessage: () => PrivateDirectMessage,
PrivateEventRelayList: () => PrivateEventRelayList,
ProblemTracker: () => ProblemTracker,
ProductSoldAsAuction: () => ProductSoldAsAuction,
ProfileBadges: () => ProfileBadges,
ProxyAnnouncement: () => ProxyAnnouncement,
PublicChatsList: () => PublicChatsList,
PublicMessage: () => PublicMessage,
Reaction: () => Reaction,
ReactionToWebsite: () => ReactionToWebsite,
RecommendRelay: () => RecommendRelay,
Redirects: () => Redirects,
RelayDiscovery: () => RelayDiscovery,
RelayList: () => RelayList,
RelayMonitorAnnouncement: () => RelayMonitorAnnouncement,
RelayReview: () => RelayReview,
RelayReviews: () => RelayReviews,
Relaysets: () => Relaysets,
ReleaseArtifactSets: () => ReleaseArtifactSets,
Reply: () => Reply,
Report: () => Report,
Reporting: () => Reporting,
RepositoryAnnouncement: () => RepositoryAnnouncement,
RepositoryState: () => RepositoryState,
Repost: () => Repost,
ReservedCashuWalletTokens: () => ReservedCashuWalletTokens,
RoomPresence: () => RoomPresence,
Scroll: () => Scroll,
Seal: () => Seal,
SearchRelaysList: () => SearchRelaysList,
ShortTextNote: () => ShortTextNote,
ShortVideo: () => ShortVideo,
SimpleGroupAdmins: () => SimpleGroupAdmins,
SimpleGroupCreateGroup: () => SimpleGroupCreateGroup,
SimpleGroupCreateInvite: () => SimpleGroupCreateInvite,
SimpleGroupDeleteEvent: () => SimpleGroupDeleteEvent,
SimpleGroupDeleteGroup: () => SimpleGroupDeleteGroup,
SimpleGroupEditMetadata: () => SimpleGroupEditMetadata,
SimpleGroupJoinRequest: () => SimpleGroupJoinRequest,
SimpleGroupLeaveRequest: () => SimpleGroupLeaveRequest,
SimpleGroupList: () => SimpleGroupList,
SimpleGroupLiveKitParticipants: () => SimpleGroupLiveKitParticipants,
SimpleGroupMembers: () => SimpleGroupMembers,
SimpleGroupPutUser: () => SimpleGroupPutUser,
SimpleGroupRemoveUser: () => SimpleGroupRemoveUser,
SimpleGroupReply: () => SimpleGroupReply,
SimpleGroupRoles: () => SimpleGroupRoles,
SimpleGroupThreadedReply: () => SimpleGroupThreadedReply,
SlideSet: () => SlideSet,
SoftwareApplication: () => SoftwareApplication,
StarterPacks: () => StarterPacks,
StatusApplied: () => StatusApplied,
StatusClosed: () => StatusClosed,
StatusDraft: () => StatusDraft,
StatusOpen: () => StatusOpen,
TidalLogin: () => TidalLogin,
Time: () => Time,
Torrent: () => Torrent,
TorrentComment: () => TorrentComment,
TransportMethodAnnouncement: () => TransportMethodAnnouncement,
UserEmojiList: () => UserEmojiList,
UserGraspList: () => UserGraspList,
UserStatuses: () => UserStatuses,
VideoViewEvent: () => VideoViewEvent,
Voice: () => Voice,
VoiceComment: () => VoiceComment,
WebBookmarks: () => WebBookmarks,
WikiArticle: () => WikiArticle,
Zap: () => Zap,
ZapGoal: () => ZapGoal,
ZapRequest: () => ZapRequest,
classifyKind: () => classifyKind,
isAddressableKind: () => isAddressableKind,
isEphemeralKind: () => isEphemeralKind,
isKind: () => isKind,
isRegularKind: () => isRegularKind,
isReplaceableKind: () => isReplaceableKind
});
function isRegularKind(kind) {
return kind < 1e4 && kind !== 0 && kind !== 3;
}
function isReplaceableKind(kind) {
return kind === 0 || kind === 3 || 1e4 <= kind && kind < 2e4;
}
function isEphemeralKind(kind) {
return 2e4 <= kind && kind < 3e4;
}
function isAddressableKind(kind) {
return 3e4 <= kind && kind < 4e4;
}
function classifyKind(kind) {
if (isRegularKind(kind))
return "regular";
if (isReplaceableKind(kind))
return "replaceable";
if (isEphemeralKind(kind))
return "ephemeral";
if (isAddressableKind(kind))
return "parameterized";
return "unknown";
}
function isKind(event, kind) {
const kindAsArray = kind instanceof Array ? kind : [kind];
return validateEvent(event) && kindAsArray.includes(event.kind) || false;
}
var Metadata = 0;
var ShortTextNote = 1;
var RecommendRelay = 2;
var Contacts = 3;
var EncryptedDirectMessage = 4;
var EventDeletion = 5;
var Repost = 6;
var Reaction = 7;
var BadgeAward = 8;
var ChatMessage = 9;
var SimpleGroupThreadedReply = 10;
var ForumThread = 11;
var SimpleGroupReply = 12;
var Seal = 13;
var PrivateDirectMessage = 14;
var FileMessage = 15;
var GenericRepost = 16;
var ReactionToWebsite = 17;
var Photo = 20;
var NormalVideo = 21;
var ShortVideo = 22;
var PublicMessage = 24;
var ChannelCreation = 40;
var ChannelMetadata = 41;
var ChannelMessage = 42;
var ChannelHideMessage = 43;
var ChannelMuteUser = 44;
var PodcastEpisode = 54;
var Chess = 64;
var MergeRequests = 818;
var PollResponse = 1018;
var Bid = 1021;
var BidConfirmation = 1022;
var OpenTimestamps = 1040;
var GiftWrap = 1059;
var FileMetadata = 1063;
var Poll = 1068;
var Comment = 1111;
var Voice = 1222;
var Scroll = 1227;
var VoiceComment = 1244;
var LiveChatMessage = 1311;
var CodeSnippet = 1337;
var Patch = 1617;
var GitPullRequest = 1618;
var GitPullRequestUpdate = 1619;
var Issue = 1621;
var Reply = 1622;
var StatusOpen = 1630;
var StatusApplied = 1631;
var StatusClosed = 1632;
var StatusDraft = 1633;
var ProblemTracker = 1971;
var Report = 1984;
var Reporting = 1984;
var Label = 1985;
var RelayReviews = 1986;
var AIEmbeddings = 1987;
var Torrent = 2003;
var TorrentComment = 2004;
var CoinjoinPool = 2022;
var DecoupledKeyClientAnnouncement = 4454;
var DecoupledEncryptionKeyDistribution = 4455;
var CommunityPostApproval = 4550;
var JobRequest = 5999;
var JobResult = 6999;
var JobFeedback = 7e3;
var ReservedCashuWalletTokens = 7374;
var CashuWalletTokens = 7375;
var CashuWalletHistory = 7376;
var GeocacheLog = 7516;
var GeocacheProofOfFind = 7517;
var SimpleGroupPutUser = 9e3;
var SimpleGroupRemoveUser = 9001;
var SimpleGroupEditMetadata = 9002;
var SimpleGroupDeleteEvent = 9005;
var SimpleGroupCreateGroup = 9007;
var SimpleGroupDeleteGroup = 9008;
var SimpleGroupCreateInvite = 9009;
var SimpleGroupJoinRequest = 9021;
var SimpleGroupLeaveRequest = 9022;
var ZapGoal = 9041;
var NutZap = 9321;
var TidalLogin = 9467;
var ZapRequest = 9734;
var Zap = 9735;
var Highlights = 9802;
var Mutelist = 1e4;
var Pinlist = 10001;
var RelayList = 10002;
var BookmarkList = 10003;
var CommunitiesList = 10004;
var PublicChatsList = 10005;
var BlockedRelaysList = 10006;
var SearchRelaysList = 10007;
var SimpleGroupList = 10009;
var FavoriteRelays = 10012;
var PrivateEventRelayList = 10013;
var InterestsList = 10015;
var NutZapInfo = 10019;
var MediaFollows = 10020;
var UserEmojiList = 10030;
var DecoupledKeyAnnouncement = 10044;
var DirectMessageRelaysList = 10050;
var FavoritePodcasts = 10054;
var BlossomServerList = 10063;
var FileServerPreference = 10096;
var GoodWikiAuthorList = 10101;
var GoodWikiRelayList = 10102;
var PodcastMetadata = 10154;
var AuthoredPodcasts = 10164;
var RelayMonitorAnnouncement = 10166;
var RoomPresence = 10312;
var UserGraspList = 10317;
var ProxyAnnouncement = 10377;
var TransportMethodAnnouncement = 11111;
var NWCWalletInfo = 13194;
var NsiteRoot = 15128;
var CashuWalletEvent = 17375;
var LightningPubRPC = 21e3;
var ClientAuth = 22242;
var NWCWalletRequest = 23194;
var NWCWalletResponse = 23195;
var NostrConnect = 24133;
var BlobsAuth = 24242;
var HTTPAuth = 27235;
var Followsets = 3e4;
var Genericlists = 30001;
var Relaysets = 30002;
var Bookmarksets = 30003;
var Curationsets = 30004;
var CuratedVideoSets = 30005;
var MuteSets = 30007;
var ProfileBadges = 30008;
var BadgeDefinition = 30009;
var Interestsets = 30015;
var CreateOrUpdateStall = 30017;
var CreateOrUpdateProduct = 30018;
var MarketplaceUI = 30019;
var ProductSoldAsAuction = 30020;
var LongFormArticle = 30023;
var DraftLong = 30024;
var Emojisets = 30030;
var ModularArticleHeader = 30040;
var ModularArticleContent = 30041;
var ReleaseArtifactSets = 30063;
var Application = 30078;
var RelayDiscovery = 30166;
var AppCurationSet = 30267;
var LiveEvent = 30311;
var InteractiveRoom = 30312;
var ConferenceEvent = 30313;
var UserStatuses = 30315;
var SlideSet = 30388;
var ClassifiedListing = 30402;
var DraftClassifiedListing = 30403;
var RepositoryAnnouncement = 30617;
var RepositoryState = 30618;
var WikiArticle = 30818;
var Redirects = 30819;
var DraftEvent = 31234;
var LinkSet = 31388;
var Feed = 31890;
var Date2 = 31922;
var Time = 31923;
var Calendar = 31924;
var CalendarEventRSVP = 31925;
var RelayReview = 31987;
var Handlerrecommendation = 31989;
var Handlerinformation = 31990;
var SoftwareApplication = 32267;
var LegacyNsiteFile = 34128;
var VideoViewEvent = 34237;
var CommunityDefinition = 34550;
var NsiteNamed = 35128;
var GeocacheListing = 37515;
var GeocacheLogEntry = 37516;
var CashuMintAnnouncement = 38172;
var FedimintAnnouncement = 38173;
var PeerToPeerOrderEvents = 38383;
var GroupMetadata = 39e3;
var SimpleGroupAdmins = 39001;
var SimpleGroupMembers = 39002;
var SimpleGroupRoles = 39003;
var SimpleGroupLiveKitParticipants = 39004;
var StarterPacks = 39089;
var MediaStarterPacks = 39092;
var WebBookmarks = 39701;
// filter.ts
function matchFilter(filter, event) {
if (filter.ids && filter.ids.indexOf(event.id) === -1) {
return false;
}
if (filter.kinds && filter.kinds.indexOf(event.kind) === -1) {
return false;
}
if (filter.authors && filter.authors.indexOf(event.pubkey) === -1) {
return false;
}
for (let f in filter) {
if (f[0] === "#") {
let tagName = f.slice(1);
let values = filter[`#${tagName}`];
if (values && !event.tags.find(([t, v]) => t === f.slice(1) && values.indexOf(v) !== -1))
return false;
}
}
if (filter.since && event.created_at < filter.since)
return false;
if (filter.until && event.created_at > filter.until)
return false;
return true;
}
function matchFilters(filters, event) {
for (let i2 = 0; i2 < filters.length; i2++) {
if (matchFilter(filters[i2], event)) {
return true;
}
}
return false;
}
function mergeFilters(...filters) {
let result = {};
for (let i2 = 0; i2 < filters.length; i2++) {
let filter = filters[i2];
Object.entries(filter).forEach(([property, values]) => {
if (property === "kinds" || property === "ids" || property === "authors" || property[0] === "#") {
result[property] = result[property] || [];
for (let v = 0; v < values.length; v++) {
let value = values[v];
if (!result[property].includes(value))
result[property].push(value);
}
}
});
if (filter.limit && (!result.limit || filter.limit > result.limit))
result.limit = filter.limit;
if (filter.until && (!result.until || filter.until > result.until))
result.until = filter.until;
if (filter.since && (!result.since || filter.since < result.since))
result.since = filter.since;
}
return result;
}
function getFilterLimit(filter) {
if (filter.ids && !filter.ids.length)
return 0;
if (filter.kinds && !filter.kinds.length)
return 0;
if (filter.authors && !filter.authors.length)
return 0;
for (const [key, value] of Object.entries(filter)) {
if (key[0] === "#" && Array.isArray(value) && !value.length)
return 0;
}
return Math.min(
Math.max(0, filter.limit ?? Infinity),
filter.ids?.length ?? Infinity,
filter.authors?.length && filter.kinds?.every((kind) => isReplaceableKind(kind)) ? filter.authors.length * filter.kinds.length : Infinity,
filter.authors?.length && filter.kinds?.every((kind) => isAddressableKind(kind)) && filter["#d"]?.length ? filter.authors.length * filter.kinds.length * filter["#d"].length : Infinity
);
}
// fakejson.ts
var fakejson_exports = {};
__export(fakejson_exports, {
getHex64: () => getHex64,
getInt: () => getInt,
getSubscriptionId: () => getSubscriptionId,
matchEventId: () => matchEventId,
matchEventKind: () => matchEventKind,
matchEventPubkey: () => matchEventPubkey
});
function getHex64(json, field) {
let len = field.length + 3;
let idx = json.indexOf(`"${field}":`) + len;
let s = json.slice(idx).indexOf(`"`) + idx + 1;
return json.slice(s, s + 64);
}
function getInt(json, field) {
let len = field.length;
let idx = json.indexOf(`"${field}":`) + len + 3;
let sliced = json.slice(idx);
let end = Math.min(sliced.indexOf(","), sliced.indexOf("}"));
return parseInt(sliced.slice(0, end), 10);
}
function getSubscriptionId(json) {
let idx = json.slice(0, 22).indexOf(`"EVENT"`);
if (idx === -1)
return null;
let pstart = json.slice(idx + 7 + 1).indexOf(`"`);
if (pstart === -1)
return null;
let start = idx + 7 + 1 + pstart;
let pend = json.slice(start + 1, 80).indexOf(`"`);
if (pend === -1)
return null;
let end = start + 1 + pend;
return json.slice(start + 1, end);
}
function matchEventId(json, id) {
return id === getHex64(json, "id");
}
function matchEventPubkey(json, pubkey) {
return pubkey === getHex64(json, "pubkey");
}
function matchEventKind(json, kind) {
return kind === getInt(json, "kind");
}
// nip42.ts
var nip42_exports = {};
__export(nip42_exports, {
makeAuthEvent: () => makeAuthEvent
});
function makeAuthEvent(relayURL, challenge) {
return {
kind: ClientAuth,
created_at: Math.floor(Date.now() / 1e3),
tags: [
["relay", relayURL],
["challenge", challenge]
],
content: ""
};
}
// abstract-relay.ts
var SendingOnClosedConnection = class extends Error {
constructor(message, relay) {
super(`Tried to send message '${message} on a closed connection to ${relay}.`);
this.name = "SendingOnClosedConnection";
}
};
var AbstractRelay = class {
url;
_connected = false;
onclose = null;
onnotice = (msg) => console.debug(`NOTICE from ${this.url}: ${msg}`);
onauth;
baseEoseTimeout = 4400;
publishTimeout = 4400;
pingFrequency = 29e3;
pingTimeout = 2e4;
resubscribeBackoff = [1e4, 1e4, 1e4, 2e4, 2e4, 3e4, 6e4];
openSubs = /* @__PURE__ */ new Map();
enablePing;
enableReconnect;
idleTimeout = 0;
idleSince = Date.now();
ongoingOperations = 0;
reconnectTimeoutHandle;
pingIntervalHandle;
reconnectAttempts = 0;
skipReconnection = false;
idleTimeoutHandle;
connectionPromise;
openCountRequests = /* @__PURE__ */ new Map();
openEventPublishes = /* @__PURE__ */ new Map();
ws;
challenge;
authPromise;
serial = 0;
verifyEvent;
_WebSocket;
constructor(url, opts) {
this.url = normalizeURL(url);
this.verifyEvent = opts.verifyEvent;
this._WebSocket = opts.websocketImplementation || WebSocket;
this.enablePing = opts.enablePing;
this.enableReconnect = opts.enableReconnect || false;
if (opts.idleTimeout)
this.idleTimeout = opts.idleTimeout;
}
static async connect(url, opts) {
const relay = new AbstractRelay(url, opts);
await relay.connect(opts);
return relay;
}
closeAllSubscriptions(reason) {
for (let [_, sub] of this.openSubs) {
sub.close(reason);
}
this.openSubs.clear();
for (let [_, ep] of this.openEventPublishes) {
ep.reject(new Error(reason));
}
this.openEventPublishes.clear();
for (let [_, cr] of this.openCountRequests) {
cr.reject(new Error(reason));
}
this.openCountRequests.clear();
}
get connected() {
return this._connected;
}
clearIdleTimeout() {
if (this.idleTimeoutHandle) {
clearTimeout(this.idleTimeoutHandle);
this.idleTimeoutHandle = void 0;
}
}
scheduleIdleClose() {
this.clearIdleTimeout();
if (this.idleTimeout > 0) {
this.idleTimeoutHandle = setTimeout(() => {
if (this.ongoingOperations === 0 && this.idleSince) {
this.close();
}
}, this.idleTimeout);
}
}
async reconnect() {
const backoff = this.resubscribeBackoff[Math.min(this.reconnectAttempts, this.resubscribeBackoff.length - 1)];
this.reconnectAttempts++;
this.reconnectTimeoutHandle = setTimeout(async () => {
try {
await this.connect();
} catch (err) {
}
}, backoff);
}
handleHardClose(reason) {
if (this.pingIntervalHandle) {
clearInterval(this.pingIntervalHandle);
this.pingIntervalHandle = void 0;
}
this._connected = false;
this.connectionPromise = void 0;
this.idleSince = void 0;
this.clearIdleTimeout();
if (this.enableReconnect && !this.skipReconnection) {
this.reconnect();
} else {
this.onclose?.();
this.closeAllSubscriptions(reason);
}
}
async connect(opts) {
let connectionTimeoutHandle;
if (this.connectionPromise)
return this.connectionPromise;
this.challenge = void 0;
this.authPromise = void 0;
this.skipReconnection = false;
this.connectionPromise = new Promise((resolve, reject) => {
if (opts?.timeout) {
connectionTimeoutHandle = setTimeout(() => {
reject("connection timed out");
this.connectionPromise = void 0;
if (this.reconnectAttempts === 0) {
this.skipReconnection = true;
}
this.onclose?.();
this.handleHardClose("relay connection timed out");
}, opts.timeout);
}
if (opts?.abort) {
opts.abort.onabort = reject;
}
try {
this.ws = new this._WebSocket(this.url);
} catch (err) {
clearTimeout(connectionTimeoutHandle);
reject(err);
return;
}
this.ws.onopen = () => {
if (this.reconnectTimeoutHandle) {
clearTimeout(this.reconnectTimeoutHandle);
this.reconnectTimeoutHandle = void 0;
}
clearTimeout(connectionTimeoutHandle);
this._connected = true;
const isReconnection = this.reconnectAttempts > 0;
this.reconnectAttempts = 0;
for (const sub of this.openSubs.values()) {
sub.eosed = false;
if (isReconnection) {
for (let f = 0; f < sub.filters.length; f++) {
if (sub.lastEmitted) {
sub.filters[f].since = sub.lastEmitted + 1;
}
}
}
sub.fire();
}
if (this.enablePing) {
this.pingIntervalHandle = setInterval(() => this.pingpong(), this.pingFrequency);
}
resolve();
};
this.ws.onerror = () => {
clearTimeout(connectionTimeoutHandle);
reject("connection failed");
this.connectionPromise = void 0;
if (this.reconnectAttempts === 0) {
this.skipReconnection = true;
}
this.onclose?.();
this.handleHardClose("relay connection failed");
};
this.ws.onclose = (ev) => {
clearTimeout(connectionTimeoutHandle);
reject(ev.message || "websocket closed");
this.handleHardClose("relay connection closed");
};
this.ws.onmessage = this._onmessage.bind(this);
});
return this.connectionPromise;
}
waitForPingPong() {
return new Promise((resolve) => {
;
this.ws.once("pong", () => resolve(true));
this.ws.ping();
});
}
waitForDummyReq() {
return new Promise((resolve, reject) => {
if (!this.connectionPromise)
return reject(new Error(`no connection to ${this.url}, can't ping`));
try {
const sub = this.subscribe(
[{ ids: ["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"], limit: 0 }],
{
label: "<forced-ping>",
oneose: () => {
resolve(true);
sub.close();
},
onclose() {
resolve(true);
},
eoseTimeout: this.pingTimeout + 1e3
}
);
} catch (err) {
reject(err);
}
});
}
async pingpong() {
if (this.ws?.readyState === 1) {
const result = await Promise.any([
this.ws && this.ws.ping && this.ws.once ? this.waitForPingPong() : this.waitForDummyReq(),
new Promise((res) => setTimeout(() => res(false), this.pingTimeout))
]);
if (!result) {
if (this.ws?.readyState === this._WebSocket.OPEN) {
this.ws?.close();
}
}
}
}
async send(message) {
if (!this.connectionPromise)
throw new SendingOnClosedConnection(message, this.url);
this.connectionPromise.then(() => {
this.ws?.send(message);
});
}
async auth(signAuthEvent) {
const challenge = this.challenge;
if (!challenge)
throw new Error("can't perform auth, no challenge was received");
if (this.authPromise)
return this.authPromise;
this.authPromise = new Promise(async (resolve, reject) => {
try {
let evt = await signAuthEvent(makeAuthEvent(this.url, challenge));
let timeout = setTimeout(() => {
let ep = this.openEventPublishes.get(evt.id);
if (ep) {
ep.reject(new Error("auth timed out"));
this.openEventPublishes.delete(evt.id);
}
}, this.publishTimeout);
this.openEventPublishes.set(evt.id, { resolve, reject, timeout });
this.send('["AUTH",' + JSON.stringify(evt) + "]");
} catch (err) {
console.warn("subscribe auth function failed:", err);
}
});
return this.authPromise;
}
async publish(event) {
this.idleSince = void 0;
this.clearIdleTimeout();
this.ongoingOperations++;
const ret = new Promise((resolve, reject) => {
const timeout = setTimeout(() => {
const ep = this.openEventPublishes.get(event.id);
if (ep) {
ep.reject(new Error("publish timed out"));
this.openEventPublishes.delete(event.id);
}
}, this.publishTimeout);
this.openEventPublishes.set(event.id, { resolve, reject, timeout });
});
try {
await this.send('["EVENT",' + JSON.stringify(event) + "]");
} catch (err) {
const ep = this.openEventPublishes.get(event.id);
if (ep) {
ep.reject(err);
this.openEventPublishes.delete(event.id);
}
}
this.ongoingOperations--;
if (this.ongoingOperations === 0) {
this.idleSince = Date.now();
this.scheduleIdleClose();
}
return ret;
}
async count(filters, params) {
return (await this.countWithHLL(filters, params)).count;
}
async countWithHLL(filters, params) {
this.serial++;
const id = params?.id || "count:" + this.serial;
const ret = new Promise((resolve, reject) => {
this.openCountRequests.set(id, { resolve, reject });
});
try {
await this.send('["COUNT","' + id + '",' + JSON.stringify(filters).substring(1));
} catch (err) {
const cr = this.openCountRequests.get(id);
if (cr) {
cr.reject(err);
this.openCountRequests.delete(id);
}
}
return ret;
}
subscribe(filters, params) {
if (params.label !== "<forced-ping>") {
this.idleSince = void 0;
this.clearIdleTimeout();
this.ongoingOperations++;
}
const sub = this.prepareSubscription(filters, params);
sub.fire();
if (params.abort) {
params.abort.onabort = () => sub.close(String(params.abort.reason || "<aborted>"));
}
return sub;
}
prepareSubscription(filters, params) {
this.serial++;
const id = params.id || (params.label ? params.label + ":" : "sub:") + this.serial;
const sub = new Subscription(this, id, filters, params);
this.openSubs.set(id, sub);
return sub;
}
close() {
this.skipReconnection = true;
if (this.reconnectTimeoutHandle) {
clearTimeout(this.reconnectTimeoutHandle);
this.reconnectTimeoutHandle = void 0;
}
if (this.pingIntervalHandle) {
clearInterval(this.pingIntervalHandle);
this.pingIntervalHandle = void 0;
}
this.closeAllSubscriptions("relay connection closed by us");
this._connected = false;
this.idleSince = void 0;
this.clearIdleTimeout();
this.onclose?.();
if (this.ws?.readyState === this._WebSocket.OPEN) {
this.ws?.close();
}
}
_onmessage(ev) {
const json = ev.data;
if (!json) {
return;
}
const subid = getSubscriptionId(json);
if (subid) {
const so = this.openSubs.get(subid);
if (!so) {
return;
}
const id = getHex64(json, "id");
const alreadyHave = so.alreadyHaveEvent?.(id);
so.receivedEvent?.(this, id);
if (alreadyHave) {
return;
}
}
try {
let data = JSON.parse(json);
switch (data[0]) {
case "EVENT": {
const so = this.openSubs.get(data[1]);
const event = data[2];
if (this.verifyEvent(event) && matchFilters(so.filters, event)) {
so.onevent(event);
} else {
so.oninvalidevent?.(event);
}
if (!so.lastEmitted || so.lastEmitted < event.created_at)
so.lastEmitted = event.created_at;
return;
}
case "COUNT": {
const id = data[1];
const payload = data[2];
const cr = this.openCountRequests.get(id);
if (cr) {
cr.resolve(payload);
this.openCountRequests.delete(id);
}
return;
}
case "EOSE": {
const so = this.openSubs.get(data[1]);
if (!so)
return;
so.receivedEose();
return;
}
case "OK": {
const id = data[1];
const ok = data[2];
const reason = data[3];
const ep = this.openEventPublishes.get(id);
if (ep) {
clearTimeout(ep.timeout);
if (ok)
ep.resolve(reason);
else
ep.reject(new Error(reason));
this.openEventPublishes.delete(id);
}
return;
}
case "CLOSED": {
const id = data[1];
const so = this.openSubs.get(id);
if (!so) {
const cr = this.openCountRequests.get(id);
if (cr) {
cr.reject(new Error(data[2]));
this.openCountRequests.delete(id);
}
return;
}
so.closed = true;
so.close(data[2]);
return;
}
case "NOTICE": {
this.onnotice(data[1]);
return;
}
case "AUTH": {
this.challenge = data[1];
if (this.onauth) {
this.auth(this.onauth).catch((err) => {
if (!(err instanceof SendingOnClosedConnection)) {
throw err;
}
});
}
return;
}
default: {
const so = this.openSubs.get(data[1]);
so?.oncustom?.(data);
return;
}
}
} catch (err) {
try {
const [_, __, event] = JSON.parse(json);
console.warn(`[nostr] relay ${this.url} error processing message:`, err, event);
} catch (_) {
console.warn(`[nostr] relay ${this.url} error processing message:`, err);
}
return;
}
}
};
var Subscription = class {
relay;
id;
lastEmitted;
closed = false;
eosed = false;
filters;
alreadyHaveEvent;
receivedEvent;
onevent;
oninvalidevent;
oneose;
onclose;
oncustom;
eoseTimeout;
eoseTimeoutHandle;
constructor(relay, id, filters, params) {
if (filters.length === 0)
throw new Error("subscription can't be created with zero filters");
this.relay = relay;
this.filters = filters;
this.id = id;
this.alreadyHaveEvent = params.alreadyHaveEvent;
this.receivedEvent = params.receivedEvent;
this.eoseTimeout = params.eoseTimeout || relay.baseEoseTimeout;
this.oneose = params.oneose;
this.onclose = params.onclose;
this.oninvalidevent = params.oninvalidevent;
this.onevent = params.onevent || ((event) => {
console.warn(
`onevent() callback not defined for subscription '${this.id}' in relay ${this.relay.url}. event received:`,
event
);
});
}
fire() {
this.relay.send('["REQ","' + this.id + '",' + JSON.stringify(this.filters).substring(1));
this.eoseTimeoutHandle = setTimeout(this.receivedEose.bind(this), this.eoseTimeout);
}
receivedEose() {
if (this.eosed)
return;
clearTimeout(this.eoseTimeoutHandle);
this.eosed = true;
this.oneose?.();
}
close(reason = "closed by caller") {
if (!this.closed && this.relay.connected) {
try {
this.relay.send('["CLOSE",' + JSON.stringify(this.id) + "]");
} catch (err) {
if (err instanceof SendingOnClosedConnection) {
} else {
throw err;
}
}
this.closed = true;
}
this.relay.openSubs.delete(this.id);
this.relay.ongoingOperations--;
if (this.relay.ongoingOperations === 0) {
this.relay.idleSince = Date.now();
this.relay.scheduleIdleClose();
}
this.onclose?.(reason);
}
};
// relay.ts
var _WebSocket;
try {
_WebSocket = WebSocket;
} catch {
}
var Relay = class extends AbstractRelay {
constructor(url, options) {
super(url, { verifyEvent, websocketImplementation: _WebSocket, ...options });
}
static async connect(url, options) {
const relay = new Relay(url, options);
await relay.connect();
return relay;
}
};
// helpers.ts
var alwaysTrue = (t) => {
t[verifiedSymbol] = true;
return true;
};
// nip45.ts
var import_sha22 = require("@noble/hashes/sha2.js");
var import_utils5 = require("@noble/hashes/utils.js");
var M = 256;
var HLL_HEX_LENGTH = M * 2;
var utf8Encoder2 = new TextEncoder();
function getCountManyFilter(target, directive) {
switch (directive) {
case "reactions":
return { "#e": [target], kinds: [7] };
case "reposts":
return { "#e": [target], kinds: [6] };
case "quotes":
return { "#q": [target], kinds: [1, 1111] };
case "replies":
return { "#e": [target], kinds: [1] };
case "comments":
return { "#E": [target], kinds: [1111] };
case "followers":
return { "#p": [target], kinds: [3] };
}
}
function newHll() {
return new Uint8Array(M);
}
function hllDecode(hex) {
if (hex.length !== HLL_HEX_LENGTH || !/^[0-9a-fA-F]+$/.test(hex))
return void 0;
const registers = new Uint8Array(M);
for (let i2 = 0; i2 < M; i2++) {
registers[i2] = parseInt(hex.slice(i2 * 2, i2 * 2 + 2), 16);
}
return registers;
}
function hllEncode(registers) {
if (registers.length !== M)
throw new Error(`invalid number of registers ${registers.length}`);
let hex = "";
for (let i2 = 0; i2 < M; i2++) {
hex += registers[i2].toString(16).padStart(2, "0");
}
return hex;
}
function mergeHll(target, source) {
if (target.length === 0)
target = newHll();
if (target.length !== M)
throw new Error(`invalid number of registers ${target.length}`);
if (source.length !== M)
throw new Error(`invalid number of registers ${source.length}`);
for (let i2 = 0; i2 < M; i2++) {
if (source[i2] > target[i2])
target[i2] = source[i2];
}
return target;
}
// abstract-pool.ts
var AbstractSimplePool = class {
relays = /* @__PURE__ */ new Map();
seenOn = /* @__PURE__ */ new Map();
trackRelays = false;
verifyEvent;
enablePing;
enableReconnect;
idleTimeout = 2e4;
automaticallyAuth;
trustedRelayURLs = /* @__PURE__ */ new Set();
onRelayConnectionFailure;
onRelayConnectionSuccess;
allowConnectingToRelay;
maxWaitForConnection;
_WebSocket;
constructor(opts) {
this.verifyEvent = opts.verifyEvent;
this._WebSocket = opts.websocketImplementation;
this.enablePing = opts.enablePing;
this.enableReconnect = opts.enableReconnect || false;
if (opts.idleTimeout)
this.idleTimeout = opts.idleTimeout;
this.automaticallyAuth = opts.automaticallyAuth;
this.onRelayConnectionFailure = opts.onRelayConnectionFailure;
this.onRelayConnectionSuccess = opts.onRelayConnectionSuccess;
this.allowConnectingToRelay = opts.allowConnectingToRelay;
this.maxWaitForConnection = opts.maxWaitForConnection || 3e3;
}
async ensureRelay(url, params) {
url = normalizeURL(url);
let relay = this.relays.get(url);
if (!relay) {
relay = new AbstractRelay(url, {
verifyEvent: this.trustedRelayURLs.has(url) ? alwaysTrue : this.verifyEvent,
websocketImplementation: this._WebSocket,
enablePing: this.enablePing,
enableReconnect: this.enableReconnect,
idleTimeout: this.idleTimeout
});
relay.onclose = () => {
this.relays.delete(url);
};
this.relays.set(url, relay);
}
if (this.automaticallyAuth) {
const authSignerFn = this.automaticallyAuth(url);
if (authSignerFn) {
relay.onauth = authSignerFn;
}
}
try {
await relay.connect({
timeout: params?.connectionTimeout,
abort: params?.abort
});
} catch (err) {
this.relays.delete(url);
throw err;
}
return relay;
}
close(relays) {
relays.map(normalizeURL).forEach((url) => {
this.relays.get(url)?.close();
this.relays.delete(url);
});
}
subscribe(relays, filter, params) {
const request = [];
const uniqUrls = [];
for (let i2 = 0; i2 < relays.length; i2++) {
const url = normalizeURL(relays[i2]);
if (!request.find((r) => r.url === url)) {
if (uniqUrls.indexOf(url) === -1) {
uniqUrls.push(url);
request.push({ url, filter });
}
}
}
return this.subscribeMap(request, params);
}
subscribeMany(relays, filter, params) {
return this.subscribe(relays, filter, params);
}
subscribeMap(requests, params) {
const grouped = /* @__PURE__ */ new Map();
for (const req of requests) {
const { url, filter } = req;
if (!grouped.has(url))
grouped.set(url, []);
grouped.get(url).push(filter);
}
const groupedRequests = Array.from(grouped.entries()).map(([url, filters]) => ({ url, filters }));
if (this.trackRelays) {
params.receivedEvent = (relay, id) => {
let set = this.seenOn.get(id);
if (!set) {
set = /* @__PURE__ */ new Set();
this.seenOn.set(id, set);
}
set.add(relay);
};
}
const _knownIds = /* @__PURE__ */ new Set();
const subs = [];
const eosesReceived = [];
let handleEose = (i2) => {
if (eosesReceived[i2])
return;
eosesReceived[i2] = true;
if (eosesReceived.filter((a) => a).length === groupedRequests.length) {
params.oneose?.();
handleEose = () => {
};
}
};
const closesReceived = [];
let handleClose = (i2, reason) => {
if (closesReceived[i2])
return;
handleEose(i2);
closesReceived[i2] = reason;
if (closesReceived.filter((a) => a).length === groupedRequests.length) {
params.onclose?.(closesReceived);
handleClose = () => {
};
}
};
const localAlreadyHaveEventHandler = (id) => {
if (params.alreadyHaveEvent?.(id)) {
return true;
}
const have = _knownIds.has(id);
_knownIds.add(id);
return have;
};
const allOpened = Promise.all(
groupedRequests.map(async ({ url, filters }, i2) => {
if (this.allowConnectingToRelay?.(url, ["read", filters]) === false) {
handleClose(i2, "connection skipped by allowConnectingToRelay");
return;
}
let relay;
try {
relay = await this.ensureRelay(url, {
connectionTimeout: this.maxWaitForConnection < (params.maxWait || 0) ? Math.max(params.maxWait * 0.8, params.maxWait - 1e3) : this.maxWaitForConnection,
abort: params.abort
});
} catch (err) {
this.onRelayConnectionFailure?.(url);
handleClose(i2, err?.message || String(err));
return;
}
this.onRelayConnectionSuccess?.(url);
let subscription = relay.subscribe(filters, {
...params,
oneose: () => handleEose(i2),
onclose: (reason) => {
if (reason.startsWith("auth-required: ") && params.onauth) {
relay.auth(params.onauth).then(() => {
relay.subscribe(filters, {
...params,
oneose: () => handleEose(i2),
onclose: (reason2) => {
handleClose(i2, reason2);
},
alreadyHaveEvent: l