fitzroy 1.4.2 → 1.6.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.js CHANGED
@@ -489,6 +489,9 @@ function mergeFootyWireStats(basicTeams, advancedTeams, matchId, season, roundNu
489
489
  roundNumber,
490
490
  team: teamName,
491
491
  competition: "AFLM",
492
+ date: null,
493
+ homeTeam: null,
494
+ awayTeam: null,
492
495
  playerId: `FW_${basic.player.replace(/\s+/g, "_")}`,
493
496
  givenName: firstName,
494
497
  surname,
@@ -528,6 +531,12 @@ function mergeFootyWireStats(basicTeams, advancedTeams, matchId, season, roundNu
528
531
  totalPossessions: null,
529
532
  timeOnGroundPercentage: adv?.timeOnGroundPercentage ?? null,
530
533
  ratingPoints: null,
534
+ position: null,
535
+ goalEfficiency: null,
536
+ shotEfficiency: null,
537
+ interchangeCounts: null,
538
+ brownlowVotes: null,
539
+ supercoachScore: basic.supercoachPoints,
531
540
  dreamTeamPoints: basic.dreamTeamPoints,
532
541
  effectiveDisposals: adv?.effectiveDisposals ?? null,
533
542
  effectiveKicks: null,
@@ -564,6 +573,22 @@ function mergeFootyWireStats(basicTeams, advancedTeams, matchId, season, roundNu
564
573
 
565
574
  // src/transforms/match-results.ts
566
575
  var FINALS_PATTERN = /final|elimination|qualifying|preliminary|semi|grand/i;
576
+ var ROUND_CODE_MAP = /* @__PURE__ */ new Map([
577
+ ["Qualifying Final", "QF"],
578
+ ["Elimination Final", "EF"],
579
+ ["Semi Final", "SF"],
580
+ ["Preliminary Final", "PF"],
581
+ ["Grand Final", "GF"]
582
+ ]);
583
+ var ROUND_NUMBER_PATTERN = /^Round\s+(\d+)$/i;
584
+ function toRoundCode(roundName) {
585
+ if (!roundName) return null;
586
+ const mapped = ROUND_CODE_MAP.get(roundName);
587
+ if (mapped) return mapped;
588
+ const m = ROUND_NUMBER_PATTERN.exec(roundName);
589
+ if (m?.[1]) return `R${m[1]}`;
590
+ return roundName;
591
+ }
567
592
  function inferRoundType(roundName) {
568
593
  return FINALS_PATTERN.test(roundName) ? "Finals" : "HomeAndAway";
569
594
  }
@@ -617,6 +642,7 @@ function transformMatchItems(items, season, competition, source = "afl-api") {
617
642
  season,
618
643
  roundNumber: item.round?.roundNumber ?? 0,
619
644
  roundType: inferRoundType(item.round?.name ?? ""),
645
+ roundName: item.round?.name ?? null,
620
646
  date: new Date(item.match.utcStartTime),
621
647
  venue: item.venue?.name ? normaliseVenueName(item.venue.name) : "",
622
648
  homeTeam: normaliseTeamName(item.match.homeTeam.name),
@@ -637,7 +663,10 @@ function transformMatchItems(items, season, competition, source = "afl-api") {
637
663
  q3Away: findPeriod(awayScore?.periodScore, 3),
638
664
  q4Away: findPeriod(awayScore?.periodScore, 4),
639
665
  status: toMatchStatus(item.match.status),
640
- attendance: null,
666
+ attendance: item.attendance ?? null,
667
+ weatherTempCelsius: item.weather?.tempInCelsius ?? null,
668
+ weatherType: item.weather?.weatherType ?? null,
669
+ roundCode: toRoundCode(item.round?.name),
641
670
  venueState: item.venue?.state ?? null,
642
671
  venueTimezone: item.venue?.timeZone ?? null,
643
672
  homeRushedBehinds: homeScore?.rushedBehinds ?? null,
@@ -750,10 +779,10 @@ var FootyWireClient = class {
750
779
  /**
751
780
  * Fetch match IDs from a season's match list page.
752
781
  *
753
- * Extracts `mid=XXXX` values from score links.
782
+ * Extracts `mid=XXXX` values from score links alongside round numbers.
754
783
  *
755
784
  * @param year - The season year.
756
- * @returns Array of match ID strings.
785
+ * @returns Array of match ID + round number pairs.
757
786
  */
758
787
  async fetchSeasonMatchIds(year) {
759
788
  const url = `${FOOTYWIRE_BASE}/ft_match_list?year=${year}`;
@@ -761,15 +790,33 @@ var FootyWireClient = class {
761
790
  if (!htmlResult.success) return htmlResult;
762
791
  try {
763
792
  const $ = cheerio2.load(htmlResult.data);
764
- const ids = [];
765
- $(".data:nth-child(5) a").each((_i, el) => {
766
- const href = $(el).attr("href") ?? "";
767
- const match = /mid=(\d+)/.exec(href);
768
- if (match?.[1]) {
769
- ids.push(match[1]);
793
+ const entries = [];
794
+ let currentRound = 0;
795
+ let lastHARound = 0;
796
+ $("tr").each((_i, row) => {
797
+ const roundHeader = $(row).find("td[colspan='7']");
798
+ if (roundHeader.length > 0) {
799
+ const text = roundHeader.text().trim();
800
+ const roundMatch = /Round\s+(\d+)/i.exec(text);
801
+ if (roundMatch?.[1]) {
802
+ currentRound = Number.parseInt(roundMatch[1], 10);
803
+ if (inferRoundType(text) === "HomeAndAway") {
804
+ lastHARound = currentRound;
805
+ }
806
+ } else if (inferRoundType(text) === "Finals") {
807
+ currentRound = finalsRoundNumber(text, lastHARound);
808
+ }
809
+ return;
810
+ }
811
+ const scoreLink = $(row).find(".data:nth-child(5) a");
812
+ if (scoreLink.length === 0) return;
813
+ const href = scoreLink.attr("href") ?? "";
814
+ const midMatch = /mid=(\d+)/.exec(href);
815
+ if (midMatch?.[1]) {
816
+ entries.push({ matchId: midMatch[1], roundNumber: currentRound });
770
817
  }
771
818
  });
772
- return ok(ids);
819
+ return ok(entries);
773
820
  } catch (cause) {
774
821
  return err(
775
822
  new ScrapeError(
@@ -873,10 +920,12 @@ function parseMatchList(html, year) {
873
920
  let currentRound = 0;
874
921
  let lastHARound = 0;
875
922
  let currentRoundType = "HomeAndAway";
923
+ let currentRoundName = "";
876
924
  $("tr").each((_i, row) => {
877
925
  const roundHeader = $(row).find("td[colspan='7']");
878
926
  if (roundHeader.length > 0) {
879
927
  const text = roundHeader.text().trim();
928
+ currentRoundName = text;
880
929
  currentRoundType = inferRoundType(text);
881
930
  const roundMatch = /Round\s+(\d+)/i.exec(text);
882
931
  if (roundMatch?.[1]) {
@@ -919,6 +968,7 @@ function parseMatchList(html, year) {
919
968
  season: year,
920
969
  roundNumber: currentRound,
921
970
  roundType: currentRoundType,
971
+ roundName: currentRoundName || null,
922
972
  date,
923
973
  venue: normaliseVenueName(venue),
924
974
  homeTeam,
@@ -940,6 +990,9 @@ function parseMatchList(html, year) {
940
990
  q4Away: null,
941
991
  status: "Complete",
942
992
  attendance: attendance ? Number.parseInt(attendance, 10) || null : null,
993
+ weatherTempCelsius: null,
994
+ weatherType: null,
995
+ roundCode: toRoundCode(currentRoundName),
943
996
  venueState: null,
944
997
  venueTimezone: null,
945
998
  homeRushedBehinds: null,
@@ -1605,6 +1658,10 @@ var CfsVenueSchema = z.object({
1605
1658
  state: z.string().optional(),
1606
1659
  timeZone: z.string().optional()
1607
1660
  }).passthrough();
1661
+ var CfsWeatherSchema = z.object({
1662
+ tempInCelsius: z.number().nullable().optional(),
1663
+ weatherType: z.string().nullable().optional()
1664
+ }).passthrough();
1608
1665
  var MatchItemSchema = z.object({
1609
1666
  match: CfsMatchSchema,
1610
1667
  score: CfsScoreSchema.nullish(),
@@ -1613,7 +1670,9 @@ var MatchItemSchema = z.object({
1613
1670
  name: z.string(),
1614
1671
  roundId: z.string(),
1615
1672
  roundNumber: z.number()
1616
- }).passthrough().optional()
1673
+ }).passthrough().optional(),
1674
+ attendance: z.number().nullable().optional(),
1675
+ weather: CfsWeatherSchema.nullable().optional()
1617
1676
  }).passthrough();
1618
1677
  var MatchItemListSchema = z.object({
1619
1678
  roundId: z.string().optional(),
@@ -1674,6 +1733,10 @@ var PlayerGameStatsSchema = z.object({
1674
1733
  metresGained: statNum,
1675
1734
  scoreInvolvements: statNum,
1676
1735
  ratingPoints: statNum,
1736
+ goalEfficiency: statNum,
1737
+ shotEfficiency: statNum,
1738
+ interchangeCounts: statNum,
1739
+ brownlowVotes: statNum,
1677
1740
  extendedStats: z.object({
1678
1741
  effectiveDisposals: statNum,
1679
1742
  effectiveKicks: statNum,
@@ -1718,8 +1781,8 @@ var PlayerStatsItemSchema = z.object({
1718
1781
  }).passthrough().nullable().optional()
1719
1782
  }).passthrough();
1720
1783
  var PlayerStatsListSchema = z.object({
1721
- homeTeamPlayerStats: z.array(PlayerStatsItemSchema),
1722
- awayTeamPlayerStats: z.array(PlayerStatsItemSchema)
1784
+ homeTeamPlayerStats: z.array(PlayerStatsItemSchema).nullable().default([]),
1785
+ awayTeamPlayerStats: z.array(PlayerStatsItemSchema).nullable().default([])
1723
1786
  }).passthrough();
1724
1787
  var RosterPlayerSchema = z.object({
1725
1788
  player: z.object({
@@ -2313,6 +2376,7 @@ function transformSquiggleGamesToResults(games, season) {
2313
2376
  season,
2314
2377
  roundNumber: g.round,
2315
2378
  roundType: inferRoundType(g.roundname),
2379
+ roundName: g.roundname || null,
2316
2380
  date: new Date(g.unixtime * 1e3),
2317
2381
  venue: normaliseVenueName(g.venue),
2318
2382
  homeTeam: normaliseTeamName(g.hteam),
@@ -2334,6 +2398,9 @@ function transformSquiggleGamesToResults(games, season) {
2334
2398
  q4Away: null,
2335
2399
  status: "Complete",
2336
2400
  attendance: null,
2401
+ weatherTempCelsius: null,
2402
+ weatherType: null,
2403
+ roundCode: toRoundCode(g.roundname),
2337
2404
  venueState: null,
2338
2405
  venueTimezone: g.tz || null,
2339
2406
  homeRushedBehinds: null,
@@ -2481,6 +2548,9 @@ function parseAflTablesGameStats(html, matchId, season, roundNumber) {
2481
2548
  roundNumber,
2482
2549
  team: teamName,
2483
2550
  competition: "AFLM",
2551
+ date: null,
2552
+ homeTeam: null,
2553
+ awayTeam: null,
2484
2554
  playerId: `AT_${displayName.replace(/\s+/g, "_")}`,
2485
2555
  givenName,
2486
2556
  surname,
@@ -2520,6 +2590,12 @@ function parseAflTablesGameStats(html, matchId, season, roundNumber) {
2520
2590
  totalPossessions: null,
2521
2591
  timeOnGroundPercentage: safeInt(cells[24] ?? ""),
2522
2592
  ratingPoints: null,
2593
+ position: null,
2594
+ goalEfficiency: null,
2595
+ shotEfficiency: null,
2596
+ interchangeCounts: null,
2597
+ brownlowVotes: null,
2598
+ supercoachScore: null,
2523
2599
  dreamTeamPoints: null,
2524
2600
  effectiveDisposals: null,
2525
2601
  effectiveKicks: null,
@@ -2743,6 +2819,7 @@ function parseSeasonPage(html, year) {
2743
2819
  const results = [];
2744
2820
  let currentRound = 0;
2745
2821
  let currentRoundType = "HomeAndAway";
2822
+ let currentRoundName = "";
2746
2823
  let lastHARound = 0;
2747
2824
  let matchCounter = 0;
2748
2825
  $("table").each((_i, table) => {
@@ -2753,6 +2830,7 @@ function parseSeasonPage(html, year) {
2753
2830
  if (roundMatch?.[1] && border !== "1") {
2754
2831
  currentRound = Number.parseInt(roundMatch[1], 10);
2755
2832
  currentRoundType = inferRoundType(text);
2833
+ currentRoundName = text;
2756
2834
  if (currentRoundType === "HomeAndAway") {
2757
2835
  lastHARound = currentRound;
2758
2836
  }
@@ -2760,6 +2838,7 @@ function parseSeasonPage(html, year) {
2760
2838
  }
2761
2839
  if (border !== "1" && inferRoundType(text) === "Finals") {
2762
2840
  currentRoundType = "Finals";
2841
+ currentRoundName = text;
2763
2842
  currentRound = finalsRoundNumber(text, lastHARound);
2764
2843
  return;
2765
2844
  }
@@ -2790,6 +2869,7 @@ function parseSeasonPage(html, year) {
2790
2869
  season: year,
2791
2870
  roundNumber: currentRound,
2792
2871
  roundType: currentRoundType,
2872
+ roundName: currentRoundName || null,
2793
2873
  date,
2794
2874
  venue,
2795
2875
  homeTeam,
@@ -2811,6 +2891,9 @@ function parseSeasonPage(html, year) {
2811
2891
  q4Away: awayQuarters[3] ?? null,
2812
2892
  status: "Complete",
2813
2893
  attendance,
2894
+ weatherTempCelsius: null,
2895
+ weatherType: null,
2896
+ roundCode: toRoundCode(currentRoundName),
2814
2897
  venueState: null,
2815
2898
  venueTimezone: null,
2816
2899
  homeRushedBehinds: null,
@@ -3265,29 +3348,14 @@ async function resolveTeamId(client, teamName, competition) {
3265
3348
  }
3266
3349
  return ok(String(match.id));
3267
3350
  }
3268
- async function fetchFromAflApi(query) {
3269
- const client = new AflApiClient();
3270
- const competition = query.competition ?? "AFLM";
3271
- const season = query.season ?? resolveDefaultSeason(competition);
3272
- const [teamIdResult, seasonResult] = await Promise.all([
3273
- resolveTeamId(client, query.team, competition),
3274
- client.resolveCompSeason(competition, season)
3275
- ]);
3276
- if (!teamIdResult.success) return teamIdResult;
3277
- if (!seasonResult.success) return seasonResult;
3278
- const teamId = Number.parseInt(teamIdResult.data, 10);
3279
- if (Number.isNaN(teamId)) {
3280
- return err(new ValidationError(`Invalid team ID: ${teamIdResult.data}`));
3281
- }
3282
- const squadResult = await client.fetchSquad(teamId, seasonResult.data);
3283
- if (!squadResult.success) return squadResult;
3284
- const teamName = normaliseTeamName(squadResult.data.squad.team?.name ?? query.team);
3285
- const players = squadResult.data.squad.players.map((p) => ({
3351
+ function mapSquadToPlayerDetails(data, fallbackTeamName, competition) {
3352
+ const resolvedName = normaliseTeamName(data.squad.team?.name ?? fallbackTeamName);
3353
+ return data.squad.players.map((p) => ({
3286
3354
  playerId: p.player.providerId ?? String(p.player.id),
3287
3355
  givenName: p.player.firstName,
3288
3356
  surname: p.player.surname,
3289
3357
  displayName: `${p.player.firstName} ${p.player.surname}`,
3290
- team: teamName,
3358
+ team: resolvedName,
3291
3359
  jumperNumber: p.jumperNumber ?? null,
3292
3360
  position: p.position ?? null,
3293
3361
  dateOfBirth: p.player.dateOfBirth ?? null,
@@ -3303,35 +3371,83 @@ async function fetchFromAflApi(query) {
3303
3371
  source: "afl-api",
3304
3372
  competition
3305
3373
  }));
3306
- return ok(players);
3374
+ }
3375
+ async function fetchFromAflApi(query) {
3376
+ const client = new AflApiClient();
3377
+ const competition = query.competition ?? "AFLM";
3378
+ const season = query.season ?? resolveDefaultSeason(competition);
3379
+ const seasonResult = await client.resolveCompSeason(competition, season);
3380
+ if (!seasonResult.success) return seasonResult;
3381
+ if (query.team) {
3382
+ const teamIdResult = await resolveTeamId(client, query.team, competition);
3383
+ if (!teamIdResult.success) return teamIdResult;
3384
+ const teamId = Number.parseInt(teamIdResult.data, 10);
3385
+ if (Number.isNaN(teamId)) {
3386
+ return err(new ValidationError(`Invalid team ID: ${teamIdResult.data}`));
3387
+ }
3388
+ const squadResult = await client.fetchSquad(teamId, seasonResult.data);
3389
+ if (!squadResult.success) return squadResult;
3390
+ return ok(mapSquadToPlayerDetails(squadResult.data, query.team, competition));
3391
+ }
3392
+ const teamType = competition === "AFLW" ? "WOMEN" : "MEN";
3393
+ const teamsResult = await client.fetchTeams(teamType);
3394
+ if (!teamsResult.success) return teamsResult;
3395
+ const teamEntries = teamsResult.data.map((t) => ({
3396
+ id: Number.parseInt(String(t.id), 10),
3397
+ name: normaliseTeamName(t.name)
3398
+ }));
3399
+ const results = await batchedMap(
3400
+ teamEntries,
3401
+ (entry) => client.fetchSquad(entry.id, seasonResult.data)
3402
+ );
3403
+ const allPlayers = [];
3404
+ for (let i = 0; i < results.length; i++) {
3405
+ const result = results[i];
3406
+ const entry = teamEntries[i];
3407
+ if (result?.success && entry) {
3408
+ allPlayers.push(...mapSquadToPlayerDetails(result.data, entry.name, competition));
3409
+ }
3410
+ }
3411
+ return ok(allPlayers);
3412
+ }
3413
+ async function fetchAllTeamsFromScraper(fetchFn, source, competition) {
3414
+ const teamNames = [...AFL_SENIOR_TEAMS];
3415
+ const results = await batchedMap(teamNames, (name) => fetchFn(name));
3416
+ const allPlayers = [];
3417
+ for (const result of results) {
3418
+ if (result.success) {
3419
+ allPlayers.push(...result.data.map((p) => ({ ...p, source, competition })));
3420
+ }
3421
+ }
3422
+ return ok(allPlayers);
3307
3423
  }
3308
3424
  async function fetchFromFootyWire(query) {
3309
3425
  const competition = query.competition ?? "AFLM";
3310
3426
  if (competition === "AFLW") return err(aflwUnsupportedError("footywire"));
3311
3427
  const client = new FootyWireClient();
3312
- const teamName = normaliseTeamName(query.team);
3313
- const result = await client.fetchPlayerList(teamName);
3314
- if (!result.success) return result;
3315
- const players = result.data.map((p) => ({
3316
- ...p,
3317
- source: "footywire",
3318
- competition
3319
- }));
3320
- return ok(players);
3428
+ if (query.team) {
3429
+ const teamName = normaliseTeamName(query.team);
3430
+ const result = await client.fetchPlayerList(teamName);
3431
+ if (!result.success) return result;
3432
+ return ok(result.data.map((p) => ({ ...p, source: "footywire", competition })));
3433
+ }
3434
+ return fetchAllTeamsFromScraper((name) => client.fetchPlayerList(name), "footywire", competition);
3321
3435
  }
3322
3436
  async function fetchFromAflTables(query) {
3323
3437
  const competition = query.competition ?? "AFLM";
3324
3438
  if (competition === "AFLW") return err(aflwUnsupportedError("afl-tables"));
3325
3439
  const client = new AflTablesClient();
3326
- const teamName = normaliseTeamName(query.team);
3327
- const result = await client.fetchPlayerList(teamName);
3328
- if (!result.success) return result;
3329
- const players = result.data.map((p) => ({
3330
- ...p,
3331
- source: "afl-tables",
3440
+ if (query.team) {
3441
+ const teamName = normaliseTeamName(query.team);
3442
+ const result = await client.fetchPlayerList(teamName);
3443
+ if (!result.success) return result;
3444
+ return ok(result.data.map((p) => ({ ...p, source: "afl-tables", competition })));
3445
+ }
3446
+ return fetchAllTeamsFromScraper(
3447
+ (name) => client.fetchPlayerList(name),
3448
+ "afl-tables",
3332
3449
  competition
3333
- }));
3334
- return ok(players);
3450
+ );
3335
3451
  }
3336
3452
  async function fetchPlayerDetails(query) {
3337
3453
  switch (query.source) {
@@ -3355,18 +3471,21 @@ async function fetchPlayerDetails(query) {
3355
3471
  function toNullable(value) {
3356
3472
  return value ?? null;
3357
3473
  }
3358
- function transformOne(item, matchId, season, roundNumber, competition, source, teamIdMap) {
3474
+ function transformOne(item, ctx) {
3359
3475
  const inner = item.player.player.player;
3360
3476
  const stats = item.playerStats?.stats;
3361
3477
  const clearances = stats?.clearances;
3362
3478
  return {
3363
- matchId,
3364
- season,
3365
- roundNumber,
3479
+ matchId: ctx.matchId,
3480
+ season: ctx.season,
3481
+ roundNumber: ctx.roundNumber,
3366
3482
  team: normaliseTeamName(
3367
- teamIdMap?.get(item.teamId) ?? AFL_API_TEAM_IDS.get(item.teamId) ?? item.teamId
3483
+ ctx.teamIdMap?.get(item.teamId) ?? AFL_API_TEAM_IDS.get(item.teamId) ?? item.teamId
3368
3484
  ),
3369
- competition,
3485
+ competition: ctx.competition,
3486
+ date: ctx.date ?? null,
3487
+ homeTeam: ctx.homeTeam ?? null,
3488
+ awayTeam: ctx.awayTeam ?? null,
3370
3489
  playerId: inner.playerId,
3371
3490
  givenName: inner.playerName.givenName,
3372
3491
  surname: inner.playerName.surname,
@@ -3406,6 +3525,12 @@ function transformOne(item, matchId, season, roundNumber, competition, source, t
3406
3525
  totalPossessions: toNullable(stats?.totalPossessions),
3407
3526
  timeOnGroundPercentage: toNullable(item.playerStats?.timeOnGroundPercentage),
3408
3527
  ratingPoints: toNullable(stats?.ratingPoints),
3528
+ position: item.player.player.position ?? null,
3529
+ goalEfficiency: toNullable(stats?.goalEfficiency),
3530
+ shotEfficiency: toNullable(stats?.shotEfficiency),
3531
+ interchangeCounts: toNullable(stats?.interchangeCounts),
3532
+ brownlowVotes: toNullable(stats?.brownlowVotes),
3533
+ supercoachScore: null,
3409
3534
  dreamTeamPoints: toNullable(stats?.dreamTeamPoints),
3410
3535
  effectiveDisposals: toNullable(stats?.extendedStats?.effectiveDisposals),
3411
3536
  effectiveKicks: toNullable(stats?.extendedStats?.effectiveKicks),
@@ -3433,16 +3558,12 @@ function transformOne(item, matchId, season, roundNumber, competition, source, t
3433
3558
  kickinsPlayon: toNullable(stats?.extendedStats?.kickinsPlayon),
3434
3559
  ruckContests: toNullable(stats?.extendedStats?.ruckContests),
3435
3560
  scoreLaunches: toNullable(stats?.extendedStats?.scoreLaunches),
3436
- source
3561
+ source: ctx.source
3437
3562
  };
3438
3563
  }
3439
- function transformPlayerStats(data, matchId, season, roundNumber, competition, source = "afl-api", teamIdMap) {
3440
- const home = data.homeTeamPlayerStats.map(
3441
- (item) => transformOne(item, matchId, season, roundNumber, competition, source, teamIdMap)
3442
- );
3443
- const away = data.awayTeamPlayerStats.map(
3444
- (item) => transformOne(item, matchId, season, roundNumber, competition, source, teamIdMap)
3445
- );
3564
+ function transformPlayerStats(data, ctx) {
3565
+ const home = (data.homeTeamPlayerStats ?? []).map((item) => transformOne(item, ctx));
3566
+ const away = (data.awayTeamPlayerStats ?? []).map((item) => transformOne(item, ctx));
3446
3567
  return [...home, ...away];
3447
3568
  }
3448
3569
 
@@ -3465,24 +3586,19 @@ async function fetchPlayerStats(query) {
3465
3586
  teamIdMap2.set(match.awayTeamId, normaliseTeamName(match.awayTeam.name));
3466
3587
  }
3467
3588
  return ok(
3468
- transformPlayerStats(
3469
- statsResult.data,
3470
- query.matchId,
3471
- query.season,
3472
- query.round ?? 0,
3589
+ transformPlayerStats(statsResult.data, {
3590
+ matchId: query.matchId,
3591
+ season: query.season,
3592
+ roundNumber: query.round ?? 0,
3473
3593
  competition,
3474
- "afl-api",
3475
- teamIdMap2
3476
- )
3594
+ source: "afl-api",
3595
+ teamIdMap: teamIdMap2
3596
+ })
3477
3597
  );
3478
3598
  }
3479
3599
  const seasonResult = await client.resolveCompSeason(competition, query.season);
3480
3600
  if (!seasonResult.success) return seasonResult;
3481
- const roundNumber = query.round ?? 1;
3482
- const matchItemsResult = await client.fetchRoundMatchItemsByNumber(
3483
- seasonResult.data,
3484
- roundNumber
3485
- );
3601
+ const matchItemsResult = query.round != null ? await client.fetchRoundMatchItemsByNumber(seasonResult.data, query.round) : await client.fetchSeasonMatchItems(seasonResult.data);
3486
3602
  if (!matchItemsResult.success) return matchItemsResult;
3487
3603
  const teamIdMap = /* @__PURE__ */ new Map();
3488
3604
  for (const item of matchItemsResult.data) {
@@ -3501,15 +3617,17 @@ async function fetchPlayerStats(query) {
3501
3617
  const item = matchItemsResult.data[i];
3502
3618
  if (!item) continue;
3503
3619
  allStats.push(
3504
- ...transformPlayerStats(
3505
- statsResult.data,
3506
- item.match.matchId,
3507
- query.season,
3508
- roundNumber,
3620
+ ...transformPlayerStats(statsResult.data, {
3621
+ matchId: item.match.matchId,
3622
+ season: query.season,
3623
+ roundNumber: item.round?.roundNumber ?? query.round ?? 0,
3509
3624
  competition,
3510
- "afl-api",
3511
- teamIdMap
3512
- )
3625
+ source: "afl-api",
3626
+ teamIdMap,
3627
+ date: new Date(item.match.utcStartTime),
3628
+ homeTeam: normaliseTeamName(item.match.homeTeam.name),
3629
+ awayTeam: normaliseTeamName(item.match.awayTeam.name)
3630
+ })
3513
3631
  );
3514
3632
  }
3515
3633
  return ok(allStats);
@@ -3519,29 +3637,26 @@ async function fetchPlayerStats(query) {
3519
3637
  const fwClient = new FootyWireClient();
3520
3638
  const idsResult = await fwClient.fetchSeasonMatchIds(query.season);
3521
3639
  if (!idsResult.success) return idsResult;
3522
- const matchIds = idsResult.data;
3523
- if (matchIds.length === 0) {
3640
+ const entries = query.round != null ? idsResult.data.filter((e) => e.roundNumber === query.round) : idsResult.data;
3641
+ if (entries.length === 0) {
3524
3642
  return ok([]);
3525
3643
  }
3526
3644
  const allStats = [];
3527
3645
  const batchSize = 5;
3528
- for (let i = 0; i < matchIds.length; i += batchSize) {
3529
- const batch = matchIds.slice(i, i + batchSize);
3646
+ for (let i = 0; i < entries.length; i += batchSize) {
3647
+ const batch = entries.slice(i, i + batchSize);
3530
3648
  const results = await Promise.all(
3531
- batch.map((mid) => fwClient.fetchMatchPlayerStats(mid, query.season, query.round ?? 0))
3649
+ batch.map((e) => fwClient.fetchMatchPlayerStats(e.matchId, query.season, e.roundNumber))
3532
3650
  );
3533
3651
  for (const result of results) {
3534
3652
  if (result.success) {
3535
3653
  allStats.push(...result.data);
3536
3654
  }
3537
3655
  }
3538
- if (i + batchSize < matchIds.length) {
3656
+ if (i + batchSize < entries.length) {
3539
3657
  await new Promise((resolve) => setTimeout(resolve, 500));
3540
3658
  }
3541
3659
  }
3542
- if (query.round != null) {
3543
- return ok(allStats.filter((s) => s.roundNumber === query.round));
3544
- }
3545
3660
  return ok(allStats);
3546
3661
  }
3547
3662
  case "afl-tables": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fitzroy",
3
- "version": "1.4.2",
3
+ "version": "1.6.0",
4
4
  "description": "TypeScript port of the fitzRoy R package — programmatic access to AFL data including match results, player stats, fixtures, ladders, and more",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",