fansunited-data-layer 0.14.1 → 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.
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../../../src/lib/api/fansunited/http.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAa,KAAK,eAAe,EAAE,MAAM,cAAc,CAAC;AAE/D;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC7B,UAAU,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC;IAC5B,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;CACnB;AAED,MAAM,WAAW,cAAc;IAC3B,0CAA0C;IAC1C,IAAI,EAAE,MAAM,CAAC;IACb,uBAAuB;IACvB,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,CAAC,CAAC;IACvD,+CAA+C;IAC/C,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,2DAA2D;IAC3D,IAAI,CAAC,EAAE,gBAAgB,CAAC;IACxB,kEAAkE;IAClE,MAAM,CAAC,EAAE,eAAe,CAAC;CAC5B;AAED,MAAM,WAAW,oBAAoB;IACjC;;;;;;;;;;OAUG;IACH,GAAG,CAAC,CAAC,EAAE,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;CAC/C;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,oBAAoB,
|
|
1
|
+
{"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../../../src/lib/api/fansunited/http.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAa,KAAK,eAAe,EAAE,MAAM,cAAc,CAAC;AAE/D;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC7B,UAAU,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC;IAC5B,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;CACnB;AAED,MAAM,WAAW,cAAc;IAC3B,0CAA0C;IAC1C,IAAI,EAAE,MAAM,CAAC;IACb,uBAAuB;IACvB,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,CAAC,CAAC;IACvD,+CAA+C;IAC/C,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,2DAA2D;IAC3D,IAAI,CAAC,EAAE,gBAAgB,CAAC;IACxB,kEAAkE;IAClE,MAAM,CAAC,EAAE,eAAe,CAAC;CAC5B;AAED,MAAM,WAAW,oBAAoB;IACjC;;;;;;;;;;OAUG;IACH,GAAG,CAAC,CAAC,EAAE,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;CAC/C;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,oBAAoB,CAkFrE"}
|
package/api/fansunited/http.js
CHANGED
|
@@ -32,10 +32,6 @@ function createHttpClient(domain) {
|
|
|
32
32
|
}
|
|
33
33
|
}
|
|
34
34
|
const timeout = 3e4;
|
|
35
|
-
const debugUrl = new URL(url.toString());
|
|
36
|
-
debugUrl.searchParams.delete("key");
|
|
37
|
-
debugUrl.searchParams.delete("client_id");
|
|
38
|
-
console.log("🔗 FansUnited API URL:", debugUrl.toString());
|
|
39
35
|
const response = await fetch(url.toString(), {
|
|
40
36
|
method: "GET",
|
|
41
37
|
headers: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"http.js","sources":["../../../src/lib/api/fansunited/http.ts"],"sourcesContent":["/**\n * HTTP client for Fans United APIs\n */\n\nimport { getConfig, type DataLayerConfig } from \"../../config\";\n\n/**\n * Next.js cache options for ISR/on-demand revalidation\n */\nexport interface NextCacheOptions {\n revalidate?: number | false;\n tags?: string[];\n}\n\nexport interface RequestOptions {\n /** API path (e.g., '/v1/competitions') */\n path: string;\n /** Query parameters */\n params?: Record<string, string | string[] | undefined>;\n /** Custom headers to include in the request */\n headers?: Record<string, string>;\n /** Next.js cache options for ISR/on-demand revalidation */\n next?: NextCacheOptions;\n /** Optional data layer config (uses singleton if not provided) */\n config?: DataLayerConfig;\n}\n\nexport interface FansUnitedHttpClient {\n /**\n * Make a GET request to a Fans United API\n *\n * Handles:\n * - API key and client ID authentication via query params\n * - Timeout\n * - Error handling\n *\n * @param options - Request options\n * @returns Parsed JSON response\n */\n get<T>(options: RequestOptions): Promise<T>;\n}\n\n/**\n * Create an HTTP client for a specific Fans United API domain\n *\n * @param domain - The base URL for the API (e.g., 'https://football.fansunitedapi.com')\n * @returns HTTP client configured for the specified domain\n *\n * @example\n * ```typescript\n * import { createHttpClient } from '../http';\n * import { FANSUNITED_FOOTBALL_DOMAIN } from '../constants';\n *\n * const footballHttp = createHttpClient(FANSUNITED_FOOTBALL_DOMAIN);\n * const data = await footballHttp.get({ path: '/v1/competitions' });\n * ```\n */\nexport function createHttpClient(domain: string): FansUnitedHttpClient {\n const baseUrl = domain.replace(/\\/$/, \"\");\n\n function buildUrl(path: string): string {\n return `${baseUrl}${path}`;\n }\n\n return {\n async get<T>(options: RequestOptions): Promise<T> {\n const finalConfig = options.config || getConfig();\n const { fansUnited } = finalConfig;\n\n if (!fansUnited) {\n throw new Error(\n \"Fans United configuration is missing. \" +\n \"Add 'fansUnited' with 'apiKey' and 'clientId' to your config.\"\n );\n }\n\n if (!fansUnited.apiKey || !fansUnited.clientId) {\n throw new Error(\"Fans United configuration requires both 'apiKey' and 'clientId'\");\n }\n\n const url = new URL(buildUrl(options.path));\n\n // Add authentication query parameters\n url.searchParams.set(\"key\", fansUnited.apiKey);\n url.searchParams.set(\"client_id\", fansUnited.clientId);\n\n // Add additional query parameters\n if (options.params) {\n for (const [key, value] of Object.entries(options.params)) {\n if (value === undefined) continue;\n\n if (Array.isArray(value)) {\n if (value.length > 0) {\n url.searchParams.set(key, value.join(\",\"));\n }\n } else {\n url.searchParams.set(key, value);\n }\n }\n }\n\n // Default timeout: 30 seconds\n const timeout = 30000;\n\n
|
|
1
|
+
{"version":3,"file":"http.js","sources":["../../../src/lib/api/fansunited/http.ts"],"sourcesContent":["/**\n * HTTP client for Fans United APIs\n */\n\nimport { getConfig, type DataLayerConfig } from \"../../config\";\n\n/**\n * Next.js cache options for ISR/on-demand revalidation\n */\nexport interface NextCacheOptions {\n revalidate?: number | false;\n tags?: string[];\n}\n\nexport interface RequestOptions {\n /** API path (e.g., '/v1/competitions') */\n path: string;\n /** Query parameters */\n params?: Record<string, string | string[] | undefined>;\n /** Custom headers to include in the request */\n headers?: Record<string, string>;\n /** Next.js cache options for ISR/on-demand revalidation */\n next?: NextCacheOptions;\n /** Optional data layer config (uses singleton if not provided) */\n config?: DataLayerConfig;\n}\n\nexport interface FansUnitedHttpClient {\n /**\n * Make a GET request to a Fans United API\n *\n * Handles:\n * - API key and client ID authentication via query params\n * - Timeout\n * - Error handling\n *\n * @param options - Request options\n * @returns Parsed JSON response\n */\n get<T>(options: RequestOptions): Promise<T>;\n}\n\n/**\n * Create an HTTP client for a specific Fans United API domain\n *\n * @param domain - The base URL for the API (e.g., 'https://football.fansunitedapi.com')\n * @returns HTTP client configured for the specified domain\n *\n * @example\n * ```typescript\n * import { createHttpClient } from '../http';\n * import { FANSUNITED_FOOTBALL_DOMAIN } from '../constants';\n *\n * const footballHttp = createHttpClient(FANSUNITED_FOOTBALL_DOMAIN);\n * const data = await footballHttp.get({ path: '/v1/competitions' });\n * ```\n */\nexport function createHttpClient(domain: string): FansUnitedHttpClient {\n const baseUrl = domain.replace(/\\/$/, \"\");\n\n function buildUrl(path: string): string {\n return `${baseUrl}${path}`;\n }\n\n return {\n async get<T>(options: RequestOptions): Promise<T> {\n const finalConfig = options.config || getConfig();\n const { fansUnited } = finalConfig;\n\n if (!fansUnited) {\n throw new Error(\n \"Fans United configuration is missing. \" +\n \"Add 'fansUnited' with 'apiKey' and 'clientId' to your config.\"\n );\n }\n\n if (!fansUnited.apiKey || !fansUnited.clientId) {\n throw new Error(\"Fans United configuration requires both 'apiKey' and 'clientId'\");\n }\n\n const url = new URL(buildUrl(options.path));\n\n // Add authentication query parameters\n url.searchParams.set(\"key\", fansUnited.apiKey);\n url.searchParams.set(\"client_id\", fansUnited.clientId);\n\n // Add additional query parameters\n if (options.params) {\n for (const [key, value] of Object.entries(options.params)) {\n if (value === undefined) continue;\n\n if (Array.isArray(value)) {\n if (value.length > 0) {\n url.searchParams.set(key, value.join(\",\"));\n }\n } else {\n url.searchParams.set(key, value);\n }\n }\n }\n\n // Default timeout: 30 seconds\n const timeout = 30000;\n\n const response = await fetch(url.toString(), {\n method: \"GET\",\n headers: {\n Accept: \"application/json\",\n \"Content-Type\": \"application/json\",\n // Merge custom headers (they can override defaults)\n ...options.headers,\n },\n signal: AbortSignal.timeout(timeout),\n // Next.js cache options for ISR/on-demand revalidation\n ...(options.next && { next: options.next }),\n });\n\n if (!response.ok) {\n // Try to get error details from response body\n let errorMessage = `Fans United API error: ${response.status} ${response.statusText}`;\n try {\n const errorBody = await response.text();\n if (errorBody) {\n errorMessage += ` - ${errorBody}`;\n }\n } catch (error) {\n // Log if we can't read the error body, but continue\n console.error(\"[Fans United HTTP] Failed to read error response body:\", {\n error: error instanceof Error ? error.message : String(error),\n status: response.status,\n statusText: response.statusText,\n });\n }\n throw new Error(errorMessage);\n }\n\n return response.json();\n },\n };\n}\n"],"names":[],"mappings":";AAyDO,SAAS,iBAAiB,QAAsC;AACnE,QAAM,UAAU,OAAO,QAAQ,OAAO,EAAE;AAExC,WAAS,SAAS,MAAsB;AACpC,WAAO,GAAG,OAAO,GAAG,IAAI;AAAA,EAC5B;AAEA,SAAO;AAAA,IACH,MAAM,IAAO,SAAqC;AAC9C,YAAM,cAAc,QAAQ,UAAU,UAAA;AACtC,YAAM,EAAE,eAAe;AAEvB,UAAI,CAAC,YAAY;AACb,cAAM,IAAI;AAAA,UACN;AAAA,QAAA;AAAA,MAGR;AAEA,UAAI,CAAC,WAAW,UAAU,CAAC,WAAW,UAAU;AAC5C,cAAM,IAAI,MAAM,iEAAiE;AAAA,MACrF;AAEA,YAAM,MAAM,IAAI,IAAI,SAAS,QAAQ,IAAI,CAAC;AAG1C,UAAI,aAAa,IAAI,OAAO,WAAW,MAAM;AAC7C,UAAI,aAAa,IAAI,aAAa,WAAW,QAAQ;AAGrD,UAAI,QAAQ,QAAQ;AAChB,mBAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,QAAQ,MAAM,GAAG;AACvD,cAAI,UAAU,OAAW;AAEzB,cAAI,MAAM,QAAQ,KAAK,GAAG;AACtB,gBAAI,MAAM,SAAS,GAAG;AAClB,kBAAI,aAAa,IAAI,KAAK,MAAM,KAAK,GAAG,CAAC;AAAA,YAC7C;AAAA,UACJ,OAAO;AACH,gBAAI,aAAa,IAAI,KAAK,KAAK;AAAA,UACnC;AAAA,QACJ;AAAA,MACJ;AAGA,YAAM,UAAU;AAEhB,YAAM,WAAW,MAAM,MAAM,IAAI,YAAY;AAAA,QACzC,QAAQ;AAAA,QACR,SAAS;AAAA,UACL,QAAQ;AAAA,UACR,gBAAgB;AAAA;AAAA,UAEhB,GAAG,QAAQ;AAAA,QAAA;AAAA,QAEf,QAAQ,YAAY,QAAQ,OAAO;AAAA;AAAA,QAEnC,GAAI,QAAQ,QAAQ,EAAE,MAAM,QAAQ,KAAA;AAAA,MAAK,CAC5C;AAED,UAAI,CAAC,SAAS,IAAI;AAEd,YAAI,eAAe,0BAA0B,SAAS,MAAM,IAAI,SAAS,UAAU;AACnF,YAAI;AACA,gBAAM,YAAY,MAAM,SAAS,KAAA;AACjC,cAAI,WAAW;AACX,4BAAgB,MAAM,SAAS;AAAA,UACnC;AAAA,QACJ,SAAS,OAAO;AAEZ,kBAAQ,MAAM,0DAA0D;AAAA,YACpE,OAAO,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AAAA,YAC5D,QAAQ,SAAS;AAAA,YACjB,YAAY,SAAS;AAAA,UAAA,CACxB;AAAA,QACL;AACA,cAAM,IAAI,MAAM,YAAY;AAAA,MAChC;AAEA,aAAO,SAAS,KAAA;AAAA,IACpB;AAAA,EAAA;AAER;"}
|
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
function getMarketTypeEnum(marketType) {
|
|
2
2
|
const marketTypeMap = {
|
|
3
|
-
"1x2": "
|
|
4
|
-
"12": "
|
|
5
|
-
OVER_UNDER: "
|
|
3
|
+
"1x2": "FT_1X2",
|
|
4
|
+
"12": "FT_1X2",
|
|
5
|
+
OVER_UNDER: "OVER_GOALS_2_5",
|
|
6
6
|
DOUBLE_CHANCE: "DOUBLE_CHANCE",
|
|
7
|
-
BOTH_TO_SCORE: "
|
|
7
|
+
BOTH_TO_SCORE: "BOTH_TEAMS_SCORE",
|
|
8
8
|
DRAW_NO_BET: "DRAW_NO_BET",
|
|
9
9
|
FIRST_TEAM_TO_SCORE: "FIRST_TEAM_TO_SCORE",
|
|
10
10
|
CORRECT_SCORE: "CORRECT_SCORE",
|
|
11
11
|
FIRST_HALF_GOALS: "FIRST_HALF_GOALS",
|
|
12
|
-
FIRST_PLAYER_TO_SCORE: "
|
|
13
|
-
PLAYER_TO_SCORE_DURING_GAME: "
|
|
14
|
-
PLAYER_TO_RECEIVE_CARD: "
|
|
15
|
-
FIRST_HALF_AND_FINAL_RESULT: "
|
|
12
|
+
FIRST_PLAYER_TO_SCORE: "PLAYER_SCORE_FIRST_GOAL",
|
|
13
|
+
PLAYER_TO_SCORE_DURING_GAME: "PLAYER_SCORE",
|
|
14
|
+
PLAYER_TO_RECEIVE_CARD: "PLAYER_YELLOW_CARD",
|
|
15
|
+
FIRST_HALF_AND_FINAL_RESULT: "HT_FT"
|
|
16
16
|
};
|
|
17
17
|
return marketTypeMap[marketType] || marketType.toUpperCase();
|
|
18
18
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"matches.js","sources":["../../../../src/lib/api/fansunited-sdk/odds/matches.ts"],"sourcesContent":["/**\n * SDK-based odds operations for matches\n *\n * These functions use the FansUnited SDK's odds namespace to fetch betting odds\n * for matches and transform them to canonical types.\n */\n\nimport type FansUnitedSDKModel from \"fansunited-sdk-esm/Core/Global/Models/FansUnitedSDKModel\";\nimport type { FUSportsBatchMatchOdds, FUSportsBettingOperatorOdds } from \"../../../types/canonical\";\nimport type { MarketType } from \"../../sportal365-sports/football/matches/types\";\n\n/**\n * Options for fetching batch match odds\n */\nexport interface GetBatchMatchOddsOptions {\n /** Market types to include (e.g., ['1x2', 'OVER_UNDER']) */\n marketTypes?: MarketType[];\n /** Filter odds by specific bookmaker/operator IDs */\n bookmakerIds?: string[];\n /** Type of odds: PRE_EVENT, LIVE, or ALL (default: ALL) */\n oddType?: \"PRE_EVENT\" | \"LIVE\" | \"ALL\";\n /** Scope type for odds (default: ORDINARY_TIME) */\n scopeType?: \"ORDINARY_TIME\" | \"FULL_TIME\";\n /** Format of odds (default: DECIMAL) */\n oddFormat?: \"FRACTIONAL\" | \"DECIMAL\" | \"MONEYLINE\";\n}\n\n/**\n * Map SDK market type string to SDK enum\n */\nfunction getMarketTypeEnum(marketType: MarketType): string {\n // Map our canonical market types to SDK market type strings\n const marketTypeMap: Record<MarketType, string> = {\n \"1x2\": \"1X2\",\n \"12\": \"12\",\n OVER_UNDER: \"OVER_UNDER\",\n DOUBLE_CHANCE: \"DOUBLE_CHANCE\",\n BOTH_TO_SCORE: \"BOTH_TO_SCORE\",\n DRAW_NO_BET: \"DRAW_NO_BET\",\n FIRST_TEAM_TO_SCORE: \"FIRST_TEAM_TO_SCORE\",\n CORRECT_SCORE: \"CORRECT_SCORE\",\n FIRST_HALF_GOALS: \"FIRST_HALF_GOALS\",\n FIRST_PLAYER_TO_SCORE: \"FIRST_PLAYER_TO_SCORE\",\n PLAYER_TO_SCORE_DURING_GAME: \"PLAYER_TO_SCORE_DURING_GAME\",\n PLAYER_TO_RECEIVE_CARD: \"PLAYER_TO_RECEIVE_CARD\",\n FIRST_HALF_AND_FINAL_RESULT: \"FIRST_HALF_AND_FINAL_RESULT\",\n };\n\n return marketTypeMap[marketType] || marketType.toUpperCase();\n}\n\n/**\n * Transform SDK odds response to canonical format\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nfunction transformSDKOdds(sdkOddsMap: Map<string, any[]>, bookmakerIds?: string[]): FUSportsBatchMatchOdds {\n const result: FUSportsBatchMatchOdds = new Map();\n\n sdkOddsMap.forEach((matchOdds, matchId) => {\n if (!matchOdds || matchOdds.length === 0) {\n return;\n }\n\n // Filter by bookmaker IDs if provided\n const filteredOdds = bookmakerIds\n ? matchOdds.filter((odd) => odd.bookmaker && bookmakerIds.includes(odd.bookmaker.id))\n : matchOdds;\n\n // Transform to canonical format\n const operators: FUSportsBettingOperatorOdds[] = filteredOdds\n .filter((odd) => odd.bookmaker && odd.markets && odd.markets.length > 0)\n .map((odd) => {\n const bookmaker = odd.bookmaker;\n const firstAsset = bookmaker.assets?.[0];\n\n // Get background color from branding or assets\n const backgroundColor = bookmaker.branding?.backgroundColor || firstAsset?.backgroundColor;\n\n // Get text color from branding, or calculate based on background\n const textColor =\n bookmaker.branding?.textColor || (isDarkColor(backgroundColor) ? \"#FFFFFF\" : \"#000000\");\n\n // Prefer transparent background URL, fallback to regular logo\n const logo = firstAsset?.transparentBackgroundUrl || firstAsset?.logo;\n\n // Get operator URL: prefer eventUrls, fallback to links homepage\n /* eslint-disable @typescript-eslint/no-explicit-any */\n const operatorUrl =\n bookmaker.eventUrls?.find((u: any) => u.appType === \"desktop\")?.url ||\n bookmaker.links?.find((l: any) => l.appType === \"desktop\")?.homepageUrl ||\n bookmaker.url ||\n \"\";\n /* eslint-enable @typescript-eslint/no-explicit-any */\n\n return {\n operator: {\n id: bookmaker.id,\n name: bookmaker.name,\n url: operatorUrl,\n branding: {\n backgroundColor,\n textColor,\n logo,\n },\n },\n type: odd.type,\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n markets: odd.markets.map((market: any) => ({\n type: {\n id: market.type.id,\n code: market.type.code,\n name: market.type.name,\n },\n period: {\n id: market.scope.id,\n type: market.scope.type,\n name: market.scope.name,\n },\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n selections: market.selections.map((selection: any) => {\n // Get the desktop URL from selection.urls array\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const selectionUrl = selection.urls?.find((u: any) => u.appType === \"desktop\")?.url;\n\n // Map movement to canonical format\n let movement: \"UP\" | \"DOWN\" | \"NONE\" = \"NONE\";\n if (selection.movement === \"UP\") movement = \"UP\";\n else if (selection.movement === \"DOWN\") movement = \"DOWN\";\n\n return {\n id: selection.id,\n name: selection.name,\n code: selection.code,\n value: selection.value,\n odds: selection.odds,\n oddsOld: selection.oddsOld,\n movement,\n url: selectionUrl,\n providerSelectionId: selection.providerInfo?.selectionId,\n };\n }),\n })),\n };\n });\n\n result.set(matchId, operators);\n });\n\n return result;\n}\n\n/**\n * Determine if a color is dark based on luminance\n */\nfunction isDarkColor(hexColor?: string): boolean {\n if (!hexColor) return true;\n\n const hex = hexColor.replace(\"#\", \"\");\n const r = parseInt(hex.substring(0, 2), 16);\n const g = parseInt(hex.substring(2, 4), 16);\n const b = parseInt(hex.substring(4, 6), 16);\n\n // Calculate relative luminance (ITU-R BT.709)\n const luminance = (0.2126 * r + 0.7152 * g + 0.0722 * b) / 255;\n\n return luminance < 0.5;\n}\n\n/**\n * Get betting odds for multiple matches in a single API call\n *\n * Fetches odds for multiple matches using the SDK's batch odds endpoint.\n * This is more efficient than making individual calls for each match.\n *\n * @param sdk - The FansUnited SDK instance\n * @param matchIds - Array of match IDs to fetch odds for\n * @param options - Optional parameters for filtering odds\n * @returns Map of match IDs to their betting odds in canonical format\n *\n * @example\n * ```typescript\n * import { useFansUnitedSDK } from 'fansunited-data-layer/client';\n * import { getBatchMatchOdds } from 'fansunited-data-layer/client';\n *\n * function MyComponent() {\n * const sdk = useFansUnitedSDK();\n *\n * const fetchOdds = async () => {\n * if (!sdk) return;\n * const oddsMap = await getBatchMatchOdds(sdk, ['match1', 'match2', 'match3'], {\n * marketTypes: ['1x2', 'OVER_UNDER'],\n * oddType: 'PRE_EVENT'\n * });\n * console.log(oddsMap); // Map<matchId, operators[]>\n * };\n * }\n * ```\n */\nexport async function getBatchMatchOdds(\n sdk: FansUnitedSDKModel,\n matchIds: string[],\n options?: GetBatchMatchOddsOptions\n): Promise<FUSportsBatchMatchOdds> {\n if (!matchIds || matchIds.length === 0) {\n return new Map();\n }\n\n // Build filters for SDK call\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const filters: any = {\n oddType: options?.oddType || \"ALL\",\n scopeType: options?.scopeType || \"ORDINARY_TIME\",\n oddFormat: options?.oddFormat || \"DECIMAL\",\n };\n\n // Add market types if provided\n if (options?.marketTypes && options.marketTypes.length > 0) {\n filters.marketTypes = options.marketTypes.map((market) => getMarketTypeEnum(market));\n }\n\n // Call SDK odds.getByMatchIds()\n const sdkResponse = await sdk.odds.getByMatchIds(matchIds, filters);\n\n // Transform to canonical format\n return transformSDKOdds(sdkResponse, options?.bookmakerIds);\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"matches.js","sources":["../../../../src/lib/api/fansunited-sdk/odds/matches.ts"],"sourcesContent":["/**\n * SDK-based odds operations for matches\n *\n * These functions use the FansUnited SDK's odds namespace to fetch betting odds\n * for matches and transform them to canonical types.\n */\n\nimport type FansUnitedSDKModel from \"fansunited-sdk-esm/Core/Global/Models/FansUnitedSDKModel\";\nimport type { FUSportsBatchMatchOdds, FUSportsBettingOperatorOdds } from \"../../../types/canonical\";\nimport type { MarketType } from \"../../sportal365-sports/football/matches/types\";\n\n/**\n * Options for fetching batch match odds\n */\nexport interface GetBatchMatchOddsOptions {\n /** Market types to include (e.g., ['1x2', 'OVER_UNDER']) */\n marketTypes?: MarketType[];\n /** Filter odds by specific bookmaker/operator IDs */\n bookmakerIds?: string[];\n /** Type of odds: PRE_EVENT, LIVE, or ALL (default: ALL) */\n oddType?: \"PRE_EVENT\" | \"LIVE\" | \"ALL\";\n /** Scope type for odds (default: ORDINARY_TIME) */\n scopeType?: \"ORDINARY_TIME\" | \"FULL_TIME\";\n /** Format of odds (default: DECIMAL) */\n oddFormat?: \"FRACTIONAL\" | \"DECIMAL\" | \"MONEYLINE\";\n}\n\n/**\n * Map SDK market type string to SDK enum\n */\nfunction getMarketTypeEnum(marketType: MarketType): string {\n // Map our canonical market types to SDK MatchOddsMarketEnum values\n const marketTypeMap: Record<MarketType, string> = {\n \"1x2\": \"FT_1X2\",\n \"12\": \"FT_1X2\",\n OVER_UNDER: \"OVER_GOALS_2_5\",\n DOUBLE_CHANCE: \"DOUBLE_CHANCE\",\n BOTH_TO_SCORE: \"BOTH_TEAMS_SCORE\",\n DRAW_NO_BET: \"DRAW_NO_BET\",\n FIRST_TEAM_TO_SCORE: \"FIRST_TEAM_TO_SCORE\",\n CORRECT_SCORE: \"CORRECT_SCORE\",\n FIRST_HALF_GOALS: \"FIRST_HALF_GOALS\",\n FIRST_PLAYER_TO_SCORE: \"PLAYER_SCORE_FIRST_GOAL\",\n PLAYER_TO_SCORE_DURING_GAME: \"PLAYER_SCORE\",\n PLAYER_TO_RECEIVE_CARD: \"PLAYER_YELLOW_CARD\",\n FIRST_HALF_AND_FINAL_RESULT: \"HT_FT\",\n };\n\n return marketTypeMap[marketType] || marketType.toUpperCase();\n}\n\n/**\n * Transform SDK odds response to canonical format\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nfunction transformSDKOdds(sdkOddsMap: Map<string, any[]>, bookmakerIds?: string[]): FUSportsBatchMatchOdds {\n const result: FUSportsBatchMatchOdds = new Map();\n\n sdkOddsMap.forEach((matchOdds, matchId) => {\n if (!matchOdds || matchOdds.length === 0) {\n return;\n }\n\n // Filter by bookmaker IDs if provided\n const filteredOdds = bookmakerIds\n ? matchOdds.filter((odd) => odd.bookmaker && bookmakerIds.includes(odd.bookmaker.id))\n : matchOdds;\n\n // Transform to canonical format\n const operators: FUSportsBettingOperatorOdds[] = filteredOdds\n .filter((odd) => odd.bookmaker && odd.markets && odd.markets.length > 0)\n .map((odd) => {\n const bookmaker = odd.bookmaker;\n const firstAsset = bookmaker.assets?.[0];\n\n // Get background color from branding or assets\n const backgroundColor = bookmaker.branding?.backgroundColor || firstAsset?.backgroundColor;\n\n // Get text color from branding, or calculate based on background\n const textColor =\n bookmaker.branding?.textColor || (isDarkColor(backgroundColor) ? \"#FFFFFF\" : \"#000000\");\n\n // Prefer transparent background URL, fallback to regular logo\n const logo = firstAsset?.transparentBackgroundUrl || firstAsset?.logo;\n\n // Get operator URL: prefer eventUrls, fallback to links homepage\n /* eslint-disable @typescript-eslint/no-explicit-any */\n const operatorUrl =\n bookmaker.eventUrls?.find((u: any) => u.appType === \"desktop\")?.url ||\n bookmaker.links?.find((l: any) => l.appType === \"desktop\")?.homepageUrl ||\n bookmaker.url ||\n \"\";\n /* eslint-enable @typescript-eslint/no-explicit-any */\n\n return {\n operator: {\n id: bookmaker.id,\n name: bookmaker.name,\n url: operatorUrl,\n branding: {\n backgroundColor,\n textColor,\n logo,\n },\n },\n type: odd.type,\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n markets: odd.markets.map((market: any) => ({\n type: {\n id: market.type.id,\n code: market.type.code,\n name: market.type.name,\n },\n period: {\n id: market.scope.id,\n type: market.scope.type,\n name: market.scope.name,\n },\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n selections: market.selections.map((selection: any) => {\n // Get the desktop URL from selection.urls array\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const selectionUrl = selection.urls?.find((u: any) => u.appType === \"desktop\")?.url;\n\n // Map movement to canonical format\n let movement: \"UP\" | \"DOWN\" | \"NONE\" = \"NONE\";\n if (selection.movement === \"UP\") movement = \"UP\";\n else if (selection.movement === \"DOWN\") movement = \"DOWN\";\n\n return {\n id: selection.id,\n name: selection.name,\n code: selection.code,\n value: selection.value,\n odds: selection.odds,\n oddsOld: selection.oddsOld,\n movement,\n url: selectionUrl,\n providerSelectionId: selection.providerInfo?.selectionId,\n };\n }),\n })),\n };\n });\n\n result.set(matchId, operators);\n });\n\n return result;\n}\n\n/**\n * Determine if a color is dark based on luminance\n */\nfunction isDarkColor(hexColor?: string): boolean {\n if (!hexColor) return true;\n\n const hex = hexColor.replace(\"#\", \"\");\n const r = parseInt(hex.substring(0, 2), 16);\n const g = parseInt(hex.substring(2, 4), 16);\n const b = parseInt(hex.substring(4, 6), 16);\n\n // Calculate relative luminance (ITU-R BT.709)\n const luminance = (0.2126 * r + 0.7152 * g + 0.0722 * b) / 255;\n\n return luminance < 0.5;\n}\n\n/**\n * Get betting odds for multiple matches in a single API call\n *\n * Fetches odds for multiple matches using the SDK's batch odds endpoint.\n * This is more efficient than making individual calls for each match.\n *\n * @param sdk - The FansUnited SDK instance\n * @param matchIds - Array of match IDs to fetch odds for\n * @param options - Optional parameters for filtering odds\n * @returns Map of match IDs to their betting odds in canonical format\n *\n * @example\n * ```typescript\n * import { useFansUnitedSDK } from 'fansunited-data-layer/client';\n * import { getBatchMatchOdds } from 'fansunited-data-layer/client';\n *\n * function MyComponent() {\n * const sdk = useFansUnitedSDK();\n *\n * const fetchOdds = async () => {\n * if (!sdk) return;\n * const oddsMap = await getBatchMatchOdds(sdk, ['match1', 'match2', 'match3'], {\n * marketTypes: ['1x2', 'OVER_UNDER'],\n * oddType: 'PRE_EVENT'\n * });\n * console.log(oddsMap); // Map<matchId, operators[]>\n * };\n * }\n * ```\n */\nexport async function getBatchMatchOdds(\n sdk: FansUnitedSDKModel,\n matchIds: string[],\n options?: GetBatchMatchOddsOptions\n): Promise<FUSportsBatchMatchOdds> {\n if (!matchIds || matchIds.length === 0) {\n return new Map();\n }\n\n // Build filters for SDK call\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const filters: any = {\n oddType: options?.oddType || \"ALL\",\n scopeType: options?.scopeType || \"ORDINARY_TIME\",\n oddFormat: options?.oddFormat || \"DECIMAL\",\n };\n\n // Add market types if provided\n if (options?.marketTypes && options.marketTypes.length > 0) {\n filters.marketTypes = options.marketTypes.map((market) => getMarketTypeEnum(market));\n }\n\n // Call SDK odds.getByMatchIds()\n const sdkResponse = await sdk.odds.getByMatchIds(matchIds, filters);\n\n // Transform to canonical format\n return transformSDKOdds(sdkResponse, options?.bookmakerIds);\n}\n"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
"description": "A TypeScript library for fetching and transforming sports data from multiple API providers. Returns clean, canonical types that are provider-agnostic.",
|
|
5
5
|
"homepage": "https://fansunited.com/",
|
|
6
6
|
"private": false,
|
|
7
|
-
"version": "0.14.
|
|
7
|
+
"version": "0.14.2",
|
|
8
8
|
"type": "module",
|
|
9
9
|
"sideEffects": false,
|
|
10
10
|
"module": "./fansunited-data-layer.js",
|