#!/usr/bin/env bare
/*
* migrate-v1-extract.mjs — Phase 1 of the v1 → v3 migration.
*
* Runs under BARE because v1's MetadataBase imports bare-path / bare-fs
* / bare-os (with native bindings). Reads v1 issues + comments from the
* MetadataBase Autobase view and dumps them to a JSON file for Phase 2
* (migrate-v1-issues.mjs, which runs under Node and writes to v3).
*
* Usage:
* bare scripts/migrate-v1-extract.mjs \
* --v1-storage /path/to/pear-git-storage \
* --out /tmp/v1-migration.json
*
* v1 daemon MUST be stopped (RocksDB exclusive lock).
*/
import fs from 'bare-fs'
import { MetadataBase } from '/storage/projects/code/hypercore-kb/hyper-fleeky/pear-git/lib/metadata-base.js'
function parseArgs (argv) {
const args = { v1Storage: null, out: null }
for (let i = 2; i < argv.length; i++) {
if (argv[i] === '--v1-storage') args.v1Storage = argv[++i]
else if (argv[i] === '--out') args.out = argv[++i]
}
if (!args.v1Storage) throw new Error('--v1-storage required')
if (!args.out) throw new Error('--out required')
return args
}
async function main () {
const args = parseArgs(Bare.argv)
console.log('[extract] v1 storage:', args.v1Storage)
console.log('[extract] output :', args.out)
const mb = new MetadataBase({ storagePath: args.v1Storage, swarmOpts: { bootstrap: [] } })
await mb.ready()
try { await mb.swarm.destroy() } catch {}
await mb._base.update()
console.log('[extract] v1 MetadataBase opened')
const byRepo = new Map()
for await (const { key, value } of mb._base.view.createReadStream({ gt: 'issue:', lt: 'issue:\xff' })) {
const parts = key.split(':')
if (parts.length < 3) continue
const repo = parts[1]
const id = parseInt(parts[2], 10)
if (isNaN(id)) continue
if (!byRepo.has(repo)) byRepo.set(repo, { issues: [], comments: [] })
byRepo.get(repo).issues.push({ id, ...value })
}
for await (const { key, value } of mb._base.view.createReadStream({ gt: 'comment:', lt: 'comment:\xff' })) {
const parts = key.split(':')
if (parts.length < 4) continue
const repo = parts[1]
const issueId = parseInt(parts[2], 10)
const commentId = parts.slice(3).join(':')
if (isNaN(issueId)) continue
if (!byRepo.has(repo)) continue
byRepo.get(repo).comments.push({ issueId, commentId, ...value })
}
let totalIssues = 0, totalComments = 0
const dump = {}
for (const [repo, b] of byRepo) {
totalIssues += b.issues.length
totalComments += b.comments.length
console.log(`[extract] ${repo}: ${b.issues.length} issues, ${b.comments.length} comments`)
dump[repo] = b
}
console.log(`[extract] total: ${totalIssues} issues, ${totalComments} comments across ${byRepo.size} repos`)
try { await mb.close?.() } catch {}
try { await mb.store.close() } catch {}
fs.writeFileSync(args.out, JSON.stringify(dump, null, 2))
console.log('[extract] wrote', args.out)
}
main().catch(err => {
console.error('[extract] ERROR:', err.message)
console.error(err.stack)
Bare.exit(1)
})