react-native-healthkit-bridge
Version:
A comprehensive React Native bridge for Apple HealthKit with TypeScript support, advanced authorization, and flexible data queries
160 lines (130 loc) • 3.69 kB
text/typescript
import { HEALTHKIT_CONFIG } from '../config/healthkit.config';
export interface CachedData<T = any> {
data: T;
timestamp: number;
ttl: number;
key: string;
}
export interface CacheStats {
hits: number;
misses: number;
size: number;
keys: string[];
hitRate: number;
}
export class HealthKitCache {
private static instance: HealthKitCache;
private cache = new Map<string, CachedData>();
private stats = { hits: 0, misses: 0 };
private constructor() {
this.startCleanupInterval();
}
static getInstance(): HealthKitCache {
if (!HealthKitCache.instance) {
HealthKitCache.instance = new HealthKitCache();
}
return HealthKitCache.instance;
}
set<T>(key: string, data: T, ttl: number = HEALTHKIT_CONFIG.CACHE_TTL): void {
if (!HEALTHKIT_CONFIG.CACHE_ENABLED) return;
// Check cache size limit
if (this.cache.size >= HEALTHKIT_CONFIG.CACHE_MAX_SIZE) {
this.evictOldest();
}
const cachedData: CachedData<T> = {
data,
timestamp: Date.now(),
ttl,
key
};
this.cache.set(key, cachedData);
}
get<T>(key: string): T | null {
if (!HEALTHKIT_CONFIG.CACHE_ENABLED) return null;
const cachedData = this.cache.get(key) as CachedData<T>;
if (!cachedData) {
this.stats.misses++;
return null;
}
// Check if expired
if (Date.now() - cachedData.timestamp > cachedData.ttl) {
this.cache.delete(key);
this.stats.misses++;
return null;
}
this.stats.hits++;
return cachedData.data;
}
has(key: string): boolean {
if (!HEALTHKIT_CONFIG.CACHE_ENABLED) return false;
const cachedData = this.cache.get(key);
if (!cachedData) return false;
// Check if expired
if (Date.now() - cachedData.timestamp > cachedData.ttl) {
this.cache.delete(key);
return false;
}
return true;
}
delete(key: string): boolean {
return this.cache.delete(key);
}
clear(): void {
this.cache.clear();
this.stats = { hits: 0, misses: 0 };
}
invalidatePattern(pattern: string): void {
const regex = new RegExp(pattern);
for (const key of this.cache.keys()) {
if (regex.test(key)) {
this.cache.delete(key);
}
}
}
getStats(): CacheStats {
const keys = Array.from(this.cache.keys());
const totalRequests = this.stats.hits + this.stats.misses;
const hitRate = totalRequests > 0 ? this.stats.hits / totalRequests : 0;
return {
hits: this.stats.hits,
misses: this.stats.misses,
size: this.cache.size,
keys,
hitRate
};
}
private evictOldest(): void {
let oldestKey: string | null = null;
let oldestTime = Date.now();
for (const [key, data] of this.cache.entries()) {
if (data.timestamp < oldestTime) {
oldestTime = data.timestamp;
oldestKey = key;
}
}
if (oldestKey) {
this.cache.delete(oldestKey);
}
}
private startCleanupInterval(): void {
// Clean up expired entries every 5 minutes
setInterval(() => {
const now = Date.now();
for (const [key, data] of this.cache.entries()) {
if (now - data.timestamp > data.ttl) {
this.cache.delete(key);
}
}
}, 5 * 60 * 1000);
}
// Helper methods for common cache keys
static createKey(operation: string, ...params: any[]): string {
return `${operation}:${params.join(':')}`;
}
static createAuthKey(types: string[]): string {
return `auth:${types.sort().join(',')}`;
}
static createQueryKey(identifier: string, unit: string, days: number): string {
return `query:${identifier}:${unit}:${days}`;
}
}