cms-renderer 0.3.3 → 0.3.5
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.
- package/dist/lib/block-renderer.js +1 -1
- package/dist/lib/block-renderer.js.map +1 -1
- package/dist/lib/client-editable-block.js +3 -2
- package/dist/lib/client-editable-block.js.map +1 -1
- package/dist/lib/custom-schemas.js +2 -0
- package/dist/lib/custom-schemas.js.map +1 -1
- package/dist/lib/proxy.js +1 -14
- package/dist/lib/proxy.js.map +1 -1
- package/dist/lib/renderer.js +1 -1
- package/dist/lib/renderer.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// lib/block-renderer.tsx
|
|
2
2
|
import React from "react";
|
|
3
|
-
import { ClientEditableBlock } from "./client-editable-block.js";
|
|
4
3
|
import { BlockToolbar } from "./block-toolbar.js";
|
|
4
|
+
import { ClientEditableBlock } from "./client-editable-block.js";
|
|
5
5
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
6
6
|
function walkReactNode(node, visitors, ctx = {}) {
|
|
7
7
|
const path = ctx.path ?? [];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../lib/block-renderer.tsx"],"sourcesContent":["/**\n * Block Renderer Component\n *\n * Dispatches block data to the appropriate component using the ComponentMap pattern.\n * This is the main entry point for rendering blocks from the CMS.\n */\n\nimport React from 'react';\nimport { ClientEditableBlock } from './client-editable-block';\nimport { BlockToolbar } from './block-toolbar';\nimport type { BlockComponentRegistry, BlockData, ResolvedRouteParams } from './types';\n\ntype TextInfo = {\n value: string;\n path: Array<string | number>;\n parentType?: React.ElementType;\n key?: React.Key | null;\n};\n\ntype ElementInfo = {\n element: React.ReactElement;\n path: Array<string | number>;\n};\n\ntype WalkVisitors = {\n /**\n * Called for every string/number child encountered.\n * Return:\n * - same string (or modified)\n * - a ReactNode (e.g. wrap in <span/>)\n */\n onText?: (info: TextInfo) => React.ReactNode;\n\n /**\n * Called for every ReactElement encountered (after children are processed).\n * Return:\n * - same element\n * - a cloned/modified element\n */\n onElement?: (info: ElementInfo) => React.ReactElement;\n};\n\n/**\n * Recursively maps a ReactNode tree, allowing transformations of text nodes and/or elements.\n * SSR-safe: does not touch DOM APIs.\n */\nexport function walkReactNode(\n node: React.ReactNode,\n visitors: WalkVisitors,\n ctx: {\n path?: Array<string | number>;\n parentType?: React.ElementType;\n key?: React.Key | null;\n } = {}\n): React.ReactNode {\n const path = ctx.path ?? [];\n\n // Fast-path primitives\n if (node == null || typeof node === 'boolean') return node;\n\n if (typeof node === 'string' || typeof node === 'number') {\n const value = String(node);\n return visitors.onText\n ? visitors.onText({ value, path, parentType: ctx.parentType, key: ctx.key })\n : node;\n }\n\n // Arrays\n if (Array.isArray(node)) {\n return node.map((child, i) => {\n // biome-ignore lint/suspicious/noExplicitAny: React child key access\n const childKey = (child as any)?.key ?? null;\n const result = walkReactNode(child, visitors, {\n path: [...path, i],\n parentType: ctx.parentType,\n key: childKey,\n });\n // Ensure array children have keys\n if (React.isValidElement(result) && result.key == null) {\n return React.cloneElement(result, { key: childKey ?? `arr-${path.join('-')}-${i}` });\n }\n return result;\n });\n }\n\n // ReactElement (including Fragment)\n if (React.isValidElement(node)) {\n // biome-ignore lint/suspicious/noExplicitAny: React element props access\n const el = node as React.ReactElement<any>;\n const elProps = el.props as Record<string, unknown> | null;\n\n // Recurse into children (if any)\n const hasChildren = elProps && 'children' in elProps;\n const nextChildren = hasChildren\n ? React.Children.map(elProps.children as React.ReactNode, (child, i) => {\n // biome-ignore lint/suspicious/noExplicitAny: React child key access\n const childKey = (child as any)?.key ?? null;\n const result = walkReactNode(child, visitors, {\n path: [...path, 'children', i],\n parentType: el.type as React.ElementType,\n key: childKey,\n });\n // Ensure children have keys\n if (React.isValidElement(result) && result.key == null) {\n return React.cloneElement(result, { key: childKey ?? `child-${path.join('-')}-${i}` });\n }\n return result;\n })\n : (elProps?.children as React.ReactNode);\n\n // Only clone if children changed (or if you want to force a clone)\n const cloned = hasChildren\n ? React.cloneElement(el, undefined, nextChildren as React.ReactNode)\n : el;\n\n return visitors.onElement ? visitors.onElement({ element: cloned, path }) : cloned;\n }\n\n // Functions, symbols, portals, etc. are rare here; return as-is\n return node;\n}\n\n// -----------------------------------------------------------------------------\n// Content Value Extraction\n// -----------------------------------------------------------------------------\n\ntype ContentMatch = {\n contentPath: string;\n value: string;\n};\n\n/**\n * Extracts all string values from a content object with their paths.\n * Returns a Map where keys are string values and values are arrays of content paths.\n */\nfunction extractContentValues(\n content: Record<string, unknown>,\n basePath: string[] = []\n): Map<string, ContentMatch[]> {\n const map = new Map<string, ContentMatch[]>();\n\n function walk(obj: unknown, path: string[]) {\n if (typeof obj === 'string' && obj.trim() !== '') {\n const contentPath = path.join('.');\n const existing = map.get(obj) || [];\n existing.push({ contentPath, value: obj });\n map.set(obj, existing);\n } else if (Array.isArray(obj)) {\n for (let index = 0; index < obj.length; index++) {\n walk(obj[index], [...path, String(index)]);\n }\n } else if (obj && typeof obj === 'object') {\n for (const [key, value] of Object.entries(obj)) {\n walk(value, [...path, key]);\n }\n }\n }\n\n walk(content, basePath);\n return map;\n}\n\n// -----------------------------------------------------------------------------\n// Props\n// -----------------------------------------------------------------------------\n\ninterface BlockRendererProps {\n /**\n * The block data to render.\n * Must have a `type` field that maps to a registered component.\n */\n block: BlockData;\n registry: Partial<BlockComponentRegistry>;\n /**\n * If true, renders the component without any tree walking or editable wrappers.\n */\n disableEditable?: boolean;\n /**\n * Resolved route parameters from parametric routes.\n * Each key is a param name (e.g., \"country\") with its value, schema name, and full document.\n */\n routeParams?: ResolvedRouteParams;\n}\n\n// -----------------------------------------------------------------------------\n// Component\n// -----------------------------------------------------------------------------\n\n/**\n * Recursively renders a React node, invoking function components to get their output.\n * This allows us to walk the full rendered tree, not just the element wrappers.\n */\nfunction renderToWalkableTree(node: React.ReactNode, keyPrefix = ''): React.ReactNode {\n if (node == null || typeof node === 'boolean') return node;\n if (typeof node === 'string' || typeof node === 'number') return node;\n\n if (Array.isArray(node)) {\n return node.map((child, i) => {\n const result = renderToWalkableTree(child, `${keyPrefix}${i}-`);\n // Ensure array children have keys\n if (React.isValidElement(result) && result.key == null) {\n // biome-ignore lint/suspicious/noExplicitAny: Adding key to element\n const existingKey = (child as any)?.key;\n return React.cloneElement(result, { key: existingKey ?? `${keyPrefix}${i}` });\n }\n return result;\n });\n }\n\n if (React.isValidElement(node)) {\n // biome-ignore lint/suspicious/noExplicitAny: React element props access\n const el = node as React.ReactElement<any>;\n const elProps = el.props as Record<string, unknown> | null;\n\n // If it's a function component, invoke it to get the rendered output\n if (typeof el.type === 'function') {\n try {\n // biome-ignore lint/complexity/noBannedTypes: Need to invoke React function component\n const rendered = (el.type as Function)(el.props);\n return renderToWalkableTree(rendered, keyPrefix);\n } catch {\n // If component throws (e.g., uses hooks), return as-is\n return node;\n }\n }\n\n // For host elements (div, span, etc.), recurse into children\n if (elProps && 'children' in elProps) {\n const newChildren = renderToWalkableTree(elProps.children as React.ReactNode, keyPrefix);\n return React.cloneElement(el, undefined, newChildren);\n }\n\n return node;\n }\n\n return node;\n}\n\n/**\n * Renders a single block by dispatching to the appropriate component.\n *\n * Uses the ComponentMap pattern: the block's `type` field determines which\n * component renders the block's `content`.\n *\n * Internally, it:\n * 1. Renders the component tree by invoking function components\n * 2. Extracts all string values from block.content\n * 3. Walks the rendered tree and wraps matching text nodes with spans\n */\nexport function BlockRenderer({\n block,\n registry,\n disableEditable,\n routeParams,\n}: BlockRendererProps) {\n const Component = registry[block.type];\n\n if (!Component) {\n // Log warning in development, render nothing in production\n if (process.env.NODE_ENV === 'development') {\n console.warn(`[BlockRenderer] Unknown block type: ${block.type}`);\n }\n return null;\n }\n\n // biome-ignore lint/suspicious/noExplicitAny: Type safety ensured by BlockData discriminated union\n const component = <Component content={block.content as any} routeParams={routeParams} />;\n\n if (disableEditable) {\n return component;\n }\n\n // Extract all string values from content with their paths\n const contentValueMap = extractContentValues(block.content as Record<string, unknown>);\n\n // Try server-side tree walking for text span injection (best-effort).\n // This works for sync server components but fails for client components,\n // async components, and components using hooks.\n let renderedComponent: React.ReactNode = component;\n let needsClientSideSpans = true;\n\n try {\n const renderedTree = renderToWalkableTree(component);\n\n // Check if tree walking produced host elements (div, section, etc.)\n // vs the original component reference (function type = couldn't be invoked)\n const isWalkable =\n React.isValidElement(renderedTree) &&\n typeof (renderedTree as React.ReactElement<Record<string, unknown>>).type === 'string';\n\n if (isWalkable) {\n const usedPaths = new Set<string>();\n\n renderedComponent = walkReactNode(renderedTree, {\n onText: ({ value, key, path }) => {\n const matches = contentValueMap.get(value);\n if (!matches || matches.length === 0) return value;\n\n const match = matches.find((m) => !usedPaths.has(m.contentPath)) ?? matches[0];\n if (!match) return value;\n\n usedPaths.add(match.contentPath);\n const spanKey = key ?? `${block.id}-${match.contentPath}-${path.join('-')}`;\n\n return (\n <span\n key={spanKey}\n data-cms-editable\n data-block-id={block.id}\n data-block-type={block.type}\n data-content-path={match.contentPath}\n >\n {value}\n </span>\n );\n },\n });\n\n needsClientSideSpans = false;\n }\n } catch {\n // Tree walking failed entirely, fall back to client-side injection\n }\n\n // Build content entries for client-side text matching when server-side fails\n const contentEntries = needsClientSideSpans\n ? Array.from(contentValueMap.entries())\n .map(([value, matches]) => ({ v: value, p: matches[0]?.contentPath }))\n .filter((e): e is { v: string; p: string } => !!e.p)\n : [];\n\n return (\n <div\n key={block.id}\n data-cms-block\n data-block-id={block.id}\n data-block-type={block.type}\n style={{ position: 'relative' }}\n >\n <style>{`\n [data-cms-block] {\n position: relative;\n }\n [data-cms-block]:hover {\n outline: 2px solid #3b82f6;\n outline-offset: 4px;\n }\n [data-cms-editable] {\n cursor: pointer;\n border-radius: 2px;\n }\n [data-cms-editable]:hover {\n outline: 2px solid #3b82f6;\n outline-offset: 2px;\n }\n .cms-block-toolbar {\n position: absolute;\n bottom: 8px;\n left: 50%;\n transform: translateX(-50%);\n display: flex;\n gap: 4px;\n background: #1f2937;\n border-radius: 6px;\n padding: 4px;\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);\n opacity: 0;\n pointer-events: none;\n transition: opacity 0.15s ease;\n z-index: 1000;\n }\n [data-cms-block]:hover .cms-block-toolbar {\n opacity: 1;\n pointer-events: auto;\n }\n .cms-block-toolbar button {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 28px;\n height: 28px;\n border: none;\n background: transparent;\n color: #9ca3af;\n border-radius: 4px;\n cursor: pointer;\n transition: background 0.15s ease, color 0.15s ease;\n }\n .cms-block-toolbar button:hover {\n background: #374151;\n color: #fff;\n }\n .cms-block-toolbar button.delete:hover {\n background: #dc2626;\n color: #fff;\n }\n .cms-block-toolbar button:disabled {\n opacity: 0.4;\n cursor: not-allowed;\n }\n .cms-block-toolbar button:disabled:hover {\n background: transparent;\n color: #9ca3af;\n }\n .cms-block-toolbar svg {\n width: 16px;\n height: 16px;\n }\n `}</style>\n <script\n // biome-ignore lint/security/noDangerouslySetInnerHtml: Inline script for iframe postMessage click routing\n dangerouslySetInnerHTML={{\n __html: `\n (function() {\n if (!window.__cmsEditableInitialized) {\n window.__cmsEditableInitialized = true;\n\n document.addEventListener('click', function(e) {\n if (e.target.closest('.cms-block-toolbar')) return;\n\n var editableTarget = e.target.closest('[data-cms-editable]');\n if (editableTarget) {\n var message = {\n type: 'cms-editable-click',\n blockId: editableTarget.getAttribute('data-block-id'),\n blockType: editableTarget.getAttribute('data-block-type'),\n contentPath: editableTarget.getAttribute('data-content-path')\n };\n if (window.parent && window.parent !== window) {\n window.parent.postMessage(message, '*');\n }\n return;\n }\n\n var blockTarget = e.target.closest('[data-cms-block]');\n if (blockTarget) {\n var message = {\n type: 'cms-editable-click',\n blockId: blockTarget.getAttribute('data-block-id'),\n blockType: blockTarget.getAttribute('data-block-type'),\n contentPath: null\n };\n if (window.parent && window.parent !== window) {\n window.parent.postMessage(message, '*');\n }\n }\n });\n }\n })();\n `,\n }}\n />\n {needsClientSideSpans && contentEntries.length > 0 ? (\n <ClientEditableBlock\n blockId={block.id}\n blockType={block.type}\n contentEntries={contentEntries}\n >\n {renderedComponent}\n </ClientEditableBlock>\n ) : (\n renderedComponent\n )}\n <BlockToolbar key=\"cms-toolbar\" blockId={block.id} />\n </div>\n );\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../lib/block-renderer.tsx"],"sourcesContent":["/**\n * Block Renderer Component\n *\n * Dispatches block data to the appropriate component using the ComponentMap pattern.\n * This is the main entry point for rendering blocks from the CMS.\n */\n\nimport React from 'react';\nimport { BlockToolbar } from './block-toolbar';\nimport { ClientEditableBlock } from './client-editable-block';\nimport type { BlockComponentRegistry, BlockData, ResolvedRouteParams } from './types';\n\ntype TextInfo = {\n value: string;\n path: Array<string | number>;\n parentType?: React.ElementType;\n key?: React.Key | null;\n};\n\ntype ElementInfo = {\n element: React.ReactElement;\n path: Array<string | number>;\n};\n\ntype WalkVisitors = {\n /**\n * Called for every string/number child encountered.\n * Return:\n * - same string (or modified)\n * - a ReactNode (e.g. wrap in <span/>)\n */\n onText?: (info: TextInfo) => React.ReactNode;\n\n /**\n * Called for every ReactElement encountered (after children are processed).\n * Return:\n * - same element\n * - a cloned/modified element\n */\n onElement?: (info: ElementInfo) => React.ReactElement;\n};\n\n/**\n * Recursively maps a ReactNode tree, allowing transformations of text nodes and/or elements.\n * SSR-safe: does not touch DOM APIs.\n */\nexport function walkReactNode(\n node: React.ReactNode,\n visitors: WalkVisitors,\n ctx: {\n path?: Array<string | number>;\n parentType?: React.ElementType;\n key?: React.Key | null;\n } = {}\n): React.ReactNode {\n const path = ctx.path ?? [];\n\n // Fast-path primitives\n if (node == null || typeof node === 'boolean') return node;\n\n if (typeof node === 'string' || typeof node === 'number') {\n const value = String(node);\n return visitors.onText\n ? visitors.onText({ value, path, parentType: ctx.parentType, key: ctx.key })\n : node;\n }\n\n // Arrays\n if (Array.isArray(node)) {\n return node.map((child, i) => {\n // biome-ignore lint/suspicious/noExplicitAny: React child key access\n const childKey = (child as any)?.key ?? null;\n const result = walkReactNode(child, visitors, {\n path: [...path, i],\n parentType: ctx.parentType,\n key: childKey,\n });\n // Ensure array children have keys\n if (React.isValidElement(result) && result.key == null) {\n return React.cloneElement(result, { key: childKey ?? `arr-${path.join('-')}-${i}` });\n }\n return result;\n });\n }\n\n // ReactElement (including Fragment)\n if (React.isValidElement(node)) {\n // biome-ignore lint/suspicious/noExplicitAny: React element props access\n const el = node as React.ReactElement<any>;\n const elProps = el.props as Record<string, unknown> | null;\n\n // Recurse into children (if any)\n const hasChildren = elProps && 'children' in elProps;\n const nextChildren = hasChildren\n ? React.Children.map(elProps.children as React.ReactNode, (child, i) => {\n // biome-ignore lint/suspicious/noExplicitAny: React child key access\n const childKey = (child as any)?.key ?? null;\n const result = walkReactNode(child, visitors, {\n path: [...path, 'children', i],\n parentType: el.type as React.ElementType,\n key: childKey,\n });\n // Ensure children have keys\n if (React.isValidElement(result) && result.key == null) {\n return React.cloneElement(result, { key: childKey ?? `child-${path.join('-')}-${i}` });\n }\n return result;\n })\n : (elProps?.children as React.ReactNode);\n\n // Only clone if children changed (or if you want to force a clone)\n const cloned = hasChildren\n ? React.cloneElement(el, undefined, nextChildren as React.ReactNode)\n : el;\n\n return visitors.onElement ? visitors.onElement({ element: cloned, path }) : cloned;\n }\n\n // Functions, symbols, portals, etc. are rare here; return as-is\n return node;\n}\n\n// -----------------------------------------------------------------------------\n// Content Value Extraction\n// -----------------------------------------------------------------------------\n\ntype ContentMatch = {\n contentPath: string;\n value: string;\n};\n\n/**\n * Extracts all string values from a content object with their paths.\n * Returns a Map where keys are string values and values are arrays of content paths.\n */\nfunction extractContentValues(\n content: Record<string, unknown>,\n basePath: string[] = []\n): Map<string, ContentMatch[]> {\n const map = new Map<string, ContentMatch[]>();\n\n function walk(obj: unknown, path: string[]) {\n if (typeof obj === 'string' && obj.trim() !== '') {\n const contentPath = path.join('.');\n const existing = map.get(obj) || [];\n existing.push({ contentPath, value: obj });\n map.set(obj, existing);\n } else if (Array.isArray(obj)) {\n for (let index = 0; index < obj.length; index++) {\n walk(obj[index], [...path, String(index)]);\n }\n } else if (obj && typeof obj === 'object') {\n for (const [key, value] of Object.entries(obj)) {\n walk(value, [...path, key]);\n }\n }\n }\n\n walk(content, basePath);\n return map;\n}\n\n// -----------------------------------------------------------------------------\n// Props\n// -----------------------------------------------------------------------------\n\ninterface BlockRendererProps {\n /**\n * The block data to render.\n * Must have a `type` field that maps to a registered component.\n */\n block: BlockData;\n registry: Partial<BlockComponentRegistry>;\n /**\n * If true, renders the component without any tree walking or editable wrappers.\n */\n disableEditable?: boolean;\n /**\n * Resolved route parameters from parametric routes.\n * Each key is a param name (e.g., \"country\") with its value, schema name, and full document.\n */\n routeParams?: ResolvedRouteParams;\n}\n\n// -----------------------------------------------------------------------------\n// Component\n// -----------------------------------------------------------------------------\n\n/**\n * Recursively renders a React node, invoking function components to get their output.\n * This allows us to walk the full rendered tree, not just the element wrappers.\n */\nfunction renderToWalkableTree(node: React.ReactNode, keyPrefix = ''): React.ReactNode {\n if (node == null || typeof node === 'boolean') return node;\n if (typeof node === 'string' || typeof node === 'number') return node;\n\n if (Array.isArray(node)) {\n return node.map((child, i) => {\n const result = renderToWalkableTree(child, `${keyPrefix}${i}-`);\n // Ensure array children have keys\n if (React.isValidElement(result) && result.key == null) {\n // biome-ignore lint/suspicious/noExplicitAny: Adding key to element\n const existingKey = (child as any)?.key;\n return React.cloneElement(result, { key: existingKey ?? `${keyPrefix}${i}` });\n }\n return result;\n });\n }\n\n if (React.isValidElement(node)) {\n // biome-ignore lint/suspicious/noExplicitAny: React element props access\n const el = node as React.ReactElement<any>;\n const elProps = el.props as Record<string, unknown> | null;\n\n // If it's a function component, invoke it to get the rendered output\n if (typeof el.type === 'function') {\n try {\n // biome-ignore lint/complexity/noBannedTypes: Need to invoke React function component\n const rendered = (el.type as Function)(el.props);\n return renderToWalkableTree(rendered, keyPrefix);\n } catch {\n // If component throws (e.g., uses hooks), return as-is\n return node;\n }\n }\n\n // For host elements (div, span, etc.), recurse into children\n if (elProps && 'children' in elProps) {\n const newChildren = renderToWalkableTree(elProps.children as React.ReactNode, keyPrefix);\n return React.cloneElement(el, undefined, newChildren);\n }\n\n return node;\n }\n\n return node;\n}\n\n/**\n * Renders a single block by dispatching to the appropriate component.\n *\n * Uses the ComponentMap pattern: the block's `type` field determines which\n * component renders the block's `content`.\n *\n * Internally, it:\n * 1. Renders the component tree by invoking function components\n * 2. Extracts all string values from block.content\n * 3. Walks the rendered tree and wraps matching text nodes with spans\n */\nexport function BlockRenderer({\n block,\n registry,\n disableEditable,\n routeParams,\n}: BlockRendererProps) {\n const Component = registry[block.type];\n\n if (!Component) {\n // Log warning in development, render nothing in production\n if (process.env.NODE_ENV === 'development') {\n console.warn(`[BlockRenderer] Unknown block type: ${block.type}`);\n }\n return null;\n }\n\n // biome-ignore lint/suspicious/noExplicitAny: Type safety ensured by BlockData discriminated union\n const component = <Component content={block.content as any} routeParams={routeParams} />;\n\n if (disableEditable) {\n return component;\n }\n\n // Extract all string values from content with their paths\n const contentValueMap = extractContentValues(block.content as Record<string, unknown>);\n\n // Try server-side tree walking for text span injection (best-effort).\n // This works for sync server components but fails for client components,\n // async components, and components using hooks.\n let renderedComponent: React.ReactNode = component;\n let needsClientSideSpans = true;\n\n try {\n const renderedTree = renderToWalkableTree(component);\n\n // Check if tree walking produced host elements (div, section, etc.)\n // vs the original component reference (function type = couldn't be invoked)\n const isWalkable =\n React.isValidElement(renderedTree) &&\n typeof (renderedTree as React.ReactElement<Record<string, unknown>>).type === 'string';\n\n if (isWalkable) {\n const usedPaths = new Set<string>();\n\n renderedComponent = walkReactNode(renderedTree, {\n onText: ({ value, key, path }) => {\n const matches = contentValueMap.get(value);\n if (!matches || matches.length === 0) return value;\n\n const match = matches.find((m) => !usedPaths.has(m.contentPath)) ?? matches[0];\n if (!match) return value;\n\n usedPaths.add(match.contentPath);\n const spanKey = key ?? `${block.id}-${match.contentPath}-${path.join('-')}`;\n\n return (\n <span\n key={spanKey}\n data-cms-editable\n data-block-id={block.id}\n data-block-type={block.type}\n data-content-path={match.contentPath}\n >\n {value}\n </span>\n );\n },\n });\n\n needsClientSideSpans = false;\n }\n } catch {\n // Tree walking failed entirely, fall back to client-side injection\n }\n\n // Build content entries for client-side text matching when server-side fails\n const contentEntries = needsClientSideSpans\n ? Array.from(contentValueMap.entries())\n .map(([value, matches]) => ({ v: value, p: matches[0]?.contentPath }))\n .filter((e): e is { v: string; p: string } => !!e.p)\n : [];\n\n return (\n <div\n key={block.id}\n data-cms-block\n data-block-id={block.id}\n data-block-type={block.type}\n style={{ position: 'relative' }}\n >\n <style>{`\n [data-cms-block] {\n position: relative;\n }\n [data-cms-block]:hover {\n outline: 2px solid #3b82f6;\n outline-offset: 4px;\n }\n [data-cms-editable] {\n cursor: pointer;\n border-radius: 2px;\n }\n [data-cms-editable]:hover {\n outline: 2px solid #3b82f6;\n outline-offset: 2px;\n }\n .cms-block-toolbar {\n position: absolute;\n bottom: 8px;\n left: 50%;\n transform: translateX(-50%);\n display: flex;\n gap: 4px;\n background: #1f2937;\n border-radius: 6px;\n padding: 4px;\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);\n opacity: 0;\n pointer-events: none;\n transition: opacity 0.15s ease;\n z-index: 1000;\n }\n [data-cms-block]:hover .cms-block-toolbar {\n opacity: 1;\n pointer-events: auto;\n }\n .cms-block-toolbar button {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 28px;\n height: 28px;\n border: none;\n background: transparent;\n color: #9ca3af;\n border-radius: 4px;\n cursor: pointer;\n transition: background 0.15s ease, color 0.15s ease;\n }\n .cms-block-toolbar button:hover {\n background: #374151;\n color: #fff;\n }\n .cms-block-toolbar button.delete:hover {\n background: #dc2626;\n color: #fff;\n }\n .cms-block-toolbar button:disabled {\n opacity: 0.4;\n cursor: not-allowed;\n }\n .cms-block-toolbar button:disabled:hover {\n background: transparent;\n color: #9ca3af;\n }\n .cms-block-toolbar svg {\n width: 16px;\n height: 16px;\n }\n `}</style>\n <script\n // biome-ignore lint/security/noDangerouslySetInnerHtml: Inline script for iframe postMessage click routing\n dangerouslySetInnerHTML={{\n __html: `\n (function() {\n if (!window.__cmsEditableInitialized) {\n window.__cmsEditableInitialized = true;\n\n document.addEventListener('click', function(e) {\n if (e.target.closest('.cms-block-toolbar')) return;\n\n var editableTarget = e.target.closest('[data-cms-editable]');\n if (editableTarget) {\n var message = {\n type: 'cms-editable-click',\n blockId: editableTarget.getAttribute('data-block-id'),\n blockType: editableTarget.getAttribute('data-block-type'),\n contentPath: editableTarget.getAttribute('data-content-path')\n };\n if (window.parent && window.parent !== window) {\n window.parent.postMessage(message, '*');\n }\n return;\n }\n\n var blockTarget = e.target.closest('[data-cms-block]');\n if (blockTarget) {\n var message = {\n type: 'cms-editable-click',\n blockId: blockTarget.getAttribute('data-block-id'),\n blockType: blockTarget.getAttribute('data-block-type'),\n contentPath: null\n };\n if (window.parent && window.parent !== window) {\n window.parent.postMessage(message, '*');\n }\n }\n });\n }\n })();\n `,\n }}\n />\n {needsClientSideSpans && contentEntries.length > 0 ? (\n <ClientEditableBlock\n blockId={block.id}\n blockType={block.type}\n contentEntries={contentEntries}\n >\n {renderedComponent}\n </ClientEditableBlock>\n ) : (\n renderedComponent\n )}\n <BlockToolbar key=\"cms-toolbar\" blockId={block.id} />\n </div>\n );\n}\n"],"mappings":"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","names":[]}
|
|
@@ -32,12 +32,13 @@ function ClientEditableBlock({
|
|
|
32
32
|
const used = /* @__PURE__ */ new Set();
|
|
33
33
|
const walker = document.createTreeWalker(container, NodeFilter.SHOW_TEXT, null);
|
|
34
34
|
const textNodes = [];
|
|
35
|
-
let n;
|
|
36
|
-
while (n
|
|
35
|
+
let n = walker.nextNode();
|
|
36
|
+
while (n !== null) {
|
|
37
37
|
const textNode = n;
|
|
38
38
|
if (textNode.parentElement?.closest(".cms-block-toolbar") == null && textNode.nodeValue?.trim()) {
|
|
39
39
|
textNodes.push(textNode);
|
|
40
40
|
}
|
|
41
|
+
n = walker.nextNode();
|
|
41
42
|
}
|
|
42
43
|
for (const textNode of textNodes) {
|
|
43
44
|
const text = textNode.nodeValue;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../lib/client-editable-block.tsx"],"sourcesContent":["'use client';\n\nimport { useCallback, useLayoutEffect, useRef } from 'react';\n\ninterface ContentEntry {\n v: string;\n p: string;\n}\n\ninterface ClientEditableBlockProps {\n blockId: string;\n blockType: string;\n contentEntries: ContentEntry[];\n children: React.ReactNode;\n}\n\n/**\n * Client-side span injector for blocks whose components use React hooks\n * and therefore can't be walked server-side.\n *\n * Uses a MutationObserver to re-inject [data-cms-editable] spans after every\n * React commit (including state-driven re-renders like animations), so the\n * overlays survive React's reconciliation.\n */\nexport function ClientEditableBlock({\n blockId,\n blockType,\n contentEntries,\n children,\n}: ClientEditableBlockProps) {\n const containerRef = useRef<HTMLDivElement>(null);\n const observerRef = useRef<MutationObserver | null>(null);\n const isInjectingRef = useRef(false);\n\n const injectSpans = useCallback(() => {\n if (isInjectingRef.current) return;\n const container = containerRef.current;\n if (!container) return;\n\n isInjectingRef.current = true;\n // Disconnect observer while we mutate the DOM to avoid re-entrant calls.\n observerRef.current?.disconnect();\n\n try {\n // Remove previously injected text-level spans, restoring the original text nodes.\n const existing = Array.from(\n container.querySelectorAll<HTMLElement>('span[data-cms-editable][data-content-path]')\n );\n for (const span of existing) {\n const parent = span.parentNode;\n if (!parent) continue;\n while (span.firstChild) parent.insertBefore(span.firstChild, span);\n parent.removeChild(span);\n }\n\n // Walk all visible text nodes and wrap the ones that match content values.\n const used = new Set<string>();\n const walker = document.createTreeWalker(container, NodeFilter.SHOW_TEXT, null);\n const textNodes: Text[] = [];\n let n
|
|
1
|
+
{"version":3,"sources":["../../lib/client-editable-block.tsx"],"sourcesContent":["'use client';\n\nimport { useCallback, useLayoutEffect, useRef } from 'react';\n\ninterface ContentEntry {\n v: string;\n p: string;\n}\n\ninterface ClientEditableBlockProps {\n blockId: string;\n blockType: string;\n contentEntries: ContentEntry[];\n children: React.ReactNode;\n}\n\n/**\n * Client-side span injector for blocks whose components use React hooks\n * and therefore can't be walked server-side.\n *\n * Uses a MutationObserver to re-inject [data-cms-editable] spans after every\n * React commit (including state-driven re-renders like animations), so the\n * overlays survive React's reconciliation.\n */\nexport function ClientEditableBlock({\n blockId,\n blockType,\n contentEntries,\n children,\n}: ClientEditableBlockProps) {\n const containerRef = useRef<HTMLDivElement>(null);\n const observerRef = useRef<MutationObserver | null>(null);\n const isInjectingRef = useRef(false);\n\n const injectSpans = useCallback(() => {\n if (isInjectingRef.current) return;\n const container = containerRef.current;\n if (!container) return;\n\n isInjectingRef.current = true;\n // Disconnect observer while we mutate the DOM to avoid re-entrant calls.\n observerRef.current?.disconnect();\n\n try {\n // Remove previously injected text-level spans, restoring the original text nodes.\n const existing = Array.from(\n container.querySelectorAll<HTMLElement>('span[data-cms-editable][data-content-path]')\n );\n for (const span of existing) {\n const parent = span.parentNode;\n if (!parent) continue;\n while (span.firstChild) parent.insertBefore(span.firstChild, span);\n parent.removeChild(span);\n }\n\n // Walk all visible text nodes and wrap the ones that match content values.\n const used = new Set<string>();\n const walker = document.createTreeWalker(container, NodeFilter.SHOW_TEXT, null);\n const textNodes: Text[] = [];\n let n = walker.nextNode();\n while (n !== null) {\n const textNode = n as Text;\n if (\n textNode.parentElement?.closest('.cms-block-toolbar') == null &&\n textNode.nodeValue?.trim()\n ) {\n textNodes.push(textNode);\n }\n n = walker.nextNode();\n }\n\n for (const textNode of textNodes) {\n const text = textNode.nodeValue;\n if (!text) continue;\n for (const entry of contentEntries) {\n if (used.has(entry.p)) continue;\n if (text.indexOf(entry.v) !== -1 && text.trim() === entry.v.trim()) {\n used.add(entry.p);\n const span = document.createElement('span');\n span.setAttribute('data-cms-editable', '');\n span.setAttribute('data-block-id', blockId);\n span.setAttribute('data-block-type', blockType);\n span.setAttribute('data-content-path', entry.p);\n textNode.parentNode?.insertBefore(span, textNode);\n span.appendChild(textNode);\n break;\n }\n }\n }\n } finally {\n isInjectingRef.current = false;\n // Reconnect after our mutations are done.\n const current = containerRef.current;\n if (current && observerRef.current) {\n observerRef.current.observe(current, {\n childList: true,\n subtree: true,\n characterData: true,\n });\n }\n }\n }, [blockId, blockType, contentEntries]);\n\n useLayoutEffect(() => {\n // Initial injection after React's first commit.\n injectSpans();\n\n // Re-inject whenever the child component mutates the DOM\n // (e.g. animated state changes that swap visible elements).\n const observer = new MutationObserver(injectSpans);\n observerRef.current = observer;\n\n if (containerRef.current) {\n observer.observe(containerRef.current, {\n childList: true,\n subtree: true,\n characterData: true,\n });\n }\n\n return () => {\n observer.disconnect();\n observerRef.current = null;\n };\n }, [injectSpans]);\n\n return <div ref={containerRef}>{children}</div>;\n}\n"],"mappings":";;;;AAEA,SAAS,aAAa,iBAAiB,cAAc;AA4H5C;AAtGF,SAAS,oBAAoB;AAAA,EAClC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAA6B;AAC3B,QAAM,eAAe,OAAuB,IAAI;AAChD,QAAM,cAAc,OAAgC,IAAI;AACxD,QAAM,iBAAiB,OAAO,KAAK;AAEnC,QAAM,cAAc,YAAY,MAAM;AACpC,QAAI,eAAe,QAAS;AAC5B,UAAM,YAAY,aAAa;AAC/B,QAAI,CAAC,UAAW;AAEhB,mBAAe,UAAU;AAEzB,gBAAY,SAAS,WAAW;AAEhC,QAAI;AAEF,YAAM,WAAW,MAAM;AAAA,QACrB,UAAU,iBAA8B,4CAA4C;AAAA,MACtF;AACA,iBAAW,QAAQ,UAAU;AAC3B,cAAM,SAAS,KAAK;AACpB,YAAI,CAAC,OAAQ;AACb,eAAO,KAAK,WAAY,QAAO,aAAa,KAAK,YAAY,IAAI;AACjE,eAAO,YAAY,IAAI;AAAA,MACzB;AAGA,YAAM,OAAO,oBAAI,IAAY;AAC7B,YAAM,SAAS,SAAS,iBAAiB,WAAW,WAAW,WAAW,IAAI;AAC9E,YAAM,YAAoB,CAAC;AAC3B,UAAI,IAAI,OAAO,SAAS;AACxB,aAAO,MAAM,MAAM;AACjB,cAAM,WAAW;AACjB,YACE,SAAS,eAAe,QAAQ,oBAAoB,KAAK,QACzD,SAAS,WAAW,KAAK,GACzB;AACA,oBAAU,KAAK,QAAQ;AAAA,QACzB;AACA,YAAI,OAAO,SAAS;AAAA,MACtB;AAEA,iBAAW,YAAY,WAAW;AAChC,cAAM,OAAO,SAAS;AACtB,YAAI,CAAC,KAAM;AACX,mBAAW,SAAS,gBAAgB;AAClC,cAAI,KAAK,IAAI,MAAM,CAAC,EAAG;AACvB,cAAI,KAAK,QAAQ,MAAM,CAAC,MAAM,MAAM,KAAK,KAAK,MAAM,MAAM,EAAE,KAAK,GAAG;AAClE,iBAAK,IAAI,MAAM,CAAC;AAChB,kBAAM,OAAO,SAAS,cAAc,MAAM;AAC1C,iBAAK,aAAa,qBAAqB,EAAE;AACzC,iBAAK,aAAa,iBAAiB,OAAO;AAC1C,iBAAK,aAAa,mBAAmB,SAAS;AAC9C,iBAAK,aAAa,qBAAqB,MAAM,CAAC;AAC9C,qBAAS,YAAY,aAAa,MAAM,QAAQ;AAChD,iBAAK,YAAY,QAAQ;AACzB;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAAA,IACF,UAAE;AACA,qBAAe,UAAU;AAEzB,YAAM,UAAU,aAAa;AAC7B,UAAI,WAAW,YAAY,SAAS;AAClC,oBAAY,QAAQ,QAAQ,SAAS;AAAA,UACnC,WAAW;AAAA,UACX,SAAS;AAAA,UACT,eAAe;AAAA,QACjB,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF,GAAG,CAAC,SAAS,WAAW,cAAc,CAAC;AAEvC,kBAAgB,MAAM;AAEpB,gBAAY;AAIZ,UAAM,WAAW,IAAI,iBAAiB,WAAW;AACjD,gBAAY,UAAU;AAEtB,QAAI,aAAa,SAAS;AACxB,eAAS,QAAQ,aAAa,SAAS;AAAA,QACrC,WAAW;AAAA,QACX,SAAS;AAAA,QACT,eAAe;AAAA,MACjB,CAAC;AAAA,IACH;AAEA,WAAO,MAAM;AACX,eAAS,WAAW;AACpB,kBAAY,UAAU;AAAA,IACxB;AAAA,EACF,GAAG,CAAC,WAAW,CAAC;AAEhB,SAAO,oBAAC,SAAI,KAAK,cAAe,UAAS;AAC3C;","names":[]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../node_modules/ret/dist/types/tokens.js","../../../../node_modules/ret/lib/types/types.ts","../../../../node_modules/ret/dist/types/set-lookup.js","../../../../node_modules/ret/lib/types/index.ts","../../../../node_modules/ret/lib/sets.ts","../../../../node_modules/ret/lib/util.ts","../../../../node_modules/ret/lib/tokenizer.ts","../../../../node_modules/ret/lib/sets-lookup.ts","../../../../node_modules/ret/lib/write-set-tokens.ts","../../../../node_modules/ret/lib/reconstruct.ts","../../../../node_modules/ret/lib/index.ts","../../../../node_modules/safe-regex2/index.js","../../lib/custom-schemas.ts","../../../../packages/cms-schema/src/documents/generate-zod-code.ts","../../../../packages/cms-schema/src/documents/registry.ts","../../../../packages/cms-schema/src/documents/schemas/country.ts","../../../../packages/cms-schema/src/documents/schemas/language.ts","../../../../packages/cms-schema/src/documents/unified-registry.ts","../../../../packages/cms-schema/src/documents/rehydration.ts","../../../../packages/cms-schema/src/fields/complex/media.ts","../../../../packages/cms-schema/src/validation/image.ts","../../../../packages/cms-schema/src/utils/safe-regex.ts","../../../../packages/cms-schema/src/documents/schema-hash.ts","../../../../packages/cms-schema/src/documents/validations/country.ts","../../lib/cms-api.ts"],"sourcesContent":["\"use strict\";\nObject.defineProperty(exports, \"__esModule\", { value: true });\n//# sourceMappingURL=tokens.js.map",null,"\"use strict\";\nObject.defineProperty(exports, \"__esModule\", { value: true });\n//# sourceMappingURL=set-lookup.js.map",null,null,null,null,null,null,null,null,"'use strict'\n\nconst parse = require('ret')\nconst types = parse.types\n\nfunction safeRegex (re, opts) {\n if (!opts) opts = {}\n /* c8 ignore next */\n const replimit = opts.limit === undefined ? 25 : opts.limit\n\n /* c8 ignore next 2 */\n if (isRegExp(re)) re = re.source\n else if (typeof re !== 'string') re = String(re)\n\n try { re = parse(re) } catch { return false }\n\n let reps = 0\n return (function walk (node, starHeight) {\n let i\n let ok\n let len\n\n if (node.type === types.REPETITION) {\n starHeight++\n reps++\n if (starHeight > 1) return false\n if (reps > replimit) return false\n }\n\n if (node.options) {\n for (i = 0, len = node.options.length; i < len; i++) {\n ok = walk({ stack: node.options[i] }, starHeight)\n if (!ok) return false\n }\n }\n const stack = node.stack || node.value?.stack\n if (!stack) return true\n\n for (i = 0; i < stack.length; i++) {\n ok = walk(stack[i], starHeight)\n if (!ok) return false\n }\n\n return true\n })(re, 0)\n}\n\nfunction isRegExp (x) {\n return {}.toString.call(x) === '[object RegExp]'\n}\n\nmodule.exports = safeRegex\nmodule.exports.default = safeRegex\nmodule.exports.safeRegex = safeRegex\n","/**\n * Custom Schema Fetcher\n *\n * Fetches custom schema definitions (field metadata as JSON) from the CMS API\n * for a given website. Uses the tRPC client to call the customSchema router.\n */\n\nimport { mkdir, writeFile } from 'node:fs/promises';\nimport { dirname } from 'node:path';\nimport type { FieldDefinition } from '@repo/cms-schema/documents';\nimport {\n generateCombinedZodSchemaCode,\n generateZodSchemaCode,\n rehydrateSchema,\n} from '@repo/cms-schema/documents';\nimport type { ZodObject, ZodRawShape } from 'zod';\nimport { type CmsConfig, getCmsClient } from './cms-api';\n\nexport interface CustomSchemaFields {\n name: string;\n displayName: string;\n description: string | null;\n fields: FieldDefinition[];\n routeSlugField: string | null;\n version: number;\n}\n\nexport type FetchCustomSchemasOptions = CmsConfig & { websiteId: string };\n\n/**\n * Fetches all custom schemas with their field definitions for a website.\n *\n * @example\n * ```ts\n * const schemas = await fetchAllCustomSchemaFields({\n * cmsUrl: 'http://localhost:3000',\n * apiKey: 'my-api-key',\n * websiteId: 'uuid-here',\n * });\n *\n * for (const schema of schemas) {\n * console.log(schema.name, schema.fields);\n * }\n * ```\n */\nexport async function fetchAllCustomSchemaFields(\n options: FetchCustomSchemasOptions\n): Promise<CustomSchemaFields[]> {\n const { websiteId } = options;\n\n const client = getCmsClient(options);\n\n // Get the list of all custom schemas (summaries only)\n const summaries = await client.customSchema.list.query({\n websiteId,\n includeSystem: false,\n });\n\n // Fetch full field definitions for each schema in parallel\n const schemas = await Promise.all(\n summaries.map(async (summary) => {\n const detail = await client.customSchema.getByName.query({\n websiteId,\n name: summary.name,\n });\n\n return {\n name: detail.name,\n displayName: detail.displayName,\n description: detail.description ?? null,\n fields: detail.fields,\n routeSlugField: detail.routeSlugField ?? null,\n version: detail.version,\n } satisfies CustomSchemaFields;\n })\n );\n\n return schemas;\n}\n\n/**\n * Fetches a single custom schema's field definitions by name.\n *\n * @example\n * ```ts\n * const schema = await fetchCustomSchemaFields({\n * cmsUrl: 'http://localhost:3000',\n * apiKey: 'my-api-key',\n * websiteId: 'uuid-here',\n * }, 'blog_post');\n *\n * console.log(schema.fields);\n * ```\n */\nexport async function fetchCustomSchemaFields(\n options: FetchCustomSchemasOptions,\n schemaName: string\n): Promise<CustomSchemaFields> {\n const { websiteId } = options;\n const client = getCmsClient(options);\n\n const detail = await client.customSchema.getByName.query({\n websiteId,\n name: schemaName,\n });\n\n return {\n name: detail.name,\n displayName: detail.displayName,\n description: detail.description ?? null,\n fields: detail.fields,\n routeSlugField: detail.routeSlugField ?? null,\n version: detail.version,\n };\n}\n\n/**\n * Converts an array of custom schema definitions into a record of Zod schemas,\n * keyed by schema name.\n *\n * @example\n * ```ts\n * const schemas = await fetchAllCustomSchemaFields(options);\n * const zodSchemas = buildZodSchemas(schemas);\n *\n * // Validate a blog post document\n * const result = zodSchemas['blog_post'].safeParse(data);\n * ```\n */\nexport function buildZodSchemas(\n schemas: CustomSchemaFields[]\n): Record<string, ZodObject<ZodRawShape>> {\n // biome-ignore lint/suspicious/noExplicitAny: avoids excessively deep type instantiation from Zod generics\n const result: Record<string, any> = {};\n\n for (const schema of schemas) {\n result[schema.name] = rehydrateSchema(schema.fields);\n }\n\n return result;\n}\n\n/**\n * Converts an array of custom schema definitions into a record of Zod source\n * code strings, keyed by schema name.\n *\n * @example\n * ```ts\n * const schemas = await fetchAllCustomSchemaFields(options);\n * const codeMap = buildZodSchemaCode(schemas);\n *\n * // Write blog_post schema to a file\n * fs.writeFileSync('blog-post.ts', codeMap['blog_post']);\n * ```\n */\nexport function buildZodSchemaCode(schemas: CustomSchemaFields[]): Record<string, string> {\n const result: Record<string, string> = {};\n\n for (const schema of schemas) {\n result[schema.name] = generateZodSchemaCode(schema.name, schema.fields);\n }\n\n return result;\n}\n\n/**\n * Generates combined Zod schema code from all custom schemas and writes it\n * to the specified file path.\n *\n * @param schemas - Array of custom schema definitions\n * @param filePath - Absolute or relative path to write the generated file\n */\nexport async function saveZodSchemaCode(\n schemas: CustomSchemaFields[],\n filePath: string\n): Promise<void> {\n if (schemas.length === 0) {\n console.warn('[saveZodSchemaCode] No schemas provided — skipping file write.');\n return;\n }\n\n const code = generateCombinedZodSchemaCode(schemas);\n\n await mkdir(dirname(filePath), { recursive: true });\n await writeFile(filePath, code, 'utf-8');\n}\n","/**\n * Zod Schema Code Generator\n *\n * Generates valid JavaScript/TypeScript Zod schema source code\n * from FieldDefinition[] (the same structure used by rehydration.ts at runtime).\n */\n\nimport type { FieldConstraints, FieldDefinition } from './schema-types';\n\n/**\n * Generates a complete Zod schema source code string from field definitions.\n *\n * @param schemaName - The schema name (used for the variable name)\n * @param fields - Array of field definitions\n * @returns Formatted Zod source code string\n */\nexport function generateZodSchemaCode(schemaName: string, fields: FieldDefinition[]): string {\n const lines: string[] = [];\n\n lines.push(`import { z } from 'zod';`);\n lines.push('');\n\n // Generate the ImageReference schema inline if any image fields exist\n const hasImageField = fields.some((f) => f.type === 'image');\n if (hasImageField) {\n appendImageReferenceSchema(lines);\n lines.push('');\n }\n\n appendSchemaBody(lines, schemaName, fields);\n\n return lines.join('\\n');\n}\n\n/**\n * Generates a single combined Zod schema source code file from multiple schema definitions.\n *\n * Produces one `import { z }`, one `ImageReferenceSchema` (if needed), then all schema exports.\n *\n * @param schemas - Array of schema definitions with name and fields\n * @returns Formatted Zod source code string for the combined file\n */\nexport function generateCombinedZodSchemaCode(\n schemas: Array<{ name: string; fields: FieldDefinition[] }>\n): string {\n const lines: string[] = [];\n\n // Auto-generated file header with linter/formatter ignore directives\n lines.push('/* eslint-disable */');\n lines.push('/* prettier-ignore */');\n lines.push('/* biome-ignore format: auto-generated */');\n lines.push('// This file is auto-generated by cms-renderer. Do not edit manually.');\n lines.push('');\n lines.push(`import { z } from 'zod';`);\n lines.push('');\n\n // Emit ImageReferenceSchema once if any schema uses image fields\n const hasImageField = schemas.some((s) => s.fields.some((f) => f.type === 'image'));\n if (hasImageField) {\n appendImageReferenceSchema(lines);\n lines.push('');\n }\n\n for (const [i, schema] of schemas.entries()) {\n if (i > 0) lines.push('');\n appendSchemaBody(lines, schema.name, schema.fields);\n }\n\n return lines.join('\\n');\n}\n\n/** Appends the z.object and type export lines for a single schema. */\nfunction appendSchemaBody(lines: string[], schemaName: string, fields: FieldDefinition[]): void {\n const varName = `${toCamelCase(schemaName)}Schema`;\n\n lines.push(`export const ${varName} = z.object({`);\n\n for (const field of fields) {\n const fieldCode = generateFieldCode(field);\n if (field.description) {\n lines.push(` /** ${field.description} */`);\n }\n lines.push(` ${field.name}: ${fieldCode},`);\n }\n\n lines.push('});');\n lines.push('');\n lines.push(`export type ${toPascalCase(schemaName)} = z.infer<typeof ${varName}>;`);\n lines.push('');\n}\n\n/** Appends the ImageReferenceSchema constant. */\nfunction appendImageReferenceSchema(lines: string[]): void {\n lines.push('const ImageReferenceSchema = z.object({');\n lines.push(\" alt: z.string().min(1, 'Alt text is required for accessibility'),\");\n lines.push(' caption: z.string().max(500).optional(),');\n lines.push(' attribution: z.string().max(255).optional(),');\n lines.push(' _asset: z.object({');\n lines.push(' id: z.string().uuid(),');\n lines.push(' }),');\n lines.push('});');\n}\n\nfunction generateFieldCode(field: FieldDefinition): string {\n let code = generateBaseTypeCode(field.type, field.constraints);\n\n if (!field.required) {\n code += '.optional()';\n }\n\n return code;\n}\n\nfunction generateBaseTypeCode(\n type: FieldDefinition['type'],\n constraints?: FieldConstraints\n): string {\n switch (type) {\n case 'string':\n return applyStringConstraintCode('z.string()', constraints);\n\n case 'number':\n return applyNumberConstraintCode('z.number()', constraints);\n\n case 'boolean':\n return 'z.boolean()';\n\n case 'image':\n return 'ImageReferenceSchema';\n\n case 'date':\n return \"z.string().date('Invalid date format. Expected YYYY-MM-DD')\";\n\n case 'datetime':\n return \"z.string().datetime({ offset: true, message: 'Invalid datetime format. Expected ISO 8601' })\";\n\n case 'url':\n return applyStringConstraintCode(\"z.string().url('Invalid URL format')\", constraints);\n\n case 'email':\n return applyStringConstraintCode(\"z.string().email('Invalid email format')\", constraints);\n\n case 'enum': {\n const values = constraints?.enumValues;\n if (!values || values.length === 0) {\n return \"z.enum([''])\";\n }\n const formatted = values.map((v) => `'${escapeString(v)}'`).join(', ');\n return `z.enum([${formatted}])`;\n }\n\n case 'reference':\n return 'z.record(z.string(), z.unknown())';\n\n case 'array': {\n const itemType = constraints?.arrayItemType ?? 'string';\n const itemCode = generatePrimitiveCode(itemType);\n return applyArrayConstraintCode(`z.array(${itemCode})`, constraints);\n }\n\n default:\n return 'z.unknown()';\n }\n}\n\nfunction generatePrimitiveCode(type: 'string' | 'number' | 'boolean'): string {\n switch (type) {\n case 'string':\n return 'z.string()';\n case 'number':\n return 'z.number()';\n case 'boolean':\n return 'z.boolean()';\n }\n}\n\nfunction applyStringConstraintCode(base: string, constraints?: FieldConstraints): string {\n if (!constraints) return base;\n\n let code = base;\n if (constraints.minLength !== undefined) {\n code += `.min(${constraints.minLength}, 'Must be at least ${constraints.minLength} characters')`;\n }\n if (constraints.maxLength !== undefined) {\n code += `.max(${constraints.maxLength}, 'Must be at most ${constraints.maxLength} characters')`;\n }\n if (constraints.pattern) {\n code += `.regex(/${escapeRegex(constraints.pattern)}/, 'Invalid format')`;\n }\n return code;\n}\n\nfunction applyNumberConstraintCode(base: string, constraints?: FieldConstraints): string {\n if (!constraints) return base;\n\n let code = base;\n if (constraints.integer) {\n code += `.int('Must be an integer')`;\n }\n if (constraints.min !== undefined) {\n code += `.min(${constraints.min}, 'Must be at least ${constraints.min}')`;\n }\n if (constraints.max !== undefined) {\n code += `.max(${constraints.max}, 'Must be at most ${constraints.max}')`;\n }\n return code;\n}\n\nfunction applyArrayConstraintCode(base: string, constraints?: FieldConstraints): string {\n if (!constraints) return base;\n\n let code = base;\n if (constraints.minItems !== undefined) {\n code += `.min(${constraints.minItems}, 'Must have at least ${constraints.minItems} items')`;\n }\n if (constraints.maxItems !== undefined) {\n code += `.max(${constraints.maxItems}, 'Must have at most ${constraints.maxItems} items')`;\n }\n return code;\n}\n\nfunction escapeString(str: string): string {\n return str.replace(/\\\\/g, '\\\\\\\\').replace(/'/g, \"\\\\'\");\n}\n\nfunction escapeRegex(pattern: string): string {\n return pattern.replace(/\\//g, '\\\\/');\n}\n\nfunction toCamelCase(str: string): string {\n return str.replace(/_([a-z])/g, (_, c) => c.toUpperCase());\n}\n\nfunction toPascalCase(str: string): string {\n const camel = toCamelCase(str);\n return camel.charAt(0).toUpperCase() + camel.slice(1);\n}\n","/**\n * Document Schema Registry.\n *\n * Centralizes registration for all document schemas.\n * The CMS reads this registry to generate UI and validation.\n */\n\nimport { type ZodObject, type ZodRawShape, z } from 'zod';\nimport type { DocumentError } from '../db/document-types';\nimport { DocumentErrors } from '../db/document-types';\nimport type { TypedSupabaseClient } from '../db/types';\nimport type { Result } from '../types/errors';\nimport { err, ok } from '../types/errors';\n\n// =============================================================================\n// Validation Schemas\n// =============================================================================\n\n/**\n * Schema name validation - must be a non-empty string.\n */\nconst schemaNameValidator = z\n .string()\n .min(1, 'Schema name must be a non-empty string')\n .refine((name) => name.trim().length > 0, 'Schema name must not be only whitespace');\n\n/**\n * Runtime validator for document schema config.\n * Validates the config object structure at runtime.\n */\nconst documentSchemaConfigValidator = z.object({\n schema: z.custom<ZodObject<ZodRawShape>>(\n (val) => val !== null && typeof val === 'object' && '_def' in val,\n { message: 'schema must be a valid Zod schema' }\n ),\n titleField: z.string().optional(),\n displayName: z.string().optional(),\n description: z.string().optional(),\n validateContent: z.function().optional(),\n});\n\n// =============================================================================\n// Types\n// =============================================================================\n\n/**\n * Configuration for a registered document schema.\n */\nexport interface DocumentSchemaConfig<T extends ZodRawShape = ZodRawShape> {\n /** Zod schema for content validation */\n schema: ZodObject<T>;\n /** Field to extract for title (default: tries 'name', 'title', 'code') */\n titleField?: string;\n /** Display name for the schema in the UI */\n displayName?: string;\n /** Description of the document type */\n description?: string;\n /** Optional custom validation function for schema-specific rules */\n validateContent?: (content: unknown, supabase: TypedSupabaseClient) => Promise<void>;\n}\n\n/**\n * Registered schema with computed metadata.\n */\nexport interface RegisteredSchema<T extends ZodRawShape = ZodRawShape>\n extends DocumentSchemaConfig<T> {\n /** Schema name (registry key) */\n name: string;\n}\n\n// =============================================================================\n// Registry\n// =============================================================================\n\nconst documentRegistry = new Map<string, DocumentSchemaConfig>();\n\n/**\n * Register a document schema.\n *\n * @example\n * registerDocumentSchema('country', {\n * schema: CountrySchema,\n * titleField: 'name',\n * displayName: 'Country',\n * });\n *\n * @throws {Error} If schema name is empty or already registered\n */\nexport function registerDocumentSchema<T extends ZodRawShape>(\n name: string,\n config: DocumentSchemaConfig<T>\n): void {\n // Validate schema name using Zod\n const nameResult = schemaNameValidator.safeParse(name);\n if (!nameResult.success) {\n throw new Error(nameResult.error.issues.at(0)?.message ?? 'Invalid schema name');\n }\n\n // Validate config structure using Zod\n const configResult = documentSchemaConfigValidator.safeParse(config);\n if (!configResult.success) {\n throw new Error(\n `Invalid schema config: ${configResult.error.issues.at(0)?.message ?? 'Unknown error'}`\n );\n }\n\n // Check for duplicate registration\n if (documentRegistry.has(name)) {\n throw new Error(`Document schema \"${name}\" is already registered`);\n }\n\n documentRegistry.set(name, config as DocumentSchemaConfig);\n}\n\n/**\n * Get a document schema configuration by name.\n */\nexport function getDocumentSchema(name: string): DocumentSchemaConfig | undefined {\n return documentRegistry.get(name);\n}\n\n/**\n * Get all registered schema names.\n */\nexport function getRegisteredDocumentSchemas(): string[] {\n return Array.from(documentRegistry.keys());\n}\n\n/**\n * Get all registered schemas with their configurations.\n */\nexport function getAllDocumentSchemas(): RegisteredSchema[] {\n return Array.from(documentRegistry.entries()).map(([name, config]) => ({\n name,\n ...config,\n }));\n}\n\n/**\n * Clear the document registry (for testing).\n */\nexport function clearDocumentRegistry(): void {\n documentRegistry.clear();\n}\n\n// =============================================================================\n// Validation\n// =============================================================================\n\n/**\n * Validate document content against a registered schema.\n *\n * @returns Result with validated data or error\n */\nexport function validateDocumentContent<T>(\n schemaName: string,\n content: unknown\n): Result<T, DocumentError> {\n const config = documentRegistry.get(schemaName);\n\n if (!config) {\n return err(DocumentErrors.schemaNotFound(schemaName));\n }\n\n const result = config.schema.safeParse(content);\n\n if (!result.success) {\n const issues = result.error.issues.map((issue) => ({\n path: issue.path.filter((p): p is string | number => typeof p !== 'symbol'),\n message: issue.message,\n }));\n return err(DocumentErrors.validationError(issues));\n }\n\n return ok(result.data as T);\n}\n\n/**\n * Detailed validation result for UI display.\n */\nexport interface DocumentValidationResult<T> {\n valid: boolean;\n data?: T;\n issues?: Array<{ path: (string | number)[]; message: string }>;\n}\n\n/**\n * Validate document content with detailed error info for UI.\n */\nexport function validateDocumentContentDetailed<T>(\n schemaName: string,\n content: unknown\n): DocumentValidationResult<T> {\n const config = documentRegistry.get(schemaName);\n\n if (!config) {\n return {\n valid: false,\n issues: [{ path: [], message: `Document schema not found: ${schemaName}` }],\n };\n }\n\n const result = config.schema.safeParse(content);\n\n if (!result.success) {\n return {\n valid: false,\n issues: result.error.issues.map((issue) => ({\n path: issue.path.filter((p): p is string | number => typeof p !== 'symbol'),\n message: issue.message,\n })),\n };\n }\n\n return { valid: true, data: result.data as T };\n}\n\n// =============================================================================\n// Title Extraction\n// =============================================================================\n\n/**\n * Extract title from document content based on schema configuration.\n *\n * Tries in order:\n * 1. Schema's configured titleField\n * 2. 'title' field\n * 3. 'name' field\n * 4. 'code' field\n * 5. 'Untitled'\n */\nexport function extractDocumentTitle(schemaName: string, content: Record<string, unknown>): string {\n const config = documentRegistry.get(schemaName);\n\n // Try configured title field first\n if (config?.titleField && content[config.titleField] !== undefined) {\n return String(content[config.titleField]);\n }\n\n // Fall back to common title fields\n if (content.title !== undefined) return String(content.title);\n if (content.name !== undefined) return String(content.name);\n if (content.code !== undefined) return String(content.code);\n\n return 'Untitled';\n}\n\n// =============================================================================\n// Schema Introspection\n// =============================================================================\n\n/**\n * Get field information from a registered schema for dynamic form generation.\n */\nexport interface SchemaFieldInfo {\n name: string;\n type: string;\n isOptional: boolean;\n required: boolean;\n isArray: boolean;\n description?: string;\n /** For enum types, the available values */\n enumValues?: string[];\n /** For array types, the item type */\n itemType?: string;\n /** For reference types, the referenced schema */\n refSchema?: string;\n /** Default value if the field has one */\n defaultValue?: unknown;\n /** Whether this field has a default value */\n hasDefault?: boolean;\n}\n\n/**\n * Extract field info from a document schema for UI generation.\n */\nexport function getSchemaFields(schemaName: string): SchemaFieldInfo[] {\n const config = documentRegistry.get(schemaName);\n if (!config) return [];\n\n const shape = config.schema.shape;\n const fields: SchemaFieldInfo[] = [];\n\n for (const [name, zodType] of Object.entries(shape)) {\n const typeDef = zodType as z.ZodTypeAny;\n const isOptional = typeDef.isOptional();\n const fieldType = getFieldType(typeDef);\n const isArray = isArrayType(typeDef);\n const { value: defaultValue, hasDefault } = getDefaultValue(typeDef);\n\n const fieldInfo: SchemaFieldInfo = {\n name,\n type: fieldType,\n isOptional,\n required: !isOptional && !hasDefault, // Field is not required if it has a default\n isArray,\n description: typeDef.description,\n defaultValue,\n hasDefault,\n };\n\n // Extract enum values if this is an enum type\n // Also check if we can extract enum values even if type wasn't detected as enum\n const enumValues = getEnumValues(typeDef);\n if (enumValues.length > 0) {\n fieldInfo.enumValues = enumValues;\n // If we found enum values but type wasn't detected as enum, fix the type\n if (fieldType !== 'enum') {\n fieldInfo.type = 'enum';\n }\n }\n\n // Extract array item info\n if (isArray) {\n const arrayInfo = getArrayItemInfo(typeDef);\n fieldInfo.itemType = arrayInfo.itemType;\n fieldInfo.refSchema = arrayInfo.refSchema;\n }\n\n fields.push(fieldInfo);\n }\n\n return fields;\n}\n\nfunction getFieldType(zodType: z.ZodTypeAny): string {\n const def = zodType._def;\n if (!def) return 'unknown';\n\n // Unwrap default to get the inner type\n // ZodDefault wraps the inner type, so we need to unwrap it first\n if ('typeName' in def && def.typeName === 'ZodDefault') {\n // ZodDefault has an innerType property\n if ('innerType' in def) {\n return getFieldType(def.innerType as z.ZodTypeAny);\n }\n // Fallback: try to access _def.innerType directly\n const innerDef = (def as unknown as { innerType?: { _def?: unknown } }).innerType;\n if (innerDef?._def) {\n return getFieldType(innerDef as z.ZodTypeAny);\n }\n }\n\n // Unwrap optional/nullable (ZodOptional, ZodNullable)\n if ('innerType' in def) {\n return getFieldType(def.innerType as z.ZodTypeAny);\n }\n\n // Handle common types\n if ('typeName' in def) {\n const typeName = def.typeName as string;\n switch (typeName) {\n case 'ZodString':\n return 'string';\n case 'ZodNumber':\n return 'number';\n case 'ZodBoolean':\n return 'boolean';\n case 'ZodArray':\n return 'array';\n case 'ZodObject':\n return 'object';\n case 'ZodEnum':\n return 'enum';\n default:\n return typeName.replace('Zod', '').toLowerCase();\n }\n }\n\n return 'unknown';\n}\n\nfunction isArrayType(zodType: z.ZodTypeAny): boolean {\n const def = zodType._def;\n if (!def) return false;\n\n // Unwrap default to get the inner type\n if ('typeName' in def && def.typeName === 'ZodDefault' && 'innerType' in def) {\n return isArrayType(def.innerType as z.ZodTypeAny);\n }\n\n // Check for array type\n if ('typeName' in def && def.typeName === 'ZodArray') {\n return true;\n }\n\n // Check inside optional/nullable\n if ('innerType' in def) {\n return isArrayType(def.innerType as z.ZodTypeAny);\n }\n\n return false;\n}\n\n/**\n * Extract default value from a Zod type.\n */\nfunction getDefaultValue(zodType: z.ZodTypeAny): { value?: unknown; hasDefault: boolean } {\n const def = zodType._def;\n if (!def) return { hasDefault: false };\n\n // Check for default value in ZodDefault\n if ('typeName' in def && def.typeName === 'ZodDefault') {\n if ('defaultValue' in def) {\n const defaultValue = def.defaultValue;\n // Default value can be a function or a value\n if (typeof defaultValue === 'function') {\n try {\n return { value: defaultValue(), hasDefault: true };\n } catch {\n return { hasDefault: false };\n }\n }\n return { value: defaultValue, hasDefault: true };\n }\n }\n\n // Unwrap optional/nullable to check inner type\n if ('innerType' in def) {\n return getDefaultValue(def.innerType as z.ZodTypeAny);\n }\n\n return { hasDefault: false };\n}\n\n/**\n * Extract enum values from a Zod enum type.\n */\nfunction getEnumValues(zodType: z.ZodTypeAny): string[] {\n const def = zodType._def;\n if (!def) return [];\n\n // Unwrap default to get the inner type\n if ('typeName' in def && def.typeName === 'ZodDefault') {\n // Try to access innerType\n if ('innerType' in def) {\n return getEnumValues(def.innerType as z.ZodTypeAny);\n }\n // Fallback: try to access _def.innerType directly\n const innerDef = (def as unknown as { innerType?: z.ZodTypeAny }).innerType;\n if (innerDef) {\n return getEnumValues(innerDef);\n }\n }\n\n // Unwrap optional/nullable\n if ('innerType' in def) {\n return getEnumValues(def.innerType as z.ZodTypeAny);\n }\n\n // Get enum values from ZodEnum\n if ('typeName' in def && def.typeName === 'ZodEnum') {\n if ('values' in def && Array.isArray(def.values)) {\n return def.values as string[];\n }\n }\n\n // Also check for values property directly (some Zod versions)\n if ('values' in def && Array.isArray(def.values)) {\n return def.values as string[];\n }\n\n return [];\n}\n\n/**\n * Extract array item type information.\n */\nfunction getArrayItemInfo(zodType: z.ZodTypeAny): { itemType: string; refSchema?: string } {\n const def = zodType._def;\n if (!def) return { itemType: 'unknown' };\n\n // Unwrap optional/nullable\n if ('innerType' in def) {\n return getArrayItemInfo(def.innerType as z.ZodTypeAny);\n }\n\n // Get array element type\n if ('type' in def) {\n const itemType = def.type as unknown as z.ZodTypeAny;\n const itemDef = itemType._def as unknown as Record<string, unknown>;\n\n // Check if it's a reference object (has _type, _ref, _schema)\n if (itemDef && 'typeName' in itemDef && itemDef.typeName === 'ZodObject') {\n if ('shape' in itemDef && typeof itemDef.shape === 'function') {\n const shape = (itemDef.shape as () => Record<string, z.ZodTypeAny>)();\n if ('_type' in shape && '_ref' in shape && '_schema' in shape) {\n // This is a reference type - try to get the schema from literal\n const schemaField = shape._schema;\n const schemaDef = schemaField?._def as unknown as Record<string, unknown> | undefined;\n if (schemaDef && 'value' in schemaDef) {\n return { itemType: 'reference', refSchema: schemaDef.value as string };\n }\n return { itemType: 'reference' };\n }\n }\n }\n\n return { itemType: getFieldType(itemType) };\n }\n\n return { itemType: 'unknown' };\n}\n","/**\n * Country Document Schema.\n *\n * Defines the structure for country documents.\n * Countries have a list of supported language codes.\n */\n\nimport { z } from 'zod';\n\n// Reference to a language document (backward pass for now, I will remove this when we figure out how to handle languages)\nconst LanguageReferenceSchema = z.object({\n _type: z.literal('reference'),\n _ref: z.string().uuid('Language reference must be a valid UUID'),\n _schema: z.literal('language'),\n});\n\n/**\n * Country schema with validation rules.\n *\n * @example\n * {\n * code: 'us',\n * name: 'United States',\n * flag: '🇺🇸',\n * languages: ['en', 'es']\n * }\n *\n * @example\n * {\n * code: 'sa',\n * name: 'Saudi Arabia',\n * flag: '🇸🇦',\n * languages: ['ar', 'en']\n * }\n */\nexport const CountrySchema = z.object({\n /** 2-letter ISO 3166-1 alpha-2 country code */\n code: z.string().length(2, 'Country code must be 2 characters'),\n\n /** English name of the country */\n name: z.string().min(1, 'Country name required'),\n\n /** Flag emoji (optional but recommended) */\n flag: z.string().optional(),\n\n /**\n * List of supported languages (at least one required).\n * Supports either:\n * - Language codes (new format)\n * - Document references to languages (legacy format for backward compatibility)\n */\n languages: z\n .array(\n z.union([z.string().length(2, 'Language code must be 2 characters'), LanguageReferenceSchema])\n )\n .min(1, 'At least one language required'),\n});\n\n/** TypeScript type inferred from schema */\nexport type Country = z.infer<typeof CountrySchema>;\n\n/** Schema name constant */\nexport const COUNTRY_SCHEMA_NAME = 'country';\n\n/** Display name for UI */\nexport const COUNTRY_DISPLAY_NAME = 'Country';\n\n/** Title field for list display */\nexport const COUNTRY_TITLE_FIELD = 'name';\n\n/**\n * Create a default country object.\n */\nexport function createDefaultCountry(): Country {\n return {\n code: '',\n name: '',\n flag: '',\n languages: [],\n };\n}\n","/**\n * Language Document Schema.\n *\n * Defines the structure for language documents.\n */\n\nimport { z } from 'zod';\n\n/**\n * Language schema with validation rules.\n *\n * @example\n * {\n * code: 'en',\n * name: 'English',\n * nativeName: 'English'\n * }\n *\n * @example\n * {\n * code: 'ar',\n * name: 'Arabic',\n * nativeName: 'العربية'\n * }\n */\nexport const LanguageSchema = z.object({\n /** 2-letter ISO 639-1 language code */\n code: z.string().length(2, 'Language code must be 2 characters'),\n\n /** English name of the language */\n name: z.string().min(1, 'Language name required'),\n\n /** Name in the language itself (optional but recommended) */\n nativeName: z.string().optional(),\n});\n\n/** TypeScript type inferred from schema */\nexport type Language = z.infer<typeof LanguageSchema>;\n\n/** Schema name constant */\nexport const LANGUAGE_SCHEMA_NAME = 'language';\n\n/** Display name for UI */\nexport const LANGUAGE_DISPLAY_NAME = 'Language';\n\n/** Title field for list display */\nexport const LANGUAGE_TITLE_FIELD = 'name';\n\n/**\n * Create a default language object.\n */\nexport function createDefaultLanguage(): Language {\n return {\n code: '',\n name: '',\n nativeName: '',\n };\n}\n","/**\n * Unified Schema Registry\n *\n * Merges system schemas (hardcoded in TypeScript) with custom schemas (stored as JSON in database).\n * Both types of schemas are served through the same API, making them indistinguishable to consumers.\n *\n * System schemas take precedence over custom schemas with the same name.\n * Custom schemas are cached with a TTL and can be invalidated.\n */\n\nimport { type ZodObject, type ZodRawShape, z } from 'zod';\nimport type { TypedSupabaseClient } from '../db/types';\nimport { rehydrateSchema } from './rehydration';\nimport { createSchemaHash } from './schema-hash';\nimport type { FieldDefinition } from './schema-types';\n\n/** Source of a schema definition */\nexport type SchemaSource = 'system' | 'custom';\n\ntype TitleCandidateFieldType = FieldDefinition['type'];\n\nconst titleCandidateFieldTypes = new Set<TitleCandidateFieldType>([\n 'string',\n 'enum',\n 'url',\n 'email',\n 'date',\n 'datetime',\n]);\n\nfunction inferTitleFieldFromZodSchema(schema: ZodObject<ZodRawShape>): string {\n const shape = schema.shape ?? {};\n const keys = Object.keys(shape);\n if (keys.includes('title')) return 'title';\n if (keys.includes('name')) return 'name';\n if (keys.includes('code')) return 'code';\n return keys[0] ?? 'title';\n}\n\nfunction resolveTitleFieldFromFields(options: {\n fields: FieldDefinition[];\n titleField?: string | null;\n routeSlugField?: string | null;\n}): string | undefined {\n const { fields, titleField, routeSlugField } = options;\n\n if (titleField) {\n return titleField;\n }\n\n const fieldMap = new Map(fields.map((f) => [f.name, f] as const));\n\n if (routeSlugField) {\n const slugField = fieldMap.get(routeSlugField);\n if (slugField && titleCandidateFieldTypes.has(slugField.type)) {\n return routeSlugField;\n }\n }\n\n for (const preferredName of ['title', 'name', 'code'] as const) {\n const preferred = fieldMap.get(preferredName);\n if (preferred && titleCandidateFieldTypes.has(preferred.type)) {\n return preferredName;\n }\n }\n\n const firstCandidate = fields.find((f) => titleCandidateFieldTypes.has(f.type));\n return firstCandidate?.name;\n}\n\n/** Unified schema configuration that works for both system and custom schemas */\nexport interface UnifiedSchemaConfig {\n /** Database ID (only present for custom schemas) */\n id?: string;\n name: string;\n schema: ZodObject<ZodRawShape>;\n displayName: string;\n description?: string;\n titleField: string;\n /** Field name to use as URL routing slug. Value is auto-copied to content.code on document save. */\n routeSlugField?: string;\n icon?: string;\n source: SchemaSource;\n version?: number;\n\n /** Optional custom validation (system schemas only) */\n validateContent?: (content: unknown, supabase: TypedSupabaseClient) => Promise<void>;\n}\n\n/** Summary info for listing schemas (without full Zod schema) */\nexport interface SchemaSummary {\n /** Database ID (only present for custom schemas) */\n id?: string;\n name: string;\n displayName: string;\n description?: string;\n icon?: string;\n source: SchemaSource;\n version?: number;\n /** Field name used for URL routing - if not set, URL routing may not work */\n routeSlugField?: string;\n}\n\n// In-memory storage\nconst systemSchemas = new Map<string, UnifiedSchemaConfig>();\nconst customSchemaCacheByWebsite = new Map<string, Map<string, UnifiedSchemaConfig>>();\nconst customSchemasCacheTimeByWebsite = new Map<string, number>();\n\nfunction cacheKeyForWebsite(websiteId?: string): string {\n return websiteId ?? '__all__';\n}\n\n/** Cache TTL in milliseconds (30 seconds) */\nconst CACHE_TTL_MS = 30_000;\n\n/**\n * Registers a system schema. Called at application startup.\n * System schemas cannot be overwritten and take precedence over custom schemas.\n *\n * @param name - Unique schema identifier (e.g., 'country', 'language')\n * @param config - Schema configuration without name and source\n * @throws If schema with name already registered\n */\nexport function registerSystemSchema(\n name: string,\n config: Omit<UnifiedSchemaConfig, 'name' | 'source' | 'titleField'> & { titleField?: string }\n): void {\n if (systemSchemas.has(name)) {\n throw new Error(`System schema \"${name}\" is already registered`);\n }\n\n systemSchemas.set(name, {\n ...config,\n name,\n titleField: config.titleField ?? inferTitleFieldFromZodSchema(config.schema),\n source: 'system',\n });\n}\n\n/**\n * Gets a schema by name (system or custom).\n * System schemas are checked first, then custom schemas are loaded from cache/database.\n *\n * @param name - Schema name to look up\n * @param supabase - Database client for loading custom schemas\n * @returns Schema configuration or null if not found\n */\nexport async function getSchema(\n name: string,\n supabase: TypedSupabaseClient,\n websiteId?: string\n): Promise<UnifiedSchemaConfig | null> {\n // System schemas take precedence\n const systemSchema = systemSchemas.get(name);\n if (systemSchema) {\n return systemSchema;\n }\n\n // Load/refresh custom schemas if needed\n await loadCustomSchemas(supabase, websiteId);\n const scopedCache = customSchemaCacheByWebsite.get(cacheKeyForWebsite(websiteId));\n return scopedCache?.get(name) ?? null;\n}\n\n/**\n * Gets all registered schemas (system + active custom).\n *\n * @param supabase - Database client for loading custom schemas\n * @returns Array of all available schema configurations\n */\nexport async function getAllSchemas(\n supabase: TypedSupabaseClient,\n websiteId?: string\n): Promise<UnifiedSchemaConfig[]> {\n await loadCustomSchemas(supabase, websiteId);\n\n const scopedCache = customSchemaCacheByWebsite.get(cacheKeyForWebsite(websiteId));\n return [...systemSchemas.values(), ...(scopedCache?.values() ?? [])];\n}\n\n/**\n * Gets summary info for all schemas (without full Zod schemas).\n * Useful for sidebar/listing where full validation isn't needed.\n */\nexport async function getAllSchemaSummaries(\n supabase: TypedSupabaseClient,\n websiteId?: string\n): Promise<SchemaSummary[]> {\n const schemas = await getAllSchemas(supabase, websiteId);\n\n return schemas.map((s) => ({\n id: s.id,\n name: s.name,\n displayName: s.displayName,\n description: s.description,\n icon: s.icon,\n source: s.source,\n version: s.version,\n routeSlugField: s.routeSlugField,\n }));\n}\n\n/**\n * Loads custom schemas from database into cache.\n * Uses TTL-based caching to avoid excessive database queries.\n *\n * @param supabase - Database client\n * @param force - If true, bypasses cache TTL\n */\nasync function loadCustomSchemas(\n supabase: TypedSupabaseClient,\n websiteId?: string,\n force = false\n): Promise<void> {\n const now = Date.now();\n const key = cacheKeyForWebsite(websiteId);\n const cacheTime = customSchemasCacheTimeByWebsite.get(key) ?? null;\n\n // Use cache if valid and not forced\n if (!force && cacheTime && now - cacheTime < CACHE_TTL_MS) {\n return;\n }\n\n let query = supabase.from('custom_schemas').select('*').eq('state', 'active');\n if (websiteId) {\n query = query.eq('website_id', websiteId);\n }\n const { data, error } = await query;\n\n if (error) {\n console.error('Failed to load custom schemas:', error);\n // Keep stale cache on error rather than clearing\n return;\n }\n\n // Rebuild cache\n const scopedCache = new Map<string, UnifiedSchemaConfig>();\n\n for (const row of data ?? []) {\n try {\n const fields = row.fields as FieldDefinition[];\n const zodSchema = rehydrateSchema(fields);\n const resolvedTitleField = resolveTitleFieldFromFields({\n fields,\n titleField: row.title_field ?? undefined,\n routeSlugField: row.route_slug_field ?? undefined,\n });\n\n const hasExistingTitleField = fields.some((f) => f.name === 'title');\n const schemaWithResolvedTitle =\n resolvedTitleField === undefined && !hasExistingTitleField\n ? zodSchema.extend({ title: z.string().optional() })\n : zodSchema;\n\n const finalTitleField =\n resolvedTitleField ?? (hasExistingTitleField ? 'title' : (fields[0]?.name ?? 'title'));\n\n scopedCache.set(row.name, {\n id: row.id,\n name: row.name,\n schema: schemaWithResolvedTitle,\n displayName: row.display_name,\n description: row.description ?? undefined,\n titleField: finalTitleField,\n routeSlugField: row.route_slug_field ?? undefined,\n icon: row.icon ?? undefined,\n source: 'custom',\n version: row.version,\n });\n } catch (err) {\n // Log but don't fail - skip invalid schemas\n console.error(`Failed to rehydrate schema \"${row.name}\":`, err);\n }\n }\n\n customSchemaCacheByWebsite.set(key, scopedCache);\n customSchemasCacheTimeByWebsite.set(key, now);\n}\n\n/**\n * Invalidates the custom schema cache.\n * Call this after creating, updating, or deleting custom schemas.\n */\nexport function invalidateCustomSchemaCache(websiteId?: string): void {\n if (!websiteId) {\n customSchemasCacheTimeByWebsite.clear();\n customSchemaCacheByWebsite.clear();\n return;\n }\n\n const key = cacheKeyForWebsite(websiteId);\n customSchemasCacheTimeByWebsite.delete(key);\n customSchemaCacheByWebsite.delete(key);\n}\n\n/**\n * Forces a reload of custom schemas from database.\n */\nexport async function refreshCustomSchemas(supabase: TypedSupabaseClient): Promise<void> {\n await loadCustomSchemas(supabase, undefined, true);\n}\n\n/**\n * Checks if a schema name is reserved (used by a system schema).\n */\nexport function isSystemSchema(name: string): boolean {\n return systemSchemas.has(name);\n}\n\n/**\n * Gets all system schema names.\n */\nexport function getSystemSchemaNames(): string[] {\n return [...systemSchemas.keys()];\n}\n\n/**\n * Clears all registrations. FOR TESTING ONLY.\n */\nexport function clearUnifiedRegistry(): void {\n systemSchemas.clear();\n customSchemaCacheByWebsite.clear();\n customSchemasCacheTimeByWebsite.clear();\n}\n\n/**\n * Creates a hash for a custom schema from its fields.\n * Used for tracking schema versions in documents.\n */\nexport function createCustomSchemaHash(fields: FieldDefinition[]): string {\n const zodSchema = rehydrateSchema(fields);\n return createSchemaHash(zodSchema);\n}\n","/**\n * Zod Schema Rehydration\n *\n * Converts JSON field definitions (stored in database) into runtime Zod schemas.\n * This enables custom schemas to be validated identically to system schemas.\n *\n * @example\n * ```typescript\n * const fields: FieldDefinition[] = [\n * { name: 'title', type: 'string', required: true, constraints: { minLength: 1 } },\n * { name: 'price', type: 'number', required: true, constraints: { min: 0 } },\n * ];\n * const schema = rehydrateSchema(fields);\n * // Equivalent to: z.object({ title: z.string().min(1), price: z.number().min(0) })\n * ```\n */\n\nimport { type ZodObject, type ZodRawShape, type ZodTypeAny, z } from 'zod';\nimport { ImageReferenceSchema } from '../fields/complex/media';\nimport { validateSafeRegex } from '../utils/safe-regex';\nimport type { FieldConstraints, FieldDefinition, FieldType } from './schema-types';\n\n/** Error thrown when field definition cannot be rehydrated */\nexport class RehydrationError extends Error {\n constructor(\n message: string,\n public readonly fieldName?: string,\n public readonly fieldType?: FieldType\n ) {\n super(message);\n this.name = 'RehydrationError';\n }\n}\n\n/**\n * Rehydrates a single field definition into a Zod schema\n */\nexport function rehydrateField(field: FieldDefinition): ZodTypeAny {\n try {\n let schema = createBaseSchema(field.type, field.constraints);\n\n if (!field.required) {\n schema = schema.optional();\n }\n\n return schema;\n } catch (err) {\n throw new RehydrationError(\n `Failed to rehydrate field \"${field.name}\": ${err instanceof Error ? err.message : 'Unknown error'}`,\n field.name,\n field.type\n );\n }\n}\n\n/**\n * Creates the base Zod schema for a given field type\n */\nfunction createBaseSchema(type: FieldType, constraints?: FieldConstraints): ZodTypeAny {\n switch (type) {\n case 'string':\n return applyStringConstraints(z.string(), constraints);\n\n case 'number':\n return applyNumberConstraints(z.number(), constraints);\n\n case 'boolean':\n return z.boolean();\n\n case 'image':\n return ImageReferenceSchema;\n\n case 'date':\n return z.string().date('Invalid date format. Expected YYYY-MM-DD');\n\n case 'datetime':\n return z\n .string()\n .datetime({ offset: true, message: 'Invalid datetime format. Expected ISO 8601' });\n\n case 'url':\n return applyStringConstraints(z.string().url('Invalid URL format'), constraints);\n\n case 'email':\n return applyStringConstraints(z.string().email('Invalid email format'), constraints);\n\n case 'enum': {\n const values = constraints?.enumValues;\n if (!values || values.length === 0) {\n throw new Error('Enum field requires at least one enumValues in constraints');\n }\n return z.enum(values as [string, ...string[]]);\n }\n\n case 'reference':\n // Reference fields store inline objects (the referenced schema's field values)\n return z.record(z.string(), z.unknown());\n\n case 'array': {\n const itemType = constraints?.arrayItemType ?? 'string';\n const itemSchema = createPrimitiveSchema(itemType);\n return applyArrayConstraints(z.array(itemSchema), constraints);\n }\n\n default:\n throw new Error(`Unknown field type: ${type}`);\n }\n}\n\n/**\n * Creates a Zod schema for primitive array item types\n */\nfunction createPrimitiveSchema(type: 'string' | 'number' | 'boolean'): ZodTypeAny {\n switch (type) {\n case 'string':\n return z.string();\n case 'number':\n return z.number();\n case 'boolean':\n return z.boolean();\n }\n}\n\n/**\n * Applies string-specific constraints to a Zod string schema\n */\nfunction applyStringConstraints(schema: z.ZodString, constraints?: FieldConstraints): z.ZodString {\n if (!constraints) return schema;\n\n // Validate minLength <= maxLength when both are set\n if (\n constraints.minLength !== undefined &&\n constraints.maxLength !== undefined &&\n constraints.minLength > constraints.maxLength\n ) {\n throw new Error(\n `Invalid string constraints: minLength (${constraints.minLength}) cannot be greater than maxLength (${constraints.maxLength})`\n );\n }\n\n let result = schema;\n\n if (constraints.minLength !== undefined) {\n result = result.min(\n constraints.minLength,\n `Must be at least ${constraints.minLength} characters`\n );\n }\n if (constraints.maxLength !== undefined) {\n result = result.max(\n constraints.maxLength,\n `Must be at most ${constraints.maxLength} characters`\n );\n }\n if (constraints.pattern) {\n // Validate pattern is safe (prevents ReDoS attacks)\n const safetyError = validateSafeRegex(constraints.pattern);\n if (safetyError) {\n throw new Error(safetyError);\n }\n try {\n result = result.regex(new RegExp(constraints.pattern), 'Invalid format');\n } catch {\n throw new Error(`Invalid regex pattern: ${constraints.pattern}`);\n }\n }\n\n return result;\n}\n\n/**\n * Applies number-specific constraints to a Zod number schema\n */\nfunction applyNumberConstraints(schema: z.ZodNumber, constraints?: FieldConstraints): z.ZodNumber {\n if (!constraints) return schema;\n\n // Validate min <= max when both are set\n if (\n constraints.min !== undefined &&\n constraints.max !== undefined &&\n constraints.min > constraints.max\n ) {\n throw new Error(\n `Invalid number constraints: min (${constraints.min}) cannot be greater than max (${constraints.max})`\n );\n }\n\n let result = schema;\n\n if (constraints.min !== undefined) {\n result = result.min(constraints.min, `Must be at least ${constraints.min}`);\n }\n if (constraints.max !== undefined) {\n result = result.max(constraints.max, `Must be at most ${constraints.max}`);\n }\n if (constraints.integer) {\n result = result.int('Must be an integer');\n }\n\n return result;\n}\n\n/**\n * Applies array-specific constraints to a Zod array schema\n */\nfunction applyArrayConstraints(\n schema: z.ZodArray<ZodTypeAny>,\n constraints?: FieldConstraints\n): z.ZodArray<ZodTypeAny> {\n if (!constraints) return schema;\n\n // Validate minItems <= maxItems when both are set\n if (\n constraints.minItems !== undefined &&\n constraints.maxItems !== undefined &&\n constraints.minItems > constraints.maxItems\n ) {\n throw new Error(\n `Invalid array constraints: minItems (${constraints.minItems}) cannot be greater than maxItems (${constraints.maxItems})`\n );\n }\n\n let result = schema;\n\n if (constraints.minItems !== undefined) {\n result = result.min(constraints.minItems, `Must have at least ${constraints.minItems} items`);\n }\n if (constraints.maxItems !== undefined) {\n result = result.max(constraints.maxItems, `Must have at most ${constraints.maxItems} items`);\n }\n\n return result;\n}\n\n/**\n * Rehydrates a complete custom schema from field definitions into a Zod object schema\n *\n * @param fields - Array of field definitions from database\n * @returns Zod object schema that validates documents\n * @throws RehydrationError if any field cannot be rehydrated\n *\n * @example\n * ```typescript\n * const fields = [\n * { name: 'model', type: 'string', required: true },\n * { name: 'price', type: 'number', required: false },\n * ];\n * const schema = rehydrateSchema(fields);\n * schema.parse({ model: 'MacBook Pro' }); // Valid\n * schema.parse({ model: 'MacBook Pro', price: 1999 }); // Valid\n * schema.parse({ price: 1999 }); // Throws - model is required\n * ```\n */\nexport function rehydrateSchema(fields: FieldDefinition[]): ZodObject<ZodRawShape> {\n if (!Array.isArray(fields) || fields.length === 0) {\n throw new RehydrationError('Schema must have at least one field');\n }\n\n const shape: Record<string, ZodTypeAny> = {};\n const seenNames = new Set<string>();\n\n for (const field of fields) {\n // Validate field name uniqueness\n if (seenNames.has(field.name)) {\n throw new RehydrationError(`Duplicate field name: \"${field.name}\"`);\n }\n seenNames.add(field.name);\n\n // Validate field name format\n if (!/^[a-zA-Z][a-zA-Z0-9_]*$/.test(field.name)) {\n throw new RehydrationError(\n `Invalid field name \"${field.name}\": must start with a letter and contain only alphanumeric characters and underscores`,\n field.name\n );\n }\n\n shape[field.name] = rehydrateField(field);\n }\n\n return z.object(shape);\n}\n\n/**\n * Validates that field definitions can be rehydrated without actually building the schema.\n * Useful for pre-validation before database insertion.\n *\n * @returns null if valid, error message if invalid\n */\nexport function validateFieldDefinitions(fields: FieldDefinition[]): string | null {\n try {\n rehydrateSchema(fields);\n return null;\n } catch (err) {\n return err instanceof Error ? err.message : 'Unknown validation error';\n }\n}\n","/**\n * Image and file field factories.\n *\n * The image() factory creates an ImageReference schema that:\n * - References an ImageAsset by ID\n * - Stores context-specific alt text (required for accessibility)\n * - Supports optional hotspot (focal point) and crop settings\n */\n\nimport { z } from 'zod';\n\nimport type { FieldMeta } from '../../types';\nimport {\n DimensionSchema,\n FileSizeSchema,\n MAX_DIMENSION,\n MAX_FILE_SIZE,\n MIN_DIMENSION,\n MimeTypeSchema,\n} from '../../validation/image';\n\n// =============================================================================\n// ImageAsset Schema (Public API type)\n// =============================================================================\n\n/**\n * Public API schema for image assets.\n *\n * This is the camelCase API representation. The database uses snake_case\n * (ImageAssetRow in db/image-asset-types.ts). Conversion between formats\n * should happen at the API boundary.\n *\n * Reuses validation schemas from validation/image.ts to ensure consistency.\n */\nexport const ImageAssetSchema = z.object({\n /** UUID primary key */\n id: z.uuid(),\n /** R2/S3 storage URL for the original file */\n url: z.url(),\n /** Image width in pixels */\n width: DimensionSchema,\n /** Image height in pixels */\n height: DimensionSchema,\n /** Original filename from upload */\n originalFilename: z.string().min(1).max(255),\n /** MIME type (only web-safe formats allowed) */\n mimeType: MimeTypeSchema,\n /** File size in bytes */\n fileSize: FileSizeSchema,\n /** Base64-encoded tiny preview for blur-up loading */\n lqip: z.string().optional(),\n});\n\nexport type ImageAsset = z.infer<typeof ImageAssetSchema>;\n\n// =============================================================================\n// ImageReference Schema (Asset-reference model)\n// =============================================================================\n\n/**\n * Hotspot defines the focal point of an image.\n * Coordinates are fractions (0-1) from top-left.\n */\nexport const HotspotSchema = z.object({\n x: z.number().min(0).max(1),\n y: z.number().min(0).max(1),\n});\n\nexport type Hotspot = z.infer<typeof HotspotSchema>;\n\n/**\n * Crop defines the region to extract from the image.\n * - x, y: Top-left coordinate of the crop region in pixels\n * - width, height: Size of the crop region in pixels\n * All values are positive integers representing pixel coordinates/dimensions.\n */\nexport const CropSchema = z.object({\n /** X coordinate of top-left corner in pixels */\n x: z.number().int().nonnegative(),\n /** Y coordinate of top-left corner in pixels */\n y: z.number().int().nonnegative(),\n /** Width of crop region in pixels (must be > 0) */\n width: z.number().int().positive(),\n /** Height of crop region in pixels (must be > 0) */\n height: z.number().int().positive(),\n});\n\nexport type Crop = z.infer<typeof CropSchema>;\n\n/**\n * ImageReference is what blocks store.\n * It points to an ImageAsset and adds context-specific metadata.\n */\nexport const ImageReferenceSchema = z.object({\n // Alt text is REQUIRED for accessibility\n alt: z.string().min(1, 'Alt text is required for accessibility'),\n\n // Optional metadata\n caption: z.string().max(500).optional(),\n attribution: z.string().max(255).optional(),\n\n // Reference to the ImageAsset with stored transformation\n _asset: z.object({\n id: z.uuid(),\n transformation: z.string().nullable().optional(),\n }),\n});\n\nexport type ImageReference = z.infer<typeof ImageReferenceSchema>;\n\n// =============================================================================\n// Image Field Factory\n// =============================================================================\n\nexport interface ImageFieldOptions {\n label: string;\n description?: string;\n required?: boolean;\n accept?: string;\n maxSize?: number;\n minWidth?: number;\n minHeight?: number;\n maxWidth?: number;\n maxHeight?: number;\n aspectRatio?: number;\n}\n\n/**\n * Create an image field with metadata.\n *\n * @example\n * const featuredImage = image({\n * label: 'Featured Image',\n * required: true,\n * accept: 'image/jpeg, image/png',\n * maxSize: 5 * 1024 * 1024,\n * });\n *\n * // Type of the value:\n * // {\n * // alt: string;\n * // caption?: string;\n * // attribution?: string;\n * // _asset: { id: string; transformation?: string | null };\n * // }\n */\nexport function image(options: ImageFieldOptions) {\n const meta: FieldMeta = {\n label: options.label,\n component: 'image-uploader',\n required: options.required,\n description: options.description,\n options: {\n accept: options.accept ?? 'image/jpeg, image/png, image/webp',\n maxSize: options.maxSize ?? MAX_FILE_SIZE, // 10MB default\n minWidth: options.minWidth ?? MIN_DIMENSION,\n minHeight: options.minHeight ?? MIN_DIMENSION,\n maxWidth: options.maxWidth ?? MAX_DIMENSION,\n maxHeight: options.maxHeight ?? MAX_DIMENSION,\n aspectRatio: options.aspectRatio,\n },\n };\n\n // Use the new ImageReferenceSchema instead of URL-only\n const result = options.required ? ImageReferenceSchema : ImageReferenceSchema.optional();\n\n return result.meta(meta);\n}\n\n// =============================================================================\n// File Field\n// =============================================================================\n\nexport interface FileFieldOptions {\n label: string;\n description?: string;\n required?: boolean;\n accept?: string;\n maxSize?: number;\n}\n\nconst fileSchema = z.object({\n url: z.url(),\n name: z.string(),\n size: z.number().int().positive(),\n type: z.string(),\n});\n\n/**\n * Create a file field with metadata.\n */\nexport function file(options: FileFieldOptions) {\n const meta: FieldMeta = {\n label: options.label,\n component: 'file-uploader',\n required: options.required,\n description: options.description,\n options: {\n accept: options.accept ?? '*/*',\n maxSize: options.maxSize,\n },\n };\n\n const result = options.required ? fileSchema : fileSchema.optional();\n return result.meta(meta);\n}\n","/**\n * Image Validation Utilities\n *\n * Provides validation for:\n * - File type (MIME type checking)\n * - File size (configurable limits)\n * - Image dimensions (width/height constraints)\n */\n\nimport { z } from 'zod';\n\n// =============================================================================\n// Constants\n// =============================================================================\n\nexport const ALLOWED_MIME_TYPES = ['image/jpeg', 'image/png', 'image/webp'] as const;\nexport type ImageMimeType = (typeof ALLOWED_MIME_TYPES)[number];\n\nexport const MAX_FILE_SIZE = 10 * 1024 * 1024; // 10MB\nexport const MIN_FILE_SIZE = 1024; // 1KB (avoid empty/corrupt files)\n\nexport const MAX_DIMENSION = 8192; // 8K resolution\nexport const MIN_DIMENSION = 10; // Minimum useful size\n\n// =============================================================================\n// Zod Schemas\n// =============================================================================\n\nexport const MimeTypeSchema = z.enum(ALLOWED_MIME_TYPES);\n\nexport const FileSizeSchema = z\n .number()\n .int()\n .min(MIN_FILE_SIZE, `File too small. Minimum: ${MIN_FILE_SIZE} bytes`)\n .max(MAX_FILE_SIZE, `File too large. Maximum: ${MAX_FILE_SIZE / 1024 / 1024}MB`);\n\nexport const DimensionSchema = z\n .number()\n .int()\n .min(MIN_DIMENSION, `Dimension too small. Minimum: ${MIN_DIMENSION}px`)\n .max(MAX_DIMENSION, `Dimension too large. Maximum: ${MAX_DIMENSION}px`);\n\n/**\n * Schema for upload request validation.\n */\nexport const UploadRequestSchema = z.object({\n filename: z\n .string()\n .min(1, 'Filename is required')\n .max(255, 'Filename too long')\n .regex(/^[^<>:\"/\\\\|?*]+$/, 'Filename contains invalid characters'),\n mimeType: MimeTypeSchema,\n fileSize: FileSizeSchema,\n});\n\nexport type UploadRequest = z.infer<typeof UploadRequestSchema>;\n\n/**\n * Schema for confirming upload with dimensions.\n * assetId is optional - if not provided, the server will generate a UUID.\n */\nexport const ConfirmUploadSchema = z.object({\n assetId: z.uuid().optional(),\n width: DimensionSchema,\n height: DimensionSchema,\n});\n\nexport type ConfirmUpload = z.infer<typeof ConfirmUploadSchema>;\n\n// =============================================================================\n// Validation Functions\n// =============================================================================\n\nexport interface ValidationResult {\n valid: boolean;\n error?: string;\n}\n\n/**\n * Validate file type by MIME type.\n */\nexport function validateMimeType(mimeType: string): ValidationResult {\n const result = MimeTypeSchema.safeParse(mimeType);\n if (result.success) {\n return { valid: true };\n }\n return {\n valid: false,\n error: `File type not supported. Use JPEG, PNG, or WebP.`,\n };\n}\n\n/**\n * Validate file size.\n */\nexport function validateFileSize(size: number): ValidationResult {\n if (size < MIN_FILE_SIZE) {\n return {\n valid: false,\n error: `File appears to be empty or corrupt.`,\n };\n }\n if (size > MAX_FILE_SIZE) {\n const sizeMB = (size / 1024 / 1024).toFixed(1);\n return {\n valid: false,\n error: `File too large (${sizeMB}MB). Maximum: 10 MB.`,\n };\n }\n return { valid: true };\n}\n\n/**\n * Validate image dimensions.\n */\nexport function validateDimensions(width: number, height: number): ValidationResult {\n if (width < MIN_DIMENSION || height < MIN_DIMENSION) {\n return {\n valid: false,\n error: `Image too small. Minimum: ${MIN_DIMENSION}x${MIN_DIMENSION}px.`,\n };\n }\n if (width > MAX_DIMENSION || height > MAX_DIMENSION) {\n return {\n valid: false,\n error: `Image too large. Maximum: ${MAX_DIMENSION}x${MAX_DIMENSION}px.`,\n };\n }\n return { valid: true };\n}\n\n/**\n * Validate complete upload request.\n */\nexport function validateUploadRequest(\n request: unknown\n): { valid: true; data: UploadRequest } | { valid: false; error: string } {\n const result = UploadRequestSchema.safeParse(request);\n if (result.success) {\n return { valid: true, data: result.data };\n }\n const firstIssue = result.error.issues[0];\n return {\n valid: false,\n error: firstIssue?.message ?? 'Invalid upload request',\n };\n}\n\n// =============================================================================\n// File Extension Utilities\n// =============================================================================\n\nconst MIME_TO_EXT: Record<ImageMimeType, string> = {\n 'image/jpeg': 'jpg',\n 'image/png': 'png',\n 'image/webp': 'webp',\n};\n\nconst EXT_TO_MIME: Record<string, ImageMimeType> = {\n jpg: 'image/jpeg',\n jpeg: 'image/jpeg',\n png: 'image/png',\n webp: 'image/webp',\n};\n\n/**\n * Get file extension from MIME type.\n */\nexport function getExtensionFromMime(mimeType: ImageMimeType | string): string {\n return MIME_TO_EXT[mimeType as ImageMimeType] ?? 'bin';\n}\n\n/**\n * Get MIME type from file extension.\n */\nexport function getMimeFromExtension(ext: string): ImageMimeType | undefined {\n return EXT_TO_MIME[ext.toLowerCase()];\n}\n\n/**\n * Extract extension from filename.\n */\nexport function getExtensionFromFilename(filename: string): string {\n const parts = filename.split('.');\n return parts.pop()?.toLowerCase?.() ?? '';\n}\n\n/**\n * Sanitize filename for storage.\n * Removes special characters, preserves extension.\n */\nexport function sanitizeFilename(filename: string): string {\n // Get extension\n const ext = getExtensionFromFilename(filename);\n\n // Get base name without extension\n const base = filename.slice(0, filename.length - ext.length - 1);\n\n // Sanitize: lowercase, replace spaces and special chars\n const sanitized = base\n .toLowerCase()\n .replace(/[^a-z0-9]/g, '-')\n .replace(/-+/g, '-')\n .replace(/^-|-$/g, '')\n .slice(0, 50); // Limit length\n\n return ext ? `${sanitized}.${ext}` : sanitized;\n}\n","/**\n * Safe regex validation utilities.\n *\n * Prevents ReDoS (Regular Expression Denial of Service) attacks by\n * detecting potentially dangerous regex patterns.\n */\n\nimport safeRegex from 'safe-regex2';\n\n/**\n * Validates that a regex pattern is safe to use.\n * Uses safe-regex2 library to detect ReDoS vulnerabilities.\n *\n * @param pattern - The regex pattern to validate\n * @returns null if safe, error message if potentially dangerous\n */\nexport function validateSafeRegex(pattern: string): string | null {\n // Limit pattern length to prevent extremely long patterns\n if (pattern.length > 500) {\n return 'Regex pattern is too long (max 500 characters)';\n }\n\n // Use safe-regex2 to detect ReDoS vulnerabilities\n if (!safeRegex(pattern)) {\n return 'Regex pattern is potentially unsafe (ReDoS risk)';\n }\n\n // Try to compile the regex to ensure it's valid\n try {\n new RegExp(pattern);\n } catch (err) {\n return `Invalid regex: ${err instanceof Error ? err.message : 'Unknown'}`;\n }\n\n return null;\n}\n","/**\n * Schema hashing utility for document versioning.\n *\n * Creates a deterministic hash of a Zod schema's shape.\n * Used to detect when documents need migration after schema changes.\n */\n\nimport hash from 'object-hash';\nimport type { ZodObject, ZodRawShape, ZodTypeAny } from 'zod';\n\n/**\n * Unwraps optional, default, and nullable wrappers to get the inner type.\n */\nfunction unwrapZodType(zodType: ZodTypeAny): ZodTypeAny {\n const current = zodType;\n const def = current._def;\n\n // Unwrap optional/nullable/default wrappers\n if ('innerType' in def && def.innerType) {\n return unwrapZodType(def.innerType as ZodTypeAny);\n }\n\n return current;\n}\n\n/**\n * Gets the type name from a Zod type, unwrapping wrappers first.\n * In Zod v4, types are stored in _def.type (not typeName).\n */\nfunction getZodTypeName(zodType: ZodTypeAny): string {\n const unwrapped = unwrapZodType(zodType);\n const def = unwrapped._def;\n\n if (!def) return 'unknown';\n\n // Zod v4 uses _def.type for primitive types\n if ('type' in def && typeof def.type === 'string') {\n return def.type as string;\n }\n\n // Fallback: check for typeName (older Zod versions or complex types)\n if ('typeName' in def) {\n return def.typeName as string;\n }\n\n return 'unknown';\n}\n\n/**\n * Creates a deterministic hash of a Zod schema's shape.\n *\n * Uses object-hash library for canonical key ordering, ensuring\n * the same hash is produced regardless of property definition order.\n *\n * @param schema - A Zod object schema\n * @returns MD5 hash string of the schema shape\n */\nexport function createSchemaHash(schema: ZodObject<ZodRawShape>): string {\n const shape = schema.shape;\n\n // Create a serializable representation of the schema\n const schemaDefinition = Object.entries(shape).reduce(\n (acc, [key, zodType]) => {\n const typeName = getZodTypeName(zodType as ZodTypeAny);\n const unwrapped = unwrapZodType(zodType as ZodTypeAny);\n const unwrappedDef = unwrapped._def as unknown as Record<string, unknown>;\n\n // Check if optional (method exists on ZodTypeAny in Zod v4)\n const isOptional =\n 'isOptional' in zodType && typeof zodType.isOptional === 'function'\n ? zodType.isOptional()\n : false;\n\n acc[key] = {\n type: typeName,\n optional: isOptional,\n // Include nested shape for objects\n ...(typeName === 'object' &&\n 'shape' in unwrappedDef &&\n typeof unwrappedDef.shape === 'function' && {\n shape: Object.keys((unwrappedDef.shape as () => Record<string, unknown>)()),\n }),\n };\n return acc;\n },\n {} as Record<string, unknown>\n );\n\n return hash(schemaDefinition, {\n algorithm: 'md5',\n respectType: false,\n unorderedArrays: false,\n });\n}\n","/**\n * Country schema-specific validation.\n *\n * Validates that language codes exist in the database.\n */\n\nimport { TRPCError } from '@trpc/server';\nimport type { ReferenceValue } from '../../db/document-types';\nimport { isReference } from '../../db/document-types';\nimport type { TypedSupabaseClient } from '../../db/types';\n\n/**\n * Validate country content, specifically checking that language codes exist.\n *\n * @param content - Validated country content\n * @param supabase - Supabase client for database queries\n * @throws {TRPCError} If validation fails\n */\nexport async function validateCountryContent(\n content: unknown,\n supabase: TypedSupabaseClient\n): Promise<void> {\n const countryContent = content as {\n languages?: Array<string | ReferenceValue>;\n };\n const languages = Array.isArray(countryContent.languages) ? countryContent.languages : [];\n\n const languageCodes = languages.filter((lang): lang is string => typeof lang === 'string');\n const languageReferences = languages.filter(isReference);\n\n // Validate legacy reference format points to language schema\n const invalidReferences = languageReferences.filter((ref) => ref._schema !== 'language');\n if (invalidReferences.length > 0) {\n throw new TRPCError({\n code: 'BAD_REQUEST',\n message: 'Language references must target the language schema',\n });\n }\n\n // Only perform database check when using code-based languages\n if (languageCodes.length > 0) {\n const { data: allLanguages, error: langError } = await supabase\n .from('documents')\n .select('content')\n .eq('schema_name', 'language');\n\n if (langError) {\n throw new TRPCError({\n code: 'INTERNAL_SERVER_ERROR',\n message: 'Failed to validate language codes',\n cause: langError,\n });\n }\n\n const existingCodes = new Set(\n (allLanguages || [])\n .map((lang) => {\n const langContent = lang.content as { code?: string };\n return langContent?.code;\n })\n .filter((code): code is string => typeof code === 'string' && code.length > 0)\n );\n\n const missingCodes = languageCodes.filter((code) => !existingCodes.has(code));\n\n if (missingCodes.length > 0) {\n throw new TRPCError({\n code: 'BAD_REQUEST',\n message: `Language codes not found: ${missingCodes.join(', ')}. Please create language documents with these codes first.`,\n });\n }\n }\n}\n","/**\n * CMS API Client\n *\n * Creates an HTTP-based tRPC client for calling the CMS API.\n * Used in Server Components to fetch routes and blocks from the CMS.\n */\n\nimport type { AppRouter } from '@repo/cms-schema/trpc';\nimport { type CreateTRPCClient, createTRPCClient, httpBatchLink } from '@trpc/client';\nimport superjson from 'superjson';\n\n/** Type alias for the CMS API client */\ntype CmsClient = CreateTRPCClient<AppRouter>;\n\n/**\n * Get the CMS API URL from the provided base URL.\n */\nfunction getCmsApiUrl(cmsUrl: string): string {\n return new URL('/api/trpc', cmsUrl).toString();\n}\n\n/** Unified configuration for all CMS API access */\nexport interface CmsConfig {\n /** CMS API base URL (e.g., 'http://localhost:3000') */\n cmsUrl: string;\n /** API key for authentication */\n apiKey?: string;\n /** Website ID to scope API requests */\n websiteId?: string;\n}\n\n/**\n * Create a custom fetch function that appends API key as query parameter.\n */\nfunction createFetchWithApiKey(apiKey?: string, websiteId?: string) {\n return async (url: URL | RequestInfo, options?: RequestInit): Promise<Response> => {\n let finalUrl = url;\n\n const urlObj = new URL(url.toString());\n if (apiKey) {\n urlObj.searchParams.set('api_key', apiKey);\n }\n if (websiteId) {\n urlObj.searchParams.set('website_id', websiteId);\n }\n if (apiKey || websiteId) {\n finalUrl = urlObj.toString();\n }\n\n const response = await fetch(finalUrl, options);\n\n return response;\n };\n}\n\n/**\n * Create a tRPC client for the CMS API.\n */\nfunction createCmsClient(options: CmsConfig): CmsClient {\n const url = getCmsApiUrl(options.cmsUrl);\n\n return createTRPCClient<AppRouter>({\n links: [\n httpBatchLink({\n url,\n transformer: superjson,\n fetch: createFetchWithApiKey(options.apiKey, options.websiteId),\n }),\n ],\n });\n}\n\n/**\n * Get a CMS client for the specified CMS URL.\n */\nexport function getCmsClient(options: CmsConfig): CmsClient {\n return createCmsClient(options);\n}\n"],"mappings":"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","names":["types","safeRegex","ok","z","z","z","z","z","z","z","z","safeRegex","err","err","z"]}
|
|
1
|
+
{"version":3,"sources":["../../../../node_modules/ret/dist/types/tokens.js","../../../../node_modules/ret/lib/types/types.ts","../../../../node_modules/ret/dist/types/set-lookup.js","../../../../node_modules/ret/lib/types/index.ts","../../../../node_modules/ret/lib/sets.ts","../../../../node_modules/ret/lib/util.ts","../../../../node_modules/ret/lib/tokenizer.ts","../../../../node_modules/ret/lib/sets-lookup.ts","../../../../node_modules/ret/lib/write-set-tokens.ts","../../../../node_modules/ret/lib/reconstruct.ts","../../../../node_modules/ret/lib/index.ts","../../../../node_modules/safe-regex2/index.js","../../lib/custom-schemas.ts","../../../../packages/cms-schema/src/documents/generate-zod-code.ts","../../../../packages/cms-schema/src/documents/registry.ts","../../../../packages/cms-schema/src/documents/schemas/country.ts","../../../../packages/cms-schema/src/documents/schemas/language.ts","../../../../packages/cms-schema/src/documents/unified-registry.ts","../../../../packages/cms-schema/src/documents/rehydration.ts","../../../../packages/cms-schema/src/fields/complex/media.ts","../../../../packages/cms-schema/src/validation/image.ts","../../../../packages/cms-schema/src/utils/safe-regex.ts","../../../../packages/cms-schema/src/documents/schema-hash.ts","../../../../packages/cms-schema/src/documents/validations/country.ts","../../lib/cms-api.ts"],"sourcesContent":["\"use strict\";\nObject.defineProperty(exports, \"__esModule\", { value: true });\n//# sourceMappingURL=tokens.js.map",null,"\"use strict\";\nObject.defineProperty(exports, \"__esModule\", { value: true });\n//# sourceMappingURL=set-lookup.js.map",null,null,null,null,null,null,null,null,"'use strict'\n\nconst parse = require('ret')\nconst types = parse.types\n\nfunction safeRegex (re, opts) {\n if (!opts) opts = {}\n /* c8 ignore next */\n const replimit = opts.limit === undefined ? 25 : opts.limit\n\n /* c8 ignore next 2 */\n if (isRegExp(re)) re = re.source\n else if (typeof re !== 'string') re = String(re)\n\n try { re = parse(re) } catch { return false }\n\n let reps = 0\n return (function walk (node, starHeight) {\n let i\n let ok\n let len\n\n if (node.type === types.REPETITION) {\n starHeight++\n reps++\n if (starHeight > 1) return false\n if (reps > replimit) return false\n }\n\n if (node.options) {\n for (i = 0, len = node.options.length; i < len; i++) {\n ok = walk({ stack: node.options[i] }, starHeight)\n if (!ok) return false\n }\n }\n const stack = node.stack || node.value?.stack\n if (!stack) return true\n\n for (i = 0; i < stack.length; i++) {\n ok = walk(stack[i], starHeight)\n if (!ok) return false\n }\n\n return true\n })(re, 0)\n}\n\nfunction isRegExp (x) {\n return {}.toString.call(x) === '[object RegExp]'\n}\n\nmodule.exports = safeRegex\nmodule.exports.default = safeRegex\nmodule.exports.safeRegex = safeRegex\n","/**\n * Custom Schema Fetcher\n *\n * Fetches custom schema definitions (field metadata as JSON) from the CMS API\n * for a given website. Uses the tRPC client to call the customSchema router.\n */\n\nimport { mkdir, writeFile } from 'node:fs/promises';\nimport { dirname } from 'node:path';\nimport type { FieldDefinition } from '@repo/cms-schema/documents';\nimport {\n generateCombinedZodSchemaCode,\n generateZodSchemaCode,\n rehydrateSchema,\n} from '@repo/cms-schema/documents';\nimport type { ZodObject, ZodRawShape } from 'zod';\nimport { type CmsConfig, getCmsClient } from './cms-api';\n\nexport interface CustomSchemaFields {\n name: string;\n displayName: string;\n description: string | null;\n fields: FieldDefinition[];\n routeSlugField: string | null;\n version: number;\n}\n\nexport type FetchCustomSchemasOptions = CmsConfig & { websiteId: string };\n\n/**\n * Fetches all custom schemas with their field definitions for a website.\n *\n * @example\n * ```ts\n * const schemas = await fetchAllCustomSchemaFields({\n * cmsUrl: 'http://localhost:3000',\n * apiKey: 'my-api-key',\n * websiteId: 'uuid-here',\n * });\n *\n * for (const schema of schemas) {\n * console.log(schema.name, schema.fields);\n * }\n * ```\n */\nexport async function fetchAllCustomSchemaFields(\n options: FetchCustomSchemasOptions\n): Promise<CustomSchemaFields[]> {\n const { websiteId } = options;\n\n const client = getCmsClient(options);\n\n // Get the list of all custom schemas (summaries only)\n const summaries = await client.customSchema.list.query({\n websiteId,\n includeSystem: false,\n });\n\n // Fetch full field definitions for each schema in parallel\n const schemas = await Promise.all(\n summaries.map(async (summary) => {\n const detail = await client.customSchema.getByName.query({\n websiteId,\n name: summary.name,\n });\n\n return {\n name: detail.name,\n displayName: detail.displayName,\n description: detail.description ?? null,\n fields: detail.fields,\n routeSlugField: detail.routeSlugField ?? null,\n version: detail.version,\n } satisfies CustomSchemaFields;\n })\n );\n\n return schemas;\n}\n\n/**\n * Fetches a single custom schema's field definitions by name.\n *\n * @example\n * ```ts\n * const schema = await fetchCustomSchemaFields({\n * cmsUrl: 'http://localhost:3000',\n * apiKey: 'my-api-key',\n * websiteId: 'uuid-here',\n * }, 'blog_post');\n *\n * console.log(schema.fields);\n * ```\n */\nexport async function fetchCustomSchemaFields(\n options: FetchCustomSchemasOptions,\n schemaName: string\n): Promise<CustomSchemaFields> {\n const { websiteId } = options;\n const client = getCmsClient(options);\n\n const detail = await client.customSchema.getByName.query({\n websiteId,\n name: schemaName,\n });\n\n return {\n name: detail.name,\n displayName: detail.displayName,\n description: detail.description ?? null,\n fields: detail.fields,\n routeSlugField: detail.routeSlugField ?? null,\n version: detail.version,\n };\n}\n\n/**\n * Converts an array of custom schema definitions into a record of Zod schemas,\n * keyed by schema name.\n *\n * @example\n * ```ts\n * const schemas = await fetchAllCustomSchemaFields(options);\n * const zodSchemas = buildZodSchemas(schemas);\n *\n * // Validate a blog post document\n * const result = zodSchemas['blog_post'].safeParse(data);\n * ```\n */\nexport function buildZodSchemas(\n schemas: CustomSchemaFields[]\n): Record<string, ZodObject<ZodRawShape>> {\n // biome-ignore lint/suspicious/noExplicitAny: avoids excessively deep type instantiation from Zod generics\n const result: Record<string, any> = {};\n\n for (const schema of schemas) {\n result[schema.name] = rehydrateSchema(schema.fields);\n }\n\n return result;\n}\n\n/**\n * Converts an array of custom schema definitions into a record of Zod source\n * code strings, keyed by schema name.\n *\n * @example\n * ```ts\n * const schemas = await fetchAllCustomSchemaFields(options);\n * const codeMap = buildZodSchemaCode(schemas);\n *\n * // Write blog_post schema to a file\n * fs.writeFileSync('blog-post.ts', codeMap['blog_post']);\n * ```\n */\nexport function buildZodSchemaCode(schemas: CustomSchemaFields[]): Record<string, string> {\n const result: Record<string, string> = {};\n\n for (const schema of schemas) {\n result[schema.name] = generateZodSchemaCode(schema.name, schema.fields);\n }\n\n return result;\n}\n\n/**\n * Generates combined Zod schema code from all custom schemas and writes it\n * to the specified file path.\n *\n * @param schemas - Array of custom schema definitions\n * @param filePath - Absolute or relative path to write the generated file\n */\nexport async function saveZodSchemaCode(\n schemas: CustomSchemaFields[],\n filePath: string\n): Promise<void> {\n if (schemas.length === 0) {\n console.warn('[saveZodSchemaCode] No schemas provided — skipping file write.');\n return;\n }\n\n const code = generateCombinedZodSchemaCode(schemas);\n\n await mkdir(dirname(filePath), { recursive: true });\n await writeFile(filePath, code, 'utf-8');\n}\n","/**\n * Zod Schema Code Generator\n *\n * Generates valid JavaScript/TypeScript Zod schema source code\n * from FieldDefinition[] (the same structure used by rehydration.ts at runtime).\n */\n\nimport type { FieldConstraints, FieldDefinition } from './schema-types';\n\n/**\n * Generates a complete Zod schema source code string from field definitions.\n *\n * @param schemaName - The schema name (used for the variable name)\n * @param fields - Array of field definitions\n * @returns Formatted Zod source code string\n */\nexport function generateZodSchemaCode(schemaName: string, fields: FieldDefinition[]): string {\n const lines: string[] = [];\n\n lines.push(`import { z } from 'zod';`);\n lines.push('');\n\n // Generate the ImageReference schema inline if any image fields exist\n const hasImageField = fields.some((f) => f.type === 'image');\n if (hasImageField) {\n appendImageReferenceSchema(lines);\n lines.push('');\n }\n\n appendSchemaBody(lines, schemaName, fields);\n\n return lines.join('\\n');\n}\n\n/**\n * Generates a single combined Zod schema source code file from multiple schema definitions.\n *\n * Produces one `import { z }`, one `ImageReferenceSchema` (if needed), then all schema exports.\n *\n * @param schemas - Array of schema definitions with name and fields\n * @returns Formatted Zod source code string for the combined file\n */\nexport function generateCombinedZodSchemaCode(\n schemas: Array<{ name: string; fields: FieldDefinition[] }>\n): string {\n const lines: string[] = [];\n\n // Auto-generated file header with linter/formatter ignore directives\n lines.push('/* eslint-disable */');\n lines.push('/* prettier-ignore */');\n lines.push('/* biome-ignore format: auto-generated */');\n lines.push('// This file is auto-generated by cms-renderer. Do not edit manually.');\n lines.push('');\n lines.push(`import { z } from 'zod';`);\n lines.push('');\n\n // Emit ImageReferenceSchema once if any schema uses image fields\n const hasImageField = schemas.some((s) => s.fields.some((f) => f.type === 'image'));\n if (hasImageField) {\n appendImageReferenceSchema(lines);\n lines.push('');\n }\n\n for (const [i, schema] of schemas.entries()) {\n if (i > 0) lines.push('');\n appendSchemaBody(lines, schema.name, schema.fields);\n }\n\n return lines.join('\\n');\n}\n\n/** Appends the z.object and type export lines for a single schema. */\nfunction appendSchemaBody(lines: string[], schemaName: string, fields: FieldDefinition[]): void {\n const varName = `${toCamelCase(schemaName)}Schema`;\n\n lines.push(`export const ${varName} = z.object({`);\n\n for (const field of fields) {\n const fieldCode = generateFieldCode(field);\n if (field.description) {\n lines.push(` /** ${field.description} */`);\n }\n lines.push(` ${field.name}: ${fieldCode},`);\n }\n\n lines.push('});');\n lines.push('');\n lines.push(`export type ${toPascalCase(schemaName)} = z.infer<typeof ${varName}>;`);\n lines.push('');\n}\n\n/** Appends the ImageReferenceSchema constant. */\nfunction appendImageReferenceSchema(lines: string[]): void {\n lines.push('const ImageReferenceSchema = z.object({');\n lines.push(\" alt: z.string().min(1, 'Alt text is required for accessibility'),\");\n lines.push(' caption: z.string().max(500).optional(),');\n lines.push(' attribution: z.string().max(255).optional(),');\n lines.push(' _asset: z.object({');\n lines.push(' id: z.string().uuid(),');\n lines.push(' }),');\n lines.push('});');\n}\n\nfunction generateFieldCode(field: FieldDefinition): string {\n let code = generateBaseTypeCode(field.type, field.constraints);\n\n if (!field.required) {\n code += '.optional()';\n }\n\n return code;\n}\n\nfunction generateBaseTypeCode(\n type: FieldDefinition['type'],\n constraints?: FieldConstraints\n): string {\n switch (type) {\n case 'string':\n return applyStringConstraintCode('z.string()', constraints);\n\n case 'number':\n return applyNumberConstraintCode('z.number()', constraints);\n\n case 'boolean':\n return 'z.boolean()';\n\n case 'image':\n return 'ImageReferenceSchema';\n\n case 'date':\n return \"z.string().date('Invalid date format. Expected YYYY-MM-DD')\";\n\n case 'datetime':\n return \"z.string().datetime({ offset: true, message: 'Invalid datetime format. Expected ISO 8601' })\";\n\n case 'url':\n return applyStringConstraintCode(\"z.string().url('Invalid URL format')\", constraints);\n\n case 'email':\n return applyStringConstraintCode(\"z.string().email('Invalid email format')\", constraints);\n\n case 'enum': {\n const values = constraints?.enumValues;\n if (!values || values.length === 0) {\n return \"z.enum([''])\";\n }\n const formatted = values.map((v) => `'${escapeString(v)}'`).join(', ');\n return `z.enum([${formatted}])`;\n }\n\n case 'reference':\n return 'z.record(z.string(), z.unknown())';\n\n case 'array': {\n const itemType = constraints?.arrayItemType ?? 'string';\n const itemCode = generatePrimitiveCode(itemType);\n return applyArrayConstraintCode(`z.array(${itemCode})`, constraints);\n }\n\n default:\n return 'z.unknown()';\n }\n}\n\nfunction generatePrimitiveCode(type: string): string {\n switch (type) {\n case 'string':\n return 'z.string()';\n case 'number':\n return 'z.number()';\n case 'boolean':\n return 'z.boolean()';\n default:\n return 'z.unknown()';\n }\n}\n\nfunction applyStringConstraintCode(base: string, constraints?: FieldConstraints): string {\n if (!constraints) return base;\n\n let code = base;\n if (constraints.minLength !== undefined) {\n code += `.min(${constraints.minLength}, 'Must be at least ${constraints.minLength} characters')`;\n }\n if (constraints.maxLength !== undefined) {\n code += `.max(${constraints.maxLength}, 'Must be at most ${constraints.maxLength} characters')`;\n }\n if (constraints.pattern) {\n code += `.regex(/${escapeRegex(constraints.pattern)}/, 'Invalid format')`;\n }\n return code;\n}\n\nfunction applyNumberConstraintCode(base: string, constraints?: FieldConstraints): string {\n if (!constraints) return base;\n\n let code = base;\n if (constraints.integer) {\n code += `.int('Must be an integer')`;\n }\n if (constraints.min !== undefined) {\n code += `.min(${constraints.min}, 'Must be at least ${constraints.min}')`;\n }\n if (constraints.max !== undefined) {\n code += `.max(${constraints.max}, 'Must be at most ${constraints.max}')`;\n }\n return code;\n}\n\nfunction applyArrayConstraintCode(base: string, constraints?: FieldConstraints): string {\n if (!constraints) return base;\n\n let code = base;\n if (constraints.minItems !== undefined) {\n code += `.min(${constraints.minItems}, 'Must have at least ${constraints.minItems} items')`;\n }\n if (constraints.maxItems !== undefined) {\n code += `.max(${constraints.maxItems}, 'Must have at most ${constraints.maxItems} items')`;\n }\n return code;\n}\n\nfunction escapeString(str: string): string {\n return str.replace(/\\\\/g, '\\\\\\\\').replace(/'/g, \"\\\\'\");\n}\n\nfunction escapeRegex(pattern: string): string {\n return pattern.replace(/\\//g, '\\\\/');\n}\n\nfunction toCamelCase(str: string): string {\n return str.replace(/_([a-z])/g, (_, c) => c.toUpperCase());\n}\n\nfunction toPascalCase(str: string): string {\n const camel = toCamelCase(str);\n return camel.charAt(0).toUpperCase() + camel.slice(1);\n}\n","/**\n * Document Schema Registry.\n *\n * Centralizes registration for all document schemas.\n * The CMS reads this registry to generate UI and validation.\n */\n\nimport { type ZodObject, type ZodRawShape, z } from 'zod';\nimport type { DocumentError } from '../db/document-types';\nimport { DocumentErrors } from '../db/document-types';\nimport type { TypedSupabaseClient } from '../db/types';\nimport type { Result } from '../types/errors';\nimport { err, ok } from '../types/errors';\n\n// =============================================================================\n// Validation Schemas\n// =============================================================================\n\n/**\n * Schema name validation - must be a non-empty string.\n */\nconst schemaNameValidator = z\n .string()\n .min(1, 'Schema name must be a non-empty string')\n .refine((name) => name.trim().length > 0, 'Schema name must not be only whitespace');\n\n/**\n * Runtime validator for document schema config.\n * Validates the config object structure at runtime.\n */\nconst documentSchemaConfigValidator = z.object({\n schema: z.custom<ZodObject<ZodRawShape>>(\n (val) => val !== null && typeof val === 'object' && '_def' in val,\n { message: 'schema must be a valid Zod schema' }\n ),\n titleField: z.string().optional(),\n displayName: z.string().optional(),\n description: z.string().optional(),\n validateContent: z.function().optional(),\n});\n\n// =============================================================================\n// Types\n// =============================================================================\n\n/**\n * Configuration for a registered document schema.\n */\nexport interface DocumentSchemaConfig<T extends ZodRawShape = ZodRawShape> {\n /** Zod schema for content validation */\n schema: ZodObject<T>;\n /** Field to extract for title (default: tries 'name', 'title', 'code') */\n titleField?: string;\n /** Display name for the schema in the UI */\n displayName?: string;\n /** Description of the document type */\n description?: string;\n /** Optional custom validation function for schema-specific rules */\n validateContent?: (content: unknown, supabase: TypedSupabaseClient) => Promise<void>;\n}\n\n/**\n * Registered schema with computed metadata.\n */\nexport interface RegisteredSchema<T extends ZodRawShape = ZodRawShape>\n extends DocumentSchemaConfig<T> {\n /** Schema name (registry key) */\n name: string;\n}\n\n// =============================================================================\n// Registry\n// =============================================================================\n\nconst documentRegistry = new Map<string, DocumentSchemaConfig>();\n\n/**\n * Register a document schema.\n *\n * @example\n * registerDocumentSchema('country', {\n * schema: CountrySchema,\n * titleField: 'name',\n * displayName: 'Country',\n * });\n *\n * @throws {Error} If schema name is empty or already registered\n */\nexport function registerDocumentSchema<T extends ZodRawShape>(\n name: string,\n config: DocumentSchemaConfig<T>\n): void {\n // Validate schema name using Zod\n const nameResult = schemaNameValidator.safeParse(name);\n if (!nameResult.success) {\n throw new Error(nameResult.error.issues.at(0)?.message ?? 'Invalid schema name');\n }\n\n // Validate config structure using Zod\n const configResult = documentSchemaConfigValidator.safeParse(config);\n if (!configResult.success) {\n throw new Error(\n `Invalid schema config: ${configResult.error.issues.at(0)?.message ?? 'Unknown error'}`\n );\n }\n\n // Check for duplicate registration\n if (documentRegistry.has(name)) {\n throw new Error(`Document schema \"${name}\" is already registered`);\n }\n\n documentRegistry.set(name, config as DocumentSchemaConfig);\n}\n\n/**\n * Get a document schema configuration by name.\n */\nexport function getDocumentSchema(name: string): DocumentSchemaConfig | undefined {\n return documentRegistry.get(name);\n}\n\n/**\n * Get all registered schema names.\n */\nexport function getRegisteredDocumentSchemas(): string[] {\n return Array.from(documentRegistry.keys());\n}\n\n/**\n * Get all registered schemas with their configurations.\n */\nexport function getAllDocumentSchemas(): RegisteredSchema[] {\n return Array.from(documentRegistry.entries()).map(([name, config]) => ({\n name,\n ...config,\n }));\n}\n\n/**\n * Clear the document registry (for testing).\n */\nexport function clearDocumentRegistry(): void {\n documentRegistry.clear();\n}\n\n// =============================================================================\n// Validation\n// =============================================================================\n\n/**\n * Validate document content against a registered schema.\n *\n * @returns Result with validated data or error\n */\nexport function validateDocumentContent<T>(\n schemaName: string,\n content: unknown\n): Result<T, DocumentError> {\n const config = documentRegistry.get(schemaName);\n\n if (!config) {\n return err(DocumentErrors.schemaNotFound(schemaName));\n }\n\n const result = config.schema.safeParse(content);\n\n if (!result.success) {\n const issues = result.error.issues.map((issue) => ({\n path: issue.path.filter((p): p is string | number => typeof p !== 'symbol'),\n message: issue.message,\n }));\n return err(DocumentErrors.validationError(issues));\n }\n\n return ok(result.data as T);\n}\n\n/**\n * Detailed validation result for UI display.\n */\nexport interface DocumentValidationResult<T> {\n valid: boolean;\n data?: T;\n issues?: Array<{ path: (string | number)[]; message: string }>;\n}\n\n/**\n * Validate document content with detailed error info for UI.\n */\nexport function validateDocumentContentDetailed<T>(\n schemaName: string,\n content: unknown\n): DocumentValidationResult<T> {\n const config = documentRegistry.get(schemaName);\n\n if (!config) {\n return {\n valid: false,\n issues: [{ path: [], message: `Document schema not found: ${schemaName}` }],\n };\n }\n\n const result = config.schema.safeParse(content);\n\n if (!result.success) {\n return {\n valid: false,\n issues: result.error.issues.map((issue) => ({\n path: issue.path.filter((p): p is string | number => typeof p !== 'symbol'),\n message: issue.message,\n })),\n };\n }\n\n return { valid: true, data: result.data as T };\n}\n\n// =============================================================================\n// Title Extraction\n// =============================================================================\n\n/**\n * Extract title from document content based on schema configuration.\n *\n * Tries in order:\n * 1. Schema's configured titleField\n * 2. 'title' field\n * 3. 'name' field\n * 4. 'code' field\n * 5. 'Untitled'\n */\nexport function extractDocumentTitle(schemaName: string, content: Record<string, unknown>): string {\n const config = documentRegistry.get(schemaName);\n\n // Try configured title field first\n if (config?.titleField && content[config.titleField] !== undefined) {\n return String(content[config.titleField]);\n }\n\n // Fall back to common title fields\n if (content.title !== undefined) return String(content.title);\n if (content.name !== undefined) return String(content.name);\n if (content.code !== undefined) return String(content.code);\n\n return 'Untitled';\n}\n\n// =============================================================================\n// Schema Introspection\n// =============================================================================\n\n/**\n * Get field information from a registered schema for dynamic form generation.\n */\nexport interface SchemaFieldInfo {\n name: string;\n type: string;\n isOptional: boolean;\n required: boolean;\n isArray: boolean;\n description?: string;\n /** For enum types, the available values */\n enumValues?: string[];\n /** For array types, the item type */\n itemType?: string;\n /** For reference types, the referenced schema */\n refSchema?: string;\n /** Default value if the field has one */\n defaultValue?: unknown;\n /** Whether this field has a default value */\n hasDefault?: boolean;\n}\n\n/**\n * Extract field info from a document schema for UI generation.\n */\nexport function getSchemaFields(schemaName: string): SchemaFieldInfo[] {\n const config = documentRegistry.get(schemaName);\n if (!config) return [];\n\n const shape = config.schema.shape;\n const fields: SchemaFieldInfo[] = [];\n\n for (const [name, zodType] of Object.entries(shape)) {\n const typeDef = zodType as z.ZodTypeAny;\n const isOptional = typeDef.isOptional();\n const fieldType = getFieldType(typeDef);\n const isArray = isArrayType(typeDef);\n const { value: defaultValue, hasDefault } = getDefaultValue(typeDef);\n\n const fieldInfo: SchemaFieldInfo = {\n name,\n type: fieldType,\n isOptional,\n required: !isOptional && !hasDefault, // Field is not required if it has a default\n isArray,\n description: typeDef.description,\n defaultValue,\n hasDefault,\n };\n\n // Extract enum values if this is an enum type\n // Also check if we can extract enum values even if type wasn't detected as enum\n const enumValues = getEnumValues(typeDef);\n if (enumValues.length > 0) {\n fieldInfo.enumValues = enumValues;\n // If we found enum values but type wasn't detected as enum, fix the type\n if (fieldType !== 'enum') {\n fieldInfo.type = 'enum';\n }\n }\n\n // Extract array item info\n if (isArray) {\n const arrayInfo = getArrayItemInfo(typeDef);\n fieldInfo.itemType = arrayInfo.itemType;\n fieldInfo.refSchema = arrayInfo.refSchema;\n }\n\n fields.push(fieldInfo);\n }\n\n return fields;\n}\n\nfunction getFieldType(zodType: z.ZodTypeAny): string {\n const def = zodType._def;\n if (!def) return 'unknown';\n\n // Unwrap default to get the inner type\n // ZodDefault wraps the inner type, so we need to unwrap it first\n if ('typeName' in def && def.typeName === 'ZodDefault') {\n // ZodDefault has an innerType property\n if ('innerType' in def) {\n return getFieldType(def.innerType as z.ZodTypeAny);\n }\n // Fallback: try to access _def.innerType directly\n const innerDef = (def as unknown as { innerType?: { _def?: unknown } }).innerType;\n if (innerDef?._def) {\n return getFieldType(innerDef as z.ZodTypeAny);\n }\n }\n\n // Unwrap optional/nullable (ZodOptional, ZodNullable)\n if ('innerType' in def) {\n return getFieldType(def.innerType as z.ZodTypeAny);\n }\n\n // Handle common types\n if ('typeName' in def) {\n const typeName = def.typeName as string;\n switch (typeName) {\n case 'ZodString':\n return 'string';\n case 'ZodNumber':\n return 'number';\n case 'ZodBoolean':\n return 'boolean';\n case 'ZodArray':\n return 'array';\n case 'ZodObject':\n return 'object';\n case 'ZodEnum':\n return 'enum';\n default:\n return typeName.replace('Zod', '').toLowerCase();\n }\n }\n\n return 'unknown';\n}\n\nfunction isArrayType(zodType: z.ZodTypeAny): boolean {\n const def = zodType._def;\n if (!def) return false;\n\n // Unwrap default to get the inner type\n if ('typeName' in def && def.typeName === 'ZodDefault' && 'innerType' in def) {\n return isArrayType(def.innerType as z.ZodTypeAny);\n }\n\n // Check for array type\n if ('typeName' in def && def.typeName === 'ZodArray') {\n return true;\n }\n\n // Check inside optional/nullable\n if ('innerType' in def) {\n return isArrayType(def.innerType as z.ZodTypeAny);\n }\n\n return false;\n}\n\n/**\n * Extract default value from a Zod type.\n */\nfunction getDefaultValue(zodType: z.ZodTypeAny): { value?: unknown; hasDefault: boolean } {\n const def = zodType._def;\n if (!def) return { hasDefault: false };\n\n // Check for default value in ZodDefault\n if ('typeName' in def && def.typeName === 'ZodDefault') {\n if ('defaultValue' in def) {\n const defaultValue = def.defaultValue;\n // Default value can be a function or a value\n if (typeof defaultValue === 'function') {\n try {\n return { value: defaultValue(), hasDefault: true };\n } catch {\n return { hasDefault: false };\n }\n }\n return { value: defaultValue, hasDefault: true };\n }\n }\n\n // Unwrap optional/nullable to check inner type\n if ('innerType' in def) {\n return getDefaultValue(def.innerType as z.ZodTypeAny);\n }\n\n return { hasDefault: false };\n}\n\n/**\n * Extract enum values from a Zod enum type.\n */\nfunction getEnumValues(zodType: z.ZodTypeAny): string[] {\n const def = zodType._def;\n if (!def) return [];\n\n // Unwrap default to get the inner type\n if ('typeName' in def && def.typeName === 'ZodDefault') {\n // Try to access innerType\n if ('innerType' in def) {\n return getEnumValues(def.innerType as z.ZodTypeAny);\n }\n // Fallback: try to access _def.innerType directly\n const innerDef = (def as unknown as { innerType?: z.ZodTypeAny }).innerType;\n if (innerDef) {\n return getEnumValues(innerDef);\n }\n }\n\n // Unwrap optional/nullable\n if ('innerType' in def) {\n return getEnumValues(def.innerType as z.ZodTypeAny);\n }\n\n // Get enum values from ZodEnum\n if ('typeName' in def && def.typeName === 'ZodEnum') {\n if ('values' in def && Array.isArray(def.values)) {\n return def.values as string[];\n }\n }\n\n // Also check for values property directly (some Zod versions)\n if ('values' in def && Array.isArray(def.values)) {\n return def.values as string[];\n }\n\n return [];\n}\n\n/**\n * Extract array item type information.\n */\nfunction getArrayItemInfo(zodType: z.ZodTypeAny): { itemType: string; refSchema?: string } {\n const def = zodType._def;\n if (!def) return { itemType: 'unknown' };\n\n // Unwrap optional/nullable\n if ('innerType' in def) {\n return getArrayItemInfo(def.innerType as z.ZodTypeAny);\n }\n\n // Get array element type\n if ('type' in def) {\n const itemType = def.type as unknown as z.ZodTypeAny;\n const itemDef = itemType._def as unknown as Record<string, unknown>;\n\n // Check if it's a reference object (has _type, _ref, _schema)\n if (itemDef && 'typeName' in itemDef && itemDef.typeName === 'ZodObject') {\n if ('shape' in itemDef && typeof itemDef.shape === 'function') {\n const shape = (itemDef.shape as () => Record<string, z.ZodTypeAny>)();\n if ('_type' in shape && '_ref' in shape && '_schema' in shape) {\n // This is a reference type - try to get the schema from literal\n const schemaField = shape._schema;\n const schemaDef = schemaField?._def as unknown as Record<string, unknown> | undefined;\n if (schemaDef && 'value' in schemaDef) {\n return { itemType: 'reference', refSchema: schemaDef.value as string };\n }\n return { itemType: 'reference' };\n }\n }\n }\n\n return { itemType: getFieldType(itemType) };\n }\n\n return { itemType: 'unknown' };\n}\n","/**\n * Country Document Schema.\n *\n * Defines the structure for country documents.\n * Countries have a list of supported language codes.\n */\n\nimport { z } from 'zod';\n\n// Reference to a language document (backward pass for now, I will remove this when we figure out how to handle languages)\nconst LanguageReferenceSchema = z.object({\n _type: z.literal('reference'),\n _ref: z.string().uuid('Language reference must be a valid UUID'),\n _schema: z.literal('language'),\n});\n\n/**\n * Country schema with validation rules.\n *\n * @example\n * {\n * code: 'us',\n * name: 'United States',\n * flag: '🇺🇸',\n * languages: ['en', 'es']\n * }\n *\n * @example\n * {\n * code: 'sa',\n * name: 'Saudi Arabia',\n * flag: '🇸🇦',\n * languages: ['ar', 'en']\n * }\n */\nexport const CountrySchema = z.object({\n /** 2-letter ISO 3166-1 alpha-2 country code */\n code: z.string().length(2, 'Country code must be 2 characters'),\n\n /** English name of the country */\n name: z.string().min(1, 'Country name required'),\n\n /** Flag emoji (optional but recommended) */\n flag: z.string().optional(),\n\n /**\n * List of supported languages (at least one required).\n * Supports either:\n * - Language codes (new format)\n * - Document references to languages (legacy format for backward compatibility)\n */\n languages: z\n .array(\n z.union([z.string().length(2, 'Language code must be 2 characters'), LanguageReferenceSchema])\n )\n .min(1, 'At least one language required'),\n});\n\n/** TypeScript type inferred from schema */\nexport type Country = z.infer<typeof CountrySchema>;\n\n/** Schema name constant */\nexport const COUNTRY_SCHEMA_NAME = 'country';\n\n/** Display name for UI */\nexport const COUNTRY_DISPLAY_NAME = 'Country';\n\n/** Title field for list display */\nexport const COUNTRY_TITLE_FIELD = 'name';\n\n/**\n * Create a default country object.\n */\nexport function createDefaultCountry(): Country {\n return {\n code: '',\n name: '',\n flag: '',\n languages: [],\n };\n}\n","/**\n * Language Document Schema.\n *\n * Defines the structure for language documents.\n */\n\nimport { z } from 'zod';\n\n/**\n * Language schema with validation rules.\n *\n * @example\n * {\n * code: 'en',\n * name: 'English',\n * nativeName: 'English'\n * }\n *\n * @example\n * {\n * code: 'ar',\n * name: 'Arabic',\n * nativeName: 'العربية'\n * }\n */\nexport const LanguageSchema = z.object({\n /** 2-letter ISO 639-1 language code */\n code: z.string().length(2, 'Language code must be 2 characters'),\n\n /** English name of the language */\n name: z.string().min(1, 'Language name required'),\n\n /** Name in the language itself (optional but recommended) */\n nativeName: z.string().optional(),\n});\n\n/** TypeScript type inferred from schema */\nexport type Language = z.infer<typeof LanguageSchema>;\n\n/** Schema name constant */\nexport const LANGUAGE_SCHEMA_NAME = 'language';\n\n/** Display name for UI */\nexport const LANGUAGE_DISPLAY_NAME = 'Language';\n\n/** Title field for list display */\nexport const LANGUAGE_TITLE_FIELD = 'name';\n\n/**\n * Create a default language object.\n */\nexport function createDefaultLanguage(): Language {\n return {\n code: '',\n name: '',\n nativeName: '',\n };\n}\n","/**\n * Unified Schema Registry\n *\n * Merges system schemas (hardcoded in TypeScript) with custom schemas (stored as JSON in database).\n * Both types of schemas are served through the same API, making them indistinguishable to consumers.\n *\n * System schemas take precedence over custom schemas with the same name.\n * Custom schemas are cached with a TTL and can be invalidated.\n */\n\nimport { type ZodObject, type ZodRawShape, z } from 'zod';\nimport type { TypedSupabaseClient } from '../db/types';\nimport { rehydrateSchema } from './rehydration';\nimport { createSchemaHash } from './schema-hash';\nimport type { FieldDefinition } from './schema-types';\n\n/** Source of a schema definition */\nexport type SchemaSource = 'system' | 'custom';\n\ntype TitleCandidateFieldType = FieldDefinition['type'];\n\nconst titleCandidateFieldTypes = new Set<TitleCandidateFieldType>([\n 'string',\n 'enum',\n 'url',\n 'email',\n 'date',\n 'datetime',\n]);\n\nfunction inferTitleFieldFromZodSchema(schema: ZodObject<ZodRawShape>): string {\n const shape = schema.shape ?? {};\n const keys = Object.keys(shape);\n if (keys.includes('title')) return 'title';\n if (keys.includes('name')) return 'name';\n if (keys.includes('code')) return 'code';\n return keys[0] ?? 'title';\n}\n\nfunction resolveTitleFieldFromFields(options: {\n fields: FieldDefinition[];\n titleField?: string | null;\n routeSlugField?: string | null;\n}): string | undefined {\n const { fields, titleField, routeSlugField } = options;\n\n if (titleField) {\n return titleField;\n }\n\n const fieldMap = new Map(fields.map((f) => [f.name, f] as const));\n\n if (routeSlugField) {\n const slugField = fieldMap.get(routeSlugField);\n if (slugField && titleCandidateFieldTypes.has(slugField.type)) {\n return routeSlugField;\n }\n }\n\n for (const preferredName of ['title', 'name', 'code'] as const) {\n const preferred = fieldMap.get(preferredName);\n if (preferred && titleCandidateFieldTypes.has(preferred.type)) {\n return preferredName;\n }\n }\n\n const firstCandidate = fields.find((f) => titleCandidateFieldTypes.has(f.type));\n return firstCandidate?.name;\n}\n\n/** Unified schema configuration that works for both system and custom schemas */\nexport interface UnifiedSchemaConfig {\n /** Database ID (only present for custom schemas) */\n id?: string;\n name: string;\n schema: ZodObject<ZodRawShape>;\n displayName: string;\n description?: string;\n titleField: string;\n /** Field name to use as URL routing slug. Value is auto-copied to content.code on document save. */\n routeSlugField?: string;\n icon?: string;\n source: SchemaSource;\n version?: number;\n\n /** Optional custom validation (system schemas only) */\n validateContent?: (content: unknown, supabase: TypedSupabaseClient) => Promise<void>;\n}\n\n/** Summary info for listing schemas (without full Zod schema) */\nexport interface SchemaSummary {\n /** Database ID (only present for custom schemas) */\n id?: string;\n name: string;\n displayName: string;\n description?: string;\n icon?: string;\n source: SchemaSource;\n version?: number;\n /** Field name used for URL routing - if not set, URL routing may not work */\n routeSlugField?: string;\n}\n\n// In-memory storage\nconst systemSchemas = new Map<string, UnifiedSchemaConfig>();\nconst customSchemaCacheByWebsite = new Map<string, Map<string, UnifiedSchemaConfig>>();\nconst customSchemasCacheTimeByWebsite = new Map<string, number>();\n\nfunction cacheKeyForWebsite(websiteId?: string): string {\n return websiteId ?? '__all__';\n}\n\n/** Cache TTL in milliseconds (30 seconds) */\nconst CACHE_TTL_MS = 30_000;\n\n/**\n * Registers a system schema. Called at application startup.\n * System schemas cannot be overwritten and take precedence over custom schemas.\n *\n * @param name - Unique schema identifier (e.g., 'country', 'language')\n * @param config - Schema configuration without name and source\n * @throws If schema with name already registered\n */\nexport function registerSystemSchema(\n name: string,\n config: Omit<UnifiedSchemaConfig, 'name' | 'source' | 'titleField'> & { titleField?: string }\n): void {\n if (systemSchemas.has(name)) {\n throw new Error(`System schema \"${name}\" is already registered`);\n }\n\n systemSchemas.set(name, {\n ...config,\n name,\n titleField: config.titleField ?? inferTitleFieldFromZodSchema(config.schema),\n source: 'system',\n });\n}\n\n/**\n * Gets a schema by name (system or custom).\n * System schemas are checked first, then custom schemas are loaded from cache/database.\n *\n * @param name - Schema name to look up\n * @param supabase - Database client for loading custom schemas\n * @returns Schema configuration or null if not found\n */\nexport async function getSchema(\n name: string,\n supabase: TypedSupabaseClient,\n websiteId?: string\n): Promise<UnifiedSchemaConfig | null> {\n // System schemas take precedence\n const systemSchema = systemSchemas.get(name);\n if (systemSchema) {\n return systemSchema;\n }\n\n // Load/refresh custom schemas if needed\n await loadCustomSchemas(supabase, websiteId);\n const scopedCache = customSchemaCacheByWebsite.get(cacheKeyForWebsite(websiteId));\n return scopedCache?.get(name) ?? null;\n}\n\n/**\n * Gets all registered schemas (system + active custom).\n *\n * @param supabase - Database client for loading custom schemas\n * @returns Array of all available schema configurations\n */\nexport async function getAllSchemas(\n supabase: TypedSupabaseClient,\n websiteId?: string\n): Promise<UnifiedSchemaConfig[]> {\n await loadCustomSchemas(supabase, websiteId);\n\n const scopedCache = customSchemaCacheByWebsite.get(cacheKeyForWebsite(websiteId));\n return [...systemSchemas.values(), ...(scopedCache?.values() ?? [])];\n}\n\n/**\n * Gets summary info for all schemas (without full Zod schemas).\n * Useful for sidebar/listing where full validation isn't needed.\n */\nexport async function getAllSchemaSummaries(\n supabase: TypedSupabaseClient,\n websiteId?: string\n): Promise<SchemaSummary[]> {\n const schemas = await getAllSchemas(supabase, websiteId);\n\n return schemas.map((s) => ({\n id: s.id,\n name: s.name,\n displayName: s.displayName,\n description: s.description,\n icon: s.icon,\n source: s.source,\n version: s.version,\n routeSlugField: s.routeSlugField,\n }));\n}\n\n/**\n * Loads custom schemas from database into cache.\n * Uses TTL-based caching to avoid excessive database queries.\n *\n * @param supabase - Database client\n * @param force - If true, bypasses cache TTL\n */\nasync function loadCustomSchemas(\n supabase: TypedSupabaseClient,\n websiteId?: string,\n force = false\n): Promise<void> {\n const now = Date.now();\n const key = cacheKeyForWebsite(websiteId);\n const cacheTime = customSchemasCacheTimeByWebsite.get(key) ?? null;\n\n // Use cache if valid and not forced\n if (!force && cacheTime && now - cacheTime < CACHE_TTL_MS) {\n return;\n }\n\n let query = supabase.from('custom_schemas').select('*').eq('state', 'active');\n if (websiteId) {\n query = query.eq('website_id', websiteId);\n }\n const { data, error } = await query;\n\n if (error) {\n console.error('Failed to load custom schemas:', error);\n // Keep stale cache on error rather than clearing\n return;\n }\n\n // Rebuild cache\n const scopedCache = new Map<string, UnifiedSchemaConfig>();\n\n for (const row of data ?? []) {\n try {\n const fields = row.fields as FieldDefinition[];\n const zodSchema = rehydrateSchema(fields);\n const resolvedTitleField = resolveTitleFieldFromFields({\n fields,\n titleField: row.title_field ?? undefined,\n routeSlugField: row.route_slug_field ?? undefined,\n });\n\n const hasExistingTitleField = fields.some((f) => f.name === 'title');\n const schemaWithResolvedTitle =\n resolvedTitleField === undefined && !hasExistingTitleField\n ? zodSchema.extend({ title: z.string().optional() })\n : zodSchema;\n\n const finalTitleField =\n resolvedTitleField ?? (hasExistingTitleField ? 'title' : (fields[0]?.name ?? 'title'));\n\n scopedCache.set(row.name, {\n id: row.id,\n name: row.name,\n schema: schemaWithResolvedTitle,\n displayName: row.display_name,\n description: row.description ?? undefined,\n titleField: finalTitleField,\n routeSlugField: row.route_slug_field ?? undefined,\n icon: row.icon ?? undefined,\n source: 'custom',\n version: row.version,\n });\n } catch (err) {\n // Log but don't fail - skip invalid schemas\n console.error(`Failed to rehydrate schema \"${row.name}\":`, err);\n }\n }\n\n customSchemaCacheByWebsite.set(key, scopedCache);\n customSchemasCacheTimeByWebsite.set(key, now);\n}\n\n/**\n * Invalidates the custom schema cache.\n * Call this after creating, updating, or deleting custom schemas.\n */\nexport function invalidateCustomSchemaCache(websiteId?: string): void {\n if (!websiteId) {\n customSchemasCacheTimeByWebsite.clear();\n customSchemaCacheByWebsite.clear();\n return;\n }\n\n const key = cacheKeyForWebsite(websiteId);\n customSchemasCacheTimeByWebsite.delete(key);\n customSchemaCacheByWebsite.delete(key);\n}\n\n/**\n * Forces a reload of custom schemas from database.\n */\nexport async function refreshCustomSchemas(supabase: TypedSupabaseClient): Promise<void> {\n await loadCustomSchemas(supabase, undefined, true);\n}\n\n/**\n * Checks if a schema name is reserved (used by a system schema).\n */\nexport function isSystemSchema(name: string): boolean {\n return systemSchemas.has(name);\n}\n\n/**\n * Gets all system schema names.\n */\nexport function getSystemSchemaNames(): string[] {\n return [...systemSchemas.keys()];\n}\n\n/**\n * Clears all registrations. FOR TESTING ONLY.\n */\nexport function clearUnifiedRegistry(): void {\n systemSchemas.clear();\n customSchemaCacheByWebsite.clear();\n customSchemasCacheTimeByWebsite.clear();\n}\n\n/**\n * Creates a hash for a custom schema from its fields.\n * Used for tracking schema versions in documents.\n */\nexport function createCustomSchemaHash(fields: FieldDefinition[]): string {\n const zodSchema = rehydrateSchema(fields);\n return createSchemaHash(zodSchema);\n}\n","/**\n * Zod Schema Rehydration\n *\n * Converts JSON field definitions (stored in database) into runtime Zod schemas.\n * This enables custom schemas to be validated identically to system schemas.\n *\n * @example\n * ```typescript\n * const fields: FieldDefinition[] = [\n * { name: 'title', type: 'string', required: true, constraints: { minLength: 1 } },\n * { name: 'price', type: 'number', required: true, constraints: { min: 0 } },\n * ];\n * const schema = rehydrateSchema(fields);\n * // Equivalent to: z.object({ title: z.string().min(1), price: z.number().min(0) })\n * ```\n */\n\nimport { type ZodObject, type ZodRawShape, type ZodTypeAny, z } from 'zod';\nimport { ImageReferenceSchema } from '../fields/complex/media';\nimport { validateSafeRegex } from '../utils/safe-regex';\nimport type { FieldConstraints, FieldDefinition, FieldType } from './schema-types';\n\n/** Error thrown when field definition cannot be rehydrated */\nexport class RehydrationError extends Error {\n constructor(\n message: string,\n public readonly fieldName?: string,\n public readonly fieldType?: FieldType\n ) {\n super(message);\n this.name = 'RehydrationError';\n }\n}\n\n/**\n * Rehydrates a single field definition into a Zod schema\n */\nexport function rehydrateField(field: FieldDefinition): ZodTypeAny {\n try {\n let schema = createBaseSchema(field.type, field.constraints);\n\n if (!field.required) {\n schema = schema.optional();\n }\n\n return schema;\n } catch (err) {\n throw new RehydrationError(\n `Failed to rehydrate field \"${field.name}\": ${err instanceof Error ? err.message : 'Unknown error'}`,\n field.name,\n field.type\n );\n }\n}\n\n/**\n * Creates the base Zod schema for a given field type\n */\nfunction createBaseSchema(type: FieldType, constraints?: FieldConstraints): ZodTypeAny {\n switch (type) {\n case 'string':\n return applyStringConstraints(z.string(), constraints);\n\n case 'number':\n return applyNumberConstraints(z.number(), constraints);\n\n case 'boolean':\n return z.boolean();\n\n case 'image':\n return ImageReferenceSchema;\n\n case 'date':\n return z.string().date('Invalid date format. Expected YYYY-MM-DD');\n\n case 'datetime':\n return z\n .string()\n .datetime({ offset: true, message: 'Invalid datetime format. Expected ISO 8601' });\n\n case 'url':\n return applyStringConstraints(z.string().url('Invalid URL format'), constraints);\n\n case 'email':\n return applyStringConstraints(z.string().email('Invalid email format'), constraints);\n\n case 'enum': {\n const values = constraints?.enumValues;\n if (!values || values.length === 0) {\n throw new Error('Enum field requires at least one enumValues in constraints');\n }\n return z.enum(values as [string, ...string[]]);\n }\n\n case 'reference':\n // Reference fields store inline objects (the referenced schema's field values)\n return z.record(z.string(), z.unknown());\n\n case 'array': {\n const itemType = constraints?.arrayItemType ?? 'string';\n const itemSchema = createPrimitiveSchema(itemType);\n return applyArrayConstraints(z.array(itemSchema), constraints);\n }\n\n default:\n throw new Error(`Unknown field type: ${type}`);\n }\n}\n\n/**\n * Creates a Zod schema for primitive array item types\n */\nfunction createPrimitiveSchema(type: 'string' | 'number' | 'boolean'): ZodTypeAny {\n switch (type) {\n case 'string':\n return z.string();\n case 'number':\n return z.number();\n case 'boolean':\n return z.boolean();\n }\n}\n\n/**\n * Applies string-specific constraints to a Zod string schema\n */\nfunction applyStringConstraints(schema: z.ZodString, constraints?: FieldConstraints): z.ZodString {\n if (!constraints) return schema;\n\n // Validate minLength <= maxLength when both are set\n if (\n constraints.minLength !== undefined &&\n constraints.maxLength !== undefined &&\n constraints.minLength > constraints.maxLength\n ) {\n throw new Error(\n `Invalid string constraints: minLength (${constraints.minLength}) cannot be greater than maxLength (${constraints.maxLength})`\n );\n }\n\n let result = schema;\n\n if (constraints.minLength !== undefined) {\n result = result.min(\n constraints.minLength,\n `Must be at least ${constraints.minLength} characters`\n );\n }\n if (constraints.maxLength !== undefined) {\n result = result.max(\n constraints.maxLength,\n `Must be at most ${constraints.maxLength} characters`\n );\n }\n if (constraints.pattern) {\n // Validate pattern is safe (prevents ReDoS attacks)\n const safetyError = validateSafeRegex(constraints.pattern);\n if (safetyError) {\n throw new Error(safetyError);\n }\n try {\n result = result.regex(new RegExp(constraints.pattern), 'Invalid format');\n } catch {\n throw new Error(`Invalid regex pattern: ${constraints.pattern}`);\n }\n }\n\n return result;\n}\n\n/**\n * Applies number-specific constraints to a Zod number schema\n */\nfunction applyNumberConstraints(schema: z.ZodNumber, constraints?: FieldConstraints): z.ZodNumber {\n if (!constraints) return schema;\n\n // Validate min <= max when both are set\n if (\n constraints.min !== undefined &&\n constraints.max !== undefined &&\n constraints.min > constraints.max\n ) {\n throw new Error(\n `Invalid number constraints: min (${constraints.min}) cannot be greater than max (${constraints.max})`\n );\n }\n\n let result = schema;\n\n if (constraints.min !== undefined) {\n result = result.min(constraints.min, `Must be at least ${constraints.min}`);\n }\n if (constraints.max !== undefined) {\n result = result.max(constraints.max, `Must be at most ${constraints.max}`);\n }\n if (constraints.integer) {\n result = result.int('Must be an integer');\n }\n\n return result;\n}\n\n/**\n * Applies array-specific constraints to a Zod array schema\n */\nfunction applyArrayConstraints(\n schema: z.ZodArray<ZodTypeAny>,\n constraints?: FieldConstraints\n): z.ZodArray<ZodTypeAny> {\n if (!constraints) return schema;\n\n // Validate minItems <= maxItems when both are set\n if (\n constraints.minItems !== undefined &&\n constraints.maxItems !== undefined &&\n constraints.minItems > constraints.maxItems\n ) {\n throw new Error(\n `Invalid array constraints: minItems (${constraints.minItems}) cannot be greater than maxItems (${constraints.maxItems})`\n );\n }\n\n let result = schema;\n\n if (constraints.minItems !== undefined) {\n result = result.min(constraints.minItems, `Must have at least ${constraints.minItems} items`);\n }\n if (constraints.maxItems !== undefined) {\n result = result.max(constraints.maxItems, `Must have at most ${constraints.maxItems} items`);\n }\n\n return result;\n}\n\n/**\n * Rehydrates a complete custom schema from field definitions into a Zod object schema\n *\n * @param fields - Array of field definitions from database\n * @returns Zod object schema that validates documents\n * @throws RehydrationError if any field cannot be rehydrated\n *\n * @example\n * ```typescript\n * const fields = [\n * { name: 'model', type: 'string', required: true },\n * { name: 'price', type: 'number', required: false },\n * ];\n * const schema = rehydrateSchema(fields);\n * schema.parse({ model: 'MacBook Pro' }); // Valid\n * schema.parse({ model: 'MacBook Pro', price: 1999 }); // Valid\n * schema.parse({ price: 1999 }); // Throws - model is required\n * ```\n */\nexport function rehydrateSchema(fields: FieldDefinition[]): ZodObject<ZodRawShape> {\n if (!Array.isArray(fields) || fields.length === 0) {\n throw new RehydrationError('Schema must have at least one field');\n }\n\n const shape: Record<string, ZodTypeAny> = {};\n const seenNames = new Set<string>();\n\n for (const field of fields) {\n // Validate field name uniqueness\n if (seenNames.has(field.name)) {\n throw new RehydrationError(`Duplicate field name: \"${field.name}\"`);\n }\n seenNames.add(field.name);\n\n // Validate field name format\n if (!/^[a-zA-Z][a-zA-Z0-9_]*$/.test(field.name)) {\n throw new RehydrationError(\n `Invalid field name \"${field.name}\": must start with a letter and contain only alphanumeric characters and underscores`,\n field.name\n );\n }\n\n shape[field.name] = rehydrateField(field);\n }\n\n return z.object(shape);\n}\n\n/**\n * Validates that field definitions can be rehydrated without actually building the schema.\n * Useful for pre-validation before database insertion.\n *\n * @returns null if valid, error message if invalid\n */\nexport function validateFieldDefinitions(fields: FieldDefinition[]): string | null {\n try {\n rehydrateSchema(fields);\n return null;\n } catch (err) {\n return err instanceof Error ? err.message : 'Unknown validation error';\n }\n}\n","/**\n * Image and file field factories.\n *\n * The image() factory creates an ImageReference schema that:\n * - References an ImageAsset by ID\n * - Stores context-specific alt text (required for accessibility)\n * - Supports optional hotspot (focal point) and crop settings\n */\n\nimport { z } from 'zod';\n\nimport type { FieldMeta } from '../../types';\nimport {\n DimensionSchema,\n FileSizeSchema,\n MAX_DIMENSION,\n MAX_FILE_SIZE,\n MIN_DIMENSION,\n MimeTypeSchema,\n} from '../../validation/image';\n\n// =============================================================================\n// ImageAsset Schema (Public API type)\n// =============================================================================\n\n/**\n * Public API schema for image assets.\n *\n * This is the camelCase API representation. The database uses snake_case\n * (ImageAssetRow in db/image-asset-types.ts). Conversion between formats\n * should happen at the API boundary.\n *\n * Reuses validation schemas from validation/image.ts to ensure consistency.\n */\nexport const ImageAssetSchema = z.object({\n /** UUID primary key */\n id: z.uuid(),\n /** R2/S3 storage URL for the original file */\n url: z.url(),\n /** Image width in pixels */\n width: DimensionSchema,\n /** Image height in pixels */\n height: DimensionSchema,\n /** Original filename from upload */\n originalFilename: z.string().min(1).max(255),\n /** MIME type (only web-safe formats allowed) */\n mimeType: MimeTypeSchema,\n /** File size in bytes */\n fileSize: FileSizeSchema,\n /** Base64-encoded tiny preview for blur-up loading */\n lqip: z.string().optional(),\n});\n\nexport type ImageAsset = z.infer<typeof ImageAssetSchema>;\n\n// =============================================================================\n// ImageReference Schema (Asset-reference model)\n// =============================================================================\n\n/**\n * Hotspot defines the focal point of an image.\n * Coordinates are fractions (0-1) from top-left.\n */\nexport const HotspotSchema = z.object({\n x: z.number().min(0).max(1),\n y: z.number().min(0).max(1),\n});\n\nexport type Hotspot = z.infer<typeof HotspotSchema>;\n\n/**\n * Crop defines the region to extract from the image.\n * - x, y: Top-left coordinate of the crop region in pixels\n * - width, height: Size of the crop region in pixels\n * All values are positive integers representing pixel coordinates/dimensions.\n */\nexport const CropSchema = z.object({\n /** X coordinate of top-left corner in pixels */\n x: z.number().int().nonnegative(),\n /** Y coordinate of top-left corner in pixels */\n y: z.number().int().nonnegative(),\n /** Width of crop region in pixels (must be > 0) */\n width: z.number().int().positive(),\n /** Height of crop region in pixels (must be > 0) */\n height: z.number().int().positive(),\n});\n\nexport type Crop = z.infer<typeof CropSchema>;\n\n/**\n * ImageReference is what blocks store.\n * It points to an ImageAsset and adds context-specific metadata.\n */\nexport const ImageReferenceSchema = z.object({\n // Alt text is REQUIRED for accessibility\n alt: z.string().min(1, 'Alt text is required for accessibility'),\n\n // Optional metadata\n caption: z.string().max(500).optional(),\n attribution: z.string().max(255).optional(),\n\n // Reference to the ImageAsset with stored transformation\n _asset: z.object({\n id: z.uuid(),\n transformation: z.string().nullable().optional(),\n }),\n});\n\nexport type ImageReference = z.infer<typeof ImageReferenceSchema>;\n\n// =============================================================================\n// Image Field Factory\n// =============================================================================\n\nexport interface ImageFieldOptions {\n label: string;\n description?: string;\n required?: boolean;\n accept?: string;\n maxSize?: number;\n minWidth?: number;\n minHeight?: number;\n maxWidth?: number;\n maxHeight?: number;\n aspectRatio?: number;\n}\n\n/**\n * Create an image field with metadata.\n *\n * @example\n * const featuredImage = image({\n * label: 'Featured Image',\n * required: true,\n * accept: 'image/jpeg, image/png',\n * maxSize: 5 * 1024 * 1024,\n * });\n *\n * // Type of the value:\n * // {\n * // alt: string;\n * // caption?: string;\n * // attribution?: string;\n * // _asset: { id: string; transformation?: string | null };\n * // }\n */\nexport function image(options: ImageFieldOptions) {\n const meta: FieldMeta = {\n label: options.label,\n component: 'image-uploader',\n required: options.required,\n description: options.description,\n options: {\n accept: options.accept ?? 'image/jpeg, image/png, image/webp',\n maxSize: options.maxSize ?? MAX_FILE_SIZE, // 10MB default\n minWidth: options.minWidth ?? MIN_DIMENSION,\n minHeight: options.minHeight ?? MIN_DIMENSION,\n maxWidth: options.maxWidth ?? MAX_DIMENSION,\n maxHeight: options.maxHeight ?? MAX_DIMENSION,\n aspectRatio: options.aspectRatio,\n },\n };\n\n // Use the new ImageReferenceSchema instead of URL-only\n const result = options.required ? ImageReferenceSchema : ImageReferenceSchema.optional();\n\n return result.meta(meta);\n}\n\n// =============================================================================\n// File Field\n// =============================================================================\n\nexport interface FileFieldOptions {\n label: string;\n description?: string;\n required?: boolean;\n accept?: string;\n maxSize?: number;\n}\n\nconst fileSchema = z.object({\n url: z.url(),\n name: z.string(),\n size: z.number().int().positive(),\n type: z.string(),\n});\n\n/**\n * Create a file field with metadata.\n */\nexport function file(options: FileFieldOptions) {\n const meta: FieldMeta = {\n label: options.label,\n component: 'file-uploader',\n required: options.required,\n description: options.description,\n options: {\n accept: options.accept ?? '*/*',\n maxSize: options.maxSize,\n },\n };\n\n const result = options.required ? fileSchema : fileSchema.optional();\n return result.meta(meta);\n}\n","/**\n * Image Validation Utilities\n *\n * Provides validation for:\n * - File type (MIME type checking)\n * - File size (configurable limits)\n * - Image dimensions (width/height constraints)\n */\n\nimport { z } from 'zod';\n\n// =============================================================================\n// Constants\n// =============================================================================\n\nexport const ALLOWED_MIME_TYPES = ['image/jpeg', 'image/png', 'image/webp'] as const;\nexport type ImageMimeType = (typeof ALLOWED_MIME_TYPES)[number];\n\nexport const MAX_FILE_SIZE = 10 * 1024 * 1024; // 10MB\nexport const MIN_FILE_SIZE = 1024; // 1KB (avoid empty/corrupt files)\n\nexport const MAX_DIMENSION = 8192; // 8K resolution\nexport const MIN_DIMENSION = 10; // Minimum useful size\n\n// =============================================================================\n// Zod Schemas\n// =============================================================================\n\nexport const MimeTypeSchema = z.enum(ALLOWED_MIME_TYPES);\n\nexport const FileSizeSchema = z\n .number()\n .int()\n .min(MIN_FILE_SIZE, `File too small. Minimum: ${MIN_FILE_SIZE} bytes`)\n .max(MAX_FILE_SIZE, `File too large. Maximum: ${MAX_FILE_SIZE / 1024 / 1024}MB`);\n\nexport const DimensionSchema = z\n .number()\n .int()\n .min(MIN_DIMENSION, `Dimension too small. Minimum: ${MIN_DIMENSION}px`)\n .max(MAX_DIMENSION, `Dimension too large. Maximum: ${MAX_DIMENSION}px`);\n\n/**\n * Schema for upload request validation.\n */\nexport const UploadRequestSchema = z.object({\n filename: z\n .string()\n .min(1, 'Filename is required')\n .max(255, 'Filename too long')\n .regex(/^[^<>:\"/\\\\|?*]+$/, 'Filename contains invalid characters'),\n mimeType: MimeTypeSchema,\n fileSize: FileSizeSchema,\n});\n\nexport type UploadRequest = z.infer<typeof UploadRequestSchema>;\n\n/**\n * Schema for confirming upload with dimensions.\n * assetId is optional - if not provided, the server will generate a UUID.\n */\nexport const ConfirmUploadSchema = z.object({\n assetId: z.uuid().optional(),\n width: DimensionSchema,\n height: DimensionSchema,\n});\n\nexport type ConfirmUpload = z.infer<typeof ConfirmUploadSchema>;\n\n// =============================================================================\n// Validation Functions\n// =============================================================================\n\nexport interface ValidationResult {\n valid: boolean;\n error?: string;\n}\n\n/**\n * Validate file type by MIME type.\n */\nexport function validateMimeType(mimeType: string): ValidationResult {\n const result = MimeTypeSchema.safeParse(mimeType);\n if (result.success) {\n return { valid: true };\n }\n return {\n valid: false,\n error: `File type not supported. Use JPEG, PNG, or WebP.`,\n };\n}\n\n/**\n * Validate file size.\n */\nexport function validateFileSize(size: number): ValidationResult {\n if (size < MIN_FILE_SIZE) {\n return {\n valid: false,\n error: `File appears to be empty or corrupt.`,\n };\n }\n if (size > MAX_FILE_SIZE) {\n const sizeMB = (size / 1024 / 1024).toFixed(1);\n return {\n valid: false,\n error: `File too large (${sizeMB}MB). Maximum: 10 MB.`,\n };\n }\n return { valid: true };\n}\n\n/**\n * Validate image dimensions.\n */\nexport function validateDimensions(width: number, height: number): ValidationResult {\n if (width < MIN_DIMENSION || height < MIN_DIMENSION) {\n return {\n valid: false,\n error: `Image too small. Minimum: ${MIN_DIMENSION}x${MIN_DIMENSION}px.`,\n };\n }\n if (width > MAX_DIMENSION || height > MAX_DIMENSION) {\n return {\n valid: false,\n error: `Image too large. Maximum: ${MAX_DIMENSION}x${MAX_DIMENSION}px.`,\n };\n }\n return { valid: true };\n}\n\n/**\n * Validate complete upload request.\n */\nexport function validateUploadRequest(\n request: unknown\n): { valid: true; data: UploadRequest } | { valid: false; error: string } {\n const result = UploadRequestSchema.safeParse(request);\n if (result.success) {\n return { valid: true, data: result.data };\n }\n const firstIssue = result.error.issues[0];\n return {\n valid: false,\n error: firstIssue?.message ?? 'Invalid upload request',\n };\n}\n\n// =============================================================================\n// File Extension Utilities\n// =============================================================================\n\nconst MIME_TO_EXT: Record<ImageMimeType, string> = {\n 'image/jpeg': 'jpg',\n 'image/png': 'png',\n 'image/webp': 'webp',\n};\n\nconst EXT_TO_MIME: Record<string, ImageMimeType> = {\n jpg: 'image/jpeg',\n jpeg: 'image/jpeg',\n png: 'image/png',\n webp: 'image/webp',\n};\n\n/**\n * Get file extension from MIME type.\n */\nexport function getExtensionFromMime(mimeType: ImageMimeType | string): string {\n return MIME_TO_EXT[mimeType as ImageMimeType] ?? 'bin';\n}\n\n/**\n * Get MIME type from file extension.\n */\nexport function getMimeFromExtension(ext: string): ImageMimeType | undefined {\n return EXT_TO_MIME[ext.toLowerCase()];\n}\n\n/**\n * Extract extension from filename.\n */\nexport function getExtensionFromFilename(filename: string): string {\n const parts = filename.split('.');\n return parts.pop()?.toLowerCase?.() ?? '';\n}\n\n/**\n * Sanitize filename for storage.\n * Removes special characters, preserves extension.\n */\nexport function sanitizeFilename(filename: string): string {\n // Get extension\n const ext = getExtensionFromFilename(filename);\n\n // Get base name without extension\n const base = filename.slice(0, filename.length - ext.length - 1);\n\n // Sanitize: lowercase, replace spaces and special chars\n const sanitized = base\n .toLowerCase()\n .replace(/[^a-z0-9]/g, '-')\n .replace(/-+/g, '-')\n .replace(/^-|-$/g, '')\n .slice(0, 50); // Limit length\n\n return ext ? `${sanitized}.${ext}` : sanitized;\n}\n","/**\n * Safe regex validation utilities.\n *\n * Prevents ReDoS (Regular Expression Denial of Service) attacks by\n * detecting potentially dangerous regex patterns.\n */\n\nimport safeRegex from 'safe-regex2';\n\n/**\n * Validates that a regex pattern is safe to use.\n * Uses safe-regex2 library to detect ReDoS vulnerabilities.\n *\n * @param pattern - The regex pattern to validate\n * @returns null if safe, error message if potentially dangerous\n */\nexport function validateSafeRegex(pattern: string): string | null {\n // Limit pattern length to prevent extremely long patterns\n if (pattern.length > 500) {\n return 'Regex pattern is too long (max 500 characters)';\n }\n\n // Use safe-regex2 to detect ReDoS vulnerabilities\n if (!safeRegex(pattern)) {\n return 'Regex pattern is potentially unsafe (ReDoS risk)';\n }\n\n // Try to compile the regex to ensure it's valid\n try {\n new RegExp(pattern);\n } catch (err) {\n return `Invalid regex: ${err instanceof Error ? err.message : 'Unknown'}`;\n }\n\n return null;\n}\n","/**\n * Schema hashing utility for document versioning.\n *\n * Creates a deterministic hash of a Zod schema's shape.\n * Used to detect when documents need migration after schema changes.\n */\n\nimport hash from 'object-hash';\nimport type { ZodObject, ZodRawShape, ZodTypeAny } from 'zod';\n\n/**\n * Unwraps optional, default, and nullable wrappers to get the inner type.\n */\nfunction unwrapZodType(zodType: ZodTypeAny): ZodTypeAny {\n const current = zodType;\n const def = current._def;\n\n // Unwrap optional/nullable/default wrappers\n if ('innerType' in def && def.innerType) {\n return unwrapZodType(def.innerType as ZodTypeAny);\n }\n\n return current;\n}\n\n/**\n * Gets the type name from a Zod type, unwrapping wrappers first.\n * In Zod v4, types are stored in _def.type (not typeName).\n */\nfunction getZodTypeName(zodType: ZodTypeAny): string {\n const unwrapped = unwrapZodType(zodType);\n const def = unwrapped._def;\n\n if (!def) return 'unknown';\n\n // Zod v4 uses _def.type for primitive types\n if ('type' in def && typeof def.type === 'string') {\n return def.type as string;\n }\n\n // Fallback: check for typeName (older Zod versions or complex types)\n if ('typeName' in def) {\n return def.typeName as string;\n }\n\n return 'unknown';\n}\n\n/**\n * Creates a deterministic hash of a Zod schema's shape.\n *\n * Uses object-hash library for canonical key ordering, ensuring\n * the same hash is produced regardless of property definition order.\n *\n * @param schema - A Zod object schema\n * @returns MD5 hash string of the schema shape\n */\nexport function createSchemaHash(schema: ZodObject<ZodRawShape>): string {\n const shape = schema.shape;\n\n // Create a serializable representation of the schema\n const schemaDefinition = Object.entries(shape).reduce(\n (acc, [key, zodType]) => {\n const typeName = getZodTypeName(zodType as ZodTypeAny);\n const unwrapped = unwrapZodType(zodType as ZodTypeAny);\n const unwrappedDef = unwrapped._def as unknown as Record<string, unknown>;\n\n // Check if optional (method exists on ZodTypeAny in Zod v4)\n const isOptional =\n 'isOptional' in zodType && typeof zodType.isOptional === 'function'\n ? zodType.isOptional()\n : false;\n\n acc[key] = {\n type: typeName,\n optional: isOptional,\n // Include nested shape for objects\n ...(typeName === 'object' &&\n 'shape' in unwrappedDef &&\n typeof unwrappedDef.shape === 'function' && {\n shape: Object.keys((unwrappedDef.shape as () => Record<string, unknown>)()),\n }),\n };\n return acc;\n },\n {} as Record<string, unknown>\n );\n\n return hash(schemaDefinition, {\n algorithm: 'md5',\n respectType: false,\n unorderedArrays: false,\n });\n}\n","/**\n * Country schema-specific validation.\n *\n * Validates that language codes exist in the database.\n */\n\nimport { TRPCError } from '@trpc/server';\nimport type { ReferenceValue } from '../../db/document-types';\nimport { isReference } from '../../db/document-types';\nimport type { TypedSupabaseClient } from '../../db/types';\n\n/**\n * Validate country content, specifically checking that language codes exist.\n *\n * @param content - Validated country content\n * @param supabase - Supabase client for database queries\n * @throws {TRPCError} If validation fails\n */\nexport async function validateCountryContent(\n content: unknown,\n supabase: TypedSupabaseClient\n): Promise<void> {\n const countryContent = content as {\n languages?: Array<string | ReferenceValue>;\n };\n const languages = Array.isArray(countryContent.languages) ? countryContent.languages : [];\n\n const languageCodes = languages.filter((lang): lang is string => typeof lang === 'string');\n const languageReferences = languages.filter(isReference);\n\n // Validate legacy reference format points to language schema\n const invalidReferences = languageReferences.filter((ref) => ref._schema !== 'language');\n if (invalidReferences.length > 0) {\n throw new TRPCError({\n code: 'BAD_REQUEST',\n message: 'Language references must target the language schema',\n });\n }\n\n // Only perform database check when using code-based languages\n if (languageCodes.length > 0) {\n const { data: allLanguages, error: langError } = await supabase\n .from('documents')\n .select('content')\n .eq('schema_name', 'language');\n\n if (langError) {\n throw new TRPCError({\n code: 'INTERNAL_SERVER_ERROR',\n message: 'Failed to validate language codes',\n cause: langError,\n });\n }\n\n const existingCodes = new Set(\n (allLanguages || [])\n .map((lang) => {\n const langContent = lang.content as { code?: string };\n return langContent?.code;\n })\n .filter((code): code is string => typeof code === 'string' && code.length > 0)\n );\n\n const missingCodes = languageCodes.filter((code) => !existingCodes.has(code));\n\n if (missingCodes.length > 0) {\n throw new TRPCError({\n code: 'BAD_REQUEST',\n message: `Language codes not found: ${missingCodes.join(', ')}. Please create language documents with these codes first.`,\n });\n }\n }\n}\n","/**\n * CMS API Client\n *\n * Creates an HTTP-based tRPC client for calling the CMS API.\n * Used in Server Components to fetch routes and blocks from the CMS.\n */\n\nimport type { AppRouter } from '@repo/cms-schema/trpc';\nimport { type CreateTRPCClient, createTRPCClient, httpBatchLink } from '@trpc/client';\nimport superjson from 'superjson';\n\n/** Type alias for the CMS API client */\ntype CmsClient = CreateTRPCClient<AppRouter>;\n\n/**\n * Get the CMS API URL from the provided base URL.\n */\nfunction getCmsApiUrl(cmsUrl: string): string {\n return new URL('/api/trpc', cmsUrl).toString();\n}\n\n/** Unified configuration for all CMS API access */\nexport interface CmsConfig {\n /** CMS API base URL (e.g., 'http://localhost:3000') */\n cmsUrl: string;\n /** API key for authentication */\n apiKey?: string;\n /** Website ID to scope API requests */\n websiteId?: string;\n}\n\n/**\n * Create a custom fetch function that appends API key as query parameter.\n */\nfunction createFetchWithApiKey(apiKey?: string, websiteId?: string) {\n return async (url: URL | RequestInfo, options?: RequestInit): Promise<Response> => {\n let finalUrl = url;\n\n const urlObj = new URL(url.toString());\n if (apiKey) {\n urlObj.searchParams.set('api_key', apiKey);\n }\n if (websiteId) {\n urlObj.searchParams.set('website_id', websiteId);\n }\n if (apiKey || websiteId) {\n finalUrl = urlObj.toString();\n }\n\n const response = await fetch(finalUrl, options);\n\n return response;\n };\n}\n\n/**\n * Create a tRPC client for the CMS API.\n */\nfunction createCmsClient(options: CmsConfig): CmsClient {\n const url = getCmsApiUrl(options.cmsUrl);\n\n return createTRPCClient<AppRouter>({\n links: [\n httpBatchLink({\n url,\n transformer: superjson,\n fetch: createFetchWithApiKey(options.apiKey, options.websiteId),\n }),\n ],\n });\n}\n\n/**\n * Get a CMS client for the specified CMS URL.\n */\nexport function getCmsClient(options: CmsConfig): CmsClient {\n return createCmsClient(options);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AACA,WAAO,eAAe,SAAS,cAAc,EAAE,OAAO,KAAK,CAAC;AAAA;AAAA;;;;;;;;ACD5D,QAAY;AAAZ,KAAA,SAAYA,QAAK;AACf,MAAAA,OAAAA,OAAA,MAAA,IAAA,CAAA,IAAA;AACA,MAAAA,OAAAA,OAAA,OAAA,IAAA,CAAA,IAAA;AACA,MAAAA,OAAAA,OAAA,UAAA,IAAA,CAAA,IAAA;AACA,MAAAA,OAAAA,OAAA,KAAA,IAAA,CAAA,IAAA;AACA,MAAAA,OAAAA,OAAA,OAAA,IAAA,CAAA,IAAA;AACA,MAAAA,OAAAA,OAAA,YAAA,IAAA,CAAA,IAAA;AACA,MAAAA,OAAAA,OAAA,WAAA,IAAA,CAAA,IAAA;AACA,MAAAA,OAAAA,OAAA,MAAA,IAAA,CAAA,IAAA;IACF,GATY,QAAA,QAAA,UAAA,QAAA,QAAK,CAAA,EAAA;;;;;ACAjB;AAAA;AAAA;AACA,WAAO,eAAe,SAAS,cAAc,EAAE,OAAO,KAAK,CAAC;AAAA;AAAA;;;;;;;;;;;;;;;;;;;ACD5D,iBAAA,kBAAA,OAAA;AACA,iBAAA,iBAAA,OAAA;AACA,iBAAA,sBAAA,OAAA;;;;;;;;;;ACFA,QAAA,UAAA;AAKA,QAAM,OAAiB,MAAM,CAAC,EAAE,MAAM,QAAA,MAAM,OAAO,MAAM,IAAI,IAAI,GAAE,CAAE;AAErE,QAAM,QAAkB,MAAM;MAC5B,EAAE,MAAM,QAAA,MAAM,MAAM,OAAO,GAAE;MAC7B,EAAE,MAAM,QAAA,MAAM,OAAO,MAAM,IAAI,IAAI,IAAG;MACtC,EAAE,MAAM,QAAA,MAAM,OAAO,MAAM,IAAI,IAAI,GAAE;MACrC,EAAE,MAAM,QAAA,MAAM,OAAO,MAAM,IAAI,IAAI,GAAE;;AAGvC,QAAM,aAAuB,MAAM;MACjC,EAAE,MAAM,QAAA,MAAM,MAAM,OAAO,EAAC;MAC5B,EAAE,MAAM,QAAA,MAAM,MAAM,OAAO,GAAE;MAC7B,EAAE,MAAM,QAAA,MAAM,MAAM,OAAO,GAAE;MAC7B,EAAE,MAAM,QAAA,MAAM,MAAM,OAAO,GAAE;MAC7B,EAAE,MAAM,QAAA,MAAM,MAAM,OAAO,GAAE;MAC7B,EAAE,MAAM,QAAA,MAAM,MAAM,OAAO,GAAE;MAC7B,EAAE,MAAM,QAAA,MAAM,MAAM,OAAO,IAAG;MAC9B,EAAE,MAAM,QAAA,MAAM,MAAM,OAAO,KAAI;MAC/B,EAAE,MAAM,QAAA,MAAM,OAAO,MAAM,MAAM,IAAI,KAAI;MACzC,EAAE,MAAM,QAAA,MAAM,MAAM,OAAO,KAAI;MAC/B,EAAE,MAAM,QAAA,MAAM,MAAM,OAAO,KAAI;MAC/B,EAAE,MAAM,QAAA,MAAM,MAAM,OAAO,KAAI;MAC/B,EAAE,MAAM,QAAA,MAAM,MAAM,OAAO,KAAI;MAC/B,EAAE,MAAM,QAAA,MAAM,MAAM,OAAO,MAAK;MAChC,EAAE,MAAM,QAAA,MAAM,MAAM,OAAO,MAAK;;AAGlC,QAAM,aAAuB,MAAM;MACjC,EAAE,MAAM,QAAA,MAAM,MAAM,OAAO,GAAE;MAC7B,EAAE,MAAM,QAAA,MAAM,MAAM,OAAO,GAAE;MAC7B,EAAE,MAAM,QAAA,MAAM,MAAM,OAAO,KAAI;MAC/B,EAAE,MAAM,QAAA,MAAM,MAAM,OAAO,KAAI;;AAIpB,YAAA,QAAiB,OAAO,EAAE,MAAM,QAAA,MAAM,KAAK,KAAK,MAAK,GAAI,KAAK,MAAK;AACnE,YAAA,WAAoB,OAAO,EAAE,MAAM,QAAA,MAAM,KAAK,KAAK,MAAK,GAAI,KAAK,KAAI;AACrE,YAAA,OAAgB,OAAO,EAAE,MAAM,QAAA,MAAM,KAAK,KAAK,KAAI,GAAI,KAAK,MAAK;AACjE,YAAA,UAAmB,OAAO,EAAE,MAAM,QAAA,MAAM,KAAK,KAAK,KAAI,GAAI,KAAK,KAAI;AACnE,YAAA,aAAsB,OAAO,EAAE,MAAM,QAAA,MAAM,KAAK,KAAK,WAAU,GAAI,KAAK,MAAK;AAC7E,YAAA,gBAAyB,OAAO,EAAE,MAAM,QAAA,MAAM,KAAK,KAAK,WAAU,GAAI,KAAK,KAAI;AAC/E,YAAA,UAAmB,OAAO,EAAE,MAAM,QAAA,MAAM,KAAK,KAAK,WAAU,GAAI,KAAK,KAAI;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AC9CtF,QAAA,UAAA;AACA,QAAA,OAAA,aAAA,cAAA;AAEA,QAAM,OAAO;AASA,YAAA,aAAa,CAAC,QAAuB;AAChD,YAAM,aAAa;AAEnB,aAAO,IAAI,QAAQ,YAAY,CAAC,GAAG,GAAG,KAAK,KAAK,KAAK,OAAO,UAAuB;AACjF,YAAI,KAAK;AACP,iBAAO;;AAGT,YAAI,OAAe,IAAI,IACrB,MAAM,SAAS,KAAK,EAAE,IACpB,MAAM,SAAS,KAAK,EAAE,IACpB,QAAQ,KAAK,QAAQ,KAAK,IAAI;UAC5B,GAAG;UACH,GAAG;UACH,GAAG;UACH,GAAG;UACH,GAAG;UACH,GAAG;UACH,KAAK;AAEb,YAAI,IAAI,OAAO,aAAa,IAAI;AAGhC,eAAO,mBAAmB,KAAK,CAAC,IAAI,KAAK,CAAC,KAAK;MACjD,CAAC;IACH;AAWa,YAAA,gBAAgB,CAAC,KAAa,cAA0C;;AACnF,UAAI,SAAoB,CAAA,GAAI,IAAqB;AACjD,YAAM,SACN;AAEA,cAAQ,KAAK,OAAO,KAAK,GAAG,OAAO,MAAM;AACvC,cAAM,KAAC,MAAA,MAAA,MAAA,MAAA,MAAA,MAAA,KAAI,GAAG,CAAC,KAAK,KAAK,MAAK,OAAG,QAAA,OAAA,SAAA,KAC9B,GAAG,CAAC,KAAK,KAAK,KAAI,OAAG,QAAA,OAAA,SAAA,KACrB,GAAG,CAAC,KAAK,KAAK,WAAU,OAAG,QAAA,OAAA,SAAA,KAC3B,GAAG,CAAC,KAAK,KAAK,SAAQ,OAAG,QAAA,OAAA,SAAA,KACzB,GAAG,CAAC,KAAK,KAAK,QAAO,OAAG,QAAA,OAAA,SAAA,KACxB,GAAG,CAAC,KAAK,KAAK,cAAa,OAAG,QAAA,OAAA,SAAA,KAC9B,GAAG,CAAC,KAAK;UACR,MAAM,QAAA,MAAM;UACZ,OAAO,GAAG,CAAC,KAAK,GAAG,CAAC,GAAG,WAAW,CAAC;UACnC,KAAK,IAAI,GAAG,EAAE,GAAG,WAAW,EAAE,SAAS,CAAC;eACxC,QAAA,OAAA,SAAA,MACA,IAAI,GAAG,EAAE,MAAM,EAAE,MAAM,QAAA,MAAM,MAAM,OAAO,EAAE,WAAW,CAAC,EAAC;AAE7D,YAAI,GAAG;AACL,iBAAO,KAAK,CAAC;eACR;AACL,iBAAO,CAAC,QAAQ,OAAO,SAAS;;;AAGpC,YAAM,IAAI,YAAY,gCAAgC,SAAS,iCAAiC;IAClG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AC1EA,QAAA,OAAA,aAAA,cAAA;AACA,QAAA,UAAA;AACA,QAAA,OAAA,aAAA,cAAA;AAOA,QAAM,wBAAwB;AAK9B,QAAM,oBAAoB;AAE1B,QAAM,QAAQ;AAQD,YAAA,YAAY,CAAC,cAA2B;AACnD,UAAI,IAAI,GAAG;AACX,UAAI,QAAc,EAAE,MAAM,QAAA,MAAM,MAAM,OAAO,CAAA,EAAE;AAG/C,UAAI,YAA0B;AAC9B,UAAI,OAAgB,MAAM;AAC1B,UAAI,aAA+B,CAAA;AAEnC,UAAI,iBAAiC,CAAA;AACrC,UAAI,aAAa;AAEjB,YAAM,YAAY,CAAC,QAAe;AAChC,cAAM,IAAI,YACR,gCACE,SACF,kCAAkC,MAAM,CAAC,EAAE;MAE/C;AAGA,UAAI,MAAM,KAAK,WAAW,SAAS;AAGnC,aAAO,IAAI,IAAI,QAAQ;AACrB,gBAAQ,IAAI,IAAI,GAAG,GAAG;;UAEpB,KAAK;AACH,gBAAI,MAAM,IAAI,QAAQ;AACpB,oBAAM,IAAI,YACR,gCACE,SACF,yBAAyB;;AAG7B,oBAAQ,IAAI,IAAI,GAAG,GAAG;cACpB,KAAK;AACH,qBAAK,KAAK,EAAE,MAAM,QAAA,MAAM,UAAU,OAAO,IAAG,CAAE;AAC9C;cAEF,KAAK;AACH,qBAAK,KAAK,EAAE,MAAM,QAAA,MAAM,UAAU,OAAO,IAAG,CAAE;AAC9C;cAEF,KAAK;AACH,qBAAK,KAAK,KAAK,MAAK,CAAE;AACtB;cAEF,KAAK;AACH,qBAAK,KAAK,KAAK,SAAQ,CAAE;AACzB;cAEF,KAAK;AACH,qBAAK,KAAK,KAAK,KAAI,CAAE;AACrB;cAEF,KAAK;AACH,qBAAK,KAAK,KAAK,QAAO,CAAE;AACxB;cAEF,KAAK;AACH,qBAAK,KAAK,KAAK,WAAU,CAAE;AAC3B;cAEF,KAAK;AACH,qBAAK,KAAK,KAAK,cAAa,CAAE;AAC9B;cAEF;AAGE,oBAAI,MAAM,KAAK,CAAC,GAAG;AACjB,sBAAI,SAAS;AAEb,yBAAO,IAAI,IAAI,UAAU,MAAM,KAAK,IAAI,CAAC,CAAC,GAAG;AAC3C,8BAAU,IAAI,GAAG;;AAGnB,sBAAI,QAAQ,SAAS,QAAQ,EAAE;AAC/B,wBAAM,YAAuB,EAAE,MAAM,QAAA,MAAM,WAAW,MAAK;AAE3D,uBAAK,KAAK,SAAS;AACnB,iCAAe,KAAK,EAAE,WAAW,OAAO,MAAM,OAAO,KAAK,SAAS,EAAC,CAAE;uBAGjE;AACL,uBAAK,KAAK,EAAE,MAAM,QAAA,MAAM,MAAM,OAAO,EAAE,WAAW,CAAC,EAAC,CAAE;;;AAI5D;;UAIF,KAAK;AACH,iBAAK,KAAK,EAAE,MAAM,QAAA,MAAM,UAAU,OAAO,IAAG,CAAE;AAC9C;UAEF,KAAK;AACH,iBAAK,KAAK,EAAE,MAAM,QAAA,MAAM,UAAU,OAAO,IAAG,CAAE;AAC9C;;UAIF,KAAK,KAAK;AAER,gBAAI;AACJ,gBAAI,IAAI,CAAC,MAAM,KAAK;AAClB,oBAAM;AACN;mBACK;AACL,oBAAM;;AAIR,gBAAI,cAAc,KAAK,cAAc,IAAI,MAAM,CAAC,GAAG,SAAS;AAG5D,iBAAK,YAAY,CAAC;AAClB,iBAAK,KAAK;cACR,MAAM,QAAA,MAAM;cACZ,KAAK,YAAY,CAAC;cAClB;aACD;AAED;;;UAKF,KAAK;AACH,iBAAK,KAAK,KAAK,QAAO,CAAE;AACxB;;UAIF,KAAK,KAAK;AAER,gBAAI,QAAe;cACjB,MAAM,QAAA,MAAM;cACZ,OAAO,CAAA;cACP,UAAU;;AAIZ,gBAAI,IAAI,CAAC,MAAM,KAAK;AAClB,kBAAI,IAAI,IAAI,CAAC;AACb,mBAAK;AAGL,kBAAI,MAAM,KAAK;AACb,sBAAM,aAAa;AACnB,sBAAM,WAAW;yBAGR,MAAM,KAAK;AACpB,sBAAM,gBAAgB;AACtB,sBAAM,WAAW;yBACR,MAAM,KAAK;AACpB,oBAAI,OAAO;AAEX,oBAAI,sBAAsB,KAAK,IAAI,CAAC,CAAC,GAAG;AACtC,0BAAQ,IAAI,CAAC;AACb;uBACK;AACL,wBAAM,IAAI,YACR,gCACE,SACF,6CAA6C,IAAI,CAAC,CAAC,yBAC3B,IAAI,CAAC,EAAE;;AAInC,uBAAO,IAAI,IAAI,UAAU,kBAAkB,KAAK,IAAI,CAAC,CAAC,GAAG;AACvD,0BAAQ,IAAI,CAAC;AACb;;AAGF,oBAAI,CAAC,MAAM;AACT,wBAAM,IAAI,YACR,gCACE,SACF,6CAA6C,IAAI,CAAC,CAAC,yBAC3B,IAAI,CAAC,EAAE;;AAInC,oBAAI,IAAI,CAAC,MAAM,KAAK;AAClB,wBAAM,IAAI,YACR,gCACE,SACF,wDACK,IAAI,CAAC,CAAC,eAAe,IAAI,CAAC,EAAE;;AAIrC,sBAAM,OAAO;AACb;yBACS,MAAM,KAAK;AACpB,sBAAM,WAAW;qBACZ;AACL,sBAAM,IAAI,YACR,gCACE,SACF,gCAAgC,CAAC,yBACT,IAAI,CAAC,EAAE;;mBAG9B;AACL,4BAAc;;AAIhB,iBAAK,KAAK,KAAK;AAGf,uBAAW,KAAK,SAAS;AAGzB,wBAAY;AACZ,mBAAO,MAAM;AAEb;;;UAKF,KAAK;AACH,gBAAI,WAAW,WAAW,GAAG;AAC3B,oBAAM,IAAI,YACR,gCACE,SACF,4BAA4B,IAAI,CAAC,EAAE;;AAGvC,wBAAY,WAAW,IAAG;AAI1B,mBAAO,UAAU,UACf,UAAU,QAAQ,UAAU,QAAQ,SAAS,CAAC,IAC9C,UAAU;AAEZ;;UAIF,KAAK,KAAK;AAGR,gBAAI,CAAC,UAAU,SAAS;AACtB,wBAAU,UAAU,CAAC,UAAU,KAAK;AACpC,qBAAO,UAAU;;AAGnB,gBAAI,QAAiB,CAAA;AACrB,sBAAU,QAAQ,KAAK,KAAK;AAC5B,mBAAO;AAEP;;;;;;;UASF,KAAK,KAAK;AACR,gBAAI,KAAK,qBAAqB,KAAK,IAAI,MAAM,CAAC,CAAC,GAAG,KAAK;AACvD,gBAAI,OAAO,MAAM;AACf,kBAAI,KAAK,WAAW,GAAG;AACrB,0BAAU,CAAC;;AAEb,oBAAM,SAAS,GAAG,CAAC,GAAG,EAAE;AACxB,oBAAM,GAAG,CAAC,IAAI,GAAG,CAAC,IAAI,SAAS,GAAG,CAAC,GAAG,EAAE,IAAI,WAAW;AACvD,mBAAK,GAAG,CAAC,EAAE;AAEX,mBAAK,KAAK;gBACR,MAAM,QAAA,MAAM;gBACZ;gBACA;gBACA,OAAO,KAAK,IAAG;eAChB;mBACI;AACL,mBAAK,KAAK;gBACR,MAAM,QAAA,MAAM;gBACZ,OAAO;eACR;;AAGH;;UAGF,KAAK;AACH,gBAAI,KAAK,WAAW,GAAG;AACrB,wBAAU,CAAC;;AAEb,iBAAK,KAAK;cACR,MAAM,QAAA,MAAM;cACZ,KAAK;cACL,KAAK;cACL,OAAO,KAAK,IAAG;aAChB;AACD;UAEF,KAAK;AACH,gBAAI,KAAK,WAAW,GAAG;AACrB,wBAAU,CAAC;;AAEb,iBAAK,KAAK;cACR,MAAM,QAAA,MAAM;cACZ,KAAK;cACL,KAAK;cACL,OAAO,KAAK,IAAG;aAChB;AAED;UAEF,KAAK;AACH,gBAAI,KAAK,WAAW,GAAG;AACrB,wBAAU,CAAC;;AAEb,iBAAK,KAAK;cACR,MAAM,QAAA,MAAM;cACZ,KAAK;cACL,KAAK;cACL,OAAO,KAAK,IAAG;aAChB;AAED;;UAIF;AACE,iBAAK,KAAK;cACR,MAAM,QAAA,MAAM;cACZ,OAAO,EAAE,WAAW,CAAC;aACtB;;;AAKP,UAAI,WAAW,WAAW,GAAG;AAC3B,cAAM,IAAI,YACR,gCACE,SACF,uBAAuB;;AAI3B,uBAAiB,gBAAgB,UAAU;AAE3C,aAAO;IACT;AAWA,aAAS,iBAAiB,gBAAgC,YAAkB;AAI1E,iBAAW,QAAQ,eAAe,QAAO,GAAI;AAC3C,YAAI,aAAa,KAAK,UAAU,OAAO;AAErC,eAAK,UAAU,OAAO,QAAA,MAAM;AAE5B,gBAAM,cAAc,KAAK,UAAU,MAAM,SAAQ;AACjD,eAAK,UAAU,QAAQ,SAAS,aAAa,CAAC;AAG9C,cAAI,CAAC,WAAW,KAAK,WAAW,GAAG;AACjC,gBAAI,IAAI;AAER,mBAAO,YAAY,CAAC,MAAM,OAAO,YAAY,CAAC,MAAM,KAAK;AACvD,mBAAK;;AAGP,gBAAI,MAAM,GAAG;AAEX,mBAAK,UAAU,QAAQ,YAAY,WAAW,CAAC;AAC/C,mBAAK;mBACA;AAIL,mBAAK,UAAU,QAAQ,SAAS,YAAY,MAAM,GAAG,CAAC,GAAG,CAAC;;AAG5D,gBAAI,YAAY,SAAS,GAAG;AAC1B,oBAAM,OAAO,KAAK,MAAM,OAAO,KAAK,QAAQ,CAAC;AAE7C,yBAAW,QAAQ,YAAY,MAAM,CAAC,GAAG;AACvC,qBAAK,MAAM,KAAK;kBACd,MAAM,QAAA,MAAM;kBACZ,OAAO,KAAK,WAAW,CAAC;iBACzB;;AAEH,mBAAK,MAAM,KAAK,GAAG,IAAI;;;;;IAKjC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AClbA,QAAA,OAAA,aAAA,cAAA;AACA,QAAA,UAAA;AAEA,aAAS,YAAY,QAAiB;AACpC,UAAI,SAA2C,CAAA;AAC/C,UAAI,MAAM;AACV,iBAAW,SAAS,QAAQ;AAC1B,YAAI,MAAM,SAAS,QAAA,MAAM,MAAM;AAC7B,iBAAO,MAAM,KAAK,IAAI;;AAOxB,YAAI,MAAM,SAAS,QAAA,MAAM,OAAO;AAC9B,iBAAO,GAAG,MAAM,IAAI,IAAI,MAAM,EAAE,EAAE,IAAI;;AAExC,eAAO;;AAET,aAAO;QACL,QAAQ,MAAM,OAAA,OAAA,CAAA,GAAM,MAAM;QAC1B;;IAEJ;AAEa,YAAA,OAAO,YAAY,KAAK,KAAI,EAAG,GAAG;AAClC,YAAA,QAAQ,YAAY,KAAK,MAAK,EAAG,GAAG;AACpC,YAAA,aAAa,YAAY,KAAK,WAAU,EAAG,GAAG;AAC9C,YAAA,aAAa,YAAY,KAAK,QAAO,EAAG,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AC7BxD,QAAA,UAAA;AACA,QAAA,OAAA,aAAA,qBAAA;AAQA,aAAgB,QAAQ,UAAgB;AACtC,aAAO,aAAa,KAAK,QACvB,aAAa,KAAK,SAChB,aAAa,KAAK,QAChB,aAAa,KAAK,QAChB,OAAO,aAAa,QAAQ;IACtC;AANA,YAAA,UAAA;AAcA,aAAS,UAAU,KAAgB,EAAE,QAAQ,IAAG,GAAa;AAG3D,UAAI,QAAQ,IAAI,QAAQ;AACtB,eAAO;;AAET,YAAM,MAAM,OAAM;AAClB,iBAAW,QAAQ,KAAK;AACtB,YAAI,KAAK,SAAS,QAAA,MAAM,KAAK;AAC3B,iBAAO;;AAET,cAAM,MAAM,KAAK,SAAS,QAAA,MAAM,OAAO,KAAK,QAAQ,GAAG,KAAK,IAAI,IAAI,KAAK,EAAE;AAC3E,YAAI,IAAI,GAAG,GAAG;AACZ,cAAI,GAAG,IAAI;eACN;AACL,iBAAO;;;AAGX,aAAO;IACT;AAQA,aAAgB,eAAe,KAAU,WAAW,OAAK;AACvD,UAAI,UAAU,IAAI,KAAK,KAAK,IAAI,GAAG;AACjC,eAAO,IAAI,MAAM,QAAQ;;AAE3B,UAAI,UAAU,IAAI,KAAK,KAAK,KAAK,GAAG;AAClC,eAAO,IAAI,MAAM,QAAQ;;AAG3B,UAAI,IAAI,OAAO,UAAU,IAAI,KAAK,KAAK,UAAU,GAAG;AAClD,eAAO;;AAET,UAAI,UAAU,IAAI,KAAK,KAAK,UAAU,GAAG;AACvC,eAAO,IAAI,MAAM,QAAQ;;AAE3B,UAAI,cAAc;AAClB,eAAS,IAAI,GAAG,IAAI,IAAI,IAAI,QAAQ,KAAK;AACvC,cAAM,SAAS,IAAI,IAAI,CAAC;AACxB,uBAAe,cAAc,MAAM;;AAErC,YAAM,WAAW,GAAG,IAAI,MAAM,MAAM,EAAE,GAAG,WAAW;AACpD,aAAO,WAAW,WAAW,IAAI,QAAQ;IAC3C;AArBA,YAAA,iBAAA;AA4BA,aAAS,cAAc,KAAuB;AAC5C,UAAI,IAAI,SAAS,QAAA,MAAM,MAAM;AAC3B,eAAO,QAAQ,IAAI,KAAK;iBACf,IAAI,SAAS,QAAA,MAAM,OAAO;AACnC,eAAO,GAAG,QAAQ,IAAI,IAAI,CAAC,IAAI,QAAQ,IAAI,EAAE,CAAC;;AAEhD,aAAO,eAAe,KAAK,IAAI;IACjC;;;;;;;;;;ACrFA,QAAA,UAAA;AACA,QAAA,qBAAA;AAEA,QAAM,cAAc,CAAC,UAA2B,MAAM,IAAI,QAAA,WAAW,EAAE,KAAK,EAAE;AAE9E,QAAM,kBAAkB,CAAC,UAA+B;AACtD,UAAI,aAAa,OAAO;AACtB,eAAO,MAAM,QAAQ,IAAI,WAAW,EAAE,KAAK,GAAG;iBACrC,WAAW,OAAO;AAC3B,eAAO,YAAY,MAAM,KAAK;aACzB;AACL,cAAM,IAAI,MAAM,8CAA8C;;IAElE;AAEa,YAAA,cAAc,CAAC,UAAyB;AACnD,cAAQ,MAAM,MAAM;QAClB,KAAK,QAAA,MAAM;AACT,iBAAO,gBAAgB,KAAK;QAC9B,KAAK,QAAA,MAAM,MAAM;AACf,gBAAM,IAAI,OAAO,aAAa,MAAM,KAAK;AAGzC,kBAAQ,mBAAmB,KAAK,CAAC,IAAI,OAAO,MAAM;;QAEpD,KAAK,QAAA,MAAM;AACT,cAAI,MAAM,UAAU,OAAO,MAAM,UAAU,KAAK;AAC9C,mBAAO,MAAM;iBACR;AACL,mBAAO,KAAK,MAAM,KAAK;;QAE3B,KAAK,QAAA,MAAM;AACT,iBAAO,KAAK,MAAM,KAAK;QACzB,KAAK,QAAA,MAAM;AACT,iBAAO,mBAAA,eAAe,KAAK;QAC7B,KAAK,QAAA,MAAM,OAAO;AAEhB,gBAAM,SACJ,MAAM,OAAO,KAAK,MAAM,IAAI,MAC1B,MAAM,WAAW,KACf,MAAM,aAAa,OACjB,MAAM,gBAAgB,OACpB;AACV,iBAAO,IAAI,MAAM,GAAG,gBAAgB,KAAK,CAAC;;QAE5C,KAAK,QAAA,MAAM,YAAY;AACrB,gBAAM,EAAE,KAAK,IAAG,IAAK;AACrB,cAAI;AACJ,cAAI,QAAQ,KAAK,QAAQ,GAAG;AAC1B,sBAAU;qBACD,QAAQ,KAAK,QAAQ,UAAU;AACxC,sBAAU;qBACD,QAAQ,KAAK,QAAQ,UAAU;AACxC,sBAAU;qBACD,QAAQ,UAAU;AAC3B,sBAAU,IAAI,GAAG;qBACR,QAAQ,KAAK;AACtB,sBAAU,IAAI,GAAG;iBACZ;AACL,sBAAU,IAAI,GAAG,IAAI,GAAG;;AAE1B,iBAAO,GAAG,QAAA,YAAY,MAAM,KAAK,CAAC,GAAG,OAAO;;QAE9C,KAAK,QAAA,MAAM;AACT,iBAAO,GAAG,mBAAA,QAAQ,MAAM,IAAI,CAAC,IAAI,mBAAA,QAAQ,MAAM,EAAE,CAAC;QACpD;AACE,gBAAM,IAAI,MAAM,sBAAsB,KAAK,EAAE;;IAEnD;;;;;;;;;;;;;;;;;;;;;;ACnEA,QAAA,UAAA;AAQS,WAAA,eAAA,SAAA,SAAA,EAAA,YAAA,MAAA,KAAA,WAAA;AAAA,aARA,QAAA;IAAK,EAAA,CAAA;AACd,iBAAA,qBAAA,OAAA;AACA,iBAAA,uBAAA,OAAA;AACA,QAAA,cAAA;AACA,QAAA,gBAAA;AACA,iBAAA,kBAAA,OAAA;AAEA,YAAA,UAAe,YAAA;AAGf,WAAO,UAAU,YAAA;AACjB,WAAO,QAAQ,QAAQ,QAAA;AACvB,WAAO,QAAQ,cAAc,cAAA;;;;;ACb7B;AAAA;AAAA;AAEA,QAAM,QAAQ;AACd,QAAM,QAAQ,MAAM;AAEpB,aAASC,WAAW,IAAI,MAAM;AAC5B,UAAI,CAAC,KAAM,QAAO,CAAC;AAEnB,YAAM,WAAW,KAAK,UAAU,SAAY,KAAK,KAAK;AAGtD,UAAI,SAAS,EAAE,EAAG,MAAK,GAAG;AAAA,eACjB,OAAO,OAAO,SAAU,MAAK,OAAO,EAAE;AAE/C,UAAI;AAAE,aAAK,MAAM,EAAE;AAAA,MAAE,QAAQ;AAAE,eAAO;AAAA,MAAM;AAE5C,UAAI,OAAO;AACX,cAAQ,SAAS,KAAM,MAAM,YAAY;AACvC,YAAI;AACJ,YAAIC;AACJ,YAAI;AAEJ,YAAI,KAAK,SAAS,MAAM,YAAY;AAClC;AACA;AACA,cAAI,aAAa,EAAG,QAAO;AAC3B,cAAI,OAAO,SAAU,QAAO;AAAA,QAC9B;AAEA,YAAI,KAAK,SAAS;AAChB,eAAK,IAAI,GAAG,MAAM,KAAK,QAAQ,QAAQ,IAAI,KAAK,KAAK;AACnD,YAAAA,MAAK,KAAK,EAAE,OAAO,KAAK,QAAQ,CAAC,EAAE,GAAG,UAAU;AAChD,gBAAI,CAACA,IAAI,QAAO;AAAA,UAClB;AAAA,QACF;AACA,cAAM,QAAQ,KAAK,SAAS,KAAK,OAAO;AACxC,YAAI,CAAC,MAAO,QAAO;AAEnB,aAAK,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;AACjC,UAAAA,MAAK,KAAK,MAAM,CAAC,GAAG,UAAU;AAC9B,cAAI,CAACA,IAAI,QAAO;AAAA,QAClB;AAEA,eAAO;AAAA,MACT,GAAG,IAAI,CAAC;AAAA,IACV;AAEA,aAAS,SAAU,GAAG;AACpB,aAAO,CAAC,EAAE,SAAS,KAAK,CAAC,MAAM;AAAA,IACjC;AAEA,WAAO,UAAUD;AACjB,WAAO,QAAQ,UAAUA;AACzB,WAAO,QAAQ,YAAYA;AAAA;AAAA;;;AC9C3B,SAAS,OAAO,iBAAiB;AACjC,SAAS,eAAe;;;ACQjB,SAAS,sBAAsB,YAAoB,QAAmC;AAC3F,QAAM,QAAkB,CAAC;AAEzB,QAAM,KAAK,0BAA0B;AACrC,QAAM,KAAK,EAAE;AAGb,QAAM,gBAAgB,OAAO,KAAK,CAAC,MAAM,EAAE,SAAS,OAAO;AAC3D,MAAI,eAAe;AACjB,+BAA2B,KAAK;AAChC,UAAM,KAAK,EAAE;AAAA,EACf;AAEA,mBAAiB,OAAO,YAAY,MAAM;AAE1C,SAAO,MAAM,KAAK,IAAI;AACxB;AAUO,SAAS,8BACd,SACQ;AACR,QAAM,QAAkB,CAAC;AAGzB,QAAM,KAAK,sBAAsB;AACjC,QAAM,KAAK,uBAAuB;AAClC,QAAM,KAAK,2CAA2C;AACtD,QAAM,KAAK,uEAAuE;AAClF,QAAM,KAAK,EAAE;AACb,QAAM,KAAK,0BAA0B;AACrC,QAAM,KAAK,EAAE;AAGb,QAAM,gBAAgB,QAAQ,KAAK,CAAC,MAAM,EAAE,OAAO,KAAK,CAAC,MAAM,EAAE,SAAS,OAAO,CAAC;AAClF,MAAI,eAAe;AACjB,+BAA2B,KAAK;AAChC,UAAM,KAAK,EAAE;AAAA,EACf;AAEA,aAAW,CAAC,GAAG,MAAM,KAAK,QAAQ,QAAQ,GAAG;AAC3C,QAAI,IAAI,EAAG,OAAM,KAAK,EAAE;AACxB,qBAAiB,OAAO,OAAO,MAAM,OAAO,MAAM;AAAA,EACpD;AAEA,SAAO,MAAM,KAAK,IAAI;AACxB;AAGA,SAAS,iBAAiB,OAAiB,YAAoB,QAAiC;AAC9F,QAAM,UAAU,GAAG,YAAY,UAAU,CAAC;AAE1C,QAAM,KAAK,gBAAgB,OAAO,eAAe;AAEjD,aAAW,SAAS,QAAQ;AAC1B,UAAM,YAAY,kBAAkB,KAAK;AACzC,QAAI,MAAM,aAAa;AACrB,YAAM,KAAK,SAAS,MAAM,WAAW,KAAK;AAAA,IAC5C;AACA,UAAM,KAAK,KAAK,MAAM,IAAI,KAAK,SAAS,GAAG;AAAA,EAC7C;AAEA,QAAM,KAAK,KAAK;AAChB,QAAM,KAAK,EAAE;AACb,QAAM,KAAK,eAAe,aAAa,UAAU,CAAC,qBAAqB,OAAO,IAAI;AAClF,QAAM,KAAK,EAAE;AACf;AAGA,SAAS,2BAA2B,OAAuB;AACzD,QAAM,KAAK,yCAAyC;AACpD,QAAM,KAAK,qEAAqE;AAChF,QAAM,KAAK,4CAA4C;AACvD,QAAM,KAAK,gDAAgD;AAC3D,QAAM,KAAK,sBAAsB;AACjC,QAAM,KAAK,4BAA4B;AACvC,QAAM,KAAK,OAAO;AAClB,QAAM,KAAK,KAAK;AAClB;AAEA,SAAS,kBAAkB,OAAgC;AACzD,MAAI,OAAO,qBAAqB,MAAM,MAAM,MAAM,WAAW;AAE7D,MAAI,CAAC,MAAM,UAAU;AACnB,YAAQ;AAAA,EACV;AAEA,SAAO;AACT;AAEA,SAAS,qBACP,MACA,aACQ;AACR,UAAQ,MAAM;AAAA,IACZ,KAAK;AACH,aAAO,0BAA0B,cAAc,WAAW;AAAA,IAE5D,KAAK;AACH,aAAO,0BAA0B,cAAc,WAAW;AAAA,IAE5D,KAAK;AACH,aAAO;AAAA,IAET,KAAK;AACH,aAAO;AAAA,IAET,KAAK;AACH,aAAO;AAAA,IAET,KAAK;AACH,aAAO;AAAA,IAET,KAAK;AACH,aAAO,0BAA0B,wCAAwC,WAAW;AAAA,IAEtF,KAAK;AACH,aAAO,0BAA0B,4CAA4C,WAAW;AAAA,IAE1F,KAAK,QAAQ;AACX,YAAM,SAAS,aAAa;AAC5B,UAAI,CAAC,UAAU,OAAO,WAAW,GAAG;AAClC,eAAO;AAAA,MACT;AACA,YAAM,YAAY,OAAO,IAAI,CAAC,MAAM,IAAI,aAAa,CAAC,CAAC,GAAG,EAAE,KAAK,IAAI;AACrE,aAAO,WAAW,SAAS;AAAA,IAC7B;AAAA,IAEA,KAAK;AACH,aAAO;AAAA,IAET,KAAK,SAAS;AACZ,YAAM,WAAW,aAAa,iBAAiB;AAC/C,YAAM,WAAW,sBAAsB,QAAQ;AAC/C,aAAO,yBAAyB,WAAW,QAAQ,KAAK,WAAW;AAAA,IACrE;AAAA,IAEA;AACE,aAAO;AAAA,EACX;AACF;AAEA,SAAS,sBAAsB,MAAsB;AACnD,UAAQ,MAAM;AAAA,IACZ,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT;AACE,aAAO;AAAA,EACX;AACF;AAEA,SAAS,0BAA0B,MAAc,aAAwC;AACvF,MAAI,CAAC,YAAa,QAAO;AAEzB,MAAI,OAAO;AACX,MAAI,YAAY,cAAc,QAAW;AACvC,YAAQ,QAAQ,YAAY,SAAS,uBAAuB,YAAY,SAAS;AAAA,EACnF;AACA,MAAI,YAAY,cAAc,QAAW;AACvC,YAAQ,QAAQ,YAAY,SAAS,sBAAsB,YAAY,SAAS;AAAA,EAClF;AACA,MAAI,YAAY,SAAS;AACvB,YAAQ,WAAW,YAAY,YAAY,OAAO,CAAC;AAAA,EACrD;AACA,SAAO;AACT;AAEA,SAAS,0BAA0B,MAAc,aAAwC;AACvF,MAAI,CAAC,YAAa,QAAO;AAEzB,MAAI,OAAO;AACX,MAAI,YAAY,SAAS;AACvB,YAAQ;AAAA,EACV;AACA,MAAI,YAAY,QAAQ,QAAW;AACjC,YAAQ,QAAQ,YAAY,GAAG,uBAAuB,YAAY,GAAG;AAAA,EACvE;AACA,MAAI,YAAY,QAAQ,QAAW;AACjC,YAAQ,QAAQ,YAAY,GAAG,sBAAsB,YAAY,GAAG;AAAA,EACtE;AACA,SAAO;AACT;AAEA,SAAS,yBAAyB,MAAc,aAAwC;AACtF,MAAI,CAAC,YAAa,QAAO;AAEzB,MAAI,OAAO;AACX,MAAI,YAAY,aAAa,QAAW;AACtC,YAAQ,QAAQ,YAAY,QAAQ,yBAAyB,YAAY,QAAQ;AAAA,EACnF;AACA,MAAI,YAAY,aAAa,QAAW;AACtC,YAAQ,QAAQ,YAAY,QAAQ,wBAAwB,YAAY,QAAQ;AAAA,EAClF;AACA,SAAO;AACT;AAEA,SAAS,aAAa,KAAqB;AACzC,SAAO,IAAI,QAAQ,OAAO,MAAM,EAAE,QAAQ,MAAM,KAAK;AACvD;AAEA,SAAS,YAAY,SAAyB;AAC5C,SAAO,QAAQ,QAAQ,OAAO,KAAK;AACrC;AAEA,SAAS,YAAY,KAAqB;AACxC,SAAO,IAAI,QAAQ,aAAa,CAAC,GAAG,MAAM,EAAE,YAAY,CAAC;AAC3D;AAEA,SAAS,aAAa,KAAqB;AACzC,QAAM,QAAQ,YAAY,GAAG;AAC7B,SAAO,MAAM,OAAO,CAAC,EAAE,YAAY,IAAI,MAAM,MAAM,CAAC;AACtD;;;ACvOA,SAA2C,SAAS;AAcpD,IAAM,sBAAsB,EACzB,OAAO,EACP,IAAI,GAAG,wCAAwC,EAC/C,OAAO,CAAC,SAAS,KAAK,KAAK,EAAE,SAAS,GAAG,yCAAyC;AAMrF,IAAM,gCAAgC,EAAE,OAAO;AAAA,EAC7C,QAAQ,EAAE;AAAA,IACR,CAAC,QAAQ,QAAQ,QAAQ,OAAO,QAAQ,YAAY,UAAU;AAAA,IAC9D,EAAE,SAAS,oCAAoC;AAAA,EACjD;AAAA,EACA,YAAY,EAAE,OAAO,EAAE,SAAS;AAAA,EAChC,aAAa,EAAE,OAAO,EAAE,SAAS;AAAA,EACjC,aAAa,EAAE,OAAO,EAAE,SAAS;AAAA,EACjC,iBAAiB,EAAE,SAAS,EAAE,SAAS;AACzC,CAAC;;;AChCD,SAAS,KAAAE,UAAS;AAGlB,IAAM,0BAA0BA,GAAE,OAAO;AAAA,EACvC,OAAOA,GAAE,QAAQ,WAAW;AAAA,EAC5B,MAAMA,GAAE,OAAO,EAAE,KAAK,yCAAyC;AAAA,EAC/D,SAASA,GAAE,QAAQ,UAAU;AAC/B,CAAC;AAqBM,IAAM,gBAAgBA,GAAE,OAAO;AAAA;AAAA,EAEpC,MAAMA,GAAE,OAAO,EAAE,OAAO,GAAG,mCAAmC;AAAA;AAAA,EAG9D,MAAMA,GAAE,OAAO,EAAE,IAAI,GAAG,uBAAuB;AAAA;AAAA,EAG/C,MAAMA,GAAE,OAAO,EAAE,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQ1B,WAAWA,GACR;AAAA,IACCA,GAAE,MAAM,CAACA,GAAE,OAAO,EAAE,OAAO,GAAG,oCAAoC,GAAG,uBAAuB,CAAC;AAAA,EAC/F,EACC,IAAI,GAAG,gCAAgC;AAC5C,CAAC;;;AClDD,SAAS,KAAAC,UAAS;AAmBX,IAAM,iBAAiBA,GAAE,OAAO;AAAA;AAAA,EAErC,MAAMA,GAAE,OAAO,EAAE,OAAO,GAAG,oCAAoC;AAAA;AAAA,EAG/D,MAAMA,GAAE,OAAO,EAAE,IAAI,GAAG,wBAAwB;AAAA;AAAA,EAGhD,YAAYA,GAAE,OAAO,EAAE,SAAS;AAClC,CAAC;;;ACxBD,SAA2C,KAAAC,UAAS;;;ACOpD,SAA4D,KAAAC,UAAS;;;ACRrE,SAAS,KAAAC,UAAS;;;ACAlB,SAAS,KAAAC,UAAS;AAMX,IAAM,qBAAqB,CAAC,cAAc,aAAa,YAAY;AAGnE,IAAM,gBAAgB,KAAK,OAAO;AAClC,IAAM,gBAAgB;AAEtB,IAAM,gBAAgB;AACtB,IAAM,gBAAgB;AAMtB,IAAM,iBAAiBA,GAAE,KAAK,kBAAkB;AAEhD,IAAM,iBAAiBA,GAC3B,OAAO,EACP,IAAI,EACJ,IAAI,eAAe,4BAA4B,aAAa,QAAQ,EACpE,IAAI,eAAe,4BAA4B,gBAAgB,OAAO,IAAI,IAAI;AAE1E,IAAM,kBAAkBA,GAC5B,OAAO,EACP,IAAI,EACJ,IAAI,eAAe,iCAAiC,aAAa,IAAI,EACrE,IAAI,eAAe,iCAAiC,aAAa,IAAI;AAKjE,IAAM,sBAAsBA,GAAE,OAAO;AAAA,EAC1C,UAAUA,GACP,OAAO,EACP,IAAI,GAAG,sBAAsB,EAC7B,IAAI,KAAK,mBAAmB,EAC5B,MAAM,oBAAoB,sCAAsC;AAAA,EACnE,UAAU;AAAA,EACV,UAAU;AACZ,CAAC;AAQM,IAAM,sBAAsBA,GAAE,OAAO;AAAA,EAC1C,SAASA,GAAE,KAAK,EAAE,SAAS;AAAA,EAC3B,OAAO;AAAA,EACP,QAAQ;AACV,CAAC;;;AD/BM,IAAM,mBAAmBC,GAAE,OAAO;AAAA;AAAA,EAEvC,IAAIA,GAAE,KAAK;AAAA;AAAA,EAEX,KAAKA,GAAE,IAAI;AAAA;AAAA,EAEX,OAAO;AAAA;AAAA,EAEP,QAAQ;AAAA;AAAA,EAER,kBAAkBA,GAAE,OAAO,EAAE,IAAI,CAAC,EAAE,IAAI,GAAG;AAAA;AAAA,EAE3C,UAAU;AAAA;AAAA,EAEV,UAAU;AAAA;AAAA,EAEV,MAAMA,GAAE,OAAO,EAAE,SAAS;AAC5B,CAAC;AAYM,IAAM,gBAAgBA,GAAE,OAAO;AAAA,EACpC,GAAGA,GAAE,OAAO,EAAE,IAAI,CAAC,EAAE,IAAI,CAAC;AAAA,EAC1B,GAAGA,GAAE,OAAO,EAAE,IAAI,CAAC,EAAE,IAAI,CAAC;AAC5B,CAAC;AAUM,IAAM,aAAaA,GAAE,OAAO;AAAA;AAAA,EAEjC,GAAGA,GAAE,OAAO,EAAE,IAAI,EAAE,YAAY;AAAA;AAAA,EAEhC,GAAGA,GAAE,OAAO,EAAE,IAAI,EAAE,YAAY;AAAA;AAAA,EAEhC,OAAOA,GAAE,OAAO,EAAE,IAAI,EAAE,SAAS;AAAA;AAAA,EAEjC,QAAQA,GAAE,OAAO,EAAE,IAAI,EAAE,SAAS;AACpC,CAAC;AAQM,IAAM,uBAAuBA,GAAE,OAAO;AAAA;AAAA,EAE3C,KAAKA,GAAE,OAAO,EAAE,IAAI,GAAG,wCAAwC;AAAA;AAAA,EAG/D,SAASA,GAAE,OAAO,EAAE,IAAI,GAAG,EAAE,SAAS;AAAA,EACtC,aAAaA,GAAE,OAAO,EAAE,IAAI,GAAG,EAAE,SAAS;AAAA;AAAA,EAG1C,QAAQA,GAAE,OAAO;AAAA,IACf,IAAIA,GAAE,KAAK;AAAA,IACX,gBAAgBA,GAAE,OAAO,EAAE,SAAS,EAAE,SAAS;AAAA,EACjD,CAAC;AACH,CAAC;AA2ED,IAAM,aAAaC,GAAE,OAAO;AAAA,EAC1B,KAAKA,GAAE,IAAI;AAAA,EACX,MAAMA,GAAE,OAAO;AAAA,EACf,MAAMA,GAAE,OAAO,EAAE,IAAI,EAAE,SAAS;AAAA,EAChC,MAAMA,GAAE,OAAO;AACjB,CAAC;;;AEnLD,yBAAsB;AASf,SAAS,kBAAkB,SAAgC;AAEhE,MAAI,QAAQ,SAAS,KAAK;AACxB,WAAO;AAAA,EACT;AAGA,MAAI,KAAC,mBAAAC,SAAU,OAAO,GAAG;AACvB,WAAO;AAAA,EACT;AAGA,MAAI;AACF,QAAI,OAAO,OAAO;AAAA,EACpB,SAASC,MAAK;AACZ,WAAO,kBAAkBA,gBAAe,QAAQA,KAAI,UAAU,SAAS;AAAA,EACzE;AAEA,SAAO;AACT;;;AHZO,IAAM,mBAAN,cAA+B,MAAM;AAAA,EAC1C,YACE,SACgB,WACA,WAChB;AACA,UAAM,OAAO;AAHG;AACA;AAGhB,SAAK,OAAO;AAAA,EACd;AACF;AAKO,SAAS,eAAe,OAAoC;AACjE,MAAI;AACF,QAAI,SAAS,iBAAiB,MAAM,MAAM,MAAM,WAAW;AAE3D,QAAI,CAAC,MAAM,UAAU;AACnB,eAAS,OAAO,SAAS;AAAA,IAC3B;AAEA,WAAO;AAAA,EACT,SAASC,MAAK;AACZ,UAAM,IAAI;AAAA,MACR,8BAA8B,MAAM,IAAI,MAAMA,gBAAe,QAAQA,KAAI,UAAU,eAAe;AAAA,MAClG,MAAM;AAAA,MACN,MAAM;AAAA,IACR;AAAA,EACF;AACF;AAKA,SAAS,iBAAiB,MAAiB,aAA4C;AACrF,UAAQ,MAAM;AAAA,IACZ,KAAK;AACH,aAAO,uBAAuBC,GAAE,OAAO,GAAG,WAAW;AAAA,IAEvD,KAAK;AACH,aAAO,uBAAuBA,GAAE,OAAO,GAAG,WAAW;AAAA,IAEvD,KAAK;AACH,aAAOA,GAAE,QAAQ;AAAA,IAEnB,KAAK;AACH,aAAO;AAAA,IAET,KAAK;AACH,aAAOA,GAAE,OAAO,EAAE,KAAK,0CAA0C;AAAA,IAEnE,KAAK;AACH,aAAOA,GACJ,OAAO,EACP,SAAS,EAAE,QAAQ,MAAM,SAAS,6CAA6C,CAAC;AAAA,IAErF,KAAK;AACH,aAAO,uBAAuBA,GAAE,OAAO,EAAE,IAAI,oBAAoB,GAAG,WAAW;AAAA,IAEjF,KAAK;AACH,aAAO,uBAAuBA,GAAE,OAAO,EAAE,MAAM,sBAAsB,GAAG,WAAW;AAAA,IAErF,KAAK,QAAQ;AACX,YAAM,SAAS,aAAa;AAC5B,UAAI,CAAC,UAAU,OAAO,WAAW,GAAG;AAClC,cAAM,IAAI,MAAM,4DAA4D;AAAA,MAC9E;AACA,aAAOA,GAAE,KAAK,MAA+B;AAAA,IAC/C;AAAA,IAEA,KAAK;AAEH,aAAOA,GAAE,OAAOA,GAAE,OAAO,GAAGA,GAAE,QAAQ,CAAC;AAAA,IAEzC,KAAK,SAAS;AACZ,YAAM,WAAW,aAAa,iBAAiB;AAC/C,YAAM,aAAa,sBAAsB,QAAQ;AACjD,aAAO,sBAAsBA,GAAE,MAAM,UAAU,GAAG,WAAW;AAAA,IAC/D;AAAA,IAEA;AACE,YAAM,IAAI,MAAM,uBAAuB,IAAI,EAAE;AAAA,EACjD;AACF;AAKA,SAAS,sBAAsB,MAAmD;AAChF,UAAQ,MAAM;AAAA,IACZ,KAAK;AACH,aAAOA,GAAE,OAAO;AAAA,IAClB,KAAK;AACH,aAAOA,GAAE,OAAO;AAAA,IAClB,KAAK;AACH,aAAOA,GAAE,QAAQ;AAAA,EACrB;AACF;AAKA,SAAS,uBAAuB,QAAqB,aAA6C;AAChG,MAAI,CAAC,YAAa,QAAO;AAGzB,MACE,YAAY,cAAc,UAC1B,YAAY,cAAc,UAC1B,YAAY,YAAY,YAAY,WACpC;AACA,UAAM,IAAI;AAAA,MACR,0CAA0C,YAAY,SAAS,uCAAuC,YAAY,SAAS;AAAA,IAC7H;AAAA,EACF;AAEA,MAAI,SAAS;AAEb,MAAI,YAAY,cAAc,QAAW;AACvC,aAAS,OAAO;AAAA,MACd,YAAY;AAAA,MACZ,oBAAoB,YAAY,SAAS;AAAA,IAC3C;AAAA,EACF;AACA,MAAI,YAAY,cAAc,QAAW;AACvC,aAAS,OAAO;AAAA,MACd,YAAY;AAAA,MACZ,mBAAmB,YAAY,SAAS;AAAA,IAC1C;AAAA,EACF;AACA,MAAI,YAAY,SAAS;AAEvB,UAAM,cAAc,kBAAkB,YAAY,OAAO;AACzD,QAAI,aAAa;AACf,YAAM,IAAI,MAAM,WAAW;AAAA,IAC7B;AACA,QAAI;AACF,eAAS,OAAO,MAAM,IAAI,OAAO,YAAY,OAAO,GAAG,gBAAgB;AAAA,IACzE,QAAQ;AACN,YAAM,IAAI,MAAM,0BAA0B,YAAY,OAAO,EAAE;AAAA,IACjE;AAAA,EACF;AAEA,SAAO;AACT;AAKA,SAAS,uBAAuB,QAAqB,aAA6C;AAChG,MAAI,CAAC,YAAa,QAAO;AAGzB,MACE,YAAY,QAAQ,UACpB,YAAY,QAAQ,UACpB,YAAY,MAAM,YAAY,KAC9B;AACA,UAAM,IAAI;AAAA,MACR,oCAAoC,YAAY,GAAG,iCAAiC,YAAY,GAAG;AAAA,IACrG;AAAA,EACF;AAEA,MAAI,SAAS;AAEb,MAAI,YAAY,QAAQ,QAAW;AACjC,aAAS,OAAO,IAAI,YAAY,KAAK,oBAAoB,YAAY,GAAG,EAAE;AAAA,EAC5E;AACA,MAAI,YAAY,QAAQ,QAAW;AACjC,aAAS,OAAO,IAAI,YAAY,KAAK,mBAAmB,YAAY,GAAG,EAAE;AAAA,EAC3E;AACA,MAAI,YAAY,SAAS;AACvB,aAAS,OAAO,IAAI,oBAAoB;AAAA,EAC1C;AAEA,SAAO;AACT;AAKA,SAAS,sBACP,QACA,aACwB;AACxB,MAAI,CAAC,YAAa,QAAO;AAGzB,MACE,YAAY,aAAa,UACzB,YAAY,aAAa,UACzB,YAAY,WAAW,YAAY,UACnC;AACA,UAAM,IAAI;AAAA,MACR,wCAAwC,YAAY,QAAQ,sCAAsC,YAAY,QAAQ;AAAA,IACxH;AAAA,EACF;AAEA,MAAI,SAAS;AAEb,MAAI,YAAY,aAAa,QAAW;AACtC,aAAS,OAAO,IAAI,YAAY,UAAU,sBAAsB,YAAY,QAAQ,QAAQ;AAAA,EAC9F;AACA,MAAI,YAAY,aAAa,QAAW;AACtC,aAAS,OAAO,IAAI,YAAY,UAAU,qBAAqB,YAAY,QAAQ,QAAQ;AAAA,EAC7F;AAEA,SAAO;AACT;AAqBO,SAAS,gBAAgB,QAAmD;AACjF,MAAI,CAAC,MAAM,QAAQ,MAAM,KAAK,OAAO,WAAW,GAAG;AACjD,UAAM,IAAI,iBAAiB,qCAAqC;AAAA,EAClE;AAEA,QAAM,QAAoC,CAAC;AAC3C,QAAM,YAAY,oBAAI,IAAY;AAElC,aAAW,SAAS,QAAQ;AAE1B,QAAI,UAAU,IAAI,MAAM,IAAI,GAAG;AAC7B,YAAM,IAAI,iBAAiB,0BAA0B,MAAM,IAAI,GAAG;AAAA,IACpE;AACA,cAAU,IAAI,MAAM,IAAI;AAGxB,QAAI,CAAC,0BAA0B,KAAK,MAAM,IAAI,GAAG;AAC/C,YAAM,IAAI;AAAA,QACR,uBAAuB,MAAM,IAAI;AAAA,QACjC,MAAM;AAAA,MACR;AAAA,IACF;AAEA,UAAM,MAAM,IAAI,IAAI,eAAe,KAAK;AAAA,EAC1C;AAEA,SAAOA,GAAE,OAAO,KAAK;AACvB;;;AIjRA,OAAO,UAAU;;;ACDjB,SAAS,iBAAiB;;;ACE1B,SAAgC,kBAAkB,qBAAqB;AACvE,OAAO,eAAe;AAQtB,SAAS,aAAa,QAAwB;AAC5C,SAAO,IAAI,IAAI,aAAa,MAAM,EAAE,SAAS;AAC/C;AAeA,SAAS,sBAAsB,QAAiB,WAAoB;AAClE,SAAO,OAAO,KAAwB,YAA6C;AACjF,QAAI,WAAW;AAEf,UAAM,SAAS,IAAI,IAAI,IAAI,SAAS,CAAC;AACrC,QAAI,QAAQ;AACV,aAAO,aAAa,IAAI,WAAW,MAAM;AAAA,IAC3C;AACA,QAAI,WAAW;AACb,aAAO,aAAa,IAAI,cAAc,SAAS;AAAA,IACjD;AACA,QAAI,UAAU,WAAW;AACvB,iBAAW,OAAO,SAAS;AAAA,IAC7B;AAEA,UAAM,WAAW,MAAM,MAAM,UAAU,OAAO;AAE9C,WAAO;AAAA,EACT;AACF;AAKA,SAAS,gBAAgB,SAA+B;AACtD,QAAM,MAAM,aAAa,QAAQ,MAAM;AAEvC,SAAO,iBAA4B;AAAA,IACjC,OAAO;AAAA,MACL,cAAc;AAAA,QACZ;AAAA,QACA,aAAa;AAAA,QACb,OAAO,sBAAsB,QAAQ,QAAQ,QAAQ,SAAS;AAAA,MAChE,CAAC;AAAA,IACH;AAAA,EACF,CAAC;AACH;AAKO,SAAS,aAAa,SAA+B;AAC1D,SAAO,gBAAgB,OAAO;AAChC;;;AZhCA,eAAsB,2BACpB,SAC+B;AAC/B,QAAM,EAAE,UAAU,IAAI;AAEtB,QAAM,SAAS,aAAa,OAAO;AAGnC,QAAM,YAAY,MAAM,OAAO,aAAa,KAAK,MAAM;AAAA,IACrD;AAAA,IACA,eAAe;AAAA,EACjB,CAAC;AAGD,QAAM,UAAU,MAAM,QAAQ;AAAA,IAC5B,UAAU,IAAI,OAAO,YAAY;AAC/B,YAAM,SAAS,MAAM,OAAO,aAAa,UAAU,MAAM;AAAA,QACvD;AAAA,QACA,MAAM,QAAQ;AAAA,MAChB,CAAC;AAED,aAAO;AAAA,QACL,MAAM,OAAO;AAAA,QACb,aAAa,OAAO;AAAA,QACpB,aAAa,OAAO,eAAe;AAAA,QACnC,QAAQ,OAAO;AAAA,QACf,gBAAgB,OAAO,kBAAkB;AAAA,QACzC,SAAS,OAAO;AAAA,MAClB;AAAA,IACF,CAAC;AAAA,EACH;AAEA,SAAO;AACT;AAgBA,eAAsB,wBACpB,SACA,YAC6B;AAC7B,QAAM,EAAE,UAAU,IAAI;AACtB,QAAM,SAAS,aAAa,OAAO;AAEnC,QAAM,SAAS,MAAM,OAAO,aAAa,UAAU,MAAM;AAAA,IACvD;AAAA,IACA,MAAM;AAAA,EACR,CAAC;AAED,SAAO;AAAA,IACL,MAAM,OAAO;AAAA,IACb,aAAa,OAAO;AAAA,IACpB,aAAa,OAAO,eAAe;AAAA,IACnC,QAAQ,OAAO;AAAA,IACf,gBAAgB,OAAO,kBAAkB;AAAA,IACzC,SAAS,OAAO;AAAA,EAClB;AACF;AAeO,SAAS,gBACd,SACwC;AAExC,QAAM,SAA8B,CAAC;AAErC,aAAW,UAAU,SAAS;AAC5B,WAAO,OAAO,IAAI,IAAI,gBAAgB,OAAO,MAAM;AAAA,EACrD;AAEA,SAAO;AACT;AAeO,SAAS,mBAAmB,SAAuD;AACxF,QAAM,SAAiC,CAAC;AAExC,aAAW,UAAU,SAAS;AAC5B,WAAO,OAAO,IAAI,IAAI,sBAAsB,OAAO,MAAM,OAAO,MAAM;AAAA,EACxE;AAEA,SAAO;AACT;AASA,eAAsB,kBACpB,SACA,UACe;AACf,MAAI,QAAQ,WAAW,GAAG;AACxB,YAAQ,KAAK,qEAAgE;AAC7E;AAAA,EACF;AAEA,QAAM,OAAO,8BAA8B,OAAO;AAElD,QAAM,MAAM,QAAQ,QAAQ,GAAG,EAAE,WAAW,KAAK,CAAC;AAClD,QAAM,UAAU,UAAU,MAAM,OAAO;AACzC;","names":["types","safeRegex","ok","z","z","z","z","z","z","z","z","safeRegex","err","err","z"]}
|
package/dist/lib/proxy.js
CHANGED
|
@@ -50,20 +50,7 @@ async function proxyToUpstream(request, pathname, upstream) {
|
|
|
50
50
|
const newLocation = `${currentOrigin}${locationUrl.pathname}${locationUrl.search}`;
|
|
51
51
|
responseHeaders.set(key, newLocation);
|
|
52
52
|
} else {
|
|
53
|
-
|
|
54
|
-
const redirectUri = locationUrl.searchParams.get("redirect_uri");
|
|
55
|
-
if (redirectUri) {
|
|
56
|
-
try {
|
|
57
|
-
const redirectUriUrl = new URL(redirectUri);
|
|
58
|
-
if (redirectUriUrl.host === upstreamUrlObj.host) {
|
|
59
|
-
const newRedirectUri = `${currentOrigin}${redirectUriUrl.pathname}${redirectUriUrl.search}`;
|
|
60
|
-
locationUrl.searchParams.set("redirect_uri", newRedirectUri);
|
|
61
|
-
finalLocation = locationUrl.toString();
|
|
62
|
-
}
|
|
63
|
-
} catch {
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
responseHeaders.set(key, finalLocation);
|
|
53
|
+
responseHeaders.set(key, value);
|
|
67
54
|
}
|
|
68
55
|
} catch {
|
|
69
56
|
responseHeaders.set(key, value);
|
package/dist/lib/proxy.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../lib/proxy.ts"],"sourcesContent":["import { type NextRequest, NextResponse } from 'next/server';\n\n/**\n * Configuration options for the CMS proxy.\n */\nexport interface ProxyConfig {\n /**\n * The upstream CMS server URL (e.g., 'https://cms.example.com').\n * Defaults to ADMIN_UPSTREAM_ORIGIN environment variable.\n */\n upstream?: string;\n /**\n * Additional path prefixes to proxy (beyond /admin, /api, /auth).\n */\n additionalPaths?: string[];\n}\n\n// Static file extensions to proxy to upstream\nconst STATIC_FILE_REGEX =\n /\\.(css|js|map|png|jpg|jpeg|gif|svg|ico|webp|avif|woff|woff2|ttf|eot|txt|xml)$/;\n\n/**\n * Check if the request originates from an admin page (via Referer header).\n */\nfunction isFromAdminPage(request: NextRequest): boolean {\n const referer = request.headers.get('referer');\n if (!referer) return false;\n\n try {\n const refererUrl = new URL(referer);\n return refererUrl.pathname.startsWith('/admin');\n } catch {\n return false;\n }\n}\n\n/**\n * Proxy a request to the upstream CMS server with proper cookie handling.\n */\nasync function proxyToUpstream(\n request: NextRequest,\n pathname: string,\n upstream: string\n): Promise<NextResponse> {\n const upstreamUrl = new URL(pathname, upstream);\n upstreamUrl.search = request.nextUrl.search;\n\n // Clone all headers from the request\n const headers = new Headers(request.headers);\n\n // Keep the original host header so the upstream app knows the real origin\n // This is important for auth redirects (WorkOS) to use the correct domain\n // The x-forwarded-* headers provide additional context\n headers.set('x-forwarded-host', request.headers.get('host') ?? '');\n headers.set('x-forwarded-proto', request.nextUrl.protocol.replace(':', ''));\n headers.set('x-forwarded-for', request.headers.get('x-forwarded-for') ?? '');\n\n const response = await fetch(upstreamUrl.toString(), {\n method: request.method,\n headers,\n body: request.body,\n // @ts-expect-error - duplex is required for streaming bodies\n duplex: 'half',\n redirect: 'manual', // Don't follow redirects, let the client handle them\n });\n\n // Create response with proper header handling\n const responseHeaders = new Headers();\n\n const upstreamUrlObj = new URL(upstream);\n const upstreamOrigin = upstreamUrlObj.origin;\n const currentOrigin = request.nextUrl.origin;\n\n // Copy headers from upstream response\n response.headers.forEach((value, key) => {\n const lowerKey = key.toLowerCase();\n\n // Handle Set-Cookie specially - rewrite domain to current host\n if (lowerKey === 'set-cookie') {\n let modifiedCookie = value;\n\n // Remove Domain attribute so cookie defaults to current host\n modifiedCookie = modifiedCookie.replace(/;\\s*Domain=[^;]*/gi, '');\n\n // Ensure Path is set (usually /admin or /)\n if (!/;\\s*Path=/i.test(modifiedCookie)) {\n modifiedCookie += '; Path=/';\n }\n\n // For secure cookies in production, ensure SameSite is appropriate\n if (!/;\\s*SameSite=/i.test(modifiedCookie)) {\n modifiedCookie += '; SameSite=Lax';\n }\n\n responseHeaders.append(key, modifiedCookie);\n }\n // Handle Location header - rewrite upstream URLs to current host\n else if (lowerKey === 'location') {\n try {\n // Parse the location (handles both absolute and relative URLs)\n const locationUrl = new URL(value, upstream);\n\n // If redirect points to upstream, rewrite to current origin\n if (locationUrl.origin === upstreamOrigin) {\n const newLocation = `${currentOrigin}${locationUrl.pathname}${locationUrl.search}`;\n responseHeaders.set(key, newLocation);\n } else {\n // External redirect (e.g., to WorkOS)\n // Rewrite redirect_uri parameter if it points to upstream\n let finalLocation = value;\n\n // Check if this is a WorkOS/OAuth redirect with redirect_uri parameter\n const redirectUri = locationUrl.searchParams.get('redirect_uri');\n if (redirectUri) {\n try {\n const redirectUriUrl = new URL(redirectUri);\n // If redirect_uri points to upstream, rewrite to current origin\n if (redirectUriUrl.host === upstreamUrlObj.host) {\n const newRedirectUri = `${currentOrigin}${redirectUriUrl.pathname}${redirectUriUrl.search}`;\n locationUrl.searchParams.set('redirect_uri', newRedirectUri);\n finalLocation = locationUrl.toString();\n }\n } catch {\n // If redirect_uri parsing fails, keep original\n }\n }\n\n responseHeaders.set(key, finalLocation);\n }\n } catch {\n // If URL parsing fails, keep original\n responseHeaders.set(key, value);\n }\n }\n // Skip headers that cause issues after fetch decompresses the body\n else if (\n lowerKey !== 'transfer-encoding' &&\n lowerKey !== 'content-encoding' &&\n lowerKey !== 'content-length'\n ) {\n responseHeaders.set(key, value);\n }\n });\n\n // Add debug header to verify middleware is running\n responseHeaders.set('x-proxied-by', 'cms-proxy');\n\n // For HTML responses, rewrite upstream URLs in the body\n const contentType = response.headers.get('content-type') ?? '';\n if (contentType.includes('text/html') && response.body) {\n let text = await response.text();\n\n // Get the upstream host for more comprehensive replacement\n const upstreamHost = upstreamUrlObj.host;\n\n // Replace full origin (https://cms.example.com)\n text = text.replaceAll(upstreamOrigin, currentOrigin);\n\n // Replace protocol-relative URLs (//cms.example.com)\n text = text.replaceAll(`//${upstreamHost}`, `//${request.nextUrl.host}`);\n\n // Replace any remaining absolute URLs with the upstream host\n // This catches cases where protocol might differ\n text = text.replaceAll(`https://${upstreamHost}`, currentOrigin);\n text = text.replaceAll(`http://${upstreamHost}`, currentOrigin);\n\n return new NextResponse(text, {\n status: response.status,\n statusText: response.statusText,\n headers: responseHeaders,\n });\n }\n\n return new NextResponse(response.body, {\n status: response.status,\n statusText: response.statusText,\n headers: responseHeaders,\n });\n}\n\n/**\n * Creates a proxy middleware function for Next.js.\n *\n * @example\n * ```ts\n * // middleware.ts\n * import { createCmsProxy } from 'cms-renderer/lib/proxy';\n *\n * const cmsProxy = createCmsProxy({\n * upstream: process.env.ADMIN_UPSTREAM_ORIGIN,\n * });\n *\n * export async function middleware(request: NextRequest) {\n * return cmsProxy(request);\n * }\n *\n * export const config = {\n * matcher: cmsProxyMatcher,\n * };\n * ```\n */\nexport function createCmsProxy(config: ProxyConfig = {}) {\n const upstream = (config.upstream ?? process.env.ADMIN_UPSTREAM_ORIGIN ?? '').replace(/\\/$/, '');\n const additionalPaths = config.additionalPaths ?? [];\n\n if (!upstream) {\n console.warn(\n '[cms-proxy] No upstream URL configured. Set ADMIN_UPSTREAM_ORIGIN or pass upstream option.'\n );\n }\n\n return async function cmsProxy(request: NextRequest): Promise<NextResponse> {\n if (!upstream) {\n return NextResponse.next();\n }\n\n const { pathname } = request.nextUrl;\n\n // Proxy /admin routes to the upstream CMS\n if (pathname.startsWith('/admin')) {\n return proxyToUpstream(request, pathname, upstream);\n }\n\n // Proxy /api routes to the upstream CMS\n if (pathname.startsWith('/api')) {\n return proxyToUpstream(request, pathname, upstream);\n }\n\n // Proxy auth routes to the upstream CMS (WorkOS callbacks, signin, etc.)\n if (pathname.startsWith('/auth')) {\n return proxyToUpstream(request, pathname, upstream);\n }\n\n // Proxy additional custom paths\n for (const pathPrefix of additionalPaths) {\n if (pathname.startsWith(pathPrefix)) {\n return proxyToUpstream(request, pathname, upstream);\n }\n }\n\n // Only proxy /_next and static files if the request comes from an admin page\n // This prevents breaking the web app's own assets\n if (isFromAdminPage(request)) {\n if (pathname.startsWith('/_next') || STATIC_FILE_REGEX.test(pathname)) {\n return proxyToUpstream(request, pathname, upstream);\n }\n }\n\n return NextResponse.next();\n };\n}\n\n/**\n * Default matcher configuration for the CMS proxy middleware.\n * Use this in your middleware.ts config export.\n *\n * @example\n * ```ts\n * export const config = {\n * matcher: cmsProxyMatcher,\n * };\n * ```\n */\nexport const cmsProxyMatcher = [\n '/admin',\n '/admin/:path*',\n '/api/:path*',\n '/auth/:path*',\n '/_next/:path*',\n '/((?:.*\\\\.(?:css|js|map|png|jpg|jpeg|gif|svg|ico|webp|avif|woff|woff2|ttf|eot|txt|xml))$)',\n];\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../lib/proxy.ts"],"sourcesContent":["import { type NextRequest, NextResponse } from 'next/server';\n\n/**\n * Configuration options for the CMS proxy.\n */\nexport interface ProxyConfig {\n /**\n * The upstream CMS server URL (e.g., 'https://cms.example.com').\n * Defaults to ADMIN_UPSTREAM_ORIGIN environment variable.\n */\n upstream?: string;\n /**\n * Additional path prefixes to proxy (beyond /admin, /api, /auth).\n */\n additionalPaths?: string[];\n}\n\n// Static file extensions to proxy to upstream\nconst STATIC_FILE_REGEX =\n /\\.(css|js|map|png|jpg|jpeg|gif|svg|ico|webp|avif|woff|woff2|ttf|eot|txt|xml)$/;\n\n/**\n * Check if the request originates from an admin page (via Referer header).\n */\nfunction isFromAdminPage(request: NextRequest): boolean {\n const referer = request.headers.get('referer');\n if (!referer) return false;\n\n try {\n const refererUrl = new URL(referer);\n return refererUrl.pathname.startsWith('/admin');\n } catch {\n return false;\n }\n}\n\n/**\n * Proxy a request to the upstream CMS server with proper cookie handling.\n */\nasync function proxyToUpstream(\n request: NextRequest,\n pathname: string,\n upstream: string\n): Promise<NextResponse> {\n const upstreamUrl = new URL(pathname, upstream);\n upstreamUrl.search = request.nextUrl.search;\n\n // Clone all headers from the request\n const headers = new Headers(request.headers);\n\n // Keep the original host header so the upstream app knows the real origin\n // This is important for auth redirects (WorkOS) to use the correct domain\n // The x-forwarded-* headers provide additional context\n headers.set('x-forwarded-host', request.headers.get('host') ?? '');\n headers.set('x-forwarded-proto', request.nextUrl.protocol.replace(':', ''));\n headers.set('x-forwarded-for', request.headers.get('x-forwarded-for') ?? '');\n\n const response = await fetch(upstreamUrl.toString(), {\n method: request.method,\n headers,\n body: request.body,\n // @ts-expect-error - duplex is required for streaming bodies\n duplex: 'half',\n redirect: 'manual', // Don't follow redirects, let the client handle them\n });\n\n // Create response with proper header handling\n const responseHeaders = new Headers();\n\n const upstreamUrlObj = new URL(upstream);\n const upstreamOrigin = upstreamUrlObj.origin;\n const currentOrigin = request.nextUrl.origin;\n\n // Copy headers from upstream response\n response.headers.forEach((value, key) => {\n const lowerKey = key.toLowerCase();\n\n // Handle Set-Cookie specially - rewrite domain to current host\n if (lowerKey === 'set-cookie') {\n let modifiedCookie = value;\n\n // Remove Domain attribute so cookie defaults to current host\n modifiedCookie = modifiedCookie.replace(/;\\s*Domain=[^;]*/gi, '');\n\n // Ensure Path is set (usually /admin or /)\n if (!/;\\s*Path=/i.test(modifiedCookie)) {\n modifiedCookie += '; Path=/';\n }\n\n // For secure cookies in production, ensure SameSite is appropriate\n if (!/;\\s*SameSite=/i.test(modifiedCookie)) {\n modifiedCookie += '; SameSite=Lax';\n }\n\n responseHeaders.append(key, modifiedCookie);\n }\n // Handle Location header - rewrite upstream URLs to current host\n else if (lowerKey === 'location') {\n try {\n // Parse the location (handles both absolute and relative URLs)\n const locationUrl = new URL(value, upstream);\n\n // If redirect points to upstream, rewrite to current origin\n if (locationUrl.origin === upstreamOrigin) {\n const newLocation = `${currentOrigin}${locationUrl.pathname}${locationUrl.search}`;\n responseHeaders.set(key, newLocation);\n } else {\n // External redirect (e.g., to WorkOS/authkit).\n // Do NOT rewrite the redirect_uri parameter. The CMS middleware encodes\n // the customer origin in the WorkOS `state` parameter (via x-forwarded-host)\n // and handles the return-to-customer-domain flow through a short-lived\n // handoff token at /api/auth/workos/complete. The redirect_uri must remain\n // pointing to the CMS host so WorkOS accepts it as a registered callback URI.\n responseHeaders.set(key, value);\n }\n } catch {\n // If URL parsing fails, keep original\n responseHeaders.set(key, value);\n }\n }\n // Skip headers that cause issues after fetch decompresses the body\n else if (\n lowerKey !== 'transfer-encoding' &&\n lowerKey !== 'content-encoding' &&\n lowerKey !== 'content-length'\n ) {\n responseHeaders.set(key, value);\n }\n });\n\n // Add debug header to verify middleware is running\n responseHeaders.set('x-proxied-by', 'cms-proxy');\n\n // For HTML responses, rewrite upstream URLs in the body\n const contentType = response.headers.get('content-type') ?? '';\n if (contentType.includes('text/html') && response.body) {\n let text = await response.text();\n\n // Get the upstream host for more comprehensive replacement\n const upstreamHost = upstreamUrlObj.host;\n\n // Replace full origin (https://cms.example.com)\n text = text.replaceAll(upstreamOrigin, currentOrigin);\n\n // Replace protocol-relative URLs (//cms.example.com)\n text = text.replaceAll(`//${upstreamHost}`, `//${request.nextUrl.host}`);\n\n // Replace any remaining absolute URLs with the upstream host\n // This catches cases where protocol might differ\n text = text.replaceAll(`https://${upstreamHost}`, currentOrigin);\n text = text.replaceAll(`http://${upstreamHost}`, currentOrigin);\n\n return new NextResponse(text, {\n status: response.status,\n statusText: response.statusText,\n headers: responseHeaders,\n });\n }\n\n return new NextResponse(response.body, {\n status: response.status,\n statusText: response.statusText,\n headers: responseHeaders,\n });\n}\n\n/**\n * Creates a proxy middleware function for Next.js.\n *\n * @example\n * ```ts\n * // middleware.ts\n * import { createCmsProxy } from 'cms-renderer/lib/proxy';\n *\n * const cmsProxy = createCmsProxy({\n * upstream: process.env.ADMIN_UPSTREAM_ORIGIN,\n * });\n *\n * export async function middleware(request: NextRequest) {\n * return cmsProxy(request);\n * }\n *\n * export const config = {\n * matcher: cmsProxyMatcher,\n * };\n * ```\n */\nexport function createCmsProxy(config: ProxyConfig = {}) {\n const upstream = (config.upstream ?? process.env.ADMIN_UPSTREAM_ORIGIN ?? '').replace(/\\/$/, '');\n const additionalPaths = config.additionalPaths ?? [];\n\n if (!upstream) {\n console.warn(\n '[cms-proxy] No upstream URL configured. Set ADMIN_UPSTREAM_ORIGIN or pass upstream option.'\n );\n }\n\n return async function cmsProxy(request: NextRequest): Promise<NextResponse> {\n if (!upstream) {\n return NextResponse.next();\n }\n\n const { pathname } = request.nextUrl;\n\n // Proxy /admin routes to the upstream CMS\n if (pathname.startsWith('/admin')) {\n return proxyToUpstream(request, pathname, upstream);\n }\n\n // Proxy /api routes to the upstream CMS\n if (pathname.startsWith('/api')) {\n return proxyToUpstream(request, pathname, upstream);\n }\n\n // Proxy auth routes to the upstream CMS (WorkOS callbacks, signin, etc.)\n if (pathname.startsWith('/auth')) {\n return proxyToUpstream(request, pathname, upstream);\n }\n\n // Proxy additional custom paths\n for (const pathPrefix of additionalPaths) {\n if (pathname.startsWith(pathPrefix)) {\n return proxyToUpstream(request, pathname, upstream);\n }\n }\n\n // Only proxy /_next and static files if the request comes from an admin page\n // This prevents breaking the web app's own assets\n if (isFromAdminPage(request)) {\n if (pathname.startsWith('/_next') || STATIC_FILE_REGEX.test(pathname)) {\n return proxyToUpstream(request, pathname, upstream);\n }\n }\n\n return NextResponse.next();\n };\n}\n\n/**\n * Default matcher configuration for the CMS proxy middleware.\n * Use this in your middleware.ts config export.\n *\n * @example\n * ```ts\n * export const config = {\n * matcher: cmsProxyMatcher,\n * };\n * ```\n */\nexport const cmsProxyMatcher = [\n '/admin',\n '/admin/:path*',\n '/api/:path*',\n '/auth/:path*',\n '/_next/:path*',\n '/((?:.*\\\\.(?:css|js|map|png|jpg|jpeg|gif|svg|ico|webp|avif|woff|woff2|ttf|eot|txt|xml))$)',\n];\n"],"mappings":"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","names":[]}
|
package/dist/lib/renderer.js
CHANGED
|
@@ -243,8 +243,8 @@ import { notFound } from "next/navigation";
|
|
|
243
243
|
|
|
244
244
|
// lib/block-renderer.tsx
|
|
245
245
|
import React from "react";
|
|
246
|
-
import { ClientEditableBlock } from "./client-editable-block.js";
|
|
247
246
|
import { BlockToolbar } from "./block-toolbar.js";
|
|
247
|
+
import { ClientEditableBlock } from "./client-editable-block.js";
|
|
248
248
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
249
249
|
function walkReactNode(node, visitors, ctx = {}) {
|
|
250
250
|
const path = ctx.path ?? [];
|
package/dist/lib/renderer.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../packages/cms-schema/src/blocks/article.ts","../../../../packages/cms-schema/src/validation/image.ts","../../../../packages/cms-schema/src/blocks/schemas/article-block.ts","../../../../packages/cms-schema/src/blocks/schemas/cta-block.ts","../../../../packages/cms-schema/src/blocks/schemas/features-block.ts","../../../../packages/cms-schema/src/blocks/schemas/hero-block.ts","../../../../packages/cms-schema/src/fields/complex/media.ts","../../../../packages/cms-schema/src/blocks/schemas/logo-trust-block.ts","../../../../packages/cms-schema/src/blocks/registry.ts","../../lib/renderer.tsx","../../lib/block-renderer.tsx","../../lib/cms-api.ts"],"sourcesContent":["/**\n * Article block normalization helpers shared across CMS and website apps.\n */\n\nexport interface ArticleContent {\n headline: string;\n author?: string;\n publishedAt?: string;\n body: string;\n tags?: readonly string[];\n status?: string;\n}\n\nexport interface ArticleRow {\n id: string;\n published_content: unknown;\n}\n\nexport interface NormalizedArticleRow extends ArticleContent {\n id: string;\n tags: string[];\n status: string;\n}\n\n/**\n * Normalize article content coming from Supabase blocks.\n * Returns null when required fields are missing or invalid.\n */\nexport function normalizeArticleContent(payload: unknown): ArticleContent | null {\n if (!payload || typeof payload !== 'object') {\n return null;\n }\n\n const record = payload as Record<string, unknown>;\n const headline = typeof record.headline === 'string' ? record.headline : null;\n const body = typeof record.body === 'string' ? record.body : null;\n\n if (!headline || !body) {\n return null;\n }\n\n const author = typeof record.author === 'string' ? record.author : undefined;\n const publishedAt = typeof record.publishedAt === 'string' ? record.publishedAt : undefined;\n const tags = Array.isArray(record.tags) ? record.tags.map((tag) => String(tag)) : undefined;\n const statusRaw = typeof record.status === 'string' ? record.status.trim() : undefined;\n\n return {\n headline,\n body,\n author,\n publishedAt,\n tags,\n status: statusRaw || undefined,\n };\n}\n\n/**\n * Normalize an article block row, applying defaults for tags/status.\n */\nexport function normalizeArticleRow(row: ArticleRow): NormalizedArticleRow | null {\n const article = normalizeArticleContent(row.published_content);\n if (!article) {\n return null;\n }\n\n return {\n ...article,\n id: row.id,\n tags: article.tags ? [...article.tags] : [],\n status: article.status ?? 'published',\n };\n}\n\n/**\n * Ensure the article is published.\n */\nexport function isArticlePublished(article: ArticleContent): boolean {\n const now = new Date();\n const publishedAt = article.publishedAt ? new Date(article.publishedAt) : null;\n\n return (\n article.status === 'published' &&\n publishedAt !== null &&\n !Number.isNaN(publishedAt.getTime()) &&\n publishedAt <= now\n );\n}\n","/**\n * Image Validation Utilities\n *\n * Provides validation for:\n * - File type (MIME type checking)\n * - File size (configurable limits)\n * - Image dimensions (width/height constraints)\n */\n\nimport { z } from 'zod';\n\n// =============================================================================\n// Constants\n// =============================================================================\n\nexport const ALLOWED_MIME_TYPES = ['image/jpeg', 'image/png', 'image/webp'] as const;\nexport type ImageMimeType = (typeof ALLOWED_MIME_TYPES)[number];\n\nexport const MAX_FILE_SIZE = 10 * 1024 * 1024; // 10MB\nexport const MIN_FILE_SIZE = 1024; // 1KB (avoid empty/corrupt files)\n\nexport const MAX_DIMENSION = 8192; // 8K resolution\nexport const MIN_DIMENSION = 10; // Minimum useful size\n\n// =============================================================================\n// Zod Schemas\n// =============================================================================\n\nexport const MimeTypeSchema = z.enum(ALLOWED_MIME_TYPES);\n\nexport const FileSizeSchema = z\n .number()\n .int()\n .min(MIN_FILE_SIZE, `File too small. Minimum: ${MIN_FILE_SIZE} bytes`)\n .max(MAX_FILE_SIZE, `File too large. Maximum: ${MAX_FILE_SIZE / 1024 / 1024}MB`);\n\nexport const DimensionSchema = z\n .number()\n .int()\n .min(MIN_DIMENSION, `Dimension too small. Minimum: ${MIN_DIMENSION}px`)\n .max(MAX_DIMENSION, `Dimension too large. Maximum: ${MAX_DIMENSION}px`);\n\n/**\n * Schema for upload request validation.\n */\nexport const UploadRequestSchema = z.object({\n filename: z\n .string()\n .min(1, 'Filename is required')\n .max(255, 'Filename too long')\n .regex(/^[^<>:\"/\\\\|?*]+$/, 'Filename contains invalid characters'),\n mimeType: MimeTypeSchema,\n fileSize: FileSizeSchema,\n});\n\nexport type UploadRequest = z.infer<typeof UploadRequestSchema>;\n\n/**\n * Schema for confirming upload with dimensions.\n * assetId is optional - if not provided, the server will generate a UUID.\n */\nexport const ConfirmUploadSchema = z.object({\n assetId: z.uuid().optional(),\n width: DimensionSchema,\n height: DimensionSchema,\n});\n\nexport type ConfirmUpload = z.infer<typeof ConfirmUploadSchema>;\n\n// =============================================================================\n// Validation Functions\n// =============================================================================\n\nexport interface ValidationResult {\n valid: boolean;\n error?: string;\n}\n\n/**\n * Validate file type by MIME type.\n */\nexport function validateMimeType(mimeType: string): ValidationResult {\n const result = MimeTypeSchema.safeParse(mimeType);\n if (result.success) {\n return { valid: true };\n }\n return {\n valid: false,\n error: `File type not supported. Use JPEG, PNG, or WebP.`,\n };\n}\n\n/**\n * Validate file size.\n */\nexport function validateFileSize(size: number): ValidationResult {\n if (size < MIN_FILE_SIZE) {\n return {\n valid: false,\n error: `File appears to be empty or corrupt.`,\n };\n }\n if (size > MAX_FILE_SIZE) {\n const sizeMB = (size / 1024 / 1024).toFixed(1);\n return {\n valid: false,\n error: `File too large (${sizeMB}MB). Maximum: 10 MB.`,\n };\n }\n return { valid: true };\n}\n\n/**\n * Validate image dimensions.\n */\nexport function validateDimensions(width: number, height: number): ValidationResult {\n if (width < MIN_DIMENSION || height < MIN_DIMENSION) {\n return {\n valid: false,\n error: `Image too small. Minimum: ${MIN_DIMENSION}x${MIN_DIMENSION}px.`,\n };\n }\n if (width > MAX_DIMENSION || height > MAX_DIMENSION) {\n return {\n valid: false,\n error: `Image too large. Maximum: ${MAX_DIMENSION}x${MAX_DIMENSION}px.`,\n };\n }\n return { valid: true };\n}\n\n/**\n * Validate complete upload request.\n */\nexport function validateUploadRequest(\n request: unknown\n): { valid: true; data: UploadRequest } | { valid: false; error: string } {\n const result = UploadRequestSchema.safeParse(request);\n if (result.success) {\n return { valid: true, data: result.data };\n }\n const firstIssue = result.error.issues[0];\n return {\n valid: false,\n error: firstIssue?.message ?? 'Invalid upload request',\n };\n}\n\n// =============================================================================\n// File Extension Utilities\n// =============================================================================\n\nconst MIME_TO_EXT: Record<ImageMimeType, string> = {\n 'image/jpeg': 'jpg',\n 'image/png': 'png',\n 'image/webp': 'webp',\n};\n\nconst EXT_TO_MIME: Record<string, ImageMimeType> = {\n jpg: 'image/jpeg',\n jpeg: 'image/jpeg',\n png: 'image/png',\n webp: 'image/webp',\n};\n\n/**\n * Get file extension from MIME type.\n */\nexport function getExtensionFromMime(mimeType: ImageMimeType | string): string {\n return MIME_TO_EXT[mimeType as ImageMimeType] ?? 'bin';\n}\n\n/**\n * Get MIME type from file extension.\n */\nexport function getMimeFromExtension(ext: string): ImageMimeType | undefined {\n return EXT_TO_MIME[ext.toLowerCase()];\n}\n\n/**\n * Extract extension from filename.\n */\nexport function getExtensionFromFilename(filename: string): string {\n const parts = filename.split('.');\n return parts.pop()?.toLowerCase?.() ?? '';\n}\n\n/**\n * Sanitize filename for storage.\n * Removes special characters, preserves extension.\n */\nexport function sanitizeFilename(filename: string): string {\n // Get extension\n const ext = getExtensionFromFilename(filename);\n\n // Get base name without extension\n const base = filename.slice(0, filename.length - ext.length - 1);\n\n // Sanitize: lowercase, replace spaces and special chars\n const sanitized = base\n .toLowerCase()\n .replace(/[^a-z0-9]/g, '-')\n .replace(/-+/g, '-')\n .replace(/^-|-$/g, '')\n .slice(0, 50); // Limit length\n\n return ext ? `${sanitized}.${ext}` : sanitized;\n}\n","/**\n * ArticleBlock Schema.\n *\n * Defines the structure and validation for ArticleBlock content.\n *\n * ArticleBlock represents a block of content focused on articles,\n * including fields such as headline, author, publication date, body content, tags, and status.\n *\n * Includes:\n * - Zod schema for validation\n * - TypeScript type inference\n * - Default values\n * - Validation function\n * - Function to create default content\n */\nimport { z } from 'zod';\n\n// Article block content schema with validation rules\nexport const ArticleBlockContentSchema = z.object({\n headline: z.string().min(1, 'Article headline is required').max(300, 'Headline too long').trim(),\n author: z.string().max(100, 'Article author too long').trim().optional(),\n publishedAt: z.iso\n .datetime({ message: 'Article publishedAt must be a valid ISO 8601 datetime string.' })\n .optional(),\n body: z.string().min(1, 'Article body content is required'),\n tags: z.array(z.string()).optional(),\n status: z.enum(['draft', 'review', 'published']),\n});\n\n// Inferred type for components\nexport type ArticleBlockContent = z.infer<typeof ArticleBlockContentSchema>;\n\n// Schema name constant\nexport const ARTICLE_BLOCK_SCHEMA_NAME = 'article';\n\n// Default values for a new ArticleBlock\nexport const ArticleBlockDefaults: Partial<ArticleBlockContent> = {\n status: 'draft',\n};\n\n// Validate ArticleBlock content\nexport function validateArticleBlockContent(content: unknown): {\n valid: boolean;\n data?: ArticleBlockContent;\n errors?: z.ZodError;\n} {\n const result = ArticleBlockContentSchema.safeParse(content);\n if (result.success) {\n return { valid: true, data: result.data };\n }\n return { valid: false, errors: result.error };\n}\n\n// Create default ArticleBlock content\nexport function createDefaultArticleContent(): ArticleBlockContent {\n return {\n headline: 'untitled article',\n author: '',\n publishedAt: new Date().toISOString(),\n body: '',\n tags: [],\n status: 'draft',\n };\n}\n","/**\n * CTABlock Schema.\n *\n * A call-to-action section with headline, description, and action buttons.\n * Supports primary and optional secondary CTA buttons.\n */\n\nimport { z } from 'zod';\n\n// CTA button schema\n// URL can be either a full URL (http://...) or a relative path (/path)\nconst CTAButtonSchema = z.object({\n text: z.string().min(1, 'Button text is required').max(50, 'Button text too long'),\n url: z.string().refine(\n (val) => {\n // Accept full URLs\n if (val.startsWith('http://') || val.startsWith('https://')) {\n try {\n new URL(val);\n return true;\n } catch {\n return false;\n }\n }\n // Accept relative paths starting with /\n if (val.startsWith('/')) {\n return true;\n }\n // Accept hash/anchor links\n if (val.startsWith('#')) {\n return true;\n }\n return false;\n },\n {\n message:\n 'URL must be a valid full URL (http://... or https://...), relative path (/path), or anchor (#anchor)',\n }\n ),\n});\n\nexport type CTAButton = z.infer<typeof CTAButtonSchema>;\n\n// CTABlock content schema with validation rules\nexport const CTABlockContentSchema = z.object({\n headline: z.string().min(1, 'Headline is required').max(100, 'Headline too long'),\n description: z.string().max(500, 'Description too long').optional(),\n primaryButton: CTAButtonSchema,\n secondaryButton: CTAButtonSchema.optional(),\n});\n\n// Inferred type for components\nexport type CTABlockContent = z.infer<typeof CTABlockContentSchema>;\n\n// Schema name constant\nexport const CTA_BLOCK_SCHEMA_NAME = 'cta-block';\n\n// Default values for a new CTABlock\nexport const CTABlockDefaults: Partial<CTABlockContent> = {\n primaryButton: {\n text: 'Get Started',\n url: '#',\n },\n};\n\n// Validate CTABlock content\nexport function validateCTABlockContent(content: unknown): {\n valid: boolean;\n data?: CTABlockContent;\n errors?: z.ZodError;\n} {\n const result = CTABlockContentSchema.safeParse(content);\n if (result.success) {\n return { valid: true, data: result.data };\n }\n return { valid: false, errors: result.error };\n}\n\n// Create default CTABlock content\nexport function createDefaultCTAContent(): CTABlockContent {\n return {\n headline: '',\n description: undefined,\n primaryButton: {\n text: 'Get Started',\n url: '#',\n },\n secondaryButton: undefined,\n };\n}\n","/**\n * FeaturesBlock Schema.\n *\n * A section displaying a list of features with icons, titles, and descriptions.\n * Supports different layouts (grid, list, carousel).\n */\n\nimport { z } from 'zod';\n\n// Layout options for features display\nexport const FeaturesLayout = ['grid', 'list', 'carousel'] as const;\nexport type FeaturesLayoutType = (typeof FeaturesLayout)[number];\n\n// Single feature item schema\nexport const FeatureItemSchema = z.object({\n icon: z.string().max(50, 'Icon name too long').optional(),\n title: z.string().min(1, 'Title is required').max(100, 'Title too long'),\n description: z.string().max(500, 'Description too long').optional(),\n});\n\nexport type FeatureItem = z.infer<typeof FeatureItemSchema>;\n\n// FeaturesBlock content schema with array validation\nexport const FeaturesBlockContentSchema = z.object({\n title: z.string().min(1, 'Section title is required').max(100, 'Title too long'),\n subtitle: z.string().max(200, 'Subtitle too long').optional(),\n features: z\n .array(FeatureItemSchema)\n .min(1, 'At least one feature is required')\n .max(6, 'Maximum 6 features allowed'),\n layout: z.enum(FeaturesLayout).default('grid'),\n});\n\n// Inferred type for components\nexport type FeaturesBlockContent = z.infer<typeof FeaturesBlockContentSchema>;\n\n// Schema name constant\nexport const FEATURES_BLOCK_SCHEMA_NAME = 'features-block';\n\n// Default values for a new FeaturesBlock\nexport const FeaturesBlockDefaults: Partial<FeaturesBlockContent> = {\n layout: 'grid',\n features: [\n {\n icon: 'star',\n title: 'Feature Title',\n description: 'Describe this feature',\n },\n ],\n};\n\n// Validate FeaturesBlock content\nexport function validateFeaturesBlockContent(content: unknown): {\n valid: boolean;\n data?: FeaturesBlockContent;\n errors?: z.ZodError;\n} {\n const result = FeaturesBlockContentSchema.safeParse(content);\n if (result.success) {\n return { valid: true, data: result.data };\n }\n return { valid: false, errors: result.error };\n}\n\n// Create default FeaturesBlock content\nexport function createDefaultFeaturesContent(): FeaturesBlockContent {\n return {\n title: '',\n subtitle: undefined,\n features: [],\n layout: 'grid',\n };\n}\n\n// Add a feature to the content\nexport function addFeature(\n content: FeaturesBlockContent,\n feature: FeatureItem\n): FeaturesBlockContent {\n if (content.features.length >= 6) {\n throw new Error('Maximum 6 features allowed');\n }\n return {\n ...content,\n features: [...content.features, feature],\n };\n}\n\n// Remove a feature by index\nexport function removeFeature(content: FeaturesBlockContent, index: number): FeaturesBlockContent {\n if (index < 0 || index >= content.features.length) {\n throw new Error('Invalid feature index');\n }\n if (content.features.length <= 1) {\n throw new Error('At least one feature is required');\n }\n return {\n ...content,\n features: content.features.filter((_, i) => i !== index),\n };\n}\n\n// Update a feature by index\nexport function updateFeature(\n content: FeaturesBlockContent,\n index: number,\n updates: Partial<FeatureItem>\n): FeaturesBlockContent {\n if (index < 0 || index >= content.features.length) {\n throw new Error('Invalid feature index');\n }\n return {\n ...content,\n features: content.features.map((feature, i) =>\n i === index ? { ...feature, ...updates } : feature\n ),\n };\n}\n","/**\n * HeroBlock Schema.\n *\n * A full-width hero section typically used at the top of pages.\n * Supports headline, subheadline, CTA button, background image, and alignment.\n */\n\nimport { z } from 'zod';\nimport { ImageReferenceSchema } from '../../fields/complex/media';\n\n// Alignment options for the hero content\nexport const HeroAlignment = ['left', 'center', 'right'] as const;\nexport type HeroAlignmentType = (typeof HeroAlignment)[number];\n\n// HeroBlock content schema with validation rules\nexport const HeroBlockContentSchema = z.object({\n headline: z.string().min(1, 'Headline is required').max(100, 'Headline too long'),\n subheadline: z.string().max(200, 'Subheadline too long').optional(),\n ctaText: z.string().max(50, 'CTA text too long').optional(),\n ctaUrl: z\n .string()\n .refine(\n (val) => {\n // Empty string is valid\n if (val === '') return true;\n // Accept full URLs\n if (val.startsWith('http://') || val.startsWith('https://')) {\n try {\n new URL(val);\n return true;\n } catch {\n return false;\n }\n }\n // Accept relative paths starting with /\n if (val.startsWith('/')) {\n return true;\n }\n // Accept hash/anchor links\n if (val.startsWith('#')) {\n return true;\n }\n return false;\n },\n {\n message:\n 'URL must be a valid full URL (http://... or https://...), relative path (/path), anchor (#anchor), or empty string',\n }\n )\n .optional()\n .or(z.literal('')),\n backgroundImage: ImageReferenceSchema.nullable().optional(),\n alignment: z.enum(HeroAlignment).default('center'),\n});\n\n// Inferred type for components\nexport type HeroBlockContent = z.infer<typeof HeroBlockContentSchema>;\n\n// Schema name constant\nexport const HERO_BLOCK_SCHEMA_NAME = 'hero-block';\n\n// Default values for a new HeroBlock\nexport const HeroBlockDefaults: Partial<HeroBlockContent> = {\n alignment: 'center',\n};\n\n// Validate HeroBlock content\nexport function validateHeroBlockContent(content: unknown): {\n valid: boolean;\n data?: HeroBlockContent;\n errors?: z.ZodError;\n} {\n const result = HeroBlockContentSchema.safeParse(content);\n if (result.success) {\n return { valid: true, data: result.data };\n }\n return { valid: false, errors: result.error };\n}\n\n// Create default HeroBlock content\nexport function createDefaultHeroContent(): HeroBlockContent {\n return {\n headline: '',\n subheadline: undefined,\n ctaText: undefined,\n ctaUrl: undefined,\n backgroundImage: undefined,\n alignment: 'center',\n };\n}\n","/**\n * Image and file field factories.\n *\n * The image() factory creates an ImageReference schema that:\n * - References an ImageAsset by ID\n * - Stores context-specific alt text (required for accessibility)\n * - Supports optional hotspot (focal point) and crop settings\n */\n\nimport { z } from 'zod';\n\nimport type { FieldMeta } from '../../types';\nimport {\n DimensionSchema,\n FileSizeSchema,\n MAX_DIMENSION,\n MAX_FILE_SIZE,\n MIN_DIMENSION,\n MimeTypeSchema,\n} from '../../validation/image';\n\n// =============================================================================\n// ImageAsset Schema (Public API type)\n// =============================================================================\n\n/**\n * Public API schema for image assets.\n *\n * This is the camelCase API representation. The database uses snake_case\n * (ImageAssetRow in db/image-asset-types.ts). Conversion between formats\n * should happen at the API boundary.\n *\n * Reuses validation schemas from validation/image.ts to ensure consistency.\n */\nexport const ImageAssetSchema = z.object({\n /** UUID primary key */\n id: z.uuid(),\n /** R2/S3 storage URL for the original file */\n url: z.url(),\n /** Image width in pixels */\n width: DimensionSchema,\n /** Image height in pixels */\n height: DimensionSchema,\n /** Original filename from upload */\n originalFilename: z.string().min(1).max(255),\n /** MIME type (only web-safe formats allowed) */\n mimeType: MimeTypeSchema,\n /** File size in bytes */\n fileSize: FileSizeSchema,\n /** Base64-encoded tiny preview for blur-up loading */\n lqip: z.string().optional(),\n});\n\nexport type ImageAsset = z.infer<typeof ImageAssetSchema>;\n\n// =============================================================================\n// ImageReference Schema (Asset-reference model)\n// =============================================================================\n\n/**\n * Hotspot defines the focal point of an image.\n * Coordinates are fractions (0-1) from top-left.\n */\nexport const HotspotSchema = z.object({\n x: z.number().min(0).max(1),\n y: z.number().min(0).max(1),\n});\n\nexport type Hotspot = z.infer<typeof HotspotSchema>;\n\n/**\n * Crop defines the region to extract from the image.\n * - x, y: Top-left coordinate of the crop region in pixels\n * - width, height: Size of the crop region in pixels\n * All values are positive integers representing pixel coordinates/dimensions.\n */\nexport const CropSchema = z.object({\n /** X coordinate of top-left corner in pixels */\n x: z.number().int().nonnegative(),\n /** Y coordinate of top-left corner in pixels */\n y: z.number().int().nonnegative(),\n /** Width of crop region in pixels (must be > 0) */\n width: z.number().int().positive(),\n /** Height of crop region in pixels (must be > 0) */\n height: z.number().int().positive(),\n});\n\nexport type Crop = z.infer<typeof CropSchema>;\n\n/**\n * ImageReference is what blocks store.\n * It points to an ImageAsset and adds context-specific metadata.\n */\nexport const ImageReferenceSchema = z.object({\n // Alt text is REQUIRED for accessibility\n alt: z.string().min(1, 'Alt text is required for accessibility'),\n\n // Optional metadata\n caption: z.string().max(500).optional(),\n attribution: z.string().max(255).optional(),\n\n // Reference to the ImageAsset with stored transformation\n _asset: z.object({\n id: z.uuid(),\n transformation: z.string().nullable().optional(),\n }),\n});\n\nexport type ImageReference = z.infer<typeof ImageReferenceSchema>;\n\n// =============================================================================\n// Image Field Factory\n// =============================================================================\n\nexport interface ImageFieldOptions {\n label: string;\n description?: string;\n required?: boolean;\n accept?: string;\n maxSize?: number;\n minWidth?: number;\n minHeight?: number;\n maxWidth?: number;\n maxHeight?: number;\n aspectRatio?: number;\n}\n\n/**\n * Create an image field with metadata.\n *\n * @example\n * const featuredImage = image({\n * label: 'Featured Image',\n * required: true,\n * accept: 'image/jpeg, image/png',\n * maxSize: 5 * 1024 * 1024,\n * });\n *\n * // Type of the value:\n * // {\n * // alt: string;\n * // caption?: string;\n * // attribution?: string;\n * // _asset: { id: string; transformation?: string | null };\n * // }\n */\nexport function image(options: ImageFieldOptions) {\n const meta: FieldMeta = {\n label: options.label,\n component: 'image-uploader',\n required: options.required,\n description: options.description,\n options: {\n accept: options.accept ?? 'image/jpeg, image/png, image/webp',\n maxSize: options.maxSize ?? MAX_FILE_SIZE, // 10MB default\n minWidth: options.minWidth ?? MIN_DIMENSION,\n minHeight: options.minHeight ?? MIN_DIMENSION,\n maxWidth: options.maxWidth ?? MAX_DIMENSION,\n maxHeight: options.maxHeight ?? MAX_DIMENSION,\n aspectRatio: options.aspectRatio,\n },\n };\n\n // Use the new ImageReferenceSchema instead of URL-only\n const result = options.required ? ImageReferenceSchema : ImageReferenceSchema.optional();\n\n return result.meta(meta);\n}\n\n// =============================================================================\n// File Field\n// =============================================================================\n\nexport interface FileFieldOptions {\n label: string;\n description?: string;\n required?: boolean;\n accept?: string;\n maxSize?: number;\n}\n\nconst fileSchema = z.object({\n url: z.url(),\n name: z.string(),\n size: z.number().int().positive(),\n type: z.string(),\n});\n\n/**\n * Create a file field with metadata.\n */\nexport function file(options: FileFieldOptions) {\n const meta: FieldMeta = {\n label: options.label,\n component: 'file-uploader',\n required: options.required,\n description: options.description,\n options: {\n accept: options.accept ?? '*/*',\n maxSize: options.maxSize,\n },\n };\n\n const result = options.required ? fileSchema : fileSchema.optional();\n return result.meta(meta);\n}\n","/**\n * LogoTrustBlock Schema.\n *\n * A section displaying customer/partner logos for trust and credibility.\n * Supports an optional title and an array of logo images.\n *\n * Each logo uses ImageReference to integrate with the image manager.\n */\n\nimport { z } from 'zod';\n\nimport { ImageReferenceSchema } from '../../fields/complex/media';\n\n// Logo item schema - uses ImageReference for image manager integration\nexport const LogoItemSchema = z.object({\n /** Unique ID for this logo item */\n id: z.uuid(),\n /** Image reference (alt + _asset with transformation) */\n image: ImageReferenceSchema,\n /** Optional company/brand name to display */\n name: z.string().max(100, 'Name too long').optional(),\n});\n\nexport type LogoItem = z.infer<typeof LogoItemSchema>;\n\n/**\n * Legacy logo format schema for backwards compatibility.\n * Used for logos stored with direct URL before the ImageReference migration.\n */\nexport const LegacyLogoItemSchema = z.object({\n /** Direct URL to the logo image */\n url: z.string(),\n /** Alt text for the image */\n alt: z.string(),\n /** Optional company/brand name */\n name: z.string().optional(),\n});\n\nexport type LegacyLogoItem = z.infer<typeof LegacyLogoItemSchema>;\n\n// LogoTrustBlock content schema with validation rules\nexport const LogoTrustBlockContentSchema = z.object({\n title: z.string().max(100, 'Title too long').optional(),\n logos: z.array(LogoItemSchema).max(20, 'Maximum 20 logos allowed'),\n});\n\n// Inferred type for components\nexport type LogoTrustBlockContent = z.infer<typeof LogoTrustBlockContentSchema>;\n\n// Schema name constant\nexport const LOGO_TRUST_BLOCK_SCHEMA_NAME = 'logo-trust-block';\n\n// Default values for a new LogoTrustBlock\nexport const LogoTrustBlockDefaults: Partial<LogoTrustBlockContent> = {\n logos: [],\n};\n\n// Validate LogoTrustBlock content\nexport function validateLogoTrustBlockContent(content: unknown): {\n valid: boolean;\n data?: LogoTrustBlockContent;\n errors?: z.ZodError;\n} {\n const result = LogoTrustBlockContentSchema.safeParse(content);\n if (result.success) {\n return { valid: true, data: result.data };\n }\n return { valid: false, errors: result.error };\n}\n\n// Create default LogoTrustBlock content\nexport function createDefaultLogoTrustContent(): LogoTrustBlockContent {\n return {\n title: undefined,\n logos: [],\n };\n}\n","/**\n * Block Schema Registry.\n *\n * Centralizes registration for all block schemas.\n * Call registerAllBlockSchemas() during application initialization.\n */\n\nimport { clearSchemaRegistry, registerBlockSchema } from '../validation';\nimport { ARTICLE_BLOCK_SCHEMA_NAME, ArticleBlockContentSchema } from './schemas/article-block';\nimport { CTA_BLOCK_SCHEMA_NAME, CTABlockContentSchema } from './schemas/cta-block';\nimport { FEATURES_BLOCK_SCHEMA_NAME, FeaturesBlockContentSchema } from './schemas/features-block';\nimport { HERO_BLOCK_SCHEMA_NAME, HeroBlockContentSchema } from './schemas/hero-block';\nimport {\n LOGO_TRUST_BLOCK_SCHEMA_NAME,\n LogoTrustBlockContentSchema,\n} from './schemas/logo-trust-block';\n\n// All registered block schema names\nexport const BLOCK_SCHEMA_NAMES = [\n ARTICLE_BLOCK_SCHEMA_NAME,\n HERO_BLOCK_SCHEMA_NAME,\n FEATURES_BLOCK_SCHEMA_NAME,\n CTA_BLOCK_SCHEMA_NAME,\n LOGO_TRUST_BLOCK_SCHEMA_NAME,\n] as const;\n\n// Union type for type-safe schema name usage\nexport type BlockSchemaName = (typeof BLOCK_SCHEMA_NAMES)[number];\n\n// Register all block schemas\nexport function registerAllBlockSchemas(): void {\n registerBlockSchema(ARTICLE_BLOCK_SCHEMA_NAME, ArticleBlockContentSchema);\n registerBlockSchema(HERO_BLOCK_SCHEMA_NAME, HeroBlockContentSchema);\n registerBlockSchema(FEATURES_BLOCK_SCHEMA_NAME, FeaturesBlockContentSchema);\n registerBlockSchema(CTA_BLOCK_SCHEMA_NAME, CTABlockContentSchema);\n registerBlockSchema(LOGO_TRUST_BLOCK_SCHEMA_NAME, LogoTrustBlockContentSchema);\n}\n\n// Clear and re-register all block schemas (for testing)\nexport function resetBlockSchemas(): void {\n clearSchemaRegistry();\n registerAllBlockSchemas();\n}\n\n// Type guard for schema names\nexport function isValidBlockSchemaName(name: string): name is BlockSchemaName {\n return BLOCK_SCHEMA_NAMES.includes(name as BlockSchemaName);\n}\n","/**\n * Catch-all Route Handler for Parametric Routes\n *\n * Handles routes with multiple segments like /us/en/products.\n * Uses the CMS API to fetch and render blocks.\n */\n\nimport {\n isArticlePublished,\n isValidBlockSchemaName,\n normalizeArticleContent,\n} from '@repo/cms-schema/blocks';\nimport type { GeneratedBlockRow } from '@repo/cms-schema/db';\nimport type { Metadata } from 'next';\nimport { notFound } from 'next/navigation';\nimport { BlockRenderer } from './block-renderer';\nimport { type CmsConfig, getCmsClient } from './cms-api';\nimport type { BlockComponentRegistry, BlockData, ResolvedRouteParams } from './types';\n\ntype PageProps = CmsConfig & {\n params: Promise<{ slug: string[] }>;\n searchParams?: Promise<{ [key: string]: string | string[] | undefined }>;\n registry?: Partial<BlockComponentRegistry>;\n};\n\nfunction getWebsiteId(providedWebsiteId?: string): string {\n const websiteId =\n providedWebsiteId ??\n process.env.NEXT_PUBLIC_WEBSITE_ID ??\n process.env.WEBSITE_ID ??\n process.env.CMS_WEBSITE_ID;\n\n if (!websiteId) {\n throw new Error(\n 'Missing websiteId for website renderer. Either pass websiteId prop or set NEXT_PUBLIC_WEBSITE_ID (or WEBSITE_ID/CMS_WEBSITE_ID) to a valid UUID.'\n );\n }\n\n const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n if (!uuidRegex.test(websiteId)) {\n throw new Error(\n `Invalid websiteId \"${websiteId}\". Provide a valid UUID via prop or set NEXT_PUBLIC_WEBSITE_ID (or WEBSITE_ID/CMS_WEBSITE_ID).`\n );\n }\n\n return websiteId;\n}\n\n/**\n * Catch-all route handler for parametric routes.\n *\n * Handles paths like:\n * - /us/en/products -> slug = ['us', 'en', 'products']\n * - /about -> slug = ['about']\n *\n * Reconstructs the full path and fetches route via tRPC.\n */\nexport default async function ParametricRoutePage({\n params,\n searchParams,\n registry,\n apiKey,\n cmsUrl,\n websiteId: providedWebsiteId,\n}: PageProps) {\n const websiteId = getWebsiteId(providedWebsiteId);\n const { slug } = await params;\n const resolvedSearchParams = await searchParams;\n\n // Check for ai_preview query param to determine if we should show generated blocks\n let aiPreviewIndex: number | null = null;\n const aiPreviewParam = resolvedSearchParams?.ai_preview;\n if (aiPreviewParam) {\n const paramValue = Array.isArray(aiPreviewParam) ? aiPreviewParam[0] : aiPreviewParam;\n if (paramValue) {\n const parsed = parseInt(paramValue, 10);\n if (!Number.isNaN(parsed)) {\n aiPreviewIndex = parsed;\n }\n }\n }\n\n // Check for edit_mode query param to enable editable wrappers\n const editModeParam = resolvedSearchParams?.edit_mode;\n const editMode = editModeParam === 'true' || editModeParam === '1';\n\n // Reconstruct full path from slug segments and normalize it\n const rawPath = `/${slug.join('/')}`;\n const path = normalizePath(rawPath);\n\n // Get CMS API client with optional API key and custom URL\n const client = getCmsClient({ apiKey, cmsUrl });\n\n try {\n // Fetch route by path via CMS API\n const { route, resolvedParams } = await client.route.getByPath.query({ websiteId, path });\n\n // Only show Live routes on public website\n if (route.state !== 'Live') {\n console.error(`Route found but not Live. Path: ${path}, State: ${route.state}`);\n notFound();\n }\n\n // Fetch all blocks by ID via CMS API (skip any that fail)\n const blockPromises = route.block_ids.map(async (blockId) => {\n try {\n const result = await client.block.getById.query({ websiteId, id: blockId });\n return result.block;\n } catch (error) {\n // Log error but don't fail the entire page\n console.error(`Failed to fetch block ${blockId}:`, error);\n return null;\n }\n });\n\n // Fetch generated blocks for preview only if ai_preview param is set\n // generated_content is an array of variants: [{ description, content }, ...]\n type GeneratedBlockData = Pick<\n GeneratedBlockRow,\n 'id' | 'generated_content' | 'generated_layout' | 'schema_name' | 'status'\n >;\n const generatedBlocksPromise =\n aiPreviewIndex !== null\n ? client.block.getGeneratedByBlockIds\n .query({ websiteId, blockIds: route.block_ids })\n .catch((error) => {\n console.error('Failed to fetch generated blocks:', error);\n return { generatedBlocks: {} as Record<string, GeneratedBlockData> };\n })\n : Promise.resolve({ generatedBlocks: {} as Record<string, GeneratedBlockData> });\n\n const [blockResults, { generatedBlocks }] = await Promise.all([\n Promise.all(blockPromises),\n generatedBlocksPromise,\n ]);\n\n // Transform blocks to BlockData format for BlockRenderer\n // Filter out any blocks that failed to load\n // Use generated content only when ai_preview param is set\n const blocks: BlockData[] = [];\n\n for (const block of blockResults) {\n if (!block || block.published_content === null) continue;\n\n // Use generated content only if ai_preview is set and generated block exists\n // ai_preview is 1-indexed, so subtract 1 to get array index\n let content: Record<string, unknown> | null = null;\n if (aiPreviewIndex !== null) {\n const generatedBlock = generatedBlocks[block.id] as GeneratedBlockData | undefined;\n const variantIndex = aiPreviewIndex - 1; // Convert to 0-indexed\n const variants = generatedBlock?.generated_content;\n if (variants && Array.isArray(variants) && variants[variantIndex]) {\n content = variants[variantIndex].content;\n }\n }\n // Fall back to published content if no variant found\n content = content ?? (block.published_content as Record<string, unknown> | null);\n if (!content) continue;\n\n // Handle 'article' blocks separately before checking schema type\n if (block.schema_name === 'article') {\n const article = normalizeArticleContent(content);\n const isPublished = article ? isArticlePublished(article) : null;\n if (article && isPublished) {\n blocks.push({ id: block.id, type: 'article', content: article });\n }\n continue;\n }\n\n // Custom blocks have a schema_id (UUID); use schema_name as the type\n // so consumers register components by schema_name in the registry\n if (block.schema_id) {\n blocks.push({ id: block.id, type: block.schema_name, content });\n continue;\n }\n\n // Skip predefined blocks with invalid schema names\n if (!isValidBlockSchemaName(block.schema_name)) {\n continue;\n }\n\n // For all predefined block types, map schema_name to type and include content\n // Image references are automatically resolved by block.getById\n blocks.push({\n id: block.id,\n type: block.schema_name,\n content,\n } as BlockData);\n }\n\n // Map resolved params to the slimmed-down shape for block components\n const routeParams: ResolvedRouteParams | undefined = resolvedParams\n ? Object.fromEntries(\n Object.entries(resolvedParams).map(([key, param]) => [\n key,\n {\n value: param.value,\n schemaName: param.schemaName,\n document: {\n id: param.document.id,\n title: param.document.title,\n content: param.document.content,\n schema_name: param.document.schema_name,\n },\n },\n ])\n )\n : undefined;\n\n return (\n <main>\n {blocks.map((block) => (\n <BlockRenderer\n key={block.id}\n block={block}\n registry={registry ?? {}}\n disableEditable={!editMode}\n routeParams={routeParams}\n />\n ))}\n </main>\n );\n } catch (error) {\n // Log error for debugging\n console.error(`Route fetch error for path: ${path}`, error);\n\n // If route not found or param validation fails, show 404\n // TRPCClientError has data.code for the error code\n const errorCode =\n error instanceof Error && 'data' in error\n ? (error as { data?: { code?: string } }).data?.code\n : error instanceof Error && 'code' in error\n ? (error as { code: string }).code\n : undefined;\n\n if (errorCode === 'NOT_FOUND' || errorCode === 'P0002') {\n notFound();\n }\n\n // Re-throw other errors\n throw error;\n }\n}\n\n// -----------------------------------------------------------------------------\n// Metadata\n// -----------------------------------------------------------------------------\n\n/**\n * Generate metadata for the page.\n * Uses Next.js 15+ async params pattern.\n */\nexport async function generateMetadata({\n params,\n apiKey,\n cmsUrl,\n websiteId: providedWebsiteId,\n}: PageProps): Promise<Metadata> {\n const websiteId = getWebsiteId(providedWebsiteId);\n const { slug } = await params;\n const rawPath = `/${slug.join('/')}`;\n const path = normalizePath(rawPath);\n const client = getCmsClient({ apiKey, cmsUrl });\n\n try {\n const { route } = await client.route.getByPath.query({ websiteId, path });\n return {\n title: `${route.path} | Website`,\n description: `Content page: ${route.path}`,\n };\n } catch {\n return {\n title: 'Page Not Found | Website',\n description: 'The requested page could not be found.',\n };\n }\n}\n\nexport function normalizePath(path: string): string {\n if (!path || path === '/') {\n return '/';\n }\n\n // Remove trailing slashes, ensure leading slash\n let normalized = path.trim();\n\n // Remove trailing slashes (but keep root \"/\")\n normalized = normalized.replace(/\\/+$/, '');\n\n // Ensure leading slash\n if (!normalized.startsWith('/')) {\n normalized = `/${normalized}`;\n }\n\n // Collapse multiple consecutive slashes to single slash\n normalized = normalized.replace(/\\/+/g, '/');\n\n return normalized;\n}\n","/**\n * Block Renderer Component\n *\n * Dispatches block data to the appropriate component using the ComponentMap pattern.\n * This is the main entry point for rendering blocks from the CMS.\n */\n\nimport React from 'react';\nimport { ClientEditableBlock } from './client-editable-block';\nimport { BlockToolbar } from './block-toolbar';\nimport type { BlockComponentRegistry, BlockData, ResolvedRouteParams } from './types';\n\ntype TextInfo = {\n value: string;\n path: Array<string | number>;\n parentType?: React.ElementType;\n key?: React.Key | null;\n};\n\ntype ElementInfo = {\n element: React.ReactElement;\n path: Array<string | number>;\n};\n\ntype WalkVisitors = {\n /**\n * Called for every string/number child encountered.\n * Return:\n * - same string (or modified)\n * - a ReactNode (e.g. wrap in <span/>)\n */\n onText?: (info: TextInfo) => React.ReactNode;\n\n /**\n * Called for every ReactElement encountered (after children are processed).\n * Return:\n * - same element\n * - a cloned/modified element\n */\n onElement?: (info: ElementInfo) => React.ReactElement;\n};\n\n/**\n * Recursively maps a ReactNode tree, allowing transformations of text nodes and/or elements.\n * SSR-safe: does not touch DOM APIs.\n */\nexport function walkReactNode(\n node: React.ReactNode,\n visitors: WalkVisitors,\n ctx: {\n path?: Array<string | number>;\n parentType?: React.ElementType;\n key?: React.Key | null;\n } = {}\n): React.ReactNode {\n const path = ctx.path ?? [];\n\n // Fast-path primitives\n if (node == null || typeof node === 'boolean') return node;\n\n if (typeof node === 'string' || typeof node === 'number') {\n const value = String(node);\n return visitors.onText\n ? visitors.onText({ value, path, parentType: ctx.parentType, key: ctx.key })\n : node;\n }\n\n // Arrays\n if (Array.isArray(node)) {\n return node.map((child, i) => {\n // biome-ignore lint/suspicious/noExplicitAny: React child key access\n const childKey = (child as any)?.key ?? null;\n const result = walkReactNode(child, visitors, {\n path: [...path, i],\n parentType: ctx.parentType,\n key: childKey,\n });\n // Ensure array children have keys\n if (React.isValidElement(result) && result.key == null) {\n return React.cloneElement(result, { key: childKey ?? `arr-${path.join('-')}-${i}` });\n }\n return result;\n });\n }\n\n // ReactElement (including Fragment)\n if (React.isValidElement(node)) {\n // biome-ignore lint/suspicious/noExplicitAny: React element props access\n const el = node as React.ReactElement<any>;\n const elProps = el.props as Record<string, unknown> | null;\n\n // Recurse into children (if any)\n const hasChildren = elProps && 'children' in elProps;\n const nextChildren = hasChildren\n ? React.Children.map(elProps.children as React.ReactNode, (child, i) => {\n // biome-ignore lint/suspicious/noExplicitAny: React child key access\n const childKey = (child as any)?.key ?? null;\n const result = walkReactNode(child, visitors, {\n path: [...path, 'children', i],\n parentType: el.type as React.ElementType,\n key: childKey,\n });\n // Ensure children have keys\n if (React.isValidElement(result) && result.key == null) {\n return React.cloneElement(result, { key: childKey ?? `child-${path.join('-')}-${i}` });\n }\n return result;\n })\n : (elProps?.children as React.ReactNode);\n\n // Only clone if children changed (or if you want to force a clone)\n const cloned = hasChildren\n ? React.cloneElement(el, undefined, nextChildren as React.ReactNode)\n : el;\n\n return visitors.onElement ? visitors.onElement({ element: cloned, path }) : cloned;\n }\n\n // Functions, symbols, portals, etc. are rare here; return as-is\n return node;\n}\n\n// -----------------------------------------------------------------------------\n// Content Value Extraction\n// -----------------------------------------------------------------------------\n\ntype ContentMatch = {\n contentPath: string;\n value: string;\n};\n\n/**\n * Extracts all string values from a content object with their paths.\n * Returns a Map where keys are string values and values are arrays of content paths.\n */\nfunction extractContentValues(\n content: Record<string, unknown>,\n basePath: string[] = []\n): Map<string, ContentMatch[]> {\n const map = new Map<string, ContentMatch[]>();\n\n function walk(obj: unknown, path: string[]) {\n if (typeof obj === 'string' && obj.trim() !== '') {\n const contentPath = path.join('.');\n const existing = map.get(obj) || [];\n existing.push({ contentPath, value: obj });\n map.set(obj, existing);\n } else if (Array.isArray(obj)) {\n for (let index = 0; index < obj.length; index++) {\n walk(obj[index], [...path, String(index)]);\n }\n } else if (obj && typeof obj === 'object') {\n for (const [key, value] of Object.entries(obj)) {\n walk(value, [...path, key]);\n }\n }\n }\n\n walk(content, basePath);\n return map;\n}\n\n// -----------------------------------------------------------------------------\n// Props\n// -----------------------------------------------------------------------------\n\ninterface BlockRendererProps {\n /**\n * The block data to render.\n * Must have a `type` field that maps to a registered component.\n */\n block: BlockData;\n registry: Partial<BlockComponentRegistry>;\n /**\n * If true, renders the component without any tree walking or editable wrappers.\n */\n disableEditable?: boolean;\n /**\n * Resolved route parameters from parametric routes.\n * Each key is a param name (e.g., \"country\") with its value, schema name, and full document.\n */\n routeParams?: ResolvedRouteParams;\n}\n\n// -----------------------------------------------------------------------------\n// Component\n// -----------------------------------------------------------------------------\n\n/**\n * Recursively renders a React node, invoking function components to get their output.\n * This allows us to walk the full rendered tree, not just the element wrappers.\n */\nfunction renderToWalkableTree(node: React.ReactNode, keyPrefix = ''): React.ReactNode {\n if (node == null || typeof node === 'boolean') return node;\n if (typeof node === 'string' || typeof node === 'number') return node;\n\n if (Array.isArray(node)) {\n return node.map((child, i) => {\n const result = renderToWalkableTree(child, `${keyPrefix}${i}-`);\n // Ensure array children have keys\n if (React.isValidElement(result) && result.key == null) {\n // biome-ignore lint/suspicious/noExplicitAny: Adding key to element\n const existingKey = (child as any)?.key;\n return React.cloneElement(result, { key: existingKey ?? `${keyPrefix}${i}` });\n }\n return result;\n });\n }\n\n if (React.isValidElement(node)) {\n // biome-ignore lint/suspicious/noExplicitAny: React element props access\n const el = node as React.ReactElement<any>;\n const elProps = el.props as Record<string, unknown> | null;\n\n // If it's a function component, invoke it to get the rendered output\n if (typeof el.type === 'function') {\n try {\n // biome-ignore lint/complexity/noBannedTypes: Need to invoke React function component\n const rendered = (el.type as Function)(el.props);\n return renderToWalkableTree(rendered, keyPrefix);\n } catch {\n // If component throws (e.g., uses hooks), return as-is\n return node;\n }\n }\n\n // For host elements (div, span, etc.), recurse into children\n if (elProps && 'children' in elProps) {\n const newChildren = renderToWalkableTree(elProps.children as React.ReactNode, keyPrefix);\n return React.cloneElement(el, undefined, newChildren);\n }\n\n return node;\n }\n\n return node;\n}\n\n/**\n * Renders a single block by dispatching to the appropriate component.\n *\n * Uses the ComponentMap pattern: the block's `type` field determines which\n * component renders the block's `content`.\n *\n * Internally, it:\n * 1. Renders the component tree by invoking function components\n * 2. Extracts all string values from block.content\n * 3. Walks the rendered tree and wraps matching text nodes with spans\n */\nexport function BlockRenderer({\n block,\n registry,\n disableEditable,\n routeParams,\n}: BlockRendererProps) {\n const Component = registry[block.type];\n\n if (!Component) {\n // Log warning in development, render nothing in production\n if (process.env.NODE_ENV === 'development') {\n console.warn(`[BlockRenderer] Unknown block type: ${block.type}`);\n }\n return null;\n }\n\n // biome-ignore lint/suspicious/noExplicitAny: Type safety ensured by BlockData discriminated union\n const component = <Component content={block.content as any} routeParams={routeParams} />;\n\n if (disableEditable) {\n return component;\n }\n\n // Extract all string values from content with their paths\n const contentValueMap = extractContentValues(block.content as Record<string, unknown>);\n\n // Try server-side tree walking for text span injection (best-effort).\n // This works for sync server components but fails for client components,\n // async components, and components using hooks.\n let renderedComponent: React.ReactNode = component;\n let needsClientSideSpans = true;\n\n try {\n const renderedTree = renderToWalkableTree(component);\n\n // Check if tree walking produced host elements (div, section, etc.)\n // vs the original component reference (function type = couldn't be invoked)\n const isWalkable =\n React.isValidElement(renderedTree) &&\n typeof (renderedTree as React.ReactElement<Record<string, unknown>>).type === 'string';\n\n if (isWalkable) {\n const usedPaths = new Set<string>();\n\n renderedComponent = walkReactNode(renderedTree, {\n onText: ({ value, key, path }) => {\n const matches = contentValueMap.get(value);\n if (!matches || matches.length === 0) return value;\n\n const match = matches.find((m) => !usedPaths.has(m.contentPath)) ?? matches[0];\n if (!match) return value;\n\n usedPaths.add(match.contentPath);\n const spanKey = key ?? `${block.id}-${match.contentPath}-${path.join('-')}`;\n\n return (\n <span\n key={spanKey}\n data-cms-editable\n data-block-id={block.id}\n data-block-type={block.type}\n data-content-path={match.contentPath}\n >\n {value}\n </span>\n );\n },\n });\n\n needsClientSideSpans = false;\n }\n } catch {\n // Tree walking failed entirely, fall back to client-side injection\n }\n\n // Build content entries for client-side text matching when server-side fails\n const contentEntries = needsClientSideSpans\n ? Array.from(contentValueMap.entries())\n .map(([value, matches]) => ({ v: value, p: matches[0]?.contentPath }))\n .filter((e): e is { v: string; p: string } => !!e.p)\n : [];\n\n return (\n <div\n key={block.id}\n data-cms-block\n data-block-id={block.id}\n data-block-type={block.type}\n style={{ position: 'relative' }}\n >\n <style>{`\n [data-cms-block] {\n position: relative;\n }\n [data-cms-block]:hover {\n outline: 2px solid #3b82f6;\n outline-offset: 4px;\n }\n [data-cms-editable] {\n cursor: pointer;\n border-radius: 2px;\n }\n [data-cms-editable]:hover {\n outline: 2px solid #3b82f6;\n outline-offset: 2px;\n }\n .cms-block-toolbar {\n position: absolute;\n bottom: 8px;\n left: 50%;\n transform: translateX(-50%);\n display: flex;\n gap: 4px;\n background: #1f2937;\n border-radius: 6px;\n padding: 4px;\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);\n opacity: 0;\n pointer-events: none;\n transition: opacity 0.15s ease;\n z-index: 1000;\n }\n [data-cms-block]:hover .cms-block-toolbar {\n opacity: 1;\n pointer-events: auto;\n }\n .cms-block-toolbar button {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 28px;\n height: 28px;\n border: none;\n background: transparent;\n color: #9ca3af;\n border-radius: 4px;\n cursor: pointer;\n transition: background 0.15s ease, color 0.15s ease;\n }\n .cms-block-toolbar button:hover {\n background: #374151;\n color: #fff;\n }\n .cms-block-toolbar button.delete:hover {\n background: #dc2626;\n color: #fff;\n }\n .cms-block-toolbar button:disabled {\n opacity: 0.4;\n cursor: not-allowed;\n }\n .cms-block-toolbar button:disabled:hover {\n background: transparent;\n color: #9ca3af;\n }\n .cms-block-toolbar svg {\n width: 16px;\n height: 16px;\n }\n `}</style>\n <script\n // biome-ignore lint/security/noDangerouslySetInnerHtml: Inline script for iframe postMessage click routing\n dangerouslySetInnerHTML={{\n __html: `\n (function() {\n if (!window.__cmsEditableInitialized) {\n window.__cmsEditableInitialized = true;\n\n document.addEventListener('click', function(e) {\n if (e.target.closest('.cms-block-toolbar')) return;\n\n var editableTarget = e.target.closest('[data-cms-editable]');\n if (editableTarget) {\n var message = {\n type: 'cms-editable-click',\n blockId: editableTarget.getAttribute('data-block-id'),\n blockType: editableTarget.getAttribute('data-block-type'),\n contentPath: editableTarget.getAttribute('data-content-path')\n };\n if (window.parent && window.parent !== window) {\n window.parent.postMessage(message, '*');\n }\n return;\n }\n\n var blockTarget = e.target.closest('[data-cms-block]');\n if (blockTarget) {\n var message = {\n type: 'cms-editable-click',\n blockId: blockTarget.getAttribute('data-block-id'),\n blockType: blockTarget.getAttribute('data-block-type'),\n contentPath: null\n };\n if (window.parent && window.parent !== window) {\n window.parent.postMessage(message, '*');\n }\n }\n });\n }\n })();\n `,\n }}\n />\n {needsClientSideSpans && contentEntries.length > 0 ? (\n <ClientEditableBlock\n blockId={block.id}\n blockType={block.type}\n contentEntries={contentEntries}\n >\n {renderedComponent}\n </ClientEditableBlock>\n ) : (\n renderedComponent\n )}\n <BlockToolbar key=\"cms-toolbar\" blockId={block.id} />\n </div>\n );\n}\n","/**\n * CMS API Client\n *\n * Creates an HTTP-based tRPC client for calling the CMS API.\n * Used in Server Components to fetch routes and blocks from the CMS.\n */\n\nimport type { AppRouter } from '@repo/cms-schema/trpc';\nimport { type CreateTRPCClient, createTRPCClient, httpBatchLink } from '@trpc/client';\nimport superjson from 'superjson';\n\n/** Type alias for the CMS API client */\ntype CmsClient = CreateTRPCClient<AppRouter>;\n\n/**\n * Get the CMS API URL from the provided base URL.\n */\nfunction getCmsApiUrl(cmsUrl: string): string {\n return new URL('/api/trpc', cmsUrl).toString();\n}\n\n/** Unified configuration for all CMS API access */\nexport interface CmsConfig {\n /** CMS API base URL (e.g., 'http://localhost:3000') */\n cmsUrl: string;\n /** API key for authentication */\n apiKey?: string;\n /** Website ID to scope API requests */\n websiteId?: string;\n}\n\n/**\n * Create a custom fetch function that appends API key as query parameter.\n */\nfunction createFetchWithApiKey(apiKey?: string, websiteId?: string) {\n return async (url: URL | RequestInfo, options?: RequestInit): Promise<Response> => {\n let finalUrl = url;\n\n const urlObj = new URL(url.toString());\n if (apiKey) {\n urlObj.searchParams.set('api_key', apiKey);\n }\n if (websiteId) {\n urlObj.searchParams.set('website_id', websiteId);\n }\n if (apiKey || websiteId) {\n finalUrl = urlObj.toString();\n }\n\n const response = await fetch(finalUrl, options);\n\n return response;\n };\n}\n\n/**\n * Create a tRPC client for the CMS API.\n */\nfunction createCmsClient(options: CmsConfig): CmsClient {\n const url = getCmsApiUrl(options.cmsUrl);\n\n return createTRPCClient<AppRouter>({\n links: [\n httpBatchLink({\n url,\n transformer: superjson,\n fetch: createFetchWithApiKey(options.apiKey, options.websiteId),\n }),\n ],\n });\n}\n\n/**\n * Get a CMS client for the specified CMS URL.\n */\nexport function getCmsClient(options: CmsConfig): CmsClient {\n return createCmsClient(options);\n}\n"],"mappings":"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","names":["z","z","z","z","z","z","z","z","z","z","jsx"]}
|
|
1
|
+
{"version":3,"sources":["../../../../packages/cms-schema/src/blocks/article.ts","../../../../packages/cms-schema/src/validation/image.ts","../../../../packages/cms-schema/src/blocks/schemas/article-block.ts","../../../../packages/cms-schema/src/blocks/schemas/cta-block.ts","../../../../packages/cms-schema/src/blocks/schemas/features-block.ts","../../../../packages/cms-schema/src/blocks/schemas/hero-block.ts","../../../../packages/cms-schema/src/fields/complex/media.ts","../../../../packages/cms-schema/src/blocks/schemas/logo-trust-block.ts","../../../../packages/cms-schema/src/blocks/registry.ts","../../lib/renderer.tsx","../../lib/block-renderer.tsx","../../lib/cms-api.ts"],"sourcesContent":["/**\n * Article block normalization helpers shared across CMS and website apps.\n */\n\nexport interface ArticleContent {\n headline: string;\n author?: string;\n publishedAt?: string;\n body: string;\n tags?: readonly string[];\n status?: string;\n}\n\nexport interface ArticleRow {\n id: string;\n published_content: unknown;\n}\n\nexport interface NormalizedArticleRow extends ArticleContent {\n id: string;\n tags: string[];\n status: string;\n}\n\n/**\n * Normalize article content coming from Supabase blocks.\n * Returns null when required fields are missing or invalid.\n */\nexport function normalizeArticleContent(payload: unknown): ArticleContent | null {\n if (!payload || typeof payload !== 'object') {\n return null;\n }\n\n const record = payload as Record<string, unknown>;\n const headline = typeof record.headline === 'string' ? record.headline : null;\n const body = typeof record.body === 'string' ? record.body : null;\n\n if (!headline || !body) {\n return null;\n }\n\n const author = typeof record.author === 'string' ? record.author : undefined;\n const publishedAt = typeof record.publishedAt === 'string' ? record.publishedAt : undefined;\n const tags = Array.isArray(record.tags) ? record.tags.map((tag) => String(tag)) : undefined;\n const statusRaw = typeof record.status === 'string' ? record.status.trim() : undefined;\n\n return {\n headline,\n body,\n author,\n publishedAt,\n tags,\n status: statusRaw || undefined,\n };\n}\n\n/**\n * Normalize an article block row, applying defaults for tags/status.\n */\nexport function normalizeArticleRow(row: ArticleRow): NormalizedArticleRow | null {\n const article = normalizeArticleContent(row.published_content);\n if (!article) {\n return null;\n }\n\n return {\n ...article,\n id: row.id,\n tags: article.tags ? [...article.tags] : [],\n status: article.status ?? 'published',\n };\n}\n\n/**\n * Ensure the article is published.\n */\nexport function isArticlePublished(article: ArticleContent): boolean {\n const now = new Date();\n const publishedAt = article.publishedAt ? new Date(article.publishedAt) : null;\n\n return (\n article.status === 'published' &&\n publishedAt !== null &&\n !Number.isNaN(publishedAt.getTime()) &&\n publishedAt <= now\n );\n}\n","/**\n * Image Validation Utilities\n *\n * Provides validation for:\n * - File type (MIME type checking)\n * - File size (configurable limits)\n * - Image dimensions (width/height constraints)\n */\n\nimport { z } from 'zod';\n\n// =============================================================================\n// Constants\n// =============================================================================\n\nexport const ALLOWED_MIME_TYPES = ['image/jpeg', 'image/png', 'image/webp'] as const;\nexport type ImageMimeType = (typeof ALLOWED_MIME_TYPES)[number];\n\nexport const MAX_FILE_SIZE = 10 * 1024 * 1024; // 10MB\nexport const MIN_FILE_SIZE = 1024; // 1KB (avoid empty/corrupt files)\n\nexport const MAX_DIMENSION = 8192; // 8K resolution\nexport const MIN_DIMENSION = 10; // Minimum useful size\n\n// =============================================================================\n// Zod Schemas\n// =============================================================================\n\nexport const MimeTypeSchema = z.enum(ALLOWED_MIME_TYPES);\n\nexport const FileSizeSchema = z\n .number()\n .int()\n .min(MIN_FILE_SIZE, `File too small. Minimum: ${MIN_FILE_SIZE} bytes`)\n .max(MAX_FILE_SIZE, `File too large. Maximum: ${MAX_FILE_SIZE / 1024 / 1024}MB`);\n\nexport const DimensionSchema = z\n .number()\n .int()\n .min(MIN_DIMENSION, `Dimension too small. Minimum: ${MIN_DIMENSION}px`)\n .max(MAX_DIMENSION, `Dimension too large. Maximum: ${MAX_DIMENSION}px`);\n\n/**\n * Schema for upload request validation.\n */\nexport const UploadRequestSchema = z.object({\n filename: z\n .string()\n .min(1, 'Filename is required')\n .max(255, 'Filename too long')\n .regex(/^[^<>:\"/\\\\|?*]+$/, 'Filename contains invalid characters'),\n mimeType: MimeTypeSchema,\n fileSize: FileSizeSchema,\n});\n\nexport type UploadRequest = z.infer<typeof UploadRequestSchema>;\n\n/**\n * Schema for confirming upload with dimensions.\n * assetId is optional - if not provided, the server will generate a UUID.\n */\nexport const ConfirmUploadSchema = z.object({\n assetId: z.uuid().optional(),\n width: DimensionSchema,\n height: DimensionSchema,\n});\n\nexport type ConfirmUpload = z.infer<typeof ConfirmUploadSchema>;\n\n// =============================================================================\n// Validation Functions\n// =============================================================================\n\nexport interface ValidationResult {\n valid: boolean;\n error?: string;\n}\n\n/**\n * Validate file type by MIME type.\n */\nexport function validateMimeType(mimeType: string): ValidationResult {\n const result = MimeTypeSchema.safeParse(mimeType);\n if (result.success) {\n return { valid: true };\n }\n return {\n valid: false,\n error: `File type not supported. Use JPEG, PNG, or WebP.`,\n };\n}\n\n/**\n * Validate file size.\n */\nexport function validateFileSize(size: number): ValidationResult {\n if (size < MIN_FILE_SIZE) {\n return {\n valid: false,\n error: `File appears to be empty or corrupt.`,\n };\n }\n if (size > MAX_FILE_SIZE) {\n const sizeMB = (size / 1024 / 1024).toFixed(1);\n return {\n valid: false,\n error: `File too large (${sizeMB}MB). Maximum: 10 MB.`,\n };\n }\n return { valid: true };\n}\n\n/**\n * Validate image dimensions.\n */\nexport function validateDimensions(width: number, height: number): ValidationResult {\n if (width < MIN_DIMENSION || height < MIN_DIMENSION) {\n return {\n valid: false,\n error: `Image too small. Minimum: ${MIN_DIMENSION}x${MIN_DIMENSION}px.`,\n };\n }\n if (width > MAX_DIMENSION || height > MAX_DIMENSION) {\n return {\n valid: false,\n error: `Image too large. Maximum: ${MAX_DIMENSION}x${MAX_DIMENSION}px.`,\n };\n }\n return { valid: true };\n}\n\n/**\n * Validate complete upload request.\n */\nexport function validateUploadRequest(\n request: unknown\n): { valid: true; data: UploadRequest } | { valid: false; error: string } {\n const result = UploadRequestSchema.safeParse(request);\n if (result.success) {\n return { valid: true, data: result.data };\n }\n const firstIssue = result.error.issues[0];\n return {\n valid: false,\n error: firstIssue?.message ?? 'Invalid upload request',\n };\n}\n\n// =============================================================================\n// File Extension Utilities\n// =============================================================================\n\nconst MIME_TO_EXT: Record<ImageMimeType, string> = {\n 'image/jpeg': 'jpg',\n 'image/png': 'png',\n 'image/webp': 'webp',\n};\n\nconst EXT_TO_MIME: Record<string, ImageMimeType> = {\n jpg: 'image/jpeg',\n jpeg: 'image/jpeg',\n png: 'image/png',\n webp: 'image/webp',\n};\n\n/**\n * Get file extension from MIME type.\n */\nexport function getExtensionFromMime(mimeType: ImageMimeType | string): string {\n return MIME_TO_EXT[mimeType as ImageMimeType] ?? 'bin';\n}\n\n/**\n * Get MIME type from file extension.\n */\nexport function getMimeFromExtension(ext: string): ImageMimeType | undefined {\n return EXT_TO_MIME[ext.toLowerCase()];\n}\n\n/**\n * Extract extension from filename.\n */\nexport function getExtensionFromFilename(filename: string): string {\n const parts = filename.split('.');\n return parts.pop()?.toLowerCase?.() ?? '';\n}\n\n/**\n * Sanitize filename for storage.\n * Removes special characters, preserves extension.\n */\nexport function sanitizeFilename(filename: string): string {\n // Get extension\n const ext = getExtensionFromFilename(filename);\n\n // Get base name without extension\n const base = filename.slice(0, filename.length - ext.length - 1);\n\n // Sanitize: lowercase, replace spaces and special chars\n const sanitized = base\n .toLowerCase()\n .replace(/[^a-z0-9]/g, '-')\n .replace(/-+/g, '-')\n .replace(/^-|-$/g, '')\n .slice(0, 50); // Limit length\n\n return ext ? `${sanitized}.${ext}` : sanitized;\n}\n","/**\n * ArticleBlock Schema.\n *\n * Defines the structure and validation for ArticleBlock content.\n *\n * ArticleBlock represents a block of content focused on articles,\n * including fields such as headline, author, publication date, body content, tags, and status.\n *\n * Includes:\n * - Zod schema for validation\n * - TypeScript type inference\n * - Default values\n * - Validation function\n * - Function to create default content\n */\nimport { z } from 'zod';\n\n// Article block content schema with validation rules\nexport const ArticleBlockContentSchema = z.object({\n headline: z.string().min(1, 'Article headline is required').max(300, 'Headline too long').trim(),\n author: z.string().max(100, 'Article author too long').trim().optional(),\n publishedAt: z.iso\n .datetime({ message: 'Article publishedAt must be a valid ISO 8601 datetime string.' })\n .optional(),\n body: z.string().min(1, 'Article body content is required'),\n tags: z.array(z.string()).optional(),\n status: z.enum(['draft', 'review', 'published']),\n});\n\n// Inferred type for components\nexport type ArticleBlockContent = z.infer<typeof ArticleBlockContentSchema>;\n\n// Schema name constant\nexport const ARTICLE_BLOCK_SCHEMA_NAME = 'article';\n\n// Default values for a new ArticleBlock\nexport const ArticleBlockDefaults: Partial<ArticleBlockContent> = {\n status: 'draft',\n};\n\n// Validate ArticleBlock content\nexport function validateArticleBlockContent(content: unknown): {\n valid: boolean;\n data?: ArticleBlockContent;\n errors?: z.ZodError;\n} {\n const result = ArticleBlockContentSchema.safeParse(content);\n if (result.success) {\n return { valid: true, data: result.data };\n }\n return { valid: false, errors: result.error };\n}\n\n// Create default ArticleBlock content\nexport function createDefaultArticleContent(): ArticleBlockContent {\n return {\n headline: 'untitled article',\n author: '',\n publishedAt: new Date().toISOString(),\n body: '',\n tags: [],\n status: 'draft',\n };\n}\n","/**\n * CTABlock Schema.\n *\n * A call-to-action section with headline, description, and action buttons.\n * Supports primary and optional secondary CTA buttons.\n */\n\nimport { z } from 'zod';\n\n// CTA button schema\n// URL can be either a full URL (http://...) or a relative path (/path)\nconst CTAButtonSchema = z.object({\n text: z.string().min(1, 'Button text is required').max(50, 'Button text too long'),\n url: z.string().refine(\n (val) => {\n // Accept full URLs\n if (val.startsWith('http://') || val.startsWith('https://')) {\n try {\n new URL(val);\n return true;\n } catch {\n return false;\n }\n }\n // Accept relative paths starting with /\n if (val.startsWith('/')) {\n return true;\n }\n // Accept hash/anchor links\n if (val.startsWith('#')) {\n return true;\n }\n return false;\n },\n {\n message:\n 'URL must be a valid full URL (http://... or https://...), relative path (/path), or anchor (#anchor)',\n }\n ),\n});\n\nexport type CTAButton = z.infer<typeof CTAButtonSchema>;\n\n// CTABlock content schema with validation rules\nexport const CTABlockContentSchema = z.object({\n headline: z.string().min(1, 'Headline is required').max(100, 'Headline too long'),\n description: z.string().max(500, 'Description too long').optional(),\n primaryButton: CTAButtonSchema,\n secondaryButton: CTAButtonSchema.optional(),\n});\n\n// Inferred type for components\nexport type CTABlockContent = z.infer<typeof CTABlockContentSchema>;\n\n// Schema name constant\nexport const CTA_BLOCK_SCHEMA_NAME = 'cta-block';\n\n// Default values for a new CTABlock\nexport const CTABlockDefaults: Partial<CTABlockContent> = {\n primaryButton: {\n text: 'Get Started',\n url: '#',\n },\n};\n\n// Validate CTABlock content\nexport function validateCTABlockContent(content: unknown): {\n valid: boolean;\n data?: CTABlockContent;\n errors?: z.ZodError;\n} {\n const result = CTABlockContentSchema.safeParse(content);\n if (result.success) {\n return { valid: true, data: result.data };\n }\n return { valid: false, errors: result.error };\n}\n\n// Create default CTABlock content\nexport function createDefaultCTAContent(): CTABlockContent {\n return {\n headline: '',\n description: undefined,\n primaryButton: {\n text: 'Get Started',\n url: '#',\n },\n secondaryButton: undefined,\n };\n}\n","/**\n * FeaturesBlock Schema.\n *\n * A section displaying a list of features with icons, titles, and descriptions.\n * Supports different layouts (grid, list, carousel).\n */\n\nimport { z } from 'zod';\n\n// Layout options for features display\nexport const FeaturesLayout = ['grid', 'list', 'carousel'] as const;\nexport type FeaturesLayoutType = (typeof FeaturesLayout)[number];\n\n// Single feature item schema\nexport const FeatureItemSchema = z.object({\n icon: z.string().max(50, 'Icon name too long').optional(),\n title: z.string().min(1, 'Title is required').max(100, 'Title too long'),\n description: z.string().max(500, 'Description too long').optional(),\n});\n\nexport type FeatureItem = z.infer<typeof FeatureItemSchema>;\n\n// FeaturesBlock content schema with array validation\nexport const FeaturesBlockContentSchema = z.object({\n title: z.string().min(1, 'Section title is required').max(100, 'Title too long'),\n subtitle: z.string().max(200, 'Subtitle too long').optional(),\n features: z\n .array(FeatureItemSchema)\n .min(1, 'At least one feature is required')\n .max(6, 'Maximum 6 features allowed'),\n layout: z.enum(FeaturesLayout).default('grid'),\n});\n\n// Inferred type for components\nexport type FeaturesBlockContent = z.infer<typeof FeaturesBlockContentSchema>;\n\n// Schema name constant\nexport const FEATURES_BLOCK_SCHEMA_NAME = 'features-block';\n\n// Default values for a new FeaturesBlock\nexport const FeaturesBlockDefaults: Partial<FeaturesBlockContent> = {\n layout: 'grid',\n features: [\n {\n icon: 'star',\n title: 'Feature Title',\n description: 'Describe this feature',\n },\n ],\n};\n\n// Validate FeaturesBlock content\nexport function validateFeaturesBlockContent(content: unknown): {\n valid: boolean;\n data?: FeaturesBlockContent;\n errors?: z.ZodError;\n} {\n const result = FeaturesBlockContentSchema.safeParse(content);\n if (result.success) {\n return { valid: true, data: result.data };\n }\n return { valid: false, errors: result.error };\n}\n\n// Create default FeaturesBlock content\nexport function createDefaultFeaturesContent(): FeaturesBlockContent {\n return {\n title: '',\n subtitle: undefined,\n features: [],\n layout: 'grid',\n };\n}\n\n// Add a feature to the content\nexport function addFeature(\n content: FeaturesBlockContent,\n feature: FeatureItem\n): FeaturesBlockContent {\n if (content.features.length >= 6) {\n throw new Error('Maximum 6 features allowed');\n }\n return {\n ...content,\n features: [...content.features, feature],\n };\n}\n\n// Remove a feature by index\nexport function removeFeature(content: FeaturesBlockContent, index: number): FeaturesBlockContent {\n if (index < 0 || index >= content.features.length) {\n throw new Error('Invalid feature index');\n }\n if (content.features.length <= 1) {\n throw new Error('At least one feature is required');\n }\n return {\n ...content,\n features: content.features.filter((_, i) => i !== index),\n };\n}\n\n// Update a feature by index\nexport function updateFeature(\n content: FeaturesBlockContent,\n index: number,\n updates: Partial<FeatureItem>\n): FeaturesBlockContent {\n if (index < 0 || index >= content.features.length) {\n throw new Error('Invalid feature index');\n }\n return {\n ...content,\n features: content.features.map((feature, i) =>\n i === index ? { ...feature, ...updates } : feature\n ),\n };\n}\n","/**\n * HeroBlock Schema.\n *\n * A full-width hero section typically used at the top of pages.\n * Supports headline, subheadline, CTA button, background image, and alignment.\n */\n\nimport { z } from 'zod';\nimport { ImageReferenceSchema } from '../../fields/complex/media';\n\n// Alignment options for the hero content\nexport const HeroAlignment = ['left', 'center', 'right'] as const;\nexport type HeroAlignmentType = (typeof HeroAlignment)[number];\n\n// HeroBlock content schema with validation rules\nexport const HeroBlockContentSchema = z.object({\n headline: z.string().min(1, 'Headline is required').max(100, 'Headline too long'),\n subheadline: z.string().max(200, 'Subheadline too long').optional(),\n ctaText: z.string().max(50, 'CTA text too long').optional(),\n ctaUrl: z\n .string()\n .refine(\n (val) => {\n // Empty string is valid\n if (val === '') return true;\n // Accept full URLs\n if (val.startsWith('http://') || val.startsWith('https://')) {\n try {\n new URL(val);\n return true;\n } catch {\n return false;\n }\n }\n // Accept relative paths starting with /\n if (val.startsWith('/')) {\n return true;\n }\n // Accept hash/anchor links\n if (val.startsWith('#')) {\n return true;\n }\n return false;\n },\n {\n message:\n 'URL must be a valid full URL (http://... or https://...), relative path (/path), anchor (#anchor), or empty string',\n }\n )\n .optional()\n .or(z.literal('')),\n backgroundImage: ImageReferenceSchema.nullable().optional(),\n alignment: z.enum(HeroAlignment).default('center'),\n});\n\n// Inferred type for components\nexport type HeroBlockContent = z.infer<typeof HeroBlockContentSchema>;\n\n// Schema name constant\nexport const HERO_BLOCK_SCHEMA_NAME = 'hero-block';\n\n// Default values for a new HeroBlock\nexport const HeroBlockDefaults: Partial<HeroBlockContent> = {\n alignment: 'center',\n};\n\n// Validate HeroBlock content\nexport function validateHeroBlockContent(content: unknown): {\n valid: boolean;\n data?: HeroBlockContent;\n errors?: z.ZodError;\n} {\n const result = HeroBlockContentSchema.safeParse(content);\n if (result.success) {\n return { valid: true, data: result.data };\n }\n return { valid: false, errors: result.error };\n}\n\n// Create default HeroBlock content\nexport function createDefaultHeroContent(): HeroBlockContent {\n return {\n headline: '',\n subheadline: undefined,\n ctaText: undefined,\n ctaUrl: undefined,\n backgroundImage: undefined,\n alignment: 'center',\n };\n}\n","/**\n * Image and file field factories.\n *\n * The image() factory creates an ImageReference schema that:\n * - References an ImageAsset by ID\n * - Stores context-specific alt text (required for accessibility)\n * - Supports optional hotspot (focal point) and crop settings\n */\n\nimport { z } from 'zod';\n\nimport type { FieldMeta } from '../../types';\nimport {\n DimensionSchema,\n FileSizeSchema,\n MAX_DIMENSION,\n MAX_FILE_SIZE,\n MIN_DIMENSION,\n MimeTypeSchema,\n} from '../../validation/image';\n\n// =============================================================================\n// ImageAsset Schema (Public API type)\n// =============================================================================\n\n/**\n * Public API schema for image assets.\n *\n * This is the camelCase API representation. The database uses snake_case\n * (ImageAssetRow in db/image-asset-types.ts). Conversion between formats\n * should happen at the API boundary.\n *\n * Reuses validation schemas from validation/image.ts to ensure consistency.\n */\nexport const ImageAssetSchema = z.object({\n /** UUID primary key */\n id: z.uuid(),\n /** R2/S3 storage URL for the original file */\n url: z.url(),\n /** Image width in pixels */\n width: DimensionSchema,\n /** Image height in pixels */\n height: DimensionSchema,\n /** Original filename from upload */\n originalFilename: z.string().min(1).max(255),\n /** MIME type (only web-safe formats allowed) */\n mimeType: MimeTypeSchema,\n /** File size in bytes */\n fileSize: FileSizeSchema,\n /** Base64-encoded tiny preview for blur-up loading */\n lqip: z.string().optional(),\n});\n\nexport type ImageAsset = z.infer<typeof ImageAssetSchema>;\n\n// =============================================================================\n// ImageReference Schema (Asset-reference model)\n// =============================================================================\n\n/**\n * Hotspot defines the focal point of an image.\n * Coordinates are fractions (0-1) from top-left.\n */\nexport const HotspotSchema = z.object({\n x: z.number().min(0).max(1),\n y: z.number().min(0).max(1),\n});\n\nexport type Hotspot = z.infer<typeof HotspotSchema>;\n\n/**\n * Crop defines the region to extract from the image.\n * - x, y: Top-left coordinate of the crop region in pixels\n * - width, height: Size of the crop region in pixels\n * All values are positive integers representing pixel coordinates/dimensions.\n */\nexport const CropSchema = z.object({\n /** X coordinate of top-left corner in pixels */\n x: z.number().int().nonnegative(),\n /** Y coordinate of top-left corner in pixels */\n y: z.number().int().nonnegative(),\n /** Width of crop region in pixels (must be > 0) */\n width: z.number().int().positive(),\n /** Height of crop region in pixels (must be > 0) */\n height: z.number().int().positive(),\n});\n\nexport type Crop = z.infer<typeof CropSchema>;\n\n/**\n * ImageReference is what blocks store.\n * It points to an ImageAsset and adds context-specific metadata.\n */\nexport const ImageReferenceSchema = z.object({\n // Alt text is REQUIRED for accessibility\n alt: z.string().min(1, 'Alt text is required for accessibility'),\n\n // Optional metadata\n caption: z.string().max(500).optional(),\n attribution: z.string().max(255).optional(),\n\n // Reference to the ImageAsset with stored transformation\n _asset: z.object({\n id: z.uuid(),\n transformation: z.string().nullable().optional(),\n }),\n});\n\nexport type ImageReference = z.infer<typeof ImageReferenceSchema>;\n\n// =============================================================================\n// Image Field Factory\n// =============================================================================\n\nexport interface ImageFieldOptions {\n label: string;\n description?: string;\n required?: boolean;\n accept?: string;\n maxSize?: number;\n minWidth?: number;\n minHeight?: number;\n maxWidth?: number;\n maxHeight?: number;\n aspectRatio?: number;\n}\n\n/**\n * Create an image field with metadata.\n *\n * @example\n * const featuredImage = image({\n * label: 'Featured Image',\n * required: true,\n * accept: 'image/jpeg, image/png',\n * maxSize: 5 * 1024 * 1024,\n * });\n *\n * // Type of the value:\n * // {\n * // alt: string;\n * // caption?: string;\n * // attribution?: string;\n * // _asset: { id: string; transformation?: string | null };\n * // }\n */\nexport function image(options: ImageFieldOptions) {\n const meta: FieldMeta = {\n label: options.label,\n component: 'image-uploader',\n required: options.required,\n description: options.description,\n options: {\n accept: options.accept ?? 'image/jpeg, image/png, image/webp',\n maxSize: options.maxSize ?? MAX_FILE_SIZE, // 10MB default\n minWidth: options.minWidth ?? MIN_DIMENSION,\n minHeight: options.minHeight ?? MIN_DIMENSION,\n maxWidth: options.maxWidth ?? MAX_DIMENSION,\n maxHeight: options.maxHeight ?? MAX_DIMENSION,\n aspectRatio: options.aspectRatio,\n },\n };\n\n // Use the new ImageReferenceSchema instead of URL-only\n const result = options.required ? ImageReferenceSchema : ImageReferenceSchema.optional();\n\n return result.meta(meta);\n}\n\n// =============================================================================\n// File Field\n// =============================================================================\n\nexport interface FileFieldOptions {\n label: string;\n description?: string;\n required?: boolean;\n accept?: string;\n maxSize?: number;\n}\n\nconst fileSchema = z.object({\n url: z.url(),\n name: z.string(),\n size: z.number().int().positive(),\n type: z.string(),\n});\n\n/**\n * Create a file field with metadata.\n */\nexport function file(options: FileFieldOptions) {\n const meta: FieldMeta = {\n label: options.label,\n component: 'file-uploader',\n required: options.required,\n description: options.description,\n options: {\n accept: options.accept ?? '*/*',\n maxSize: options.maxSize,\n },\n };\n\n const result = options.required ? fileSchema : fileSchema.optional();\n return result.meta(meta);\n}\n","/**\n * LogoTrustBlock Schema.\n *\n * A section displaying customer/partner logos for trust and credibility.\n * Supports an optional title and an array of logo images.\n *\n * Each logo uses ImageReference to integrate with the image manager.\n */\n\nimport { z } from 'zod';\n\nimport { ImageReferenceSchema } from '../../fields/complex/media';\n\n// Logo item schema - uses ImageReference for image manager integration\nexport const LogoItemSchema = z.object({\n /** Unique ID for this logo item */\n id: z.uuid(),\n /** Image reference (alt + _asset with transformation) */\n image: ImageReferenceSchema,\n /** Optional company/brand name to display */\n name: z.string().max(100, 'Name too long').optional(),\n});\n\nexport type LogoItem = z.infer<typeof LogoItemSchema>;\n\n/**\n * Legacy logo format schema for backwards compatibility.\n * Used for logos stored with direct URL before the ImageReference migration.\n */\nexport const LegacyLogoItemSchema = z.object({\n /** Direct URL to the logo image */\n url: z.string(),\n /** Alt text for the image */\n alt: z.string(),\n /** Optional company/brand name */\n name: z.string().optional(),\n});\n\nexport type LegacyLogoItem = z.infer<typeof LegacyLogoItemSchema>;\n\n// LogoTrustBlock content schema with validation rules\nexport const LogoTrustBlockContentSchema = z.object({\n title: z.string().max(100, 'Title too long').optional(),\n logos: z.array(LogoItemSchema).max(20, 'Maximum 20 logos allowed'),\n});\n\n// Inferred type for components\nexport type LogoTrustBlockContent = z.infer<typeof LogoTrustBlockContentSchema>;\n\n// Schema name constant\nexport const LOGO_TRUST_BLOCK_SCHEMA_NAME = 'logo-trust-block';\n\n// Default values for a new LogoTrustBlock\nexport const LogoTrustBlockDefaults: Partial<LogoTrustBlockContent> = {\n logos: [],\n};\n\n// Validate LogoTrustBlock content\nexport function validateLogoTrustBlockContent(content: unknown): {\n valid: boolean;\n data?: LogoTrustBlockContent;\n errors?: z.ZodError;\n} {\n const result = LogoTrustBlockContentSchema.safeParse(content);\n if (result.success) {\n return { valid: true, data: result.data };\n }\n return { valid: false, errors: result.error };\n}\n\n// Create default LogoTrustBlock content\nexport function createDefaultLogoTrustContent(): LogoTrustBlockContent {\n return {\n title: undefined,\n logos: [],\n };\n}\n","/**\n * Block Schema Registry.\n *\n * Centralizes registration for all block schemas.\n * Call registerAllBlockSchemas() during application initialization.\n */\n\nimport { clearSchemaRegistry, registerBlockSchema } from '../validation';\nimport { ARTICLE_BLOCK_SCHEMA_NAME, ArticleBlockContentSchema } from './schemas/article-block';\nimport { CTA_BLOCK_SCHEMA_NAME, CTABlockContentSchema } from './schemas/cta-block';\nimport { FEATURES_BLOCK_SCHEMA_NAME, FeaturesBlockContentSchema } from './schemas/features-block';\nimport { HERO_BLOCK_SCHEMA_NAME, HeroBlockContentSchema } from './schemas/hero-block';\nimport {\n LOGO_TRUST_BLOCK_SCHEMA_NAME,\n LogoTrustBlockContentSchema,\n} from './schemas/logo-trust-block';\n\n// All registered block schema names\nexport const BLOCK_SCHEMA_NAMES = [\n ARTICLE_BLOCK_SCHEMA_NAME,\n HERO_BLOCK_SCHEMA_NAME,\n FEATURES_BLOCK_SCHEMA_NAME,\n CTA_BLOCK_SCHEMA_NAME,\n LOGO_TRUST_BLOCK_SCHEMA_NAME,\n] as const;\n\n// Union type for type-safe schema name usage\nexport type BlockSchemaName = (typeof BLOCK_SCHEMA_NAMES)[number];\n\n// Register all block schemas\nexport function registerAllBlockSchemas(): void {\n registerBlockSchema(ARTICLE_BLOCK_SCHEMA_NAME, ArticleBlockContentSchema);\n registerBlockSchema(HERO_BLOCK_SCHEMA_NAME, HeroBlockContentSchema);\n registerBlockSchema(FEATURES_BLOCK_SCHEMA_NAME, FeaturesBlockContentSchema);\n registerBlockSchema(CTA_BLOCK_SCHEMA_NAME, CTABlockContentSchema);\n registerBlockSchema(LOGO_TRUST_BLOCK_SCHEMA_NAME, LogoTrustBlockContentSchema);\n}\n\n// Clear and re-register all block schemas (for testing)\nexport function resetBlockSchemas(): void {\n clearSchemaRegistry();\n registerAllBlockSchemas();\n}\n\n// Type guard for schema names\nexport function isValidBlockSchemaName(name: string): name is BlockSchemaName {\n return BLOCK_SCHEMA_NAMES.includes(name as BlockSchemaName);\n}\n","/**\n * Catch-all Route Handler for Parametric Routes\n *\n * Handles routes with multiple segments like /us/en/products.\n * Uses the CMS API to fetch and render blocks.\n */\n\nimport {\n isArticlePublished,\n isValidBlockSchemaName,\n normalizeArticleContent,\n} from '@repo/cms-schema/blocks';\nimport type { GeneratedBlockRow } from '@repo/cms-schema/db';\nimport type { Metadata } from 'next';\nimport { notFound } from 'next/navigation';\nimport { BlockRenderer } from './block-renderer';\nimport { type CmsConfig, getCmsClient } from './cms-api';\nimport type { BlockComponentRegistry, BlockData, ResolvedRouteParams } from './types';\n\ntype PageProps = CmsConfig & {\n params: Promise<{ slug: string[] }>;\n searchParams?: Promise<{ [key: string]: string | string[] | undefined }>;\n registry?: Partial<BlockComponentRegistry>;\n};\n\nfunction getWebsiteId(providedWebsiteId?: string): string {\n const websiteId =\n providedWebsiteId ??\n process.env.NEXT_PUBLIC_WEBSITE_ID ??\n process.env.WEBSITE_ID ??\n process.env.CMS_WEBSITE_ID;\n\n if (!websiteId) {\n throw new Error(\n 'Missing websiteId for website renderer. Either pass websiteId prop or set NEXT_PUBLIC_WEBSITE_ID (or WEBSITE_ID/CMS_WEBSITE_ID) to a valid UUID.'\n );\n }\n\n const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n if (!uuidRegex.test(websiteId)) {\n throw new Error(\n `Invalid websiteId \"${websiteId}\". Provide a valid UUID via prop or set NEXT_PUBLIC_WEBSITE_ID (or WEBSITE_ID/CMS_WEBSITE_ID).`\n );\n }\n\n return websiteId;\n}\n\n/**\n * Catch-all route handler for parametric routes.\n *\n * Handles paths like:\n * - /us/en/products -> slug = ['us', 'en', 'products']\n * - /about -> slug = ['about']\n *\n * Reconstructs the full path and fetches route via tRPC.\n */\nexport default async function ParametricRoutePage({\n params,\n searchParams,\n registry,\n apiKey,\n cmsUrl,\n websiteId: providedWebsiteId,\n}: PageProps) {\n const websiteId = getWebsiteId(providedWebsiteId);\n const { slug } = await params;\n const resolvedSearchParams = await searchParams;\n\n // Check for ai_preview query param to determine if we should show generated blocks\n let aiPreviewIndex: number | null = null;\n const aiPreviewParam = resolvedSearchParams?.ai_preview;\n if (aiPreviewParam) {\n const paramValue = Array.isArray(aiPreviewParam) ? aiPreviewParam[0] : aiPreviewParam;\n if (paramValue) {\n const parsed = parseInt(paramValue, 10);\n if (!Number.isNaN(parsed)) {\n aiPreviewIndex = parsed;\n }\n }\n }\n\n // Check for edit_mode query param to enable editable wrappers\n const editModeParam = resolvedSearchParams?.edit_mode;\n const editMode = editModeParam === 'true' || editModeParam === '1';\n\n // Reconstruct full path from slug segments and normalize it\n const rawPath = `/${slug.join('/')}`;\n const path = normalizePath(rawPath);\n\n // Get CMS API client with optional API key and custom URL\n const client = getCmsClient({ apiKey, cmsUrl });\n\n try {\n // Fetch route by path via CMS API\n const { route, resolvedParams } = await client.route.getByPath.query({ websiteId, path });\n\n // Only show Live routes on public website\n if (route.state !== 'Live') {\n console.error(`Route found but not Live. Path: ${path}, State: ${route.state}`);\n notFound();\n }\n\n // Fetch all blocks by ID via CMS API (skip any that fail)\n const blockPromises = route.block_ids.map(async (blockId) => {\n try {\n const result = await client.block.getById.query({ websiteId, id: blockId });\n return result.block;\n } catch (error) {\n // Log error but don't fail the entire page\n console.error(`Failed to fetch block ${blockId}:`, error);\n return null;\n }\n });\n\n // Fetch generated blocks for preview only if ai_preview param is set\n // generated_content is an array of variants: [{ description, content }, ...]\n type GeneratedBlockData = Pick<\n GeneratedBlockRow,\n 'id' | 'generated_content' | 'generated_layout' | 'schema_name' | 'status'\n >;\n const generatedBlocksPromise =\n aiPreviewIndex !== null\n ? client.block.getGeneratedByBlockIds\n .query({ websiteId, blockIds: route.block_ids })\n .catch((error) => {\n console.error('Failed to fetch generated blocks:', error);\n return { generatedBlocks: {} as Record<string, GeneratedBlockData> };\n })\n : Promise.resolve({ generatedBlocks: {} as Record<string, GeneratedBlockData> });\n\n const [blockResults, { generatedBlocks }] = await Promise.all([\n Promise.all(blockPromises),\n generatedBlocksPromise,\n ]);\n\n // Transform blocks to BlockData format for BlockRenderer\n // Filter out any blocks that failed to load\n // Use generated content only when ai_preview param is set\n const blocks: BlockData[] = [];\n\n for (const block of blockResults) {\n if (!block || block.published_content === null) continue;\n\n // Use generated content only if ai_preview is set and generated block exists\n // ai_preview is 1-indexed, so subtract 1 to get array index\n let content: Record<string, unknown> | null = null;\n if (aiPreviewIndex !== null) {\n const generatedBlock = generatedBlocks[block.id] as GeneratedBlockData | undefined;\n const variantIndex = aiPreviewIndex - 1; // Convert to 0-indexed\n const variants = generatedBlock?.generated_content;\n if (variants && Array.isArray(variants) && variants[variantIndex]) {\n content = variants[variantIndex].content;\n }\n }\n // Fall back to published content if no variant found\n content = content ?? (block.published_content as Record<string, unknown> | null);\n if (!content) continue;\n\n // Handle 'article' blocks separately before checking schema type\n if (block.schema_name === 'article') {\n const article = normalizeArticleContent(content);\n const isPublished = article ? isArticlePublished(article) : null;\n if (article && isPublished) {\n blocks.push({ id: block.id, type: 'article', content: article });\n }\n continue;\n }\n\n // Custom blocks have a schema_id (UUID); use schema_name as the type\n // so consumers register components by schema_name in the registry\n if (block.schema_id) {\n blocks.push({ id: block.id, type: block.schema_name, content });\n continue;\n }\n\n // Skip predefined blocks with invalid schema names\n if (!isValidBlockSchemaName(block.schema_name)) {\n continue;\n }\n\n // For all predefined block types, map schema_name to type and include content\n // Image references are automatically resolved by block.getById\n blocks.push({\n id: block.id,\n type: block.schema_name,\n content,\n } as BlockData);\n }\n\n // Map resolved params to the slimmed-down shape for block components\n const routeParams: ResolvedRouteParams | undefined = resolvedParams\n ? Object.fromEntries(\n Object.entries(resolvedParams).map(([key, param]) => [\n key,\n {\n value: param.value,\n schemaName: param.schemaName,\n document: {\n id: param.document.id,\n title: param.document.title,\n content: param.document.content,\n schema_name: param.document.schema_name,\n },\n },\n ])\n )\n : undefined;\n\n return (\n <main>\n {blocks.map((block) => (\n <BlockRenderer\n key={block.id}\n block={block}\n registry={registry ?? {}}\n disableEditable={!editMode}\n routeParams={routeParams}\n />\n ))}\n </main>\n );\n } catch (error) {\n // Log error for debugging\n console.error(`Route fetch error for path: ${path}`, error);\n\n // If route not found or param validation fails, show 404\n // TRPCClientError has data.code for the error code\n const errorCode =\n error instanceof Error && 'data' in error\n ? (error as { data?: { code?: string } }).data?.code\n : error instanceof Error && 'code' in error\n ? (error as { code: string }).code\n : undefined;\n\n if (errorCode === 'NOT_FOUND' || errorCode === 'P0002') {\n notFound();\n }\n\n // Re-throw other errors\n throw error;\n }\n}\n\n// -----------------------------------------------------------------------------\n// Metadata\n// -----------------------------------------------------------------------------\n\n/**\n * Generate metadata for the page.\n * Uses Next.js 15+ async params pattern.\n */\nexport async function generateMetadata({\n params,\n apiKey,\n cmsUrl,\n websiteId: providedWebsiteId,\n}: PageProps): Promise<Metadata> {\n const websiteId = getWebsiteId(providedWebsiteId);\n const { slug } = await params;\n const rawPath = `/${slug.join('/')}`;\n const path = normalizePath(rawPath);\n const client = getCmsClient({ apiKey, cmsUrl });\n\n try {\n const { route } = await client.route.getByPath.query({ websiteId, path });\n return {\n title: `${route.path} | Website`,\n description: `Content page: ${route.path}`,\n };\n } catch {\n return {\n title: 'Page Not Found | Website',\n description: 'The requested page could not be found.',\n };\n }\n}\n\nexport function normalizePath(path: string): string {\n if (!path || path === '/') {\n return '/';\n }\n\n // Remove trailing slashes, ensure leading slash\n let normalized = path.trim();\n\n // Remove trailing slashes (but keep root \"/\")\n normalized = normalized.replace(/\\/+$/, '');\n\n // Ensure leading slash\n if (!normalized.startsWith('/')) {\n normalized = `/${normalized}`;\n }\n\n // Collapse multiple consecutive slashes to single slash\n normalized = normalized.replace(/\\/+/g, '/');\n\n return normalized;\n}\n","/**\n * Block Renderer Component\n *\n * Dispatches block data to the appropriate component using the ComponentMap pattern.\n * This is the main entry point for rendering blocks from the CMS.\n */\n\nimport React from 'react';\nimport { BlockToolbar } from './block-toolbar';\nimport { ClientEditableBlock } from './client-editable-block';\nimport type { BlockComponentRegistry, BlockData, ResolvedRouteParams } from './types';\n\ntype TextInfo = {\n value: string;\n path: Array<string | number>;\n parentType?: React.ElementType;\n key?: React.Key | null;\n};\n\ntype ElementInfo = {\n element: React.ReactElement;\n path: Array<string | number>;\n};\n\ntype WalkVisitors = {\n /**\n * Called for every string/number child encountered.\n * Return:\n * - same string (or modified)\n * - a ReactNode (e.g. wrap in <span/>)\n */\n onText?: (info: TextInfo) => React.ReactNode;\n\n /**\n * Called for every ReactElement encountered (after children are processed).\n * Return:\n * - same element\n * - a cloned/modified element\n */\n onElement?: (info: ElementInfo) => React.ReactElement;\n};\n\n/**\n * Recursively maps a ReactNode tree, allowing transformations of text nodes and/or elements.\n * SSR-safe: does not touch DOM APIs.\n */\nexport function walkReactNode(\n node: React.ReactNode,\n visitors: WalkVisitors,\n ctx: {\n path?: Array<string | number>;\n parentType?: React.ElementType;\n key?: React.Key | null;\n } = {}\n): React.ReactNode {\n const path = ctx.path ?? [];\n\n // Fast-path primitives\n if (node == null || typeof node === 'boolean') return node;\n\n if (typeof node === 'string' || typeof node === 'number') {\n const value = String(node);\n return visitors.onText\n ? visitors.onText({ value, path, parentType: ctx.parentType, key: ctx.key })\n : node;\n }\n\n // Arrays\n if (Array.isArray(node)) {\n return node.map((child, i) => {\n // biome-ignore lint/suspicious/noExplicitAny: React child key access\n const childKey = (child as any)?.key ?? null;\n const result = walkReactNode(child, visitors, {\n path: [...path, i],\n parentType: ctx.parentType,\n key: childKey,\n });\n // Ensure array children have keys\n if (React.isValidElement(result) && result.key == null) {\n return React.cloneElement(result, { key: childKey ?? `arr-${path.join('-')}-${i}` });\n }\n return result;\n });\n }\n\n // ReactElement (including Fragment)\n if (React.isValidElement(node)) {\n // biome-ignore lint/suspicious/noExplicitAny: React element props access\n const el = node as React.ReactElement<any>;\n const elProps = el.props as Record<string, unknown> | null;\n\n // Recurse into children (if any)\n const hasChildren = elProps && 'children' in elProps;\n const nextChildren = hasChildren\n ? React.Children.map(elProps.children as React.ReactNode, (child, i) => {\n // biome-ignore lint/suspicious/noExplicitAny: React child key access\n const childKey = (child as any)?.key ?? null;\n const result = walkReactNode(child, visitors, {\n path: [...path, 'children', i],\n parentType: el.type as React.ElementType,\n key: childKey,\n });\n // Ensure children have keys\n if (React.isValidElement(result) && result.key == null) {\n return React.cloneElement(result, { key: childKey ?? `child-${path.join('-')}-${i}` });\n }\n return result;\n })\n : (elProps?.children as React.ReactNode);\n\n // Only clone if children changed (or if you want to force a clone)\n const cloned = hasChildren\n ? React.cloneElement(el, undefined, nextChildren as React.ReactNode)\n : el;\n\n return visitors.onElement ? visitors.onElement({ element: cloned, path }) : cloned;\n }\n\n // Functions, symbols, portals, etc. are rare here; return as-is\n return node;\n}\n\n// -----------------------------------------------------------------------------\n// Content Value Extraction\n// -----------------------------------------------------------------------------\n\ntype ContentMatch = {\n contentPath: string;\n value: string;\n};\n\n/**\n * Extracts all string values from a content object with their paths.\n * Returns a Map where keys are string values and values are arrays of content paths.\n */\nfunction extractContentValues(\n content: Record<string, unknown>,\n basePath: string[] = []\n): Map<string, ContentMatch[]> {\n const map = new Map<string, ContentMatch[]>();\n\n function walk(obj: unknown, path: string[]) {\n if (typeof obj === 'string' && obj.trim() !== '') {\n const contentPath = path.join('.');\n const existing = map.get(obj) || [];\n existing.push({ contentPath, value: obj });\n map.set(obj, existing);\n } else if (Array.isArray(obj)) {\n for (let index = 0; index < obj.length; index++) {\n walk(obj[index], [...path, String(index)]);\n }\n } else if (obj && typeof obj === 'object') {\n for (const [key, value] of Object.entries(obj)) {\n walk(value, [...path, key]);\n }\n }\n }\n\n walk(content, basePath);\n return map;\n}\n\n// -----------------------------------------------------------------------------\n// Props\n// -----------------------------------------------------------------------------\n\ninterface BlockRendererProps {\n /**\n * The block data to render.\n * Must have a `type` field that maps to a registered component.\n */\n block: BlockData;\n registry: Partial<BlockComponentRegistry>;\n /**\n * If true, renders the component without any tree walking or editable wrappers.\n */\n disableEditable?: boolean;\n /**\n * Resolved route parameters from parametric routes.\n * Each key is a param name (e.g., \"country\") with its value, schema name, and full document.\n */\n routeParams?: ResolvedRouteParams;\n}\n\n// -----------------------------------------------------------------------------\n// Component\n// -----------------------------------------------------------------------------\n\n/**\n * Recursively renders a React node, invoking function components to get their output.\n * This allows us to walk the full rendered tree, not just the element wrappers.\n */\nfunction renderToWalkableTree(node: React.ReactNode, keyPrefix = ''): React.ReactNode {\n if (node == null || typeof node === 'boolean') return node;\n if (typeof node === 'string' || typeof node === 'number') return node;\n\n if (Array.isArray(node)) {\n return node.map((child, i) => {\n const result = renderToWalkableTree(child, `${keyPrefix}${i}-`);\n // Ensure array children have keys\n if (React.isValidElement(result) && result.key == null) {\n // biome-ignore lint/suspicious/noExplicitAny: Adding key to element\n const existingKey = (child as any)?.key;\n return React.cloneElement(result, { key: existingKey ?? `${keyPrefix}${i}` });\n }\n return result;\n });\n }\n\n if (React.isValidElement(node)) {\n // biome-ignore lint/suspicious/noExplicitAny: React element props access\n const el = node as React.ReactElement<any>;\n const elProps = el.props as Record<string, unknown> | null;\n\n // If it's a function component, invoke it to get the rendered output\n if (typeof el.type === 'function') {\n try {\n // biome-ignore lint/complexity/noBannedTypes: Need to invoke React function component\n const rendered = (el.type as Function)(el.props);\n return renderToWalkableTree(rendered, keyPrefix);\n } catch {\n // If component throws (e.g., uses hooks), return as-is\n return node;\n }\n }\n\n // For host elements (div, span, etc.), recurse into children\n if (elProps && 'children' in elProps) {\n const newChildren = renderToWalkableTree(elProps.children as React.ReactNode, keyPrefix);\n return React.cloneElement(el, undefined, newChildren);\n }\n\n return node;\n }\n\n return node;\n}\n\n/**\n * Renders a single block by dispatching to the appropriate component.\n *\n * Uses the ComponentMap pattern: the block's `type` field determines which\n * component renders the block's `content`.\n *\n * Internally, it:\n * 1. Renders the component tree by invoking function components\n * 2. Extracts all string values from block.content\n * 3. Walks the rendered tree and wraps matching text nodes with spans\n */\nexport function BlockRenderer({\n block,\n registry,\n disableEditable,\n routeParams,\n}: BlockRendererProps) {\n const Component = registry[block.type];\n\n if (!Component) {\n // Log warning in development, render nothing in production\n if (process.env.NODE_ENV === 'development') {\n console.warn(`[BlockRenderer] Unknown block type: ${block.type}`);\n }\n return null;\n }\n\n // biome-ignore lint/suspicious/noExplicitAny: Type safety ensured by BlockData discriminated union\n const component = <Component content={block.content as any} routeParams={routeParams} />;\n\n if (disableEditable) {\n return component;\n }\n\n // Extract all string values from content with their paths\n const contentValueMap = extractContentValues(block.content as Record<string, unknown>);\n\n // Try server-side tree walking for text span injection (best-effort).\n // This works for sync server components but fails for client components,\n // async components, and components using hooks.\n let renderedComponent: React.ReactNode = component;\n let needsClientSideSpans = true;\n\n try {\n const renderedTree = renderToWalkableTree(component);\n\n // Check if tree walking produced host elements (div, section, etc.)\n // vs the original component reference (function type = couldn't be invoked)\n const isWalkable =\n React.isValidElement(renderedTree) &&\n typeof (renderedTree as React.ReactElement<Record<string, unknown>>).type === 'string';\n\n if (isWalkable) {\n const usedPaths = new Set<string>();\n\n renderedComponent = walkReactNode(renderedTree, {\n onText: ({ value, key, path }) => {\n const matches = contentValueMap.get(value);\n if (!matches || matches.length === 0) return value;\n\n const match = matches.find((m) => !usedPaths.has(m.contentPath)) ?? matches[0];\n if (!match) return value;\n\n usedPaths.add(match.contentPath);\n const spanKey = key ?? `${block.id}-${match.contentPath}-${path.join('-')}`;\n\n return (\n <span\n key={spanKey}\n data-cms-editable\n data-block-id={block.id}\n data-block-type={block.type}\n data-content-path={match.contentPath}\n >\n {value}\n </span>\n );\n },\n });\n\n needsClientSideSpans = false;\n }\n } catch {\n // Tree walking failed entirely, fall back to client-side injection\n }\n\n // Build content entries for client-side text matching when server-side fails\n const contentEntries = needsClientSideSpans\n ? Array.from(contentValueMap.entries())\n .map(([value, matches]) => ({ v: value, p: matches[0]?.contentPath }))\n .filter((e): e is { v: string; p: string } => !!e.p)\n : [];\n\n return (\n <div\n key={block.id}\n data-cms-block\n data-block-id={block.id}\n data-block-type={block.type}\n style={{ position: 'relative' }}\n >\n <style>{`\n [data-cms-block] {\n position: relative;\n }\n [data-cms-block]:hover {\n outline: 2px solid #3b82f6;\n outline-offset: 4px;\n }\n [data-cms-editable] {\n cursor: pointer;\n border-radius: 2px;\n }\n [data-cms-editable]:hover {\n outline: 2px solid #3b82f6;\n outline-offset: 2px;\n }\n .cms-block-toolbar {\n position: absolute;\n bottom: 8px;\n left: 50%;\n transform: translateX(-50%);\n display: flex;\n gap: 4px;\n background: #1f2937;\n border-radius: 6px;\n padding: 4px;\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);\n opacity: 0;\n pointer-events: none;\n transition: opacity 0.15s ease;\n z-index: 1000;\n }\n [data-cms-block]:hover .cms-block-toolbar {\n opacity: 1;\n pointer-events: auto;\n }\n .cms-block-toolbar button {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 28px;\n height: 28px;\n border: none;\n background: transparent;\n color: #9ca3af;\n border-radius: 4px;\n cursor: pointer;\n transition: background 0.15s ease, color 0.15s ease;\n }\n .cms-block-toolbar button:hover {\n background: #374151;\n color: #fff;\n }\n .cms-block-toolbar button.delete:hover {\n background: #dc2626;\n color: #fff;\n }\n .cms-block-toolbar button:disabled {\n opacity: 0.4;\n cursor: not-allowed;\n }\n .cms-block-toolbar button:disabled:hover {\n background: transparent;\n color: #9ca3af;\n }\n .cms-block-toolbar svg {\n width: 16px;\n height: 16px;\n }\n `}</style>\n <script\n // biome-ignore lint/security/noDangerouslySetInnerHtml: Inline script for iframe postMessage click routing\n dangerouslySetInnerHTML={{\n __html: `\n (function() {\n if (!window.__cmsEditableInitialized) {\n window.__cmsEditableInitialized = true;\n\n document.addEventListener('click', function(e) {\n if (e.target.closest('.cms-block-toolbar')) return;\n\n var editableTarget = e.target.closest('[data-cms-editable]');\n if (editableTarget) {\n var message = {\n type: 'cms-editable-click',\n blockId: editableTarget.getAttribute('data-block-id'),\n blockType: editableTarget.getAttribute('data-block-type'),\n contentPath: editableTarget.getAttribute('data-content-path')\n };\n if (window.parent && window.parent !== window) {\n window.parent.postMessage(message, '*');\n }\n return;\n }\n\n var blockTarget = e.target.closest('[data-cms-block]');\n if (blockTarget) {\n var message = {\n type: 'cms-editable-click',\n blockId: blockTarget.getAttribute('data-block-id'),\n blockType: blockTarget.getAttribute('data-block-type'),\n contentPath: null\n };\n if (window.parent && window.parent !== window) {\n window.parent.postMessage(message, '*');\n }\n }\n });\n }\n })();\n `,\n }}\n />\n {needsClientSideSpans && contentEntries.length > 0 ? (\n <ClientEditableBlock\n blockId={block.id}\n blockType={block.type}\n contentEntries={contentEntries}\n >\n {renderedComponent}\n </ClientEditableBlock>\n ) : (\n renderedComponent\n )}\n <BlockToolbar key=\"cms-toolbar\" blockId={block.id} />\n </div>\n );\n}\n","/**\n * CMS API Client\n *\n * Creates an HTTP-based tRPC client for calling the CMS API.\n * Used in Server Components to fetch routes and blocks from the CMS.\n */\n\nimport type { AppRouter } from '@repo/cms-schema/trpc';\nimport { type CreateTRPCClient, createTRPCClient, httpBatchLink } from '@trpc/client';\nimport superjson from 'superjson';\n\n/** Type alias for the CMS API client */\ntype CmsClient = CreateTRPCClient<AppRouter>;\n\n/**\n * Get the CMS API URL from the provided base URL.\n */\nfunction getCmsApiUrl(cmsUrl: string): string {\n return new URL('/api/trpc', cmsUrl).toString();\n}\n\n/** Unified configuration for all CMS API access */\nexport interface CmsConfig {\n /** CMS API base URL (e.g., 'http://localhost:3000') */\n cmsUrl: string;\n /** API key for authentication */\n apiKey?: string;\n /** Website ID to scope API requests */\n websiteId?: string;\n}\n\n/**\n * Create a custom fetch function that appends API key as query parameter.\n */\nfunction createFetchWithApiKey(apiKey?: string, websiteId?: string) {\n return async (url: URL | RequestInfo, options?: RequestInit): Promise<Response> => {\n let finalUrl = url;\n\n const urlObj = new URL(url.toString());\n if (apiKey) {\n urlObj.searchParams.set('api_key', apiKey);\n }\n if (websiteId) {\n urlObj.searchParams.set('website_id', websiteId);\n }\n if (apiKey || websiteId) {\n finalUrl = urlObj.toString();\n }\n\n const response = await fetch(finalUrl, options);\n\n return response;\n };\n}\n\n/**\n * Create a tRPC client for the CMS API.\n */\nfunction createCmsClient(options: CmsConfig): CmsClient {\n const url = getCmsApiUrl(options.cmsUrl);\n\n return createTRPCClient<AppRouter>({\n links: [\n httpBatchLink({\n url,\n transformer: superjson,\n fetch: createFetchWithApiKey(options.apiKey, options.websiteId),\n }),\n ],\n });\n}\n\n/**\n * Get a CMS client for the specified CMS URL.\n */\nexport function getCmsClient(options: CmsConfig): CmsClient {\n return createCmsClient(options);\n}\n"],"mappings":";AA4BO,SAAS,wBAAwB,SAAyC;AAC/E,MAAI,CAAC,WAAW,OAAO,YAAY,UAAU;AAC3C,WAAO;AAAA,EACT;AAEA,QAAM,SAAS;AACf,QAAM,WAAW,OAAO,OAAO,aAAa,WAAW,OAAO,WAAW;AACzE,QAAM,OAAO,OAAO,OAAO,SAAS,WAAW,OAAO,OAAO;AAE7D,MAAI,CAAC,YAAY,CAAC,MAAM;AACtB,WAAO;AAAA,EACT;AAEA,QAAM,SAAS,OAAO,OAAO,WAAW,WAAW,OAAO,SAAS;AACnE,QAAM,cAAc,OAAO,OAAO,gBAAgB,WAAW,OAAO,cAAc;AAClF,QAAM,OAAO,MAAM,QAAQ,OAAO,IAAI,IAAI,OAAO,KAAK,IAAI,CAAC,QAAQ,OAAO,GAAG,CAAC,IAAI;AAClF,QAAM,YAAY,OAAO,OAAO,WAAW,WAAW,OAAO,OAAO,KAAK,IAAI;AAE7E,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,QAAQ,aAAa;AAAA,EACvB;AACF;AAsBO,SAAS,mBAAmB,SAAkC;AACnE,QAAM,MAAM,oBAAI,KAAK;AACrB,QAAM,cAAc,QAAQ,cAAc,IAAI,KAAK,QAAQ,WAAW,IAAI;AAE1E,SACE,QAAQ,WAAW,eACnB,gBAAgB,QAChB,CAAC,OAAO,MAAM,YAAY,QAAQ,CAAC,KACnC,eAAe;AAEnB;;;AC7EA,SAAS,SAAS;AAMX,IAAM,qBAAqB,CAAC,cAAc,aAAa,YAAY;AAGnE,IAAM,gBAAgB,KAAK,OAAO;AAClC,IAAM,gBAAgB;AAEtB,IAAM,gBAAgB;AACtB,IAAM,gBAAgB;AAMtB,IAAM,iBAAiB,EAAE,KAAK,kBAAkB;AAEhD,IAAM,iBAAiB,EAC3B,OAAO,EACP,IAAI,EACJ,IAAI,eAAe,4BAA4B,aAAa,QAAQ,EACpE,IAAI,eAAe,4BAA4B,gBAAgB,OAAO,IAAI,IAAI;AAE1E,IAAM,kBAAkB,EAC5B,OAAO,EACP,IAAI,EACJ,IAAI,eAAe,iCAAiC,aAAa,IAAI,EACrE,IAAI,eAAe,iCAAiC,aAAa,IAAI;AAKjE,IAAM,sBAAsB,EAAE,OAAO;AAAA,EAC1C,UAAU,EACP,OAAO,EACP,IAAI,GAAG,sBAAsB,EAC7B,IAAI,KAAK,mBAAmB,EAC5B,MAAM,oBAAoB,sCAAsC;AAAA,EACnE,UAAU;AAAA,EACV,UAAU;AACZ,CAAC;AAQM,IAAM,sBAAsB,EAAE,OAAO;AAAA,EAC1C,SAAS,EAAE,KAAK,EAAE,SAAS;AAAA,EAC3B,OAAO;AAAA,EACP,QAAQ;AACV,CAAC;;;AClDD,SAAS,KAAAA,UAAS;AAGX,IAAM,4BAA4BA,GAAE,OAAO;AAAA,EAChD,UAAUA,GAAE,OAAO,EAAE,IAAI,GAAG,8BAA8B,EAAE,IAAI,KAAK,mBAAmB,EAAE,KAAK;AAAA,EAC/F,QAAQA,GAAE,OAAO,EAAE,IAAI,KAAK,yBAAyB,EAAE,KAAK,EAAE,SAAS;AAAA,EACvE,aAAaA,GAAE,IACZ,SAAS,EAAE,SAAS,gEAAgE,CAAC,EACrF,SAAS;AAAA,EACZ,MAAMA,GAAE,OAAO,EAAE,IAAI,GAAG,kCAAkC;AAAA,EAC1D,MAAMA,GAAE,MAAMA,GAAE,OAAO,CAAC,EAAE,SAAS;AAAA,EACnC,QAAQA,GAAE,KAAK,CAAC,SAAS,UAAU,WAAW,CAAC;AACjD,CAAC;AAMM,IAAM,4BAA4B;;;AC1BzC,SAAS,KAAAC,UAAS;AAIlB,IAAM,kBAAkBA,GAAE,OAAO;AAAA,EAC/B,MAAMA,GAAE,OAAO,EAAE,IAAI,GAAG,yBAAyB,EAAE,IAAI,IAAI,sBAAsB;AAAA,EACjF,KAAKA,GAAE,OAAO,EAAE;AAAA,IACd,CAAC,QAAQ;AAEP,UAAI,IAAI,WAAW,SAAS,KAAK,IAAI,WAAW,UAAU,GAAG;AAC3D,YAAI;AACF,cAAI,IAAI,GAAG;AACX,iBAAO;AAAA,QACT,QAAQ;AACN,iBAAO;AAAA,QACT;AAAA,MACF;AAEA,UAAI,IAAI,WAAW,GAAG,GAAG;AACvB,eAAO;AAAA,MACT;AAEA,UAAI,IAAI,WAAW,GAAG,GAAG;AACvB,eAAO;AAAA,MACT;AACA,aAAO;AAAA,IACT;AAAA,IACA;AAAA,MACE,SACE;AAAA,IACJ;AAAA,EACF;AACF,CAAC;AAKM,IAAM,wBAAwBA,GAAE,OAAO;AAAA,EAC5C,UAAUA,GAAE,OAAO,EAAE,IAAI,GAAG,sBAAsB,EAAE,IAAI,KAAK,mBAAmB;AAAA,EAChF,aAAaA,GAAE,OAAO,EAAE,IAAI,KAAK,sBAAsB,EAAE,SAAS;AAAA,EAClE,eAAe;AAAA,EACf,iBAAiB,gBAAgB,SAAS;AAC5C,CAAC;AAMM,IAAM,wBAAwB;;;AChDrC,SAAS,KAAAC,UAAS;AAGX,IAAM,iBAAiB,CAAC,QAAQ,QAAQ,UAAU;AAIlD,IAAM,oBAAoBA,GAAE,OAAO;AAAA,EACxC,MAAMA,GAAE,OAAO,EAAE,IAAI,IAAI,oBAAoB,EAAE,SAAS;AAAA,EACxD,OAAOA,GAAE,OAAO,EAAE,IAAI,GAAG,mBAAmB,EAAE,IAAI,KAAK,gBAAgB;AAAA,EACvE,aAAaA,GAAE,OAAO,EAAE,IAAI,KAAK,sBAAsB,EAAE,SAAS;AACpE,CAAC;AAKM,IAAM,6BAA6BA,GAAE,OAAO;AAAA,EACjD,OAAOA,GAAE,OAAO,EAAE,IAAI,GAAG,2BAA2B,EAAE,IAAI,KAAK,gBAAgB;AAAA,EAC/E,UAAUA,GAAE,OAAO,EAAE,IAAI,KAAK,mBAAmB,EAAE,SAAS;AAAA,EAC5D,UAAUA,GACP,MAAM,iBAAiB,EACvB,IAAI,GAAG,kCAAkC,EACzC,IAAI,GAAG,4BAA4B;AAAA,EACtC,QAAQA,GAAE,KAAK,cAAc,EAAE,QAAQ,MAAM;AAC/C,CAAC;AAMM,IAAM,6BAA6B;;;AC9B1C,SAAS,KAAAC,UAAS;;;ACElB,SAAS,KAAAC,UAAS;AAyBX,IAAM,mBAAmBC,GAAE,OAAO;AAAA;AAAA,EAEvC,IAAIA,GAAE,KAAK;AAAA;AAAA,EAEX,KAAKA,GAAE,IAAI;AAAA;AAAA,EAEX,OAAO;AAAA;AAAA,EAEP,QAAQ;AAAA;AAAA,EAER,kBAAkBA,GAAE,OAAO,EAAE,IAAI,CAAC,EAAE,IAAI,GAAG;AAAA;AAAA,EAE3C,UAAU;AAAA;AAAA,EAEV,UAAU;AAAA;AAAA,EAEV,MAAMA,GAAE,OAAO,EAAE,SAAS;AAC5B,CAAC;AAYM,IAAM,gBAAgBA,GAAE,OAAO;AAAA,EACpC,GAAGA,GAAE,OAAO,EAAE,IAAI,CAAC,EAAE,IAAI,CAAC;AAAA,EAC1B,GAAGA,GAAE,OAAO,EAAE,IAAI,CAAC,EAAE,IAAI,CAAC;AAC5B,CAAC;AAUM,IAAM,aAAaA,GAAE,OAAO;AAAA;AAAA,EAEjC,GAAGA,GAAE,OAAO,EAAE,IAAI,EAAE,YAAY;AAAA;AAAA,EAEhC,GAAGA,GAAE,OAAO,EAAE,IAAI,EAAE,YAAY;AAAA;AAAA,EAEhC,OAAOA,GAAE,OAAO,EAAE,IAAI,EAAE,SAAS;AAAA;AAAA,EAEjC,QAAQA,GAAE,OAAO,EAAE,IAAI,EAAE,SAAS;AACpC,CAAC;AAQM,IAAM,uBAAuBA,GAAE,OAAO;AAAA;AAAA,EAE3C,KAAKA,GAAE,OAAO,EAAE,IAAI,GAAG,wCAAwC;AAAA;AAAA,EAG/D,SAASA,GAAE,OAAO,EAAE,IAAI,GAAG,EAAE,SAAS;AAAA,EACtC,aAAaA,GAAE,OAAO,EAAE,IAAI,GAAG,EAAE,SAAS;AAAA;AAAA,EAG1C,QAAQA,GAAE,OAAO;AAAA,IACf,IAAIA,GAAE,KAAK;AAAA,IACX,gBAAgBA,GAAE,OAAO,EAAE,SAAS,EAAE,SAAS;AAAA,EACjD,CAAC;AACH,CAAC;AA2ED,IAAM,aAAaC,GAAE,OAAO;AAAA,EAC1B,KAAKA,GAAE,IAAI;AAAA,EACX,MAAMA,GAAE,OAAO;AAAA,EACf,MAAMA,GAAE,OAAO,EAAE,IAAI,EAAE,SAAS;AAAA,EAChC,MAAMA,GAAE,OAAO;AACjB,CAAC;;;AD/KM,IAAM,gBAAgB,CAAC,QAAQ,UAAU,OAAO;AAIhD,IAAM,yBAAyBC,GAAE,OAAO;AAAA,EAC7C,UAAUA,GAAE,OAAO,EAAE,IAAI,GAAG,sBAAsB,EAAE,IAAI,KAAK,mBAAmB;AAAA,EAChF,aAAaA,GAAE,OAAO,EAAE,IAAI,KAAK,sBAAsB,EAAE,SAAS;AAAA,EAClE,SAASA,GAAE,OAAO,EAAE,IAAI,IAAI,mBAAmB,EAAE,SAAS;AAAA,EAC1D,QAAQA,GACL,OAAO,EACP;AAAA,IACC,CAAC,QAAQ;AAEP,UAAI,QAAQ,GAAI,QAAO;AAEvB,UAAI,IAAI,WAAW,SAAS,KAAK,IAAI,WAAW,UAAU,GAAG;AAC3D,YAAI;AACF,cAAI,IAAI,GAAG;AACX,iBAAO;AAAA,QACT,QAAQ;AACN,iBAAO;AAAA,QACT;AAAA,MACF;AAEA,UAAI,IAAI,WAAW,GAAG,GAAG;AACvB,eAAO;AAAA,MACT;AAEA,UAAI,IAAI,WAAW,GAAG,GAAG;AACvB,eAAO;AAAA,MACT;AACA,aAAO;AAAA,IACT;AAAA,IACA;AAAA,MACE,SACE;AAAA,IACJ;AAAA,EACF,EACC,SAAS,EACT,GAAGA,GAAE,QAAQ,EAAE,CAAC;AAAA,EACnB,iBAAiB,qBAAqB,SAAS,EAAE,SAAS;AAAA,EAC1D,WAAWA,GAAE,KAAK,aAAa,EAAE,QAAQ,QAAQ;AACnD,CAAC;AAMM,IAAM,yBAAyB;;;AElDtC,SAAS,KAAAC,UAAS;AAKX,IAAM,iBAAiBC,GAAE,OAAO;AAAA;AAAA,EAErC,IAAIA,GAAE,KAAK;AAAA;AAAA,EAEX,OAAO;AAAA;AAAA,EAEP,MAAMA,GAAE,OAAO,EAAE,IAAI,KAAK,eAAe,EAAE,SAAS;AACtD,CAAC;AAQM,IAAM,uBAAuBA,GAAE,OAAO;AAAA;AAAA,EAE3C,KAAKA,GAAE,OAAO;AAAA;AAAA,EAEd,KAAKA,GAAE,OAAO;AAAA;AAAA,EAEd,MAAMA,GAAE,OAAO,EAAE,SAAS;AAC5B,CAAC;AAKM,IAAM,8BAA8BA,GAAE,OAAO;AAAA,EAClD,OAAOA,GAAE,OAAO,EAAE,IAAI,KAAK,gBAAgB,EAAE,SAAS;AAAA,EACtD,OAAOA,GAAE,MAAM,cAAc,EAAE,IAAI,IAAI,0BAA0B;AACnE,CAAC;AAMM,IAAM,+BAA+B;;;AChCrC,IAAM,qBAAqB;AAAA,EAChC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAqBO,SAAS,uBAAuB,MAAuC;AAC5E,SAAO,mBAAmB,SAAS,IAAuB;AAC5D;;;ACjCA,SAAS,gBAAgB;;;ACPzB,OAAO,WAAW;AAClB,SAAS,oBAAoB;AAC7B,SAAS,2BAA2B;AAiQhB,cAkEhB,YAlEgB;AA5Nb,SAAS,cACd,MACA,UACA,MAII,CAAC,GACY;AACjB,QAAM,OAAO,IAAI,QAAQ,CAAC;AAG1B,MAAI,QAAQ,QAAQ,OAAO,SAAS,UAAW,QAAO;AAEtD,MAAI,OAAO,SAAS,YAAY,OAAO,SAAS,UAAU;AACxD,UAAM,QAAQ,OAAO,IAAI;AACzB,WAAO,SAAS,SACZ,SAAS,OAAO,EAAE,OAAO,MAAM,YAAY,IAAI,YAAY,KAAK,IAAI,IAAI,CAAC,IACzE;AAAA,EACN;AAGA,MAAI,MAAM,QAAQ,IAAI,GAAG;AACvB,WAAO,KAAK,IAAI,CAAC,OAAO,MAAM;AAE5B,YAAM,WAAY,OAAe,OAAO;AACxC,YAAM,SAAS,cAAc,OAAO,UAAU;AAAA,QAC5C,MAAM,CAAC,GAAG,MAAM,CAAC;AAAA,QACjB,YAAY,IAAI;AAAA,QAChB,KAAK;AAAA,MACP,CAAC;AAED,UAAI,MAAM,eAAe,MAAM,KAAK,OAAO,OAAO,MAAM;AACtD,eAAO,MAAM,aAAa,QAAQ,EAAE,KAAK,YAAY,OAAO,KAAK,KAAK,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC;AAAA,MACrF;AACA,aAAO;AAAA,IACT,CAAC;AAAA,EACH;AAGA,MAAI,MAAM,eAAe,IAAI,GAAG;AAE9B,UAAM,KAAK;AACX,UAAM,UAAU,GAAG;AAGnB,UAAM,cAAc,WAAW,cAAc;AAC7C,UAAM,eAAe,cACjB,MAAM,SAAS,IAAI,QAAQ,UAA6B,CAAC,OAAO,MAAM;AAEpE,YAAM,WAAY,OAAe,OAAO;AACxC,YAAM,SAAS,cAAc,OAAO,UAAU;AAAA,QAC5C,MAAM,CAAC,GAAG,MAAM,YAAY,CAAC;AAAA,QAC7B,YAAY,GAAG;AAAA,QACf,KAAK;AAAA,MACP,CAAC;AAED,UAAI,MAAM,eAAe,MAAM,KAAK,OAAO,OAAO,MAAM;AACtD,eAAO,MAAM,aAAa,QAAQ,EAAE,KAAK,YAAY,SAAS,KAAK,KAAK,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC;AAAA,MACvF;AACA,aAAO;AAAA,IACT,CAAC,IACA,SAAS;AAGd,UAAM,SAAS,cACX,MAAM,aAAa,IAAI,QAAW,YAA+B,IACjE;AAEJ,WAAO,SAAS,YAAY,SAAS,UAAU,EAAE,SAAS,QAAQ,KAAK,CAAC,IAAI;AAAA,EAC9E;AAGA,SAAO;AACT;AAeA,SAAS,qBACP,SACA,WAAqB,CAAC,GACO;AAC7B,QAAM,MAAM,oBAAI,IAA4B;AAE5C,WAAS,KAAK,KAAc,MAAgB;AAC1C,QAAI,OAAO,QAAQ,YAAY,IAAI,KAAK,MAAM,IAAI;AAChD,YAAM,cAAc,KAAK,KAAK,GAAG;AACjC,YAAM,WAAW,IAAI,IAAI,GAAG,KAAK,CAAC;AAClC,eAAS,KAAK,EAAE,aAAa,OAAO,IAAI,CAAC;AACzC,UAAI,IAAI,KAAK,QAAQ;AAAA,IACvB,WAAW,MAAM,QAAQ,GAAG,GAAG;AAC7B,eAAS,QAAQ,GAAG,QAAQ,IAAI,QAAQ,SAAS;AAC/C,aAAK,IAAI,KAAK,GAAG,CAAC,GAAG,MAAM,OAAO,KAAK,CAAC,CAAC;AAAA,MAC3C;AAAA,IACF,WAAW,OAAO,OAAO,QAAQ,UAAU;AACzC,iBAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,GAAG,GAAG;AAC9C,aAAK,OAAO,CAAC,GAAG,MAAM,GAAG,CAAC;AAAA,MAC5B;AAAA,IACF;AAAA,EACF;AAEA,OAAK,SAAS,QAAQ;AACtB,SAAO;AACT;AAgCA,SAAS,qBAAqB,MAAuB,YAAY,IAAqB;AACpF,MAAI,QAAQ,QAAQ,OAAO,SAAS,UAAW,QAAO;AACtD,MAAI,OAAO,SAAS,YAAY,OAAO,SAAS,SAAU,QAAO;AAEjE,MAAI,MAAM,QAAQ,IAAI,GAAG;AACvB,WAAO,KAAK,IAAI,CAAC,OAAO,MAAM;AAC5B,YAAM,SAAS,qBAAqB,OAAO,GAAG,SAAS,GAAG,CAAC,GAAG;AAE9D,UAAI,MAAM,eAAe,MAAM,KAAK,OAAO,OAAO,MAAM;AAEtD,cAAM,cAAe,OAAe;AACpC,eAAO,MAAM,aAAa,QAAQ,EAAE,KAAK,eAAe,GAAG,SAAS,GAAG,CAAC,GAAG,CAAC;AAAA,MAC9E;AACA,aAAO;AAAA,IACT,CAAC;AAAA,EACH;AAEA,MAAI,MAAM,eAAe,IAAI,GAAG;AAE9B,UAAM,KAAK;AACX,UAAM,UAAU,GAAG;AAGnB,QAAI,OAAO,GAAG,SAAS,YAAY;AACjC,UAAI;AAEF,cAAM,WAAY,GAAG,KAAkB,GAAG,KAAK;AAC/C,eAAO,qBAAqB,UAAU,SAAS;AAAA,MACjD,QAAQ;AAEN,eAAO;AAAA,MACT;AAAA,IACF;AAGA,QAAI,WAAW,cAAc,SAAS;AACpC,YAAM,cAAc,qBAAqB,QAAQ,UAA6B,SAAS;AACvF,aAAO,MAAM,aAAa,IAAI,QAAW,WAAW;AAAA,IACtD;AAEA,WAAO;AAAA,EACT;AAEA,SAAO;AACT;AAaO,SAAS,cAAc;AAAA,EAC5B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAAuB;AACrB,QAAM,YAAY,SAAS,MAAM,IAAI;AAErC,MAAI,CAAC,WAAW;AAEd,QAAI,QAAQ,IAAI,aAAa,eAAe;AAC1C,cAAQ,KAAK,uCAAuC,MAAM,IAAI,EAAE;AAAA,IAClE;AACA,WAAO;AAAA,EACT;AAGA,QAAM,YAAY,oBAAC,aAAU,SAAS,MAAM,SAAgB,aAA0B;AAEtF,MAAI,iBAAiB;AACnB,WAAO;AAAA,EACT;AAGA,QAAM,kBAAkB,qBAAqB,MAAM,OAAkC;AAKrF,MAAI,oBAAqC;AACzC,MAAI,uBAAuB;AAE3B,MAAI;AACF,UAAM,eAAe,qBAAqB,SAAS;AAInD,UAAM,aACJ,MAAM,eAAe,YAAY,KACjC,OAAQ,aAA6D,SAAS;AAEhF,QAAI,YAAY;AACd,YAAM,YAAY,oBAAI,IAAY;AAElC,0BAAoB,cAAc,cAAc;AAAA,QAC9C,QAAQ,CAAC,EAAE,OAAO,KAAK,KAAK,MAAM;AAChC,gBAAM,UAAU,gBAAgB,IAAI,KAAK;AACzC,cAAI,CAAC,WAAW,QAAQ,WAAW,EAAG,QAAO;AAE7C,gBAAM,QAAQ,QAAQ,KAAK,CAAC,MAAM,CAAC,UAAU,IAAI,EAAE,WAAW,CAAC,KAAK,QAAQ,CAAC;AAC7E,cAAI,CAAC,MAAO,QAAO;AAEnB,oBAAU,IAAI,MAAM,WAAW;AAC/B,gBAAM,UAAU,OAAO,GAAG,MAAM,EAAE,IAAI,MAAM,WAAW,IAAI,KAAK,KAAK,GAAG,CAAC;AAEzE,iBACE;AAAA,YAAC;AAAA;AAAA,cAEC,qBAAiB;AAAA,cACjB,iBAAe,MAAM;AAAA,cACrB,mBAAiB,MAAM;AAAA,cACvB,qBAAmB,MAAM;AAAA,cAExB;AAAA;AAAA,YANI;AAAA,UAOP;AAAA,QAEJ;AAAA,MACF,CAAC;AAED,6BAAuB;AAAA,IACzB;AAAA,EACF,QAAQ;AAAA,EAER;AAGA,QAAM,iBAAiB,uBACnB,MAAM,KAAK,gBAAgB,QAAQ,CAAC,EACjC,IAAI,CAAC,CAAC,OAAO,OAAO,OAAO,EAAE,GAAG,OAAO,GAAG,QAAQ,CAAC,GAAG,YAAY,EAAE,EACpE,OAAO,CAAC,MAAqC,CAAC,CAAC,EAAE,CAAC,IACrD,CAAC;AAEL,SACE;AAAA,IAAC;AAAA;AAAA,MAEC,kBAAc;AAAA,MACd,iBAAe,MAAM;AAAA,MACrB,mBAAiB,MAAM;AAAA,MACvB,OAAO,EAAE,UAAU,WAAW;AAAA,MAE9B;AAAA,4BAAC,WAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,SAqEN;AAAA,QACF;AAAA,UAAC;AAAA;AAAA,YAEC,yBAAyB;AAAA,cACvB,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,YAsCV;AAAA;AAAA,QACF;AAAA,QACC,wBAAwB,eAAe,SAAS,IAC/C;AAAA,UAAC;AAAA;AAAA,YACC,SAAS,MAAM;AAAA,YACf,WAAW,MAAM;AAAA,YACjB;AAAA,YAEC;AAAA;AAAA,QACH,IAEA;AAAA,QAEF,oBAAC,gBAA+B,SAAS,MAAM,MAA7B,aAAiC;AAAA;AAAA;AAAA,IAlI9C,MAAM;AAAA,EAmIb;AAEJ;;;AC1cA,SAAgC,kBAAkB,qBAAqB;AACvE,OAAO,eAAe;AAQtB,SAAS,aAAa,QAAwB;AAC5C,SAAO,IAAI,IAAI,aAAa,MAAM,EAAE,SAAS;AAC/C;AAeA,SAAS,sBAAsB,QAAiB,WAAoB;AAClE,SAAO,OAAO,KAAwB,YAA6C;AACjF,QAAI,WAAW;AAEf,UAAM,SAAS,IAAI,IAAI,IAAI,SAAS,CAAC;AACrC,QAAI,QAAQ;AACV,aAAO,aAAa,IAAI,WAAW,MAAM;AAAA,IAC3C;AACA,QAAI,WAAW;AACb,aAAO,aAAa,IAAI,cAAc,SAAS;AAAA,IACjD;AACA,QAAI,UAAU,WAAW;AACvB,iBAAW,OAAO,SAAS;AAAA,IAC7B;AAEA,UAAM,WAAW,MAAM,MAAM,UAAU,OAAO;AAE9C,WAAO;AAAA,EACT;AACF;AAKA,SAAS,gBAAgB,SAA+B;AACtD,QAAM,MAAM,aAAa,QAAQ,MAAM;AAEvC,SAAO,iBAA4B;AAAA,IACjC,OAAO;AAAA,MACL,cAAc;AAAA,QACZ;AAAA,QACA,aAAa;AAAA,QACb,OAAO,sBAAsB,QAAQ,QAAQ,QAAQ,SAAS;AAAA,MAChE,CAAC;AAAA,IACH;AAAA,EACF,CAAC;AACH;AAKO,SAAS,aAAa,SAA+B;AAC1D,SAAO,gBAAgB,OAAO;AAChC;;;AFuIU,gBAAAC,YAAA;AA3LV,SAAS,aAAa,mBAAoC;AACxD,QAAM,YACJ,qBACA,QAAQ,IAAI,0BACZ,QAAQ,IAAI,cACZ,QAAQ,IAAI;AAEd,MAAI,CAAC,WAAW;AACd,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AAEA,QAAM,YAAY;AAClB,MAAI,CAAC,UAAU,KAAK,SAAS,GAAG;AAC9B,UAAM,IAAI;AAAA,MACR,sBAAsB,SAAS;AAAA,IACjC;AAAA,EACF;AAEA,SAAO;AACT;AAWA,eAAO,oBAA2C;AAAA,EAChD;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,WAAW;AACb,GAAc;AACZ,QAAM,YAAY,aAAa,iBAAiB;AAChD,QAAM,EAAE,KAAK,IAAI,MAAM;AACvB,QAAM,uBAAuB,MAAM;AAGnC,MAAI,iBAAgC;AACpC,QAAM,iBAAiB,sBAAsB;AAC7C,MAAI,gBAAgB;AAClB,UAAM,aAAa,MAAM,QAAQ,cAAc,IAAI,eAAe,CAAC,IAAI;AACvE,QAAI,YAAY;AACd,YAAM,SAAS,SAAS,YAAY,EAAE;AACtC,UAAI,CAAC,OAAO,MAAM,MAAM,GAAG;AACzB,yBAAiB;AAAA,MACnB;AAAA,IACF;AAAA,EACF;AAGA,QAAM,gBAAgB,sBAAsB;AAC5C,QAAM,WAAW,kBAAkB,UAAU,kBAAkB;AAG/D,QAAM,UAAU,IAAI,KAAK,KAAK,GAAG,CAAC;AAClC,QAAM,OAAO,cAAc,OAAO;AAGlC,QAAM,SAAS,aAAa,EAAE,QAAQ,OAAO,CAAC;AAE9C,MAAI;AAEF,UAAM,EAAE,OAAO,eAAe,IAAI,MAAM,OAAO,MAAM,UAAU,MAAM,EAAE,WAAW,KAAK,CAAC;AAGxF,QAAI,MAAM,UAAU,QAAQ;AAC1B,cAAQ,MAAM,mCAAmC,IAAI,YAAY,MAAM,KAAK,EAAE;AAC9E,eAAS;AAAA,IACX;AAGA,UAAM,gBAAgB,MAAM,UAAU,IAAI,OAAO,YAAY;AAC3D,UAAI;AACF,cAAM,SAAS,MAAM,OAAO,MAAM,QAAQ,MAAM,EAAE,WAAW,IAAI,QAAQ,CAAC;AAC1E,eAAO,OAAO;AAAA,MAChB,SAAS,OAAO;AAEd,gBAAQ,MAAM,yBAAyB,OAAO,KAAK,KAAK;AACxD,eAAO;AAAA,MACT;AAAA,IACF,CAAC;AAQD,UAAM,yBACJ,mBAAmB,OACf,OAAO,MAAM,uBACV,MAAM,EAAE,WAAW,UAAU,MAAM,UAAU,CAAC,EAC9C,MAAM,CAAC,UAAU;AAChB,cAAQ,MAAM,qCAAqC,KAAK;AACxD,aAAO,EAAE,iBAAiB,CAAC,EAAwC;AAAA,IACrE,CAAC,IACH,QAAQ,QAAQ,EAAE,iBAAiB,CAAC,EAAwC,CAAC;AAEnF,UAAM,CAAC,cAAc,EAAE,gBAAgB,CAAC,IAAI,MAAM,QAAQ,IAAI;AAAA,MAC5D,QAAQ,IAAI,aAAa;AAAA,MACzB;AAAA,IACF,CAAC;AAKD,UAAM,SAAsB,CAAC;AAE7B,eAAW,SAAS,cAAc;AAChC,UAAI,CAAC,SAAS,MAAM,sBAAsB,KAAM;AAIhD,UAAI,UAA0C;AAC9C,UAAI,mBAAmB,MAAM;AAC3B,cAAM,iBAAiB,gBAAgB,MAAM,EAAE;AAC/C,cAAM,eAAe,iBAAiB;AACtC,cAAM,WAAW,gBAAgB;AACjC,YAAI,YAAY,MAAM,QAAQ,QAAQ,KAAK,SAAS,YAAY,GAAG;AACjE,oBAAU,SAAS,YAAY,EAAE;AAAA,QACnC;AAAA,MACF;AAEA,gBAAU,WAAY,MAAM;AAC5B,UAAI,CAAC,QAAS;AAGd,UAAI,MAAM,gBAAgB,WAAW;AACnC,cAAM,UAAU,wBAAwB,OAAO;AAC/C,cAAM,cAAc,UAAU,mBAAmB,OAAO,IAAI;AAC5D,YAAI,WAAW,aAAa;AAC1B,iBAAO,KAAK,EAAE,IAAI,MAAM,IAAI,MAAM,WAAW,SAAS,QAAQ,CAAC;AAAA,QACjE;AACA;AAAA,MACF;AAIA,UAAI,MAAM,WAAW;AACnB,eAAO,KAAK,EAAE,IAAI,MAAM,IAAI,MAAM,MAAM,aAAa,QAAQ,CAAC;AAC9D;AAAA,MACF;AAGA,UAAI,CAAC,uBAAuB,MAAM,WAAW,GAAG;AAC9C;AAAA,MACF;AAIA,aAAO,KAAK;AAAA,QACV,IAAI,MAAM;AAAA,QACV,MAAM,MAAM;AAAA,QACZ;AAAA,MACF,CAAc;AAAA,IAChB;AAGA,UAAM,cAA+C,iBACjD,OAAO;AAAA,MACL,OAAO,QAAQ,cAAc,EAAE,IAAI,CAAC,CAAC,KAAK,KAAK,MAAM;AAAA,QACnD;AAAA,QACA;AAAA,UACE,OAAO,MAAM;AAAA,UACb,YAAY,MAAM;AAAA,UAClB,UAAU;AAAA,YACR,IAAI,MAAM,SAAS;AAAA,YACnB,OAAO,MAAM,SAAS;AAAA,YACtB,SAAS,MAAM,SAAS;AAAA,YACxB,aAAa,MAAM,SAAS;AAAA,UAC9B;AAAA,QACF;AAAA,MACF,CAAC;AAAA,IACH,IACA;AAEJ,WACE,gBAAAA,KAAC,UACE,iBAAO,IAAI,CAAC,UACX,gBAAAA;AAAA,MAAC;AAAA;AAAA,QAEC;AAAA,QACA,UAAU,YAAY,CAAC;AAAA,QACvB,iBAAiB,CAAC;AAAA,QAClB;AAAA;AAAA,MAJK,MAAM;AAAA,IAKb,CACD,GACH;AAAA,EAEJ,SAAS,OAAO;AAEd,YAAQ,MAAM,+BAA+B,IAAI,IAAI,KAAK;AAI1D,UAAM,YACJ,iBAAiB,SAAS,UAAU,QAC/B,MAAuC,MAAM,OAC9C,iBAAiB,SAAS,UAAU,QACjC,MAA2B,OAC5B;AAER,QAAI,cAAc,eAAe,cAAc,SAAS;AACtD,eAAS;AAAA,IACX;AAGA,UAAM;AAAA,EACR;AACF;AAUA,eAAsB,iBAAiB;AAAA,EACrC;AAAA,EACA;AAAA,EACA;AAAA,EACA,WAAW;AACb,GAAiC;AAC/B,QAAM,YAAY,aAAa,iBAAiB;AAChD,QAAM,EAAE,KAAK,IAAI,MAAM;AACvB,QAAM,UAAU,IAAI,KAAK,KAAK,GAAG,CAAC;AAClC,QAAM,OAAO,cAAc,OAAO;AAClC,QAAM,SAAS,aAAa,EAAE,QAAQ,OAAO,CAAC;AAE9C,MAAI;AACF,UAAM,EAAE,MAAM,IAAI,MAAM,OAAO,MAAM,UAAU,MAAM,EAAE,WAAW,KAAK,CAAC;AACxE,WAAO;AAAA,MACL,OAAO,GAAG,MAAM,IAAI;AAAA,MACpB,aAAa,iBAAiB,MAAM,IAAI;AAAA,IAC1C;AAAA,EACF,QAAQ;AACN,WAAO;AAAA,MACL,OAAO;AAAA,MACP,aAAa;AAAA,IACf;AAAA,EACF;AACF;AAEO,SAAS,cAAc,MAAsB;AAClD,MAAI,CAAC,QAAQ,SAAS,KAAK;AACzB,WAAO;AAAA,EACT;AAGA,MAAI,aAAa,KAAK,KAAK;AAG3B,eAAa,WAAW,QAAQ,QAAQ,EAAE;AAG1C,MAAI,CAAC,WAAW,WAAW,GAAG,GAAG;AAC/B,iBAAa,IAAI,UAAU;AAAA,EAC7B;AAGA,eAAa,WAAW,QAAQ,QAAQ,GAAG;AAE3C,SAAO;AACT;","names":["z","z","z","z","z","z","z","z","z","z","jsx"]}
|