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

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 };
}