@appaflytech/zeyrek-ai-mcp 0.0.6 → 0.0.7
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.
- package/.env.example +2 -2
- package/.env.production.example +8 -0
- package/README.md +9 -8
- package/dist/config.js +18 -3
- package/dist/knowledge/backendConstraints.js +85 -0
- package/dist/knowledge/componentCatalog.js +357 -54
- package/dist/knowledge/componentEvents.js +120 -25
- package/dist/knowledge/functionGuards.js +134 -0
- package/dist/knowledge/normalize.js +53 -2
- package/dist/knowledge/outlines.js +31 -0
- package/dist/knowledge/policy.js +14 -5
- package/dist/knowledge/rules.js +343 -1
- package/dist/platform/csv.js +123 -0
- package/dist/platform/errors.js +62 -3
- package/dist/platform/http.js +1 -0
- package/dist/platform/responses.js +16 -4
- package/dist/platform/screenshot.js +18 -1
- package/dist/resources/examples/flow-call-function.json +66 -0
- package/dist/resources/examples/function-total.json +70 -0
- package/dist/resources/examples/microfunction-node-sum.json +56 -0
- package/dist/resources/examples/page-dynamic-form.json +1 -1
- package/dist/resources/examples/page-sign-in.json +1 -1
- package/dist/resources/guides/component.md +13 -1
- package/dist/resources/guides/entity.md +321 -17
- package/dist/resources/guides/expressions.md +3 -0
- package/dist/resources/guides/functions.md +262 -0
- package/dist/resources/guides/getting-started.md +5 -0
- package/dist/resources/guides/merge.md +231 -0
- package/dist/resources/guides/microfunction.md +166 -0
- package/dist/resources/guides/microservice.md +61 -2
- package/dist/resources/guides/modify-existing.md +23 -0
- package/dist/resources/guides/page.md +20 -4
- package/dist/resources/guides/translation.md +24 -0
- package/dist/resources/guides/workflow.md +1 -1
- package/dist/resources/schema-fingerprint.json +4 -4
- package/dist/resources/schemas/AnalyzeImportQuery.json +51 -0
- package/dist/resources/schemas/ApplyMergePlanCommand.json +19 -0
- package/dist/resources/schemas/CreateBulkRowCommand.json +3 -0
- package/dist/resources/schemas/CreateEnvironmentCommand.json +31 -0
- package/dist/resources/schemas/CreateGlobalFunctionCommand.json +95 -0
- package/dist/resources/schemas/CreateImportPlanCommand.json +278 -0
- package/dist/resources/schemas/CreateMergePlanCommand.json +34 -0
- package/dist/resources/schemas/CreateMicrofunctionCommand.json +117 -0
- package/dist/resources/schemas/CreateMicrofunctionRuntimeCommand.json +51 -0
- package/dist/resources/schemas/CreateMicroserviceCommand.json +119 -1
- package/dist/resources/schemas/CreateWorkflowCommand.json +13 -2
- package/dist/resources/schemas/DataJobDto.json +267 -0
- package/dist/resources/schemas/ExportRowsCommand.json +172 -0
- package/dist/resources/schemas/Filter.json +3 -1
- package/dist/resources/schemas/GetRowsQuery.json +3 -1
- package/dist/resources/schemas/GlobalFunctionDto.json +89 -0
- package/dist/resources/schemas/GlobalFunctionUpdateResultDto.json +249 -0
- package/dist/resources/schemas/ImportAnalysisDto.json +401 -0
- package/dist/resources/schemas/ImportPlanDto.json +260 -0
- package/dist/resources/schemas/ImportSampleDto.json +90 -0
- package/dist/resources/schemas/InvokeMicrofunctionCommand.json +25 -0
- package/dist/resources/schemas/PreviewMicroserviceCommand.json +119 -1
- package/dist/resources/schemas/PreviewWorkflowCommand.json +13 -2
- package/dist/resources/schemas/ResolveMergeConflictsCommand.json +58 -0
- package/dist/resources/schemas/RevertMergePlanCommand.json +19 -0
- package/dist/resources/schemas/RunImportCommand.json +62 -0
- package/dist/resources/schemas/StepDto.json +13 -2
- package/dist/resources/schemas/UpdateBulkRowCommand.json +3 -0
- package/dist/resources/schemas/UpdateEntityCommand.json +3 -1
- package/dist/resources/schemas/UpdateGlobalFunctionCommand.json +66 -0
- package/dist/resources/schemas/UpdateMicrofunctionCommand.json +121 -0
- package/dist/resources/schemas/UpdateMicrofunctionRuntimeCommand.json +54 -0
- package/dist/resources/schemas/UpdateMicroserviceCommand.json +119 -1
- package/dist/resources/schemas/UpdateWorkflowCommand.json +13 -2
- package/dist/server.js +15 -3
- package/dist/tools/activities.js +40 -0
- package/dist/tools/automations.js +14 -5
- package/dist/tools/common.js +16 -0
- package/dist/tools/dependencies.js +1 -1
- package/dist/tools/environments.js +52 -0
- package/dist/tools/files.js +51 -50
- package/dist/tools/flows.js +95 -2
- package/dist/tools/functions.js +331 -0
- package/dist/tools/importSchemas.js +79 -0
- package/dist/tools/imports.js +241 -0
- package/dist/tools/merges.js +113 -0
- package/dist/tools/microfunctions.js +155 -0
- package/dist/tools/microservices.js +42 -3
- package/dist/tools/pages.js +62 -10
- package/dist/tools/platformInfo.js +4 -1
- package/dist/tools/revisions.js +94 -0
- package/dist/tools/rows.js +342 -22
- package/dist/tools/screenWrite.js +11 -2
- package/dist/tools/templates.js +32 -5
- package/dist/tools/workflows.js +24 -6
- package/package.json +3 -1
|
@@ -433,10 +433,12 @@ boş bırakmak geçerli bir tercihtir** (ENT-002 warn'ı bunu hatırlatır, zoru
|
|
|
433
433
|
- İki entity'yi önce ilişkisiz oluşturup ilişki alanını sonra `entity_patch` ile eklemek en
|
|
434
434
|
güvenli sıra (id'ler ancak oluşunca belli olur).
|
|
435
435
|
- `display: true` entity başına tek alanda olabilir; ilişkilerde görüntülenen değer bu alandır.
|
|
436
|
-
- **Boş ilişki `null`
|
|
437
|
-
`{
|
|
438
|
-
`
|
|
439
|
-
|
|
436
|
+
- **Boş ilişki `null` döner** (2026-09-09'dan beri). Öncesinde platform her üyesi `null` olan bir
|
|
437
|
+
nesne (`{"name": null, "id": null}`) döndürüyordu; gateway `prune` bunu `{}`'ye indirdiği için
|
|
438
|
+
ajan tarafında `{}` görünüyordu ve JS'te truthy olduğu için `if (row.manager_id)` "yöneticisi
|
|
439
|
+
var" diyordu (İK koşusu D-8). Şekil `row_list`'in okuma yolunda düzeltildi. **Gateway `null`
|
|
440
|
+
alanları tümüyle atar**, yani `row_list` çıktısında boş ilişki alanını **hiç görmezsin** —
|
|
441
|
+
alanın yokluğu "boş" demektir.
|
|
440
442
|
|
|
441
443
|
## Index'ler
|
|
442
444
|
|
|
@@ -578,16 +580,55 @@ bilmezsen "kayıt bozulmuş" sanırsın.
|
|
|
578
580
|
- `row_*` tool'ları `entityId` olarak slug kabul eder (`employees`, çakışmada
|
|
579
581
|
`BuiltIn/employees`); gateway id'ye çevirir ve yanıtta `gatewayResolved` ile bildirir.
|
|
580
582
|
- `row_create/row_update` anahtarları **field slug'larıdır** (name değil): `{"code": "IK"}`.
|
|
581
|
-
- **
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
satır
|
|
587
|
-
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
583
|
+
- **Satırlar bir DOSYADAN geliyorsa gövdeye hiç koyma: `path`.** `row_create { entityId, path:
|
|
584
|
+
"seed.csv" }` gateway'e dosyayı okutur, 1000'lik partilere böler ve ardışık bulk çağırır;
|
|
585
|
+
bağlama tek bir `{ written, batches }` döner. Ölçüm: 170 KB'lık tohum verisi `rows[]` ile üç
|
|
586
|
+
kez ajan bağlamından geçti (2026-09-03 koşusu P3-3). `.json` satır dizisi ya da `.csv` (UTF-8,
|
|
587
|
+
ayraç ilk satırdan; `;` sayısı `,` sayısını geçerse `;`) okur, değerler string gider ve tipe
|
|
588
|
+
backend çevirir. Sınır 50 MB / 100.000 satır — üstü içe aktarma zinciridir.
|
|
589
|
+
- **Partiler arası transaction YOKTUR.** Her parti kendi içinde tek transaction'dır: düşen parti
|
|
590
|
+
kendi satırlarını geri alır, **önceki partiler yazılmış kalır**. Gateway ilk düşen partide durur
|
|
591
|
+
ve `stopped.fileLine` ile hangi DOSYA SATIRINDAN devam edileceğini söyler. Hepsi-ya-hiç
|
|
592
|
+
gerekiyorsa `path` değil `import_run` kullanın.
|
|
593
|
+
- **İlişki alanına id yerine ad yazmak: `relationsByName: true`.** `departman: "Satış"` hedef
|
|
594
|
+
tablonun display ya da benzersiz alanında aranır ve id'ye çevrilir; yalnız toplu yolda
|
|
595
|
+
(`rows` / `path`) çalışır. Eşleşmeyen değer 422 döner ve hangi satırın hangi alanı olduğunu
|
|
596
|
+
söyler — sessizce boş bırakmaz. Öncesinde m2m ve tekil ilişki için `row_list` ile display → id
|
|
597
|
+
sözlüğü elle kuruluyordu.
|
|
598
|
+
- **Toplu yazımda (`rows[]`) yanıt `data: [{ "<pk slug>": değer }, …]` döner** — istek sırasında,
|
|
599
|
+
her satır için bir kayıt. `row_create` bunu 2026-09-04'ten beri, `row_update` **2026-09-09'dan
|
|
600
|
+
beri** yapar; o tarihe kadar toplu update yalnız `{success:true}` dönüyordu ve hangi satırın
|
|
601
|
+
yazıldığını öğrenmek için fazladan bir `row_list` turu gerekiyordu.
|
|
602
|
+
- Yazdıktan sonra `row_list` ile geri oku; ilişki ve dosya alanları `{ id, <displayAlanı> }`
|
|
603
|
+
nesnesi olarak döner.
|
|
604
|
+
- **Okunan şekli olduğu gibi geri yazabilirsin (2026-09-09'dan beri).** İlişki, dosya ve m2m
|
|
605
|
+
alanlarında yazma yolu hem id string'ini hem `{ id, … }` nesnesini kabul eder; nesne verilirse
|
|
606
|
+
`id` okunur. Öncesinde bu **sessizce veri kaybettiriyordu**: dosya alanında `500
|
|
607
|
+
InvalidCastException`, tekil ilişkide `200` + FK'nın `NULL`'a düşmesi, m2m'de `200` + bağın
|
|
608
|
+
silinmesi (2026-09-09 koşusu S-01). İd string'i hâlâ en kısa ve en güvenli şekildir.
|
|
609
|
+
- **Boş to-one ilişki `null`, boş many-to-many alan `[]` döner** (2026-09-09'dan beri). m2m
|
|
610
|
+
öncesinde JSON metni olarak `""` dönüyordu — `Array.isArray` `false`, `forEach` çökerdi (S-02).
|
|
611
|
+
Gateway `null` alanları attığı için boş to-one ilişki yanıtta **hiç görünmez**.
|
|
612
|
+
|
|
613
|
+
### Alan tipi × yazarken × okurken
|
|
614
|
+
|
|
615
|
+
Tek tabloda tüm şekiller. "Yazarken" sütunundaki **her iki** biçim de kabul edilir; id string'i en
|
|
616
|
+
kısa ve en güvenli olandır.
|
|
617
|
+
|
|
618
|
+
| Alan tipi | Yazarken | Okurken (`row_list`) | Boşken okurken |
|
|
619
|
+
|---|---|---|---|
|
|
620
|
+
| tekil ilişki (`relation`, one-to-many) | `"<id>"` · `{ "id": "<id>", … }` | `{ id, <displayAlanı> }` | `null` (gateway alanı hiç göstermez) |
|
|
621
|
+
| tekil dosya (`file`) | `"<fileId>"` · `{ "id": "<fileId>", … }` | `{ id, display_name }` | `null` (gateway alanı hiç göstermez) |
|
|
622
|
+
| çoklu ilişki (m2m) | `["<id>", …]` · `[{ "id": "<id>", … }, …]` | `[{ id, <displayAlanı> }, …]` | `[]` |
|
|
623
|
+
| çoklu dosya (FILE m2m) | `["<fileId>", …]` · `[{ "id": "<fileId>", … }, …]` | `[{ id, display_name }, …]` | `[]` |
|
|
624
|
+
| `text` kolonda JSON | `JSON.stringify(...)` (string) | **ayrıştırılmış** nesne/dizi | — |
|
|
625
|
+
| enum (`valueOptions`) | değerin kendisi (`"active"`) | aynı string | `null` |
|
|
626
|
+
| ilişki, `relationsByName: true` ile | görünen ad (`"Satış"`) — display ya da benzersiz alan | `{ id, <displayAlanı> }` | `null` |
|
|
627
|
+
|
|
628
|
+
Üç tuzak aynı tablodan okunur: (1) yazma ve okuma şekli **farklıdır**, ama 2026-09-09'dan beri
|
|
629
|
+
okunan şekil geri yazılabilir; (2) `text` kolonda JSON'u okurken **ikinci kez** `JSON.parse` etme;
|
|
630
|
+
(3) `row_get` ilişki alanlarını `row_list`'ten farklı olarak **düz FK** döner — nesne bekleyen kod
|
|
631
|
+
orada `undefined` alır.
|
|
591
632
|
- **`isAuditTrailActive: true` ile KURULAN entity'ye satır yazılamaz.** `sys_*` kolonları create
|
|
592
633
|
yolunda oluşmaz, yalnız ilk **etkin** `entity_update`'inde eklenir; o ana kadar **her**
|
|
593
634
|
`row_create` şu hatayla 500 verir:
|
|
@@ -621,9 +662,24 @@ bilmezsen "kayıt bozulmuş" sanırsın.
|
|
|
621
662
|
> da yan kayıt oluşmaz; `true` + `basic/detailed` iken "anonim" dediğin satırın yanında IP
|
|
622
663
|
> durur. Bu, ürünün gizlilik vaadini sessizce bozan tek ayardır.
|
|
623
664
|
|
|
624
|
-
-
|
|
625
|
-
|
|
626
|
-
"varsayılanı
|
|
665
|
+
- **Varsayılan alanın adı `default-value`, `static` DEĞİL.** `defaultValueType` üç değer alır:
|
|
666
|
+
`none` · `default-value` (sabit değer, `defaultValue` ile birlikte) · `sql-expression`.
|
|
667
|
+
`defaultValueType: "default-value"` **gerçekten DB varsayılanı kurar** — `ALTER TABLE … ADD
|
|
668
|
+
COLUMN … NOT NULL DEFAULT …` üretilir, dolu tabloda mevcut satırlar da o değerle dolar (ölçüm
|
|
669
|
+
2026-09-09: 3 satırlık tabloya tek çağrıda `NOT NULL DEFAULT 0` eklendi, üç satır da `0` oldu).
|
|
670
|
+
Eski "`static` sessizce `none`'a düşer" notu bir **yanlış anlaşılmaydı**: `static` diye bir değer
|
|
671
|
+
hiç olmadı, geçersiz belirteç sessizce `none`'a düşüyordu. 2026-09-09'dan beri geçersiz belirteç
|
|
672
|
+
**istek anında reddedilir** ve kabul edilen değerleri sayar:
|
|
673
|
+
`Please use a valid value for 'DefaultValueType': none, default-value, sql-expression. Received: 'static'.`
|
|
674
|
+
Aynı düzeltme **her** enum alanı için geçerlidir — yanlış yazılmış bir `operator`, `module` ya da
|
|
675
|
+
`commonType` artık sessizce ilk üyeye düşmez.
|
|
676
|
+
- **Dolu tabloya zorunlu (NOT NULL) alan eklemenin iki yolu var**, ikisi de ölçüldü:
|
|
677
|
+
1. Alana varsayılan ver (`defaultValueType: "default-value"` + `defaultValue`) → **tek çağrı**.
|
|
678
|
+
2. Önce `nullable: true` ekle → satırları `row_update` ile doldur → `entity_patch` ile
|
|
679
|
+
`/fields[slug=…]/nullable` `false` yap → **üç tur**.
|
|
680
|
+
|
|
681
|
+
Varsayılansız zorunlu alan doğrudan eklenirse istek artık bu iki yolu adıyla söyleyen bir hata
|
|
682
|
+
döner (eskiden ham migration hatası geliyordu: `column "…" contains null values`).
|
|
627
683
|
- **`row_list.filters` desteklenir.** Tekil filtre `{ field, operator, value, filterType:'single' }`,
|
|
628
684
|
grup filtre `{ filterType:'group', conditionType, children:[...] }` biçimindedir. Tekil filtrede
|
|
629
685
|
`field`, grupta `children` zorunludur; `in`/`notin` operatörleri liste değer ister. Eksik/yanlış
|
|
@@ -746,6 +802,254 @@ hiç doğmaz, çünkü automation yoktur.
|
|
|
746
802
|
Toplu **güncelleme** de aynı yola aittir: var olan satırlardan hesaplanan bir damga/toplam için
|
|
747
803
|
`UPDATE … FROM` yaz, satırları `row_update`'in gövdesinde taşıma.
|
|
748
804
|
|
|
805
|
+
## İçe aktarma — dosyayı gövdede taşıma
|
|
806
|
+
|
|
807
|
+
Excel/CSV/JSON'dan gelen bir tabloyu `row_create` gövdesiyle satır satır yazmayın. 300 satırlık bir
|
|
808
|
+
listeyi elle taşımak iki koşunun en pahalı kalemiydi; dosyanın kendisi zaten elinizde.
|
|
809
|
+
|
|
810
|
+
**Hangi yol?** Tablo ZATEN VARSA ve satır sayısı 5.000'in altındaysa `row_create { path }`
|
|
811
|
+
yeter — tek adım, şema kurmaz, kuru koşu yapmaz. Şema kurmak, sütun tipini tartışmak, ilişki
|
|
812
|
+
adaylarını tespit etmek ya da hepsi-ya-hiç garantisi gerekiyorsa `import_*` zinciri gerekir:
|
|
813
|
+
kuru koşusu vardır, hataları satır satır listeler ve sınırlı kapsamla geri alınabilir (§4).
|
|
814
|
+
|
|
815
|
+
Zincir dört çağrıdır ve **hiçbiri satırı gövdede taşımaz**:
|
|
816
|
+
|
|
817
|
+
```
|
|
818
|
+
import_analyze { datasourceId, path } → sayfalar, sütunlar, çıkarılan tipler, ilk satırlar
|
|
819
|
+
import_plan { datasourceId, fileId, tables } → planId + KURU KOŞU sayıları ve satır hataları
|
|
820
|
+
import_run { datasourceId, planId } → jobId (202), iş arka planda
|
|
821
|
+
import_status { jobId } → validating → writing → completed + sonuç
|
|
822
|
+
```
|
|
823
|
+
|
|
824
|
+
`path` yerine `fileId` de verilebilir (dosya `file_upload` ile önceden yüklendiyse).
|
|
825
|
+
|
|
826
|
+
### 1. Analiz — hiçbir şey yazılmaz
|
|
827
|
+
|
|
828
|
+
Tipi, başlık satırını ve yerel ayarı görmeden plan gövdesi yazmayın. Yerel ayar sonucun
|
|
829
|
+
kendisidir: `tr-TR` profilinde `15.03.2024` tarih ve `42.500,00` ondalık; `en-US` profilinde
|
|
830
|
+
ikisi de **metin** olur. Yanıttaki `unconvertible` sıfır değilse önce `locale`'i sorgulayın,
|
|
831
|
+
sonra sütun tipini.
|
|
832
|
+
|
|
833
|
+
`summary` sayfa/satır/aday/uyarı sayısını, `note` ilişki adaylarını özetler.
|
|
834
|
+
|
|
835
|
+
**`unique` bayrağı bir gözlemdir, kısıt değil.** "Bu dosyadaki bütün değerler farklıydı" demektir;
|
|
836
|
+
30 satırlık bir örnekte maaş ya da ad sütunu da benzersiz çıkar. Plan gövdesinde `field.unique`
|
|
837
|
+
yalnız **anahtar sütun** için `true` verilir (`flags` içinde `key` olan); her benzersiz sütunu
|
|
838
|
+
unique kurmak tabloyu tek dosyaya kilitler ve aynı zammı iki kişiye veren bir sonraki dosya
|
|
839
|
+
`unique` hatasıyla düşer.
|
|
840
|
+
|
|
841
|
+
### 1a. JSON dosyası — kök dört biçimden biri
|
|
842
|
+
|
|
843
|
+
`.json` de okunur ve aynı zincirden geçer. Kökün şekli sayfaların nasıl çıkacağını belirler:
|
|
844
|
+
|
|
845
|
+
| Kök | Ne olur |
|
|
846
|
+
|---|---|
|
|
847
|
+
| Nesne dizisi `[{...}, {...}]` | Tek sayfa; sayfa adı dosya adıdır, başlık satırı anahtarların birleşimidir (ilk görülme sırasına göre) |
|
|
848
|
+
| Tek nesne `{ "ad": "Test" }` | Tek satırlık sayfa; mevcut bir tabloya tek kayıt eklemenin en kısa yolu |
|
|
849
|
+
| HER özelliği nesne dizisi olan nesne `{ "iller": [...], "ilceler": [...] }` | Her özellik ayrı sayfa; sayfa adı özelliğin adıdır — çok tablolu planın JSON karşılığı budur. Bir özellik bile nesne dizisi değilse kök tek satır sayılır |
|
|
850
|
+
| `{ "meta": {...}, "data": [...] }` | `row_export`'un ürettiği gövde; `data` tek sayfa olur, `meta` `_zeyrek` sayfasına düşer ve analizde atlanır |
|
|
851
|
+
|
|
852
|
+
Boş nesne, skaler kök, sözdizimi ya da kodlama hatası gerekçeli `400 ImportFileUnreadableException`
|
|
853
|
+
alır. Metin BOM'a, yoksa katı UTF-8'e, olmazsa windows-1254'e göre çözülür; `headerRow` yok sayılır.
|
|
854
|
+
|
|
855
|
+
**JSON'da tip dosyadan gelir, yerel ayardan değil.** Sayı ve `true`/`false` doğrudan okunur;
|
|
856
|
+
`locale` yalnız metin olarak yazılmış tarih ve sayıları çözmek için kalır. Eksik anahtar boş
|
|
857
|
+
hücredir. İç içe nesne sıkıştırılmış JSON metnine düşer. Skaler dizi `"; "` ile birleşir —
|
|
858
|
+
`multi-value` adayı bu sayede JSON'da da çıkar, ayracı `"; "` verin.
|
|
859
|
+
|
|
860
|
+
### 1b. İlişki adayları — tekrar eden sütunlar
|
|
861
|
+
|
|
862
|
+
Analiz her sayfa için `relationCandidates` döner. Bunlar **öneridir**: seçimi `import_plan`
|
|
863
|
+
gövdesindeki `columns[].relation` yapar. Aday yoksa sütun metin kalır ve hiçbir tablo açılmaz;
|
|
864
|
+
`recommended` değeri `text` olan bir aday da (bkz. `manual`) siz seçmedikçe hiçbir şey açmaz.
|
|
865
|
+
|
|
866
|
+
| `kind` | Ne gördü | Seçenekler | Ne zaman hangisi |
|
|
867
|
+
|---|---|---|---|
|
|
868
|
+
| `category` | Az sayıda değer kendini tekrar ediyor | `relation-existing` · `relation-new` · `select` · `text` | `target.existingEntityId` doluysa **önce onu dene** (`relation-existing`); yoksa 20'den az değer için `select`, üstü için `relation-new` |
|
|
869
|
+
| `multi-value` | Bir hücrede ayraçla ayrılmış liste | `multi-relation-new` · `multi-relation-existing` · `multi-select` · `text` | Ayracı `delimiter` ile geçir. `multi-relation-new` ayrı bir tablo, ana tabloda bir m2m alanı ve junction tablosunu kurar; 20'den az değer için `multi-select` daha ucuzdur. **MEVCUT tabloya** `many: true` yazılıyorsa m2m alanı tabloda zaten olmalı — yoksa plan `ImportPlanStale` ile düşer |
|
|
870
|
+
| `hierarchy` | İki sütun arasında üst/alt bağımlılığı (`ilçe → il`) | `hierarchy-two-tables` · `hierarchy-parent-only` · `text` | `hierarchy-two-tables` varsayılan; `parentColumnIndex` ve `parentTable` ALT sütunun `relation`'ına konur |
|
|
871
|
+
| `cross-sheet` | Değerler başka bir sayfanın benzersiz sütununda | `relation-sheet` · `text` | O sayfayı da `tables`'a ekleyin; `target.sheetKey` hangi sayfa olduğunu söyler |
|
|
872
|
+
| `manual` | Hiçbir şey tekrar etmiyor — sütun serbest metinden ayırt edilemiyor | `relation-existing` · `relation-new` · `text` | Değerler mevcut bir tabloyla örtüşüyorsa önerisi `relation-existing` olur (personel dosyasındaki `İl` sütunu, satır başına benzersiz olsa bile `İller` tablosuna bağlanır); örtüşme yoksa öneri `text`'tir ve **tablo açmak insan kararıdır**. Ortalama 40 karakterden uzun metin hiç önerilmez |
|
|
873
|
+
|
|
874
|
+
**Platform tablosunu önce dene.** `roles` (ad) ve `users` (e-posta) projede zaten vardır; aday
|
|
875
|
+
onlarla eşleşiyorsa yeni tablo açmak yerine `relation-existing` seçin — yoksa aynı kavramın iki
|
|
876
|
+
tablosu olur.
|
|
877
|
+
|
|
878
|
+
**`fdViolations > 0` ise bağımlılık yaklaşıktır.** Aday yine gelir ama kaç satırın kuralı bozduğunu
|
|
879
|
+
söyler; `unmatched: 'error'` ile koşun ve düşen satırları rapor dosyasından okuyun.
|
|
880
|
+
|
|
881
|
+
**`parentCandidates` — her sütun bir üst sütunun altına bağlanabilir.** Aday, `column` ·
|
|
882
|
+
`distinctCount` · `nameViolations` üçlüsüyle gelir. Ad tekrarı bağlanmaya engel değildir:
|
|
883
|
+
bir alt kaydın kimliği adı değil, **üstüyle birlikte adıdır**. Türkiye verisinde "Merkez" ilçesi
|
|
884
|
+
onlarca ilde, "Cumhuriyet" mahallesi onlarca ilçede geçer; `nameViolations` bunu sayar, seçim
|
|
885
|
+
sizindir. Seçtiğinizde `parentColumnIndex` (ve yeni tablo açıyorsanız `parentTable`) o sütunun
|
|
886
|
+
`relation`'ına yazılır — hiyerarşi adayıyla aynı gövde.
|
|
887
|
+
|
|
888
|
+
### 1c. Hiyerarşi gövdesi — İl → İlçe → Mahalle
|
|
889
|
+
|
|
890
|
+
Zincir ayrı bir kural değildir: her ardışık çift kendi `hierarchy` adayını üretir ve her biri
|
|
891
|
+
**alt sütunun** `relation`'ına yazılır. Üst tablo ayrıca bir sütuna bağlanmaz.
|
|
892
|
+
|
|
893
|
+
```json
|
|
894
|
+
"columns": [
|
|
895
|
+
{ "index": 0, "include": true,
|
|
896
|
+
"relation": { "newTable": { "name": "İller", "slug": "iller" }, "unmatched": "error" } },
|
|
897
|
+
{ "index": 1, "include": true,
|
|
898
|
+
"relation": { "newTable": { "name": "İlçeler", "slug": "ilceler" },
|
|
899
|
+
"parentColumnIndex": 0,
|
|
900
|
+
"parentTable": { "name": "İller", "slug": "iller" },
|
|
901
|
+
"unmatched": "error" } },
|
|
902
|
+
{ "index": 2, "include": true,
|
|
903
|
+
"relation": { "newTable": { "name": "Mahalleler", "slug": "mahalleler" },
|
|
904
|
+
"parentColumnIndex": 1,
|
|
905
|
+
"parentTable": { "name": "İlçeler", "slug": "ilceler" },
|
|
906
|
+
"unmatched": "error" } }
|
|
907
|
+
]
|
|
908
|
+
```
|
|
909
|
+
|
|
910
|
+
Planın döndüğü `tables[].order` **backend planlayıcısından gelir** (FK topolojik sırası):
|
|
911
|
+
`iller` → `ilceler` → `mahalleler` → ana tablo. Ajan sıralamaz, sıraya güvenir.
|
|
912
|
+
|
|
913
|
+
Ana tabloda **ilişki yazdığınız her sütun için** bir ilişki alanı açılır: yukarıdaki gövde
|
|
914
|
+
`il`, `ilce` ve `mahalle` için üç alan verir. Bir sütuna doğrudan bağlanmak istemiyorsanız
|
|
915
|
+
`relation`'ını yazmayın, yalnız `parentColumnIndex` hedefi olarak bırakın; alt seviye yine
|
|
916
|
+
kapsamlı çözülür.
|
|
917
|
+
|
|
918
|
+
**Zincir kimlikle çözülür, adla değil.** Satır yazılırken en üstteki sütun kapsamsız, her alt
|
|
919
|
+
seviye bir üstündeki çözülmüş id ile eşlenir; alt tablo satırları da üst zincirinin tamamıyla
|
|
920
|
+
tekilleştirilir. Bu yüzden "Bolu/Merkez" ile "Çorum/Merkez" ayrı iki ilçe satırı olur.
|
|
921
|
+
|
|
922
|
+
**Adaylar en yakın üstten başlar.** `import_analyze` zincirde üç çift döndürür (`il→ilçe`,
|
|
923
|
+
`il→mahalle`, `ilçe→mahalle`) ve sıralamayı üst sütunun farklı değer sayısına göre azalan yapar;
|
|
924
|
+
listedeki ilk aday `ilçe→mahalle` olur. Bir sütun yalnız **bir** ilişki taşıyabildiği için
|
|
925
|
+
`il→mahalle`'yi de seçerseniz `ilçe→mahalle` ezilir ve zincir kopar.
|
|
926
|
+
|
|
927
|
+
### 2. Plan — kuru koşu
|
|
928
|
+
|
|
929
|
+
`import_plan` tek satır bile yazmadan tip dönüşümünü, zorunluluğu, benzersizliği ve ilişki
|
|
930
|
+
çözümünü dener. Gövde analiz çıktısından kurulur:
|
|
931
|
+
|
|
932
|
+
```json
|
|
933
|
+
{
|
|
934
|
+
"datasourceId": "…",
|
|
935
|
+
"fileId": "…",
|
|
936
|
+
"locale": "tr-TR",
|
|
937
|
+
"errorPolicy": "skip-rows",
|
|
938
|
+
"tables": [
|
|
939
|
+
{
|
|
940
|
+
"sheetKey": "Personel",
|
|
941
|
+
"mode": "create",
|
|
942
|
+
"target": { "name": "Personel", "slug": "personel" },
|
|
943
|
+
"columns": [
|
|
944
|
+
{ "index": 0, "field": { "slug": "sicil_no", "name": "Sicil No", "commonType": "number", "dbType": "INT8", "unique": true, "nullable": false } },
|
|
945
|
+
{ "index": 1, "field": { "slug": "ad_soyad", "name": "Ad Soyad", "commonType": "text", "dbType": "VARCHAR", "display": true } }
|
|
946
|
+
]
|
|
947
|
+
},
|
|
948
|
+
{
|
|
949
|
+
"sheetKey": "Departmanlar",
|
|
950
|
+
"mode": "upsert",
|
|
951
|
+
"target": { "entityId": "…" },
|
|
952
|
+
"keyField": "ad",
|
|
953
|
+
"emptyCellPolicy": "keep",
|
|
954
|
+
"columns": [{ "index": 0, "existingFieldSlug": "ad" }]
|
|
955
|
+
}
|
|
956
|
+
]
|
|
957
|
+
}
|
|
958
|
+
```
|
|
959
|
+
|
|
960
|
+
- `mode: create` → `target.name` + `target.slug` ve her sütunda `field`.
|
|
961
|
+
- `mode: append | upsert | update` → `target.entityId` ve her sütunda `existingFieldSlug`.
|
|
962
|
+
- `upsert`/`update` → `keyField` **zorunlu** ve benzersiz olmalı (unique alan, tek alanlı unique
|
|
963
|
+
index, `code` ya da `id`); değilse 422.
|
|
964
|
+
- `emptyCellPolicy: keep` boş hücreyi yok sayar — mevcut değeri silmez ve "değişti" saymaz.
|
|
965
|
+
|
|
966
|
+
> **Kuru koşu hatalarını düzeltmeden `import_run` çağırmayın.** `errorPolicy: 'skip-rows'`
|
|
967
|
+
> hatalı satırı sessizce atlar; plandaki `errors` listesine bakmadan çalıştırırsanız eksik
|
|
968
|
+
> tablonun farkına varmazsınız. Hata oranı %50'yi geçerse `import_run` `force: true` ister.
|
|
969
|
+
|
|
970
|
+
### 3. Çalıştır ve izle
|
|
971
|
+
|
|
972
|
+
`import_run` **202** döner, gateway beklemez. `import_status` `writing` durumunda
|
|
973
|
+
`progress.written/total` verir ve `retryAfterSeconds: 5` ekler; `completed` olunca
|
|
974
|
+
`result.tables[].inserted/updated/unchanged/skipped` ve `reportFileId` gelir. Atlanan satırların
|
|
975
|
+
tam listesi o dosyadadır (`file_download`).
|
|
976
|
+
|
|
977
|
+
İçe aktarma **development kararıdır**: `import_plan`, `import_run`, `import_undo` ve `import_cancel`
|
|
978
|
+
production'da istek gitmeden reddedilir.
|
|
979
|
+
|
|
980
|
+
Şemalar: `ngapps://schemas/ImportAnalysisDto`, `ngapps://schemas/ImportPlanDto`,
|
|
981
|
+
`ngapps://schemas/DataJobDto`.
|
|
982
|
+
|
|
983
|
+
### 4. Geçmiş, iptal ve geri alma
|
|
984
|
+
|
|
985
|
+
- `import_history` en yeniden eskiye listeler; `kind: 'import'` planları da getirir.
|
|
986
|
+
- `import_cancel` `pending`/`validating`'i hemen, `writing`'i parti sonunda durdurur; yazılanlar kalır,
|
|
987
|
+
geri alınamaz. İş önce biterse 409.
|
|
988
|
+
- `import_undo` yalnız `undoAvailableUntil` dolu `completed` işte çalışır; 202 döner, bitmeden
|
|
989
|
+
tekrar çağrılmaz. Kapsam:
|
|
990
|
+
- açılan tablo, başka tablo bağlı değilse **bütün satırlarıyla** silinir;
|
|
991
|
+
- referans satırları silinir, güncellenen sütunlar önceki değerine döner;
|
|
992
|
+
- mevcut tabloya **eklenen** satırlar kalır (yalnız ekleme yapan işte pencere açılmaz);
|
|
993
|
+
- çakışma denetimi yoktur.
|
|
994
|
+
|
|
995
|
+
## Dışa aktarma ve geri yükleme
|
|
996
|
+
|
|
997
|
+
`row_export` bir tablonun satırlarını dosyaya yazar ve dosyayı **Sürücü'ye** koyar. Bağlama yalnız
|
|
998
|
+
`fileId`, ad ve bayt sayısı döner — içerik hiç geçmez, okumak isterseniz `file_download`.
|
|
999
|
+
|
|
1000
|
+
```
|
|
1001
|
+
row_export { entityId, format: 'xlsx' | 'csv' | 'json', scope, reimportable? }
|
|
1002
|
+
→ { fileId, fileName, bytes } ≤ 5.000 satır
|
|
1003
|
+
→ { jobId } üstü; import_status { jobId } ile izlenir,
|
|
1004
|
+
biten işin dosyası result.reportFileId
|
|
1005
|
+
```
|
|
1006
|
+
|
|
1007
|
+
Kapsam üç şekildedir: `{ kind: 'view', filters, sort }` (grid'in gördüğü küme),
|
|
1008
|
+
`{ kind: 'all' }`, `{ kind: 'selected', rowIds }` (en çok 1.000 id — üstü filtreyle).
|
|
1009
|
+
Sütunlar `columns: 'visible'` + `selectedFields` ile daraltılır. CSV UTF-8 BOM'ludur; ayraç ve
|
|
1010
|
+
ondalık projenin dilinden gelir (tr `;` · `1,5`, en `,` · `1.5`), şablonda `locale` ile seçilir.
|
|
1011
|
+
|
|
1012
|
+
### Akış C — dışa aktar, düzenle, geri yükle
|
|
1013
|
+
|
|
1014
|
+
Bir tabloyu toplu düzeltmenin yolu satır satır `row_update` değildir. `reimportable: true`
|
|
1015
|
+
dosyaya `id` sütununu ve `_zeyrek` meta sayfasını ekler; o iki şey olmadan geri yükleme hangi
|
|
1016
|
+
satırın hangisi olduğunu bilemez ve her satırı yeni kayıt sayar.
|
|
1017
|
+
|
|
1018
|
+
```
|
|
1019
|
+
row_export { entityId, reimportable: true } → fileId
|
|
1020
|
+
file_download { id: fileId, outPath } → dosya diske iner
|
|
1021
|
+
<dosyayı düzenle>
|
|
1022
|
+
file_upload { path } → yeni fileId
|
|
1023
|
+
import_analyze { datasourceId, fileId } → meta sayfa okunur, tablo tanınır
|
|
1024
|
+
import_plan { …, tables: [{ mode: 'upsert', keyField: 'id' }] }
|
|
1025
|
+
import_run { planId } → 202, import_status ile izlenir
|
|
1026
|
+
```
|
|
1027
|
+
|
|
1028
|
+
`keyField: 'id'` ile mod `upsert`: dosyadaki id eşleşen satır güncellenir, eşleşmeyen eklenir.
|
|
1029
|
+
`reimportable` ilişki sütunlarını ad+id olarak yazar, dolayısıyla adı elle değiştirseniz de id
|
|
1030
|
+
kazanır — ilişkiyi gerçekten değiştirmek istiyorsanız id sütununu düzenleyin.
|
|
1031
|
+
|
|
1032
|
+
### Şablon — dosyanın şeklini önce görün
|
|
1033
|
+
|
|
1034
|
+
`row_export { entityId, template: true, format?, locale? }` tablonun alanlarından **iki sentetik
|
|
1035
|
+
örnek satırlı** şablon üretir ve değiştirilmeden geri yüklenir: PK, sistem ve artan kod sütunu yok,
|
|
1036
|
+
`password` sıradan metin, ipucu veri alanında değil (xlsx başlık yorumu + `_zeyrek`, json `meta`).
|
|
1037
|
+
`inline: true` şekli yanıta da koyar: `sample.columns` (slug, başlık, tip, zorunlu, seçenekler),
|
|
1038
|
+
`sample.keyField`, `sample.data`. İlişki sütunu boş ve `manual`dır: hücreye hedefin `matchField`
|
|
1039
|
+
değerini yazın, şablon hedeften satır okumaz. `data` JSON biçimindedir (ondalık nokta, tarih
|
|
1040
|
+
`yyyy-MM-dd`); bu değerlerle CSV yazıyorsanız analiz ve planda `locale: 'en-US'` verin.
|
|
1041
|
+
`password` gibi gizli adlı sütunun örnek değeri gateway'de `***` maskelenir; kendi değerinizi
|
|
1042
|
+
yazın. Toplu yazımda satır satır `row_create` yerine:
|
|
1043
|
+
|
|
1044
|
+
```
|
|
1045
|
+
row_export { entityId, template: true, inline: true } → sample.columns, sample.data, sample.keyField
|
|
1046
|
+
<başlıkları aynen koruyarak CSV metni üret>
|
|
1047
|
+
file_upload { text, filename: '<slug>.csv' } → fileId
|
|
1048
|
+
import_analyze { datasourceId, fileId, locale } → sütun slug'ları = alan slug'ları
|
|
1049
|
+
import_plan { datasourceId, fileId, locale, tables: [{ mode: 'append' | 'upsert', keyField: sample.keyField, … }] }
|
|
1050
|
+
import_run { planId } → 202, import_status ile izlenir
|
|
1051
|
+
```
|
|
1052
|
+
|
|
749
1053
|
## Büyük entity'lerde
|
|
750
1054
|
|
|
751
1055
|
`entity_get view:'outline'` alan listesi; `view:'part', part:'fields[slug=email]'` tek alan;
|
|
@@ -35,6 +35,7 @@ tetikleyen satır `row` / `previousRow` / `user`.
|
|
|
35
35
|
| `Now(tz)` | Verilen saat diliminde şimdi, **`"yyyy-MM-ddTHH:mm:ss"` METİN olarak**. **JS motoru UTC'ye sabitlidir** (`opt.TimeZone = Utc`), yani `new Date()` her zaman UTC'dir — yerel gün/saat mantığı için bunu kullanın, offset'i koda gömmeyin | `Now('Europe/Istanbul')` → `"2026-08-18T19:11:10"` |
|
|
36
36
|
| `Today(tz)` | O saat diliminde bugün, `"yyyy-MM-dd"` metni — gün sınırı filtreleri için en kısa yol | `Today('Europe/Istanbul') + 'T00:00:00'` |
|
|
37
37
|
| `ToTimeZone(v, tz)` | Bir değeri (Date, DateTimeOffset ya da ISO metin) o saat dilimine çevirir, aynı metin biçiminde | `ToTimeZone(Steps.randevu.data.baslangic, 'Europe/Istanbul')` |
|
|
38
|
+
| `Functions.<ad>(…)` | **YOK — sunucuda kullanıcı fonksiyonu yüklenmez** (2026-09-10). Gateway `WF-030` ile istek gitmeden bloklar; zorlanırsa Jint `Functions is not defined` fırlatır. Sunucuda paylaşılan hesap **`source:'workflow'` fonksiyondur** ve çıplak adla çağrılır (`sum(1, 2)`, `function_create source:'workflow'`) |
|
|
38
39
|
|
|
39
40
|
> **Bu üçü METİN döner, Date DÖNMEZ** — bilerek. `UtcNow()` bir JS `Date` döndürür ve üzerinde
|
|
40
41
|
> `getTime()`/`getUTCFullYear()` çalışır; ama Jint'in `DateTimeOffset` → `Date` çevriminde **saat
|
|
@@ -105,6 +106,7 @@ dalından devam eder.
|
|
|
105
106
|
| `LocalStorage` · `Widget` · `params` | yerel veri · widget bağlamı · olay parametreleri |
|
|
106
107
|
| `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')` |
|
|
107
108
|
| `log(...)` | console (proxy'leri temizler) |
|
|
109
|
+
| `Functions` | **Kullanıcı fonksiyonları** (`function_create`): `Functions.toplamHesapla(fiyat, adet)` — uygulama kapsamlılar her sayfada, sayfa kapsamlılar yalnız kendi sayfasında. **Yalnız tarayıcı**; sunucuda bu ad alanı yoktur (2026-09-10, `WF-030`) |
|
|
108
110
|
|
|
109
111
|
> **`.map is not a function` bir dizi sözleşmesi sorunu DEĞİL, düşmüş bir adımdır.**
|
|
110
112
|
> Ölçüldü 2026-09-05 (`f09-repro/probe`, yerel kurulum): başarılı bir microservice adımında
|
|
@@ -179,3 +181,4 @@ Karıştırma: `roles_csv` **UI/filtre** tarafının yoludur; backend middleware
|
|
|
179
181
|
| `system.` / `file.` / `crypto.` / `convert.` | ✓ | — |
|
|
180
182
|
| `cookie` | okunur sözlük (`cookie.X`) | `get/set/remove` fonksiyonları |
|
|
181
183
|
| `await` | — (senkron ifade) | ✓ (`code` adımı) |
|
|
184
|
+
| `Functions.<ad>()` (kullanıcı fonksiyonu) | — (sunucuda yok, `WF-030` block) | ✓ uygulama + o sayfanın sayfa fonksiyonları |
|