UNPKG

nostr-websocket-utils

Version:

Robust WebSocket utilities for Nostr applications with automatic reconnection, supporting both ESM and CommonJS. Features channel-based messaging, heartbeat monitoring, message queueing, and comprehensive error handling with type-safe handlers.

101 lines 3.35 kB
/** * @file Message queue implementation * @module core/queue */ import { MessagePriority } from '../types/index.js'; import { createLogger } from '../utils/logger.js'; /** * Message queue implementation for handling WebSocket messages */ export class MessageQueue { constructor(sender, options = {}) { this.sender = sender; this.options = options; this.queue = []; this.processing = false; this.logger = createLogger('MessageQueue'); } /** * Add a message to the queue */ async enqueue(message) { if (this.options.maxSize && this.queue.length >= this.options.maxSize) { throw new Error('Queue is full'); } const [type, ...data] = message; const queueItem = { type, data: data.length === 1 ? data[0] : data, priority: MessagePriority.NORMAL, queuedAt: Date.now(), retryCount: 0 }; this.queue.push(queueItem); this.queue.sort((a, b) => (a.priority === b.priority) ? (a.queuedAt - b.queuedAt) : (a.priority === MessagePriority.HIGH ? -1 : 1)); if (!this.processing) { this.processQueue().catch(error => { this.logger.error({ error }, 'Error processing queue'); }); } } /** * Process messages in the queue */ async processQueue() { if (this.processing || this.queue.length === 0) { return; } this.processing = true; try { while (this.queue.length > 0) { const item = this.queue[0]; const message = [item.type, item.data]; try { await this.sender(message); this.queue.shift(); } catch (error) { this.logger.error({ error, message }, 'Failed to send message'); if (this.options.maxRetries && item.retryCount >= this.options.maxRetries) { this.logger.warn({ message }, 'Max retries reached, removing message from queue'); this.queue.shift(); continue; } item.retryCount++; await new Promise(resolve => setTimeout(resolve, this.options.retryDelay || 1000)); } } } finally { this.processing = false; } // Clean up stale messages (reverse iteration to avoid index mutation bug) if (this.options.staleTimeout) { const now = Date.now(); const staleTimeout = this.options.staleTimeout; for (let i = this.queue.length - 1; i >= 0; i--) { if (now - this.queue[i].queuedAt > staleTimeout) { this.logger.warn({ message: this.queue[i] }, 'Message is stale, removing from queue'); this.queue.splice(i, 1); } } } } /** * Get the current size of the queue */ getSize() { return this.queue.length; } /** * Clear all messages from the queue */ clear() { this.queue.length = 0; } } //# sourceMappingURL=queue.js.map