@claudinho/core 0.11.2 → 0.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +231 -18
- package/dist/index.js +293 -17
- package/package.json +2 -2
package/dist/index.d.ts
CHANGED
|
@@ -153,11 +153,27 @@ type Stage = 'GROUP' | 'R32' | 'R16' | 'QF' | 'SF' | '3P' | 'F' | 'FRIENDLY' | '
|
|
|
153
153
|
type Status = 'SCHEDULED' | 'LIVE' | 'HT' | 'FT' | 'POSTPONED' | 'CANCELLED';
|
|
154
154
|
/** Match outcome from the home team's perspective. */
|
|
155
155
|
type Outcome = 'H' | 'D' | 'A';
|
|
156
|
-
/**
|
|
156
|
+
/**
|
|
157
|
+
* One of a match's goals or cards, as the provider listed it (ESPN's
|
|
158
|
+
* `competitions[0].details`, read by the grammar in `trust/espn.ts` and sealed
|
|
159
|
+
* by `sealEvent` in `trust/match.ts`, on the feed and the cache path alike).
|
|
160
|
+
* Key order is the serialized order.
|
|
161
|
+
*/
|
|
157
162
|
interface MatchEvent {
|
|
163
|
+
/** The kind. `SUB` stays in the type; ESPN's scoreboard never produces one. */
|
|
158
164
|
type: 'GOAL' | 'OWN_GOAL' | 'PEN' | 'YELLOW' | 'RED' | 'SUB';
|
|
165
|
+
/** The minute the provider's clock showed: `90` for `90'+3'`. */
|
|
159
166
|
minute: number;
|
|
167
|
+
/** Added time past `minute` (`3` for `90'+3'`), 1 to 30; absent when none. */
|
|
168
|
+
added?: number;
|
|
169
|
+
/**
|
|
170
|
+
* The side the event belongs to: the side a goal COUNTED for (an own goal
|
|
171
|
+
* included), the side a card was shown to. The identity; `teamCode` is a label.
|
|
172
|
+
*/
|
|
173
|
+
side: 'home' | 'away';
|
|
174
|
+
/** That side's code, a display label. */
|
|
160
175
|
teamCode: string;
|
|
176
|
+
/** The player, when the provider names exactly one of the expected team. */
|
|
161
177
|
player?: string;
|
|
162
178
|
}
|
|
163
179
|
/** A single fixture/result — the central entity. */
|
|
@@ -201,7 +217,13 @@ interface Match {
|
|
|
201
217
|
/** Live match minute when in progress. */
|
|
202
218
|
minute?: number;
|
|
203
219
|
status: Status;
|
|
204
|
-
/**
|
|
220
|
+
/**
|
|
221
|
+
* The provider's goals and cards that could be read, in time order (minute,
|
|
222
|
+
* then added time). Never a claim that the list is every goal: a detail the
|
|
223
|
+
* grammar cannot read is left out, and a live timeline lags its score by
|
|
224
|
+
* nature. None on a match with no score (scheduled, postponed, cancelled);
|
|
225
|
+
* the key is absent when none was read.
|
|
226
|
+
*/
|
|
205
227
|
events?: MatchEvent[];
|
|
206
228
|
/**
|
|
207
229
|
* Winner's team code when the provider marks one (e.g. ESPN `competitor.winner`
|
|
@@ -279,6 +301,27 @@ type Lang = 'en' | 'es' | 'pt' | 'fr';
|
|
|
279
301
|
declare function normalizeLang(lang?: string): Lang;
|
|
280
302
|
/** Translate a message key with optional `{placeholder}` interpolation. */
|
|
281
303
|
declare function t(lang: string | undefined, key: string, vars?: Record<string, string>): string;
|
|
304
|
+
/**
|
|
305
|
+
* The ten words of the plugin's moments, as `claudinho ambient --json` sends
|
|
306
|
+
* them (`words`), in this order: core's `moment.*` keys in one language.
|
|
307
|
+
* `goalFor` and `win` are templates holding `{team}` once, which the plugin
|
|
308
|
+
* fills with the record's own names; names, scores and minutes are never
|
|
309
|
+
* translated.
|
|
310
|
+
*/
|
|
311
|
+
interface MomentWords {
|
|
312
|
+
readonly goal: string;
|
|
313
|
+
readonly ownGoal: string;
|
|
314
|
+
readonly ownGoalDetail: string;
|
|
315
|
+
readonly goalFor: string;
|
|
316
|
+
readonly goals: string;
|
|
317
|
+
readonly scoreUpdated: string;
|
|
318
|
+
readonly penalties: string;
|
|
319
|
+
readonly fullTime: string;
|
|
320
|
+
readonly win: string;
|
|
321
|
+
readonly draw: string;
|
|
322
|
+
}
|
|
323
|
+
/** The moment words in `lang` (an unsupported one is English), the templates left unfilled. */
|
|
324
|
+
declare function momentWords(lang: string | undefined): MomentWords;
|
|
282
325
|
/**
|
|
283
326
|
* Localized stage label, from the match (its stage, its group letter under the
|
|
284
327
|
* group stage, and the words an OTHER stage carries). An `OTHER` stage prints
|
|
@@ -384,6 +427,42 @@ declare function padVisible(s: string, width: number): string;
|
|
|
384
427
|
*/
|
|
385
428
|
declare function withFlag(text: string, flag: string | undefined, side: 'home' | 'away'): string;
|
|
386
429
|
|
|
430
|
+
interface TimelineOptions {
|
|
431
|
+
/** The reader's language (en/es/pt/fr); English when absent. */
|
|
432
|
+
readonly lang?: string;
|
|
433
|
+
/** Whether a nation's flag is printed beside its code (a club has none either way). */
|
|
434
|
+
readonly flags: boolean;
|
|
435
|
+
}
|
|
436
|
+
/** One part of a timeline row: its text, and `strong` on the player. */
|
|
437
|
+
interface TimelinePart {
|
|
438
|
+
readonly text: string;
|
|
439
|
+
readonly style?: 'strong';
|
|
440
|
+
}
|
|
441
|
+
/**
|
|
442
|
+
* The match's events as rows of parts; `[]` when it has none. Each row is
|
|
443
|
+
* the aligned columns as one plain part (two spaces, the minute, the marker,
|
|
444
|
+
* one space and the localized kind word, the side's label), then, when the
|
|
445
|
+
* provider named a player, two spaces and the player as one `strong` part.
|
|
446
|
+
* Joined, a row is its {@link timelineRows} line.
|
|
447
|
+
*/
|
|
448
|
+
declare function timelineParts(match: Match, opts: TimelineOptions): TimelinePart[][];
|
|
449
|
+
/** The match's events as aligned text rows ({@link timelineParts} joined); `[]` when it has none. */
|
|
450
|
+
declare function timelineRows(match: Match, opts: TimelineOptions): string[];
|
|
451
|
+
/**
|
|
452
|
+
* The goal that made the record's score, PROVEN, or `null`. The record's
|
|
453
|
+
* scoring events, counted per side in their order (an own goal for the side
|
|
454
|
+
* it credited, which is the event's `side`), must equal its regulation score:
|
|
455
|
+
* then the timeline is whole and its last scoring event is the goal that made
|
|
456
|
+
* this score. Anything else proves nothing (a timeline that lags the score, a
|
|
457
|
+
* disallowed goal still listed, no score, no scoring event): the first scorer
|
|
458
|
+
* is never named for a second goal. Two scoring events identical in type,
|
|
459
|
+
* minute, added time, side and player prove nothing either: the same goal
|
|
460
|
+
* listed twice beside a timeline that lags would count as two. The ambient
|
|
461
|
+
* view's `lastGoal`, written by the refresher on each live record it
|
|
462
|
+
* publishes.
|
|
463
|
+
*/
|
|
464
|
+
declare function provenLastGoal(match: Match): MatchEvent | null;
|
|
465
|
+
|
|
387
466
|
/** Outcome (home perspective) from a final/looking scoreline. */
|
|
388
467
|
declare function outcomeFromScore(home: number, away: number): Outcome;
|
|
389
468
|
/** Is the match currently in play (including halftime)? */
|
|
@@ -871,8 +950,9 @@ declare function fixturesByGroup(group: string, fixtures?: Match[]): Match[];
|
|
|
871
950
|
/**
|
|
872
951
|
* Eligible to be "the next match": the kickoff is still ahead AND the match is
|
|
873
952
|
* still going to be played. ONE predicate for every selector (`next`, the
|
|
874
|
-
*
|
|
875
|
-
*
|
|
953
|
+
* knockout-fixture cache, the market fixture, the refresher cadence; the
|
|
954
|
+
* statusline countdown asks it for a kickoff strictly ahead and also names a
|
|
955
|
+
* DUE fixture, the CLI's `isDue`) — a cancelled or postponed fixture keeps its kickoff, so
|
|
876
956
|
* a time-only check advertised it as the next game (audit A07). Finished is
|
|
877
957
|
* deliberately NOT a clause: under a real clock a finished match never has a
|
|
878
958
|
* future kickoff, and excluding it only changed seeded-clock replays of an
|
|
@@ -1683,7 +1763,7 @@ interface KnockoutFixturesResult {
|
|
|
1683
1763
|
/**
|
|
1684
1764
|
* The RESOLVED upcoming knockout fixtures from the live overlay — the data the
|
|
1685
1765
|
* hot-path statusline can't fetch itself but needs to show a real next-match
|
|
1686
|
-
* countdown (e.g. "🇲🇽 vs 🇪🇨 in 2d") instead of a 🏳️ placeholder. The cold-path
|
|
1766
|
+
* countdown (e.g. "⚽ 🇲🇽 vs 🇪🇨 in 2d") instead of a 🏳️ placeholder. The cold-path
|
|
1687
1767
|
* refresher calls this and caches the result; the statusline reads the cache.
|
|
1688
1768
|
*
|
|
1689
1769
|
* Returns only fixtures where BOTH sides are resolved (neither is the 🏳️ placeholder) and kickoff
|
|
@@ -1794,6 +1874,8 @@ declare function getScheduleAhead(adapter: ProviderAdapter, now?: Date): Promise
|
|
|
1794
1874
|
* an adjacent bucket. Adapters without a window fetch fall back to `fetchLive()`.
|
|
1795
1875
|
*/
|
|
1796
1876
|
declare function getLiveMatches(adapter: ProviderAdapter, now?: Date): Promise<LiveResult>;
|
|
1877
|
+
/** The finished records a live read returns, at most (the refresher's record of play keeps as many). */
|
|
1878
|
+
declare const MAX_LIVE_FINISHED = 64;
|
|
1797
1879
|
/** A live read with the answer's own account of itself: what the refresher decides from. */
|
|
1798
1880
|
interface LiveReadResult extends LiveResult {
|
|
1799
1881
|
/**
|
|
@@ -1802,14 +1884,30 @@ interface LiveReadResult extends LiveResult {
|
|
|
1802
1884
|
* whole and holds no match in play does not prove none is.
|
|
1803
1885
|
*/
|
|
1804
1886
|
complete: boolean;
|
|
1887
|
+
/**
|
|
1888
|
+
* The window's records with status `FT` that hold a score, as the adapter
|
|
1889
|
+
* sealed them, asked of the whole window before the in-play filter: the ids
|
|
1890
|
+
* the caller passed as `seen` first, in the window's order, then the others,
|
|
1891
|
+
* at most {@link MAX_LIVE_FINISHED}. `[]` for a failed read. The evidence of
|
|
1892
|
+
* full time for a match the refresher saw in play; never on
|
|
1893
|
+
* {@link getLiveMatches}' result.
|
|
1894
|
+
*/
|
|
1895
|
+
finished: Match[];
|
|
1896
|
+
/**
|
|
1897
|
+
* The cap left finished records out: an id the caller saw that is absent
|
|
1898
|
+
* from `finished` may then be one of them, so its absence decides nothing.
|
|
1899
|
+
*/
|
|
1900
|
+
finishedTruncated: boolean;
|
|
1805
1901
|
}
|
|
1806
1902
|
/**
|
|
1807
1903
|
* {@link getLiveMatches}, keeping the window's `complete` verdict (read from
|
|
1808
|
-
* the window's result BEFORE the in-play filter makes a new array)
|
|
1809
|
-
*
|
|
1810
|
-
*
|
|
1904
|
+
* the window's result BEFORE the in-play filter makes a new array), and the
|
|
1905
|
+
* window's finished records (`finished`, chosen ids-first by `seen`, the ids
|
|
1906
|
+
* the refresher's record of play holds as seen in play). For the cold-path
|
|
1907
|
+
* refresher, which keeps polling after a match was seen in play until a WHOLE
|
|
1908
|
+
* read holds none.
|
|
1811
1909
|
*/
|
|
1812
|
-
declare function getLiveRead(adapter: ProviderAdapter, now?: Date): Promise<LiveReadResult>;
|
|
1910
|
+
declare function getLiveRead(adapter: ProviderAdapter, now?: Date, seen?: readonly string[]): Promise<LiveReadResult>;
|
|
1813
1911
|
|
|
1814
1912
|
/** A roster team plus its group letter (A–L). */
|
|
1815
1913
|
interface TeamInfo extends Team {
|
|
@@ -1951,6 +2049,15 @@ interface BoundedList<T> {
|
|
|
1951
2049
|
* make an unbounded traversal safe. `takeBounded` is the one that bounds work.
|
|
1952
2050
|
*/
|
|
1953
2051
|
declare function bounded<T>(items: readonly T[], max: number, complete?: boolean): BoundedList<T>;
|
|
2052
|
+
/**
|
|
2053
|
+
* Take at most `max` items from something that may not be an array at all,
|
|
2054
|
+
* BEFORE any per-item work happens.
|
|
2055
|
+
*
|
|
2056
|
+
* This is the work bound. Mapping, sorting or sanitizing first and slicing
|
|
2057
|
+
* afterwards is what made a 4,000-row group cost 300ms to return 32, and a
|
|
2058
|
+
* 100k-event record cost 5.3s on a 150ms surface.
|
|
2059
|
+
*/
|
|
2060
|
+
declare function takeBounded<T>(value: unknown, max: number): T[];
|
|
1954
2061
|
|
|
1955
2062
|
/**
|
|
1956
2063
|
* The vocabulary for "we could not use this".
|
|
@@ -2514,7 +2621,7 @@ declare const DEFAULT_MAX_AGE_MS: number;
|
|
|
2514
2621
|
* "markets favor X 100%" after full time reads as a bug, not information.
|
|
2515
2622
|
* An unparseable kickoff fails open (the reliability gate still applies).
|
|
2516
2623
|
*/
|
|
2517
|
-
declare function marketRelevant(match: Match, now
|
|
2624
|
+
declare function marketRelevant(match: Match, now: Date): boolean;
|
|
2518
2625
|
/**
|
|
2519
2626
|
* Re-scale outcome probabilities so the positive ones sum to 1. This removes
|
|
2520
2627
|
* market "vig" (raw prices sum to >1) and is scale-agnostic: inputs may be
|
|
@@ -2886,6 +2993,15 @@ declare function isPinnedSide(side: Team, pin: {
|
|
|
2886
2993
|
readonly code: string;
|
|
2887
2994
|
readonly name: string;
|
|
2888
2995
|
}): boolean;
|
|
2996
|
+
/**
|
|
2997
|
+
* THE constructor of a `MatchEvent`, on the feed path (the candidates the ESPN
|
|
2998
|
+
* detail grammar built) and the cache path alike. An event with no side, a
|
|
2999
|
+
* side that is neither, an unreadable minute or an added time outside 1..30 is
|
|
3000
|
+
* refused (`null`); the match stands. Keys in the order `MatchEvent` declares
|
|
3001
|
+
* them. Exported for the one other reader of an event, the ambient view, which
|
|
3002
|
+
* re-seals the `lastGoal` the refresher stored on a cached live record.
|
|
3003
|
+
*/
|
|
3004
|
+
declare function sealEvent(raw: unknown): MatchEvent | null;
|
|
2889
3005
|
/**
|
|
2890
3006
|
* Validate candidate parts into a Match, or say why not.
|
|
2891
3007
|
*
|
|
@@ -3252,12 +3368,41 @@ declare function bracketCapability(competition: string, table?: readonly Competi
|
|
|
3252
3368
|
* - a selected competition: an {@link AmbientView}.
|
|
3253
3369
|
*/
|
|
3254
3370
|
|
|
3371
|
+
/**
|
|
3372
|
+
* One part of the band's line (`lineParts`): a run of its text, `strong` for a
|
|
3373
|
+
* score token the fit kept whole. The parts' texts joined are the line.
|
|
3374
|
+
*/
|
|
3375
|
+
interface AmbientLinePart {
|
|
3376
|
+
readonly text: string;
|
|
3377
|
+
readonly style?: 'strong';
|
|
3378
|
+
}
|
|
3379
|
+
/** The two names the hook's line prints for a record (a nation's roster name on the bundled competition). */
|
|
3380
|
+
interface AmbientNames {
|
|
3381
|
+
readonly home: string;
|
|
3382
|
+
readonly away: string;
|
|
3383
|
+
}
|
|
3384
|
+
/**
|
|
3385
|
+
* A finished match as the ambient view lists it (`finished`): the sealed
|
|
3386
|
+
* record (status `FT`, its final score), as a live one is serialized, with:
|
|
3387
|
+
*/
|
|
3388
|
+
type AmbientFinishedMatch = Match & {
|
|
3389
|
+
/**
|
|
3390
|
+
* The side of the SAVED pin (`claudinho follow <alias> --team`, by core's
|
|
3391
|
+
* `isPinnedSide`), or `null`: never `CLAUDINHO_TEAM`, which only orders.
|
|
3392
|
+
*/
|
|
3393
|
+
readonly pinnedSide: 'home' | 'away' | null;
|
|
3394
|
+
/** The names the hook's line prints for it. */
|
|
3395
|
+
readonly names: AmbientNames;
|
|
3396
|
+
/** The hook's own line for it, `(FT)` in the minute's place. */
|
|
3397
|
+
readonly line: string;
|
|
3398
|
+
};
|
|
3255
3399
|
/**
|
|
3256
3400
|
* A live match as the ambient view lists it: the sealed record, as the other
|
|
3257
3401
|
* `--json` twins serialize a `Match` (`status`, the `score` object, `shootout`
|
|
3258
3402
|
* after a shootout, `Team.id` where the cache holds it, a nation's generated
|
|
3259
3403
|
* `flag`, no `flag` key on a club), with no `events` key (the ambient reader
|
|
3260
|
-
* never seals a timeline), and
|
|
3404
|
+
* never seals a timeline), and what it says about the reader's own team, the
|
|
3405
|
+
* names and the last goal:
|
|
3261
3406
|
*/
|
|
3262
3407
|
type AmbientMatch = Match & {
|
|
3263
3408
|
/**
|
|
@@ -3271,6 +3416,10 @@ type AmbientMatch = Match & {
|
|
|
3271
3416
|
* World Cup's nations); whatever `CLAUDINHO_TEAM` prefers.
|
|
3272
3417
|
*/
|
|
3273
3418
|
readonly pinned: boolean;
|
|
3419
|
+
/** Which side is the SAVED pin's (by core's `isPinnedSide`), or `null`: never `CLAUDINHO_TEAM`'s. */
|
|
3420
|
+
readonly pinnedSide: 'home' | 'away' | null;
|
|
3421
|
+
/** The names the hook's line prints for it (a nation's roster name on the bundled competition). */
|
|
3422
|
+
readonly names: AmbientNames;
|
|
3274
3423
|
/**
|
|
3275
3424
|
* The hook's own line for this record, as `claudinho hook` prints it (the
|
|
3276
3425
|
* names, pinned to the World Cup's roster on the bundle, each with its flag
|
|
@@ -3279,17 +3428,40 @@ type AmbientMatch = Match & {
|
|
|
3279
3428
|
* never composes one.
|
|
3280
3429
|
*/
|
|
3281
3430
|
readonly line: string;
|
|
3431
|
+
/**
|
|
3432
|
+
* The goal that made the record's score, PROVEN by the refresher (its
|
|
3433
|
+
* scoring events counted per side equal the score: core's
|
|
3434
|
+
* `provenLastGoal`), stored on the cached record and re-sealed by the
|
|
3435
|
+
* reader with core's `sealEvent` and kept only as a goal, an own goal or a
|
|
3436
|
+
* penalty whose `teamCode` is the code of the side its `side` names, a side
|
|
3437
|
+
* that has scored (its regulation score at least 1); `null`
|
|
3438
|
+
* when nothing is proven or the stored value is anything else.
|
|
3439
|
+
*/
|
|
3440
|
+
readonly lastGoal: MatchEvent | null;
|
|
3282
3441
|
};
|
|
3283
3442
|
/** The ambient view of a selected competition, its keys in this order. */
|
|
3284
3443
|
interface AmbientView {
|
|
3285
3444
|
/**
|
|
3286
|
-
* The statusline's line
|
|
3287
|
-
*
|
|
3288
|
-
*
|
|
3289
|
-
*
|
|
3290
|
-
* the
|
|
3445
|
+
* The statusline's line rendered in `lang` (the same reader and the same
|
|
3446
|
+
* options as `claudinho prompt`, which prints it in English), fitted to
|
|
3447
|
+
* `--columns N` when given: whole matches are kept while they fit and
|
|
3448
|
+
* the rest counted in the `+N` (or `+more`) suffix, which is never cut
|
|
3449
|
+
* while the width holds it; below the suffix's own width
|
|
3450
|
+
* the line is the width's cut, never wider than asked. Its own tokens are in
|
|
3451
|
+
* `lang` (the countdown, the syncing phrase, the live segment's half-time
|
|
3452
|
+
* and live words); the overflow marker (`+N`, `+more`), the World Cup's
|
|
3453
|
+
* sign-off and nothing known (`⚽ —`) are English in every language, so
|
|
3454
|
+
* `idle` and `empty` say what they say in every language.
|
|
3291
3455
|
*/
|
|
3292
3456
|
readonly line: string;
|
|
3457
|
+
/**
|
|
3458
|
+
* The line as parts, built by the fitter as it places each shown score
|
|
3459
|
+
* token: their texts joined are `line` exactly, a score token the fit kept
|
|
3460
|
+
* whole is `strong`, one it cut is not. One plain part, the line itself, on
|
|
3461
|
+
* the countdown, the syncing line, nothing known and the sign-off, and
|
|
3462
|
+
* whenever the parts would not join to the line.
|
|
3463
|
+
*/
|
|
3464
|
+
readonly lineParts: readonly AmbientLinePart[];
|
|
3293
3465
|
/**
|
|
3294
3466
|
* The line is the one long-lived line, the edition's sign-off (the World
|
|
3295
3467
|
* Cup's, once every bundled window has closed): nothing changes until the
|
|
@@ -3322,6 +3494,18 @@ interface AmbientView {
|
|
|
3322
3494
|
* listed, best effort, as the line shows it, and is never current.
|
|
3323
3495
|
*/
|
|
3324
3496
|
readonly live: BoundedList<AmbientMatch>;
|
|
3497
|
+
/**
|
|
3498
|
+
* The matches the refresher SAW in play that a whole read then returned
|
|
3499
|
+
* finished (the snapshot's record of play), each for 10 minutes from the
|
|
3500
|
+
* read that first saw it finished, judged by the reader's clock (a stamp in
|
|
3501
|
+
* the future is not believed), whatever the snapshot's display window (the
|
|
3502
|
+
* refresher stops polling once the last match ends, so the snapshot ages
|
|
3503
|
+
* past it while an entry is still inside its own 10 minutes); never one the
|
|
3504
|
+
* live list holds. Listed only from a snapshot whose live read was whole
|
|
3505
|
+
* (`liveComplete: true`); otherwise `[]`, which then says nothing. At most
|
|
3506
|
+
* 64. Absence from it never means a match is over.
|
|
3507
|
+
*/
|
|
3508
|
+
readonly finished: readonly AmbientFinishedMatch[];
|
|
3325
3509
|
/**
|
|
3326
3510
|
* The live list is a believed snapshot's inside its display window (its
|
|
3327
3511
|
* stamp's age, by the reader's own rule, is finite and short of the window),
|
|
@@ -3337,7 +3521,9 @@ interface AmbientView {
|
|
|
3337
3521
|
/**
|
|
3338
3522
|
* The fixture the statusline's countdown names (its resolved pairing; the
|
|
3339
3523
|
* 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,
|
|
3524
|
+
* there is none to count down to. A fixture ahead, or a DUE one (still
|
|
3525
|
+
* scheduled, its kickoff passed by at most 30 minutes, not held by the
|
|
3526
|
+
* snapshot's record of play: the line reads "now"); never one in play.
|
|
3341
3527
|
*/
|
|
3342
3528
|
readonly next: Match | null;
|
|
3343
3529
|
/**
|
|
@@ -3357,6 +3543,19 @@ interface AmbientView {
|
|
|
3357
3543
|
readonly pickUnreadable?: true;
|
|
3358
3544
|
/** The selection, as every `--json` answer carries it (core `selectionExtras`). */
|
|
3359
3545
|
readonly competition: CompetitionKey;
|
|
3546
|
+
/**
|
|
3547
|
+
* The CLI's resolved language (`--lang`, `CLAUDINHO_LANG`, `LANG`'s language
|
|
3548
|
+
* part, each only when it is one of the four, else `en`), the language of
|
|
3549
|
+
* `line`, `lineParts` and `words`. `context`, `attribution`, `disclaimer`
|
|
3550
|
+
* and each record's `line` stay English in every language.
|
|
3551
|
+
*/
|
|
3552
|
+
readonly lang: Lang;
|
|
3553
|
+
/**
|
|
3554
|
+
* The plugin's moment words in `lang` (core's `moment.*` keys, `momentWords`):
|
|
3555
|
+
* `goalFor` and `win` templates holding `{team}` once, for the plugin to
|
|
3556
|
+
* fill with a record's own `names`.
|
|
3557
|
+
*/
|
|
3558
|
+
readonly words: MomentWords;
|
|
3360
3559
|
/** The snapshot says its last refresh failed (`false` with no snapshot). */
|
|
3361
3560
|
readonly degraded: boolean;
|
|
3362
3561
|
/** The provider the snapshot was read from, whatever its stamp; `null` with no snapshot. */
|
|
@@ -3374,6 +3573,20 @@ interface AmbientView {
|
|
|
3374
3573
|
readonly staleAfter: string | null;
|
|
3375
3574
|
/** The non-affiliation sentence, core's `DISCLAIMER`. */
|
|
3376
3575
|
readonly disclaimer: string;
|
|
3576
|
+
/**
|
|
3577
|
+
* The provider's attribution sentence, in English as every ambient string:
|
|
3578
|
+
* core's `live.data` copy with `liveSourceLabel(source)` (`Live data:
|
|
3579
|
+
* ESPN`), when the snapshot names a provider (`source` is a string) AND
|
|
3580
|
+
* the line is neither the sign-off (`idle`) nor nothing known (`empty`);
|
|
3581
|
+
* `null` otherwise: no snapshot, the sign-off (made from the bundled clock),
|
|
3582
|
+
* nothing known. `degraded` does not decide: a degraded snapshot still shows
|
|
3583
|
+
* a live row an earlier read served, or a countdown to a fixture discovery
|
|
3584
|
+
* read; nor does a read that was not whole. A limit: on the bundled
|
|
3585
|
+
* competition, a countdown made from the bundled skeleton (before its
|
|
3586
|
+
* knockout pairings resolve) would be attributed too; unreachable while that
|
|
3587
|
+
* edition is over.
|
|
3588
|
+
*/
|
|
3589
|
+
readonly attribution: string | null;
|
|
3377
3590
|
}
|
|
3378
3591
|
|
|
3379
3592
|
/** The two v1 snippet shapes. `social` is the rich card; `compact` is one terse line per match. */
|
|
@@ -4067,4 +4280,4 @@ declare function lookAtEntry(path: string): FileEntry;
|
|
|
4067
4280
|
*/
|
|
4068
4281
|
declare function lookAtOwnFile(path: string, maxBytes: number): OwnFile;
|
|
4069
4282
|
|
|
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 };
|
|
4283
|
+
export { type AmbientFinishedMatch, type AmbientLinePart, type AmbientMatch, type AmbientNames, 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_LIVE_FINISHED, 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, type MomentWords, 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 TimelineOptions, type TimelinePart, 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, momentWords, nationArg, nationToFlag, nationToRegion, nextFixtureForPin, nextFixtureForTeam, nextHorizonSentence, nextNoneReadSentence, nextShareCard, normalizeLang, normalizeOutcomes, outcomeFromScore, padVisible, parseCachedMarketSignal, parseCachedMatch, parseCachedMatches, parseCachedScheduleIndex, parseStandings, parseTeamSlot, parsedValue, pinUnder, productFlag, provenLastGoal, rallyCryFor, rallyEntryFor, readJsonBounded, readSmallFile, readUserConfig, requestClock, resolveClub, resolveCompetition, resolveMarketSource, resolveTz, resolvedValues, retryAfterMs, rosterFor, sanitizeBundledFixture, savedSlug, scheduleEntryOf, scheduleSpan, scoreline, sealEvent, sealMarketSignal, sealMatch, sealScheduleEntry, sealSeason, selectOne, selectedCompetition, selectionExtras, selectionRefusal, selectionVerdict, servedExtras, stageLabel, stageLabelI18n, standingsShapeOf, statesPartial, t, tableData, tableKeyArg, tableShareCard, tableTitle, takeBounded, teamKind, timelineParts, timelineRows, truncateVisible, unresolved, unservedSentence, valid, verdictExtras, verdictNotice, verdictQualifiers, withFlag };
|
package/dist/index.js
CHANGED
|
@@ -332,6 +332,13 @@ var EN = {
|
|
|
332
332
|
"status.fullTime": "full-time",
|
|
333
333
|
"status.postponed": "postponed",
|
|
334
334
|
"status.cancelled": "cancelled",
|
|
335
|
+
// A timeline row's kind word (core `timelineRows`).
|
|
336
|
+
"event.goal": "Goal",
|
|
337
|
+
"event.ownGoal": "Own goal",
|
|
338
|
+
"event.penalty": "Penalty",
|
|
339
|
+
"event.yellow": "Yellow card",
|
|
340
|
+
"event.red": "Red card",
|
|
341
|
+
"event.sub": "Substitution",
|
|
335
342
|
"live.title": "Live now",
|
|
336
343
|
"today.title": "Today's matches",
|
|
337
344
|
"today.on": "Matches",
|
|
@@ -340,6 +347,23 @@ var EN = {
|
|
|
340
347
|
"next.in": "in {countdown}",
|
|
341
348
|
// A kickoff that has passed, where the countdown would say "in" (core `countdownPhrase`).
|
|
342
349
|
"countdown.now": "now",
|
|
350
|
+
// The ambient line's syncing phrase (`claudinho ambient --json` alone; the
|
|
351
|
+
// statusline's own `prompt` stays English).
|
|
352
|
+
"ambient.syncing": "live \xB7 syncing\u2026",
|
|
353
|
+
// The plugin's moment words, sent by `claudinho ambient --json` as `words`
|
|
354
|
+
// (`{team}` a template the plugin fills with the record's own names). Each
|
|
355
|
+
// at most 40 characters; `{team}` once in `moment.goalFor` and `moment.win`
|
|
356
|
+
// and nowhere else, or the plugin falls back to its English words.
|
|
357
|
+
"moment.goal": "GOAL!",
|
|
358
|
+
"moment.ownGoal": "Own goal",
|
|
359
|
+
"moment.ownGoalDetail": "own goal",
|
|
360
|
+
"moment.goalFor": "Goal for {team}",
|
|
361
|
+
"moment.goals": "Goals",
|
|
362
|
+
"moment.scoreUpdated": "Score updated",
|
|
363
|
+
"moment.penalties": "Penalties",
|
|
364
|
+
"moment.fullTime": "Full time",
|
|
365
|
+
"moment.win": "{team} win",
|
|
366
|
+
"moment.draw": "Draw",
|
|
343
367
|
// A title that introduces the lines below it (the MCP text).
|
|
344
368
|
heading: "{title}:",
|
|
345
369
|
// A list's empty state and the outage lines: the CLI's sentences, the MCP
|
|
@@ -351,6 +375,7 @@ var EN = {
|
|
|
351
375
|
"feed.earlierRecord": "Live state could not be refreshed \u2014 showing the provider's earlier record.",
|
|
352
376
|
"next.none": "No upcoming fixture found for {team}.",
|
|
353
377
|
"match.none": "No match found with id {id}.",
|
|
378
|
+
"match.kickoff": "Kickoff",
|
|
354
379
|
// The MCP text's own sentences for these states (the CLI says `live.degraded`
|
|
355
380
|
// there): they keep the team or the id the answer is about.
|
|
356
381
|
"next.unreachable": "Couldn't reach the data provider \u2014 no upcoming fixture confirmed for {team}.",
|
|
@@ -428,6 +453,12 @@ var ES = {
|
|
|
428
453
|
"status.fullTime": "finalizado",
|
|
429
454
|
"status.postponed": "aplazado",
|
|
430
455
|
"status.cancelled": "cancelado",
|
|
456
|
+
"event.goal": "Gol",
|
|
457
|
+
"event.ownGoal": "Gol en contra",
|
|
458
|
+
"event.penalty": "Penal",
|
|
459
|
+
"event.yellow": "Tarjeta amarilla",
|
|
460
|
+
"event.red": "Tarjeta roja",
|
|
461
|
+
"event.sub": "Cambio",
|
|
431
462
|
"live.title": "En vivo",
|
|
432
463
|
"today.title": "Partidos de hoy",
|
|
433
464
|
"today.on": "Partidos",
|
|
@@ -435,6 +466,17 @@ var ES = {
|
|
|
435
466
|
"next.label": "Pr\xF3ximo partido de {team}",
|
|
436
467
|
"next.in": "en {countdown}",
|
|
437
468
|
"countdown.now": "ahora",
|
|
469
|
+
"ambient.syncing": "en vivo \xB7 sincronizando\u2026",
|
|
470
|
+
"moment.goal": "\xA1GOL!",
|
|
471
|
+
"moment.ownGoal": "Gol en contra",
|
|
472
|
+
"moment.ownGoalDetail": "gol en contra",
|
|
473
|
+
"moment.goalFor": "Gol de {team}",
|
|
474
|
+
"moment.goals": "Goles",
|
|
475
|
+
"moment.scoreUpdated": "Marcador corregido",
|
|
476
|
+
"moment.penalties": "Penales",
|
|
477
|
+
"moment.fullTime": "Final",
|
|
478
|
+
"moment.win": "Gana {team}",
|
|
479
|
+
"moment.draw": "Empate",
|
|
438
480
|
heading: "{title}:",
|
|
439
481
|
"today.none": "No hay partidos para esta fecha.",
|
|
440
482
|
"live.none": "No hay partidos en juego ahora mismo.",
|
|
@@ -443,6 +485,7 @@ var ES = {
|
|
|
443
485
|
"feed.earlierRecord": "No se pudo actualizar el estado en vivo \u2014 se muestra el registro anterior del proveedor.",
|
|
444
486
|
"next.none": "No se encontr\xF3 pr\xF3ximo partido para {team}.",
|
|
445
487
|
"match.none": "No se encontr\xF3 partido con id {id}.",
|
|
488
|
+
"match.kickoff": "Inicio",
|
|
446
489
|
"next.unreachable": "No se pudo contactar al proveedor de datos \u2014 ning\xFAn pr\xF3ximo partido confirmado para {team}.",
|
|
447
490
|
"match.unreachable": "No se pudo contactar al proveedor de datos \u2014 no se pudo consultar el partido {id}.",
|
|
448
491
|
"team.ambiguous": '"{query}" es ambiguo. \xBFQuisiste decir:',
|
|
@@ -517,6 +560,12 @@ var PT = {
|
|
|
517
560
|
"status.fullTime": "encerrado",
|
|
518
561
|
"status.postponed": "adiado",
|
|
519
562
|
"status.cancelled": "cancelado",
|
|
563
|
+
"event.goal": "Gol",
|
|
564
|
+
"event.ownGoal": "Gol contra",
|
|
565
|
+
"event.penalty": "P\xEAnalti",
|
|
566
|
+
"event.yellow": "Cart\xE3o amarelo",
|
|
567
|
+
"event.red": "Cart\xE3o vermelho",
|
|
568
|
+
"event.sub": "Substitui\xE7\xE3o",
|
|
520
569
|
"live.title": "Ao vivo",
|
|
521
570
|
"today.title": "Jogos de hoje",
|
|
522
571
|
"today.on": "Jogos",
|
|
@@ -524,6 +573,17 @@ var PT = {
|
|
|
524
573
|
"next.label": "Pr\xF3ximo jogo de {team}",
|
|
525
574
|
"next.in": "em {countdown}",
|
|
526
575
|
"countdown.now": "agora",
|
|
576
|
+
"ambient.syncing": "ao vivo \xB7 sincronizando\u2026",
|
|
577
|
+
"moment.goal": "GOL!",
|
|
578
|
+
"moment.ownGoal": "Gol contra",
|
|
579
|
+
"moment.ownGoalDetail": "gol contra",
|
|
580
|
+
"moment.goalFor": "Gol \xB7 {team}",
|
|
581
|
+
"moment.goals": "Gols",
|
|
582
|
+
"moment.scoreUpdated": "Placar corrigido",
|
|
583
|
+
"moment.penalties": "P\xEAnaltis",
|
|
584
|
+
"moment.fullTime": "Fim de jogo",
|
|
585
|
+
"moment.win": "{team} vence",
|
|
586
|
+
"moment.draw": "Empate",
|
|
527
587
|
heading: "{title}:",
|
|
528
588
|
"today.none": "Nenhum jogo para esta data.",
|
|
529
589
|
"live.none": "Nenhum jogo em andamento agora.",
|
|
@@ -532,6 +592,7 @@ var PT = {
|
|
|
532
592
|
"feed.earlierRecord": "N\xE3o foi poss\xEDvel atualizar o estado ao vivo \u2014 mostrando o registro anterior do provedor.",
|
|
533
593
|
"next.none": "Nenhum pr\xF3ximo jogo encontrado para {team}.",
|
|
534
594
|
"match.none": "Nenhum jogo encontrado com id {id}.",
|
|
595
|
+
"match.kickoff": "In\xEDcio",
|
|
535
596
|
"next.unreachable": "N\xE3o foi poss\xEDvel contatar o provedor de dados \u2014 nenhum pr\xF3ximo jogo confirmado para {team}.",
|
|
536
597
|
"match.unreachable": "N\xE3o foi poss\xEDvel contatar o provedor de dados \u2014 o jogo {id} n\xE3o p\xF4de ser consultado.",
|
|
537
598
|
"team.ambiguous": '"{query}" \xE9 amb\xEDguo. Voc\xEA quis dizer:',
|
|
@@ -606,6 +667,12 @@ var FR = {
|
|
|
606
667
|
"status.fullTime": "termin\xE9",
|
|
607
668
|
"status.postponed": "report\xE9",
|
|
608
669
|
"status.cancelled": "annul\xE9",
|
|
670
|
+
"event.goal": "But",
|
|
671
|
+
"event.ownGoal": "But contre son camp",
|
|
672
|
+
"event.penalty": "Penalty",
|
|
673
|
+
"event.yellow": "Carton jaune",
|
|
674
|
+
"event.red": "Carton rouge",
|
|
675
|
+
"event.sub": "Remplacement",
|
|
609
676
|
"live.title": "En direct",
|
|
610
677
|
"today.title": "Matchs d'aujourd'hui",
|
|
611
678
|
"today.on": "Matchs",
|
|
@@ -613,6 +680,17 @@ var FR = {
|
|
|
613
680
|
"next.label": "Prochain match de {team}",
|
|
614
681
|
"next.in": "dans {countdown}",
|
|
615
682
|
"countdown.now": "maintenant",
|
|
683
|
+
"ambient.syncing": "en direct \xB7 synchronisation\u2026",
|
|
684
|
+
"moment.goal": "BUT !",
|
|
685
|
+
"moment.ownGoal": "But contre son camp",
|
|
686
|
+
"moment.ownGoalDetail": "contre son camp",
|
|
687
|
+
"moment.goalFor": "But pour {team}",
|
|
688
|
+
"moment.goals": "Buts",
|
|
689
|
+
"moment.scoreUpdated": "Score corrig\xE9",
|
|
690
|
+
"moment.penalties": "Tirs au but",
|
|
691
|
+
"moment.fullTime": "Fin du match",
|
|
692
|
+
"moment.win": "{team} gagne",
|
|
693
|
+
"moment.draw": "Match nul",
|
|
616
694
|
heading: "{title} :",
|
|
617
695
|
"today.none": "Aucun match pr\xE9vu pour cette date.",
|
|
618
696
|
"live.none": "Aucun match en cours pour l'instant.",
|
|
@@ -621,6 +699,7 @@ var FR = {
|
|
|
621
699
|
"feed.earlierRecord": "L\u2019\xE9tat en direct n\u2019a pas pu \xEAtre actualis\xE9 \u2014 affichage de l\u2019enregistrement pr\xE9c\xE9dent du fournisseur.",
|
|
622
700
|
"next.none": "Aucun prochain match trouv\xE9 pour {team}.",
|
|
623
701
|
"match.none": "Aucun match trouv\xE9 avec id {id}.",
|
|
702
|
+
"match.kickoff": "Coup d\u2019envoi",
|
|
624
703
|
"next.unreachable": "Impossible de joindre le fournisseur de donn\xE9es \u2014 aucun prochain match confirm\xE9 pour {team}.",
|
|
625
704
|
"match.unreachable": "Impossible de joindre le fournisseur de donn\xE9es \u2014 le match {id} n\u2019a pas pu \xEAtre consult\xE9.",
|
|
626
705
|
"team.ambiguous": '"{query}" est ambigu. Vouliez-vous dire :',
|
|
@@ -654,6 +733,20 @@ function t(lang, key, vars) {
|
|
|
654
733
|
(whole, name) => Object.hasOwn(vars, name) ? vars[name] : whole
|
|
655
734
|
);
|
|
656
735
|
}
|
|
736
|
+
function momentWords(lang) {
|
|
737
|
+
return {
|
|
738
|
+
goal: t(lang, "moment.goal"),
|
|
739
|
+
ownGoal: t(lang, "moment.ownGoal"),
|
|
740
|
+
ownGoalDetail: t(lang, "moment.ownGoalDetail"),
|
|
741
|
+
goalFor: t(lang, "moment.goalFor"),
|
|
742
|
+
goals: t(lang, "moment.goals"),
|
|
743
|
+
scoreUpdated: t(lang, "moment.scoreUpdated"),
|
|
744
|
+
penalties: t(lang, "moment.penalties"),
|
|
745
|
+
fullTime: t(lang, "moment.fullTime"),
|
|
746
|
+
win: t(lang, "moment.win"),
|
|
747
|
+
draw: t(lang, "moment.draw")
|
|
748
|
+
};
|
|
749
|
+
}
|
|
657
750
|
var STAGE_KEYS = Object.freeze({
|
|
658
751
|
GROUP: "stage.groupStage",
|
|
659
752
|
R32: "stage.r32",
|
|
@@ -842,6 +935,70 @@ function withFlag(text, flag2, side) {
|
|
|
842
935
|
return side === "home" ? `${flag2} ${text}` : `${text} ${flag2}`;
|
|
843
936
|
}
|
|
844
937
|
|
|
938
|
+
// src/timeline.ts
|
|
939
|
+
var KIND_KEYS = Object.freeze({
|
|
940
|
+
GOAL: "event.goal",
|
|
941
|
+
OWN_GOAL: "event.ownGoal",
|
|
942
|
+
PEN: "event.penalty",
|
|
943
|
+
YELLOW: "event.yellow",
|
|
944
|
+
RED: "event.red",
|
|
945
|
+
SUB: "event.sub"
|
|
946
|
+
});
|
|
947
|
+
var KIND_MARKS = Object.freeze({
|
|
948
|
+
GOAL: "\u26BD",
|
|
949
|
+
OWN_GOAL: "\u26BD",
|
|
950
|
+
PEN: "\u26BD",
|
|
951
|
+
YELLOW: "\u{1F7E8}",
|
|
952
|
+
RED: "\u{1F7E5}",
|
|
953
|
+
SUB: "\u{1F504}"
|
|
954
|
+
});
|
|
955
|
+
function timelineParts(match, opts) {
|
|
956
|
+
const events = match.events ?? [];
|
|
957
|
+
if (events.length === 0) return [];
|
|
958
|
+
const cells = events.map((e) => {
|
|
959
|
+
const team = e.side === "home" ? match.home : match.away;
|
|
960
|
+
return {
|
|
961
|
+
minute: e.added !== void 0 ? `${e.minute}+${e.added}'` : `${e.minute}'`,
|
|
962
|
+
mark: KIND_MARKS[e.type],
|
|
963
|
+
kind: t(opts.lang, KIND_KEYS[e.type]),
|
|
964
|
+
label: withFlag(team.code, opts.flags ? team.flag : void 0, e.side),
|
|
965
|
+
player: e.player
|
|
966
|
+
};
|
|
967
|
+
});
|
|
968
|
+
const widest = (pick2) => Math.max(...cells.map((c) => displayWidth(pick2(c))));
|
|
969
|
+
const minuteW = widest((c) => c.minute);
|
|
970
|
+
const kindW = widest((c) => c.kind);
|
|
971
|
+
const labelW = widest((c) => c.label);
|
|
972
|
+
return cells.map((c) => {
|
|
973
|
+
const columns = ` ${padVisible(c.minute, minuteW)} ${c.mark} ${padVisible(c.kind, kindW)} ${padVisible(c.label, labelW)}`;
|
|
974
|
+
if (!c.player) return [{ text: columns.trimEnd() }];
|
|
975
|
+
return [{ text: `${columns} ` }, { text: c.player, style: "strong" }];
|
|
976
|
+
});
|
|
977
|
+
}
|
|
978
|
+
function timelineRows(match, opts) {
|
|
979
|
+
return timelineParts(match, opts).map((row) => row.map((p) => p.text).join(""));
|
|
980
|
+
}
|
|
981
|
+
var SCORING = /* @__PURE__ */ new Set(["GOAL", "OWN_GOAL", "PEN"]);
|
|
982
|
+
function provenLastGoal(match) {
|
|
983
|
+
const score = match.score;
|
|
984
|
+
if (!score) return null;
|
|
985
|
+
let home = 0;
|
|
986
|
+
let away = 0;
|
|
987
|
+
let last = null;
|
|
988
|
+
const identities = /* @__PURE__ */ new Set();
|
|
989
|
+
for (const e of match.events ?? []) {
|
|
990
|
+
if (!SCORING.has(e.type)) continue;
|
|
991
|
+
const identity = JSON.stringify([e.type, e.minute, e.added ?? 0, e.side, e.player ?? null]);
|
|
992
|
+
if (identities.has(identity)) return null;
|
|
993
|
+
identities.add(identity);
|
|
994
|
+
if (e.side === "home") home++;
|
|
995
|
+
else if (e.side === "away") away++;
|
|
996
|
+
else continue;
|
|
997
|
+
last = e;
|
|
998
|
+
}
|
|
999
|
+
return last !== null && home === score.home && away === score.away ? last : null;
|
|
1000
|
+
}
|
|
1001
|
+
|
|
845
1002
|
// src/normalize.ts
|
|
846
1003
|
function outcomeFromScore(home, away) {
|
|
847
1004
|
if (home > away) return "H";
|
|
@@ -1132,18 +1289,33 @@ function sealScorePair(raw) {
|
|
|
1132
1289
|
const away = count(v.away, MAX_GOALS);
|
|
1133
1290
|
return home !== void 0 && away !== void 0 ? { home, away } : void 0;
|
|
1134
1291
|
}
|
|
1292
|
+
var MAX_ADDED_MINUTES = 30;
|
|
1293
|
+
var SIDES = /* @__PURE__ */ new Set(["home", "away"]);
|
|
1135
1294
|
function sealEvent(raw) {
|
|
1136
|
-
if (!raw || typeof raw !== "object") return
|
|
1295
|
+
if (!raw || typeof raw !== "object") return null;
|
|
1137
1296
|
const e = raw;
|
|
1138
1297
|
const type = member(e.type, EVENT_TYPES);
|
|
1139
|
-
if (!type) return
|
|
1298
|
+
if (!type) return null;
|
|
1140
1299
|
const minute = count(e.minute, MAX_MINUTE);
|
|
1141
|
-
if (minute === void 0) return
|
|
1142
|
-
|
|
1300
|
+
if (minute === void 0) return null;
|
|
1301
|
+
let added;
|
|
1302
|
+
if (e.added !== void 0) {
|
|
1303
|
+
added = count(e.added, MAX_ADDED_MINUTES);
|
|
1304
|
+
if (added === void 0 || added < 1) return null;
|
|
1305
|
+
}
|
|
1306
|
+
const side = member(e.side, SIDES);
|
|
1307
|
+
if (!side) return null;
|
|
1308
|
+
const out = { type, minute };
|
|
1309
|
+
if (added !== void 0) out.added = added;
|
|
1310
|
+
out.side = side;
|
|
1311
|
+
out.teamCode = humanLabel(e.teamCode, TEAM_CODE_COLUMNS);
|
|
1143
1312
|
const player = humanLabel(e.player);
|
|
1144
1313
|
if (player) out.player = player;
|
|
1145
1314
|
return out;
|
|
1146
1315
|
}
|
|
1316
|
+
function byClock(a, b) {
|
|
1317
|
+
return a.minute - b.minute || (a.added ?? 0) - (b.added ?? 0);
|
|
1318
|
+
}
|
|
1147
1319
|
function sealMatch(parts, opts = {}) {
|
|
1148
1320
|
if (!parts || typeof parts !== "object") return malformed("match is not an object");
|
|
1149
1321
|
const id = opaqueId(parts.id, ESPN_ID);
|
|
@@ -1188,7 +1360,8 @@ function sealMatch(parts, opts = {}) {
|
|
|
1188
1360
|
winnerCode = claimedWinner;
|
|
1189
1361
|
}
|
|
1190
1362
|
}
|
|
1191
|
-
const
|
|
1363
|
+
const timelined = status === "LIVE" || status === "HT" || status === "FT";
|
|
1364
|
+
const events = opts.events === false || !timelined ? [] : takeBounded(parts.events, MAX_MATCH_EVENTS).map(sealEvent).filter((e) => !!e).sort(byClock);
|
|
1192
1365
|
const out = { id, stage };
|
|
1193
1366
|
if (stageLabel2) out.stageLabel = stageLabel2;
|
|
1194
1367
|
if (group) out.group = group;
|
|
@@ -4446,6 +4619,75 @@ function parseMinute(st) {
|
|
|
4446
4619
|
}
|
|
4447
4620
|
return void 0;
|
|
4448
4621
|
}
|
|
4622
|
+
var DETAIL_FLAGS = ["scoringPlay", "yellowCard", "redCard", "penaltyKick", "ownGoal", "shootout"];
|
|
4623
|
+
var DETAIL_MINUTE = /^(\d{1,3})'$/;
|
|
4624
|
+
var DETAIL_MINUTE_ADDED = /^(\d{1,3})'\+(\d{1,2})'$/;
|
|
4625
|
+
function detailKind(d) {
|
|
4626
|
+
const f = {};
|
|
4627
|
+
for (const name of DETAIL_FLAGS) {
|
|
4628
|
+
const v = flag(d[name]);
|
|
4629
|
+
if (v === void 0) return void 0;
|
|
4630
|
+
f[name] = v;
|
|
4631
|
+
}
|
|
4632
|
+
if (f.shootout) return void 0;
|
|
4633
|
+
const { scoringPlay: s, yellowCard: y, redCard: r, penaltyKick: p, ownGoal: o } = f;
|
|
4634
|
+
if (s && !y && !r) {
|
|
4635
|
+
if (!p && !o) return "GOAL";
|
|
4636
|
+
if (o && !p) return "OWN_GOAL";
|
|
4637
|
+
if (p && !o) return "PEN";
|
|
4638
|
+
return void 0;
|
|
4639
|
+
}
|
|
4640
|
+
if (p || o || s) return void 0;
|
|
4641
|
+
if (y && !r) return "YELLOW";
|
|
4642
|
+
if (r && !y) return "RED";
|
|
4643
|
+
return void 0;
|
|
4644
|
+
}
|
|
4645
|
+
function detailMinute(clock) {
|
|
4646
|
+
if (!clock || typeof clock !== "object") return void 0;
|
|
4647
|
+
const dv = clock.displayValue;
|
|
4648
|
+
if (typeof dv !== "string") return void 0;
|
|
4649
|
+
const plain = DETAIL_MINUTE.exec(dv);
|
|
4650
|
+
if (plain) return { minute: Number(plain[1]) };
|
|
4651
|
+
const extra = DETAIL_MINUTE_ADDED.exec(dv);
|
|
4652
|
+
if (!extra) return void 0;
|
|
4653
|
+
return { minute: Number(extra[1]), added: Number(extra[2]) };
|
|
4654
|
+
}
|
|
4655
|
+
function rawTeamId(holder) {
|
|
4656
|
+
if (!holder || typeof holder !== "object") return void 0;
|
|
4657
|
+
const team = holder.team;
|
|
4658
|
+
if (!team || typeof team !== "object") return void 0;
|
|
4659
|
+
return opaqueId(team.id, ESPN_ID);
|
|
4660
|
+
}
|
|
4661
|
+
function detailEvents(details, sides) {
|
|
4662
|
+
const out = [];
|
|
4663
|
+
for (const raw of takeBounded(details, MAX_MATCH_EVENTS)) {
|
|
4664
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) continue;
|
|
4665
|
+
const d = raw;
|
|
4666
|
+
const type = detailKind(d);
|
|
4667
|
+
if (!type) continue;
|
|
4668
|
+
const when = detailMinute(d.clock);
|
|
4669
|
+
if (!when) continue;
|
|
4670
|
+
const teamId = rawTeamId(d);
|
|
4671
|
+
const side = teamId === void 0 ? void 0 : teamId === sides.homeId ? "home" : teamId === sides.awayId ? "away" : void 0;
|
|
4672
|
+
if (!side) continue;
|
|
4673
|
+
const event = { type, minute: when.minute };
|
|
4674
|
+
if (when.added !== void 0) event.added = when.added;
|
|
4675
|
+
event.side = side;
|
|
4676
|
+
event.teamCode = side === "home" ? sides.homeCode : sides.awayCode;
|
|
4677
|
+
const player = detailPlayer(d.athletesInvolved, type === "OWN_GOAL" ? side === "home" ? sides.awayId : sides.homeId : teamId);
|
|
4678
|
+
if (player) event.player = player;
|
|
4679
|
+
out.push(event);
|
|
4680
|
+
}
|
|
4681
|
+
return out;
|
|
4682
|
+
}
|
|
4683
|
+
function detailPlayer(athletes, expectedTeamId) {
|
|
4684
|
+
if (!Array.isArray(athletes) || athletes.length !== 1 || expectedTeamId === void 0) return void 0;
|
|
4685
|
+
const a = athletes[0];
|
|
4686
|
+
if (rawTeamId(a) !== expectedTeamId) return void 0;
|
|
4687
|
+
const name = a.displayName;
|
|
4688
|
+
if (!isHumanLabel(name)) return void 0;
|
|
4689
|
+
return humanLabel(name) || void 0;
|
|
4690
|
+
}
|
|
4449
4691
|
var MAX_SLUG_UNITS2 = 64;
|
|
4450
4692
|
var SLUG = /^[a-z0-9-]+$/;
|
|
4451
4693
|
var EXACT_STAGE = Object.freeze({
|
|
@@ -4567,7 +4809,14 @@ function parseEspnEvent(raw, ctx = {}) {
|
|
|
4567
4809
|
status,
|
|
4568
4810
|
winnerCode,
|
|
4569
4811
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
4570
|
-
|
|
4812
|
+
// The goals and cards: ESPN's `details`, read by the detail grammar; the
|
|
4813
|
+
// seal keeps them only on a status that holds a score.
|
|
4814
|
+
events: detailEvents(comp?.details, {
|
|
4815
|
+
homeId: rawTeamId(homeRaw),
|
|
4816
|
+
awayId: rawTeamId(awayRaw),
|
|
4817
|
+
homeCode: home.code,
|
|
4818
|
+
awayCode: away.code
|
|
4819
|
+
})
|
|
4571
4820
|
},
|
|
4572
4821
|
// The final reseal states the same kind the competitors were built with.
|
|
4573
4822
|
{ teamKind: kind }
|
|
@@ -4890,7 +5139,7 @@ function fetchMeta(result) {
|
|
|
4890
5139
|
// src/adapters/espn.ts
|
|
4891
5140
|
var ESPN_SOCCER = "https://site.api.espn.com/apis/site/v2/sports/soccer";
|
|
4892
5141
|
var DEFAULT_COMPETITION = BUNDLED_SLUG;
|
|
4893
|
-
var USER_AGENT = `claudinho/${"0.
|
|
5142
|
+
var USER_AGENT = `claudinho/${"0.12.0"} (+https://github.com/arturogarrido/claudinho)`;
|
|
4894
5143
|
var MAX_RESPONSE_BYTES = 5 * 1024 * 1024;
|
|
4895
5144
|
var DEFAULT_COOLDOWN_MS = 5 * 6e4;
|
|
4896
5145
|
var MAX_COOLDOWN_MS = 15 * 6e4;
|
|
@@ -7016,10 +7265,19 @@ async function getScheduleAhead(adapter, now = /* @__PURE__ */ new Date()) {
|
|
|
7016
7265
|
};
|
|
7017
7266
|
}
|
|
7018
7267
|
async function getLiveMatches(adapter, now = /* @__PURE__ */ new Date()) {
|
|
7019
|
-
const { complete: _complete, ...result } = await getLiveRead(adapter, now);
|
|
7268
|
+
const { complete: _complete, finished: _finished, finishedTruncated: _truncated, ...result } = await getLiveRead(adapter, now);
|
|
7020
7269
|
return result;
|
|
7021
7270
|
}
|
|
7022
|
-
|
|
7271
|
+
var MAX_LIVE_FINISHED = 64;
|
|
7272
|
+
function finishedOf(records, seen) {
|
|
7273
|
+
const wanted = new Set(takeBounded(seen, MAX_LIVE_FINISHED));
|
|
7274
|
+
const done = records.filter((m) => m.status === "FT" && m.score !== void 0);
|
|
7275
|
+
const first = done.filter((m) => wanted.has(m.id));
|
|
7276
|
+
const rest = done.filter((m) => !wanted.has(m.id));
|
|
7277
|
+
const all = [...first, ...rest];
|
|
7278
|
+
return { finished: all.slice(0, MAX_LIVE_FINISHED), truncated: all.length > MAX_LIVE_FINISHED };
|
|
7279
|
+
}
|
|
7280
|
+
async function getLiveRead(adapter, now = /* @__PURE__ */ new Date(), seen = []) {
|
|
7023
7281
|
try {
|
|
7024
7282
|
const day = now.toISOString().slice(0, 10);
|
|
7025
7283
|
const fetched = adapter.fetchWindow ? await adapter.fetchWindow(shiftUtcDate(day, -1), shiftUtcDate(day, 1), { acrossSeasons: true }) : await adapter.fetchLive();
|
|
@@ -7027,6 +7285,7 @@ async function getLiveRead(adapter, now = /* @__PURE__ */ new Date()) {
|
|
|
7027
7285
|
const season = meta?.season;
|
|
7028
7286
|
const matches = fetched.filter((m) => isLive(m.status));
|
|
7029
7287
|
const between = betweenEditionsOf(adapter, meta, fetched, providerDayOf(adapter, now));
|
|
7288
|
+
const done = finishedOf(fetched, seen);
|
|
7030
7289
|
return {
|
|
7031
7290
|
matches,
|
|
7032
7291
|
degraded: false,
|
|
@@ -7038,10 +7297,12 @@ async function getLiveRead(adapter, now = /* @__PURE__ */ new Date()) {
|
|
|
7038
7297
|
// boolean below is the refresher's, unchanged: false for that, for an
|
|
7039
7298
|
// adapter that said nothing, and for a failed read alike.
|
|
7040
7299
|
...partialOfRead(meta),
|
|
7041
|
-
complete: meta?.complete === true
|
|
7300
|
+
complete: meta?.complete === true,
|
|
7301
|
+
finished: done.finished,
|
|
7302
|
+
finishedTruncated: done.truncated
|
|
7042
7303
|
};
|
|
7043
7304
|
} catch {
|
|
7044
|
-
return { matches: [], degraded: true, complete: false };
|
|
7305
|
+
return { matches: [], degraded: true, complete: false, finished: [], finishedTruncated: false };
|
|
7045
7306
|
}
|
|
7046
7307
|
}
|
|
7047
7308
|
|
|
@@ -7291,15 +7552,22 @@ function sealMarketSignal(raw, options = {}) {
|
|
|
7291
7552
|
if (sourceMarketId) out.sourceMarketId = sourceMarketId;
|
|
7292
7553
|
out.asOf = canonicalTimestamp(s.asOf) ?? "";
|
|
7293
7554
|
out.fetchedAt = canonicalTimestamp(s.fetchedAt) ?? "";
|
|
7555
|
+
const judgedAt = options.now ?? /* @__PURE__ */ new Date();
|
|
7556
|
+
const fetchedMs = Date.parse(out.fetchedAt);
|
|
7557
|
+
if (out.fetchedAt === "" || !Number.isFinite(fetchedMs)) {
|
|
7558
|
+
return malformed("signal carries no readable fetch time");
|
|
7559
|
+
}
|
|
7560
|
+
if (fetchedMs > judgedAt.getTime()) {
|
|
7561
|
+
return unresolved("signal was fetched after the clock it is judged by");
|
|
7562
|
+
}
|
|
7294
7563
|
out.outcomes = outcomes;
|
|
7295
7564
|
const isAmbiguous = s.ambiguous !== false;
|
|
7296
7565
|
const favorite = isAmbiguous ? void 0 : deriveFavorite(outcomes);
|
|
7297
7566
|
if (favorite) out.favorite = favorite;
|
|
7298
7567
|
if (liquidity !== void 0) out.liquidity = liquidity;
|
|
7299
7568
|
if (volume24h !== void 0) out.volume24h = volume24h;
|
|
7300
|
-
out.stale =
|
|
7569
|
+
out.stale = isStaleSignal(out, { now: judgedAt, maxAgeMs: options.maxAgeMs });
|
|
7301
7570
|
out.ambiguous = isAmbiguous || out.source === "";
|
|
7302
|
-
out.stale = out.stale || isStaleSignal(out, { now: options.now, maxAgeMs: options.maxAgeMs });
|
|
7303
7571
|
return valid(out);
|
|
7304
7572
|
}
|
|
7305
7573
|
function parseCachedMarketSignal(raw, options = {}) {
|
|
@@ -7308,7 +7576,7 @@ function parseCachedMarketSignal(raw, options = {}) {
|
|
|
7308
7576
|
|
|
7309
7577
|
// src/markets/normalize.ts
|
|
7310
7578
|
var DEFAULT_MAX_AGE_MS = 15 * 6e4;
|
|
7311
|
-
function marketRelevant(match, now
|
|
7579
|
+
function marketRelevant(match, now) {
|
|
7312
7580
|
if (isLive(match.status)) return true;
|
|
7313
7581
|
if (isFinished(match.status)) return false;
|
|
7314
7582
|
const k = Date.parse(match.kickoff);
|
|
@@ -7397,12 +7665,13 @@ function buildMarketSignal(input) {
|
|
|
7397
7665
|
const outcomes = normalizeOutcomes(input.outcomes);
|
|
7398
7666
|
const ambiguous2 = input.ambiguous === true || !mapsCleanly(input.match, outcomes);
|
|
7399
7667
|
const favorite = ambiguous2 ? void 0 : deriveFavorite(outcomes);
|
|
7668
|
+
const clock = input.now ?? /* @__PURE__ */ new Date();
|
|
7400
7669
|
const signal = {
|
|
7401
7670
|
matchId: input.match.id,
|
|
7402
7671
|
source: input.source,
|
|
7403
7672
|
sourceMarketId: input.sourceMarketId,
|
|
7404
7673
|
asOf: input.asOf,
|
|
7405
|
-
fetchedAt: input.fetchedAt ??
|
|
7674
|
+
fetchedAt: input.fetchedAt ?? clock.toISOString(),
|
|
7406
7675
|
outcomes,
|
|
7407
7676
|
favorite,
|
|
7408
7677
|
liquidity: input.liquidity,
|
|
@@ -7410,8 +7679,8 @@ function buildMarketSignal(input) {
|
|
|
7410
7679
|
stale: false,
|
|
7411
7680
|
ambiguous: ambiguous2
|
|
7412
7681
|
};
|
|
7413
|
-
signal.stale = isStaleSignal(signal, { now:
|
|
7414
|
-
const sealed = sealMarketSignal(signal, { now:
|
|
7682
|
+
signal.stale = isStaleSignal(signal, { now: clock, maxAgeMs: input.maxAgeMs });
|
|
7683
|
+
const sealed = sealMarketSignal(signal, { now: clock, maxAgeMs: input.maxAgeMs });
|
|
7415
7684
|
if (sealed.kind !== "valid") {
|
|
7416
7685
|
return { ...signal, outcomes: [], favorite: void 0, stale: true, ambiguous: true };
|
|
7417
7686
|
}
|
|
@@ -8687,6 +8956,7 @@ export {
|
|
|
8687
8956
|
MAX_CONFIG_BYTES,
|
|
8688
8957
|
MAX_COOLDOWN_MS,
|
|
8689
8958
|
MAX_LABEL_COLUMNS,
|
|
8959
|
+
MAX_LIVE_FINISHED,
|
|
8690
8960
|
MAX_RESPONSE_BYTES,
|
|
8691
8961
|
MAX_SCHEDULE_INDEX,
|
|
8692
8962
|
NO_BRACKET,
|
|
@@ -8836,6 +9106,7 @@ export {
|
|
|
8836
9106
|
matchWindowSentence,
|
|
8837
9107
|
mergeLive,
|
|
8838
9108
|
modeLine,
|
|
9109
|
+
momentWords,
|
|
8839
9110
|
nationArg,
|
|
8840
9111
|
nationToFlag,
|
|
8841
9112
|
nationToRegion,
|
|
@@ -8857,6 +9128,7 @@ export {
|
|
|
8857
9128
|
parsedValue,
|
|
8858
9129
|
pinUnder,
|
|
8859
9130
|
productFlag,
|
|
9131
|
+
provenLastGoal,
|
|
8860
9132
|
rallyCryFor,
|
|
8861
9133
|
rallyEntryFor,
|
|
8862
9134
|
readJsonBounded,
|
|
@@ -8875,6 +9147,7 @@ export {
|
|
|
8875
9147
|
scheduleEntryOf,
|
|
8876
9148
|
scheduleSpan,
|
|
8877
9149
|
scoreline,
|
|
9150
|
+
sealEvent,
|
|
8878
9151
|
sealMarketSignal,
|
|
8879
9152
|
sealMatch,
|
|
8880
9153
|
sealScheduleEntry,
|
|
@@ -8894,7 +9167,10 @@ export {
|
|
|
8894
9167
|
tableKeyArg,
|
|
8895
9168
|
tableShareCard,
|
|
8896
9169
|
tableTitle,
|
|
9170
|
+
takeBounded,
|
|
8897
9171
|
teamKind,
|
|
9172
|
+
timelineParts,
|
|
9173
|
+
timelineRows,
|
|
8898
9174
|
truncateVisible,
|
|
8899
9175
|
unresolved,
|
|
8900
9176
|
unservedSentence,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@claudinho/core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.12.0",
|
|
4
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",
|
|
@@ -67,7 +67,7 @@
|
|
|
67
67
|
"club-world-cup"
|
|
68
68
|
],
|
|
69
69
|
"devDependencies": {
|
|
70
|
-
"@types/node": "^22.20.
|
|
70
|
+
"@types/node": "^22.20.5",
|
|
71
71
|
"@vitest/coverage-v8": "^5.0.3",
|
|
72
72
|
"tsup": "^8.0.0",
|
|
73
73
|
"tsx": "^4.23.15",
|