Browse Source

feat: complete Priority 1 type safety improvements

- 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
master
Matthew Raymer 4 days ago
parent
commit
7b4caef5a7
  1. 8
      examples/hello-poll.ts
  2. 16
      examples/stale-data-ux.ts
  3. 2
      packages/polling-contracts/src/clock-sync.ts
  4. 8
      packages/polling-contracts/src/telemetry.ts
  5. 12
      packages/polling-contracts/src/types.ts
  6. 16
      packages/polling-contracts/src/validation.ts
  7. 16
      src/definitions.ts
  8. 10
      src/web/index.ts
  9. 56
      test-apps/android-test/src/index.ts
  10. 38
      test-apps/ios-test/src/index.ts
  11. 30
      test-apps/shared/typescript/EndorserAPIClient.ts
  12. 4
      test-apps/shared/typescript/SecurityManager.ts
  13. 12
      test-apps/shared/typescript/TimeSafariNotificationManager.ts

8
examples/hello-poll.ts

@ -24,7 +24,7 @@ import {
// Mock server for testing
class MockServer {
private data: any[] = [
private data: Record<string, unknown>[] = [
{
planSummary: {
jwtId: '1704067200_abc123_def45678',
@ -81,13 +81,13 @@ class MockServer {
// Mock storage adapter
class MockStorageAdapter {
private storage = new Map<string, any>();
private storage = new Map<string, unknown>();
async get(key: string): Promise<any> {
async get(key: string): Promise<unknown> {
return this.storage.get(key);
}
async set(key: string, value: any): Promise<void> {
async set(key: string, value: unknown): Promise<void> {
this.storage.set(key, value);
}

16
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<string, unknown>; // Android Context
private notificationManager: Record<string, unknown>; // NotificationManager
constructor(context: any) {
constructor(context: Record<string, unknown>) {
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<string, unknown> {
// Create PendingIntent for refresh action
return {
action: 'com.timesafari.dailynotification.REFRESH_DATA',
@ -72,7 +72,7 @@ class AndroidStaleDataUX {
};
}
private createSettingsIntent(): any {
private createSettingsIntent(): Record<string, unknown> {
// 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<string, unknown>; // UIViewController
constructor(viewController: any) {
constructor(viewController: Record<string, unknown>) {
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<string, unknown>) {
this.platform = platform;
switch (platform) {

2
packages/polling-contracts/src/clock-sync.ts

@ -73,7 +73,7 @@ export class ClockSyncManager {
return this.lastSyncTime;
}
validateJwtTimestamp(jwt: any): boolean {
validateJwtTimestamp(jwt: Record<string, unknown>): boolean {
const now = this.getServerTime();
const iat = jwt.iat * 1000; // Convert to milliseconds
const exp = jwt.exp * 1000;

8
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<string, unknown> {
// 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<string, unknown> {
// 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<string, unknown> {
// 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<string, unknown> = {};
for (const [name, metric] of this.metrics) {
metrics[name] = metric.value;
}

12
packages/polling-contracts/src/types.ts

@ -17,7 +17,7 @@ export interface GenericPollingRequest<TRequest, TResponse> {
// Response handling
responseSchema: ResponseSchema<TResponse>;
transformResponse?: (rawResponse: any) => TResponse;
transformResponse?: (rawResponse: unknown) => TResponse;
// Error handling
retryConfig?: RetryConfiguration;
@ -29,9 +29,9 @@ export interface GenericPollingRequest<TRequest, TResponse> {
export interface ResponseSchema<T> {
// 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<T> {
@ -49,7 +49,7 @@ export interface PollingResult<T> {
export interface PollingError {
code: string;
message: string;
details?: any;
details?: Record<string, unknown>;
retryable: boolean;
retryAfter?: number;
}
@ -120,8 +120,8 @@ export interface NotificationGroupingRules {
// Storage
export interface StorageAdapter {
get(key: string): Promise<any>;
set(key: string, value: any): Promise<void>;
get(key: string): Promise<unknown>;
set(key: string, value: unknown): Promise<void>;
delete(key: string): Promise<void>;
exists(key: string): Promise<boolean>;
}

16
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<T>(schema: z.ZodSchema<T>) {
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<T>(schema: z.ZodSchema<T>) {
*/
export function safeParseWithError<T>(
schema: z.ZodSchema<T>,
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

16
src/definitions.ts

@ -243,7 +243,7 @@ export interface UserNotificationConfig {
badge?: boolean;
actions?: NotificationAction[];
category?: string;
userInfo?: Record<string, any>;
userInfo?: Record<string, unknown>;
}
export interface NotificationAction {
@ -271,7 +271,7 @@ export interface ContentFetchResult {
contentAge: number;
error?: string;
retryCount: number;
metadata?: Record<string, any>;
metadata?: Record<string, unknown>;
}
export interface DualScheduleStatus {
@ -358,7 +358,7 @@ export interface DailyNotificationPlugin {
resumeDualSchedule(): Promise<void>;
// Content management methods
getContentCache(): Promise<Record<string, any>>;
getContentCache(): Promise<Record<string, unknown>>;
clearContentCache(): Promise<void>;
getContentHistory(): Promise<ContentFetchResult[]>;
@ -415,7 +415,7 @@ export interface OfferSummaryRecord {
amountGivenConfirmed: number;
objectDescription: string;
validThrough?: string;
fullClaim?: Record<string, any>;
fullClaim?: Record<string, unknown>;
}
export interface OffersToPlansResponse {
@ -443,7 +443,7 @@ export interface PlansLastUpdatedResponse {
export interface PlanSummaryWithPreviousClaim {
plan: PlanSummary;
wrappedClaimBefore?: Record<string, any>;
wrappedClaimBefore?: Record<string, unknown>;
}
export interface PlanSummary {
@ -507,7 +507,7 @@ export interface TimeSafariProjectNotification {
project: PlanSummary;
changes?: {
fields: string[];
previousValues?: Record<string, any>;
previousValues?: Record<string, unknown>;
};
relevantOffers?: OfferSummaryRecord[];
notificationPriority: 'high' | 'medium' | 'low';
@ -519,7 +519,7 @@ export interface TimeSafariPersonNotification {
personDid: string;
changes?: {
fields: string[];
previousValues?: Record<string, any>;
previousValues?: Record<string, unknown>;
};
relevantProjects?: PlanSummary[];
notificationPriority: 'high' | 'medium' | 'low';
@ -531,7 +531,7 @@ export interface TimeSafariItemNotification {
itemId: string;
changes?: {
fields: string[];
previousValues?: Record<string, any>;
previousValues?: Record<string, unknown>;
};
relevantContext?: 'project' | 'offer' | 'person';
notificationPriority: 'high' | 'medium' | 'low';

10
src/web/index.ts

@ -299,7 +299,7 @@ export class DailyNotificationWeb implements DailyNotificationPlugin {
/**
* Update dual schedule configuration (web implementation)
*/
async updateDualScheduleConfig(config: any): Promise<void> {
async updateDualScheduleConfig(config: Record<string, unknown>): Promise<void> {
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<void> {
async scheduleDailyReminder(options: Record<string, unknown>): Promise<void> {
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<any[]> {
async getScheduledReminders(): Promise<Record<string, unknown>[]> {
console.log('Get scheduled reminders called on web platform');
return []; // Mock empty array for web
}
async updateDailyReminder(reminderId: string, options: any): Promise<void> {
async updateDailyReminder(reminderId: string, options: Record<string, unknown>): Promise<void> {
console.log('Update daily reminder called on web platform:', reminderId, options);
// Mock implementation for web
}

56
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<string, unknown>;
static getInstance(): ConfigLoader {
if (!this.instance) {
@ -74,7 +74,7 @@ class ConfigLoader {
};
}
getConfig(): any {
getConfig(): Record<string, unknown> {
return this.config;
}
@ -121,7 +121,7 @@ class ConfigLoader {
};
}
getAuthHeaders(): any {
getAuthHeaders(): Record<string, string> {
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<string, unknown>) {
this.config = config;
}
private config: any;
private config: Record<string, unknown>;
async initialize(): Promise<void> {
console.log('Mock notification service initialized');
}
async scheduleDualNotification(config: any): Promise<void> {
async scheduleDualNotification(config: Record<string, unknown>): Promise<void> {
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<string, unknown>) {
console.log(`[INFO] ${message}`, data);
}
error(message: string, data?: any) {
error(message: string, data?: Record<string, unknown>) {
console.error(`[ERROR] ${message}`, data);
}
debug(message: string, data?: any) {
debug(message: string, data?: Record<string, unknown>) {
console.log(`[DEBUG] ${message}`, data);
}
}
@ -195,7 +195,7 @@ class PermissionManager {
this.renderStatus(mockStatus);
}
private renderStatus(status: any): void {
private renderStatus(status: Record<string, unknown>): 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<string, unknown>) => {
this.log('✅ Content fetch successful', data);
await this.processEndorserNotificationBundle(data);
},
onError: async (error: any) => {
onError: async (error: Record<string, unknown>) => {
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<string, unknown>) => {
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<string, unknown>) => {
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<string, unknown>) => {
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<string, unknown>) => {
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<void> {
private async processEndorserNotificationBundle(data: Record<string, unknown>): Promise<void> {
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<void> {
private async handleOffersNotification(event: Record<string, unknown>): Promise<void> {
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<string, unknown>) => {
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<void> {
private async handleProjectsNotification(event: Record<string, unknown>): Promise<void> {
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<string, unknown>) => {
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<void> {
private async handlePeopleNotification(event: Record<string, unknown>): Promise<void> {
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<void> {
private async handleItemsNotification(event: Record<string, unknown>): Promise<void> {
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<string, unknown>) {
const timestamp = new Date().toLocaleTimeString();
const logEntry = document.createElement('div');
logEntry.innerHTML = `<span class="timestamp">[${timestamp}]</span> ${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

38
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<string, unknown>): 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<string, unknown>) => {
this.log('✅ Content fetch successful', data);
await this.processEndorserNotificationBundle(data);
},
onError: async (error: any) => {
onError: async (error: Record<string, unknown>) => {
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<string, unknown>) => {
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<string, unknown>) => {
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<string, unknown>) => {
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<string, unknown>) => {
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<void> {
private async processEndorserNotificationBundle(data: Record<string, unknown>): Promise<void> {
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<void> {
private async handleOffersNotification(event: Record<string, unknown>): Promise<void> {
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<string, unknown>) => {
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<void> {
private async handleProjectsNotification(event: Record<string, unknown>): Promise<void> {
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<string, unknown>) => {
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<void> {
private async handlePeopleNotification(event: Record<string, unknown>): Promise<void> {
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<void> {
private async handleItemsNotification(event: Record<string, unknown>): Promise<void> {
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<string, unknown>) {
const timestamp = new Date().toLocaleTimeString();
const logEntry = document.createElement('div');
logEntry.innerHTML = `<span class="timestamp">[${timestamp}]</span> ${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

30
test-apps/shared/typescript/EndorserAPIClient.ts

@ -62,7 +62,7 @@ export class EndorserAPIClient {
private config: EndorserAPIConfig;
private authToken?: string;
private rateLimiter: Map<string, number> = new Map();
private requestCache: Map<string, { data: any; timestamp: number }> = new Map();
private requestCache: Map<string, { data: unknown; timestamp: number }> = new Map();
constructor(config: Partial<EndorserAPIConfig> = {}) {
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;

4
test-apps/shared/typescript/SecurityManager.ts

@ -41,7 +41,7 @@ export interface SecurityConfig {
export interface CryptoOperation {
success: boolean;
data?: any;
data?: Record<string, unknown>;
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<string, unknown>, _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

12
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<string, { data: any; timestamp: number }> = new Map();
private cache: Map<string, { data: unknown; timestamp: number }> = new Map();
private activeGeneration = new Set<string>();
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

Loading…
Cancel
Save