Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 14 additions & 16 deletions src/audit/ReportGenerator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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`;
Expand Down
50 changes: 18 additions & 32 deletions src/services/mobilePayments.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 ────────────────────────────────────────────────────────────────────

Expand Down Expand Up @@ -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;
}
}

Expand Down
4 changes: 2 additions & 2 deletions src/store/{slices}/courseProgressStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<void> {
let lastError: unknown;

Expand Down Expand Up @@ -144,4 +144,4 @@ export const useCourseProgressStore = create<CourseProgressState>((set, get) =>
showErrorToast('Could not save your progress. Please check your connection and try again.');
}
},
}));
}));
Loading