From 3351cc08c2081bfdba69da3faee9bb53993ac09e Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Mon, 23 Jun 2025 16:55:53 +0200 Subject: [PATCH] fixes linter issues --- web/src/client/components/file-browser.ts | 18 +++++-- web/src/client/components/monaco-editor.ts | 2 +- web/src/client/sw.ts | 11 ++-- web/src/client/utils/monaco-loader.ts | 15 ++++-- .../utils/offline-notification-manager.ts | 18 ++++--- web/src/server/routes/filesystem.ts | 5 +- web/src/server/routes/push.ts | 53 +++++++++++-------- web/src/server/services/bell-event-handler.ts | 5 +- .../services/push-notification-service.ts | 13 +++-- 9 files changed, 92 insertions(+), 48 deletions(-) diff --git a/web/src/client/components/file-browser.ts b/web/src/client/components/file-browser.ts index 74d2379b..9db0033d 100644 --- a/web/src/client/components/file-browser.ts +++ b/web/src/client/components/file-browser.ts @@ -462,7 +462,8 @@ export class FileBrowser extends LitElement { } // Fallback to simple diff display - const lines = this.diff!.diff.split('\n'); + if (!this.diff) return html``; + const lines = this.diff.diff.split('\n'); return html`
${lines.map((line) => { @@ -839,11 +840,22 @@ export class FileBrowser extends LitElement { document.addEventListener('touchend', handleTouchEnd); // Store handlers for removal - (this as any)._touchHandlers = { handleTouchStart, handleTouchEnd }; + interface TouchHandlers { + handleTouchStart: (e: TouchEvent) => void; + handleTouchEnd: (e: TouchEvent) => void; + } + (this as unknown as { _touchHandlers: TouchHandlers })._touchHandlers = { + handleTouchStart, + handleTouchEnd, + }; } private removeTouchHandlers() { - const handlers = (this as any)._touchHandlers; + interface TouchHandlers { + handleTouchStart: (e: TouchEvent) => void; + handleTouchEnd: (e: TouchEvent) => void; + } + const handlers = (this as unknown as { _touchHandlers?: TouchHandlers })._touchHandlers; if (handlers) { document.removeEventListener('touchstart', handlers.handleTouchStart); document.removeEventListener('touchend', handlers.handleTouchEnd); diff --git a/web/src/client/components/monaco-editor.ts b/web/src/client/components/monaco-editor.ts index 8551a582..5f02d8c4 100644 --- a/web/src/client/components/monaco-editor.ts +++ b/web/src/client/components/monaco-editor.ts @@ -22,7 +22,7 @@ const logger = createLogger('monaco-editor'); // Import Monaco Editor types declare global { interface Window { - monaco: any; + monaco: typeof import('monaco-editor'); } } diff --git a/web/src/client/sw.ts b/web/src/client/sw.ts index 8e45cde9..0fcbb85e 100644 --- a/web/src/client/sw.ts +++ b/web/src/client/sw.ts @@ -61,7 +61,7 @@ interface PushNotificationPayload { } // Install event -self.addEventListener('install', (event: ExtendableEvent) => { +self.addEventListener('install', (_event: ExtendableEvent) => { console.log('[SW] Installing service worker'); // Force activation of new service worker @@ -158,8 +158,13 @@ async function handlePushNotification(payload: PushNotificationPayload): Promise } } -function getDefaultActions(data: NotificationData): any[] { - const baseActions: any[] = [ +interface NotificationAction { + action: string; + title: string; +} + +function getDefaultActions(data: NotificationData): NotificationAction[] { + const baseActions: NotificationAction[] = [ { action: 'dismiss', title: 'Dismiss', diff --git a/web/src/client/utils/monaco-loader.ts b/web/src/client/utils/monaco-loader.ts index 943ea22c..bff7adbb 100644 --- a/web/src/client/utils/monaco-loader.ts +++ b/web/src/client/utils/monaco-loader.ts @@ -11,8 +11,11 @@ const logger = createLogger('monaco-loader'); // Declare monaco on window declare global { interface Window { - monaco: any; - require: any; + monaco: typeof import('monaco-editor'); + require: { + (dependencies: string[], callback: (...args: unknown[]) => void): void; + config: (config: { paths: { [key: string]: string } }) => void; + }; } } @@ -42,8 +45,10 @@ async function loadMonacoEditor(): Promise { // Disable workers - they interfere with diff computation // Monaco will fall back to synchronous mode which works fine window.MonacoEnvironment = { - getWorker: function (_workerId: string, _label: string) { - return null as any; + getWorker: function (_workerId: string, _label: string): Worker { + // Return a dummy worker that will never be used + // Monaco will fall back to synchronous mode + return new Worker('data:,'); }, }; @@ -150,7 +155,7 @@ export async function initializeMonaco(): Promise { /** * Ensure Monaco is loaded and ready */ -export async function ensureMonacoLoaded(): Promise { +export async function ensureMonacoLoaded(): Promise { await initializeMonaco(); return window.monaco; } diff --git a/web/src/client/utils/offline-notification-manager.ts b/web/src/client/utils/offline-notification-manager.ts index 6928b917..d8fee8b0 100644 --- a/web/src/client/utils/offline-notification-manager.ts +++ b/web/src/client/utils/offline-notification-manager.ts @@ -205,8 +205,9 @@ export class OfflineNotificationManager { return []; } + const db = this.db; return new Promise((resolve, reject) => { - const transaction = this.db!.transaction([STORE_NAME], 'readonly'); + const transaction = db.transaction([STORE_NAME], 'readonly'); const store = transaction.objectStore(STORE_NAME); const index = store.index('nextRetry'); @@ -229,8 +230,9 @@ export class OfflineNotificationManager { throw new Error('Database not initialized'); } + const db = this.db; return new Promise((resolve, reject) => { - const transaction = this.db!.transaction([STORE_NAME], 'readwrite'); + const transaction = db.transaction([STORE_NAME], 'readwrite'); const store = transaction.objectStore(STORE_NAME); const request = store.add(notification); @@ -244,8 +246,9 @@ export class OfflineNotificationManager { throw new Error('Database not initialized'); } + const db = this.db; return new Promise((resolve, reject) => { - const transaction = this.db!.transaction([STORE_NAME], 'readwrite'); + const transaction = db.transaction([STORE_NAME], 'readwrite'); const store = transaction.objectStore(STORE_NAME); const request = store.put(notification); @@ -259,8 +262,9 @@ export class OfflineNotificationManager { throw new Error('Database not initialized'); } + const db = this.db; return new Promise((resolve, reject) => { - const transaction = this.db!.transaction([STORE_NAME], 'readwrite'); + const transaction = db.transaction([STORE_NAME], 'readwrite'); const store = transaction.objectStore(STORE_NAME); const request = store.delete(id); @@ -277,8 +281,9 @@ export class OfflineNotificationManager { return { total: 0, pending: 0, failed: 0, lastProcessed: 0 }; } + const db = this.db; return new Promise((resolve, reject) => { - const transaction = this.db!.transaction([STORE_NAME], 'readonly'); + const transaction = db.transaction([STORE_NAME], 'readonly'); const store = transaction.objectStore(STORE_NAME); const request = store.getAll(); @@ -311,8 +316,9 @@ export class OfflineNotificationManager { return; } + const db = this.db; return new Promise((resolve, reject) => { - const transaction = this.db!.transaction([STORE_NAME], 'readwrite'); + const transaction = db.transaction([STORE_NAME], 'readwrite'); const store = transaction.objectStore(STORE_NAME); const request = store.clear(); diff --git a/web/src/server/routes/filesystem.ts b/web/src/server/routes/filesystem.ts index 17bc76cf..393b1831 100644 --- a/web/src/server/routes/filesystem.ts +++ b/web/src/server/routes/filesystem.ts @@ -530,7 +530,10 @@ export function createFilesystemRoutes(): Router { logger.error(`Failed to get HEAD version of ./${relativePath}:`, error); // Check if it's a stderr message if (error instanceof Error && 'stderr' in error) { - logger.error(`Git stderr: ${(error as any).stderr}`); + const execError = error as Error & { stderr?: string }; + if (execError.stderr) { + logger.error(`Git stderr: ${execError.stderr}`); + } } // For non-git repos, show no diff originalContent = currentContent; diff --git a/web/src/server/routes/push.ts b/web/src/server/routes/push.ts index 580b3217..ef19aaf5 100644 --- a/web/src/server/routes/push.ts +++ b/web/src/server/routes/push.ts @@ -1,6 +1,7 @@ import { Router, Request, Response } from 'express'; import { VapidManager } from '../utils/vapid-manager.js'; import { PushNotificationService } from '../services/push-notification-service.js'; +import { BellEventHandler } from '../services/bell-event-handler.js'; import { createLogger } from '../utils/logger.js'; const logger = createLogger('push-routes'); @@ -8,25 +9,13 @@ const logger = createLogger('push-routes'); export interface CreatePushRoutesOptions { vapidManager: VapidManager; pushNotificationService: PushNotificationService | null; - bellEventHandler?: any; + bellEventHandler?: BellEventHandler; } export function createPushRoutes(options: CreatePushRoutesOptions): Router { const { vapidManager, pushNotificationService } = options; const router = Router(); - // Helper function to check if push service is available - const checkPushService = (res: Response): boolean => { - if (!pushNotificationService) { - res.status(503).json({ - error: 'Push notifications not initialized', - message: 'Push notification service is not available', - }); - return false; - } - return true; - }; - /** * Get VAPID public key for client registration */ @@ -65,7 +54,12 @@ export function createPushRoutes(options: CreatePushRoutesOptions): Router { * Subscribe to push notifications */ router.post('/push/subscribe', async (req: Request, res: Response) => { - if (!checkPushService(res)) return; + if (!pushNotificationService) { + return res.status(503).json({ + error: 'Push notifications not initialized', + message: 'Push notification service is not available', + }); + } try { const { endpoint, keys } = req.body; @@ -77,7 +71,7 @@ export function createPushRoutes(options: CreatePushRoutesOptions): Router { }); } - const subscriptionId = await pushNotificationService!.addSubscription(endpoint, keys); + const subscriptionId = await pushNotificationService.addSubscription(endpoint, keys); res.json({ success: true, @@ -99,7 +93,12 @@ export function createPushRoutes(options: CreatePushRoutesOptions): Router { * Unsubscribe from push notifications */ router.post('/push/unsubscribe', async (req: Request, res: Response) => { - if (!checkPushService(res)) return; + if (!pushNotificationService) { + return res.status(503).json({ + error: 'Push notifications not initialized', + message: 'Push notification service is not available', + }); + } try { const { endpoint } = req.body; @@ -112,11 +111,11 @@ export function createPushRoutes(options: CreatePushRoutesOptions): Router { } // For simplicity, we'll find and remove by endpoint - const subscriptions = pushNotificationService!.getSubscriptions(); + const subscriptions = pushNotificationService.getSubscriptions(); const subscription = subscriptions.find((sub) => sub.endpoint === endpoint); if (subscription) { - await pushNotificationService!.removeSubscription(subscription.id); + await pushNotificationService.removeSubscription(subscription.id); logger.log(`Push subscription removed: ${subscription.id}`); } @@ -137,10 +136,15 @@ export function createPushRoutes(options: CreatePushRoutesOptions): Router { * Send test notification */ router.post('/push/test', async (req: Request, res: Response) => { - if (!checkPushService(res)) return; + if (!pushNotificationService) { + return res.status(503).json({ + error: 'Push notifications not initialized', + message: 'Push notification service is not available', + }); + } try { - const result = await pushNotificationService!.sendNotification({ + const result = await pushNotificationService.sendNotification({ type: 'test', title: '🔔 Test Notification', body: 'This is a test notification from VibeTunnel', @@ -178,10 +182,15 @@ export function createPushRoutes(options: CreatePushRoutesOptions): Router { * Get service status */ router.get('/push/status', (req: Request, res: Response) => { - if (!checkPushService(res)) return; + if (!pushNotificationService) { + return res.status(503).json({ + error: 'Push notifications not initialized', + message: 'Push notification service is not available', + }); + } try { - const subscriptions = pushNotificationService!.getSubscriptions(); + const subscriptions = pushNotificationService.getSubscriptions(); res.json({ enabled: vapidManager.isEnabled(), diff --git a/web/src/server/services/bell-event-handler.ts b/web/src/server/services/bell-event-handler.ts index c81cf47c..b9611cd5 100644 --- a/web/src/server/services/bell-event-handler.ts +++ b/web/src/server/services/bell-event-handler.ts @@ -7,6 +7,7 @@ import { SessionInfo } from '../../shared/types.js'; import { createLogger } from '../utils/logger.js'; +import { PushNotificationService } from './push-notification-service.js'; const logger = createLogger('bell-event-handler'); @@ -45,7 +46,7 @@ export interface BellNotificationPayload { * Ultra-simple bell event handler */ export class BellEventHandler { - private pushNotificationService: any = null; + private pushNotificationService: PushNotificationService | null = null; constructor() { logger.debug('BellEventHandler initialized'); @@ -54,7 +55,7 @@ export class BellEventHandler { /** * Set the push notification service for sending notifications */ - setPushNotificationService(service: any): void { + setPushNotificationService(service: PushNotificationService): void { this.pushNotificationService = service; logger.debug('Push notification service configured'); } diff --git a/web/src/server/services/push-notification-service.ts b/web/src/server/services/push-notification-service.ts index 04b9b900..d3bb1287 100644 --- a/web/src/server/services/push-notification-service.ts +++ b/web/src/server/services/push-notification-service.ts @@ -45,7 +45,7 @@ export interface NotificationPayload { action: string; title: string; }>; - data?: any; + data?: Record; } /** @@ -195,13 +195,15 @@ export class PushNotificationService { const shouldRemove = this.shouldRemoveSubscription(error); if (shouldRemove) { this.subscriptions.delete(subscription.id); + const webPushError = error as Error & { statusCode?: number }; logger.log( - `Removed expired subscription: ${subscription.id} (status: ${(error as any).statusCode})` + `Removed expired subscription: ${subscription.id} (status: ${webPushError.statusCode})` ); } else { // Debug log for unhandled errors + const webPushError = error as Error & { statusCode?: number }; logger.debug( - `Not removing subscription ${subscription.id}, error: ${error instanceof Error ? error.message : String(error)}, statusCode: ${(error as any).statusCode}` + `Not removing subscription ${subscription.id}, error: ${error instanceof Error ? error.message : String(error)}, statusCode: ${webPushError.statusCode}` ); } } @@ -254,7 +256,7 @@ export class PushNotificationService { // Check for HTTP 410 Gone status (subscription expired) // WebPushError has a statusCode property - const webPushError = error as any; + const webPushError = error as Error & { statusCode?: number }; if (webPushError.statusCode === 410) { return true; } @@ -315,7 +317,8 @@ export class PushNotificationService { logger.debug(`Loaded ${subscriptions.length} subscriptions`); } catch (error) { - if ((error as any).code === 'ENOENT') { + const fsError = error as NodeJS.ErrnoException; + if (fsError.code === 'ENOENT') { logger.debug('No existing subscriptions file found'); } else { logger.error('Failed to load subscriptions:', error);