@abeedoo/radish-schemas 1.7.7
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/README.md +317 -0
- package/index.js +38 -0
- package/package.json +51 -0
- package/prompts/index.js +118 -0
- package/prompts/radish-app-generation.md +259 -0
- package/prompts/radish-components-generation.md +138 -0
- package/prompts/radish-roles-generation.md +133 -0
- package/prompts/radish-schema-generation.md +267 -0
- package/prompts/radish-theme-generation.md +138 -0
- package/prompts/radish-types-generation.md +294 -0
- package/prompts/radish-ui-generation.md +227 -0
- package/schemas/app.schema.json +289 -0
- package/schemas/components.schema.json +199 -0
- package/schemas/index.js +49 -0
- package/schemas/roles.schema.json +64 -0
- package/schemas/theme.schema.json +196 -0
- package/schemas/types.schema.json +761 -0
- package/schemas/ui.schema.json +346 -0
- package/validators/index.js +175 -0
|
@@ -0,0 +1,346 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
+
"$id": "https://radish.dev/schemas/ui.schema.json",
|
|
4
|
+
"title": "Radish UI Blueprint",
|
|
5
|
+
"description": "Schema for defining UI routes, pages, layouts, and content blocks. Content blocks are entity-driven and reference entities defined in the types blueprint.",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"required": [
|
|
8
|
+
"version",
|
|
9
|
+
"routes",
|
|
10
|
+
"pages"
|
|
11
|
+
],
|
|
12
|
+
"additionalProperties": false,
|
|
13
|
+
"properties": {
|
|
14
|
+
"version": {
|
|
15
|
+
"type": "integer",
|
|
16
|
+
"minimum": 1,
|
|
17
|
+
"description": "Blueprint spec version"
|
|
18
|
+
},
|
|
19
|
+
"app": {
|
|
20
|
+
"type": "object",
|
|
21
|
+
"description": "Application metadata for UI context (name, description used in templates)",
|
|
22
|
+
"properties": {
|
|
23
|
+
"name": {
|
|
24
|
+
"type": "string",
|
|
25
|
+
"minLength": 1,
|
|
26
|
+
"description": "Application name for display in UI"
|
|
27
|
+
},
|
|
28
|
+
"description": {
|
|
29
|
+
"type": "string",
|
|
30
|
+
"description": "Application description for display in UI"
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
"additionalProperties": false
|
|
34
|
+
},
|
|
35
|
+
"routes": {
|
|
36
|
+
"type": "array",
|
|
37
|
+
"description": "Route definitions mapping URL paths to pages",
|
|
38
|
+
"minItems": 1,
|
|
39
|
+
"items": {
|
|
40
|
+
"$ref": "#/definitions/route"
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
"pages": {
|
|
44
|
+
"type": "object",
|
|
45
|
+
"description": "Page definitions keyed by page identifier",
|
|
46
|
+
"minProperties": 1,
|
|
47
|
+
"patternProperties": {
|
|
48
|
+
"^[a-z][a-z0-9-]*$": {
|
|
49
|
+
"$ref": "#/definitions/page"
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
"additionalProperties": false
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
"definitions": {
|
|
56
|
+
"route": {
|
|
57
|
+
"type": "object",
|
|
58
|
+
"required": [
|
|
59
|
+
"path",
|
|
60
|
+
"page"
|
|
61
|
+
],
|
|
62
|
+
"additionalProperties": false,
|
|
63
|
+
"properties": {
|
|
64
|
+
"path": {
|
|
65
|
+
"type": "string",
|
|
66
|
+
"minLength": 1,
|
|
67
|
+
"description": "URL path pattern. Use [param] for dynamic segments (e.g., /courses/[id])",
|
|
68
|
+
"pattern": "^/"
|
|
69
|
+
},
|
|
70
|
+
"page": {
|
|
71
|
+
"type": "string",
|
|
72
|
+
"minLength": 1,
|
|
73
|
+
"description": "Page identifier referencing a key in the pages object",
|
|
74
|
+
"pattern": "^[a-z][a-z0-9-]*$"
|
|
75
|
+
},
|
|
76
|
+
"title": {
|
|
77
|
+
"type": "string",
|
|
78
|
+
"description": "Page title for navigation and breadcrumbs"
|
|
79
|
+
},
|
|
80
|
+
"access": {
|
|
81
|
+
"$ref": "#/definitions/access"
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
},
|
|
85
|
+
"access": {
|
|
86
|
+
"description": "Access control for a route. Can be a simple string or a role/permission object.",
|
|
87
|
+
"oneOf": [
|
|
88
|
+
{
|
|
89
|
+
"type": "string",
|
|
90
|
+
"enum": [
|
|
91
|
+
"public",
|
|
92
|
+
"authenticated"
|
|
93
|
+
],
|
|
94
|
+
"description": "Simple access level"
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
"type": "object",
|
|
98
|
+
"description": "Role and/or permission-based access control",
|
|
99
|
+
"additionalProperties": false,
|
|
100
|
+
"properties": {
|
|
101
|
+
"roles": {
|
|
102
|
+
"type": "array",
|
|
103
|
+
"items": {
|
|
104
|
+
"type": "string"
|
|
105
|
+
},
|
|
106
|
+
"description": "Required roles (any match grants access)"
|
|
107
|
+
},
|
|
108
|
+
"permissions": {
|
|
109
|
+
"type": "array",
|
|
110
|
+
"items": {
|
|
111
|
+
"type": "string"
|
|
112
|
+
},
|
|
113
|
+
"description": "Required permissions (any match grants access)"
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
]
|
|
118
|
+
},
|
|
119
|
+
"page": {
|
|
120
|
+
"type": "object",
|
|
121
|
+
"required": [
|
|
122
|
+
"content"
|
|
123
|
+
],
|
|
124
|
+
"additionalProperties": false,
|
|
125
|
+
"properties": {
|
|
126
|
+
"layout": {
|
|
127
|
+
"$ref": "#/definitions/layout"
|
|
128
|
+
},
|
|
129
|
+
"content": {
|
|
130
|
+
"type": "array",
|
|
131
|
+
"description": "Ordered list of content blocks to render on the page",
|
|
132
|
+
"minItems": 1,
|
|
133
|
+
"items": {
|
|
134
|
+
"$ref": "#/definitions/contentBlock"
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
},
|
|
139
|
+
"layout": {
|
|
140
|
+
"type": "object",
|
|
141
|
+
"description": "Page layout structure",
|
|
142
|
+
"additionalProperties": false,
|
|
143
|
+
"properties": {
|
|
144
|
+
"header": {
|
|
145
|
+
"type": "boolean",
|
|
146
|
+
"description": "Show application header"
|
|
147
|
+
},
|
|
148
|
+
"navigation": {
|
|
149
|
+
"type": "boolean",
|
|
150
|
+
"description": "Show main navigation"
|
|
151
|
+
},
|
|
152
|
+
"sidebar": {
|
|
153
|
+
"type": "boolean",
|
|
154
|
+
"description": "Show sidebar"
|
|
155
|
+
},
|
|
156
|
+
"footer": {
|
|
157
|
+
"type": "boolean",
|
|
158
|
+
"description": "Show application footer"
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
},
|
|
162
|
+
"contentBlock": {
|
|
163
|
+
"type": "object",
|
|
164
|
+
"required": [
|
|
165
|
+
"type"
|
|
166
|
+
],
|
|
167
|
+
"description": "A content block rendered on a page",
|
|
168
|
+
"allOf": [
|
|
169
|
+
{
|
|
170
|
+
"if": {
|
|
171
|
+
"properties": { "type": { "const": "hero" } },
|
|
172
|
+
"required": ["type"]
|
|
173
|
+
},
|
|
174
|
+
"then": {
|
|
175
|
+
"additionalProperties": false,
|
|
176
|
+
"required": ["type", "title"],
|
|
177
|
+
"properties": {
|
|
178
|
+
"type": { "const": "hero" },
|
|
179
|
+
"title": {
|
|
180
|
+
"type": "string",
|
|
181
|
+
"description": "Hero title. Supports interpolation with {app.name}, {app.description}"
|
|
182
|
+
},
|
|
183
|
+
"subtitle": {
|
|
184
|
+
"type": "string",
|
|
185
|
+
"description": "Hero subtitle. Supports interpolation"
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
},
|
|
190
|
+
{
|
|
191
|
+
"if": {
|
|
192
|
+
"properties": { "type": { "const": "entity-list" } },
|
|
193
|
+
"required": ["type"]
|
|
194
|
+
},
|
|
195
|
+
"then": {
|
|
196
|
+
"additionalProperties": false,
|
|
197
|
+
"required": ["type", "entity"],
|
|
198
|
+
"properties": {
|
|
199
|
+
"type": { "const": "entity-list" },
|
|
200
|
+
"entity": {
|
|
201
|
+
"type": "string",
|
|
202
|
+
"description": "Entity name from types blueprint (PascalCase)",
|
|
203
|
+
"pattern": "^[A-Z][A-Za-z0-9]*$"
|
|
204
|
+
},
|
|
205
|
+
"title": {
|
|
206
|
+
"type": "string",
|
|
207
|
+
"description": "Section heading"
|
|
208
|
+
},
|
|
209
|
+
"display": {
|
|
210
|
+
"type": "string",
|
|
211
|
+
"enum": ["table", "grid", "list"],
|
|
212
|
+
"description": "Display mode for the list",
|
|
213
|
+
"default": "table"
|
|
214
|
+
},
|
|
215
|
+
"limit": {
|
|
216
|
+
"type": "integer",
|
|
217
|
+
"minimum": 1,
|
|
218
|
+
"description": "Maximum items to display"
|
|
219
|
+
},
|
|
220
|
+
"fields": {
|
|
221
|
+
"type": "array",
|
|
222
|
+
"items": { "type": "string" },
|
|
223
|
+
"description": "Fields to display. If omitted, auto-detected from entity metadata"
|
|
224
|
+
},
|
|
225
|
+
"filter": {
|
|
226
|
+
"$ref": "#/definitions/filter"
|
|
227
|
+
},
|
|
228
|
+
"sort": {
|
|
229
|
+
"$ref": "#/definitions/sort"
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
},
|
|
234
|
+
{
|
|
235
|
+
"if": {
|
|
236
|
+
"properties": { "type": { "const": "entity-detail" } },
|
|
237
|
+
"required": ["type"]
|
|
238
|
+
},
|
|
239
|
+
"then": {
|
|
240
|
+
"additionalProperties": false,
|
|
241
|
+
"required": ["type", "entity"],
|
|
242
|
+
"properties": {
|
|
243
|
+
"type": { "const": "entity-detail" },
|
|
244
|
+
"entity": {
|
|
245
|
+
"type": "string",
|
|
246
|
+
"description": "Entity name from types blueprint (PascalCase)",
|
|
247
|
+
"pattern": "^[A-Z][A-Za-z0-9]*$"
|
|
248
|
+
},
|
|
249
|
+
"title": {
|
|
250
|
+
"type": "string",
|
|
251
|
+
"description": "Section heading"
|
|
252
|
+
},
|
|
253
|
+
"fields": {
|
|
254
|
+
"type": "array",
|
|
255
|
+
"items": { "type": "string" },
|
|
256
|
+
"description": "Fields to display. If omitted, auto-detected from entity metadata"
|
|
257
|
+
},
|
|
258
|
+
"actions": {
|
|
259
|
+
"type": "array",
|
|
260
|
+
"items": {
|
|
261
|
+
"type": "string",
|
|
262
|
+
"enum": ["edit", "delete"]
|
|
263
|
+
},
|
|
264
|
+
"description": "Action buttons to show on the detail view"
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
},
|
|
269
|
+
{
|
|
270
|
+
"if": {
|
|
271
|
+
"properties": { "type": { "const": "entity-form" } },
|
|
272
|
+
"required": ["type"]
|
|
273
|
+
},
|
|
274
|
+
"then": {
|
|
275
|
+
"additionalProperties": false,
|
|
276
|
+
"required": ["type", "entity"],
|
|
277
|
+
"properties": {
|
|
278
|
+
"type": { "const": "entity-form" },
|
|
279
|
+
"entity": {
|
|
280
|
+
"type": "string",
|
|
281
|
+
"description": "Entity name from types blueprint (PascalCase)",
|
|
282
|
+
"pattern": "^[A-Z][A-Za-z0-9]*$"
|
|
283
|
+
},
|
|
284
|
+
"title": {
|
|
285
|
+
"type": "string",
|
|
286
|
+
"description": "Form heading"
|
|
287
|
+
},
|
|
288
|
+
"action": {
|
|
289
|
+
"type": "string",
|
|
290
|
+
"enum": ["create", "edit"],
|
|
291
|
+
"description": "Form action type",
|
|
292
|
+
"default": "create"
|
|
293
|
+
},
|
|
294
|
+
"fields": {
|
|
295
|
+
"type": "array",
|
|
296
|
+
"items": { "type": "string" },
|
|
297
|
+
"description": "Fields to include in form. If omitted, auto-detected from entity metadata"
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
],
|
|
303
|
+
"properties": {
|
|
304
|
+
"type": {
|
|
305
|
+
"type": "string",
|
|
306
|
+
"enum": ["hero", "entity-list", "entity-detail", "entity-form"],
|
|
307
|
+
"description": "Content block type"
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
},
|
|
311
|
+
"filter": {
|
|
312
|
+
"type": "object",
|
|
313
|
+
"description": "Filter to apply when loading entity data",
|
|
314
|
+
"required": ["field", "value"],
|
|
315
|
+
"additionalProperties": false,
|
|
316
|
+
"properties": {
|
|
317
|
+
"field": {
|
|
318
|
+
"type": "string",
|
|
319
|
+
"description": "Field name to filter on"
|
|
320
|
+
},
|
|
321
|
+
"value": {
|
|
322
|
+
"type": "string",
|
|
323
|
+
"description": "Filter value. Supports interpolation with route params (e.g., {courseId}, {course.id})"
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
},
|
|
327
|
+
"sort": {
|
|
328
|
+
"type": "object",
|
|
329
|
+
"description": "Default sort order for entity lists",
|
|
330
|
+
"required": ["field"],
|
|
331
|
+
"additionalProperties": false,
|
|
332
|
+
"properties": {
|
|
333
|
+
"field": {
|
|
334
|
+
"type": "string",
|
|
335
|
+
"description": "Field name to sort by"
|
|
336
|
+
},
|
|
337
|
+
"direction": {
|
|
338
|
+
"type": "string",
|
|
339
|
+
"enum": ["asc", "desc"],
|
|
340
|
+
"default": "asc",
|
|
341
|
+
"description": "Sort direction"
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
}
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import Ajv from 'ajv';
|
|
2
|
+
import addFormats from 'ajv-formats';
|
|
3
|
+
import { typesSchema, rolesSchema, appSchema, uiSchema, componentsSchema, themeSchema } from '../schemas/index.js';
|
|
4
|
+
|
|
5
|
+
// Initialize AJV
|
|
6
|
+
const ajv = new Ajv({ allErrors: true, strict: false });
|
|
7
|
+
addFormats(ajv);
|
|
8
|
+
|
|
9
|
+
// Compile validators
|
|
10
|
+
const validators = {
|
|
11
|
+
types: ajv.compile(typesSchema),
|
|
12
|
+
roles: ajv.compile(rolesSchema),
|
|
13
|
+
app: ajv.compile(appSchema),
|
|
14
|
+
ui: ajv.compile(uiSchema),
|
|
15
|
+
components: ajv.compile(componentsSchema),
|
|
16
|
+
theme: ajv.compile(themeSchema)
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Validate a blueprint against its schema
|
|
21
|
+
* @param {any} data - Parsed JSON data to validate
|
|
22
|
+
* @param {'types' | 'roles' | 'app' | 'ui' | 'components' | 'theme'} type - Schema type to validate against
|
|
23
|
+
* @returns {{ valid: boolean, errors: Array }}
|
|
24
|
+
*/
|
|
25
|
+
export function validateBlueprint(data, type) {
|
|
26
|
+
const validate = validators[type];
|
|
27
|
+
if (!validate) {
|
|
28
|
+
throw new Error(`Unknown schema type: ${type}`);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const isValid = validate(data);
|
|
32
|
+
|
|
33
|
+
return {
|
|
34
|
+
valid: isValid,
|
|
35
|
+
errors: validate.errors || []
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Get the raw JSON schemas
|
|
41
|
+
* @returns {{ types: object, roles: object, app: object, ui: object }}
|
|
42
|
+
*/
|
|
43
|
+
export function getSchemas() {
|
|
44
|
+
return {
|
|
45
|
+
types: typesSchema,
|
|
46
|
+
roles: rolesSchema,
|
|
47
|
+
app: appSchema,
|
|
48
|
+
ui: uiSchema,
|
|
49
|
+
components: componentsSchema,
|
|
50
|
+
theme: themeSchema
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Format validation errors for display
|
|
56
|
+
* @param {Array} errors - AJV validation errors
|
|
57
|
+
* @returns {string} Formatted error message
|
|
58
|
+
*/
|
|
59
|
+
export function formatValidationErrors(errors) {
|
|
60
|
+
if (!errors || errors.length === 0) {
|
|
61
|
+
return 'No errors';
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return errors
|
|
65
|
+
.map(err => {
|
|
66
|
+
const path = err.instancePath || '/';
|
|
67
|
+
const message = err.message;
|
|
68
|
+
const params = err.params ? JSON.stringify(err.params) : '';
|
|
69
|
+
return `${path}: ${message} ${params}`.trim();
|
|
70
|
+
})
|
|
71
|
+
.join('\n');
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Parse a JSON string and validate against a schema
|
|
76
|
+
* @param {string} jsonString - Raw JSON string to parse and validate
|
|
77
|
+
* @param {'types' | 'roles' | 'app' | 'ui' | 'components' | 'theme'} type - Schema type to validate against
|
|
78
|
+
* @returns {{ valid: boolean, errors: Array, data: object|null }}
|
|
79
|
+
*/
|
|
80
|
+
export function validateFromJSON(jsonString, type) {
|
|
81
|
+
let data;
|
|
82
|
+
try {
|
|
83
|
+
data = JSON.parse(jsonString);
|
|
84
|
+
} catch (err) {
|
|
85
|
+
return {
|
|
86
|
+
valid: false,
|
|
87
|
+
errors: [{ instancePath: '/', message: `Invalid JSON: ${err.message}`, params: {} }],
|
|
88
|
+
data: null
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const result = validateBlueprint(data, type);
|
|
93
|
+
return { ...result, data };
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Convert a blueprint object to YAML string for display purposes
|
|
98
|
+
* @param {object} data - Blueprint data object
|
|
99
|
+
* @param {number} indent - Indentation level (default: 2)
|
|
100
|
+
* @returns {string} YAML-formatted string
|
|
101
|
+
*/
|
|
102
|
+
export function toYAML(data, indent = 2) {
|
|
103
|
+
return jsonToYaml(data, 0, indent);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Simple JSON-to-YAML converter for display purposes.
|
|
108
|
+
* No external dependencies required.
|
|
109
|
+
*/
|
|
110
|
+
function jsonToYaml(value, level, indent) {
|
|
111
|
+
const pad = ' '.repeat(level * indent);
|
|
112
|
+
const childPad = ' '.repeat((level + 1) * indent);
|
|
113
|
+
|
|
114
|
+
if (value === null || value === undefined) {
|
|
115
|
+
return 'null';
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
if (typeof value === 'boolean' || typeof value === 'number') {
|
|
119
|
+
return String(value);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
if (typeof value === 'string') {
|
|
123
|
+
if (value.includes('\n') || value.includes(': ') || value.includes('#') ||
|
|
124
|
+
value.startsWith('{') || value.startsWith('[') || value.startsWith('"') ||
|
|
125
|
+
value.startsWith("'") || value === '' || value === 'true' || value === 'false' ||
|
|
126
|
+
value === 'null' || /^\d+(\.\d+)?$/.test(value)) {
|
|
127
|
+
return JSON.stringify(value);
|
|
128
|
+
}
|
|
129
|
+
return value;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
if (Array.isArray(value)) {
|
|
133
|
+
if (value.length === 0) return '[]';
|
|
134
|
+
// Check if all items are simple (non-object) values
|
|
135
|
+
const allSimple = value.every(item => typeof item !== 'object' || item === null);
|
|
136
|
+
if (allSimple) {
|
|
137
|
+
const items = value.map(item => jsonToYaml(item, 0, indent));
|
|
138
|
+
if (items.join(', ').length < 80) {
|
|
139
|
+
return `[${items.join(', ')}]`;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
return '\n' + value.map(item => {
|
|
143
|
+
if (typeof item === 'object' && item !== null && !Array.isArray(item)) {
|
|
144
|
+
const entries = Object.entries(item);
|
|
145
|
+
if (entries.length === 0) return `${childPad}- {}`;
|
|
146
|
+
const [firstKey, firstVal] = entries[0];
|
|
147
|
+
const firstLine = `${childPad}- ${firstKey}: ${jsonToYaml(firstVal, level + 2, indent)}`;
|
|
148
|
+
const rest = entries.slice(1).map(([k, v]) => {
|
|
149
|
+
return `${childPad} ${k}: ${jsonToYaml(v, level + 2, indent)}`;
|
|
150
|
+
});
|
|
151
|
+
return [firstLine, ...rest].join('\n');
|
|
152
|
+
}
|
|
153
|
+
return `${childPad}- ${jsonToYaml(item, level + 1, indent)}`;
|
|
154
|
+
}).join('\n');
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
if (typeof value === 'object') {
|
|
158
|
+
const entries = Object.entries(value);
|
|
159
|
+
if (entries.length === 0) return '{}';
|
|
160
|
+
return '\n' + entries.map(([key, val]) => {
|
|
161
|
+
const rendered = jsonToYaml(val, level + 1, indent);
|
|
162
|
+
if (typeof val === 'object' && val !== null && !Array.isArray(val) && Object.keys(val).length > 0) {
|
|
163
|
+
return `${childPad}${key}:${rendered}`;
|
|
164
|
+
}
|
|
165
|
+
if (Array.isArray(val) && val.length > 0 && val.some(item => typeof item === 'object' && item !== null)) {
|
|
166
|
+
return `${childPad}${key}:${rendered}`;
|
|
167
|
+
}
|
|
168
|
+
return `${childPad}${key}: ${rendered}`;
|
|
169
|
+
}).join('\n');
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
return String(value);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
export { typesSchema, rolesSchema, appSchema, uiSchema, componentsSchema, themeSchema, validators };
|