@seed-ship/mcp-ui-solid 2.2.4 → 2.2.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/ComponentToolbar.cjs +76 -0
- package/dist/components/ComponentToolbar.cjs.map +1 -0
- package/dist/components/ComponentToolbar.d.ts +47 -0
- package/dist/components/ComponentToolbar.d.ts.map +1 -0
- package/dist/components/ComponentToolbar.js +76 -0
- package/dist/components/ComponentToolbar.js.map +1 -0
- package/dist/index.cjs +2 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/services/component-registry.cjs +405 -1
- package/dist/services/component-registry.cjs.map +1 -1
- package/dist/services/component-registry.d.ts +40 -0
- package/dist/services/component-registry.d.ts.map +1 -1
- package/dist/services/component-registry.js +406 -2
- package/dist/services/component-registry.js.map +1 -1
- package/dist/services/validation.cjs +12 -1
- package/dist/services/validation.cjs.map +1 -1
- package/dist/services/validation.d.ts.map +1 -1
- package/dist/services/validation.js +12 -1
- package/dist/services/validation.js.map +1 -1
- package/package.json +1 -1
- package/src/components/ComponentToolbar.tsx +101 -0
- package/src/index.ts +2 -0
- package/src/services/component-registry.test.ts +49 -41
- package/src/services/component-registry.ts +444 -0
- package/src/services/validation.ts +12 -0
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"component-registry.js","sources":["../../src/services/component-registry.ts"],"sourcesContent":["/**\n * Component Registry Service\n * Phase 0: Static registry with Quickchart and Table definitions\n * Phase 1: Dynamic registry populated from /api/mcp/tools/list\n *\n * Provides component schemas for LLM prompt engineering\n */\n\nimport type { ComponentRegistryEntry, ComponentType } from '../types'\nimport { DEFAULT_RESOURCE_LIMITS } from './validation'\n\n/**\n * Quickchart Component Registry Entry\n * Based on Quickchart API documentation\n */\nexport const QuickchartRegistry: ComponentRegistryEntry = {\n type: 'chart',\n name: 'Quickchart',\n description:\n 'Render charts using Quickchart.io API. Supports bar, line, pie, doughnut, radar, and scatter charts. Best for visualizing numerical data with 2-10 data series and up to 1000 data points.',\n schema: {\n type: 'object',\n properties: {\n type: {\n type: 'string',\n enum: ['bar', 'line', 'pie', 'doughnut', 'radar', 'scatter'],\n description: 'Chart type',\n },\n title: {\n type: 'string',\n description: 'Chart title (optional)',\n },\n data: {\n type: 'object',\n properties: {\n labels: {\n type: 'array',\n items: { type: 'string' },\n description: 'X-axis labels',\n },\n datasets: {\n type: 'array',\n items: {\n type: 'object',\n properties: {\n label: { type: 'string' },\n data: {\n type: 'array',\n items: { type: 'number' },\n },\n backgroundColor: {\n oneOf: [{ type: 'string' }, { type: 'array', items: { type: 'string' } }],\n },\n borderColor: {\n oneOf: [{ type: 'string' }, { type: 'array', items: { type: 'string' } }],\n },\n borderWidth: { type: 'number' },\n },\n required: ['label', 'data'],\n },\n },\n },\n required: ['labels', 'datasets'],\n },\n options: {\n type: 'object',\n description: 'Chart.js options for customization',\n },\n },\n required: ['type', 'data'],\n },\n examples: [\n {\n query: 'Show me document types distribution',\n component: {\n id: 'example-bar-1',\n type: 'chart',\n position: { colStart: 1, colSpan: 6 },\n params: {\n type: 'bar',\n title: 'Document Types',\n data: {\n labels: ['PDF', 'DOCX', 'TXT', 'XLSX'],\n datasets: [\n {\n label: 'Count',\n data: [245, 189, 123, 98],\n backgroundColor: ['rgba(59, 130, 246, 0.8)'],\n },\n ],\n },\n },\n },\n },\n {\n query: 'Display upload trends over the last week',\n component: {\n id: 'example-line-1',\n type: 'chart',\n position: { colStart: 1, colSpan: 6 },\n params: {\n type: 'line',\n title: 'Upload Trends',\n data: {\n labels: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'],\n datasets: [\n {\n label: 'Uploads',\n data: [42, 38, 51, 47, 63, 29, 15],\n borderColor: 'rgb(59, 130, 246)',\n },\n ],\n },\n options: {\n tension: 0.4,\n },\n },\n },\n },\n ],\n limits: DEFAULT_RESOURCE_LIMITS,\n}\n\n/**\n * Table Component Registry Entry\n */\nexport const TableRegistry: ComponentRegistryEntry = {\n type: 'table',\n name: 'DataTable',\n description:\n 'Render tabular data with sortable columns and pagination. Best for displaying structured records with up to 100 rows. Supports column width customization and cell formatting.',\n schema: {\n type: 'object',\n properties: {\n title: {\n type: 'string',\n description: 'Table title (optional)',\n },\n columns: {\n type: 'array',\n items: {\n type: 'object',\n properties: {\n key: { type: 'string', description: 'Data key for this column' },\n label: { type: 'string', description: 'Column header label' },\n sortable: { type: 'boolean', description: 'Whether column is sortable' },\n width: { type: 'string', description: 'CSS width (e.g., \"30%\")' },\n },\n required: ['key', 'label'],\n },\n minItems: 1,\n },\n rows: {\n type: 'array',\n items: {\n type: 'object',\n description: 'Row data matching column keys',\n },\n maxItems: 100,\n },\n pagination: {\n type: 'object',\n properties: {\n currentPage: { type: 'number' },\n pageSize: { type: 'number' },\n totalRows: { type: 'number' },\n },\n },\n },\n required: ['columns', 'rows'],\n },\n examples: [\n {\n query: 'Show me the most recent documents',\n component: {\n id: 'example-table-1',\n type: 'table',\n position: { colStart: 1, colSpan: 8 },\n params: {\n title: 'Recent Documents',\n columns: [\n { key: 'name', label: 'Name', sortable: true, width: '40%' },\n { key: 'type', label: 'Type', sortable: true, width: '15%' },\n { key: 'size', label: 'Size', width: '15%' },\n { key: 'modified', label: 'Modified', sortable: true, width: '30%' },\n ],\n rows: [\n { name: 'Report.pdf', type: 'PDF', size: '2.4 MB', modified: '2 hours ago' },\n { name: 'Slides.pptx', type: 'PPTX', size: '8.7 MB', modified: '1 day ago' },\n ],\n },\n },\n },\n ],\n limits: DEFAULT_RESOURCE_LIMITS,\n}\n\n/**\n * Metric Card Component Registry Entry\n */\nexport const MetricRegistry: ComponentRegistryEntry = {\n type: 'metric',\n name: 'MetricCard',\n description:\n 'Display a single metric with optional trend indicator. Best for KPIs, statistics, and summary numbers. Supports trend direction (up/down/neutral) and subtitles.',\n schema: {\n type: 'object',\n properties: {\n title: {\n type: 'string',\n description: 'Metric title',\n },\n value: {\n oneOf: [{ type: 'string' }, { type: 'number' }],\n description: 'Metric value',\n },\n unit: {\n type: 'string',\n description: 'Unit of measurement (optional)',\n },\n trend: {\n type: 'object',\n properties: {\n value: { type: 'number', description: 'Percentage change' },\n direction: { type: 'string', enum: ['up', 'down', 'neutral'] },\n },\n },\n subtitle: {\n type: 'string',\n description: 'Additional context (optional)',\n },\n },\n required: ['title', 'value'],\n },\n examples: [\n {\n query: 'Show total document count',\n component: {\n id: 'example-metric-1',\n type: 'metric',\n position: { colStart: 1, colSpan: 3 },\n params: {\n title: 'Total Documents',\n value: '1,247',\n trend: {\n value: 12.5,\n direction: 'up',\n },\n subtitle: '+142 this month',\n },\n },\n },\n ],\n limits: {\n maxDataPoints: 1,\n maxTableRows: 1,\n maxPayloadSize: 5 * 1024, // 5KB\n renderTimeout: 1000, // 1s\n },\n}\n\n/**\n * Text Component Registry Entry\n */\nexport const TextRegistry: ComponentRegistryEntry = {\n type: 'text',\n name: 'TextBlock',\n description:\n 'Render text content with optional markdown support. Best for explanations, summaries, and context. Supports basic HTML formatting.',\n schema: {\n type: 'object',\n properties: {\n content: {\n type: 'string',\n description: 'Text content (HTML allowed, will be sanitized)',\n },\n markdown: {\n type: 'boolean',\n description: 'Whether content is markdown (not yet implemented)',\n },\n className: {\n type: 'string',\n description: 'Custom CSS classes',\n },\n },\n required: ['content'],\n },\n examples: [\n {\n query: 'Explain the document distribution',\n component: {\n id: 'example-text-1',\n type: 'text',\n position: { colStart: 1, colSpan: 12 },\n params: {\n content:\n '<p>Your document library contains <strong>1,247 files</strong> across 5 different formats. PDFs represent the largest category at 35% of total storage.</p>',\n },\n },\n },\n ],\n limits: {\n maxDataPoints: 1,\n maxTableRows: 1,\n maxPayloadSize: 10 * 1024, // 10KB\n renderTimeout: 1000, // 1s\n },\n}\n\n// ============================================================================\n// Sprint 4: Additional Component Registry Entries\n// ============================================================================\n\n/**\n * Grid Component Registry Entry\n * Nested CSS Grid layout for organizing multiple components\n */\nexport const GridRegistry: ComponentRegistryEntry = {\n type: 'grid',\n name: 'GridLayout',\n description:\n 'Nested CSS Grid layout for organizing multiple components. Supports named areas, responsive columns (1-12), and custom gap spacing. Best for complex dashboard layouts and template builder.',\n schema: {\n type: 'object',\n properties: {\n columns: {\n type: 'number',\n description: 'Number of columns (default: 12)',\n },\n gap: {\n type: 'string',\n description: 'Gap between items (e.g., \"1rem\")',\n },\n minRowHeight: {\n type: 'string',\n description: 'Minimum row height (optional)',\n },\n areas: {\n type: 'array',\n items: {\n type: 'array',\n items: { type: 'string' },\n },\n description: 'CSS Grid template areas for named regions',\n },\n children: {\n type: 'array',\n items: { type: 'object' },\n description: 'Child UIComponents to render within the grid',\n },\n },\n required: ['children'],\n },\n examples: [],\n limits: DEFAULT_RESOURCE_LIMITS,\n}\n\n/**\n * Action Component Registry Entry\n * Interactive button or link that triggers tool calls\n */\nexport const ActionRegistry: ComponentRegistryEntry = {\n type: 'action',\n name: 'ActionButton',\n description:\n 'Interactive button or link that triggers tool calls or navigation. Best for user interactions, form submissions, and workflow triggers.',\n schema: {\n type: 'object',\n properties: {\n label: {\n type: 'string',\n description: 'Button text',\n },\n type: {\n type: 'string',\n enum: ['button', 'link'],\n description: 'Render as button or link',\n },\n action: {\n type: 'string',\n enum: ['tool-call', 'link', 'submit'],\n description: 'Action type to perform',\n },\n toolName: {\n type: 'string',\n description: 'Tool name to call (for tool-call action)',\n },\n params: {\n type: 'object',\n description: 'Parameters to pass to the tool',\n },\n url: {\n type: 'string',\n description: 'URL for link action',\n },\n variant: {\n type: 'string',\n enum: ['primary', 'secondary', 'outline', 'ghost', 'danger'],\n description: 'Visual style variant',\n },\n size: {\n type: 'string',\n enum: ['sm', 'md', 'lg'],\n description: 'Button size',\n },\n disabled: {\n type: 'boolean',\n description: 'Whether the action is disabled',\n },\n },\n required: ['label', 'type', 'action'],\n },\n examples: [],\n limits: DEFAULT_RESOURCE_LIMITS,\n}\n\n/**\n * Footer Component Registry Entry\n * Display execution metadata like timing and source count\n */\nexport const FooterRegistry: ComponentRegistryEntry = {\n type: 'footer',\n name: 'FooterSection',\n description:\n 'Footer section displaying execution metadata. Best for showing timing, model info, and source counts. Auto-injected by layouts when metadata is provided.',\n schema: {\n type: 'object',\n properties: {\n poweredBy: {\n type: 'string',\n description: 'Powered by text (optional)',\n },\n executionTime: {\n type: 'number',\n description: 'Execution time in milliseconds',\n },\n model: {\n type: 'string',\n description: 'LLM model used',\n },\n sourceCount: {\n type: 'number',\n description: 'Number of sources used',\n },\n customText: {\n type: 'string',\n description: 'Custom footer text',\n },\n links: {\n type: 'array',\n items: {\n type: 'object',\n properties: {\n label: { type: 'string' },\n url: { type: 'string' },\n },\n },\n description: 'Footer links',\n },\n },\n },\n examples: [],\n limits: {\n maxDataPoints: 1,\n maxTableRows: 1,\n maxPayloadSize: 5 * 1024,\n renderTimeout: 1000,\n },\n}\n\n/**\n * Carousel Component Registry Entry\n * Display multiple items with horizontal scrolling\n */\nexport const CarouselRegistry: ComponentRegistryEntry = {\n type: 'carousel',\n name: 'Carousel',\n description:\n 'Horizontal carousel for displaying multiple items with snap scrolling and navigation buttons. Best for showcasing related content, image galleries, or card collections.',\n schema: {\n type: 'object',\n properties: {\n items: {\n type: 'array',\n items: { type: 'object' },\n description: 'Array of UIComponents to display in carousel',\n },\n height: {\n type: 'string',\n description: 'Carousel height (optional)',\n },\n },\n required: ['items'],\n },\n examples: [],\n limits: DEFAULT_RESOURCE_LIMITS,\n}\n\n/**\n * Artifact Component Registry Entry\n * Display downloadable artifacts like generated files\n */\nexport const ArtifactRegistry: ComponentRegistryEntry = {\n type: 'artifact',\n name: 'Artifact',\n description:\n 'Display downloadable artifacts like generated files or exports. Shows filename, size, and download button. Best for CSV exports, PDF reports, and generated documents.',\n schema: {\n type: 'object',\n properties: {\n url: {\n type: 'string',\n description: 'Download URL for the artifact',\n },\n filename: {\n type: 'string',\n description: 'Display filename',\n },\n mimeType: {\n type: 'string',\n description: 'MIME type (e.g., \"text/csv\", \"application/pdf\")',\n },\n size: {\n type: 'number',\n description: 'File size in bytes',\n },\n description: {\n type: 'string',\n description: 'Description of the artifact',\n },\n },\n required: ['url', 'filename', 'mimeType'],\n },\n examples: [],\n limits: {\n maxDataPoints: 1,\n maxTableRows: 1,\n maxPayloadSize: 5 * 1024,\n renderTimeout: 1000,\n },\n}\n\n/**\n * Component Registry - All components indexed by type\n */\nexport const ComponentRegistry: Map<ComponentType, ComponentRegistryEntry> = new Map([\n ['chart', QuickchartRegistry],\n ['table', TableRegistry],\n ['metric', MetricRegistry],\n ['text', TextRegistry],\n // Sprint 4 additions\n ['grid', GridRegistry],\n ['action', ActionRegistry],\n ['footer', FooterRegistry],\n ['carousel', CarouselRegistry],\n ['artifact', ArtifactRegistry],\n])\n\n/**\n * Get component registry entry by type\n */\nexport function getComponentEntry(type: ComponentType): ComponentRegistryEntry | undefined {\n return ComponentRegistry.get(type)\n}\n\n/**\n * Get all component types\n */\nexport function getAllComponentTypes(): ComponentType[] {\n return Array.from(ComponentRegistry.keys())\n}\n\n/**\n * Get registry as JSON for LLM context\n */\nexport function getRegistryForLLM(): string {\n const entries = Array.from(ComponentRegistry.values()).map((entry) => ({\n type: entry.type,\n name: entry.name,\n description: entry.description,\n schema: entry.schema,\n examples: entry.examples.map((ex) => ({\n query: ex.query,\n component: ex.component,\n })),\n limits: entry.limits,\n }))\n\n return JSON.stringify(entries, null, 2)\n}\n\n/**\n * Validate component against registry schema\n * (Future: Use Zod for runtime validation)\n */\nexport function validateAgainstRegistry(\n componentType: ComponentType,\n params: any\n): { valid: boolean; errors?: string[]; warnings?: string[] } {\n const entry = getComponentEntry(componentType)\n if (!entry) {\n // Warn but don't block — renderer may exist even without registry entry\n return { valid: true, warnings: [`No registry entry for type: ${componentType}`] }\n }\n\n // Basic validation (Phase 1 will add Zod schema validation)\n const required = entry.schema.required || []\n const missing = required.filter((key: string) => !(key in params))\n\n if (missing.length > 0) {\n return {\n valid: false,\n errors: missing.map((key: string) => `Missing required field: ${key}`),\n }\n }\n\n return { valid: true }\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"component-registry.js","sources":["../../src/services/component-registry.ts"],"sourcesContent":["/**\n * Component Registry Service\n * Phase 0: Static registry with Quickchart and Table definitions\n * Phase 1: Dynamic registry populated from /api/mcp/tools/list\n *\n * Provides component schemas for LLM prompt engineering\n */\n\nimport type { ComponentRegistryEntry, ComponentType } from '../types'\nimport { DEFAULT_RESOURCE_LIMITS } from './validation'\n\n/**\n * Quickchart Component Registry Entry\n * Based on Quickchart API documentation\n */\nexport const QuickchartRegistry: ComponentRegistryEntry = {\n type: 'chart',\n name: 'Quickchart',\n description:\n 'Render charts using Quickchart.io API. Supports bar, line, pie, doughnut, radar, and scatter charts. Best for visualizing numerical data with 2-10 data series and up to 1000 data points.',\n schema: {\n type: 'object',\n properties: {\n type: {\n type: 'string',\n enum: ['bar', 'line', 'pie', 'doughnut', 'radar', 'scatter'],\n description: 'Chart type',\n },\n title: {\n type: 'string',\n description: 'Chart title (optional)',\n },\n data: {\n type: 'object',\n properties: {\n labels: {\n type: 'array',\n items: { type: 'string' },\n description: 'X-axis labels',\n },\n datasets: {\n type: 'array',\n items: {\n type: 'object',\n properties: {\n label: { type: 'string' },\n data: {\n type: 'array',\n items: { type: 'number' },\n },\n backgroundColor: {\n oneOf: [{ type: 'string' }, { type: 'array', items: { type: 'string' } }],\n },\n borderColor: {\n oneOf: [{ type: 'string' }, { type: 'array', items: { type: 'string' } }],\n },\n borderWidth: { type: 'number' },\n },\n required: ['label', 'data'],\n },\n },\n },\n required: ['labels', 'datasets'],\n },\n options: {\n type: 'object',\n description: 'Chart.js options for customization',\n },\n },\n required: ['type', 'data'],\n },\n examples: [\n {\n query: 'Show me document types distribution',\n component: {\n id: 'example-bar-1',\n type: 'chart',\n position: { colStart: 1, colSpan: 6 },\n params: {\n type: 'bar',\n title: 'Document Types',\n data: {\n labels: ['PDF', 'DOCX', 'TXT', 'XLSX'],\n datasets: [\n {\n label: 'Count',\n data: [245, 189, 123, 98],\n backgroundColor: ['rgba(59, 130, 246, 0.8)'],\n },\n ],\n },\n },\n },\n },\n {\n query: 'Display upload trends over the last week',\n component: {\n id: 'example-line-1',\n type: 'chart',\n position: { colStart: 1, colSpan: 6 },\n params: {\n type: 'line',\n title: 'Upload Trends',\n data: {\n labels: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'],\n datasets: [\n {\n label: 'Uploads',\n data: [42, 38, 51, 47, 63, 29, 15],\n borderColor: 'rgb(59, 130, 246)',\n },\n ],\n },\n options: {\n tension: 0.4,\n },\n },\n },\n },\n ],\n limits: DEFAULT_RESOURCE_LIMITS,\n}\n\n/**\n * Table Component Registry Entry\n */\nexport const TableRegistry: ComponentRegistryEntry = {\n type: 'table',\n name: 'DataTable',\n description:\n 'Render tabular data with sortable columns and pagination. Best for displaying structured records with up to 100 rows. Supports column width customization and cell formatting.',\n schema: {\n type: 'object',\n properties: {\n title: {\n type: 'string',\n description: 'Table title (optional)',\n },\n columns: {\n type: 'array',\n items: {\n type: 'object',\n properties: {\n key: { type: 'string', description: 'Data key for this column' },\n label: { type: 'string', description: 'Column header label' },\n sortable: { type: 'boolean', description: 'Whether column is sortable' },\n width: { type: 'string', description: 'CSS width (e.g., \"30%\")' },\n },\n required: ['key', 'label'],\n },\n minItems: 1,\n },\n rows: {\n type: 'array',\n items: {\n type: 'object',\n description: 'Row data matching column keys',\n },\n maxItems: 100,\n },\n pagination: {\n type: 'object',\n properties: {\n currentPage: { type: 'number' },\n pageSize: { type: 'number' },\n totalRows: { type: 'number' },\n },\n },\n },\n required: ['columns', 'rows'],\n },\n examples: [\n {\n query: 'Show me the most recent documents',\n component: {\n id: 'example-table-1',\n type: 'table',\n position: { colStart: 1, colSpan: 8 },\n params: {\n title: 'Recent Documents',\n columns: [\n { key: 'name', label: 'Name', sortable: true, width: '40%' },\n { key: 'type', label: 'Type', sortable: true, width: '15%' },\n { key: 'size', label: 'Size', width: '15%' },\n { key: 'modified', label: 'Modified', sortable: true, width: '30%' },\n ],\n rows: [\n { name: 'Report.pdf', type: 'PDF', size: '2.4 MB', modified: '2 hours ago' },\n { name: 'Slides.pptx', type: 'PPTX', size: '8.7 MB', modified: '1 day ago' },\n ],\n },\n },\n },\n ],\n limits: DEFAULT_RESOURCE_LIMITS,\n}\n\n/**\n * Metric Card Component Registry Entry\n */\nexport const MetricRegistry: ComponentRegistryEntry = {\n type: 'metric',\n name: 'MetricCard',\n description:\n 'Display a single metric with optional trend indicator. Best for KPIs, statistics, and summary numbers. Supports trend direction (up/down/neutral) and subtitles.',\n schema: {\n type: 'object',\n properties: {\n title: {\n type: 'string',\n description: 'Metric title',\n },\n value: {\n oneOf: [{ type: 'string' }, { type: 'number' }],\n description: 'Metric value',\n },\n unit: {\n type: 'string',\n description: 'Unit of measurement (optional)',\n },\n trend: {\n type: 'object',\n properties: {\n value: { type: 'number', description: 'Percentage change' },\n direction: { type: 'string', enum: ['up', 'down', 'neutral'] },\n },\n },\n subtitle: {\n type: 'string',\n description: 'Additional context (optional)',\n },\n },\n required: ['title', 'value'],\n },\n examples: [\n {\n query: 'Show total document count',\n component: {\n id: 'example-metric-1',\n type: 'metric',\n position: { colStart: 1, colSpan: 3 },\n params: {\n title: 'Total Documents',\n value: '1,247',\n trend: {\n value: 12.5,\n direction: 'up',\n },\n subtitle: '+142 this month',\n },\n },\n },\n ],\n limits: {\n maxDataPoints: 1,\n maxTableRows: 1,\n maxPayloadSize: 5 * 1024, // 5KB\n renderTimeout: 1000, // 1s\n },\n}\n\n/**\n * Text Component Registry Entry\n */\nexport const TextRegistry: ComponentRegistryEntry = {\n type: 'text',\n name: 'TextBlock',\n description:\n 'Render text content with optional markdown support. Best for explanations, summaries, and context. Supports basic HTML formatting.',\n schema: {\n type: 'object',\n properties: {\n content: {\n type: 'string',\n description: 'Text content (HTML allowed, will be sanitized)',\n },\n markdown: {\n type: 'boolean',\n description: 'Whether content is markdown (not yet implemented)',\n },\n className: {\n type: 'string',\n description: 'Custom CSS classes',\n },\n },\n required: ['content'],\n },\n examples: [\n {\n query: 'Explain the document distribution',\n component: {\n id: 'example-text-1',\n type: 'text',\n position: { colStart: 1, colSpan: 12 },\n params: {\n content:\n '<p>Your document library contains <strong>1,247 files</strong> across 5 different formats. PDFs represent the largest category at 35% of total storage.</p>',\n },\n },\n },\n ],\n limits: {\n maxDataPoints: 1,\n maxTableRows: 1,\n maxPayloadSize: 10 * 1024, // 10KB\n renderTimeout: 1000, // 1s\n },\n}\n\n// ============================================================================\n// Sprint 4: Additional Component Registry Entries\n// ============================================================================\n\n/**\n * Grid Component Registry Entry\n * Nested CSS Grid layout for organizing multiple components\n */\nexport const GridRegistry: ComponentRegistryEntry = {\n type: 'grid',\n name: 'GridLayout',\n description:\n 'Nested CSS Grid layout for organizing multiple components. Supports named areas, responsive columns (1-12), and custom gap spacing. Best for complex dashboard layouts and template builder.',\n schema: {\n type: 'object',\n properties: {\n columns: {\n type: 'number',\n description: 'Number of columns (default: 12)',\n },\n gap: {\n type: 'string',\n description: 'Gap between items (e.g., \"1rem\")',\n },\n minRowHeight: {\n type: 'string',\n description: 'Minimum row height (optional)',\n },\n areas: {\n type: 'array',\n items: {\n type: 'array',\n items: { type: 'string' },\n },\n description: 'CSS Grid template areas for named regions',\n },\n children: {\n type: 'array',\n items: { type: 'object' },\n description: 'Child UIComponents to render within the grid',\n },\n },\n required: ['children'],\n },\n examples: [],\n limits: DEFAULT_RESOURCE_LIMITS,\n}\n\n/**\n * Action Component Registry Entry\n * Interactive button or link that triggers tool calls\n */\nexport const ActionRegistry: ComponentRegistryEntry = {\n type: 'action',\n name: 'ActionButton',\n description:\n 'Interactive button or link that triggers tool calls or navigation. Best for user interactions, form submissions, and workflow triggers.',\n schema: {\n type: 'object',\n properties: {\n label: {\n type: 'string',\n description: 'Button text',\n },\n type: {\n type: 'string',\n enum: ['button', 'link'],\n description: 'Render as button or link',\n },\n action: {\n type: 'string',\n enum: ['tool-call', 'link', 'submit'],\n description: 'Action type to perform',\n },\n toolName: {\n type: 'string',\n description: 'Tool name to call (for tool-call action)',\n },\n params: {\n type: 'object',\n description: 'Parameters to pass to the tool',\n },\n url: {\n type: 'string',\n description: 'URL for link action',\n },\n variant: {\n type: 'string',\n enum: ['primary', 'secondary', 'outline', 'ghost', 'danger'],\n description: 'Visual style variant',\n },\n size: {\n type: 'string',\n enum: ['sm', 'md', 'lg'],\n description: 'Button size',\n },\n disabled: {\n type: 'boolean',\n description: 'Whether the action is disabled',\n },\n },\n required: ['label', 'type', 'action'],\n },\n examples: [],\n limits: DEFAULT_RESOURCE_LIMITS,\n}\n\n/**\n * Footer Component Registry Entry\n * Display execution metadata like timing and source count\n */\nexport const FooterRegistry: ComponentRegistryEntry = {\n type: 'footer',\n name: 'FooterSection',\n description:\n 'Footer section displaying execution metadata. Best for showing timing, model info, and source counts. Auto-injected by layouts when metadata is provided.',\n schema: {\n type: 'object',\n properties: {\n poweredBy: {\n type: 'string',\n description: 'Powered by text (optional)',\n },\n executionTime: {\n type: 'number',\n description: 'Execution time in milliseconds',\n },\n model: {\n type: 'string',\n description: 'LLM model used',\n },\n sourceCount: {\n type: 'number',\n description: 'Number of sources used',\n },\n customText: {\n type: 'string',\n description: 'Custom footer text',\n },\n links: {\n type: 'array',\n items: {\n type: 'object',\n properties: {\n label: { type: 'string' },\n url: { type: 'string' },\n },\n },\n description: 'Footer links',\n },\n },\n },\n examples: [],\n limits: {\n maxDataPoints: 1,\n maxTableRows: 1,\n maxPayloadSize: 5 * 1024,\n renderTimeout: 1000,\n },\n}\n\n/**\n * Carousel Component Registry Entry\n * Display multiple items with horizontal scrolling\n */\nexport const CarouselRegistry: ComponentRegistryEntry = {\n type: 'carousel',\n name: 'Carousel',\n description:\n 'Horizontal carousel for displaying multiple items with snap scrolling and navigation buttons. Best for showcasing related content, image galleries, or card collections.',\n schema: {\n type: 'object',\n properties: {\n items: {\n type: 'array',\n items: { type: 'object' },\n description: 'Array of UIComponents to display in carousel',\n },\n height: {\n type: 'string',\n description: 'Carousel height (optional)',\n },\n },\n required: ['items'],\n },\n examples: [],\n limits: DEFAULT_RESOURCE_LIMITS,\n}\n\n/**\n * Artifact Component Registry Entry\n * Display downloadable artifacts like generated files\n */\nexport const ArtifactRegistry: ComponentRegistryEntry = {\n type: 'artifact',\n name: 'Artifact',\n description:\n 'Display downloadable artifacts like generated files or exports. Shows filename, size, and download button. Best for CSV exports, PDF reports, and generated documents.',\n schema: {\n type: 'object',\n properties: {\n url: {\n type: 'string',\n description: 'Download URL for the artifact',\n },\n filename: {\n type: 'string',\n description: 'Display filename',\n },\n mimeType: {\n type: 'string',\n description: 'MIME type (e.g., \"text/csv\", \"application/pdf\")',\n },\n size: {\n type: 'number',\n description: 'File size in bytes',\n },\n description: {\n type: 'string',\n description: 'Description of the artifact',\n },\n },\n required: ['url', 'filename', 'mimeType'],\n },\n examples: [],\n limits: {\n maxDataPoints: 1,\n maxTableRows: 1,\n maxPayloadSize: 5 * 1024,\n renderTimeout: 1000,\n },\n}\n\n/**\n * Code Block Registry Entry\n */\nexport const CodeRegistry: ComponentRegistryEntry = {\n type: 'code',\n name: 'CodeBlock',\n description:\n 'Render syntax-highlighted code blocks with line numbers, copy button, and word wrap toggle. Supports all languages via highlight.js auto-detection. Best for displaying source code, configuration files, CLI output, or API responses.',\n schema: {\n type: 'object',\n properties: {\n code: { type: 'string', description: 'The code content to display' },\n language: { type: 'string', description: 'Programming language for syntax highlighting (auto-detected if omitted)' },\n filename: { type: 'string', description: 'Filename shown in header bar' },\n showLineNumbers: { type: 'boolean', description: 'Show line numbers (default: true)' },\n startLine: { type: 'number', description: 'Starting line number (default: 1)' },\n maxHeight: { type: 'string', description: 'CSS max-height for scrollable code blocks' },\n theme: { type: 'string', enum: ['light', 'dark'], description: 'Color theme (follows system preference by default)' },\n },\n required: ['code'],\n },\n examples: [\n {\n query: 'Show me how to connect to the API',\n component: {\n id: 'example-code-1',\n type: 'code',\n position: { colStart: 1, colSpan: 8 },\n params: {\n code: 'const client = new MCPClient({ url: \"https://api.example.com\" });\\nawait client.connect();\\nconst result = await client.query(\"SELECT * FROM documents\");',\n language: 'typescript',\n filename: 'example.ts',\n },\n },\n },\n ],\n limits: DEFAULT_RESOURCE_LIMITS,\n}\n\n/**\n * Map Registry Entry\n */\nexport const MapRegistry: ComponentRegistryEntry = {\n type: 'map',\n name: 'InteractiveMap',\n description:\n 'Render interactive maps with markers using Leaflet. Supports marker clustering, custom tile layers, and auto-fitting bounds. Best for displaying geographic data with up to 1000 markers.',\n schema: {\n type: 'object',\n properties: {\n center: { type: 'array', items: { type: 'number' }, minItems: 2, maxItems: 2, description: 'Map center [lat, lng]' },\n zoom: { type: 'number', description: 'Zoom level (1-18, default: 13)' },\n markers: {\n type: 'array',\n items: {\n type: 'object',\n properties: {\n position: { type: 'array', items: { type: 'number' }, minItems: 2, maxItems: 2 },\n title: { type: 'string' },\n popup: { type: 'string' },\n },\n required: ['position'],\n },\n },\n height: { type: 'string', description: 'CSS height (default: 400px)' },\n fitBounds: { type: 'boolean', description: 'Auto-fit to show all markers' },\n clustering: { type: 'boolean', description: 'Enable marker clustering for large datasets' },\n },\n required: [],\n },\n examples: [\n {\n query: 'Show office locations on a map',\n component: {\n id: 'example-map-1',\n type: 'map',\n position: { colStart: 1, colSpan: 12 },\n params: {\n center: [48.8566, 2.3522],\n zoom: 5,\n markers: [\n { position: [48.8566, 2.3522], tooltip: 'Paris', popup: 'HQ — 120 employees' },\n { position: [51.5074, -0.1278], tooltip: 'London', popup: 'UK Office — 45 employees' },\n ],\n fitBounds: true,\n },\n },\n },\n ],\n limits: DEFAULT_RESOURCE_LIMITS,\n}\n\n/**\n * Form Registry Entry\n */\nexport const FormRegistry: ComponentRegistryEntry = {\n type: 'form',\n name: 'Form',\n description:\n 'Render interactive forms with text inputs, selects, checkboxes, date pickers, and conditional fields. Supports persistence, validation, and submit actions that trigger MCP tool calls.',\n schema: {\n type: 'object',\n properties: {\n title: { type: 'string', description: 'Form title' },\n fields: {\n type: 'array',\n items: {\n type: 'object',\n properties: {\n name: { type: 'string' },\n label: { type: 'string' },\n type: { type: 'string', enum: ['text', 'number', 'email', 'password', 'textarea', 'select', 'checkbox', 'radio', 'date'] },\n required: { type: 'boolean' },\n placeholder: { type: 'string' },\n options: { type: 'array', items: { type: 'object', properties: { label: { type: 'string' }, value: { type: 'string' } } } },\n },\n required: ['name', 'label', 'type'],\n },\n },\n submitLabel: { type: 'string', description: 'Submit button text (default: \"Submit\")' },\n layout: { type: 'string', enum: ['vertical', 'horizontal', 'inline'] },\n },\n required: ['fields'],\n },\n examples: [\n {\n query: 'Create a document upload form',\n component: {\n id: 'example-form-1',\n type: 'form',\n position: { colStart: 1, colSpan: 6 },\n params: {\n title: 'Upload Document',\n fields: [\n { name: 'title', label: 'Document Title', type: 'text', required: true },\n { name: 'category', label: 'Category', type: 'select', options: [{ label: 'Report', value: 'report' }, { label: 'Invoice', value: 'invoice' }] },\n { name: 'notes', label: 'Notes', type: 'textarea' },\n ],\n submitLabel: 'Upload',\n },\n },\n },\n ],\n limits: DEFAULT_RESOURCE_LIMITS,\n}\n\n/**\n * Modal Registry Entry\n */\nexport const ModalRegistry: ComponentRegistryEntry = {\n type: 'modal',\n name: 'Modal',\n description:\n 'Render a dialog overlay with Portal rendering. Supports sizes from small to fullscreen, close on Escape/backdrop, and nested content. Best for confirmations, detail views, and focused interactions.',\n schema: {\n type: 'object',\n properties: {\n title: { type: 'string', description: 'Modal header title' },\n size: { type: 'string', enum: ['sm', 'md', 'lg', 'xl', 'full'], description: 'Modal width (default: md)' },\n showClose: { type: 'boolean', description: 'Show close button (default: true)' },\n closeOnEscape: { type: 'boolean', description: 'Close on Escape key (default: true)' },\n closeOnBackdrop: { type: 'boolean', description: 'Close on backdrop click (default: true)' },\n maxHeight: { type: 'string', description: 'CSS max-height for scrollable content' },\n },\n required: [],\n },\n examples: [\n {\n query: 'Show document details in a dialog',\n component: {\n id: 'example-modal-1',\n type: 'modal',\n position: { colStart: 1, colSpan: 12 },\n params: {\n title: 'Document Details',\n size: 'lg',\n },\n },\n },\n ],\n limits: DEFAULT_RESOURCE_LIMITS,\n}\n\n/**\n * Action Group Registry Entry\n */\nexport const ActionGroupRegistry: ComponentRegistryEntry = {\n type: 'action-group',\n name: 'ActionGroup',\n description:\n 'Render a group of action buttons in horizontal, vertical, or grid layout. Each action triggers an MCP tool call. Best for presenting multiple related actions like CRUD operations or workflow steps.',\n schema: {\n type: 'object',\n properties: {\n actions: {\n type: 'array',\n items: {\n type: 'object',\n properties: {\n label: { type: 'string' },\n toolName: { type: 'string' },\n params: { type: 'object' },\n variant: { type: 'string', enum: ['primary', 'secondary', 'danger', 'ghost'] },\n icon: { type: 'string' },\n },\n required: ['label', 'toolName'],\n },\n },\n layout: { type: 'string', enum: ['horizontal', 'vertical', 'grid'], description: 'Button layout' },\n label: { type: 'string', description: 'Group label' },\n },\n required: ['actions'],\n },\n examples: [\n {\n query: 'Show actions for this document',\n component: {\n id: 'example-action-group-1',\n type: 'action-group',\n position: { colStart: 1, colSpan: 6 },\n params: {\n label: 'Document Actions',\n actions: [\n { label: 'Download', type: 'button', action: 'tool-call', toolName: 'document_download', params: { id: '123' }, variant: 'primary' },\n { label: 'Share', type: 'button', action: 'tool-call', toolName: 'document_share', params: { id: '123' }, variant: 'secondary' },\n { label: 'Delete', type: 'button', action: 'tool-call', toolName: 'document_delete', params: { id: '123' }, variant: 'danger' },\n ],\n layout: 'horizontal',\n },\n },\n },\n ],\n limits: DEFAULT_RESOURCE_LIMITS,\n}\n\n/**\n * Image Gallery Registry Entry\n */\nexport const ImageGalleryRegistry: ComponentRegistryEntry = {\n type: 'image-gallery',\n name: 'ImageGallery',\n description:\n 'Render a grid of images with lightbox overlay for fullscreen viewing. Supports captions, configurable columns, aspect ratios, and keyboard navigation in lightbox mode.',\n schema: {\n type: 'object',\n properties: {\n title: { type: 'string', description: 'Gallery title' },\n images: {\n type: 'array',\n items: {\n type: 'object',\n properties: {\n url: { type: 'string', description: 'Image URL' },\n alt: { type: 'string', description: 'Alt text' },\n caption: { type: 'string', description: 'Caption text' },\n thumbnail: { type: 'string', description: 'Thumbnail URL (optional, falls back to url)' },\n },\n required: ['url'],\n },\n },\n columns: { type: 'number', enum: [2, 3, 4, 5], description: 'Grid columns (default: 3)' },\n aspectRatio: { type: 'string', enum: ['1:1', '16:9', '4:3', 'auto'] },\n lightbox: { type: 'boolean', description: 'Enable lightbox overlay (default: true)' },\n },\n required: ['images'],\n },\n examples: [\n {\n query: 'Show document thumbnails',\n component: {\n id: 'example-gallery-1',\n type: 'image-gallery',\n position: { colStart: 1, colSpan: 12 },\n params: {\n title: 'Recent Documents',\n images: [\n { url: '/thumbnails/doc1.png', alt: 'Q4 Report', caption: 'Q4 Report — 24 pages' },\n { url: '/thumbnails/doc2.png', alt: 'Invoice #4521', caption: 'Invoice #4521' },\n ],\n columns: 4,\n },\n },\n },\n ],\n limits: DEFAULT_RESOURCE_LIMITS,\n}\n\n/**\n * Video Registry Entry\n */\nexport const VideoRegistry: ComponentRegistryEntry = {\n type: 'video',\n name: 'Video',\n description:\n 'Embed video from YouTube, Vimeo, or direct URLs. Auto-detects provider from URL and renders appropriate embed. Supports aspect ratios, autoplay, and start time.',\n schema: {\n type: 'object',\n properties: {\n url: { type: 'string', description: 'Video URL (YouTube, Vimeo, or direct)' },\n title: { type: 'string', description: 'Video title' },\n caption: { type: 'string', description: 'Caption below video' },\n aspectRatio: { type: 'string', enum: ['16:9', '4:3', '1:1', '21:9'], description: 'Aspect ratio (default: 16:9)' },\n autoplay: { type: 'boolean', description: 'Auto-play video' },\n startTime: { type: 'number', description: 'Start time in seconds' },\n },\n required: ['url'],\n },\n examples: [\n {\n query: 'Show the product demo video',\n component: {\n id: 'example-video-1',\n type: 'video',\n position: { colStart: 1, colSpan: 8 },\n params: {\n url: 'https://www.youtube.com/watch?v=dQw4w9WgXcQ',\n title: 'Product Demo',\n aspectRatio: '16:9',\n },\n },\n },\n ],\n limits: DEFAULT_RESOURCE_LIMITS,\n}\n\n/**\n * Iframe Registry Entry\n */\nexport const IframeRegistry: ComponentRegistryEntry = {\n type: 'iframe',\n name: 'Iframe',\n description:\n 'Embed external content via sandboxed iframe. Domain whitelist enforced for security. Supports Mermaid diagrams, Excalidraw, GitHub Gists, Figma, and 60+ whitelisted domains.',\n schema: {\n type: 'object',\n properties: {\n url: { type: 'string', description: 'URL to embed (must be on whitelist)' },\n title: { type: 'string', description: 'Iframe title for accessibility' },\n height: { type: 'string', description: 'CSS height (default: 400px)' },\n sandbox: { type: 'string', description: 'Sandbox attribute (default: restrictive)' },\n },\n required: ['url'],\n },\n examples: [\n {\n query: 'Show the architecture diagram',\n component: {\n id: 'example-iframe-1',\n type: 'iframe',\n position: { colStart: 1, colSpan: 12 },\n params: {\n url: 'https://mermaid.ink/svg/graph+TD;A-->B;B-->C',\n title: 'Architecture Diagram',\n height: '500px',\n },\n },\n },\n ],\n limits: DEFAULT_RESOURCE_LIMITS,\n}\n\n/**\n * Image Registry Entry\n */\nexport const ImageRegistry: ComponentRegistryEntry = {\n type: 'image',\n name: 'Image',\n description:\n 'Render a single image with optional alt text, caption, and link. Best for logos, screenshots, diagrams, or any standalone visual content.',\n schema: {\n type: 'object',\n properties: {\n url: { type: 'string', description: 'Image URL' },\n alt: { type: 'string', description: 'Alt text for accessibility' },\n title: { type: 'string', description: 'Image title / heading' },\n width: { type: 'string', description: 'CSS width' },\n height: { type: 'string', description: 'CSS height' },\n },\n required: ['url'],\n },\n examples: [\n {\n query: 'Show the company logo',\n component: {\n id: 'example-image-1',\n type: 'image',\n position: { colStart: 1, colSpan: 4 },\n params: {\n url: '/images/logo.png',\n alt: 'Company Logo',\n } as any,\n },\n },\n ],\n limits: DEFAULT_RESOURCE_LIMITS,\n}\n\n/**\n * Link Registry Entry\n */\nexport const LinkRegistry: ComponentRegistryEntry = {\n type: 'link',\n name: 'Link',\n description:\n 'Render a styled link card with title, description, and URL. Best for navigation, references, and external resource links.',\n schema: {\n type: 'object',\n properties: {\n url: { type: 'string', description: 'Link destination URL' },\n label: { type: 'string', description: 'Link display text' },\n description: { type: 'string', description: 'Link description' },\n icon: { type: 'string', description: 'Icon identifier' },\n },\n required: ['url', 'label'],\n },\n examples: [\n {\n query: 'Link to the API documentation',\n component: {\n id: 'example-link-1',\n type: 'link',\n position: { colStart: 1, colSpan: 4 },\n params: {\n url: 'https://docs.example.com/api',\n label: 'API Documentation',\n description: 'Full reference for the REST API',\n } as any,\n },\n },\n ],\n limits: DEFAULT_RESOURCE_LIMITS,\n}\n\n/**\n * Component Registry - All components indexed by type\n */\nexport const ComponentRegistry: Map<ComponentType, ComponentRegistryEntry> = new Map([\n ['chart', QuickchartRegistry],\n ['table', TableRegistry],\n ['metric', MetricRegistry],\n ['text', TextRegistry],\n // Sprint 4 additions\n ['grid', GridRegistry],\n ['action', ActionRegistry],\n ['footer', FooterRegistry],\n ['carousel', CarouselRegistry],\n ['artifact', ArtifactRegistry],\n // v2.2.5: Complete registry\n ['code', CodeRegistry],\n ['map', MapRegistry],\n ['form', FormRegistry],\n ['modal', ModalRegistry],\n ['action-group', ActionGroupRegistry],\n ['image-gallery', ImageGalleryRegistry],\n ['video', VideoRegistry],\n ['iframe', IframeRegistry],\n ['image', ImageRegistry],\n ['link', LinkRegistry],\n])\n\n/**\n * Get component registry entry by type\n */\nexport function getComponentEntry(type: ComponentType): ComponentRegistryEntry | undefined {\n return ComponentRegistry.get(type)\n}\n\n/**\n * Get all component types\n */\nexport function getAllComponentTypes(): ComponentType[] {\n return Array.from(ComponentRegistry.keys())\n}\n\n/**\n * Get registry as JSON for LLM context\n */\nexport function getRegistryForLLM(): string {\n const entries = Array.from(ComponentRegistry.values()).map((entry) => ({\n type: entry.type,\n name: entry.name,\n description: entry.description,\n schema: entry.schema,\n examples: entry.examples.map((ex) => ({\n query: ex.query,\n component: ex.component,\n })),\n limits: entry.limits,\n }))\n\n return JSON.stringify(entries, null, 2)\n}\n\n/**\n * Validate component against registry schema\n * (Future: Use Zod for runtime validation)\n */\nexport function validateAgainstRegistry(\n componentType: ComponentType,\n params: any\n): { valid: boolean; errors?: string[]; warnings?: string[] } {\n const entry = getComponentEntry(componentType)\n if (!entry) {\n // Warn but don't block — renderer may exist even without registry entry\n return { valid: true, warnings: [`No registry entry for type: ${componentType}`] }\n }\n\n // Basic validation (Phase 1 will add Zod schema validation)\n const required = entry.schema.required || []\n const missing = required.filter((key: string) => !(key in params))\n\n if (missing.length > 0) {\n return {\n valid: false,\n errors: missing.map((key: string) => `Missing required field: ${key}`),\n }\n }\n\n return { valid: true }\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -112,7 +112,18 @@ const DEFAULT_IFRAME_DOMAINS = [
|
|
|
112
112
|
"amazon.co.jp",
|
|
113
113
|
"images-amazon.com",
|
|
114
114
|
"media-amazon.com",
|
|
115
|
-
"ws-na.amazon-adsystem.com"
|
|
115
|
+
"ws-na.amazon-adsystem.com",
|
|
116
|
+
// MCP Connectors — embed-capable services (v2.2.7)
|
|
117
|
+
"gamma.app",
|
|
118
|
+
"www.gamma.app",
|
|
119
|
+
"app.hubspot.com",
|
|
120
|
+
"share.hubspot.com",
|
|
121
|
+
"www.data.gouv.fr",
|
|
122
|
+
"data.gouv.fr",
|
|
123
|
+
"clinicaltrials.gov",
|
|
124
|
+
"www.clinicaltrials.gov",
|
|
125
|
+
"linear.app",
|
|
126
|
+
"www.linear.app"
|
|
116
127
|
];
|
|
117
128
|
function validateGridPosition(position) {
|
|
118
129
|
const errors = [];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validation.cjs","sources":["../../src/services/validation.ts"],"sourcesContent":["/**\n * Component Validation Service\n * Phase 0: Resource Limits & Schema Validation\n *\n * Validates LLM-generated components against:\n * - JSON schema\n * - Resource limits (data points, payload size, grid bounds)\n * - Security constraints (domain whitelist, XSS prevention)\n */\n\nimport type {\n UIComponent,\n UILayout,\n ValidationResult,\n ResourceLimits,\n ChartComponentParams,\n TableComponentParams,\n FormFieldParams,\n IframePolicy,\n ValidationOptions,\n ComponentType,\n} from '../types'\n\n/**\n * All known ComponentType values — used to distinguish known-but-unvalidated\n * types (pass through) from truly unknown strings (reject).\n */\nconst KNOWN_COMPONENT_TYPES: Set<string> = new Set<ComponentType>([\n 'chart', 'table', 'metric', 'text', 'grid', 'iframe', 'image', 'link',\n 'action', 'footer', 'carousel', 'artifact', 'form', 'modal',\n 'action-group', 'image-gallery', 'video', 'code', 'map',\n])\n\n/**\n * Default resource limits (configurable via env)\n */\nexport const DEFAULT_RESOURCE_LIMITS: ResourceLimits = {\n maxDataPoints: 1000,\n maxTableRows: 100,\n maxPayloadSize: 50 * 1024, // 50KB\n renderTimeout: 5000, // 5 seconds\n}\n\n/**\n * Default allowed iframe domains (whitelist)\n * Must match CSP frame-src directive\n * Updated Sprint 7: Added code, design, docs, and map providers\n *\n * This list is exported for transparency and can be extended via ValidationOptions\n */\nexport const DEFAULT_IFRAME_DOMAINS = [\n // Charts\n 'quickchart.io',\n 'www.quickchart.io',\n\n // Deposium\n 'deposium.com',\n 'deposium.vip',\n 'deposium.ai',\n\n // Development\n 'localhost',\n\n // Video providers (Sprint 5)\n 'youtube.com',\n 'www.youtube.com',\n 'youtube-nocookie.com',\n 'www.youtube-nocookie.com',\n 'youtu.be',\n 'vimeo.com',\n 'player.vimeo.com',\n\n // Code playgrounds (Sprint 7)\n 'codepen.io',\n 'codesandbox.io',\n 'stackblitz.com',\n 'jsfiddle.net',\n\n // Design tools (Sprint 7)\n 'figma.com',\n 'www.figma.com',\n 'miro.com',\n\n // Google services (Sprint 7)\n 'docs.google.com',\n 'drive.google.com',\n 'sheets.google.com',\n 'slides.google.com',\n 'maps.google.com',\n 'www.google.com',\n 'datastudio.google.com',\n 'lookerstudio.google.com',\n\n // Productivity (Sprint 7)\n 'airtable.com',\n 'notion.so',\n 'www.notion.so',\n\n // Maps (Sprint 7)\n 'openstreetmap.org',\n 'www.openstreetmap.org',\n\n // Analytics/Dashboards (Sprint 7)\n 'public.tableau.com',\n 'app.powerbi.com',\n 'observablehq.com',\n\n // Diagrams & Whiteboards (v2.0.0)\n 'mermaid.live',\n 'excalidraw.com',\n 'lucidchart.com',\n 'lucid.app',\n\n // Video - Business (v2.0.0)\n 'loom.com',\n 'www.loom.com',\n 'cloudflarestream.com',\n 'streamable.com',\n\n // Code repositories (v2.0.0)\n 'github.com',\n 'gist.github.com',\n 'gitlab.com',\n 'replit.com',\n 'glitch.com',\n\n // Business tools (v2.0.0)\n 'calendly.com',\n 'typeform.com',\n 'cal.com',\n\n // Design (v2.0.0)\n 'canva.com',\n\n // Deploy previews (v2.0.0)\n 'vercel.app',\n 'netlify.app',\n\n // E-commerce (v2.0.0)\n 'amazon.com',\n 'amazon.fr',\n 'amazon.de',\n 'amazon.co.uk',\n 'amazon.es',\n 'amazon.it',\n 'amazon.ca',\n 'amazon.co.jp',\n 'images-amazon.com',\n 'media-amazon.com',\n 'ws-na.amazon-adsystem.com',\n]\n\n/**\n * Validate grid position bounds (1-12 columns)\n */\nexport function validateGridPosition(position: UIComponent['position']): ValidationResult {\n const errors: ValidationResult['errors'] = []\n\n // ✅ PHASE 3 FIX: Defensive check for undefined position\n if (!position) {\n return {\n valid: false,\n errors: [\n {\n path: 'position',\n message: 'Position is required',\n code: 'MISSING_POSITION',\n },\n ],\n }\n }\n\n if (position.colStart < 1 || position.colStart > 12) {\n errors.push({\n path: 'position.colStart',\n message: 'Column start must be between 1 and 12',\n code: 'INVALID_GRID_COL_START',\n })\n }\n\n if (position.colSpan < 1 || position.colSpan > 12) {\n errors.push({\n path: 'position.colSpan',\n message: 'Column span must be between 1 and 12',\n code: 'INVALID_GRID_COL_SPAN',\n })\n }\n\n if (position.colStart + position.colSpan - 1 > 12) {\n errors.push({\n path: 'position',\n message: 'Column start + span exceeds grid width (12)',\n code: 'GRID_OVERFLOW',\n })\n }\n\n if (position.rowStart !== undefined && position.rowStart < 1) {\n errors.push({\n path: 'position.rowStart',\n message: 'Row start must be >= 1',\n code: 'INVALID_GRID_ROW_START',\n })\n }\n\n if (position.rowSpan !== undefined && position.rowSpan < 1) {\n errors.push({\n path: 'position.rowSpan',\n message: 'Row span must be >= 1',\n code: 'INVALID_GRID_ROW_SPAN',\n })\n }\n\n return {\n valid: errors.length === 0,\n errors: errors.length > 0 ? errors : undefined,\n }\n}\n\n/**\n * Validate chart component against resource limits\n */\nexport function validateChartComponent(\n params: ChartComponentParams,\n limits: ResourceLimits = DEFAULT_RESOURCE_LIMITS\n): ValidationResult {\n const errors: ValidationResult['errors'] = []\n\n // Validate data points count\n const totalDataPoints = params.data.datasets.reduce(\n (sum, dataset) => sum + dataset.data.length,\n 0\n )\n\n if (totalDataPoints > limits.maxDataPoints) {\n errors.push({\n path: 'params.data',\n message: `Chart exceeds max data points: ${totalDataPoints} > ${limits.maxDataPoints}`,\n code: 'RESOURCE_LIMIT_EXCEEDED',\n })\n }\n\n // Validate labels match dataset length\n const expectedLength = params.data.labels.length\n for (const [index, dataset] of params.data.datasets.entries()) {\n if (dataset.data.length !== expectedLength) {\n errors.push({\n path: `params.data.datasets[${index}]`,\n message: `Dataset length mismatch: expected ${expectedLength}, got ${dataset.data.length}`,\n code: 'DATA_LENGTH_MISMATCH',\n })\n }\n }\n\n // Validate numeric data\n for (const [index, dataset] of params.data.datasets.entries()) {\n for (const [dataIndex, value] of dataset.data.entries()) {\n if (typeof value !== 'number' || !Number.isFinite(value)) {\n errors.push({\n path: `params.data.datasets[${index}].data[${dataIndex}]`,\n message: `Invalid data value: ${value} (must be finite number)`,\n code: 'INVALID_DATA_TYPE',\n })\n }\n }\n }\n\n return {\n valid: errors.length === 0,\n errors: errors.length > 0 ? errors : undefined,\n }\n}\n\n/**\n * Validate table component against resource limits\n */\nexport function validateTableComponent(\n params: TableComponentParams,\n limits: ResourceLimits = DEFAULT_RESOURCE_LIMITS\n): ValidationResult {\n const errors: ValidationResult['errors'] = []\n\n // Validate row count\n if (params.rows.length > limits.maxTableRows) {\n errors.push({\n path: 'params.rows',\n message: `Table exceeds max rows: ${params.rows.length} > ${limits.maxTableRows}`,\n code: 'RESOURCE_LIMIT_EXCEEDED',\n })\n }\n\n // Validate columns\n if (params.columns.length === 0) {\n errors.push({\n path: 'params.columns',\n message: 'Table must have at least one column',\n code: 'EMPTY_COLUMNS',\n })\n }\n\n // Validate column keys are unique\n const columnKeys = new Set<string>()\n for (const [index, column] of params.columns.entries()) {\n if (columnKeys.has(column.key)) {\n errors.push({\n path: `params.columns[${index}]`,\n message: `Duplicate column key: ${column.key}`,\n code: 'DUPLICATE_COLUMN_KEY',\n })\n }\n columnKeys.add(column.key)\n }\n\n // Validate rows have valid data for defined columns\n for (const [rowIndex, row] of params.rows.entries()) {\n for (const column of params.columns) {\n if (!(column.key in row)) {\n errors.push({\n path: `params.rows[${rowIndex}]`,\n message: `Missing column key: ${column.key}`,\n code: 'MISSING_COLUMN_DATA',\n })\n }\n }\n }\n\n return {\n valid: errors.length === 0,\n errors: errors.length > 0 ? errors : undefined,\n }\n}\n\n/**\n * Validate payload size\n */\nexport function validatePayloadSize(\n component: UIComponent,\n limits: ResourceLimits = DEFAULT_RESOURCE_LIMITS\n): ValidationResult {\n const payloadSize = JSON.stringify(component).length\n\n if (payloadSize > limits.maxPayloadSize) {\n return {\n valid: false,\n errors: [\n {\n path: 'component',\n message: `Payload size exceeds limit: ${payloadSize} > ${limits.maxPayloadSize} bytes`,\n code: 'PAYLOAD_TOO_LARGE',\n },\n ],\n }\n }\n\n return { valid: true }\n}\n\n/**\n * Sanitize string to prevent XSS\n * Basic implementation - DOMPurify used at render time\n */\nexport function sanitizeString(input: string): string {\n return input\n .replace(/<script\\b[^<]*(?:(?!<\\/script>)<[^<]*)*<\\/script>/gi, '')\n .replace(/on\\w+=\"[^\"]*\"/gi, '')\n .replace(/javascript:/gi, '')\n}\n\n/**\n * Validate iframe domain against whitelist\n *\n * @param url - The URL to validate\n * @param options - Optional validation options\n * @param options.policy - 'strict' (default), 'extend', or 'allow-all'\n * @param options.customDomains - Additional domains when policy is 'extend'\n */\nexport function validateIframeDomain(\n url: string,\n options?: { policy?: IframePolicy; customDomains?: string[] }\n): ValidationResult {\n // If allow-all, skip validation\n if (options?.policy === 'allow-all') {\n return { valid: true }\n }\n\n try {\n const parsedUrl = new URL(url)\n const domain = parsedUrl.hostname\n\n // Build effective whitelist\n let effectiveWhitelist = DEFAULT_IFRAME_DOMAINS\n if (options?.policy === 'extend' && options.customDomains) {\n effectiveWhitelist = [...DEFAULT_IFRAME_DOMAINS, ...options.customDomains]\n }\n\n const isAllowed = effectiveWhitelist.some(\n (allowed) => domain === allowed || domain.endsWith(`.${allowed}`) || allowed === 'localhost'\n )\n\n if (!isAllowed) {\n return {\n valid: false,\n errors: [\n {\n path: 'url',\n message: `Domain not whitelisted: ${domain}`,\n code: 'DOMAIN_NOT_WHITELISTED',\n },\n ],\n }\n }\n\n return { valid: true }\n } catch (error) {\n return {\n valid: false,\n errors: [\n {\n path: 'url',\n message: 'Invalid URL format',\n code: 'INVALID_URL',\n },\n ],\n }\n }\n}\n\n/**\n * Validate entire component\n *\n * @param component - The component to validate\n * @param options - Optional validation options (limits, iframePolicy, customIframeDomains)\n */\nexport function validateComponent(\n component: UIComponent,\n options?: ValidationOptions\n): ValidationResult {\n const limits = options?.limits ?? DEFAULT_RESOURCE_LIMITS\n const errors: ValidationResult['errors'] = []\n\n // Validate grid position\n const gridResult = validateGridPosition(component.position)\n if (!gridResult.valid) {\n errors.push(...(gridResult.errors || []))\n }\n\n // Validate payload size\n const sizeResult = validatePayloadSize(component, limits)\n if (!sizeResult.valid) {\n errors.push(...(sizeResult.errors || []))\n }\n\n // Type-specific validation\n switch (component.type) {\n case 'chart': {\n const chartResult = validateChartComponent(component.params as ChartComponentParams, limits)\n if (!chartResult.valid) {\n errors.push(...(chartResult.errors || []))\n }\n break\n }\n\n case 'table': {\n const tableResult = validateTableComponent(component.params as TableComponentParams, limits)\n if (!tableResult.valid) {\n errors.push(...(tableResult.errors || []))\n }\n break\n }\n\n case 'metric': {\n // Basic validation for metrics\n const metricParams = component.params as any\n if (!metricParams.title || !metricParams.value) {\n errors.push({\n path: 'params',\n message: 'Metric must have title and value',\n code: 'INVALID_METRIC',\n })\n }\n break\n }\n\n case 'text': {\n // Basic validation for text\n const textParams = component.params as any\n if (!textParams.content) {\n errors.push({\n path: 'params',\n message: 'Text component must have content',\n code: 'INVALID_TEXT',\n })\n }\n break\n }\n\n case 'iframe': {\n // Basic validation for iframe\n const iframeParams = component.params as any\n if (!iframeParams.url) {\n errors.push({\n path: 'params',\n message: 'Iframe component must have url',\n code: 'INVALID_IFRAME',\n })\n } else {\n // Validate iframe domain against whitelist\n const iframeResult = validateIframeDomain(iframeParams.url, {\n policy: options?.iframePolicy,\n customDomains: options?.customIframeDomains,\n })\n if (!iframeResult.valid) {\n errors.push(...(iframeResult.errors || []))\n }\n }\n break\n }\n\n case 'image': {\n // Basic validation for image\n const imageParams = component.params as any\n if (!imageParams.url) {\n errors.push({\n path: 'params',\n message: 'Image component must have url',\n code: 'INVALID_IMAGE',\n })\n }\n break\n }\n\n case 'link': {\n // Basic validation for link\n const linkParams = component.params as any\n if (!linkParams.url) {\n errors.push({\n path: 'params',\n message: 'Link component must have url',\n code: 'INVALID_LINK',\n })\n }\n break\n }\n\n case 'action': {\n // Basic validation for action\n const actionParams = component.params as any\n if (!actionParams.label) {\n errors.push({\n path: 'params',\n message: 'Action component must have label',\n code: 'INVALID_ACTION',\n })\n }\n break\n }\n\n default:\n // Known types without specific validation pass through — renderer handles errors\n // Truly unknown types (e.g. typos in streamed JSON) are rejected\n if (!KNOWN_COMPONENT_TYPES.has(component.type)) {\n errors.push({\n path: 'type',\n message: `Unknown component type: ${component.type}`,\n code: 'UNKNOWN_COMPONENT_TYPE',\n })\n }\n break\n }\n\n return {\n valid: errors.length === 0,\n errors: errors.length > 0 ? errors : undefined,\n }\n}\n\n/**\n * Validate entire layout\n *\n * @param layout - The layout to validate\n * @param options - Optional validation options (limits, iframePolicy, customIframeDomains)\n */\nexport function validateLayout(\n layout: UILayout,\n options?: ValidationOptions\n): ValidationResult {\n const errors: ValidationResult['errors'] = []\n\n // Validate component count\n if (layout.components.length === 0) {\n errors.push({\n path: 'components',\n message: 'Layout must have at least one component',\n code: 'EMPTY_LAYOUT',\n })\n }\n\n if (layout.components.length > 12) {\n errors.push({\n path: 'components',\n message: `Layout exceeds max components: ${layout.components.length} > 12`,\n code: 'TOO_MANY_COMPONENTS',\n })\n }\n\n // Validate each component\n for (const [index, component] of layout.components.entries()) {\n const result = validateComponent(component, options)\n if (!result.valid) {\n errors.push(\n ...(result.errors?.map((error) => ({\n ...error,\n path: `components[${index}].${error.path}`,\n })) || [])\n )\n }\n }\n\n // Validate grid configuration\n if (layout.grid.columns !== 12) {\n errors.push({\n path: 'grid.columns',\n message: 'Grid must have 12 columns (Bootstrap-like)',\n code: 'INVALID_GRID_COLUMNS',\n })\n }\n\n return {\n valid: errors.length === 0,\n errors: errors.length > 0 ? errors : undefined,\n }\n}\n\n/**\n * Validate a single form field value against field rules\n */\nexport function validateFieldValue(\n value: any,\n field: FormFieldParams\n): { valid: boolean; error?: string } {\n // Required check\n if (field.required) {\n if (value === undefined || value === null || value === '') {\n return { valid: false, error: `${field.label || field.name} is required` }\n }\n if (field.type === 'checkbox' && value !== true) {\n return { valid: false, error: `${field.label || field.name} must be checked` }\n }\n }\n\n // Skip further validation if value is empty and not required\n if (value === undefined || value === null || value === '') {\n return { valid: true }\n }\n\n // Type-specific validation\n switch (field.type) {\n case 'text':\n case 'textarea':\n case 'password':\n if (field.minLength && String(value).length < field.minLength) {\n return { valid: false, error: `Minimum ${field.minLength} characters required` }\n }\n if (field.maxLength && String(value).length > field.maxLength) {\n return { valid: false, error: `Maximum ${field.maxLength} characters allowed` }\n }\n if (field.pattern && !new RegExp(field.pattern).test(String(value))) {\n return { valid: false, error: 'Invalid format' }\n }\n break\n\n case 'email':\n if (!/^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/.test(String(value))) {\n return { valid: false, error: 'Invalid email address' }\n }\n break\n\n case 'number': {\n const numValue = Number(value)\n if (isNaN(numValue)) {\n return { valid: false, error: 'Must be a valid number' }\n }\n if (field.min !== undefined && numValue < field.min) {\n return { valid: false, error: `Minimum value is ${field.min}` }\n }\n if (field.max !== undefined && numValue > field.max) {\n return { valid: false, error: `Maximum value is ${field.max}` }\n }\n break\n }\n\n case 'date':\n if (field.minDate && value < field.minDate) {\n return { valid: false, error: `Date must be after ${field.minDate}` }\n }\n if (field.maxDate && value > field.maxDate) {\n return { valid: false, error: `Date must be before ${field.maxDate}` }\n }\n break\n\n case 'select':\n case 'radio':\n // Validate that value is one of the options\n if (field.options && field.options.length > 0) {\n const validValues = field.options.map((opt) => opt.value)\n if (!validValues.includes(String(value))) {\n return { valid: false, error: 'Please select a valid option' }\n }\n }\n break\n }\n\n return { valid: true }\n}\n\n/**\n * Validate entire form data against field definitions\n */\nexport function validateFormData(\n data: Record<string, any>,\n fields: FormFieldParams[]\n): { valid: boolean; errors: Record<string, string> } {\n const errors: Record<string, string> = {}\n\n for (const field of fields) {\n const result = validateFieldValue(data[field.name], field)\n if (!result.valid && result.error) {\n errors[field.name] = result.error\n }\n }\n\n return {\n valid: Object.keys(errors).length === 0,\n errors,\n }\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"validation.cjs","sources":["../../src/services/validation.ts"],"sourcesContent":["/**\n * Component Validation Service\n * Phase 0: Resource Limits & Schema Validation\n *\n * Validates LLM-generated components against:\n * - JSON schema\n * - Resource limits (data points, payload size, grid bounds)\n * - Security constraints (domain whitelist, XSS prevention)\n */\n\nimport type {\n UIComponent,\n UILayout,\n ValidationResult,\n ResourceLimits,\n ChartComponentParams,\n TableComponentParams,\n FormFieldParams,\n IframePolicy,\n ValidationOptions,\n ComponentType,\n} from '../types'\n\n/**\n * All known ComponentType values — used to distinguish known-but-unvalidated\n * types (pass through) from truly unknown strings (reject).\n */\nconst KNOWN_COMPONENT_TYPES: Set<string> = new Set<ComponentType>([\n 'chart', 'table', 'metric', 'text', 'grid', 'iframe', 'image', 'link',\n 'action', 'footer', 'carousel', 'artifact', 'form', 'modal',\n 'action-group', 'image-gallery', 'video', 'code', 'map',\n])\n\n/**\n * Default resource limits (configurable via env)\n */\nexport const DEFAULT_RESOURCE_LIMITS: ResourceLimits = {\n maxDataPoints: 1000,\n maxTableRows: 100,\n maxPayloadSize: 50 * 1024, // 50KB\n renderTimeout: 5000, // 5 seconds\n}\n\n/**\n * Default allowed iframe domains (whitelist)\n * Must match CSP frame-src directive\n * Updated Sprint 7: Added code, design, docs, and map providers\n *\n * This list is exported for transparency and can be extended via ValidationOptions\n */\nexport const DEFAULT_IFRAME_DOMAINS = [\n // Charts\n 'quickchart.io',\n 'www.quickchart.io',\n\n // Deposium\n 'deposium.com',\n 'deposium.vip',\n 'deposium.ai',\n\n // Development\n 'localhost',\n\n // Video providers (Sprint 5)\n 'youtube.com',\n 'www.youtube.com',\n 'youtube-nocookie.com',\n 'www.youtube-nocookie.com',\n 'youtu.be',\n 'vimeo.com',\n 'player.vimeo.com',\n\n // Code playgrounds (Sprint 7)\n 'codepen.io',\n 'codesandbox.io',\n 'stackblitz.com',\n 'jsfiddle.net',\n\n // Design tools (Sprint 7)\n 'figma.com',\n 'www.figma.com',\n 'miro.com',\n\n // Google services (Sprint 7)\n 'docs.google.com',\n 'drive.google.com',\n 'sheets.google.com',\n 'slides.google.com',\n 'maps.google.com',\n 'www.google.com',\n 'datastudio.google.com',\n 'lookerstudio.google.com',\n\n // Productivity (Sprint 7)\n 'airtable.com',\n 'notion.so',\n 'www.notion.so',\n\n // Maps (Sprint 7)\n 'openstreetmap.org',\n 'www.openstreetmap.org',\n\n // Analytics/Dashboards (Sprint 7)\n 'public.tableau.com',\n 'app.powerbi.com',\n 'observablehq.com',\n\n // Diagrams & Whiteboards (v2.0.0)\n 'mermaid.live',\n 'excalidraw.com',\n 'lucidchart.com',\n 'lucid.app',\n\n // Video - Business (v2.0.0)\n 'loom.com',\n 'www.loom.com',\n 'cloudflarestream.com',\n 'streamable.com',\n\n // Code repositories (v2.0.0)\n 'github.com',\n 'gist.github.com',\n 'gitlab.com',\n 'replit.com',\n 'glitch.com',\n\n // Business tools (v2.0.0)\n 'calendly.com',\n 'typeform.com',\n 'cal.com',\n\n // Design (v2.0.0)\n 'canva.com',\n\n // Deploy previews (v2.0.0)\n 'vercel.app',\n 'netlify.app',\n\n // E-commerce (v2.0.0)\n 'amazon.com',\n 'amazon.fr',\n 'amazon.de',\n 'amazon.co.uk',\n 'amazon.es',\n 'amazon.it',\n 'amazon.ca',\n 'amazon.co.jp',\n 'images-amazon.com',\n 'media-amazon.com',\n 'ws-na.amazon-adsystem.com',\n\n // MCP Connectors — embed-capable services (v2.2.7)\n 'gamma.app',\n 'www.gamma.app',\n 'app.hubspot.com',\n 'share.hubspot.com',\n 'www.data.gouv.fr',\n 'data.gouv.fr',\n 'clinicaltrials.gov',\n 'www.clinicaltrials.gov',\n 'linear.app',\n 'www.linear.app',\n]\n\n/**\n * Validate grid position bounds (1-12 columns)\n */\nexport function validateGridPosition(position: UIComponent['position']): ValidationResult {\n const errors: ValidationResult['errors'] = []\n\n // ✅ PHASE 3 FIX: Defensive check for undefined position\n if (!position) {\n return {\n valid: false,\n errors: [\n {\n path: 'position',\n message: 'Position is required',\n code: 'MISSING_POSITION',\n },\n ],\n }\n }\n\n if (position.colStart < 1 || position.colStart > 12) {\n errors.push({\n path: 'position.colStart',\n message: 'Column start must be between 1 and 12',\n code: 'INVALID_GRID_COL_START',\n })\n }\n\n if (position.colSpan < 1 || position.colSpan > 12) {\n errors.push({\n path: 'position.colSpan',\n message: 'Column span must be between 1 and 12',\n code: 'INVALID_GRID_COL_SPAN',\n })\n }\n\n if (position.colStart + position.colSpan - 1 > 12) {\n errors.push({\n path: 'position',\n message: 'Column start + span exceeds grid width (12)',\n code: 'GRID_OVERFLOW',\n })\n }\n\n if (position.rowStart !== undefined && position.rowStart < 1) {\n errors.push({\n path: 'position.rowStart',\n message: 'Row start must be >= 1',\n code: 'INVALID_GRID_ROW_START',\n })\n }\n\n if (position.rowSpan !== undefined && position.rowSpan < 1) {\n errors.push({\n path: 'position.rowSpan',\n message: 'Row span must be >= 1',\n code: 'INVALID_GRID_ROW_SPAN',\n })\n }\n\n return {\n valid: errors.length === 0,\n errors: errors.length > 0 ? errors : undefined,\n }\n}\n\n/**\n * Validate chart component against resource limits\n */\nexport function validateChartComponent(\n params: ChartComponentParams,\n limits: ResourceLimits = DEFAULT_RESOURCE_LIMITS\n): ValidationResult {\n const errors: ValidationResult['errors'] = []\n\n // Validate data points count\n const totalDataPoints = params.data.datasets.reduce(\n (sum, dataset) => sum + dataset.data.length,\n 0\n )\n\n if (totalDataPoints > limits.maxDataPoints) {\n errors.push({\n path: 'params.data',\n message: `Chart exceeds max data points: ${totalDataPoints} > ${limits.maxDataPoints}`,\n code: 'RESOURCE_LIMIT_EXCEEDED',\n })\n }\n\n // Validate labels match dataset length\n const expectedLength = params.data.labels.length\n for (const [index, dataset] of params.data.datasets.entries()) {\n if (dataset.data.length !== expectedLength) {\n errors.push({\n path: `params.data.datasets[${index}]`,\n message: `Dataset length mismatch: expected ${expectedLength}, got ${dataset.data.length}`,\n code: 'DATA_LENGTH_MISMATCH',\n })\n }\n }\n\n // Validate numeric data\n for (const [index, dataset] of params.data.datasets.entries()) {\n for (const [dataIndex, value] of dataset.data.entries()) {\n if (typeof value !== 'number' || !Number.isFinite(value)) {\n errors.push({\n path: `params.data.datasets[${index}].data[${dataIndex}]`,\n message: `Invalid data value: ${value} (must be finite number)`,\n code: 'INVALID_DATA_TYPE',\n })\n }\n }\n }\n\n return {\n valid: errors.length === 0,\n errors: errors.length > 0 ? errors : undefined,\n }\n}\n\n/**\n * Validate table component against resource limits\n */\nexport function validateTableComponent(\n params: TableComponentParams,\n limits: ResourceLimits = DEFAULT_RESOURCE_LIMITS\n): ValidationResult {\n const errors: ValidationResult['errors'] = []\n\n // Validate row count\n if (params.rows.length > limits.maxTableRows) {\n errors.push({\n path: 'params.rows',\n message: `Table exceeds max rows: ${params.rows.length} > ${limits.maxTableRows}`,\n code: 'RESOURCE_LIMIT_EXCEEDED',\n })\n }\n\n // Validate columns\n if (params.columns.length === 0) {\n errors.push({\n path: 'params.columns',\n message: 'Table must have at least one column',\n code: 'EMPTY_COLUMNS',\n })\n }\n\n // Validate column keys are unique\n const columnKeys = new Set<string>()\n for (const [index, column] of params.columns.entries()) {\n if (columnKeys.has(column.key)) {\n errors.push({\n path: `params.columns[${index}]`,\n message: `Duplicate column key: ${column.key}`,\n code: 'DUPLICATE_COLUMN_KEY',\n })\n }\n columnKeys.add(column.key)\n }\n\n // Validate rows have valid data for defined columns\n for (const [rowIndex, row] of params.rows.entries()) {\n for (const column of params.columns) {\n if (!(column.key in row)) {\n errors.push({\n path: `params.rows[${rowIndex}]`,\n message: `Missing column key: ${column.key}`,\n code: 'MISSING_COLUMN_DATA',\n })\n }\n }\n }\n\n return {\n valid: errors.length === 0,\n errors: errors.length > 0 ? errors : undefined,\n }\n}\n\n/**\n * Validate payload size\n */\nexport function validatePayloadSize(\n component: UIComponent,\n limits: ResourceLimits = DEFAULT_RESOURCE_LIMITS\n): ValidationResult {\n const payloadSize = JSON.stringify(component).length\n\n if (payloadSize > limits.maxPayloadSize) {\n return {\n valid: false,\n errors: [\n {\n path: 'component',\n message: `Payload size exceeds limit: ${payloadSize} > ${limits.maxPayloadSize} bytes`,\n code: 'PAYLOAD_TOO_LARGE',\n },\n ],\n }\n }\n\n return { valid: true }\n}\n\n/**\n * Sanitize string to prevent XSS\n * Basic implementation - DOMPurify used at render time\n */\nexport function sanitizeString(input: string): string {\n return input\n .replace(/<script\\b[^<]*(?:(?!<\\/script>)<[^<]*)*<\\/script>/gi, '')\n .replace(/on\\w+=\"[^\"]*\"/gi, '')\n .replace(/javascript:/gi, '')\n}\n\n/**\n * Validate iframe domain against whitelist\n *\n * @param url - The URL to validate\n * @param options - Optional validation options\n * @param options.policy - 'strict' (default), 'extend', or 'allow-all'\n * @param options.customDomains - Additional domains when policy is 'extend'\n */\nexport function validateIframeDomain(\n url: string,\n options?: { policy?: IframePolicy; customDomains?: string[] }\n): ValidationResult {\n // If allow-all, skip validation\n if (options?.policy === 'allow-all') {\n return { valid: true }\n }\n\n try {\n const parsedUrl = new URL(url)\n const domain = parsedUrl.hostname\n\n // Build effective whitelist\n let effectiveWhitelist = DEFAULT_IFRAME_DOMAINS\n if (options?.policy === 'extend' && options.customDomains) {\n effectiveWhitelist = [...DEFAULT_IFRAME_DOMAINS, ...options.customDomains]\n }\n\n const isAllowed = effectiveWhitelist.some(\n (allowed) => domain === allowed || domain.endsWith(`.${allowed}`) || allowed === 'localhost'\n )\n\n if (!isAllowed) {\n return {\n valid: false,\n errors: [\n {\n path: 'url',\n message: `Domain not whitelisted: ${domain}`,\n code: 'DOMAIN_NOT_WHITELISTED',\n },\n ],\n }\n }\n\n return { valid: true }\n } catch (error) {\n return {\n valid: false,\n errors: [\n {\n path: 'url',\n message: 'Invalid URL format',\n code: 'INVALID_URL',\n },\n ],\n }\n }\n}\n\n/**\n * Validate entire component\n *\n * @param component - The component to validate\n * @param options - Optional validation options (limits, iframePolicy, customIframeDomains)\n */\nexport function validateComponent(\n component: UIComponent,\n options?: ValidationOptions\n): ValidationResult {\n const limits = options?.limits ?? DEFAULT_RESOURCE_LIMITS\n const errors: ValidationResult['errors'] = []\n\n // Validate grid position\n const gridResult = validateGridPosition(component.position)\n if (!gridResult.valid) {\n errors.push(...(gridResult.errors || []))\n }\n\n // Validate payload size\n const sizeResult = validatePayloadSize(component, limits)\n if (!sizeResult.valid) {\n errors.push(...(sizeResult.errors || []))\n }\n\n // Type-specific validation\n switch (component.type) {\n case 'chart': {\n const chartResult = validateChartComponent(component.params as ChartComponentParams, limits)\n if (!chartResult.valid) {\n errors.push(...(chartResult.errors || []))\n }\n break\n }\n\n case 'table': {\n const tableResult = validateTableComponent(component.params as TableComponentParams, limits)\n if (!tableResult.valid) {\n errors.push(...(tableResult.errors || []))\n }\n break\n }\n\n case 'metric': {\n // Basic validation for metrics\n const metricParams = component.params as any\n if (!metricParams.title || !metricParams.value) {\n errors.push({\n path: 'params',\n message: 'Metric must have title and value',\n code: 'INVALID_METRIC',\n })\n }\n break\n }\n\n case 'text': {\n // Basic validation for text\n const textParams = component.params as any\n if (!textParams.content) {\n errors.push({\n path: 'params',\n message: 'Text component must have content',\n code: 'INVALID_TEXT',\n })\n }\n break\n }\n\n case 'iframe': {\n // Basic validation for iframe\n const iframeParams = component.params as any\n if (!iframeParams.url) {\n errors.push({\n path: 'params',\n message: 'Iframe component must have url',\n code: 'INVALID_IFRAME',\n })\n } else {\n // Validate iframe domain against whitelist\n const iframeResult = validateIframeDomain(iframeParams.url, {\n policy: options?.iframePolicy,\n customDomains: options?.customIframeDomains,\n })\n if (!iframeResult.valid) {\n errors.push(...(iframeResult.errors || []))\n }\n }\n break\n }\n\n case 'image': {\n // Basic validation for image\n const imageParams = component.params as any\n if (!imageParams.url) {\n errors.push({\n path: 'params',\n message: 'Image component must have url',\n code: 'INVALID_IMAGE',\n })\n }\n break\n }\n\n case 'link': {\n // Basic validation for link\n const linkParams = component.params as any\n if (!linkParams.url) {\n errors.push({\n path: 'params',\n message: 'Link component must have url',\n code: 'INVALID_LINK',\n })\n }\n break\n }\n\n case 'action': {\n // Basic validation for action\n const actionParams = component.params as any\n if (!actionParams.label) {\n errors.push({\n path: 'params',\n message: 'Action component must have label',\n code: 'INVALID_ACTION',\n })\n }\n break\n }\n\n default:\n // Known types without specific validation pass through — renderer handles errors\n // Truly unknown types (e.g. typos in streamed JSON) are rejected\n if (!KNOWN_COMPONENT_TYPES.has(component.type)) {\n errors.push({\n path: 'type',\n message: `Unknown component type: ${component.type}`,\n code: 'UNKNOWN_COMPONENT_TYPE',\n })\n }\n break\n }\n\n return {\n valid: errors.length === 0,\n errors: errors.length > 0 ? errors : undefined,\n }\n}\n\n/**\n * Validate entire layout\n *\n * @param layout - The layout to validate\n * @param options - Optional validation options (limits, iframePolicy, customIframeDomains)\n */\nexport function validateLayout(\n layout: UILayout,\n options?: ValidationOptions\n): ValidationResult {\n const errors: ValidationResult['errors'] = []\n\n // Validate component count\n if (layout.components.length === 0) {\n errors.push({\n path: 'components',\n message: 'Layout must have at least one component',\n code: 'EMPTY_LAYOUT',\n })\n }\n\n if (layout.components.length > 12) {\n errors.push({\n path: 'components',\n message: `Layout exceeds max components: ${layout.components.length} > 12`,\n code: 'TOO_MANY_COMPONENTS',\n })\n }\n\n // Validate each component\n for (const [index, component] of layout.components.entries()) {\n const result = validateComponent(component, options)\n if (!result.valid) {\n errors.push(\n ...(result.errors?.map((error) => ({\n ...error,\n path: `components[${index}].${error.path}`,\n })) || [])\n )\n }\n }\n\n // Validate grid configuration\n if (layout.grid.columns !== 12) {\n errors.push({\n path: 'grid.columns',\n message: 'Grid must have 12 columns (Bootstrap-like)',\n code: 'INVALID_GRID_COLUMNS',\n })\n }\n\n return {\n valid: errors.length === 0,\n errors: errors.length > 0 ? errors : undefined,\n }\n}\n\n/**\n * Validate a single form field value against field rules\n */\nexport function validateFieldValue(\n value: any,\n field: FormFieldParams\n): { valid: boolean; error?: string } {\n // Required check\n if (field.required) {\n if (value === undefined || value === null || value === '') {\n return { valid: false, error: `${field.label || field.name} is required` }\n }\n if (field.type === 'checkbox' && value !== true) {\n return { valid: false, error: `${field.label || field.name} must be checked` }\n }\n }\n\n // Skip further validation if value is empty and not required\n if (value === undefined || value === null || value === '') {\n return { valid: true }\n }\n\n // Type-specific validation\n switch (field.type) {\n case 'text':\n case 'textarea':\n case 'password':\n if (field.minLength && String(value).length < field.minLength) {\n return { valid: false, error: `Minimum ${field.minLength} characters required` }\n }\n if (field.maxLength && String(value).length > field.maxLength) {\n return { valid: false, error: `Maximum ${field.maxLength} characters allowed` }\n }\n if (field.pattern && !new RegExp(field.pattern).test(String(value))) {\n return { valid: false, error: 'Invalid format' }\n }\n break\n\n case 'email':\n if (!/^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/.test(String(value))) {\n return { valid: false, error: 'Invalid email address' }\n }\n break\n\n case 'number': {\n const numValue = Number(value)\n if (isNaN(numValue)) {\n return { valid: false, error: 'Must be a valid number' }\n }\n if (field.min !== undefined && numValue < field.min) {\n return { valid: false, error: `Minimum value is ${field.min}` }\n }\n if (field.max !== undefined && numValue > field.max) {\n return { valid: false, error: `Maximum value is ${field.max}` }\n }\n break\n }\n\n case 'date':\n if (field.minDate && value < field.minDate) {\n return { valid: false, error: `Date must be after ${field.minDate}` }\n }\n if (field.maxDate && value > field.maxDate) {\n return { valid: false, error: `Date must be before ${field.maxDate}` }\n }\n break\n\n case 'select':\n case 'radio':\n // Validate that value is one of the options\n if (field.options && field.options.length > 0) {\n const validValues = field.options.map((opt) => opt.value)\n if (!validValues.includes(String(value))) {\n return { valid: false, error: 'Please select a valid option' }\n }\n }\n break\n }\n\n return { valid: true }\n}\n\n/**\n * Validate entire form data against field definitions\n */\nexport function validateFormData(\n data: Record<string, any>,\n fields: FormFieldParams[]\n): { valid: boolean; errors: Record<string, string> } {\n const errors: Record<string, string> = {}\n\n for (const field of fields) {\n const result = validateFieldValue(data[field.name], field)\n if (!result.valid && result.error) {\n errors[field.name] = result.error\n }\n }\n\n return {\n valid: Object.keys(errors).length === 0,\n errors,\n }\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../../src/services/validation.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EACV,WAAW,EACX,QAAQ,EACR,gBAAgB,EAChB,cAAc,EACd,oBAAoB,EACpB,oBAAoB,EACpB,eAAe,EACf,YAAY,EACZ,iBAAiB,EAElB,MAAM,UAAU,CAAA;AAYjB;;GAEG;AACH,eAAO,MAAM,uBAAuB,EAAE,cAKrC,CAAA;AAED;;;;;;GAMG;AACH,eAAO,MAAM,sBAAsB,
|
|
1
|
+
{"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../../src/services/validation.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EACV,WAAW,EACX,QAAQ,EACR,gBAAgB,EAChB,cAAc,EACd,oBAAoB,EACpB,oBAAoB,EACpB,eAAe,EACf,YAAY,EACZ,iBAAiB,EAElB,MAAM,UAAU,CAAA;AAYjB;;GAEG;AACH,eAAO,MAAM,uBAAuB,EAAE,cAKrC,CAAA;AAED;;;;;;GAMG;AACH,eAAO,MAAM,sBAAsB,UAgHlC,CAAA;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,WAAW,CAAC,UAAU,CAAC,GAAG,gBAAgB,CA6DxF;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,oBAAoB,EAC5B,MAAM,GAAE,cAAwC,GAC/C,gBAAgB,CA8ClB;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,oBAAoB,EAC5B,MAAM,GAAE,cAAwC,GAC/C,gBAAgB,CAmDlB;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CACjC,SAAS,EAAE,WAAW,EACtB,MAAM,GAAE,cAAwC,GAC/C,gBAAgB,CAiBlB;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAKpD;AAED;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAClC,GAAG,EAAE,MAAM,EACX,OAAO,CAAC,EAAE;IAAE,MAAM,CAAC,EAAE,YAAY,CAAC;IAAC,aAAa,CAAC,EAAE,MAAM,EAAE,CAAA;CAAE,GAC5D,gBAAgB,CA8ClB;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAC/B,SAAS,EAAE,WAAW,EACtB,OAAO,CAAC,EAAE,iBAAiB,GAC1B,gBAAgB,CA0IlB;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAC5B,MAAM,EAAE,QAAQ,EAChB,OAAO,CAAC,EAAE,iBAAiB,GAC1B,gBAAgB,CA8ClB;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,GAAG,EACV,KAAK,EAAE,eAAe,GACrB;IAAE,KAAK,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,CA0EpC;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EACzB,MAAM,EAAE,eAAe,EAAE,GACxB;IAAE,KAAK,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAAE,CAcpD"}
|
|
@@ -110,7 +110,18 @@ const DEFAULT_IFRAME_DOMAINS = [
|
|
|
110
110
|
"amazon.co.jp",
|
|
111
111
|
"images-amazon.com",
|
|
112
112
|
"media-amazon.com",
|
|
113
|
-
"ws-na.amazon-adsystem.com"
|
|
113
|
+
"ws-na.amazon-adsystem.com",
|
|
114
|
+
// MCP Connectors — embed-capable services (v2.2.7)
|
|
115
|
+
"gamma.app",
|
|
116
|
+
"www.gamma.app",
|
|
117
|
+
"app.hubspot.com",
|
|
118
|
+
"share.hubspot.com",
|
|
119
|
+
"www.data.gouv.fr",
|
|
120
|
+
"data.gouv.fr",
|
|
121
|
+
"clinicaltrials.gov",
|
|
122
|
+
"www.clinicaltrials.gov",
|
|
123
|
+
"linear.app",
|
|
124
|
+
"www.linear.app"
|
|
114
125
|
];
|
|
115
126
|
function validateGridPosition(position) {
|
|
116
127
|
const errors = [];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validation.js","sources":["../../src/services/validation.ts"],"sourcesContent":["/**\n * Component Validation Service\n * Phase 0: Resource Limits & Schema Validation\n *\n * Validates LLM-generated components against:\n * - JSON schema\n * - Resource limits (data points, payload size, grid bounds)\n * - Security constraints (domain whitelist, XSS prevention)\n */\n\nimport type {\n UIComponent,\n UILayout,\n ValidationResult,\n ResourceLimits,\n ChartComponentParams,\n TableComponentParams,\n FormFieldParams,\n IframePolicy,\n ValidationOptions,\n ComponentType,\n} from '../types'\n\n/**\n * All known ComponentType values — used to distinguish known-but-unvalidated\n * types (pass through) from truly unknown strings (reject).\n */\nconst KNOWN_COMPONENT_TYPES: Set<string> = new Set<ComponentType>([\n 'chart', 'table', 'metric', 'text', 'grid', 'iframe', 'image', 'link',\n 'action', 'footer', 'carousel', 'artifact', 'form', 'modal',\n 'action-group', 'image-gallery', 'video', 'code', 'map',\n])\n\n/**\n * Default resource limits (configurable via env)\n */\nexport const DEFAULT_RESOURCE_LIMITS: ResourceLimits = {\n maxDataPoints: 1000,\n maxTableRows: 100,\n maxPayloadSize: 50 * 1024, // 50KB\n renderTimeout: 5000, // 5 seconds\n}\n\n/**\n * Default allowed iframe domains (whitelist)\n * Must match CSP frame-src directive\n * Updated Sprint 7: Added code, design, docs, and map providers\n *\n * This list is exported for transparency and can be extended via ValidationOptions\n */\nexport const DEFAULT_IFRAME_DOMAINS = [\n // Charts\n 'quickchart.io',\n 'www.quickchart.io',\n\n // Deposium\n 'deposium.com',\n 'deposium.vip',\n 'deposium.ai',\n\n // Development\n 'localhost',\n\n // Video providers (Sprint 5)\n 'youtube.com',\n 'www.youtube.com',\n 'youtube-nocookie.com',\n 'www.youtube-nocookie.com',\n 'youtu.be',\n 'vimeo.com',\n 'player.vimeo.com',\n\n // Code playgrounds (Sprint 7)\n 'codepen.io',\n 'codesandbox.io',\n 'stackblitz.com',\n 'jsfiddle.net',\n\n // Design tools (Sprint 7)\n 'figma.com',\n 'www.figma.com',\n 'miro.com',\n\n // Google services (Sprint 7)\n 'docs.google.com',\n 'drive.google.com',\n 'sheets.google.com',\n 'slides.google.com',\n 'maps.google.com',\n 'www.google.com',\n 'datastudio.google.com',\n 'lookerstudio.google.com',\n\n // Productivity (Sprint 7)\n 'airtable.com',\n 'notion.so',\n 'www.notion.so',\n\n // Maps (Sprint 7)\n 'openstreetmap.org',\n 'www.openstreetmap.org',\n\n // Analytics/Dashboards (Sprint 7)\n 'public.tableau.com',\n 'app.powerbi.com',\n 'observablehq.com',\n\n // Diagrams & Whiteboards (v2.0.0)\n 'mermaid.live',\n 'excalidraw.com',\n 'lucidchart.com',\n 'lucid.app',\n\n // Video - Business (v2.0.0)\n 'loom.com',\n 'www.loom.com',\n 'cloudflarestream.com',\n 'streamable.com',\n\n // Code repositories (v2.0.0)\n 'github.com',\n 'gist.github.com',\n 'gitlab.com',\n 'replit.com',\n 'glitch.com',\n\n // Business tools (v2.0.0)\n 'calendly.com',\n 'typeform.com',\n 'cal.com',\n\n // Design (v2.0.0)\n 'canva.com',\n\n // Deploy previews (v2.0.0)\n 'vercel.app',\n 'netlify.app',\n\n // E-commerce (v2.0.0)\n 'amazon.com',\n 'amazon.fr',\n 'amazon.de',\n 'amazon.co.uk',\n 'amazon.es',\n 'amazon.it',\n 'amazon.ca',\n 'amazon.co.jp',\n 'images-amazon.com',\n 'media-amazon.com',\n 'ws-na.amazon-adsystem.com',\n]\n\n/**\n * Validate grid position bounds (1-12 columns)\n */\nexport function validateGridPosition(position: UIComponent['position']): ValidationResult {\n const errors: ValidationResult['errors'] = []\n\n // ✅ PHASE 3 FIX: Defensive check for undefined position\n if (!position) {\n return {\n valid: false,\n errors: [\n {\n path: 'position',\n message: 'Position is required',\n code: 'MISSING_POSITION',\n },\n ],\n }\n }\n\n if (position.colStart < 1 || position.colStart > 12) {\n errors.push({\n path: 'position.colStart',\n message: 'Column start must be between 1 and 12',\n code: 'INVALID_GRID_COL_START',\n })\n }\n\n if (position.colSpan < 1 || position.colSpan > 12) {\n errors.push({\n path: 'position.colSpan',\n message: 'Column span must be between 1 and 12',\n code: 'INVALID_GRID_COL_SPAN',\n })\n }\n\n if (position.colStart + position.colSpan - 1 > 12) {\n errors.push({\n path: 'position',\n message: 'Column start + span exceeds grid width (12)',\n code: 'GRID_OVERFLOW',\n })\n }\n\n if (position.rowStart !== undefined && position.rowStart < 1) {\n errors.push({\n path: 'position.rowStart',\n message: 'Row start must be >= 1',\n code: 'INVALID_GRID_ROW_START',\n })\n }\n\n if (position.rowSpan !== undefined && position.rowSpan < 1) {\n errors.push({\n path: 'position.rowSpan',\n message: 'Row span must be >= 1',\n code: 'INVALID_GRID_ROW_SPAN',\n })\n }\n\n return {\n valid: errors.length === 0,\n errors: errors.length > 0 ? errors : undefined,\n }\n}\n\n/**\n * Validate chart component against resource limits\n */\nexport function validateChartComponent(\n params: ChartComponentParams,\n limits: ResourceLimits = DEFAULT_RESOURCE_LIMITS\n): ValidationResult {\n const errors: ValidationResult['errors'] = []\n\n // Validate data points count\n const totalDataPoints = params.data.datasets.reduce(\n (sum, dataset) => sum + dataset.data.length,\n 0\n )\n\n if (totalDataPoints > limits.maxDataPoints) {\n errors.push({\n path: 'params.data',\n message: `Chart exceeds max data points: ${totalDataPoints} > ${limits.maxDataPoints}`,\n code: 'RESOURCE_LIMIT_EXCEEDED',\n })\n }\n\n // Validate labels match dataset length\n const expectedLength = params.data.labels.length\n for (const [index, dataset] of params.data.datasets.entries()) {\n if (dataset.data.length !== expectedLength) {\n errors.push({\n path: `params.data.datasets[${index}]`,\n message: `Dataset length mismatch: expected ${expectedLength}, got ${dataset.data.length}`,\n code: 'DATA_LENGTH_MISMATCH',\n })\n }\n }\n\n // Validate numeric data\n for (const [index, dataset] of params.data.datasets.entries()) {\n for (const [dataIndex, value] of dataset.data.entries()) {\n if (typeof value !== 'number' || !Number.isFinite(value)) {\n errors.push({\n path: `params.data.datasets[${index}].data[${dataIndex}]`,\n message: `Invalid data value: ${value} (must be finite number)`,\n code: 'INVALID_DATA_TYPE',\n })\n }\n }\n }\n\n return {\n valid: errors.length === 0,\n errors: errors.length > 0 ? errors : undefined,\n }\n}\n\n/**\n * Validate table component against resource limits\n */\nexport function validateTableComponent(\n params: TableComponentParams,\n limits: ResourceLimits = DEFAULT_RESOURCE_LIMITS\n): ValidationResult {\n const errors: ValidationResult['errors'] = []\n\n // Validate row count\n if (params.rows.length > limits.maxTableRows) {\n errors.push({\n path: 'params.rows',\n message: `Table exceeds max rows: ${params.rows.length} > ${limits.maxTableRows}`,\n code: 'RESOURCE_LIMIT_EXCEEDED',\n })\n }\n\n // Validate columns\n if (params.columns.length === 0) {\n errors.push({\n path: 'params.columns',\n message: 'Table must have at least one column',\n code: 'EMPTY_COLUMNS',\n })\n }\n\n // Validate column keys are unique\n const columnKeys = new Set<string>()\n for (const [index, column] of params.columns.entries()) {\n if (columnKeys.has(column.key)) {\n errors.push({\n path: `params.columns[${index}]`,\n message: `Duplicate column key: ${column.key}`,\n code: 'DUPLICATE_COLUMN_KEY',\n })\n }\n columnKeys.add(column.key)\n }\n\n // Validate rows have valid data for defined columns\n for (const [rowIndex, row] of params.rows.entries()) {\n for (const column of params.columns) {\n if (!(column.key in row)) {\n errors.push({\n path: `params.rows[${rowIndex}]`,\n message: `Missing column key: ${column.key}`,\n code: 'MISSING_COLUMN_DATA',\n })\n }\n }\n }\n\n return {\n valid: errors.length === 0,\n errors: errors.length > 0 ? errors : undefined,\n }\n}\n\n/**\n * Validate payload size\n */\nexport function validatePayloadSize(\n component: UIComponent,\n limits: ResourceLimits = DEFAULT_RESOURCE_LIMITS\n): ValidationResult {\n const payloadSize = JSON.stringify(component).length\n\n if (payloadSize > limits.maxPayloadSize) {\n return {\n valid: false,\n errors: [\n {\n path: 'component',\n message: `Payload size exceeds limit: ${payloadSize} > ${limits.maxPayloadSize} bytes`,\n code: 'PAYLOAD_TOO_LARGE',\n },\n ],\n }\n }\n\n return { valid: true }\n}\n\n/**\n * Sanitize string to prevent XSS\n * Basic implementation - DOMPurify used at render time\n */\nexport function sanitizeString(input: string): string {\n return input\n .replace(/<script\\b[^<]*(?:(?!<\\/script>)<[^<]*)*<\\/script>/gi, '')\n .replace(/on\\w+=\"[^\"]*\"/gi, '')\n .replace(/javascript:/gi, '')\n}\n\n/**\n * Validate iframe domain against whitelist\n *\n * @param url - The URL to validate\n * @param options - Optional validation options\n * @param options.policy - 'strict' (default), 'extend', or 'allow-all'\n * @param options.customDomains - Additional domains when policy is 'extend'\n */\nexport function validateIframeDomain(\n url: string,\n options?: { policy?: IframePolicy; customDomains?: string[] }\n): ValidationResult {\n // If allow-all, skip validation\n if (options?.policy === 'allow-all') {\n return { valid: true }\n }\n\n try {\n const parsedUrl = new URL(url)\n const domain = parsedUrl.hostname\n\n // Build effective whitelist\n let effectiveWhitelist = DEFAULT_IFRAME_DOMAINS\n if (options?.policy === 'extend' && options.customDomains) {\n effectiveWhitelist = [...DEFAULT_IFRAME_DOMAINS, ...options.customDomains]\n }\n\n const isAllowed = effectiveWhitelist.some(\n (allowed) => domain === allowed || domain.endsWith(`.${allowed}`) || allowed === 'localhost'\n )\n\n if (!isAllowed) {\n return {\n valid: false,\n errors: [\n {\n path: 'url',\n message: `Domain not whitelisted: ${domain}`,\n code: 'DOMAIN_NOT_WHITELISTED',\n },\n ],\n }\n }\n\n return { valid: true }\n } catch (error) {\n return {\n valid: false,\n errors: [\n {\n path: 'url',\n message: 'Invalid URL format',\n code: 'INVALID_URL',\n },\n ],\n }\n }\n}\n\n/**\n * Validate entire component\n *\n * @param component - The component to validate\n * @param options - Optional validation options (limits, iframePolicy, customIframeDomains)\n */\nexport function validateComponent(\n component: UIComponent,\n options?: ValidationOptions\n): ValidationResult {\n const limits = options?.limits ?? DEFAULT_RESOURCE_LIMITS\n const errors: ValidationResult['errors'] = []\n\n // Validate grid position\n const gridResult = validateGridPosition(component.position)\n if (!gridResult.valid) {\n errors.push(...(gridResult.errors || []))\n }\n\n // Validate payload size\n const sizeResult = validatePayloadSize(component, limits)\n if (!sizeResult.valid) {\n errors.push(...(sizeResult.errors || []))\n }\n\n // Type-specific validation\n switch (component.type) {\n case 'chart': {\n const chartResult = validateChartComponent(component.params as ChartComponentParams, limits)\n if (!chartResult.valid) {\n errors.push(...(chartResult.errors || []))\n }\n break\n }\n\n case 'table': {\n const tableResult = validateTableComponent(component.params as TableComponentParams, limits)\n if (!tableResult.valid) {\n errors.push(...(tableResult.errors || []))\n }\n break\n }\n\n case 'metric': {\n // Basic validation for metrics\n const metricParams = component.params as any\n if (!metricParams.title || !metricParams.value) {\n errors.push({\n path: 'params',\n message: 'Metric must have title and value',\n code: 'INVALID_METRIC',\n })\n }\n break\n }\n\n case 'text': {\n // Basic validation for text\n const textParams = component.params as any\n if (!textParams.content) {\n errors.push({\n path: 'params',\n message: 'Text component must have content',\n code: 'INVALID_TEXT',\n })\n }\n break\n }\n\n case 'iframe': {\n // Basic validation for iframe\n const iframeParams = component.params as any\n if (!iframeParams.url) {\n errors.push({\n path: 'params',\n message: 'Iframe component must have url',\n code: 'INVALID_IFRAME',\n })\n } else {\n // Validate iframe domain against whitelist\n const iframeResult = validateIframeDomain(iframeParams.url, {\n policy: options?.iframePolicy,\n customDomains: options?.customIframeDomains,\n })\n if (!iframeResult.valid) {\n errors.push(...(iframeResult.errors || []))\n }\n }\n break\n }\n\n case 'image': {\n // Basic validation for image\n const imageParams = component.params as any\n if (!imageParams.url) {\n errors.push({\n path: 'params',\n message: 'Image component must have url',\n code: 'INVALID_IMAGE',\n })\n }\n break\n }\n\n case 'link': {\n // Basic validation for link\n const linkParams = component.params as any\n if (!linkParams.url) {\n errors.push({\n path: 'params',\n message: 'Link component must have url',\n code: 'INVALID_LINK',\n })\n }\n break\n }\n\n case 'action': {\n // Basic validation for action\n const actionParams = component.params as any\n if (!actionParams.label) {\n errors.push({\n path: 'params',\n message: 'Action component must have label',\n code: 'INVALID_ACTION',\n })\n }\n break\n }\n\n default:\n // Known types without specific validation pass through — renderer handles errors\n // Truly unknown types (e.g. typos in streamed JSON) are rejected\n if (!KNOWN_COMPONENT_TYPES.has(component.type)) {\n errors.push({\n path: 'type',\n message: `Unknown component type: ${component.type}`,\n code: 'UNKNOWN_COMPONENT_TYPE',\n })\n }\n break\n }\n\n return {\n valid: errors.length === 0,\n errors: errors.length > 0 ? errors : undefined,\n }\n}\n\n/**\n * Validate entire layout\n *\n * @param layout - The layout to validate\n * @param options - Optional validation options (limits, iframePolicy, customIframeDomains)\n */\nexport function validateLayout(\n layout: UILayout,\n options?: ValidationOptions\n): ValidationResult {\n const errors: ValidationResult['errors'] = []\n\n // Validate component count\n if (layout.components.length === 0) {\n errors.push({\n path: 'components',\n message: 'Layout must have at least one component',\n code: 'EMPTY_LAYOUT',\n })\n }\n\n if (layout.components.length > 12) {\n errors.push({\n path: 'components',\n message: `Layout exceeds max components: ${layout.components.length} > 12`,\n code: 'TOO_MANY_COMPONENTS',\n })\n }\n\n // Validate each component\n for (const [index, component] of layout.components.entries()) {\n const result = validateComponent(component, options)\n if (!result.valid) {\n errors.push(\n ...(result.errors?.map((error) => ({\n ...error,\n path: `components[${index}].${error.path}`,\n })) || [])\n )\n }\n }\n\n // Validate grid configuration\n if (layout.grid.columns !== 12) {\n errors.push({\n path: 'grid.columns',\n message: 'Grid must have 12 columns (Bootstrap-like)',\n code: 'INVALID_GRID_COLUMNS',\n })\n }\n\n return {\n valid: errors.length === 0,\n errors: errors.length > 0 ? errors : undefined,\n }\n}\n\n/**\n * Validate a single form field value against field rules\n */\nexport function validateFieldValue(\n value: any,\n field: FormFieldParams\n): { valid: boolean; error?: string } {\n // Required check\n if (field.required) {\n if (value === undefined || value === null || value === '') {\n return { valid: false, error: `${field.label || field.name} is required` }\n }\n if (field.type === 'checkbox' && value !== true) {\n return { valid: false, error: `${field.label || field.name} must be checked` }\n }\n }\n\n // Skip further validation if value is empty and not required\n if (value === undefined || value === null || value === '') {\n return { valid: true }\n }\n\n // Type-specific validation\n switch (field.type) {\n case 'text':\n case 'textarea':\n case 'password':\n if (field.minLength && String(value).length < field.minLength) {\n return { valid: false, error: `Minimum ${field.minLength} characters required` }\n }\n if (field.maxLength && String(value).length > field.maxLength) {\n return { valid: false, error: `Maximum ${field.maxLength} characters allowed` }\n }\n if (field.pattern && !new RegExp(field.pattern).test(String(value))) {\n return { valid: false, error: 'Invalid format' }\n }\n break\n\n case 'email':\n if (!/^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/.test(String(value))) {\n return { valid: false, error: 'Invalid email address' }\n }\n break\n\n case 'number': {\n const numValue = Number(value)\n if (isNaN(numValue)) {\n return { valid: false, error: 'Must be a valid number' }\n }\n if (field.min !== undefined && numValue < field.min) {\n return { valid: false, error: `Minimum value is ${field.min}` }\n }\n if (field.max !== undefined && numValue > field.max) {\n return { valid: false, error: `Maximum value is ${field.max}` }\n }\n break\n }\n\n case 'date':\n if (field.minDate && value < field.minDate) {\n return { valid: false, error: `Date must be after ${field.minDate}` }\n }\n if (field.maxDate && value > field.maxDate) {\n return { valid: false, error: `Date must be before ${field.maxDate}` }\n }\n break\n\n case 'select':\n case 'radio':\n // Validate that value is one of the options\n if (field.options && field.options.length > 0) {\n const validValues = field.options.map((opt) => opt.value)\n if (!validValues.includes(String(value))) {\n return { valid: false, error: 'Please select a valid option' }\n }\n }\n break\n }\n\n return { valid: true }\n}\n\n/**\n * Validate entire form data against field definitions\n */\nexport function validateFormData(\n data: Record<string, any>,\n fields: FormFieldParams[]\n): { valid: boolean; errors: Record<string, string> } {\n const errors: Record<string, string> = {}\n\n for (const field of fields) {\n const result = validateFieldValue(data[field.name], field)\n if (!result.valid && result.error) {\n errors[field.name] = result.error\n }\n }\n\n return {\n valid: Object.keys(errors).length === 0,\n errors,\n }\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"validation.js","sources":["../../src/services/validation.ts"],"sourcesContent":["/**\n * Component Validation Service\n * Phase 0: Resource Limits & Schema Validation\n *\n * Validates LLM-generated components against:\n * - JSON schema\n * - Resource limits (data points, payload size, grid bounds)\n * - Security constraints (domain whitelist, XSS prevention)\n */\n\nimport type {\n UIComponent,\n UILayout,\n ValidationResult,\n ResourceLimits,\n ChartComponentParams,\n TableComponentParams,\n FormFieldParams,\n IframePolicy,\n ValidationOptions,\n ComponentType,\n} from '../types'\n\n/**\n * All known ComponentType values — used to distinguish known-but-unvalidated\n * types (pass through) from truly unknown strings (reject).\n */\nconst KNOWN_COMPONENT_TYPES: Set<string> = new Set<ComponentType>([\n 'chart', 'table', 'metric', 'text', 'grid', 'iframe', 'image', 'link',\n 'action', 'footer', 'carousel', 'artifact', 'form', 'modal',\n 'action-group', 'image-gallery', 'video', 'code', 'map',\n])\n\n/**\n * Default resource limits (configurable via env)\n */\nexport const DEFAULT_RESOURCE_LIMITS: ResourceLimits = {\n maxDataPoints: 1000,\n maxTableRows: 100,\n maxPayloadSize: 50 * 1024, // 50KB\n renderTimeout: 5000, // 5 seconds\n}\n\n/**\n * Default allowed iframe domains (whitelist)\n * Must match CSP frame-src directive\n * Updated Sprint 7: Added code, design, docs, and map providers\n *\n * This list is exported for transparency and can be extended via ValidationOptions\n */\nexport const DEFAULT_IFRAME_DOMAINS = [\n // Charts\n 'quickchart.io',\n 'www.quickchart.io',\n\n // Deposium\n 'deposium.com',\n 'deposium.vip',\n 'deposium.ai',\n\n // Development\n 'localhost',\n\n // Video providers (Sprint 5)\n 'youtube.com',\n 'www.youtube.com',\n 'youtube-nocookie.com',\n 'www.youtube-nocookie.com',\n 'youtu.be',\n 'vimeo.com',\n 'player.vimeo.com',\n\n // Code playgrounds (Sprint 7)\n 'codepen.io',\n 'codesandbox.io',\n 'stackblitz.com',\n 'jsfiddle.net',\n\n // Design tools (Sprint 7)\n 'figma.com',\n 'www.figma.com',\n 'miro.com',\n\n // Google services (Sprint 7)\n 'docs.google.com',\n 'drive.google.com',\n 'sheets.google.com',\n 'slides.google.com',\n 'maps.google.com',\n 'www.google.com',\n 'datastudio.google.com',\n 'lookerstudio.google.com',\n\n // Productivity (Sprint 7)\n 'airtable.com',\n 'notion.so',\n 'www.notion.so',\n\n // Maps (Sprint 7)\n 'openstreetmap.org',\n 'www.openstreetmap.org',\n\n // Analytics/Dashboards (Sprint 7)\n 'public.tableau.com',\n 'app.powerbi.com',\n 'observablehq.com',\n\n // Diagrams & Whiteboards (v2.0.0)\n 'mermaid.live',\n 'excalidraw.com',\n 'lucidchart.com',\n 'lucid.app',\n\n // Video - Business (v2.0.0)\n 'loom.com',\n 'www.loom.com',\n 'cloudflarestream.com',\n 'streamable.com',\n\n // Code repositories (v2.0.0)\n 'github.com',\n 'gist.github.com',\n 'gitlab.com',\n 'replit.com',\n 'glitch.com',\n\n // Business tools (v2.0.0)\n 'calendly.com',\n 'typeform.com',\n 'cal.com',\n\n // Design (v2.0.0)\n 'canva.com',\n\n // Deploy previews (v2.0.0)\n 'vercel.app',\n 'netlify.app',\n\n // E-commerce (v2.0.0)\n 'amazon.com',\n 'amazon.fr',\n 'amazon.de',\n 'amazon.co.uk',\n 'amazon.es',\n 'amazon.it',\n 'amazon.ca',\n 'amazon.co.jp',\n 'images-amazon.com',\n 'media-amazon.com',\n 'ws-na.amazon-adsystem.com',\n\n // MCP Connectors — embed-capable services (v2.2.7)\n 'gamma.app',\n 'www.gamma.app',\n 'app.hubspot.com',\n 'share.hubspot.com',\n 'www.data.gouv.fr',\n 'data.gouv.fr',\n 'clinicaltrials.gov',\n 'www.clinicaltrials.gov',\n 'linear.app',\n 'www.linear.app',\n]\n\n/**\n * Validate grid position bounds (1-12 columns)\n */\nexport function validateGridPosition(position: UIComponent['position']): ValidationResult {\n const errors: ValidationResult['errors'] = []\n\n // ✅ PHASE 3 FIX: Defensive check for undefined position\n if (!position) {\n return {\n valid: false,\n errors: [\n {\n path: 'position',\n message: 'Position is required',\n code: 'MISSING_POSITION',\n },\n ],\n }\n }\n\n if (position.colStart < 1 || position.colStart > 12) {\n errors.push({\n path: 'position.colStart',\n message: 'Column start must be between 1 and 12',\n code: 'INVALID_GRID_COL_START',\n })\n }\n\n if (position.colSpan < 1 || position.colSpan > 12) {\n errors.push({\n path: 'position.colSpan',\n message: 'Column span must be between 1 and 12',\n code: 'INVALID_GRID_COL_SPAN',\n })\n }\n\n if (position.colStart + position.colSpan - 1 > 12) {\n errors.push({\n path: 'position',\n message: 'Column start + span exceeds grid width (12)',\n code: 'GRID_OVERFLOW',\n })\n }\n\n if (position.rowStart !== undefined && position.rowStart < 1) {\n errors.push({\n path: 'position.rowStart',\n message: 'Row start must be >= 1',\n code: 'INVALID_GRID_ROW_START',\n })\n }\n\n if (position.rowSpan !== undefined && position.rowSpan < 1) {\n errors.push({\n path: 'position.rowSpan',\n message: 'Row span must be >= 1',\n code: 'INVALID_GRID_ROW_SPAN',\n })\n }\n\n return {\n valid: errors.length === 0,\n errors: errors.length > 0 ? errors : undefined,\n }\n}\n\n/**\n * Validate chart component against resource limits\n */\nexport function validateChartComponent(\n params: ChartComponentParams,\n limits: ResourceLimits = DEFAULT_RESOURCE_LIMITS\n): ValidationResult {\n const errors: ValidationResult['errors'] = []\n\n // Validate data points count\n const totalDataPoints = params.data.datasets.reduce(\n (sum, dataset) => sum + dataset.data.length,\n 0\n )\n\n if (totalDataPoints > limits.maxDataPoints) {\n errors.push({\n path: 'params.data',\n message: `Chart exceeds max data points: ${totalDataPoints} > ${limits.maxDataPoints}`,\n code: 'RESOURCE_LIMIT_EXCEEDED',\n })\n }\n\n // Validate labels match dataset length\n const expectedLength = params.data.labels.length\n for (const [index, dataset] of params.data.datasets.entries()) {\n if (dataset.data.length !== expectedLength) {\n errors.push({\n path: `params.data.datasets[${index}]`,\n message: `Dataset length mismatch: expected ${expectedLength}, got ${dataset.data.length}`,\n code: 'DATA_LENGTH_MISMATCH',\n })\n }\n }\n\n // Validate numeric data\n for (const [index, dataset] of params.data.datasets.entries()) {\n for (const [dataIndex, value] of dataset.data.entries()) {\n if (typeof value !== 'number' || !Number.isFinite(value)) {\n errors.push({\n path: `params.data.datasets[${index}].data[${dataIndex}]`,\n message: `Invalid data value: ${value} (must be finite number)`,\n code: 'INVALID_DATA_TYPE',\n })\n }\n }\n }\n\n return {\n valid: errors.length === 0,\n errors: errors.length > 0 ? errors : undefined,\n }\n}\n\n/**\n * Validate table component against resource limits\n */\nexport function validateTableComponent(\n params: TableComponentParams,\n limits: ResourceLimits = DEFAULT_RESOURCE_LIMITS\n): ValidationResult {\n const errors: ValidationResult['errors'] = []\n\n // Validate row count\n if (params.rows.length > limits.maxTableRows) {\n errors.push({\n path: 'params.rows',\n message: `Table exceeds max rows: ${params.rows.length} > ${limits.maxTableRows}`,\n code: 'RESOURCE_LIMIT_EXCEEDED',\n })\n }\n\n // Validate columns\n if (params.columns.length === 0) {\n errors.push({\n path: 'params.columns',\n message: 'Table must have at least one column',\n code: 'EMPTY_COLUMNS',\n })\n }\n\n // Validate column keys are unique\n const columnKeys = new Set<string>()\n for (const [index, column] of params.columns.entries()) {\n if (columnKeys.has(column.key)) {\n errors.push({\n path: `params.columns[${index}]`,\n message: `Duplicate column key: ${column.key}`,\n code: 'DUPLICATE_COLUMN_KEY',\n })\n }\n columnKeys.add(column.key)\n }\n\n // Validate rows have valid data for defined columns\n for (const [rowIndex, row] of params.rows.entries()) {\n for (const column of params.columns) {\n if (!(column.key in row)) {\n errors.push({\n path: `params.rows[${rowIndex}]`,\n message: `Missing column key: ${column.key}`,\n code: 'MISSING_COLUMN_DATA',\n })\n }\n }\n }\n\n return {\n valid: errors.length === 0,\n errors: errors.length > 0 ? errors : undefined,\n }\n}\n\n/**\n * Validate payload size\n */\nexport function validatePayloadSize(\n component: UIComponent,\n limits: ResourceLimits = DEFAULT_RESOURCE_LIMITS\n): ValidationResult {\n const payloadSize = JSON.stringify(component).length\n\n if (payloadSize > limits.maxPayloadSize) {\n return {\n valid: false,\n errors: [\n {\n path: 'component',\n message: `Payload size exceeds limit: ${payloadSize} > ${limits.maxPayloadSize} bytes`,\n code: 'PAYLOAD_TOO_LARGE',\n },\n ],\n }\n }\n\n return { valid: true }\n}\n\n/**\n * Sanitize string to prevent XSS\n * Basic implementation - DOMPurify used at render time\n */\nexport function sanitizeString(input: string): string {\n return input\n .replace(/<script\\b[^<]*(?:(?!<\\/script>)<[^<]*)*<\\/script>/gi, '')\n .replace(/on\\w+=\"[^\"]*\"/gi, '')\n .replace(/javascript:/gi, '')\n}\n\n/**\n * Validate iframe domain against whitelist\n *\n * @param url - The URL to validate\n * @param options - Optional validation options\n * @param options.policy - 'strict' (default), 'extend', or 'allow-all'\n * @param options.customDomains - Additional domains when policy is 'extend'\n */\nexport function validateIframeDomain(\n url: string,\n options?: { policy?: IframePolicy; customDomains?: string[] }\n): ValidationResult {\n // If allow-all, skip validation\n if (options?.policy === 'allow-all') {\n return { valid: true }\n }\n\n try {\n const parsedUrl = new URL(url)\n const domain = parsedUrl.hostname\n\n // Build effective whitelist\n let effectiveWhitelist = DEFAULT_IFRAME_DOMAINS\n if (options?.policy === 'extend' && options.customDomains) {\n effectiveWhitelist = [...DEFAULT_IFRAME_DOMAINS, ...options.customDomains]\n }\n\n const isAllowed = effectiveWhitelist.some(\n (allowed) => domain === allowed || domain.endsWith(`.${allowed}`) || allowed === 'localhost'\n )\n\n if (!isAllowed) {\n return {\n valid: false,\n errors: [\n {\n path: 'url',\n message: `Domain not whitelisted: ${domain}`,\n code: 'DOMAIN_NOT_WHITELISTED',\n },\n ],\n }\n }\n\n return { valid: true }\n } catch (error) {\n return {\n valid: false,\n errors: [\n {\n path: 'url',\n message: 'Invalid URL format',\n code: 'INVALID_URL',\n },\n ],\n }\n }\n}\n\n/**\n * Validate entire component\n *\n * @param component - The component to validate\n * @param options - Optional validation options (limits, iframePolicy, customIframeDomains)\n */\nexport function validateComponent(\n component: UIComponent,\n options?: ValidationOptions\n): ValidationResult {\n const limits = options?.limits ?? DEFAULT_RESOURCE_LIMITS\n const errors: ValidationResult['errors'] = []\n\n // Validate grid position\n const gridResult = validateGridPosition(component.position)\n if (!gridResult.valid) {\n errors.push(...(gridResult.errors || []))\n }\n\n // Validate payload size\n const sizeResult = validatePayloadSize(component, limits)\n if (!sizeResult.valid) {\n errors.push(...(sizeResult.errors || []))\n }\n\n // Type-specific validation\n switch (component.type) {\n case 'chart': {\n const chartResult = validateChartComponent(component.params as ChartComponentParams, limits)\n if (!chartResult.valid) {\n errors.push(...(chartResult.errors || []))\n }\n break\n }\n\n case 'table': {\n const tableResult = validateTableComponent(component.params as TableComponentParams, limits)\n if (!tableResult.valid) {\n errors.push(...(tableResult.errors || []))\n }\n break\n }\n\n case 'metric': {\n // Basic validation for metrics\n const metricParams = component.params as any\n if (!metricParams.title || !metricParams.value) {\n errors.push({\n path: 'params',\n message: 'Metric must have title and value',\n code: 'INVALID_METRIC',\n })\n }\n break\n }\n\n case 'text': {\n // Basic validation for text\n const textParams = component.params as any\n if (!textParams.content) {\n errors.push({\n path: 'params',\n message: 'Text component must have content',\n code: 'INVALID_TEXT',\n })\n }\n break\n }\n\n case 'iframe': {\n // Basic validation for iframe\n const iframeParams = component.params as any\n if (!iframeParams.url) {\n errors.push({\n path: 'params',\n message: 'Iframe component must have url',\n code: 'INVALID_IFRAME',\n })\n } else {\n // Validate iframe domain against whitelist\n const iframeResult = validateIframeDomain(iframeParams.url, {\n policy: options?.iframePolicy,\n customDomains: options?.customIframeDomains,\n })\n if (!iframeResult.valid) {\n errors.push(...(iframeResult.errors || []))\n }\n }\n break\n }\n\n case 'image': {\n // Basic validation for image\n const imageParams = component.params as any\n if (!imageParams.url) {\n errors.push({\n path: 'params',\n message: 'Image component must have url',\n code: 'INVALID_IMAGE',\n })\n }\n break\n }\n\n case 'link': {\n // Basic validation for link\n const linkParams = component.params as any\n if (!linkParams.url) {\n errors.push({\n path: 'params',\n message: 'Link component must have url',\n code: 'INVALID_LINK',\n })\n }\n break\n }\n\n case 'action': {\n // Basic validation for action\n const actionParams = component.params as any\n if (!actionParams.label) {\n errors.push({\n path: 'params',\n message: 'Action component must have label',\n code: 'INVALID_ACTION',\n })\n }\n break\n }\n\n default:\n // Known types without specific validation pass through — renderer handles errors\n // Truly unknown types (e.g. typos in streamed JSON) are rejected\n if (!KNOWN_COMPONENT_TYPES.has(component.type)) {\n errors.push({\n path: 'type',\n message: `Unknown component type: ${component.type}`,\n code: 'UNKNOWN_COMPONENT_TYPE',\n })\n }\n break\n }\n\n return {\n valid: errors.length === 0,\n errors: errors.length > 0 ? errors : undefined,\n }\n}\n\n/**\n * Validate entire layout\n *\n * @param layout - The layout to validate\n * @param options - Optional validation options (limits, iframePolicy, customIframeDomains)\n */\nexport function validateLayout(\n layout: UILayout,\n options?: ValidationOptions\n): ValidationResult {\n const errors: ValidationResult['errors'] = []\n\n // Validate component count\n if (layout.components.length === 0) {\n errors.push({\n path: 'components',\n message: 'Layout must have at least one component',\n code: 'EMPTY_LAYOUT',\n })\n }\n\n if (layout.components.length > 12) {\n errors.push({\n path: 'components',\n message: `Layout exceeds max components: ${layout.components.length} > 12`,\n code: 'TOO_MANY_COMPONENTS',\n })\n }\n\n // Validate each component\n for (const [index, component] of layout.components.entries()) {\n const result = validateComponent(component, options)\n if (!result.valid) {\n errors.push(\n ...(result.errors?.map((error) => ({\n ...error,\n path: `components[${index}].${error.path}`,\n })) || [])\n )\n }\n }\n\n // Validate grid configuration\n if (layout.grid.columns !== 12) {\n errors.push({\n path: 'grid.columns',\n message: 'Grid must have 12 columns (Bootstrap-like)',\n code: 'INVALID_GRID_COLUMNS',\n })\n }\n\n return {\n valid: errors.length === 0,\n errors: errors.length > 0 ? errors : undefined,\n }\n}\n\n/**\n * Validate a single form field value against field rules\n */\nexport function validateFieldValue(\n value: any,\n field: FormFieldParams\n): { valid: boolean; error?: string } {\n // Required check\n if (field.required) {\n if (value === undefined || value === null || value === '') {\n return { valid: false, error: `${field.label || field.name} is required` }\n }\n if (field.type === 'checkbox' && value !== true) {\n return { valid: false, error: `${field.label || field.name} must be checked` }\n }\n }\n\n // Skip further validation if value is empty and not required\n if (value === undefined || value === null || value === '') {\n return { valid: true }\n }\n\n // Type-specific validation\n switch (field.type) {\n case 'text':\n case 'textarea':\n case 'password':\n if (field.minLength && String(value).length < field.minLength) {\n return { valid: false, error: `Minimum ${field.minLength} characters required` }\n }\n if (field.maxLength && String(value).length > field.maxLength) {\n return { valid: false, error: `Maximum ${field.maxLength} characters allowed` }\n }\n if (field.pattern && !new RegExp(field.pattern).test(String(value))) {\n return { valid: false, error: 'Invalid format' }\n }\n break\n\n case 'email':\n if (!/^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/.test(String(value))) {\n return { valid: false, error: 'Invalid email address' }\n }\n break\n\n case 'number': {\n const numValue = Number(value)\n if (isNaN(numValue)) {\n return { valid: false, error: 'Must be a valid number' }\n }\n if (field.min !== undefined && numValue < field.min) {\n return { valid: false, error: `Minimum value is ${field.min}` }\n }\n if (field.max !== undefined && numValue > field.max) {\n return { valid: false, error: `Maximum value is ${field.max}` }\n }\n break\n }\n\n case 'date':\n if (field.minDate && value < field.minDate) {\n return { valid: false, error: `Date must be after ${field.minDate}` }\n }\n if (field.maxDate && value > field.maxDate) {\n return { valid: false, error: `Date must be before ${field.maxDate}` }\n }\n break\n\n case 'select':\n case 'radio':\n // Validate that value is one of the options\n if (field.options && field.options.length > 0) {\n const validValues = field.options.map((opt) => opt.value)\n if (!validValues.includes(String(value))) {\n return { valid: false, error: 'Please select a valid option' }\n }\n }\n break\n }\n\n return { valid: true }\n}\n\n/**\n * Validate entire form data against field definitions\n */\nexport function validateFormData(\n data: Record<string, any>,\n fields: FormFieldParams[]\n): { valid: boolean; errors: Record<string, string> } {\n const errors: Record<string, string> = {}\n\n for (const field of fields) {\n const result = validateFieldValue(data[field.name], field)\n if (!result.valid && result.error) {\n errors[field.name] = result.error\n }\n }\n\n return {\n valid: Object.keys(errors).length === 0,\n errors,\n }\n}\n"],"names":[],"mappings":"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"}
|