fix: use real Buzz relay EOSE

This commit is contained in:
Shakker
2026-07-31 09:06:16 +01:00
parent 828f88c5ca
commit b48984792f
2 changed files with 66 additions and 0 deletions

View File

@@ -0,0 +1,32 @@
import type { Filter, Relay } from "nostr-tools";
import { describe, expect, it, vi } from "vitest";
import { openBuzzRelaySubscription } from "./relay-subscription.js";
describe("openBuzzRelaySubscription", () => {
it("sends an explicit REQ without synthesizing EOSE", async () => {
vi.useFakeTimers();
const oneose = vi.fn();
const subscription = {
id: "sub:1",
close: vi.fn(),
} as unknown as ReturnType<Relay["prepareSubscription"]>;
const relay = {
idleSince: Date.now(),
ongoingOperations: 0,
prepareSubscription: vi.fn(() => subscription),
send: vi.fn(async () => {}),
} as unknown as Relay;
const filters: Filter[] = [{ kinds: [0], authors: ["a".repeat(64)] }];
const opened = openBuzzRelaySubscription(relay, filters, { oneose });
await vi.advanceTimersByTimeAsync(5_000);
expect(opened).toBe(subscription);
expect(relay.prepareSubscription).toHaveBeenCalledWith(filters, { oneose });
expect(relay.send).toHaveBeenCalledWith(JSON.stringify(["REQ", "sub:1", ...filters]));
expect(relay.ongoingOperations).toBe(1);
expect(relay.idleSince).toBeUndefined();
expect(oneose).not.toHaveBeenCalled();
vi.useRealTimers();
});
});

View File

@@ -0,0 +1,34 @@
import type { Filter, Relay } from "nostr-tools";
type BuzzRelaySubscriptionParams = Omit<Parameters<Relay["prepareSubscription"]>[1], "abort">;
export function openBuzzRelaySubscription(
relay: Relay,
filters: Filter[],
params: BuzzRelaySubscriptionParams,
): ReturnType<Relay["prepareSubscription"]> {
// Relay.subscribe() synthesizes EOSE after 4.4 seconds. Buzz needs the relay's
// real EOSE before replacing or closing subscriptions, otherwise an async REQ
// can register after CLOSE and remain orphaned on the server.
relay.idleSince = undefined;
relay.ongoingOperations += 1;
let subscription: ReturnType<Relay["prepareSubscription"]>;
try {
subscription = relay.prepareSubscription(filters, params);
} catch (error) {
relay.ongoingOperations -= 1;
if (relay.ongoingOperations === 0) {
relay.idleSince = Date.now();
relay.scheduleIdleClose();
}
throw error;
}
const frame = JSON.stringify(["REQ", subscription.id, ...filters]);
void relay.send(frame).catch((error: unknown) => {
const message = error instanceof Error ? error.message : String(error);
subscription.close(`Buzz relay subscription request failed: ${message}`);
});
return subscription;
}