@appaflytech/zeyrek-ai-mcp 0.0.22 → 0.0.24
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 +15 -0
- package/dist/config.js +9 -0
- package/dist/index.js +2 -0
- package/dist/knowledge/componentCatalog.js +4 -2
- package/dist/knowledge/componentEvents.js +3 -3
- package/dist/knowledge/mobileComponentCatalog.js +308 -50
- package/dist/knowledge/mobileComponentEvents.js +130 -3
- package/dist/knowledge/normalize.js +16 -6
- package/dist/knowledge/platform/lint.js +110 -3
- package/dist/knowledge/platform/mobile.js +1 -1
- package/dist/knowledge/platform/mobileRules.js +70 -11
- package/dist/knowledge/platform/web.js +1 -1
- package/dist/knowledge/rules.js +106 -11
- package/dist/knowledge/styleResolve.js +49 -3
- package/dist/knowledge/styleResolveNative.js +104 -4
- package/dist/observability/trace.js +1 -1
- package/dist/platform/argumentAliases.js +128 -0
- package/dist/platform/bodyFile.js +103 -0
- package/dist/platform/capture/capture.js +29 -5
- package/dist/platform/capture/console-log.js +80 -0
- package/dist/platform/capture/index.js +1 -0
- package/dist/platform/diagnostics.js +10 -4
- package/dist/platform/http.js +23 -7
- package/dist/platform/responses.js +6 -2
- package/dist/platform/schemaReadMemory.js +60 -0
- package/dist/platform/secrets.js +9 -0
- package/dist/platform/strictArguments.js +68 -12
- package/dist/platform/tokenVault.js +187 -4
- package/dist/resources/guides/expressions.md +1 -1
- package/dist/resources/guides/microservice.md +9 -2
- package/dist/resources/guides/middleware.md +5 -1
- package/dist/resources/guides/mobile.md +165 -10
- package/dist/resources/guides/page.md +161 -41
- package/dist/resources/guides/theme.md +4 -1
- package/dist/resources/guides/workflow.md +7 -1
- package/dist/resources/schema-fingerprint.json +2 -2
- package/dist/server.js +1 -1
- package/dist/tools/datasources.js +1 -1
- package/dist/tools/designSchemaPatch.js +4 -3
- package/dist/tools/externalComponents.js +1 -1
- package/dist/tools/flows.js +70 -4
- package/dist/tools/layouts.js +5 -5
- package/dist/tools/microservices.js +26 -10
- package/dist/tools/pages.js +192 -36
- package/dist/tools/platformInfo.js +4 -1
- package/dist/tools/rows.js +7 -1
- package/dist/tools/schemaOps.js +1 -1
- package/dist/tools/screenWrite.js +4 -3
- package/dist/tools/screens.js +81 -0
- package/package.json +1 -1
|
@@ -226,7 +226,7 @@ döndürür; hata yok, sayfalama yok. (Bağımsız `pagination` komponenti bu tu
|
|
|
226
226
|
| `dropdown.search` · `multi-select.search` | `{ searchText }` | `self.searchText` |
|
|
227
227
|
| `tabs.tab` | `{ selected }` | `self.selected` ya da `event.data.selected` |
|
|
228
228
|
| `chart.dataPointClick` | `{ name, seriesName, value, dataIndex, row }` | `self.row` (kaynak satırın tamamı) |
|
|
229
|
-
| `repeater.change` · `repeater.select` | `{ selectedIndex }` | `self.selectedIndex` · `self.selectedItem` (akış satır bağlamında koşmaz, `item`/`Row` boş) |
|
|
229
|
+
| `repeater.change` · `repeater.select` | `{ selectedIndex }` | `self.selectedIndex` · `self.selectedItem` (akış satır bağlamında koşmaz, `item`/`Row` boş); satırdaki bir düğmenin akışında setter'ın yazım kapsamı OLAYIN kaynağı değil HEDEFİN ağaçtaki yeriyle belirlenir — satırın şablonu içindeki bir hedefe satır bazlı (`_repeaterValues`), dışındaki bir hedefe (modal, sayfa düzeyi metin) sayfa kapsamında yazılır (ZM-08, 2026-09-26); iç içe repeater zaten `PAGE-013` ile reddedilir, ama bir DIŞ repeater'ın şablonuna İÇ satırdan yazan bir akış varsa yazım dış satırın değil İÇ satırın kimliğiyle anahtarlanır — bilinen açık, PAGE-013 riski düşük tutar |
|
|
230
230
|
| `html` aksiyonu (`fireEvent`) | ikinci argüman | **`event.data`** |
|
|
231
231
|
| `kanban.cardMove` | `{ id, row, from, to }` | **`event.data`** (self'e yazılmaz) |
|
|
232
232
|
| `kanban.cardAdd` · `kanban.cardRemove` | `{ column }` · `{ id, row, column }` | **`event.data`** (self'e yazılmaz; kart eklenmez/gizlenmez, akış yazıp `rows`'u tazeler) |
|
|
@@ -249,7 +249,8 @@ dalı bulamaz ve akışı **sessizce durdurur** (`flow-engine.ts:79`); Studio da
|
|
|
249
249
|
`sourceBranch === 'always'` ile çizdiği için panelde **Start kartından sonrası boş görünür**.
|
|
250
250
|
İstisnalar: `condition` → dal adı rule id'sidir (ve o id bir **GUID**'dir — aşağıdaki tuzak 6),
|
|
251
251
|
`confirm` → `yes|no|always`, `microservice` →
|
|
252
|
-
`always` (eski akışlarda `success` de çalışır)
|
|
252
|
+
`always` (eski akışlarda `success` de çalışır) **+ `error`** — bağlarsan hata anında `always`
|
|
253
|
+
yerine ondan sürer (2026-09-26, ADR 0004; ayrıntı aşağıdaki §"4xx de hatadır").
|
|
253
254
|
|
|
254
255
|
**2. `iteratees[].type` yalnız `change` | `fill`.** `setter.ts` başka değerde hiçbir dala
|
|
255
256
|
girmez — adım çalışır, hiçbir şey yapmaz. `change` tek prop'a değer yazar, `fill` diziyi
|
|
@@ -375,16 +376,22 @@ Satır eşlemeli tablo yüklemesinin tam şekli (yükleniyor bayrağı → uç
|
|
|
375
376
|
göstermek error flow'un işidir.
|
|
376
377
|
|
|
377
378
|
> **4xx de hatadır ve `always` dalı seni KURTARMAZ.** `microservice.ts` `.catch` bloğu
|
|
378
|
-
> reddedilen her istekte koşar — 401/403/404/422 dâhil
|
|
379
|
-
>
|
|
379
|
+
> reddedilen her istekte koşar — 401/403/404/422 dâhil — ve storage'a HER ZAMAN
|
|
380
|
+
> `{ data: null, headers: null, error: { status, message, body } }` yazar (ADR 0004,
|
|
381
|
+
> 2026-09-26 — eskiden hata gövdesi `data`'ya yazılıyordu). `.finally` sonra ÜÇ yoldan birini
|
|
382
|
+
> dener, SIRAYLA:
|
|
380
383
|
>
|
|
381
|
-
> 1. **
|
|
382
|
-
> `
|
|
383
|
-
>
|
|
384
|
-
>
|
|
385
|
-
>
|
|
386
|
-
> `!!Steps.signIn.data.token` gibi bir
|
|
387
|
-
>
|
|
384
|
+
> 1. **Adımın kendi `error` dalı BAĞLIYSA** akış oradan sürer — `always` **hiç koşmaz**
|
|
385
|
+
> (`hasBranch('error')`). Bu dal `Steps.<ad>.error` okuyabilir; ana akışın kalanı hiç çalışmaz.
|
|
386
|
+
> 2. **`error` dalı yoksa, `errorSteps` VARSA** `engine.onError` true döner ve `.finally` `return`
|
|
387
|
+
> eder — yine `always` koşmaz, flow'un hata akışı devralır (aşağıdaki karşılaştırma tablosu).
|
|
388
|
+
> 3. **İkisi de yoksa** `always` koşar, ama `Steps.<ad>.data` artık **NULL**dır — başarı gövdesi
|
|
389
|
+
> değil, hiç gövde değil. Sıradaki bir `condition` `!!Steps.signIn.data.token` gibi bir
|
|
390
|
+
> ifadeyi `null.token` üzerinden dener; `runCode` hatayı yutar (K2), ifade `undefined` döner ve
|
|
391
|
+
> akış sessizce diğer dala düşer — `else` dalı bağlı değilse akış orada biter. `data`yı bir
|
|
392
|
+
> liste prop'una yazan bir setter artık patlayan bir nesne değil `null` görür (repeater/table
|
|
393
|
+
> `null`'ı boş liste sayar), ama hata OLDUĞUNU fark etmeden geçer — gateway bunu **FLOW-028**
|
|
394
|
+
> ile uyarır.
|
|
388
395
|
>
|
|
389
396
|
> **`errorSteps` yalnız `start` taşıyorsa hata yolu YOKTUR:** `hasErrorFlow`
|
|
390
397
|
> `errorTasks.some(t => t.type !== 'start')` arar. Start'ın ardına en az bir adım bağla —
|
|
@@ -393,15 +400,18 @@ göstermek error flow'un işidir.
|
|
|
393
400
|
> **Mobil uygulama:** mobil runtime (`@ng-apps/ui-native` `NativeRuntime`) `errorSteps`'i web gibi
|
|
394
401
|
> `errorTasks`'a eşler — hata yolu iki platformda aynı koşar.
|
|
395
402
|
|
|
396
|
-
> **Hata metni
|
|
403
|
+
> **Hata metni birden çok yerde durur, hepsi aynı şey değildir (2026-09-26 itibarıyla, ADR 0004).**
|
|
397
404
|
> `onError` hata akışını koşturmadan önce `machine.setStorage('error', errorInfo)` yapar
|
|
398
|
-
> (`flow-engine.ts`), yani hata akışının adımları `Steps.error`'ı okuyabilir
|
|
405
|
+
> (`flow-engine.ts`), yani hata akışının adımları `Steps.error`'ı okuyabilir; patlayan adımın
|
|
406
|
+
> KENDİ storage'ı ise (bağlı `error` dalı da `always` da) `Steps.<adımAdı>.error` nesnesini taşır:
|
|
399
407
|
>
|
|
400
408
|
> | İfade | Ne taşır | Kaynak |
|
|
401
409
|
> |---|---|---|
|
|
402
|
-
> | **`Steps.error.message`** | **
|
|
403
|
-
> | `Steps.<adımAdı>.error` |
|
|
404
|
-
> | `Steps.<adımAdı>.
|
|
410
|
+
> | **`Steps.error.message`** | **Yalnız `errorSteps` içinde** — ucun kendi mesajı, `err.reason` → HTTP durum metni → `'Service request failed'`. Yanında `Steps.error.code` · `Steps.error.step.name` (patlayan adım) · `Steps.error.step.data` (ucun hata gövdesi; parametre ifadesi patladıysa `null`) gelir | `microservice.ts` `err.reason \|\| HttpStatusText[err.code]` → `flow-engine.ts` `onError` |
|
|
411
|
+
> | `Steps.<adımAdı>.error.message` | Patlayan adımın KENDİ storage'ında ucun mesajı — `error` dalında da `always`'te de okunur, sıra aynı: `err.reason` → HTTP durum metni → `'Service request failed'` | `microservice.ts` `.catch` |
|
|
412
|
+
> | `Steps.<adımAdı>.error.status` | HTTP durum KODU (sayı) — parametre ifadesi patladıysa `null` | `microservice.ts` `.catch` |
|
|
413
|
+
> | `Steps.<adımAdı>.error.body` | Ucun hata gövdesi — eski `Steps.<adımAdı>.data`'nın taşındığı yer | `microservice.ts` `.catch` |
|
|
414
|
+
> | `Steps.<adımAdı>.data` | **HER ZAMAN `null`**'dır — başarı gövdesi değil, hiçbir gövde değil (eskiden hata gövdesiydi) | `microservice.ts` `.catch` |
|
|
405
415
|
>
|
|
406
416
|
> **`alert` adımının `message`'ı DÜZ METİNDİR — ifade yazarsan ekranda o ifade görünür.**
|
|
407
417
|
> `alert.ts` `task.argument.message`'ı doğrudan `toast.error()`'a geçirir ve `args` hiçbir yerde
|
|
@@ -429,15 +439,24 @@ göstermek error flow'un işidir.
|
|
|
429
439
|
> `{ "type": "confirm", "args": { "message": "Bu risk ve 4 aksiyonu silinecek.", "title": "Riski sil",
|
|
430
440
|
> "confirmText": "Sil", "cancelText": "Vazgeç" } }`.
|
|
431
441
|
|
|
432
|
-
> **Hangi yolu seçeceksin — `errorSteps` mi, ana akışta
|
|
433
|
-
>
|
|
442
|
+
> **Hangi yolu seçeceksin — adımın kendi `error` dalı mı, `errorSteps` mi, ana akışta
|
|
443
|
+
> `condition` mı?** Bir hatada sırayla denenir (yukarıdaki 4xx bölümü) — üçü birden TANIMLI
|
|
444
|
+
> olabilir ama aynı anda yalnız biri koşar: **`error` dalı bağlıysa o kazanır**, yoksa
|
|
445
|
+
> `errorSteps`, ikisi de yoksa `always` + `condition`:
|
|
434
446
|
>
|
|
435
|
-
> | | `errorSteps` | ana akışta `condition Steps.<adımAdı>.error` |
|
|
436
|
-
>
|
|
437
|
-
> |
|
|
438
|
-
> |
|
|
439
|
-
> |
|
|
440
|
-
> |
|
|
447
|
+
> | | adımın kendi `error` dalı (g-02) | `errorSteps` | ana akışta `condition !!Steps.<adımAdı>.error` |
|
|
448
|
+
> |---|---|---|---|
|
|
449
|
+
> | Kapsam | yalnız BU adımın hatası | akıştaki HERHANGİ bir adımın hatası | yalnız BU adımın hatası |
|
|
450
|
+
> | Ana akışın kalanı | **koşmaz** — bu dal devralır | **koşmaz** — hata akışı devralır | koşar, dalı sen seçersin |
|
|
451
|
+
> | Yükleniyor bayrağı · modal kapatma · tablo tazeleme | dalda **yeniden kurmak zorundasın** | hata akışında **yeniden kurmak zorundasın**; kurmazsan bayrak açık kalır | zaten ana akışta duruyor |
|
|
452
|
+
> | Hata metni | `Steps.<adımAdı>.error.message` | `Steps.error.message` (yukarıdaki tablo) | `Steps.<adımAdı>.error.message`; hata olduğunu `!!Steps.<adımAdı>.error` söyler — `.error.status` parametre ifadesi patladıysa `null`dur, o yolu KAÇIRIR |
|
|
453
|
+
> | Ne zaman doğru | hata anında BU adımın sonrası **tamamen başka bir yola** sapacaksa | hata anında akış **tamamen başka bir yola** sapacaksa (başarısız giriş, yetkisiz sayfa) | kalan adımların çoğu hata hâlinde de koşacaksa — **form gönderimlerinin varsayılanı budur** (Kural 7) |
|
|
454
|
+
>
|
|
455
|
+
> **"Ana akışta condition" sütunu `.error`'u okumalı, `.data`'yı değil.** `Steps.<adımAdı>.data`'nın
|
|
456
|
+
> DOLU/BOŞ olmasına bakarak başarı/hata ayırmak (`data.token` gibi bir alanın varlığını yoklamak)
|
|
457
|
+
> artık yanıltıcıdır — `data` hata anında zaten `null`'dur (yukarıdaki §"4xx de hatadır"). Böyle bir
|
|
458
|
+
> `condition`'ı gateway **FLOW-028** ile uyarır; adıma ayrıca bir `error` dalı bağlayın (aşağıdaki
|
|
459
|
+
> 7c örneği) — `error` dalı bağlıysa uyarı düşer, çünkü hata artık `always`'e hiç uğramaz.
|
|
441
460
|
|
|
442
461
|
Zorunlu filtresi olan uçlarda (`patient-*`, `doctor-appointments` gibi `isRequired: true`
|
|
443
462
|
filtreli microservice'ler) parametreyi adımda ver, yoksa uç `MissingParameterException` atar:
|
|
@@ -514,7 +533,7 @@ Tarif: `platform_guide('middleware')`.
|
|
|
514
533
|
|
|
515
534
|
**Sözlükler** (`StepType.cs`, `StepParameterValueType.cs` — hepsi küçük harf):
|
|
516
535
|
`start · microservice · setter · setcookie · validation · condition · redirect · alert ·
|
|
517
|
-
confirm · flow-trigger · storage · navigate · code · download · function · end`
|
|
536
|
+
confirm · flow-trigger · storage · navigate · code · download · function · share · end`
|
|
518
537
|
değer tipleri: `static · page · property · storage · variable · code · item · state`
|
|
519
538
|
|
|
520
539
|
> **Sözlükte olmak KOŞACAĞI anlamına gelmez — koşan tipler barındırıcıdan gelir.** Motor yedi
|
|
@@ -527,7 +546,7 @@ değer tipleri: `static · page · property · storage · variable · code · it
|
|
|
527
546
|
> |---|---|---|---|
|
|
528
547
|
> | `start` · `setter` · `code` · `condition` · `microservice` · `validation` · `flow-trigger` · `function` | ✅ | ✅ | `flow-engine.ts` — motorun kendisi |
|
|
529
548
|
> | `alert` · `redirect` · `setcookie` · `storage` | ✅ | ✅ | barındırıcı; mobilde `setcookie` cihaz deposuna yazar (`MOB-005`) |
|
|
530
|
-
> | `confirm` · `download` | ✅ | ✅ | `Runtime.svelte` · `runtime-tasks.ts` (`download` mobilde açma/paylaşım — `MOB-005`) |
|
|
549
|
+
> | `confirm` · `download` · `share` | ✅ | ✅ | `Runtime.svelte` · `runtime-tasks.ts` (`download` mobilde açma/paylaşım — `MOB-005`; `share` aşağıda) |
|
|
531
550
|
> | `navigate` | ❌ | ✅ (`redirect` takma adı) | web'deki karşılığı `redirect`; gateway `redirect`'e çevirir |
|
|
532
551
|
> | `end` · `query` | ❌ | ❌ | hiçbir yerde — üstelik `query` backend sözlüğünde de yok (`StepType.cs` onu saymaz), yalnız frontend enum'unda duruyor |
|
|
533
552
|
>
|
|
@@ -564,6 +583,19 @@ değer tipleri: `static · page · property · storage · variable · code · it
|
|
|
564
583
|
> adımında ve `{{ }}` bağlamasında `Functions.<ad>(…)` ile de çağrılır; gateway bilinmeyen adı ve
|
|
565
584
|
> fazla argümanı FLOW-025 ile uyarır. Tam gövde `ngapps://examples/flow-call-function`, kurallar
|
|
566
585
|
> **`platform_guide('functions')`**.
|
|
586
|
+
>
|
|
587
|
+
> **`share` adımı ("Share")** metin ve bağlantı paylaşır: `args: { title, message, path }`. `title`
|
|
588
|
+
> DÜZ METİNDİR (ifade yazılırsa FLOW-022 uyarır); `message` ve `path` `storage.value` gibi
|
|
589
|
+
> İFADEDİR — düz metin tırnak ister (`"'Şuna bak'"`, `"'https://…/urun/' + Page.queryParams.id"`).
|
|
590
|
+
> Bağlantının adı `url` DEĞİL `path`'tir: backend `Title`/`Message`/`Path` alanlarında saklar ve
|
|
591
|
+
> tanımadığı anahtarı 200 ile sessizce düşürür (FLOW-027 `url`/`link`/`text`'i ve tırnaksız adresi
|
|
592
|
+
> bloklar). Bağlantı ŞEMALI TAM ADRES olmalı (`https://…`, `mailto:…`): şemasız değer (`www.…`,
|
|
593
|
+
> `urun/42`) adres sayılmaz, metnin sonuna eklenir. İfadenin sonucu yalnız dize ya da sayıysa
|
|
594
|
+
> paylaşılır; `false` (`koşul && 'x'`), `null` ve nesne boş sayılır. Cihazda paylaşım sayfası açılır
|
|
595
|
+
> (Android'de bağlantı metnin sonuna eklenir), web'de tarayıcının paylaşım penceresi, yoksa ya da
|
|
596
|
+
> reddederse metin + bağlantı panoya yazılır ve bildirim çıkar. Tek dal `always`: iOS'ta sayfa
|
|
597
|
+
> kapanınca, Android'de seçici açılır açılmaz (RN seçicinin kapanışını bildirmez) sürer; metin de
|
|
598
|
+
> bağlantı da boşsa hiçbir şey açılmaz, akış hemen sürer.
|
|
567
599
|
|
|
568
600
|
> Bir microservice adımı kaydedildiğinde platform `args.microserviceSchema`'yı
|
|
569
601
|
> (parameterScheme + returnScheme + `isReturnArray`) kendisi doldurur — `flow_get` ile geri
|
|
@@ -1081,15 +1113,22 @@ UYGULAMAZ** — koordinatları aynen kaydeder (aşağı itme yalnız `page_creat
|
|
|
1081
1113
|
> SONRAKİ ağaca bakarak. Layout'ta da geçerlidir: kırılma
|
|
1082
1114
|
> `_repeaterValues` geri yazmasındadır, barındıranın sayfa mı layout mu olduğuna bakmaz.
|
|
1083
1115
|
>
|
|
1116
|
+
> **Mobil uygulamada** başlıklar altında gruplu liste (güne/kategoriye göre) iç içe repeater
|
|
1117
|
+
> gerektirmez: tek `grouped-list` (`groupBy` + başlık `{{ Section.title }}` + öğe şablonu) —
|
|
1118
|
+
> `platform_guide('mobile')` §Güne göre görev listesi. PAGE-013 mesajı mobilde bunu önerir.
|
|
1119
|
+
>
|
|
1084
1120
|
> **Yapılacak:** satır içi listeyi tek bir `textarea`'da **satır başına bir öğe** olarak topla
|
|
1085
1121
|
> (`options_text`), kaydederken `split('\n')`'le böl. Ölçülmüş çalışan sürüm: anket editörü
|
|
1086
1122
|
> (`anket-admin/anket-duzenle`). Gerçekten satır satır düzenleme gerekiyorsa iç listeyi AYRI bir
|
|
1087
1123
|
> sayfada/modalda yönet — iç içe repeater bunu karşılamıyor.
|
|
1088
1124
|
|
|
1089
1125
|
> **Repeater satırını akıştan silmek/eklemek:** repeater ÇOCUĞUNDAKİ bir butonun click akışında
|
|
1090
|
-
>
|
|
1091
|
-
> (`isRepeaterSelf` dalı
|
|
1092
|
-
>
|
|
1126
|
+
> tıklanan satır olay makineye gitmeden SEÇİLİR, yani `q_list.selectedIndex` o satırdır — her runtime
|
|
1127
|
+
> sürümünde. Hedef repeater'ın KENDİSİ olduğu için `rows`'a yazmak serbesttir (`isRepeaterSelf` dalı
|
|
1128
|
+
> satır-değeri yoluna girmez). Desen:
|
|
1129
|
+
> `q_list.rows = (q_list.rows || []).filter(function(r, i){ return i !== q_list.selectedIndex; })`.
|
|
1130
|
+
> `RowIndex` de aynı satırı verir, ama yalnız ARC-121 düzeltmesini (2026-09-27) taşıyan runtime'da;
|
|
1131
|
+
> eskisinde akışta tanımsızdı.
|
|
1093
1132
|
> Ekleme aynı şekilde `.slice()` + `push` ile yapılır; satır numarasını ekledikten sonra yeniden ata.
|
|
1094
1133
|
|
|
1095
1134
|
**Gönderimi saklamak:** satır başına normalize kayıt yerine tek `TEXT` kolonda anlık görüntü
|
|
@@ -1363,6 +1402,16 @@ alanları silinir. Gateway bu yüzden `page_update`'te sayfayı önce okur, yaln
|
|
|
1363
1402
|
alanları değiştirip tam gövdeyi geri yazar (`layout_update` ve `component_update` de aynı).
|
|
1364
1403
|
Yine de `schema` parametresini elle doldurursan okuduğun hâli değil, **verdiğin liste** yazılır.
|
|
1365
1404
|
|
|
1405
|
+
**`page_update` sürümsüz yazmaz (PAGE-039).** Platform sayfa PUT'unu `baseVersion` olmadan reddeder.
|
|
1406
|
+
`baseVersion` vermezsen gateway bu sayfada en son gördüğü sürümü gönderir: `page_get` ya da
|
|
1407
|
+
`page_schema_get` yanıtındaki `version`, `page_schema_patch`'in `newVersion`'ı ya da önceki
|
|
1408
|
+
`page_update`'in yazdığı sürüm. Hiçbiri yoksa istek **gönderilmez** — önce oku. Sayfa o sürümden beri
|
|
1409
|
+
değiştiyse (Studio'da açık sekme, başka ajan) 409 döner; mesaj güncel sürümü ve son yazmayı (zaman,
|
|
1410
|
+
kullanıcı, kaynak, revizyon) söyler ve hiçbir şey yazılmaz. Doğru yol yeniden okuyup değişikliği
|
|
1411
|
+
`page_schema_patch` ile yapmaktır; aradaki değişikliği bilerek ezmek için `force: true` son sürümü
|
|
1412
|
+
okuyup onun üstüne yazar ve yanıt `forced: true` taşır. Sebep: Bitki Bakım koşusunda sürümsüz bir
|
|
1413
|
+
tam gövde yazımı ana sayfayı ve profili boşalttı, akışları sildirdi (14:02).
|
|
1414
|
+
|
|
1366
1415
|
### Atomiklik: 500 KB'lık şemada tek komponente dokunmak
|
|
1367
1416
|
|
|
1368
1417
|
`page_schema_patch` şemanın tamamını göndermez — yalnız op'ları gönderir. Tam gövde ne LLM
|
|
@@ -1372,7 +1421,8 @@ ve geri yazar. Sözleşme (`PatchPageSchemaCommandHandler`):
|
|
|
1372
1421
|
| Aşama | Ne yapar |
|
|
1373
1422
|
|---|---|
|
|
1374
1423
|
| Kilit | Sayfa başına 60 sn'lik dağıtık kilit; başka bir aktör yazıyorsa istek `PageSchemaLockedException` ile döner (409) |
|
|
1375
|
-
| Sürüm | `baseVersion` verdiysen `UpdatedAt.Ticks` ile karşılaştırılır; tutmazsa `PageSchemaVersionConflictException` (409) ve **hiçbir op uygulanmaz** |
|
|
1424
|
+
| Sürüm | `baseVersion` verdiysen `UpdatedAt.Ticks` ile karşılaştırılır; tutmazsa `PageSchemaVersionConflictException` (409) ve **hiçbir op uygulanmaz** — bütün op'lar etkisizse hariç (aşağıdaki "Tekrar" satırı) |
|
|
1425
|
+
| Tekrar | `add_component`'in `elementId`'si zaten varsa ve düğüm istenen ebeveynde, istenen içerikle duruyorsa op **etkisizdir** (hata değil; konum karşılaştırılmaz). Hedef düğümü değiştirmeyen `replace_prop`/`set_*`/`add_event` da etkisizdir. Bütün op'lar etkisizse yama yazılmaz, sürüm artmaz ve eski `baseVersion`'a rağmen 200 `noop: true` döner; sürüm eskiyse `stale: true` ve `newVersion` sizin gönderdiğinizdir. Aynı id'de farklı düğüm hâlâ 422 `duplicate-id`'dir |
|
|
1376
1426
|
| Uygulama | Her op hedefini ağaçta arar (`Find` → JSON Pointer) ve **yalnız o düğüme** dokunur; kardeşler, üst düğümler ve diğer alt ağaçlar bit düzeyinde korunur |
|
|
1377
1427
|
| Hep-ya-hiç | Op'lar önce bellekte uygulanır; **tek bir hata** (bilinmeyen op, bulunamayan hedef, tekrar eden elementId, doğrulama) tüm komutu düşürür — kısmi yazma yoktur |
|
|
1378
1428
|
| Doğrulama | Tam şema `PageSchemaValidator`'dan geçer (422 + alan bazlı `errors`) |
|
|
@@ -1381,7 +1431,18 @@ ve geri yazar. Sözleşme (`PatchPageSchemaCommandHandler`):
|
|
|
1381
1431
|
|
|
1382
1432
|
Yanıt üç alan döner: `applied` (uygulanan op sayısı), **`appliedPatch`** (her op'un şemada tam
|
|
1383
1433
|
olarak hangi JSON Pointer'a ne yaptığı — RFC 6902 biçiminde; "gerçekten yalnız o komponente mi
|
|
1384
|
-
dokunuldu" sorusunun kanıtı) ve **`newVersion`** (`UpdatedAt.Ticks`).
|
|
1434
|
+
dokunuldu" sorusunun kanıtı) ve **`newVersion`** (`UpdatedAt.Ticks`). Etkisiz op varsa `unchanged`
|
|
1435
|
+
(elementId listesi) ve `alreadyApplied` cümlesi gelir; hepsi etkisizse `noop: true`.
|
|
1436
|
+
|
|
1437
|
+
**Yanıt kaybolursa aynı gövdeyle tekrarla.** Ağ koptu, zaman aşımı oldu ve yamanın uygulanıp
|
|
1438
|
+
uygulanmadığını bilmiyorsan aynı `page_schema_patch`'i **aynı gövde ve aynı `baseVersion`** ile
|
|
1439
|
+
yeniden gönder. Uygulanmışsa `noop: true` + `alreadyApplied` döner, hiçbir şey ikinci kez yazılmaz;
|
|
1440
|
+
uygulanmamışsa yama şimdi uygulanır. `add_component` (gövdede `id` verme — platform her istekte yeni
|
|
1441
|
+
`id` üretir, karşılaştırma onu yok sayar), aynı değeri yazan `replace_prop`/`set_*` ve aynı bağı kuran
|
|
1442
|
+
`add_event` tekrarda etkisizdir. İki istisna: `remove_component` ikinci kez `unknown-target` döner — o
|
|
1443
|
+
durumda `page_schema_get` ile bak; eklediğin düğümün bir prop'unu aynı yamanın sonraki op'u
|
|
1444
|
+
değiştirdiyse tekrar `duplicate-id` alır. Yanıt `stale: true` taşıyorsa sayfa sürümünden beri başka bir
|
|
1445
|
+
yazımla da değişmiştir: devam etmeden önce yeniden oku. `idempotencyKey` yoktur ve gerekmez.
|
|
1385
1446
|
|
|
1386
1447
|
> **`appliedPatch` gövdesi VARSAYILAN OLARAK iskelettir.** Girdi `{ op, path }` döner; eklenen
|
|
1387
1448
|
> düğüm `component` altında yalnız `id`/`elementId`/`name`/`type`/`children` taşır. Kanıt zaten
|
|
@@ -1401,12 +1462,29 @@ yamanın `baseVersion`'ıdır. Kontrol platformda atomiktir (satır kilidi + sü
|
|
|
1401
1462
|
`UPDATE`'te), yani iki çağrı arasına giren yazarı da yakalar. `baseVersion` vermezsen gateway yalnız
|
|
1402
1463
|
bu çağrıda zaten okuduğu sayfanın sürümünü gönderir; hiç okumadıysa yama sürümsüz gider ve son
|
|
1403
1464
|
yazan kazanır. Eski `expectedUpdatedAt` hâlâ çalışır ama `baseVersion`'ın yerini tutmaz.
|
|
1465
|
+
|
|
1466
|
+
**Studio da sürüm gönderir (2026-09-26, ZM-27).** Önceden Studio'da açık kalan bir sekme, yayını
|
|
1467
|
+
kaçırdığı yamalarının üstüne kendi eski (bir kez boş) şemasını yazıyor ve bağı kalmayan akışları
|
|
1468
|
+
sildiriyordu. Artık Studio'nun her kaydı sürüm taşır: eski sekme 409 alır ve taslağını senin
|
|
1469
|
+
yazdığının üstüne birleştirir. `revision_list`'te yamandan sonra `source: studio` satırı görürsen bu
|
|
1470
|
+
artık bir üzerine yazma değil, birleştirilmiş bir kayıttır — yine de `revision_diff` ile bak.
|
|
1404
1471
|
Yanıttaki `touched`, kaydı değişen elementId'leri listeler (eklenen alt ağaç dâhil).
|
|
1405
1472
|
|
|
1406
1473
|
> **Büyük şemada çalışma sırası:** `mode:'summary'` ile ağacı çıkar (props yok, 500 KB'lık sayfada
|
|
1407
1474
|
> bile küçük) → hedefin `elementId`'sini al → gerekiyorsa `mode:'subtree'` ile yalnız o dalı oku →
|
|
1408
1475
|
> op'ları `page_schema_validate` ile kuru koştur → `page_schema_patch`. `mode:'full'` yalnız
|
|
1409
1476
|
> gerçekten tüm şema gerektiğinde çağrılır.
|
|
1477
|
+
>
|
|
1478
|
+
> **`full` sığmazsa hata verir, kırpmaz.** Yanıt boyut sınırını aşan şemada `mode:'full'` hata döner
|
|
1479
|
+
> ve düğüm sayısını söyler. Tamamı gerekiyorsa `cursor:'0'` ile oku ve `nextCursor` gelmeyene kadar
|
|
1480
|
+
> sür: her parça `items[]` taşır, kalem `parentElementId` düğümünün `children[index]`'inin yerine
|
|
1481
|
+
> geçer (`parentElementId` yoksa kök dizinin `[index]`'i); alt ağacı sığmayan düğüm kabuk gelir
|
|
1482
|
+
> (`children: [{ elementId, deferred: true }]`) ve sonraki kalemler onu doldurur. Toplanan düğüm sayısı
|
|
1483
|
+
> `nodeCount`'a eşit değilse okuma eksiktir. Okuma arasında sayfa değişirse 409 — baştan başla.
|
|
1484
|
+
> `allowTruncated:true` kırpılmış gövdeyi açıkça verir (`truncated: true`); o gövdeye dayanan
|
|
1485
|
+
> `page_schema_patch` PAGE-038 uyarısı taşır. **`page_update`'e kırpık ya da yarım şema gönderme:**
|
|
1486
|
+
> PUT tam REPLACE'tir, eksik düğümler geri dönüşsüz silinir — gateway düğüm eksilten şemayı
|
|
1487
|
+
> `allowNodeLoss:true` olmadan PAGE-037 ile reddeder.
|
|
1410
1488
|
|
|
1411
1489
|
> **`remove_component`'in yan etkisi — tek istisna.** Sayfa kaydedilirken artık hiçbir komponent
|
|
1412
1490
|
> olayının, prop'unun ya da sayfa olayının referans vermediği akışlar platformca **kalıcı olarak
|
|
@@ -1831,15 +1909,24 @@ page_container
|
|
|
1831
1909
|
```
|
|
1832
1910
|
start → [setter] btn.isLoading = true, btn.isDisabled = true, notification gizle
|
|
1833
1911
|
→ [microservice] sign-in (POST; email + password body'den)
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1912
|
+
├─ error → [setter] btn.isLoading=false, btn.isDisabled=false,
|
|
1913
|
+
│ notification.content =
|
|
1914
|
+
│ { type:'code', ref:'Steps.signIn.error?.message ?? "Giriş başarısız"' }
|
|
1915
|
+
└─ always → [condition] !!Steps.signIn.data.token
|
|
1916
|
+
├─ always → [setter] buton durumunu sıfırla
|
|
1917
|
+
├─ if → [setcookie] { name:'APP_Token', value:'Steps.signIn.data.token',
|
|
1918
|
+
│ expiresInHours: 240 }
|
|
1919
|
+
│ → [redirect] hedef sayfaya
|
|
1920
|
+
└─ else → [setter] notification.content =
|
|
1921
|
+
{ type:'code', ref:'Steps.signIn.error?.message ?? "Giriş başarısız"' }
|
|
1841
1922
|
```
|
|
1842
1923
|
|
|
1924
|
+
- **`sign-in` adımının `error` dalı burada ZORUNLUDUR, süs değil.** Dal bağlı değilken bu şablonun
|
|
1925
|
+
`condition`'ı `Steps.signIn.data.token`'ı okur ve gateway'i **FLOW-028** ile uyarır (yukarıdaki
|
|
1926
|
+
"Hangi yolu seçeceksin" notu): hata anında `data` `null`'dur, token okuması sessizce `undefined`
|
|
1927
|
+
döner. Dal bağlanınca hata onun üzerinden sürer — `always`/`condition` yalnız BAŞARIDA çalışır,
|
|
1928
|
+
yani `data.token` orada gerçek bir iş kuralıdır (uç 200 dönüp token'sız yanıt verirse), hata
|
|
1929
|
+
algılama YÖNTEMİ değil.
|
|
1843
1930
|
- **Cookie adı middleware'in okuduğu adla birebir aynı olmalı** (`cookie.APP_Token` —
|
|
1844
1931
|
`platform_guide('middleware')` Tarif 1). Uyuşmazlık sessizce "hep 401" demektir.
|
|
1845
1932
|
- `redirect` adımı `isRaw: true` iken `path` **çok satırlı JS bloğudur** (son ifade değeri döner) —
|
|
@@ -2029,9 +2116,14 @@ hiçbiri** bağlı değildi; üç portalın da rol menüsü boş çıkıyor, pan
|
|
|
2029
2116
|
> (`dto_get` ile bak: genelde `role`, `role_name`, `roles_csv` vardır). Sayfa tarafında CSV
|
|
2030
2117
|
> kullan — ayrıntı ve şablon `platform_guide('expressions')` §2.
|
|
2031
2118
|
>
|
|
2032
|
-
> **Uç hata dönerse `.data`
|
|
2033
|
-
>
|
|
2034
|
-
> `(items ?? []).map is not a function
|
|
2119
|
+
> **Uç hata dönerse `.data` NULL'dur, hata gövdesi değil (ADR 0004, 2026-09-26).** 2026-09-26'ya
|
|
2120
|
+
> kadar `Steps.x.data` 401/404 sonrası hata gövdesiydi ve `rows || []` bunu yakalamıyordu (nesne
|
|
2121
|
+
> truthy) → `rows.filter is not a function` / `(items ?? []).map is not a function`; artık uç hata
|
|
2122
|
+
> dönünce `data` **null**'dur ve bu adım boş veriyle koşar — `rows` gibi bir listeye bağlarsan
|
|
2123
|
+
> bileşen `null`'ı boş liste sayar, `Array.isArray(...) ? ... : []` sarmalamaya artık gerek yok.
|
|
2124
|
+
> Hata OLDUĞUNU fark etmeden geçmemek için `error` dalı bağlayın (yukarıdaki §"4xx de hatadır",
|
|
2125
|
+
> gateway bunu bağlı değilken **FLOW-028** ile uyarır); hata gövdesini görmek istiyorsan
|
|
2126
|
+
> `Steps.x.error.body`'yi okuyun.
|
|
2035
2127
|
|
|
2036
2128
|
## ⚠️ `page_schema_patch` `html` komponentini reddediyor — katalogda mükerrer kayıt (2026-08-19)
|
|
2037
2129
|
|
|
@@ -2094,6 +2186,31 @@ micro-op hattına dönün.
|
|
|
2094
2186
|
> uyarı yoktu. Silinmesini bilerek istiyorsanız çağrıyı `allowFlowDeletion: true` ile tekrarlayın;
|
|
2095
2187
|
> yanıt `deletedFlows` ile hangi akışların gittiğini listeler.
|
|
2096
2188
|
>
|
|
2189
|
+
> **Silinen akış geri kurulabilir (2026-09-26, ZM-27).** Sayfa kaydının sildiği akış artık revizyon
|
|
2190
|
+
> defterine `operation: delete` satırı olarak düşer (kaynağı `studio` ya da `mcp`). `flow_restore`
|
|
2191
|
+
> akışı son revizyonundaki ad, slug ve adımlarla AYNI id ile yeniden kurar; olaya bağlamaz —
|
|
2192
|
+
> geri geldikten sonra `set_event`. İki sınır: revizyon saklama süresi (budanan akış 404) ve
|
|
2193
|
+
> adımların hedeflediği komponentler — akış bir komponenti tetikleyen/parametre olarak
|
|
2194
|
+
> gösteriyorsa o komponent şemada durmalıdır (canlı ölçüldü: `flow_restore` "Trigger Element Id"
|
|
2195
|
+
> dış anahtarına çarpar).
|
|
2196
|
+
>
|
|
2197
|
+
> **Boşalan sayfa: `page_restore` (2026-09-26).** Sayfayı ve akışlarını birlikte geri kurar:
|
|
2198
|
+
> `revision_list(module: 'page', recordId: <pageId>)` → boşalmadan önceki revizyonu bul (`source` ve
|
|
2199
|
+
> `correlationId` yardım eder; `revision_get` ile içeriğine bak) → `page_restore({ revisionNo })`.
|
|
2200
|
+
> Platform önce eksik akışları adımsız kurar, sonra şemayı eski komponent id'leriyle yazar, sonra
|
|
2201
|
+
> adımları yazar — akış→komponent ve olay→akış dış anahtarları bu sırayla tek transaction'da çözülür.
|
|
2202
|
+
> Yanıt `restoredFlows` ve `deletedFlows` (N'den sonra bağlanmış, şimdi yetim kalan) döner.
|
|
2203
|
+
> Revizyonu kalmamış bir akış varsa 409 ve hiçbir şey yazılmaz.
|
|
2204
|
+
>
|
|
2205
|
+
> **Doğru revizyonu seç.** Şemayı yüklemeyen komutların (`UpdateProjectCommand`,
|
|
2206
|
+
> `UpdateApplicationCommand`…) yazdığı sayfa revizyonları komponent taşımaz; komponenti olan sayfayı
|
|
2207
|
+
> böyle bir revizyona döndürmek 409 `PageRevisionEmptyException` ile reddedilir (bilerek boşaltmak için
|
|
2208
|
+
> `allowEmpty: true`). `correlationId`'si `PatchPageSchemaCommand` ya da `UpdatePageCommand` olan
|
|
2209
|
+
> revizyonu seç. İki sınır daha: N'den beri silinen layout/rol/middleware/özel komponent ya da başka
|
|
2210
|
+
> sayfanın aldığı slug geri yüklemeyi dış anahtar/benzersizlik hatasıyla düşürür (transaction geri
|
|
2211
|
+
> alınır, veri kaybı yok); yalnız başka bir akışın adımından çağrılan (olaya bağlı olmayan) akışlar
|
|
2212
|
+
> geri kurulmaz — onlar için `flow_restore`.
|
|
2213
|
+
>
|
|
2097
2214
|
> **FLOW-018 artık silinen düğümün ALT AĞACINI da görüyor.** Eskiden yalnız op'un kendi
|
|
2098
2215
|
> `target`'ına bakılıyordu: bir kapsayıcı silinince ÇOCUĞUNA yazan setter sessizce kopuyor ve
|
|
2099
2216
|
> uyarı hiç çıkmıyordu (09-05, `sign_in_form` → `btn_sign_in`).
|
|
@@ -2137,6 +2254,9 @@ micro-op hattına dönün.
|
|
|
2137
2254
|
style + Kural 1h deseniyle yeniden kurulunca kart 400px'e ve ortaya oturdu. Gateway bunu **PAGE-014 (uyarı)** ile
|
|
2138
2255
|
yanıtın `layoutWarnings` alanında bildirir. **Düzeni `style` ile kurmaya hiç kalkışma:**
|
|
2139
2256
|
ortalama/yaslama/aralık işi kapsayıcının kendi `layout: 'flex'` prop grubuna aittir (Kural 1f-2).
|
|
2257
|
+
**İstisna `html`:** onun `style`'ı yazarın işaretlemesine kapsamlı giren CSS metnidir (web'de iç
|
|
2258
|
+
seçicili sınıf, mobilde belgenin stil bloğu) — string asıl biçimdir ve PAGE-014 onda susar. Mobilde
|
|
2259
|
+
`html`'e nesne `style` verme: çizilmez (MOB-002).
|
|
2140
2260
|
- **Menüde "bulunduğun sayfa" vurgusu: `sidebar`/`layout`'ta `activeHref`, `header`'da
|
|
2141
2261
|
`activeNavHref`.** `sidebar` ve `layout` menü ağacını ortak renderer'la çizer: `activeHref` boşken
|
|
2142
2262
|
tarayıcıdaki adrese uyan satırı kendileri vurgular, prop yazılırsa o satır vurgulanır (akıştan
|
|
@@ -98,7 +98,10 @@ theme_update({ theme: {
|
|
|
98
98
|
```
|
|
99
99
|
|
|
100
100
|
**Yazı tipi ve ölçek:** `fontFamily` panelin Google Fonts listesinden bir ad olmalı — runtime
|
|
101
|
-
fontu ADIYLA Google'dan çeker, tanınmayan ad sessizce sans-serif'e düşer (THEME-008).
|
|
101
|
+
fontu ADIYLA Google'dan çeker, tanınmayan ad sessizce sans-serif'e düşer (THEME-008). Mobilde de
|
|
102
|
+
aynı kural geçerlidir: cihaz aynı adı Google Fonts'tan TTF olarak indirip `expo-font` ile yükler
|
|
103
|
+
(400 ve 700 kalınlık) — Android'de kalın yüz SENTETİKTİR (iOS gerçek kalın dosyayı çizer,
|
|
104
|
+
ZM-10, 2026-09-26).
|
|
102
105
|
|
|
103
106
|
```jsonc
|
|
104
107
|
theme_update({ theme: {
|
|
@@ -648,9 +648,15 @@ Tam örnek, ölçülen çıktı şekli ve `code` çocuğu tuzağının çıkış
|
|
|
648
648
|
|
|
649
649
|
```js
|
|
650
650
|
// dizi (isSingle:false) · nesne (isSingle:true) · düz değer (yalnız pk dönen yazma)
|
|
651
|
-
(
|
|
651
|
+
(x.data && typeof x.data !== 'string' && typeof x.data.length === 'number' ? (x.data[0] && x.data[0].id) : (x.data && x.data.id !== undefined ? x.data.id : x.data))
|
|
652
652
|
```
|
|
653
653
|
|
|
654
|
+
Diziyi `Array.isArray` ile değil `.length` ile tanıyın: 2026-09-27'den önceki backend'de tablo
|
|
655
|
+
adımının listesi Jint'e sarılı bir CLR nesnesi olarak iniyordu ve `Array.isArray(x.data)` `false`
|
|
656
|
+
dönüyordu — koşuda (2026-09-24) giriş akışı bu yüzden her kullanıcıyı 401 ile reddetti
|
|
657
|
+
(`zeyrek-backend/docs/incident-log.md` #89). `.length`, `.map`, `.filter` o sürümde de çalışıyordu;
|
|
658
|
+
düzeltilmiş backend'de `Array.isArray` de doğrudur.
|
|
659
|
+
|
|
654
660
|
Şekli bilmiyorsan bu yazımı kullan; `workflow_preview` çıktısında `steps[].output`'a bakarak
|
|
655
661
|
hangi vakada olduğunu da görebilirsin.
|
|
656
662
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
|
-
"hash": "
|
|
3
|
-
"generatedAt": "2026-09-
|
|
2
|
+
"hash": "466761837eb9a463fe5984ca119fc04fbdc946cf363177b71efd1b195b8729ca",
|
|
3
|
+
"generatedAt": "2026-09-27T17:47:09.197Z",
|
|
4
4
|
"types": 186,
|
|
5
5
|
"files": 108,
|
|
6
6
|
"note": "Şemaları üreten backend kaynaklarının parmak izi. Elle düzenlemeyin: 'npm run generate-schemas' yazar, 'npm run schema:check' denetler.",
|
package/dist/server.js
CHANGED
|
@@ -84,7 +84,7 @@ const moduleRegistrations = {
|
|
|
84
84
|
mobile: registerMobileTools,
|
|
85
85
|
};
|
|
86
86
|
export function createGatewayServer(platform) {
|
|
87
|
-
const server = rejectUnknownArguments(instrumentServer(new McpServer({ name: "ngapps", version: "0.1.0" }), platform.config));
|
|
87
|
+
const server = rejectUnknownArguments(instrumentServer(new McpServer({ name: "ngapps", version: "0.1.0" }), platform.config), platform);
|
|
88
88
|
for (const moduleName of platform.config.modules) {
|
|
89
89
|
moduleRegistrations[moduleName](server, platform);
|
|
90
90
|
}
|
|
@@ -118,7 +118,7 @@ export function registerDatasourceTools(server, platform) {
|
|
|
118
118
|
}, handle(config, ({ project, environment }) => platform.tenant('GET', project, '/datasources/resources', { environment })));
|
|
119
119
|
server.registerTool('datasource_configuration_options', {
|
|
120
120
|
title: 'Yapılandırma seçenekleri',
|
|
121
|
-
description: "Datasource'un alan tipi kataloğunu döner: commonType başına dbType listesi, varsayılan tip, tipin kabul ettiği seçenek anahtarları (length/precision/collation/...) ve dönüştürülebilir tipler; ayrıca ilişki kuralları (onUpdate/onDelete) ve identity generation tipleri. VARSAYILAN YANIT ÖZETTİR (~5 KB): collation DEĞER listesi (816 kayıt, tam yanıtın %79'u) dışarıdadır — tipin collation kabul edip etmediği zaten options'ta yazar. Collation değerleri gerçekten gerekiyorsa includeCollations: true ile tam yanıtı isteyin (~45 KB).",
|
|
121
|
+
description: "Datasource'un alan tipi kataloğunu döner: commonType başına dbType listesi, varsayılan tip, tipin kabul ettiği seçenek anahtarları (length/precision/collation/...) ve dönüştürülebilir tipler; ayrıca ilişki kuralları (onUpdate/onDelete) ve identity generation tipleri. İlişki alanına yazarken kuralı SAYI olarak verin (`None` 0 · `Cascade` 1 · `SetNull` 2 · `SetDefault` 3 — `Rule` sırası): her backend sürümünde çalışır. Katalog adı listeler; `onDelete` adı (`Cascade`) 2026-09-27'den sonraki backend'de de kabul edilir, `onUpdate` adı hâlâ reddedilir (500). VARSAYILAN YANIT ÖZETTİR (~5 KB): collation DEĞER listesi (816 kayıt, tam yanıtın %79'u) dışarıdadır — tipin collation kabul edip etmediği zaten options'ta yazar. Collation değerleri gerçekten gerekiyorsa includeCollations: true ile tam yanıtı isteyin (~45 KB).",
|
|
122
122
|
inputSchema: {
|
|
123
123
|
project: projectParam,
|
|
124
124
|
environment: environmentParam,
|
|
@@ -87,7 +87,8 @@ export function registerDesignSchemaPatchTool(server, platform, host) {
|
|
|
87
87
|
if (deletedFlows.length > 0 && allowFlowDeletion !== true)
|
|
88
88
|
throw new PlatformApiError(orphanedFlowMessage(deletedFlows), 400);
|
|
89
89
|
const added = opRecords.filter(entry => entry.op === 'add_component').map(entry => entry.component);
|
|
90
|
-
|
|
90
|
+
const resolved = await resolveProfile(platform, applicationId);
|
|
91
|
+
assertScreenWriteGuards(spec.kind, { schema: added }, resolved.profile.platform);
|
|
91
92
|
const catalogNotes = await screenCatalogNotes(platform, project, environment, applicationId, added);
|
|
92
93
|
const flowWarnings = opRecords.some(entry => entry.op === 'remove_component')
|
|
93
94
|
? [...orphanedFlowWarnings(nodes, events, opRecords), ...staleSetterWarnings(Array.isArray(record.flows) ? record.flows : [], opRecords, nodes)]
|
|
@@ -102,12 +103,12 @@ export function registerDesignSchemaPatchTool(server, platform, host) {
|
|
|
102
103
|
|| (entry.op === 'replace_prop' && FLEX_LAYOUT_PROP_NAMES.has(String(entry.prop ?? ''))));
|
|
103
104
|
const nested = simulatable ? nestedRepeaterNodes(simulated) : [];
|
|
104
105
|
if (nested.length > 0)
|
|
105
|
-
throw new PlatformApiError(nestedRepeaterMessage(nested), 400);
|
|
106
|
-
const resolved = await resolveProfile(platform, applicationId);
|
|
106
|
+
throw new PlatformApiError(nestedRepeaterMessage(nested, resolved.profile.platform), 400);
|
|
107
107
|
const reshaped = simulatable || opRecords.some(entry => entry.op === 'remove_component');
|
|
108
108
|
const profileWarnings = await screenProfileWarnings(platform, resolved, project, environment, applicationId, {
|
|
109
109
|
kind: spec.kind,
|
|
110
110
|
ops: [...opRecords, ...propTypeOps],
|
|
111
|
+
savedNodes: nodes,
|
|
111
112
|
steps: await boundFlowSteps(platform, resolved, project, environment, host, applicationId, id, opRecords)
|
|
112
113
|
});
|
|
113
114
|
const layoutWarnings = reshaped
|
|
@@ -21,7 +21,7 @@ async function assertExternalComponentsAllowed(platform, applicationId, tool) {
|
|
|
21
21
|
throw new PlatformApiError(`${tool}: ${findingLine({
|
|
22
22
|
code: 'MOB-006',
|
|
23
23
|
severity: 'warn',
|
|
24
|
-
message: "harici bileşen mobil uygulamada desteklenmiyor — protokol v1 tarayıcıda koşan .mjs custom element'tir, native çizer onu yükleyemez (mobil protokol v2 Faz 3). Çağrı platforma gönderilmedi; mobil ekranı katalog bileşenleriyle kurun (component_list)."
|
|
24
|
+
message: "harici bileşen mobil uygulamada desteklenmiyor — protokol v1 tarayıcıda koşan .mjs custom element'tir, native çizer onu yükleyemez (mobil protokol v2 Faz 3). Çağrı platforma gönderilmedi; mobil ekranı katalog bileşenleriyle kurun (component_list). Katalogda karşılığı olmayan küçük görünüm için geçici yol 'html' bileşenidir: içerik korumalı belgede tema değişkenleriyle çizilir, fireEvent/ngApps.emit ile akışa bağlanır (component_get('html'))."
|
|
25
25
|
})}`, 400);
|
|
26
26
|
}
|
|
27
27
|
/** Platformun köprü protokolü; scaffold ve publish bu sürüme göre üretir/doğrular (backend ExternalComponentRules.CurrentProtocolVersion) */
|
package/dist/tools/flows.js
CHANGED
|
@@ -275,7 +275,7 @@ async function functionWarnings(platform, host, project, environment, applicatio
|
|
|
275
275
|
}
|
|
276
276
|
const missing = fn.parameters.filter(parameter => parameter.required && !given.includes(parameter.name)).map(parameter => parameter.name);
|
|
277
277
|
if (missing.length > 0) {
|
|
278
|
-
warnings.add(`FLOW-024: '${label(step)}' adımında '${fn.name}' fonksiyonunun zorunlu parametresi eksik: ${missing.join(', ')} — çağrı 'Functions.${fn.name}: "${missing[0]}" is required' ile düşer, Steps.${String(step.name ?? label(step))}.error dolar ve
|
|
278
|
+
warnings.add(`FLOW-024: '${label(step)}' adımında '${fn.name}' fonksiyonunun zorunlu parametresi eksik: ${missing.join(', ')} — çağrı 'Functions.${fn.name}: "${missing[0]}" is required' ile düşer, Steps.${String(step.name ?? label(step))}.error.message dolar ve .data null'dur; errorSteps yoksa akış 'always' dalından devam eder.`);
|
|
279
279
|
}
|
|
280
280
|
}
|
|
281
281
|
for (const { step, origin, ref } of callSites) {
|
|
@@ -296,6 +296,60 @@ async function functionWarnings(platform, host, project, environment, applicatio
|
|
|
296
296
|
}
|
|
297
297
|
return [...warnings];
|
|
298
298
|
}
|
|
299
|
+
/**
|
|
300
|
+
* FLOW-028 (g-02, ADR 0004 — ZM-07). `microservice.ts` `.finally` üç yoldan birini dener: adımın
|
|
301
|
+
* kendi `error` dalı bağlıysa oradan sürer, yoksa flow `errorSteps`'i (`engine.onError`) devralır,
|
|
302
|
+
* ikisi de yoksa `always` koşar ve storage artık `{ data: null, headers: null, error }`'dır — hata
|
|
303
|
+
* gövdesi ARTIK `data`'da DEĞİL. İkisi de bağlı değilken bu adımın `.data`'sını okuyan bir
|
|
304
|
+
* setter/condition varsa hata anında NULL üzerinden koşar ve yazar hiç fark etmez. Aynı yürüyüş
|
|
305
|
+
* FLOW-024/025'in kullandığı `flowExpressionSites` taramasıdır; setter'ın STORAGE (kod olmayan)
|
|
306
|
+
* referansı ayrıca kendi düz alanından okunur (FLOW-008/009 deseni, `rowsTargetsByStepName`).
|
|
307
|
+
*/
|
|
308
|
+
function microserviceAlwaysDataWarnings(steps, errorSteps) {
|
|
309
|
+
const mainSteps = (Array.isArray(steps) ? steps : []).filter(isRecord);
|
|
310
|
+
const hasErrorFlow = (Array.isArray(errorSteps) ? errorSteps : []).filter(isRecord).some(step => String(step.type ?? '') !== 'start');
|
|
311
|
+
const label = (step) => String(step.stepId ?? step.name ?? '?');
|
|
312
|
+
const escapeRegExp = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
313
|
+
const warnings = new Set();
|
|
314
|
+
for (const step of mainSteps) {
|
|
315
|
+
if (String(step.type ?? '') !== 'microservice')
|
|
316
|
+
continue;
|
|
317
|
+
const name = String(step.name ?? step.stepId ?? '');
|
|
318
|
+
if (name === '')
|
|
319
|
+
continue;
|
|
320
|
+
const hasErrorBranch = (Array.isArray(step.branches) ? step.branches : [])
|
|
321
|
+
.filter(isRecord)
|
|
322
|
+
.some(branch => String(branch.name ?? '') === 'error' && Boolean(branch.nextStepId));
|
|
323
|
+
if (hasErrorBranch || hasErrorFlow)
|
|
324
|
+
continue;
|
|
325
|
+
const pattern = new RegExp(`Steps\\s*(?:\\.\\s*${escapeRegExp(name)}\\b|\\[\\s*['"]${escapeRegExp(name)}['"]\\s*\\])\\s*\\??\\.\\s*data\\b`);
|
|
326
|
+
const message = (other) => `FLOW-028: '${label(other)}' adımı 'always' dalında '${name}.data' okuyor ama '${name}' adımının ne bağlı bir 'error' dalı ne akışın 'errorSteps'i var — uç hata dönerse bu adımın 'data'sı NULL olur (eskiden hata gövdesiydi, ADR 0004) ve '${label(other)}' boş/null veriyle koşar. '${name}' adımına 'error' dalı bağlayın ya da flow'a errorSteps verin.`;
|
|
327
|
+
for (const other of mainSteps) {
|
|
328
|
+
if (other === step)
|
|
329
|
+
continue;
|
|
330
|
+
if (String(other.type ?? '') === 'setter') {
|
|
331
|
+
const iteratees = Array.isArray(other.args?.iteratees)
|
|
332
|
+
? other.args.iteratees.filter(isRecord)
|
|
333
|
+
: [];
|
|
334
|
+
for (const iteratee of iteratees) {
|
|
335
|
+
const mapping = (iteratee.mapping ?? {});
|
|
336
|
+
const value = (iteratee.value ?? {});
|
|
337
|
+
const reference = typeof mapping.source === 'string' && mapping.source.length > 0
|
|
338
|
+
? mapping.source
|
|
339
|
+
: typeof value.ref === 'string' ? value.ref : '';
|
|
340
|
+
if (value.type === 'storage' && (reference === `${name}.data` || reference.startsWith(`${name}.data.`) || reference.startsWith(`${name}.data[`))) {
|
|
341
|
+
warnings.add(message(other));
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
for (const site of flowExpressionSites(other)) {
|
|
346
|
+
if (pattern.test(site.expression))
|
|
347
|
+
warnings.add(message(other));
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
return [...warnings];
|
|
352
|
+
}
|
|
299
353
|
export function registerFlowTools(server, platform) {
|
|
300
354
|
const config = platform.config;
|
|
301
355
|
server.registerTool('flow_list', {
|
|
@@ -369,7 +423,8 @@ export function registerFlowTools(server, platform) {
|
|
|
369
423
|
...await requiredParameterWarnings(platform, project, environment, applicationId, steps),
|
|
370
424
|
...await functionWarnings(platform, host, project, environment, applicationId, hostId, steps),
|
|
371
425
|
...flowBodyWarnings(steps),
|
|
372
|
-
...unknownSetterTargetWarnings(steps, errorSteps ?? [], hostNodes(hostRecord))
|
|
426
|
+
...unknownSetterTargetWarnings(steps, errorSteps ?? [], hostNodes(hostRecord)),
|
|
427
|
+
...microserviceAlwaysDataWarnings(steps, errorSteps ?? [])
|
|
373
428
|
];
|
|
374
429
|
// Gövde yalnız akıştır (designer-partial-update K1): barındırıcı zarfı platformda hâlâ isteğe bağlı
|
|
375
430
|
// ama gönderilirse okunan tam kaydı yeniden yazar, açık Studio designer'ının kayıtlı tabanını
|
|
@@ -438,7 +493,8 @@ export function registerFlowTools(server, platform) {
|
|
|
438
493
|
...await requiredParameterWarnings(platform, project, environment, applicationId, flow.steps),
|
|
439
494
|
...await functionWarnings(platform, host, project, environment, applicationId, hostId, flow.steps),
|
|
440
495
|
...flowBodyWarnings(flow.steps),
|
|
441
|
-
...unknownSetterTargetWarnings(flow.steps, flow.errorSteps, hostNodes(hostRecord))
|
|
496
|
+
...unknownSetterTargetWarnings(flow.steps, flow.errorSteps, hostNodes(hostRecord)),
|
|
497
|
+
...microserviceAlwaysDataWarnings(flow.steps, flow.errorSteps)
|
|
442
498
|
];
|
|
443
499
|
await platform.tenant('PUT', project, flowPath(host, applicationId, hostId, flowId), { environment, body: { flow } });
|
|
444
500
|
// Kısmi güncellemede yalnız GÖNDERİLEN alanlar karşılaştırılır; verilmeyen alan için
|
|
@@ -490,7 +546,8 @@ export function registerFlowTools(server, platform) {
|
|
|
490
546
|
...await requiredParameterWarnings(platform, project, environment, applicationId, flow.steps),
|
|
491
547
|
...await functionWarnings(platform, host, project, environment, applicationId, hostId, flow.steps),
|
|
492
548
|
...flowBodyWarnings(flow.steps),
|
|
493
|
-
...unknownSetterTargetWarnings(flow.steps, flow.errorSteps, hostNodes(hostRecord))
|
|
549
|
+
...unknownSetterTargetWarnings(flow.steps, flow.errorSteps, hostNodes(hostRecord)),
|
|
550
|
+
...microserviceAlwaysDataWarnings(flow.steps, flow.errorSteps)
|
|
494
551
|
];
|
|
495
552
|
// Gövde değil FARK gönderilir; platform PATCH ucu akışı tek başına yazar. Net diff okunan kayıt ile son hâl arasından hesaplanır.
|
|
496
553
|
const netOps = fastJsonPatch.compare(current, flow);
|
|
@@ -521,4 +578,13 @@ export function registerFlowTools(server, platform) {
|
|
|
521
578
|
await platform.tenant('DELETE', project, flowPath(host, applicationId, hostId, flowId), { environment, body: { id: flowId } });
|
|
522
579
|
return { deleted: flowId, host, hostId };
|
|
523
580
|
}));
|
|
581
|
+
server.registerTool('flow_restore', {
|
|
582
|
+
title: 'Silinen akışı geri yükle',
|
|
583
|
+
description: "Silinmiş bir akışı revizyon defterindeki son içeriğiyle (ad, slug, adımlar, barındırıcı) AYNI id ile yeniden kurar. Sayfa kaydı bağı kalmayan akışı kalıcı siler (FLOW-002, allowFlowDeletion) — Studio'nun eski sekmesi boş şema kaydettiğinde de (ZM-27). Akışı olaya YENİDEN BAĞLAMAZ: geri geldikten sonra set_event ile bağlayın. SINIR: adımları sayfanın bir komponentini hedefliyorsa (tetikleyen element, iterate/parametre komponenti) o komponent de şemada olmalıdır, yoksa geri yükleme reddedilir — sayfa boşaldıysa (ZM-27) page_restore kullanın: sayfayı ve akışlarını birlikte geri kurar. Akış hâlâ varsa 409, saklama süresi dolup revizyonu budanmışsa 404 döner. Silinen akışı revision_list(module: 'flow') satırlarında operation: delete olarak bulun; recordId akış id'sidir.",
|
|
584
|
+
inputSchema: { project: projectParam, environment: environmentParam, flowId: flowIdParam },
|
|
585
|
+
annotations: writes
|
|
586
|
+
}, handle(config, async ({ project, environment, flowId }) => {
|
|
587
|
+
const restored = unwrap(await platform.tenant('POST', project, `/flows/${flowId}/restore`, { environment }));
|
|
588
|
+
return { restored: flowId, flow: restored };
|
|
589
|
+
}));
|
|
524
590
|
}
|