// The deterministic PROJECTION — the heart of atproto-base.
//
// An atproto commit is a PURE function of (record set, did, rev, prev). Signing is RFC6979-
// deterministic (ECDSA secp256k1 low-S), so signing the same unsignedCommit with the shared
// #atproto key yields byte-identical sig + CID on EVERY co-owner. Therefore the atproto chain is a
// deterministic projection of the autobase INDEXED view, and WHO signs is irrelevant — no signer
// election, no fork, indexer-change-agnostic. See DESIGN.md.
//
// `records` take atproto-repo's shape: { key, value } or { collection, rkey, record }. The MST build
// is order-independent (proven in atproto-repo), so a shuffled record set yields the identical root.
// We reuse atproto-repo's byte-exact primitives (verified against real relay-accepted repos) so our
// commits stay wire-identical to what a real relay validates.
import { cidForValue } from 'atproto-repo/lib/cid.js'
import { buildMst, reachableBlocks } from 'atproto-repo/lib/mst.js'
import { unsignedCommit, signCommit, commitCid } from 'atproto-repo/lib/commit.js'
import { writeCar } from 'atproto-repo/lib/car.js'
import { nextRev } from './rev.js'
export { nextRev }
// Project + sign one atproto commit deterministically. `prev` = CID of the previous commit (null for
// genesis). Returns { carBytes, commitCid, dataCid, signedCommit } — byte-identical across co-owners
// for identical inputs. Mirrors atproto-repo's createRepo assembly but adds prev-chaining, so a whole
// append-only chain is a deterministic projection.
export async function projectCommit ({ records, did, rev, prev = null, privateKey }) {
const recordBlocks = new Map()
const mstRecords = []
for (const r of records) {
const key = r.key !== undefined ? r.key : `${r.collection}/${r.rkey}`
const value = r.record !== undefined ? r.record : r.value
const { bytes, cid } = await cidForValue(value)
recordBlocks.set(cid.toString(), { cid, bytes })
mstRecords.push({ key, value: cid })
}
const { root: dataCid, ns } = buildMst(mstRecords)
const mstBlocks = reachableBlocks(ns, dataCid)
const signed = signCommit(unsignedCommit({ did, rev, data: dataCid, prev }), privateKey)
const { bytes: commitBytes, cid: cCid } = await cidForValue(signed)
const blocks = [{ cid: cCid, bytes: commitBytes }, ...mstBlocks.values(), ...recordBlocks.values()]
const carBytes = await writeCar(cCid, blocks)
return { carBytes, commitCid: commitCid(signed), dataCid, signedCommit: signed }
}