@seed-ship/mcp-ui-solid 5.5.0 → 5.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +62 -0
- package/dist/components/FormRenderer.cjs +13 -2
- package/dist/components/FormRenderer.cjs.map +1 -1
- package/dist/components/FormRenderer.d.ts.map +1 -1
- package/dist/components/FormRenderer.js +13 -2
- package/dist/components/FormRenderer.js.map +1 -1
- package/dist/components/GenerativeUIErrorBoundary.cjs +11 -0
- package/dist/components/GenerativeUIErrorBoundary.cjs.map +1 -1
- package/dist/components/GenerativeUIErrorBoundary.d.ts.map +1 -1
- package/dist/components/GenerativeUIErrorBoundary.js +11 -0
- package/dist/components/GenerativeUIErrorBoundary.js.map +1 -1
- package/dist/components/StreamingUIRenderer.cjs +49 -3
- package/dist/components/StreamingUIRenderer.cjs.map +1 -1
- package/dist/components/StreamingUIRenderer.d.ts.map +1 -1
- package/dist/components/StreamingUIRenderer.js +51 -5
- package/dist/components/StreamingUIRenderer.js.map +1 -1
- package/dist/components/UIResourceRenderer.cjs +62 -3
- package/dist/components/UIResourceRenderer.cjs.map +1 -1
- package/dist/components/UIResourceRenderer.d.ts.map +1 -1
- package/dist/components/UIResourceRenderer.js +64 -5
- package/dist/components/UIResourceRenderer.js.map +1 -1
- package/dist/context/MCPUITelemetryContext.cjs +25 -0
- package/dist/context/MCPUITelemetryContext.cjs.map +1 -0
- package/dist/context/MCPUITelemetryContext.d.ts +36 -0
- package/dist/context/MCPUITelemetryContext.d.ts.map +1 -0
- package/dist/context/MCPUITelemetryContext.js +25 -0
- package/dist/context/MCPUITelemetryContext.js.map +1 -0
- package/dist/index.cjs +6 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +4 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -1
- package/dist/mcp-ui-spec/dist/schemas.cjs +16 -5
- package/dist/mcp-ui-spec/dist/schemas.cjs.map +1 -1
- package/dist/mcp-ui-spec/dist/schemas.js +16 -5
- package/dist/mcp-ui-spec/dist/schemas.js.map +1 -1
- package/dist/services/telemetry.cjs +56 -0
- package/dist/services/telemetry.cjs.map +1 -0
- package/dist/services/telemetry.d.ts +87 -0
- package/dist/services/telemetry.d.ts.map +1 -0
- package/dist/services/telemetry.js +56 -0
- package/dist/services/telemetry.js.map +1 -0
- package/dist/services/validation.cjs +28 -26
- package/dist/services/validation.cjs.map +1 -1
- package/dist/services/validation.d.ts.map +1 -1
- package/dist/services/validation.js +29 -27
- package/dist/services/validation.js.map +1 -1
- package/package.json +2 -2
- package/src/components/FormRenderer.tsx +14 -0
- package/src/components/GenerativeUIErrorBoundary.tsx +17 -1
- package/src/components/StreamingUIRenderer.tsx +55 -3
- package/src/components/UIResourceRenderer.tsx +79 -3
- package/src/context/MCPUITelemetryContext.test.tsx +119 -0
- package/src/context/MCPUITelemetryContext.tsx +71 -0
- package/src/index.ts +15 -0
- package/src/services/telemetry.test.ts +134 -0
- package/src/services/telemetry.ts +149 -0
- package/src/services/validation.test.ts +53 -3
- package/src/services/validation.ts +54 -44
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -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 { ZodIssue, ZodSchema } from 'zod'\nimport {\n MetricComponentParamsSchema,\n TextComponentParamsSchema,\n IframeComponentParamsSchema,\n ImageComponentParamsSchema,\n LinkComponentParamsSchema,\n CarouselComponentParamsSchema,\n ArtifactComponentParamsSchema,\n ActionParamsSchema,\n VideoComponentParamsSchema,\n ImageGalleryParamsSchema,\n ActionGroupParamsSchema,\n CodeComponentParamsSchema,\n} from '@seed-ship/mcp-ui-spec'\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 * Spec-driven validation dispatch table (B.1 — v5.5.0).\n *\n * For each ComponentType where we delegate shape validation to a Zod schema\n * from `@seed-ship/mcp-ui-spec`, this table maps:\n * - the schema to safeParse against\n * - the legacy error code to emit when shape parsing fails (preserves the\n * pre-v5.5.0 `errors[].code` API contract — see MCP-UI-AUDIT-2026-04-26.md\n * §I.3.a + §J.1)\n *\n * Types deliberately omitted (kept on the imperative path):\n * - `chart`, `table` — have rich imperative validators with their own\n * codes (MISSING_DATA, DATA_LENGTH_MISMATCH, RESOURCE_LIMIT_EXCEEDED, …)\n * - `form` — spec FormFieldSchema has strict regex on field\n * names that could reject LLM-generated payloads. Conservative.\n * - `map` — spec center is `tuple([number, number])`; production\n * payloads use `{lat, lng}` objects. Avoid backward-compat regression.\n * - `modal` — all params are optional; nothing to enforce.\n * - `grid`, `footer`, `composite` — pass-through, validated elsewhere.\n */\nconst SPEC_VALIDATORS: Partial<Record<ComponentType, { schema: ZodSchema; legacyCode: string }>> = {\n metric: { schema: MetricComponentParamsSchema, legacyCode: 'INVALID_METRIC' },\n text: { schema: TextComponentParamsSchema, legacyCode: 'INVALID_TEXT' },\n iframe: { schema: IframeComponentParamsSchema, legacyCode: 'INVALID_IFRAME' },\n image: { schema: ImageComponentParamsSchema, legacyCode: 'INVALID_IMAGE' },\n link: { schema: LinkComponentParamsSchema, legacyCode: 'INVALID_LINK' },\n action: { schema: ActionParamsSchema, legacyCode: 'INVALID_ACTION' },\n video: { schema: VideoComponentParamsSchema, legacyCode: 'INVALID_VIDEO' },\n carousel: { schema: CarouselComponentParamsSchema, legacyCode: 'EMPTY_CAROUSEL' },\n 'image-gallery': { schema: ImageGalleryParamsSchema, legacyCode: 'EMPTY_GALLERY' },\n 'action-group': { schema: ActionGroupParamsSchema, legacyCode: 'EMPTY_ACTION_GROUP' },\n code: { schema: CodeComponentParamsSchema, legacyCode: 'INVALID_CODE' },\n artifact: { schema: ArtifactComponentParamsSchema, legacyCode: 'INVALID_ARTIFACT' },\n}\n\n/**\n * Map a Zod issue list to the legacy `ValidationError[]` shape.\n *\n * Preserves the pre-v5.5.0 contract: `path` always begins with `params`,\n * `code` is the per-type legacy code (so consumers that filtered by\n * `errors[].code === 'EMPTY_CAROUSEL'` keep working), `message` is Zod's\n * native human-readable message.\n */\nfunction mapZodIssuesToErrors(\n issues: readonly ZodIssue[],\n legacyCode: string\n): NonNullable<ValidationResult['errors']> {\n return issues.map((issue) => ({\n path: issue.path.length > 0 ? `params.${issue.path.join('.')}` : 'params',\n message: issue.message,\n code: legacyCode,\n }))\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 // Payment platforms (v2.2.12)\n 'polar.sh',\n 'www.polar.sh',\n 'checkout.stripe.com',\n 'js.stripe.com',\n 'billing.stripe.com',\n 'buy.stripe.com',\n 'connect.stripe.com',\n 'invoice.stripe.com',\n]\n\n/**\n * Trusted iframe domains that require allow-same-origin to function.\n * These domains need access to their own cookies/storage for auth.\n * All other whitelisted domains get a restrictive sandbox without allow-same-origin.\n */\nexport const TRUSTED_IFRAME_DOMAINS = [\n // Deposium (own domains)\n 'deposium.com',\n 'deposium.vip',\n 'deposium.ai',\n 'localhost',\n\n // Google services (need auth cookies)\n 'docs.google.com',\n 'drive.google.com',\n 'sheets.google.com',\n 'slides.google.com',\n 'maps.google.com',\n 'datastudio.google.com',\n 'lookerstudio.google.com',\n\n // Productivity (need auth)\n 'notion.so',\n 'www.notion.so',\n 'airtable.com',\n 'figma.com',\n 'www.figma.com',\n 'miro.com',\n\n // Payment (need auth + cookies for checkout)\n 'polar.sh',\n 'www.polar.sh',\n 'checkout.stripe.com',\n 'js.stripe.com',\n 'billing.stripe.com',\n 'buy.stripe.com',\n 'connect.stripe.com',\n 'invoice.stripe.com',\n\n // Business tools (need auth)\n 'app.hubspot.com',\n 'share.hubspot.com',\n 'app.powerbi.com',\n 'linear.app',\n 'www.linear.app',\n 'calendly.com',\n 'typeform.com',\n 'cal.com',\n 'canva.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 // Guard: params.data must exist with labels + datasets\n if (!params?.data) {\n return { valid: false, errors: [{ path: 'params.data', message: 'Missing chart data object', code: 'MISSING_DATA' }] }\n }\n if (!Array.isArray(params.data.datasets)) {\n return { valid: false, errors: [{ path: 'params.data.datasets', message: 'Missing or invalid datasets array', code: 'MISSING_DATASETS' }] }\n }\n // Detect point-based charts (scatter/bubble) or object data (time-series line)\n const chartType = params.type || 'bar'\n const firstDataPoint = params.data.datasets[0]?.data?.[0]\n const hasObjectData = typeof firstDataPoint === 'object' && firstDataPoint !== null && 'x' in firstDataPoint\n const isPointChart = chartType === 'scatter' || chartType === 'bubble' || hasObjectData\n\n // Labels required only for categorical charts (not scatter/bubble/time-series)\n if (!isPointChart) {\n if (!Array.isArray(params.data.labels)) {\n return { valid: false, errors: [{ path: 'params.data.labels', message: 'Missing or invalid labels array', code: 'MISSING_LABELS' }] }\n }\n }\n\n // Validate data points count\n const totalDataPoints = params.data.datasets.reduce(\n (sum, dataset) => sum + (Array.isArray(dataset.data) ? dataset.data.length : 0),\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 // Length mismatch check — only for categorical charts, skip empty datasets\n if (!isPointChart && Array.isArray(params.data.labels)) {\n const expectedLength = params.data.labels.length\n for (const [index, dataset] of params.data.datasets.entries()) {\n if (Array.isArray(dataset.data) && dataset.data.length > 0 && 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\n // Data type validation — numbers for categorical, {x,y} objects for point charts\n for (const [index, dataset] of params.data.datasets.entries()) {\n if (!Array.isArray(dataset.data)) continue\n for (const [dataIndex, value] of dataset.data.entries()) {\n if (isPointChart) {\n const vObj = value as any\n if (typeof value !== 'object' || value === null || vObj.x == null || typeof vObj.y !== 'number') {\n errors.push({\n path: `params.data.datasets[${index}].data[${dataIndex}]`,\n message: `Invalid point data: expected {x, y} object`,\n code: 'INVALID_POINT_DATA',\n })\n }\n } else {\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\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 * Get the appropriate sandbox attribute for an iframe URL.\n *\n * Trusted domains (Google, Deposium, payment, auth-requiring services) get\n * `allow-same-origin` so they can access their own cookies/storage.\n * All other whitelisted domains get a restrictive sandbox without it,\n * preventing access to the parent page's localStorage/cookies.\n *\n * @param url - The iframe URL\n * @param options - Optional custom trusted domains\n * @returns sandbox attribute string\n */\nexport function getIframeSandbox(\n url: string,\n options?: { customTrustedDomains?: string[] }\n): string {\n const baseSandbox = 'allow-scripts allow-popups'\n\n try {\n const domain = new URL(url).hostname\n let trustedList = TRUSTED_IFRAME_DOMAINS\n if (options?.customTrustedDomains) {\n trustedList = [...TRUSTED_IFRAME_DOMAINS, ...options.customTrustedDomains]\n }\n\n const isTrusted = trustedList.some(\n (trusted) => domain === trusted || domain.endsWith(`.${trusted}`)\n )\n\n if (isTrusted) {\n return `${baseSandbox} allow-same-origin allow-forms`\n }\n } catch {\n // Invalid URL — use restrictive sandbox\n }\n\n return baseSandbox\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 // Guard: params must exist\n if (!component.params) {\n return { valid: false, errors: [{ path: 'params', message: 'Missing component params', code: 'MISSING_PARAMS' }] }\n }\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 (B.1 — v5.5.0).\n //\n // 12 types delegate shape validation to Zod schemas in `mcp-ui-spec` via\n // SPEC_VALIDATORS. The 5 remaining types stay imperative because they\n // need cross-field consistency, resource limits, or backward-compat logic\n // that pure Zod can't express without `.refine()` (see SPEC_VALIDATORS docstring).\n const specValidator = SPEC_VALIDATORS[component.type]\n if (specValidator) {\n const result = specValidator.schema.safeParse(component.params)\n if (!result.success) {\n errors.push(...mapZodIssuesToErrors(result.error.issues, specValidator.legacyCode))\n }\n // Iframe + video: chain the domain whitelist post-check ONLY when the\n // shape parse succeeded (i.e. url is present and a string). Skipping the\n // domain check when shape failed avoids cascading errors on the same field.\n if (result.success && (component.type === 'iframe' || component.type === 'video')) {\n const url = (component.params as { url?: string })?.url\n if (typeof url === 'string') {\n const domainResult = validateIframeDomain(url, {\n policy: options?.iframePolicy,\n customDomains: options?.customIframeDomains,\n })\n if (!domainResult.valid) {\n errors.push(...(domainResult.errors || []))\n }\n }\n }\n } else {\n // Imperative path for chart/table/form/map/modal/grid/footer/composite.\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 'form': {\n const formParams = component.params as { fields?: unknown[] }\n if (!Array.isArray(formParams.fields) || formParams.fields.length === 0) {\n errors.push({ path: 'params.fields', message: 'Form must have non-empty fields array', code: 'EMPTY_FORM' })\n }\n break\n }\n\n case 'map': {\n // Map can auto-detect center from markers, so center is not strictly required.\n // Spec MapComponentParamsSchema would be too strict (tuple-only center) — kept imperative.\n const mapParams = component.params as { center?: unknown; markers?: unknown[] }\n if (!mapParams.center && (!Array.isArray(mapParams.markers) || mapParams.markers.length === 0)) {\n errors.push({ path: 'params', message: 'Map must have center or markers', code: 'INVALID_MAP' })\n }\n break\n }\n\n case 'modal':\n // Modal is valid with minimal params (title optional, content can be children).\n break\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\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 // valueFormat validation (v4.3.0) — runs after type-specific checks\n if (field.valueFormat && value !== undefined && value !== null && value !== '') {\n const vals = Array.isArray(value) ? value : [String(value)]\n for (const v of vals) {\n if (!new RegExp(field.valueFormat).test(v)) {\n return { valid: false, error: field.valueFormatHint || `Invalid format (expected: ${field.valueFormat})` }\n }\n }\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":["MetricComponentParamsSchema","TextComponentParamsSchema","IframeComponentParamsSchema","ImageComponentParamsSchema","LinkComponentParamsSchema","ActionParamsSchema","VideoComponentParamsSchema","CarouselComponentParamsSchema","ImageGalleryParamsSchema","ActionGroupParamsSchema","CodeComponentParamsSchema","ArtifactComponentParamsSchema"],"mappings":";;;AA0CA,MAAM,4CAAyC,IAAmB;AAAA,EAChE;AAAA,EAAS;AAAA,EAAS;AAAA,EAAU;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAU;AAAA,EAAS;AAAA,EAC/D;AAAA,EAAU;AAAA,EAAU;AAAA,EAAY;AAAA,EAAY;AAAA,EAAQ;AAAA,EACpD;AAAA,EAAgB;AAAA,EAAiB;AAAA,EAAS;AAAA,EAAQ;AACpD,CAAC;AAsBD,MAAM,kBAA6F;AAAA,EACjG,QAAQ,EAAE,QAAQA,qCAA6B,YAAY,iBAAA;AAAA,EAC3D,MAAM,EAAE,QAAQC,mCAA2B,YAAY,eAAA;AAAA,EACvD,QAAQ,EAAE,QAAQC,qCAA6B,YAAY,iBAAA;AAAA,EAC3D,OAAO,EAAE,QAAQC,oCAA4B,YAAY,gBAAA;AAAA,EACzD,MAAM,EAAE,QAAQC,mCAA2B,YAAY,eAAA;AAAA,EACvD,QAAQ,EAAE,QAAQC,4BAAoB,YAAY,iBAAA;AAAA,EAClD,OAAO,EAAE,QAAQC,oCAA4B,YAAY,gBAAA;AAAA,EACzD,UAAU,EAAE,QAAQC,uCAA+B,YAAY,iBAAA;AAAA,EAC/D,iBAAiB,EAAE,QAAQC,kCAA0B,YAAY,gBAAA;AAAA,EACjE,gBAAgB,EAAE,QAAQC,iCAAyB,YAAY,qBAAA;AAAA,EAC/D,MAAM,EAAE,QAAQC,mCAA2B,YAAY,eAAA;AAAA,EACvD,UAAU,EAAE,QAAQC,QAAAA,+BAA+B,YAAY,mBAAA;AACjE;AAUA,SAAS,qBACP,QACA,YACyC;AACzC,SAAO,OAAO,IAAI,CAAC,WAAW;AAAA,IAC5B,MAAM,MAAM,KAAK,SAAS,IAAI,UAAU,MAAM,KAAK,KAAK,GAAG,CAAC,KAAK;AAAA,IACjE,SAAS,MAAM;AAAA,IACf,MAAM;AAAA,EAAA,EACN;AACJ;AAKO,MAAM,0BAA0C;AAAA,EACrD,eAAe;AAAA,EACf,cAAc;AAAA,EACd,gBAAgB,KAAK;AAAA;AAAA,EACrB,eAAe;AAAA;AACjB;AASO,MAAM,yBAAyB;AAAA;AAAA,EAEpC;AAAA,EACA;AAAA;AAAA,EAGA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAGA;AAAA;AAAA,EAGA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAGA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAGA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAGA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAGA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAGA;AAAA,EACA;AAAA;AAAA,EAGA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAGA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAGA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAGA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAGA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAGA;AAAA;AAAA,EAGA;AAAA,EACA;AAAA;AAAA,EAGA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAGA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAGA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAOO,MAAM,yBAAyB;AAAA;AAAA,EAEpC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAGA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAGA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAGA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAGA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAC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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 { ZodIssue, ZodSchema } from 'zod'\nimport {\n MetricComponentParamsSchema,\n TextComponentParamsSchema,\n IframeComponentParamsSchema,\n ImageComponentParamsSchema,\n LinkComponentParamsSchema,\n CarouselComponentParamsSchema,\n ArtifactComponentParamsSchema,\n ActionParamsSchema,\n VideoComponentParamsSchema,\n ImageGalleryParamsSchema,\n ActionGroupParamsSchema,\n CodeComponentParamsSchema,\n // v5.6.0 — added after spec@5.0.2 relaxations (deposium audit §M)\n MapComponentParamsSchema,\n FormComponentParamsSchema,\n} from '@seed-ship/mcp-ui-spec'\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 * Spec-driven validation dispatch table (B.1 — v5.5.0, expanded in v5.6.0).\n *\n * For each ComponentType where we delegate shape validation to a Zod schema\n * from `@seed-ship/mcp-ui-spec`, this table maps:\n * - the schema to safeParse against\n * - the legacy error code to emit when shape parsing fails (preserves the\n * pre-v5.5.0 `errors[].code` API contract — see MCP-UI-AUDIT-2026-04-26.md\n * §I.3.a + §J.1)\n *\n * **v5.6.0** : `map` and `form` joined the dispatch after spec@5.0.2 relaxed\n * their schemas (LatLngPoint union for map.center, regex relax for\n * field.name) per deposium audit §L answers. Closed B.1 to **14/17 types**.\n *\n * Types deliberately omitted (kept on the imperative path):\n * - `chart`, `table` — have rich imperative validators with their own\n * codes (MISSING_DATA, DATA_LENGTH_MISMATCH, RESOURCE_LIMIT_EXCEEDED, …)\n * - `modal` — all params are optional; nothing to enforce.\n * - `grid`, `footer`, `composite` — pass-through, validated elsewhere.\n */\nconst SPEC_VALIDATORS: Partial<Record<ComponentType, { schema: ZodSchema; legacyCode: string }>> = {\n metric: { schema: MetricComponentParamsSchema, legacyCode: 'INVALID_METRIC' },\n text: { schema: TextComponentParamsSchema, legacyCode: 'INVALID_TEXT' },\n iframe: { schema: IframeComponentParamsSchema, legacyCode: 'INVALID_IFRAME' },\n image: { schema: ImageComponentParamsSchema, legacyCode: 'INVALID_IMAGE' },\n link: { schema: LinkComponentParamsSchema, legacyCode: 'INVALID_LINK' },\n action: { schema: ActionParamsSchema, legacyCode: 'INVALID_ACTION' },\n video: { schema: VideoComponentParamsSchema, legacyCode: 'INVALID_VIDEO' },\n carousel: { schema: CarouselComponentParamsSchema, legacyCode: 'EMPTY_CAROUSEL' },\n 'image-gallery': { schema: ImageGalleryParamsSchema, legacyCode: 'EMPTY_GALLERY' },\n 'action-group': { schema: ActionGroupParamsSchema, legacyCode: 'EMPTY_ACTION_GROUP' },\n code: { schema: CodeComponentParamsSchema, legacyCode: 'INVALID_CODE' },\n artifact: { schema: ArtifactComponentParamsSchema, legacyCode: 'INVALID_ARTIFACT' },\n // v5.6.0 additions\n form: { schema: FormComponentParamsSchema, legacyCode: 'EMPTY_FORM' },\n map: { schema: MapComponentParamsSchema, legacyCode: 'INVALID_MAP' },\n}\n\n/**\n * Map a Zod issue list to the legacy `ValidationError[]` shape.\n *\n * Preserves the pre-v5.5.0 contract: `path` always begins with `params`,\n * `code` is the per-type legacy code (so consumers that filtered by\n * `errors[].code === 'EMPTY_CAROUSEL'` keep working), `message` is Zod's\n * native human-readable message.\n */\nfunction mapZodIssuesToErrors(\n issues: readonly ZodIssue[],\n legacyCode: string\n): NonNullable<ValidationResult['errors']> {\n return issues.map((issue) => ({\n path: issue.path.length > 0 ? `params.${issue.path.join('.')}` : 'params',\n message: issue.message,\n code: legacyCode,\n }))\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 // Payment platforms (v2.2.12)\n 'polar.sh',\n 'www.polar.sh',\n 'checkout.stripe.com',\n 'js.stripe.com',\n 'billing.stripe.com',\n 'buy.stripe.com',\n 'connect.stripe.com',\n 'invoice.stripe.com',\n]\n\n/**\n * Trusted iframe domains that require allow-same-origin to function.\n * These domains need access to their own cookies/storage for auth.\n * All other whitelisted domains get a restrictive sandbox without allow-same-origin.\n */\nexport const TRUSTED_IFRAME_DOMAINS = [\n // Deposium (own domains)\n 'deposium.com',\n 'deposium.vip',\n 'deposium.ai',\n 'localhost',\n\n // Google services (need auth cookies)\n 'docs.google.com',\n 'drive.google.com',\n 'sheets.google.com',\n 'slides.google.com',\n 'maps.google.com',\n 'datastudio.google.com',\n 'lookerstudio.google.com',\n\n // Productivity (need auth)\n 'notion.so',\n 'www.notion.so',\n 'airtable.com',\n 'figma.com',\n 'www.figma.com',\n 'miro.com',\n\n // Payment (need auth + cookies for checkout)\n 'polar.sh',\n 'www.polar.sh',\n 'checkout.stripe.com',\n 'js.stripe.com',\n 'billing.stripe.com',\n 'buy.stripe.com',\n 'connect.stripe.com',\n 'invoice.stripe.com',\n\n // Business tools (need auth)\n 'app.hubspot.com',\n 'share.hubspot.com',\n 'app.powerbi.com',\n 'linear.app',\n 'www.linear.app',\n 'calendly.com',\n 'typeform.com',\n 'cal.com',\n 'canva.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 // Guard: params.data must exist with labels + datasets\n if (!params?.data) {\n return { valid: false, errors: [{ path: 'params.data', message: 'Missing chart data object', code: 'MISSING_DATA' }] }\n }\n if (!Array.isArray(params.data.datasets)) {\n return { valid: false, errors: [{ path: 'params.data.datasets', message: 'Missing or invalid datasets array', code: 'MISSING_DATASETS' }] }\n }\n // Detect point-based charts (scatter/bubble) or object data (time-series line)\n const chartType = params.type || 'bar'\n const firstDataPoint = params.data.datasets[0]?.data?.[0]\n const hasObjectData = typeof firstDataPoint === 'object' && firstDataPoint !== null && 'x' in firstDataPoint\n const isPointChart = chartType === 'scatter' || chartType === 'bubble' || hasObjectData\n\n // Labels required only for categorical charts (not scatter/bubble/time-series)\n if (!isPointChart) {\n if (!Array.isArray(params.data.labels)) {\n return { valid: false, errors: [{ path: 'params.data.labels', message: 'Missing or invalid labels array', code: 'MISSING_LABELS' }] }\n }\n }\n\n // Validate data points count\n const totalDataPoints = params.data.datasets.reduce(\n (sum, dataset) => sum + (Array.isArray(dataset.data) ? dataset.data.length : 0),\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 // Length mismatch check — only for categorical charts, skip empty datasets\n if (!isPointChart && Array.isArray(params.data.labels)) {\n const expectedLength = params.data.labels.length\n for (const [index, dataset] of params.data.datasets.entries()) {\n if (Array.isArray(dataset.data) && dataset.data.length > 0 && 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\n // Data type validation — numbers for categorical, {x,y} objects for point charts\n for (const [index, dataset] of params.data.datasets.entries()) {\n if (!Array.isArray(dataset.data)) continue\n for (const [dataIndex, value] of dataset.data.entries()) {\n if (isPointChart) {\n const vObj = value as any\n if (typeof value !== 'object' || value === null || vObj.x == null || typeof vObj.y !== 'number') {\n errors.push({\n path: `params.data.datasets[${index}].data[${dataIndex}]`,\n message: `Invalid point data: expected {x, y} object`,\n code: 'INVALID_POINT_DATA',\n })\n }\n } else {\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\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 // SECURITY (v5.5.1) — pre-fix bug: predicate was `allowed === 'localhost'`\n // which trivially returned true for every URL once the whitelist contained\n // 'localhost' (an entry from DEFAULT_IFRAME_DOMAINS), making the entire\n // domain whitelist inoperative. Fixed: only the URL's actual hostname\n // being 'localhost' (or a 127.0.0.x loopback) bypasses the whitelist.\n const isLoopback = domain === 'localhost' || /^127(\\.\\d{1,3}){3}$/.test(domain)\n const isAllowed =\n isLoopback ||\n effectiveWhitelist.some(\n (allowed) => allowed !== 'localhost' && (domain === allowed || domain.endsWith(`.${allowed}`))\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 * Get the appropriate sandbox attribute for an iframe URL.\n *\n * Trusted domains (Google, Deposium, payment, auth-requiring services) get\n * `allow-same-origin` so they can access their own cookies/storage.\n * All other whitelisted domains get a restrictive sandbox without it,\n * preventing access to the parent page's localStorage/cookies.\n *\n * @param url - The iframe URL\n * @param options - Optional custom trusted domains\n * @returns sandbox attribute string\n */\nexport function getIframeSandbox(\n url: string,\n options?: { customTrustedDomains?: string[] }\n): string {\n const baseSandbox = 'allow-scripts allow-popups'\n\n try {\n const domain = new URL(url).hostname\n let trustedList = TRUSTED_IFRAME_DOMAINS\n if (options?.customTrustedDomains) {\n trustedList = [...TRUSTED_IFRAME_DOMAINS, ...options.customTrustedDomains]\n }\n\n const isTrusted = trustedList.some(\n (trusted) => domain === trusted || domain.endsWith(`.${trusted}`)\n )\n\n if (isTrusted) {\n return `${baseSandbox} allow-same-origin allow-forms`\n }\n } catch {\n // Invalid URL — use restrictive sandbox\n }\n\n return baseSandbox\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 // Guard: params must exist\n if (!component.params) {\n return { valid: false, errors: [{ path: 'params', message: 'Missing component params', code: 'MISSING_PARAMS' }] }\n }\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 (B.1 — v5.5.0, expanded v5.6.0).\n //\n // 14 types delegate shape validation to Zod schemas in `mcp-ui-spec` via\n // SPEC_VALIDATORS. The 3 remaining types stay imperative because they\n // need cross-field consistency, resource limits, or have nothing to validate\n // (see SPEC_VALIDATORS docstring).\n const specValidator = SPEC_VALIDATORS[component.type]\n if (specValidator) {\n const result = specValidator.schema.safeParse(component.params)\n if (!result.success) {\n errors.push(...mapZodIssuesToErrors(result.error.issues, specValidator.legacyCode))\n }\n // Post-spec chained checks. Skipped when the shape parse failed to avoid\n // cascading errors on already-broken payloads.\n if (result.success) {\n // Iframe + video: domain whitelist\n if (component.type === 'iframe' || component.type === 'video') {\n const url = (component.params as { url?: string })?.url\n if (typeof url === 'string') {\n const domainResult = validateIframeDomain(url, {\n policy: options?.iframePolicy,\n customDomains: options?.customIframeDomains,\n })\n if (!domainResult.valid) {\n errors.push(...(domainResult.errors || []))\n }\n }\n }\n // Map (v5.6.0): center OR markers required. Spec has both .optional()\n // since auto-center from markers is supported, but we need ONE of them.\n if (component.type === 'map') {\n const mapParams = component.params as { center?: unknown; markers?: unknown[] }\n if (!mapParams.center && (!Array.isArray(mapParams.markers) || mapParams.markers.length === 0)) {\n errors.push({\n path: 'params',\n message: 'Map must have center or markers',\n code: 'INVALID_MAP',\n })\n }\n }\n }\n } else {\n // Imperative path for chart/table/modal/grid/footer/composite.\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 'modal':\n // Modal is valid with minimal params (title optional, content can be children).\n break\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\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 // valueFormat validation (v4.3.0) — runs after type-specific checks\n if (field.valueFormat && value !== undefined && value !== null && value !== '') {\n const vals = Array.isArray(value) ? value : [String(value)]\n for (const v of vals) {\n if (!new RegExp(field.valueFormat).test(v)) {\n return { valid: false, error: field.valueFormatHint || `Invalid format (expected: ${field.valueFormat})` }\n }\n }\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":["MetricComponentParamsSchema","TextComponentParamsSchema","IframeComponentParamsSchema","ImageComponentParamsSchema","LinkComponentParamsSchema","ActionParamsSchema","VideoComponentParamsSchema","CarouselComponentParamsSchema","ImageGalleryParamsSchema","ActionGroupParamsSchema","CodeComponentParamsSchema","ArtifactComponentParamsSchema","FormComponentParamsSchema","MapComponentParamsSchema"],"mappings":";;;AA6CA,MAAM,4CAAyC,IAAmB;AAAA,EAChE;AAAA,EAAS;AAAA,EAAS;AAAA,EAAU;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAU;AAAA,EAAS;AAAA,EAC/D;AAAA,EAAU;AAAA,EAAU;AAAA,EAAY;AAAA,EAAY;AAAA,EAAQ;AAAA,EACpD;AAAA,EAAgB;AAAA,EAAiB;AAAA,EAAS;AAAA,EAAQ;AACpD,CAAC;AAsBD,MAAM,kBAA6F;AAAA,EACjG,QAAQ,EAAE,QAAQA,qCAA6B,YAAY,iBAAA;AAAA,EAC3D,MAAM,EAAE,QAAQC,mCAA2B,YAAY,eAAA;AAAA,EACvD,QAAQ,EAAE,QAAQC,qCAA6B,YAAY,iBAAA;AAAA,EAC3D,OAAO,EAAE,QAAQC,oCAA4B,YAAY,gBAAA;AAAA,EACzD,MAAM,EAAE,QAAQC,mCAA2B,YAAY,eAAA;AAAA,EACvD,QAAQ,EAAE,QAAQC,4BAAoB,YAAY,iBAAA;AAAA,EAClD,OAAO,EAAE,QAAQC,oCAA4B,YAAY,gBAAA;AAAA,EACzD,UAAU,EAAE,QAAQC,uCAA+B,YAAY,iBAAA;AAAA,EAC/D,iBAAiB,EAAE,QAAQC,kCAA0B,YAAY,gBAAA;AAAA,EACjE,gBAAgB,EAAE,QAAQC,iCAAyB,YAAY,qBAAA;AAAA,EAC/D,MAAM,EAAE,QAAQC,mCAA2B,YAAY,eAAA;AAAA,EACvD,UAAU,EAAE,QAAQC,uCAA+B,YAAY,mBAAA;AAAA;AAAA,EAE/D,MAAM,EAAE,QAAQC,mCAA2B,YAAY,aAAA;AAAA,EACvD,KAAK,EAAE,QAAQC,QAAAA,0BAA0B,YAAY,cAAA;AACvD;AAUA,SAAS,qBACP,QACA,YACyC;AACzC,SAAO,OAAO,IAAI,CAAC,WAAW;AAAA,IAC5B,MAAM,MAAM,KAAK,SAAS,IAAI,UAAU,MAAM,KAAK,KAAK,GAAG,CAAC,KAAK;AAAA,IACjE,SAAS,MAAM;AAAA,IACf,MAAM;AAAA,EAAA,EACN;AACJ;AAKO,MAAM,0BAA0C;AAAA,EACrD,eAAe;AAAA,EACf,cAAc;AAAA,EACd,gBAAgB,KAAK;AAAA;AAAA,EACrB,eAAe;AAAA;AACjB;AASO,MAAM,yBAAyB;AAAA;AAAA,EAEpC;AAAA,EACA;AAAA;AAAA,EAGA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAGA;AAAA;AAAA,EAGA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAGA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAGA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAGA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAGA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAGA;AAAA,EACA;AAAA;AAAA,EAGA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAGA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAGA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAGA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAGA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAGA;AAAA;AAAA,EAGA;AAAA,EACA;AAAA;AAAA,EAGA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAGA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAGA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAOO,MAAM,yBAAyB;AAAA;AAAA,EAEpC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAGA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAGA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAGA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../../src/services/validation.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;
|
|
1
|
+
{"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../../src/services/validation.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAoBH,OAAO,KAAK,EACV,WAAW,EACX,QAAQ,EACR,gBAAgB,EAChB,cAAc,EACd,oBAAoB,EACpB,oBAAoB,EACpB,eAAe,EACf,YAAY,EACZ,iBAAiB,EAElB,MAAM,UAAU,CAAA;AAqEjB;;GAEG;AACH,eAAO,MAAM,uBAAuB,EAAE,cAKrC,CAAA;AAED;;;;;;GAMG;AACH,eAAO,MAAM,sBAAsB,UA0HlC,CAAA;AAED;;;;GAIG;AACH,eAAO,MAAM,sBAAsB,UA4ClC,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,CAgFlB;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,CAsDlB;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,gBAAgB,CAC9B,GAAG,EAAE,MAAM,EACX,OAAO,CAAC,EAAE;IAAE,oBAAoB,CAAC,EAAE,MAAM,EAAE,CAAA;CAAE,GAC5C,MAAM,CAsBR;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAC/B,SAAS,EAAE,WAAW,EACtB,OAAO,CAAC,EAAE,iBAAiB,GAC1B,gBAAgB,CAuGlB;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,CAoFpC;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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ArtifactComponentParamsSchema, CodeComponentParamsSchema, ActionGroupParamsSchema, ImageGalleryParamsSchema, CarouselComponentParamsSchema, VideoComponentParamsSchema, ActionParamsSchema, LinkComponentParamsSchema, ImageComponentParamsSchema, IframeComponentParamsSchema, TextComponentParamsSchema, MetricComponentParamsSchema } from "../mcp-ui-spec/dist/schemas.js";
|
|
1
|
+
import { MapComponentParamsSchema, FormComponentParamsSchema, ArtifactComponentParamsSchema, CodeComponentParamsSchema, ActionGroupParamsSchema, ImageGalleryParamsSchema, CarouselComponentParamsSchema, VideoComponentParamsSchema, ActionParamsSchema, LinkComponentParamsSchema, ImageComponentParamsSchema, IframeComponentParamsSchema, TextComponentParamsSchema, MetricComponentParamsSchema } from "../mcp-ui-spec/dist/schemas.js";
|
|
2
2
|
const KNOWN_COMPONENT_TYPES = /* @__PURE__ */ new Set([
|
|
3
3
|
"chart",
|
|
4
4
|
"table",
|
|
@@ -32,7 +32,10 @@ const SPEC_VALIDATORS = {
|
|
|
32
32
|
"image-gallery": { schema: ImageGalleryParamsSchema, legacyCode: "EMPTY_GALLERY" },
|
|
33
33
|
"action-group": { schema: ActionGroupParamsSchema, legacyCode: "EMPTY_ACTION_GROUP" },
|
|
34
34
|
code: { schema: CodeComponentParamsSchema, legacyCode: "INVALID_CODE" },
|
|
35
|
-
artifact: { schema: ArtifactComponentParamsSchema, legacyCode: "INVALID_ARTIFACT" }
|
|
35
|
+
artifact: { schema: ArtifactComponentParamsSchema, legacyCode: "INVALID_ARTIFACT" },
|
|
36
|
+
// v5.6.0 additions
|
|
37
|
+
form: { schema: FormComponentParamsSchema, legacyCode: "EMPTY_FORM" },
|
|
38
|
+
map: { schema: MapComponentParamsSchema, legacyCode: "INVALID_MAP" }
|
|
36
39
|
};
|
|
37
40
|
function mapZodIssuesToErrors(issues, legacyCode) {
|
|
38
41
|
return issues.map((issue) => ({
|
|
@@ -391,8 +394,9 @@ function validateIframeDomain(url, options) {
|
|
|
391
394
|
if ((options == null ? void 0 : options.policy) === "extend" && options.customDomains) {
|
|
392
395
|
effectiveWhitelist = [...DEFAULT_IFRAME_DOMAINS, ...options.customDomains];
|
|
393
396
|
}
|
|
394
|
-
const
|
|
395
|
-
|
|
397
|
+
const isLoopback = domain === "localhost" || /^127(\.\d{1,3}){3}$/.test(domain);
|
|
398
|
+
const isAllowed = isLoopback || effectiveWhitelist.some(
|
|
399
|
+
(allowed) => allowed !== "localhost" && (domain === allowed || domain.endsWith(`.${allowed}`))
|
|
396
400
|
);
|
|
397
401
|
if (!isAllowed) {
|
|
398
402
|
return {
|
|
@@ -459,15 +463,27 @@ function validateComponent(component, options) {
|
|
|
459
463
|
if (!result.success) {
|
|
460
464
|
errors.push(...mapZodIssuesToErrors(result.error.issues, specValidator.legacyCode));
|
|
461
465
|
}
|
|
462
|
-
if (result.success
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
466
|
+
if (result.success) {
|
|
467
|
+
if (component.type === "iframe" || component.type === "video") {
|
|
468
|
+
const url = (_a = component.params) == null ? void 0 : _a.url;
|
|
469
|
+
if (typeof url === "string") {
|
|
470
|
+
const domainResult = validateIframeDomain(url, {
|
|
471
|
+
policy: options == null ? void 0 : options.iframePolicy,
|
|
472
|
+
customDomains: options == null ? void 0 : options.customIframeDomains
|
|
473
|
+
});
|
|
474
|
+
if (!domainResult.valid) {
|
|
475
|
+
errors.push(...domainResult.errors || []);
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
if (component.type === "map") {
|
|
480
|
+
const mapParams = component.params;
|
|
481
|
+
if (!mapParams.center && (!Array.isArray(mapParams.markers) || mapParams.markers.length === 0)) {
|
|
482
|
+
errors.push({
|
|
483
|
+
path: "params",
|
|
484
|
+
message: "Map must have center or markers",
|
|
485
|
+
code: "INVALID_MAP"
|
|
486
|
+
});
|
|
471
487
|
}
|
|
472
488
|
}
|
|
473
489
|
}
|
|
@@ -487,20 +503,6 @@ function validateComponent(component, options) {
|
|
|
487
503
|
}
|
|
488
504
|
break;
|
|
489
505
|
}
|
|
490
|
-
case "form": {
|
|
491
|
-
const formParams = component.params;
|
|
492
|
-
if (!Array.isArray(formParams.fields) || formParams.fields.length === 0) {
|
|
493
|
-
errors.push({ path: "params.fields", message: "Form must have non-empty fields array", code: "EMPTY_FORM" });
|
|
494
|
-
}
|
|
495
|
-
break;
|
|
496
|
-
}
|
|
497
|
-
case "map": {
|
|
498
|
-
const mapParams = component.params;
|
|
499
|
-
if (!mapParams.center && (!Array.isArray(mapParams.markers) || mapParams.markers.length === 0)) {
|
|
500
|
-
errors.push({ path: "params", message: "Map must have center or markers", code: "INVALID_MAP" });
|
|
501
|
-
}
|
|
502
|
-
break;
|
|
503
|
-
}
|
|
504
506
|
case "modal":
|
|
505
507
|
break;
|
|
506
508
|
default:
|
|
@@ -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 { ZodIssue, ZodSchema } from 'zod'\nimport {\n MetricComponentParamsSchema,\n TextComponentParamsSchema,\n IframeComponentParamsSchema,\n ImageComponentParamsSchema,\n LinkComponentParamsSchema,\n CarouselComponentParamsSchema,\n ArtifactComponentParamsSchema,\n ActionParamsSchema,\n VideoComponentParamsSchema,\n ImageGalleryParamsSchema,\n ActionGroupParamsSchema,\n CodeComponentParamsSchema,\n} from '@seed-ship/mcp-ui-spec'\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 * Spec-driven validation dispatch table (B.1 — v5.5.0).\n *\n * For each ComponentType where we delegate shape validation to a Zod schema\n * from `@seed-ship/mcp-ui-spec`, this table maps:\n * - the schema to safeParse against\n * - the legacy error code to emit when shape parsing fails (preserves the\n * pre-v5.5.0 `errors[].code` API contract — see MCP-UI-AUDIT-2026-04-26.md\n * §I.3.a + §J.1)\n *\n * Types deliberately omitted (kept on the imperative path):\n * - `chart`, `table` — have rich imperative validators with their own\n * codes (MISSING_DATA, DATA_LENGTH_MISMATCH, RESOURCE_LIMIT_EXCEEDED, …)\n * - `form` — spec FormFieldSchema has strict regex on field\n * names that could reject LLM-generated payloads. Conservative.\n * - `map` — spec center is `tuple([number, number])`; production\n * payloads use `{lat, lng}` objects. Avoid backward-compat regression.\n * - `modal` — all params are optional; nothing to enforce.\n * - `grid`, `footer`, `composite` — pass-through, validated elsewhere.\n */\nconst SPEC_VALIDATORS: Partial<Record<ComponentType, { schema: ZodSchema; legacyCode: string }>> = {\n metric: { schema: MetricComponentParamsSchema, legacyCode: 'INVALID_METRIC' },\n text: { schema: TextComponentParamsSchema, legacyCode: 'INVALID_TEXT' },\n iframe: { schema: IframeComponentParamsSchema, legacyCode: 'INVALID_IFRAME' },\n image: { schema: ImageComponentParamsSchema, legacyCode: 'INVALID_IMAGE' },\n link: { schema: LinkComponentParamsSchema, legacyCode: 'INVALID_LINK' },\n action: { schema: ActionParamsSchema, legacyCode: 'INVALID_ACTION' },\n video: { schema: VideoComponentParamsSchema, legacyCode: 'INVALID_VIDEO' },\n carousel: { schema: CarouselComponentParamsSchema, legacyCode: 'EMPTY_CAROUSEL' },\n 'image-gallery': { schema: ImageGalleryParamsSchema, legacyCode: 'EMPTY_GALLERY' },\n 'action-group': { schema: ActionGroupParamsSchema, legacyCode: 'EMPTY_ACTION_GROUP' },\n code: { schema: CodeComponentParamsSchema, legacyCode: 'INVALID_CODE' },\n artifact: { schema: ArtifactComponentParamsSchema, legacyCode: 'INVALID_ARTIFACT' },\n}\n\n/**\n * Map a Zod issue list to the legacy `ValidationError[]` shape.\n *\n * Preserves the pre-v5.5.0 contract: `path` always begins with `params`,\n * `code` is the per-type legacy code (so consumers that filtered by\n * `errors[].code === 'EMPTY_CAROUSEL'` keep working), `message` is Zod's\n * native human-readable message.\n */\nfunction mapZodIssuesToErrors(\n issues: readonly ZodIssue[],\n legacyCode: string\n): NonNullable<ValidationResult['errors']> {\n return issues.map((issue) => ({\n path: issue.path.length > 0 ? `params.${issue.path.join('.')}` : 'params',\n message: issue.message,\n code: legacyCode,\n }))\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 // Payment platforms (v2.2.12)\n 'polar.sh',\n 'www.polar.sh',\n 'checkout.stripe.com',\n 'js.stripe.com',\n 'billing.stripe.com',\n 'buy.stripe.com',\n 'connect.stripe.com',\n 'invoice.stripe.com',\n]\n\n/**\n * Trusted iframe domains that require allow-same-origin to function.\n * These domains need access to their own cookies/storage for auth.\n * All other whitelisted domains get a restrictive sandbox without allow-same-origin.\n */\nexport const TRUSTED_IFRAME_DOMAINS = [\n // Deposium (own domains)\n 'deposium.com',\n 'deposium.vip',\n 'deposium.ai',\n 'localhost',\n\n // Google services (need auth cookies)\n 'docs.google.com',\n 'drive.google.com',\n 'sheets.google.com',\n 'slides.google.com',\n 'maps.google.com',\n 'datastudio.google.com',\n 'lookerstudio.google.com',\n\n // Productivity (need auth)\n 'notion.so',\n 'www.notion.so',\n 'airtable.com',\n 'figma.com',\n 'www.figma.com',\n 'miro.com',\n\n // Payment (need auth + cookies for checkout)\n 'polar.sh',\n 'www.polar.sh',\n 'checkout.stripe.com',\n 'js.stripe.com',\n 'billing.stripe.com',\n 'buy.stripe.com',\n 'connect.stripe.com',\n 'invoice.stripe.com',\n\n // Business tools (need auth)\n 'app.hubspot.com',\n 'share.hubspot.com',\n 'app.powerbi.com',\n 'linear.app',\n 'www.linear.app',\n 'calendly.com',\n 'typeform.com',\n 'cal.com',\n 'canva.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 // Guard: params.data must exist with labels + datasets\n if (!params?.data) {\n return { valid: false, errors: [{ path: 'params.data', message: 'Missing chart data object', code: 'MISSING_DATA' }] }\n }\n if (!Array.isArray(params.data.datasets)) {\n return { valid: false, errors: [{ path: 'params.data.datasets', message: 'Missing or invalid datasets array', code: 'MISSING_DATASETS' }] }\n }\n // Detect point-based charts (scatter/bubble) or object data (time-series line)\n const chartType = params.type || 'bar'\n const firstDataPoint = params.data.datasets[0]?.data?.[0]\n const hasObjectData = typeof firstDataPoint === 'object' && firstDataPoint !== null && 'x' in firstDataPoint\n const isPointChart = chartType === 'scatter' || chartType === 'bubble' || hasObjectData\n\n // Labels required only for categorical charts (not scatter/bubble/time-series)\n if (!isPointChart) {\n if (!Array.isArray(params.data.labels)) {\n return { valid: false, errors: [{ path: 'params.data.labels', message: 'Missing or invalid labels array', code: 'MISSING_LABELS' }] }\n }\n }\n\n // Validate data points count\n const totalDataPoints = params.data.datasets.reduce(\n (sum, dataset) => sum + (Array.isArray(dataset.data) ? dataset.data.length : 0),\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 // Length mismatch check — only for categorical charts, skip empty datasets\n if (!isPointChart && Array.isArray(params.data.labels)) {\n const expectedLength = params.data.labels.length\n for (const [index, dataset] of params.data.datasets.entries()) {\n if (Array.isArray(dataset.data) && dataset.data.length > 0 && 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\n // Data type validation — numbers for categorical, {x,y} objects for point charts\n for (const [index, dataset] of params.data.datasets.entries()) {\n if (!Array.isArray(dataset.data)) continue\n for (const [dataIndex, value] of dataset.data.entries()) {\n if (isPointChart) {\n const vObj = value as any\n if (typeof value !== 'object' || value === null || vObj.x == null || typeof vObj.y !== 'number') {\n errors.push({\n path: `params.data.datasets[${index}].data[${dataIndex}]`,\n message: `Invalid point data: expected {x, y} object`,\n code: 'INVALID_POINT_DATA',\n })\n }\n } else {\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\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 * Get the appropriate sandbox attribute for an iframe URL.\n *\n * Trusted domains (Google, Deposium, payment, auth-requiring services) get\n * `allow-same-origin` so they can access their own cookies/storage.\n * All other whitelisted domains get a restrictive sandbox without it,\n * preventing access to the parent page's localStorage/cookies.\n *\n * @param url - The iframe URL\n * @param options - Optional custom trusted domains\n * @returns sandbox attribute string\n */\nexport function getIframeSandbox(\n url: string,\n options?: { customTrustedDomains?: string[] }\n): string {\n const baseSandbox = 'allow-scripts allow-popups'\n\n try {\n const domain = new URL(url).hostname\n let trustedList = TRUSTED_IFRAME_DOMAINS\n if (options?.customTrustedDomains) {\n trustedList = [...TRUSTED_IFRAME_DOMAINS, ...options.customTrustedDomains]\n }\n\n const isTrusted = trustedList.some(\n (trusted) => domain === trusted || domain.endsWith(`.${trusted}`)\n )\n\n if (isTrusted) {\n return `${baseSandbox} allow-same-origin allow-forms`\n }\n } catch {\n // Invalid URL — use restrictive sandbox\n }\n\n return baseSandbox\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 // Guard: params must exist\n if (!component.params) {\n return { valid: false, errors: [{ path: 'params', message: 'Missing component params', code: 'MISSING_PARAMS' }] }\n }\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 (B.1 — v5.5.0).\n //\n // 12 types delegate shape validation to Zod schemas in `mcp-ui-spec` via\n // SPEC_VALIDATORS. The 5 remaining types stay imperative because they\n // need cross-field consistency, resource limits, or backward-compat logic\n // that pure Zod can't express without `.refine()` (see SPEC_VALIDATORS docstring).\n const specValidator = SPEC_VALIDATORS[component.type]\n if (specValidator) {\n const result = specValidator.schema.safeParse(component.params)\n if (!result.success) {\n errors.push(...mapZodIssuesToErrors(result.error.issues, specValidator.legacyCode))\n }\n // Iframe + video: chain the domain whitelist post-check ONLY when the\n // shape parse succeeded (i.e. url is present and a string). Skipping the\n // domain check when shape failed avoids cascading errors on the same field.\n if (result.success && (component.type === 'iframe' || component.type === 'video')) {\n const url = (component.params as { url?: string })?.url\n if (typeof url === 'string') {\n const domainResult = validateIframeDomain(url, {\n policy: options?.iframePolicy,\n customDomains: options?.customIframeDomains,\n })\n if (!domainResult.valid) {\n errors.push(...(domainResult.errors || []))\n }\n }\n }\n } else {\n // Imperative path for chart/table/form/map/modal/grid/footer/composite.\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 'form': {\n const formParams = component.params as { fields?: unknown[] }\n if (!Array.isArray(formParams.fields) || formParams.fields.length === 0) {\n errors.push({ path: 'params.fields', message: 'Form must have non-empty fields array', code: 'EMPTY_FORM' })\n }\n break\n }\n\n case 'map': {\n // Map can auto-detect center from markers, so center is not strictly required.\n // Spec MapComponentParamsSchema would be too strict (tuple-only center) — kept imperative.\n const mapParams = component.params as { center?: unknown; markers?: unknown[] }\n if (!mapParams.center && (!Array.isArray(mapParams.markers) || mapParams.markers.length === 0)) {\n errors.push({ path: 'params', message: 'Map must have center or markers', code: 'INVALID_MAP' })\n }\n break\n }\n\n case 'modal':\n // Modal is valid with minimal params (title optional, content can be children).\n break\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\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 // valueFormat validation (v4.3.0) — runs after type-specific checks\n if (field.valueFormat && value !== undefined && value !== null && value !== '') {\n const vals = Array.isArray(value) ? value : [String(value)]\n for (const v of vals) {\n if (!new RegExp(field.valueFormat).test(v)) {\n return { valid: false, error: field.valueFormatHint || `Invalid format (expected: ${field.valueFormat})` }\n }\n }\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":";AA0CA,MAAM,4CAAyC,IAAmB;AAAA,EAChE;AAAA,EAAS;AAAA,EAAS;AAAA,EAAU;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAU;AAAA,EAAS;AAAA,EAC/D;AAAA,EAAU;AAAA,EAAU;AAAA,EAAY;AAAA,EAAY;AAAA,EAAQ;AAAA,EACpD;AAAA,EAAgB;AAAA,EAAiB;AAAA,EAAS;AAAA,EAAQ;AACpD,CAAC;AAsBD,MAAM,kBAA6F;AAAA,EACjG,QAAQ,EAAE,QAAQ,6BAA6B,YAAY,iBAAA;AAAA,EAC3D,MAAM,EAAE,QAAQ,2BAA2B,YAAY,eAAA;AAAA,EACvD,QAAQ,EAAE,QAAQ,6BAA6B,YAAY,iBAAA;AAAA,EAC3D,OAAO,EAAE,QAAQ,4BAA4B,YAAY,gBAAA;AAAA,EACzD,MAAM,EAAE,QAAQ,2BAA2B,YAAY,eAAA;AAAA,EACvD,QAAQ,EAAE,QAAQ,oBAAoB,YAAY,iBAAA;AAAA,EAClD,OAAO,EAAE,QAAQ,4BAA4B,YAAY,gBAAA;AAAA,EACzD,UAAU,EAAE,QAAQ,+BAA+B,YAAY,iBAAA;AAAA,EAC/D,iBAAiB,EAAE,QAAQ,0BAA0B,YAAY,gBAAA;AAAA,EACjE,gBAAgB,EAAE,QAAQ,yBAAyB,YAAY,qBAAA;AAAA,EAC/D,MAAM,EAAE,QAAQ,2BAA2B,YAAY,eAAA;AAAA,EACvD,UAAU,EAAE,QAAQ,+BAA+B,YAAY,mBAAA;AACjE;AAUA,SAAS,qBACP,QACA,YACyC;AACzC,SAAO,OAAO,IAAI,CAAC,WAAW;AAAA,IAC5B,MAAM,MAAM,KAAK,SAAS,IAAI,UAAU,MAAM,KAAK,KAAK,GAAG,CAAC,KAAK;AAAA,IACjE,SAAS,MAAM;AAAA,IACf,MAAM;AAAA,EAAA,EACN;AACJ;AAKO,MAAM,0BAA0C;AAAA,EACrD,eAAe;AAAA,EACf,cAAc;AAAA,EACd,gBAAgB,KAAK;AAAA;AAAA,EACrB,eAAe;AAAA;AACjB;AASO,MAAM,yBAAyB;AAAA;AAAA,EAEpC;AAAA,EACA;AAAA;AAAA,EAGA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAGA;AAAA;AAAA,EAGA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAGA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAGA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAGA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAGA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAGA;AAAA,EACA;AAAA;AAAA,EAGA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAGA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAGA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAGA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAGA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAGA;AAAA;AAAA,EAGA;AAAA,EACA;AAAA;AAAA,EAGA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAGA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAGA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAOO,MAAM,yBAAyB;AAAA;AAAA,EAEpC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAGA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAGA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAGA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAGA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAC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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 { ZodIssue, ZodSchema } from 'zod'\nimport {\n MetricComponentParamsSchema,\n TextComponentParamsSchema,\n IframeComponentParamsSchema,\n ImageComponentParamsSchema,\n LinkComponentParamsSchema,\n CarouselComponentParamsSchema,\n ArtifactComponentParamsSchema,\n ActionParamsSchema,\n VideoComponentParamsSchema,\n ImageGalleryParamsSchema,\n ActionGroupParamsSchema,\n CodeComponentParamsSchema,\n // v5.6.0 — added after spec@5.0.2 relaxations (deposium audit §M)\n MapComponentParamsSchema,\n FormComponentParamsSchema,\n} from '@seed-ship/mcp-ui-spec'\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 * Spec-driven validation dispatch table (B.1 — v5.5.0, expanded in v5.6.0).\n *\n * For each ComponentType where we delegate shape validation to a Zod schema\n * from `@seed-ship/mcp-ui-spec`, this table maps:\n * - the schema to safeParse against\n * - the legacy error code to emit when shape parsing fails (preserves the\n * pre-v5.5.0 `errors[].code` API contract — see MCP-UI-AUDIT-2026-04-26.md\n * §I.3.a + §J.1)\n *\n * **v5.6.0** : `map` and `form` joined the dispatch after spec@5.0.2 relaxed\n * their schemas (LatLngPoint union for map.center, regex relax for\n * field.name) per deposium audit §L answers. Closed B.1 to **14/17 types**.\n *\n * Types deliberately omitted (kept on the imperative path):\n * - `chart`, `table` — have rich imperative validators with their own\n * codes (MISSING_DATA, DATA_LENGTH_MISMATCH, RESOURCE_LIMIT_EXCEEDED, …)\n * - `modal` — all params are optional; nothing to enforce.\n * - `grid`, `footer`, `composite` — pass-through, validated elsewhere.\n */\nconst SPEC_VALIDATORS: Partial<Record<ComponentType, { schema: ZodSchema; legacyCode: string }>> = {\n metric: { schema: MetricComponentParamsSchema, legacyCode: 'INVALID_METRIC' },\n text: { schema: TextComponentParamsSchema, legacyCode: 'INVALID_TEXT' },\n iframe: { schema: IframeComponentParamsSchema, legacyCode: 'INVALID_IFRAME' },\n image: { schema: ImageComponentParamsSchema, legacyCode: 'INVALID_IMAGE' },\n link: { schema: LinkComponentParamsSchema, legacyCode: 'INVALID_LINK' },\n action: { schema: ActionParamsSchema, legacyCode: 'INVALID_ACTION' },\n video: { schema: VideoComponentParamsSchema, legacyCode: 'INVALID_VIDEO' },\n carousel: { schema: CarouselComponentParamsSchema, legacyCode: 'EMPTY_CAROUSEL' },\n 'image-gallery': { schema: ImageGalleryParamsSchema, legacyCode: 'EMPTY_GALLERY' },\n 'action-group': { schema: ActionGroupParamsSchema, legacyCode: 'EMPTY_ACTION_GROUP' },\n code: { schema: CodeComponentParamsSchema, legacyCode: 'INVALID_CODE' },\n artifact: { schema: ArtifactComponentParamsSchema, legacyCode: 'INVALID_ARTIFACT' },\n // v5.6.0 additions\n form: { schema: FormComponentParamsSchema, legacyCode: 'EMPTY_FORM' },\n map: { schema: MapComponentParamsSchema, legacyCode: 'INVALID_MAP' },\n}\n\n/**\n * Map a Zod issue list to the legacy `ValidationError[]` shape.\n *\n * Preserves the pre-v5.5.0 contract: `path` always begins with `params`,\n * `code` is the per-type legacy code (so consumers that filtered by\n * `errors[].code === 'EMPTY_CAROUSEL'` keep working), `message` is Zod's\n * native human-readable message.\n */\nfunction mapZodIssuesToErrors(\n issues: readonly ZodIssue[],\n legacyCode: string\n): NonNullable<ValidationResult['errors']> {\n return issues.map((issue) => ({\n path: issue.path.length > 0 ? `params.${issue.path.join('.')}` : 'params',\n message: issue.message,\n code: legacyCode,\n }))\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 // Payment platforms (v2.2.12)\n 'polar.sh',\n 'www.polar.sh',\n 'checkout.stripe.com',\n 'js.stripe.com',\n 'billing.stripe.com',\n 'buy.stripe.com',\n 'connect.stripe.com',\n 'invoice.stripe.com',\n]\n\n/**\n * Trusted iframe domains that require allow-same-origin to function.\n * These domains need access to their own cookies/storage for auth.\n * All other whitelisted domains get a restrictive sandbox without allow-same-origin.\n */\nexport const TRUSTED_IFRAME_DOMAINS = [\n // Deposium (own domains)\n 'deposium.com',\n 'deposium.vip',\n 'deposium.ai',\n 'localhost',\n\n // Google services (need auth cookies)\n 'docs.google.com',\n 'drive.google.com',\n 'sheets.google.com',\n 'slides.google.com',\n 'maps.google.com',\n 'datastudio.google.com',\n 'lookerstudio.google.com',\n\n // Productivity (need auth)\n 'notion.so',\n 'www.notion.so',\n 'airtable.com',\n 'figma.com',\n 'www.figma.com',\n 'miro.com',\n\n // Payment (need auth + cookies for checkout)\n 'polar.sh',\n 'www.polar.sh',\n 'checkout.stripe.com',\n 'js.stripe.com',\n 'billing.stripe.com',\n 'buy.stripe.com',\n 'connect.stripe.com',\n 'invoice.stripe.com',\n\n // Business tools (need auth)\n 'app.hubspot.com',\n 'share.hubspot.com',\n 'app.powerbi.com',\n 'linear.app',\n 'www.linear.app',\n 'calendly.com',\n 'typeform.com',\n 'cal.com',\n 'canva.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 // Guard: params.data must exist with labels + datasets\n if (!params?.data) {\n return { valid: false, errors: [{ path: 'params.data', message: 'Missing chart data object', code: 'MISSING_DATA' }] }\n }\n if (!Array.isArray(params.data.datasets)) {\n return { valid: false, errors: [{ path: 'params.data.datasets', message: 'Missing or invalid datasets array', code: 'MISSING_DATASETS' }] }\n }\n // Detect point-based charts (scatter/bubble) or object data (time-series line)\n const chartType = params.type || 'bar'\n const firstDataPoint = params.data.datasets[0]?.data?.[0]\n const hasObjectData = typeof firstDataPoint === 'object' && firstDataPoint !== null && 'x' in firstDataPoint\n const isPointChart = chartType === 'scatter' || chartType === 'bubble' || hasObjectData\n\n // Labels required only for categorical charts (not scatter/bubble/time-series)\n if (!isPointChart) {\n if (!Array.isArray(params.data.labels)) {\n return { valid: false, errors: [{ path: 'params.data.labels', message: 'Missing or invalid labels array', code: 'MISSING_LABELS' }] }\n }\n }\n\n // Validate data points count\n const totalDataPoints = params.data.datasets.reduce(\n (sum, dataset) => sum + (Array.isArray(dataset.data) ? dataset.data.length : 0),\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 // Length mismatch check — only for categorical charts, skip empty datasets\n if (!isPointChart && Array.isArray(params.data.labels)) {\n const expectedLength = params.data.labels.length\n for (const [index, dataset] of params.data.datasets.entries()) {\n if (Array.isArray(dataset.data) && dataset.data.length > 0 && 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\n // Data type validation — numbers for categorical, {x,y} objects for point charts\n for (const [index, dataset] of params.data.datasets.entries()) {\n if (!Array.isArray(dataset.data)) continue\n for (const [dataIndex, value] of dataset.data.entries()) {\n if (isPointChart) {\n const vObj = value as any\n if (typeof value !== 'object' || value === null || vObj.x == null || typeof vObj.y !== 'number') {\n errors.push({\n path: `params.data.datasets[${index}].data[${dataIndex}]`,\n message: `Invalid point data: expected {x, y} object`,\n code: 'INVALID_POINT_DATA',\n })\n }\n } else {\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\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 // SECURITY (v5.5.1) — pre-fix bug: predicate was `allowed === 'localhost'`\n // which trivially returned true for every URL once the whitelist contained\n // 'localhost' (an entry from DEFAULT_IFRAME_DOMAINS), making the entire\n // domain whitelist inoperative. Fixed: only the URL's actual hostname\n // being 'localhost' (or a 127.0.0.x loopback) bypasses the whitelist.\n const isLoopback = domain === 'localhost' || /^127(\\.\\d{1,3}){3}$/.test(domain)\n const isAllowed =\n isLoopback ||\n effectiveWhitelist.some(\n (allowed) => allowed !== 'localhost' && (domain === allowed || domain.endsWith(`.${allowed}`))\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 * Get the appropriate sandbox attribute for an iframe URL.\n *\n * Trusted domains (Google, Deposium, payment, auth-requiring services) get\n * `allow-same-origin` so they can access their own cookies/storage.\n * All other whitelisted domains get a restrictive sandbox without it,\n * preventing access to the parent page's localStorage/cookies.\n *\n * @param url - The iframe URL\n * @param options - Optional custom trusted domains\n * @returns sandbox attribute string\n */\nexport function getIframeSandbox(\n url: string,\n options?: { customTrustedDomains?: string[] }\n): string {\n const baseSandbox = 'allow-scripts allow-popups'\n\n try {\n const domain = new URL(url).hostname\n let trustedList = TRUSTED_IFRAME_DOMAINS\n if (options?.customTrustedDomains) {\n trustedList = [...TRUSTED_IFRAME_DOMAINS, ...options.customTrustedDomains]\n }\n\n const isTrusted = trustedList.some(\n (trusted) => domain === trusted || domain.endsWith(`.${trusted}`)\n )\n\n if (isTrusted) {\n return `${baseSandbox} allow-same-origin allow-forms`\n }\n } catch {\n // Invalid URL — use restrictive sandbox\n }\n\n return baseSandbox\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 // Guard: params must exist\n if (!component.params) {\n return { valid: false, errors: [{ path: 'params', message: 'Missing component params', code: 'MISSING_PARAMS' }] }\n }\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 (B.1 — v5.5.0, expanded v5.6.0).\n //\n // 14 types delegate shape validation to Zod schemas in `mcp-ui-spec` via\n // SPEC_VALIDATORS. The 3 remaining types stay imperative because they\n // need cross-field consistency, resource limits, or have nothing to validate\n // (see SPEC_VALIDATORS docstring).\n const specValidator = SPEC_VALIDATORS[component.type]\n if (specValidator) {\n const result = specValidator.schema.safeParse(component.params)\n if (!result.success) {\n errors.push(...mapZodIssuesToErrors(result.error.issues, specValidator.legacyCode))\n }\n // Post-spec chained checks. Skipped when the shape parse failed to avoid\n // cascading errors on already-broken payloads.\n if (result.success) {\n // Iframe + video: domain whitelist\n if (component.type === 'iframe' || component.type === 'video') {\n const url = (component.params as { url?: string })?.url\n if (typeof url === 'string') {\n const domainResult = validateIframeDomain(url, {\n policy: options?.iframePolicy,\n customDomains: options?.customIframeDomains,\n })\n if (!domainResult.valid) {\n errors.push(...(domainResult.errors || []))\n }\n }\n }\n // Map (v5.6.0): center OR markers required. Spec has both .optional()\n // since auto-center from markers is supported, but we need ONE of them.\n if (component.type === 'map') {\n const mapParams = component.params as { center?: unknown; markers?: unknown[] }\n if (!mapParams.center && (!Array.isArray(mapParams.markers) || mapParams.markers.length === 0)) {\n errors.push({\n path: 'params',\n message: 'Map must have center or markers',\n code: 'INVALID_MAP',\n })\n }\n }\n }\n } else {\n // Imperative path for chart/table/modal/grid/footer/composite.\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 'modal':\n // Modal is valid with minimal params (title optional, content can be children).\n break\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\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 // valueFormat validation (v4.3.0) — runs after type-specific checks\n if (field.valueFormat && value !== undefined && value !== null && value !== '') {\n const vals = Array.isArray(value) ? value : [String(value)]\n for (const v of vals) {\n if (!new RegExp(field.valueFormat).test(v)) {\n return { valid: false, error: field.valueFormatHint || `Invalid format (expected: ${field.valueFormat})` }\n }\n }\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":";AA6CA,MAAM,4CAAyC,IAAmB;AAAA,EAChE;AAAA,EAAS;AAAA,EAAS;AAAA,EAAU;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAU;AAAA,EAAS;AAAA,EAC/D;AAAA,EAAU;AAAA,EAAU;AAAA,EAAY;AAAA,EAAY;AAAA,EAAQ;AAAA,EACpD;AAAA,EAAgB;AAAA,EAAiB;AAAA,EAAS;AAAA,EAAQ;AACpD,CAAC;AAsBD,MAAM,kBAA6F;AAAA,EACjG,QAAQ,EAAE,QAAQ,6BAA6B,YAAY,iBAAA;AAAA,EAC3D,MAAM,EAAE,QAAQ,2BAA2B,YAAY,eAAA;AAAA,EACvD,QAAQ,EAAE,QAAQ,6BAA6B,YAAY,iBAAA;AAAA,EAC3D,OAAO,EAAE,QAAQ,4BAA4B,YAAY,gBAAA;AAAA,EACzD,MAAM,EAAE,QAAQ,2BAA2B,YAAY,eAAA;AAAA,EACvD,QAAQ,EAAE,QAAQ,oBAAoB,YAAY,iBAAA;AAAA,EAClD,OAAO,EAAE,QAAQ,4BAA4B,YAAY,gBAAA;AAAA,EACzD,UAAU,EAAE,QAAQ,+BAA+B,YAAY,iBAAA;AAAA,EAC/D,iBAAiB,EAAE,QAAQ,0BAA0B,YAAY,gBAAA;AAAA,EACjE,gBAAgB,EAAE,QAAQ,yBAAyB,YAAY,qBAAA;AAAA,EAC/D,MAAM,EAAE,QAAQ,2BAA2B,YAAY,eAAA;AAAA,EACvD,UAAU,EAAE,QAAQ,+BAA+B,YAAY,mBAAA;AAAA;AAAA,EAE/D,MAAM,EAAE,QAAQ,2BAA2B,YAAY,aAAA;AAAA,EACvD,KAAK,EAAE,QAAQ,0BAA0B,YAAY,cAAA;AACvD;AAUA,SAAS,qBACP,QACA,YACyC;AACzC,SAAO,OAAO,IAAI,CAAC,WAAW;AAAA,IAC5B,MAAM,MAAM,KAAK,SAAS,IAAI,UAAU,MAAM,KAAK,KAAK,GAAG,CAAC,KAAK;AAAA,IACjE,SAAS,MAAM;AAAA,IACf,MAAM;AAAA,EAAA,EACN;AACJ;AAKO,MAAM,0BAA0C;AAAA,EACrD,eAAe;AAAA,EACf,cAAc;AAAA,EACd,gBAAgB,KAAK;AAAA;AAAA,EACrB,eAAe;AAAA;AACjB;AASO,MAAM,yBAAyB;AAAA;AAAA,EAEpC;AAAA,EACA;AAAA;AAAA,EAGA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAGA;AAAA;AAAA,EAGA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAGA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAGA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAGA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAGA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAGA;AAAA,EACA;AAAA;AAAA,EAGA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAGA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAGA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAGA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAGA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAGA;AAAA;AAAA,EAGA;AAAA,EACA;AAAA;AAAA,EAGA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAGA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAGA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAOO,MAAM,yBAAyB;AAAA;AAAA,EAEpC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAGA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAGA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAGA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAGA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAKO,SAAS,qBAAqB,UAAqD;AACxF,QAAM,SAAqC,CAAA;AAG3C,MAAI,CAAC,UAAU;AACb,WAAO;AAAA,MACL,OAAO;AAAA,MACP,QAAQ;AAAA,QACN;AAAA,UACE,MAAM;AAAA,UACN,SAAS;AAAA,UACT,MAAM;AAAA,QAAA;AAAA,MACR;AAAA,IACF;AAAA,EAEJ;AAEA,MAAI,SAAS,WAAW,KAAK,SAAS,WAAW,IAAI;AACnD,WAAO,KAAK;AAAA,MACV,MAAM;AAAA,MACN,SAAS;AAAA,MACT,MAAM;AAAA,IAAA,CACP;AAAA,EACH;AAEA,MAAI,SAAS,UAAU,KAAK,SAAS,UAAU,IAAI;AACjD,WAAO,KAAK;AAAA,MACV,MAAM;AAAA,MACN,SAAS;AAAA,MACT,MAAM;AAAA,IAAA,CACP;AAAA,EACH;AAEA,MAAI,SAAS,WAAW,SAAS,UAAU,IAAI,IAAI;AACjD,WAAO,KAAK;AAAA,MACV,MAAM;AAAA,MACN,SAAS;AAAA,MACT,MAAM;AAAA,IAAA,CACP;AAAA,EACH;AAEA,MAAI,SAAS,aAAa,UAAa,SAAS,WAAW,GAAG;AAC5D,WAAO,KAAK;AAAA,MACV,MAAM;AAAA,MACN,SAAS;AAAA,MACT,MAAM;AAAA,IAAA,CACP;AAAA,EACH;AAEA,MAAI,SAAS,YAAY,UAAa,SAAS,UAAU,GAAG;AAC1D,WAAO,KAAK;AAAA,MACV,MAAM;AAAA,MACN,SAAS;AAAA,MACT,MAAM;AAAA,IAAA,CACP;AAAA,EACH;AAEA,SAAO;AAAA,IACL,OAAO,OAAO,WAAW;AAAA,IACzB,QAAQ,OAAO,SAAS,IAAI,SAAS;AAAA,EAAA;AAEzC;AAKO,SAAS,uBACd,QACA,SAAyB,yBACP;;AAClB,QAAM,SAAqC,CAAA;AAG3C,MAAI,EAAC,iCAAQ,OAAM;AACjB,WAAO,EAAE,OAAO,OAAO,QAAQ,CAAC,EAAE,MAAM,eAAe,SAAS,6BAA6B,MAAM,eAAA,CAAgB,EAAA;AAAA,EACrH;AACA,MAAI,CAAC,MAAM,QAAQ,OAAO,KAAK,QAAQ,GAAG;AACxC,WAAO,EAAE,OAAO,OAAO,QAAQ,CAAC,EAAE,MAAM,wBAAwB,SAAS,qCAAqC,MAAM,mBAAA,CAAoB,EAAA;AAAA,EAC1I;AAEA,QAAM,YAAY,OAAO,QAAQ;AACjC,QAAM,kBAAiB,kBAAO,KAAK,SAAS,CAAC,MAAtB,mBAAyB,SAAzB,mBAAgC;AACvD,QAAM,gBAAgB,OAAO,mBAAmB,YAAY,mBAAmB,QAAQ,OAAO;AAC9F,QAAM,eAAe,cAAc,aAAa,cAAc,YAAY;AAG1E,MAAI,CAAC,cAAc;AACjB,QAAI,CAAC,MAAM,QAAQ,OAAO,KAAK,MAAM,GAAG;AACtC,aAAO,EAAE,OAAO,OAAO,QAAQ,CAAC,EAAE,MAAM,sBAAsB,SAAS,mCAAmC,MAAM,iBAAA,CAAkB,EAAA;AAAA,IACpI;AAAA,EACF;AAGA,QAAM,kBAAkB,OAAO,KAAK,SAAS;AAAA,IAC3C,CAAC,KAAK,YAAY,OAAO,MAAM,QAAQ,QAAQ,IAAI,IAAI,QAAQ,KAAK,SAAS;AAAA,IAC7E;AAAA,EAAA;AAGF,MAAI,kBAAkB,OAAO,eAAe;AAC1C,WAAO,KAAK;AAAA,MACV,MAAM;AAAA,MACN,SAAS,kCAAkC,eAAe,MAAM,OAAO,aAAa;AAAA,MACpF,MAAM;AAAA,IAAA,CACP;AAAA,EACH;AAGA,MAAI,CAAC,gBAAgB,MAAM,QAAQ,OAAO,KAAK,MAAM,GAAG;AACtD,UAAM,iBAAiB,OAAO,KAAK,OAAO;AAC1C,eAAW,CAAC,OAAO,OAAO,KAAK,OAAO,KAAK,SAAS,WAAW;AAC7D,UAAI,MAAM,QAAQ,QAAQ,IAAI,KAAK,QAAQ,KAAK,SAAS,KAAK,QAAQ,KAAK,WAAW,gBAAgB;AACpG,eAAO,KAAK;AAAA,UACV,MAAM,wBAAwB,KAAK;AAAA,UACnC,SAAS,qCAAqC,cAAc,SAAS,QAAQ,KAAK,MAAM;AAAA,UACxF,MAAM;AAAA,QAAA,CACP;AAAA,MACH;AAAA,IACF;AAAA,EACF;AAGA,aAAW,CAAC,OAAO,OAAO,KAAK,OAAO,KAAK,SAAS,WAAW;AAC7D,QAAI,CAAC,MAAM,QAAQ,QAAQ,IAAI,EAAG;AAClC,eAAW,CAAC,WAAW,KAAK,KAAK,QAAQ,KAAK,WAAW;AACvD,UAAI,cAAc;AAChB,cAAM,OAAO;AACb,YAAI,OAAO,UAAU,YAAY,UAAU,QAAQ,KAAK,KAAK,QAAQ,OAAO,KAAK,MAAM,UAAU;AAC/F,iBAAO,KAAK;AAAA,YACV,MAAM,wBAAwB,KAAK,UAAU,SAAS;AAAA,YACtD,SAAS;AAAA,YACT,MAAM;AAAA,UAAA,CACP;AAAA,QACH;AAAA,MACF,OAAO;AACL,YAAI,OAAO,UAAU,YAAY,CAAC,OAAO,SAAS,KAAK,GAAG;AACxD,iBAAO,KAAK;AAAA,YACV,MAAM,wBAAwB,KAAK,UAAU,SAAS;AAAA,YACtD,SAAS,uBAAuB,KAAK;AAAA,YACrC,MAAM;AAAA,UAAA,CACP;AAAA,QACH;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AAAA,IACL,OAAO,OAAO,WAAW;AAAA,IACzB,QAAQ,OAAO,SAAS,IAAI,SAAS;AAAA,EAAA;AAEzC;AAKO,SAAS,uBACd,QACA,SAAyB,yBACP;AAClB,QAAM,SAAqC,CAAA;AAG3C,MAAI,OAAO,KAAK,SAAS,OAAO,cAAc;AAC5C,WAAO,KAAK;AAAA,MACV,MAAM;AAAA,MACN,SAAS,2BAA2B,OAAO,KAAK,MAAM,MAAM,OAAO,YAAY;AAAA,MAC/E,MAAM;AAAA,IAAA,CACP;AAAA,EACH;AAGA,MAAI,OAAO,QAAQ,WAAW,GAAG;AAC/B,WAAO,KAAK;AAAA,MACV,MAAM;AAAA,MACN,SAAS;AAAA,MACT,MAAM;AAAA,IAAA,CACP;AAAA,EACH;AAGA,QAAM,iCAAiB,IAAA;AACvB,aAAW,CAAC,OAAO,MAAM,KAAK,OAAO,QAAQ,WAAW;AACtD,QAAI,WAAW,IAAI,OAAO,GAAG,GAAG;AAC9B,aAAO,KAAK;AAAA,QACV,MAAM,kBAAkB,KAAK;AAAA,QAC7B,SAAS,yBAAyB,OAAO,GAAG;AAAA,QAC5C,MAAM;AAAA,MAAA,CACP;AAAA,IACH;AACA,eAAW,IAAI,OAAO,GAAG;AAAA,EAC3B;AAGA,aAAW,CAAC,UAAU,GAAG,KAAK,OAAO,KAAK,WAAW;AACnD,eAAW,UAAU,OAAO,SAAS;AACnC,UAAI,EAAE,OAAO,OAAO,MAAM;AACxB,eAAO,KAAK;AAAA,UACV,MAAM,eAAe,QAAQ;AAAA,UAC7B,SAAS,uBAAuB,OAAO,GAAG;AAAA,UAC1C,MAAM;AAAA,QAAA,CACP;AAAA,MACH;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AAAA,IACL,OAAO,OAAO,WAAW;AAAA,IACzB,QAAQ,OAAO,SAAS,IAAI,SAAS;AAAA,EAAA;AAEzC;AAKO,SAAS,oBACd,WACA,SAAyB,yBACP;AAClB,QAAM,cAAc,KAAK,UAAU,SAAS,EAAE;AAE9C,MAAI,cAAc,OAAO,gBAAgB;AACvC,WAAO;AAAA,MACL,OAAO;AAAA,MACP,QAAQ;AAAA,QACN;AAAA,UACE,MAAM;AAAA,UACN,SAAS,+BAA+B,WAAW,MAAM,OAAO,cAAc;AAAA,UAC9E,MAAM;AAAA,QAAA;AAAA,MACR;AAAA,IACF;AAAA,EAEJ;AAEA,SAAO,EAAE,OAAO,KAAA;AAClB;AAMO,SAAS,eAAe,OAAuB;AACpD,SAAO,MACJ,QAAQ,uDAAuD,EAAE,EACjE,QAAQ,mBAAmB,EAAE,EAC7B,QAAQ,iBAAiB,EAAE;AAChC;AAUO,SAAS,qBACd,KACA,SACkB;AAElB,OAAI,mCAAS,YAAW,aAAa;AACnC,WAAO,EAAE,OAAO,KAAA;AAAA,EAClB;AAEA,MAAI;AACF,UAAM,YAAY,IAAI,IAAI,GAAG;AAC7B,UAAM,SAAS,UAAU;AAGzB,QAAI,qBAAqB;AACzB,SAAI,mCAAS,YAAW,YAAY,QAAQ,eAAe;AACzD,2BAAqB,CAAC,GAAG,wBAAwB,GAAG,QAAQ,aAAa;AAAA,IAC3E;AAOA,UAAM,aAAa,WAAW,eAAe,sBAAsB,KAAK,MAAM;AAC9E,UAAM,YACJ,cACA,mBAAmB;AAAA,MACjB,CAAC,YAAY,YAAY,gBAAgB,WAAW,WAAW,OAAO,SAAS,IAAI,OAAO,EAAE;AAAA,IAAA;AAGhG,QAAI,CAAC,WAAW;AACd,aAAO;AAAA,QACL,OAAO;AAAA,QACP,QAAQ;AAAA,UACN;AAAA,YACE,MAAM;AAAA,YACN,SAAS,2BAA2B,MAAM;AAAA,YAC1C,MAAM;AAAA,UAAA;AAAA,QACR;AAAA,MACF;AAAA,IAEJ;AAEA,WAAO,EAAE,OAAO,KAAA;AAAA,EAClB,SAAS,OAAO;AACd,WAAO;AAAA,MACL,OAAO;AAAA,MACP,QAAQ;AAAA,QACN;AAAA,UACE,MAAM;AAAA,UACN,SAAS;AAAA,UACT,MAAM;AAAA,QAAA;AAAA,MACR;AAAA,IACF;AAAA,EAEJ;AACF;AAcO,SAAS,iBACd,KACA,SACQ;AACR,QAAM,cAAc;AAEpB,MAAI;AACF,UAAM,SAAS,IAAI,IAAI,GAAG,EAAE;AAC5B,QAAI,cAAc;AAClB,QAAI,mCAAS,sBAAsB;AACjC,oBAAc,CAAC,GAAG,wBAAwB,GAAG,QAAQ,oBAAoB;AAAA,IAC3E;AAEA,UAAM,YAAY,YAAY;AAAA,MAC5B,CAAC,YAAY,WAAW,WAAW,OAAO,SAAS,IAAI,OAAO,EAAE;AAAA,IAAA;AAGlE,QAAI,WAAW;AACb,aAAO,GAAG,WAAW;AAAA,IACvB;AAAA,EACF,QAAQ;AAAA,EAER;AAEA,SAAO;AACT;AAQO,SAAS,kBACd,WACA,SACkB;;AAClB,QAAM,UAAS,mCAAS,WAAU;AAClC,QAAM,SAAqC,CAAA;AAG3C,MAAI,CAAC,UAAU,QAAQ;AACrB,WAAO,EAAE,OAAO,OAAO,QAAQ,CAAC,EAAE,MAAM,UAAU,SAAS,4BAA4B,MAAM,iBAAA,CAAkB,EAAA;AAAA,EACjH;AAGA,QAAM,aAAa,qBAAqB,UAAU,QAAQ;AAC1D,MAAI,CAAC,WAAW,OAAO;AACrB,WAAO,KAAK,GAAI,WAAW,UAAU,CAAA,CAAG;AAAA,EAC1C;AAGA,QAAM,aAAa,oBAAoB,WAAW,MAAM;AACxD,MAAI,CAAC,WAAW,OAAO;AACrB,WAAO,KAAK,GAAI,WAAW,UAAU,CAAA,CAAG;AAAA,EAC1C;AAQA,QAAM,gBAAgB,gBAAgB,UAAU,IAAI;AACpD,MAAI,eAAe;AACjB,UAAM,SAAS,cAAc,OAAO,UAAU,UAAU,MAAM;AAC9D,QAAI,CAAC,OAAO,SAAS;AACnB,aAAO,KAAK,GAAG,qBAAqB,OAAO,MAAM,QAAQ,cAAc,UAAU,CAAC;AAAA,IACpF;AAGA,QAAI,OAAO,SAAS;AAElB,UAAI,UAAU,SAAS,YAAY,UAAU,SAAS,SAAS;AAC7D,cAAM,OAAO,eAAU,WAAV,mBAAuC;AACpD,YAAI,OAAO,QAAQ,UAAU;AAC3B,gBAAM,eAAe,qBAAqB,KAAK;AAAA,YAC7C,QAAQ,mCAAS;AAAA,YACjB,eAAe,mCAAS;AAAA,UAAA,CACzB;AACD,cAAI,CAAC,aAAa,OAAO;AACvB,mBAAO,KAAK,GAAI,aAAa,UAAU,CAAA,CAAG;AAAA,UAC5C;AAAA,QACF;AAAA,MACF;AAGA,UAAI,UAAU,SAAS,OAAO;AAC5B,cAAM,YAAY,UAAU;AAC5B,YAAI,CAAC,UAAU,WAAW,CAAC,MAAM,QAAQ,UAAU,OAAO,KAAK,UAAU,QAAQ,WAAW,IAAI;AAC9F,iBAAO,KAAK;AAAA,YACV,MAAM;AAAA,YACN,SAAS;AAAA,YACT,MAAM;AAAA,UAAA,CACP;AAAA,QACH;AAAA,MACF;AAAA,IACF;AAAA,EACF,OAAO;AAEL,YAAQ,UAAU,MAAA;AAAA,MAChB,KAAK,SAAS;AACZ,cAAM,cAAc,uBAAuB,UAAU,QAAgC,MAAM;AAC3F,YAAI,CAAC,YAAY,OAAO;AACtB,iBAAO,KAAK,GAAI,YAAY,UAAU,CAAA,CAAG;AAAA,QAC3C;AACA;AAAA,MACF;AAAA,MAEA,KAAK,SAAS;AACZ,cAAM,cAAc,uBAAuB,UAAU,QAAgC,MAAM;AAC3F,YAAI,CAAC,YAAY,OAAO;AACtB,iBAAO,KAAK,GAAI,YAAY,UAAU,CAAA,CAAG;AAAA,QAC3C;AACA;AAAA,MACF;AAAA,MAEA,KAAK;AAEH;AAAA,MAEF;AAGE,YAAI,CAAC,sBAAsB,IAAI,UAAU,IAAI,GAAG;AAC9C,iBAAO,KAAK;AAAA,YACV,MAAM;AAAA,YACN,SAAS,2BAA2B,UAAU,IAAI;AAAA,YAClD,MAAM;AAAA,UAAA,CACP;AAAA,QACH;AACA;AAAA,IAAA;AAAA,EAEN;AAEA,SAAO;AAAA,IACL,OAAO,OAAO,WAAW;AAAA,IACzB,QAAQ,OAAO,SAAS,IAAI,SAAS;AAAA,EAAA;AAEzC;AAQO,SAAS,eACd,QACA,SACkB;;AAClB,QAAM,SAAqC,CAAA;AAG3C,MAAI,OAAO,WAAW,WAAW,GAAG;AAClC,WAAO,KAAK;AAAA,MACV,MAAM;AAAA,MACN,SAAS;AAAA,MACT,MAAM;AAAA,IAAA,CACP;AAAA,EACH;AAEA,MAAI,OAAO,WAAW,SAAS,IAAI;AACjC,WAAO,KAAK;AAAA,MACV,MAAM;AAAA,MACN,SAAS,kCAAkC,OAAO,WAAW,MAAM;AAAA,MACnE,MAAM;AAAA,IAAA,CACP;AAAA,EACH;AAGA,aAAW,CAAC,OAAO,SAAS,KAAK,OAAO,WAAW,WAAW;AAC5D,UAAM,SAAS,kBAAkB,WAAW,OAAO;AACnD,QAAI,CAAC,OAAO,OAAO;AACjB,aAAO;AAAA,QACL,KAAI,YAAO,WAAP,mBAAe,IAAI,CAAC,WAAW;AAAA,UACjC,GAAG;AAAA,UACH,MAAM,cAAc,KAAK,KAAK,MAAM,IAAI;AAAA,QAAA,QACnC,CAAA;AAAA,MAAC;AAAA,IAEZ;AAAA,EACF;AAGA,MAAI,OAAO,KAAK,YAAY,IAAI;AAC9B,WAAO,KAAK;AAAA,MACV,MAAM;AAAA,MACN,SAAS;AAAA,MACT,MAAM;AAAA,IAAA,CACP;AAAA,EACH;AAEA,SAAO;AAAA,IACL,OAAO,OAAO,WAAW;AAAA,IACzB,QAAQ,OAAO,SAAS,IAAI,SAAS;AAAA,EAAA;AAEzC;AAKO,SAAS,mBACd,OACA,OACoC;AAEpC,MAAI,MAAM,UAAU;AAClB,QAAI,UAAU,UAAa,UAAU,QAAQ,UAAU,IAAI;AACzD,aAAO,EAAE,OAAO,OAAO,OAAO,GAAG,MAAM,SAAS,MAAM,IAAI,eAAA;AAAA,IAC5D;AACA,QAAI,MAAM,SAAS,cAAc,UAAU,MAAM;AAC/C,aAAO,EAAE,OAAO,OAAO,OAAO,GAAG,MAAM,SAAS,MAAM,IAAI,mBAAA;AAAA,IAC5D;AAAA,EACF;AAGA,MAAI,UAAU,UAAa,UAAU,QAAQ,UAAU,IAAI;AACzD,WAAO,EAAE,OAAO,KAAA;AAAA,EAClB;AAGA,UAAQ,MAAM,MAAA;AAAA,IACZ,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AACH,UAAI,MAAM,aAAa,OAAO,KAAK,EAAE,SAAS,MAAM,WAAW;AAC7D,eAAO,EAAE,OAAO,OAAO,OAAO,WAAW,MAAM,SAAS,uBAAA;AAAA,MAC1D;AACA,UAAI,MAAM,aAAa,OAAO,KAAK,EAAE,SAAS,MAAM,WAAW;AAC7D,eAAO,EAAE,OAAO,OAAO,OAAO,WAAW,MAAM,SAAS,sBAAA;AAAA,MAC1D;AACA,UAAI,MAAM,WAAW,CAAC,IAAI,OAAO,MAAM,OAAO,EAAE,KAAK,OAAO,KAAK,CAAC,GAAG;AACnE,eAAO,EAAE,OAAO,OAAO,OAAO,iBAAA;AAAA,MAChC;AACA;AAAA,IAEF,KAAK;AACH,UAAI,CAAC,6BAA6B,KAAK,OAAO,KAAK,CAAC,GAAG;AACrD,eAAO,EAAE,OAAO,OAAO,OAAO,wBAAA;AAAA,MAChC;AACA;AAAA,IAEF,KAAK,UAAU;AACb,YAAM,WAAW,OAAO,KAAK;AAC7B,UAAI,MAAM,QAAQ,GAAG;AACnB,eAAO,EAAE,OAAO,OAAO,OAAO,yBAAA;AAAA,MAChC;AACA,UAAI,MAAM,QAAQ,UAAa,WAAW,MAAM,KAAK;AACnD,eAAO,EAAE,OAAO,OAAO,OAAO,oBAAoB,MAAM,GAAG,GAAA;AAAA,MAC7D;AACA,UAAI,MAAM,QAAQ,UAAa,WAAW,MAAM,KAAK;AACnD,eAAO,EAAE,OAAO,OAAO,OAAO,oBAAoB,MAAM,GAAG,GAAA;AAAA,MAC7D;AACA;AAAA,IACF;AAAA,IAEA,KAAK;AACH,UAAI,MAAM,WAAW,QAAQ,MAAM,SAAS;AAC1C,eAAO,EAAE,OAAO,OAAO,OAAO,sBAAsB,MAAM,OAAO,GAAA;AAAA,MACnE;AACA,UAAI,MAAM,WAAW,QAAQ,MAAM,SAAS;AAC1C,eAAO,EAAE,OAAO,OAAO,OAAO,uBAAuB,MAAM,OAAO,GAAA;AAAA,MACpE;AACA;AAAA,IAEF,KAAK;AAAA,IACL,KAAK;AAEH,UAAI,MAAM,WAAW,MAAM,QAAQ,SAAS,GAAG;AAC7C,cAAM,cAAc,MAAM,QAAQ,IAAI,CAAC,QAAQ,IAAI,KAAK;AACxD,YAAI,CAAC,YAAY,SAAS,OAAO,KAAK,CAAC,GAAG;AACxC,iBAAO,EAAE,OAAO,OAAO,OAAO,+BAAA;AAAA,QAChC;AAAA,MACF;AACA;AAAA,EAAA;AAIJ,MAAI,MAAM,eAAe,UAAU,UAAa,UAAU,QAAQ,UAAU,IAAI;AAC9E,UAAM,OAAO,MAAM,QAAQ,KAAK,IAAI,QAAQ,CAAC,OAAO,KAAK,CAAC;AAC1D,eAAW,KAAK,MAAM;AACpB,UAAI,CAAC,IAAI,OAAO,MAAM,WAAW,EAAE,KAAK,CAAC,GAAG;AAC1C,eAAO,EAAE,OAAO,OAAO,OAAO,MAAM,mBAAmB,6BAA6B,MAAM,WAAW,IAAA;AAAA,MACvG;AAAA,IACF;AAAA,EACF;AAEA,SAAO,EAAE,OAAO,KAAA;AAClB;AAKO,SAAS,iBACd,MACA,QACoD;AACpD,QAAM,SAAiC,CAAA;AAEvC,aAAW,SAAS,QAAQ;AAC1B,UAAM,SAAS,mBAAmB,KAAK,MAAM,IAAI,GAAG,KAAK;AACzD,QAAI,CAAC,OAAO,SAAS,OAAO,OAAO;AACjC,aAAO,MAAM,IAAI,IAAI,OAAO;AAAA,IAC9B;AAAA,EACF;AAEA,SAAO;AAAA,IACL,OAAO,OAAO,KAAK,MAAM,EAAE,WAAW;AAAA,IACtC;AAAA,EAAA;AAEJ;"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@seed-ship/mcp-ui-solid",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.6.0",
|
|
4
4
|
"description": "SolidJS components for rendering MCP-generated UI resources",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.cjs",
|
|
@@ -143,7 +143,7 @@
|
|
|
143
143
|
"dompurify": "^3.4.1",
|
|
144
144
|
"marked": "^16.3.0",
|
|
145
145
|
"zod": "^3.22.4",
|
|
146
|
-
"@seed-ship/mcp-ui-spec": "^5.0.
|
|
146
|
+
"@seed-ship/mcp-ui-spec": "^5.0.2"
|
|
147
147
|
},
|
|
148
148
|
"devDependencies": {
|
|
149
149
|
"@size-limit/esbuild": "^12.0.0",
|
|
@@ -12,6 +12,7 @@ import { useAction } from '../hooks/useAction'
|
|
|
12
12
|
import { validateFormData } from '../services/validation'
|
|
13
13
|
import { evaluateCondition } from '../hooks/useConditionalField'
|
|
14
14
|
import { useFormPersistence } from '../hooks/useFormPersistence'
|
|
15
|
+
import { useTelemetry } from '../context/MCPUITelemetryContext'
|
|
15
16
|
|
|
16
17
|
export interface FormRendererProps {
|
|
17
18
|
component: UIComponent
|
|
@@ -42,6 +43,7 @@ export const FormRenderer: Component<FormRendererProps> = (props) => {
|
|
|
42
43
|
const [errors, setErrors] = createSignal<Record<string, string>>({})
|
|
43
44
|
const [isSubmitting, setIsSubmitting] = createSignal(false)
|
|
44
45
|
const { execute } = useAction()
|
|
46
|
+
const telemetry = useTelemetry()
|
|
45
47
|
|
|
46
48
|
// Form persistence (Sprint 4)
|
|
47
49
|
let clearPersisted: (() => void) | undefined
|
|
@@ -210,6 +212,18 @@ export const FormRenderer: Component<FormRendererProps> = (props) => {
|
|
|
210
212
|
clearPersisted()
|
|
211
213
|
}
|
|
212
214
|
|
|
215
|
+
// Telemetry: action:dispatched on successful submit (B.5 — v5.6.0).
|
|
216
|
+
// Privacy: only the action name (toolName or 'submit'), NO form values.
|
|
217
|
+
if (telemetry) {
|
|
218
|
+
telemetry.dispatch({
|
|
219
|
+
type: 'action:dispatched',
|
|
220
|
+
id: props.component.id,
|
|
221
|
+
componentType: 'form',
|
|
222
|
+
actionName: params().submitAction?.toolName ?? 'submit',
|
|
223
|
+
ts: Date.now(),
|
|
224
|
+
})
|
|
225
|
+
}
|
|
226
|
+
|
|
213
227
|
props.onSubmit?.(visibleFormData)
|
|
214
228
|
setIsSubmitting(false)
|
|
215
229
|
}
|