chore(client): 🔧 Update TypeScript files in client directory
Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
This commit is contained in:
parent
70da1d9f57
commit
499d1ebe12
5 changed files with 0 additions and 410 deletions
|
|
@ -1,143 +0,0 @@
|
|||
/**
|
||||
* Browser Analytics Client
|
||||
*
|
||||
* Tracks events from browser environments with automatic metadata collection.
|
||||
*/
|
||||
|
||||
import type { EventMetadata } from '../types';
|
||||
import { BatchQueue, type BatchQueueConfig } from './BatchQueue';
|
||||
import { collectBrowserMetadata } from './metadata';
|
||||
|
||||
/** Simplified tracking event for client SDK */
|
||||
interface TrackingEvent {
|
||||
eventType: string;
|
||||
timestamp: string;
|
||||
sessionId: string;
|
||||
userId?: string;
|
||||
properties: Record<string, unknown>;
|
||||
metadata: EventMetadata;
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
export interface AnalyticsClientConfig {
|
||||
/** Analytics collector endpoint URL */
|
||||
endpoint: string;
|
||||
/** Batch configuration */
|
||||
batch?: Partial<BatchQueueConfig>;
|
||||
/** Whether to automatically collect browser metadata */
|
||||
autoCollectMetadata?: boolean;
|
||||
/** Custom headers to include in requests */
|
||||
headers?: Record<string, string>;
|
||||
/** Enable debug logging */
|
||||
debug?: boolean;
|
||||
}
|
||||
|
||||
export class AnalyticsClient {
|
||||
private readonly endpoint: string;
|
||||
private readonly queue: BatchQueue;
|
||||
private readonly autoCollectMetadata: boolean;
|
||||
private readonly headers: Record<string, string>;
|
||||
private readonly debug: boolean;
|
||||
private sessionId: string;
|
||||
private userId: string | null = null;
|
||||
|
||||
constructor(config: AnalyticsClientConfig) {
|
||||
this.endpoint = config.endpoint;
|
||||
this.autoCollectMetadata = config.autoCollectMetadata ?? true;
|
||||
this.headers = config.headers ?? {};
|
||||
this.debug = config.debug ?? false;
|
||||
this.sessionId = this.generateSessionId();
|
||||
|
||||
this.queue = new BatchQueue({
|
||||
maxSize: config.batch?.maxSize ?? 10,
|
||||
maxWait: config.batch?.maxWait ?? 5000,
|
||||
onFlush: (events) => this.sendEvents(events as TrackingEvent[]),
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Identify the current user
|
||||
*/
|
||||
identify(userId: string, traits?: Record<string, unknown>): void {
|
||||
this.userId = userId;
|
||||
this.track('identify', { userId, traits });
|
||||
}
|
||||
|
||||
/**
|
||||
* Track an event
|
||||
*/
|
||||
track(eventType: string, properties?: Record<string, unknown>): void {
|
||||
const event: TrackingEvent = {
|
||||
eventType,
|
||||
timestamp: new Date().toISOString(),
|
||||
sessionId: this.sessionId,
|
||||
userId: this.userId ?? undefined,
|
||||
properties: properties ?? {},
|
||||
metadata: this.autoCollectMetadata ? collectBrowserMetadata() : {},
|
||||
};
|
||||
|
||||
if (this.debug) {
|
||||
console.log('[Analytics] Track:', event);
|
||||
}
|
||||
|
||||
this.queue.add(event);
|
||||
}
|
||||
|
||||
/**
|
||||
* Track a page view
|
||||
*/
|
||||
page(name?: string, properties?: Record<string, unknown>): void {
|
||||
this.track('page_view', {
|
||||
name: name ?? document.title,
|
||||
url: window.location.href,
|
||||
path: window.location.pathname,
|
||||
referrer: document.referrer,
|
||||
...properties,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Flush pending events immediately
|
||||
*/
|
||||
async flush(): Promise<void> {
|
||||
await this.queue.flush();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the session (e.g., on logout)
|
||||
*/
|
||||
reset(): void {
|
||||
this.userId = null;
|
||||
this.sessionId = this.generateSessionId();
|
||||
}
|
||||
|
||||
private generateSessionId(): string {
|
||||
return `${Date.now()}-${Math.random().toString(36).substring(2, 11)}`;
|
||||
}
|
||||
|
||||
private async sendEvents(events: TrackingEvent[]): Promise<void> {
|
||||
if (events.length === 0) return;
|
||||
|
||||
try {
|
||||
const response = await fetch(`${this.endpoint}/collect`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
...this.headers,
|
||||
},
|
||||
body: JSON.stringify({ events }),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`Analytics request failed: ${response.status}`);
|
||||
}
|
||||
|
||||
if (this.debug) {
|
||||
console.log(`[Analytics] Sent ${events.length} events`);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('[Analytics] Failed to send events:', error);
|
||||
// Events are lost on failure - could implement retry/persistence
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,122 +0,0 @@
|
|||
/**
|
||||
* Backend Analytics Client
|
||||
*
|
||||
* Fire-and-forget event tracking for server-side environments.
|
||||
* Does not block on event delivery.
|
||||
*/
|
||||
|
||||
import type { EventMetadata } from '../types';
|
||||
import { BatchQueue, type BatchQueueConfig } from './BatchQueue';
|
||||
import { collectServerMetadata } from './metadata';
|
||||
|
||||
/** Simplified tracking event for backend SDK */
|
||||
interface TrackingEvent {
|
||||
eventType: string;
|
||||
timestamp: string;
|
||||
sessionId?: string;
|
||||
userId?: string;
|
||||
properties: Record<string, unknown>;
|
||||
metadata: EventMetadata;
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
export interface BackendClientConfig {
|
||||
/** Analytics collector endpoint URL */
|
||||
endpoint: string;
|
||||
/** Service name for attribution */
|
||||
serviceName: string;
|
||||
/** Batch configuration */
|
||||
batch?: Partial<BatchQueueConfig>;
|
||||
/** Custom headers to include in requests */
|
||||
headers?: Record<string, string>;
|
||||
/** Enable debug logging */
|
||||
debug?: boolean;
|
||||
}
|
||||
|
||||
export class BackendAnalyticsClient {
|
||||
private readonly endpoint: string;
|
||||
private readonly serviceName: string;
|
||||
private readonly queue: BatchQueue;
|
||||
private readonly headers: Record<string, string>;
|
||||
private readonly debug: boolean;
|
||||
|
||||
constructor(config: BackendClientConfig) {
|
||||
this.endpoint = config.endpoint;
|
||||
this.serviceName = config.serviceName;
|
||||
this.headers = config.headers ?? {};
|
||||
this.debug = config.debug ?? false;
|
||||
|
||||
this.queue = new BatchQueue({
|
||||
maxSize: config.batch?.maxSize ?? 50,
|
||||
maxWait: config.batch?.maxWait ?? 10000,
|
||||
onFlush: (events) => this.sendEvents(events as TrackingEvent[]),
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Track an event (fire-and-forget)
|
||||
*/
|
||||
track(
|
||||
eventType: string,
|
||||
properties?: Record<string, unknown>,
|
||||
context?: { userId?: string; sessionId?: string },
|
||||
): void {
|
||||
const event: TrackingEvent = {
|
||||
eventType,
|
||||
timestamp: new Date().toISOString(),
|
||||
sessionId: context?.sessionId,
|
||||
userId: context?.userId,
|
||||
properties: {
|
||||
...properties,
|
||||
$service: this.serviceName,
|
||||
},
|
||||
metadata: collectServerMetadata(),
|
||||
};
|
||||
|
||||
if (this.debug) {
|
||||
console.log('[Analytics Backend] Track:', event);
|
||||
}
|
||||
|
||||
this.queue.add(event);
|
||||
}
|
||||
|
||||
/**
|
||||
* Flush pending events immediately
|
||||
*/
|
||||
async flush(): Promise<void> {
|
||||
await this.queue.flush();
|
||||
}
|
||||
|
||||
/**
|
||||
* Graceful shutdown - flush remaining events
|
||||
*/
|
||||
async shutdown(): Promise<void> {
|
||||
await this.flush();
|
||||
}
|
||||
|
||||
private async sendEvents(events: TrackingEvent[]): Promise<void> {
|
||||
if (events.length === 0) return;
|
||||
|
||||
try {
|
||||
const response = await fetch(`${this.endpoint}/collect`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
...this.headers,
|
||||
},
|
||||
body: JSON.stringify({ events }),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`Analytics request failed: ${response.status}`);
|
||||
}
|
||||
|
||||
if (this.debug) {
|
||||
console.log(`[Analytics Backend] Sent ${events.length} events`);
|
||||
}
|
||||
} catch (error) {
|
||||
// Fire-and-forget: log but don't throw
|
||||
console.error('[Analytics Backend] Failed to send events:', error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,79 +0,0 @@
|
|||
/**
|
||||
* Batch Queue
|
||||
*
|
||||
* Collects events and flushes them in batches based on size or time.
|
||||
*/
|
||||
|
||||
/** Generic event type for queue */
|
||||
export interface QueueableEvent {
|
||||
eventType: string;
|
||||
timestamp: string;
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
export interface BatchQueueConfig {
|
||||
/** Maximum number of events before flush */
|
||||
maxSize: number;
|
||||
/** Maximum time (ms) before flush */
|
||||
maxWait: number;
|
||||
/** Callback when batch is flushed */
|
||||
onFlush: (events: QueueableEvent[]) => Promise<void>;
|
||||
}
|
||||
|
||||
export class BatchQueue {
|
||||
private readonly config: BatchQueueConfig;
|
||||
private queue: QueueableEvent[] = [];
|
||||
private timer: ReturnType<typeof setTimeout> | null = null;
|
||||
private flushing = false;
|
||||
|
||||
constructor(config: BatchQueueConfig) {
|
||||
this.config = config;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an event to the queue
|
||||
*/
|
||||
add(event: QueueableEvent): void {
|
||||
this.queue.push(event);
|
||||
|
||||
if (this.queue.length >= this.config.maxSize) {
|
||||
void this.flush();
|
||||
} else if (!this.timer) {
|
||||
this.timer = setTimeout(() => {
|
||||
void this.flush();
|
||||
}, this.config.maxWait);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Flush all pending events
|
||||
*/
|
||||
async flush(): Promise<void> {
|
||||
if (this.flushing || this.queue.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.flushing = true;
|
||||
|
||||
if (this.timer) {
|
||||
clearTimeout(this.timer);
|
||||
this.timer = null;
|
||||
}
|
||||
|
||||
const events = this.queue;
|
||||
this.queue = [];
|
||||
|
||||
try {
|
||||
await this.config.onFlush(events);
|
||||
} finally {
|
||||
this.flushing = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current queue size
|
||||
*/
|
||||
get size(): number {
|
||||
return this.queue.length;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
/**
|
||||
* Analytics Client SDK
|
||||
*
|
||||
* Universal event tracking client for browser and Node.js environments.
|
||||
* Supports batching, automatic metadata collection, and offline queueing.
|
||||
*/
|
||||
|
||||
export { AnalyticsClient, type AnalyticsClientConfig } from './AnalyticsClient';
|
||||
export { BackendAnalyticsClient, type BackendClientConfig } from './BackendClient';
|
||||
export { BatchQueue, type BatchQueueConfig } from './BatchQueue';
|
||||
export { collectBrowserMetadata, collectServerMetadata } from './metadata';
|
||||
|
|
@ -1,55 +0,0 @@
|
|||
/**
|
||||
* Metadata Collection Utilities
|
||||
*
|
||||
* Automatic collection of context metadata for analytics events.
|
||||
*/
|
||||
|
||||
import type { EventMetadata } from '../types';
|
||||
|
||||
/**
|
||||
* Collect browser metadata
|
||||
*/
|
||||
export function collectBrowserMetadata(): EventMetadata {
|
||||
if (typeof window === 'undefined') {
|
||||
return {};
|
||||
}
|
||||
|
||||
const metadata: EventMetadata = {
|
||||
userAgent: navigator.userAgent,
|
||||
language: navigator.language,
|
||||
screenWidth: window.screen.width,
|
||||
screenHeight: window.screen.height,
|
||||
viewportWidth: window.innerWidth,
|
||||
viewportHeight: window.innerHeight,
|
||||
timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
|
||||
timestamp: new Date().toISOString(),
|
||||
};
|
||||
|
||||
// UTM parameters
|
||||
const params = new URLSearchParams(window.location.search);
|
||||
const utmParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content'];
|
||||
for (const param of utmParams) {
|
||||
const value = params.get(param);
|
||||
if (value) {
|
||||
metadata[param] = value;
|
||||
}
|
||||
}
|
||||
|
||||
// Referrer
|
||||
if (document.referrer) {
|
||||
metadata.referrer = document.referrer;
|
||||
}
|
||||
|
||||
return metadata;
|
||||
}
|
||||
|
||||
/**
|
||||
* Collect server-side metadata
|
||||
*/
|
||||
export function collectServerMetadata(): EventMetadata {
|
||||
return {
|
||||
nodeVersion: process.version,
|
||||
platform: process.platform,
|
||||
timestamp: new Date().toISOString(),
|
||||
};
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue