diff --git a/frontend/src/hooks/useAnalyticsTracking.ts b/frontend/src/hooks/useAnalyticsTracking.ts index eb2958ae..0a2d4e8b 100644 --- a/frontend/src/hooks/useAnalyticsTracking.ts +++ b/frontend/src/hooks/useAnalyticsTracking.ts @@ -100,33 +100,29 @@ export function useAnalyticsTracking() { [analyticsService] ); - // Track feature usage const trackFeatureUsage = useCallback( - (featureName: string, properties?: Record) => { + (featureName: string, properties?: Record) => { analyticsService.trackFeatureUsage(featureName, properties); }, [analyticsService] ); - // Track error const trackError = useCallback( - (errorName: string, errorMessage: string, context?: Record) => { + (errorName: string, errorMessage: string, context?: Record) => { analyticsService.trackError(errorName, errorMessage, context); }, [analyticsService] ); - // Track custom event const trackEvent = useCallback( - (eventName: string, properties?: Record) => { + (eventName: string, properties?: Record) => { analyticsService.trackEvent(eventName, properties); }, [analyticsService] ); - // Update user properties const updateUserProperties = useCallback( - (properties: Record) => { + (properties: Record) => { analyticsService.updateUserProperties(properties); }, [analyticsService] diff --git a/frontend/src/hooks/useChartRendering.ts b/frontend/src/hooks/useChartRendering.ts index 4d461b0e..03810bd5 100644 --- a/frontend/src/hooks/useChartRendering.ts +++ b/frontend/src/hooks/useChartRendering.ts @@ -1,6 +1,8 @@ import { useEffect, useRef } from 'react'; import { Candlestick, ChartScale } from '@/types/charting'; import { CandlestickRenderer } from '@/services/CandlestickRenderer'; +import type { ChartDataManager } from '@/services/ChartDataManager'; +import type { DrawingToolManager } from '@/services/DrawingToolManager'; interface UseChartRenderingProps { canvasRef: React.RefObject; @@ -11,8 +13,8 @@ interface UseChartRenderingProps { candles: Candlestick[]; scale: ChartScale | null; hoveredCandle: Candlestick | null; - dataManager: any; - drawingManager: any; + dataManager: ChartDataManager; + drawingManager: DrawingToolManager; onScaleChange: (scale: ChartScale) => void; } diff --git a/frontend/src/hooks/useLogger.ts b/frontend/src/hooks/useLogger.ts index ddab1f1f..02416b82 100644 --- a/frontend/src/hooks/useLogger.ts +++ b/frontend/src/hooks/useLogger.ts @@ -1,13 +1,14 @@ import { useCallback, useMemo } from 'react'; import { logger, type LogEntry, type LogLevel } from '@/utils/logger'; import { monitoringService } from '@/services/MonitoringService'; +import type { LogData } from '@/types/common'; interface UseLoggerReturn { - debug: (message: string, context?: Record) => void; - info: (message: string, context?: Record) => void; - warn: (message: string, context?: Record) => void; - error: (message: string, context?: Record) => void; - fatal: (message: string, context?: Record) => void; + debug: (message: string, context?: LogData) => void; + info: (message: string, context?: LogData) => void; + warn: (message: string, context?: LogData) => void; + error: (message: string, context?: LogData) => void; + fatal: (message: string, context?: LogData) => void; setRequestId: (id: string) => void; setUserId: (id: string) => void; setTransactionId: (id: string) => void; @@ -23,24 +24,24 @@ interface UseLoggerReturn { name: string, value: number, unit?: 'ms' | 'bytes' | 'count', - context?: Record + context?: LogData ) => void; - trackError: (type: string, message: string, context?: Record) => void; - trackUserAction: (action: string, context?: Record) => void; + trackError: (type: string, message: string, context?: LogData) => void; + trackUserAction: (action: string, context?: LogData) => void; trackContractCall: ( contract: string, functionName: string, duration: number, success: boolean, - context?: Record + context?: LogData ) => void; - trackPageView: (page: string, context?: Record) => void; - trackButtonClick: (button: string, context?: Record) => void; + trackPageView: (page: string, context?: LogData) => void; + trackButtonClick: (button: string, context?: LogData) => void; trackTransaction: ( type: string, status: string, duration: number, - context?: Record + context?: LogData ) => void; } @@ -50,35 +51,35 @@ export function useLogger(component?: string): UseLoggerReturn { }, [component]); const debug = useCallback( - (message: string, ctx?: Record) => { + (message: string, ctx?: LogData) => { logger.debug(message, { ...context, ...ctx }); }, [context] ); const info = useCallback( - (message: string, ctx?: Record) => { + (message: string, ctx?: LogData) => { logger.info(message, { ...context, ...ctx }); }, [context] ); const warn = useCallback( - (message: string, ctx?: Record) => { + (message: string, ctx?: LogData) => { logger.warn(message, { ...context, ...ctx }); }, [context] ); const error = useCallback( - (message: string, ctx?: Record) => { + (message: string, ctx?: LogData) => { logger.error(message, { ...context, ...ctx }); }, [context] ); const fatal = useCallback( - (message: string, ctx?: Record) => { + (message: string, ctx?: LogData) => { logger.fatal(message, { ...context, ...ctx }); }, [context] @@ -151,7 +152,7 @@ export function useLogger(component?: string): UseLoggerReturn { name: string, value: number, unit: 'ms' | 'bytes' | 'count' = 'ms', - ctx?: Record + ctx?: LogData ) => { monitoringService.trackPerformance(name, value, unit, { ...context, ...ctx }); }, @@ -159,14 +160,14 @@ export function useLogger(component?: string): UseLoggerReturn { ); const trackError = useCallback( - (type: string, message: string, ctx?: Record) => { + (type: string, message: string, ctx?: LogData) => { monitoringService.trackError(type, message, { ...context, ...ctx }); }, [context] ); const trackUserAction = useCallback( - (action: string, ctx?: Record) => { + (action: string, ctx?: LogData) => { monitoringService.trackUserAction(action, { ...context, ...ctx }); }, [context] @@ -178,7 +179,7 @@ export function useLogger(component?: string): UseLoggerReturn { functionName: string, duration: number, success: boolean, - ctx?: Record + ctx?: LogData ) => { monitoringService.trackContractCall( contract, @@ -192,21 +193,21 @@ export function useLogger(component?: string): UseLoggerReturn { ); const trackPageView = useCallback( - (page: string, ctx?: Record) => { + (page: string, ctx?: LogData) => { monitoringService.trackPageView(page, { ...context, ...ctx }); }, [context] ); const trackButtonClick = useCallback( - (button: string, ctx?: Record) => { + (button: string, ctx?: LogData) => { monitoringService.trackButtonClick(button, { ...context, ...ctx }); }, [context] ); const trackTransaction = useCallback( - (type: string, status: string, duration: number, ctx?: Record) => { + (type: string, status: string, duration: number, ctx?: LogData) => { monitoringService.trackTransaction(type, status, duration, { ...context, ...ctx }); }, [context] diff --git a/frontend/src/hooks/useSecureStorage.ts b/frontend/src/hooks/useSecureStorage.ts index 0ba02f18..b7bf5875 100644 --- a/frontend/src/hooks/useSecureStorage.ts +++ b/frontend/src/hooks/useSecureStorage.ts @@ -2,6 +2,7 @@ import { useState, useEffect, useCallback } from 'react'; import { SecureStorageV2Service } from '@/services/SecureStorageV2Service'; import { StorageMigrationService } from '@/services/StorageMigrationService'; import { EncryptionService } from '@/services/EncryptionService'; +import type { JsonValue } from '@/types/common'; export interface SecureStorageStatus { initialized: boolean; @@ -68,7 +69,7 @@ export function useSecureStorage() { }, []); const setItem = useCallback( - async (key: string, value: any, options?: Parameters[2]) => { + async (key: string, value: JsonValue, options?: Parameters[2]) => { return SecureStorageV2Service.setItem(key, value, options); }, [] diff --git a/frontend/src/services/AnalyticsService.ts b/frontend/src/services/AnalyticsService.ts index 872e63b2..7e209c45 100644 --- a/frontend/src/services/AnalyticsService.ts +++ b/frontend/src/services/AnalyticsService.ts @@ -1,13 +1,18 @@ -/** - * Analytics Service - * - * Centralized service for tracking user behavior and platform metrics - * Supports multiple analytics providers (Mixpanel, Amplitude, custom) - */ +export type EventPropertyValue = string | number | boolean | null | undefined; + +export function isEventPropertyValue(value: unknown): value is EventPropertyValue { + return ( + value === null || + value === undefined || + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ); +} export interface AnalyticsEvent { name: string; - properties?: Record; + properties?: Record; timestamp?: number; } @@ -19,7 +24,11 @@ export interface UserProperties { totalPredictions?: number; totalStaked?: number; winRate?: number; - [key: string]: any; + sessionId?: string; + network?: string; + theme?: string; + language?: string; + [key: string]: EventPropertyValue; } export interface AnalyticsProvider { @@ -77,9 +86,6 @@ export class AnalyticsService { return `session_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; } - /** - * Initialize analytics with user ID - */ initialize(userId: string, userProperties?: UserProperties): void { if (!this.isEnabled) return; @@ -90,10 +96,7 @@ export class AnalyticsService { }); } - /** - * Track a user action - */ - trackEvent(eventName: string, properties?: Record): void { + trackEvent(eventName: string, properties?: Record): void { if (!this.isEnabled) return; this.provider.track({ @@ -107,9 +110,6 @@ export class AnalyticsService { }); } - /** - * Track market creation - */ trackMarketCreated(marketId: number, question: string, category: string): void { this.trackEvent('market_created', { marketId, @@ -118,9 +118,6 @@ export class AnalyticsService { }); } - /** - * Track market prediction - */ trackPrediction(marketId: number, outcome: 'yes' | 'no', amount: number): void { this.trackEvent('prediction_made', { marketId, @@ -129,9 +126,6 @@ export class AnalyticsService { }); } - /** - * Track market resolution - */ trackMarketResolved(marketId: number, outcome: 'yes' | 'no' | 'disputed'): void { this.trackEvent('market_resolved', { marketId, @@ -139,9 +133,6 @@ export class AnalyticsService { }); } - /** - * Track winnings claimed - */ trackWinningsClaimed(marketId: number, amount: number): void { this.trackEvent('winnings_claimed', { marketId, @@ -149,19 +140,13 @@ export class AnalyticsService { }); } - /** - * Track page view - */ - trackPageView(pageName: string, properties?: Record): void { + trackPageView(pageName: string, properties?: Record): void { this.trackEvent('page_view', { page: pageName, ...properties, }); } - /** - * Track wallet connection - */ trackWalletConnected(walletType: string, address: string): void { this.trackEvent('wallet_connected', { walletType, @@ -169,17 +154,15 @@ export class AnalyticsService { }); } - /** - * Track wallet disconnection - */ trackWalletDisconnected(): void { this.trackEvent('wallet_disconnected'); } - /** - * Track error - */ - trackError(errorName: string, errorMessage: string, context?: Record): void { + trackError( + errorName: string, + errorMessage: string, + context?: Record + ): void { this.trackEvent('error_occurred', { errorName, errorMessage, @@ -187,19 +170,16 @@ export class AnalyticsService { }); } - /** - * Track feature usage - */ - trackFeatureUsage(featureName: string, properties?: Record): void { + trackFeatureUsage( + featureName: string, + properties?: Record + ): void { this.trackEvent('feature_used', { feature: featureName, ...properties, }); } - /** - * Track search - */ trackSearch(query: string, resultsCount: number): void { this.trackEvent('search_performed', { query, @@ -207,9 +187,6 @@ export class AnalyticsService { }); } - /** - * Track filter applied - */ trackFilterApplied(filterType: string, filterValue: string): void { this.trackEvent('filter_applied', { filterType, @@ -217,9 +194,6 @@ export class AnalyticsService { }); } - /** - * Track sort applied - */ trackSortApplied(sortBy: string, sortOrder: 'asc' | 'desc'): void { this.trackEvent('sort_applied', { sortBy, @@ -227,9 +201,6 @@ export class AnalyticsService { }); } - /** - * Track time spent on page - */ trackTimeSpent(pageName: string, timeInSeconds: number): void { this.trackEvent('time_spent', { page: pageName, @@ -237,9 +208,6 @@ export class AnalyticsService { }); } - /** - * Update user properties - */ updateUserProperties(properties: UserProperties): void { if (!this.isEnabled) return; @@ -249,38 +217,25 @@ export class AnalyticsService { }); } - /** - * Reset analytics (logout) - */ reset(): void { this.currentUserId = null; this.sessionId = this.generateSessionId(); this.provider.reset(); } - /** - * Enable/disable analytics - */ setEnabled(enabled: boolean): void { this.isEnabled = enabled; } - /** - * Get current session ID - */ getSessionId(): string { return this.sessionId; } - /** - * Get current user ID - */ getCurrentUserId(): string | null { return this.currentUserId; } } -// Singleton instance let analyticsInstance: AnalyticsService | null = null; export function getAnalyticsService(): AnalyticsService { @@ -290,7 +245,10 @@ export function getAnalyticsService(): AnalyticsService { return analyticsInstance; } -export function initializeAnalytics(provider?: AnalyticsProvider, enabled?: boolean): AnalyticsService { +export function initializeAnalytics( + provider?: AnalyticsProvider, + enabled?: boolean +): AnalyticsService { analyticsInstance = new AnalyticsService(provider, enabled); return analyticsInstance; } diff --git a/frontend/src/services/DataDeletionService.ts b/frontend/src/services/DataDeletionService.ts index 978076f5..3bfdb637 100644 --- a/frontend/src/services/DataDeletionService.ts +++ b/frontend/src/services/DataDeletionService.ts @@ -1,6 +1,15 @@ import { GDPRComplianceService } from './GDPRComplianceService'; import { SecureStorageService } from './SecureStorageService'; +export interface DeletionLogEntry { + userId: string; + scope: string; + reason: string | undefined; + deletedCount: number; + errorCount: number; + timestamp: number; +} + export interface DeletionRequest { userId: string; scope: 'all' | 'pii' | 'analytics' | 'marketing' | 'preferences'; @@ -165,7 +174,7 @@ export class DataDeletionService { if (typeof localStorage === 'undefined') return; const existing = localStorage.getItem(DELETION_LOG_KEY); - const log: any[] = existing ? JSON.parse(existing) : []; + const log: DeletionLogEntry[] = existing ? JSON.parse(existing) : []; log.push({ userId: request.userId, @@ -183,7 +192,7 @@ export class DataDeletionService { } } - static getDeletionLog(): any[] { + static getDeletionLog(): DeletionLogEntry[] { try { if (typeof localStorage === 'undefined') return []; const stored = localStorage.getItem(DELETION_LOG_KEY); diff --git a/frontend/src/services/DataExportService.ts b/frontend/src/services/DataExportService.ts index f57fe154..7ebd671f 100644 --- a/frontend/src/services/DataExportService.ts +++ b/frontend/src/services/DataExportService.ts @@ -1,5 +1,8 @@ import { GDPRComplianceService } from './GDPRComplianceService'; import { SecureStorageService } from './SecureStorageService'; +import type { JsonValue, JsonObject } from '@/types/common'; + +type ExportData = Record; export interface ExportFormat { type: 'json' | 'csv' | 'pdf'; @@ -58,11 +61,11 @@ export class DataExportService { } } - private static collectUserData(request: ExportRequest): Record { - const data: Record = { + private static collectUserData(request: ExportRequest): ExportData { + const data: ExportData = { exportDate: new Date().toISOString(), userId: request.userId, - consent: GDPRComplianceService.getUserConsent(), + consent: GDPRComplianceService.getUserConsent() as JsonValue, }; if (typeof localStorage === 'undefined') { @@ -100,7 +103,7 @@ export class DataExportService { return data; } - private static getTransactions(): any[] { + private static getTransactions(): JsonValue[] { try { if (typeof localStorage === 'undefined') return []; const stored = localStorage.getItem('tx_history'); @@ -110,7 +113,7 @@ export class DataExportService { } } - private static getStakeHistory(): any[] { + private static getStakeHistory(): JsonValue[] { try { if (typeof localStorage === 'undefined') return []; const stored = localStorage.getItem('stake_history'); @@ -120,8 +123,8 @@ export class DataExportService { } } - private static getPreferences(): Record { - const preferences: Record = {}; + private static getPreferences(): Record { + const preferences: Record = {}; try { if (typeof localStorage === 'undefined') return preferences; @@ -151,7 +154,7 @@ export class DataExportService { } private static exportAsJSON( - data: Record, + data: ExportData, request: ExportRequest ): ExportResult { try { @@ -183,7 +186,7 @@ export class DataExportService { } private static exportAsCSV( - data: Record, + data: ExportData, request: ExportRequest ): ExportResult { try { @@ -212,8 +215,8 @@ export class DataExportService { } private static exportAsPDF( - data: Record, - request: ExportRequest + data: ExportData, + _request: ExportRequest ): ExportResult { return { success: false, @@ -222,10 +225,10 @@ export class DataExportService { } private static flattenObject( - obj: Record, + obj: Record, prefix: string = '' - ): Record { - const result: Record = {}; + ): Record { + const result: Record = {}; for (const [key, value] of Object.entries(obj)) { const newKey = prefix ? `${prefix}.${key}` : key; @@ -233,18 +236,18 @@ export class DataExportService { if (value === null || value === undefined) { result[newKey] = ''; } else if (typeof value === 'object' && !Array.isArray(value)) { - Object.assign(result, this.flattenObject(value, newKey)); + Object.assign(result, this.flattenObject(value as Record, newKey)); } else if (Array.isArray(value)) { result[newKey] = JSON.stringify(value); } else { - result[newKey] = value; + result[newKey] = value as string | number | boolean; } } return result; } - private static formatCSVValue(value: any): string { + private static formatCSVValue(value: string | number | boolean): string { if (value === null || value === undefined) { return ''; } diff --git a/frontend/src/services/DataRetentionService.ts b/frontend/src/services/DataRetentionService.ts index abadaeb8..70572c63 100644 --- a/frontend/src/services/DataRetentionService.ts +++ b/frontend/src/services/DataRetentionService.ts @@ -1,4 +1,5 @@ import { GDPRComplianceService } from './GDPRComplianceService'; +import type { JsonValue } from '@/types/common'; export interface RetentionPolicy { category: string; @@ -12,7 +13,7 @@ export interface DataItem { category: string; createdAt: number; lastAccessed?: number; - data: any; + data: JsonValue; } export class DataRetentionService { diff --git a/frontend/src/services/GDPRComplianceService.ts b/frontend/src/services/GDPRComplianceService.ts index d67b02be..60b1671b 100644 --- a/frontend/src/services/GDPRComplianceService.ts +++ b/frontend/src/services/GDPRComplianceService.ts @@ -1,6 +1,22 @@ -import { PIIDetectionService } from './PIIDetectionService'; +import { PIIDetectionService, type PIIValue } from './PIIDetectionService'; import { SecureStorageV2Service } from './SecureStorageV2Service'; +export type ActivityDetails = Record; + +export interface ConsentHistoryEntry { + timestamp: number; + previous: { + analytics: boolean; + marketing: boolean; + personalization: boolean; + } | null; + current: { + analytics: boolean; + marketing: boolean; + personalization: boolean; + }; +} + export interface UserConsent { necessary: boolean; analytics: boolean; @@ -30,7 +46,7 @@ export interface UserData { activityLog: Array<{ action: string; timestamp: number; - details?: Record; + details?: ActivityDetails; }>; } @@ -172,7 +188,7 @@ export class GDPRComplianceService { if (typeof localStorage === 'undefined') return; const stored = localStorage.getItem(CONSENT_HISTORY_KEY); - const history: any[] = stored ? JSON.parse(stored) : []; + const history: ConsentHistoryEntry[] = stored ? JSON.parse(stored) : []; history.push({ timestamp: current.timestamp, @@ -206,7 +222,7 @@ export class GDPRComplianceService { } static checkConsentForStorage( - data: Record, + data: Record, category: 'analytics' | 'marketing' | 'personalization' | 'necessary' ): ConsentCheckResult { if (category === 'necessary') { @@ -429,7 +445,7 @@ export class GDPRComplianceService { return DATA_PROCESSING_ACTIVITIES.find(a => a.name === name); } - static getConsentHistory(): any[] { + static getConsentHistory(): ConsentHistoryEntry[] { try { if (typeof localStorage === 'undefined') return []; const stored = localStorage.getItem(CONSENT_HISTORY_KEY); @@ -442,7 +458,7 @@ export class GDPRComplianceService { static generatePrivacyReport(): { status: ReturnType; processingActivities: DataProcessingActivity[]; - consentHistory: any[]; + consentHistory: ConsentHistoryEntry[]; dataRetentionDays: number; } { return { diff --git a/frontend/src/services/IndexedDBService.ts b/frontend/src/services/IndexedDBService.ts index bccf8351..9d131023 100644 --- a/frontend/src/services/IndexedDBService.ts +++ b/frontend/src/services/IndexedDBService.ts @@ -1,6 +1,8 @@ +import type { JsonValue } from '@/types/common'; + export interface StoredData { key: string; - value: any; + value: JsonValue; timestamp: number; expiresAt?: number; encrypted: boolean; @@ -39,7 +41,7 @@ export class IndexedDBService { }); } - static async setItem(key: string, value: any, expiresIn?: number): Promise { + static async setItem(key: string, value: JsonValue, expiresIn?: number): Promise { await this.initialize(); if (!this.db) { @@ -64,7 +66,7 @@ export class IndexedDBService { }); } - static async getItem(key: string): Promise { + static async getItem(key: string): Promise { await this.initialize(); if (!this.db) { @@ -90,7 +92,7 @@ export class IndexedDBService { return; } - resolve(data.value); + resolve(data.value as T); }; request.onerror = () => reject(new Error('Failed to retrieve data')); diff --git a/frontend/src/services/PIIDetectionService.ts b/frontend/src/services/PIIDetectionService.ts index ab004669..3af98a29 100644 --- a/frontend/src/services/PIIDetectionService.ts +++ b/frontend/src/services/PIIDetectionService.ts @@ -1,7 +1,9 @@ +export type PIIValue = string | number | boolean | null | undefined; + export interface PIIField { type: 'email' | 'phone' | 'address' | 'name' | 'ssn' | 'creditCard' | 'ipAddress' | 'walletAddress' | 'custom'; field: string; - value: any; + value: PIIValue; sensitive: boolean; } @@ -25,7 +27,7 @@ export class PIIDetectionService { 'ipAddress', 'walletAddress', 'userId', 'username' ]; - static detectPII(data: Record): PIIDetectionResult { + static detectPII(data: Record): PIIDetectionResult { const fields: PIIField[] = []; for (const [key, value] of Object.entries(data)) { @@ -44,7 +46,7 @@ export class PIIDetectionService { }; } - private static detectFieldPII(field: string, value: any): PIIField | null { + private static detectFieldPII(field: string, value: PIIValue): PIIField | null { const fieldLower = field.toLowerCase(); const valueStr = String(value); @@ -106,8 +108,8 @@ export class PIIDetectionService { } } - static sanitizeData(data: Record): Record { - const result: Record = {}; + static sanitizeData(data: Record): Record { + const result: Record = {}; const detection = this.detectPII(data); for (const [key, value] of Object.entries(data)) { @@ -122,7 +124,7 @@ export class PIIDetectionService { return result; } - static validateConsent(data: Record, hasConsent: boolean): boolean { + static validateConsent(data: Record, hasConsent: boolean): boolean { const detection = this.detectPII(data); if (!detection.requiresConsent) { return true; diff --git a/frontend/src/services/SecureStorageService.ts b/frontend/src/services/SecureStorageService.ts index 4bab1b47..ed5aa586 100644 --- a/frontend/src/services/SecureStorageService.ts +++ b/frontend/src/services/SecureStorageService.ts @@ -1,5 +1,6 @@ import { GDPRComplianceService } from './GDPRComplianceService'; import { PIIDetectionService } from './PIIDetectionService'; +import type { JsonValue } from '@/types/common'; export interface StorageOptions { requireConsent?: boolean; @@ -9,7 +10,7 @@ export interface StorageOptions { } export interface StorageEntry { - value: any; + value: JsonValue; timestamp: number; expiresAt?: number; category: string; @@ -19,7 +20,7 @@ export interface StorageEntry { export class SecureStorageService { private static readonly PREFIX = 'secure_'; - static setItem(key: string, value: any, options: StorageOptions = {}): boolean { + static setItem(key: string, value: JsonValue, options: StorageOptions = {}): boolean { try { if (typeof localStorage === 'undefined') return false; @@ -30,7 +31,9 @@ export class SecureStorageService { } = options; const detection = PIIDetectionService.detectPII( - typeof value === 'object' ? value : { value } + typeof value === 'object' && value !== null && !Array.isArray(value) + ? (value as Record) + : { value: String(value) } ); if (detection.requiresConsent && requireConsent) { @@ -61,7 +64,7 @@ export class SecureStorageService { } } - static getItem(key: string): T | null { + static getItem(key: string): T | null { try { if (typeof localStorage === 'undefined') return null; @@ -81,7 +84,7 @@ export class SecureStorageService { return null; } - return entry.value; + return entry.value as T; } catch (error) { console.error('Failed to retrieve data:', error); return null; diff --git a/frontend/src/services/SecureStorageV2Service.ts b/frontend/src/services/SecureStorageV2Service.ts index 9757bcc6..89263803 100644 --- a/frontend/src/services/SecureStorageV2Service.ts +++ b/frontend/src/services/SecureStorageV2Service.ts @@ -2,6 +2,7 @@ import { EncryptionService, type EncryptedData } from './EncryptionService'; import { IndexedDBService } from './IndexedDBService'; import { GDPRComplianceService } from './GDPRComplianceService'; import { PIIDetectionService } from './PIIDetectionService'; +import type { JsonValue } from '@/types/common'; export interface SecureStorageOptions { encrypt?: boolean; @@ -35,7 +36,7 @@ export class SecureStorageV2Service { static async setItem( key: string, - value: any, + value: JsonValue, options: SecureStorageOptions = {} ): Promise { try { @@ -49,7 +50,9 @@ export class SecureStorageV2Service { } = options; const detection = PIIDetectionService.detectPII( - typeof value === 'object' ? value : { value } + typeof value === 'object' && value !== null && !Array.isArray(value) + ? (value as Record) + : { value: String(value) } ); if (detection.requiresConsent && requireConsent) { @@ -72,7 +75,7 @@ export class SecureStorageV2Service { expiresAt: expiresIn ? Date.now() + expiresIn : undefined, }; - let dataToStore: any; + let dataToStore: JsonValue; if (encrypt) { const encrypted = await EncryptionService.encryptObject({ @@ -92,7 +95,7 @@ export class SecureStorageV2Service { } } - static async getItem(key: string): Promise { + static async getItem(key: string): Promise { try { await this.initialize(); @@ -118,7 +121,7 @@ export class SecureStorageV2Service { return null; } - return stored.value; + return stored.value as T; } catch (error) { console.error(`Failed to retrieve ${key}:`, error); return null; @@ -247,7 +250,7 @@ export class SecureStorageV2Service { private static async getItemWithMetadata( key: string - ): Promise<{ value: any; metadata: StorageMetadata } | null> { + ): Promise<{ value: JsonValue; metadata: StorageMetadata } | null> { try { const stored = await IndexedDBService.getItem(key); if (!stored) return null; @@ -263,7 +266,7 @@ export class SecureStorageV2Service { } } - private static isEncryptedData(value: any): value is EncryptedData { + private static isEncryptedData(value: unknown): value is EncryptedData { return ( typeof value === 'object' && value !== null && diff --git a/frontend/src/services/__tests__/AnalyticsService.test.ts b/frontend/src/services/__tests__/AnalyticsService.test.ts index 8b7d7ec5..ba3c4cb5 100644 --- a/frontend/src/services/__tests__/AnalyticsService.test.ts +++ b/frontend/src/services/__tests__/AnalyticsService.test.ts @@ -3,8 +3,10 @@ import { AnalyticsService, getAnalyticsService, initializeAnalytics, + isEventPropertyValue, type AnalyticsProvider, type AnalyticsEvent, + type EventPropertyValue, type UserProperties, } from '../AnalyticsService'; @@ -262,4 +264,33 @@ describe('AnalyticsService', () => { expect(mockProvider.events[0].properties?.customProp2).toBe(42); }); }); -}); + + describe('EventPropertyValue type guard', () => { + it('accepts valid property values', () => { + const validValues: unknown[] = ['string', 42, true, false, null, undefined]; + validValues.forEach(v => { + expect(isEventPropertyValue(v)).toBe(true); + }); + }); + + it('rejects objects and arrays', () => { + const invalidValues: unknown[] = [{}, [], new Date(), () => {}]; + invalidValues.forEach(v => { + expect(isEventPropertyValue(v)).toBe(false); + }); + }); + + it('event properties conform to EventPropertyValue', () => { + const props: Record = { + marketId: 1, + outcome: 'yes', + amount: 1000, + active: true, + extra: null, + }; + service.trackEvent('typed_event', props); + expect(mockProvider.events).toHaveLength(1); + expect(mockProvider.events[0].properties?.marketId).toBe(1); + }); + }); +}); \ No newline at end of file diff --git a/frontend/src/types/charting.ts b/frontend/src/types/charting.ts index 3ef88f12..87ed5a78 100644 --- a/frontend/src/types/charting.ts +++ b/frontend/src/types/charting.ts @@ -1,3 +1,5 @@ +import type { JsonValue } from './common'; + export type Timeframe = '1m' | '5m' | '15m' | '30m' | '1h' | '4h' | '1d' | '1w' | '1M'; export interface OHLCV { @@ -133,7 +135,7 @@ export interface AnalysisPattern { export interface ChartEvent { type: 'candle_hover' | 'candle_click' | 'timeframe_change' | 'indicator_add' | 'indicator_remove' | 'tool_add' | 'tool_remove'; - data: any; + data: JsonValue; timestamp: number; } diff --git a/frontend/src/types/common.ts b/frontend/src/types/common.ts index 74fb1580..9e432e61 100644 --- a/frontend/src/types/common.ts +++ b/frontend/src/types/common.ts @@ -27,3 +27,13 @@ export interface JsonObject { [key: string]: JsonValue; } export type JsonArray = JsonValue[]; + +export type LogData = Record; + +export interface CacheEntry { + value: T; + timestamp: number; + ttl: number; +} + +export type EventCallback = (data?: T) => void; diff --git a/frontend/src/types/rbac.ts b/frontend/src/types/rbac.ts index 170c5c66..847ce16e 100644 --- a/frontend/src/types/rbac.ts +++ b/frontend/src/types/rbac.ts @@ -1,3 +1,5 @@ +import type { JsonValue } from './common'; + export enum Permission { CREATE_MARKET = 'create_market', EDIT_MARKET = 'edit_market', @@ -73,8 +75,8 @@ export interface AuditLog { action: string; resource: string; resourceId: string; - oldValue?: any; - newValue?: any; + oldValue?: JsonValue; + newValue?: JsonValue; status: 'success' | 'failure'; timestamp: number; ipAddress?: string; diff --git a/frontend/src/types/reducers.ts b/frontend/src/types/reducers.ts index caba27a8..bfdaba6b 100644 --- a/frontend/src/types/reducers.ts +++ b/frontend/src/types/reducers.ts @@ -2,6 +2,8 @@ * Common Types for useReducer Pattern */ +import type { JsonValue } from './common'; + export type AsyncState = { data: T | null; loading: boolean; @@ -55,13 +57,13 @@ export type FilterAction = export type ModalState = { isOpen: boolean; - data: any | null; + data: JsonValue | null; }; export type ModalAction = - | { type: 'OPEN'; payload?: any } + | { type: 'OPEN'; payload?: JsonValue } | { type: 'CLOSE' } - | { type: 'SET_DATA'; payload: any }; + | { type: 'SET_DATA'; payload: JsonValue }; export type ListState = { items: T[]; diff --git a/frontend/src/types/referral.ts b/frontend/src/types/referral.ts index 9cf33691..bc1687a0 100644 --- a/frontend/src/types/referral.ts +++ b/frontend/src/types/referral.ts @@ -1,3 +1,5 @@ +import type { Market, Prediction } from './market'; + export interface ReferralCode { code: string; owner: string; @@ -193,7 +195,7 @@ export type ReferralHookReturn = { export type ReferralIntegrationHookReturn = { recordRewardOnAction: (actionAmount: number, actionType: string) => Promise; - triggerRewardIfReferred: (market: any, prediction: any) => Promise; + triggerRewardIfReferred: (market: Market, prediction: Prediction) => Promise; isPending: boolean; lastError: string | null; rewards: ReferralTransaction[]; diff --git a/frontend/src/types/sync.ts b/frontend/src/types/sync.ts index 10092c74..48796e11 100644 --- a/frontend/src/types/sync.ts +++ b/frontend/src/types/sync.ts @@ -1,3 +1,5 @@ +import type { JsonValue } from './common'; + export type SyncAction = 'create' | 'update' | 'delete'; export type ConflictResolution = 'local' | 'remote' | 'merge' | 'manual'; export type SyncStatus = 'synced' | 'pending' | 'syncing' | 'error' | 'offline'; @@ -8,7 +10,7 @@ export interface SyncEntity { localVersion: number; remoteVersion: number; lastSyncTime: number; - data: Record; + data: Record; hash: string; } @@ -18,11 +20,11 @@ export interface SyncConflict { entityType: string; localVersion: number; remoteVersion: number; - localData: Record; - remoteData: Record; + localData: Record; + remoteData: Record; timestamp: number; resolution?: ConflictResolution; - resolvedData?: Record; + resolvedData?: Record; } export interface QueuedAction { @@ -30,7 +32,7 @@ export interface QueuedAction { entityId: string; entityType: string; action: SyncAction; - payload: Record; + payload: Record; timestamp: number; retryCount: number; maxRetries: number; @@ -61,7 +63,7 @@ export interface SyncConfig { export interface SyncEvent { type: 'sync_start' | 'sync_complete' | 'sync_error' | 'conflict_detected' | 'action_queued' | 'action_synced' | 'offline' | 'online'; timestamp: number; - data?: Record; + data?: Record; error?: string; } @@ -81,14 +83,14 @@ export interface DataSnapshot { id: string; timestamp: number; entityType: string; - data: Record; + data: Record; hash: string; } export interface SyncDiff { fields: Record; added: string[]; removed: string[]; diff --git a/frontend/src/types/template.ts b/frontend/src/types/template.ts index 16567847..7333438a 100644 --- a/frontend/src/types/template.ts +++ b/frontend/src/types/template.ts @@ -41,7 +41,7 @@ export interface TemplateValidationRule { field: string; rule: string; message: string; - validator: (value: any) => boolean; + validator: (value: unknown) => boolean; } export interface ValidationState { diff --git a/frontend/src/types/websocket.ts b/frontend/src/types/websocket.ts index 99fc6550..7b967258 100644 --- a/frontend/src/types/websocket.ts +++ b/frontend/src/types/websocket.ts @@ -1,3 +1,5 @@ +import type { JsonValue } from './common'; + export interface MarketUpdate { marketId: string; price: number; @@ -37,7 +39,7 @@ export interface TradeUpdate { export interface WebSocketMessage { type: 'subscribe' | 'unsubscribe' | 'update' | 'error' | 'ping' | 'pong'; channel?: string; - data?: any; + data?: JsonValue; error?: string; sequence?: number; } diff --git a/frontend/src/utils/logger.ts b/frontend/src/utils/logger.ts index a70eb1b5..55ab4cc2 100644 --- a/frontend/src/utils/logger.ts +++ b/frontend/src/utils/logger.ts @@ -1,10 +1,12 @@ +import type { LogData } from '@/types/common'; + export type LogLevel = 'debug' | 'info' | 'warn' | 'error' | 'fatal'; export interface LogEntry { timestamp: number; level: LogLevel; message: string; - context?: Record; + context?: LogData; requestId?: string; userId?: string; transactionId?: string; @@ -84,7 +86,7 @@ class Logger { private createEntry( level: LogLevel, message: string, - context?: Record + context?: LogData ): LogEntry { const entry: LogEntry = { timestamp: Date.now(), @@ -111,7 +113,7 @@ class Logger { private log( level: LogLevel, message: string, - context?: Record + context?: LogData ): void { if (!this.shouldLog(level)) return; @@ -172,23 +174,23 @@ class Logger { return parts.join(' '); } - debug(message: string, context?: Record): void { + debug(message: string, context?: LogData): void { this.log('debug', message, context); } - info(message: string, context?: Record): void { + info(message: string, context?: LogData): void { this.log('info', message, context); } - warn(message: string, context?: Record): void { + warn(message: string, context?: LogData): void { this.log('warn', message, context); } - error(message: string, context?: Record): void { + error(message: string, context?: LogData): void { this.log('error', message, context); } - fatal(message: string, context?: Record): void { + fatal(message: string, context?: LogData): void { this.log('fatal', message, context); } diff --git a/frontend/src/utils/oracleErrorHandling.ts b/frontend/src/utils/oracleErrorHandling.ts index a270e954..877faadc 100644 --- a/frontend/src/utils/oracleErrorHandling.ts +++ b/frontend/src/utils/oracleErrorHandling.ts @@ -1,9 +1,11 @@ +import type { JsonValue } from '@/types/common'; + export class OracleError extends Error { constructor( message: string, public code: string, public statusCode: number = 500, - public details?: any + public details?: JsonValue ) { super(message); this.name = 'OracleError'; @@ -11,42 +13,42 @@ export class OracleError extends Error { } export class ValidationError extends OracleError { - constructor(message: string, details?: any) { + constructor(message: string, details?: JsonValue) { super(message, 'VALIDATION_ERROR', 400, details); this.name = 'ValidationError'; } } export class ProviderError extends OracleError { - constructor(message: string, public providerId: string, details?: any) { + constructor(message: string, public providerId: string, details?: JsonValue) { super(message, 'PROVIDER_ERROR', 502, details); this.name = 'ProviderError'; } } export class ConsensusError extends OracleError { - constructor(message: string, details?: any) { + constructor(message: string, details?: JsonValue) { super(message, 'CONSENSUS_ERROR', 424, details); this.name = 'ConsensusError'; } } export class TimeoutError extends OracleError { - constructor(message: string, details?: any) { + constructor(message: string, details?: JsonValue) { super(message, 'TIMEOUT_ERROR', 504, details); this.name = 'TimeoutError'; } } export class NetworkError extends OracleError { - constructor(message: string, details?: any) { + constructor(message: string, details?: JsonValue) { super(message, 'NETWORK_ERROR', 503, details); this.name = 'NetworkError'; } } export class FallbackError extends OracleError { - constructor(message: string, details?: any) { + constructor(message: string, details?: JsonValue) { super(message, 'FALLBACK_ERROR', 503, details); this.name = 'FallbackError'; } @@ -77,7 +79,7 @@ export class ErrorHandler { message: string; code: string; statusCode: number; - details?: any; + details?: JsonValue; } { return { message: error.message, diff --git a/frontend/src/utils/performance.ts b/frontend/src/utils/performance.ts index ee5d8ea0..97d7b7d0 100644 --- a/frontend/src/utils/performance.ts +++ b/frontend/src/utils/performance.ts @@ -2,11 +2,22 @@ * Performance monitoring and optimization for sync system */ +export type MetricTags = Record; + +export interface OperationStatistics { + count: number; + min: number; + max: number; + avg: number; + p95: number; + p99: number; +} + export interface PerformanceMetric { name: string; duration: number; timestamp: number; - tags?: Record; + tags?: MetricTags; } export class PerformanceMonitor { @@ -23,7 +34,7 @@ export class PerformanceMonitor { }; } - recordMetric(name: string, duration: number, tags?: Record): void { + recordMetric(name: string, duration: number, tags?: MetricTags): void { const metric: PerformanceMetric = { name, duration, @@ -47,14 +58,7 @@ export class PerformanceMonitor { return this.metrics.get(name) || []; } - getStatistics(name: string): { - count: number; - min: number; - max: number; - avg: number; - p95: number; - p99: number; - } { + getStatistics(name: string): OperationStatistics { const metrics = this.getMetrics(name); if (metrics.length === 0) { @@ -73,8 +77,8 @@ export class PerformanceMonitor { return { count, min, max, avg, p95, p99 }; } - getAllStatistics(): Record { - const stats: Record = {}; + getAllStatistics(): Record { + const stats: Record = {}; for (const [name] of this.metrics) { stats[name] = this.getStatistics(name); diff --git a/frontend/src/utils/performanceMonitor.ts b/frontend/src/utils/performanceMonitor.ts index 1d43d7f7..ce5bd92e 100644 --- a/frontend/src/utils/performanceMonitor.ts +++ b/frontend/src/utils/performanceMonitor.ts @@ -5,13 +5,20 @@ interface PerformanceMetric { cached: boolean; } +export interface PerformanceMetricSummary { + count: number; + averageDuration: number; + cacheHitRate: number; + improvement: number; +} + class PerformanceMonitor { private metrics: PerformanceMetric[] = []; private readonly MAX_METRICS = 100; startMeasure(name: string): () => void { const startTime = performance.now(); - + return (cached: boolean = false) => { const duration = performance.now() - startTime; this.recordMetric({ @@ -25,7 +32,7 @@ class PerformanceMonitor { recordMetric(metric: PerformanceMetric): void { this.metrics.push(metric); - + if (this.metrics.length > this.MAX_METRICS) { this.metrics.shift(); } @@ -41,7 +48,7 @@ class PerformanceMonitor { getAverageDuration(name: string): number { const filtered = this.metrics.filter(m => m.name === name); if (filtered.length === 0) return 0; - + const total = filtered.reduce((sum, m) => sum + m.duration, 0); return total / filtered.length; } @@ -49,7 +56,7 @@ class PerformanceMonitor { getCacheHitRate(name: string): number { const filtered = this.metrics.filter(m => m.name === name); if (filtered.length === 0) return 0; - + const cached = filtered.filter(m => m.cached).length; return (cached / filtered.length) * 100; } @@ -58,12 +65,12 @@ class PerformanceMonitor { const filtered = this.metrics.filter(m => m.name === name); const cached = filtered.filter(m => m.cached); const uncached = filtered.filter(m => !m.cached); - + if (cached.length === 0 || uncached.length === 0) return 0; - + const cachedAvg = cached.reduce((sum, m) => sum + m.duration, 0) / cached.length; const uncachedAvg = uncached.reduce((sum, m) => sum + m.duration, 0) / uncached.length; - + return ((uncachedAvg - cachedAvg) / uncachedAvg) * 100; } @@ -71,10 +78,10 @@ class PerformanceMonitor { this.metrics = []; } - getSummary(): Record { + getSummary(): Record { const names = [...new Set(this.metrics.map(m => m.name))]; - - return names.reduce((acc, name) => { + + return names.reduce>((acc, name) => { acc[name] = { count: this.metrics.filter(m => m.name === name).length, averageDuration: this.getAverageDuration(name), @@ -82,7 +89,7 @@ class PerformanceMonitor { improvement: this.getPerformanceImprovement(name), }; return acc; - }, {} as Record); + }, {}); } } diff --git a/frontend/src/utils/reducerHelpers.ts b/frontend/src/utils/reducerHelpers.ts index 7983fbf1..5686f97e 100644 --- a/frontend/src/utils/reducerHelpers.ts +++ b/frontend/src/utils/reducerHelpers.ts @@ -6,13 +6,13 @@ export function createAction( type: T ): P extends void ? { type: T } : { type: T; payload: P } { return ((payload?: P) => - payload === undefined ? { type } : { type, payload }) as any; + payload === undefined ? { type } : { type, payload }) as P extends void ? { type: T } : { type: T; payload: P }; } -export function combineReducers>( - reducers: { [K in keyof S]: (state: S[K], action: any) => S[K] } +export function combineReducers>( + reducers: { [K in keyof S]: (state: S[K], action: { type: string }) => S[K] } ) { - return (state: S, action: any): S => { + return (state: S, action: { type: string }): S => { const nextState = {} as S; let hasChanged = false; @@ -31,7 +31,7 @@ export function combineReducers>( export function createReducer( initialState: S, - handlers: Record S> + handlers: Record S> ) { return (state: S = initialState, action: A): S => { if (handlers.hasOwnProperty(action.type)) { diff --git a/frontend/src/utils/reputationHelpers.ts b/frontend/src/utils/reputationHelpers.ts index 5779c74c..45c13761 100644 --- a/frontend/src/utils/reputationHelpers.ts +++ b/frontend/src/utils/reputationHelpers.ts @@ -1,20 +1,21 @@ import { FraudAlert, UserReputation } from '@/types/reputation'; +import type { JsonValue } from '@/types/common'; export class ReputationEventBus { - private listeners: Map void>> = new Map(); + private listeners: Map void>> = new Map(); - on(event: string, callback: (data: any) => void): void { + on(event: string, callback: (data: JsonValue) => void): void { if (!this.listeners.has(event)) { this.listeners.set(event, new Set()); } this.listeners.get(event)?.add(callback); } - off(event: string, callback: (data: any) => void): void { + off(event: string, callback: (data: JsonValue) => void): void { this.listeners.get(event)?.delete(callback); } - emit(event: string, data: any): void { + emit(event: string, data: JsonValue): void { this.listeners.get(event)?.forEach((callback) => { try { callback(data); @@ -36,10 +37,10 @@ export class ReputationLogger { timestamp: number; level: string; message: string; - data?: any; + data?: JsonValue; }> = []; - static log(level: 'info' | 'warn' | 'error', message: string, data?: any): void { + static log(level: 'info' | 'warn' | 'error', message: string, data?: JsonValue): void { const entry = { timestamp: Date.now(), level, @@ -76,17 +77,17 @@ export class ReputationLogger { } export class ReputationCache { - private cache: Map = new Map(); + private cache: Map = new Map(); private readonly defaultTTL = 5 * 60 * 1000; - set(key: string, value: any, ttl: number = this.defaultTTL): void { + set(key: string, value: JsonValue, ttl: number = this.defaultTTL): void { this.cache.set(key, { value, expiresAt: Date.now() + ttl, }); } - get(key: string): any | null { + get(key: string): JsonValue | null { const entry = this.cache.get(key); if (!entry) { diff --git a/frontend/src/utils/storageAudit.ts b/frontend/src/utils/storageAudit.ts index 5f9d98c9..c9c79107 100644 --- a/frontend/src/utils/storageAudit.ts +++ b/frontend/src/utils/storageAudit.ts @@ -85,7 +85,7 @@ export class StorageAudit { ); if (isSensitiveKey) { - let parsedValue: any; + let parsedValue: unknown; try { parsedValue = JSON.parse(value); } catch { @@ -93,7 +93,9 @@ export class StorageAudit { } const detection = PIIDetectionService.detectPII( - typeof parsedValue === 'object' ? parsedValue : { value: parsedValue } + typeof parsedValue === 'object' && parsedValue !== null && !Array.isArray(parsedValue) + ? (parsedValue as Record) + : { value: String(parsedValue) } ); if (detection.hasPII) { diff --git a/frontend/src/utils/syncAnalytics.ts b/frontend/src/utils/syncAnalytics.ts index 6d86a573..d7cf81bd 100644 --- a/frontend/src/utils/syncAnalytics.ts +++ b/frontend/src/utils/syncAnalytics.ts @@ -2,10 +2,12 @@ * Sync analytics and reporting for synchronization system */ +export type SyncEventData = Record; + export interface SyncAnalyticsEvent { type: string; timestamp: number; - data: Record; + data: SyncEventData; } export interface SyncAnalyticsReport { @@ -26,7 +28,7 @@ export class SyncAnalyticsService { private events: SyncAnalyticsEvent[] = []; private maxEvents: number = 10000; - recordEvent(type: string, data: Record): void { + recordEvent(type: string, data: SyncEventData = {}): void { const event: SyncAnalyticsEvent = { type, timestamp: Date.now(), diff --git a/frontend/src/utils/templateAnalytics.ts b/frontend/src/utils/templateAnalytics.ts index 07d87c09..56471178 100644 --- a/frontend/src/utils/templateAnalytics.ts +++ b/frontend/src/utils/templateAnalytics.ts @@ -1,4 +1,6 @@ -import { TemplateCategory, TEMPLATE_CATEGORIES } from '@/types/template'; +import { TemplateCategory } from '@/types/template'; + +type TemplateEventData = Record; export interface TemplateAnalytics { templateId: TemplateCategory; @@ -28,10 +30,34 @@ export interface ValidationAnalytics { successRateAfterError: number; } +interface TemplateSession { + templateId: TemplateCategory; + startTime: number; + stepHistory: Array<{ stepId: string; entryTime: number; exitTime: number }>; + validationErrors: Array<{ errorType: string; errorMessage: string; timestamp: number }>; + completionTime?: number; + totalSessionTime?: number; + success?: boolean; +} + +interface ValidationAttempt { + field: string; + error: string; + timestamp: number; +} + +declare global { + interface Window { + gtag?: (command: string, eventName: string, params?: TemplateEventData & { event_category?: string }) => void; + amplitude?: { + track: (eventName: string, properties?: TemplateEventData & { sessionId?: string }) => void; + }; + } +} + class TemplateAnalyticsTracker { private templateSessions: Map = new Map(); private validateAttempts: Map = new Map(); - private sessionId: string; constructor() { @@ -40,7 +66,7 @@ class TemplateAnalyticsTracker { trackTemplateSelection(templateId: TemplateCategory): void { const sessionKey = this.sessionId; - + if (!this.templateSessions.has(sessionKey)) { this.templateSessions.set(sessionKey, { templateId, @@ -98,8 +124,7 @@ class TemplateAnalyticsTracker { }): void { const session = this.templateSessions.get(this.sessionId); if (session) { - const totalTime = Date.now() - session.startTime; - session.completionTime = totalTime; + session.completionTime = Date.now() - session.startTime; } this.sendEvent('form_completed', { @@ -112,8 +137,7 @@ class TemplateAnalyticsTracker { trackMarketCreated(success: boolean, error?: string): void { const session = this.templateSessions.get(this.sessionId); if (session) { - const totalTime = Date.now() - session.startTime; - session.totalSessionTime = totalTime; + session.totalSessionTime = Date.now() - session.startTime; session.success = success; } @@ -124,17 +148,25 @@ class TemplateAnalyticsTracker { } } - getSessionAnalytics() { + getSessionAnalytics(): { + templateId: TemplateCategory; + totalTime: number; + completionTime: number | undefined; + stepCount: number; + errorCount: number; + success: boolean; + steps: Array<{ stepId: string; duration: number }>; + } | null { const session = this.templateSessions.get(this.sessionId); if (!session) return null; return { templateId: session.templateId, - totalTime: session.totalSessionTime || Date.now() - session.startTime, + totalTime: session.totalSessionTime ?? Date.now() - session.startTime, completionTime: session.completionTime, stepCount: session.stepHistory.length, errorCount: session.validationErrors.length, - success: session.success || false, + success: session.success ?? false, steps: session.stepHistory.map(step => ({ stepId: step.stepId, duration: step.exitTime - step.entryTime, @@ -142,7 +174,7 @@ class TemplateAnalyticsTracker { }; } - private sendEvent(eventName: string, eventData?: Record): void { + private sendEvent(eventName: string, eventData?: TemplateEventData): void { if (window.gtag) { window.gtag('event', eventName, { event_category: 'market_creation_wizard', @@ -163,20 +195,4 @@ class TemplateAnalyticsTracker { } } -interface TemplateSession { - templateId: TemplateCategory; - startTime: number; - stepHistory: Array<{ stepId: string; entryTime: number; exitTime: number }>; - validationErrors: Array<{ errorType: string; errorMessage: string; timestamp: number }>; - completionTime?: number; - totalSessionTime?: number; - success?: boolean; -} - -interface ValidationAttempt { - field: string; - error: string; - timestamp: number; -} - export const analyticsTracker = new TemplateAnalyticsTracker(); diff --git a/frontend/src/utils/validators/AggregationDataValidator.ts b/frontend/src/utils/validators/AggregationDataValidator.ts index 7238c7b2..612458f7 100644 --- a/frontend/src/utils/validators/AggregationDataValidator.ts +++ b/frontend/src/utils/validators/AggregationDataValidator.ts @@ -3,7 +3,7 @@ import { BaseValidator, ValidationResult } from './BaseValidator'; import { CommonValidators } from './commonValidators'; export class AggregationDataValidator extends BaseValidator { - isValid(price: any): boolean { + isValid(price: unknown): boolean { if (!CommonValidators.isValidObject(price)) return false; return ( @@ -16,7 +16,7 @@ export class AggregationDataValidator extends BaseValidator { ); } - validate(price: any): ValidationResult { + validate(price: unknown): ValidationResult { const errors: string[] = []; if (!CommonValidators.isValidObject(price)) { @@ -41,7 +41,7 @@ export class AggregationDataValidator extends BaseValidator { }; } - sanitize(price: any): AggregatedPrice | null { + sanitize(price: unknown): AggregatedPrice | null { if (!CommonValidators.isValidObject(price)) return null; const sanitized: AggregatedPrice = { @@ -49,7 +49,7 @@ export class AggregationDataValidator extends BaseValidator { timestamp: CommonValidators.sanitizeTimestamp(price.timestamp), sources: CommonValidators.sanitizeArray( price.sources, - (s: any) => typeof s === 'string' + (s: unknown) => typeof s === 'string' ), confidence: CommonValidators.sanitizeNumber(price.confidence, 0.5, 0, 1), consensusReached: Boolean(price.consensusReached), diff --git a/frontend/src/utils/validators/BaseValidator.ts b/frontend/src/utils/validators/BaseValidator.ts index 26a0b537..2c5a55c2 100644 --- a/frontend/src/utils/validators/BaseValidator.ts +++ b/frontend/src/utils/validators/BaseValidator.ts @@ -9,9 +9,9 @@ export interface ExtendedValidationResult extends ValidationResult { export abstract class BaseValidator { protected validateField( - value: any, + value: unknown, fieldName: string, - validator: (val: any) => boolean, + validator: (val: unknown) => boolean, errorMessage?: string ): string | null { if (!validator(value)) { @@ -24,7 +24,7 @@ export abstract class BaseValidator { return checks.filter((error): error is string => error !== null); } - abstract isValid(data: any): boolean; - abstract validate(data: any): ValidationResult | ExtendedValidationResult; - abstract sanitize(data: any): T | null; + abstract isValid(data: unknown): boolean; + abstract validate(data: unknown): ValidationResult | ExtendedValidationResult; + abstract sanitize(data: unknown): T | null; } diff --git a/frontend/src/utils/validators/ConfigurationValidator.ts b/frontend/src/utils/validators/ConfigurationValidator.ts index e9c2c34e..7dc498f1 100644 --- a/frontend/src/utils/validators/ConfigurationValidator.ts +++ b/frontend/src/utils/validators/ConfigurationValidator.ts @@ -1,39 +1,39 @@ import { BaseValidator, ValidationResult } from './BaseValidator'; import { CommonValidators } from './commonValidators'; -export class ConfigurationValidator extends BaseValidator { +export class ConfigurationValidator extends BaseValidator> { private static readonly MARKET_ID_PATTERN = /^[a-zA-Z0-9\/-]+$/; private static readonly MAX_TIMEFRAME = 86400000; - static isValidMarketId(marketId: any): boolean { + static isValidMarketId(marketId: unknown): boolean { return ( CommonValidators.isValidString(marketId, 1, 256) && - this.MARKET_ID_PATTERN.test(marketId) + this.MARKET_ID_PATTERN.test(marketId as string) ); } - static isValidProviderId(providerId: any): boolean { + static isValidProviderId(providerId: unknown): boolean { return CommonValidators.isValidString(providerId, 1, 128); } - static isValidThreshold(threshold: any): boolean { + static isValidThreshold(threshold: unknown): boolean { return CommonValidators.isValidRatio(threshold); } - static isValidTimeframe(timeframe: any): boolean { + static isValidTimeframe(timeframe: unknown): boolean { return ( CommonValidators.isValidPositiveNumber(timeframe) && - timeframe > 0 && - timeframe < this.MAX_TIMEFRAME + (timeframe as number) > 0 && + (timeframe as number) < this.MAX_TIMEFRAME ); } - isValid(config: any): boolean { + isValid(config: unknown): boolean { if (!CommonValidators.isValidObject(config)) return false; return this.validate(config).valid; } - validate(config: any): ValidationResult { + validate(config: unknown): ValidationResult { const errors: string[] = []; if (!CommonValidators.isValidObject(config)) { @@ -48,9 +48,11 @@ export class ConfigurationValidator extends BaseValidator { } if (config.providers && Array.isArray(config.providers)) { - config.providers.forEach((provider: any, index: number) => { - if (!ConfigurationValidator.isValidProviderId(provider.id)) { - errors.push(`Invalid provider ID at index ${index}`); + config.providers.forEach((provider: unknown, index: number) => { + if (typeof provider === 'object' && provider !== null && 'id' in provider) { + if (!ConfigurationValidator.isValidProviderId((provider as { id: unknown }).id)) { + errors.push(`Invalid provider ID at index ${index}`); + } } }); } @@ -61,8 +63,8 @@ export class ConfigurationValidator extends BaseValidator { }; } - sanitize(config: any): any | null { + sanitize(config: unknown): Record | null { if (!CommonValidators.isValidObject(config)) return null; - return config; + return config as Record; } } diff --git a/frontend/src/utils/validators/PriceDataValidator.ts b/frontend/src/utils/validators/PriceDataValidator.ts index fd3f931c..ba0710b2 100644 --- a/frontend/src/utils/validators/PriceDataValidator.ts +++ b/frontend/src/utils/validators/PriceDataValidator.ts @@ -3,7 +3,7 @@ import { BaseValidator, ValidationResult } from './BaseValidator'; import { CommonValidators } from './commonValidators'; export class PriceDataValidator extends BaseValidator { - isValid(price: any): boolean { + isValid(price: unknown): boolean { if (!CommonValidators.isValidObject(price)) return false; return ( @@ -14,7 +14,7 @@ export class PriceDataValidator extends BaseValidator { ); } - validate(price: any): ValidationResult { + validate(price: unknown): ValidationResult { const errors: string[] = []; if (!CommonValidators.isValidObject(price)) { @@ -37,7 +37,7 @@ export class PriceDataValidator extends BaseValidator { }; } - sanitize(price: any): OraclePrice | null { + sanitize(price: unknown): OraclePrice | null { if (!CommonValidators.isValidObject(price)) return null; const sanitized: OraclePrice = { @@ -54,13 +54,13 @@ export class PriceDataValidator extends BaseValidator { return sanitized; } - validateArray(prices: any[]): { + validateArray(prices: unknown[]): { valid: OraclePrice[]; - invalid: any[]; + invalid: unknown[]; errors: string[]; } { const valid: OraclePrice[] = []; - const invalid: any[] = []; + const invalid: unknown[] = []; const errors: string[] = []; if (!Array.isArray(prices)) { @@ -70,7 +70,7 @@ export class PriceDataValidator extends BaseValidator { prices.forEach((price, index) => { if (this.isValid(price)) { - valid.push(price); + valid.push(price as OraclePrice); } else { invalid.push(price); errors.push(`Invalid price at index ${index}`); diff --git a/frontend/src/utils/validators/ProviderHealthValidator.ts b/frontend/src/utils/validators/ProviderHealthValidator.ts index 12869156..fdfd3fec 100644 --- a/frontend/src/utils/validators/ProviderHealthValidator.ts +++ b/frontend/src/utils/validators/ProviderHealthValidator.ts @@ -3,7 +3,7 @@ import { BaseValidator, ExtendedValidationResult } from './BaseValidator'; import { CommonValidators } from './commonValidators'; export class ProviderHealthValidator extends BaseValidator { - isValid(health: any): boolean { + isValid(health: unknown): boolean { if (!CommonValidators.isValidObject(health)) return false; return ( @@ -17,7 +17,7 @@ export class ProviderHealthValidator extends BaseValidator { ); } - validate(health: any): ExtendedValidationResult { + validate(health: unknown): ExtendedValidationResult { const errors: string[] = []; const warnings: string[] = []; @@ -57,7 +57,7 @@ export class ProviderHealthValidator extends BaseValidator { }; } - sanitize(health: any): ProviderHealth | null { + sanitize(health: unknown): ProviderHealth | null { if (!CommonValidators.isValidObject(health)) return null; const sanitized: ProviderHealth = { diff --git a/frontend/src/utils/validators/commonValidators.ts b/frontend/src/utils/validators/commonValidators.ts index 460c2a38..1949d1c2 100644 --- a/frontend/src/utils/validators/commonValidators.ts +++ b/frontend/src/utils/validators/commonValidators.ts @@ -1,56 +1,56 @@ export class CommonValidators { - static isValidNumber(value: any): boolean { + static isValidNumber(value: unknown): boolean { return typeof value === 'number' && isFinite(value); } - static isValidPositiveNumber(value: any): boolean { - return this.isValidNumber(value) && value >= 0; + static isValidPositiveNumber(value: unknown): boolean { + return this.isValidNumber(value) && (value as number) >= 0; } - static isValidRatio(value: any): boolean { - return this.isValidNumber(value) && value >= 0 && value <= 1; + static isValidRatio(value: unknown): boolean { + return this.isValidNumber(value) && (value as number) >= 0 && (value as number) <= 1; } - static isValidTimestamp(timestamp: any): boolean { + static isValidTimestamp(timestamp: unknown): boolean { if (!this.isValidNumber(timestamp)) return false; const now = Date.now(); - return timestamp > 0 && timestamp <= now + 1000; + return (timestamp as number) > 0 && (timestamp as number) <= now + 1000; } - static isValidString(value: any, minLength: number = 1, maxLength?: number): boolean { + static isValidString(value: unknown, minLength: number = 1, maxLength?: number): boolean { if (typeof value !== 'string') return false; if (value.length < minLength) return false; if (maxLength && value.length > maxLength) return false; return true; } - static isValidArray(value: any, minLength: number = 0): boolean { + static isValidArray(value: unknown, minLength: number = 0): boolean { return Array.isArray(value) && value.length >= minLength; } - static isValidObject(value: any): boolean { + static isValidObject(value: unknown): value is Record { return typeof value === 'object' && value !== null; } - static sanitizeNumber(value: any, defaultValue: number = 0, min?: number, max?: number): number { + static sanitizeNumber(value: unknown, defaultValue: number = 0, min?: number, max?: number): number { let num = Number(value) || defaultValue; if (min !== undefined) num = Math.max(min, num); if (max !== undefined) num = Math.min(max, num); return num; } - static sanitizeString(value: any, defaultValue: string = '', maxLength?: number): string { + static sanitizeString(value: unknown, defaultValue: string = '', maxLength?: number): string { let str = String(value || defaultValue); if (maxLength) str = str.slice(0, maxLength); return str; } - static sanitizeTimestamp(value: any, defaultValue?: number): number { + static sanitizeTimestamp(value: unknown, defaultValue?: number): number { const timestamp = Number(value) || defaultValue || Date.now(); return Math.min(Date.now(), timestamp); } - static sanitizeArray(value: any, filter?: (item: any) => boolean): T[] { + static sanitizeArray(value: unknown, filter?: (item: unknown) => boolean): T[] { if (!Array.isArray(value)) return []; return filter ? value.filter(filter) : value; } diff --git a/frontend/src/utils/validators/types.ts b/frontend/src/utils/validators/types.ts index 2a01c0c1..e123a0c6 100644 --- a/frontend/src/utils/validators/types.ts +++ b/frontend/src/utils/validators/types.ts @@ -6,21 +6,21 @@ export interface ValidatorConfig { export interface ValidationContext { path?: string; - parent?: any; - root?: any; + parent?: unknown; + root?: unknown; } -export interface FieldValidationRule { +export interface FieldValidationRule { field: keyof T; - validator: (value: any) => boolean; + validator: (value: unknown) => boolean; errorMessage?: string; required?: boolean; } -export interface SanitizationRule { +export interface SanitizationRule { field: keyof T; - sanitizer: (value: any) => any; - defaultValue?: any; + sanitizer: (value: unknown) => unknown; + defaultValue?: unknown; } export type ValidatorFunction = (value: T) => boolean;