fansunited-data-layer 0.15.3 → 0.17.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 +41 -0
- package/api/fansunited/index.d.ts +2 -0
- package/api/fansunited/index.d.ts.map +1 -1
- package/api/fansunited/search/__tests__/locale-agnostic-cache.test.d.ts +2 -0
- package/api/fansunited/search/__tests__/locale-agnostic-cache.test.d.ts.map +1 -0
- package/api/fansunited/search/index.d.ts.map +1 -1
- package/api/fansunited/search/index.js +41 -25
- package/api/fansunited/search/index.js.map +1 -1
- package/api/fansunited/search/transformer.js +3 -2
- package/api/fansunited/search/transformer.js.map +1 -1
- package/api/fansunited/sports/competition/__tests__/exports.test.d.ts +2 -0
- package/api/fansunited/sports/competition/__tests__/exports.test.d.ts.map +1 -0
- package/api/fansunited/sports/competition/__tests__/hydrated.test.d.ts +2 -0
- package/api/fansunited/sports/competition/__tests__/hydrated.test.d.ts.map +1 -0
- package/api/fansunited/sports/competition/__tests__/index.test.d.ts +2 -0
- package/api/fansunited/sports/competition/__tests__/index.test.d.ts.map +1 -0
- package/api/fansunited/sports/competition/hydrated.d.ts +45 -0
- package/api/fansunited/sports/competition/hydrated.d.ts.map +1 -0
- package/api/fansunited/sports/competition/hydrated.js +26 -0
- package/api/fansunited/sports/competition/hydrated.js.map +1 -0
- package/api/fansunited/sports/competition/index.d.ts +19 -0
- package/api/fansunited/sports/competition/index.d.ts.map +1 -0
- package/api/fansunited/sports/competition/index.js +27 -0
- package/api/fansunited/sports/competition/index.js.map +1 -0
- package/api/fansunited/sports/competition/transformer.d.ts +8 -0
- package/api/fansunited/sports/competition/transformer.d.ts.map +1 -0
- package/api/fansunited/sports/competition/transformer.js +52 -0
- package/api/fansunited/sports/competition/transformer.js.map +1 -0
- package/api/fansunited/sports/competition/types.d.ts +57 -0
- package/api/fansunited/sports/competition/types.d.ts.map +1 -0
- package/api/fansunited/sports/constants.d.ts +5 -0
- package/api/fansunited/sports/constants.d.ts.map +1 -0
- package/api/fansunited/sports/constants.js +5 -0
- package/api/fansunited/sports/constants.js.map +1 -0
- package/api/fansunited/sports/http.d.ts +8 -0
- package/api/fansunited/sports/http.d.ts.map +1 -0
- package/api/fansunited/sports/http.js +7 -0
- package/api/fansunited/sports/http.js.map +1 -0
- package/api/fansunited/sports/index.d.ts +15 -0
- package/api/fansunited/sports/index.d.ts.map +1 -0
- package/api/fansunited/sports/livescore/__tests__/hydrated.test.d.ts +2 -0
- package/api/fansunited/sports/livescore/__tests__/hydrated.test.d.ts.map +1 -0
- package/api/fansunited/sports/livescore/hydrated.d.ts +48 -0
- package/api/fansunited/sports/livescore/hydrated.d.ts.map +1 -0
- package/api/fansunited/sports/livescore/hydrated.js +34 -0
- package/api/fansunited/sports/livescore/hydrated.js.map +1 -0
- package/api/fansunited/sports/livescore/index.d.ts +27 -0
- package/api/fansunited/sports/livescore/index.d.ts.map +1 -0
- package/api/fansunited/sports/livescore/index.js +36 -0
- package/api/fansunited/sports/livescore/index.js.map +1 -0
- package/api/fansunited/sports/livescore/transformer.d.ts +8 -0
- package/api/fansunited/sports/livescore/transformer.d.ts.map +1 -0
- package/api/fansunited/sports/livescore/transformer.js +44 -0
- package/api/fansunited/sports/livescore/transformer.js.map +1 -0
- package/api/fansunited/sports/livescore/types.d.ts +58 -0
- package/api/fansunited/sports/livescore/types.d.ts.map +1 -0
- package/cache/__tests__/redis-integration.test.d.ts +14 -0
- package/cache/__tests__/redis-integration.test.d.ts.map +1 -0
- package/cache/__tests__/redis-l2-store.d.ts +52 -0
- package/cache/__tests__/redis-l2-store.d.ts.map +1 -0
- package/cache/__tests__/test-l2-store.d.ts +18 -0
- package/cache/__tests__/test-l2-store.d.ts.map +1 -0
- package/cache/cache-manager.d.ts +117 -12
- package/cache/cache-manager.d.ts.map +1 -1
- package/cache/cache-manager.js +23 -14
- package/cache/cache-manager.js.map +1 -1
- package/cache/cleanup.d.ts +1 -1
- package/cache/cleanup.d.ts.map +1 -1
- package/cache/index.d.ts +3 -2
- package/cache/index.d.ts.map +1 -1
- package/cache/sqlite-store.d.ts +4 -4
- package/cache/sqlite-store.d.ts.map +1 -1
- package/cache/types.d.ts +9 -1
- package/cache/types.d.ts.map +1 -1
- package/cache/types.js +5 -0
- package/cache/types.js.map +1 -0
- package/fansunited-data-layer.js +13 -1
- package/fansunited-data-layer.js.map +1 -1
- package/index.d.ts +5 -5
- package/index.d.ts.map +1 -1
- package/package.json +2 -1
- package/types/canonical/index.d.ts +2 -0
- package/types/canonical/index.d.ts.map +1 -1
- package/types/canonical/sports-competition.types.d.ts +100 -0
- package/types/canonical/sports-competition.types.d.ts.map +1 -0
- package/types/canonical/sports-livescore.types.d.ts +84 -0
- package/types/canonical/sports-livescore.types.d.ts.map +1 -0
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hydrated wrapper for the Fans United Sports API competition endpoint.
|
|
3
|
+
*
|
|
4
|
+
* Composes the (cached) raw competition response with a (cached) batch
|
|
5
|
+
* resolution of every entity ID in `meta.entityIds` via the Search API,
|
|
6
|
+
* and returns one object the consumer can render directly.
|
|
7
|
+
*
|
|
8
|
+
* Cache topology:
|
|
9
|
+
* - sports/competition response → cached under "sportsCompetition"
|
|
10
|
+
* (5min stale / 1h max) by id+season.
|
|
11
|
+
* - search/entity responses → cached under "search" (24h stale / 7d max)
|
|
12
|
+
* by id only; the raw multi-locale payload serves every locale via
|
|
13
|
+
* transformEntity(raw, lang).
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* const data = await getFansUnitedSportsCompetitionHydrated("fb:c:1", { locale: "BG" });
|
|
17
|
+
* for (const entry of data.season.stages[0].groups[0].standings) {
|
|
18
|
+
* const team = data.entities.get(entry.competitorId);
|
|
19
|
+
* console.log(`${entry.rank} ${team?.name}`);
|
|
20
|
+
* }
|
|
21
|
+
*/
|
|
22
|
+
import type { FUSportsCompetitionDetails } from "../../../../types/canonical/sports-competition.types";
|
|
23
|
+
import type { DataLayerConfig } from "../../../../config";
|
|
24
|
+
import type { NextCacheOptions } from "../../http";
|
|
25
|
+
import type { SearchEntityResult } from "../../search/types";
|
|
26
|
+
export interface GetSportsCompetitionHydratedOptions {
|
|
27
|
+
/** Season identifier (e.g. "fb:c:1:2025/26"). Defaults to the active season. */
|
|
28
|
+
seasonId?: string;
|
|
29
|
+
/** Locale code (e.g. "EN", "BG") used to resolve entity translations. Falls back to EN. */
|
|
30
|
+
locale?: string;
|
|
31
|
+
/** Next.js cache options for ISR/on-demand revalidation. */
|
|
32
|
+
next?: NextCacheOptions;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Sports API competition response plus a lookup map for every referenced entity.
|
|
36
|
+
*
|
|
37
|
+
* The map is keyed by entity ID. Missing IDs (404s from the Search API) are
|
|
38
|
+
* silently omitted — render code should fall back gracefully on a missing entry:
|
|
39
|
+
* `entities.get(id) ?? { name: id }`.
|
|
40
|
+
*/
|
|
41
|
+
export interface FUSportsCompetitionHydrated extends FUSportsCompetitionDetails {
|
|
42
|
+
entities: Map<string, SearchEntityResult>;
|
|
43
|
+
}
|
|
44
|
+
export declare function getFansUnitedSportsCompetitionHydrated(competitionId: string, options?: GetSportsCompetitionHydratedOptions, config?: DataLayerConfig): Promise<FUSportsCompetitionHydrated>;
|
|
45
|
+
//# sourceMappingURL=hydrated.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hydrated.d.ts","sourceRoot":"","sources":["../../../../../src/lib/api/fansunited/sports/competition/hydrated.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,sDAAsD,CAAC;AACvG,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAC1D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AACnD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAI7D,MAAM,WAAW,mCAAmC;IAChD,gFAAgF;IAChF,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,2FAA2F;IAC3F,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,4DAA4D;IAC5D,IAAI,CAAC,EAAE,gBAAgB,CAAC;CAC3B;AAED;;;;;;GAMG;AACH,MAAM,WAAW,2BAA4B,SAAQ,0BAA0B;IAC3E,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAAC;CAC7C;AAED,wBAAsB,sCAAsC,CACxD,aAAa,EAAE,MAAM,EACrB,OAAO,CAAC,EAAE,mCAAmC,EAC7C,MAAM,CAAC,EAAE,eAAe,GACzB,OAAO,CAAC,2BAA2B,CAAC,CAsBtC"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { getFansUnitedSportsCompetition } from "./index.js";
|
|
2
|
+
import { getFansUnitedEntitiesByIds } from "../../search/index.js";
|
|
3
|
+
async function getFansUnitedSportsCompetitionHydrated(competitionId, options, config) {
|
|
4
|
+
const raw = await getFansUnitedSportsCompetition(
|
|
5
|
+
competitionId,
|
|
6
|
+
{ seasonId: options?.seasonId, next: options?.next },
|
|
7
|
+
config
|
|
8
|
+
);
|
|
9
|
+
const entityIds = raw.meta.entityIds;
|
|
10
|
+
const entities = /* @__PURE__ */ new Map();
|
|
11
|
+
if (entityIds.length > 0) {
|
|
12
|
+
const resolved = await getFansUnitedEntitiesByIds(entityIds, {
|
|
13
|
+
lang: options?.locale,
|
|
14
|
+
next: options?.next,
|
|
15
|
+
config
|
|
16
|
+
});
|
|
17
|
+
for (const e of resolved) {
|
|
18
|
+
entities.set(e.id, e);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
return { ...raw, entities };
|
|
22
|
+
}
|
|
23
|
+
export {
|
|
24
|
+
getFansUnitedSportsCompetitionHydrated
|
|
25
|
+
};
|
|
26
|
+
//# sourceMappingURL=hydrated.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hydrated.js","sources":["../../../../../src/lib/api/fansunited/sports/competition/hydrated.ts"],"sourcesContent":["/**\n * Hydrated wrapper for the Fans United Sports API competition endpoint.\n *\n * Composes the (cached) raw competition response with a (cached) batch\n * resolution of every entity ID in `meta.entityIds` via the Search API,\n * and returns one object the consumer can render directly.\n *\n * Cache topology:\n * - sports/competition response → cached under \"sportsCompetition\"\n * (5min stale / 1h max) by id+season.\n * - search/entity responses → cached under \"search\" (24h stale / 7d max)\n * by id only; the raw multi-locale payload serves every locale via\n * transformEntity(raw, lang).\n *\n * @example\n * const data = await getFansUnitedSportsCompetitionHydrated(\"fb:c:1\", { locale: \"BG\" });\n * for (const entry of data.season.stages[0].groups[0].standings) {\n * const team = data.entities.get(entry.competitorId);\n * console.log(`${entry.rank} ${team?.name}`);\n * }\n */\n\nimport type { FUSportsCompetitionDetails } from \"../../../../types/canonical/sports-competition.types\";\nimport type { DataLayerConfig } from \"../../../../config\";\nimport type { NextCacheOptions } from \"../../http\";\nimport type { SearchEntityResult } from \"../../search/types\";\nimport { getFansUnitedSportsCompetition } from \"./index\";\nimport { getFansUnitedEntitiesByIds } from \"../../search\";\n\nexport interface GetSportsCompetitionHydratedOptions {\n /** Season identifier (e.g. \"fb:c:1:2025/26\"). Defaults to the active season. */\n seasonId?: string;\n /** Locale code (e.g. \"EN\", \"BG\") used to resolve entity translations. Falls back to EN. */\n locale?: string;\n /** Next.js cache options for ISR/on-demand revalidation. */\n next?: NextCacheOptions;\n}\n\n/**\n * Sports API competition response plus a lookup map for every referenced entity.\n *\n * The map is keyed by entity ID. Missing IDs (404s from the Search API) are\n * silently omitted — render code should fall back gracefully on a missing entry:\n * `entities.get(id) ?? { name: id }`.\n */\nexport interface FUSportsCompetitionHydrated extends FUSportsCompetitionDetails {\n entities: Map<string, SearchEntityResult>;\n}\n\nexport async function getFansUnitedSportsCompetitionHydrated(\n competitionId: string,\n options?: GetSportsCompetitionHydratedOptions,\n config?: DataLayerConfig\n): Promise<FUSportsCompetitionHydrated> {\n const raw = await getFansUnitedSportsCompetition(\n competitionId,\n { seasonId: options?.seasonId, next: options?.next },\n config\n );\n\n const entityIds = raw.meta.entityIds;\n const entities = new Map<string, SearchEntityResult>();\n\n if (entityIds.length > 0) {\n const resolved = await getFansUnitedEntitiesByIds(entityIds, {\n lang: options?.locale,\n next: options?.next,\n config,\n });\n for (const e of resolved) {\n entities.set(e.id, e);\n }\n }\n\n return { ...raw, entities };\n}\n"],"names":[],"mappings":";;AAiDA,eAAsB,uCAClB,eACA,SACA,QACoC;AACpC,QAAM,MAAM,MAAM;AAAA,IACd;AAAA,IACA,EAAE,UAAU,SAAS,UAAU,MAAM,SAAS,KAAA;AAAA,IAC9C;AAAA,EAAA;AAGJ,QAAM,YAAY,IAAI,KAAK;AAC3B,QAAM,+BAAe,IAAA;AAErB,MAAI,UAAU,SAAS,GAAG;AACtB,UAAM,WAAW,MAAM,2BAA2B,WAAW;AAAA,MACzD,MAAM,SAAS;AAAA,MACf,MAAM,SAAS;AAAA,MACf;AAAA,IAAA,CACH;AACD,eAAW,KAAK,UAAU;AACtB,eAAS,IAAI,EAAE,IAAI,CAAC;AAAA,IACxB;AAAA,EACJ;AAEA,SAAO,EAAE,GAAG,KAAK,SAAA;AACrB;"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Competition endpoint for the Fans United Sports API.
|
|
3
|
+
*/
|
|
4
|
+
import type { FUSportsCompetitionDetails } from "../../../../types/canonical/sports-competition.types";
|
|
5
|
+
import { type DataLayerConfig } from "../../../../config";
|
|
6
|
+
import type { GetSportsCompetitionOptions } from "./types";
|
|
7
|
+
export type { GetSportsCompetitionOptions } from "./types";
|
|
8
|
+
/**
|
|
9
|
+
* Get a competition by ID from the Fans United Sports API.
|
|
10
|
+
*
|
|
11
|
+
* Cached with SWR under entity type "sportsCompetition" (5min stale / 1h max).
|
|
12
|
+
* Cache key: `fudl:sports:competition:<id>:<seasonId | "active">`.
|
|
13
|
+
*
|
|
14
|
+
* @param competitionId - Competition identifier (e.g. "fb:c:1")
|
|
15
|
+
* @param options - Query options (season override, Next.js cache hints)
|
|
16
|
+
* @param config - Optional data layer config (uses the singleton if omitted)
|
|
17
|
+
*/
|
|
18
|
+
export declare function getFansUnitedSportsCompetition(competitionId: string, options?: GetSportsCompetitionOptions, config?: DataLayerConfig): Promise<FUSportsCompetitionDetails>;
|
|
19
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/lib/api/fansunited/sports/competition/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,sDAAsD,CAAC;AACvG,OAAO,EAAa,KAAK,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAIrE,OAAO,KAAK,EAAE,2BAA2B,EAAgC,MAAM,SAAS,CAAC;AAEzF,YAAY,EAAE,2BAA2B,EAAE,MAAM,SAAS,CAAC;AAE3D;;;;;;;;;GASG;AACH,wBAAsB,8BAA8B,CAChD,aAAa,EAAE,MAAM,EACrB,OAAO,CAAC,EAAE,2BAA2B,EACrC,MAAM,CAAC,EAAE,eAAe,GACzB,OAAO,CAAC,0BAA0B,CAAC,CAoBrC"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { getConfig } from "../../../../config/index.js";
|
|
2
|
+
import { sportsHttp } from "../http.js";
|
|
3
|
+
import { transformSportsCompetition } from "./transformer.js";
|
|
4
|
+
import { cached } from "../../../../cache/cache-manager.js";
|
|
5
|
+
import { CACHE_KEY_PREFIX } from "../../../../cache/types.js";
|
|
6
|
+
async function getFansUnitedSportsCompetition(competitionId, options, config) {
|
|
7
|
+
const finalConfig = config || getConfig();
|
|
8
|
+
const seasonKeyPart = options?.seasonId ?? "active";
|
|
9
|
+
const cacheKey = `${CACHE_KEY_PREFIX}sports:competition:${competitionId}:${seasonKeyPart}`;
|
|
10
|
+
return cached(cacheKey, "sportsCompetition", async () => {
|
|
11
|
+
const params = {};
|
|
12
|
+
if (options?.seasonId) {
|
|
13
|
+
params.season_id = options.seasonId;
|
|
14
|
+
}
|
|
15
|
+
const raw = await sportsHttp.get({
|
|
16
|
+
path: `/v1/competitions/${competitionId}`,
|
|
17
|
+
params,
|
|
18
|
+
next: options?.next,
|
|
19
|
+
config: finalConfig
|
|
20
|
+
});
|
|
21
|
+
return transformSportsCompetition(raw);
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
export {
|
|
25
|
+
getFansUnitedSportsCompetition
|
|
26
|
+
};
|
|
27
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../../../../../src/lib/api/fansunited/sports/competition/index.ts"],"sourcesContent":["/**\n * Competition endpoint for the Fans United Sports API.\n */\n\nimport type { FUSportsCompetitionDetails } from \"../../../../types/canonical/sports-competition.types\";\nimport { getConfig, type DataLayerConfig } from \"../../../../config\";\nimport { cached, CACHE_KEY_PREFIX } from \"../../../../cache\";\nimport { sportsHttp } from \"../http\";\nimport { transformSportsCompetition } from \"./transformer\";\nimport type { GetSportsCompetitionOptions, RawSportsCompetitionResponse } from \"./types\";\n\nexport type { GetSportsCompetitionOptions } from \"./types\";\n\n/**\n * Get a competition by ID from the Fans United Sports API.\n *\n * Cached with SWR under entity type \"sportsCompetition\" (5min stale / 1h max).\n * Cache key: `fudl:sports:competition:<id>:<seasonId | \"active\">`.\n *\n * @param competitionId - Competition identifier (e.g. \"fb:c:1\")\n * @param options - Query options (season override, Next.js cache hints)\n * @param config - Optional data layer config (uses the singleton if omitted)\n */\nexport async function getFansUnitedSportsCompetition(\n competitionId: string,\n options?: GetSportsCompetitionOptions,\n config?: DataLayerConfig\n): Promise<FUSportsCompetitionDetails> {\n const finalConfig = config || getConfig();\n const seasonKeyPart = options?.seasonId ?? \"active\";\n const cacheKey = `${CACHE_KEY_PREFIX}sports:competition:${competitionId}:${seasonKeyPart}`;\n\n return cached(cacheKey, \"sportsCompetition\", async () => {\n const params: Record<string, string | undefined> = {};\n if (options?.seasonId) {\n params.season_id = options.seasonId;\n }\n\n const raw = await sportsHttp.get<RawSportsCompetitionResponse>({\n path: `/v1/competitions/${competitionId}`,\n params,\n next: options?.next,\n config: finalConfig,\n });\n\n return transformSportsCompetition(raw);\n });\n}\n"],"names":[],"mappings":";;;;;AAuBA,eAAsB,+BAClB,eACA,SACA,QACmC;AACnC,QAAM,cAAc,UAAU,UAAA;AAC9B,QAAM,gBAAgB,SAAS,YAAY;AAC3C,QAAM,WAAW,GAAG,gBAAgB,sBAAsB,aAAa,IAAI,aAAa;AAExF,SAAO,OAAO,UAAU,qBAAqB,YAAY;AACrD,UAAM,SAA6C,CAAA;AACnD,QAAI,SAAS,UAAU;AACnB,aAAO,YAAY,QAAQ;AAAA,IAC/B;AAEA,UAAM,MAAM,MAAM,WAAW,IAAkC;AAAA,MAC3D,MAAM,oBAAoB,aAAa;AAAA,MACvC;AAAA,MACA,MAAM,SAAS;AAAA,MACf,QAAQ;AAAA,IAAA,CACX;AAED,WAAO,2BAA2B,GAAG;AAAA,EACzC,CAAC;AACL;"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Transforms the raw Sports API competition response into the canonical
|
|
3
|
+
* {@link FUSportsCompetitionDetails} shape.
|
|
4
|
+
*/
|
|
5
|
+
import type { FUSportsCompetitionDetails } from "../../../../types/canonical/sports-competition.types";
|
|
6
|
+
import type { RawSportsCompetitionResponse } from "./types";
|
|
7
|
+
export declare function transformSportsCompetition(raw: RawSportsCompetitionResponse): FUSportsCompetitionDetails;
|
|
8
|
+
//# sourceMappingURL=transformer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transformer.d.ts","sourceRoot":"","sources":["../../../../../src/lib/api/fansunited/sports/competition/transformer.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EACR,0BAA0B,EAK7B,MAAM,sDAAsD,CAAC;AAC9D,OAAO,KAAK,EACR,4BAA4B,EAK/B,MAAM,SAAS,CAAC;AA8CjB,wBAAgB,0BAA0B,CAAC,GAAG,EAAE,4BAA4B,GAAG,0BAA0B,CAOxG"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
function transformStandingEntry(raw) {
|
|
2
|
+
return {
|
|
3
|
+
rank: raw.rank,
|
|
4
|
+
lastRank: raw.last_rank,
|
|
5
|
+
competitorId: raw.competitor_id,
|
|
6
|
+
stats: raw.stats.map((s) => ({ key: s.key, value: s.value })),
|
|
7
|
+
rules: raw.rules
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
function transformStandingGroup(raw) {
|
|
11
|
+
return {
|
|
12
|
+
id: raw.id,
|
|
13
|
+
name: raw.name,
|
|
14
|
+
standings: raw.standings.map(transformStandingEntry)
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
function transformStage(raw) {
|
|
18
|
+
return {
|
|
19
|
+
id: raw.id,
|
|
20
|
+
stageNameId: raw.stage_name_id,
|
|
21
|
+
type: raw.type,
|
|
22
|
+
status: raw.status,
|
|
23
|
+
coverage: raw.coverage,
|
|
24
|
+
order: raw.order,
|
|
25
|
+
startDate: raw.start_date,
|
|
26
|
+
endDate: raw.end_date,
|
|
27
|
+
groups: raw.groups ? raw.groups.map(transformStandingGroup) : null
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
function transformSeason(raw) {
|
|
31
|
+
return {
|
|
32
|
+
id: raw.id,
|
|
33
|
+
name: raw.name,
|
|
34
|
+
year: raw.year,
|
|
35
|
+
status: raw.status,
|
|
36
|
+
startDate: raw.start_date,
|
|
37
|
+
endDate: raw.end_date,
|
|
38
|
+
stages: raw.stages.map(transformStage)
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
function transformSportsCompetition(raw) {
|
|
42
|
+
return {
|
|
43
|
+
id: raw.id,
|
|
44
|
+
sport: raw.sport,
|
|
45
|
+
meta: { entityIds: raw.meta?.entity_ids ?? [] },
|
|
46
|
+
season: transformSeason(raw.season)
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
export {
|
|
50
|
+
transformSportsCompetition
|
|
51
|
+
};
|
|
52
|
+
//# sourceMappingURL=transformer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transformer.js","sources":["../../../../../src/lib/api/fansunited/sports/competition/transformer.ts"],"sourcesContent":["/**\n * Transforms the raw Sports API competition response into the canonical\n * {@link FUSportsCompetitionDetails} shape.\n */\n\nimport type {\n FUSportsCompetitionDetails,\n FUSportsCompetitionSeason,\n FUSportsCompetitionStage,\n FUSportsCompetitionStandingEntry,\n FUSportsCompetitionStandingGroup,\n} from \"../../../../types/canonical/sports-competition.types\";\nimport type {\n RawSportsCompetitionResponse,\n RawSportsCompetitionSeason,\n RawSportsCompetitionStage,\n RawSportsCompetitionStandingEntry,\n RawSportsCompetitionStandingGroup,\n} from \"./types\";\n\nfunction transformStandingEntry(raw: RawSportsCompetitionStandingEntry): FUSportsCompetitionStandingEntry {\n return {\n rank: raw.rank,\n lastRank: raw.last_rank,\n competitorId: raw.competitor_id,\n stats: raw.stats.map((s) => ({ key: s.key, value: s.value })),\n rules: raw.rules,\n };\n}\n\nfunction transformStandingGroup(raw: RawSportsCompetitionStandingGroup): FUSportsCompetitionStandingGroup {\n return {\n id: raw.id,\n name: raw.name,\n standings: raw.standings.map(transformStandingEntry),\n };\n}\n\nfunction transformStage(raw: RawSportsCompetitionStage): FUSportsCompetitionStage {\n return {\n id: raw.id,\n stageNameId: raw.stage_name_id,\n type: raw.type,\n status: raw.status,\n coverage: raw.coverage,\n order: raw.order,\n startDate: raw.start_date,\n endDate: raw.end_date,\n groups: raw.groups ? raw.groups.map(transformStandingGroup) : null,\n };\n}\n\nfunction transformSeason(raw: RawSportsCompetitionSeason): FUSportsCompetitionSeason {\n return {\n id: raw.id,\n name: raw.name,\n year: raw.year,\n status: raw.status,\n startDate: raw.start_date,\n endDate: raw.end_date,\n stages: raw.stages.map(transformStage),\n };\n}\n\nexport function transformSportsCompetition(raw: RawSportsCompetitionResponse): FUSportsCompetitionDetails {\n return {\n id: raw.id,\n sport: raw.sport,\n meta: { entityIds: raw.meta?.entity_ids ?? [] },\n season: transformSeason(raw.season),\n };\n}\n"],"names":[],"mappings":"AAoBA,SAAS,uBAAuB,KAA0E;AACtG,SAAO;AAAA,IACH,MAAM,IAAI;AAAA,IACV,UAAU,IAAI;AAAA,IACd,cAAc,IAAI;AAAA,IAClB,OAAO,IAAI,MAAM,IAAI,CAAC,OAAO,EAAE,KAAK,EAAE,KAAK,OAAO,EAAE,QAAQ;AAAA,IAC5D,OAAO,IAAI;AAAA,EAAA;AAEnB;AAEA,SAAS,uBAAuB,KAA0E;AACtG,SAAO;AAAA,IACH,IAAI,IAAI;AAAA,IACR,MAAM,IAAI;AAAA,IACV,WAAW,IAAI,UAAU,IAAI,sBAAsB;AAAA,EAAA;AAE3D;AAEA,SAAS,eAAe,KAA0D;AAC9E,SAAO;AAAA,IACH,IAAI,IAAI;AAAA,IACR,aAAa,IAAI;AAAA,IACjB,MAAM,IAAI;AAAA,IACV,QAAQ,IAAI;AAAA,IACZ,UAAU,IAAI;AAAA,IACd,OAAO,IAAI;AAAA,IACX,WAAW,IAAI;AAAA,IACf,SAAS,IAAI;AAAA,IACb,QAAQ,IAAI,SAAS,IAAI,OAAO,IAAI,sBAAsB,IAAI;AAAA,EAAA;AAEtE;AAEA,SAAS,gBAAgB,KAA4D;AACjF,SAAO;AAAA,IACH,IAAI,IAAI;AAAA,IACR,MAAM,IAAI;AAAA,IACV,MAAM,IAAI;AAAA,IACV,QAAQ,IAAI;AAAA,IACZ,WAAW,IAAI;AAAA,IACf,SAAS,IAAI;AAAA,IACb,QAAQ,IAAI,OAAO,IAAI,cAAc;AAAA,EAAA;AAE7C;AAEO,SAAS,2BAA2B,KAA+D;AACtG,SAAO;AAAA,IACH,IAAI,IAAI;AAAA,IACR,OAAO,IAAI;AAAA,IACX,MAAM,EAAE,WAAW,IAAI,MAAM,cAAc,CAAA,EAAC;AAAA,IAC5C,QAAQ,gBAAgB,IAAI,MAAM;AAAA,EAAA;AAE1C;"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Raw API response and option types for the Fans United Sports API
|
|
3
|
+
* competition-by-id endpoint.
|
|
4
|
+
*/
|
|
5
|
+
import type { NextCacheOptions } from "../../http";
|
|
6
|
+
export interface RawSportsCompetitionStandingStat {
|
|
7
|
+
key: string;
|
|
8
|
+
value: string;
|
|
9
|
+
}
|
|
10
|
+
export interface RawSportsCompetitionStandingEntry {
|
|
11
|
+
rank: number;
|
|
12
|
+
last_rank?: number;
|
|
13
|
+
competitor_id: string;
|
|
14
|
+
stats: RawSportsCompetitionStandingStat[];
|
|
15
|
+
rules?: string[];
|
|
16
|
+
}
|
|
17
|
+
export interface RawSportsCompetitionStandingGroup {
|
|
18
|
+
id: number | null;
|
|
19
|
+
name: string | null;
|
|
20
|
+
standings: RawSportsCompetitionStandingEntry[];
|
|
21
|
+
}
|
|
22
|
+
export interface RawSportsCompetitionStage {
|
|
23
|
+
id: string;
|
|
24
|
+
stage_name_id: string;
|
|
25
|
+
type: "standing" | "bracket";
|
|
26
|
+
status: string;
|
|
27
|
+
coverage: string;
|
|
28
|
+
order: number;
|
|
29
|
+
start_date?: string;
|
|
30
|
+
end_date?: string;
|
|
31
|
+
groups: RawSportsCompetitionStandingGroup[] | null;
|
|
32
|
+
}
|
|
33
|
+
export interface RawSportsCompetitionSeason {
|
|
34
|
+
id: string;
|
|
35
|
+
name: string;
|
|
36
|
+
year?: string;
|
|
37
|
+
status: string;
|
|
38
|
+
start_date?: string;
|
|
39
|
+
end_date?: string;
|
|
40
|
+
stages: RawSportsCompetitionStage[];
|
|
41
|
+
}
|
|
42
|
+
export interface RawSportsCompetitionMeta {
|
|
43
|
+
entity_ids: string[];
|
|
44
|
+
}
|
|
45
|
+
export interface RawSportsCompetitionResponse {
|
|
46
|
+
id: string;
|
|
47
|
+
sport: string;
|
|
48
|
+
meta: RawSportsCompetitionMeta;
|
|
49
|
+
season: RawSportsCompetitionSeason;
|
|
50
|
+
}
|
|
51
|
+
export interface GetSportsCompetitionOptions {
|
|
52
|
+
/** Season identifier (e.g. "fb:c:1:2025/26"). Defaults to the active season. */
|
|
53
|
+
seasonId?: string;
|
|
54
|
+
/** Next.js cache options for ISR/on-demand revalidation. */
|
|
55
|
+
next?: NextCacheOptions;
|
|
56
|
+
}
|
|
57
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../../src/lib/api/fansunited/sports/competition/types.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAMnD,MAAM,WAAW,gCAAgC;IAC7C,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,iCAAiC;IAC9C,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;IACtB,KAAK,EAAE,gCAAgC,EAAE,CAAC;IAC1C,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;CACpB;AAED,MAAM,WAAW,iCAAiC;IAC9C,EAAE,EAAE,MAAM,GAAG,IAAI,CAAC;IAClB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,SAAS,EAAE,iCAAiC,EAAE,CAAC;CAClD;AAED,MAAM,WAAW,yBAAyB;IACtC,EAAE,EAAE,MAAM,CAAC;IACX,aAAa,EAAE,MAAM,CAAC;IACtB,IAAI,EAAE,UAAU,GAAG,SAAS,CAAC;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,iCAAiC,EAAE,GAAG,IAAI,CAAC;CACtD;AAED,MAAM,WAAW,0BAA0B;IACvC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,yBAAyB,EAAE,CAAC;CACvC;AAED,MAAM,WAAW,wBAAwB;IACrC,UAAU,EAAE,MAAM,EAAE,CAAC;CACxB;AAED,MAAM,WAAW,4BAA4B;IACzC,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,wBAAwB,CAAC;IAC/B,MAAM,EAAE,0BAA0B,CAAC;CACtC;AAMD,MAAM,WAAW,2BAA2B;IACxC,gFAAgF;IAChF,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,4DAA4D;IAC5D,IAAI,CAAC,EAAE,gBAAgB,CAAC;CAC3B"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../../../src/lib/api/fansunited/sports/constants.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,eAAO,MAAM,wBAAwB,qCAAqC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"constants.js","sources":["../../../../src/lib/api/fansunited/sports/constants.ts"],"sourcesContent":["/**\n * Constants for the Fans United Sports API\n */\n\nexport const FANSUNITED_SPORTS_DOMAIN = \"https://sports.fansunitedapi.com\";\n"],"names":[],"mappings":"AAIO,MAAM,2BAA2B;"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../../../../src/lib/api/fansunited/sports/http.ts"],"names":[],"mappings":"AAAA;;GAEG;AAKH;;GAEG;AACH,eAAO,MAAM,UAAU,wCAA6C,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http.js","sources":["../../../../src/lib/api/fansunited/sports/http.ts"],"sourcesContent":["/**\n * Sports HTTP client for the Fans United Sports API\n */\n\nimport { createHttpClient } from \"../http\";\nimport { FANSUNITED_SPORTS_DOMAIN } from \"./constants\";\n\n/**\n * HTTP client configured for the Fans United Sports API\n */\nexport const sportsHttp = createHttpClient(FANSUNITED_SPORTS_DOMAIN);\n"],"names":[],"mappings":";;AAUO,MAAM,aAAa,iBAAiB,wBAAwB;"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fans United Sports API
|
|
3
|
+
*
|
|
4
|
+
* Endpoints served from https://sports.fansunitedapi.com.
|
|
5
|
+
* All functions return canonical FUSports types.
|
|
6
|
+
*/
|
|
7
|
+
export { getFansUnitedSportsCompetition } from "./competition";
|
|
8
|
+
export type { GetSportsCompetitionOptions } from "./competition/types";
|
|
9
|
+
export { getFansUnitedSportsLivescore } from "./livescore";
|
|
10
|
+
export type { GetSportsLivescoreOptions } from "./livescore/types";
|
|
11
|
+
export { getFansUnitedSportsCompetitionHydrated } from "./competition/hydrated";
|
|
12
|
+
export type { FUSportsCompetitionHydrated, GetSportsCompetitionHydratedOptions } from "./competition/hydrated";
|
|
13
|
+
export { getFansUnitedSportsLivescoreHydrated } from "./livescore/hydrated";
|
|
14
|
+
export type { FUSportsLivescoreHydrated, GetSportsLivescoreHydratedOptions } from "./livescore/hydrated";
|
|
15
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/lib/api/fansunited/sports/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,8BAA8B,EAAE,MAAM,eAAe,CAAC;AAC/D,YAAY,EAAE,2BAA2B,EAAE,MAAM,qBAAqB,CAAC;AAEvE,OAAO,EAAE,4BAA4B,EAAE,MAAM,aAAa,CAAC;AAC3D,YAAY,EAAE,yBAAyB,EAAE,MAAM,mBAAmB,CAAC;AAEnE,OAAO,EAAE,sCAAsC,EAAE,MAAM,wBAAwB,CAAC;AAChF,YAAY,EAAE,2BAA2B,EAAE,mCAAmC,EAAE,MAAM,wBAAwB,CAAC;AAE/G,OAAO,EAAE,oCAAoC,EAAE,MAAM,sBAAsB,CAAC;AAC5E,YAAY,EAAE,yBAAyB,EAAE,iCAAiC,EAAE,MAAM,sBAAsB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hydrated.test.d.ts","sourceRoot":"","sources":["../../../../../../src/lib/api/fansunited/sports/livescore/__tests__/hydrated.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hydrated wrapper for the Fans United Sports API livescore endpoint.
|
|
3
|
+
*
|
|
4
|
+
* Returns the raw livescore payload merged with a lookup map of every
|
|
5
|
+
* referenced entity (competitions + competitors), resolved through the
|
|
6
|
+
* Search API.
|
|
7
|
+
*
|
|
8
|
+
* Cache topology — different from {@link getFansUnitedSportsCompetitionHydrated}:
|
|
9
|
+
* - livescore response → **not** cached. Live scores change minute-by-minute,
|
|
10
|
+
* and the design spec keeps live data on a client-side polling channel
|
|
11
|
+
* rather than the SWR cache. Each call hits the livescore API fresh.
|
|
12
|
+
* - search/entity responses → cached under "search" (24h stale / 7d max)
|
|
13
|
+
* by id only; the raw multi-locale payload serves every locale via
|
|
14
|
+
* transformEntity(raw, lang).
|
|
15
|
+
*
|
|
16
|
+
* The net effect: repeated calls to this function each make one livescore
|
|
17
|
+
* request, but reuse already-fetched entity payloads across calls and locales.
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* const board = await getFansUnitedSportsLivescoreHydrated({ sport: "FOOTBALL", locale: "BG" });
|
|
21
|
+
* for (const group of board.data) {
|
|
22
|
+
* const comp = board.entities.get(group.competitionId);
|
|
23
|
+
* console.log(`${comp?.name} — ${group.events.length} events`);
|
|
24
|
+
* for (const event of group.events) {
|
|
25
|
+
* const home = board.entities.get(event.competitorOne.id);
|
|
26
|
+
* const away = board.entities.get(event.competitorTwo.id);
|
|
27
|
+
* console.log(` ${home?.name} vs ${away?.name}`);
|
|
28
|
+
* }
|
|
29
|
+
* }
|
|
30
|
+
*/
|
|
31
|
+
import type { FUSportsLivescore } from "../../../../types/canonical/sports-livescore.types";
|
|
32
|
+
import type { DataLayerConfig } from "../../../../config";
|
|
33
|
+
import type { SearchEntityResult } from "../../search/types";
|
|
34
|
+
import type { GetSportsLivescoreOptions } from "./types";
|
|
35
|
+
export interface GetSportsLivescoreHydratedOptions extends GetSportsLivescoreOptions {
|
|
36
|
+
/** Locale code (e.g. "EN", "BG") used to resolve entity translations. Falls back to EN. */
|
|
37
|
+
locale?: string;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Livescore payload plus a lookup map for every referenced entity
|
|
41
|
+
* (competitions + competitors). Missing IDs are silently omitted — render
|
|
42
|
+
* code should fall back gracefully: `entities.get(id) ?? { name: id }`.
|
|
43
|
+
*/
|
|
44
|
+
export interface FUSportsLivescoreHydrated extends FUSportsLivescore {
|
|
45
|
+
entities: Map<string, SearchEntityResult>;
|
|
46
|
+
}
|
|
47
|
+
export declare function getFansUnitedSportsLivescoreHydrated(options?: GetSportsLivescoreHydratedOptions, config?: DataLayerConfig): Promise<FUSportsLivescoreHydrated>;
|
|
48
|
+
//# sourceMappingURL=hydrated.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hydrated.d.ts","sourceRoot":"","sources":["../../../../../src/lib/api/fansunited/sports/livescore/hydrated.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,oDAAoD,CAAC;AAC5F,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAC1D,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAG7D,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,SAAS,CAAC;AAEzD,MAAM,WAAW,iCAAkC,SAAQ,yBAAyB;IAChF,2FAA2F;IAC3F,MAAM,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;;;GAIG;AACH,MAAM,WAAW,yBAA0B,SAAQ,iBAAiB;IAChE,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAAC;CAC7C;AAiBD,wBAAsB,oCAAoC,CACtD,OAAO,CAAC,EAAE,iCAAiC,EAC3C,MAAM,CAAC,EAAE,eAAe,GACzB,OAAO,CAAC,yBAAyB,CAAC,CAmBpC"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { getFansUnitedSportsLivescore } from "./index.js";
|
|
2
|
+
import { getFansUnitedEntitiesByIds } from "../../search/index.js";
|
|
3
|
+
function collectEntityIds(livescore) {
|
|
4
|
+
const ids = /* @__PURE__ */ new Set();
|
|
5
|
+
for (const group of livescore.data) {
|
|
6
|
+
if (group.competitionId) ids.add(group.competitionId);
|
|
7
|
+
for (const event of group.events) {
|
|
8
|
+
if (event.competitorOne?.id) ids.add(event.competitorOne.id);
|
|
9
|
+
if (event.competitorTwo?.id) ids.add(event.competitorTwo.id);
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
return Array.from(ids);
|
|
13
|
+
}
|
|
14
|
+
async function getFansUnitedSportsLivescoreHydrated(options, config) {
|
|
15
|
+
const { locale, ...livescoreOptions } = options ?? {};
|
|
16
|
+
const raw = await getFansUnitedSportsLivescore(livescoreOptions, config);
|
|
17
|
+
const entityIds = collectEntityIds(raw);
|
|
18
|
+
const entities = /* @__PURE__ */ new Map();
|
|
19
|
+
if (entityIds.length > 0) {
|
|
20
|
+
const resolved = await getFansUnitedEntitiesByIds(entityIds, {
|
|
21
|
+
lang: locale,
|
|
22
|
+
next: livescoreOptions.next,
|
|
23
|
+
config
|
|
24
|
+
});
|
|
25
|
+
for (const e of resolved) {
|
|
26
|
+
entities.set(e.id, e);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return { ...raw, entities };
|
|
30
|
+
}
|
|
31
|
+
export {
|
|
32
|
+
getFansUnitedSportsLivescoreHydrated
|
|
33
|
+
};
|
|
34
|
+
//# sourceMappingURL=hydrated.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hydrated.js","sources":["../../../../../src/lib/api/fansunited/sports/livescore/hydrated.ts"],"sourcesContent":["/**\n * Hydrated wrapper for the Fans United Sports API livescore endpoint.\n *\n * Returns the raw livescore payload merged with a lookup map of every\n * referenced entity (competitions + competitors), resolved through the\n * Search API.\n *\n * Cache topology — different from {@link getFansUnitedSportsCompetitionHydrated}:\n * - livescore response → **not** cached. Live scores change minute-by-minute,\n * and the design spec keeps live data on a client-side polling channel\n * rather than the SWR cache. Each call hits the livescore API fresh.\n * - search/entity responses → cached under \"search\" (24h stale / 7d max)\n * by id only; the raw multi-locale payload serves every locale via\n * transformEntity(raw, lang).\n *\n * The net effect: repeated calls to this function each make one livescore\n * request, but reuse already-fetched entity payloads across calls and locales.\n *\n * @example\n * const board = await getFansUnitedSportsLivescoreHydrated({ sport: \"FOOTBALL\", locale: \"BG\" });\n * for (const group of board.data) {\n * const comp = board.entities.get(group.competitionId);\n * console.log(`${comp?.name} — ${group.events.length} events`);\n * for (const event of group.events) {\n * const home = board.entities.get(event.competitorOne.id);\n * const away = board.entities.get(event.competitorTwo.id);\n * console.log(` ${home?.name} vs ${away?.name}`);\n * }\n * }\n */\n\nimport type { FUSportsLivescore } from \"../../../../types/canonical/sports-livescore.types\";\nimport type { DataLayerConfig } from \"../../../../config\";\nimport type { SearchEntityResult } from \"../../search/types\";\nimport { getFansUnitedSportsLivescore } from \"./index\";\nimport { getFansUnitedEntitiesByIds } from \"../../search\";\nimport type { GetSportsLivescoreOptions } from \"./types\";\n\nexport interface GetSportsLivescoreHydratedOptions extends GetSportsLivescoreOptions {\n /** Locale code (e.g. \"EN\", \"BG\") used to resolve entity translations. Falls back to EN. */\n locale?: string;\n}\n\n/**\n * Livescore payload plus a lookup map for every referenced entity\n * (competitions + competitors). Missing IDs are silently omitted — render\n * code should fall back gracefully: `entities.get(id) ?? { name: id }`.\n */\nexport interface FUSportsLivescoreHydrated extends FUSportsLivescore {\n entities: Map<string, SearchEntityResult>;\n}\n\n/**\n * Collect every entity ID referenced in the livescore response, deduplicated.\n */\nfunction collectEntityIds(livescore: FUSportsLivescore): string[] {\n const ids = new Set<string>();\n for (const group of livescore.data) {\n if (group.competitionId) ids.add(group.competitionId);\n for (const event of group.events) {\n if (event.competitorOne?.id) ids.add(event.competitorOne.id);\n if (event.competitorTwo?.id) ids.add(event.competitorTwo.id);\n }\n }\n return Array.from(ids);\n}\n\nexport async function getFansUnitedSportsLivescoreHydrated(\n options?: GetSportsLivescoreHydratedOptions,\n config?: DataLayerConfig\n): Promise<FUSportsLivescoreHydrated> {\n const { locale, ...livescoreOptions } = options ?? {};\n const raw = await getFansUnitedSportsLivescore(livescoreOptions, config);\n\n const entityIds = collectEntityIds(raw);\n const entities = new Map<string, SearchEntityResult>();\n\n if (entityIds.length > 0) {\n const resolved = await getFansUnitedEntitiesByIds(entityIds, {\n lang: locale,\n next: livescoreOptions.next,\n config,\n });\n for (const e of resolved) {\n entities.set(e.id, e);\n }\n }\n\n return { ...raw, entities };\n}\n"],"names":[],"mappings":";;AAuDA,SAAS,iBAAiB,WAAwC;AAC9D,QAAM,0BAAU,IAAA;AAChB,aAAW,SAAS,UAAU,MAAM;AAChC,QAAI,MAAM,cAAe,KAAI,IAAI,MAAM,aAAa;AACpD,eAAW,SAAS,MAAM,QAAQ;AAC9B,UAAI,MAAM,eAAe,QAAQ,IAAI,MAAM,cAAc,EAAE;AAC3D,UAAI,MAAM,eAAe,QAAQ,IAAI,MAAM,cAAc,EAAE;AAAA,IAC/D;AAAA,EACJ;AACA,SAAO,MAAM,KAAK,GAAG;AACzB;AAEA,eAAsB,qCAClB,SACA,QACkC;AAClC,QAAM,EAAE,QAAQ,GAAG,iBAAA,IAAqB,WAAW,CAAA;AACnD,QAAM,MAAM,MAAM,6BAA6B,kBAAkB,MAAM;AAEvE,QAAM,YAAY,iBAAiB,GAAG;AACtC,QAAM,+BAAe,IAAA;AAErB,MAAI,UAAU,SAAS,GAAG;AACtB,UAAM,WAAW,MAAM,2BAA2B,WAAW;AAAA,MACzD,MAAM;AAAA,MACN,MAAM,iBAAiB;AAAA,MACvB;AAAA,IAAA,CACH;AACD,eAAW,KAAK,UAAU;AACtB,eAAS,IAAI,EAAE,IAAI,CAAC;AAAA,IACxB;AAAA,EACJ;AAEA,SAAO,EAAE,GAAG,KAAK,SAAA;AACrB;"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Livescore endpoint for the Fans United Sports API.
|
|
3
|
+
*/
|
|
4
|
+
import type { FUSportsLivescore } from "../../../../types/canonical/sports-livescore.types";
|
|
5
|
+
import { type DataLayerConfig } from "../../../../config";
|
|
6
|
+
import type { GetSportsLivescoreOptions } from "./types";
|
|
7
|
+
export type { GetSportsLivescoreOptions } from "./types";
|
|
8
|
+
/**
|
|
9
|
+
* Get live scores from the Fans United Sports API.
|
|
10
|
+
*
|
|
11
|
+
* Returns events for a given date grouped by competition, optionally filtered
|
|
12
|
+
* by sport, competitions, match IDs, and event status. Defaults to the current
|
|
13
|
+
* date (UTC) and all sports.
|
|
14
|
+
*
|
|
15
|
+
* @param options - Query filters and ordering (all optional)
|
|
16
|
+
* @param config - Optional data layer config (uses the singleton if omitted)
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* ```typescript
|
|
20
|
+
* const livescore = await getFansUnitedSportsLivescore({ sport: "FOOTBALL" });
|
|
21
|
+
* for (const group of livescore.data) {
|
|
22
|
+
* console.log(group.competitionId, group.events.length);
|
|
23
|
+
* }
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
export declare function getFansUnitedSportsLivescore(options?: GetSportsLivescoreOptions, config?: DataLayerConfig): Promise<FUSportsLivescore>;
|
|
27
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/lib/api/fansunited/sports/livescore/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,oDAAoD,CAAC;AAC5F,OAAO,EAAa,KAAK,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAGrE,OAAO,KAAK,EAAE,yBAAyB,EAA8B,MAAM,SAAS,CAAC;AAErF,YAAY,EAAE,yBAAyB,EAAE,MAAM,SAAS,CAAC;AAEzD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAsB,4BAA4B,CAC9C,OAAO,CAAC,EAAE,yBAAyB,EACnC,MAAM,CAAC,EAAE,eAAe,GACzB,OAAO,CAAC,iBAAiB,CAAC,CA+B5B"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { getConfig } from "../../../../config/index.js";
|
|
2
|
+
import { sportsHttp } from "../http.js";
|
|
3
|
+
import { transformSportsLivescore } from "./transformer.js";
|
|
4
|
+
async function getFansUnitedSportsLivescore(options, config) {
|
|
5
|
+
const finalConfig = config || getConfig();
|
|
6
|
+
const params = {};
|
|
7
|
+
if (options?.competitionIds?.length) {
|
|
8
|
+
params.competitions_ids = options.competitionIds.join(",");
|
|
9
|
+
}
|
|
10
|
+
if (options?.matchIds?.length) {
|
|
11
|
+
params.match_ids = options.matchIds.join(",");
|
|
12
|
+
}
|
|
13
|
+
if (options?.competitionsOrder?.length) {
|
|
14
|
+
params.competitions_order = options.competitionsOrder.join(",");
|
|
15
|
+
}
|
|
16
|
+
if (options?.date) {
|
|
17
|
+
params.date = options.date;
|
|
18
|
+
}
|
|
19
|
+
if (options?.eventStatus) {
|
|
20
|
+
params.event_status = options.eventStatus;
|
|
21
|
+
}
|
|
22
|
+
if (options?.sport) {
|
|
23
|
+
params.sport = options.sport;
|
|
24
|
+
}
|
|
25
|
+
const raw = await sportsHttp.get({
|
|
26
|
+
path: "/v1/livescore",
|
|
27
|
+
params,
|
|
28
|
+
next: options?.next,
|
|
29
|
+
config: finalConfig
|
|
30
|
+
});
|
|
31
|
+
return transformSportsLivescore(raw);
|
|
32
|
+
}
|
|
33
|
+
export {
|
|
34
|
+
getFansUnitedSportsLivescore
|
|
35
|
+
};
|
|
36
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../../../../../src/lib/api/fansunited/sports/livescore/index.ts"],"sourcesContent":["/**\n * Livescore endpoint for the Fans United Sports API.\n */\n\nimport type { FUSportsLivescore } from \"../../../../types/canonical/sports-livescore.types\";\nimport { getConfig, type DataLayerConfig } from \"../../../../config\";\nimport { sportsHttp } from \"../http\";\nimport { transformSportsLivescore } from \"./transformer\";\nimport type { GetSportsLivescoreOptions, RawSportsLivescoreResponse } from \"./types\";\n\nexport type { GetSportsLivescoreOptions } from \"./types\";\n\n/**\n * Get live scores from the Fans United Sports API.\n *\n * Returns events for a given date grouped by competition, optionally filtered\n * by sport, competitions, match IDs, and event status. Defaults to the current\n * date (UTC) and all sports.\n *\n * @param options - Query filters and ordering (all optional)\n * @param config - Optional data layer config (uses the singleton if omitted)\n *\n * @example\n * ```typescript\n * const livescore = await getFansUnitedSportsLivescore({ sport: \"FOOTBALL\" });\n * for (const group of livescore.data) {\n * console.log(group.competitionId, group.events.length);\n * }\n * ```\n */\nexport async function getFansUnitedSportsLivescore(\n options?: GetSportsLivescoreOptions,\n config?: DataLayerConfig\n): Promise<FUSportsLivescore> {\n const finalConfig = config || getConfig();\n\n const params: Record<string, string | undefined> = {};\n if (options?.competitionIds?.length) {\n params.competitions_ids = options.competitionIds.join(\",\");\n }\n if (options?.matchIds?.length) {\n params.match_ids = options.matchIds.join(\",\");\n }\n if (options?.competitionsOrder?.length) {\n params.competitions_order = options.competitionsOrder.join(\",\");\n }\n if (options?.date) {\n params.date = options.date;\n }\n if (options?.eventStatus) {\n params.event_status = options.eventStatus;\n }\n if (options?.sport) {\n params.sport = options.sport;\n }\n\n const raw = await sportsHttp.get<RawSportsLivescoreResponse>({\n path: \"/v1/livescore\",\n params,\n next: options?.next,\n config: finalConfig,\n });\n\n return transformSportsLivescore(raw);\n}\n"],"names":[],"mappings":";;;AA8BA,eAAsB,6BAClB,SACA,QAC0B;AAC1B,QAAM,cAAc,UAAU,UAAA;AAE9B,QAAM,SAA6C,CAAA;AACnD,MAAI,SAAS,gBAAgB,QAAQ;AACjC,WAAO,mBAAmB,QAAQ,eAAe,KAAK,GAAG;AAAA,EAC7D;AACA,MAAI,SAAS,UAAU,QAAQ;AAC3B,WAAO,YAAY,QAAQ,SAAS,KAAK,GAAG;AAAA,EAChD;AACA,MAAI,SAAS,mBAAmB,QAAQ;AACpC,WAAO,qBAAqB,QAAQ,kBAAkB,KAAK,GAAG;AAAA,EAClE;AACA,MAAI,SAAS,MAAM;AACf,WAAO,OAAO,QAAQ;AAAA,EAC1B;AACA,MAAI,SAAS,aAAa;AACtB,WAAO,eAAe,QAAQ;AAAA,EAClC;AACA,MAAI,SAAS,OAAO;AAChB,WAAO,QAAQ,QAAQ;AAAA,EAC3B;AAEA,QAAM,MAAM,MAAM,WAAW,IAAgC;AAAA,IACzD,MAAM;AAAA,IACN;AAAA,IACA,MAAM,SAAS;AAAA,IACf,QAAQ;AAAA,EAAA,CACX;AAED,SAAO,yBAAyB,GAAG;AACvC;"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Transforms the raw Sports API livescore response into the canonical
|
|
3
|
+
* {@link FUSportsLivescore} shape.
|
|
4
|
+
*/
|
|
5
|
+
import type { FUSportsLivescore } from "../../../../types/canonical/sports-livescore.types";
|
|
6
|
+
import type { RawSportsLivescoreResponse } from "./types";
|
|
7
|
+
export declare function transformSportsLivescore(raw: RawSportsLivescoreResponse): FUSportsLivescore;
|
|
8
|
+
//# sourceMappingURL=transformer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transformer.d.ts","sourceRoot":"","sources":["../../../../../src/lib/api/fansunited/sports/livescore/transformer.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EACR,iBAAiB,EAKpB,MAAM,oDAAoD,CAAC;AAC5D,OAAO,KAAK,EAGR,0BAA0B,EAG7B,MAAM,SAAS,CAAC;AAqCjB,wBAAgB,wBAAwB,CAAC,GAAG,EAAE,0BAA0B,GAAG,iBAAiB,CAU3F"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
function transformStatus(raw) {
|
|
2
|
+
return {
|
|
3
|
+
type: raw.type,
|
|
4
|
+
code: raw.code,
|
|
5
|
+
subType: raw.sub_type
|
|
6
|
+
};
|
|
7
|
+
}
|
|
8
|
+
function transformScore(raw) {
|
|
9
|
+
return {
|
|
10
|
+
key: raw.key,
|
|
11
|
+
competitorOne: raw.competitor_one,
|
|
12
|
+
competitorTwo: raw.competitor_two
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
function transformEvent(raw) {
|
|
16
|
+
return {
|
|
17
|
+
id: raw.id,
|
|
18
|
+
status: transformStatus(raw.status),
|
|
19
|
+
score: raw.score ? raw.score.map(transformScore) : void 0,
|
|
20
|
+
competitorOne: { id: raw.competitor_one.id, type: raw.competitor_one.type },
|
|
21
|
+
competitorTwo: { id: raw.competitor_two.id, type: raw.competitor_two.type },
|
|
22
|
+
startTime: raw.start_time
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
function transformGroup(raw) {
|
|
26
|
+
return {
|
|
27
|
+
competitionId: raw.competition_id,
|
|
28
|
+
sport: raw.sport,
|
|
29
|
+
events: raw.events.map(transformEvent)
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
function transformSportsLivescore(raw) {
|
|
33
|
+
return {
|
|
34
|
+
data: raw.data.map(transformGroup),
|
|
35
|
+
meta: {
|
|
36
|
+
totalCompetitions: raw.meta?.total_competitions ?? raw.data.length,
|
|
37
|
+
totalEvents: raw.meta?.total_events ?? raw.data.reduce((sum, group) => sum + group.events.length, 0)
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
export {
|
|
42
|
+
transformSportsLivescore
|
|
43
|
+
};
|
|
44
|
+
//# sourceMappingURL=transformer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transformer.js","sources":["../../../../../src/lib/api/fansunited/sports/livescore/transformer.ts"],"sourcesContent":["/**\n * Transforms the raw Sports API livescore response into the canonical\n * {@link FUSportsLivescore} shape.\n */\n\nimport type {\n FUSportsLivescore,\n FUSportsLivescoreCompetitionGroup,\n FUSportsLivescoreEvent,\n FUSportsLivescoreScore,\n FUSportsLivescoreStatus,\n} from \"../../../../types/canonical/sports-livescore.types\";\nimport type {\n RawSportsLivescoreCompetitionGroup,\n RawSportsLivescoreEvent,\n RawSportsLivescoreResponse,\n RawSportsLivescoreScore,\n RawSportsLivescoreStatus,\n} from \"./types\";\n\nfunction transformStatus(raw: RawSportsLivescoreStatus): FUSportsLivescoreStatus {\n return {\n type: raw.type,\n code: raw.code,\n subType: raw.sub_type,\n };\n}\n\nfunction transformScore(raw: RawSportsLivescoreScore): FUSportsLivescoreScore {\n return {\n key: raw.key,\n competitorOne: raw.competitor_one,\n competitorTwo: raw.competitor_two,\n };\n}\n\nfunction transformEvent(raw: RawSportsLivescoreEvent): FUSportsLivescoreEvent {\n return {\n id: raw.id,\n status: transformStatus(raw.status),\n score: raw.score ? raw.score.map(transformScore) : undefined,\n competitorOne: { id: raw.competitor_one.id, type: raw.competitor_one.type },\n competitorTwo: { id: raw.competitor_two.id, type: raw.competitor_two.type },\n startTime: raw.start_time,\n };\n}\n\nfunction transformGroup(raw: RawSportsLivescoreCompetitionGroup): FUSportsLivescoreCompetitionGroup {\n return {\n competitionId: raw.competition_id,\n sport: raw.sport,\n events: raw.events.map(transformEvent),\n };\n}\n\nexport function transformSportsLivescore(raw: RawSportsLivescoreResponse): FUSportsLivescore {\n return {\n data: raw.data.map(transformGroup),\n meta: {\n totalCompetitions: raw.meta?.total_competitions ?? raw.data.length,\n totalEvents:\n raw.meta?.total_events ??\n raw.data.reduce((sum, group) => sum + group.events.length, 0),\n },\n };\n}\n"],"names":[],"mappings":"AAoBA,SAAS,gBAAgB,KAAwD;AAC7E,SAAO;AAAA,IACH,MAAM,IAAI;AAAA,IACV,MAAM,IAAI;AAAA,IACV,SAAS,IAAI;AAAA,EAAA;AAErB;AAEA,SAAS,eAAe,KAAsD;AAC1E,SAAO;AAAA,IACH,KAAK,IAAI;AAAA,IACT,eAAe,IAAI;AAAA,IACnB,eAAe,IAAI;AAAA,EAAA;AAE3B;AAEA,SAAS,eAAe,KAAsD;AAC1E,SAAO;AAAA,IACH,IAAI,IAAI;AAAA,IACR,QAAQ,gBAAgB,IAAI,MAAM;AAAA,IAClC,OAAO,IAAI,QAAQ,IAAI,MAAM,IAAI,cAAc,IAAI;AAAA,IACnD,eAAe,EAAE,IAAI,IAAI,eAAe,IAAI,MAAM,IAAI,eAAe,KAAA;AAAA,IACrE,eAAe,EAAE,IAAI,IAAI,eAAe,IAAI,MAAM,IAAI,eAAe,KAAA;AAAA,IACrE,WAAW,IAAI;AAAA,EAAA;AAEvB;AAEA,SAAS,eAAe,KAA4E;AAChG,SAAO;AAAA,IACH,eAAe,IAAI;AAAA,IACnB,OAAO,IAAI;AAAA,IACX,QAAQ,IAAI,OAAO,IAAI,cAAc;AAAA,EAAA;AAE7C;AAEO,SAAS,yBAAyB,KAAoD;AACzF,SAAO;AAAA,IACH,MAAM,IAAI,KAAK,IAAI,cAAc;AAAA,IACjC,MAAM;AAAA,MACF,mBAAmB,IAAI,MAAM,sBAAsB,IAAI,KAAK;AAAA,MAC5D,aACI,IAAI,MAAM,gBACV,IAAI,KAAK,OAAO,CAAC,KAAK,UAAU,MAAM,MAAM,OAAO,QAAQ,CAAC;AAAA,IAAA;AAAA,EACpE;AAER;"}
|