mirror of
https://github.com/PR0M3TH3AN/bitvid.git
synced 2026-10-27 16:02:26 +00:00
publishEventToRelay expected the old nostr-tools publish handle (.on/.then), but
nostr-tools 2.17 SimplePool.publish(relays, event) returns an ARRAY of per-relay
promises. The array has neither .on nor .then, so the code (a) fell through to
the "optimistic success" path (publishes were never truly confirmed) and (b)
never attached a .catch to the inner promise — so every relay that rejected
("blocked", "auth-required", "publish timed out", websocket error, …) leaked an
unhandled promise rejection. With a large/flaky relay list (e.g. a 35-relay
account hitting many paid/restricted relays) that floods the event loop and can
freeze the tab — observed when republishing the relay list and when publishing a
video.
Fix: detect the array return and Promise.allSettled it — any fulfilled = success,
otherwise a failure result; every rejection is consumed. Confirmed publishes now
resolve from real acks instead of the optimistic fallback.
New tests assert a rejecting array-promise becomes a failure result with NO
unhandled rejection leak, a fulfilling one succeeds, and an all-rejecting publish
still resolves. Mutation-verified; existing publish tests pass under the suite.
Co-Authored-By: Claude Opus 4.8 <[email protected]>
65 lines
2.5 KiB
JavaScript
65 lines
2.5 KiB
JavaScript
// nostr-tools SimplePool.publish(relays, event) returns an ARRAY of per-relay
|
|
// promises. publishEventToRelay must consume them: a relay that rejects
|
|
// ("blocked", "auth-required", "publish timed out", websocket error, …) must be
|
|
// turned into a {success:false} result, NOT leak as an unhandled promise
|
|
// rejection. With a large/flaky relay list, leaked rejections flood the event
|
|
// loop and can freeze the tab (observed when republishing a 35-relay list).
|
|
|
|
import "./test-helpers/setup-localstorage.mjs";
|
|
import assert from "node:assert/strict";
|
|
import test from "node:test";
|
|
import { publishEventToRelay } from "../js/nostrPublish.js";
|
|
|
|
const EVENT = { id: "e1", kind: 1, content: "hi" };
|
|
|
|
// A pool whose publish() returns an array of promises, like nostr-tools 2.17.
|
|
function arrayReturningPool(makePromise) {
|
|
return {
|
|
publish(urls) {
|
|
return urls.map(() => makePromise());
|
|
},
|
|
};
|
|
}
|
|
|
|
test("a rejecting array-promise becomes a failure result (no unhandled rejection)", async () => {
|
|
const unhandled = [];
|
|
const onUnhandled = (reason) => unhandled.push(reason);
|
|
process.on("unhandledRejection", onUnhandled);
|
|
try {
|
|
const pool = arrayReturningPool(() =>
|
|
Promise.reject(new Error("blocked: pubkey is not allowed to publish")),
|
|
);
|
|
|
|
const result = await publishEventToRelay(pool, "wss://relay.example", EVENT);
|
|
|
|
assert.equal(result.success, false, "a rejecting publish is a failure result");
|
|
assert.equal(result.url, "wss://relay.example");
|
|
assert.ok(result.error, "the rejection reason is captured on the result");
|
|
|
|
// Let any leaked microtask rejections surface before asserting.
|
|
await new Promise((r) => setTimeout(r, 10));
|
|
assert.equal(
|
|
unhandled.length,
|
|
0,
|
|
`no unhandled rejections should leak (saw ${unhandled.length})`,
|
|
);
|
|
} finally {
|
|
process.off("unhandledRejection", onUnhandled);
|
|
}
|
|
});
|
|
|
|
test("a fulfilling array-promise becomes a success result", async () => {
|
|
const pool = arrayReturningPool(() => Promise.resolve("ok"));
|
|
const result = await publishEventToRelay(pool, "wss://relay.example", EVENT);
|
|
assert.equal(result.success, true);
|
|
assert.equal(result.url, "wss://relay.example");
|
|
});
|
|
|
|
test("all-rejecting publish still resolves (does not hang) with a failure", async () => {
|
|
const pool = arrayReturningPool(() => Promise.reject(new Error("auth-required")));
|
|
const result = await publishEventToRelay(pool, "wss://relay.example", EVENT, {
|
|
timeoutMs: 2000,
|
|
});
|
|
assert.equal(result.success, false);
|
|
});
|