Files
bitvid/js/nostr/managers/ConnectionManager.js
T
Claude 804b3323c4 Add code health lint guards, consolidate relay constants, document agent prompts
Three CI-enforced lint scripts to prevent common codebase growth problems:

- lint:file-size — enforces 1000-line max for new JS files, grandfathers
  existing oversized files and prevents them from growing further
- lint:innerhtml — tracks innerHTML usage per-file with a baseline count,
  blocks new innerHTML additions (guides agents toward safe DOM APIs)
- Relay constants consolidated from duplicate definitions in client.js and
  ConnectionManager.js into shared js/nostr/relayConstants.js
- AGENTS.md Section 13: documents all three rules with examples
- docs/scheduled-agent-prompts.md: copy-paste prompts for recurring
  file decomposition, innerHTML migration, and constant dedup tasks

https://claude.ai/code/session_01LzErpH9FNw4hAPsjoYb1v9
2026-02-10 03:25:18 +00:00

965 lines
27 KiB
JavaScript

import {
DEFAULT_RELAY_URLS,
RELAY_URLS,
ensureNostrTools,
resolveSimplePoolConstructor,
shimLegacySimplePoolMethods,
nostrToolsBootstrapFailure,
} from "../toolkit.js";
import { devLogger, userLogger } from "../../utils/logger.js";
import { isDevMode } from "../../config.js";
import { sanitizeRelayList } from "../nip46Client.js";
import {
logCountTimeoutCleanupFailure,
logRelayCountFailure,
} from "../countDiagnostics.js";
import {
RELAY_CONNECT_TIMEOUT_MS,
RELAY_RECONNECT_BASE_DELAY_MS,
RELAY_RECONNECT_MAX_DELAY_MS,
RELAY_RECONNECT_MAX_ATTEMPTS,
RELAY_BACKOFF_BASE_DELAY_MS,
RELAY_BACKOFF_MAX_DELAY_MS,
RELAY_CIRCUIT_BREAKER_THRESHOLD,
RELAY_CIRCUIT_BREAKER_COOLDOWN_MS,
RELAY_FAILURE_WINDOW_MS,
RELAY_FAILURE_WINDOW_THRESHOLD,
RELAY_SUMMARY_LOG_INTERVAL_MS,
} from "../relayConstants.js";
function withRequestTimeout(promise, timeoutMs, onTimeout, message = "Request timed out") {
const resolvedTimeout = Number(timeoutMs);
const effectiveTimeout =
Number.isFinite(resolvedTimeout) && resolvedTimeout > 0
? Math.floor(resolvedTimeout)
: 4000;
let timeoutId = null;
let settled = false;
return new Promise((resolve, reject) => {
timeoutId = setTimeout(() => {
if (settled) {
return;
}
settled = true;
if (typeof onTimeout === "function") {
try {
onTimeout();
} catch (cleanupError) {
logCountTimeoutCleanupFailure(cleanupError);
}
}
reject(new Error(message));
}, effectiveTimeout);
Promise.resolve(promise)
.then((value) => {
if (settled) {
return;
}
settled = true;
if (timeoutId) {
clearTimeout(timeoutId);
}
resolve(value);
})
.catch((error) => {
if (settled) {
return;
}
settled = true;
if (timeoutId) {
clearTimeout(timeoutId);
}
reject(error);
});
});
}
export class ConnectionManager {
constructor() {
this.pool = null;
this.poolPromise = null;
this.relays = sanitizeRelayList(Array.from(DEFAULT_RELAY_URLS));
this.readRelays = Array.from(this.relays);
this.writeRelays = Array.from(this.relays);
this.countUnsupportedRelays = new Set();
this.unreachableRelays = new Set();
this.relayBackoff = new Map();
this.relayFailureCounts = new Map();
this.relayFailureWindows = new Map();
this.relayCircuitBreakers = new Map();
this.relaySummaryLogTimestamps = new Map();
this.relayReconnectTimer = null;
this.relayReconnectAttempt = 0;
this.countRequestCounter = 0;
}
async ensurePool() {
if (this.pool) {
return this.pool;
}
if (this.poolPromise) {
return this.poolPromise;
}
const tools = await ensureNostrTools();
const SimplePool = resolveSimplePoolConstructor(tools);
if (typeof SimplePool !== "function") {
if (tools && typeof tools === "object") {
const availableKeys = Object.keys(tools).join(", ");
devLogger.warn(
"[nostr] NostrTools helpers did not expose SimplePool. Available keys:",
availableKeys
);
} else {
userLogger.warn(
"[nostr] NostrTools helpers were unavailable. Check that nostr-tools bundles can load on this domain."
);
}
if (nostrToolsBootstrapFailure) {
userLogger.warn(
"[nostr] nostr-tools bootstrap failure details:",
nostrToolsBootstrapFailure
);
}
const error = new Error(
"NostrTools SimplePool is unavailable. Verify that nostr-tools resources can load on this domain."
);
error.code = "nostr-simplepool-unavailable";
if (nostrToolsBootstrapFailure) {
error.bootstrapFailure = nostrToolsBootstrapFailure;
}
this.poolPromise = null;
throw error;
}
const creation = Promise.resolve().then(() => {
const instance = new SimplePool();
if (typeof instance.ensureRelay === "function") {
const originalEnsureRelay = instance.ensureRelay.bind(instance);
instance.ensureRelay = async (url) => {
const relay = await originalEnsureRelay(url);
if (relay && typeof relay.setMaxListeners === "function") {
try {
relay.setMaxListeners(200);
} catch (error) {
// ignore
}
}
return relay;
};
} else {
devLogger.warn(
"[nostr] SimplePool.ensureRelay missing; max listeners patch skipped."
);
}
shimLegacySimplePoolMethods(instance);
this.pool = instance;
return instance;
});
const shared = creation
.then((instance) => {
this.poolPromise = Promise.resolve(instance);
return instance;
})
.catch((error) => {
this.poolPromise = null;
throw error;
});
this.poolPromise = shared;
return shared;
}
async connectToRelays() {
const relayTargets = this.getHealthyRelays(this.relays);
if (!relayTargets.length) {
return [];
}
const results = await Promise.all(
relayTargets.map(
(url) =>
new Promise((resolve) => {
const sub = this.pool.sub([url], [{ kinds: [0], limit: 1 }]);
const timeout = setTimeout(() => {
sub.unsub();
resolve({ url, success: false });
}, RELAY_CONNECT_TIMEOUT_MS);
const succeed = () => {
clearTimeout(timeout);
sub.unsub();
resolve({ url, success: true });
};
sub.on("event", succeed);
sub.on("eose", succeed);
})
)
);
for (const result of results) {
if (result.success) {
this.clearRelayBackoff(result.url);
} else {
this.markRelayUnreachable(result.url, 60000, {
reason: "connect-timeout",
});
if (isDevMode) {
devLogger.warn(`[nostr] Marked relay as unreachable: ${result.url}`);
}
}
}
return results;
}
resolveRelayReconnectDelayMs(attempt) {
const safeAttempt = Number.isFinite(attempt) ? Math.max(0, attempt) : 0;
const computed = RELAY_RECONNECT_BASE_DELAY_MS * Math.pow(2, safeAttempt);
return Math.min(RELAY_RECONNECT_MAX_DELAY_MS, computed);
}
resetRelayReconnectState() {
this.relayReconnectAttempt = 0;
if (this.relayReconnectTimer) {
clearTimeout(this.relayReconnectTimer);
this.relayReconnectTimer = null;
}
}
scheduleRelayReconnect({ reason = "retry" } = {}) {
if (this.relayReconnectTimer) {
return;
}
if (this.relayReconnectAttempt >= RELAY_RECONNECT_MAX_ATTEMPTS) {
if (isDevMode) {
devLogger.debug(
"[nostr] Relay reconnect attempts exhausted.",
{
attempts: this.relayReconnectAttempt,
reason,
},
);
}
return;
}
const delayMs = this.resolveRelayReconnectDelayMs(this.relayReconnectAttempt);
const attemptNumber = this.relayReconnectAttempt + 1;
this.relayReconnectAttempt = attemptNumber;
if (isDevMode) {
devLogger.debug("[nostr] Scheduling relay reconnect attempt.", {
attempt: attemptNumber,
delayMs,
reason,
});
}
this.relayReconnectTimer = setTimeout(async () => {
this.relayReconnectTimer = null;
try {
const results = await this.connectToRelays();
const successful = results.some((result) => result.success);
if (successful) {
this.resetRelayReconnectState();
return;
}
} catch (error) {
devLogger.warn("[nostr] Relay reconnect attempt failed:", error);
}
this.scheduleRelayReconnect({ reason: "reconnect-failed" });
}, delayMs);
}
logRelaySummary({
key,
level = "warn",
message,
payload,
} = {}) {
if (!key || !message) {
return;
}
const now = Date.now();
const lastLogged = this.relaySummaryLogTimestamps.get(key) || 0;
if (now - lastLogged < RELAY_SUMMARY_LOG_INTERVAL_MS) {
return;
}
this.relaySummaryLogTimestamps.set(key, now);
const channel = isDevMode ? devLogger : userLogger;
const logFn =
typeof channel[level] === "function" ? channel[level] : channel.warn;
if (payload) {
logFn(`[nostr] ${message}`, payload);
} else {
logFn(`[nostr] ${message}`);
}
}
resolveRelayBackoffMs(failureCount, ttlOverride) {
const baseMs = RELAY_BACKOFF_BASE_DELAY_MS;
const maxMs = RELAY_BACKOFF_MAX_DELAY_MS;
const computed = Math.min(
maxMs,
baseMs * Math.pow(2, Math.max(0, failureCount - 1))
);
const override = Number(ttlOverride);
if (Number.isFinite(override) && override > 0) {
return Math.min(computed, Math.floor(override));
}
return computed;
}
clearRelayBackoff(url) {
if (!url || typeof url !== "string") {
return;
}
const normalized = url.trim();
if (!normalized) {
return;
}
const hadBackoffState =
this.relayBackoff.has(normalized) ||
this.unreachableRelays.has(normalized) ||
this.relayFailureCounts.has(normalized) ||
this.relayCircuitBreakers.has(normalized);
this.relayBackoff.delete(normalized);
this.relayFailureCounts.delete(normalized);
this.relayFailureWindows.delete(normalized);
this.unreachableRelays.delete(normalized);
this.relayCircuitBreakers.delete(normalized);
if (hadBackoffState) {
this.logRelaySummary({
key: `relay-recovered:${normalized}`,
level: "info",
message: "Relay recovered; backoff cleared.",
payload: { relay: normalized },
});
}
}
recordRelayFailureWindow(url) {
if (!url || typeof url !== "string") {
return 0;
}
const normalized = url.trim();
if (!normalized) {
return 0;
}
const now = Date.now();
const cutoff = now - RELAY_FAILURE_WINDOW_MS;
const history = this.relayFailureWindows.get(normalized) || [];
const filtered = history.filter(
(timestamp) => Number.isFinite(timestamp) && timestamp >= cutoff,
);
filtered.push(now);
this.relayFailureWindows.set(normalized, filtered);
return filtered.length;
}
markRelayUnreachable(url, ttlMs = 60000, { reason = null } = {}) {
if (!url || typeof url !== "string") {
return;
}
const normalized = url.trim();
if (!normalized) {
return;
}
const windowFailureCount = this.recordRelayFailureWindow(normalized);
const nextFailureCount = (this.relayFailureCounts.get(normalized) || 0) + 1;
this.relayFailureCounts.set(normalized, nextFailureCount);
const backoffMs = this.resolveRelayBackoffMs(nextFailureCount, ttlMs);
const retryAt = Date.now() + backoffMs;
this.relayBackoff.set(normalized, {
retryAt,
backoffMs,
failureCount: nextFailureCount,
reason,
});
this.unreachableRelays.add(normalized);
const shouldOpenCircuit =
nextFailureCount >= RELAY_CIRCUIT_BREAKER_THRESHOLD ||
windowFailureCount >= RELAY_FAILURE_WINDOW_THRESHOLD;
if (shouldOpenCircuit) {
const now = Date.now();
const openUntil = now + RELAY_CIRCUIT_BREAKER_COOLDOWN_MS;
const existing = this.relayCircuitBreakers.get(normalized);
const nextOpenUntil =
existing && Number.isFinite(existing.openUntil)
? Math.max(existing.openUntil, openUntil)
: openUntil;
const circuitReason =
reason ||
(nextFailureCount >= RELAY_CIRCUIT_BREAKER_THRESHOLD
? "consecutive-failures"
: "windowed-failures");
this.relayCircuitBreakers.set(normalized, {
openUntil: nextOpenUntil,
failureCount: nextFailureCount,
reason: circuitReason,
});
this.logRelaySummary({
key: `relay-circuit:${normalized}`,
level: "warn",
message: "Circuit breaker opened for relay.",
payload: {
relay: normalized,
openUntil: nextOpenUntil,
failureCount: nextFailureCount,
reason: circuitReason,
},
});
}
this.logRelaySummary({
key: `relay-backoff:${normalized}`,
level: "warn",
message: "Relay backoff applied.",
payload: {
relay: normalized,
backoffMs,
failureCount: nextFailureCount,
retryAt,
reason,
},
});
}
getHealthyRelays(candidates) {
const source = Array.isArray(candidates) ? candidates : this.relays;
if (!this.unreachableRelays.size) {
return source;
}
const now = Date.now();
return source.filter((url) => {
const circuit = this.relayCircuitBreakers.get(url);
if (circuit && Number.isFinite(circuit.openUntil)) {
if (circuit.openUntil > now) {
return false;
}
this.relayCircuitBreakers.delete(url);
this.relayFailureCounts.delete(url);
this.relayFailureWindows.delete(url);
this.unreachableRelays.delete(url);
this.relayBackoff.delete(url);
this.logRelaySummary({
key: `relay-circuit-reset:${url}`,
level: "info",
message: "Circuit breaker reset for relay.",
payload: { relay: url },
});
}
if (!this.unreachableRelays.has(url)) {
return true;
}
const entry = this.relayBackoff.get(url);
if (!entry) {
return false;
}
if (Number.isFinite(entry.retryAt) && entry.retryAt > now) {
return false;
}
this.relayBackoff.delete(url);
this.relayFailureCounts.delete(url);
this.relayFailureWindows.delete(url);
this.unreachableRelays.delete(url);
this.logRelaySummary({
key: `relay-backoff-expired:${url}`,
level: "info",
message: "Relay backoff expired.",
payload: { relay: url },
});
return true;
});
}
applyRelayPreferences(preferences = {}) {
const normalizedPrefs =
preferences && typeof preferences === "object" ? preferences : {};
const sanitizedAll = sanitizeRelayList(
Array.isArray(normalizedPrefs.all)
? normalizedPrefs.all
: this.relays
);
const effectiveAll =
sanitizedAll.length > 0
? sanitizedAll
: sanitizeRelayList(Array.from(DEFAULT_RELAY_URLS));
const sanitizedRead = sanitizeRelayList(
Array.isArray(normalizedPrefs.read)
? normalizedPrefs.read
: effectiveAll
);
const sanitizedWrite = sanitizeRelayList(
Array.isArray(normalizedPrefs.write)
? normalizedPrefs.write
: effectiveAll
);
this.relays = effectiveAll.length ? effectiveAll : Array.from(RELAY_URLS);
this.readRelays = sanitizedRead.length ? sanitizedRead : Array.from(this.relays);
this.writeRelays = sanitizedWrite.length ? sanitizedWrite : Array.from(this.relays);
}
makeCountUnsupportedError(relayUrl) {
const normalizedUrl =
typeof relayUrl === "string" && relayUrl.trim()
? relayUrl.trim()
: "";
const error = new Error(
`[nostr] Relay ${normalizedUrl} does not support COUNT frames.`
);
error.code = "count-unsupported";
error.relay = normalizedUrl;
error.unsupported = true;
return error;
}
getRequestTimeoutMs(timeoutMs) {
const candidate = Number(timeoutMs);
if (Number.isFinite(candidate) && candidate > 0) {
return Math.floor(candidate);
}
const poolTimeout = Number(this.pool?.getTimeout);
if (Number.isFinite(poolTimeout) && poolTimeout > 0) {
return Math.floor(poolTimeout);
}
return 3400;
}
normalizeCountFilter(filter) {
if (!filter || typeof filter !== "object") {
return null;
}
const normalized = {};
const toStringArray = (value) => {
if (value === undefined || value === null) {
return [];
}
const source = Array.isArray(value) ? value : [value];
const collected = [];
for (const item of source) {
if (typeof item !== "string") {
continue;
}
const trimmed = item.trim();
if (!trimmed || collected.includes(trimmed)) {
continue;
}
collected.push(trimmed);
}
return collected;
};
if (filter.kinds !== undefined) {
const kindsSource = Array.isArray(filter.kinds)
? filter.kinds
: [filter.kinds];
const normalizedKinds = [];
const seenKinds = new Set();
for (const candidate of kindsSource) {
const parsed = Number(candidate);
if (!Number.isFinite(parsed)) {
continue;
}
const normalizedValue = Math.floor(parsed);
if (seenKinds.has(normalizedValue)) {
continue;
}
seenKinds.add(normalizedValue);
normalizedKinds.push(normalizedValue);
}
if (normalizedKinds.length) {
normalized.kinds = normalizedKinds;
}
}
const ids = toStringArray(filter.ids);
if (ids.length) {
normalized.ids = ids;
}
const authors = toStringArray(filter.authors);
if (authors.length) {
normalized.authors = authors;
}
for (const [key, value] of Object.entries(filter)) {
if (!key.startsWith("#")) {
continue;
}
const tagValues = toStringArray(value);
if (tagValues.length) {
normalized[key] = tagValues;
}
}
if (filter.since !== undefined) {
const parsedSince = Number(filter.since);
if (Number.isFinite(parsedSince)) {
normalized.since = Math.floor(parsedSince);
}
}
if (filter.until !== undefined) {
const parsedUntil = Number(filter.until);
if (Number.isFinite(parsedUntil)) {
normalized.until = Math.floor(parsedUntil);
}
}
if (filter.limit !== undefined) {
const parsedLimit = Number(filter.limit);
if (Number.isFinite(parsedLimit) && parsedLimit >= 0) {
normalized.limit = Math.floor(parsedLimit);
}
}
return Object.keys(normalized).length ? normalized : null;
}
normalizeCountFilters(filters) {
if (!filters) {
return [];
}
const list = Array.isArray(filters) ? filters : [filters];
const normalized = [];
for (const candidate of list) {
const normalizedFilter = this.normalizeCountFilter(candidate);
if (normalizedFilter) {
normalized.push(normalizedFilter);
}
}
return normalized;
}
generateCountRequestId(prefix = "count") {
this.countRequestCounter += 1;
if (this.countRequestCounter > Number.MAX_SAFE_INTEGER - 1) {
this.countRequestCounter = 1;
}
const normalizedPrefix =
typeof prefix === "string" && prefix.trim() ? prefix.trim() : "count";
const timestamp = Date.now().toString(36);
const counter = this.countRequestCounter.toString(36);
const random = Math.random().toString(36).slice(2, 8);
return `${normalizedPrefix}:${timestamp}:${counter}${random}`;
}
extractCountValue(payload) {
if (typeof payload === "number") {
const value = Math.floor(payload);
return value >= 0 ? value : 0;
}
if (payload && typeof payload === "object") {
const candidate =
typeof payload.count === "number"
? payload.count
: Number(payload.count);
if (Number.isFinite(candidate)) {
const value = Math.floor(candidate);
return value >= 0 ? value : 0;
}
}
const parsed = Number(payload);
if (Number.isFinite(parsed)) {
const value = Math.floor(parsed);
return value >= 0 ? value : 0;
}
return 0;
}
async sendRawCountFrame(relayUrl, filters, options = {}) {
if (!this.pool) {
throw new Error(
"Nostr pool not initialized. Call ensurePool() before requesting counts."
);
}
const normalizedUrl =
typeof relayUrl === "string" ? relayUrl.trim() : "";
if (!normalizedUrl) {
throw new Error("Invalid relay URL for COUNT request.");
}
if (this.countUnsupportedRelays.has(normalizedUrl)) {
throw this.makeCountUnsupportedError(normalizedUrl);
}
const normalizedFilters = this.normalizeCountFilters(filters);
if (!normalizedFilters.length) {
throw new Error("At least one filter is required for a COUNT request.");
}
const requestId =
typeof options.subId === "string" && options.subId.trim()
? options.subId.trim()
: this.generateCountRequestId();
let relay;
try {
relay = await this.pool.ensureRelay(normalizedUrl);
} catch (error) {
const connectError = new Error(
`Failed to connect to relay ${normalizedUrl}`
);
connectError.code = "relay-connect-failed";
this.markRelayUnreachable(normalizedUrl, 60000, {
reason: "connect-failed",
});
throw connectError;
}
if (!relay) {
const relayError = new Error(
`Relay ${normalizedUrl} is unavailable for COUNT requests.`
);
relayError.code = "relay-unavailable";
this.markRelayUnreachable(normalizedUrl, 60000, {
reason: "relay-unavailable",
});
throw relayError;
}
const frame = ["COUNT", requestId, ...normalizedFilters];
let countPromise;
if (
relay.openCountRequests instanceof Map &&
typeof relay.send === "function"
) {
countPromise = new Promise((resolve, reject) => {
const cleanup = () => {
if (relay.openCountRequests instanceof Map) {
relay.openCountRequests.delete(requestId);
}
};
relay.openCountRequests.set(requestId, {
resolve: (value) => {
cleanup();
resolve(value);
},
reject: (error) => {
cleanup();
reject(error);
},
});
let sendResult;
try {
sendResult = relay.send(JSON.stringify(frame));
} catch (error) {
cleanup();
reject(error);
return;
}
if (sendResult && typeof sendResult.catch === "function") {
sendResult.catch((error) => {
cleanup();
reject(error);
});
}
});
} else if (typeof relay.count === "function") {
countPromise = relay.count(normalizedFilters, { id: requestId });
} else {
this.countUnsupportedRelays.add(normalizedUrl);
throw this.makeCountUnsupportedError(normalizedUrl);
}
const timeoutMs = this.getRequestTimeoutMs(options.timeoutMs);
let rawResult;
try {
rawResult = await withRequestTimeout(
countPromise,
timeoutMs,
() => {
if (relay?.openCountRequests instanceof Map) {
relay.openCountRequests.delete(requestId);
}
},
`COUNT request timed out after ${timeoutMs}ms`
);
} catch (error) {
if (
error instanceof Error &&
error.message.includes("COUNT request timed out")
) {
error.code = "count-timeout";
}
throw error;
}
const countValue = this.extractCountValue(rawResult);
return ["COUNT", requestId, { count: countValue }];
}
async countEventsAcrossRelays(filters, options = {}) {
const normalizedFilters = this.normalizeCountFilters(filters);
if (!normalizedFilters.length) {
return { total: 0, best: null, perRelay: [], partial: false };
}
const relayList =
Array.isArray(options.relays) && options.relays.length
? options.relays
: Array.isArray(this.relays) && this.relays.length
? this.relays
: RELAY_URLS;
const normalizedRelayList = relayList
.map((url) => (typeof url === "string" ? url.trim() : ""))
.filter(Boolean);
const eligibleRelays = this.getHealthyRelays(normalizedRelayList);
const eligibleRelaySet = new Set(eligibleRelays);
const activeRelays = [];
const precomputedEntries = [];
for (const url of normalizedRelayList) {
if (this.countUnsupportedRelays.has(url)) {
const error = this.makeCountUnsupportedError(url);
precomputedEntries.push({
url,
ok: false,
error,
unsupported: true,
});
continue;
}
if (!eligibleRelaySet.has(url)) {
precomputedEntries.push({
url,
ok: false,
skipped: true,
reason: "backoff",
});
continue;
}
activeRelays.push(url);
}
const activeResults = await Promise.all(
activeRelays.map(async (url) => {
try {
const frame = await this.sendRawCountFrame(url, normalizedFilters, {
timeoutMs: options.timeoutMs,
});
const count = this.extractCountValue(frame?.[2]);
this.clearRelayBackoff(url);
return { url, ok: true, frame, count };
} catch (error) {
const isUnsupported = error?.code === "count-unsupported";
const isTimeout =
error?.code === "count-timeout" || error?.code === "timeout";
if (isUnsupported) {
this.countUnsupportedRelays.add(url);
} else {
logRelayCountFailure(url, error);
// If the count request timed out, we don't want to kill the entire relay connection
// because it might still be good for subscriptions.
// We only trip the circuit breaker for hard errors.
if (!isTimeout) {
this.markRelayUnreachable(url, 60000, {
reason: "count-error",
});
} else if (isDevMode) {
devLogger.warn(
`[nostr] Relay ${url} count timed out (ignored for circuit breaker).`
);
}
}
return {
url,
ok: false,
error,
unsupported: isUnsupported,
timedOut: isTimeout,
errorCode: error?.code || null,
};
}
})
);
const resultsByUrl = new Map();
for (const entry of [...precomputedEntries, ...activeResults]) {
if (entry && typeof entry.url === "string") {
resultsByUrl.set(entry.url, entry);
}
}
const perRelayResults = normalizedRelayList.map((url) => {
if (resultsByUrl.has(url)) {
return resultsByUrl.get(url);
}
return { url, ok: false };
});
let bestEstimate = null;
const perRelay = perRelayResults.map((entry) => {
if (!entry || !entry.ok) {
return entry;
}
const numericValue = Number(entry.count);
const normalizedCount =
Number.isFinite(numericValue) && numericValue >= 0 ? numericValue : 0;
const normalizedEntry = {
...entry,
count: normalizedCount,
};
if (!Number.isFinite(numericValue) || numericValue < 0) {
normalizedEntry.rawCount = entry.count;
}
if (
!bestEstimate ||
normalizedCount > bestEstimate.count ||
(bestEstimate && normalizedCount === bestEstimate.count && !bestEstimate.frame)
) {
bestEstimate = {
relay: normalizedEntry.url,
count: normalizedCount,
frame: normalizedEntry.frame,
};
}
return normalizedEntry;
});
const total = bestEstimate ? bestEstimate.count : 0;
const partial = perRelayResults.some(
(entry) => entry?.timedOut || entry?.skipped
);
return {
total,
best: bestEstimate,
perRelay,
partial,
};
}
}