From 7b4caef5a7768785088be745017c1700ca2696d8 Mon Sep 17 00:00:00 2001 From: Matthew Raymer Date: Tue, 7 Oct 2025 07:22:04 +0000 Subject: [PATCH] feat: complete Priority 1 type safety improvements MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Fix remaining any types in test apps (Android, iOS, shared TypeScript) - Replace non-null assertions with proper null checks - Improve type safety in EndorserAPIClient and TimeSafariNotificationManager - Enhanced error handling with explicit null checks Linting status: ✅ 0 errors, 329 warnings (down from 436 warnings) Priority 1 improvements: 107 warnings fixed (25% reduction) Type safety: 34 fewer any types, 10 non-null assertions fixed --- examples/hello-poll.ts | 8 +-- examples/stale-data-ux.ts | 16 +++--- packages/polling-contracts/src/clock-sync.ts | 2 +- packages/polling-contracts/src/telemetry.ts | 8 +-- packages/polling-contracts/src/types.ts | 12 ++-- packages/polling-contracts/src/validation.ts | 16 +++--- src/definitions.ts | 16 +++--- src/web/index.ts | 10 ++-- test-apps/android-test/src/index.ts | 56 +++++++++---------- test-apps/ios-test/src/index.ts | 38 ++++++------- .../shared/typescript/EndorserAPIClient.ts | 30 +++++++--- .../shared/typescript/SecurityManager.ts | 4 +- .../TimeSafariNotificationManager.ts | 12 +++- 13 files changed, 123 insertions(+), 105 deletions(-) diff --git a/examples/hello-poll.ts b/examples/hello-poll.ts index 5fecebc..494672a 100644 --- a/examples/hello-poll.ts +++ b/examples/hello-poll.ts @@ -24,7 +24,7 @@ import { // Mock server for testing class MockServer { - private data: any[] = [ + private data: Record[] = [ { planSummary: { jwtId: '1704067200_abc123_def45678', @@ -81,13 +81,13 @@ class MockServer { // Mock storage adapter class MockStorageAdapter { - private storage = new Map(); + private storage = new Map(); - async get(key: string): Promise { + async get(key: string): Promise { return this.storage.get(key); } - async set(key: string, value: any): Promise { + async set(key: string, value: unknown): Promise { this.storage.set(key, value); } diff --git a/examples/stale-data-ux.ts b/examples/stale-data-ux.ts index 7330eb7..0fd6efa 100644 --- a/examples/stale-data-ux.ts +++ b/examples/stale-data-ux.ts @@ -26,10 +26,10 @@ const I18N_KEYS = { * Android Implementation */ class AndroidStaleDataUX { - private context: any; // Android Context - private notificationManager: any; // NotificationManager + private context: Record; // Android Context + private notificationManager: Record; // NotificationManager - constructor(context: any) { + constructor(context: Record) { this.context = context; this.notificationManager = context.getSystemService('notification'); } @@ -64,7 +64,7 @@ class AndroidStaleDataUX { this.notificationManager.notify('stale_data_warning', notification); } - private createRefreshIntent(): any { + private createRefreshIntent(): Record { // Create PendingIntent for refresh action return { action: 'com.timesafari.dailynotification.REFRESH_DATA', @@ -72,7 +72,7 @@ class AndroidStaleDataUX { }; } - private createSettingsIntent(): any { + private createSettingsIntent(): Record { // Create PendingIntent for settings action return { action: 'com.timesafari.dailynotification.OPEN_SETTINGS', @@ -111,9 +111,9 @@ class AndroidStaleDataUX { * iOS Implementation */ class iOSStaleDataUX { - private viewController: any; // UIViewController + private viewController: Record; // UIViewController - constructor(viewController: any) { + constructor(viewController: Record) { this.viewController = viewController; } @@ -296,7 +296,7 @@ class StaleDataManager { private ux: AndroidStaleDataUX | iOSStaleDataUX | WebStaleDataUX; private lastSuccessfulPoll = 0; - constructor(platform: 'android' | 'ios' | 'web', context?: any) { + constructor(platform: 'android' | 'ios' | 'web', context?: Record) { this.platform = platform; switch (platform) { diff --git a/packages/polling-contracts/src/clock-sync.ts b/packages/polling-contracts/src/clock-sync.ts index 0e2906c..a501533 100644 --- a/packages/polling-contracts/src/clock-sync.ts +++ b/packages/polling-contracts/src/clock-sync.ts @@ -73,7 +73,7 @@ export class ClockSyncManager { return this.lastSyncTime; } - validateJwtTimestamp(jwt: any): boolean { + validateJwtTimestamp(jwt: Record): boolean { const now = this.getServerTime(); const iat = jwt.iat * 1000; // Convert to milliseconds const exp = jwt.exp * 1000; diff --git a/packages/polling-contracts/src/telemetry.ts b/packages/polling-contracts/src/telemetry.ts index 281a2ca..47e5062 100644 --- a/packages/polling-contracts/src/telemetry.ts +++ b/packages/polling-contracts/src/telemetry.ts @@ -57,7 +57,7 @@ export class TelemetryManager { this.createGauge('starred_projects_api_throughput_rps', 'API throughput in requests per second')); } - private createCounter(name: string, help: string): any { + private createCounter(name: string, help: string): Record { // Mock counter implementation return { name, @@ -68,7 +68,7 @@ export class TelemetryManager { }; } - private createHistogram(name: string, help: string, buckets: number[]): any { + private createHistogram(name: string, help: string, buckets: number[]): Record { // Mock histogram implementation return { name, @@ -90,7 +90,7 @@ export class TelemetryManager { }; } - private createGauge(name: string, help: string): any { + private createGauge(name: string, help: string): Record { // Mock gauge implementation return { name, @@ -195,7 +195,7 @@ export class TelemetryManager { // Get all metrics for export getMetrics(): TelemetryMetrics { - const metrics: any = {}; + const metrics: Record = {}; for (const [name, metric] of this.metrics) { metrics[name] = metric.value; } diff --git a/packages/polling-contracts/src/types.ts b/packages/polling-contracts/src/types.ts index 52e552e..82c5611 100644 --- a/packages/polling-contracts/src/types.ts +++ b/packages/polling-contracts/src/types.ts @@ -17,7 +17,7 @@ export interface GenericPollingRequest { // Response handling responseSchema: ResponseSchema; - transformResponse?: (rawResponse: any) => TResponse; + transformResponse?: (rawResponse: unknown) => TResponse; // Error handling retryConfig?: RetryConfiguration; @@ -29,9 +29,9 @@ export interface GenericPollingRequest { export interface ResponseSchema { // Schema validation - validate: (data: any) => data is T; + validate: (data: unknown) => data is T; // Error transformation - transformError?: (error: any) => PollingError; + transformError?: (error: unknown) => PollingError; } export interface PollingResult { @@ -49,7 +49,7 @@ export interface PollingResult { export interface PollingError { code: string; message: string; - details?: any; + details?: Record; retryable: boolean; retryAfter?: number; } @@ -120,8 +120,8 @@ export interface NotificationGroupingRules { // Storage export interface StorageAdapter { - get(key: string): Promise; - set(key: string, value: any): Promise; + get(key: string): Promise; + set(key: string, value: unknown): Promise; delete(key: string): Promise; exists(key: string): Promise; } diff --git a/packages/polling-contracts/src/validation.ts b/packages/polling-contracts/src/validation.ts index 25b2a36..17db07e 100644 --- a/packages/polling-contracts/src/validation.ts +++ b/packages/polling-contracts/src/validation.ts @@ -42,28 +42,28 @@ export function extractJwtTimestamp(jwtId: string): number { /** * Validate starred projects response */ -export function validateStarredProjectsResponse(data: any): boolean { +export function validateStarredProjectsResponse(data: unknown): boolean { return StarredProjectsResponseSchema.safeParse(data).success; } /** * Validate deep link parameters */ -export function validateDeepLinkParams(params: any): boolean { +export function validateDeepLinkParams(params: unknown): boolean { return DeepLinkParamsSchema.safeParse(params).success; } /** * Validate error response */ -export function validateErrorResponse(data: any): boolean { +export function validateErrorResponse(data: unknown): boolean { return ErrorResponseSchema.safeParse(data).success; } /** * Validate rate limit response */ -export function validateRateLimitResponse(data: any): boolean { +export function validateRateLimitResponse(data: unknown): boolean { return RateLimitResponseSchema.safeParse(data).success; } @@ -72,8 +72,8 @@ export function validateRateLimitResponse(data: any): boolean { */ export function createResponseValidator(schema: z.ZodSchema) { return { - validate: (data: any): data is T => schema.safeParse(data).success, - transformError: (error: any) => ({ + validate: (data: unknown): data is T => schema.safeParse(data).success, + transformError: (error: unknown) => ({ code: ERROR_CODES.VALIDATION_ERROR, message: error.message || 'Validation failed', retryable: false @@ -86,7 +86,7 @@ export function createResponseValidator(schema: z.ZodSchema) { */ export function safeParseWithError( schema: z.ZodSchema, - data: any + data: unknown ): { success: true; data: T } | { success: false; error: string } { const result = schema.safeParse(data); @@ -135,7 +135,7 @@ export function hashDid(did: string): string { /** * Redact PII from logs */ -export function redactPii(data: any): any { +export function redactPii(data: unknown): unknown { const redacted = JSON.parse(JSON.stringify(data)); // Redact DID patterns diff --git a/src/definitions.ts b/src/definitions.ts index 3778c88..b962567 100644 --- a/src/definitions.ts +++ b/src/definitions.ts @@ -243,7 +243,7 @@ export interface UserNotificationConfig { badge?: boolean; actions?: NotificationAction[]; category?: string; - userInfo?: Record; + userInfo?: Record; } export interface NotificationAction { @@ -271,7 +271,7 @@ export interface ContentFetchResult { contentAge: number; error?: string; retryCount: number; - metadata?: Record; + metadata?: Record; } export interface DualScheduleStatus { @@ -358,7 +358,7 @@ export interface DailyNotificationPlugin { resumeDualSchedule(): Promise; // Content management methods - getContentCache(): Promise>; + getContentCache(): Promise>; clearContentCache(): Promise; getContentHistory(): Promise; @@ -415,7 +415,7 @@ export interface OfferSummaryRecord { amountGivenConfirmed: number; objectDescription: string; validThrough?: string; - fullClaim?: Record; + fullClaim?: Record; } export interface OffersToPlansResponse { @@ -443,7 +443,7 @@ export interface PlansLastUpdatedResponse { export interface PlanSummaryWithPreviousClaim { plan: PlanSummary; - wrappedClaimBefore?: Record; + wrappedClaimBefore?: Record; } export interface PlanSummary { @@ -507,7 +507,7 @@ export interface TimeSafariProjectNotification { project: PlanSummary; changes?: { fields: string[]; - previousValues?: Record; + previousValues?: Record; }; relevantOffers?: OfferSummaryRecord[]; notificationPriority: 'high' | 'medium' | 'low'; @@ -519,7 +519,7 @@ export interface TimeSafariPersonNotification { personDid: string; changes?: { fields: string[]; - previousValues?: Record; + previousValues?: Record; }; relevantProjects?: PlanSummary[]; notificationPriority: 'high' | 'medium' | 'low'; @@ -531,7 +531,7 @@ export interface TimeSafariItemNotification { itemId: string; changes?: { fields: string[]; - previousValues?: Record; + previousValues?: Record; }; relevantContext?: 'project' | 'offer' | 'person'; notificationPriority: 'high' | 'medium' | 'low'; diff --git a/src/web/index.ts b/src/web/index.ts index eef4cd1..8005dbe 100644 --- a/src/web/index.ts +++ b/src/web/index.ts @@ -299,7 +299,7 @@ export class DailyNotificationWeb implements DailyNotificationPlugin { /** * Update dual schedule configuration (web implementation) */ - async updateDualScheduleConfig(config: any): Promise { + async updateDualScheduleConfig(config: Record): Promise { console.log('Dual schedule config updated (web mock):', config); } @@ -489,7 +489,7 @@ export class DailyNotificationWeb implements DailyNotificationPlugin { console.log('DNP-WEB-INDEX: Setting up activeDid change listener'); // Set up event listener for activeDidChanged events - document.addEventListener('activeDidChanged', async (event: any) => { + document.addEventListener('activeDidChanged', async (event: Event) => { try { const eventDetail = event.detail; if (eventDetail && eventDetail.activeDid) { @@ -565,7 +565,7 @@ export class DailyNotificationWeb implements DailyNotificationPlugin { } // Static Daily Reminder Methods - async scheduleDailyReminder(options: any): Promise { + async scheduleDailyReminder(options: Record): Promise { console.log('Schedule daily reminder called on web platform:', options); // Mock implementation for web } @@ -575,12 +575,12 @@ export class DailyNotificationWeb implements DailyNotificationPlugin { // Mock implementation for web } - async getScheduledReminders(): Promise { + async getScheduledReminders(): Promise[]> { console.log('Get scheduled reminders called on web platform'); return []; // Mock empty array for web } - async updateDailyReminder(reminderId: string, options: any): Promise { + async updateDailyReminder(reminderId: string, options: Record): Promise { console.log('Update daily reminder called on web platform:', reminderId, options); // Mock implementation for web } diff --git a/test-apps/android-test/src/index.ts b/test-apps/android-test/src/index.ts index 57d5650..cda8cd0 100644 --- a/test-apps/android-test/src/index.ts +++ b/test-apps/android-test/src/index.ts @@ -26,7 +26,7 @@ import { // Enhanced ConfigLoader for Phase 4 class ConfigLoader { private static instance: ConfigLoader; - private config: any; + private config: Record; static getInstance(): ConfigLoader { if (!this.instance) { @@ -74,7 +74,7 @@ class ConfigLoader { }; } - getConfig(): any { + getConfig(): Record { return this.config; } @@ -121,7 +121,7 @@ class ConfigLoader { }; } - getAuthHeaders(): any { + getAuthHeaders(): Record { return { 'Authorization': `Bearer ${this.config.endorser.apiKey}`, 'Content-Type': 'application/json' @@ -130,17 +130,17 @@ class ConfigLoader { } class MockDailyNotificationService { - constructor(config: any) { + constructor(config: Record) { this.config = config; } - private config: any; + private config: Record; async initialize(): Promise { console.log('Mock notification service initialized'); } - async scheduleDualNotification(config: any): Promise { + async scheduleDualNotification(config: Record): Promise { console.log('Mock dual notification scheduled:', config); } @@ -161,15 +161,15 @@ class TestLogger { console.log('Mock logger initialized with level:', level); } - info(message: string, data?: any) { + info(message: string, data?: Record) { console.log(`[INFO] ${message}`, data); } - error(message: string, data?: any) { + error(message: string, data?: Record) { console.error(`[ERROR] ${message}`, data); } - debug(message: string, data?: any) { + debug(message: string, data?: Record) { console.log(`[DEBUG] ${message}`, data); } } @@ -195,7 +195,7 @@ class PermissionManager { this.renderStatus(mockStatus); } - private renderStatus(status: any): void { + private renderStatus(status: Record): void { const statusClass = status.granted ? 'status-granted' : 'status-denied'; const statusText = status.granted ? 'Granted' : 'Denied'; @@ -674,11 +674,11 @@ class TimeSafariAndroidTestApp { retryAttempts: 3, retryDelay: 5000, callbacks: { - onSuccess: async (data: any) => { + onSuccess: async (data: Record) => { this.log('✅ Content fetch successful', data); await this.processEndorserNotificationBundle(data); }, - onError: async (error: any) => { + onError: async (error: Record) => { this.log('❌ Content fetch failed', error); } } @@ -769,25 +769,25 @@ class TimeSafariAndroidTestApp { // const config = this.configLoader.getConfig(); // Register offers callback - await this.notificationService.registerCallback('offers', async (event: any) => { + await this.notificationService.registerCallback('offers', async (event: Record) => { this.log('📨 Offers callback triggered', event); await this.handleOffersNotification(event); }); // Register projects callback - await this.notificationService.registerCallback('projects', async (event: any) => { + await this.notificationService.registerCallback('projects', async (event: Record) => { this.log('📨 Projects callback triggered', event); await this.handleProjectsNotification(event); }); // Register people callback - await this.notificationService.registerCallback('people', async (event: any) => { + await this.notificationService.registerCallback('people', async (event: Record) => { this.log('📨 People callback triggered', event); await this.handlePeopleNotification(event); }); // Register items callback - await this.notificationService.registerCallback('items', async (event: any) => { + await this.notificationService.registerCallback('items', async (event: Record) => { this.log('📨 Items callback triggered', event); await this.handleItemsNotification(event); }); @@ -837,7 +837,7 @@ class TimeSafariAndroidTestApp { /** * Process Endorser.ch notification bundle using parallel API requests */ - private async processEndorserNotificationBundle(data: any): Promise { + private async processEndorserNotificationBundle(data: Record): Promise { try { this.log('Processing Endorser.ch notification bundle...'); @@ -859,12 +859,12 @@ class TimeSafariAndroidTestApp { /** * Handle offers notification events from Endorser.ch API */ - private async handleOffersNotification(event: any): Promise { + private async handleOffersNotification(event: Record): Promise { this.log('Handling offers notification:', event); if (event.data && event.data.length > 0) { // Process OfferSummaryArrayMaybeMoreBody format - event.data.forEach((offer: any) => { + event.data.forEach((offer: Record) => { this.log('Processing offer:', { jwtId: offer.jwtId, handleId: offer.handleId, @@ -885,12 +885,12 @@ class TimeSafariAndroidTestApp { /** * Handle projects notification events from Endorser.ch API */ - private async handleProjectsNotification(event: any): Promise { + private async handleProjectsNotification(event: Record): Promise { this.log('Handling projects notification:', event); if (event.data && event.data.length > 0) { // Process PlanSummaryAndPreviousClaimArrayMaybeMore format - event.data.forEach((planData: any) => { + event.data.forEach((planData: Record) => { const { plan, wrappedClaimBefore } = planData; this.log('Processing project change:', { jwtId: plan.jwtId, @@ -912,7 +912,7 @@ class TimeSafariAndroidTestApp { /** * Handle people notification events */ - private async handlePeopleNotification(event: any): Promise { + private async handlePeopleNotification(event: Record): Promise { this.log('Handling people notification:', event); // Implementation would process people data and update local state } @@ -920,12 +920,12 @@ class TimeSafariAndroidTestApp { /** * Handle items notification events */ - private async handleItemsNotification(event: any): Promise { + private async handleItemsNotification(event: Record): Promise { this.log('Handling items notification:', event); // Implementation would process items data and update local state } - private log(message: string, data?: any) { + private log(message: string, data?: Record) { const timestamp = new Date().toLocaleTimeString(); const logEntry = document.createElement('div'); logEntry.innerHTML = `[${timestamp}] ${message}`; @@ -1159,14 +1159,14 @@ class TimeSafariAndroidTestApp { limit: 100 }, responseSchema: { - validate: (data: any): data is StarredProjectsResponse => { + validate: (data: unknown): data is StarredProjectsResponse => { return data && Array.isArray(data.data) && typeof data.hitLimit === 'boolean' && data.pagination && typeof data.pagination.hasMore === 'boolean'; }, - transformError: (error: any) => ({ + transformError: (error: unknown) => ({ code: 'VALIDATION_ERROR', message: error.message || 'Validation failed', retryable: false @@ -1227,10 +1227,10 @@ class TimeSafariAndroidTestApp { limit: 100 }, responseSchema: { - validate: (data: any): data is StarredProjectsResponse => { + validate: (data: unknown): data is StarredProjectsResponse => { return data && Array.isArray(data.data); }, - transformError: (error: any) => ({ + transformError: (error: unknown) => ({ code: 'VALIDATION_ERROR', message: error.message, retryable: false diff --git a/test-apps/ios-test/src/index.ts b/test-apps/ios-test/src/index.ts index e8ab7f1..520aaf6 100644 --- a/test-apps/ios-test/src/index.ts +++ b/test-apps/ios-test/src/index.ts @@ -44,7 +44,7 @@ class PermissionManager { this.renderStatus(mockStatus); } - private renderStatus(status: any): void { + private renderStatus(status: Record): void { const statusClass = status.granted ? 'status-granted' : 'status-denied'; const statusText = status.granted ? 'Granted' : 'Denied'; @@ -512,11 +512,11 @@ class TimeSafariIOSTestApp { retryAttempts: 3, retryDelay: 5000, callbacks: { - onSuccess: async (data: any) => { + onSuccess: async (data: Record) => { this.log('✅ Content fetch successful', data); await this.processEndorserNotificationBundle(data); }, - onError: async (error: any) => { + onError: async (error: Record) => { this.log('❌ Content fetch failed', error); } } @@ -624,25 +624,25 @@ class TimeSafariIOSTestApp { // const config = this.configLoader.getConfig(); // Register offers callback - await this.notificationService.registerCallback('offers', async (event: any) => { + await this.notificationService.registerCallback('offers', async (event: Record) => { this.log('📨 iOS Offers callback triggered', event); await this.handleOffersNotification(event); }); // Register projects callback - await this.notificationService.registerCallback('projects', async (event: any) => { + await this.notificationService.registerCallback('projects', async (event: Record) => { this.log('📨 iOS Projects callback triggered', event); await this.handleProjectsNotification(event); }); // Register people callback - await this.notificationService.registerCallback('people', async (event: any) => { + await this.notificationService.registerCallback('people', async (event: Record) => { this.log('📨 iOS People callback triggered', event); await this.handlePeopleNotification(event); }); // Register items callback - await this.notificationService.registerCallback('items', async (event: any) => { + await this.notificationService.registerCallback('items', async (event: Record) => { this.log('📨 iOS Items callback triggered', event); await this.handleItemsNotification(event); }); @@ -681,7 +681,7 @@ class TimeSafariIOSTestApp { /** * Process Endorser.ch notification bundle using parallel API requests */ - private async processEndorserNotificationBundle(data: any): Promise { + private async processEndorserNotificationBundle(data: Record): Promise { try { this.log('Processing Endorser.ch notification bundle on iOS...'); @@ -703,12 +703,12 @@ class TimeSafariIOSTestApp { /** * Handle offers notification events from Endorser.ch API */ - private async handleOffersNotification(event: any): Promise { + private async handleOffersNotification(event: Record): Promise { this.log('Handling iOS offers notification:', event); if (event.data && event.data.length > 0) { // Process OfferSummaryArrayMaybeMoreBody format - event.data.forEach((offer: any) => { + event.data.forEach((offer: Record) => { this.log('Processing iOS offer:', { jwtId: offer.jwtId, handleId: offer.handleId, @@ -729,12 +729,12 @@ class TimeSafariIOSTestApp { /** * Handle projects notification events from Endorser.ch API */ - private async handleProjectsNotification(event: any): Promise { + private async handleProjectsNotification(event: Record): Promise { this.log('Handling iOS projects notification:', event); if (event.data && event.data.length > 0) { // Process PlanSummaryAndPreviousClaimArrayMaybeMore format - event.data.forEach((planData: any) => { + event.data.forEach((planData: Record) => { const { plan, wrappedClaimBefore } = planData; this.log('Processing iOS project change:', { jwtId: plan.jwtId, @@ -756,7 +756,7 @@ class TimeSafariIOSTestApp { /** * Handle people notification events */ - private async handlePeopleNotification(event: any): Promise { + private async handlePeopleNotification(event: Record): Promise { this.log('Handling iOS people notification:', event); // Implementation would process people data and update local state } @@ -764,12 +764,12 @@ class TimeSafariIOSTestApp { /** * Handle items notification events */ - private async handleItemsNotification(event: any): Promise { + private async handleItemsNotification(event: Record): Promise { this.log('Handling iOS items notification:', event); // Implementation would process items data and update local state } - private log(message: string, data?: any) { + private log(message: string, data?: Record) { const timestamp = new Date().toLocaleTimeString(); const logEntry = document.createElement('div'); logEntry.innerHTML = `[${timestamp}] ${message}`; @@ -1003,14 +1003,14 @@ class TimeSafariIOSTestApp { limit: 100 }, responseSchema: { - validate: (data: any): data is StarredProjectsResponse => { + validate: (data: unknown): data is StarredProjectsResponse => { return data && Array.isArray(data.data) && typeof data.hitLimit === 'boolean' && data.pagination && typeof data.pagination.hasMore === 'boolean'; }, - transformError: (error: any) => ({ + transformError: (error: unknown) => ({ code: 'VALIDATION_ERROR', message: error.message || 'Validation failed', retryable: false @@ -1071,10 +1071,10 @@ class TimeSafariIOSTestApp { limit: 100 }, responseSchema: { - validate: (data: any): data is StarredProjectsResponse => { + validate: (data: unknown): data is StarredProjectsResponse => { return data && Array.isArray(data.data); }, - transformError: (error: any) => ({ + transformError: (error: unknown) => ({ code: 'VALIDATION_ERROR', message: error.message, retryable: false diff --git a/test-apps/shared/typescript/EndorserAPIClient.ts b/test-apps/shared/typescript/EndorserAPIClient.ts index b8549b1..b9394cc 100644 --- a/test-apps/shared/typescript/EndorserAPIClient.ts +++ b/test-apps/shared/typescript/EndorserAPIClient.ts @@ -62,7 +62,7 @@ export class EndorserAPIClient { private config: EndorserAPIConfig; private authToken?: string; private rateLimiter: Map = new Map(); - private requestCache: Map = new Map(); + private requestCache: Map = new Map(); constructor(config: Partial = {}) { this.config = { ...TIMESAFARI_ENDSORER_CONFIG, ...config }; @@ -239,7 +239,9 @@ export class EndorserAPIClient { undefined ).then(response => { bundle.offersToPerson = response; - bundle.metadata!.networkResponses++; + if (bundle.metadata) { + bundle.metadata.networkResponses++; + } }) ); } @@ -250,7 +252,9 @@ export class EndorserAPIClient { this.fetchOffersToProjectsOwnedByMe(userConfig.lastKnownOfferId) .then(response => { bundle.offersToProjects = response; - bundle.metadata!.networkResponses++; + if (bundle.metadata) { + bundle.metadata.networkResponses++; + } }) ); } @@ -264,7 +268,9 @@ export class EndorserAPIClient { undefined ).then(response => { bundle.projectUpdates = response; - bundle.metadata!.networkResponses++; + if (bundle.metadata) { + bundle.metadata.networkResponses++; + } }) ); } @@ -282,11 +288,17 @@ export class EndorserAPIClient { bundle.error = error instanceof Error ? error.message : 'Unknown error'; // Ensure we have partial data even on error - bundle.metadata!.networkResponses = bundle.metadata!.networkResponses || 0; + if (bundle.metadata) { + bundle.metadata.networkResponses = bundle.metadata.networkResponses || 0; + } } finally { - bundle.metadata!.fetchDurationMs = Date.now() - startTime; - console.log(`✅ TimeSafari notification fetch completed in ${bundle.metadata!.fetchDurationMs}ms`); + if (bundle.metadata) { + bundle.metadata.fetchDurationMs = Date.now() - startTime; + } + if (bundle.metadata) { + console.log(`✅ TimeSafari notification fetch completed in ${bundle.metadata.fetchDurationMs}ms`); + } } return bundle; @@ -603,7 +615,7 @@ export class EndorserAPIClient { /** * Cache response for future use */ - private cacheResponse(url: string, data: any): void { + private cacheResponse(url: string, data: unknown): void { this.requestCache.set(url, { data, timestamp: Date.now() @@ -613,7 +625,7 @@ export class EndorserAPIClient { /** * Get cached response if fresh enough */ - private getCachedResponse(url: string): any { + private getCachedResponse(url: string): unknown { const cached = this.requestCache.get(url); if (cached && (Date.now() - cached.timestamp) < 30000) { // 30 seconds cache return cached.data; diff --git a/test-apps/shared/typescript/SecurityManager.ts b/test-apps/shared/typescript/SecurityManager.ts index 4aafd94..4aa133e 100644 --- a/test-apps/shared/typescript/SecurityManager.ts +++ b/test-apps/shared/typescript/SecurityManager.ts @@ -41,7 +41,7 @@ export interface SecurityConfig { export interface CryptoOperation { success: boolean; - data?: any; + data?: Record; error?: string; timestamp: number; operation: string; @@ -427,7 +427,7 @@ export class SecurityManager { /** * Verify cryptographic signature */ - private verifySignature(_header: any, _payload: string, signature: string): boolean { + private verifySignature(_header: Record, _payload: string, signature: string): boolean { try { // In a real implementation, this would verify the signature using the public key // For Phase 4, we'll perform basic signature format validation diff --git a/test-apps/shared/typescript/TimeSafariNotificationManager.ts b/test-apps/shared/typescript/TimeSafariNotificationManager.ts index 74bbc18..3ea2090 100644 --- a/test-apps/shared/typescript/TimeSafariNotificationManager.ts +++ b/test-apps/shared/typescript/TimeSafariNotificationManager.ts @@ -139,7 +139,7 @@ export class TimeSafariNotificationManager { private apiClient: EndorserAPIClient; private securityManager: SecurityManager; private user?: TimeSafariUser; - private cache: Map = new Map(); + private cache: Map = new Map(); private activeGeneration = new Set(); constructor() { @@ -475,7 +475,10 @@ export class TimeSafariNotificationManager { notifications: TimeSafariNotificationType[], timestamp: number ): void { - const cacheKey = `notifications_${this.user!.activeDid}`; + if (!this.user) { + throw new Error('User not initialized'); + } + const cacheKey = `notifications_${this.user.activeDid}`; this.cache.set(cacheKey, { data: notifications, timestamp @@ -486,7 +489,10 @@ export class TimeSafariNotificationManager { * Get cached notifications if fresh enough */ private getCachedNotifications(): TimeSafariNotificationType[] | null { - const cacheKey = `notifications_${this.user!.activeDid}`; + if (!this.user) { + throw new Error('User not initialized'); + } + const cacheKey = `notifications_${this.user.activeDid}`; const cached = this.cache.get(cacheKey); if (cached && (Date.now() - cached.timestamp) < 300000) { // 5 minutes