@nostr-dev-kit/ndk
Version:
NDK - Nostr Development Kit
1,438 lines (1,413 loc) • 114 kB
TypeScript
import { EventEmitter as EventEmitter$1 } from 'node:events';
import { EventEmitter } from 'tseep';
import * as nostr_tools_lib_types_nip19_js from 'nostr-tools/lib/types/nip19.js';
import debug$1 from 'debug';
import { LRUCache } from 'typescript-lru-cache';
declare enum NDKKind {
Metadata = 0,
Text = 1,
RecommendRelay = 2,
Contacts = 3,
EncryptedDirectMessage = 4,
EventDeletion = 5,
Repost = 6,
Reaction = 7,
BadgeAward = 8,
GroupChat = 9,
Thread = 11,
GroupReply = 12,
GiftWrapSeal = 13,
PrivateDirectMessage = 14,
Image = 20,
Video = 21,
ShortVideo = 22,
Story = 23,
Vanish = 62,
CashuWalletBackup = 375,
GiftWrap = 1059,
GenericRepost = 16,
ChannelCreation = 40,
ChannelMetadata = 41,
ChannelMessage = 42,
ChannelHideMessage = 43,
ChannelMuteUser = 44,
WikiMergeRequest = 818,
GenericReply = 1111,
Media = 1063,
DraftCheckpoint = 1234,
Task = 1934,
Report = 1984,
Label = 1985,
DVMReqTextExtraction = 5000,
DVMReqTextSummarization = 5001,
DVMReqTextTranslation = 5002,
DVMReqTextGeneration = 5050,
DVMReqImageGeneration = 5100,
DVMReqTextToSpeech = 5250,
DVMReqDiscoveryNostrContent = 5300,
DVMReqDiscoveryNostrPeople = 5301,
DVMReqTimestamping = 5900,
DVMEventSchedule = 5905,
DVMJobFeedback = 7000,
Subscribe = 7001,
Unsubscribe = 7002,
SubscriptionReceipt = 7003,
CashuReserve = 7373,
CashuQuote = 7374,
CashuToken = 7375,
CashuWalletTx = 7376,
GroupAdminAddUser = 9000,
GroupAdminRemoveUser = 9001,
GroupAdminEditMetadata = 9002,
GroupAdminEditStatus = 9006,
GroupAdminCreateGroup = 9007,
GroupAdminRequestJoin = 9021,
MuteList = 10000,
PinList = 10001,
RelayList = 10002,
BookmarkList = 10003,
CommunityList = 10004,
PublicChatList = 10005,
BlockRelayList = 10006,
SearchRelayList = 10007,
SimpleGroupList = 10009,
InterestList = 10015,
CashuMintList = 10019,
EmojiList = 10030,
DirectMessageReceiveRelayList = 10050,
BlossomList = 10063,
NostrWaletConnectInfo = 13194,
TierList = 17000,
CashuWallet = 17375,
FollowSet = 30000,
CategorizedPeopleList = 30000,// Deprecated but left for backwards compatibility
CategorizedBookmarkList = 30001,// Deprecated but left for backwards compatibility
RelaySet = 30002,
CategorizedRelayList = 30002,// Deprecated but left for backwards compatibility
BookmarkSet = 30003,
/**
* @deprecated Use ArticleCurationSet instead
*/
CurationSet = 30004,// Deprecated but left for backwards compatibility
ArticleCurationSet = 30004,
VideoCurationSet = 30005,
ImageCurationSet = 30006,
InterestSet = 30015,
InterestsList = 30015,// Deprecated but left for backwards compatibility
ProjectTemplate = 30717,
EmojiSet = 30030,
ModularArticle = 30040,
ModularArticleItem = 30041,
Wiki = 30818,
Draft = 31234,
Project = 31933,
SubscriptionTier = 37001,
EcashMintRecommendation = 38000,
HighlightSet = 39802,
CategorizedHighlightList = 39802,// Deprecated but left for backwards compatibility
Nutzap = 9321,
ZapRequest = 9734,
Zap = 9735,
Highlight = 9802,
ClientAuth = 22242,
NostrWalletConnectReq = 23194,
NostrWalletConnectRes = 23195,
NostrConnect = 24133,
BlossomUpload = 24242,
HttpAuth = 27235,
ProfileBadge = 30008,
BadgeDefinition = 30009,
MarketStall = 30017,
MarketProduct = 30018,
Article = 30023,
AppSpecificData = 30078,
Classified = 30402,
HorizontalVideo = 34235,
VerticalVideo = 34236,
LegacyCashuWallet = 37375,
GroupMetadata = 39000,// NIP-29
GroupAdmins = 39001,// NIP-29
GroupMembers = 39002,// NIP-29
FollowPack = 39089,
MediaFollowPack = 39092,
AppRecommendation = 31989,
AppHandler = 31990
}
type NDKEncryptionScheme = "nip04" | "nip44";
declare enum NdkNutzapStatus {
INITIAL = "initial",
PROCESSING = "processing",
REDEEMED = "redeemed",
SPENT = "spent",
MISSING_PRIVKEY = "missing_privkey",
TEMPORARY_ERROR = "temporary_error",
PERMANENT_ERROR = "permanent_error",
INVALID_NUTZAP = "invalid_nutzap"
}
interface NDKNutzapState {
nutzap?: NDKNutzap;
status: NdkNutzapStatus;
redeemedById?: NDKEventId;
errorMessage?: string;
redeemedAmount?: number;
}
type NDKPoolStats = {
total: number;
connected: number;
disconnected: number;
connecting: number;
};
/**
* Handles connections to all relays. A single pool should be used per NDK instance.
*
* @emit connecting - Emitted when a relay in the pool is connecting.
* @emit connect - Emitted when all relays in the pool are connected, or when the specified timeout has elapsed, and some relays are connected.
* @emit notice - Emitted when a relay in the pool sends a notice.
* @emit flapping - Emitted when a relay in the pool is flapping.
* @emit relay:connect - Emitted when a relay in the pool connects.
* @emit relay:ready - Emitted when a relay in the pool is ready to serve requests.
* @emit relay:disconnect - Emitted when a relay in the pool disconnects.
*/
declare class NDKPool extends EventEmitter<{
notice: (relay: NDKRelay, notice: string) => void;
flapping: (relay: NDKRelay) => void;
connect: () => void;
"relay:connecting": (relay: NDKRelay) => void;
/**
* Emitted when a relay in the pool connects.
* @param relay - The relay that connected.
*/
"relay:connect": (relay: NDKRelay) => void;
"relay:ready": (relay: NDKRelay) => void;
"relay:disconnect": (relay: NDKRelay) => void;
"relay:auth": (relay: NDKRelay, challenge: string) => void;
"relay:authed": (relay: NDKRelay) => void;
}> {
private _relays;
private status;
autoConnectRelays: Set<string>;
poolBlacklistRelayUrls: Set<string>;
private debug;
private temporaryRelayTimers;
private flappingRelays;
private backoffTimes;
private ndk;
private disconnectionTimes;
private systemEventDetector?;
get blacklistRelayUrls(): Set<string>;
/**
* @param relayUrls - The URLs of the relays to connect to.
* @param blacklistedRelayUrls - URLs to blacklist for this pool IN ADDITION to those blacklisted at the ndk-level
* @param ndk - The NDK instance.
* @param opts - Options for the pool.
*/
constructor(relayUrls: WebSocket["url"][], blacklistedRelayUrls: WebSocket["url"][], ndk: NDK, { debug, name, }?: {
debug?: debug$1.Debugger;
name?: string;
});
get relays(): Map<string, NDKRelay>;
set relayUrls(urls: WebSocket["url"][]);
private _name;
get name(): string;
set name(name: string);
/**
* 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: NDKRelay, removeIfUnusedAfter?: number, filters?: NDKFilter[] | string): void;
/**
* 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: NDKRelay, connect?: boolean): void;
/**
* 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: string): boolean;
/**
* Checks whether a relay is already connected in the pool.
*/
isRelayConnected(url: WebSocket["url"]): boolean;
/**
* 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: WebSocket["url"], connect?: boolean, temporary?: boolean, filters?: NDKFilter[]): NDKRelay;
private handleRelayConnect;
private handleRelayReady;
/**
* 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.
*/
connect(timeoutMs?: number): Promise<void>;
private checkOnFlappingRelays;
/**
* Records when a relay disconnects to detect system-wide events
*/
private recordDisconnection;
/**
* Checks if multiple relays disconnected simultaneously, indicating a system event
*/
private checkForSystemWideDisconnection;
/**
* Handles system-wide reconnection (e.g., after sleep/wake or network change)
*/
private handleSystemWideReconnection;
private handleFlapping;
size(): number;
/**
* Returns the status of each relay in the pool.
* @returns {NDKPoolStats} An object containing the number of relays in each status.
*/
stats(): NDKPoolStats;
connectedRelays(): NDKRelay[];
permanentAndConnectedRelays(): NDKRelay[];
/**
* Get a list of all relay urls in the pool.
*/
urls(): string[];
}
/**
* NDKUserProfile represents a user's kind 0 profile metadata
*/
interface NDKUserProfile {
[key: string]: string | number | undefined;
created_at?: number;
name?: string;
displayName?: string;
/**
* @deprecated Use picture instead
*/
image?: string;
picture?: string;
banner?: string;
bio?: string;
nip05?: string;
lud06?: string;
lud16?: string;
about?: string;
website?: string;
profileEvent?: string;
}
type NDKPaymentConfirmationLN = {
preimage: string;
};
type LNPaymentRequest = string;
type LnPaymentInfo = {
pr: LNPaymentRequest;
};
type NDKLUD18ServicePayerData = Partial<{
name: {
mandatory: boolean;
};
pubkey: {
mandatory: boolean;
};
identifier: {
mandatory: boolean;
};
email: {
mandatory: boolean;
};
auth: {
mandatory: boolean;
k1: string;
};
}> & Record<string, unknown>;
type NDKLnUrlData = {
tag: string;
callback: string;
minSendable: number;
maxSendable: number;
metadata: string;
payerData?: NDKLUD18ServicePayerData;
commentAllowed?: number;
/**
* Pubkey of the zapper that should publish zap receipts for this user
*/
nostrPubkey?: Hexpubkey;
allowsNostr?: boolean;
};
type NDKCacheEntry<T> = T & {
cachedAt?: number;
};
interface NDKCacheAdapter {
/**
* Whether this cache adapter is expected to be fast.
* If this is true, the cache will be queried before the relays.
* When this is false, the cache will be queried in addition to the relays.
*/
locking: boolean;
/**
* Weather the cache is ready.
*/
ready?: boolean;
initializeAsync?(ndk: NDK): Promise<void>;
initialize?(ndk: NDK): void;
/**
* Either synchronously or asynchronously queries the cache.
*
* Cache adapters that return values synchronously should return an array of events.
* Asynchronous cache adapters should call the subscription.eventReceived method for each event.
*/
query(subscription: NDKSubscription): NDKEvent[] | Promise<NDKEvent[]>;
setEvent(event: NDKEvent, filters: NDKFilter[], relay?: NDKRelay): Promise<void>;
/**
* Called when an event is deleted by the client.
* Cache adapters should remove the event from their cache.
* @param eventIds - The ids of the events that were deleted.
*/
deleteEventIds?(eventIds: NDKEventId[]): Promise<void>;
/**
* Fetches a profile from the cache synchronously.
* @param pubkey - The pubkey of the profile to fetch.
* @returns The profile, or null if it is not in the cache.
*/
fetchProfileSync?(pubkey: Hexpubkey): NDKCacheEntry<NDKUserProfile> | null;
/**
* Retrieve all profiles from the cache synchronously.
* @returns A map of pubkeys to profiles.
*/
getAllProfilesSync?(): Map<Hexpubkey, NDKCacheEntry<NDKUserProfile>>;
/**
* Special purpose
*/
fetchProfile?(pubkey: Hexpubkey): Promise<NDKCacheEntry<NDKUserProfile> | null>;
saveProfile?(pubkey: Hexpubkey, profile: NDKUserProfile): void;
/**
* Fetches profiles that match the given filter.
* @param filter
* @returns NDKUserProfiles that match the filter.
* @example
* const searchFunc = (pubkey, profile) => profile.name.toLowerCase().includes("alice");
* const allAliceProfiles = await cache.getProfiles(searchFunc);
*/
getProfiles?: (filter: (pubkey: Hexpubkey, profile: NDKUserProfile) => boolean) => Promise<Map<Hexpubkey, NDKUserProfile> | undefined>;
loadNip05?(nip05: string, maxAgeForMissing?: number): Promise<ProfilePointer | null | "missing">;
saveNip05?(nip05: string, profile: ProfilePointer | null): void;
/**
* Fetches a user's LNURL data from the cache.
* @param pubkey The pubkey of the user to fetch the LNURL data for.
* @param maxAgeInSecs The maximum age of the data in seconds.
* @param maxAgeForMissing The maximum age of the data in seconds if it is missing before it returns that it should be refetched.
* @returns The LNURL data, null if it is not in the cache and under the maxAgeForMissing, or "missing" if it should be refetched.
*/
loadUsersLNURLDoc?(pubkey: Hexpubkey, maxAgeInSecs?: number, maxAgeForMissing?: number): Promise<NDKLnUrlData | null | "missing">;
saveUsersLNURLDoc?(pubkey: Hexpubkey, doc: NDKLnUrlData | null): void;
/**
* Updates information about the relay.
*/
updateRelayStatus?(relayUrl: WebSocket["url"], info: NDKCacheRelayInfo): void;
/**
* Fetches information about the relay.
*/
getRelayStatus?(relayUrl: WebSocket["url"]): NDKCacheRelayInfo | undefined;
/**
* Tracks a publishing event.
* @param event
* @param relayUrls List of relays that the event will be published to.
*/
addUnpublishedEvent?(event: NDKEvent, relayUrls: WebSocket["url"][]): void;
/**
* Fetches all unpublished events.
*/
getUnpublishedEvents?(): Promise<{
event: NDKEvent;
relays?: WebSocket["url"][];
lastTryAt?: number;
}[]>;
/**
* Removes an unpublished event.
*/
discardUnpublishedEvent?(eventId: NDKEventId): void;
/**
* Called when the cache is ready.
*/
onReady?(callback: () => void): void;
/**
* Get a decrypted event from the cache by ID.
* @param eventId - The ID of the decrypted event to get.
* @returns The decrypted event, or null if it doesn't exist.
*/
getDecryptedEvent?(eventId: NDKEventId): NDKEvent | null;
/**
* Store a decrypted event in the cache.
* @param event - The decrypted event to store.
*/
addDecryptedEvent?(event: NDKEvent): void;
/**
* Cleans up the cache. This is called when the user logs out.
*/
clear?(): Promise<void>;
/**
* Gets all nutzap states from the cache.
* @returns A map of event IDs to nutzap states.
*/
getAllNutzapStates?(): Promise<Map<NDKEventId, NDKNutzapState>>;
/**
* Sets the state of a nutzap in the cache.
* @param id The ID of the nutzap event.
* @param stateChange The partial state change to apply.
*/
setNutzapState?(id: NDKEventId, stateChange: Partial<NDKNutzapState>): Promise<void>;
}
type NDKCacheRelayInfo = {
lastConnectedAt?: number;
dontConnectBefore?: number;
};
type ContentTag = {
tags: NDKTag[];
content: string;
};
interface NDKSigner {
/**
* Synchronously get the public key of the signer.
* @throws {Error} "Not ready" when the signer is not ready to provide a pubkey synchronously (e.g., NIP-07 or NIP-46 signers)
* @returns The public key in hex format
*/
get pubkey(): string;
/**
* Blocks until the signer is ready and returns the associated NDKUser.
* @returns A promise that resolves to the NDKUser instance.
*/
blockUntilReady(): Promise<NDKUser>;
/**
* Getter for the user property.
* @returns A promise that resolves to the NDKUser instance.
*/
user(): Promise<NDKUser>;
get userSync(): NDKUser;
/**
* Signs the given Nostr event.
* @param event - The Nostr event to be signed.
* @returns A promise that resolves to the signature of the signed event.
*/
sign(event: NostrEvent): Promise<string>;
/**
* Getter for the preferred relays.
* @returns A promise containing a simple map of preferred relays and their read/write policies.
*/
relays?(ndk?: NDK): Promise<NDKRelay[]>;
/**
* Determine the types of encryption (by nip) that this signer can perform.
* Implementing classes SHOULD return a value even for legacy (only nip04) third party signers.
* @nip Optionally returns an array with single supported nip or empty, to check for truthy or falsy.
* @return A promised list of any (or none) of these strings ['nip04', 'nip44']
*/
encryptionEnabled?(scheme?: NDKEncryptionScheme): Promise<NDKEncryptionScheme[]>;
/**
* Encrypts the given Nostr event for the given recipient.
* Implementing classes SHOULD equate legacy (only nip04) to nip == `nip04` || undefined
* @param recipient - The recipient (pubkey or conversationKey) of the encrypted value.
* @param value - The value to be encrypted.
* @param nip - which NIP is being implemented ('nip04', 'nip44')
*/
encrypt(recipient: NDKUser, value: string, scheme?: NDKEncryptionScheme): Promise<string>;
/**
* Decrypts the given value.
* Implementing classes SHOULD equate legacy (only nip04) to nip == `nip04` || undefined
* @param sender - The sender (pubkey or conversationKey) of the encrypted value
* @param value - The value to be decrypted
* @param scheme - which NIP is being implemented ('nip04', 'nip44', 'nip49')
*/
decrypt(sender: NDKUser, value: string, scheme?: NDKEncryptionScheme): Promise<string>;
/**
* Serializes the signer's essential data into a storable format.
* @returns A JSON string containing the type and payload.
*/
toPayload(): string;
}
type Proof$1 = {
/**
* Keyset id, used to link proofs to a mint an its MintKeys.
*/
id: string;
/**
* Amount denominated in Satoshis. Has to match the amount of the mints signing key.
*/
amount: number;
/**
* The initial secret that was (randomly) chosen for the creation of this proof.
*/
secret: string;
/**
* The unblinded signature for this secret, signed by the mints private key.
*/
C: string;
};
/**
* Represents a NIP-61 nutzap
*/
declare class NDKNutzap extends NDKEvent {
private debug;
private _proofs;
static kind: NDKKind;
static kinds: NDKKind[];
constructor(ndk?: NDK, event?: NostrEvent | NDKEvent);
static from(event: NDKEvent): NDKNutzap | undefined;
set comment(comment: string | undefined);
get comment(): string;
set proofs(proofs: Proof$1[]);
get proofs(): Proof$1[];
get rawP2pk(): string | undefined;
/**
* Gets the p2pk pubkey that is embedded in the first proof.
*
* Note that this returns a nostr pubkey, not a cashu pubkey (no "02" prefix)
*/
get p2pk(): string | undefined;
/**
* Get the mint where this nutzap proofs exist
*/
get mint(): string;
set mint(value: string);
get unit(): string;
set unit(value: string | undefined);
get amount(): number;
sender: NDKUser;
/**
* Set the target of the nutzap
* @param target The target of the nutzap (a user or an event)
*/
set target(target: NDKEvent | NDKUser);
set recipientPubkey(pubkey: Hexpubkey);
get recipientPubkey(): Hexpubkey;
get recipient(): NDKUser;
toNostrEvent(): Promise<NostrEvent>;
/**
* Validates that the nutzap conforms to NIP-61
*/
get isValid(): boolean;
}
/**
* A signer that uses an in-memory private key (nsec).
*
* @example
* ```ts
* const signer = NDKPrivateKeySigner.generate();
* console.log('your nsec is', signer.nsec);
* console.log('your pubkey is', signer.pubkey);
* console.log('your npub is', signer.npub);
* ```
*
* @example
* ```ts
* const signer = new NDKPrivateKeySigner(nsec);
* ```
*/
declare class NDKPrivateKeySigner implements NDKSigner {
private _user;
private _privateKey;
private _pubkey?;
/**
* Create a new signer from a private key.
* @param privateKey - The private key to use in hex form or nsec.
* @param ndk - The NDK instance to use.
*/
constructor(privateKeyOrNsec: Uint8Array | string, ndk?: NDK);
/**
* Get the private key in hex form.
*/
get privateKey(): string;
/**
* Get the public key in hex form.
*/
get pubkey(): string;
/**
* Get the private key in nsec form.
*/
get nsec(): string;
/**
* Get the public key in npub form.
*/
get npub(): string;
/**
* Generate a new private key.
*/
static generate(): NDKPrivateKeySigner;
/**
* Noop in NDKPrivateKeySigner.
*/
blockUntilReady(): Promise<NDKUser>;
/**
* Get the user.
*/
user(): Promise<NDKUser>;
/**
* Get the user.
*/
get userSync(): NDKUser;
sign(event: NostrEvent): Promise<string>;
encryptionEnabled(scheme?: NDKEncryptionScheme): Promise<NDKEncryptionScheme[]>;
encrypt(recipient: NDKUser, value: string, scheme?: NDKEncryptionScheme): Promise<string>;
decrypt(sender: NDKUser, value: string, scheme?: NDKEncryptionScheme): Promise<string>;
/**
* Serializes the signer's private key into a storable format.
* @returns A JSON string containing the type and the hex private key.
*/
toPayload(): string;
/**
* Deserializes the signer from a payload string.
* @param payloadString The JSON string obtained from toPayload().
* @param ndk Optional NDK instance.
* @returns An instance of NDKPrivateKeySigner.
*/
static fromPayload(payloadString: string, ndk?: NDK): Promise<NDKPrivateKeySigner>;
}
/**
* NDKAuthPolicies are functions that are called when a relay requests authentication
* so that you can define a behavior for your application.
*
* @param relay The relay that requested authentication.
* @param challenge The challenge that the relay sent.
*/
type NDKAuthPolicy = (relay: NDKRelay, challenge: string) => Promise<boolean | undefined | NDKEvent>;
type Nip07RelayMap = {
[key: string]: {
read: boolean;
write: boolean;
};
};
type Nip44 = {
encrypt: (recipient: Hexpubkey, value: string) => Promise<string>;
decrypt: (sender: Hexpubkey, value: string) => Promise<string>;
};
declare global {
interface Window {
nostr?: {
getPublicKey(): Promise<string>;
signEvent(event: NostrEvent): Promise<{
sig: string;
}>;
getRelays?: () => Promise<Nip07RelayMap>;
nip04?: {
encrypt(recipientHexPubKey: string, value: string): Promise<string>;
decrypt(senderHexPubKey: string, value: string): Promise<string>;
};
nip44?: Nip44;
};
}
}
type NDKFilterFingerprint = string;
type NDKEventSerialized = string;
/**
* Provides information that should be used to send a NIP-61 nutzap.
* mints: URLs of the mints that can be used.
* relays: URLs of the relays where nutzap must be published
* p2pk: Optional pubkey to use for P2PK lock
*/
type CashuPaymentInfo = {
/**
* Mints that must be used for the payment
*/
mints?: string[];
/**
* Relays where nutzap must be published
*/
relays?: string[];
/**
* Optional pubkey to use for P2PK lock
*/
p2pk?: string;
/**
* Intramint fallback allowed:
*
* When set to true, if cross-mint payments fail, we will
* fallback to sending an intra-mint payment.
*/
allowIntramintFallback?: boolean;
};
/**
* This is what a wallet implementing Cashu payments should provide back
* when a payment has been requested.
*/
type NDKPaymentConfirmationCashu = {
/**
* Proof of the payment
*/
proofs: Proof$1[];
/**
* Mint
*/
mint: string;
};
type NDKZapDetails<T> = T & {
/**
* Target of the zap
*/
target: NDKEvent | NDKUser;
/**
* Comment for the zap
*/
comment?: string;
/**
* Tags to add to the zap
*/
tags?: NDKTag[];
/**
* Pubkey of the user to zap to
*/
recipientPubkey: string;
/**
* Amount of the payment
*/
amount: number;
/**
* Unit of the payment (e.g. msat)
*/
unit: string;
/**
* Description of the payment for the sender's record
*/
paymentDescription?: string;
/**
* If this payment is for a nip57 zap, this will contain the zap request.
*/
nip57ZapRequest?: NDKEvent;
/**
* When set to true, when a pubkey is not zappable, we will
* automatically fallback to using NIP-61.
*
* Every pubkey must be able to receive money.
*
* @default false
*/
nutzapAsFallback?: boolean;
};
type NDKPaymentConfirmation = NDKPaymentConfirmationLN | NDKNutzap;
type NDKZapSplit = {
pubkey: string;
amount: number;
};
type NDKZapMethod = "nip57" | "nip61";
type NDKLnLudData = {
lud06?: string;
lud16?: string;
};
type NDKZapMethodInfo = NDKLnLudData | CashuPaymentInfo;
type LnPayCb = (payment: NDKZapDetails<LnPaymentInfo>) => Promise<NDKPaymentConfirmationLN | undefined>;
type CashuPayCb = (payment: NDKZapDetails<CashuPaymentInfo>, onLnInvoice?: (pr: string) => void) => Promise<NDKPaymentConfirmationCashu | undefined>;
type Hexpubkey = string;
type Npub = string;
type ProfilePointer = {
pubkey: string;
relays?: string[];
nip46?: string[];
};
interface NDKUserParams {
npub?: Npub;
hexpubkey?: Hexpubkey;
pubkey?: Hexpubkey;
nip05?: string;
relayUrls?: string[];
nip46Urls?: string[];
nprofile?: string;
}
/**
* Represents a pubkey.
*/
declare class NDKUser {
ndk: NDK | undefined;
profile?: NDKUserProfile;
profileEvent?: NDKEvent;
private _npub?;
private _pubkey?;
relayUrls: string[];
readonly nip46Urls: string[];
constructor(opts: NDKUserParams);
get npub(): string;
get nprofile(): string;
set npub(npub: Npub);
/**
* Get the user's pubkey
* @returns {string} The user's pubkey
*/
get pubkey(): string;
/**
* Set the user's pubkey
* @param pubkey {string} The user's pubkey
*/
set pubkey(pubkey: string);
/**
* Equivalent to NDKEvent.filters().
* @returns {NDKFilter}
*/
filter(): NDKFilter;
/**
* Gets NIP-57 and NIP-61 information that this user has signaled
*
* @param getAll {boolean} Whether to get all zap info or just the first one
*/
getZapInfo(timeoutMs?: number): Promise<Map<NDKZapMethod, NDKZapMethodInfo>>;
/**
* Instantiate an NDKUser from a NIP-05 string
* @param nip05Id {string} The user's NIP-05
* @param ndk {NDK} An NDK instance
* @param skipCache {boolean} Whether to skip the cache or not
* @returns {NDKUser | undefined} An NDKUser if one is found for the given NIP-05, undefined otherwise.
*/
static fromNip05(nip05Id: string, ndk: NDK, skipCache?: boolean): Promise<NDKUser | undefined>;
/**
* Fetch a user's profile
* @param opts {NDKSubscriptionOptions} A set of NDKSubscriptionOptions
* @param storeProfileEvent {boolean} Whether to store the profile event or not
* @returns User Profile
*/
fetchProfile(opts?: NDKSubscriptionOptions, storeProfileEvent?: boolean): Promise<NDKUserProfile | null>;
/**
* Returns a set of users that this user follows.
*
* @deprecated Use followSet instead
*/
follows: (opts?: NDKSubscriptionOptions | undefined, outbox?: boolean | undefined, kind?: number | undefined) => Promise<Set<NDKUser>>;
/**
* Returns a set of pubkeys that this user follows.
*
* @param opts - NDKSubscriptionOptions
* @param outbox - boolean
* @param kind - number
*/
followSet(opts?: NDKSubscriptionOptions, outbox?: boolean, kind?: number): Promise<Set<Hexpubkey>>;
/** @deprecated Use referenceTags instead. */
/**
* Get the tag that can be used to reference this user in an event
* @returns {NDKTag} an NDKTag
*/
tagReference(): NDKTag;
/**
* Get the tags that can be used to reference this user in an event
* @returns {NDKTag[]} an array of NDKTag
*/
referenceTags(marker?: string): NDKTag[];
/**
* Publishes the current profile.
*/
publish(): Promise<void>;
/**
* Add a follow to this user's contact list
*
* @param newFollow {NDKUser} The user to follow
* @param currentFollowList {Set<NDKUser>} The current follow list
* @param kind {NDKKind} The kind to use for this contact list (defaults to `3`)
* @returns {Promise<boolean>} True if the follow was added, false if the follow already exists
*/
follow(newFollow: NDKUser, currentFollowList?: Set<NDKUser>, kind?: NDKKind): Promise<boolean>;
/**
* Remove a follow from this user's contact list
*
* @param user {NDKUser} The user to unfollow
* @param currentFollowList {Set<NDKUser>} The current follow list
* @param kind {NDKKind} The kind to use for this contact list (defaults to `3`)
* @returns The relays were the follow list was published or false if the user wasn't found
*/
unfollow(user: NDKUser, currentFollowList?: Set<NDKUser>, kind?: NDKKind): Promise<Set<NDKRelay> | boolean>;
/**
* 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.)
*/
validateNip05(nip05Id: string): Promise<boolean | null>;
}
type NDKSubscriptionId = string;
/**
* This class monitors active subscriptions.
*/
declare class NDKSubscriptionManager {
subscriptions: Map<NDKSubscriptionId, NDKSubscription>;
seenEvents: Map<string, NDKRelay[]>;
constructor();
add(sub: NDKSubscription): void;
seenEvent(eventId: NDKEventId, relay: NDKRelay): void;
/**
* Whenever an event comes in, this function is called.
* This function matches the received event against all the
* known (i.e. active) NDKSubscriptions, and if it matches,
* it sends the event to the subscription.
*
* This is the single place in the codebase that matches
* incoming events with parties interested in the event.
*
* This is also what allows for reactivity in NDK apps, such that
* whenever an active subscription receives an event that some
* other active subscription would want to receive, both receive it.
*
* TODO This also allows for subscriptions that overlap in meaning
* to be collapsed into one.
*
* I.e. if a subscription with filter: kinds: [1], authors: [alice]
* is created and EOSEs, and then a subsequent subscription with
* kinds: [1], authors: [alice] is created, once the second subscription
* EOSEs we can safely close it, increment its refCount and close it,
* and when the first subscription receives a new event from Alice this
* code will make the second subscription receive the event even though
* it has no active subscription on a relay.
* @param event Raw event received from a relay
* @param relay Relay that sent the event
* @param optimisticPublish Whether the event is coming from an optimistic publish
*/
dispatchEvent(event: NostrEvent, relay?: NDKRelay, optimisticPublish?: boolean): void;
}
type Item = {
subscription: NDKSubscription;
filters: NDKFilter[];
};
declare enum NDKRelaySubscriptionStatus {
INITIAL = 0,
/**
* The subscription is pending execution.
*/
PENDING = 1,
/**
* The subscription is waiting for the relay to be ready.
*/
WAITING = 2,
/**
* The subscription is currently running.
*/
RUNNING = 3,
CLOSED = 4
}
/**
* Groups together a number of NDKSubscriptions (as created by the user),
* filters (as computed internally), executed, or to be executed, within
* a single specific relay.
*/
declare class NDKRelaySubscription {
fingerprint: NDKFilterFingerprint;
items: Map<NDKSubscriptionInternalId, Item>;
topSubManager: NDKSubscriptionManager;
debug: debug.Debugger;
/**
* Tracks the status of this REQ.
*/
status: NDKRelaySubscriptionStatus;
onClose?: (sub: NDKRelaySubscription) => void;
private relay;
/**
* Whether this subscription has reached EOSE.
*/
private eosed;
/**
* Timeout at which this subscription will
* start executing.
*/
private executionTimer?;
/**
* Track the time at which this subscription will fire.
*/
private fireTime?;
/**
* The delay type that the current fireTime was calculated with.
*/
private delayType?;
/**
* The filters that have been executed.
*/
executeFilters?: NDKFilter[];
readonly id: string;
/**
*
* @param fingerprint The fingerprint of this subscription.
*/
constructor(relay: NDKRelay, fingerprint: NDKFilterFingerprint | null, topSubManager: NDKSubscriptionManager);
private _subId?;
get subId(): string;
private subIdParts;
private addSubIdPart;
addItem(subscription: NDKSubscription, filters: NDKFilter[]): void;
/**
* A subscription has been closed, remove it from the list of items.
* @param subscription
*/
removeItem(subscription: NDKSubscription): void;
private close;
cleanup(): void;
private evaluateExecutionPlan;
private schedule;
private executeOnRelayReady;
private finalizeSubId;
private reExecuteAfterAuth;
private execute;
onstart(): void;
onevent(event: NostrEvent): void;
oneose(subId: string): void;
onclose(_reason?: string): void;
onclosed(reason?: string): void;
/**
* Grabs the filters from all the subscriptions
* and merges them into a single filter.
*/
private compileFilters;
}
declare class NDKRelayConnectivity {
private ndkRelay;
private ws?;
private _status;
private timeoutMs?;
private connectedAt?;
private _connectionStats;
private debug;
netDebug?: NDKNetDebug;
private connectTimeout;
private reconnectTimeout;
private ndk?;
openSubs: Map<string, NDKRelaySubscription>;
private openCountRequests;
private openEventPublishes;
private serial;
baseEoseTimeout: number;
private keepalive?;
private wsStateMonitor?;
private sleepDetector?;
private lastSleepCheck;
private lastMessageSent;
private wasIdle;
constructor(ndkRelay: NDKRelay, ndk?: NDK);
/**
* Sets up keepalive, WebSocket state monitoring, and sleep detection
*/
private setupMonitoring;
/**
* Handles detection of a stale connection
*/
private handleStaleConnection;
/**
* Handles possible system wake event
*/
private handlePossibleWake;
/**
* Resets the reconnection state for system-wide events
* Used by NDKPool when detecting system sleep/wake
*/
resetReconnectionState(): void;
/**
* Connects to the NDK relay and handles the connection lifecycle.
*
* This method attempts to establish a WebSocket connection to the NDK relay specified in the `ndkRelay` object.
* If the connection is successful, it updates the connection statistics, sets the connection status to `CONNECTED`,
* and emits `connect` and `ready` events on the `ndkRelay` object.
*
* If the connection attempt fails, it handles the error by either initiating a reconnection attempt or emitting a
* `delayed-connect` event on the `ndkRelay` object, depending on the `reconnect` parameter.
*
* @param timeoutMs - The timeout in milliseconds for the connection attempt. If not provided, the default timeout from the `ndkRelay` object is used.
* @param reconnect - Indicates whether a reconnection should be attempted if the connection fails. Defaults to `true`.
* @returns A Promise that resolves when the connection is established, or rejects if the connection fails.
*/
connect(timeoutMs?: number, reconnect?: boolean): Promise<void>;
/**
* Disconnects the WebSocket connection to the NDK relay.
* This method sets the connection status to `NDKRelayStatus.DISCONNECTING`,
* attempts to close the WebSocket connection, and sets the status to
* `NDKRelayStatus.DISCONNECTED` if the disconnect operation fails.
*/
disconnect(): void;
/**
* Handles the error that occurred when attempting to connect to the NDK relay.
* If `reconnect` is `true`, this method will initiate a reconnection attempt.
* Otherwise, it will emit a `delayed-connect` event on the `ndkRelay` object,
* indicating that a reconnection should be attempted after a delay.
*
* @param reconnect - Indicates whether a reconnection should be attempted.
*/
onConnectionError(reconnect: boolean): void;
/**
* Handles the connection event when the WebSocket connection is established.
* This method is called when the WebSocket connection is successfully opened.
* It clears any existing connection and reconnection timeouts, updates the connection statistics,
* sets the connection status to `CONNECTED`, and emits `connect` and `ready` events on the `ndkRelay` object.
*/
private onConnect;
/**
* Handles the disconnection event when the WebSocket connection is closed.
* This method is called when the WebSocket connection is successfully closed.
* It updates the connection statistics, sets the connection status to `DISCONNECTED`,
* initiates a reconnection attempt if we didn't disconnect ourselves,
* and emits a `disconnect` event on the `ndkRelay` object.
*/
private onDisconnect;
/**
* Handles incoming messages from the NDK relay WebSocket connection.
* This method is called whenever a message is received from the relay.
* It parses the message data and dispatches the appropriate handling logic based on the message type.
*
* @param event - The MessageEvent containing the received message data.
*/
private onMessage;
/**
* Handles an authentication request from the NDK relay.
*
* If an authentication policy is configured, it will be used to authenticate the connection.
* Otherwise, the `auth` event will be emitted to allow the application to handle the authentication.
*
* @param challenge - The authentication challenge provided by the NDK relay.
*/
private onAuthRequested;
/**
* Handles errors that occur on the WebSocket connection to the relay.
* @param error - The error or event that occurred.
*/
private onError;
/**
* Gets the current status of the NDK relay connection.
* @returns {NDKRelayStatus} The current status of the NDK relay connection.
*/
get status(): NDKRelayStatus;
/**
* Checks if the NDK relay connection is currently available.
* @returns {boolean} `true` if the relay connection is in the `CONNECTED` status, `false` otherwise.
*/
isAvailable(): boolean;
/**
* Checks if the NDK relay connection is flapping, which means the connection is rapidly
* disconnecting and reconnecting. This is determined by analyzing the durations of the
* last three connection attempts. If the standard deviation of the durations is less
* than 1000 milliseconds, the connection is considered to be flapping.
*
* @returns {boolean} `true` if the connection is flapping, `false` otherwise.
*/
private isFlapping;
/**
* Handles a notice received from the NDK relay.
* If the notice indicates the relay is complaining (e.g. "too many" or "maximum"),
* the method disconnects from the relay and attempts to reconnect after a 2-second delay.
* A debug message is logged with the relay URL and the notice text.
* The "notice" event is emitted on the ndkRelay instance with the notice text.
*
* @param notice - The notice text received from the NDK relay.
*/
private onNotice;
/**
* Attempts to reconnect to the NDK relay after a connection is lost.
* This function is called recursively to handle multiple reconnection attempts.
* It checks if the relay is flapping and emits a "flapping" event if so.
* It then calculates a delay before the next reconnection attempt based on the number of previous attempts.
* The function sets a timeout to execute the next reconnection attempt after the calculated delay.
* If the maximum number of reconnection attempts is reached, a debug message is logged.
*
* @param attempt - The current attempt number (default is 0).
*/
private handleReconnection;
/**
* Sends a message to the NDK relay if the connection is in the CONNECTED state and the WebSocket is open.
* If the connection is not in the CONNECTED state or the WebSocket is not open, logs a debug message and throws an error.
*
* @param message - The message to send to the NDK relay.
* @throws {Error} If attempting to send on a closed relay connection.
*/
send(message: string): Promise<void>;
/**
* Authenticates the NDK event by sending it to the NDK relay and returning a promise that resolves with the result.
*
* @param event - The NDK event to authenticate.
* @returns A promise that resolves with the authentication result.
*/
private auth;
/**
* Publishes an NDK event to the relay and returns a promise that resolves with the result.
*
* @param event - The NDK event to publish.
* @returns A promise that resolves with the result of the event publication.
* @throws {Error} If attempting to publish on a closed relay connection.
*/
publish(event: NostrEvent): Promise<string>;
/**
* Counts the number of events that match the provided filters.
*
* @param filters - The filters to apply to the count request.
* @param params - An optional object containing a custom id for the count request.
* @returns A promise that resolves with the number of matching events.
* @throws {Error} If attempting to send the count request on a closed relay connection.
*/
count(filters: NDKFilter[], params: {
id?: string | null;
}): Promise<number>;
close(subId: string, reason?: string): void;
/**
* Subscribes to the NDK relay with the provided filters and parameters.
*
* @param filters - The filters to apply to the subscription.
* @param params - The subscription parameters, including an optional custom id.
* @returns A new NDKRelaySubscription instance.
*/
req(relaySub: NDKRelaySubscription): void;
/**
* Utility functions to update the connection stats.
*/
private updateConnectionStats;
/** Returns the connection stats. */
get connectionStats(): NDKRelayConnectionStats;
/** Returns the relay URL */
get url(): WebSocket["url"];
get connected(): boolean;
}
type NDKRelayScore = number;
/**
* The subscription manager of an NDKRelay is in charge of orchestrating the subscriptions
* that are created and closed in a given relay.
*
* The manager is responsible for:
* * restarting subscriptions when they are unexpectedly closed
* * scheduling subscriptions that are received before the relay is connected
* * grouping similar subscriptions to be compiled into individual REQs
*/
declare class NDKRelaySubscriptionManager {
private relay;
subscriptions: Map<NDKFilterFingerprint, NDKRelaySubscription[]>;
private generalSubManager;
/**
* @param relay - The relay instance.
* @param generalSubManager - The subscription manager instance.
*/
constructor(relay: NDKRelay, generalSubManager: NDKSubscriptionManager);
/**
* Adds a subscription to the manager.
*/
addSubscription(sub: NDKSubscription, filters: NDKFilter[]): void;
createSubscription(_sub: NDKSubscription, _filters: NDKFilter[], fingerprint?: NDKFilterFingerprint): NDKRelaySubscription;
private onRelaySubscriptionClose;
}
declare enum NDKRelayStatus {
DISCONNECTING = 0,// 0
DISCONNECTED = 1,// 1
RECONNECTING = 2,// 2
FLAPPING = 3,// 3
CONNECTING = 4,// 4
CONNECTED = 5,// 5
AUTH_REQUESTED = 6,// 6
AUTHENTICATING = 7,// 7
AUTHENTICATED = 8
}
interface NDKRelayConnectionStats {
/**
* The number of times a connection has been attempted.
*/
attempts: number;
/**
* The number of times a connection has been successfully established.
*/
success: number;
/**
* The durations of the last 100 connections in milliseconds.
*/
durations: number[];
/**
* The time the current connection was established in milliseconds.
*/
connectedAt?: number;
/**
* Timestamp of the next reconnection attempt.
*/
nextReconnectAt?: number;
/**
* Signature validation ratio for this relay.
* @see NDKRelayOptions.validationRatio
*/
validationRatio?: number;
}
/**
* The NDKRelay class represents a connection to a relay.
*
* @emits NDKRelay#connect
* @emits NDKRelay#ready
* @emits NDKRelay#disconnect
* @emits NDKRelay#notice
* @emits NDKRelay#event
* @emits NDKRelay#published when an event is published to the relay
* @emits NDKRelay#publish:failed when an event fails to publish to the relay
* @emits NDKRelay#eose when the relay has reached the end of stored events
* @emits NDKRelay#auth when the relay requires authentication
* @emits NDKRelay#authed when the relay has authenticated
* @emits NDKRelay#delayed-connect when the relay will wait before reconnecting
*/
declare class NDKRelay extends EventEmitter<{
connect: () => void;
ready: () => void;
/**
* Emitted when the relay has reached the end of stored events.
*/
disconnect: () => void;
flapping: (stats: NDKRelayConnectionStats) => void;
notice: (notice: string) => void;
auth: (challenge: string) => void;
authed: () => void;
"auth:failed": (error: Error) => void;
published: (event: NDKEvent) => void;
"publish:failed": (event: NDKEvent, error: Error) => void;
"delayed-connect": (delayInMs: number) => void;
}> {
readonly url: WebSocket["url"];
readonly scores: Map<NDKUser, NDKRelayScore>;
connectivity: NDKRelayConnectivity;
subs: NDKRelaySubscriptionManager;
private publisher;
authPolicy?: NDKAuthPolicy;
/**
* The lowest validation ratio this relay can reach.
*/
lowestValidationRatio?: number;
/**
* Current validation ratio this relay is targeting.
*/
targetValidationRatio?: number;
validationRatioFn?: (relay: NDKRelay, validatedCount: number, nonValidatedCount: number) => number;
/**
* This tracks events that have been seen by this relay
* with a valid signature.
*/
validatedEventCount: number;
/**
* This tracks events that have been seen by this relay
* but have not been validated.
*/
nonValidatedEventCount: number;
/**
* Whether this relay is trusted.
*
* Trusted relay's events do not get their signature verified.
*/
trusted: boolean;
complaining: boolean;
readonly debug: debug$1.Debugger;
static defaultValidationRatioUpdateFn: (relay: NDKRelay, validatedCount: number, _nonValidatedCount: number) => number;
constructor(url: WebSocket["url"], authPolicy: NDKAuthPolicy | undefined, ndk: NDK);
private updateValidationRatio;
get status(): NDKRelayStatus;
get connectionStats(): NDKRelayConnectionStats;
/**
* Connects to the relay.
*/
connect(timeoutMs?: number, reconnect?: boolean): Promise<void>;
/**
* Disconnects from the relay.
*/
disconnect(): void;
/**
* 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: NDKSubscription, filters: NDKFilter[]): void;
/**
* 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