fitzroy 1.4.0 → 1.4.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.
Files changed (3) hide show
  1. package/dist/cli.js +80 -30
  2. package/dist/index.js +29 -29
  3. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -315,27 +315,6 @@ var init_team_mapping = __esm({
315
315
  }
316
316
  });
317
317
 
318
- // src/lib/parse-utils.ts
319
- function safeInt(text) {
320
- const cleaned = text.replace(/[^0-9-]/g, "").trim();
321
- if (!cleaned) return null;
322
- const n = Number.parseInt(cleaned, 10);
323
- return Number.isNaN(n) ? null : n;
324
- }
325
- function parseIntOr0(text) {
326
- const n = Number.parseInt(text.replace(/[^0-9-]/g, ""), 10);
327
- return Number.isNaN(n) ? 0 : n;
328
- }
329
- function parseFloatOr0(text) {
330
- const n = Number.parseFloat(text.replace(/[^0-9.-]/g, ""));
331
- return Number.isNaN(n) ? 0 : n;
332
- }
333
- var init_parse_utils = __esm({
334
- "src/lib/parse-utils.ts"() {
335
- "use strict";
336
- }
337
- });
338
-
339
318
  // src/lib/venue-mapping.ts
340
319
  function normaliseVenueName(raw) {
341
320
  const trimmed = raw.trim();
@@ -392,6 +371,27 @@ var init_venue_mapping = __esm({
392
371
  }
393
372
  });
394
373
 
374
+ // src/lib/parse-utils.ts
375
+ function safeInt(text) {
376
+ const cleaned = text.replace(/[^0-9-]/g, "").trim();
377
+ if (!cleaned) return null;
378
+ const n = Number.parseInt(cleaned, 10);
379
+ return Number.isNaN(n) ? null : n;
380
+ }
381
+ function parseIntOr0(text) {
382
+ const n = Number.parseInt(text.replace(/[^0-9-]/g, ""), 10);
383
+ return Number.isNaN(n) ? 0 : n;
384
+ }
385
+ function parseFloatOr0(text) {
386
+ const n = Number.parseFloat(text.replace(/[^0-9.-]/g, ""));
387
+ return Number.isNaN(n) ? 0 : n;
388
+ }
389
+ var init_parse_utils = __esm({
390
+ "src/lib/parse-utils.ts"() {
391
+ "use strict";
392
+ }
393
+ });
394
+
395
395
  // src/transforms/footywire-player-stats.ts
396
396
  import * as cheerio from "cheerio";
397
397
  function cleanPlayerName(raw) {
@@ -413,7 +413,7 @@ function parseStatsTable(html, expectedCols, rowParser) {
413
413
  const headerText = teamHeader.text().trim();
414
414
  const match = /^(\w[\w\s]+?)\s+Match Statistics/i.exec(headerText);
415
415
  if (match?.[1]) {
416
- teamName = match[1].trim();
416
+ teamName = normaliseTeamName(match[1].trim());
417
417
  }
418
418
  }
419
419
  const parsed = [];
@@ -586,6 +586,7 @@ var init_footywire_player_stats = __esm({
586
586
  "src/transforms/footywire-player-stats.ts"() {
587
587
  "use strict";
588
588
  init_parse_utils();
589
+ init_team_mapping();
589
590
  BASIC_COLS = [
590
591
  "Player",
591
592
  "K",
@@ -780,7 +781,7 @@ function parseMatchList(html, year) {
780
781
  roundNumber: currentRound,
781
782
  roundType: currentRoundType,
782
783
  date,
783
- venue,
784
+ venue: normaliseVenueName(venue),
784
785
  homeTeam,
785
786
  awayTeam,
786
787
  homeGoals,
@@ -856,7 +857,7 @@ function parseFixtureList(html, year) {
856
857
  roundNumber: currentRound,
857
858
  roundType: currentRoundType,
858
859
  date,
859
- venue,
860
+ venue: normaliseVenueName(venue),
860
861
  homeTeam,
861
862
  awayTeam,
862
863
  status: hasScore ? "Complete" : "Upcoming",
@@ -1002,6 +1003,7 @@ var init_footywire = __esm({
1002
1003
  init_errors();
1003
1004
  init_result();
1004
1005
  init_team_mapping();
1006
+ init_venue_mapping();
1005
1007
  init_footywire_player_stats();
1006
1008
  init_match_results();
1007
1009
  FOOTYWIRE_BASE = "https://www.footywire.com/afl/footy";
@@ -2329,7 +2331,7 @@ function transformSquiggleGamesToFixture(games, season) {
2329
2331
  roundNumber: g.round,
2330
2332
  roundType: inferRoundType(g.roundname),
2331
2333
  date: new Date(g.unixtime * 1e3),
2332
- venue: g.venue,
2334
+ venue: normaliseVenueName(g.venue),
2333
2335
  homeTeam: normaliseTeamName(g.hteam),
2334
2336
  awayTeam: normaliseTeamName(g.ateam),
2335
2337
  status: toMatchStatus2(g.complete),
@@ -2368,7 +2370,7 @@ function toFixture(item, season, fallbackRoundNumber, competition) {
2368
2370
  roundNumber: item.round?.roundNumber ?? fallbackRoundNumber,
2369
2371
  roundType: inferRoundType(item.round?.name ?? ""),
2370
2372
  date: new Date(item.match.utcStartTime),
2371
- venue: item.venue?.name ?? "",
2373
+ venue: normaliseVenueName(item.venue?.name ?? ""),
2372
2374
  homeTeam: normaliseTeamName(item.match.homeTeam.name),
2373
2375
  awayTeam: normaliseTeamName(item.match.awayTeam.name),
2374
2376
  status: toMatchStatus(item.match.status),
@@ -2437,6 +2439,7 @@ var init_fixture = __esm({
2437
2439
  init_errors();
2438
2440
  init_result();
2439
2441
  init_team_mapping();
2442
+ init_venue_mapping();
2440
2443
  init_afl_api();
2441
2444
  init_footywire();
2442
2445
  init_squiggle();
@@ -2613,7 +2616,7 @@ function parseSeasonPage(html, year) {
2613
2616
  const awayPoints = Number.parseInt($(awayCells[2]).text().trim(), 10) || 0;
2614
2617
  const infoText = $(homeCells[3]).text().trim();
2615
2618
  const date = parseDateFromInfo(infoText, year);
2616
- const venue = parseVenueFromInfo($(homeCells[3]).html() ?? "");
2619
+ const venue = normaliseVenueName(parseVenueFromInfo($(homeCells[3]).html() ?? ""));
2617
2620
  const attendance = parseAttendanceFromInfo(infoText);
2618
2621
  const homeFinal = homeQuarters[3];
2619
2622
  const awayFinal = awayQuarters[3];
@@ -2801,6 +2804,7 @@ var init_afl_tables = __esm({
2801
2804
  init_errors();
2802
2805
  init_result();
2803
2806
  init_team_mapping();
2807
+ init_venue_mapping();
2804
2808
  init_afl_tables_player_stats();
2805
2809
  init_match_results();
2806
2810
  AFL_TABLES_BASE = "https://afltables.com/afl/seas";
@@ -3913,9 +3917,17 @@ function escapeField(value) {
3913
3917
  }
3914
3918
  return value;
3915
3919
  }
3920
+ function dateToAestIso(date) {
3921
+ const parts = AEST_ISO_FORMATTER.formatToParts(date);
3922
+ const get = (type) => parts.find((p) => p.type === type)?.value ?? "";
3923
+ const offset = get("timeZoneName");
3924
+ const sign = offset.includes("-") ? "-" : "+";
3925
+ const offsetHours = offset.replace(/[^0-9]/g, "").padStart(2, "0");
3926
+ return `${get("year")}-${get("month")}-${get("day")}T${get("hour")}:${get("minute")}:${get("second")}${sign}${offsetHours}:00`;
3927
+ }
3916
3928
  function toStringValue(value) {
3917
3929
  if (value === null || value === void 0) return "";
3918
- if (value instanceof Date) return value.toISOString();
3930
+ if (value instanceof Date) return dateToAestIso(value);
3919
3931
  if (typeof value === "object") return JSON.stringify(value);
3920
3932
  return String(value);
3921
3933
  }
@@ -3931,19 +3943,57 @@ function formatCsv(data) {
3931
3943
  }
3932
3944
  return lines.join("\n");
3933
3945
  }
3946
+ var AEST_ISO_FORMATTER;
3934
3947
  var init_csv = __esm({
3935
3948
  "src/cli/formatters/csv.ts"() {
3936
3949
  "use strict";
3950
+ AEST_ISO_FORMATTER = new Intl.DateTimeFormat("en-AU", {
3951
+ timeZone: "Australia/Melbourne",
3952
+ year: "numeric",
3953
+ month: "2-digit",
3954
+ day: "2-digit",
3955
+ hour: "2-digit",
3956
+ minute: "2-digit",
3957
+ second: "2-digit",
3958
+ hour12: false,
3959
+ timeZoneName: "shortOffset"
3960
+ });
3937
3961
  }
3938
3962
  });
3939
3963
 
3940
3964
  // src/cli/formatters/json.ts
3965
+ function dateToAestIso2(date) {
3966
+ const parts = AEST_ISO_FORMATTER2.formatToParts(date);
3967
+ const get = (type) => parts.find((p) => p.type === type)?.value ?? "";
3968
+ const offset = get("timeZoneName");
3969
+ const sign = offset.includes("-") ? "-" : "+";
3970
+ const offsetHours = offset.replace(/[^0-9]/g, "").padStart(2, "0");
3971
+ return `${get("year")}-${get("month")}-${get("day")}T${get("hour")}:${get("minute")}:${get("second")}${sign}${offsetHours}:00`;
3972
+ }
3973
+ function jsonReplacer(_key, value) {
3974
+ if (typeof value === "string" && /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.\d{3}Z$/.test(value)) {
3975
+ return dateToAestIso2(new Date(value));
3976
+ }
3977
+ return value;
3978
+ }
3941
3979
  function formatJson(data) {
3942
- return JSON.stringify(data, null, 2);
3980
+ return JSON.stringify(data, jsonReplacer, 2);
3943
3981
  }
3982
+ var AEST_ISO_FORMATTER2;
3944
3983
  var init_json = __esm({
3945
3984
  "src/cli/formatters/json.ts"() {
3946
3985
  "use strict";
3986
+ AEST_ISO_FORMATTER2 = new Intl.DateTimeFormat("en-AU", {
3987
+ timeZone: "Australia/Melbourne",
3988
+ year: "numeric",
3989
+ month: "2-digit",
3990
+ day: "2-digit",
3991
+ hour: "2-digit",
3992
+ minute: "2-digit",
3993
+ second: "2-digit",
3994
+ hour12: false,
3995
+ timeZoneName: "shortOffset"
3996
+ });
3947
3997
  }
3948
3998
  });
3949
3999
 
@@ -5229,7 +5279,7 @@ resolveAliases();
5229
5279
  var main = defineCommand11({
5230
5280
  meta: {
5231
5281
  name: "fitzroy",
5232
- version: "1.4.0",
5282
+ version: "1.4.1",
5233
5283
  description: "TypeScript port of the fitzRoy R package \u2014 fetch AFL data from the command line"
5234
5284
  },
5235
5285
  subCommands: {
package/dist/index.js CHANGED
@@ -264,25 +264,6 @@ var AFL_API_TEAM_IDS = /* @__PURE__ */ new Map([
264
264
  ["CD_T140", "Western Bulldogs"]
265
265
  ]);
266
266
 
267
- // src/transforms/footywire-player-stats.ts
268
- import * as cheerio from "cheerio";
269
-
270
- // src/lib/parse-utils.ts
271
- function safeInt(text) {
272
- const cleaned = text.replace(/[^0-9-]/g, "").trim();
273
- if (!cleaned) return null;
274
- const n = Number.parseInt(cleaned, 10);
275
- return Number.isNaN(n) ? null : n;
276
- }
277
- function parseIntOr0(text) {
278
- const n = Number.parseInt(text.replace(/[^0-9-]/g, ""), 10);
279
- return Number.isNaN(n) ? 0 : n;
280
- }
281
- function parseFloatOr0(text) {
282
- const n = Number.parseFloat(text.replace(/[^0-9.-]/g, ""));
283
- return Number.isNaN(n) ? 0 : n;
284
- }
285
-
286
267
  // src/lib/venue-mapping.ts
287
268
  var VENUE_ALIASES = [
288
269
  ["MCG", "M.C.G.", "Melbourne Cricket Ground"],
@@ -333,6 +314,25 @@ function normaliseVenueName(raw) {
333
314
  return VENUE_ALIAS_MAP.get(trimmed.toLowerCase()) ?? trimmed;
334
315
  }
335
316
 
317
+ // src/transforms/footywire-player-stats.ts
318
+ import * as cheerio from "cheerio";
319
+
320
+ // src/lib/parse-utils.ts
321
+ function safeInt(text) {
322
+ const cleaned = text.replace(/[^0-9-]/g, "").trim();
323
+ if (!cleaned) return null;
324
+ const n = Number.parseInt(cleaned, 10);
325
+ return Number.isNaN(n) ? null : n;
326
+ }
327
+ function parseIntOr0(text) {
328
+ const n = Number.parseInt(text.replace(/[^0-9-]/g, ""), 10);
329
+ return Number.isNaN(n) ? 0 : n;
330
+ }
331
+ function parseFloatOr0(text) {
332
+ const n = Number.parseFloat(text.replace(/[^0-9.-]/g, ""));
333
+ return Number.isNaN(n) ? 0 : n;
334
+ }
335
+
336
336
  // src/transforms/footywire-player-stats.ts
337
337
  var BASIC_COLS = [
338
338
  "Player",
@@ -393,7 +393,7 @@ function parseStatsTable(html, expectedCols, rowParser) {
393
393
  const headerText = teamHeader.text().trim();
394
394
  const match = /^(\w[\w\s]+?)\s+Match Statistics/i.exec(headerText);
395
395
  if (match?.[1]) {
396
- teamName = match[1].trim();
396
+ teamName = normaliseTeamName(match[1].trim());
397
397
  }
398
398
  }
399
399
  const parsed = [];
@@ -920,7 +920,7 @@ function parseMatchList(html, year) {
920
920
  roundNumber: currentRound,
921
921
  roundType: currentRoundType,
922
922
  date,
923
- venue,
923
+ venue: normaliseVenueName(venue),
924
924
  homeTeam,
925
925
  awayTeam,
926
926
  homeGoals,
@@ -996,7 +996,7 @@ function parseFixtureList(html, year) {
996
996
  roundNumber: currentRound,
997
997
  roundType: currentRoundType,
998
998
  date,
999
- venue,
999
+ venue: normaliseVenueName(venue),
1000
1000
  homeTeam,
1001
1001
  awayTeam,
1002
1002
  status: hasScore ? "Complete" : "Upcoming",
@@ -1175,7 +1175,7 @@ function parseBrownlowVotes(html, season) {
1175
1175
  const tds = $(row).find("td");
1176
1176
  if (tds.length < 9) return;
1177
1177
  const player = $(tds[0]).text().trim();
1178
- const team = $(tds[1]).text().trim();
1178
+ const team = normaliseTeamName($(tds[1]).text().trim());
1179
1179
  if (!player || player.toLowerCase() === "player") return;
1180
1180
  const votes3 = safeInt($(tds[2]).text()) ?? 0;
1181
1181
  const votes2 = safeInt($(tds[3]).text()) ?? 0;
@@ -1212,7 +1212,7 @@ function parseAllAustralian(html, season) {
1212
1212
  const playerLink = $(cell).find("a");
1213
1213
  const playerName = playerLink.text().trim();
1214
1214
  const teamSpan = $(cell).find("span.playerflag");
1215
- const team = teamSpan.text().trim();
1215
+ const team = normaliseTeamName(teamSpan.text().trim());
1216
1216
  if (playerName && team) {
1217
1217
  results.push({
1218
1218
  type: "all-australian",
@@ -1250,8 +1250,8 @@ function parseRisingStarNominations(html, season) {
1250
1250
  const round = safeInt(roundText);
1251
1251
  if (round == null) return;
1252
1252
  const player = $(tds[1]).text().trim();
1253
- const team = $(tds[2]).text().trim();
1254
- const opponent = $(tds[3]).text().trim();
1253
+ const team = normaliseTeamName($(tds[2]).text().trim());
1254
+ const opponent = normaliseTeamName($(tds[3]).text().trim());
1255
1255
  if (!player || player.toLowerCase() === "name") return;
1256
1256
  results.push({
1257
1257
  type: "rising-star",
@@ -2351,7 +2351,7 @@ function transformSquiggleGamesToFixture(games, season) {
2351
2351
  roundNumber: g.round,
2352
2352
  roundType: inferRoundType(g.roundname),
2353
2353
  date: new Date(g.unixtime * 1e3),
2354
- venue: g.venue,
2354
+ venue: normaliseVenueName(g.venue),
2355
2355
  homeTeam: normaliseTeamName(g.hteam),
2356
2356
  awayTeam: normaliseTeamName(g.ateam),
2357
2357
  status: toMatchStatus2(g.complete),
@@ -2382,7 +2382,7 @@ function toFixture(item, season, fallbackRoundNumber, competition) {
2382
2382
  roundNumber: item.round?.roundNumber ?? fallbackRoundNumber,
2383
2383
  roundType: inferRoundType(item.round?.name ?? ""),
2384
2384
  date: new Date(item.match.utcStartTime),
2385
- venue: item.venue?.name ?? "",
2385
+ venue: normaliseVenueName(item.venue?.name ?? ""),
2386
2386
  homeTeam: normaliseTeamName(item.match.homeTeam.name),
2387
2387
  awayTeam: normaliseTeamName(item.match.awayTeam.name),
2388
2388
  status: toMatchStatus(item.match.status),
@@ -2780,7 +2780,7 @@ function parseSeasonPage(html, year) {
2780
2780
  const awayPoints = Number.parseInt($(awayCells[2]).text().trim(), 10) || 0;
2781
2781
  const infoText = $(homeCells[3]).text().trim();
2782
2782
  const date = parseDateFromInfo(infoText, year);
2783
- const venue = parseVenueFromInfo($(homeCells[3]).html() ?? "");
2783
+ const venue = normaliseVenueName(parseVenueFromInfo($(homeCells[3]).html() ?? ""));
2784
2784
  const attendance = parseAttendanceFromInfo(infoText);
2785
2785
  const homeFinal = homeQuarters[3];
2786
2786
  const awayFinal = awayQuarters[3];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fitzroy",
3
- "version": "1.4.0",
3
+ "version": "1.4.1",
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",