// Normalize + sanitize a validated blocks array before persisting. Runs AFTER // validateBlocks (which guarantees the envelope/prop shape), so this can assume // well-formed input and focus on: applying each block's registry `sanitize` // normalizer (e.g. rich_text runs its html through the allowlist), stamping the // registry `version`, defaulting `visible` to true, and recursing one level into // container slots. Returns a new array; never mutates the input. const { getBlock } = require('./registry') function sanitizeBlocks(blocks) { if (!Array.isArray(blocks)) return [] return blocks.map(sanitizeOne) } function sanitizeOne(block) { const def = getBlock(block.type) if (!def) return block // unreachable after validation, but stay defensive let props = block.props && typeof block.props === 'object' ? { ...block.props } : {} // Recurse into container slots first (leaf sub-blocks get sanitized too). if (def.container) { for (const slot of def.containerSlots) { if (Array.isArray(props[slot])) props[slot] = props[slot].map(sanitizeOne) } } // Apply the block's own normalizer last (operates on its scalar props). if (def.sanitize) { try { props = def.sanitize(props) } catch { // Leave props as-is; validation already passed, a sanitize throw shouldn't // block the save. } } return { id: block.id, type: block.type, version: Number.isInteger(block.version) ? block.version : def.version, visible: block.visible !== false, props, } } module.exports = { sanitizeBlocks }