import { DEFAULT_PRESET, MODES, OVERRIDE_SCHEMA_VERSION, PLATFORMS, PRESETS, SUPPORTED_OVERRIDE_SCHEMA_VERSIONS, THEME_OVERRIDE_EXTENSION_ID, } from '../constants'; import type { Preset } from '../types'; import { canonicalizeRgbHex, canonicalizeRgbOrRgbaHex } from '../utils/color'; import { hasExactKeys, hasOwn, isRecord } from '../utils/common'; import type { InvariantSelection, LinkedSelection, OverrideDiagnostic, OverrideRuntimeData, ParsedThemeOverride, ParseThemeOverride, ParseThemeOverrideResult, SelectedValue, SelectionDefinition, ThemeInputs, ThemeSelections, TokenOverride, TokenOverrides, } from './contracts'; import { overrideFontCatalog, overrideRuntimeData } from './artifacts'; import { createOverrideDiagnostic, hasErrorDiagnostics } from './diagnostics'; const SUPPORTED_PRESETS = new Set(PRESETS); const SUPPORTED_SCHEMA_VERSIONS = new Set(SUPPORTED_OVERRIDE_SCHEMA_VERSIONS); const DANGEROUS_KEYS = new Set(['__proto__', 'prototype', 'constructor']); function inspectObjectSafety( value: unknown, diagnostics: OverrideDiagnostic[], path = '$', ancestors = new WeakSet(), ): void { if (value === null || typeof value !== 'object') return; if (ancestors.has(value)) { diagnostics.push( createOverrideDiagnostic('override.invalid_structure', { intentPath: path, message: 'Theme override data must be acyclic JSON-compatible data.', }), ); return; } ancestors.add(value); if (!Array.isArray(value)) { const prototype = Object.getPrototypeOf(value) as object | null; if (prototype !== Object.prototype && prototype !== null) { diagnostics.push( createOverrideDiagnostic('override.prototype_key', { intentPath: path, message: `Unsafe object prototype at ${path}.`, }), ); } } for (const key of Reflect.ownKeys(value)) { if (typeof key !== 'string') { diagnostics.push( createOverrideDiagnostic('override.invalid_structure', { intentPath: path, message: `Theme override data contains a non-string key at ${path}.`, }), ); continue; } const childPath = Array.isArray(value) ? `${path}[${key}]` : `${path}.${key}`; if (DANGEROUS_KEYS.has(key)) { diagnostics.push( createOverrideDiagnostic('override.prototype_key', { intentPath: childPath, message: `Unsafe object key "${key}" is not allowed.`, }), ); continue; } inspectObjectSafety((value as Record)[key], diagnostics, childPath, ancestors); } ancestors.delete(value); } function parseReference(value: unknown): { readonly ref: string } | undefined { if (!isRecord(value) || !hasExactKeys(value, ['ref']) || typeof value.ref !== 'string') { return undefined; } const ref = value.ref.trim(); if ( ref.length === 0 || ref.startsWith('modes.') || ref.startsWith('platform.') || /[{}]/.test(ref) || /^var\(/i.test(ref) ) { return undefined; } return { ref }; } function canonicalizeDimension(value: unknown): string | undefined { if (typeof value !== 'string' || !/^[+-]?(?:\d+(?:\.\d+)?|\.\d+)px$/.test(value)) { return undefined; } const number = Number(value.slice(0, -2)); if (!Number.isFinite(number)) return undefined; return `${String(Object.is(number, -0) ? 0 : number)}px`; } function parseLiteral( value: unknown, targetPath: string, data: OverrideRuntimeData, basePreset: Preset, ): SelectedValue | undefined { if (!isRecord(value) || !hasExactKeys(value, ['value'])) return undefined; const metadata = data.targets.targets[targetPath]; if (!metadata.literalEligible) return undefined; let canonical: string | number | undefined; if (metadata.type === 'color') { canonical = canonicalizeRgbOrRgbaHex(value.value); } else if (metadata.type === 'dimension') { canonical = canonicalizeDimension(value.value); } else if (metadata.type === 'number') { canonical = typeof value.value === 'number' && Number.isFinite(value.value) ? value.value : undefined; } else if (metadata.type === 'string' || metadata.type === 'fontFamily') { canonical = typeof value.value === 'string' && value.value.trim().length > 0 ? value.value.trim() : undefined; if (canonical !== undefined && targetPath.startsWith('font.display')) { const allowed = overrideFontCatalog.presetFamilies[basePreset].some((family) => PLATFORMS.every( (platform) => overrideFontCatalog.families[family].byPlatform[platform][targetPath] === canonical, ), ); if (!allowed) canonical = undefined; } } return canonical === undefined ? undefined : { value: canonical }; } function parseDirectSelectedValue( value: unknown, targetPath: string, data: OverrideRuntimeData, basePreset: Preset, diagnostics: OverrideDiagnostic[], intentPath: string, ): SelectedValue | undefined { const metadata = data.targets.targets[targetPath]; if (isRecord(value) && hasExactKeys(value, ['ref'])) { const reference = parseReference(value); if (!reference) { diagnostics.push( createOverrideDiagnostic('override.invalid_reference', { intentPath, tokenPath: targetPath, message: `Reference for ${targetPath} must be a canonical primitive token path.`, }), ); return undefined; } const hasReferenceType = hasOwn(data.primitives.types, reference.ref); const available = hasOwn(data.primitives.byPreset[basePreset], reference.ref); if ( !metadata.referenceEligible || !available || !hasReferenceType || data.primitives.types[reference.ref] !== metadata.type ) { diagnostics.push( createOverrideDiagnostic('override.invalid_reference', { intentPath, tokenPath: targetPath, message: `Reference "${reference.ref}" is not an eligible ${metadata.type} primitive for ${targetPath}.`, }), ); return undefined; } return reference; } const literal = parseLiteral(value, targetPath, data, basePreset); if (literal) return literal; diagnostics.push( createOverrideDiagnostic('override.literal_type_mismatch', { intentPath, tokenPath: targetPath, message: `Direct value for ${targetPath} does not match its ${metadata.type} contract.`, }), ); return undefined; } function parseLinkedSelection(value: unknown): LinkedSelection | undefined { if (!isRecord(value) || typeof value.mode !== 'string') return undefined; if (value.mode === 'linked' && hasExactKeys(value, ['mode', 'source'])) { const source = parseReference(value.source); return source ? { mode: 'linked', source } : undefined; } if (value.mode === 'unlinked' && hasExactKeys(value, ['mode', 'light', 'dark'])) { const light = parseReference(value.light); const dark = parseReference(value.dark); return light && dark ? { mode: 'unlinked', light, dark } : undefined; } return undefined; } function parseInvariantSelection(value: unknown): InvariantSelection | undefined { if (!isRecord(value) || !hasExactKeys(value, ['source'])) return undefined; const source = parseReference(value.source); return source ? { source } : undefined; } function parseKnownSelection( definition: SelectionDefinition, value: unknown, ): ThemeSelections[keyof ThemeSelections] | undefined { switch (definition.valueShape) { case 'linked-reference': return parseLinkedSelection(value); case 'invariant-reference': return parseInvariantSelection(value); case 'chart-palette': return undefined; } } function parseTokenOverride( value: unknown, targetPath: string, data: OverrideRuntimeData, basePreset: Preset, diagnostics: OverrideDiagnostic[], ): TokenOverride | undefined { const intentPath = `overrides.${targetPath}`; const modeMap = isRecord(value) && Object.keys(value).length > 0 && Object.keys(value).every((key) => key === 'light' || key === 'dark'); if (!modeMap) { return parseDirectSelectedValue(value, targetPath, data, basePreset, diagnostics, intentPath); } const metadata = data.targets.targets[targetPath]; if (metadata.modePolicy === 'invariant') { diagnostics.push( createOverrideDiagnostic('override.mode_policy_violation', { intentPath, tokenPath: targetPath, message: `${targetPath} is invariant and must use one mode-agnostic selected value.`, }), ); return undefined; } const result: { light?: SelectedValue; dark?: SelectedValue } = {}; const diagnosticStart = diagnostics.length; for (const mode of MODES) { if (!hasOwn(value, mode)) continue; const selected = parseDirectSelectedValue( value[mode], targetPath, data, basePreset, diagnostics, `${intentPath}.${mode}`, ); if (selected) result[mode] = selected; } if (hasErrorDiagnostics(diagnostics.slice(diagnosticStart))) return undefined; return result as TokenOverride; } export function parseThemeOverrideWithData( raw: unknown, data: OverrideRuntimeData, supportedSchemaVersions: ReadonlySet = SUPPORTED_SCHEMA_VERSIONS, ): ParseThemeOverrideResult { const diagnostics: OverrideDiagnostic[] = []; inspectObjectSafety(raw, diagnostics); if (hasErrorDiagnostics(diagnostics)) return { valid: false, diagnostics }; if (!isRecord(raw)) { return { valid: false, diagnostics: [ createOverrideDiagnostic('override.invalid_structure', { intentPath: '$', message: 'Theme override payload must be an object.', }), ], }; } const extensions = isRecord(raw.$extensions) ? raw.$extensions : undefined; const metadata = extensions && isRecord(extensions[THEME_OVERRIDE_EXTENSION_ID]) ? extensions[THEME_OVERRIDE_EXTENSION_ID] : undefined; let basePreset: Preset = DEFAULT_PRESET; if (!extensions || !metadata || !hasExactKeys(metadata, ['schemaVersion', 'basePreset'])) { diagnostics.push( createOverrideDiagnostic('override.invalid_metadata', { intentPath: `$extensions.${THEME_OVERRIDE_EXTENSION_ID}`, }), ); } else { if ( typeof metadata.schemaVersion !== 'string' || !supportedSchemaVersions.has(metadata.schemaVersion) ) { diagnostics.push( typeof metadata.schemaVersion === 'string' ? createOverrideDiagnostic('override.unsupported_schema_version', { intentPath: `$extensions.${THEME_OVERRIDE_EXTENSION_ID}.schemaVersion`, message: `Unsupported theme override schema version "${metadata.schemaVersion}".`, }) : createOverrideDiagnostic('override.invalid_metadata', { intentPath: `$extensions.${THEME_OVERRIDE_EXTENSION_ID}.schemaVersion`, message: `Theme override schemaVersion must be "${OVERRIDE_SCHEMA_VERSION}".`, }), ); } if ( typeof metadata.basePreset !== 'string' || !SUPPORTED_PRESETS.has(metadata.basePreset as Preset) ) { diagnostics.push( createOverrideDiagnostic('override.invalid_metadata', { intentPath: `$extensions.${THEME_OVERRIDE_EXTENSION_ID}.basePreset`, message: 'Theme override basePreset must be poise, prestige, or pulse.', }), ); } else { basePreset = metadata.basePreset as Preset; } } const preservedTopLevel: Record = {}; for (const [key, value] of Object.entries(raw)) { if (!['$extensions', 'inputs', 'selections', 'overrides'].includes(key)) { preservedTopLevel[key] = value; } } const preservedExtensions: Record = {}; if (extensions) { for (const [key, value] of Object.entries(extensions)) { if (key !== THEME_OVERRIDE_EXTENSION_ID) preservedExtensions[key] = value; } } const knownInputs: Record = {}; const unknownInputs: Record = {}; if (raw.inputs !== undefined && !isRecord(raw.inputs)) { diagnostics.push( createOverrideDiagnostic('override.invalid_structure', { intentPath: 'inputs', message: 'inputs must be an object when present.', }), ); } else if (isRecord(raw.inputs)) { for (const [id, value] of Object.entries(raw.inputs)) { const definition = data.catalogs.getInput(id); if (!definition) { unknownInputs[id] = value; diagnostics.push( createOverrideDiagnostic('override.unknown_input', { intentPath: `inputs.${id}`, message: `Unknown input "${id}" was preserved but will not be expanded.`, }), ); continue; } const parsed = definition.valueType === 'font-family' ? typeof value === 'string' && value.trim().length > 0 ? value.trim() : undefined : canonicalizeRgbHex(value); if (parsed === undefined) { diagnostics.push( createOverrideDiagnostic('override.invalid_value', { intentPath: `inputs.${id}`, message: definition.valueType === 'font-family' ? `${id} must be a non-empty family name.` : `${id} must be a #RGB or #RRGGBB color seed.`, }), ); } else { knownInputs[id] = parsed; } } } const knownSelections: Record = {}; const unknownSelections: Record = {}; if (raw.selections !== undefined && !isRecord(raw.selections)) { diagnostics.push( createOverrideDiagnostic('override.invalid_structure', { intentPath: 'selections', message: 'selections must be an object when present.', }), ); } else if (isRecord(raw.selections)) { for (const [id, value] of Object.entries(raw.selections)) { const definition = data.catalogs.getSelection(id); if (!definition) { unknownSelections[id] = value; diagnostics.push( createOverrideDiagnostic('override.unknown_selection', { intentPath: `selections.${id}`, message: `Unknown selection "${id}" was preserved but will not be expanded.`, }), ); continue; } const parsed = parseKnownSelection(definition, value); if (parsed === undefined) { diagnostics.push( createOverrideDiagnostic('override.invalid_value', { intentPath: `selections.${id}`, message: `Known selection "${id}" does not match its exact value shape.`, }), ); } else { knownSelections[id] = parsed; } } } const knownOverrides: Record = {}; const unknownOverrides: Record = {}; if (raw.overrides !== undefined && !isRecord(raw.overrides)) { diagnostics.push( createOverrideDiagnostic('override.invalid_structure', { intentPath: 'overrides', message: 'overrides must be an object when present.', }), ); } else if (isRecord(raw.overrides)) { if ( (hasOwn(raw.overrides, 'light') && isRecord(raw.overrides.light)) || (hasOwn(raw.overrides, 'dark') && isRecord(raw.overrides.dark)) ) { diagnostics.push( createOverrideDiagnostic('override.legacy_shape', { intentPath: 'overrides', }), ); } else { for (const [targetPath, value] of Object.entries(raw.overrides)) { if ( !hasOwn(data.targets.targets, targetPath) || !data.targets.targets[targetPath].availableIn.includes(basePreset) ) { unknownOverrides[targetPath] = value; diagnostics.push( createOverrideDiagnostic('override.unknown_target', { intentPath: `overrides.${targetPath}`, tokenPath: targetPath, message: `Unknown direct target "${targetPath}" was preserved but will not be expanded.`, }), ); continue; } const parsed = parseTokenOverride(value, targetPath, data, basePreset, diagnostics); if (parsed) knownOverrides[targetPath] = parsed; } } } if (hasErrorDiagnostics(diagnostics)) return { valid: false, diagnostics }; const preservedUnknown = { topLevel: preservedTopLevel, extensions: preservedExtensions, inputs: unknownInputs, selections: unknownSelections, overrides: unknownOverrides, }; const parsed: ParsedThemeOverride = { kind: 'parsed-theme-override', raw, metadata: { schemaVersion: OVERRIDE_SCHEMA_VERSION, basePreset }, inputs: { known: knownInputs as ThemeInputs, unknown: unknownInputs, }, selections: { known: knownSelections as ThemeSelections, unknown: unknownSelections, }, overrides: { known: knownOverrides as TokenOverrides, unknown: unknownOverrides, }, preservedUnknown, }; return { valid: true, parsed, diagnostics }; } export const parseThemeOverride: ParseThemeOverride = (raw) => parseThemeOverrideWithData(raw, overrideRuntimeData);