/** * Opaque _rev token generation and validation. * * Format: base64("version:updated_at") * Stateless — server decodes and checks both components. * * Rules: * - No _rev sent → blind write (backwards-compatible) * - _rev matches → write proceeds, new _rev returned * - _rev mismatch → 409 Conflict */ import type { ContentItem } from "../database/repositories/types.js"; import { encodeBase64, decodeBase64 } from "../utils/base64.js"; /** * Generate a _rev token from a content item's version and updatedAt. */ export function encodeRev(item: ContentItem): string { return encodeBase64(`${item.version}:${item.updatedAt}`); } /** * Decode a _rev token into its components. * Returns null if the token is malformed. */ export function decodeRev(rev: string): { version: number; updatedAt: string } | null { try { const decoded = decodeBase64(rev); const colonIdx = decoded.indexOf(":"); if (colonIdx === -1) return null; const version = parseInt(decoded.slice(0, colonIdx), 10); const updatedAt = decoded.slice(colonIdx + 1); if (isNaN(version) || !updatedAt) return null; return { version, updatedAt }; } catch { return null; } } /** * Validate a _rev token against a content item. * Returns null if valid (or if no _rev provided), or an error message if invalid. */ export function validateRev( rev: string | undefined, item: ContentItem, ): { valid: true } | { valid: false; message: string } { // No _rev = blind write (backwards-compatible) if (!rev) return { valid: true }; const decoded = decodeRev(rev); if (!decoded) { return { valid: false, message: "Malformed _rev token" }; } if (decoded.version !== item.version || decoded.updatedAt !== item.updatedAt) { return { valid: false, message: "Content has been modified since last read (version conflict)", }; } return { valid: true }; }