OverviewFilesCommitsIssuesDocsReleasesPeers

hyper-vdrive / lib / serve.cjs

69 lines · javascript ref:
// 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 }