fitzroy 1.1.0 → 1.2.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/cli.js +823 -329
- package/dist/index.d.ts +272 -261
- package/dist/index.js +160 -98
- 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,
|
|
@@ -1396,6 +1407,22 @@ async function fetchCoachesVotes(query) {
|
|
|
1396
1407
|
return ok(votes);
|
|
1397
1408
|
}
|
|
1398
1409
|
|
|
1410
|
+
// src/lib/concurrency.ts
|
|
1411
|
+
async function batchedMap(items, fn, options) {
|
|
1412
|
+
const batchSize = options?.batchSize ?? 5;
|
|
1413
|
+
const delayMs = options?.delayMs ?? 0;
|
|
1414
|
+
const results = [];
|
|
1415
|
+
for (let i = 0; i < items.length; i += batchSize) {
|
|
1416
|
+
const batch = items.slice(i, i + batchSize);
|
|
1417
|
+
const batchResults = await Promise.all(batch.map(fn));
|
|
1418
|
+
results.push(...batchResults);
|
|
1419
|
+
if (delayMs > 0 && i + batchSize < items.length) {
|
|
1420
|
+
await new Promise((resolve) => setTimeout(resolve, delayMs));
|
|
1421
|
+
}
|
|
1422
|
+
}
|
|
1423
|
+
return results;
|
|
1424
|
+
}
|
|
1425
|
+
|
|
1399
1426
|
// src/lib/validation.ts
|
|
1400
1427
|
import { z } from "zod/v4";
|
|
1401
1428
|
var AflApiTokenSchema = z.object({
|
|
@@ -1501,71 +1528,72 @@ var CfsPlayerInnerSchema = z.object({
|
|
|
1501
1528
|
captain: z.boolean().optional(),
|
|
1502
1529
|
playerJumperNumber: z.number().optional()
|
|
1503
1530
|
}).passthrough();
|
|
1531
|
+
var statNum = z.number().nullable().optional();
|
|
1504
1532
|
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:
|
|
1533
|
+
goals: statNum,
|
|
1534
|
+
behinds: statNum,
|
|
1535
|
+
kicks: statNum,
|
|
1536
|
+
handballs: statNum,
|
|
1537
|
+
disposals: statNum,
|
|
1538
|
+
marks: statNum,
|
|
1539
|
+
bounces: statNum,
|
|
1540
|
+
tackles: statNum,
|
|
1541
|
+
contestedPossessions: statNum,
|
|
1542
|
+
uncontestedPossessions: statNum,
|
|
1543
|
+
totalPossessions: statNum,
|
|
1544
|
+
inside50s: statNum,
|
|
1545
|
+
marksInside50: statNum,
|
|
1546
|
+
contestedMarks: statNum,
|
|
1547
|
+
hitouts: statNum,
|
|
1548
|
+
onePercenters: statNum,
|
|
1549
|
+
disposalEfficiency: statNum,
|
|
1550
|
+
clangers: statNum,
|
|
1551
|
+
freesFor: statNum,
|
|
1552
|
+
freesAgainst: statNum,
|
|
1553
|
+
dreamTeamPoints: statNum,
|
|
1526
1554
|
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:
|
|
1555
|
+
centreClearances: statNum,
|
|
1556
|
+
stoppageClearances: statNum,
|
|
1557
|
+
totalClearances: statNum
|
|
1558
|
+
}).passthrough().nullable().optional(),
|
|
1559
|
+
rebound50s: statNum,
|
|
1560
|
+
goalAssists: statNum,
|
|
1561
|
+
goalAccuracy: statNum,
|
|
1562
|
+
turnovers: statNum,
|
|
1563
|
+
intercepts: statNum,
|
|
1564
|
+
tacklesInside50: statNum,
|
|
1565
|
+
shotsAtGoal: statNum,
|
|
1566
|
+
metresGained: statNum,
|
|
1567
|
+
scoreInvolvements: statNum,
|
|
1568
|
+
ratingPoints: statNum,
|
|
1541
1569
|
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()
|
|
1570
|
+
effectiveDisposals: statNum,
|
|
1571
|
+
effectiveKicks: statNum,
|
|
1572
|
+
kickEfficiency: statNum,
|
|
1573
|
+
kickToHandballRatio: statNum,
|
|
1574
|
+
pressureActs: statNum,
|
|
1575
|
+
defHalfPressureActs: statNum,
|
|
1576
|
+
spoils: statNum,
|
|
1577
|
+
hitoutsToAdvantage: statNum,
|
|
1578
|
+
hitoutWinPercentage: statNum,
|
|
1579
|
+
hitoutToAdvantageRate: statNum,
|
|
1580
|
+
groundBallGets: statNum,
|
|
1581
|
+
f50GroundBallGets: statNum,
|
|
1582
|
+
interceptMarks: statNum,
|
|
1583
|
+
marksOnLead: statNum,
|
|
1584
|
+
contestedPossessionRate: statNum,
|
|
1585
|
+
contestOffOneOnOnes: statNum,
|
|
1586
|
+
contestOffWins: statNum,
|
|
1587
|
+
contestOffWinsPercentage: statNum,
|
|
1588
|
+
contestDefOneOnOnes: statNum,
|
|
1589
|
+
contestDefLosses: statNum,
|
|
1590
|
+
contestDefLossPercentage: statNum,
|
|
1591
|
+
centreBounceAttendances: statNum,
|
|
1592
|
+
kickins: statNum,
|
|
1593
|
+
kickinsPlayon: statNum,
|
|
1594
|
+
ruckContests: statNum,
|
|
1595
|
+
scoreLaunches: statNum
|
|
1596
|
+
}).passthrough().nullable().optional()
|
|
1569
1597
|
}).passthrough();
|
|
1570
1598
|
var PlayerStatsItemSchema = z.object({
|
|
1571
1599
|
player: z.object({
|
|
@@ -1578,7 +1606,7 @@ var PlayerStatsItemSchema = z.object({
|
|
|
1578
1606
|
teamId: z.string(),
|
|
1579
1607
|
playerStats: z.object({
|
|
1580
1608
|
stats: PlayerGameStatsSchema,
|
|
1581
|
-
timeOnGroundPercentage: z.number().optional()
|
|
1609
|
+
timeOnGroundPercentage: z.number().nullable().optional()
|
|
1582
1610
|
}).passthrough()
|
|
1583
1611
|
}).passthrough();
|
|
1584
1612
|
var PlayerStatsListSchema = z.object({
|
|
@@ -1680,6 +1708,7 @@ var AflApiClient = class {
|
|
|
1680
1708
|
fetchFn;
|
|
1681
1709
|
tokenUrl;
|
|
1682
1710
|
cachedToken = null;
|
|
1711
|
+
pendingAuth = null;
|
|
1683
1712
|
constructor(options) {
|
|
1684
1713
|
this.fetchFn = options?.fetchFn ?? globalThis.fetch;
|
|
1685
1714
|
this.tokenUrl = options?.tokenUrl ?? TOKEN_URL;
|
|
@@ -1687,9 +1716,21 @@ var AflApiClient = class {
|
|
|
1687
1716
|
/**
|
|
1688
1717
|
* Authenticate with the WMCTok token endpoint and cache the token.
|
|
1689
1718
|
*
|
|
1719
|
+
* Concurrent callers share the same in-flight request to avoid
|
|
1720
|
+
* redundant token fetches (thundering herd prevention).
|
|
1721
|
+
*
|
|
1690
1722
|
* @returns The access token on success, or an error Result.
|
|
1691
1723
|
*/
|
|
1692
1724
|
async authenticate() {
|
|
1725
|
+
if (this.pendingAuth) {
|
|
1726
|
+
return this.pendingAuth;
|
|
1727
|
+
}
|
|
1728
|
+
this.pendingAuth = this.doAuthenticate().finally(() => {
|
|
1729
|
+
this.pendingAuth = null;
|
|
1730
|
+
});
|
|
1731
|
+
return this.pendingAuth;
|
|
1732
|
+
}
|
|
1733
|
+
async doAuthenticate() {
|
|
1693
1734
|
try {
|
|
1694
1735
|
const response = await this.fetchFn(this.tokenUrl, {
|
|
1695
1736
|
method: "POST",
|
|
@@ -1944,7 +1985,7 @@ var AflApiClient = class {
|
|
|
1944
1985
|
return roundsResult;
|
|
1945
1986
|
}
|
|
1946
1987
|
const providerIds = roundsResult.data.flatMap((r) => r.providerId ? [r.providerId] : []);
|
|
1947
|
-
const results = await
|
|
1988
|
+
const results = await batchedMap(providerIds, (id) => this.fetchRoundMatchItems(id));
|
|
1948
1989
|
const allItems = [];
|
|
1949
1990
|
for (const result of results) {
|
|
1950
1991
|
if (!result.success) {
|
|
@@ -2278,8 +2319,9 @@ async function fetchFixture(query) {
|
|
|
2278
2319
|
const roundProviderIds = roundsResult.data.flatMap(
|
|
2279
2320
|
(r) => r.providerId ? [{ providerId: r.providerId, roundNumber: r.roundNumber }] : []
|
|
2280
2321
|
);
|
|
2281
|
-
const roundResults = await
|
|
2282
|
-
roundProviderIds
|
|
2322
|
+
const roundResults = await batchedMap(
|
|
2323
|
+
roundProviderIds,
|
|
2324
|
+
(r) => client.fetchRoundMatchItems(r.providerId)
|
|
2283
2325
|
);
|
|
2284
2326
|
const fixtures = [];
|
|
2285
2327
|
for (let i = 0; i < roundResults.length; i++) {
|
|
@@ -2696,6 +2738,7 @@ function parseAttendanceFromInfo(text) {
|
|
|
2696
2738
|
if (!match?.[1]) return null;
|
|
2697
2739
|
return Number.parseInt(match[1].replace(/,/g, ""), 10) || null;
|
|
2698
2740
|
}
|
|
2741
|
+
var GP_HEADERS = /* @__PURE__ */ new Set(["gm", "gp", "p", "mp", "games"]);
|
|
2699
2742
|
function parseAflTablesTeamStats(html, year) {
|
|
2700
2743
|
const $ = cheerio6.load(html);
|
|
2701
2744
|
const teamMap = /* @__PURE__ */ new Map();
|
|
@@ -2709,6 +2752,7 @@ function parseAflTablesTeamStats(html, year) {
|
|
|
2709
2752
|
$(rows[0]).find("td, th").each((_ci, cell) => {
|
|
2710
2753
|
headers.push($(cell).text().trim());
|
|
2711
2754
|
});
|
|
2755
|
+
const gpColIdx = headers.findIndex((h, i) => i > 0 && GP_HEADERS.has(h.toLowerCase()));
|
|
2712
2756
|
for (let ri = 1; ri < rows.length; ri++) {
|
|
2713
2757
|
const cells = $(rows[ri]).find("td");
|
|
2714
2758
|
if (cells.length < 3) continue;
|
|
@@ -2721,7 +2765,12 @@ function parseAflTablesTeamStats(html, year) {
|
|
|
2721
2765
|
}
|
|
2722
2766
|
const entry = teamMap.get(teamName);
|
|
2723
2767
|
if (!entry) continue;
|
|
2768
|
+
if (gpColIdx >= 0 && suffix === "_for") {
|
|
2769
|
+
const gpVal = Number.parseFloat($(cells[gpColIdx]).text().trim().replace(/,/g, "")) || 0;
|
|
2770
|
+
entry.gamesPlayed = gpVal;
|
|
2771
|
+
}
|
|
2724
2772
|
for (let ci = 1; ci < cells.length; ci++) {
|
|
2773
|
+
if (ci === gpColIdx) continue;
|
|
2725
2774
|
const header = headers[ci];
|
|
2726
2775
|
if (!header) continue;
|
|
2727
2776
|
const value = Number.parseFloat($(cells[ci]).text().trim().replace(/,/g, "")) || 0;
|
|
@@ -3023,8 +3072,9 @@ async function fetchLineup(query) {
|
|
|
3023
3072
|
if (matchItems.data.length === 0) {
|
|
3024
3073
|
return err(new AflApiError(`No matches found for round ${query.round}`));
|
|
3025
3074
|
}
|
|
3026
|
-
const rosterResults = await
|
|
3027
|
-
matchItems.data
|
|
3075
|
+
const rosterResults = await batchedMap(
|
|
3076
|
+
matchItems.data,
|
|
3077
|
+
(item) => client.fetchMatchRoster(item.match.matchId)
|
|
3028
3078
|
);
|
|
3029
3079
|
const lineups = [];
|
|
3030
3080
|
for (const rosterResult of rosterResults) {
|
|
@@ -3279,15 +3329,26 @@ async function fetchPlayerStats(query) {
|
|
|
3279
3329
|
case "afl-api": {
|
|
3280
3330
|
const client = new AflApiClient();
|
|
3281
3331
|
if (query.matchId) {
|
|
3282
|
-
const
|
|
3283
|
-
|
|
3332
|
+
const [rosterResult, statsResult] = await Promise.all([
|
|
3333
|
+
client.fetchMatchRoster(query.matchId),
|
|
3334
|
+
client.fetchPlayerStats(query.matchId)
|
|
3335
|
+
]);
|
|
3336
|
+
if (!statsResult.success) return statsResult;
|
|
3337
|
+
const teamIdMap2 = /* @__PURE__ */ new Map();
|
|
3338
|
+
if (rosterResult.success) {
|
|
3339
|
+
const match = rosterResult.data.match;
|
|
3340
|
+
teamIdMap2.set(match.homeTeamId, match.homeTeam.name);
|
|
3341
|
+
teamIdMap2.set(match.awayTeamId, match.awayTeam.name);
|
|
3342
|
+
}
|
|
3284
3343
|
return ok(
|
|
3285
3344
|
transformPlayerStats(
|
|
3286
|
-
|
|
3345
|
+
statsResult.data,
|
|
3287
3346
|
query.matchId,
|
|
3288
3347
|
query.season,
|
|
3289
3348
|
query.round ?? 0,
|
|
3290
|
-
competition
|
|
3349
|
+
competition,
|
|
3350
|
+
"afl-api",
|
|
3351
|
+
teamIdMap2.size > 0 ? teamIdMap2 : void 0
|
|
3291
3352
|
)
|
|
3292
3353
|
);
|
|
3293
3354
|
}
|
|
@@ -3304,8 +3365,9 @@ async function fetchPlayerStats(query) {
|
|
|
3304
3365
|
teamIdMap.set(item.match.homeTeamId, item.match.homeTeam.name);
|
|
3305
3366
|
teamIdMap.set(item.match.awayTeamId, item.match.awayTeam.name);
|
|
3306
3367
|
}
|
|
3307
|
-
const statsResults = await
|
|
3308
|
-
matchItemsResult.data
|
|
3368
|
+
const statsResults = await batchedMap(
|
|
3369
|
+
matchItemsResult.data,
|
|
3370
|
+
(item) => client.fetchPlayerStats(item.match.matchId)
|
|
3309
3371
|
);
|
|
3310
3372
|
const allStats = [];
|
|
3311
3373
|
for (let i = 0; i < statsResults.length; i++) {
|