feat: release design system engine, studio and component contracts

This commit is contained in:
Clintchiz committed 2026-10-19 13:09:03 +05:30
1 parent ff1065fb27
commit 3943b946d6
693 files changed
+175546 -3894

No files matched your search

+221
View File
@@ -0,0 +1,221 @@
import type { DesignDiagnostic, DesignSystem, DesignToken, TokenType } from "./types.ts";
import { validateRecipes } from "./recipes.ts";
export const DESIGN_SYSTEM_SCHEMA_VERSION = 1;
export const TOKEN_TYPES: readonly TokenType[] = [
"color",
"dimension",
"number",
"fontFamily",
"duration",
"shadow",
"fontWeight",
"colorScheme",
];
const forbidden = new Set(["__proto__", "prototype", "constructor"]);
export const validName = (value: string): boolean =>
/^[a-zA-Z][\w.-]*$/.test(value) && !value.split(/[.-]/).some((part) => forbidden.has(part));
export const isRecord = (v: unknown): v is Record<string, unknown> =>
typeof v === "object" &&
v !== null &&
!Array.isArray(v) &&
[Object.prototype, null].includes(Object.getPrototypeOf(v));
const length = /^-?(?:\d*\.)?\d+(?:px|rem|em|%|vw|vh|dvh|svh|ch|ex|vmin|vmax)$/;
/** Deliberately bounded CSS subset. References use { ref }, never arbitrary var()/url(). */
export function validTokenValue(type: TokenType, value: unknown): boolean {
if (isRecord(value))
return Object.keys(value).length === 1 && typeof value.ref === "string" && validName(value.ref);
if (typeof value === "number")
return (
Number.isFinite(value) &&
(type === "number" || (type === "fontWeight" && value >= 1 && value <= 1000))
);
if (
typeof value !== "string" ||
!value.trim() ||
value.length > 2048 ||
/[;{}<>\\]|\/\*|\*\//.test(value) ||
[...value].some((character) => character.charCodeAt(0) < 32)
)
return false;
if (type === "colorScheme") return value === "light" || value === "dark";
if (type === "dimension") return value === "0" || length.test(value);
if (type === "duration") return /^(?:\d*\.)?\d+(ms|s)$/.test(value);
if (type === "fontWeight") return /^(normal|bold|[1-9]\d{0,2}|1000)$/.test(value);
if (type === "number") return /^-?(?:\d*\.)?\d+$/.test(value) && Number.isFinite(Number(value));
if (type === "fontFamily")
return /^(?:"[\w -]+"|'[\w -]+'|[a-zA-Z][\w -]*)(?:\s*,\s*(?:"[\w -]+"|'[\w -]+'|[a-zA-Z][\w -]*))*$/.test(
value,
);
if (type === "color") {
if (
/^(?:#[\da-f]{3}|#[\da-f]{4}|#[\da-f]{6}|#[\da-f]{8})$/i.test(value) ||
/^(?:transparent|currentColor|black|white|red|green|blue|gray|grey|orange|purple|yellow|pink|rebeccapurple)$/i.test(
value,
)
)
return true;
const call = /^(rgb|rgba|hsl|hsla|oklch|oklab|lab|lch)\(([^()]+)\)$/i.exec(value);
if (!call) return false;
const args = call[2]!.trim();
const kind = call[1]!.toLowerCase();
let channels: string[];
if (args.includes(",")) {
if (args.includes("/") || !["rgb", "rgba", "hsl", "hsla"].includes(kind)) return false;
channels = args.split(/\s*,\s*/);
} else {
const sections = args.split(/\s*\/\s*/);
if (sections.length > 2) return false;
channels = sections[0]!.trim().split(/\s+/);
if (channels.length !== 3) return false;
if (sections.length === 2) channels.push(sections[1]!);
}
return (
(channels.length === 3 || channels.length === 4) &&
channels.every((channel, index) => {
const hue =
(["hsl", "hsla"].includes(kind) && index === 0) ||
(["lch", "oklch"].includes(kind) && index === 2);
const pattern = hue ? /^[+-]?(?:\d*\.)?\d+(?:deg)?$/ : /^[+-]?(?:\d*\.)?\d+%?$/;
if (!pattern.test(channel) || !Number.isFinite(parseFloat(channel))) return false;
return (
!(["hsl", "hsla"].includes(kind) && (index === 1 || index === 2)) || channel.endsWith("%")
);
})
);
}
if (type === "shadow") {
if (value === "none") return true;
const shadows = value.split(/,(?![^()]*\))/);
return shadows.every((shadow) => {
let colors = 0;
const dimensions = shadow
.replace(
/(?:rgba?|hsla?|oklch|oklab|lab|lch)\([^()]*\)|#[\da-f]+|\b(?:black|white|transparent|currentColor)\b/gi,
(color) => {
if (!validTokenValue("color", color)) return "INVALID";
colors++;
return "";
},
)
.trim()
.split(/\s+/);
const inset = dimensions.indexOf("inset");
if (inset !== -1) dimensions.splice(inset, 1);
return (
colors <= 1 &&
dimensions.length >= 2 &&
dimensions.length <= 4 &&
dimensions.every((part) => part === "0" || (length.test(part) && !part.endsWith("%"))) &&
(dimensions.length < 3 || parseFloat(dimensions[2]!) >= 0)
);
});
}
return false;
}
export function validateDesignSystem(input: unknown): DesignDiagnostic[] {
const issues: DesignDiagnostic[] = [];
const error = (code: string, path: string, message: string) =>
issues.push({ code, path, message });
if (!isRecord(input))
return [{ code: "WRN-DS-SCHEMA", path: "$", message: "Expected a data-only object." }];
const fields = new Set([
"schemaVersion",
"id",
"name",
"version",
"description",
"extends",
"tokens",
"modes",
"densities",
"defaultMode",
"recipes",
]);
for (const key of Object.keys(input).sort())
if (!fields.has(key)) error("WRN-DS-FIELD", key, "Unknown or unsupported field.");
if (input.schemaVersion !== 1)
error("WRN-DS-VERSION", "schemaVersion", "Only schemaVersion 1 is supported.");
for (const key of ["id", "name", "version"] as const)
if (typeof input[key] !== "string" || !input[key].trim())
error("WRN-DS-SCHEMA", key, "Expected a nonempty string.");
if (typeof input.id === "string" && !validName(input.id))
error("WRN-DS-NAME", "id", "Invalid system identifier.");
if (input.description !== undefined && typeof input.description !== "string")
error("WRN-DS-SCHEMA", "description", "Expected a string.");
if (
input.extends !== undefined &&
(!Array.isArray(input.extends) ||
input.extends.some((v) => typeof v !== "string" || !validName(v)))
)
error("WRN-DS-SCHEMA", "extends", "Expected system identifier array.");
if (
input.defaultMode !== undefined &&
(typeof input.defaultMode !== "string" || !validName(input.defaultMode))
)
error("WRN-DS-NAME", "defaultMode", "Invalid mode name.");
const map = (value: unknown, path: string) => {
if (!isRecord(value)) {
error("WRN-DS-SCHEMA", path, "Expected a token map.");
return;
}
for (const [key, token] of Object.entries(value).sort()) {
const at = `${path}.${key}`;
if (!validName(key)) error("WRN-DS-NAME", at, "Invalid token name.");
if (!isRecord(token)) {
error("WRN-DS-SCHEMA", at, "Expected a token object.");
continue;
}
for (const field of Object.keys(token))
if (!["type", "value", "legacy", "description"].includes(field))
error("WRN-DS-FIELD", `${at}.${field}`, "Unknown token field.");
if (!TOKEN_TYPES.includes(token.type as TokenType))
error("WRN-DS-TYPE", `${at}.type`, "Unsupported token type.");
else if (!validTokenValue(token.type as TokenType, token.value))
error("WRN-DS-VALUE", `${at}.value`, "Invalid or unsupported token value.");
if (
token.legacy !== undefined &&
(typeof token.legacy !== "string" ||
!/^[a-z][a-z0-9-]*$/.test(token.legacy) ||
!validName(token.legacy))
)
error("WRN-DS-NAME", `${at}.legacy`, "Invalid legacy token suffix.");
if (token.legacy === "color-scheme" && token.type !== "colorScheme")
error("WRN-DS-TYPE", `${at}.legacy`, "color-scheme requires colorScheme type.");
if (token.description !== undefined && typeof token.description !== "string")
error("WRN-DS-SCHEMA", `${at}.description`, "Expected a string.");
}
};
map(input.tokens, "tokens");
for (const axis of ["modes", "densities"])
if (input[axis] !== undefined) {
if (!isRecord(input[axis])) error("WRN-DS-SCHEMA", axis, "Expected a map.");
else
for (const [key, tokens] of Object.entries(input[axis]).sort()) {
if (!validName(key)) error("WRN-DS-NAME", `${axis}.${key}`, "Invalid axis name.");
map(tokens, `${axis}.${key}`);
}
}
issues.push(...validateRecipes(input.recipes));
return issues;
}
export class DesignSystemError extends Error {
constructor(public readonly diagnostics: DesignDiagnostic[]) {
super(diagnostics.map((d) => `${d.code} ${d.path}: ${d.message}`).join("\n"));
this.name = "DesignSystemError";
}
}
export function assertDesignSystem(input: unknown): asserts input is DesignSystem {
const issues = validateDesignSystem(input);
if (issues.length) throw new DesignSystemError(issues);
}
export function defineDesignSystem<T extends DesignSystem>(input: T): T {
assertDesignSystem(input);
return input;
}
export function isReference(value: DesignToken["value"]): value is { ref: string } {
return isRecord(value);
}