@claudinho/core 0.8.17 → 0.8.19
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 +73 -1
- package/dist/index.js +151 -6
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -183,6 +183,54 @@ declare function isValidTimeZone(tz: string | undefined | null): boolean;
|
|
|
183
183
|
*/
|
|
184
184
|
declare function isValidDate(s: string | undefined | null): boolean;
|
|
185
185
|
|
|
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
|
+
/**
|
|
217
|
+
* Display-width helpers for monospace/plain-text alignment.
|
|
218
|
+
*
|
|
219
|
+
* `String.prototype.padEnd` counts UTF-16 units, but emoji flags occupy 2
|
|
220
|
+
* terminal columns regardless of how many units encode them: a regional-
|
|
221
|
+
* indicator flag (🇲🇽) is 4 units, while a tag-sequence flag (England 🏴) is
|
|
222
|
+
* 14 — so unit-counted padding pushed England/Scotland rows ~10 columns out of
|
|
223
|
+
* line. These helpers count grapheme clusters, with pictographic clusters
|
|
224
|
+
* (flags included) as 2 columns, matching how terminals render them.
|
|
225
|
+
*/
|
|
226
|
+
/** Terminal display width of a string (grapheme clusters; emoji count as 2). */
|
|
227
|
+
declare function displayWidth(s: string): number;
|
|
228
|
+
/**
|
|
229
|
+
* Pad with trailing spaces to `width` DISPLAY columns (never truncates — a
|
|
230
|
+
* too-long value overflows its column rather than being cut mid-name).
|
|
231
|
+
*/
|
|
232
|
+
declare function padVisible(s: string, width: number): string;
|
|
233
|
+
|
|
186
234
|
/** Outcome (home perspective) from a final/looking scoreline. */
|
|
187
235
|
declare function outcomeFromScore(home: number, away: number): Outcome;
|
|
188
236
|
/** Is the match currently in play (including halftime)? */
|
|
@@ -355,6 +403,30 @@ declare function currentOrNextFixtureForTeam(code: string, opts?: {
|
|
|
355
403
|
/** Sorted list of distinct group letters present in the schedule. */
|
|
356
404
|
declare function groups(fixtures?: Match[]): string[];
|
|
357
405
|
|
|
406
|
+
/** A roster team plus its group letter (A–L). */
|
|
407
|
+
interface TeamInfo extends Team {
|
|
408
|
+
group?: string;
|
|
409
|
+
}
|
|
410
|
+
/**
|
|
411
|
+
* The 48-team roster from the bundled group-stage fixtures, sorted by name.
|
|
412
|
+
* Placeholder/knockout slots (non-3-letter code or 🏳️) are skipped.
|
|
413
|
+
*/
|
|
414
|
+
declare function allTeams(fixtures?: Match[]): TeamInfo[];
|
|
415
|
+
interface TeamLookup {
|
|
416
|
+
query: string;
|
|
417
|
+
/** The single confident match, or null when the query is ambiguous or unknown. */
|
|
418
|
+
team: TeamInfo | null;
|
|
419
|
+
/** All candidate teams, best-first (1+ whenever anything matched). */
|
|
420
|
+
matches: TeamInfo[];
|
|
421
|
+
}
|
|
422
|
+
/**
|
|
423
|
+
* Resolve a free-text team name or code to a roster team. Precedence: exact code,
|
|
424
|
+
* exact name, alias, then prefix/substring fuzzy on the name (fuzzy only for
|
|
425
|
+
* queries of 3+ letters, so a 1–2 char query can't substring-match noise). A
|
|
426
|
+
* unique fuzzy hit resolves; multiple hits return as candidates with `team: null`.
|
|
427
|
+
*/
|
|
428
|
+
declare function lookupTeam(query: string, fixtures?: Match[]): TeamLookup;
|
|
429
|
+
|
|
358
430
|
/** One row of a group table. */
|
|
359
431
|
interface StandingRow {
|
|
360
432
|
team: Team;
|
|
@@ -1152,4 +1224,4 @@ declare function formatShareBracket(input: ShareBracketInput, options?: ShareBra
|
|
|
1152
1224
|
/** Compact one-line-per-match bracket for narrow share contexts. */
|
|
1153
1225
|
declare function formatBracketCompactLine(mv: BracketMatchView, opts?: BracketFormatOpts): string;
|
|
1154
1226
|
|
|
1155
|
-
export { BRACKET_STAGE_ORDER, type BracketFormatOpts, type BracketMatchNode, type BracketMatchView, type BracketResult, type BracketTopology, type BracketView, type BuildSignalInput, DEFAULT_COMPETITION, DEFAULT_FLAVOR, DEFAULT_MAX_AGE_MS, EspnAdapter, type EspnAdapterOptions, FLAVOR_LEVELS, FakeMarketProvider, type FakeMarketProviderOptions, type FavoriteStrength, type FlavorLevel, type FormatOpts, type GroupStandings, type KnockoutFixturesResult, LIVE_WINDOW_MS, type Lang, type LedgerRow, type LiveResult, type MapContext, type MarketFavorite, type MarketMapping, type MarketMappingTable, type MarketOutcome, type MarketOutcomeKind, type MarketProvider, type MarketSignal, type MarketSignalOptions, type MarketSignalsResult, type Match, type MatchByIdResult, type MatchEvent, type NextFixtureResult, type Outcome, PolymarketProvider, type PolymarketProviderOptions, type ProviderAdapter, type ProviderCapabilities, type PunditPick, type ResolvedParticipant, SHARE_DISCLAIMER, SHARE_HASHTAG, type ShareBracketInput, type ShareBracketOptions, type ShareSnippetInput, type ShareSnippetOptions, type ShareStyle, type ShareTableInput, type SlotRef, type Stage, type StandingRow, type StandingsResult, type Status, type Team, allFixtures, asFlavorLevel, buildBracketTopology, buildBracketView, buildMarketSignal, byKickoff, competitionBase, computeStandings, countdown, currentOrNextFixtureForTeam, deriveFavorite, favoriteStrength, fixturesByDate, fixturesByGroup, fixturesByTeam, fixturesInLiveWindow, flagEmoji, formatBracketCompactLine, formatBracketList, formatBracketMatchLine, formatBracketTree, formatDate, formatKickoff, formatShareBracket, formatShareSnippet, formatShareTable, formatTime, getBracket, getKnockoutFixtures, getLiveMatches, getMarketSignal, getMarketSignals, getMatchById, getMatchesForDate, getNextFixtureForTeam, getStandings, groups, hasSaneDistribution, isFinished, isFlavorLevel, isLive, isReliableMarketSignal, isResolvedNation, isStaleSignal, isValidDate, isValidTimeZone, liveSourceLabel, loadBracketTopology, localDate, makeAdapter, makeMarketProvider, mapEspnEvent, mapsCleanly, marketAttributionText, marketBlock, marketFavoriteText, marketFixtureForTeam, marketLine, marketProbabilityText, marketRelevant, marketSignalRendersFor, marketSourceLabel, matchFlavor, matchKey, matchLocation, mergeLive, nationToFlag, nationToRegion, nextFixtureForTeam, normalizeLang, normalizeOutcomes, outcomeFromScore, parseStandings, parseTeamSlot, resolveCompetition, resolveMarketSource, resolveTz, sanitizeBundledFixture, scoreline, stageLabel, stageLabelI18n, t };
|
|
1227
|
+
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, FEED_TEXT_MAX, FLAVOR_LEVELS, FakeMarketProvider, type FakeMarketProviderOptions, type FavoriteStrength, type FlavorLevel, type FormatOpts, type GroupStandings, type KnockoutFixturesResult, LIVE_WINDOW_MS, type Lang, type LedgerRow, type LiveResult, type MapContext, type MarketFavorite, type MarketMapping, type MarketMappingTable, type MarketOutcome, type MarketOutcomeKind, type MarketProvider, type MarketSignal, type MarketSignalOptions, type MarketSignalsResult, type Match, type MatchByIdResult, type MatchEvent, type NextFixtureResult, type Outcome, PolymarketProvider, type PolymarketProviderOptions, type ProviderAdapter, type ProviderCapabilities, type PunditPick, type ResolvedParticipant, SHARE_DISCLAIMER, SHARE_HASHTAG, type ShareBracketInput, type ShareBracketOptions, type ShareSnippetInput, type ShareSnippetOptions, type ShareStyle, type ShareTableInput, type SlotRef, type Stage, type StandingRow, type StandingsResult, type Status, type Team, type TeamInfo, type TeamLookup, allFixtures, allTeams, asFlavorLevel, buildBracketTopology, buildBracketView, buildMarketSignal, byKickoff, competitionBase, computeStandings, countdown, currentOrNextFixtureForTeam, deriveFavorite, displayWidth, favoriteStrength, fixturesByDate, fixturesByGroup, fixturesByTeam, fixturesInLiveWindow, flagEmoji, formatBracketCompactLine, formatBracketList, formatBracketMatchLine, formatBracketTree, formatDate, formatKickoff, formatShareBracket, formatShareSnippet, formatShareTable, formatTime, getBracket, getKnockoutFixtures, getLiveMatches, getMarketSignal, getMarketSignals, getMatchById, getMatchesForDate, getNextFixtureForTeam, getStandings, groups, hasSaneDistribution, isFinished, isFlavorLevel, isLive, isReliableMarketSignal, isResolvedNation, isStaleSignal, isValidDate, isValidTimeZone, liveSourceLabel, loadBracketTopology, localDate, lookupTeam, makeAdapter, makeMarketProvider, mapEspnEvent, mapsCleanly, marketAttributionText, marketBlock, marketFavoriteText, marketFixtureForTeam, marketLine, marketProbabilityText, marketRelevant, marketSignalRendersFor, marketSourceLabel, matchFlavor, matchKey, matchLocation, mergeLive, nationToFlag, nationToRegion, nextFixtureForTeam, normalizeLang, normalizeOutcomes, outcomeFromScore, padVisible, parseStandings, parseTeamSlot, resolveCompetition, resolveMarketSource, resolveTz, sanitizeBundledFixture, sanitizeFeedText, sanitizeMatchStrings, scoreline, stageLabel, stageLabelI18n, t };
|
package/dist/index.js
CHANGED
|
@@ -502,6 +502,67 @@ function shiftUtcDate(dateISO, days) {
|
|
|
502
502
|
return new Date(Date.UTC(y ?? 1970, (m ?? 1) - 1, (d ?? 1) + days)).toISOString().slice(0, 10);
|
|
503
503
|
}
|
|
504
504
|
|
|
505
|
+
// src/sanitize.ts
|
|
506
|
+
var FEED_TEXT_MAX = 100;
|
|
507
|
+
function sanitizeFeedText(value, max = FEED_TEXT_MAX) {
|
|
508
|
+
let out = "";
|
|
509
|
+
let count = 0;
|
|
510
|
+
for (const ch of String(value)) {
|
|
511
|
+
const cp = ch.codePointAt(0) ?? 0;
|
|
512
|
+
const isWhitespaceControl = cp === 9 || cp === 10 || cp === 13;
|
|
513
|
+
if ((cp <= 31 || cp >= 127 && cp <= 159) && !isWhitespaceControl) continue;
|
|
514
|
+
if (count >= max) break;
|
|
515
|
+
out += isWhitespaceControl ? " " : ch;
|
|
516
|
+
count++;
|
|
517
|
+
}
|
|
518
|
+
return out;
|
|
519
|
+
}
|
|
520
|
+
function sanitizeTeam(t2) {
|
|
521
|
+
return {
|
|
522
|
+
...t2 ?? {},
|
|
523
|
+
code: sanitizeFeedText(t2?.code ?? ""),
|
|
524
|
+
name: sanitizeFeedText(t2?.name ?? ""),
|
|
525
|
+
flag: sanitizeFeedText(t2?.flag ?? "")
|
|
526
|
+
};
|
|
527
|
+
}
|
|
528
|
+
function finiteOrUndefined(v) {
|
|
529
|
+
return typeof v === "number" && Number.isFinite(v) ? v : void 0;
|
|
530
|
+
}
|
|
531
|
+
function sanitizeScorePair(v) {
|
|
532
|
+
const home = finiteOrUndefined(v?.home);
|
|
533
|
+
const away = finiteOrUndefined(v?.away);
|
|
534
|
+
return home !== void 0 && away !== void 0 ? { home, away } : void 0;
|
|
535
|
+
}
|
|
536
|
+
function sanitizeMatchStrings(m) {
|
|
537
|
+
const score = sanitizeScorePair(m.score);
|
|
538
|
+
return {
|
|
539
|
+
...m,
|
|
540
|
+
venue: sanitizeFeedText(m.venue ?? ""),
|
|
541
|
+
city: m.city == null ? m.city : sanitizeFeedText(m.city),
|
|
542
|
+
country: m.country == null ? m.country : sanitizeFeedText(m.country),
|
|
543
|
+
home: sanitizeTeam(m.home),
|
|
544
|
+
away: sanitizeTeam(m.away),
|
|
545
|
+
score,
|
|
546
|
+
shootout: score ? sanitizeScorePair(m.shootout) : void 0,
|
|
547
|
+
minute: finiteOrUndefined(m.minute)
|
|
548
|
+
};
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
// src/text.ts
|
|
552
|
+
var segmenter = new Intl.Segmenter();
|
|
553
|
+
var WIDE_CLUSTER = new RegExp("^(?:\\p{Regional_Indicator}|\\p{Extended_Pictographic})", "u");
|
|
554
|
+
function displayWidth(s) {
|
|
555
|
+
let w = 0;
|
|
556
|
+
for (const { segment } of segmenter.segment(s)) {
|
|
557
|
+
w += WIDE_CLUSTER.test(segment) ? 2 : 1;
|
|
558
|
+
}
|
|
559
|
+
return w;
|
|
560
|
+
}
|
|
561
|
+
function padVisible(s, width) {
|
|
562
|
+
const w = displayWidth(s);
|
|
563
|
+
return w >= width ? s : s + " ".repeat(width - w);
|
|
564
|
+
}
|
|
565
|
+
|
|
505
566
|
// src/normalize.ts
|
|
506
567
|
function outcomeFromScore(home, away) {
|
|
507
568
|
if (home > away) return "H";
|
|
@@ -2900,6 +2961,62 @@ function groups(fixtures = SCHEDULE) {
|
|
|
2900
2961
|
return [...set].sort();
|
|
2901
2962
|
}
|
|
2902
2963
|
|
|
2964
|
+
// src/teams.ts
|
|
2965
|
+
function norm2(s) {
|
|
2966
|
+
return s.toLowerCase().normalize("NFD").replace(/[̀-ͯ]/g, "").replace(/[^a-z]/g, "");
|
|
2967
|
+
}
|
|
2968
|
+
function allTeams(fixtures = allFixtures()) {
|
|
2969
|
+
const seen = /* @__PURE__ */ new Map();
|
|
2970
|
+
for (const m of fixtures) {
|
|
2971
|
+
if (m.stage !== "GROUP") continue;
|
|
2972
|
+
for (const t2 of [m.home, m.away]) {
|
|
2973
|
+
if (!/^[A-Z]{3}$/.test(t2.code) || t2.flag === "\u{1F3F3}\uFE0F") continue;
|
|
2974
|
+
if (!seen.has(t2.code)) seen.set(t2.code, { ...t2, group: m.group ?? void 0 });
|
|
2975
|
+
}
|
|
2976
|
+
}
|
|
2977
|
+
return [...seen.values()].sort((a, b) => a.name.localeCompare(b.name));
|
|
2978
|
+
}
|
|
2979
|
+
var TEAM_ALIASES = {
|
|
2980
|
+
turkey: "TUR",
|
|
2981
|
+
holland: "NED",
|
|
2982
|
+
korea: "KOR",
|
|
2983
|
+
skorea: "KOR",
|
|
2984
|
+
korearepublic: "KOR",
|
|
2985
|
+
republicofkorea: "KOR",
|
|
2986
|
+
czechrepublic: "CZE",
|
|
2987
|
+
congo: "COD",
|
|
2988
|
+
drcongo: "COD",
|
|
2989
|
+
drc: "COD",
|
|
2990
|
+
democraticrepublicofcongo: "COD",
|
|
2991
|
+
democraticrepublicofthecongo: "COD",
|
|
2992
|
+
cotedivoire: "CIV",
|
|
2993
|
+
caboverde: "CPV",
|
|
2994
|
+
bosnia: "BIH",
|
|
2995
|
+
bosniaandherzegovina: "BIH",
|
|
2996
|
+
us: "USA",
|
|
2997
|
+
america: "USA",
|
|
2998
|
+
unitedstatesofamerica: "USA"
|
|
2999
|
+
};
|
|
3000
|
+
function lookupTeam(query, fixtures = allFixtures()) {
|
|
3001
|
+
const roster = allTeams(fixtures);
|
|
3002
|
+
const q = norm2(query);
|
|
3003
|
+
if (!q) return { query, team: null, matches: [] };
|
|
3004
|
+
const byCode = roster.find((t2) => norm2(t2.code) === q);
|
|
3005
|
+
if (byCode) return { query, team: byCode, matches: [byCode] };
|
|
3006
|
+
const byName = roster.find((t2) => norm2(t2.name) === q);
|
|
3007
|
+
if (byName) return { query, team: byName, matches: [byName] };
|
|
3008
|
+
const aliasCode = TEAM_ALIASES[q];
|
|
3009
|
+
if (aliasCode) {
|
|
3010
|
+
const t2 = roster.find((r) => r.code === aliasCode);
|
|
3011
|
+
if (t2) return { query, team: t2, matches: [t2] };
|
|
3012
|
+
}
|
|
3013
|
+
if (q.length < 3) return { query, team: null, matches: [] };
|
|
3014
|
+
const prefix = roster.filter((t2) => norm2(t2.name).startsWith(q));
|
|
3015
|
+
const substr = roster.filter((t2) => norm2(t2.name).includes(q) && !prefix.includes(t2));
|
|
3016
|
+
const matches = [...prefix, ...substr];
|
|
3017
|
+
return { query, team: matches.length === 1 ? matches[0] : null, matches };
|
|
3018
|
+
}
|
|
3019
|
+
|
|
2903
3020
|
// src/standings.ts
|
|
2904
3021
|
function blankRow(team) {
|
|
2905
3022
|
return {
|
|
@@ -2969,6 +3086,7 @@ var ESPN_SOCCER = "https://site.api.espn.com/apis/site/v2/sports/soccer";
|
|
|
2969
3086
|
var DEFAULT_COMPETITION = "fifa.world";
|
|
2970
3087
|
var DEFAULT_BASE = `${ESPN_SOCCER}/${DEFAULT_COMPETITION}`;
|
|
2971
3088
|
var USER_AGENT = "claudinho/0.0 (+https://github.com/arturogarrido/claudinho)";
|
|
3089
|
+
var MAX_RESPONSE_BYTES = 5 * 1024 * 1024;
|
|
2972
3090
|
function competitionBase(slug) {
|
|
2973
3091
|
return `${ESPN_SOCCER}/${slug}`;
|
|
2974
3092
|
}
|
|
@@ -3012,9 +3130,15 @@ function toInt(s) {
|
|
|
3012
3130
|
return Number.isFinite(n) ? n : void 0;
|
|
3013
3131
|
}
|
|
3014
3132
|
function toTeam(t2) {
|
|
3015
|
-
const name =
|
|
3016
|
-
|
|
3017
|
-
|
|
3133
|
+
const name = sanitizeFeedText(
|
|
3134
|
+
t2?.displayName ?? t2?.name ?? t2?.location ?? t2?.shortDisplayName ?? "TBD"
|
|
3135
|
+
);
|
|
3136
|
+
const code = sanitizeFeedText(t2?.abbreviation ?? name.slice(0, 3)).toUpperCase();
|
|
3137
|
+
return {
|
|
3138
|
+
code,
|
|
3139
|
+
name,
|
|
3140
|
+
flag: nationToFlag(sanitizeFeedText(t2?.displayName ?? t2?.abbreviation ?? name))
|
|
3141
|
+
};
|
|
3018
3142
|
}
|
|
3019
3143
|
function mapEspnEvent(ev, ctx = {}) {
|
|
3020
3144
|
const comp = ev.competitions?.[0];
|
|
@@ -3045,9 +3169,9 @@ function mapEspnEvent(ev, ctx = {}) {
|
|
|
3045
3169
|
stage,
|
|
3046
3170
|
group,
|
|
3047
3171
|
kickoff: ev.date,
|
|
3048
|
-
venue: comp?.venue?.fullName ?? "",
|
|
3049
|
-
city: comp?.venue?.address?.city || void 0,
|
|
3050
|
-
country: comp?.venue?.address?.country || void 0,
|
|
3172
|
+
venue: sanitizeFeedText(comp?.venue?.fullName ?? ""),
|
|
3173
|
+
city: sanitizeFeedText(comp?.venue?.address?.city ?? "") || void 0,
|
|
3174
|
+
country: sanitizeFeedText(comp?.venue?.address?.country ?? "") || void 0,
|
|
3051
3175
|
home,
|
|
3052
3176
|
away,
|
|
3053
3177
|
score: hasScore ? { home: hs, away: as } : void 0,
|
|
@@ -3171,11 +3295,18 @@ var EspnAdapter = class {
|
|
|
3171
3295
|
try {
|
|
3172
3296
|
const res = await doFetch(url, {
|
|
3173
3297
|
signal: controller.signal,
|
|
3298
|
+
// ESPN never legitimately redirects; following one would sidestep the
|
|
3299
|
+
// fixed-host guarantee, so treat any redirect as a failure (degraded).
|
|
3300
|
+
redirect: "error",
|
|
3174
3301
|
headers: { Accept: "application/json", "User-Agent": USER_AGENT }
|
|
3175
3302
|
});
|
|
3176
3303
|
if (!res.ok) {
|
|
3177
3304
|
throw new Error(`ESPN request failed: ${res.status} ${res.statusText}`);
|
|
3178
3305
|
}
|
|
3306
|
+
const length = Number(res.headers?.get?.("content-length"));
|
|
3307
|
+
if (Number.isFinite(length) && length > MAX_RESPONSE_BYTES) {
|
|
3308
|
+
throw new Error(`ESPN response too large: ${length} bytes`);
|
|
3309
|
+
}
|
|
3179
3310
|
return await res.json();
|
|
3180
3311
|
} finally {
|
|
3181
3312
|
clearTimeout(timer);
|
|
@@ -4511,12 +4642,19 @@ var PolymarketProvider = class {
|
|
|
4511
4642
|
const doFetch = this.opts.fetchImpl ?? fetch;
|
|
4512
4643
|
const res = await doFetch(url, {
|
|
4513
4644
|
signal: AbortSignal.timeout(timeoutMs ?? this.opts.timeoutMs ?? DEFAULT_TIMEOUT_MS),
|
|
4645
|
+
// Gamma never legitimately redirects; following one would sidestep the
|
|
4646
|
+
// host allow-list (it only validates the base URL), so reject redirects.
|
|
4647
|
+
redirect: "error",
|
|
4514
4648
|
headers: { Accept: "application/json", "User-Agent": USER_AGENT2 }
|
|
4515
4649
|
});
|
|
4516
4650
|
if (res.status === 404) return void 0;
|
|
4517
4651
|
if (!res.ok) {
|
|
4518
4652
|
throw new Error(`Polymarket request failed: ${res.status} ${res.statusText}`);
|
|
4519
4653
|
}
|
|
4654
|
+
const length = Number(res.headers?.get?.("content-length"));
|
|
4655
|
+
if (Number.isFinite(length) && length > MAX_RESPONSE_BYTES) {
|
|
4656
|
+
throw new Error(`Polymarket response too large: ${length} bytes`);
|
|
4657
|
+
}
|
|
4520
4658
|
const data = await res.json();
|
|
4521
4659
|
const event = Array.isArray(data) ? data[0] : data;
|
|
4522
4660
|
return event && typeof event === "object" ? event : void 0;
|
|
@@ -4969,6 +5107,7 @@ export {
|
|
|
4969
5107
|
DEFAULT_FLAVOR,
|
|
4970
5108
|
DEFAULT_MAX_AGE_MS,
|
|
4971
5109
|
EspnAdapter,
|
|
5110
|
+
FEED_TEXT_MAX,
|
|
4972
5111
|
FLAVOR_LEVELS,
|
|
4973
5112
|
FakeMarketProvider,
|
|
4974
5113
|
LIVE_WINDOW_MS,
|
|
@@ -4976,6 +5115,7 @@ export {
|
|
|
4976
5115
|
SHARE_DISCLAIMER,
|
|
4977
5116
|
SHARE_HASHTAG,
|
|
4978
5117
|
allFixtures,
|
|
5118
|
+
allTeams,
|
|
4979
5119
|
asFlavorLevel,
|
|
4980
5120
|
buildBracketTopology,
|
|
4981
5121
|
buildBracketView,
|
|
@@ -4986,6 +5126,7 @@ export {
|
|
|
4986
5126
|
countdown,
|
|
4987
5127
|
currentOrNextFixtureForTeam,
|
|
4988
5128
|
deriveFavorite,
|
|
5129
|
+
displayWidth,
|
|
4989
5130
|
favoriteStrength,
|
|
4990
5131
|
fixturesByDate,
|
|
4991
5132
|
fixturesByGroup,
|
|
@@ -5024,6 +5165,7 @@ export {
|
|
|
5024
5165
|
liveSourceLabel,
|
|
5025
5166
|
loadBracketTopology,
|
|
5026
5167
|
localDate,
|
|
5168
|
+
lookupTeam,
|
|
5027
5169
|
makeAdapter,
|
|
5028
5170
|
makeMarketProvider,
|
|
5029
5171
|
mapEspnEvent,
|
|
@@ -5047,12 +5189,15 @@ export {
|
|
|
5047
5189
|
normalizeLang,
|
|
5048
5190
|
normalizeOutcomes,
|
|
5049
5191
|
outcomeFromScore,
|
|
5192
|
+
padVisible,
|
|
5050
5193
|
parseStandings,
|
|
5051
5194
|
parseTeamSlot,
|
|
5052
5195
|
resolveCompetition,
|
|
5053
5196
|
resolveMarketSource,
|
|
5054
5197
|
resolveTz,
|
|
5055
5198
|
sanitizeBundledFixture,
|
|
5199
|
+
sanitizeFeedText,
|
|
5200
|
+
sanitizeMatchStrings,
|
|
5056
5201
|
scoreline,
|
|
5057
5202
|
stageLabel,
|
|
5058
5203
|
stageLabelI18n,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@claudinho/core",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.19",
|
|
4
4
|
"description": "Domain model, provider adapters (ESPN), standings, bundled schedule, and the read-only Polymarket market-signal sidecar powering Claudinho. Not affiliated with FIFA or Anthropic.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|