diff --git a/AIClasses/AIFunctionCall.ts b/AIClasses/AIFunctionCall.ts index 4f191ff..7042f6a 100644 --- a/AIClasses/AIFunctionCall.ts +++ b/AIClasses/AIFunctionCall.ts @@ -1,10 +1,10 @@ // platform agnostic function call class used to execute the requested function export class AIFunctionCall { public readonly name: string; - public readonly arguments: Record; + public readonly arguments: Record; public readonly toolId?: string; - constructor(name: string, args: Record, toolId?: string) { + constructor(name: string, args: Record, toolId?: string) { this.name = name; this.arguments = args; this.toolId = toolId; diff --git a/AIClasses/Claude/Claude.ts b/AIClasses/Claude/Claude.ts index b3584d3..204ce58 100644 --- a/AIClasses/Claude/Claude.ts +++ b/AIClasses/Claude/Claude.ts @@ -13,7 +13,7 @@ import type { ConversationContent } from "Conversations/ConversationContent"; import { Role } from "Enums/Role"; import type { SettingsService } from "Services/SettingsService"; import type { RawMessageStreamEvent, ContentBlockParam, Tool } from '@anthropic-ai/sdk/resources/messages'; -import type { StoredFunctionCall, StoredFunctionResponse } from "AIClasses/FunctionDefinitions/AIFunctionTypes"; +import type { StoredFunctionCall, StoredFunctionResponse } from "AIClasses/Schemas/AIFunctionTypes"; export class Claude implements IAIClass { diff --git a/AIClasses/FunctionDefinitions/AIFunctionTypes.ts b/AIClasses/FunctionDefinitions/AIFunctionTypes.ts deleted file mode 100644 index f7150a5..0000000 --- a/AIClasses/FunctionDefinitions/AIFunctionTypes.ts +++ /dev/null @@ -1,40 +0,0 @@ -// Shared type definitions for AI function calls and responses across all providers - -/** - * JSON Schema property definition supporting nested structures - * Used for defining function parameters in a provider-agnostic way - */ -export type JSONSchemaProperty = { - type?: string; - description?: string; - items?: JSONSchemaProperty; - properties?: Record; - required?: string[]; - enum?: string[]; - [key: string]: unknown; -}; - -/** - * Stored function call format used across all AI providers - * This is the format saved to conversation history when a function is called - */ -export interface StoredFunctionCall { - functionCall: { - id: string; - name: string; - args: Record; - }; -} - -/** - * Stored function response format used across all AI providers - * This is the format saved to conversation history when a function returns - * Note: The actual stored data includes a 'name' field (see AIFunctionResponse.toConversationString) - */ -export interface StoredFunctionResponse { - id: string; - functionResponse: { - name: string; - response: unknown; - }; -} diff --git a/AIClasses/FunctionDefinitions/IAIFunctionDefinition.ts b/AIClasses/FunctionDefinitions/IAIFunctionDefinition.ts index ecd1a0a..13c109f 100644 --- a/AIClasses/FunctionDefinitions/IAIFunctionDefinition.ts +++ b/AIClasses/FunctionDefinitions/IAIFunctionDefinition.ts @@ -1,4 +1,4 @@ -import type { JSONSchemaProperty } from "./AIFunctionTypes"; +import type { JSONSchemaProperty } from "../Schemas/AIFunctionTypes"; // platform agnostic function definition used to present function calls in an API call export interface IAIFunctionDefinition { diff --git a/AIClasses/Gemini/Gemini.ts b/AIClasses/Gemini/Gemini.ts index 96fd5c2..6e03988 100644 --- a/AIClasses/Gemini/Gemini.ts +++ b/AIClasses/Gemini/Gemini.ts @@ -13,7 +13,7 @@ import type { AIFunctionDefinitions } from "AIClasses/FunctionDefinitions/AIFunc import { isValidJson } from "Helpers/Helpers"; import type { ConversationContent } from "Conversations/ConversationContent"; import type { SettingsService } from "Services/SettingsService"; -import type { StoredFunctionCall, StoredFunctionResponse } from "AIClasses/FunctionDefinitions/AIFunctionTypes"; +import type { StoredFunctionCall, StoredFunctionResponse } from "AIClasses/Schemas/AIFunctionTypes"; import type { Candidate, Part, FunctionDeclaration } from "@google/genai"; import { FinishReason } from "@google/genai"; diff --git a/AIClasses/OpenAI/OpenAI.ts b/AIClasses/OpenAI/OpenAI.ts index 614ec6f..7b51ef9 100644 --- a/AIClasses/OpenAI/OpenAI.ts +++ b/AIClasses/OpenAI/OpenAI.ts @@ -12,7 +12,7 @@ import type { AIFunctionDefinitions } from "AIClasses/FunctionDefinitions/AIFunc import { Role } from "Enums/Role"; import { isValidJson } from "Helpers/Helpers"; import type { SettingsService } from "Services/SettingsService"; -import type { StoredFunctionCall, StoredFunctionResponse } from "AIClasses/FunctionDefinitions/AIFunctionTypes"; +import type { StoredFunctionCall, StoredFunctionResponse } from "AIClasses/Schemas/AIFunctionTypes"; import type { ChatCompletionChunk, ChatCompletionTool } from "openai/resources/chat/completions"; interface IToolCallAccumulator { diff --git a/AIClasses/Schemas/AIFunctionSchemas.ts b/AIClasses/Schemas/AIFunctionSchemas.ts new file mode 100644 index 0000000..8026a84 --- /dev/null +++ b/AIClasses/Schemas/AIFunctionSchemas.ts @@ -0,0 +1,48 @@ +/** Zod schemas for runtime validation of AI function arguments **/ + +import { z } from "zod"; + +// Zod schemas for AI function arguments +// These provide runtime validation of data received from AI providers + +export const SearchVaultFilesArgsSchema = z.object({ + search_terms: z.array(z.string()), + user_message: z.string() +}); + +export const ReadVaultFilesArgsSchema = z.object({ + file_paths: z.array(z.string()), + user_message: z.string() +}); + +export const WriteVaultFileArgsSchema = z.object({ + file_path: z.string(), + content: z.string(), + user_message: z.string() +}); + +export const DeleteVaultFilesArgsSchema = z.object({ + file_paths: z.array(z.string()), + user_message: z.string(), + confirm_deletion: z.boolean() +}); + +export const MoveVaultFilesArgsSchema = z.object({ + source_paths: z.array(z.string()), + destination_paths: z.array(z.string()), + user_message: z.string() +}); + +export const ListVaultFilesArgsSchema = z.object({ + path: z.string(), + recursive: z.boolean(), + user_message: z.string() +}); + +// Infer TypeScript types from schemas +export type SearchVaultFilesArgs = z.infer; +export type ReadVaultFilesArgs = z.infer; +export type WriteVaultFileArgs = z.infer; +export type DeleteVaultFilesArgs = z.infer; +export type MoveVaultFilesArgs = z.infer; +export type ListVaultFilesArgs = z.infer; diff --git a/AIClasses/Schemas/AIFunctionTypes.ts b/AIClasses/Schemas/AIFunctionTypes.ts new file mode 100644 index 0000000..b3619e6 --- /dev/null +++ b/AIClasses/Schemas/AIFunctionTypes.ts @@ -0,0 +1,33 @@ +/** Shared type definitions for AI function calls and responses across all providers **/ + +// JSON Schema property definition supporting nested structures +// Used for defining function parameters in a provider-agnostic way +export type JSONSchemaProperty = { + type?: string; + description?: string; + items?: JSONSchemaProperty; + properties?: Record; + required?: string[]; + enum?: string[]; + [key: string]: unknown; +}; + +//Stored function call format used across all AI providers +// This is the format saved to conversation history when a function is called +export interface StoredFunctionCall { + functionCall: { + id: string; + name: string; + args: Record; + }; +} + +// Stored function response format used across all AI providers +// This is the format saved to conversation history when a function returns +export interface StoredFunctionResponse { + id: string; + functionResponse: { + name: string; + response: unknown; + }; +} \ No newline at end of file diff --git a/Helpers/Helpers.ts b/Helpers/Helpers.ts index 8a82167..05d9719 100644 --- a/Helpers/Helpers.ts +++ b/Helpers/Helpers.ts @@ -2,15 +2,17 @@ import type VaultkeeperAIPlugin from "main"; export function openPluginSettings(plugin: VaultkeeperAIPlugin) { // @ts-ignore - accessing internal API + // eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access plugin.app.setting.open(); // @ts-ignore - accessing internal API + // eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access plugin.app.setting.openTabById(plugin.manifest.id); } export function isValidJson(str: string): boolean { try { JSON.parse(str); - } catch (e) { + } catch { return false; } return true; diff --git a/Modals/ConversationHistoryModal.ts b/Modals/ConversationHistoryModal.ts index a06a8db..0033cd5 100644 --- a/Modals/ConversationHistoryModal.ts +++ b/Modals/ConversationHistoryModal.ts @@ -27,7 +27,7 @@ export class ConversationHistoryModal extends Modal { private readonly fileSystemService: FileSystemService = Resolve(Services.FileSystemService); private readonly chatService: ChatService = Resolve(Services.ChatService); - private component: Record | null = null; + private component: ReturnType | null = null; private items: IListItem[]; private conversations: Conversation[]; public onModalClose?: () => void; @@ -37,27 +37,31 @@ export class ConversationHistoryModal extends Modal { super(plugin.app); } - override async open() { + private async loadConversations() { this.conversations = await this.conversationFileSystemService.getAllConversations(); this.items = this.conversations - .sort((a, b) => b.updated.getTime() - a.updated.getTime()) - .map((conversation) => { - const filePath = this.conversationFileSystemService.generateConversationPath(conversation); - return { - id: filePath, - date: dateToString(conversation.created, false), - updated: conversation.updated, - title: conversation.title, - selected: false, - filePath: filePath - }; - }); + .sort((a, b) => b.updated.getTime() - a.updated.getTime()) + .map((conversation) => { + const filePath = this.conversationFileSystemService.generateConversationPath(conversation); + return { + id: filePath, + date: dateToString(conversation.created, false), + updated: conversation.updated, + title: conversation.title, + selected: false, + filePath: filePath + }; + }); - super.open(); + // Update the component with loaded items if it's already mounted + if (this.component) { + this.component.items = this.items; + } } onOpen() { + void this.loadConversations(); const { contentEl, modalEl, containerEl } = this; containerEl.addClass(Selector.ConversationHistoryModal); @@ -115,7 +119,7 @@ export class ConversationHistoryModal extends Modal { onClose() { if (this.component) { - unmount(this.component); + void unmount(this.component); this.component = null; } diff --git a/Modals/HelpModal.ts b/Modals/HelpModal.ts index b05bffd..bd02d69 100644 --- a/Modals/HelpModal.ts +++ b/Modals/HelpModal.ts @@ -8,7 +8,8 @@ import { Selector } from 'Enums/Selector'; export class HelpModal extends Modal { - private component: Record | null = null; + private component: ReturnType | null = null; + private initialTopic?: number; public constructor() { const plugin = Resolve(Services.VaultkeeperAIPlugin); @@ -25,19 +26,19 @@ export class HelpModal extends Modal { target: contentEl, props: { onClose: () => this.close(), - initialTopic: (this as any).initialTopic + initialTopic: this.initialTopic } }); } public open(initialTopic?: number): void { - (this as any).initialTopic = initialTopic; + this.initialTopic = initialTopic; super.open(); } onClose() { if (this.component) { - unmount(this.component); + void unmount(this.component); this.component = null; } diff --git a/Services/AIFunctionService.ts b/Services/AIFunctionService.ts index d663a45..34a40af 100644 --- a/Services/AIFunctionService.ts +++ b/Services/AIFunctionService.ts @@ -6,6 +6,14 @@ import { AIFunctionResponse } from "AIClasses/FunctionDefinitions/AIFunctionResp import type { AIFunctionCall } from "AIClasses/AIFunctionCall"; import type { ISearchMatch } from "../Helpers/SearchTypes"; import { normalizePath, TAbstractFile, TFile } from "obsidian"; +import { + SearchVaultFilesArgsSchema, + ReadVaultFilesArgsSchema, + WriteVaultFileArgsSchema, + DeleteVaultFilesArgsSchema, + MoveVaultFilesArgsSchema, + ListVaultFilesArgsSchema +} from "AIClasses/Schemas/AIFunctionSchemas"; export class AIFunctionService { @@ -13,23 +21,77 @@ export class AIFunctionService { public async performAIFunction(functionCall: AIFunctionCall): Promise { switch (functionCall.name) { - case AIFunction.SearchVaultFiles: - return new AIFunctionResponse(functionCall.name, await this.searchVaultFiles(functionCall.arguments.search_terms), functionCall.toolId); + case AIFunction.SearchVaultFiles: { + const parseResult = SearchVaultFilesArgsSchema.safeParse(functionCall.arguments); + if (!parseResult.success) { + return new AIFunctionResponse( + functionCall.name, + { error: `Invalid arguments for SearchVaultFiles: ${parseResult.error.message}` }, + functionCall.toolId + ); + } + return new AIFunctionResponse(functionCall.name, await this.searchVaultFiles(parseResult.data.search_terms), functionCall.toolId); + } - case AIFunction.ReadVaultFiles: - return new AIFunctionResponse(functionCall.name, await this.readVaultFiles(functionCall.arguments.file_paths), functionCall.toolId); + case AIFunction.ReadVaultFiles: { + const parseResult = ReadVaultFilesArgsSchema.safeParse(functionCall.arguments); + if (!parseResult.success) { + return new AIFunctionResponse( + functionCall.name, + { error: `Invalid arguments for ReadVaultFiles: ${parseResult.error.message}` }, + functionCall.toolId + ); + } + return new AIFunctionResponse(functionCall.name, await this.readVaultFiles(parseResult.data.file_paths), functionCall.toolId); + } - case AIFunction.WriteVaultFile: - return new AIFunctionResponse(functionCall.name, await this.writeVaultFile(functionCall.arguments.file_path, functionCall.arguments.content), functionCall.toolId); + case AIFunction.WriteVaultFile: { + const parseResult = WriteVaultFileArgsSchema.safeParse(functionCall.arguments); + if (!parseResult.success) { + return new AIFunctionResponse( + functionCall.name, + { error: `Invalid arguments for WriteVaultFile: ${parseResult.error.message}` }, + functionCall.toolId + ); + } + return new AIFunctionResponse(functionCall.name, await this.writeVaultFile(parseResult.data.file_path, parseResult.data.content), functionCall.toolId); + } - case AIFunction.DeleteVaultFiles: - return new AIFunctionResponse(functionCall.name, await this.deleteVaultFiles(functionCall.arguments.file_paths, functionCall.arguments.confirm_deletion), functionCall.toolId); + case AIFunction.DeleteVaultFiles: { + const parseResult = DeleteVaultFilesArgsSchema.safeParse(functionCall.arguments); + if (!parseResult.success) { + return new AIFunctionResponse( + functionCall.name, + { error: `Invalid arguments for DeleteVaultFiles: ${parseResult.error.message}` }, + functionCall.toolId + ); + } + return new AIFunctionResponse(functionCall.name, await this.deleteVaultFiles(parseResult.data.file_paths, parseResult.data.confirm_deletion), functionCall.toolId); + } - case AIFunction.MoveVaultFiles: - return new AIFunctionResponse(functionCall.name, await this.moveVaultFiles(functionCall.arguments.source_paths, functionCall.arguments.destination_paths), functionCall.toolId); + case AIFunction.MoveVaultFiles: { + const parseResult = MoveVaultFilesArgsSchema.safeParse(functionCall.arguments); + if (!parseResult.success) { + return new AIFunctionResponse( + functionCall.name, + { error: `Invalid arguments for MoveVaultFiles: ${parseResult.error.message}` }, + functionCall.toolId + ); + } + return new AIFunctionResponse(functionCall.name, await this.moveVaultFiles(parseResult.data.source_paths, parseResult.data.destination_paths), functionCall.toolId); + } - case AIFunction.ListVaultFiles: - return new AIFunctionResponse(functionCall.name, await this.ListVaultFiles(functionCall.arguments.path, functionCall.arguments.recursive), functionCall.toolId); + case AIFunction.ListVaultFiles: { + const parseResult = ListVaultFilesArgsSchema.safeParse(functionCall.arguments); + if (!parseResult.success) { + return new AIFunctionResponse( + functionCall.name, + { error: `Invalid arguments for ListVaultFiles: ${parseResult.error.message}` }, + functionCall.toolId + ); + } + return new AIFunctionResponse(functionCall.name, await this.ListVaultFiles(parseResult.data.path, parseResult.data.recursive), functionCall.toolId); + } // this is only used by gemini case AIFunction.RequestWebSearch: diff --git a/Services/ChatService.ts b/Services/ChatService.ts index fb09487..9f7b99d 100644 --- a/Services/ChatService.ts +++ b/Services/ChatService.ts @@ -64,22 +64,23 @@ export class ChatService { this.abortController = new AbortController(); conversation.contents.push(new ConversationContent(Role.User, userRequest, formattedRequest)); - this.conversationService.saveConversation(conversation); + await this.conversationService.saveConversation(conversation); callbacks.onSubmit(); callbacks.onStreamingUpdate(null); if (conversation.contents.length === 1) { this.onNameChanged?.(conversation.title); // on change for initial conversation name - this.namingService.requestName(conversation, formattedRequest, this.onNameChanged, this.abortController); + await this.namingService.requestName(conversation, formattedRequest, this.onNameChanged, this.abortController); } // Process AI responses and function calls let response = await this.streamRequestResponse(conversation, allowDestructiveActions, callbacks); while (response.functionCall || response.shouldContinue) { if (response.functionCall) { - if (response.functionCall.arguments.user_message) { - callbacks.onThoughtUpdate(response.functionCall.arguments.user_message); + const userMessage = response.functionCall.arguments.user_message; + if (userMessage && typeof userMessage === "string") { + callbacks.onThoughtUpdate(userMessage); } const functionResponse = await this.aiFunctionService.performAIFunction(response.functionCall); @@ -94,7 +95,7 @@ export class ChatService { response = await this.streamRequestResponse(conversation, allowDestructiveActions, callbacks); } } finally { - this.conversationService.saveConversation(conversation); + await this.conversationService.saveConversation(conversation); this.abortController = null; if (this.semaphoreHeld) { this.semaphoreHeld = false; @@ -238,7 +239,7 @@ export class ChatService { // Step 1: Remove markdown code blocks that might contain the function call // Pattern matches ```json\n...\n``` or ```\n...\n``` - sanitized = sanitized.replace(/```(?:json)?\s*\n?([\s\S]*?)\n?```/g, (match, codeContent) => { + sanitized = sanitized.replace(/```(?:json)?\s*\n?([\s\S]*?)\n?```/g, (match: string, codeContent: string) => { // If the code block contains our function call, remove it entirely if (codeContent.trim() === functionCallString.trim()) { return ''; @@ -252,16 +253,16 @@ export class ChatService { // Step 3: Handle pretty-printed variations by normalizing both strings try { - const functionCallObj = JSON.parse(functionCallString); + const functionCallObj: unknown = JSON.parse(functionCallString); const normalizedTarget = JSON.stringify(functionCallObj); // Find and remove any JSON that matches when normalized // This regex finds JSON objects/arrays in the text - const jsonPattern = /\{(?:[^{}]|(?:\{(?:[^{}]|(?:\{[^{}]*\}))*\}))*\}|\[(?:[^\[\]]|(?:\[(?:[^\[\]]|(?:\[[^\[\]]*\]))*\]))*\]/g; + const jsonPattern = /\{(?:[^{}]|(?:\{(?:[^{}]|(?:\{[^{}]*\}))*\}))*\}|\[(?:[^[\]]|(?:\[(?:[^[\]]|(?:\[[^[\]]*\]))*\]))*\]/g; sanitized = sanitized.replace(jsonPattern, (match) => { try { - const parsedMatch = JSON.parse(match); + const parsedMatch: unknown = JSON.parse(match); const normalizedMatch = JSON.stringify(parsedMatch); // Remove if it matches our function call when normalized return normalizedMatch === normalizedTarget ? '' : match; diff --git a/Services/SanitiserService.ts b/Services/SanitiserService.ts index 61b295f..337de9d 100644 --- a/Services/SanitiserService.ts +++ b/Services/SanitiserService.ts @@ -10,6 +10,7 @@ export interface ISanitizeOptions { export class SanitiserService { // Regular expressions for different character classes private readonly illegalRe = /[?<>\\:*|"]/g; + // eslint-disable-next-line no-control-regex -- Intentionally matching control characters for sanitization 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; diff --git a/Views/MainView.ts b/Views/MainView.ts index 80509de..0a8007a 100644 --- a/Views/MainView.ts +++ b/Views/MainView.ts @@ -8,6 +8,11 @@ import { Services } from 'Services/Services'; export const VIEW_TYPE_MAIN = 'vaultkeeper-ai-main-view'; +interface ChatWindowComponent { + focusInput: () => void; + resetChatArea: () => void; +} + export class MainView extends ItemView { private statusBarService: StatusBarService = Resolve(Services.StatusBarService); @@ -17,7 +22,7 @@ export class MainView extends ItemView { } topBar: ReturnType | undefined; - input: ReturnType | undefined; + input: ChatWindowComponent | undefined; getViewType() { return VIEW_TYPE_MAIN; @@ -31,6 +36,8 @@ export class MainView extends ItemView { return 'sparkles'; } + // ItemView requires onOpen to return Promise, but mount operations are synchronous + // eslint-disable-next-line @typescript-eslint/require-await async onOpen() { const container = this.contentEl; container.empty(); @@ -51,15 +58,15 @@ export class MainView extends ItemView { this.input = mount(ChatWindow, { target: container, props: {} - }); + }) as ChatWindowComponent; } async onClose() { if (this.topBar) { - unmount(this.topBar); + await unmount(this.topBar); } if (this.input) { - unmount(this.input); + await unmount(this.input); } this.statusBarService.removeStatusBarMessage(); } diff --git a/__tests__/Services/AIFunctionService.test.ts b/__tests__/Services/AIFunctionService.test.ts index b5307ed..5ccf31b 100644 --- a/__tests__/Services/AIFunctionService.test.ts +++ b/__tests__/Services/AIFunctionService.test.ts @@ -80,7 +80,7 @@ describe('AIFunctionService - Integration Tests', () => { const result = await service.performAIFunction({ name: AIFunction.SearchVaultFiles, - arguments: { search_terms: ['test'] }, + arguments: { search_terms: ['test'], user_message: 'test search' }, toolId: 'tool_1' } as any); @@ -106,7 +106,7 @@ describe('AIFunctionService - Integration Tests', () => { it('should return empty array when search term is empty', async () => { const result = await service.performAIFunction({ name: AIFunction.SearchVaultFiles, - arguments: { search_terms: [''] }, + arguments: { search_terms: [''], user_message: 'test search' }, toolId: 'tool_2' } as any); @@ -117,7 +117,7 @@ describe('AIFunctionService - Integration Tests', () => { it('should return empty array when search term is whitespace', async () => { const result = await service.performAIFunction({ name: AIFunction.SearchVaultFiles, - arguments: { search_terms: [' '] }, + arguments: { search_terms: [' '], user_message: 'test search' }, toolId: 'tool_3' } as any); @@ -130,7 +130,7 @@ describe('AIFunctionService - Integration Tests', () => { const result = await service.performAIFunction({ name: AIFunction.SearchVaultFiles, - arguments: { search_terms: ['nonexistent'] }, + arguments: { search_terms: ['nonexistent'], user_message: 'test search' }, toolId: 'tool_4' } as any); @@ -150,7 +150,7 @@ describe('AIFunctionService - Integration Tests', () => { const result = await service.performAIFunction({ name: AIFunction.SearchVaultFiles, - arguments: { search_terms: ['single'] }, + arguments: { search_terms: ['single'], user_message: 'test search' }, toolId: 'tool_5' } as any); @@ -170,7 +170,7 @@ describe('AIFunctionService - Integration Tests', () => { const result = await service.performAIFunction({ name: AIFunction.ReadVaultFiles, - arguments: { file_paths: ['file1.md', 'file2.md', 'file3.md'] }, + arguments: { file_paths: ['file1.md', 'file2.md', 'file3.md'], user_message: 'test search' }, toolId: 'tool_6' } as any); @@ -191,7 +191,7 @@ describe('AIFunctionService - Integration Tests', () => { const result = await service.performAIFunction({ name: AIFunction.ReadVaultFiles, - arguments: { file_paths: ['exists.md', 'missing1.md', 'missing2.md'] }, + arguments: { file_paths: ['exists.md', 'missing1.md', 'missing2.md'], user_message: 'test search' }, toolId: 'tool_7' } as any); @@ -212,7 +212,7 @@ describe('AIFunctionService - Integration Tests', () => { const result = await service.performAIFunction({ name: AIFunction.ReadVaultFiles, - arguments: { file_paths: ['a.md', 'missing.md', 'b.md'] }, + arguments: { file_paths: ['a.md', 'missing.md', 'b.md'], user_message: 'test search' }, toolId: 'tool_8' } as any); @@ -225,7 +225,7 @@ describe('AIFunctionService - Integration Tests', () => { it('should handle empty file list', async () => { const result = await service.performAIFunction({ name: AIFunction.ReadVaultFiles, - arguments: { file_paths: [] }, + arguments: { file_paths: [], user_message: 'test search' }, toolId: 'tool_9' } as any); @@ -237,7 +237,7 @@ describe('AIFunctionService - Integration Tests', () => { const result = await service.performAIFunction({ name: AIFunction.ReadVaultFiles, - arguments: { file_paths: ['single.md'] }, + arguments: { file_paths: ['single.md'], user_message: 'test search' }, toolId: 'tool_10' } as any); @@ -254,7 +254,8 @@ describe('AIFunctionService - Integration Tests', () => { name: AIFunction.WriteVaultFile, arguments: { file_path: 'notes/new-note.md', - content: '# New Note\n\nContent here' + content: '# New Note\n\nContent here', + user_message: 'test search' }, toolId: 'tool_11' } as any); @@ -274,7 +275,8 @@ describe('AIFunctionService - Integration Tests', () => { name: AIFunction.WriteVaultFile, arguments: { file_path: 'protected.md', - content: 'Content' + content: 'Content', + user_message: 'test search' }, toolId: 'tool_12' } as any); @@ -290,7 +292,8 @@ describe('AIFunctionService - Integration Tests', () => { name: AIFunction.WriteVaultFile, arguments: { file_path: 'folder\\subfolder\\file.md', - content: 'Content' + content: 'Content', + user_message: 'test search' }, toolId: 'tool_13' } as any); @@ -309,7 +312,8 @@ describe('AIFunctionService - Integration Tests', () => { name: AIFunction.WriteVaultFile, arguments: { file_path: 'empty.md', - content: '' + content: '', + user_message: 'test search' }, toolId: 'tool_14' } as any); @@ -330,7 +334,8 @@ describe('AIFunctionService - Integration Tests', () => { name: AIFunction.DeleteVaultFiles, arguments: { file_paths: ['file1.md', 'file2.md', 'file3.md'], - confirm_deletion: true + confirm_deletion: true, + user_message: 'test search' }, toolId: 'tool_15' } as any); @@ -349,7 +354,8 @@ describe('AIFunctionService - Integration Tests', () => { name: AIFunction.DeleteVaultFiles, arguments: { file_paths: ['file1.md', 'file2.md'], - confirm_deletion: false + confirm_deletion: false, + user_message: 'test search' }, toolId: 'tool_16' } as any); @@ -370,7 +376,8 @@ describe('AIFunctionService - Integration Tests', () => { name: AIFunction.DeleteVaultFiles, arguments: { file_paths: ['a.md', 'missing.md', 'c.md'], - confirm_deletion: true + confirm_deletion: true, + user_message: 'test search' }, toolId: 'tool_17' } as any); @@ -391,7 +398,8 @@ describe('AIFunctionService - Integration Tests', () => { name: AIFunction.DeleteVaultFiles, arguments: { file_paths: ['file1.md', 'file2.md'], - confirm_deletion: true + confirm_deletion: true, + user_message: 'test search' }, toolId: 'tool_18' } as any); @@ -406,7 +414,8 @@ describe('AIFunctionService - Integration Tests', () => { name: AIFunction.DeleteVaultFiles, arguments: { file_paths: [], - confirm_deletion: true + confirm_deletion: true, + user_message: 'test search' }, toolId: 'tool_19' } as any); @@ -426,7 +435,8 @@ describe('AIFunctionService - Integration Tests', () => { name: AIFunction.MoveVaultFiles, arguments: { source_paths: ['a.md', 'b.md', 'c.md'], - destination_paths: ['dest/a.md', 'dest/b.md', 'dest/c.md'] + destination_paths: ['dest/a.md', 'dest/b.md', 'dest/c.md'], + user_message: 'test search' }, toolId: 'tool_20' } as any); @@ -445,7 +455,8 @@ describe('AIFunctionService - Integration Tests', () => { name: AIFunction.MoveVaultFiles, arguments: { source_paths: ['a.md', 'b.md'], - destination_paths: ['dest/a.md'] + destination_paths: ['dest/a.md'], + user_message: 'test search' }, toolId: 'tool_21' } as any); @@ -466,7 +477,8 @@ describe('AIFunctionService - Integration Tests', () => { name: AIFunction.MoveVaultFiles, arguments: { source_paths: ['a.md', 'b.md', 'c.md'], - destination_paths: ['new/a.md', 'existing.md', 'new/c.md'] + destination_paths: ['new/a.md', 'existing.md', 'new/c.md'], + user_message: 'test search' }, toolId: 'tool_22' } as any); @@ -485,7 +497,8 @@ describe('AIFunctionService - Integration Tests', () => { name: AIFunction.MoveVaultFiles, arguments: { source_paths: ['old/file.md'], - destination_paths: ['new/file.md'] + destination_paths: ['new/file.md'], + user_message: 'test search' }, toolId: 'tool_23' } as any); @@ -498,7 +511,8 @@ describe('AIFunctionService - Integration Tests', () => { name: AIFunction.MoveVaultFiles, arguments: { source_paths: [], - destination_paths: [] + destination_paths: [], + user_message: 'test search' }, toolId: 'tool_24' } as any); @@ -575,7 +589,7 @@ describe('AIFunctionService - Integration Tests', () => { const searchResult = await service.performAIFunction({ name: AIFunction.SearchVaultFiles, - arguments: { search_terms: ['test'] }, + arguments: { search_terms: ['test'], user_message: 'test search' }, toolId: 'search_1' } as any); @@ -586,7 +600,7 @@ describe('AIFunctionService - Integration Tests', () => { const readResult = await service.performAIFunction({ name: AIFunction.ReadVaultFiles, - arguments: { file_paths: [foundPath] }, + arguments: { file_paths: [foundPath], user_message: 'test search' }, toolId: 'read_1' } as any); @@ -602,7 +616,8 @@ describe('AIFunctionService - Integration Tests', () => { name: AIFunction.WriteVaultFile, arguments: { file_path: 'temp.md', - content: 'Temporary content' + content: 'Temporary content', + user_message: 'test search' }, toolId: 'write_1' } as any); @@ -616,7 +631,8 @@ describe('AIFunctionService - Integration Tests', () => { name: AIFunction.MoveVaultFiles, arguments: { source_paths: ['temp.md'], - destination_paths: ['archive/temp.md'] + destination_paths: ['archive/temp.md'], + user_message: 'test search' }, toolId: 'move_1' } as any); diff --git a/main.ts b/main.ts index 90c9ab1..68c88da 100644 --- a/main.ts +++ b/main.ts @@ -25,15 +25,15 @@ export default class VaultkeeperAIPlugin extends Plugin { ); this.addCommand({ - id: "vaultkeeper-ai", - name: "Vaultkeeper AI", - callback: () => { - this.activateView(); + id: "open", + name: "Open", + callback: async () => { + await this.activateView(); } }); - this.addRibbonIcon("sparkles", "Vaultkeeper AI", (_: MouseEvent) => { - this.activateView(); + this.addRibbonIcon("sparkles", "Vaultkeeper AI", async () => { + await this.activateView(); }); this.addSettingTab(new VaultkeeperAISettingTab()); @@ -43,7 +43,7 @@ export default class VaultkeeperAIPlugin extends Plugin { }); } - public async onunload() { + public onunload() { Resolve(Services.StatusBarService).removeStatusBarMessage(); DeregisterAllServices(); } @@ -62,7 +62,7 @@ export default class VaultkeeperAIPlugin extends Plugin { } if (leaf != null) { - workspace.revealLeaf(leaf); + await workspace.revealLeaf(leaf); } } diff --git a/package-lock.json b/package-lock.json index c9f1b0d..be6c07d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -31,15 +31,16 @@ "remark-rehype": "^11.1.2", "remark-wiki-link": "^2.0.1", "unified": "^11.0.5", - "uuid": "^13.0.0" + "uuid": "^13.0.0", + "zod": "^4.1.12" }, "devDependencies": { "@testing-library/svelte": "^5.2.8", "@types/express": "^5.0.5", "@types/node": "^24.10.0", "@types/path-browserify": "^1.0.3", - "@typescript-eslint/eslint-plugin": "8.46.3", - "@typescript-eslint/parser": "8.46.3", + "@typescript-eslint/eslint-plugin": "8.46.4", + "@typescript-eslint/parser": "8.46.4", "@vitest/ui": "^4.0.8", "builtin-modules": "5.0.0", "esbuild": "^0.27.0", @@ -1021,9 +1022,9 @@ "license": "MIT" }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.53.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.53.1.tgz", - "integrity": "sha512-bxZtughE4VNVJlL1RdoSE545kc4JxL7op57KKoi59/gwuU5rV6jLWFXXc8jwgFoT6vtj+ZjO+Z2C5nrY0Cl6wA==", + "version": "4.53.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.53.2.tgz", + "integrity": "sha512-yDPzwsgiFO26RJA4nZo8I+xqzh7sJTZIWQOxn+/XOdPE31lAvLIYCKqjV+lNH/vxE2L2iH3plKxDCRK6i+CwhA==", "cpu": [ "arm" ], @@ -1035,9 +1036,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.53.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.53.1.tgz", - "integrity": "sha512-44a1hreb02cAAfAKmZfXVercPFaDjqXCK+iKeVOlJ9ltvnO6QqsBHgKVPTu+MJHSLLeMEUbeG2qiDYgbFPU48g==", + "version": "4.53.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.53.2.tgz", + "integrity": "sha512-k8FontTxIE7b0/OGKeSN5B6j25EuppBcWM33Z19JoVT7UTXFSo3D9CdU39wGTeb29NO3XxpMNauh09B+Ibw+9g==", "cpu": [ "arm64" ], @@ -1049,9 +1050,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.53.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.53.1.tgz", - "integrity": "sha512-usmzIgD0rf1syoOZ2WZvy8YpXK5G1V3btm3QZddoGSa6mOgfXWkkv+642bfUUldomgrbiLQGrPryb7DXLovPWQ==", + "version": "4.53.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.53.2.tgz", + "integrity": "sha512-A6s4gJpomNBtJ2yioj8bflM2oogDwzUiMl2yNJ2v9E7++sHrSrsQ29fOfn5DM/iCzpWcebNYEdXpaK4tr2RhfQ==", "cpu": [ "arm64" ], @@ -1063,9 +1064,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.53.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.53.1.tgz", - "integrity": "sha512-is3r/k4vig2Gt8mKtTlzzyaSQ+hd87kDxiN3uDSDwggJLUV56Umli6OoL+/YZa/KvtdrdyNfMKHzL/P4siOOmg==", + "version": "4.53.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.53.2.tgz", + "integrity": "sha512-e6XqVmXlHrBlG56obu9gDRPW3O3hLxpwHpLsBJvuI8qqnsrtSZ9ERoWUXtPOkY8c78WghyPHZdmPhHLWNdAGEw==", "cpu": [ "x64" ], @@ -1077,9 +1078,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.53.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.53.1.tgz", - "integrity": "sha512-QJ1ksgp/bDJkZB4daldVmHaEQkG4r8PUXitCOC2WRmRaSaHx5RwPoI3DHVfXKwDkB+Sk6auFI/+JHacTekPRSw==", + "version": "4.53.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.53.2.tgz", + "integrity": "sha512-v0E9lJW8VsrwPux5Qe5CwmH/CF/2mQs6xU1MF3nmUxmZUCHazCjLgYvToOk+YuuUqLQBio1qkkREhxhc656ViA==", "cpu": [ "arm64" ], @@ -1091,9 +1092,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.53.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.53.1.tgz", - "integrity": "sha512-J6ma5xgAzvqsnU6a0+jgGX/gvoGokqpkx6zY4cWizRrm0ffhHDpJKQgC8dtDb3+MqfZDIqs64REbfHDMzxLMqQ==", + "version": "4.53.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.53.2.tgz", + "integrity": "sha512-ClAmAPx3ZCHtp6ysl4XEhWU69GUB1D+s7G9YjHGhIGCSrsg00nEGRRZHmINYxkdoJehde8VIsDC5t9C0gb6yqA==", "cpu": [ "x64" ], @@ -1105,9 +1106,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.53.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.53.1.tgz", - "integrity": "sha512-JzWRR41o2U3/KMNKRuZNsDUAcAVUYhsPuMlx5RUldw0E4lvSIXFUwejtYz1HJXohUmqs/M6BBJAUBzKXZVddbg==", + "version": "4.53.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.53.2.tgz", + "integrity": "sha512-EPlb95nUsz6Dd9Qy13fI5kUPXNSljaG9FiJ4YUGU1O/Q77i5DYFW5KR8g1OzTcdZUqQQ1KdDqsTohdFVwCwjqg==", "cpu": [ "arm" ], @@ -1119,9 +1120,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.53.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.53.1.tgz", - "integrity": "sha512-L8kRIrnfMrEoHLHtHn+4uYA52fiLDEDyezgxZtGUTiII/yb04Krq+vk3P2Try+Vya9LeCE9ZHU8CXD6J9EhzHQ==", + "version": "4.53.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.53.2.tgz", + "integrity": "sha512-BOmnVW+khAUX+YZvNfa0tGTEMVVEerOxN0pDk2E6N6DsEIa2Ctj48FOMfNDdrwinocKaC7YXUZ1pHlKpnkja/Q==", "cpu": [ "arm" ], @@ -1133,9 +1134,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.53.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.53.1.tgz", - "integrity": "sha512-ysAc0MFRV+WtQ8li8hi3EoFi7us6d1UzaS/+Dp7FYZfg3NdDljGMoVyiIp6Ucz7uhlYDBZ/zt6XI0YEZbUO11Q==", + "version": "4.53.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.53.2.tgz", + "integrity": "sha512-Xt2byDZ+6OVNuREgBXr4+CZDJtrVso5woFtpKdGPhpTPHcNG7D8YXeQzpNbFRxzTVqJf7kvPMCub/pcGUWgBjA==", "cpu": [ "arm64" ], @@ -1147,9 +1148,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.53.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.53.1.tgz", - "integrity": "sha512-UV6l9MJpDbDZZ/fJvqNcvO1PcivGEf1AvKuTcHoLjVZVFeAMygnamCTDikCVMRnA+qJe+B3pSbgX2+lBMqgBhA==", + "version": "4.53.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.53.2.tgz", + "integrity": "sha512-+LdZSldy/I9N8+klim/Y1HsKbJ3BbInHav5qE9Iy77dtHC/pibw1SR/fXlWyAk0ThnpRKoODwnAuSjqxFRDHUQ==", "cpu": [ "arm64" ], @@ -1161,9 +1162,9 @@ ] }, "node_modules/@rollup/rollup-linux-loong64-gnu": { - "version": "4.53.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.53.1.tgz", - "integrity": "sha512-UDUtelEprkA85g95Q+nj3Xf0M4hHa4DiJ+3P3h4BuGliY4NReYYqwlc0Y8ICLjN4+uIgCEvaygYlpf0hUj90Yg==", + "version": "4.53.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.53.2.tgz", + "integrity": "sha512-8ms8sjmyc1jWJS6WdNSA23rEfdjWB30LH8Wqj0Cqvv7qSHnvw6kgMMXRdop6hkmGPlyYBdRPkjJnj3KCUHV/uQ==", "cpu": [ "loong64" ], @@ -1175,9 +1176,9 @@ ] }, "node_modules/@rollup/rollup-linux-ppc64-gnu": { - "version": "4.53.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.53.1.tgz", - "integrity": "sha512-vrRn+BYhEtNOte/zbc2wAUQReJXxEx2URfTol6OEfY2zFEUK92pkFBSXRylDM7aHi+YqEPJt9/ABYzmcrS4SgQ==", + "version": "4.53.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.53.2.tgz", + "integrity": "sha512-3HRQLUQbpBDMmzoxPJYd3W6vrVHOo2cVW8RUo87Xz0JPJcBLBr5kZ1pGcQAhdZgX9VV7NbGNipah1omKKe23/g==", "cpu": [ "ppc64" ], @@ -1189,9 +1190,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.53.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.53.1.tgz", - "integrity": "sha512-gto/1CxHyi4A7YqZZNznQYrVlPSaodOBPKM+6xcDSCMVZN/Fzb4K+AIkNz/1yAYz9h3Ng+e2fY9H6bgawVq17w==", + "version": "4.53.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.53.2.tgz", + "integrity": "sha512-fMjKi+ojnmIvhk34gZP94vjogXNNUKMEYs+EDaB/5TG/wUkoeua7p7VCHnE6T2Tx+iaghAqQX8teQzcvrYpaQA==", "cpu": [ "riscv64" ], @@ -1203,9 +1204,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.53.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.53.1.tgz", - "integrity": "sha512-KZ6Vx7jAw3aLNjFR8eYVcQVdFa/cvBzDNRFM3z7XhNNunWjA03eUrEwJYPk0G8V7Gs08IThFKcAPS4WY/ybIrQ==", + "version": "4.53.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.53.2.tgz", + "integrity": "sha512-XuGFGU+VwUUV5kLvoAdi0Wz5Xbh2SrjIxCtZj6Wq8MDp4bflb/+ThZsVxokM7n0pcbkEr2h5/pzqzDYI7cCgLQ==", "cpu": [ "riscv64" ], @@ -1217,9 +1218,9 @@ ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.53.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.53.1.tgz", - "integrity": "sha512-HvEixy2s/rWNgpwyKpXJcHmE7om1M89hxBTBi9Fs6zVuLU4gOrEMQNbNsN/tBVIMbLyysz/iwNiGtMOpLAOlvA==", + "version": "4.53.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.53.2.tgz", + "integrity": "sha512-w6yjZF0P+NGzWR3AXWX9zc0DNEGdtvykB03uhonSHMRa+oWA6novflo2WaJr6JZakG2ucsyb+rvhrKac6NIy+w==", "cpu": [ "s390x" ], @@ -1231,9 +1232,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.53.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.53.1.tgz", - "integrity": "sha512-E/n8x2MSjAQgjj9IixO4UeEUeqXLtiA7pyoXCFYLuXpBA/t2hnbIdxHfA7kK9BFsYAoNU4st1rHYdldl8dTqGA==", + "version": "4.53.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.53.2.tgz", + "integrity": "sha512-yo8d6tdfdeBArzC7T/PnHd7OypfI9cbuZzPnzLJIyKYFhAQ8SvlkKtKBMbXDxe1h03Rcr7u++nFS7tqXz87Gtw==", "cpu": [ "x64" ], @@ -1245,9 +1246,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.53.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.53.1.tgz", - "integrity": "sha512-IhJ087PbLOQXCN6Ui/3FUkI9pWNZe/Z7rEIVOzMsOs1/HSAECCvSZ7PkIbkNqL/AZn6WbZvnoVZw/qwqYMo4/w==", + "version": "4.53.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.53.2.tgz", + "integrity": "sha512-ah59c1YkCxKExPP8O9PwOvs+XRLKwh/mV+3YdKqQ5AMQ0r4M4ZDuOrpWkUaqO7fzAHdINzV9tEVu8vNw48z0lA==", "cpu": [ "x64" ], @@ -1259,9 +1260,9 @@ ] }, "node_modules/@rollup/rollup-openharmony-arm64": { - "version": "4.53.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.53.1.tgz", - "integrity": "sha512-0++oPNgLJHBblreu0SFM7b3mAsBJBTY0Ksrmu9N6ZVrPiTkRgda52mWR7TKhHAsUb9noCjFvAw9l6ZO1yzaVbA==", + "version": "4.53.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.53.2.tgz", + "integrity": "sha512-4VEd19Wmhr+Zy7hbUsFZ6YXEiP48hE//KPLCSVNY5RMGX2/7HZ+QkN55a3atM1C/BZCGIgqN+xrVgtdak2S9+A==", "cpu": [ "arm64" ], @@ -1273,9 +1274,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.53.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.53.1.tgz", - "integrity": "sha512-VJXivz61c5uVdbmitLkDlbcTk9Or43YC2QVLRkqp86QoeFSqI81bNgjhttqhKNMKnQMWnecOCm7lZz4s+WLGpQ==", + "version": "4.53.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.53.2.tgz", + "integrity": "sha512-IlbHFYc/pQCgew/d5fslcy1KEaYVCJ44G8pajugd8VoOEI8ODhtb/j8XMhLpwHCMB3yk2J07ctup10gpw2nyMA==", "cpu": [ "arm64" ], @@ -1287,9 +1288,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.53.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.53.1.tgz", - "integrity": "sha512-NmZPVTUOitCXUH6erJDzTQ/jotYw4CnkMDjCYRxNHVD9bNyfrGoIse684F9okwzKCV4AIHRbUkeTBc9F2OOH5Q==", + "version": "4.53.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.53.2.tgz", + "integrity": "sha512-lNlPEGgdUfSzdCWU176ku/dQRnA7W+Gp8d+cWv73jYrb8uT7HTVVxq62DUYxjbaByuf1Yk0RIIAbDzp+CnOTFg==", "cpu": [ "ia32" ], @@ -1301,9 +1302,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-gnu": { - "version": "4.53.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.53.1.tgz", - "integrity": "sha512-2SNj7COIdAf6yliSpLdLG8BEsp5lgzRehgfkP0Av8zKfQFKku6JcvbobvHASPJu4f3BFxej5g+HuQPvqPhHvpQ==", + "version": "4.53.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.53.2.tgz", + "integrity": "sha512-S6YojNVrHybQis2lYov1sd+uj7K0Q05NxHcGktuMMdIQ2VixGwAfbJ23NnlvvVV1bdpR2m5MsNBViHJKcA4ADw==", "cpu": [ "x64" ], @@ -1315,9 +1316,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.53.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.53.1.tgz", - "integrity": "sha512-rLarc1Ofcs3DHtgSzFO31pZsCh8g05R2azN1q3fF+H423Co87My0R+tazOEvYVKXSLh8C4LerMK41/K7wlklcg==", + "version": "4.53.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.53.2.tgz", + "integrity": "sha512-k+/Rkcyx//P6fetPoLMb8pBeqJBNGx81uuf7iljX9++yNBVRDQgD04L+SVXmXmh5ZP4/WOp4mWF0kmi06PW2tA==", "cpu": [ "x64" ], @@ -1745,17 +1746,17 @@ "license": "MIT" }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.46.3", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.46.3.tgz", - "integrity": "sha512-sbaQ27XBUopBkRiuY/P9sWGOWUW4rl8fDoHIUmLpZd8uldsTyB4/Zg6bWTegPoTLnKj9Hqgn3QD6cjPNB32Odw==", + "version": "8.46.4", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.46.4.tgz", + "integrity": "sha512-R48VhmTJqplNyDxCyqqVkFSZIx1qX6PzwqgcXn1olLrzxcSBDlOsbtcnQuQhNtnNiJ4Xe5gREI1foajYaYU2Vg==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.46.3", - "@typescript-eslint/type-utils": "8.46.3", - "@typescript-eslint/utils": "8.46.3", - "@typescript-eslint/visitor-keys": "8.46.3", + "@typescript-eslint/scope-manager": "8.46.4", + "@typescript-eslint/type-utils": "8.46.4", + "@typescript-eslint/utils": "8.46.4", + "@typescript-eslint/visitor-keys": "8.46.4", "graphemer": "^1.4.0", "ignore": "^7.0.0", "natural-compare": "^1.4.0", @@ -1769,22 +1770,22 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^8.46.3", + "@typescript-eslint/parser": "^8.46.4", "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/@typescript-eslint/parser": { - "version": "8.46.3", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.46.3.tgz", - "integrity": "sha512-6m1I5RmHBGTnUGS113G04DMu3CpSdxCAU/UvtjNWL4Nuf3MW9tQhiJqRlHzChIkhy6kZSAQmc+I1bcGjE3yNKg==", + "version": "8.46.4", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.46.4.tgz", + "integrity": "sha512-tK3GPFWbirvNgsNKto+UmB/cRtn6TZfyw0D6IKrW55n6Vbs7KJoZtI//kpTKzE/DUmmnAFD8/Ca46s7Obs92/w==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.46.3", - "@typescript-eslint/types": "8.46.3", - "@typescript-eslint/typescript-estree": "8.46.3", - "@typescript-eslint/visitor-keys": "8.46.3", + "@typescript-eslint/scope-manager": "8.46.4", + "@typescript-eslint/types": "8.46.4", + "@typescript-eslint/typescript-estree": "8.46.4", + "@typescript-eslint/visitor-keys": "8.46.4", "debug": "^4.3.4" }, "engines": { @@ -1800,14 +1801,14 @@ } }, "node_modules/@typescript-eslint/project-service": { - "version": "8.46.3", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.46.3.tgz", - "integrity": "sha512-Fz8yFXsp2wDFeUElO88S9n4w1I4CWDTXDqDr9gYvZgUpwXQqmZBr9+NTTql5R3J7+hrJZPdpiWaB9VNhAKYLuQ==", + "version": "8.46.4", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.46.4.tgz", + "integrity": "sha512-nPiRSKuvtTN+no/2N1kt2tUh/HoFzeEgOm9fQ6XQk4/ApGqjx0zFIIaLJ6wooR1HIoozvj2j6vTi/1fgAz7UYQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.46.3", - "@typescript-eslint/types": "^8.46.3", + "@typescript-eslint/tsconfig-utils": "^8.46.4", + "@typescript-eslint/types": "^8.46.4", "debug": "^4.3.4" }, "engines": { @@ -1822,14 +1823,14 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.46.3", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.46.3.tgz", - "integrity": "sha512-FCi7Y1zgrmxp3DfWfr+3m9ansUUFoy8dkEdeQSgA9gbm8DaHYvZCdkFRQrtKiedFf3Ha6VmoqoAaP68+i+22kg==", + "version": "8.46.4", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.46.4.tgz", + "integrity": "sha512-tMDbLGXb1wC+McN1M6QeDx7P7c0UWO5z9CXqp7J8E+xGcJuUuevWKxuG8j41FoweS3+L41SkyKKkia16jpX7CA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.46.3", - "@typescript-eslint/visitor-keys": "8.46.3" + "@typescript-eslint/types": "8.46.4", + "@typescript-eslint/visitor-keys": "8.46.4" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1840,9 +1841,9 @@ } }, "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.46.3", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.46.3.tgz", - "integrity": "sha512-GLupljMniHNIROP0zE7nCcybptolcH8QZfXOpCfhQDAdwJ/ZTlcaBOYebSOZotpti/3HrHSw7D3PZm75gYFsOA==", + "version": "8.46.4", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.46.4.tgz", + "integrity": "sha512-+/XqaZPIAk6Cjg7NWgSGe27X4zMGqrFqZ8atJsX3CWxH/jACqWnrWI68h7nHQld0y+k9eTTjb9r+KU4twLoo9A==", "dev": true, "license": "MIT", "engines": { @@ -1857,15 +1858,15 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.46.3", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.46.3.tgz", - "integrity": "sha512-ZPCADbr+qfz3aiTTYNNkCbUt+cjNwI/5McyANNrFBpVxPt7GqpEYz5ZfdwuFyGUnJ9FdDXbGODUu6iRCI6XRXw==", + "version": "8.46.4", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.46.4.tgz", + "integrity": "sha512-V4QC8h3fdT5Wro6vANk6eojqfbv5bpwHuMsBcJUJkqs2z5XnYhJzyz9Y02eUmF9u3PgXEUiOt4w4KHR3P+z0PQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.46.3", - "@typescript-eslint/typescript-estree": "8.46.3", - "@typescript-eslint/utils": "8.46.3", + "@typescript-eslint/types": "8.46.4", + "@typescript-eslint/typescript-estree": "8.46.4", + "@typescript-eslint/utils": "8.46.4", "debug": "^4.3.4", "ts-api-utils": "^2.1.0" }, @@ -1882,9 +1883,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.46.3", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.46.3.tgz", - "integrity": "sha512-G7Ok9WN/ggW7e/tOf8TQYMaxgID3Iujn231hfi0Pc7ZheztIJVpO44ekY00b7akqc6nZcvregk0Jpah3kep6hA==", + "version": "8.46.4", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.46.4.tgz", + "integrity": "sha512-USjyxm3gQEePdUwJBFjjGNG18xY9A2grDVGuk7/9AkjIF1L+ZrVnwR5VAU5JXtUnBL/Nwt3H31KlRDaksnM7/w==", "dev": true, "license": "MIT", "engines": { @@ -1896,16 +1897,16 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.46.3", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.46.3.tgz", - "integrity": "sha512-f/NvtRjOm80BtNM5OQtlaBdM5BRFUv7gf381j9wygDNL+qOYSNOgtQ/DCndiYi80iIOv76QqaTmp4fa9hwI0OA==", + "version": "8.46.4", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.46.4.tgz", + "integrity": "sha512-7oV2qEOr1d4NWNmpXLR35LvCfOkTNymY9oyW+lUHkmCno7aOmIf/hMaydnJBUTBMRCOGZh8YjkFOc8dadEoNGA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.46.3", - "@typescript-eslint/tsconfig-utils": "8.46.3", - "@typescript-eslint/types": "8.46.3", - "@typescript-eslint/visitor-keys": "8.46.3", + "@typescript-eslint/project-service": "8.46.4", + "@typescript-eslint/tsconfig-utils": "8.46.4", + "@typescript-eslint/types": "8.46.4", + "@typescript-eslint/visitor-keys": "8.46.4", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", @@ -1925,16 +1926,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.46.3", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.46.3.tgz", - "integrity": "sha512-VXw7qmdkucEx9WkmR3ld/u6VhRyKeiF1uxWwCy/iuNfokjJ7VhsgLSOTjsol8BunSw190zABzpwdNsze2Kpo4g==", + "version": "8.46.4", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.46.4.tgz", + "integrity": "sha512-AbSv11fklGXV6T28dp2Me04Uw90R2iJ30g2bgLz529Koehrmkbs1r7paFqr1vPCZi7hHwYxYtxfyQMRC8QaVSg==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.7.0", - "@typescript-eslint/scope-manager": "8.46.3", - "@typescript-eslint/types": "8.46.3", - "@typescript-eslint/typescript-estree": "8.46.3" + "@typescript-eslint/scope-manager": "8.46.4", + "@typescript-eslint/types": "8.46.4", + "@typescript-eslint/typescript-estree": "8.46.4" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1949,13 +1950,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.46.3", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.46.3.tgz", - "integrity": "sha512-uk574k8IU0rOF/AjniX8qbLSGURJVUCeM5e4MIMKBFFi8weeiLrG1fyQejyLXQpRZbU/1BuQasleV/RfHC3hHg==", + "version": "8.46.4", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.46.4.tgz", + "integrity": "sha512-/++5CYLQqsO9HFGLI7APrxBJYo+5OCMpViuhV8q5/Qa3o5mMrF//eQHks+PXcsAVaLdn817fMuS7zqoXNNZGaw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.46.3", + "@typescript-eslint/types": "8.46.4", "eslint-visitor-keys": "^4.2.1" }, "engines": { @@ -8068,9 +8069,9 @@ } }, "node_modules/rollup": { - "version": "4.53.1", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.53.1.tgz", - "integrity": "sha512-n2I0V0lN3E9cxxMqBCT3opWOiQBzRN7UG60z/WDKqdX2zHUS/39lezBcsckZFsV6fUTSnfqI7kHf60jDAPGKug==", + "version": "4.53.2", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.53.2.tgz", + "integrity": "sha512-MHngMYwGJVi6Fmnk6ISmnk7JAHRNF0UkuucA0CUW3N3a4KnONPEZz+vUanQP/ZC/iY1Qkf3bwPWzyY84wEks1g==", "dev": true, "license": "MIT", "dependencies": { @@ -8084,28 +8085,28 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.53.1", - "@rollup/rollup-android-arm64": "4.53.1", - "@rollup/rollup-darwin-arm64": "4.53.1", - "@rollup/rollup-darwin-x64": "4.53.1", - "@rollup/rollup-freebsd-arm64": "4.53.1", - "@rollup/rollup-freebsd-x64": "4.53.1", - "@rollup/rollup-linux-arm-gnueabihf": "4.53.1", - "@rollup/rollup-linux-arm-musleabihf": "4.53.1", - "@rollup/rollup-linux-arm64-gnu": "4.53.1", - "@rollup/rollup-linux-arm64-musl": "4.53.1", - "@rollup/rollup-linux-loong64-gnu": "4.53.1", - "@rollup/rollup-linux-ppc64-gnu": "4.53.1", - "@rollup/rollup-linux-riscv64-gnu": "4.53.1", - "@rollup/rollup-linux-riscv64-musl": "4.53.1", - "@rollup/rollup-linux-s390x-gnu": "4.53.1", - "@rollup/rollup-linux-x64-gnu": "4.53.1", - "@rollup/rollup-linux-x64-musl": "4.53.1", - "@rollup/rollup-openharmony-arm64": "4.53.1", - "@rollup/rollup-win32-arm64-msvc": "4.53.1", - "@rollup/rollup-win32-ia32-msvc": "4.53.1", - "@rollup/rollup-win32-x64-gnu": "4.53.1", - "@rollup/rollup-win32-x64-msvc": "4.53.1", + "@rollup/rollup-android-arm-eabi": "4.53.2", + "@rollup/rollup-android-arm64": "4.53.2", + "@rollup/rollup-darwin-arm64": "4.53.2", + "@rollup/rollup-darwin-x64": "4.53.2", + "@rollup/rollup-freebsd-arm64": "4.53.2", + "@rollup/rollup-freebsd-x64": "4.53.2", + "@rollup/rollup-linux-arm-gnueabihf": "4.53.2", + "@rollup/rollup-linux-arm-musleabihf": "4.53.2", + "@rollup/rollup-linux-arm64-gnu": "4.53.2", + "@rollup/rollup-linux-arm64-musl": "4.53.2", + "@rollup/rollup-linux-loong64-gnu": "4.53.2", + "@rollup/rollup-linux-ppc64-gnu": "4.53.2", + "@rollup/rollup-linux-riscv64-gnu": "4.53.2", + "@rollup/rollup-linux-riscv64-musl": "4.53.2", + "@rollup/rollup-linux-s390x-gnu": "4.53.2", + "@rollup/rollup-linux-x64-gnu": "4.53.2", + "@rollup/rollup-linux-x64-musl": "4.53.2", + "@rollup/rollup-openharmony-arm64": "4.53.2", + "@rollup/rollup-win32-arm64-msvc": "4.53.2", + "@rollup/rollup-win32-ia32-msvc": "4.53.2", + "@rollup/rollup-win32-x64-gnu": "4.53.2", + "@rollup/rollup-win32-x64-msvc": "4.53.2", "fsevents": "~2.3.2" } }, @@ -9306,17 +9307,17 @@ } }, "node_modules/typescript-eslint": { - "version": "8.46.3", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.46.3.tgz", - "integrity": "sha512-bAfgMavTuGo+8n6/QQDVQz4tZ4f7Soqg53RbrlZQEoAltYop/XR4RAts/I0BrO3TTClTSTFJ0wYbla+P8cEWJA==", + "version": "8.46.4", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.46.4.tgz", + "integrity": "sha512-KALyxkpYV5Ix7UhvjTwJXZv76VWsHG+NjNlt/z+a17SOQSiOcBdUXdbJdyXi7RPxrBFECtFOiPwUJQusJuCqrg==", "dev": true, "license": "MIT", "peer": true, "dependencies": { - "@typescript-eslint/eslint-plugin": "8.46.3", - "@typescript-eslint/parser": "8.46.3", - "@typescript-eslint/typescript-estree": "8.46.3", - "@typescript-eslint/utils": "8.46.3" + "@typescript-eslint/eslint-plugin": "8.46.4", + "@typescript-eslint/parser": "8.46.4", + "@typescript-eslint/typescript-estree": "8.46.4", + "@typescript-eslint/utils": "8.46.4" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -10568,6 +10569,15 @@ "dev": true, "license": "MIT" }, + "node_modules/zod": { + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.1.12.tgz", + "integrity": "sha512-JInaHOamG8pt5+Ey8kGmdcAcg3OL9reK8ltczgHTAwNhMys/6ThXHityHxVV2p3fkw/c+MAvBHFVYHFZDmjMCQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, "node_modules/zwitch": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz", diff --git a/package.json b/package.json index d82db20..f104887 100644 --- a/package.json +++ b/package.json @@ -24,8 +24,8 @@ "@types/express": "^5.0.5", "@types/node": "^24.10.0", "@types/path-browserify": "^1.0.3", - "@typescript-eslint/eslint-plugin": "8.46.3", - "@typescript-eslint/parser": "8.46.3", + "@typescript-eslint/eslint-plugin": "8.46.4", + "@typescript-eslint/parser": "8.46.4", "@vitest/ui": "^4.0.8", "builtin-modules": "5.0.0", "esbuild": "^0.27.0", @@ -64,6 +64,7 @@ "remark-rehype": "^11.1.2", "remark-wiki-link": "^2.0.1", "unified": "^11.0.5", - "uuid": "^13.0.0" + "uuid": "^13.0.0", + "zod": "^4.1.12" } }