fitzroy 1.1.0 → 1.1.1
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/cli.js +376 -212
- package/dist/index.d.ts +267 -261
- package/dist/index.js +121 -91
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -47,35 +47,40 @@ function parseAflApiDate(iso) {
|
|
|
47
47
|
}
|
|
48
48
|
return date;
|
|
49
49
|
}
|
|
50
|
-
function parseFootyWireDate(dateStr) {
|
|
50
|
+
function parseFootyWireDate(dateStr, defaultYear) {
|
|
51
51
|
const trimmed = dateStr.trim();
|
|
52
52
|
if (trimmed === "") {
|
|
53
53
|
return null;
|
|
54
54
|
}
|
|
55
55
|
const withoutDow = trimmed.replace(/^(?:Mon|Tue|Wed|Thu|Fri|Sat|Sun)\w*\s+/i, "");
|
|
56
56
|
const normalised = withoutDow.replace(/-/g, " ");
|
|
57
|
-
const
|
|
58
|
-
if (
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
return null;
|
|
64
|
-
}
|
|
65
|
-
const monthIndex = MONTH_ABBREV_TO_INDEX.get(monthStr.toLowerCase());
|
|
66
|
-
if (monthIndex === void 0) {
|
|
67
|
-
return null;
|
|
68
|
-
}
|
|
69
|
-
const year = Number.parseInt(yearStr, 10);
|
|
70
|
-
const day = Number.parseInt(dayStr, 10);
|
|
71
|
-
const date = new Date(Date.UTC(year, monthIndex, day));
|
|
72
|
-
if (Number.isNaN(date.getTime())) {
|
|
73
|
-
return null;
|
|
57
|
+
const fullMatch = /^(\d{1,2})\s+([A-Za-z]+)\s+(\d{4})$/.exec(normalised);
|
|
58
|
+
if (fullMatch) {
|
|
59
|
+
const [, dayStr, monthStr, yearStr] = fullMatch;
|
|
60
|
+
if (dayStr && monthStr && yearStr) {
|
|
61
|
+
return buildUtcDate(Number.parseInt(yearStr, 10), monthStr, Number.parseInt(dayStr, 10));
|
|
62
|
+
}
|
|
74
63
|
}
|
|
75
|
-
|
|
76
|
-
|
|
64
|
+
const shortMatch = /^(\d{1,2})\s+([A-Za-z]+)(?:\s+(\d{1,2}):(\d{2})(am|pm))?$/i.exec(normalised);
|
|
65
|
+
if (shortMatch && defaultYear != null) {
|
|
66
|
+
const [, dayStr, monthStr, hourStr, minStr, ampm] = shortMatch;
|
|
67
|
+
if (!dayStr || !monthStr) return null;
|
|
68
|
+
const monthIndex = MONTH_ABBREV_TO_INDEX.get(monthStr.toLowerCase());
|
|
69
|
+
if (monthIndex === void 0) return null;
|
|
70
|
+
const day = Number.parseInt(dayStr, 10);
|
|
71
|
+
const hasTime = hourStr && minStr && ampm;
|
|
72
|
+
if (!hasTime) {
|
|
73
|
+
return buildUtcDate(defaultYear, monthStr, day);
|
|
74
|
+
}
|
|
75
|
+
let aestHours = Number.parseInt(hourStr, 10);
|
|
76
|
+
const minutes = Number.parseInt(minStr, 10);
|
|
77
|
+
if (ampm.toLowerCase() === "pm" && aestHours < 12) aestHours += 12;
|
|
78
|
+
if (ampm.toLowerCase() === "am" && aestHours === 12) aestHours = 0;
|
|
79
|
+
const date = new Date(Date.UTC(defaultYear, monthIndex, day, aestHours - 10, minutes));
|
|
80
|
+
if (Number.isNaN(date.getTime())) return null;
|
|
81
|
+
return date;
|
|
77
82
|
}
|
|
78
|
-
return
|
|
83
|
+
return null;
|
|
79
84
|
}
|
|
80
85
|
function parseAflTablesDate(dateStr) {
|
|
81
86
|
const trimmed = dateStr.trim();
|
|
@@ -810,7 +815,7 @@ function parseMatchList(html, year) {
|
|
|
810
815
|
const scoreLink = scoreCell.find("a").attr("href") ?? "";
|
|
811
816
|
const midMatch = /mid=(\d+)/.exec(scoreLink);
|
|
812
817
|
const matchId = midMatch?.[1] ? `FW_${midMatch[1]}` : `FW_${year}_R${currentRound}_${homeTeam}`;
|
|
813
|
-
const date = parseFootyWireDate(dateText) ?? new Date(year, 0, 1);
|
|
818
|
+
const date = parseFootyWireDate(dateText, year) ?? new Date(Date.UTC(year, 0, 1));
|
|
814
819
|
const homeGoals = Math.floor(homePoints / 6);
|
|
815
820
|
const homeBehinds = homePoints - homeGoals * 6;
|
|
816
821
|
const awayGoals = Math.floor(awayPoints / 6);
|
|
@@ -880,7 +885,7 @@ function parseFixtureList(html, year) {
|
|
|
880
885
|
if (teamLinks.length < 2) return;
|
|
881
886
|
const homeTeam = normaliseTeamName($(teamLinks[0]).text().trim());
|
|
882
887
|
const awayTeam = normaliseTeamName($(teamLinks[1]).text().trim());
|
|
883
|
-
const date = parseFootyWireDate(dateText) ?? new Date(year, 0, 1);
|
|
888
|
+
const date = parseFootyWireDate(dateText, year) ?? new Date(Date.UTC(year, 0, 1));
|
|
884
889
|
gameNumber++;
|
|
885
890
|
const scoreCell = cells.length >= 5 ? $(cells[4]) : null;
|
|
886
891
|
const scoreText = scoreCell?.text().trim() ?? "";
|
|
@@ -988,6 +993,12 @@ var FOOTYWIRE_SLUG_MAP = /* @__PURE__ */ new Map([
|
|
|
988
993
|
function teamNameToFootyWireSlug(teamName) {
|
|
989
994
|
return FOOTYWIRE_SLUG_MAP.get(teamName);
|
|
990
995
|
}
|
|
996
|
+
function normaliseDob(raw) {
|
|
997
|
+
if (!raw) return null;
|
|
998
|
+
const parsed = parseFootyWireDate(raw);
|
|
999
|
+
if (parsed) return parsed.toISOString().slice(0, 10);
|
|
1000
|
+
return raw;
|
|
1001
|
+
}
|
|
991
1002
|
function parseFootyWirePlayerList(html, teamName) {
|
|
992
1003
|
const $ = cheerio2.load(html);
|
|
993
1004
|
const players = [];
|
|
@@ -1029,7 +1040,7 @@ function parseFootyWirePlayerList(html, teamName) {
|
|
|
1029
1040
|
team: teamName,
|
|
1030
1041
|
jumperNumber,
|
|
1031
1042
|
position: position || null,
|
|
1032
|
-
dateOfBirth: dobText
|
|
1043
|
+
dateOfBirth: normaliseDob(dobText),
|
|
1033
1044
|
heightCm,
|
|
1034
1045
|
weightKg: null,
|
|
1035
1046
|
gamesPlayed,
|
|
@@ -1501,71 +1512,72 @@ var CfsPlayerInnerSchema = z.object({
|
|
|
1501
1512
|
captain: z.boolean().optional(),
|
|
1502
1513
|
playerJumperNumber: z.number().optional()
|
|
1503
1514
|
}).passthrough();
|
|
1515
|
+
var statNum = z.number().nullable().optional();
|
|
1504
1516
|
var PlayerGameStatsSchema = z.object({
|
|
1505
|
-
goals:
|
|
1506
|
-
behinds:
|
|
1507
|
-
kicks:
|
|
1508
|
-
handballs:
|
|
1509
|
-
disposals:
|
|
1510
|
-
marks:
|
|
1511
|
-
bounces:
|
|
1512
|
-
tackles:
|
|
1513
|
-
contestedPossessions:
|
|
1514
|
-
uncontestedPossessions:
|
|
1515
|
-
totalPossessions:
|
|
1516
|
-
inside50s:
|
|
1517
|
-
marksInside50:
|
|
1518
|
-
contestedMarks:
|
|
1519
|
-
hitouts:
|
|
1520
|
-
onePercenters:
|
|
1521
|
-
disposalEfficiency:
|
|
1522
|
-
clangers:
|
|
1523
|
-
freesFor:
|
|
1524
|
-
freesAgainst:
|
|
1525
|
-
dreamTeamPoints:
|
|
1517
|
+
goals: statNum,
|
|
1518
|
+
behinds: statNum,
|
|
1519
|
+
kicks: statNum,
|
|
1520
|
+
handballs: statNum,
|
|
1521
|
+
disposals: statNum,
|
|
1522
|
+
marks: statNum,
|
|
1523
|
+
bounces: statNum,
|
|
1524
|
+
tackles: statNum,
|
|
1525
|
+
contestedPossessions: statNum,
|
|
1526
|
+
uncontestedPossessions: statNum,
|
|
1527
|
+
totalPossessions: statNum,
|
|
1528
|
+
inside50s: statNum,
|
|
1529
|
+
marksInside50: statNum,
|
|
1530
|
+
contestedMarks: statNum,
|
|
1531
|
+
hitouts: statNum,
|
|
1532
|
+
onePercenters: statNum,
|
|
1533
|
+
disposalEfficiency: statNum,
|
|
1534
|
+
clangers: statNum,
|
|
1535
|
+
freesFor: statNum,
|
|
1536
|
+
freesAgainst: statNum,
|
|
1537
|
+
dreamTeamPoints: statNum,
|
|
1526
1538
|
clearances: z.object({
|
|
1527
|
-
centreClearances:
|
|
1528
|
-
stoppageClearances:
|
|
1529
|
-
totalClearances:
|
|
1530
|
-
}).passthrough().optional(),
|
|
1531
|
-
rebound50s:
|
|
1532
|
-
goalAssists:
|
|
1533
|
-
goalAccuracy:
|
|
1534
|
-
turnovers:
|
|
1535
|
-
intercepts:
|
|
1536
|
-
tacklesInside50:
|
|
1537
|
-
shotsAtGoal:
|
|
1538
|
-
metresGained:
|
|
1539
|
-
scoreInvolvements:
|
|
1540
|
-
ratingPoints:
|
|
1539
|
+
centreClearances: statNum,
|
|
1540
|
+
stoppageClearances: statNum,
|
|
1541
|
+
totalClearances: statNum
|
|
1542
|
+
}).passthrough().nullable().optional(),
|
|
1543
|
+
rebound50s: statNum,
|
|
1544
|
+
goalAssists: statNum,
|
|
1545
|
+
goalAccuracy: statNum,
|
|
1546
|
+
turnovers: statNum,
|
|
1547
|
+
intercepts: statNum,
|
|
1548
|
+
tacklesInside50: statNum,
|
|
1549
|
+
shotsAtGoal: statNum,
|
|
1550
|
+
metresGained: statNum,
|
|
1551
|
+
scoreInvolvements: statNum,
|
|
1552
|
+
ratingPoints: statNum,
|
|
1541
1553
|
extendedStats: z.object({
|
|
1542
|
-
effectiveDisposals:
|
|
1543
|
-
effectiveKicks:
|
|
1544
|
-
kickEfficiency:
|
|
1545
|
-
kickToHandballRatio:
|
|
1546
|
-
pressureActs:
|
|
1547
|
-
defHalfPressureActs:
|
|
1548
|
-
spoils:
|
|
1549
|
-
hitoutsToAdvantage:
|
|
1550
|
-
hitoutWinPercentage:
|
|
1551
|
-
hitoutToAdvantageRate:
|
|
1552
|
-
groundBallGets:
|
|
1553
|
-
f50GroundBallGets:
|
|
1554
|
-
interceptMarks:
|
|
1555
|
-
marksOnLead:
|
|
1556
|
-
contestedPossessionRate:
|
|
1557
|
-
contestOffOneOnOnes:
|
|
1558
|
-
contestOffWins:
|
|
1559
|
-
contestOffWinsPercentage:
|
|
1560
|
-
contestDefOneOnOnes:
|
|
1561
|
-
contestDefLosses:
|
|
1562
|
-
contestDefLossPercentage:
|
|
1563
|
-
centreBounceAttendances:
|
|
1564
|
-
kickins:
|
|
1565
|
-
kickinsPlayon:
|
|
1566
|
-
ruckContests:
|
|
1567
|
-
scoreLaunches:
|
|
1568
|
-
}).passthrough().optional()
|
|
1554
|
+
effectiveDisposals: statNum,
|
|
1555
|
+
effectiveKicks: statNum,
|
|
1556
|
+
kickEfficiency: statNum,
|
|
1557
|
+
kickToHandballRatio: statNum,
|
|
1558
|
+
pressureActs: statNum,
|
|
1559
|
+
defHalfPressureActs: statNum,
|
|
1560
|
+
spoils: statNum,
|
|
1561
|
+
hitoutsToAdvantage: statNum,
|
|
1562
|
+
hitoutWinPercentage: statNum,
|
|
1563
|
+
hitoutToAdvantageRate: statNum,
|
|
1564
|
+
groundBallGets: statNum,
|
|
1565
|
+
f50GroundBallGets: statNum,
|
|
1566
|
+
interceptMarks: statNum,
|
|
1567
|
+
marksOnLead: statNum,
|
|
1568
|
+
contestedPossessionRate: statNum,
|
|
1569
|
+
contestOffOneOnOnes: statNum,
|
|
1570
|
+
contestOffWins: statNum,
|
|
1571
|
+
contestOffWinsPercentage: statNum,
|
|
1572
|
+
contestDefOneOnOnes: statNum,
|
|
1573
|
+
contestDefLosses: statNum,
|
|
1574
|
+
contestDefLossPercentage: statNum,
|
|
1575
|
+
centreBounceAttendances: statNum,
|
|
1576
|
+
kickins: statNum,
|
|
1577
|
+
kickinsPlayon: statNum,
|
|
1578
|
+
ruckContests: statNum,
|
|
1579
|
+
scoreLaunches: statNum
|
|
1580
|
+
}).passthrough().nullable().optional()
|
|
1569
1581
|
}).passthrough();
|
|
1570
1582
|
var PlayerStatsItemSchema = z.object({
|
|
1571
1583
|
player: z.object({
|
|
@@ -1578,7 +1590,7 @@ var PlayerStatsItemSchema = z.object({
|
|
|
1578
1590
|
teamId: z.string(),
|
|
1579
1591
|
playerStats: z.object({
|
|
1580
1592
|
stats: PlayerGameStatsSchema,
|
|
1581
|
-
timeOnGroundPercentage: z.number().optional()
|
|
1593
|
+
timeOnGroundPercentage: z.number().nullable().optional()
|
|
1582
1594
|
}).passthrough()
|
|
1583
1595
|
}).passthrough();
|
|
1584
1596
|
var PlayerStatsListSchema = z.object({
|
|
@@ -2696,6 +2708,7 @@ function parseAttendanceFromInfo(text) {
|
|
|
2696
2708
|
if (!match?.[1]) return null;
|
|
2697
2709
|
return Number.parseInt(match[1].replace(/,/g, ""), 10) || null;
|
|
2698
2710
|
}
|
|
2711
|
+
var GP_HEADERS = /* @__PURE__ */ new Set(["gm", "gp", "p", "mp", "games"]);
|
|
2699
2712
|
function parseAflTablesTeamStats(html, year) {
|
|
2700
2713
|
const $ = cheerio6.load(html);
|
|
2701
2714
|
const teamMap = /* @__PURE__ */ new Map();
|
|
@@ -2709,6 +2722,7 @@ function parseAflTablesTeamStats(html, year) {
|
|
|
2709
2722
|
$(rows[0]).find("td, th").each((_ci, cell) => {
|
|
2710
2723
|
headers.push($(cell).text().trim());
|
|
2711
2724
|
});
|
|
2725
|
+
const gpColIdx = headers.findIndex((h, i) => i > 0 && GP_HEADERS.has(h.toLowerCase()));
|
|
2712
2726
|
for (let ri = 1; ri < rows.length; ri++) {
|
|
2713
2727
|
const cells = $(rows[ri]).find("td");
|
|
2714
2728
|
if (cells.length < 3) continue;
|
|
@@ -2721,7 +2735,12 @@ function parseAflTablesTeamStats(html, year) {
|
|
|
2721
2735
|
}
|
|
2722
2736
|
const entry = teamMap.get(teamName);
|
|
2723
2737
|
if (!entry) continue;
|
|
2738
|
+
if (gpColIdx >= 0 && suffix === "_for") {
|
|
2739
|
+
const gpVal = Number.parseFloat($(cells[gpColIdx]).text().trim().replace(/,/g, "")) || 0;
|
|
2740
|
+
entry.gamesPlayed = gpVal;
|
|
2741
|
+
}
|
|
2724
2742
|
for (let ci = 1; ci < cells.length; ci++) {
|
|
2743
|
+
if (ci === gpColIdx) continue;
|
|
2725
2744
|
const header = headers[ci];
|
|
2726
2745
|
if (!header) continue;
|
|
2727
2746
|
const value = Number.parseFloat($(cells[ci]).text().trim().replace(/,/g, "")) || 0;
|
|
@@ -3279,15 +3298,26 @@ async function fetchPlayerStats(query) {
|
|
|
3279
3298
|
case "afl-api": {
|
|
3280
3299
|
const client = new AflApiClient();
|
|
3281
3300
|
if (query.matchId) {
|
|
3282
|
-
const
|
|
3283
|
-
|
|
3301
|
+
const [rosterResult, statsResult] = await Promise.all([
|
|
3302
|
+
client.fetchMatchRoster(query.matchId),
|
|
3303
|
+
client.fetchPlayerStats(query.matchId)
|
|
3304
|
+
]);
|
|
3305
|
+
if (!statsResult.success) return statsResult;
|
|
3306
|
+
const teamIdMap2 = /* @__PURE__ */ new Map();
|
|
3307
|
+
if (rosterResult.success) {
|
|
3308
|
+
const match = rosterResult.data.match;
|
|
3309
|
+
teamIdMap2.set(match.homeTeamId, match.homeTeam.name);
|
|
3310
|
+
teamIdMap2.set(match.awayTeamId, match.awayTeam.name);
|
|
3311
|
+
}
|
|
3284
3312
|
return ok(
|
|
3285
3313
|
transformPlayerStats(
|
|
3286
|
-
|
|
3314
|
+
statsResult.data,
|
|
3287
3315
|
query.matchId,
|
|
3288
3316
|
query.season,
|
|
3289
3317
|
query.round ?? 0,
|
|
3290
|
-
competition
|
|
3318
|
+
competition,
|
|
3319
|
+
"afl-api",
|
|
3320
|
+
teamIdMap2.size > 0 ? teamIdMap2 : void 0
|
|
3291
3321
|
)
|
|
3292
3322
|
);
|
|
3293
3323
|
}
|