diff --git a/.claude/skills/meta-decompile/scripts/meta-decompile.ps1 b/.claude/skills/meta-decompile/scripts/meta-decompile.ps1 index 8db3ca6b1..aaff1e4d9 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.67 — XML объекта метаданных 1С → JSON-черновик формата meta-compile +# meta-decompile v0.68 — XML объекта метаданных 1С → JSON-черновик формата meta-compile # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills # # Поддержаны: Catalog, ExchangePlan, ChartOfCharacteristicTypes, ChartOfAccounts, ChartOfCalculationTypes, Document, @@ -207,8 +207,9 @@ function Get-TypeShorthand { $parts += $fr; break # Date | DateTime } '(^|:)base64Binary$' { - # xs:base64Binary — это ДвоичныеДанные, если рядом есть свои квалификаторы; - # ХранилищеЗначения платформа пишет как v8:ValueStorage, но принимает и эту форму. + # xs:base64Binary — всегда ДвоичныеДанные (ХранилищеЗначения — это v8:ValueStorage). + # Узел без квалификаторов встречается только в рукописном XML: замерено на 8.3.24.1691 — + # платформа читает его как безлимит (Length 0, Variable) и так же выгружает обратно. $bq = $typeNode.SelectSingleNode('v8:BinaryDataQualifiers', $nsm) if ($bq) { $blen = $bq.SelectSingleNode('v8:Length', $nsm) @@ -220,7 +221,7 @@ function Get-TypeShorthand { if ($balVal -eq 'Variable' -and $blenVal) { $parts += "BinaryData($blenVal)" } elseif ($blenVal -and $blenVal -ne '4294967292') { $parts += "BinaryData($blenVal,fixed)" } else { $parts += 'BinaryData' } - } else { $parts += 'ValueStorage' } + } else { $parts += 'BinaryData(0)' } break } default { $parts += (Strip-NsPrefix $raw) } # cfg:CatalogRef.X → CatalogRef.X diff --git a/.claude/skills/meta-decompile/scripts/meta-decompile.py b/.claude/skills/meta-decompile/scripts/meta-decompile.py index c27cccc12..ff6fc4f6f 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.67 — XML объекта метаданных 1С → JSON-черновик формата meta-compile +# meta-decompile v0.68 — XML объекта метаданных 1С → JSON-черновик формата meta-compile # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills # # Зеркало meta-decompile.ps1 (КАНОН). Структура 1:1 — те же имена функций, порядок, комментарии. @@ -311,8 +311,9 @@ def get_type_shorthand(type_node): fr = _text(dn) parts.append(fr) # Date | DateTime elif re.search(r'(^|:)base64Binary$', raw, re.I): - # xs:base64Binary — это ДвоичныеДанные, если рядом есть свои квалификаторы; - # ХранилищеЗначения платформа пишет как v8:ValueStorage, но принимает и эту форму. + # xs:base64Binary — всегда ДвоичныеДанные (ХранилищеЗначения — это v8:ValueStorage). + # Узел без квалификаторов встречается только в рукописном XML: замерено на 8.3.24.1691 — + # платформа читает его как безлимит (Length 0, Variable) и так же выгружает обратно. bq = type_node.find('v8:BinaryDataQualifiers', NS) if bq is not None: blen = bq.find('v8:Length', NS) @@ -328,7 +329,7 @@ def get_type_shorthand(type_node): else: parts.append('BinaryData') else: - parts.append('ValueStorage') + parts.append('BinaryData(0)') else: parts.append(strip_ns_prefix(raw)) # cfg:CatalogRef.X → CatalogRef.X elif ln == 'TypeSet': diff --git a/.claude/skills/meta-info/scripts/meta-info.ps1 b/.claude/skills/meta-info/scripts/meta-info.ps1 index 4baf89f32..5ec676dfe 100644 --- a/.claude/skills/meta-info/scripts/meta-info.ps1 +++ b/.claude/skills/meta-info/scripts/meta-info.ps1 @@ -1,4 +1,4 @@ -# meta-info v1.13 — Compact summary of 1C metadata object +# meta-info v1.14 — Compact summary of 1C metadata object # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills [CmdletBinding(PositionalBinding=$false)] param( @@ -233,12 +233,10 @@ function Format-SingleType([string]$raw, $parentNode) { } "v8:ValueStorage" { return "ХранилищеЗначения" } "v8:UUID" { return "УникальныйИдентификатор" } - "xs:base64Binary" { - # ДвоичныеДанные только при своих квалификаторах: без них это вторая форма - # ХранилищаЗначения, которую платформа принимает (так же различает meta-decompile). - $bq = $parentNode.SelectSingleNode("v8:BinaryDataQualifiers", $ns) - if ($bq) { return "ДвоичныеДанные" } else { return "ХранилищеЗначения" } - } + # xs:base64Binary — всегда ДвоичныеДанные, и без квалификаторов тоже: замерено + # на 8.3.24.1691 — такой узел платформа загружает и выгружает обратно как безлимитные + # двоичные данные (Length 0, AllowedLength Variable), а не как ХранилищеЗначения. + "xs:base64Binary" { return "ДвоичныеДанные" } "v8:Null" { return "Null" } default { # Normalize d5p1:/dNpN: -> cfg: diff --git a/.claude/skills/meta-info/scripts/meta-info.py b/.claude/skills/meta-info/scripts/meta-info.py index 8c71683ec..93bb765a4 100644 --- a/.claude/skills/meta-info/scripts/meta-info.py +++ b/.claude/skills/meta-info/scripts/meta-info.py @@ -1,4 +1,4 @@ -# meta-info v1.13 — Compact summary of 1C metadata object (Python port) +# meta-info v1.14 — Compact summary of 1C metadata object (Python port) # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills import argparse import os @@ -320,10 +320,10 @@ def format_single_type(raw, parent_node): if raw == "v8:UUID": return "УникальныйИдентификатор" if raw == "xs:base64Binary": - # ДвоичныеДанные только при своих квалификаторах: без них это вторая форма - # ХранилищаЗначения, которую платформа принимает (так же различает meta-decompile). - bq = find(parent_node, "v8:BinaryDataQualifiers") - return "ДвоичныеДанные" if bq is not None else "ХранилищеЗначения" + # xs:base64Binary — всегда ДвоичныеДанные, и без квалификаторов тоже: замерено + # на 8.3.24.1691 — такой узел платформа загружает и выгружает обратно как безлимитные + # двоичные данные (Length 0, AllowedLength Variable), а не как ХранилищеЗначения. + return "ДвоичныеДанные" if raw == "v8:Null": return "Null" # Normalize d5p1:/dNpN: → cfg: (both map to same namespace) diff --git a/docs/meta-dsl-spec.md b/docs/meta-dsl-spec.md index 871939ae7..33bb79d38 100644 --- a/docs/meta-dsl-spec.md +++ b/docs/meta-dsl-spec.md @@ -2096,9 +2096,12 @@ ChildObjects и модулей. сворачивает в неё только точное совпадение 4294967292/Fixed; любая другая длина возвращается со скобками. Посторонний текст в скобках (`BinaryData(abc)`) — ошибка компиляции. -`BinaryData` (ДвоичныеДанные) — **не** `ValueStorage`: платформа пишет его как `xs:base64Binary` -с `BinaryDataQualifiers`, тогда как ХранилищеЗначения — как `v8:ValueStorage`. Декомпилятор -различает их по наличию квалификаторов. +`BinaryData` (ДвоичныеДанные) — **не** `ValueStorage`: это разные типы и разные узлы XML — +`xs:base64Binary` и `v8:ValueStorage` соответственно. Квалификаторы их НЕ различают: `xs:base64Binary` +без `BinaryDataQualifiers` — тоже двоичные данные, просто безлимитные. Замерено на 8.3.24.1691: +такой узел загружается и выгружается обратно как `Length 0` / `AllowedLength Variable`, то есть +`BinaryData(0)` — именно так его возвращает декомпилятор. Рукописный XML — единственное место, +где узел без квалификаторов встречается: сама платформа их пишет всегда. **Составной тип у поля платформа запрещает** — при загрузке отвечает «Поле не может иметь составной тип», хотя «Руководство разработчика» (17.5.2.3.4) его разрешает. Документация опережает реализацию. diff --git a/tests/skills/cases/meta-info/base64binary-no-qualifiers.json b/tests/skills/cases/meta-info/base64binary-no-qualifiers.json new file mode 100644 index 000000000..9b055220e --- /dev/null +++ b/tests/skills/cases/meta-info/base64binary-no-qualifiers.json @@ -0,0 +1,9 @@ +{ + "name": "xs:base64Binary без квалификаторов — тоже ДвоичныеДанные (так читает платформа)", + "setup": "fixture:base64binary-no-qualifiers", + "params": { "objectPath": "Catalogs/Вложения.xml" }, + "expect": { + "stdoutContains": ["ДвоичныеДанные"], + "stdoutNotContains": ["ХранилищеЗначения"] + } +} diff --git a/tests/skills/cases/meta-info/fixtures/valuestorage-base64/Catalogs/Вложения.xml b/tests/skills/cases/meta-info/fixtures/base64binary-no-qualifiers/Catalogs/Вложения.xml similarity index 86% rename from tests/skills/cases/meta-info/fixtures/valuestorage-base64/Catalogs/Вложения.xml rename to tests/skills/cases/meta-info/fixtures/base64binary-no-qualifiers/Catalogs/Вложения.xml index 5ea1c237f..0f1d5e092 100644 --- a/tests/skills/cases/meta-info/fixtures/valuestorage-base64/Catalogs/Вложения.xml +++ b/tests/skills/cases/meta-info/fixtures/base64binary-no-qualifiers/Catalogs/Вложения.xml @@ -1,26 +1,26 @@  - + - a11e37c8-2444-4782-b7bd-8aea08cb9e59 - 67e5e532-fae8-43f7-b462-e873cb74032b + 530584ee-68fe-4c87-874b-31db5d632ed5 + 151b840f-7889-44d7-abe6-f32230786b00 - 8f8a87cf-1986-4972-83eb-d7c86b9f53ab - 26b89d83-cfca-4c88-85f3-7001585e4a9e + 97f676c1-ad3e-47db-ae76-7453f36679d7 + c89cba3e-afdd-404f-93f6-ad643fd24ed2 - 5f2cdd97-e03d-485d-ad58-a24996d3d050 - 5cb35f34-0105-4770-bb71-bfefe7c8dde0 + 8ce45d3d-4670-4bbd-af79-53bd8c2eff3e + 433231d3-72ef-456b-a26b-e6f7bb6073d9 - bc462ca1-8645-40de-8805-05528f49b77d - 7a34ec0e-1899-4005-aab7-73f55df542cd + 658db93d-1c94-4f7d-a61d-24d87a4481be + 4bc9957e-9e0c-495a-8fde-86d0c6a28d59 - 772a6cd5-a420-46eb-89f9-580707183957 - 80c973c9-2823-40ee-8149-d6e4a8182a36 + a80fe341-afcb-4dbd-9460-d88f2893090c + 1c89452b-5baa-40e7-b4d2-005ad2bd73b9 @@ -87,7 +87,7 @@ false - + Данные @@ -127,7 +127,7 @@ Use - + Файл diff --git a/tests/skills/cases/meta-info/fixtures/base64binary-no-qualifiers/Catalogs/Вложения/Ext/ObjectModule.bsl b/tests/skills/cases/meta-info/fixtures/base64binary-no-qualifiers/Catalogs/Вложения/Ext/ObjectModule.bsl new file mode 100644 index 000000000..5f282702b --- /dev/null +++ b/tests/skills/cases/meta-info/fixtures/base64binary-no-qualifiers/Catalogs/Вложения/Ext/ObjectModule.bsl @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/tests/skills/cases/meta-info/fixtures/base64binary-no-qualifiers/Configuration.xml b/tests/skills/cases/meta-info/fixtures/base64binary-no-qualifiers/Configuration.xml new file mode 100644 index 000000000..47887238d --- /dev/null +++ b/tests/skills/cases/meta-info/fixtures/base64binary-no-qualifiers/Configuration.xml @@ -0,0 +1,252 @@ + + + + + + 9cd510cd-abfc-11d4-9434-004095e12fc7 + 075ea794-2a38-43bd-9c37-64ff79e9f2b5 + + + 9fcd25a0-4822-11d4-9414-008048da11f9 + fdea22a7-0420-49e4-a9d1-28c2717b8b6f + + + e3687481-0a87-462c-a166-9f34594f9bba + 3872cb5b-4cce-4b4e-8c10-d337e7d97448 + + + 9de14907-ec23-4a07-96f0-85521cb6b53b + 5aed964c-3b0e-44c7-801a-5f018aeef7c2 + + + 51f2d5d8-ea4d-4064-8892-82951750031e + 4aad5a14-7415-4ce3-9fdd-7eb09a1019d9 + + + e68182ea-4237-4383-967f-90c1e3370bc7 + 19830fba-ec25-4015-bf5e-c440ac37185b + + + fb282519-d103-4dd3-bc12-cb271d631dfc + 6e22caf7-13bd-4ace-858f-8fd79049aa1e + + + + ПробаХранилища + + + ru + ПробаХранилища + + + + + Version8_3_24 + ManagedApplication + + PlatformApplication + + Russian + + + + + false + false + false + + + + + + + + + + + + + + + + + + + + + + Biometrics + true + + + Location + false + + + BackgroundLocation + false + + + BluetoothPrinters + false + + + WiFiPrinters + false + + + Contacts + false + + + Calendars + false + + + PushNotifications + false + + + LocalNotifications + false + + + InAppPurchases + false + + + PersonalComputerFileExchange + false + + + Ads + false + + + NumberDialing + false + + + CallProcessing + false + + + CallLog + false + + + AutoSendSMS + false + + + ReceiveSMS + false + + + SMSLog + false + + + Camera + false + + + Microphone + false + + + MusicLibrary + false + + + PictureAndVideoLibraries + false + + + AudioPlaybackAndVibration + false + + + BackgroundAudioPlaybackAndVibration + false + + + InstallPackages + false + + + OSBackup + true + + + ApplicationUsageStatistics + false + + + BarcodeScanning + false + + + BackgroundAudioRecording + false + + + AllFilesAccess + false + + + Videoconferences + false + + + NFC + false + + + DocumentScanning + false + + + SpeechToText + false + + + Geofences + false + + + IncomingShareRequests + false + + + AllIncomingShareRequestsTypesProcessing + false + + + + + + Normal + + + Language.Русский + + + + + + Managed + NotAutoFree + DontUse + DontUse + TaxiEnableVersion8_2 + DontUse + Version8_3_24 + + + + Русский + Вложения + + + \ No newline at end of file diff --git a/tests/skills/cases/meta-info/fixtures/base64binary-no-qualifiers/Ext/ClientApplicationInterface.xml b/tests/skills/cases/meta-info/fixtures/base64binary-no-qualifiers/Ext/ClientApplicationInterface.xml new file mode 100644 index 000000000..49bbbe202 --- /dev/null +++ b/tests/skills/cases/meta-info/fixtures/base64binary-no-qualifiers/Ext/ClientApplicationInterface.xml @@ -0,0 +1,18 @@ + + + + + cbab57f2-a0f3-4f0a-89ea-4cb19570ab75 + + + + + b553047f-c9aa-4157-978d-448ecad24248 + + + + + + + + \ No newline at end of file diff --git a/tests/skills/cases/meta-info/fixtures/base64binary-no-qualifiers/Languages/Русский.xml b/tests/skills/cases/meta-info/fixtures/base64binary-no-qualifiers/Languages/Русский.xml new file mode 100644 index 000000000..9f0da83f5 --- /dev/null +++ b/tests/skills/cases/meta-info/fixtures/base64binary-no-qualifiers/Languages/Русский.xml @@ -0,0 +1,16 @@ + + + + + Русский + + + ru + Русский + + + + ru + + + \ No newline at end of file diff --git a/tests/skills/cases/meta-info/snapshots/valuestorage-base64/Catalogs/Вложения.xml b/tests/skills/cases/meta-info/snapshots/base64binary-no-qualifiers/Catalogs/Вложения.xml similarity index 100% rename from tests/skills/cases/meta-info/snapshots/valuestorage-base64/Catalogs/Вложения.xml rename to tests/skills/cases/meta-info/snapshots/base64binary-no-qualifiers/Catalogs/Вложения.xml diff --git a/tests/skills/cases/meta-info/snapshots/base64binary-no-qualifiers/Catalogs/Вложения/Ext/ObjectModule.bsl b/tests/skills/cases/meta-info/snapshots/base64binary-no-qualifiers/Catalogs/Вложения/Ext/ObjectModule.bsl new file mode 100644 index 000000000..e69de29bb diff --git a/tests/skills/cases/meta-info/snapshots/base64binary-no-qualifiers/Configuration.xml b/tests/skills/cases/meta-info/snapshots/base64binary-no-qualifiers/Configuration.xml new file mode 100644 index 000000000..18b33b7dc --- /dev/null +++ b/tests/skills/cases/meta-info/snapshots/base64binary-no-qualifiers/Configuration.xml @@ -0,0 +1,252 @@ + + + + + + UUID-002 + UUID-003 + + + UUID-004 + UUID-005 + + + UUID-006 + UUID-007 + + + UUID-008 + UUID-009 + + + UUID-010 + UUID-011 + + + UUID-012 + UUID-013 + + + UUID-014 + UUID-015 + + + + ПробаХранилища + + + ru + ПробаХранилища + + + + + Version8_3_24 + ManagedApplication + + PlatformApplication + + Russian + + + + + false + false + false + + + + + + + + + + + + + + + + + + + + + + Biometrics + true + + + Location + false + + + BackgroundLocation + false + + + BluetoothPrinters + false + + + WiFiPrinters + false + + + Contacts + false + + + Calendars + false + + + PushNotifications + false + + + LocalNotifications + false + + + InAppPurchases + false + + + PersonalComputerFileExchange + false + + + Ads + false + + + NumberDialing + false + + + CallProcessing + false + + + CallLog + false + + + AutoSendSMS + false + + + ReceiveSMS + false + + + SMSLog + false + + + Camera + false + + + Microphone + false + + + MusicLibrary + false + + + PictureAndVideoLibraries + false + + + AudioPlaybackAndVibration + false + + + BackgroundAudioPlaybackAndVibration + false + + + InstallPackages + false + + + OSBackup + true + + + ApplicationUsageStatistics + false + + + BarcodeScanning + false + + + BackgroundAudioRecording + false + + + AllFilesAccess + false + + + Videoconferences + false + + + NFC + false + + + DocumentScanning + false + + + SpeechToText + false + + + Geofences + false + + + IncomingShareRequests + false + + + AllIncomingShareRequestsTypesProcessing + false + + + + + + Normal + + + Language.Русский + + + + + + Managed + NotAutoFree + DontUse + DontUse + TaxiEnableVersion8_2 + DontUse + Version8_3_24 + + + + Русский + Вложения + + + \ No newline at end of file diff --git a/tests/skills/cases/meta-info/snapshots/base64binary-no-qualifiers/Ext/ClientApplicationInterface.xml b/tests/skills/cases/meta-info/snapshots/base64binary-no-qualifiers/Ext/ClientApplicationInterface.xml new file mode 100644 index 000000000..3c1161b2d --- /dev/null +++ b/tests/skills/cases/meta-info/snapshots/base64binary-no-qualifiers/Ext/ClientApplicationInterface.xml @@ -0,0 +1,18 @@ + + + + + UUID-002 + + + + + UUID-004 + + + + + + + + \ No newline at end of file diff --git a/tests/skills/cases/meta-info/snapshots/base64binary-no-qualifiers/Languages/Русский.xml b/tests/skills/cases/meta-info/snapshots/base64binary-no-qualifiers/Languages/Русский.xml new file mode 100644 index 000000000..37c60d786 --- /dev/null +++ b/tests/skills/cases/meta-info/snapshots/base64binary-no-qualifiers/Languages/Русский.xml @@ -0,0 +1,16 @@ + + + + + Русский + + + ru + Русский + + + + ru + + + \ No newline at end of file diff --git a/tests/skills/cases/meta-info/valuestorage-base64.json b/tests/skills/cases/meta-info/valuestorage-base64.json deleted file mode 100644 index d1ba34db5..000000000 --- a/tests/skills/cases/meta-info/valuestorage-base64.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "name": "xs:base64Binary без квалификаторов — ХранилищеЗначения, с ними — ДвоичныеДанные", - "setup": "fixture:valuestorage-base64", - "params": { "objectPath": "Catalogs/Вложения.xml" }, - "expect": { - "stdoutContains": ["ХранилищеЗначения", "ДвоичныеДанные"] - } -} diff --git a/tests/skills/check-uuid-invariant.mjs b/tests/skills/check-uuid-invariant.mjs index 3c235d660..2e980114b 100644 --- a/tests/skills/check-uuid-invariant.mjs +++ b/tests/skills/check-uuid-invariant.mjs @@ -125,6 +125,48 @@ for (const runtime of runtimes) { if (!predefAfter.has(id)) fail(`id предопределённого элемента ${id} пропал после добавления новых (перегенерирован?)`); } console.log(`[${runtime}] проверено ${checked} uuid объекта + ${predefBefore.size} id предопределённых (сохранены при add)`); + + // 4. Внешний источник данных: правки идут в ДВА файла (источник и таблицу), причём таблицу + // навык создаёт сам. Уникальный для этого вида риск — пересборка файла источника целиком: + // она дала бы источнику новый uuid и осиротила бы уже существующие таблицы. + const edsInp = join(work, 'eds.json'); + writeFileSync(edsInp, JSON.stringify({ + type: 'ExternalDataSource', name: 'PG', + tables: { products: { keyFields: ['id'], fields: ['id: Number(10,0)', 'name: String(150)'] } }, + functions: { total: { expression: 'public.f_total(&1)', returns: 'Number(15,2)' } }, + }), 'utf8'); + skill(runtime, 'meta-compile', ['-JsonPath', edsInp, '-OutputDir', work], work); + const srcXml = join(work, 'ExternalDataSources', 'PG.xml'); + const tblXml = join(work, 'ExternalDataSources', 'PG', 'Tables', 'products.xml'); + const edsBefore = new Set([...collectUuids(srcXml), ...collectUuids(tblXml)]); + + const edsEdit1 = join(work, 'eds-e1.json'); + writeFileSync(edsEdit1, JSON.stringify({ + add: { + tables: { sales: { keyFields: ['id'], fields: ['id: Number(10,0)'] } }, + functions: { nextKey: 'NEXT VALUE FOR public.seq_key' }, + }, + }), 'utf8'); + skill(runtime, 'meta-edit', ['-ObjectPath', srcXml, '-DefinitionFile', edsEdit1, '-NoValidate'], work); + + const edsEdit2 = join(work, 'eds-e2.json'); + writeFileSync(edsEdit2, JSON.stringify({ + add: { fields: ['barcode: String(20) | nullable'] }, + modify: { fields: { name: { name: 'title', type: 'String(200)' } } }, + }), 'utf8'); + skill(runtime, 'meta-edit', ['-ObjectPath', tblXml, '-DefinitionFile', edsEdit2, '-NoValidate'], work); + + const edsAfter = new Set([...collectUuids(srcXml), ...collectUuids(tblXml)]); + // Правка обязана быть применённой: без этого проверка uuid ничего не значит. + if (!readFileSync(tblXml, 'utf8').includes('title')) { + fail('поле не переименовано — сценарий внешнего источника проверил бы uuid вхолостую'); + } + let edsChecked = 0; + for (const uuid of edsBefore) { + edsChecked++; + if (!edsAfter.has(uuid)) fail(`uuid ${uuid} внешнего источника пропал после правки (пересборка файла?)`); + } + console.log(`[${runtime}] проверено ${edsChecked} uuid внешнего источника и его таблицы`); } catch (e) { const detail = (e.stderr || e.message || '').toString(); console.log(`[${runtime}] ОШИБКА прогона: ${detail.slice(0, 300)}`);