@appaflytech/zeyrek-ai-mcp 0.0.25 → 0.0.27

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.
@@ -358,15 +358,18 @@ export const componentKnowledge = {
358
358
  "SEÇİM İKİ YOLDAN YAZILIR, ikisi de self.selectedIndex'e: (1) satırın HERHANGİ bir yerine tıklama — her tıklamada `select`, satır değiştiyse önce `change`; (2) satırdaki bir çocuğun yaydığı olay (buton click, girdi change/focus…) — satır değiştiyse `change`. Yalnız text/image taşıyan kart da tıklamayla seçilir (2026-09-15'ten önce ancak olay yayan bir çocukla seçilebiliyordu). İSTİSNA: tıklamayı kendisi yutan etkileşimli çocuklar (dropdown-button, collapsible-container, tag-input, tarih seçici, dosya girdileri, tree-view, table) satıra ulaştırmaz — orada select gelmez, seçim yalnız o çocuk olay yayarsa change ile yazılır. Etikete tıklamak (switch/checkbox/radio) tek tıklama sayılır. Satırdaki butona tıklanınca akışların BAŞLAMA sırası: repeater `change` → butonun click akışı → repeater `select` (akışlar asenkron, bitiş sırası garanti değil); butonun akışında <repeater>.selectedIndex zaten o satırdır. Butonun akışı satırı SİLER ya da selectedIndex'e null yazarsa, ardından gelen select eski sırayla yayılır ve selectedIndex'i yeniden yazar — silme/temizleme akışını butona bağla, repeater'ın select'ine iş bağlama.",
359
359
  "SEÇİLİ SATIRIN VERİSİ `selectedItem`'dır (şema prop'u DEĞİL, runtime türetir): `{{ <repeater>.selectedItem.<alan> }}` ve akışta self.selectedItem. selectedIndex yazılmamışken İLK SATIRI (rows[0]) döndürür — 'henüz seçim yok' ile 'ilk satır seçili'yi ayırmak için selectedIndex'e bak (sayı değilse seçim yok). rows yenilenince selectedIndex SIFIRLANMAZ; eski sıra yeni listede başka satırı gösterebilir, gerekiyorsa sorgu akışında selectedIndex'i null yaz.",
360
360
  "rows dizi DEĞİLSE (henüz koşmamış sorgu, null yazan akış) liste boş çizilir, sayfa düşmez; aynı _id/id'yi taşıyan satırlar da çizilir AMA çocuk girdilerin satır başına değerleri o kimliğe göre tutulduğu için bu satırlar yazılan değeri PAYLAŞIR — girdi taşıyan kartlarda id'ler benzersiz olmalı. Boş liste için ayrı bir emptystate bileşeni koy ve isHidden'ını `{{ (<repeater>.rows || []).length > 0 }}` yap — repeater'ın kendi boş durum metni yok.",
361
- "Izgara: columnCount kolon (en az 1), kartlar arası dikey boşluk verticalSpacing (px, yazılmazsa 20), yatay boşluk horizontalSpacing (px, yazılmazsa 0). autoRowSize:false iken bütün satırlar EN UZUN kart kadar yüksek (1fr); true iken her satır kendi içeriği kadar. Boş/null boşluk varsayılana düşer."
361
+ "Izgara: columnCount kolon (en az 1; katalog varsayılanı 5, yazılmamış eski düğüm 1), kartlar arası dikey boşluk verticalSpacing (px; katalog varsayılanı 12, yazılmazsa 20), yatay boşluk horizontalSpacing (px; katalog varsayılanı 12, yazılmazsa 0). autoRowSize:false iken bütün satırlar EN UZUN kart kadar yüksek (1fr); true iken her satır kendi içeriği kadar. Boş/null boşluk varsayılana düşer.",
362
+ "KARTIN İÇİ container ailesiyle AYNI yedi yerleşim prop'uyla dizilir (2026-09-29): layout ('flex' | 'grid', katalog varsayılanı 'flex') + direction/horizontalAlign/verticalAlign/size/gap/wrap — anlamları container kaydındakiyle birebir; TEK FARK direction varsayılanının 'column' olması (kart dikey yığındır). Satır (row) akışında heightType:'fill' yalnız align-self:stretch'tir ve wrap açıkken kısa kart hücreyi doldurmaz — eşit boylu kartlar için column kullan. Her satırın kartı ayrı bir .ng-app__node-children sarmalıdır ve bu düzeni giyer; çocuklar blocks[].position sırasıyla (önce y, sonra x) akar ve flex çocuk sözleşmesini (appearance.widthType/heightType, spacer mode) alır. Kartların KENDİ dizilişi (kaç kolon, aralarındaki boşluk) hâlâ columnCount/verticalSpacing/horizontalSpacing'tir. layout prop'u hiç yazılmamış eski repeater'ın kartı 12 kolon ızgara kalır.",
363
+ "Studio'ya bırakılan YENİ repeater dolu doğar: 10 görselli örnek satır (id, image, title, category, price, description; satırda 5 kart, iki tam sıra) ve tek bir kart şablonu — tema yüzeyli bir container (layout flex, direction column, gap small; appearance widthType/heightType 'fill') içinde image (240px kapak), kategori, başlık (1 satır), açıklama (2 satır), esnek spacer (mode 'flex' — fiyatı ve butonu kartın tabanına iter), fiyat ('{{ item.price }} ₺') ve buton (kind 'info'). Şemayı sen kuruyorsan bu şablon GELMEZ: kartı children olarak yaz; kartın içindeki her parçaya appearance.widthType:'fill' ver — sütun akışında genişliği yazılmamış çocuk 12 kolon payına düşer ve o pay sütunda yükseklik anlamına gelir."
362
364
  ],
363
365
  propNotes: {
364
366
  surfaceStyle: NODE_SURFACE_NOTE,
365
367
  rows: "Yinelenen veri; çocuk komponent ifadelerinde satıra item / Row ile, satırın sırasına RowIndex ile (0'dan başlar) erişilir. Satırlara kalıcı bir id ya da _id ver: girilen değerler ve kart örnekleri ona göre eşlenir.",
366
368
  columns: "YALNIZ Studio'daki Rows düzenleyicisinin alanlarını tanımlar; runtime OKUMAZ. Kartta ne görüneceğini repeater'ın children'ı ve onların `{{ item.<alan> }}` ifadeleri belirler — columns yazmak kartı değiştirmez.",
367
- columnCount: 'Bir satırdaki kart sayısı (tam sayı, en az 1; 0/boş 1 sayılır).',
368
- verticalSpacing: 'Kart satırları arası boşluk, px. Yazılmazsa 20 (şema varsayılanı; 2026-09-15 öncesi yazılmamış düğüm 0 çiziyordu).',
369
- horizontalSpacing: 'Yan yana kartlar arası boşluk, px. Yazılmazsa 0.',
369
+ columnCount: 'Bir satırdaki kart sayısı (tam sayı, en az 1; 0/boş 1 sayılır). Katalog varsayılanı 5 (2026-09-29).',
370
+ verticalSpacing: 'Kart satırları arası boşluk, px. Yazılmazsa 20 (bileşen varsayılanı; 2026-09-15 öncesi yazılmamış düğüm 0 çiziyordu); yeni düğümün katalog varsayılanı 12 (2026-09-29).',
371
+ horizontalSpacing: 'Yan yana kartlar arası boşluk, px. Yazılmazsa 0; katalog varsayılanı 12 (2026-09-29).',
372
+ layout: "KARTIN İÇİ: 'flex' (katalog varsayılanı; akış) | 'grid' (12 kolonluk ızgara). Prop hiç yazılmamışsa renderer IZGARA çizer. Diğer altı yerleşim prop'u yalnız 'flex' iken etkilidir; anlamları container kaydındakiyle aynı.",
370
373
  selectedIndex: "Tıklanan/olay yayan satırın sırası (0'dan). Runtime `change` ve `select` ile yazar; akışta self.selectedIndex, başka bileşende `{{ <repeater>.selectedIndex }}`. Şemada gizli durum prop'udur (panelde çizilmez); akış yazarak seçimi değiştirebilir ya da null yazıp temizleyebilir. Seçili satırın verisi için behavior'daki selectedItem maddesine bak."
371
374
  }
372
375
  },
@@ -1196,9 +1199,9 @@ export const componentKnowledge = {
1196
1199
  }
1197
1200
  },
1198
1201
  navigations: {
1199
- summary: "Çok seviyeli gezinme menüsü. 2026-08-27'de araç kutusuna alındı; 2026-09-22'de TEK AĞAÇ · 12 DÜZEN modeline geçti (layout prop'u): sidebar, header, hybrid (header+sidebar), dual (rail+panel), bottom (alt sekme çubuğu), dock, tabs, breadcrumb, command (komut paleti), launcher, footer, toc (içindekiler). Aynı navigations ağacı hepsinde kullanılır.",
1202
+ summary: "Çok seviyeli gezinme menüsü. 2026-08-27'de araç kutusuna alındı; 2026-09-22'de TEK AĞAÇ · 12 DÜZEN modeline geçti (navLayout prop'u — 2026-09-29'a kadar adı layout idi): sidebar, header, hybrid (header+sidebar), dual (rail+panel), bottom (alt sekme çubuğu), dock, tabs, breadcrumb, command (komut paleti), launcher, footer, toc (içindekiler). Aynı navigations ağacı hepsinde kullanılır.",
1200
1203
  behavior: [
1201
- "layout prop'u hangi KABUĞUN çizeceğini seçer; seçim tipli bir kayıttan yapılır (components/navigations/navigation-layouts/). Tanınmayan ya da temizlenmiş (null) bir değer sidebar'a düşer — ekran boş kalmaz.",
1204
+ "navLayout prop'u hangi KABUĞUN çizeceğini seçer; seçim tipli bir kayıttan yapılır (components/navigations/navigation-layouts/). Tanınmayan ya da temizlenmiş (null) bir değer sidebar'a düşer — ekran boş kalmaz.",
1202
1205
  "variant 'drawer' bir düzen DEĞİL sidebar/hybrid'in menü tipidir ve kendi kabuğunu çağırır (tetikleyici + karartma + örtü katmanı); içindeki satırların düz mü akordiyon mu olduğunu drawerContent söyler.",
1203
1206
  "Derin seviyeler nestingMode ile açılır: 'inline' yerinde ağaç, 'drill' panel içinde panel (drillHeader üç biçimde), 'cascade' sidebar'da kademeli flyout / drawer'da drawer-içinde-drawer. inlineDepth bir panelde yerinde gösterilen seviye sayısıdır.",
1204
1207
  "Bazı prop bileşimlerinin ekranda karşılığı yoktur ve ÇİZİM ANINDA zorlanır (navigation-effective.ts): drawerSide 'bottom' iken cascade → drill ve push → overlay; fullscreen + cascade → üst üste; ≤768px'te sidebar/header/hybrid/dual/dock drawer'a döner (mobileBehavior 'drawer' ise) ve hover ile açma kapanır.",
@@ -1212,13 +1215,15 @@ export const componentKnowledge = {
1212
1215
  "Daraltma durumu tarayıcıda (localStorage, 'ng-nav-collapsed:<elementId>') saklanır ve SONRAKİ SAYFADA okunur — menü linkleri tam sayfa yüklediği için bellekteki durum her tıklamada kayboluyordu. Tuvalde okunmaz.",
1213
1216
  "Menü içi arama bir ALAN gibi çizilir (kenarlık + açık zemin + '/' ipucu); sayfada yazı alanı dışındayken '/' aramaya odaklanır (kısayolun sayfada tek sahibi vardır). command düzeninin tetikleyicisi de alan görünümündedir ve Ctrl K / ⌘ K ipucu taşır; iki yerde de yer tutucu searchPlaceholder'dır.",
1214
1217
  "KAPSAYICIDIR (isDroppable, 2026-09-24): layout gibi içine komponent bırakılır ve bırakılan çocuklar menünün YANINDA/ALTINDA kendi içerik alanında çizilir, menü satırlarının arasında değil. Akış yönünü düzen belirler (navigation-content.ts): sidebar/hybrid/dual menü solda içerik sağda — sidebarSide right ise ters; toc tocPosition ile; header/tabs/breadcrumb içerik altta; footer/bottom ve dock bottom içerik üstte (dock top altta); drawer/command/launcher akışta yer kaplamaz, içerik alanı tek başına kalır. İçerik alanı navigations dizisi BOŞKEN de durur — son menü öğesi silinince bırakılan bileşenler kaybolmaz. İçine hiçbir şey bırakılmamışsa alan akıştan çıkar (menünün yanına boşluk düşmez); kenara yapışık panelde (sidebar/hybrid/dual, floating değil) menü ile içerik arasında boşluk YOKTUR, panelin kenarlığı ayırır. Bırakılan içerik alanın boyunu doldurur (fill zinciri) — yan menü + içerik sütunu kabuğu tek navigations ile kurulabilir. Kanvasta çocukların çizilmesi için kayıtlı düğümde states.isDroppable true olmalı.",
1218
+ "İÇERİK ALANININ YERLEŞİMİ container ailesiyle AYNI yedi prop'tur (2026-09-29): layout ('flex' | 'grid', katalog varsayılanı 'flex') + direction/horizontalAlign/verticalAlign/size/gap/wrap, anlamları ve varsayılanları container kaydındakiyle birebir. Bunlar menüyü DEĞİL, menünün yanına bırakılan çocukları dizer (RenderComponent içerik alanındaki .ng-app__node-children sarmalına yazar). Menünün kendi biçimi navLayout, satır yoğunluğu density'dir. layout prop'u 'flex'/'grid' dışında bir değer taşıyan (ya da hiç taşımayan) düğümün içerik alanı 12 kolon IZGARA çizilir.",
1219
+ "YENİDEN ADLANDIRMA (2026-09-29, ADR 0007): menü düzeni layout → navLayout, satır yoğunluğu size → density. Kayıtlı eski düğümler göç görmedi: navLayout yoksa bileşen eski layout değerini (sidebar/header/… ise) düzen sayar, density yoksa eski size'ı (sm/md/lg ise) — ekranda değişiklik olmaz. AMA backend doğrulayıcısı eski değerli düğüme DOKUNAN patch'i invalid-enum-value ile reddeder (layout artık flex|grid, size shrink|grow): böyle bir düğümü düzenlerken aynı patch'te navLayout'a eski layout değerini, density'ye eski size değerini yaz ve layout'u 'grid' (eski görünüm) ya da 'flex', size'ı 'shrink' yap.",
1215
1220
  "sidebar ve layout bileşenleri bu ağacı GÖMÜLÜ kipte mount eder (embedded): panel kromunu — genişlik, kenarlık, zemin, logo, kullanıcı alanı — o kabuklar çizer, navigations ikinci kez çizmez.",
1216
1221
  "Satır görünümü TİPLİ navigationStyle grubuyla verilir (layout ile aynı grup); serbest style'ın &__item, &__item--active ve &__icon parçaları gruptan SONRA gelir (son söz).",
1217
- 'Karar kaydı: zeyrek-frontend/docs/adr/0001-navigation-duzen-kaydi.md.'
1222
+ 'Karar kayıtları: zeyrek-frontend/docs/adr/0001-navigation-duzen-kaydi.md (düzen kaydı), 0007-navigation-duzen-proplari-yeniden-adlandirildi.md (navLayout/density adları).'
1218
1223
  ],
1219
1224
  propNotes: {
1220
1225
  navigations: "Ağaç: { title, href, icon, isHidden, isExpandable, isExpanded, children } + görünüm alanları { badge, badgeTone, isDisabled, target }. Alan adları kaydedilmiş sayfaların sözleşmesidir ve değişmedi. href düz metin ya da bağlantı seçicisinin nesnesi ({ type:'page', data, ref }) olabilir; ikisini de getHref çözer.",
1221
- layout: "Navigasyonun ekrandaki yeri ve biçimi; 12 seçenek. Kabuk seçimini bu prop yapar — tek istisna, sidebar ve hybrid düzenlerinde drawer menü tipinin kendi kabuğunu çağırmasıdır (bkz. variant).",
1226
+ navLayout: "Navigasyonun ekrandaki yeri ve biçimi; 12 seçenek (2026-09-29'a kadar prop'un adı layout idi). Kabuk seçimini bu prop yapar — tek istisna, sidebar ve hybrid düzenlerinde drawer menü tipinin kendi kabuğunu çağırmasıdır (bkz. variant).",
1222
1227
  variant: "Menü tipi: 'flat' (alt öğeler hep açık), 'accordion' (açılır/kapanır), 'drawer' (tetikleyiciyle açılan panel). Yalnız sidebar ailesinde anlamlıdır.",
1223
1228
  displayMode: "'full' ikon+metin · 'rail' yalnız ikon (alt seviyeler kademeli flyout) · 'text' yalnız metin.",
1224
1229
  nestingMode: "Derin seviyeler nasıl açılır: 'inline' | 'drill' | 'cascade'. Rail'de her zaman cascade'e zorlanır.",
@@ -1296,7 +1301,7 @@ export const componentKnowledge = {
1296
1301
  showFooter: "Panelin alt bandı (kullanıcı alanı vb.). Kapatılırsa liste panelin dibine kadar uzar.",
1297
1302
  showBadges: "Öğelerin badge alanını çizer. Rail modunda rozetin yazacak yeri olmadığı için sayı tek bir NOKTAYA iner.",
1298
1303
  tooltips: "Yalnız ikon görünen satırlarda üzerine gelince adı balonla gösterir. Rail’de ve daraltılabilir panelde anlamlıdır; etiketin zaten göründüğü yerde basılmaz.",
1299
- size: "Yoğunluk kademesi: 'sm' | 'md' | 'lg'. Tek bir prop satır yüksekliğini, yazı boyutunu, satır dolgusunu ve ikon-metin boşluğunu birlikte değiştirir (navigation-tokens.ts ROW_HEIGHTS/FONT_SIZES/ROW_PADDINGS/ROW_GAPS) — bunlar ayrı ayrı ayarlanmaz.",
1304
+ density: "Yoğunluk kademesi: 'sm' | 'md' | 'lg' (2026-09-29'a kadar prop'un adı size idi). Tek bir prop satır yüksekliğini, yazı boyutunu, satır dolgusunu ve ikon-metin boşluğunu birlikte değiştirir (navigation-tokens.ts ROW_HEIGHTS/FONT_SIZES/ROW_PADDINGS/ROW_GAPS) — bunlar ayrı ayrı ayarlanmaz.",
1300
1305
  radius: "Satırın köşe yuvarlaklığı (px, 0–24). SIFIR verildiğinde satırın dış boşluğu da sıfırlanır ve satır panelin kenarına dayanır — yuvarlak köşeli bir satırın kenara yapışması tasarımın istemediği tek durumdur.",
1301
1306
  indent: "Bir seviyenin girintisi (px, 8–48). maxIndent’e kadar her seviye bu kadar içeri alınır, sonrası 12px’lik küçük adımlara döner. Rail’de girinti çizilecek yer yoktur.",
1302
1307
  itemGap: "Satırlar arası dikey boşluk (px, 0–16). Kök listeye ve her çocuk listesine birlikte uygulanır; satır yüksekliğini değiştirmez (onu size belirler).",
@@ -1316,7 +1321,14 @@ export const componentKnowledge = {
1316
1321
  activeBgColor: "Aktif satırın zemini. activeIndicator hangisi olursa olsun geçerlidir — \"bar\"/\"pill\"/\"text\" kendi tonlarını yazar ama AÇIK seçimini ezmez (--ng-nav-active-bg-set).",
1317
1322
  activeTextColor: "Aktif satırın yazı rengi. activeBgColor ile aynı kural: gösterge biçimi bunu ezmez.",
1318
1323
  hoverColor: "Fare üzerindeyken satırın zemini. \"bar\" göstergesi aktif satırın zemini olarak da bunu kullanır.",
1319
- borderColor: "Panel kenarlığı, grup ayraçları ve kılavuz çizgilerinin ortak rengi."
1324
+ borderColor: "Panel kenarlığı, grup ayraçları ve kılavuz çizgilerinin ortak rengi.",
1325
+ layout: "İÇERİK ALANI yerleşimi: 'flex' (katalog varsayılanı; akış) | 'grid' (12 kolonluk ızgara). Menüyü değil bırakılan çocukları dizer; anlamı container kaydındakiyle aynı. Menünün düzeni navLayout'tur.",
1326
+ direction: "İçerik alanı flex yönü: 'row' (varsayılan) | 'column'. Yalnız flex yerleşimde okunur; container ile aynı.",
1327
+ horizontalAlign: 'left | center | right | stretch — içerik alanı; container ile aynı eksen kuralları.',
1328
+ verticalAlign: 'top | middle | bottom | stretch — içerik alanı; container ile aynı eksen kuralları.',
1329
+ size: "İçerik alanı çocuk boyu: 'shrink' (varsayılan) | 'grow'. Satır yoğunluğu DEĞİLDİR — o density'dir (2026-09-29'a kadar size yoğunluktu).",
1330
+ gap: 'none | small | medium (varsayılan) | large ya da ham CSS değeri — içerik alanı çocukları arası; container ile aynı.',
1331
+ wrap: 'İçerik alanında flex-wrap; varsayılan açık, container ile aynı.'
1320
1332
  }
1321
1333
  },
1322
1334
  'layout-shell': {
@@ -1910,7 +1910,7 @@ export const FLEX_LAYOUT_PROP_NAMES = new Set(['layout', ...Object.keys(FLEX_LAY
1910
1910
  * `icon` gibi komponentlerin KENDİ `size` prop'u flex `size`'ı değildir ve dokuz komponent için
1911
1911
  * yanlış uyarı üretiyordu (09-04 Y-08).
1912
1912
  */
1913
- const FLEX_LAYOUT_HOST_TYPES = new Set(['aibox', 'box', 'box-section', 'container', 'page-container', 'form', 'layout', 'widget-area']);
1913
+ const FLEX_LAYOUT_HOST_TYPES = new Set(['aibox', 'box', 'box-section', 'container', 'page-container', 'form', 'layout', 'widget-area', 'repeater', 'navigations']);
1914
1914
  /**
1915
1915
  * Eski `cssgrid` (box/aibox'ın akan gerçek `display:grid` kipi; 2026-09-02'de seçenek olmaktan
1916
1916
  * çıktı, kayıtlı düğümlerde çalışmaya devam ediyor) düzen prop'larının HEPSİNİ ölü yapmaz:
@@ -69,12 +69,21 @@ function carriesValue(value) {
69
69
  return value.trim() !== '';
70
70
  return true;
71
71
  }
72
- function workflowCallSites(body) {
72
+ /**
73
+ * `type` tipindeki adımlar ve `loop` adımının `rule.stepType === type` olan çocuk kuralı.
74
+ * `LoopStep.cs` çocuğu `rule.Children.FirstOrDefault()` ile aynı tipin keyed `IStep`'ine verir:
75
+ * çocuk, üst seviyedeki aynı tip adımla AYNI kuralı okur ve aynı denetimden geçmelidir. İki
76
+ * kullanıcısı var: WF-032 (`workflow`) ve WF-MF-001 (`microfunction`; loop içinde koştuğu
77
+ * ölçüldü 2026-09-28, zeyrek-backend/docs/microfunction/03-workflow-adimi.md §Loop içinde).
78
+ * Etiketteki "(loop çocuğu)" bulguyu okuyan
79
+ * ajanı adımın `rule.stepRule`'una yönlendirir.
80
+ */
81
+ function stepCallSites(body, type) {
73
82
  return workflowSteps(body).flatMap((step) => {
74
83
  const rule = (step.rule ?? {});
75
- if (step.type === 'workflow')
84
+ if (step.type === type)
76
85
  return [{ step, rule, label: stepLabel(step) }];
77
- if (step.type === 'loop' && rule.stepType === 'workflow' && rule.stepRule && typeof rule.stepRule === 'object') {
86
+ if (step.type === 'loop' && rule.stepType === type && rule.stepRule && typeof rule.stepRule === 'object') {
78
87
  return [{ step, rule: rule.stepRule, label: `${stepLabel(step)} (loop çocuğu)` }];
79
88
  }
80
89
  return [];
@@ -713,19 +722,17 @@ export const policyRules = [
713
722
  id: 'WF-MF-001',
714
723
  module: 'workflow',
715
724
  appliesTo: ['create', 'update', 'preview'],
716
- sourceRef: "MicrofunctionStep.cs: rule.MicrofunctionId ile kayıt yüklenir, Status != Published ise FailedOutput(MicrofunctionNotPublishedException) — adım hata dalına düşer, akış 200 dönse bile iş yapılmaz. Ölçüldü 2026-09-10 (B4 doğrulaması: taslak fonksiyona bağlı uç 500 MicrofunctionNotPublishedException)",
717
- check: definition => {
718
- const body = workflowBody(definition);
719
- return workflowSteps(body)
720
- .filter(step => step.type === 'microfunction')
721
- .flatMap((step) => {
722
- const rule = (step.rule ?? {});
725
+ sourceRef: "MicrofunctionStep.cs: rule.MicrofunctionId ile kayıt yüklenir, Status != Published ise FailedOutput(MicrofunctionNotPublishedException) — adım hata dalına düşer, akış 200 dönse bile iş yapılmaz. Ölçüldü 2026-09-10 (B4 doğrulaması: taslak fonksiyona bağlı uç 500 MicrofunctionNotPublishedException). Loop çocuğu (rule.stepType: 'microfunction', rule.stepRule) aynı adımla koşar — ölçüldü 2026-09-28, microfunction-faz-2/development; taslak seçen loop İLK iterasyonda fail dalına düşer ve durur (ölçülen çıktı [{ item: 1 }])",
726
+ check: (definition, context) => {
727
+ const body = workflowBody(definition, context.workflowKind);
728
+ return stepCallSites(body, 'microfunction')
729
+ .flatMap(({ rule, label }) => {
723
730
  const microfunctionId = rule.microfunctionId;
724
731
  if (!hasText(microfunctionId)) {
725
732
  return [{
726
733
  ruleId: 'WF-MF-001',
727
734
  severity: 'block',
728
- message: `'${stepLabel(step)}' microfunction adımında rule.microfunctionId yok — adım hangi fonksiyonu koşturacağını bilmez. microfunction_list(status: 'published') ile id seçin; argümanlar rule.expression'a yazılır (fonksiyonun parameterDto'su varsa o şemaya göre).`,
735
+ message: `'${label}' microfunction adımında rule.microfunctionId yok — adım hangi fonksiyonu koşturacağını bilmez. microfunction_list(status: 'published') ile id seçin; argümanlar rule.expression'a yazılır (fonksiyonun parameterDto'su varsa o şemaya göre).`,
729
736
  guide: 'ngapps://guides/microfunction'
730
737
  }];
731
738
  }
@@ -733,7 +740,7 @@ export const policyRules = [
733
740
  return [{
734
741
  ruleId: 'WF-MF-001',
735
742
  severity: 'block',
736
- message: `'${stepLabel(step)}' adımının rule.microfunctionId'si GUID değil: '${String(microfunctionId)}'. Platform alanı Guid'e çeviremediği için 400 döner.`,
743
+ message: `'${label}' adımının rule.microfunctionId'si GUID değil: '${String(microfunctionId)}'. Platform alanı Guid'e çeviremediği için 400 döner.`,
737
744
  guide: 'ngapps://guides/microfunction'
738
745
  }];
739
746
  }
@@ -746,7 +753,7 @@ export const policyRules = [
746
753
  module: 'workflow',
747
754
  appliesTo: ['create', 'update', 'preview'],
748
755
  sourceRef: "WorkflowStep.cs EvaluateAsync: kural yalnız rule.WorkflowId ve rule.Expression okur; girdi workflow.HasParameter iken Expression'ın DataTransferObjectExpression.Eval'iyle bağlanır, başka hiçbir alan okunmaz. Studio WorkflowTrigger.svelte de yalnız bu iki alanı yazar ve expression'ı çağrılan akışın parameterScheme'inden nesne literali olarak doldurur. RFQ koşusu W-7 (2026-09-14): payloadExpression/customParameters kaydedildi, alt akış girdisiz koştu",
749
- check: (definition, context) => workflowCallSites(workflowBody(definition, context.workflowKind)).flatMap(({ label, rule }) => {
756
+ check: (definition, context) => stepCallSites(workflowBody(definition, context.workflowKind), 'workflow').flatMap(({ label, rule }) => {
750
757
  const workflowId = rule.workflowId;
751
758
  if (!hasText(workflowId)) {
752
759
  return [{
@@ -2206,7 +2213,7 @@ export const policyRules = [
2206
2213
  findings.push({
2207
2214
  ruleId: 'WF-018',
2208
2215
  severity: 'block',
2209
- message: `'${stepLabel(step)}' loop step'inde rule.stepRule yok${hasChildren ? " — ama rule.children dolu: çocuk kural alanının adı 'children' DEĞİL 'stepRule'dur (tekil)" : ''}. Çocuksuz loop çalışma anında NullReferenceException verir ve hata mesajı arrayExpression'ı suçlar. Çocuğun kendi rule gövdesini rule.stepRule'a koyun; loop'un çıktısı [{ item, result }] dizisidir. Geçerli öğeye 'loopItem' ile erişilir AMA yalnız loop'un kendi değerlendirdiği ifadelerde (table çocuğunun fields[].value / filters[].value gibi) — 'code' çocuğunda loopItem TANIMSIZDIR ve adım "loopItem is not defined" ile düşer (ölçüldü 2026-09-16); her öğe için hesabı arrayExpression'da önden yapın.`,
2216
+ message: `'${stepLabel(step)}' loop step'inde rule.stepRule yok${hasChildren ? " — ama rule.children dolu: çocuk kural alanının adı 'children' DEĞİL 'stepRule'dur (tekil)" : ''}. Çocuksuz loop çalışma anında NullReferenceException verir ve hata mesajı arrayExpression'ı suçlar. Çocuğun kendi rule gövdesini rule.stepRule'a koyun; loop'un çıktısı [{ item, result }] dizisidir. Geçerli öğeye 'loopItem' ile erişilir AMA yalnız loop'un kendi değerlendirdiği ifadelerde (table çocuğunun fields[].value / filters[].value, workflow ve microfunction çocuğunun expression'ı gibi) — 'code' çocuğunda loopItem TANIMSIZDIR ve adım "loopItem is not defined" ile düşer (ölçüldü 2026-09-16); her öğe için hesabı arrayExpression'da önden yapın.`,
2210
2217
  guide: 'ngapps://guides/workflow'
2211
2218
  });
2212
2219
  }
@@ -2974,15 +2981,13 @@ export const policyRules = [
2974
2981
  export async function microfunctionStepFindings(platform, project, environment, definition, kind, policyOverride = false) {
2975
2982
  if (platform.config.policyMode === 'off')
2976
2983
  return [];
2977
- const steps = workflowSteps(workflowBody(definition, kind))
2978
- .filter(step => step.type === 'microfunction')
2979
- .filter(step => hasText((step.rule ?? {}).microfunctionId));
2980
- if (steps.length === 0)
2984
+ const sites = stepCallSites(workflowBody(definition, kind), 'microfunction')
2985
+ .filter(site => hasText(site.rule.microfunctionId));
2986
+ if (sites.length === 0)
2981
2987
  return [];
2982
2988
  const records = new Map();
2983
2989
  const findings = [];
2984
- for (const step of steps) {
2985
- const rule = (step.rule ?? {});
2990
+ for (const { rule, label } of sites) {
2986
2991
  const microfunctionId = String(rule.microfunctionId);
2987
2992
  if (!records.has(microfunctionId)) {
2988
2993
  try {
@@ -3000,7 +3005,7 @@ export async function microfunctionStepFindings(platform, project, environment,
3000
3005
  findings.push({
3001
3006
  ruleId: 'WF-MF-001',
3002
3007
  severity: 'block',
3003
- message: `'${stepLabel(step)}' adımının microfunctionId'si (${microfunctionId}) bu ortamda YOK. Adım kuralı step_rules.microfunction_id ile kayda FK'lıdır: gövde gitseydi platform 400 dönerdi. microfunction_list ile bu ortamdaki kayıtları görün — fonksiyon başka bir dalda/ortamda kalmış olabilir.`,
3008
+ message: `'${label}' adımının microfunctionId'si (${microfunctionId}) bu ortamda YOK. Adım kuralı step_rules.microfunction_id ile kayda FK'lıdır: gövde gitseydi platform 400 dönerdi. microfunction_list ile bu ortamdaki kayıtları görün — fonksiyon başka bir dalda/ortamda kalmış olabilir.`,
3004
3009
  guide: 'ngapps://guides/microfunction'
3005
3010
  });
3006
3011
  continue;
@@ -3010,7 +3015,7 @@ export async function microfunctionStepFindings(platform, project, environment,
3010
3015
  findings.push({
3011
3016
  ruleId: 'WF-MF-001',
3012
3017
  severity: 'block',
3013
- message: `'${stepLabel(step)}' adımı '${String(record.name ?? microfunctionId)}' fonksiyonunu seçiyor ama kaydın durumu '${status}'${status === 'publish-failed' && hasText(record.publishError) ? ` (publishError: ${String(record.publishError)})` : ''}. Step YALNIZ yayındaki kaydı koşturur: MicrofunctionStep kaydı yükleyip Status != Published görünce MicrofunctionNotPublishedException ile FAIL dalına düşer — akış kurulur, kaydedilir ve koşumda sessizce hata yoluna girer. Önce microfunction_publish çağırın ve microfunction_get ile 'published' olduğunu görün.`,
3018
+ message: `'${label}' adımı '${String(record.name ?? microfunctionId)}' fonksiyonunu seçiyor ama kaydın durumu '${status}'${status === 'publish-failed' && hasText(record.publishError) ? ` (publishError: ${String(record.publishError)})` : ''}. Step YALNIZ yayındaki kaydı koşturur: MicrofunctionStep kaydı yükleyip Status != Published görünce MicrofunctionNotPublishedException ile FAIL dalına düşer — akış kurulur, kaydedilir ve koşumda sessizce hata yoluna girer. Önce microfunction_publish çağırın ve microfunction_get ile 'published' olduğunu görün.`,
3014
3019
  guide: 'ngapps://guides/microfunction'
3015
3020
  });
3016
3021
  continue;
@@ -3019,7 +3024,7 @@ export async function microfunctionStepFindings(platform, project, environment,
3019
3024
  findings.push({
3020
3025
  ruleId: 'WF-MF-001',
3021
3026
  severity: 'warn',
3022
- message: `'${stepLabel(step)}' adımının fonksiyonu ('${String(record.name ?? microfunctionId)}') şu anda yayınlanıyor. Yayın ASENKRONDUR: bu akış şimdi koşarsa adım fail dalına düşer. microfunction_get ile 'published' olduğunu doğrulayın (yayın başarısız olursa durum 'publish-failed' olur).`,
3027
+ message: `'${label}' adımının fonksiyonu ('${String(record.name ?? microfunctionId)}') şu anda yayınlanıyor. Yayın ASENKRONDUR: bu akış şimdi koşarsa adım fail dalına düşer. microfunction_get ile 'published' olduğunu doğrulayın (yayın başarısız olursa durum 'publish-failed' olur).`,
3023
3028
  guide: 'ngapps://guides/microfunction'
3024
3029
  });
3025
3030
  }
@@ -3035,7 +3040,7 @@ export async function microfunctionStepFindings(platform, project, environment,
3035
3040
  findings.push({
3036
3041
  ruleId: 'WF-MF-001',
3037
3042
  severity: 'warn',
3038
- message: `'${stepLabel(step)}' adımında rule.expression boş ama '${String(record.name ?? microfunctionId)}' fonksiyonunun bir parametre DTO'su var (parameterDtoId: ${String(record.parameterDtoId)}). Fonksiyon args olarak boş nesne alır ve kendi alanlarını undefined görür. Argümanları ifadeyle geçirin — şemayı microfunction_get çıktısındaki parameterScheme verir.`,
3043
+ message: `'${label}' adımında rule.expression boş ama '${String(record.name ?? microfunctionId)}' fonksiyonunun bir parametre DTO'su var (parameterDtoId: ${String(record.parameterDtoId)}). Fonksiyon args olarak boş nesne alır ve kendi alanlarını undefined görür. Argümanları ifadeyle geçirin — şemayı microfunction_get çıktısındaki parameterScheme verir.`,
3039
3044
  guide: 'ngapps://guides/microfunction'
3040
3045
  });
3041
3046
  }
@@ -3061,7 +3066,7 @@ export async function workflowStepFindings(platform, project, environment, appli
3061
3066
  : hasText(definition.applicationId) ? String(definition.applicationId) : undefined;
3062
3067
  if (application === undefined)
3063
3068
  return [];
3064
- const sites = workflowCallSites(workflowBody(definition, kind))
3069
+ const sites = stepCallSites(workflowBody(definition, kind), 'workflow')
3065
3070
  .filter(site => GUID_PATTERN.test(String(site.rule.workflowId ?? '')));
3066
3071
  if (sites.length === 0)
3067
3072
  return [];
@@ -131,6 +131,34 @@ kullanılabilir. Kuralı iki alan taşır:
131
131
  - `workflow_preview` adımı GERÇEK runtime'da koşturur; preview'ın rollback garantisi fonksiyonun
132
132
  dış dünyada yaptığı işi geri ALMAZ.
133
133
 
134
+ ### Loop içinde
135
+
136
+ Loop adımı dizinin her öğesi için fonksiyonu bir kez çağırır. Çocuğun tipi loop'un kendi
137
+ `rule.stepType`'ında, kuralı `rule.stepRule`'dadır (tekil — `children` değil, WF-018):
138
+
139
+ ```jsonc
140
+ { "id": "<GUID>", "name": "her", "type": "loop",
141
+ "rule": {
142
+ "stepType": "microfunction",
143
+ "arrayExpression": "input.kalemler || []",
144
+ "stepRule": { "stepType": "microfunction", "microfunctionId": "<published id>", "expression": "({ x: loopItem.x })" }
145
+ },
146
+ "branches": [ /* success + fail */ ] }
147
+ ```
148
+
149
+ - Çıktı `[{ item, result }]` dizisidir: `result` fonksiyonun o öğedeki dönüşü. Sonraki adımda
150
+ `her.data.map(r => r.result)`.
151
+ - **Öğe fonksiyona yalnız `args` ile gider.** `expression` loop'un motorunda değerlendirilir:
152
+ `loopItem`, `input` ve loop'tan önceki adımlar orada görünür. `context.input` akışın kendi
153
+ girdisidir, iterasyonun öğesi DEĞİL.
154
+ - `context.steps` loop'tan önce koşmuş adımları taşır (2026-09-28'den önce loop içinde boş
155
+ geliyordu; backend düzeltmesi).
156
+ - WF-MF-001 loop çocuğunu da denetler; bulgu adımı `her (loop çocuğu)` diye adlandırır. Taslak bir
157
+ fonksiyonu seçen loop ilk iterasyonda fail dalına düşer ve orada durur.
158
+ - Her iterasyon önceki adımların çıktısını `context.steps` ile yeniden taşır ve bu da
159
+ `MaxPayloadBytes` (5 MB) sınırına sayılır: büyük bir sorgudan sonra koşan loop'ta sınır
160
+ iterasyon başına dolabilir.
161
+
134
162
  Tam gövde örneği: `ngapps://examples/microfunction-node-sum`.
135
163
 
136
164
  ## Built-In ve AWS Lambda farkı
@@ -373,7 +373,7 @@ gidilir, hiçbiri tutmazsa `else` dalı yürür:
373
373
 
374
374
  | Step | workflow | automation | middleware |
375
375
  |------|:-:|:-:|:-:|
376
- | start, condition, code, loop, table, query, http, workflow, email, file-upload | ✓ | ✓ | ✓ |
376
+ | start, condition, code, loop, table, query, http, workflow, microfunction, email, file-upload | ✓ | ✓ | ✓ |
377
377
  | variable (Set Variable), set-user, setcookie, redirect | – | – | ✓ |
378
378
  | response | ✓ | – | ✓ |
379
379
  | convert | ✓ | – | – |
@@ -521,6 +521,7 @@ Tek uçta "başlık + N alt kayıt" yazmanın yolu budur (anket + sorular + seç
521
521
  `fields[].value` / `filters[].value` ifadelerinde `loopItem.<alan>` yazılır (ölçüldü 2026-09-16:
522
522
  `table` çocuğunun filtresinde `loopItem.slug` → `'super-admin'`); `input` ve önceki adımlar
523
523
  (`createSurvey.data.id`) da görünmeye devam eder.
524
+ - **`microfunction` çocuğunda `loopItem` argüman ifadesinde görünür** (`stepRule.expression`, loop'un motorunda değerlendirilir); fonksiyon öğeyi yalnız `args` ile alır. Ayrıntı: `platform_guide('microfunction')` §Workflow adımı → Loop içinde.
524
525
  - **`code` çocuğunda `loopItem` YOKTUR (ölçüm 2026-09-16).** `code` adımı kendi JS motorunu kurar
525
526
  ve `LoopStep`'in `loopItem` ataması oraya ulaşmaz: adım **`Error: loopItem is not defined`** ile
526
527
  düşer, o yinelemenin çıktısında `result` hiç oluşmaz. Motorda görünenler `input` · `system` ·
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@appaflytech/zeyrek-ai-mcp",
3
- "version": "0.0.25",
3
+ "version": "0.0.27",
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",