bowlslink-client 0.1.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/README.md +167 -0
- package/dist/api.d.ts +13 -0
- package/dist/api.d.ts.map +1 -0
- package/dist/api.js +29 -0
- package/dist/api.js.map +1 -0
- package/dist/cjs/api.js +32 -0
- package/dist/cjs/client.js +329 -0
- package/dist/cjs/index.js +5 -0
- package/dist/cjs/package.json +1 -0
- package/dist/cjs/types.js +3 -0
- package/dist/client.d.ts +39 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +326 -0
- package/dist/client.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/dist/types.d.ts +101 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +3 -0
- package/dist/types.js.map +1 -0
- package/package.json +48 -0
package/README.md
ADDED
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
# bowlslink-client
|
|
2
|
+
|
|
3
|
+
A TypeScript client library for the [BowlsLink](https://results.bowlslink.com.au/) Results API. Fetch pennant teams, match results, ladder standings, and team sheets for any Australian bowls club.
|
|
4
|
+
|
|
5
|
+
Built for club website maintainers who want to display live pennant data without reverse-engineering the API themselves.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install bowlslink-client
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Quick Start
|
|
14
|
+
|
|
15
|
+
```ts
|
|
16
|
+
import { BowlsLinkClient } from "bowlslink-client";
|
|
17
|
+
|
|
18
|
+
const client = new BowlsLinkClient({
|
|
19
|
+
clubId: "2d83742c-5153-4f22-bcb3-68868f34e0d2", // Your club's BowlsLink UUID
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
// Get all pennant teams, matches, and standings
|
|
23
|
+
const data = await client.getPennantData();
|
|
24
|
+
|
|
25
|
+
for (const team of data.teams) {
|
|
26
|
+
console.log(`${team.name} — ${team.competitionName}`);
|
|
27
|
+
console.log(` Ladder: ${team.ladder?.position ?? "?"} (W${team.ladder?.wins} L${team.ladder?.losses})`);
|
|
28
|
+
console.log(` Matches: ${team.matches.length}`);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Get team sheets (player/rink data) for a specific match
|
|
32
|
+
const detail = await client.getMatchDetail("c0acaf56-ec90-4112-ac48-9d1d8412e7d8");
|
|
33
|
+
|
|
34
|
+
for (const team of detail.teams) {
|
|
35
|
+
console.log(`\n${team.teamName}`);
|
|
36
|
+
for (const rink of team.rinks) {
|
|
37
|
+
console.log(` ${rink.label}: ${rink.players.map((p) => `${p.name} (${p.position})`).join(", ")}`);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Finding Your Club ID
|
|
43
|
+
|
|
44
|
+
Your club's BowlsLink UUID is in the URL when you view your club on the results site:
|
|
45
|
+
|
|
46
|
+
```
|
|
47
|
+
https://results.bowlslink.com.au/club/2d83742c-5153-4f22-bcb3-68868f34e0d2
|
|
48
|
+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
49
|
+
This is your club ID
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
You can also find it by searching for your club at [results.bowlslink.com.au/search](https://results.bowlslink.com.au/search).
|
|
53
|
+
|
|
54
|
+
## API
|
|
55
|
+
|
|
56
|
+
### `new BowlsLinkClient(config)`
|
|
57
|
+
|
|
58
|
+
| Option | Type | Required | Description |
|
|
59
|
+
|--------|------|----------|-------------|
|
|
60
|
+
| `clubId` | `string` | ✅ | Your club's BowlsLink UUID |
|
|
61
|
+
| `baseUrl` | `string` | | API base URL (default: `https://api.bowlslink.com.au/results-api`) |
|
|
62
|
+
| `fetch` | `typeof fetch` | | Custom fetch implementation for testing or non-browser environments |
|
|
63
|
+
|
|
64
|
+
### `client.getPennantData(): Promise<PennantData>`
|
|
65
|
+
|
|
66
|
+
Returns all active pennant entries for the club with competition details, ladder standings, and match history including finals.
|
|
67
|
+
|
|
68
|
+
```ts
|
|
69
|
+
interface PennantData {
|
|
70
|
+
lastUpdated: string; // ISO 8601 timestamp
|
|
71
|
+
teams: Team[];
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
interface Team {
|
|
75
|
+
name: string; // e.g. "Keilor 1"
|
|
76
|
+
competitionId: string;
|
|
77
|
+
competitionName: string | null; // e.g. "2025-26 Metro Pennant Weekend Division 2"
|
|
78
|
+
competitionStatus: string | null;
|
|
79
|
+
bowlslinkUrl: string; // Link to competition on BowlsLink
|
|
80
|
+
ladder: LadderEntry | null;
|
|
81
|
+
matches: Match[];
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
interface Match {
|
|
85
|
+
matchId: string;
|
|
86
|
+
round: number;
|
|
87
|
+
roundLabel: string; // "Round 1", "Semi-Finals", "Grand Final", etc.
|
|
88
|
+
dateUtc: number; // Unix timestamp
|
|
89
|
+
state: string; // "PLAYED" | "SCHEDULED" | etc.
|
|
90
|
+
isHome: boolean;
|
|
91
|
+
isFinals: boolean; // true for finals-series matches
|
|
92
|
+
opponent: string;
|
|
93
|
+
opponentId: string;
|
|
94
|
+
myCompetitorId: string;
|
|
95
|
+
teamScore: number | null;
|
|
96
|
+
opponentScore: number | null;
|
|
97
|
+
outcome: "W" | "L" | "draw" | "np" | null;
|
|
98
|
+
teamPoints: number | null;
|
|
99
|
+
}
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### `client.getMatchDetail(matchId): Promise<MatchDetail>`
|
|
103
|
+
|
|
104
|
+
Returns team sheets with rink and player data for a specific match.
|
|
105
|
+
|
|
106
|
+
```ts
|
|
107
|
+
interface MatchDetail {
|
|
108
|
+
matchId: string;
|
|
109
|
+
teams: MatchTeam[];
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
interface MatchTeam {
|
|
113
|
+
competitorId: string;
|
|
114
|
+
teamName: string;
|
|
115
|
+
rinks: Rink[];
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
interface Rink {
|
|
119
|
+
label: string; // "Rink 1", "Rink 2", etc.
|
|
120
|
+
players: Player[]; // Sorted: lead, second, third, skip
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
interface Player {
|
|
124
|
+
name: string;
|
|
125
|
+
position: string; // "lead" | "second" | "third" | "skip"
|
|
126
|
+
}
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
## Known Quirks
|
|
130
|
+
|
|
131
|
+
Things we've discovered about the BowlsLink API that this library handles for you:
|
|
132
|
+
|
|
133
|
+
- **Finals are a separate endpoint.** Regular season matches come from `/competition/{id}/matches`, but finals come from `/competition/{id}/finals-series-matches`. This library fetches both and merges them.
|
|
134
|
+
|
|
135
|
+
- **Higher-division finals are separate competitions.** Divisions 1–3 have their finals as entirely separate competition entries (e.g. "Division 2 Finals (Divisional)"). The regular season competition moves to `completed` status and drops off the active club entries feed. Lower divisions (4+) keep finals within the same competition using the `isFinalsSeries` flag.
|
|
136
|
+
|
|
137
|
+
- **Ladder API only returns pool 1.** In multi-section competitions, the `/ladder` endpoint only returns standings for Section 1. For teams in other sections, this library automatically computes standings from match results as a fallback.
|
|
138
|
+
|
|
139
|
+
- **The API is public but undocumented.** These endpoints are reverse-engineered from the BowlsLink SPA. They could change without notice.
|
|
140
|
+
|
|
141
|
+
## Server-Side Usage
|
|
142
|
+
|
|
143
|
+
The BowlsLink API has CORS headers allowing browser access, but for club websites we recommend calling it server-side to avoid exposing your requests to rate limiting and to enable caching:
|
|
144
|
+
|
|
145
|
+
```ts
|
|
146
|
+
import express from "express";
|
|
147
|
+
import { BowlsLinkClient } from "bowlslink-client";
|
|
148
|
+
|
|
149
|
+
const app = express();
|
|
150
|
+
const client = new BowlsLinkClient({ clubId: "your-club-id" });
|
|
151
|
+
|
|
152
|
+
let cache = null;
|
|
153
|
+
const CACHE_TTL = 10 * 60 * 1000; // 10 minutes
|
|
154
|
+
|
|
155
|
+
app.get("/api/pennant", async (req, res) => {
|
|
156
|
+
if (cache && Date.now() - cache.timestamp < CACHE_TTL) {
|
|
157
|
+
return res.json(cache.data);
|
|
158
|
+
}
|
|
159
|
+
const data = await client.getPennantData();
|
|
160
|
+
cache = { data, timestamp: Date.now() };
|
|
161
|
+
res.json(data);
|
|
162
|
+
});
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
## License
|
|
166
|
+
|
|
167
|
+
MIT
|
package/dist/api.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { JsonApiInclude, JsonApiResponse } from "./types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Fetch a path from the BowlsLink Results API and return the parsed JSON:API response.
|
|
4
|
+
*
|
|
5
|
+
* @throws {Error} On non-OK HTTP responses.
|
|
6
|
+
*/
|
|
7
|
+
export declare function apiFetch(path: string, baseUrl?: string, fetchFn?: typeof globalThis.fetch): Promise<JsonApiResponse>;
|
|
8
|
+
/**
|
|
9
|
+
* Build a lookup map of `{ [id]: attributes }` for a specific type from a
|
|
10
|
+
* JSON:API `include` array.
|
|
11
|
+
*/
|
|
12
|
+
export declare function buildLookup(include: JsonApiInclude[], type: string): Record<string, Record<string, unknown>>;
|
|
13
|
+
//# sourceMappingURL=api.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAKlE;;;;GAIG;AACH,wBAAsB,QAAQ,CAC5B,IAAI,EAAE,MAAM,EACZ,OAAO,GAAE,MAAyB,EAClC,OAAO,GAAE,OAAO,UAAU,CAAC,KAAwB,GAClD,OAAO,CAAC,eAAe,CAAC,CAO1B;AAED;;;GAGG;AACH,wBAAgB,WAAW,CACzB,OAAO,EAAE,cAAc,EAAE,EACzB,IAAI,EAAE,MAAM,GACX,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAQzC"}
|
package/dist/api.js
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
const DEFAULT_BASE_URL = "https://api.bowlslink.com.au/results-api";
|
|
2
|
+
const HEADERS = { Accept: "application/json", "Content-Type": "application/json" };
|
|
3
|
+
/**
|
|
4
|
+
* Fetch a path from the BowlsLink Results API and return the parsed JSON:API response.
|
|
5
|
+
*
|
|
6
|
+
* @throws {Error} On non-OK HTTP responses.
|
|
7
|
+
*/
|
|
8
|
+
export async function apiFetch(path, baseUrl = DEFAULT_BASE_URL, fetchFn = globalThis.fetch) {
|
|
9
|
+
const url = `${baseUrl}${path}`;
|
|
10
|
+
const res = await fetchFn(url, { headers: HEADERS });
|
|
11
|
+
if (!res.ok) {
|
|
12
|
+
throw new Error(`BowlsLink API error: ${res.status} ${res.statusText} for ${url}`);
|
|
13
|
+
}
|
|
14
|
+
return (await res.json());
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Build a lookup map of `{ [id]: attributes }` for a specific type from a
|
|
18
|
+
* JSON:API `include` array.
|
|
19
|
+
*/
|
|
20
|
+
export function buildLookup(include, type) {
|
|
21
|
+
const map = {};
|
|
22
|
+
for (const item of include) {
|
|
23
|
+
if (item.type === type) {
|
|
24
|
+
map[item.id] = item.attributes ?? {};
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
return map;
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=api.js.map
|
package/dist/api.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api.js","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"AAEA,MAAM,gBAAgB,GAAG,0CAA0C,CAAC;AACpE,MAAM,OAAO,GAAG,EAAE,MAAM,EAAE,kBAAkB,EAAE,cAAc,EAAE,kBAAkB,EAAE,CAAC;AAEnF;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,QAAQ,CAC5B,IAAY,EACZ,UAAkB,gBAAgB,EAClC,UAAmC,UAAU,CAAC,KAAK;IAEnD,MAAM,GAAG,GAAG,GAAG,OAAO,GAAG,IAAI,EAAE,CAAC;IAChC,MAAM,GAAG,GAAG,MAAM,OAAO,CAAC,GAAG,EAAE,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC,CAAC;IACrD,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;QACZ,MAAM,IAAI,KAAK,CAAC,wBAAwB,GAAG,CAAC,MAAM,IAAI,GAAG,CAAC,UAAU,QAAQ,GAAG,EAAE,CAAC,CAAC;IACrF,CAAC;IACD,OAAO,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAAoB,CAAC;AAC/C,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,WAAW,CACzB,OAAyB,EACzB,IAAY;IAEZ,MAAM,GAAG,GAA4C,EAAE,CAAC;IACxD,KAAK,MAAM,IAAI,IAAI,OAAO,EAAE,CAAC;QAC3B,IAAI,IAAI,CAAC,IAAI,KAAK,IAAI,EAAE,CAAC;YACvB,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,UAAU,IAAI,EAAE,CAAC;QACvC,CAAC;IACH,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC"}
|
package/dist/cjs/api.js
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.apiFetch = apiFetch;
|
|
4
|
+
exports.buildLookup = buildLookup;
|
|
5
|
+
const DEFAULT_BASE_URL = "https://api.bowlslink.com.au/results-api";
|
|
6
|
+
const HEADERS = { Accept: "application/json", "Content-Type": "application/json" };
|
|
7
|
+
/**
|
|
8
|
+
* Fetch a path from the BowlsLink Results API and return the parsed JSON:API response.
|
|
9
|
+
*
|
|
10
|
+
* @throws {Error} On non-OK HTTP responses.
|
|
11
|
+
*/
|
|
12
|
+
async function apiFetch(path, baseUrl = DEFAULT_BASE_URL, fetchFn = globalThis.fetch) {
|
|
13
|
+
const url = `${baseUrl}${path}`;
|
|
14
|
+
const res = await fetchFn(url, { headers: HEADERS });
|
|
15
|
+
if (!res.ok) {
|
|
16
|
+
throw new Error(`BowlsLink API error: ${res.status} ${res.statusText} for ${url}`);
|
|
17
|
+
}
|
|
18
|
+
return (await res.json());
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Build a lookup map of `{ [id]: attributes }` for a specific type from a
|
|
22
|
+
* JSON:API `include` array.
|
|
23
|
+
*/
|
|
24
|
+
function buildLookup(include, type) {
|
|
25
|
+
const map = {};
|
|
26
|
+
for (const item of include) {
|
|
27
|
+
if (item.type === type) {
|
|
28
|
+
map[item.id] = item.attributes ?? {};
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return map;
|
|
32
|
+
}
|
|
@@ -0,0 +1,329 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.BowlsLinkClient = void 0;
|
|
4
|
+
const api_js_1 = require("./api.js");
|
|
5
|
+
const DEFAULT_BASE_URL = "https://api.bowlslink.com.au/results-api";
|
|
6
|
+
const POSITION_ORDER = ["lead", "second", "third", "skip"];
|
|
7
|
+
/**
|
|
8
|
+
* BowlsLink client for fetching a single club's pennant data.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```ts
|
|
12
|
+
* import { BowlsLinkClient } from "bowlslink-client";
|
|
13
|
+
*
|
|
14
|
+
* const client = new BowlsLinkClient({ clubId: "2d83742c-..." });
|
|
15
|
+
* const data = await client.getPennantData();
|
|
16
|
+
* console.log(data.teams);
|
|
17
|
+
* ```
|
|
18
|
+
*/
|
|
19
|
+
class BowlsLinkClient {
|
|
20
|
+
clubId;
|
|
21
|
+
baseUrl;
|
|
22
|
+
fetchFn;
|
|
23
|
+
constructor(config) {
|
|
24
|
+
this.clubId = config.clubId;
|
|
25
|
+
this.baseUrl = config.baseUrl ?? DEFAULT_BASE_URL;
|
|
26
|
+
this.fetchFn = config.fetch ?? globalThis.fetch;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Fetch all active pennant entries for the club, including competition
|
|
30
|
+
* details, ladder standings, regular-season matches, and finals.
|
|
31
|
+
*/
|
|
32
|
+
async getPennantData() {
|
|
33
|
+
// 1. Fetch all active entries for the club
|
|
34
|
+
const entriesPayload = await this.fetch(`/club/${this.clubId}/entries?filter%5Bstate%5D=active`);
|
|
35
|
+
const entries = entriesPayload.include
|
|
36
|
+
.filter((item) => item.type === "entry")
|
|
37
|
+
.map((item) => ({
|
|
38
|
+
id: item.id,
|
|
39
|
+
name: String(item.attributes.name ?? ""),
|
|
40
|
+
competitorId: item.includes?.competitor?.id,
|
|
41
|
+
competitionId: item.includes?.competition?.id,
|
|
42
|
+
}));
|
|
43
|
+
// 2. Fetch competition detail, ladder, and matches per unique competition
|
|
44
|
+
const uniqueCompIds = [...new Set(entries.map((e) => e.competitionId).filter(Boolean))];
|
|
45
|
+
const competitionData = Object.fromEntries(await Promise.all(uniqueCompIds.map(async (compId) => {
|
|
46
|
+
const [compPayload, ladderPayload, matchesPayload, finalsPayload] = await Promise.all([
|
|
47
|
+
this.fetch(`/competition/${compId}`),
|
|
48
|
+
this.fetch(`/competition/${compId}/ladder`),
|
|
49
|
+
this.fetch(`/competition/${compId}/matches`),
|
|
50
|
+
this.fetch(`/competition/${compId}/finals-series-matches`).catch(() => null),
|
|
51
|
+
]);
|
|
52
|
+
// Merge finals-series-matches into regular matches so the rest of
|
|
53
|
+
// the processing (competitor lookup, match shaping) works unchanged.
|
|
54
|
+
if (finalsPayload?.include?.length) {
|
|
55
|
+
matchesPayload.include = [...matchesPayload.include, ...finalsPayload.include];
|
|
56
|
+
}
|
|
57
|
+
return [compId, { compPayload, ladderPayload, matchesPayload }];
|
|
58
|
+
})));
|
|
59
|
+
// 3. Build each team
|
|
60
|
+
const teams = entries
|
|
61
|
+
.filter((e) => e.competitionId && competitionData[e.competitionId])
|
|
62
|
+
.map((entry) => this.buildTeam(entry, competitionData[entry.competitionId]));
|
|
63
|
+
// Drop entries with no matches and sort by name
|
|
64
|
+
const activeTeams = teams
|
|
65
|
+
.filter((t) => t.matches.length > 0)
|
|
66
|
+
.sort((a, b) => a.name.localeCompare(b.name, undefined, { numeric: true }));
|
|
67
|
+
return {
|
|
68
|
+
lastUpdated: new Date().toISOString(),
|
|
69
|
+
teams: activeTeams,
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Fetch match detail (team sheets with rink/player data) for a single match.
|
|
74
|
+
*/
|
|
75
|
+
async getMatchDetail(matchId) {
|
|
76
|
+
const payload = await this.fetch(`/match/${matchId}`);
|
|
77
|
+
const includes = payload.include ?? [];
|
|
78
|
+
// Index competitors
|
|
79
|
+
const competitors = {};
|
|
80
|
+
for (const i of includes) {
|
|
81
|
+
if (i.type === "competitor") {
|
|
82
|
+
competitors[i.id] = String(i.attributes.name ?? "");
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
// Index players
|
|
86
|
+
const playerIndex = {};
|
|
87
|
+
for (const i of includes) {
|
|
88
|
+
if (i.type === "competitorPlayer") {
|
|
89
|
+
playerIndex[i.id] = {
|
|
90
|
+
name: String(i.attributes.fullName ?? ""),
|
|
91
|
+
position: String(i.attributes.assignedPosition ?? "player"),
|
|
92
|
+
competitorId: i.includes?.competitor?.id,
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
// Build rinks per competitor from rinkMatchResult objects
|
|
97
|
+
const rinksByComp = {};
|
|
98
|
+
for (const i of includes) {
|
|
99
|
+
if (i.type === "rinkMatchResult") {
|
|
100
|
+
const rinkLabel = String(i.attributes.specialisation ?? "").replace(/^Team\s+/i, "Rink ");
|
|
101
|
+
const processPlayers = (refs) => (refs ?? [])
|
|
102
|
+
.map((ref) => playerIndex[ref.id])
|
|
103
|
+
.filter(Boolean)
|
|
104
|
+
.sort((a, b) => POSITION_ORDER.indexOf(a.position) - POSITION_ORDER.indexOf(b.position));
|
|
105
|
+
const c1Players = processPlayers(i.includes?.competitorOnePlayers);
|
|
106
|
+
const c2Players = processPlayers(i.includes?.competitorTwoPlayers);
|
|
107
|
+
for (const players of [c1Players, c2Players]) {
|
|
108
|
+
if (players.length === 0)
|
|
109
|
+
continue;
|
|
110
|
+
const compId = players[0].competitorId;
|
|
111
|
+
if (!compId)
|
|
112
|
+
continue;
|
|
113
|
+
if (!rinksByComp[compId])
|
|
114
|
+
rinksByComp[compId] = [];
|
|
115
|
+
rinksByComp[compId].push({
|
|
116
|
+
label: rinkLabel,
|
|
117
|
+
players: players.map(({ name, position }) => ({ name, position })),
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
// Sort rinks within each team
|
|
123
|
+
for (const rinks of Object.values(rinksByComp)) {
|
|
124
|
+
rinks.sort((a, b) => a.label.localeCompare(b.label, undefined, { numeric: true }));
|
|
125
|
+
}
|
|
126
|
+
const teams = Object.entries(competitors).map(([compId, name]) => ({
|
|
127
|
+
competitorId: compId,
|
|
128
|
+
teamName: name,
|
|
129
|
+
rinks: rinksByComp[compId] ?? [],
|
|
130
|
+
}));
|
|
131
|
+
return { matchId, teams };
|
|
132
|
+
}
|
|
133
|
+
// ─── Private helpers ─────────────────────────────────────────────────────
|
|
134
|
+
fetch(path) {
|
|
135
|
+
return (0, api_js_1.apiFetch)(path, this.baseUrl, this.fetchFn);
|
|
136
|
+
}
|
|
137
|
+
buildTeam(entry, data) {
|
|
138
|
+
const { compPayload, ladderPayload, matchesPayload } = data;
|
|
139
|
+
// Competition details
|
|
140
|
+
const competition = compPayload.include.find((i) => i.type === "competition");
|
|
141
|
+
const compAttrs = competition?.attributes ?? {};
|
|
142
|
+
// Ladder row (may only be present for pool 1 in multi-section competitions)
|
|
143
|
+
const ladderRow = ladderPayload.include.find((i) => i.type === "ladderRow" &&
|
|
144
|
+
i.attributes?.competitorId === entry.competitorId);
|
|
145
|
+
let ladder = ladderRow ? this.parseLadderRow(ladderRow.attributes.fields) : null;
|
|
146
|
+
// Build lookups from matches payload
|
|
147
|
+
const competitorNames = (0, api_js_1.buildLookup)(matchesPayload.include, "competitor");
|
|
148
|
+
const resultMap = (0, api_js_1.buildLookup)(matchesPayload.include, "multiFormatResult");
|
|
149
|
+
// Compute ladder from match data if the API didn't return one
|
|
150
|
+
if (!ladder && entry.competitorId) {
|
|
151
|
+
ladder = this.computeLadder(entry.competitorId, matchesPayload.include, resultMap);
|
|
152
|
+
}
|
|
153
|
+
// Filter and shape matches for this team
|
|
154
|
+
const matches = this.buildMatches(entry, matchesPayload.include, competitorNames, resultMap);
|
|
155
|
+
return {
|
|
156
|
+
name: entry.name,
|
|
157
|
+
competitionId: entry.competitionId ?? "",
|
|
158
|
+
competitionName: compAttrs.name ?? null,
|
|
159
|
+
competitionStatus: compAttrs.competitionStatus ?? null,
|
|
160
|
+
bowlslinkUrl: `https://results.bowlslink.com.au/competition/${entry.competitionId}`,
|
|
161
|
+
ladder,
|
|
162
|
+
matches,
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* When the ladder API doesn't return data for a competitor (common in
|
|
167
|
+
* multi-section competitions where /ladder only returns pool 1), compute
|
|
168
|
+
* standings from match results as a fallback.
|
|
169
|
+
*/
|
|
170
|
+
computeLadder(competitorId, includes, resultMap) {
|
|
171
|
+
// Determine which pool this competitor plays in
|
|
172
|
+
const poolMatch = includes.find((i) => i.type === "match" &&
|
|
173
|
+
(i.includes?.competitorOne?.id === competitorId ||
|
|
174
|
+
i.includes?.competitorTwo?.id === competitorId));
|
|
175
|
+
const competitorPool = poolMatch?.attributes?.pool ?? null;
|
|
176
|
+
if (competitorPool === null)
|
|
177
|
+
return null;
|
|
178
|
+
// Gather all played matches in this pool (exclude finals)
|
|
179
|
+
const poolMatches = includes.filter((i) => i.type === "match" &&
|
|
180
|
+
i.attributes?.pool === competitorPool &&
|
|
181
|
+
i.attributes?.matchState === "PLAYED" &&
|
|
182
|
+
!i.attributes?.isFinalsSeries);
|
|
183
|
+
// Build per-competitor stats
|
|
184
|
+
const standings = {};
|
|
185
|
+
const ensure = (id) => {
|
|
186
|
+
if (!standings[id]) {
|
|
187
|
+
standings[id] = { played: 0, wins: 0, losses: 0, draws: 0, score: 0, againstScore: 0, points: 0 };
|
|
188
|
+
}
|
|
189
|
+
};
|
|
190
|
+
for (const m of poolMatches) {
|
|
191
|
+
const c1id = m.includes?.competitorOne?.id;
|
|
192
|
+
const c2id = m.includes?.competitorTwo?.id;
|
|
193
|
+
const resultId = m.includes?.result?.id;
|
|
194
|
+
const result = resultId ? resultMap[resultId] : undefined;
|
|
195
|
+
if (!result?.isCompleted)
|
|
196
|
+
continue;
|
|
197
|
+
if (c1id)
|
|
198
|
+
ensure(c1id);
|
|
199
|
+
if (c2id)
|
|
200
|
+
ensure(c2id);
|
|
201
|
+
const s1 = result.competitorOneScore ?? 0;
|
|
202
|
+
const s2 = result.competitorTwoScore ?? 0;
|
|
203
|
+
const p1 = result.competitorOnePoints ?? 0;
|
|
204
|
+
const p2 = result.competitorTwoPoints ?? 0;
|
|
205
|
+
if (c1id) {
|
|
206
|
+
standings[c1id].played += 1;
|
|
207
|
+
standings[c1id].score += s1;
|
|
208
|
+
standings[c1id].againstScore += s2;
|
|
209
|
+
standings[c1id].points += p1;
|
|
210
|
+
}
|
|
211
|
+
if (c2id) {
|
|
212
|
+
standings[c2id].played += 1;
|
|
213
|
+
standings[c2id].score += s2;
|
|
214
|
+
standings[c2id].againstScore += s1;
|
|
215
|
+
standings[c2id].points += p2;
|
|
216
|
+
}
|
|
217
|
+
const winnerId = result.winnerId;
|
|
218
|
+
if (winnerId === c1id) {
|
|
219
|
+
if (c1id)
|
|
220
|
+
standings[c1id].wins += 1;
|
|
221
|
+
if (c2id)
|
|
222
|
+
standings[c2id].losses += 1;
|
|
223
|
+
}
|
|
224
|
+
else if (winnerId === c2id) {
|
|
225
|
+
if (c2id)
|
|
226
|
+
standings[c2id].wins += 1;
|
|
227
|
+
if (c1id)
|
|
228
|
+
standings[c1id].losses += 1;
|
|
229
|
+
}
|
|
230
|
+
else {
|
|
231
|
+
if (c1id)
|
|
232
|
+
standings[c1id].draws += 1;
|
|
233
|
+
if (c2id)
|
|
234
|
+
standings[c2id].draws += 1;
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
// Sort by points desc, then score difference
|
|
238
|
+
const sorted = Object.entries(standings).sort(([, a], [, b]) => {
|
|
239
|
+
const ptsDiff = b.points - a.points;
|
|
240
|
+
if (ptsDiff !== 0)
|
|
241
|
+
return ptsDiff;
|
|
242
|
+
return (b.score - b.againstScore) - (a.score - a.againstScore);
|
|
243
|
+
});
|
|
244
|
+
const myIdx = sorted.findIndex(([cid]) => cid === competitorId);
|
|
245
|
+
if (myIdx === -1)
|
|
246
|
+
return null;
|
|
247
|
+
const my = sorted[myIdx][1];
|
|
248
|
+
return {
|
|
249
|
+
position: myIdx + 1,
|
|
250
|
+
played: my.played,
|
|
251
|
+
wins: my.wins,
|
|
252
|
+
losses: my.losses,
|
|
253
|
+
draws: my.draws,
|
|
254
|
+
byes: 0,
|
|
255
|
+
score: my.score,
|
|
256
|
+
againstScore: my.againstScore,
|
|
257
|
+
scoreDifference: my.score - my.againstScore,
|
|
258
|
+
points: my.points,
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
buildMatches(entry, includes, competitorNames, resultMap) {
|
|
262
|
+
if (!entry.competitorId)
|
|
263
|
+
return [];
|
|
264
|
+
return includes
|
|
265
|
+
.filter((i) => i.type === "match")
|
|
266
|
+
.filter((m) => {
|
|
267
|
+
const c1 = m.includes?.competitorOne?.id;
|
|
268
|
+
const c2 = m.includes?.competitorTwo?.id;
|
|
269
|
+
return c1 === entry.competitorId || c2 === entry.competitorId;
|
|
270
|
+
})
|
|
271
|
+
.map((m) => {
|
|
272
|
+
const attrs = m.attributes;
|
|
273
|
+
const c1 = m.includes?.competitorOne?.id;
|
|
274
|
+
const isHome = c1 === entry.competitorId;
|
|
275
|
+
const opponentId = isHome
|
|
276
|
+
? m.includes?.competitorTwo?.id ?? ""
|
|
277
|
+
: c1 ?? "";
|
|
278
|
+
const resultId = m.includes?.result?.id;
|
|
279
|
+
const result = resultId ? resultMap[resultId] : undefined;
|
|
280
|
+
let outcome = null;
|
|
281
|
+
if (result?.isCompleted) {
|
|
282
|
+
if (result.winnerId === null) {
|
|
283
|
+
outcome = result.status === "non-played" ? "np" : "draw";
|
|
284
|
+
}
|
|
285
|
+
else {
|
|
286
|
+
outcome = result.winnerId === entry.competitorId ? "W" : "L";
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
return {
|
|
290
|
+
matchId: m.id,
|
|
291
|
+
round: attrs.round,
|
|
292
|
+
roundLabel: String(attrs.roundLabel ?? ""),
|
|
293
|
+
dateUtc: attrs.matchDayUtc,
|
|
294
|
+
state: String(attrs.matchState ?? ""),
|
|
295
|
+
isHome,
|
|
296
|
+
isFinals: Boolean(attrs.isFinalsSeries),
|
|
297
|
+
opponent: String(competitorNames[opponentId]?.name ?? "TBD"),
|
|
298
|
+
opponentId,
|
|
299
|
+
myCompetitorId: entry.competitorId,
|
|
300
|
+
teamScore: result
|
|
301
|
+
? (isHome ? result.competitorOneScore : result.competitorTwoScore)
|
|
302
|
+
: null,
|
|
303
|
+
opponentScore: result
|
|
304
|
+
? (isHome ? result.competitorTwoScore : result.competitorOneScore)
|
|
305
|
+
: null,
|
|
306
|
+
outcome,
|
|
307
|
+
teamPoints: result
|
|
308
|
+
? (isHome ? result.competitorOnePoints : result.competitorTwoPoints)
|
|
309
|
+
: null,
|
|
310
|
+
};
|
|
311
|
+
})
|
|
312
|
+
.sort((a, b) => a.round - b.round);
|
|
313
|
+
}
|
|
314
|
+
parseLadderRow(fields) {
|
|
315
|
+
return {
|
|
316
|
+
position: fields.position,
|
|
317
|
+
played: fields.played,
|
|
318
|
+
wins: fields.wins,
|
|
319
|
+
losses: fields.losses,
|
|
320
|
+
draws: fields.draws,
|
|
321
|
+
byes: fields.byes,
|
|
322
|
+
score: fields.score,
|
|
323
|
+
againstScore: fields.againstScore,
|
|
324
|
+
scoreDifference: fields.scoreDifference,
|
|
325
|
+
points: fields.points,
|
|
326
|
+
};
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
exports.BowlsLinkClient = BowlsLinkClient;
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.BowlsLinkClient = void 0;
|
|
4
|
+
var client_js_1 = require("./client.js");
|
|
5
|
+
Object.defineProperty(exports, "BowlsLinkClient", { enumerable: true, get: function () { return client_js_1.BowlsLinkClient; } });
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"type":"commonjs"}
|
package/dist/client.d.ts
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { BowlsLinkConfig, MatchDetail, PennantData } from "./types.js";
|
|
2
|
+
/**
|
|
3
|
+
* BowlsLink client for fetching a single club's pennant data.
|
|
4
|
+
*
|
|
5
|
+
* @example
|
|
6
|
+
* ```ts
|
|
7
|
+
* import { BowlsLinkClient } from "bowlslink-client";
|
|
8
|
+
*
|
|
9
|
+
* const client = new BowlsLinkClient({ clubId: "2d83742c-..." });
|
|
10
|
+
* const data = await client.getPennantData();
|
|
11
|
+
* console.log(data.teams);
|
|
12
|
+
* ```
|
|
13
|
+
*/
|
|
14
|
+
export declare class BowlsLinkClient {
|
|
15
|
+
private readonly clubId;
|
|
16
|
+
private readonly baseUrl;
|
|
17
|
+
private readonly fetchFn;
|
|
18
|
+
constructor(config: BowlsLinkConfig);
|
|
19
|
+
/**
|
|
20
|
+
* Fetch all active pennant entries for the club, including competition
|
|
21
|
+
* details, ladder standings, regular-season matches, and finals.
|
|
22
|
+
*/
|
|
23
|
+
getPennantData(): Promise<PennantData>;
|
|
24
|
+
/**
|
|
25
|
+
* Fetch match detail (team sheets with rink/player data) for a single match.
|
|
26
|
+
*/
|
|
27
|
+
getMatchDetail(matchId: string): Promise<MatchDetail>;
|
|
28
|
+
private fetch;
|
|
29
|
+
private buildTeam;
|
|
30
|
+
/**
|
|
31
|
+
* When the ladder API doesn't return data for a competitor (common in
|
|
32
|
+
* multi-section competitions where /ladder only returns pool 1), compute
|
|
33
|
+
* standings from match results as a fallback.
|
|
34
|
+
*/
|
|
35
|
+
private computeLadder;
|
|
36
|
+
private buildMatches;
|
|
37
|
+
private parseLadderRow;
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,eAAe,EAIf,WAAW,EAEX,WAAW,EAGZ,MAAM,YAAY,CAAC;AAKpB;;;;;;;;;;;GAWG;AACH,qBAAa,eAAe;IAC1B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA0B;gBAEtC,MAAM,EAAE,eAAe;IAMnC;;;OAGG;IACG,cAAc,IAAI,OAAO,CAAC,WAAW,CAAC;IA2D5C;;OAEG;IACG,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC;IAuE3D,OAAO,CAAC,KAAK;IAIb,OAAO,CAAC,SAAS;IA6CjB;;;;OAIG;IACH,OAAO,CAAC,aAAa;IAsGrB,OAAO,CAAC,YAAY;IA4DpB,OAAO,CAAC,cAAc;CAcvB"}
|
package/dist/client.js
ADDED
|
@@ -0,0 +1,326 @@
|
|
|
1
|
+
import { apiFetch, buildLookup } from "./api.js";
|
|
2
|
+
const DEFAULT_BASE_URL = "https://api.bowlslink.com.au/results-api";
|
|
3
|
+
const POSITION_ORDER = ["lead", "second", "third", "skip"];
|
|
4
|
+
/**
|
|
5
|
+
* BowlsLink client for fetching a single club's pennant data.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```ts
|
|
9
|
+
* import { BowlsLinkClient } from "bowlslink-client";
|
|
10
|
+
*
|
|
11
|
+
* const client = new BowlsLinkClient({ clubId: "2d83742c-..." });
|
|
12
|
+
* const data = await client.getPennantData();
|
|
13
|
+
* console.log(data.teams);
|
|
14
|
+
* ```
|
|
15
|
+
*/
|
|
16
|
+
export class BowlsLinkClient {
|
|
17
|
+
clubId;
|
|
18
|
+
baseUrl;
|
|
19
|
+
fetchFn;
|
|
20
|
+
constructor(config) {
|
|
21
|
+
this.clubId = config.clubId;
|
|
22
|
+
this.baseUrl = config.baseUrl ?? DEFAULT_BASE_URL;
|
|
23
|
+
this.fetchFn = config.fetch ?? globalThis.fetch;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Fetch all active pennant entries for the club, including competition
|
|
27
|
+
* details, ladder standings, regular-season matches, and finals.
|
|
28
|
+
*/
|
|
29
|
+
async getPennantData() {
|
|
30
|
+
// 1. Fetch all active entries for the club
|
|
31
|
+
const entriesPayload = await this.fetch(`/club/${this.clubId}/entries?filter%5Bstate%5D=active`);
|
|
32
|
+
const entries = entriesPayload.include
|
|
33
|
+
.filter((item) => item.type === "entry")
|
|
34
|
+
.map((item) => ({
|
|
35
|
+
id: item.id,
|
|
36
|
+
name: String(item.attributes.name ?? ""),
|
|
37
|
+
competitorId: item.includes?.competitor?.id,
|
|
38
|
+
competitionId: item.includes?.competition?.id,
|
|
39
|
+
}));
|
|
40
|
+
// 2. Fetch competition detail, ladder, and matches per unique competition
|
|
41
|
+
const uniqueCompIds = [...new Set(entries.map((e) => e.competitionId).filter(Boolean))];
|
|
42
|
+
const competitionData = Object.fromEntries(await Promise.all(uniqueCompIds.map(async (compId) => {
|
|
43
|
+
const [compPayload, ladderPayload, matchesPayload, finalsPayload] = await Promise.all([
|
|
44
|
+
this.fetch(`/competition/${compId}`),
|
|
45
|
+
this.fetch(`/competition/${compId}/ladder`),
|
|
46
|
+
this.fetch(`/competition/${compId}/matches`),
|
|
47
|
+
this.fetch(`/competition/${compId}/finals-series-matches`).catch(() => null),
|
|
48
|
+
]);
|
|
49
|
+
// Merge finals-series-matches into regular matches so the rest of
|
|
50
|
+
// the processing (competitor lookup, match shaping) works unchanged.
|
|
51
|
+
if (finalsPayload?.include?.length) {
|
|
52
|
+
matchesPayload.include = [...matchesPayload.include, ...finalsPayload.include];
|
|
53
|
+
}
|
|
54
|
+
return [compId, { compPayload, ladderPayload, matchesPayload }];
|
|
55
|
+
})));
|
|
56
|
+
// 3. Build each team
|
|
57
|
+
const teams = entries
|
|
58
|
+
.filter((e) => e.competitionId && competitionData[e.competitionId])
|
|
59
|
+
.map((entry) => this.buildTeam(entry, competitionData[entry.competitionId]));
|
|
60
|
+
// Drop entries with no matches and sort by name
|
|
61
|
+
const activeTeams = teams
|
|
62
|
+
.filter((t) => t.matches.length > 0)
|
|
63
|
+
.sort((a, b) => a.name.localeCompare(b.name, undefined, { numeric: true }));
|
|
64
|
+
return {
|
|
65
|
+
lastUpdated: new Date().toISOString(),
|
|
66
|
+
teams: activeTeams,
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Fetch match detail (team sheets with rink/player data) for a single match.
|
|
71
|
+
*/
|
|
72
|
+
async getMatchDetail(matchId) {
|
|
73
|
+
const payload = await this.fetch(`/match/${matchId}`);
|
|
74
|
+
const includes = payload.include ?? [];
|
|
75
|
+
// Index competitors
|
|
76
|
+
const competitors = {};
|
|
77
|
+
for (const i of includes) {
|
|
78
|
+
if (i.type === "competitor") {
|
|
79
|
+
competitors[i.id] = String(i.attributes.name ?? "");
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
// Index players
|
|
83
|
+
const playerIndex = {};
|
|
84
|
+
for (const i of includes) {
|
|
85
|
+
if (i.type === "competitorPlayer") {
|
|
86
|
+
playerIndex[i.id] = {
|
|
87
|
+
name: String(i.attributes.fullName ?? ""),
|
|
88
|
+
position: String(i.attributes.assignedPosition ?? "player"),
|
|
89
|
+
competitorId: i.includes?.competitor?.id,
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
// Build rinks per competitor from rinkMatchResult objects
|
|
94
|
+
const rinksByComp = {};
|
|
95
|
+
for (const i of includes) {
|
|
96
|
+
if (i.type === "rinkMatchResult") {
|
|
97
|
+
const rinkLabel = String(i.attributes.specialisation ?? "").replace(/^Team\s+/i, "Rink ");
|
|
98
|
+
const processPlayers = (refs) => (refs ?? [])
|
|
99
|
+
.map((ref) => playerIndex[ref.id])
|
|
100
|
+
.filter(Boolean)
|
|
101
|
+
.sort((a, b) => POSITION_ORDER.indexOf(a.position) - POSITION_ORDER.indexOf(b.position));
|
|
102
|
+
const c1Players = processPlayers(i.includes?.competitorOnePlayers);
|
|
103
|
+
const c2Players = processPlayers(i.includes?.competitorTwoPlayers);
|
|
104
|
+
for (const players of [c1Players, c2Players]) {
|
|
105
|
+
if (players.length === 0)
|
|
106
|
+
continue;
|
|
107
|
+
const compId = players[0].competitorId;
|
|
108
|
+
if (!compId)
|
|
109
|
+
continue;
|
|
110
|
+
if (!rinksByComp[compId])
|
|
111
|
+
rinksByComp[compId] = [];
|
|
112
|
+
rinksByComp[compId].push({
|
|
113
|
+
label: rinkLabel,
|
|
114
|
+
players: players.map(({ name, position }) => ({ name, position })),
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
// Sort rinks within each team
|
|
120
|
+
for (const rinks of Object.values(rinksByComp)) {
|
|
121
|
+
rinks.sort((a, b) => a.label.localeCompare(b.label, undefined, { numeric: true }));
|
|
122
|
+
}
|
|
123
|
+
const teams = Object.entries(competitors).map(([compId, name]) => ({
|
|
124
|
+
competitorId: compId,
|
|
125
|
+
teamName: name,
|
|
126
|
+
rinks: rinksByComp[compId] ?? [],
|
|
127
|
+
}));
|
|
128
|
+
return { matchId, teams };
|
|
129
|
+
}
|
|
130
|
+
// ─── Private helpers ─────────────────────────────────────────────────────
|
|
131
|
+
fetch(path) {
|
|
132
|
+
return apiFetch(path, this.baseUrl, this.fetchFn);
|
|
133
|
+
}
|
|
134
|
+
buildTeam(entry, data) {
|
|
135
|
+
const { compPayload, ladderPayload, matchesPayload } = data;
|
|
136
|
+
// Competition details
|
|
137
|
+
const competition = compPayload.include.find((i) => i.type === "competition");
|
|
138
|
+
const compAttrs = competition?.attributes ?? {};
|
|
139
|
+
// Ladder row (may only be present for pool 1 in multi-section competitions)
|
|
140
|
+
const ladderRow = ladderPayload.include.find((i) => i.type === "ladderRow" &&
|
|
141
|
+
i.attributes?.competitorId === entry.competitorId);
|
|
142
|
+
let ladder = ladderRow ? this.parseLadderRow(ladderRow.attributes.fields) : null;
|
|
143
|
+
// Build lookups from matches payload
|
|
144
|
+
const competitorNames = buildLookup(matchesPayload.include, "competitor");
|
|
145
|
+
const resultMap = buildLookup(matchesPayload.include, "multiFormatResult");
|
|
146
|
+
// Compute ladder from match data if the API didn't return one
|
|
147
|
+
if (!ladder && entry.competitorId) {
|
|
148
|
+
ladder = this.computeLadder(entry.competitorId, matchesPayload.include, resultMap);
|
|
149
|
+
}
|
|
150
|
+
// Filter and shape matches for this team
|
|
151
|
+
const matches = this.buildMatches(entry, matchesPayload.include, competitorNames, resultMap);
|
|
152
|
+
return {
|
|
153
|
+
name: entry.name,
|
|
154
|
+
competitionId: entry.competitionId ?? "",
|
|
155
|
+
competitionName: compAttrs.name ?? null,
|
|
156
|
+
competitionStatus: compAttrs.competitionStatus ?? null,
|
|
157
|
+
bowlslinkUrl: `https://results.bowlslink.com.au/competition/${entry.competitionId}`,
|
|
158
|
+
ladder,
|
|
159
|
+
matches,
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* When the ladder API doesn't return data for a competitor (common in
|
|
164
|
+
* multi-section competitions where /ladder only returns pool 1), compute
|
|
165
|
+
* standings from match results as a fallback.
|
|
166
|
+
*/
|
|
167
|
+
computeLadder(competitorId, includes, resultMap) {
|
|
168
|
+
// Determine which pool this competitor plays in
|
|
169
|
+
const poolMatch = includes.find((i) => i.type === "match" &&
|
|
170
|
+
(i.includes?.competitorOne?.id === competitorId ||
|
|
171
|
+
i.includes?.competitorTwo?.id === competitorId));
|
|
172
|
+
const competitorPool = poolMatch?.attributes?.pool ?? null;
|
|
173
|
+
if (competitorPool === null)
|
|
174
|
+
return null;
|
|
175
|
+
// Gather all played matches in this pool (exclude finals)
|
|
176
|
+
const poolMatches = includes.filter((i) => i.type === "match" &&
|
|
177
|
+
i.attributes?.pool === competitorPool &&
|
|
178
|
+
i.attributes?.matchState === "PLAYED" &&
|
|
179
|
+
!i.attributes?.isFinalsSeries);
|
|
180
|
+
// Build per-competitor stats
|
|
181
|
+
const standings = {};
|
|
182
|
+
const ensure = (id) => {
|
|
183
|
+
if (!standings[id]) {
|
|
184
|
+
standings[id] = { played: 0, wins: 0, losses: 0, draws: 0, score: 0, againstScore: 0, points: 0 };
|
|
185
|
+
}
|
|
186
|
+
};
|
|
187
|
+
for (const m of poolMatches) {
|
|
188
|
+
const c1id = m.includes?.competitorOne?.id;
|
|
189
|
+
const c2id = m.includes?.competitorTwo?.id;
|
|
190
|
+
const resultId = m.includes?.result?.id;
|
|
191
|
+
const result = resultId ? resultMap[resultId] : undefined;
|
|
192
|
+
if (!result?.isCompleted)
|
|
193
|
+
continue;
|
|
194
|
+
if (c1id)
|
|
195
|
+
ensure(c1id);
|
|
196
|
+
if (c2id)
|
|
197
|
+
ensure(c2id);
|
|
198
|
+
const s1 = result.competitorOneScore ?? 0;
|
|
199
|
+
const s2 = result.competitorTwoScore ?? 0;
|
|
200
|
+
const p1 = result.competitorOnePoints ?? 0;
|
|
201
|
+
const p2 = result.competitorTwoPoints ?? 0;
|
|
202
|
+
if (c1id) {
|
|
203
|
+
standings[c1id].played += 1;
|
|
204
|
+
standings[c1id].score += s1;
|
|
205
|
+
standings[c1id].againstScore += s2;
|
|
206
|
+
standings[c1id].points += p1;
|
|
207
|
+
}
|
|
208
|
+
if (c2id) {
|
|
209
|
+
standings[c2id].played += 1;
|
|
210
|
+
standings[c2id].score += s2;
|
|
211
|
+
standings[c2id].againstScore += s1;
|
|
212
|
+
standings[c2id].points += p2;
|
|
213
|
+
}
|
|
214
|
+
const winnerId = result.winnerId;
|
|
215
|
+
if (winnerId === c1id) {
|
|
216
|
+
if (c1id)
|
|
217
|
+
standings[c1id].wins += 1;
|
|
218
|
+
if (c2id)
|
|
219
|
+
standings[c2id].losses += 1;
|
|
220
|
+
}
|
|
221
|
+
else if (winnerId === c2id) {
|
|
222
|
+
if (c2id)
|
|
223
|
+
standings[c2id].wins += 1;
|
|
224
|
+
if (c1id)
|
|
225
|
+
standings[c1id].losses += 1;
|
|
226
|
+
}
|
|
227
|
+
else {
|
|
228
|
+
if (c1id)
|
|
229
|
+
standings[c1id].draws += 1;
|
|
230
|
+
if (c2id)
|
|
231
|
+
standings[c2id].draws += 1;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
// Sort by points desc, then score difference
|
|
235
|
+
const sorted = Object.entries(standings).sort(([, a], [, b]) => {
|
|
236
|
+
const ptsDiff = b.points - a.points;
|
|
237
|
+
if (ptsDiff !== 0)
|
|
238
|
+
return ptsDiff;
|
|
239
|
+
return (b.score - b.againstScore) - (a.score - a.againstScore);
|
|
240
|
+
});
|
|
241
|
+
const myIdx = sorted.findIndex(([cid]) => cid === competitorId);
|
|
242
|
+
if (myIdx === -1)
|
|
243
|
+
return null;
|
|
244
|
+
const my = sorted[myIdx][1];
|
|
245
|
+
return {
|
|
246
|
+
position: myIdx + 1,
|
|
247
|
+
played: my.played,
|
|
248
|
+
wins: my.wins,
|
|
249
|
+
losses: my.losses,
|
|
250
|
+
draws: my.draws,
|
|
251
|
+
byes: 0,
|
|
252
|
+
score: my.score,
|
|
253
|
+
againstScore: my.againstScore,
|
|
254
|
+
scoreDifference: my.score - my.againstScore,
|
|
255
|
+
points: my.points,
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
buildMatches(entry, includes, competitorNames, resultMap) {
|
|
259
|
+
if (!entry.competitorId)
|
|
260
|
+
return [];
|
|
261
|
+
return includes
|
|
262
|
+
.filter((i) => i.type === "match")
|
|
263
|
+
.filter((m) => {
|
|
264
|
+
const c1 = m.includes?.competitorOne?.id;
|
|
265
|
+
const c2 = m.includes?.competitorTwo?.id;
|
|
266
|
+
return c1 === entry.competitorId || c2 === entry.competitorId;
|
|
267
|
+
})
|
|
268
|
+
.map((m) => {
|
|
269
|
+
const attrs = m.attributes;
|
|
270
|
+
const c1 = m.includes?.competitorOne?.id;
|
|
271
|
+
const isHome = c1 === entry.competitorId;
|
|
272
|
+
const opponentId = isHome
|
|
273
|
+
? m.includes?.competitorTwo?.id ?? ""
|
|
274
|
+
: c1 ?? "";
|
|
275
|
+
const resultId = m.includes?.result?.id;
|
|
276
|
+
const result = resultId ? resultMap[resultId] : undefined;
|
|
277
|
+
let outcome = null;
|
|
278
|
+
if (result?.isCompleted) {
|
|
279
|
+
if (result.winnerId === null) {
|
|
280
|
+
outcome = result.status === "non-played" ? "np" : "draw";
|
|
281
|
+
}
|
|
282
|
+
else {
|
|
283
|
+
outcome = result.winnerId === entry.competitorId ? "W" : "L";
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
return {
|
|
287
|
+
matchId: m.id,
|
|
288
|
+
round: attrs.round,
|
|
289
|
+
roundLabel: String(attrs.roundLabel ?? ""),
|
|
290
|
+
dateUtc: attrs.matchDayUtc,
|
|
291
|
+
state: String(attrs.matchState ?? ""),
|
|
292
|
+
isHome,
|
|
293
|
+
isFinals: Boolean(attrs.isFinalsSeries),
|
|
294
|
+
opponent: String(competitorNames[opponentId]?.name ?? "TBD"),
|
|
295
|
+
opponentId,
|
|
296
|
+
myCompetitorId: entry.competitorId,
|
|
297
|
+
teamScore: result
|
|
298
|
+
? (isHome ? result.competitorOneScore : result.competitorTwoScore)
|
|
299
|
+
: null,
|
|
300
|
+
opponentScore: result
|
|
301
|
+
? (isHome ? result.competitorTwoScore : result.competitorOneScore)
|
|
302
|
+
: null,
|
|
303
|
+
outcome,
|
|
304
|
+
teamPoints: result
|
|
305
|
+
? (isHome ? result.competitorOnePoints : result.competitorTwoPoints)
|
|
306
|
+
: null,
|
|
307
|
+
};
|
|
308
|
+
})
|
|
309
|
+
.sort((a, b) => a.round - b.round);
|
|
310
|
+
}
|
|
311
|
+
parseLadderRow(fields) {
|
|
312
|
+
return {
|
|
313
|
+
position: fields.position,
|
|
314
|
+
played: fields.played,
|
|
315
|
+
wins: fields.wins,
|
|
316
|
+
losses: fields.losses,
|
|
317
|
+
draws: fields.draws,
|
|
318
|
+
byes: fields.byes,
|
|
319
|
+
score: fields.score,
|
|
320
|
+
againstScore: fields.againstScore,
|
|
321
|
+
scoreDifference: fields.scoreDifference,
|
|
322
|
+
points: fields.points,
|
|
323
|
+
};
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAC9C,YAAY,EACV,eAAe,EACf,WAAW,EACX,KAAK,EACL,WAAW,EACX,SAAS,EACT,WAAW,EACX,MAAM,EACN,IAAI,EACJ,IAAI,GACL,MAAM,YAAY,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/** Configuration for a BowlsLink client instance. */
|
|
2
|
+
export interface BowlsLinkConfig {
|
|
3
|
+
/** The BowlsLink club UUID (found on the club's BowlsLink results page). */
|
|
4
|
+
clubId: string;
|
|
5
|
+
/**
|
|
6
|
+
* Optional base URL for the results API.
|
|
7
|
+
* @default "https://api.bowlslink.com.au/results-api"
|
|
8
|
+
*/
|
|
9
|
+
baseUrl?: string;
|
|
10
|
+
/**
|
|
11
|
+
* Optional custom fetch implementation (for testing or environments without
|
|
12
|
+
* a global fetch). Defaults to the global `fetch`.
|
|
13
|
+
*/
|
|
14
|
+
fetch?: typeof globalThis.fetch;
|
|
15
|
+
}
|
|
16
|
+
/** A club's pennant team within a competition. */
|
|
17
|
+
export interface Team {
|
|
18
|
+
name: string;
|
|
19
|
+
competitionId: string;
|
|
20
|
+
competitionName: string | null;
|
|
21
|
+
competitionStatus: string | null;
|
|
22
|
+
bowlslinkUrl: string;
|
|
23
|
+
ladder: LadderEntry | null;
|
|
24
|
+
matches: Match[];
|
|
25
|
+
}
|
|
26
|
+
/** Ladder (standings) row for a team. */
|
|
27
|
+
export interface LadderEntry {
|
|
28
|
+
position: number;
|
|
29
|
+
played: number;
|
|
30
|
+
wins: number;
|
|
31
|
+
losses: number;
|
|
32
|
+
draws: number;
|
|
33
|
+
byes: number;
|
|
34
|
+
score: number;
|
|
35
|
+
againstScore: number;
|
|
36
|
+
scoreDifference: number;
|
|
37
|
+
points: number;
|
|
38
|
+
}
|
|
39
|
+
/** A single match for a team. */
|
|
40
|
+
export interface Match {
|
|
41
|
+
matchId: string;
|
|
42
|
+
round: number;
|
|
43
|
+
roundLabel: string;
|
|
44
|
+
dateUtc: number;
|
|
45
|
+
state: string;
|
|
46
|
+
isHome: boolean;
|
|
47
|
+
isFinals: boolean;
|
|
48
|
+
opponent: string;
|
|
49
|
+
opponentId: string;
|
|
50
|
+
myCompetitorId: string;
|
|
51
|
+
teamScore: number | null;
|
|
52
|
+
opponentScore: number | null;
|
|
53
|
+
outcome: "W" | "L" | "draw" | "np" | null;
|
|
54
|
+
teamPoints: number | null;
|
|
55
|
+
}
|
|
56
|
+
/** Full pennant data response for a club. */
|
|
57
|
+
export interface PennantData {
|
|
58
|
+
lastUpdated: string;
|
|
59
|
+
teams: Team[];
|
|
60
|
+
}
|
|
61
|
+
/** A player within a rink. */
|
|
62
|
+
export interface Player {
|
|
63
|
+
name: string;
|
|
64
|
+
position: string;
|
|
65
|
+
}
|
|
66
|
+
/** A rink (sub-team) within a match. */
|
|
67
|
+
export interface Rink {
|
|
68
|
+
label: string;
|
|
69
|
+
players: Player[];
|
|
70
|
+
}
|
|
71
|
+
/** A team's rink/player data for a specific match. */
|
|
72
|
+
export interface MatchTeam {
|
|
73
|
+
competitorId: string;
|
|
74
|
+
teamName: string;
|
|
75
|
+
rinks: Rink[];
|
|
76
|
+
}
|
|
77
|
+
/** Match detail response with team sheets. */
|
|
78
|
+
export interface MatchDetail {
|
|
79
|
+
matchId: string;
|
|
80
|
+
teams: MatchTeam[];
|
|
81
|
+
}
|
|
82
|
+
/** A single item in a JSON:API `include` array. */
|
|
83
|
+
export interface JsonApiInclude {
|
|
84
|
+
type: string;
|
|
85
|
+
id: string;
|
|
86
|
+
attributes: Record<string, unknown>;
|
|
87
|
+
includes?: Record<string, {
|
|
88
|
+
type: string;
|
|
89
|
+
id: string;
|
|
90
|
+
} | {
|
|
91
|
+
type: string;
|
|
92
|
+
id: string;
|
|
93
|
+
}[]>;
|
|
94
|
+
}
|
|
95
|
+
/** Shape of a JSON:API response from BowlsLink. */
|
|
96
|
+
export interface JsonApiResponse {
|
|
97
|
+
data: unknown;
|
|
98
|
+
include: JsonApiInclude[];
|
|
99
|
+
metadata?: unknown;
|
|
100
|
+
}
|
|
101
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,gFAAgF"}
|
package/package.json
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "bowlslink-client",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Client library for the BowlsLink Results API — fetch club entries, matches, ladders, and team sheets.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/cjs/index.js",
|
|
7
|
+
"module": "dist/index.js",
|
|
8
|
+
"types": "dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"import": {
|
|
12
|
+
"types": "./dist/index.d.ts",
|
|
13
|
+
"default": "./dist/index.js"
|
|
14
|
+
},
|
|
15
|
+
"require": {
|
|
16
|
+
"types": "./dist/cjs/index.d.ts",
|
|
17
|
+
"default": "./dist/cjs/index.js"
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
"files": [
|
|
22
|
+
"dist"
|
|
23
|
+
],
|
|
24
|
+
"scripts": {
|
|
25
|
+
"build": "tsc && tsc -p tsconfig.cjs.json && echo '{\"type\":\"commonjs\"}' > dist/cjs/package.json",
|
|
26
|
+
"test": "vitest run",
|
|
27
|
+
"test:watch": "vitest",
|
|
28
|
+
"prepare": "npm run build",
|
|
29
|
+
"prepublishOnly": "npm run build"
|
|
30
|
+
},
|
|
31
|
+
"keywords": [
|
|
32
|
+
"bowlslink",
|
|
33
|
+
"bowls",
|
|
34
|
+
"lawn-bowls",
|
|
35
|
+
"australia",
|
|
36
|
+
"pennant",
|
|
37
|
+
"results"
|
|
38
|
+
],
|
|
39
|
+
"license": "MIT",
|
|
40
|
+
"repository": {
|
|
41
|
+
"type": "git",
|
|
42
|
+
"url": "https://github.com/sje397/bowlslink-api"
|
|
43
|
+
},
|
|
44
|
+
"devDependencies": {
|
|
45
|
+
"typescript": "^5.7.0",
|
|
46
|
+
"vitest": "^3.0.0"
|
|
47
|
+
}
|
|
48
|
+
}
|