import { Index } from 'flexsearch' import crypto from 'crypto' export default class NotesAPI { constructor(adapter) { if (!adapter) { throw new Error('NotesAPI requires a storage adapter') } this.adapter = adapter this.notesCache = new Map() this.index = new Index({ tokenize: 'tolerant', resolution: 9, }) } async init() { await this.adapter.init() const notes = await this.adapter.getAll() for (const note of notes) { this.notesCache.set(note.id, note) this.index.add(note.id, note.title + '\n' + note.content) } } /* ----------------------- Public API ------------------------*/ getCategories() { const categories = new Set() for (const note of this.notesCache.values()) { if (note.category) { categories.add(note.category) } } return Array.from(categories).sort() } getCategoryNotes(categoryName) { return Array.from(this.notesCache.values()) .filter((n) => n.category === categoryName) .sort((a, b) => new Date(b.updatedAt) - new Date(a.updatedAt)) } getNote(id) { return this.notesCache.get(id) ?? null } async createNote(metadata = {}, content = '') { const id = crypto.randomUUID() const now = new Date().toISOString() const note = { id, title: metadata.title || 'Untitled', category: metadata.category || null, createdAt: now, updatedAt: now, content, } this.notesCache.set(id, note) this.index.add(id, note.title + '\n' + content) await this.adapter.create(note) return note } async deleteNote(id) { await this.adapter.delete(id) this.notesCache.delete(id) this.index.remove(id) } async updateNote(id, content) { const note = this.notesCache.get(id) if (!note) throw new Error('Note not found') note.content = content note.updatedAt = new Date().toISOString() this.index.update(id, note.title + '\n' + content) await this.adapter.update(note) return note } async updateNoteMetadata(id, updates = {}) { const note = this.notesCache.get(id) if (!note) throw new Error('Note not found') const allowedFields = ['title', 'category'] for (const key of Object.keys(updates)) { if (!allowedFields.includes(key)) { throw new Error(`Invalid metadata field: ${key}`) } } if (updates.title !== undefined) { note.title = updates.title } if (updates.category !== undefined) { note.category = updates.category } note.updatedAt = new Date().toISOString() this.index.update(id, note.title + '\n' + note.content) await this.adapter.update(note) return note } search(query) { const ids = this.index.search(query) return ids.map((id) => this.notesCache.get(id)) } }