Fix relay disappearing: make port.onmessage synchronous (all handlers fire-and-forget). Fix discovered relays serving data: replace broken LRU cache iteration with kind 3 cache query + per-pubkey trackerData.get(). Remove debug logging.
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"VERSION": "v0.7.49",
|
||||
"VERSION_NUMBER": "0.7.49",
|
||||
"BUILD_DATE": "2026-06-26T14:27:42.415Z"
|
||||
"VERSION": "v0.7.50",
|
||||
"VERSION_NUMBER": "0.7.50",
|
||||
"BUILD_DATE": "2026-06-26T14:52:01.942Z"
|
||||
}
|
||||
|
||||
@@ -6464,89 +6464,34 @@ async function handleGetDiscoveredRelays(requestId, port) {
|
||||
}
|
||||
|
||||
// Build relayUrl -> [pubkeys] map by inverting the outbox tracker.
|
||||
// ndk.outboxTracker.data is an LRUCache (typescript-lru-cache).
|
||||
// We try a few iteration strategies to be resilient to API differences.
|
||||
// The typescript-lru-cache iteration API is broken (it yields millions
|
||||
// of phantom keys from only a handful of real entries), so instead of
|
||||
// iterating the LRU cache we query the NDK cache for the user's kind 3
|
||||
// contact list and then look up each followed pubkey by key.
|
||||
const relayServesPubkeys = new Map(); // normalizedUrl -> Set<pubkey>
|
||||
try {
|
||||
const trackerData = ndk?.outboxTracker?.data;
|
||||
if (trackerData) {
|
||||
// Debug: log what the LRU cache looks like
|
||||
const trackerKeys = typeof trackerData.keys === 'function' ? Array.from(trackerData.keys()) :
|
||||
(trackerData instanceof Map ? Array.from(trackerData.keys()) : []);
|
||||
console.log('[Worker] getDiscoveredRelays: tracker has', trackerKeys.length, 'entries');
|
||||
const kind3Events = await ndk.fetchEvents(
|
||||
{ kinds: [3], authors: [currentPubkey], limit: 1 },
|
||||
{ cacheUsage: 'ONLY_CACHE' }
|
||||
);
|
||||
const latestKind3 = kind3Events && kind3Events.size > 0
|
||||
? Array.from(kind3Events).sort((a, b) => (b.created_at || 0) - (a.created_at || 0))[0]
|
||||
: null;
|
||||
const followedPubkeys = latestKind3 ? extractFollowedPubkeysFromKind3(latestKind3) : [];
|
||||
|
||||
let pubkeysProcessed = 0;
|
||||
let pubkeysWithWriteRelays = 0;
|
||||
|
||||
const collectForPubkey = (pubkey) => {
|
||||
pubkeysProcessed++;
|
||||
try {
|
||||
const outboxItem = trackerData.get(pubkey);
|
||||
if (!outboxItem) return;
|
||||
const writeRelays = outboxItem.writeRelays;
|
||||
if (!writeRelays || writeRelays.size === 0) return;
|
||||
pubkeysWithWriteRelays++;
|
||||
for (const r of writeRelays) {
|
||||
const rUrl = r?.url ? normalizeRelayUrl(r.url) : (typeof r === 'string' ? normalizeRelayUrl(r) : null);
|
||||
if (!rUrl) continue;
|
||||
if (!relayServesPubkeys.has(rUrl)) {
|
||||
relayServesPubkeys.set(rUrl, new Set());
|
||||
}
|
||||
relayServesPubkeys.get(rUrl).add(pubkey);
|
||||
}
|
||||
} catch (_) {
|
||||
// ignore per-pubkey errors
|
||||
}
|
||||
};
|
||||
|
||||
// Strategy 1: standard Map-like entries()
|
||||
let iterated = false;
|
||||
for (const pubkey of followedPubkeys) {
|
||||
try {
|
||||
if (typeof trackerData.entries === 'function') {
|
||||
for (const [pubkey] of trackerData.entries()) {
|
||||
collectForPubkey(pubkey);
|
||||
const outboxItem = ndk?.outboxTracker?.data?.get(pubkey);
|
||||
if (!outboxItem?.writeRelays) continue;
|
||||
for (const relayUrl of outboxItem.writeRelays) {
|
||||
const normalized = normalizeRelayUrl(relayUrl);
|
||||
if (!normalized) continue;
|
||||
if (!relayServesPubkeys.has(normalized)) {
|
||||
relayServesPubkeys.set(normalized, new Set());
|
||||
}
|
||||
iterated = true;
|
||||
relayServesPubkeys.get(normalized).add(pubkey);
|
||||
}
|
||||
} catch (_) {}
|
||||
|
||||
// Strategy 2: keys() + get()
|
||||
if (!iterated) {
|
||||
try {
|
||||
if (typeof trackerData.keys === 'function') {
|
||||
for (const pubkey of trackerData.keys()) {
|
||||
collectForPubkey(pubkey);
|
||||
}
|
||||
iterated = true;
|
||||
}
|
||||
} catch (_) {}
|
||||
}
|
||||
|
||||
// Strategy 3: iterate as Map directly
|
||||
if (!iterated && trackerData instanceof Map) {
|
||||
for (const pubkey of trackerData.keys()) {
|
||||
collectForPubkey(pubkey);
|
||||
}
|
||||
iterated = true;
|
||||
}
|
||||
|
||||
// Strategy 4: try internal storage if LRU cache has it
|
||||
if (!iterated) {
|
||||
try {
|
||||
// Some LRU caches store entries in an internal Map or object
|
||||
const internal = trackerData._map || trackerData.cache || trackerData._cache;
|
||||
if (internal instanceof Map) {
|
||||
for (const [pubkey] of internal) {
|
||||
collectForPubkey(pubkey);
|
||||
}
|
||||
iterated = true;
|
||||
}
|
||||
} catch (_) {}
|
||||
}
|
||||
|
||||
console.log('[Worker] getDiscoveredRelays: processed', pubkeysProcessed, 'pubkeys,', pubkeysWithWriteRelays, 'with write relays, iterated:', iterated, ', relayServesPubkeys size:', relayServesPubkeys.size);
|
||||
} else {
|
||||
console.log('[Worker] getDiscoveredRelays: no outbox tracker data available');
|
||||
}
|
||||
} catch (err) {
|
||||
console.warn('[Worker] getDiscoveredRelays: outbox tracker inversion failed:', err?.message || err);
|
||||
@@ -6852,7 +6797,7 @@ self.onconnect = (event) => {
|
||||
|
||||
connectedPorts.push(port);
|
||||
|
||||
port.onmessage = async (e) => {
|
||||
port.onmessage = (e) => {
|
||||
const {
|
||||
type,
|
||||
pubkey,
|
||||
@@ -6894,11 +6839,11 @@ self.onconnect = (event) => {
|
||||
|
||||
switch(type) {
|
||||
case 'init':
|
||||
await handleInit(pubkey, port);
|
||||
void handleInit(pubkey, port);
|
||||
break;
|
||||
|
||||
case 'subscribe':
|
||||
await handleSubscribe(subId, filters, opts, port);
|
||||
void handleSubscribe(subId, filters, opts, port);
|
||||
break;
|
||||
|
||||
case 'unsubscribe':
|
||||
@@ -6906,27 +6851,27 @@ self.onconnect = (event) => {
|
||||
break;
|
||||
|
||||
case 'publish':
|
||||
await handlePublish(requestId, event, port);
|
||||
void handlePublish(requestId, event, port);
|
||||
break;
|
||||
|
||||
case 'publishRaw':
|
||||
await handlePublishRaw(requestId, event, port);
|
||||
void handlePublishRaw(requestId, event, port);
|
||||
break;
|
||||
|
||||
case 'publishRawToRelay':
|
||||
await handlePublishRawToRelay(requestId, event, relayUrl, port);
|
||||
void handlePublishRawToRelay(requestId, event, relayUrl, port);
|
||||
break;
|
||||
|
||||
case 'sendNip17Message':
|
||||
await handleSendNip17Message(requestId, recipientPubkey, content, port);
|
||||
void handleSendNip17Message(requestId, recipientPubkey, content, port);
|
||||
break;
|
||||
|
||||
case 'fetchEvents':
|
||||
await handleFetchEvents(requestId, filters, relayUrl, port);
|
||||
void handleFetchEvents(requestId, filters, relayUrl, port);
|
||||
break;
|
||||
|
||||
case 'ndkFetchEvents':
|
||||
await handleNdkFetchEvents(requestId, filters, port);
|
||||
void handleNdkFetchEvents(requestId, filters, port);
|
||||
break;
|
||||
|
||||
case 'warmOutbox':
|
||||
@@ -6937,7 +6882,7 @@ self.onconnect = (event) => {
|
||||
break;
|
||||
|
||||
case 'setRelayEventLogging':
|
||||
await handleSetRelayEventLogging(requestId, enabled, port);
|
||||
void handleSetRelayEventLogging(requestId, enabled, port);
|
||||
break;
|
||||
|
||||
case 'getDiscoveredRelays':
|
||||
@@ -6973,109 +6918,101 @@ self.onconnect = (event) => {
|
||||
break;
|
||||
|
||||
case 'queryCache':
|
||||
await handleQueryCache(requestId, filters, port);
|
||||
void handleQueryCache(requestId, filters, port);
|
||||
break;
|
||||
|
||||
case 'purgePlaylistCache':
|
||||
await handlePurgePlaylistCache(requestId, playlistIdentifier, ownerPubkey, playlistKind, eventIds, port);
|
||||
void handlePurgePlaylistCache(requestId, playlistIdentifier, ownerPubkey, playlistKind, eventIds, port);
|
||||
break;
|
||||
|
||||
case 'syncMuteList': {
|
||||
try {
|
||||
const result = await loadWorkerMuteList(currentPubkey, { reason: 'sync-request' });
|
||||
port.postMessage({
|
||||
type: 'response',
|
||||
requestId,
|
||||
data: { success: true, result }
|
||||
case 'syncMuteList':
|
||||
void loadWorkerMuteList(currentPubkey, { reason: 'sync-request' })
|
||||
.then((result) => {
|
||||
port.postMessage({ type: 'response', requestId, data: { success: true, result } });
|
||||
})
|
||||
.catch((error) => {
|
||||
port.postMessage({ type: 'response', requestId, error: error?.message || String(error) });
|
||||
});
|
||||
} catch (error) {
|
||||
port.postMessage({
|
||||
type: 'response',
|
||||
requestId,
|
||||
error: error?.message || String(error)
|
||||
});
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case 'fetchCachedProfile':
|
||||
await handleFetchCachedProfile(requestId, e.data.pubkey, port);
|
||||
void handleFetchCachedProfile(requestId, e.data.pubkey, port);
|
||||
break;
|
||||
|
||||
case 'storeProfile':
|
||||
await handleStoreProfile(e.data.pubkey, e.data.profile);
|
||||
void handleStoreProfile(e.data.pubkey, e.data.profile);
|
||||
break;
|
||||
|
||||
case 'getAllCachedProfiles':
|
||||
await handleGetAllCachedProfiles(requestId, port);
|
||||
void handleGetAllCachedProfiles(requestId, port);
|
||||
break;
|
||||
|
||||
case 'getUserSettings':
|
||||
await handleGetUserSettings(requestId, port);
|
||||
void handleGetUserSettings(requestId, port);
|
||||
break;
|
||||
|
||||
case 'patchUserSettings':
|
||||
await handlePatchUserSettings(requestId, patch, options, port);
|
||||
void handlePatchUserSettings(requestId, patch, options, port);
|
||||
break;
|
||||
|
||||
case 'setUserSettings':
|
||||
await handleSetUserSettings(requestId, settings, options, port);
|
||||
void handleSetUserSettings(requestId, settings, options, port);
|
||||
break;
|
||||
|
||||
case 'resetUserSettings':
|
||||
await handleResetUserSettings(requestId, options, port);
|
||||
void handleResetUserSettings(requestId, options, port);
|
||||
break;
|
||||
|
||||
case 'encryptContent':
|
||||
await handleEncryptContent(requestId, e.data.recipientPubkey, e.data.plaintext, e.data.scheme, port);
|
||||
void handleEncryptContent(requestId, e.data.recipientPubkey, e.data.plaintext, e.data.scheme, port);
|
||||
break;
|
||||
|
||||
case 'decryptContent':
|
||||
await handleDecryptContent(requestId, e.data.senderPubkey, e.data.ciphertext, e.data.scheme, port);
|
||||
void handleDecryptContent(requestId, e.data.senderPubkey, e.data.ciphertext, e.data.scheme, port);
|
||||
break;
|
||||
|
||||
case 'walletPublishMintList':
|
||||
await handleWalletPublishMintList(requestId, relays, receiveMints, port);
|
||||
void handleWalletPublishMintList(requestId, relays, receiveMints, port);
|
||||
break;
|
||||
|
||||
case 'walletFetchMintList':
|
||||
await handleWalletFetchMintList(requestId, targetPubkey, port);
|
||||
void handleWalletFetchMintList(requestId, targetPubkey, port);
|
||||
break;
|
||||
|
||||
case 'walletInit':
|
||||
await handleWalletInit(requestId, port);
|
||||
void handleWalletInit(requestId, port);
|
||||
break;
|
||||
|
||||
case 'walletStart':
|
||||
await handleWalletStart(requestId, pubkey, port);
|
||||
void handleWalletStart(requestId, pubkey, port);
|
||||
break;
|
||||
|
||||
case 'walletCreate':
|
||||
await handleWalletCreate(requestId, mints, relays, port);
|
||||
void handleWalletCreate(requestId, mints, relays, port);
|
||||
break;
|
||||
|
||||
case 'walletHasWallet':
|
||||
await handleWalletHasWallet(requestId, port);
|
||||
void handleWalletHasWallet(requestId, port);
|
||||
break;
|
||||
|
||||
case 'walletGetBalance':
|
||||
await handleWalletGetBalance(requestId, port);
|
||||
void handleWalletGetBalance(requestId, port);
|
||||
break;
|
||||
|
||||
case 'walletGetMints':
|
||||
await handleWalletGetMints(requestId, port);
|
||||
void handleWalletGetMints(requestId, port);
|
||||
break;
|
||||
|
||||
case 'walletReceiveToken':
|
||||
await handleWalletReceiveToken(requestId, token, description, port);
|
||||
void handleWalletReceiveToken(requestId, token, description, port);
|
||||
break;
|
||||
|
||||
case 'walletSendToken':
|
||||
await handleWalletSendToken(requestId, amount, memo, mint, port);
|
||||
void handleWalletSendToken(requestId, amount, memo, mint, port);
|
||||
break;
|
||||
|
||||
case 'walletSendNutzap':
|
||||
await handleWalletSendNutzap(
|
||||
void handleWalletSendNutzap(
|
||||
requestId,
|
||||
amount,
|
||||
memo,
|
||||
@@ -7090,23 +7027,23 @@ self.onconnect = (event) => {
|
||||
break;
|
||||
|
||||
case 'walletCreateDeposit':
|
||||
await handleWalletCreateDeposit(requestId, amount, mint, port);
|
||||
void handleWalletCreateDeposit(requestId, amount, mint, port);
|
||||
break;
|
||||
|
||||
case 'walletPayInvoice':
|
||||
await handleWalletPayInvoice(requestId, invoice, mint, port);
|
||||
void handleWalletPayInvoice(requestId, invoice, mint, port);
|
||||
break;
|
||||
|
||||
case 'walletAddMint':
|
||||
await handleWalletAddMint(requestId, mintUrl, port);
|
||||
void handleWalletAddMint(requestId, mintUrl, port);
|
||||
break;
|
||||
|
||||
case 'walletRemoveMint':
|
||||
await handleWalletRemoveMint(requestId, mintUrl, port);
|
||||
void handleWalletRemoveMint(requestId, mintUrl, port);
|
||||
break;
|
||||
|
||||
case 'walletGetTransactions':
|
||||
await handleWalletGetTransactions(requestId, port);
|
||||
void handleWalletGetTransactions(requestId, port);
|
||||
break;
|
||||
|
||||
case 'walletShutdown':
|
||||
@@ -7114,11 +7051,11 @@ self.onconnect = (event) => {
|
||||
break;
|
||||
|
||||
case 'walletCheckProofs':
|
||||
await handleWalletCheckProofs(requestId, port);
|
||||
void handleWalletCheckProofs(requestId, port);
|
||||
break;
|
||||
|
||||
case 'walletRepublish':
|
||||
await handleWalletRepublish(requestId, port);
|
||||
void handleWalletRepublish(requestId, port);
|
||||
break;
|
||||
|
||||
default:
|
||||
|
||||
Reference in New Issue
Block a user