fix(form-compile,form-validate): warn on invalid XDTO types, add Check 12

form-compile now warns when model uses runtime types like
FormDataStructure that don't exist in XML schema. Expanded cfg:
regex to cover all 25 known prefixes.

form-validate adds Check 12 — validates all <v8:Type> values:
ERROR for known-invalid types, WARN for unrecognized bare types,
pass-through for unknown namespaced types (future-proof).

Updated SKILL.md with full type reference and invalid type warning.
Updated docs/1c-form-spec.md with missing type groups.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Nick Shirokov
2026-04-05 18:07:07 +03:00
co-authored by Claude Opus 4.6
parent ff068202e3
commit dd88f78969
8 changed files with 264 additions and 17 deletions
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
# form-validate v1.1 — Validate 1C managed form
# form-validate v1.2 — Validate 1C managed form
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
import argparse
@@ -13,6 +13,40 @@ V8_NS = "http://v8.1c.ru/8.1/data/core"
NSMAP = {"f": F_NS, "v8": V8_NS}
KNOWN_INVALID_TYPES = {
'FormDataStructure', 'FormDataCollection', 'FormDataTree',
'FormDataTreeItem', 'FormDataCollectionItem',
'FormGroup', 'FormField', 'FormButton', 'FormDecoration', 'FormTable',
}
VALID_CLOSED_TYPES = {
'xs:boolean', 'xs:string', 'xs:decimal', 'xs:dateTime', 'xs:binary',
'v8:FillChecking', 'v8:Null', 'v8:StandardPeriod', 'v8:StandardBeginningDate', 'v8:Type',
'v8:TypeDescription', 'v8:UUID', 'v8:ValueListType', 'v8:ValueTable', 'v8:ValueTree',
'v8:Universal', 'v8:FixedArray', 'v8:FixedStructure',
'v8ui:Color', 'v8ui:Font', 'v8ui:FormattedString', 'v8ui:HorizontalAlign',
'v8ui:Picture', 'v8ui:SizeChangeMode', 'v8ui:VerticalAlign',
'dcsset:DataCompositionComparisonType', 'dcsset:DataCompositionFieldPlacement',
'dcsset:Filter', 'dcsset:SettingsComposer', 'dcsset:DataCompositionSettings',
'dcssch:DataCompositionSchema',
'dcscor:DataCompositionComparisonType', 'dcscor:DataCompositionGroupType',
'dcscor:DataCompositionPeriodAdditionType', 'dcscor:DataCompositionSortDirection', 'dcscor:Field',
'ent:AccountType', 'ent:AccumulationRecordType', 'ent:AccountingRecordType',
}
VALID_CFG_PREFIXES = {
'AccountingRegisterRecordSet', 'AccumulationRegisterRecordSet',
'BusinessProcessObject', 'BusinessProcessRef',
'CatalogObject', 'CatalogRef',
'ChartOfAccountsObject', 'ChartOfAccountsRef',
'ChartOfCalculationTypesObject', 'ChartOfCalculationTypesRef',
'ChartOfCharacteristicTypesObject', 'ChartOfCharacteristicTypesRef',
'ConstantsSet', 'DataProcessorObject', 'DocumentObject', 'DocumentRef',
'DynamicList', 'EnumRef', 'ExchangePlanObject', 'ExchangePlanRef',
'InformationRegisterRecordManager', 'InformationRegisterRecordSet',
'ReportObject', 'TaskObject', 'TaskRef',
}
def localname(el):
return etree.QName(el.tag).localname
@@ -588,6 +622,45 @@ def main():
if call_type_without_base:
report_warn("callType attributes found but no BaseForm \u2014 possible incorrect structure")
# --- Check 12: Type validation ---
if not stopped:
type_nodes = root.xpath('//v8:Type', namespaces={'v8': V8_NS})
type_error_count = 0
type_warn_count = 0
type_count = len(type_nodes)
for tn in type_nodes:
if stopped:
break
tv = (tn.text or "").strip()
if not tv:
continue
if tv in KNOWN_INVALID_TYPES:
report_error(f'12. Type "{tv}": invalid runtime/UI type (not valid in XDTO schema)')
type_error_count += 1
elif tv in VALID_CLOSED_TYPES:
pass # OK
elif tv.startswith("cfg:"):
suffix = tv[4:] # after "cfg:"
prefix = suffix.split(".")[0]
if prefix in VALID_CFG_PREFIXES or suffix == "DynamicList":
pass # OK
else:
report_warn(f'12. Type "{tv}": unrecognized cfg prefix')
type_warn_count += 1
elif ":" in tv:
pass # unknown namespace, pass through
else:
report_warn(f'12. Type "{tv}": bare type without namespace prefix')
type_warn_count += 1
if type_error_count == 0 and type_warn_count == 0:
if type_count > 0:
report_ok(f'12. Types: {type_count} values, all valid')
else:
report_ok('12. Types: no type values to check')
# --- Finalize ---
checks = ok_count + errors + warnings
if errors == 0 and warnings == 0 and not detailed: