OverviewFilesCommitsIssuesDocsReleasesPeers

pear-git-v3 / lib / services / org-event-log-messages.js

75 lines · javascript ref:
/**
 * Wire codec for the org-event-log Hypercore entries.
 *
 * Single entry type, compact-encoded:
 *
 *   { op: uint8,              // 0=created, 1=updated (reserved), 2=deleted (reserved)
 *     name: string,           // org name (short ASCII)
 *     ts: uint,               // unix ms
 *     issueBootstrapKey: buffer(32) | null,   // per-org ISSUE tracker autobase
 *     orgBootstrapKey:   buffer(32) | null }   // per-org RECORD autobase (#37)
 *
 * Mirrors repo-event-log-messages.js (which serves the same purpose
 * for git repos). Difference: no `head` field — orgs don't have refs.
 *
 * Each trailing key uses a 1-byte presence prefix (0|1) followed by the
 * raw 32-byte key when present — same pattern as hyper-overlay/lib/
 * messages.js uses for nullable buffer fields.
 *
 * `orgBootstrapKey` was added in #37 (per-org-autobase scope): the
 * org's own RECORD autobase, so a mirror can mountOrg() it. Decode is
 * backward-compatible — entries written before this field is absent
 * (the buffer ends after issueBootstrapKey) decode it as null.
 */

import c from 'compact-encoding'

// Nullable 32-byte key: 1-byte presence prefix + raw key when present.
function encodeKey (state, key) {
  c.uint8.encode(state, key ? 1 : 0)
  if (!key) return
  const buf = typeof key === 'string' ? Buffer.from(key, 'hex') : key
  buf.copy(state.buffer, state.start, 0, 32)
  state.start += 32
}

// Decode a nullable key, tolerating end-of-buffer (older entries that
// lack a trailing key field) → null.
function decodeKey (state) {
  if (state.start >= state.end) return null
  const has = c.uint8.decode(state)
  if (!has) return null
  const key = Buffer.from(state.buffer.subarray(state.start, state.start + 32))
  state.start += 32
  return key
}

export const entry = {
  preencode (state, m) {
    c.uint8.preencode(state, m.op | 0)
    c.string.preencode(state, m.name || '')
    c.uint.preencode(state, m.ts || 0)
    c.uint8.preencode(state, m.issueBootstrapKey ? 1 : 0)
    if (m.issueBootstrapKey) state.end += 32
    c.uint8.preencode(state, m.orgBootstrapKey ? 1 : 0)
    if (m.orgBootstrapKey) state.end += 32
  },
  encode (state, m) {
    c.uint8.encode(state, m.op | 0)
    c.string.encode(state, m.name || '')
    c.uint.encode(state, m.ts || 0)
    encodeKey(state, m.issueBootstrapKey)
    encodeKey(state, m.orgBootstrapKey)
  },
  decode (state) {
    const op = c.uint8.decode(state)
    const name = c.string.decode(state)
    const ts = c.uint.decode(state)
    const issueBootstrapKey = decodeKey(state)
    // Backward-compat: entries written before #37 end here; orgBootstrapKey
    // is absent → null (decodeKey guards on remaining buffer length).
    const orgBootstrapKey = decodeKey(state)
    return { op, name, ts, issueBootstrapKey, orgBootstrapKey }
  }
}