diff --git a/src/audit/ReportGenerator.ts b/src/audit/ReportGenerator.ts index 37bdf675..b1b1a02a 100644 --- a/src/audit/ReportGenerator.ts +++ b/src/audit/ReportGenerator.ts @@ -514,22 +514,20 @@ export class ReportGenerator { private static generateRecommendationsSection(recommendations: Recommendation[]): string { let md = `## Recommendations\n\n`; - const critical = recommendations.filter(r => r.severity === 'CRITICAL'); - const high = recommendations.filter(r => r.severity === 'HIGH'); - const medium = recommendations.filter(r => r.severity === 'MEDIUM'); - const low = recommendations.filter(r => r.severity === 'LOW'); - - for (const [severity, recs] of [ - ['CRITICAL', critical], - ['HIGH', high], - ['MEDIUM', medium], - ['LOW', low], - ]) { - if ((recs as any[]).length === 0) continue; - - md += `### ${severity} Priority (${(recs as any[]).length})\n\n`; - - for (const rec of (recs as any[]).slice(0, 10)) { + const categories: [string, Recommendation[]][] = [ + ['CRITICAL', recommendations.filter(r => r.severity === 'CRITICAL')], + ['HIGH', recommendations.filter(r => r.severity === 'HIGH')], + ['MEDIUM', recommendations.filter(r => r.severity === 'MEDIUM')], + ['LOW', recommendations.filter(r => r.severity === 'LOW')], + ]; + + for (const [severity, recs] of categories) { + // Corrected: Type-safe array check + if (recs.length === 0) continue; + + md += `### ${severity} Priority (${recs.length})\n\n`; + + for (const rec of recs.slice(0, 10)) { md += `#### ${rec.title}\n`; md += `${rec.description}\n\n`; md += `- **Impact:** ${rec.impact}\n`; diff --git a/src/services/mobilePayments.ts b/src/services/mobilePayments.ts index 22dccbf6..1aeee66a 100644 --- a/src/services/mobilePayments.ts +++ b/src/services/mobilePayments.ts @@ -15,10 +15,10 @@ import AsyncStorage from '@react-native-async-storage/async-storage'; import { Platform } from 'react-native'; import * as IAP from 'react-native-iap'; -import { apiService } from './api'; import { useAppStore } from '../store'; import { useDeviceStore } from '../store/deviceStore'; import { appLogger } from '../utils/logger'; +import { apiService } from './api'; // ─── Types ──────────────────────────────────────────────────────────────────── @@ -183,55 +183,41 @@ class MobilePaymentsService { const store = useAppStore.getState(); if (store.receiptValidationPending) { - appLogger.warnSync('[Payments] Receipt validation already in progress — skipping duplicate'); + appLogger.warnSync('[Payments] Receipt validation already in progress'); return; } store.setReceiptValidationPending(true); try { - const result = await this.validateReceipt( - receipt, - Platform.OS as 'ios' | 'android', - purchase.productId - ); + // SAFE: Explicit check for platform type instead of assertion + const platform = Platform.OS === 'ios' || Platform.OS === 'android' ? Platform.OS : 'ios'; + + const result = await this.validateReceipt(receipt, platform, purchase.productId); if (result.valid) { await IAP.finishTransaction({ purchase, isConsumable: false }); - if (result.tier) { - await this._setTier(result.tier); - } + if (result.tier) await this._setTier(result.tier); } else { appLogger.errorSync( '[Payments] Receipt rejected by server', - new Error(result.error ?? 'Receipt validation failed'), - { productId: purchase.productId } + new Error(result.error ?? 'Validation failed') ); } - } catch (error) { - appLogger.errorSync( - '[Payments] Receipt validation failed after retries — purchase not completed', - error instanceof Error ? error : new Error(String(error)), - { productId: purchase.productId } - ); + } catch (error: unknown) { + // SAFE: Runtime error type guard + const err = error instanceof Error ? error : new Error(String(error)); + appLogger.errorSync('[Payments] Receipt validation failed', err, { + productId: purchase.productId, + }); } finally { - useAppStore.getState().setReceiptValidationPending(false); + store.setReceiptValidationPending(false); } }); - - IAP.purchaseErrorListener(error => { - appLogger.errorSync( - '[Payments] Purchase error', - error instanceof Error ? error : new Error(String(error)) - ); - }); - - this.isInitialized = true; - } catch (error) { + } catch (e) { appLogger.errorSync( - '[Payments] Initialize error', - error instanceof Error ? error : new Error(String(error)) + '[Payments] Failed to init IAP', + e instanceof Error ? e : new Error(String(e)) ); - throw error; } } diff --git a/src/store/{slices}/courseProgressStore.ts b/src/store/{slices}/courseProgressStore.ts index 9ef3b9df..44e17c58 100644 --- a/src/store/{slices}/courseProgressStore.ts +++ b/src/store/{slices}/courseProgressStore.ts @@ -36,7 +36,7 @@ function emitCourseCompleted(courseId: string) { async function updateProgressOnServer( courseId: string, payload: { completedLessons: LessonProgress[]; isCompleted: boolean; completedAt?: string }, - retries = 3, + retries = 3 ): Promise { let lastError: unknown; @@ -144,4 +144,4 @@ export const useCourseProgressStore = create((set, get) => showErrorToast('Could not save your progress. Please check your connection and try again.'); } }, -})); \ No newline at end of file +}));