@decantr/essence-spec 1.0.0-beta.4 → 1.0.0-beta.6
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/LICENSE +21 -0
- package/dist/index.d.ts +139 -2
- package/dist/index.js +334 -31
- package/dist/index.js.map +1 -1
- package/package.json +9 -6
- package/schema/essence.v2.json +3 -1
- package/schema/essence.v3.json +217 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Decantr AI
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/dist/index.d.ts
CHANGED
|
@@ -106,7 +106,126 @@ interface SectionedEssence {
|
|
|
106
106
|
accessibility?: Accessibility;
|
|
107
107
|
_impression?: Impression;
|
|
108
108
|
}
|
|
109
|
-
|
|
109
|
+
interface EssenceDNA {
|
|
110
|
+
theme: Theme;
|
|
111
|
+
spacing: {
|
|
112
|
+
base_unit: number;
|
|
113
|
+
scale: string;
|
|
114
|
+
density: DensityLevel;
|
|
115
|
+
content_gap: string;
|
|
116
|
+
};
|
|
117
|
+
typography: {
|
|
118
|
+
scale: string;
|
|
119
|
+
heading_weight: number;
|
|
120
|
+
body_weight: number;
|
|
121
|
+
};
|
|
122
|
+
color: {
|
|
123
|
+
palette: string;
|
|
124
|
+
accent_count: number;
|
|
125
|
+
cvd_preference: CvdPreference | string;
|
|
126
|
+
};
|
|
127
|
+
radius: {
|
|
128
|
+
philosophy: ThemeShape | string;
|
|
129
|
+
base: number;
|
|
130
|
+
};
|
|
131
|
+
elevation: {
|
|
132
|
+
system: string;
|
|
133
|
+
max_levels: number;
|
|
134
|
+
};
|
|
135
|
+
motion: {
|
|
136
|
+
preference: string;
|
|
137
|
+
duration_scale: number;
|
|
138
|
+
reduce_motion: boolean;
|
|
139
|
+
timing?: string;
|
|
140
|
+
durations?: Record<string, string>;
|
|
141
|
+
};
|
|
142
|
+
accessibility: {
|
|
143
|
+
wcag_level: WcagLevel;
|
|
144
|
+
focus_visible: boolean;
|
|
145
|
+
skip_nav: boolean;
|
|
146
|
+
};
|
|
147
|
+
personality: string[];
|
|
148
|
+
constraints?: {
|
|
149
|
+
mode?: string;
|
|
150
|
+
typography?: string;
|
|
151
|
+
borders?: string;
|
|
152
|
+
corners?: string;
|
|
153
|
+
shadows?: string;
|
|
154
|
+
effects?: Record<string, string>;
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
/** Only density and mode may be overridden per-page. DNA axioms like wcag_level and theme.style are never overrideable. */
|
|
158
|
+
interface DNAOverrides {
|
|
159
|
+
density?: DensityLevel;
|
|
160
|
+
mode?: ThemeMode;
|
|
161
|
+
}
|
|
162
|
+
interface BlueprintPage {
|
|
163
|
+
id: string;
|
|
164
|
+
route?: string;
|
|
165
|
+
shell_override?: ShellType | null;
|
|
166
|
+
layout: LayoutItem[];
|
|
167
|
+
patterns?: PatternRef[];
|
|
168
|
+
dna_overrides?: DNAOverrides;
|
|
169
|
+
surface?: string;
|
|
170
|
+
}
|
|
171
|
+
type ArchetypeRole = 'primary' | 'gateway' | 'public' | 'auxiliary';
|
|
172
|
+
interface EssenceV31Section {
|
|
173
|
+
id: string;
|
|
174
|
+
role: ArchetypeRole;
|
|
175
|
+
shell: ShellType | string;
|
|
176
|
+
features: string[];
|
|
177
|
+
description: string;
|
|
178
|
+
pages: BlueprintPage[];
|
|
179
|
+
}
|
|
180
|
+
interface RouteEntry {
|
|
181
|
+
section: string;
|
|
182
|
+
page: string;
|
|
183
|
+
}
|
|
184
|
+
interface EssenceBlueprint {
|
|
185
|
+
shell?: ShellType | string;
|
|
186
|
+
sections?: EssenceV31Section[];
|
|
187
|
+
pages?: BlueprintPage[];
|
|
188
|
+
features: string[];
|
|
189
|
+
routes?: Record<string, RouteEntry>;
|
|
190
|
+
icon_style?: string;
|
|
191
|
+
avatar_style?: string;
|
|
192
|
+
}
|
|
193
|
+
interface EssenceV3Guard {
|
|
194
|
+
mode: GuardMode;
|
|
195
|
+
dna_enforcement: 'error' | 'warn' | 'off';
|
|
196
|
+
blueprint_enforcement: 'warn' | 'off';
|
|
197
|
+
}
|
|
198
|
+
interface EssenceMeta {
|
|
199
|
+
archetype: string;
|
|
200
|
+
target: GeneratorTarget;
|
|
201
|
+
platform: Platform;
|
|
202
|
+
guard: EssenceV3Guard;
|
|
203
|
+
seo?: {
|
|
204
|
+
schema_org?: string[];
|
|
205
|
+
meta_priorities?: string[];
|
|
206
|
+
};
|
|
207
|
+
navigation?: {
|
|
208
|
+
hotkeys?: Array<{
|
|
209
|
+
key: string;
|
|
210
|
+
route?: string;
|
|
211
|
+
action?: string;
|
|
212
|
+
label: string;
|
|
213
|
+
}>;
|
|
214
|
+
command_palette?: boolean;
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
interface EssenceV3 {
|
|
218
|
+
$schema?: string;
|
|
219
|
+
version: '3.0.0' | '3.1.0';
|
|
220
|
+
dna: EssenceDNA;
|
|
221
|
+
blueprint: EssenceBlueprint;
|
|
222
|
+
meta: EssenceMeta;
|
|
223
|
+
_impression?: Impression;
|
|
224
|
+
}
|
|
225
|
+
type EssenceFile = Essence | SectionedEssence | EssenceV3;
|
|
226
|
+
declare function isV3(essence: EssenceFile): essence is EssenceV3;
|
|
227
|
+
/** Flatten sections into a flat page list (for guards and backward compat). */
|
|
228
|
+
declare function flattenPages(blueprint: EssenceBlueprint): BlueprintPage[];
|
|
110
229
|
declare function isSectioned(essence: EssenceFile): essence is SectionedEssence;
|
|
111
230
|
declare function isSimple(essence: EssenceFile): essence is Essence;
|
|
112
231
|
|
|
@@ -122,11 +241,18 @@ interface RecipeSpatialHints {
|
|
|
122
241
|
}
|
|
123
242
|
declare function computeDensity(personality: string[], recipeSpatial?: RecipeSpatialHints): Density;
|
|
124
243
|
|
|
244
|
+
interface AutoFix {
|
|
245
|
+
type: 'add_page' | 'update_layout' | 'update_blueprint';
|
|
246
|
+
patch: Record<string, unknown>;
|
|
247
|
+
}
|
|
125
248
|
interface GuardViolation {
|
|
126
249
|
rule: 'style' | 'structure' | 'layout' | 'recipe' | 'density' | 'theme-mode' | 'pattern-exists' | 'accessibility';
|
|
127
250
|
severity: 'error' | 'warning';
|
|
128
251
|
message: string;
|
|
129
252
|
suggestion?: string;
|
|
253
|
+
layer?: 'dna' | 'blueprint';
|
|
254
|
+
autoFixable?: boolean;
|
|
255
|
+
autoFix?: AutoFix;
|
|
130
256
|
}
|
|
131
257
|
interface GuardContext {
|
|
132
258
|
pageId?: string;
|
|
@@ -144,4 +270,15 @@ declare function evaluateGuard(essence: EssenceFile, context?: GuardContext): Gu
|
|
|
144
270
|
|
|
145
271
|
declare function normalizeEssence(input: Record<string, unknown>): EssenceFile;
|
|
146
272
|
|
|
147
|
-
|
|
273
|
+
/**
|
|
274
|
+
* Migrate a v2 EssenceFile to v3 format (DNA/Blueprint/Meta split).
|
|
275
|
+
* If already v3, returns unchanged. SectionedEssence uses first section's theme.
|
|
276
|
+
*/
|
|
277
|
+
declare function migrateV2ToV3(essence: EssenceFile): EssenceV3;
|
|
278
|
+
/**
|
|
279
|
+
* Migrate a v3.0 flat-page EssenceV3 to v3.1 sectioned format.
|
|
280
|
+
* If already v3.1 or has blueprint.sections, returns unchanged.
|
|
281
|
+
*/
|
|
282
|
+
declare function migrateV30ToV31(essence: EssenceV3): EssenceV3;
|
|
283
|
+
|
|
284
|
+
export { type Accessibility, type ArchetypeRole, type AutoFix, type BlueprintPage, type ColumnLayout, type CvdPreference, type DNAOverrides, type Density, type DensityLevel, type Essence, type EssenceBlueprint, type EssenceDNA, type EssenceFile, type EssenceMeta, type EssenceSection, type EssenceV3, type EssenceV31Section, type EssenceV3Guard, type GeneratorTarget, type Guard, type GuardContext, type GuardMode, type GuardViolation, type Impression, type LayoutItem, type PatternRef, type Platform, type PlatformType, type RouteEntry, type RoutingStrategy, type SectionedEssence, type ShellType, type StructurePage, type Theme, type ThemeMode, type ThemeShape, type ThemeStyle, type ValidationResult, type WcagLevel, computeDensity, evaluateGuard, flattenPages, isSectioned, isSimple, isV3, migrateV2ToV3, migrateV30ToV31, normalizeEssence, validateEssence };
|
package/dist/index.js
CHANGED
|
@@ -1,29 +1,65 @@
|
|
|
1
1
|
// src/types.ts
|
|
2
|
+
function isV3(essence) {
|
|
3
|
+
return (essence.version === "3.0.0" || essence.version === "3.1.0") && "dna" in essence && "blueprint" in essence;
|
|
4
|
+
}
|
|
5
|
+
function flattenPages(blueprint) {
|
|
6
|
+
if (blueprint.sections && blueprint.sections.length > 0) {
|
|
7
|
+
return blueprint.sections.flatMap(
|
|
8
|
+
(s) => s.pages.map((p) => ({
|
|
9
|
+
...p,
|
|
10
|
+
shell_override: p.shell_override ?? (s.shell !== blueprint.shell ? s.shell : void 0)
|
|
11
|
+
}))
|
|
12
|
+
);
|
|
13
|
+
}
|
|
14
|
+
return blueprint.pages ?? [];
|
|
15
|
+
}
|
|
2
16
|
function isSectioned(essence) {
|
|
17
|
+
if (isV3(essence)) return false;
|
|
3
18
|
return "sections" in essence && Array.isArray(essence.sections);
|
|
4
19
|
}
|
|
5
20
|
function isSimple(essence) {
|
|
21
|
+
if (isV3(essence)) return false;
|
|
6
22
|
return "archetype" in essence && !("sections" in essence);
|
|
7
23
|
}
|
|
8
24
|
|
|
9
25
|
// src/validate.ts
|
|
10
26
|
import Ajv from "ajv";
|
|
11
|
-
import { readFileSync } from "fs";
|
|
27
|
+
import { readFileSync, existsSync } from "fs";
|
|
12
28
|
import { fileURLToPath } from "url";
|
|
13
29
|
import { dirname, join } from "path";
|
|
14
30
|
var __dirname = dirname(fileURLToPath(import.meta.url));
|
|
15
|
-
var
|
|
31
|
+
var v2SchemaPath = join(__dirname, "..", "schema", "essence.v2.json");
|
|
32
|
+
var v3SchemaPath = join(__dirname, "..", "schema", "essence.v3.json");
|
|
16
33
|
var cachedAjv = null;
|
|
17
|
-
var
|
|
18
|
-
function
|
|
19
|
-
if (
|
|
34
|
+
var validatorCache = /* @__PURE__ */ new Map();
|
|
35
|
+
function getAjv() {
|
|
36
|
+
if (!cachedAjv) {
|
|
37
|
+
cachedAjv = new Ajv({ allErrors: true, strict: false, validateSchema: false });
|
|
38
|
+
}
|
|
39
|
+
return cachedAjv;
|
|
40
|
+
}
|
|
41
|
+
function getValidator(version) {
|
|
42
|
+
if (validatorCache.has(version)) return validatorCache.get(version);
|
|
43
|
+
const schemaPath = version === "v3" ? v3SchemaPath : v2SchemaPath;
|
|
44
|
+
if (!existsSync(schemaPath)) return null;
|
|
20
45
|
const schema = JSON.parse(readFileSync(schemaPath, "utf-8"));
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
46
|
+
const ajv = getAjv();
|
|
47
|
+
const validate = ajv.compile(schema);
|
|
48
|
+
validatorCache.set(version, validate);
|
|
49
|
+
return validate;
|
|
50
|
+
}
|
|
51
|
+
function detectVersion(data) {
|
|
52
|
+
if (typeof data === "object" && data !== null && "version" in data) {
|
|
53
|
+
if (data.version === "3.0.0") return "v3";
|
|
54
|
+
}
|
|
55
|
+
return "v2";
|
|
24
56
|
}
|
|
25
57
|
function validateEssence(data) {
|
|
26
|
-
const
|
|
58
|
+
const version = detectVersion(data);
|
|
59
|
+
const validate = getValidator(version);
|
|
60
|
+
if (!validate) {
|
|
61
|
+
return { valid: false, errors: [`No schema available for ${version} documents`] };
|
|
62
|
+
}
|
|
27
63
|
const valid = validate(data);
|
|
28
64
|
if (valid) {
|
|
29
65
|
return { valid: true, errors: [] };
|
|
@@ -76,8 +112,15 @@ function computeDensity(personality, recipeSpatial) {
|
|
|
76
112
|
// src/guard.ts
|
|
77
113
|
function checkThemeModeCompatibility(essence, context) {
|
|
78
114
|
if (!context.themeRegistry) return null;
|
|
79
|
-
|
|
80
|
-
|
|
115
|
+
let themeId;
|
|
116
|
+
let mode;
|
|
117
|
+
if (isV3(essence)) {
|
|
118
|
+
themeId = essence.dna.theme?.style ?? null;
|
|
119
|
+
mode = essence.dna.theme?.mode ?? null;
|
|
120
|
+
} else {
|
|
121
|
+
themeId = isSimple(essence) ? essence.theme?.style : null;
|
|
122
|
+
mode = isSimple(essence) ? essence.theme?.mode : null;
|
|
123
|
+
}
|
|
81
124
|
if (!themeId || !mode) return null;
|
|
82
125
|
const theme = context.themeRegistry.get(themeId);
|
|
83
126
|
if (!theme) {
|
|
@@ -90,7 +133,8 @@ function checkThemeModeCompatibility(essence, context) {
|
|
|
90
133
|
rule: "theme-mode",
|
|
91
134
|
severity: "error",
|
|
92
135
|
message: `Theme "${themeId}" does not support "${mode}" mode. Supported modes: ${supportedModes}.`,
|
|
93
|
-
suggestion
|
|
136
|
+
suggestion,
|
|
137
|
+
...isV3(essence) ? { layer: "dna", autoFixable: false } : {}
|
|
94
138
|
};
|
|
95
139
|
}
|
|
96
140
|
return null;
|
|
@@ -140,16 +184,17 @@ function checkPatternExistence(essence, context) {
|
|
|
140
184
|
const suggestion = similar.length > 0 ? `Similar patterns: ${similar.join(", ")}` : `Run "decantr search ${patternId}" to find alternatives.`;
|
|
141
185
|
violations.push({
|
|
142
186
|
rule: "pattern-exists",
|
|
143
|
-
severity: "error",
|
|
187
|
+
severity: isV3(essence) ? "warning" : "error",
|
|
144
188
|
message: `Pattern "${patternId}" is referenced but does not exist in the registry.`,
|
|
145
|
-
suggestion
|
|
189
|
+
suggestion,
|
|
190
|
+
...isV3(essence) ? { layer: "blueprint", autoFixable: false } : {}
|
|
146
191
|
});
|
|
147
192
|
}
|
|
148
193
|
}
|
|
149
194
|
return violations;
|
|
150
195
|
}
|
|
151
196
|
function checkAccessibility(essence, context) {
|
|
152
|
-
const accessibility = "accessibility" in essence ? essence.accessibility : void 0;
|
|
197
|
+
const accessibility = isV3(essence) ? essence.dna.accessibility : "accessibility" in essence ? essence.accessibility : void 0;
|
|
153
198
|
if (!accessibility?.wcag_level || accessibility.wcag_level === "none") {
|
|
154
199
|
return null;
|
|
155
200
|
}
|
|
@@ -160,25 +205,33 @@ function checkAccessibility(essence, context) {
|
|
|
160
205
|
rule: "accessibility",
|
|
161
206
|
severity: "error",
|
|
162
207
|
message: `WCAG ${accessibility.wcag_level} compliance required. Issues found: ${issueList}${moreCount}`,
|
|
163
|
-
suggestion: "Fix accessibility issues before proceeding. Run an accessibility audit for details."
|
|
208
|
+
suggestion: "Fix accessibility issues before proceeding. Run an accessibility audit for details.",
|
|
209
|
+
...isV3(essence) ? { layer: "dna", autoFixable: false } : {}
|
|
164
210
|
};
|
|
165
211
|
}
|
|
166
212
|
return null;
|
|
167
213
|
}
|
|
168
214
|
function evaluateGuard(essence, context = {}) {
|
|
169
|
-
const guard = essence.guard;
|
|
215
|
+
const guard = isV3(essence) ? essence.meta.guard : essence.guard;
|
|
170
216
|
if (guard.mode === "creative") {
|
|
171
217
|
return [];
|
|
172
218
|
}
|
|
173
219
|
const violations = [];
|
|
174
220
|
const isStrict = guard.mode === "strict";
|
|
175
221
|
if (context.style) {
|
|
176
|
-
|
|
177
|
-
if (
|
|
222
|
+
let essenceStyle;
|
|
223
|
+
if (isV3(essence)) {
|
|
224
|
+
essenceStyle = essence.dna.theme.style;
|
|
225
|
+
} else {
|
|
226
|
+
essenceStyle = isSimple(essence) ? essence.theme.style : null;
|
|
227
|
+
}
|
|
228
|
+
const enforceStyle = isV3(essence) ? true : guard.enforce_style !== false;
|
|
229
|
+
if (essenceStyle && context.style !== essenceStyle && enforceStyle) {
|
|
178
230
|
violations.push({
|
|
179
231
|
rule: "style",
|
|
180
232
|
severity: "error",
|
|
181
|
-
message: `Style "${context.style}" does not match essence theme "${essenceStyle}". Change the essence theme first
|
|
233
|
+
message: `Style "${context.style}" does not match essence theme "${essenceStyle}". Change the essence theme first.`,
|
|
234
|
+
...isV3(essence) ? { layer: "dna", autoFixable: false } : {}
|
|
182
235
|
});
|
|
183
236
|
}
|
|
184
237
|
}
|
|
@@ -188,8 +241,13 @@ function evaluateGuard(essence, context = {}) {
|
|
|
188
241
|
if (!pageExists) {
|
|
189
242
|
violations.push({
|
|
190
243
|
rule: "structure",
|
|
191
|
-
severity: "error",
|
|
192
|
-
message: `Page "${context.pageId}" does not exist in essence structure. Add it to the essence first
|
|
244
|
+
severity: isV3(essence) ? "warning" : "error",
|
|
245
|
+
message: `Page "${context.pageId}" does not exist in essence structure. Add it to the essence first.`,
|
|
246
|
+
...isV3(essence) ? {
|
|
247
|
+
layer: "blueprint",
|
|
248
|
+
autoFixable: true,
|
|
249
|
+
autoFix: { type: "add_page", patch: { id: context.pageId } }
|
|
250
|
+
} : {}
|
|
193
251
|
});
|
|
194
252
|
}
|
|
195
253
|
}
|
|
@@ -205,28 +263,50 @@ function evaluateGuard(essence, context = {}) {
|
|
|
205
263
|
if (!matches) {
|
|
206
264
|
violations.push({
|
|
207
265
|
rule: "layout",
|
|
208
|
-
severity: "error",
|
|
209
|
-
message: `Layout for page "${context.pageId}" deviates from essence. Expected: [${essenceLayout.join(", ")}]
|
|
266
|
+
severity: isV3(essence) ? "warning" : "error",
|
|
267
|
+
message: `Layout for page "${context.pageId}" deviates from essence. Expected: [${essenceLayout.join(", ")}].`,
|
|
268
|
+
...isV3(essence) ? {
|
|
269
|
+
layer: "blueprint",
|
|
270
|
+
autoFixable: true,
|
|
271
|
+
autoFix: { type: "update_layout", patch: { page: context.pageId, layout: context.layout } }
|
|
272
|
+
} : {}
|
|
210
273
|
});
|
|
211
274
|
}
|
|
212
275
|
}
|
|
213
276
|
}
|
|
214
|
-
if (context.recipe
|
|
215
|
-
|
|
216
|
-
|
|
277
|
+
if (context.recipe) {
|
|
278
|
+
let essenceRecipe;
|
|
279
|
+
let enforceRecipe;
|
|
280
|
+
if (isV3(essence)) {
|
|
281
|
+
essenceRecipe = essence.dna.theme.recipe;
|
|
282
|
+
enforceRecipe = true;
|
|
283
|
+
} else {
|
|
284
|
+
essenceRecipe = isSimple(essence) ? essence.theme.recipe : null;
|
|
285
|
+
enforceRecipe = guard.enforce_recipe !== false;
|
|
286
|
+
}
|
|
287
|
+
if (essenceRecipe && context.recipe !== essenceRecipe && enforceRecipe) {
|
|
217
288
|
violations.push({
|
|
218
289
|
rule: "recipe",
|
|
219
290
|
severity: "error",
|
|
220
|
-
message: `Recipe "${context.recipe}" does not match essence recipe "${essenceRecipe}"
|
|
291
|
+
message: `Recipe "${context.recipe}" does not match essence recipe "${essenceRecipe}".`,
|
|
292
|
+
...isV3(essence) ? { layer: "dna", autoFixable: false } : {}
|
|
221
293
|
});
|
|
222
294
|
}
|
|
223
295
|
}
|
|
224
296
|
if (isStrict && context.density_gap) {
|
|
225
|
-
|
|
297
|
+
let expectedGap;
|
|
298
|
+
if (isV3(essence)) {
|
|
299
|
+
const overriddenDensity = getPageDensityOverride(essence, context.pageId);
|
|
300
|
+
expectedGap = overriddenDensity ?? essence.dna.spacing.content_gap;
|
|
301
|
+
} else {
|
|
302
|
+
expectedGap = essence.density.content_gap;
|
|
303
|
+
}
|
|
304
|
+
if (context.density_gap !== expectedGap) {
|
|
226
305
|
violations.push({
|
|
227
306
|
rule: "density",
|
|
228
307
|
severity: "warning",
|
|
229
|
-
message: `Content gap "${context.density_gap}" does not match essence density "${
|
|
308
|
+
message: `Content gap "${context.density_gap}" does not match essence density "${expectedGap}".`,
|
|
309
|
+
...isV3(essence) ? { layer: "dna", autoFixable: false } : {}
|
|
230
310
|
});
|
|
231
311
|
}
|
|
232
312
|
}
|
|
@@ -240,16 +320,56 @@ function evaluateGuard(essence, context = {}) {
|
|
|
240
320
|
if (accessibilityViolation) {
|
|
241
321
|
violations.push(accessibilityViolation);
|
|
242
322
|
}
|
|
323
|
+
if (isV3(essence)) {
|
|
324
|
+
const v3Guard = guard;
|
|
325
|
+
if (v3Guard.dna_enforcement === "off") {
|
|
326
|
+
return violations.filter((v) => v.layer !== "dna");
|
|
327
|
+
}
|
|
328
|
+
if (v3Guard.dna_enforcement === "warn") {
|
|
329
|
+
for (const v of violations) {
|
|
330
|
+
if (v.layer === "dna") {
|
|
331
|
+
v.severity = "warning";
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
if (v3Guard.blueprint_enforcement === "off") {
|
|
336
|
+
return violations.filter((v) => v.layer !== "blueprint");
|
|
337
|
+
}
|
|
338
|
+
}
|
|
243
339
|
return violations;
|
|
244
340
|
}
|
|
245
341
|
function getAllPages(essence) {
|
|
342
|
+
if (isV3(essence)) {
|
|
343
|
+
const pages = flattenPages(essence.blueprint);
|
|
344
|
+
return pages.map((page) => ({
|
|
345
|
+
id: page.id,
|
|
346
|
+
shell: page.shell_override ?? essence.blueprint.shell ?? "",
|
|
347
|
+
layout: page.layout,
|
|
348
|
+
...page.surface ? { surface: page.surface } : {}
|
|
349
|
+
}));
|
|
350
|
+
}
|
|
246
351
|
if (isSimple(essence)) return essence.structure;
|
|
247
352
|
if (isSectioned(essence)) return essence.sections.flatMap((s) => s.structure);
|
|
248
|
-
|
|
353
|
+
throw new Error("Unknown EssenceFile type");
|
|
354
|
+
}
|
|
355
|
+
function getPageDensityOverride(essence, pageId) {
|
|
356
|
+
if (!pageId) return void 0;
|
|
357
|
+
const pages = flattenPages(essence.blueprint);
|
|
358
|
+
const page = pages.find((p) => p.id === pageId);
|
|
359
|
+
if (!page?.dna_overrides?.density) return void 0;
|
|
360
|
+
const densityGapMap = {
|
|
361
|
+
compact: "2",
|
|
362
|
+
comfortable: "4",
|
|
363
|
+
spacious: "6"
|
|
364
|
+
};
|
|
365
|
+
return densityGapMap[page.dna_overrides.density] ?? void 0;
|
|
249
366
|
}
|
|
250
367
|
|
|
251
368
|
// src/normalize.ts
|
|
252
369
|
function normalizeEssence(input) {
|
|
370
|
+
if (input.version === "3.0.0") {
|
|
371
|
+
return input;
|
|
372
|
+
}
|
|
253
373
|
if (input.theme && input.platform) {
|
|
254
374
|
return input;
|
|
255
375
|
}
|
|
@@ -350,11 +470,194 @@ function stripGapPrefix(gap) {
|
|
|
350
470
|
const match = gap.match(/^_gap(\d+)$/);
|
|
351
471
|
return match ? match[1] : gap;
|
|
352
472
|
}
|
|
473
|
+
|
|
474
|
+
// src/migrate.ts
|
|
475
|
+
function migrateV2ToV3(essence) {
|
|
476
|
+
if (isV3(essence)) return essence;
|
|
477
|
+
if (isSectioned(essence)) {
|
|
478
|
+
return migrateSectionedToV3(essence);
|
|
479
|
+
}
|
|
480
|
+
if (isSimple(essence)) {
|
|
481
|
+
return migrateSimpleToV3(essence);
|
|
482
|
+
}
|
|
483
|
+
throw new Error("Unknown EssenceFile type \u2014 cannot migrate");
|
|
484
|
+
}
|
|
485
|
+
function migrateSimpleToV3(essence) {
|
|
486
|
+
const dna = buildDNA(essence);
|
|
487
|
+
const blueprint = buildBlueprintFromSimple(essence);
|
|
488
|
+
const meta = buildMeta(essence);
|
|
489
|
+
return {
|
|
490
|
+
version: "3.0.0",
|
|
491
|
+
dna,
|
|
492
|
+
blueprint,
|
|
493
|
+
meta,
|
|
494
|
+
...essence._impression ? { _impression: essence._impression } : {}
|
|
495
|
+
};
|
|
496
|
+
}
|
|
497
|
+
function migrateSectionedToV3(essence) {
|
|
498
|
+
const firstSection = essence.sections[0];
|
|
499
|
+
const syntheticSimple = {
|
|
500
|
+
theme: firstSection.theme,
|
|
501
|
+
density: essence.density,
|
|
502
|
+
guard: essence.guard,
|
|
503
|
+
accessibility: essence.accessibility
|
|
504
|
+
};
|
|
505
|
+
const dna = buildDNA(syntheticSimple);
|
|
506
|
+
dna.personality = essence.personality;
|
|
507
|
+
const pages = essence.sections.flatMap(
|
|
508
|
+
(section) => section.structure.map((page) => ({
|
|
509
|
+
id: page.id,
|
|
510
|
+
shell_override: page.shell,
|
|
511
|
+
layout: page.layout,
|
|
512
|
+
...page.surface ? { surface: page.surface } : {}
|
|
513
|
+
}))
|
|
514
|
+
);
|
|
515
|
+
const allFeatures = [
|
|
516
|
+
...essence.shared_features ?? [],
|
|
517
|
+
...essence.sections.flatMap((s) => s.features ?? [])
|
|
518
|
+
];
|
|
519
|
+
const blueprint = {
|
|
520
|
+
shell: firstSection.structure[0]?.shell ?? "top-nav-main",
|
|
521
|
+
pages,
|
|
522
|
+
features: [...new Set(allFeatures)]
|
|
523
|
+
};
|
|
524
|
+
const meta = {
|
|
525
|
+
archetype: firstSection.archetype,
|
|
526
|
+
target: essence.target,
|
|
527
|
+
platform: essence.platform,
|
|
528
|
+
guard: migrateGuard(essence.guard.mode)
|
|
529
|
+
};
|
|
530
|
+
return {
|
|
531
|
+
version: "3.0.0",
|
|
532
|
+
dna,
|
|
533
|
+
blueprint,
|
|
534
|
+
meta,
|
|
535
|
+
...essence._impression ? { _impression: essence._impression } : {}
|
|
536
|
+
};
|
|
537
|
+
}
|
|
538
|
+
function buildDNA(essence) {
|
|
539
|
+
const shape = essence.theme.shape ?? "rounded";
|
|
540
|
+
const radiusBase = inferRadiusBase(shape);
|
|
541
|
+
return {
|
|
542
|
+
theme: {
|
|
543
|
+
style: essence.theme.style,
|
|
544
|
+
mode: essence.theme.mode,
|
|
545
|
+
recipe: essence.theme.recipe,
|
|
546
|
+
...essence.theme.shape ? { shape: essence.theme.shape } : {}
|
|
547
|
+
},
|
|
548
|
+
spacing: {
|
|
549
|
+
base_unit: 4,
|
|
550
|
+
scale: "linear",
|
|
551
|
+
density: essence.density?.level ?? "comfortable",
|
|
552
|
+
content_gap: essence.density?.content_gap ?? "4"
|
|
553
|
+
},
|
|
554
|
+
typography: {
|
|
555
|
+
scale: "modular",
|
|
556
|
+
heading_weight: 600,
|
|
557
|
+
body_weight: 400
|
|
558
|
+
},
|
|
559
|
+
color: {
|
|
560
|
+
palette: "semantic",
|
|
561
|
+
accent_count: 1,
|
|
562
|
+
cvd_preference: essence.accessibility?.cvd_preference ?? "auto"
|
|
563
|
+
},
|
|
564
|
+
radius: {
|
|
565
|
+
philosophy: shape,
|
|
566
|
+
base: radiusBase
|
|
567
|
+
},
|
|
568
|
+
elevation: {
|
|
569
|
+
system: "layered",
|
|
570
|
+
max_levels: 3
|
|
571
|
+
},
|
|
572
|
+
motion: {
|
|
573
|
+
preference: "subtle",
|
|
574
|
+
duration_scale: 1,
|
|
575
|
+
reduce_motion: true
|
|
576
|
+
},
|
|
577
|
+
accessibility: {
|
|
578
|
+
wcag_level: essence.accessibility?.wcag_level ?? "AA",
|
|
579
|
+
focus_visible: true,
|
|
580
|
+
skip_nav: true
|
|
581
|
+
},
|
|
582
|
+
personality: essence.personality ?? ["professional"]
|
|
583
|
+
};
|
|
584
|
+
}
|
|
585
|
+
function buildBlueprintFromSimple(essence) {
|
|
586
|
+
const defaultShell = essence.structure[0]?.shell ?? "top-nav-main";
|
|
587
|
+
return {
|
|
588
|
+
shell: defaultShell,
|
|
589
|
+
pages: essence.structure.map((page) => ({
|
|
590
|
+
id: page.id,
|
|
591
|
+
...page.shell !== defaultShell ? { shell_override: page.shell } : {},
|
|
592
|
+
layout: page.layout,
|
|
593
|
+
...page.surface ? { surface: page.surface } : {}
|
|
594
|
+
})),
|
|
595
|
+
features: essence.features ?? []
|
|
596
|
+
};
|
|
597
|
+
}
|
|
598
|
+
function buildMeta(essence) {
|
|
599
|
+
return {
|
|
600
|
+
archetype: essence.archetype,
|
|
601
|
+
target: essence.target,
|
|
602
|
+
platform: essence.platform,
|
|
603
|
+
guard: migrateGuard(essence.guard.mode)
|
|
604
|
+
};
|
|
605
|
+
}
|
|
606
|
+
function migrateGuard(mode) {
|
|
607
|
+
switch (mode) {
|
|
608
|
+
case "strict":
|
|
609
|
+
return { mode: "strict", dna_enforcement: "error", blueprint_enforcement: "warn" };
|
|
610
|
+
case "guided":
|
|
611
|
+
return { mode: "guided", dna_enforcement: "error", blueprint_enforcement: "off" };
|
|
612
|
+
case "creative":
|
|
613
|
+
default:
|
|
614
|
+
return { mode: "creative", dna_enforcement: "off", blueprint_enforcement: "off" };
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
function inferRadiusBase(shape) {
|
|
618
|
+
switch (shape) {
|
|
619
|
+
case "pill":
|
|
620
|
+
return 12;
|
|
621
|
+
case "rounded":
|
|
622
|
+
return 8;
|
|
623
|
+
case "sharp":
|
|
624
|
+
return 2;
|
|
625
|
+
default:
|
|
626
|
+
return 8;
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
function migrateV30ToV31(essence) {
|
|
630
|
+
if (essence.version === "3.1.0" || essence.blueprint.sections) {
|
|
631
|
+
return essence;
|
|
632
|
+
}
|
|
633
|
+
const section = {
|
|
634
|
+
id: essence.meta.archetype,
|
|
635
|
+
role: "primary",
|
|
636
|
+
shell: essence.blueprint.shell ?? "top-nav-main",
|
|
637
|
+
features: essence.blueprint.features,
|
|
638
|
+
description: "",
|
|
639
|
+
pages: essence.blueprint.pages ?? []
|
|
640
|
+
};
|
|
641
|
+
return {
|
|
642
|
+
...essence,
|
|
643
|
+
version: "3.1.0",
|
|
644
|
+
blueprint: {
|
|
645
|
+
...essence.blueprint,
|
|
646
|
+
sections: [section],
|
|
647
|
+
pages: void 0,
|
|
648
|
+
routes: {}
|
|
649
|
+
}
|
|
650
|
+
};
|
|
651
|
+
}
|
|
353
652
|
export {
|
|
354
653
|
computeDensity,
|
|
355
654
|
evaluateGuard,
|
|
655
|
+
flattenPages,
|
|
356
656
|
isSectioned,
|
|
357
657
|
isSimple,
|
|
658
|
+
isV3,
|
|
659
|
+
migrateV2ToV3,
|
|
660
|
+
migrateV30ToV31,
|
|
358
661
|
normalizeEssence,
|
|
359
662
|
validateEssence
|
|
360
663
|
};
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/types.ts","../src/validate.ts","../src/density.ts","../src/guard.ts","../src/normalize.ts"],"sourcesContent":["/**\n * Essence v2 schema types — normalized terminology.\n *\n * Terminology mapping (v1 wine terms → v2 normalized):\n * terroir → archetype, vintage → theme, tannins → features,\n * carafe → shell, cork → guard, blend → layout, clarity → density,\n * vessel → platform, character → personality,\n * decantation process → essence pipeline\n */\n\n// --- Theme ---\n\nexport type ThemeStyle =\n | 'auradecantism'\n | 'clean'\n | 'glassmorphism'\n | 'retro'\n | 'bioluminescent'\n | 'launchpad'\n | 'dopamine'\n | 'editorial'\n | 'liquid-glass'\n | 'prismatic';\n\nexport type ThemeMode = 'light' | 'dark' | 'auto';\nexport type ThemeShape = 'sharp' | 'rounded' | 'pill';\n\nexport interface Theme {\n style: ThemeStyle | string;\n mode: ThemeMode;\n recipe: string;\n shape?: ThemeShape;\n}\n\n// --- Platform ---\n\nexport type PlatformType = 'spa' | 'ssr' | 'static';\nexport type RoutingStrategy = 'hash' | 'history';\n\nexport interface Platform {\n type: PlatformType;\n routing: RoutingStrategy;\n}\n\n// --- Structure ---\n\nexport type ShellType =\n | 'sidebar-main'\n | 'top-nav-main'\n | 'full-bleed'\n | 'dashboard'\n | 'stacked'\n | string;\n\nexport type LayoutItem = string | PatternRef | ColumnLayout;\n\nexport interface PatternRef {\n pattern: string;\n preset?: string;\n as?: string;\n}\n\nexport interface ColumnLayout {\n cols: string[];\n at?: 'sm' | 'md' | 'lg' | 'xl' | '2xl';\n span?: Record<string, number>;\n // AUTO: Multi-breakpoint grid — array of { at, cols } entries for cascading breakpoints\n breakpoints?: { at: string; cols: number }[];\n // AUTO: \"container\" uses container queries instead of viewport breakpoints\n responsive?: 'viewport' | 'container';\n}\n\nexport interface StructurePage {\n id: string;\n shell: ShellType;\n layout: LayoutItem[];\n surface?: string;\n}\n\n// --- Density ---\n\nexport type DensityLevel = 'compact' | 'comfortable' | 'spacious';\n\nexport interface Density {\n level: DensityLevel;\n content_gap: string;\n}\n\n// --- Guard ---\n\nexport type GuardMode = 'creative' | 'guided' | 'strict';\n\nexport interface Guard {\n enforce_style?: boolean;\n enforce_recipe?: boolean;\n mode: GuardMode;\n}\n\n// --- Impression ---\n\nexport interface Impression {\n vibe: string[];\n references: string[];\n density_intent: DensityLevel | string;\n layout_intent: ShellType | string;\n novel_elements: string[];\n}\n\n// --- Generator Target ---\n\nexport type GeneratorTarget = 'decantr' | 'react' | 'vue' | 'svelte' | string;\n\n// --- Accessibility ---\n\nexport type WcagLevel = 'none' | 'A' | 'AA' | 'AAA';\n\nexport type CvdPreference =\n | 'none'\n | 'auto'\n | 'deuteranopia'\n | 'protanopia'\n | 'tritanopia'\n | 'achromatopsia';\n\nexport interface Accessibility {\n wcag_level?: WcagLevel;\n cvd_preference?: CvdPreference;\n}\n\n// --- Essence (simple) ---\n\nexport interface Essence {\n $schema?: string;\n version: string;\n archetype: string;\n theme: Theme;\n personality: string[];\n platform: Platform;\n structure: StructurePage[];\n features: string[];\n density: Density;\n guard: Guard;\n target: GeneratorTarget;\n accessibility?: Accessibility;\n _impression?: Impression;\n}\n\n// --- Essence (sectioned) ---\n\nexport interface EssenceSection {\n id: string;\n path: string;\n archetype: string;\n theme: Theme;\n structure: StructurePage[];\n features?: string[];\n}\n\nexport interface SectionedEssence {\n $schema?: string;\n version: string;\n platform: Platform;\n personality: string[];\n sections: EssenceSection[];\n shared_features?: string[];\n density: Density;\n guard: Guard;\n target: GeneratorTarget;\n accessibility?: Accessibility;\n _impression?: Impression;\n}\n\nexport type EssenceFile = Essence | SectionedEssence;\n\nexport function isSectioned(essence: EssenceFile): essence is SectionedEssence {\n return 'sections' in essence && Array.isArray((essence as SectionedEssence).sections);\n}\n\nexport function isSimple(essence: EssenceFile): essence is Essence {\n return 'archetype' in essence && !('sections' in essence);\n}\n","import Ajv from 'ajv';\nimport { readFileSync } from 'node:fs';\nimport { fileURLToPath } from 'node:url';\nimport { dirname, join } from 'node:path';\n\nconst __dirname = dirname(fileURLToPath(import.meta.url));\nconst schemaPath = join(__dirname, '..', 'schema', 'essence.v2.json');\n\nlet cachedAjv: Ajv | null = null;\nlet cachedValidate: ReturnType<Ajv['compile']> | null = null;\n\nfunction getValidator() {\n if (cachedValidate) return cachedValidate;\n const schema = JSON.parse(readFileSync(schemaPath, 'utf-8'));\n cachedAjv = new Ajv({ allErrors: true, strict: false, validateSchema: false });\n cachedValidate = cachedAjv.compile(schema);\n return cachedValidate;\n}\n\nexport interface ValidationResult {\n valid: boolean;\n errors: string[];\n}\n\nexport function validateEssence(data: unknown): ValidationResult {\n const validate = getValidator();\n const valid = validate(data);\n\n if (valid) {\n return { valid: true, errors: [] };\n }\n\n const errors = (validate.errors ?? []).map((err) => {\n const path = err.instancePath || '/';\n const msg = err.message ?? 'unknown error';\n return `${path}: ${msg}`;\n });\n\n return { valid: false, errors };\n}\n","import type { Density, DensityLevel } from './types.js';\n\ninterface RecipeSpatialHints {\n density_bias?: number;\n content_gap_shift?: number;\n}\n\ninterface DensityRule {\n traits: string[];\n level: DensityLevel;\n gap: number;\n}\n\nconst RULES: DensityRule[] = [\n { traits: ['tactical', 'dense', 'data-dense', 'utilitarian'], level: 'compact', gap: 3 },\n { traits: ['luxurious', 'premium', 'editorial'], level: 'spacious', gap: 8 },\n { traits: ['playful', 'bouncy', 'expressive'], level: 'comfortable', gap: 5 },\n { traits: ['minimal', 'clean', 'airy'], level: 'spacious', gap: 6 },\n { traits: ['professional', 'modern', 'balanced'], level: 'comfortable', gap: 4 },\n];\n\nconst MIN_GAP = 2;\nconst MAX_GAP = 10;\n\nfunction clamp(value: number, min: number, max: number): number {\n return Math.max(min, Math.min(max, value));\n}\n\nexport function computeDensity(\n personality: string[],\n recipeSpatial?: RecipeSpatialHints,\n): Density {\n const lower = personality.map(c => c.toLowerCase());\n\n let level: DensityLevel = 'comfortable';\n let gap = 4;\n\n for (const rule of RULES) {\n if (rule.traits.some(t => lower.includes(t))) {\n level = rule.level;\n gap = rule.gap;\n break;\n }\n }\n\n if (recipeSpatial?.density_bias) {\n gap += recipeSpatial.density_bias;\n }\n if (recipeSpatial?.content_gap_shift) {\n gap += recipeSpatial.content_gap_shift;\n }\n\n gap = clamp(gap, MIN_GAP, MAX_GAP);\n\n if (gap <= 3) level = 'compact';\n else if (gap >= 6) level = 'spacious';\n else level = 'comfortable';\n\n return { level, content_gap: String(gap) };\n}\n","import type { EssenceFile, StructurePage, LayoutItem } from './types.js';\nimport { isSimple, isSectioned } from './types.js';\n\nexport interface GuardViolation {\n rule: 'style' | 'structure' | 'layout' | 'recipe' | 'density' | 'theme-mode' | 'pattern-exists' | 'accessibility';\n severity: 'error' | 'warning';\n message: string;\n suggestion?: string;\n}\n\nexport interface GuardContext {\n pageId?: string;\n style?: string;\n recipe?: string;\n layout?: string[];\n density_gap?: string;\n themeRegistry?: Map<string, { modes: string[] }>;\n patternRegistry?: Map<string, unknown>;\n a11y_issues?: string[];\n}\n\n/**\n * Check if the theme supports the specified mode.\n */\nfunction checkThemeModeCompatibility(\n essence: EssenceFile,\n context: GuardContext\n): GuardViolation | null {\n if (!context.themeRegistry) return null;\n\n const themeId = isSimple(essence) ? essence.theme?.style : null;\n const mode = isSimple(essence) ? essence.theme?.mode : null;\n\n if (!themeId || !mode) return null;\n\n const theme = context.themeRegistry.get(themeId);\n if (!theme) {\n // Theme not found - separate validation\n return null;\n }\n\n if (!theme.modes.includes(mode) && mode !== 'auto') {\n const supportedModes = theme.modes.join(', ');\n const suggestion = theme.modes.length > 0\n ? `Use mode: \"${theme.modes[0]}\" instead, or choose a theme that supports ${mode} mode.`\n : undefined;\n\n return {\n rule: 'theme-mode',\n severity: 'error',\n message: `Theme \"${themeId}\" does not support \"${mode}\" mode. Supported modes: ${supportedModes}.`,\n suggestion,\n };\n }\n\n return null;\n}\n\n/**\n * Collect all pattern IDs from a layout item, recursively handling nested structures.\n */\nfunction collectPatternIds(item: LayoutItem, ids: Set<string>): void {\n if (typeof item === 'string') {\n ids.add(item);\n return;\n }\n if (typeof item === 'object' && item !== null) {\n if ('pattern' in item && typeof item.pattern === 'string') {\n ids.add(item.pattern);\n }\n if ('cols' in item && Array.isArray(item.cols)) {\n for (const col of item.cols) {\n // cols contains strings (pattern IDs)\n if (typeof col === 'string') {\n ids.add(col);\n }\n }\n }\n }\n}\n\n/**\n * Find similar pattern IDs in the registry for suggestion.\n */\nfunction findSimilarPatterns(target: string, registry: Map<string, unknown>): string[] {\n const similar: string[] = [];\n const targetLower = target.toLowerCase();\n\n for (const id of registry.keys()) {\n const idLower = id.toLowerCase();\n // Simple similarity: contains target or target contains id\n if (idLower.includes(targetLower) || targetLower.includes(idLower)) {\n similar.push(id);\n }\n }\n\n return similar.slice(0, 3); // Top 3 suggestions\n}\n\n/**\n * Check if all referenced patterns exist in the registry.\n */\nfunction checkPatternExistence(\n essence: EssenceFile,\n context: GuardContext\n): GuardViolation[] {\n if (!context.patternRegistry) return [];\n\n const violations: GuardViolation[] = [];\n const referencedPatterns = new Set<string>();\n\n // Collect all pattern references from structure\n const pages = getAllPages(essence);\n for (const page of pages) {\n for (const item of page.layout || []) {\n collectPatternIds(item, referencedPatterns);\n }\n }\n\n // Check each pattern exists\n for (const patternId of referencedPatterns) {\n if (!context.patternRegistry.has(patternId)) {\n // Find similar patterns for suggestion\n const similar = findSimilarPatterns(patternId, context.patternRegistry);\n const suggestion = similar.length > 0\n ? `Similar patterns: ${similar.join(', ')}`\n : `Run \"decantr search ${patternId}\" to find alternatives.`;\n\n violations.push({\n rule: 'pattern-exists',\n severity: 'error',\n message: `Pattern \"${patternId}\" is referenced but does not exist in the registry.`,\n suggestion,\n });\n }\n }\n\n return violations;\n}\n\n/**\n * Check accessibility compliance based on declared WCAG level.\n */\nfunction checkAccessibility(\n essence: EssenceFile,\n context: GuardContext\n): GuardViolation | null {\n const accessibility = 'accessibility' in essence ? essence.accessibility : undefined;\n\n if (!accessibility?.wcag_level || accessibility.wcag_level === 'none') {\n return null;\n }\n\n if (context.a11y_issues && context.a11y_issues.length > 0) {\n const issueList = context.a11y_issues.slice(0, 3).join(', ');\n const moreCount = context.a11y_issues.length > 3 ? ` (+${context.a11y_issues.length - 3} more)` : '';\n\n return {\n rule: 'accessibility',\n severity: 'error',\n message: `WCAG ${accessibility.wcag_level} compliance required. Issues found: ${issueList}${moreCount}`,\n suggestion: 'Fix accessibility issues before proceeding. Run an accessibility audit for details.',\n };\n }\n\n return null;\n}\n\nexport function evaluateGuard(essence: EssenceFile, context: GuardContext = {}): GuardViolation[] {\n const guard = essence.guard;\n\n if (guard.mode === 'creative') {\n return [];\n }\n\n const violations: GuardViolation[] = [];\n const isStrict = guard.mode === 'strict';\n\n // Rule 1: Style guard\n if (context.style) {\n const essenceStyle = isSimple(essence) ? essence.theme.style : null;\n if (essenceStyle && context.style !== essenceStyle && guard.enforce_style !== false) {\n violations.push({\n rule: 'style',\n severity: 'error',\n message: `Style \"${context.style}\" does not match essence theme \"${essenceStyle}\". Change the essence theme first.`,\n });\n }\n }\n\n // Rule 2: Structure guard (enforced in both guided and strict)\n if (context.pageId) {\n const pages = getAllPages(essence);\n const pageExists = pages.some(p => p.id === context.pageId);\n if (!pageExists) {\n violations.push({\n rule: 'structure',\n severity: 'error',\n message: `Page \"${context.pageId}\" does not exist in essence structure. Add it to the essence first.`,\n });\n }\n }\n\n // Rule 3: Layout guard (strict only)\n if (isStrict && context.pageId && context.layout) {\n const pages = getAllPages(essence);\n const page = pages.find(p => p.id === context.pageId);\n if (page) {\n const essenceLayout = page.layout.map(item =>\n typeof item === 'string' ? item : 'pattern' in item ? item.pattern : 'cols'\n );\n const proposedLayout = context.layout;\n const matches = essenceLayout.length === proposedLayout.length &&\n essenceLayout.every((item, i) => item === proposedLayout[i]);\n if (!matches) {\n violations.push({\n rule: 'layout',\n severity: 'error',\n message: `Layout for page \"${context.pageId}\" deviates from essence. Expected: [${essenceLayout.join(', ')}].`,\n });\n }\n }\n }\n\n // Rule 4: Recipe guard\n if (context.recipe && guard.enforce_recipe !== false) {\n const essenceRecipe = isSimple(essence) ? essence.theme.recipe : null;\n if (essenceRecipe && context.recipe !== essenceRecipe) {\n violations.push({\n rule: 'recipe',\n severity: 'error',\n message: `Recipe \"${context.recipe}\" does not match essence recipe \"${essenceRecipe}\".`,\n });\n }\n }\n\n // Rule 5: Density guard (strict only)\n if (isStrict && context.density_gap) {\n if (context.density_gap !== essence.density.content_gap) {\n violations.push({\n rule: 'density',\n severity: 'warning',\n message: `Content gap \"${context.density_gap}\" does not match essence density \"${essence.density.content_gap}\".`,\n });\n }\n }\n\n // Rule 6: Theme/mode compatibility (always checked when registry available)\n const themeModeViolation = checkThemeModeCompatibility(essence, context);\n if (themeModeViolation) {\n violations.push(themeModeViolation);\n }\n\n // Rule 7: Pattern existence (always checked when registry available)\n const patternViolations = checkPatternExistence(essence, context);\n violations.push(...patternViolations);\n\n // Rule 8: Accessibility (when wcag_level is set, guided and strict modes)\n const accessibilityViolation = checkAccessibility(essence, context);\n if (accessibilityViolation) {\n violations.push(accessibilityViolation);\n }\n\n return violations;\n}\n\nfunction getAllPages(essence: EssenceFile): StructurePage[] {\n if (isSimple(essence)) return essence.structure;\n if (isSectioned(essence)) return essence.sections.flatMap(s => s.structure);\n return [];\n}\n","import type { Essence, SectionedEssence, EssenceFile, StructurePage, GuardMode } from './types.js';\n\nexport function normalizeEssence(input: Record<string, unknown>): EssenceFile {\n // Detect v2: has 'theme' and 'platform' (normalized terms)\n if (input.theme && input.platform) {\n return input as unknown as EssenceFile;\n }\n\n if (input.sections) {\n return normalizeSectioned(input);\n }\n\n return normalizeSimple(input);\n}\n\nfunction normalizeSimple(v1: Record<string, unknown>): Essence {\n // v1 field names (wine terms) → v2 normalized names:\n // vintage → theme, vessel → platform, clarity → density,\n // cork → guard, terroir/vignette → archetype, tannins → features,\n // carafe → shell, blend → layout, character → personality\n const vintage = v1.vintage as Record<string, string> | undefined;\n const vessel = v1.vessel as Record<string, string> | undefined;\n const clarity = v1.clarity as Record<string, string> | undefined;\n const cork = v1.cork as Record<string, unknown> | undefined;\n const structure = v1.structure as Array<Record<string, unknown>> | undefined;\n\n return {\n version: '2.0.0',\n archetype: (v1.terroir ?? v1.vignette ?? v1.archetype) as string,\n theme: {\n style: vintage?.style ?? '',\n mode: (vintage?.mode ?? 'dark') as 'light' | 'dark' | 'auto',\n recipe: vintage?.recipe ?? '',\n ...(vintage?.shape ? { shape: vintage.shape as 'sharp' | 'rounded' | 'pill' } : {}),\n },\n // Accept both old 'character' and new 'personality' field names\n personality: (v1.character ?? v1.personality) as string[],\n platform: {\n type: (vessel?.type ?? 'spa') as 'spa' | 'ssr' | 'static',\n routing: (vessel?.routing ?? 'hash') as 'hash' | 'history',\n },\n structure: (structure ?? []).map(normalizeStructurePage),\n features: (v1.tannins ?? v1.features ?? []) as string[],\n density: {\n level: (clarity?.density ?? 'comfortable') as 'compact' | 'comfortable' | 'spacious',\n content_gap: stripGapPrefix(clarity?.content_gap ?? '4'),\n },\n guard: normalizeGuard(cork),\n target: (v1.target as string) ?? 'decantr',\n ...(v1._impression ? { _impression: v1._impression as Essence['_impression'] } : {}),\n };\n}\n\nfunction normalizeSectioned(v1: Record<string, unknown>): SectionedEssence {\n // v1 field names (wine terms) → v2 normalized names:\n // vessel → platform, clarity → density, cork → guard,\n // terroir/vignette → archetype, tannins → features,\n // shared_tannins → shared_features, character → personality\n const vessel = v1.vessel as Record<string, string> | undefined;\n const clarity = v1.clarity as Record<string, string> | undefined;\n const cork = v1.cork as Record<string, unknown> | undefined;\n const sections = v1.sections as Array<Record<string, unknown>>;\n\n return {\n version: '2.0.0',\n platform: {\n type: (vessel?.type ?? 'spa') as 'spa' | 'ssr' | 'static',\n routing: (vessel?.routing ?? 'hash') as 'hash' | 'history',\n },\n // Accept both old 'character' and new 'personality' field names\n personality: (v1.character ?? v1.personality) as string[],\n sections: sections.map(section => ({\n id: section.id as string,\n path: section.path as string,\n archetype: (section.terroir ?? section.vignette ?? section.archetype) as string,\n theme: {\n style: (section.vintage as Record<string, string>)?.style ?? '',\n mode: ((section.vintage as Record<string, string>)?.mode ?? 'dark') as 'light' | 'dark' | 'auto',\n recipe: (section.vintage as Record<string, string>)?.recipe ?? '',\n },\n structure: ((section.structure as Array<Record<string, unknown>>) ?? []).map(normalizeStructurePage),\n ...(section.tannins ? { features: section.tannins as string[] } : {}),\n })),\n ...(v1.shared_tannins ? { shared_features: v1.shared_tannins as string[] } : {}),\n density: {\n level: (clarity?.density ?? 'comfortable') as 'compact' | 'comfortable' | 'spacious',\n content_gap: stripGapPrefix(clarity?.content_gap ?? '4'),\n },\n guard: normalizeGuard(cork),\n target: (v1.target as string) ?? 'decantr',\n };\n}\n\nfunction normalizeStructurePage(page: Record<string, unknown>): StructurePage {\n // Accept v1 wine terms (carafe → shell, blend → layout) for backward compatibility\n return {\n id: page.id as string,\n shell: (page.carafe ?? page.shell) as string,\n layout: (page.blend ?? page.layout) as StructurePage['layout'],\n ...(page.surface ? { surface: page.surface as string } : {}),\n };\n}\n\n// Accept v1 'cork' object and normalize to v2 'guard'\nfunction normalizeGuard(cork: Record<string, unknown> | undefined): Essence['guard'] {\n if (!cork) return { mode: 'creative' };\n\n const modeMap: Record<string, GuardMode> = {\n creative: 'creative',\n maintenance: 'strict',\n strict: 'strict',\n guided: 'guided',\n };\n\n return {\n ...(cork.enforce_style !== undefined ? { enforce_style: cork.enforce_style as boolean } : {}),\n ...(cork.enforce_recipe !== undefined ? { enforce_recipe: cork.enforce_recipe as boolean } : {}),\n mode: modeMap[(cork.mode as string) ?? 'creative'] ?? 'creative',\n };\n}\n\nfunction stripGapPrefix(gap: string): string {\n const match = gap.match(/^_gap(\\d+)$/);\n return match ? match[1] : gap;\n}\n"],"mappings":";AA8KO,SAAS,YAAY,SAAmD;AAC7E,SAAO,cAAc,WAAW,MAAM,QAAS,QAA6B,QAAQ;AACtF;AAEO,SAAS,SAAS,SAA0C;AACjE,SAAO,eAAe,WAAW,EAAE,cAAc;AACnD;;;ACpLA,OAAO,SAAS;AAChB,SAAS,oBAAoB;AAC7B,SAAS,qBAAqB;AAC9B,SAAS,SAAS,YAAY;AAE9B,IAAM,YAAY,QAAQ,cAAc,YAAY,GAAG,CAAC;AACxD,IAAM,aAAa,KAAK,WAAW,MAAM,UAAU,iBAAiB;AAEpE,IAAI,YAAwB;AAC5B,IAAI,iBAAoD;AAExD,SAAS,eAAe;AACtB,MAAI,eAAgB,QAAO;AAC3B,QAAM,SAAS,KAAK,MAAM,aAAa,YAAY,OAAO,CAAC;AAC3D,cAAY,IAAI,IAAI,EAAE,WAAW,MAAM,QAAQ,OAAO,gBAAgB,MAAM,CAAC;AAC7E,mBAAiB,UAAU,QAAQ,MAAM;AACzC,SAAO;AACT;AAOO,SAAS,gBAAgB,MAAiC;AAC/D,QAAM,WAAW,aAAa;AAC9B,QAAM,QAAQ,SAAS,IAAI;AAE3B,MAAI,OAAO;AACT,WAAO,EAAE,OAAO,MAAM,QAAQ,CAAC,EAAE;AAAA,EACnC;AAEA,QAAM,UAAU,SAAS,UAAU,CAAC,GAAG,IAAI,CAAC,QAAQ;AAClD,UAAM,OAAO,IAAI,gBAAgB;AACjC,UAAM,MAAM,IAAI,WAAW;AAC3B,WAAO,GAAG,IAAI,KAAK,GAAG;AAAA,EACxB,CAAC;AAED,SAAO,EAAE,OAAO,OAAO,OAAO;AAChC;;;AC1BA,IAAM,QAAuB;AAAA,EAC3B,EAAE,QAAQ,CAAC,YAAY,SAAS,cAAc,aAAa,GAAG,OAAO,WAAW,KAAK,EAAE;AAAA,EACvF,EAAE,QAAQ,CAAC,aAAa,WAAW,WAAW,GAAG,OAAO,YAAY,KAAK,EAAE;AAAA,EAC3E,EAAE,QAAQ,CAAC,WAAW,UAAU,YAAY,GAAG,OAAO,eAAe,KAAK,EAAE;AAAA,EAC5E,EAAE,QAAQ,CAAC,WAAW,SAAS,MAAM,GAAG,OAAO,YAAY,KAAK,EAAE;AAAA,EAClE,EAAE,QAAQ,CAAC,gBAAgB,UAAU,UAAU,GAAG,OAAO,eAAe,KAAK,EAAE;AACjF;AAEA,IAAM,UAAU;AAChB,IAAM,UAAU;AAEhB,SAAS,MAAM,OAAe,KAAa,KAAqB;AAC9D,SAAO,KAAK,IAAI,KAAK,KAAK,IAAI,KAAK,KAAK,CAAC;AAC3C;AAEO,SAAS,eACd,aACA,eACS;AACT,QAAM,QAAQ,YAAY,IAAI,OAAK,EAAE,YAAY,CAAC;AAElD,MAAI,QAAsB;AAC1B,MAAI,MAAM;AAEV,aAAW,QAAQ,OAAO;AACxB,QAAI,KAAK,OAAO,KAAK,OAAK,MAAM,SAAS,CAAC,CAAC,GAAG;AAC5C,cAAQ,KAAK;AACb,YAAM,KAAK;AACX;AAAA,IACF;AAAA,EACF;AAEA,MAAI,eAAe,cAAc;AAC/B,WAAO,cAAc;AAAA,EACvB;AACA,MAAI,eAAe,mBAAmB;AACpC,WAAO,cAAc;AAAA,EACvB;AAEA,QAAM,MAAM,KAAK,SAAS,OAAO;AAEjC,MAAI,OAAO,EAAG,SAAQ;AAAA,WACb,OAAO,EAAG,SAAQ;AAAA,MACtB,SAAQ;AAEb,SAAO,EAAE,OAAO,aAAa,OAAO,GAAG,EAAE;AAC3C;;;ACnCA,SAAS,4BACP,SACA,SACuB;AACvB,MAAI,CAAC,QAAQ,cAAe,QAAO;AAEnC,QAAM,UAAU,SAAS,OAAO,IAAI,QAAQ,OAAO,QAAQ;AAC3D,QAAM,OAAO,SAAS,OAAO,IAAI,QAAQ,OAAO,OAAO;AAEvD,MAAI,CAAC,WAAW,CAAC,KAAM,QAAO;AAE9B,QAAM,QAAQ,QAAQ,cAAc,IAAI,OAAO;AAC/C,MAAI,CAAC,OAAO;AAEV,WAAO;AAAA,EACT;AAEA,MAAI,CAAC,MAAM,MAAM,SAAS,IAAI,KAAK,SAAS,QAAQ;AAClD,UAAM,iBAAiB,MAAM,MAAM,KAAK,IAAI;AAC5C,UAAM,aAAa,MAAM,MAAM,SAAS,IACpC,cAAc,MAAM,MAAM,CAAC,CAAC,8CAA8C,IAAI,WAC9E;AAEJ,WAAO;AAAA,MACL,MAAM;AAAA,MACN,UAAU;AAAA,MACV,SAAS,UAAU,OAAO,uBAAuB,IAAI,4BAA4B,cAAc;AAAA,MAC/F;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AACT;AAKA,SAAS,kBAAkB,MAAkB,KAAwB;AACnE,MAAI,OAAO,SAAS,UAAU;AAC5B,QAAI,IAAI,IAAI;AACZ;AAAA,EACF;AACA,MAAI,OAAO,SAAS,YAAY,SAAS,MAAM;AAC7C,QAAI,aAAa,QAAQ,OAAO,KAAK,YAAY,UAAU;AACzD,UAAI,IAAI,KAAK,OAAO;AAAA,IACtB;AACA,QAAI,UAAU,QAAQ,MAAM,QAAQ,KAAK,IAAI,GAAG;AAC9C,iBAAW,OAAO,KAAK,MAAM;AAE3B,YAAI,OAAO,QAAQ,UAAU;AAC3B,cAAI,IAAI,GAAG;AAAA,QACb;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;AAKA,SAAS,oBAAoB,QAAgB,UAA0C;AACrF,QAAM,UAAoB,CAAC;AAC3B,QAAM,cAAc,OAAO,YAAY;AAEvC,aAAW,MAAM,SAAS,KAAK,GAAG;AAChC,UAAM,UAAU,GAAG,YAAY;AAE/B,QAAI,QAAQ,SAAS,WAAW,KAAK,YAAY,SAAS,OAAO,GAAG;AAClE,cAAQ,KAAK,EAAE;AAAA,IACjB;AAAA,EACF;AAEA,SAAO,QAAQ,MAAM,GAAG,CAAC;AAC3B;AAKA,SAAS,sBACP,SACA,SACkB;AAClB,MAAI,CAAC,QAAQ,gBAAiB,QAAO,CAAC;AAEtC,QAAM,aAA+B,CAAC;AACtC,QAAM,qBAAqB,oBAAI,IAAY;AAG3C,QAAM,QAAQ,YAAY,OAAO;AACjC,aAAW,QAAQ,OAAO;AACxB,eAAW,QAAQ,KAAK,UAAU,CAAC,GAAG;AACpC,wBAAkB,MAAM,kBAAkB;AAAA,IAC5C;AAAA,EACF;AAGA,aAAW,aAAa,oBAAoB;AAC1C,QAAI,CAAC,QAAQ,gBAAgB,IAAI,SAAS,GAAG;AAE3C,YAAM,UAAU,oBAAoB,WAAW,QAAQ,eAAe;AACtE,YAAM,aAAa,QAAQ,SAAS,IAChC,qBAAqB,QAAQ,KAAK,IAAI,CAAC,KACvC,uBAAuB,SAAS;AAEpC,iBAAW,KAAK;AAAA,QACd,MAAM;AAAA,QACN,UAAU;AAAA,QACV,SAAS,YAAY,SAAS;AAAA,QAC9B;AAAA,MACF,CAAC;AAAA,IACH;AAAA,EACF;AAEA,SAAO;AACT;AAKA,SAAS,mBACP,SACA,SACuB;AACvB,QAAM,gBAAgB,mBAAmB,UAAU,QAAQ,gBAAgB;AAE3E,MAAI,CAAC,eAAe,cAAc,cAAc,eAAe,QAAQ;AACrE,WAAO;AAAA,EACT;AAEA,MAAI,QAAQ,eAAe,QAAQ,YAAY,SAAS,GAAG;AACzD,UAAM,YAAY,QAAQ,YAAY,MAAM,GAAG,CAAC,EAAE,KAAK,IAAI;AAC3D,UAAM,YAAY,QAAQ,YAAY,SAAS,IAAI,MAAM,QAAQ,YAAY,SAAS,CAAC,WAAW;AAElG,WAAO;AAAA,MACL,MAAM;AAAA,MACN,UAAU;AAAA,MACV,SAAS,QAAQ,cAAc,UAAU,uCAAuC,SAAS,GAAG,SAAS;AAAA,MACrG,YAAY;AAAA,IACd;AAAA,EACF;AAEA,SAAO;AACT;AAEO,SAAS,cAAc,SAAsB,UAAwB,CAAC,GAAqB;AAChG,QAAM,QAAQ,QAAQ;AAEtB,MAAI,MAAM,SAAS,YAAY;AAC7B,WAAO,CAAC;AAAA,EACV;AAEA,QAAM,aAA+B,CAAC;AACtC,QAAM,WAAW,MAAM,SAAS;AAGhC,MAAI,QAAQ,OAAO;AACjB,UAAM,eAAe,SAAS,OAAO,IAAI,QAAQ,MAAM,QAAQ;AAC/D,QAAI,gBAAgB,QAAQ,UAAU,gBAAgB,MAAM,kBAAkB,OAAO;AACnF,iBAAW,KAAK;AAAA,QACd,MAAM;AAAA,QACN,UAAU;AAAA,QACV,SAAS,UAAU,QAAQ,KAAK,mCAAmC,YAAY;AAAA,MACjF,CAAC;AAAA,IACH;AAAA,EACF;AAGA,MAAI,QAAQ,QAAQ;AAClB,UAAM,QAAQ,YAAY,OAAO;AACjC,UAAM,aAAa,MAAM,KAAK,OAAK,EAAE,OAAO,QAAQ,MAAM;AAC1D,QAAI,CAAC,YAAY;AACf,iBAAW,KAAK;AAAA,QACd,MAAM;AAAA,QACN,UAAU;AAAA,QACV,SAAS,SAAS,QAAQ,MAAM;AAAA,MAClC,CAAC;AAAA,IACH;AAAA,EACF;AAGA,MAAI,YAAY,QAAQ,UAAU,QAAQ,QAAQ;AAChD,UAAM,QAAQ,YAAY,OAAO;AACjC,UAAM,OAAO,MAAM,KAAK,OAAK,EAAE,OAAO,QAAQ,MAAM;AACpD,QAAI,MAAM;AACR,YAAM,gBAAgB,KAAK,OAAO;AAAA,QAAI,UACpC,OAAO,SAAS,WAAW,OAAO,aAAa,OAAO,KAAK,UAAU;AAAA,MACvE;AACA,YAAM,iBAAiB,QAAQ;AAC/B,YAAM,UAAU,cAAc,WAAW,eAAe,UACtD,cAAc,MAAM,CAAC,MAAM,MAAM,SAAS,eAAe,CAAC,CAAC;AAC7D,UAAI,CAAC,SAAS;AACZ,mBAAW,KAAK;AAAA,UACd,MAAM;AAAA,UACN,UAAU;AAAA,UACV,SAAS,oBAAoB,QAAQ,MAAM,uCAAuC,cAAc,KAAK,IAAI,CAAC;AAAA,QAC5G,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AAGA,MAAI,QAAQ,UAAU,MAAM,mBAAmB,OAAO;AACpD,UAAM,gBAAgB,SAAS,OAAO,IAAI,QAAQ,MAAM,SAAS;AACjE,QAAI,iBAAiB,QAAQ,WAAW,eAAe;AACrD,iBAAW,KAAK;AAAA,QACd,MAAM;AAAA,QACN,UAAU;AAAA,QACV,SAAS,WAAW,QAAQ,MAAM,oCAAoC,aAAa;AAAA,MACrF,CAAC;AAAA,IACH;AAAA,EACF;AAGA,MAAI,YAAY,QAAQ,aAAa;AACnC,QAAI,QAAQ,gBAAgB,QAAQ,QAAQ,aAAa;AACvD,iBAAW,KAAK;AAAA,QACd,MAAM;AAAA,QACN,UAAU;AAAA,QACV,SAAS,gBAAgB,QAAQ,WAAW,qCAAqC,QAAQ,QAAQ,WAAW;AAAA,MAC9G,CAAC;AAAA,IACH;AAAA,EACF;AAGA,QAAM,qBAAqB,4BAA4B,SAAS,OAAO;AACvE,MAAI,oBAAoB;AACtB,eAAW,KAAK,kBAAkB;AAAA,EACpC;AAGA,QAAM,oBAAoB,sBAAsB,SAAS,OAAO;AAChE,aAAW,KAAK,GAAG,iBAAiB;AAGpC,QAAM,yBAAyB,mBAAmB,SAAS,OAAO;AAClE,MAAI,wBAAwB;AAC1B,eAAW,KAAK,sBAAsB;AAAA,EACxC;AAEA,SAAO;AACT;AAEA,SAAS,YAAY,SAAuC;AAC1D,MAAI,SAAS,OAAO,EAAG,QAAO,QAAQ;AACtC,MAAI,YAAY,OAAO,EAAG,QAAO,QAAQ,SAAS,QAAQ,OAAK,EAAE,SAAS;AAC1E,SAAO,CAAC;AACV;;;AC5QO,SAAS,iBAAiB,OAA6C;AAE5E,MAAI,MAAM,SAAS,MAAM,UAAU;AACjC,WAAO;AAAA,EACT;AAEA,MAAI,MAAM,UAAU;AAClB,WAAO,mBAAmB,KAAK;AAAA,EACjC;AAEA,SAAO,gBAAgB,KAAK;AAC9B;AAEA,SAAS,gBAAgB,IAAsC;AAK7D,QAAM,UAAU,GAAG;AACnB,QAAM,SAAS,GAAG;AAClB,QAAM,UAAU,GAAG;AACnB,QAAM,OAAO,GAAG;AAChB,QAAM,YAAY,GAAG;AAErB,SAAO;AAAA,IACL,SAAS;AAAA,IACT,WAAY,GAAG,WAAW,GAAG,YAAY,GAAG;AAAA,IAC5C,OAAO;AAAA,MACL,OAAO,SAAS,SAAS;AAAA,MACzB,MAAO,SAAS,QAAQ;AAAA,MACxB,QAAQ,SAAS,UAAU;AAAA,MAC3B,GAAI,SAAS,QAAQ,EAAE,OAAO,QAAQ,MAAsC,IAAI,CAAC;AAAA,IACnF;AAAA;AAAA,IAEA,aAAc,GAAG,aAAa,GAAG;AAAA,IACjC,UAAU;AAAA,MACR,MAAO,QAAQ,QAAQ;AAAA,MACvB,SAAU,QAAQ,WAAW;AAAA,IAC/B;AAAA,IACA,YAAY,aAAa,CAAC,GAAG,IAAI,sBAAsB;AAAA,IACvD,UAAW,GAAG,WAAW,GAAG,YAAY,CAAC;AAAA,IACzC,SAAS;AAAA,MACP,OAAQ,SAAS,WAAW;AAAA,MAC5B,aAAa,eAAe,SAAS,eAAe,GAAG;AAAA,IACzD;AAAA,IACA,OAAO,eAAe,IAAI;AAAA,IAC1B,QAAS,GAAG,UAAqB;AAAA,IACjC,GAAI,GAAG,cAAc,EAAE,aAAa,GAAG,YAAsC,IAAI,CAAC;AAAA,EACpF;AACF;AAEA,SAAS,mBAAmB,IAA+C;AAKzE,QAAM,SAAS,GAAG;AAClB,QAAM,UAAU,GAAG;AACnB,QAAM,OAAO,GAAG;AAChB,QAAM,WAAW,GAAG;AAEpB,SAAO;AAAA,IACL,SAAS;AAAA,IACT,UAAU;AAAA,MACR,MAAO,QAAQ,QAAQ;AAAA,MACvB,SAAU,QAAQ,WAAW;AAAA,IAC/B;AAAA;AAAA,IAEA,aAAc,GAAG,aAAa,GAAG;AAAA,IACjC,UAAU,SAAS,IAAI,cAAY;AAAA,MACjC,IAAI,QAAQ;AAAA,MACZ,MAAM,QAAQ;AAAA,MACd,WAAY,QAAQ,WAAW,QAAQ,YAAY,QAAQ;AAAA,MAC3D,OAAO;AAAA,QACL,OAAQ,QAAQ,SAAoC,SAAS;AAAA,QAC7D,MAAQ,QAAQ,SAAoC,QAAQ;AAAA,QAC5D,QAAS,QAAQ,SAAoC,UAAU;AAAA,MACjE;AAAA,MACA,YAAa,QAAQ,aAAgD,CAAC,GAAG,IAAI,sBAAsB;AAAA,MACnG,GAAI,QAAQ,UAAU,EAAE,UAAU,QAAQ,QAAoB,IAAI,CAAC;AAAA,IACrE,EAAE;AAAA,IACF,GAAI,GAAG,iBAAiB,EAAE,iBAAiB,GAAG,eAA2B,IAAI,CAAC;AAAA,IAC9E,SAAS;AAAA,MACP,OAAQ,SAAS,WAAW;AAAA,MAC5B,aAAa,eAAe,SAAS,eAAe,GAAG;AAAA,IACzD;AAAA,IACA,OAAO,eAAe,IAAI;AAAA,IAC1B,QAAS,GAAG,UAAqB;AAAA,EACnC;AACF;AAEA,SAAS,uBAAuB,MAA8C;AAE5E,SAAO;AAAA,IACL,IAAI,KAAK;AAAA,IACT,OAAQ,KAAK,UAAU,KAAK;AAAA,IAC5B,QAAS,KAAK,SAAS,KAAK;AAAA,IAC5B,GAAI,KAAK,UAAU,EAAE,SAAS,KAAK,QAAkB,IAAI,CAAC;AAAA,EAC5D;AACF;AAGA,SAAS,eAAe,MAA6D;AACnF,MAAI,CAAC,KAAM,QAAO,EAAE,MAAM,WAAW;AAErC,QAAM,UAAqC;AAAA,IACzC,UAAU;AAAA,IACV,aAAa;AAAA,IACb,QAAQ;AAAA,IACR,QAAQ;AAAA,EACV;AAEA,SAAO;AAAA,IACL,GAAI,KAAK,kBAAkB,SAAY,EAAE,eAAe,KAAK,cAAyB,IAAI,CAAC;AAAA,IAC3F,GAAI,KAAK,mBAAmB,SAAY,EAAE,gBAAgB,KAAK,eAA0B,IAAI,CAAC;AAAA,IAC9F,MAAM,QAAS,KAAK,QAAmB,UAAU,KAAK;AAAA,EACxD;AACF;AAEA,SAAS,eAAe,KAAqB;AAC3C,QAAM,QAAQ,IAAI,MAAM,aAAa;AACrC,SAAO,QAAQ,MAAM,CAAC,IAAI;AAC5B;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/types.ts","../src/validate.ts","../src/density.ts","../src/guard.ts","../src/normalize.ts","../src/migrate.ts"],"sourcesContent":["/**\n * Essence v2 schema types — normalized terminology.\n *\n * Terminology mapping (v1 wine terms → v2 normalized):\n * terroir → archetype, vintage → theme, tannins → features,\n * carafe → shell, cork → guard, blend → layout, clarity → density,\n * vessel → platform, character → personality,\n * decantation process → essence pipeline\n */\n\n// --- Theme ---\n\nexport type ThemeStyle =\n | 'auradecantism'\n | 'clean'\n | 'glassmorphism'\n | 'retro'\n | 'bioluminescent'\n | 'launchpad'\n | 'dopamine'\n | 'editorial'\n | 'liquid-glass'\n | 'prismatic';\n\nexport type ThemeMode = 'light' | 'dark' | 'auto';\nexport type ThemeShape = 'sharp' | 'rounded' | 'pill';\n\nexport interface Theme {\n style: ThemeStyle | string;\n mode: ThemeMode;\n recipe: string;\n shape?: ThemeShape;\n}\n\n// --- Platform ---\n\nexport type PlatformType = 'spa' | 'ssr' | 'static';\nexport type RoutingStrategy = 'hash' | 'history';\n\nexport interface Platform {\n type: PlatformType;\n routing: RoutingStrategy;\n}\n\n// --- Structure ---\n\nexport type ShellType =\n | 'sidebar-main'\n | 'top-nav-main'\n | 'full-bleed'\n | 'dashboard'\n | 'stacked'\n | string;\n\nexport type LayoutItem = string | PatternRef | ColumnLayout;\n\nexport interface PatternRef {\n pattern: string;\n preset?: string;\n as?: string;\n}\n\nexport interface ColumnLayout {\n cols: string[];\n at?: 'sm' | 'md' | 'lg' | 'xl' | '2xl';\n span?: Record<string, number>;\n // AUTO: Multi-breakpoint grid — array of { at, cols } entries for cascading breakpoints\n breakpoints?: { at: string; cols: number }[];\n // AUTO: \"container\" uses container queries instead of viewport breakpoints\n responsive?: 'viewport' | 'container';\n}\n\nexport interface StructurePage {\n id: string;\n shell: ShellType;\n layout: LayoutItem[];\n surface?: string;\n}\n\n// --- Density ---\n\nexport type DensityLevel = 'compact' | 'comfortable' | 'spacious';\n\nexport interface Density {\n level: DensityLevel;\n content_gap: string;\n}\n\n// --- Guard ---\n\nexport type GuardMode = 'creative' | 'guided' | 'strict';\n\nexport interface Guard {\n enforce_style?: boolean;\n enforce_recipe?: boolean;\n mode: GuardMode;\n}\n\n// --- Impression ---\n\nexport interface Impression {\n vibe: string[];\n references: string[];\n density_intent: DensityLevel | string;\n layout_intent: ShellType | string;\n novel_elements: string[];\n}\n\n// --- Generator Target ---\n\nexport type GeneratorTarget = 'decantr' | 'react' | 'vue' | 'svelte' | string;\n\n// --- Accessibility ---\n\nexport type WcagLevel = 'none' | 'A' | 'AA' | 'AAA';\n\nexport type CvdPreference =\n | 'none'\n | 'auto'\n | 'deuteranopia'\n | 'protanopia'\n | 'tritanopia'\n | 'achromatopsia';\n\nexport interface Accessibility {\n wcag_level?: WcagLevel;\n cvd_preference?: CvdPreference;\n}\n\n// --- Essence (simple) ---\n\nexport interface Essence {\n $schema?: string;\n version: string;\n archetype: string;\n theme: Theme;\n personality: string[];\n platform: Platform;\n structure: StructurePage[];\n features: string[];\n density: Density;\n guard: Guard;\n target: GeneratorTarget;\n accessibility?: Accessibility;\n _impression?: Impression;\n}\n\n// --- Essence (sectioned) ---\n\nexport interface EssenceSection {\n id: string;\n path: string;\n archetype: string;\n theme: Theme;\n structure: StructurePage[];\n features?: string[];\n}\n\nexport interface SectionedEssence {\n $schema?: string;\n version: string;\n platform: Platform;\n personality: string[];\n sections: EssenceSection[];\n shared_features?: string[];\n density: Density;\n guard: Guard;\n target: GeneratorTarget;\n accessibility?: Accessibility;\n _impression?: Impression;\n}\n\n// --- Essence v3: DNA/Blueprint/Meta split ---\n\nexport interface EssenceDNA {\n theme: Theme;\n spacing: {\n base_unit: number;\n scale: string;\n density: DensityLevel;\n content_gap: string;\n };\n typography: {\n scale: string;\n heading_weight: number;\n body_weight: number;\n };\n color: {\n palette: string;\n accent_count: number;\n cvd_preference: CvdPreference | string;\n };\n radius: {\n philosophy: ThemeShape | string;\n base: number;\n };\n elevation: {\n system: string;\n max_levels: number;\n };\n motion: {\n preference: string;\n duration_scale: number;\n reduce_motion: boolean;\n timing?: string;\n durations?: Record<string, string>;\n };\n accessibility: {\n wcag_level: WcagLevel;\n focus_visible: boolean;\n skip_nav: boolean;\n };\n personality: string[];\n constraints?: {\n mode?: string;\n typography?: string;\n borders?: string;\n corners?: string;\n shadows?: string;\n effects?: Record<string, string>;\n };\n}\n\n/** Only density and mode may be overridden per-page. DNA axioms like wcag_level and theme.style are never overrideable. */\nexport interface DNAOverrides {\n density?: DensityLevel;\n mode?: ThemeMode;\n}\n\nexport interface BlueprintPage {\n id: string;\n route?: string;\n shell_override?: ShellType | null;\n layout: LayoutItem[];\n patterns?: PatternRef[];\n dna_overrides?: DNAOverrides;\n surface?: string;\n}\n\nexport type ArchetypeRole = 'primary' | 'gateway' | 'public' | 'auxiliary';\n\nexport interface EssenceV31Section {\n id: string;\n role: ArchetypeRole;\n shell: ShellType | string;\n features: string[];\n description: string;\n pages: BlueprintPage[];\n}\n\nexport interface RouteEntry {\n section: string;\n page: string;\n}\n\nexport interface EssenceBlueprint {\n shell?: ShellType | string;\n sections?: EssenceV31Section[];\n pages?: BlueprintPage[];\n features: string[];\n routes?: Record<string, RouteEntry>;\n icon_style?: string;\n avatar_style?: string;\n}\n\nexport interface EssenceV3Guard {\n mode: GuardMode;\n dna_enforcement: 'error' | 'warn' | 'off';\n blueprint_enforcement: 'warn' | 'off';\n}\n\nexport interface EssenceMeta {\n archetype: string;\n target: GeneratorTarget;\n platform: Platform;\n guard: EssenceV3Guard;\n seo?: {\n schema_org?: string[];\n meta_priorities?: string[];\n };\n navigation?: {\n hotkeys?: Array<{ key: string; route?: string; action?: string; label: string }>;\n command_palette?: boolean;\n };\n}\n\nexport interface EssenceV3 {\n $schema?: string;\n version: '3.0.0' | '3.1.0';\n dna: EssenceDNA;\n blueprint: EssenceBlueprint;\n meta: EssenceMeta;\n _impression?: Impression;\n}\n\n// --- Discriminated union ---\n\nexport type EssenceFile = Essence | SectionedEssence | EssenceV3;\n\nexport function isV3(essence: EssenceFile): essence is EssenceV3 {\n return (essence.version === '3.0.0' || essence.version === '3.1.0') && 'dna' in essence && 'blueprint' in essence;\n}\n\n/** Flatten sections into a flat page list (for guards and backward compat). */\nexport function flattenPages(blueprint: EssenceBlueprint): BlueprintPage[] {\n if (blueprint.sections && blueprint.sections.length > 0) {\n return blueprint.sections.flatMap(s =>\n s.pages.map(p => ({\n ...p,\n shell_override: p.shell_override ?? (s.shell !== blueprint.shell ? (s.shell as ShellType) : undefined),\n }))\n );\n }\n return blueprint.pages ?? [];\n}\n\nexport function isSectioned(essence: EssenceFile): essence is SectionedEssence {\n if (isV3(essence)) return false;\n return 'sections' in essence && Array.isArray((essence as SectionedEssence).sections);\n}\n\nexport function isSimple(essence: EssenceFile): essence is Essence {\n if (isV3(essence)) return false;\n return 'archetype' in essence && !('sections' in essence);\n}\n","import Ajv from 'ajv';\nimport { readFileSync, existsSync } from 'node:fs';\nimport { fileURLToPath } from 'node:url';\nimport { dirname, join } from 'node:path';\n\nconst __dirname = dirname(fileURLToPath(import.meta.url));\nconst v2SchemaPath = join(__dirname, '..', 'schema', 'essence.v2.json');\nconst v3SchemaPath = join(__dirname, '..', 'schema', 'essence.v3.json');\n\nlet cachedAjv: Ajv | null = null;\nconst validatorCache = new Map<string, ReturnType<Ajv['compile']>>();\n\nfunction getAjv(): Ajv {\n if (!cachedAjv) {\n cachedAjv = new Ajv({ allErrors: true, strict: false, validateSchema: false });\n }\n return cachedAjv;\n}\n\nfunction getValidator(version: 'v2' | 'v3'): ReturnType<Ajv['compile']> | null {\n if (validatorCache.has(version)) return validatorCache.get(version)!;\n\n const schemaPath = version === 'v3' ? v3SchemaPath : v2SchemaPath;\n if (!existsSync(schemaPath)) return null;\n\n const schema = JSON.parse(readFileSync(schemaPath, 'utf-8'));\n const ajv = getAjv();\n const validate = ajv.compile(schema);\n validatorCache.set(version, validate);\n return validate;\n}\n\nfunction detectVersion(data: unknown): 'v2' | 'v3' {\n if (typeof data === 'object' && data !== null && 'version' in data) {\n if ((data as Record<string, unknown>).version === '3.0.0') return 'v3';\n }\n return 'v2';\n}\n\nexport interface ValidationResult {\n valid: boolean;\n errors: string[];\n}\n\nexport function validateEssence(data: unknown): ValidationResult {\n const version = detectVersion(data);\n const validate = getValidator(version);\n\n if (!validate) {\n return { valid: false, errors: [`No schema available for ${version} documents`] };\n }\n\n const valid = validate(data);\n\n if (valid) {\n return { valid: true, errors: [] };\n }\n\n const errors = (validate.errors ?? []).map((err) => {\n const path = err.instancePath || '/';\n const msg = err.message ?? 'unknown error';\n return `${path}: ${msg}`;\n });\n\n return { valid: false, errors };\n}\n","import type { Density, DensityLevel } from './types.js';\n\ninterface RecipeSpatialHints {\n density_bias?: number;\n content_gap_shift?: number;\n}\n\ninterface DensityRule {\n traits: string[];\n level: DensityLevel;\n gap: number;\n}\n\nconst RULES: DensityRule[] = [\n { traits: ['tactical', 'dense', 'data-dense', 'utilitarian'], level: 'compact', gap: 3 },\n { traits: ['luxurious', 'premium', 'editorial'], level: 'spacious', gap: 8 },\n { traits: ['playful', 'bouncy', 'expressive'], level: 'comfortable', gap: 5 },\n { traits: ['minimal', 'clean', 'airy'], level: 'spacious', gap: 6 },\n { traits: ['professional', 'modern', 'balanced'], level: 'comfortable', gap: 4 },\n];\n\nconst MIN_GAP = 2;\nconst MAX_GAP = 10;\n\nfunction clamp(value: number, min: number, max: number): number {\n return Math.max(min, Math.min(max, value));\n}\n\nexport function computeDensity(\n personality: string[],\n recipeSpatial?: RecipeSpatialHints,\n): Density {\n const lower = personality.map(c => c.toLowerCase());\n\n let level: DensityLevel = 'comfortable';\n let gap = 4;\n\n for (const rule of RULES) {\n if (rule.traits.some(t => lower.includes(t))) {\n level = rule.level;\n gap = rule.gap;\n break;\n }\n }\n\n if (recipeSpatial?.density_bias) {\n gap += recipeSpatial.density_bias;\n }\n if (recipeSpatial?.content_gap_shift) {\n gap += recipeSpatial.content_gap_shift;\n }\n\n gap = clamp(gap, MIN_GAP, MAX_GAP);\n\n if (gap <= 3) level = 'compact';\n else if (gap >= 6) level = 'spacious';\n else level = 'comfortable';\n\n return { level, content_gap: String(gap) };\n}\n","import type { EssenceFile, EssenceV3, EssenceV3Guard, StructurePage, LayoutItem, DensityLevel } from './types.js';\nimport { isSimple, isSectioned, isV3, flattenPages } from './types.js';\n\nexport interface AutoFix {\n type: 'add_page' | 'update_layout' | 'update_blueprint';\n patch: Record<string, unknown>;\n}\n\nexport interface GuardViolation {\n rule: 'style' | 'structure' | 'layout' | 'recipe' | 'density' | 'theme-mode' | 'pattern-exists' | 'accessibility';\n severity: 'error' | 'warning';\n message: string;\n suggestion?: string;\n layer?: 'dna' | 'blueprint';\n autoFixable?: boolean;\n autoFix?: AutoFix;\n}\n\nexport interface GuardContext {\n pageId?: string;\n style?: string;\n recipe?: string;\n layout?: string[];\n density_gap?: string;\n themeRegistry?: Map<string, { modes: string[] }>;\n patternRegistry?: Map<string, unknown>;\n a11y_issues?: string[];\n}\n\n/**\n * Check if the theme supports the specified mode.\n */\nfunction checkThemeModeCompatibility(\n essence: EssenceFile,\n context: GuardContext\n): GuardViolation | null {\n if (!context.themeRegistry) return null;\n\n let themeId: string | null;\n let mode: string | null;\n if (isV3(essence)) {\n themeId = essence.dna.theme?.style ?? null;\n mode = essence.dna.theme?.mode ?? null;\n } else {\n themeId = isSimple(essence) ? essence.theme?.style : null;\n mode = isSimple(essence) ? essence.theme?.mode : null;\n }\n\n if (!themeId || !mode) return null;\n\n const theme = context.themeRegistry.get(themeId);\n if (!theme) {\n // Theme not found - separate validation\n return null;\n }\n\n if (!theme.modes.includes(mode) && mode !== 'auto') {\n const supportedModes = theme.modes.join(', ');\n const suggestion = theme.modes.length > 0\n ? `Use mode: \"${theme.modes[0]}\" instead, or choose a theme that supports ${mode} mode.`\n : undefined;\n\n return {\n rule: 'theme-mode',\n severity: 'error',\n message: `Theme \"${themeId}\" does not support \"${mode}\" mode. Supported modes: ${supportedModes}.`,\n suggestion,\n ...(isV3(essence) ? { layer: 'dna' as const, autoFixable: false } : {}),\n };\n }\n\n return null;\n}\n\n/**\n * Collect all pattern IDs from a layout item, recursively handling nested structures.\n */\nfunction collectPatternIds(item: LayoutItem, ids: Set<string>): void {\n if (typeof item === 'string') {\n ids.add(item);\n return;\n }\n if (typeof item === 'object' && item !== null) {\n if ('pattern' in item && typeof item.pattern === 'string') {\n ids.add(item.pattern);\n }\n if ('cols' in item && Array.isArray(item.cols)) {\n for (const col of item.cols) {\n // cols contains strings (pattern IDs)\n if (typeof col === 'string') {\n ids.add(col);\n }\n }\n }\n }\n}\n\n/**\n * Find similar pattern IDs in the registry for suggestion.\n */\nfunction findSimilarPatterns(target: string, registry: Map<string, unknown>): string[] {\n const similar: string[] = [];\n const targetLower = target.toLowerCase();\n\n for (const id of registry.keys()) {\n const idLower = id.toLowerCase();\n // Simple similarity: contains target or target contains id\n if (idLower.includes(targetLower) || targetLower.includes(idLower)) {\n similar.push(id);\n }\n }\n\n return similar.slice(0, 3); // Top 3 suggestions\n}\n\n/**\n * Check if all referenced patterns exist in the registry.\n */\nfunction checkPatternExistence(\n essence: EssenceFile,\n context: GuardContext\n): GuardViolation[] {\n if (!context.patternRegistry) return [];\n\n const violations: GuardViolation[] = [];\n const referencedPatterns = new Set<string>();\n\n // Collect all pattern references from structure\n const pages = getAllPages(essence);\n for (const page of pages) {\n for (const item of page.layout || []) {\n collectPatternIds(item, referencedPatterns);\n }\n }\n\n // Check each pattern exists\n for (const patternId of referencedPatterns) {\n if (!context.patternRegistry.has(patternId)) {\n // Find similar patterns for suggestion\n const similar = findSimilarPatterns(patternId, context.patternRegistry);\n const suggestion = similar.length > 0\n ? `Similar patterns: ${similar.join(', ')}`\n : `Run \"decantr search ${patternId}\" to find alternatives.`;\n\n violations.push({\n rule: 'pattern-exists',\n severity: isV3(essence) ? 'warning' : 'error',\n message: `Pattern \"${patternId}\" is referenced but does not exist in the registry.`,\n suggestion,\n ...(isV3(essence) ? { layer: 'blueprint' as const, autoFixable: false } : {}),\n });\n }\n }\n\n return violations;\n}\n\n/**\n * Check accessibility compliance based on declared WCAG level.\n */\nfunction checkAccessibility(\n essence: EssenceFile,\n context: GuardContext\n): GuardViolation | null {\n const accessibility = isV3(essence) ? essence.dna.accessibility : ('accessibility' in essence ? essence.accessibility : undefined);\n\n if (!accessibility?.wcag_level || accessibility.wcag_level === 'none') {\n return null;\n }\n\n if (context.a11y_issues && context.a11y_issues.length > 0) {\n const issueList = context.a11y_issues.slice(0, 3).join(', ');\n const moreCount = context.a11y_issues.length > 3 ? ` (+${context.a11y_issues.length - 3} more)` : '';\n\n return {\n rule: 'accessibility',\n severity: 'error',\n message: `WCAG ${accessibility.wcag_level} compliance required. Issues found: ${issueList}${moreCount}`,\n suggestion: 'Fix accessibility issues before proceeding. Run an accessibility audit for details.',\n ...(isV3(essence) ? { layer: 'dna' as const, autoFixable: false } : {}),\n };\n }\n\n return null;\n}\n\nexport function evaluateGuard(essence: EssenceFile, context: GuardContext = {}): GuardViolation[] {\n const guard = isV3(essence) ? essence.meta.guard : essence.guard;\n\n if (guard.mode === 'creative') {\n return [];\n }\n\n const violations: GuardViolation[] = [];\n const isStrict = guard.mode === 'strict';\n\n // Rule 1: Style guard\n if (context.style) {\n let essenceStyle: string | null;\n if (isV3(essence)) {\n essenceStyle = essence.dna.theme.style;\n } else {\n essenceStyle = isSimple(essence) ? essence.theme.style : null;\n }\n const enforceStyle = isV3(essence) ? true : (guard as import('./types.js').Guard).enforce_style !== false;\n if (essenceStyle && context.style !== essenceStyle && enforceStyle) {\n violations.push({\n rule: 'style',\n severity: 'error',\n message: `Style \"${context.style}\" does not match essence theme \"${essenceStyle}\". Change the essence theme first.`,\n ...(isV3(essence) ? { layer: 'dna' as const, autoFixable: false } : {}),\n });\n }\n }\n\n // Rule 2: Structure guard (enforced in both guided and strict)\n if (context.pageId) {\n const pages = getAllPages(essence);\n const pageExists = pages.some(p => p.id === context.pageId);\n if (!pageExists) {\n violations.push({\n rule: 'structure',\n severity: isV3(essence) ? 'warning' : 'error',\n message: `Page \"${context.pageId}\" does not exist in essence structure. Add it to the essence first.`,\n ...(isV3(essence) ? {\n layer: 'blueprint' as const,\n autoFixable: true,\n autoFix: { type: 'add_page' as const, patch: { id: context.pageId } },\n } : {}),\n });\n }\n }\n\n // Rule 3: Layout guard (strict only)\n if (isStrict && context.pageId && context.layout) {\n const pages = getAllPages(essence);\n const page = pages.find(p => p.id === context.pageId);\n if (page) {\n const essenceLayout = page.layout.map(item =>\n typeof item === 'string' ? item : 'pattern' in item ? item.pattern : 'cols'\n );\n const proposedLayout = context.layout;\n const matches = essenceLayout.length === proposedLayout.length &&\n essenceLayout.every((item, i) => item === proposedLayout[i]);\n if (!matches) {\n violations.push({\n rule: 'layout',\n severity: isV3(essence) ? 'warning' : 'error',\n message: `Layout for page \"${context.pageId}\" deviates from essence. Expected: [${essenceLayout.join(', ')}].`,\n ...(isV3(essence) ? {\n layer: 'blueprint' as const,\n autoFixable: true,\n autoFix: { type: 'update_layout' as const, patch: { page: context.pageId, layout: context.layout } },\n } : {}),\n });\n }\n }\n }\n\n // Rule 4: Recipe guard\n if (context.recipe) {\n let essenceRecipe: string | null;\n let enforceRecipe: boolean;\n if (isV3(essence)) {\n essenceRecipe = essence.dna.theme.recipe;\n enforceRecipe = true;\n } else {\n essenceRecipe = isSimple(essence) ? essence.theme.recipe : null;\n enforceRecipe = (guard as import('./types.js').Guard).enforce_recipe !== false;\n }\n if (essenceRecipe && context.recipe !== essenceRecipe && enforceRecipe) {\n violations.push({\n rule: 'recipe',\n severity: 'error',\n message: `Recipe \"${context.recipe}\" does not match essence recipe \"${essenceRecipe}\".`,\n ...(isV3(essence) ? { layer: 'dna' as const, autoFixable: false } : {}),\n });\n }\n }\n\n // Rule 5: Density guard (strict only)\n if (isStrict && context.density_gap) {\n let expectedGap: string;\n if (isV3(essence)) {\n // Check per-page dna_overrides for density if a pageId is provided\n const overriddenDensity = getPageDensityOverride(essence, context.pageId);\n expectedGap = overriddenDensity ?? essence.dna.spacing.content_gap;\n } else {\n expectedGap = essence.density.content_gap;\n }\n if (context.density_gap !== expectedGap) {\n violations.push({\n rule: 'density',\n severity: 'warning',\n message: `Content gap \"${context.density_gap}\" does not match essence density \"${expectedGap}\".`,\n ...(isV3(essence) ? { layer: 'dna' as const, autoFixable: false } : {}),\n });\n }\n }\n\n // Rule 6: Theme/mode compatibility (always checked when registry available)\n const themeModeViolation = checkThemeModeCompatibility(essence, context);\n if (themeModeViolation) {\n violations.push(themeModeViolation);\n }\n\n // Rule 7: Pattern existence (always checked when registry available)\n const patternViolations = checkPatternExistence(essence, context);\n violations.push(...patternViolations);\n\n // Rule 8: Accessibility (when wcag_level is set, guided and strict modes)\n const accessibilityViolation = checkAccessibility(essence, context);\n if (accessibilityViolation) {\n violations.push(accessibilityViolation);\n }\n\n // Apply v3 enforcement level filtering\n if (isV3(essence)) {\n const v3Guard = guard as EssenceV3Guard;\n\n // DNA enforcement\n if (v3Guard.dna_enforcement === 'off') {\n // Remove all DNA-layer violations\n return violations.filter(v => v.layer !== 'dna');\n }\n if (v3Guard.dna_enforcement === 'warn') {\n // Downgrade DNA-layer violations to warning severity\n for (const v of violations) {\n if (v.layer === 'dna') {\n v.severity = 'warning';\n }\n }\n }\n\n // Blueprint enforcement\n if (v3Guard.blueprint_enforcement === 'off') {\n // Remove all blueprint-layer violations\n return violations.filter(v => v.layer !== 'blueprint');\n }\n }\n\n return violations;\n}\n\nfunction getAllPages(essence: EssenceFile): StructurePage[] {\n if (isV3(essence)) {\n // Map v3 BlueprintPages to StructurePage shape for guard evaluation\n const pages = flattenPages(essence.blueprint);\n return pages.map(page => ({\n id: page.id,\n shell: page.shell_override ?? essence.blueprint.shell ?? '',\n layout: page.layout,\n ...(page.surface ? { surface: page.surface } : {}),\n }));\n }\n if (isSimple(essence)) return essence.structure;\n if (isSectioned(essence)) return essence.sections.flatMap(s => s.structure);\n throw new Error('Unknown EssenceFile type');\n}\n\n/** Get per-page density override from v3 blueprint, if set. */\nfunction getPageDensityOverride(essence: EssenceV3, pageId?: string): string | undefined {\n if (!pageId) return undefined;\n const pages = flattenPages(essence.blueprint);\n const page = pages.find(p => p.id === pageId);\n if (!page?.dna_overrides?.density) return undefined;\n // Map density level to a content_gap value\n const densityGapMap: Record<DensityLevel, string> = {\n compact: '2',\n comfortable: '4',\n spacious: '6',\n };\n return densityGapMap[page.dna_overrides.density] ?? undefined;\n}\n","import type { Essence, SectionedEssence, EssenceFile, EssenceV3, StructurePage, GuardMode } from './types.js';\n\nexport function normalizeEssence(input: Record<string, unknown>): EssenceFile {\n // v3: version-based detection only (no structural fallback to avoid false positives)\n if (input.version === '3.0.0') {\n return input as unknown as EssenceV3;\n }\n\n // Detect v2: has 'theme' and 'platform' (normalized terms)\n if (input.theme && input.platform) {\n return input as unknown as EssenceFile;\n }\n\n if (input.sections) {\n return normalizeSectioned(input);\n }\n\n return normalizeSimple(input);\n}\n\nfunction normalizeSimple(v1: Record<string, unknown>): Essence {\n // v1 field names (wine terms) → v2 normalized names:\n // vintage → theme, vessel → platform, clarity → density,\n // cork → guard, terroir/vignette → archetype, tannins → features,\n // carafe → shell, blend → layout, character → personality\n const vintage = v1.vintage as Record<string, string> | undefined;\n const vessel = v1.vessel as Record<string, string> | undefined;\n const clarity = v1.clarity as Record<string, string> | undefined;\n const cork = v1.cork as Record<string, unknown> | undefined;\n const structure = v1.structure as Array<Record<string, unknown>> | undefined;\n\n return {\n version: '2.0.0',\n archetype: (v1.terroir ?? v1.vignette ?? v1.archetype) as string,\n theme: {\n style: vintage?.style ?? '',\n mode: (vintage?.mode ?? 'dark') as 'light' | 'dark' | 'auto',\n recipe: vintage?.recipe ?? '',\n ...(vintage?.shape ? { shape: vintage.shape as 'sharp' | 'rounded' | 'pill' } : {}),\n },\n // Accept both old 'character' and new 'personality' field names\n personality: (v1.character ?? v1.personality) as string[],\n platform: {\n type: (vessel?.type ?? 'spa') as 'spa' | 'ssr' | 'static',\n routing: (vessel?.routing ?? 'hash') as 'hash' | 'history',\n },\n structure: (structure ?? []).map(normalizeStructurePage),\n features: (v1.tannins ?? v1.features ?? []) as string[],\n density: {\n level: (clarity?.density ?? 'comfortable') as 'compact' | 'comfortable' | 'spacious',\n content_gap: stripGapPrefix(clarity?.content_gap ?? '4'),\n },\n guard: normalizeGuard(cork),\n target: (v1.target as string) ?? 'decantr',\n ...(v1._impression ? { _impression: v1._impression as Essence['_impression'] } : {}),\n };\n}\n\nfunction normalizeSectioned(v1: Record<string, unknown>): SectionedEssence {\n // v1 field names (wine terms) → v2 normalized names:\n // vessel → platform, clarity → density, cork → guard,\n // terroir/vignette → archetype, tannins → features,\n // shared_tannins → shared_features, character → personality\n const vessel = v1.vessel as Record<string, string> | undefined;\n const clarity = v1.clarity as Record<string, string> | undefined;\n const cork = v1.cork as Record<string, unknown> | undefined;\n const sections = v1.sections as Array<Record<string, unknown>>;\n\n return {\n version: '2.0.0',\n platform: {\n type: (vessel?.type ?? 'spa') as 'spa' | 'ssr' | 'static',\n routing: (vessel?.routing ?? 'hash') as 'hash' | 'history',\n },\n // Accept both old 'character' and new 'personality' field names\n personality: (v1.character ?? v1.personality) as string[],\n sections: sections.map(section => ({\n id: section.id as string,\n path: section.path as string,\n archetype: (section.terroir ?? section.vignette ?? section.archetype) as string,\n theme: {\n style: (section.vintage as Record<string, string>)?.style ?? '',\n mode: ((section.vintage as Record<string, string>)?.mode ?? 'dark') as 'light' | 'dark' | 'auto',\n recipe: (section.vintage as Record<string, string>)?.recipe ?? '',\n },\n structure: ((section.structure as Array<Record<string, unknown>>) ?? []).map(normalizeStructurePage),\n ...(section.tannins ? { features: section.tannins as string[] } : {}),\n })),\n ...(v1.shared_tannins ? { shared_features: v1.shared_tannins as string[] } : {}),\n density: {\n level: (clarity?.density ?? 'comfortable') as 'compact' | 'comfortable' | 'spacious',\n content_gap: stripGapPrefix(clarity?.content_gap ?? '4'),\n },\n guard: normalizeGuard(cork),\n target: (v1.target as string) ?? 'decantr',\n };\n}\n\nfunction normalizeStructurePage(page: Record<string, unknown>): StructurePage {\n // Accept v1 wine terms (carafe → shell, blend → layout) for backward compatibility\n return {\n id: page.id as string,\n shell: (page.carafe ?? page.shell) as string,\n layout: (page.blend ?? page.layout) as StructurePage['layout'],\n ...(page.surface ? { surface: page.surface as string } : {}),\n };\n}\n\n// Accept v1 'cork' object and normalize to v2 'guard'\nfunction normalizeGuard(cork: Record<string, unknown> | undefined): Essence['guard'] {\n if (!cork) return { mode: 'creative' };\n\n const modeMap: Record<string, GuardMode> = {\n creative: 'creative',\n maintenance: 'strict',\n strict: 'strict',\n guided: 'guided',\n };\n\n return {\n ...(cork.enforce_style !== undefined ? { enforce_style: cork.enforce_style as boolean } : {}),\n ...(cork.enforce_recipe !== undefined ? { enforce_recipe: cork.enforce_recipe as boolean } : {}),\n mode: modeMap[(cork.mode as string) ?? 'creative'] ?? 'creative',\n };\n}\n\nfunction stripGapPrefix(gap: string): string {\n const match = gap.match(/^_gap(\\d+)$/);\n return match ? match[1] : gap;\n}\n","import type {\n Essence,\n SectionedEssence,\n EssenceV3,\n EssenceFile,\n EssenceDNA,\n EssenceBlueprint,\n EssenceMeta,\n EssenceV31Section,\n ThemeShape,\n DensityLevel,\n GuardMode,\n} from './types.js';\nimport { isSimple, isSectioned, isV3 } from './types.js';\n\n/**\n * Migrate a v2 EssenceFile to v3 format (DNA/Blueprint/Meta split).\n * If already v3, returns unchanged. SectionedEssence uses first section's theme.\n */\nexport function migrateV2ToV3(essence: EssenceFile): EssenceV3 {\n if (isV3(essence)) return essence;\n\n if (isSectioned(essence)) {\n return migrateSectionedToV3(essence);\n }\n\n if (isSimple(essence)) {\n return migrateSimpleToV3(essence);\n }\n\n throw new Error('Unknown EssenceFile type — cannot migrate');\n}\n\nfunction migrateSimpleToV3(essence: Essence): EssenceV3 {\n const dna = buildDNA(essence);\n const blueprint = buildBlueprintFromSimple(essence);\n const meta = buildMeta(essence);\n\n return {\n version: '3.0.0',\n dna,\n blueprint,\n meta,\n ...(essence._impression ? { _impression: essence._impression } : {}),\n };\n}\n\nfunction migrateSectionedToV3(essence: SectionedEssence): EssenceV3 {\n // Use first section's theme as the DNA theme (sectioned essences have per-section themes)\n const firstSection = essence.sections[0];\n const syntheticSimple: Partial<Essence> = {\n theme: firstSection.theme,\n density: essence.density,\n guard: essence.guard,\n accessibility: essence.accessibility,\n };\n\n const dna = buildDNA(syntheticSimple as Essence);\n // Override personality from the sectioned root\n dna.personality = essence.personality;\n\n // Flatten all sections' pages into the blueprint\n const pages = essence.sections.flatMap(section =>\n section.structure.map(page => ({\n id: page.id,\n shell_override: page.shell as string | null,\n layout: page.layout,\n ...(page.surface ? { surface: page.surface } : {}),\n }))\n );\n\n const allFeatures = [\n ...(essence.shared_features ?? []),\n ...essence.sections.flatMap(s => s.features ?? []),\n ];\n\n const blueprint: EssenceBlueprint = {\n shell: firstSection.structure[0]?.shell ?? 'top-nav-main',\n pages,\n features: [...new Set(allFeatures)],\n };\n\n const meta: EssenceMeta = {\n archetype: firstSection.archetype,\n target: essence.target,\n platform: essence.platform,\n guard: migrateGuard(essence.guard.mode),\n };\n\n return {\n version: '3.0.0',\n dna,\n blueprint,\n meta,\n ...(essence._impression ? { _impression: essence._impression } : {}),\n };\n}\n\nfunction buildDNA(essence: Essence): EssenceDNA {\n const shape = essence.theme.shape ?? 'rounded';\n const radiusBase = inferRadiusBase(shape);\n\n return {\n theme: {\n style: essence.theme.style,\n mode: essence.theme.mode,\n recipe: essence.theme.recipe,\n ...(essence.theme.shape ? { shape: essence.theme.shape } : {}),\n },\n spacing: {\n base_unit: 4,\n scale: 'linear',\n density: (essence.density?.level ?? 'comfortable') as DensityLevel,\n content_gap: essence.density?.content_gap ?? '4',\n },\n typography: {\n scale: 'modular',\n heading_weight: 600,\n body_weight: 400,\n },\n color: {\n palette: 'semantic',\n accent_count: 1,\n cvd_preference: essence.accessibility?.cvd_preference ?? 'auto',\n },\n radius: {\n philosophy: shape,\n base: radiusBase,\n },\n elevation: {\n system: 'layered',\n max_levels: 3,\n },\n motion: {\n preference: 'subtle',\n duration_scale: 1.0,\n reduce_motion: true,\n },\n accessibility: {\n wcag_level: essence.accessibility?.wcag_level ?? 'AA',\n focus_visible: true,\n skip_nav: true,\n },\n personality: essence.personality ?? ['professional'],\n };\n}\n\nfunction buildBlueprintFromSimple(essence: Essence): EssenceBlueprint {\n const defaultShell = essence.structure[0]?.shell ?? 'top-nav-main';\n\n return {\n shell: defaultShell,\n pages: essence.structure.map(page => ({\n id: page.id,\n ...(page.shell !== defaultShell ? { shell_override: page.shell } : {}),\n layout: page.layout,\n ...(page.surface ? { surface: page.surface } : {}),\n })),\n features: essence.features ?? [],\n };\n}\n\nfunction buildMeta(essence: Essence): EssenceMeta {\n return {\n archetype: essence.archetype,\n target: essence.target,\n platform: essence.platform,\n guard: migrateGuard(essence.guard.mode),\n };\n}\n\nfunction migrateGuard(mode: GuardMode): EssenceMeta['guard'] {\n switch (mode) {\n case 'strict':\n return { mode: 'strict', dna_enforcement: 'error', blueprint_enforcement: 'warn' };\n case 'guided':\n return { mode: 'guided', dna_enforcement: 'error', blueprint_enforcement: 'off' };\n case 'creative':\n default:\n return { mode: 'creative', dna_enforcement: 'off', blueprint_enforcement: 'off' };\n }\n}\n\nfunction inferRadiusBase(shape: ThemeShape | string): number {\n switch (shape) {\n case 'pill': return 12;\n case 'rounded': return 8;\n case 'sharp': return 2;\n default: return 8;\n }\n}\n\n/**\n * Migrate a v3.0 flat-page EssenceV3 to v3.1 sectioned format.\n * If already v3.1 or has blueprint.sections, returns unchanged.\n */\nexport function migrateV30ToV31(essence: EssenceV3): EssenceV3 {\n if (essence.version === '3.1.0' || essence.blueprint.sections) {\n return essence;\n }\n\n const section: EssenceV31Section = {\n id: essence.meta.archetype,\n role: 'primary',\n shell: essence.blueprint.shell ?? 'top-nav-main',\n features: essence.blueprint.features,\n description: '',\n pages: essence.blueprint.pages ?? [],\n };\n\n return {\n ...essence,\n version: '3.1.0',\n blueprint: {\n ...essence.blueprint,\n sections: [section],\n pages: undefined,\n routes: {},\n },\n };\n}\n"],"mappings":"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","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@decantr/essence-spec",
|
|
3
|
-
"version": "1.0.0-beta.
|
|
3
|
+
"version": "1.0.0-beta.6",
|
|
4
4
|
"description": "Essence schema, validator, and TypeScript types for Decantr",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -21,16 +21,19 @@
|
|
|
21
21
|
"import": "./schema/essence.v2.json"
|
|
22
22
|
}
|
|
23
23
|
},
|
|
24
|
-
"files": [
|
|
24
|
+
"files": [
|
|
25
|
+
"dist",
|
|
26
|
+
"schema"
|
|
27
|
+
],
|
|
25
28
|
"publishConfig": {
|
|
26
29
|
"access": "public"
|
|
27
30
|
},
|
|
31
|
+
"dependencies": {
|
|
32
|
+
"ajv": "^8.17.0"
|
|
33
|
+
},
|
|
28
34
|
"scripts": {
|
|
29
35
|
"build": "tsup",
|
|
30
36
|
"test": "vitest run",
|
|
31
37
|
"test:watch": "vitest"
|
|
32
|
-
},
|
|
33
|
-
"dependencies": {
|
|
34
|
-
"ajv": "^8.17.0"
|
|
35
38
|
}
|
|
36
|
-
}
|
|
39
|
+
}
|
package/schema/essence.v2.json
CHANGED
|
@@ -101,7 +101,9 @@
|
|
|
101
101
|
"properties": {
|
|
102
102
|
"cols": { "type": "array", "items": { "type": "string" }, "minItems": 2 },
|
|
103
103
|
"at": { "type": "string", "enum": ["sm", "md", "lg", "xl", "2xl"] },
|
|
104
|
-
"span": { "type": "object", "additionalProperties": { "type": "number", "minimum": 1 } }
|
|
104
|
+
"span": { "type": "object", "additionalProperties": { "type": "number", "minimum": 1 } },
|
|
105
|
+
"breakpoints": { "type": "array", "items": { "type": "object", "required": ["at", "cols"], "properties": { "at": { "type": "string" }, "cols": { "type": "number" } } } },
|
|
106
|
+
"responsive": { "type": "string", "enum": ["viewport", "container"] }
|
|
105
107
|
}
|
|
106
108
|
},
|
|
107
109
|
"Density": {
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://decantr.ai/schema/essence/3.0.json",
|
|
4
|
+
"title": "Decantr Essence v3",
|
|
5
|
+
"description": "Design intent specification with DNA/Blueprint/Meta split",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"required": ["version", "dna", "blueprint", "meta"],
|
|
8
|
+
"additionalProperties": false,
|
|
9
|
+
"properties": {
|
|
10
|
+
"$schema": { "type": "string" },
|
|
11
|
+
"version": { "type": "string", "const": "3.0.0" },
|
|
12
|
+
"dna": { "$ref": "#/$defs/DNA" },
|
|
13
|
+
"blueprint": { "$ref": "#/$defs/Blueprint" },
|
|
14
|
+
"meta": { "$ref": "#/$defs/Meta" },
|
|
15
|
+
"_impression": { "$ref": "#/$defs/Impression" }
|
|
16
|
+
},
|
|
17
|
+
"$defs": {
|
|
18
|
+
"DNA": {
|
|
19
|
+
"type": "object",
|
|
20
|
+
"required": ["theme", "spacing", "typography", "color", "radius", "elevation", "motion", "accessibility", "personality"],
|
|
21
|
+
"additionalProperties": false,
|
|
22
|
+
"properties": {
|
|
23
|
+
"theme": { "$ref": "#/$defs/Theme" },
|
|
24
|
+
"spacing": { "$ref": "#/$defs/Spacing" },
|
|
25
|
+
"typography": { "$ref": "#/$defs/Typography" },
|
|
26
|
+
"color": { "$ref": "#/$defs/Color" },
|
|
27
|
+
"radius": { "$ref": "#/$defs/Radius" },
|
|
28
|
+
"elevation": { "$ref": "#/$defs/Elevation" },
|
|
29
|
+
"motion": { "$ref": "#/$defs/Motion" },
|
|
30
|
+
"accessibility": { "$ref": "#/$defs/AccessibilityV3" },
|
|
31
|
+
"personality": { "type": "array", "items": { "type": "string" }, "minItems": 1 }
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
"Theme": {
|
|
35
|
+
"type": "object",
|
|
36
|
+
"required": ["style", "mode", "recipe"],
|
|
37
|
+
"additionalProperties": false,
|
|
38
|
+
"properties": {
|
|
39
|
+
"style": { "type": "string", "minLength": 1 },
|
|
40
|
+
"mode": { "type": "string", "enum": ["light", "dark", "auto"] },
|
|
41
|
+
"recipe": { "type": "string", "minLength": 1 },
|
|
42
|
+
"shape": { "type": "string", "enum": ["sharp", "rounded", "pill"] }
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
"Spacing": {
|
|
46
|
+
"type": "object",
|
|
47
|
+
"required": ["base_unit", "scale", "density", "content_gap"],
|
|
48
|
+
"additionalProperties": false,
|
|
49
|
+
"properties": {
|
|
50
|
+
"base_unit": { "type": "number", "minimum": 1 },
|
|
51
|
+
"scale": { "type": "string", "minLength": 1 },
|
|
52
|
+
"density": { "type": "string", "enum": ["compact", "comfortable", "spacious"] },
|
|
53
|
+
"content_gap": { "type": "string", "minLength": 1 }
|
|
54
|
+
}
|
|
55
|
+
},
|
|
56
|
+
"Typography": {
|
|
57
|
+
"type": "object",
|
|
58
|
+
"required": ["scale", "heading_weight", "body_weight"],
|
|
59
|
+
"additionalProperties": false,
|
|
60
|
+
"properties": {
|
|
61
|
+
"scale": { "type": "string", "minLength": 1 },
|
|
62
|
+
"heading_weight": { "type": "number", "minimum": 100, "maximum": 900 },
|
|
63
|
+
"body_weight": { "type": "number", "minimum": 100, "maximum": 900 }
|
|
64
|
+
}
|
|
65
|
+
},
|
|
66
|
+
"Color": {
|
|
67
|
+
"type": "object",
|
|
68
|
+
"required": ["palette", "accent_count", "cvd_preference"],
|
|
69
|
+
"additionalProperties": false,
|
|
70
|
+
"properties": {
|
|
71
|
+
"palette": { "type": "string", "minLength": 1 },
|
|
72
|
+
"accent_count": { "type": "number", "minimum": 0 },
|
|
73
|
+
"cvd_preference": { "type": "string" }
|
|
74
|
+
}
|
|
75
|
+
},
|
|
76
|
+
"Radius": {
|
|
77
|
+
"type": "object",
|
|
78
|
+
"required": ["philosophy", "base"],
|
|
79
|
+
"additionalProperties": false,
|
|
80
|
+
"properties": {
|
|
81
|
+
"philosophy": { "type": "string", "minLength": 1 },
|
|
82
|
+
"base": { "type": "number", "minimum": 0 }
|
|
83
|
+
}
|
|
84
|
+
},
|
|
85
|
+
"Elevation": {
|
|
86
|
+
"type": "object",
|
|
87
|
+
"required": ["system", "max_levels"],
|
|
88
|
+
"additionalProperties": false,
|
|
89
|
+
"properties": {
|
|
90
|
+
"system": { "type": "string", "minLength": 1 },
|
|
91
|
+
"max_levels": { "type": "number", "minimum": 0 }
|
|
92
|
+
}
|
|
93
|
+
},
|
|
94
|
+
"Motion": {
|
|
95
|
+
"type": "object",
|
|
96
|
+
"required": ["preference", "duration_scale", "reduce_motion"],
|
|
97
|
+
"additionalProperties": false,
|
|
98
|
+
"properties": {
|
|
99
|
+
"preference": { "type": "string", "minLength": 1 },
|
|
100
|
+
"duration_scale": { "type": "number", "minimum": 0 },
|
|
101
|
+
"reduce_motion": { "type": "boolean" }
|
|
102
|
+
}
|
|
103
|
+
},
|
|
104
|
+
"AccessibilityV3": {
|
|
105
|
+
"type": "object",
|
|
106
|
+
"required": ["wcag_level", "focus_visible", "skip_nav"],
|
|
107
|
+
"additionalProperties": false,
|
|
108
|
+
"properties": {
|
|
109
|
+
"wcag_level": { "type": "string", "enum": ["none", "A", "AA", "AAA"] },
|
|
110
|
+
"focus_visible": { "type": "boolean" },
|
|
111
|
+
"skip_nav": { "type": "boolean" }
|
|
112
|
+
}
|
|
113
|
+
},
|
|
114
|
+
"Blueprint": {
|
|
115
|
+
"type": "object",
|
|
116
|
+
"required": ["shell", "pages", "features"],
|
|
117
|
+
"additionalProperties": false,
|
|
118
|
+
"properties": {
|
|
119
|
+
"shell": { "type": "string", "minLength": 1 },
|
|
120
|
+
"pages": { "type": "array", "items": { "$ref": "#/$defs/BlueprintPage" }, "minItems": 1 },
|
|
121
|
+
"features": { "type": "array", "items": { "type": "string" } },
|
|
122
|
+
"icon_style": { "type": "string" },
|
|
123
|
+
"avatar_style": { "type": "string" }
|
|
124
|
+
}
|
|
125
|
+
},
|
|
126
|
+
"BlueprintPage": {
|
|
127
|
+
"type": "object",
|
|
128
|
+
"required": ["id", "layout"],
|
|
129
|
+
"additionalProperties": false,
|
|
130
|
+
"properties": {
|
|
131
|
+
"id": { "type": "string", "pattern": "^[a-z][a-z0-9-]*$" },
|
|
132
|
+
"shell_override": { "oneOf": [{ "type": "string", "minLength": 1 }, { "type": "null" }] },
|
|
133
|
+
"layout": { "type": "array", "items": { "$ref": "#/$defs/LayoutItem" }, "minItems": 1 },
|
|
134
|
+
"dna_overrides": { "$ref": "#/$defs/DNAOverrides" },
|
|
135
|
+
"surface": { "type": "string" }
|
|
136
|
+
}
|
|
137
|
+
},
|
|
138
|
+
"DNAOverrides": {
|
|
139
|
+
"type": "object",
|
|
140
|
+
"additionalProperties": false,
|
|
141
|
+
"properties": {
|
|
142
|
+
"density": { "type": "string", "enum": ["compact", "comfortable", "spacious"] },
|
|
143
|
+
"mode": { "type": "string", "enum": ["light", "dark", "auto"] }
|
|
144
|
+
}
|
|
145
|
+
},
|
|
146
|
+
"LayoutItem": {
|
|
147
|
+
"oneOf": [
|
|
148
|
+
{ "type": "string", "minLength": 1 },
|
|
149
|
+
{ "$ref": "#/$defs/PatternRef" },
|
|
150
|
+
{ "$ref": "#/$defs/ColumnLayout" }
|
|
151
|
+
]
|
|
152
|
+
},
|
|
153
|
+
"PatternRef": {
|
|
154
|
+
"type": "object",
|
|
155
|
+
"required": ["pattern"],
|
|
156
|
+
"additionalProperties": false,
|
|
157
|
+
"properties": {
|
|
158
|
+
"pattern": { "type": "string", "minLength": 1 },
|
|
159
|
+
"preset": { "type": "string" },
|
|
160
|
+
"as": { "type": "string" }
|
|
161
|
+
}
|
|
162
|
+
},
|
|
163
|
+
"ColumnLayout": {
|
|
164
|
+
"type": "object",
|
|
165
|
+
"required": ["cols"],
|
|
166
|
+
"additionalProperties": false,
|
|
167
|
+
"properties": {
|
|
168
|
+
"cols": { "type": "array", "items": { "type": "string" }, "minItems": 2 },
|
|
169
|
+
"at": { "type": "string", "enum": ["sm", "md", "lg", "xl", "2xl"] },
|
|
170
|
+
"span": { "type": "object", "additionalProperties": { "type": "number", "minimum": 1 } },
|
|
171
|
+
"breakpoints": { "type": "array", "items": { "type": "object", "required": ["at", "cols"], "properties": { "at": { "type": "string" }, "cols": { "type": "number" } } } },
|
|
172
|
+
"responsive": { "type": "string", "enum": ["viewport", "container"] }
|
|
173
|
+
}
|
|
174
|
+
},
|
|
175
|
+
"Meta": {
|
|
176
|
+
"type": "object",
|
|
177
|
+
"required": ["archetype", "target", "platform", "guard"],
|
|
178
|
+
"additionalProperties": false,
|
|
179
|
+
"properties": {
|
|
180
|
+
"archetype": { "type": "string", "minLength": 1 },
|
|
181
|
+
"target": { "type": "string", "minLength": 1 },
|
|
182
|
+
"platform": { "$ref": "#/$defs/Platform" },
|
|
183
|
+
"guard": { "$ref": "#/$defs/GuardV3" }
|
|
184
|
+
}
|
|
185
|
+
},
|
|
186
|
+
"Platform": {
|
|
187
|
+
"type": "object",
|
|
188
|
+
"required": ["type", "routing"],
|
|
189
|
+
"additionalProperties": false,
|
|
190
|
+
"properties": {
|
|
191
|
+
"type": { "type": "string", "enum": ["spa", "ssr", "static"] },
|
|
192
|
+
"routing": { "type": "string", "enum": ["hash", "history"] }
|
|
193
|
+
}
|
|
194
|
+
},
|
|
195
|
+
"GuardV3": {
|
|
196
|
+
"type": "object",
|
|
197
|
+
"required": ["mode", "dna_enforcement", "blueprint_enforcement"],
|
|
198
|
+
"additionalProperties": false,
|
|
199
|
+
"properties": {
|
|
200
|
+
"mode": { "type": "string", "enum": ["creative", "guided", "strict"] },
|
|
201
|
+
"dna_enforcement": { "type": "string", "enum": ["error", "warn", "off"] },
|
|
202
|
+
"blueprint_enforcement": { "type": "string", "enum": ["warn", "off"] }
|
|
203
|
+
}
|
|
204
|
+
},
|
|
205
|
+
"Impression": {
|
|
206
|
+
"type": "object",
|
|
207
|
+
"additionalProperties": false,
|
|
208
|
+
"properties": {
|
|
209
|
+
"vibe": { "type": "array", "items": { "type": "string" } },
|
|
210
|
+
"references": { "type": "array", "items": { "type": "string" } },
|
|
211
|
+
"density_intent": { "type": "string" },
|
|
212
|
+
"layout_intent": { "type": "string" },
|
|
213
|
+
"novel_elements": { "type": "array", "items": { "type": "string" } }
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
}
|