@koumoul/vjsf 2.4.0 → 2.6.0
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/main.js +1 -1
- package/dist/third-party.js +16 -4
- package/lib/VJsfNoDeps.js +53 -22
- package/lib/deps/third-party.js +2 -0
- package/lib/mixins/EditableArray.js +2 -0
- package/lib/mixins/ObjectContainer.js +5 -1
- package/lib/mixins/SelectProperty.js +5 -4
- package/lib/mixins/SimpleProperty.js +33 -2
- package/lib/mixins/Validatable.js +13 -5
- package/lib/utils/options.js +3 -1
- package/lib/utils/rules.js +15 -1
- package/lib/utils/schema.js +7 -0
- package/package.json +5 -3
package/lib/VJsfNoDeps.js
CHANGED
|
@@ -16,6 +16,7 @@ import MarkdownEditor from './mixins/MarkdownEditor'
|
|
|
16
16
|
import Tooltip from './mixins/Tooltip'
|
|
17
17
|
import Validatable from './mixins/Validatable'
|
|
18
18
|
import Dependent from './mixins/Dependent'
|
|
19
|
+
const expr = require('property-expr')
|
|
19
20
|
|
|
20
21
|
const debugFlag = global.navigator && global.navigator.cookieEnabled && global.localStorage && global.localStorage.debug
|
|
21
22
|
|
|
@@ -42,6 +43,7 @@ export default {
|
|
|
42
43
|
schema: { type: Object, required: true },
|
|
43
44
|
value: { required: true },
|
|
44
45
|
options: { type: Object },
|
|
46
|
+
optionsRoot: { type: Object },
|
|
45
47
|
modelRoot: { type: [Object, Array, String, Number, Boolean] },
|
|
46
48
|
modelKey: { type: [String, Number], default: 'root' },
|
|
47
49
|
parentKey: { type: String, default: '' },
|
|
@@ -55,6 +57,9 @@ export default {
|
|
|
55
57
|
}
|
|
56
58
|
},
|
|
57
59
|
computed: {
|
|
60
|
+
initialOptions() {
|
|
61
|
+
return this.fullKey === 'root' ? (this.options || {}) : this.optionsRoot
|
|
62
|
+
},
|
|
58
63
|
fullOptions() {
|
|
59
64
|
this.debug('compute fullOptions')
|
|
60
65
|
const _global = (typeof window !== 'undefined' && window) || (typeof global !== 'undefined' && global) || {}
|
|
@@ -72,17 +77,32 @@ export default {
|
|
|
72
77
|
|
|
73
78
|
fullOptions.httpLib = fullOptions.httpLib || this.axios || this.$http || this.$axios || _global.axios
|
|
74
79
|
|
|
80
|
+
// validator function generator is either given or prepared using ajv if present in the context
|
|
75
81
|
if (!fullOptions.validator) {
|
|
82
|
+
const ajvLocalize = fullOptions.ajvLocalize || _global.ajvLocalize
|
|
83
|
+
const ajvAddFormats = fullOptions.ajvAddFormats || _global.ajvAddFormats
|
|
84
|
+
const localizeAjv = !!ajvLocalize && fullOptions.locale && ajvLocalize[fullOptions.locale]
|
|
76
85
|
let ajv = fullOptions.ajv
|
|
77
86
|
if (!ajv) {
|
|
78
|
-
const Ajv = _global.Ajv || (_global.ajv7 && _global.ajv7.default) || (_global.ajv2019 && _global.ajv2019.default)
|
|
79
|
-
|
|
87
|
+
const Ajv = fullOptions.Ajv || _global.Ajv || (_global.ajv7 && _global.ajv7.default) || (_global.ajv2019 && _global.ajv2019.default)
|
|
88
|
+
// TODO: use strict mode but remove our x-* annotations before
|
|
89
|
+
if (Ajv) {
|
|
90
|
+
ajv = new Ajv(localizeAjv ? { allErrors: true, messages: false, strict: false } : { strict: false })
|
|
91
|
+
if (ajvAddFormats) ajvAddFormats(ajv)
|
|
92
|
+
ajv.addFormat('hexcolor', /^#[0-9A-Fa-f]{6,8}$/)
|
|
93
|
+
}
|
|
80
94
|
}
|
|
81
95
|
if (ajv) {
|
|
82
96
|
fullOptions.validator = (schema) => {
|
|
83
97
|
const validate = ajv.compile(schema)
|
|
84
98
|
return (model) => {
|
|
85
|
-
|
|
99
|
+
const valid = validate(model)
|
|
100
|
+
if (!valid) {
|
|
101
|
+
if (localizeAjv) {
|
|
102
|
+
ajvLocalize[fullOptions.locale](validate.errors)
|
|
103
|
+
}
|
|
104
|
+
return ajv.errorsText(validate.errors, { dataVar: '' })
|
|
105
|
+
}
|
|
86
106
|
}
|
|
87
107
|
}
|
|
88
108
|
}
|
|
@@ -133,7 +153,7 @@ export default {
|
|
|
133
153
|
rules() {
|
|
134
154
|
this.debug('compute rules')
|
|
135
155
|
if (!this.fullSchema) return
|
|
136
|
-
return getRules(this.fullSchema, this.fullOptions, this.required, this.isOneOfSelect)
|
|
156
|
+
return getRules(this.schema, this.fullSchema, this.fullOptions, this.required, this.isOneOfSelect)
|
|
137
157
|
},
|
|
138
158
|
disabled() {
|
|
139
159
|
if (!this.fullSchema) return
|
|
@@ -254,7 +274,7 @@ export default {
|
|
|
254
274
|
return
|
|
255
275
|
}
|
|
256
276
|
|
|
257
|
-
if (this.fullSchema['x-if'] && !this.
|
|
277
|
+
if (this.fullSchema['x-if'] && !this.getFromExpr(this.fullSchema['x-if'])) {
|
|
258
278
|
return
|
|
259
279
|
}
|
|
260
280
|
|
|
@@ -309,15 +329,35 @@ export default {
|
|
|
309
329
|
}
|
|
310
330
|
return this._vjsf_cache[key].value
|
|
311
331
|
},
|
|
312
|
-
//
|
|
313
|
-
|
|
314
|
-
const
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
332
|
+
// used by all functionalities that require looking into the data or the context (x-if, fromData, etc)
|
|
333
|
+
getFromExpr(exp) {
|
|
334
|
+
const expData = {
|
|
335
|
+
modelRoot: this.modelRoot,
|
|
336
|
+
root: this.modelRoot,
|
|
337
|
+
model: this.value,
|
|
338
|
+
value: this.value,
|
|
339
|
+
context: this.options.context
|
|
340
|
+
}
|
|
341
|
+
this._vjsf_getters = this._vjsf_getters || {}
|
|
342
|
+
|
|
343
|
+
if (this.initialOptions.evalMethod === 'newFunction') {
|
|
344
|
+
// use a powerful meta-programming approach with "new Function", not safe if the schema is user-submitted
|
|
345
|
+
// eslint-disable-next-line no-new-func
|
|
346
|
+
this._vjsf_getters[exp] = this._vjsf_getters[exp] || new Function(...Object.keys(expData), `return ${exp}`)
|
|
347
|
+
return this._vjsf_getters[exp](...Object.values(expData))
|
|
348
|
+
} else {
|
|
349
|
+
exp = this.prefixExpr(exp)
|
|
350
|
+
// otherwise a safer but not as powerful deep getter method
|
|
351
|
+
this._vjsf_getters[exp] = this._vjsf_getters[exp] || expr.getter(exp, true)
|
|
352
|
+
return this._vjsf_getters[exp](expData)
|
|
319
353
|
}
|
|
320
|
-
|
|
354
|
+
},
|
|
355
|
+
// used by getFromExpr to support simpler expressions that look into the root model by default
|
|
356
|
+
prefixExpr(key) {
|
|
357
|
+
if (key.startsWith('context.') || key.startsWith('model.') || key.startsWith('value.') || key.startsWith('modelRoot.') || key.startsWith('root.')) return key
|
|
358
|
+
// no specific prefix found, we use modelRoot for retro-compatibility
|
|
359
|
+
if (this.modelRoot) return 'root.' + key
|
|
360
|
+
return 'model.' + key
|
|
321
361
|
},
|
|
322
362
|
renderPropSlots(h) {
|
|
323
363
|
const slots = []
|
|
@@ -441,15 +481,6 @@ export default {
|
|
|
441
481
|
value = this.value.filter(item => ![undefined, null].includes(item))
|
|
442
482
|
}
|
|
443
483
|
return this.input(this.fixProperties(value), true)
|
|
444
|
-
},
|
|
445
|
-
watchKey(key) {
|
|
446
|
-
if (key.startsWith('context.')) return 'options.' + key
|
|
447
|
-
if (key.startsWith('root.')) return key.replace('root.', 'modelRoot.')
|
|
448
|
-
if (key.startsWith('modelRoot.')) return key
|
|
449
|
-
|
|
450
|
-
// no specific prefix found, we use modelRoot for retro-compatibility
|
|
451
|
-
if (this.modelRoot) return 'modelRoot.' + key
|
|
452
|
-
return 'value.' + key
|
|
453
484
|
}
|
|
454
485
|
}
|
|
455
486
|
}
|
package/lib/deps/third-party.js
CHANGED
|
@@ -7,3 +7,5 @@ const _global = (typeof window !== 'undefined' && window) || (typeof global !==
|
|
|
7
7
|
_global.markdownit = require('markdown-it')
|
|
8
8
|
Vue.component('draggable', Draggable)
|
|
9
9
|
_global.Ajv = require('ajv')
|
|
10
|
+
_global.ajvLocalize = require('ajv-i18n')
|
|
11
|
+
_global.ajvAddFormats = require('ajv-formats')
|
|
@@ -104,6 +104,7 @@ export default {
|
|
|
104
104
|
modelKey,
|
|
105
105
|
parentKey: `${this.fullKey}.`,
|
|
106
106
|
options: { ...this.fullOptions, hideReadOnly: false },
|
|
107
|
+
optionsRoot: this.initialOptions,
|
|
107
108
|
sectionDepth: this.sectionDepth + 1,
|
|
108
109
|
separateValidation: false
|
|
109
110
|
},
|
|
@@ -171,6 +172,7 @@ export default {
|
|
|
171
172
|
modelKey: `item-${i}`,
|
|
172
173
|
parentKey: `${this.fullKey}.`,
|
|
173
174
|
options,
|
|
175
|
+
optionsRoot: this.initialOptions,
|
|
174
176
|
sectionDepth: this.sectionDepth + 1,
|
|
175
177
|
separateValidation: this.fullOptions.editMode !== 'inline'
|
|
176
178
|
},
|
|
@@ -197,6 +197,7 @@ export default {
|
|
|
197
197
|
parentKey: `${this.fullKey}.`,
|
|
198
198
|
required: forceRequired || !!(this.fullSchema.required && this.fullSchema.required.includes(schema.key)),
|
|
199
199
|
options: { ...this.fullOptions, autofocus: this.fullOptions.autofocus && this.objectContainerChildrenCount === 1 },
|
|
200
|
+
optionsRoot: this.initialOptions,
|
|
200
201
|
sectionDepth
|
|
201
202
|
},
|
|
202
203
|
class: this.fullOptions.childrenClass,
|
|
@@ -303,7 +304,7 @@ export default {
|
|
|
303
304
|
value: this.currentOneOf,
|
|
304
305
|
label: (this.subSchemasConstProp && this.subSchemasConstProp.title) || this.fullSchema.title,
|
|
305
306
|
items: this.subSchemas
|
|
306
|
-
.filter(item => !item['x-if'] || !!this.
|
|
307
|
+
.filter(item => !item['x-if'] || !!this.getFromExpr(item['x-if']))
|
|
307
308
|
.filter(item => item.properties && item.properties[this.subSchemasConstProp.key]),
|
|
308
309
|
required: this.subSchemasRequired,
|
|
309
310
|
clearable: !this.subSchemasRequired,
|
|
@@ -324,6 +325,9 @@ export default {
|
|
|
324
325
|
}
|
|
325
326
|
}
|
|
326
327
|
return [h('v-row', { class: `ma-0 ${this.fullOptions.objectContainerClass}` }, [
|
|
328
|
+
// display a local error only we don't already have an error displayed in the children
|
|
329
|
+
(this.localRuleError && !this.dedupChildrenWithValidatedErrors.length) && h('v-col', { props: { cols: 12 }, class: { 'px-0': true, 'error--text': true } }, this.localRuleError),
|
|
330
|
+
// display the description as block of text on top of section
|
|
327
331
|
this.fullSchema.description && !this.subSchemasConstProp && h('v-col', { props: { cols: 12 }, class: { 'pa-0': true }, domProps: { innerHTML: this.htmlDescription } })]
|
|
328
332
|
.concat(flatChildren).concat(sectionsChildren))
|
|
329
333
|
]
|
|
@@ -27,7 +27,8 @@ export default {
|
|
|
27
27
|
oneOfSelect() {
|
|
28
28
|
if (!this.fullSchema) return
|
|
29
29
|
if (this.fullSchema.type === 'array' && this.fullSchema.items && ['string', 'integer', 'number'].includes(this.fullSchema.items.type) && (this.fullSchema.items.oneOf || this.fullSchema.items.anyOf)) return true
|
|
30
|
-
if (['string', 'integer', 'number'].includes(this.fullSchema.type) &&
|
|
30
|
+
if (['string', 'integer', 'number'].includes(this.fullSchema.type) && this.fullSchema.oneOf && this.fullSchema.oneOf[0] && this.fullSchema.oneOf[0].const !== undefined) return true
|
|
31
|
+
if (['string', 'integer', 'number'].includes(this.fullSchema.type) && this.fullSchema.anyOf && this.fullSchema.anyOf[0] && this.fullSchema.anyOf[0].const !== undefined) return true
|
|
31
32
|
return false
|
|
32
33
|
},
|
|
33
34
|
examplesSelect() {
|
|
@@ -104,7 +105,7 @@ export default {
|
|
|
104
105
|
const of = schema.anyOf || schema.oneOf
|
|
105
106
|
this.openEndedSelect = schema.anyOf && !!schema.anyOf.find(item => !item.const && !item.enum)
|
|
106
107
|
this.rawSelectItems = of
|
|
107
|
-
.filter(item => !item['x-if'] || !!this.
|
|
108
|
+
.filter(item => !item['x-if'] || !!this.getFromExpr(item['x-if']))
|
|
108
109
|
.filter(item => !!item.const || !!item.enum)
|
|
109
110
|
.map(item => ({ ...item, [this.itemKey]: item.const || (item.enum && item.enum[0]), [this.itemTitle]: item.title }))
|
|
110
111
|
}
|
|
@@ -119,14 +120,14 @@ export default {
|
|
|
119
120
|
// Case of a select based on an array somewhere in the data
|
|
120
121
|
if (this.fullSchema['x-fromData']) {
|
|
121
122
|
this.openEndedSelect = this.customTag === 'v-combobox' || this.fullSchema['x-display'] === 'combobox'
|
|
122
|
-
this.$watch(this.
|
|
123
|
+
this.$watch(() => this.getFromExpr(this.fullSchema['x-fromData']), (val) => {
|
|
123
124
|
this.rawSelectItems = val
|
|
124
125
|
}, { immediate: true })
|
|
125
126
|
}
|
|
126
127
|
// Watch the dynamic parts of the URL used to fill the select field
|
|
127
128
|
if (this.fromUrlKeys) {
|
|
128
129
|
this.fromUrlKeys.forEach(key => {
|
|
129
|
-
this.$watch(this.
|
|
130
|
+
this.$watch(() => this.getFromExpr(key), (val) => {
|
|
130
131
|
this.fromUrlParams[key] = val
|
|
131
132
|
this.fetchSelectItems()
|
|
132
133
|
}, { immediate: true })
|
|
@@ -31,7 +31,10 @@ export default {
|
|
|
31
31
|
const scopedSlots = {}
|
|
32
32
|
let tooltipSlot = 'append-outer'
|
|
33
33
|
|
|
34
|
-
|
|
34
|
+
const sep = this.fullSchema.separator || this.fullSchema['x-separator']
|
|
35
|
+
|
|
36
|
+
// simple string
|
|
37
|
+
if (this.fullSchema.type === 'string' && !sep) {
|
|
35
38
|
if (this.display === 'textarea' || (this.fullSchema.maxLength && this.fullSchema.maxLength > 1000 && this.display !== 'single-line')) {
|
|
36
39
|
tag = 'v-textarea'
|
|
37
40
|
Object.assign(props, this.fullOptions.textareaProps)
|
|
@@ -43,6 +46,34 @@ export default {
|
|
|
43
46
|
}
|
|
44
47
|
}
|
|
45
48
|
|
|
49
|
+
// multivalued string with separator
|
|
50
|
+
if (this.fullSchema.type === 'string' && sep) {
|
|
51
|
+
tag = 'v-combobox'
|
|
52
|
+
Object.assign(props, this.fullOptions.comboboxProps)
|
|
53
|
+
props.chips = true
|
|
54
|
+
props.multiple = true
|
|
55
|
+
props.appendIcon = ''
|
|
56
|
+
props.type = 'string'
|
|
57
|
+
props.validateOnBlur = true
|
|
58
|
+
props.value = this.value ? this.value.split(sep) : []
|
|
59
|
+
|
|
60
|
+
on.input = value => this.input(value.join(sep))
|
|
61
|
+
|
|
62
|
+
scopedSlots.selection = slotProps => {
|
|
63
|
+
const onClose = () => {
|
|
64
|
+
const value = this.value ? this.value.split(sep) : []
|
|
65
|
+
value.splice(slotProps.index, 1)
|
|
66
|
+
this.input(value.join(sep))
|
|
67
|
+
this.change()
|
|
68
|
+
}
|
|
69
|
+
return h('v-chip', {
|
|
70
|
+
props: { close: true },
|
|
71
|
+
on: { 'click:close': onClose }
|
|
72
|
+
}, slotProps.item)
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// simple boolean
|
|
46
77
|
if (['number', 'integer'].includes(this.fullSchema.type)) {
|
|
47
78
|
if (this.display === 'slider') {
|
|
48
79
|
tag = 'v-slider'
|
|
@@ -83,7 +114,7 @@ export default {
|
|
|
83
114
|
props.appendIcon = ''
|
|
84
115
|
props.type = 'string'
|
|
85
116
|
props.validateOnBlur = true
|
|
86
|
-
const itemRules = getRules(schemaUtils.prepareFullSchema(this.fullSchema.items, null, this.fullOptions), this.fullOptions)
|
|
117
|
+
const itemRules = getRules(this.fullSchema.items, schemaUtils.prepareFullSchema(this.fullSchema.items, null, this.fullOptions), this.fullOptions)
|
|
87
118
|
props.rules = props.rules.concat([(values) => {
|
|
88
119
|
const valuesMessages = values.map(value => {
|
|
89
120
|
const brokenRule = itemRules.find(rule => {
|
|
@@ -34,15 +34,23 @@ export default {
|
|
|
34
34
|
},
|
|
35
35
|
hasError() {
|
|
36
36
|
this.debug('compute hasError')
|
|
37
|
-
return !!this.inputs.find(input => input.hasError) || !!this.
|
|
37
|
+
return !!this.inputs.find(input => input.hasError) || !!this.localRuleError
|
|
38
38
|
},
|
|
39
|
-
|
|
40
|
-
this.debug('compute
|
|
41
|
-
return !!this.inputs.find(input => input.
|
|
39
|
+
hasValidatedError() {
|
|
40
|
+
this.debug('compute hasValidatedError')
|
|
41
|
+
return !!this.inputs.find(input => input.hasValidatedError || (input.hasError && (input.validated || input.shouldValidate))) ||
|
|
42
|
+
(this.localRuleError && (this.validated || this.shouldValidate))
|
|
42
43
|
},
|
|
43
44
|
childrenWithValidatedErrors() {
|
|
44
45
|
this.debug('compute childrenWithValidatedErrors')
|
|
45
|
-
return Object.keys(this.childrenInputs).filter(key => !!this.childrenInputs[key].
|
|
46
|
+
return Object.keys(this.childrenInputs).filter(key => !!this.childrenInputs[key].hasValidatedError)
|
|
47
|
+
},
|
|
48
|
+
localRuleError() {
|
|
49
|
+
if (!this.validated || !this.rules || !this.rules.length) return false
|
|
50
|
+
const brokenRule = this.rules.find(rule => {
|
|
51
|
+
return typeof rule(this.value) === 'string'
|
|
52
|
+
})
|
|
53
|
+
return brokenRule && brokenRule(this.value)
|
|
46
54
|
}
|
|
47
55
|
},
|
|
48
56
|
watch: {
|
package/lib/utils/options.js
CHANGED
package/lib/utils/rules.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { deepEqual } from 'fast-equals'
|
|
2
2
|
|
|
3
|
-
export const getRules = (fullSchema, options, required, isOneOfSelect) => {
|
|
3
|
+
export const getRules = (schema, fullSchema, options, required, isOneOfSelect) => {
|
|
4
4
|
const rules = []
|
|
5
5
|
if (required) {
|
|
6
6
|
rules.push((val) => (val !== undefined && val !== null && val !== '') || options.messages.required)
|
|
@@ -54,6 +54,11 @@ export const getRules = (fullSchema, options, required, isOneOfSelect) => {
|
|
|
54
54
|
rules.push((val) => (val === undefined || val === null || !val.find(valItem => !fullSchema.items.oneOf.find(item => deepEqual(item.const, valItem)))) || '')
|
|
55
55
|
}
|
|
56
56
|
|
|
57
|
+
// ajv validation
|
|
58
|
+
if (options.validator && options.useValidator) {
|
|
59
|
+
rules.push(getAjvRule(schema, options.validator))
|
|
60
|
+
}
|
|
61
|
+
|
|
57
62
|
const customRules = (fullSchema['x-rules'] || []).map(rule => {
|
|
58
63
|
if (typeof rule === 'string') {
|
|
59
64
|
const ruleFunction = options.rules && options.rules[rule]
|
|
@@ -65,3 +70,12 @@ export const getRules = (fullSchema, options, required, isOneOfSelect) => {
|
|
|
65
70
|
}).filter(rule => !!rule)
|
|
66
71
|
return rules.concat(customRules)
|
|
67
72
|
}
|
|
73
|
+
|
|
74
|
+
const getAjvRule = (schema, validator, locale) => {
|
|
75
|
+
const validate = validator(schema)
|
|
76
|
+
return (val) => {
|
|
77
|
+
if (val === null || val === undefined) return true
|
|
78
|
+
const error = validate(val)
|
|
79
|
+
return !error || error
|
|
80
|
+
}
|
|
81
|
+
}
|
package/lib/utils/schema.js
CHANGED
|
@@ -30,6 +30,13 @@ schemaUtils.prepareFullSchema = (schema, value, options) => {
|
|
|
30
30
|
|
|
31
31
|
if (!fullSchema.type && fullSchema.properties) fullSchema.type = 'object'
|
|
32
32
|
|
|
33
|
+
// detect type from combination info
|
|
34
|
+
if (!fullSchema.type) {
|
|
35
|
+
const combination = fullSchema.anyOf || fullSchema.oneOf || fullSchema.allOf
|
|
36
|
+
const typedCombinationItem = combination && combination.find(c => !!c.type)
|
|
37
|
+
if (typedCombinationItem) fullSchema.type = typedCombinationItem.type
|
|
38
|
+
}
|
|
39
|
+
|
|
33
40
|
// manage null type in an array, for example ['string', 'null']
|
|
34
41
|
if (Array.isArray(fullSchema.type)) {
|
|
35
42
|
fullSchema.nullable = fullSchema.type.includes('null')
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@koumoul/vjsf",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.6.0",
|
|
4
4
|
"description": "Generate forms for the vuetify UI library (vuejs) based on annotated JSON schemas.",
|
|
5
5
|
"main": "dist/main.js",
|
|
6
6
|
"scripts": {
|
|
@@ -47,14 +47,16 @@
|
|
|
47
47
|
"homepage": "https://github.com/koumoul-dev/vuetify-jsonschema-form#readme",
|
|
48
48
|
"dependencies": {
|
|
49
49
|
"@mdi/js": "^5.5.55",
|
|
50
|
-
"ajv": "^6.
|
|
50
|
+
"ajv": "^8.6.2",
|
|
51
|
+
"ajv-formats": "^2.1.1",
|
|
52
|
+
"ajv-i18n": "^4.1.0",
|
|
51
53
|
"debounce": "^1.2.0",
|
|
52
54
|
"fast-copy": "^2.1.1",
|
|
53
55
|
"fast-equals": "^2.0.0",
|
|
54
56
|
"markdown-it": "^8.4.2",
|
|
55
57
|
"match-all": "^1.2.5",
|
|
56
58
|
"object-hash": "^2.1.1",
|
|
57
|
-
"property-expr": "^
|
|
59
|
+
"property-expr": "^2.0.4",
|
|
58
60
|
"vuedraggable": "^2.24.3"
|
|
59
61
|
},
|
|
60
62
|
"devDependencies": {
|