From 05beeca7d3e27a07bf2a238533bd1ce836baf052 Mon Sep 17 00:00:00 2001 From: Nick Shirokov Date: Sun, 2 Aug 2026 11:47:02 +0300 Subject: [PATCH] =?UTF-8?q?fix(meta-compile,meta-decompile):=20=D0=BC?= =?UTF-8?q?=D0=BD=D0=BE=D0=B3=D0=BE=D1=8F=D0=B7=D1=8B=D1=87=D0=BD=D1=8B?= =?UTF-8?q?=D0=B5=20=D1=81=D0=B8=D0=BD=D0=BE=D0=BD=D0=B8=D0=BC=D1=8B=20?= =?UTF-8?q?=D0=B8=20=D1=80=D0=B5=D0=B3=D0=B8=D1=81=D1=82=D1=80=20RootURL?= =?UTF-8?q?=20=D0=B2=20=D1=81=D0=B5=D1=80=D0=B2=D0=B8=D1=81=D0=B0=D1=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Прогон сервисов на конфигурациях формата 2.17 (ERP + БП, 46 объектов) вскрыл то, чего не было в УТ: 25 совпадений из 46. ERP двуязычна, и синоним шаблона, метода, операции и параметра приезжает объектом {ru,en}. Компилятор интерполировал его в строку — в XML попадал литерал "@{ru=Версия; en=Version}" вместо пары языковых элементов. Значение теперь передаётся в эмиттер как есть, он и так умеет обе формы. RootURL сравнивался с дефолтом (имя в нижнем регистре) регистронезависимо, поэтому MobileAppReceiptScanner считался дефолтным и терял регистр при регенерации. Сравнение сделано регистрочувствительным — тот же класс ошибки, что уже ловили на синонимах методов. Спека meta-dsl-spec дополнена: объектные формы шаблонов, методов, операций и параметров; xdtoPackages как список; descriptorFileName; dataLockControlMode; нотация Кларка для типов из своего пространства имён; Use в ReuseSessions. Проверка: сервисы ERP+БП 46/46, сервисы УТ 25/25, сюита 76/76 на обоих портах, 1С-сертификация кейса с многоязычным синонимом пройдена. Co-Authored-By: Claude Opus 5 (1M context) --- .../meta-compile/scripts/meta-compile.ps1 | 10 +++--- .../meta-compile/scripts/meta-compile.py | 10 +++--- .../meta-decompile/scripts/meta-decompile.ps1 | 6 ++-- .../meta-decompile/scripts/meta-decompile.py | 4 ++- docs/meta-dsl-spec.md | 31 ++++++++++++++++--- .../cases/meta-compile/http-service.json | 3 +- .../http-service/HTTPServices/ТоварныйAPI.xml | 4 +++ 7 files changed, 50 insertions(+), 18 deletions(-) diff --git a/.claude/skills/meta-compile/scripts/meta-compile.ps1 b/.claude/skills/meta-compile/scripts/meta-compile.ps1 index d5313ec5..c10610a2 100644 --- a/.claude/skills/meta-compile/scripts/meta-compile.ps1 +++ b/.claude/skills/meta-compile/scripts/meta-compile.ps1 @@ -1,4 +1,4 @@ -# meta-compile v1.72 — Compile 1C metadata object from JSON +# meta-compile v1.73 — Compile 1C metadata object from JSON # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills param( [Parameter(Mandatory)] @@ -3922,7 +3922,7 @@ function Emit-URLTemplate { $template = "$tmplDef" } else { $template = if ($tmplDef.template) { "$($tmplDef.template)" } else { "/$($tmplName.ToLower())" } - if ($tmplDef.synonym) { $tmplSynonym = "$($tmplDef.synonym)" } + if ($tmplDef.synonym) { $tmplSynonym = $tmplDef.synonym } # объект {ru,en} НЕ интерполировать if ($tmplDef.comment) { $tmplComment = "$($tmplDef.comment)" } if ($tmplDef.methods) { $tmplDef.methods.PSObject.Properties | ForEach-Object { @@ -3953,7 +3953,7 @@ function Emit-URLTemplate { } else { $httpMethod = if ($mDef.httpMethod) { "$($mDef.httpMethod)" } else { 'GET' } $handler = if ($mDef.handler) { "$($mDef.handler)" } else { "${tmplName}${methodName}" } - $methodSynonym = if ($mDef.synonym) { "$($mDef.synonym)" } else { Split-CamelCase $methodName } + $methodSynonym = if ($mDef.synonym) { $mDef.synonym } else { Split-CamelCase $methodName } $methodComment = if ($mDef.comment) { "$($mDef.comment)" } else { "" } } @@ -3997,7 +3997,7 @@ function Emit-Operation { if ($opDef.transactioned -eq $true) { $transactioned = "true" } if ($opDef.handler) { $handler = "$($opDef.handler)" } if ($opDef.procedureName) { $handler = "$($opDef.procedureName)" } - if ($opDef.synonym) { $opSynonym = "$($opDef.synonym)" } + if ($opDef.synonym) { $opSynonym = $opDef.synonym } if ($opDef.comment) { $opComment = "$($opDef.comment)" } if ($opDef.dataLockControlMode) { $dataLock = "$($opDef.dataLockControlMode)" } if ($opDef.parameters) { @@ -4036,7 +4036,7 @@ function Emit-Operation { if ($paramDef.type) { $paramType = "$($paramDef.type)" } if ($paramDef.nillable -eq $false) { $paramNillable = "false" } if ($paramDef.direction) { $paramDir = "$($paramDef.direction)" } - if ($paramDef.synonym) { $paramSynonym = "$($paramDef.synonym)" } + if ($paramDef.synonym) { $paramSynonym = $paramDef.synonym } if ($paramDef.comment) { $paramComment = "$($paramDef.comment)" } } diff --git a/.claude/skills/meta-compile/scripts/meta-compile.py b/.claude/skills/meta-compile/scripts/meta-compile.py index 3025379b..884d68be 100644 --- a/.claude/skills/meta-compile/scripts/meta-compile.py +++ b/.claude/skills/meta-compile/scripts/meta-compile.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# meta-compile v1.72 — Compile 1C metadata object from JSON +# meta-compile v1.73 — Compile 1C metadata object from JSON # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills import argparse @@ -3832,7 +3832,7 @@ def emit_url_template(indent, tmpl_name, tmpl_def): else: template = str(tmpl_def['template']) if tmpl_def.get('template') else f'/{tmpl_name.lower()}' if tmpl_def.get('synonym'): - tmpl_synonym = str(tmpl_def['synonym']) + tmpl_synonym = tmpl_def['synonym'] # объект {ru,en} НЕ приводить к строке if tmpl_def.get('comment'): tmpl_comment = str(tmpl_def['comment']) if tmpl_def.get('methods'): @@ -3861,7 +3861,7 @@ def emit_url_template(indent, tmpl_name, tmpl_def): else: http_method = str(m_def.get('httpMethod') or 'GET') handler = str(m_def.get('handler') or f'{tmpl_name}{method_name}') - method_synonym = str(m_def['synonym']) if m_def.get('synonym') else split_camel_case(method_name) + method_synonym = m_def['synonym'] if m_def.get('synonym') else split_camel_case(method_name) method_comment = str(m_def.get('comment') or '') X(f'{indent}\t\t') X(f'{indent}\t\t\t') @@ -3901,7 +3901,7 @@ def emit_operation(indent, op_name, op_def): if op_def.get('procedureName'): handler = str(op_def['procedureName']) if op_def.get('synonym'): - op_synonym = str(op_def['synonym']) + op_synonym = op_def['synonym'] if op_def.get('comment'): op_comment = str(op_def['comment']) if op_def.get('dataLockControlMode'): @@ -3940,7 +3940,7 @@ def emit_operation(indent, op_name, op_def): if param_def.get('direction'): param_dir = str(param_def['direction']) if param_def.get('synonym'): - param_synonym = str(param_def['synonym']) + param_synonym = param_def['synonym'] if param_def.get('comment'): param_comment = str(param_def['comment']) X(f'{indent}\t\t') diff --git a/.claude/skills/meta-decompile/scripts/meta-decompile.ps1 b/.claude/skills/meta-decompile/scripts/meta-decompile.ps1 index 3df1ea41..b8e9a49f 100644 --- a/.claude/skills/meta-decompile/scripts/meta-decompile.ps1 +++ b/.claude/skills/meta-decompile/scripts/meta-decompile.ps1 @@ -1,4 +1,4 @@ -# meta-decompile v0.58 — XML объекта метаданных 1С → JSON-черновик формата meta-compile +# meta-decompile v0.59 — XML объекта метаданных 1С → JSON-черновик формата meta-compile # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills # # Поддержаны: Catalog, ExchangePlan, ChartOfCharacteristicTypes, ChartOfAccounts, ChartOfCalculationTypes, Document, @@ -812,7 +812,9 @@ if ($objType -eq 'WebService') { # HTTPService — корневой URL, повторное использование сеансов, время жизни сеанса. # Шаблоны URL с методами разбираются в блоке ChildObjects. if ($objType -eq 'HTTPService') { - $ru = P 'RootURL'; if ($ru -and $ru -ne $objName.ToLower()) { $dsl['rootURL'] = $ru } + # -cne: дефолт — имя в нижнем регистре, но реальный RootURL часто отличается ТОЛЬКО регистром + # (MobileAppReceiptScanner), и регистронезависимое сравнение считало его дефолтным. + $ru = P 'RootURL'; if ($ru -and $ru -cne $objName.ToLower()) { $dsl['rootURL'] = $ru } Add-EnumProp 'reuseSessions' 'ReuseSessions' 'DontUse' Add-IntProp 'sessionMaxAge' 'SessionMaxAge' 20 } diff --git a/.claude/skills/meta-decompile/scripts/meta-decompile.py b/.claude/skills/meta-decompile/scripts/meta-decompile.py index 15393e79..4ad9d808 100644 --- a/.claude/skills/meta-decompile/scripts/meta-decompile.py +++ b/.claude/skills/meta-decompile/scripts/meta-decompile.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# meta-decompile v0.58 — XML объекта метаданных 1С → JSON-черновик формата meta-compile +# meta-decompile v0.59 — XML объекта метаданных 1С → JSON-черновик формата meta-compile # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills # # Зеркало meta-decompile.ps1 (КАНОН). Структура 1:1 — те же имена функций, порядок, комментарии. @@ -1234,6 +1234,8 @@ def build_dsl(): # HTTPService — корневой URL, повторное использование сеансов, время жизни сеанса. if obj_type == 'HTTPService': ru = P('RootURL') + # Сравнение регистрочувствительное: реальный RootURL часто отличается от дефолта + # только регистром (MobileAppReceiptScanner). if ru and ru != obj_name.lower(): dsl['rootURL'] = ru add_enum_prop('reuseSessions', 'ReuseSessions', 'DontUse') diff --git a/docs/meta-dsl-spec.md b/docs/meta-dsl-spec.md index b8fd1968..3ad852fd 100644 --- a/docs/meta-dsl-spec.md +++ b/docs/meta-dsl-spec.md @@ -1462,17 +1462,24 @@ ChildObjects и модулей. | Поле JSON | Умолчание | XML элемент | |-----------|----------|-------------| | `rootURL` | = имя (lowercase) | RootURL | -| `reuseSessions` | `DontUse` | ReuseSessions | +| `reuseSessions` | `DontUse` | ReuseSessions (`DontUse` / `Use` / `AutoUse`) | | `sessionMaxAge` | `20` | SessionMaxAge | | `urlTemplates` | `{}` | → URLTemplate в ChildObjects (§16) | +Шаблон: строкой — только путь; объектом — `template`, `synonym`, `comment`, `methods`. +Метод: строкой — только HTTP-метод; объектом — `httpMethod`, `handler`, `synonym`, `comment`. +Обработчик по умолчанию `<ИмяШаблона><ИмяМетода>`; в типовых конфигурациях он обычно +произвольный, тогда задают `handler` явно. + Модули: `Ext/Module.bsl` (пустой). ```json { "type": "HTTPService", "name": "API", "rootURL": "api", "urlTemplates": { - "Users": { "template": "/v1/users", "methods": { "Get": "GET", "Create": "POST" } } + "Users": { "template": "/v1/users", "methods": { "Get": "GET", "Create": "POST" } }, + "rpc": { "template": "/rpc", "methods": { + "post": { "httpMethod": "POST", "handler": "УдаленныйВызовЧерезТелоЗапроса" } } } } } ``` @@ -1482,21 +1489,37 @@ ChildObjects и модулей. | Поле JSON | Умолчание | XML элемент | |-----------|----------|-------------| | `namespace` | `""` | Namespace | -| `xdtoPackages` | `""` | XDTOPackages | -| `reuseSessions` | `DontUse` | ReuseSessions | +| `xdtoPackages` | `[]` | XDTOPackages (список, см. ниже) | +| `descriptorFileName` | `<Имя>.1cws` | DescriptorFileName | +| `reuseSessions` | `DontUse` | ReuseSessions (`DontUse` / `Use` / `AutoUse`) | | `sessionMaxAge` | `20` | SessionMaxAge | | `operations` | `{}` | → Operation в ChildObjects (§17) | +`xdtoPackages` — массив: значение `XDTOPackage.Имя` даёт ссылку на пакет конфигурации, +любое другое трактуется как URI внешнего пространства имён. + +Операция: `returnType`, `nillable`, `transactioned`, `procedureName` (синоним ключа — `handler`), +`dataLockControlMode` (умолчание `Managed`), `synonym`, `comment`, `parameters`. +Параметр: `type`, `nillable`, `direction`, `synonym`, `comment`; строкой — только тип. + +Тип из собственного пространства имён задаётся нотацией Кларка `{uri}ИмяТипа` — компилятор +объявит локальный `xmlns` в теге, как это делает платформа. + Модули: `Ext/Module.bsl` (пустой). ```json { "type": "WebService", "name": "DataExchange", "namespace": "http://www.1c.ru/DataExchange", + "xdtoPackages": ["XDTOPackage.ОбменДанными", "http://v8.1c.ru/8.3/data/ext"], "operations": { "TestConnection": { "returnType": "xs:boolean", "handler": "ПроверкаПодключения", "parameters": { "ErrorMessage": { "type": "xs:string", "direction": "Out" } } + }, + "Exchange": { + "returnType": "{http://www.1c.ru/DataExchange}Message", + "parameters": { "Data": { "type": "{http://www.1c.ru/DataExchange}Message", "nillable": false } } } } } diff --git a/tests/skills/cases/meta-compile/http-service.json b/tests/skills/cases/meta-compile/http-service.json index 3a839607..92581f7c 100644 --- a/tests/skills/cases/meta-compile/http-service.json +++ b/tests/skills/cases/meta-compile/http-service.json @@ -19,7 +19,8 @@ "template": "/rpc", "comment": "Удалённый вызов", "methods": { - "post": { "httpMethod": "POST", "handler": "ВызовЧерезТелоЗапроса", "synonym": "Post" } + "post": { "httpMethod": "POST", "handler": "ВызовЧерезТелоЗапроса", + "synonym": { "ru": "Отправить", "en": "Post" } } } } } diff --git a/tests/skills/cases/meta-compile/snapshots/http-service/HTTPServices/ТоварныйAPI.xml b/tests/skills/cases/meta-compile/snapshots/http-service/HTTPServices/ТоварныйAPI.xml index d446b118..506c86d5 100644 --- a/tests/skills/cases/meta-compile/snapshots/http-service/HTTPServices/ТоварныйAPI.xml +++ b/tests/skills/cases/meta-compile/snapshots/http-service/HTTPServices/ТоварныйAPI.xml @@ -77,6 +77,10 @@ ru + Отправить + + + en Post