• v0.18.0 84c29e9b08

    0.18.0
    All checks were successful
    CI / gate (push) Successful in 40s
    CI / image (push) Successful in 14s
    Stable

    joeyr released this 2026-08-04 16:18:52 -05:00 | 188 commits to main since this release

    Changed

    • champion-counters.json is now built from Riot match-v5, not scraped from
      op.gg.
      generatedFrom.source reads riot. No schema change and no consumer
      change: source has always been z.string().min(1), "present so a future
      second source is a value, not a fork".

      Matchups come from a self-join of participant_stat — two participants in the
      same match, at the same role, on opposite sides of the win partition. It
      needed no capture phase, unlike runes and items: every column it reads has
      been recorded since the table existed, so the swap built a complete file from
      rows already on disk. Zero additional Riot requests; ~271 fewer op.gg pages a
      night.

      What changes downstream is the sample. op.gg's file was built from a
      population and a window nobody here could see — roughly 192,000 matches on
      16.15, against the ~12,000 this service had collected at the same point. Ours
      is smaller, it is the games this crawl actually observed in the seeded ladder
      band, and it grows for as long as the patch runs. Every cell carries its own
      games.

      Two differences worth expecting rather than debugging: games per matchup
      is roughly an order of magnitude lower
      and climbs across a patch instead of
      arriving whole, and mirror matchups are absent — the crawl is queue 420,
      where draft pick makes champion selection exclusive across both teams, so
      Yasuo never faces Yasuo.

    • tierList still comes from op.gg, and it is now the only thing that does
      for ranked.
      It is op.gg's {tier, rank} bucketing, which is a score;
      deriving one here by sorting champion-roles on win rate would be inventing a
      scoring function and publishing it as though measured. Five tier-list pages a
      night, down from 276.

      A night op.gg cannot be reached publishes five empty arrays, never
      yesterday's list — the file is judged as a whole on generatedFrom.patch, so a
      stale tier list inside a current-patch artifact reads as current. The matchups
      publish as normal, and lol-companion's TierListProvider already rescrapes a
      role it finds empty.

    • COUNTERS_ENABLED now gates both sources, ranked and classic, exactly as
      RUNES_ENABLED does. COUNTERS_MIN_COVERAGE and COUNTERS_MAX_MINUTES are
      classic-only from this release: they describe a page crawl, and the ranked
      build is a database self-join.

    • One op.gg client for the whole window. The ranked tier lists and the
      classic pass used to be sequential calls that each built their own; the ranked
      half is now consumed by the Riot half, so they can overlap. Sharing one client
      means one limiter and one pacing, rather than two limiters at twice the rate.

    Removed

    • rankedPass in index.ts. The only op.gg pass is classic — the mechanism,
      deliberately, rather than a flag that could put ranked counters back on op.gg.
      src/counters.ts and the op.gg page extractors are untouched and still live:
      queue 4310 is absent from match-v5, so op.gg remains classic's only source.

    Notes

    • No consumer change is required or expected. The schema is byte-identical
      and lol-companion's mirror parses source: 'riot' today.
    • The first published file after this deploys describes whatever the crawl has
      stored for the live patch. Mid-patch that is a full file; on patch-drop
      morning it is a thin one, correctly labelled — where the op.gg build used to
      abort on its coverage floor and leave yesterday's file live instead.
    • New index participant_stat_pair_idx (patch, match_id, role), applied at
      boot. It serves the self-join; the existing rollup index leads on champion and
      cannot.
    Downloads