@appaflytech/zeyrek-ai-mcp 0.0.62 → 0.0.64
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 +6 -0
- package/dist/config.js +2 -0
- package/dist/knowledge/backendConstraints.js +2 -2
- package/dist/knowledge/componentCatalog.js +1 -1
- package/dist/knowledge/mobileStore.js +24 -4
- package/dist/knowledge/normalize.js +10 -0
- package/dist/knowledge/platform/mobileLayoutLint.js +8 -0
- package/dist/knowledge/platform/mobileRules.js +6 -0
- package/dist/knowledge/rules.js +89 -0
- package/dist/observability/gapSink.js +43 -0
- package/dist/platform/argumentAliases.js +6 -0
- package/dist/platform/capture/actions.js +71 -2
- package/dist/platform/capture/capture.js +5 -2
- package/dist/platform/responses.js +5 -2
- package/dist/platform/writeDiff.js +52 -1
- package/dist/resources/examples/middleware-password-reset.json +1281 -0
- package/dist/resources/examples/mobile-reorder-persist.json +33 -7
- package/dist/resources/examples/workflow-email-step.json +67 -0
- package/dist/resources/guides/deployment.md +13 -1
- package/dist/resources/guides/entity.md +7 -0
- package/dist/resources/guides/expressions.md +5 -0
- package/dist/resources/guides/getting-started.md +15 -30
- package/dist/resources/guides/microservice.md +30 -2
- package/dist/resources/guides/middleware.md +83 -3
- package/dist/resources/guides/mobile-layout.md +35 -3
- package/dist/resources/guides/mobile-publish.md +33 -1
- package/dist/resources/guides/mobile-steps.md +12 -2
- package/dist/resources/guides/mobile.md +11 -8
- package/dist/resources/guides/page.md +28 -3
- package/dist/resources/guides/translation.md +6 -0
- package/dist/resources/guides/workflow.md +30 -1
- package/dist/resources/schema-fingerprint.json +2 -2
- package/dist/tools/applications.js +7 -4
- package/dist/tools/common.js +8 -0
- package/dist/tools/designSchemaPatch.js +4 -1
- package/dist/tools/flows.js +14 -4
- package/dist/tools/layouts.js +3 -1
- package/dist/tools/microservices.js +8 -2
- package/dist/tools/mobileStore.js +13 -2
- package/dist/tools/pages.js +27 -12
- package/dist/tools/platformInfo.js +15 -6
- package/dist/tools/workflows.js +4 -2
- package/package.json +1 -1
|
@@ -191,8 +191,8 @@ Workflow'un `file-upload` adımı da aynı nesneyi döndürür, adres `<adım>.d
|
|
|
191
191
|
|
|
192
192
|
Mobil `repeater`'ın çocukları TAM İKİ `view`'dır (web'de tek şablon) — satır şablonu ve boş görünüm (ADR
|
|
193
193
|
docs/adr/0005; başka şekil 422 `fixed-views`). Satır yokken boş görünüm bir kez, tam genişlikte
|
|
194
|
-
çizilir; boş ekranı oraya tasarla. `
|
|
195
|
-
`
|
|
194
|
+
çizilir; boş ekranı oraya tasarla. Mobil `view` çocuklarını sütunda dizer.
|
|
195
|
+
`columnCount` > 1'de `horizontalSpacing` hücre payıdır, satır taşmaz. Tam gövde:
|
|
196
196
|
`ngapps://examples/mobile-list-detail-form`.
|
|
197
197
|
|
|
198
198
|
Sürükle-sırala: `reorderable: true` + `orderField` (yalnız dikey, tek sütun — MOB-035); bırakınca `reorder`
|
|
@@ -397,15 +397,15 @@ etiketi ailesini `typographyStyle`'dan alır, alan etiketleri platform yazı tip
|
|
|
397
397
|
`badge.ref` **saklı** state yolu; `keep`: ikinci dokunuşta yığın durur.
|
|
398
398
|
Çubuk düzeyinde iki davranış anahtarı vardır ve **yalnız varsayılandan ayrılınca yazılır**: `"keepStackOnSwitch": false`
|
|
399
399
|
(sekme değişince hedef sekme köke döner; alan yoksa yığın korunur) ve `"hideOnKeyboard": true` (klavye açılınca çubuk
|
|
400
|
-
gizlenir;
|
|
400
|
+
gizlenir; `platform_guide('mobile-layout')` §Klavye). `true`/`false`'tan başka değer (ör. metin `"false"`) yok sayılır ve `MOB-007` uyarır.
|
|
401
401
|
İkinci dokunuş (`reselect`) ile sekme değişimi (`keepStackOnSwitch`) ayrı ayardır; eski runtime anahtarları bilmez ve
|
|
402
402
|
varsayılan davranışta kalır.
|
|
403
|
-
- **Sekme sayfasına `redirect` sekmeye geçer, kopya itmez
|
|
403
|
+
- **Sekme sayfasına `redirect` sekmeye geçer, kopya itmez** (kabuk 0.12.0+, `tabRedirectReturns`; eskide yığına kopya iter). Hedef (`pageId` ya da sayfanın yolu) bir sekme sayfasıysa
|
|
404
404
|
o sekmeye geçilir; bulunulan sekmenin kök sayfasıysa o sekmenin yığını köke iner — "kaydet → listeye dön" böyle
|
|
405
405
|
yazılır. Başka sayfa bulunulan sekmenin yığınına itilir.
|
|
406
|
-
- **`back` adımı iki kabukta da yığında bir geri gider
|
|
406
|
+
- **`back` adımı iki kabukta da yığında bir geri gider** (kabuk 0.12.0+, `backStep`; eskide önceki sayfaya `redirect`). Gidemezse (yığının ilk ekranı) hiçbir şey yapmaz, akış yine
|
|
407
407
|
`always`'tan sürer ve telefonun defterine `back-unavailable` düşer (`mobile_error_list`; kök ADR 0013).
|
|
408
|
-
- **Sayfadan çıkılınca açık akış yarıda kalmaz
|
|
408
|
+
- **Sayfadan çıkılınca açık akış yarıda kalmaz** (kabuk 0.12.0+, `flowDrain`): telefona yazan adımlar sonuna kadar koşar, ekrana dokunanlar atlanır
|
|
409
409
|
(`platform_guide('mobile-steps')` §Sayfa kapanınca).
|
|
410
410
|
- **Çekmece bir bileşendir.** `drawer` widget'ı + `app-bar`'ın `menuTarget` prop'u çekmeceyi açar.
|
|
411
411
|
Çekmece layout'un kök dizisindedir (`views: [shell_root, nav_drawer]`), kabuk kabının içinde değil.
|
|
@@ -484,7 +484,7 @@ Sunucudan gönderilen bildirim yerel `notification-schedule`'dan ayrıdır. Kana
|
|
|
484
484
|
## Kalıcı state
|
|
485
485
|
|
|
486
486
|
State varsayılan olarak **bellektedir**: `state.page` sayfa her açıldığında `variable_create`'teki `value`'dan başlar,
|
|
487
|
-
ekran kapanınca kaybolur. **Mobil kabukta `state.app` uygulama + ortam başına tek depodur** (kök ADR 0031): bir
|
|
487
|
+
ekran kapanınca kaybolur. **Mobil kabukta `state.app` uygulama + ortam başına tek depodur** (kök ADR 0031; kabuk 0.12.0+, `sharedAppState` — eskide her sayfa kendi kopyasını alır): bir
|
|
488
488
|
sayfanın yazdığı arkadaki sayfada da anında görünür; açılan sayfanın `value`'su yalnız depoda olmayan anahtarı
|
|
489
489
|
doldurur. Depo süreç ömrü kadar yaşar ve çıkışta silinir. Studio önizlemesi ve web her sayfaya kendi kopyasını verir (38 S-5).
|
|
490
490
|
Uygulama kapanıp açıldığında kaldığı yerden sürmesi gereken değer için değişkene saklama türü verin:
|
|
@@ -532,6 +532,8 @@ variable_create({ applicationId, pageId, variables: [
|
|
|
532
532
|
ö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
|
|
533
533
|
(liste katmanda açılır, `optionsSeen` kaç seçenek görüldüğünü söyler); `tab` bir `tabs`
|
|
534
534
|
bileşeninin sekmesini anahtarıyla (`<elementId>__tab-<anahtar>`) bulur.
|
|
535
|
+
- **`click` gerçek noktaya basar** (SCR-024): o noktada üstte başka öğe varsa `applied: false` + `coveredBy` —
|
|
536
|
+
`platform_guide('mobile-steps')` §Önizlemede çekim hedefleri.
|
|
535
537
|
- `flowRuns: 0` + `requests: 0` bir tıklamada akışın koşmadığını söyler: olay bağını
|
|
536
538
|
`page_runtime_get`, akışı `flow_get` ile doğrulayın.
|
|
537
539
|
- Kuru doğrulama: `page_schema_validate` mobil kataloğa karşı doğrular (`MOB-003`).
|
|
@@ -596,7 +598,8 @@ yaptıkları. Derlenen uygulama
|
|
|
596
598
|
- Ağ yokken ekranlar ve (açıksa) son okunan veri cihazdaki kopyadan gelir: `properties.mobile.offline`
|
|
597
599
|
(MOB-033), `Steps.<ad>.cache.at`, `error.network` — `platform_guide('page')` §Kural 1g-4. Önizleme önbellek kullanmaz.
|
|
598
600
|
Yalnız GET okuma saklanır (POST okuma ağsız açılmaz → GET + query-string, FLOW-044); kuyruk değişimi sayfanın
|
|
599
|
-
`offlineQueueChange` olayıdır, yoklama gerekmez (§Kural 1g-4).
|
|
601
|
+
`offlineQueueChange` olayıdır, yoklama gerekmez (§Kural 1g-4; kabuk 0.12.0+, `offlineQueueEvent`). Hangi özelliğin
|
|
602
|
+
hangi kabukta olduğu: `platform_guide('mobile-publish')` §Kabuk sürümleri.
|
|
600
603
|
Uygulama kaydı (ad, slug, `properties`, ana sayfa, bulunamadı sayfası) `application_update` ile yalnız
|
|
601
604
|
**development**'a yazılır; production'da `deployment_start`'tan sonra geçerli olur (APP-001) — önce development'ta
|
|
602
605
|
deneyin. Slug değişirse production derlemesi dağıtıma kadar eski slug'ı taşır.
|
|
@@ -1556,7 +1556,7 @@ Kayıtlar kullanıcıya, ortama ve veri diline bağlıdır; çıkışta ve kulla
|
|
|
1556
1556
|
- **Görünürlük:** kaydın sonucu akışa dönmez. `Device.offline.queue = { pending, rejected }` ve
|
|
1557
1557
|
`Device.offline.rejected` (`id`, `service`, `at`, `status`, `code`, `message`); kod adımında `Page.offline.list()`,
|
|
1558
1558
|
`.count()`, `.sync()`, `.resolve(id, 'retry' | 'discard')`. Süresi dolan kayıt yeniden denenmez.
|
|
1559
|
-
- **Kuyruk değişince ekran tazelenir
|
|
1559
|
+
- **Kuyruk değişince ekran tazelenir** (kabuk 0.12.0+, `offlineQueueEvent`; eskide olay koşmaz, listeyi bir zamanlayıcıyla yeniden okuyun): mobil sayfanın `offlineQueueChange` olayı (`page.events`, `mount`/`destroy`/`focus`
|
|
1560
1560
|
yanında; Studio'da "Çevrimdışı kuyruk değiştiğinde") kayıt girdiğinde, gönderildiğinde, reddedildiğinde ve silindiğinde koşar.
|
|
1561
1561
|
Yük taşımaz: güncel sayaç olaydan hemen önce yazılan `Device.offline`'dadır. İlk okuma olay üretmez; olaya bağlı akış
|
|
1562
1562
|
koşarken kuyruğun değişmesi olayı yeniden tetiklemez (kuyruğa yazan akış döngüye girmez). Listeyi bekleyenlerle
|
|
@@ -1567,6 +1567,22 @@ Kayıtlar kullanıcıya, ortama ve veri diline bağlıdır; çıkışta ve kulla
|
|
|
1567
1567
|
|
|
1568
1568
|
Örnek: `ngapps://examples/mobile-offline-queue` (doz işaretleme: servis bildirimi + `queued` dalı).
|
|
1569
1569
|
|
|
1570
|
+
**Hangi dal koşar** (`microservice` adımı; sıra kök ADR 0004, `ng-apps-flow` `microservice.ts`). Her hücre koşan
|
|
1571
|
+
TEK yoldur; `always` yalnız "always" yazan hücrede koşar:
|
|
1572
|
+
|
|
1573
|
+
| Durum | `error` dalı bağlı | `error` yok, akışta `errorSteps` var | ikisi de yok |
|
|
1574
|
+
|---|---|---|---|
|
|
1575
|
+
| Çevrimiçi başarı | `always` | `always` | `always` |
|
|
1576
|
+
| Çevrimiçi ret (4xx/5xx; bildirimsiz servise `queued` bağlı yazma da 422) | `error` — **`always` koşmaz** | `errorSteps` | `always` (`Steps.<ad>.data` `null`) |
|
|
1577
|
+
| Ağ yok + `queued` bağlı + mobil + yazma (POST/PUT/DELETE) + dosyasız | `queued` | `queued` | `queued` |
|
|
1578
|
+
| Ağ yok, ötekiler (web, önizleme, GET, `queued` yok) | `error` | `errorSteps` | `always` (`data` `null`) |
|
|
1579
|
+
| Ağ yok + `queued` bağlı ama dosya parametresi var | `error` (kod `SERVICE_CALL_FILE_NOT_QUEUED`) | `errorSteps` | `always` |
|
|
1580
|
+
|
|
1581
|
+
`success` dalı yalnız eski şemada vardır; bağlıysa başarıda `always` yerine o koşar, `error` ve `errorSteps` yokken
|
|
1582
|
+
hatada da o koşar. Hata yanıtı `uiActions` taşıyorsa (sunucunun döndürdüğü arayüz eylemleri) o eylemler koşar ve adım
|
|
1583
|
+
hiçbir dala geçmez. `error` bağlıyken ortak son adımı
|
|
1584
|
+
(`txt_sonuc`, yükleme kapatma) `error` dalına da bağlayın: hata `always`'e hiç uğramaz.
|
|
1585
|
+
|
|
1570
1586
|
**Veri önbelleği açıkken akış ne görür:**
|
|
1571
1587
|
|
|
1572
1588
|
- Saklanan okuma 8 sn süre sınırı taşır; yanıt gelirse olduğu gibi döner ve arkada saklanır. Saklanmayan isteklerde
|
|
@@ -1603,6 +1619,8 @@ AYNI; veri bağlantı gelince Zeyrek'e kendiliğinden gider. Altı adım birlikt
|
|
|
1603
1619
|
2. **Okuma GET:** okuyan uç `httpMethod: 'get'`, adım `isRaw: false`; uç filtreleri ve adım parametreleri
|
|
1604
1620
|
`source: 'query-string'` (GET'te gövde gitmez). Önbellek yalnız GET okumayı saklar; POST ile okuyan adım ağsızken
|
|
1605
1621
|
kayıttan dönemez ve hata yoluna düşer — **FLOW-044** uyarır (yöntem servis tanımındandır, adıma yazılan saklanmaz).
|
|
1622
|
+
İş akışı kaynaklı uçta gateway okuma mı yazma mı bilemez; POST ile gidilirse yalnız bilgi notu düşer (uç
|
|
1623
|
+
`offlineWritable` ya da `queued` dalı bağlıysa o da düşmez), okuyan akışın ucunu GET yapmak sizin işinizdir.
|
|
1606
1624
|
(Ölçüm: POST okuyan bir uygulamanın listesi ağsızken "yüklenemedi" dedi.)
|
|
1607
1625
|
3. **Yazma kuyruğu, uyarısız:** yazan uç `offlineWritable: true`; `microservice` adımının `queued` dalı `always` ile
|
|
1608
1626
|
**aynı adıma** bağlanır — ağsız yazma da çevrimiçi gibi biter. Adımın `error` dalı çevrimiçi reddi uyarır; kuyruktaki
|
|
@@ -1614,7 +1632,8 @@ AYNI; veri bağlantı gelince Zeyrek'e kendiliğinden gider. Altı adım birlikt
|
|
|
1614
1632
|
`offlineQueueChange` olayına da bağlanır: kuyruğa giren kayıt listeye hemen, gönderilen kayıt sunucudan gelir.
|
|
1615
1633
|
Kaydı yazan sayfadan çıkılsa da yazma akışı tamamlanır (kök ADR 0030) ve `state.app` mobil kabukta sayfalar arası
|
|
1616
1634
|
ortaktır (kök ADR 0031): "istekten önce yaz", "kuyruktan oku" ve "birkaç saniyede bir yokla" geçici çözümleri
|
|
1617
|
-
gerekmez (
|
|
1635
|
+
kabuk 0.12.0+'da gerekmez (`flowDrain`, `sharedAppState`, `offlineQueueEvent`; eski kabukta kalır — yayındaki sürüm
|
|
1636
|
+
`mobile_store_get` `shell`; Defter saha raporu, 2026-10-30).
|
|
1618
1637
|
5. **Düğme kilidi:** yazan düğme istek süresince `isLoading` + `isDisabled`; her dalın sonunda açılır. Kilitsiz düğmede
|
|
1619
1638
|
hızlı ikinci dokunuş yeni bir istek ve yeni bir `Idempotency-Key`'dir — sunucu ikisini de uygular (ölçüm: 1,6 sn'de
|
|
1620
1639
|
üç dokunuş, üç kayıt).
|
|
@@ -1660,7 +1679,11 @@ kaydedilmemiş sırada kalır. Hatayı kullanıcıya gösterin; eski sıraya dö
|
|
|
1660
1679
|
1. Uç: tablo kaynaklı `operation: 'bulk-update'`, `fields` = birincil anahtar + sıra alanı (başka alan koymayın —
|
|
1661
1680
|
tanımdaki alan gövdede yoksa NULL yazılır, `platform_guide('microservice')` §Güncelleme sözleşmesi). Gövdedeki
|
|
1662
1681
|
anahtarlar alan **slug'larıdır**; `fields[].name` ile yeniden adlandırma yoktur (platform sessizce düşürür), yani
|
|
1663
|
-
`{ id, value }` yaması uca olduğu gibi gitmez, eşlenir.
|
|
1682
|
+
`{ id, value }` yaması uca olduğu gibi gitmez, eşlenir. **Uç sahiplikli kurulur:** `middlewareIds` ile oturum ister
|
|
1683
|
+
(`platform_guide('middleware')` Tarif 1) ve `filters`'ta `{ field: <sahip alanı>, operator: 'eq', source: 'expression',
|
|
1684
|
+
value: 'user.id' }` taşır. Platform bu kapsamı her satırın `UPDATE`'ine koyar; başkasının satır kimliği gelirse bütün
|
|
1685
|
+
parti 404 ile reddedilir, hiçbir satır yazılmaz (MS-027, `platform_guide('microservice')` §Toplu yazmada sahiplik).
|
|
1686
|
+
Filtresiz uç gönderilen her kimliği yazar.
|
|
1664
1687
|
2. Akış adımı: ucun gövde parametrelerinden **yalnız birine** (birincil anahtarınkine, ör. `id`) satır listesini
|
|
1665
1688
|
`code` ile verin, öbürlerini boş bırakın. Servisin şeması `isParameterArray` dediği için değeri olan tek gövde
|
|
1666
1689
|
parametresinin dizisi gövdenin KENDİSİ olur — parametrenin adı anlamını yitirir:
|
|
@@ -2516,6 +2539,8 @@ start → [setter] btn.isLoading = true, btn.isDisabled = true, notification giz
|
|
|
2516
2539
|
- Prop değeri nesne olabilir: `icon` → `{ "type": "static", "ref": { "name": "progress_activity" } }`.
|
|
2517
2540
|
- Giriş sayfası kimlik işinin görünen yarısıdır. Sign-in ucu + auth middleware'i kurup bu sayfayı
|
|
2518
2541
|
kurmamak, sistemi kullanılamaz bırakır.
|
|
2542
|
+
- "Şifremi unuttum" bağlantısı anonim bir sıfırlama sayfasına gider; uçlar ve sayfa akışı
|
|
2543
|
+
`platform_guide('middleware')` §Tarif 4.
|
|
2519
2544
|
|
|
2520
2545
|
### 7d — Detay / profil sayfası
|
|
2521
2546
|
|
|
@@ -283,6 +283,12 @@ motor olmasıdır (tarayıcı JS'i ve Jint).
|
|
|
283
283
|
verilmişse sekme başlığı çeviri sözlüğünden çözülür; anahtar boşsa `browserTitle` olduğu gibi
|
|
284
284
|
kullanılır. İkisi de boşsa sayfa adı yazılır.
|
|
285
285
|
|
|
286
|
+
**Uygulama öneki** (kök `docs/adr/0034`): `application_update { properties: { …, web: { browserTitle: 'IMS' } } }`
|
|
287
|
+
verilirse her sekme `"IMS | <sayfa başlığı>"` olur. Önek düz metindir, her dilde aynıdır; çevrilen yalnız sayfa
|
|
288
|
+
kısmıdır. `application_update` tam kaydı yazar: mevcut `properties`'i `application_get`'ten alıp `web` anahtarını
|
|
289
|
+
ekleyin, yoksa öteki ayarlar (mobil sekmeler, geçiş) silinir. Önek yalnız `development`'a yazılır; `production`
|
|
290
|
+
dağıtımla alır.
|
|
291
|
+
|
|
286
292
|
> **Sekme başlığı canlı dil değişimini İZLER.** `language-selector` ile dil değiştirildiğinde
|
|
287
293
|
> sayfa içeriğiyle birlikte sekme başlığı da yeni dilde çözülür; yeniden yükleme gerekmez
|
|
288
294
|
> (ölçüldü 2026-09-11). Anahtarın seçilen dilde değeri yoksa projenin varsayılan diline düşer.
|
|
@@ -116,7 +116,9 @@ Aktif dil `context.language`, anahtar çözmek için `Loc(key)`. Motor kültür
|
|
|
116
116
|
## Step'ler nasıl bağlanır
|
|
117
117
|
|
|
118
118
|
Her step'in `branches[]` dizisi vardır; `nextStepId` sonraki step'in id'sini gösterir.
|
|
119
|
-
Bağlanmamış step çalışmaz. `start` tek branch taşır
|
|
119
|
+
Bağlanmamış step çalışmaz. `start` tek branch taşır ve o dalın **adı** `start`, **tipi** `success`'tir:
|
|
120
|
+
`{ name: 'start', type: 'success', nextStepId }` — `type: 'start'` platformda 400 alır (dal tipi yalnız `success` ·
|
|
121
|
+
`fail` · `impersonation` · `global-error`; WF-038). Rehberlerdeki `start → …` okları bu dalı gösterir. Normal step'ler `success` + `fail`;
|
|
120
122
|
`condition` koşul branch'leri + `else`; terminal step'ler (`response`, `convert`, `revert`)
|
|
121
123
|
**branch taşımaz**. Ayrı bir `end` step tipi yoktur; son dalın `nextStepId` değerini
|
|
122
124
|
`null` bırakarak akışı bitirin.
|
|
@@ -426,6 +428,33 @@ Workflow ya da otomasyon, olay anında BELİRLİ uygulama kullanıcılarına (ya
|
|
|
426
428
|
`automation_trigger` ile çalıştıramaz — istek gitmeden reddedilir. Development'ta deneyin.
|
|
427
429
|
- `to.users` GUID olmalı; uygulama `applicationId` ile verilirse o da mobil olmalı (değilse 404, hata dalı).
|
|
428
430
|
|
|
431
|
+
## Email adımı: alıcı yalnız `rule.expression` (WF-037)
|
|
432
|
+
|
|
433
|
+
E-posta adımı platformun SMTP ayarıyla (lokal yığında `mailpit`, arayüz `http://localhost:8025`) ileti gönderir.
|
|
434
|
+
Gönderen adresi platform ayarıdır; adım yalnız gönderen **adını** verir.
|
|
435
|
+
|
|
436
|
+
| Kural anahtarı | Ne | Örnek |
|
|
437
|
+
|---|---|---|
|
|
438
|
+
| `expression` | Alıcılar: `{ to, cc, bcc }` adres dizgisi listesi döndüren ifade. `cc`/`bcc` isteğe bağlı | `"({ to: [input.email] })"` |
|
|
439
|
+
| `subject` | Konu ifadesi | `"'Şifre sıfırlama kodu'"` |
|
|
440
|
+
| `body` | Gövde ifadesi; HTML olarak gider | `` "`<p>Kodunuz: <b>${kod.data}</b></p>`" `` |
|
|
441
|
+
| `displayName` | Gönderen adı | `"'Defter'"` |
|
|
442
|
+
| `attachment` | Base64 dizgi listesi döndüren ifade (isteğe bağlı) | `"[]"` |
|
|
443
|
+
|
|
444
|
+
- **Alıcı başka bir anahtardan okunmaz.** `send`, `send.toMail`, `to`, `toMail`, `sendTo`, `recipients` kural
|
|
445
|
+
düzeyinde yazılırsa platform bağlarken atar; gateway kaydı WF-037 ile durdurur. `workflow_step_schema`'daki
|
|
446
|
+
`expression.to|cc|bcc` bu ifadenin **döndürdüğü şekildir** (2026-11-04'ten önce şema bunu `send.toMail`/`send.to`
|
|
447
|
+
diye kural anahtarı gibi gösteriyordu — zeyrek-backend incident #197).
|
|
448
|
+
- Liste öğesi adres **dizgisidir**; boş öğe (`null`, `undefined`, `''`) atlanır, böylece `cc: [row.managerEmail]`
|
|
449
|
+
yöneticisiz kayıtta yalnız `to`'ya gider. `{ type, value }` gibi nesne öğe adımı `EmailRecipientInvalidException` ile,
|
|
450
|
+
hiç alıcı olmaması `EmailRecipientsMissingException` ile **fail** dalına düşürür (ileti gönderilmez). Eski
|
|
451
|
+
`({ toMail: '…' })` / `bccMails` biçimi hâlâ okunur, yeni akışta kullanmayın.
|
|
452
|
+
- SMTP hatası da fail dalıdır; fail dalı bağlı değilse akışın global hata dalına gider.
|
|
453
|
+
- **Önizleme gerçekten gönderir** (Push'tan farkı): `workflow_preview` ile denediğiniz alıcıya ileti gider. Lokal
|
|
454
|
+
yığında `curl -s localhost:8025/api/v1/messages` son iletileri döner.
|
|
455
|
+
- Ölçülmüş örnek: `ngapps://examples/workflow-email-step`. Parola sıfırlama tarifi: `platform_guide('middleware')`
|
|
456
|
+
§Tarif 4.
|
|
457
|
+
|
|
429
458
|
## Step tipi × workflow tipi izin matrisi
|
|
430
459
|
|
|
431
460
|
| Step | workflow | automation | middleware |
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
|
-
"hash": "
|
|
3
|
-
"generatedAt": "2026-11-
|
|
2
|
+
"hash": "88ccb2c27599cdeb3701f0e4546adee3c3845bd3b7c32c1d5a49b254c70e47a3",
|
|
3
|
+
"generatedAt": "2026-11-04T11:15:39.446Z",
|
|
4
4
|
"types": 377,
|
|
5
5
|
"files": 196,
|
|
6
6
|
"note": "Şemaları üreten backend kaynaklarının parmak izi. Elle düzenlemeyin: 'npm run generate-schemas' yazar, 'npm run schema:check' denetler.",
|
|
@@ -113,7 +113,7 @@ export function registerApplicationTools(server, platform) {
|
|
|
113
113
|
}));
|
|
114
114
|
server.registerTool('application_update', {
|
|
115
115
|
title: 'Application güncelle',
|
|
116
|
-
description: `Application bilgilerini günceller; homepageId zorunludur (mevcut değeri application_get ile alın). ORTAM (K5, b-15): bütün alanlar — name, slug, color, dns, properties (mobile.offline, sekmeler, geçi
|
|
116
|
+
description: `Application bilgilerini günceller; homepageId zorunludur (mevcut değeri application_get ile alın). ORTAM (K5, b-15): bütün alanlar — name, slug, color, dns, properties (mobile.offline, sekmeler, geçiş…; web.browserTitle = tarayıcı sekmesi öneki, "IMS | <sayfa>"), homepageId, notFoundPageId — yalnız development'a (ve Core'a) yazılır; production onları deployment_start ile alır (dal ortamları açıldıkları andaki kopyayı tutar), yani önce development'ta deneyin. Slug değişirse production adresi dağıtıma kadar eski slug'la çalışır. Yanıttaki environmentNote bunu çağrı başına söyler. ${templateSubscriptionLock}`,
|
|
117
117
|
inputSchema: {
|
|
118
118
|
id: guid,
|
|
119
119
|
slug: z.string().min(1),
|
|
@@ -141,11 +141,14 @@ export function registerApplicationTools(server, platform) {
|
|
|
141
141
|
}));
|
|
142
142
|
server.registerTool('application_delete', {
|
|
143
143
|
title: 'Application sil',
|
|
144
|
-
description: `Application'ı kalıcı olarak siler — development'tan (ve Core'dan). ORTAM (b-17, APP-001): production'daki kopya ve kullanıcı verisi deployment_start'a kadar yerinde kalır, silme dağıtımla gider; dallar kendi kopyasını tutar. ${templateSubscriptionLock}`,
|
|
145
|
-
inputSchema: {
|
|
144
|
+
description: `Application'ı kalıcı olarak siler — development'tan (ve Core'dan). ORTAM (b-17, APP-001): production'daki kopya ve kullanıcı verisi deployment_start'a kadar yerinde kalır, silme dağıtımla gider; dallar kendi kopyasını tutar. SON UYGULAMA (PS-17): varsayılan olmayan projenin tek uygulaması silinirse PROJE DE SİLİNİR — teması, tabloları, verisi ve veritabanıyla; platform bunu deleteProject: true olmadan 409 LastApplicationDeleteRequiresConfirmationException ile reddeder. deleteProject'i yalnız kullanıcı projenin de gitmesini açıkça istediyse verin. ${templateSubscriptionLock}`,
|
|
145
|
+
inputSchema: {
|
|
146
|
+
id: guid,
|
|
147
|
+
deleteProject: z.boolean().optional().describe('Projenin son uygulamasını silerken projenin de silinmesine açık onay. Yalnız kullanıcı istediyse true; başka durumda platform yok sayar.')
|
|
148
|
+
},
|
|
146
149
|
annotations: destructive
|
|
147
150
|
}, handle(config, async (args) => {
|
|
148
|
-
const response = await platform.org('DELETE', `/applications/${args.id}
|
|
151
|
+
const response = await platform.org('DELETE', `/applications/${args.id}`, args.deleteProject === true ? { query: { deleteProject: true } } : {});
|
|
149
152
|
forgetApplication(platform, args.id);
|
|
150
153
|
return response;
|
|
151
154
|
}));
|
package/dist/tools/common.js
CHANGED
|
@@ -90,3 +90,11 @@ export const readOnly = { readOnlyHint: true, openWorldHint: false };
|
|
|
90
90
|
export const writes = { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: false };
|
|
91
91
|
export const updates = { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: false };
|
|
92
92
|
export const destructive = { readOnlyHint: false, destructiveHint: true, idempotentHint: true, openWorldHint: false };
|
|
93
|
+
/** A patch whose net diff against the stored record is empty: nothing is sent (the platform rejects empty `ops` with 400, PS-28). */
|
|
94
|
+
export function unchangedPatch(applied) {
|
|
95
|
+
return {
|
|
96
|
+
outcome: 'unchanged',
|
|
97
|
+
applied,
|
|
98
|
+
note: "değişiklik yok — gönderilen op'lar kayıtlı tanımı değiştirmiyor; platforma istek gönderilmedi"
|
|
99
|
+
};
|
|
100
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { orphanedFlowWarnings, orphanedFlowPlan, orphanedFlowMessage, boundEventOps, stripGatewayEventFields, staleSetterWarnings, normalizeSchemaOps, assertAddComponentIds, assertNoRetiredTypes, validationNote, componentNameNote, componentDefaultsNote, replacePropNote, flexSizeWarnings, nestedRepeaterNodes, nestedRepeaterMessage, stringStyleNodes, stringStyleWarning, inertFlexPropNodes, inertFlexPropWarning, verticalGapWarnings, withNormalization, FLEX_LAYOUT_PROP_NAMES } from '../knowledge/normalize.js';
|
|
1
|
+
import { orphanedFlowWarnings, orphanedFlowPlan, orphanedFlowMessage, boundEventOps, flowIdEventOps, stripGatewayEventFields, staleSetterWarnings, normalizeSchemaOps, assertAddComponentIds, assertNoRetiredTypes, validationNote, componentNameNote, componentDefaultsNote, replacePropNote, flexSizeWarnings, nestedRepeaterNodes, nestedRepeaterMessage, stringStyleNodes, stringStyleWarning, inertFlexPropNodes, inertFlexPropWarning, verticalGapWarnings, withNormalization, FLEX_LAYOUT_PROP_NAMES } from '../knowledge/normalize.js';
|
|
2
2
|
import { opsParam, writeViewParam, outlineWriteResponse, unknownEventOps, collectTypes, simulateLayoutOps, removeNodeFromTree, planEventRemovals, callerSlots, planEmulatedOps, recordsWithEventRemovals, serverOpsInOrder, assertOpBodies, applySkeletonOps, reshapesSkeleton } from './schemaOps.js';
|
|
3
3
|
import { guid, projectParam, environmentParam, updates, microOps } from './common.js';
|
|
4
4
|
import { fetchHost, hostPath } from './screens.js';
|
|
@@ -107,6 +107,9 @@ export function registerDesignSchemaPatchTool(server, platform, host) {
|
|
|
107
107
|
const connectionIssues = [...opRecords, ...propTypeOps].flatMap(entry => connectDataOpIssues(entry, connectedProps).map(issue => `${issue.code} — ${issue.message}`));
|
|
108
108
|
if (connectionIssues.length > 0)
|
|
109
109
|
throw new PlatformApiError(`Connect Data op gövdesi geçersiz (istek gönderilmedi): ${connectionIssues.join(' · ')}`, 400);
|
|
110
|
+
const flowIdOps = flowIdEventOps(opRecords);
|
|
111
|
+
if (flowIdOps.length > 0)
|
|
112
|
+
throw new PlatformApiError(`PAGE-052 — olay akışı eventValue ile alır (istek gönderilmedi): ${flowIdOps.join(' · ')}`, 400);
|
|
110
113
|
if (opRecords.some(entry => entry.op === 'set_event' || entry.op === 'add_event')) {
|
|
111
114
|
const bound = boundEventOps(nodes, opRecords);
|
|
112
115
|
if (bound.length > 0)
|
package/dist/tools/flows.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { fetchHost, flowPath, hostParam, unwrap } from './screens.js';
|
|
2
|
-
import { guid, projectParam, environmentParam, readOnly, writes, updates, destructive, microOps } from './common.js';
|
|
2
|
+
import { guid, projectParam, environmentParam, readOnly, writes, updates, destructive, microOps, unchangedPatch } from './common.js';
|
|
3
3
|
import { applyPolicyMode, evaluatePolicy, enforcePolicy, attachFindings } from '../knowledge/policy.js';
|
|
4
4
|
import { connectedSetterFindings } from '../knowledge/connectDataDefinition.js';
|
|
5
5
|
import { flowBindings, normalizeFlowSteps, targetlessSetterWarnings, unknownSetterTargetWarnings, withNormalization } from '../knowledge/normalize.js';
|
|
@@ -390,7 +390,8 @@ async function queuedBranchWarnings(platform, applicationId, steps, errorSteps,
|
|
|
390
390
|
* yöntemle giden mobil adım ağsızken kayıttan dönemez, hata yoluna düşer. Yöntem servis tanımındandır: backend adımın
|
|
391
391
|
* `httpMethod`'unu her okumada servisten üretir (`StepManagementSchema`), adıma yazılan saklanmaz. Veri önbelleği kapalı
|
|
392
392
|
* uygulamada hiçbir okuma saklanmadığı için susar. İş akışı ve sorgu kaynaklı uç okuma mı yazma mı bilinmez (kök ADR 0022
|
|
393
|
-
* Y1)
|
|
393
|
+
* Y1): uyarı üretmez; iş akışı kaynaklı uca GET dışı yöntemle giden adım yalnız bilgi notu alır (PS-31) — uç `offlineWritable`
|
|
394
|
+
* ise ya da adımın `queued` dalı bağlıysa yazmadır, not da düşmez; sorgu kaynaklı uç susar. Okunamayan uygulama ya da servis de not üretmez.
|
|
394
395
|
*/
|
|
395
396
|
async function uncachedReadWarnings(platform, applicationId, steps, errorSteps, readService) {
|
|
396
397
|
const serviceSteps = [...(Array.isArray(steps) ? steps : []), ...(Array.isArray(errorSteps) ? errorSteps : [])].filter(isRecord)
|
|
@@ -409,11 +410,18 @@ async function uncachedReadWarnings(platform, applicationId, steps, errorSteps,
|
|
|
409
410
|
continue;
|
|
410
411
|
const source = String(definition.source ?? '').toLowerCase();
|
|
411
412
|
const operation = String(definition.sourceParameters?.operation ?? '').toLowerCase();
|
|
412
|
-
if (source !== 'view' && !(source === 'table' && (operation === 'get' || operation === 'count')))
|
|
413
|
-
continue;
|
|
414
413
|
const method = String(definition.httpMethod ?? 'get').toLowerCase();
|
|
415
414
|
if (method === 'get')
|
|
416
415
|
continue;
|
|
416
|
+
if (source === 'workflow') {
|
|
417
|
+
const queued = (Array.isArray(step.branches) ? step.branches : []).filter(isRecord).some(branch => String(branch.name ?? '') === 'queued' && Boolean(branch.nextStepId));
|
|
418
|
+
if (definition.offlineWritable === true || queued)
|
|
419
|
+
continue;
|
|
420
|
+
warnings.push(`FLOW-044 (bilgi): '${String(step.stepId ?? step.name ?? '?')}' adımı iş akışı kaynaklı '${String(definition.slug ?? args.microserviceId)}' ucuna ${method.toUpperCase()} ile gidiyor — akışın okuduğu mu yazdığı mı tanımdan bilinmiyor; okuyan bir uçsa GET yapın: veri önbelleği yalnız GET okumayı saklar, ağsızken yalnız GET kayıttan döner. Yazan uçsa bu not geçersizdir.`);
|
|
421
|
+
continue;
|
|
422
|
+
}
|
|
423
|
+
if (source !== 'view' && !(source === 'table' && (operation === 'get' || operation === 'count')))
|
|
424
|
+
continue;
|
|
417
425
|
warnings.push(`FLOW-044: '${String(step.stepId ?? step.name ?? '?')}' adımı '${String(definition.slug ?? args.microserviceId)}' servisinden ${method.toUpperCase()} ile okuyor — veri önbelleği yalnız GET okumayı saklar: ağsızken bu adım kayıttan dönemez, hata yoluna düşer. Okuyan ucu GET yapın (microservice_patch '/httpMethod' 'get'; adımın yöntemi servisten gelir) ve gövdeden okunan parametreleri query-string'e taşıyın — uç filtrelerinin ve adım parametrelerinin source'u 'query-string' olmalı, GET'te gövde gitmez.`);
|
|
418
426
|
}
|
|
419
427
|
return warnings;
|
|
@@ -794,6 +802,8 @@ export function registerFlowTools(server, platform) {
|
|
|
794
802
|
];
|
|
795
803
|
// 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.
|
|
796
804
|
const netOps = fastJsonPatch.compare(current, flow);
|
|
805
|
+
if (netOps.length === 0)
|
|
806
|
+
return attachFindings(unchangedPatch(operations.length), [...findings, ...purchaseFindings]);
|
|
797
807
|
const saved = await platform.tenant('PATCH', project, flowPath(host, applicationId, hostId, flowId), {
|
|
798
808
|
environment,
|
|
799
809
|
body: { baseVersion: baseVersionOf(current, 'flow_get'), ops: netOps }
|
package/dist/tools/layouts.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { guid, projectParam, environmentParam, pagingParams, readOnly, writes, updates, destructive, microOps } from './common.js';
|
|
1
|
+
import { guid, projectParam, environmentParam, pagingParams, readOnly, writes, updates, destructive, microOps, unchangedPatch } from './common.js';
|
|
2
2
|
import { resolvePatchOperations } from '../knowledge/outlines.js';
|
|
3
3
|
import { patchResult, baseVersionOf } from '../platform/patch.js';
|
|
4
4
|
import fastJsonPatch from 'fast-json-patch';
|
|
@@ -259,6 +259,8 @@ export function registerLayoutTools(server, platform) {
|
|
|
259
259
|
// kuralları, value'suz olayların elenmesi) son hâle işlendikten sonra net diff hesaplanır.
|
|
260
260
|
const finalDocument = { ...patched, name: body.name, parameters: body.parameters, views: body.views, events: body.events };
|
|
261
261
|
const netOps = fastJsonPatch.compare(original, finalDocument);
|
|
262
|
+
if (netOps.length === 0)
|
|
263
|
+
return attachFindings(unchangedPatch(operations.length), textFindings);
|
|
262
264
|
const saved = await platform.tenant('PATCH', project, `/layouts/${applicationId}/${id}`, {
|
|
263
265
|
environment,
|
|
264
266
|
body: { baseVersion: baseVersionOf(document, 'layout_get'), ops: netOps }
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { guid, appRef, withResolved, projectParam, environmentParam, definitionParam, languageParam, pagingParams, asUserIdParam, readOnly, writes, updates, destructive, microOps } from './common.js';
|
|
1
|
+
import { guid, appRef, withResolved, projectParam, environmentParam, definitionParam, languageParam, pagingParams, asUserIdParam, readOnly, writes, updates, destructive, microOps, unchangedPatch } from './common.js';
|
|
2
2
|
import { languageHeader } from '../knowledge/languageCodes.js';
|
|
3
3
|
import { hydrateMicroserviceRefs } from '../knowledge/hydrate.js';
|
|
4
4
|
import { RefResolver } from '../platform/refs.js';
|
|
@@ -449,6 +449,8 @@ export function registerMicroserviceTools(server, platform) {
|
|
|
449
449
|
// Gövde değil FARK gönderilir: gateway normalizasyonları da op'a çevrilsin diye net diff
|
|
450
450
|
// okunan kayıt ile son hâl arasından hesaplanır (tam PUT yok, yalnız değişen alanlar yazılır).
|
|
451
451
|
const netOps = fastJsonPatch.compare(definition, patched);
|
|
452
|
+
if (netOps.length === 0)
|
|
453
|
+
return attachFindings(unchangedPatch(operations.length), findings);
|
|
452
454
|
const saved = await platform.tenant('PATCH', project, `/microservices/${resolvedApplicationId}/${id}`, {
|
|
453
455
|
environment,
|
|
454
456
|
body: { baseVersion: baseVersionOf(definition, 'microservice_get'), ops: netOps }
|
|
@@ -489,7 +491,11 @@ export function registerMicroserviceTools(server, platform) {
|
|
|
489
491
|
const bound = bindCallParameters(method, parameters, query, body, notes);
|
|
490
492
|
assertFilterOperators('microservice_execute', bound);
|
|
491
493
|
const raw = await platform.tenantRaw(method, project, executePath('m', application, microservice, routeParams), { environment, query: bound.query, body: bound.body, headers: requestHeaders, anonymous, mirrorAuthorizationCookie: true, noNetworkRetry: true });
|
|
492
|
-
return withNormalization({
|
|
494
|
+
return withNormalization({
|
|
495
|
+
...raw,
|
|
496
|
+
headers: headersForMode(raw.headers, includeHeaders ?? 'essential'),
|
|
497
|
+
truncationHint: "uç tablo/görünüm kaynaklıysa query ile sayfalayın — sayfa boyu ve başlangıç parametrelerinin adı uç tanımının sourceParameters.limit alanındadır (varsayılan limit ve offset; microservice_get ile bakın), toplam satırı total-count başlığı verir; uç başka kaynaklıysa filtreyi daraltın; bütün tablo gerekiyorsa row_export."
|
|
498
|
+
}, notes);
|
|
493
499
|
}));
|
|
494
500
|
server.registerTool('microservice_debug', {
|
|
495
501
|
title: 'Microservice debug',
|
|
@@ -12,7 +12,7 @@ import { LAYOUT_IDS } from '../platform/storeArt/layouts.js';
|
|
|
12
12
|
import { STYLE_IDS } from '../platform/storeArt/styles.js';
|
|
13
13
|
import { attachFindings, enforcePolicy, evaluatePolicy } from '../knowledge/policy.js';
|
|
14
14
|
import { policyRules } from '../knowledge/rules.js';
|
|
15
|
-
import { APPLE_RELEASE_TYPES, ASSET_KINDS, BUILD_ASSET_KINDS, COLOR_PATTERN, DECLARATION_KEYS, DEVICE_CLASSES, LOCALE_PATTERN, MAX_ASSET_BYTES, MAX_IMAGES_PER_SLOT, ORIENTATIONS, PLAY_TRACKS, RELEASE_STATUSES, SCREENSHOT_CAPTURE_DEVICES, SPLASH_RESIZE_MODES, USER_INTERFACE_STYLES, adsStoreWarnings, trackingDeclarationWarnings, assetSlotProblem, changedStoreFields, mergeStoreSettings, permissionLaunchShellWarnings, permissionLockShellWarnings, permissionStoreBuildWarnings } from '../knowledge/mobileStore.js';
|
|
15
|
+
import { APPLE_RELEASE_TYPES, ASSET_KINDS, BUILD_ASSET_KINDS, COLOR_PATTERN, DECLARATION_KEYS, DEVICE_CLASSES, LOCALE_PATTERN, MAX_ASSET_BYTES, MAX_IMAGES_PER_SLOT, ORIENTATIONS, PLAY_TRACKS, RELEASE_STATUSES, SCREENSHOT_CAPTURE_DEVICES, SPLASH_RESIZE_MODES, USER_INTERFACE_STYLES, adsStoreWarnings, shellCapabilityView, trackingDeclarationWarnings, assetSlotProblem, changedStoreFields, mergeStoreSettings, permissionLaunchShellWarnings, permissionLockShellWarnings, permissionStoreBuildWarnings } from '../knowledge/mobileStore.js';
|
|
16
16
|
/**
|
|
17
17
|
* Mobil mağaza yayınının ajan yolu (`plans/mobil-magaza-yayini` g-11, `plans/mobil-yayin-mcp` g-01):
|
|
18
18
|
* gizli olmayan mağaza ayarı, mağaza görselleri, beyanlar ve derleme işi. Gizli bilgi (Apple/Google
|
|
@@ -388,7 +388,7 @@ export function registerMobileStoreTools(server, platform, storeArt = {}) {
|
|
|
388
388
|
const galleryServer = storeArt.gallery ?? sharedGalleryServer;
|
|
389
389
|
server.registerTool('mobile_store_get', {
|
|
390
390
|
title: 'Mobil mağaza ayarını oku',
|
|
391
|
-
description: "Mobil uygulamanın gizli olmayan mağaza ayarını döner (GET mobile-store/{appId}): kimlik (bundleIdentifier, androidPackage, scheme), sürüm ve sayaçlar, görünüm, izin metinleri, kipleri ve açılışta sorulanlar (permissions, permissionModes, launchPermissions), listing (dil başına mağaza metni), urls, apple, play, review; lockedFields (ilk derlemeden sonra değişmeyen alanlar), görsel yuvası başına sayı (assets: 'screenshot/tr/iphone69': 3) ve beyanlar (declarations: anahtar → işaretli mi); settings.ads reklam ayarıdır (kök ADR 0012: enabled, App ID'ler, testMode, consentForm, appOpenUnit, units[] = adBanner.unit ve ad-show.value'nun andığı yerleşimler) ve reklam açıkken adsWarnings döner (MOB-026: test kipi, platformda birimsiz yerleşim, konsol beyanları); settings.appLock uygulama kilididir (kök ADR 0021: enabled, timeoutSeconds 0·60·300·900). Kayıt hiç yazılmamışsa persisted:false ve VARSAYILANLAR döner (kaydedilmez). manifest:true derlemeye gidecek manifesti de ekler (manifest-preview; sayaçlar SIRADAKİ numara); üretilemiyorsa manifestError alan adıyla döner. detail ayrıntı bölümleri ekler: 'assets' görselleri id'li liste olarak döner (mobile_asset_update / mobile_asset_delete id ister), 'signing' uygulamanın kimlik bilgilerini (signing.app: iOS profili, Android keystore, harita anahtarı, demo hesabı) ve organizasyonun Apple/Google hesaplarını + sertifikalarını (signing.accounts), 'runners' çevrimiçi runner'ları, 'audit' son 20 gizli bilgi olayını (subject: hangi tür, kim, ne zaman, hangi iş), 'permissions' izin görünümünü (permissions.permissions[]: uygulamanın isteyebileceği her izin için mode, text/defaultText, usage — izni kullanan sayfa·bileşen ya da akış adımı (setter ile yazılan tetikleyici prop dahil), ilk 20 yer + usageCount, çağrının environment'ından; usageReadable false ise o ortam okunamamıştır ve boş usage 'kullanılmıyor' demek değildir —, build — mağaza derlemesi taşıyor mu ve neden; HER ZAMAN production'dan: build.usageCount production'daki kullanım, build.staticUsageCount onun yazılı (ifadesiz) kısmı —, devices — aktif cihazların izin durumu sayıları; missingFromStoreBuilds — o izni taşımayan son mağaza derlemeleri; shell.carriesPermissions false ise kipler ve kabuğun henüz taşımadığı izinler bir sonraki kabuk sürümüyle etkili olur; shell.carriesLaunchPermissions false ise açılışta sorulan izinler (askOnLaunch) o kabukta sorulmaz, shell.carriesAppLock false ise açık uygulama kilidi o kabukta yoktur — permissionWarnings söyler), 'status' mağaza durumunu (GET store-status: apple { state, appStoreState, versionString, releaseType, phased* }, play.tracks[] { track, status, versionCodes, rolloutPercent }, actions[] { platform, target, track, irreversible, consequence } — Studio › Yayınla'daki yayın düğmeleri; irreversible:true geri alınamaz, eylemi MCP başlatmaz, kullanıcıya consequence'ı söyleyip Studio'ya yönlendirin), 'changes' son production dağıtımından beri değişenleri iki kovada (GET changes-since-production: live — dağıtımla telefonlara gider, mağaza gerekmez; store — izin/kimlik/görsel gibi yeni mağaza sürümü isteyenler). Gizli DEĞER hiçbir bölümde yoktur — yalnız tür, maskeli id ve meta (ekip, son kullanma); bir bölüm okunamazsa (uç hatası) <bölüm>Error döner, çağrı düşmez. Gizli bilgiyi EKLEMEK/DEĞİŞTİRMEK MCP'den yapılmaz — Studio › Yayın › Hesaplar.",
|
|
391
|
+
description: "Mobil uygulamanın gizli olmayan mağaza ayarını döner (GET mobile-store/{appId}): kimlik (bundleIdentifier, androidPackage, scheme), sürüm ve sayaçlar, görünüm, izin metinleri, kipleri ve açılışta sorulanlar (permissions, permissionModes, launchPermissions), listing (dil başına mağaza metni), urls, apple, play, review; lockedFields (ilk derlemeden sonra değişmeyen alanlar), görsel yuvası başına sayı (assets: 'screenshot/tr/iphone69': 3) ve beyanlar (declarations: anahtar → işaretli mi); settings.ads reklam ayarıdır (kök ADR 0012: enabled, App ID'ler, testMode, consentForm, appOpenUnit, units[] = adBanner.unit ve ad-show.value'nun andığı yerleşimler) ve reklam açıkken adsWarnings döner (MOB-026: test kipi, platformda birimsiz yerleşim, konsol beyanları); settings.appLock uygulama kilididir (kök ADR 0021: enabled, timeoutSeconds 0·60·300·900). Kayıt hiç yazılmamışsa persisted:false ve VARSAYILANLAR döner (kaydedilmez). manifest:true derlemeye gidecek manifesti de ekler (manifest-preview; sayaçlar SIRADAKİ numara); üretilemiyorsa manifestError alan adıyla döner. detail ayrıntı bölümleri ekler: 'assets' görselleri id'li liste olarak döner (mobile_asset_update / mobile_asset_delete id ister), 'signing' uygulamanın kimlik bilgilerini (signing.app: iOS profili, Android keystore, harita anahtarı, demo hesabı) ve organizasyonun Apple/Google hesaplarını + sertifikalarını (signing.accounts), 'runners' çevrimiçi runner'ları, 'audit' son 20 gizli bilgi olayını (subject: hangi tür, kim, ne zaman, hangi iş), 'permissions' izin görünümünü (permissions.permissions[]: uygulamanın isteyebileceği her izin için mode, text/defaultText, usage — izni kullanan sayfa·bileşen ya da akış adımı (setter ile yazılan tetikleyici prop dahil), ilk 20 yer + usageCount, çağrının environment'ından; usageReadable false ise o ortam okunamamıştır ve boş usage 'kullanılmıyor' demek değildir —, build — mağaza derlemesi taşıyor mu ve neden; HER ZAMAN production'dan: build.usageCount production'daki kullanım, build.staticUsageCount onun yazılı (ifadesiz) kısmı —, devices — aktif cihazların izin durumu sayıları; missingFromStoreBuilds — o izni taşımayan son mağaza derlemeleri; shell.carriesPermissions false ise kipler ve kabuğun henüz taşımadığı izinler bir sonraki kabuk sürümüyle etkili olur; shell.carriesLaunchPermissions false ise açılışta sorulan izinler (askOnLaunch) o kabukta sorulmaz, shell.carriesAppLock false ise açık uygulama kilidi o kabukta yoktur — permissionWarnings söyler), 'status' mağaza durumunu (GET store-status: apple { state, appStoreState, versionString, releaseType, phased* }, play.tracks[] { track, status, versionCodes, rolloutPercent }, actions[] { platform, target, track, irreversible, consequence } — Studio › Yayınla'daki yayın düğmeleri; irreversible:true geri alınamaz, eylemi MCP başlatmaz, kullanıcıya consequence'ı söyleyip Studio'ya yönlendirin), 'changes' son production dağıtımından beri değişenleri iki kovada (GET changes-since-production: live — dağıtımla telefonlara gider, mağaza gerekmez; store — izin/kimlik/görsel gibi yeni mağaza sürümü isteyenler). shell HER ÇAĞRIDA döner (GET mobile-store/shell, tarama yapmaz): yayındaki kabuk paketinin sürümü ve yetenek tablosu (shell.capabilities: ad → { carried, since }; ör. backStep, keyboardInsets, flowDrain — permissions.shell.capabilities aynı tablonun backend'deki ham dizi biçimidir) — bir özelliğin telefonda çalışıp çalışmadığını ve bir geçici çözümün kaldırılıp kaldırılamayacağını buradan okuyun (platform_guide('mobile-publish') §Kabuk sürümleri); okunamazsa shellError döner, çağrı düşmez. Gizli DEĞER hiçbir bölümde yoktur — yalnız tür, maskeli id ve meta (ekip, son kullanma); bir bölüm okunamazsa (uç hatası) <bölüm>Error döner, çağrı düşmez. Gizli bilgiyi EKLEMEK/DEĞİŞTİRMEK MCP'den yapılmaz — Studio › Yayın › Hesaplar.",
|
|
392
392
|
inputSchema: {
|
|
393
393
|
applicationId,
|
|
394
394
|
manifest: z.boolean().optional().describe('true: derleme manifestinin önizlemesini de ekler'),
|
|
@@ -402,6 +402,17 @@ export function registerMobileStoreTools(server, platform, storeArt = {}) {
|
|
|
402
402
|
const store = storeSummary(unwrap(await platform.org('GET', `/mobile-store/${appId}`)), sections.has('assets'));
|
|
403
403
|
const details = await Promise.all(DETAIL_SECTIONS.filter(section => section !== 'assets' && sections.has(section)).map(section => readDetail(platform, appId, section, { ...(project ? { project } : {}), ...(environment ? { environment } : {}) })));
|
|
404
404
|
const result = Object.assign(store, ...details);
|
|
405
|
+
// g-05: kabuk tablosu yapılandırmadan okunur (GET mobile-store/shell, tarama yok); düşmesi görünümü düşürmez.
|
|
406
|
+
try {
|
|
407
|
+
const shell = shellCapabilityView(unwrap(await platform.org('GET', '/mobile-store/shell')));
|
|
408
|
+
if (shell)
|
|
409
|
+
result.shell = shell;
|
|
410
|
+
}
|
|
411
|
+
catch (error) {
|
|
412
|
+
if (!(error instanceof PlatformApiError))
|
|
413
|
+
throw error;
|
|
414
|
+
result.shellError = error.message;
|
|
415
|
+
}
|
|
405
416
|
if (!manifest)
|
|
406
417
|
return result;
|
|
407
418
|
try {
|
package/dist/tools/pages.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { guid, projectParam, environmentParam, pagingParams, asUserIdParam, readOnly, writes, updates, destructive, microOps, isBranchName, branchSlug } from './common.js';
|
|
2
|
-
import { orphanedFlowWarnings, orphanedFlowPlan, orphanedFlowMessage, boundEventOps, stripGatewayEventFields, staleSetterWarnings, lostSetterTargets, fillComponentNames, componentNameNote, normalizeComponentDefaults, normalizeSchemaOps, schemaOpValidationErrors, codePropTemplateErrors, addComponentIdErrors, missingBlocksNodes, retiredTypeNodes, assertNoRetiredNodes, validationNote, componentDefaultsNote, replacePropNote, ensurePageContainer, retargetRootAdds, rootAddsNote, flexSizeWarnings, spacingLayerWarnings, reorderSchemaBlocks, reorderNote, nestedRepeaterNodes, nestedRepeaterMessage, stringStyleNodes, stringStyleWarning, inertFlexPropNodes, inertFlexPropWarning, FLEX_LAYOUT_PROP_NAMES, resetPageContainerPosition, pageContainerPositionWarning, verticalGapWarnings, flexIndexOrder, flexIndexOrderNote, withNormalization, parseVariableEntries, PAGE_CONTAINER_ELEMENT_ID, screenEventsNeedPlatform } from '../knowledge/normalize.js';
|
|
2
|
+
import { orphanedFlowWarnings, orphanedFlowPlan, orphanedFlowMessage, boundEventOps, flowIdEventOps, stripGatewayEventFields, staleSetterWarnings, lostSetterTargets, fillComponentNames, componentNameNote, normalizeComponentDefaults, normalizeSchemaOps, schemaOpValidationErrors, codePropTemplateErrors, addComponentIdErrors, missingBlocksNodes, retiredTypeNodes, assertNoRetiredNodes, validationNote, componentDefaultsNote, replacePropNote, ensurePageContainer, retargetRootAdds, rootAddsNote, flexSizeWarnings, spacingLayerWarnings, reorderSchemaBlocks, reorderNote, nestedRepeaterNodes, nestedRepeaterMessage, stringStyleNodes, stringStyleWarning, inertFlexPropNodes, inertFlexPropWarning, FLEX_LAYOUT_PROP_NAMES, resetPageContainerPosition, pageContainerPositionWarning, verticalGapWarnings, flexIndexOrder, flexIndexOrderNote, withNormalization, parseVariableEntries, PAGE_CONTAINER_ELEMENT_ID, screenEventsNeedPlatform } from '../knowledge/normalize.js';
|
|
3
3
|
import { functionSignature } from '../knowledge/functionGuards.js';
|
|
4
4
|
import { attachFindings } from '../knowledge/policy.js';
|
|
5
5
|
import { fetchHost, pageUpdateBody, schemaParam, eventsParam, unwrap, modeParam, summarize, findNode, flowPath, countSchemaNodes, missingElementIds, readFullSchema } from './screens.js';
|
|
@@ -7,7 +7,7 @@ import { fetchComponentEventNames } from './components.js';
|
|
|
7
7
|
import { assertScreenWriteGuards, screenCatalogNotes, assertScreenCatalog, unknownPropOps } from './screenWrite.js';
|
|
8
8
|
import { opsParam, writeViewParam, outlineWriteResponse, unknownEventOps, collectTypes, simulateLayoutOps, applySkeletonOps, reshapesSkeleton, planEventRemovals, callerSlots, planEmulatedOps, recordsWithEventRemovals, serverOpsInOrder, rejectedOpsMessage } from './schemaOps.js';
|
|
9
9
|
import { formBindingWarnings, formSubmitButtonWarnings } from '../knowledge/coverage.js';
|
|
10
|
-
import { emptyWriteDiff, schemaOpDrops, unwrapProps, withWriteDiff, writeDiff } from '../platform/writeDiff.js';
|
|
10
|
+
import { emptyWriteDiff, pageWriteDrops, schemaOpDrops, unwrapProps, withWriteDiff, writeDiff } from '../platform/writeDiff.js';
|
|
11
11
|
import { PlatformApiError } from '../platform/errors.js';
|
|
12
12
|
import { existingMatch, ifExistsParam, listAll, withOutcome } from '../platform/ifExists.js';
|
|
13
13
|
import { handle, toolResult, toolError } from '../platform/responses.js';
|
|
@@ -325,7 +325,7 @@ export function registerPageTools(server, platform, injectedScreenshotter, injec
|
|
|
325
325
|
dx: z.number().optional().describe("Yalnız 'swipe': jestin yatay kayması (CSS pikseli; sola kaydırma eksi). to ile birlikte verilmez"),
|
|
326
326
|
dy: z.number().optional().describe("Yalnız 'swipe': jestin dikey kayması (CSS pikseli; yukarı eksi). to ile birlikte verilmez"),
|
|
327
327
|
blur: z.boolean().optional().describe("Yalnız 'set': false verilirse odak alanda KALIR (telefonda klavye açıkken yazılan hâl) — input+change atılır, blur/focusout atılmaz; odak kaybına bağlı akış ('changed') bu adımda KOŞMAZ. Varsayılan true: gerçek kullanıcı gibi yazıp alandan çıkar")
|
|
328
|
-
})).max(20).optional().describe("Kare ALINMADAN ÖNCE sayfada koşturulacak etkileşim adımları: [{action:'set', target:'flt_month', value:'2022-10'}, {action:'click', target:'btn_sign_in'}, {action:'tab', target:'tabs_settings', value:'iot'}, {action:'select', target:'dd_city', value:'34'}, {action:'submit', target:'frm_note'}, {action:'drag', target:'kb_board__card-42', to:'kb_board__col-Assigned'}]. 'tab' SEKME SEÇER: sekme grubunun köküne 'click' göndermek DOM sırasındaki İLK sekmeye gider ve ikinci sekme hiç açılamıyordu — tablist köküne click artık reddedilir ve bu fiile yönlendirir. Eşleme önce '<elementId>__tab-<sekme anahtarı>' kimliğiyle, o yoksa görünen metinle yapılır; actionsReport.matchedBy hangisi olduğunu, actionsReport.activeTab adımdan SONRA hangi sekmenin açık olduğunu söyler. Etkin olmayan sekmenin içeriği DOM'da YOKTUR: o sekmedeki bir düğüme yazmadan önce 'tab' adımı gerekir. 'select' açılır kutudan seçer — kutu native alan değildir, değeri yazmak hiçbir şey yapmaz, seçeneğe tıklanması gerekir; liste 3 sn'ye kadar YOKLANIR (sorgudan beslenen kutuda seçenekler geç basılır) ve actionsReport.optionsSeen + waitedMs neyin görüldüğünü söyler. Autocomplete'te 'set' metni alana yazar, 'select' metni yazıp öneri satırını GÖRÜNEN METİNLE seçer (value ile değil; eşleşme yoksa yazılan metin alanda kalır ve note bunu söyler). 'submit' formu gönder butonu OLMADAN gönderir (form.requestSubmit); actionsReport.submitVia hangi yolun kullanıldığını yazar. 'drag' HTML5 sürükle-bırak olaylarını (dragstart→dragover→drop→dragend) hedefin merkezinde atar; kanban cardMove ve takvim eventMove bununla tetiklenir. 'swipe' DOKUNMATİK jesttir (mobil): hedefin merkezine basar, 12 adımda 16 ms arayla taşır ve bırakır (pointer + mouse olayları; sıfır kayma reddedilir — dokunuş 'click'tir) — tutamaçtan sıralama {action:'swipe', target:'lst__row-2__handle', to:'lst__row-0'}, satır kaydırma eylemi {action:'swipe', target:'lst__row-1', dx:-160, dy:0}; 'drag' web'in HTML5 sürüklemesi, 'swipe' dokunmatik jest. 'network' bağlamın AĞINI keser ya da açar ({action:'network', value:'offline'} → ağsız yaz/kaydet → {action:'network', value:'online'}): sayfanın tamamı etkilenir, hedef almaz; actionsReport.online sayfanın gördüğü navigator.onLine'dır, ağ açılınca koşan eşitleme akışları flowRuns'a yazılır. Çağrının sonunda ağ yeniden açılır; ağsız davranışı iki çağrı arasında sürdürmek için session ile depo taşınır, ağ taşınmaz. 'set' + blur:false odağı alanda bırakır (telefonda klavye açıkken). 'set' native setter kullanır ve input+change olaylarını bubbles ile atar (düz DOM ataması framework'ün duyduğu olayı üretmez); ardından kök düğümün runtime prop'larını yeniden okur: actionsReport.propWritten değerin prop'a ULAŞIP ulaşmadığıdır ve false ise applied da false döner — DOM'a yazılmış ama bileşenin duymadığı değer 'uygulandı' sayılmaz (G-14). Her adımdan sonra yerleşme beklenir ve yanıttaki actionsReport hedefin bulunup bulunmadığını ve adımdan sonra KAÇ AĞ İSTEĞİ çıktığını söyler — 'bağ kuruldu' ile 'çalışıyor' arasındaki farkı ancak bu ayırır (G-24)."),
|
|
328
|
+
})).max(20).optional().describe("Kare ALINMADAN ÖNCE sayfada koşturulacak etkileşim adımları: [{action:'set', target:'flt_month', value:'2022-10'}, {action:'click', target:'btn_sign_in'}, {action:'tab', target:'tabs_settings', value:'iot'}, {action:'select', target:'dd_city', value:'34'}, {action:'submit', target:'frm_note'}, {action:'drag', target:'kb_board__card-42', to:'kb_board__col-Assigned'}]. 'click' hedefi seçer (düğümün kendisi basılabilirse o → <elementId>__press → içteki ilk düğme) ve hedefin görünen bir noktasına GERÇEK fare olayıyla basar, telefondaki parmak gibi: önce ortaya, ortada hedefin içindeki başka basılabilir öğe (kabın düğmesi, çipin kaldır'ı) varsa kutunun başka bir noktasına; görünmeyen hedef önce görünüme kaydırılır. Hiçbir noktası hedefin değilse eylem UYGULANMAZ: actionsReport.coveredBy örten öğenin elementId'sidir (şema düğümü değilse rolü ve metni) — telefonda da dokunuşu o alır. 'tab' SEKME SEÇER: sekme grubunun köküne 'click' göndermek DOM sırasındaki İLK sekmeye gider ve ikinci sekme hiç açılamıyordu — tablist köküne click artık reddedilir ve bu fiile yönlendirir. Eşleme önce '<elementId>__tab-<sekme anahtarı>' kimliğiyle, o yoksa görünen metinle yapılır; actionsReport.matchedBy hangisi olduğunu, actionsReport.activeTab adımdan SONRA hangi sekmenin açık olduğunu söyler. Etkin olmayan sekmenin içeriği DOM'da YOKTUR: o sekmedeki bir düğüme yazmadan önce 'tab' adımı gerekir. 'select' açılır kutudan seçer — kutu native alan değildir, değeri yazmak hiçbir şey yapmaz, seçeneğe tıklanması gerekir; liste 3 sn'ye kadar YOKLANIR (sorgudan beslenen kutuda seçenekler geç basılır) ve actionsReport.optionsSeen + waitedMs neyin görüldüğünü söyler. Autocomplete'te 'set' metni alana yazar, 'select' metni yazıp öneri satırını GÖRÜNEN METİNLE seçer (value ile değil; eşleşme yoksa yazılan metin alanda kalır ve note bunu söyler). 'submit' formu gönder butonu OLMADAN gönderir (form.requestSubmit); actionsReport.submitVia hangi yolun kullanıldığını yazar. 'drag' HTML5 sürükle-bırak olaylarını (dragstart→dragover→drop→dragend) hedefin merkezinde atar; kanban cardMove ve takvim eventMove bununla tetiklenir. 'swipe' DOKUNMATİK jesttir (mobil): hedefin merkezine basar, 12 adımda 16 ms arayla taşır ve bırakır (pointer + mouse olayları; sıfır kayma reddedilir — dokunuş 'click'tir) — tutamaçtan sıralama {action:'swipe', target:'lst__row-2__handle', to:'lst__row-0'}, satır kaydırma eylemi {action:'swipe', target:'lst__row-1', dx:-160, dy:0}; 'drag' web'in HTML5 sürüklemesi, 'swipe' dokunmatik jest. 'network' bağlamın AĞINI keser ya da açar ({action:'network', value:'offline'} → ağsız yaz/kaydet → {action:'network', value:'online'}): sayfanın tamamı etkilenir, hedef almaz; actionsReport.online sayfanın gördüğü navigator.onLine'dır, ağ açılınca koşan eşitleme akışları flowRuns'a yazılır. Çağrının sonunda ağ yeniden açılır; ağsız davranışı iki çağrı arasında sürdürmek için session ile depo taşınır, ağ taşınmaz. 'set' + blur:false odağı alanda bırakır (telefonda klavye açıkken). 'set' native setter kullanır ve input+change olaylarını bubbles ile atar (düz DOM ataması framework'ün duyduğu olayı üretmez); ardından kök düğümün runtime prop'larını yeniden okur: actionsReport.propWritten değerin prop'a ULAŞIP ulaşmadığıdır ve false ise applied da false döner — DOM'a yazılmış ama bileşenin duymadığı değer 'uygulandı' sayılmaz (G-14). Her adımdan sonra yerleşme beklenir ve yanıttaki actionsReport hedefin bulunup bulunmadığını ve adımdan sonra KAÇ AĞ İSTEĞİ çıktığını söyler — 'bağ kuruldu' ile 'çalışıyor' arasındaki farkı ancak bu ayırır (G-24)."),
|
|
329
329
|
session: z.string().regex(/^[a-z0-9-]{1,40}$/).optional().describe("Çağrılar arası tarayıcı oturumu (kısa ad, [a-z0-9-]{1,40}): verilirse çekim sonunda çerezler, localStorage ve IndexedDB gateway BELLEĞİNDE saklanır ve aynı adla gelen sonraki çekim o depoyla açılır — kalıcı state.app ve çevrimdışı servis kuyruğu ikinci çağrıda okunur. Yanıttaki sessionRestored önceki depo yüklendi mi söyler (ilk çağrıda false). Kimlik ve adres anahtara dahildir: aynı adla farklı kullanıcı (jetonun sub'ı; yenilenen jeton aynı oturumdur), asUserId, cookies, uygulama ya da ortam ayrı oturumdur. Depo okunamazsa oturum silinir ve yanıt sessionSaved: false der. Ömür 30 dk, en çok 8 oturum (en eskisi düşer); gateway yeniden başlayınca silinir. sessionStorage (güvenli depo) taşınmaz. Ağ durumu taşınmaz: her çekim ağ AÇIK başlar. Verilmezse her çekim boş depoyla başlar (bugünkü yalıtım)."),
|
|
330
330
|
language: z.string().min(1).optional().describe("Kareyi BU DİLDE çeker: adrese '?lang=<kod>' eklenir ve runtime dili oradan seçer (öncelik: ?lang → tarayıcıda kayıtlı seçim → tarayıcı dilleri → projenin varsayılanı). Seçim hem çeviriye bağlı prop'ları hem çok dilli alanların satır değerlerini kapsar, yani ikinci dilin karesi için sayfaya dil değiştirici KOYMAK GEREKMEZ — referans tasarımı bozulmadan ikinci dil kanıtlanır. ISO 639-1 iki harfli kod ('tr', 'en'); bölgeli kod ('tr-TR') indirgenir ve indirgeme yanıtta bildirilir. Kod projenin languages listesinde yoksa çağrı REDDEDİLİR: runtime tanımadığı kodu yok sayıp kareyi varsayılan dilde çizerdi ve 'çevrildi' sanılan yanlış bir kare raporlanırdı. MOBİL uygulamada da geçerlidir (/preview aynı ?lang'i okur); cihazın kendisinde ?lang yoktur (platform_guide('mobile') §Çok dil)."),
|
|
331
331
|
colorScheme: z.enum(['light', 'dark']).optional().describe("Kareyi açık ya da koyu temada çeker. Ziyaretçi tema tercihi çerezde değil localStorage'da yaşar ve tema store'u onu sayfa açılırken bir kez okur; gateway tercihi sayfa script'lerinden ÖNCE eker, bu yüzden sayfada 'theme-toggle' komponenti OLMASA DA koyu tema karesi alınabilir. Verilmezse projenin kendi modu çizilir."),
|
|
@@ -720,7 +720,7 @@ export function registerPageTools(server, platform, injectedScreenshotter, injec
|
|
|
720
720
|
});
|
|
721
721
|
server.registerTool('page_schema_get', {
|
|
722
722
|
title: 'Sayfa şemasını oku',
|
|
723
|
-
description: "Sayfanın komponent şemasını okur. Yanıttaki version değerini page_schema_patch'e baseVersion olarak verin: sayfa o okumadan beri değiştiyse yama 409 ile reddedilir ve hiçbir op uygulanmaz. 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 — her parça items[] ({ parentElementId, index, node }) taşır, kalem parentElementId düğümünün children[index]'inin yerine geçer (parentElementId yoksa kök dizinin [index]'i), alt ağacı sığmayan düğüm kabuk gelir (children: [{ elementId, deferred: true }]) ve sonraki parçalar onu doldurur; nextCursor bitene kadar sürün. allowTruncated:true kırpılmış gövdeyi açıkça verir (truncated: true; hangi düğümlerin düştüğü söylenmez) — o gövdeyi page_update'e geri yazmayın (PAGE-043).",
|
|
723
|
+
description: "Sayfanın komponent şemasını okur. Yanıttaki version değerini page_schema_patch'e baseVersion olarak verin: sayfa o okumadan beri değiştiyse yama 409 ile reddedilir ve hiçbir op uygulanmaz. YANIT ŞEKLİ kipe göre değişir ve schemaShape alanı bunu söyler: summary ve full'da schema DİZİDİR (schemaShape: 'list'), subtree'de schema TEK DÜĞÜMDÜR (schemaShape: 'node', dizi değil). 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 — her parça items[] ({ parentElementId, index, node }) taşır, kalem parentElementId düğümünün children[index]'inin yerine geçer (parentElementId yoksa kök dizinin [index]'i), alt ağacı sığmayan düğüm kabuk gelir (children: [{ elementId, deferred: true }]) ve sonraki parçalar onu doldurur; nextCursor bitene kadar sürün. allowTruncated:true kırpılmış gövdeyi açıkça verir (truncated: true; hangi düğümlerin düştüğü söylenmez) — o gövdeyi page_update'e geri yazmayın (PAGE-043).",
|
|
724
724
|
inputSchema: {
|
|
725
725
|
project: projectParam,
|
|
726
726
|
environment: environmentParam,
|
|
@@ -747,7 +747,7 @@ export function registerPageTools(server, platform, injectedScreenshotter, injec
|
|
|
747
747
|
field: 'schema',
|
|
748
748
|
nodes: page.schema ?? [],
|
|
749
749
|
versionKey: String(version ?? ''),
|
|
750
|
-
base: { version, updatedAt, ...typeField },
|
|
750
|
+
base: { version, updatedAt, ...typeField, schemaShape: 'list' },
|
|
751
751
|
cursor,
|
|
752
752
|
allowTruncated,
|
|
753
753
|
maxResponseBytes: config.maxResponseBytes,
|
|
@@ -760,9 +760,9 @@ export function registerPageTools(server, platform, injectedScreenshotter, injec
|
|
|
760
760
|
const subtree = findNode(page.schema, componentId);
|
|
761
761
|
if (!subtree)
|
|
762
762
|
throw new PlatformApiError(`Sayfada '${componentId}' id'li bir komponent yok; page_schema_get'i summary kipinde çağırıp hedefi doğrulayın.`);
|
|
763
|
-
return { version, updatedAt, ...typeField, nodeCount: countSchemaNodes([subtree]), schema: subtree };
|
|
763
|
+
return { version, updatedAt, ...typeField, schemaShape: 'node', nodeCount: countSchemaNodes([subtree]), schema: subtree };
|
|
764
764
|
}
|
|
765
|
-
return { version, updatedAt, ...typeField, schema: (page.schema ?? []).map(node => summarize(node)) };
|
|
765
|
+
return { version, updatedAt, ...typeField, schemaShape: 'list', schema: (page.schema ?? []).map(node => summarize(node)) };
|
|
766
766
|
}));
|
|
767
767
|
server.registerTool('page_state', {
|
|
768
768
|
title: 'Sayfa durumunu getir',
|
|
@@ -850,6 +850,8 @@ export function registerPageTools(server, platform, injectedScreenshotter, injec
|
|
|
850
850
|
for (const issue of connectDataOpIssues(entry, connectedProps))
|
|
851
851
|
reject(issue.code, entry, [issue.message]);
|
|
852
852
|
}
|
|
853
|
+
for (const entry of opRecords)
|
|
854
|
+
reject('PAGE-052', entry, flowIdEventOps([entry]));
|
|
853
855
|
if (opRecords.some(entry => entry.op === 'set_event' || entry.op === 'add_event')) {
|
|
854
856
|
const typesByElementId = new Map();
|
|
855
857
|
try {
|
|
@@ -1130,7 +1132,11 @@ export function registerPageTools(server, platform, injectedScreenshotter, injec
|
|
|
1130
1132
|
// G-23 micro-op ayağı: yama 200 döner ve `appliedPatch` op'un ŞEMAYA yazıldığını söyler, ama
|
|
1131
1133
|
// alanın gerçekten KAYDEDİLDİĞİNİ söylemez. Yamadan sonra şemayı bir kez okuyup op'un yazmak
|
|
1132
1134
|
// istediğiyle kayıtlı olanı karşılaştırıyoruz. Okuma başarısız olursa yama yine geçerlidir.
|
|
1133
|
-
|
|
1135
|
+
// Olay op'ları platforma giden SIRALI listeden alınır: `remove_event` yalnız orada durur ve önceki bağı geçersiz kılar.
|
|
1136
|
+
const contentOps = [
|
|
1137
|
+
...sentOps.filter(entry => entry.op === 'add_component' || entry.op === 'replace_prop'),
|
|
1138
|
+
...serverOps.filter(entry => ['set_event', 'add_event', 'remove_event'].includes(String(entry.op)))
|
|
1139
|
+
];
|
|
1134
1140
|
let opDrops = emptyWriteDiff();
|
|
1135
1141
|
if (contentOps.length > 0) {
|
|
1136
1142
|
try {
|
|
@@ -1244,6 +1250,10 @@ export function registerPageTools(server, platform, injectedScreenshotter, injec
|
|
|
1244
1250
|
const changed = Object.keys(overrides).filter(key => JSON.stringify(before[key]) !== JSON.stringify(overrides[key]));
|
|
1245
1251
|
const written = await platform.tenant('PUT', project, `/pages/${applicationId}/${pageId}`, { environment, body });
|
|
1246
1252
|
rememberVersion(project, environment, pageId, written?.data?.version);
|
|
1253
|
+
const sentCounts = { events: body.events, schemaNodes: countSchemaNodes(body.schema) };
|
|
1254
|
+
const savedCounts = Array.isArray(written?.data?.events) && Array.isArray(written?.data?.schema)
|
|
1255
|
+
? { events: written.data.events, schemaNodes: countSchemaNodes(written.data.schema) }
|
|
1256
|
+
: undefined;
|
|
1247
1257
|
const updateWarnings = schema === undefined
|
|
1248
1258
|
? []
|
|
1249
1259
|
: [...gaps, ...stringStyleWarning(stringStyles), ...(resolved.profile.layout.modes.has('grid') ? inertFlexPropWarning(inertFlexPropNodes(container.schema)) : []), ...flexSizeWarnings(container.schema, 'PAGE-017 (uyarı)', 'PAGE-018 (uyarı)'), ...spacingLayerWarnings(container.schema, 'PAGE-029 (uyarı)', 'PAGE-030 (uyarı)'), ...profileWarnings,
|
|
@@ -1252,12 +1262,12 @@ export function registerPageTools(server, platform, injectedScreenshotter, injec
|
|
|
1252
1262
|
id: pageId,
|
|
1253
1263
|
outcome: 'updated',
|
|
1254
1264
|
changed,
|
|
1255
|
-
preserved: { schemaNodes:
|
|
1265
|
+
preserved: { schemaNodes: (savedCounts ?? sentCounts).schemaNodes, events: (savedCounts ?? sentCounts).events.length },
|
|
1256
1266
|
...applicationTypeField(resolved)
|
|
1257
1267
|
};
|
|
1258
1268
|
if (updateWarnings.length > 0)
|
|
1259
1269
|
summary.layoutWarnings = updateWarnings;
|
|
1260
|
-
return attachFindings(withNormalization(summary, schema === undefined ? [] : [...container.fixes, ...skeletonNotes, ...componentNameNote(named), ...reset, ...componentDefaultsNote(fixed), ...catalogNotes, ...reorderNote(moved), ...mobileSpacingNote(spacing)]), textFindings);
|
|
1270
|
+
return attachFindings(withWriteDiff(withNormalization(summary, schema === undefined ? [] : [...container.fixes, ...skeletonNotes, ...componentNameNote(named), ...reset, ...componentDefaultsNote(fixed), ...catalogNotes, ...reorderNote(moved), ...mobileSpacingNote(spacing)]), pageWriteDrops(sentCounts, savedCounts)), textFindings);
|
|
1261
1271
|
}
|
|
1262
1272
|
// PAGE-051 yalnız gerçekten OLUŞTURAN dalda: `ifExists: 'update'` var olan sayfada page_update gibi
|
|
1263
1273
|
// davranır ve kayıtlı emekli düğüm yazılabilir kalmalıdır.
|
|
@@ -1391,17 +1401,22 @@ export function registerPageTools(server, platform, injectedScreenshotter, injec
|
|
|
1391
1401
|
throw await versionConflict(error, project, environment, applicationId, pageId, String(body.baseVersion ?? ''), "page_schema_get ile yeniden okuyun ve şema değişikliğini page_schema_patch ile uygulayın — yalnız dokunduğunuz yer yazılır, aradaki değişiklik korunur. Üstbilgi (ad, slug, layout, roller) için okuduktan sonra page_update'i yeniden çağırın; aradaki değişikliği bilerek ezmek için force:true.");
|
|
1392
1402
|
});
|
|
1393
1403
|
rememberVersion(project, environment, pageId, written?.data?.version);
|
|
1404
|
+
// PAGE-053: sayılar PUT yanıtındaki KAYITLI sayfadan; gönderilen gövde yalnız yanıt kaydı taşımazsa kullanılır.
|
|
1405
|
+
const sentCounts = { events: body.events, schemaNodes: countSchemaNodes(body.schema) };
|
|
1406
|
+
const savedCounts = Array.isArray(written?.data?.events) && Array.isArray(written?.data?.schema)
|
|
1407
|
+
? { events: written.data.events, schemaNodes: countSchemaNodes(written.data.schema) }
|
|
1408
|
+
: undefined;
|
|
1394
1409
|
const summary = {
|
|
1395
1410
|
id: pageId,
|
|
1396
1411
|
changed: Object.keys(overrides),
|
|
1397
|
-
preserved: { schemaNodes:
|
|
1412
|
+
preserved: { schemaNodes: (savedCounts ?? sentCounts).schemaNodes, events: (savedCounts ?? sentCounts).events.length }
|
|
1398
1413
|
};
|
|
1399
1414
|
if (force === true)
|
|
1400
1415
|
summary.forced = true;
|
|
1401
1416
|
if (gaps.length > 0 || skeletonWarnings.length > 0)
|
|
1402
1417
|
summary.layoutWarnings = [...gaps, ...skeletonWarnings];
|
|
1403
1418
|
const findings = [...await legacyRoleListFindings(platform, project, environment, overrides, 'page'), ...textFindings];
|
|
1404
|
-
return attachFindings(withNormalization(summary, [...componentNameNote(named), ...reset, ...componentDefaultsNote(fixed), ...catalogNotes, ...reorderNote(moved)]), findings);
|
|
1419
|
+
return attachFindings(withWriteDiff(withNormalization(summary, [...componentNameNote(named), ...reset, ...componentDefaultsNote(fixed), ...catalogNotes, ...reorderNote(moved)]), pageWriteDrops(sentCounts, savedCounts)), findings);
|
|
1405
1420
|
}));
|
|
1406
1421
|
server.registerTool('page_clone', {
|
|
1407
1422
|
title: 'Sayfa klonla',
|