@appaflytech/zeyrek-ai-mcp 0.0.4 → 0.0.6

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.
Files changed (101) hide show
  1. package/.env.example +8 -3
  2. package/.env.production.example +2 -0
  3. package/README.md +229 -193
  4. package/dist/config.js +20 -2
  5. package/dist/knowledge/backendConstraints.js +897 -0
  6. package/dist/knowledge/componentCatalog.js +196 -42
  7. package/dist/knowledge/coverage.js +43 -0
  8. package/dist/knowledge/flowGuards.js +30 -0
  9. package/dist/knowledge/guideSections.js +49 -0
  10. package/dist/knowledge/hydrate.js +97 -0
  11. package/dist/knowledge/languageCodes.js +36 -0
  12. package/dist/knowledge/languages.js +105 -0
  13. package/dist/knowledge/layoutSummary.js +10 -0
  14. package/dist/knowledge/localization.js +51 -0
  15. package/dist/knowledge/normalize.js +449 -16
  16. package/dist/knowledge/outlines.js +136 -12
  17. package/dist/knowledge/propTypes.js +80 -0
  18. package/dist/knowledge/rules.js +618 -43
  19. package/dist/knowledge/schemaGuard.js +199 -0
  20. package/dist/knowledge/styleResolve.js +63 -4
  21. package/dist/observability/trace.js +27 -3
  22. package/dist/platform/errors.js +46 -4
  23. package/dist/platform/http.js +33 -4
  24. package/dist/platform/patch.js +27 -0
  25. package/dist/platform/refs.js +104 -0
  26. package/dist/platform/responses.js +9 -2
  27. package/dist/platform/screenshot.js +527 -16
  28. package/dist/platform/secrets.js +40 -0
  29. package/dist/platform/tokenVault.js +89 -0
  30. package/dist/platform/writeDiff.js +281 -0
  31. package/dist/resources/examples/layout-app-shell.json +1 -1
  32. package/dist/resources/examples/microservice-table-list.json +2 -4
  33. package/dist/resources/examples/page-dynamic-form.json +2 -2
  34. package/dist/resources/examples/page-flex-sections.json +99 -0
  35. package/dist/resources/examples/page-sign-in.json +3 -2
  36. package/dist/resources/guides/component.md +69 -8
  37. package/dist/resources/guides/entity.md +404 -16
  38. package/dist/resources/guides/expressions.md +44 -2
  39. package/dist/resources/guides/external-component.md +9 -0
  40. package/dist/resources/guides/getting-started.md +77 -7
  41. package/dist/resources/guides/layout.md +53 -10
  42. package/dist/resources/guides/microservice.md +226 -13
  43. package/dist/resources/guides/middleware.md +89 -1
  44. package/dist/resources/guides/page.md +383 -46
  45. package/dist/resources/guides/translation.md +126 -8
  46. package/dist/resources/guides/view.md +16 -1
  47. package/dist/resources/guides/workflow.md +198 -27
  48. package/dist/resources/knowledge.js +5 -0
  49. package/dist/resources/rules.js +27 -0
  50. package/dist/resources/schema-fingerprint.json +8 -0
  51. package/dist/resources/schemas/CreateBulkRowCommand.json +33 -0
  52. package/dist/resources/schemas/CreateDatasourceCommand.json +15 -6
  53. package/dist/resources/schemas/CreateDiscoveredEntitiesCommand.json +19 -8
  54. package/dist/resources/schemas/CreateEntityCommand.json +52 -12
  55. package/dist/resources/schemas/CreateMicroserviceCommand.json +26 -2
  56. package/dist/resources/schemas/CreateQueryCommand.json +56 -3
  57. package/dist/resources/schemas/CreateRowCommand.json +38 -0
  58. package/dist/resources/schemas/CreateViewCommand.json +13 -10
  59. package/dist/resources/schemas/CreateWorkflowCommand.json +95 -14
  60. package/dist/resources/schemas/GetRowsQuery.json +3 -0
  61. package/dist/resources/schemas/PreviewMicroserviceCommand.json +16 -0
  62. package/dist/resources/schemas/PreviewQueryCommand.json +5 -2
  63. package/dist/resources/schemas/PreviewWorkflowCommand.json +34 -9
  64. package/dist/resources/schemas/StepDto.json +23 -3
  65. package/dist/resources/schemas/UpdateBulkRowCommand.json +33 -0
  66. package/dist/resources/schemas/UpdateDatasourceCommand.json +15 -6
  67. package/dist/resources/schemas/UpdateEntityCommand.json +49 -10
  68. package/dist/resources/schemas/UpdateMicroserviceCommand.json +28 -3
  69. package/dist/resources/schemas/UpdateQueryCommand.json +13 -3
  70. package/dist/resources/schemas/UpdateRowCommand.json +40 -0
  71. package/dist/resources/schemas/UpdateViewCommand.json +15 -10
  72. package/dist/resources/schemas/UpdateWorkflowCommand.json +95 -14
  73. package/dist/server.js +4 -0
  74. package/dist/tools/access.js +3 -3
  75. package/dist/tools/applications.js +1 -1
  76. package/dist/tools/automations.js +63 -30
  77. package/dist/tools/common.js +39 -0
  78. package/dist/tools/components.js +16 -7
  79. package/dist/tools/datasources.js +83 -21
  80. package/dist/tools/deployments.js +34 -2
  81. package/dist/tools/dtos.js +25 -12
  82. package/dist/tools/entities.js +113 -28
  83. package/dist/tools/externalComponents.js +9 -3
  84. package/dist/tools/files.js +20 -6
  85. package/dist/tools/flows.js +110 -11
  86. package/dist/tools/folders.js +7 -2
  87. package/dist/tools/globalFunctions.js +63 -0
  88. package/dist/tools/layouts.js +103 -29
  89. package/dist/tools/microservices.js +229 -38
  90. package/dist/tools/organizations.js +5 -6
  91. package/dist/tools/pages.js +365 -100
  92. package/dist/tools/platformInfo.js +152 -11
  93. package/dist/tools/projects.js +183 -33
  94. package/dist/tools/queries.js +32 -10
  95. package/dist/tools/rows.js +159 -45
  96. package/dist/tools/screenWrite.js +59 -0
  97. package/dist/tools/screens.js +28 -0
  98. package/dist/tools/translations.js +24 -8
  99. package/dist/tools/views.js +20 -10
  100. package/dist/tools/workflows.js +33 -16
  101. package/package.json +6 -3
@@ -15,7 +15,10 @@ Uydurma fonksiyon adı runtime'da `X is not defined` 500'üdür — yalnız bura
15
15
  **Değişkenler:** step çıktıları (`<stepAdı>.data`), middleware değişkenleri (`user`,
16
16
  `settings`, `permissions`… — `workflow_middleware_variables` ile keşfet), HTTP bağlamı:
17
17
  `cookie`, `header`, `queryString`, `body`, `triggerSource`/`context` (tetikleyen ucun
18
- type/name/roles/impersonatedUserId'ı). Workflow'da istek girdisi `input.`, automation'da
18
+ type/name/roles/impersonatedUserId'ı). Gövde bir JSON **nesnesi** ise `body.alan` diye okunur,
19
+ gövde bir JSON **dizisi** ise `body[0].alan` diye — 2026-09-04 öncesinde nesne gövde de diziye
20
+ sarılıyordu ve `body.alan` sessizce `undefined` dönüyordu, bu yüzden gövdeye bakan yetki kapıları
21
+ açık kalıyordu. Workflow'da istek girdisi `input.`, automation'da
19
22
  tetikleyen satır `row` / `previousRow` / `user`.
20
23
 
21
24
  **Fonksiyonlar (qm'de aktif kullanılanlar önce):**
@@ -57,6 +60,27 @@ tetikleyen satır `row` / `previousRow` / `user`.
57
60
  (motor tr-TR + UTC); automation zinciri istek başına workflow başına 10 koşumda
58
61
  `InfiniteLoopException` ile kesilir (sonsuz döngü koruması).
59
62
 
63
+ > **İki ayrı sınır var ve önce KÜÇÜK olan konuşur.** `AutomationConsumer` automation'ı
64
+ > çalıştırmadan önce `triggerCount > 2` kontrolü yapar (yani 3. tetiklemede keser);
65
+ > `WorkflowContext.CheckProcessCount` ise workflow koşumunu 10'da keser. Yukarıdaki 10 yalnız
66
+ > ikincisidir. Hata mesajı artık hangi automation'ın kaç kez tetiklendiğini ve sınırı söyler.
67
+ >
68
+ > **Kapıyı doğru yere koy:** sayaç, akışın ilk adımı koşmadan önce artar. Tetikleyen satıra geri
69
+ > yazan bir automation'da `row.status !== previousRow.status` kapısını akışın içindeki bir
70
+ > `condition` ADIMINA koymak koşumu saydırmayı sürdürür; aynı ifade `automation.condition`
71
+ > alanında ise yeniden tetikleme hiç doğmaz ve sayaç artmaz (`RowConsumer.cs:130-143`).
72
+ >
73
+ > **Yanıltıcı `failed` DÜZELTİLDİ (2026-09-05).** Sayaç pas başına İKİ artıyordu: runner'ın kendi
74
+ > 10'luk koruması (`CheckProcessCount`) ile tetikleme zincirinin sayacı aynı sözlüğü paylaşıyordu,
75
+ > bu yüzden tetikleyen satırını güncelleyen her automation ikinci pasında `processCount: 3` ile
76
+ > kesiliyordu — ölçümde 10 durum değişikliğinin **10'unda da**. İki sayaç ayrıldı; artık her pas
77
+ > zinciri bir artırır: 1. pas kapıyı geçip yazar, 2. pas kapıya takılıp durur ve zincir `2`'de
78
+ > biter. Ölçüm (aralıklı 10 güncelleme): 20 olay, hepsi `completed`, `failed` yok.
79
+ >
80
+ > Kalan tek durum bir YARIŞTIR: aynı satıra ara vermeden art arda güncelleme atılırsa bir zincir
81
+ > önceki zincirin yazması havadayken başlayabilir ve seyrek bir `failed` düşebilir (10 hızlı
82
+ > güncellemede 1). Satır başına tek akış bekliyorsanız güncellemeleri aralıklandırın.
83
+
60
84
  **Tohum parola üretmek istiyorsan** hash'i platformun kendisine ürettir: parolayı düz metin alan
61
85
  bir workflow yazıp `crypto.SHA256.encode(input.password)` sonucunu döndür, ya da kullanıcı kaydını
62
86
  açan ucu kullan. Dışarıda kendi SHA-256'nı hesaplayıp yazarsan giriş akışı **hiçbir zaman**
@@ -73,14 +97,32 @@ dalından devam eder.
73
97
  | `Page` | `Page.user`, `Page.variables`, `Page.components`, `Page.properties`, `Page.queryParams`, `Page.authorized(roles)` |
74
98
  | **Komponent adları** | Her komponent adıyla doğrudan değişkendir: `specialties_table.rows = data` hem prop'u yazar hem UI'ı günceller (change event). Addaki `-` silinir: `tickets-table` → `ticketstable` |
75
99
  | `state.page` / `state.app` | Kalıcı state (oku/yaz): `state.page.selectedId = row.id`. **Tanımlı değişkenler de buradadır:** `variable_create` ile yazılan her `key` sayfa düzeyinde `state.page.<key>`, uygulama düzeyinde `state.app.<key>` olur; `value` nesneyse alanlarına inilir (`state.page.MyTest.isActive`) |
76
- | `Steps` | Adım çıktıları: `Steps.load_specialties` (stepId ile); `error` = son hata |
100
+ | `Steps` | Adım çıktıları: `Steps.load_specialties` — anahtar adımın **ADIdır**, `stepId` değil (`setStorage(task.name, …)`); `Steps.<adım>.error` o adımın hatası — ama YALNIZ HTTP durum metnidir (401 için `'Unauthorized'`), ucun mesajı değil. `Steps.error` ise hata akışı için doldurulur (`onError` koşmadan önce `setStorage('error', …)` yapar) ama **YAPIŞKANDIR**: storage sayfa başına tek `FlowMachine`'de yaşar ve `'error'` anahtarı hiç temizlenmez, yani ilk hatadan sonra o sayfadaki her akışta dolu kalır — normal akışta `Steps.error` okuyan bir ifade bayat hatayı görür ve şekli `{ message, code, step: { id, name, type, data } }`'dir: kullanıcıya gösterilecek metin `Steps.error.message`'tır (`err.response.data.message`, boşsa durum metni). **Başarılı bir adımda `Steps.<adım>.data` gerçek bir `Array`'dir** — `.map` · `.filter` · `.concat` · `.length` çalışır. Adım HATA dönerse aynı alan ucun hata gövdesini taşır; aşağıdaki kutuya bakın |
77
101
  | `self` · `item` · `Row` / `RowIndex` | Tetikleyen komponent (yazılabilir) · repeater/tablo satırı (yazmak satırı günceller) |
78
102
  | `event` | `{name, data, source: {id, props}}` |
79
103
  | `cookie` | `cookie.get(name)` / `cookie.set(name, value, {expiresInHours, path})` / `cookie.remove(name)` — tarayıcı cookie'si |
80
104
  | `setComponentProperty(name, key, value)` / `getComponentProperty(name, key)` | fonksiyon alternatifi |
81
105
  | `LocalStorage` · `Widget` · `params` | yerel veri · widget bağlamı · olay parametreleri |
106
+ | `t(key)` | Çeviri anahtarını çözer: aktif dil → projenin varsayılan dili → anahtarın kendisi. Boş string de "eksik" sayılır. Sözlük `machine.context.state.translations`'tan gelir; dil `language-selector` ile değişince bir sonraki çağrı yeni dili görür. **Yalnız UI flow kodunda vardır** — backend ifadelerinde karşılığı `Loc()`'tur (`platform_guide('workflow')`) | `t('home.welcome')` |
82
107
  | `log(...)` | console (proxy'leri temizler) |
83
108
 
109
+ > **`.map is not a function` bir dizi sözleşmesi sorunu DEĞİL, düşmüş bir adımdır.**
110
+ > Ölçüldü 2026-09-05 (`f09-repro/probe`, yerel kurulum): başarılı bir microservice adımında
111
+ > `Steps.<adım>.data` `[object Array]`'dir, `Array.isArray` true döner ve `.map`/`.filter`/
112
+ > `.concat` çalışır. Adım hata döndüğünde (401, 500, geçersiz parametre) motor aynı alana ucun
113
+ > **hata gövdesini** yazar — o bir nesnedir, dizi metodları yoktur ve
114
+ > `(Steps.x.data || []).map is not a function` tam olarak burada çıkar. 2026-09-05 koşusunda
115
+ > boş kalan şirket filtresinin sebebi buydu: adım korumalı bir uca 401 ile düşmüştü.
116
+ > **Teşhis sırası:** önce `Steps.<adım>.error`'a bak, sonra ifadeyi suçla. Bir dizi bekliyorsan
117
+ > `Array.isArray(Steps.<adım>.data)` ile koru; boş listeye düşmek, hata gövdesi üzerinde
118
+ > dönmekten iyidir.
119
+ >
120
+ > **Patlayan ifade artık sessiz değil (F-09).** Hedef prop yine YAZILMAZ — bozuk bir değeri
121
+ > yazmak durumu kötüleştirir — ama akış bunu yapısal olarak bildirir: `page_screenshot` yanıtı
122
+ > `runtimeErrors` taşır ve hangi komponentin hangi prop'unun yazılamadığını (`element` ·
123
+ > `property`) söyler. Aynı defter hata dönen microservice adımlarını da kaydeder. **Kare
124
+ > "çalışıyor" görünse bile `runtimeErrors` doluysa o adımlar işini YAPMAMIŞTIR.**
125
+
84
126
  **Prop'a doğrudan ifade bağlama (`{{ }}`):** akış kurmadan bir prop'u ifadeye bağlamak için
85
127
  prop `type: 'code'` olur, **ifade `ref` alanına `{{ }}` içinde** yazılır, `data` tasarım zamanı
86
128
  değerini taşır:
@@ -127,3 +127,12 @@ Platform, npm paketi olmadan servis eder:
127
127
 
128
128
  `external_component_scaffold` bu sözleşmeye uyan bir iskeleti yerel dizine
129
129
  zaten yazar; kit, iskeleti elle düzenlerken referans olarak işinize yarar.
130
+
131
+ ## Backend doğrulama sınırları (EXT-001)
132
+
133
+ `external_component_publish` manifest'i kayıt ucuna gitmeden denetler (block): `protocolVersion`
134
+ 1; `definition.blocks` en az bir blok; `definition.props[].name` DOM/host adı olamaz
135
+ (`attributes`, `children`, `class`, `className`, `id`, `name`, `style`, `slot`, `innerHTML`…) ve
136
+ `zx`/`on` ile başlayamaz; `definition.props[].type` platform sözlüğünden (`text`, `number`,
137
+ `switch`, `dropdown`, `object`, `array`, `views`, `component`…). Slug deseni `^x-[a-z][a-z0-9-]*$`
138
+ ve 2 MB paket sınırı tool'un kendisinde denetlenir. Tam liste: `ngapps://rules/backend-constraints`.
@@ -7,21 +7,41 @@ Bağımlılıklar bu sırayı zorunlu kılar; atlama:
7
7
  ```
8
8
  1. project_create (application zorunlu: ilk application projeyle birlikte açılır;
9
9
  application slug'ı ORG GENELİNDE tekildir — 'web' büyük olasılıkla
10
- doludur, 409'da project_list ile mevcut slug'ları tara)
10
+ doludur, 409'da project_list ile mevcut slug'ları tara.
11
+ DİL: gateway create'ten sonra projenin dillerini kurar — verirsen
12
+ languages/defaultLanguage'ı, vermezsen tek dillik ['en'] tohumunu
13
+ (PRJ-003). Ürünün dili farklıysa BURADA yaz; dilsiz projede yazılan
14
+ çeviriler runtime'da ham anahtar olarak çizilir, bkz.
15
+ platform_guide('translation'))
11
16
  2. project_get (environment id'leri + application id burada)
12
- 3. datasource_list (her projede built-in datasource hazır gelir — çoğu iş için yeterli;
13
- dış DB gerekiyorsa datasource_test_connection → datasource_create)
17
+ 3. datasource_list (built-in datasource PROJEYLE BİRLİKTE doğar: project_create ayrı bir
18
+ veritabanı, her environment için ayrı bir şema ve o veritabanına bağlı
19
+ built-in datasource açar. Entity'lerini oraya aç — listede
20
+ isBuiltIn: true olan kayıt odur, silinemez.
21
+ TEMİZ BİR İSİM ALANI İSTİYORSAN YENİ PROJE AÇ, yeni datasource değil:
22
+ tek datasource içinde önek/şema ayırmanın yolu YOK.
23
+ datasource_create YALNIZ kullanıcı kendi veritabanını verdiğinde
24
+ çağrılır ('host/port/kullanıcı/parola şunlar'); built-in'in parolası
25
+ hiçbir tool'dan okunamaz ve okunması gerekmez. Dış bir DB'ye kurulan
26
+ entity users'a ilişki KURAMAZ — kimlik gerektiren her uygulama
27
+ built-in'de kurulur, bkz. platform_guide('entity'))
14
28
  4. entity_list (ÖNCE BAK: platform her projeye hazır entity'ler açar — users (kimlik:
15
29
  (applicationId'siz) email/password/first_name/last_name), roles, user_roles, permissions.
16
30
  applicationId VERİRSEN bunlar listede GÖRÜNMEZ. Ürünün "müşteri" /
17
31
  "öğrenci" / "üye" kavramı için sıfırdan bir kimlik tablosu açmadan önce
18
- users'ı gör — bkz. platform_guide('entity') §'Hazır entity'ler')
32
+ users'ı gör — bkz. platform_guide('entity') §'Hazır entity'ler'.
33
+ ROL: role_create diye bir tool YOK ve olmamalı — roles sıradan bir
34
+ entity'dir. Projeyle gelen roller yetmiyorsa yol entity_get('roles') +
35
+ kendi microservice ucun; sayfaya bağlama page_create roleIds ile)
19
36
  5. entity_create (applicationIds Studio'nun application bazlı entity listesi içindir —
20
37
  boşsa satır uçları ÇALIŞIR ama entity Studio listesinde görünmez;
21
38
  önce ilişkisiz gövdeler, sonra update/patch ile ilişki alanları —
22
39
  bkz. platform_guide('entity'))
23
40
  6. row_create (test verisi; alan tanımlarını row_list ile doğrula)
24
- 7. dto_list / dto_create (workflow/automation'ın parameterDtoId/returnDtoId sözleşmeleri;
41
+ 7. dto_list / dto_create — çocuk alanın `type` değeri **`"string"`**'tir; `"text"` yazmak sessizce
42
+ `object`'e çevrilir ve DTO bozuk kurulur (ölçüldü 2026-09-05, teşhis platformun kendi ürettiği
43
+ bir DTO'yu okuyarak yapıldı). Şablon için var olan bir DTO'yu `dto_get` ile alın.
44
+ (workflow/automation'ın parameterDtoId/returnDtoId sözleşmeleri;
25
45
  platform entity başına DTO üretir — önce dto_list ile ara)
26
46
  8. workflow_create (type: 'middleware' — YALNIZ ihtiyaç varsa; bkz. aşağıdaki karar tablosu.
27
47
  Kuruyorsan guard + 'user' değişkeni; uçlara middlewareIds ile
@@ -51,8 +71,17 @@ kilitler:
51
71
  2. O workflow'un ucu, kimlik kurulduğu için `middlewareIds` ile auth middleware'ine bağlıdır.
52
72
  3. Auth middleware'i token'ı **çerezten** okur; ortada henüz kullanıcı yoktur, dolayısıyla token
53
73
  da yoktur.
54
- 4. **Yeni projede `users` tablosu BOŞ gelir** — `organization_sync_users` Studio operatörünü
55
- otomatik kopyalamaz.
74
+ 4. **Yeni projede `users` tablosu BOŞ gelir.** Studio operatörü (`admin@…`) organizasyon
75
+ kullanıcısıdır, uygulamanın kullanıcısı DEĞİLDİR; kimse onu otomatik kopyalamaz.
76
+
77
+ **Bu bir platform boşluğudur, kısa yolu yoktur.** İlk kullanıcıyı açmanın desteklenen tek yolu
78
+ aşağıdaki bootstrap desenidir: geçici bir uç aç, kullanıcıyı yaz, ucu hemen sil. Bakım/migration
79
+ uçlarını (`/organizations/sync-users` gibi `Sync*` uçları) **kullanma** — onlar ürün yüzeyinin
80
+ parçası değildir, özellik geliştirirken veri düzeltmek için yazılmışlardır ve kaldırılmaktadırlar.
81
+ Gateway de onları araç olarak yayınlamaz.
82
+
83
+ Bu tur, kimlik kuran her yeni projede ödenmek zorunda: iki koşuda da (09-02 ve 09-04) `users`
84
+ boş bulundu ve ikisi de bu deseni elle kurdu.
56
85
 
57
86
  **Bootstrap deseni** (crm koşumunda uygulandı):
58
87
 
@@ -107,3 +136,44 @@ koda baktığını gizlemek yerine raporlamak.
107
136
  - microservice → `microservice_execute`, beklenmedik sonuçta `microservice_debug`
108
137
  - workflow → `workflow_preview`; automation → `automation_trigger` + `automation_histories`
109
138
  - "API 200 döndü" tek başına bitti saymaz; politika bulgularını (`policyFindings`) oku ve gereğini yap
139
+
140
+
141
+ ## Yazdıktan sonra `silentlyDropped` alanına bak
142
+
143
+ Platformun bir alanı **hatasız yutması** bu depodaki en pahalı hata sınıfı: çağrı 200 döner,
144
+ `appliedPatch` uygulandığını söyler, `*_get` değeri geri verir ve o alan hiç işe yaramaz.
145
+ 2026-09-05 koşusunda kaybedilen sürenin çoğu sekiz böyle çağrıdan geldi.
146
+
147
+ Gateway yazma tool'larında gönderdiğin gövdeyi kaydedilenle karşılaştırır ve farkı yanıta
148
+ `silentlyDropped` olarak koyar (`microservice_*`, `query_*`, `flow_*`, `dto_*`, `workflow_*`,
149
+ `layout_create`, `page_schema_patch`). Boş değilse o alan **kaydedilmemiştir**:
150
+
151
+ ```jsonc
152
+ "silentlyDropped": [
153
+ "steps[stepId=sync_filters].args.expression: gönderilen \"state.page.month = …\" ↔ kaydedilen yok"
154
+ ]
155
+ ```
156
+
157
+ Diff dizi öğelerini indeksle değil KİMLİKLE eşler (`stepId`, `elementId`, `name`, `id`), çünkü
158
+ platform diziyi kendi sırasında geri döndürür. Diff üretilemeyen tek durum, yanıtın kayıt değil
159
+ kabul gövdesi olmasıdır; o zaman alan hiç görünmez ve doğrulamayı `*_get` ile kendin yaparsın.
160
+
161
+ ## `unmappedMembers`: anahtarın komutta HİÇ olmadığını söyler
162
+
163
+ İki kanal aynı yanıtta çıkabilir ve **kanıt güçleri farklıdır**:
164
+
165
+ - `silentlyDropped` bir SEZGİSELDİR: gönderdiğin gövdeyi kaydedilenle karşılaştırır. Platform bir
166
+ alanı başka adla saklarsa ya da id'yi yeniden atarsa yanlış alarm verebilir.
167
+ - `unmappedMembers` OTORİTERDİR: o anahtar gönderdiğin komutun şemasında **yoktur**. Platform onu
168
+ hata vermeden yok sayar, yani yazdığını sandığın değer hiç kaydedilmez.
169
+
170
+ ```jsonc
171
+ "unmappedMembers": ["indexes[0].isUnique"]
172
+ ```
173
+
174
+ Bu satırı gördüğünde alanı silme — **doğru adı bul**. Şema `ngapps://schemas/<Komut>` altındadır
175
+ (ör. `ngapps://schemas/UpdateEntityCommand`) ve gerçek maliyeti ölçülmüştür: `unique` yerine
176
+ `isUnique` yazılan bir index UNIQUE OLMADAN kurulur ve çağrı 200 döner.
177
+
178
+ Kanal yalnız senin yazdığın anahtarları bildirir; gateway'in bir kaydı okuyup geri yazarken
179
+ taşıdığı alanlar (`slug`, `datasourceId`, `audit` gibi) burada görünmez.
@@ -12,10 +12,22 @@ kutusu layout modülünde süzülmez — tüm katalog kullanılabilir.
12
12
 
13
13
  **2. Şema alanının adı `schema` değil `views`.** (LAY-001)
14
14
 
15
- **3. Micro-op hattı YOKTUR.** `page_schema_patch`'in layout karşılığı yok (backend'de
16
- `PATCH /layouts/.../schema` ucu yok). Değişiklik `layout_update` ile yazılır; gateway kaydı
17
- önce okur ve yalnız verdiğin alanları değiştirir, `views` göndermezsen ağaç korunur.
18
- Okumak için `layout_schema_get` (summary kipi hedef bulmak için yeterlidir).
15
+ **3. Micro-op ucu yok, ama `PATCH /layouts/{app}/{id}` var.** `page_schema_patch`'in birebir
16
+ karşılığı yoktur (`PATCH /layouts/.../schema` ucu yok); onun yerine platform genel bir JSON Patch
17
+ ucu verir ve `layout_patch` onu kullanır: layout'u okur, JSON Patch'i ve kendi normalizasyonlarını
18
+ uygular, doğrulamadan geçirir ve **farkı** `{ baseVersion, ops }` olarak yazar — tam gövde PUT'u
19
+ atılmaz. Böylece tek bir başlık düzeltmesi için 12 düğümlük ağacı yeniden göndermezsin (ölçüm:
20
+ 09-04 koşusu Y-25). Yol İNDEKS yerine SEÇİCİ alır:
21
+ `/views/0/children[elementId=baslik]/props/content/data`.
22
+
23
+ **Sürüm ve 409.** Okunan kaydın `version` alanı `baseVersion` olarak gider. Sen okuduktan sonra
24
+ başka biri yazdıysa platform **409** döner ve HİÇBİR op uygulanmaz; doğru tepki "aynı op'ları
25
+ tekrar göndermek" değil, `layout_schema_get` ile yeniden okuyup patch'i güncel hâle göre kurmaktır.
26
+ `version` bir **string**tir (mikrosaniye cinsinden zaman damgası, JavaScript'in güvenli tam sayı
27
+ sınırının üstünde) — sayıya çevirme, olduğu gibi geri gönder. `expectedUpdatedAt` verirsen gateway
28
+ ayrıca istek gitmeden önce yerel bir kontrol daha yapar. Tam ağacı kendin yazacaksan `layout_update`;
29
+ `views` göndermezsen mevcut ağaç korunur. Okumak için `layout_schema_get` (summary kipi hedef bulmak
30
+ için yeterlidir).
19
31
 
20
32
  **4. TEK slot.** Sayfa şeması **her** slot'a ayrı ayrı kopyalanır — iki slot, sayfayı iki
21
33
  kez çizer ve `elementId`'leri tekrarlar. (LAY-002 / LAY-006)
@@ -45,7 +57,9 @@ runtime hem Studio tuvali bölgeleri 12 kolonluk ızgarayla yerleştirir.
45
57
  **7. Mobilde flex YIĞILMAZ.** Runtime'ın mobil kuralı yalnız ızgara özelliklerini ezer.
46
58
  Telefonda alt alta düşmesi gereken satırı `layout:'grid'` ile kur; `layout:'flex'` +
47
59
  `direction:'row'` telefonda da yan yana kalır (375px'te ölçüldü: ızgara çocukları 375/375px
48
- alt alta, flex çocukları 49/310px yan yana).
60
+ alt alta, flex çocukları 49/310px yan yana). Not: `wrap` varsayılanı artık açık (2026-09-01) —
61
+ sığmayan çocuk alt satıra SARAR ama bu ızgara yığılması değildir; dar ölçülü çocuklar yan
62
+ yana kalmaya devam eder.
49
63
 
50
64
  **8. Doğrulama tek yerden:** `page_runtime_get`. Layout'un kendi serve URL'i yoktur —
51
65
  gerçek çıktıyı, layout'a bağlı bir sayfanın runtime paketinde görürsün (slot yerleşimi
@@ -67,9 +81,12 @@ container isWrapper:true heightType:'fill' layout:'flex' direction:'row' gap:
67
81
  └ slot heightType:'fill' block w12 y6
68
82
  ```
69
83
 
70
- - **Genişlik kolon payıdır:** yan menü `w:2` = 12'de 2 → 1440px tasarım genişliğinde 240px.
71
- Sabit piksel istiyorsan `appearance.widthPx: 240` (yalnız flex çocuğunda okunur; ızgara
72
- kabında okunmaz).
84
+ - **Genişlik `appearance.widthType`'tan gelir (2026-09-01); kolon payı fallback'tir.**
85
+ Yan menü için `widthType: 'fixed'` + `widthString: "240px"` (serbest CSS uzunluğu:
86
+ `"50%"`, `"10vw"`, `calc(...)` de geçer; yalnız flex kabın çocuğunda okunur, mutlak
87
+ ızgara kabında okunmaz). `'fill'` kabın tüm genişliğini alır, `'auto'` içeriğe oturur;
88
+ `widthType` HİÇ yazılmazsa `w:2` = 12'de 2 → 1440px tasarım genişliğinde 240px payı
89
+ geçerlidir. String yalnız `'fixed'` modda okunur — tek başına `widthString` ölüdür.
73
90
  - **Yükseklik `fill`'dir**, `size.h` değil: kök ekranı doldurur, yan menü ve içerik sütunu
74
91
  kökün bıraktığını, slot da üst şeritten artanı alır. Slot'un grid satırı ayrıca açık
75
92
  uçludur (`y / auto`), yani sayfa uzadıkça slot uzar.
@@ -115,6 +132,29 @@ bağlantı nesnesi ya da tam yol).
115
132
  // flex anlamının devreye girmemesi.
116
133
  ```
117
134
 
135
+ ## Çeviri: header/sidebar etiketini anahtara bağlamak
136
+
137
+ Layout'un `views` ağacındaki bir etiketi çeviriye bağlamak için **bütün ağacı yeniden yazma**.
138
+ `layout_patch` `set_prop_type` micro-op'unu tanır ve prop'un tipini yerinde değiştirir:
139
+
140
+ ```jsonc
141
+ { "op": "set_prop_type", "target": "app_header_title", "prop": "content",
142
+ "propType": "translation", "ref": "layout.header.title" }
143
+ ```
144
+
145
+ - Anahtar **`ref`'te** yaşar, `data`'da değil — runtime `data`'yı her turda çözümlenmiş değerle
146
+ ezer. `ref` boşsa gateway isteği göndermeden reddeder.
147
+ - Mevcut statik metin `data`'da yedek olarak KALIR, böylece Studio canvas'ında boş kutu görünmez.
148
+ - `set_prop_type` ile JSON Patch aynı `ops` listesinde karışık verilebilir; micro-op'lar önce
149
+ uygulanır, sonra patch yolları çözülür.
150
+ - Aynı op sayfa tarafında `page_schema_patch` ile çalışır — sözleşme birebir aynıdır. Anahtarları
151
+ `translation_list` ile bulun; fallback zinciri ve `t:` doğrulama mesajı sözleşmesi
152
+ `platform_guide('translation')`'da.
153
+
154
+ > Layout'un diğer micro-op'ları YOKTUR: `add_component`, `remove_component`, `replace_prop` ve
155
+ > arkadaşları yalnız sayfa hattındadır. Layout'ta düğüm eklemek/silmek için JSON Patch ya da
156
+ > `layout_update` kullanılır.
157
+
118
158
  ## Sayfayı layout'a bağlama
119
159
 
120
160
  `page_update` ile `layoutId`. Bağlı sayfanın kendi şeması slot'un içine girer; sayfanın
@@ -140,8 +180,11 @@ bunlardan biri olabilir:
140
180
  (`mapPageLayoutToPreview`), `packages/ng-apps-ui/src/shared/render/render.functions.ts`
141
181
  (`resolveNodeBoxStyle` — slot'un `y / auto` satırı, flex çocuk genişliği),
142
182
  `packages/ng-apps-ui/src/shared/render/RenderComponent.svelte` (mobil media kuralı),
143
- `packages/ng-apps-screen-builder/src/flex-layout.ts` (`getFlexChildStyle` — `widthPx` ve
144
- `heightType:'fill'` dalı; tuval ve runtime aynı fonksiyonu okur),
183
+ `packages/ng-apps-screen-builder/src/flex-layout.ts` (`getFlexChildStyle` — `widthType`
184
+ fill/fixed/auto dalları, `normalizeCssSize` kabul kuralı; tuval ve runtime aynı fonksiyonu okur),
185
+ `apps/studio/src/features/page/components/appearance/appearance-size.functions.ts` (panelin
186
+ mod↔string yazım kuralları), `apps/studio/src/components/screen-builder/quick-stack.functions.ts`
187
+ (Quick Stack iskeleti; `docs/features/quick-stack.md`),
145
188
  `apps/studio/src/features/layout/layout.functions.ts` (`getDefaultLayout` — Kural 5a/5b'deki iskelet),
146
189
  `docs/components/layout-composition.md`, `docs/specs/2026-08-28-free-layout-responsive-design.md`,
147
190
  `docs/issues.md` ARC-031 / BE-002 / CQ-143.
@@ -43,23 +43,119 @@ servis formunda görünür ve `microservice_get` outline görünümünde geri ok
43
43
  (varsayılan 20) ve `offset` alır. Kurulum sırası: `view_create` (aranacak tablo/alanlar +
44
44
  analizör) → `view_rebuild` → `source:'view'` microservice. Satış özeti / top-N gibi
45
45
  rapor işleri view'e DEĞİL raw query'e aittir (`query_create` + query kaynaklı uç).
46
+ **Alan kapsamı:** view kaynaklı uç `selectFields` boşken yalnız view'ın indekslediği
47
+ alanları + birincil anahtarı döndürür. İndekslenmemiş kolon (maaş, TCKN) kendiliğinden
48
+ gelmez; daha fazlasını istiyorsan `selectFields` ile açıkça iste (İK koşusu M-7).
46
49
  - `workflow`: çok adımlı iş mantığı gerekiyorsa microservice'i workflow'a bağla
47
- (`workflowId`).
50
+ (`workflowId`). **Yanıtı açık bir DTO sözleşmesine bağlamanın tek yolu da budur** — DTO
51
+ alanları uçta değil akıştadır; bkz. aşağıda §"Yanıt sözleşmesi (DTO) uca DEĞİL workflow'a
52
+ bağlanır".
48
53
  - `query`: yalnız ham SQL gerçekten şartsa. Politika lint'i her query kullanımında gerekçe
49
54
  ister (MS-002, warn).
50
- 2. **`selectFields` asla boş bırakılmaz.** Boş = platform TÜM alanları döndürür; join'li
51
- sorguda maliyet katlanır. Join varken boş bırakmak block'tur (MS-001).
55
+
56
+ > **BOŞ STRING PARAMETRE `NULL`'A ÇEVRİLİR — sorgu sessizce 0 satır döner.** Platform
57
+ > önizleme/çalıştırma parametrelerini normalize ederken boş ya da yalnız boşluktan oluşan
58
+ > değeri `null` yapar. `WHERE company_id = @company` yazıp `company: ''` gönderirsen koşul
59
+ > `= NULL` olur ve **hiçbir satırla eşleşmez**: HTTP 200, boş sonuç, hata yok, uyarı yok.
60
+ > Üç koşuda üst üste ölçüldü (09-02, 09-04 birinci ve dördüncü koşu) ve üçünde de teşhis
61
+ > ancak `SELECT (@p IS NULL)` gibi bir sonda ile yapılabildi.
62
+ >
63
+ > **Parametre listesi ZORUNLUDUR.** `@token` kullanan bir sorguyu `query_create` ile
64
+ > kaydederken her belirteci `parameters` içinde `{ name, type, value }` olarak bildir. Eksik
65
+ > bırakılırsa istek **400** döner ve mesaj hangi belirtecin bildirilmediğini adıyla söyler.
66
+ > (2026-09-04'ten önce aynı durum `500 "Please correct the query syntax."` veriyordu ve SQL'i
67
+ > suçluyordu; kaydetme yolu gövdeyi önce bir kez çalıştırıp yanıt şemasını ürettiği için
68
+ > bağlanmamış belirteç sözdizimi hatası gibi görünüyordu.) `value` yalnız kayıt anında
69
+ > kullanılır: SQL doğrulanır ve `returnScheme` ondan üretilir.
70
+ > Yorum satırındaki `{{ad}}`/`@ad` belirteçleri parametre sayılmaz — kaydetme yolu
71
+ > sorguyu yorumlarından arındırdıktan sonra tarar. SQL çalıştırılamazsa kayıt **yine de**
72
+ > oluşur: `returnScheme` boş kalır ve yanıttaki `message` nedeni söyler; SQL'i düzeltip
73
+ > `query_update` ile kaydet.
74
+ >
75
+ > "Filtre yok" demek istiyorsan iki doğru kalıptan birini kullan:
76
+ > `(@company IS NULL OR c.company_id::text = @company)` ya da bir sentinel değer
77
+ > (`'all'`) ve SQL'de ona bakan bir dal. Aynı davranış workflow query adımında da geçerli;
78
+ > oradaki anlatım `platform_guide('workflow')` "Query adımı" bölümünde.
79
+ >
80
+ > **KAPSAM UYARISI: query kaynaklı ucun parametreleri ÇAĞRIDAN gelir.** Middleware'in
81
+ > kurduğu `user` bu yola girmez — kimlik/tenant kapsamını gövdeye yazan bir uç
82
+ > sahtelenebilir (ölçüldü 2026-09-07). Kapsam sunucuda zorlanacaksa sorguyu bir workflow
83
+ > query adımından çağır; reçete aşağıda §"Kimlik kapsaması × uç kaynağı".
84
+ 2. **`selectFields` asla boş bırakılmaz.** Boş = platform TÜM alanları döndürür (view kaynaklı
85
+ uçta: yalnız view'ın indekslediği alanlar + birincil anahtar); join'li sorguda maliyet
86
+ katlanır. Join varken boş bırakmak block'tur (MS-001).
52
87
  3. **Join'ler ilişkilerden türetilir.** `sourceParameters.joins` öğesi:
53
88
  `{ alias, entityId, fieldId, path, joins }` — iç içe join mümkündür. Her join'in getirdiği
54
89
  alanlar da `selectFields`'ta `{ alias, fieldId, path, fieldSlug }` ile açıkça seçilir.
55
90
  **`path` boş bırakılamaz** — Studio join'li seçimleri yalnız `path` üzerinden eşleştirir
56
91
  (aşağıdaki bölüme bak).
92
+ 3b. **Kayıtlı ucu önizleme:** `microservice_preview` tam gövde istemez — `id` verirsen gateway
93
+ tanımı platformdan çeker ve yalnız `previewParameters`'ı senden alır (09-04 Y-09). Şekil
94
+ `{ body: [{…}] }` · `{ queryString: {…} }` · `{ routeParams: {…} }`; **tanımsız bir şekil
95
+ sessizce yutulur** ve bütün sayaçlar 0 döner (09-02 M-2).
57
96
  4. **HTTP sözleşmesi:** `type: 'rest-api'` + `httpMethod: get|post|put|delete` + `url`
58
97
  (route şablonu). Zamanlanmış işler `type: 'schedule-job'`, kuyruk tüketicileri `consumer`.
98
+ 4b. **`query_preview` yazma sorgusunu ÇALIŞTIRIR, GERİ ALIR ve BUNU SÖYLEMEZ.**
99
+ `INSERT`/`UPDATE`/`DELETE` önizlediğinde `affectedRowCount` **gerçek** sayıyı döndürür ama
100
+ transaction geri alınır ve tabloda satır kalmaz. **Yanıtta rollback işareti yoktur** — yalnız
101
+ `{ affectedRowCount, elapsedMilliseconds }` gelir (2026-09-07'de ölçüldü; `rolledBack` alanı
102
+ yalnız OKUYAN sorgunun yanıt şeklinde var ve orada hep `false`). Yani sayıya bakan çağıran
103
+ yazdığını sanır: 09-04 4. koşusunda 624 satırlık bir seed böyle "yazıldı" sanıldı. Kalıcı
104
+ yazma için sorguyu kaydedip query kaynaklı bir uçtan ya da workflow'dan çalıştır; kalıcılığın
105
+ tek kanıtı sonradan çekilen `row_list`'tir. **Var olan satırlardan türetilmiş satır üretmenin
106
+ uçtan uca reçetesi** (anahtar sözleşmesi dâhil) `platform_guide('entity')` §"Toplu ve
107
+ türetilmiş satır" bölümündedir; aynı davranış `platform_guide('workflow')` "Query adımı"
108
+ bölümünde de yazılıdır.
109
+
110
+ 4c. **Sayfalama toplamı `Total-Count` header'ından gelir.** Sayfalı bir uç kurduğunda toplam
111
+ kayıt sayısı gövdede değil header'dadır (`microservice_execute` yanıtında `headers` altında,
112
+ küçük harfle `total-count`). Tabloya bağlarken sayfa göstergesinin okuduğu sözleşme budur;
113
+ ayrıntı `platform_guide('page')` sayfalama bölümü. Alternatif olarak sorgunun kendisi
114
+ `COUNT(*) OVER()` ile toplamı satıra koyabilir — o zaman header'a hiç bağımlı kalmazsın.
115
+
59
116
  5. **Doğrulama akışı:** `microservice_preview` (kaydetmeden) → kaydet → `microservice_execute`
60
117
  (gerçek runtime yolu) → sonuç beklenmedikse **aynı parametrelerle `microservice_debug`** —
61
118
  adım adım executionItems ağacı döner.
62
119
 
120
+ ## Yanıt sözleşmesi (DTO) uca DEĞİL workflow'a bağlanır (MS-018, block)
121
+
122
+ `returnDtoId` · `parameterDtoId` · `returnBehavior` · `parameterBehavior` **microservice
123
+ komutunda yoktur** (`Create/UpdateMicroserviceCommand`). Gövdeye yazarsan platform 200 döner ve
124
+ alanı sessizce düşürür: uç sözleşmesiz kalır, ne hata ne uyarı gelir (2026-09-09 koşusu `S-2`).
125
+ Gateway bu gövdeyi artık MS-018 ile **platforma hiç göndermeden** reddeder.
126
+
127
+ Yanıt şeklini kaynak belirler:
128
+
129
+ | Kaynak | Yanıt şeklini ne belirler | Açık DTO sözleşmesi |
130
+ |---|---|---|
131
+ | `table` | `sourceParameters.selectFields` (+ join alanları) | yok |
132
+ | `query` | sorgunun `returnScheme`'i — kayıt anında SQL'den üretilir | yok |
133
+ | `view` | view'ın indekslediği alanlar + birincil anahtar (`selectFields` ile genişletilir) | yok |
134
+ | `workflow` | akışın `returnDtoId` + `returnBehavior` sözleşmesi | **var** |
135
+
136
+ Yani "yanıtın alanları şu DTO'dan olsun" diyeceksen uç **workflow kaynaklı** kurulur:
137
+
138
+ 1. `dto_list` ile mevcut DTO'yu bul (platform entity başına DTO üretir) ya da `dto_create` → `dtoId`
139
+ 2. `workflow_create` → `returnDtoId: dtoId` + `returnBehavior: 'object' | 'array' | 'primitive'`.
140
+ WF-002: `type:'workflow'` akışta `returnDtoId` **zorunludur** (yoksa akış `unready` kalır);
141
+ `returnBehavior`'ı boş bırakırsan gateway `object` ile doldurup `gatewayNormalized` der.
142
+ 3. `microservice_create` → `source: 'workflow'` + `workflowId` — DTO alanı **uca yazılmaz**.
143
+ 4. Aynı uç `middlewareIds` ile kimlik kapsamasını da taşır (§"Kimlik kapsaması × uç kaynağı").
144
+
145
+ Kayıtlı bir akışa sözleşmeyi sonradan bağlamak tek çağrıdır:
146
+ `workflow_patch(id: <workflowId>, ops: [{ op: 'add', path: '/returnDtoId', value: <dtoId> }, { op: 'add', path: '/returnBehavior', value: 'object' }])`.
147
+
148
+ **Sözleşmenin bağlandığını uçtan oku:** workflow kaynaklı uçta `microservice_get` yanıtındaki
149
+ `returnScheme` **DTO'nun alanlarını** taşır; table/query/view kaynaklı uçta şema kaynaktan türer.
150
+ Bağlanmamış sözleşmenin belirtisi budur — uç 200 döner ama `returnScheme` DTO'yu değil kaynağı
151
+ yansıtır (2026-09-09'da ölçüldü: `returnDtoId` yazılan table kaynaklı uçta şema `['id','name']`
152
+ kaldı, DTO'nun beş alanı gelmedi).
153
+
154
+ Sözleşme yalnız **2xx** yanıtlarda uygulanır: 401/422 döndüren bir `response` adımının gövdesi
155
+ DTO'dan süzülmez (WF-026 — `platform_guide('workflow')` §Hata yolu). Sözleşme **süzer, uydurmaz**:
156
+ `bodyExpression`'ın ürettiği DTO dışı alan yanıttan düşer, DTO'da olup üretilmeyen alan gelmez
157
+ (ölçüm: `gizliNot` alanı yanıtta yok, `slot_minutes` de yok).
158
+
63
159
  ## Asgari doğru table-source microservice (platformda doğrulanmış kurallar)
64
160
 
65
161
  - `operation` enum'u **tam olarak** şudur ([MicroserviceTableOperation](../../../../zeyrek-backend/src/NgApps.Domain/Contexts/Studio/Microservices/MicroserviceTableOperation.cs)):
@@ -69,12 +165,26 @@ servis formunda görünür ve `microservice_get` outline görünümünde geri ok
69
165
  yapmasını iki ayrı uç + akışta condition dallanmasıyla kurmayın: tek `operation: 'upsert'`
70
166
  ucu yeterlidir. Aynı işlem workflow **table step**'inde de desteklenir
71
167
  ([TableStep](../../../../zeyrek-backend/src/NgApps.Domain/Contexts/Flow/Workflows/Steps/TableStep.cs)).
168
+
169
+ - **Upsert eşleşmesi `filters` ile kurulur, PK `fields`'a KONMAZ** (ölçüm: İK koşusu M-4,
170
+ 2026-09-01). PK'yı `fields`'a koyarsan `AlwaysGeneratedFieldCantBeWritten` alırsın. Doğru şekil:
171
+ `filters`'a `source: "body"` + `isRequired: false` bir id filtresi; gövdede id **varsa** eşleşen
172
+ satır güncellenir, **yoksa** yeni satır açılır. `isRequired: true` yaparsan "yeni kayıt" yolu
173
+ kapanır (id gelmeyince uç reddeder).
174
+
175
+ > **Upsert'te üretilen alan tuzağı.** `operation: 'upsert'` UPDATE yolunda da alan ifadelerini
176
+ > yeniden değerlendirir; sicil/kod gibi üretilen bir değeri `source: 'expression'` ile yazarsan
177
+ > güncellemede **sessizce değişir** (İK koşusu M-3: EMP-0005 → EMP-0006). Alan bazlı
178
+ > `writeOn: create | update | always` platformda **yok**; o gelene kadar oluşturma ve güncelleme
179
+ > için iki ayrı uç kurun. Gateway **MS-016** ile uyarır.
72
180
  - **`count` vardır** — kayıt sayısı için tüm satırları çekip uzunluğuna bakmayın; panel/KPI
73
181
  sayaçlarının doğru şekli budur.
74
182
  - Gövde kökünde **`entityId` zorunlu**. (Eski "kökte datasourceId zorunlu" bilgisi yanlıştı —
75
183
  komutta öyle bir alan yok, gönderilirse yok sayılır; asıl zorunluluk selectFields'taki fieldId.)
76
- - `selectFields[]` öğelerinde **`fieldId` zorunlu** (yalnız `fieldSlug` yetmez); kök tablo
77
- alanlarında alias = **entity'nin slug'ı** ve `path` **yazılmaz**. Alias verilmezse
184
+ - `selectFields[]` / `filters[]` öğelerinde platform **`fieldId`** ister; gateway'e **`fieldSlug`**
185
+ vermen yeter — kök entity'nin alanları için id'yi gateway çözer ve `gatewayResolved` ile bildirir
186
+ (join'lenen entity'lerin alanlarında hâlâ `fieldId` gerekir). Kök `entityId` de slug olabilir.
187
+ Kök tablo alanlarında alias = **entity'nin slug'ı** ve `path` **yazılmaz**. Alias verilmezse
78
188
  `ArgumentNullException(key)`, farklı alias verilirse "missing FROM-clause entry" gelir.
79
189
  - `sort` ve `limit` **her zaman** verilir (MS-008, block) — aşağıya bak.
80
190
  - Filtrelerde `value` bir **JavaScript ifadesidir** — sabit metin tırnak içinde
@@ -90,6 +200,11 @@ servis formunda görünür ve `microservice_get` outline görünümünde geri ok
90
200
  `{ alias, fieldId, fieldSlug }`). Aynı kural workflow table create/update step'lerinde de
91
201
  geçerlidir. Boş `returnFields` ile write operasyonu yazarsan MS-009 uyarır; buna karşılık
92
202
  write operasyonunda boş `selectFields` **uyarı üretmez** (platform onu zaten yok sayar).
203
+ - **Platformun ürettiği alanları `fields` içine koymayın.** Primary key/identity gibi
204
+ `alwaysGenerated` alanlar yazılırsa istek 400 döner ve hatalı alan slug'larını sayar; bu alanlar
205
+ create'te platformca üretilir, upsert eşleşmesi gerekiyorsa değer `fields` yerine `filters`ta
206
+ taşınır. Incremental alanın create/update zamanlaması ayrı tasarım konusudur; mevcut sınır için
207
+ [`writeOn` tasarımına](../../../../zeyrek-backend/docs/alan-yazma-zamani/01-tasarim.md) bakın.
93
208
 
94
209
  ```jsonc
95
210
  {
@@ -208,6 +323,19 @@ karşılamayı imkânsız kılar. Karar kuralı:
208
323
  "isRequired": false, "children": [], "order": 0 }
209
324
  ```
210
325
 
326
+ > **`source: "query-string"` / `"body"` / `"header"` filtresinde `value` VERİLMEZ** (ölçüm: İK
327
+ > koşusu M-2, 2026-09-01). Değer çağrıdan gelir, parametre adı alan slug'ından türer. `value`
328
+ > yazarsan platform onu JS ifadesi sanıp eval eder → `JavaScriptException: <ad> is not defined`
329
+ > ile 500. `value` yalnız `source: "expression"` ve `"static"` kaynaklarında anlamlıdır. Sabit
330
+ > ön-süzme (tenant, aktiflik) `expression` ile yapılır. Gateway **MS-015** lint'i bu
331
+ > kombinasyonu istek platforma gitmeden yakalar.
332
+
333
+ > **Dış kaynaklı filtreye (`query-string` / `body` / `header`) `value` yazılmaz.** Değer çağrıdan
334
+ > gelir; tanımdaki `value` çalışma zamanında yok sayılır — parametre gelmezse filtre atlanır ve uç
335
+ > **süzülmemiş** tam liste döner (2026-09-03 öncesinde aynı alan JS olarak değerlendirilip 500
336
+ > veriyordu; İK koşusu M-2). Tanımdan gelen sabit değer istiyorsan `source: 'static'` ya da
337
+ > `'expression'` kullan.
338
+
211
339
  ### Dinamik filtre (`enableDynamicFilters`) — süzülebilir tablonun uç şekli
212
340
 
213
341
  `enableDynamicFilters: true` verilen uç, süzme koşullarını **tanımdan değil çağrıdan** alır.
@@ -267,12 +395,69 @@ hem `joins` ile getirin hem `selectFields`'a koyun; ondan sonra `filters[0][fiel
267
395
  > (qm kanıtı: `sy-users-get-my-profile`). Ayrıntı ve tarifler: `platform_guide('middleware')`.
268
396
  > Middleware bağlamadan `user.` okuyan expression yazarsan MS-011 uyarır — runtime'da
269
397
  > `user is not defined` 500'ü yersin.
398
+ >
399
+ > **`middlewareIds`'i `microservice_create` GÖVDESİNDE ver, sonradan patch'leme.** Ucu kurup
400
+ > ardından `microservice_patch` ile `/middlewareIds` yazmak her uç için fazladan bir tur demektir:
401
+ > 2026-09-06 koşusunda dokuz uç kuruldu ve dokuz patch çağrısının dokuzu da yalnız bu yolu yazdı
402
+ > (~3 dk). Alan create gövdesinde zaten kabul ediliyor.
270
403
 
271
404
  > Sayfa tarafındaki karşılığı: kullanıcı ekrandan süzüyorsa (`table` + `filterMode` açık, ya da
272
405
  > `datatable` + `isFilterable` kolonlar) uç `enableDynamicFilters: true` olmalı; komponent seçilen
273
406
  > koşulları kendi `filters` prop'una yazar ve akış onu uca `filters` parametresi olarak geçirir.
274
407
  > Süzme yoksa statik query-string filtreleri yeterlidir — bkz. `platform_guide('page')` Kural 1e.
275
408
 
409
+ ### Kimlik kapsaması × uç kaynağı — query kaynaklı uç BAĞLAM GÖRMEZ
410
+
411
+ Yukarıdaki `source: 'expression'` + `value: 'user.id'` kalıbı **yalnız table kaynaklı** uçlarda
412
+ vardır. Agregasyon/rapor uçları query kaynaklı olduğu için aynı kalıp orada yoktur ve
413
+ **parametrelerinin tamamı çağrıdan gelir** — kapsamı gövdeye yazan bir uç sahtelenebilir.
414
+
415
+ | Uç kaynağı | Parametre/filtre nereden gelir | Kapsam sunucuda zorlanır mı |
416
+ |---|---|---|
417
+ | `table` | `filters[]` · `source:'expression'`, `value:'user.id'` | **Evet** (MS-011 middleware bağını denetler) |
418
+ | `query` | Yalnız çağrının kendisi: **body → query-string → header** (`Query.PrepareQueryParameters` → `FindValue`) | **Hayır** — ifade değerlendirmesi yoktur |
419
+ | `workflow` | Adım ifadeleri; `user.*` bağlı middleware'den gelir | **Evet** (ölçüldü 2026-09-07) |
420
+ | `view` | Arama sözleşmesi (`searchText`) + view'ın indekslediği alanlar | **ölçülmedi** — kapsam şartsa workflow yolunu kullan |
421
+
422
+ Query parametresine ifade kaynağı **eklenemez**: `parameters[].source` komut şemasında yoktur,
423
+ platform anahtarı sessizce atar (gateway `unmappedMembers` ile söyler) ve kayıtlı şema
424
+ `source: 'body'` kalır. Bu bir eksik değil, modülün tanımı: query modülü SQL seviyesinde çalışan
425
+ ham SQL yüzeyidir, istek bağlamını bilmez.
426
+
427
+ **Ölçüm (2026-09-07, aynı sorgu, aynı middleware — middleware `user.owner_key = 'OWNER-A'` kurdu):**
428
+
429
+ | Uç | Gövde | Yanıt |
430
+ |---|---|---|
431
+ | `source:'query'` | `{ owner_key:'OWNER-A' }` | `{ owner_key:'OWNER-A', row_count:3, total:60 }` |
432
+ | `source:'query'` | `{ owner_key:'OWNER-B' }` | `{ owner_key:'OWNER-B', row_count:2, total:300 }` ← **başkasının verisi** |
433
+ | `source:'workflow'` | `{ owner_key:'OWNER-B' }` | `{ owner_key:'OWNER-A', row_count:3, total:60 }` ← gövde **yok sayıldı** |
434
+ | `source:'workflow'` | `{ owner_key:'OWNER-B', min_amount:20 }` | `{ owner_key:'OWNER-A', row_count:2, total:50 }` ← kapsam dışı filtre gövdeden okundu |
435
+
436
+ #### Reçete: kapsanmış agregasyon ucu
437
+
438
+ Sorguyu değiştirmeye gerek yok; onu **bir katman yukarıdan** çağırırsın.
439
+
440
+ 1. `query_create` — agregasyonu kapsam parametresiyle yaz:
441
+ `… WHERE company_id = @company_id AND created_at >= @from`, `parameters` ile her belirteci bildir.
442
+ 2. `workflow_create` (`type: 'workflow'`) — start → **query adımı** → response. Adımın bağlaması
443
+ kapsamı middleware'den, kalan filtreleri gövdeden alır:
444
+ `rule.expression = "{ company_id: user.company_id, from: input.from }"`
445
+ (bağlamanın iki yazımı ve canlı motorun tuzağı: `platform_guide('workflow')` §"Query adımı").
446
+ 3. `microservice_create` — `source: 'workflow'`, `workflowId`, **`middlewareIds` dolu**.
447
+ 4. Doğrula: aynı ucu gövdeye **başka birinin** kapsam değerini yazarak çağır; yanıt değişmemeli.
448
+
449
+ `middlewareIds` boşken bu akış `500 JavaScriptException: user is not defined` verir (ölçüldü) —
450
+ MS-011'in workflow tarafındaki karşılığıdır ve gateway o dizinin boş kalmasını göremez, çünkü
451
+ uçtan görünen tek şey `workflowId`'dir.
452
+
453
+ Middleware tarafındaki karşılığı ve `user` değişkenini kuran akış:
454
+ `platform_guide('middleware')` Tarif 3c.
455
+
456
+ **Her agregasyonu sarmalama.** Kapsam gerekmeyen bir özet (herkese açık istatistik, zaten
457
+ middleware'in reddettiği bir panelde tüm şirketleri gösteren yönetici raporu) düz `source:'query'`
458
+ kalır: fazladan workflow, DTO ve response adımı bedavaya gelmez. Ölçü basit — **bu parametreyi
459
+ çağıran seçebilmeli mi?** Hayırsa sarmala.
460
+
276
461
  ### `sort` ve `limit` her zaman verilir
277
462
 
278
463
  **MS-008 (block).** Platform bunları null bırakır; Studio formu ise `data.sort.name` /
@@ -285,6 +470,24 @@ Gateway bu kuralları MS-005/MS-006 olarak istek gitmeden denetler; platform val
285
470
  hatası alırsan `microservice_debug` çıktısıyla raporla — o zaman gerçek bir bug'dır.
286
471
  Query source'a kaçış yalnız ham SQL şartsa; query yazarken parser `SELECT` sonrası satır sonu
287
472
  kabul etmez — ilk kolon `SELECT` ile aynı satırda olmalı ("Please use a supported query type").
473
+ Kayıtlı query'nin `@ad` / `{{ad}}` belirteçleri uçta **body → query-string → header** sırasıyla
474
+ bağlanır; bunun için parametreler query kaydedilirken `parameters` ile bildirilmiş olmalıdır
475
+ (`platform_guide('workflow')` §"Query adımı"). Davranış `query_preview` ile aynıdır.
476
+
477
+ **`language` AYRILMIŞ bir parametredir.** Ham sorguda `{{language}}` (ya da `@language`)
478
+ kullanırsan platform onu isteğin diliyle kendisi doldurur — `parameters` listesine yazmana gerek
479
+ YOKTUR ve yazarsan doğrulama onu zorunlu saymaz. Çok dilli companion tablosunu elle join eden bir
480
+ raporun dili böyle alınır:
481
+
482
+ ```sql
483
+ SELECT p.id, COALESCE(l.title, d.title) AS title
484
+ FROM products p
485
+ LEFT JOIN products_localization l ON l.product_id = p.id AND l.language = {{language}}
486
+ LEFT JOIN products_localization d ON d.product_id = p.id AND d.language = 'en'
487
+ ```
488
+
489
+ `query_preview` da aynı parametreyi ekleyerek koşar, yani şema çıkarımı `{{language}}` yüzünden
490
+ "eksik parametre" diye patlamaz.
288
491
 
289
492
  ## Middleware'siz uçta çok adımlı yazım ATOMİK DEĞİL (ölçüm 2026-08-19)
290
493
 
@@ -336,13 +539,23 @@ kabul etti. Ayrıntı ve doğru rol kapısı tarifi: `platform_guide('middleware
336
539
  - `operation: 'list'` → geçersiz; `get` + `isSingle: false` kullanın (PLT-006)
337
540
  - Üst seviye `entityId` eksik (MS-004, warn)
338
541
  - Gerekçesiz `source: 'query'` (MS-002, warn)
542
+ - Backend zorunlulukları (MS-017, block — istek gitmeden): `name` ve `url` boş olamaz (update'te
543
+ `slug` da); `queryId`/`entityId`/`workflowId` verilirse boş GUID olamaz; `source: 'expression'`
544
+ alan ve filtrede `value` dolu; tekil filtrede `fieldId`; `writeOn` yalnız `operation: 'upsert'`
545
+ ucunda; join'de `entityId` + `fieldId`. Tam katalog: `ngapps://rules/backend-constraints`.
546
+ - Query kaydı (QRY-001, block): `rawQuery`'deki her `@ad` / `{{ad}}` `parameters[]`'ta
547
+ `{ name, type }` ile bildirilir — yalnız create denetler, update sessiz geçer ve sorgu çalışma
548
+ anında düşer; `slug`/`rawQuery`/`datasourceId` zorunlu; `outputType` `array` | `object`.
339
549
  - Execute'ta dönen `httpStatus` microservice'in kendi sonucudur — 4xx/5xx görünce önce
340
550
  `microservice_debug` ile hangi adımın ürettiğine bak, tanımı ona göre düzelt.
341
- - **`microservice_update` / `microservice_patch`, `operation: 'get'` tipli microservice'lerde
342
- HER ZAMAN 500 verir** (`The instance of entity type 'Application' cannot be tracked` —
343
- platform regresyonu, P-02 olarak düzeltilecek). Ayrım **operasyona** bağlıdır, tool'a değil:
344
- `operation: 'create' | 'update' | 'delete' | 'upsert'` kayıtlarında PUT/PATCH sorunsuz çalışır
345
- (crm koşumunda `update` uçlarında yeniden doğrulandı, 2026-08-25); yalnız `get` (liste ya da
346
- `isSingle`) uçlarında patlar. Get-tipi bir microservice'i değiştirmen gerekiyorsa **`microservice_delete` +
347
- `microservice_create`** kullan ve tanımı ilk POST'ta doğru üret; silme kümesini kendi
348
- ürettiğin slug listesiyle sınırla. `entity_patch`, `workflow_patch`, `row_update` sağlamdır.
551
+ - **`microservice_update` / `microservice_patch` tüm operasyon tiplerinde çalışır.** Eski
552
+ "`operation: 'get'` tipli kayıtta HER ZAMAN 500 (`Application cannot be tracked`)" kuralı P-02
553
+ regresyonuydu; 2026-08-16'da düzeltildi, 2026-09-01 İK koşusunda get ucunda
554
+ `microservice_patch` ile canlı doğrulandı (M-9). Get-tipi bir microservice'i değiştirmek için
555
+ artık delete+create gerekmez. `entity_patch`, `workflow_patch`, `row_update` sağlamdır.
556
+ - **`microservice_patch` tam gövde geri yazar: okunan kayıtta olmayan alan SİLİNİR.** 09-04
557
+ koşusunda gidiş-dönüşte `description` düştü ve MS-014 patch'i bloke etti (Y-16). Gateway artık
558
+ açıklamasız bir patch gövdesini hiç göndermez; hata mesajı düzeltmeyi verir
559
+ (`{ "op": "add", "path": "/description", "value": "…" }`). Patch yanıtındaki `silentlyDropped`
560
+ alanı, gönderilen ama kaydedilmeyen alanları listeler — boş değilse platform bir alanı yutmuş
561
+ demektir.