@claudinho/core 0.9.2 → 0.9.4
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 +1 -1
- package/dist/index.d.ts +422 -167
- package/dist/index.js +1087 -332
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -167,7 +167,11 @@ declare function formatDate(iso: string, opts?: FormatOpts): string;
|
|
|
167
167
|
declare function formatTime(iso: string, opts?: FormatOpts): string;
|
|
168
168
|
/** Compact human countdown until kickoff: "3d4h", "2h10m", "45m", or "now". */
|
|
169
169
|
declare function countdown(iso: string, from?: Date): string;
|
|
170
|
-
/**
|
|
170
|
+
/**
|
|
171
|
+
* The calendar date (YYYY-MM-DD) of a kickoff in the target timezone, or ''
|
|
172
|
+
* when the input can't be parsed — an unfileable fixture matches no date rather
|
|
173
|
+
* than throwing out of whatever was grouping by day.
|
|
174
|
+
*/
|
|
171
175
|
declare function localDate(iso: string, tz?: string): string;
|
|
172
176
|
|
|
173
177
|
/**
|
|
@@ -183,36 +187,6 @@ declare function isValidTimeZone(tz: string | undefined | null): boolean;
|
|
|
183
187
|
*/
|
|
184
188
|
declare function isValidDate(s: string | undefined | null): boolean;
|
|
185
189
|
|
|
186
|
-
/**
|
|
187
|
-
* Feed-string sanitizer — the chokepoint between untrusted provider data and
|
|
188
|
-
* every output surface (terminal, statusline, share cards, and the Claude Code
|
|
189
|
-
* hook, whose stdout lands in the model's context). Strips control characters
|
|
190
|
-
* (C0 incl. ESC, DEL, C1) so a compromised feed can't inject ANSI escapes or
|
|
191
|
-
* multi-line text, and caps length so one field can't flood a surface.
|
|
192
|
-
*
|
|
193
|
-
* Applied at the ESPN adapter boundary (toTeam / mapEspnEvent) and mirrored on
|
|
194
|
-
* the statusline's cache reads (defense against a poisoned cache file).
|
|
195
|
-
*/
|
|
196
|
-
|
|
197
|
-
/** Default per-field cap — generous for any real team/venue name. */
|
|
198
|
-
declare const FEED_TEXT_MAX = 100;
|
|
199
|
-
/**
|
|
200
|
-
* Strip C0/C1 control characters (including ESC) and cap at `max` code points.
|
|
201
|
-
* Whitespace controls (tab/newline/CR) become a single space so words a hostile
|
|
202
|
-
* feed split across lines don't fuse together. Total: never throws.
|
|
203
|
-
*/
|
|
204
|
-
declare function sanitizeFeedText(value: string, max?: number): string;
|
|
205
|
-
/**
|
|
206
|
-
* Sanitized, display-safe copy of a Match. Used on cache reads (the
|
|
207
|
-
* statusline/hook render straight from the cache file), so it must be total:
|
|
208
|
-
* a malformed entry yields empty strings, never a throw. Beyond the string
|
|
209
|
-
* fields, the RENDERED numeric fields (score, shootout, minute) are dropped
|
|
210
|
-
* unless they are real finite numbers — poisoned values degrade to "vs" /
|
|
211
|
-
* "LIVE", never to injected text. Shootout never survives without its score
|
|
212
|
-
* (the adapter-level invariant, re-enforced here).
|
|
213
|
-
*/
|
|
214
|
-
declare function sanitizeMatchStrings(m: Match): Match;
|
|
215
|
-
|
|
216
190
|
/**
|
|
217
191
|
* Display-width helpers for monospace/plain-text alignment.
|
|
218
192
|
*
|
|
@@ -225,6 +199,15 @@ declare function sanitizeMatchStrings(m: Match): Match;
|
|
|
225
199
|
*/
|
|
226
200
|
/** Terminal display width of a string (grapheme clusters; emoji count as 2). */
|
|
227
201
|
declare function displayWidth(s: string): number;
|
|
202
|
+
/**
|
|
203
|
+
* Truncate to `maxColumns` display columns, appending `marker` when anything
|
|
204
|
+
* was dropped. Never splits a grapheme cluster.
|
|
205
|
+
*
|
|
206
|
+
* `padVisible` deliberately never truncates, so a single over-wide value pushed
|
|
207
|
+
* every other column out of line for the whole table — and on the statusline,
|
|
208
|
+
* whose entire contract is one short line, nothing bounded the result at all.
|
|
209
|
+
*/
|
|
210
|
+
declare function truncateVisible(s: string, maxColumns: number, marker?: string): string;
|
|
228
211
|
/**
|
|
229
212
|
* Pad with trailing spaces to `width` DISPLAY columns (never truncates — a
|
|
230
213
|
* too-long value overflows its column rather than being cut mid-name).
|
|
@@ -401,6 +384,31 @@ declare const LIVE_WINDOW_MS: number;
|
|
|
401
384
|
declare const KNOCKOUT_EXTRA_TIME_MS: number;
|
|
402
385
|
/** Live-window length for a fixture — longer for knockout (extra time + penalties). */
|
|
403
386
|
declare function liveWindowMsFor(m: Match): number;
|
|
387
|
+
/**
|
|
388
|
+
* True once EVERY bundled fixture's (stage-aware) live window has ELAPSED —
|
|
389
|
+
* derived from the bundled schedule rather than a hardcoded date (same reasoning
|
|
390
|
+
* as the derived knockout window: the `CLAUDINHO_COMPETITION` seam must not carry
|
|
391
|
+
* a dead calendar).
|
|
392
|
+
*
|
|
393
|
+
* Named for exactly what it measures. This is a TIME predicate, not proof that
|
|
394
|
+
* every match was played: the bundled schedule is a **resultless skeleton** (every
|
|
395
|
+
* fixture ships `SCHEDULED` by design), so status can't inform it — a POSTPONED or
|
|
396
|
+
* abandoned fixture still reads as elapsed once its window passes. That is
|
|
397
|
+
* acceptable for the only use it has (turning a spent schedule into a sign-off),
|
|
398
|
+
* and it's why this is not called `isTournamentComplete`. **Make it status-aware
|
|
399
|
+
* against the live overlay before reusing it for anything that asserts results.**
|
|
400
|
+
*
|
|
401
|
+
* Deliberately a GLOBAL condition, independent of any team filter: an eliminated
|
|
402
|
+
* team mid-tournament has no next fixture either, and must NOT read as "over".
|
|
403
|
+
* Every other empty case still fails closed to the neutral `⚽ —`.
|
|
404
|
+
*
|
|
405
|
+
* Callers must additionally confirm the bundled schedule even applies — it
|
|
406
|
+
* describes the DEFAULT competition, so `CLAUDINHO_COMPETITION` pointing elsewhere
|
|
407
|
+
* makes this answer meaningless (see the `DEFAULT_COMPETITION` gates in the CLI).
|
|
408
|
+
*
|
|
409
|
+
* Empty fixture list → false: "we know nothing" is not "it's over".
|
|
410
|
+
*/
|
|
411
|
+
declare function isTournamentWindowOver(now?: number, fixtures?: Match[]): boolean;
|
|
404
412
|
/** Fixtures whose (stage-aware) live window contains `now` (cheap, static — no network). */
|
|
405
413
|
declare function fixturesInLiveWindow(now?: number, fixtures?: Match[]): Match[];
|
|
406
414
|
/**
|
|
@@ -484,6 +492,17 @@ interface ProviderCapabilities {
|
|
|
484
492
|
interface ProviderAdapter {
|
|
485
493
|
readonly name: string;
|
|
486
494
|
readonly capabilities: ProviderCapabilities;
|
|
495
|
+
/**
|
|
496
|
+
* Expected group-table scope for omission checks. Omit when the competition's
|
|
497
|
+
* full group set is not known in advance.
|
|
498
|
+
*/
|
|
499
|
+
readonly expectedStandingsGroups?: readonly string[];
|
|
500
|
+
/**
|
|
501
|
+
* Groups whose degraded roster may be derived from the bundled schedule.
|
|
502
|
+
* This is deliberately separate from expected scope: a custom competition
|
|
503
|
+
* can have known groups without sharing the bundled World Cup teams.
|
|
504
|
+
*/
|
|
505
|
+
readonly standingsFallbackGroups?: readonly string[];
|
|
487
506
|
/** All fixtures/results for a single calendar date (provider's timezone semantics). */
|
|
488
507
|
fetchByDate(dateISO: string): Promise<Match[]>;
|
|
489
508
|
/** Currently in-progress matches (poll path). */
|
|
@@ -493,8 +512,9 @@ interface ProviderAdapter {
|
|
|
493
512
|
/**
|
|
494
513
|
* Optional authoritative group tables (cumulative across the group stage).
|
|
495
514
|
* Returned in standings order per group. Providers that can't supply a real
|
|
496
|
-
* table omit this; callers then
|
|
497
|
-
*
|
|
515
|
+
* table omit this; callers then fail closed (degraded), using a roster at zero
|
|
516
|
+
* only when `standingsFallbackGroups` declares bundled-schedule compatibility
|
|
517
|
+
* — never a wrong, partial table computed from a narrow live window.
|
|
498
518
|
*/
|
|
499
519
|
fetchStandings?(): Promise<GroupStandings[]>;
|
|
500
520
|
/** Optional push subscription (websocket/SSE providers). Returns an unsubscribe fn. */
|
|
@@ -513,6 +533,118 @@ interface ProviderAdapter {
|
|
|
513
533
|
};
|
|
514
534
|
}
|
|
515
535
|
|
|
536
|
+
/**
|
|
537
|
+
* Bounded collections — the type that carries its own honesty.
|
|
538
|
+
*
|
|
539
|
+
* Every round of review found a list that was capped without saying so, or that
|
|
540
|
+
* reported a post-cap count as the total, or that said "none found" when it had
|
|
541
|
+
* simply run out of budget. Those are the same bug three ways: the shape of the
|
|
542
|
+
* data did not carry what the reader needed to interpret it. A `BoundedList`
|
|
543
|
+
* cannot be constructed without stating all four facts.
|
|
544
|
+
*/
|
|
545
|
+
interface BoundedList<T> {
|
|
546
|
+
/** What survived the cap. */
|
|
547
|
+
readonly items: readonly T[];
|
|
548
|
+
/**
|
|
549
|
+
* How many were established before capping. Exact when `complete` is true;
|
|
550
|
+
* otherwise it may only be a lower bound over the records examined.
|
|
551
|
+
*/
|
|
552
|
+
readonly total: number;
|
|
553
|
+
/** `items.length` — kept explicit so a serialized payload is self-describing. */
|
|
554
|
+
readonly shown: number;
|
|
555
|
+
/** Did the cap drop anything? */
|
|
556
|
+
readonly truncated: boolean;
|
|
557
|
+
/**
|
|
558
|
+
* Did we finish looking? False when a deadline, error, or partial fetch cut
|
|
559
|
+
* the work short — so a caller can tell "there are none" from "we don't know".
|
|
560
|
+
* An empty list with `complete: false` must NEVER render as "nothing found".
|
|
561
|
+
*/
|
|
562
|
+
readonly complete: boolean;
|
|
563
|
+
}
|
|
564
|
+
/**
|
|
565
|
+
* Bound a list, recording what that cost.
|
|
566
|
+
*
|
|
567
|
+
* SLICE BEFORE MAP is the caller's job — this records the outcome, it does not
|
|
568
|
+
* make an unbounded traversal safe. `takeBounded` is the one that bounds work.
|
|
569
|
+
*/
|
|
570
|
+
declare function bounded<T>(items: readonly T[], max: number, complete?: boolean): BoundedList<T>;
|
|
571
|
+
|
|
572
|
+
/**
|
|
573
|
+
* The vocabulary for "we could not use this".
|
|
574
|
+
*
|
|
575
|
+
* `undefined` was doing five jobs at once — absent, malformed, ambiguous, timed
|
|
576
|
+
* out, and definitively none — and the caller could not tell them apart. That
|
|
577
|
+
* ambiguity is what let a schema failure get negative-cached as the FACT "this
|
|
578
|
+
* fixture has no market", and what made `checked` mean two different things on
|
|
579
|
+
* two paths. Every rejection now says which kind it is.
|
|
580
|
+
*/
|
|
581
|
+
/** A value we produced, or the reason we did not. */
|
|
582
|
+
type ParseResult<T> = {
|
|
583
|
+
readonly kind: 'valid';
|
|
584
|
+
readonly value: T;
|
|
585
|
+
}
|
|
586
|
+
/** We read the payload fine, and the answer is genuinely "nothing here". Cacheable. */
|
|
587
|
+
| {
|
|
588
|
+
readonly kind: 'definitive-none';
|
|
589
|
+
readonly reason: string;
|
|
590
|
+
}
|
|
591
|
+
/** We could not read the payload. A fact about US, not about the fixture. NOT cacheable. */
|
|
592
|
+
| {
|
|
593
|
+
readonly kind: 'malformed';
|
|
594
|
+
readonly reason: string;
|
|
595
|
+
}
|
|
596
|
+
/**
|
|
597
|
+
* The payload admits more than one reading. Never guess between them.
|
|
598
|
+
*
|
|
599
|
+
* CACHEABLE, unlike the two below. This is a fact about a payload we read
|
|
600
|
+
* successfully — two legs claiming the same team, an incoherent 1X2 — and it
|
|
601
|
+
* is STABLE: fetching again returns the same bytes and the same ambiguity.
|
|
602
|
+
* Treating it as non-cacheable meant re-fetching on every single command,
|
|
603
|
+
* forever, and under the default-on enrichment deadline those doomed fixtures
|
|
604
|
+
* consumed the whole budget and starved the resolvable ones behind them.
|
|
605
|
+
* A negative TTL is a bounded delay if the provider later fixes their data;
|
|
606
|
+
* a permanent refetch loop is not bounded by anything.
|
|
607
|
+
*/
|
|
608
|
+
| {
|
|
609
|
+
readonly kind: 'ambiguous';
|
|
610
|
+
readonly reason: string;
|
|
611
|
+
}
|
|
612
|
+
/**
|
|
613
|
+
* We never reached a verdict — the deadline expired, the budget ran out.
|
|
614
|
+
* A fact about the CLOCK, not about the fixture. NOT cacheable.
|
|
615
|
+
*
|
|
616
|
+
* This kind exists because the alternative was folding a timeout into
|
|
617
|
+
* `malformed`, which is the same conflation this type was written to end.
|
|
618
|
+
*/
|
|
619
|
+
| {
|
|
620
|
+
readonly kind: 'unresolved';
|
|
621
|
+
readonly reason: string;
|
|
622
|
+
};
|
|
623
|
+
declare const valid: <T>(value: T) => ParseResult<T>;
|
|
624
|
+
declare const definitiveNone: <T>(reason: string) => ParseResult<T>;
|
|
625
|
+
declare const malformed: <T>(reason: string) => ParseResult<T>;
|
|
626
|
+
declare const ambiguous: <T>(reason: string) => ParseResult<T>;
|
|
627
|
+
declare const unresolved: <T>(reason: string) => ParseResult<T>;
|
|
628
|
+
/** The value, or undefined — for callers that genuinely do not care why. */
|
|
629
|
+
declare function parsedValue<T>(r: ParseResult<T>): T | undefined;
|
|
630
|
+
/**
|
|
631
|
+
* May this rejection be remembered for the length of a TTL?
|
|
632
|
+
*
|
|
633
|
+
* The line is whether we READ the payload, not whether we liked it. A
|
|
634
|
+
* definitive none and an ambiguity are both conclusions drawn from bytes we
|
|
635
|
+
* understood, and both are stable across a refetch — so remembering them is
|
|
636
|
+
* correct, and re-asking immediately just burns the request.
|
|
637
|
+
*
|
|
638
|
+
* `malformed` and `unresolved` are facts about US: a shape we could not read
|
|
639
|
+
* (which may be a provider mid-deploy) and a clock that ran out. Remembering
|
|
640
|
+
* either would suppress the retry that recovers.
|
|
641
|
+
*/
|
|
642
|
+
declare function isCacheable<T>(r: ParseResult<T>): boolean;
|
|
643
|
+
|
|
644
|
+
interface MapContext {
|
|
645
|
+
groupByTeam?: Record<string, string>;
|
|
646
|
+
}
|
|
647
|
+
|
|
516
648
|
/**
|
|
517
649
|
* ESPN adapter — the free, keyless default/fallback source.
|
|
518
650
|
*
|
|
@@ -542,70 +674,21 @@ declare class ProviderError extends Error {
|
|
|
542
674
|
/** 429/403 — the upstream is refusing us; retrying at the live cadence makes it worse. */
|
|
543
675
|
get throttled(): boolean;
|
|
544
676
|
}
|
|
545
|
-
interface EspnStatusType {
|
|
546
|
-
name?: string;
|
|
547
|
-
state?: string;
|
|
548
|
-
completed?: boolean;
|
|
549
|
-
shortDetail?: string;
|
|
550
|
-
}
|
|
551
|
-
interface EspnStatus {
|
|
552
|
-
type?: EspnStatusType;
|
|
553
|
-
clock?: number;
|
|
554
|
-
displayClock?: string;
|
|
555
|
-
period?: number;
|
|
556
|
-
}
|
|
557
|
-
interface EspnTeam {
|
|
558
|
-
abbreviation?: string;
|
|
559
|
-
displayName?: string;
|
|
560
|
-
shortDisplayName?: string;
|
|
561
|
-
name?: string;
|
|
562
|
-
location?: string;
|
|
563
|
-
}
|
|
564
|
-
interface EspnCompetitor {
|
|
565
|
-
homeAway?: 'home' | 'away';
|
|
566
|
-
score?: string;
|
|
567
|
-
/** Penalty-shootout tally, present only on shootout matches (ESPN sends a number). */
|
|
568
|
-
shootoutScore?: number | string;
|
|
569
|
-
winner?: boolean;
|
|
570
|
-
team?: EspnTeam;
|
|
571
|
-
}
|
|
572
|
-
interface EspnCompetition {
|
|
573
|
-
id?: string;
|
|
574
|
-
date?: string;
|
|
575
|
-
competitors?: EspnCompetitor[];
|
|
576
|
-
venue?: {
|
|
577
|
-
fullName?: string;
|
|
578
|
-
address?: {
|
|
579
|
-
city?: string;
|
|
580
|
-
country?: string;
|
|
581
|
-
};
|
|
582
|
-
};
|
|
583
|
-
status?: EspnStatus;
|
|
584
|
-
}
|
|
585
|
-
interface EspnSeason {
|
|
586
|
-
year?: number;
|
|
587
|
-
slug?: string;
|
|
588
|
-
}
|
|
589
|
-
interface EspnEvent {
|
|
590
|
-
id: string;
|
|
591
|
-
date: string;
|
|
592
|
-
name?: string;
|
|
593
|
-
shortName?: string;
|
|
594
|
-
season?: EspnSeason;
|
|
595
|
-
status?: EspnStatus;
|
|
596
|
-
competitions?: EspnCompetition[];
|
|
597
|
-
}
|
|
598
|
-
/** Optional context to enrich mapping (authoritative team->group letter). */
|
|
599
|
-
interface MapContext {
|
|
600
|
-
/** Map of UPPERCASE team code -> group letter ("A".."L"), from standings. */
|
|
601
|
-
groupByTeam?: Record<string, string>;
|
|
602
|
-
}
|
|
603
677
|
/** Map a single ESPN event into the canonical Match model. Exported for tests. */
|
|
604
|
-
declare function mapEspnEvent(ev:
|
|
678
|
+
declare function mapEspnEvent(ev: unknown, ctx?: MapContext): Match | undefined;
|
|
679
|
+
/** Project an ESPN standings payload onto group tables. Exported for tests. */
|
|
680
|
+
declare function parseStandings(data: unknown): GroupStandings[];
|
|
605
681
|
interface EspnAdapterOptions {
|
|
606
682
|
baseUrl?: string;
|
|
607
683
|
fetchImpl?: typeof fetch;
|
|
608
684
|
timeoutMs?: number;
|
|
685
|
+
/**
|
|
686
|
+
* Expected standings groups for completeness checks and definitive group
|
|
687
|
+
* validation. Defaults to the bundled groups for the default World Cup base;
|
|
688
|
+
* custom bases leave the scope open unless supplied. Declaring custom groups
|
|
689
|
+
* does not authorize use of the bundled World Cup roster on failure.
|
|
690
|
+
*/
|
|
691
|
+
expectedStandingsGroups?: readonly string[];
|
|
609
692
|
/**
|
|
610
693
|
* Enrich group-stage matches with their group letter via the standings
|
|
611
694
|
* endpoint (one extra request). Default true. Set false on the hot live-poll
|
|
@@ -613,39 +696,13 @@ interface EspnAdapterOptions {
|
|
|
613
696
|
*/
|
|
614
697
|
enrichGroups?: boolean;
|
|
615
698
|
}
|
|
616
|
-
interface EspnStandingsStat {
|
|
617
|
-
name?: string;
|
|
618
|
-
value?: number;
|
|
619
|
-
}
|
|
620
|
-
interface EspnStandingsEntry {
|
|
621
|
-
team?: EspnTeam;
|
|
622
|
-
stats?: EspnStandingsStat[];
|
|
623
|
-
}
|
|
624
|
-
interface EspnStandingsChild {
|
|
625
|
-
name?: string;
|
|
626
|
-
abbreviation?: string;
|
|
627
|
-
standings?: {
|
|
628
|
-
entries?: EspnStandingsEntry[];
|
|
629
|
-
};
|
|
630
|
-
}
|
|
631
|
-
interface EspnStandings {
|
|
632
|
-
children?: EspnStandingsChild[];
|
|
633
|
-
}
|
|
634
|
-
/**
|
|
635
|
-
* Parse the standings payload into group tables. Pure (exported for tests).
|
|
636
|
-
*
|
|
637
|
-
* Two non-obvious robustness points, both verified against the live response:
|
|
638
|
-
* - ESPN's `entries` array is NOT in rank order, so we sort by the `rank` stat
|
|
639
|
-
* (falling back to points → GD → GF → code when rank is absent).
|
|
640
|
-
* - Non-group `children` (knockout brackets) are skipped by the "Group X" name
|
|
641
|
-
* test, so this stays correct once the bracket phase begins.
|
|
642
|
-
*/
|
|
643
|
-
declare function parseStandings(data: EspnStandings): GroupStandings[];
|
|
644
699
|
declare class EspnAdapter implements ProviderAdapter {
|
|
645
700
|
private readonly opts;
|
|
646
701
|
readonly name = "espn";
|
|
647
702
|
readonly capabilities: ProviderCapabilities;
|
|
648
|
-
|
|
703
|
+
readonly expectedStandingsGroups?: readonly string[];
|
|
704
|
+
readonly standingsFallbackGroups?: readonly string[];
|
|
705
|
+
/** Short-lived team-code -> group-letter map (built lazily from standings). */
|
|
649
706
|
private groupMap?;
|
|
650
707
|
/**
|
|
651
708
|
* One in-flight/recent standings fetch shared by fetchStandings and
|
|
@@ -677,15 +734,17 @@ declare class EspnAdapter implements ProviderAdapter {
|
|
|
677
734
|
private sharedStandings;
|
|
678
735
|
/**
|
|
679
736
|
* Authoritative, cumulative group tables from the standings endpoint. Throws
|
|
680
|
-
* on fetch
|
|
681
|
-
*
|
|
737
|
+
* on fetch failure. Group-stage only: non-group `children` are filtered out
|
|
738
|
+
* by {@link parseStandings}; malformed rows are omitted without hiding their
|
|
739
|
+
* readable siblings.
|
|
682
740
|
*/
|
|
683
741
|
fetchStandings(): Promise<GroupStandings[]>;
|
|
684
742
|
/**
|
|
685
|
-
* Build (and cache) a team-code -> group-letter map from the standings
|
|
743
|
+
* Build (and briefly cache) a team-code -> group-letter map from the standings
|
|
686
744
|
* endpoint. Best-effort: returns {} if standings are unavailable — but a
|
|
687
|
-
* transient failure is NOT cached
|
|
688
|
-
*
|
|
745
|
+
* transient failure is NOT cached, and a partial successful parse expires at
|
|
746
|
+
* the standings TTL, so neither can silently drop group letters for the
|
|
747
|
+
* adapter's lifetime.
|
|
689
748
|
* Reuses the same parse/fetch as {@link fetchStandings}, so the two never
|
|
690
749
|
* drift and one command never fetches standings twice.
|
|
691
750
|
*/
|
|
@@ -737,22 +796,33 @@ declare function getMatchesForDate(adapter: ProviderAdapter, dateISO: string): P
|
|
|
737
796
|
interface StandingsResult {
|
|
738
797
|
/** Group tables in group-letter order; each table's rows in standings order. */
|
|
739
798
|
tables: GroupStandings[];
|
|
740
|
-
/** True when no authoritative table was available
|
|
799
|
+
/** True when no authoritative table was available; tables may be a static roster or empty. */
|
|
741
800
|
degraded: boolean;
|
|
742
|
-
/**
|
|
801
|
+
/** Provider that served the authoritative result, including an empty one (absent when degraded). */
|
|
743
802
|
source?: string;
|
|
744
803
|
}
|
|
745
804
|
/**
|
|
746
805
|
* Authoritative group tables, preferring the provider's cumulative standings and
|
|
747
|
-
* FAILING CLOSED
|
|
806
|
+
* FAILING CLOSED when none is available. An adapter with explicit bundled-
|
|
807
|
+
* roster compatibility may use the roster-at-zero; otherwise it returns an
|
|
808
|
+
* empty degraded result because expected group letters alone do not prove the
|
|
809
|
+
* bundle belongs to the same competition.
|
|
748
810
|
*
|
|
749
811
|
* Deliberately does NOT compute a table from a live-match window: that silently
|
|
750
812
|
* drops earlier matchdays and reports a wrong, partial table (e.g. all-zeros for
|
|
751
813
|
* a group not playing today) — the bug this replaced. A degraded roster is
|
|
752
|
-
*
|
|
814
|
+
* explicitly zeroed; a confidently-wrong table is the failure mode we refuse.
|
|
753
815
|
*
|
|
754
|
-
* An empty `tables` with `degraded: false` means the fetch succeeded but
|
|
755
|
-
* asked-for group
|
|
816
|
+
* An empty `tables` with `degraded: false` means either the fetch succeeded but
|
|
817
|
+
* the asked-for group wasn't in it, or the group is definitively outside the
|
|
818
|
+
* adapter's declared scope (caller renders "no such group"). A group declared
|
|
819
|
+
* in that expected scope but omitted from a partial
|
|
820
|
+
* provider result takes the degraded fallback instead of rendering as an
|
|
821
|
+
* authoritative empty table. An aggregate read also falls back when any group
|
|
822
|
+
* in that scope is absent; one result-level verdict cannot honestly describe a
|
|
823
|
+
* mix of live tables and static roster tables. Transport failure without
|
|
824
|
+
* explicit bundled-roster compatibility stays empty and degraded; it must
|
|
825
|
+
* never borrow the bundled World Cup roster.
|
|
756
826
|
*/
|
|
757
827
|
declare function getStandings(adapter: ProviderAdapter, group?: string): Promise<StandingsResult>;
|
|
758
828
|
declare function knockoutWindow(): {
|
|
@@ -856,15 +926,65 @@ declare function getMatchById(adapter: ProviderAdapter, id: string): Promise<Mat
|
|
|
856
926
|
declare function getLiveMatches(adapter: ProviderAdapter, now?: Date): Promise<LiveResult>;
|
|
857
927
|
|
|
858
928
|
/**
|
|
859
|
-
*
|
|
860
|
-
*
|
|
861
|
-
*
|
|
862
|
-
*
|
|
863
|
-
*
|
|
929
|
+
* Choosing one of several, and reporting on a batch.
|
|
930
|
+
*
|
|
931
|
+
* Two shapes, both replacing a boolean that was carrying more meaning than a
|
|
932
|
+
* boolean can hold.
|
|
933
|
+
*
|
|
934
|
+
* `Selection` replaces "the selector returned undefined". Finding no market for
|
|
935
|
+
* a team and finding TWO markets for that team both produced `undefined`, and
|
|
936
|
+
* the caller — having no way to tell them apart — recorded the second as the
|
|
937
|
+
* definitive fact "this fixture has no market" and negative-cached it for the
|
|
938
|
+
* whole TTL. An ambiguity is the one thing we must never resolve by guessing,
|
|
939
|
+
* and it was the case being guessed at.
|
|
940
|
+
*
|
|
941
|
+
* `BatchResolution` replaces `checked: Set<string>`. That set was maintained
|
|
942
|
+
* alongside the results rather than derived from them, so "did we reach a
|
|
943
|
+
* verdict?" and "what was the verdict?" could disagree — and did, on two paths.
|
|
944
|
+
* Here the verdict IS the record, and cacheability is read off it.
|
|
945
|
+
*/
|
|
946
|
+
|
|
947
|
+
/** Exactly one, none, or more than one — never "one of the several". */
|
|
948
|
+
type Selection<T> = {
|
|
949
|
+
readonly kind: 'one';
|
|
950
|
+
readonly value: T;
|
|
951
|
+
} | {
|
|
952
|
+
readonly kind: 'none';
|
|
953
|
+
} | {
|
|
954
|
+
readonly kind: 'ambiguous';
|
|
955
|
+
readonly count: number;
|
|
956
|
+
};
|
|
957
|
+
/**
|
|
958
|
+
* The single candidate, or an honest account of why there isn't one.
|
|
864
959
|
*
|
|
865
|
-
*
|
|
866
|
-
*
|
|
960
|
+
* Deliberately NOT `candidates[0]`: which of two legs claiming the same team is
|
|
961
|
+
* the real one is not a question the payload answers, so it is not a question
|
|
962
|
+
* this function answers either.
|
|
867
963
|
*/
|
|
964
|
+
declare function selectOne<T>(candidates: readonly T[]): Selection<T>;
|
|
965
|
+
/**
|
|
966
|
+
* Every input's verdict, plus whether every input GOT one.
|
|
967
|
+
*
|
|
968
|
+
* `complete: false` means the batch was cut short (deadline, provider error) —
|
|
969
|
+
* the missing entries are unasked questions, not negative answers.
|
|
970
|
+
*/
|
|
971
|
+
interface BatchResolution<T> {
|
|
972
|
+
readonly results: ReadonlyMap<string, ParseResult<T>>;
|
|
973
|
+
readonly complete: boolean;
|
|
974
|
+
}
|
|
975
|
+
/** The values that resolved, keyed as they went in. */
|
|
976
|
+
declare function resolvedValues<T>(batch: BatchResolution<T>): Map<string, T>;
|
|
977
|
+
/**
|
|
978
|
+
* The keys whose verdict may be remembered — see {@link isCacheable}.
|
|
979
|
+
*
|
|
980
|
+
* This is the old `checked` set, now DERIVED from each verdict instead of
|
|
981
|
+
* tracked beside it. A malformed or unresolved key is absent, so a shape we
|
|
982
|
+
* could not read and a deadline that expired are both retried next time rather
|
|
983
|
+
* than cached as the fact that this fixture has no market.
|
|
984
|
+
*/
|
|
985
|
+
declare function cacheableKeys<T>(batch: BatchResolution<T>): Set<string>;
|
|
986
|
+
/** An empty batch that reached nobody — what a total provider failure returns. */
|
|
987
|
+
declare function emptyBatch<T>(): BatchResolution<T>;
|
|
868
988
|
|
|
869
989
|
/** Which match result a priced line refers to (home win / draw / away win). */
|
|
870
990
|
type MarketOutcomeKind = 'home' | 'draw' | 'away' | 'other';
|
|
@@ -937,16 +1057,16 @@ interface MarketSignalOptions {
|
|
|
937
1057
|
timeoutMs?: number;
|
|
938
1058
|
}
|
|
939
1059
|
/**
|
|
940
|
-
* Result of a batch lookup
|
|
941
|
-
*
|
|
942
|
-
*
|
|
943
|
-
*
|
|
944
|
-
*
|
|
1060
|
+
* Result of a batch lookup: every match id's VERDICT, plus whether the batch
|
|
1061
|
+
* finished. Read the signals with `resolvedValues` and the negative-cacheable
|
|
1062
|
+
* ids with `cacheableKeys`.
|
|
1063
|
+
*
|
|
1064
|
+
* This replaces a `checked: Set<string>` that was maintained beside the results
|
|
1065
|
+
* instead of derived from them — so an ambiguous or unreadable payload could be
|
|
1066
|
+
* recorded as the definitive fact "this fixture has no market", suppressing the
|
|
1067
|
+
* refetch for the whole TTL.
|
|
945
1068
|
*/
|
|
946
|
-
|
|
947
|
-
signals: Map<string, MarketSignal>;
|
|
948
|
-
checked: Set<string>;
|
|
949
|
-
}
|
|
1069
|
+
type MarketSignalsResult = BatchResolution<MarketSignal>;
|
|
950
1070
|
/**
|
|
951
1071
|
* A prediction-market provider. A *separate* swap-point from ProviderAdapter
|
|
952
1072
|
* (which supplies match data): different cadence, reliability, and legal
|
|
@@ -956,8 +1076,8 @@ interface MarketProvider {
|
|
|
956
1076
|
readonly name: string;
|
|
957
1077
|
/** Signal for one match, or undefined when nothing maps cleanly. */
|
|
958
1078
|
findSignal(match: Match, options?: MarketSignalOptions): Promise<MarketSignal | undefined>;
|
|
959
|
-
/** Batch form;
|
|
960
|
-
findSignals(matches: Match[], options?: MarketSignalOptions): Promise<MarketSignalsResult>;
|
|
1079
|
+
/** Batch form; a verdict per match id (see MarketSignalsResult). */
|
|
1080
|
+
findSignals(matches: readonly Match[], options?: MarketSignalOptions): Promise<MarketSignalsResult>;
|
|
961
1081
|
}
|
|
962
1082
|
|
|
963
1083
|
/** Default freshness window: a signal older than this is stale (15 minutes). */
|
|
@@ -1001,7 +1121,13 @@ declare function mapsCleanly(match: Match, outcomes: MarketOutcome[]): boolean;
|
|
|
1001
1121
|
declare function marketSignalRendersFor(match: Match, signal: MarketSignal): boolean;
|
|
1002
1122
|
/** Sanity check: ≥2 priced outcomes whose probabilities sum to ~1. */
|
|
1003
1123
|
declare function hasSaneDistribution(outcomes: MarketOutcome[]): boolean;
|
|
1004
|
-
/**
|
|
1124
|
+
/**
|
|
1125
|
+
* Is the signal outside the freshness window? Unparseable timestamps are stale.
|
|
1126
|
+
*
|
|
1127
|
+
* A FUTURE `asOf` is stale too. Freshness was a one-sided test (`now - asOf >
|
|
1128
|
+
* maxAge`), so a timestamp dated forward was never stale and never expired —
|
|
1129
|
+
* failing open permanently, in the direction that looks most trustworthy.
|
|
1130
|
+
*/
|
|
1005
1131
|
declare function isStaleSignal(signal: MarketSignal, options?: MarketSignalOptions): boolean;
|
|
1006
1132
|
/**
|
|
1007
1133
|
* The load-bearing gate. A signal is reliable only when it is unambiguous, has
|
|
@@ -1077,8 +1203,8 @@ declare function resolveMarketSource(explicit?: string): string;
|
|
|
1077
1203
|
declare function makeMarketProvider(source?: string): MarketProvider;
|
|
1078
1204
|
/** Fetch one match's signal; never throws — undefined on any error. */
|
|
1079
1205
|
declare function getMarketSignal(provider: MarketProvider, match: Match, options?: MarketSignalOptions): Promise<MarketSignal | undefined>;
|
|
1080
|
-
/** Batch fetch; never throws — empty
|
|
1081
|
-
declare function getMarketSignals(provider: MarketProvider, matches: Match[], options?: MarketSignalOptions): Promise<MarketSignalsResult>;
|
|
1206
|
+
/** Batch fetch; never throws — an empty, INCOMPLETE batch on any error. */
|
|
1207
|
+
declare function getMarketSignals(provider: MarketProvider, matches: readonly Match[], options?: MarketSignalOptions): Promise<MarketSignalsResult>;
|
|
1082
1208
|
|
|
1083
1209
|
/**
|
|
1084
1210
|
* A network-free MarketProvider for tests and local UX validation. Returns
|
|
@@ -1100,10 +1226,130 @@ declare class FakeMarketProvider implements MarketProvider {
|
|
|
1100
1226
|
readonly name = "fake";
|
|
1101
1227
|
constructor(opts?: FakeMarketProviderOptions);
|
|
1102
1228
|
findSignal(match: Match, options?: MarketSignalOptions): Promise<MarketSignal | undefined>;
|
|
1103
|
-
findSignals(matches: Match[], options?: MarketSignalOptions): Promise<MarketSignalsResult>;
|
|
1229
|
+
findSignals(matches: readonly Match[], options?: MarketSignalOptions): Promise<MarketSignalsResult>;
|
|
1104
1230
|
private synthesize;
|
|
1105
1231
|
}
|
|
1106
1232
|
|
|
1233
|
+
/** Display columns a label may occupy. Generous for any real team or venue name. */
|
|
1234
|
+
declare const MAX_LABEL_COLUMNS = 100;
|
|
1235
|
+
/**
|
|
1236
|
+
* Prose meant for a human: a team name, a venue, a city, a market outcome label.
|
|
1237
|
+
*
|
|
1238
|
+
* NFC-normalized so the same name has one representation, bounded on input
|
|
1239
|
+
* length (work), display columns (layout) and code points (bytes), and free of
|
|
1240
|
+
* anything invisible. Whitespace controls fold to a space so words a hostile
|
|
1241
|
+
* feed split across lines cannot fuse.
|
|
1242
|
+
*
|
|
1243
|
+
* Total: never throws, whatever the input.
|
|
1244
|
+
*/
|
|
1245
|
+
declare function humanLabel(value: unknown, maxColumns?: number): string;
|
|
1246
|
+
/**
|
|
1247
|
+
* A flag, GENERATED from the bundled map — never accepted from anywhere.
|
|
1248
|
+
*
|
|
1249
|
+
* This is the load-bearing line of the whole module. Because a flag is produced
|
|
1250
|
+
* here rather than passed through, no untrusted input ever needs to carry an
|
|
1251
|
+
* emoji, so {@link humanLabel} can refuse every invisible code point without
|
|
1252
|
+
* exception. The three P1s that each rode the old emoji exemption (TAG
|
|
1253
|
+
* sequences, variation selectors, ZWJ chains) are not defended against — they
|
|
1254
|
+
* have nowhere to enter.
|
|
1255
|
+
*/
|
|
1256
|
+
declare function productFlag(nameOrCode: string | undefined): string;
|
|
1257
|
+
|
|
1258
|
+
/**
|
|
1259
|
+
* The single place a `Match` is sealed — whichever path it arrived by.
|
|
1260
|
+
*
|
|
1261
|
+
* This closes the PATH asymmetry. A fixture reaches a renderer two ways: live
|
|
1262
|
+
* from the ESPN adapter, or read back from the local cache file that the
|
|
1263
|
+
* statusline and hook render on every prompt. Those two paths had SEPARATE
|
|
1264
|
+
* rules, so every fix landed on one of them:
|
|
1265
|
+
*
|
|
1266
|
+
* - the live path DERIVED each team's flag from the nation; the cache path
|
|
1267
|
+
* accepted whatever string sat in `flag`, which is why the emoji exemption
|
|
1268
|
+
* existed at all and why TAG, variation selectors and ZWJ each produced a P1
|
|
1269
|
+
* - the live path required `status !== 'SCHEDULED'` before keeping a score;
|
|
1270
|
+
* the cache path kept any numeric pair, so an edited cache file could show
|
|
1271
|
+
* a scoreline on a fixture that has not kicked off
|
|
1272
|
+
* - the live path set `winnerCode` to one of the two competitors by
|
|
1273
|
+
* construction; the cache path passed any string through, and `winnerCode`
|
|
1274
|
+
* is what advances a team through the bracket
|
|
1275
|
+
*
|
|
1276
|
+
* Both paths now end HERE, so a rule cannot be added to one and forgotten on the
|
|
1277
|
+
* other. `parseEspnEvent` assembles candidate parts from the feed and seals
|
|
1278
|
+
* them; `parseCachedMatch` seals the record it read. The parity property in
|
|
1279
|
+
* trust-parity.test.ts asserts the two agree, and idempotence — sealing an
|
|
1280
|
+
* already-sealed Match returns it unchanged — is what makes the round trip safe.
|
|
1281
|
+
*/
|
|
1282
|
+
|
|
1283
|
+
/** Loosely-typed candidate fields — whatever the feed or the cache file held. */
|
|
1284
|
+
interface MatchParts {
|
|
1285
|
+
id?: unknown;
|
|
1286
|
+
stage?: unknown;
|
|
1287
|
+
group?: unknown;
|
|
1288
|
+
kickoff?: unknown;
|
|
1289
|
+
venue?: unknown;
|
|
1290
|
+
city?: unknown;
|
|
1291
|
+
country?: unknown;
|
|
1292
|
+
home?: unknown;
|
|
1293
|
+
away?: unknown;
|
|
1294
|
+
score?: unknown;
|
|
1295
|
+
shootout?: unknown;
|
|
1296
|
+
minute?: unknown;
|
|
1297
|
+
status?: unknown;
|
|
1298
|
+
winnerCode?: unknown;
|
|
1299
|
+
updatedAt?: unknown;
|
|
1300
|
+
events?: unknown;
|
|
1301
|
+
}
|
|
1302
|
+
/**
|
|
1303
|
+
* Validate candidate parts into a Match, or say why not.
|
|
1304
|
+
*
|
|
1305
|
+
* `id`, `kickoff`, `stage` and `status` DROP the whole fixture rather than
|
|
1306
|
+
* falling back to a default. Each decides what the reader is told — status picks
|
|
1307
|
+
* between "FT" and a live scoreline, kickoff decides which calendar day the
|
|
1308
|
+
* fixture is filed under — so substituting a plausible value would invent the
|
|
1309
|
+
* very fact the bad field destroyed.
|
|
1310
|
+
*/
|
|
1311
|
+
interface SealOptions {
|
|
1312
|
+
/**
|
|
1313
|
+
* Seal in-match events too. Default true.
|
|
1314
|
+
*
|
|
1315
|
+
* The statusline and hook render a scoreline, not a timeline — they never
|
|
1316
|
+
* read `events` — and sealing them is the DOMINANT cost on a 150ms path: a
|
|
1317
|
+
* poisoned cache of 64 live matches carrying 128 events each measured
|
|
1318
|
+
* 11.9 s, because each event's `player` is a label and a label is segmented
|
|
1319
|
+
* grapheme by grapheme. Bounding the COUNT was not enough when the surface
|
|
1320
|
+
* needs ZERO. Cheapest work is work not done.
|
|
1321
|
+
*/
|
|
1322
|
+
readonly events?: boolean;
|
|
1323
|
+
}
|
|
1324
|
+
declare function sealMatch(parts: MatchParts, opts?: SealOptions): ParseResult<Match>;
|
|
1325
|
+
/**
|
|
1326
|
+
* A Match read back from our own cache file.
|
|
1327
|
+
*
|
|
1328
|
+
* The cache is a local file the statusline renders on every prompt, so it is
|
|
1329
|
+
* untrusted input in exactly the way a feed response is — with the extra twist
|
|
1330
|
+
* that it holds values we ourselves wrote, which is what made it tempting to
|
|
1331
|
+
* trust. It goes through the same seal.
|
|
1332
|
+
*/
|
|
1333
|
+
declare function parseCachedMatch(raw: unknown, opts?: SealOptions): ParseResult<Match>;
|
|
1334
|
+
/** Cached fixtures, bounded BEFORE the per-record work. */
|
|
1335
|
+
declare function parseCachedMatches(raw: unknown, max: number, opts?: SealOptions): BoundedList<Match>;
|
|
1336
|
+
|
|
1337
|
+
/**
|
|
1338
|
+
* Validate a market signal, or say why not.
|
|
1339
|
+
*
|
|
1340
|
+
* `matchId` drops the whole signal: it is the key everything else is bound to,
|
|
1341
|
+
* and a signal that cannot name its fixture cannot be checked against one.
|
|
1342
|
+
*/
|
|
1343
|
+
declare function sealMarketSignal(raw: unknown, options?: {
|
|
1344
|
+
now?: Date;
|
|
1345
|
+
maxAgeMs?: number;
|
|
1346
|
+
}): ParseResult<MarketSignal>;
|
|
1347
|
+
/** A signal read back from our own cache file — same seal as the live one. */
|
|
1348
|
+
declare function parseCachedMarketSignal(raw: unknown, options?: {
|
|
1349
|
+
now?: Date;
|
|
1350
|
+
maxAgeMs?: number;
|
|
1351
|
+
}): ParseResult<MarketSignal>;
|
|
1352
|
+
|
|
1107
1353
|
/**
|
|
1108
1354
|
* Optional override of the derived event slug for a fixture whose Polymarket
|
|
1109
1355
|
* slug doesn't follow `fifwc-{home}-{away}-{date}` (e.g. an abbreviation that
|
|
@@ -1131,12 +1377,18 @@ declare class PolymarketProvider implements MarketProvider {
|
|
|
1131
1377
|
readonly name = "polymarket";
|
|
1132
1378
|
constructor(opts?: PolymarketProviderOptions);
|
|
1133
1379
|
findSignal(match: Match, options?: MarketSignalOptions): Promise<MarketSignal | undefined>;
|
|
1134
|
-
findSignals(matches: Match[], options?: MarketSignalOptions): Promise<
|
|
1380
|
+
findSignals(matches: readonly Match[], options?: MarketSignalOptions): Promise<BatchResolution<MarketSignal>>;
|
|
1135
1381
|
/**
|
|
1136
|
-
* Resolve one match
|
|
1137
|
-
*
|
|
1138
|
-
*
|
|
1139
|
-
*
|
|
1382
|
+
* Resolve one match into a verdict.
|
|
1383
|
+
*
|
|
1384
|
+
* Every exit says which KIND of non-answer it is, because that decides
|
|
1385
|
+
* whether it may be remembered — see `isCacheable`: a conclusion we drew from
|
|
1386
|
+
* a payload we READ is cacheable (including an ambiguity, which is stable),
|
|
1387
|
+
* while a shape we could not read is not. Previously a single
|
|
1388
|
+
* `checked: boolean` collapsed five distinct situations into two, and the
|
|
1389
|
+
* ones that landed on the wrong side of it — an ambiguous payload, a
|
|
1390
|
+
* two-legged market, an incoherent 1X2 — were negative-cached as the fact
|
|
1391
|
+
* that this fixture has no market.
|
|
1140
1392
|
*/
|
|
1141
1393
|
private resolveOne;
|
|
1142
1394
|
private fetchEvent;
|
|
@@ -1166,12 +1418,15 @@ interface ShareSnippetInput {
|
|
|
1166
1418
|
/** Pre-resolved, English title line, e.g. "Next up for Mexico". */
|
|
1167
1419
|
title: string;
|
|
1168
1420
|
/** Matches to render (0..n). An empty set still yields a valid titled card. */
|
|
1169
|
-
|
|
1421
|
+
/** Read-only: callers pass a bounded view, which must not be mutated. */
|
|
1422
|
+
matches: readonly Match[];
|
|
1170
1423
|
/**
|
|
1171
1424
|
* Reliable, display-ready market signals keyed by match id (sidecar — never
|
|
1172
1425
|
* embedded in Match). Callers gate these; the formatter only renders.
|
|
1173
1426
|
*/
|
|
1174
1427
|
marketSignals?: Map<string, MarketSignal>;
|
|
1428
|
+
/** False when market enrichment stopped before every relevant match was checked. */
|
|
1429
|
+
marketComplete?: boolean;
|
|
1175
1430
|
/** Live-data provider name (e.g. "espn") for attribution; omit when static/degraded. */
|
|
1176
1431
|
source?: string;
|
|
1177
1432
|
/**
|
|
@@ -1202,9 +1457,9 @@ interface ShareSnippetInput {
|
|
|
1202
1457
|
declare function formatShareSnippet(input: ShareSnippetInput, options?: ShareSnippetOptions): string;
|
|
1203
1458
|
interface ShareTableInput {
|
|
1204
1459
|
/** Group tables to render (1..n); each in standings order. */
|
|
1205
|
-
tables: {
|
|
1460
|
+
tables: readonly {
|
|
1206
1461
|
group: string;
|
|
1207
|
-
rows: StandingRow[];
|
|
1462
|
+
rows: readonly StandingRow[];
|
|
1208
1463
|
}[];
|
|
1209
1464
|
/** Live-data provider name for attribution; omit when degraded/static. */
|
|
1210
1465
|
source?: string;
|
|
@@ -1213,10 +1468,10 @@ interface ShareTableInput {
|
|
|
1213
1468
|
/** Body line when there are no tables (e.g. "No group Z."). */
|
|
1214
1469
|
emptyNote?: string;
|
|
1215
1470
|
/**
|
|
1216
|
-
* True when
|
|
1217
|
-
*
|
|
1218
|
-
*
|
|
1219
|
-
*
|
|
1471
|
+
* True when no authoritative table was available. Non-empty rows are a
|
|
1472
|
+
* static roster, not live results; an empty open-scope outage is described by
|
|
1473
|
+
* `emptyNote`. A shared card is pasted into public/social, so degraded state
|
|
1474
|
+
* MUST be surfaced rather than reading as an authoritative table.
|
|
1220
1475
|
*/
|
|
1221
1476
|
degraded?: boolean;
|
|
1222
1477
|
}
|
|
@@ -1293,4 +1548,4 @@ declare function formatShareBracket(input: ShareBracketInput, options?: ShareBra
|
|
|
1293
1548
|
/** Compact one-line-per-match bracket for narrow share contexts. */
|
|
1294
1549
|
declare function formatBracketCompactLine(mv: BracketMatchView, opts?: BracketFormatOpts): string;
|
|
1295
1550
|
|
|
1296
|
-
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,
|
|
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 };
|