@claudinho/core 0.9.4 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -756,20 +756,31 @@ declare class EspnAdapter implements ProviderAdapter {
756
756
  /**
757
757
  * The ESPN competition slug to fetch live state from. Defaults to the 2026
758
758
  * World Cup (`fifa.world`); override with CLAUDINHO_COMPETITION (e.g.
759
- * `fifa.friendly` to follow international friendlies during pre-tournament
760
- * testing). Only affects the *live* fetch the bundled static schedule is
761
- * always the World Cup.
759
+ * `eng.1`, `uefa.champions`, `fifa.friendly`). Only affects the *live* fetch
760
+ * the bundled static schedule is always the World Cup.
761
+ *
762
+ * Lives in its own module so that both the live layer and the market sidecar
763
+ * read the ONE resolver without importing each other.
762
764
  */
763
765
  declare function resolveCompetition(explicit?: string): string;
766
+
764
767
  /** Provider names {@link makeAdapter} can construct (the CLI validates against this). */
765
768
  declare const KNOWN_SOURCES: readonly ["espn"];
769
+ interface AdapterOptions {
770
+ /**
771
+ * Enrich group-stage fixtures with their group letter (one extra standings
772
+ * request per poll). Default on; the statusline refresher turns it off because
773
+ * it never renders group letters.
774
+ */
775
+ enrichGroups?: boolean;
776
+ }
766
777
  /**
767
778
  * Construct a provider adapter for a `--source` name (default: espn). An
768
779
  * unknown source FAILS LOUD instead of silently running ESPN — `--source foo`
769
780
  * previously no-op'd, which lied about what the flag did (attribution stayed
770
781
  * honest, but the advertised knob did nothing).
771
782
  */
772
- declare function makeAdapter(source?: string): ProviderAdapter;
783
+ declare function makeAdapter(source?: string, opts?: AdapterOptions): ProviderAdapter;
773
784
  /**
774
785
  * Merge live matches over a base set by id. Live entries replace base entries
775
786
  * with the same id; unknown ids are appended.
@@ -1184,11 +1195,19 @@ declare function marketLine(signal: MarketSignal, match: Match): string;
1184
1195
  declare function marketBlock(signal: MarketSignal, match: Match): string[];
1185
1196
 
1186
1197
  /**
1187
- * Provider factory + graceful-degradation wrappers, mirroring `live.ts`'s
1188
- * getMatchesForDate contract: a market signal is optional enrichment, so any
1189
- * provider/network/parse error degrades to "no signal" and never throws.
1198
+ * Competitions the market sidecar covers CLAUDINHO'S implementation scope, not
1199
+ * Polymarket's. Polymarket does carry per-match moneylines for leagues (the
1200
+ * event `epl-lee-new-2026-09-14` has Leeds / draw / Newcastle legs), but this
1201
+ * sidecar derives its event slugs from the World Cup series only (`fifwc-…`,
1202
+ * series `soccer-fifwc` — see `deriveEventSlugs`) and validates fixture↔market
1203
+ * identity with nation tokens. Outside this set every fixture would derive a
1204
+ * slug that cannot exist, so the sidecar is switched off by construction
1205
+ * instead of issuing doomed requests. Supporting a league is slug-derivation,
1206
+ * mapping and validation work per competition — not widening this set.
1190
1207
  */
1191
-
1208
+ declare const MARKET_COMPETITIONS: ReadonlySet<string>;
1209
+ /** Whether the market sidecar can say anything about the active competition. */
1210
+ declare function marketsCoverCompetition(competition?: string): boolean;
1192
1211
  /**
1193
1212
  * Resolve the market-data source: explicit arg > CLAUDINHO_MARKETS_SOURCE env >
1194
1213
  * 'polymarket' (mirrors resolveCompetition). Set CLAUDINHO_MARKETS_SOURCE=fake
@@ -1548,4 +1567,4 @@ declare function formatShareBracket(input: ShareBracketInput, options?: ShareBra
1548
1567
  /** Compact one-line-per-match bracket for narrow share contexts. */
1549
1568
  declare function formatBracketCompactLine(mv: BracketMatchView, opts?: BracketFormatOpts): string;
1550
1569
 
1551
- export { BRACKET_STAGE_ORDER, type BatchResolution, type BoundedList, type BracketFormatOpts, type BracketMatchNode, type BracketMatchView, type BracketResult, type BracketTopology, type BracketView, type BuildSignalInput, DEFAULT_COMPETITION, DEFAULT_FLAVOR, DEFAULT_MAX_AGE_MS, EspnAdapter, type EspnAdapterOptions, FLAVOR_LEVELS, FakeMarketProvider, type FakeMarketProviderOptions, type FavoriteStrength, type FlavorLevel, type FormatOpts, type GroupStandings, KNOCKOUT_EXTRA_TIME_MS, KNOWN_SOURCES, type KnockoutFixturesResult, LIVE_WINDOW_MS, type Lang, type LedgerRow, type LiveResult, MAX_LABEL_COLUMNS, type MapContext, type MarketFavorite, type MarketMapping, type MarketMappingTable, type MarketOutcome, type MarketOutcomeKind, type MarketProvider, type MarketSignal, type MarketSignalOptions, type MarketSignalsResult, type Match, type MatchByIdResult, type MatchEvent, type NextFixtureResult, type Outcome, type ParseResult, PolymarketProvider, type PolymarketProviderOptions, type ProviderAdapter, type ProviderCapabilities, ProviderError, type ProviderErrorKind, type PunditPick, type ResolvedParticipant, SHARE_DISCLAIMER, SHARE_HASHTAG, type Selection, type ShareBracketInput, type ShareBracketOptions, type ShareSnippetInput, type ShareSnippetOptions, type ShareStyle, type ShareTableInput, type SlotRef, type Stage, type StandingRow, type StandingsResult, type Status, type Team, type TeamInfo, type TeamLookup, allFixtures, allTeams, ambiguous, asFlavorLevel, bounded, buildBracketTopology, buildBracketView, buildMarketSignal, byKickoff, cacheableKeys, competitionBase, computeStandings, countdown, currentOrNextFixtureForTeam, definitiveNone, deriveFavorite, displayWidth, emptyBatch, favoriteStrength, fixturesByDate, fixturesByGroup, fixturesByTeam, fixturesInLiveWindow, flagEmoji, formatBracketCompactLine, formatBracketList, formatBracketMatchLine, formatBracketTree, formatDate, formatKickoff, formatShareBracket, formatShareSnippet, formatShareTable, formatTime, getBracket, getKnockoutFixtures, getLiveMatches, getMarketSignal, getMarketSignals, getMatchById, getMatchesForDate, getNextFixtureForTeam, getStandings, groups, hasSaneDistribution, humanLabel, isCacheable, isFinished, isFlavorLevel, isLive, isReliableMarketSignal, isResolvedNation, isStaleSignal, isTournamentWindowOver, isValidDate, isValidTimeZone, knockoutWindow, liveSourceLabel, liveWindowMsFor, loadBracketTopology, localDate, lookupTeam, makeAdapter, makeMarketProvider, malformed, mapEspnEvent, mapsCleanly, marketAttributionText, marketBlock, marketFavoriteText, marketFixtureForTeam, marketLine, marketProbabilityText, marketRelevant, marketSignalRendersFor, marketSourceLabel, matchFlavor, matchKey, matchLocation, mergeLive, nationToFlag, nationToRegion, nextFixtureForTeam, normalizeLang, normalizeOutcomes, outcomeFromScore, padVisible, parseCachedMarketSignal, parseCachedMatch, parseCachedMatches, parseStandings, parseTeamSlot, parsedValue, productFlag, resolveCompetition, resolveMarketSource, resolveTz, resolvedValues, sanitizeBundledFixture, scoreline, sealMarketSignal, sealMatch, selectOne, stageLabel, stageLabelI18n, t, truncateVisible, unresolved, valid };
1570
+ export { BRACKET_STAGE_ORDER, type BatchResolution, type BoundedList, type BracketFormatOpts, type BracketMatchNode, type BracketMatchView, type BracketResult, type BracketTopology, type BracketView, type BuildSignalInput, DEFAULT_COMPETITION, DEFAULT_FLAVOR, DEFAULT_MAX_AGE_MS, EspnAdapter, type EspnAdapterOptions, FLAVOR_LEVELS, FakeMarketProvider, type FakeMarketProviderOptions, type FavoriteStrength, type FlavorLevel, type FormatOpts, type GroupStandings, KNOCKOUT_EXTRA_TIME_MS, KNOWN_SOURCES, type KnockoutFixturesResult, LIVE_WINDOW_MS, type Lang, type LedgerRow, type LiveResult, MARKET_COMPETITIONS, MAX_LABEL_COLUMNS, type MapContext, type MarketFavorite, type MarketMapping, type MarketMappingTable, type MarketOutcome, type MarketOutcomeKind, type MarketProvider, type MarketSignal, type MarketSignalOptions, type MarketSignalsResult, type Match, type MatchByIdResult, type MatchEvent, type NextFixtureResult, type Outcome, type ParseResult, PolymarketProvider, type PolymarketProviderOptions, type ProviderAdapter, type ProviderCapabilities, ProviderError, type ProviderErrorKind, type PunditPick, type ResolvedParticipant, SHARE_DISCLAIMER, SHARE_HASHTAG, type Selection, type ShareBracketInput, type ShareBracketOptions, type ShareSnippetInput, type ShareSnippetOptions, type ShareStyle, type ShareTableInput, type SlotRef, type Stage, type StandingRow, type StandingsResult, type Status, type Team, type TeamInfo, type TeamLookup, allFixtures, allTeams, ambiguous, asFlavorLevel, bounded, buildBracketTopology, buildBracketView, buildMarketSignal, byKickoff, cacheableKeys, competitionBase, computeStandings, countdown, currentOrNextFixtureForTeam, definitiveNone, deriveFavorite, displayWidth, emptyBatch, favoriteStrength, fixturesByDate, fixturesByGroup, fixturesByTeam, fixturesInLiveWindow, flagEmoji, formatBracketCompactLine, formatBracketList, formatBracketMatchLine, formatBracketTree, formatDate, formatKickoff, formatShareBracket, formatShareSnippet, formatShareTable, formatTime, getBracket, getKnockoutFixtures, getLiveMatches, getMarketSignal, getMarketSignals, getMatchById, getMatchesForDate, getNextFixtureForTeam, getStandings, groups, hasSaneDistribution, humanLabel, isCacheable, isFinished, isFlavorLevel, isLive, isReliableMarketSignal, isResolvedNation, isStaleSignal, isTournamentWindowOver, isValidDate, isValidTimeZone, knockoutWindow, liveSourceLabel, liveWindowMsFor, loadBracketTopology, localDate, lookupTeam, makeAdapter, makeMarketProvider, malformed, mapEspnEvent, mapsCleanly, marketAttributionText, marketBlock, marketFavoriteText, marketFixtureForTeam, marketLine, marketProbabilityText, marketRelevant, marketSignalRendersFor, marketSourceLabel, marketsCoverCompetition, matchFlavor, matchKey, matchLocation, mergeLive, nationToFlag, nationToRegion, nextFixtureForTeam, normalizeLang, normalizeOutcomes, outcomeFromScore, padVisible, parseCachedMarketSignal, parseCachedMatch, parseCachedMatches, parseStandings, parseTeamSlot, parsedValue, productFlag, resolveCompetition, resolveMarketSource, resolveTz, resolvedValues, sanitizeBundledFixture, scoreline, sealMarketSignal, sealMatch, selectOne, stageLabel, stageLabelI18n, t, truncateVisible, unresolved, valid };
package/dist/index.js CHANGED
@@ -3620,7 +3620,7 @@ function parseEspnStandings(raw) {
3620
3620
  complete = false;
3621
3621
  }
3622
3622
  const entries = takeBounded(rawEntries, MAX_GROUP_ROWS);
3623
- const seenTeams = /* @__PURE__ */ new Set();
3623
+ const seenCodes = /* @__PURE__ */ new Map();
3624
3624
  const seenRanks = /* @__PURE__ */ new Set();
3625
3625
  const ranked = [];
3626
3626
  for (const e of entries) {
@@ -3629,14 +3629,17 @@ function parseEspnStandings(raw) {
3629
3629
  if (r.kind !== "definitive-none") complete = false;
3630
3630
  continue;
3631
3631
  }
3632
- const key = r.value.providerId ?? r.value.team.code;
3633
- if (seenTeams.has(key) || seenRanks.has(r.value.providerRank) || r.value.providerId !== void 0 && seenProviderIds.has(r.value.providerId)) {
3632
+ const { providerId, providerRank } = r.value;
3633
+ const code = r.value.team.code;
3634
+ const priorHadId = seenCodes.get(code);
3635
+ const codeCollision = priorHadId !== void 0 && (providerId === void 0 || priorHadId === false);
3636
+ if (codeCollision || seenRanks.has(providerRank) || providerId !== void 0 && seenProviderIds.has(providerId)) {
3634
3637
  complete = false;
3635
3638
  continue;
3636
3639
  }
3637
- seenTeams.add(key);
3638
- seenRanks.add(r.value.providerRank);
3639
- if (r.value.providerId !== void 0) seenProviderIds.add(r.value.providerId);
3640
+ seenCodes.set(code, providerId !== void 0);
3641
+ seenRanks.add(providerRank);
3642
+ if (providerId !== void 0) seenProviderIds.add(providerId);
3640
3643
  const { providerId: _dropId, providerRank: rank, ...row } = r.value;
3641
3644
  ranked.push({ row, rank });
3642
3645
  }
@@ -3666,7 +3669,7 @@ function parseEspnStandings(raw) {
3666
3669
  var ESPN_SOCCER = "https://site.api.espn.com/apis/site/v2/sports/soccer";
3667
3670
  var DEFAULT_COMPETITION = "fifa.world";
3668
3671
  var DEFAULT_BASE = `${ESPN_SOCCER}/${DEFAULT_COMPETITION}`;
3669
- var USER_AGENT = `claudinho/${"0.9.4"} (+https://github.com/arturogarrido/claudinho)`;
3672
+ var USER_AGENT = `claudinho/${"0.10.0"} (+https://github.com/arturogarrido/claudinho)`;
3670
3673
  var MAX_RESPONSE_BYTES = 5 * 1024 * 1024;
3671
3674
  function competitionBase(slug) {
3672
3675
  return `${ESPN_SOCCER}/${slug}`;
@@ -4751,7 +4754,7 @@ function loadBracketTopology() {
4751
4754
  return TOPOLOGY;
4752
4755
  }
4753
4756
 
4754
- // src/live.ts
4757
+ // src/competition.ts
4755
4758
  function resolveCompetition(explicit) {
4756
4759
  if (explicit) return explicit;
4757
4760
  if (typeof process !== "undefined" && process.env?.CLAUDINHO_COMPETITION) {
@@ -4759,13 +4762,15 @@ function resolveCompetition(explicit) {
4759
4762
  }
4760
4763
  return DEFAULT_COMPETITION;
4761
4764
  }
4765
+
4766
+ // src/live.ts
4762
4767
  var KNOWN_SOURCES = ["espn"];
4763
- function makeAdapter(source = "espn") {
4768
+ function makeAdapter(source = "espn", opts = {}) {
4764
4769
  switch (source) {
4765
4770
  case "espn": {
4766
4771
  const competition = resolveCompetition();
4767
4772
  const baseUrl = competition === DEFAULT_COMPETITION ? void 0 : competitionBase(competition);
4768
- return new EspnAdapter({ baseUrl });
4773
+ return new EspnAdapter({ baseUrl, enrichGroups: opts.enrichGroups });
4769
4774
  }
4770
4775
  default:
4771
4776
  throw new Error(
@@ -5644,6 +5649,10 @@ function numberish(v) {
5644
5649
  }
5645
5650
 
5646
5651
  // src/markets/provider.ts
5652
+ var MARKET_COMPETITIONS = /* @__PURE__ */ new Set([DEFAULT_COMPETITION]);
5653
+ function marketsCoverCompetition(competition = resolveCompetition()) {
5654
+ return MARKET_COMPETITIONS.has(competition);
5655
+ }
5647
5656
  function resolveMarketSource(explicit) {
5648
5657
  if (explicit) return explicit;
5649
5658
  if (typeof process !== "undefined" && process.env?.CLAUDINHO_MARKETS_SOURCE) {
@@ -5660,6 +5669,7 @@ function makeMarketProvider(source) {
5660
5669
  return new FakeMarketProvider();
5661
5670
  // no synth → yields no signals, no network
5662
5671
  default:
5672
+ if (!marketsCoverCompetition()) return new FakeMarketProvider();
5663
5673
  return new PolymarketProvider();
5664
5674
  }
5665
5675
  }
@@ -5948,6 +5958,7 @@ export {
5948
5958
  KNOCKOUT_EXTRA_TIME_MS,
5949
5959
  KNOWN_SOURCES,
5950
5960
  LIVE_WINDOW_MS,
5961
+ MARKET_COMPETITIONS,
5951
5962
  MAX_LABEL_COLUMNS,
5952
5963
  PolymarketProvider,
5953
5964
  ProviderError,
@@ -6029,6 +6040,7 @@ export {
6029
6040
  marketRelevant,
6030
6041
  marketSignalRendersFor,
6031
6042
  marketSourceLabel,
6043
+ marketsCoverCompetition,
6032
6044
  matchFlavor,
6033
6045
  matchKey,
6034
6046
  matchLocation,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@claudinho/core",
3
- "version": "0.9.4",
3
+ "version": "0.10.0",
4
4
  "description": "Domain model, provider adapters (ESPN), standings, bundled schedule, and the read-only Polymarket market-signal sidecar powering Claudinho. Not affiliated with FIFA or Anthropic.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -53,12 +53,12 @@
53
53
  "agent"
54
54
  ],
55
55
  "devDependencies": {
56
- "@types/node": "^22.20.1",
57
- "@vitest/coverage-v8": "^4.1.10",
56
+ "@types/node": "^22.20.2",
57
+ "@vitest/coverage-v8": "^4.1.11",
58
58
  "tsup": "^8.0.0",
59
- "tsx": "^4.23.1",
59
+ "tsx": "^4.23.13",
60
60
  "typescript": "^5.7.0",
61
- "vitest": "^4.1.10"
61
+ "vitest": "^4.1.11"
62
62
  },
63
63
  "scripts": {
64
64
  "build": "tsup",