@defra/forms-model 3.0.431 → 3.0.433
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/dist/module/form/form-definition/index.js +2 -1
- package/dist/module/form/form-definition/index.js.map +1 -1
- package/dist/module/form/form-definition/types.js.map +1 -1
- package/dist/module/form/form-editor/index.js +107 -0
- package/dist/module/form/form-editor/index.js.map +1 -1
- package/dist/module/form/form-editor/types.js.map +1 -1
- package/dist/module/form/form-manager/index.js +1 -1
- package/dist/module/form/form-manager/index.js.map +1 -1
- package/dist/types/form/form-definition/index.d.ts.map +1 -1
- package/dist/types/form/form-definition/types.d.ts +1 -0
- package/dist/types/form/form-definition/types.d.ts.map +1 -1
- package/dist/types/form/form-editor/index.d.ts +21 -1
- package/dist/types/form/form-editor/index.d.ts.map +1 -1
- package/dist/types/form/form-editor/types.d.ts +8 -2
- package/dist/types/form/form-editor/types.d.ts.map +1 -1
- package/dist/types/form/form-manager/index.d.ts +1 -1
- package/dist/types/form/form-manager/index.d.ts.map +1 -1
- package/package.json +1 -1
- package/schemas/component-schema-v2.json +162 -0
- package/schemas/component-schema.json +162 -0
- package/schemas/date-sub-schema.json +11 -0
- package/schemas/form-definition-schema.json +1886 -0
- package/schemas/form-definition-v2-payload-schema.json +1469 -0
- package/schemas/form-editor-input-check-answers-setting-schema.json +18 -0
- package/schemas/form-editor-input-page-schema.json +41 -0
- package/schemas/form-editor-input-page-settings-schema.json +28 -0
- package/schemas/form-editor-input-question-schema.json +38 -0
- package/schemas/form-metadata-author-schema.json +24 -0
- package/schemas/form-metadata-contact-schema.json +61 -0
- package/schemas/form-metadata-email-schema.json +25 -0
- package/schemas/form-metadata-input-schema.json +127 -0
- package/schemas/form-metadata-online-schema.json +25 -0
- package/schemas/form-metadata-schema.json +340 -0
- package/schemas/form-metadata-state-schema.json +72 -0
- package/schemas/form-submit-payload-schema.json +124 -0
- package/schemas/form-submit-record-schema.json +30 -0
- package/schemas/form-submit-recordset-schema.json +62 -0
- package/schemas/list-schema-v2.json +496 -0
- package/schemas/list-schema.json +496 -0
- package/schemas/max-future-schema.json +8 -0
- package/schemas/max-length-schema.json +8 -0
- package/schemas/max-past-schema.json +8 -0
- package/schemas/max-schema.json +7 -0
- package/schemas/min-length-schema.json +8 -0
- package/schemas/min-schema.json +7 -0
- package/schemas/page-schema-payload-v2.json +400 -0
- package/schemas/page-schema-v2.json +400 -0
- package/schemas/page-schema.json +400 -0
- package/schemas/page-type-schema.json +11 -0
- package/schemas/pagination-options-schema.json +27 -0
- package/schemas/patch-page-schema.json +26 -0
- package/schemas/query-options-schema.json +94 -0
- package/schemas/question-schema.json +7 -0
- package/schemas/question-type-full-schema.json +22 -0
- package/schemas/question-type-schema.json +20 -0
- package/schemas/search-options-schema.json +59 -0
- package/schemas/sorting-options-schema.json +28 -0
- package/schemas/written-answer-sub-schema.json +12 -0
- package/src/form/form-definition/index.ts +5 -1
- package/src/form/form-definition/types.ts +1 -0
- package/src/form/form-editor/index.ts +147 -1
- package/src/form/form-editor/types.ts +19 -2
- package/src/form/form-manager/index.ts +1 -1
- package/scripts/generate-schemas.js +0 -238
- package/scripts/schema-modules/constants.js +0 -39
- package/scripts/schema-modules/schema-processors.js +0 -109
- package/scripts/schema-modules/schema-simplifiers.js +0 -351
- package/scripts/schema-modules/title-processors.js +0 -327
- package/scripts/schema-modules/types.js +0 -21
- package/scripts/schema-modules/utils.js +0 -41
@@ -0,0 +1,28 @@
|
|
1
|
+
{
|
2
|
+
"type": "object",
|
3
|
+
"properties": {
|
4
|
+
"sortBy": {
|
5
|
+
"type": "string",
|
6
|
+
"description": "Field to sort results by",
|
7
|
+
"enum": [
|
8
|
+
"updatedAt",
|
9
|
+
"title"
|
10
|
+
],
|
11
|
+
"title": "Sort By"
|
12
|
+
},
|
13
|
+
"order": {
|
14
|
+
"type": "string",
|
15
|
+
"description": "Sort order (ascending or descending)",
|
16
|
+
"enum": [
|
17
|
+
"asc",
|
18
|
+
"desc"
|
19
|
+
],
|
20
|
+
"title": "Order"
|
21
|
+
}
|
22
|
+
},
|
23
|
+
"additionalProperties": false,
|
24
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
25
|
+
"title": "Sorting Options Schema",
|
26
|
+
"description": "JSON Schema for validating Sorting Options Schema in Defra forms",
|
27
|
+
"$id": "@defra/forms-model/schemas/sorting-options-schema.json"
|
28
|
+
}
|
@@ -0,0 +1,12 @@
|
|
1
|
+
{
|
2
|
+
"type": "string",
|
3
|
+
"description": "Subtype for written answer questions",
|
4
|
+
"enum": [
|
5
|
+
"TextField",
|
6
|
+
"MultilineTextField",
|
7
|
+
"NumberField"
|
8
|
+
],
|
9
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
10
|
+
"title": "Written Answer Sub Schema",
|
11
|
+
"$id": "@defra/forms-model/schemas/written-answer-sub-schema.json"
|
12
|
+
}
|
@@ -479,7 +479,11 @@ const baseListItemSchema = Joi.object<Item>()
|
|
479
479
|
condition: Joi.string()
|
480
480
|
.allow('')
|
481
481
|
.optional()
|
482
|
-
.description('Condition that determines if this item is shown')
|
482
|
+
.description('Condition that determines if this item is shown'),
|
483
|
+
hint: Joi.string()
|
484
|
+
.allow('')
|
485
|
+
.optional()
|
486
|
+
.description('Optional hint text to be shown on list item')
|
483
487
|
})
|
484
488
|
|
485
489
|
const stringListItemSchema = baseListItemSchema
|
@@ -1,4 +1,4 @@
|
|
1
|
-
import Joi from 'joi'
|
1
|
+
import Joi, { type ArraySchema, type GetRuleOptions } from 'joi'
|
2
2
|
|
3
3
|
import { ComponentType } from '~/src/components/enums.js'
|
4
4
|
import {
|
@@ -291,7 +291,153 @@ export const classesSchema = Joi.string()
|
|
291
291
|
.allow('')
|
292
292
|
.description('Custom CSS classes to apply to the component')
|
293
293
|
|
294
|
+
type GenericRuleOptions<K extends string, T> = Omit<GetRuleOptions, 'args'> & {
|
295
|
+
args: Record<K, T>
|
296
|
+
}
|
297
|
+
|
298
|
+
interface DSLSchema<TSchema = Record<string, unknown>[]>
|
299
|
+
extends ArraySchema<TSchema> {
|
300
|
+
rowSeparator: (rowSep: string | RegExp) => DSLSchema<TSchema>
|
301
|
+
row: (rowSep: string | RegExp) => DSLSchema<TSchema>
|
302
|
+
colSeparator: (colSep: string | RegExp) => DSLSchema<TSchema>
|
303
|
+
col: (colSep: string | RegExp) => DSLSchema<TSchema>
|
304
|
+
keys: (keys: string[]) => DSLSchema<TSchema>
|
305
|
+
}
|
306
|
+
|
307
|
+
interface CustomValidator extends Joi.Root {
|
308
|
+
dsv<TSchema>(): DSLSchema<TSchema>
|
309
|
+
}
|
310
|
+
|
311
|
+
export const customValidator = Joi.extend((joi: Joi.Root) => {
|
312
|
+
return {
|
313
|
+
type: 'dsv',
|
314
|
+
base: joi.array(),
|
315
|
+
messages: {
|
316
|
+
'dsv.invalid': 'Invalid parse string'
|
317
|
+
},
|
318
|
+
coerce: {
|
319
|
+
from: 'string',
|
320
|
+
method(value: string, helpers) {
|
321
|
+
try {
|
322
|
+
// Only called when prefs.convert is true
|
323
|
+
// Rules
|
324
|
+
const rowSeparatorRule = helpers.schema.$_getRule('rowSeparator') as
|
325
|
+
| undefined
|
326
|
+
| GenericRuleOptions<'rowSeparator', string | RegExp>
|
327
|
+
const colSeparatorRule = helpers.schema.$_getRule('colSeparator') as
|
328
|
+
| undefined
|
329
|
+
| GenericRuleOptions<'colSeparator', string | RegExp>
|
330
|
+
const keysRule = helpers.schema.$_getRule('keys') as
|
331
|
+
| undefined
|
332
|
+
| GenericRuleOptions<'keys', string[]>
|
333
|
+
|
334
|
+
// Rows
|
335
|
+
const rowSeparator = rowSeparatorRule?.args.rowSeparator ?? /\r?\n/
|
336
|
+
const rows = value
|
337
|
+
.split(rowSeparator)
|
338
|
+
.map((v) => v.trim())
|
339
|
+
.filter(Boolean)
|
340
|
+
|
341
|
+
// Columns
|
342
|
+
const colSeparator = colSeparatorRule?.args.colSeparator ?? ','
|
343
|
+
const keys = keysRule?.args.keys ?? ['key', 'value']
|
344
|
+
|
345
|
+
const coercedValue = rows.map((row) => {
|
346
|
+
return row
|
347
|
+
.split(colSeparator)
|
348
|
+
.reduce<Record<string, string>>((acc, col, idx) => {
|
349
|
+
return {
|
350
|
+
...acc,
|
351
|
+
[keys[idx]]: col.trim()
|
352
|
+
}
|
353
|
+
}, {})
|
354
|
+
})
|
355
|
+
return { value: coercedValue }
|
356
|
+
} catch (_err) {
|
357
|
+
// eslint-disable-next-line no-console
|
358
|
+
console.error(_err)
|
359
|
+
return { value, errors: [helpers.error('dsv.invalid')] }
|
360
|
+
}
|
361
|
+
}
|
362
|
+
},
|
363
|
+
rules: {
|
364
|
+
rowSeparator: {
|
365
|
+
convert: true,
|
366
|
+
alias: 'row',
|
367
|
+
method(rowSeparator: string) {
|
368
|
+
return this.$_addRule({
|
369
|
+
name: 'rowSeparator',
|
370
|
+
args: { rowSeparator }
|
371
|
+
})
|
372
|
+
},
|
373
|
+
args: [
|
374
|
+
{
|
375
|
+
name: 'rowSeparator',
|
376
|
+
ref: true,
|
377
|
+
assert: (value) =>
|
378
|
+
typeof value === 'string' || value instanceof RegExp,
|
379
|
+
message: 'must be a string or regex'
|
380
|
+
}
|
381
|
+
]
|
382
|
+
},
|
383
|
+
colSeparator: {
|
384
|
+
convert: true,
|
385
|
+
alias: 'col',
|
386
|
+
method(colSeparator: string) {
|
387
|
+
return this.$_addRule({
|
388
|
+
name: 'colSeparator',
|
389
|
+
args: { colSeparator }
|
390
|
+
})
|
391
|
+
},
|
392
|
+
args: [
|
393
|
+
{
|
394
|
+
name: 'colSeparator',
|
395
|
+
ref: true,
|
396
|
+
assert: (value) =>
|
397
|
+
typeof value === 'string' || value instanceof RegExp,
|
398
|
+
message: 'must be a string or regex'
|
399
|
+
}
|
400
|
+
]
|
401
|
+
},
|
402
|
+
keys: {
|
403
|
+
convert: true,
|
404
|
+
method(keys: string[]) {
|
405
|
+
return this.$_addRule({ name: 'keys', args: { keys } })
|
406
|
+
},
|
407
|
+
args: [
|
408
|
+
{
|
409
|
+
name: 'keys',
|
410
|
+
ref: true,
|
411
|
+
assert: (value) =>
|
412
|
+
Array.isArray(value) && value.every((k) => typeof k === 'string'),
|
413
|
+
message: 'must be an array of strings'
|
414
|
+
}
|
415
|
+
]
|
416
|
+
}
|
417
|
+
}
|
418
|
+
}
|
419
|
+
}) as CustomValidator
|
420
|
+
|
421
|
+
export const autoCompleteOptionsSchema = customValidator
|
422
|
+
.dsv<{ text: string; value: string }>()
|
423
|
+
.row(/\r?\n/)
|
424
|
+
.col(':')
|
425
|
+
.keys(['text', 'value'])
|
426
|
+
.items(
|
427
|
+
customValidator.object({
|
428
|
+
text: customValidator.string().min(1).disallow('').required(),
|
429
|
+
value: customValidator
|
430
|
+
.string()
|
431
|
+
.default((parent: { text: string; value?: string }) => parent.text)
|
432
|
+
.min(1)
|
433
|
+
.disallow('')
|
434
|
+
})
|
435
|
+
)
|
436
|
+
.min(1)
|
437
|
+
.required()
|
438
|
+
|
294
439
|
export const questionDetailsFullSchema = {
|
440
|
+
autoCompleteOptionsSchema,
|
295
441
|
classesSchema,
|
296
442
|
documentTypesSchema,
|
297
443
|
enhancedActionSchema,
|
@@ -1,4 +1,5 @@
|
|
1
1
|
import { type ComponentDef } from '~/src/components/types.js'
|
2
|
+
import { type Item } from '~/src/index.js'
|
2
3
|
|
3
4
|
/**
|
4
5
|
* Interface for `FormEditor` Joi schema
|
@@ -203,6 +204,8 @@ export interface FormEditor {
|
|
203
204
|
* The value of the radio item
|
204
205
|
*/
|
205
206
|
radioValue: string
|
207
|
+
|
208
|
+
autoCompleteOptions: Item[]
|
206
209
|
}
|
207
210
|
|
208
211
|
export type FormEditorInputPage = Pick<
|
@@ -242,6 +245,7 @@ export type FormEditorInputQuestion = Pick<
|
|
242
245
|
| 'documentTypes'
|
243
246
|
| 'imageTypes'
|
244
247
|
| 'tabularDataTypes'
|
248
|
+
| 'autoCompleteOptions'
|
245
249
|
| 'enhancedAction'
|
246
250
|
| 'radioId'
|
247
251
|
| 'radioLabel'
|
@@ -296,9 +300,14 @@ export interface GovukField {
|
|
296
300
|
fieldset?: {
|
297
301
|
legend?: { text?: string; isPageHeading?: boolean; classes?: string }
|
298
302
|
}
|
299
|
-
value?: string | boolean | number | string[]
|
303
|
+
value?: string | boolean | number | string[] | Item[]
|
300
304
|
classes?: string
|
301
|
-
label?: {
|
305
|
+
label?: {
|
306
|
+
text?: string
|
307
|
+
html?: string
|
308
|
+
classes?: string
|
309
|
+
isPageHeading?: boolean
|
310
|
+
}
|
302
311
|
hint?: { text?: string; html?: string; classes?: string }
|
303
312
|
items?: { text?: string; value?: string; checked?: boolean }[]
|
304
313
|
rows?: number
|
@@ -316,9 +325,17 @@ export interface FormEditorGovukField {
|
|
316
325
|
imageTypes?: GovukField
|
317
326
|
tabularDataTypes?: GovukField
|
318
327
|
radiosOrCheckboxes?: GovukField
|
328
|
+
autoCompleteOptions?: GovukField
|
319
329
|
errorMessage?: { text: string }
|
320
330
|
}
|
321
331
|
|
332
|
+
export type FormEditorGovukFieldBase = Omit<
|
333
|
+
FormEditorGovukField,
|
334
|
+
'errorMessage'
|
335
|
+
>
|
336
|
+
|
337
|
+
export type FormEditorGovukFieldBaseKeys = keyof FormEditorGovukFieldBase
|
338
|
+
|
322
339
|
export interface FormEditorCheckbox {
|
323
340
|
text?: string
|
324
341
|
hint?: {
|
@@ -1,7 +1,7 @@
|
|
1
1
|
import Joi from 'joi'
|
2
2
|
|
3
|
+
import { pageSchema } from '~/src/form/form-definition/index.js'
|
3
4
|
import { type PatchPageFields } from '~/src/form/form-manager/types.js'
|
4
|
-
import { pageSchema } from '~/src/index.js'
|
5
5
|
|
6
6
|
export const patchPageSchema = Joi.object<PatchPageFields>()
|
7
7
|
.keys({
|
@@ -1,238 +0,0 @@
|
|
1
|
-
import fs from 'fs'
|
2
|
-
import path from 'path'
|
3
|
-
import parse from 'joi-to-json'
|
4
|
-
|
5
|
-
import { schemasDir } from './schema-modules/constants.js'
|
6
|
-
import { ensureDirectoryExists, toTitleCase } from './schema-modules/utils.js'
|
7
|
-
import { addTitles } from './schema-modules/title-processors.js'
|
8
|
-
import { simplifyForDocs } from './schema-modules/schema-simplifiers.js'
|
9
|
-
|
10
|
-
/**
|
11
|
-
* Cleans the schemas directory by removing all existing JSON files
|
12
|
-
* @returns {number} Number of files cleaned
|
13
|
-
*/
|
14
|
-
export function cleanSchemaDirectory() {
|
15
|
-
console.log('Cleaning existing schema files...')
|
16
|
-
const existingFiles = fs
|
17
|
-
.readdirSync(schemasDir)
|
18
|
-
.filter((file) => file.endsWith('.json'))
|
19
|
-
|
20
|
-
for (const file of existingFiles) {
|
21
|
-
fs.unlinkSync(path.join(schemasDir, file))
|
22
|
-
}
|
23
|
-
|
24
|
-
console.log(`Cleaned ${existingFiles.length} existing schema files`)
|
25
|
-
return existingFiles.length
|
26
|
-
}
|
27
|
-
|
28
|
-
/**
|
29
|
-
* Gets the schema map that defines which files should be generated
|
30
|
-
* @returns {Record<string, string>} Schema map with filename-to-schema-export mapping
|
31
|
-
*/
|
32
|
-
export function getSchemaMap() {
|
33
|
-
return {
|
34
|
-
// Form definition schemas
|
35
|
-
'form-definition-schema': 'formDefinitionSchema',
|
36
|
-
'form-definition-v2-payload-schema': 'formDefinitionV2PayloadSchema',
|
37
|
-
|
38
|
-
// Component schemas
|
39
|
-
'component-schema': 'componentSchema',
|
40
|
-
'component-schema-v2': 'componentSchemaV2',
|
41
|
-
|
42
|
-
// Page schemas
|
43
|
-
'page-schema': 'pageSchema',
|
44
|
-
'page-schema-v2': 'pageSchemaV2',
|
45
|
-
'page-schema-payload-v2': 'pageSchemaPayloadV2',
|
46
|
-
|
47
|
-
// List schemas
|
48
|
-
'list-schema': 'listSchema',
|
49
|
-
'list-schema-v2': 'listSchemaV2',
|
50
|
-
|
51
|
-
// Form metadata schemas
|
52
|
-
'form-metadata-schema': 'formMetadataSchema',
|
53
|
-
'form-metadata-author-schema': 'formMetadataAuthorSchema',
|
54
|
-
'form-metadata-input-schema': 'formMetadataInputSchema',
|
55
|
-
'form-metadata-state-schema': 'formMetadataStateSchema',
|
56
|
-
|
57
|
-
// Form metadata field schemas
|
58
|
-
'form-metadata-contact-schema': 'contactSchema',
|
59
|
-
'form-metadata-email-schema': 'emailSchema',
|
60
|
-
'form-metadata-online-schema': 'onlineSchema',
|
61
|
-
|
62
|
-
// Form editor schemas
|
63
|
-
'form-editor-input-page-schema': 'formEditorInputPageSchema',
|
64
|
-
'form-editor-input-check-answers-setting-schema':
|
65
|
-
'formEditorInputCheckAnswersSettingSchema',
|
66
|
-
'form-editor-input-question-schema': 'formEditorInputQuestionSchema',
|
67
|
-
'form-editor-input-page-settings-schema':
|
68
|
-
'formEditorInputPageSettingsSchema',
|
69
|
-
|
70
|
-
// Form editor field schemas
|
71
|
-
'page-type-schema': 'pageTypeSchema',
|
72
|
-
'question-type-schema': 'questionTypeSchema',
|
73
|
-
'question-type-full-schema': 'questionTypeFullSchema',
|
74
|
-
'written-answer-sub-schema': 'writtenAnswerSubSchema',
|
75
|
-
'date-sub-schema': 'dateSubSchema',
|
76
|
-
|
77
|
-
// Form submission schemas
|
78
|
-
'form-submit-payload-schema': 'formSubmitPayloadSchema',
|
79
|
-
'form-submit-record-schema': 'formSubmitRecordSchema',
|
80
|
-
'form-submit-recordset-schema': 'formSubmitRecordsetSchema',
|
81
|
-
|
82
|
-
// Form manager schemas
|
83
|
-
'patch-page-schema': 'patchPageSchema',
|
84
|
-
|
85
|
-
// Section schemas
|
86
|
-
'question-schema': 'questionSchema',
|
87
|
-
|
88
|
-
// Validation schemas
|
89
|
-
'min-schema': 'minSchema',
|
90
|
-
'max-schema': 'maxSchema',
|
91
|
-
'min-length-schema': 'minLengthSchema',
|
92
|
-
'max-length-schema': 'maxLengthSchema',
|
93
|
-
'max-future-schema': 'maxFutureSchema',
|
94
|
-
'max-past-schema': 'maxPastSchema',
|
95
|
-
|
96
|
-
// Common schemas
|
97
|
-
'search-options-schema': 'searchOptionsSchema',
|
98
|
-
'query-options-schema': 'queryOptionsSchema',
|
99
|
-
'pagination-options-schema': 'paginationOptionsSchema',
|
100
|
-
'sorting-options-schema': 'sortingOptionsSchema'
|
101
|
-
}
|
102
|
-
}
|
103
|
-
|
104
|
-
/**
|
105
|
-
* Process a single schema and create its JSON Schema file
|
106
|
-
* @param {string} fileName - Output file name
|
107
|
-
* @param {string} schemaName - Schema export name in the model
|
108
|
-
* @param {Record<string, unknown>} model - The loaded model containing schemas
|
109
|
-
* @returns {boolean} Whether processing was successful
|
110
|
-
*/
|
111
|
-
export function processSchema(fileName, schemaName, model) {
|
112
|
-
try {
|
113
|
-
/** @type {unknown} */
|
114
|
-
const joiSchema = model[schemaName]
|
115
|
-
|
116
|
-
if (!joiSchema) {
|
117
|
-
return false
|
118
|
-
}
|
119
|
-
|
120
|
-
/** @type {import('./schema-modules/types.js').SchemaObject} */
|
121
|
-
let jsonSchema = parse(
|
122
|
-
/** @type {Schema} */ (joiSchema),
|
123
|
-
'json',
|
124
|
-
{},
|
125
|
-
{
|
126
|
-
includeSchemaDialect: true,
|
127
|
-
includeDescriptions: true
|
128
|
-
}
|
129
|
-
)
|
130
|
-
|
131
|
-
const title = toTitleCase(fileName)
|
132
|
-
if (!jsonSchema.title) {
|
133
|
-
jsonSchema.title = title
|
134
|
-
}
|
135
|
-
if (!jsonSchema.description) {
|
136
|
-
jsonSchema.description = `JSON Schema for validating ${title} in Defra forms`
|
137
|
-
}
|
138
|
-
|
139
|
-
if (!jsonSchema.$id) {
|
140
|
-
jsonSchema.$id = `@defra/forms-model/schemas/${fileName}.json`
|
141
|
-
}
|
142
|
-
|
143
|
-
addTitles(jsonSchema, '')
|
144
|
-
|
145
|
-
jsonSchema = simplifyForDocs(jsonSchema, '')
|
146
|
-
|
147
|
-
const outputPath = path.join(schemasDir, `${fileName}.json`)
|
148
|
-
fs.writeFileSync(outputPath, JSON.stringify(jsonSchema, null, 2))
|
149
|
-
return true
|
150
|
-
} catch (/** @type {unknown} */ err) {
|
151
|
-
const error = err instanceof Error ? err : new Error(String(err))
|
152
|
-
console.error(`✗ Failed to process ${fileName}: ${error.message}`)
|
153
|
-
return false
|
154
|
-
}
|
155
|
-
}
|
156
|
-
|
157
|
-
/**
|
158
|
-
* Loads the model with all schemas
|
159
|
-
* @returns {Promise<Record<string, unknown>>} Loaded model
|
160
|
-
*/
|
161
|
-
async function loadModelSchemas() {
|
162
|
-
console.log('Loading model schemas...')
|
163
|
-
return import('../dist/module/index.js')
|
164
|
-
}
|
165
|
-
|
166
|
-
/**
|
167
|
-
* Processes all schemas and generates JSON Schema files
|
168
|
-
* @param {Record<string, unknown>} model - The loaded model containing schemas
|
169
|
-
* @returns {{ successCount: number, errorCount: number }} Object containing success and error counts
|
170
|
-
*/
|
171
|
-
export function processAllSchemas(model) {
|
172
|
-
const schemaMap = getSchemaMap()
|
173
|
-
let successCount = 0
|
174
|
-
let errorCount = 0
|
175
|
-
|
176
|
-
for (const [fileName, schemaName] of Object.entries(schemaMap)) {
|
177
|
-
const success = processSchema(fileName, schemaName, model)
|
178
|
-
|
179
|
-
if (success) {
|
180
|
-
successCount++
|
181
|
-
} else {
|
182
|
-
errorCount++
|
183
|
-
}
|
184
|
-
}
|
185
|
-
|
186
|
-
return { successCount, errorCount }
|
187
|
-
}
|
188
|
-
|
189
|
-
/**
|
190
|
-
* Generates schema files from Joi schemas
|
191
|
-
*/
|
192
|
-
export async function generateSchemas() {
|
193
|
-
try {
|
194
|
-
const model = await loadModelSchemas()
|
195
|
-
|
196
|
-
ensureDirectoryExists(schemasDir)
|
197
|
-
|
198
|
-
cleanSchemaDirectory()
|
199
|
-
|
200
|
-
const { successCount, errorCount } = processAllSchemas(model)
|
201
|
-
|
202
|
-
console.log('\nSchema generation complete!')
|
203
|
-
console.log(`✓ Successfully generated ${successCount} schemas`)
|
204
|
-
if (errorCount > 0) {
|
205
|
-
console.log(`✗ Failed to generate ${errorCount} schemas`)
|
206
|
-
}
|
207
|
-
|
208
|
-
return { successCount, errorCount }
|
209
|
-
} catch (err) {
|
210
|
-
const error = err instanceof Error ? err : new Error(String(err))
|
211
|
-
console.error(`\n✗ Schema generation failed: ${error.message}`)
|
212
|
-
throw error
|
213
|
-
}
|
214
|
-
}
|
215
|
-
|
216
|
-
// Only run when executed directly, not when imported as a module
|
217
|
-
if (import.meta.url === `file://${process.argv[1]}`) {
|
218
|
-
;(async () => {
|
219
|
-
try {
|
220
|
-
await generateSchemas()
|
221
|
-
} catch (err) {
|
222
|
-
console.error('Schema generation failed:', err)
|
223
|
-
throw err
|
224
|
-
}
|
225
|
-
})().catch((err) => {
|
226
|
-
console.error('Unhandled error:', err)
|
227
|
-
// eslint-disable-next-line no-process-exit
|
228
|
-
process.exit(1)
|
229
|
-
})
|
230
|
-
}
|
231
|
-
|
232
|
-
/**
|
233
|
-
* @import { Schema } from 'joi'
|
234
|
-
*/
|
235
|
-
|
236
|
-
/**
|
237
|
-
* @import { SchemaObject } from './schema-modules/types.js'
|
238
|
-
*/
|
@@ -1,39 +0,0 @@
|
|
1
|
-
import path from 'path'
|
2
|
-
import { fileURLToPath } from 'url'
|
3
|
-
|
4
|
-
export const currentDirname = path.dirname(fileURLToPath(import.meta.url))
|
5
|
-
export const schemasDir = path.resolve(currentDirname, '../../schemas')
|
6
|
-
|
7
|
-
/**
|
8
|
-
* Condition type constants
|
9
|
-
*/
|
10
|
-
export const CONDITION_TYPES = {
|
11
|
-
DEFINITION: 'Condition Definition',
|
12
|
-
REFERENCE: 'Condition Reference',
|
13
|
-
NESTED_GROUP: 'Nested Condition Group'
|
14
|
-
}
|
15
|
-
|
16
|
-
/**
|
17
|
-
* Value type constants
|
18
|
-
*/
|
19
|
-
export const VALUE_TYPES = {
|
20
|
-
STATIC: 'Static Value',
|
21
|
-
RELATIVE_DATE: 'Relative Date Value'
|
22
|
-
}
|
23
|
-
|
24
|
-
/**
|
25
|
-
* Common description constants
|
26
|
-
*/
|
27
|
-
export const DESCRIPTIONS = {
|
28
|
-
NESTED_CONDITION_GROUP:
|
29
|
-
'A nested group of conditions that allows building complex logical expressions with multiple levels.'
|
30
|
-
}
|
31
|
-
|
32
|
-
/**
|
33
|
-
* Path segment constants
|
34
|
-
*/
|
35
|
-
export const PATH_SEGMENTS = {
|
36
|
-
CONDITIONS: 'conditions',
|
37
|
-
ITEMS: 'items',
|
38
|
-
PROPERTIES: 'properties'
|
39
|
-
}
|
@@ -1,109 +0,0 @@
|
|
1
|
-
import { CONDITION_TYPES, DESCRIPTIONS, VALUE_TYPES } from './constants.js'
|
2
|
-
|
3
|
-
/**
|
4
|
-
* Fixes titles for condition items in anyOfTitles
|
5
|
-
* @param {SchemaObject} obj - Schema to process
|
6
|
-
* @returns {boolean} True if changes were made
|
7
|
-
*/
|
8
|
-
export function fixConditionItems(obj) {
|
9
|
-
if (!obj.anyOfTitles?.includes('Conditions Item Variant 3')) {
|
10
|
-
return false
|
11
|
-
}
|
12
|
-
|
13
|
-
const EXPECTED_CONDITION_TYPES_COUNT = 3
|
14
|
-
|
15
|
-
obj.anyOfTitles = [
|
16
|
-
CONDITION_TYPES.DEFINITION,
|
17
|
-
CONDITION_TYPES.REFERENCE,
|
18
|
-
CONDITION_TYPES.NESTED_GROUP
|
19
|
-
]
|
20
|
-
|
21
|
-
if (obj.anyOf?.length === EXPECTED_CONDITION_TYPES_COUNT) {
|
22
|
-
if (obj.anyOf[0].properties?.field) {
|
23
|
-
obj.anyOf[0].title = CONDITION_TYPES.DEFINITION
|
24
|
-
}
|
25
|
-
if (obj.anyOf[1].properties?.conditionName) {
|
26
|
-
obj.anyOf[1].title = CONDITION_TYPES.REFERENCE
|
27
|
-
}
|
28
|
-
if (obj.anyOf[2].$ref?.includes('conditionGroupSchema')) {
|
29
|
-
obj.anyOf[2].title = CONDITION_TYPES.NESTED_GROUP
|
30
|
-
obj.anyOf[2].description = DESCRIPTIONS.NESTED_CONDITION_GROUP
|
31
|
-
}
|
32
|
-
}
|
33
|
-
return true
|
34
|
-
}
|
35
|
-
|
36
|
-
/**
|
37
|
-
* Fixes titles for value objects in anyOfTitles
|
38
|
-
* @param {SchemaObject} obj - Schema to process
|
39
|
-
* @returns {boolean} True if changes were made
|
40
|
-
*/
|
41
|
-
export function fixValueObjects(obj) {
|
42
|
-
if (
|
43
|
-
obj.anyOfTitles?.length !== 2 ||
|
44
|
-
obj.anyOfTitles[0] !== 'Value (object)' ||
|
45
|
-
obj.anyOfTitles[1] !== 'Value (object)'
|
46
|
-
) {
|
47
|
-
return false
|
48
|
-
}
|
49
|
-
|
50
|
-
obj.anyOfTitles = [VALUE_TYPES.STATIC, VALUE_TYPES.RELATIVE_DATE]
|
51
|
-
|
52
|
-
if (obj.anyOf?.length === 2) {
|
53
|
-
if (obj.anyOf[0].properties?.value) {
|
54
|
-
obj.anyOf[0].title = VALUE_TYPES.STATIC
|
55
|
-
}
|
56
|
-
if (obj.anyOf[1].properties?.period) {
|
57
|
-
obj.anyOf[1].title = VALUE_TYPES.RELATIVE_DATE
|
58
|
-
}
|
59
|
-
}
|
60
|
-
|
61
|
-
return true
|
62
|
-
}
|
63
|
-
|
64
|
-
/**
|
65
|
-
* Processes the anyOfTitles array in an object
|
66
|
-
* @param {SchemaObject} obj - Schema to process
|
67
|
-
*/
|
68
|
-
export function processAnyOfTitles(obj) {
|
69
|
-
if (!obj || typeof obj !== 'object') {
|
70
|
-
return
|
71
|
-
}
|
72
|
-
|
73
|
-
if (!Array.isArray(obj.anyOfTitles)) {
|
74
|
-
return
|
75
|
-
}
|
76
|
-
|
77
|
-
if (!fixConditionItems(obj)) {
|
78
|
-
fixValueObjects(obj)
|
79
|
-
}
|
80
|
-
}
|
81
|
-
|
82
|
-
/**
|
83
|
-
* Recursively fixes all condition titles and anyOfTitles throughout the schema
|
84
|
-
* regardless of their nesting level
|
85
|
-
* @param {SchemaObject} obj - The schema or subschema to fix
|
86
|
-
*/
|
87
|
-
export function fixConditionTitles(obj) {
|
88
|
-
if (!obj || typeof obj !== 'object') {
|
89
|
-
return
|
90
|
-
}
|
91
|
-
|
92
|
-
processAnyOfTitles(obj)
|
93
|
-
|
94
|
-
if (obj.title === 'Conditions Item Variant 3') {
|
95
|
-
obj.title = CONDITION_TYPES.NESTED_GROUP
|
96
|
-
obj.description = DESCRIPTIONS.NESTED_CONDITION_GROUP
|
97
|
-
}
|
98
|
-
|
99
|
-
Object.keys(obj).forEach((key) => {
|
100
|
-
const value = obj[key]
|
101
|
-
if (value && typeof value === 'object') {
|
102
|
-
fixConditionTitles(value)
|
103
|
-
}
|
104
|
-
})
|
105
|
-
}
|
106
|
-
|
107
|
-
/**
|
108
|
-
* @import { SchemaObject } from './types.js'
|
109
|
-
*/
|