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);