import { AppServices } from './app-services'; import { NoopAnalyticsService } from './analytics/noop-analytics-service'; import { UmamiAnalyticsService } from './analytics/umami-analytics-service'; import { MemoryCacheService } from './cache/memory-cache-service'; import { GlitchtipErrorReportingService } from './errors/glitchtip-error-reporting-service'; import { NoopErrorReportingService } from './errors/noop-error-reporting-service'; import { NoopLoggerService } from './logging/noop-logger-service'; import { PinoLoggerService } from './logging/pino-logger-service'; import { NoopNotificationService } from './notifications/gotify-notification-service'; import { config, getMaskedConfig } from '../config'; declare global { var __appServices: AppServices | undefined; } let singleton: AppServices | undefined; /** * Get the application services singleton. */ export function getAppServices(): AppServices { // Return cached instance if available if (typeof window === 'undefined' && globalThis.__appServices) return globalThis.__appServices; if (singleton) return singleton; // Create logger first to log initialization const logger = typeof window === 'undefined' ? new PinoLoggerService('server') : new NoopLoggerService(); // Log initialization if (typeof window === 'undefined') { // Server-side logger.info('Initializing server application services', { environment: getMaskedConfig(), timestamp: new Date().toISOString(), }); } else { // Client-side logger.info('Initializing client application services', { environment: getMaskedConfig(), timestamp: new Date().toISOString(), }); } // Determine which services to enable based on environment variables const umamiEnabled = config.analytics.umami.enabled; const sentryEnabled = config.errors.glitchtip.enabled; logger.info('Service configuration', { umamiEnabled, sentryEnabled, isServer: typeof window === 'undefined', }); // Create analytics service (Umami or no-op) const analytics = umamiEnabled ? new UmamiAnalyticsService({ enabled: true }, logger) : new NoopAnalyticsService(); if (umamiEnabled) { logger.info('Umami analytics service initialized'); } else { logger.info('Noop analytics service initialized (analytics disabled)'); } // Create notification service const notifications = new NoopNotificationService(); logger.info('Notification service initialized (noop)'); // Create error reporting service (GlitchTip/Sentry or no-op) const errors = sentryEnabled ? new GlitchtipErrorReportingService( { enabled: true, dsn: config.errors.glitchtip.dsn, tracesSampleRate: 0.1, // Default to 10% sampling }, logger, notifications, ) : new NoopErrorReportingService(); if (sentryEnabled) { logger.info( `GlitchTip error reporting service initialized (${typeof window === 'undefined' ? 'server' : 'client'})`, ); } else { logger.info('Noop error reporting service initialized (error reporting disabled)'); } const cache = new MemoryCacheService(); logger.info('Memory cache service initialized'); logger.info('Pino logger service initialized', { name: typeof window === 'undefined' ? 'server' : 'client', level: config.logging.level, }); // Create and cache the singleton if (typeof window === 'undefined') { globalThis.__appServices = new AppServices(analytics, errors, cache, logger, notifications); return globalThis.__appServices; } singleton = new AppServices(analytics, errors, cache, logger, notifications); logger.info('All application services initialized successfully'); return singleton; }