OverviewFilesCommitsIssuesDocsReleasesPeers

pear-git-v3 / lib / services / migrate / open-old-sources.js

84 lines · javascript ref:
/**
 * Open the v1 migration SOURCES in-place, on the daemon's own corestore.
 *
 * The v1 layout is reconstructible from corestore namespaces alone (no key
 * conveyance): prod created these autobases LOCALLY, so reopening with the
 * same namespace + bootstrapKey:null re-attaches to the existing local-writer
 * cores and replays their persisted ops.
 *
 * COLLISION-SAFE with the v2 per-org HyperOrgs on the SAME store (verified):
 *   - v1 single-orgs autobase cores live at namespace path ['orgs']
 *     (the Room's Autobase local + 'view' cores).
 *   - v2 HyperOrgs puts its index at ['orgs','orgs-index'] (name 'log') and
 *     each per-org Room at ['orgs','org/<name>']; it creates NOTHING directly
 *     at ['orgs'] root. Disjoint cores → old + new coexist in-place.
 *   - v1 org issue trackers live at namespace ['issues:org:<name>'] — wholly
 *     separate from 'orgs'.
 *
 * Reopen-replay is faithful: the orgsSchema / issuesSchema `create-org` /
 * `create-issue` apply that built the persisted views is byte-identical
 * between v1 (0.2.1) and now — only the autobase TOPOLOGY changed, not the
 * view-building apply. So replaying the v1 log through the current schema
 * yields the identical view.
 *
 * Every opener returns an object with `.view` (Hyperbee) + `.close()` that the
 * caller MUST close. On a fresh store (no v1 data) the opener returns an empty
 * autobase → readOldOrgs/readOldIssues yield nothing → the migration no-ops.
 */

import { Room } from 'hyper-rooms'
import { orgsSchema } from 'hyper-orgs'
import { HyperIssues } from 'hyper-issues'

/**
 * Reopen the v1 single-`orgs` autobase (the migration source for orgs).
 * @param {Corestore} corestore - the daemon's corestore (NOT pre-namespaced)
 * @param {object} [opts]
 * @param {string} [opts.namespace='orgs']
 * @param {Buffer|string|null} [opts.bootstrapKey=null]
 * @returns {Promise<Room>} a Room with `.view` (org:* Hyperbee) + `.close()`
 */
export async function openOldOrgsBase (corestore, opts = {}) {
  const room = new Room(corestore, {
    schema: orgsSchema,
    namespace: opts.namespace || 'orgs',
    bootstrapKey: opts.bootstrapKey || null
  })
  await room.ready()
  return room
}

/**
 * Build the `openOldOrgTracker(name)` closure the migration calls per org.
 * Reopens each org's v1 separate `issues:org:<name>` tracker on the same
 * corestore. Tracks opened trackers so the caller can close them all.
 *
 * @param {Corestore} corestore - the daemon's corestore
 * @param {object} [opts]
 * @param {(name:string)=>(Buffer|string|null)} [opts.bootstrapKeyFor] - look up
 *   a known v1 tracker bootstrapKey for an org (for trackers joined from
 *   elsewhere). Defaults to null (local-writer reopen by namespace) — correct
 *   for the ORIGIN daemon, where every org tracker was created locally.
 * @returns {{ open: (name:string)=>Promise<HyperIssues>, closeAll: ()=>Promise<void> }}
 */
export function makeOpenOldOrgTracker (corestore, opts = {}) {
  const bootstrapKeyFor = typeof opts.bootstrapKeyFor === 'function' ? opts.bootstrapKeyFor : () => null
  const opened = []
  return {
    async open (name) {
      const tracker = new HyperIssues(corestore, {
        namespace: 'issues:org:' + name,
        bootstrapKey: bootstrapKeyFor(name) || null
      })
      await tracker.ready()
      opened.push(tracker)
      return tracker
    },
    async closeAll () {
      for (const t of opened.splice(0)) {
        try { await t.close() } catch {}
      }
    }
  }
}