mirror of
https://github.com/PR0M3TH3AN/bitvid.git
synced 2026-10-31 09:52:24 +00:00
Merge pull request #205 from PR0M3TH3AN/codex/fix-console-warnings-and-errors
Handle nostr readiness and count fallback
This commit is contained in:
2 files changed
+147
-16
No files matched your search
+45
-13
@@ -194,8 +194,27 @@ function clearLegacyStorageFor(listKey) {
|
||||
}
|
||||
}
|
||||
|
||||
function ensureNostrReady() {
|
||||
if (!nostrClient || !nostrClient.pool) {
|
||||
async function ensureNostrReady() {
|
||||
if (!nostrClient) {
|
||||
throw createError(
|
||||
"nostr-unavailable",
|
||||
"Nostr relay pool is not initialized."
|
||||
);
|
||||
}
|
||||
|
||||
if (typeof nostrClient.waitForPool === "function") {
|
||||
try {
|
||||
await nostrClient.waitForPool();
|
||||
} catch (error) {
|
||||
throw createError(
|
||||
"nostr-unavailable",
|
||||
"Nostr relay pool is not initialized.",
|
||||
error
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (!nostrClient.pool) {
|
||||
throw createError(
|
||||
"nostr-unavailable",
|
||||
"Nostr relay pool is not initialized."
|
||||
@@ -257,7 +276,7 @@ function extractNpubsFromEvent(event) {
|
||||
}
|
||||
|
||||
async function loadNostrList(identifier) {
|
||||
const relays = ensureNostrReady();
|
||||
const relays = await ensureNostrReady();
|
||||
const dTagValue = `${ADMIN_LIST_NAMESPACE}:${identifier}`;
|
||||
const filter = {
|
||||
kinds: [30000],
|
||||
@@ -401,14 +420,27 @@ function publishToRelay(url, signedEvent, listKey) {
|
||||
}
|
||||
|
||||
if (typeof pub.on === "function") {
|
||||
pub.on("ok", () => finalize(true));
|
||||
pub.on("seen", () => finalize(true));
|
||||
pub.on("failed", (reason) => {
|
||||
const error =
|
||||
reason instanceof Error ? reason : new Error(String(reason || "failed"));
|
||||
finalize(false, error);
|
||||
});
|
||||
return;
|
||||
try {
|
||||
pub.on("ok", () => finalize(true));
|
||||
pub.on("seen", () => finalize(true));
|
||||
pub.on("failed", (reason) => {
|
||||
const error =
|
||||
reason instanceof Error
|
||||
? reason
|
||||
: new Error(String(reason || "failed"));
|
||||
finalize(false, error);
|
||||
});
|
||||
return;
|
||||
} catch (listenerError) {
|
||||
if (isDevMode) {
|
||||
console.warn(
|
||||
`[adminListStore] Failed to wire publish listeners for ${url}:`,
|
||||
listenerError
|
||||
);
|
||||
}
|
||||
finalize(true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof pub.then === "function") {
|
||||
@@ -433,7 +465,7 @@ function publishToRelay(url, signedEvent, listKey) {
|
||||
}
|
||||
|
||||
async function persistNostrState(actorNpub, updates = {}) {
|
||||
ensureNostrReady();
|
||||
await ensureNostrReady();
|
||||
const extension = window?.nostr;
|
||||
if (!extension || typeof extension.signEvent !== "function") {
|
||||
throw createError(
|
||||
@@ -510,7 +542,7 @@ async function persistNostrState(actorNpub, updates = {}) {
|
||||
throw createError("signature-failed", "Failed to sign admin list event.", error);
|
||||
}
|
||||
|
||||
const relays = ensureNostrReady();
|
||||
const relays = await ensureNostrReady();
|
||||
const publishResults = await Promise.all(
|
||||
relays.map((url) => publishToRelay(url, signedEvent, listKey))
|
||||
);
|
||||
|
||||
+102
-3
@@ -36,6 +36,7 @@ const NIP07_LOGIN_TIMEOUT_ERROR_MESSAGE =
|
||||
"Timed out waiting for the NIP-07 extension. Check the extension prompt and try again.";
|
||||
const SESSION_ACTOR_STORAGE_KEY = "bitvid:sessionActor:v1";
|
||||
const WATCH_HISTORY_CACHE_STORAGE_KEY = "bitvid:watchHistoryCache:v1";
|
||||
const COUNT_UNSUPPORTED_COOLDOWN_MS = 10 * 60 * 1000; // 10 minutes
|
||||
|
||||
// To limit error spam
|
||||
let errorLogCount = 0;
|
||||
@@ -55,6 +56,36 @@ function logErrorOnce(message, eventContent = null) {
|
||||
}
|
||||
}
|
||||
|
||||
function createDeferred() {
|
||||
let resolveFn;
|
||||
let rejectFn;
|
||||
let state = "pending";
|
||||
const promise = new Promise((resolve, reject) => {
|
||||
resolveFn = (value) => {
|
||||
if (state !== "pending") {
|
||||
return;
|
||||
}
|
||||
state = "fulfilled";
|
||||
resolve(value);
|
||||
};
|
||||
rejectFn = (error) => {
|
||||
if (state !== "pending") {
|
||||
return;
|
||||
}
|
||||
state = "rejected";
|
||||
reject(error);
|
||||
};
|
||||
});
|
||||
|
||||
return {
|
||||
promise,
|
||||
resolve: resolveFn,
|
||||
reject: rejectFn,
|
||||
isSettled: () => state !== "pending",
|
||||
getState: () => state,
|
||||
};
|
||||
}
|
||||
|
||||
function withNip07Timeout(operation) {
|
||||
let timeoutId;
|
||||
const timeoutPromise = new Promise((_, reject) => {
|
||||
@@ -1108,6 +1139,9 @@ class NostrClient {
|
||||
this.watchHistoryRepublishTimers = new Map();
|
||||
this.watchHistoryCacheTtlMs = WATCH_HISTORY_CACHE_TTL_MS;
|
||||
this.countRequestCounter = 0;
|
||||
this.countUnsupportedRelays = new Map();
|
||||
|
||||
this._poolReadyDeferred = createDeferred();
|
||||
}
|
||||
|
||||
restoreLocalData() {
|
||||
@@ -3597,13 +3631,19 @@ class NostrClient {
|
||||
.filter((r) => r.success)
|
||||
.map((r) => r.url);
|
||||
if (successfulRelays.length === 0) {
|
||||
throw new Error("No relays connected");
|
||||
const error = new Error("No relays connected");
|
||||
this._poolReadyDeferred.reject(error);
|
||||
this.pool = null;
|
||||
throw error;
|
||||
}
|
||||
if (isDevMode) {
|
||||
console.log(`Connected to ${successfulRelays.length} relay(s)`);
|
||||
}
|
||||
this._poolReadyDeferred.resolve();
|
||||
} catch (err) {
|
||||
console.error("Nostr init failed:", err);
|
||||
this._poolReadyDeferred.reject(err);
|
||||
this.pool = null;
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
@@ -4699,6 +4739,46 @@ class NostrClient {
|
||||
return normalized;
|
||||
}
|
||||
|
||||
markCountUnsupported(relayUrl, ttlMs = COUNT_UNSUPPORTED_COOLDOWN_MS) {
|
||||
const normalizedUrl = typeof relayUrl === "string" ? relayUrl.trim() : "";
|
||||
if (!normalizedUrl) {
|
||||
return;
|
||||
}
|
||||
const now = Date.now();
|
||||
const expiry = now + Math.max(0, Number(ttlMs) || 0);
|
||||
this.countUnsupportedRelays.set(normalizedUrl, expiry);
|
||||
}
|
||||
|
||||
isCountTemporarilyUnsupported(relayUrl) {
|
||||
const normalizedUrl = typeof relayUrl === "string" ? relayUrl.trim() : "";
|
||||
if (!normalizedUrl) {
|
||||
return false;
|
||||
}
|
||||
const expiry = this.countUnsupportedRelays.get(normalizedUrl);
|
||||
if (!expiry) {
|
||||
return false;
|
||||
}
|
||||
if (Date.now() >= expiry) {
|
||||
this.countUnsupportedRelays.delete(normalizedUrl);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
waitForPool(timeoutMs) {
|
||||
if (this.pool && this._poolReadyDeferred.isSettled()) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
const promise = this._poolReadyDeferred.promise;
|
||||
const numericTimeout = Number(timeoutMs);
|
||||
if (!Number.isFinite(numericTimeout) || numericTimeout <= 0) {
|
||||
return promise;
|
||||
}
|
||||
|
||||
return withRequestTimeout(promise, numericTimeout, null, "Timed out waiting for Nostr pool readiness");
|
||||
}
|
||||
|
||||
generateCountRequestId(prefix = "count") {
|
||||
this.countRequestCounter += 1;
|
||||
if (this.countRequestCounter > Number.MAX_SAFE_INTEGER - 1) {
|
||||
@@ -4818,6 +4898,7 @@ class NostrClient {
|
||||
} else if (typeof relay.count === "function") {
|
||||
countPromise = relay.count(normalizedFilters, { id: requestId });
|
||||
} else {
|
||||
this.markCountUnsupported(normalizedUrl);
|
||||
throw new Error(
|
||||
`[nostr] Relay ${normalizedUrl} does not support COUNT frames.`
|
||||
);
|
||||
@@ -4854,6 +4935,13 @@ class NostrClient {
|
||||
|
||||
const perRelayResults = await Promise.all(
|
||||
relayList.map(async (url) => {
|
||||
if (this.isCountTemporarilyUnsupported(url)) {
|
||||
return {
|
||||
url,
|
||||
ok: false,
|
||||
error: new Error("COUNT unsupported (cached)"),
|
||||
};
|
||||
}
|
||||
try {
|
||||
const frame = await this.sendRawCountFrame(url, normalizedFilters, {
|
||||
timeoutMs: options.timeoutMs,
|
||||
@@ -4861,8 +4949,19 @@ class NostrClient {
|
||||
const count = this.extractCountValue(frame?.[2]);
|
||||
return { url, ok: true, frame, count };
|
||||
} catch (error) {
|
||||
if (isDevMode) {
|
||||
console.warn(`[nostr] COUNT request failed on ${url}:`, error);
|
||||
if (
|
||||
error &&
|
||||
typeof error.message === "string" &&
|
||||
error.message.includes("does not support COUNT frames")
|
||||
) {
|
||||
this.markCountUnsupported(url);
|
||||
logErrorOnce(`[nostr] COUNT disabled for ${url} (no support).`);
|
||||
} else if (isDevMode) {
|
||||
const message =
|
||||
error && typeof error.message === "string"
|
||||
? error.message
|
||||
: String(error);
|
||||
logErrorOnce(`[nostr] COUNT request failed on ${url}: ${message}`);
|
||||
}
|
||||
return { url, ok: false, error };
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user