OverviewFilesCommitsIssuesDocsReleasesPeers

hyper-social / hyper-wiki / index.js

135 lines · javascript ref:
import { Wiki } from './lib/wiki.js'

export { Wiki }

// CLI entry point
const isMain = typeof Pear !== 'undefined' || process.argv[1]?.includes('hyper-wiki')

if (isMain && process.argv.length > 2) {
  const args = parseArgs(typeof Pear !== 'undefined' ? Pear.config.args : process.argv.slice(2))

  if (args.help) {
    console.log(`
hyper-wiki — P2P wiki with Hyperdrive storage

Usage:
  hyper-wiki create <slug> --title "Page Title" --content "Markdown content"
  hyper-wiki read <slug>
  hyper-wiki update <slug> --content "Updated content"
  hyper-wiki delete <slug>
  hyper-wiki list
  hyper-wiki search <query>

Options:
  --storage <path>       Storage directory (default: ./wiki-data)
  --title <title>        Page title
  --content <content>    Page content (markdown)
  --author <name>        Author name
  --replicate            Enable P2P replication via Hyperswarm
  --help                 Show this help
`)
    process.exit(0)
  }

  run(args).catch((err) => {
    console.error('Error:', err.message)
    process.exit(1)
  })
}

async function run (args) {
  const storage = args.storage || './wiki-data'
  const wiki = new Wiki(storage, { replicate: args.replicate })
  await wiki.ready()

  const cmd = args._[0]
  const slug = args._[1]

  switch (cmd) {
    case 'create':
    case 'update': {
      if (!slug) throw new Error('Usage: hyper-wiki create <slug>')
      const content = args.content || ''
      const meta = await wiki.create(slug, content, {
        title: args.title || slug,
        author: args.author || ''
      })
      console.log(`${cmd === 'create' ? 'Created' : 'Updated'}: ${slug} (v${meta.version})`)
      break
    }
    case 'read': {
      if (!slug) throw new Error('Usage: hyper-wiki read <slug>')
      const page = await wiki.read(slug)
      if (!page) {
        console.log(`Page not found: ${slug}`)
      } else {
        console.log(`# ${page.title} (v${page.version})`)
        console.log(`Author: ${page.author || 'unknown'} | Updated: ${new Date(page.updated).toISOString()}`)
        console.log('---')
        console.log(page.content)
      }
      break
    }
    case 'delete': {
      if (!slug) throw new Error('Usage: hyper-wiki delete <slug>')
      await wiki.remove(slug)
      console.log(`Deleted: ${slug}`)
      break
    }
    case 'list': {
      const pages = await wiki.list()
      if (pages.length === 0) {
        console.log('No pages yet.')
      } else {
        for (const p of pages) {
          console.log(`  ${p.slug.padEnd(30)} v${p.version} ${new Date(p.updated).toISOString().slice(0, 10)}  ${p.title}`)
        }
      }
      break
    }
    case 'search': {
      const query = args._.slice(1).join(' ')
      if (!query) throw new Error('Usage: hyper-wiki search <query>')
      const results = await wiki.search(query)
      if (results.length === 0) {
        console.log(`No results for: ${query}`)
      } else {
        for (const r of results) {
          console.log(`  ${r.slug.padEnd(30)} score:${r.score}  ${r.title}`)
        }
      }
      break
    }
    default:
      console.error(`Unknown command: ${cmd}`)
      console.error('Run with --help for usage')
      process.exit(1)
  }

  await wiki.close()
}

function parseArgs (argv) {
  const result = { _: [] }
  for (let i = 0; i < argv.length; i++) {
    const arg = argv[i]
    if (arg === '--help') {
      result.help = true
    } else if (arg === '--replicate') {
      result.replicate = true
    } else if (arg.startsWith('--')) {
      const key = arg.slice(2)
      const next = argv[i + 1]
      if (next && !next.startsWith('--')) {
        result[key] = next
        i++
      } else {
        result[key] = true
      }
    } else {
      result._.push(arg)
    }
  }
  return result
}