feat(xdto-compile,xdto-decompile,xdto-validate): пакеты XDTO из XML-схемы

Три навыка для работы с пакетами XDTO. Формат описания — обычная XSD,
своего DSL нет: рутину снимает конвертер (локальные объявления префиксов
dNpM на каждой ссылке, инвертированная кратность lowerBound/upperBound,
фасеты атрибутами вместо дочерних элементов, обязательный порядок
элементов верхнего уровня). То, чего XSD выразить не может — nillable
у атрибута, qualified у свойства, «атрибут записан явно» — едет
атрибутами из пространства имён модели XDTO по правилу «то же имя,
что в Package.bin». Свойства объекта метаданных живут в xs:appinfo,
поэтому пара decompile → compile замыкается без потерь.

Инвариант bin → xsd → bin проверен побайтово на 760 пакетах выгрузок
Бухгалтерии и ERP 8.3.24 (харнесс debug/xdto/roundtrip-corpus.mjs).
Сборка из рукописной XSD проверена загрузкой в базу 8.3.24 — именно
она вскрыла обязательный порядок import→property→valueType→objectType,
невидимый для корпусной сверки: все выгрузки уже канонические.

xdto-validate ловит два класса тихих дефектов, которые платформа не
диагностирует: подмену неразрешённого чужого типа на xs:anyType при
импорте XML-схемы и nillable у свойства-атрибута, теряемый экспортом
схемы в Конфигураторе.

Тесты: 18 снэпшот-кейсов на синтетических схемах (типовые конфигурации
в репозиторий не тащим), паритет PS↔PY на общих эталонах. Раннер
получил caseFiles — копирование файлов кейса в workDir для навыков
с файловым, а не JSON входом.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Nick Shirokov
2026-07-25 21:21:57 +03:00
co-authored by Claude Opus 5
parent d544071b1e
commit d05aef54b4
140 changed files with 9601 additions and 1 deletions
@@ -0,0 +1,11 @@
{
"script": "xdto-validate/scripts/xdto-validate",
"setup": "empty-config",
"args": [
{ "flag": "-PackagePath", "from": "workPath", "field": "packagePath" }
],
"snapshot": {
"root": "workDir",
"normalizeUuids": true
}
}
@@ -0,0 +1,17 @@
{
"name": "несогласованные фасеты: length с min/maxLength, fractionDigits > totalDigits",
"caseFiles": [
"pkg-facets/XDTOPackages/facets.xml",
"pkg-facets/XDTOPackages/facets/Ext/Package.bin"
],
"params": { "packagePath": "pkg-facets/XDTOPackages/facets" },
"expectError": true,
"expect": {
"stdoutContains": [
"length несовместим с minLength/maxLength",
"minLength (3) больше maxLength (2)",
"fractionDigits (9) больше totalDigits (5)"
]
},
"noSnapshot": "навык только читает — сравнивать нечего, проверка через stdoutContains"
}
@@ -0,0 +1,12 @@
{
"name": "nillable у свойства-атрибута — не переживёт экспорт XML-схемы",
"caseFiles": [
"pkg-nillattr/XDTOPackages/nillattr.xml",
"pkg-nillattr/XDTOPackages/nillattr/Ext/Package.bin"
],
"params": { "packagePath": "pkg-nillattr/XDTOPackages/nillattr" },
"expect": {
"stdoutContains": ["nillable", "form=\"Attribute\""]
},
"noSnapshot": "навык только читает — сравнивать нечего, проверка через stdoutContains"
}
@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<MetaDataObject xmlns="http://v8.1c.ru/8.3/MDClasses" xmlns:v8="http://v8.1c.ru/8.1/data/core" version="2.17">
<XDTOPackage uuid="11111111-2222-3333-4444-555555555555">
<Properties>
<Name>anytype</Name>
<Synonym>
<v8:item>
<v8:lang>ru</v8:lang>
<v8:content>anytype</v8:content>
</v8:item>
</Synonym>
<Comment/>
<Namespace>urn:test:anytype</Namespace>
</Properties>
</XDTOPackage>
</MetaDataObject>
@@ -0,0 +1,6 @@
<package xmlns="http://v8.1c.ru/8.1/xdto" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" targetNamespace="urn:test:anytype">
<import namespace="urn:test:зависимость"/>
<objectType name="Документ">
<property name="Контакт" type="xs:anyType"/>
</objectType>
</package>
@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<MetaDataObject xmlns="http://v8.1c.ru/8.3/MDClasses" xmlns:v8="http://v8.1c.ru/8.1/data/core" version="2.17">
<XDTOPackage uuid="11111111-2222-3333-4444-555555555555">
<Properties>
<Name>facets</Name>
<Synonym>
<v8:item>
<v8:lang>ru</v8:lang>
<v8:content>facets</v8:content>
</v8:item>
</Synonym>
<Comment/>
<Namespace>urn:test:facets</Namespace>
</Properties>
</XDTOPackage>
</MetaDataObject>
@@ -0,0 +1,4 @@
<package xmlns="http://v8.1c.ru/8.1/xdto" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" targetNamespace="urn:test:facets">
<valueType name="Плохой" base="xs:string" variety="Atomic" length="10" minLength="3" maxLength="2"/>
<valueType name="Число" base="xs:decimal" variety="Atomic" totalDigits="5" fractionDigits="9"/>
</package>
@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<MetaDataObject xmlns="http://v8.1c.ru/8.3/MDClasses" xmlns:v8="http://v8.1c.ru/8.1/data/core" version="2.17">
<XDTOPackage uuid="11111111-2222-3333-4444-555555555555">
<Properties>
<Name>nillattr</Name>
<Synonym>
<v8:item>
<v8:lang>ru</v8:lang>
<v8:content>nillattr</v8:content>
</v8:item>
</Synonym>
<Comment/>
<Namespace>urn:test:nillattr</Namespace>
</Properties>
</XDTOPackage>
</MetaDataObject>
@@ -0,0 +1,5 @@
<package xmlns="http://v8.1c.ru/8.1/xdto" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" targetNamespace="urn:test:nillattr">
<objectType name="Контакт">
<property name="Представление" type="xs:string" lowerBound="0" nillable="true" form="Attribute"/>
</objectType>
</package>
@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<MetaDataObject xmlns="http://v8.1c.ru/8.3/MDClasses" xmlns:v8="http://v8.1c.ru/8.1/data/core" version="2.17">
<XDTOPackage uuid="11111111-2222-3333-4444-555555555555">
<Properties>
<Name>unresolved</Name>
<Synonym>
<v8:item>
<v8:lang>ru</v8:lang>
<v8:content>unresolved</v8:content>
</v8:item>
</Synonym>
<Comment/>
<Namespace>urn:test:unresolved</Namespace>
</Properties>
</XDTOPackage>
</MetaDataObject>
@@ -0,0 +1,6 @@
<package xmlns="http://v8.1c.ru/8.1/xdto" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" targetNamespace="urn:test:unresolved">
<objectType name="Документ">
<property xmlns:d3p1="urn:test:unresolved" name="Поле" type="d3p1:НетТакогоТипа"/>
<property xmlns:d3p1="urn:test:чужой" name="Чужое" type="d3p1:Тип"/>
</objectType>
</package>
@@ -0,0 +1,15 @@
{
"name": "тихая деградация: xs:anyType при объявленном import",
"caseFiles": [
"pkg-anytype/XDTOPackages/anytype.xml",
"pkg-anytype/XDTOPackages/anytype/Ext/Package.bin"
],
"params": { "packagePath": "pkg-anytype/XDTOPackages/anytype" },
"expect": {
"stdoutContains": [
"тихая деградация",
"ни один тип из этого пространства имён не используется"
]
},
"noSnapshot": "навык только читает — сравнивать нечего, проверка через stdoutContains"
}
@@ -0,0 +1,16 @@
{
"name": "неразрешимые ссылки: нет локального типа и нет import под чужой ns",
"caseFiles": [
"pkg-unresolved/XDTOPackages/unresolved.xml",
"pkg-unresolved/XDTOPackages/unresolved/Ext/Package.bin"
],
"params": { "packagePath": "pkg-unresolved/XDTOPackages/unresolved" },
"expectError": true,
"expect": {
"stdoutContains": [
"не разрешается: в пакете нет типа \"НетТакогоТипа\"",
"не объявлен"
]
},
"noSnapshot": "навык только читает — сравнивать нечего, проверка через stdoutContains"
}
@@ -0,0 +1,15 @@
{
"name": "корректный пакет, собранный xdto-compile — ошибок нет",
"caseFiles": ["valid.xsd"],
"preRun": [
{
"script": "xdto-compile/scripts/xdto-compile",
"args": { "-XsdPath": "{workDir}/valid.xsd", "-OutputDir": "{workDir}" }
}
],
"params": { "packagePath": "XDTOPackages/valid" },
"expect": {
"stdoutContains": ["Validation OK"]
},
"noSnapshot": "навык только читает — сравнивать нечего, проверка через stdoutContains"
}
@@ -0,0 +1,16 @@
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:tns="urn:test:valid"
targetNamespace="urn:test:valid">
<xs:simpleType name="СуммаТип">
<xs:restriction base="xs:decimal">
<xs:totalDigits value="18"/>
<xs:fractionDigits value="2"/>
</xs:restriction>
</xs:simpleType>
<xs:complexType name="Платёж">
<xs:sequence>
<xs:element name="Сумма" type="tns:СуммаТип"/>
</xs:sequence>
<xs:attribute name="Номер" type="xs:string"/>
</xs:complexType>
</xs:schema>