You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
77 lines
2.7 KiB
77 lines
2.7 KiB
import { SimplePool } from 'nostr-tools'; |
|
import { sanitizeErrorMessage } from '../utils/error-sanitizer.js'; |
|
|
|
/** |
|
* Publish event to Nostr relays using SimplePool |
|
* |
|
* This function publishes to all relays in parallel. Each relay has its own |
|
* timeout (default 4400ms) to prevent hanging when relays fail. |
|
* |
|
* @param {Object} event - Nostr event to publish |
|
* @param {string[]} relays - Array of relay URLs |
|
* @param {Uint8Array} privateKeyBytes - Private key bytes (required for auth if needed) |
|
* @param {string} pubkey - Public key (optional, for logging) |
|
* @returns {Promise<{success: string[], failed: Array<{relay: string, error: string}>}>} |
|
*/ |
|
export async function publishToRelays(event, relays, privateKeyBytes, pubkey = null) { |
|
if (!privateKeyBytes) { |
|
throw new Error('Private key is required for publishing events'); |
|
} |
|
|
|
if (!relays || relays.length === 0) { |
|
return { success: [], failed: [] }; |
|
} |
|
|
|
const pool = new SimplePool(); |
|
const success = []; |
|
const failed = []; |
|
|
|
try { |
|
// pool.publish returns an array of Promises, one for each relay |
|
// Each promise resolves to a string (reason) on success or rejects with an error on failure |
|
const publishPromises = pool.publish(relays, event); |
|
|
|
// Wait for all promises to settle (either resolve or reject) |
|
// Use allSettled to handle both successes and failures without throwing |
|
const results = await Promise.allSettled(publishPromises); |
|
|
|
// Process results - map back to relay URLs |
|
for (let i = 0; i < results.length; i++) { |
|
const result = results[i]; |
|
const relayUrl = relays[i]; |
|
|
|
if (result.status === 'fulfilled') { |
|
// Promise resolved successfully - relay accepted the event |
|
// The resolved value is a string (reason from the relay's OK message) |
|
success.push(relayUrl); |
|
} else { |
|
// Promise rejected - relay failed or timed out |
|
const errorMessage = result.reason instanceof Error |
|
? result.reason.message |
|
: String(result.reason); |
|
|
|
failed.push({ |
|
relay: relayUrl, |
|
error: sanitizeErrorMessage(errorMessage) |
|
}); |
|
} |
|
} |
|
} catch (error) { |
|
// Fallback error handling (shouldn't happen with allSettled, but just in case) |
|
const errorMessage = error instanceof Error ? error.message : String(error); |
|
const sanitizedError = sanitizeErrorMessage(errorMessage); |
|
|
|
for (const relayUrl of relays) { |
|
failed.push({ relay: relayUrl, error: sanitizedError }); |
|
} |
|
} finally { |
|
// Close all connections in the pool |
|
try { |
|
await pool.close(relays); |
|
} catch (closeError) { |
|
// Ignore close errors - connections may already be closed |
|
} |
|
} |
|
|
|
return { success, failed }; |
|
}
|
|
|