@appaflytech/zeyrek-ai-mcp 0.0.40 → 0.0.41
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/dist/knowledge/componentCatalog.js +68 -7
- package/dist/knowledge/componentEvents.js +33 -0
- package/dist/knowledge/mobileComponentCatalog.js +5 -5
- package/dist/knowledge/normalize.js +30 -12
- package/dist/knowledge/platform/mobile.js +1 -1
- package/dist/knowledge/platform/web.js +1 -1
- package/dist/knowledge/rules.js +166 -24
- package/dist/knowledge/styleResolve.js +0 -1
- package/dist/platform/capture/actions.js +24 -1
- package/dist/platform/capture/summary.js +4 -1
- package/dist/resources/examples/mobile-app-shell-drawer.json +1 -0
- package/dist/resources/examples/mobile-list-detail-form.json +3 -0
- package/dist/resources/guides/entity.md +16 -0
- package/dist/resources/guides/expressions.md +7 -6
- package/dist/resources/guides/microservice.md +1 -1
- package/dist/resources/guides/middleware.md +2 -0
- package/dist/resources/guides/mobile.md +28 -19
- package/dist/resources/guides/page.md +47 -16
- package/dist/resources/guides/translation.md +41 -1
- package/dist/resources/guides/workflow.md +31 -1
- package/dist/resources/schema-fingerprint.json +2 -2
- package/dist/tools/entities.js +41 -4
- package/dist/tools/layouts.js +22 -7
- package/dist/tools/mobile.js +15 -1
- package/dist/tools/pages.js +28 -42
- package/dist/tools/queries.js +1 -1
- package/dist/tools/rows.js +14 -3
- package/dist/tools/screenWrite.js +2 -2
- package/dist/tools/screens.js +39 -0
- package/dist/tools/translations.js +148 -6
- package/package.json +1 -1
|
@@ -179,12 +179,12 @@ istekle gider.
|
|
|
179
179
|
Gateway alan tipini şemadan bilemediği için uyarı metinseldir: `.value` ile biten en az üç parçalı
|
|
180
180
|
yol (`urlInput.value` gibi bileşen değeri ve `fileInput1.value.value` uyarı almaz).
|
|
181
181
|
|
|
182
|
-
Workflow'un
|
|
183
|
-
|
|
182
|
+
Workflow'un `file-upload` adımı da aynı nesneyi döndürür, adres `<adım>.data.absolute_path`
|
|
183
|
+
(`platform_guide('workflow')`). `page_screenshot` ile listede görseli doğrulayın.
|
|
184
184
|
|
|
185
185
|
## Liste ve boş durumu
|
|
186
186
|
|
|
187
|
-
Mobil `repeater`'ın çocukları TAM İKİ `view`'dır (web
|
|
187
|
+
Mobil `repeater`'ın çocukları TAM İKİ `view`'dır (web'de tek şablon) — satır şablonu ve boş görünüm (ADR
|
|
188
188
|
docs/adr/0005; başka şekil 422 `fixed-views`). Satır yokken boş görünüm bir kez, tam genişlikte
|
|
189
189
|
çizilir; boş ekranı oraya tasarla. `emptyTitle`/`emptyDescription`/`emptyIcon` prop'ları ve kardeş
|
|
190
190
|
`emptystate` yolu 2026-09-30'da kaldırıldı. Mobil `view` çocuklarını sütunda dizer. Tam gövde:
|
|
@@ -204,15 +204,15 @@ bölümleri bileşen render anında çıkarır.
|
|
|
204
204
|
3. **Çocuklar konumsaldır:** çocuk 0 bölüm başlığı, çocuk 1 öğe şablonu; fazlası çizilmez.
|
|
205
205
|
|
|
206
206
|
```json
|
|
207
|
-
{ "elementId": "tasks", "name": "tasks", "type": "grouped-list",
|
|
207
|
+
{ "elementId": "tasks", "name": "tasks", "type": "grouped-list", "blocks": [],
|
|
208
208
|
"props": { "rows": { "type": "code", "ref": "{{ q_tasks.data }}" },
|
|
209
|
-
|
|
210
|
-
|
|
209
|
+
"groupBy": { "type": "static", "data": "dueDate" },
|
|
210
|
+
"itemPressable": { "type": "static", "data": true } },
|
|
211
211
|
"children": [
|
|
212
|
-
{ "elementId": "day_title", "name": "day_title", "type": "text",
|
|
212
|
+
{ "elementId": "day_title", "name": "day_title", "type": "text", "blocks": [],
|
|
213
213
|
"props": { "content": { "type": "code", "ref": "{{ Section.title }} · {{ Section.count }}" } } },
|
|
214
|
-
{ "elementId": "task_row", "name": "task_row", "type": "container", "props": {},
|
|
215
|
-
"children": [ { "elementId": "task_title", "name": "task_title", "type": "text",
|
|
214
|
+
{ "elementId": "task_row", "name": "task_row", "type": "container", "props": {}, "blocks": [],
|
|
215
|
+
"children": [ { "elementId": "task_title", "name": "task_title", "type": "text", "blocks": [],
|
|
216
216
|
"props": { "content": { "type": "code", "ref": "{{ item.title }}" } } } ] } ] }
|
|
217
217
|
```
|
|
218
218
|
|
|
@@ -241,10 +241,10 @@ düğümü sayfa kök kabının SONUNA koyun (akıştaki yeri boş kalır).
|
|
|
241
241
|
sayılır), `respectSafeArea` (varsayılan açık). Alt eylem çubuğu:
|
|
242
242
|
|
|
243
243
|
```json
|
|
244
|
-
{ "elementId": "action_bar", "name": "action_bar", "type": "floating-container",
|
|
244
|
+
{ "elementId": "action_bar", "name": "action_bar", "type": "floating-container", "blocks": [],
|
|
245
245
|
"props": { "anchor": { "type": "static", "data": "bottom-center" },
|
|
246
|
-
|
|
247
|
-
"children": [ { "elementId": "bar", "name": "bar", "type": "container",
|
|
246
|
+
"fullWidth": { "type": "static", "data": true } },
|
|
247
|
+
"children": [ { "elementId": "bar", "name": "bar", "type": "container", "blocks": [],
|
|
248
248
|
"props": { "direction": { "type": "static", "data": "row" } }, "children": [] } ] }
|
|
249
249
|
```
|
|
250
250
|
|
|
@@ -411,8 +411,8 @@ Motorun kendi adımları (`setter`, `condition`, `microservice`, `code`, `functi
|
|
|
411
411
|
|---|---|
|
|
412
412
|
| `redirect` | yığına iter (göreli yol) ya da dışarıda açar (tam adres) |
|
|
413
413
|
| `navigate` | eski mobil şemaların adı — `redirect`'in takma adıdır; gateway `redirect`'e çevirir |
|
|
414
|
-
| `alert` | tipli uyarı (`success`/`error`/`info`/`warning`) toast'tur ve beklemez; düz uyarı onay bekler. Eylem düğmeli bildirim ("Silindi · Geri al") `alert`le kurulmaz: sayfaya bir `toast` bileşeni koyun (`actions: [{ text, flow }]`, gerekirse `keepOpen`), akış setter'la `isOpen: true` yazar; basış eylemin kendi akışını koşar (`actionClick { text, index }`) |
|
|
415
|
-
| `confirm` | `yes` / `no`
|
|
414
|
+
| `alert` | tipli uyarı (`success`/`error`/`info`/`warning`) toast'tur ve beklemez; düz uyarı onay bekler. `message`/`description` metin şablonudur (`"{{ t('kayit.tamam') }}"`; `platform_guide('page')` §alert). Eylem düğmeli bildirim ("Silindi · Geri al") `alert`le kurulmaz: sayfaya bir `toast` bileşeni koyun (`actions: [{ text, flow }]`, gerekirse `keepOpen`), akış setter'la `isOpen: true` yazar; basış eylemin kendi akışını koşar (`actionClick { text, index }`) |
|
|
415
|
+
| `confirm` | `yes` / `no` dalları; `message`/`title`/`confirmText`/`cancelText` metin şablonudur (`{{ }}`, çeviri `t('anahtar')`) |
|
|
416
416
|
| `download` | adres cihazın açma sayfasında açılır; ikili gövde (JSON değil) önbelleğe dosya olarak yazılıp paylaşım sayfasında (`expo-sharing`) açılır, Studio mobil önizlemesi dosyayı indirir (`MOB-005`, bilgi) |
|
|
417
417
|
| `storage` | `args.name` `state.page.<key>` / `state.app.<key>` ise o state'i yazar (§Kalıcı state), değilse cihaz deposuna |
|
|
418
418
|
| `setcookie` | güvenli cihaz deposuna (SecureStore) yazar; değer sonraki her istekte `X-Flow-Cookie` başlığıyla gider ve middleware/workflow onu web'deki gibi `cookie.<ad>` olarak okur — `Cookie` başlığıyla gitmez, çünkü iOS onu yerel çerez kavanozuyla virgülle birleştirip bozuyordu (`MOB-005`, bilgi); aynı adlı sunucu çerezi varsa o kazanır. Studio paleti mobilde de sunar |
|
|
@@ -421,10 +421,11 @@ Motorun kendi adımları (`setter`, `condition`, `microservice`, `code`, `functi
|
|
|
421
421
|
| `location` | cihaz konumu (`expo-location`; ilk kullanımda izin sorulur; derleme konumu taşımıyorsa sorulmaz, `error` dalı `status: 'unavailable'`). `args.value` doğruluk, düz metin: `low` · `balanced` · `high` (boş → `balanced`); `args.isRaw: true` → adres çözülmez. Sonuç `Steps.<adım>.data = { latitude, longitude, accuracy, address }`, `address = { country, region, subregion, city, district, street, postalCode }` ya da `null` (Türkiye'de il `region`, ilçe `subregion`, yoksa `city`). İzin reddi `Steps.<adım>.error = { status: 'denied', message }`, okunamayan konum `unavailable`: adımın `error` dalı bağlıysa o koşar (`always` koşmaz), yoksa akışın hata akışı, o da yoksa `always` (`data: null`). Web'de tarayıcı konumu, adres hep `null` |
|
|
422
422
|
|
|
423
423
|
| `notification-permission` | bildirim izni ister (sorulmuşsa yeniden sormaz, kalıcı red yeniden sorulmaz). Sonuç `Steps.<adım>.data.status`: `granted` · `denied` · `undetermined`; red hata değildir, hep `always` |
|
|
424
|
-
| `notification-schedule` | yerel bildirim planlar (`expo-notifications`; uzak push değil). `args.name` kimlik
|
|
425
|
-
| `notification-cancel` | `args.name` kimlikli planlı bildirimi siler, `'*'` hepsini; hep `always` |
|
|
424
|
+
| `notification-schedule` | yerel bildirim planlar (`expo-notifications`; uzak push değil). `args.name` kimlik ve `args.title` başlık **metin şablonudur** — yalnız `{{ }}` içi çözülür (`'ilac-{{ item.id }}-0800'` kayıt başına ayrı kimlik, `"{{ t('ilac.vakti') }}"` çeviri; `{{ }}` dışındaki ifade çözülmez, FLOW-022); aynı kimlik eskisinin yerini alır. `args.message` gövde (**ifade**), `args.value` zaman (**ifade**): sonucu `'HH:mm'` → her gün, `{ weekday: 1–7, time: 'HH:mm' }` → her hafta (1 = Pazartesi), ISO tarih-saat → bir kez. `args.path` dokununca açılacak sayfanın slug'ı, düz metin — şablon çözmez (+ `args.query` ifade), boşsa uygulama açılır. İzin yoksa `error.status: 'denied'`, geçmiş ya da okunamayan zaman `invalid`: `error` dalı → hata akışı → `always`. Web'de desteklenmez (`unsupported`) — sayfa kapalıyken tarayıcı gösteremez |
|
|
425
|
+
| `notification-cancel` | `args.name` kimlikli planlı bildirimi siler (planlamadaki gibi metin şablonu: `'ilac-{{ item.id }}-0800'`), `'*'` hepsini; hep `always` |
|
|
426
426
|
| `permission` | bir cihaz iznine bakar ya da ister. `args.value` izin türü, **düz metin — ifade olamaz** (mağaza derlemesinin taşıyacağı izinler bu değerden, akış koşmadan hesaplanır; `FLOW-038`); `args.name` kip: `check` yalnız okur (sistem penceresi açılmaz), `request` verilmemiş ve hâlâ sorulabilen izni ister (boş → `request`). Sonuç `Steps.<adım>.data = { status, canAskAgain }`: `granted` · `denied` · `undetermined` · `unavailable` (derleme bu izni taşımıyor ya da web önizlemesi); `canAskAgain: false` → yalnız Ayarlar'dan değişir. `tracking` (iOS App Tracking Transparency) sonucu ayrıca `advertisingId` taşır: izin `granted` iken IDFA (iOS) / AAID (Android), değilse `null`; oynatıcı `tracking`'i hiç taşımaz (`unavailable`). Red hata değildir, hep `always`. Türler: `mobile_store_get { detail: ['permissions'] }` (`support: supported` satırlarının `key`'i) |
|
|
427
427
|
| `open-settings` | uygulamanın sistem ayar sayfasını açar; `Steps.<adım>.data.opened`. Argüman almaz |
|
|
428
|
+
| `back` | gezinme yığınında bir geri (iOS geri kaydırmasıyla aynı sonuç; kök ADR 0013). Argüman almaz (yazılan okunmaz), sonuç yazmaz, hata üretmez, hep `always`. Yığının ilk ekranında hiçbir şey yapmaz; Studio önizlemesinde runtime'ın kendi gezinmesi yoksa yine hiçbir şey, web uygulamasında tarayıcının geri'sidir. Formu kaydedip önceki ekrana dönmek için `redirect` yerine bunu kullanın: `redirect` yığına yeni ekran iter, geri kaydırması kaydedilen forma döner. 2026-10-08 öncesi runtime ve backend `back`'i tanımaz |
|
|
428
429
|
| `ad-show` | tam ekran reklam (kök ADR 0012). `args.value` mağaza ayarındaki yerleşimin **adı**, düz metin (`FLOW-039`); biçimi `interstitial` · `rewarded` · `rewardedInterstitial` olmalı. Reklam yüklenip gösterilir, kullanıcı kapatınca `Steps.<adım>.data = { shown: true, reward }` ile sürer — `reward` ödüllü reklamı sonuna kadar izleyende `{ type, amount }`, değilse `null` (ödül ayrı dal değil, `condition` ile okunur). 10 sn'de reklam yoksa, SDK hata verirse, derleme reklam taşımıyorsa ya da rıza yoksa `error.status: 'unavailable'`; tanımsız ya da banner yerleşimi `invalid`: `error` dalı → hata akışı → `always`. Oynatıcı ve web önizlemesi her zaman `unavailable` — reklamsız dalı orada sınayın. Paletten yalnız mobilde |
|
|
429
430
|
|
|
430
431
|
Bildirim adımları paletten yalnız mobil uygulamada sunulur. Tarif — S04 "her gün hatırlat": önce
|
|
@@ -455,7 +456,11 @@ ayarda tanımlı olduğu sayfa/akış yazımında denetlenemez — `mobile_store
|
|
|
455
456
|
düğmenin `click`'i → `location` (`always` → setter `il.value = Steps.konum.data.address?.region`, `error` →
|
|
456
457
|
setter hata metni). Uzak push (sunucudan gelen bildirim) ayrı bölümde: §Uzak push.
|
|
457
458
|
|
|
458
|
-
Sayfa ve layout `mount` akışları ilk çizimden sonra, `destroy` akışları sayfa kalkarken koşar.
|
|
459
|
+
Sayfa ve layout `mount` akışları ilk çizimden sonra, `destroy` akışları sayfa kalkarken koşar. Yığındaki
|
|
460
|
+
ve sekmedeki ekran bağlı kalır: geri dönüşte `mount` KOŞMAZ. Listeyi yeniden okumak için sayfanın
|
|
461
|
+
**`focus`** olayı vardır (layout'ta yok): ilk görünüşte `mount`'tan sonra ve her geri dönüşte / sekme
|
|
462
|
+
değişiminde koşar. Bağ: `page_update` `events: [{ name: 'focus', value: <flow id> }]`. Form doldurma
|
|
463
|
+
`mount`'ta, liste okuması `focus`'ta — aynı okuma ikisine bağlanırsa ilk açılışta çift istek gider.
|
|
459
464
|
`Functions.<ad>()` uygulama ve sayfa fonksiyonlarını görür (`platform_guide('functions')`).
|
|
460
465
|
|
|
461
466
|
|
|
@@ -488,7 +493,8 @@ Sunucudan gönderilen bildirim yerel `notification-schedule`'dan ayrıdır. Kana
|
|
|
488
493
|
## Kalıcı state
|
|
489
494
|
|
|
490
495
|
State varsayılan olarak **bellektedir**: sayfa her açıldığında `variable_create`'te yazılan `value`'dan
|
|
491
|
-
başlar, akışın yazdığı değer ekran kapanınca kaybolur
|
|
496
|
+
başlar, akışın yazdığı değer ekran kapanınca kaybolur — `state.app` dahil: sayfalar arası
|
|
497
|
+
taşımak da `storage` ister (38 S-5). Uygulama kapanıp açıldığında kaldığı yerden
|
|
492
498
|
sürmesi gereken değer için değişkene saklama türü verin:
|
|
493
499
|
|
|
494
500
|
| `storage` | Depo | Ne için |
|
|
@@ -526,7 +532,10 @@ variable_create({ applicationId, pageId, variables: [
|
|
|
526
532
|
780x1688 px) çeker; `'tablet'` 820x1180 @2 ya da `{ width, height, scale? }`. `viewport` verilirse
|
|
527
533
|
cihaz yok sayılır (`deviceNote`). Yanıt `applicationType: 'mobile'` ve `platformNote` taşır: kare
|
|
528
534
|
react-native-web çizimidir, cihazdakiyle yapısal olarak (düğüm, sıra, ölçü, renk) aynıdır.
|
|
529
|
-
- Eylemler aynı sözlük: `click`
|
|
535
|
+
- Eylemler aynı sözlük: `click` önce düğümün kendisine (basılabilirse: `role="button"`/`"switch"`/
|
|
536
|
+
`"combobox"`), sonra `<elementId>__press` yüzeyine, en son içteki ilk düğmeye basar — anahtar ve
|
|
537
|
+
onay kutusu kök kimlikle çevrilir, çoklu seçimi `<elementId>__trigger` açar (çipin "kaldır"ına
|
|
538
|
+
basmaz); `set` alana odaklanır, yazar ve odaktan çıkar (`changed` koşar); `submit` runtime
|
|
530
539
|
köprüsüyle gider (`submitVia: 'bridge'`); sekme öğesi `__tabs__item-<sayfa slug'ı>`, çubuk `__tabs`
|
|
531
540
|
özet satırıdır. `select` açılır listeyi açar ve seçeneği değerle ya da görünen metinle seçer
|
|
532
541
|
(liste katmanda açılır, `optionsSeen` kaç seçenek görüldüğünü söyler); `tab` bir `tabs`
|
|
@@ -282,7 +282,8 @@ dalı bulamaz ve akışı **sessizce durdurur** (`flow-engine.ts:79`); Studio da
|
|
|
282
282
|
İstisnalar: `condition` → dal adı rule id'sidir (ve o id bir **GUID**'dir — aşağıdaki tuzak 6),
|
|
283
283
|
`confirm` → `yes|no|always`, `microservice` →
|
|
284
284
|
`always` (eski akışlarda `success` de çalışır) **+ `error`** — bağlarsan hata anında `always`
|
|
285
|
-
yerine ondan sürer (2026-09-26, ADR 0004; ayrıntı aşağıdaki §"4xx de hatadır")
|
|
285
|
+
yerine ondan sürer (2026-09-26, ADR 0004; ayrıntı aşağıdaki §"4xx de hatadır"), `validation` →
|
|
286
|
+
`success` (hepsi geçti) | `error` (biri düştü), ardından her durumda `always` (kuralları aşağıda).
|
|
286
287
|
|
|
287
288
|
**2. `iteratees[].type` yalnız `change` | `fill`.** `setter.ts` başka değerde hiçbir dala
|
|
288
289
|
girmez — adım çalışır, hiçbir şey yapmaz. `change` tek prop'a değer yazar, `fill` diziyi
|
|
@@ -445,13 +446,15 @@ göstermek error flow'un işidir.
|
|
|
445
446
|
> | `Steps.<adımAdı>.error.body` | Ucun hata gövdesi — eski `Steps.<adımAdı>.data`'nın taşındığı yer | `microservice.ts` `.catch` |
|
|
446
447
|
> | `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` |
|
|
447
448
|
>
|
|
448
|
-
> **`alert` adımının `message`'ı
|
|
449
|
-
>
|
|
450
|
-
>
|
|
451
|
-
> "
|
|
452
|
-
> `
|
|
453
|
-
>
|
|
454
|
-
>
|
|
449
|
+
> **`alert` adımının `message`'ı ve `description`'ı METİN ŞABLONUDUR — yalnız `{{ }}` içi çözülür.**
|
|
450
|
+
> Bileşen prop bağlamasıyla aynı çözücü (`@ng-apps/flow` `resolveStepText` → `runPropertyCode`; web ve
|
|
451
|
+
> cihaz aynı): `{ "type": "alert", "args": { "message": "{{ Steps.error?.message ?? 'İşlem başarısız' }}",
|
|
452
|
+
> "alertType": "error" } }` ucun mesajını gösterir, `"{{ t('kayit.tamam') }}"` etkin dildeki çeviriyi.
|
|
453
|
+
> `{{ }}` DIŞINDA yazılan ifade çözülmez: `"message": "Steps.error.message"` kullanıcıya harfi harfine
|
|
454
|
+
> `Steps.error.message` gösterir (FLOW-022 uyarır). `{{ }}` içermeyen metin olduğu gibi kalır. 2026-10-08
|
|
455
|
+
> öncesi runtime'da (yeniden yayımlanmamış kurulum, eski mobil derleme) bu alanlar düz metindi; orada
|
|
456
|
+
> ucun mesajı bir `setter` (ya da `code`) adımıyla bir bileşene yazılır — mesajı ekranda kalıcı
|
|
457
|
+
> göstermek için bugün de bu yol geçerlidir:
|
|
455
458
|
>
|
|
456
459
|
> ```jsonc
|
|
457
460
|
> // errorSteps: start → bu setter. Metni bir notification/text komponentine yazar.
|
|
@@ -463,13 +466,14 @@ göstermek error flow'un işidir.
|
|
|
463
466
|
> "branches": [{ "id": "<GUID>", "name": "always", "nextStepId": null }] }
|
|
464
467
|
> ```
|
|
465
468
|
|
|
466
|
-
> **`confirm` adımının diyalog çerçevesi de yazılabilir.** `args` dört
|
|
469
|
+
> **`confirm` adımının diyalog çerçevesi de yazılabilir.** `args` dört metin alanı taşır:
|
|
467
470
|
> `message` (gövde), `title` (başlık), `confirmText` (onay düğmesi, `yes` dalı), `cancelText`
|
|
468
471
|
> (vazgeç düğmesi, `no` dalı). Son üçü isteğe bağlıdır; boş bırakılan alan dile göre sabit metne
|
|
469
472
|
> düşer ("Emin misiniz?" / "Evet" / "Hayır" — `dialog.functions.ts` `resolveDialogFrame`).
|
|
470
|
-
> Dördü de `alert`'in `message`'ı gibi
|
|
471
|
-
>
|
|
472
|
-
> "
|
|
473
|
+
> Dördü de `alert`'in `message`'ı gibi METİN ŞABLONUDUR (FLOW-022 hepsini denetler); çok dilli
|
|
474
|
+
> uygulamada çeviri anahtarıyla yazılır:
|
|
475
|
+
> `{ "type": "confirm", "args": { "message": "{{ t('risk.sil.onay') }}", "title": "{{ Row.name }} silinsin mi?",
|
|
476
|
+
> "confirmText": "{{ t('sil') }}", "cancelText": "Vazgeç" } }`.
|
|
473
477
|
|
|
474
478
|
> **Hangi yolu seçeceksin — adımın kendi `error` dalı mı, `errorSteps` mi, ana akışta
|
|
475
479
|
> `condition` mı?** Bir hatada sırayla denenir (yukarıdaki 4xx bölümü) — üçü birden TANIMLI
|
|
@@ -528,6 +532,30 @@ Tarif: `platform_guide('middleware')`.
|
|
|
528
532
|
- Adım hedef akışı **bekler** (`await engine.run`), sonra `always` dalından devam eder.
|
|
529
533
|
- Gateway boş/yarım `args`'ı **FLOW-014** ile yakalar.
|
|
530
534
|
|
|
535
|
+
**`validation` adımının kuralları `args.validations` dizisindedir** (kök ADR 0014). Her kural
|
|
536
|
+
`{ id?, component, property, type, errorMessage, settings }`: `component` sayfadaki bileşenin
|
|
537
|
+
**elementId**'si (boş → bileşensiz kural; sayfada olmayan ad 404), `property` okunacak prop
|
|
538
|
+
(çoğunlukla `value`), `type` ∈ `required · email · regex · min-length · max-length · min-value ·
|
|
539
|
+
max-value`, `errorMessage` düz metin ya da `t:` anahtarı (düşen alanın `invalidText`'ine yazılır),
|
|
540
|
+
`settings` tek parametredir — runtime **ilk değeri** okur (`{ "value": 8 }`, `{ "pattern": "^\\d+$" }`;
|
|
541
|
+
parametresiz tipte `{}`). Kurallar dizi sırasıyla koşar; kuralsız adım yükte `[]` döner ve her
|
|
542
|
+
çalıştırmada `success`'e gider.
|
|
543
|
+
|
|
544
|
+
```jsonc
|
|
545
|
+
{ "stepId": "kontrol", "name": "kontrol", "type": "validation", "kind": "normal",
|
|
546
|
+
"args": { "validations": [
|
|
547
|
+
{ "component": "email", "property": "value", "type": "email", "errorMessage": "t:validation.email", "settings": {} },
|
|
548
|
+
{ "component": "sifre", "property": "value", "type": "min-length", "errorMessage": "En az 8 karakter", "settings": { "value": 8 } } ] },
|
|
549
|
+
"branches": [ { "id": "<GUID>", "name": "success", "nextStepId": "kaydet" },
|
|
550
|
+
{ "id": "<GUID>", "name": "error", "nextStepId": null } ] }
|
|
551
|
+
```
|
|
552
|
+
|
|
553
|
+
- `always` `success`/`error`'dan **sonra** da koşar — kaydetmeyi `always`'e bağlarsan düşen
|
|
554
|
+
formda da kaydeder.
|
|
555
|
+
- Bu şekil komponentin kendi `validations`'ından (`set_validations { type, message, value }`)
|
|
556
|
+
farklıdır; `message`/`value` burada sessizce düşer. Tanınmayan `type` runtime'da **geçer** sayılır
|
|
557
|
+
(platform kümeyi doğrulamaz). Gateway ikisini de **FLOW-041** ile yakalar.
|
|
558
|
+
|
|
531
559
|
> **`code` adımının gövdesi `args.value` alanındadır — düz JS metni olarak.** Runtime onu
|
|
532
560
|
> `(async () => { <value> })()` içinde koşturur (`code.ts:18`), yani `await` serbesttir ve dönüş
|
|
533
561
|
> değeri akışa taşınmaz; etkiyi `Page`/`state`/komponent yazarak üretirsin. **Alan boşsa adım
|
|
@@ -618,7 +646,7 @@ değer tipleri: `static · page · property · storage · variable · code · it
|
|
|
618
646
|
> **`platform_guide('functions')`**.
|
|
619
647
|
>
|
|
620
648
|
> **`share` adımı ("Share")** metin ve bağlantı paylaşır: `args: { title, message, path }`. `title`
|
|
621
|
-
> DÜZ METİNDİR (ifade yazılırsa FLOW-022 uyarır); `message` ve `path` `storage.value` gibi
|
|
649
|
+
> DÜZ METİNDİR — `{{ }}` şablonu da çözülmez (ifade ya da `{{ }}` yazılırsa FLOW-022 uyarır); `message` ve `path` `storage.value` gibi
|
|
622
650
|
> İFADEDİR — düz metin tırnak ister (`"'Şuna bak'"`, `"'https://…/urun/' + Page.queryParams.id"`).
|
|
623
651
|
> Bağlantının adı `url` DEĞİL `path`'tir: backend `Title`/`Message`/`Path` alanlarında saklar ve
|
|
624
652
|
> tanımadığı anahtarı 200 ile sessizce düşürür (FLOW-035 `url`/`link`/`text`'i ve tırnaksız adresi
|
|
@@ -937,13 +965,16 @@ kapsayıcının kardeşi olarak yaşar (qm `[SY] Profil bilgilerim`: `page_conta
|
|
|
937
965
|
> Bu tiplerde `size.h` gerçek yüksekliktir — göz kararı değil, içeriğe göre hesapla
|
|
938
966
|
> (bir satır ≈ 10px; Kural 1f).
|
|
939
967
|
|
|
940
|
-
## Kural 1d-2 — Sayfanın ve layout'un KENDİ olayları: `mount` · `destroy`
|
|
968
|
+
## Kural 1d-2 — Sayfanın ve layout'un KENDİ olayları: `mount` · `destroy` (mobil sayfada + `focus`)
|
|
941
969
|
|
|
942
970
|
Komponent olaylarından AYRI olarak sayfa ve layout kayıtları kendi olay listesini taşır
|
|
943
971
|
(`events: [{ name, value: <flow id> }]`) ve geçerli adlar SABİTTİR: **`mount`**
|
|
944
972
|
(yüklendiğinde) ve **`destroy`** (sayfadan/layouttan ayrılırken — runtime destroy akışlarını
|
|
945
|
-
navigasyondan ÖNCE koşar).
|
|
946
|
-
|
|
973
|
+
navigasyondan ÖNCE koşar). **Mobil uygulamanın SAYFASI** bir ad daha alır: **`focus`** — ekran
|
|
974
|
+
her göründüğünde koşar (ilk açılışta `mount`'tan sonra, sonra yığından geri dönüşte ve sekme
|
|
975
|
+
değişiminde; `platform_guide('mobile')`). Başka ad platformca kabul edilir ama bağ sessizce ölü
|
|
976
|
+
kalır — gateway **PAGE-011 / LAY-004** ile istek gitmeden reddeder; `focus` layout'ta ve web
|
|
977
|
+
sayfasında da reddedilir (web sayfayı her gezinmede baştan kurar, `mount` yeter).
|
|
947
978
|
|
|
948
979
|
- Bağlama yolu `set_event` DEĞİLDİR (o komponent hedefler): sayfada `page_create` /
|
|
949
980
|
`page_update`'in `events` parametresi, layout'ta `layout_create/update`'in `events`'i.
|
|
@@ -55,7 +55,8 @@ diye bir alan **hiç görünmez**; blok tam olarak bunu görünür kılmak için
|
|
|
55
55
|
| Tool | Ne yapar |
|
|
56
56
|
|---|---|
|
|
57
57
|
| `translation_list` | Application'ın tüm `(language, key, value)` kayıtlarını düz liste döner |
|
|
58
|
-
| `translation_update` | `keys` (ekle/güncelle, `componentId` opsiyonel) ve/veya `deletedKeys` (sil) — ikisi de aynı çağrıda verilebilir, en az biri dolu
|
|
58
|
+
| `translation_update` | `keys` (ekle/güncelle, `componentId` opsiyonel) ve/veya `deletedKeys` (sil) — ikisi de aynı çağrıda verilebilir, en az biri dolu olmalı. `file` kipi: doldurulmuş Excel'i içe aktarır (aşağıda) |
|
|
59
|
+
| `translation_export` | Çevirmene verilecek Excel dosyasını yerel yola yazar (aşağıda) |
|
|
59
60
|
| `project_update` | `languages`/`defaultLanguage` — proje dillerini kurar/değiştirir |
|
|
60
61
|
|
|
61
62
|
```jsonc
|
|
@@ -194,6 +195,45 @@ Runtime `t:` ile başlayan bir mesajı anahtar sayar ve normal zincirden çözer
|
|
|
194
195
|
olduğu gibi gösterilir. Çözüm çalışma anında olduğu için şema varsayılanlarını değiştirmek
|
|
195
196
|
gerekmez — `schema.ts`'e dokunulmaz, katalog yeniden yüklenmez.
|
|
196
197
|
|
|
198
|
+
## Dosyayla toplu çeviri (Excel)
|
|
199
|
+
|
|
200
|
+
İnsan bir çevirmene yeni bir dil yaptıracaksa: sözlük Excel'e iner, çevirmen hedef sütunu doldurur,
|
|
201
|
+
dosya geri yüklenir. **Çeviriyi sen yapacaksan dosyaya gerek yok** — `translation_list` ile oku,
|
|
202
|
+
`translation_update { keys }` ile yaz.
|
|
203
|
+
|
|
204
|
+
```jsonc
|
|
205
|
+
// 1. dışa aktar — dosya YALNIZ outPath'e yazılır, Sürücü'ye yüklenmez
|
|
206
|
+
{ "applicationId": "<app id>", "targetLanguage": "es", "outPath": "C:/ceviri/crm-es.xlsx" } // translation_export
|
|
207
|
+
// → { keys: 120, targetMissing: 120, sourceLanguage: "en", note: "… TR-004 …" }
|
|
208
|
+
|
|
209
|
+
// 2. çevirmen C sütununu doldurur; 3. önce kuru koşu
|
|
210
|
+
{ "applicationId": "<app id>", "file": "C:/ceviri/crm-es.xlsx", "targetLanguage": "es", "dryRun": true } // translation_update
|
|
211
|
+
// → { created: 118, skippedEmpty: 2, unknownKeyCount: 0, targetLanguageInProject: false, note: "… ekleyecek …" }
|
|
212
|
+
|
|
213
|
+
// 4. gerçek içe aktarma
|
|
214
|
+
{ "applicationId": "<app id>", "file": "C:/ceviri/crm-es.xlsx", "targetLanguage": "es" } // translation_update
|
|
215
|
+
// → { created: 118, languageAdded: true, … }
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
Dosya: tek sayfa, `A` Key · `B` kaynak dil (yalnız referans, okunmaz) · `C` hedef dil; başlıklar
|
|
219
|
+
`Key` · `Default Language (EN)` · `Target Language (ES)`. İçe aktarma **başlık satırını okumaz**:
|
|
220
|
+
değerler `translation_update`'in zorunlu `targetLanguage`'ındaki dile yazılır, `C1`'deki `(ES)` yalnız
|
|
221
|
+
çevirmen içindir ve dili belirlemez. Gizli `_zeyrek`
|
|
222
|
+
sayfası dosyanın hangi application'dan çıktığını söyler; başka bir application'ın dosyası yine
|
|
223
|
+
yazılır ama yanıt `applicationMismatch: true` ve not taşır.
|
|
224
|
+
|
|
225
|
+
- **Dolu hücre mevcut değeri ezer**, boş/boşluk hücre atlanır, hiçbir şey silinmez.
|
|
226
|
+
- **Application'da olmayan key açılmaz** — `unknownKeys`'e düşer. Yeni key `keys` kipiyle açılır.
|
|
227
|
+
- Key eşleşmesi önce birebir, yoksa büyük/küçük harf duyarsız tek aday; aynı key dosyada iki satırda
|
|
228
|
+
ise 422 ve hiçbir şey yazılmaz.
|
|
229
|
+
- **Hedef dil projede yoksa içe aktarma onu projeye ekler — bütün ortamlara** (TR-004). Önce
|
|
230
|
+
`project_update` gerekmez; ama dil production'a da gider ve orada çeviriler deployment'la gelene
|
|
231
|
+
kadar runtime varsayılan dile düşer.
|
|
232
|
+
- Dosya platformda **saklanmaz**: ne dışa aktarma ne içe aktarma Sürücü'ye bir şey bırakır.
|
|
233
|
+
- Studio'nun çeviri panelindeki JSON içe aktarma bundan farklıdır: o yalnız boşları doldurur ve
|
|
234
|
+
projede olmayan dili atlar.
|
|
235
|
+
- Sınırlar: `.xlsx` (`.xls`, csv, json reddedilir), 10 MB, 10.000 satır.
|
|
236
|
+
|
|
197
237
|
## Bilinen sınırlar
|
|
198
238
|
|
|
199
239
|
- **Flow adımlarında Translation kaynağı YOK.** `value.type` sözlüğünde (`platform_guide('page')`
|
|
@@ -286,7 +286,10 @@ Global-error dalında `error` değişkeni tanımlıdır:
|
|
|
286
286
|
```
|
|
287
287
|
|
|
288
288
|
Fail dalında aynı zincir `<adım adı>.error.cause`'tadır (`<adım adı>.error` = `{ code, message, detail,
|
|
289
|
-
type, cause }`).
|
|
289
|
+
type, cause }`). Serbest `error` değişkenini YALNIZ global-error dalı kurar
|
|
290
|
+
(`ActivityContext.EnterGlobalErrorHandling`): bağlı `fail` dalının hedefi `error.message` okursa
|
|
291
|
+
`undefined` alır — orada `<adım adı>.error.message` / `.code` okuyun (WF-035 uyarır; hedef aynı zamanda
|
|
292
|
+
global-error dalının hedefi olsa da).
|
|
290
293
|
|
|
291
294
|
`error.step` hangi adımın patladığını söyler. `input`/`data` alanları ifade değerlerini ve adım
|
|
292
295
|
çıktısını taşır — gövdeye yalnız ihtiyaç duyduğun alt kümeyi yaz (gateway'in varsayılanı
|
|
@@ -575,6 +578,33 @@ Tam örnek, ölçülen çıktı şekli ve `code` çocuğu tuzağının çıkış
|
|
|
575
578
|
> `The JSON value could not be converted to System.Guid` ile reddetti. Geçerli önek harfleri
|
|
576
579
|
> yalnız `a`–`f`'tir.
|
|
577
580
|
|
|
581
|
+
## `file-upload` adımı: çıktı, dosya alanının tablo okuma biçimidir (K10, 2026-10-08)
|
|
582
|
+
|
|
583
|
+
Adım `rule.fileString`'i (base64, JS ifadesi) `rule.folderId` klasörüne `rule.fileName` (JS ifadesi;
|
|
584
|
+
boşsa GUID) adıyla yazar, dosyayı geri okur ve **tablo okumasının FILE alanında döndürdüğü nesneyi**
|
|
585
|
+
üretir (`FileUploadStep.OutputSchema`; iki yol aynı `FilePathMapper`'ı kullanır):
|
|
586
|
+
|
|
587
|
+
```json
|
|
588
|
+
{
|
|
589
|
+
"id": "0199c3a2-7b1e-7c4d-9a10-3f2b8c6d5e41",
|
|
590
|
+
"path": "uploads/photo.png",
|
|
591
|
+
"absolute_path": "https://acme.example.com/dev/uploads/photo.png",
|
|
592
|
+
"type": "ref",
|
|
593
|
+
"value": "0199c3a2-7b1e-7c4d-9a10-3f2b8c6d5e41",
|
|
594
|
+
"fileName": "photo.png"
|
|
595
|
+
}
|
|
596
|
+
```
|
|
597
|
+
|
|
598
|
+
- Sonraki adımlar **adım adı + `.data`** ile okur: adres `yukle.data.absolute_path`, kimlik
|
|
599
|
+
`yukle.data.id` (`value` aynı id'dir). `absolute_path` = uygulamanın dosya sunum ucu (org slug +
|
|
600
|
+
ortamın depolama öneki) + `/` + `path`; `path` boşsa `""`. Görsel için ikinci okuma gerekmez.
|
|
601
|
+
- **Aynı içerik klasörde varsa** yeni dosya yazılmaz: `id`, adres ve `fileName` var olan dosyanındır.
|
|
602
|
+
- **Mobil görsel:** `response` adımı nesneyi döndürür (`bodyExpression: "({ foto: yukle.data })"`),
|
|
603
|
+
sayfa akışı `Steps.<sayfa adımı>.data.foto`'yu `image.src`'ye verir — nesnenin kendisi ya da
|
|
604
|
+
`.absolute_path`; `.value` dosya kimliğidir, görsel çizilmez (`platform_guide('mobile')` §Dosya ve fotoğraf).
|
|
605
|
+
- 2026-10-08'den eski backend imajı yalnız `{ id }` döndürür: `yukle.data.absolute_path` `undefined`
|
|
606
|
+
çıkıyorsa imaj bayattır. Kaynak: backend `FileUploadStepTests`; canlı koşuyla henüz ölçülmedi.
|
|
607
|
+
|
|
578
608
|
## Table step kuralları
|
|
579
609
|
|
|
580
610
|
- `rule` içinde `datasourceId` + `entityId` zorunlu; `selectFields`/`filters` öğelerinde
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
|
-
"hash": "
|
|
3
|
-
"generatedAt": "2026-10-
|
|
2
|
+
"hash": "1766032527c4215282c73fb003cbe3526babafabc8588b80289dc28fd4e8b393",
|
|
3
|
+
"generatedAt": "2026-10-12T12:46:03.355Z",
|
|
4
4
|
"types": 337,
|
|
5
5
|
"files": 167,
|
|
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/tools/entities.js
CHANGED
|
@@ -26,6 +26,40 @@ function preserveApplicationBinding(definition) {
|
|
|
26
26
|
if (applicationIds.length > 0)
|
|
27
27
|
definition.applicationIds = applicationIds;
|
|
28
28
|
}
|
|
29
|
+
/**
|
|
30
|
+
* 37 S-1: platform PATCH op'larını `EntityDto` OKUMA şekline uygular (`PatchEntityCommandHandler` →
|
|
31
|
+
* `UpdateEntityCommand.FromDto`, `ApplicationIds = Applications.Select(a => a.ApplicationId)`). Bağ
|
|
32
|
+
* `/applications` yolunda `{ applicationId }` öğeleriyle yazılır; `/applicationIds` belgede yoktur
|
|
33
|
+
* (gateway önizlemesi `OPERATION_PATH_UNRESOLVABLE` ile düşer), çıplak guid 500 `JsonException`,
|
|
34
|
+
* `{ id }` boş `applicationId` ile "This record is in use" verir. Ajan yazma adını bilerek geldiği
|
|
35
|
+
* için reddetmek yerine çevrilir ve `gatewayNormalized` ile söylenir (microservice `/roleIds` emsali).
|
|
36
|
+
*/
|
|
37
|
+
function normalizeApplicationBindingOps(operations) {
|
|
38
|
+
const notes = [];
|
|
39
|
+
const asBinding = (entry) => {
|
|
40
|
+
if (typeof entry === 'string')
|
|
41
|
+
return { applicationId: entry };
|
|
42
|
+
if (entry && typeof entry === 'object' && !('applicationId' in entry) && typeof entry.id === 'string')
|
|
43
|
+
return { applicationId: entry.id };
|
|
44
|
+
return entry;
|
|
45
|
+
};
|
|
46
|
+
for (const operation of operations) {
|
|
47
|
+
const path = typeof operation.path === 'string' ? operation.path : '';
|
|
48
|
+
if (path === '/applicationIds' || path.startsWith('/applicationIds/')) {
|
|
49
|
+
operation.path = `/applications${path.slice('/applicationIds'.length)}`;
|
|
50
|
+
notes.push(`patch yolu '${path}' → '${operation.path}': yamalanan belge entity_get'in OKUMA şeklidir (EntityDto) ve uygulama bağı orada 'applications' [{ applicationId }] alanıdır; 'applicationIds' yalnız create/update GÖVDESİNDE vardır.`);
|
|
51
|
+
}
|
|
52
|
+
const target = operation.path;
|
|
53
|
+
if (target !== '/applications' && !target.startsWith('/applications/'))
|
|
54
|
+
continue;
|
|
55
|
+
const before = JSON.stringify(operation.value);
|
|
56
|
+
operation.value = target === '/applications' && Array.isArray(operation.value) ? operation.value.map(asBinding) : target.split('/').length === 3 ? asBinding(operation.value) : operation.value;
|
|
57
|
+
if (JSON.stringify(operation.value) !== before) {
|
|
58
|
+
notes.push(`'${target}' değeri { applicationId } öğesine çevrildi — çalışan şekil: { op: 'replace', path: '/applications', value: [{ applicationId: '<application id>' }] }.`);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return notes;
|
|
62
|
+
}
|
|
29
63
|
/**
|
|
30
64
|
* `UpdateEntityCommandHandler` gövdedeki `Indexes` null ise onu BOŞ LİSTEYE çeviriyor
|
|
31
65
|
* (`request.Indexes?…ToList() ?? []`) ve `Entity.FindProcessedIndexes` boş listede entity'nin
|
|
@@ -178,7 +212,7 @@ export function registerEntityTools(server, platform) {
|
|
|
178
212
|
}));
|
|
179
213
|
server.registerTool('entity_patch', {
|
|
180
214
|
title: 'Entity\'ye patch uygula',
|
|
181
|
-
description: "YENİ alanı korumayla ya da türetilmiş alan olarak eklemek için de kullanılır (PROT-003/PROT-004); eklenen ya da kuralı değişen türetilmiş alanı dolu satırlarda derived-fill işi doldurur (tablo kilitlenmez, bitiş import_history { entityId }). VAR OLAN alanın korumasını değiştirmez — platform 400 ProtectionChangeNotAllowed döner, gateway istek göndermeden durdurur ve yol entity_protection_update'tir. Tam gövdeyi bağlama almadan hedefli değişiklik: gateway mevcut tanımı okur, yolları çözer ve op'ları platformun PATCH /entities/{id} ucuna `{ baseVersion, ops }` olarak geçirir — tam gövde PUT'u ATILMAZ, yalnız fark yazılır. Yanıt outline özetidir. YOLLARDA SEÇİCİ KULLANIN, indeks değil: '/fields[slug=email]/nullable' — indeksle yazmak alan sırası değişince SESSİZCE yanlış alanı yazar. Gateway seçiciyi indekse çevirir ve hangi kayda indiğini yanıttaki patchTargets ile söyler. `baseVersion` okunan kaydın `version` alanından gelir; kayıt bu arada değiştiyse platform 409 döner ve HİÇBİR op uygulanmaz. 'applications' → 'applicationIds' çevirisini
|
|
215
|
+
description: "YENİ alanı korumayla ya da türetilmiş alan olarak eklemek için de kullanılır (PROT-003/PROT-004); eklenen ya da kuralı değişen türetilmiş alanı dolu satırlarda derived-fill işi doldurur (tablo kilitlenmez, bitiş import_history { entityId }). VAR OLAN alanın korumasını değiştirmez — platform 400 ProtectionChangeNotAllowed döner, gateway istek göndermeden durdurur ve yol entity_protection_update'tir. Tam gövdeyi bağlama almadan hedefli değişiklik: gateway mevcut tanımı okur, yolları çözer ve op'ları platformun PATCH /entities/{id} ucuna `{ baseVersion, ops }` olarak geçirir — tam gövde PUT'u ATILMAZ, yalnız fark yazılır. Yanıt outline özetidir. YOLLARDA SEÇİCİ KULLANIN, indeks değil: '/fields[slug=email]/nullable' — indeksle yazmak alan sırası değişince SESSİZCE yanlış alanı yazar. Gateway seçiciyi indekse çevirir ve hangi kayda indiğini yanıttaki patchTargets ile söyler. `baseVersion` okunan kaydın `version` alanından gelir; kayıt bu arada değiştiyse platform 409 döner ve HİÇBİR op uygulanmaz. UYGULAMA BAĞI '/applications' yolundadır ve öğeleri { applicationId } nesneleridir: {op:'replace', path:'/applications', value:[{applicationId:'<application id>'}]} — tam listeyi yazar; tek bağ eklemek {op:'add', path:'/applications/-', value:{applicationId:'<application id>'}}. '/applicationIds' yolunu ya da çıplak guid / { id } öğesini gateway bu şekle çevirir ve gatewayNormalized ile söyler; 'applications' → 'applicationIds' gövde çevirisini platform yapar. `fields[].isLocalized: true` alanı ana tablodan çıkarıp companion `<slug>_localization` entity'sine taşır (yalnız düz METİN alanı; anahtar/unique/ilişki olamaz) — platform_guide('entity') §'Çoklu dilli alan'.",
|
|
182
216
|
inputSchema: { project: projectParam, environment: environmentParam, datasourceId: guid.optional().describe('Verilmezse gateway datasource ağacından bulur'), id: entityRef, operations: patchOperationsParam.optional(), ops: patchOperationsParam.optional().describe("'operations' ile aynı alan (eşanlamlı) — sayfa micro-op tool'larıyla aynı adı kullanabilmeniz için"), policyOverride: policyOverrideParam },
|
|
183
217
|
annotations: updates
|
|
184
218
|
}, handle(config, async ({ project, environment, datasourceId, id, operations: givenOperations, ops, policyOverride }) => {
|
|
@@ -187,7 +221,10 @@ export function registerEntityTools(server, platform) {
|
|
|
187
221
|
const entity = datasourceId && isGuid(id) ? { entityId: id, datasourceId } : await resolver.entity(id);
|
|
188
222
|
const current = await platform.tenant('GET', project, `/entities/${entity.datasourceId}/${entity.entityId}`, { environment });
|
|
189
223
|
const definition = structuredClone((current?.data ?? current));
|
|
190
|
-
|
|
224
|
+
// Bağ yolu seçici çözümünden ÖNCE çevrilir: '/applicationIds' belgede yoktur (37 S-1).
|
|
225
|
+
const bindingOperations = structuredClone(operations);
|
|
226
|
+
const bindingNotes = normalizeApplicationBindingOps(bindingOperations);
|
|
227
|
+
const resolvedOps = resolvePatchOperations(definition, bindingOperations, 'entity_get');
|
|
191
228
|
// Politika, yazmadan önce sonucu görmeli; yerel kopya YALNIZ değerlendirme için, gövde olarak gönderilmez.
|
|
192
229
|
const preview = fastJsonPatch.applyPatch(definition, resolvedOps.operations, true, false).newDocument;
|
|
193
230
|
const protectionChanges = changedExistingProtections(current?.data ?? current, preview);
|
|
@@ -208,7 +245,7 @@ export function registerEntityTools(server, platform) {
|
|
|
208
245
|
});
|
|
209
246
|
const written = patchResult(saved, preview);
|
|
210
247
|
const touched = patchedSections(written.data, resolvedOps.operations);
|
|
211
|
-
return attachFindings(withUnmappedMembers(withResolved({
|
|
248
|
+
return attachFindings(withUnmappedMembers(withResolved(withNormalization({
|
|
212
249
|
applied: operations.length,
|
|
213
250
|
newVersion: written.newVersion,
|
|
214
251
|
patchTargets: resolvedOps.targets,
|
|
@@ -216,7 +253,7 @@ export function registerEntityTools(server, platform) {
|
|
|
216
253
|
// S-8: yazılanın kanıtı yanıtta dursun; dokunulmayan bölümler outline'da kalır.
|
|
217
254
|
...(Object.keys(touched).length > 0 ? { touched } : {}),
|
|
218
255
|
outline: entityOutline(written.data)
|
|
219
|
-
}, resolver.resolved), drift), findings);
|
|
256
|
+
}, bindingNotes), resolver.resolved), drift), findings);
|
|
220
257
|
}));
|
|
221
258
|
server.registerTool('entity_protection_update', {
|
|
222
259
|
title: 'Alan korumasını değiştir',
|
package/dist/tools/layouts.js
CHANGED
|
@@ -3,7 +3,7 @@ import { resolvePatchOperations } from '../knowledge/outlines.js';
|
|
|
3
3
|
import { patchResult, baseVersionOf } from '../platform/patch.js';
|
|
4
4
|
import fastJsonPatch from 'fast-json-patch';
|
|
5
5
|
import { fillComponentNames, componentNameNote, normalizeComponentDefaults, componentDefaultsNote, flexSizeWarnings, spacingLayerWarnings, verticalGapWarnings, withNormalization } from '../knowledge/normalize.js';
|
|
6
|
-
import { fetchHost, layoutUpdateBody, schemaParam, eventsParam, modeParam, summarize, findNode } from './screens.js';
|
|
6
|
+
import { fetchHost, layoutUpdateBody, schemaParam, eventsParam, modeParam, summarize, findNode, readFullSchema } from './screens.js';
|
|
7
7
|
import { assertScreenWriteGuards, assertScreenCatalog } from './screenWrite.js';
|
|
8
8
|
import { applyPropTypesToSchema, isSetPropTypeOp, setPropTypeErrors } from '../knowledge/propTypes.js';
|
|
9
9
|
import { writeDiff, withWriteDiff } from '../platform/writeDiff.js';
|
|
@@ -58,24 +58,39 @@ export function registerLayoutTools(server, platform) {
|
|
|
58
58
|
}));
|
|
59
59
|
server.registerTool('layout_schema_get', {
|
|
60
60
|
title: "Layout komponent ağacını oku",
|
|
61
|
-
description: "Layout'un komponent ağacını (views) okur — page_schema_get'in layout karşılığı. Ağaçta hedefli değişiklik için layout_schema_patch (sayfa ile aynı micro-op sözlüğü, PATCH /layouts/{app}/{id}/schema). Ad/parametre/olay alanlarında ya da düz JSON Patch yolu gerektiğinde layout_patch — o araç buradaki updatedAt'i expectedUpdatedAt olarak kabul eder. Tam ağacı kendiniz yazacaksanız layout_update.",
|
|
61
|
+
description: "Layout'un komponent ağacını (views) okur — page_schema_get'in layout karşılığı. Ağaçta hedefli değişiklik için layout_schema_patch (sayfa ile aynı micro-op sözlüğü, PATCH /layouts/{app}/{id}/schema). Ad/parametre/olay alanlarında ya da düz JSON Patch yolu gerektiğinde layout_patch — o araç buradaki updatedAt'i expectedUpdatedAt olarak kabul eder. Tam ağacı kendiniz yazacaksanız layout_update. full ve subtree yanıtı nodeCount taşır; full tek yanıta sığmazsa HATA döner (sessiz kırpma yok): cursor:'0' ile parça parça okuyun — page_schema_get ile aynı sözleşme (items[] { parentElementId, index, node }, nextCursor bitene kadar; layout okuma arasında değişirse 409). allowTruncated:true kırpılmış gövdeyi açıkça verir — o gövdeyi layout_update'e geri yazmayın.",
|
|
62
62
|
inputSchema: {
|
|
63
63
|
project: projectParam,
|
|
64
64
|
environment: environmentParam,
|
|
65
65
|
applicationId: guid,
|
|
66
66
|
id: guid,
|
|
67
|
-
mode: modeParam,
|
|
68
|
-
componentId: z.string().optional().describe("subtree kipinde kök alınacak komponentin elementId'si")
|
|
67
|
+
mode: modeParam.describe("summary (varsayılan): yalnız id/elementId/name/type ağacı — hedef bulmak için bunu kullanın. subtree: componentId ile tek alt ağaç. full: tüm views; tek yanıta sığmazsa HATA döner — cursor:'0' ile parça parça okuyun (nextCursor bitene kadar) ya da allowTruncated:true ile kırpılmış gövdeyi açıkça isteyin."),
|
|
68
|
+
componentId: z.string().optional().describe("subtree kipinde kök alınacak komponentin elementId'si"),
|
|
69
|
+
cursor: z.string().min(1).optional().describe("Yalnız mode:'full': parçalı okuma. İlk çağrıda '0', sonra yanıttaki nextCursor; nextCursor gelmeyince views bitmiştir. Layout okuma arasında değişirse (updatedAt) 409 döner — cursor:'0' ile baştan başlayın."),
|
|
70
|
+
allowTruncated: z.boolean().optional().describe("Yalnız mode:'full': true verilirse sığmayan views hata yerine kırpılarak döner (nodeCount toplamı söyler, düşen düğümler söylenmez). Varsayılan false.")
|
|
69
71
|
},
|
|
70
72
|
annotations: readOnly
|
|
71
|
-
}, handle(config, async ({ project, environment, applicationId, id, mode, componentId }) => {
|
|
73
|
+
}, handle(config, async ({ project, environment, applicationId, id, mode, componentId, cursor, allowTruncated }) => {
|
|
72
74
|
const layout = await fetchHost(platform, 'layout', project, environment, applicationId, id);
|
|
73
75
|
const typeField = applicationTypeField(await resolveProfile(platform, applicationId));
|
|
74
76
|
const audit = layout.audit;
|
|
75
77
|
const updatedAt = audit?.updatedAt ?? audit?.createdAt;
|
|
76
78
|
const views = (layout.views ?? []);
|
|
77
|
-
if (mode
|
|
78
|
-
|
|
79
|
+
if (cursor !== undefined && mode !== 'full') {
|
|
80
|
+
throw new PlatformApiError(`cursor yalnız mode:'full' ile çalışır (verilen mode: '${mode ?? 'summary'}'); parçalı okuma tüm views içindir. mode:'full' ile cursor:'0' verin ya da cursor'ı kaldırın.`, 400);
|
|
81
|
+
}
|
|
82
|
+
if (mode === 'full') {
|
|
83
|
+
return readFullSchema({
|
|
84
|
+
noun: 'Layout',
|
|
85
|
+
field: 'views',
|
|
86
|
+
nodes: views,
|
|
87
|
+
versionKey: String(updatedAt ?? ''),
|
|
88
|
+
base: { updatedAt, ...typeField },
|
|
89
|
+
cursor,
|
|
90
|
+
allowTruncated,
|
|
91
|
+
maxResponseBytes: config.maxResponseBytes
|
|
92
|
+
});
|
|
93
|
+
}
|
|
79
94
|
if (mode === 'subtree') {
|
|
80
95
|
if (!componentId)
|
|
81
96
|
throw new PlatformApiError("mode='subtree' için componentId zorunludur.");
|
package/dist/tools/mobile.js
CHANGED
|
@@ -49,6 +49,7 @@ export function buildMobileAppPlan(input) {
|
|
|
49
49
|
];
|
|
50
50
|
}
|
|
51
51
|
/** Kabuk ağacı örnek dosyasından okunur — örnek ile aracın kurduğu layout tek kaynaktır. */
|
|
52
|
+
const drawerButtonId = (pageSlug) => `menu_${pageSlug.replace(/[^A-Za-z0-9]+/g, '_')}`;
|
|
52
53
|
function shellViews(input) {
|
|
53
54
|
const example = JSON.parse(readKnowledgeFile('examples', input.drawer ? 'mobile-app-shell-drawer.json' : 'mobile-app-shell-stack.json'));
|
|
54
55
|
const views = structuredClone(example.layoutCreate.views);
|
|
@@ -57,6 +58,19 @@ function shellViews(input) {
|
|
|
57
58
|
const appBar = findAppBar(views);
|
|
58
59
|
if (appBar && input.title)
|
|
59
60
|
appBar.props.title = { type: 'static', ref: null, data: input.title };
|
|
61
|
+
// 37 S-9 / g-24: örneğin düğmeleri ŞABLONDUR; çekmece uygulamanın kendi sayfalarından birer düğme taşır.
|
|
62
|
+
const drawer = views.find(node => node.type === 'drawer');
|
|
63
|
+
const template = drawer?.children?.find(node => node.type === 'button');
|
|
64
|
+
if (drawer && template) {
|
|
65
|
+
drawer.children = input.pages.map(page => {
|
|
66
|
+
const elementId = drawerButtonId(page.pageSlug);
|
|
67
|
+
const button = structuredClone(template);
|
|
68
|
+
button.elementId = elementId;
|
|
69
|
+
button.name = elementId;
|
|
70
|
+
button.props = { ...button.props, text: { type: 'static', ref: null, data: page.pageName } };
|
|
71
|
+
return button;
|
|
72
|
+
});
|
|
73
|
+
}
|
|
60
74
|
return views;
|
|
61
75
|
}
|
|
62
76
|
const SHELL_TYPES = ['app-bar', 'slot'];
|
|
@@ -274,7 +288,7 @@ export function registerMobileTools(server, platform) {
|
|
|
274
288
|
...(tabs ? { tabs: tabs.map(tab => tab.pageSlug) } : {}),
|
|
275
289
|
completed,
|
|
276
290
|
...(warnings.length > 0 ? { warnings } : {}),
|
|
277
|
-
next:
|
|
291
|
+
next: `Ekranları page_schema_patch ile doldurun; doğrulama page_screenshot (mobilde varsayılan device: 'phone'). ${input.drawer ? `Çekmece düğmeleri sayfalardan türedi (${pages.map(page => drawerButtonId(page.pageSlug)).join(', ')}): her birine flow_create (host: 'layout', redirect) + layout_schema_patch set_event { event: 'click' } bağlayın. ` : ''}platform_guide('mobile').`
|
|
278
292
|
};
|
|
279
293
|
}));
|
|
280
294
|
}
|