/** * @fileoverview Storage interface definitions for the tm-core package * This file defines the contract for all storage implementations */ import type { Task, TaskMetadata, TaskStatus } from '../types/index.js'; /** * Result type for updateTaskStatus operations */ export interface UpdateStatusResult { success: boolean; oldStatus: TaskStatus; newStatus: TaskStatus; taskId: string; } /** * Interface for storage operations on tasks * All storage implementations must implement this interface */ export interface IStorage { /** * Load all tasks from storage, optionally filtered by tag * @param tag - Optional tag to filter tasks by * @returns Promise that resolves to an array of tasks */ loadTasks(tag?: string): Promise; /** * Load a single task by ID * @param taskId - ID of the task to load * @param tag - Optional tag context for the task * @returns Promise that resolves to the task or null if not found */ loadTask(taskId: string, tag?: string): Promise; /** * Save tasks to storage, replacing existing tasks * @param tasks - Array of tasks to save * @param tag - Optional tag context for the tasks * @returns Promise that resolves when save is complete */ saveTasks(tasks: Task[], tag?: string): Promise; /** * Append new tasks to existing storage without replacing * @param tasks - Array of tasks to append * @param tag - Optional tag context for the tasks * @returns Promise that resolves when append is complete */ appendTasks(tasks: Task[], tag?: string): Promise; /** * Update a specific task by ID * @param taskId - ID of the task to update * @param updates - Partial task object with fields to update * @param tag - Optional tag context for the task * @returns Promise that resolves when update is complete */ updateTask( taskId: string, updates: Partial, tag?: string ): Promise; /** * Update task or subtask status by ID * @param taskId - ID of the task or subtask (e.g., "1" or "1.2") * @param newStatus - New status to set * @param tag - Optional tag context for the task * @returns Promise that resolves to update result with old and new status */ updateTaskStatus( taskId: string, newStatus: TaskStatus, tag?: string ): Promise; /** * Delete a task by ID * @param taskId - ID of the task to delete * @param tag - Optional tag context for the task * @returns Promise that resolves when deletion is complete */ deleteTask(taskId: string, tag?: string): Promise; /** * Check if tasks exist in storage for the given tag * @param tag - Optional tag to check existence for * @returns Promise that resolves to boolean indicating existence */ exists(tag?: string): Promise; /** * Load metadata about the task collection * @param tag - Optional tag to get metadata for * @returns Promise that resolves to task metadata */ loadMetadata(tag?: string): Promise; /** * Save metadata about the task collection * @param metadata - Metadata object to save * @param tag - Optional tag context for the metadata * @returns Promise that resolves when save is complete */ saveMetadata(metadata: TaskMetadata, tag?: string): Promise; /** * Get all available tags in storage * @returns Promise that resolves to array of available tags */ getAllTags(): Promise; /** * Delete all tasks and metadata for a specific tag * @param tag - Tag to delete * @returns Promise that resolves when deletion is complete */ deleteTag(tag: string): Promise; /** * Rename a tag (move all tasks from old tag to new tag) * @param oldTag - Current tag name * @param newTag - New tag name * @returns Promise that resolves when rename is complete */ renameTag(oldTag: string, newTag: string): Promise; /** * Copy all tasks from one tag to another * @param sourceTag - Source tag to copy from * @param targetTag - Target tag to copy to * @returns Promise that resolves when copy is complete */ copyTag(sourceTag: string, targetTag: string): Promise; /** * Initialize storage (create necessary directories, files, etc.) * @returns Promise that resolves when initialization is complete */ initialize(): Promise; /** * Clean up and close storage connections * @returns Promise that resolves when cleanup is complete */ close(): Promise; /** * Get storage statistics (file sizes, task counts, etc.) * @returns Promise that resolves to storage statistics */ getStats(): Promise; } /** * Storage statistics interface */ export interface StorageStats { /** Total number of tasks across all tags */ totalTasks: number; /** Total number of tags */ totalTags: number; /** Storage size in bytes */ storageSize: number; /** Last modified timestamp */ lastModified: string; /** Available tags with task counts */ tagStats: Array<{ tag: string; taskCount: number; lastModified: string; }>; } /** * Configuration options for storage implementations */ export interface StorageConfig { /** Base path for storage */ basePath: string; /** Enable backup creation */ enableBackup?: boolean; /** Maximum number of backups to keep */ maxBackups?: number; /** Enable compression for storage */ enableCompression?: boolean; /** File encoding (default: utf8) */ encoding?: BufferEncoding; /** Enable atomic writes */ atomicWrites?: boolean; } /** * Base abstract class for storage implementations * Provides common functionality and enforces the interface */ export abstract class BaseStorage implements IStorage { protected config: StorageConfig; constructor(config: StorageConfig) { this.config = config; } // Abstract methods that must be implemented by concrete classes abstract loadTasks(tag?: string): Promise; abstract loadTask(taskId: string, tag?: string): Promise; abstract saveTasks(tasks: Task[], tag?: string): Promise; abstract appendTasks(tasks: Task[], tag?: string): Promise; abstract updateTask( taskId: string, updates: Partial, tag?: string ): Promise; abstract updateTaskStatus( taskId: string, newStatus: TaskStatus, tag?: string ): Promise; abstract deleteTask(taskId: string, tag?: string): Promise; abstract exists(tag?: string): Promise; abstract loadMetadata(tag?: string): Promise; abstract saveMetadata(metadata: TaskMetadata, tag?: string): Promise; abstract getAllTags(): Promise; abstract deleteTag(tag: string): Promise; abstract renameTag(oldTag: string, newTag: string): Promise; abstract copyTag(sourceTag: string, targetTag: string): Promise; abstract initialize(): Promise; abstract close(): Promise; abstract getStats(): Promise; /** * Utility method to generate backup filename * @param originalPath - Original file path * @returns Backup file path with timestamp */ protected generateBackupPath(originalPath: string): string { const timestamp = new Date().toISOString().replace(/[:.]/g, '-'); const parts = originalPath.split('.'); const extension = parts.pop(); const baseName = parts.join('.'); return `${baseName}.backup.${timestamp}.${extension}`; } /** * Utility method to validate task data before storage operations * @param task - Task to validate * @throws Error if task is invalid */ protected validateTask(task: Task): void { if (!task.id) { throw new Error('Task ID is required'); } if (!task.title) { throw new Error('Task title is required'); } if (!task.description) { throw new Error('Task description is required'); } if (!task.status) { throw new Error('Task status is required'); } } /** * Utility method to sanitize tag names for file system safety * @param tag - Tag name to sanitize * @returns Sanitized tag name */ protected sanitizeTag(tag: string): string { return tag.replace(/[^a-zA-Z0-9-_]/g, '-').toLowerCase(); } }