@appaflytech/zeyrek-ai-mcp 0.0.37 → 0.0.41
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/knowledge/componentCatalog.js +457 -156
- package/dist/knowledge/componentEvents.js +49 -8
- package/dist/knowledge/connectDataBind.js +10 -2
- package/dist/knowledge/expressionPrelude.js +1 -0
- package/dist/knowledge/mobileComponentCatalog.js +21 -9
- package/dist/knowledge/mobileComponentEvents.js +21 -1
- package/dist/knowledge/mobileStore.js +75 -0
- package/dist/knowledge/normalize.js +52 -17
- package/dist/knowledge/platform/mobile.js +1 -1
- package/dist/knowledge/platform/mobileRules.js +1 -1
- package/dist/knowledge/platform/web.js +1 -1
- package/dist/knowledge/rules.js +244 -25
- package/dist/knowledge/styleResolve.js +27 -8
- package/dist/knowledge/styleResolveNative.js +4 -2
- package/dist/knowledge/themeResolve.js +1 -1
- package/dist/knowledge/themeSchema.js +2 -1
- package/dist/observability/trace.js +7 -2
- package/dist/platform/capture/actions.js +24 -1
- package/dist/platform/capture/summary.js +4 -1
- package/dist/platform/secrets.js +50 -2
- package/dist/resources/examples/mobile-app-shell-drawer.json +1 -0
- package/dist/resources/examples/mobile-list-detail-form.json +3 -0
- package/dist/resources/guides/component.md +1 -1
- package/dist/resources/guides/entity.md +16 -0
- package/dist/resources/guides/expressions.md +7 -6
- package/dist/resources/guides/merge.md +5 -4
- package/dist/resources/guides/microservice.md +2 -2
- package/dist/resources/guides/middleware.md +3 -1
- package/dist/resources/guides/mobile-publish.md +24 -0
- package/dist/resources/guides/mobile.md +50 -25
- package/dist/resources/guides/page.md +61 -23
- package/dist/resources/guides/theme.md +1 -0
- package/dist/resources/guides/translation.md +41 -1
- package/dist/resources/guides/workflow.md +31 -1
- package/dist/resources/schema-fingerprint.json +3 -3
- package/dist/resources/schemas/GlobalFunctionUpdateResultDto.json +2 -1
- package/dist/resources/schemas/OrganizationProjectDto.json +8 -0
- package/dist/resources/schemas/UpsertMobileStoreSettingsCommand.json +59 -0
- package/dist/tools/applications.js +6 -3
- package/dist/tools/common.js +9 -5
- package/dist/tools/entities.js +41 -4
- package/dist/tools/environments.js +1 -1
- package/dist/tools/flows.js +5 -1
- package/dist/tools/layouts.js +22 -7
- package/dist/tools/mobile.js +15 -1
- package/dist/tools/mobileStore.js +21 -5
- package/dist/tools/pages.js +31 -45
- package/dist/tools/queries.js +1 -1
- package/dist/tools/rows.js +14 -3
- package/dist/tools/screenWrite.js +2 -2
- package/dist/tools/screens.js +39 -0
- package/dist/tools/translations.js +148 -6
- package/package.json +1 -1
|
@@ -135,9 +135,9 @@ alanlar denetlenmez); katalog okunamazsa denetim susar ve yazma eskisi gibi geç
|
|
|
135
135
|
**Düğümü silip yeniden kurmayın:** o yol ona yazan akış setter'larını sessizce öldürür
|
|
136
136
|
(`FLOW-018`, aşağıda Kural 2).
|
|
137
137
|
- **Aynı ızgara hücrelerini iki komponente veremezsin.** Studio'da komponent tuvale bırakıldığında
|
|
138
|
-
çakışan kardeşleri aşağı itme işini
|
|
139
|
-
(`
|
|
140
|
-
/ `page_update` o
|
|
138
|
+
çakışan kardeşleri aşağı itme işini Studio'nun ızgara motoru yapar
|
|
139
|
+
(`@ng-apps/screen-builder` `moveItem`); komple şema yazan `page_create`
|
|
140
|
+
/ `page_update` o motordan geçmez, yani verdiğin koordinatlar olduğu gibi kayda girer ve tuvalde
|
|
141
141
|
komponentler üst üste biner. **Gateway aynı algoritmayı şemanın tamamına uyguluyor** (kök dizi +
|
|
142
142
|
her `children`, yalnız desktop `blocks[]` girdisi; `x`/`w`/`h` ve dizi sırası korunur, çakışanın
|
|
143
143
|
`position.y`'si aşağı kayar) ve yanıtta `gatewayNormalized: "çakışan komponentler ızgarada aşağı
|
|
@@ -174,10 +174,9 @@ alanlar denetlenmez); katalog okunamazsa denetim susar ve yazma eskisi gibi geç
|
|
|
174
174
|
## Kural 1b — `page_build` YOK
|
|
175
175
|
|
|
176
176
|
Entity'den otomatik sayfa üreten `page_build` tool'u **kaldırıldı**. Platformun
|
|
177
|
-
`POST /pages/{app}/build/{page}` ucu
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
platformda zaten var → doğru yol yukarıdaki micro-op hattıdır.
|
|
177
|
+
`POST /pages/{app}/build/{page}` ucu ve onun delege ettiği sayfa üretme servisi de 2026-09-18'de
|
|
178
|
+
kaldırıldı; otomatik sayfa üretimi yok. Sayfa/komponent şemasının tam karşılığı platformda zaten
|
|
179
|
+
var → doğru yol yukarıdaki micro-op hattıdır.
|
|
181
180
|
|
|
182
181
|
## Kural 1c — Komponente VERİ bağlama: `mount` olayı + flow (setter adımı)
|
|
183
182
|
|
|
@@ -283,7 +282,8 @@ dalı bulamaz ve akışı **sessizce durdurur** (`flow-engine.ts:79`); Studio da
|
|
|
283
282
|
İstisnalar: `condition` → dal adı rule id'sidir (ve o id bir **GUID**'dir — aşağıdaki tuzak 6),
|
|
284
283
|
`confirm` → `yes|no|always`, `microservice` →
|
|
285
284
|
`always` (eski akışlarda `success` de çalışır) **+ `error`** — bağlarsan hata anında `always`
|
|
286
|
-
yerine ondan sürer (2026-09-26, ADR 0004; ayrıntı aşağıdaki §"4xx de hatadır")
|
|
285
|
+
yerine ondan sürer (2026-09-26, ADR 0004; ayrıntı aşağıdaki §"4xx de hatadır"), `validation` →
|
|
286
|
+
`success` (hepsi geçti) | `error` (biri düştü), ardından her durumda `always` (kuralları aşağıda).
|
|
287
287
|
|
|
288
288
|
**2. `iteratees[].type` yalnız `change` | `fill`.** `setter.ts` başka değerde hiçbir dala
|
|
289
289
|
girmez — adım çalışır, hiçbir şey yapmaz. `change` tek prop'a değer yazar, `fill` diziyi
|
|
@@ -446,13 +446,15 @@ göstermek error flow'un işidir.
|
|
|
446
446
|
> | `Steps.<adımAdı>.error.body` | Ucun hata gövdesi — eski `Steps.<adımAdı>.data`'nın taşındığı yer | `microservice.ts` `.catch` |
|
|
447
447
|
> | `Steps.<adımAdı>.data` | **HER ZAMAN `null`**'dır — başarı gövdesi değil, hiçbir gövde değil (eskiden hata gövdesiydi) | `microservice.ts` `.catch` |
|
|
448
448
|
>
|
|
449
|
-
> **`alert` adımının `message`'ı
|
|
450
|
-
>
|
|
451
|
-
>
|
|
452
|
-
> "
|
|
453
|
-
> `
|
|
454
|
-
>
|
|
455
|
-
>
|
|
449
|
+
> **`alert` adımının `message`'ı ve `description`'ı METİN ŞABLONUDUR — yalnız `{{ }}` içi çözülür.**
|
|
450
|
+
> Bileşen prop bağlamasıyla aynı çözücü (`@ng-apps/flow` `resolveStepText` → `runPropertyCode`; web ve
|
|
451
|
+
> cihaz aynı): `{ "type": "alert", "args": { "message": "{{ Steps.error?.message ?? 'İşlem başarısız' }}",
|
|
452
|
+
> "alertType": "error" } }` ucun mesajını gösterir, `"{{ t('kayit.tamam') }}"` etkin dildeki çeviriyi.
|
|
453
|
+
> `{{ }}` DIŞINDA yazılan ifade çözülmez: `"message": "Steps.error.message"` kullanıcıya harfi harfine
|
|
454
|
+
> `Steps.error.message` gösterir (FLOW-022 uyarır). `{{ }}` içermeyen metin olduğu gibi kalır. 2026-10-08
|
|
455
|
+
> öncesi runtime'da (yeniden yayımlanmamış kurulum, eski mobil derleme) bu alanlar düz metindi; orada
|
|
456
|
+
> ucun mesajı bir `setter` (ya da `code`) adımıyla bir bileşene yazılır — mesajı ekranda kalıcı
|
|
457
|
+
> göstermek için bugün de bu yol geçerlidir:
|
|
456
458
|
>
|
|
457
459
|
> ```jsonc
|
|
458
460
|
> // errorSteps: start → bu setter. Metni bir notification/text komponentine yazar.
|
|
@@ -464,13 +466,14 @@ göstermek error flow'un işidir.
|
|
|
464
466
|
> "branches": [{ "id": "<GUID>", "name": "always", "nextStepId": null }] }
|
|
465
467
|
> ```
|
|
466
468
|
|
|
467
|
-
> **`confirm` adımının diyalog çerçevesi de yazılabilir.** `args` dört
|
|
469
|
+
> **`confirm` adımının diyalog çerçevesi de yazılabilir.** `args` dört metin alanı taşır:
|
|
468
470
|
> `message` (gövde), `title` (başlık), `confirmText` (onay düğmesi, `yes` dalı), `cancelText`
|
|
469
471
|
> (vazgeç düğmesi, `no` dalı). Son üçü isteğe bağlıdır; boş bırakılan alan dile göre sabit metne
|
|
470
472
|
> düşer ("Emin misiniz?" / "Evet" / "Hayır" — `dialog.functions.ts` `resolveDialogFrame`).
|
|
471
|
-
> Dördü de `alert`'in `message`'ı gibi
|
|
472
|
-
>
|
|
473
|
-
> "
|
|
473
|
+
> Dördü de `alert`'in `message`'ı gibi METİN ŞABLONUDUR (FLOW-022 hepsini denetler); çok dilli
|
|
474
|
+
> uygulamada çeviri anahtarıyla yazılır:
|
|
475
|
+
> `{ "type": "confirm", "args": { "message": "{{ t('risk.sil.onay') }}", "title": "{{ Row.name }} silinsin mi?",
|
|
476
|
+
> "confirmText": "{{ t('sil') }}", "cancelText": "Vazgeç" } }`.
|
|
474
477
|
|
|
475
478
|
> **Hangi yolu seçeceksin — adımın kendi `error` dalı mı, `errorSteps` mi, ana akışta
|
|
476
479
|
> `condition` mı?** Bir hatada sırayla denenir (yukarıdaki 4xx bölümü) — üçü birden TANIMLI
|
|
@@ -529,6 +532,30 @@ Tarif: `platform_guide('middleware')`.
|
|
|
529
532
|
- Adım hedef akışı **bekler** (`await engine.run`), sonra `always` dalından devam eder.
|
|
530
533
|
- Gateway boş/yarım `args`'ı **FLOW-014** ile yakalar.
|
|
531
534
|
|
|
535
|
+
**`validation` adımının kuralları `args.validations` dizisindedir** (kök ADR 0014). Her kural
|
|
536
|
+
`{ id?, component, property, type, errorMessage, settings }`: `component` sayfadaki bileşenin
|
|
537
|
+
**elementId**'si (boş → bileşensiz kural; sayfada olmayan ad 404), `property` okunacak prop
|
|
538
|
+
(çoğunlukla `value`), `type` ∈ `required · email · regex · min-length · max-length · min-value ·
|
|
539
|
+
max-value`, `errorMessage` düz metin ya da `t:` anahtarı (düşen alanın `invalidText`'ine yazılır),
|
|
540
|
+
`settings` tek parametredir — runtime **ilk değeri** okur (`{ "value": 8 }`, `{ "pattern": "^\\d+$" }`;
|
|
541
|
+
parametresiz tipte `{}`). Kurallar dizi sırasıyla koşar; kuralsız adım yükte `[]` döner ve her
|
|
542
|
+
çalıştırmada `success`'e gider.
|
|
543
|
+
|
|
544
|
+
```jsonc
|
|
545
|
+
{ "stepId": "kontrol", "name": "kontrol", "type": "validation", "kind": "normal",
|
|
546
|
+
"args": { "validations": [
|
|
547
|
+
{ "component": "email", "property": "value", "type": "email", "errorMessage": "t:validation.email", "settings": {} },
|
|
548
|
+
{ "component": "sifre", "property": "value", "type": "min-length", "errorMessage": "En az 8 karakter", "settings": { "value": 8 } } ] },
|
|
549
|
+
"branches": [ { "id": "<GUID>", "name": "success", "nextStepId": "kaydet" },
|
|
550
|
+
{ "id": "<GUID>", "name": "error", "nextStepId": null } ] }
|
|
551
|
+
```
|
|
552
|
+
|
|
553
|
+
- `always` `success`/`error`'dan **sonra** da koşar — kaydetmeyi `always`'e bağlarsan düşen
|
|
554
|
+
formda da kaydeder.
|
|
555
|
+
- Bu şekil komponentin kendi `validations`'ından (`set_validations { type, message, value }`)
|
|
556
|
+
farklıdır; `message`/`value` burada sessizce düşer. Tanınmayan `type` runtime'da **geçer** sayılır
|
|
557
|
+
(platform kümeyi doğrulamaz). Gateway ikisini de **FLOW-041** ile yakalar.
|
|
558
|
+
|
|
532
559
|
> **`code` adımının gövdesi `args.value` alanındadır — düz JS metni olarak.** Runtime onu
|
|
533
560
|
> `(async () => { <value> })()` içinde koşturur (`code.ts:18`), yani `await` serbesttir ve dönüş
|
|
534
561
|
> değeri akışa taşınmaz; etkiyi `Page`/`state`/komponent yazarak üretirsin. **Alan boşsa adım
|
|
@@ -619,7 +646,7 @@ değer tipleri: `static · page · property · storage · variable · code · it
|
|
|
619
646
|
> **`platform_guide('functions')`**.
|
|
620
647
|
>
|
|
621
648
|
> **`share` adımı ("Share")** metin ve bağlantı paylaşır: `args: { title, message, path }`. `title`
|
|
622
|
-
> DÜZ METİNDİR (ifade yazılırsa FLOW-022 uyarır); `message` ve `path` `storage.value` gibi
|
|
649
|
+
> DÜZ METİNDİR — `{{ }}` şablonu da çözülmez (ifade ya da `{{ }}` yazılırsa FLOW-022 uyarır); `message` ve `path` `storage.value` gibi
|
|
623
650
|
> İFADEDİR — düz metin tırnak ister (`"'Şuna bak'"`, `"'https://…/urun/' + Page.queryParams.id"`).
|
|
624
651
|
> Bağlantının adı `url` DEĞİL `path`'tir: backend `Title`/`Message`/`Path` alanlarında saklar ve
|
|
625
652
|
> tanımadığı anahtarı 200 ile sessizce düşürür (FLOW-035 `url`/`link`/`text`'i ve tırnaksız adresi
|
|
@@ -879,6 +906,14 @@ Tire yerine `_` kullan.
|
|
|
879
906
|
> `component_schema_patch` yamalarında), `layout_create`, `layout_update(views)` ve `layout_patch`;
|
|
880
907
|
> `page_schema_validate` aynı kararı verir. Ad doldurulduktan sonra denetlenir: geçersiz bir
|
|
881
908
|
> elementId adsız gelirse o da reddedilir.
|
|
909
|
+
>
|
|
910
|
+
> **`name` hiç verilmezse** gateway onu `elementId` ile tamamlar ve `gatewayNormalized`'da `name`
|
|
911
|
+
> diye bildirir: adsız düğümü platform kabul eder ama sayfada bir akış ya da `{{ }}` kod prop'u
|
|
912
|
+
> varsa FlowEngine `runCode` "Cannot read properties of undefined (reading 'catch')" ile düşer, akış
|
|
913
|
+
> `running:1`'de takılır ve hata adsız düğümü işaret etmez. Tamamlama PAGE-009 denetiminden ÖNCE
|
|
914
|
+
> koşar, yani `elementId` bu kuralın geçerli saydığı bir ad değilse (`'Ana Baslik'`, `'1abc'`,
|
|
915
|
+
> `'Functions'`) tamamlanan ad da reddedilir. Yine de her `add_component` gövdesine
|
|
916
|
+
> `name = elementId` yaz; tamamlama bir güvenlik ağıdır.
|
|
882
917
|
|
|
883
918
|
## Kural 1d — Sayfa iskeleti: `page_container` ve boyutlandırma
|
|
884
919
|
|
|
@@ -930,13 +965,16 @@ kapsayıcının kardeşi olarak yaşar (qm `[SY] Profil bilgilerim`: `page_conta
|
|
|
930
965
|
> Bu tiplerde `size.h` gerçek yüksekliktir — göz kararı değil, içeriğe göre hesapla
|
|
931
966
|
> (bir satır ≈ 10px; Kural 1f).
|
|
932
967
|
|
|
933
|
-
## Kural 1d-2 — Sayfanın ve layout'un KENDİ olayları: `mount` · `destroy`
|
|
968
|
+
## Kural 1d-2 — Sayfanın ve layout'un KENDİ olayları: `mount` · `destroy` (mobil sayfada + `focus`)
|
|
934
969
|
|
|
935
970
|
Komponent olaylarından AYRI olarak sayfa ve layout kayıtları kendi olay listesini taşır
|
|
936
971
|
(`events: [{ name, value: <flow id> }]`) ve geçerli adlar SABİTTİR: **`mount`**
|
|
937
972
|
(yüklendiğinde) ve **`destroy`** (sayfadan/layouttan ayrılırken — runtime destroy akışlarını
|
|
938
|
-
navigasyondan ÖNCE koşar).
|
|
939
|
-
|
|
973
|
+
navigasyondan ÖNCE koşar). **Mobil uygulamanın SAYFASI** bir ad daha alır: **`focus`** — ekran
|
|
974
|
+
her göründüğünde koşar (ilk açılışta `mount`'tan sonra, sonra yığından geri dönüşte ve sekme
|
|
975
|
+
değişiminde; `platform_guide('mobile')`). Başka ad platformca kabul edilir ama bağ sessizce ölü
|
|
976
|
+
kalır — gateway **PAGE-011 / LAY-004** ile istek gitmeden reddeder; `focus` layout'ta ve web
|
|
977
|
+
sayfasında da reddedilir (web sayfayı her gezinmede baştan kurar, `mount` yeter).
|
|
940
978
|
|
|
941
979
|
- Bağlama yolu `set_event` DEĞİLDİR (o komponent hedefler): sayfada `page_create` /
|
|
942
980
|
`page_update`'in `events` parametresi, layout'ta `layout_create/update`'in `events`'i.
|
|
@@ -18,6 +18,7 @@ yazmalarının aksine burada "önce development'ta dene" diye bir ayrım YOKTUR.
|
|
|
18
18
|
| Alan | Ne yapar | Ürettiği değişken(ler) |
|
|
19
19
|
|---|---|---|
|
|
20
20
|
| `mode` | `'light'` (varsayılan) \| `'dark'` — yayınlanan uygulamanın modu | palet tabanını seçer |
|
|
21
|
+
| `direction` | `'ltr'` \| `'rtl'` \| `'auto'` — sayfanın okuma yönü; `auto` uygulama dilinden türetir (`ar` `he` `fa` `ur` `ps` `sd` `ug` `yi` `dv` → rtl). Ayar YOKSA `dir` hiç yazılmaz (sayfa üst öğenin yönünü miras alır): `ar` ile kurulmuş mevcut proje bu ayar açılmadan aynalanmaz. Sayfa oluşturucu tuvali yönü uygulamaz. Grid yerleşimi kendiliğinden aynalanır; bileşenlerin ok simgeleri ve fiziksel `left/right` CSS'i bileşen bazında düzeltilir (ADR 0006) | CSS değişkeni üretmez, kap `dir` özniteliğine yazılır |
|
|
21
22
|
| `palette` | AYDINLIK moddaki renk override'ları; `accent.onPrimary` isteğe bağlıdır — accent ÜSTÜNDEKİ metin, verilmezse kontrasttan türer (`#4787FA` gibi açık bir mavi siyah metin üretir, tasarımda beyazsa buraya yazın) | `--ng-accent-*` · `--ng-background-*` · `--ng-text-*` · `--ng-border-*` · `--ng-status-*` · `--ng-text-on-accent` |
|
|
22
23
|
| `paletteDark` | KARANLIK moddaki renk override'ları | aynı küme |
|
|
23
24
|
| `fontFamily` · `headingFontFamily` | Gövde ve başlık yazı tipi (Google Fonts adı) | `--ng-font` · `--ng-font-heading` |
|
|
@@ -55,7 +55,8 @@ diye bir alan **hiç görünmez**; blok tam olarak bunu görünür kılmak için
|
|
|
55
55
|
| Tool | Ne yapar |
|
|
56
56
|
|---|---|
|
|
57
57
|
| `translation_list` | Application'ın tüm `(language, key, value)` kayıtlarını düz liste döner |
|
|
58
|
-
| `translation_update` | `keys` (ekle/güncelle, `componentId` opsiyonel) ve/veya `deletedKeys` (sil) — ikisi de aynı çağrıda verilebilir, en az biri dolu
|
|
58
|
+
| `translation_update` | `keys` (ekle/güncelle, `componentId` opsiyonel) ve/veya `deletedKeys` (sil) — ikisi de aynı çağrıda verilebilir, en az biri dolu olmalı. `file` kipi: doldurulmuş Excel'i içe aktarır (aşağıda) |
|
|
59
|
+
| `translation_export` | Çevirmene verilecek Excel dosyasını yerel yola yazar (aşağıda) |
|
|
59
60
|
| `project_update` | `languages`/`defaultLanguage` — proje dillerini kurar/değiştirir |
|
|
60
61
|
|
|
61
62
|
```jsonc
|
|
@@ -194,6 +195,45 @@ Runtime `t:` ile başlayan bir mesajı anahtar sayar ve normal zincirden çözer
|
|
|
194
195
|
olduğu gibi gösterilir. Çözüm çalışma anında olduğu için şema varsayılanlarını değiştirmek
|
|
195
196
|
gerekmez — `schema.ts`'e dokunulmaz, katalog yeniden yüklenmez.
|
|
196
197
|
|
|
198
|
+
## Dosyayla toplu çeviri (Excel)
|
|
199
|
+
|
|
200
|
+
İnsan bir çevirmene yeni bir dil yaptıracaksa: sözlük Excel'e iner, çevirmen hedef sütunu doldurur,
|
|
201
|
+
dosya geri yüklenir. **Çeviriyi sen yapacaksan dosyaya gerek yok** — `translation_list` ile oku,
|
|
202
|
+
`translation_update { keys }` ile yaz.
|
|
203
|
+
|
|
204
|
+
```jsonc
|
|
205
|
+
// 1. dışa aktar — dosya YALNIZ outPath'e yazılır, Sürücü'ye yüklenmez
|
|
206
|
+
{ "applicationId": "<app id>", "targetLanguage": "es", "outPath": "C:/ceviri/crm-es.xlsx" } // translation_export
|
|
207
|
+
// → { keys: 120, targetMissing: 120, sourceLanguage: "en", note: "… TR-004 …" }
|
|
208
|
+
|
|
209
|
+
// 2. çevirmen C sütununu doldurur; 3. önce kuru koşu
|
|
210
|
+
{ "applicationId": "<app id>", "file": "C:/ceviri/crm-es.xlsx", "targetLanguage": "es", "dryRun": true } // translation_update
|
|
211
|
+
// → { created: 118, skippedEmpty: 2, unknownKeyCount: 0, targetLanguageInProject: false, note: "… ekleyecek …" }
|
|
212
|
+
|
|
213
|
+
// 4. gerçek içe aktarma
|
|
214
|
+
{ "applicationId": "<app id>", "file": "C:/ceviri/crm-es.xlsx", "targetLanguage": "es" } // translation_update
|
|
215
|
+
// → { created: 118, languageAdded: true, … }
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
Dosya: tek sayfa, `A` Key · `B` kaynak dil (yalnız referans, okunmaz) · `C` hedef dil; başlıklar
|
|
219
|
+
`Key` · `Default Language (EN)` · `Target Language (ES)`. İçe aktarma **başlık satırını okumaz**:
|
|
220
|
+
değerler `translation_update`'in zorunlu `targetLanguage`'ındaki dile yazılır, `C1`'deki `(ES)` yalnız
|
|
221
|
+
çevirmen içindir ve dili belirlemez. Gizli `_zeyrek`
|
|
222
|
+
sayfası dosyanın hangi application'dan çıktığını söyler; başka bir application'ın dosyası yine
|
|
223
|
+
yazılır ama yanıt `applicationMismatch: true` ve not taşır.
|
|
224
|
+
|
|
225
|
+
- **Dolu hücre mevcut değeri ezer**, boş/boşluk hücre atlanır, hiçbir şey silinmez.
|
|
226
|
+
- **Application'da olmayan key açılmaz** — `unknownKeys`'e düşer. Yeni key `keys` kipiyle açılır.
|
|
227
|
+
- Key eşleşmesi önce birebir, yoksa büyük/küçük harf duyarsız tek aday; aynı key dosyada iki satırda
|
|
228
|
+
ise 422 ve hiçbir şey yazılmaz.
|
|
229
|
+
- **Hedef dil projede yoksa içe aktarma onu projeye ekler — bütün ortamlara** (TR-004). Önce
|
|
230
|
+
`project_update` gerekmez; ama dil production'a da gider ve orada çeviriler deployment'la gelene
|
|
231
|
+
kadar runtime varsayılan dile düşer.
|
|
232
|
+
- Dosya platformda **saklanmaz**: ne dışa aktarma ne içe aktarma Sürücü'ye bir şey bırakır.
|
|
233
|
+
- Studio'nun çeviri panelindeki JSON içe aktarma bundan farklıdır: o yalnız boşları doldurur ve
|
|
234
|
+
projede olmayan dili atlar.
|
|
235
|
+
- Sınırlar: `.xlsx` (`.xls`, csv, json reddedilir), 10 MB, 10.000 satır.
|
|
236
|
+
|
|
197
237
|
## Bilinen sınırlar
|
|
198
238
|
|
|
199
239
|
- **Flow adımlarında Translation kaynağı YOK.** `value.type` sözlüğünde (`platform_guide('page')`
|
|
@@ -286,7 +286,10 @@ Global-error dalında `error` değişkeni tanımlıdır:
|
|
|
286
286
|
```
|
|
287
287
|
|
|
288
288
|
Fail dalında aynı zincir `<adım adı>.error.cause`'tadır (`<adım adı>.error` = `{ code, message, detail,
|
|
289
|
-
type, cause }`).
|
|
289
|
+
type, cause }`). Serbest `error` değişkenini YALNIZ global-error dalı kurar
|
|
290
|
+
(`ActivityContext.EnterGlobalErrorHandling`): bağlı `fail` dalının hedefi `error.message` okursa
|
|
291
|
+
`undefined` alır — orada `<adım adı>.error.message` / `.code` okuyun (WF-035 uyarır; hedef aynı zamanda
|
|
292
|
+
global-error dalının hedefi olsa da).
|
|
290
293
|
|
|
291
294
|
`error.step` hangi adımın patladığını söyler. `input`/`data` alanları ifade değerlerini ve adım
|
|
292
295
|
çıktısını taşır — gövdeye yalnız ihtiyaç duyduğun alt kümeyi yaz (gateway'in varsayılanı
|
|
@@ -575,6 +578,33 @@ Tam örnek, ölçülen çıktı şekli ve `code` çocuğu tuzağının çıkış
|
|
|
575
578
|
> `The JSON value could not be converted to System.Guid` ile reddetti. Geçerli önek harfleri
|
|
576
579
|
> yalnız `a`–`f`'tir.
|
|
577
580
|
|
|
581
|
+
## `file-upload` adımı: çıktı, dosya alanının tablo okuma biçimidir (K10, 2026-10-08)
|
|
582
|
+
|
|
583
|
+
Adım `rule.fileString`'i (base64, JS ifadesi) `rule.folderId` klasörüne `rule.fileName` (JS ifadesi;
|
|
584
|
+
boşsa GUID) adıyla yazar, dosyayı geri okur ve **tablo okumasının FILE alanında döndürdüğü nesneyi**
|
|
585
|
+
üretir (`FileUploadStep.OutputSchema`; iki yol aynı `FilePathMapper`'ı kullanır):
|
|
586
|
+
|
|
587
|
+
```json
|
|
588
|
+
{
|
|
589
|
+
"id": "0199c3a2-7b1e-7c4d-9a10-3f2b8c6d5e41",
|
|
590
|
+
"path": "uploads/photo.png",
|
|
591
|
+
"absolute_path": "https://acme.example.com/dev/uploads/photo.png",
|
|
592
|
+
"type": "ref",
|
|
593
|
+
"value": "0199c3a2-7b1e-7c4d-9a10-3f2b8c6d5e41",
|
|
594
|
+
"fileName": "photo.png"
|
|
595
|
+
}
|
|
596
|
+
```
|
|
597
|
+
|
|
598
|
+
- Sonraki adımlar **adım adı + `.data`** ile okur: adres `yukle.data.absolute_path`, kimlik
|
|
599
|
+
`yukle.data.id` (`value` aynı id'dir). `absolute_path` = uygulamanın dosya sunum ucu (org slug +
|
|
600
|
+
ortamın depolama öneki) + `/` + `path`; `path` boşsa `""`. Görsel için ikinci okuma gerekmez.
|
|
601
|
+
- **Aynı içerik klasörde varsa** yeni dosya yazılmaz: `id`, adres ve `fileName` var olan dosyanındır.
|
|
602
|
+
- **Mobil görsel:** `response` adımı nesneyi döndürür (`bodyExpression: "({ foto: yukle.data })"`),
|
|
603
|
+
sayfa akışı `Steps.<sayfa adımı>.data.foto`'yu `image.src`'ye verir — nesnenin kendisi ya da
|
|
604
|
+
`.absolute_path`; `.value` dosya kimliğidir, görsel çizilmez (`platform_guide('mobile')` §Dosya ve fotoğraf).
|
|
605
|
+
- 2026-10-08'den eski backend imajı yalnız `{ id }` döndürür: `yukle.data.absolute_path` `undefined`
|
|
606
|
+
çıkıyorsa imaj bayattır. Kaynak: backend `FileUploadStepTests`; canlı koşuyla henüz ölçülmedi.
|
|
607
|
+
|
|
578
608
|
## Table step kuralları
|
|
579
609
|
|
|
580
610
|
- `rule` içinde `datasourceId` + `entityId` zorunlu; `selectFields`/`filters` öğelerinde
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
|
-
"hash": "
|
|
3
|
-
"generatedAt": "2026-10-
|
|
4
|
-
"types":
|
|
2
|
+
"hash": "1766032527c4215282c73fb003cbe3526babafabc8588b80289dc28fd4e8b393",
|
|
3
|
+
"generatedAt": "2026-10-12T12:46:03.355Z",
|
|
4
|
+
"types": 337,
|
|
5
5
|
"files": 167,
|
|
6
6
|
"note": "Şemaları üreten backend kaynaklarının parmak izi. Elle düzenlemeyin: 'npm run generate-schemas' yazar, 'npm run schema:check' denetler.",
|
|
7
7
|
"missing": [
|
|
@@ -117,6 +117,9 @@
|
|
|
117
117
|
"review": {
|
|
118
118
|
"$ref": "#/components/schemas/NgApps.Domain.Contexts.Organization.MobileStore.MobileStoreReviewModel"
|
|
119
119
|
},
|
|
120
|
+
"ads": {
|
|
121
|
+
"$ref": "#/components/schemas/NgApps.Domain.Contexts.Organization.MobileStore.MobileStoreAdsModel"
|
|
122
|
+
},
|
|
120
123
|
"expectedUpdatedAt": {
|
|
121
124
|
"type": "string",
|
|
122
125
|
"format": "date-time",
|
|
@@ -255,6 +258,40 @@
|
|
|
255
258
|
},
|
|
256
259
|
"additionalProperties": false
|
|
257
260
|
},
|
|
261
|
+
"NgApps.Domain.Contexts.Organization.MobileStore.MobileStoreAdsModel": {
|
|
262
|
+
"type": "object",
|
|
263
|
+
"properties": {
|
|
264
|
+
"enabled": {
|
|
265
|
+
"type": "boolean"
|
|
266
|
+
},
|
|
267
|
+
"iosAppId": {
|
|
268
|
+
"type": "string",
|
|
269
|
+
"nullable": true
|
|
270
|
+
},
|
|
271
|
+
"androidAppId": {
|
|
272
|
+
"type": "string",
|
|
273
|
+
"nullable": true
|
|
274
|
+
},
|
|
275
|
+
"testMode": {
|
|
276
|
+
"type": "boolean"
|
|
277
|
+
},
|
|
278
|
+
"consentForm": {
|
|
279
|
+
"type": "boolean"
|
|
280
|
+
},
|
|
281
|
+
"appOpenUnit": {
|
|
282
|
+
"type": "string",
|
|
283
|
+
"nullable": true
|
|
284
|
+
},
|
|
285
|
+
"units": {
|
|
286
|
+
"type": "array",
|
|
287
|
+
"items": {
|
|
288
|
+
"$ref": "#/components/schemas/NgApps.Domain.Contexts.Organization.MobileStore.MobileStoreAdUnitModel"
|
|
289
|
+
},
|
|
290
|
+
"nullable": true
|
|
291
|
+
}
|
|
292
|
+
},
|
|
293
|
+
"additionalProperties": false
|
|
294
|
+
},
|
|
258
295
|
"NgApps.Domain.Contexts.Organization.MobileStore.MobileStoreAppleCategoriesModel": {
|
|
259
296
|
"type": "object",
|
|
260
297
|
"properties": {
|
|
@@ -268,6 +305,28 @@
|
|
|
268
305
|
}
|
|
269
306
|
},
|
|
270
307
|
"additionalProperties": false
|
|
308
|
+
},
|
|
309
|
+
"NgApps.Domain.Contexts.Organization.MobileStore.MobileStoreAdUnitModel": {
|
|
310
|
+
"type": "object",
|
|
311
|
+
"properties": {
|
|
312
|
+
"name": {
|
|
313
|
+
"type": "string",
|
|
314
|
+
"nullable": true
|
|
315
|
+
},
|
|
316
|
+
"format": {
|
|
317
|
+
"type": "string",
|
|
318
|
+
"nullable": true
|
|
319
|
+
},
|
|
320
|
+
"ios": {
|
|
321
|
+
"type": "string",
|
|
322
|
+
"nullable": true
|
|
323
|
+
},
|
|
324
|
+
"android": {
|
|
325
|
+
"type": "string",
|
|
326
|
+
"nullable": true
|
|
327
|
+
}
|
|
328
|
+
},
|
|
329
|
+
"additionalProperties": false
|
|
271
330
|
}
|
|
272
331
|
}
|
|
273
332
|
}
|
|
@@ -3,11 +3,14 @@ import { handle } from '../platform/responses.js';
|
|
|
3
3
|
import { forgetApplication, readApplicationPlatform, resolveApplication } from '../knowledge/platform/resolve.js';
|
|
4
4
|
import { mobileTabsProblems, readMobileTabs } from '../knowledge/platform/mobileTabs.js';
|
|
5
5
|
import { findingLine } from '../knowledge/platform/profile.js';
|
|
6
|
+
import { listComponents } from './components.js';
|
|
6
7
|
import { z } from 'zod';
|
|
7
8
|
/**
|
|
8
9
|
* MOB-007 (sekme ayarı) — `properties.mobile.tabs` yazan mobil uygulama çağrısının denetimi. Yazım
|
|
9
10
|
* platforma YİNE gider (D3; backend `properties`'i doğrulamaz): bulgu `platformWarnings`'e düşer.
|
|
10
|
-
* Sayfa ve widget listesi okunamazsa o denetimler atlanır, biçim denetimi yine koşar.
|
|
11
|
+
* Sayfa ve widget listesi okunamazsa o denetimler atlanır, biçim denetimi yine koşar. Widget listesi
|
|
12
|
+
* `listComponents`'tan gelir: uç `type` verilmezse yalnız web bileşenlerini döner ve mobil widget'ı
|
|
13
|
+
* gösteren sekme ayarı yanlış MOB-007 alırdı (ARC-245).
|
|
11
14
|
*/
|
|
12
15
|
export async function mobileTabsWarnings(platform, applicationId, projectSlug, homepageId, properties) {
|
|
13
16
|
const tabs = readMobileTabs(properties);
|
|
@@ -19,8 +22,8 @@ export async function mobileTabsWarnings(platform, applicationId, projectSlug, h
|
|
|
19
22
|
.catch(() => undefined)
|
|
20
23
|
: undefined;
|
|
21
24
|
const widgetIds = applicationId && projectSlug
|
|
22
|
-
? await platform
|
|
23
|
-
.then(
|
|
25
|
+
? await listComponents(platform, projectSlug, undefined, applicationId)
|
|
26
|
+
.then(components => new Set(components.filter(entry => entry.kind === 'widget').map(entry => String(entry.id ?? ''))))
|
|
24
27
|
.catch(() => undefined)
|
|
25
28
|
: undefined;
|
|
26
29
|
return mobileTabsProblems(tabs, {
|
package/dist/tools/common.js
CHANGED
|
@@ -61,12 +61,12 @@ export const pagingParams = {
|
|
|
61
61
|
isDescending: z.boolean().optional()
|
|
62
62
|
};
|
|
63
63
|
/**
|
|
64
|
-
* Dal adı kuralı: `Environment` ctor'unun
|
|
65
|
-
* `environment_create`'i değil, `page_screenshot`'ın
|
|
66
|
-
* için tek yerde durur — iki kopya birbirinden
|
|
67
|
-
* görüntüleyemez hâle gelir.
|
|
64
|
+
* Dal adı kuralı: `Environment` ctor'unun birebir aynası (`/` ve `-` serbest, 3-31 karakter),
|
|
65
|
+
* gerçek koruma backend'dedir. Ad yalnız `environment_create`'i değil, `page_screenshot`'ın
|
|
66
|
+
* `/branch/{slug}` şeridini de ilgilendirdiği için tek yerde durur — iki kopya birbirinden
|
|
67
|
+
* ayrılırsa gateway kabul ettiği bir adı görüntüleyemez hâle gelir.
|
|
68
68
|
*/
|
|
69
|
-
export const branchNamePattern = /^[a-z][a-z0-9_]
|
|
69
|
+
export const branchNamePattern = /^(?=.{3,31}$)[a-z][a-z0-9_-]*(\/[a-z0-9][a-z0-9_-]*)*$/;
|
|
70
70
|
const reservedEnvironmentNames = new Set(['development', 'production', 'public', 'information_schema', 'dev', 'view', 'api', 'files', 'hubs']);
|
|
71
71
|
/** Sabit ortamlar, url sözcükleri ve postgres şemaları: dal adı olamazlar */
|
|
72
72
|
export function isReservedEnvironmentName(name) {
|
|
@@ -76,6 +76,10 @@ export function isReservedEnvironmentName(name) {
|
|
|
76
76
|
export function isBranchName(name) {
|
|
77
77
|
return branchNamePattern.test(name) && !isReservedEnvironmentName(name);
|
|
78
78
|
}
|
|
79
|
+
/** Dalın adres parçası: her `/` → `-` (backend `Environment.Slug`); `feature/x` → `feature-x` */
|
|
80
|
+
export function branchSlug(name) {
|
|
81
|
+
return name.replace(/\//g, '-');
|
|
82
|
+
}
|
|
79
83
|
export const readOnly = { readOnlyHint: true, openWorldHint: false };
|
|
80
84
|
export const writes = { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: false };
|
|
81
85
|
export const updates = { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: false };
|
package/dist/tools/entities.js
CHANGED
|
@@ -26,6 +26,40 @@ function preserveApplicationBinding(definition) {
|
|
|
26
26
|
if (applicationIds.length > 0)
|
|
27
27
|
definition.applicationIds = applicationIds;
|
|
28
28
|
}
|
|
29
|
+
/**
|
|
30
|
+
* 37 S-1: platform PATCH op'larını `EntityDto` OKUMA şekline uygular (`PatchEntityCommandHandler` →
|
|
31
|
+
* `UpdateEntityCommand.FromDto`, `ApplicationIds = Applications.Select(a => a.ApplicationId)`). Bağ
|
|
32
|
+
* `/applications` yolunda `{ applicationId }` öğeleriyle yazılır; `/applicationIds` belgede yoktur
|
|
33
|
+
* (gateway önizlemesi `OPERATION_PATH_UNRESOLVABLE` ile düşer), çıplak guid 500 `JsonException`,
|
|
34
|
+
* `{ id }` boş `applicationId` ile "This record is in use" verir. Ajan yazma adını bilerek geldiği
|
|
35
|
+
* için reddetmek yerine çevrilir ve `gatewayNormalized` ile söylenir (microservice `/roleIds` emsali).
|
|
36
|
+
*/
|
|
37
|
+
function normalizeApplicationBindingOps(operations) {
|
|
38
|
+
const notes = [];
|
|
39
|
+
const asBinding = (entry) => {
|
|
40
|
+
if (typeof entry === 'string')
|
|
41
|
+
return { applicationId: entry };
|
|
42
|
+
if (entry && typeof entry === 'object' && !('applicationId' in entry) && typeof entry.id === 'string')
|
|
43
|
+
return { applicationId: entry.id };
|
|
44
|
+
return entry;
|
|
45
|
+
};
|
|
46
|
+
for (const operation of operations) {
|
|
47
|
+
const path = typeof operation.path === 'string' ? operation.path : '';
|
|
48
|
+
if (path === '/applicationIds' || path.startsWith('/applicationIds/')) {
|
|
49
|
+
operation.path = `/applications${path.slice('/applicationIds'.length)}`;
|
|
50
|
+
notes.push(`patch yolu '${path}' → '${operation.path}': yamalanan belge entity_get'in OKUMA şeklidir (EntityDto) ve uygulama bağı orada 'applications' [{ applicationId }] alanıdır; 'applicationIds' yalnız create/update GÖVDESİNDE vardır.`);
|
|
51
|
+
}
|
|
52
|
+
const target = operation.path;
|
|
53
|
+
if (target !== '/applications' && !target.startsWith('/applications/'))
|
|
54
|
+
continue;
|
|
55
|
+
const before = JSON.stringify(operation.value);
|
|
56
|
+
operation.value = target === '/applications' && Array.isArray(operation.value) ? operation.value.map(asBinding) : target.split('/').length === 3 ? asBinding(operation.value) : operation.value;
|
|
57
|
+
if (JSON.stringify(operation.value) !== before) {
|
|
58
|
+
notes.push(`'${target}' değeri { applicationId } öğesine çevrildi — çalışan şekil: { op: 'replace', path: '/applications', value: [{ applicationId: '<application id>' }] }.`);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return notes;
|
|
62
|
+
}
|
|
29
63
|
/**
|
|
30
64
|
* `UpdateEntityCommandHandler` gövdedeki `Indexes` null ise onu BOŞ LİSTEYE çeviriyor
|
|
31
65
|
* (`request.Indexes?…ToList() ?? []`) ve `Entity.FindProcessedIndexes` boş listede entity'nin
|
|
@@ -178,7 +212,7 @@ export function registerEntityTools(server, platform) {
|
|
|
178
212
|
}));
|
|
179
213
|
server.registerTool('entity_patch', {
|
|
180
214
|
title: 'Entity\'ye patch uygula',
|
|
181
|
-
description: "YENİ alanı korumayla ya da türetilmiş alan olarak eklemek için de kullanılır (PROT-003/PROT-004); eklenen ya da kuralı değişen türetilmiş alanı dolu satırlarda derived-fill işi doldurur (tablo kilitlenmez, bitiş import_history { entityId }). VAR OLAN alanın korumasını değiştirmez — platform 400 ProtectionChangeNotAllowed döner, gateway istek göndermeden durdurur ve yol entity_protection_update'tir. Tam gövdeyi bağlama almadan hedefli değişiklik: gateway mevcut tanımı okur, yolları çözer ve op'ları platformun PATCH /entities/{id} ucuna `{ baseVersion, ops }` olarak geçirir — tam gövde PUT'u ATILMAZ, yalnız fark yazılır. Yanıt outline özetidir. YOLLARDA SEÇİCİ KULLANIN, indeks değil: '/fields[slug=email]/nullable' — indeksle yazmak alan sırası değişince SESSİZCE yanlış alanı yazar. Gateway seçiciyi indekse çevirir ve hangi kayda indiğini yanıttaki patchTargets ile söyler. `baseVersion` okunan kaydın `version` alanından gelir; kayıt bu arada değiştiyse platform 409 döner ve HİÇBİR op uygulanmaz. 'applications' → 'applicationIds' çevirisini
|
|
215
|
+
description: "YENİ alanı korumayla ya da türetilmiş alan olarak eklemek için de kullanılır (PROT-003/PROT-004); eklenen ya da kuralı değişen türetilmiş alanı dolu satırlarda derived-fill işi doldurur (tablo kilitlenmez, bitiş import_history { entityId }). VAR OLAN alanın korumasını değiştirmez — platform 400 ProtectionChangeNotAllowed döner, gateway istek göndermeden durdurur ve yol entity_protection_update'tir. Tam gövdeyi bağlama almadan hedefli değişiklik: gateway mevcut tanımı okur, yolları çözer ve op'ları platformun PATCH /entities/{id} ucuna `{ baseVersion, ops }` olarak geçirir — tam gövde PUT'u ATILMAZ, yalnız fark yazılır. Yanıt outline özetidir. YOLLARDA SEÇİCİ KULLANIN, indeks değil: '/fields[slug=email]/nullable' — indeksle yazmak alan sırası değişince SESSİZCE yanlış alanı yazar. Gateway seçiciyi indekse çevirir ve hangi kayda indiğini yanıttaki patchTargets ile söyler. `baseVersion` okunan kaydın `version` alanından gelir; kayıt bu arada değiştiyse platform 409 döner ve HİÇBİR op uygulanmaz. UYGULAMA BAĞI '/applications' yolundadır ve öğeleri { applicationId } nesneleridir: {op:'replace', path:'/applications', value:[{applicationId:'<application id>'}]} — tam listeyi yazar; tek bağ eklemek {op:'add', path:'/applications/-', value:{applicationId:'<application id>'}}. '/applicationIds' yolunu ya da çıplak guid / { id } öğesini gateway bu şekle çevirir ve gatewayNormalized ile söyler; 'applications' → 'applicationIds' gövde çevirisini platform yapar. `fields[].isLocalized: true` alanı ana tablodan çıkarıp companion `<slug>_localization` entity'sine taşır (yalnız düz METİN alanı; anahtar/unique/ilişki olamaz) — platform_guide('entity') §'Çoklu dilli alan'.",
|
|
182
216
|
inputSchema: { project: projectParam, environment: environmentParam, datasourceId: guid.optional().describe('Verilmezse gateway datasource ağacından bulur'), id: entityRef, operations: patchOperationsParam.optional(), ops: patchOperationsParam.optional().describe("'operations' ile aynı alan (eşanlamlı) — sayfa micro-op tool'larıyla aynı adı kullanabilmeniz için"), policyOverride: policyOverrideParam },
|
|
183
217
|
annotations: updates
|
|
184
218
|
}, handle(config, async ({ project, environment, datasourceId, id, operations: givenOperations, ops, policyOverride }) => {
|
|
@@ -187,7 +221,10 @@ export function registerEntityTools(server, platform) {
|
|
|
187
221
|
const entity = datasourceId && isGuid(id) ? { entityId: id, datasourceId } : await resolver.entity(id);
|
|
188
222
|
const current = await platform.tenant('GET', project, `/entities/${entity.datasourceId}/${entity.entityId}`, { environment });
|
|
189
223
|
const definition = structuredClone((current?.data ?? current));
|
|
190
|
-
|
|
224
|
+
// Bağ yolu seçici çözümünden ÖNCE çevrilir: '/applicationIds' belgede yoktur (37 S-1).
|
|
225
|
+
const bindingOperations = structuredClone(operations);
|
|
226
|
+
const bindingNotes = normalizeApplicationBindingOps(bindingOperations);
|
|
227
|
+
const resolvedOps = resolvePatchOperations(definition, bindingOperations, 'entity_get');
|
|
191
228
|
// Politika, yazmadan önce sonucu görmeli; yerel kopya YALNIZ değerlendirme için, gövde olarak gönderilmez.
|
|
192
229
|
const preview = fastJsonPatch.applyPatch(definition, resolvedOps.operations, true, false).newDocument;
|
|
193
230
|
const protectionChanges = changedExistingProtections(current?.data ?? current, preview);
|
|
@@ -208,7 +245,7 @@ export function registerEntityTools(server, platform) {
|
|
|
208
245
|
});
|
|
209
246
|
const written = patchResult(saved, preview);
|
|
210
247
|
const touched = patchedSections(written.data, resolvedOps.operations);
|
|
211
|
-
return attachFindings(withUnmappedMembers(withResolved({
|
|
248
|
+
return attachFindings(withUnmappedMembers(withResolved(withNormalization({
|
|
212
249
|
applied: operations.length,
|
|
213
250
|
newVersion: written.newVersion,
|
|
214
251
|
patchTargets: resolvedOps.targets,
|
|
@@ -216,7 +253,7 @@ export function registerEntityTools(server, platform) {
|
|
|
216
253
|
// S-8: yazılanın kanıtı yanıtta dursun; dokunulmayan bölümler outline'da kalır.
|
|
217
254
|
...(Object.keys(touched).length > 0 ? { touched } : {}),
|
|
218
255
|
outline: entityOutline(written.data)
|
|
219
|
-
}, resolver.resolved), drift), findings);
|
|
256
|
+
}, bindingNotes), resolver.resolved), drift), findings);
|
|
220
257
|
}));
|
|
221
258
|
server.registerTool('entity_protection_update', {
|
|
222
259
|
title: 'Alan korumasını değiştir',
|
|
@@ -4,7 +4,7 @@ import { handle } from '../platform/responses.js';
|
|
|
4
4
|
import { z } from 'zod';
|
|
5
5
|
/** Aggregate kuralının aynası (`Environment` ctor); gerçek koruma backend'dedir. Kural `common.ts`'te. */
|
|
6
6
|
const branchName = z.string().regex(branchNamePattern)
|
|
7
|
-
.describe("Dal adı: küçük harfle başlar
|
|
7
|
+
.describe("Dal adı: küçük harfle başlar; küçük harf/rakam/alt çizgi/tire ve '/' ile ayrılmış parçalar (ör. feature/odeme), 3-31 karakter. Bu ad PG şema adı olur ve değişmez; adres parçası (slug) '/' yerine '-' taşır.");
|
|
8
8
|
function assertBranchName(name) {
|
|
9
9
|
if (isReservedEnvironmentName(name)) {
|
|
10
10
|
throw new PlatformApiError(`environment_create: '${name}' rezerve bir şema adı — dal adı olarak kullanılamaz. Başka bir ad seçin.`, 400);
|
package/dist/tools/flows.js
CHANGED
|
@@ -515,10 +515,14 @@ export function registerFlowTools(server, platform) {
|
|
|
515
515
|
};
|
|
516
516
|
const authored = { steps: steps ?? [], errorSteps: errorSteps ?? [] };
|
|
517
517
|
const normalized = [...normalizeFlowSteps(authored.steps), ...normalizeFlowSteps(authored.errorSteps)];
|
|
518
|
+
// Kurallar yalnız GÖNDERİLEN adımlara bakar (kayıtlı gövdeyi suçlamamak için); stepId
|
|
519
|
+
// benzersizliği ise birleşik akışta ölçülür: yalnız steps verilip kayıtlı errorSteps'le
|
|
520
|
+
// çakışan kimlik de platformda 500 verir.
|
|
518
521
|
const findings = steps || errorSteps
|
|
519
522
|
? [
|
|
520
523
|
...enforcePolicy(evaluatePolicy(config, policyRules, 'flow', authored, { operation: 'update' }, policyOverride)),
|
|
521
|
-
...enforcePolicy(config.policyMode === 'off' ? [] : applyPolicyMode(config, connectedSetterFindings(authored.steps, authored.errorSteps, hostNodes(hostRecord)), policyOverride))
|
|
524
|
+
...enforcePolicy(config.policyMode === 'off' ? [] : applyPolicyMode(config, connectedSetterFindings(authored.steps, authored.errorSteps, hostNodes(hostRecord)), policyOverride)),
|
|
525
|
+
...enforcePolicy(evaluatePolicy(config, policyRules, 'flow', flow, { operation: 'update' }, policyOverride).filter(finding => finding.ruleId === 'FLOW-027'))
|
|
522
526
|
]
|
|
523
527
|
: [];
|
|
524
528
|
const parameterWarnings = [
|
package/dist/tools/layouts.js
CHANGED
|
@@ -3,7 +3,7 @@ import { resolvePatchOperations } from '../knowledge/outlines.js';
|
|
|
3
3
|
import { patchResult, baseVersionOf } from '../platform/patch.js';
|
|
4
4
|
import fastJsonPatch from 'fast-json-patch';
|
|
5
5
|
import { fillComponentNames, componentNameNote, normalizeComponentDefaults, componentDefaultsNote, flexSizeWarnings, spacingLayerWarnings, verticalGapWarnings, withNormalization } from '../knowledge/normalize.js';
|
|
6
|
-
import { fetchHost, layoutUpdateBody, schemaParam, eventsParam, modeParam, summarize, findNode } from './screens.js';
|
|
6
|
+
import { fetchHost, layoutUpdateBody, schemaParam, eventsParam, modeParam, summarize, findNode, readFullSchema } from './screens.js';
|
|
7
7
|
import { assertScreenWriteGuards, assertScreenCatalog } from './screenWrite.js';
|
|
8
8
|
import { applyPropTypesToSchema, isSetPropTypeOp, setPropTypeErrors } from '../knowledge/propTypes.js';
|
|
9
9
|
import { writeDiff, withWriteDiff } from '../platform/writeDiff.js';
|
|
@@ -58,24 +58,39 @@ export function registerLayoutTools(server, platform) {
|
|
|
58
58
|
}));
|
|
59
59
|
server.registerTool('layout_schema_get', {
|
|
60
60
|
title: "Layout komponent ağacını oku",
|
|
61
|
-
description: "Layout'un komponent ağacını (views) okur — page_schema_get'in layout karşılığı. Ağaçta hedefli değişiklik için layout_schema_patch (sayfa ile aynı micro-op sözlüğü, PATCH /layouts/{app}/{id}/schema). Ad/parametre/olay alanlarında ya da düz JSON Patch yolu gerektiğinde layout_patch — o araç buradaki updatedAt'i expectedUpdatedAt olarak kabul eder. Tam ağacı kendiniz yazacaksanız layout_update.",
|
|
61
|
+
description: "Layout'un komponent ağacını (views) okur — page_schema_get'in layout karşılığı. Ağaçta hedefli değişiklik için layout_schema_patch (sayfa ile aynı micro-op sözlüğü, PATCH /layouts/{app}/{id}/schema). Ad/parametre/olay alanlarında ya da düz JSON Patch yolu gerektiğinde layout_patch — o araç buradaki updatedAt'i expectedUpdatedAt olarak kabul eder. Tam ağacı kendiniz yazacaksanız layout_update. full ve subtree yanıtı nodeCount taşır; full tek yanıta sığmazsa HATA döner (sessiz kırpma yok): cursor:'0' ile parça parça okuyun — page_schema_get ile aynı sözleşme (items[] { parentElementId, index, node }, nextCursor bitene kadar; layout okuma arasında değişirse 409). allowTruncated:true kırpılmış gövdeyi açıkça verir — o gövdeyi layout_update'e geri yazmayın.",
|
|
62
62
|
inputSchema: {
|
|
63
63
|
project: projectParam,
|
|
64
64
|
environment: environmentParam,
|
|
65
65
|
applicationId: guid,
|
|
66
66
|
id: guid,
|
|
67
|
-
mode: modeParam,
|
|
68
|
-
componentId: z.string().optional().describe("subtree kipinde kök alınacak komponentin elementId'si")
|
|
67
|
+
mode: modeParam.describe("summary (varsayılan): yalnız id/elementId/name/type ağacı — hedef bulmak için bunu kullanın. subtree: componentId ile tek alt ağaç. full: tüm views; tek yanıta sığmazsa HATA döner — cursor:'0' ile parça parça okuyun (nextCursor bitene kadar) ya da allowTruncated:true ile kırpılmış gövdeyi açıkça isteyin."),
|
|
68
|
+
componentId: z.string().optional().describe("subtree kipinde kök alınacak komponentin elementId'si"),
|
|
69
|
+
cursor: z.string().min(1).optional().describe("Yalnız mode:'full': parçalı okuma. İlk çağrıda '0', sonra yanıttaki nextCursor; nextCursor gelmeyince views bitmiştir. Layout okuma arasında değişirse (updatedAt) 409 döner — cursor:'0' ile baştan başlayın."),
|
|
70
|
+
allowTruncated: z.boolean().optional().describe("Yalnız mode:'full': true verilirse sığmayan views hata yerine kırpılarak döner (nodeCount toplamı söyler, düşen düğümler söylenmez). Varsayılan false.")
|
|
69
71
|
},
|
|
70
72
|
annotations: readOnly
|
|
71
|
-
}, handle(config, async ({ project, environment, applicationId, id, mode, componentId }) => {
|
|
73
|
+
}, handle(config, async ({ project, environment, applicationId, id, mode, componentId, cursor, allowTruncated }) => {
|
|
72
74
|
const layout = await fetchHost(platform, 'layout', project, environment, applicationId, id);
|
|
73
75
|
const typeField = applicationTypeField(await resolveProfile(platform, applicationId));
|
|
74
76
|
const audit = layout.audit;
|
|
75
77
|
const updatedAt = audit?.updatedAt ?? audit?.createdAt;
|
|
76
78
|
const views = (layout.views ?? []);
|
|
77
|
-
if (mode
|
|
78
|
-
|
|
79
|
+
if (cursor !== undefined && mode !== 'full') {
|
|
80
|
+
throw new PlatformApiError(`cursor yalnız mode:'full' ile çalışır (verilen mode: '${mode ?? 'summary'}'); parçalı okuma tüm views içindir. mode:'full' ile cursor:'0' verin ya da cursor'ı kaldırın.`, 400);
|
|
81
|
+
}
|
|
82
|
+
if (mode === 'full') {
|
|
83
|
+
return readFullSchema({
|
|
84
|
+
noun: 'Layout',
|
|
85
|
+
field: 'views',
|
|
86
|
+
nodes: views,
|
|
87
|
+
versionKey: String(updatedAt ?? ''),
|
|
88
|
+
base: { updatedAt, ...typeField },
|
|
89
|
+
cursor,
|
|
90
|
+
allowTruncated,
|
|
91
|
+
maxResponseBytes: config.maxResponseBytes
|
|
92
|
+
});
|
|
93
|
+
}
|
|
79
94
|
if (mode === 'subtree') {
|
|
80
95
|
if (!componentId)
|
|
81
96
|
throw new PlatformApiError("mode='subtree' için componentId zorunludur.");
|