mirror of
https://github.com/PR0M3TH3AN/bitvid.git
synced 2026-10-27 07:52:24 +00:00
- Added `js/nostr/nip46Queue.js` to manage concurrent RPC requests. - Updated `Nip46RpcClient` to serialize outgoing requests, preventing "rate-limited" errors. - Implemented exponential backoff for retries when relays reject events due to rate limits. - Added priority levels to `sendRpc` to prioritize user actions (signing, connecting) over background tasks (decrypting). - Verified with new unit tests in `tests/unit/nip46Queue.test.mjs`.
89 lines
2.1 KiB
JavaScript
89 lines
2.1 KiB
JavaScript
// js/nostr/nip46Queue.js
|
|
|
|
import { devLogger } from "../utils/logger.js";
|
|
|
|
export const NIP46_PRIORITY = Object.freeze({
|
|
HIGH: 10,
|
|
NORMAL: 5,
|
|
LOW: 1,
|
|
});
|
|
|
|
export class Nip46RequestQueue {
|
|
constructor({ minDelayMs = 250 } = {}) {
|
|
this.minDelayMs = minDelayMs;
|
|
this.queue = [];
|
|
this.running = false;
|
|
this.lastRunTime = 0;
|
|
}
|
|
|
|
enqueue(task, priority = NIP46_PRIORITY.NORMAL) {
|
|
return new Promise((resolve, reject) => {
|
|
this.queue.push({
|
|
task,
|
|
priority: Number.isFinite(priority) ? priority : NIP46_PRIORITY.NORMAL,
|
|
resolve,
|
|
reject,
|
|
addedAt: Date.now(),
|
|
});
|
|
// Sort by priority (descending), then by insertion time (ascending) for fairness
|
|
this.queue.sort((a, b) => {
|
|
if (b.priority !== a.priority) {
|
|
return b.priority - a.priority;
|
|
}
|
|
return a.addedAt - b.addedAt;
|
|
});
|
|
this.process();
|
|
});
|
|
}
|
|
|
|
async process() {
|
|
if (this.running) return;
|
|
this.running = true;
|
|
|
|
while (this.queue.length > 0) {
|
|
const now = Date.now();
|
|
const timeSinceLast = now - this.lastRunTime;
|
|
|
|
if (timeSinceLast < this.minDelayMs) {
|
|
const waitMs = this.minDelayMs - timeSinceLast;
|
|
await new Promise((r) => setTimeout(r, waitMs));
|
|
}
|
|
|
|
const item = this.queue.shift();
|
|
if (!item) break; // Should not happen given while condition
|
|
|
|
const { task, resolve, reject } = item;
|
|
|
|
try {
|
|
const result = await task();
|
|
this.lastRunTime = Date.now();
|
|
resolve(result);
|
|
} catch (error) {
|
|
this.lastRunTime = Date.now();
|
|
reject(error);
|
|
}
|
|
}
|
|
|
|
this.running = false;
|
|
}
|
|
|
|
/**
|
|
* Clears the queue, rejecting all pending tasks.
|
|
* Useful when disconnecting or destroying the client.
|
|
*/
|
|
clear(error) {
|
|
const pending = this.queue;
|
|
this.queue = [];
|
|
|
|
const rejection = error || new Error("Queue cleared");
|
|
|
|
for (const item of pending) {
|
|
try {
|
|
item.reject(rejection);
|
|
} catch (e) {
|
|
devLogger.warn("[nostr] Failed to reject cleared queue item:", e);
|
|
}
|
|
}
|
|
}
|
|
}
|