OverviewFilesCommitsIssuesDocsReleasesPeers

favela-web / lib / strongref.js

44 lines · javascript ref:
// strongref.js — build a com.atproto.repo.strongRef { uri, cid } for a record we are referencing.
//
// WHY THIS EXISTS. A like written as `subject: { uri }` indexes perfectly in favela and is INVALID to
// bsky. app.bsky.feed.like.subject is a strongRef, where uri AND cid are both required, so a like missing
// the cid fails lexicon validation at the relay: it federates as nothing while the local UI shows it
// working. Shipped exactly that in 0.2.41 and only found it because the likes never appeared on bsky.
//
// The cid must be of the EXACT stored bytes, never a re-encode. OverlayRepoStore.getRaw's own comment
// says so ("The bridge CIDs the EXACT bytes (never a re-encode) — so these must be the same bytes `put`
// wrote"), and the store's determinism contract is that it writes dagCbor.encode(record) verbatim. A
// re-encode would usually match and would be a silent, occasional mismatch when it did not — the worst
// possible failure shape for a content address.
import { cidForBytesSync } from 'atproto-repo/lib/cid.js'

// at://<did>/<collection>/<rkey> -> { did, collection, rkey }, or null if it is not that shape.
// Deliberately strict: a malformed uri must not half-parse into a lookup against the wrong record.
export function parseAtUri (uri) {
  if (typeof uri !== 'string' || !uri.startsWith('at://')) return null
  const parts = uri.slice('at://'.length).split('/')
  if (parts.length !== 3) return null
  const [did, collection, rkey] = parts
  if (!did.startsWith('did:') || !collection.includes('.') || !rkey) return null
  return { did, collection, rkey }
}

// Resolve a strongRef for an at:// uri, or null when the record's bytes are not available locally.
//
// Returning null rather than a uri-only ref is the whole point: the caller must REFUSE to write the
// referencing record instead of writing an invalid one. A like that cannot be made valid should fail
// visibly, not federate as nothing.
export async function strongRef (uri, getRecordStore) {
  const ref = parseAtUri(uri)
  if (!ref || typeof getRecordStore !== 'function') return null
  try {
    const store = await getRecordStore(ref.did)
    if (!store || typeof store.getRaw !== 'function') return null
    const raw = await store.getRaw(ref.collection, ref.rkey)
    if (!raw || !raw.bytes) return null
    return { uri, cid: String(cidForBytesSync(raw.bytes)) }
  } catch {
    return null
  }
}