fansunited-data-layer 0.16.0 → 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 -2
- 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 +3 -12
- package/api/fansunited/sports/competition/index.d.ts.map +1 -1
- package/api/fansunited/sports/competition/index.js +16 -10
- package/api/fansunited/sports/competition/index.js.map +1 -1
- package/api/fansunited/sports/index.d.ts +6 -0
- package/api/fansunited/sports/index.d.ts.map +1 -1
- 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 +11 -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 +1 -0
- package/types/canonical/index.d.ts.map +1 -1
- package/types/canonical/sports-livescore.types.d.ts +84 -0
- package/types/canonical/sports-livescore.types.d.ts.map +1 -0
package/cache/cache-manager.d.ts
CHANGED
|
@@ -4,34 +4,139 @@
|
|
|
4
4
|
* L2 (SQLite) is never statically imported here — it's injected at runtime
|
|
5
5
|
* via initL2() so that browser bundles don't pull in better-sqlite3.
|
|
6
6
|
*/
|
|
7
|
-
import type { CacheEntry, EntityCacheConfig, EntityType } from "./types";
|
|
8
|
-
/** Interface that L2 stores must satisfy */
|
|
7
|
+
import type { CacheEntry, EntityCacheConfig, EntityType, MaybePromise } from "./types";
|
|
8
|
+
/** Interface that L2 stores must satisfy. Methods may be sync or async. */
|
|
9
9
|
export interface L2Store {
|
|
10
10
|
get isInitialized(): boolean;
|
|
11
|
-
get<T>(key: string): CacheEntry<T> | undefined
|
|
12
|
-
set(key: string, entity:
|
|
11
|
+
get<T>(key: string): MaybePromise<CacheEntry<T> | undefined>;
|
|
12
|
+
set(key: string, entity: EntityType, data: unknown): MaybePromise<void>;
|
|
13
13
|
setMany(entries: {
|
|
14
14
|
key: string;
|
|
15
|
-
entity:
|
|
15
|
+
entity: EntityType;
|
|
16
16
|
data: unknown;
|
|
17
|
-
}[]): void
|
|
18
|
-
cleanup(entity:
|
|
19
|
-
clear(): void
|
|
17
|
+
}[]): MaybePromise<void>;
|
|
18
|
+
cleanup(entity: EntityType, maxTTLSeconds: number): MaybePromise<number>;
|
|
19
|
+
clear(): MaybePromise<void>;
|
|
20
20
|
}
|
|
21
21
|
/**
|
|
22
|
-
* Initialize L2 cache layer. Call
|
|
23
|
-
*
|
|
22
|
+
* Initialize the L2 cache layer. Call once at server startup; the binding
|
|
23
|
+
* is process-wide and replaces any previously-set store.
|
|
24
|
+
*
|
|
25
|
+
* In browser bundles, never call this — L2 is opt-in and the cache will
|
|
26
|
+
* operate L1-only when uninitialized (correct, just slower).
|
|
27
|
+
*
|
|
28
|
+
* In multi-instance deployments (e.g. Cloud Run), L2 should be a shared,
|
|
29
|
+
* durable store (Redis, DynamoDB, …) so a warm cache from one instance
|
|
30
|
+
* serves every other instance. A reference Redis adapter is at
|
|
31
|
+
* `src/lib/cache/__tests__/redis-l2-store.ts`.
|
|
32
|
+
*
|
|
33
|
+
* @example Next.js instrumentation hook
|
|
34
|
+
* ```ts
|
|
35
|
+
* // instrumentation.ts
|
|
36
|
+
* export async function register() {
|
|
37
|
+
* if (process.env.NEXT_RUNTIME !== "nodejs") return;
|
|
38
|
+
* const { initL2 } = await import("fansunited-data-layer");
|
|
39
|
+
* const { createRedisL2Store } = await import("./src/lib/cache/redisL2Store");
|
|
40
|
+
* const { getRedis } = await import("./src/lib/redis");
|
|
41
|
+
* initL2(createRedisL2Store({ client: getRedis() }));
|
|
42
|
+
* }
|
|
43
|
+
* ```
|
|
44
|
+
*
|
|
45
|
+
* @see {@link L2Store} for the interface a custom store must satisfy.
|
|
24
46
|
*/
|
|
25
47
|
export declare function initL2(store: L2Store): void;
|
|
26
48
|
/** Get the L2 store (if initialized) */
|
|
27
49
|
export declare function getL2(): L2Store | null;
|
|
28
50
|
declare const ENTITY_TTL_CONFIG: Record<EntityType, EntityCacheConfig>;
|
|
29
51
|
/**
|
|
30
|
-
*
|
|
52
|
+
* Replace the TTL config for an entity type at runtime. The new values
|
|
53
|
+
* apply to subsequent `cached()` / `cachedBatch()` calls; existing L2
|
|
54
|
+
* entries keep whatever TTL they were written with.
|
|
55
|
+
*
|
|
56
|
+
* Useful for:
|
|
57
|
+
* - per-environment tuning (dev: short TTLs to see changes; prod: long)
|
|
58
|
+
* - overriding ahead of a known traffic spike
|
|
59
|
+
* - tests that need to simulate stale / expired entries in seconds
|
|
60
|
+
*
|
|
61
|
+
* @example
|
|
62
|
+
* ```ts
|
|
63
|
+
* setEntityTTL("sportsCompetition", { staleTTL: 30, maxTTL: 600 });
|
|
64
|
+
* ```
|
|
65
|
+
*
|
|
66
|
+
* @see ../README.md § 7 for the full list of default TTLs.
|
|
67
|
+
*/
|
|
68
|
+
export declare function setEntityTTL(entity: EntityType, config: EntityCacheConfig): void;
|
|
69
|
+
/**
|
|
70
|
+
* Read the current TTL config for an entity type.
|
|
71
|
+
*
|
|
72
|
+
* Primarily for L2 adapters that translate `entity` into an underlying
|
|
73
|
+
* eviction TTL (e.g. Redis `EX` seconds = `maxTTL`). The returned object
|
|
74
|
+
* is a defensive copy — mutating it has no effect on the manager.
|
|
75
|
+
*
|
|
76
|
+
* @throws when called with an entity name not in the config.
|
|
77
|
+
*/
|
|
78
|
+
export declare function getEntityTTL(entity: EntityType): EntityCacheConfig;
|
|
79
|
+
/**
|
|
80
|
+
* Single-key cache-through with stale-while-revalidate.
|
|
81
|
+
*
|
|
82
|
+
* Flow:
|
|
83
|
+
* 1. L1 hit, fresh (< staleTTL) → return.
|
|
84
|
+
* 2. L1 hit, stale but valid (< maxTTL) → return + kick background refresh.
|
|
85
|
+
* 3. L1 miss, L2 hit, fresh → promote to L1 + return.
|
|
86
|
+
* 4. L1 miss, L2 hit, stale but valid → promote + return + background refresh.
|
|
87
|
+
* 5. Full miss → await `fetcher`, populate L1+L2, return.
|
|
88
|
+
*
|
|
89
|
+
* Background refreshes share a per-key in-flight set, so concurrent
|
|
90
|
+
* callers don't trigger a thundering herd of upstream fetches.
|
|
91
|
+
*
|
|
92
|
+
* The `key` must include the {@link CACHE_KEY_PREFIX} (`"fudl:"`) by
|
|
93
|
+
* convention so it can be SCANned safely in a shared Redis instance.
|
|
94
|
+
*
|
|
95
|
+
* @param key Fully-qualified cache key (e.g. `"fudl:sports:competition:fb:c:1:active"`).
|
|
96
|
+
* @param entity Entity type — drives the SWR window via `ENTITY_TTL_CONFIG`.
|
|
97
|
+
* @param fetcher Upstream call, invoked on miss or background refresh.
|
|
98
|
+
*
|
|
99
|
+
* @example
|
|
100
|
+
* ```ts
|
|
101
|
+
* const data = await cached(
|
|
102
|
+
* `${CACHE_KEY_PREFIX}sports:competition:${id}:active`,
|
|
103
|
+
* "sportsCompetition",
|
|
104
|
+
* () => fetchFromSportsApi(id),
|
|
105
|
+
* );
|
|
106
|
+
* ```
|
|
31
107
|
*/
|
|
32
108
|
export declare function cached<T>(key: string, entity: EntityType, fetcher: () => Promise<T>): Promise<T>;
|
|
33
109
|
/**
|
|
34
|
-
*
|
|
110
|
+
* Multi-key cache-through with stale-while-revalidate.
|
|
111
|
+
*
|
|
112
|
+
* For each `id`, the cache is checked (L1, then L2) and only the *missing*
|
|
113
|
+
* IDs are passed to `batchFetcher` in a single upstream call. Stale-but-
|
|
114
|
+
* valid entries are returned immediately and refreshed in the background
|
|
115
|
+
* per-key, just like {@link cached}.
|
|
116
|
+
*
|
|
117
|
+
* Why a separate function from `cached`? An N-ID lookup is one upstream
|
|
118
|
+
* batch request, not N parallel single-key calls — which is the whole
|
|
119
|
+
* reason the Search API supports `?ids=…,…` in the first place.
|
|
120
|
+
*
|
|
121
|
+
* @param ids IDs to resolve, in request order.
|
|
122
|
+
* @param entity Entity type — drives the SWR window.
|
|
123
|
+
* @param batchFetcher Called with the subset of IDs not in cache.
|
|
124
|
+
* Must return a `Map<id, value>` (missing IDs okay).
|
|
125
|
+
* @param keyFn Builds a fully-qualified cache key from an ID.
|
|
126
|
+
* Convention: `(id) => \`${CACHE_KEY_PREFIX}entity:${id}\``.
|
|
127
|
+
*
|
|
128
|
+
* @example
|
|
129
|
+
* ```ts
|
|
130
|
+
* const entities = await cachedBatch(
|
|
131
|
+
* ids,
|
|
132
|
+
* "search",
|
|
133
|
+
* async (missingIds) => {
|
|
134
|
+
* const resp = await searchApi.get({ ids: missingIds, limit: missingIds.length });
|
|
135
|
+
* return new Map(resp.data.map((e) => [e.id, e]));
|
|
136
|
+
* },
|
|
137
|
+
* (id) => `${CACHE_KEY_PREFIX}entity:${id}`,
|
|
138
|
+
* );
|
|
139
|
+
* ```
|
|
35
140
|
*/
|
|
36
141
|
export declare function cachedBatch<T>(ids: string[], entity: EntityType, batchFetcher: (missingIds: string[]) => Promise<Map<string, T>>, keyFn: (id: string) => string): Promise<Map<string, T>>;
|
|
37
142
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cache-manager.d.ts","sourceRoot":"","sources":["../../src/lib/cache/cache-manager.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EAAE,UAAU,EAAE,iBAAiB,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"cache-manager.d.ts","sourceRoot":"","sources":["../../src/lib/cache/cache-manager.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EAAE,UAAU,EAAE,iBAAiB,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAEvF,2EAA2E;AAC3E,MAAM,WAAW,OAAO;IACpB,IAAI,aAAa,IAAI,OAAO,CAAC;IAC7B,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,GAAG,YAAY,CAAC,UAAU,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC,CAAC;IAC7D,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,IAAI,EAAE,OAAO,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC;IACxE,OAAO,CAAC,OAAO,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,UAAU,CAAC;QAAC,IAAI,EAAE,OAAO,CAAA;KAAE,EAAE,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC;IAC3F,OAAO,CAAC,MAAM,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,GAAG,YAAY,CAAC,MAAM,CAAC,CAAC;IACzE,KAAK,IAAI,YAAY,CAAC,IAAI,CAAC,CAAC;CAC/B;AAKD;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,MAAM,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,CAE3C;AAED,wCAAwC;AACxC,wBAAgB,KAAK,IAAI,OAAO,GAAG,IAAI,CAEtC;AAED,QAAA,MAAM,iBAAiB,EAAE,MAAM,CAAC,UAAU,EAAE,iBAAiB,CAS5D,CAAC;AAEF;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,iBAAiB,GAAG,IAAI,CAEhF;AAED;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,UAAU,GAAG,iBAAiB,CAElE;AAwCD;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,wBAAsB,MAAM,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAoCtG;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,wBAAsB,WAAW,CAAC,CAAC,EAC/B,GAAG,EAAE,MAAM,EAAE,EACb,MAAM,EAAE,UAAU,EAClB,YAAY,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAC/D,KAAK,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,MAAM,GAC9B,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,CA6DzB;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAE5C;AAGD,OAAO,EAAE,iBAAiB,EAAE,CAAC"}
|
package/cache/cache-manager.js
CHANGED
|
@@ -4,14 +4,21 @@ function initL2(store) {
|
|
|
4
4
|
l2 = store;
|
|
5
5
|
}
|
|
6
6
|
const ENTITY_TTL_CONFIG = {
|
|
7
|
-
competitions: { staleTTL:
|
|
8
|
-
teams: { staleTTL:
|
|
9
|
-
athletes: { staleTTL:
|
|
10
|
-
venues: { staleTTL:
|
|
11
|
-
countries: { staleTTL:
|
|
12
|
-
coaches: { staleTTL: 24 * 3600, maxTTL:
|
|
13
|
-
search: { staleTTL:
|
|
7
|
+
competitions: { staleTTL: 8 * 3600, maxTTL: 72 * 3600 },
|
|
8
|
+
teams: { staleTTL: 8 * 3600, maxTTL: 48 * 3600 },
|
|
9
|
+
athletes: { staleTTL: 8 * 3600, maxTTL: 48 * 3600 },
|
|
10
|
+
venues: { staleTTL: 7 * 24 * 3600, maxTTL: 14 * 24 * 3600 },
|
|
11
|
+
countries: { staleTTL: 7 * 24 * 3600, maxTTL: 14 * 24 * 3600 },
|
|
12
|
+
coaches: { staleTTL: 24 * 3600, maxTTL: 72 * 3600 },
|
|
13
|
+
search: { staleTTL: 8 * 3600, maxTTL: 2 * 24 * 3600 },
|
|
14
|
+
sportsCompetition: { staleTTL: 5 * 60, maxTTL: 3600 }
|
|
14
15
|
};
|
|
16
|
+
function setEntityTTL(entity, config) {
|
|
17
|
+
ENTITY_TTL_CONFIG[entity] = { ...config };
|
|
18
|
+
}
|
|
19
|
+
function getEntityTTL(entity) {
|
|
20
|
+
return { ...getConfig(entity) };
|
|
21
|
+
}
|
|
15
22
|
const refreshing = /* @__PURE__ */ new Set();
|
|
16
23
|
function getConfig(entity) {
|
|
17
24
|
const config = ENTITY_TTL_CONFIG[entity];
|
|
@@ -29,10 +36,10 @@ function isExpired(entry, maxTTL) {
|
|
|
29
36
|
function backgroundRefresh(key, entity, fetcher) {
|
|
30
37
|
if (refreshing.has(key)) return;
|
|
31
38
|
refreshing.add(key);
|
|
32
|
-
fetcher().then((data) => {
|
|
39
|
+
fetcher().then(async (data) => {
|
|
33
40
|
memoryStore.set(key, data);
|
|
34
41
|
if (l2?.isInitialized) {
|
|
35
|
-
l2.set(key, entity, data);
|
|
42
|
+
await l2.set(key, entity, data);
|
|
36
43
|
}
|
|
37
44
|
}).catch((err) => {
|
|
38
45
|
console.error(`[cache] Background refresh failed for ${key}:`, err);
|
|
@@ -53,7 +60,7 @@ async function cached(key, entity, fetcher) {
|
|
|
53
60
|
}
|
|
54
61
|
}
|
|
55
62
|
if (l2?.isInitialized) {
|
|
56
|
-
const l2Entry = l2.get(key);
|
|
63
|
+
const l2Entry = await l2.get(key);
|
|
57
64
|
if (l2Entry && !isExpired(l2Entry, maxTTL)) {
|
|
58
65
|
memoryStore.set(key, l2Entry.data);
|
|
59
66
|
if (isFresh(l2Entry, staleTTL)) {
|
|
@@ -66,7 +73,7 @@ async function cached(key, entity, fetcher) {
|
|
|
66
73
|
const data = await fetcher();
|
|
67
74
|
memoryStore.set(key, data);
|
|
68
75
|
if (l2?.isInitialized) {
|
|
69
|
-
l2.set(key, entity, data);
|
|
76
|
+
await l2.set(key, entity, data);
|
|
70
77
|
}
|
|
71
78
|
return data;
|
|
72
79
|
}
|
|
@@ -91,7 +98,7 @@ async function cachedBatch(ids, entity, batchFetcher, keyFn) {
|
|
|
91
98
|
found = true;
|
|
92
99
|
}
|
|
93
100
|
if (!found && l2?.isInitialized) {
|
|
94
|
-
const l2Entry = l2.get(key);
|
|
101
|
+
const l2Entry = await l2.get(key);
|
|
95
102
|
if (l2Entry && !isExpired(l2Entry, maxTTL)) {
|
|
96
103
|
memoryStore.set(key, l2Entry.data);
|
|
97
104
|
results.set(id, l2Entry.data);
|
|
@@ -116,7 +123,7 @@ async function cachedBatch(ids, entity, batchFetcher, keyFn) {
|
|
|
116
123
|
const key = keyFn(id);
|
|
117
124
|
memoryStore.set(key, data);
|
|
118
125
|
if (l2?.isInitialized) {
|
|
119
|
-
l2.set(key, entity, data);
|
|
126
|
+
await l2.set(key, entity, data);
|
|
120
127
|
}
|
|
121
128
|
results.set(id, data);
|
|
122
129
|
}
|
|
@@ -130,7 +137,9 @@ export {
|
|
|
130
137
|
ENTITY_TTL_CONFIG,
|
|
131
138
|
cached,
|
|
132
139
|
cachedBatch,
|
|
140
|
+
getEntityTTL,
|
|
133
141
|
initL2,
|
|
134
|
-
invalidate
|
|
142
|
+
invalidate,
|
|
143
|
+
setEntityTTL
|
|
135
144
|
};
|
|
136
145
|
//# sourceMappingURL=cache-manager.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cache-manager.js","sources":["../../src/lib/cache/cache-manager.ts"],"sourcesContent":["/**\n * Cache Manager - Orchestrates L1 (memory) + optional L2 with SWR pattern\n *\n * L2 (SQLite) is never statically imported here — it's injected at runtime\n * via initL2() so that browser bundles don't pull in better-sqlite3.\n */\n\nimport { memoryStore } from \"./memory-store\";\nimport type { CacheEntry, EntityCacheConfig, EntityType } from \"./types\";\n\n/** Interface that L2 stores must satisfy */\nexport interface L2Store {\n get isInitialized(): boolean;\n get<T>(key: string): CacheEntry<T> | undefined;\n set(key: string, entity: string, data: unknown): void;\n setMany(entries: { key: string; entity: string; data: unknown }[]): void;\n cleanup(entity: string, maxTTLSeconds: number): number;\n clear(): void;\n}\n\n/** The active L2 store — null until initL2() is called */\nlet l2: L2Store | null = null;\n\n/**\n * Initialize L2 cache layer. Call on server startup.\n * Client-side apps skip this — L2 is never loaded.\n */\nexport function initL2(store: L2Store): void {\n l2 = store;\n}\n\n/** Get the L2 store (if initialized) */\nexport function getL2(): L2Store | null {\n return l2;\n}\n\nconst ENTITY_TTL_CONFIG: Record<EntityType, EntityCacheConfig> = {\n competitions: { staleTTL: 24 * 3600, maxTTL: 72 * 3600 },\n teams: { staleTTL: 24 * 3600, maxTTL: 48 * 3600 },\n athletes: { staleTTL: 24 * 3600, maxTTL: 48 * 3600 },\n venues: { staleTTL: 72 * 3600, maxTTL: 168 * 3600 },\n countries: { staleTTL: 96 * 3600, maxTTL: 168 * 3600 },\n coaches: { staleTTL: 24 * 3600, maxTTL: 48 * 3600 },\n search: { staleTTL: 300, maxTTL: 1800 },\n};\n\n/** Set of keys currently being refreshed in the background (thundering herd prevention) */\nconst refreshing = new Set<string>();\n\nfunction getConfig(entity: EntityType): EntityCacheConfig {\n const config = ENTITY_TTL_CONFIG[entity];\n if (!config) {\n throw new Error(`Unknown entity type: ${entity}`);\n }\n return config;\n}\n\nfunction isFresh(entry: CacheEntry<unknown>, staleTTL: number): boolean {\n return Date.now() - entry.storedAt < staleTTL * 1000;\n}\n\nfunction isExpired(entry: CacheEntry<unknown>, maxTTL: number): boolean {\n return Date.now() - entry.storedAt >= maxTTL * 1000;\n}\n\nfunction backgroundRefresh<T>(key: string, entity: EntityType, fetcher: () => Promise<T>): void {\n if (refreshing.has(key)) return;\n refreshing.add(key);\n\n fetcher()\n .then((data) => {\n memoryStore.set(key, data);\n if (l2?.isInitialized) {\n l2.set(key, entity, data);\n }\n })\n .catch((err) => {\n console.error(`[cache] Background refresh failed for ${key}:`, err);\n })\n .finally(() => {\n refreshing.delete(key);\n });\n}\n\n/**\n * Single-key cache-through with SWR (Stale-While-Revalidate)\n */\nexport async function cached<T>(key: string, entity: EntityType, fetcher: () => Promise<T>): Promise<T> {\n const { staleTTL, maxTTL } = getConfig(entity);\n\n // L1 check\n const l1 = memoryStore.get<T>(key);\n if (l1) {\n if (isFresh(l1, staleTTL)) {\n return l1.data;\n }\n if (!isExpired(l1, maxTTL)) {\n backgroundRefresh(key, entity, fetcher);\n return l1.data;\n }\n }\n\n // L2 check\n if (l2?.isInitialized) {\n const l2Entry = l2.get<T>(key);\n if (l2Entry && !isExpired(l2Entry, maxTTL)) {\n // Promote to L1\n memoryStore.set(key, l2Entry.data);\n if (isFresh(l2Entry, staleTTL)) {\n return l2Entry.data;\n }\n backgroundRefresh(key, entity, fetcher);\n return l2Entry.data;\n }\n }\n\n // Full miss — await fetcher\n const data = await fetcher();\n memoryStore.set(key, data);\n if (l2?.isInitialized) {\n l2.set(key, entity, data);\n }\n return data;\n}\n\n/**\n * Smart batch cache — only fetches uncached IDs from the API\n */\nexport async function cachedBatch<T>(\n ids: string[],\n entity: EntityType,\n batchFetcher: (missingIds: string[]) => Promise<Map<string, T>>,\n keyFn: (id: string) => string\n): Promise<Map<string, T>> {\n const { staleTTL, maxTTL } = getConfig(entity);\n const results = new Map<string, T>();\n const missingIds: string[] = [];\n\n for (const id of ids) {\n const key = keyFn(id);\n let found = false;\n\n // L1 check\n const l1 = memoryStore.get<T>(key);\n if (l1 && !isExpired(l1, maxTTL)) {\n results.set(id, l1.data);\n if (!isFresh(l1, staleTTL)) {\n backgroundRefresh(key, entity, async () => {\n const fetched = await batchFetcher([id]);\n const item = fetched.get(id);\n if (!item) throw new Error(`Batch fetcher did not return ID: ${id}`);\n return item;\n });\n }\n found = true;\n }\n\n // L2 check\n if (!found && l2?.isInitialized) {\n const l2Entry = l2.get<T>(key);\n if (l2Entry && !isExpired(l2Entry, maxTTL)) {\n memoryStore.set(key, l2Entry.data);\n results.set(id, l2Entry.data);\n if (!isFresh(l2Entry, staleTTL)) {\n backgroundRefresh(key, entity, async () => {\n const fetched = await batchFetcher([id]);\n const item = fetched.get(id);\n if (!item) throw new Error(`Batch fetcher did not return ID: ${id}`);\n return item;\n });\n }\n found = true;\n }\n }\n\n if (!found) {\n missingIds.push(id);\n }\n }\n\n // Fetch missing IDs in a single batch\n if (missingIds.length > 0) {\n const fetched = await batchFetcher(missingIds);\n for (const [id, data] of fetched) {\n const key = keyFn(id);\n memoryStore.set(key, data);\n if (l2?.isInitialized) {\n l2.set(key, entity, data);\n }\n results.set(id, data);\n }\n }\n\n return results;\n}\n\n/**\n * Remove an entry from both cache layers\n */\nexport function invalidate(key: string): void {\n memoryStore.delete(key);\n}\n\n// Export for testing\nexport { ENTITY_TTL_CONFIG };\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"cache-manager.js","sources":["../../src/lib/cache/cache-manager.ts"],"sourcesContent":["/**\n * Cache Manager - Orchestrates L1 (memory) + optional L2 with SWR pattern\n *\n * L2 (SQLite) is never statically imported here — it's injected at runtime\n * via initL2() so that browser bundles don't pull in better-sqlite3.\n */\n\nimport { memoryStore } from \"./memory-store\";\nimport type { CacheEntry, EntityCacheConfig, EntityType, MaybePromise } from \"./types\";\n\n/** Interface that L2 stores must satisfy. Methods may be sync or async. */\nexport interface L2Store {\n get isInitialized(): boolean;\n get<T>(key: string): MaybePromise<CacheEntry<T> | undefined>;\n set(key: string, entity: EntityType, data: unknown): MaybePromise<void>;\n setMany(entries: { key: string; entity: EntityType; data: unknown }[]): MaybePromise<void>;\n cleanup(entity: EntityType, maxTTLSeconds: number): MaybePromise<number>;\n clear(): MaybePromise<void>;\n}\n\n/** The active L2 store — null until initL2() is called */\nlet l2: L2Store | null = null;\n\n/**\n * Initialize the L2 cache layer. Call once at server startup; the binding\n * is process-wide and replaces any previously-set store.\n *\n * In browser bundles, never call this — L2 is opt-in and the cache will\n * operate L1-only when uninitialized (correct, just slower).\n *\n * In multi-instance deployments (e.g. Cloud Run), L2 should be a shared,\n * durable store (Redis, DynamoDB, …) so a warm cache from one instance\n * serves every other instance. A reference Redis adapter is at\n * `src/lib/cache/__tests__/redis-l2-store.ts`.\n *\n * @example Next.js instrumentation hook\n * ```ts\n * // instrumentation.ts\n * export async function register() {\n * if (process.env.NEXT_RUNTIME !== \"nodejs\") return;\n * const { initL2 } = await import(\"fansunited-data-layer\");\n * const { createRedisL2Store } = await import(\"./src/lib/cache/redisL2Store\");\n * const { getRedis } = await import(\"./src/lib/redis\");\n * initL2(createRedisL2Store({ client: getRedis() }));\n * }\n * ```\n *\n * @see {@link L2Store} for the interface a custom store must satisfy.\n */\nexport function initL2(store: L2Store): void {\n l2 = store;\n}\n\n/** Get the L2 store (if initialized) */\nexport function getL2(): L2Store | null {\n return l2;\n}\n\nconst ENTITY_TTL_CONFIG: Record<EntityType, EntityCacheConfig> = {\n competitions: { staleTTL: 8 * 3600, maxTTL: 72 * 3600 },\n teams: { staleTTL: 8 * 3600, maxTTL: 48 * 3600 },\n athletes: { staleTTL: 8 * 3600, maxTTL: 48 * 3600 },\n venues: { staleTTL: 7 * 24 * 3600, maxTTL: 14 * 24 * 3600 },\n countries: { staleTTL: 7 * 24 * 3600, maxTTL: 14 * 24 * 3600 },\n coaches: { staleTTL: 24 * 3600, maxTTL: 72 * 3600 },\n search: { staleTTL: 8 * 3600, maxTTL: 2 * 24 * 3600 },\n sportsCompetition: { staleTTL: 5 * 60, maxTTL: 3600 },\n};\n\n/**\n * Replace the TTL config for an entity type at runtime. The new values\n * apply to subsequent `cached()` / `cachedBatch()` calls; existing L2\n * entries keep whatever TTL they were written with.\n *\n * Useful for:\n * - per-environment tuning (dev: short TTLs to see changes; prod: long)\n * - overriding ahead of a known traffic spike\n * - tests that need to simulate stale / expired entries in seconds\n *\n * @example\n * ```ts\n * setEntityTTL(\"sportsCompetition\", { staleTTL: 30, maxTTL: 600 });\n * ```\n *\n * @see ../README.md § 7 for the full list of default TTLs.\n */\nexport function setEntityTTL(entity: EntityType, config: EntityCacheConfig): void {\n ENTITY_TTL_CONFIG[entity] = { ...config };\n}\n\n/**\n * Read the current TTL config for an entity type.\n *\n * Primarily for L2 adapters that translate `entity` into an underlying\n * eviction TTL (e.g. Redis `EX` seconds = `maxTTL`). The returned object\n * is a defensive copy — mutating it has no effect on the manager.\n *\n * @throws when called with an entity name not in the config.\n */\nexport function getEntityTTL(entity: EntityType): EntityCacheConfig {\n return { ...getConfig(entity) };\n}\n\n/** Set of keys currently being refreshed in the background (thundering herd prevention) */\nconst refreshing = new Set<string>();\n\nfunction getConfig(entity: EntityType): EntityCacheConfig {\n const config = ENTITY_TTL_CONFIG[entity];\n if (!config) {\n throw new Error(`Unknown entity type: ${entity}`);\n }\n return config;\n}\n\nfunction isFresh(entry: CacheEntry<unknown>, staleTTL: number): boolean {\n return Date.now() - entry.storedAt < staleTTL * 1000;\n}\n\nfunction isExpired(entry: CacheEntry<unknown>, maxTTL: number): boolean {\n return Date.now() - entry.storedAt >= maxTTL * 1000;\n}\n\nfunction backgroundRefresh<T>(key: string, entity: EntityType, fetcher: () => Promise<T>): void {\n if (refreshing.has(key)) return;\n refreshing.add(key);\n\n fetcher()\n .then(async (data) => {\n memoryStore.set(key, data);\n if (l2?.isInitialized) {\n await l2.set(key, entity, data);\n }\n })\n .catch((err) => {\n console.error(`[cache] Background refresh failed for ${key}:`, err);\n })\n .finally(() => {\n refreshing.delete(key);\n });\n}\n\n/**\n * Single-key cache-through with stale-while-revalidate.\n *\n * Flow:\n * 1. L1 hit, fresh (< staleTTL) → return.\n * 2. L1 hit, stale but valid (< maxTTL) → return + kick background refresh.\n * 3. L1 miss, L2 hit, fresh → promote to L1 + return.\n * 4. L1 miss, L2 hit, stale but valid → promote + return + background refresh.\n * 5. Full miss → await `fetcher`, populate L1+L2, return.\n *\n * Background refreshes share a per-key in-flight set, so concurrent\n * callers don't trigger a thundering herd of upstream fetches.\n *\n * The `key` must include the {@link CACHE_KEY_PREFIX} (`\"fudl:\"`) by\n * convention so it can be SCANned safely in a shared Redis instance.\n *\n * @param key Fully-qualified cache key (e.g. `\"fudl:sports:competition:fb:c:1:active\"`).\n * @param entity Entity type — drives the SWR window via `ENTITY_TTL_CONFIG`.\n * @param fetcher Upstream call, invoked on miss or background refresh.\n *\n * @example\n * ```ts\n * const data = await cached(\n * `${CACHE_KEY_PREFIX}sports:competition:${id}:active`,\n * \"sportsCompetition\",\n * () => fetchFromSportsApi(id),\n * );\n * ```\n */\nexport async function cached<T>(key: string, entity: EntityType, fetcher: () => Promise<T>): Promise<T> {\n const { staleTTL, maxTTL } = getConfig(entity);\n\n // L1 check\n const l1 = memoryStore.get<T>(key);\n if (l1) {\n if (isFresh(l1, staleTTL)) {\n return l1.data;\n }\n if (!isExpired(l1, maxTTL)) {\n backgroundRefresh(key, entity, fetcher);\n return l1.data;\n }\n }\n\n // L2 check\n if (l2?.isInitialized) {\n const l2Entry = await l2.get<T>(key);\n if (l2Entry && !isExpired(l2Entry, maxTTL)) {\n // Promote to L1\n memoryStore.set(key, l2Entry.data);\n if (isFresh(l2Entry, staleTTL)) {\n return l2Entry.data;\n }\n backgroundRefresh(key, entity, fetcher);\n return l2Entry.data;\n }\n }\n\n // Full miss — await fetcher\n const data = await fetcher();\n memoryStore.set(key, data);\n if (l2?.isInitialized) {\n await l2.set(key, entity, data);\n }\n return data;\n}\n\n/**\n * Multi-key cache-through with stale-while-revalidate.\n *\n * For each `id`, the cache is checked (L1, then L2) and only the *missing*\n * IDs are passed to `batchFetcher` in a single upstream call. Stale-but-\n * valid entries are returned immediately and refreshed in the background\n * per-key, just like {@link cached}.\n *\n * Why a separate function from `cached`? An N-ID lookup is one upstream\n * batch request, not N parallel single-key calls — which is the whole\n * reason the Search API supports `?ids=…,…` in the first place.\n *\n * @param ids IDs to resolve, in request order.\n * @param entity Entity type — drives the SWR window.\n * @param batchFetcher Called with the subset of IDs not in cache.\n * Must return a `Map<id, value>` (missing IDs okay).\n * @param keyFn Builds a fully-qualified cache key from an ID.\n * Convention: `(id) => \\`${CACHE_KEY_PREFIX}entity:${id}\\``.\n *\n * @example\n * ```ts\n * const entities = await cachedBatch(\n * ids,\n * \"search\",\n * async (missingIds) => {\n * const resp = await searchApi.get({ ids: missingIds, limit: missingIds.length });\n * return new Map(resp.data.map((e) => [e.id, e]));\n * },\n * (id) => `${CACHE_KEY_PREFIX}entity:${id}`,\n * );\n * ```\n */\nexport async function cachedBatch<T>(\n ids: string[],\n entity: EntityType,\n batchFetcher: (missingIds: string[]) => Promise<Map<string, T>>,\n keyFn: (id: string) => string\n): Promise<Map<string, T>> {\n const { staleTTL, maxTTL } = getConfig(entity);\n const results = new Map<string, T>();\n const missingIds: string[] = [];\n\n for (const id of ids) {\n const key = keyFn(id);\n let found = false;\n\n // L1 check\n const l1 = memoryStore.get<T>(key);\n if (l1 && !isExpired(l1, maxTTL)) {\n results.set(id, l1.data);\n if (!isFresh(l1, staleTTL)) {\n backgroundRefresh(key, entity, async () => {\n const fetched = await batchFetcher([id]);\n const item = fetched.get(id);\n if (!item) throw new Error(`Batch fetcher did not return ID: ${id}`);\n return item;\n });\n }\n found = true;\n }\n\n // L2 check\n if (!found && l2?.isInitialized) {\n const l2Entry = await l2.get<T>(key);\n if (l2Entry && !isExpired(l2Entry, maxTTL)) {\n memoryStore.set(key, l2Entry.data);\n results.set(id, l2Entry.data);\n if (!isFresh(l2Entry, staleTTL)) {\n backgroundRefresh(key, entity, async () => {\n const fetched = await batchFetcher([id]);\n const item = fetched.get(id);\n if (!item) throw new Error(`Batch fetcher did not return ID: ${id}`);\n return item;\n });\n }\n found = true;\n }\n }\n\n if (!found) {\n missingIds.push(id);\n }\n }\n\n // Fetch missing IDs in a single batch\n if (missingIds.length > 0) {\n const fetched = await batchFetcher(missingIds);\n for (const [id, data] of fetched) {\n const key = keyFn(id);\n memoryStore.set(key, data);\n if (l2?.isInitialized) {\n await l2.set(key, entity, data);\n }\n results.set(id, data);\n }\n }\n\n return results;\n}\n\n/**\n * Remove an entry from both cache layers\n */\nexport function invalidate(key: string): void {\n memoryStore.delete(key);\n}\n\n// Export for testing\nexport { ENTITY_TTL_CONFIG };\n"],"names":[],"mappings":";AAqBA,IAAI,KAAqB;AA4BlB,SAAS,OAAO,OAAsB;AACzC,OAAK;AACT;AAOA,MAAM,oBAA2D;AAAA,EAC7D,cAAc,EAAE,UAAU,IAAI,MAAM,QAAQ,KAAK,KAAA;AAAA,EACjD,OAAO,EAAE,UAAU,IAAI,MAAM,QAAQ,KAAK,KAAA;AAAA,EAC1C,UAAU,EAAE,UAAU,IAAI,MAAM,QAAQ,KAAK,KAAA;AAAA,EAC7C,QAAQ,EAAE,UAAU,IAAI,KAAK,MAAM,QAAQ,KAAK,KAAK,KAAA;AAAA,EACrD,WAAW,EAAE,UAAU,IAAI,KAAK,MAAM,QAAQ,KAAK,KAAK,KAAA;AAAA,EACxD,SAAS,EAAE,UAAU,KAAK,MAAM,QAAQ,KAAK,KAAA;AAAA,EAC7C,QAAQ,EAAE,UAAU,IAAI,MAAM,QAAQ,IAAI,KAAK,KAAA;AAAA,EAC/C,mBAAmB,EAAE,UAAU,IAAI,IAAI,QAAQ,KAAA;AACnD;AAmBO,SAAS,aAAa,QAAoB,QAAiC;AAC9E,oBAAkB,MAAM,IAAI,EAAE,GAAG,OAAA;AACrC;AAWO,SAAS,aAAa,QAAuC;AAChE,SAAO,EAAE,GAAG,UAAU,MAAM,EAAA;AAChC;AAGA,MAAM,iCAAiB,IAAA;AAEvB,SAAS,UAAU,QAAuC;AACtD,QAAM,SAAS,kBAAkB,MAAM;AACvC,MAAI,CAAC,QAAQ;AACT,UAAM,IAAI,MAAM,wBAAwB,MAAM,EAAE;AAAA,EACpD;AACA,SAAO;AACX;AAEA,SAAS,QAAQ,OAA4B,UAA2B;AACpE,SAAO,KAAK,IAAA,IAAQ,MAAM,WAAW,WAAW;AACpD;AAEA,SAAS,UAAU,OAA4B,QAAyB;AACpE,SAAO,KAAK,IAAA,IAAQ,MAAM,YAAY,SAAS;AACnD;AAEA,SAAS,kBAAqB,KAAa,QAAoB,SAAiC;AAC5F,MAAI,WAAW,IAAI,GAAG,EAAG;AACzB,aAAW,IAAI,GAAG;AAElB,UAAA,EACK,KAAK,OAAO,SAAS;AAClB,gBAAY,IAAI,KAAK,IAAI;AACzB,QAAI,IAAI,eAAe;AACnB,YAAM,GAAG,IAAI,KAAK,QAAQ,IAAI;AAAA,IAClC;AAAA,EACJ,CAAC,EACA,MAAM,CAAC,QAAQ;AACZ,YAAQ,MAAM,yCAAyC,GAAG,KAAK,GAAG;AAAA,EACtE,CAAC,EACA,QAAQ,MAAM;AACX,eAAW,OAAO,GAAG;AAAA,EACzB,CAAC;AACT;AA+BA,eAAsB,OAAU,KAAa,QAAoB,SAAuC;AACpG,QAAM,EAAE,UAAU,WAAW,UAAU,MAAM;AAG7C,QAAM,KAAK,YAAY,IAAO,GAAG;AACjC,MAAI,IAAI;AACJ,QAAI,QAAQ,IAAI,QAAQ,GAAG;AACvB,aAAO,GAAG;AAAA,IACd;AACA,QAAI,CAAC,UAAU,IAAI,MAAM,GAAG;AACxB,wBAAkB,KAAK,QAAQ,OAAO;AACtC,aAAO,GAAG;AAAA,IACd;AAAA,EACJ;AAGA,MAAI,IAAI,eAAe;AACnB,UAAM,UAAU,MAAM,GAAG,IAAO,GAAG;AACnC,QAAI,WAAW,CAAC,UAAU,SAAS,MAAM,GAAG;AAExC,kBAAY,IAAI,KAAK,QAAQ,IAAI;AACjC,UAAI,QAAQ,SAAS,QAAQ,GAAG;AAC5B,eAAO,QAAQ;AAAA,MACnB;AACA,wBAAkB,KAAK,QAAQ,OAAO;AACtC,aAAO,QAAQ;AAAA,IACnB;AAAA,EACJ;AAGA,QAAM,OAAO,MAAM,QAAA;AACnB,cAAY,IAAI,KAAK,IAAI;AACzB,MAAI,IAAI,eAAe;AACnB,UAAM,GAAG,IAAI,KAAK,QAAQ,IAAI;AAAA,EAClC;AACA,SAAO;AACX;AAkCA,eAAsB,YAClB,KACA,QACA,cACA,OACuB;AACvB,QAAM,EAAE,UAAU,WAAW,UAAU,MAAM;AAC7C,QAAM,8BAAc,IAAA;AACpB,QAAM,aAAuB,CAAA;AAE7B,aAAW,MAAM,KAAK;AAClB,UAAM,MAAM,MAAM,EAAE;AACpB,QAAI,QAAQ;AAGZ,UAAM,KAAK,YAAY,IAAO,GAAG;AACjC,QAAI,MAAM,CAAC,UAAU,IAAI,MAAM,GAAG;AAC9B,cAAQ,IAAI,IAAI,GAAG,IAAI;AACvB,UAAI,CAAC,QAAQ,IAAI,QAAQ,GAAG;AACxB,0BAAkB,KAAK,QAAQ,YAAY;AACvC,gBAAM,UAAU,MAAM,aAAa,CAAC,EAAE,CAAC;AACvC,gBAAM,OAAO,QAAQ,IAAI,EAAE;AAC3B,cAAI,CAAC,KAAM,OAAM,IAAI,MAAM,oCAAoC,EAAE,EAAE;AACnE,iBAAO;AAAA,QACX,CAAC;AAAA,MACL;AACA,cAAQ;AAAA,IACZ;AAGA,QAAI,CAAC,SAAS,IAAI,eAAe;AAC7B,YAAM,UAAU,MAAM,GAAG,IAAO,GAAG;AACnC,UAAI,WAAW,CAAC,UAAU,SAAS,MAAM,GAAG;AACxC,oBAAY,IAAI,KAAK,QAAQ,IAAI;AACjC,gBAAQ,IAAI,IAAI,QAAQ,IAAI;AAC5B,YAAI,CAAC,QAAQ,SAAS,QAAQ,GAAG;AAC7B,4BAAkB,KAAK,QAAQ,YAAY;AACvC,kBAAM,UAAU,MAAM,aAAa,CAAC,EAAE,CAAC;AACvC,kBAAM,OAAO,QAAQ,IAAI,EAAE;AAC3B,gBAAI,CAAC,KAAM,OAAM,IAAI,MAAM,oCAAoC,EAAE,EAAE;AACnE,mBAAO;AAAA,UACX,CAAC;AAAA,QACL;AACA,gBAAQ;AAAA,MACZ;AAAA,IACJ;AAEA,QAAI,CAAC,OAAO;AACR,iBAAW,KAAK,EAAE;AAAA,IACtB;AAAA,EACJ;AAGA,MAAI,WAAW,SAAS,GAAG;AACvB,UAAM,UAAU,MAAM,aAAa,UAAU;AAC7C,eAAW,CAAC,IAAI,IAAI,KAAK,SAAS;AAC9B,YAAM,MAAM,MAAM,EAAE;AACpB,kBAAY,IAAI,KAAK,IAAI;AACzB,UAAI,IAAI,eAAe;AACnB,cAAM,GAAG,IAAI,KAAK,QAAQ,IAAI;AAAA,MAClC;AACA,cAAQ,IAAI,IAAI,IAAI;AAAA,IACxB;AAAA,EACJ;AAEA,SAAO;AACX;AAKO,SAAS,WAAW,KAAmB;AAC1C,cAAY,OAAO,GAAG;AAC1B;"}
|
package/cache/cleanup.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Periodic L2 cache cleanup
|
|
3
3
|
*/
|
|
4
|
-
export declare function runCleanup(): void
|
|
4
|
+
export declare function runCleanup(): Promise<void>;
|
|
5
5
|
export declare function startCleanupSchedule(intervalMs?: number): void;
|
|
6
6
|
export declare function stopCleanupSchedule(): void;
|
|
7
7
|
//# sourceMappingURL=cleanup.d.ts.map
|
package/cache/cleanup.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cleanup.d.ts","sourceRoot":"","sources":["../../src/lib/cache/cleanup.ts"],"names":[],"mappings":"AAAA;;GAEG;AAOH,
|
|
1
|
+
{"version":3,"file":"cleanup.d.ts","sourceRoot":"","sources":["../../src/lib/cache/cleanup.ts"],"names":[],"mappings":"AAAA;;GAEG;AAOH,wBAAsB,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC,CAWhD;AAED,wBAAgB,oBAAoB,CAAC,UAAU,SAAiB,GAAG,IAAI,CAGtE;AAED,wBAAgB,mBAAmB,IAAI,IAAI,CAK1C"}
|
package/cache/index.d.ts
CHANGED
|
@@ -6,8 +6,9 @@
|
|
|
6
6
|
* import { initSqliteStore, sqliteStore } from './cache/sqlite-store';
|
|
7
7
|
*/
|
|
8
8
|
export { memoryStore } from "./memory-store";
|
|
9
|
-
export { cached, cachedBatch, invalidate, initL2 } from "./cache-manager";
|
|
9
|
+
export { cached, cachedBatch, invalidate, initL2, setEntityTTL, getEntityTTL } from "./cache-manager";
|
|
10
10
|
export type { L2Store } from "./cache-manager";
|
|
11
11
|
export { runCleanup, startCleanupSchedule, stopCleanupSchedule } from "./cleanup";
|
|
12
|
-
export type { CacheEntry, EntityCacheConfig, EntityType } from "./types";
|
|
12
|
+
export type { CacheEntry, EntityCacheConfig, EntityType, MaybePromise } from "./types";
|
|
13
|
+
export { CACHE_KEY_PREFIX } from "./types";
|
|
13
14
|
//# sourceMappingURL=index.d.ts.map
|
package/cache/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/lib/cache/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAC7C,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/lib/cache/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAC7C,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AACtG,YAAY,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAC/C,OAAO,EAAE,UAAU,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,MAAM,WAAW,CAAC;AAClF,YAAY,EAAE,UAAU,EAAE,iBAAiB,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvF,OAAO,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC"}
|
package/cache/sqlite-store.d.ts
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* L2 Cache - SQLite on-disk store (server-only)
|
|
3
3
|
*/
|
|
4
|
-
import type { CacheEntry } from "./types";
|
|
4
|
+
import type { CacheEntry, EntityType } from "./types";
|
|
5
5
|
export declare function initSqliteStore(dbPath?: string): void;
|
|
6
6
|
export declare const sqliteStore: {
|
|
7
7
|
readonly isInitialized: boolean;
|
|
8
8
|
get<T>(key: string): CacheEntry<T> | undefined;
|
|
9
|
-
set(key: string, entity:
|
|
9
|
+
set(key: string, entity: EntityType, data: unknown): void;
|
|
10
10
|
setMany(entries: {
|
|
11
11
|
key: string;
|
|
12
|
-
entity:
|
|
12
|
+
entity: EntityType;
|
|
13
13
|
data: unknown;
|
|
14
14
|
}[]): void;
|
|
15
|
-
cleanup(entity:
|
|
15
|
+
cleanup(entity: EntityType, maxTTLSeconds: number): number;
|
|
16
16
|
clear(): void;
|
|
17
17
|
close(): void;
|
|
18
18
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sqlite-store.d.ts","sourceRoot":"","sources":["../../src/lib/cache/sqlite-store.ts"],"names":[],"mappings":"AAAA;;GAEG;AAKH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"sqlite-store.d.ts","sourceRoot":"","sources":["../../src/lib/cache/sqlite-store.ts"],"names":[],"mappings":"AAAA;;GAEG;AAKH,OAAO,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAWtD,wBAAgB,eAAe,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CA4BrD;AAED,eAAO,MAAM,WAAW;4BACC,OAAO;QAIxB,CAAC,OAAO,MAAM,GAAG,UAAU,CAAC,CAAC,CAAC,GAAG,SAAS;aAarC,MAAM,UAAU,UAAU,QAAQ,OAAO,GAAG,IAAI;qBASxC;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,UAAU,CAAC;QAAC,IAAI,EAAE,OAAO,CAAA;KAAE,EAAE,GAAG,IAAI;oBAgB5D,UAAU,iBAAiB,MAAM,GAAG,MAAM;aAQjD,IAAI;aAIJ,IAAI;CAMhB,CAAC"}
|
package/cache/types.d.ts
CHANGED
|
@@ -11,5 +11,13 @@ export interface EntityCacheConfig {
|
|
|
11
11
|
/** Time in seconds before data is completely expired and must be re-fetched */
|
|
12
12
|
maxTTL: number;
|
|
13
13
|
}
|
|
14
|
-
export type EntityType = "competitions" | "teams" | "athletes" | "venues" | "countries" | "coaches" | "search";
|
|
14
|
+
export type EntityType = "competitions" | "teams" | "athletes" | "venues" | "countries" | "coaches" | "search" | "sportsCompetition";
|
|
15
|
+
/** Convenience: a value that may or may not be wrapped in a Promise. */
|
|
16
|
+
export type MaybePromise<T> = T | Promise<T>;
|
|
17
|
+
/**
|
|
18
|
+
* Prefix for every cache key emitted by this library.
|
|
19
|
+
* Namespaces the data-layer's entries inside a shared store (Redis SCAN by `fudl:*`).
|
|
20
|
+
* Bump the literal (e.g. `"fudl:v2:"`) to invalidate every cached entry across deploys.
|
|
21
|
+
*/
|
|
22
|
+
export declare const CACHE_KEY_PREFIX = "fudl:";
|
|
15
23
|
//# sourceMappingURL=types.d.ts.map
|
package/cache/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/lib/cache/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,MAAM,WAAW,UAAU,CAAC,CAAC;IACzB,IAAI,EAAE,CAAC,CAAC;IACR,QAAQ,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,iBAAiB;IAC9B,oFAAoF;IACpF,QAAQ,EAAE,MAAM,CAAC;IACjB,+EAA+E;IAC/E,MAAM,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,MAAM,UAAU,
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/lib/cache/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,MAAM,WAAW,UAAU,CAAC,CAAC;IACzB,IAAI,EAAE,CAAC,CAAC;IACR,QAAQ,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,iBAAiB;IAC9B,oFAAoF;IACpF,QAAQ,EAAE,MAAM,CAAC;IACjB,+EAA+E;IAC/E,MAAM,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,MAAM,UAAU,GAChB,cAAc,GACd,OAAO,GACP,UAAU,GACV,QAAQ,GACR,WAAW,GACX,SAAS,GACT,QAAQ,GACR,mBAAmB,CAAC;AAE1B,wEAAwE;AACxE,MAAM,MAAM,YAAY,CAAC,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;AAE7C;;;;GAIG;AACH,eAAO,MAAM,gBAAgB,UAAU,CAAC"}
|
package/cache/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sources":["../../src/lib/cache/types.ts"],"sourcesContent":["/**\n * Shared cache types\n */\n\nexport interface CacheEntry<T> {\n data: T;\n storedAt: number; // Unix timestamp in ms\n}\n\nexport interface EntityCacheConfig {\n /** Time in seconds before data is considered stale (triggers background refresh) */\n staleTTL: number;\n /** Time in seconds before data is completely expired and must be re-fetched */\n maxTTL: number;\n}\n\nexport type EntityType =\n | \"competitions\"\n | \"teams\"\n | \"athletes\"\n | \"venues\"\n | \"countries\"\n | \"coaches\"\n | \"search\"\n | \"sportsCompetition\";\n\n/** Convenience: a value that may or may not be wrapped in a Promise. */\nexport type MaybePromise<T> = T | Promise<T>;\n\n/**\n * Prefix for every cache key emitted by this library.\n * Namespaces the data-layer's entries inside a shared store (Redis SCAN by `fudl:*`).\n * Bump the literal (e.g. `\"fudl:v2:\"`) to invalidate every cached entry across deploys.\n */\nexport const CACHE_KEY_PREFIX = \"fudl:\";\n"],"names":[],"mappings":"AAkCO,MAAM,mBAAmB;"}
|
package/fansunited-data-layer.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { getConfig, isConfigured, setConfig } from "./config/index.js";
|
|
2
2
|
import { memoryStore } from "./cache/memory-store.js";
|
|
3
|
+
import { CACHE_KEY_PREFIX } from "./cache/types.js";
|
|
3
4
|
import { GOAL_EVENT_TYPES } from "./utilities/stats/core/types.js";
|
|
4
5
|
import { addProviderRef, getProviderRefId, toProviderRefArray } from "./api/sportal365-sports/shared/providerRef.helper.js";
|
|
5
6
|
import { aggregatePlayerStatisticsFromMatches, getAllPlayerStatisticsFromMatches, getMostCardedPlayersFromMatches, getTopAssistersFromMatches, getTopGoalContributorsFromMatches, getTopScorersFromMatches } from "./helpers/player.helpers.js";
|
|
6
7
|
import { analyzeMatch, calcAverage, calcPercentage, formatAsAverage, formatPossessionPercentage, formatStatValue, formatWithAverage, getOpponentScore, getTeamScore, isMatchFinished, isTeamAway, isTeamHome, isTeamInMatch } from "./utilities/stats/core/helpers.js";
|
|
7
|
-
import { cached, cachedBatch, initL2, invalidate } from "./cache/cache-manager.js";
|
|
8
|
+
import { cached, cachedBatch, getEntityTTL, initL2, invalidate, setEntityTTL } from "./cache/cache-manager.js";
|
|
8
9
|
import { calculateH2HStats, getRecentH2HMeetings } from "./utilities/stats/match/headToHead.js";
|
|
9
10
|
import { calculateStreak, getTeamStreaks, getTeamStreaksComparison, streakFilters } from "./utilities/stats/team/streaks.js";
|
|
10
11
|
import { getActiveSeason } from "./helpers/competition.helpers.js";
|
|
@@ -18,6 +19,9 @@ import { getFansUnitedFootballPlayer, getFansUnitedFootballPlayers } from "./api
|
|
|
18
19
|
import { getFansUnitedFootballSearch } from "./api/fansunited/football/search/index.js";
|
|
19
20
|
import { getFansUnitedFootballTeam, getFansUnitedFootballTeams } from "./api/fansunited/football/teams/index.js";
|
|
20
21
|
import { getFansUnitedSportsCompetition } from "./api/fansunited/sports/competition/index.js";
|
|
22
|
+
import { getFansUnitedSportsCompetitionHydrated } from "./api/fansunited/sports/competition/hydrated.js";
|
|
23
|
+
import { getFansUnitedSportsLivescore } from "./api/fansunited/sports/livescore/index.js";
|
|
24
|
+
import { getFansUnitedSportsLivescoreHydrated } from "./api/fansunited/sports/livescore/hydrated.js";
|
|
21
25
|
import { getFootballCompetition, getFootballSeasonDetails } from "./api/sportal365-sports/football/competitions/index.js";
|
|
22
26
|
import { getFootballLivescore, getFootballMatch, getFootballMatchCommentary, getFootballMatchEvents, getFootballMatchLineups, getFootballMatchOdds, getFootballMatchStatistics, getFootballMatches } from "./api/sportal365-sports/football/matches/index.js";
|
|
23
27
|
import { getFootballPlayerCareerStatistics, getFootballPlayerRecentStatistics, getFootballPlayerSeasonStatistics, getFootballPlayerSeasonStatisticsByPlayerIds } from "./api/sportal365-sports/football/statistics/index.js";
|
|
@@ -38,6 +42,7 @@ import { search } from "./api/sportal365-sports/search/index.js";
|
|
|
38
42
|
import { searchFootball } from "./api/sportal365-sports/football/search/index.js";
|
|
39
43
|
import { useAllPlayerStatisticsFromMatches, useMostCardedPlayersFromMatches, usePlayerStatistics, usePlayerStatisticsMap, useTopAssistersFromMatches, useTopGoalContributorsFromMatches, useTopScorersFromMatches } from "./helpers/player.hooks.js";
|
|
40
44
|
export {
|
|
45
|
+
CACHE_KEY_PREFIX,
|
|
41
46
|
GOAL_EVENT_TYPES,
|
|
42
47
|
addProviderRef,
|
|
43
48
|
aggregatePlayerStatisticsFromMatches,
|
|
@@ -57,6 +62,7 @@ export {
|
|
|
57
62
|
getBasketballLivescore,
|
|
58
63
|
getBatchMatchOdds,
|
|
59
64
|
getConfig,
|
|
65
|
+
getEntityTTL,
|
|
60
66
|
getFansUnitedCompetitions,
|
|
61
67
|
getFansUnitedCountries,
|
|
62
68
|
getFansUnitedEntitiesByIds,
|
|
@@ -76,6 +82,9 @@ export {
|
|
|
76
82
|
getFansUnitedFootballTeams,
|
|
77
83
|
getFansUnitedSearchEntities,
|
|
78
84
|
getFansUnitedSportsCompetition,
|
|
85
|
+
getFansUnitedSportsCompetitionHydrated,
|
|
86
|
+
getFansUnitedSportsLivescore,
|
|
87
|
+
getFansUnitedSportsLivescoreHydrated,
|
|
79
88
|
getFansUnitedTeamsByCompetition,
|
|
80
89
|
getFansUnitedTeamsByCountry,
|
|
81
90
|
getFansUnitedVenues,
|
|
@@ -139,6 +148,7 @@ export {
|
|
|
139
148
|
search,
|
|
140
149
|
searchFootball,
|
|
141
150
|
setConfig,
|
|
151
|
+
setEntityTTL,
|
|
142
152
|
streakFilters,
|
|
143
153
|
toProviderRefArray,
|
|
144
154
|
useAllPlayerStatisticsFromMatches,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fansunited-data-layer.js","sources":[],"sourcesContent":[],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"fansunited-data-layer.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
|
package/index.d.ts
CHANGED
|
@@ -26,19 +26,19 @@
|
|
|
26
26
|
export { setConfig, getConfig, isConfigured, type DataLayerConfig, type Sportal365SportsConfig, type PayloadConfig, } from "./config";
|
|
27
27
|
export { getFootballMatch, getFootballMatchEvents, getFootballMatchLineups, getFootballMatchOdds, getFootballMatchStatistics, getFootballMatchCommentary, getFootballMatches, getFootballLivescore, getBasketballLivescore, getTennisLivescore, getFootballTeam, getFootballTeamSquad, getFootballCompetition, getFootballSeasonDetails, getFootballStandings, getFootballPlayerSeasonStatistics, getFootballPlayerSeasonStatisticsByPlayerIds, getFootballPlayerCareerStatistics, getFootballPlayerRecentStatistics, getTeamSeasonStatistics, getSportal365Standings, searchFootball, } from "./api/sportal365-sports";
|
|
28
28
|
export { search } from "./api/sportal365-sports";
|
|
29
|
-
export { getFansUnitedFootballCompetitions, getFansUnitedFootballCompetition, getFansUnitedFootballTeam, getFansUnitedFootballTeams, getFansUnitedFootballPlayer, getFansUnitedFootballPlayers, getFansUnitedFootballMatch, getFansUnitedFootballMatches, getFansUnitedFootballTeamNextMatch, getFansUnitedFootballTeamPreviousMatch, getFansUnitedFootballPlayerNextMatch, getFansUnitedFootballPlayerPreviousMatch, getFansUnitedFootballSearch, getFansUnitedSearchEntities, getFansUnitedEntityById, getFansUnitedEntitiesByIds, getFansUnitedCompetitions, getFansUnitedTeamsByCountry, getFansUnitedTeamsByCompetition, getFansUnitedCountries, getFansUnitedVenues, getFansUnitedSportsCompetition, } from "./api/fansunited";
|
|
29
|
+
export { getFansUnitedFootballCompetitions, getFansUnitedFootballCompetition, getFansUnitedFootballTeam, getFansUnitedFootballTeams, getFansUnitedFootballPlayer, getFansUnitedFootballPlayers, getFansUnitedFootballMatch, getFansUnitedFootballMatches, getFansUnitedFootballTeamNextMatch, getFansUnitedFootballTeamPreviousMatch, getFansUnitedFootballPlayerNextMatch, getFansUnitedFootballPlayerPreviousMatch, getFansUnitedFootballSearch, getFansUnitedSearchEntities, getFansUnitedEntityById, getFansUnitedEntitiesByIds, getFansUnitedCompetitions, getFansUnitedTeamsByCountry, getFansUnitedTeamsByCompetition, getFansUnitedCountries, getFansUnitedVenues, getFansUnitedSportsCompetition, getFansUnitedSportsCompetitionHydrated, getFansUnitedSportsLivescore, getFansUnitedSportsLivescoreHydrated, } from "./api/fansunited";
|
|
30
30
|
export { getLeaderboardMatches, getLeaderboardTeamMatches, getLeaderboardTemplate, getBatchMatchOdds, } from "./api/fansunited-sdk";
|
|
31
31
|
export type { GetBatchMatchOddsOptions } from "./api/fansunited-sdk";
|
|
32
|
-
export type { GetFootballCompetitionsOptions, GetFootballCompetitionOptions as GetFansUnitedFootballCompetitionOptions, GetFootballTeamOptions as GetFansUnitedFootballTeamOptions, GetFootballTeamsOptions as GetFansUnitedFootballTeamsOptions, GetFootballPlayerOptions as GetFansUnitedFootballPlayerOptions, GetFootballPlayersOptions as GetFansUnitedFootballPlayersOptions, GetFootballMatchOptions as GetFansUnitedFootballMatchOptions, GetFootballMatchesOptions as GetFansUnitedFootballMatchesOptions, GetFootballSearchOptions as GetFansUnitedFootballSearchOptions, SearchEntityType as FansUnitedSearchEntityType, FansUnitedFootballSearchResult, SearchMode, EntityContentType, EntitySearchOptions, GetEntityByIdOptions, GetEntitiesByIdsOptions, GetCompetitionsOptions, GetTeamsByCountryOptions, GetTeamsByCompetitionOptions, GetCountriesOptions, GetVenuesOptions, RawSearchEntity, SearchEntityResult, SearchEntitySummary, EntitySearchResponse, PaginationMeta, PaginatedResult, GetSportsCompetitionOptions, } from "./api/fansunited";
|
|
32
|
+
export type { GetFootballCompetitionsOptions, GetFootballCompetitionOptions as GetFansUnitedFootballCompetitionOptions, GetFootballTeamOptions as GetFansUnitedFootballTeamOptions, GetFootballTeamsOptions as GetFansUnitedFootballTeamsOptions, GetFootballPlayerOptions as GetFansUnitedFootballPlayerOptions, GetFootballPlayersOptions as GetFansUnitedFootballPlayersOptions, GetFootballMatchOptions as GetFansUnitedFootballMatchOptions, GetFootballMatchesOptions as GetFansUnitedFootballMatchesOptions, GetFootballSearchOptions as GetFansUnitedFootballSearchOptions, SearchEntityType as FansUnitedSearchEntityType, FansUnitedFootballSearchResult, SearchMode, EntityContentType, EntitySearchOptions, GetEntityByIdOptions, GetEntitiesByIdsOptions, GetCompetitionsOptions, GetTeamsByCountryOptions, GetTeamsByCompetitionOptions, GetCountriesOptions, GetVenuesOptions, RawSearchEntity, SearchEntityResult, SearchEntitySummary, EntitySearchResponse, PaginationMeta, PaginatedResult, GetSportsCompetitionOptions, GetSportsCompetitionHydratedOptions, GetSportsLivescoreOptions, GetSportsLivescoreHydratedOptions, FUSportsCompetitionHydrated, FUSportsLivescoreHydrated, } from "./api/fansunited";
|
|
33
33
|
export { getProviderRefId, addProviderRef, toProviderRefArray } from "./api/sportal365-sports/shared";
|
|
34
34
|
export type { GetFootballMatchOptions, GetFootballMatchEventsOptions, GetFootballMatchLineupsOptions, GetFootballMatchOddsOptions, GetFootballMatchStatisticsOptions, GetFootballMatchCommentaryOptions, GetFootballMatchesOptions, GetFootballLivescoreOptions, GetBasketballLivescoreOptions, BasketballMatchStatusType, GetTennisLivescoreOptions, TennisMatchStatusType, TennisMatchType, TennisGender, GetFootballTeamOptions, GetFootballTeamSquadOptions, GetFootballCompetitionOptions, GetFootballSeasonDetailsOptions, GetFootballStandingsOptions, GetFootballPlayerSeasonStatisticsOptions, GetFootballPlayerSeasonStatisticsByPlayerIdsOptions, GetFootballPlayerCareerStatisticsOptions, GetFootballPlayerRecentStatisticsOptions, GetTeamSeasonStatisticsOptions, GetSportal365StandingsOptions, StandingsSport, StandingsCoverageType, GetFootballSearchOptions, SearchEntityType, GetSportsSearchOptions, SportType, SportsSearchEntityType, FootballStandingsExpandOption, OddType, ScopeType, OddFormat, MarketType, OptionalMatchData, MatchStatusType, SortDirection, SelectionFilter, NextCacheOptions, } from "./api/sportal365-sports";
|
|
35
|
-
export type { FUSportsCountry, FUSportsCity, FUSportsCompetitor, FUSportsCompetitorBase, FUSportsTeamCompetitor, FUSportsPlayerCompetitor, FUSportsPairCompetitor, FUSportsShirtColor, FUSportsSeason, FUSportsRound, FUSportsStage, FUSportsSeasonDetails, FUSportsCompetition, FUSportsVenue, FUSportsOfficial, FUSportsMember, FUSportsMatchMember, FUSportsCoach, FUSportsRelated, FUSportsSocials, FUBranding, FUSportsCompetitionSummary, FUSportsTeamSummary, FUSportsPlayerSummary, FUSportsCountrySummary, FUSportsVenueSummary, FUSportsCoachSummary, FUSportsMatchScore, FUSportsMatchStatus, FUSportsMatchTiming, FUSportsMatchWinner, FUSportsMatch, FUSportsMatchSimple, FUSportsFootballEventType, FUSportsOtherEventType, FUSportsMatchEvent, FUSportsLineupMember, FUSportsCompetitorLineup, FUSportsMatchLineups, FUSportsStatisticEntry, FUSportsCompetitorStatistics, FUSportsMatchStatistics, FUSportsPlayerStatistic, FUSportsPlayerSeasonStatistics, FUSportsCareerTournamentSeason, FUSportsCareerTeam, FUSportsPlayerCareerStatisticsEntry, FUSportsPlayerCareerStatistics, FUSportsPlayerRecentStatistics, FUSportsTeamSeasonStatistics, FUSportsBettingOperatorBranding, FUSportsBettingOperator, FUSportsOddSelection, FUSportsMarketPeriod, FUSportsMarketType, FUSportsOddMarket, FUSportsBettingOperatorOdds, FUSportsMatchOdds, FUSportsCommentaryDetail, FUSportsCommentaryMeta, FUSportsCommentaryItem, FUSportsStandingEntry, FUSportsCompetitorForm, FUSportsStandingLegendItem, FUSportsStandingsMetadata, FUSportsStandings, FUSportsStandingsTable, FUSportsCompetitionStageType, FUSportsCompetitionStandingStat, FUSportsCompetitionStandingEntry, FUSportsCompetitionStandingGroup, FUSportsCompetitionStage, FUSportsCompetitionSeason, FUSportsCompetitionMeta, FUSportsCompetitionDetails, FUSportsSquadMember, FUSportsSquad, FUSportsSearchResultBase, FUSportsTournamentSearchResult, FUSportsPlayerSearchResult, FUSportsTeamSearchResult, FUSportsPresidentSearchResult, FUSportsVenueSearchResult, FUSportsCoachSearchResult, FUSportsSearchResult, FUSportsSearchResults, FUSportsTranslation, FUSportsDisplayAsset, FUSportsSportsSearchResultBase, FUSportsSportsSearchCountry, FUSportsTeamSportsSearchResult, FUSportsCitySportsSearchResult, FUSportsHorseSportsSearchResult, FUSportsPlayerSportsSearchResult, FUSportsCoachSportsSearchResult, FUSportsRefereeSportsSearchResult, FUSportsVenueSportsSearchResult, FUSportsTournamentSportsSearchResult, FUSportsMatchSportsSearchResult, FUSportsCountrySportsSearchResult, FUSportsSportsSearchResult, FUSportsSportsSearchResults, } from "./types/canonical";
|
|
35
|
+
export type { FUSportsCountry, FUSportsCity, FUSportsCompetitor, FUSportsCompetitorBase, FUSportsTeamCompetitor, FUSportsPlayerCompetitor, FUSportsPairCompetitor, FUSportsShirtColor, FUSportsSeason, FUSportsRound, FUSportsStage, FUSportsSeasonDetails, FUSportsCompetition, FUSportsVenue, FUSportsOfficial, FUSportsMember, FUSportsMatchMember, FUSportsCoach, FUSportsRelated, FUSportsSocials, FUBranding, FUSportsCompetitionSummary, FUSportsTeamSummary, FUSportsPlayerSummary, FUSportsCountrySummary, FUSportsVenueSummary, FUSportsCoachSummary, FUSportsMatchScore, FUSportsMatchStatus, FUSportsMatchTiming, FUSportsMatchWinner, FUSportsMatch, FUSportsMatchSimple, FUSportsFootballEventType, FUSportsOtherEventType, FUSportsMatchEvent, FUSportsLineupMember, FUSportsCompetitorLineup, FUSportsMatchLineups, FUSportsStatisticEntry, FUSportsCompetitorStatistics, FUSportsMatchStatistics, FUSportsPlayerStatistic, FUSportsPlayerSeasonStatistics, FUSportsCareerTournamentSeason, FUSportsCareerTeam, FUSportsPlayerCareerStatisticsEntry, FUSportsPlayerCareerStatistics, FUSportsPlayerRecentStatistics, FUSportsTeamSeasonStatistics, FUSportsBettingOperatorBranding, FUSportsBettingOperator, FUSportsOddSelection, FUSportsMarketPeriod, FUSportsMarketType, FUSportsOddMarket, FUSportsBettingOperatorOdds, FUSportsMatchOdds, FUSportsCommentaryDetail, FUSportsCommentaryMeta, FUSportsCommentaryItem, FUSportsStandingEntry, FUSportsCompetitorForm, FUSportsStandingLegendItem, FUSportsStandingsMetadata, FUSportsStandings, FUSportsStandingsTable, FUSportsCompetitionStageType, FUSportsCompetitionStandingStat, FUSportsCompetitionStandingEntry, FUSportsCompetitionStandingGroup, FUSportsCompetitionStage, FUSportsCompetitionSeason, FUSportsCompetitionMeta, FUSportsCompetitionDetails, FUSportsLivescoreSport, FUSportsLivescoreEventStatus, FUSportsLivescoreStatus, FUSportsLivescoreScore, FUSportsLivescoreCompetitor, FUSportsLivescoreEvent, FUSportsLivescoreCompetitionGroup, FUSportsLivescoreMeta, FUSportsLivescore, FUSportsSquadMember, FUSportsSquad, FUSportsSearchResultBase, FUSportsTournamentSearchResult, FUSportsPlayerSearchResult, FUSportsTeamSearchResult, FUSportsPresidentSearchResult, FUSportsVenueSearchResult, FUSportsCoachSearchResult, FUSportsSearchResult, FUSportsSearchResults, FUSportsTranslation, FUSportsDisplayAsset, FUSportsSportsSearchResultBase, FUSportsSportsSearchCountry, FUSportsTeamSportsSearchResult, FUSportsCitySportsSearchResult, FUSportsHorseSportsSearchResult, FUSportsPlayerSportsSearchResult, FUSportsCoachSportsSearchResult, FUSportsRefereeSportsSearchResult, FUSportsVenueSportsSearchResult, FUSportsTournamentSportsSearchResult, FUSportsMatchSportsSearchResult, FUSportsCountrySportsSearchResult, FUSportsSportsSearchResult, FUSportsSportsSearchResults, } from "./types/canonical";
|
|
36
36
|
export { getActiveSeason, getTeamNextMatch, getTeamPreviousMatch, getTeamUpcomingMatches, getTeamFinishedMatches, getTeamResultsTable, getTeamTopScorers, getTeamTopAssisters, getTeamTopAppearances, getTeamTopMinutesPlayed, getTeamTopCleanSheets, getTeamMostDecorated, aggregatePlayerStatisticsFromMatches, getTopScorersFromMatches, getTopAssistersFromMatches, getMostCardedPlayersFromMatches, getTopGoalContributorsFromMatches, getAllPlayerStatisticsFromMatches, useTopScorersFromMatches, useTopAssistersFromMatches, useMostCardedPlayersFromMatches, useTopGoalContributorsFromMatches, useAllPlayerStatisticsFromMatches, usePlayerStatisticsMap, usePlayerStatistics, } from "./helpers";
|
|
37
37
|
export type { TeamResultTableResult, TeamResultTableRow, TeamResultTable } from "./helpers";
|
|
38
38
|
export { calcPercentage, calcAverage, formatStatValue, formatWithAverage, formatAsAverage, formatPossessionPercentage, analyzeMatch, isTeamHome, isTeamAway, isTeamInMatch, isMatchFinished, getTeamScore, getOpponentScore, calculateH2HStats, getRecentH2HMeetings, getTeamHomeAwayStats, getTeamOverUnderStats, getMatchResult, getTeamGoalStats, calculateStreak, getTeamStreaks, getTeamStreaksComparison, streakFilters, getTeamsCommonOpponents, } from "./utilities/stats";
|
|
39
39
|
export type { MatchAnalysis, MatchResult, MatchFilter, VenueFilter, H2HStats, H2HMatch, HomeVsAwayStats, TeamOverUnderStats, TeamGoalStats, GoalStatRow, StreakData, StreakRow, TeamOpponentResults, TeamsResultsRow, } from "./utilities/stats";
|
|
40
40
|
export { GOAL_EVENT_TYPES } from "./utilities/stats";
|
|
41
|
-
export { initL2, cached, cachedBatch, invalidate } from "./cache";
|
|
42
|
-
export type { CacheEntry, EntityCacheConfig, EntityType, L2Store } from "./cache";
|
|
41
|
+
export { initL2, cached, cachedBatch, invalidate, setEntityTTL, getEntityTTL, CACHE_KEY_PREFIX } from "./cache";
|
|
42
|
+
export type { CacheEntry, EntityCacheConfig, EntityType, L2Store, MaybePromise } from "./cache";
|
|
43
43
|
export { memoryStore } from "./cache/memory-store";
|
|
44
44
|
//# sourceMappingURL=index.d.ts.map
|
package/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/lib/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAGH,OAAO,EACH,SAAS,EACT,SAAS,EACT,YAAY,EACZ,KAAK,eAAe,EACpB,KAAK,sBAAsB,EAC3B,KAAK,aAAa,GACrB,MAAM,UAAU,CAAC;AAGlB,OAAO,EACH,gBAAgB,EAChB,sBAAsB,EACtB,uBAAuB,EACvB,oBAAoB,EACpB,0BAA0B,EAC1B,0BAA0B,EAC1B,kBAAkB,EAClB,oBAAoB,EACpB,sBAAsB,EACtB,kBAAkB,EAClB,eAAe,EACf,oBAAoB,EACpB,sBAAsB,EACtB,wBAAwB,EACxB,oBAAoB,EACpB,iCAAiC,EACjC,4CAA4C,EAC5C,iCAAiC,EACjC,iCAAiC,EACjC,uBAAuB,EACvB,sBAAsB,EACtB,cAAc,GACjB,MAAM,yBAAyB,CAAC;AAGjC,OAAO,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AAGjD,OAAO,EACH,iCAAiC,EACjC,gCAAgC,EAChC,yBAAyB,EACzB,0BAA0B,EAC1B,2BAA2B,EAC3B,4BAA4B,EAC5B,0BAA0B,EAC1B,4BAA4B,EAC5B,kCAAkC,EAClC,sCAAsC,EACtC,oCAAoC,EACpC,wCAAwC,EACxC,2BAA2B,EAE3B,2BAA2B,EAC3B,uBAAuB,EACvB,0BAA0B,EAC1B,yBAAyB,EACzB,2BAA2B,EAC3B,+BAA+B,EAC/B,sBAAsB,EACtB,mBAAmB,EAEnB,8BAA8B,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/lib/index.ts"],"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.
|
|
7
|
+
"version": "0.17.0",
|
|
8
8
|
"type": "module",
|
|
9
9
|
"sideEffects": false,
|
|
10
10
|
"module": "./fansunited-data-layer.js",
|
|
@@ -48,6 +48,7 @@
|
|
|
48
48
|
"fansunited-sdk-esm": "^1.119.2",
|
|
49
49
|
"fansunited-sports-ui": "^0.0.1",
|
|
50
50
|
"globals": "^16.5.0",
|
|
51
|
+
"ioredis": "^5.11.1",
|
|
51
52
|
"prettier": "^3.7.3",
|
|
52
53
|
"react": "^19.2.0",
|
|
53
54
|
"react-dom": "^19.2.0",
|
|
@@ -12,6 +12,7 @@ export type { FUSportsBettingOperatorBranding, FUSportsBettingOperator, FUSports
|
|
|
12
12
|
export type { FUSportsCommentaryDetail, FUSportsCommentaryMeta, FUSportsCommentaryItem } from "./commentary.types";
|
|
13
13
|
export type { FUSportsStandingEntry, FUSportsCompetitorForm, FUSportsStandingLegendItem, FUSportsStandingsMetadata, FUSportsStandings, FUSportsStandingsTable, } from "./standing.types";
|
|
14
14
|
export type { FUSportsCompetitionStageType, FUSportsCompetitionStandingStat, FUSportsCompetitionStandingEntry, FUSportsCompetitionStandingGroup, FUSportsCompetitionStage, FUSportsCompetitionSeason, FUSportsCompetitionMeta, FUSportsCompetitionDetails, } from "./sports-competition.types";
|
|
15
|
+
export type { FUSportsLivescoreSport, FUSportsLivescoreEventStatus, FUSportsLivescoreStatus, FUSportsLivescoreScore, FUSportsLivescoreCompetitor, FUSportsLivescoreEvent, FUSportsLivescoreCompetitionGroup, FUSportsLivescoreMeta, FUSportsLivescore, } from "./sports-livescore.types";
|
|
15
16
|
export type { FUSportsSquadMember, FUSportsSquad } from "./squad.types";
|
|
16
17
|
export type { FUSportsSearchResultBase, FUSportsTournamentSearchResult, FUSportsPlayerSearchResult, FUSportsTeamSearchResult, FUSportsPresidentSearchResult, FUSportsVenueSearchResult, FUSportsCoachSearchResult, FUSportsSearchResult, FUSportsSearchResults, } from "./search.types";
|
|
17
18
|
export type { FUSportsTranslation, FUSportsDisplayAsset, FUSportsSportsSearchResultBase, FUSportsSportsSearchCountry, FUSportsTeamSportsSearchResult, FUSportsCitySportsSearchResult, FUSportsHorseSportsSearchResult, FUSportsPlayerSportsSearchResult, FUSportsCoachSportsSearchResult, FUSportsRefereeSportsSearchResult, FUSportsVenueSportsSearchResult, FUSportsTournamentSportsSearchResult, FUSportsMatchSportsSearchResult, FUSportsCountrySportsSearchResult, FUSportsSportsSearchResult, FUSportsSportsSearchResults, } from "./sports-search.types";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/lib/types/canonical/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,YAAY,EACR,eAAe,EACf,YAAY,EACZ,kBAAkB,EAClB,sBAAsB,EACtB,sBAAsB,EACtB,wBAAwB,EACxB,sBAAsB,EACtB,kBAAkB,EAClB,cAAc,EACd,aAAa,EACb,aAAa,EACb,qBAAqB,EACrB,mBAAmB,EACnB,aAAa,EACb,gBAAgB,EAChB,cAAc,EACd,mBAAmB,EACnB,wBAAwB,EACxB,aAAa,EACb,eAAe,EACf,eAAe,EACf,UAAU,EAEV,0BAA0B,EAC1B,mBAAmB,EACnB,qBAAqB,EACrB,sBAAsB,EACtB,oBAAoB,EACpB,oBAAoB,GACvB,MAAM,cAAc,CAAC;AAGtB,YAAY,EACR,kBAAkB,EAClB,mBAAmB,EACnB,mBAAmB,EACnB,mBAAmB,EACnB,aAAa,EACb,mBAAmB,GACtB,MAAM,eAAe,CAAC;AAGvB,YAAY,EAAE,yBAAyB,EAAE,sBAAsB,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AAG3G,YAAY,EAAE,oBAAoB,EAAE,wBAAwB,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAG3G,YAAY,EACR,mBAAmB,EACnB,sBAAsB,EACtB,4BAA4B,EAC5B,uBAAuB,EACvB,uBAAuB,EACvB,8BAA8B,EAC9B,8BAA8B,EAC9B,kBAAkB,EAClB,mCAAmC,EACnC,8BAA8B,EAC9B,8BAA8B,EAC9B,4BAA4B,GAC/B,MAAM,oBAAoB,CAAC;AAG5B,YAAY,EACR,+BAA+B,EAC/B,uBAAuB,EACvB,oBAAoB,EACpB,oBAAoB,EACpB,kBAAkB,EAClB,iBAAiB,EACjB,2BAA2B,EAC3B,iBAAiB,EACjB,sBAAsB,GACzB,MAAM,cAAc,CAAC;AAGtB,YAAY,EAAE,wBAAwB,EAAE,sBAAsB,EAAE,sBAAsB,EAAE,MAAM,oBAAoB,CAAC;AAGnH,YAAY,EACR,qBAAqB,EACrB,sBAAsB,EACtB,0BAA0B,EAC1B,yBAAyB,EACzB,iBAAiB,EACjB,sBAAsB,GACzB,MAAM,kBAAkB,CAAC;AAG1B,YAAY,EACR,4BAA4B,EAC5B,+BAA+B,EAC/B,gCAAgC,EAChC,gCAAgC,EAChC,wBAAwB,EACxB,yBAAyB,EACzB,uBAAuB,EACvB,0BAA0B,GAC7B,MAAM,4BAA4B,CAAC;AAGpC,YAAY,EAAE,mBAAmB,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAGxE,YAAY,EACR,wBAAwB,EACxB,8BAA8B,EAC9B,0BAA0B,EAC1B,wBAAwB,EACxB,6BAA6B,EAC7B,yBAAyB,EACzB,yBAAyB,EACzB,oBAAoB,EACpB,qBAAqB,GACxB,MAAM,gBAAgB,CAAC;AAGxB,YAAY,EACR,mBAAmB,EACnB,oBAAoB,EACpB,8BAA8B,EAC9B,2BAA2B,EAC3B,8BAA8B,EAC9B,8BAA8B,EAC9B,+BAA+B,EAC/B,gCAAgC,EAChC,+BAA+B,EAC/B,iCAAiC,EACjC,+BAA+B,EAC/B,oCAAoC,EACpC,+BAA+B,EAC/B,iCAAiC,EACjC,0BAA0B,EAC1B,2BAA2B,GAC9B,MAAM,uBAAuB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/lib/types/canonical/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,YAAY,EACR,eAAe,EACf,YAAY,EACZ,kBAAkB,EAClB,sBAAsB,EACtB,sBAAsB,EACtB,wBAAwB,EACxB,sBAAsB,EACtB,kBAAkB,EAClB,cAAc,EACd,aAAa,EACb,aAAa,EACb,qBAAqB,EACrB,mBAAmB,EACnB,aAAa,EACb,gBAAgB,EAChB,cAAc,EACd,mBAAmB,EACnB,wBAAwB,EACxB,aAAa,EACb,eAAe,EACf,eAAe,EACf,UAAU,EAEV,0BAA0B,EAC1B,mBAAmB,EACnB,qBAAqB,EACrB,sBAAsB,EACtB,oBAAoB,EACpB,oBAAoB,GACvB,MAAM,cAAc,CAAC;AAGtB,YAAY,EACR,kBAAkB,EAClB,mBAAmB,EACnB,mBAAmB,EACnB,mBAAmB,EACnB,aAAa,EACb,mBAAmB,GACtB,MAAM,eAAe,CAAC;AAGvB,YAAY,EAAE,yBAAyB,EAAE,sBAAsB,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AAG3G,YAAY,EAAE,oBAAoB,EAAE,wBAAwB,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAG3G,YAAY,EACR,mBAAmB,EACnB,sBAAsB,EACtB,4BAA4B,EAC5B,uBAAuB,EACvB,uBAAuB,EACvB,8BAA8B,EAC9B,8BAA8B,EAC9B,kBAAkB,EAClB,mCAAmC,EACnC,8BAA8B,EAC9B,8BAA8B,EAC9B,4BAA4B,GAC/B,MAAM,oBAAoB,CAAC;AAG5B,YAAY,EACR,+BAA+B,EAC/B,uBAAuB,EACvB,oBAAoB,EACpB,oBAAoB,EACpB,kBAAkB,EAClB,iBAAiB,EACjB,2BAA2B,EAC3B,iBAAiB,EACjB,sBAAsB,GACzB,MAAM,cAAc,CAAC;AAGtB,YAAY,EAAE,wBAAwB,EAAE,sBAAsB,EAAE,sBAAsB,EAAE,MAAM,oBAAoB,CAAC;AAGnH,YAAY,EACR,qBAAqB,EACrB,sBAAsB,EACtB,0BAA0B,EAC1B,yBAAyB,EACzB,iBAAiB,EACjB,sBAAsB,GACzB,MAAM,kBAAkB,CAAC;AAG1B,YAAY,EACR,4BAA4B,EAC5B,+BAA+B,EAC/B,gCAAgC,EAChC,gCAAgC,EAChC,wBAAwB,EACxB,yBAAyB,EACzB,uBAAuB,EACvB,0BAA0B,GAC7B,MAAM,4BAA4B,CAAC;AAGpC,YAAY,EACR,sBAAsB,EACtB,4BAA4B,EAC5B,uBAAuB,EACvB,sBAAsB,EACtB,2BAA2B,EAC3B,sBAAsB,EACtB,iCAAiC,EACjC,qBAAqB,EACrB,iBAAiB,GACpB,MAAM,0BAA0B,CAAC;AAGlC,YAAY,EAAE,mBAAmB,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAGxE,YAAY,EACR,wBAAwB,EACxB,8BAA8B,EAC9B,0BAA0B,EAC1B,wBAAwB,EACxB,6BAA6B,EAC7B,yBAAyB,EACzB,yBAAyB,EACzB,oBAAoB,EACpB,qBAAqB,GACxB,MAAM,gBAAgB,CAAC;AAGxB,YAAY,EACR,mBAAmB,EACnB,oBAAoB,EACpB,8BAA8B,EAC9B,2BAA2B,EAC3B,8BAA8B,EAC9B,8BAA8B,EAC9B,+BAA+B,EAC/B,gCAAgC,EAChC,+BAA+B,EAC/B,iCAAiC,EACjC,+BAA+B,EAC/B,oCAAoC,EACpC,+BAA+B,EAC/B,iCAAiC,EACjC,0BAA0B,EAC1B,2BAA2B,GAC9B,MAAM,uBAAuB,CAAC"}
|