···
+
* Analytics data type definitions
+
interface EndpointMetrics {
+
averageResponseTime: number;
+
interface StatusMetrics {
+
averageResponseTime: number;
+
averageResponseTime: number;
+
interface UserAgentMetrics {
+
interface LatencyPercentiles {
+
interface LatencyDistribution {
+
interface LatencyOverTimeMetrics {
+
averageResponseTime: number;
+
interface LatencyAnalytics {
+
percentiles: LatencyPercentiles;
+
distribution: Array<LatencyDistribution>;
+
slowestEndpoints: Array<EndpointMetrics>;
+
latencyOverTime: Array<LatencyOverTimeMetrics>;
+
interface PerformanceMetrics {
+
interface PeakTraffic {
+
peakDayRequests: number;
+
interface DashboardMetrics {
+
totalWithStats: number;
+
interface TrafficOverview {
+
routes: Record<string, number>;
+
* Analytics method return types
+
interface FullAnalyticsData {
+
requestsByEndpoint: Array<EndpointMetrics>;
+
requestsByStatus: Array<StatusMetrics>;
+
requestsByDay: Array<DayMetrics>;
+
averageResponseTime: number | null;
+
topUserAgents: Array<UserAgentMetrics>;
+
latencyAnalytics: LatencyAnalytics;
+
performanceMetrics: PerformanceMetrics;
+
peakTraffic: PeakTraffic;
+
dashboardMetrics: DashboardMetrics;
+
trafficOverview: Array<TrafficOverview>;
+
interface EssentialStatsData {
+
averageResponseTime: number | null;
+
requestsByDay: Array<DayMetrics>;
+
latencyOverTime: Array<LatencyOverTimeMetrics>;
+
type UserAgentData = Array<UserAgentMetrics>;
+
* Discriminated union for all analytics cache data types
+
type AnalyticsCacheData =
+
| { type: 'analytics'; data: FullAnalyticsData }
+
| { type: 'essential'; data: EssentialStatsData }
+
| { type: 'charts'; data: ChartData }
+
| { type: 'useragents'; data: UserAgentData };
+
* Type-safe analytics cache entry
+
interface AnalyticsCacheEntry {
+
data: AnalyticsCacheData;
+
* Type guard functions for cache data
+
function isAnalyticsData(data: AnalyticsCacheData): data is { type: 'analytics'; data: FullAnalyticsData } {
+
return data.type === 'analytics';
+
function isEssentialStatsData(data: AnalyticsCacheData): data is { type: 'essential'; data: EssentialStatsData } {
+
return data.type === 'essential';
+
function isChartData(data: AnalyticsCacheData): data is { type: 'charts'; data: ChartData } {
+
return data.type === 'charts';
+
function isUserAgentData(data: AnalyticsCacheData): data is { type: 'useragents'; data: UserAgentData } {
+
return data.type === 'useragents';
+
* Type-safe cache helper methods
+
private cache: Map<string, AnalyticsCacheEntry>;
+
private cacheTTL: number;
+
private maxCacheSize: number;
+
constructor(cacheTTL: number = 30000, maxCacheSize: number = 10) {
+
this.cache = new Map();
+
this.cacheTTL = cacheTTL;
+
this.maxCacheSize = maxCacheSize;
+
* Get cached analytics data with type safety
+
getAnalyticsData(key: string): FullAnalyticsData | null {
+
const cached = this.cache.get(key);
+
const now = Date.now();
+
if (cached && now - cached.timestamp < this.cacheTTL && isAnalyticsData(cached.data)) {
+
return cached.data.data;
+
* Get cached essential stats data with type safety
+
getEssentialStatsData(key: string): EssentialStatsData | null {
+
const cached = this.cache.get(key);
+
const now = Date.now();
+
if (cached && now - cached.timestamp < this.cacheTTL && isEssentialStatsData(cached.data)) {
+
return cached.data.data;
+
* Get cached chart data with type safety
+
getChartData(key: string): ChartData | null {
+
const cached = this.cache.get(key);
+
const now = Date.now();
+
if (cached && now - cached.timestamp < this.cacheTTL && isChartData(cached.data)) {
+
return cached.data.data;
+
* Get cached user agent data with type safety
+
getUserAgentData(key: string): UserAgentData | null {
+
const cached = this.cache.get(key);
+
const now = Date.now();
+
if (cached && now - cached.timestamp < this.cacheTTL && isUserAgentData(cached.data)) {
+
return cached.data.data;
+
* Set analytics data in cache with type safety
+
setAnalyticsData(key: string, data: FullAnalyticsData): void {
+
this.setCacheEntry(key, { type: 'analytics', data });
+
* Set essential stats data in cache with type safety
+
setEssentialStatsData(key: string, data: EssentialStatsData): void {
+
this.setCacheEntry(key, { type: 'essential', data });
+
* Set chart data in cache with type safety
+
setChartData(key: string, data: ChartData): void {
+
this.setCacheEntry(key, { type: 'charts', data });
+
* Set user agent data in cache with type safety
+
setUserAgentData(key: string, data: UserAgentData): void {
+
this.setCacheEntry(key, { type: 'useragents', data });
+
* Internal method to set cache entry and manage cache size
+
private setCacheEntry(key: string, data: AnalyticsCacheData): void {
+
// Clean up old cache entries
+
if (this.cache.size > this.maxCacheSize) {
+
const keys = Array.from(this.cache.keys());
+
const oldestKey = keys[0];
+
this.cache.delete(oldestKey);
* @fileoverview This file contains the Cache class for storing user and emoji data with automatic expiration. To use the module in your project, import the default export and create a new instance of the Cache class. The class provides methods for inserting and retrieving user and emoji data from the cache. The cache automatically purges expired items every hour.
···
private defaultExpiration: number; // in hours
private onEmojiExpired?: () => void;
+
private typedAnalyticsCache: AnalyticsCache; // Type-safe analytics cache helper
// Background user update queue to avoid Slack API limits
private userUpdateQueue: Set<string> = new Set();
···
this.db = new Database(dbPath);
this.defaultExpiration = defaultExpirationHours;
this.onEmojiExpired = onEmojiExpired;
+
// Initialize type-safe analytics cache
+
this.typedAnalyticsCache = new AnalyticsCache();
this.setupPurgeSchedule();
···
···
const cacheKey = `analytics_${days}`;
+
const cached = this.typedAnalyticsCache.getAnalyticsData(cacheKey);
const cutoffTime = Date.now() - days * 24 * 60 * 60 * 1000;
···
const dataRequests = requestsByEndpoint
.filter((e) => e.endpoint === "User Data" || e.endpoint === "Emoji Data")
.reduce((sum, e) => sum + e.count, 0);
redirectRequests + dataRequests > 0
? (redirectRequests / (redirectRequests + dataRequests)) * 100
···
peakHour: peakHourData?.hour || "N/A",
···
+
this.typedAnalyticsCache.setAnalyticsData(cacheKey, result);
···
const cacheKey = `essential_${days}`;
+
const cached = this.typedAnalyticsCache.getEssentialStatsData(cacheKey);
const cutoffTime = Date.now() - days * 24 * 60 * 60 * 1000;
···
+
this.typedAnalyticsCache.setEssentialStatsData(cacheKey, result);
···
const cacheKey = `charts_${days}`;
+
const cached = this.typedAnalyticsCache.getChartData(cacheKey);
const cutoffTime = Date.now() - days * 24 * 60 * 60 * 1000;
···
+
this.typedAnalyticsCache.setChartData(cacheKey, result);
···
): Promise<Array<{ userAgent: string; count: number }>> {
const cacheKey = `useragents_${days}`;
+
const cached = this.typedAnalyticsCache.getUserAgentData(cacheKey);
const cutoffTime = Date.now() - days * 24 * 60 * 60 * 1000;
···
.all(cutoffTime) as Array<{ userAgent: string; count: number }>;
+
this.typedAnalyticsCache.setUserAgentData(cacheKey, topUserAgents);