@defra/forms-model 3.0.430 → 3.0.431
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 +163 -1
- package/dist/module/common/pagination/index.js +2 -2
- package/dist/module/common/pagination/index.js.map +1 -1
- package/dist/module/common/search/index.js +4 -4
- package/dist/module/common/search/index.js.map +1 -1
- package/dist/module/common/sorting/index.js +2 -2
- package/dist/module/common/sorting/index.js.map +1 -1
- package/dist/module/form/form-definition/index.js +156 -156
- package/dist/module/form/form-definition/index.js.map +1 -1
- package/dist/module/form/form-editor/index.js +42 -42
- package/dist/module/form/form-editor/index.js.map +1 -1
- package/dist/module/form/form-manager/index.js +3 -3
- package/dist/module/form/form-manager/index.js.map +1 -1
- package/dist/module/form/form-metadata/index.js +34 -34
- package/dist/module/form/form-metadata/index.js.map +1 -1
- package/dist/module/form/form-submission/index.js +13 -13
- package/dist/module/form/form-submission/index.js.map +1 -1
- package/dist/module/types/joi-to-json.d.js +2 -0
- package/dist/module/types/joi-to-json.d.js.map +1 -0
- package/dist/types/common/pagination/index.d.ts.map +1 -1
- package/dist/types/common/search/index.d.ts.map +1 -1
- package/dist/types/common/sorting/index.d.ts.map +1 -1
- package/dist/types/form/form-definition/index.d.ts.map +1 -1
- package/dist/types/form/form-editor/index.d.ts +12 -12
- package/dist/types/form/form-editor/index.d.ts.map +1 -1
- package/dist/types/form/form-manager/index.d.ts.map +1 -1
- package/dist/types/form/form-metadata/index.d.ts.map +1 -1
- package/dist/types/form/form-submission/index.d.ts.map +1 -1
- package/package.json +6 -4
- package/scripts/generate-schemas.js +238 -0
- package/scripts/schema-modules/constants.js +39 -0
- package/scripts/schema-modules/schema-processors.js +109 -0
- package/scripts/schema-modules/schema-simplifiers.js +351 -0
- package/scripts/schema-modules/title-processors.js +327 -0
- package/scripts/schema-modules/types.js +21 -0
- package/scripts/schema-modules/utils.js +41 -0
- package/src/common/pagination/index.ts +8 -1
- package/src/common/search/index.ts +17 -3
- package/src/common/sorting/index.ts +8 -2
- package/src/form/form-definition/index.ts +567 -238
- package/src/form/form-editor/index.ts +202 -29
- package/src/form/form-manager/index.ts +11 -2
- package/src/form/form-metadata/index.ts +118 -40
- package/src/form/form-submission/index.ts +33 -10
- package/src/types/joi-to-json.d.ts +15 -0
@@ -0,0 +1,351 @@
|
|
1
|
+
import {
|
2
|
+
CONDITION_TYPES,
|
3
|
+
DESCRIPTIONS,
|
4
|
+
PATH_SEGMENTS,
|
5
|
+
VALUE_TYPES
|
6
|
+
} from './constants.js'
|
7
|
+
import { fixConditionTitles } from './schema-processors.js'
|
8
|
+
|
9
|
+
/**
|
10
|
+
* Handles reference-specific titles
|
11
|
+
* @param {SchemaObject} result - Schema to process
|
12
|
+
*/
|
13
|
+
export function handleReferenceSpecificTitles(result) {
|
14
|
+
if (result.$ref?.includes('conditionGroupSchema')) {
|
15
|
+
result.title = CONDITION_TYPES.NESTED_GROUP
|
16
|
+
result.description = DESCRIPTIONS.NESTED_CONDITION_GROUP
|
17
|
+
}
|
18
|
+
}
|
19
|
+
|
20
|
+
/**
|
21
|
+
* Simplifies anyOf validations that just enumerate types
|
22
|
+
* @param {SchemaObject} result - Schema to process
|
23
|
+
*/
|
24
|
+
export function simplifyTypeEnumerations(result) {
|
25
|
+
if (!result.anyOf || !Array.isArray(result.anyOf)) {
|
26
|
+
return
|
27
|
+
}
|
28
|
+
|
29
|
+
/** @type {Array<string>} */
|
30
|
+
const types = result.anyOf
|
31
|
+
.map(
|
32
|
+
/**
|
33
|
+
* @param {SchemaObject} item
|
34
|
+
* @returns {string|undefined}
|
35
|
+
*/
|
36
|
+
(item) => {
|
37
|
+
return item.type
|
38
|
+
}
|
39
|
+
)
|
40
|
+
.filter(/** @returns {val is string} */ (val) => Boolean(val))
|
41
|
+
|
42
|
+
const MIN_TYPES_FOR_ENUMERATION = 3
|
43
|
+
|
44
|
+
const isTypeEnumeration =
|
45
|
+
types.length >= MIN_TYPES_FOR_ENUMERATION &&
|
46
|
+
types.some((t) =>
|
47
|
+
['string', 'number', 'boolean', 'array', 'object'].includes(t)
|
48
|
+
)
|
49
|
+
|
50
|
+
if (isTypeEnumeration) {
|
51
|
+
delete result.anyOf
|
52
|
+
if (
|
53
|
+
!result.description ||
|
54
|
+
result.description.indexOf('value type') === -1
|
55
|
+
) {
|
56
|
+
result.description = `${String(result.description || '')}${result.description ? '\n\n' : ''}Value can be of various types (${types.join(', ')}).`
|
57
|
+
}
|
58
|
+
}
|
59
|
+
}
|
60
|
+
|
61
|
+
/**
|
62
|
+
* Improves titles for condition items
|
63
|
+
* @param {SchemaObject} result - Schema to process
|
64
|
+
* @param {string} parentPath - Path to current schema
|
65
|
+
*/
|
66
|
+
export function improveConditionItemTitles(result, parentPath) {
|
67
|
+
if (!parentPath.includes(`/${PATH_SEGMENTS.CONDITIONS}`) || !result.anyOf) {
|
68
|
+
return
|
69
|
+
}
|
70
|
+
|
71
|
+
result.anyOf.forEach(
|
72
|
+
/** @param {SchemaObject} item */ (item) => {
|
73
|
+
if (item.title?.includes('Item (object)') && item.properties) {
|
74
|
+
if (item.properties.field) {
|
75
|
+
item.title = CONDITION_TYPES.DEFINITION
|
76
|
+
} else if (item.properties.conditionName) {
|
77
|
+
item.title = CONDITION_TYPES.REFERENCE
|
78
|
+
} else if (item.properties.conditions) {
|
79
|
+
item.title = CONDITION_TYPES.NESTED_GROUP
|
80
|
+
} else {
|
81
|
+
item.title = `Unknown Condition Item Type`
|
82
|
+
}
|
83
|
+
}
|
84
|
+
}
|
85
|
+
)
|
86
|
+
}
|
87
|
+
|
88
|
+
/**
|
89
|
+
* Improves titles for value objects in conditions
|
90
|
+
* @param {SchemaObject} result - Schema to process
|
91
|
+
* @param {string} parentPath - Path to current schema
|
92
|
+
*/
|
93
|
+
export function improveValueObjectTitles(result, parentPath) {
|
94
|
+
if (!parentPath.includes('/value') || !result.anyOf) {
|
95
|
+
return
|
96
|
+
}
|
97
|
+
|
98
|
+
result.anyOf.forEach(
|
99
|
+
/** @param {SchemaObject} item */ (item) => {
|
100
|
+
if (!item.properties) {
|
101
|
+
return
|
102
|
+
}
|
103
|
+
|
104
|
+
if (
|
105
|
+
item.properties.period &&
|
106
|
+
item.properties.unit &&
|
107
|
+
item.properties.direction
|
108
|
+
) {
|
109
|
+
item.title = VALUE_TYPES.RELATIVE_DATE
|
110
|
+
} else if (
|
111
|
+
item.properties.value &&
|
112
|
+
item.properties.type &&
|
113
|
+
item.properties.display
|
114
|
+
) {
|
115
|
+
item.title = VALUE_TYPES.STATIC
|
116
|
+
} else {
|
117
|
+
item.title = 'Unknown Value Type'
|
118
|
+
}
|
119
|
+
}
|
120
|
+
)
|
121
|
+
}
|
122
|
+
|
123
|
+
/**
|
124
|
+
* Improves descriptions for condition operators
|
125
|
+
* @param {SchemaObject} result - Schema to process
|
126
|
+
* @param {string} parentPath - Path to current schema
|
127
|
+
*/
|
128
|
+
export function improveOperatorDescriptions(result, parentPath) {
|
129
|
+
if (
|
130
|
+
parentPath.endsWith('/operator') &&
|
131
|
+
parentPath.includes(`/${PATH_SEGMENTS.CONDITIONS}`)
|
132
|
+
) {
|
133
|
+
result.description =
|
134
|
+
'Comparison operator: equals, notEquals, contains, notContains, greaterThan, lessThan, isEmpty, isNotEmpty'
|
135
|
+
}
|
136
|
+
}
|
137
|
+
|
138
|
+
/**
|
139
|
+
* Adds examples to recursive condition groups
|
140
|
+
* @param {SchemaObject} result - Schema to process
|
141
|
+
* @param {string} parentPath - Path to current schema
|
142
|
+
*/
|
143
|
+
export function addExamplesToConditionGroups(result, parentPath) {
|
144
|
+
if (
|
145
|
+
result.title !== 'Condition Group Schema' &&
|
146
|
+
!parentPath.includes('conditionGroupSchema')
|
147
|
+
) {
|
148
|
+
return
|
149
|
+
}
|
150
|
+
|
151
|
+
if (result.description) {
|
152
|
+
result.description = `${result.description} Note: This structure can be nested multiple levels deep for complex condition logic.`
|
153
|
+
}
|
154
|
+
|
155
|
+
result.examples = [
|
156
|
+
{
|
157
|
+
conditions: [
|
158
|
+
{
|
159
|
+
field: {
|
160
|
+
name: 'farmType',
|
161
|
+
type: 'string',
|
162
|
+
display: 'Type of farm'
|
163
|
+
},
|
164
|
+
operator: 'equals',
|
165
|
+
value: {
|
166
|
+
type: 'string',
|
167
|
+
value: 'livestock',
|
168
|
+
display: 'Livestock farm'
|
169
|
+
},
|
170
|
+
coordinator: 'AND'
|
171
|
+
},
|
172
|
+
{
|
173
|
+
conditionName: 'hasEnvironmentalPermit',
|
174
|
+
conditionDisplayName: 'Has an environmental permit',
|
175
|
+
coordinator: 'OR'
|
176
|
+
}
|
177
|
+
]
|
178
|
+
}
|
179
|
+
]
|
180
|
+
}
|
181
|
+
|
182
|
+
/**
|
183
|
+
* Handles repeat property special case
|
184
|
+
* @param {SchemaObject} result - Schema to process
|
185
|
+
* @param {string} parentPath - Path to current schema
|
186
|
+
* @returns {SchemaObject|null} Processed schema or null if no special handling needed
|
187
|
+
*/
|
188
|
+
export function handleRepeatProperty(result, parentPath) {
|
189
|
+
if (
|
190
|
+
!parentPath.endsWith('/repeat') ||
|
191
|
+
!result.oneOf ||
|
192
|
+
result.oneOf.length <= 1
|
193
|
+
) {
|
194
|
+
return null
|
195
|
+
}
|
196
|
+
|
197
|
+
const mainOption = result.oneOf[0]
|
198
|
+
|
199
|
+
mainOption.description = `${mainOption.description || ''}\n\nNOTE: This configuration is only used when the 'controller' property is set to 'RepeatPageController'. Otherwise, this property should not be specified.`
|
200
|
+
|
201
|
+
delete result.oneOf
|
202
|
+
delete result.anyOf
|
203
|
+
|
204
|
+
Object.assign(result, mainOption)
|
205
|
+
return result
|
206
|
+
}
|
207
|
+
|
208
|
+
/**
|
209
|
+
* Handles name/title fields
|
210
|
+
* @param {SchemaObject} result - Schema to process
|
211
|
+
* @param {string} parentPath - Path to current schema
|
212
|
+
*/
|
213
|
+
export function handleNameTitleFields(result, parentPath) {
|
214
|
+
if (
|
215
|
+
(parentPath.endsWith('/name') || parentPath.endsWith('/title')) &&
|
216
|
+
result.oneOf &&
|
217
|
+
result.oneOf.length > 1 &&
|
218
|
+
result.anyOf
|
219
|
+
) {
|
220
|
+
delete result.anyOf
|
221
|
+
}
|
222
|
+
}
|
223
|
+
|
224
|
+
/**
|
225
|
+
* Improves titles for list items
|
226
|
+
* @param {SchemaObject} result - Schema to process
|
227
|
+
* @param {string} parentPath - Path to current schema
|
228
|
+
*/
|
229
|
+
export function improveListItemTitles(result, parentPath) {
|
230
|
+
if (
|
231
|
+
!parentPath.endsWith(`/${PATH_SEGMENTS.ITEMS}`) ||
|
232
|
+
!result.oneOf ||
|
233
|
+
result.oneOf.length <= 1
|
234
|
+
) {
|
235
|
+
return
|
236
|
+
}
|
237
|
+
|
238
|
+
result.oneOf.forEach(
|
239
|
+
/** @param {SchemaObject} option */ (option) => {
|
240
|
+
if (option.description?.includes('string values')) {
|
241
|
+
option.title = 'String List Items'
|
242
|
+
} else if (option.description?.includes('numeric values')) {
|
243
|
+
option.title = 'Number List Items'
|
244
|
+
} else {
|
245
|
+
option.title = 'Generic List Items'
|
246
|
+
}
|
247
|
+
}
|
248
|
+
)
|
249
|
+
|
250
|
+
if (result.anyOf) {
|
251
|
+
delete result.anyOf
|
252
|
+
}
|
253
|
+
}
|
254
|
+
|
255
|
+
/**
|
256
|
+
* Simplifies documentation for condition items in arrays
|
257
|
+
* @param {SchemaObject} result - Schema to process
|
258
|
+
* @param {string} parentPath - Path to current schema
|
259
|
+
*/
|
260
|
+
export function simplifyConditionArrays(result, parentPath) {
|
261
|
+
if (
|
262
|
+
!parentPath.includes(
|
263
|
+
`${PATH_SEGMENTS.CONDITIONS}/${PATH_SEGMENTS.ITEMS}`
|
264
|
+
) &&
|
265
|
+
!parentPath.endsWith(`/${PATH_SEGMENTS.CONDITIONS}`)
|
266
|
+
) {
|
267
|
+
return
|
268
|
+
}
|
269
|
+
|
270
|
+
if (result.items?.anyOf) {
|
271
|
+
result.description = `${result.description || ''}\n\nElements can be direct conditions, references to named conditions, or nested condition groups. This structure allows building complex logical expressions with AND/OR operators.`
|
272
|
+
}
|
273
|
+
}
|
274
|
+
|
275
|
+
/**
|
276
|
+
* Recursively processes properties of a schema
|
277
|
+
* @param {SchemaObject} result - Schema to process
|
278
|
+
* @param {string} parentPath - Path to current schema
|
279
|
+
*/
|
280
|
+
export function processNestedSchemas(result, parentPath) {
|
281
|
+
if (result.properties) {
|
282
|
+
Object.entries(result.properties).forEach(([propName, propSchema]) => {
|
283
|
+
result.properties[propName] = simplifyForDocs(
|
284
|
+
propSchema,
|
285
|
+
`${parentPath}/${PATH_SEGMENTS.PROPERTIES}/${propName}`
|
286
|
+
)
|
287
|
+
})
|
288
|
+
}
|
289
|
+
|
290
|
+
if (result.items) {
|
291
|
+
if (Array.isArray(result.items)) {
|
292
|
+
result.items = result.items.map((item, idx) =>
|
293
|
+
simplifyForDocs(item, `${parentPath}/${PATH_SEGMENTS.ITEMS}/${idx}`)
|
294
|
+
)
|
295
|
+
} else {
|
296
|
+
result.items = simplifyForDocs(
|
297
|
+
result.items,
|
298
|
+
`${parentPath}/${PATH_SEGMENTS.ITEMS}`
|
299
|
+
)
|
300
|
+
}
|
301
|
+
}
|
302
|
+
|
303
|
+
;['oneOf', 'anyOf', 'allOf'].forEach((keyword) => {
|
304
|
+
if (result[keyword] && Array.isArray(result[keyword])) {
|
305
|
+
result[keyword] = result[keyword].map((subSchema, idx) =>
|
306
|
+
simplifyForDocs(subSchema, `${parentPath}/${keyword}/${idx}`)
|
307
|
+
)
|
308
|
+
}
|
309
|
+
})
|
310
|
+
}
|
311
|
+
|
312
|
+
/**
|
313
|
+
* Simplifies a schema for documentation purposes by removing unnecessary internal validation structures
|
314
|
+
* @param {SchemaObject} schema - The schema to simplify
|
315
|
+
* @param {string} parentPath - Path to the current schema location
|
316
|
+
* @returns {SchemaObject} Simplified schema
|
317
|
+
*/
|
318
|
+
export function simplifyForDocs(schema, parentPath = '') {
|
319
|
+
if (typeof schema !== 'object') {
|
320
|
+
return schema
|
321
|
+
}
|
322
|
+
|
323
|
+
/** @type {SchemaObject} */
|
324
|
+
const result = JSON.parse(JSON.stringify(schema))
|
325
|
+
|
326
|
+
handleReferenceSpecificTitles(result)
|
327
|
+
simplifyTypeEnumerations(result)
|
328
|
+
improveConditionItemTitles(result, parentPath)
|
329
|
+
improveValueObjectTitles(result, parentPath)
|
330
|
+
improveOperatorDescriptions(result, parentPath)
|
331
|
+
addExamplesToConditionGroups(result, parentPath)
|
332
|
+
|
333
|
+
const specialResult = handleRepeatProperty(result, parentPath)
|
334
|
+
if (specialResult) {
|
335
|
+
return specialResult
|
336
|
+
}
|
337
|
+
|
338
|
+
handleNameTitleFields(result, parentPath)
|
339
|
+
improveListItemTitles(result, parentPath)
|
340
|
+
simplifyConditionArrays(result, parentPath)
|
341
|
+
|
342
|
+
fixConditionTitles(result)
|
343
|
+
|
344
|
+
processNestedSchemas(result, parentPath)
|
345
|
+
|
346
|
+
return result
|
347
|
+
}
|
348
|
+
|
349
|
+
/**
|
350
|
+
* @import { SchemaObject } from './types.js'
|
351
|
+
*/
|
@@ -0,0 +1,327 @@
|
|
1
|
+
import { formatPropertyName } from './utils.js'
|
2
|
+
|
3
|
+
/**
|
4
|
+
* Sets a title on schema object if missing
|
5
|
+
* @param {SchemaObject} schema - Schema object to update
|
6
|
+
* @param {string} parentName - Parent object name for context
|
7
|
+
*/
|
8
|
+
export function setSchemaTitle(schema, parentName) {
|
9
|
+
if (schema.title) {
|
10
|
+
return
|
11
|
+
} else if (schema.description && typeof schema.description === 'string') {
|
12
|
+
schema.title = schema.description.split('.')[0].trim()
|
13
|
+
} else if (parentName) {
|
14
|
+
schema.title = formatPropertyName(parentName)
|
15
|
+
} else if (schema.type) {
|
16
|
+
schema.title = formatPropertyName(String(schema.type))
|
17
|
+
} else {
|
18
|
+
schema.title = 'Unknown Schema'
|
19
|
+
}
|
20
|
+
}
|
21
|
+
|
22
|
+
/**
|
23
|
+
* Sets titles for repeat-related schemas
|
24
|
+
* @param {SchemaObject} subSchema - Schema to update
|
25
|
+
* @param {number} index - Index in array
|
26
|
+
*/
|
27
|
+
export function setRepeatTitles(subSchema, index) {
|
28
|
+
if (index === 0) {
|
29
|
+
subSchema.title = 'Repeat Configuration'
|
30
|
+
subSchema.description =
|
31
|
+
'Configuration for repeatable pages, used with RepeatPageController. ' +
|
32
|
+
'Defines how repeatable sections behave including min/max repetitions and display options.'
|
33
|
+
} else {
|
34
|
+
subSchema.title = 'Alternative Validation'
|
35
|
+
subSchema.description =
|
36
|
+
'Alternative validation schema used internally. Not typically configured directly.'
|
37
|
+
}
|
38
|
+
}
|
39
|
+
|
40
|
+
/**
|
41
|
+
* Sets titles for component name schemas
|
42
|
+
* @param {SchemaObject} subSchema - Schema to update
|
43
|
+
* @param {number} index - Index in array
|
44
|
+
*/
|
45
|
+
export function setNameTitles(subSchema, index) {
|
46
|
+
if (index === 0) {
|
47
|
+
subSchema.title = 'Display Component Name'
|
48
|
+
subSchema.description =
|
49
|
+
'Name format for display-only components like HTML, Markdown, etc.'
|
50
|
+
} else {
|
51
|
+
subSchema.title = 'Input Component Name'
|
52
|
+
subSchema.description =
|
53
|
+
'Name format for input components that collect user data.'
|
54
|
+
}
|
55
|
+
}
|
56
|
+
|
57
|
+
/**
|
58
|
+
* Sets titles for title-related schemas
|
59
|
+
* @param {SchemaObject} subSchema - Schema to update
|
60
|
+
* @param {number} index - Index in array
|
61
|
+
*/
|
62
|
+
export function setTitleTitles(subSchema, index) {
|
63
|
+
if (index === 0) {
|
64
|
+
subSchema.title = 'Display Component Title'
|
65
|
+
subSchema.description = 'Title format for display-only components.'
|
66
|
+
} else {
|
67
|
+
subSchema.title = 'Input Component Title'
|
68
|
+
subSchema.description = 'Title displayed above input components.'
|
69
|
+
}
|
70
|
+
}
|
71
|
+
|
72
|
+
/**
|
73
|
+
* Sets titles for pages schemas
|
74
|
+
* @param {SchemaObject} subSchema - Schema to update
|
75
|
+
* @param {number} index - Index in array
|
76
|
+
*/
|
77
|
+
export function setPagesTitles(subSchema, index) {
|
78
|
+
if (index === 0) {
|
79
|
+
subSchema.title = 'V2 Pages'
|
80
|
+
subSchema.description =
|
81
|
+
'Pages definition for forms using engine version V2.'
|
82
|
+
} else {
|
83
|
+
subSchema.title = 'V1 Pages'
|
84
|
+
subSchema.description =
|
85
|
+
'Pages definition for forms using engine version V1.'
|
86
|
+
}
|
87
|
+
}
|
88
|
+
|
89
|
+
/**
|
90
|
+
* Sets specific titles based on parent name and keyword
|
91
|
+
* @param {SchemaObject} subSchema - The schema to process
|
92
|
+
* @param {string} parentName - Parent name
|
93
|
+
* @param {string} keyword - oneOf, anyOf, or allOf
|
94
|
+
* @param {number} index - Index in the array
|
95
|
+
* @returns {boolean} Whether a specific title was set
|
96
|
+
*/
|
97
|
+
export function setSpecificTitle(subSchema, parentName, keyword, index) {
|
98
|
+
if (parentName === 'repeat' && keyword === 'oneOf') {
|
99
|
+
setRepeatTitles(subSchema, index)
|
100
|
+
return true
|
101
|
+
}
|
102
|
+
|
103
|
+
if (parentName === 'name' && keyword === 'oneOf') {
|
104
|
+
setNameTitles(subSchema, index)
|
105
|
+
return true
|
106
|
+
}
|
107
|
+
|
108
|
+
if (parentName === 'title' && keyword === 'oneOf') {
|
109
|
+
setTitleTitles(subSchema, index)
|
110
|
+
return true
|
111
|
+
}
|
112
|
+
|
113
|
+
if (parentName === 'pages' && keyword === 'oneOf') {
|
114
|
+
setPagesTitles(subSchema, index)
|
115
|
+
return true
|
116
|
+
}
|
117
|
+
|
118
|
+
return false
|
119
|
+
}
|
120
|
+
|
121
|
+
/**
|
122
|
+
* Sets title for alternative validation schemas
|
123
|
+
* @param {SchemaObject} subSchema - The schema to process
|
124
|
+
* @param {string} parentName - Parent name
|
125
|
+
* @param {string} keyword - oneOf, anyOf, or allOf
|
126
|
+
*/
|
127
|
+
export function setAlternativeValidationTitle(subSchema, parentName, keyword) {
|
128
|
+
if (
|
129
|
+
keyword === 'anyOf' &&
|
130
|
+
parentName.includes('Alternative Validation') &&
|
131
|
+
subSchema.type
|
132
|
+
) {
|
133
|
+
subSchema.title = `${subSchema.type} Type`
|
134
|
+
subSchema.description = `**INTERNAL VALIDATION ONLY** - This is an internal schema structure used for validation purposes.
|
135
|
+
When using the ${parentName} property, you should only configure the Repeat Configuration structure
|
136
|
+
when controller is set to "RepeatPageController".`
|
137
|
+
}
|
138
|
+
}
|
139
|
+
|
140
|
+
/**
|
141
|
+
* Handles specialized title setting for oneOf/anyOf cases
|
142
|
+
* @param {SchemaObject} subSchema - The schema to process
|
143
|
+
* @param {string} parentName - Parent name
|
144
|
+
* @param {string} keyword - oneOf, anyOf, or allOf
|
145
|
+
* @param {number} index - Index in the array
|
146
|
+
*/
|
147
|
+
export function handleSpecialTitles(subSchema, parentName, keyword, index) {
|
148
|
+
if (subSchema.title) {
|
149
|
+
return
|
150
|
+
}
|
151
|
+
|
152
|
+
if (!setSpecificTitle(subSchema, parentName, keyword, index)) {
|
153
|
+
if (subSchema.type) {
|
154
|
+
subSchema.title = `${formatPropertyName(parentName)} (${subSchema.type})`
|
155
|
+
} else {
|
156
|
+
subSchema.title = `${formatPropertyName(parentName)} Variant ${index + 1}`
|
157
|
+
}
|
158
|
+
}
|
159
|
+
|
160
|
+
setAlternativeValidationTitle(subSchema, parentName, keyword)
|
161
|
+
}
|
162
|
+
|
163
|
+
/**
|
164
|
+
* Process schema properties
|
165
|
+
* @param {SchemaObject} schema - Schema to process
|
166
|
+
*/
|
167
|
+
export function processProperties(schema) {
|
168
|
+
if (!schema.properties || typeof schema.properties !== 'object') {
|
169
|
+
return
|
170
|
+
}
|
171
|
+
|
172
|
+
const entries = Object.entries(schema.properties)
|
173
|
+
|
174
|
+
entries.forEach(([propName, propSchema]) => {
|
175
|
+
if (!propSchema.title) {
|
176
|
+
propSchema.title = formatPropertyName(propName)
|
177
|
+
}
|
178
|
+
|
179
|
+
if (!propSchema.description && propSchema.title) {
|
180
|
+
propSchema.description = `The ${propSchema.title.toLowerCase()} value.`
|
181
|
+
}
|
182
|
+
|
183
|
+
addTitles(propSchema, propName)
|
184
|
+
})
|
185
|
+
}
|
186
|
+
|
187
|
+
/**
|
188
|
+
* Process array items in schema
|
189
|
+
* @param {SchemaObject} schema - Schema to process
|
190
|
+
* @param {string} parentName - Parent object name
|
191
|
+
*/
|
192
|
+
export function processArrayItems(schema, parentName) {
|
193
|
+
if (!schema.items) {
|
194
|
+
return
|
195
|
+
}
|
196
|
+
|
197
|
+
if (Array.isArray(schema.items)) {
|
198
|
+
schema.items.forEach((item, index) => {
|
199
|
+
if (!item.title) {
|
200
|
+
item.title = `${formatPropertyName(parentName)} Item ${index + 1}`
|
201
|
+
}
|
202
|
+
addTitles(item, item.title)
|
203
|
+
})
|
204
|
+
} else {
|
205
|
+
if (!schema.items.title) {
|
206
|
+
schema.items.title = `${formatPropertyName(parentName)} Item`
|
207
|
+
}
|
208
|
+
addTitles(schema.items, schema.items.title)
|
209
|
+
}
|
210
|
+
}
|
211
|
+
|
212
|
+
/**
|
213
|
+
* Process combination keywords (oneOf, anyOf, allOf)
|
214
|
+
* @param {SchemaObject} schema - Schema to process
|
215
|
+
* @param {string} parentName - Parent object name
|
216
|
+
*/
|
217
|
+
export function processCombinationKeywords(schema, parentName) {
|
218
|
+
;['oneOf', 'anyOf', 'allOf'].forEach((keyword) => {
|
219
|
+
if (!schema[keyword] || !Array.isArray(schema[keyword])) {
|
220
|
+
return
|
221
|
+
}
|
222
|
+
|
223
|
+
schema[`${keyword}Titles`] = []
|
224
|
+
|
225
|
+
schema[keyword].forEach((subSchema, index) => {
|
226
|
+
handleSpecialTitles(subSchema, parentName, keyword, index)
|
227
|
+
|
228
|
+
schema[`${keyword}Titles`][index] = subSchema.title
|
229
|
+
addTitles(subSchema, subSchema.title || parentName)
|
230
|
+
})
|
231
|
+
})
|
232
|
+
}
|
233
|
+
|
234
|
+
/**
|
235
|
+
* Process schema references and definitions
|
236
|
+
* @param {SchemaObject} schema - Schema to process
|
237
|
+
*/
|
238
|
+
export function processReferences(schema) {
|
239
|
+
if (schema.schemas) {
|
240
|
+
Object.entries(schema.schemas).forEach(([schemaName, schemaObj]) => {
|
241
|
+
if (!schemaObj.title) {
|
242
|
+
schemaObj.title = formatPropertyName(schemaName)
|
243
|
+
}
|
244
|
+
addTitles(schemaObj, schemaName)
|
245
|
+
})
|
246
|
+
}
|
247
|
+
|
248
|
+
if (schema.components?.schemas) {
|
249
|
+
Object.entries(schema.components.schemas).forEach(
|
250
|
+
([schemaName, schemaObj]) => {
|
251
|
+
if (!schemaObj.title) {
|
252
|
+
schemaObj.title = formatPropertyName(schemaName)
|
253
|
+
}
|
254
|
+
addTitles(schemaObj, schemaName)
|
255
|
+
}
|
256
|
+
)
|
257
|
+
}
|
258
|
+
|
259
|
+
if (schema.$defs) {
|
260
|
+
Object.entries(schema.$defs).forEach(([defName, defSchema]) => {
|
261
|
+
if (!defSchema.title) {
|
262
|
+
defSchema.title = formatPropertyName(defName)
|
263
|
+
}
|
264
|
+
addTitles(defSchema, defName)
|
265
|
+
})
|
266
|
+
}
|
267
|
+
}
|
268
|
+
|
269
|
+
/**
|
270
|
+
* Process additional and pattern properties
|
271
|
+
* @param {SchemaObject} schema - Schema to process
|
272
|
+
* @param {string} parentName - Parent object name
|
273
|
+
*/
|
274
|
+
export function processAdditionalProperties(schema, parentName) {
|
275
|
+
if (
|
276
|
+
schema.additionalProperties &&
|
277
|
+
typeof schema.additionalProperties === 'object'
|
278
|
+
) {
|
279
|
+
if (!schema.additionalProperties.title) {
|
280
|
+
schema.additionalProperties.title = `Additional Properties (${parentName})`
|
281
|
+
}
|
282
|
+
addTitles(schema.additionalProperties, `Additional ${parentName}`)
|
283
|
+
}
|
284
|
+
|
285
|
+
if (schema.patternProperties) {
|
286
|
+
Object.entries(schema.patternProperties).forEach(
|
287
|
+
([pattern, patternSchema]) => {
|
288
|
+
if (!patternSchema.title) {
|
289
|
+
patternSchema.title = `Pattern Property (${pattern})`
|
290
|
+
}
|
291
|
+
addTitles(patternSchema, `Pattern ${parentName}`)
|
292
|
+
}
|
293
|
+
)
|
294
|
+
}
|
295
|
+
}
|
296
|
+
|
297
|
+
/**
|
298
|
+
* Recursively enhances schemas with descriptive titles and descriptions
|
299
|
+
* for better documentation output
|
300
|
+
* @param {SchemaObject} schema - The schema object to enhance
|
301
|
+
* @param {string} parentName - Name of the parent object for context
|
302
|
+
* @returns {SchemaObject} The enhanced schema
|
303
|
+
*/
|
304
|
+
export function addTitles(schema, parentName = '') {
|
305
|
+
if (!schema || typeof schema !== 'object') {
|
306
|
+
return schema
|
307
|
+
}
|
308
|
+
|
309
|
+
setSchemaTitle(schema, parentName)
|
310
|
+
|
311
|
+
// process oneOf, anyOf, allOf with improved titles
|
312
|
+
processCombinationKeywords(schema, parentName)
|
313
|
+
|
314
|
+
processProperties(schema)
|
315
|
+
|
316
|
+
processArrayItems(schema, parentName)
|
317
|
+
|
318
|
+
processReferences(schema)
|
319
|
+
|
320
|
+
processAdditionalProperties(schema, parentName)
|
321
|
+
|
322
|
+
return schema
|
323
|
+
}
|
324
|
+
|
325
|
+
/**
|
326
|
+
* @import { SchemaObject } from './types.js'
|
327
|
+
*/
|
@@ -0,0 +1,21 @@
|
|
1
|
+
/**
|
2
|
+
* @typedef {{[key: string]: any}} StringIndexedObject
|
3
|
+
* @typedef {object & StringIndexedObject} SchemaObject
|
4
|
+
* @property {string} [title] - Schema title
|
5
|
+
* @property {string} [description] - Schema description
|
6
|
+
* @property {string} [type] - Schema type
|
7
|
+
* @property {{[key: string]: SchemaObject}} [properties] - Schema properties
|
8
|
+
* @property {SchemaObject|SchemaObject[]} [items] - Array items schema
|
9
|
+
* @property {SchemaObject[]} [oneOf] - OneOf array of schemas
|
10
|
+
* @property {SchemaObject[]} [anyOf] - AnyOf array of schemas
|
11
|
+
* @property {SchemaObject[]} [allOf] - AllOf array of schemas
|
12
|
+
* @property {object} [additionalProperties] - Additional properties schema
|
13
|
+
* @property {object} [patternProperties] - Pattern properties schemas
|
14
|
+
* @property {object} [components] - OpenAPI components
|
15
|
+
* @property {object} [schemas] - OpenAPI schemas
|
16
|
+
* @property {object} [$defs] - JSON Schema definitions
|
17
|
+
* @property {string} [$ref] - JSON Schema reference
|
18
|
+
* @property {*} [x] - Any additional properties with string keys
|
19
|
+
*/
|
20
|
+
|
21
|
+
export {}
|