diff --git a/js/nostr/client.js b/js/nostr/client.js index 039d6e17..bbf84da6 100644 --- a/js/nostr/client.js +++ b/js/nostr/client.js @@ -3814,10 +3814,27 @@ export class NostrClient { : this.relays; const healthyCandidates = this.getHealthyRelays(relaysToUse); + let relayCandidates = healthyCandidates; + if (!healthyCandidates.length && relaysToUse.length) { + const sanitizedFallback = sanitizeRelayList(relaysToUse); + const defaultFallback = sanitizeRelayList(Array.from(DEFAULT_RELAY_URLS)); + relayCandidates = sanitizedFallback.length + ? sanitizedFallback + : defaultFallback.length + ? defaultFallback + : sanitizeRelayList(Array.from(RELAY_URLS)); + devLogger.warn( + "[fetchListIncrementally] Healthy relays exhausted; using fallback relay list for one-off fetch.", + { + requested: relaysToUse, + fallback: relayCandidates, + }, + ); + } const readPreferences = new Set(Array.isArray(this.readRelays) ? this.readRelays : []); // Sort relays: prefer user's read relays first - const sortedRelays = healthyCandidates.sort((a, b) => { + const sortedRelays = relayCandidates.sort((a, b) => { const aPreferred = readPreferences.has(a); const bPreferred = readPreferences.has(b); if (aPreferred && !bPreferred) return -1; @@ -3844,7 +3861,7 @@ export class NostrClient { const pool = await this.ensurePool(); const actualFetchFn = typeof fetchFn === "function" ? fetchFn - : (r, f) => pool.list([r], [f]); + : (r, f, timeout) => pool.list([r], [f], { timeout }); const chunks = []; for (let i = 0; i < normalizedRelays.length; i += concurrencyLimit) { @@ -3902,7 +3919,7 @@ export class NostrClient { try { // Wrap fetch with a timeout to prevent hanging on slow relays let events = await withRequestTimeout( - actualFetchFn(relayUrl, filter), + actualFetchFn(relayUrl, filter, listTimeoutMs), listTimeoutMs, null, `Fetch from ${relayUrl} timed out` @@ -3937,7 +3954,7 @@ export class NostrClient { try { delete filter.since; const events = await withRequestTimeout( - actualFetchFn(relayUrl, filter), + actualFetchFn(relayUrl, filter, listTimeoutMs), listTimeoutMs, null, `Full fetch fallback from ${relayUrl} timed out` diff --git a/package-lock.json b/package-lock.json index 5a96352f..3c456c33 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2147,9 +2147,9 @@ } }, "node_modules/fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -3211,6 +3211,21 @@ "node": ">=18" } }, + "node_modules/playwright/node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, "node_modules/pngjs": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-7.0.0.tgz",