fansunited-data-layer 0.13.0 → 0.14.2

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 (242) hide show
  1. package/api/fansunited/constants.js +5 -0
  2. package/api/fansunited/constants.js.map +1 -0
  3. package/api/fansunited/football/competition/index.js +21 -0
  4. package/api/fansunited/football/competition/index.js.map +1 -0
  5. package/api/fansunited/football/competition/transformer.js +41 -0
  6. package/api/fansunited/football/competition/transformer.js.map +1 -0
  7. package/api/fansunited/football/competitions/index.js +56 -0
  8. package/api/fansunited/football/competitions/index.js.map +1 -0
  9. package/api/fansunited/football/competitions/transformer.js +68 -0
  10. package/api/fansunited/football/competitions/transformer.js.map +1 -0
  11. package/api/fansunited/football/http.js +7 -0
  12. package/api/fansunited/football/http.js.map +1 -0
  13. package/api/fansunited/football/matches/index.js +132 -0
  14. package/api/fansunited/football/matches/index.js.map +1 -0
  15. package/api/fansunited/football/matches/transformer.js +148 -0
  16. package/api/fansunited/football/matches/transformer.js.map +1 -0
  17. package/api/fansunited/football/players/index.js +60 -0
  18. package/api/fansunited/football/players/index.js.map +1 -0
  19. package/api/fansunited/football/players/transformer.js +85 -0
  20. package/api/fansunited/football/players/transformer.js.map +1 -0
  21. package/api/fansunited/football/search/index.js +64 -0
  22. package/api/fansunited/football/search/index.js.map +1 -0
  23. package/api/fansunited/football/search/transformer.js +114 -0
  24. package/api/fansunited/football/search/transformer.js.map +1 -0
  25. package/api/fansunited/football/teams/index.js +66 -0
  26. package/api/fansunited/football/teams/index.js.map +1 -0
  27. package/api/fansunited/football/teams/transformer.js +53 -0
  28. package/api/fansunited/football/teams/transformer.js.map +1 -0
  29. package/api/fansunited/http.d.ts.map +1 -1
  30. package/api/fansunited/http.js +70 -0
  31. package/api/fansunited/http.js.map +1 -0
  32. package/api/fansunited/search/constants.js +5 -0
  33. package/api/fansunited/search/constants.js.map +1 -0
  34. package/api/fansunited/search/http.js +7 -0
  35. package/api/fansunited/search/http.js.map +1 -0
  36. package/api/fansunited/search/index.js +308 -0
  37. package/api/fansunited/search/index.js.map +1 -0
  38. package/api/fansunited/search/transformer.js +374 -0
  39. package/api/fansunited/search/transformer.js.map +1 -0
  40. package/api/fansunited-sdk/loyalty/matches.js +21 -0
  41. package/api/fansunited-sdk/loyalty/matches.js.map +1 -0
  42. package/api/fansunited-sdk/loyalty/template.js +52 -0
  43. package/api/fansunited-sdk/loyalty/template.js.map +1 -0
  44. package/api/fansunited-sdk/odds/matches.js +109 -0
  45. package/api/fansunited-sdk/odds/matches.js.map +1 -0
  46. package/api/sportal365-sports/basketball/games/index.js +32 -0
  47. package/api/sportal365-sports/basketball/games/index.js.map +1 -0
  48. package/api/sportal365-sports/basketball/games/transformers/game.js +137 -0
  49. package/api/sportal365-sports/basketball/games/transformers/game.js.map +1 -0
  50. package/api/sportal365-sports/basketball/http.js +7 -0
  51. package/api/sportal365-sports/basketball/http.js.map +1 -0
  52. package/api/sportal365-sports/constants.js +15 -0
  53. package/api/sportal365-sports/constants.js.map +1 -0
  54. package/api/sportal365-sports/football/competitions/index.js +52 -0
  55. package/api/sportal365-sports/football/competitions/index.js.map +1 -0
  56. package/api/sportal365-sports/football/competitions/utils.js +92 -0
  57. package/api/sportal365-sports/football/competitions/utils.js.map +1 -0
  58. package/api/sportal365-sports/football/http.js +7 -0
  59. package/api/sportal365-sports/football/http.js.map +1 -0
  60. package/api/sportal365-sports/football/matches/index.js +206 -0
  61. package/api/sportal365-sports/football/matches/index.js.map +1 -0
  62. package/api/sportal365-sports/football/matches/transformers/commentary.js +27 -0
  63. package/api/sportal365-sports/football/matches/transformers/commentary.js.map +1 -0
  64. package/api/sportal365-sports/football/matches/transformers/lineup.js +74 -0
  65. package/api/sportal365-sports/football/matches/transformers/lineup.js.map +1 -0
  66. package/api/sportal365-sports/football/matches/transformers/match-event.js +60 -0
  67. package/api/sportal365-sports/football/matches/transformers/match-event.js.map +1 -0
  68. package/api/sportal365-sports/football/matches/transformers/match.js +195 -0
  69. package/api/sportal365-sports/football/matches/transformers/match.js.map +1 -0
  70. package/api/sportal365-sports/football/matches/transformers/odds.js +11 -0
  71. package/api/sportal365-sports/football/matches/transformers/odds.js.map +1 -0
  72. package/api/sportal365-sports/football/matches/transformers/statistics.js +41 -0
  73. package/api/sportal365-sports/football/matches/transformers/statistics.js.map +1 -0
  74. package/api/sportal365-sports/football/search/index.js +42 -0
  75. package/api/sportal365-sports/football/search/index.js.map +1 -0
  76. package/api/sportal365-sports/football/search/search.transformer.js +166 -0
  77. package/api/sportal365-sports/football/search/search.transformer.js.map +1 -0
  78. package/api/sportal365-sports/football/standings/index.js +28 -0
  79. package/api/sportal365-sports/football/standings/index.js.map +1 -0
  80. package/api/sportal365-sports/football/standings/standing.transformer.js +107 -0
  81. package/api/sportal365-sports/football/standings/standing.transformer.js.map +1 -0
  82. package/api/sportal365-sports/football/statistics/index.js +154 -0
  83. package/api/sportal365-sports/football/statistics/index.js.map +1 -0
  84. package/api/sportal365-sports/football/statistics/player-career.transformer.js +77 -0
  85. package/api/sportal365-sports/football/statistics/player-career.transformer.js.map +1 -0
  86. package/api/sportal365-sports/football/statistics/player-recent.transformer.js +54 -0
  87. package/api/sportal365-sports/football/statistics/player-recent.transformer.js.map +1 -0
  88. package/api/sportal365-sports/football/statistics/player-season.transformer.js +147 -0
  89. package/api/sportal365-sports/football/statistics/player-season.transformer.js.map +1 -0
  90. package/api/sportal365-sports/football/teams/index.js +73 -0
  91. package/api/sportal365-sports/football/teams/index.js.map +1 -0
  92. package/api/sportal365-sports/football/teams/utils.js +196 -0
  93. package/api/sportal365-sports/football/teams/utils.js.map +1 -0
  94. package/api/sportal365-sports/http.js +58 -0
  95. package/api/sportal365-sports/http.js.map +1 -0
  96. package/api/sportal365-sports/search/http.js +7 -0
  97. package/api/sportal365-sports/search/http.js.map +1 -0
  98. package/api/sportal365-sports/search/index.js +61 -0
  99. package/api/sportal365-sports/search/index.js.map +1 -0
  100. package/api/sportal365-sports/search/search.transformer.js +254 -0
  101. package/api/sportal365-sports/search/search.transformer.js.map +1 -0
  102. package/api/sportal365-sports/shared/odds.transformer.js +65 -0
  103. package/api/sportal365-sports/shared/odds.transformer.js.map +1 -0
  104. package/api/sportal365-sports/shared/providerRef.helper.js +31 -0
  105. package/api/sportal365-sports/shared/providerRef.helper.js.map +1 -0
  106. package/api/sportal365-sports/standings/http.js +7 -0
  107. package/api/sportal365-sports/standings/http.js.map +1 -0
  108. package/api/sportal365-sports/standings/index.js +29 -0
  109. package/api/sportal365-sports/standings/index.js.map +1 -0
  110. package/api/sportal365-sports/standings/standing.transformer.js +185 -0
  111. package/api/sportal365-sports/standings/standing.transformer.js.map +1 -0
  112. package/api/sportal365-sports/statistics/http.js +7 -0
  113. package/api/sportal365-sports/statistics/http.js.map +1 -0
  114. package/api/sportal365-sports/statistics/index.js +25 -0
  115. package/api/sportal365-sports/statistics/index.js.map +1 -0
  116. package/api/sportal365-sports/statistics/team-stats.transformer.js +16 -0
  117. package/api/sportal365-sports/statistics/team-stats.transformer.js.map +1 -0
  118. package/api/sportal365-sports/tennis/http.js +7 -0
  119. package/api/sportal365-sports/tennis/http.js.map +1 -0
  120. package/api/sportal365-sports/tennis/matches/index.js +34 -0
  121. package/api/sportal365-sports/tennis/matches/index.js.map +1 -0
  122. package/api/sportal365-sports/tennis/matches/transformers/match.js +193 -0
  123. package/api/sportal365-sports/tennis/matches/transformers/match.js.map +1 -0
  124. package/cache/cache-manager.js +136 -0
  125. package/cache/cache-manager.js.map +1 -0
  126. package/cache/memory-store.js +29 -0
  127. package/cache/memory-store.js.map +1 -0
  128. package/client.js +38 -12337
  129. package/client.js.map +1 -0
  130. package/config/index.js +20 -0
  131. package/config/index.js.map +1 -0
  132. package/fansunited-data-layer.js +146 -3553
  133. package/fansunited-data-layer.js.map +1 -0
  134. package/helpers/competition.helpers.js +11 -0
  135. package/helpers/competition.helpers.js.map +1 -0
  136. package/helpers/player.helpers.js +165 -0
  137. package/helpers/player.helpers.js.map +1 -0
  138. package/helpers/player.hooks.js +34 -0
  139. package/helpers/player.hooks.js.map +1 -0
  140. package/helpers/team.helpers.js +216 -0
  141. package/helpers/team.helpers.js.map +1 -0
  142. package/package.json +6 -8
  143. package/providers/competition/hooks/useCompetitionStats.js +10 -0
  144. package/providers/competition/hooks/useCompetitionStats.js.map +1 -0
  145. package/providers/competition/hooks/useGoalDistribution.js +23 -0
  146. package/providers/competition/hooks/useGoalDistribution.js.map +1 -0
  147. package/providers/competition/hooks/useMatchHelpers.js +85 -0
  148. package/providers/competition/hooks/useMatchHelpers.js.map +1 -0
  149. package/providers/competition/hooks/useStandingsCalculations.js +89 -0
  150. package/providers/competition/hooks/useStandingsCalculations.js.map +1 -0
  151. package/providers/competition/hooks/useStandingsHelpers.js +61 -0
  152. package/providers/competition/hooks/useStandingsHelpers.js.map +1 -0
  153. package/providers/competition/hooks/useTeamPosition.js +15 -0
  154. package/providers/competition/hooks/useTeamPosition.js.map +1 -0
  155. package/providers/competition/hooks/useTeamResultsTable.js +86 -0
  156. package/providers/competition/hooks/useTeamResultsTable.js.map +1 -0
  157. package/providers/competition/utils/competitionStats.js +109 -0
  158. package/providers/competition/utils/competitionStats.js.map +1 -0
  159. package/providers/competition/utils/goalDistribution.js +64 -0
  160. package/providers/competition/utils/goalDistribution.js.map +1 -0
  161. package/providers/competition/utils/standingsCalculations.js +152 -0
  162. package/providers/competition/utils/standingsCalculations.js.map +1 -0
  163. package/providers/competition.context.js +14 -0
  164. package/providers/competition.context.js.map +1 -0
  165. package/providers/competition.provider.js +187 -0
  166. package/providers/competition.provider.js.map +1 -0
  167. package/providers/fansunited-config.context.js +6 -0
  168. package/providers/fansunited-config.context.js.map +1 -0
  169. package/providers/fansunited-config.hooks.js +20 -0
  170. package/providers/fansunited-config.hooks.js.map +1 -0
  171. package/providers/fansunited-config.provider.js +9 -0
  172. package/providers/fansunited-config.provider.js.map +1 -0
  173. package/providers/fansunited-sdk.hook.js +50 -0
  174. package/providers/fansunited-sdk.hook.js.map +1 -0
  175. package/providers/leaderboard/hooks/useMatchHelpers.js +30 -0
  176. package/providers/leaderboard/hooks/useMatchHelpers.js.map +1 -0
  177. package/providers/leaderboard/hooks/useTemplateHelpers.js +39 -0
  178. package/providers/leaderboard/hooks/useTemplateHelpers.js.map +1 -0
  179. package/providers/leaderboard.context.js +14 -0
  180. package/providers/leaderboard.context.js.map +1 -0
  181. package/providers/leaderboard.provider.js +98 -0
  182. package/providers/leaderboard.provider.js.map +1 -0
  183. package/providers/match/hooks/useBatchMatchOdds.js +72 -0
  184. package/providers/match/hooks/useBatchMatchOdds.js.map +1 -0
  185. package/providers/match/hooks/useEventHelpers.js +46 -0
  186. package/providers/match/hooks/useEventHelpers.js.map +1 -0
  187. package/providers/match/hooks/useHeadToHeadHelpers.js +52 -0
  188. package/providers/match/hooks/useHeadToHeadHelpers.js.map +1 -0
  189. package/providers/match/hooks/useLineupHelpers.js +53 -0
  190. package/providers/match/hooks/useLineupHelpers.js.map +1 -0
  191. package/providers/match/hooks/useMatchStandingsCalculations.js +332 -0
  192. package/providers/match/hooks/useMatchStandingsCalculations.js.map +1 -0
  193. package/providers/match/hooks/useMatchStatus.js +37 -0
  194. package/providers/match/hooks/useMatchStatus.js.map +1 -0
  195. package/providers/match/hooks/useOddsHelpers.js +54 -0
  196. package/providers/match/hooks/useOddsHelpers.js.map +1 -0
  197. package/providers/match/hooks/useScoreHelpers.js +34 -0
  198. package/providers/match/hooks/useScoreHelpers.js.map +1 -0
  199. package/providers/match/hooks/useStandingsHelpers.js +53 -0
  200. package/providers/match/hooks/useStandingsHelpers.js.map +1 -0
  201. package/providers/match/hooks/useStatisticsHelpers.js +40 -0
  202. package/providers/match/hooks/useStatisticsHelpers.js.map +1 -0
  203. package/providers/match/hooks/useTeamHelpers.js +38 -0
  204. package/providers/match/hooks/useTeamHelpers.js.map +1 -0
  205. package/providers/match/hooks/useTeamStatsHelpers.js +141 -0
  206. package/providers/match/hooks/useTeamStatsHelpers.js.map +1 -0
  207. package/providers/match.context.js +14 -0
  208. package/providers/match.context.js.map +1 -0
  209. package/providers/match.provider.js +176 -0
  210. package/providers/match.provider.js.map +1 -0
  211. package/providers/team/hooks/useFormStats.js +116 -0
  212. package/providers/team/hooks/useFormStats.js.map +1 -0
  213. package/providers/team/hooks/useMatchHelpers.js +46 -0
  214. package/providers/team/hooks/useMatchHelpers.js.map +1 -0
  215. package/providers/team/hooks/useSquadHelpers.js +67 -0
  216. package/providers/team/hooks/useSquadHelpers.js.map +1 -0
  217. package/providers/team.context.js +14 -0
  218. package/providers/team.context.js.map +1 -0
  219. package/providers/team.provider.js +98 -0
  220. package/providers/team.provider.js.map +1 -0
  221. package/utilities/stats/core/helpers.js +105 -0
  222. package/utilities/stats/core/helpers.js.map +1 -0
  223. package/utilities/stats/core/types.js +5 -0
  224. package/utilities/stats/core/types.js.map +1 -0
  225. package/utilities/stats/match/headToHead.js +83 -0
  226. package/utilities/stats/match/headToHead.js.map +1 -0
  227. package/utilities/stats/match/homeVsAway.js +140 -0
  228. package/utilities/stats/match/homeVsAway.js.map +1 -0
  229. package/utilities/stats/match/overUnder.js +91 -0
  230. package/utilities/stats/match/overUnder.js.map +1 -0
  231. package/utilities/stats/match/result.js +21 -0
  232. package/utilities/stats/match/result.js.map +1 -0
  233. package/utilities/stats/team/commonOpponents.js +77 -0
  234. package/utilities/stats/team/commonOpponents.js.map +1 -0
  235. package/utilities/stats/team/goalStats.js +50 -0
  236. package/utilities/stats/team/goalStats.js.map +1 -0
  237. package/utilities/stats/team/streaks.js +183 -0
  238. package/utilities/stats/team/streaks.js.map +1 -0
  239. package/client.cjs +0 -8
  240. package/fansunited-data-layer.cjs +0 -1
  241. package/matches-BvHU-bP0.js +0 -1795
  242. package/matches-Ci0Ci_Mw.cjs +0 -1
@@ -0,0 +1,185 @@
1
+ function mapColumnToStat(code) {
2
+ const mapping = {
3
+ RANK: null,
4
+ // rank is a top-level field, not a stat
5
+ PLAYED: "played",
6
+ WINS: "won",
7
+ DRAWS: "drawn",
8
+ LOSSES: "lost",
9
+ GOALS_FOR: "goalsFor",
10
+ GOALS_AGAINST: "goalsAgainst",
11
+ POINTS: "points"
12
+ };
13
+ if (code in mapping) return mapping[code];
14
+ return code.toLowerCase().replace(/_/g, "");
15
+ }
16
+ function mapRuleTypeToKey(ruleType) {
17
+ const mapping = {
18
+ CHAMPIONS_LEAGUE: "championsLeague",
19
+ CHAMPIONS_LEAGUE_QUAL: "championsLeagueQual",
20
+ CHAMPIONS_LEAGUE_8: "championsLeague8",
21
+ CHAMPIONS_LEAGUE_PLAYOFF_16: "championsLeaguePlayoff16",
22
+ PLAYOFF_TO_CHAMPIONS_LEAGUE: "playoffChampionsLeague",
23
+ PLAYOFF_TO_CHAMPIONS_LEAGUE_QUAL: "playoffChampionsLeagueQual",
24
+ UEFA_CUP: "europaLeague",
25
+ UEFA_QUAL: "europaLeagueQual",
26
+ UEFA_QUAL_PLAYOFF: "europaLeagueQualPlayoff",
27
+ PLAYOFF_TO_UEFA_QUAL: "playoffEuropaLeagueQual",
28
+ EUROPA_LEAGUE_8: "europaLeague8",
29
+ EUROPA_LEAGUE_PLAYOFF_16: "europaLeaguePlayoff16",
30
+ EUROPA_CONFERENCE_LEAGUE_PLAYOFF: "europaConferenceLeaguePlayoff",
31
+ EUROPA_CONFERENCE_LEAGUE_QUALIFICATION: "europaConferenceLeagueQual",
32
+ CONFERENCE_LEAGUE_8: "conferenceLeague8",
33
+ CONFERENCE_LEAGUE_PLAYOFF_16: "conferenceLeaguePlayoff16",
34
+ RELEGATION: "relegation",
35
+ RELEGATION_PLAYOFF: "relegationPlayoff",
36
+ PROMOTION: "promotion",
37
+ PROMOTION_PLAYOFF: "promotionPlayoff",
38
+ QUALIFICATION_TO_NEXT_STAGE: "qualificationNextStage",
39
+ POSSIBLE_QUALIFICATION_TO_NEXT_STAGE: "possibleQualificationNextStage",
40
+ CHAMPIONSHIP_PLAYOFF: "championshipPlayoff",
41
+ OVERALL_CHAMPIONSHIP_PLAYOFF: "overallChampionshipPlayoff",
42
+ CONFERENCE_CHAMPIONSHIP_PLAYOFF: "conferenceChampionshipPlayoff",
43
+ POSSIBLE_CONFERENCE_CHAMPIONSHIP_PLAYOFF: "possibleConferenceChampionshipPlayoff",
44
+ DIVISION_CHAMPIONSHIP_PLAYOFF: "divisionChampionshipPlayoff",
45
+ COPA_LIBERTADORES: "copaLibertadores",
46
+ PLAYOFF_TO_COPA_LIBERTADORES: "playoffCopaLibertadores",
47
+ PLAYOFF_TO_COPA_LIBERTADORES_QUAL: "playoffCopaLibertadoresQual",
48
+ PLAYOFF_TO_COPA_SUDAMERICANA: "playoffCopaSudamericana",
49
+ PLAYOFF_TO_CONCACAF_CHAMPIONS_LEAGUE: "playoffConcacafChampionsLeague",
50
+ CAF_CHAMPIONS_QUAL: "cafChampionsQual",
51
+ CAF_CONFEGERATION_CUP_QUAL: "cafConfederationCupQual",
52
+ CFU_CLUB_CHAMPIONSHIP: "cfuClubChampionship",
53
+ AFC_CHAMPIONS_LEAGUE: "afcChampionsLeague",
54
+ AFC_CHAMPIONS_LEAGUE_QUAL: "afcChampionsLeagueQual",
55
+ PLAYOFF_TO_AFC_CHAMPIONS_LEAGUE_QUAL: "playoffAfcChampionsLeagueQual",
56
+ WORLD_CUP: "worldCup"
57
+ };
58
+ return mapping[ruleType] ?? ruleType.toLowerCase().replace(/_/g, "");
59
+ }
60
+ function mapRuleTypeToPosition(ruleType) {
61
+ if (ruleType === "RELEGATION" || ruleType === "RELEGATION_PLAYOFF") return "BOTTOM";
62
+ if (ruleType.includes("PLAYOFF")) return "MIDDLE";
63
+ return "TOP";
64
+ }
65
+ function buildForm(teamId, formMatches) {
66
+ if (!formMatches || formMatches.length === 0) return void 0;
67
+ const outcomeMap = {
68
+ win: "W",
69
+ loss: "L",
70
+ draw: "D"
71
+ };
72
+ const results = formMatches.map((match) => {
73
+ const teamParticipant = match.participants.find((p) => p.id === teamId);
74
+ const opponentParticipant = match.participants.find((p) => p.id !== teamId);
75
+ const isHome = teamParticipant?.position === "1";
76
+ const displayResult = match.result.find((r) => r.type === "display");
77
+ const homeScore = displayResult?.results.find((r) => r.position === "1")?.value;
78
+ const awayScore = displayResult?.results.find((r) => r.position === "2")?.value;
79
+ const score = homeScore && awayScore ? `${homeScore}-${awayScore}` : void 0;
80
+ return {
81
+ matchId: match.id,
82
+ result: outcomeMap[match.outcome] ?? "L",
83
+ score,
84
+ homeAway: isHome ? "HOME" : "AWAY",
85
+ opponent: {
86
+ id: opponentParticipant?.id ?? "",
87
+ name: opponentParticipant?.name ?? "",
88
+ logo: opponentParticipant?.display_asset?.url
89
+ },
90
+ date: new Date(match.start_time),
91
+ competition: match.competition ? {
92
+ id: match.competition.id,
93
+ name: match.competition.name
94
+ } : void 0
95
+ };
96
+ });
97
+ return {
98
+ competitorId: teamId,
99
+ results,
100
+ formString: results.map((r) => r.result).join("")
101
+ };
102
+ }
103
+ function extractStats(columns) {
104
+ const stats = {};
105
+ for (const column of columns) {
106
+ const statKey = mapColumnToStat(column.type.code);
107
+ if (statKey) {
108
+ stats[statKey] = Number(column.value);
109
+ }
110
+ }
111
+ if (stats.goalsFor != null && stats.goalsAgainst != null && stats.goalDifference == null) {
112
+ stats.goalDifference = stats.goalsFor - stats.goalsAgainst;
113
+ }
114
+ return stats;
115
+ }
116
+ function extractRank(columns) {
117
+ const rankColumn = columns.find((c) => c.type.code === "RANK");
118
+ return rankColumn ? Number(rankColumn.value) : 0;
119
+ }
120
+ function transformEntry(raw) {
121
+ const rules = raw.rules?.length > 0 ? raw.rules.map((r) => mapRuleTypeToKey(r.standing_api_rule_type)) : void 0;
122
+ return {
123
+ rank: extractRank(raw.columns),
124
+ competitor: {
125
+ id: raw.team.id,
126
+ name: raw.team.name,
127
+ shortName: raw.team.short_name ?? void 0,
128
+ logo: void 0
129
+ // Standings API doesn't include logo in team object
130
+ },
131
+ stats: extractStats(raw.columns),
132
+ form: buildForm(raw.team.id, raw.form ?? []),
133
+ rules
134
+ };
135
+ }
136
+ function buildLegend(entries) {
137
+ const legendMap = /* @__PURE__ */ new Map();
138
+ for (const entry of entries) {
139
+ if (entry.rules) {
140
+ for (const rule of entry.rules) {
141
+ const key = mapRuleTypeToKey(rule.standing_api_rule_type);
142
+ if (!legendMap.has(key)) {
143
+ legendMap.set(key, {
144
+ key,
145
+ name: rule.name,
146
+ position: mapRuleTypeToPosition(rule.type)
147
+ });
148
+ }
149
+ }
150
+ }
151
+ }
152
+ return Array.from(legendMap.values());
153
+ }
154
+ function transformGroupStandings(raw) {
155
+ const entries = raw.standing.map(transformEntry);
156
+ const legend = buildLegend(raw.standing);
157
+ return {
158
+ entries,
159
+ metadata: { legend }
160
+ };
161
+ }
162
+ function transformStageData(stageData) {
163
+ return stageData.standings.map((groupStandings) => ({
164
+ stage: {
165
+ id: stageData.stage.id,
166
+ name: stageData.stage.name
167
+ },
168
+ group: {
169
+ id: groupStandings.group.id,
170
+ name: groupStandings.group.name,
171
+ type: groupStandings.group.type
172
+ },
173
+ coverageType: groupStandings.type,
174
+ rankingType: groupStandings.ranking_type,
175
+ standings: transformGroupStandings(groupStandings)
176
+ }));
177
+ }
178
+ function transformStandingsApiResponse(raw) {
179
+ if (!raw?.data || raw.data.length === 0) return [];
180
+ return raw.data.flatMap(transformStageData);
181
+ }
182
+ export {
183
+ transformStandingsApiResponse
184
+ };
185
+ //# sourceMappingURL=standing.transformer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"standing.transformer.js","sources":["../../../../src/lib/api/sportal365-sports/standings/standing.transformer.ts"],"sourcesContent":["/**\n * Standings API Transformer\n * Converts raw Standings API response to canonical FUSports types.\n *\n * Key differences from Football API transformer:\n * - Stats come from key-value columns instead of direct properties\n * - Rules use `standing_api_rule_type` for canonical key mapping\n * - Supports multi-stage / multi-group structures (e.g., World Cup groups, Bulgarian league stages)\n * - Form data uses shared RawFormMatch structure\n */\n\nimport type {\n FUSportsCompetitorForm,\n FUSportsStandingEntry,\n FUSportsStandingLegendItem,\n FUSportsStandings,\n FUSportsStandingsTable,\n} from \"../../../types/canonical\";\nimport type { RawFormMatch } from \"../shared/types\";\nimport type {\n RawStandingsApiColumn,\n RawStandingsApiEntry,\n RawStandingsApiGroupStandings,\n RawStandingsApiResponse,\n RawStandingsApiStageData,\n} from \"./standing.types\";\n\n// ============================================================================\n// Column Code -> Stat Key Mapping\n// ============================================================================\n\n/** Map a column code to a canonical stat key. Returns null for non-stat columns like RANK. */\nfunction mapColumnToStat(code: string): string | null {\n const mapping: Record<string, string | null> = {\n RANK: null, // rank is a top-level field, not a stat\n PLAYED: \"played\",\n WINS: \"won\",\n DRAWS: \"drawn\",\n LOSSES: \"lost\",\n GOALS_FOR: \"goalsFor\",\n GOALS_AGAINST: \"goalsAgainst\",\n POINTS: \"points\",\n };\n\n if (code in mapping) return mapping[code];\n // Unknown columns: lowercase with underscores removed for generic stat access\n return code.toLowerCase().replace(/_/g, \"\");\n}\n\n// ============================================================================\n// Rule Mapping\n// ============================================================================\n\n/** Map standing_api_rule_type to canonical rule key */\nfunction mapRuleTypeToKey(ruleType: string): string {\n const mapping: Record<string, string> = {\n CHAMPIONS_LEAGUE: \"championsLeague\",\n CHAMPIONS_LEAGUE_QUAL: \"championsLeagueQual\",\n CHAMPIONS_LEAGUE_8: \"championsLeague8\",\n CHAMPIONS_LEAGUE_PLAYOFF_16: \"championsLeaguePlayoff16\",\n PLAYOFF_TO_CHAMPIONS_LEAGUE: \"playoffChampionsLeague\",\n PLAYOFF_TO_CHAMPIONS_LEAGUE_QUAL: \"playoffChampionsLeagueQual\",\n UEFA_CUP: \"europaLeague\",\n UEFA_QUAL: \"europaLeagueQual\",\n UEFA_QUAL_PLAYOFF: \"europaLeagueQualPlayoff\",\n PLAYOFF_TO_UEFA_QUAL: \"playoffEuropaLeagueQual\",\n EUROPA_LEAGUE_8: \"europaLeague8\",\n EUROPA_LEAGUE_PLAYOFF_16: \"europaLeaguePlayoff16\",\n EUROPA_CONFERENCE_LEAGUE_PLAYOFF: \"europaConferenceLeaguePlayoff\",\n EUROPA_CONFERENCE_LEAGUE_QUALIFICATION: \"europaConferenceLeagueQual\",\n CONFERENCE_LEAGUE_8: \"conferenceLeague8\",\n CONFERENCE_LEAGUE_PLAYOFF_16: \"conferenceLeaguePlayoff16\",\n RELEGATION: \"relegation\",\n RELEGATION_PLAYOFF: \"relegationPlayoff\",\n PROMOTION: \"promotion\",\n PROMOTION_PLAYOFF: \"promotionPlayoff\",\n QUALIFICATION_TO_NEXT_STAGE: \"qualificationNextStage\",\n POSSIBLE_QUALIFICATION_TO_NEXT_STAGE: \"possibleQualificationNextStage\",\n CHAMPIONSHIP_PLAYOFF: \"championshipPlayoff\",\n OVERALL_CHAMPIONSHIP_PLAYOFF: \"overallChampionshipPlayoff\",\n CONFERENCE_CHAMPIONSHIP_PLAYOFF: \"conferenceChampionshipPlayoff\",\n POSSIBLE_CONFERENCE_CHAMPIONSHIP_PLAYOFF: \"possibleConferenceChampionshipPlayoff\",\n DIVISION_CHAMPIONSHIP_PLAYOFF: \"divisionChampionshipPlayoff\",\n COPA_LIBERTADORES: \"copaLibertadores\",\n PLAYOFF_TO_COPA_LIBERTADORES: \"playoffCopaLibertadores\",\n PLAYOFF_TO_COPA_LIBERTADORES_QUAL: \"playoffCopaLibertadoresQual\",\n PLAYOFF_TO_COPA_SUDAMERICANA: \"playoffCopaSudamericana\",\n PLAYOFF_TO_CONCACAF_CHAMPIONS_LEAGUE: \"playoffConcacafChampionsLeague\",\n CAF_CHAMPIONS_QUAL: \"cafChampionsQual\",\n CAF_CONFEGERATION_CUP_QUAL: \"cafConfederationCupQual\",\n CFU_CLUB_CHAMPIONSHIP: \"cfuClubChampionship\",\n AFC_CHAMPIONS_LEAGUE: \"afcChampionsLeague\",\n AFC_CHAMPIONS_LEAGUE_QUAL: \"afcChampionsLeagueQual\",\n PLAYOFF_TO_AFC_CHAMPIONS_LEAGUE_QUAL: \"playoffAfcChampionsLeagueQual\",\n WORLD_CUP: \"worldCup\",\n };\n return mapping[ruleType] ?? ruleType.toLowerCase().replace(/_/g, \"\");\n}\n\n/** Map rule type to position (TOP/BOTTOM/MIDDLE) */\nfunction mapRuleTypeToPosition(ruleType: string): \"TOP\" | \"BOTTOM\" | \"MIDDLE\" {\n if (ruleType === \"RELEGATION\" || ruleType === \"RELEGATION_PLAYOFF\") return \"BOTTOM\";\n if (ruleType.includes(\"PLAYOFF\")) return \"MIDDLE\";\n return \"TOP\";\n}\n\n// ============================================================================\n// Form Builder\n// ============================================================================\n\nfunction buildForm(teamId: string, formMatches: RawFormMatch[]): FUSportsCompetitorForm | undefined {\n if (!formMatches || formMatches.length === 0) return undefined;\n\n const outcomeMap: Record<string, \"W\" | \"D\" | \"L\"> = {\n win: \"W\",\n loss: \"L\",\n draw: \"D\",\n };\n\n const results = formMatches.map((match) => {\n const teamParticipant = match.participants.find((p) => p.id === teamId);\n const opponentParticipant = match.participants.find((p) => p.id !== teamId);\n const isHome = teamParticipant?.position === \"1\";\n\n // Extract score from display result\n const displayResult = match.result.find((r) => r.type === \"display\");\n const homeScore = displayResult?.results.find((r) => r.position === \"1\")?.value;\n const awayScore = displayResult?.results.find((r) => r.position === \"2\")?.value;\n const score = homeScore && awayScore ? `${homeScore}-${awayScore}` : undefined;\n\n return {\n matchId: match.id,\n result: outcomeMap[match.outcome] ?? (\"L\" as \"W\" | \"D\" | \"L\"),\n score,\n homeAway: (isHome ? \"HOME\" : \"AWAY\") as \"HOME\" | \"AWAY\",\n opponent: {\n id: opponentParticipant?.id ?? \"\",\n name: opponentParticipant?.name ?? \"\",\n logo: opponentParticipant?.display_asset?.url,\n },\n date: new Date(match.start_time),\n competition: match.competition\n ? {\n id: match.competition.id,\n name: match.competition.name,\n }\n : undefined,\n };\n });\n\n return {\n competitorId: teamId,\n results,\n formString: results.map((r) => r.result).join(\"\"),\n };\n}\n\n// ============================================================================\n// Entry Transformer\n// ============================================================================\n\n/** Extract stats from columns array */\nfunction extractStats(columns: RawStandingsApiColumn[]): FUSportsStandingEntry[\"stats\"] {\n const stats: FUSportsStandingEntry[\"stats\"] = {};\n\n for (const column of columns) {\n const statKey = mapColumnToStat(column.type.code);\n if (statKey) {\n stats[statKey] = Number(column.value);\n }\n }\n\n // Compute goal difference if we have both goals for and against\n if (stats.goalsFor != null && stats.goalsAgainst != null && stats.goalDifference == null) {\n stats.goalDifference = stats.goalsFor - stats.goalsAgainst;\n }\n\n return stats;\n}\n\n/** Extract rank from columns (RANK column) */\nfunction extractRank(columns: RawStandingsApiColumn[]): number {\n const rankColumn = columns.find((c) => c.type.code === \"RANK\");\n return rankColumn ? Number(rankColumn.value) : 0;\n}\n\n/** Transform a single standings entry to canonical format */\nfunction transformEntry(raw: RawStandingsApiEntry): FUSportsStandingEntry {\n const rules = raw.rules?.length > 0 ? raw.rules.map((r) => mapRuleTypeToKey(r.standing_api_rule_type)) : undefined;\n\n return {\n rank: extractRank(raw.columns),\n competitor: {\n id: raw.team.id,\n name: raw.team.name,\n shortName: raw.team.short_name ?? undefined,\n logo: undefined, // Standings API doesn't include logo in team object\n },\n stats: extractStats(raw.columns),\n form: buildForm(raw.team.id, raw.form ?? []),\n rules,\n };\n}\n\n// ============================================================================\n// Legend Builder\n// ============================================================================\n\nfunction buildLegend(entries: RawStandingsApiEntry[]): FUSportsStandingLegendItem[] {\n const legendMap = new Map<string, FUSportsStandingLegendItem>();\n\n for (const entry of entries) {\n if (entry.rules) {\n for (const rule of entry.rules) {\n const key = mapRuleTypeToKey(rule.standing_api_rule_type);\n if (!legendMap.has(key)) {\n legendMap.set(key, {\n key,\n name: rule.name,\n position: mapRuleTypeToPosition(rule.type),\n });\n }\n }\n }\n }\n\n return Array.from(legendMap.values());\n}\n\n// ============================================================================\n// Group Standings Transformer\n// ============================================================================\n\n/** Transform a single group standings block to canonical FUSportsStandings */\nfunction transformGroupStandings(raw: RawStandingsApiGroupStandings): FUSportsStandings {\n const entries = raw.standing.map(transformEntry);\n const legend = buildLegend(raw.standing);\n\n return {\n entries,\n metadata: { legend },\n };\n}\n\n// ============================================================================\n// Top-Level Transformer\n// ============================================================================\n\n/** Transform a stage data block to FUSportsStandingsTable[] */\nfunction transformStageData(stageData: RawStandingsApiStageData): FUSportsStandingsTable[] {\n return stageData.standings.map((groupStandings) => ({\n stage: {\n id: stageData.stage.id,\n name: stageData.stage.name,\n },\n group: {\n id: groupStandings.group.id,\n name: groupStandings.group.name,\n type: groupStandings.group.type,\n },\n coverageType: groupStandings.type,\n rankingType: groupStandings.ranking_type,\n standings: transformGroupStandings(groupStandings),\n }));\n}\n\n/**\n * Transform a full Standings API response to an array of FUSportsStandingsTable.\n * Each table represents one stage + group + coverage type combination.\n */\nexport function transformStandingsApiResponse(raw: RawStandingsApiResponse): FUSportsStandingsTable[] {\n if (!raw?.data || raw.data.length === 0) return [];\n\n return raw.data.flatMap(transformStageData);\n}\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ import { createHttpClient } from "../http.js";
2
+ import { SPORTAL365_STATISTICS_DOMAIN } from "../constants.js";
3
+ const statisticsHttp = createHttpClient(SPORTAL365_STATISTICS_DOMAIN);
4
+ export {
5
+ statisticsHttp
6
+ };
7
+ //# sourceMappingURL=http.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http.js","sources":["../../../../src/lib/api/sportal365-sports/statistics/http.ts"],"sourcesContent":["/**\n * Statistics HTTP client for Sportal365 API\n */\n\nimport { createHttpClient } from \"../http\";\nimport { SPORTAL365_STATISTICS_DOMAIN } from \"../constants\";\n\n/**\n * HTTP client configured for the Sportal365 Statistics API\n */\nexport const statisticsHttp = createHttpClient(SPORTAL365_STATISTICS_DOMAIN);\n\n"],"names":[],"mappings":";;AAUO,MAAM,iBAAiB,iBAAiB,4BAA4B;"}
@@ -0,0 +1,25 @@
1
+ import { getConfig } from "../../../config/index.js";
2
+ import { statisticsHttp } from "./http.js";
3
+ import { transformTeamStatistics } from "./team-stats.transformer.js";
4
+ async function getTeamSeasonStatistics(options, config) {
5
+ const finalConfig = config || getConfig();
6
+ const cacheOptions = options.next ?? { revalidate: 600 };
7
+ const raw = await statisticsHttp.get({
8
+ path: "/statistics/aggregate",
9
+ params: {
10
+ participant_ids: options.teamId,
11
+ season_ids: options.seasonId,
12
+ participant_type: "TEAM"
13
+ },
14
+ next: cacheOptions,
15
+ config: finalConfig
16
+ });
17
+ if (!raw.data || raw.data.length === 0) {
18
+ throw new Error(`No statistics found for team ${options.teamId} in season ${options.seasonId}`);
19
+ }
20
+ return transformTeamStatistics(raw.data[0], options.seasonId);
21
+ }
22
+ export {
23
+ getTeamSeasonStatistics
24
+ };
25
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sources":["../../../../src/lib/api/sportal365-sports/statistics/index.ts"],"sourcesContent":["/**\n * Sportal365 Sports API - Statistics\n *\n * Statistics endpoints for the Sportal365 Sports API.\n * All functions return canonical FUSports types.\n */\n\nimport type { FUSportsTeamSeasonStatistics } from \"../../../types/canonical\";\nimport { getConfig, type DataLayerConfig } from \"../../../config\";\nimport type { NextCacheOptions } from \"../http\";\nimport { statisticsHttp } from \"./http\";\nimport type { RawTeamStatisticsResponse } from \"./team-stats.types\";\nimport { transformTeamStatistics } from \"./team-stats.transformer\";\n\n/**\n * Options for getting team season statistics\n */\nexport interface GetTeamSeasonStatisticsOptions {\n /** Team ID (participant ID) - required */\n teamId: string;\n /** Season ID - required */\n seasonId: string;\n /** Next.js cache options for ISR/on-demand revalidation */\n next?: NextCacheOptions;\n}\n\n/**\n * Get team season statistics\n *\n * Fetches aggregate statistics for a team in a specific season.\n * Returns detailed statistics including goals, assists, shots, possession, etc.\n *\n * By default, results are cached for 10 minutes (600 seconds).\n * You can override this by passing a custom `next` option.\n *\n * @param options - Required parameters (teamId, seasonId)\n * @param config - Optional data layer config (uses singleton if not provided)\n * @returns Canonical FUSportsTeamSeasonStatistics\n *\n * @example\n * ```typescript\n * const stats = await getTeamSeasonStatistics({\n * teamId: '07810505-bdad-4099-bb1d-736c87d0b32c',\n * seasonId: 'e69110fd-4dc5-42f2-91ff-52bdd2326a80'\n * });\n * console.log(stats.teamName); // \"Manchester United\"\n * console.log(stats.statistics); // Array of statistics\n * ```\n *\n * @example\n * ```typescript\n * // Custom cache duration (5 minutes)\n * const stats = await getTeamSeasonStatistics({\n * teamId: '07810505-bdad-4099-bb1d-736c87d0b32c',\n * seasonId: 'e69110fd-4dc5-42f2-91ff-52bdd2326a80',\n * next: { revalidate: 300 }\n * });\n * ```\n */\nexport async function getTeamSeasonStatistics(\n options: GetTeamSeasonStatisticsOptions,\n config?: DataLayerConfig\n): Promise<FUSportsTeamSeasonStatistics> {\n const finalConfig = config || getConfig();\n\n // Default cache: 10 minutes\n const cacheOptions = options.next ?? { revalidate: 600 };\n\n const raw = await statisticsHttp.get<RawTeamStatisticsResponse>({\n path: \"/statistics/aggregate\",\n params: {\n participant_ids: options.teamId,\n season_ids: options.seasonId,\n participant_type: \"TEAM\",\n },\n next: cacheOptions,\n config: finalConfig,\n });\n\n // The API returns an array, but we only request one team\n if (!raw.data || raw.data.length === 0) {\n throw new Error(`No statistics found for team ${options.teamId} in season ${options.seasonId}`);\n }\n\n return transformTeamStatistics(raw.data[0], options.seasonId);\n}\n"],"names":[],"mappings":";;;AA2DA,eAAsB,wBAClB,SACA,QACqC;AACrC,QAAM,cAAc,UAAU,UAAA;AAG9B,QAAM,eAAe,QAAQ,QAAQ,EAAE,YAAY,IAAA;AAEnD,QAAM,MAAM,MAAM,eAAe,IAA+B;AAAA,IAC5D,MAAM;AAAA,IACN,QAAQ;AAAA,MACJ,iBAAiB,QAAQ;AAAA,MACzB,YAAY,QAAQ;AAAA,MACpB,kBAAkB;AAAA,IAAA;AAAA,IAEtB,MAAM;AAAA,IACN,QAAQ;AAAA,EAAA,CACX;AAGD,MAAI,CAAC,IAAI,QAAQ,IAAI,KAAK,WAAW,GAAG;AACpC,UAAM,IAAI,MAAM,gCAAgC,QAAQ,MAAM,cAAc,QAAQ,QAAQ,EAAE;AAAA,EAClG;AAEA,SAAO,wBAAwB,IAAI,KAAK,CAAC,GAAG,QAAQ,QAAQ;AAChE;"}
@@ -0,0 +1,16 @@
1
+ function transformTeamStatistics(raw, seasonId) {
2
+ return {
3
+ teamId: raw.id,
4
+ teamName: raw.name,
5
+ seasonId,
6
+ statistics: raw.statistics.map((stat) => ({
7
+ key: stat.id,
8
+ value: stat.value,
9
+ label: stat.name
10
+ }))
11
+ };
12
+ }
13
+ export {
14
+ transformTeamStatistics
15
+ };
16
+ //# sourceMappingURL=team-stats.transformer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"team-stats.transformer.js","sources":["../../../../src/lib/api/sportal365-sports/statistics/team-stats.transformer.ts"],"sourcesContent":["/**\n * Team statistics transformation utilities\n * Transforms raw API responses to canonical FUSports types\n */\n\nimport type { FUSportsTeamSeasonStatistics } from \"../../../types/canonical\";\nimport type { RawTeamStatistics } from \"./team-stats.types\";\n\n/**\n * Transform raw team statistics to canonical FUSportsTeamSeasonStatistics\n *\n * Note: The API doesn't return seasonId in the response, so it must be provided\n * from the request parameters.\n */\nexport function transformTeamStatistics(raw: RawTeamStatistics, seasonId: string): FUSportsTeamSeasonStatistics {\n return {\n teamId: raw.id,\n teamName: raw.name,\n seasonId,\n statistics: raw.statistics.map((stat) => ({\n key: stat.id,\n value: stat.value,\n label: stat.name,\n })),\n };\n}\n"],"names":[],"mappings":"AAcO,SAAS,wBAAwB,KAAwB,UAAgD;AAC5G,SAAO;AAAA,IACH,QAAQ,IAAI;AAAA,IACZ,UAAU,IAAI;AAAA,IACd;AAAA,IACA,YAAY,IAAI,WAAW,IAAI,CAAC,UAAU;AAAA,MACtC,KAAK,KAAK;AAAA,MACV,OAAO,KAAK;AAAA,MACZ,OAAO,KAAK;AAAA,IAAA,EACd;AAAA,EAAA;AAEV;"}
@@ -0,0 +1,7 @@
1
+ import { createHttpClient } from "../http.js";
2
+ import { SPORTAL365_TENNIS_DOMAIN } from "../constants.js";
3
+ const tennisHttp = createHttpClient(SPORTAL365_TENNIS_DOMAIN);
4
+ export {
5
+ tennisHttp
6
+ };
7
+ //# sourceMappingURL=http.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http.js","sources":["../../../../src/lib/api/sportal365-sports/tennis/http.ts"],"sourcesContent":["/**\n * Tennis HTTP client for Sportal365 API\n */\n\nimport { createHttpClient } from '../http';\nimport { SPORTAL365_TENNIS_DOMAIN } from '../constants';\n\n/**\n * HTTP client configured for the Sportal365 Tennis API\n */\nexport const tennisHttp = createHttpClient(SPORTAL365_TENNIS_DOMAIN);\n\n"],"names":[],"mappings":";;AAUO,MAAM,aAAa,iBAAiB,wBAAwB;"}
@@ -0,0 +1,34 @@
1
+ import { getConfig } from "../../../../config/index.js";
2
+ import { tennisHttp } from "../http.js";
3
+ import { transformTennisMatch } from "./transformers/match.js";
4
+ async function getTennisLivescore(options, config) {
5
+ const finalConfig = config || getConfig();
6
+ const { sportal365Sports } = finalConfig;
7
+ const raw = await tennisHttp.get({
8
+ path: `/matches/livescore`,
9
+ params: {
10
+ date: options?.date,
11
+ utc_offset: options?.utcOffset?.toString(),
12
+ offset: "0",
13
+ limit: "1000",
14
+ type: options?.type,
15
+ gender: options?.gender,
16
+ status_type: options?.statusType,
17
+ competition_list: options?.competitionList,
18
+ odd_client: options?.oddClient ?? sportal365Sports?.oddClient,
19
+ odd_type: options?.oddType,
20
+ market_types: options?.marketTypes,
21
+ language_code: options?.languageCode ?? sportal365Sports?.languageCode ?? "en"
22
+ },
23
+ headers: {
24
+ "Accept-Language": options?.languageCode ?? sportal365Sports?.languageCode ?? "en"
25
+ },
26
+ next: options?.next,
27
+ config: finalConfig
28
+ });
29
+ return raw.matches.map(transformTennisMatch);
30
+ }
31
+ export {
32
+ getTennisLivescore
33
+ };
34
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sources":["../../../../../src/lib/api/sportal365-sports/tennis/matches/index.ts"],"sourcesContent":["/**\n * Tennis matches API functions\n */\n\nimport type { FUSportsMatch } from \"../../../../types/canonical\";\nimport type { DataLayerConfig } from \"../../../../config\";\nimport { getConfig } from \"../../../../config\";\nimport { tennisHttp } from \"../http\";\nimport type { RawTennisMatch, GetTennisLivescoreOptions } from \"./types\";\nimport { transformTennisMatch } from \"./transformers/match\";\n\n/**\n * Get tennis livescore data\n *\n * Fetches live and recent tennis matches from the Sportal365 Tennis API.\n * Returns data in canonical FUSportsMatch format.\n *\n * @param options - Optional filters and configuration\n * @param config - Optional data layer configuration\n * @returns Array of tennis matches in canonical format\n *\n * @example\n * ```typescript\n * // Get all live tennis matches\n * const liveMatches = await getTennisLivescore({\n * statusType: ['LIVE']\n * });\n *\n * // Get singles matches for a specific date\n * const matches = await getTennisLivescore({\n * date: '2024-01-15',\n * type: 'SINGLE',\n * gender: 'MALE'\n * });\n * ```\n */\nexport async function getTennisLivescore(\n options?: GetTennisLivescoreOptions,\n config?: DataLayerConfig\n): Promise<FUSportsMatch[]> {\n const finalConfig = config || getConfig();\n const { sportal365Sports } = finalConfig;\n\n const raw = await tennisHttp.get<{ matches: RawTennisMatch[] }>({\n path: `/matches/livescore`,\n params: {\n date: options?.date,\n utc_offset: options?.utcOffset?.toString(),\n offset: \"0\",\n limit: \"1000\",\n type: options?.type,\n gender: options?.gender,\n status_type: options?.statusType,\n competition_list: options?.competitionList,\n odd_client: options?.oddClient ?? sportal365Sports?.oddClient,\n odd_type: options?.oddType,\n market_types: options?.marketTypes,\n language_code: options?.languageCode ?? sportal365Sports?.languageCode ?? \"en\",\n },\n headers: {\n \"Accept-Language\": options?.languageCode ?? sportal365Sports?.languageCode ?? \"en\",\n },\n next: options?.next,\n config: finalConfig,\n });\n\n return raw.matches.map(transformTennisMatch);\n}\n"],"names":[],"mappings":";;;AAoCA,eAAsB,mBAClB,SACA,QACwB;AACxB,QAAM,cAAc,UAAU,UAAA;AAC9B,QAAM,EAAE,qBAAqB;AAE7B,QAAM,MAAM,MAAM,WAAW,IAAmC;AAAA,IAC5D,MAAM;AAAA,IACN,QAAQ;AAAA,MACJ,MAAM,SAAS;AAAA,MACf,YAAY,SAAS,WAAW,SAAA;AAAA,MAChC,QAAQ;AAAA,MACR,OAAO;AAAA,MACP,MAAM,SAAS;AAAA,MACf,QAAQ,SAAS;AAAA,MACjB,aAAa,SAAS;AAAA,MACtB,kBAAkB,SAAS;AAAA,MAC3B,YAAY,SAAS,aAAa,kBAAkB;AAAA,MACpD,UAAU,SAAS;AAAA,MACnB,cAAc,SAAS;AAAA,MACvB,eAAe,SAAS,gBAAgB,kBAAkB,gBAAgB;AAAA,IAAA;AAAA,IAE9E,SAAS;AAAA,MACL,mBAAmB,SAAS,gBAAgB,kBAAkB,gBAAgB;AAAA,IAAA;AAAA,IAElF,MAAM,SAAS;AAAA,IACf,QAAQ;AAAA,EAAA,CACX;AAED,SAAO,IAAI,QAAQ,IAAI,oBAAoB;AAC/C;"}
@@ -0,0 +1,193 @@
1
+ import { transformOdds } from "../../../shared/odds.transformer.js";
2
+ import { toProviderRefArray } from "../../../shared/providerRef.helper.js";
3
+ function transformStatus(raw) {
4
+ const typeMap = {
5
+ FINISHED: "finished",
6
+ NOT_STARTED: "not_started",
7
+ LIVE: "live",
8
+ INTERRUPTED: "interrupted",
9
+ CANCELLED: "cancelled",
10
+ POSTPONED: "postponed"
11
+ };
12
+ const normalizedType = raw.type.toUpperCase();
13
+ return {
14
+ code: typeMap[normalizedType] ?? "not_started",
15
+ name: raw.name,
16
+ shortName: raw.short_name,
17
+ type: normalizedType
18
+ };
19
+ }
20
+ function transformParticipant(raw) {
21
+ if (raw.type === "PLAYER") {
22
+ const player = {
23
+ id: raw.id,
24
+ name: raw.name,
25
+ shortName: raw.short_name,
26
+ threeLetterCode: raw.three_letter_code,
27
+ type: "player",
28
+ country: raw.details?.[0]?.country ? {
29
+ id: raw.details[0].country.id,
30
+ name: raw.details[0].country.name
31
+ } : void 0,
32
+ gender: raw.details?.[0]?.gender
33
+ };
34
+ return player;
35
+ } else if (raw.type === "TEAM_DOUBLE" && raw.details && raw.details.length === 2) {
36
+ const player1 = {
37
+ id: raw.details[0].id,
38
+ name: raw.details[0].name,
39
+ shortName: raw.details[0].short_name,
40
+ threeLetterCode: raw.details[0].three_letter_code,
41
+ type: "player",
42
+ country: raw.details[0].country ? {
43
+ id: raw.details[0].country.id,
44
+ name: raw.details[0].country.name
45
+ } : void 0,
46
+ gender: raw.details[0].gender
47
+ };
48
+ const player2 = {
49
+ id: raw.details[1].id,
50
+ name: raw.details[1].name,
51
+ shortName: raw.details[1].short_name,
52
+ threeLetterCode: raw.details[1].three_letter_code,
53
+ type: "player",
54
+ country: raw.details[1].country ? {
55
+ id: raw.details[1].country.id,
56
+ name: raw.details[1].country.name
57
+ } : void 0,
58
+ gender: raw.details[1].gender
59
+ };
60
+ const pair = {
61
+ id: raw.id,
62
+ name: raw.name,
63
+ shortName: raw.short_name,
64
+ threeLetterCode: raw.three_letter_code,
65
+ type: "pair",
66
+ players: [player1, player2]
67
+ };
68
+ return pair;
69
+ } else {
70
+ const team = {
71
+ id: raw.id,
72
+ name: raw.name,
73
+ shortName: raw.short_name,
74
+ threeLetterCode: raw.three_letter_code,
75
+ type: "national",
76
+ country: raw.details?.[0]?.country ? {
77
+ id: raw.details[0].country.id,
78
+ name: raw.details[0].country.name
79
+ } : void 0,
80
+ gender: raw.details?.[0]?.gender
81
+ };
82
+ return team;
83
+ }
84
+ }
85
+ function transformTiming(raw) {
86
+ return {
87
+ kickoffTime: new Date(raw.scheduled_start_time),
88
+ phaseStartedAt: raw.started_at ? new Date(raw.started_at) : void 0,
89
+ finishedAt: raw.finished_at ? new Date(raw.finished_at) : void 0
90
+ };
91
+ }
92
+ function transformCompetition(raw) {
93
+ if (!raw.tournament) return void 0;
94
+ const tournament = raw.tournament;
95
+ return {
96
+ id: tournament.competition?.id ?? tournament.id,
97
+ name: tournament.competition?.name ?? tournament.name,
98
+ slug: tournament.competition?.slug ?? tournament.slug,
99
+ type: "TOURNAMENT",
100
+ gender: tournament.gender,
101
+ region: "INTERNATIONAL",
102
+ country: tournament.country ? {
103
+ id: tournament.country.id,
104
+ name: tournament.country.name
105
+ } : void 0
106
+ };
107
+ }
108
+ function transformScore(raw) {
109
+ if (!raw.results || raw.results.length === 0) return void 0;
110
+ const setResults = raw.results.filter((r) => /^set\d+$/i.test(r.type.code));
111
+ if (setResults.length === 0) return void 0;
112
+ const periods = [];
113
+ for (const result of setResults) {
114
+ const val1 = result.values.find((v) => v.position === "1");
115
+ const val2 = result.values.find((v) => v.position === "2");
116
+ if (!val1 || !val2 || val1.value == null || val2.value == null) continue;
117
+ periods.push({
118
+ competitorOne: val1.value,
119
+ competitorTwo: val2.value
120
+ });
121
+ }
122
+ if (periods.length === 0) return void 0;
123
+ const setsWonResult = raw.results.find((r) => r.type.code === "setswon");
124
+ const setsWonVal1 = setsWonResult?.values.find((v) => v.position === "1");
125
+ const setsWonVal2 = setsWonResult?.values.find((v) => v.position === "2");
126
+ let totalSetsOne;
127
+ let totalSetsTwo;
128
+ if (setsWonVal1?.value != null && setsWonVal2?.value != null) {
129
+ totalSetsOne = parseInt(setsWonVal1.value);
130
+ totalSetsTwo = parseInt(setsWonVal2.value);
131
+ } else {
132
+ totalSetsOne = periods.filter((p) => parseInt(p.competitorOne) > parseInt(p.competitorTwo)).length;
133
+ totalSetsTwo = periods.filter((p) => parseInt(p.competitorTwo) > parseInt(p.competitorOne)).length;
134
+ }
135
+ return {
136
+ competitorOne: String(totalSetsOne),
137
+ competitorTwo: String(totalSetsTwo),
138
+ breakdown: {
139
+ total: {
140
+ competitorOne: String(totalSetsOne),
141
+ competitorTwo: String(totalSetsTwo)
142
+ },
143
+ periods
144
+ }
145
+ };
146
+ }
147
+ function transformWinner(raw) {
148
+ const winner = raw.participants.find((p) => p.winner === "YES");
149
+ if (!winner) return void 0;
150
+ return {
151
+ competitorId: winner.id
152
+ };
153
+ }
154
+ function transformTennisMatch(raw) {
155
+ const participants = raw.participants.sort((a, b) => a.position - b.position);
156
+ return {
157
+ id: raw.id,
158
+ slug: raw.slug,
159
+ sport: "tennis",
160
+ status: transformStatus(raw.status),
161
+ timing: transformTiming(raw),
162
+ competitorOne: transformParticipant(participants[0]),
163
+ competitorTwo: transformParticipant(participants[1]),
164
+ competition: transformCompetition(raw),
165
+ round: raw.round ? {
166
+ id: raw.round.id,
167
+ key: raw.round.uuid,
168
+ name: raw.round.name,
169
+ type: "REGULAR",
170
+ providerRef: toProviderRefArray(raw.round.uuid)
171
+ } : void 0,
172
+ score: transformScore(raw),
173
+ winner: transformWinner(raw),
174
+ odds: raw.odds ? transformOdds(raw.odds) : void 0,
175
+ coverage: raw.coverage?.type === "NOT_LIVE" ? "UNKNOWN" : raw.coverage?.type,
176
+ metadata: {
177
+ matchType: raw.type,
178
+ bestOfSets: raw.best_of_sets,
179
+ surface: raw.tournament?.surface,
180
+ indoorOutdoor: raw.tournament?.indoor_outdoor
181
+ },
182
+ providerRef: [
183
+ {
184
+ provider: "sportal365-tennis",
185
+ id: raw.id
186
+ }
187
+ ]
188
+ };
189
+ }
190
+ export {
191
+ transformTennisMatch
192
+ };
193
+ //# sourceMappingURL=match.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"match.js","sources":["../../../../../../src/lib/api/sportal365-sports/tennis/matches/transformers/match.ts"],"sourcesContent":["/**\n * Tennis match transformation utilities\n * Transforms raw Tennis API match responses to canonical FUSports types\n */\n\nimport type {\n FUSportsMatch,\n FUSportsMatchStatus,\n FUSportsMatchTiming,\n FUSportsCompetitor,\n FUSportsPlayerCompetitor,\n FUSportsPairCompetitor,\n FUSportsTeamCompetitor,\n FUSportsCompetition,\n FUSportsMatchScore,\n FUSportsMatchWinner,\n} from \"../../../../../types/canonical\";\nimport type { RawTennisMatch, RawTennisParticipant } from \"../types\";\nimport { toProviderRefArray, transformOdds } from \"../../../shared\";\n\n/**\n * Transform raw tennis status to canonical format\n */\nfunction transformStatus(raw: RawTennisMatch[\"status\"]): FUSportsMatchStatus {\n const typeMap: Record<string, FUSportsMatchStatus[\"code\"]> = {\n FINISHED: \"finished\",\n NOT_STARTED: \"not_started\",\n LIVE: \"live\",\n INTERRUPTED: \"interrupted\",\n CANCELLED: \"cancelled\",\n POSTPONED: \"postponed\",\n };\n\n const normalizedType = raw.type.toUpperCase();\n return {\n code: typeMap[normalizedType] ?? \"not_started\",\n name: raw.name,\n shortName: raw.short_name,\n type: normalizedType as FUSportsMatchStatus[\"type\"],\n };\n}\n\n/**\n * Transform raw tennis participant to canonical competitor\n * Tennis can have: individual players, pairs (doubles), or teams\n */\nfunction transformParticipant(raw: RawTennisParticipant): FUSportsCompetitor {\n if (raw.type === \"PLAYER\") {\n // Single player\n const player: FUSportsPlayerCompetitor = {\n id: raw.id,\n name: raw.name,\n shortName: raw.short_name,\n threeLetterCode: raw.three_letter_code,\n type: \"player\",\n country: raw.details?.[0]?.country\n ? {\n id: raw.details[0].country.id,\n name: raw.details[0].country.name,\n }\n : undefined,\n gender: raw.details?.[0]?.gender as FUSportsPlayerCompetitor[\"gender\"],\n };\n return player;\n } else if (raw.type === \"TEAM_DOUBLE\" && raw.details && raw.details.length === 2) {\n // Pair/doubles team\n const player1: FUSportsPlayerCompetitor = {\n id: raw.details[0].id,\n name: raw.details[0].name,\n shortName: raw.details[0].short_name,\n threeLetterCode: raw.details[0].three_letter_code,\n type: \"player\",\n country: raw.details[0].country\n ? {\n id: raw.details[0].country.id,\n name: raw.details[0].country.name,\n }\n : undefined,\n gender: raw.details[0].gender as FUSportsPlayerCompetitor[\"gender\"],\n };\n\n const player2: FUSportsPlayerCompetitor = {\n id: raw.details[1].id,\n name: raw.details[1].name,\n shortName: raw.details[1].short_name,\n threeLetterCode: raw.details[1].three_letter_code,\n type: \"player\",\n country: raw.details[1].country\n ? {\n id: raw.details[1].country.id,\n name: raw.details[1].country.name,\n }\n : undefined,\n gender: raw.details[1].gender as FUSportsPlayerCompetitor[\"gender\"],\n };\n\n const pair: FUSportsPairCompetitor = {\n id: raw.id,\n name: raw.name,\n shortName: raw.short_name,\n threeLetterCode: raw.three_letter_code,\n type: \"pair\",\n players: [player1, player2],\n };\n return pair;\n } else {\n // Team (for team competitions like Davis Cup)\n const team: FUSportsTeamCompetitor = {\n id: raw.id,\n name: raw.name,\n shortName: raw.short_name,\n threeLetterCode: raw.three_letter_code,\n type: \"national\",\n country: raw.details?.[0]?.country\n ? {\n id: raw.details[0].country.id,\n name: raw.details[0].country.name,\n }\n : undefined,\n gender: raw.details?.[0]?.gender as FUSportsTeamCompetitor[\"gender\"],\n };\n return team;\n }\n}\n\n/**\n * Transform raw tennis timing to canonical format\n */\nfunction transformTiming(raw: RawTennisMatch): FUSportsMatchTiming {\n return {\n kickoffTime: new Date(raw.scheduled_start_time),\n phaseStartedAt: raw.started_at ? new Date(raw.started_at) : undefined,\n finishedAt: raw.finished_at ? new Date(raw.finished_at) : undefined,\n };\n}\n\n/**\n * Transform raw tennis competition to canonical format\n */\nfunction transformCompetition(raw: RawTennisMatch): FUSportsCompetition | undefined {\n if (!raw.tournament) return undefined;\n\n const tournament = raw.tournament;\n return {\n id: tournament.competition?.id ?? tournament.id,\n name: tournament.competition?.name ?? tournament.name,\n slug: tournament.competition?.slug ?? tournament.slug,\n type: \"TOURNAMENT\" as const,\n gender: tournament.gender as FUSportsCompetition[\"gender\"],\n region: \"INTERNATIONAL\" as const,\n country: tournament.country\n ? {\n id: tournament.country.id,\n name: tournament.country.name,\n }\n : undefined,\n };\n}\n\n/**\n * Transform raw tennis score to canonical format\n * Tennis scores are an array of sets with games\n */\nfunction transformScore(raw: RawTennisMatch): FUSportsMatchScore | undefined {\n if (!raw.results || raw.results.length === 0) return undefined;\n\n // Individual set entries have codes \"set1\", \"set2\", \"set3\", etc.\n // Other entries: \"gamescore\", \"setswon\", \"runningscore\", \"finalresult\"\n const setResults = raw.results.filter((r) => /^set\\d+$/i.test(r.type.code));\n if (setResults.length === 0) return undefined;\n\n const periods: Array<{ competitorOne: string; competitorTwo: string }> = [];\n\n for (const result of setResults) {\n const val1 = result.values.find((v) => v.position === \"1\");\n const val2 = result.values.find((v) => v.position === \"2\");\n\n if (!val1 || !val2 || val1.value == null || val2.value == null) continue;\n\n periods.push({\n competitorOne: val1.value,\n competitorTwo: val2.value,\n });\n }\n\n if (periods.length === 0) return undefined;\n\n // Use the \"setswon\" entry for the total sets score if available — more reliable than counting\n const setsWonResult = raw.results.find((r) => r.type.code === \"setswon\");\n const setsWonVal1 = setsWonResult?.values.find((v) => v.position === \"1\");\n const setsWonVal2 = setsWonResult?.values.find((v) => v.position === \"2\");\n\n let totalSetsOne: number;\n let totalSetsTwo: number;\n\n if (setsWonVal1?.value != null && setsWonVal2?.value != null) {\n totalSetsOne = parseInt(setsWonVal1.value);\n totalSetsTwo = parseInt(setsWonVal2.value);\n } else {\n // Fallback: count set wins from individual set entries\n totalSetsOne = periods.filter((p) => parseInt(p.competitorOne) > parseInt(p.competitorTwo)).length;\n totalSetsTwo = periods.filter((p) => parseInt(p.competitorTwo) > parseInt(p.competitorOne)).length;\n }\n\n return {\n competitorOne: String(totalSetsOne),\n competitorTwo: String(totalSetsTwo),\n breakdown: {\n total: {\n competitorOne: String(totalSetsOne),\n competitorTwo: String(totalSetsTwo),\n },\n periods,\n },\n };\n}\n\n/**\n * Transform raw tennis winner to canonical format\n */\nfunction transformWinner(raw: RawTennisMatch): FUSportsMatchWinner | undefined {\n const winner = raw.participants.find((p) => p.winner === \"YES\");\n if (!winner) return undefined;\n\n return {\n competitorId: winner.id,\n };\n}\n\n/**\n * Transform raw tennis match response to canonical FUSportsMatch\n */\nexport function transformTennisMatch(raw: RawTennisMatch): FUSportsMatch {\n const participants = raw.participants.sort((a, b) => a.position - b.position);\n\n return {\n id: raw.id,\n slug: raw.slug,\n sport: \"tennis\",\n status: transformStatus(raw.status),\n timing: transformTiming(raw),\n\n competitorOne: transformParticipant(participants[0]),\n competitorTwo: transformParticipant(participants[1]),\n\n competition: transformCompetition(raw),\n round: raw.round\n ? {\n id: raw.round.id,\n key: raw.round.uuid,\n name: raw.round.name,\n type: \"REGULAR\",\n providerRef: toProviderRefArray(raw.round.uuid),\n }\n : undefined,\n\n score: transformScore(raw),\n\n winner: transformWinner(raw),\n\n odds: raw.odds ? transformOdds(raw.odds) : undefined,\n\n coverage: raw.coverage?.type === \"NOT_LIVE\" ? \"UNKNOWN\" : (raw.coverage?.type as FUSportsMatch[\"coverage\"]),\n\n metadata: {\n matchType: raw.type,\n bestOfSets: raw.best_of_sets,\n surface: raw.tournament?.surface,\n indoorOutdoor: raw.tournament?.indoor_outdoor,\n },\n\n providerRef: [\n {\n provider: \"sportal365-tennis\",\n id: raw.id,\n },\n ],\n };\n}\n"],"names":[],"mappings":"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"}