/**
* NoteService — Business logic for note operations.
*
* Wraps NoteManager with side effects (site publishing)
* and owner resolution for feeds.
*/
export class NoteService {
constructor ({ noteManager, authManager, sitePublisher }) {
this.noteManager = noteManager
this.authManager = authManager
this.sitePublisher = sitePublisher
}
/**
* List notes in a notebook with pagination.
*/
async list (notebook, options = {}) {
const limit = Math.min(Math.max(1, parseInt(options.limit) || 50), 500)
const offset = Math.max(0, parseInt(options.offset) || 0)
return this.noteManager.listNotes(notebook, { limit, offset })
}
/**
* Get a single note, resolving by slug or hex ID.
*/
async get (notebook, identifier) {
return this.noteManager.resolveNote(notebook, identifier)
}
/**
* Create a note and trigger site publish.
*/
async create (notebook, options) {
const note = await this.noteManager.createNote(notebook, {
title: options.title,
content: options.content,
tags: options.tags,
type: options.type
})
this._publishInBackground(notebook)
return note
}
/**
* Update a note and trigger site publish.
*/
async update (notebook, noteId, updates) {
const updated = await this.noteManager.updateNote(notebook, noteId, {
title: updates.title,
content: updates.content,
tags: updates.tags
})
this._publishInBackground(notebook)
return updated
}
/**
* Delete a note and trigger site publish.
*/
async delete (notebook, noteId) {
const result = await this.noteManager.deleteNote(notebook, noteId)
this._publishInBackground(notebook)
return result
}
/**
* Move a note between notebooks.
*/
async move (fromNotebook, noteId, toNotebook) {
return this.noteManager.moveNote(fromNotebook, noteId, toNotebook)
}
/**
* Merge two notes.
*/
async merge (notebook, noteId1, noteId2, options) {
return this.noteManager.mergeNotes(notebook, noteId1, noteId2, options)
}
/**
* Split a note at headings.
*/
async split (notebook, noteId, options) {
return this.noteManager.splitNote(notebook, noteId, options)
}
/**
* Recent notes feed with owner resolution.
*
* @param {Object} options - { limit }
* @returns {Promise<Array>} Notes with ownerName field
*/
async recentFeed (options = {}) {
const limit = Math.min(parseInt(options.limit) || 20, 50)
const notes = await this.noteManager.recentNotes({ limit })
const registry = this.authManager.identityRegistry
for (const note of notes) {
if (note.owner) {
try {
const user = await registry.getUser(note.owner)
note.ownerName = user ? (user.username || user.name || 'Anonymous') : 'Anonymous'
} catch { note.ownerName = 'Anonymous' }
} else {
note.ownerName = 'Anonymous'
}
}
return notes
}
/**
* Search notes.
*/
async search (query, options = {}) {
return this.noteManager.searchNotes(query, { notebook: options.notebook })
}
/**
* Trigger site publish in background (fire-and-forget).
*/
_publishInBackground (notebook) {
if (this.sitePublisher) {
this.sitePublisher.publishNotebook(notebook).catch(() => {})
}
}
}