/**
* Data Export Module
*
* Exports notebooks, notes, and media metadata as portable JSON structures.
* Used by the /api/export endpoints for backup and migration.
*/
import fs from 'bare-fs'
import path from 'bare-path'
import { extractMediaHashes } from './multiwriter/notebook-apply.js'
// Read app version from package.json at module load time
let _version = '0.0.0'
try {
const _dir = path.dirname(new URL(import.meta.url).pathname)
const _pkg = JSON.parse(fs.readFileSync(path.join(_dir, '..', 'package.json'), 'utf-8'))
_version = _pkg.version || _version
} catch {}
export const APP_VERSION = _version
/**
* Build export manifest describing the export contents.
*/
export function buildExportManifest (notebooks, version) {
return {
version: version || '0.0.0',
format: 'hypernote-export-v1',
exportedAt: new Date().toISOString(),
notebooks: notebooks.map(nb => ({
slug: nb.slug,
name: nb.name,
noteCount: nb.noteCount || 0
}))
}
}
/**
* Export a single notebook as a portable data structure.
* Returns { metadata, notes: [{ id, slug, title, content, tags, ... }], mediaMap: { hash: { size, mimeType } } }
*/
export async function exportNotebook (noteManager, mediaStore, slug) {
const metadata = await noteManager.getNotebook(slug)
if (!metadata) return null
const notes = await noteManager.listNotes(slug)
const mediaMap = {}
// Collect all media hashes referenced in note content
for (const note of notes) {
const hashes = extractMediaHashes(note.content || '')
for (const hash of hashes) {
if (mediaMap[hash]) continue
const info = await mediaStore.getInfo(hash)
if (info) {
mediaMap[hash] = { size: info.size, mimeType: info.mimeType }
}
}
}
return {
metadata: {
slug: metadata.slug,
name: metadata.name,
description: metadata.description || '',
visibility: metadata.visibility || 'public',
createdAt: metadata.createdAt,
updatedAt: metadata.updatedAt
},
notes: notes.map(n => ({
id: n.id,
slug: n.slug,
title: n.title,
content: n.content || '',
tags: n.tags || [],
type: n.type || 'markdown',
createdAt: n.createdAt,
updatedAt: n.updatedAt
})),
mediaMap
}
}
/**
* Export all notebooks.
*/
export async function exportAll (noteManager, mediaStore, version) {
const notebooks = await noteManager.listNotebooks()
const manifest = buildExportManifest(notebooks, version)
const data = {}
for (const nb of notebooks) {
data[nb.slug] = await exportNotebook(noteManager, mediaStore, nb.slug)
}
return { manifest, notebooks: data }
}