// Reopen a published drive for SERVING, without re-chunking anything.
//
// `pear seed` cannot do this job: it opens the drive by key in PEAR's own
// corestore and downloads it, which would materialise a full second copy of
// every byte — the copy this package exists to avoid. It is a re-seeder, not
// a source. So the origin serves its own drives, out of its own store.
//
// The bytes are resolved through the locations index written at publish time,
// so opening a drive is O(index) rather than O(all published bytes).
const idEnc = require('hypercore-id-encoding')
const Corestore = require('corestore')
const Hyperdrive = require('hyperdrive')
const { ContentStore, build } = require('./storage.cjs')
const { loadLocations } = require('./locations.cjs')
/**
* @param {object} opts
* @param {string} opts.storeDir the drive's store (pointers, tree, metadata)
* @param {string} opts.root resolves the locations index's relative paths
* @param {object} [opts.corestoreOpts]
* @returns {Promise<{drive, store, content, link, key, discoveryKey, blocks, locations}>}
*/
async function openDrive ({ storeDir, root, corestoreOpts } = {}) {
if (!storeDir || !root) throw new Error('openDrive requires { storeDir, root }')
const content = new ContentStore()
const locations = loadLocations(storeDir, content, root)
const Storage = build(content)
const store = new Corestore(new Storage(storeDir), corestoreOpts)
const drive = new Hyperdrive(store)
await drive.ready()
await drive.getBlobs()
return {
drive,
store,
content,
locations,
link: 'pear://' + idEnc.normalize(drive.key),
key: drive.key.toString('hex'),
discoveryKey: drive.discoveryKey,
blocks: drive.blobs.core.length
}
}
/**
* Announce an already-open drive on a swarm the caller owns.
*
* `server: true, client: false` — an origin has everything and needs nothing.
* A second node holding the same pointers can announce with `client: true` to
* become an additional source, which is how a pear:// link ends up served by
* more than one backend.
*/
function announce (swarm, opened, { client = false } = {}) {
const discovery = swarm.join(opened.discoveryKey, { server: true, client })
return discovery
}
async function closeDrive (opened) {
if (!opened) return
await opened.drive.close().catch(() => {})
await opened.store.close().catch(() => {})
if (opened.content && typeof opened.content.close === 'function') opened.content.close()
}
module.exports = { openDrive, announce, closeDrive }