improved a lot of things

This commit is contained in:
SinachPat
2026-04-26 09:09:55 +01:00
parent 2911f22df8
commit 97315846be
70 changed files with 4967 additions and 10 deletions
+89
View File
@@ -0,0 +1,89 @@
import { DesignLanguageFileBodySchema, type DesignLanguageFileBody } from './schema.js';
import type { ZodError } from 'zod';
// ── Validation result ─────────────────────────────────────────────────────────
export interface ValidationSuccess {
valid: true;
dlf: DesignLanguageFileBody;
}
export interface ValidationFailure {
valid: false;
errors: ValidationError[];
}
export interface ValidationError {
path: string;
message: string;
}
export type ValidationResult = ValidationSuccess | ValidationFailure;
// ── Parse & validate ──────────────────────────────────────────────────────────
export function validateDesignLanguageFile(input: unknown): ValidationResult {
const result = DesignLanguageFileBodySchema.safeParse(input);
if (result.success) {
return { valid: true, dlf: result.data };
}
return { valid: false, errors: formatZodErrors(result.error) };
}
function formatZodErrors(error: ZodError): ValidationError[] {
return error.errors.map(e => ({
path: e.path.join('.') || '(root)',
message: e.message,
}));
}
// ── Runtime constraint checks ─────────────────────────────────────────────────
// These run during visual edits and AI completions to catch violations
// against the active DLF before they're shown to the user.
export interface ViolationCheck {
/** Name of the component being checked */
componentName: string;
/** Props being applied */
props: Record<string, unknown>;
/** The active DLF */
dlf: DesignLanguageFileBody;
}
export interface Violation {
prop: string;
value: unknown;
message: string;
severity: 'error' | 'warning';
}
export function checkComponentConstraints(check: ViolationCheck): Violation[] {
const { componentName, props, dlf } = check;
const violations: Violation[] = [];
const componentRule = dlf.components?.[componentName];
if (!componentRule) return violations;
const { props: propRules } = componentRule;
if (!propRules) return violations;
for (const [propKey, rule] of Object.entries(propRules)) {
const value = props[propKey];
// Use hasOwnProperty to distinguish "key absent" from "key set to undefined".
// Under exactOptionalPropertyTypes these are semantically different.
if (rule.required && !Object.prototype.hasOwnProperty.call(props, propKey)) {
violations.push({ prop: propKey, value, message: `"${propKey}" is required by design system rules`, severity: 'error' });
}
if (rule.forbidden && value !== undefined && rule.forbidden.includes(value)) {
violations.push({ prop: propKey, value, message: `"${propKey}=${String(value)}" is forbidden by design system rules`, severity: 'error' });
}
if (rule.allowed && value !== undefined && !rule.allowed.includes(value)) {
violations.push({ prop: propKey, value, message: `"${propKey}=${String(value)}" is not in the allowed values list`, severity: 'warning' });
}
}
return violations;
}