{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "mrdoge-odds-selector-adapter",
  "title": "Odds Selector Adapter (Mr. Doge SDK)",
  "description": "Pairs Over/Under markets into OddsLine[] and picks a representative line, for Odds Selector's lines prop.",
  "dependencies": [
    "@mrdoge/protocol"
  ],
  "registryDependencies": [
    "odds-selector"
  ],
  "files": [
    {
      "path": "lib/mrdoge-adapters/odds-lines.ts",
      "content": "import type { Market } from \"@mrdoge/protocol\"\nimport type { OddsLine } from \"@/registry/mrdoge-ui/odds-selector/odds-selector\"\n\n// Over/Under outcomes only carry a generic \"O\"/\"U\" code, no threshold in\n// it. The threshold lives in the caption instead (\"Over 1.5\", \"Under\n// 1.5\"), so that's what has to be parsed to sort lines and label them.\nfunction parseThreshold(caption: string | null | undefined): number {\n  const match = caption?.match(/[\\d.]+/)\n  return match ? parseFloat(match[0]) : NaN\n}\n\n/**\n * Some bet types post one market per line instead of one market total.\n * SOCCER_UNDER_OVER posts a separate market for each Over/Under threshold\n * (0.5, 1.5, 2.5, ...). Pairs each market's two outcomes into one OddsLine\n * row and sorts by threshold, for OddsSelector's `lines` prop.\n */\nexport function toOddsLines(markets: Market[]): OddsLine[] {\n  const lines: (OddsLine & { threshold: number })[] = []\n  for (const market of markets) {\n    const over = market.lines.find((line) => line.code.startsWith(\"O\"))\n    const under = market.lines.find((line) => line.code.startsWith(\"U\"))\n    if (!over || !under) continue\n    const threshold = parseThreshold(over.caption ?? under.caption)\n    // Can't sort or label a line without a real threshold. Drop it\n    // rather than showing a broken \"NaN\" row.\n    if (Number.isNaN(threshold)) continue\n    lines.push({\n      id: market.id,\n      threshold,\n      label: String(threshold),\n      over: {\n        id: over.id,\n        label: over.caption ?? over.code,\n        price: over.price.toFixed(2),\n        suspended: !over.isAvailable,\n      },\n      under: {\n        id: under.id,\n        label: under.caption ?? under.code,\n        price: under.price.toFixed(2),\n        suspended: !under.isAvailable,\n      },\n    })\n  }\n  return lines.sort((a, b) => a.threshold - b.threshold)\n}\n\n/**\n * Picks the line closest to an even split: the Over/Under prices closest\n * to each other, e.g. Over 1.90 / Under 2.00 rather than a lopsided Over\n * 1.18 / Under 4.50. A reasonable single-market pick when there's only\n * room to show one Total Goals line, e.g. alongside other markets on a\n * board. The first market in the snapshot is otherwise arbitrary.\n */\nexport function pickMostBalancedMarket(markets: Market[]): Market | undefined {\n  let best: Market | undefined\n  let bestSpread = Infinity\n  for (const market of markets) {\n    const over = market.lines.find((line) => line.code.startsWith(\"O\"))\n    const under = market.lines.find((line) => line.code.startsWith(\"U\"))\n    if (!over || !under) continue\n    const spread = Math.abs(over.price - under.price)\n    if (spread < bestSpread) {\n      best = market\n      bestSpread = spread\n    }\n  }\n  return best\n}\n",
      "type": "registry:lib",
      "target": "lib/mrdoge-adapters/odds-lines.ts"
    }
  ],
  "type": "registry:lib"
}