desen-core 1.0.0-draft.3 → 1.0.0-draft.31
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +1 -1
- package/LICENSE.md +202 -0
- package/dist/schemas/binding.d.ts +16 -16
- package/dist/schemas/binding.js +1 -0
- package/dist/schemas/composition.d.ts +90 -2
- package/dist/schemas/composition.js +55 -6
- package/dist/schemas/element.d.ts +56 -49
- package/dist/schemas/element.js +1 -0
- package/dist/schemas/style.d.ts +195 -4
- package/dist/schemas/style.js +78 -3
- package/dist/schemas/surface.d.ts +18 -23
- package/dist/schemas/surface.js +2 -1
- package/package.json +1 -1
- package/src/schemas/binding.ts +1 -0
- package/src/schemas/composition.ts +58 -8
- package/src/schemas/element.ts +1 -0
- package/src/schemas/style.ts +83 -3
- package/src/schemas/surface.ts +2 -1
package/src/schemas/style.ts
CHANGED
|
@@ -29,6 +29,16 @@ export const styleSpec = z.object({
|
|
|
29
29
|
font_weight_token: dtcgTokenPath.optional(),
|
|
30
30
|
line_height: z.number().optional(),
|
|
31
31
|
line_height_token: dtcgTokenPath.optional(),
|
|
32
|
+
line_height_percent: z.number().optional(),
|
|
33
|
+
letter_spacing: z.number().optional(),
|
|
34
|
+
letter_spacing_token: dtcgTokenPath.optional(),
|
|
35
|
+
letter_spacing_percent: z.number().optional(),
|
|
36
|
+
text_align: z.string().optional(),
|
|
37
|
+
text_decoration: z.string().optional(),
|
|
38
|
+
text_transform: z.string().optional(),
|
|
39
|
+
|
|
40
|
+
// ── Nested text_style (for composition children) ──
|
|
41
|
+
text_style: z.record(z.any()).optional(),
|
|
32
42
|
|
|
33
43
|
// ── Spacing Properties ──
|
|
34
44
|
padding: z.union([
|
|
@@ -53,10 +63,14 @@ export const styleSpec = z.object({
|
|
|
53
63
|
margin_token: dtcgTokenPath.optional(),
|
|
54
64
|
|
|
55
65
|
// ── Border Properties ──
|
|
56
|
-
border_radius: z.number().optional(),
|
|
66
|
+
border_radius: z.union([z.number(), z.array(z.number())]).optional(),
|
|
57
67
|
border_radius_token: dtcgTokenPath.optional(),
|
|
58
68
|
border_width: z.number().optional(),
|
|
59
69
|
border_width_token: dtcgTokenPath.optional(),
|
|
70
|
+
border_top_width: z.number().optional(),
|
|
71
|
+
border_right_width: z.number().optional(),
|
|
72
|
+
border_bottom_width: z.number().optional(),
|
|
73
|
+
border_left_width: z.number().optional(),
|
|
60
74
|
|
|
61
75
|
// ── Dimension Properties ──
|
|
62
76
|
min_height: z.number().optional(),
|
|
@@ -68,10 +82,76 @@ export const styleSpec = z.object({
|
|
|
68
82
|
height: z.union([z.number(), z.string()]).optional(),
|
|
69
83
|
height_token: dtcgTokenPath.optional(),
|
|
70
84
|
|
|
71
|
-
// ──
|
|
85
|
+
// ── Visual ──
|
|
72
86
|
opacity: z.number().min(0).max(1).optional(),
|
|
73
87
|
opacity_token: dtcgTokenPath.optional(),
|
|
74
|
-
|
|
88
|
+
box_shadow: z.string().optional(),
|
|
89
|
+
}).passthrough().superRefine((data, ctx) => {
|
|
90
|
+
// Fail-Closed Governance: If a literal style value exists, its corresponding token MUST exist.
|
|
91
|
+
const rules = [
|
|
92
|
+
{ literal: 'bg_color', token: 'bg_color_token' },
|
|
93
|
+
{ literal: 'text_color', token: 'text_color_token' },
|
|
94
|
+
{ literal: 'border_color', token: 'border_color_token' },
|
|
95
|
+
{ literal: 'font_family', token: 'font_family_token' },
|
|
96
|
+
{ literal: 'font_size', token: 'font_size_token' },
|
|
97
|
+
{ literal: 'font_weight', token: 'font_weight_token' },
|
|
98
|
+
{ literal: 'line_height', token: 'line_height_token' },
|
|
99
|
+
{ literal: 'line_height_percent', token: 'line_height_token' },
|
|
100
|
+
{ literal: 'letter_spacing', token: 'letter_spacing_token' },
|
|
101
|
+
{ literal: 'letter_spacing_percent', token: 'letter_spacing_token' },
|
|
102
|
+
{ literal: 'padding', token: 'padding_token' },
|
|
103
|
+
{ literal: 'margin', token: 'margin_token' },
|
|
104
|
+
{ literal: 'border_radius', token: 'border_radius_token' },
|
|
105
|
+
{ literal: 'border_width', token: 'border_width_token' },
|
|
106
|
+
{ literal: 'border_top_width', token: 'border_width_token' },
|
|
107
|
+
{ literal: 'border_right_width', token: 'border_width_token' },
|
|
108
|
+
{ literal: 'border_bottom_width', token: 'border_width_token' },
|
|
109
|
+
{ literal: 'border_left_width', token: 'border_width_token' }
|
|
110
|
+
];
|
|
111
|
+
|
|
112
|
+
rules.forEach(({ literal, token }) => {
|
|
113
|
+
const val = data[literal as keyof typeof data];
|
|
114
|
+
if (val !== undefined && data[token as keyof typeof data] === undefined) {
|
|
115
|
+
|
|
116
|
+
// EXEMPTION: Border widths of 0 or 1 do not require token binding
|
|
117
|
+
if (literal.includes('border_width') || literal.includes('border_top_width') || literal.includes('border_right_width') || literal.includes('border_bottom_width') || literal.includes('border_left_width')) {
|
|
118
|
+
if (val === 0 || val === 1) return;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
ctx.addIssue({
|
|
122
|
+
code: z.ZodIssueCode.custom,
|
|
123
|
+
message: `Fail-Closed Governance: Unbound Literal Value. Element declares [${literal}] but is missing [${token}]. Hardcoded visual styles are rejected by the design system.`,
|
|
124
|
+
path: [literal],
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
// Also validate nested text_style if present
|
|
130
|
+
if (data.text_style && typeof data.text_style === 'object') {
|
|
131
|
+
const ts = data.text_style as Record<string, any>;
|
|
132
|
+
const textRules = [
|
|
133
|
+
{ literal: 'text_color', token: 'text_color_token' },
|
|
134
|
+
{ literal: 'font_family', token: 'font_family_token' },
|
|
135
|
+
{ literal: 'font_size', token: 'font_size_token' },
|
|
136
|
+
{ literal: 'font_weight', token: 'font_weight_token' },
|
|
137
|
+
{ literal: 'line_height', token: 'line_height_token' },
|
|
138
|
+
{ literal: 'line_height_percent', token: 'line_height_token' },
|
|
139
|
+
{ literal: 'letter_spacing', token: 'letter_spacing_token' },
|
|
140
|
+
{ literal: 'letter_spacing_percent', token: 'letter_spacing_token' },
|
|
141
|
+
];
|
|
142
|
+
textRules.forEach(({ literal, token }) => {
|
|
143
|
+
if (ts[literal] !== undefined && ts[token] === undefined) {
|
|
144
|
+
ctx.addIssue({
|
|
145
|
+
code: z.ZodIssueCode.custom,
|
|
146
|
+
message: `Fail-Closed Governance: Unbound Literal Value. text_style declares [${literal}] but is missing [${token}]. Hardcoded visual styles are rejected.`,
|
|
147
|
+
path: ['text_style', literal],
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
return true;
|
|
154
|
+
}); // Allow vendor extensions (§2.9)
|
|
75
155
|
|
|
76
156
|
// ─── Types ───────────────────────────────────────────────────────
|
|
77
157
|
export type StyleSpec = z.infer<typeof styleSpec>;
|
package/src/schemas/surface.ts
CHANGED
|
@@ -10,11 +10,12 @@ import { telemetrySpec } from "./telemetry";
|
|
|
10
10
|
// root level; it must be contained within a Composition.
|
|
11
11
|
export const surfaceSpec = z.object({
|
|
12
12
|
id: z.string().min(1).regex(/^[A-Za-z][A-Za-z0-9_.:-]{0,127}$/),
|
|
13
|
+
name: z.string().optional(),
|
|
13
14
|
root: compositionSpec,
|
|
14
15
|
telemetry: telemetrySpec,
|
|
15
16
|
metadata: z.record(z.any()).optional(),
|
|
16
17
|
environment: z.record(z.any()).optional(),
|
|
17
|
-
}).
|
|
18
|
+
}).strict();
|
|
18
19
|
|
|
19
20
|
// ─── Types ───────────────────────────────────────────────────────
|
|
20
21
|
export type SurfaceSpec = z.infer<typeof surfaceSpec>;
|