@appaflytech/zeyrek-ai-mcp 0.0.30 → 0.0.31

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.
@@ -129,7 +129,7 @@ export const componentKnowledge = {
129
129
  "Aksiyona tıklanınca komponent ÖNCE tıklanan satırı kendi selectedRow prop'una yazar, SONRA akışı çalıştırır (Table.svelte:72 setNode + onEmit) — akışta satır '<elementId>.selectedRow.<alan>' ile okunur. selectedRow katalogda hidden'dır; setter ile yazılmaz, komponent doldurur.",
130
130
  "style prop'u 2026-08-27'de EKLENDİ: parçalar &__toolbar · &__title · &__head · &__th · &__body · &__row · &__td. table'ın aksine tek yazım kuralı vardır — hepsi designerCss'ten geçer, yani camelCase yazılır ve iç seçici alır. (Bu tarihten önce datatable stillendirilemiyordu; eski notlar 'style YOKTUR' diyorsa güncel değildir.)",
131
131
  "Satır bazlı _columns görünüm ezmesi YOKTUR — o table'a özgüdür; renklendirme yalnız kolonun colorMap/autoColor sözlüğüyle, satırdan bağımsız kurulur. Aksiyon ise satıra göre değişebilir: rows[i]._actions[<aksiyon index>] = { isHidden, isDisabled, text … } o satırda aksiyonun üstüne biner (rowActions).",
132
- "SATIR KOŞULLU AKSİYON (2026-09-25): actions[i].isHidden / isDisabled bir kod ifadesi olabilir — { type:'code', ref:'{{ !item.isCustomizable }}' }; tablo onu HER SATIR için item = o satır ile çözer (machine.evaluateExpression). Connect Data'ya bağlı tabloda satırlar _actions taşıyamadığı için tek yol budur. Sıra: tanım → ifade → satırın _actions'ı. Koşullu aksiyon sütun hesabına aday sayılır; kanvasta ifade çözülmez, aksiyon her satırda görünür.",
132
+ "SATIR KOŞULLU AKSİYON (2026-09-25): actions[i].isHidden / isDisabled bir kod ifadesi olabilir — { type:'code', ref:'{{ !item.isCustomizable }}' }; tablo onu HER SATIR için item = o satır ile çözer (machine.evaluateExpression). Connect Data'ya bağlı tabloda satırlar _actions taşıyamadığı için tek yol budur. Sıra: tanım → ifade → satırın _actions'ı. Sütun çizilen satırlardan hesaplanır: aksiyon en az bir satırda görünüyorsa sayılır, her satırda gizliyse (koşul ya da _actions) sütun hiç çizilmez (2026-10-02); satır yokken koşullu aksiyon aday sayılır. Kanvasta ifade çözülmez, aksiyon her satırda görünür.",
133
133
  "HÜCRE ŞABLONU (2026-09-21): bir kolonun hücresi serbest tasarlanabilir — tablonun ÇOCUĞU olarak bir datatable-cell düğümü (props.column = o kolonun name'i) eklenir, içine bırakılan bileşenler o kolonda her satırda çizilir ve satırı item olarak okur ({{ item.title }}). Tablonun başka türde çocuğu olmaz. Ayrıntı: datatable-cell kaydı.",
134
134
  "keepSampleData (gizli switch, 2026-09-21) bir TASARIMCI kararıdır: true iken studio kanvasında bağlı olmayan tablonun üstündeki 'Örnek veri gösteriliyor · Connect data' çağrısı ve perdesi çizilmez. Runtime okumaz, veriyi etkilemez; örnek satırlarla bırakılacak bir tabloda true yazılabilir, rows bağlanınca anlamı kalmaz. Kanvasta tablo kendi içinde yatay kayar ve kolon başlığı tıklaması o kolonun panelini açar (designer.interactive) — bu da yalnız studio davranışıdır.",
135
135
  "KOLON GENİŞLİĞİ (2026-09-22): başlığın sağ kenarı sürüklenerek (ya da tutamaç odaktayken ←/→, 10px) kolon daraltılıp genişletilir; en dar 60px. Kanvasta bu kolonun columns[i].width değerini yazar — paneldeki Width alanıyla aynı değer, tek geri alma adımı. Runtime'da tanıma YAZILMAZ: ziyaretçinin seçimi tarayıcıda kolon tercihiyle (localStorage ZY_DataTableColumns_<elementId>.widths) saklanır, kolon menüsündeki Sıfırla onu da siler. Genişliği kalıcı vermek için width yaz.",
@@ -137,12 +137,17 @@ export const componentKnowledge = {
137
137
  "DÜZENLEME (2026-09-17, f-21; satır bazlı): editMode none|row|batch. Satır ✏ düğmesi ya da çift tıkla düzenlemeye açılır ve YAZILABİLİR HÜCRELERİNİN HEPSİ aynı anda editör olur (tek hücre editörü yok); ilişki hücresi lookup panelini açar. Enter satırı bitirir, Escape vazgeçer. row kipinde satır sonundaki ✓ o satırı kaydeder (hatasızsa satır kapanır), batch'te ✓ yalnız satırı kapatır ve üst çubukta 'N değişiklik · Kaydet · Vazgeç' kalır; kapanmış ama kaydedilmemiş satırda ✓/✕ görünür. canAddRow yeni satırı açar (newRowDefaults ile dolu, düzenlemede açılır; üstteki '+ Satır' düğmesi 2026-09-18'de KALKTI — yerine addRowButton ile satırların altında/üstünde mavi '+ Satır ekle' satırı ve addRowShortcut açıkken tablo odaktayken Ctrl/Cmd+Enter; buton alttayken yeni satır da en alta eklenir), canDeleteRow satır sonunda onaylı Sil. Kaydet 'save' olayı yayar ve changeset'i self.changeset'e yazar. BAĞLI TABLODA (rows connectData, behavior.edit açık) editMode/canEditRow/canAddRow/canDeleteRow/newRowDefaults/keyField'ı runtime tanım ∧ yazma kabiliyetinden YAZAR, yazmayı connect-data ucuna kendisi yapar (create → update → delete; hatalar self.rowErrors, satır taslakta kalır) — save için AKIŞ YAZILMAZ. Hücre yalnız capability.canWrite (policy ∧ editable) açıksa düzenlenir; bağlı olmayan tabloda kolonun editable işareti (columns[].editable). image/file/progress/link hücresi, bağlı olmayan tablonun maskeli hücresi ve anahtarı (keyField; bağlı tabloda kaynağın birincil anahtarı, yoksa id) olmayan ya da sayfada tekrar eden satır düzenlenmez/silinmez. Kaydı süren satırın ✓/Sil/Kaydet düğmesi pasiftir (çift kayıt yok); atomic tanımda toplu yazmada düşen satırın komşuları 'Kaydedilmedi' işaretiyle taslakta kalır (hiçbiri yazılmadı; writeMode partial'da komşular yazılır, yalnız düşen satır kırmızı kalır); ✕ satırın hatasını da siler. Hücre seçimi (selectedCells, cellSelectionChange) yalnız düzenleme kipinde. Kanvasta düzenleme pasiftir. GÜNCELLEME (2026-09-18): tasarımcıda batch seçeneğinin etiketi 'Cell'dir (değer batch kalır). Yazılabilir ilişki hücresi TEK tıkta yalnız seçilir; ÇİFT tık düzenler: row kipinde satırın bütün yazılabilir hücreleri açılır, Cell (batch) kipinde kayıtlı satırın YALNIZ çift tıklanan hücresi editör olur (Enter ya da alandan çıkmak değeri taslakta bırakıp kapatır, Escape yalnız editörü kapatır; ✏ ve yeni satır iki kipte de bütün satırı açar); ilişki hücresinde seçim paneli açılır; düzenlenen satırda ilişki hücresi bir seçici düğmedir (değer + ok). batch kipinde çift tıklanan hücre aynı kolondaki çoklu hücre seçiminin içindeyse yazılan değer seçili BÜTÜN hücrelere gider (row kipinde yalnız o satır). batch kipinde Sil onaysızdır ve satırı 'SİL' rozetiyle, üstü çizili olarak silinecek diye İŞARETLER (↶ ile geri alınır); silme kayıtta changeset.deleted'a girer. Yeni satır 'YENİ' rozeti ve yeşil zemin alır; değişen hücrede köşe işareti ve altında üstü çizili eski değer vardır. Üst çubuk KALKTI: tablonun altında 'N değişiklik · N yeni · N silinecek · N hata' özeti, 'Tümünü geri al' ve 'Değişiklikleri kaydet (N)' durur. Filtre özetinde ve filtre çubuğunda lookup'tan seçilen ilişki değeri kimliğiyle değil görünen alanıyla yazılır; filtre çubuğunda ilişki kolonu doğrudan seçim paneliyle açılır ve seçim hemen uygulanır. GÜNCELLEME (2026-09-19): editMode'a cell (hücre başına kayıt; tanımda saveMode 'cell') eklendi; tasarımcıda batch'in etiketi artık 'Batch', 'Cell' etiketi yeni cell kipidir. cell kipinde kayıtlı satırın çift tıklanan hücresi tek başına açılır ve ✓/✕ editörün kendi çerçevesinin sağ ucunda, ayraçtan sonra iki ikon düğmedir (arama kutusunun temizle düğmesi gibi) (satır ✏ düğmesi yoktur): ✓ ya da Enter yalnız o alanı hemen kaydeder (update patch'inde tek alan), ✕ ya da Escape vazgeçer; alandan çıkmak editörü kapatmaz. Aynı anda tek hücre açıktır, başka hücreye geçmek açık hücrenin kaydedilmemiş değerini atar; kayıt hata verirse hücre açık kalır ve hata altında görünür. İlişki hücresinde panel kapanınca hücre açık kalır, seçim ✓ ile yazılır. Yeni satır, Sil (onaylı) ve toplu düzenleme row kipindeki gibi hemen kaydedilir; edit kolonu yalnız canAddRow ya da canDeleteRow açıksa görünür. Çoklu hücre seçiminin içindeki hücre açılırsa (batch ve cell) değer seçili bütün hücrelere yazılır; cell kipinde ✓ hepsini tek changeset'te kaydeder, ✕ hepsinden vazgeçer. Edit kolonu (✏ 🗑) sağa yapışıktır (sticky), genişliği boştaki düğmelere göredir; ✏/🗑 hover'da görünür. Çakışan işlemler kilitlidir: toplu satır seçimi varken çift tık/✏ düzenleme açmaz, Satır ekle (ve Ctrl+Enter) gizlenir, ✏/🗑 görünmez; düzenleme açıkken satır seçim kutuları devre dışıdır ve ✏/🗑 görünmez. Escape ilişki hücresinde (lookup paneli açıkken de) düzenlemeyi kapatır.",
138
138
  "LOOKUP (2026-09-17, f-23): bağlı tabloda YAZILABİLİR relation hücresi lookup panelini açar (hedef tablo op:lookup ile, arama + 'Daha fazla'), seçim yalnız düzenlenen satırda; salt okunur tabloda, editable dışı alanda ya da güncellenemeyen satırda hücre HİÇBİR ŞEY açmaz (2026-09-21 — eski gezinme paneli kaldırıldı). Panelin 'Detaylı ara…' bağlantısı tanımın lookup.mode'una göre pencere ya da sağ çekmece açar (mode yazılmamışsa pencere, 2026-09-19) ve paneldeki arama metniyle başlar: kolonlar lookup.fields (türetilmişte anahtar dışı alanlar), arama, lookup.behavior.filterable alanlarında VE koşulları, başlıktan sıralama, sayfalar; o2m/self tekil seçim ('Seç' ya da çift tık), m2m onay kutuları + 'Uygula'; 'Temizle' alan zorunluysa (describe isRequired) gizli; yazma yetkisi yoksa panel yalnız gezinme. Seçilen hedef hücreye yerel yazılır, kayıt { alan: '<id>' } / m2m ['<id>', …] olarak gider. Filtre panelinde relation kolonunun değeri 'Seç…' ile aynı panelden seçilir (in/notin çoklu), chip'te görünen ad. Hedefin görünen alanı metin (ve korumasız) ise sunucu ilişkiye contains/icontains da sunar (2026-09-21): bu işleçte değer seçilmez, YAZILIR ve hedefin görünen alanında aranır ({ field:'city', operator:'icontains', value:'ist' } → EXISTS … title ILIKE '%ist%'); gelişmiş panelde, column modundaki işleç düğmesinde ve bar hapının editöründe (işleç seçimi + kayıt seçimi ya da metin) çıkar; bar ile gelişmiş panel birlikteyken panel çubuğun ÜSTÜNDE açılır, çubuk hep tablonun hemen üstündedir. Hedef tabloyu okuyamayan (targetReadable:false) hücre gri “—” + kilit çizer ve panel açmaz. Hedefin maskeli alanı ayrıntılı aramada maske olarak (Göster ile) çizilir.",
139
139
  "TOPLU (2026-09-17, f-22): bulkActions none|edit|delete|both açık ve editMode none değilken başa onay kutusu kolonu gelir (başlıkta sayfadaki satırları seç); anahtarlı sunucu satırları seçilir, seçim self.selectedRows'a yazılır ve rowSelectionChange yayılır. Seçim çubuğu: 'N seçili · Toplu düzenle · Sil · Temizle'. Toplu düzenle bir FORMDUR ve sağdan açılan bir ÇEKMECEDE (drawer, 2026-09-19) alanlar alt alta listelenir; altta Vazgeç · Uygula, arka plan/×/Escape uygulamadan kapatır: yazılabilen her kolon için editör (anahtar üçlü seçim, ilişki alanında lookup paneli); yalnız doldurulan alanlar uygulanır, boş bırakılan değişmez; ilişki alanının ×'i temizler (o2m null, m2m boş liste — hücre editörüyle aynı); row kipinde bütün doldurulan alanlar tek save (tek update isteği, bulk), batch'te taslağa girer. Sil onaylı, tek delete. Hatasız yazılan satırlar seçimden düşer, hatalılar kırmızı ve seçili kalır; 1000 satır üstünde toplu eylem kapanır. BAĞLI TABLODA bulkActions'ı runtime yazar: tanım edit.bulk ∧ meta.write.bulk, update → edit, delete → delete. Hücre seçimi (Shift/Ctrl/Alt, selectedCells, cellSelectionChange) tek başına değer doldurmaz; batch kipinde seçimin içindeki bir hücre çift tıkla düzenlenirse değer seçili hücrelerin hepsine yazılır (2026-09-18), çok alanı birlikte değiştirmek toplu düzenle formuyla yapılır. Kilitli (yazılamayan) hücre seçime girmez.",
140
- "YERLEŞİM VE FİLTRE MODLARI (2026-09-18; 2026-09-21'den beri gelişmiş oluşturucu filterMode değil advancedFilter anahtarıdır ve column/bar ile birlikte açılabilir): advancedFilter (koşul oluşturucu: tümü/herhangi biri, VE/VEYA, grup; kurallar tutamaçtan sürüklenip grubun içine/dışına taşınır; Uygula yalnız her kuralın kolonu ve değeri doluyken görünür, Sıfırla yalnız uygulanmış filtre ya da taslakta kural varken; ziyaretçi filtreyi adıyla kaydedebilir — tanıma yazılmaz, tarayıcıda element id'siyle saklanır (localStorage ZY_DataTableFilters_<elementId>), üst çubukta Filtre düğmesinin yanındaki 'Kayıtlı filtreler' açılır menüsünden (filterPosition left'te sağında, right'ta solunda) panel açılmadan seçilip uygulanır, kanvasta yoktur; filtre yokken panel boş bir kuralla açılır; 'Filtre (n)' düğmesiyle açılır, Ctrl+Enter uygular — düğme ARAÇ GRUBUNUN PARÇASI DEĞİLDİR (2026-09-18): araçlar nerede olursa olsun tablonun üstündeki çubukta durur, filterPosition left'te başlığın hemen ardına, right'ta sağ kenara) · column (başlığın altında kolon başına filtre satırı: metinde işleç menüsü, sayı/tarihte min–max (bağlı tablonun birincil anahtarı hariç: tek değer, eşittir), boolean Tümü/Evet/Hayır, enum/tag'de çoklu seçim, relation'da lookup) · bar (tablo üstünde kolon başına açılır düğme + panel) · none. ÜÇ MOD DA AYNI filters sözleşmesini yazar ve mount'u tetikler — bağlı tabloda işleçler capability.operators'tan gelir (aralık yalnız gte+lte sunulursa), enabled:false kolon hiçbir modda çıkmaz; column/bar ekranı her zaman filters prop'undan kurulur (sunucu 422 ile reddedince geri yazılan filtre görünür). showFilterSummary aktif filtre + sıralamayı kaldırılabilir chip'ler olarak gösterir; bar modunda çizilmez (aktif filtreler barın haplarında durur ve oradan düzenlenir), sonuç sayısı ne barda ne özette yazılır — footer'daki kayıt özeti taşır. Başlık (title) boşsa üst çubuk yalnız araçlar üstteyse ya da advanced filtre düğmesi varsa çizilir; başlıksız tabloda filterMode bar iken üst köşedeki araçlar ayrı çubuk açmaz, filtre çubuğunun ucuna oturur. Araçlar (showRefresh · showExport: bu sayfanın CSV'si · showColumnManager: 'Column settings' menüsü — kolon gizle/göster ve sürükleyerek (ya da tutamaçta ↑/↓) sırala, tanıma yazılmaz; seçim tarayıcıda element id'siyle saklanır (localStorage ZY_DataTableColumns_<elementId>) ve tablo aynı kolonlarla açılır, Reset kaydı siler, kanvas okumaz; üçü de yalnız ikon + tooltip) toolsPosition ile dört köşeden birine oturur; footer (sol altta kayıt özeti '1–10 / 24 kayıt · 1 seçili' — showRecordSummary ile kapatılır —, sayfa başına, «ilk ‹önceki sayfa numaraları sonraki› son» düğmeleri) footerPosition ile tablonun altında ya da üstündedir; sayfalama paginationPosition ile kayıt sayısının ardında (left) ya da sağ kenardadır, stickyFooter footer'ı sayfa kaydırmasında görünür kenara yapıştırır. stickyHeader başlığı tablonun KENDİ kaydırmasında üstte tutar (sabit yükseklikli tablo); kolonun isPinned'i kolonu başa alıp yatay kaydırmada solda sabitler. size sm|md|lg satır yüksekliğini, hücre boşluğunu ve yazı boyunu değiştirir. YENİ BIRAKILAN TABLO (2026-09-18): başlıksız, filterMode bar, araçlar sağ üstte (filtre çubuğunun ucunda), isPaginated açık, sayfalama sağ altta, kayıt özeti sol altta, stickyHeader açık, size md, Status kolonu solid tag; bu değerler yalnız şema varsayılanıdır — prop'u hiç yazılmamış eski düğüm eski görünüşünü korur (advanced, sağ üst araçlar, sağda sayfalama, yapışkan başlık kapalı). paginationMode pages (numaralı) | infinite (gövde sona kaydırıldıkça sonraki sayfa offset=yüklenen satır sayısıyla istenir ve satırlar birikir; kendi yüksekliğiyle uzayan tabloda footer'da 'Daha fazla' düğmesi). Gövde durumları: yükleniyor (iskelet), kayıt yok, filtre sonucu boş (Filtreleri kaldır), hata (loadError; Tekrar dene mount'u yeniden çalıştırır, satır varken tablo üstünde şerit)."
140
+ "YERLEŞİM VE FİLTRE MODLARI (2026-09-18; 2026-09-21'den beri gelişmiş oluşturucu filterMode değil advancedFilter anahtarıdır ve column/bar ile birlikte açılabilir): advancedFilter (koşul oluşturucu: tümü/herhangi biri, VE/VEYA, grup; kurallar tutamaçtan sürüklenip grubun içine/dışına taşınır; Uygula yalnız her kuralın kolonu ve değeri doluyken görünür, Sıfırla yalnız uygulanmış filtre ya da taslakta kural varken; ziyaretçi filtreyi adıyla kaydedebilir — tanıma yazılmaz, tarayıcıda element id'siyle saklanır (localStorage ZY_DataTableFilters_<elementId>), üst çubukta Filtre düğmesinin yanındaki 'Kayıtlı filtreler' açılır menüsünden (filterPosition left'te sağında, right'ta solunda) panel açılmadan seçilip uygulanır, kanvasta yoktur; filtre yokken panel boş bir kuralla açılır; 'Filtre (n)' düğmesiyle açılır, Ctrl+Enter uygular — düğme ARAÇ GRUBUNUN PARÇASI DEĞİLDİR (2026-09-18): araçlar nerede olursa olsun tablonun üstündeki çubukta durur, filterPosition left'te başlığın hemen ardına, right'ta sağ kenara) · column (başlığın altında kolon başına filtre satırı: metinde işleç menüsü, sayı/tarihte min–max (bağlı tablonun birincil anahtarı hariç: tek değer, eşittir; date ve datetime kolonda gün seçilir ve gte 'GÜNT00:00:00' · lte 'GÜNT23:59:59' gider — bitiş günü dahildir, 2026-10-02), boolean Tümü/Evet/Hayır, enum/tag'de çoklu seçim, relation'da lookup) · bar (tablo üstünde kolon başına açılır düğme + panel) · none. ÜÇ MOD DA AYNI filters sözleşmesini yazar ve mount'u tetikler — bağlı tabloda işleçler capability.operators'tan gelir (aralık yalnız gte+lte sunulursa), enabled:false kolon hiçbir modda çıkmaz; column/bar ekranı her zaman filters prop'undan kurulur (sunucu 422 ile reddedince geri yazılan filtre görünür). showFilterSummary aktif filtre + sıralamayı kaldırılabilir chip'ler olarak gösterir; bar modunda çizilmez (aktif filtreler barın haplarında durur ve oradan düzenlenir), sonuç sayısı ne barda ne özette yazılır — footer'daki kayıt özeti taşır. Başlık (title) boşsa üst çubuk yalnız araçlar üstteyse ya da advanced filtre düğmesi varsa çizilir; başlıksız tabloda filterMode bar iken üst köşedeki araçlar ayrı çubuk açmaz, filtre çubuğunun ucuna oturur. Araçlar (showRefresh · showExport: bu sayfanın CSV'si · showColumnManager: 'Column settings' menüsü — kolon gizle/göster ve sürükleyerek (ya da tutamaçta ↑/↓) sırala, tanıma yazılmaz; seçim tarayıcıda element id'siyle saklanır (localStorage ZY_DataTableColumns_<elementId>) ve tablo aynı kolonlarla açılır, Reset kaydı siler, kanvas okumaz; üçü de yalnız ikon + tooltip) toolsPosition ile dört köşeden birine oturur; footer (sol altta kayıt özeti '1–10 / 24 kayıt · 1 seçili' — showRecordSummary ile kapatılır —, sayfa başına, «ilk ‹önceki sayfa numaraları sonraki› son» düğmeleri) footerPosition ile tablonun altında ya da üstündedir; sayfalama paginationPosition ile kayıt sayısının ardında (left) ya da sağ kenardadır, stickyFooter footer'ı sayfa kaydırmasında görünür kenara yapıştırır. stickyHeader başlığı tablonun KENDİ kaydırmasında üstte tutar (sabit yükseklikli tablo); kolonun isPinned'i kolonu başa alıp yatay kaydırmada solda sabitler. size sm|md|lg satır yüksekliğini, hücre boşluğunu ve yazı boyunu değiştirir. YENİ BIRAKILAN TABLO (2026-09-18): başlıksız, filterMode bar, araçlar sağ üstte (filtre çubuğunun ucunda), isPaginated açık, sayfalama sağ altta, kayıt özeti sol altta, stickyHeader açık, size md, Status kolonu solid tag; bu değerler yalnız şema varsayılanıdır — prop'u hiç yazılmamış eski düğüm eski görünüşünü korur (advanced, sağ üst araçlar, sağda sayfalama, yapışkan başlık kapalı). paginationMode pages (numaralı) | infinite (gövde sona kaydırıldıkça sonraki sayfa offset=yüklenen satır sayısıyla istenir ve satırlar birikir; kendi yüksekliğiyle uzayan tabloda footer'da 'Daha fazla' düğmesi). Gövde durumları: yükleniyor (iskelet), kayıt yok, filtre sonucu boş (Filtreleri kaldır), hata (loadError; Tekrar dene mount'u yeniden çalıştırır, satır varken tablo üstünde şerit).",
141
+ "TİPE BAĞLI İLİŞKİ (2026-10-01, ADR 0012): 'Kime' gibi bir hücre satırın türüne göre farklı tabloya bağlanıyorsa (hedef başına ayrı nullable FK: hedef_user_id, hedef_pozisyon_id, hedef_birim_id) kolonu görünen adı tutan METİN alanına bağla ve columns[i].relationTypeField = türü tutan alan (ör. hedef_tipi), relationTargets = [{ value:'kullanici', field:'hedef_user_id' }, …] yaz. Hedef FK kolonları columns'ta isHidden:true durmalı ve tanımın editable listesinde olmalı (tür, ad ve üç FK). Satır düzenlemesinde hücre o satırın (taslaktaki) türünün gizli ilişki kolonunun lookup'ını açar; seçim TEK YAMAYLA seçilen FK'yi, öbür FK'leri null'u ve görünen adı (hedefin görünen alanı) yazar; tür değişince FK'ler ve ad boşalır. Tür boşken hücre salt okunurdur. Toplu düzenleme bu kolonu sunmaz. Birden çok türün paylaştığı hedef tabloda (birimler: departman/firma/grup) relationTargets öğesine filterField + filterValues ('holding, company') ve isteğe bağlı excludeField + excludeValues yazılır; alanlar tanımda o FK'nin lookup.behavior.filterable listesinde olmalı. Tür değişince Kime seçicisi hemen açılır; seçimsiz kapanırsa tür ve Kime eski hâline döner. Kayıt satırın kaydıdır — editMode row önerilir.",
142
+ "SÜRÜKLE-SIRALA (2026-10-01, ADR 0012): canReorderRows:true + orderField:'<sayı alanı>' (editMode none değilken ve sıra kolonu yazılabilirken) satır başına tutamaç kolonu açar; satırlar orderField'a göre çizilir (boşlar sonda). Sürükle ya da tutamaç odaktayken ↑/↓. Bırakılan satır kendi adımı olur, aynı sıradaki satırlar grup kalır (iki eş sıralı satırın arasına bırakılan o gruba katılır), adımlar 1..n yeniden numaralanır; yalnız değeri değişen satırlar yazılır. row/cell kipinde hemen tek save (operation rowReorder), batch'te taslakta bekler. Yeni ve anahtarsız satır taşınmaz; açık düzenleme, toplu seçim ya da süren kayıt varken tutamaç pasiftir. Eş sıra vermek için sıra hücresi elle düzenlenir.",
141
143
  ],
142
144
  propNotes: {
145
+ searchText: "Gizli, AKIŞIN yazdığı arama metni (2026-10-02, web ve mobil). Tablonun kendi arama kutusu yoktur; ayrı bir textinput'un değeri setter ile <tablo>.searchText'e yazılır. Bağlı tabloda setter bu yazımdan sonra listeyi YENİDEN OKUR (sayfa başa döner) ve istek search taşır: sunucu tanımın behavior.searchable alanlarında içerir (icontains) koşuluyla arar — entity kaynağında ve dinamik filtreli Table kaynaklı microservice'te. Searchable boşsa istek 422 ConnectDataFilterNotAllowed. Aynı yol filters / sortField / sortType / offset / limit için de geçerlidir: setter bunlardan birini yazınca bağlı liste yeniden okunur.",
146
+ canReorderRows: 'boolean (varsayılan false) — satırları tutamaçtan sürükleyerek sıralama; orderField olmadan ya da editMode none iken etkisiz.',
147
+ orderField: "Sırayı tutan sayı alanının adı (ör. 'sira'); canReorderRows açıkken satırlar buna göre çizilir ve sürükleme buraya yazar.",
143
148
  surfaceStyle: NODE_SURFACE_NOTE + " Datatable'ın kabı VARSAYILAN olarak kendi 1px çerçevesini, 6px köşesini ve yüzey zeminini çizer (filtre çubuğu + ızgara + footer'ı saran çerçeve). Grupta bir kenarlık yazılınca (borderTop/Right/Bottom/Left — 'none' dahil) kap KENDİ çerçevesini bırakır: çerçeveyi kaldırmak için dört yöne 'none' yaz; düz çizgi yazmak ikinci çerçeve eklemez. radius yazılınca kap köşeyi düğüm kutusundan alır, background yazılınca kabın zemini saydamlaşır (2026-09-18).",
144
149
  editMode: "none | row | cell | batch (tasarımcı etiketleri Row / Cell / Batch). Bağlı tabloda runtime tanımın behavior.edit.saveMode'undan yazar; elle yazmak bağlı tabloda etkisizdir. Bağlı olmayan tabloda save olayındaki self.changeset'i akış yazar.",
145
- operation: "Salt okunur, save'den ÖNCE yazılır (2026-09-21): kaydı doğuran işlem — rowAdd · rowEdit · cellEdit · rowDelete · bulkEdit · bulkDelete · batchSave (batch kipinin 'Değişiklikleri kaydet'i, karışık changeset). Ayrı olay YOKTUR: tek save akışı Condition'da self.operation ile ayırır.",
150
+ operation: "Salt okunur, save'den ÖNCE yazılır (2026-09-21): kaydı doğuran işlem — rowAdd · rowEdit · cellEdit · rowDelete · bulkEdit · bulkDelete · batchSave (batch kipinin 'Değişiklikleri kaydet'i, karışık changeset) · rowReorder (sürükle-sırala). Ayrı olay YOKTUR: tek save akışı Condition'da self.operation ile ayırır.",
146
151
  editedRows: "Salt okunur, save'den önce yazılır: eklenen/düzenlenen satırların TAM hâli (sunucu satırı + yama; yeni satırda girilen değerler) — changeset.updated yalnız yamayı taşır. Kolonlara bağlıdır: akış editörü self.editedRows[0].<kolon> önerir.",
147
152
  deletedRows: "Salt okunur, save'den önce yazılır: silinen satırların TAM hâli (tek silmede bir öğe) — changeset.deleted yalnız { rowId, id } taşır.",
148
153
  editedCell: "Salt okunur, save'den önce yazılır: yalnız operation cellEdit iken { rowId, field, value, previousValue }, diğer işlemlerde null.",
@@ -154,7 +159,7 @@ export const componentKnowledge = {
154
159
  image: "IMAGE/FILE kolonu düz URL string'i KABUL ETMEZ: hücre `{ absolute_path, display_name }` (ya da `fileName`) alanlarını taşıyan bir DOSYA NESNESİ (veya nesne dizisi) bekler — `DisplayCellView.svelte:10` `file.absolute_path ?? ''` okur, düz string verilince kaynak boş kalır ve hücre SESSİZCE boş çizilir (09-04 Y-21). Satır verisi bir metin kolonunda URL taşıyorsa kolonu 'url'/'link' yap ya da veriyi { absolute_path: '<url>', display_name: '<ad>' } biçimine çevir; entity'nin FILE alanı zaten bu şekli döner.",
155
160
  actions: "Satır aksiyonları. kind sözlüğü BUTON'unkinden farklıdır: error · information · solid · transparent · dark · danger · warning (butonda success/error/info/primary/warning). Yanlış sözlükten değer invalid-enum-value verir.",
156
161
  selectedRow: "Salt okunur: satır aksiyonu tıklanınca komponentin kendisi doldurur; akış ifadesinde '<elementId>.selectedRow' ile okunur.",
157
- colorMap: "Kolon içi değer→renk sözlüğü: [{ value:'Active', color:'#16a34a' }]. STATİKTİR — hücrenin kendi değerine bakar, satırın başka alanına göre hesaplanamaz. Satıra göre koşullu renk gerekiyorsa ya table + _columns ezmesi kullan ya da satır verisinde rengi taşı ({ label, color }) — render sırası: veri objesindeki color → colorMap[etiket] → autoColor. TAG HÜCRE ŞEKLİ: metin, { label, color? } ya da { value, color? } (label yoksa value okunur; ikisi de yoksa obje ham JSON basılır) veya bunların dizisi. RENK (veri objesinde de colorMap'te de): semantik ad primary · info · success · warning · error TEMA TOKEN'INA çözülür (tema değişince izler, ham hex yazmaya gerek yok); hex/rgb/'var(--ng-…)'/CSS renk adı olduğu gibi geçer; başka bir değer (yazım hatası, çözülmemiş '{{ }}') o kademeyi ATLAR ve sıradakine düşer. Veri objesindeki variant OKUNMAZ — outline/solid kolonun appearance'ından gelir. Solid rozetin yazısı BEYAZDIR, yalnız gerçekten açık zeminde (sarı/amber, açık gri; LCH açıklığı > 70) siyah seçilir — token'da CSS ile, hex'te ölçülerek. SATIR MODU (2026-09-18): [{ value, color, mode:'solid' }] o değeri kolonun appearance'ı (subtle/outline) ne olursa olsun dolu zemin + BEYAZ yazıyla çizer; mode yazılmazsa ya da 'soft' ise kolonun görünümü geçerlidir. Renksiz satırın solid modu yok sayılır.",
162
+ colorMap: "Kolon içi değer→renk sözlüğü: [{ value:'Active', color:'#16a34a' }]. ETİKET (2026-10-01): satıra label yazılırsa hücre ham değer yerine onu çizer — [{ value:'Approved', label:'Onaylandı', color:'success' }] veritabanındaki İngilizce durumu Türkçe rozetle gösterir; filtre seçenekleri de etiketi gösterir, sıralama/filtre ham değerle çalışır. Bağlı kolonda aynı değerin enum etiketini EZER. Etiketli değerin solid modu etikete göre uygulanır. STATİKTİR — hücrenin kendi değerine bakar, satırın başka alanına göre hesaplanamaz. Satıra göre koşullu renk gerekiyorsa ya table + _columns ezmesi kullan ya da satır verisinde rengi taşı ({ label, color }) — render sırası: veri objesindeki color → colorMap[etiket] → autoColor. TAG HÜCRE ŞEKLİ: metin, { label, color? } ya da { value, color? } (label yoksa value okunur; ikisi de yoksa obje ham JSON basılır) veya bunların dizisi. RENK (veri objesinde de colorMap'te de): semantik ad primary · info · success · warning · error TEMA TOKEN'INA çözülür (tema değişince izler, ham hex yazmaya gerek yok); hex/rgb/'var(--ng-…)'/CSS renk adı olduğu gibi geçer; başka bir değer (yazım hatası, çözülmemiş '{{ }}') o kademeyi ATLAR ve sıradakine düşer. Veri objesindeki variant OKUNMAZ — outline/solid kolonun appearance'ından gelir. Solid rozetin yazısı BEYAZDIR, yalnız gerçekten açık zeminde (sarı/amber, açık gri; LCH açıklığı > 70) siyah seçilir — token'da CSS ile, hex'te ölçülerek. SATIR MODU (2026-09-18): [{ value, color, mode:'solid' }] o değeri kolonun appearance'ı (subtle/outline) ne olursa olsun dolu zemin + BEYAZ yazıyla çizer; mode yazılmazsa ya da 'soft' ise kolonun görünümü geçerlidir. Renksiz satırın solid modu yok sayılır.",
158
163
  tableStyle: "zebra | plain — satır zemini varyantı; CSS DEĞİL. Serbest CSS için ayrı `style` prop'u vardır (parça bazlı).",
159
164
  style: "Parça bazlı CSS: &__toolbar · &__title · &__footer · &__summary · &__filter-bar · &__filter-panel · &__head · &__th · &__body · &__row · &__td. designerCss her bildirime !important ekler, yani komponentin kendi çerçeve/ayraç varsayılanlarını ezer — Figma tipi düz tablo bununla kurulur.",
160
165
  filterMode: "column | bar | none — satır içi filtre arayüzü; gelişmiş koşul oluşturucu ondan bağımsız advancedFilter anahtarıdır, ikisi birlikte açılabilir. Hepsi aynı filters dizisini yazar, akış/bağlantı farkı görmez. column/bar modunda VE/VEYA ve grup kurulamaz (kök kurallar and ile bağlanır); oradan uygulanan filtre yalnız o kolonun kök kurallarını değiştirir, gelişmiş paneldeki grup/or kuralları KORUNUR ve kolon/bar kuralları panelde kural olarak görünür. Bar'ın 'Filtreleri temizle'si yalnız bar kurallarını kaldırır. Eski düğümdeki filterMode=advanced (advancedFilter yazılmamışken) gelişmiş oluşturucu olarak okunur.",
@@ -466,7 +471,7 @@ export const componentKnowledge = {
466
471
  'multi-select': {
467
472
  summary: 'Aranabilir çoklu seçim; value bir değer dizisidir, seçilenler etiket (chip) olarak görünür, search olayı sunucu taraflı arama kurmaya yarar.',
468
473
  behavior: [
469
- "value bir DİZİDİR ve üç biçimi de okunur: düz değer (['policies', 7]), şemanın biçimi ([{ value, label }]) ve eski ikizler ([{ id, text }]). Düz değer yazmak 2026-09-11'e kadar etiketi 'undefined' gösteriyordu; artık değer etiketin kendisi olur ve doğru metin options yüklenince gelir.",
474
+ "value bir DİZİDİR ve üç biçimi de okunur: düz değer (['policies', 7]), şemanın biçimi ([{ value, label }]) ve eski ikizler ([{ id, text }]). Düz değer yazmak 2026-09-11'e kadar etiketi 'undefined' gösteriyordu; artık değer etiketin kendisi olur ve doğru metin options yüklenince gelir (2026-10-01'e kadar gelmiyordu: akışın yazdığı id dizisi seçenekler yüklense de çipte id olarak kalıyordu).",
470
475
  "Etiketin metni options'tan tazelenir: aynı value'lu option listede varsa onun label'ı yazılır. Bu arama METİNLEŞTİREREK karşılaştırır (String(a) === String(b)), AMA yalnız etiket metni için: satırın işaretli görünmesi ve seçimin kaldırılması KATI karşılaştırma kullanır. Yani value'da metin '34', options'ta sayı 34 varsa çip doğru etiketle çizilir ama liste satırı işaretsiz kalır ve tıklamak hiçbir şey yapmaz — eşlemeyi akış katmanında aynı tipe getir (CQ-208).",
471
476
  "change yükü { value: [{ value, label, id, text }] } taşır ve düğümün value prop'una yazılır: akışta self.value ile okunur. id/text, value/label'ın eski adlı ikizleridir — kayıtlı sayfalar onlarla okuduğu için bilerek korunuyor.",
472
477
  'Her seçimden sonra arama kutusu TEMİZLENİR ve tam liste geri gelir (bilinçli davranış); liste kapanınca da temizlenir ve bir kez boş search yayılır.',
@@ -176,17 +176,17 @@ export function connectDataShapeIssues(ref, path = 'ref') {
176
176
  ...(definition.filter && !filtersOnServer ? ['filter'] : []),
177
177
  ...(definition.sort?.length && !filtersOnServer ? ['sort'] : []),
178
178
  ...(behavior?.filterable?.length && !filtersOnServer ? ['behavior.filterable'] : []),
179
- ...(behavior?.searchable?.length ? ['behavior.searchable'] : []),
179
+ ...(behavior?.searchable?.length && !filtersOnServer ? ['behavior.searchable'] : []),
180
180
  ...(behavior?.edit?.create || behavior?.edit?.update || behavior?.edit?.delete ? ['behavior.edit'] : []),
181
181
  ...(definition.page?.mode === 'cursor' ? ['page.mode'] : []),
182
182
  ...definition.fields.filter(field => field.expand?.length).map(field => `fields.${field.slug}.expand`),
183
183
  ...definition.fields.filter(field => field.lookup).map(field => `fields.${field.slug}.lookup`)
184
184
  ];
185
185
  for (const feature of unsupported) {
186
- const searchHint = feature === 'behavior.searchable' && (kind === 'view' || kind === 'microservice')
187
- ? ' Arama view (ve view kaynaklı microservice) kaynağında searchable ile değil, istekteki search ile sunucuda çalışır (describe source.textSearch).'
186
+ const searchHint = feature === 'behavior.searchable' && kind === 'view'
187
+ ? ' Arama view kaynağında searchable ile değil, istekteki search ile sunucuda çalışır (describe source.textSearch).'
188
188
  : '';
189
- issues.push({ code: 'CD-013', rule: 'connect-data-source-feature-unsupported', path: `${path}.${feature}`, message: `${kind} kaynağında '${feature}' yok: bu kaynak tanımda ${filtersOnServer ? 'searchable, yazma, expand ve lookup' : 'filtre, sıralama, searchable, yazma, expand ve lookup'} taşımaz${feature === 'page.mode' ? '; cursor sayfalama yalnız entity kaynağında' : ''}.${searchHint}` });
189
+ issues.push({ code: 'CD-013', rule: 'connect-data-source-feature-unsupported', path: `${path}.${feature}`, message: `${kind} kaynağında '${feature}' yok: bu kaynak tanımda ${filtersOnServer ? 'yazma, expand ve lookup' : 'filtre, sıralama, searchable, yazma, expand ve lookup'} taşımaz${feature === 'page.mode' ? '; cursor sayfalama yalnız entity kaynağında' : ''}.${searchHint}` });
190
190
  }
191
191
  }
192
192
  if (Object.keys(definition.params ?? {}).length && (kind === 'entity' || kind === 'view')) {
@@ -86,7 +86,7 @@ export const connectDataRuleCatalog = [
86
86
  {
87
87
  id: 'CD-013',
88
88
  severity: 'block',
89
- rule: "query, view ve workflow kaynağında filter, sort, behavior.filterable/searchable, açık edit, expand ve lookup yok; microservice kaynağında searchable, edit, expand ve lookup yok (filtre/sıralama uç destekliyorsa describe canFilter/sortable ile açılır). page.mode 'cursor' yalnız entity kaynağında (b-35 A).",
89
+ rule: "query, view ve workflow kaynağında filter, sort, behavior.filterable/searchable, açık edit, expand ve lookup yok; microservice kaynağında edit, expand ve lookup yok (filtre/sıralama/arama uç destekliyorsa describe canFilter/sortable ile açılır; arama yalnız içerir işleçli korumasız metin alanında — dinamik filtreye OR içerir koşulu olarak iner, 2026-10-02). page.mode 'cursor' yalnız entity kaynağında (b-35 A).",
90
90
  enforcedBy: 'set_connect_data op gövdesi ve connect_data_bind — connectDataShapeIssues; connect_data_bind params adları describe source.parameters\'a karşı (buildDefinition)',
91
91
  source: 'b-31/b-32 ConnectDataDefinitionValidator connect-data-source-feature-unsupported (422); f-33 ConnectDataService.ValidateSourcesAsync ref.params.<ad>'
92
92
  },
@@ -329,7 +329,7 @@ export function profileFindings(profile, input) {
329
329
  target: stepIds.join(', '),
330
330
  message: type === 'download'
331
331
  ? "mobil uygulamada 'download' tarayıcı indirmesi yapmaz: adres dönen yanıt Linking.openURL ile açılır, ikili gövde (JSON değil) önbelleğe yazılıp paylaşım sayfasında (expo-sharing) açılır"
332
- : "mobil uygulamada 'setcookie' çerez yazmaz, cihaz deposuna (storage, expo-sqlite/kv-store) eşlenir — okuyan ifadeler aynı çalışır"
332
+ : "mobil uygulamada 'setcookie' tarayıcı çerezi yazmaz, değeri güvenli cihaz deposuna (SecureStore) koyar; sonraki her istekte X-Flow-Cookie başlığıyla gider ve middleware/workflow onu web'deki gibi cookie.<ad> olarak okur — okuyan ifadeler aynı çalışır"
333
333
  });
334
334
  }
335
335
  return findings;
@@ -182,7 +182,7 @@ export const processNodeSchemas = [
182
182
  { type: 'start', purpose: 'Sürecin giriş noktası; tanımda üst düzeyde tam bir tane', required: ['id', 'type', 'description'], fields: { ...commonFields, trigger: 'row-event | manual | api | schedule | sub-process | message | signal', entity: 'row-event için entity slug', operation: 'create | update | delete', condition: 'Jint (row, previousRow)' }, example: { id: 'baslangic', type: 'start', description: 'Satır oluşunca başlar.', trigger: 'row-event', entity: 'gorevler', operation: 'create' } },
183
183
  { type: 'user-task', purpose: 'İnsan görevi; kutuya düşer', required: ['id', 'type', 'description', 'assignee'], fields: { ...commonFields, resultKey: 'sonucun adı: motor görev sonucunu { outcome, action, completedBy, payload } olarak vars.tasks.<resultKey> altına yazar (yoksa id snake_case yazılır: ik-kaydi → vars.tasks.ik_kaydi; eski ifadeler için kebab-case id altına da yazılır). Her user-task düğümüne snake_case verin (teslimat_kontrol) — kebab-case/slug değil: tire kabul edilmez, aynı tipte benzersizdir. Geçit vars.tasks.teslimat_kontrol.action === "sorunlu" diye okur; yazılmamış bir ad (vars.tasks.teslimatKontrol) undefined döner', form: '{ dtoId } görev formunun DTO kimliği (dto_list) — { pageId } sayfa formu, { formKey } dış form anahtarı. SLUG KABUL EDİLMEZ: dtoSlug yazmak sessizce düşer (BPM-021)', assignee: 'atama sözlüğü (ör. "subject-field:atanan_id", "role:finans")', mode: 'claim (varsayılan) | individual', join: 'all | any | force-complete (individual)', actions: '[{ id, label, completes, emits, evidence }]', completeOn: '{ trigger: row-event, operation, condition }', sla: '"P2D" ya da [{ name, duration, onBreach }]', requestInfo: 'bool', allowReassign: 'bool', external: 'bool — dış kullanıcı görevi', revisionBound: 'bool — bölge yeniden başlarsa eski görev superseded', undoWindow: 'ISO süre', assessment: '{ questionBank { entity, where, questionField, optionsField, answerField }, count, passScore (1..count, BPM-020), attempts, random, completeAction } — okuma kanıtından önce sınav' }, example: { id: 'gorevi-yap', type: 'user-task', resultKey: 'gorevi_yap', description: 'Atanan görevi yapar.', assignee: 'subject-field:atanan_id', sla: 'P3D' } },
184
184
  { type: 'approval', purpose: 'Onay görevi ve politika kararı', required: ['id', 'type', 'description', 'assignee'], fields: { ...commonFields, resultKey: 'kararın adı: motor sonucu vars.approvals.<resultKey> altına yazar (yoksa id snake_case yazılır: yonetici-onayi → vars.approvals.yonetici_onayi). Her approval düğümüne snake_case verin (yonetici_onayi) — kebab-case/slug değil: tire kabul edilmez, aynı tipte benzersizdir', form: '{ dtoId } görev formunun DTO kimliği (dto_list) — { pageId } sayfa formu, { formKey } dış form anahtarı. SLUG KABUL EDİLMEZ: dtoSlug yazmak sessizce düşer (BPM-021)', assignee: 'atama sözlüğü', policy: 'sequential | all | any | quorum(n) | majority | weighted(0.6)', rejectPolicy: 'any-reject | quorum(n) | majority', onReject: 'end | back-to:<düğüm> | restart', commentRequired: 'bool ya da Jint ("outcome === \'rejected\'")', selfApproval: 'escalate | skip | allow', weights: '{ "role:x": 2 }', veto: '["role:yk-baskani"]', abstain: 'bool', countAs: 'vars yolu' }, example: { id: 'kontrol-onay', type: 'approval', resultKey: 'kontrol_onay', description: 'Birim yöneticisi onaylar.', assignee: 'unit-manager of subject-field:atanan_id', policy: 'any', onReject: 'back-to:iade' } },
185
- { type: 'service', purpose: 'Workflow, bağlayıcı, dış motor ya da AI çağrısı (yan etkili iş)', required: ['id', 'type', 'description'], fields: { ...commonFields, workflowId: 'workflow GUID — workflowId | workflowSlug | connector | externalEngine | ai alanlarından biri zorunlu', connector: '{ name, operation, credentialRef, input }', externalEngine: '{ engine: camunda8 | camunda7 | flowable, processKey, credentialRef, input }', input: '{ ad: "<Jint>" }', output: 'vars yolu ("vars.seviye")', retry: '{ count, backoff, delay }', runAs: 'system ise caution zorunlu (BPM-005)', ai: '{ operation: summarize | classify | extract | suggest-assignee | suggest-decision, labels, schema, files, input, instructions, autonomy: suggest | act-in-adhoc (BPM-019) } — öneri output yoluna yazılır, karar vermez' }, example: { id: 'seviye-hesapla', type: 'service', description: 'Tutardan onay seviyesini hesaplar.', workflowId: '<guid>', input: { tutar: 'row.tutar' }, output: 'vars.seviye' } },
185
+ { type: 'service', purpose: 'Workflow, bağlayıcı, dış motor ya da AI çağrısı (yan etkili iş)', required: ['id', 'type', 'description'], fields: { ...commonFields, workflowId: 'workflow GUID — workflowId | connector | externalEngine | ai alanlarından biri zorunlu; yalnız workflowSlug yazılan düğüm DefinitionInvalid alır (slug değişebilir), yanıt workflowId ve workflowSlug alanlarını birlikte döner', connector: '{ name, operation, credentialRef, input }', externalEngine: '{ engine: camunda8 | camunda7 | flowable, processKey, credentialRef, input }', input: '{ ad: "<Jint>" }', output: 'vars yolu ("vars.seviye")', retry: '{ count, backoff, delay }', runAs: 'system ise caution zorunlu (BPM-005)', ai: '{ operation: summarize | classify | extract | suggest-assignee | suggest-decision, labels, schema, files, input, instructions, autonomy: suggest | act-in-adhoc (BPM-019) } — öneri output yoluna yazılır, karar vermez' }, example: { id: 'seviye-hesapla', type: 'service', description: 'Tutardan onay seviyesini hesaplar.', workflowId: '<guid>', input: { tutar: 'row.tutar' }, output: 'vars.seviye' } },
186
186
  { type: 'notify', purpose: 'Bildirim (kutu, e-posta outbox)', required: ['id', 'type', 'description', 'to'], fields: { ...commonFields, to: 'atama sözlüğü', channels: '["inbox", "email"]', template: '{ subject, body } — {{row.x}} yer tutucuları', ackRequired: 'bool' }, example: { id: 'bildir', type: 'notify', description: 'Yöneticiye bildirim.', to: 'unit-manager of subject-field:atanan_id', channels: ['inbox'] } },
187
187
  { type: 'gateway', purpose: 'Dallanma ve birleşme', required: ['id', 'type', 'description', 'kind'], fields: { ...commonFields, kind: 'exclusive | parallel | inclusive (koşulu tutan her dal; birleşmede hâlâ ulaşabilecek dallar beklenir) | event-based (yalnız wait/timer düğümlerine gider; ilk gelen olay kazanır, diğer bekleyişler iptal)', default: 'exclusive/inclusive için hiçbir koşul tutmazsa gidilen hedef' }, example: { id: 'butce-karari', type: 'gateway', kind: 'exclusive', description: 'Bütçe uygun mu?', default: 'reddedildi' } },
188
188
  { type: 'timer', purpose: 'Süre ya da tarih bekleme', required: ['id', 'type', 'description'], fields: { ...commonFields, duration: 'ISO süre', until: 'Jint tarih ifadesi', calendar: 'iş takvimi slug' }, example: { id: 'bekle', type: 'timer', description: 'Bir gün bekler.', duration: 'P1D' } },
@@ -2343,7 +2343,7 @@ export const policyRules = [
2343
2343
  id: 'FLOW-028',
2344
2344
  module: 'flow',
2345
2345
  appliesTo: ['create', 'update'],
2346
- sourceRef: "Yetki şablonu (access.json 1.7.0) 33 kod adımında platformun oturum uçlarına ham fetch atıyordu ('/api/' + project + '/runtime/' + application + '/auth/me' ve '/auth/sign-in'). Ham istek ng-apps-flow/src/api/instance.ts'in yaptıklarını atlar: X-Environment çözümü (resolveRuntimeEnvironment; elle ayrıştırma /preview şeridinde index -1 verir), CSRF başlığı (çerezli POST 403 CsrfTokenMismatchException — S2-37 · S3-7), 401'de tek refresh + yeniden deneme, ve ExecuteMicroserviceBehavior'ın microservice.execute kararı ile gölge günlüğü. Düzeltme 2026-09-22: giriş ve oturum bilgisi access.session.create / access.session.get system operation'larına taşındı (backend ADR 0004). Dış API'ye fetch meşrudur; kural yalnız göreli platform yollarını (/api/, /m/, /runtime/) görür",
2346
+ sourceRef: "Yetki şablonu (access.json 1.7.0) 33 kod adımında platformun oturum uçlarına ham fetch atıyordu ('/api/' + project + '/runtime/' + application + '/auth/me' ve '/auth/sign-in'). Ham istek ng-apps-flow/src/api/instance.ts'in yaptıklarını atlar: X-Environment çözümü (resolveRuntimeEnvironment; elle ayrıştırma /preview şeridinde index -1 verir), CSRF başlığı (çerezli POST 403 CsrfTokenMismatchException — S2-37 · S3-7), 401'de tek refresh + yeniden deneme, ve ExecuteMicroserviceBehavior'ın microservice.execute kararı ile gölge günlüğü. Düzeltme 2026-09-22: giriş ve oturum bilgisi access.session.create / access.session.get system operation'larına taşındı (backend ADR 0004); çıkış 2026-10-01'de access.session.delete ile eklendi. Dış API'ye fetch meşrudur; kural yalnız göreli platform yollarını (/api/, /m/, /runtime/) görür",
2347
2347
  check: definition => flowAllSteps(definition)
2348
2348
  .filter(step => step.type === 'code')
2349
2349
  .flatMap((step) => {
@@ -2353,11 +2353,34 @@ export const policyRules = [
2353
2353
  return [{
2354
2354
  ruleId: 'FLOW-028',
2355
2355
  severity: 'warn',
2356
- message: `'${flowStepLabel(step)}' code adımı platformun kendi ucuna ham istek atıyor. Bu yol runtime'ın istek katmanını atlar: X-Environment elle kurulur, CSRF başlığı yoktur (çerezli POST 403 CsrfTokenMismatchException), 401'de oturum yenilenmez ve istek microservice.execute kararını ve gölge günlüğünü hiç görmez. Platform davranışını çağırmanın yolu microservice adımıdır: karşılığı olan bir system operation varsa (microservice_system_operation_list — giriş için access.session.create, oturumdaki kullanıcı için access.session.get) source:'system' bir uç açın ve akışa type:'microservice' adımı koyun; sonuç Steps.<adım>.data'dadır, kod adımı yalnız onu biçimler.`,
2356
+ message: `'${flowStepLabel(step)}' code adımı platformun kendi ucuna ham istek atıyor. Bu yol runtime'ın istek katmanını atlar: X-Environment elle kurulur, CSRF başlığı yoktur (çerezli POST 403 CsrfTokenMismatchException), 401'de oturum yenilenmez ve istek microservice.execute kararını ve gölge günlüğünü hiç görmez. Platform davranışını çağırmanın yolu microservice adımıdır: karşılığı olan bir system operation varsa (microservice_system_operation_list — giriş için access.session.create, oturumdaki kullanıcı için access.session.get, çıkış için access.session.delete) source:'system' bir uç açın ve akışa type:'microservice' adımı koyun; sonuç Steps.<adım>.data'dadır, kod adımı yalnız onu biçimler.`,
2357
2357
  guide: 'ngapps://guides/page'
2358
2358
  }];
2359
2359
  })
2360
2360
  },
2361
+ {
2362
+ id: 'FLOW-037',
2363
+ module: 'flow',
2364
+ appliesTo: ['create', 'update'],
2365
+ sourceRef: "mobile-qm koşusu 2026-10-01: çıkış akışı oturumu storage adımları (ZY_App_qm/ZY_AppRefresh_qm/ZY_Csrf_qm = '') ve kod adımında cookie.remove ile kapatmaya çalıştı. Platformun oturum çerezleri (AppSessionConstants: ZY_App_ · ZY_AppRefresh_ · ZY_Csrf_) sunucunun HttpOnly Set-Cookie'sidir: web'de document.cookie onları göremez, mobilde akışın cookie/storage deposu Keychain/kv-store'dur ve iOS NSHTTPCookieStorage'a ulaşmaz. Refresh ailesi sunucuda iptal edilmediği için kapat-aç sonrası auth/refresh oturumu yeniden yazdı (kullanıcı girişli açıldı). Çıkış access.session.delete system operation'ıdır (guard 2026-10-01'den beri session: süresi dolmuş jetonda 401, runtime yenileyip tekrarlar; uç isAnonymous:true kalır): aileyi iptal eder, üç çerezi yanıtla siler",
2366
+ check: definition => flowAllSteps(definition)
2367
+ .flatMap((step) => {
2368
+ const args = (step.args ?? {});
2369
+ const name = String(args.name ?? '');
2370
+ const body = String(args.value ?? '');
2371
+ const touchesSessionCookie = (step.type === 'storage' || step.type === 'setcookie')
2372
+ ? /^ZY_(App|AppRefresh|Csrf)_/.test(name)
2373
+ : step.type === 'code' && /\bcookie\s*\.\s*(remove|set)\s*\(/.test(body) && /['"`]ZY_(App|AppRefresh|Csrf)_/.test(body);
2374
+ if (!touchesSessionCookie)
2375
+ return [];
2376
+ return [{
2377
+ ruleId: 'FLOW-037',
2378
+ severity: 'warn',
2379
+ message: `'${flowStepLabel(step)}' adımı platformun oturum çerezine (ZY_App_ · ZY_AppRefresh_ · ZY_Csrf_) akıştan dokunuyor. Bu çerezler sunucunun HttpOnly çerezleridir: akışın cookie/storage deposu onlara ulaşmaz (mobilde Keychain/kv-store, web'de document.cookie) ve refresh ailesi sunucuda açık kaldığı için kullanıcı kapat-aç sonrası yeniden girişli olur. Çıkış için source:'system', systemOperation:'access.session.delete', isAnonymous:true bir uç açın ve akışa type:'microservice' adımı koyun, ardından redirect ile giriş sayfasına gidin — uç refresh ailesini iptal eder ve üç çerezi yanıtla siler. Uygulamanın kendi değerleri (ör. 'beni hatırla' bayrağı) storage'da kalabilir.`,
2380
+ guide: 'ngapps://guides/access'
2381
+ }];
2382
+ })
2383
+ },
2361
2384
  {
2362
2385
  id: 'FLOW-020',
2363
2386
  module: 'flow',
@@ -3258,6 +3281,30 @@ export const policyRules = [
3258
3281
  }];
3259
3282
  }
3260
3283
  },
3284
+ {
3285
+ id: 'MW-007',
3286
+ module: 'workflow',
3287
+ appliesTo: ['create', 'update', 'preview'],
3288
+ sourceRef: "Sayfa middleware'inin variable adımı WorkflowContext değişkenine yazar ve sayfa yanıtı bu değişkenleri Page.variables olarak istemciye taşır (GetPageBySlugQuery → PageAppDto.Map(workflowContext.ClientVariables), önbellek isabetinde ExecutePageBehavior). rule.isPrivate işaretli değişken sunucuda kalır (WorkflowContext.AddPrivateVariable). qm '[SY] Settings Setter' { ...settings.data } ile sso_client_secret'ı istemciye verdi (2026-10-02)",
3289
+ check: (definition, context) => {
3290
+ if (context.workflowKind !== 'middleware')
3291
+ return [];
3292
+ const exposing = workflowSteps(definition).filter(step => {
3293
+ if (step.type !== 'variable' || step.rule?.isPrivate === true)
3294
+ return false;
3295
+ const expression = String(step.rule?.expression ?? '');
3296
+ return /\.\.\.\s*[A-Za-z_$]/.test(expression) || /secret|password|passwd|api_?key|private_?key/i.test(expression);
3297
+ });
3298
+ return exposing.length === 0
3299
+ ? []
3300
+ : [{
3301
+ ruleId: 'MW-007',
3302
+ severity: 'warn',
3303
+ message: `'${exposing.map(stepLabel).join(', ')}' değişkeni bütün bir kaydı yayıyor ya da gizli değer adı taşıyor. Bu middleware bir SAYFAYA bağlanırsa değişken Page.variables ile istemciye gider (tarayıcıda/cihazda okunur). Gizli değer taşıyan değişkene rule.isPrivate: true verin — aynı istekteki sonraki middleware'ler okumaya devam eder, istemciye gitmez (sayfanın akışları istemcide koşar ve Page.variables'ı okur: işaretli değişken orada undefined'dır); istemcinin gerçekten ihtiyaç duyduğu alanları ayrı bir değişkende izin listesiyle kurun: '({ app_name: settings.data.app_name })' (platform_guide('middleware') Tarif 2). Yalnız microservice uçlarına bağlı middleware'de değişken istemciye dönmez.`,
3304
+ guide: 'ngapps://guides/middleware'
3305
+ }];
3306
+ }
3307
+ },
3261
3308
  // Backend doğrulayıcılarının aynası (ENT-015…018, MS-017, WF-026, QRY/VIEW/DTO/ROW/DS/PRJ/TR/FLD/FILE/EXT):
3262
3309
  // tek liste `backendConstraints.ts`'te durur, buradan yalnız politika motoruna katılır.
3263
3310
  ...themePolicyRules,
@@ -182,7 +182,7 @@
182
182
  ]
183
183
  },
184
184
  "settingsSetterVariant": {
185
- "note": "Genel amaçlı setter (qm '[SY] Settings Setter', 3 step): start -> settings [table get, ayarlar tablosu] -> setVar [type:'variable', rule.name:'settings', rule.expression:'{ ...settings.data }', rule.dataTransferObjectId:<ayarlar DTO>]. Response YOK — sadece değişken kurar; bağlandığı her uçta settings.<alan> okunur. Ad öneki ([A]/[SY]) alfabetik koşum sırasını kurmak içindir."
185
+ "note": "Genel amaçlı setter (qm '[SY] Settings Setter'): start -> settings [table get, ayarlar tablosu] -> setPrivate [type:'variable', rule.name:'settingsPrivate', rule.expression:'{ ...settings.data }', rule.dataTransferObjectId:<ayarlar DTO>, rule.isPrivate:true] -> setPublic [type:'variable', rule.name:'settings', rule.expression:'({ app_name: settings.data.app_name })']. Response YOK — sadece değişken kurar; bağlandığı her uçta settings.<alan> okunur. Sayfaya bağlanırsa işaretsiz değişkenler Page.variables ile istemciye gider: gizli değer taşıyan ya da kaydı yayan değişken isPrivate:true olmalı, istemciye gidecek alanlar izin listesiyle kurulur (MW-007). Ad öneki ([A]/[SY]) alfabetik koşum sırasını kurmak içindir."
186
186
  },
187
187
  "roleGateVariant": {
188
188
  "note": "Rol kapısını AYRI bir middleware'e ayırma: kimlik middleware'i set-user'a ULAŞMADAN biterse (çerez yok, koşul else dalı) 'user' hiç tanımlanmaz ve rolü okuyan middleware ham 'user is not defined' ile 500 verir — kullanıcı 401 yerine 500 görür (crm koşumu B-3). Kapıyı bu akışın İÇİNE, setUser'dan SONRA koy. Authorized hedefin rolleri BOŞSA true döndüğü için tek middleware her uca bağlanabilir; kapı yalnız roleIds dolu uçlarda kapanır.",
@@ -350,9 +350,8 @@ hesaplar.
350
350
  olunca değerler yerindedir; o ana kadar yeni kolona filtre 0 satır dönebilir. Kaynak alan o sırada
351
351
  dönüşümdeyse doldurma onu atlar; dönüşüm bitince (`protection-*` `completed`) platform aynı tabloya
352
352
  bir `derived-fill` işi daha açar ve dönüşümün önceden işlediği satırları da doldurur — değerleri o
353
- işin bitişinden sonra okuyun. Yalnız ad değişikliği ve silme iş açmaz. Bilinen eksik:
354
- deployment/merge ya da şablon kurulumuyla hedef ortama gelen türetilmiş alan orada kendiliğinden
355
- dolmaz (platform bulgusu VK-21).
353
+ işin bitişinden sonra okuyun. Yalnız ad değişikliği ve silme iş açmaz. Deployment, merge ya da şablon
354
+ güncellemesiyle hedef ortama gelen türetilmiş alanı da platform hedefte aynı işle doldurur.
356
355
 
357
356
  ## Arama ve sıralama (PROT-001 · PROT-002)
358
357
 
@@ -379,7 +378,8 @@ Kompleks projede kimliği platform yönetir; uygulamanın kendi kimlik middlewar
379
378
 
380
379
  - Jeton önce çerezden, yoksa `Authorization: Bearer`'dan okunur. Çerezle gelen **durum değiştiren**
381
380
  istek `X-ZY-CSRF` başlığında `ZY_Csrf_{proje}` değerini ister (yoksa `403 CsrfTokenMismatch`);
382
- Bearer'lı istek CSRF istemez. Giriş hız sınırı 5 dk'da 10 deneme (429).
381
+ Bearer'lı istek CSRF istemez. Oturum veren yanıt değeri `X-ZY-CSRF` yanıt başlığıyla da döner (mobil
382
+ runtime çerez kavanozunu okuyamaz, değeri buradan alır). Giriş hız sınırı 5 dk'da 10 deneme (429).
383
383
  - Kullanıcı projenin `users` tablosundadır; parola biçimi değişmedi (`Hasher.Hash`).
384
384
  - **Özne:** kullanıcı id'si, pozisyonları, birimleri, şirketleri, rol slug'ları, işlev kodları.
385
385
  **Kompleks projede rol YALNIZ pozisyondan gelir** (2026-09-21, S-15): `users` ekranından verilen
@@ -397,6 +397,20 @@ Kompleks projede kimliği platform yönetir; uygulamanın kendi kimlik middlewar
397
397
  kartı `access.session.get` system microservice'iyle `microservice` adımından çağrılır: runtime
398
398
  `X-Environment`'ı, CSRF'i ve 401'de tek yenilemeyi kendisi yapar, giriş gövdesi jeton taşımaz.
399
399
  Yanlış parola `401 AppSignInFailedException` döner (oturum hatası değildir, yenileme tetiklemez).
400
+ - **Çıkış `access.session.delete`'tir (FLOW-037).** `source: 'system'`, `isAnonymous: true` bir uç
401
+ açın; çıkış akışı `microservice` adımıyla onu çağırır, ardından `redirect` ile giriş sayfasına
402
+ gider. Uç refresh ailesini sunucuda iptal eder ve üç oturum çerezini (`ZY_App_` · `ZY_AppRefresh_` ·
403
+ `ZY_Csrf_`) yanıtla siler. Koruması `session`'dır: erişim jetonu (15 dk) dolmuşsa uç
404
+ `401 AppSessionRequired` döner, runtime (web `/view` ve mobil) oturumu bir kez yeniler ve çıkışı
405
+ tekrarlar — aile ancak böyle bulunur, refresh çerezi uca gitmez. Yenileme düşerse (aile zaten
406
+ kapalı) runtime uygulamanın giriş/açılış sayfasına gider; çıkış akışının `error` dalı da `redirect`
407
+ ile giriş sayfasına bağlanır (FLOW-036). Uç yine `isAnonymous: true` kalır: anonim olmayan uçta
408
+ `microservice.execute` izni sorulur, çıkış her rol için izin isterdi. Akışın
409
+ `cookie.remove` / `storage` / `setcookie` adımları bu çerezlere **ulaşmaz** — HttpOnly'dir; mobilde
410
+ akış deposu Keychain/kv-store'dur, cihazın çerez kavanozu değil. Öyle kurulan çıkış ekranda çalışır
411
+ görünür, ama refresh çerezi (30 gün) kalır ve uygulama kapat-aç sonrası girişli açılır (ölçüldü
412
+ 2026-10-01, `mobile-qm`). Uygulamanın kendi değerleri (ör. "oturumu açık tut" bayrağı) `storage`'da
413
+ kalabilir.
400
414
 
401
415
  ## Zorlama modu
402
416
 
@@ -164,7 +164,8 @@ da yazılır) — yine de açık bir snake_case ad verin (`"resultKey": "grup_on
164
164
  düğümü geri yazar). "Ret satıra durum yazsın" isteyen tasarım, ret hedefini geri yazmayı yapan bir
165
165
  `script` düğümü yapar ve oradan asıl hedefe bir kenar çeker (ölçüldü 2026-09-21, S-6 CAPA).
166
166
 
167
- **service** — workflow çağrısı: `workflowId`, `input: { ad: "<Jint>" }`, `output: "vars.x"`,
167
+ **service** — workflow çağrısı: `workflowId` (zorunlu; yalnız `workflowSlug` yazılan düğüm önizlemede ve yayında
168
+ `DefinitionInvalid` alır — slug değişebilir, bağ kimlikle tutulur; yanıt ikisini birlikte döner), `input: { ad: "<Jint>" }`, `output: "vars.x"`,
168
169
  `retry: { count }`. Başarısızlık `retry.count` (varsayılan 3) sonra örneği `faulted` yapar.
169
170
  `runAs: system` gerekçesi `caution`'da durur (BPM-005). `input` anahtarları workflow'un **parametre DTO
170
171
  alanlarının `slug`'larıdır** (`dto_get` → `children[].slug`); platform onları alan kimliğine bağlar. DTO'da
@@ -210,6 +211,10 @@ nesnesi), `input`, `output`, `binding: latest (varsayılan) | deployed | v:<n>`
210
211
  aktif sürümü okur; sabitlemek için `v:<n>`), `forEach { entity, where: "item.aktif === true",
211
212
  batch }` (partili başlatma, hepsi bitince devam). Alt örnek kuyruktan başlar; üst örnek
212
213
  `sub-process-completed` ile ilerler.
214
+ `v:<n>` yayında **o sürümün kimliğine** bağlanır; numara ortamın yerel sayacıdır ve merge/promote
215
+ ara sürümleri taşımaz. Aktarım bağı hedefte **aynı içerikli** sürüme yeniden yazar (numara değişebilir,
216
+ ör. development `v:3` → production `v:2`); hedefte o içerik yoksa promote 422
217
+ `SubProcessBindingNotCarried` ile durur — önce çağrılan sürümü taşıyın ya da bağı değiştirin.
213
218
 
214
219
  **region** — uyarlanabilir blok ve olay kapsamı: `nodes`, `edges`, `limits { maxSteps, nodeTypes,
215
220
  assigneeRules }`, `on[]`. Girişler iç kenarlarda hedef olmayan düğümlerdir; iç tokenlar bitince dış
@@ -202,7 +202,7 @@ Her şema düğümünde iki sözlük vardır; ikisi de ortak render katmanında
202
202
  | `widthString` / `heightString` | CSS uzunluğu | Sabit ölçü; **yalnız ilgili `*Type: 'fixed'` iken okunur** (aksi PAGE-017 uyarısı). Kabul: çıplak sayı (px sayılır) · `px % vw vh vmin vmax rem em ch` birimli değer · `calc(...)`. Negatif/`pt`/`fr`/`auto` GEÇMEZ — platform kaydeder ama panel ve runtime sessizce düşürür (PAGE-018). Eski `widthPx` alanı 2026-09-01'de kaldırıldı |
203
203
  | `marginType` / `marginValue` | `normal` · `none` · `custom` + px | Dış boşluk: **yazılmazsa `normal` sayılır** = her kenara 4px (`spacing.ts` `CANVAS_NORMAL_MARGIN`); custom=`marginValue`. Bileşenleri dip dibe dizmek için `none` ŞART — `gap:'none'` olsa bile normal margin komşular arasında 8px bırakır (sıkı dizilim tarifi: `platform_guide('page')` Kural 1f-2) |
204
204
  | `paddingType` / `paddingValue` | `normal` · `none` · `custom` + px | İç boşluk: normal=10px. **Yalnız droppable kaplarda ve tipli stil modelindeki yapraklarda (box · text · container · statcard · icon · form) çizilir**; `slot`, kabuk kökleri (`layout`/`layout-shell`) ve `modal`/`drawer`/`auth-layout` hiç almaz — platform 200 döner, ekran değişmez (PAGE-029). Değerler piksel SAYISIDIR: `24`, `"24px"` değil (PAGE-030) |
205
- | `alignType` | `left` · `center` · `right` | İçerik hizalama (destekleyen komponentlerde) |
205
+ | `alignType` | `left` · `center` · `right` | İçerik hizalama (destekleyen komponentlerde). **Flex SÜTUN kabının çocuğunda düğümün kendisini hizalar**: `center`/`right` → `align-self: center`/`flex-end` (genişlik `fill` değilken; kabın `horizontalAlign`ı bütün çocuklar içindir, sağa yaslanacak tek buton böyle anlatılır). `left` bir şey ezmez; satır kabında ana eksen kabındır (2026-10-02 — öncesinde runtime okumuyor, modal gövdesindeki `alignType:'right'` buton solda kalıyordu) |
206
206
  | `aspectRatio` | sayı | En-boy oranı kilidi (medya komponentleri) |
207
207
 
208
208
  `isHidden` bu sözlüklerde değil PROP'tur ve her komponentte çalışır; flow'lar `margin` /
@@ -39,6 +39,16 @@ trail tabloları hariç, dış datasource'lara dokunulmaz). Kayıt taşıması a
39
39
  (`promote`) kuralıdır ve orada aynen geçerlidir. **Satırlar merge'de geri taşınmaz** — merge yalnız
40
40
  tanım işidir.
41
41
 
42
+ Tanım sayılan ve merge'in taşıdığı üç şey 2026-09-25'ten beri listede: **çeviriler**
43
+ (translation kalemi, adı çeviri anahtarı), **microfunction'lar** (kod ve arşiv; hedefte **taslak**
44
+ kalır, yayını kullanıcı yapar) ve zamanlanmış işin **duraklatma durumu**. Öncesinde dalda
45
+ eklenen çeviri ve microfunction development'a hiç gelmiyordu.
46
+
47
+ - Aynı çeviri anahtarını (uygulama, dil, anahtar) dal da development da kendi başına eklediyse plan
48
+ yalnız dalınkini `add` gösterir; apply development'ın satırına **dalın değerini** yazar.
49
+ - Microfunction **runtime**'ı merge'de taşınmaz (kimlik bilgisi taşır). Dalda yeni bir runtime kurup
50
+ microfunction'ı ona bağlarsanız apply `23503` ile düşer — runtime'ı önce development'ta kurun.
51
+
42
52
  Dalda `page_screenshot` **çalışır** (2026-09-09'dan beri); ayrıntısı bir sonraki bölümde.
43
53
 
44
54
  ## Dalda görsel doğrulama
@@ -85,7 +95,7 @@ değişkenler, çözülen ref'ler) kareyi tamamlar: kare ne çizildiğini, bu ik
85
95
  - `derivedFromKey`: kalem OTOMATİK ÜRETİLMİŞ bir kayıtsa (varsayılan DTO) kaynağının anahtarı
86
96
  - `elements[]`: `path`, `kind`, `base`, `source` (dal), `target` (development), `resolution`
87
97
 
88
- `dataRisk` bir **uyarıdır, apply'ı bloklamaz**; üç sebepten biriyle doğrudur:
98
+ `dataRisk` bir **uyarıdır, apply'ı bloklamaz**; dört sebepten biriyle doğrudur:
89
99
 
90
100
  | `dataRiskReasons` | Ne demek |
91
101
  |---|---|
@@ -93,8 +103,32 @@ değişkenler, çözülen ref'ler) kareyi tamamlar: kare ne çizildiğini, bu ik
93
103
  | `field-narrowed` | Tip / `nullable` / `unique` daraltılıyor |
94
104
  | `not-null-without-default-on-populated-table` | **Hedefte satır varken** varsayılansız zorunlu alan ekleniyor — bu apply'ı DDL aşamasında düşürür. Alanı `nullable` yapın ya da `defaultValueType: "default-value"` + `defaultValue` verin |
95
105
 
96
- Son satır 2026-09-09'da eklendi; öncesinde bu durum `dataRisk: 0` diyordu ve risk ancak apply
97
- denenerek öğreniliyordu. Hedef tablo **boşsa** aynı alan riskli sayılmaz.
106
+ | `table-removed` | **Hedefte satırı olan** tablo siliniyor; boş tablo riskli sayılmaz |
107
+
108
+ `not-null-…` satırı 2026-09-09'da eklendi; öncesinde bu durum `dataRisk: 0` diyordu ve risk ancak
109
+ apply denenerek öğreniliyordu. Hedef tablo **boşsa** aynı alan riskli sayılmaz.
110
+
111
+ Aynı denetimin sert hâli promote'tadır: deploy başlamadan production verisine karşı koşar ve veri
112
+ bozacak değişikliği 400 ile reddeder (`deployment_start` açıklaması).
113
+
114
+ ### Silinen kaydın yeni bağımlısı: `dependents[]`
115
+
116
+ Dal bir kaydı sildi, development ise dal açıldıktan **sonra** o kaydı kullanmaya başladıysa
117
+ (bir sayfa sorguyu bağladı, bir akış servisi çağırdı) silme `delete` olarak değil **`conflict`**
118
+ olarak gelir ve kalemde `dependents[]` (`{ id, name, module }`) dolar:
119
+
120
+ - `choice: "target"` kaydı ve bağımlısını korur,
121
+ - `choice: "source"` siler — bağımlı FK ile düşebilir ya da kırık bağla kalır.
122
+
123
+ Sorgunun çıktı DTO'su ve alanları **aynı çakışma grubundadır**: biri çözülünce diğerleri de o
124
+ tarafa gider. Dal açılmadan önce var olan ve development'ta değişmeyen bağımlı çakışma üretmez —
125
+ onu dal zaten görmüştü.
126
+
127
+ ### Süreç kenarlarında çakışma
128
+
129
+ `versions[…].model.edges` listesinde iki taraf **aynı geçidin** çıkışlarına dokunduysa tek bir
130
+ çakışma gelir, yolu `versions[id=…].model.edges{from=<düğüm>}` biçimindedir — o geçidin bütün
131
+ çıkışları bir karar olarak çözülür. Farklı geçitlere yapılan değişiklikler kendiliğinden birleşir.
98
132
 
99
133
  `derivedFromKey` dolu olan kalemi ayrıca çözmeye çalışmayın: kaynağını (entity ya da query)
100
134
  çözdüğünüzde aynı yoldaki çakışması **kendiliğinden** aynı tarafa çözülür. Entity adını değiştiren
@@ -884,6 +884,12 @@ Okuyan bir uç prop'a **akışsız** bağlanabilir (`connect_data_bind`, `platfo
884
884
 
885
885
  - Uç **okur** (GET tablo `get`, query ya da view kaynağı), **middleware'siz** ve **dönüş şeması** vardır; yazan tablo/sistem
886
886
  ucu, middleware'li uç ve şemasız uç `connect_data_describe`'da 422 `ConnectDataSourceUnsupported`.
887
+ **Middleware'li uç bilinçli olarak dışarıda (karar 2026-10-02, qm madde 5C):** connectData yolu ucun middleware
888
+ zincirini koşmaz; kaynak olarak kabul edilseydi ucun korumaları (kimlik, rol, setter değişkenleri) atlanırdı.
889
+ Korumalı ucu listeye bağlamanın yolu **akıştır**: bileşenin `mount` (ve arama/sayfa) akışında microservice adımı
890
+ ucu çağırır — middleware'ler koşar —, setter dönen satırları `rows`'a yazar. Uç projedeki başka bir uygulamada
891
+ olabilir (microservice adımı servisin kendi uygulamasının adresine gider). Middleware'i kaldırıp ucu açmak çözüm
892
+ değildir.
887
893
  - **Tablo ucunda `enableDynamicFilters: true`** alanları `canFilter`/`sortable` yapar (kullanıcı filtresi tanım filtresiyle
888
894
  AND'lenir); `sort`/`limit` query-string parametreleri offset sayfalama açar. Dinamik filtresiz ya da query kaynaklı uç
889
895
  `pageMode: client` kalır (CD-007) ve süzmez.
@@ -68,7 +68,12 @@ cevabı adımın ifadesinde gizlidir. Ayrıntı ve yazım ölçütü: `platform_
68
68
  4. **`input` YOKTUR.** Middleware girdisini HTTP bağlamından okur: `cookie`, `header`,
69
69
  `queryString`, `body`, `triggerSource` (hangi microservice/sayfa tetikledi: type, id, name,
70
70
  slug, roles, pathAndQuery — emekliye ayrılacak) ve `context` (type, name, roles, path,
71
- **impersonatedUserId**, language). Kimlik devri **yalnız `context.impersonatedUserId`**'dedir;
71
+ **impersonatedUserId**, language). `context.path` isteği yapan sayfanın adresidir
72
+ (`/view|dev|branch/{ortam}/{proje}/{uygulama}/{sayfa}`): mobil runtime onu `X-Page-Path`
73
+ başlığıyla yollar, web'de tarayıcının `Referer`'ından okunur. Doğrudan API çağrısında,
74
+ zamanlanmış/otomasyon koşusunda ve sayfası olmayan istekte **`null`'dır** — `split` gibi bir
75
+ metot çağırmadan önce koru: `(context.path || '').split('/')[1]`. İstemci beyanıdır, yetki kararı
76
+ için okunmaz. Kimlik devri **yalnız `context.impersonatedUserId`**'dedir;
72
77
  `triggerSource.impersonatedUserId` diye bir alan yoktur, hep `undefined` okunur (MW-006).
73
78
  `context.impersonatedUserId` ve start adımının `impersonation` dalı yalnız Studio önizlemesinde
74
79
  seçilen uygulama kullanıcısı için dolar; kimlik devrini yalnız super-admin yapabilir, anonim ya da
@@ -276,12 +281,29 @@ kurulur.** Her projenin ayar middleware'ine ihtiyacı yoktur; okunmayan bir `set
276
281
  sadece fazladan parça demektir. Karar tablosu: `platform_guide('getting-started')`.
277
282
 
278
283
  ```
279
- start → settings [table get: ayarlar tablosu] → setVar [variable name:'settings',
280
- expression: '{ ...settings.data }', dataTransferObjectId: <ayarlar DTO>]
284
+ start → settings [table get: ayarlar tablosu]
285
+ → setPrivate [variable name:'settingsPrivate', expression: '{ ...settings.data }',
286
+ dataTransferObjectId: <ayarlar DTO>, isPrivate: true]
287
+ → setPublic [variable name:'settings',
288
+ expression: '({ app_name: settings.data.app_name, theme: settings.data.theme })']
281
289
  ```
282
290
 
283
291
  Response yok — tek işi değişken kurmak; bağlandığı her uçta `settings.<alan>` okunur.
284
292
 
293
+ **Sayfaya bağlanan middleware'in değişkeni istemciye gider.** Sayfa yanıtı middleware
294
+ değişkenlerini `Page.variables` olarak taşır; tarayıcıda ve cihazda okunur. Bu yüzden:
295
+
296
+ - Gizli değer taşıyan (şifre, `client_secret`, API anahtarı) ya da bütün bir kaydı yayan değişkene
297
+ **`rule.isPrivate: true`** ver (Studio: "Sunucuda tut"). Aynı istekteki sonraki middleware'ler
298
+ onu okumaya devam eder; istemciye gitmez. Sayfanın akışları istemcide koşar ve `Page.variables`'ı
299
+ okur — işaretli değişken orada `undefined`'dır; akışın ihtiyaç duyduğu alanı işaretleme.
300
+ - İstemcinin gerçekten ihtiyaç duyduğu alanları ayrı, işaretsiz bir değişkende **izin listesiyle**
301
+ kur (yukarıdaki `settings`). Aynı ad sonra işaretsiz bir adımla yeniden yazılırsa istemciye açılır.
302
+ - Yalnız microservice uçlarına bağlı middleware'de değişken yanıtla dönmez; işaret yine de zararsızdır.
303
+ - Lint: `MW-007` (işaretsiz değişken bir kaydı yayıyor ya da gizli değer adı taşıyor → warn).
304
+ Ölçüm: qm `[SY] Settings Setter` `{ ...settings.data }` ile `sso_client_secret`'ı istemciye
305
+ veriyordu (2026-10-02).
306
+
285
307
  ## Tarif 3 — Kimlik kapsaması: "sadece benim kayıtlarım" ucu
286
308
 
287
309
  Kullanıcıya özel veri dönen uçlarda kimliği **istemci parametresinden alma** (sahtelenir);
@@ -400,7 +400,7 @@ Motorun kendi adımları (`setter`, `condition`, `microservice`, `code`, `functi
400
400
  | `confirm` | `yes` / `no` dalları |
401
401
  | `download` | adres cihazın açma sayfasında açılır; ikili gövde (JSON değil) önbelleğe dosya olarak yazılıp paylaşım sayfasında (`expo-sharing`) açılır, Studio mobil önizlemesi dosyayı indirir (`MOB-005`, bilgi) |
402
402
  | `storage` | `args.name` `state.page.<key>` / `state.app.<key>` ise o state'i yazar (§Kalıcı state), değilse cihaz deposuna |
403
- | `setcookie` | cihaz deposuna yazar (`MOB-005`, bilgi); Studio paleti mobilde de sunar |
403
+ | `setcookie` | güvenli cihaz deposuna (SecureStore) yazar; değer sonraki her istekte `X-Flow-Cookie` başlığıyla gider ve middleware/workflow onu web'deki gibi `cookie.<ad>` olarak okur — `Cookie` başlığıyla gitmez, çünkü iOS onu yerel çerez kavanozuyla virgülle birleştirip bozuyordu (`MOB-005`, bilgi); aynı adlı sunucu çerezi varsa o kazanır. Studio paleti mobilde de sunar |
404
404
  | `share` | RN paylaşım sayfası (metin + bağlantı; Android'de bağlantı metne eklenir, `title` iOS'ta e-posta konusu, Android'de seçici başlığı); akış `always` ile sürer — iOS'ta sayfa kapanınca, Android'de seçici açılır açılmaz (RN kapanışı bildirmez). Argümanlar `platform_guide('page')` §`share` adımı |
405
405
  | `haptic` | dokunsal geri bildirim (`expo-haptics`, izin istemez). `args.value` stil, **düz metin**: `success` · `warning` · `error` · `light` · `medium` · `heavy` · `selection`; boş → `medium`. Değer yazmaz, hep `always`. Web'de titreşim varsa kısa titreşim, yoksa hiçbir şey (paletten yalnız mobilde sunulur) |
406
406
  | `location` | cihaz konumu (`expo-location`; ilk kullanımda izin sorulur). `args.value` doğruluk, düz metin: `low` · `balanced` · `high` (boş → `balanced`); `args.isRaw: true` → adres çözülmez. Sonuç `Steps.<adım>.data = { latitude, longitude, accuracy, address }`, `address = { country, region, subregion, city, district, street, postalCode }` ya da `null` (Türkiye'de il `region`, ilçe `subregion`, yoksa `city`). İzin reddi `Steps.<adım>.error = { status: 'denied', message }`, okunamayan konum `unavailable`: adımın `error` dalı bağlıysa o koşar (`always` koşmaz), yoksa akışın hata akışı, o da yoksa `always` (`data: null`). Web'de tarayıcı konumu, adres hep `null` |
@@ -679,7 +679,9 @@ istemcide: `platform_guide({ resource: 'ngapps://examples/<ad>' })`.
679
679
  Kayıt yoksa bileşen bağlanamaz (CD-001).
680
680
  2. `connect_data_sources` → kaynak listesi: `entity` · `query` (yalnız okuyan) · `view` · `workflow` · `microservice`.
681
681
  Entity dışı kaynak yazmaz. Query, view ve workflow tanımı filtre/sıralama/searchable taşımaz (CD-013); microservice
682
- yalnız dinamik filtreli tablo ucunda süzer ve parametreli sıralamada sıralar (describe `canFilter`/`sortable`, CD-002).
682
+ yalnız dinamik filtreli tablo ucunda süzer ve aranır, parametreli sıralamada sıralar (describe `canFilter`/`sortable`,
683
+ CD-002). Microservice'te `searchable`'a yalnız `icontains` işleçli korumasız metin alanı konur; tablonun arama metni
684
+ bu alanlar üzerinde OR "içerir" filtresine çevrilip uca dinamik filtre olarak gider.
683
685
  Describe `source.pageMode: client` ise sunucu sayfalamaz, runtime ilk 1000 satırı alıp tarayıcıda süzer (CD-007); view
684
686
  `textSearch`: arama istekteki `search` ile, aramasız liste boş. `isSchemaSampled`: DTO'suz query ilk satırdan okundu.
685
687
  3. `connect_data_describe { source }` → alan başına efektif kabiliyet (`access` · `operators` · `sortable` · `canWrite` ·
@@ -1,6 +1,6 @@
1
1
  {
2
- "hash": "824bc047e324388649db7bab9e15a2b6602fd6a8ef8a51c7b7a6e5ff58bac7fa",
3
- "generatedAt": "2026-09-30T16:50:58.347Z",
2
+ "hash": "1a01b5888212e4382727318ae17c095c5257f26ec53176998c2b62d98132d737",
3
+ "generatedAt": "2026-10-02T09:40:42.805Z",
4
4
  "types": 324,
5
5
  "files": 163,
6
6
  "note": "Şemaları üreten backend kaynaklarının parmak izi. Elle düzenlemeyin: 'npm run generate-schemas' yazar, 'npm run schema:check' denetler.",
@@ -364,6 +364,9 @@
364
364
  "isExpression": {
365
365
  "type": "boolean"
366
366
  },
367
+ "isPrivate": {
368
+ "type": "boolean"
369
+ },
367
370
  "queryOutputType": {
368
371
  "$ref": "#/components/schemas/NgApps.Domain.Contexts.Datastore.Queries.QueryOutputType"
369
372
  },
@@ -302,6 +302,9 @@
302
302
  "isExpression": {
303
303
  "type": "boolean"
304
304
  },
305
+ "isPrivate": {
306
+ "type": "boolean"
307
+ },
305
308
  "queryOutputType": {
306
309
  "$ref": "#/components/schemas/NgApps.Domain.Contexts.Datastore.Queries.QueryOutputType"
307
310
  },
@@ -207,6 +207,9 @@
207
207
  "isExpression": {
208
208
  "type": "boolean"
209
209
  },
210
+ "isPrivate": {
211
+ "type": "boolean"
212
+ },
210
213
  "queryOutputType": {
211
214
  "$ref": "#/components/schemas/NgApps.Domain.Contexts.Datastore.Queries.QueryOutputType"
212
215
  },
@@ -364,6 +364,9 @@
364
364
  "isExpression": {
365
365
  "type": "boolean"
366
366
  },
367
+ "isPrivate": {
368
+ "type": "boolean"
369
+ },
367
370
  "queryOutputType": {
368
371
  "$ref": "#/components/schemas/NgApps.Domain.Contexts.Datastore.Queries.QueryOutputType"
369
372
  },
@@ -1,16 +1,51 @@
1
1
  import { guid, readOnly, writes } from './common.js';
2
2
  import { handle } from '../platform/responses.js';
3
+ // Koşu 42 S-21: promote yayınlanmamış taslağı taşımaz (sözleşme) ve saga bunu hiçbir yerde söylemez.
4
+ // Liste kaynak environment'ın şemasından okunur; okunamazsa deployment yine başlar, uyarı düşer.
5
+ async function readUnpublishedDrafts(platform, projectId, sourceEnvironmentId) {
6
+ const project = await platform.org('GET', `/projects/${projectId}`);
7
+ const slug = project?.data?.slug;
8
+ const environment = project?.data?.environments?.find(entry => entry.id === sourceEnvironmentId)?.name;
9
+ if (!slug || !environment)
10
+ return undefined;
11
+ const drafts = [];
12
+ for (let pageIndex = 0; pageIndex < 50; pageIndex++) {
13
+ const page = await platform.tenant('GET', slug, '/bpm/definitions', { environment, query: { pageIndex, pageLength: 200 } });
14
+ for (const definition of page?.data ?? []) {
15
+ if (definition.hasDraft !== true)
16
+ continue;
17
+ // Taslak numarası 2026-09-25'ten eski platformda gelmez; yeni taslak her zaman en son sürümdür.
18
+ drafts.push({ slug: definition.slug, name: definition.name, draftVersionNumber: definition.draftVersionNumber ?? definition.latestVersionNumber ?? null, activeVersionNumber: definition.activeVersionNumber ?? null });
19
+ }
20
+ if (pageIndex + 1 >= (page?.meta?.totalPage ?? 1))
21
+ break;
22
+ }
23
+ return drafts;
24
+ }
3
25
  export function registerDeploymentTools(server, platform) {
4
26
  const config = platform.config;
5
27
  server.registerTool('deployment_start', {
6
28
  title: 'Deployment başlat',
7
- description: 'Projeyi kaynak environment\'tan hedef environment\'a deploy eder. Asenkron bir saga\'dır: sonuç bu yanıtta bitmez, deployment_status ile izleyin. Environment id\'lerini project_get çıktısından alın. BPM: süreç tanımları ve iş takvimleri taşınır — hedefte farklı aktif model yeni sürüm olarak yayınlanır (production\'da gelişte aktif), hedefin uyarlaması korunur; uyarlama, örnek, görev ve defter taşınmaz; kaynakta olmayan tanım silinir, açık örneği varsa emekliye ayrılır.',
29
+ description: 'Projeyi kaynak environment\'tan hedef environment\'a deploy eder. Asenkron bir saga\'dır: sonuç bu yanıtta bitmez, deployment_status ile izleyin. Environment id\'lerini project_get çıktısından alın. BPM: süreç tanımları ve iş takvimleri taşınır — hedefte farklı aktif model yeni sürüm olarak yayınlanır (production\'da gelişte aktif), hedefin uyarlaması korunur; uyarlama, örnek, görev ve defter taşınmaz; kaynakta olmayan tanım silinir, açık örneği varsa emekliye ayrılır. KAYNAK YALNIZ DEVELOPMENT\'TIR: dal ya da production kaynak verilirse 400 (CantDeployFromBranchException) — dalı önce development\'a merge edin. ÖN DENETİM: deploy başlamadan production verisi denetlenir; veriyi bozacak ya da silecek her değişiklik 400 (DeploymentDataRiskException) ile listelenir ve deploy BAŞLAMAZ — biçim \'tablo.alan: tür (satır)\', türler not-null | unique | field-removed | table-removed | localization-move. Kayıt taşıması \'all\' olan tabloda production development\'ın AYNASIDIR (kaynakta olmayan satır ve çevirisi silinir). Microfunction\'ın yalnız YAYINLANMIŞ kodu taşınır ve hedefte yayınlanır; hiç yayınlanmamış olan hedefte taslak kalır. Zamanlanmış işin duraklatma durumu taşınır. YAYINLANMAMIŞ TASLAK TAŞINMAZ: kaynakta taslağı olan süreçler yanıtta unpublishedDrafts ({ slug, name, draftVersionNumber, activeVersionNumber }) ve note ile listelenir; hedefte aktif sürüm kalır. Taşımak için taslağı kaynakta yayınlayın (requireGreenScenarios açıksa önce senaryoları taslak üzerinde koşun), sonra yeniden deploy edin.',
8
30
  inputSchema: { projectId: guid, sourceEnvironmentId: guid, targetEnvironmentId: guid },
9
31
  annotations: writes
10
- }, handle(config, ({ projectId, ...body }) => platform.org('POST', `/deployments/${projectId}/deploy`, { body })));
32
+ }, handle(config, async ({ projectId, ...body }) => {
33
+ const unpublishedDrafts = await readUnpublishedDrafts(platform, projectId, body.sourceEnvironmentId).catch(() => undefined);
34
+ const payload = await platform.org('POST', `/deployments/${projectId}/deploy`, { body });
35
+ if (!unpublishedDrafts?.length || !payload || typeof payload !== 'object')
36
+ return payload;
37
+ const listed = unpublishedDrafts
38
+ .map(draft => `${String(draft.slug)} (taslak v${String(draft.draftVersionNumber)}, aktif ${draft.activeVersionNumber === null ? 'yok' : `v${String(draft.activeVersionNumber)}`})`)
39
+ .join(', ');
40
+ return {
41
+ ...payload,
42
+ unpublishedDrafts,
43
+ note: `Kaynakta yayınlanmamış taslağı olan ${unpublishedDrafts.length} süreç var; promote taslak taşımaz, hedefte aktif sürüm kalır: ${listed}. Taşımak için taslağı kaynakta yayınlayın (requireGreenScenarios açıksa önce senaryoları taslak üzerinde koşun), sonra yeniden deploy edin.`
44
+ };
45
+ }));
11
46
  server.registerTool('deployment_status', {
12
47
  title: 'Deployment durumu',
13
- description: "Projenin son deployment durumunu döner. 'projectId' ZORUNLUDUR (project_list/project_get'ten alın) — verilmezse çağrı MCP şema katmanında reddedilir, platforma hiç gitmez. Hiç deployment yapılmamış projede yanıt BOŞ gelir; o durumda tool 'hiç deployment kaydı yok' bilgisini açıkça döner, yani boş yanıtı 'tool çalışmadı' diye okumayın. Durum satırı modül başına ayrılmaz: süreç tanımları ve iş takvimleri sayfa ve workflow'lardan sonra aynı saga adımında aktarılır; hedefte çalışamayan (eksik workflow/entity/rol) tanım aktarımı DefinitionInvalid ile düşürür.",
48
+ description: "Projenin son deployment durumunu döner. 'projectId' ZORUNLUDUR (project_list/project_get'ten alın) — verilmezse çağrı MCP şema katmanında reddedilir, platforma hiç gitmez. Hiç deployment yapılmamış projede yanıt BOŞ gelir; o durumda tool 'hiç deployment kaydı yok' bilgisini açıkça döner, yani boş yanıtı 'tool çalışmadı' diye okumayın. Durum satırı modül başına ayrılmaz: süreç tanımları ve iş takvimleri sayfa ve workflow'lardan sonra aynı saga adımında aktarılır; hedefte çalışamayan (eksik workflow/entity/rol) tanım aktarımı DefinitionInvalid ile düşürür. type 'error' ise 'error' alanı düşüş nedenini taşır (DefinitionInvalid'de bulgular 'yol: mesaj | …' olarak eklenir); aktarım atomiktir, hedef değişmemiştir.",
14
49
  inputSchema: { projectId: guid },
15
50
  annotations: readOnly
16
51
  }, handle(config, async ({ projectId }) => {
@@ -55,7 +55,7 @@ export function registerMergeTools(server, platform) {
55
55
  }, handle(config, ({ projectId, ...query }) => platform.org('GET', `/merges/${projectId}`, { query })));
56
56
  server.registerTool('merge_get', {
57
57
  title: 'Merge planını oku',
58
- description: "Bir merge planını döner. items[] yalnız DEĞİŞEN kayıtları taşır; her kayıtta kind (add|update|delete|conflict) ve elements[] vardır. Çakışan elemanlarda base/source/target üç değeri birden gelir. dataRisk: true olan kalemde SEBEP dataRiskReasons[] içindedir: 'field-removed' (alan siliniyor), 'field-narrowed' (tip/nullable/unique daraltılıyor), 'not-null-without-default-on-populated-table' (hedefte satır varken varsayılansız NOT NULL alan ekleniyor — bu apply'ı DDL aşamasında düşürür, alanı nullable yapın ya da varsayılan verin). dataRisk bir UYARIDIR, apply'ı bloklamaz. derivedFromKey dolu olan kalem OTOMATİK ÜRETİLMİŞ bir kayıttır (varsayılan DTO); kaynağını (entity ya da query) çözdüğünüzde aynı yoldaki çakışması aynı tarafa KENDİLİĞİNDEN çözülür — ayrı ayrı ve zıt çözülemez. Uygulanmış planlarda title, description, createdBy ve appliedBy da gelir. kind=revert ise bu bir geri alma planıdır: revertsPlanId geri aldığı planı verir ve elements'te source merge ÖNCESİ, target ŞİMDİKİ değerdir. isStale: true ise plan alındığından beri DAL ya da development değişmiştir: items o değişikliği taşimaz ve merge_apply 409 döner — merge_refresh ile planı tazeleyin. elements[].isNoise: true olan eleman GÖSTERİM gürültüsüdür — kanvasın kendi durumu (workflow schema), DTO'dan türetilen şema (microservice schema.parameterScheme/returnScheme), sunucu yönetimli anahtar; merge gövdesinde KALIR ve uygulanır, yalnız okurken atlanır. Çakışan eleman asla işaretlenmez. items[].references[] o kalemin gövdesindeki guid'lerin karşılığıdır ({ id, name, module }): bir table step'in rule.entityId'si hangi tabloya, bir branch'in nextStepId'si hangi adıma, bir filtrenin fieldId'si hangi alana işaret ediyor — plan dışına çıkmadan okunur. items[].parentKey dolu olan kayıt başka bir kaydın çocuğudur (DTO çocukları, iç içe klasörler); ebeveyniyle birlikte okunur. items[].recordType kaydın modül içindeki türüdür — bir workflow kaydı 'workflow', 'automation' ya da 'middleware' olabilir ve modül üçüne de workflow der. items[].hiddenFields o kaydın GÖVDESİNDE okunmayacak anahtarlardır (aynı politika; eklenen/silinen kayıt tek eleman olarak geldiği için yolun değil anahtarın sorusudur).",
58
+ description: "Bir merge planını döner. items[] yalnız DEĞİŞEN kayıtları taşır; her kayıtta kind (add|update|delete|conflict) ve elements[] vardır. Çakışan elemanlarda base/source/target üç değeri birden gelir. dataRisk: true olan kalemde SEBEP dataRiskReasons[] içindedir: 'field-removed' (alan siliniyor), 'field-narrowed' (tip/nullable/unique daraltılıyor), 'not-null-without-default-on-populated-table' (hedefte satır varken varsayılansız NOT NULL alan ekleniyor — bu apply'ı DDL aşamasında düşürür, alanı nullable yapın ya da varsayılan verin), 'table-removed' (hedefte satırı olan tablo siliniyor). dataRisk bir UYARIDIR, apply'ı bloklamaz. items[].dependents[] ({ id, name, module }) dolu olan kalem, DALIN SİLDİĞİ ama development'ın dal açıldıktan sonra kullanmaya başladığı kayıttır: silme ÇAKIŞMAYA döner; hedef lehine çözüm kaydı ve bağımlısını korur, kaynak lehine çözüm siler (bağımlı FK ile düşebilir). Sorgunun çıktı DTO'su ve alanları aynı çakışma grubuna girer. Süreç kenarlarında (edges) iki taraf AYNI geçidin çıkışlarına dokunduysa yol 'versions[id=…].model.edges{from=<düğüm>}' olan TEK çakışma gelir; farklı geçitler kendiliğinden birleşir. Merge çevirileri (translation, adı 'key') ve microfunction'ları da taşır; microfunction hedefte TASLAK kalır, yayını kullanıcı yapar. derivedFromKey dolu olan kalem OTOMATİK ÜRETİLMİŞ bir kayıttır (varsayılan DTO); kaynağını (entity ya da query) çözdüğünüzde aynı yoldaki çakışması aynı tarafa KENDİLİĞİNDEN çözülür — ayrı ayrı ve zıt çözülemez. Uygulanmış planlarda title, description, createdBy ve appliedBy da gelir. kind=revert ise bu bir geri alma planıdır: revertsPlanId geri aldığı planı verir ve elements'te source merge ÖNCESİ, target ŞİMDİKİ değerdir. isStale: true ise plan alındığından beri DAL ya da development değişmiştir: items o değişikliği taşimaz ve merge_apply 409 döner — merge_refresh ile planı tazeleyin. elements[].isNoise: true olan eleman GÖSTERİM gürültüsüdür — kanvasın kendi durumu (workflow schema), DTO'dan türetilen şema (microservice schema.parameterScheme/returnScheme), sunucu yönetimli anahtar; merge gövdesinde KALIR ve uygulanır, yalnız okurken atlanır. Çakışan eleman asla işaretlenmez. items[].references[] o kalemin gövdesindeki guid'lerin karşılığıdır ({ id, name, module }): bir table step'in rule.entityId'si hangi tabloya, bir branch'in nextStepId'si hangi adıma, bir filtrenin fieldId'si hangi alana işaret ediyor — plan dışına çıkmadan okunur. items[].parentKey dolu olan kayıt başka bir kaydın çocuğudur (DTO çocukları, iç içe klasörler); ebeveyniyle birlikte okunur. items[].recordType kaydın modül içindeki türüdür — bir workflow kaydı 'workflow', 'automation' ya da 'middleware' olabilir ve modül üçüne de workflow der. items[].hiddenFields o kaydın GÖVDESİNDE okunmayacak anahtarlardır (aynı politika; eklenen/silinen kayıt tek eleman olarak geldiği için yolun değil anahtarın sorusudur).",
59
59
  inputSchema: { projectId: guid, planId: guid, view: mergeView },
60
60
  annotations: readOnly
61
61
  }, handle(config, ({ projectId, planId, view }) => platform.org('GET', `/merges/${projectId}/${planId}?view=${view ?? 'conflicts'}`)));
@@ -264,7 +264,7 @@ export function registerMicroserviceTools(server, platform) {
264
264
  const config = platform.config;
265
265
  server.registerTool('microservice_system_operation_list', {
266
266
  title: 'Sistem operasyonlarını listele',
267
- description: "Bir microservice'in systemOperation alanına konabilecek hazır platform operasyonlarının kataloğunu döner (GET /{project}/microservices/system-operations): operasyon adı, karar motorunun soracağı izin, kaynak tipi, beklediği parametreler ve açıklaması. Sistem operasyonlu uç kendi adımlarını yazmaz — platformun hazır davranışını (ör. satır sahibini değiştirme, maskeli alan açma, uygulama girişi) uygulamanın kendi ucu gibi yayınlar. Her satırın 'guard' alanı korumayı söyler: 'permission' → oturum + buradaki permission karara bağlanır; permission BOŞ olan 'permission' satırında izni işlemin çağırdığı servis verir (ADR-0004): süreç çalışma zamanı işlemleri (process.* — görev kutusu ve eylemleri, process.task.sign dahil; örnek, vekalet, devir, bildirim, tercih, rapor) kişiye ve kayda göre karar verir, REST bpm/* ucuyla aynı görünürlüğü ve aynı alan adlarını döner; yalnız process.override.* process.manage ister; 'session' → operasyon izin sormaz, yalnız oturum ister (ör. access.session.get: oturumdaki kullanıcı; uç anonim değilse microservice.execute kararı yine uygulanır); 'anonymous' → oturum da izin de sorulmaz (access.session.create: giriş; girdi yalnız gövdeden okunur, çerezler yalnız application/json istekte yazılır, gövdede jeton dönmez) ve uç isAnonymous:true olmak zorundadır, önizlemede çalışmaz. Süreç ekranı process.* işlemleriyle kurulur, Page.bpm ile değil (BPM-022); kod adımından platform ucuna ham fetch yerine bu uçlar kullanılır (FLOW-028). Katalog sabittir, projeye göre değişmez.",
267
+ description: "Bir microservice'in systemOperation alanına konabilecek hazır platform operasyonlarının kataloğunu döner (GET /{project}/microservices/system-operations): operasyon adı, karar motorunun soracağı izin, kaynak tipi, beklediği parametreler ve açıklaması. Sistem operasyonlu uç kendi adımlarını yazmaz — platformun hazır davranışını (ör. satır sahibini değiştirme, maskeli alan açma, uygulama girişi) uygulamanın kendi ucu gibi yayınlar. Her satırın 'guard' alanı korumayı söyler: 'permission' → oturum + buradaki permission karara bağlanır; permission BOŞ olan 'permission' satırında izni işlemin çağırdığı servis verir (ADR-0004): süreç çalışma zamanı işlemleri (process.* — görev kutusu ve eylemleri, process.task.sign dahil; örnek, vekalet, devir, bildirim, tercih, rapor) kişiye ve kayda göre karar verir, REST bpm/* ucuyla aynı görünürlüğü ve aynı alan adlarını döner; yalnız process.override.* process.manage ister; 'session' → operasyon izin sormaz, yalnız oturum ister (ör. access.session.get: oturumdaki kullanıcı · access.session.delete: çıkış; refresh ailesini iptal eder ve üç oturum çerezini yanıtla siler, erişim jetonu dolmuşsa 401 AppSessionRequired döner ve runtime yenileyip tekrarlar — akışın cookie/storage adımları bu HttpOnly çerezlere ulaşmaz, FLOW-037, uç isAnonymous:true kalır; uç anonim değilse microservice.execute kararı yine uygulanır); 'anonymous' → oturum da izin de sorulmaz (access.session.create: giriş; girdi yalnız gövdeden okunur, çerezler yalnız application/json istekte yazılır, gövdede jeton dönmez) ve uç isAnonymous:true olmak zorundadır, önizlemede çalışmaz. Süreç ekranı process.* işlemleriyle kurulur, Page.bpm ile değil (BPM-022); kod adımından platform ucuna ham fetch yerine bu uçlar kullanılır (FLOW-028). Katalog sabittir, projeye göre değişmez.",
268
268
  inputSchema: { project: projectParam, environment: environmentParam },
269
269
  annotations: readOnly
270
270
  }, handle(config, ({ project, environment }) => platform.tenant('GET', project, '/microservices/system-operations', { environment })));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@appaflytech/zeyrek-ai-mcp",
3
- "version": "0.0.30",
3
+ "version": "0.0.31",
4
4
  "type": "module",
5
5
  "description": "Zeyrek (NgApps) platform APIs as MCP tools — data, logic and Studio design surface in one gateway",
6
6
  "license": "UNLICENSED",
@@ -60,4 +60,4 @@
60
60
  "typescript": "^5.7.2",
61
61
  "vitest": "^3.0.0"
62
62
  }
63
- }
63
+ }