@10up/block-renderer-validator 0.1.4

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.
@@ -0,0 +1,61 @@
1
+ import type { BlockTemplate, BlockCatalog, ValidationResult } from '@10up/block-renderer-core';
2
+ /**
3
+ * Result of template validation with path information.
4
+ */
5
+ export interface TemplateValidationResult extends ValidationResult {
6
+ /** Paths to elements with errors (e.g., "[0][2][1]") */
7
+ paths?: string[];
8
+ }
9
+ /**
10
+ * Validates the structural format of a block template using Zod.
11
+ *
12
+ * @param template - The template to validate
13
+ * @returns Validation result with structural errors
14
+ */
15
+ export declare function validateTemplateStructure(template: unknown): TemplateValidationResult;
16
+ /**
17
+ * Validates that all block types in the template exist in the catalog.
18
+ *
19
+ * @param template - The template to validate
20
+ * @param catalog - The block catalog to check against
21
+ * @returns Validation result with unknown block warnings
22
+ */
23
+ export declare function validateTemplateBlockTypes(template: BlockTemplate, catalog: BlockCatalog): TemplateValidationResult;
24
+ /**
25
+ * Validates nesting constraints for a block template.
26
+ *
27
+ * Checks:
28
+ * - `parent` constraints: block must be direct child of specified parent types
29
+ * - `ancestor` constraints: block must have specified ancestor somewhere above
30
+ * - `allowedBlocks` constraints: parent can only contain specified child types
31
+ *
32
+ * @param template - The template to validate
33
+ * @param catalog - The block catalog with nesting rules
34
+ * @returns Validation result with nesting errors
35
+ */
36
+ export declare function validateTemplateNesting(template: BlockTemplate, catalog: BlockCatalog): TemplateValidationResult;
37
+ /**
38
+ * Options for template validation.
39
+ */
40
+ export interface ValidateTemplateOptions {
41
+ /** Block catalog for block type and nesting validation */
42
+ catalog?: BlockCatalog;
43
+ /** Whether to validate block types exist (default: true if catalog provided) */
44
+ validateBlockTypes?: boolean;
45
+ /** Whether to validate nesting rules (default: true if catalog provided) */
46
+ validateNesting?: boolean;
47
+ }
48
+ /**
49
+ * Full validation of a block template.
50
+ *
51
+ * Performs:
52
+ * 1. Structural validation (always)
53
+ * 2. Block type validation (if catalog provided)
54
+ * 3. Nesting constraint validation (if catalog provided)
55
+ *
56
+ * @param template - The template to validate
57
+ * @param options - Validation options
58
+ * @returns Combined validation result
59
+ */
60
+ export declare function validateTemplate(template: unknown, options?: ValidateTemplateOptions): TemplateValidationResult;
61
+ //# sourceMappingURL=template-validator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"template-validator.d.ts","sourceRoot":"","sources":["../src/template-validator.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,aAAa,EAEb,YAAY,EACZ,gBAAgB,EACjB,MAAM,2BAA2B,CAAC;AAGnC;;GAEG;AACH,MAAM,WAAW,wBAAyB,SAAQ,gBAAgB;IAChE,wDAAwD;IACxD,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;CAClB;AAED;;;;;GAKG;AACH,wBAAgB,yBAAyB,CACvC,QAAQ,EAAE,OAAO,GAChB,wBAAwB,CAa1B;AAED;;;;;;GAMG;AACH,wBAAgB,0BAA0B,CACxC,QAAQ,EAAE,aAAa,EACvB,OAAO,EAAE,YAAY,GACpB,wBAAwB,CA2B1B;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,uBAAuB,CACrC,QAAQ,EAAE,aAAa,EACvB,OAAO,EAAE,YAAY,GACpB,wBAAwB,CAiF1B;AAED;;GAEG;AACH,MAAM,WAAW,uBAAuB;IACtC,0DAA0D;IAC1D,OAAO,CAAC,EAAE,YAAY,CAAC;IACvB,gFAAgF;IAChF,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,4EAA4E;IAC5E,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,OAAO,EACjB,OAAO,GAAE,uBAA4B,GACpC,wBAAwB,CA8B1B"}
@@ -0,0 +1,158 @@
1
+ import { blockTemplateSchema } from '@10up/block-renderer-core';
2
+ /**
3
+ * Validates the structural format of a block template using Zod.
4
+ *
5
+ * @param template - The template to validate
6
+ * @returns Validation result with structural errors
7
+ */
8
+ export function validateTemplateStructure(template) {
9
+ const result = blockTemplateSchema.safeParse(template);
10
+ if (result.success) {
11
+ return { valid: true, errors: [] };
12
+ }
13
+ const errors = result.error.issues.map((issue) => {
14
+ const path = issue.path.join('.');
15
+ return `Invalid template structure at ${path || 'root'}: ${issue.message}`;
16
+ });
17
+ return { valid: false, errors };
18
+ }
19
+ /**
20
+ * Validates that all block types in the template exist in the catalog.
21
+ *
22
+ * @param template - The template to validate
23
+ * @param catalog - The block catalog to check against
24
+ * @returns Validation result with unknown block warnings
25
+ */
26
+ export function validateTemplateBlockTypes(template, catalog) {
27
+ const errors = [];
28
+ const warnings = [];
29
+ function validateEntry(entry, path) {
30
+ const [blockType, , innerBlocks] = entry;
31
+ if (!catalog.has(blockType)) {
32
+ warnings.push(`${path}: Unknown block type "${blockType}"`);
33
+ }
34
+ if (innerBlocks) {
35
+ innerBlocks.forEach((child, i) => {
36
+ validateEntry(child, `${path}[2][${i}]`);
37
+ });
38
+ }
39
+ }
40
+ template.forEach((entry, i) => {
41
+ validateEntry(entry, `[${i}]`);
42
+ });
43
+ return {
44
+ valid: errors.length === 0,
45
+ errors,
46
+ warnings: warnings.length > 0 ? warnings : undefined,
47
+ };
48
+ }
49
+ /**
50
+ * Validates nesting constraints for a block template.
51
+ *
52
+ * Checks:
53
+ * - `parent` constraints: block must be direct child of specified parent types
54
+ * - `ancestor` constraints: block must have specified ancestor somewhere above
55
+ * - `allowedBlocks` constraints: parent can only contain specified child types
56
+ *
57
+ * @param template - The template to validate
58
+ * @param catalog - The block catalog with nesting rules
59
+ * @returns Validation result with nesting errors
60
+ */
61
+ export function validateTemplateNesting(template, catalog) {
62
+ const errors = [];
63
+ function validateEntry(entry, path, parentType, ancestorTypes) {
64
+ const [blockType, , innerBlocks] = entry;
65
+ const definition = catalog.get(blockType);
66
+ if (!definition) {
67
+ // Skip validation for unknown blocks but still recurse into children
68
+ if (innerBlocks) {
69
+ const newAncestors = [...ancestorTypes, blockType];
70
+ innerBlocks.forEach((child, i) => {
71
+ validateEntry(child, `${path}[2][${i}]`, blockType, newAncestors);
72
+ });
73
+ }
74
+ return;
75
+ }
76
+ // Validate parent constraint
77
+ if (definition.parent && definition.parent.length > 0) {
78
+ if (!parentType) {
79
+ errors.push(`${path}: Block "${blockType}" must be inside one of: ${definition.parent.join(', ')}`);
80
+ }
81
+ else if (!definition.parent.includes(parentType)) {
82
+ errors.push(`${path}: Block "${blockType}" cannot be a direct child of "${parentType}". ` +
83
+ `Allowed parents: ${definition.parent.join(', ')}`);
84
+ }
85
+ }
86
+ // Validate ancestor constraint
87
+ if (definition.ancestor && definition.ancestor.length > 0) {
88
+ const hasRequiredAncestor = definition.ancestor.some((required) => ancestorTypes.includes(required));
89
+ if (!hasRequiredAncestor && parentType) {
90
+ errors.push(`${path}: Block "${blockType}" must be nested inside one of: ${definition.ancestor.join(', ')}`);
91
+ }
92
+ }
93
+ // Validate children constraints (allowedBlocks)
94
+ if (innerBlocks && innerBlocks.length > 0) {
95
+ if (definition.allowedBlocks !== true &&
96
+ Array.isArray(definition.allowedBlocks)) {
97
+ for (let i = 0; i < innerBlocks.length; i++) {
98
+ const childType = innerBlocks[i][0];
99
+ if (!definition.allowedBlocks.includes(childType)) {
100
+ errors.push(`${path}[2][${i}]: Block "${blockType}" cannot contain "${childType}". ` +
101
+ `Allowed children: ${definition.allowedBlocks.join(', ')}`);
102
+ }
103
+ }
104
+ }
105
+ }
106
+ // Recurse into inner blocks
107
+ if (innerBlocks) {
108
+ const newAncestors = [...ancestorTypes, blockType];
109
+ innerBlocks.forEach((child, i) => {
110
+ validateEntry(child, `${path}[2][${i}]`, blockType, newAncestors);
111
+ });
112
+ }
113
+ }
114
+ template.forEach((entry, i) => {
115
+ validateEntry(entry, `[${i}]`, null, []);
116
+ });
117
+ return { valid: errors.length === 0, errors };
118
+ }
119
+ /**
120
+ * Full validation of a block template.
121
+ *
122
+ * Performs:
123
+ * 1. Structural validation (always)
124
+ * 2. Block type validation (if catalog provided)
125
+ * 3. Nesting constraint validation (if catalog provided)
126
+ *
127
+ * @param template - The template to validate
128
+ * @param options - Validation options
129
+ * @returns Combined validation result
130
+ */
131
+ export function validateTemplate(template, options = {}) {
132
+ const errors = [];
133
+ const warnings = [];
134
+ // 1. Structural validation
135
+ const structureResult = validateTemplateStructure(template);
136
+ if (!structureResult.valid) {
137
+ return structureResult;
138
+ }
139
+ const validTemplate = template;
140
+ // 2. Block type validation (if catalog provided)
141
+ if (options.catalog && options.validateBlockTypes !== false) {
142
+ const typeResult = validateTemplateBlockTypes(validTemplate, options.catalog);
143
+ errors.push(...typeResult.errors);
144
+ if (typeResult.warnings)
145
+ warnings.push(...typeResult.warnings);
146
+ }
147
+ // 3. Nesting validation (if catalog provided)
148
+ if (options.catalog && options.validateNesting !== false) {
149
+ const nestingResult = validateTemplateNesting(validTemplate, options.catalog);
150
+ errors.push(...nestingResult.errors);
151
+ }
152
+ return {
153
+ valid: errors.length === 0,
154
+ errors,
155
+ warnings: warnings.length > 0 ? warnings : undefined,
156
+ };
157
+ }
158
+ //# sourceMappingURL=template-validator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"template-validator.js","sourceRoot":"","sources":["../src/template-validator.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,23 @@
1
+ import type { BlockTree, BlockElement, ValidationResult } from '@10up/block-renderer-core';
2
+ import type { ThemeTokens } from '@10up/block-renderer-theme-json';
3
+ /**
4
+ * Validates color tokens used in a block element.
5
+ */
6
+ export declare function validateColorTokens(element: BlockElement, tokens: ThemeTokens): ValidationResult;
7
+ /**
8
+ * Validates typography tokens used in a block element.
9
+ */
10
+ export declare function validateTypographyTokens(element: BlockElement, tokens: ThemeTokens): ValidationResult;
11
+ /**
12
+ * Validates spacing tokens used in a block element.
13
+ */
14
+ export declare function validateSpacingTokens(element: BlockElement, tokens: ThemeTokens): ValidationResult;
15
+ /**
16
+ * Validates all theme tokens used in a block element.
17
+ */
18
+ export declare function validateElementTokens(element: BlockElement, tokens: ThemeTokens): ValidationResult;
19
+ /**
20
+ * Validates all theme tokens used in a block tree.
21
+ */
22
+ export declare function validateTreeTokens(tree: BlockTree, tokens: ThemeTokens): ValidationResult;
23
+ //# sourceMappingURL=token-validator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"token-validator.d.ts","sourceRoot":"","sources":["../src/token-validator.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAC3F,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iCAAiC,CAAC;AA0EnE;;GAEG;AACH,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,YAAY,EACrB,MAAM,EAAE,WAAW,GAClB,gBAAgB,CAiDlB;AAED;;GAEG;AACH,wBAAgB,wBAAwB,CACtC,OAAO,EAAE,YAAY,EACrB,MAAM,EAAE,WAAW,GAClB,gBAAgB,CA0BlB;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,YAAY,EACrB,MAAM,EAAE,WAAW,GAClB,gBAAgB,CAwBlB;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,YAAY,EACrB,MAAM,EAAE,WAAW,GAClB,gBAAgB,CAmBlB;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAChC,IAAI,EAAE,SAAS,EACf,MAAM,EAAE,WAAW,GAClB,gBAAgB,CAqBlB"}
@@ -0,0 +1,192 @@
1
+ import { validateSlug, parseCssVarReference } from '@10up/block-renderer-theme-json';
2
+ /**
3
+ * Color-related attribute names that should use theme tokens.
4
+ */
5
+ const COLOR_ATTRIBUTES = [
6
+ 'backgroundColor',
7
+ 'textColor',
8
+ 'borderColor',
9
+ 'overlayColor',
10
+ 'customBackgroundColor',
11
+ 'customTextColor',
12
+ ];
13
+ /**
14
+ * Spacing-related style paths that should use theme tokens.
15
+ */
16
+ const SPACING_STYLE_PATHS = [
17
+ 'style.spacing.margin',
18
+ 'style.spacing.padding',
19
+ 'style.spacing.blockGap',
20
+ ];
21
+ /**
22
+ * Checks if a value is a direct token slug (not a CSS var reference).
23
+ */
24
+ function isDirectSlug(value) {
25
+ if (typeof value !== 'string')
26
+ return false;
27
+ // Direct slugs don't start with var: or have CSS variable syntax
28
+ return !value.startsWith('var:') && !value.startsWith('var(') && !value.startsWith('#');
29
+ }
30
+ /**
31
+ * Gets a nested value from an object using a dot-separated path.
32
+ */
33
+ function getNestedValue(obj, path) {
34
+ const parts = path.split('.');
35
+ let current = obj;
36
+ for (const part of parts) {
37
+ if (current === null || current === undefined || typeof current !== 'object') {
38
+ return undefined;
39
+ }
40
+ current = current[part];
41
+ }
42
+ return current;
43
+ }
44
+ /**
45
+ * Extracts spacing values from a sides object or string.
46
+ */
47
+ function extractSpacingValues(value) {
48
+ if (typeof value === 'string') {
49
+ return [value];
50
+ }
51
+ if (typeof value === 'object' && value !== null) {
52
+ const sides = value;
53
+ const values = [];
54
+ for (const side of ['top', 'right', 'bottom', 'left', 'horizontal', 'vertical']) {
55
+ if (typeof sides[side] === 'string') {
56
+ values.push(sides[side]);
57
+ }
58
+ }
59
+ return values;
60
+ }
61
+ return [];
62
+ }
63
+ /**
64
+ * Validates color tokens used in a block element.
65
+ */
66
+ export function validateColorTokens(element, tokens) {
67
+ const errors = [];
68
+ const warnings = [];
69
+ // Check direct color attributes
70
+ for (const attr of COLOR_ATTRIBUTES) {
71
+ const value = element.props[attr];
72
+ if (isDirectSlug(value)) {
73
+ if (!validateSlug(tokens, 'color', value)) {
74
+ errors.push(`[${element.key}] Unknown color token "${value}" in ${attr}. ` +
75
+ `Available: ${tokens.colors.palette.map(c => c.slug).join(', ')}`);
76
+ }
77
+ }
78
+ }
79
+ // Check gradient attribute
80
+ const gradient = element.props.gradient;
81
+ if (isDirectSlug(gradient)) {
82
+ if (!validateSlug(tokens, 'gradient', gradient)) {
83
+ errors.push(`[${element.key}] Unknown gradient token "${gradient}". ` +
84
+ `Available: ${tokens.colors.gradients.map(g => g.slug).join(', ')}`);
85
+ }
86
+ }
87
+ // Check style.color values
88
+ const styleColor = getNestedValue(element.props, 'style.color');
89
+ if (styleColor) {
90
+ for (const [key, value] of Object.entries(styleColor)) {
91
+ if (typeof value === 'string' && value.startsWith('var:preset|color|')) {
92
+ const parsed = parseCssVarReference(value);
93
+ if (parsed && !validateSlug(tokens, parsed.type, parsed.slug)) {
94
+ errors.push(`[${element.key}] Unknown color token "${parsed.slug}" in style.color.${key}`);
95
+ }
96
+ }
97
+ }
98
+ }
99
+ return {
100
+ valid: errors.length === 0,
101
+ errors,
102
+ warnings: warnings.length > 0 ? warnings : undefined,
103
+ };
104
+ }
105
+ /**
106
+ * Validates typography tokens used in a block element.
107
+ */
108
+ export function validateTypographyTokens(element, tokens) {
109
+ const errors = [];
110
+ // Check fontSize
111
+ const fontSize = element.props.fontSize;
112
+ if (isDirectSlug(fontSize)) {
113
+ if (!validateSlug(tokens, 'font-size', fontSize)) {
114
+ errors.push(`[${element.key}] Unknown font size token "${fontSize}". ` +
115
+ `Available: ${tokens.typography.fontSizes.map(f => f.slug).join(', ')}`);
116
+ }
117
+ }
118
+ // Check fontFamily
119
+ const fontFamily = element.props.fontFamily;
120
+ if (isDirectSlug(fontFamily)) {
121
+ if (!validateSlug(tokens, 'font-family', fontFamily)) {
122
+ errors.push(`[${element.key}] Unknown font family token "${fontFamily}". ` +
123
+ `Available: ${tokens.typography.fontFamilies.map(f => f.slug).join(', ')}`);
124
+ }
125
+ }
126
+ return { valid: errors.length === 0, errors };
127
+ }
128
+ /**
129
+ * Validates spacing tokens used in a block element.
130
+ */
131
+ export function validateSpacingTokens(element, tokens) {
132
+ const errors = [];
133
+ for (const path of SPACING_STYLE_PATHS) {
134
+ const value = getNestedValue(element.props, path);
135
+ if (value === undefined)
136
+ continue;
137
+ const spacingValues = extractSpacingValues(value);
138
+ for (const spacing of spacingValues) {
139
+ // Check if it's a CSS var reference
140
+ if (spacing.startsWith('var:preset|spacing|')) {
141
+ const parsed = parseCssVarReference(spacing);
142
+ if (parsed && !validateSlug(tokens, 'spacing', parsed.slug)) {
143
+ errors.push(`[${element.key}] Unknown spacing token "${parsed.slug}" in ${path}. ` +
144
+ `Available: ${tokens.spacing.spacingSizes.map(s => s.slug).join(', ')}`);
145
+ }
146
+ }
147
+ }
148
+ }
149
+ return { valid: errors.length === 0, errors };
150
+ }
151
+ /**
152
+ * Validates all theme tokens used in a block element.
153
+ */
154
+ export function validateElementTokens(element, tokens) {
155
+ const errors = [];
156
+ const warnings = [];
157
+ const colorResult = validateColorTokens(element, tokens);
158
+ errors.push(...colorResult.errors);
159
+ if (colorResult.warnings)
160
+ warnings.push(...colorResult.warnings);
161
+ const typographyResult = validateTypographyTokens(element, tokens);
162
+ errors.push(...typographyResult.errors);
163
+ const spacingResult = validateSpacingTokens(element, tokens);
164
+ errors.push(...spacingResult.errors);
165
+ return {
166
+ valid: errors.length === 0,
167
+ errors,
168
+ warnings: warnings.length > 0 ? warnings : undefined,
169
+ };
170
+ }
171
+ /**
172
+ * Validates all theme tokens used in a block tree.
173
+ */
174
+ export function validateTreeTokens(tree, tokens) {
175
+ const errors = [];
176
+ const warnings = [];
177
+ for (const element of Object.values(tree.elements)) {
178
+ const result = validateElementTokens(element, tokens);
179
+ if (!result.valid) {
180
+ errors.push(...result.errors);
181
+ }
182
+ if (result.warnings) {
183
+ warnings.push(...result.warnings);
184
+ }
185
+ }
186
+ return {
187
+ valid: errors.length === 0,
188
+ errors,
189
+ warnings: warnings.length > 0 ? warnings : undefined,
190
+ };
191
+ }
192
+ //# sourceMappingURL=token-validator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"token-validator.js","sourceRoot":"","sources":["../src/token-validator.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,26 @@
1
+ import type { BlockTree, BlockElement, BlockDefinition, BlockCatalog, ValidationResult } from '@10up/block-renderer-core';
2
+ /**
3
+ * Validates parent constraints for a block.
4
+ * Checks if the block's direct parent is in its allowed `parent` list.
5
+ */
6
+ export declare function validateParent(element: BlockElement, tree: BlockTree, definition: BlockDefinition): ValidationResult;
7
+ /**
8
+ * Validates ancestor constraints for a block.
9
+ * Checks if any required ancestors exist somewhere in the tree path.
10
+ */
11
+ export declare function validateAncestor(element: BlockElement, tree: BlockTree, definition: BlockDefinition): ValidationResult;
12
+ /**
13
+ * Validates children constraints for a block.
14
+ * Checks if all children are in the block's `allowedBlocks` list.
15
+ */
16
+ export declare function validateChildren(element: BlockElement, tree: BlockTree, definition: BlockDefinition): ValidationResult;
17
+ /**
18
+ * Validates the `multiple` constraint.
19
+ * Checks if blocks with `multiple: false` appear only once.
20
+ */
21
+ export declare function validateMultiple(tree: BlockTree, catalog: BlockCatalog): ValidationResult;
22
+ /**
23
+ * Validates all nesting rules for a tree.
24
+ */
25
+ export declare function validateTreeNesting(tree: BlockTree, catalog: BlockCatalog): ValidationResult;
26
+ //# sourceMappingURL=tree-validator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tree-validator.d.ts","sourceRoot":"","sources":["../src/tree-validator.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,SAAS,EACT,YAAY,EACZ,eAAe,EACf,YAAY,EACZ,gBAAgB,EACjB,MAAM,2BAA2B,CAAC;AA6CnC;;;GAGG;AACH,wBAAgB,cAAc,CAC5B,OAAO,EAAE,YAAY,EACrB,IAAI,EAAE,SAAS,EACf,UAAU,EAAE,eAAe,GAC1B,gBAAgB,CA4BlB;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,YAAY,EACrB,IAAI,EAAE,SAAS,EACf,UAAU,EAAE,eAAe,GAC1B,gBAAgB,CAwBlB;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,YAAY,EACrB,IAAI,EAAE,SAAS,EACf,UAAU,EAAE,eAAe,GAC1B,gBAAgB,CAyBlB;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,SAAS,EACf,OAAO,EAAE,YAAY,GACpB,gBAAgB,CAuBlB;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,SAAS,EACf,OAAO,EAAE,YAAY,GACpB,gBAAgB,CA2ClB"}
@@ -0,0 +1,160 @@
1
+ /**
2
+ * Gets the parent element of a block in the tree.
3
+ */
4
+ function getParentElement(element, tree) {
5
+ if (!element.parentKey)
6
+ return null;
7
+ return tree.elements[element.parentKey] || null;
8
+ }
9
+ /**
10
+ * Gets all ancestor elements of a block (from immediate parent to root).
11
+ */
12
+ function getAncestors(element, tree) {
13
+ const ancestors = [];
14
+ let current = getParentElement(element, tree);
15
+ while (current) {
16
+ ancestors.push(current);
17
+ current = getParentElement(current, tree);
18
+ }
19
+ return ancestors;
20
+ }
21
+ /**
22
+ * Gets the children elements of a block.
23
+ */
24
+ function getChildren(element, tree) {
25
+ if (!element.children)
26
+ return [];
27
+ return element.children
28
+ .map(key => tree.elements[key])
29
+ .filter((child) => child !== undefined);
30
+ }
31
+ /**
32
+ * Validates parent constraints for a block.
33
+ * Checks if the block's direct parent is in its allowed `parent` list.
34
+ */
35
+ export function validateParent(element, tree, definition) {
36
+ const errors = [];
37
+ // If no parent constraint, all parents are valid
38
+ if (!definition.parent || definition.parent.length === 0) {
39
+ return { valid: true, errors: [] };
40
+ }
41
+ const parent = getParentElement(element, tree);
42
+ // If block requires a parent but has none
43
+ if (!parent) {
44
+ errors.push(`[${element.key}] Block "${element.type}" must be inside one of: ${definition.parent.join(', ')}`);
45
+ return { valid: false, errors };
46
+ }
47
+ // Check if parent type is in allowed list
48
+ if (!definition.parent.includes(parent.type)) {
49
+ errors.push(`[${element.key}] Block "${element.type}" cannot be a direct child of "${parent.type}". ` +
50
+ `Allowed parents: ${definition.parent.join(', ')}`);
51
+ return { valid: false, errors };
52
+ }
53
+ return { valid: true, errors: [] };
54
+ }
55
+ /**
56
+ * Validates ancestor constraints for a block.
57
+ * Checks if any required ancestors exist somewhere in the tree path.
58
+ */
59
+ export function validateAncestor(element, tree, definition) {
60
+ const errors = [];
61
+ // If no ancestor constraint, all ancestors are valid
62
+ if (!definition.ancestor || definition.ancestor.length === 0) {
63
+ return { valid: true, errors: [] };
64
+ }
65
+ const ancestors = getAncestors(element, tree);
66
+ const ancestorTypes = ancestors.map(a => a.type);
67
+ // Check if at least one required ancestor exists
68
+ const hasRequiredAncestor = definition.ancestor.some(requiredType => ancestorTypes.includes(requiredType));
69
+ if (!hasRequiredAncestor) {
70
+ errors.push(`[${element.key}] Block "${element.type}" must be nested inside one of: ${definition.ancestor.join(', ')}`);
71
+ return { valid: false, errors };
72
+ }
73
+ return { valid: true, errors: [] };
74
+ }
75
+ /**
76
+ * Validates children constraints for a block.
77
+ * Checks if all children are in the block's `allowedBlocks` list.
78
+ */
79
+ export function validateChildren(element, tree, definition) {
80
+ const errors = [];
81
+ // If allowedBlocks is true, any block is allowed
82
+ if (definition.allowedBlocks === true) {
83
+ return { valid: true, errors: [] };
84
+ }
85
+ // If no allowedBlocks constraint, all children are valid
86
+ if (!definition.allowedBlocks || !Array.isArray(definition.allowedBlocks)) {
87
+ return { valid: true, errors: [] };
88
+ }
89
+ const children = getChildren(element, tree);
90
+ for (const child of children) {
91
+ if (!definition.allowedBlocks.includes(child.type)) {
92
+ errors.push(`[${element.key}] Block "${element.type}" cannot contain "${child.type}". ` +
93
+ `Allowed children: ${definition.allowedBlocks.join(', ')}`);
94
+ }
95
+ }
96
+ return { valid: errors.length === 0, errors };
97
+ }
98
+ /**
99
+ * Validates the `multiple` constraint.
100
+ * Checks if blocks with `multiple: false` appear only once.
101
+ */
102
+ export function validateMultiple(tree, catalog) {
103
+ const errors = [];
104
+ const blockCounts = new Map();
105
+ // Count occurrences of each block type
106
+ for (const element of Object.values(tree.elements)) {
107
+ const existing = blockCounts.get(element.type) || [];
108
+ existing.push(element.key);
109
+ blockCounts.set(element.type, existing);
110
+ }
111
+ // Check blocks with multiple: false
112
+ for (const [blockType, keys] of blockCounts.entries()) {
113
+ const definition = catalog.get(blockType);
114
+ if (definition?.multiple === false && keys.length > 1) {
115
+ errors.push(`Block "${blockType}" can only appear once, but found ${keys.length} instances: ${keys.join(', ')}`);
116
+ }
117
+ }
118
+ return { valid: errors.length === 0, errors };
119
+ }
120
+ /**
121
+ * Validates all nesting rules for a tree.
122
+ */
123
+ export function validateTreeNesting(tree, catalog) {
124
+ const errors = [];
125
+ const warnings = [];
126
+ // Validate each element's nesting
127
+ for (const element of Object.values(tree.elements)) {
128
+ const definition = catalog.get(element.type);
129
+ if (!definition) {
130
+ // Unknown block type - skip nesting validation
131
+ continue;
132
+ }
133
+ // Validate parent constraint
134
+ const parentResult = validateParent(element, tree, definition);
135
+ if (!parentResult.valid) {
136
+ errors.push(...parentResult.errors);
137
+ }
138
+ // Validate ancestor constraint
139
+ const ancestorResult = validateAncestor(element, tree, definition);
140
+ if (!ancestorResult.valid) {
141
+ errors.push(...ancestorResult.errors);
142
+ }
143
+ // Validate children constraint
144
+ const childrenResult = validateChildren(element, tree, definition);
145
+ if (!childrenResult.valid) {
146
+ errors.push(...childrenResult.errors);
147
+ }
148
+ }
149
+ // Validate multiple constraints
150
+ const multipleResult = validateMultiple(tree, catalog);
151
+ if (!multipleResult.valid) {
152
+ errors.push(...multipleResult.errors);
153
+ }
154
+ return {
155
+ valid: errors.length === 0,
156
+ errors,
157
+ warnings: warnings.length > 0 ? warnings : undefined,
158
+ };
159
+ }
160
+ //# sourceMappingURL=tree-validator.js.map