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
This commit is contained in:
Andrew Beal 2025-11-10 13:44:16 +00:00
parent 643129517a
commit 235609dc4c
10 changed files with 29 additions and 29 deletions

View file

@ -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,

View file

@ -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";

View file

@ -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

View file

@ -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<VaultkeeperAIPlugin>(Services.VaultkeeperAIPlugin, plugin);
RegisterSingleton<SettingsService>(Services.SettingsService, new SettingsService(await plugin.loadData()));
RegisterSingleton<SettingsService>(Services.SettingsService, new SettingsService(await plugin.loadData() as Partial<IVaultkeeperAISettings>));
}
export function RegisterDependencies() {

View file

@ -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 {

View file

@ -1,5 +1,4 @@
import type { AIFunctionCall } from "AIClasses/AIFunctionCall";
import type { IAIClass } from "AIClasses/IAIClass";
import { Selector } from "Enums/Selector";
export interface IStreamChunk {

View file

@ -130,6 +130,9 @@ export class VaultCacheService {
this.folders.delete(args.oldPath);
}
break;
case FileEvent.Modify:
break; // ignore modifications for folders
}
this.fuzzySortPrepareFolders();
}

View file

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

View file

@ -37,9 +37,11 @@ const mockVault = {
};
const mockFileManager = {
renameFile: vi.fn()
renameFile: vi.fn(),
trashFile: vi.fn()
} as unknown as FileManager & {
renameFile: ReturnType<typeof vi.fn>;
trashFile: ReturnType<typeof vi.fn>;
};
// 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);

View file

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