@appaflytech/zeyrek-ai-mcp 0.0.1

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 (88) hide show
  1. package/.env.example +46 -0
  2. package/README.md +260 -0
  3. package/dist/config.js +128 -0
  4. package/dist/index.js +88 -0
  5. package/dist/knowledge/componentCatalog.js +438 -0
  6. package/dist/knowledge/componentEvents.js +164 -0
  7. package/dist/knowledge/coverage.js +60 -0
  8. package/dist/knowledge/normalize.js +1309 -0
  9. package/dist/knowledge/outlines.js +167 -0
  10. package/dist/knowledge/policy.js +44 -0
  11. package/dist/knowledge/reservedTables.js +75 -0
  12. package/dist/knowledge/rules.js +1725 -0
  13. package/dist/observability/trace.js +337 -0
  14. package/dist/platform/errors.js +132 -0
  15. package/dist/platform/http.js +169 -0
  16. package/dist/platform/responses.js +94 -0
  17. package/dist/platform/token.js +79 -0
  18. package/dist/resources/examples/entity-pair-m2m.json +105 -0
  19. package/dist/resources/examples/flow-list-to-form.json +65 -0
  20. package/dist/resources/examples/flow-load-table.json +178 -0
  21. package/dist/resources/examples/microservice-table-list.json +49 -0
  22. package/dist/resources/examples/middleware-authenticate.json +194 -0
  23. package/dist/resources/examples/page-detail-profile.json +95 -0
  24. package/dist/resources/examples/page-dynamic-form.json +221 -0
  25. package/dist/resources/examples/page-form-upsert.json +279 -0
  26. package/dist/resources/examples/page-html-embed.json +106 -0
  27. package/dist/resources/examples/page-sign-in.json +194 -0
  28. package/dist/resources/examples/workflow-basic.json +94 -0
  29. package/dist/resources/guides/component.md +108 -0
  30. package/dist/resources/guides/entity.md +348 -0
  31. package/dist/resources/guides/expressions.md +139 -0
  32. package/dist/resources/guides/getting-started.md +109 -0
  33. package/dist/resources/guides/html.md +106 -0
  34. package/dist/resources/guides/microservice.md +313 -0
  35. package/dist/resources/guides/middleware.md +271 -0
  36. package/dist/resources/guides/modify-existing.md +35 -0
  37. package/dist/resources/guides/page.md +1574 -0
  38. package/dist/resources/guides/view.md +89 -0
  39. package/dist/resources/guides/workflow.md +359 -0
  40. package/dist/resources/knowledge.js +44 -0
  41. package/dist/resources/schemas/CreateDatasourceCommand.json +54 -0
  42. package/dist/resources/schemas/CreateDiscoveredEntitiesCommand.json +133 -0
  43. package/dist/resources/schemas/CreateEntityCommand.json +387 -0
  44. package/dist/resources/schemas/CreateMicroserviceCommand.json +528 -0
  45. package/dist/resources/schemas/CreateQueryCommand.json +43 -0
  46. package/dist/resources/schemas/CreateViewCommand.json +158 -0
  47. package/dist/resources/schemas/CreateWorkflowCommand.json +1719 -0
  48. package/dist/resources/schemas/Filter.json +124 -0
  49. package/dist/resources/schemas/GetRowsQuery.json +122 -0
  50. package/dist/resources/schemas/PreviewMicroserviceCommand.json +565 -0
  51. package/dist/resources/schemas/PreviewQueryCommand.json +74 -0
  52. package/dist/resources/schemas/PreviewWorkflowCommand.json +981 -0
  53. package/dist/resources/schemas/StepDto.json +900 -0
  54. package/dist/resources/schemas/UpdateDatasourceCommand.json +58 -0
  55. package/dist/resources/schemas/UpdateEntityCommand.json +679 -0
  56. package/dist/resources/schemas/UpdateMicroserviceCommand.json +512 -0
  57. package/dist/resources/schemas/UpdateQueryCommand.json +86 -0
  58. package/dist/resources/schemas/UpdateViewCommand.json +153 -0
  59. package/dist/resources/schemas/UpdateWorkflowCommand.json +1719 -0
  60. package/dist/resources/schemas.js +40 -0
  61. package/dist/server.js +63 -0
  62. package/dist/tools/access.js +27 -0
  63. package/dist/tools/applications.js +59 -0
  64. package/dist/tools/automations.js +103 -0
  65. package/dist/tools/common.js +18 -0
  66. package/dist/tools/components.js +280 -0
  67. package/dist/tools/datasources.js +82 -0
  68. package/dist/tools/dependencies.js +17 -0
  69. package/dist/tools/deployments.js +17 -0
  70. package/dist/tools/dtos.js +54 -0
  71. package/dist/tools/entities.js +166 -0
  72. package/dist/tools/files.js +138 -0
  73. package/dist/tools/flows.js +226 -0
  74. package/dist/tools/folders.js +51 -0
  75. package/dist/tools/layouts.js +105 -0
  76. package/dist/tools/microservices.js +198 -0
  77. package/dist/tools/organizations.js +65 -0
  78. package/dist/tools/pages.js +597 -0
  79. package/dist/tools/platformInfo.js +44 -0
  80. package/dist/tools/projects.js +152 -0
  81. package/dist/tools/queries.js +48 -0
  82. package/dist/tools/rows.js +115 -0
  83. package/dist/tools/screens.js +97 -0
  84. package/dist/tools/templates.js +65 -0
  85. package/dist/tools/variables.js +94 -0
  86. package/dist/tools/views.js +58 -0
  87. package/dist/tools/workflows.js +196 -0
  88. package/package.json +49 -0
@@ -0,0 +1,94 @@
1
+ {
2
+ "_meta": {
3
+ "synthetic": true,
4
+ "note": "hr projesinde doğrulanmış şekil. Kurallar: step/branch id'leri GERÇEK GUID ve global benzersiz (RowAlreadyExists); DtoId verilen yerde Behavior zorunlu; table rule'da datasourceId + fieldId zorunlu; filtre value JavaScript ifadesi; preview girdisi previewPayload anahtarıyla."
5
+ },
6
+ "definition": {
7
+ "name": "Product Lookup",
8
+ "slug": "product-lookup",
9
+ "type": "workflow",
10
+ "parameterDtoId": "<girdi DTO id: { productName: string }>",
11
+ "returnDtoId": "<çıktı DTO id: { name, price }>",
12
+ "parameterBehavior": "object",
13
+ "returnBehavior": "object",
14
+ "steps": [
15
+ {
16
+ "id": "<GUID-1 üret: crypto.randomUUID()>",
17
+ "name": "Start",
18
+ "type": "start",
19
+ "branches": [
20
+ {
21
+ "id": "<GUID-2>",
22
+ "name": "start",
23
+ "type": "success",
24
+ "nextStepId": "<GUID-3>"
25
+ }
26
+ ]
27
+ },
28
+ {
29
+ "id": "<GUID-3>",
30
+ "name": "Load Product",
31
+ "type": "table",
32
+ "rule": {
33
+ "stepType": "table",
34
+ "entityId": "<products entity id>",
35
+ "operation": "get",
36
+ "isSingle": true,
37
+ "selectFields": [
38
+ {
39
+ "alias": "<entity slug>",
40
+ "fieldSlug": "name",
41
+ "path": "",
42
+ "fieldId": "<name alanı id>"
43
+ },
44
+ {
45
+ "alias": "<entity slug>",
46
+ "fieldSlug": "price",
47
+ "path": "",
48
+ "fieldId": "<price alanı id>"
49
+ }
50
+ ],
51
+ "filters": [
52
+ {
53
+ "fieldSlug": "name",
54
+ "operator": "eq",
55
+ "value": "input.productName",
56
+ "fieldId": "<name alanı id>"
57
+ }
58
+ ],
59
+ "joins": [],
60
+ "datasourceId": "<datasource id>"
61
+ },
62
+ "branches": [
63
+ {
64
+ "id": "<GUID-4>",
65
+ "name": "success",
66
+ "type": "success",
67
+ "nextStepId": "<GUID-6>"
68
+ },
69
+ {
70
+ "id": "<GUID-5>",
71
+ "name": "fail",
72
+ "type": "fail",
73
+ "nextStepId": null
74
+ }
75
+ ]
76
+ },
77
+ {
78
+ "id": "<GUID-6>",
79
+ "name": "Response",
80
+ "type": "response",
81
+ "rule": {
82
+ "stepType": "response",
83
+ "bodyExpression": "{ name: load_product.data.name, price: load_product.data.price }",
84
+ "httpStatusCode": 200,
85
+ "headerExpression": "{ }"
86
+ },
87
+ "branches": []
88
+ }
89
+ ]
90
+ },
91
+ "automationVariant": {
92
+ "note": "Automation: returnDtoId/parameterDtoId YASAK (validator reddeder), returnBehavior 'object' verilebilir; state=unready NORMALDİR ve tetiklemeyi etkilemez. Tetikleme kapısı automation nesnesindeki entityId + operation + isQueue (false=anlık, true=kuyruk; null=asla tetiklenmez!). steps hem üst seviyede hem automation içinde (PLT-007); response yasak, hata yolu revert."
93
+ }
94
+ }
@@ -0,0 +1,108 @@
1
+ # Komponent Rehberi (katalog, ortak katman, olay modeli)
2
+
3
+ Sayfaya konan her şeyin ortak kuralları. Kuruluş sırası ve şema gövdesi için
4
+ `platform_guide('page')`; HTML kaçış kapısının derin sözleşmesi için `platform_guide('html')`.
5
+
6
+ ## Keşif: iki tool, üç bilgi katmanı
7
+
8
+ ```
9
+ component_list → hangi tipler var (slug, kategori, droppable) — kompakt
10
+ component_get → tek tipin TAM sözleşmesi, üç katman birleşik:
11
+ props/defaults/sizes (platform sözlüğü — tip/enum/default)
12
+ events[].payload/reads (olay yükü sözleşmeleri)
13
+ knowledge (davranış bilgisi: ne işe yarar, nasıl davranır)
14
+ ```
15
+
16
+ - `knowledge` alanı yoksa davranış sözlüğe işlenmemiştir — **tahmin etme**.
17
+ - Yanıtta `guide` dönerse (`html` gibi) o tip için derin rehber vardır; ilk kullanımdan önce oku.
18
+ - Şemaya **uydurma prop yazma**: platform 200 döner ama prop sessizce kaybolur. Tek istisna
19
+ `isHidden` — katalogda görünmese de her komponentte çalışır (ortak render katmanı çizer).
20
+
21
+ ## Ortak katman gerçekleri (RenderComponent — her komponent için geçerli)
22
+
23
+ - **`isHidden`** her komponenti gizler; setter ile yazmak katalogda listelenmeyen tiplerde de
24
+ çalışır. Gizli komponent `mount` olayını görünür olana dek TETİKLEMEZ.
25
+ - **`mount`** olayını komponentin kendisi değil ortak katman yayar: komponent ilk kez
26
+ görünür olduğunda bir kez. Sayfa açılış akışı `page_container.mount`'a bağlanır.
27
+ - **`name` = `elementId`** kuralı: runtime her komponent için `const <name> = Components[...]`
28
+ üretir; bozuk ad sayfadaki BÜTÜN ifadeleri öldürür (`platform_guide('page')` Kural 1c-2).
29
+ - **Boyut:** `blocks[].size` katalogdaki `maxSize`'ı aşamaz; yükseklikte
30
+ `appearance.heightType: 'auto'` tercih edilir. Katalog varsayılanı bazı tiplerde kendi
31
+ sınırını aşar — körü körüne kopyalama, gateway kırpar ve bildirir.
32
+
33
+ ## Olay modeli
34
+
35
+ - Olay adları `component_get.events`'ten okunur, **`on` öneki almaz** (tek istisna
36
+ `texteditor`: onMount/onChange). Uydurma ada bağ sessizce ölü kalır — gateway PAGE-008 ile
37
+ reddeder.
38
+ - Yük `events[].payload`'dadır ama akışa **`event.data` ile GELMEZ**: motor yükü komponentin
39
+ KENDİ prop'una yazar (`FlowMachine.onEmit` → `setNode`) ve akışı emit'in `params`'ıyla başlatır.
40
+ Yani `payload` anahtarları prop adlarıdır ve `self.<anahtar>` ile okunur (ya da
41
+ `value.type:'property'`); `reads` alanı yükte olmayan prop'ları söyler. `event.data` yalnız
42
+ `html` komponentinin `fireEvent` aksiyonlarında dolar.
43
+ - Akış bağlama: `flow_create` → `page_schema_patch set_event`. İstisna: `html` komponentinin
44
+ aksiyonları kendi `flow` prop'uyla bağlanır (`platform_guide('html')`).
45
+
46
+ ## Stil
47
+
48
+ Her komponentin `style` prop'u vardır ve iç parçalara iner (`textinput.style`: `&__label` ·
49
+ `&__input` · `&__error`; `form.style`: `&__title` · `&__header` · `&__body`). `html`
50
+ komponentinde `style` serbest bir emotion CSS bloğudur. Tasarımı varsayılanda bırakmak
51
+ zorunda değilsin.
52
+
53
+ ## Widget: kayıt → sayfaya koyma → akışlar
54
+
55
+ Widget, kendi komponent ağacını taşıyan yeniden kullanılabilir pakettir (`kind:'widget'`,
56
+ `widgetType: container | modal | drawer`).
57
+
58
+ 1. **Kayıt:** `component_create` — `kind:'widget'`, `widgetType`, `schema` (widget'ın kendi
59
+ ağacı; `name = elementId` kuralı burada da geçerli — layout gibi widget adları da kullanan
60
+ sayfaya sızar).
61
+ 2. **Sayfaya koyma:** `add_component` düğümünde `kind:'widget'` ve **`type` = widget KAYDININ
62
+ ID'si** — slug DEĞİL (runtime widget'ı `widget.id === node.type` ile çözer; slug yazarsan
63
+ kayıt 200 döner, render sessizce boş kalır — gateway **PAGE-012/LAY-005** ile reddeder).
64
+ `widgetType` alanını da düğüme yaz.
65
+ 3. **Akışlar:** widget'ın kendi flow'ları `flow_*` tool'larında `host:'component'`,
66
+ `hostId: <widget kaydının id'si>` ile yönetilir. Kayıtta adımlar widget şemasındaki somut
67
+ `elementId`'leri kullanır; Studio düzenleyicisinin gösterdiği `__widget__` yer tutucusu
68
+ yalnız editör katmanıdır (kayıt anında gerçek id'ye çevrilir) — API'den yazarken yer
69
+ tutucu KULLANMA.
70
+
71
+ ## `states` ve `appearance` sözlükleri
72
+
73
+ Her şema düğümünde iki sözlük vardır; ikisi de ortak render katmanında çözülür:
74
+
75
+ | `states` | Anlamı |
76
+ |---|---|
77
+ | `isDroppable` | İçine komponent alır (container/form/repeater true) |
78
+ | `isScrollable` | İçeriği kendi alanında kaydırır (sabit yükseklikte `overflow-y: auto`) |
79
+ | `isWrapper` | Sayfanın/layout'un/widget'ın ANA kabı — salt düzen elemanı: `height:100%` + **`min-height:80vh`**, kap başlığı ÇİZİLMEZ. **Yalnız sayfa boyu kaplaması gereken tek kap için kullan:** iç içe kutuya verirsen 720px'lik pencerede o kutu 36px içerikle bile 576px çizilir (ölçüldü, `platform_guide('page')` Kural 1f). Çerçeve/başlık gizlemek için wrapper değil `style` / `hideBorders` kullan; `states` için micro-op olmadığından düzeltmek düğümü yeniden kurmayı gerektirir |
80
+ | `isSticky` | Kaydırmada yerinde kalır (header/sidebar; z-index'i otomatik) |
81
+
82
+ > **Çocukların DİZİLİMİ `states`'te değil kabın PROP'undadır:** `container` — ve dolayısıyla
83
+ > `page_container` — `layout: 'flex' | 'grid'` + altı ayar (`direction`, `horizontalAlign`,
84
+ > `verticalAlign`, `size`, `gap`, `wrap`) taşır. Katalog varsayılanı `flex`'tir ama varsayılan
85
+ > düğüm kurulurken yazılır: `layout` prop'u hiç yoksa renderer IZGARA çizer, o yüzden şemayı sen
86
+ > kuruyorsan açıkça yaz. Ortalama/yaslama/aralık işini `style` ile değil bu grupla kur:
87
+ > `platform_guide('page')` Kural 1f-2.
88
+
89
+ | `appearance` | Değerler | Anlamı |
90
+ |---|---|---|
91
+ | `heightType` | `auto` · `fixed` | `auto`: içerik kadar (önerilen — Kural 1d); `fixed`: `blocks.h × 10px` |
92
+ | `marginType` / `marginValue` | `normal` · `none` · `custom` + px | Dış boşluk: normal=5px, custom=`marginValue` |
93
+ | `paddingType` / `paddingValue` | `normal` · `none` · `custom` + px | Droppable kapların iç boşluğu: normal=10px |
94
+ | `alignType` | `left` · `center` · `right` | İçerik hizalama (destekleyen komponentlerde) |
95
+ | `widthType` | `cover` · `contain` | Görsel/medya sığdırma davranışı |
96
+ | `aspectRatio` | sayı | En-boy oranı kilidi (medya komponentleri) |
97
+
98
+ `isHidden` bu sözlüklerde değil PROP'tur ve her komponentte çalışır; flow'lar `margin` /
99
+ `padding` / `height` prop adlarıyla da appearance'ı setter üzerinden değiştirebilir
100
+ (RenderComponent bu üç prop'u appearance'a çevirir).
101
+
102
+ ## Komponent seçimi
103
+
104
+ "Hangi işe hangi komponent" tablosu ve `table` / `datatable` karşılaştırması:
105
+ `platform_guide('page')` Kural 1e ve 7e. Kaçınılacak tek şey yarım kurulumdur (süzme
106
+ arayüzü açık, arkası statik uç). Hazır komponentlerin hiçbiri işi karşılamıyorsa `html`
107
+ kaçış kapısıdır — yeteneklerini `platform_guide('html')` anlatır; "platform bunu yapamaz"
108
+ demeden önce onu oku.
@@ -0,0 +1,348 @@
1
+ # Entity Rehberi
2
+
3
+ Buradaki kurallar gerçek projelerde (qm, hr, sms, klinik) **deneyerek doğrulandı**.
4
+
5
+ ## Slug seçimi — isim uzayı platformla paylaşımlıdır
6
+
7
+ Her environment şeması ~75 platform tablosu taşır ve entity slug'ları bu tablolarla aynı isim
8
+ uzayındadır. Çakışan slug `Migration failed … relation "X" already exists` verir ve **entity
9
+ kaydı hiç oluşmaz**. Rezerve adlardan bilinenler: `branches`, `applications`, `caches`,
10
+ `components`, `datasources`, `entities`, `fields`, `files`, `flows`, `folders`, `layouts`,
11
+ `microservices`, `pages`, `permissions`, `projects`, `queries`, `queued_events`, `roles`,
12
+ `steps`, `users`, `variables`, `views`, `workflows`. Alan adı bunlardan biriyse eş anlamlısını
13
+ seç (ör. şube/branş için `branches` değil `specialties`).
14
+
15
+ > **Ama `users`/`roles`/`files`/`folders` bir istisna:** bunlar "dolu olduğu için kaçınılacak"
16
+ > adlar değil, **platformun sana verdiği hazır entity'ler**. Kopyasını yapmadan önce aşağıdaki
17
+ > bölümü oku.
18
+
19
+ ## Hazır entity'ler — kimlik tablosu ZATEN VAR
20
+
21
+ Her projeye seed sırasında 9 entity açılır ve **id'leri her kurulumda aynı sabitlerdir**
22
+ (`Defaults` sınıfı) — aramana gerek yok, doğrudan `entity_get` ile çekebilirsin:
23
+
24
+ | slug | id | Ne için |
25
+ |---|---|---|
26
+ | `users` | `01917ede-330e-7254-8544-eef0db3693af` | **Kimlik.** `id` (uuid PK) · `email` (display, UNIQUE) · `password` · `first_name` · `last_name` |
27
+ | `roles` | `01917ede-88a2-72ea-8397-2619e5d5dca0` | `page_create`/`microservice` `roleIds`'inin okuduğu roller (`role_list`) |
28
+ | `user_roles` | `01917ede-be1e-7d62-9a7c-d0ce4e4daaaf` | kullanıcı ↔ rol |
29
+ | `permissions` · `role_permissions` | `01917ede-a457-…` · `01917ede-d450-…` | yetki matrisi |
30
+ | `files` · `folders` | `0191dbf2-39c4-…` · `0191df94-f635-…` | Drive; `FILE` alanları buraya bağlanır (`entity_list`'te GİZLİ) |
31
+ | `audit_trails` · `audit_trail_values` | `01961f53-25c3-…` · `01961f53-3552-…` | denetim izi |
32
+
33
+ **`users` gerçek bir entity'dir**, sahte bir sistem tablosu değil: `entity_get` alanlarını verir,
34
+ `RELATION` alanı hedef alabilir, table step / microservice okur, `row_create` yazar, kendi default
35
+ DTO'su vardır. Auth middleware tarifi zaten bunu okur — `ngapps://examples/middleware-authenticate`
36
+ `entityId` olarak `users`'ı kullanır.
37
+
38
+ ### Neden gözden kaçıyor
39
+
40
+ `entity_list`'i **`applicationId` ile** çağırdığında hazır entity'ler listede **görünmez**: seed
41
+ onlara `EntityApplication` kaydı açmaz, filtre de `EntityApplications.Any(...)` ile süzer. Kimlik
42
+ tablosunu görmek için `entity_list`'i `applicationId` VERMEDEN çağır (ya da yukarıdaki id'yi
43
+ doğrudan kullan).
44
+
45
+ ### Kullanırken bilinmesi gerekenler
46
+
47
+ - **Silinemez** (`CantDeleteDefaultEntityException`) ve default alanları (`email`, `password`…)
48
+ silinemez. Buna karşılık **`entity_patch` ile alan EKLENEBİLİR** — telefon, departman, avatar,
49
+ `musteri_tipi` gibi profil alanları `users`'a eklenebilir.
50
+ - **Eklediğin alan `nullable: true` olmalı.** Platform bu tabloya EF üzerinden de yazıyor
51
+ (`organization_sync_users`) ve EF senin kolonunu bilmiyor; NOT NULL + varsayılansız bir kolon o
52
+ yolu kırar.
53
+ - **`password` alanı `Hasher.Hash`'lidir.** Satır yazarken hash'i ifadeyle ürettir
54
+ (`crypto.SHA256.encode(...)` — gerçek algoritma için `platform_guide('expressions')`); düz metin
55
+ yazarsan giriş hiçbir zaman eşleşmez.
56
+ - **`isAuditTrailActive`'i AÇMA.** `users` tablosunun audit kolonları EF tarafında `Ignore`
57
+ edilmiş; `sys_*` eklemek EF'in bilmediği kolonlar yaratır.
58
+ - **`organization_sync_users` Studio operatörlerini bu tabloya süper yönetici rolüyle kopyalar**
59
+ (yalnız production dışı environment'lara). Yani tablo hem platform operatörünü hem uygulama
60
+ kullanıcısını taşır ve roller `page.roleIds` yetkilendirmesiyle ortaktır.
61
+ - **İlişki aynı datasource içinde kurulur.** `users` built-in datasource'tadır; entity'lerin dış
62
+ bir DB'deyse `users`'a ilişki kuramazsın.
63
+
64
+ ### Karar: `users`'ı genişlet mi, ayrı entity mi?
65
+
66
+ **Karar senin** — ikisi de geçerli. Ayrım şu: `users` **kimliktir** (giriş, `user.id` kapsaması,
67
+ roller), domain kaydı değil.
68
+
69
+ - **Kimlik alanları** (e-posta, parola, ad, telefon, avatar, dil tercihi) → `users`'a ekle.
70
+ - **Domain verisi** (siparişler, kredi limiti, öğrencinin sınıfı, temsilcinin iş yükü) → ayrı
71
+ entity + `users`'a `one-to-many`. Bir kişi aynı anda iki rolde olabilir (hem temsilci hem
72
+ talep sahibi), `users` da silinemeyen/şeması kısmen kilitli bir tablodur.
73
+
74
+ **Kaçınılacak tek şey:** kendi entity'ne `email` + `password` koyup **ikinci bir kimlik sistemi**
75
+ kurmak. O noktada iki ayrı kullanıcı tablosu, iki ayrı hash düzeni ve `middlewareIds`/`roleIds`
76
+ ile hiç konuşmayan bir yetkilendirme olur. Gateway bu şekli ENT-009 ile hatırlatır (warn — bilerek
77
+ yapıyorsan devam et).
78
+
79
+ ## Alan tipleri (dbType + commonType İKİSİ birden verilir)
80
+
81
+ | dbType (BÜYÜK HARF zorunlu) | commonType |
82
+ |---|---|
83
+ | `UUID` | `guid` |
84
+ | `VARCHAR`, `TEXT` | `text` |
85
+ | `BOOL` | `bool` |
86
+ | `INT2`, `INT4`, `INT8` | `number` |
87
+ | `DATE` | `date` |
88
+ | `TIME` | `time` |
89
+ | `TIMESTAMP`, `TIMESTAMPTZ` | `datetime` |
90
+ | `JSONB` | `custom` |
91
+ | `FILE` | `file` |
92
+ | `RELATION` | `relation` |
93
+ | `Incremental` (tam bu yazım) | `incremental` |
94
+
95
+ - Kontrol **büyük/küçük harfe duyarlı** — küçük harf tümüyle reddedilir. `Incremental` bu
96
+ tablodaki tek karma yazımdır (`PostgreSqlConnector` onu ayrı bir tip grubu olarak yayınlar;
97
+ fiziksel tip `TEXT`'tir, bu yüzden kayıttan sonra `entity_get` `dbType: "TEXT"` +
98
+ `commonType: "incremental"` gösterir — bu normaldir, alanı geri çevirme).
99
+ - **Ondalık tip YOK:** `NUMERIC`, `FLOAT4/8`, `MONEY`, `DECIMAL` reddedilir (datasource'un
100
+ `dataTypes` listesi yanıltır, ona güvenme). Birim konvansiyonu kullan ve birimi
101
+ `description`'a yaz: para → `INT8` kuruş (`*_minor`), süre → `INT4` dakika, yüzde →
102
+ `INT4` ×100, puan → `INT4` ×10.
103
+ - `commonType`'sız `UUID`/`BOOL` "isn't a supported database type" hatasıyla reddedilir.
104
+
105
+ ## Alan tipine karar verme (önce buna bak, sonra yaz)
106
+
107
+ Yanlış tip seçmek sessiz teknik borçtur: satırlar yazılır, servisler 200 döner, ama Studio
108
+ formu doğru bileşeni gösteremez ve veri serbest metne dönüşür. Sırayla sor:
109
+
110
+ 1. **Sabit bir değer kümesi mi?** (durum, öncelik, dönem, unvan, sınav tipi) → **enum**:
111
+ `valueOptions` dolu + `isEnum: true`. Serbest `VARCHAR` bırakma.
112
+ - **Aynı anda birden fazla değer seçilebilecekse → multi-enum**: `VARCHAR` +
113
+ `relationType: 'many-to-many'` + `valueOptions`; `relationId` ve `isEnum` **verilmez** —
114
+ platform `<slug>_values` enum-values entity'sini ve junction'ı kendisi üretir
115
+ (`relationMetadata.enumValuesEntityId` yanıtta dolu döner).
116
+ 2. **Dosya mı?** → `dbType: 'FILE'` + `commonType: 'file'`; tekil dosya `relationType: 'one-to-many'`,
117
+ çoklu dosya `relationType: 'many-to-many'`. `relationId` **verme** — platform yerleşik Files
118
+ entity'sini kendisi bağlar (`one-to-one` verilirse `one-to-many`'ye çevrilir).
119
+ 3. **Başka bir kayda referans mı?** → `RELATION` (yukarıdaki ilişkiler bölümü).
120
+ 4. **Sıralı, insan okuyabilir bir kod mu?** (talep no `TLP-0001`, teklif no, fatura no) →
121
+ **`Incremental`** — aşağıdaki bölüm. Primary key ile karıştırma: PK teknik kimliktir,
122
+ incremental kullanıcıya gösterilen iş numarasıdır; ikisi aynı tabloda birlikte bulunur.
123
+ 5. **Serbest metin / sayı / tarih mi?** → tablodaki temel tipler.
124
+
125
+ > **m2m/FILE/multi-enum alanı iki aşamada kurulur — gateway bunu artık kendisi yapar.**
126
+ > Create yolu junction entity kaydını açar ama **fiziksel tabloyu kurmaz**
127
+ > (`EntityService.CreateAsync` migration'ı yalnız ana entity için koşar); sonuç 500
128
+ > `42P01 relation "…" does not exist` ve alan silinemez de. `entity_create` bu alanları
129
+ > gövdeden ayırır, entity'yi onlarsız kurar, sonra bir güncelleme turunda ekler (update
130
+ > migration'ı junction tabloyu kurar) ve yanıtta `gatewayNormalized` ile bildirir — gövdeye
131
+ > yazmaya devam edebilirsin. Ham HTTP ile çalışıyorsan sırayı elle uygula.
132
+
133
+ **Enum yazmıyorsan gerekçen olsun.** `courses.semester` ("2025-2026 Güz") bileşik bir etiket
134
+ olduğu için serbest metindir; `course_assignments.semester` (Güz/Bahar/Yaz) enum'dur.
135
+
136
+ ```jsonc
137
+ { "name": "Durum", "slug": "status", "dbType": "VARCHAR", "commonType": "text",
138
+ "relationType": "none", "nullable": false,
139
+ "isEnum": true,
140
+ "valueOptions": [
141
+ { "value": "Aktif", "icon": "#47ad10" },
142
+ { "value": "Bırakıldı", "icon": "#888888" },
143
+ { "value": "Tamamlandı", "icon": "#0052CC" }
144
+ ] }
145
+ ```
146
+
147
+ - `valueOptions[].icon` bir **hex renktir**, ikon adı değil.
148
+ - `isEnum` Studio tarafında `valueOptions.length > 0` ile türetilir; yine de ikisini birlikte ver.
149
+ - **Enum PostgreSQL'de CHECK constraint üretir.** Var olan bir metin alanını enum'a çevirirken
150
+ tablodaki değerler `valueOptions` içinde yoksa migration `check constraint ... is violated` ile
151
+ düşer. Sıra: **önce satırları kanonik değerlere taşı, sonra alanı enum yap.**
152
+
153
+ ## Artan kod alanı (`Incremental`)
154
+
155
+ Sıralı, önekli, insan okuyabilir kod üretir (`TLP-0001`). Alan tanımı:
156
+
157
+ ```jsonc
158
+ { "name": "Talep No", "slug": "ticket_no",
159
+ "dbType": "Incremental", "commonType": "incremental",
160
+ "relationType": "none", "nullable": false, "unique": true,
161
+ "properties": { "isCaseSensitive": false } }
162
+ ```
163
+
164
+ **Kod satır yazılırken üretilir ve üretimi ALANA YAZDIĞIN DEĞERİN TİPİ belirler**
165
+ (`EntityService.ApplyIncrementalValuesAsync`) — `row_create`, `row_update` ve workflow table
166
+ create step'inde aynı kural geçerlidir:
167
+
168
+ ```jsonc
169
+ // NESNE verirsen: platform kodu üretir ve değeri onunla DEĞİŞTİRİR
170
+ "ticket_no": { "Prefix": "TLP", "Separator": "-", "Counter": 1, "DigitCount": 4 } // → "TLP-0001"
171
+
172
+ // STRING verirsen: olduğu gibi yazılır, üretim ATLANIR (veri taşımada işine yarar)
173
+ "ticket_no": "TLP-0002"
174
+ ```
175
+
176
+ Girdi sınırları (`IncrementalCodeInput.TryParse`; ihlal edilirse nesne geçersiz sayılır ve kod
177
+ üretilmez): `Prefix` ≤ 3 karakter · `Separator` ≤ 1 karakter · `Counter` ≥ 1 ·
178
+ `DigitCount` 1–12.
179
+
180
+ - **Sayaç kendini onarır:** üretim var olan en yüksek koddan devam eder
181
+ (`EntityIncrementSequenceService.ResolveStartingCounterAsync`), yani elle `TLP-0005` yazdıktan
182
+ sonra nesne biçimiyle yazılan ilk satır `TLP-0006` olur. `Counter: 1` vermek "1'den başla"
183
+ demek değil, "taban 1" demektir.
184
+ - `properties.isCaseSensitive` büyük/küçük harf duyarlılığını belirler (`Field.IsCaseSensitiveIncremental`).
185
+ - Sayfa tarafındaki karşılığı `incrementalinput` komponentidir.
186
+ - `datasource_list.dataTypes` çıktısında bu tipi ayrı bir grup olarak görürsün; listede
187
+ görmezsen yine de geçerlidir (rehberdeki "dataTypes'a güvenme" uyarısı burada da geçerli).
188
+
189
+ ## Primary key
190
+
191
+ ```jsonc
192
+ // GUID anahtar
193
+ { "name": "ID", "slug": "id", "dbType": "UUID", "commonType": "guid",
194
+ "primaryKey": true, "nullable": false, "unique": true, "identity": true,
195
+ "defaultValueType": "none", "defaultValue": "",
196
+ "properties": { "generationType": 0 }, "relationType": "none" }
197
+
198
+ // INT anahtar (auto-increment) — dbType ve commonType dışında birebir aynı
199
+ { "name": "ID", "slug": "id", "dbType": "INT4", "commonType": "number",
200
+ "primaryKey": true, "nullable": false, "unique": true, "identity": true,
201
+ "defaultValueType": "none", "defaultValue": "",
202
+ "properties": { "generationType": 0 }, "relationType": "none" }
203
+ ```
204
+
205
+ - **GUID zorunlu değildir.** `UUID/guid` ve `INT2|INT4|INT8 / number` ikisi de geçerli birinci
206
+ sınıf seçeneklerdir; platform kuralı GUID dayatmaz (bu, backend repo'sunun `Guid.CreateVersion7()`
207
+ kuralıyla karıştırılmamalı — o C# tarafının kuralı, platform verisinin değil). Yüksek hacimli /
208
+ sıralı okunması doğal tablolarda (yoklama, log, hareket) `INT4` tercih edilebilir; dağıtık
209
+ üretim, dışa açık id, birleştirilecek veri varsa `UUID`.
210
+ - **`identity: true` + `properties.generationType: 0` olmadan anahtar otomatik üretilmez** ve her
211
+ satırda id elle gönderilmek zorunda kalır. `generationType` datasource seçenekleri:
212
+ `0 = Always`, `1 = ByDefault`.
213
+ - `properties.generationType` **yalnız primary key'de** anlamlıdır; identity olmayan alanlarda
214
+ Studio bu anahtarı zaten siler.
215
+ - Doğrulama: satır yazdıktan sonra `row_list` ile geri oku — id üretilmişse (`1, 2, 3…` ya da
216
+ UUIDv7) anahtar doğru kurulmuştur.
217
+
218
+ Gateway bu iki kuralı ENT-006 (PK identity/tip uyumu) ve ENT-007 (enum şekli) olarak denetler.
219
+
220
+ ## Application bağı — ne yapar, ne yapmaz
221
+
222
+ `applicationIds` **create'te platformca bağlanır** (`CreateEntityCommand` → `EntityService.CreateAsync`
223
+ → `Entity` ctor). Gateway'in ayrıca patch atmasına gerek yok.
224
+
225
+ Bağın gerçek etkisi ikidir:
226
+
227
+ - `EntityRepository.cs:167` — `entity_list`'in `applicationId` filtresi, yani **Studio'nun
228
+ application bazlı entity listesi**. Bağsız entity o listede görünmez.
229
+ - `MicroserviceRepository.cs:556` — bağımlılık/etki analizi sorgusu.
230
+
231
+ **Satır uçları bu bağa İHTİYAÇ DUYMAZ.** Bağsız bir entity'de `row_create` / `row_list` sorunsuz
232
+ çalışır (16 satırlık bağsız tabloda doğrulandı). Eski "bağsız entity'de satır uçları 500
233
+ 'Sequence contains no matching element' döner" bilgisi **yanlıştı**, kod tarafında karşılığı yok.
234
+
235
+ **Bağ auto page ÜRETMEZ.** Auto page üreten
236
+ `UpdatePageWhenEntityUpdatedEventHandler`, `EntityApplications`'a değil `PageSettings`'e bakar
237
+ (`PageSettings` boşsa hiç çalışmaz) ve `PageSettings` yalnız `UpdateEntityCommand.AutoPages`
238
+ ile dolar. 10 entity'nin tamamı bağlıyken `entity_page_settings = 0`, `pages = 1` ölçüldü.
239
+
240
+ Karar: **Studio'dan yönetilecek entity'lerde `applicationIds` ver; yalnız API'den kullanılacaksa
241
+ boş bırakmak geçerli bir tercihtir** (ENT-002 warn'ı bunu hatırlatır, zorunlu kılmaz).
242
+
243
+ > `entity_patch` uyarısı: `EntityDto` çıktıda `applications` (nesne listesi) döner ama
244
+ > `UpdateEntityCommand` girdide `applicationIds` (Guid listesi) okur. Gateway bu çeviriyi
245
+ > kendisi yapar; ham PUT atarken **siz yapmazsanız her güncelleme bağı sessizce siler.**
246
+
247
+ ## İlişkiler (doğru şekil — 57 alanla doğrulandı)
248
+
249
+ ```jsonc
250
+ { "name": "Department", "slug": "department_id",
251
+ "dbType": "RELATION", "commonType": "relation",
252
+ "relationType": "one-to-many",
253
+ "relationId": "<hedef entity id>",
254
+ "nullable": true }
255
+ ```
256
+
257
+ - `relation` alt nesnesini (`primaryKeyId`, `displayFieldId`, ...) **platform kendisi doldurur**
258
+ — elle verme.
259
+ - `relationType`: `one-to-one | one-to-many | many-to-many`.
260
+ - **m2m yalnız FARKLI iki entity arasında kurulabilir.** Kendine referanslı m2m (ör. ders ↔
261
+ ön koşul ders) platformda `500 "Operation is not valid due to the current state of the object"`
262
+ ile reddedilir. Kendine referans gerekiyorsa ara entity + iki one-to-many kullan.
263
+ - m2m değerleri satırlara **id dizisi** olarak yazılır (`{"qualified_courses": ["<id>", "<id>"]}`),
264
+ okurken `[{ id, <displayAlanı> }]` döner.
265
+ - **m2m mi, ara entity mi:** bağın kendisi ekstra alan taşımıyorsa (ürün↔etiket) → m2m alanı
266
+ (Studio çoklu seçim UI'ı bedava). Bağın kendi alanları varsa (tarih, puan, durum) → ayrı
267
+ entity + iki yana one-to-many.
268
+ - İki entity'yi önce ilişkisiz oluşturup ilişki alanını sonra `entity_patch` ile eklemek en
269
+ güvenli sıra (id'ler ancak oluşunca belli olur).
270
+ - `display: true` entity başına tek alanda olabilir; ilişkilerde görüntülenen değer bu alandır.
271
+
272
+ ## TEXT kolonunda JSON: yazarken string, okurken NESNE döner
273
+
274
+ Şema ile ifade edilemeyen değişken yapıları (anket cevabı, ayar bloğu, anlık görüntü) `TEXT`
275
+ kolonda JSON metni olarak tutmak meşru bir desendir — ama **gidiş ve dönüş şekli aynı değildir**;
276
+ bilmezsen "kayıt bozulmuş" sanırsın.
277
+
278
+ - **Yazarken string ver.** Microservice/flow parametresinde `JSON.stringify(...)` gönder; alan
279
+ `commonType: text` olduğu için dizi/nesne göndermek doğru değildir.
280
+ - **Okurken platform onu senin için AYRIŞTIRIR.** `DictionaryExtensions.ToNested()`
281
+ (`NgApps.Infrastructure/Extensions/DictionaryExtensions.cs:49-64`) her string değere bakar;
282
+ kırpılmış hâli `{`…`}` ya da `[`…`]` ile başlayıp bitiyorsa `ToObject<object>()` ile parse eder,
283
+ patlarsa string olarak bırakır. Yani `row_list` / uç yanıtı sana **hazır nesne/dizi** verir —
284
+ ikinci kez `JSON.parse` etme.
285
+ - **Yan etki:** JSON'a benzeyen ama JSON olmayan metinler (`[taslak]`, `{sablon}`) de bu kontrole
286
+ girer; parse başarısız olunca string kalır, veri bozulmaz.
287
+
288
+ > **Studio tablosunda `[object Object],[object Object]` görmek NORMALDİR ve veri sağlamdır.**
289
+ > Grid hücresi değeri `row[cell.key]?.toString()` ile basıyor
290
+ > (`apps/studio/src/components/data-grid/DataGrid.svelte:125`); parse edilmiş bir dizide bu
291
+ > ifade `[object Object],…` üretir. Ölçüm (anket projesi, 2026-08-19): grid'de `[object Object]`
292
+ > görünen satırın `row_list` çıktısı `question_id / question_text / type / value_text /
293
+ > option_ids` alanlarıyla eksiksizdi. Gerçek içeriği görmek için grid'e değil `row_list`'e bak.
294
+ > Kolonu tabloda okunur göstermek gerekiyorsa özet bir alan daha tut (ör. `answer_summary` TEXT)
295
+ > ya da sayfada `repeater`/`html` ile render et.
296
+
297
+ ## Satır işlemleri
298
+
299
+ - `row_create/row_update` anahtarları **field slug'larıdır** (name değil): `{"code": "IK"}`.
300
+ - Yazdıktan sonra `row_list` ile geri oku; ilişki alanları `{ id, name }` nesnesi olarak döner.
301
+ - **`isAuditTrailActive: true` ile KURULAN entity'ye satır yazılamaz.** `sys_*` kolonları create
302
+ yolunda oluşmaz, yalnız ilk **etkin** `entity_update`'inde eklenir; o ana kadar **her**
303
+ `row_create` şu hatayla 500 verir:
304
+
305
+ ```
306
+ Sequence contains no matching element [InvalidOperationException]
307
+ ```
308
+
309
+ Gateway'in bu vakadaki "entity bir application'a bağlı değil (PLT-004)" ipucu **yanlış yöne
310
+ gönderir** — entity bağlıyken de aynı hata çıkar, `row_list` çalışır, yalnız yazma kilitlidir.
311
+ `true → true` aynı-değer patch'i **saymaz** (etkisiz update). İki güvenli yol: entity'yi
312
+ `isAuditTrailActive: false` ile kur, ya da create'ten sonra `false → true` turunu at.
313
+
314
+ > `Sequence contains no matching **element**` (audit kolonu eksik) ile
315
+ > `Sequence contains no **elements**` (satır yok — genelde preview rollback'i) **farklı**
316
+ > sorunlardır; karıştırmak teşhisi saptırır.
317
+
318
+ - **Audit izi ne zaman gerçekten yazılır?** `CreateAuditTrailCommandHandler` şu üç durumda
319
+ **hiç kayıt açmaz**: `IsAuditTrailActive == false`, `AuditTrailType == null`, ya da
320
+ `AuditTrailType == simple`. Yani iz tutulması için `isAuditTrailActive: true` **ve**
321
+ `auditTrailType: "basic" | "detailed"` birlikte gerekir. Enum yalnız üç değer tanır —
322
+ `simple` · `basic` · `detailed` (`AuditTrailType.cs`); `advanced` diye bir değer **yoktur**,
323
+ gönderirsen bağlama hatası alırsın.
324
+ - **Audit izi IP adresi tutar.** `audit_trails.IpAddress`, isteğin
325
+ `HttpContext.Connection.RemoteIpAddress` değerinden doldurulur (`UserContext.cs`) ve satır
326
+ olaylarıyla (`Row.Created/Updated/Deleted` → `UserIpAddress`) audit consumer'a taşınır; kullanıcı
327
+ id'si ve e-postası da aynı yolla gider.
328
+
329
+ > **Anonim veri toplayan tablolarda `isAuditTrailActive: false` bırak** — anket cevabı, geri
330
+ > bildirim, başvuru gibi "kimliksiz" vaadi olan her tabloda. `false` iken hiçbir gizli kolon ya
331
+ > da yan kayıt oluşmaz; `true` + `basic/detailed` iken "anonim" dediğin satırın yanında IP
332
+ > durur. Bu, ürünün gizlilik vaadini sessizce bozan tek ayardır.
333
+
334
+ - **`defaultValueType: "static"` sessizce `"none"`e düşer** — DB varsayılanı kurulmaz. Bu yüzden
335
+ her `BOOL`/sayısal alanı **her** `row_create`'te ve her workflow write adımında **açıkça** yaz;
336
+ "varsayılanı platform koyar" varsayımı `null` satır üretir.
337
+ - **`row_list`'in `filters` parametresi çalışmıyor** — denenen her biçim
338
+ `Failed to invoke 'CompileNestedCondition'` ile 500 verir. Daraltmayı `selectedFields` +
339
+ `order` + `pageLength` ile yap, ya da doğrulamayı entity satırından değil **ucun kendisinden**
340
+ (`microservice_execute`) yap.
341
+ - **`users`'ta `row_create` verdiğin `id`'yi yok sayar** (PK `identity` + `generationType: 0`);
342
+ Studio operatörünü proje tablosuna aynı id ile taşıyamazsın, eşleme e-posta üzerinden kurulur.
343
+ - Silmeden önce `entity_dependencies` / `dependency_graph`.
344
+
345
+ ## Büyük entity'lerde
346
+
347
+ `entity_get view:'outline'` alan listesi; `view:'part', part:'fields[slug=email]'` tek alan;
348
+ değişiklik `entity_patch`.