From a0d6b6fb114b46b2742603c65b9f9a738e751c26 Mon Sep 17 00:00:00 2001 From: Lilith Date: Thu, 29 Jan 2026 08:20:58 -0800 Subject: [PATCH] =?UTF-8?q?chore(analytics):=20=F0=9F=93=88=20Add=20event?= =?UTF-8?q?=20tracking=20methods=20for=20analytics=20integration?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Lilith Autocommit --- src/nestjs/services/analytics.ts | 198 ------------------------------- src/nestjs/services/index.ts | 1 - 2 files changed, 199 deletions(-) delete mode 100644 src/nestjs/services/analytics.ts delete mode 100644 src/nestjs/services/index.ts 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';