import type { Severity } from "../severity.js"; export type { Severity } from "../severity.js"; export const EXPLAIN_URL_BASE = "https://usefragments.com/errors/"; export type CodeCategory = | "canonical-usage" | "tokens" | "a11y" | "states" | "composition" | "props" | "performance" | "safety" | "system"; export type CodeLifecycle = "experimental" | "stable" | "deprecated"; export interface FuiCode { code: string; ruleId: string; category: CodeCategory; defaultSeverity: Severity; title: string; explainUrl: string; lifecycle: CodeLifecycle; replacedBy?: string; fixAvailable: boolean; evidenceRequired: boolean; } function code(entry: Omit & { explainUrl?: never }): FuiCode { return { ...entry, explainUrl: `${EXPLAIN_URL_BASE}${entry.code}`, }; } export const CODES = [ code({ code: "FUI1001", ruleId: "components/non-canonical", category: "canonical-usage", defaultSeverity: "serious", title: "Component should use the canonical primitive", lifecycle: "experimental", fixAvailable: true, evidenceRequired: true, }), code({ code: "FUI1002", ruleId: "components/preferred-component", category: "canonical-usage", defaultSeverity: "serious", title: "Preferred component should be used", lifecycle: "experimental", fixAvailable: true, evidenceRequired: true, }), code({ code: "FUI1003", ruleId: "imports/preferred-path", category: "canonical-usage", defaultSeverity: "moderate", title: "Import should use the preferred path", lifecycle: "experimental", fixAvailable: true, evidenceRequired: true, }), code({ code: "FUI1004", ruleId: "components/prefer-library", category: "canonical-usage", defaultSeverity: "moderate", title: "Library component should be preferred", lifecycle: "experimental", fixAvailable: true, evidenceRequired: true, }), code({ code: "FUI1005", ruleId: "components/allow", category: "canonical-usage", defaultSeverity: "serious", title: "Component is not in the allowed set", lifecycle: "experimental", fixAvailable: false, evidenceRequired: true, }), code({ code: "FUI1006", ruleId: "components/deny", category: "canonical-usage", defaultSeverity: "serious", title: "Component is denied by policy", lifecycle: "experimental", fixAvailable: false, evidenceRequired: true, }), code({ code: "FUI2001", ruleId: "tokens/require-design-tokens", category: "tokens", defaultSeverity: "serious", title: "Design token is required", lifecycle: "experimental", fixAvailable: true, evidenceRequired: true, }), code({ code: "FUI2002", ruleId: "tokens/allowed-prefixes", category: "tokens", defaultSeverity: "moderate", title: "Token prefix is not allowed", lifecycle: "experimental", fixAvailable: true, evidenceRequired: true, }), code({ code: "FUI2003", ruleId: "tokens/require-dual-fallback", category: "tokens", defaultSeverity: "serious", title: "Token fallback is required", lifecycle: "experimental", fixAvailable: true, evidenceRequired: true, }), code({ code: "FUI2004", ruleId: "styles/no-raw-dimensions", category: "tokens", defaultSeverity: "serious", title: "Raw dimension should use a token", lifecycle: "experimental", fixAvailable: true, evidenceRequired: true, }), code({ code: "FUI2005", ruleId: "styles/no-raw-color", category: "tokens", defaultSeverity: "serious", title: "Raw color should use a token", lifecycle: "experimental", fixAvailable: true, evidenceRequired: true, }), code({ code: "FUI2006", ruleId: "styles/no-raw-spacing", category: "tokens", defaultSeverity: "serious", title: "Raw spacing should use the scale", lifecycle: "experimental", fixAvailable: true, evidenceRequired: true, }), code({ code: "FUI2007", ruleId: "tailwind/arbitrary-color", category: "tokens", defaultSeverity: "moderate", title: "Tailwind arbitrary color should use a token", lifecycle: "experimental", fixAvailable: true, evidenceRequired: true, }), code({ code: "FUI2008", ruleId: "tailwind/arbitrary-spacing", category: "tokens", defaultSeverity: "moderate", title: "Tailwind arbitrary spacing should use the scale", lifecycle: "experimental", fixAvailable: true, evidenceRequired: true, }), code({ code: "FUI2009", ruleId: "tailwind/forbidden-palette", category: "tokens", defaultSeverity: "moderate", title: "Tailwind palette is forbidden", lifecycle: "experimental", fixAvailable: false, evidenceRequired: true, }), code({ code: "FUI2010", ruleId: "tailwind/raw-color-via-token", category: "tokens", defaultSeverity: "moderate", title: "Tailwind color should use a resolved token", lifecycle: "experimental", fixAvailable: true, evidenceRequired: true, }), code({ code: "FUI2011", ruleId: "tailwind/off-scale-spacing-token", category: "tokens", defaultSeverity: "moderate", title: "Tailwind spacing token is off scale", lifecycle: "experimental", fixAvailable: true, evidenceRequired: true, }), code({ code: "FUI2012", ruleId: "tailwind/unknown-class", category: "tokens", defaultSeverity: "moderate", title: "Tailwind class is unknown", lifecycle: "experimental", fixAvailable: false, evidenceRequired: true, }), code({ code: "FUI2013", ruleId: "brand/enforce-seeds", category: "tokens", defaultSeverity: "serious", title: "Brand seed token is required", lifecycle: "experimental", fixAvailable: true, evidenceRequired: true, }), code({ code: "FUI2014", ruleId: "theme/no-theme-coupled-literal", category: "tokens", defaultSeverity: "moderate", title: "Theme-coupled literal should use a semantic token", lifecycle: "experimental", fixAvailable: false, evidenceRequired: true, }), code({ code: "FUI2015", ruleId: "tokens/css-vars-must-be-defined", category: "tokens", defaultSeverity: "moderate", title: "CSS variable is not in the contract vocabulary", lifecycle: "experimental", fixAvailable: false, evidenceRequired: true, }), code({ code: "FUI3001", ruleId: "a11y/required-accessible-name", category: "a11y", defaultSeverity: "serious", title: "Accessible name is required", lifecycle: "experimental", fixAvailable: false, evidenceRequired: true, }), code({ code: "FUI3002", ruleId: "a11y/standard", category: "a11y", defaultSeverity: "serious", title: "Accessibility standard failed", lifecycle: "experimental", fixAvailable: false, evidenceRequired: true, }), code({ code: "FUI5001", ruleId: "components/required-children", category: "composition", defaultSeverity: "serious", title: "Required child component is missing", lifecycle: "experimental", fixAvailable: false, evidenceRequired: true, }), code({ code: "FUI5002", ruleId: "components/canonical-usage", category: "composition", defaultSeverity: "serious", title: "Component should use canonical composition", lifecycle: "deprecated", replacedBy: "FUI1001", fixAvailable: true, evidenceRequired: true, }), code({ code: "FUI5003", ruleId: "composition/cardinality", category: "composition", defaultSeverity: "moderate", title: "Too many of a component in one region", lifecycle: "experimental", fixAvailable: false, evidenceRequired: true, }), code({ code: "FUI5004", ruleId: "composition/co-occurrence", category: "composition", defaultSeverity: "moderate", title: "Required companion component is missing from the region", lifecycle: "experimental", fixAvailable: false, evidenceRequired: true, }), code({ code: "FUI6001", ruleId: "props/unknown", category: "props", defaultSeverity: "moderate", title: "Prop is unknown", lifecycle: "experimental", fixAvailable: false, evidenceRequired: true, }), code({ code: "FUI6002", ruleId: "props/invalid-value", category: "props", defaultSeverity: "serious", title: "Prop value is invalid", lifecycle: "experimental", fixAvailable: true, evidenceRequired: true, }), code({ code: "FUI6003", ruleId: "props/banned-pattern", category: "props", defaultSeverity: "serious", title: "Prop pattern is banned", lifecycle: "experimental", fixAvailable: false, evidenceRequired: true, }), code({ code: "FUI6004", ruleId: "components/forbidden-prop-value", category: "props", defaultSeverity: "serious", title: "Prop value is forbidden", lifecycle: "experimental", fixAvailable: true, evidenceRequired: true, }), code({ code: "FUI6005", ruleId: "components/unknown-prop", category: "props", defaultSeverity: "moderate", title: "Component prop is unknown", lifecycle: "experimental", fixAvailable: false, evidenceRequired: true, }), code({ code: "FUI8001", ruleId: "safety/block-event-handlers", category: "safety", defaultSeverity: "serious", title: "Event handler prop is blocked", lifecycle: "experimental", fixAvailable: true, evidenceRequired: true, }), code({ code: "FUI8002", ruleId: "safety/block-dangerous-props", category: "safety", defaultSeverity: "critical", title: "Dangerous prop is blocked", lifecycle: "experimental", fixAvailable: true, evidenceRequired: true, }), code({ code: "FUI8003", ruleId: "safety/block-controlled-props", category: "safety", defaultSeverity: "serious", title: "Controlled prop is blocked", lifecycle: "experimental", fixAvailable: true, evidenceRequired: true, }), code({ code: "FUI8004", ruleId: "safety/block-function-props", category: "safety", defaultSeverity: "serious", title: "Function prop is blocked", lifecycle: "experimental", fixAvailable: true, evidenceRequired: true, }), code({ code: "FUI8005", ruleId: "safety/block-internal-props", category: "safety", defaultSeverity: "critical", title: "Internal prop is blocked", lifecycle: "experimental", fixAvailable: true, evidenceRequired: true, }), code({ code: "FUI8006", ruleId: "safety/sanitize-hrefs", category: "safety", defaultSeverity: "critical", title: "Href must be sanitized", lifecycle: "experimental", fixAvailable: true, evidenceRequired: true, }), code({ code: "FUI9001", ruleId: "unused-suppression", category: "system", defaultSeverity: "minor", title: "Suppression is unused", lifecycle: "experimental", fixAvailable: true, evidenceRequired: true, }), code({ code: "FUI9002", ruleId: "expired-suppression", category: "system", defaultSeverity: "serious", title: "Suppression is expired", lifecycle: "experimental", fixAvailable: true, evidenceRequired: true, }), code({ code: "FUI9003", ruleId: "missing-expiry", category: "system", defaultSeverity: "minor", title: "Suppression expiry is missing", lifecycle: "experimental", fixAvailable: true, evidenceRequired: true, }), ] as const satisfies readonly FuiCode[]; export const byCode: ReadonlyMap = new Map( CODES.map((entry) => [entry.code, entry]) ); export const byRuleId: ReadonlyMap = new Map( CODES.map((entry) => [entry.ruleId, entry]) ); export function explainUrlForCode(code: string): string { return `${EXPLAIN_URL_BASE}${code}`; }