diff --git a/src/nestjs/services/analytics.ts b/src/nestjs/services/analytics.ts deleted file mode 100644 index 9dca577..0000000 --- a/src/nestjs/services/analytics.ts +++ /dev/null @@ -1,198 +0,0 @@ -import { Injectable, Inject, OnModuleDestroy } from '@nestjs/common'; -import { ANALYTICS_OPTIONS, type AnalyticsModuleOptions } from '../module'; -import type { AnalyticsEvent } from '../types'; -import type { TrackEventPayload } from '../../types'; - -@Injectable() -export class AnalyticsService implements OnModuleDestroy { - private eventQueue: AnalyticsEvent[] = []; - private flushTimer: ReturnType | null = null; - - constructor( - @Inject(ANALYTICS_OPTIONS) private readonly options: AnalyticsModuleOptions - ) { - this.startFlushTimer(); - } - - async onModuleDestroy(): Promise { - if (this.flushTimer) { - clearInterval(this.flushTimer); - } - await this.flush(); - } - - /** - * Track an analytics event - */ - async track(event: AnalyticsEvent): Promise { - if (!this.options.enabled) { - return; - } - - const fullEvent: AnalyticsEvent = { - ...event, - timestamp: event.timestamp || new Date().toISOString(), - sessionId: event.sessionId || 'server', - }; - - if (this.options.debug) { - console.log('[Analytics] Track:', fullEvent); - } - - this.eventQueue.push(fullEvent); - - const maxSize = this.options.batch?.maxSize ?? 100; - if (this.eventQueue.length >= maxSize) { - await this.flush(); - } - } - - /** - * Track a page view event - */ - async trackPageView( - url: string, - options: { title?: string; referrer?: string; userId?: string } = {} - ): Promise { - const parsedUrl = new URL(url, 'http://localhost'); - const queryParams: Record = {}; - parsedUrl.searchParams.forEach((value, key) => { - queryParams[key] = value; - }); - await this.track({ - type: 'page_view', - userId: options.userId, - properties: { - url, - title: options.title, - referrer: options.referrer, - path: parsedUrl.pathname, - queryParams, - }, - }); - } - - /** - * Track a conversion event - */ - async trackConversion( - goalId: string, - goalName: string, - options: { value?: number; currency?: string; userId?: string } = {} - ): Promise { - await this.track({ - type: 'conversion', - userId: options.userId, - properties: { - goalId, - goalName, - value: options.value, - currency: options.currency, - }, - }); - } - - /** - * Track a revenue event - */ - async trackRevenue( - transactionId: string, - amount: number, - currency: string, - options: { productIds?: string[]; category?: string; userId?: string } = {} - ): Promise { - await this.track({ - type: 'revenue', - userId: options.userId, - properties: { - transactionId, - amount, - currency, - productIds: options.productIds, - category: options.category, - }, - }); - } - - /** - * Track a custom event - */ - async trackCustom( - name: string, - properties: Record = {}, - options: { category?: string; userId?: string } = {} - ): Promise { - await this.track({ - type: 'custom', - userId: options.userId, - properties: { - name, - category: options.category, - ...properties, - }, - }); - } - - /** - * Flush queued events to the collector - */ - async flush(): Promise { - if (this.eventQueue.length === 0) { - return; - } - - const eventsToSend = [...this.eventQueue]; - this.eventQueue = []; - - try { - const payload: TrackEventPayload = { - // Cast events to the expected type - they match the interface - events: eventsToSend as unknown as TrackEventPayload['events'], - }; - - const response = await fetch(this.options.collector.url, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - ...(this.options.collector.apiKey && { - Authorization: `Bearer ${this.options.collector.apiKey}`, - }), - }, - body: JSON.stringify(payload), - signal: AbortSignal.timeout(this.options.collector.timeout ?? 5000), - }); - - if (!response.ok) { - throw new Error(`Collector responded with ${response.status}`); - } - - if (this.options.debug) { - console.log(`[Analytics] Flushed ${eventsToSend.length} events`); - } - } catch (error) { - // Re-queue events on failure - this.eventQueue = [...eventsToSend, ...this.eventQueue]; - - if (this.options.debug) { - console.error('[Analytics] Failed to flush events:', error); - } - - // Optionally retry based on config - const retries = this.options.collector.retries ?? 0; - if (retries > 0) { - // Implement retry logic if needed - } - } - } - - private startFlushTimer(): void { - const maxWait = this.options.batch?.maxWait ?? 10000; - if (maxWait > 0) { - this.flushTimer = setInterval(() => { - this.flush().catch(() => { - // Errors are logged in flush() - }); - }, maxWait); - } - } -} diff --git a/src/nestjs/services/index.ts b/src/nestjs/services/index.ts deleted file mode 100644 index 5f721b6..0000000 --- a/src/nestjs/services/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { AnalyticsService } from './analytics';