From 235609dc4cabded6b4703662d6edfc0d43273aec Mon Sep 17 00:00:00 2001 From: Andrew Beal Date: Mon, 10 Nov 2025 13:44:16 +0000 Subject: [PATCH] refactor: improve type safety and remove unused imports - Add explicit type casting for API call results - Fix regex escape sequences to use Unicode notation - Remove unused type imports across multiple services - Update file deletion to use FileManager.trashFile - Add missing switch case for folder modify events - Move CSS imports to ES6 import syntax --- Services/ConversationFileSystemService.ts | 2 +- Services/FileSystemService.ts | 3 +-- Services/SanitiserService.ts | 12 ++++++------ Services/ServiceRegistration.ts | 6 +++--- Services/StatusBarService.ts | 1 - Services/StreamingService.ts | 1 - Services/VaultCacheService.ts | 3 +++ Services/VaultService.ts | 2 +- __tests__/Services/VaultService.test.ts | 20 +++++++++++--------- main.ts | 8 +++----- 10 files changed, 29 insertions(+), 29 deletions(-) diff --git a/Services/ConversationFileSystemService.ts b/Services/ConversationFileSystemService.ts index 6737c78..04b62dd 100644 --- a/Services/ConversationFileSystemService.ts +++ b/Services/ConversationFileSystemService.ts @@ -31,7 +31,7 @@ export class ConversationFileSystemService { created: conversation.created.toISOString(), updated: conversation.updated.toISOString(), contents: conversation.contents - .filter(content => content.content !== Copy.ApiRequestAborted) + .filter(content => content.content !== Copy.ApiRequestAborted.toString()) .map(content => ({ role: content.role, content: content.content, diff --git a/Services/FileSystemService.ts b/Services/FileSystemService.ts index 1300d86..8fb8080 100644 --- a/Services/FileSystemService.ts +++ b/Services/FileSystemService.ts @@ -1,5 +1,4 @@ -import type VaultkeeperAIPlugin from "main"; -import { TAbstractFile, TFile, TFolder, type Vault } from "obsidian"; +import { TAbstractFile, TFile, TFolder } from "obsidian"; import { Resolve } from "./DependencyService"; import { Services } from "./Services"; import { isValidJson } from "Helpers/Helpers"; diff --git a/Services/SanitiserService.ts b/Services/SanitiserService.ts index e26424f..61b295f 100644 --- a/Services/SanitiserService.ts +++ b/Services/SanitiserService.ts @@ -9,11 +9,11 @@ export interface ISanitizeOptions { export class SanitiserService { // Regular expressions for different character classes - private readonly illegalRe = /[\?<>\\:\*\|"]/g; - private readonly controlRe = /[\x00-\x1f\x80-\x9f]/g; + private readonly illegalRe = /[?<>\\:*|"]/g; + private readonly controlRe = /[\u0000-\u001f\u0080-\u009f]/g; private readonly reservedRe = /^\.+$/; private readonly windowsReservedRe = /^(con|prn|aux|nul|com[0-9]|lpt[0-9])(\..*)?$/i; - private readonly windowsTrailingRe = /[\. ]+$/; + private readonly windowsTrailingRe = /[. ]+$/; constructor() { } @@ -42,16 +42,16 @@ export class SanitiserService { const outputSeparator = options.separator || "/"; // Detect if this is an absolute path - const isAbsolute = input.startsWith("/") || /^[a-zA-Z]:[\\\/]/.test(input); + const isAbsolute = input.startsWith("/") || /^[a-zA-Z]:[\\/]/.test(input); // Detect Windows drive letter (e.g., C:) - const driveMatch = input.match(/^([a-zA-Z]:)[\\\/]/); + const driveMatch = input.match(/^([a-zA-Z]:)[\\/]/); const driveLetter = driveMatch ? driveMatch[1] : ""; // Split by both forward and back slashes // Note: Forward slashes and backslashes are treated as path separators, // not as illegal characters within segments. This is intentional for cross-platform compatibility. - let segments = input.split(/[\\\/]+/); + let segments = input.split(/[\\/]+/); // Remove empty segments (from leading/trailing slashes or multiple consecutive slashes) // But keep track of whether we started with a slash diff --git a/Services/ServiceRegistration.ts b/Services/ServiceRegistration.ts index 0192c0a..f47e52d 100644 --- a/Services/ServiceRegistration.ts +++ b/Services/ServiceRegistration.ts @@ -11,7 +11,7 @@ import { StreamingMarkdownService } from "./StreamingMarkdownService"; import { FileSystemService } from "./FileSystemService"; import { ConversationFileSystemService } from "./ConversationFileSystemService"; import { ConversationHistoryModal } from "Modals/ConversationHistoryModal"; -import { FileManager, type App } from "obsidian"; +import { FileManager } from "obsidian"; import { AIFunctionService } from "./AIFunctionService"; import { StreamingService } from "./StreamingService"; import { AIFunctionDefinitions } from "AIClasses/FunctionDefinitions/AIFunctionDefinitions"; @@ -34,12 +34,12 @@ import { UserInputService } from "./UserInputService"; import { SearchStateStore } from "Stores/SearchStateStore"; import { InputService } from "./InputService"; import { HTMLService } from "./HTMLService"; -import { SettingsService } from "./SettingsService"; +import { SettingsService, type IVaultkeeperAISettings } from "./SettingsService"; import { HelpModal } from "Modals/HelpModal"; export async function RegisterPlugin(plugin: VaultkeeperAIPlugin) { RegisterSingleton(Services.VaultkeeperAIPlugin, plugin); - RegisterSingleton(Services.SettingsService, new SettingsService(await plugin.loadData())); + RegisterSingleton(Services.SettingsService, new SettingsService(await plugin.loadData() as Partial)); } export function RegisterDependencies() { diff --git a/Services/StatusBarService.ts b/Services/StatusBarService.ts index 71b3bd7..4894985 100644 --- a/Services/StatusBarService.ts +++ b/Services/StatusBarService.ts @@ -1,7 +1,6 @@ import type VaultkeeperAIPlugin from "main"; import { Resolve } from "./DependencyService"; import { Services } from "./Services"; -import { Selector } from "Enums/Selector"; export class StatusBarService { diff --git a/Services/StreamingService.ts b/Services/StreamingService.ts index 04f23c7..4c33923 100644 --- a/Services/StreamingService.ts +++ b/Services/StreamingService.ts @@ -1,5 +1,4 @@ import type { AIFunctionCall } from "AIClasses/AIFunctionCall"; -import type { IAIClass } from "AIClasses/IAIClass"; import { Selector } from "Enums/Selector"; export interface IStreamChunk { diff --git a/Services/VaultCacheService.ts b/Services/VaultCacheService.ts index 1d19cdc..f015c0a 100644 --- a/Services/VaultCacheService.ts +++ b/Services/VaultCacheService.ts @@ -130,6 +130,9 @@ export class VaultCacheService { this.folders.delete(args.oldPath); } break; + + case FileEvent.Modify: + break; // ignore modifications for folders } this.fuzzySortPrepareFolders(); } diff --git a/Services/VaultService.ts b/Services/VaultService.ts index 9fafba1..6002993 100644 --- a/Services/VaultService.ts +++ b/Services/VaultService.ts @@ -97,7 +97,7 @@ export class VaultService { return { success: false, error: "File is in exclusion list" }; } try { - await this.vault.trash(file, true); + await this.fileManager.trashFile(file); return { success: true }; } catch (error) { console.error(`Error deleting file ${file.path}:`, error); diff --git a/__tests__/Services/VaultService.test.ts b/__tests__/Services/VaultService.test.ts index 3d8871e..552f116 100644 --- a/__tests__/Services/VaultService.test.ts +++ b/__tests__/Services/VaultService.test.ts @@ -37,9 +37,11 @@ const mockVault = { }; const mockFileManager = { - renameFile: vi.fn() + renameFile: vi.fn(), + trashFile: vi.fn() } as unknown as FileManager & { renameFile: ReturnType; + trashFile: ReturnType; }; // Create a mutable settings object that tests can modify @@ -400,12 +402,12 @@ describe('VaultService - Integration Tests', () => { describe('delete', () => { it('should delete file successfully when not excluded', async () => { const mockFile = createMockFile('note.md'); - mockVault.trash.mockResolvedValue(undefined); + mockFileManager.trashFile.mockResolvedValue(undefined); const result = await vaultService.delete(mockFile); expect(result).toEqual({ success: true }); - expect(mockVault.trash).toHaveBeenCalledWith(mockFile, true); + expect(mockFileManager.trashFile).toHaveBeenCalledWith(mockFile); }); it('should not delete file and return error when excluded', async () => { @@ -414,22 +416,22 @@ describe('VaultService - Integration Tests', () => { const result = await vaultService.delete(mockFile, false); expect(result).toEqual({ success: false, error: 'File is in exclusion list' }); - expect(mockVault.trash).not.toHaveBeenCalled(); + expect(mockFileManager.trashFile).not.toHaveBeenCalled(); expect(consoleErrorSpy).toHaveBeenCalled(); }); - it('should pass force parameter to vault.delete', async () => { + it('should call fileManager.trashFile to delete file', async () => { const mockFile = createMockFile('note.md'); - mockVault.trash.mockResolvedValue(undefined); + mockFileManager.trashFile.mockResolvedValue(undefined); await vaultService.delete(mockFile, true); - expect(mockVault.trash).toHaveBeenCalledWith(mockFile, true); + expect(mockFileManager.trashFile).toHaveBeenCalledWith(mockFile); }); it('should return error when deletion fails', async () => { const mockFile = createMockFile('note.md'); - mockVault.trash.mockRejectedValue(new Error('Deletion failed')); + mockFileManager.trashFile.mockRejectedValue(new Error('Deletion failed')); const result = await vaultService.delete(mockFile); @@ -439,7 +441,7 @@ describe('VaultService - Integration Tests', () => { it('should handle non-Error objects in catch block', async () => { const mockFile = createMockFile('note.md'); - mockVault.trash.mockRejectedValue('string error'); + mockFileManager.trashFile.mockRejectedValue('string error'); const result = await vaultService.delete(mockFile); diff --git a/main.ts b/main.ts index 9b02ef9..90c9ab1 100644 --- a/main.ts +++ b/main.ts @@ -10,14 +10,12 @@ import { Path } from "Enums/Path"; import { Copy } from "Enums/Copy"; import type { SettingsService } from "Services/SettingsService"; +import "katex/dist/katex.min.css"; +import "./styles.css"; + export default class VaultkeeperAIPlugin extends Plugin { public async onload() { - // KaTeX CSS is bundled with the plugin to comply with CSP - require("katex/dist/katex.min.css"); - // Plugin styles - require("./styles.css"); - await RegisterPlugin(this); RegisterDependencies();