@claudinho/core 0.8.16 → 0.8.18

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
@@ -355,6 +355,30 @@ declare function currentOrNextFixtureForTeam(code: string, opts?: {
355
355
  /** Sorted list of distinct group letters present in the schedule. */
356
356
  declare function groups(fixtures?: Match[]): string[];
357
357
 
358
+ /** A roster team plus its group letter (A–L). */
359
+ interface TeamInfo extends Team {
360
+ group?: string;
361
+ }
362
+ /**
363
+ * The 48-team roster from the bundled group-stage fixtures, sorted by name.
364
+ * Placeholder/knockout slots (non-3-letter code or 🏳️) are skipped.
365
+ */
366
+ declare function allTeams(fixtures?: Match[]): TeamInfo[];
367
+ interface TeamLookup {
368
+ query: string;
369
+ /** The single confident match, or null when the query is ambiguous or unknown. */
370
+ team: TeamInfo | null;
371
+ /** All candidate teams, best-first (1+ whenever anything matched). */
372
+ matches: TeamInfo[];
373
+ }
374
+ /**
375
+ * Resolve a free-text team name or code to a roster team. Precedence: exact code,
376
+ * exact name, alias, then prefix/substring fuzzy on the name (fuzzy only for
377
+ * queries of 3+ letters, so a 1–2 char query can't substring-match noise). A
378
+ * unique fuzzy hit resolves; multiple hits return as candidates with `team: null`.
379
+ */
380
+ declare function lookupTeam(query: string, fixtures?: Match[]): TeamLookup;
381
+
358
382
  /** One row of a group table. */
359
383
  interface StandingRow {
360
384
  team: Team;
@@ -1152,4 +1176,4 @@ declare function formatShareBracket(input: ShareBracketInput, options?: ShareBra
1152
1176
  /** Compact one-line-per-match bracket for narrow share contexts. */
1153
1177
  declare function formatBracketCompactLine(mv: BracketMatchView, opts?: BracketFormatOpts): string;
1154
1178
 
1155
- export { BRACKET_STAGE_ORDER, 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, type KnockoutFixturesResult, LIVE_WINDOW_MS, type Lang, type LedgerRow, type LiveResult, 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, PolymarketProvider, type PolymarketProviderOptions, type ProviderAdapter, type ProviderCapabilities, type PunditPick, type ResolvedParticipant, SHARE_DISCLAIMER, SHARE_HASHTAG, type ShareBracketInput, type ShareBracketOptions, type ShareSnippetInput, type ShareSnippetOptions, type ShareStyle, type ShareTableInput, type SlotRef, type Stage, type StandingRow, type StandingsResult, type Status, type Team, allFixtures, asFlavorLevel, buildBracketTopology, buildBracketView, buildMarketSignal, byKickoff, competitionBase, computeStandings, countdown, currentOrNextFixtureForTeam, deriveFavorite, 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, isFinished, isFlavorLevel, isLive, isReliableMarketSignal, isResolvedNation, isStaleSignal, isValidDate, isValidTimeZone, liveSourceLabel, loadBracketTopology, localDate, makeAdapter, makeMarketProvider, mapEspnEvent, mapsCleanly, marketAttributionText, marketBlock, marketFavoriteText, marketFixtureForTeam, marketLine, marketProbabilityText, marketRelevant, marketSignalRendersFor, marketSourceLabel, matchFlavor, matchKey, matchLocation, mergeLive, nationToFlag, nationToRegion, nextFixtureForTeam, normalizeLang, normalizeOutcomes, outcomeFromScore, parseStandings, parseTeamSlot, resolveCompetition, resolveMarketSource, resolveTz, sanitizeBundledFixture, scoreline, stageLabel, stageLabelI18n, t };
1179
+ export { BRACKET_STAGE_ORDER, 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, type KnockoutFixturesResult, LIVE_WINDOW_MS, type Lang, type LedgerRow, type LiveResult, 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, PolymarketProvider, type PolymarketProviderOptions, type ProviderAdapter, type ProviderCapabilities, type PunditPick, type ResolvedParticipant, SHARE_DISCLAIMER, SHARE_HASHTAG, 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, asFlavorLevel, buildBracketTopology, buildBracketView, buildMarketSignal, byKickoff, competitionBase, computeStandings, countdown, currentOrNextFixtureForTeam, deriveFavorite, 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, isFinished, isFlavorLevel, isLive, isReliableMarketSignal, isResolvedNation, isStaleSignal, isValidDate, isValidTimeZone, liveSourceLabel, loadBracketTopology, localDate, lookupTeam, makeAdapter, makeMarketProvider, mapEspnEvent, mapsCleanly, marketAttributionText, marketBlock, marketFavoriteText, marketFixtureForTeam, marketLine, marketProbabilityText, marketRelevant, marketSignalRendersFor, marketSourceLabel, matchFlavor, matchKey, matchLocation, mergeLive, nationToFlag, nationToRegion, nextFixtureForTeam, normalizeLang, normalizeOutcomes, outcomeFromScore, parseStandings, parseTeamSlot, resolveCompetition, resolveMarketSource, resolveTz, sanitizeBundledFixture, scoreline, stageLabel, stageLabelI18n, t };
package/dist/index.js CHANGED
@@ -2900,6 +2900,62 @@ function groups(fixtures = SCHEDULE) {
2900
2900
  return [...set].sort();
2901
2901
  }
2902
2902
 
2903
+ // src/teams.ts
2904
+ function norm2(s) {
2905
+ return s.toLowerCase().normalize("NFD").replace(/[̀-ͯ]/g, "").replace(/[^a-z]/g, "");
2906
+ }
2907
+ function allTeams(fixtures = allFixtures()) {
2908
+ const seen = /* @__PURE__ */ new Map();
2909
+ for (const m of fixtures) {
2910
+ if (m.stage !== "GROUP") continue;
2911
+ for (const t2 of [m.home, m.away]) {
2912
+ if (!/^[A-Z]{3}$/.test(t2.code) || t2.flag === "\u{1F3F3}\uFE0F") continue;
2913
+ if (!seen.has(t2.code)) seen.set(t2.code, { ...t2, group: m.group ?? void 0 });
2914
+ }
2915
+ }
2916
+ return [...seen.values()].sort((a, b) => a.name.localeCompare(b.name));
2917
+ }
2918
+ var TEAM_ALIASES = {
2919
+ turkey: "TUR",
2920
+ holland: "NED",
2921
+ korea: "KOR",
2922
+ skorea: "KOR",
2923
+ korearepublic: "KOR",
2924
+ republicofkorea: "KOR",
2925
+ czechrepublic: "CZE",
2926
+ congo: "COD",
2927
+ drcongo: "COD",
2928
+ drc: "COD",
2929
+ democraticrepublicofcongo: "COD",
2930
+ democraticrepublicofthecongo: "COD",
2931
+ cotedivoire: "CIV",
2932
+ caboverde: "CPV",
2933
+ bosnia: "BIH",
2934
+ bosniaandherzegovina: "BIH",
2935
+ us: "USA",
2936
+ america: "USA",
2937
+ unitedstatesofamerica: "USA"
2938
+ };
2939
+ function lookupTeam(query, fixtures = allFixtures()) {
2940
+ const roster = allTeams(fixtures);
2941
+ const q = norm2(query);
2942
+ if (!q) return { query, team: null, matches: [] };
2943
+ const byCode = roster.find((t2) => norm2(t2.code) === q);
2944
+ if (byCode) return { query, team: byCode, matches: [byCode] };
2945
+ const byName = roster.find((t2) => norm2(t2.name) === q);
2946
+ if (byName) return { query, team: byName, matches: [byName] };
2947
+ const aliasCode = TEAM_ALIASES[q];
2948
+ if (aliasCode) {
2949
+ const t2 = roster.find((r) => r.code === aliasCode);
2950
+ if (t2) return { query, team: t2, matches: [t2] };
2951
+ }
2952
+ if (q.length < 3) return { query, team: null, matches: [] };
2953
+ const prefix = roster.filter((t2) => norm2(t2.name).startsWith(q));
2954
+ const substr = roster.filter((t2) => norm2(t2.name).includes(q) && !prefix.includes(t2));
2955
+ const matches = [...prefix, ...substr];
2956
+ return { query, team: matches.length === 1 ? matches[0] : null, matches };
2957
+ }
2958
+
2903
2959
  // src/standings.ts
2904
2960
  function blankRow(team) {
2905
2961
  return {
@@ -4465,7 +4521,8 @@ var PolymarketProvider = class {
4465
4521
  opts;
4466
4522
  name = "polymarket";
4467
4523
  async findSignal(match, options) {
4468
- return (await this.resolveOne(match, options)).signal;
4524
+ const deadline = options?.deadlineMs != null ? Date.now() + options.deadlineMs : Number.POSITIVE_INFINITY;
4525
+ return (await this.resolveOne(match, options, deadline)).signal;
4469
4526
  }
4470
4527
  async findSignals(matches, options) {
4471
4528
  const signals = /* @__PURE__ */ new Map();
@@ -4473,7 +4530,7 @@ var PolymarketProvider = class {
4473
4530
  const deadline = options?.deadlineMs != null ? Date.now() + options.deadlineMs : Number.POSITIVE_INFINITY;
4474
4531
  for (const m of matches) {
4475
4532
  if (Date.now() >= deadline) break;
4476
- const r = await this.resolveOne(m, options);
4533
+ const r = await this.resolveOne(m, options, deadline);
4477
4534
  if (r.checked) checked.add(m.id);
4478
4535
  if (r.signal) signals.set(m.id, r.signal);
4479
4536
  }
@@ -4485,13 +4542,16 @@ var PolymarketProvider = class {
4485
4542
  * provider/network error — so transient failures are retried, not
4486
4543
  * negative-cached.
4487
4544
  */
4488
- async resolveOne(match, options) {
4545
+ async resolveOne(match, options, deadline = Number.POSITIVE_INFINITY) {
4489
4546
  const entry = (this.opts.mapping ?? BUNDLED_MAPPING)[match.id];
4490
4547
  const slugs = entry?.eventSlug ? [entry.eventSlug] : deriveEventSlugs(match);
4491
4548
  if (slugs.length === 0) return { checked: true };
4549
+ const configured = options?.timeoutMs ?? this.opts.timeoutMs ?? DEFAULT_TIMEOUT_MS;
4492
4550
  try {
4493
4551
  for (const slug of slugs) {
4494
- const event = await this.fetchEvent(slug, options?.timeoutMs);
4552
+ const remaining = deadline - Date.now();
4553
+ if (remaining <= 0) return { checked: false };
4554
+ const event = await this.fetchEvent(slug, Math.min(configured, remaining));
4495
4555
  const signal = event ? this.toSignal(match, slug, event, options) : void 0;
4496
4556
  if (signal) return { signal, checked: true };
4497
4557
  }
@@ -4571,6 +4631,27 @@ var PolymarketProvider = class {
4571
4631
  return signal.ambiguous ? void 0 : signal;
4572
4632
  }
4573
4633
  };
4634
+ var POLYMARKET_TOKEN = {
4635
+ SUI: "che",
4636
+ // Switzerland
4637
+ NED: "nld",
4638
+ // Netherlands
4639
+ URU: "ury",
4640
+ // Uruguay
4641
+ POR: "prt",
4642
+ // Portugal
4643
+ CRO: "hrv",
4644
+ // Croatia
4645
+ COD: "cdr",
4646
+ // DR Congo
4647
+ CPV: "cvi"
4648
+ // Cabo Verde
4649
+ };
4650
+ function pmTokens(code) {
4651
+ const c = code.toLowerCase();
4652
+ const alias = POLYMARKET_TOKEN[code.toUpperCase()];
4653
+ return alias && alias !== c ? [alias, c] : [c];
4654
+ }
4574
4655
  function deriveEventSlugs(match) {
4575
4656
  const home = match.home.code.toLowerCase();
4576
4657
  const away = match.away.code.toLowerCase();
@@ -4578,10 +4659,18 @@ function deriveEventSlugs(match) {
4578
4659
  if (!/^[a-z]{3}$/.test(home) || !/^[a-z]{3}$/.test(away)) return [];
4579
4660
  const utcDate = match.kickoff.slice(0, 10);
4580
4661
  if (!/^\d{4}-\d{2}-\d{2}$/.test(utcDate)) return [];
4581
- return [
4582
- `fifwc-${home}-${away}-${utcDate}`,
4583
- `fifwc-${home}-${away}-${shiftUtcDate(utcDate, -1)}`
4584
- ];
4662
+ const dates = [utcDate, shiftUtcDate(utcDate, -1)];
4663
+ const homeToks = pmTokens(match.home.code);
4664
+ const awayToks = pmTokens(match.away.code);
4665
+ const slugs = [];
4666
+ for (const d of dates) {
4667
+ for (const h of homeToks) {
4668
+ for (const a of awayToks) {
4669
+ slugs.push(`fifwc-${h}-${a}-${d}`);
4670
+ }
4671
+ }
4672
+ }
4673
+ return [...new Set(slugs)];
4585
4674
  }
4586
4675
  function slugToken(m) {
4587
4676
  return (m.slug ?? "").toLowerCase().split("-").pop() ?? "";
@@ -4590,10 +4679,10 @@ function isDrawMarket(m) {
4590
4679
  return slugToken(m) === "draw" || (m.groupItemTitle ?? "").trim().toLowerCase().startsWith("draw");
4591
4680
  }
4592
4681
  function pickMarket(markets, teamCode, teamName) {
4593
- const code = teamCode.toLowerCase();
4682
+ const tokens = pmTokens(teamCode);
4594
4683
  const name = teamName.trim().toLowerCase();
4595
4684
  const teamMarkets = markets.filter((m) => !isDrawMarket(m));
4596
- const bySlug = teamMarkets.find((m) => slugToken(m) === code);
4685
+ const bySlug = teamMarkets.find((m) => tokens.includes(slugToken(m)));
4597
4686
  if (bySlug) return bySlug;
4598
4687
  return teamMarkets.find((m) => (m.groupItemTitle ?? "").trim().toLowerCase() === name);
4599
4688
  }
@@ -4943,6 +5032,7 @@ export {
4943
5032
  SHARE_DISCLAIMER,
4944
5033
  SHARE_HASHTAG,
4945
5034
  allFixtures,
5035
+ allTeams,
4946
5036
  asFlavorLevel,
4947
5037
  buildBracketTopology,
4948
5038
  buildBracketView,
@@ -4991,6 +5081,7 @@ export {
4991
5081
  liveSourceLabel,
4992
5082
  loadBracketTopology,
4993
5083
  localDate,
5084
+ lookupTeam,
4994
5085
  makeAdapter,
4995
5086
  makeMarketProvider,
4996
5087
  mapEspnEvent,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@claudinho/core",
3
- "version": "0.8.16",
3
+ "version": "0.8.18",
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",