@bettercms-ai/ui 0.6.0 → 0.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/resolve.ts"],"sourcesContent":["/**\n * Pure component-instance resolver — clone a component's blockJson and write each\n * declared override at its target (blockId + dotted path), honoring the props[]\n * allowlist. The single source of truth shared by the dashboard canvas preview, the\n * backend live HTML renderer, and @bettercms-ai/next's SSG output, so all three resolve\n * instances identically. React-free and exported on its own subpath (@bettercms-ai/ui/\n * resolve) so the Hono server can import it without pulling in the block components.\n */\n\n/** Minimal block shape the resolver needs — every real block type is structurally wider.\n * `props` is `unknown` (not `Record<string, unknown>`) so typed block unions like\n * @bettercms-ai/types' ContentBlock — whose `props` are specific interfaces without an\n * index signature — still satisfy the constraint. */\nexport type ResolverBlock = { type: string; id?: string; props?: unknown };\n\n/** Minimal prop-def shape — the real ComponentPropDef is structurally wider. */\nexport type ResolverPropDef = { key: string; target: { blockId: string; path: string } };\n\n/** The child block arrays a container holds, one entry per nested slot (all container types). */\nexport function childArrays<B extends ResolverBlock>(block: B): B[][] {\n const p = (block.props ?? {}) as Record<string, unknown>;\n switch (block.type) {\n case \"columns\":\n return Array.isArray(p.columns) ? (p.columns as B[][]) : [];\n case \"section\":\n return Array.isArray(p.children) ? [p.children as B[]] : [];\n case \"slider\":\n return Array.isArray(p.slides)\n ? (p.slides as { children?: B[] }[]).map((s) => (Array.isArray(s.children) ? s.children : []))\n : [];\n case \"tabs\":\n return Array.isArray(p.tabs)\n ? (p.tabs as { children?: B[] }[]).map((t) => (Array.isArray(t.children) ? t.children : []))\n : [];\n default:\n return [];\n }\n}\n\n/** Find a block by id anywhere in a tree (descends every container). */\nexport function findBlockById<B extends ResolverBlock>(blocks: B[], id: string): B | null {\n for (const b of blocks) {\n if (b?.id === id) return b;\n for (const child of childArrays(b)) {\n const found = findBlockById(child, id);\n if (found) return found;\n }\n }\n return null;\n}\n\n/** Write `value` at a dotted path (e.g. \"props.text\"), creating objects as needed. */\nexport function setPath(obj: Record<string, unknown>, path: string, value: unknown): void {\n const keys = path.split(\".\");\n let cur: Record<string, unknown> = obj;\n for (let i = 0; i < keys.length - 1; i++) {\n const k = keys[i]!;\n if (typeof cur[k] !== \"object\" || cur[k] === null) cur[k] = {};\n cur = cur[k] as Record<string, unknown>;\n }\n cur[keys[keys.length - 1]!] = value;\n}\n\n/**\n * Apply an instance's `overrides` onto a clone of a component's blockJson, honoring the\n * declared `props[]` allowlist (key → target block + path). Returns the original array\n * untouched (referentially stable) when there's nothing to override; returns [] for a\n * non-array input.\n */\nexport function applyOverrides<B extends ResolverBlock>(\n blockJson: unknown,\n propDefs: ResolverPropDef[],\n overrides: Record<string, unknown> | undefined,\n): B[] {\n if (!Array.isArray(blockJson)) return [];\n if (!propDefs.length || !overrides || Object.keys(overrides).length === 0) {\n return blockJson as B[];\n }\n const clone = structuredClone(blockJson) as B[];\n for (const def of propDefs) {\n if (!(def.key in overrides)) continue;\n const target = findBlockById(clone, def.target.blockId);\n if (target) setPath(target as unknown as Record<string, unknown>, def.target.path, overrides[def.key]);\n }\n return clone;\n}\n\n/**\n * How deep a component may nest another component before we stop. Matches the guard the\n * three live renderers already apply, so a tree that renders is a tree that snapshots.\n */\nexport const MAX_COMPONENT_DEPTH = 10;\n\n/** What `resolveDeep` needs to look a nested component up by id. */\nexport type ResolverComponentDef = { blockJson: unknown; props: ResolverPropDef[] };\n\n/** Rebuild a container block with each of its child arrays mapped. The write-side twin of\n * `childArrays` — same four container types, same order. Non-containers pass through. */\nfunction mapChildArrays<B extends ResolverBlock>(block: B, map: (children: B[]) => B[]): B {\n const p = (block.props ?? {}) as Record<string, unknown>;\n switch (block.type) {\n case \"columns\":\n if (!Array.isArray(p.columns)) return block;\n return { ...block, props: { ...p, columns: (p.columns as B[][]).map(map) } };\n case \"section\":\n if (!Array.isArray(p.children)) return block;\n return { ...block, props: { ...p, children: map(p.children as B[]) } };\n case \"slider\":\n if (!Array.isArray(p.slides)) return block;\n return {\n ...block,\n props: {\n ...p,\n slides: (p.slides as { children?: B[] }[]).map((s) =>\n Array.isArray(s.children) ? { ...s, children: map(s.children) } : s,\n ),\n },\n };\n case \"tabs\":\n if (!Array.isArray(p.tabs)) return block;\n return {\n ...block,\n props: {\n ...p,\n tabs: (p.tabs as { children?: B[] }[]).map((t) =>\n Array.isArray(t.children) ? { ...t, children: map(t.children) } : t,\n ),\n },\n };\n default:\n return block;\n }\n}\n\n/**\n * `applyOverrides`, then INLINE every nested `component` block into its resolved blocks.\n *\n * This is the deep/frozen counterpart to `applyOverrides`, and it exists for exactly one\n * caller family: publishing. A published entry's `component-ref` freezes the resolved tree\n * into `published_data` so that later edits to the component cannot silently rewrite content\n * that was already published. `applyOverrides` alone does not deliver that promise once a\n * component nests another (which a `slot` prop makes routine): the outer tree is frozen, but\n * the nested `component` block is still a live REFERENCE that re-resolves at bake time.\n *\n * The three LIVE renderers deliberately keep using `applyOverrides` plus their own\n * per-render recursion — they want the live reference. So there is still exactly one\n * implementation of \"apply overrides to a component\"; this only adds the inlining.\n *\n * Fail-soft, matching the renderers: an empty id, an unknown component, a cycle, or a tree\n * deeper than MAX_COMPONENT_DEPTH resolves to NOTHING rather than throwing — the same thing\n * a visitor already sees for a deleted component.\n *\n * `seen` is the ancestry of the current branch and the caller MUST seed it with the id of\n * the component being resolved, or a component that slots itself will not be caught. It is\n * ancestry-scoped, not global: the same component placed twice as siblings renders twice.\n */\nexport function resolveDeep<B extends ResolverBlock>(\n blockJson: unknown,\n propDefs: ResolverPropDef[],\n overrides: Record<string, unknown> | undefined,\n lookup: (componentId: string) => ResolverComponentDef | undefined,\n seen: readonly string[] = [],\n depth = 0,\n): B[] {\n const applied = applyOverrides<B>(blockJson, propDefs, overrides);\n\n const out: B[] = [];\n for (const block of applied) {\n if (block?.type !== \"component\") {\n out.push(mapChildArrays(block, (children) => resolveDeep<B>(children, [], undefined, lookup, seen, depth)));\n continue;\n }\n // THE INVARIANT: the returned tree never contains a `component` block. Every branch below\n // either inlines it or drops it — including the depth cap, which must NOT bail out by\n // returning the tree as-is. Doing that would leave a live reference sitting inside a\n // \"frozen\" snapshot, which is the exact half-frozen state this function exists to prevent\n // and is harder to notice than rendering nothing.\n if (depth >= MAX_COMPONENT_DEPTH) continue; // too deep — drop, never leave a live ref\n const p = (block.props ?? {}) as { componentId?: unknown; overrides?: unknown };\n const cid = typeof p.componentId === \"string\" ? p.componentId : \"\";\n if (!cid || seen.includes(cid)) continue; // empty slot, or a cycle — render nothing\n const def = lookup(cid);\n if (!def) continue; // unknown/unpublished/deleted — render nothing\n out.push(\n ...resolveDeep<B>(\n def.blockJson,\n def.props,\n p.overrides as Record<string, unknown> | undefined,\n lookup,\n [...seen, cid],\n depth + 1,\n ),\n );\n }\n return out;\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/resolve.ts"],"sourcesContent":["/**\n * Pure component-instance resolver — clone a component's blockJson and write each\n * declared override at its target (blockId + dotted path), honoring the props[]\n * allowlist. The single source of truth shared by the dashboard canvas preview, the\n * backend live HTML renderer, and @bettercms-ai/next's SSG output, so all three resolve\n * instances identically. React-free and exported on its own subpath (@bettercms-ai/ui/\n * resolve) so the Hono server can import it without pulling in the block components.\n */\n\n/** Minimal block shape the resolver needs — every real block type is structurally wider.\n * `props` is `unknown` (not `Record<string, unknown>`) so typed block unions like\n * @bettercms-ai/types' ContentBlock — whose `props` are specific interfaces without an\n * index signature — still satisfy the constraint. */\nexport type ResolverBlock = { type: string; id?: string; props?: unknown };\n\n/** Minimal prop-def shape — the real ComponentPropDef is structurally wider.\n *\n * `type` is carried for callers that need it (the backfill reads it to decide which values are\n * rich text). The resolver itself branches on the TARGET alone. @see writeRichTextOverride */\nexport type ResolverPropDef = {\n key: string;\n type?: string;\n target: { blockId: string; path: string };\n /** A `table`/`group` prop's row shape — the ORDERED columns a row holds. @see expandRows */\n config?: { fields?: ResolverSubField[] } & Record<string, unknown>;\n};\n\n/** One column of a `table` row. `fields` (or its `config.fields` mirror) makes it a nested table. */\nexport type ResolverSubField = {\n key: string;\n type?: string;\n fields?: ResolverSubField[];\n config?: { fields?: ResolverSubField[] } & Record<string, unknown>;\n};\n\n/** The child block arrays a container holds, one entry per nested slot (all container types). */\nexport function childArrays<B extends ResolverBlock>(block: B): B[][] {\n const p = (block.props ?? {}) as Record<string, unknown>;\n switch (block.type) {\n case \"columns\":\n return Array.isArray(p.columns) ? (p.columns as B[][]) : [];\n case \"section\":\n return Array.isArray(p.children) ? [p.children as B[]] : [];\n case \"slider\":\n return Array.isArray(p.slides)\n ? (p.slides as { children?: B[] }[]).map((s) => (Array.isArray(s.children) ? s.children : []))\n : [];\n case \"tabs\":\n return Array.isArray(p.tabs)\n ? (p.tabs as { children?: B[] }[]).map((t) => (Array.isArray(t.children) ? t.children : []))\n : [];\n default:\n return [];\n }\n}\n\n/** Find a block by id anywhere in a tree (descends every container). */\nexport function findBlockById<B extends ResolverBlock>(blocks: B[], id: string): B | null {\n for (const b of blocks) {\n if (b?.id === id) return b;\n for (const child of childArrays(b)) {\n const found = findBlockById(child, id);\n if (found) return found;\n }\n }\n return null;\n}\n\n/** Write `value` at a dotted path (e.g. \"props.text\"), creating objects as needed. */\nexport function setPath(obj: Record<string, unknown>, path: string, value: unknown): void {\n const keys = path.split(\".\");\n let cur: Record<string, unknown> = obj;\n for (let i = 0; i < keys.length - 1; i++) {\n const k = keys[i]!;\n if (typeof cur[k] !== \"object\" || cur[k] === null) cur[k] = {};\n cur = cur[k] as Record<string, unknown>;\n }\n cur[keys[keys.length - 1]!] = value;\n}\n\n/**\n * `props.html` — the one target path that holds MARKUP.\n *\n * Named the same thing, for the same reason, as `MARKUP_PATH` in the backend's\n * `lib/components/prop-value.ts`: the authority is the TARGET, never the declared type. A prop\n * can be declared `richtext` and still land on a plain-text slot like `heading.props.text`, and\n * treating that one as an envelope would write an object where the renderer escapes a string.\n */\nconst MARKUP_PATH = \"props.html\";\n\n/**\n * An override aimed at `props.html`, written as the block's own `{ html, content }` PAIR.\n *\n * 🔴 The two keys are siblings on `props`, not one value at `props.html`. A `text`/`richtext`\n * block stores `props.html` (the rendered cache) beside `props.content` (Portable Text, the\n * source of truth), and every reader — `packages/ui/src/blocks/Text.tsx`,\n * `packages/next/src/blocks.tsx`, the backend's `render-page.ts` via `readStructuredContent` —\n * PREFERS `content`. A single `setPath(…, \"props.html\", value)` therefore has two failure modes\n * and this function exists to close both:\n *\n * an ENVELOPE written whole ⇒ `props.html` is an object, the renderer does `String(p.html)`,\n * and the page shows the words \"[object Object]\".\n * a STRING written alone ⇒ the DEFINITION's `props.content` survives underneath it, and\n * since readers prefer `content`, the instance's new html is\n * invisible — the override silently does nothing.\n *\n * So an envelope splits into both keys, and anything else writes `html` and DELETES `content`.\n * Deleting is the whole point of the second branch: the stale structure must not outlive the\n * html it no longer describes.\n *\n * 🔴 THE TARGET DECIDES, NOT THE DECLARED TYPE. An earlier version gated this on\n * `type === \"richtext\"`, which put `type: \"text\"` overrides — a perfectly ordinary legacy\n * declaration — back on the plain `setPath`, writing `html` while the definition's `content`\n * stayed put. The instant the backfill gives that definition block a `content`, every such\n * instance renders the DEFINITION's words and the override becomes invisible. Invalidation is a\n * property of the SLOT (`props.html` has a `props.content` sibling that outranks it), so it\n * cannot be conditional on what a prop calls itself — the same argument `prop-value.ts` makes\n * about a `richtext` prop landing on a plain-text target, pointed the other way.\n */\nfunction writeRichTextOverride(target: ResolverBlock, value: unknown): void {\n const block = target as unknown as Record<string, unknown>;\n if (typeof block.props !== \"object\" || block.props === null) block.props = {};\n const props = block.props as Record<string, unknown>;\n\n const envelope = value && typeof value === \"object\" && !Array.isArray(value)\n ? (value as { html?: unknown; content?: unknown })\n : null;\n\n if (envelope && typeof envelope.html === \"string\") {\n props.html = envelope.html;\n // An envelope carrying no `content` is an html-only value, and it clears the old structure\n // for the same reason a bare string does.\n if (envelope.content === undefined) delete props.content;\n else props.content = envelope.content;\n return;\n }\n props.html = value;\n delete props.content;\n}\n\n/** True for the one TARGET whose value is a `{ html, content }` pair. @see writeRichTextOverride */\nconst writesMarkupSlot = (def: ResolverPropDef): boolean => def.target.path === MARKUP_PATH;\n\n/** `props.src` — the target that holds an IMAGE, whose canonical value carries its own alt. */\nexport const IMAGE_PATH = \"props.src\";\n\n/**\n * An override aimed at `props.src`, written as the block's own `src` + `alt` PAIR.\n *\n * 🔴 ALT IS PART OF THE VALUE, and it belongs to the INSTANCE. The canonical image a field\n * stores is `{ id?, url, name?, altText? }` (field-value-normalize.ts), and an image prop\n * targets one path — so writing the value whole put an OBJECT in `src` (`src=\"[object Object]\"`)\n * while writing only the url left every placement inheriting the DEFINITION's alt: two instances\n * of one hero, two different photographs, one screen-reader description, and it belonged to\n * whichever page was componentized first.\n *\n * So an envelope splits into both keys, and a bare string writes `src` alone — a caller who sent\n * only a URL has said nothing about the alt, and silently blanking it would be a regression for\n * every component that has one today.\n */\nfunction writeImageOverride(target: ResolverBlock, value: unknown): void {\n const block = target as unknown as Record<string, unknown>;\n if (typeof block.props !== \"object\" || block.props === null) block.props = {};\n const props = block.props as Record<string, unknown>;\n\n if (value && typeof value === \"object\" && !Array.isArray(value)) {\n const envelope = value as { url?: unknown; src?: unknown; altText?: unknown; alt?: unknown };\n const url = typeof envelope.url === \"string\" ? envelope.url : envelope.src;\n if (typeof url === \"string\") props.src = url;\n // `altText` is the canonical key; `alt` is what a hand-written value uses. An envelope that\n // names neither leaves the block's own alt alone rather than blanking it.\n const alt = typeof envelope.altText === \"string\" ? envelope.altText : envelope.alt;\n if (typeof alt === \"string\") props.alt = alt;\n return;\n }\n props.src = value;\n}\n\n/** True for the target whose value is an image envelope. @see writeImageOverride */\nconst writesImageSlot = (def: ResolverPropDef): boolean => def.target.path === IMAGE_PATH;\n\n/**\n * `props.rows` — the one target path whose value is a LIST, and therefore REPEATS.\n *\n * 🔴 WHY THIS EXISTS. A repeater becomes one `table` prop pointing at a container block whose\n * children are ONE ROW's blocks — the row TEMPLATE. `setPath` assigned that array to\n * `props.rows`, and no renderer reads `props.rows`: `Section` renders `props.children`. So an\n * FAQ with nine questions rendered the single seeded row, on the canvas, in delivery and in the\n * SSG output alike, and the other eight were invisible everywhere except the dock that edited\n * them.\n */\nexport const ROWS_PATH = \"props.rows\";\n\n/**\n * Where one row COLUMN's value lands inside the block that renders it, by block type.\n *\n * The template's blocks mirror the row's declared sub-fields one for one and in order (that is\n * how the componentize lane builds them), so a column is written at the slot ITS OWN BLOCK TYPE\n * reads — the same pairing `blockFor`/`PROP_OF_KIND` make on the way in. A block type not listed\n * here is left exactly as the template had it rather than guessed at: writing a headline into an\n * unknown slot is worse than a row that renders its template value.\n */\n/**\n * Which BLOCK TYPES can hold a column of each declared type.\n *\n * 🔴 A SLOT IS NOT A TYPE. `ROW_SLOTS` says WHERE a cell is written on a given block; it says\n * nothing about whether the value belongs there. A component declaring a `richtext` column whose\n * template child is a `button` passed a check that only asked \"does this block have a slot?\" —\n * and then the rich-text envelope was written into `props.href` while the page field holding that\n * copy was marked hidden. So the column's own type decides which children can carry it, and the\n * lists are exactly the slots below read the right way round: text goes where text renders\n * (never into an href), a url goes to the one block whose slot IS an href.\n *\n * A column type this map does not know is not this rule's to judge — the child having a slot at\n * all is the whole check for it.\n */\nconst CELL_TYPES: Record<string, readonly string[]> = {\n text: [\"heading\", \"text\", \"richtext\"],\n richtext: [\"text\", \"richtext\"],\n url: [\"button\"],\n image: [\"image\", \"video\"],\n};\n\nconst ROW_SLOTS: Record<string, string> = {\n heading: \"props.text\",\n text: \"props.html\",\n richtext: \"props.html\",\n image: \"props.src\",\n button: \"props.href\",\n video: \"props.src\",\n};\n\n/** The columns a row declares — `fields` is the authority, `config.fields` its legacy mirror. */\nconst subFieldsOf = (node: { fields?: ResolverSubField[]; config?: { fields?: ResolverSubField[] } }):\n | ResolverSubField[]\n | null => node.fields ?? node.config?.fields ?? null;\n\n/**\n * Can this container's ROW TEMPLATE be repeated for these columns?\n *\n * 🔴 THE ONE STATEMENT OF WHAT `expandRows` NEEDS, exported because the componentize lane has to\n * ask the SAME question before it points a placement at an existing component. Keys and types\n * matching is not enough: a component whose FAQ template holds two children for a one-column row\n * (or a child nothing writes into) passes every prop check and then silently fails to expand —\n * rows do not render, while the page fields that hold their copy are marked hidden. Two\n * implementations of \"does this template fit\" would be two answers, and the pair that disagreed\n * is exactly that bug.\n *\n * ONE template child per declared column, in order; a `table` column's child is a container of\n * its own and recurses; every other child must be a block type a row cell can be written into.\n */\nexport function rowTemplateFits(target: ResolverBlock, fields: ResolverSubField[] | null): boolean {\n if (!fields) return false;\n const props = target.props as Record<string, unknown> | undefined;\n const template = Array.isArray(props?.children) ? (props!.children as ResolverBlock[]) : null;\n if (!template || template.length !== fields.length) return false;\n return fields.every((field, column) => {\n const child = template[column]!;\n const nested = subFieldsOf(field);\n if (field.type === \"table\" || nested) return rowTemplateFits(child, nested);\n if (ROW_SLOTS[child.type] === undefined) return false;\n const allowed = field.type === undefined ? undefined : CELL_TYPES[field.type];\n return allowed === undefined || allowed.includes(child.type);\n });\n}\n\n/**\n * Expand a container's ROW TEMPLATE once per override row, writing each column into the block\n * that renders it. Returns false when the shape does not match, and the caller then falls back\n * to the plain write — a mismatch must never mis-assign a value into somebody else's slot.\n *\n * REQUIRES the template's children to correspond to the declared columns one for one, in order,\n * because that is the only correspondence either side states. A nested `table` column is a\n * container of its own and recurses on the same rule, which is how a card list whose cards each\n * hold a bullet list renders every bullet of every card.\n *\n * Row copies get suffixed ids (`f0r1#2`) so a tree never carries the same block id twice —\n * `findBlockById` and every renderer's React key depend on that.\n */\nfunction expandRows(target: ResolverBlock, value: unknown, fields: ResolverSubField[] | null): boolean {\n if (!Array.isArray(value) || !fields) return false;\n if (!rowTemplateFits(target, fields)) return false;\n const props = target.props as Record<string, unknown>;\n const template = props.children as ResolverBlock[];\n\n const children: ResolverBlock[] = [];\n value.forEach((row, index) => {\n const cells = (row ?? {}) as Record<string, unknown>;\n template.forEach((block, column) => {\n const copy = structuredClone(block);\n suffixIds(copy, `#${index}`);\n const field = fields[column]!;\n const cell = cells[field.key];\n const nested = subFieldsOf(field);\n if (nested && Array.isArray(cell)) {\n // A column that is itself a table: the same expansion, one level down.\n if (!expandRows(copy, cell, nested)) writeCell(copy, cell);\n } else if (cell !== undefined) {\n writeCell(copy, cell);\n }\n children.push(copy);\n });\n });\n props.children = children;\n // Kept beside the expansion: it is what the dock reads back, and what says how many rows this\n // instance holds without walking the tree.\n props.rows = value;\n return true;\n}\n\n/** Write one row cell at the slot its block type reads. @see ROW_SLOTS */\nfunction writeCell(block: ResolverBlock, cell: unknown): void {\n const path = ROW_SLOTS[block.type];\n if (!path) return;\n if (path === MARKUP_PATH) writeRichTextOverride(block, cell);\n else if (path === IMAGE_PATH) writeImageOverride(block, cell);\n else setPath(block as unknown as Record<string, unknown>, path, cell);\n}\n\n/**\n * Stamp a row coordinate onto EVERY id in a cloned row — the subtree, not just its root.\n *\n * 🔴 GRANDCHILDREN COLLIDE OTHERWISE. Suffixing only the clone's root left a nested row template\n * carrying its original ids under every outer row, so card 1's first bullet and card 2's first\n * bullet were both `c1r0#0`: `findBlockById` returns whichever comes first (so an override aimed\n * at one lands on the other) and React keys repeat. The nested expansion then appends its own\n * coordinate to these, which is what makes `c1r0#0#1` mean \"outer row 0, inner row 1\".\n */\nfunction suffixIds(block: ResolverBlock, suffix: string): void {\n if (typeof block.id === \"string\") block.id = `${block.id}${suffix}`;\n for (const children of childArrays(block)) for (const child of children) suffixIds(child, suffix);\n}\n\n/**\n * Apply an instance's `overrides` onto a clone of a component's blockJson, honoring the\n * declared `props[]` allowlist (key → target block + path). Returns the original array\n * untouched (referentially stable) when there's nothing to override; returns [] for a\n * non-array input.\n *\n * Every prop but three is written verbatim at its `target.path`. The exceptions are the paths\n * whose value is not a scalar the renderer can read where it lands: `props.html` is written as a\n * `{ html, content }` PAIR, `props.src` as a `src` + `alt` pair, and `props.rows` REPEATS the\n * target's row template once per row.\n * @see writeRichTextOverride · @see writeImageOverride · @see expandRows\n */\nexport function applyOverrides<B extends ResolverBlock>(\n blockJson: unknown,\n propDefs: ResolverPropDef[],\n overrides: Record<string, unknown> | undefined,\n): B[] {\n if (!Array.isArray(blockJson)) return [];\n if (!propDefs.length || !overrides || Object.keys(overrides).length === 0) {\n return blockJson as B[];\n }\n const clone = structuredClone(blockJson) as B[];\n for (const def of propDefs) {\n if (!(def.key in overrides)) continue;\n const target = findBlockById(clone, def.target.blockId);\n if (!target) continue;\n if (writesMarkupSlot(def)) writeRichTextOverride(target, overrides[def.key]);\n else if (writesImageSlot(def)) writeImageOverride(target, overrides[def.key]);\n else if (def.target.path === ROWS_PATH && expandRows(target, overrides[def.key], subFieldsOf(def))) {\n /* the template was repeated per row — see expandRows */\n } else setPath(target as unknown as Record<string, unknown>, def.target.path, overrides[def.key]);\n }\n return clone;\n}\n\n/**\n * How deep a component may nest another component before we stop. Matches the guard the\n * three live renderers already apply, so a tree that renders is a tree that snapshots.\n */\nexport const MAX_COMPONENT_DEPTH = 10;\n\n/** What `resolveDeep` needs to look a nested component up by id. */\nexport type ResolverComponentDef = { blockJson: unknown; props: ResolverPropDef[] };\n\n/** Rebuild a container block with each of its child arrays mapped. The write-side twin of\n * `childArrays` — same four container types, same order. Non-containers pass through. */\nfunction mapChildArrays<B extends ResolverBlock>(block: B, map: (children: B[]) => B[]): B {\n const p = (block.props ?? {}) as Record<string, unknown>;\n switch (block.type) {\n case \"columns\":\n if (!Array.isArray(p.columns)) return block;\n return { ...block, props: { ...p, columns: (p.columns as B[][]).map(map) } };\n case \"section\":\n if (!Array.isArray(p.children)) return block;\n return { ...block, props: { ...p, children: map(p.children as B[]) } };\n case \"slider\":\n if (!Array.isArray(p.slides)) return block;\n return {\n ...block,\n props: {\n ...p,\n slides: (p.slides as { children?: B[] }[]).map((s) =>\n Array.isArray(s.children) ? { ...s, children: map(s.children) } : s,\n ),\n },\n };\n case \"tabs\":\n if (!Array.isArray(p.tabs)) return block;\n return {\n ...block,\n props: {\n ...p,\n tabs: (p.tabs as { children?: B[] }[]).map((t) =>\n Array.isArray(t.children) ? { ...t, children: map(t.children) } : t,\n ),\n },\n };\n default:\n return block;\n }\n}\n\n/**\n * `applyOverrides`, then INLINE every nested `component` block into its resolved blocks.\n *\n * This is the deep/frozen counterpart to `applyOverrides`, and it exists for exactly one\n * caller family: publishing. A published entry's `component-ref` freezes the resolved tree\n * into `published_data` so that later edits to the component cannot silently rewrite content\n * that was already published. `applyOverrides` alone does not deliver that promise once a\n * component nests another (which a `slot` prop makes routine): the outer tree is frozen, but\n * the nested `component` block is still a live REFERENCE that re-resolves at bake time.\n *\n * The three LIVE renderers deliberately keep using `applyOverrides` plus their own\n * per-render recursion — they want the live reference. So there is still exactly one\n * implementation of \"apply overrides to a component\"; this only adds the inlining.\n *\n * Fail-soft, matching the renderers: an empty id, an unknown component, a cycle, or a tree\n * deeper than MAX_COMPONENT_DEPTH resolves to NOTHING rather than throwing — the same thing\n * a visitor already sees for a deleted component.\n *\n * `seen` is the ancestry of the current branch and the caller MUST seed it with the id of\n * the component being resolved, or a component that slots itself will not be caught. It is\n * ancestry-scoped, not global: the same component placed twice as siblings renders twice.\n */\nexport function resolveDeep<B extends ResolverBlock>(\n blockJson: unknown,\n propDefs: ResolverPropDef[],\n overrides: Record<string, unknown> | undefined,\n lookup: (componentId: string) => ResolverComponentDef | undefined,\n seen: readonly string[] = [],\n depth = 0,\n): B[] {\n const applied = applyOverrides<B>(blockJson, propDefs, overrides);\n\n const out: B[] = [];\n for (const block of applied) {\n if (block?.type !== \"component\") {\n out.push(mapChildArrays(block, (children) => resolveDeep<B>(children, [], undefined, lookup, seen, depth)));\n continue;\n }\n // THE INVARIANT: the returned tree never contains a `component` block. Every branch below\n // either inlines it or drops it — including the depth cap, which must NOT bail out by\n // returning the tree as-is. Doing that would leave a live reference sitting inside a\n // \"frozen\" snapshot, which is the exact half-frozen state this function exists to prevent\n // and is harder to notice than rendering nothing.\n if (depth >= MAX_COMPONENT_DEPTH) continue; // too deep — drop, never leave a live ref\n const p = (block.props ?? {}) as { componentId?: unknown; overrides?: unknown };\n const cid = typeof p.componentId === \"string\" ? p.componentId : \"\";\n if (!cid || seen.includes(cid)) continue; // empty slot, or a cycle — render nothing\n const def = lookup(cid);\n if (!def) continue; // unknown/unpublished/deleted — render nothing\n out.push(\n ...resolveDeep<B>(\n def.blockJson,\n def.props,\n p.overrides as Record<string, unknown> | undefined,\n lookup,\n [...seen, cid],\n depth + 1,\n ),\n );\n }\n return out;\n}\n"],"mappings":";;;AAoCO,SAAS,YAAqC,OAAiB;AACpE,QAAM,IAAK,MAAM,SAAS,CAAC;AAC3B,UAAQ,MAAM,MAAM;AAAA,IAClB,KAAK;AACH,aAAO,MAAM,QAAQ,EAAE,OAAO,IAAK,EAAE,UAAoB,CAAC;AAAA,IAC5D,KAAK;AACH,aAAO,MAAM,QAAQ,EAAE,QAAQ,IAAI,CAAC,EAAE,QAAe,IAAI,CAAC;AAAA,IAC5D,KAAK;AACH,aAAO,MAAM,QAAQ,EAAE,MAAM,IACxB,EAAE,OAAgC,IAAI,CAAC,MAAO,MAAM,QAAQ,EAAE,QAAQ,IAAI,EAAE,WAAW,CAAC,CAAE,IAC3F,CAAC;AAAA,IACP,KAAK;AACH,aAAO,MAAM,QAAQ,EAAE,IAAI,IACtB,EAAE,KAA8B,IAAI,CAAC,MAAO,MAAM,QAAQ,EAAE,QAAQ,IAAI,EAAE,WAAW,CAAC,CAAE,IACzF,CAAC;AAAA,IACP;AACE,aAAO,CAAC;AAAA,EACZ;AACF;AAGO,SAAS,cAAuC,QAAa,IAAsB;AACxF,aAAW,KAAK,QAAQ;AACtB,QAAI,GAAG,OAAO,GAAI,QAAO;AACzB,eAAW,SAAS,YAAY,CAAC,GAAG;AAClC,YAAM,QAAQ,cAAc,OAAO,EAAE;AACrC,UAAI,MAAO,QAAO;AAAA,IACpB;AAAA,EACF;AACA,SAAO;AACT;AAGO,SAAS,QAAQ,KAA8B,MAAc,OAAsB;AACxF,QAAM,OAAO,KAAK,MAAM,GAAG;AAC3B,MAAI,MAA+B;AACnC,WAAS,IAAI,GAAG,IAAI,KAAK,SAAS,GAAG,KAAK;AACxC,UAAM,IAAI,KAAK,CAAC;AAChB,QAAI,OAAO,IAAI,CAAC,MAAM,YAAY,IAAI,CAAC,MAAM,KAAM,KAAI,CAAC,IAAI,CAAC;AAC7D,UAAM,IAAI,CAAC;AAAA,EACb;AACA,MAAI,KAAK,KAAK,SAAS,CAAC,CAAE,IAAI;AAChC;AAUA,IAAM,cAAc;AA+BpB,SAAS,sBAAsB,QAAuB,OAAsB;AAC1E,QAAM,QAAQ;AACd,MAAI,OAAO,MAAM,UAAU,YAAY,MAAM,UAAU,KAAM,OAAM,QAAQ,CAAC;AAC5E,QAAM,QAAQ,MAAM;AAEpB,QAAM,WAAW,SAAS,OAAO,UAAU,YAAY,CAAC,MAAM,QAAQ,KAAK,IACtE,QACD;AAEJ,MAAI,YAAY,OAAO,SAAS,SAAS,UAAU;AACjD,UAAM,OAAO,SAAS;AAGtB,QAAI,SAAS,YAAY,OAAW,QAAO,MAAM;AAAA,QAC5C,OAAM,UAAU,SAAS;AAC9B;AAAA,EACF;AACA,QAAM,OAAO;AACb,SAAO,MAAM;AACf;AAGA,IAAM,mBAAmB,CAAC,QAAkC,IAAI,OAAO,SAAS;AAGzE,IAAM,aAAa;AAgB1B,SAAS,mBAAmB,QAAuB,OAAsB;AACvE,QAAM,QAAQ;AACd,MAAI,OAAO,MAAM,UAAU,YAAY,MAAM,UAAU,KAAM,OAAM,QAAQ,CAAC;AAC5E,QAAM,QAAQ,MAAM;AAEpB,MAAI,SAAS,OAAO,UAAU,YAAY,CAAC,MAAM,QAAQ,KAAK,GAAG;AAC/D,UAAM,WAAW;AACjB,UAAM,MAAM,OAAO,SAAS,QAAQ,WAAW,SAAS,MAAM,SAAS;AACvE,QAAI,OAAO,QAAQ,SAAU,OAAM,MAAM;AAGzC,UAAM,MAAM,OAAO,SAAS,YAAY,WAAW,SAAS,UAAU,SAAS;AAC/E,QAAI,OAAO,QAAQ,SAAU,OAAM,MAAM;AACzC;AAAA,EACF;AACA,QAAM,MAAM;AACd;AAGA,IAAM,kBAAkB,CAAC,QAAkC,IAAI,OAAO,SAAS;AAYxE,IAAM,YAAY;AAyBzB,IAAM,aAAgD;AAAA,EACpD,MAAM,CAAC,WAAW,QAAQ,UAAU;AAAA,EACpC,UAAU,CAAC,QAAQ,UAAU;AAAA,EAC7B,KAAK,CAAC,QAAQ;AAAA,EACd,OAAO,CAAC,SAAS,OAAO;AAC1B;AAEA,IAAM,YAAoC;AAAA,EACxC,SAAS;AAAA,EACT,MAAM;AAAA,EACN,UAAU;AAAA,EACV,OAAO;AAAA,EACP,QAAQ;AAAA,EACR,OAAO;AACT;AAGA,IAAM,cAAc,CAAC,SAET,KAAK,UAAU,KAAK,QAAQ,UAAU;AAgB3C,SAAS,gBAAgB,QAAuB,QAA4C;AACjG,MAAI,CAAC,OAAQ,QAAO;AACpB,QAAM,QAAQ,OAAO;AACrB,QAAM,WAAW,MAAM,QAAQ,OAAO,QAAQ,IAAK,MAAO,WAA+B;AACzF,MAAI,CAAC,YAAY,SAAS,WAAW,OAAO,OAAQ,QAAO;AAC3D,SAAO,OAAO,MAAM,CAAC,OAAO,WAAW;AACrC,UAAM,QAAQ,SAAS,MAAM;AAC7B,UAAM,SAAS,YAAY,KAAK;AAChC,QAAI,MAAM,SAAS,WAAW,OAAQ,QAAO,gBAAgB,OAAO,MAAM;AAC1E,QAAI,UAAU,MAAM,IAAI,MAAM,OAAW,QAAO;AAChD,UAAM,UAAU,MAAM,SAAS,SAAY,SAAY,WAAW,MAAM,IAAI;AAC5E,WAAO,YAAY,UAAa,QAAQ,SAAS,MAAM,IAAI;AAAA,EAC7D,CAAC;AACH;AAeA,SAAS,WAAW,QAAuB,OAAgB,QAA4C;AACrG,MAAI,CAAC,MAAM,QAAQ,KAAK,KAAK,CAAC,OAAQ,QAAO;AAC7C,MAAI,CAAC,gBAAgB,QAAQ,MAAM,EAAG,QAAO;AAC7C,QAAM,QAAQ,OAAO;AACrB,QAAM,WAAW,MAAM;AAEvB,QAAM,WAA4B,CAAC;AACnC,QAAM,QAAQ,CAAC,KAAK,UAAU;AAC5B,UAAM,QAAS,OAAO,CAAC;AACvB,aAAS,QAAQ,CAAC,OAAO,WAAW;AAClC,YAAM,OAAO,gBAAgB,KAAK;AAClC,gBAAU,MAAM,IAAI,KAAK,EAAE;AAC3B,YAAM,QAAQ,OAAO,MAAM;AAC3B,YAAM,OAAO,MAAM,MAAM,GAAG;AAC5B,YAAM,SAAS,YAAY,KAAK;AAChC,UAAI,UAAU,MAAM,QAAQ,IAAI,GAAG;AAEjC,YAAI,CAAC,WAAW,MAAM,MAAM,MAAM,EAAG,WAAU,MAAM,IAAI;AAAA,MAC3D,WAAW,SAAS,QAAW;AAC7B,kBAAU,MAAM,IAAI;AAAA,MACtB;AACA,eAAS,KAAK,IAAI;AAAA,IACpB,CAAC;AAAA,EACH,CAAC;AACD,QAAM,WAAW;AAGjB,QAAM,OAAO;AACb,SAAO;AACT;AAGA,SAAS,UAAU,OAAsB,MAAqB;AAC5D,QAAM,OAAO,UAAU,MAAM,IAAI;AACjC,MAAI,CAAC,KAAM;AACX,MAAI,SAAS,YAAa,uBAAsB,OAAO,IAAI;AAAA,WAClD,SAAS,WAAY,oBAAmB,OAAO,IAAI;AAAA,MACvD,SAAQ,OAA6C,MAAM,IAAI;AACtE;AAWA,SAAS,UAAU,OAAsB,QAAsB;AAC7D,MAAI,OAAO,MAAM,OAAO,SAAU,OAAM,KAAK,GAAG,MAAM,EAAE,GAAG,MAAM;AACjE,aAAW,YAAY,YAAY,KAAK,EAAG,YAAW,SAAS,SAAU,WAAU,OAAO,MAAM;AAClG;AAcO,SAAS,eACd,WACA,UACA,WACK;AACL,MAAI,CAAC,MAAM,QAAQ,SAAS,EAAG,QAAO,CAAC;AACvC,MAAI,CAAC,SAAS,UAAU,CAAC,aAAa,OAAO,KAAK,SAAS,EAAE,WAAW,GAAG;AACzE,WAAO;AAAA,EACT;AACA,QAAM,QAAQ,gBAAgB,SAAS;AACvC,aAAW,OAAO,UAAU;AAC1B,QAAI,EAAE,IAAI,OAAO,WAAY;AAC7B,UAAM,SAAS,cAAc,OAAO,IAAI,OAAO,OAAO;AACtD,QAAI,CAAC,OAAQ;AACb,QAAI,iBAAiB,GAAG,EAAG,uBAAsB,QAAQ,UAAU,IAAI,GAAG,CAAC;AAAA,aAClE,gBAAgB,GAAG,EAAG,oBAAmB,QAAQ,UAAU,IAAI,GAAG,CAAC;AAAA,aACnE,IAAI,OAAO,SAAS,aAAa,WAAW,QAAQ,UAAU,IAAI,GAAG,GAAG,YAAY,GAAG,CAAC,GAAG;AAAA,IAEpG,MAAO,SAAQ,QAA8C,IAAI,OAAO,MAAM,UAAU,IAAI,GAAG,CAAC;AAAA,EAClG;AACA,SAAO;AACT;AAMO,IAAM,sBAAsB;AAOnC,SAAS,eAAwC,OAAU,KAAgC;AACzF,QAAM,IAAK,MAAM,SAAS,CAAC;AAC3B,UAAQ,MAAM,MAAM;AAAA,IAClB,KAAK;AACH,UAAI,CAAC,MAAM,QAAQ,EAAE,OAAO,EAAG,QAAO;AACtC,aAAO,EAAE,GAAG,OAAO,OAAO,EAAE,GAAG,GAAG,SAAU,EAAE,QAAkB,IAAI,GAAG,EAAE,EAAE;AAAA,IAC7E,KAAK;AACH,UAAI,CAAC,MAAM,QAAQ,EAAE,QAAQ,EAAG,QAAO;AACvC,aAAO,EAAE,GAAG,OAAO,OAAO,EAAE,GAAG,GAAG,UAAU,IAAI,EAAE,QAAe,EAAE,EAAE;AAAA,IACvE,KAAK;AACH,UAAI,CAAC,MAAM,QAAQ,EAAE,MAAM,EAAG,QAAO;AACrC,aAAO;AAAA,QACL,GAAG;AAAA,QACH,OAAO;AAAA,UACL,GAAG;AAAA,UACH,QAAS,EAAE,OAAgC;AAAA,YAAI,CAAC,MAC9C,MAAM,QAAQ,EAAE,QAAQ,IAAI,EAAE,GAAG,GAAG,UAAU,IAAI,EAAE,QAAQ,EAAE,IAAI;AAAA,UACpE;AAAA,QACF;AAAA,MACF;AAAA,IACF,KAAK;AACH,UAAI,CAAC,MAAM,QAAQ,EAAE,IAAI,EAAG,QAAO;AACnC,aAAO;AAAA,QACL,GAAG;AAAA,QACH,OAAO;AAAA,UACL,GAAG;AAAA,UACH,MAAO,EAAE,KAA8B;AAAA,YAAI,CAAC,MAC1C,MAAM,QAAQ,EAAE,QAAQ,IAAI,EAAE,GAAG,GAAG,UAAU,IAAI,EAAE,QAAQ,EAAE,IAAI;AAAA,UACpE;AAAA,QACF;AAAA,MACF;AAAA,IACF;AACE,aAAO;AAAA,EACX;AACF;AAwBO,SAAS,YACd,WACA,UACA,WACA,QACA,OAA0B,CAAC,GAC3B,QAAQ,GACH;AACL,QAAM,UAAU,eAAkB,WAAW,UAAU,SAAS;AAEhE,QAAM,MAAW,CAAC;AAClB,aAAW,SAAS,SAAS;AAC3B,QAAI,OAAO,SAAS,aAAa;AAC/B,UAAI,KAAK,eAAe,OAAO,CAAC,aAAa,YAAe,UAAU,CAAC,GAAG,QAAW,QAAQ,MAAM,KAAK,CAAC,CAAC;AAC1G;AAAA,IACF;AAMA,QAAI,SAAS,oBAAqB;AAClC,UAAM,IAAK,MAAM,SAAS,CAAC;AAC3B,UAAM,MAAM,OAAO,EAAE,gBAAgB,WAAW,EAAE,cAAc;AAChE,QAAI,CAAC,OAAO,KAAK,SAAS,GAAG,EAAG;AAChC,UAAM,MAAM,OAAO,GAAG;AACtB,QAAI,CAAC,IAAK;AACV,QAAI;AAAA,MACF,GAAG;AAAA,QACD,IAAI;AAAA,QACJ,IAAI;AAAA,QACJ,EAAE;AAAA,QACF;AAAA,QACA,CAAC,GAAG,MAAM,GAAG;AAAA,QACb,QAAQ;AAAA,MACV;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bettercms-ai/ui",
3
- "version": "0.6.0",
3
+ "version": "0.6.1",
4
4
  "type": "module",
5
5
  "description": "BetterCMS block components + registry/renderer for rendering page block_json in React (dashboard builder preview + headless sites).",
6
6
  "sideEffects": false,
@@ -54,7 +54,9 @@
54
54
  "dependencies": {
55
55
  "@bettercms-ai/types": "^1.10.0",
56
56
  "clsx": "^2.1.1",
57
- "tailwind-merge": "^2.6.0"
57
+ "tailwind-merge": "^2.6.0",
58
+ "@portabletext/react": "^6.2.0",
59
+ "@bettercms-ai/richtext": "^0.2.0"
58
60
  },
59
61
  "peerDependencies": {
60
62
  "react": ">=18",