export interface QuoteSubscriptionPriorityTarget { route?: "auto" | "provider" | "broker"; surface?: "portfolio" | "watchlist" | "detail" | "monitor" | "inline" | "options" | "screener" | "unknown"; visible?: boolean; selected?: boolean; weight?: number; } function quoteSubscriptionPriorityScore(target: QuoteSubscriptionPriorityTarget): number { let score = Number.isFinite(target.weight) ? Math.max(0, target.weight ?? 0) : 0; if (target.selected) score += 10_000; if (target.visible) score += 5_000; if (target.surface === "detail" || target.surface === "monitor" || target.surface === "options") score += 4_000; if (target.surface === "portfolio" || target.surface === "watchlist") score += 1_000; if (target.surface === "screener") score += 700; if (target.surface === "inline") score += 200; return score; } const ROUTE_PRECEDENCE: Record, number> = { auto: 0, provider: 1, broker: 2, }; /** * Merges every pane's request for one instrument. Priority fields come from * the most important request, but the route does not: a broker request wins * whenever any pane makes it, and the router decides whether that broker can * stream. A cursor or focus change therefore never moves an instrument * between sources. */ export function mergeQuoteSubscriptionTargets( targets: Iterable, ): T | null { let selectedTarget: T | null = null; let route: QuoteSubscriptionPriorityTarget["route"]; let selectedScore = -1; let visible = false; let selected = false; let weight = 0; let hasVisible = false; let hasSelected = false; let hasWeight = false; for (const target of targets) { const score = quoteSubscriptionPriorityScore(target); if (!selectedTarget || score > selectedScore) { selectedTarget = target; selectedScore = score; } if (target.route && (!route || ROUTE_PRECEDENCE[target.route] > ROUTE_PRECEDENCE[route])) { route = target.route; } if (target.visible !== undefined) { hasVisible = true; visible ||= target.visible; } if (target.selected !== undefined) { hasSelected = true; selected ||= target.selected; } if (Number.isFinite(target.weight)) { hasWeight = true; weight = Math.max(weight, Math.max(0, target.weight ?? 0)); } } if (!selectedTarget) return null; const merged = { ...selectedTarget }; if (route) (merged as QuoteSubscriptionPriorityTarget).route = route; if (hasVisible) merged.visible = visible; else delete merged.visible; if (hasSelected) merged.selected = selected; else delete merged.selected; if (hasWeight) merged.weight = weight; else delete merged.weight; return merged; }