@uniweb/semantic-parser 1.1.4 → 1.1.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/AGENTS.md +8 -11
- package/README.md +3 -160
- package/package.json +2 -5
- package/src/index.js +1 -2
- package/src/processors/groups.js +16 -15
- package/docs/api.md +0 -350
- package/docs/entity-consolidation.md +0 -470
- package/docs/file-structure.md +0 -50
- package/docs/guide.md +0 -206
- package/docs/mapping-patterns.md +0 -928
- package/docs/text-component-reference.md +0 -515
- package/reference/README.md +0 -195
- package/reference/Text.js +0 -188
- package/src/mappers/accessor.js +0 -312
- package/src/mappers/extractors.js +0 -416
- package/src/mappers/helpers.js +0 -234
- package/src/mappers/index.js +0 -28
- package/src/mappers/types.js +0 -495
- package/src/processors/groups_backup.js +0 -379
- package/src/processors/groups_doc.md +0 -179
- package/src/processors/sequence_backup.js +0 -402
- package/src/processors_old/byType.js +0 -129
- package/src/processors_old/groups.js +0 -240
- package/src/processors_old/sequence.js +0 -140
package/reference/Text.js
DELETED
|
@@ -1,188 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Text - A smart typography component for rendering content from semantic-parser
|
|
5
|
-
*
|
|
6
|
-
* Features:
|
|
7
|
-
* - Handles single strings or arrays of paragraphs
|
|
8
|
-
* - Smart semantic defaults for different content types
|
|
9
|
-
* - Automatic filtering of empty content
|
|
10
|
-
*
|
|
11
|
-
* Security Model:
|
|
12
|
-
* - Assumes content is ALREADY SANITIZED at the engine level
|
|
13
|
-
* - Does NOT sanitize HTML (that's the engine's responsibility)
|
|
14
|
-
* - Trusts the data it receives and renders it as-is
|
|
15
|
-
*
|
|
16
|
-
* @param {Object} props
|
|
17
|
-
* @param {string|string[]} props.text - The content to render. Can be a string or an array of strings.
|
|
18
|
-
* @param {string} [props.as='p'] - The tag to use for the wrapper or primary semantic element (e.g. 'h1', 'p', 'div').
|
|
19
|
-
* @param {boolean} [props.html=true] - If true, renders content as HTML. If false, renders as plain text.
|
|
20
|
-
* @param {string} [props.className] - Optional className to apply to the outer wrapper or individual elements.
|
|
21
|
-
* @param {string} [props.lineAs] - For array inputs: tag to wrap each line. Defaults to 'div' for headings, 'p' for others.
|
|
22
|
-
*
|
|
23
|
-
* @example
|
|
24
|
-
* // Simple paragraph (semantic default)
|
|
25
|
-
* <Text text="Hello World" />
|
|
26
|
-
*
|
|
27
|
-
* // Explicit heading
|
|
28
|
-
* <Text text="Hello World" as="h1" />
|
|
29
|
-
*
|
|
30
|
-
* // Multi-line heading
|
|
31
|
-
* <Text text={["Welcome to", "Our Platform"]} as="h1" />
|
|
32
|
-
*
|
|
33
|
-
* // Multiple paragraphs (clean semantic output)
|
|
34
|
-
* <Text text={["First paragraph", "Second paragraph"]} />
|
|
35
|
-
*
|
|
36
|
-
* // Rich HTML content (assumes already sanitized by engine)
|
|
37
|
-
* <Text text={["Safe <strong>bold</strong> text", "With <em>emphasis</em>"]} />
|
|
38
|
-
*
|
|
39
|
-
* // Plain text when HTML is disabled
|
|
40
|
-
* <Text text="No <strong>formatting</strong> here" html={false} />
|
|
41
|
-
*
|
|
42
|
-
* // Explicit div wrapper when needed
|
|
43
|
-
* <Text text={["Item 1", "Item 2"]} as="div" lineAs="span" />
|
|
44
|
-
*/
|
|
45
|
-
function Text({ text, as = 'p', html = true, className, lineAs }) {
|
|
46
|
-
const isArray = Array.isArray(text);
|
|
47
|
-
const Tag = as;
|
|
48
|
-
const isHeading = as === 'h1' || as === 'h2' || as === 'h3' || as === 'h4' || as === 'h5' || as === 'h6';
|
|
49
|
-
|
|
50
|
-
// Single string input
|
|
51
|
-
if (!isArray) {
|
|
52
|
-
if (!text || text.trim() === '') return null;
|
|
53
|
-
|
|
54
|
-
if (html) {
|
|
55
|
-
return (
|
|
56
|
-
<Tag
|
|
57
|
-
className={className}
|
|
58
|
-
dangerouslySetInnerHTML={{ __html: text }}
|
|
59
|
-
/>
|
|
60
|
-
);
|
|
61
|
-
}
|
|
62
|
-
return <Tag className={className}>{text}</Tag>;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
// Array input - filter empty content first
|
|
66
|
-
const filteredText = text.filter(
|
|
67
|
-
(item) => typeof item === 'string' && item.trim() !== ''
|
|
68
|
-
);
|
|
69
|
-
|
|
70
|
-
if (filteredText.length === 0) {
|
|
71
|
-
return null; // Don't render anything for empty arrays
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
// Determine the line wrapper tag with smart defaults
|
|
75
|
-
const LineTag = lineAs || (isHeading ? 'div' : 'p');
|
|
76
|
-
|
|
77
|
-
// Multi-line heading: wrap all lines in a single heading tag
|
|
78
|
-
if (isHeading) {
|
|
79
|
-
return (
|
|
80
|
-
<Tag className={className}>
|
|
81
|
-
{filteredText.map((line, i) => {
|
|
82
|
-
if (html) {
|
|
83
|
-
return (
|
|
84
|
-
<LineTag
|
|
85
|
-
key={i}
|
|
86
|
-
dangerouslySetInnerHTML={{ __html: line }}
|
|
87
|
-
/>
|
|
88
|
-
);
|
|
89
|
-
}
|
|
90
|
-
return <LineTag key={i}>{line}</LineTag>;
|
|
91
|
-
})}
|
|
92
|
-
</Tag>
|
|
93
|
-
);
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
// Non-heading arrays: render each line as separate element
|
|
97
|
-
return (
|
|
98
|
-
<>
|
|
99
|
-
{filteredText.map((line, i) => {
|
|
100
|
-
if (html) {
|
|
101
|
-
return (
|
|
102
|
-
<LineTag
|
|
103
|
-
key={i}
|
|
104
|
-
className={className}
|
|
105
|
-
dangerouslySetInnerHTML={{ __html: line }}
|
|
106
|
-
/>
|
|
107
|
-
);
|
|
108
|
-
}
|
|
109
|
-
return (
|
|
110
|
-
<LineTag key={i} className={className}>
|
|
111
|
-
{line}
|
|
112
|
-
</LineTag>
|
|
113
|
-
);
|
|
114
|
-
})}
|
|
115
|
-
</>
|
|
116
|
-
);
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
// ============================================================================
|
|
120
|
-
// Semantic Wrapper Components - Thin wrappers around Text for common use cases
|
|
121
|
-
// ============================================================================
|
|
122
|
-
|
|
123
|
-
/**
|
|
124
|
-
* H1 - Heading level 1 component
|
|
125
|
-
* @param {Object} props - All Text props except 'as' (automatically set to 'h1')
|
|
126
|
-
* @example
|
|
127
|
-
* <H1 text="Main Title" />
|
|
128
|
-
* <H1 text={["Multi-line", "Main Title"]} />
|
|
129
|
-
*/
|
|
130
|
-
export const H1 = (props) => <Text {...props} as="h1" />;
|
|
131
|
-
|
|
132
|
-
/**
|
|
133
|
-
* H2 - Heading level 2 component
|
|
134
|
-
* @param {Object} props - All Text props except 'as' (automatically set to 'h2')
|
|
135
|
-
*/
|
|
136
|
-
export const H2 = (props) => <Text {...props} as="h2" />;
|
|
137
|
-
|
|
138
|
-
/**
|
|
139
|
-
* H3 - Heading level 3 component
|
|
140
|
-
* @param {Object} props - All Text props except 'as' (automatically set to 'h3')
|
|
141
|
-
*/
|
|
142
|
-
export const H3 = (props) => <Text {...props} as="h3" />;
|
|
143
|
-
|
|
144
|
-
/**
|
|
145
|
-
* H4 - Heading level 4 component
|
|
146
|
-
* @param {Object} props - All Text props except 'as' (automatically set to 'h4')
|
|
147
|
-
*/
|
|
148
|
-
export const H4 = (props) => <Text {...props} as="h4" />;
|
|
149
|
-
|
|
150
|
-
/**
|
|
151
|
-
* H5 - Heading level 5 component
|
|
152
|
-
* @param {Object} props - All Text props except 'as' (automatically set to 'h5')
|
|
153
|
-
*/
|
|
154
|
-
export const H5 = (props) => <Text {...props} as="h5" />;
|
|
155
|
-
|
|
156
|
-
/**
|
|
157
|
-
* H6 - Heading level 6 component
|
|
158
|
-
* @param {Object} props - All Text props except 'as' (automatically set to 'h6')
|
|
159
|
-
*/
|
|
160
|
-
export const H6 = (props) => <Text {...props} as="h6" />;
|
|
161
|
-
|
|
162
|
-
/**
|
|
163
|
-
* P - Paragraph component (explicitly semantic)
|
|
164
|
-
* @param {Object} props - All Text props except 'as' (automatically set to 'p')
|
|
165
|
-
* @example
|
|
166
|
-
* <P text="A paragraph of content" />
|
|
167
|
-
* <P text={["First paragraph", "Second paragraph"]} />
|
|
168
|
-
*/
|
|
169
|
-
export const P = (props) => <Text {...props} as="p" />;
|
|
170
|
-
|
|
171
|
-
/**
|
|
172
|
-
* PlainText - Text component with HTML processing disabled
|
|
173
|
-
* @param {Object} props - All Text props except 'html' (automatically set to false)
|
|
174
|
-
* @example
|
|
175
|
-
* <PlainText text="Display <strong>tags</strong> as literal text" />
|
|
176
|
-
*/
|
|
177
|
-
export const PlainText = (props) => <Text {...props} html={false} />;
|
|
178
|
-
|
|
179
|
-
/**
|
|
180
|
-
* Div - Explicit div wrapper component
|
|
181
|
-
* @param {Object} props - All Text props except 'as' (automatically set to 'div')
|
|
182
|
-
* @example
|
|
183
|
-
* <Div text={["Item 1", "Item 2"]} lineAs="span" />
|
|
184
|
-
*/
|
|
185
|
-
export const Div = (props) => <Text {...props} as="div" />;
|
|
186
|
-
|
|
187
|
-
// Export all components
|
|
188
|
-
export default Text;
|
package/src/mappers/accessor.js
DELETED
|
@@ -1,312 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Path-based accessor for extracting values from parsed content
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import { applyType, validateType } from './types.js';
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* Parse a path string into segments, handling array indices
|
|
9
|
-
* @param {string} path - Path string (e.g., 'groups.main.body.imgs[0].url')
|
|
10
|
-
* @returns {Array} Array of path segments
|
|
11
|
-
*/
|
|
12
|
-
function parsePath(path) {
|
|
13
|
-
const segments = [];
|
|
14
|
-
const parts = path.split('.');
|
|
15
|
-
|
|
16
|
-
for (const part of parts) {
|
|
17
|
-
// Check for array index notation: key[0]
|
|
18
|
-
const match = part.match(/^(.+?)\[(\d+)\]$/);
|
|
19
|
-
if (match) {
|
|
20
|
-
segments.push({ key: match[1], type: 'object' });
|
|
21
|
-
segments.push({ index: parseInt(match[2], 10), type: 'array' });
|
|
22
|
-
} else {
|
|
23
|
-
segments.push({ key: part, type: 'object' });
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
return segments;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* Get value at path from parsed content
|
|
32
|
-
* @param {Object} parsed - Parsed content from parseContent()
|
|
33
|
-
* @param {string} path - Path to value (e.g., 'groups.main.header.title')
|
|
34
|
-
* @param {Object} options - Options for extraction
|
|
35
|
-
* @param {*} options.defaultValue - Default value if path doesn't exist
|
|
36
|
-
* @param {Function} options.transform - Transformation function to apply to value
|
|
37
|
-
* @param {boolean} options.required - Throw error if value is missing
|
|
38
|
-
* @param {string} options.type - Field type for automatic transformation
|
|
39
|
-
* @param {boolean} options.treatEmptyAsDefault - Treat empty strings as missing
|
|
40
|
-
* @returns {*} Value at path
|
|
41
|
-
*/
|
|
42
|
-
function getByPath(parsed, path, options = {}) {
|
|
43
|
-
const {
|
|
44
|
-
defaultValue,
|
|
45
|
-
transform,
|
|
46
|
-
required = false,
|
|
47
|
-
type,
|
|
48
|
-
treatEmptyAsDefault = false,
|
|
49
|
-
...typeOptions
|
|
50
|
-
} = options;
|
|
51
|
-
|
|
52
|
-
if (!parsed || !path) {
|
|
53
|
-
if (required) {
|
|
54
|
-
throw new Error('Path is required');
|
|
55
|
-
}
|
|
56
|
-
return defaultValue;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
const segments = parsePath(path);
|
|
60
|
-
let current = parsed;
|
|
61
|
-
|
|
62
|
-
for (let i = 0; i < segments.length; i++) {
|
|
63
|
-
const segment = segments[i];
|
|
64
|
-
|
|
65
|
-
if (current === null || current === undefined) {
|
|
66
|
-
if (required) {
|
|
67
|
-
throw new Error(`Required field missing at path: ${path}`);
|
|
68
|
-
}
|
|
69
|
-
return defaultValue;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
if (segment.type === 'array') {
|
|
73
|
-
if (!Array.isArray(current)) {
|
|
74
|
-
if (required) {
|
|
75
|
-
throw new Error(`Expected array at path segment ${i} in: ${path}`);
|
|
76
|
-
}
|
|
77
|
-
return defaultValue;
|
|
78
|
-
}
|
|
79
|
-
current = current[segment.index];
|
|
80
|
-
} else {
|
|
81
|
-
current = current[segment.key];
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
let value = current !== undefined ? current : defaultValue;
|
|
86
|
-
|
|
87
|
-
// Treat empty strings as missing if requested
|
|
88
|
-
if (treatEmptyAsDefault && value === '') {
|
|
89
|
-
value = defaultValue;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
if (required && (value === undefined || value === null || value === '')) {
|
|
93
|
-
throw new Error(`Required field missing at path: ${path}`);
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
// Apply type transformation if specified
|
|
97
|
-
if (type && value !== undefined && value !== null) {
|
|
98
|
-
value = applyType(value, type, typeOptions);
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
// Apply custom transform after type transformation
|
|
102
|
-
return transform ? transform(value) : value;
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
/**
|
|
106
|
-
* Extract multiple values using a schema
|
|
107
|
-
* @param {Object} parsed - Parsed content from parseContent()
|
|
108
|
-
* @param {Object} schema - Schema defining paths and transformations
|
|
109
|
-
* @param {Object} options - Extraction options
|
|
110
|
-
* @param {string} options.mode - Execution mode ('visual-editor' or 'build')
|
|
111
|
-
* @returns {Object} Extracted values
|
|
112
|
-
*
|
|
113
|
-
* @example
|
|
114
|
-
* const schema = {
|
|
115
|
-
* title: {
|
|
116
|
-
* path: 'groups.main.header.title',
|
|
117
|
-
* type: 'plaintext',
|
|
118
|
-
* maxLength: 60
|
|
119
|
-
* },
|
|
120
|
-
* image: {
|
|
121
|
-
* path: 'groups.main.body.imgs[0].url',
|
|
122
|
-
* type: 'image',
|
|
123
|
-
* defaultValue: '/placeholder.jpg'
|
|
124
|
-
* },
|
|
125
|
-
* description: {
|
|
126
|
-
* path: 'groups.main.body.paragraphs',
|
|
127
|
-
* type: 'excerpt',
|
|
128
|
-
* maxLength: 150
|
|
129
|
-
* }
|
|
130
|
-
* };
|
|
131
|
-
* const data = extractBySchema(parsed, schema, { mode: 'visual-editor' });
|
|
132
|
-
*/
|
|
133
|
-
function extractBySchema(parsed, schema, options = {}) {
|
|
134
|
-
const { mode = 'visual-editor' } = options;
|
|
135
|
-
const result = {};
|
|
136
|
-
const validationResults = [];
|
|
137
|
-
|
|
138
|
-
for (const [key, config] of Object.entries(schema)) {
|
|
139
|
-
// Allow shorthand: key: 'path.to.value'
|
|
140
|
-
if (typeof config === 'string') {
|
|
141
|
-
result[key] = getByPath(parsed, config);
|
|
142
|
-
} else {
|
|
143
|
-
// Full config: { path, type, defaultValue, transform, required, ... }
|
|
144
|
-
const { path, type, ...fieldOptions } = config;
|
|
145
|
-
|
|
146
|
-
// Extract value
|
|
147
|
-
result[key] = getByPath(parsed, path, { type, ...fieldOptions });
|
|
148
|
-
|
|
149
|
-
// Validate if type specified and in build mode
|
|
150
|
-
if (type && mode === 'build') {
|
|
151
|
-
const rawValue = getByPath(parsed, path, {
|
|
152
|
-
defaultValue: fieldOptions.defaultValue
|
|
153
|
-
});
|
|
154
|
-
const errors = validateType(rawValue, type, {
|
|
155
|
-
...fieldOptions,
|
|
156
|
-
fieldName: key
|
|
157
|
-
}, mode);
|
|
158
|
-
validationResults.push(...errors);
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
// In build mode, log validation results
|
|
164
|
-
if (mode === 'build' && validationResults.length > 0) {
|
|
165
|
-
const errors = validationResults.filter(v => v.severity === 'error');
|
|
166
|
-
const warnings = validationResults.filter(v => v.severity === 'warning');
|
|
167
|
-
|
|
168
|
-
if (warnings.length > 0) {
|
|
169
|
-
console.warn('Content validation warnings:');
|
|
170
|
-
warnings.forEach(w => {
|
|
171
|
-
console.warn(` [${w.field}] ${w.message}${w.autoFix ? ' (auto-fixed)' : ''}`);
|
|
172
|
-
});
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
if (errors.length > 0) {
|
|
176
|
-
console.error('Content validation errors:');
|
|
177
|
-
errors.forEach(e => {
|
|
178
|
-
console.error(` [${e.field}] ${e.message}`);
|
|
179
|
-
});
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
return result;
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
/**
|
|
187
|
-
* Check if a path exists in parsed content
|
|
188
|
-
* @param {Object} parsed - Parsed content
|
|
189
|
-
* @param {string} path - Path to check
|
|
190
|
-
* @returns {boolean} True if path exists and has a non-null/undefined value
|
|
191
|
-
*/
|
|
192
|
-
function hasPath(parsed, path) {
|
|
193
|
-
try {
|
|
194
|
-
const value = getByPath(parsed, path);
|
|
195
|
-
return value !== null && value !== undefined;
|
|
196
|
-
} catch {
|
|
197
|
-
return false;
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
/**
|
|
202
|
-
* Get multiple paths, return first that exists
|
|
203
|
-
* @param {Object} parsed - Parsed content
|
|
204
|
-
* @param {Array<string>} paths - Array of paths to try
|
|
205
|
-
* @param {*} defaultValue - Default if none exist
|
|
206
|
-
* @returns {*} First existing value or default
|
|
207
|
-
*/
|
|
208
|
-
function getFirstExisting(parsed, paths, defaultValue = null) {
|
|
209
|
-
for (const path of paths) {
|
|
210
|
-
if (hasPath(parsed, path)) {
|
|
211
|
-
return getByPath(parsed, path);
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
return defaultValue;
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
/**
|
|
218
|
-
* Extract values from array of items using same path
|
|
219
|
-
* @param {Object} parsed - Parsed content
|
|
220
|
-
* @param {string} arrayPath - Path to array
|
|
221
|
-
* @param {string|Object} itemConfig - Path or config for each item
|
|
222
|
-
* @returns {Array} Extracted values
|
|
223
|
-
*
|
|
224
|
-
* @example
|
|
225
|
-
* // Get all item titles
|
|
226
|
-
* mapArray(parsed, 'groups.items', 'header.title')
|
|
227
|
-
*
|
|
228
|
-
* // Get objects from each item
|
|
229
|
-
* mapArray(parsed, 'groups.items', {
|
|
230
|
-
* title: 'header.title',
|
|
231
|
-
* text: { path: 'body.paragraphs', transform: p => p.join(' ') }
|
|
232
|
-
* })
|
|
233
|
-
*/
|
|
234
|
-
function mapArray(parsed, arrayPath, itemConfig) {
|
|
235
|
-
const array = getByPath(parsed, arrayPath, { defaultValue: [] });
|
|
236
|
-
|
|
237
|
-
if (!Array.isArray(array)) {
|
|
238
|
-
return [];
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
return array.map(item => {
|
|
242
|
-
if (typeof itemConfig === 'string') {
|
|
243
|
-
return getByPath({ item }, `item.${itemConfig}`);
|
|
244
|
-
} else {
|
|
245
|
-
return extractBySchema({ item },
|
|
246
|
-
Object.entries(itemConfig).reduce((acc, [key, config]) => {
|
|
247
|
-
if (typeof config === 'string') {
|
|
248
|
-
acc[key] = `item.${config}`;
|
|
249
|
-
} else {
|
|
250
|
-
acc[key] = { ...config, path: `item.${config.path}` };
|
|
251
|
-
}
|
|
252
|
-
return acc;
|
|
253
|
-
}, {})
|
|
254
|
-
);
|
|
255
|
-
}
|
|
256
|
-
});
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
/**
|
|
260
|
-
* Validate content against schema without extracting
|
|
261
|
-
* Useful for providing UI hints in visual editor
|
|
262
|
-
* @param {Object} parsed - Parsed content
|
|
263
|
-
* @param {Object} schema - Schema to validate against
|
|
264
|
-
* @param {Object} options - Validation options
|
|
265
|
-
* @param {string} options.mode - Execution mode ('visual-editor' or 'build')
|
|
266
|
-
* @returns {Object} Validation results by field
|
|
267
|
-
*
|
|
268
|
-
* @example
|
|
269
|
-
* const hints = validateSchema(parsed, schema);
|
|
270
|
-
* // {
|
|
271
|
-
* // title: [{ type: 'max_length', severity: 'info', message: '...' }],
|
|
272
|
-
* // image: [{ type: 'required', severity: 'error', message: '...' }]
|
|
273
|
-
* // }
|
|
274
|
-
*/
|
|
275
|
-
function validateSchema(parsed, schema, options = {}) {
|
|
276
|
-
const { mode = 'visual-editor' } = options;
|
|
277
|
-
const results = {};
|
|
278
|
-
|
|
279
|
-
for (const [key, config] of Object.entries(schema)) {
|
|
280
|
-
if (typeof config === 'string') {
|
|
281
|
-
continue; // No validation for shorthand
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
const { path, type, ...fieldOptions } = config;
|
|
285
|
-
|
|
286
|
-
if (type) {
|
|
287
|
-
const rawValue = getByPath(parsed, path, {
|
|
288
|
-
defaultValue: fieldOptions.defaultValue
|
|
289
|
-
});
|
|
290
|
-
|
|
291
|
-
const errors = validateType(rawValue, type, {
|
|
292
|
-
...fieldOptions,
|
|
293
|
-
fieldName: key
|
|
294
|
-
}, mode);
|
|
295
|
-
|
|
296
|
-
if (errors.length > 0) {
|
|
297
|
-
results[key] = errors;
|
|
298
|
-
}
|
|
299
|
-
}
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
return results;
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
export {
|
|
306
|
-
getByPath,
|
|
307
|
-
extractBySchema,
|
|
308
|
-
validateSchema,
|
|
309
|
-
hasPath,
|
|
310
|
-
getFirstExisting,
|
|
311
|
-
mapArray
|
|
312
|
-
};
|