OverviewFilesCommitsIssuesDocsReleasesPeers

hyper-note / lib / services / note-service.js

129 lines · javascript ref:
/**
 * 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(() => {})
    }
  }
}