cms-renderer 0.6.12 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"sources":["../../lib/cms-api.ts","../../lib/renderer.tsx","../../../../packages/cms-schema/src/blocks/article.ts","../../../../packages/cms-schema/src/blocks/metadata.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","../../../../packages/cms-schema/src/routing/normalize-path.ts","../../../../packages/cms-schema/src/routing/path-validation.ts","../../../../packages/cms-schema/src/documents/schemas/language.ts","../../lib/block-renderer.tsx","../../lib/cms-overlay-script.ts","../../lib/cms-post-message.ts","../../lib/parametric-route.tsx"],"sourcesContent":["/**\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\nconst clientCache = new Map<string, CmsClient>();\n\nfunction getClientCacheKey(options: CmsConfig): string {\n return `${options.cmsUrl}|${options.apiKey ?? ''}|${options.websiteId ?? ''}`;\n}\n\n/**\n * Get a CMS client for the specified CMS URL.\n * Uses a singleton cache to avoid recreating clients with identical configuration.\n */\nexport function getCmsClient(options: CmsConfig): CmsClient {\n const cacheKey = getClientCacheKey(options);\n\n let client = clientCache.get(cacheKey);\n if (!client) {\n client = createCmsClient(options);\n clientCache.set(cacheKey, client);\n }\n\n return client;\n}\n","/**\n * Catch-all Route Handler for Parametric Routes (Next.js)\n *\n * Delegates to `renderParametricRoute` and maps outcomes to Next.js navigation APIs.\n */\n\nimport type { Metadata } from 'next';\nimport { notFound } from 'next/navigation';\nimport {\n getWebsiteId,\n normalizePath,\n type ParametricRouteProps,\n renderParametricRoute,\n} from './parametric-route';\n\ntype PageProps = ParametricRouteProps;\n\nexport default async function ParametricRoutePage(props: PageProps) {\n const result = await renderParametricRoute(props);\n if (result.status === 'not_found') {\n notFound();\n }\n if (result.status === 'error') {\n throw result.error;\n }\n return result.node;\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 // Lazy-load cms-api so importing `renderer` doesn't eagerly pull in env-dependent\n // client construction (helps Next.js bundling + keeps this metadata path lean).\n const { getCmsClient } = await import('./cms-api');\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","/**\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 * Block metadata for template builder UI.\n *\n * Provides display names, descriptions, and categories for each block type.\n */\n\nimport type { BlockSchemaName } from './registry';\n\n/**\n * Block category for filtering in the block library.\n */\nexport type BlockCategory = string;\n\nexport interface BlockFieldMetadata {\n name: string;\n label: string;\n type: string;\n}\n\nexport interface BlockVariantMetadata {\n name: string;\n}\n\n/**\n * Metadata for a block schema.\n */\nexport interface BlockMetadata {\n /** Human-readable name */\n name: string;\n /** Short description */\n description: string;\n /** Category for filtering */\n category: BlockCategory;\n /** Icon identifier (for future use) */\n icon: string;\n /** Field summary used by the builder library */\n fields?: BlockFieldMetadata[];\n /** Variant summary used by the builder library */\n variants?: BlockVariantMetadata[];\n}\n\n/**\n * Metadata registry for all block schemas.\n */\nexport const BLOCK_METADATA: Record<BlockSchemaName, BlockMetadata> = {\n 'hero-block': {\n name: 'Hero',\n description: 'Full-width hero section with headline, subheadline, and CTA',\n category: 'Marketing',\n icon: 'image',\n fields: [\n { name: 'headline', label: 'Headline', type: 'Text' },\n { name: 'subheadline', label: 'Subheadline', type: 'Text' },\n { name: 'ctaText', label: 'CTA Label', type: 'Text' },\n { name: 'ctaUrl', label: 'CTA URL', type: 'URL' },\n { name: 'backgroundImage', label: 'Background Image', type: 'Image' },\n ],\n variants: [{ name: 'Left' }, { name: 'Center' }, { name: 'Right' }],\n },\n 'features-block': {\n name: 'Features',\n description: 'Grid of feature cards with icons and descriptions',\n category: 'Marketing',\n icon: 'grid',\n fields: [\n { name: 'title', label: 'Section Title', type: 'Text' },\n { name: 'subtitle', label: 'Subtitle', type: 'Text' },\n { name: 'features', label: 'Features', type: 'List' },\n { name: 'layout', label: 'Layout', type: 'Select' },\n ],\n variants: [{ name: 'Grid' }, { name: 'List' }, { name: 'Carousel' }],\n },\n 'cta-block': {\n name: 'Call to Action',\n description: 'Conversion-focused section with headline and buttons',\n category: 'Marketing',\n icon: 'pointer',\n fields: [\n { name: 'headline', label: 'Headline', type: 'Text' },\n { name: 'description', label: 'Description', type: 'Text' },\n { name: 'primaryButton', label: 'Primary Button', type: 'Object' },\n { name: 'secondaryButton', label: 'Secondary Button', type: 'Object' },\n ],\n variants: [{ name: 'Single CTA' }, { name: 'Dual CTA' }],\n },\n 'logo-trust-block': {\n name: 'Logo Trust Bar',\n description: 'Row of partner or client logos to build trust',\n category: 'Content',\n icon: 'building',\n fields: [\n { name: 'title', label: 'Title', type: 'Text' },\n { name: 'logos', label: 'Logos', type: 'Image List' },\n ],\n variants: [{ name: 'Compact' }, { name: 'Expanded' }],\n },\n article: {\n name: 'Article',\n description: 'Rich content article with markdown body and metadata',\n category: 'Content',\n icon: 'document-text',\n fields: [\n { name: 'headline', label: 'Headline', type: 'Text' },\n { name: 'author', label: 'Author', type: 'Text' },\n { name: 'publishedAt', label: 'Published At', type: 'Datetime' },\n { name: 'body', label: 'Body', type: 'Rich Text' },\n { name: 'tags', label: 'Tags', type: 'List' },\n { name: 'status', label: 'Status', type: 'Select' },\n ],\n variants: [{ name: 'Editorial' }, { name: 'News' }],\n },\n};\n\nfunction uniqueCategories(): BlockCategory[] {\n return Array.from(new Set(Object.values(BLOCK_METADATA).map((meta) => meta.category))).sort();\n}\n\n/**\n * Block categories with display names and sort order.\n */\nexport const BLOCK_CATEGORIES: { id: BlockCategory; name: string }[] = uniqueCategories().map(\n (category) => ({ id: category, name: category })\n);\n\n/**\n * Get metadata for a block schema.\n */\nexport function getBlockMetadata(schemaName: string): BlockMetadata | undefined {\n return BLOCK_METADATA[schemaName as BlockSchemaName];\n}\n\n/**\n * Get all blocks filtered by category.\n */\nexport function getBlocksByCategory(\n category: BlockCategory | 'All'\n): Array<{ schemaName: BlockSchemaName; metadata: BlockMetadata }> {\n return (Object.entries(BLOCK_METADATA) as Array<[BlockSchemaName, BlockMetadata]>)\n .filter(([, meta]) => category === 'All' || meta.category === category)\n .map(([schemaName, metadata]) => ({ schemaName, metadata }));\n}\n\n/**\n * Get category counts for block library UI.\n */\nexport function getBlockCategoryCounts(): Record<BlockCategory | 'All', number> {\n const counts: Record<BlockCategory | 'All', number> = { All: 0 };\n\n for (const meta of Object.values(BLOCK_METADATA)) {\n counts.All = (counts.All ?? 0) + 1;\n counts[meta.category] = (counts[meta.category] ?? 0) + 1;\n }\n\n return counts;\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.nullish();\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.nullish();\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 * Normalize route paths for consistent storage and matching.\n *\n * Rules:\n * - Always starts with \"/\"\n * - Trailing slashes are removed (except for root \"/\")\n * - Multiple slashes are collapsed to single slash\n */\n\n/**\n * Normalizes a path for storage and matching.\n * - Ensures leading slash\n * - Removes trailing slash (except for root \"/\")\n * - Collapses multiple slashes\n *\n * @param path - The path to normalize\n * @returns Normalized path\n *\n * @example\n * normalizePath(\"/us/en/\") // \"/us/en\"\n * normalizePath(\"us/en\") // \"/us/en\"\n * normalizePath(\"/\") // \"/\"\n * normalizePath(\"//us//en//\") // \"/us/en\"\n */\nexport function normalizePath(path: string): string {\n if (!path || path === '/') {\n return '/';\n }\n\n let normalized = path.trim();\n if (!normalized) {\n return '/';\n }\n if (normalized === '/') {\n return '/';\n }\n\n // Remove trailing slashes (but keep root \"/\") — linear time, no regex (ReDoS-safe)\n let end = normalized.length;\n while (end > 1 && normalized.charCodeAt(end - 1) === 47) {\n end--;\n }\n if (end < normalized.length) {\n normalized = normalized.slice(0, end);\n }\n\n if (!normalized.startsWith('/')) {\n normalized = `/${normalized}`;\n }\n\n // Collapse duplicate slashes — split/join is linear in path length\n const segments = normalized.split('/').filter((s) => s.length > 0);\n if (segments.length === 0) {\n return '/';\n }\n return `/${segments.join('/')}`;\n}\n","import { z } from 'zod';\n\n/**\n * Path validation regex.\n *\n * Rules:\n * - Must start with '/'\n * - Remaining characters may only be: Unicode letters, Unicode numbers, '-', '_', '{', '}', '/', ':'\n *\n * Note:\n * - This enforces allowed characters only. Placeholder structure is validated in `pathSchema`.\n */\nexport const pathRegex = /^\\/[\\p{L}\\p{N}\\-_{}/:]*$/u;\n\n/**\n * Reusable path schema for validation.\n * Enforces both:\n * - Character-level safety (`pathRegex`)\n * - Placeholder segment structure (`:name` or `{name}`), with non-empty names\n */\nconst placeholderSegmentRegex = /^(:[a-zA-Z0-9_]+|\\{[a-zA-Z0-9_]+\\})$/;\n\nfunction hasValidPlaceholderStructure(path: string): boolean {\n if (path === '/') {\n return true;\n }\n\n const segments = path.split('/').filter(Boolean);\n return segments.every((segment) => {\n const hasPlaceholderSyntax =\n segment.includes(':') || segment.includes('{') || segment.includes('}');\n\n if (!hasPlaceholderSyntax) {\n return true;\n }\n\n return placeholderSegmentRegex.test(segment);\n });\n}\n\nexport const pathSchema = z\n .string()\n .min(1, 'Path is required')\n .regex(\n pathRegex,\n 'Path must start with / and contain only letters, numbers, hyphens, underscores, slashes, colons, and braces'\n )\n .refine(\n hasValidPlaceholderStructure,\n 'Path placeholders must be in the form :name or {name} with non-empty names'\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/**\n * Static list of all languages supported by the translation system.\n *\n * This is the authoritative source — both the language documents page and the\n * translation language picker render from this array. The Rust translation\n * service validates incoming language codes against the same set.\n */\nexport const SUPPORTED_LANGUAGES: Language[] = [\n { code: 'ar', name: 'Arabic', nativeName: 'العربية' },\n { code: 'zh', name: 'Chinese (Simplified)', nativeName: '中文(简体)' },\n { code: 'nl', name: 'Dutch', nativeName: 'Nederlands' },\n { code: 'en', name: 'English', nativeName: 'English' },\n { code: 'fr', name: 'French', nativeName: 'Français' },\n { code: 'de', name: 'German', nativeName: 'Deutsch' },\n { code: 'it', name: 'Italian', nativeName: 'Italiano' },\n { code: 'ja', name: 'Japanese', nativeName: '日本語' },\n { code: 'ko', name: 'Korean', nativeName: '한국어' },\n { code: 'pl', name: 'Polish', nativeName: 'Polski' },\n { code: 'pt', name: 'Portuguese', nativeName: 'Português' },\n { code: 'ru', name: 'Russian', nativeName: 'Русский' },\n { code: 'es', name: 'Spanish', nativeName: 'Español' },\n { code: 'tr', name: 'Turkish', nativeName: 'Türkçe' },\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 { generateCmsOverlayScript } from './cms-overlay-script';\nimport { getCmsParentTargetOrigin } from './cms-post-message';\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 inSvg?: boolean;\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 inSvg?: boolean;\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, inSvg: ctx.inSvg })\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 inSvg: ctx.inSvg,\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 // Track SVG context so we never inject <span> inside SVG subtrees\n const nextInSvg = ctx.inSvg || el.type === 'svg';\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 inSvg: nextInSvg,\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 */\nexport function 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// CmsEditableInit — render once per page in edit mode\n// -----------------------------------------------------------------------------\n\n/**\n * Renders the shared CMS edit-mode styles and click-routing script.\n * Place this once at the top of your page when edit_mode is active.\n */\nexport function CmsEditableInit({\n cmsUrl,\n cmsParentOrigin,\n}: {\n cmsUrl?: string;\n /** Admin window origin when proxied (from ?cms_parent_origin=). */\n cmsParentOrigin?: string;\n}) {\n const targetOrigin = getCmsParentTargetOrigin(cmsUrl, cmsParentOrigin) ?? '';\n\n return (\n <script\n // biome-ignore lint/security/noDangerouslySetInnerHtml: Inline script for CMS overlay functionality\n dangerouslySetInnerHTML={{\n __html: generateCmsOverlayScript(targetOrigin),\n }}\n />\n );\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 * The current URL path (e.g. \"/en/test\").\n * When provided, enables path-namespaced component lookup.\n * Registry keys like \"/{lang}/test Article\" will be matched against this path,\n * where `{x}` and `(x)` are treated as single-segment wildcards.\n */\n path?: string;\n}\n\n// -----------------------------------------------------------------------------\n// Path-namespaced component resolution\n// -----------------------------------------------------------------------------\n\n/**\n * Returns true if each segment of `path` matches the corresponding segment in\n * `pattern`, where a pattern segment wrapped in `{…}` or `(…)` is a wildcard.\n */\nexport function pathMatchesPattern(path: string, pattern: string): boolean {\n const pathSegs = path.split('/').filter(Boolean);\n const patternSegs = pattern.split('/').filter(Boolean);\n if (pathSegs.length !== patternSegs.length) return false;\n for (let i = 0; i < patternSegs.length; i++) {\n const seg = patternSegs[i];\n if (!seg) return false;\n if ((seg.startsWith('{') && seg.endsWith('}')) || (seg.startsWith('(') && seg.endsWith(')'))) {\n continue;\n }\n if (seg !== pathSegs[i]) return false;\n }\n return true;\n}\n\n/**\n * Resolves the component for `blockType` from the registry.\n *\n * When `path` is provided, keys of the form `\"/{pattern} BlockType\"` are\n * checked first. The first key whose path pattern matches the current path\n * and whose block-type suffix matches `blockType` wins. Falls back to a\n * direct `registry[blockType]` lookup.\n */\nexport function resolveComponent(\n registry: Partial<BlockComponentRegistry>,\n blockType: string,\n path?: string\n): BlockComponentRegistry[string] | undefined {\n if (path) {\n for (const key of Object.keys(registry)) {\n if (!key.startsWith('/')) continue;\n const spaceIdx = key.indexOf(' ');\n if (spaceIdx === -1) continue;\n const pathPattern = key.slice(0, spaceIdx);\n const registeredType = key.slice(spaceIdx + 1);\n if (registeredType !== blockType) continue;\n if (pathMatchesPattern(path, pathPattern)) {\n return registry[key];\n }\n }\n }\n return registry[blockType];\n}\n\n// -----------------------------------------------------------------------------\n// Component\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 * In editable mode, renders a hidden sentinel before the component. The shared\n * CMS overlay script uses that sentinel to stamp attributes on the component's\n * root element without adding a layout-affecting wrapper.\n */\nexport function BlockRenderer({\n block,\n registry,\n disableEditable,\n routeParams,\n path,\n}: BlockRendererProps) {\n const Component = resolveComponent(registry, block.type, path);\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 // Extract language code from route params if any param is bound to the language schema.\n const language = routeParams\n ? Object.values(routeParams).find((p) => p.schemaName === 'language')?.value\n : undefined;\n\n const component = (\n <Component content={block.content} routeParams={routeParams} language={language} />\n );\n\n if (disableEditable) {\n return component;\n }\n\n return (\n <>\n <span\n data-cms-sentinel=\"\"\n data-block-id={block.id}\n data-block-type={block.type}\n style={{ display: 'none' }}\n aria-hidden=\"true\"\n />\n {component}\n </>\n );\n}\n","/**\n * CMS Overlay Script\n *\n * Framework-agnostic vanilla JS script that handles all CMS edit mode overlays:\n * - Block hover outlines\n * - Cursor indicator\n * - Block toolbar (move up/down, delete)\n *\n * Uses a sentinel-based approach: hidden <span> elements mark block positions,\n * and this script stamps data attributes on the actual block elements (next sibling).\n * This avoids wrapper elements that could break layouts.\n *\n * Compatible with Next.js, TanStack Start, Remix, and other frameworks.\n */\n\nexport function generateCmsOverlayScript(cmsParentOrigin: string): string {\n return `\n(function() {\n if (window.__cmsOverlayInitialized) return;\n window.__cmsOverlayInitialized = true;\n\n var CMS_PARENT_ORIGIN = ${JSON.stringify(cmsParentOrigin)};\n\n var style = document.createElement('style');\n style.setAttribute('data-cms-overlay', '');\n style.textContent = \\`\n [data-cms-block],\n [data-cms-editable] {\n cursor: pointer;\n }\n [data-cms-editable] {\n border-radius: 2px;\n }\n [data-cms-editable]:not([data-cms-block]):hover {\n outline: 2px solid #3b82f6;\n outline-offset: 2px;\n }\n #cms-overlay-root {\n position: fixed;\n top: 0;\n left: 0;\n width: 0;\n height: 0;\n pointer-events: none;\n z-index: 99998;\n }\n #cms-overlay-root > * {\n pointer-events: none;\n }\n .cms-block-outline {\n position: fixed;\n border: 2px solid #3b82f6;\n border-radius: 4px;\n pointer-events: none;\n z-index: 99997;\n transition: all 0.15s ease;\n }\n .cms-block-outline.cms-outline-selected {\n border-color: #2563eb;\n border-width: 3px;\n }\n .cms-block-cursor {\n position: fixed;\n width: 22px;\n height: 22px;\n background: radial-gradient(circle, #fff 5px, #000 5px);\n border-radius: 50%;\n pointer-events: none;\n z-index: 99999;\n transform: translate(-50%, -50%);\n opacity: 0;\n transition: opacity 0.1s ease;\n }\n .cms-block-cursor.cms-cursor-visible {\n opacity: 1;\n }\n .cms-block-toolbar {\n position: fixed;\n display: flex;\n gap: 4px;\n background: #1f2937;\n border-radius: 6px;\n padding: 4px;\n box-shadow: 0 4px 12px rgba(0,0,0,0.25);\n z-index: 99999;\n pointer-events: none;\n opacity: 0;\n transform: scale(0.9) translateY(4px);\n transition: opacity 0.15s ease, transform 0.15s ease;\n }\n .cms-block-toolbar.cms-toolbar-visible {\n opacity: 1;\n transform: scale(1) translateY(0);\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 \\`;\n document.head.appendChild(style);\n\n function stampBlockElements() {\n var sentinels = document.querySelectorAll('[data-cms-sentinel]');\n sentinels.forEach(function(sentinel) {\n var blockEl = sentinel.nextElementSibling;\n if (!blockEl) return;\n\n var blockId = sentinel.getAttribute('data-block-id');\n var blockType = sentinel.getAttribute('data-block-type');\n\n blockEl.setAttribute('data-cms-block', '');\n blockEl.setAttribute('data-block-id', blockId);\n blockEl.setAttribute('data-block-type', blockType);\n });\n }\n\n stampBlockElements();\n\n var stampObserver = new MutationObserver(function(mutations) {\n var needsStamp = mutations.some(function(m) {\n return m.addedNodes.length > 0;\n });\n if (needsStamp) stampBlockElements();\n });\n stampObserver.observe(document.body, { childList: true, subtree: true });\n\n var overlayRoot = document.createElement('div');\n overlayRoot.id = 'cms-overlay-root';\n document.body.appendChild(overlayRoot);\n\n var cursor = document.createElement('div');\n cursor.className = 'cms-block-cursor';\n overlayRoot.appendChild(cursor);\n\n var outline = document.createElement('div');\n outline.className = 'cms-block-outline';\n outline.style.display = 'none';\n overlayRoot.appendChild(outline);\n\n var selectedOutline = document.createElement('div');\n selectedOutline.className = 'cms-block-outline cms-outline-selected';\n selectedOutline.style.display = 'none';\n overlayRoot.appendChild(selectedOutline);\n\n var toolbar = document.createElement('div');\n toolbar.className = 'cms-block-toolbar';\n toolbar.setAttribute('data-cms-toolbar', '');\n toolbar.innerHTML = \\`\n <button class=\"move-up\" title=\"Move up\">\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M18 15l-6-6-6 6\"/>\n </svg>\n </button>\n <button class=\"move-down\" title=\"Move down\">\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M6 9l6 6 6-6\"/>\n </svg>\n </button>\n <button class=\"delete\" title=\"Delete\">\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16\"/>\n </svg>\n </button>\n \\`;\n overlayRoot.appendChild(toolbar);\n\n var currentBlockId = null;\n var toolbarVisible = false;\n var selectedBlockId = null;\n\n function decoratePreviewUrl(rawHref) {\n if (!rawHref) return rawHref;\n try {\n var url = new URL(rawHref, window.location.href);\n if (url.origin !== window.location.origin) return rawHref;\n url.searchParams.set('edit_mode', 'true');\n if (CMS_PARENT_ORIGIN) {\n url.searchParams.set('cms_parent_origin', CMS_PARENT_ORIGIN);\n }\n return url.toString();\n } catch (_) {\n return rawHref;\n }\n }\n\n function preserveEditParamsOnNavigation(e) {\n if (e.defaultPrevented || e.button !== 0 || e.metaKey || e.ctrlKey || e.shiftKey || e.altKey) {\n return;\n }\n\n var anchor = e.target.closest('a[href]');\n if (!anchor || (anchor.target && anchor.target !== '_self') || anchor.hasAttribute('download')) {\n return;\n }\n\n var href = anchor.getAttribute('href');\n var decorated = decoratePreviewUrl(href);\n if (decorated && decorated !== href) {\n anchor.setAttribute('href', decorated);\n }\n }\n\n function postToParent(message) {\n if (!CMS_PARENT_ORIGIN || !window.parent || window.parent === window) {\n return;\n }\n window.parent.postMessage(message, CMS_PARENT_ORIGIN);\n }\n\n function sendReadySignal() {\n postToParent({ type: 'cms-preview-ready' });\n }\n sendReadySignal();\n // The preview iframe can execute before the admin listener is attached.\n setTimeout(sendReadySignal, 500);\n setTimeout(sendReadySignal, 1500);\n\n function getBlockElement(blockId) {\n return document.querySelector('[data-block-id=\"' + blockId + '\"]');\n }\n\n function getAllBlocks() {\n return Array.from(document.querySelectorAll('[data-cms-block]'));\n }\n\n function getBlockIndex(blockId) {\n var blocks = getAllBlocks();\n for (var i = 0; i < blocks.length; i++) {\n if (blocks[i].getAttribute('data-block-id') === blockId) return i;\n }\n return -1;\n }\n\n function updateOutline(el, outlineEl) {\n if (!el) {\n outlineEl.style.display = 'none';\n return;\n }\n var rect = el.getBoundingClientRect();\n outlineEl.style.display = 'block';\n outlineEl.style.top = rect.top + 'px';\n outlineEl.style.left = rect.left + 'px';\n outlineEl.style.width = rect.width + 'px';\n outlineEl.style.height = rect.height + 'px';\n }\n\n function positionToolbar(x, y) {\n var rect = toolbar.getBoundingClientRect();\n var top = Math.max(4, y - rect.height - 12);\n var left = Math.max(4, Math.min(x - rect.width / 2, window.innerWidth - rect.width - 4));\n toolbar.style.top = top + 'px';\n toolbar.style.left = left + 'px';\n }\n\n function showToolbar(x, y, blockId) {\n currentBlockId = blockId;\n toolbarVisible = true;\n positionToolbar(x, y);\n toolbar.classList.add('cms-toolbar-visible');\n\n var index = getBlockIndex(blockId);\n var total = getAllBlocks().length;\n toolbar.querySelector('.move-up').disabled = index <= 0;\n toolbar.querySelector('.move-down').disabled = index >= total - 1;\n }\n\n function hideToolbar() {\n toolbarVisible = false;\n toolbar.classList.remove('cms-toolbar-visible');\n }\n\n function showCursor(x, y) {\n cursor.style.top = y + 'px';\n cursor.style.left = x + 'px';\n cursor.classList.add('cms-cursor-visible');\n }\n\n function hideCursor() {\n cursor.classList.remove('cms-cursor-visible');\n }\n\n document.addEventListener('mouseover', function(e) {\n if (toolbarVisible) return;\n var block = e.target.closest('[data-cms-block]');\n if (block) {\n updateOutline(block, outline);\n }\n });\n\n document.addEventListener('mouseout', function(e) {\n var block = e.target.closest('[data-cms-block]');\n var related = e.relatedTarget ? e.relatedTarget.closest('[data-cms-block]') : null;\n if (block && block !== related) {\n outline.style.display = 'none';\n hideCursor();\n }\n });\n\n document.addEventListener('mousemove', function(e) {\n if (toolbarVisible) return;\n var block = e.target.closest('[data-cms-block]');\n if (block) {\n showCursor(e.clientX, e.clientY);\n }\n });\n\n document.addEventListener('contextmenu', function(e) {\n if (e.target.closest('[data-cms-toolbar]')) return;\n var block = e.target.closest('[data-cms-block]');\n if (block) {\n if (toolbarVisible) return;\n e.preventDefault();\n hideCursor();\n outline.style.display = 'none';\n var blockId = block.getAttribute('data-block-id');\n showToolbar(e.clientX, e.clientY, blockId);\n }\n });\n\n document.addEventListener('click', function(e) {\n preserveEditParamsOnNavigation(e);\n\n if (toolbarVisible && !e.target.closest('[data-cms-toolbar]')) {\n var block = e.target.closest('[data-cms-block]');\n if (!block || block.getAttribute('data-block-id') !== currentBlockId) {\n hideToolbar();\n }\n }\n\n if (e.target.closest('[data-cms-toolbar]')) return;\n\n var editable = e.target.closest('[data-cms-editable]');\n if (editable) {\n postToParent({\n type: 'cms-editable-click',\n blockId: editable.getAttribute('data-block-id'),\n blockType: editable.getAttribute('data-block-type'),\n contentPath: editable.getAttribute('data-content-path')\n });\n return;\n }\n\n var block = e.target.closest('[data-cms-block]');\n if (block) {\n postToParent({\n type: 'cms-editable-click',\n blockId: block.getAttribute('data-block-id'),\n blockType: block.getAttribute('data-block-type'),\n contentPath: null\n });\n }\n }, true);\n\n toolbar.querySelector('.move-up').addEventListener('click', function() {\n if (!currentBlockId) return;\n postToParent({ type: 'cms-block-action', action: 'move-up', blockId: currentBlockId });\n hideToolbar();\n });\n\n toolbar.querySelector('.move-down').addEventListener('click', function() {\n if (!currentBlockId) return;\n postToParent({ type: 'cms-block-action', action: 'move-down', blockId: currentBlockId });\n hideToolbar();\n });\n\n toolbar.querySelector('.delete').addEventListener('click', function() {\n if (!currentBlockId) return;\n postToParent({ type: 'cms-block-action', action: 'delete', blockId: currentBlockId });\n hideToolbar();\n });\n\n window.addEventListener('scroll', function() {\n hideCursor();\n hideToolbar();\n if (selectedBlockId) {\n var el = getBlockElement(selectedBlockId);\n updateOutline(el, selectedOutline);\n }\n }, { passive: true, capture: true });\n\n window.addEventListener('resize', function() {\n if (selectedBlockId) {\n var el = getBlockElement(selectedBlockId);\n updateOutline(el, selectedOutline);\n }\n }, { passive: true });\n\n window.addEventListener('message', function(e) {\n if (CMS_PARENT_ORIGIN && e.origin !== CMS_PARENT_ORIGIN) return;\n if (e.source !== window.parent) return;\n\n if (e.data && e.data.type === 'cms-select-block') {\n selectedBlockId = e.data.blockId || null;\n if (selectedBlockId) {\n var el = getBlockElement(selectedBlockId);\n updateOutline(el, selectedOutline);\n } else {\n selectedOutline.style.display = 'none';\n }\n }\n });\n})();\n`;\n}\n","/**\n * Trusted-origin helpers for CMS template-builder postMessage traffic\n * between the admin UI (parent) and the site preview iframe (child).\n */\n\nexport const CMS_PARENT_ORIGIN_PARAM = 'cms_parent_origin';\n\nexport function parseOrigin(url: string | undefined): string | null {\n if (!url) return null;\n try {\n return new URL(url).origin;\n } catch {\n return null;\n }\n}\n\nfunction getParentOriginFromQueryParam(): string | null {\n if (typeof window === 'undefined') return null;\n const value = new URLSearchParams(window.location.search).get(CMS_PARENT_ORIGIN_PARAM);\n return parseOrigin(value ?? undefined);\n}\n\nfunction getReferrerOrigin(): string | null {\n if (typeof document === 'undefined' || !document.referrer) return null;\n return parseOrigin(document.referrer);\n}\n\n/** Same-origin embed (e.g. proxied admin + preview on the customer domain). */\nfunction getSameOriginParentOrigin(): string | null {\n if (typeof window === 'undefined' || !window.parent || window.parent === window) {\n return null;\n }\n try {\n return window.parent.location.origin;\n } catch {\n return null;\n }\n}\n\n/** Origins allowed to send messages to the preview iframe (CMS admin hosts). */\nexport function getAllowedCmsParentOrigins(explicitParentOrigin?: string): string[] {\n const origins = new Set<string>();\n const apiOrigin = parseOrigin(process.env.NEXT_PUBLIC_CMS_API_URL);\n if (apiOrigin) origins.add(apiOrigin);\n\n const fromQuery = parseOrigin(explicitParentOrigin) ?? getParentOriginFromQueryParam();\n if (fromQuery) origins.add(fromQuery);\n\n const referrerOrigin = getReferrerOrigin();\n if (referrerOrigin) origins.add(referrerOrigin);\n\n const sameOriginParent = getSameOriginParentOrigin();\n if (sameOriginParent) origins.add(sameOriginParent);\n\n return [...origins];\n}\n\n/**\n * Target origin when posting from the preview iframe to the CMS parent.\n *\n * When the admin is proxied on a customer domain, the parent window origin is\n * the customer site — not NEXT_PUBLIC_CMS_API_URL. Prefer explicit/referrer/\n * same-origin parent detection over the CMS API URL.\n */\nexport function getCmsParentTargetOrigin(\n preferredCmsUrl?: string,\n explicitParentOrigin?: string\n): string | null {\n const fromExplicit = parseOrigin(explicitParentOrigin) ?? getParentOriginFromQueryParam();\n if (fromExplicit) return fromExplicit;\n\n const referrerOrigin = getReferrerOrigin();\n if (referrerOrigin) return referrerOrigin;\n\n const sameOriginParent = getSameOriginParentOrigin();\n if (sameOriginParent) return sameOriginParent;\n\n const preferredOrigin = parseOrigin(preferredCmsUrl);\n if (preferredOrigin) return preferredOrigin;\n\n const allowed = getAllowedCmsParentOrigins(explicitParentOrigin);\n return allowed[0] ?? null;\n}\n\nexport function isTrustedCmsParentMessage(\n event: MessageEvent,\n explicitParentOrigin?: string\n): boolean {\n const allowed = getAllowedCmsParentOrigins(explicitParentOrigin);\n if (allowed.length === 0) return false;\n if (!allowed.includes(event.origin)) return false;\n return event.source === window.parent;\n}\n\nexport function postMessageToCmsParent(\n message: unknown,\n options?: { preferredCmsUrl?: string; explicitParentOrigin?: string }\n): void {\n if (typeof window === 'undefined' || !window.parent || window.parent === window) return;\n const targetOrigin = getCmsParentTargetOrigin(\n options?.preferredCmsUrl,\n options?.explicitParentOrigin\n );\n if (!targetOrigin) return;\n window.parent.postMessage(message, targetOrigin);\n}\n","/**\n * Framework-agnostic parametric route rendering for Profound CMS.\n * Next.js and TanStack Start call `renderParametricRoute` and map `not_found`\n * to their respective 404 mechanisms.\n */\n\nimport {\n isArticlePublished,\n isValidBlockSchemaName,\n normalizeArticleContent,\n} from '@repo/cms-schema/blocks';\nimport type { GeneratedBlockRow } from '@repo/cms-schema/db';\nimport { normalizePath } from '@repo/cms-schema/routing';\nimport type { ReactNode } from 'react';\nimport { BlockRenderer, CmsEditableInit } from './block-renderer';\nimport { type CmsConfig, getCmsClient } from './cms-api';\nimport type { BlockComponentRegistry, BlockData, ResolvedRouteParams } from './types';\n\nexport type ParametricRouteProps = CmsConfig & {\n params: Promise<{ slug: string[] }> | { slug: string[] };\n searchParams?:\n | Promise<{ [key: string]: string | string[] | boolean | undefined }>\n | { [key: string]: string | string[] | boolean | undefined };\n registry?: Partial<BlockComponentRegistry>;\n};\n\nexport type ParametricRouteResult =\n | { status: 'ok'; node: ReactNode }\n | { status: 'not_found' }\n | { status: 'error'; error: unknown };\n\n/** @internal Exported for generateMetadata in renderer.tsx */\nexport function 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 * Renders CMS-backed blocks for a multi-segment path. Maps NOT_FOUND-style API\n * outcomes to `{ status: 'not_found' }` instead of throwing.\n */\nexport async function renderParametricRoute({\n params,\n searchParams,\n registry,\n apiKey,\n cmsUrl,\n websiteId: providedWebsiteId,\n}: ParametricRouteProps): Promise<ParametricRouteResult> {\n const websiteId = getWebsiteId(providedWebsiteId);\n // Support both Promise (Next.js App Router) and plain object (TanStack Start) patterns\n const { slug } = 'then' in params ? await params : params;\n const resolvedSearchParams =\n searchParams && 'then' in searchParams ? await searchParams : searchParams;\n\n let aiPreviewIndex: number | null = null;\n const aiPreviewParam = resolvedSearchParams?.ai_preview;\n if (aiPreviewParam && typeof aiPreviewParam !== 'boolean') {\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 const editModeParam = resolvedSearchParams?.edit_mode;\n // Support both string (Next.js) and boolean (TanStack Start) values\n const editMode = editModeParam === true || editModeParam === 'true' || editModeParam === '1';\n\n const cmsParentOriginParam = resolvedSearchParams?.cms_parent_origin;\n const cmsParentOrigin =\n typeof cmsParentOriginParam === 'boolean'\n ? undefined\n : Array.isArray(cmsParentOriginParam)\n ? cmsParentOriginParam[0]\n : cmsParentOriginParam;\n\n const rawPath = `/${slug.join('/')}`;\n const path = normalizePath(rawPath);\n\n if (/\\.[a-zA-Z0-9]+$/.test(path)) {\n return { status: 'not_found' };\n }\n\n const client = getCmsClient({ apiKey, cmsUrl });\n\n try {\n const { route, resolvedParams } = await client.route.getByPath.query({\n websiteId,\n path,\n });\n\n if (route.state !== 'Live') {\n return { status: 'not_found' };\n }\n\n type GeneratedBlockData = Pick<\n GeneratedBlockRow,\n 'id' | 'generated_content' | 'generated_layout' | 'schema_name' | 'status'\n >;\n\n // For parametric routes with CEL expressions, use getWithCelCache to get\n // blocks with cached CEL values merged in. This enables implicit bindings\n // where 10,000+ documents automatically get correctly evaluated values.\n const hasResolvedParams = resolvedParams && Object.keys(resolvedParams).length > 0;\n\n const blockResultsPromise = hasResolvedParams\n ? client.block.getWithCelCache\n .query({\n websiteId,\n blockIds: route.block_ids,\n routePath: route.path,\n params: Object.fromEntries(\n Object.entries(resolvedParams).map(([key, param]) => [key, param.value])\n ),\n })\n .then((result) => result.blocks)\n .catch((error) => {\n console.error('Failed to fetch blocks with CEL cache:', error);\n // Fall back to regular block fetch\n return client.block.getByIds\n .query({ websiteId, ids: route.block_ids })\n .catch(() => [] as Awaited<ReturnType<typeof client.block.getByIds.query>>);\n })\n : client.block.getByIds.query({ websiteId, ids: route.block_ids }).catch((error) => {\n console.error('Failed to fetch blocks:', error);\n return [] as Awaited<ReturnType<typeof client.block.getByIds.query>>;\n });\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 blockResultsPromise,\n generatedBlocksPromise,\n ]);\n\n const blocks: BlockData[] = [];\n\n for (const block of blockResults) {\n if (!block || block.published_content === null) continue;\n\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;\n const variants = generatedBlock?.generated_content;\n if (variants && Array.isArray(variants) && variants[variantIndex]) {\n content = variants[variantIndex].content;\n }\n }\n content = content ?? (block.published_content as Record<string, unknown> | null);\n if (!content) continue;\n\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 if (block.schema_id) {\n blocks.push({ id: block.id, type: block.schema_name, content });\n continue;\n }\n\n if (!isValidBlockSchemaName(block.schema_name)) {\n continue;\n }\n\n blocks.push({\n id: block.id,\n type: block.schema_name,\n content,\n } as BlockData);\n }\n\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 status: 'ok',\n node: (\n <main>\n {editMode && <CmsEditableInit cmsUrl={cmsUrl} cmsParentOrigin={cmsParentOrigin} />}\n {blocks.map((block) => (\n <BlockRenderer\n key={block.id}\n block={block}\n registry={registry ?? {}}\n disableEditable={!editMode}\n routeParams={routeParams}\n path={path}\n />\n ))}\n </main>\n ),\n };\n } catch (error) {\n console.error(`Route fetch error for path: ${path}`, error);\n\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' || errorCode === 'BAD_REQUEST') {\n return { status: 'not_found' };\n }\n\n return { status: 'error', error };\n }\n}\n\nexport { normalizePath };\n"],"mappings":"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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;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;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;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;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","names":["z","z","z","z","z","z","z","z","z","z","z","z","jsx","jsxs","getCmsClient"]}
1
+ {"version":3,"sources":["../../lib/cms-api.ts","../../lib/renderer.tsx","../../../../packages/cms-schema/src/blocks/article.ts","../../../../packages/cms-schema/src/blocks/metadata.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","../../../../packages/cms-schema/src/routing/normalize-path.ts","../../../../packages/cms-schema/src/routing/path-validation.ts","../../../../packages/cms-schema/src/documents/schemas/language.ts","../../lib/block-renderer.tsx","../../lib/cms-overlay-script.ts","../../lib/cms-post-message.ts","../../lib/parametric-route.tsx"],"sourcesContent":["/**\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\nconst clientCache = new Map<string, CmsClient>();\n\nfunction getClientCacheKey(options: CmsConfig): string {\n return `${options.cmsUrl}|${options.apiKey ?? ''}|${options.websiteId ?? ''}`;\n}\n\n/**\n * Get a CMS client for the specified CMS URL.\n * Uses a singleton cache to avoid recreating clients with identical configuration.\n */\nexport function getCmsClient(options: CmsConfig): CmsClient {\n const cacheKey = getClientCacheKey(options);\n\n let client = clientCache.get(cacheKey);\n if (!client) {\n client = createCmsClient(options);\n clientCache.set(cacheKey, client);\n }\n\n return client;\n}\n","/**\n * Catch-all Route Handler for Parametric Routes (Next.js)\n *\n * Delegates to `renderParametricRoute` and maps outcomes to Next.js navigation APIs.\n */\n\nimport type { Metadata } from 'next';\nimport { notFound } from 'next/navigation';\nimport {\n getWebsiteId,\n normalizePath,\n type ParametricRouteProps,\n renderParametricRoute,\n} from './parametric-route';\n\ntype PageProps = ParametricRouteProps;\n\n/**\n * Production page: renders published content with no edit overlays or hovers.\n */\nexport default async function ParametricRoutePage(props: PageProps) {\n const result = await renderParametricRoute(props);\n if (result.status === 'not_found') {\n notFound();\n }\n if (result.status === 'error') {\n throw result.error;\n }\n return result.node;\n}\n\n/**\n * Preview page: mounted at the dedicated `/cms-preview_` routes by the consuming\n * app. Forces edit-mode overlays/hovers and renders draft content. `preview` is\n * passed here rather than via props so production routes can never enable it.\n */\nexport async function ParametricRoutePreviewPage(props: PageProps) {\n const result = await renderParametricRoute(props, { preview: true });\n if (result.status === 'not_found') {\n notFound();\n }\n if (result.status === 'error') {\n throw result.error;\n }\n return result.node;\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 // Lazy-load cms-api so importing `renderer` doesn't eagerly pull in env-dependent\n // client construction (helps Next.js bundling + keeps this metadata path lean).\n const { getCmsClient } = await import('./cms-api');\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","/**\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 * Block metadata for template builder UI.\n *\n * Provides display names, descriptions, and categories for each block type.\n */\n\nimport type { BlockSchemaName } from './registry';\n\n/**\n * Block category for filtering in the block library.\n */\nexport type BlockCategory = string;\n\nexport interface BlockFieldMetadata {\n name: string;\n label: string;\n type: string;\n}\n\nexport interface BlockVariantMetadata {\n name: string;\n}\n\n/**\n * Metadata for a block schema.\n */\nexport interface BlockMetadata {\n /** Human-readable name */\n name: string;\n /** Short description */\n description: string;\n /** Category for filtering */\n category: BlockCategory;\n /** Icon identifier (for future use) */\n icon: string;\n /** Field summary used by the builder library */\n fields?: BlockFieldMetadata[];\n /** Variant summary used by the builder library */\n variants?: BlockVariantMetadata[];\n}\n\n/**\n * Metadata registry for all block schemas.\n */\nexport const BLOCK_METADATA: Record<BlockSchemaName, BlockMetadata> = {\n 'hero-block': {\n name: 'Hero',\n description: 'Full-width hero section with headline, subheadline, and CTA',\n category: 'Marketing',\n icon: 'image',\n fields: [\n { name: 'headline', label: 'Headline', type: 'Text' },\n { name: 'subheadline', label: 'Subheadline', type: 'Text' },\n { name: 'ctaText', label: 'CTA Label', type: 'Text' },\n { name: 'ctaUrl', label: 'CTA URL', type: 'URL' },\n { name: 'backgroundImage', label: 'Background Image', type: 'Image' },\n ],\n variants: [{ name: 'Left' }, { name: 'Center' }, { name: 'Right' }],\n },\n 'features-block': {\n name: 'Features',\n description: 'Grid of feature cards with icons and descriptions',\n category: 'Marketing',\n icon: 'grid',\n fields: [\n { name: 'title', label: 'Section Title', type: 'Text' },\n { name: 'subtitle', label: 'Subtitle', type: 'Text' },\n { name: 'features', label: 'Features', type: 'List' },\n { name: 'layout', label: 'Layout', type: 'Select' },\n ],\n variants: [{ name: 'Grid' }, { name: 'List' }, { name: 'Carousel' }],\n },\n 'cta-block': {\n name: 'Call to Action',\n description: 'Conversion-focused section with headline and buttons',\n category: 'Marketing',\n icon: 'pointer',\n fields: [\n { name: 'headline', label: 'Headline', type: 'Text' },\n { name: 'description', label: 'Description', type: 'Text' },\n { name: 'primaryButton', label: 'Primary Button', type: 'Object' },\n { name: 'secondaryButton', label: 'Secondary Button', type: 'Object' },\n ],\n variants: [{ name: 'Single CTA' }, { name: 'Dual CTA' }],\n },\n 'logo-trust-block': {\n name: 'Logo Trust Bar',\n description: 'Row of partner or client logos to build trust',\n category: 'Content',\n icon: 'building',\n fields: [\n { name: 'title', label: 'Title', type: 'Text' },\n { name: 'logos', label: 'Logos', type: 'Image List' },\n ],\n variants: [{ name: 'Compact' }, { name: 'Expanded' }],\n },\n article: {\n name: 'Article',\n description: 'Rich content article with markdown body and metadata',\n category: 'Content',\n icon: 'document-text',\n fields: [\n { name: 'headline', label: 'Headline', type: 'Text' },\n { name: 'author', label: 'Author', type: 'Text' },\n { name: 'publishedAt', label: 'Published At', type: 'Datetime' },\n { name: 'body', label: 'Body', type: 'Rich Text' },\n { name: 'tags', label: 'Tags', type: 'List' },\n { name: 'status', label: 'Status', type: 'Select' },\n ],\n variants: [{ name: 'Editorial' }, { name: 'News' }],\n },\n};\n\nfunction uniqueCategories(): BlockCategory[] {\n return Array.from(new Set(Object.values(BLOCK_METADATA).map((meta) => meta.category))).sort();\n}\n\n/**\n * Block categories with display names and sort order.\n */\nexport const BLOCK_CATEGORIES: { id: BlockCategory; name: string }[] = uniqueCategories().map(\n (category) => ({ id: category, name: category })\n);\n\n/**\n * Get metadata for a block schema.\n */\nexport function getBlockMetadata(schemaName: string): BlockMetadata | undefined {\n return BLOCK_METADATA[schemaName as BlockSchemaName];\n}\n\n/**\n * Get all blocks filtered by category.\n */\nexport function getBlocksByCategory(\n category: BlockCategory | 'All'\n): Array<{ schemaName: BlockSchemaName; metadata: BlockMetadata }> {\n return (Object.entries(BLOCK_METADATA) as Array<[BlockSchemaName, BlockMetadata]>)\n .filter(([, meta]) => category === 'All' || meta.category === category)\n .map(([schemaName, metadata]) => ({ schemaName, metadata }));\n}\n\n/**\n * Get category counts for block library UI.\n */\nexport function getBlockCategoryCounts(): Record<BlockCategory | 'All', number> {\n const counts: Record<BlockCategory | 'All', number> = { All: 0 };\n\n for (const meta of Object.values(BLOCK_METADATA)) {\n counts.All = (counts.All ?? 0) + 1;\n counts[meta.category] = (counts[meta.category] ?? 0) + 1;\n }\n\n return counts;\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.nullish();\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.nullish();\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 * Normalize route paths for consistent storage and matching.\n *\n * Rules:\n * - Always starts with \"/\"\n * - Trailing slashes are removed (except for root \"/\")\n * - Multiple slashes are collapsed to single slash\n */\n\n/**\n * Normalizes a path for storage and matching.\n * - Ensures leading slash\n * - Removes trailing slash (except for root \"/\")\n * - Collapses multiple slashes\n *\n * @param path - The path to normalize\n * @returns Normalized path\n *\n * @example\n * normalizePath(\"/us/en/\") // \"/us/en\"\n * normalizePath(\"us/en\") // \"/us/en\"\n * normalizePath(\"/\") // \"/\"\n * normalizePath(\"//us//en//\") // \"/us/en\"\n */\nexport function normalizePath(path: string): string {\n if (!path || path === '/') {\n return '/';\n }\n\n let normalized = path.trim();\n if (!normalized) {\n return '/';\n }\n if (normalized === '/') {\n return '/';\n }\n\n // Remove trailing slashes (but keep root \"/\") — linear time, no regex (ReDoS-safe)\n let end = normalized.length;\n while (end > 1 && normalized.charCodeAt(end - 1) === 47) {\n end--;\n }\n if (end < normalized.length) {\n normalized = normalized.slice(0, end);\n }\n\n if (!normalized.startsWith('/')) {\n normalized = `/${normalized}`;\n }\n\n // Collapse duplicate slashes — split/join is linear in path length\n const segments = normalized.split('/').filter((s) => s.length > 0);\n if (segments.length === 0) {\n return '/';\n }\n return `/${segments.join('/')}`;\n}\n","import { z } from 'zod';\n\n/**\n * Path validation regex.\n *\n * Rules:\n * - Must start with '/'\n * - Remaining characters may only be: Unicode letters, Unicode numbers, '-', '_', '{', '}', '/', ':'\n *\n * Note:\n * - This enforces allowed characters only. Placeholder structure is validated in `pathSchema`.\n */\nexport const pathRegex = /^\\/[\\p{L}\\p{N}\\-_{}/:]*$/u;\n\n/**\n * Reusable path schema for validation.\n * Enforces both:\n * - Character-level safety (`pathRegex`)\n * - Placeholder segment structure (`:name` or `{name}`), with non-empty names\n */\nconst placeholderSegmentRegex = /^(:[a-zA-Z0-9_]+|\\{[a-zA-Z0-9_]+\\})$/;\n\nfunction hasValidPlaceholderStructure(path: string): boolean {\n if (path === '/') {\n return true;\n }\n\n const segments = path.split('/').filter(Boolean);\n return segments.every((segment) => {\n const hasPlaceholderSyntax =\n segment.includes(':') || segment.includes('{') || segment.includes('}');\n\n if (!hasPlaceholderSyntax) {\n return true;\n }\n\n return placeholderSegmentRegex.test(segment);\n });\n}\n\nexport const pathSchema = z\n .string()\n .min(1, 'Path is required')\n .regex(\n pathRegex,\n 'Path must start with / and contain only letters, numbers, hyphens, underscores, slashes, colons, and braces'\n )\n .refine(\n hasValidPlaceholderStructure,\n 'Path placeholders must be in the form :name or {name} with non-empty names'\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/**\n * Static list of all languages supported by the translation system.\n *\n * This is the authoritative source — both the language documents page and the\n * translation language picker render from this array. The Rust translation\n * service validates incoming language codes against the same set.\n */\nexport const SUPPORTED_LANGUAGES: Language[] = [\n { code: 'ar', name: 'Arabic', nativeName: 'العربية' },\n { code: 'bg', name: 'Bulgarian', nativeName: 'Български' },\n { code: 'zh', name: 'Chinese (Simplified)', nativeName: '中文(简体)' },\n { code: 'hr', name: 'Croatian', nativeName: 'Hrvatski' },\n { code: 'cs', name: 'Czech', nativeName: 'Čeština' },\n { code: 'da', name: 'Danish', nativeName: 'Dansk' },\n { code: 'nl', name: 'Dutch', nativeName: 'Nederlands' },\n { code: 'en', name: 'English', nativeName: 'English' },\n { code: 'et', name: 'Estonian', nativeName: 'Eesti' },\n { code: 'fi', name: 'Finnish', nativeName: 'Suomi' },\n { code: 'fr', name: 'French', nativeName: 'Français' },\n { code: 'de', name: 'German', nativeName: 'Deutsch' },\n { code: 'el', name: 'Greek', nativeName: 'Ελληνικά' },\n { code: 'he', name: 'Hebrew', nativeName: 'עברית' },\n { code: 'hi', name: 'Hindi', nativeName: 'हिन्दी' },\n { code: 'hu', name: 'Hungarian', nativeName: 'Magyar' },\n { code: 'id', name: 'Indonesian', nativeName: 'Bahasa Indonesia' },\n { code: 'it', name: 'Italian', nativeName: 'Italiano' },\n { code: 'ja', name: 'Japanese', nativeName: '日本語' },\n { code: 'ko', name: 'Korean', nativeName: '한국어' },\n { code: 'lv', name: 'Latvian', nativeName: 'Latviešu' },\n { code: 'lt', name: 'Lithuanian', nativeName: 'Lietuvių' },\n { code: 'no', name: 'Norwegian', nativeName: 'Norsk' },\n { code: 'pl', name: 'Polish', nativeName: 'Polski' },\n { code: 'pt', name: 'Portuguese', nativeName: 'Português' },\n { code: 'ro', name: 'Romanian', nativeName: 'Română' },\n { code: 'ru', name: 'Russian', nativeName: 'Русский' },\n { code: 'sk', name: 'Slovak', nativeName: 'Slovenčina' },\n { code: 'sl', name: 'Slovenian', nativeName: 'Slovenščina' },\n { code: 'es', name: 'Spanish', nativeName: 'Español' },\n { code: 'sv', name: 'Swedish', nativeName: 'Svenska' },\n { code: 'th', name: 'Thai', nativeName: 'ไทย' },\n { code: 'tr', name: 'Turkish', nativeName: 'Türkçe' },\n { code: 'uk', name: 'Ukrainian', nativeName: 'Українська' },\n { code: 'vi', name: 'Vietnamese', nativeName: 'Tiếng Việt' },\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 { generateCmsOverlayScript } from './cms-overlay-script';\nimport { getCmsParentTargetOrigin } from './cms-post-message';\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 inSvg?: boolean;\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 inSvg?: boolean;\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, inSvg: ctx.inSvg })\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 inSvg: ctx.inSvg,\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 // Track SVG context so we never inject <span> inside SVG subtrees\n const nextInSvg = ctx.inSvg || el.type === 'svg';\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 inSvg: nextInSvg,\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 */\nexport function 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// CmsEditableInit — render once per page in edit mode\n// -----------------------------------------------------------------------------\n\n/**\n * Renders the shared CMS edit-mode styles and click-routing script.\n * Place this once at the top of your page when edit_mode is active.\n */\nexport function CmsEditableInit({\n cmsUrl,\n cmsParentOrigin,\n}: {\n cmsUrl?: string;\n /** Admin window origin when proxied (from ?cms_parent_origin=). */\n cmsParentOrigin?: string;\n}) {\n const targetOrigin = getCmsParentTargetOrigin(cmsUrl, cmsParentOrigin) ?? '';\n\n return (\n <script\n // biome-ignore lint/security/noDangerouslySetInnerHtml: Inline script for CMS overlay functionality\n dangerouslySetInnerHTML={{\n __html: generateCmsOverlayScript(targetOrigin),\n }}\n />\n );\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 * If true, wraps matched text nodes in contentEditable CMS spans.\n */\n enableContentEditable?: 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 * The current URL path (e.g. \"/en/test\").\n * When provided, enables path-namespaced component lookup.\n * Registry keys like \"/{lang}/test Article\" will be matched against this path,\n * where `{x}` and `(x)` are treated as single-segment wildcards.\n */\n path?: string;\n}\n\n// -----------------------------------------------------------------------------\n// Path-namespaced component resolution\n// -----------------------------------------------------------------------------\n\n/**\n * Returns true if each segment of `path` matches the corresponding segment in\n * `pattern`, where a pattern segment wrapped in `{…}` or `(…)` is a wildcard.\n */\nexport function pathMatchesPattern(path: string, pattern: string): boolean {\n const pathSegs = path.split('/').filter(Boolean);\n const patternSegs = pattern.split('/').filter(Boolean);\n if (pathSegs.length !== patternSegs.length) return false;\n for (let i = 0; i < patternSegs.length; i++) {\n const seg = patternSegs[i];\n if (!seg) return false;\n if ((seg.startsWith('{') && seg.endsWith('}')) || (seg.startsWith('(') && seg.endsWith(')'))) {\n continue;\n }\n if (seg !== pathSegs[i]) return false;\n }\n return true;\n}\n\n/**\n * Resolves the component for `blockType` from the registry.\n *\n * When `path` is provided, keys of the form `\"/{pattern} BlockType\"` are\n * checked first. The first key whose path pattern matches the current path\n * and whose block-type suffix matches `blockType` wins. Falls back to a\n * direct `registry[blockType]` lookup.\n */\nexport function resolveComponent(\n registry: Partial<BlockComponentRegistry>,\n blockType: string,\n path?: string\n): BlockComponentRegistry[string] | undefined {\n if (path) {\n for (const key of Object.keys(registry)) {\n if (!key.startsWith('/')) continue;\n const spaceIdx = key.indexOf(' ');\n if (spaceIdx === -1) continue;\n const pathPattern = key.slice(0, spaceIdx);\n const registeredType = key.slice(spaceIdx + 1);\n if (registeredType !== blockType) continue;\n if (pathMatchesPattern(path, pathPattern)) {\n return registry[key];\n }\n }\n }\n return registry[blockType];\n}\n\n// -----------------------------------------------------------------------------\n// Component\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 * In editable mode, renders a hidden sentinel before the component. The shared\n * CMS overlay script uses that sentinel to stamp attributes on the component's\n * root element without adding a layout-affecting wrapper.\n */\nexport function BlockRenderer({\n block,\n registry,\n disableEditable,\n enableContentEditable,\n routeParams,\n path,\n}: BlockRendererProps) {\n const Component = resolveComponent(registry, block.type, path);\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 // Extract language code from route params if any param is bound to the language schema.\n const language = routeParams\n ? Object.values(routeParams).find((p) => p.schemaName === 'language')?.value\n : undefined;\n\n const component = (\n <Component content={block.content} routeParams={routeParams} language={language} />\n );\n\n if (disableEditable) {\n return component;\n }\n\n const contentEntries = enableContentEditable\n ? [...extractContentValues(block.content as Record<string, unknown>).values()]\n .flat()\n .map(({ value, contentPath }) => ({ v: value, p: contentPath }))\n : undefined;\n\n return (\n <>\n <span\n data-cms-sentinel=\"\"\n data-block-id={block.id}\n data-block-type={block.type}\n data-content-entries={contentEntries ? JSON.stringify(contentEntries) : undefined}\n style={{ display: 'none' }}\n aria-hidden=\"true\"\n />\n {component}\n </>\n );\n}\n","/**\n * CMS Overlay Script\n *\n * Framework-agnostic vanilla JS script that handles all CMS edit mode overlays:\n * - Block hover outlines\n * - Cursor indicator\n * - Block toolbar (move up/down, delete)\n *\n * Uses a sentinel-based approach: hidden <span> elements mark block positions,\n * and this script stamps data attributes on the actual block elements (next sibling).\n * This avoids wrapper elements that could break layouts.\n *\n * Compatible with Next.js, TanStack Start, Remix, and other frameworks.\n */\n\nexport function generateCmsOverlayScript(cmsParentOrigin: string): string {\n return `\n(function() {\n if (window.__cmsOverlayInitialized) return;\n window.__cmsOverlayInitialized = true;\n\n var CMS_PARENT_ORIGIN = ${JSON.stringify(cmsParentOrigin)};\n\n var style = document.createElement('style');\n style.setAttribute('data-cms-overlay', '');\n style.textContent = \\`\n [data-cms-block],\n [data-cms-editable] {\n cursor: pointer;\n }\n [data-cms-editable] {\n border-radius: 2px;\n }\n [data-cms-editable]:not([data-cms-block]):hover {\n outline: 2px solid var(--component-accent, #A78BFA);\n outline-offset: 2px;\n }\n #cms-overlay-root {\n position: fixed;\n top: 0;\n left: 0;\n width: 0;\n height: 0;\n pointer-events: none;\n z-index: 99998;\n }\n #cms-overlay-root > *:not(.cms-block-toolbar) {\n pointer-events: none;\n }\n .cms-block-outline {\n position: fixed;\n box-sizing: border-box;\n border: 4px solid var(--component-accent, #A78BFA);\n border-radius: 4px;\n pointer-events: none;\n z-index: 99997;\n transition: all 0.15s ease;\n }\n .cms-block-outline.cms-outline-selected {\n border-color: var(--component-accent, #A78BFA);\n border-width: 4px;\n }\n .cms-block-label {\n position: absolute;\n top: 0;\n left: 0;\n display: none;\n max-width: 240px;\n padding: 2px 8px;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n font-size: 12px;\n font-weight: 600;\n line-height: 1.4;\n color: #fff;\n background: var(--component-accent, #A78BFA);\n border-radius: 4px 4px 4px 0;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n pointer-events: none;\n }\n .cms-block-label.cms-label-visible {\n display: block;\n }\n .cms-block-cursor {\n position: fixed;\n width: 22px;\n height: 22px;\n background: radial-gradient(circle, #fff 5px, #000 5px);\n border-radius: 50%;\n pointer-events: none;\n z-index: 99999;\n transform: translate(-50%, -50%);\n opacity: 0;\n transition: opacity 0.1s ease;\n }\n .cms-block-cursor.cms-cursor-visible {\n opacity: 1;\n }\n .cms-block-toolbar {\n position: fixed;\n display: flex;\n gap: 4px;\n background: #1f2937;\n border-radius: 6px;\n padding: 4px;\n box-shadow: 0 4px 12px rgba(0,0,0,0.25);\n z-index: 99999;\n pointer-events: none;\n opacity: 0;\n transform: scale(0.9) translateY(4px);\n transition: opacity 0.15s ease, transform 0.15s ease;\n }\n .cms-block-toolbar.cms-toolbar-visible {\n opacity: 1;\n transform: scale(1) translateY(0);\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 \\`;\n document.head.appendChild(style);\n\n function stampBlockElements() {\n var sentinels = document.querySelectorAll('[data-cms-sentinel]');\n sentinels.forEach(function(sentinel) {\n var blockEl = sentinel.nextElementSibling;\n if (!blockEl) return;\n\n var blockId = sentinel.getAttribute('data-block-id');\n var blockType = sentinel.getAttribute('data-block-type');\n\n blockEl.setAttribute('data-cms-block', '');\n blockEl.setAttribute('data-block-id', blockId);\n blockEl.setAttribute('data-block-type', blockType);\n\n injectEditableSpans(\n blockEl,\n blockId,\n blockType,\n sentinel.getAttribute('data-content-entries')\n );\n });\n }\n\n function injectEditableSpans(blockEl, blockId, blockType, rawEntries) {\n if (!rawEntries || blockEl.querySelector('[data-cms-editable][data-content-path]')) return;\n\n var entries;\n try {\n entries = JSON.parse(rawEntries);\n } catch (_) {\n return;\n }\n if (!Array.isArray(entries) || entries.length === 0) return;\n\n var used = {};\n var walker = document.createTreeWalker(blockEl, NodeFilter.SHOW_TEXT, null);\n var textNodes = [];\n var node = walker.nextNode();\n while (node !== null) {\n if (node.nodeValue && node.nodeValue.trim()) {\n textNodes.push(node);\n }\n node = walker.nextNode();\n }\n\n for (var i = 0; i < textNodes.length; i++) {\n var textNode = textNodes[i];\n var parentEl = textNode.parentElement;\n if (!parentEl || parentEl.closest('svg') || parentEl.closest('[data-cms-editable]')) {\n continue;\n }\n\n var text = textNode.nodeValue;\n if (!text) continue;\n\n for (var j = 0; j < entries.length; j++) {\n var entry = entries[j];\n if (!entry || typeof entry.v !== 'string' || typeof entry.p !== 'string' || used[entry.p]) {\n continue;\n }\n if (text.trim() !== entry.v.trim()) continue;\n\n used[entry.p] = true;\n var 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 span.setAttribute('contenteditable', 'true');\n parentEl.insertBefore(span, textNode);\n span.appendChild(textNode);\n break;\n }\n }\n }\n\n stampBlockElements();\n\n var stampObserver = new MutationObserver(function(mutations) {\n var needsStamp = mutations.some(function(m) {\n return m.addedNodes.length > 0;\n });\n if (needsStamp) stampBlockElements();\n });\n stampObserver.observe(document.body, { childList: true, subtree: true });\n\n var overlayRoot = document.createElement('div');\n overlayRoot.id = 'cms-overlay-root';\n document.body.appendChild(overlayRoot);\n\n var cursor = document.createElement('div');\n cursor.className = 'cms-block-cursor';\n overlayRoot.appendChild(cursor);\n\n function createOutline(extraClass) {\n var el = document.createElement('div');\n el.className = 'cms-block-outline' + (extraClass ? ' ' + extraClass : '');\n el.style.display = 'none';\n var label = document.createElement('span');\n label.className = 'cms-block-label';\n el.appendChild(label);\n overlayRoot.appendChild(el);\n return el;\n }\n\n var outline = createOutline();\n var selectedOutline = createOutline('cms-outline-selected');\n\n var toolbar = document.createElement('div');\n toolbar.className = 'cms-block-toolbar';\n toolbar.setAttribute('data-cms-toolbar', '');\n toolbar.innerHTML = \\`\n <button class=\"move-up\" title=\"Move up\">\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M18 15l-6-6-6 6\"/>\n </svg>\n </button>\n <button class=\"move-down\" title=\"Move down\">\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M6 9l6 6 6-6\"/>\n </svg>\n </button>\n <button class=\"delete\" title=\"Delete\">\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16\"/>\n </svg>\n </button>\n \\`;\n overlayRoot.appendChild(toolbar);\n\n var currentBlockId = null;\n var toolbarVisible = false;\n var selectedBlockId = null;\n\n // In edit mode we hijack every link/button activation. Following a link or\n // firing a button's own handler makes inline editing on that element\n // impossible (the click navigates away or triggers an action instead of\n // placing the caret). We block the default action and stop the event from\n // reaching the element's own listeners, while still letting our selection /\n // edit routing run below.\n function blockInteractiveActivation(e) {\n var interactive = e.target.closest(\n 'a[href], button, [role=\"button\"], input[type=\"submit\"], input[type=\"button\"], input[type=\"reset\"]'\n );\n if (interactive) {\n e.preventDefault();\n e.stopPropagation();\n }\n }\n\n function postToParent(message) {\n if (!CMS_PARENT_ORIGIN || !window.parent || window.parent === window) {\n return;\n }\n window.parent.postMessage(message, CMS_PARENT_ORIGIN);\n }\n\n function sendReadySignal() {\n postToParent({ type: 'cms-preview-ready' });\n }\n sendReadySignal();\n // The preview iframe can execute before the admin listener is attached.\n setTimeout(sendReadySignal, 500);\n setTimeout(sendReadySignal, 1500);\n\n function getBlockElement(blockId) {\n return document.querySelector('[data-block-id=\"' + blockId + '\"]');\n }\n\n function getAllBlocks() {\n return Array.from(document.querySelectorAll('[data-cms-block]'));\n }\n\n function getBlockIndex(blockId) {\n var blocks = getAllBlocks();\n for (var i = 0; i < blocks.length; i++) {\n if (blocks[i].getAttribute('data-block-id') === blockId) return i;\n }\n return -1;\n }\n\n function formatBlockType(type) {\n if (!type) return 'Block';\n return type\n .replace(/[-_]+/g, ' ')\n .replace(/\\\\b\\\\w/g, function(c) { return c.toUpperCase(); });\n }\n\n function updateOutline(el, outlineEl) {\n var label = outlineEl.querySelector('.cms-block-label');\n if (!el) {\n outlineEl.style.display = 'none';\n if (label) label.classList.remove('cms-label-visible');\n return;\n }\n var rect = el.getBoundingClientRect();\n outlineEl.style.display = 'block';\n outlineEl.style.top = rect.top + 'px';\n outlineEl.style.left = rect.left + 'px';\n outlineEl.style.width = rect.width + 'px';\n outlineEl.style.height = rect.height + 'px';\n if (label) {\n label.textContent = formatBlockType(el.getAttribute('data-block-type'));\n label.classList.add('cms-label-visible');\n }\n }\n\n function positionToolbar(x, y) {\n var rect = toolbar.getBoundingClientRect();\n var top = Math.max(4, y - rect.height - 12);\n var left = Math.max(4, Math.min(x - rect.width / 2, window.innerWidth - rect.width - 4));\n toolbar.style.top = top + 'px';\n toolbar.style.left = left + 'px';\n }\n\n function showToolbar(x, y, blockId) {\n currentBlockId = blockId;\n toolbarVisible = true;\n positionToolbar(x, y);\n toolbar.classList.add('cms-toolbar-visible');\n\n var index = getBlockIndex(blockId);\n var total = getAllBlocks().length;\n toolbar.querySelector('.move-up').disabled = index <= 0;\n toolbar.querySelector('.move-down').disabled = index >= total - 1;\n }\n\n function hideToolbar() {\n toolbarVisible = false;\n toolbar.classList.remove('cms-toolbar-visible');\n }\n\n function showCursor(x, y) {\n cursor.style.top = y + 'px';\n cursor.style.left = x + 'px';\n cursor.classList.add('cms-cursor-visible');\n }\n\n function hideCursor() {\n cursor.classList.remove('cms-cursor-visible');\n }\n\n document.addEventListener('mouseover', function(e) {\n if (toolbarVisible) return;\n var block = e.target.closest('[data-cms-block]');\n if (block) {\n updateOutline(block, outline);\n }\n });\n\n document.addEventListener('mouseout', function(e) {\n var block = e.target.closest('[data-cms-block]');\n var related = e.relatedTarget ? e.relatedTarget.closest('[data-cms-block]') : null;\n if (block && block !== related) {\n outline.style.display = 'none';\n hideCursor();\n }\n });\n\n document.addEventListener('mousemove', function(e) {\n if (toolbarVisible) return;\n var block = e.target.closest('[data-cms-block]');\n if (block) {\n showCursor(e.clientX, e.clientY);\n }\n });\n\n document.addEventListener('contextmenu', function(e) {\n if (e.target.closest('[data-cms-toolbar]')) return;\n var block = e.target.closest('[data-cms-block]');\n if (block) {\n if (toolbarVisible) return;\n e.preventDefault();\n hideCursor();\n outline.style.display = 'none';\n var blockId = block.getAttribute('data-block-id');\n showToolbar(e.clientX, e.clientY, blockId);\n }\n });\n\n document.addEventListener('click', function(e) {\n // The floating toolbar is interactive (and its buttons are real <button>s);\n // bail out before any blocking so its own handlers run.\n if (e.target.closest('[data-cms-toolbar]')) return;\n\n blockInteractiveActivation(e);\n\n if (toolbarVisible) {\n var activeBlock = e.target.closest('[data-cms-block]');\n if (!activeBlock || activeBlock.getAttribute('data-block-id') !== currentBlockId) {\n hideToolbar();\n }\n }\n\n var editable = e.target.closest('[data-cms-editable]');\n if (editable) {\n postToParent({\n type: 'cms-editable-click',\n blockId: editable.getAttribute('data-block-id'),\n blockType: editable.getAttribute('data-block-type'),\n contentPath: editable.getAttribute('data-content-path')\n });\n return;\n }\n\n var block = e.target.closest('[data-cms-block]');\n if (block) {\n postToParent({\n type: 'cms-editable-click',\n blockId: block.getAttribute('data-block-id'),\n blockType: block.getAttribute('data-block-type'),\n contentPath: null\n });\n }\n }, true);\n\n toolbar.querySelector('.move-up').addEventListener('click', function() {\n if (!currentBlockId) return;\n postToParent({ type: 'cms-block-action', action: 'move-up', blockId: currentBlockId });\n hideToolbar();\n });\n\n toolbar.querySelector('.move-down').addEventListener('click', function() {\n if (!currentBlockId) return;\n postToParent({ type: 'cms-block-action', action: 'move-down', blockId: currentBlockId });\n hideToolbar();\n });\n\n toolbar.querySelector('.delete').addEventListener('click', function() {\n if (!currentBlockId) return;\n postToParent({ type: 'cms-block-action', action: 'delete', blockId: currentBlockId });\n hideToolbar();\n });\n\n window.addEventListener('scroll', function() {\n hideCursor();\n hideToolbar();\n if (selectedBlockId) {\n var el = getBlockElement(selectedBlockId);\n updateOutline(el, selectedOutline);\n }\n }, { passive: true, capture: true });\n\n window.addEventListener('resize', function() {\n if (selectedBlockId) {\n var el = getBlockElement(selectedBlockId);\n updateOutline(el, selectedOutline);\n }\n }, { passive: true });\n\n window.addEventListener('message', function(e) {\n if (CMS_PARENT_ORIGIN && e.origin !== CMS_PARENT_ORIGIN) return;\n if (e.source !== window.parent) return;\n\n if (e.data && e.data.type === 'cms-select-block') {\n selectedBlockId = e.data.blockId || null;\n if (selectedBlockId) {\n var el = getBlockElement(selectedBlockId);\n updateOutline(el, selectedOutline);\n } else {\n selectedOutline.style.display = 'none';\n }\n }\n });\n})();\n`;\n}\n","/**\n * Trusted-origin helpers for CMS template-builder postMessage traffic\n * between the admin UI (parent) and the site preview iframe (child).\n */\n\nexport const CMS_PARENT_ORIGIN_PARAM = 'cms_parent_origin';\n\nexport function parseOrigin(url: string | undefined): string | null {\n if (!url) return null;\n try {\n return new URL(url).origin;\n } catch {\n return null;\n }\n}\n\nfunction getParentOriginFromQueryParam(): string | null {\n if (typeof window === 'undefined') return null;\n const value = new URLSearchParams(window.location.search).get(CMS_PARENT_ORIGIN_PARAM);\n return parseOrigin(value ?? undefined);\n}\n\nfunction getReferrerOrigin(): string | null {\n if (typeof document === 'undefined' || !document.referrer) return null;\n return parseOrigin(document.referrer);\n}\n\n/** Same-origin embed (e.g. proxied admin + preview on the customer domain). */\nfunction getSameOriginParentOrigin(): string | null {\n if (typeof window === 'undefined' || !window.parent || window.parent === window) {\n return null;\n }\n try {\n return window.parent.location.origin;\n } catch {\n return null;\n }\n}\n\n/** Origins allowed to send messages to the preview iframe (CMS admin hosts). */\nexport function getAllowedCmsParentOrigins(explicitParentOrigin?: string): string[] {\n const origins = new Set<string>();\n const apiOrigin = parseOrigin(process.env.NEXT_PUBLIC_CMS_API_URL);\n if (apiOrigin) origins.add(apiOrigin);\n\n const fromQuery = parseOrigin(explicitParentOrigin) ?? getParentOriginFromQueryParam();\n if (fromQuery) origins.add(fromQuery);\n\n const referrerOrigin = getReferrerOrigin();\n if (referrerOrigin) origins.add(referrerOrigin);\n\n const sameOriginParent = getSameOriginParentOrigin();\n if (sameOriginParent) origins.add(sameOriginParent);\n\n return [...origins];\n}\n\n/**\n * Target origin when posting from the preview iframe to the CMS parent.\n *\n * When the admin is proxied on a customer domain, the parent window origin is\n * the customer site — not NEXT_PUBLIC_CMS_API_URL. Prefer explicit/referrer/\n * same-origin parent detection over the CMS API URL.\n */\nexport function getCmsParentTargetOrigin(\n preferredCmsUrl?: string,\n explicitParentOrigin?: string\n): string | null {\n const fromExplicit = parseOrigin(explicitParentOrigin) ?? getParentOriginFromQueryParam();\n if (fromExplicit) return fromExplicit;\n\n const referrerOrigin = getReferrerOrigin();\n if (referrerOrigin) return referrerOrigin;\n\n const sameOriginParent = getSameOriginParentOrigin();\n if (sameOriginParent) return sameOriginParent;\n\n const preferredOrigin = parseOrigin(preferredCmsUrl);\n if (preferredOrigin) return preferredOrigin;\n\n const allowed = getAllowedCmsParentOrigins(explicitParentOrigin);\n return allowed[0] ?? null;\n}\n\nexport function isTrustedCmsParentMessage(\n event: MessageEvent,\n explicitParentOrigin?: string\n): boolean {\n const allowed = getAllowedCmsParentOrigins(explicitParentOrigin);\n if (allowed.length === 0) return false;\n if (!allowed.includes(event.origin)) return false;\n return event.source === window.parent;\n}\n\nexport function postMessageToCmsParent(\n message: unknown,\n options?: { preferredCmsUrl?: string; explicitParentOrigin?: string }\n): void {\n if (typeof window === 'undefined' || !window.parent || window.parent === window) return;\n const targetOrigin = getCmsParentTargetOrigin(\n options?.preferredCmsUrl,\n options?.explicitParentOrigin\n );\n if (!targetOrigin) return;\n window.parent.postMessage(message, targetOrigin);\n}\n","/**\n * Framework-agnostic parametric route rendering for Profound CMS.\n * Next.js and TanStack Start call `renderParametricRoute` and map `not_found`\n * to their respective 404 mechanisms.\n */\n\nimport {\n isArticlePublished,\n isValidBlockSchemaName,\n normalizeArticleContent,\n} from '@repo/cms-schema/blocks';\nimport type { GeneratedBlockRow } from '@repo/cms-schema/db';\nimport { normalizePath } from '@repo/cms-schema/routing';\nimport type { ReactNode } from 'react';\nimport { BlockRenderer, CmsEditableInit } from './block-renderer';\nimport { type CmsConfig, getCmsClient } from './cms-api';\nimport type { BlockComponentRegistry, BlockData, ResolvedRouteParams } from './types';\n\nexport type ParametricRouteProps = CmsConfig & {\n params: Promise<{ slug: string[] }> | { slug: string[] };\n searchParams?:\n | Promise<{ [key: string]: string | string[] | boolean | undefined }>\n | { [key: string]: string | string[] | boolean | undefined };\n registry?: Partial<BlockComponentRegistry>;\n};\n\n/**\n * Render-time options that are NOT part of the page props surface. `preview` is\n * supplied only by the dedicated preview page wrapper (`ParametricRoutePreviewPage`),\n * never by query params, so the production page can never accidentally opt into\n * edit overlays or draft content.\n */\nexport type RenderParametricRouteOptions = {\n /**\n * Renders the preview experience: forces the edit-mode overlay/hovers and pulls\n * block content from `draft_content` instead of `published_content`.\n */\n preview?: boolean;\n};\n\nexport type ParametricRouteResult =\n | { status: 'ok'; node: ReactNode }\n | { status: 'not_found' }\n | { status: 'error'; error: unknown };\n\n/** @internal Exported for generateMetadata in renderer.tsx */\nexport function 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 * Renders CMS-backed blocks for a multi-segment path. Maps NOT_FOUND-style API\n * outcomes to `{ status: 'not_found' }` instead of throwing.\n */\nexport async function renderParametricRoute(\n {\n params,\n searchParams,\n registry,\n apiKey,\n cmsUrl,\n websiteId: providedWebsiteId,\n }: ParametricRouteProps,\n { preview = false }: RenderParametricRouteOptions = {}\n): Promise<ParametricRouteResult> {\n const websiteId = getWebsiteId(providedWebsiteId);\n // Support both Promise (Next.js App Router) and plain object (TanStack Start) patterns\n const { slug } = 'then' in params ? await params : params;\n const resolvedSearchParams =\n searchParams && 'then' in searchParams ? await searchParams : searchParams;\n\n let aiPreviewIndex: number | null = null;\n const aiPreviewParam = resolvedSearchParams?.ai_preview;\n if (aiPreviewParam && typeof aiPreviewParam !== 'boolean') {\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 const cmsParentOriginParam = resolvedSearchParams?.cms_parent_origin;\n const cmsParentOrigin =\n typeof cmsParentOriginParam === 'boolean'\n ? undefined\n : Array.isArray(cmsParentOriginParam)\n ? cmsParentOriginParam[0]\n : cmsParentOriginParam;\n\n const previewMode = preview === true;\n const editModeParam = resolvedSearchParams?.edit_mode;\n // Support both string (Next.js) and boolean (TanStack Start) values\n const editMode =\n previewMode || editModeParam === true || editModeParam === 'true' || editModeParam === '1';\n\n const rawPath = `/${slug.join('/')}`;\n const path = normalizePath(rawPath);\n\n if (/\\.[a-zA-Z0-9]+$/.test(path)) {\n return { status: 'not_found' };\n }\n\n const client = getCmsClient({ apiKey, cmsUrl });\n\n try {\n const { route, resolvedParams } = await client.route.getByPath.query({\n websiteId,\n path,\n });\n\n if (route.state !== 'Live') {\n return { status: 'not_found' };\n }\n\n type GeneratedBlockData = Pick<\n GeneratedBlockRow,\n 'id' | 'generated_content' | 'generated_layout' | 'schema_name' | 'status'\n >;\n\n // For parametric routes with CEL expressions, use getWithCelCache to get\n // blocks with cached CEL values merged in. This enables implicit bindings\n // where 10,000+ documents automatically get correctly evaluated values.\n const hasResolvedParams = resolvedParams && Object.keys(resolvedParams).length > 0;\n\n const blockResultsPromise = hasResolvedParams\n ? client.block.getWithCelCache\n .query({\n websiteId,\n blockIds: route.block_ids,\n routePath: route.path,\n params: Object.fromEntries(\n Object.entries(resolvedParams).map(([key, param]) => [key, param.value])\n ),\n })\n .then((result) => result.blocks)\n .catch((error) => {\n console.error('Failed to fetch blocks with CEL cache:', error);\n // Fall back to regular block fetch\n return client.block.getByIds\n .query({ websiteId, ids: route.block_ids })\n .catch(() => [] as Awaited<ReturnType<typeof client.block.getByIds.query>>);\n })\n : client.block.getByIds.query({ websiteId, ids: route.block_ids }).catch((error) => {\n console.error('Failed to fetch blocks:', error);\n return [] as Awaited<ReturnType<typeof client.block.getByIds.query>>;\n });\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 blockResultsPromise,\n generatedBlocksPromise,\n ]);\n\n const blocks: BlockData[] = [];\n\n for (const block of blockResults) {\n if (!block) continue;\n\n // Production renders live \"published\" content; the preview route renders the\n // in-progress \"draft\" content so editors see unpublished changes.\n const blockContent = previewMode\n ? (block.draft_content as Record<string, unknown> | null)\n : (block.published_content as Record<string, unknown> | null);\n if (blockContent == null) continue;\n\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;\n const variants = generatedBlock?.generated_content;\n if (variants && Array.isArray(variants) && variants[variantIndex]) {\n content = variants[variantIndex].content;\n }\n }\n content = content ?? blockContent;\n if (!content) continue;\n\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 if (block.schema_id) {\n blocks.push({ id: block.id, type: block.schema_name, content });\n continue;\n }\n\n if (!isValidBlockSchemaName(block.schema_name)) {\n continue;\n }\n\n blocks.push({\n id: block.id,\n type: block.schema_name,\n content,\n } as BlockData);\n }\n\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 status: 'ok',\n node: (\n <main>\n {editMode && <CmsEditableInit cmsUrl={cmsUrl} cmsParentOrigin={cmsParentOrigin} />}\n {blocks.map((block) => (\n <BlockRenderer\n key={block.id}\n block={block}\n registry={registry ?? {}}\n disableEditable={!editMode}\n enableContentEditable={previewMode}\n routeParams={routeParams}\n path={path}\n />\n ))}\n </main>\n ),\n };\n } catch (error) {\n console.error(`Route fetch error for path: ${path}`, error);\n\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' || errorCode === 'BAD_REQUEST') {\n return { status: 'not_found' };\n }\n\n return { status: 'error', error };\n }\n}\n\nexport { normalizePath };\n"],"mappings":"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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;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;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;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;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","names":["z","z","z","z","z","z","z","z","z","z","z","z","jsx","jsxs","getCmsClient"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cms-renderer",
3
- "version": "0.6.12",
3
+ "version": "0.7.0",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "exports": {