import { readFileSync } from "node:fs" export const repositoryRoot = new URL("../", import.meta.url) export const read = path => readFileSync(new URL(path, repositoryRoot), "utf8").replace(/\r\n/g, "\n") const source = read("packages/ui/src/theme/contract.ts") export const definitions = [...source.matchAll(/"([^"]+)": \{ property: "([^"]+)", defaultValue: [^\n]+?, type: "(color|radius)" \}/g)] .map(([, key, property, type]) => ({ key, property, type })) if (!definitions.length || new Set(definitions.map(def => def.key)).size !== definitions.length || new Set(definitions.map(def => def.property)).size !== definitions.length) throw Error("Missing or duplicate contract entries") const groupsSource = source.slice(source.indexOf("export const runtimeGradientGroups")) export const gradientGroups = [...groupsSource.matchAll(/\[([^\[\]]+)\]/g)].map(([, body]) => [...body.matchAll(/"([^"]+)"/g)].map(([, key]) => key)) if (!gradientGroups.length || gradientGroups.some(group => !group.length || group.some(key => !definitions.some(def => def.key === key)))) throw Error("Unrecognized production gradient groups") // Build-time check derived from the production allowlist. Runtime uses isRuntimeTheme. export function validateDocument(theme, fullDefault = false) { if (!theme || theme.schemaVersion !== 1 || !theme.tokens || typeof theme.tokens !== "object" || Array.isArray(theme.tokens)) throw Error("Expected v1 RuntimeTheme document") for (const [key, value] of Object.entries(theme.tokens)) { const definition = definitions.find(def => def.key === key) if (!definition || typeof value !== "string" || !(definition.type === "color" ? /^#[\da-f]{6}$/i : /^(?:[2-9]|1[0-2])px$/).test(value)) throw Error(`Invalid Theme token: ${key}`) } for (const group of gradientGroups) { const count = group.filter(key => Object.hasOwn(theme.tokens, key)).length if (count && count !== group.length) throw Error(`Incomplete gradient group: ${group[0]}`) } if (fullDefault && definitions.some(def => def.type === "color" && !Object.hasOwn(theme.tokens, def.key))) throw Error("default.json must contain every public color role/palette/gradient value") if (Object.keys(theme.tokens).some(key => definitions.find(def => def.key === key).type !== "color")) throw Error("Theme artifacts are color-only; geometry belongs to package CSS") return theme }