@appaflytech/zeyrek-ai-mcp 0.0.12 → 0.0.14

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.
@@ -0,0 +1,132 @@
1
+ {
2
+ "_meta": {
3
+ "synthetic": false,
4
+ "note": "loop adımı: bir dizinin her öğesi için TEK bir çocuk adım koşturur. Üç şey tuzaktır ve üçü de ölçüldü: çocuk kuralının alanı TEKİLDİR (rule.stepRule, 'children' değil), çocuğun tipi loop'un kendi rule.stepType alanında yazar, ve geçerli öğeye erişim aracı olan loopItem YALNIZ loop'un kendi değerlendirdiği ifadelerde vardır — code çocuğunda YOKTUR. Lokal stack'te koşturuldu (test projesi, development, 2026-09-16).",
5
+ "harvestedFrom": "canlı ölçüm 2026-09-16 (workflow_preview, test/development) + kod: zeyrek-backend LoopStep.cs:38,45-56,73-77, WorkflowDto.cs:399"
6
+ },
7
+ "_rules": {
8
+ "cocukAlaniTekil": "Çocuğun kural gövdesi rule.stepRule alanına konur — TEKİL. 'children' yazarsan domain tarafı boş kalır, çocuk null kuralla koşar ve loop NullReferenceException verir; hata mesajı yanıltıcı biçimde arrayExpression'ı suçlar (WF-018 lint'i bunu istek gitmeden keser).",
9
+ "cocukTipi": "Çocuğun tipi loop'un KENDİ rule.stepType alanındadır ('table', 'code', 'http'…), stepRule'un içinde değil. stepRule kendi stepType'ını da taşır; ikisini aynı yazın.",
10
+ "dizi": "rule.arrayExpression diziyi üreten bir JS ifadesidir: 'input.kalemler || []'. Boş bırakılamaz (WF-018 block). Dizi değilse ya da null ise döngü koşmaz.",
11
+ "loopItem": "ÖLÇÜLDÜ 2026-09-16 — SINIRLI. Geçerli öğe loopItem adıyla YALNIZ loop'un kendi değerlendirdiği ifadelerde görünür: table çocuğunun filtre ve alan ifadeleri bunu görür (ölçüldü: filtre 'loopItem.slug' → 'super-admin'). AMA code çocuğu kendi JS motorunu kurar ve orada loopItem TANIMSIZDIR: adım 'Error: loopItem is not defined' ile düşer (motorun globalleri input · system · file · crypto · convert · Log · UtcNow …). Her öğe için hesap yapmanız gerekiyorsa hesabı arrayExpression'da önden yapın ya da çocuğu table/http adımı olarak kurun.",
12
+ "filtredeAlias": "Çocuk table adımının filtresi alias TAŞIMAK ZORUNDADIR ('alias': '<entity alias>', 'path': ''). Alias'sız filtre SESSİZCE DÜŞER: ifade doğru değerlendirilir (preview input satırında loopItem.kod'un değeri görünür) ama sorguya uygulanmaz ve her yineleme AYNI ilk satırı döner — hata yoktur, veri yanlıştır. Ölçüldü 2026-09-16: alias'sız iki yineleme de ilk rolü döndü, alias eklenince her yineleme kendi satırını döndü. Aynı tuzak düz table adımında da geçerlidir (platform_guide('workflow') §'Table adımının filtresi').",
13
+ "indeksYok": "İndeks değişkeni YOKTUR — yalnız loopItem vardır. Sıra numarası gerekiyorsa diziyi arrayExpression'da üretin: 'input.kalemler.map((k, i) => ({ ...k, sira: i }))' ve çocukta loopItem.sira kullanın.",
14
+ "cikti": "Çıktı [{ item, result }] dizisidir: item o yinelemenin öğesi, result çocuğun o yinelemedeki çıktısı. İfadede <adım>.data ile okunur; sonuçları düzleştirmek için <adım>.data.map(r => r.result). Bir yineleme düşerse o girişte result HİÇ OLUŞMAZ, yalnız item kalır.",
15
+ "preview": "workflow_preview her yinelemeyi children[] içinde kind:'loopIteration' olarak ayrı ayrı gösterir — hangi öğede düştüğünü oradan görürsünüz."
16
+ },
17
+ "definition": {
18
+ "name": "Kalemleri İşle",
19
+ "slug": "kalemleri-isle",
20
+ "description": "Gelen kalem listesinin her satırı için stok kaydını okur ve sonuçları toplu döner.",
21
+ "type": "workflow",
22
+ "parameterDtoId": "<girdi DTO id: { kalemler: array }>",
23
+ "parameterBehavior": "object",
24
+ "returnDtoId": "<çıktı DTO id: { sonuclar: array }>",
25
+ "returnBehavior": "object",
26
+ "steps": [
27
+ {
28
+ "id": "<GUID-1 üret: crypto.randomUUID()>",
29
+ "name": "Start",
30
+ "type": "start",
31
+ "description": "Akışı başlatır.",
32
+ "branches": [
33
+ {
34
+ "id": "<GUID-2>",
35
+ "name": "start",
36
+ "type": "success",
37
+ "nextStepId": "<GUID-3>"
38
+ },
39
+ {
40
+ "id": "<GUID-9>",
41
+ "name": "global-error",
42
+ "type": "global-error",
43
+ "nextStepId": "<GUID-8>"
44
+ }
45
+ ]
46
+ },
47
+ {
48
+ "id": "<GUID-3>",
49
+ "name": "her_kalem",
50
+ "type": "loop",
51
+ "description": "Her kalem için stok satırını okur; kalem koduna loopItem ile erişir.",
52
+ "caution": "Çocuk adım tek bir tanedir ve her yinelemede ayrı koşar; büyük listede tur sayısı kadar sorgu demektir.",
53
+ "rule": {
54
+ "stepType": "table",
55
+ "arrayExpression": "input.kalemler || []",
56
+ "stepRule": {
57
+ "stepType": "table",
58
+ "entityId": "<stoklar entity id>",
59
+ "datasourceId": "<datasource id>",
60
+ "operation": "get",
61
+ "isSingle": true,
62
+ "alias": "stoklar",
63
+ "selectFields": [
64
+ {
65
+ "alias": "stoklar",
66
+ "fieldSlug": "adet",
67
+ "path": "",
68
+ "fieldId": "<adet alanı id>"
69
+ }
70
+ ],
71
+ "filters": [
72
+ {
73
+ "alias": "stoklar",
74
+ "path": "",
75
+ "fieldSlug": "kod",
76
+ "operator": "eq",
77
+ "value": "loopItem.kod",
78
+ "fieldId": "<kod alanı id>"
79
+ }
80
+ ],
81
+ "joins": []
82
+ }
83
+ },
84
+ "branches": [
85
+ {
86
+ "id": "<GUID-4>",
87
+ "name": "success",
88
+ "type": "success",
89
+ "nextStepId": "<GUID-6>"
90
+ },
91
+ {
92
+ "id": "<GUID-5>",
93
+ "name": "fail",
94
+ "type": "fail",
95
+ "nextStepId": null
96
+ }
97
+ ]
98
+ },
99
+ {
100
+ "id": "<GUID-6>",
101
+ "name": "Response",
102
+ "type": "response",
103
+ "description": "Her kalemin okunan stok sonucunu tek listede 200 ile döner.",
104
+ "rule": {
105
+ "stepType": "response",
106
+ "bodyExpression": "({ sonuclar: her_kalem.data.map(r => ({ kod: r.item.kod, adet: r.result.adet })) })",
107
+ "httpStatusCode": 200
108
+ },
109
+ "branches": []
110
+ },
111
+ {
112
+ "id": "<GUID-8>",
113
+ "name": "onError",
114
+ "type": "response",
115
+ "description": "Akış patlarsa hata kodunu ve mesajını 500 ile döner.",
116
+ "rule": {
117
+ "stepType": "response",
118
+ "bodyExpression": "({ code: error.code, message: error.message })",
119
+ "httpStatusCode": 500
120
+ },
121
+ "branches": []
122
+ }
123
+ ]
124
+ },
125
+ "usage": {
126
+ "olculenCikti": "[{ item: { kod: 'K-1' }, result: { adet: 12 } }, …] — item öğe, result çocuğun çıktısı.",
127
+ "indeksEklemek": "arrayExpression: \"(input.kalemler || []).map((k, i) => ({ ...k, sira: i }))\" ve çocukta loopItem.sira.",
128
+ "codeCocugu": "code çocuğunda loopItem YOKTUR (ölçüldü). Her öğe için hesap gerekiyorsa hesabı arrayExpression'a taşıyın: \"(input.kalemler || []).map(k => ({ ...k, tutar: k.fiyat * k.adet }))\" — böylece çocuk yalnız yazma/okuma yapar ve loopItem.tutar'a bakar.",
129
+ "dogrulama": "workflow_preview; yanıtta loop adımının output'u [{item, result}] dizisi olmalı ve children[] her yineleme için bir kind:'loopIteration' girişi taşımalı. Bir girişte result yoksa o yineleme düşmüştür — runtimeErrors alanına bakın.",
130
+ "aliasTuzagi": "Filtreye alias koymayı unutursanız döngü hatasız koşar ve HER yineleme aynı satırı döner; preview'da fark etmek için children[] içindeki her yinelemenin output'unu karşılaştırın."
131
+ }
132
+ }
@@ -105,9 +105,11 @@ nullable, gateway'den görünmez:
105
105
 
106
106
  **İki farklı anahtar, karıştırma:**
107
107
 
108
- - **id** → `page_create/page_update` ve `microservice_create` `roleIds` alanı (gövdede `roles` diye bir
109
- yazma alanı YOKTUR; `microservice.roles` middleware'de RUNTIME şeklidir, `{ id, name: '<slug>' }`).
110
- `role_list`ten okunur.
108
+ - **id** → `page_create/page_update` ve `microservice_create` `roleIds` alanı; `role_list`ten okunur.
109
+ **Yazma gövdesinde alan adı `roleIds`, okuma şeklinde `roles`** (`{ id, name: '<slug>' }`) — ve
110
+ `microservice_patch` **okuma şeklini** yamalar, yani orada yol `/roles`'tır
111
+ (`platform_guide('microservice')` §"Patch belgesi okuma şeklidir"). `microservice.roles`
112
+ middleware'de de bu runtime şeklidir.
111
113
  - **slug** → `Authorized(user.roles)`, middleware'in kurduğu `user.roles` / `Page.user.roles`.
112
114
  Platformun get-user sorgusu `roles`'u `user_roles` üzerinden **slug dizisi** olarak üretir
113
115
  (`SELECT … ARRAY(SELECT r.slug FROM roles r INNER JOIN user_roles ur …)`), auth middleware
@@ -233,6 +235,38 @@ olduğu için serbest metindir; `course_assignments.semester` (Güz/Bahar/Yaz) e
233
235
  tablodaki değerler `valueOptions` içinde yoksa migration `check constraint ... is violated` ile
234
236
  düşer. Sıra: **önce satırları kanonik değerlere taşı, sonra alanı enum yap.**
235
237
 
238
+ ### Tek bir enum değerini ekle / sil / yeniden adlandır
239
+
240
+ **Listeyi yeniden göndermek gerekmez.** `entity_patch` düz RFC 6902 op'ları taşır ve seçici
241
+ (`[slug=…]`, `[value=…]`) `valueOptions` derinliğinde de çözülür:
242
+
243
+ ```jsonc
244
+ // ekle — dizinin sonuna; "-" JSON Pointer'ın "diziye ekle" belirtecidir
245
+ { "op": "add", "path": "/fields[slug=status]/valueOptions/-",
246
+ "value": { "value": "Onayda", "icon": "#0052CC", "order": 4 } }
247
+
248
+ // sil
249
+ { "op": "remove", "path": "/fields[slug=status]/valueOptions[value=İptal]" }
250
+
251
+ // yeniden adlandır (etiketi değiştir, sırayı koru)
252
+ { "op": "replace", "path": "/fields[slug=status]/valueOptions[value=İptal]/value", "value": "Reddedildi" }
253
+ ```
254
+
255
+ - **`isEnum` türetilmiştir** (`ValueOptions.Any()`), patch'te ayrıca yazılmaz; alan zaten enum
256
+ olduğu sürece tek op yeter.
257
+ - **Düz metin alanını enum'a çevirmek bu yol DEĞİLDİR:** `valueOptions` boş/null ise `/-` yolu
258
+ düşer — tam diziyi `replace` ile yaz (`/fields[slug=status]/valueOptions`).
259
+ - `order`'ı ekleme sırasında ver; verilmezse `0` olur ve sıralı raporlar sessizce bozulur
260
+ (yukarıdaki `order` maddesi).
261
+ - Çok dilli etiket gerekiyorsa `labels` aynı op'un `value` nesnesine girer.
262
+ - **Sil ve yeniden adlandırmadan ÖNCE satırları taşı.** Değeri hâlâ taşıyan satır varsa istek
263
+ **reddedilir ve hiçbir şey yazılmaz** — ölçüldü 2026-09-16, mesaj okunabilir: `Migration failed.
264
+ Please try again. Details: check constraint "check_<field id>" of relation "<tablo>" is violated
265
+ by some row`. Platform eski değeri satırlarda yeniden adlandırmaz. Sıra: `row_update` ile kanonik
266
+ değere taşı → sonra enum op'unu gönder (aynı sırayla ölçüldü: taşıma sonrası `remove` geçti).
267
+ - Yanıtın outline'ı alanın yeni değer listesini (`values`, ilk 30 + `valueOptionCount`) taşır —
268
+ ikinci bir `entity_get` turu gerekmez.
269
+
236
270
  ## Artan kod alanı (`Incremental`)
237
271
 
238
272
  Sıralı, önekli, insan okuyabilir kod üretir (`TLP-0001`). Alan tanımı:
@@ -1070,3 +1104,6 @@ gönderme.
1070
1104
  `version` bir **string**tir (mikrosaniye zaman damgası, `Number.MAX_SAFE_INTEGER`'ın üstünde):
1071
1105
  sayıya çevirirsen yuvarlanır ve her patch 409 döner. Yanıttaki `newVersion` bir sonraki patch'in
1072
1106
  `baseVersion`'ıdır.
1107
+
1108
+ Seçim listesine (enum) tek değer eklemek, silmek ya da yeniden adlandırmak da bu yoldan gider ve
1109
+ listeyi yeniden göndermeyi **gerektirmez** — §"Tek bir enum değerini ekle / sil / yeniden adlandır".
@@ -56,6 +56,10 @@ Bağımlılıklar bu sırayı zorunlu kılar; atlama:
56
56
  ifadelerde Functions.<ad>(…) ile, akışlar 'function' adımıyla çağırır;
57
57
  sayfaya özgü fonksiyon (scope:'page') sayfa açıldıktan sonra gelir.
58
58
  Kurallar platform_guide('functions'))
59
+ function_update (GÖVDE DEĞİŞİNCE: function_update { id, body } — patch aracı YOKTUR,
60
+ fonksiyonun tek büyük alanı gövdedir ve gateway vermediğin alanları
61
+ mevcut kayıttan doldurur. Adı değiştirirsen platform çağıranları AYNI
62
+ işlemde yeni ada taşır (renamedReferences))
59
63
  11. layout_create (header/sidebar + MUTLAKA bir 'slot' — slot'suz layout'a bağlı sayfa
60
64
  boş görünür; bkz. platform_guide('page') Kural 5)
61
65
  12. page_create + … (İŞ BURADA BİTMEZ: her kaydın yönetim ekranı, kimlik varsa giriş
@@ -620,6 +620,45 @@ gerekmese bile** ona bir middleware bağla. Tek `variable` adımlı, hiçbir şe
620
620
  Zayıf alternatif: bütün doğrulamaları ilk yazmadan **önce** bitirip yazma bölümünde hiç
621
621
  `response`/hata üretmemek — beklenmedik istisnaya karşı korumasızdır.
622
622
 
623
+ ## Patch belgesi OKUMA şeklidir — rol listesi `/roles` yolundadır (MS-022)
624
+
625
+ `microservice_patch` yamayı **`microservice_get`'in döndürdüğü gövdeye** uygular; platform da onu
626
+ kendi tarafında `MicroserviceDto` olarak okuyup `UpdateMicroserviceCommand`'a çevirir
627
+ (`PatchMicroserviceCommand.cs:36-55`). Yani yamalanan belge **yazma komutu değildir** ve iki
628
+ belgenin alan adları her yerde aynı değildir:
629
+
630
+ | Ne | Create/update GÖVDESİ | Patch YOLU (okuma şekli) |
631
+ |---|---|---|
632
+ | Rol listesi | `roleIds: [<guid>]` | **`/roles`** → `[{ id, name }]` |
633
+ | Middleware listesi | `middlewareIds: [<guid>]` | `/middlewareIds` (aynı) |
634
+ | Alan listesi | `sourceParameters.selectFields` | `/sourceParameters/selectFields` (aynı) |
635
+
636
+ **Ucun rolünü değiştirmek için tanımı yeniden göndermek gerekmez** — tek op yeter:
637
+
638
+ ```jsonc
639
+ // tüm listeyi yaz (tercih edilen): name isteğe bağlıdır, platform yalnız id okur
640
+ { "op": "replace", "path": "/roles", "value": [{ "id": "<role_list id>" }] }
641
+
642
+ // listeye ekle
643
+ { "op": "add", "path": "/roles/-", "value": { "id": "<role_list id>" } }
644
+
645
+ // tek rolü çıkar
646
+ { "op": "remove", "path": "/roles[id=<role_list id>]" }
647
+ ```
648
+
649
+ - **`roleIds` yolu patch belgesinde YOKTUR.** Verirsen gateway `/roles`'a çevirir, çıplak guid'leri
650
+ `{ id }` nesnesine sarar ve çeviriyi `gatewayNormalized` ile söyler — istek düşmez. Çevrilmeseydi
651
+ `replace /roleIds` gateway'de `OPERATION_PATH_UNRESOLVABLE` verirdi, `add /roleIds` ise platformda
652
+ **sessizce yutulurdu** (bilinmeyen üye atılır, 200 döner, rol değişmez).
653
+ - **Tam dizi `replace`'i tercih et.** Rolü olmayan uçta alan `null` serileşebilir ve `/roles/-`
654
+ çözülemez; gateway bunu tek öğelik tam diziye katlar ve notu yanıta yazar.
655
+ - Rol id'leri `role_list`ten gelir. `role_list` **slug döndürmez** ve iki anahtar farklıdır:
656
+ buradaki `id` ile yetkilendirmenin karşılaştırdığı `slug` aynı şey değildir
657
+ (`platform_guide('entity')` §"Hazır entity'ler").
658
+ - Yazmanın kanıtı yanıttadır: outline `roleIds` taşır, ikinci bir `microservice_get` gerekmez.
659
+ - **Rol tek başına kapı değildir** — §"Uçlar varsayılan olarak ANONİMDİR": koruma `middlewareIds`
660
+ ile gelir.
661
+
623
662
  ## Uçlar varsayılan olarak ANONİMDİR
624
663
 
625
664
  Çalıştırma rotası `[AllowAnonymous]`'tur (`MicroservicesController.cs:90`): `middlewareIds` boş
@@ -629,6 +668,7 @@ Anonim veri toplama (anket, başvuru, geri bildirim) bu sayede kurulabilir — a
629
668
 
630
669
  `roles` bir kapı değildir: `roleIds` dolu + `middlewareIds` boş bir uç tokensız POST'u 200 ile
631
670
  kabul etti. Ayrıntı ve doğru rol kapısı tarifi: `platform_guide('middleware')` Tarif 3b.
671
+ Rol listesini **değiştirmek** için yukarıdaki §"Patch belgesi OKUMA şeklidir".
632
672
 
633
673
  > **Anonimlik/erişim testi `microservice_execute` + `anonymous: true` ile yapılır.** Bayrak
634
674
  > verildiğinde gateway isteğe HİÇBİR `Authorization` header'ı koymaz (varsayılan davranışta
@@ -12,6 +12,10 @@ Gerçek projelerde tek tanım 20-100 bin satır JSON olabilir. **Tam gövdeyi ba
12
12
  belleğinde çeker, patch'i uygular, politika
13
13
  kontrolünden geçirir, geri yazar
14
14
  5. *_get view:'outline' → değişikliği teyit et
15
+
16
+ function_update {id, body} → FONKSİYONDA *_patch YOKTUR: tek büyük alan
17
+ gövdedir, yalnız onu gönder (gateway kalan
18
+ alanları mevcut kayıttan doldurur)
15
19
  ```
16
20
 
17
21
  `view:'full'` **son çaredir** — yalnız küçük kayıtlarda ya da patch'in kurulamadığı köklü
@@ -23,12 +27,19 @@ yeniden yazımlarda.
23
27
  ekleme), `/fields/5/nullable`.
24
28
  - Dizin indekslerini tahmin etme — `view:'outline'` çıktısındaki sıradan ya da
25
29
  `view:'part'` ile doğrulayarak yaz.
30
+ - **Bir listeye tek öğe eklemek listeyi yeniden göndermek değildir:** `add` + `/-`
31
+ (`/fields[slug=status]/valueOptions/-`, `/sourceParameters/selectFields/-`); tek öğeyi silmek
32
+ ya da değiştirmek için seçici (`[value=…]`, `[slug=…]`). Enum örnekleri:
33
+ `platform_guide('entity')` §"Tek bir enum değerini ekle / sil / yeniden adlandır".
26
34
  - Silme yerine `replace` tercih et; `remove` kullanacaksan önce o path'i `part` ile görüntüle.
27
35
  - Yanıttaki outline özeti değişikliğin uygulandığı hâli gösterir — teyit için oku.
28
36
  - **Kullanıcı fonksiyonunda patch aracı yok:** `function_update` tam gövde yazar ama vermediğin
29
37
  alanları mevcut kayıttan doldurur — yalnız `body` göndermen yeter (fonksiyonun tek büyük alanı
30
- gövdedir). Adı değiştirmeden önce `dependency_graph` (`module:'ui-function'`) ile çağıranları
31
- bul: `{{ }}`/`code` ifadelerindeki `Functions.<eskiAd>(` çağrıları metinseldir ve kırılır.
38
+ gövdedir). `function_get` çıktısı `function_update`'in şablonudur; örnek:
39
+ `ngapps://examples/function-total` `functionUpdateArgs`. Adı değiştirmeden önce
40
+ `dependency_graph` (`module:'ui-function'`) ile çağıranları bul: `{{ }}`/`code` ifadelerindeki
41
+ `Functions.<eskiAd>(` çağrıları metinseldir — platform onları aynı işlemde taşır, ayrıntı
42
+ `platform_guide('functions')` §Güncelleme ve silme.
32
43
 
33
44
  ## Değiştirmeden önce geçmişe bak
34
45
 
@@ -167,11 +167,23 @@ ve `datatable` yalnız `mount` yayar (`datatable`'ınki bilinçli — Kural 1e).
167
167
  sessizce ölü kalır** — önce `component_get`. Gateway `set_event`'i katalogla karşılaştırır ve
168
168
  uymayan adı **PAGE-008** ile istek gitmeden reddeder.
169
169
 
170
+ **Bir olaya birden fazla akış bağlanabilir — ikinciyi `add_event` ile ekle.** `set_event` olayın
171
+ BÜTÜN bağlarını tek akışa indirir: başlık alanının `change` olayına makale önerisi bağlıyken akıllı
172
+ öneriyi `set_event` ile bağlamak makale önerisini devre dışı bırakır. Gateway bunu **PAGE-034** ile
173
+ istek gitmeden reddeder. Doğru op `{ "op": "add_event", "target": "<elementId>", "event": "change",
174
+ "eventValue": "<ikinci flow id>" }`'dir. Olay tetiklenince bağlı akışların hepsi **paralel ve
175
+ birbirinden bağımsız** koşar: biri hata verirse diğeri etkilenmez, **sıra garantisi yoktur**. Biri
176
+ diğerinin sonucunu bekliyorsa ya da ikisi aynı değişkene/prop'a yazıyorsa ayrı bağ değil,
177
+ `flow-trigger` adımıyla zincir kur (Kural 1c). Mevcut bağı bilerek değiştirmek için `set_event`
178
+ op'una `replaceBoundFlows: true` ekle. **Mobil runtime yalnız ilk akışı koşar** — mobil
179
+ uygulamada olay başına tek akış bağla.
180
+
170
181
  **Olay KALDIRMAK için `remove_event` kullan.** Platformun `set_event` micro-op'u olay silemez:
171
182
  `eventValue: null` verildiğinde kayıt `{ name: 'click' }` olarak (değersiz) kalır, diziden
172
183
  çıkmaz — şemada ölü bağ artığı bırakır. `{ "op": "remove_event", "target": "<elementId>",
173
184
  "event": "click" }` platformun op'udur: olayı diziden çıkarır (yanıtta `removedEvents`); olmayan
174
- bir olayı kaldırmaya çalışmak `missing-event` ile 422 döner. Sayfa düzeyi olaylar için
185
+ bir olayı kaldırmaya çalışmak `missing-event` ile 422 döner. Olaya birden fazla akış bağlıysa
186
+ kaldırılacak akışı `eventValue` ile ver; vermezsen `ambiguous-event` ile 422 döner. Sayfa düzeyi olaylar için
175
187
  `target: "page"`. Referanssız kalan akış silineceği için `allowFlowDeletion` kuralı (G-21) bu op'ta
176
188
  da geçerlidir.
177
189
 
@@ -401,6 +401,41 @@ gidilir, hiçbiri tutmazsa `else` dalı yürür:
401
401
  > `rule.isExpression` bayrağı bu iki alan için **okunmaz**.
402
402
 
403
403
 
404
+ ## HTTP adımı: gövde ayrı alan değil, `payloadExpression` üç anahtarı birden taşır (WF-033)
405
+
406
+ Dört alan: `rule.urlExpression` · `httpMethod` · `contentType` · `payloadExpression`.
407
+ **Gövdenin kendi alanı yoktur** — `payloadExpression` tek bir ifadeyle `{ header, body,
408
+ queryString }` **üçünü birden** üretmek zorundadır; yürütücü üçüne de `dynamic` üye erişimiyle
409
+ bakar (`HttpStep.cs:32,42,59`).
410
+
411
+ ```jsonc
412
+ "rule": {
413
+ "stepType": "http",
414
+ "urlExpression": "'https://api.example.com/orders'",
415
+ "httpMethod": "POST",
416
+ "contentType": "application/json",
417
+ "payloadExpression": "({ body: { orderId: input.orderId }, header: { Authorization: 'Bearer ' + system.token }, queryString: {} })"
418
+ }
419
+ ```
420
+
421
+ | Tuzak | Ne olur |
422
+ |---|---|
423
+ | Bir anahtar eksik (`queryString` yok) | Adım **düşer**: `RuntimeBinderException` — ölçülen mesaj birebir `'System.Dynamic.ExpandoObject' does not contain a definition for 'queryString'`. Boş da olsa `{}` yazın |
424
+ | `urlExpression` tırnaksız | İfadedir, düz metin değil: `https://…` yazarsan JS onu değişken sanır ve adım `x is not defined` der |
425
+ | `contentType` boş | Gövde `body.ToString()` ile gider (`HttpStep.cs:63-85`); nesne gövdesi tip adına dönüşür. POST/PUT'ta açıkça yazın |
426
+ | Yanıt JSON değil | Gövde **sessizce** boş nesne olur (`:92`) — XML dönen uçtan veri de hata da alamazsınız |
427
+ | Adım adında boşluk | Çıktıya adım adıyla erişilir; ad geçerli JS tanımlayıcısı olmalı (`call_roles`, `Call Roles` değil) |
428
+
429
+ **Çıktıyı okurken şemaya değil ölçüme güvenin (2026-09-16).** `workflow_step_schema('http')`
430
+ `returnScheme`'i kök seviyede `{ httpStatusCode, data }` ilan eder, ama koşumda ifade bağlamındaki
431
+ adım `{ data, meta: { elapsedTime, httpStatusCode, totalCount }, error }` şeklindedir:
432
+
433
+ - gövde → **`<adım>.data`**
434
+ - durum kodu → **`<adım>.meta.httpStatusCode`**
435
+ - `<adım>.httpStatusCode` → **undefined** (şemanın vaat ettiği ad çalışmaz)
436
+
437
+ Tam örnek ve POST varyantı: `ngapps://examples/workflow-http-step`.
438
+
404
439
  ## Workflow adımı: başka bir akışı parametreyle çağırmak (WF-032)
405
440
 
406
441
  Ortak mantığı (kayıt açma, bildirim, numaralandırma) **bir kez** `type:'workflow'` akışı olarak
@@ -458,6 +493,8 @@ yazıp diğer akışlardan çağırmanın yolu budur — 30 adımı kopyalamak d
458
493
  - Aynı alt akışı birden çok adımdan çağırmak serbesttir; özyineleme runner'ın koşum sayacına
459
494
  takılır (kural envanteri WF-028 — gateway kuralı değil, backend davranışı).
460
495
 
496
+ Tam örnek (çağıran + çağrılan akış, canlı ölçümle yazıldı): `ngapps://examples/workflow-call-workflow`.
497
+
461
498
  ## Loop step — dizinin her öğesi için bir adım koştur
462
499
 
463
500
  Tek uçta "başlık + N alt kayıt" yazmanın yolu budur (anket + sorular + seçenekler tek çağrıda;
@@ -478,9 +515,19 @@ Tek uçta "başlık + N alt kayıt" yazmanın yolu budur (anket + sorular + seç
478
515
  (`StepRuleDto StepRule`); `children` gönderirsen `rule.Children` boş kalır ve loop
479
516
  **`NullReferenceException`** ile patlar — hata mesajı `arrayExpression`'ı işaret ettiği için
480
517
  seni ifadeyi suçlamaya iter, oysa sorun anahtar adıdır.
481
- - **Geçerli öğe `loopItem`'dır.** Çocuğun `fields[].value` / `filters[].value` ifadelerinde
482
- `loopItem.<alan>` yazılır; `input` ve önceki adımlar (`createSurvey.data.id`) da görünmeye
483
- devam eder.
518
+ - **Geçerli öğe `loopItem`'dır — ama YALNIZ loop'un kendi değerlendirdiği ifadelerde.** Çocuğun
519
+ `fields[].value` / `filters[].value` ifadelerinde `loopItem.<alan>` yazılır (ölçüldü 2026-09-16:
520
+ `table` çocuğunun filtresinde `loopItem.slug` → `'super-admin'`); `input` ve önceki adımlar
521
+ (`createSurvey.data.id`) da görünmeye devam eder.
522
+ - **`code` çocuğunda `loopItem` YOKTUR (ölçüm 2026-09-16).** `code` adımı kendi JS motorunu kurar
523
+ ve `LoopStep`'in `loopItem` ataması oraya ulaşmaz: adım **`Error: loopItem is not defined`** ile
524
+ düşer, o yinelemenin çıktısında `result` hiç oluşmaz. Motorda görünenler `input` · `system` ·
525
+ `file` · `crypto` · `convert` ve ifade fonksiyonlarıdır. **Her öğe için hesap yapman gerekiyorsa
526
+ hesabı `arrayExpression`'da önden yap** — orası JS'tir ve diziyi istediğin şekle sokabilirsin:
527
+ `(input.kalemler || []).map(k => ({ ...k, tutar: k.fiyat * k.adet }))`; çocuk sonra
528
+ `loopItem.tutar`'a bakar.
529
+ - **İndeks değişkeni yoktur.** Sıra numarası gerekiyorsa diziyi `arrayExpression`'da üret:
530
+ `input.kalemler.map((k, i) => ({ ...k, sira: i }))`.
484
531
  - **Çıktı `[{ item, result }]` dizisidir.** `result` çocuk adımın çıktısıdır; `table create`
485
532
  için `{ id }`. Sonraki adımda `loopQuestions.data` ile okunur — **yazılan kayıtların id'sini
486
533
  girdideki öğeyle eşleştirmenin yolu budur.**
@@ -496,6 +543,9 @@ Tek uçta "başlık + N alt kayıt" yazmanın yolu budur (anket + sorular + seç
496
543
  İstemci alt kayıtları **düz** gönderir ve satırları `key`/`qkey` ile eşler — iç içe dizi
497
544
  göndermek gerekmez (iç içe loop denenmedi; `loopItem` iç loop'ta gölgeleneceği için önerilmez).
498
545
 
546
+ Tam örnek, ölçülen çıktı şekli ve `code` çocuğu tuzağının çıkış yolu:
547
+ `ngapps://examples/workflow-loop-step`.
548
+
499
549
  > **Table step'te `create`/`update` satır değerleri `fields[]`'tedir ve JS İFADESİDİR.**
500
550
  > `{ "fieldId": "<alan id>", "source": "expression", "value": "input.title", "order": 0 }` —
501
551
  > `PrepareParameters` her `fields[].value`'yu `EvaluateExpression` ile koşturur
@@ -1,8 +1,10 @@
1
1
  {
2
- "hash": "54430a8d4050fd201aaee88608a5d06b947edf6d561ad31354914422df4e94ce",
3
- "generatedAt": "2026-09-16T11:17:11.569Z",
4
- "types": 185,
5
- "files": 107,
2
+ "hash": "05c7864d58e308973805f7becee1578ce9eddad196abcdf7eebb43b1e7677549",
3
+ "generatedAt": "2026-09-16T15:53:10.903Z",
4
+ "types": 186,
5
+ "files": 108,
6
6
  "note": "Şemaları üreten backend kaynaklarının parmak izi. Elle düzenlemeyin: 'npm run generate-schemas' yazar, 'npm run schema:check' denetler.",
7
- "missing": []
7
+ "missing": [
8
+ "NgApps.Infrastructure.Models.IdNamePair`1[[System.Guid, System.Private.CoreLib, Version=10.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]]"
9
+ ]
8
10
  }