@claudinho/core 0.11.0 → 0.11.2

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/README.md CHANGED
@@ -1,10 +1,10 @@
1
1
  # @claudinho/core ⚽
2
2
 
3
3
  **The engine behind Claudinho: the domain model, the provider adapters (ESPN), live scores
4
- and standings for the competition you follow (15 supported), the bundled 2026 World Cup
4
+ and standings for the competition you follow (16 supported), the bundled 2026 World Cup
5
5
  schedule with its knockout bracket and read-only Polymarket sidecar.** Claudinho puts live
6
6
  football in your dev environment for the competition you follow: the World Cup, the Premier
7
- League, LALIGA, the Champions League and 11 more. This package powers
7
+ League, LALIGA, the Champions League and 12 more. This package powers
8
8
  [`@claudinho/cli`](https://www.npmjs.com/package/@claudinho/cli) and
9
9
  [`@claudinho/mcp`](https://www.npmjs.com/package/@claudinho/mcp).
10
10
 
@@ -23,7 +23,7 @@ npm i @claudinho/core
23
23
  - **`ProviderAdapter`**: the swappable data-vendor interface (`fetchByDate`, `fetchLive`, optional `fetchWindow`/`fetchStandings`); `EspnAdapter` included. An adapter serves exactly one competition and states it (`competition`, required); a provider that knows which season a response belongs to attaches it to the returned array (`attachFetchMeta`, read with `fetchMeta`), with the seasons its parts stated (`seasons`) and the records it left out (`omitted`). A window whose parts state two seasons is refused unless it is asked `acrossSeasons`, which three callers that merge nothing do (the live read, and off the bundled competition the dated read and the match refresh); `getNextFixtureForTeam`, `getBracket`, `getMatchesForDate`, `getLiveMatches`, `getMatchById` and `marketFixtureForTeam` say `partial` when the read behind them was not whole (`dayAttribution` and the "none read" sentences in the card builders say what that means for a day), and `verdictExtras` / `verdictNotice` / `verdictQualifiers` turn a result's verdict into a key and a sentence
24
24
  - **Static schedule**: the World Cup's 104 fixtures (groups, venues, host cities, kickoffs) bundled (the other competitions' schedules are read from the feed); query with `allFixtures`, `fixturesByDate` (groups by your timezone), `fixturesByTeam`, `fixturesByGroup`, `nextFixtureForTeam`, `groups`
25
25
  - **Live overlay**: `makeAdapter(source, { competition })`, `getMatchesForDate`, `getLiveMatches`, `mergeLive` (static base + live state, with graceful degradation). The competition is resolved once by the caller (`resolveCompetition(explicit, env, saved)`: an alias or a slug, the first present source deciding (the flag, the environment, the saved choice the caller read with `readUserConfig`); with none it answers `none`, nothing chosen, never a default; anything else is refused with the aliases; core reads no environment and no file, the caller hands them in), and said by `modeLine` and `selectionExtras`; the bundled schedule is merged only for its own competition and edition (`bundleApplies`)
26
- - **The supported set**: `SUPPORTED`, ONE table of the fifteen competitions (slug, alias, name, teams, kind, season name, standings shape, bracket and markets capabilities, cadence); `deriveTables(table)` builds every written view from it, and `entryOf`, `capabilitiesOf`, `competitionLabel`, `listCompetitions` take a table, so a new row reaches every consumer
26
+ - **The supported set**: `SUPPORTED`, ONE table of the sixteen competitions (slug, alias, name, teams, kind, season name, standings shape, bracket and markets capabilities, cadence); `deriveTables(table)` builds every written view from it, and `entryOf`, `capabilitiesOf`, `competitionLabel`, `listCompetitions` take a table, so a new row reaches every consumer
27
27
  - **Standings**: `getStandings` fetches authoritative, cumulative tables from the provider (`GroupStandings`: a table has a key, `A`, `A1`, `A-B` or `LEAGUE`, and every table but a lettered group a `label`), failing closed when none is available, and saying `incomplete` when the provider sent a table that could not be read: the compatible bundled World Cup scope may return a degraded roster-at-zero, while custom competitions return empty + degraded rather than borrowing those teams; `computeStandings` derives a table from a set of matches (points / GD / GF tiebreak)
28
28
  - **Helpers**: emoji flags (`nationToFlag`), TZ-aware time (`formatKickoff`, `formatDate`, `formatTime`, `countdown`, `localDate`), location strings (`matchLocation`), localized commentary flair (`matchFlavor`, `flavorsFor` for a list with no phrase twice, `matchFlairs` for a list's flair slots, the cries included, and `matchFlair` for one line / `FlavorLevel`; the team rally cries, `rallyCryFor` / `RALLY_CRIES`, the pin deciding between two through `isPinnedSide`), validators (`isValidDate`, `isValidTimeZone`)
29
29
  - **Prediction-market signals (sidecar)**: read-only market signals kept *separate* from `Match`: the `MarketSignal` / `MarketProvider` model, the `PolymarketProvider` (public Gamma data only: no auth/trading/links; event slugs auto-derived per fixture, validation fails closed), a `FakeMarketProvider`, `makeMarketProvider`, `getMarketSignal` / `getMarketSignals`, the `isReliableMarketSignal` gate, and approved-copy formatters (`marketFavoriteText`, `marketProbabilityText`, `marketBlock`). Informational only, never betting advice.
package/dist/index.d.ts CHANGED
@@ -313,16 +313,21 @@ declare function formatDate(iso: string, opts?: FormatOpts): string;
313
313
  declare function formatTime(iso: string, opts?: FormatOpts): string;
314
314
  /** What {@link countdown} says of a kickoff that has passed (the statusline prints it as it is: English, by its rule). */
315
315
  declare const COUNTDOWN_NOW = "now";
316
- /** Compact human countdown until kickoff: "3d4h", "2h10m", "45m", or {@link COUNTDOWN_NOW}. */
317
- declare function countdown(iso: string, from?: Date): string;
316
+ /**
317
+ * Compact human countdown until kickoff: "3d4h", "2h10m", "45m", or {@link COUNTDOWN_NOW}, from `from`: the clock of
318
+ * the request or render that says it, which every caller hands in (no default: the wall clock is never read here).
319
+ */
320
+ declare function countdown(iso: string, from: Date): string;
318
321
  /**
319
322
  * The countdown as the reader's phrase, ONE rule for every localized line that
320
323
  * says it (CLI `next`, the MCP match line): "in 3d4h" (`next.in`), or, for a
321
324
  * kickoff that has passed (a stale record, the bundle's skeleton during an
322
325
  * outage), the language's own word for now (`countdown.now`), never the
323
- * English word inside a translated "in".
326
+ * English word inside a translated "in". Relative to the REQUEST's clock
327
+ * (`from`), the one every other read of the request takes and every caller
328
+ * passes: the wall clock is never read here.
324
329
  */
325
- declare function countdownPhrase(lang: string | undefined, iso: string, from?: Date): string;
330
+ declare function countdownPhrase(lang: string | undefined, iso: string, from: Date): string;
326
331
  /**
327
332
  * The calendar date (YYYY-MM-DD) of a kickoff in the target timezone, or ''
328
333
  * when the input can't be parsed — an unfileable fixture matches no date rather
@@ -417,7 +422,7 @@ declare function stageLabel(m: Pick<Match, 'stage' | 'group' | 'stageLabel'>): s
417
422
  declare function joinSegments(segments: readonly (string | undefined)[]): string;
418
423
 
419
424
  /**
420
- * The supported set, as ONE table: the fifteen competitions the product
425
+ * The supported set, as ONE table: the sixteen competitions the product
421
426
  * answers for, one row each, and every written fact about a competition that
422
427
  * the rest of the code reads (its teams' kind, its own kind, a league's season
423
428
  * name, its standings shape, what `bracket` and the market sidecar offer, how
@@ -483,14 +488,15 @@ interface CompetitionEntry {
483
488
  readonly cadenceYears: 1 | 2 | 4;
484
489
  }
485
490
  /**
486
- * The fifteen, in the order they are listed.
491
+ * The sixteen, in the order they are listed.
487
492
  * - Nations: the World Cup, the Euro, the Copa America (four-yearly), the
488
493
  * UEFA Nations League, the Concacaf Nations League (its next editions are
489
494
  * 2026/27 and 2028/29, by Concacaf's published 2026 to 2030 calendar) and
490
495
  * the Gold Cup (two-yearly).
491
- * - The five leagues, yearly. A league's season name as measured on the real
492
- * feed (Oct 3, 2026); `mex.1` has none (its slugs are `torneo-apertura`,
493
- * `torneo-clausura` and its play-off rounds).
496
+ * - The six leagues, yearly. A league's season name as measured on the real
497
+ * feed (Oct 3, 2026); `mex.1` and `uru.1` have none (their slugs are
498
+ * `torneo-apertura`, `torneo-clausura` and, for `mex.1`, its play-off
499
+ * rounds).
494
500
  * - The four club cups. The Champions League's league phase reads as one
495
501
  * table; the Concacaf Champions Cup has none (knockout from the first
496
502
  * round); the Club World Cup is four-yearly.
@@ -521,8 +527,8 @@ interface DerivedTables {
521
527
  }
522
528
  /**
523
529
  * The views every consumer reads, derived from a table. A function of the
524
- * table, so a test can run it on one with a sixteenth row and see every view
525
- * carry it. Every view is frozen.
530
+ * table, so a test can run it on one with a row past the table's end and
531
+ * see every view carry it. Every view is frozen.
526
532
  *
527
533
  * A row that offers a bracket for any competition but the bundled one is
528
534
  * REFUSED here, at load, naming it: an offered bracket needs a knockout
@@ -1383,6 +1389,15 @@ interface LiveResult {
1383
1389
  */
1384
1390
  served?: readonly string[];
1385
1391
  }
1392
+ /**
1393
+ * THE clock of a request: the one its caller gave (a test's, a command's
1394
+ * context), else the adapter's read clock ({@link clockOf}: the clock the
1395
+ * adapter's reads and its cooldown count by, the wall clock in production).
1396
+ * A surface reads it ONCE per request, after resolving its adapter, and hands
1397
+ * it to every read and every rendering of the request, so a lookup and a
1398
+ * countdown in one answer agree on "now".
1399
+ */
1400
+ declare function requestClock(adapter: ProviderAdapter, given?: Date): Date;
1386
1401
  /** Human label for a live-data provider name (attribution). Text only. */
1387
1402
  declare function liveSourceLabel(source: string): string;
1388
1403
  /**
@@ -2739,7 +2754,7 @@ type CompetitionKind = 'league' | 'cup' | 'friendly';
2739
2754
  /**
2740
2755
  * The teams each competition fields: the supported table's (`nation` for the
2741
2756
  * World Cup, the Euro, the Copa America, both Nations Leagues and the Gold
2742
- * Cup; `club` for the five leagues and the four club cups), and the friendly
2757
+ * Cup; `club` for the six leagues and the four club cups), and the friendly
2743
2758
  * competition's (`nation`). An unlisted competition fields clubs: a nations
2744
2759
  * competition the set does not list renders its teams by name until it is
2745
2760
  * written down.
@@ -2748,7 +2763,7 @@ declare const TEAM_KIND: Readonly<Record<string, TeamKind>>;
2748
2763
  /** The teams a competition fields; an unlisted (or absent) competition fields clubs. */
2749
2764
  declare function teamKind(competition: string | undefined): TeamKind;
2750
2765
  /**
2751
- * Each competition's kind: the supported table's (`league`: the five leagues,
2766
+ * Each competition's kind: the supported table's (`league`: the six leagues,
2752
2767
  * whose regular season the stage grammar reads as `REGULAR`; `cup`: the other
2753
2768
  * ten), and `friendly` for `fifa.friendly`, the one competition whose
2754
2769
  * `friendly` slug is the `FRIENDLY` stage.
@@ -3053,6 +3068,314 @@ declare const FAN_PROJECT = "Independent fan project";
3053
3068
  */
3054
3069
  declare function disclaimerLine(host?: string): string;
3055
3070
 
3071
+ /**
3072
+ * Where a selection came from: the command line's `--competition` or a tool's
3073
+ * `competition` argument (`flag`), `CLAUDINHO_COMPETITION` (`env`), or the
3074
+ * user's saved choice (`saved`: the config file `claudinho follow` writes).
3075
+ * Nothing else chooses: with none of the three there is no competition
3076
+ * (`{ kind: 'none' }`), never a default.
3077
+ */
3078
+ type ChosenBy = 'flag' | 'env' | 'saved';
3079
+ /** A competition a request is for, and where that choice came from. */
3080
+ interface SelectedCompetition {
3081
+ readonly kind: 'selected';
3082
+ /** The provider's slug the request asks for. */
3083
+ readonly slug: string;
3084
+ /** The table's alias; absent for a raw slug. */
3085
+ readonly alias?: string;
3086
+ /** What a surface names it: the table's name, or a raw slug as it is. */
3087
+ readonly name: string;
3088
+ readonly chosenBy: ChosenBy;
3089
+ /** A raw slug the table does not hold: the escape hatch, labelled. */
3090
+ readonly experimental: boolean;
3091
+ }
3092
+ /**
3093
+ * What the edge resolved: a competition; a value that is neither an alias nor
3094
+ * a slug, REFUSED with the aliases (an unknown value is never a request); or
3095
+ * `none`: nothing was chosen (no flag, no environment, no saved choice). The
3096
+ * finished World Cup is not a default anyone falls into without choosing.
3097
+ */
3098
+ type CompetitionSelection = SelectedCompetition | {
3099
+ readonly kind: 'refused';
3100
+ readonly value: string;
3101
+ readonly aliases: string[];
3102
+ /** The source that gave the refused value (a flag or the environment: the file's reader refuses its own). */
3103
+ readonly chosenBy: ChosenBy;
3104
+ } | {
3105
+ readonly kind: 'none';
3106
+ };
3107
+ /**
3108
+ * The ONE constructor of a selected competition: a slug and where it was
3109
+ * chosen, described by the table (its alias and name) or, for a slug the
3110
+ * table does not hold, as itself and experimental. The resolver builds every
3111
+ * selection through it; an edge that already holds a slug (a config built
3112
+ * without the resolver) describes it through it too, never by hand.
3113
+ */
3114
+ declare function selectedCompetition(slug: string, chosenBy: ChosenBy): SelectedCompetition;
3115
+ /**
3116
+ * THE EDGE. The one function that decides which competition a request is for,
3117
+ * from the values its edge hands in: the flag (`--competition`, a tool's
3118
+ * `competition` argument), then the environment (`CLAUDINHO_COMPETITION`), then
3119
+ * the saved choice (the config file's `competition`, read at the edge through
3120
+ * core's one reader: `readUserConfig`). The FIRST present source decides (an
3121
+ * empty string is absent): a present value that is refused is refused, even
3122
+ * when a lower source holds a valid one (what was asked for is not answered
3123
+ * with something else). With no source present, `none`: nothing is chosen,
3124
+ * and every competition-answering surface says so instead of answering.
3125
+ *
3126
+ * Each value is an alias (`premier-league`), a slug in the table (`eng.1`), or
3127
+ * a raw ESPN slug the table does not hold (`fifa.friendly`, `esp.copa_del_rey`:
3128
+ * lower-case segments of letters, digits and underscores joined by dots, 64
3129
+ * units at most; experimental);
3130
+ * anything else (`foo`, `ENG.1`, a space) is refused with the aliases.
3131
+ *
3132
+ * Core reads NO environment and no file: the edges pass them. It is called
3133
+ * where a request ENTERS (the CLI's option resolution, the MCP server's
3134
+ * request) and nowhere else. From there the competition travels as a value
3135
+ * (on the config, on the adapter), so nothing further down can answer for a
3136
+ * different competition halfway through. `core/test/selection-identity.test.ts`
3137
+ * fails if a call appears anywhere else.
3138
+ */
3139
+ declare function resolveCompetition(explicit?: string, env?: string, saved?: string): CompetitionSelection;
3140
+ /**
3141
+ * The line every competition-answering text answer prints after its header:
3142
+ * the competition's name, then where the choice came from when it was the
3143
+ * flag or the environment (`from the command line`; on MCP, `from the
3144
+ * request`; `from the environment`), then `experimental` for a raw slug. A
3145
+ * saved choice prints the name alone. The name is not localized; the rest is.
3146
+ * Empty for a selection that is not a competition.
3147
+ */
3148
+ declare function modeLine(selection: CompetitionSelection, lang?: string,
3149
+ /** What a flag is on this surface: the command line's, or a tool request's argument. */
3150
+ flag?: 'command' | 'request'): string;
3151
+ /** The structured form of a selection: what `--json` and MCP `data` carry. */
3152
+ interface CompetitionKey {
3153
+ readonly slug: string;
3154
+ readonly alias?: string;
3155
+ readonly name: string;
3156
+ readonly chosenBy: ChosenBy;
3157
+ readonly experimental?: true;
3158
+ }
3159
+ /**
3160
+ * The selection as ONE structured key, `competition`, for every
3161
+ * competition-answering structured answer (CLI `--json`, MCP `data`): the
3162
+ * alias only when there is one, `experimental` only when true; `null` when
3163
+ * nothing is chosen (beside the `noCompetition` verdict: see
3164
+ * {@link selectionVerdict}). Nothing for a refused value (it is an error, never
3165
+ * an answer). Every emit site spreads this; none builds the key by hand.
3166
+ */
3167
+ declare function selectionExtras(selection: CompetitionSelection): {
3168
+ competition?: CompetitionKey | null;
3169
+ };
3170
+ /**
3171
+ * The verdict a selection states about every answer made under it: with
3172
+ * nothing chosen, `noCompetition` (the first replacing verdict: its sentence
3173
+ * stands instead of any answer, and its key goes beside `competition: null`);
3174
+ * nothing otherwise. A surface hands THIS to `verdictNotice`/`verdictExtras`,
3175
+ * never an object it builds itself.
3176
+ */
3177
+ declare function selectionVerdict(selection: CompetitionSelection): VerdictSource;
3178
+ /**
3179
+ * Why a selection is not a competition, in the reader's language; undefined
3180
+ * for a competition. A refused value names the value (bounded) and the
3181
+ * aliases; nothing chosen is the CLI's sentence (`claudinho follow <alias>`,
3182
+ * `follow --list`): MCP answers nothing chosen with the `noCompetition`
3183
+ * verdict's own sentence (`verdictNotice`), never this one. The CLI raises it
3184
+ * as an input error, MCP a refused value as a tool error, before any request.
3185
+ */
3186
+ declare function selectionRefusal(selection: CompetitionSelection, lang?: string): string | undefined;
3187
+ /** The competition whose schedule ships bundled in the clients: the World Cup. */
3188
+ declare const BUNDLE_COMPETITION = "fifa.world";
3189
+ /**
3190
+ * The edition the bundled schedule describes, read from the schedule itself
3191
+ * (the year of its first kickoff) rather than written down a second time.
3192
+ */
3193
+ declare function bundleSeasonYear(): number;
3194
+ /**
3195
+ * Does the bundled schedule describe what this request is about?
3196
+ *
3197
+ * The competition is an ARGUMENT — the caller's adapter states it — never a
3198
+ * default. Off the bundle, every path built on the skeleton (date merge,
3199
+ * `match <id>`, `next`, the bracket, the knockout-fixture cache, the market
3200
+ * fixture) must not read it: an empty foreign day once showed 104 World Cup
3201
+ * fixtures with foreign attribution (audit A03).
3202
+ *
3203
+ * The bundle is ONE EDITION of its competition. When the provider reported the
3204
+ * season a response belongs to and it is a different one, the bundle does not
3205
+ * describe that response. With no provider answer (offline, degraded) there is
3206
+ * nothing to compare, and the bundle stays what it is: the 2026 edition.
3207
+ */
3208
+ declare function bundleApplies(competition: string, season?: SeasonInfo): boolean;
3209
+ /**
3210
+ * The competitions that HAVE NO BRACKET: league seasons with no knockout tie of
3211
+ * their own, where "no bracket" is the answer, not a gap. Written down in the
3212
+ * supported table (`bracket: 'not-applicable'`), one fact each, never inferred
3213
+ * from the standings shape (`STANDINGS_SHAPE` says
3214
+ * how a table is read, not whether a knockout follows it: `uefa.champions` and
3215
+ * `mex.1` read as one league table and both have one).
3216
+ * - `eng.1`: the Premier League; a 38-round league, no play-off.
3217
+ * - `esp.1`: LaLiga; a 38-round league, no play-off.
3218
+ * Deliberately NOT here: `ita.1` (championship and relegation play-offs since
3219
+ * the FIGC's May 2026 decision), `ger.1` (the relegation play-off), `mex.1`
3220
+ * (the Liguilla), `uefa.champions` (a knockout after the league phase). A
3221
+ * competition joins by a written fact. A derived view, kept for its readers (it
3222
+ * is exported); {@link bracketCapability} reads the row itself.
3223
+ */
3224
+ declare const NO_BRACKET: ReadonlySet<string>;
3225
+ /**
3226
+ * What `bracket` is for a competition, one of three values, read from its ROW
3227
+ * (one row, one fact: a listing, the README matrix and the command cannot
3228
+ * disagree):
3229
+ * - `offered`: the row offers it (only the bundled competition can:
3230
+ * `deriveTables` refuses any other row that does);
3231
+ * - `inapplicable`: the row says the competition has none (`not-applicable`);
3232
+ * - `unsupported`: it may have one, and it is not offered yet; also a slug
3233
+ * the table does not hold.
3234
+ */
3235
+ declare function bracketCapability(competition: string, table?: readonly CompetitionEntry[]): 'offered' | 'inapplicable' | 'unsupported';
3236
+
3237
+ /**
3238
+ * The ambient view: what `claudinho ambient --json` prints, ONE JSON object on
3239
+ * one line, for a program that shows the statusline and the hook itself (the
3240
+ * Claude Code plugin's band, its toasts and its prompt context). A type and
3241
+ * its documentation only: the CLI builds it from its local cache (the
3242
+ * statusline's reader, in `packages/cli`), and core reads no cache and no
3243
+ * environment for it.
3244
+ *
3245
+ * Three shapes reach a reader, and the command always exits 0:
3246
+ * - nothing chosen (no `--competition`, no `CLAUDINHO_COMPETITION`, no saved
3247
+ * choice): the selection's structured twin, `competition: null` beside the
3248
+ * verdict that says nothing is chosen, and `line` (the statusline's
3249
+ * first-run line); no cache is read and no refresher started;
3250
+ * - a value that is no competition, or a failure anywhere: `line` (the
3251
+ * statusline's empty line) and `empty: true` alone, no view;
3252
+ * - a selected competition: an {@link AmbientView}.
3253
+ */
3254
+
3255
+ /**
3256
+ * A live match as the ambient view lists it: the sealed record, as the other
3257
+ * `--json` twins serialize a `Match` (`status`, the `score` object, `shootout`
3258
+ * after a shootout, `Team.id` where the cache holds it, a nation's generated
3259
+ * `flag`, no `flag` key on a club), with no `events` key (the ambient reader
3260
+ * never seals a timeline), and two booleans about the reader's own team:
3261
+ */
3262
+ type AmbientMatch = Match & {
3263
+ /**
3264
+ * A side of it is the ambient PREFERENCE's (`pick`): `CLAUDINHO_TEAM`'s
3265
+ * code, else the saved pin. The picked matches come first in the list.
3266
+ */
3267
+ readonly picked: boolean;
3268
+ /**
3269
+ * A side of it is the SAVED pin's (`claudinho follow <alias> --team`), by
3270
+ * the pin's id when it has one (a club), by its code when it has none (the
3271
+ * World Cup's nations); whatever `CLAUDINHO_TEAM` prefers.
3272
+ */
3273
+ readonly pinned: boolean;
3274
+ /**
3275
+ * The hook's own line for this record, as `claudinho hook` prints it (the
3276
+ * names, pinned to the World Cup's roster on the bundle, each with its flag
3277
+ * where flags are on and the side has one, the scoreline with a shootout's
3278
+ * tally, the minute token: `67'`, `half-time`, `live`). A program says it,
3279
+ * never composes one.
3280
+ */
3281
+ readonly line: string;
3282
+ };
3283
+ /** The ambient view of a selected competition, its keys in this order. */
3284
+ interface AmbientView {
3285
+ /**
3286
+ * The statusline's line, as `claudinho prompt` prints it (the same reader,
3287
+ * the same options), fitted to `--columns N` when given: whole matches are
3288
+ * kept while they fit and the rest counted in the `+N` (or `+more`) suffix,
3289
+ * which is never cut while the width holds it; below the suffix's own width
3290
+ * the line is the width's cut, never wider than asked.
3291
+ */
3292
+ readonly line: string;
3293
+ /**
3294
+ * The line is the one long-lived line, the edition's sign-off (the World
3295
+ * Cup's, once every bundled window has closed): nothing changes until the
3296
+ * user follows another competition, so a program reads it slowly. Said of
3297
+ * the line before any fit: a program reads the pace from this, never from the
3298
+ * text, which `--columns` may have cut.
3299
+ */
3300
+ readonly idle: boolean;
3301
+ /**
3302
+ * The line says nothing is known (`⚽ —`): no live match read, no window to
3303
+ * sync in, nothing to count down to. A program shows nothing for it. Said
3304
+ * of the line before any fit: a program reads the visibility from this,
3305
+ * never from the text.
3306
+ */
3307
+ readonly empty: boolean;
3308
+ /** The hook's block, as `claudinho hook` prints it while a match is live; `null` when it prints nothing. */
3309
+ readonly context: string | null;
3310
+ /**
3311
+ * The live matches the statusline reads (a believed snapshot's, inside the
3312
+ * display window), the preference's first. `complete` says the scan was
3313
+ * whole and `total` is exact only then; an empty list with `complete: false`
3314
+ * is "not known". An empty list is "nothing on" ONLY beside `current: true`:
3315
+ * with `current: false` (no snapshot, a stale one, a stamp the reader does
3316
+ * not believe, a degraded one, one whose read was not whole or that does
3317
+ * not say, the syncing window) it says nothing about
3318
+ * play, a match may be on (`line` is the authority: it prints
3319
+ * `live · syncing…` in the window), and a program must never read
3320
+ * `complete && items.length === 0` alone as "nothing on". A degraded
3321
+ * snapshot (the last read failed) may still carry a live record: it is
3322
+ * listed, best effort, as the line shows it, and is never current.
3323
+ */
3324
+ readonly live: BoundedList<AmbientMatch>;
3325
+ /**
3326
+ * The live list is a believed snapshot's inside its display window (its
3327
+ * stamp's age, by the reader's own rule, is finite and short of the window),
3328
+ * the snapshot is not degraded (a degraded one means the fetch failed, not
3329
+ * that the feed said nothing: it is never current, whatever it carries), the
3330
+ * snapshot states that the read that filled its live slice was whole (a read
3331
+ * that was not, an unreadable record left out beside readable ones, is never
3332
+ * current; nor is a snapshot that does not say, written before 0.11.2, until
3333
+ * one refresh cycle rewrites it), and the line is not syncing: then, and
3334
+ * only then, an empty list means the feed said nothing is in play.
3335
+ */
3336
+ readonly current: boolean;
3337
+ /**
3338
+ * The fixture the statusline's countdown names (its resolved pairing; the
3339
+ * preference's first), whether or not a match is in play now; `null` when
3340
+ * there is none to count down to. A fixture ahead, never one in play.
3341
+ */
3342
+ readonly next: Match | null;
3343
+ /**
3344
+ * The ambient preference as the CLI holds it: `{ code }` from
3345
+ * `CLAUDINHO_TEAM`, the saved pin (`{ id?, code, name }`) when that is
3346
+ * unset, or `null`.
3347
+ */
3348
+ readonly pick: {
3349
+ readonly code: string;
3350
+ } | Pin | null;
3351
+ /**
3352
+ * Present (and `true`) when `CLAUDINHO_TEAM` is set to a value the hot path
3353
+ * cannot read offline (off the World Cup, anything but a three-letter code;
3354
+ * on it, a name its roster does not hold): `pick` is then `null`, never the
3355
+ * pin (the environment is the override).
3356
+ */
3357
+ readonly pickUnreadable?: true;
3358
+ /** The selection, as every `--json` answer carries it (core `selectionExtras`). */
3359
+ readonly competition: CompetitionKey;
3360
+ /** The snapshot says its last refresh failed (`false` with no snapshot). */
3361
+ readonly degraded: boolean;
3362
+ /** The provider the snapshot was read from, whatever its stamp; `null` with no snapshot. */
3363
+ readonly source: string | null;
3364
+ /**
3365
+ * When the snapshot was written (ISO 8601, re-emitted canonically), or
3366
+ * `null` with no snapshot or a stamp the reader does not believe (further in
3367
+ * the future than its clock-skew allowance: its live scores are not shown).
3368
+ */
3369
+ readonly updatedAt: string | null;
3370
+ /**
3371
+ * When the snapshot's live scores stop being shown (`updatedAt` plus the
3372
+ * statusline's display window), or `null` whenever `updatedAt` is.
3373
+ */
3374
+ readonly staleAfter: string | null;
3375
+ /** The non-affiliation sentence, core's `DISCLAIMER`. */
3376
+ readonly disclaimer: string;
3377
+ }
3378
+
3056
3379
  /** The two v1 snippet shapes. `social` is the rich card; `compact` is one terse line per match. */
3057
3380
  type ShareStyle = 'compact' | 'social';
3058
3381
  /** The project social tag — a distribution unit, default-on but removable. */
@@ -3543,172 +3866,6 @@ declare function isPlaceholderSide(team: Pick<Team, 'flag'>): boolean;
3543
3866
  /** The bundled knockout bracket topology (generated by gen:schedule). */
3544
3867
  declare function loadBracketTopology(): BracketTopology;
3545
3868
 
3546
- /**
3547
- * Where a selection came from: the command line's `--competition` or a tool's
3548
- * `competition` argument (`flag`), `CLAUDINHO_COMPETITION` (`env`), or the
3549
- * user's saved choice (`saved`: the config file `claudinho follow` writes).
3550
- * Nothing else chooses: with none of the three there is no competition
3551
- * (`{ kind: 'none' }`), never a default.
3552
- */
3553
- type ChosenBy = 'flag' | 'env' | 'saved';
3554
- /** A competition a request is for, and where that choice came from. */
3555
- interface SelectedCompetition {
3556
- readonly kind: 'selected';
3557
- /** The provider's slug the request asks for. */
3558
- readonly slug: string;
3559
- /** The table's alias; absent for a raw slug. */
3560
- readonly alias?: string;
3561
- /** What a surface names it: the table's name, or a raw slug as it is. */
3562
- readonly name: string;
3563
- readonly chosenBy: ChosenBy;
3564
- /** A raw slug the table does not hold: the escape hatch, labelled. */
3565
- readonly experimental: boolean;
3566
- }
3567
- /**
3568
- * What the edge resolved: a competition; a value that is neither an alias nor
3569
- * a slug, REFUSED with the aliases (an unknown value is never a request); or
3570
- * `none`: nothing was chosen (no flag, no environment, no saved choice). The
3571
- * finished World Cup is not a default anyone falls into without choosing.
3572
- */
3573
- type CompetitionSelection = SelectedCompetition | {
3574
- readonly kind: 'refused';
3575
- readonly value: string;
3576
- readonly aliases: string[];
3577
- /** The source that gave the refused value (a flag or the environment: the file's reader refuses its own). */
3578
- readonly chosenBy: ChosenBy;
3579
- } | {
3580
- readonly kind: 'none';
3581
- };
3582
- /**
3583
- * The ONE constructor of a selected competition: a slug and where it was
3584
- * chosen, described by the table (its alias and name) or, for a slug the
3585
- * table does not hold, as itself and experimental. The resolver builds every
3586
- * selection through it; an edge that already holds a slug (a config built
3587
- * without the resolver) describes it through it too, never by hand.
3588
- */
3589
- declare function selectedCompetition(slug: string, chosenBy: ChosenBy): SelectedCompetition;
3590
- /**
3591
- * THE EDGE. The one function that decides which competition a request is for,
3592
- * from the values its edge hands in: the flag (`--competition`, a tool's
3593
- * `competition` argument), then the environment (`CLAUDINHO_COMPETITION`), then
3594
- * the saved choice (the config file's `competition`, read at the edge through
3595
- * core's one reader: `readUserConfig`). The FIRST present source decides (an
3596
- * empty string is absent): a present value that is refused is refused, even
3597
- * when a lower source holds a valid one (what was asked for is not answered
3598
- * with something else). With no source present, `none`: nothing is chosen,
3599
- * and every competition-answering surface says so instead of answering.
3600
- *
3601
- * Each value is an alias (`premier-league`), a slug in the table (`eng.1`), or
3602
- * a raw ESPN slug the table does not hold (`fifa.friendly`, `esp.copa_del_rey`:
3603
- * lower-case segments of letters, digits and underscores joined by dots, 64
3604
- * units at most; experimental);
3605
- * anything else (`foo`, `ENG.1`, a space) is refused with the aliases.
3606
- *
3607
- * Core reads NO environment and no file: the edges pass them. It is called
3608
- * where a request ENTERS (the CLI's option resolution, the MCP server's
3609
- * request) and nowhere else. From there the competition travels as a value
3610
- * (on the config, on the adapter), so nothing further down can answer for a
3611
- * different competition halfway through. `core/test/selection-identity.test.ts`
3612
- * fails if a call appears anywhere else.
3613
- */
3614
- declare function resolveCompetition(explicit?: string, env?: string, saved?: string): CompetitionSelection;
3615
- /**
3616
- * The line every competition-answering text answer prints after its header:
3617
- * the competition's name, then where the choice came from when it was the
3618
- * flag or the environment (`from the command line`; on MCP, `from the
3619
- * request`; `from the environment`), then `experimental` for a raw slug. A
3620
- * saved choice prints the name alone. The name is not localized; the rest is.
3621
- * Empty for a selection that is not a competition.
3622
- */
3623
- declare function modeLine(selection: CompetitionSelection, lang?: string,
3624
- /** What a flag is on this surface: the command line's, or a tool request's argument. */
3625
- flag?: 'command' | 'request'): string;
3626
- /** The structured form of a selection: what `--json` and MCP `data` carry. */
3627
- interface CompetitionKey {
3628
- readonly slug: string;
3629
- readonly alias?: string;
3630
- readonly name: string;
3631
- readonly chosenBy: ChosenBy;
3632
- readonly experimental?: true;
3633
- }
3634
- /**
3635
- * The selection as ONE structured key, `competition`, for every
3636
- * competition-answering structured answer (CLI `--json`, MCP `data`): the
3637
- * alias only when there is one, `experimental` only when true; `null` when
3638
- * nothing is chosen (beside the `noCompetition` verdict: see
3639
- * {@link selectionVerdict}). Nothing for a refused value (it is an error, never
3640
- * an answer). Every emit site spreads this; none builds the key by hand.
3641
- */
3642
- declare function selectionExtras(selection: CompetitionSelection): {
3643
- competition?: CompetitionKey | null;
3644
- };
3645
- /**
3646
- * The verdict a selection states about every answer made under it: with
3647
- * nothing chosen, `noCompetition` (the first replacing verdict: its sentence
3648
- * stands instead of any answer, and its key goes beside `competition: null`);
3649
- * nothing otherwise. A surface hands THIS to `verdictNotice`/`verdictExtras`,
3650
- * never an object it builds itself.
3651
- */
3652
- declare function selectionVerdict(selection: CompetitionSelection): VerdictSource;
3653
- /**
3654
- * Why a selection is not a competition, in the reader's language; undefined
3655
- * for a competition. A refused value names the value (bounded) and the
3656
- * aliases; nothing chosen is the CLI's sentence (`claudinho follow <alias>`,
3657
- * `follow --list`): MCP answers nothing chosen with the `noCompetition`
3658
- * verdict's own sentence (`verdictNotice`), never this one. The CLI raises it
3659
- * as an input error, MCP a refused value as a tool error, before any request.
3660
- */
3661
- declare function selectionRefusal(selection: CompetitionSelection, lang?: string): string | undefined;
3662
- /** The competition whose schedule ships bundled in the clients: the World Cup. */
3663
- declare const BUNDLE_COMPETITION = "fifa.world";
3664
- /**
3665
- * The edition the bundled schedule describes, read from the schedule itself
3666
- * (the year of its first kickoff) rather than written down a second time.
3667
- */
3668
- declare function bundleSeasonYear(): number;
3669
- /**
3670
- * Does the bundled schedule describe what this request is about?
3671
- *
3672
- * The competition is an ARGUMENT — the caller's adapter states it — never a
3673
- * default. Off the bundle, every path built on the skeleton (date merge,
3674
- * `match <id>`, `next`, the bracket, the knockout-fixture cache, the market
3675
- * fixture) must not read it: an empty foreign day once showed 104 World Cup
3676
- * fixtures with foreign attribution (audit A03).
3677
- *
3678
- * The bundle is ONE EDITION of its competition. When the provider reported the
3679
- * season a response belongs to and it is a different one, the bundle does not
3680
- * describe that response. With no provider answer (offline, degraded) there is
3681
- * nothing to compare, and the bundle stays what it is: the 2026 edition.
3682
- */
3683
- declare function bundleApplies(competition: string, season?: SeasonInfo): boolean;
3684
- /**
3685
- * The competitions that HAVE NO BRACKET: league seasons with no knockout tie of
3686
- * their own, where "no bracket" is the answer, not a gap. Written down in the
3687
- * supported table (`bracket: 'not-applicable'`), one fact each, never inferred
3688
- * from the standings shape (`STANDINGS_SHAPE` says
3689
- * how a table is read, not whether a knockout follows it: `uefa.champions` and
3690
- * `mex.1` read as one league table and both have one).
3691
- * - `eng.1`: the Premier League; a 38-round league, no play-off.
3692
- * - `esp.1`: LaLiga; a 38-round league, no play-off.
3693
- * Deliberately NOT here: `ita.1` (championship and relegation play-offs since
3694
- * the FIGC's May 2026 decision), `ger.1` (the relegation play-off), `mex.1`
3695
- * (the Liguilla), `uefa.champions` (a knockout after the league phase). A
3696
- * competition joins by a written fact. A derived view, kept for its readers (it
3697
- * is exported); {@link bracketCapability} reads the row itself.
3698
- */
3699
- declare const NO_BRACKET: ReadonlySet<string>;
3700
- /**
3701
- * What `bracket` is for a competition, one of three values, read from its ROW
3702
- * (one row, one fact: a listing, the README matrix and the command cannot
3703
- * disagree):
3704
- * - `offered`: the row offers it (only the bundled competition can:
3705
- * `deriveTables` refuses any other row that does);
3706
- * - `inapplicable`: the row says the competition has none (`not-applicable`);
3707
- * - `unsupported`: it may have one, and it is not offered yet; also a slug
3708
- * the table does not hold.
3709
- */
3710
- declare function bracketCapability(competition: string, table?: readonly CompetitionEntry[]): 'offered' | 'inapplicable' | 'unsupported';
3711
-
3712
3869
  /**
3713
3870
  * Metadata that belongs to ONE fetch result.
3714
3871
  *
@@ -3910,4 +4067,4 @@ declare function lookAtEntry(path: string): FileEntry;
3910
4067
  */
3911
4068
  declare function lookAtOwnFile(path: string, maxBytes: number): OwnFile;
3912
4069
 
3913
- export { BRACKET_STAGE_ORDER, BUNDLE_COMPETITION, type BatchResolution, type BetweenEditions, type BoundedList, type BracketFormatOpts, type BracketMatchNode, type BracketMatchView, type BracketResult, type BracketShareCard, type BracketTopology, type BracketView, type BuildSignalInput, COMPETITION_KIND, COUNTDOWN_NOW, type Capabilities, type Capability, type ChosenBy, type ClubResolution, type CompetitionEntry, type CompetitionKey, type CompetitionKind, type CompetitionSelection, DEFAULT_COMPETITION, DEFAULT_COOLDOWN_MS, DEFAULT_FLAVOR, DEFAULT_MAX_AGE_MS, DISCLAIMER, type DayAttribution, type DerivedTables, EARLIER_RECORD_NOTE, EspnAdapter, type EspnAdapterOptions, FAN_PROJECT, FLAVOR_BANKS, FLAVOR_LEVELS, FLAVOR_MOMENTS, FakeMarketProvider, type FakeMarketProviderOptions, type FavoriteStrength, type FetchMeta, type FetchWindowOptions, type FileEntry, type Flair, type FlairOpts, type FlavorBank, type FlavorLevel, type Moment as FlavorMoment, type FlavorOpts, type FormatOpts, type GroupStandings, KNOCKOUT_EXTRA_TIME_MS, KNOWN_SOURCES, type KnockoutFixturesResult, LIVE_WINDOW_MS, type Lang, type LedgerRow, type ListedCompetition, type LiveReadResult, type LiveResult, MARKETS_SCOPE_NOTE, MARKET_COMPETITIONS, MAX_CONFIG_BYTES, MAX_COOLDOWN_MS, MAX_LABEL_COLUMNS, MAX_RESPONSE_BYTES, MAX_SCHEDULE_INDEX, 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 MatchShareCard, NO_BRACKET, type NextFixtureResult, type NoConfigReason, type Outcome, type OwnFile, type ParseResult, type Pin, PolymarketProvider, type PolymarketProviderOptions, type ProviderAdapter, type ProviderCapabilities, ProviderError, type ProviderErrorKind, type PunditPick, RALLY_CRIES, type RallyCry, type RallyPin, type ReadAccount, type ResolvedParticipant, ResponseTooLargeError, type Roster, SCHEDULE_AHEAD_DAYS, SCHEDULE_LOOKBACK_DAYS, SEASON_SLUG, SHARE_DISCLAIMER, SHARE_HASHTAG, STANDINGS_SHAPE, SUPPORTED, type ScheduleAheadResult, type ScheduleEntry, type SeasonInfo, type SelectedCompetition, type Selection, type ShareBracketInput, type ShareBracketOptions, type ShareCardContext, type ShareCardMarket, type ShareCardView, type ShareSnippetInput, type ShareSnippetOptions, type ShareStyle, type ShareTableInput, type SlotRef, type SmallFile, type Stage, type StandingRow, type StandingsResult, type Status, TABLE_KEY_ARG, TEAM_KIND, type TableData, type TableShareCard, type Team, type TeamInfo, type TeamKind, type TeamLookup, type UserConfig, type UserConfigRead, type VerdictExtras, type VerdictSource, allFixtures, allTeams, ambiguous, asFlavorLevel, attachFetchMeta, bounded, bracketCapability, bracketShareCard, buildBracketTopology, buildBracketView, buildMarketSignal, bundleApplies, bundleSeasonYear, byKickoff, cacheDirFor, cacheableKeys, capabilitiesOf, competitionBase, competitionKind, competitionLabel, competitionValue, computeStandings, configPath, countdown, countdownPhrase, currentOrNextFixtureForTeam, dateNoneRead, dateNoneReadSentence, dateShareCard, dateUnreached, dateUnreachedSentence, dayAttribution, definitiveNone, deriveFavorite, deriveTables, disclaimerLine, displayWidth, emptyBatch, entryOf, favoriteStrength, fetchMeta, fixturesByDate, fixturesByGroup, fixturesByTeam, fixturesInLiveWindow, flagEmoji, flavorsFor, formatBracketCompactLine, formatBracketList, formatBracketMatchLine, formatBracketTree, formatDate, formatKickoff, formatShareBracket, formatShareSnippet, formatShareTable, formatTime, getBracket, getKnockoutFixtures, getLiveMatches, getLiveRead, getMarketSignal, getMarketSignals, getMatchById, getMatchesForDate, getNextFixtureForTeam, getScheduleAhead, getStandings, groups, hasLiveWindow, hasSaneDistribution, humanLabel, isCacheable, isFinished, isFlavorLevel, isHumanLabel, isKnockoutStage, isKnownSource, isLive, isMexicoNationalTeam, isPinnedSide, isPlaceholderSide, isReliableMarketSignal, isStaleSignal, isTeam, isTournamentWindowOver, isUpcoming, isValidDate, isValidTimeZone, joinSegments, knockoutWindow, listCompetitions, liveNoneRead, liveNoneReadSentence, liveShareCard, liveSourceLabel, liveWindowMsFor, loadBracketTopology, localDate, lookAtEntry, lookAtOwnFile, lookAtSmallFile, lookupTeam, makeAdapter, makeMarketProvider, malformed, mapEspnEvent, mapsCleanly, marketAttributionText, marketBlock, marketDisplayable, marketFavoriteText, marketFixtureForTeam, marketLine, marketProbabilityText, marketRelevant, marketScopeVerdict, marketSignalRendersFor, marketSourceLabel, marketsCoverCompetition, marketsNoneRead, marketsNoneReadNote, marketsNoneReadSentence, matchFlair, matchFlairs, matchFlavor, matchKey, matchLocation, matchNoneReadSentence, matchShareCard, matchWindowSentence, mergeLive, modeLine, nationArg, nationToFlag, nationToRegion, nextFixtureForPin, nextFixtureForTeam, nextHorizonSentence, nextNoneReadSentence, nextShareCard, normalizeLang, normalizeOutcomes, outcomeFromScore, padVisible, parseCachedMarketSignal, parseCachedMatch, parseCachedMatches, parseCachedScheduleIndex, parseStandings, parseTeamSlot, parsedValue, pinUnder, productFlag, rallyCryFor, rallyEntryFor, readJsonBounded, readSmallFile, readUserConfig, resolveClub, resolveCompetition, resolveMarketSource, resolveTz, resolvedValues, retryAfterMs, rosterFor, sanitizeBundledFixture, savedSlug, scheduleEntryOf, scheduleSpan, scoreline, sealMarketSignal, sealMatch, sealScheduleEntry, sealSeason, selectOne, selectedCompetition, selectionExtras, selectionRefusal, selectionVerdict, servedExtras, stageLabel, stageLabelI18n, standingsShapeOf, statesPartial, t, tableData, tableKeyArg, tableShareCard, tableTitle, teamKind, truncateVisible, unresolved, unservedSentence, valid, verdictExtras, verdictNotice, verdictQualifiers, withFlag };
4070
+ export { type AmbientMatch, type AmbientView, BRACKET_STAGE_ORDER, BUNDLE_COMPETITION, type BatchResolution, type BetweenEditions, type BoundedList, type BracketFormatOpts, type BracketMatchNode, type BracketMatchView, type BracketResult, type BracketShareCard, type BracketTopology, type BracketView, type BuildSignalInput, COMPETITION_KIND, COUNTDOWN_NOW, type Capabilities, type Capability, type ChosenBy, type ClubResolution, type CompetitionEntry, type CompetitionKey, type CompetitionKind, type CompetitionSelection, DEFAULT_COMPETITION, DEFAULT_COOLDOWN_MS, DEFAULT_FLAVOR, DEFAULT_MAX_AGE_MS, DISCLAIMER, type DayAttribution, type DerivedTables, EARLIER_RECORD_NOTE, EspnAdapter, type EspnAdapterOptions, FAN_PROJECT, FLAVOR_BANKS, FLAVOR_LEVELS, FLAVOR_MOMENTS, FakeMarketProvider, type FakeMarketProviderOptions, type FavoriteStrength, type FetchMeta, type FetchWindowOptions, type FileEntry, type Flair, type FlairOpts, type FlavorBank, type FlavorLevel, type Moment as FlavorMoment, type FlavorOpts, type FormatOpts, type GroupStandings, KNOCKOUT_EXTRA_TIME_MS, KNOWN_SOURCES, type KnockoutFixturesResult, LIVE_WINDOW_MS, type Lang, type LedgerRow, type ListedCompetition, type LiveReadResult, type LiveResult, MARKETS_SCOPE_NOTE, MARKET_COMPETITIONS, MAX_CONFIG_BYTES, MAX_COOLDOWN_MS, MAX_LABEL_COLUMNS, MAX_RESPONSE_BYTES, MAX_SCHEDULE_INDEX, 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 MatchShareCard, NO_BRACKET, type NextFixtureResult, type NoConfigReason, type Outcome, type OwnFile, type ParseResult, type Pin, PolymarketProvider, type PolymarketProviderOptions, type ProviderAdapter, type ProviderCapabilities, ProviderError, type ProviderErrorKind, type PunditPick, RALLY_CRIES, type RallyCry, type RallyPin, type ReadAccount, type ResolvedParticipant, ResponseTooLargeError, type Roster, SCHEDULE_AHEAD_DAYS, SCHEDULE_LOOKBACK_DAYS, SEASON_SLUG, SHARE_DISCLAIMER, SHARE_HASHTAG, STANDINGS_SHAPE, SUPPORTED, type ScheduleAheadResult, type ScheduleEntry, type SeasonInfo, type SelectedCompetition, type Selection, type ShareBracketInput, type ShareBracketOptions, type ShareCardContext, type ShareCardMarket, type ShareCardView, type ShareSnippetInput, type ShareSnippetOptions, type ShareStyle, type ShareTableInput, type SlotRef, type SmallFile, type Stage, type StandingRow, type StandingsResult, type Status, TABLE_KEY_ARG, TEAM_KIND, type TableData, type TableShareCard, type Team, type TeamInfo, type TeamKind, type TeamLookup, type UserConfig, type UserConfigRead, type VerdictExtras, type VerdictSource, allFixtures, allTeams, ambiguous, asFlavorLevel, attachFetchMeta, bounded, bracketCapability, bracketShareCard, buildBracketTopology, buildBracketView, buildMarketSignal, bundleApplies, bundleSeasonYear, byKickoff, cacheDirFor, cacheableKeys, capabilitiesOf, competitionBase, competitionKind, competitionLabel, competitionValue, computeStandings, configPath, countdown, countdownPhrase, currentOrNextFixtureForTeam, dateNoneRead, dateNoneReadSentence, dateShareCard, dateUnreached, dateUnreachedSentence, dayAttribution, definitiveNone, deriveFavorite, deriveTables, disclaimerLine, displayWidth, emptyBatch, entryOf, favoriteStrength, fetchMeta, fixturesByDate, fixturesByGroup, fixturesByTeam, fixturesInLiveWindow, flagEmoji, flavorsFor, formatBracketCompactLine, formatBracketList, formatBracketMatchLine, formatBracketTree, formatDate, formatKickoff, formatShareBracket, formatShareSnippet, formatShareTable, formatTime, getBracket, getKnockoutFixtures, getLiveMatches, getLiveRead, getMarketSignal, getMarketSignals, getMatchById, getMatchesForDate, getNextFixtureForTeam, getScheduleAhead, getStandings, groups, hasLiveWindow, hasSaneDistribution, humanLabel, isCacheable, isFinished, isFlavorLevel, isHumanLabel, isKnockoutStage, isKnownSource, isLive, isMexicoNationalTeam, isPinnedSide, isPlaceholderSide, isReliableMarketSignal, isStaleSignal, isTeam, isTournamentWindowOver, isUpcoming, isValidDate, isValidTimeZone, joinSegments, knockoutWindow, listCompetitions, liveNoneRead, liveNoneReadSentence, liveShareCard, liveSourceLabel, liveWindowMsFor, loadBracketTopology, localDate, lookAtEntry, lookAtOwnFile, lookAtSmallFile, lookupTeam, makeAdapter, makeMarketProvider, malformed, mapEspnEvent, mapsCleanly, marketAttributionText, marketBlock, marketDisplayable, marketFavoriteText, marketFixtureForTeam, marketLine, marketProbabilityText, marketRelevant, marketScopeVerdict, marketSignalRendersFor, marketSourceLabel, marketsCoverCompetition, marketsNoneRead, marketsNoneReadNote, marketsNoneReadSentence, matchFlair, matchFlairs, matchFlavor, matchKey, matchLocation, matchNoneReadSentence, matchShareCard, matchWindowSentence, mergeLive, modeLine, nationArg, nationToFlag, nationToRegion, nextFixtureForPin, nextFixtureForTeam, nextHorizonSentence, nextNoneReadSentence, nextShareCard, normalizeLang, normalizeOutcomes, outcomeFromScore, padVisible, parseCachedMarketSignal, parseCachedMatch, parseCachedMatches, parseCachedScheduleIndex, parseStandings, parseTeamSlot, parsedValue, pinUnder, productFlag, rallyCryFor, rallyEntryFor, readJsonBounded, readSmallFile, readUserConfig, requestClock, resolveClub, resolveCompetition, resolveMarketSource, resolveTz, resolvedValues, retryAfterMs, rosterFor, sanitizeBundledFixture, savedSlug, scheduleEntryOf, scheduleSpan, scoreline, sealMarketSignal, sealMatch, sealScheduleEntry, sealSeason, selectOne, selectedCompetition, selectionExtras, selectionRefusal, selectionVerdict, servedExtras, stageLabel, stageLabelI18n, standingsShapeOf, statesPartial, t, tableData, tableKeyArg, tableShareCard, tableTitle, teamKind, truncateVisible, unresolved, unservedSentence, valid, verdictExtras, verdictNotice, verdictQualifiers, withFlag };
package/dist/index.js CHANGED
@@ -763,7 +763,7 @@ function formatTime(iso, opts = {}) {
763
763
  }).format(when);
764
764
  }
765
765
  var COUNTDOWN_NOW = "now";
766
- function countdown(iso, from = /* @__PURE__ */ new Date()) {
766
+ function countdown(iso, from) {
767
767
  const when = parsedDate(iso);
768
768
  if (!when) return UNKNOWN_TIME;
769
769
  const ms = when.getTime() - from.getTime();
@@ -776,7 +776,7 @@ function countdown(iso, from = /* @__PURE__ */ new Date()) {
776
776
  if (hours > 0) return `${hours}h${mins}m`;
777
777
  return `${mins}m`;
778
778
  }
779
- function countdownPhrase(lang, iso, from = /* @__PURE__ */ new Date()) {
779
+ function countdownPhrase(lang, iso, from) {
780
780
  const left = countdown(iso, from);
781
781
  return left === COUNTDOWN_NOW ? t(lang, "countdown.now") : t(lang, "next.in", { countdown: left });
782
782
  }
@@ -4220,6 +4220,7 @@ var SUPPORTED = Object.freeze(
4220
4220
  { slug: "ita.1", alias: "serie-a", name: "Serie A", teams: "club", kind: "league", seasonSlug: "italian-serie-a", standings: "league", bracket: "not-offered-yet", markets: "not-offered-yet", cadenceYears: 1 },
4221
4221
  { slug: "ger.1", alias: "bundesliga", name: "Bundesliga", teams: "club", kind: "league", seasonSlug: "german-bundesliga", standings: "league", bracket: "not-offered-yet", markets: "not-offered-yet", cadenceYears: 1 },
4222
4222
  { slug: "mex.1", alias: "liga-mx", name: "Liga MX", teams: "club", kind: "league", standings: "league", bracket: "not-offered-yet", markets: "not-offered-yet", cadenceYears: 1 },
4223
+ { slug: "uru.1", alias: "liga-auf", name: "Liga AUF Uruguaya", teams: "club", kind: "league", standings: "league", bracket: "not-offered-yet", markets: "not-offered-yet", cadenceYears: 1 },
4223
4224
  { slug: "uefa.champions", alias: "champions-league", name: "Champions League", teams: "club", kind: "cup", standings: "league", bracket: "not-offered-yet", markets: "not-offered-yet", cadenceYears: 1 },
4224
4225
  { slug: "conmebol.libertadores", alias: "libertadores", name: "Libertadores", teams: "club", kind: "cup", standings: "groups", bracket: "not-offered-yet", markets: "not-offered-yet", cadenceYears: 1 },
4225
4226
  { slug: "concacaf.champions", alias: "concacaf-champions-cup", name: "Concacaf Champions Cup", teams: "club", kind: "cup", standings: "none", bracket: "not-offered-yet", markets: "not-offered-yet", cadenceYears: 1 },
@@ -4889,7 +4890,7 @@ function fetchMeta(result) {
4889
4890
  // src/adapters/espn.ts
4890
4891
  var ESPN_SOCCER = "https://site.api.espn.com/apis/site/v2/sports/soccer";
4891
4892
  var DEFAULT_COMPETITION = BUNDLED_SLUG;
4892
- var USER_AGENT = `claudinho/${"0.11.0"} (+https://github.com/arturogarrido/claudinho)`;
4893
+ var USER_AGENT = `claudinho/${"0.11.2"} (+https://github.com/arturogarrido/claudinho)`;
4893
4894
  var MAX_RESPONSE_BYTES = 5 * 1024 * 1024;
4894
4895
  var DEFAULT_COOLDOWN_MS = 5 * 6e4;
4895
4896
  var MAX_COOLDOWN_MS = 15 * 6e4;
@@ -6605,6 +6606,9 @@ function mergeLive(base, live) {
6605
6606
  function clockOf(adapter) {
6606
6607
  return new Date(adapter.now?.() ?? Date.now());
6607
6608
  }
6609
+ function requestClock(adapter, given) {
6610
+ return given ?? clockOf(adapter);
6611
+ }
6608
6612
  function providerDayOf(adapter, instant) {
6609
6613
  if (Number.isNaN(instant.getTime())) return "";
6610
6614
  return adapter.bucketDay?.(instant) || instant.toISOString().slice(0, 10);
@@ -6779,7 +6783,7 @@ async function marketFixtureForTeam(adapter, code, now = /* @__PURE__ */ new Dat
6779
6783
  });
6780
6784
  let refreshRead = {};
6781
6785
  if (candidate) {
6782
- const { partial, served: _served, ...r } = await getMatchById(adapter, candidate.id);
6786
+ const { partial, served: _served, ...r } = await getMatchById(adapter, candidate.id, now);
6783
6787
  refreshRead = partial ? { partial } : {};
6784
6788
  const m = r.degraded ? candidate : r.match ?? candidate;
6785
6789
  if (!isFinished(m.status)) return { ...r, match: m, ...bothReads(windowRead, refreshRead) };
@@ -8858,6 +8862,7 @@ export {
8858
8862
  readJsonBounded,
8859
8863
  readSmallFile,
8860
8864
  readUserConfig,
8865
+ requestClock,
8861
8866
  resolveClub,
8862
8867
  resolveCompetition,
8863
8868
  resolveMarketSource,
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@claudinho/core",
3
- "version": "0.11.0",
4
- "description": "The engine behind Claudinho: the domain model, the provider adapters (ESPN), live scores and standings for the competition you follow (15 supported), the bundled 2026 World Cup schedule with its knockout bracket and read-only Polymarket sidecar. Independent fan project. Not affiliated with FIFA, any confederation, league or club, or Anthropic.",
3
+ "version": "0.11.2",
4
+ "description": "The engine behind Claudinho: the domain model, the provider adapters (ESPN), live scores and standings for the competition you follow (16 supported), the bundled 2026 World Cup schedule with its knockout bracket and read-only Polymarket sidecar. Independent fan project. Not affiliated with FIFA, any confederation, league or club, or Anthropic.",
5
5
  "type": "module",
6
6
  "license": "MIT",
7
7
  "author": "Arturo Garrido",
@@ -60,6 +60,7 @@
60
60
  "serie-a",
61
61
  "bundesliga",
62
62
  "liga-mx",
63
+ "liga-auf",
63
64
  "champions-league",
64
65
  "libertadores",
65
66
  "concacaf-champions-cup",