@appaflytech/zeyrek-ai-mcp 0.0.51 → 0.0.53
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 +3 -1
- package/dist/knowledge/componentEvents.js +13 -1
- package/dist/knowledge/expressionPrelude.js +3 -0
- package/dist/knowledge/mobileComponentCatalog.js +4 -1
- package/dist/knowledge/mobileStore.js +258 -1
- package/dist/knowledge/normalize.js +4 -3
- package/dist/knowledge/platform/mobile.js +1 -1
- package/dist/knowledge/platform/mobileRules.js +79 -0
- package/dist/knowledge/rules.js +199 -6
- package/dist/knowledge/storeImport.js +69 -0
- package/dist/platform/argumentAliases.js +13 -4
- package/dist/platform/bodyFile.js +195 -38
- package/dist/platform/capture/actions.js +24 -5
- package/dist/platform/capture/capture.js +80 -11
- package/dist/platform/capture/persistent-browser.js +23 -1
- package/dist/platform/capture/readiness.js +32 -1
- package/dist/platform/errors.js +45 -5
- package/dist/platform/http.js +17 -9
- package/dist/platform/responses.js +7 -1
- package/dist/platform/secrets.js +8 -0
- package/dist/platform/token.js +32 -21
- package/dist/resources/examples/flow-credit-spend.json +233 -0
- package/dist/resources/examples/flow-purchase-paywall.json +662 -0
- package/dist/resources/examples/mobile-offline-list.json +1 -1
- package/dist/resources/examples/mobile-offline-queue.json +1 -1
- package/dist/resources/examples/mobile-offline-seamless.json +12 -3
- package/dist/resources/examples/store-import-mapping.json +25 -0
- package/dist/resources/guides/deployment.md +9 -0
- package/dist/resources/guides/expressions.md +1 -0
- package/dist/resources/guides/microservice.md +13 -1
- package/dist/resources/guides/mobile-publish.md +67 -2
- package/dist/resources/guides/mobile-steps.md +91 -3
- package/dist/resources/guides/mobile.md +38 -8
- package/dist/resources/guides/page.md +39 -8
- package/dist/resources/guides/translation.md +3 -1
- package/dist/resources/guides/workflow.md +30 -0
- package/dist/resources/schema-fingerprint.json +2 -2
- package/dist/resources/schemas/CreateWorkflowCommand.json +6 -1
- package/dist/resources/schemas/GlobalFunctionUpdateResultDto.json +4 -0
- package/dist/resources/schemas/PreviewWorkflowCommand.json +6 -1
- package/dist/resources/schemas/StepDto.json +6 -1
- package/dist/resources/schemas/UpdateWorkflowCommand.json +6 -1
- package/dist/server.js +4 -0
- package/dist/tools/applications.js +7 -2
- package/dist/tools/deployments.js +1 -1
- package/dist/tools/designSchemaPatch.js +7 -2
- package/dist/tools/environments.js +40 -0
- package/dist/tools/flows.js +102 -9
- package/dist/tools/mobile.js +56 -0
- package/dist/tools/mobilePurchases.js +236 -0
- package/dist/tools/mobileStore.js +2 -2
- package/dist/tools/mobileStoreImport.js +173 -0
- package/dist/tools/pages.js +45 -11
- package/dist/tools/platformInfo.js +4 -4
- package/dist/tools/screenWrite.js +107 -1
- package/dist/tools/variables.js +1 -1
- package/package.json +1 -1
- package/dist/resources/guides/mobile-flow.md +0 -64
|
@@ -12,8 +12,8 @@
|
|
|
12
12
|
"list": "Liste = sunucunun yanıtı (ağsızken son okunan) + Page.offline.list()'teki status === 'pending' kayıtlar (service = yazma ucunun slug'ı, parameters'tan alan). Reddedilen kayıt (status 'rejected') listeye katılmaz. Ağ yokluğu hata metni göstermez: tx_liste_hata yalnız Steps.<okuma>.error && !error.network iken görünür.",
|
|
13
13
|
"lock": "Yazan düğme istek süresince isLoading + isDisabled. Kilitsiz düğmede hızlı ikinci dokunuş YENİ bir istek ve yeni bir Idempotency-Key'dir — sunucu ikisini de uygular (ölçüldü: 1,6 sn'de üç dokunuş, üç kayıt). Kilit her dalın sonunda (always, queued, error) açılır.",
|
|
14
14
|
"landing": "Açılış sayfası korumasız giriş ekranıdır; kalıcı state.app.oturumVar (storage: device) true ise mount akışı listeye replace ile geçer. Giriş işareti yazar, çıkış siler. Geçici çare: korumalı açılış sayfası ağsız ilk açılışta çizilemiyor (kök ADR 0024 taslak) ve kabuk bilgisi yokken middleware reddi girişe yönlenmiyor (zeyrek-frontend ARC-342). Mağaza derlemesinde açılış manifestin landingPageSlug'ıdır.",
|
|
15
|
-
"refresh": "Liste scroll-view refreshable + refresh olayı aynı yükleme akışına
|
|
16
|
-
"preview": "Studio önizlemesi, /dev ve page_screenshot önbellek ve kuyruk KULLANMAZ: page_screenshot ağı
|
|
15
|
+
"refresh": "Liste scroll-view refreshable + refresh olayı ve sayfanın offlineQueueChange olayı aynı yükleme akışına bağlı (pageUpdateArgs, kök ADR 0029): kuyruğa giren kayıt listeye hemen katılır, bağlantı dönünce gönderilen kayıt kuyruk değiştiği için akış yeniden koşup kaydı sunucudan getirir — listeden düşmez. Bağlantı dönüşünün kendisi açık ekranın mount akışını koşturmaz (kök ADR 0025 taslak); çekerek yenileme yine çalışır.",
|
|
16
|
+
"preview": "Studio önizlemesi, /dev ve page_screenshot önbellek ve kuyruk KULLANMAZ: page_screenshot network eylemiyle ağı keser (SCR-020) ama okuma da yazma da hata yolundan döner; önbellekli ve kuyruklu davranış yalnız cihazda (uçak kipi) görülür. Ekran açıkken değişen akış ekran yeniden açılınca etkindir.",
|
|
17
17
|
"notWhere": "Kuyruktaki kayda bağlı ikinci işlem (dönen kimlikle yönlendirme) çevrimdışıyken koşamaz; o akışlara queued dalı vermeyin. Giriş ve hiç açılmamış ekran ağ ister — fark yalnız orada görünür."
|
|
18
18
|
},
|
|
19
19
|
"applicationUpdateArgs": {
|
|
@@ -1514,7 +1514,16 @@
|
|
|
1514
1514
|
"event": "click",
|
|
1515
1515
|
"eventValue": "<cikis akış id>"
|
|
1516
1516
|
}
|
|
1517
|
-
]
|
|
1517
|
+
],
|
|
1518
|
+
"pageUpdateArgs": {
|
|
1519
|
+
"_note": "Sayfa olayı page_update { events } ile yazılır (mobil sayfa olayı; listeyi kuyruk değişince yeniden kurar).",
|
|
1520
|
+
"events": [
|
|
1521
|
+
{
|
|
1522
|
+
"name": "offlineQueueChange",
|
|
1523
|
+
"value": "<notlar-yukle akış id>"
|
|
1524
|
+
}
|
|
1525
|
+
]
|
|
1526
|
+
}
|
|
1518
1527
|
}
|
|
1519
1528
|
]
|
|
1520
1529
|
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"_meta": {
|
|
3
|
+
"synthetic": true,
|
|
4
|
+
"note": "Mağazadan taşıma: içe aktarma raporu okunduktan sonra Zeyrek'e yazılacak seçimler (temsilî Anatoli kimlikleri; plans/mobil-magazadan-tasima §Ürün eşleme kuralları)."
|
|
5
|
+
},
|
|
6
|
+
"tool": "mobile_store_import_mapping",
|
|
7
|
+
"arguments": {
|
|
8
|
+
"applicationId": "01a13ea2-c7ed-70ed-9046-6e4eea34f216",
|
|
9
|
+
"importId": "01a17000-0000-7000-8000-000000000001",
|
|
10
|
+
"mapping": {
|
|
11
|
+
"scheme": "anatoli",
|
|
12
|
+
"choice.listing.tr.title": "apple",
|
|
13
|
+
"choice.listing.en.description": "custom",
|
|
14
|
+
"custom.listing.en.description": "Discover Anatolia, offline.",
|
|
15
|
+
"skip.language.ar": "true",
|
|
16
|
+
"product.0.key": "premium_yillik",
|
|
17
|
+
"product.0.entitlement": "premium",
|
|
18
|
+
"product.1.key": "cevrimdisi_paket",
|
|
19
|
+
"product.1.entitlement": "offline",
|
|
20
|
+
"declaration.appPrivacy": "true",
|
|
21
|
+
"declaration.dataSafety": "true"
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
"next": "mobile_store_import_apply { applicationId, importId } → mobile_product_list → mobile_store_notification_forwarding { action: 'switch', confirm: true } (kullanıcı onayıyla) → mobile_purchase_import { dryRun: true }"
|
|
25
|
+
}
|
|
@@ -97,6 +97,15 @@ değişimidir: adresin gösterdiği **dosya kopyalanmaz**, production'da adres *
|
|
|
97
97
|
production'a taşımak için onu dosya tipli prop'a (`ref`) ya da taşınan bir satırın dosya sütununa
|
|
98
98
|
bağlayın.
|
|
99
99
|
|
|
100
|
+
## Dağıtımdan sonra doğrula
|
|
101
|
+
|
|
102
|
+
`deployment_status` bitti dedikten sonra `environment_diff({ projectId })` (varsayılan development → production)
|
|
103
|
+
iki ortamın tanım farkını tek çağrıda verir: `equal: true` ise özetlenen modüllerde fark yok. Fark tanım özetinden
|
|
104
|
+
gelir, gövdeyi elle karşılaştırmaya gerek yok. Dağıtım kimlikleri korur; ama development'ta yeniden yazılan bir akış
|
|
105
|
+
(adım parametrelerinin kimlikleri yenilenir) içeriği aynı olsa da `changed` görünebilir.
|
|
106
|
+
`notCovered`'daki modüller (tablolar, bileşenler, erişim, süreçler) ve tablo satırları bu karşılaştırmanın
|
|
107
|
+
dışındadır; satır için §Tablo satırları. `digestNote` varsa özet bayat olabilir.
|
|
108
|
+
|
|
100
109
|
## Production'a doğrudan yazılmaz
|
|
101
110
|
|
|
102
111
|
Eksik satır ya da dosya production'da elle tamamlanamaz. Production ortamında `file_upload`,
|
|
@@ -112,6 +112,7 @@ dalından devam eder.
|
|
|
112
112
|
| `resetForm(name)` | Formu sıfırlar: alanlar başlangıç değerine döner, form "gönderilmemiş" olur, doğrulama mesajları silinir. Kayıt sonrası "yeni kayıt" için alanları tek tek `null`'a çekme — gönderilmiş formda her biri "zorunlu alan" mesajı çıkarır. Ad ya da komponent değişkeni alır: `resetForm('risk_form')` / `resetForm(risk_form)`. **Yalnız UI flow kodunda** |
|
|
113
113
|
| `LocalStorage` · `Widget` · `params` | yerel veri · widget bağlamı · olay parametreleri. `LocalStorage` barındırıcının deposudur, salt okunur ve değerler METİNdir: web'de uygulamanın kökenindeki `localStorage` (Studio önizlemesinde Studio'nun kendi anahtarları da görünür), cihazda akışın `storage` deposu. `storage` adımı düz bir anahtara yazınca `LocalStorage.<anahtar>` aynı akışta yeni değeri verir ve sayfanın bağları yeniden değerlendirilir (`isSelected: {{ LocalStorage['zeyrek:theme-mode'] === 'dark' }}`). `state.page\|app.<key>`'e yazan adım `LocalStorage`'a girmez (ARC-147, 2026-09-29) |
|
|
114
114
|
| `Device` | Salt okunur cihaz durumu, barındırıcı günceller: `Device.isOnline` — ağ bağlantısı var mı (bilinmiyorsa `true`). Değişince sayfanın bütün bağları yeniden değerlendirilir; çevrimdışı bandı: `isHidden: {{ Device.isOnline }}`. Cihazda `expo-network`, web'de tarayıcının `online`/`offline` olayları. `Device.biometric = { available, enrolled, kinds }` (kök ADR 0021): `available` cihaz kullanıcıyı şu an doğrulayabilir (biyometri **ya da** cihaz parolası kurulu), `enrolled` biyometri kayıtlı, `kinds` ⊆ `face · fingerprint · iris`; barındırıcı vermezse (web, derleme taşımıyor) `{ available: false, enrolled: false, kinds: [] }`, Studio önizlemesi ve kanvas senaryosu `{ true, true, ['face'] }`. Uygulama ön plana dönünce yeniden okunur. Ağ yokken okumaların ne döndüğü (`Steps.<ad>.cache.at`, `error.network`): `platform_guide('page')` §Kural 1g-4. `Device.offline` (yalnız cihazda): `{ ready, total }` — uygulamanın kaç ekranı çevrimdışı açılmaya hazır; paket inmeden ve web/önizlemede yoktur (`Device.offline?.ready`); servis yazması kuyruğu varsa `queue: { pending, rejected }` ve `rejected` (reddedilen kayıtlar) da buradadır. `Device` adında komponent ifade bağlamından süzülür (ayrılmış ad) |
|
|
115
|
+
| `Entitlements` · `Products` · `Balances` | Uygulama içi satın almanın salt okunur durumu (kök ADR 0027), barındırıcı yazar: `Balances.<bakiye>` sayı — tüketilen ürünün (kredi, jeton) güncel bakiyesi, sunucudan gelir ve harcama/satın alma sonrası kendiliğinden güncellenir (bilinmeyen bakiye `undefined`: `{{ Balances.kredi ?? 0 }}`; iade sonrası negatif olabilir); `balance-spend` adımının `args.value`'sundaki düz ad katalogda tüketilen ürünün `balance`'ı olmalıdır (yoksa yazım `FLOW-046` ile durur). Hak tarafı: `Entitlements.<hak>` boolean — hak açık mı (bilinmeyen hak `false`; ekran ürüne değil hakka bakar: `isHidden: {{ !Entitlements.premium }}`); `Products.<anahtar> = { price, period, trial }` — mağazanın yerelleştirilmiş fiyat metni (`₺949,99`), süre ve deneme ISO 8601 (`P1Y`, `P7D`) ya da `null`; `purchase.value`'ya yazılan düz anahtar katalogda olmalıdır (yoksa yazım `FLOW-045` ile durur), seçimden gelen anahtar `{{ … }}` şablonudur ve yazımda denetlenmez. Değişince bağlar yeniden okunur (satın alma ve geri yükleme sonrası kendiliğinden). Studio önizlemesi ve oynatıcı katalogdan deneme değerleri verir; web uygulamasında ikisi de boş nesnedir. `Device`'ın altında değil, kendi adlarıyla. Tarif: `platform_guide('mobile-steps')` §Satın alma tarifi |
|
|
115
116
|
| **Ayrılmış adlar** | Bu tablodaki globallerle (ve önsözün iç adlarıyla: `payload`, `error`, `repeaterId`, `_` ile başlayanlar…) aynı `name`'i taşıyan komponent (runtime yalnız İLK `-`'yi siler: `ite-m` da `item` sayılır) ifadede kendi adıyla GÖRÜNMEZ — süzülür, uyarı loglanır (liste: `@ng-apps/flow` `EXPRESSION_PRELUDE_NAMES`). Eskiden aynı ad sayfanın BÜTÜN ifadelerini `SyntaxError`'la düşürürdü (ARC-143). Gateway artık bu adların HEPSİNİ **PAGE-009** ile sayfa/layout yazma yollarında ve `page_schema_validate`'te istek gitmeden reddeder — komponente `t`, `state`, `event`, `item`, `self`, `Page`, `Row` gibi ad verme |
|
|
116
117
|
| `t(key)` | Çeviri anahtarını çözer: aktif dil → projenin varsayılan dili → anahtarın kendisi. Boş string de "eksik" sayılır. Sözlük `machine.context.state.translations`'tan gelir; dil `language-selector` ile değişince bir sonraki çağrı yeni dili görür. **Yalnız UI flow kodunda vardır** — backend ifadelerinde karşılığı `Loc()`'tur (`platform_guide('workflow')`) | `t('home.welcome')` |
|
|
117
118
|
| `log(...)` | console (proxy'leri temizler) |
|
|
@@ -186,6 +186,13 @@ DTO'dan süzülmez (WF-026 — `platform_guide('workflow')` §Hata yolu). Sözle
|
|
|
186
186
|
`filters`'a `source: "body"` + `isRequired: false` bir id filtresi; gövdede id **varsa** eşleşen
|
|
187
187
|
satır güncellenir, **yoksa** yeni satır açılır. `isRequired: true` yaparsan "yeni kayıt" yolu
|
|
188
188
|
kapanır (id gelmeyince uç reddeder).
|
|
189
|
+
- **İstemcinin ürettiği kimlik korunur** (çevrimdışı not, telefonda üretilen UUID): upsert'te `uuid` PK'nın `eq`
|
|
190
|
+
filtresi eşleşme bulamazsa yeni satır **o kimlikle** açılır (filtre kökte ya da tek `and` grubundaysa; `or` varken ve
|
|
191
|
+
sayı kimlikte taşınmaz — zeyrek-backend incident-log #190). Sonraki eşitleme aynı satırı bulur; ayrı `client_id` kolonu gerekmez. Aynı kural
|
|
192
|
+
workflow table adımının upsert'inde de geçerlidir. `properties.generationType: 1` (ByDefault) PK'ya create'te kimlik
|
|
193
|
+
`fields`'tan da yazılabilir; `0` (Always) PK'ya yazmak `AlwaysGeneratedFieldCantBeWritten` alır
|
|
194
|
+
(`platform_guide('entity')`). Aynı anahtarla aynı anda gelen iki upsert'in ikisi de create yoluna düşebilir; ikincisi
|
|
195
|
+
PK çakışmasıyla hata alır (#191, açık) — çevrimdışı kuyruk kayıtları tek tek gönderir.
|
|
189
196
|
|
|
190
197
|
> **Upsert'te üretilen alan tuzağı.** `operation: 'upsert'` UPDATE yolunda da alan ifadelerini
|
|
191
198
|
> yeniden değerlendirir; sicil/kod gibi üretilen bir değeri `source: 'expression'` ile yazarsan
|
|
@@ -380,7 +387,12 @@ döner, hata vermez. Karmaşık erişim modelinde platform sahip koşulunu satı
|
|
|
380
387
|
AND'ler (`platform_guide('access')`); orada bu grup gereksizdir ama zararsızdır. AND/OR filtrenin
|
|
381
388
|
kendinde değil **bir öncekine bağlantısındadır**: `or` taşıyan çocuk, ilk çocuk bile olsa hedefi
|
|
382
389
|
genişletir. Kural yalnız kayıtta uygulanır; 2026-10-15'ten önce kaydedilmiş uyumsuz uç yeniden
|
|
383
|
-
kaydedilince reddedilir. Workflow table adımı bu kuralı uygulamaz
|
|
390
|
+
kaydedilince reddedilir. Workflow table adımı bu kuralı uygulamaz; ama grubu korur: `owner = X AND (id = A OR
|
|
391
|
+
client_id = A)` gibi iç içe grup update/delete/upsert'te olduğu gibi gider (2026-10-30'a dek grup düşüyor, çocukları kendi
|
|
392
|
+
bağlacıyla düz listeye katılıyordu ve sahiplik koşulu atlanabiliyordu — zeyrek-backend incident-log #189).
|
|
393
|
+
|
|
394
|
+
**Okumada VE/VEYA gruplaması serbesttir:** `get`/`count` ucunda `filterType: 'group'` + `children` ile
|
|
395
|
+
`durum = 'açık' AND (sahip = ben OR paylaşılan = true)` kurulur; tek kök kuralı yalnız yazan uçlar içindir.
|
|
384
396
|
|
|
385
397
|
### Toplu güncelleme FİLTREYLE kurulmaz
|
|
386
398
|
|
|
@@ -129,8 +129,10 @@ açılmaz, yükleme `mobile_asset_create { path: files[i].path }` ile yapılır.
|
|
|
129
129
|
|
|
130
130
|
## Hedefler
|
|
131
131
|
|
|
132
|
-
`build` (iki platform, yalnız derler
|
|
133
|
-
|
|
132
|
+
`build` (iki platform, yalnız derler; iOS'ta imzalama kimliği yokken imzasız **simülatör** `.app.zip`'i — uygulamanın
|
|
133
|
+
kendi derlemesini bilgisayarda denemenin yolu; kimlik tamsa imzalı `.ipa` çıkar ve simülatörde açılmaz, yarım kimlik
|
|
134
|
+
`secrets_missing`) · `testflight` (yalnız iOS) · `internal` (yalnız Android, Play iç test). Sayfa değişikliğini telefonda
|
|
135
|
+
ya da kendi Mac'inde görmek için derleme gerekmez: `platform_guide('mobile')` §Cihazda dene → Senin Mac'inde. Android paketi `packageType`: `aab` (varsayılan, mağaza) · `apk` (yalnız `build`; telefona doğrudan
|
|
134
136
|
kurulur, mağazaya **gitmez** — indirme bağlantısı Studio'da). Runner verilmezse backend seçer: kurumun kendi
|
|
135
137
|
Mac'i çevrimiçiyse o, değilse Zeyrek'in runner'ı; yanıtta `runnerKind` (`own` · `zeyrek`). TestFlight'ta runner yükledikten sonra iş `running` + `store_processing` kalır (`awaitingStore`),
|
|
136
138
|
Apple işleyince `completed` (en çok 60 dk); bu durumda iptal 409. İptal 409'unun mesajı nedeni söyler (iş zaten
|
|
@@ -284,6 +286,69 @@ birleşir (`{ appLock: { timeoutSeconds: 300 } }` açık/kapalıyı korur).
|
|
|
284
286
|
görüntüsü örtülür, Android'de örtülmez. **Oynatıcıda (Expo Go) ve Studio önizlemesinde kilit yoktur** — tek bir
|
|
285
287
|
ekranı kapı arkasına almak için `biometric-auth` adımı (platform_guide('mobile-steps')).
|
|
286
288
|
|
|
289
|
+
## Satın almalar
|
|
290
|
+
|
|
291
|
+
Uygulama içi satın alma (kök ADR 0027): abonelik (`subscription`), tek seferlik kalıcı ürün (`non_consumable`) ve tüketilen
|
|
292
|
+
ürün (`consumable` — kredi, jeton). Ürün Zeyrek anahtarıyla anılır, mağaza kimliği sayfaya yazılmaz; hak (`entitlement`) Apple'da
|
|
293
|
+
abonelik grubu, Google'da abonelik, abonelik ürünü Google'da temel plandır (anahtar, `_` → `-`). Sıra:
|
|
294
|
+
|
|
295
|
+
1. **Ürün:** `mobile_product_upsert` (anahtar, tür, hak, TRY fiyatı, abonelikte süre ve deneme, dil başına ad ≤ 30 ve
|
|
296
|
+
açıklama ≤ 45) — `mobile_product_list` mağaza başına durumu gösterir. Mağazaya gitmiş ürünün anahtarı, türü, hakkı ve
|
|
297
|
+
bakiye adı kilitlidir; ad/fiyat/süre/miktar değişikliği `pendingChanges`'e düşer (miktar mağaza alanı değildir, doğrudan
|
|
298
|
+
geçer). **Tüketilen ürün:** `kind: 'consumable'`, hak yerine `balance` (bakiyenin adı, ekranlar `Balances.<ad>` okur) ve
|
|
299
|
+
`grantsAmount` (her satın almada eklenen miktar, 1–1.000.000); süre, deneme ve hak yasak. Apple'da `CONSUMABLE`, Google'da
|
|
300
|
+
tek seferlik tüketilebilir ürün — grup, temel plan ve deneme adımı yoktur, gönderim yolu `non_consumable` ile aynıdır.
|
|
301
|
+
Aynı `balance`'ı birden çok ürün paylaşır. Kullanıcı ürünü her istediğinde yeniden alır; mağaza **geri yüklemez** (K9):
|
|
302
|
+
girişsiz uygulamada telefon değişince bakiye gider, girişli uygulamada hesaba taşınır (`MOB-036` kredi harcama akışı
|
|
303
|
+
yazılırken girişsiz uygulamayı uyarır). İade o satırın miktarını bakiyeden geri alır, bakiye negatife düşebilir.
|
|
304
|
+
2. **Hazırlık:** `mobile_purchase_readiness` — iOS: Apple hesabı, ücretli uygulama sözleşmesi, satın alma anahtarı
|
|
305
|
+
(`.p8`), bildirim adresi; Android: Play hizmet hesabı, ödeme profili, faturalandırmalı derleme, hizmet hesabı izni,
|
|
306
|
+
gerçek zamanlı bildirim konusu. `manual` satırları konsolda insan yapar; satır `hint`'i nerede olduğunu söyler.
|
|
307
|
+
3. **Faturalandırmalı derleme (Android):** Google ürün açmayı `BILLING` izinli bir derleme yüklenmeden reddeder
|
|
308
|
+
(`play_billing_build`). Ürünlü uygulamanın derlemesi `purchases` bloğunu ve izni taşır — kabuk 0.9.0+ (ön kontrol
|
|
309
|
+
P-23: `purchases_shell_outdated` eski kabuk, `purchases_unknown_product` bir `purchase` adımının düz anahtarı
|
|
310
|
+
katalogda yok, `purchases_not_sent` ürün mağazaya gitmemiş); `mobile_build_start
|
|
311
|
+
{ platform: 'android', target: 'internal' }`. Ürünü olmayan uygulamanın ikilisinde satın alma modülü ve izin yoktur.
|
|
312
|
+
4. **Gönder:** `mobile_product_send` (202, asenkron) → `mobile_product_send_status`. Geri alınamaz: mağazadaki ürün API'den
|
|
313
|
+
silinmez, Apple ürün kimliğini yeniden kullandırmaz — anahtar ve fiyatı kullanıcıya onaylatın. Fiyat değişiminde
|
|
314
|
+
mevcut aboneler varsayılan olarak eski fiyatta kalır (`applyToExisting: true` onlara da uygular). Düşen adım
|
|
315
|
+
(`paid_agreement`, `play_permission`, `play_billing_build`) giderilince `mobile_product_send { retrySendId }`.
|
|
316
|
+
5. **Ekran:** `purchase` / `restore-purchases` adımları ve `Entitlements` (`platform_guide('mobile-steps')` §Satın alma
|
|
317
|
+
tarifi); tüketilen ürün için `balance-spend` adımı ve `Balances` (§Kredi harcama tarifi).
|
|
318
|
+
|
|
319
|
+
**İnsanın işi (MCP yapmaz):** satın alma anahtarını (`.p8` + Key ID + Issuer ID) Studio › Satın almalar › Mağaza
|
|
320
|
+
hazırlığı'nda yüklemek (gizli bilgi); App Store Connect'te ücretli uygulama sözleşmesi, banka ve vergi; bildirim adresini
|
|
321
|
+
(`notificationUrl` satırının `copyValue`'su) App Store Connect › App Information › App Store Server Notifications
|
|
322
|
+
(Version 2, production ve sandbox aynı adres) alanına yazmak; Play Console'da ödeme profili, hizmet hesabına ürün izni,
|
|
323
|
+
gerçek zamanlı bildirim Pub/Sub konusu ve kimlik doğrulamalı push aboneliği (`rtdnTopic` `copyValue`); App Review
|
|
324
|
+
inceleme görseli. Apple ilk satın alma ürününü uygulamanın yeni sürümüyle inceler: ürün `ready` kalır, sürümle
|
|
325
|
+
incelemeye gönderilince `in_review` olur. Satışlar `mobile_purchase_list` (gerçek satış `production`'dadır; TestFlight
|
|
326
|
+
ve Play iç test satın almaları `environment: test` etiketiyle, `includeTest: true` olmadan görünmez).
|
|
327
|
+
|
|
328
|
+
## Mağazadan taşıma
|
|
329
|
+
|
|
330
|
+
Yayındaki bir uygulama (App Store / Google Play) Zeyrek'e **bir kez okunur, bir kez onaylanır**; sonra tek kaynak Zeyrek'tir
|
|
331
|
+
(kök ADR 0028). Yalnız ilk derlemeden önce (MOB-037).
|
|
332
|
+
|
|
333
|
+
1. `mobile_store_import_start { applicationId, bundleId, packageName }` → bağlantı satırı ve okuma işi.
|
|
334
|
+
2. `mobile_store_import_report` → `state: ready` olunca kovalar: **auto** (Zeyrek'e gelir) · **upload** (siz yüklersiniz:
|
|
335
|
+
ikon, 6.5" ekran görüntüsü) · **kept** (mağazada kalır: beyan içerikleri, ülke fiyatı, promosyon, tüketilen ürün).
|
|
336
|
+
3. `mobile_store_import_mapping` → `scheme` (eski uygulamanın URL şeması, zorunlu), iki mağaza farkı
|
|
337
|
+
(`choice.<satır>`), ürün anahtarı/hak (`product.<i>.key|entitlement`), beyan onayı. Mağaza kimliği seçilmez (MOB-038).
|
|
338
|
+
4. `mobile_store_import_apply` → tek işlem; mağazaya hiçbir şey gitmez. Örnek: `store-import-mapping`.
|
|
339
|
+
|
|
340
|
+
**Aboneler — üç kaynak:** eski liste (`mobile_purchase_import`, önce `dryRun` — MOB-039) · mağaza bildirimi (katalogdaki
|
|
341
|
+
ürünün tanınmayan işlemi **sahipsiz** satın alma açar) · cihaz (kabuk ilk açılışta sürüm başına bir kez sessiz geri
|
|
342
|
+
yükler, kabuk ≥ 0.10.0). Sahipsiz satır `mobile_purchase_list { owner: 'ownerless' }` ile görünür; geri yükleme kuruluma,
|
|
343
|
+
giriş hesaba bağlar.
|
|
344
|
+
|
|
345
|
+
**Apple bildirimleri:** `mobile_store_notification_forwarding { action: 'switch', confirm: true }` adresi Zeyrek'e çevirir,
|
|
346
|
+
eski adresi 90 gün iletir (MOB-040). Google'da Pub/Sub konusuna ikinci abonelik insanın işidir.
|
|
347
|
+
|
|
348
|
+
**Yayın öncesi:** ön kontrol P-24…P-31 (kimlik, sürüm, derleme numarası, upload anahtarı, ürünler, kabuk, bildirim, URL
|
|
349
|
+
şeması). Yayından hemen önce `mobile_store_diff { action: 'read' }`; `store_changed` / `both_changed` satırları
|
|
350
|
+
`choose` ile seçilmeden mağaza yayını başlamaz (MOB-041). Yayın yalnız `send` listesindeki alanları yazar.
|
|
351
|
+
|
|
287
352
|
## Hata kodları
|
|
288
353
|
|
|
289
354
|
`mobile_build_status` → `error.code`.
|
|
@@ -11,10 +11,10 @@ Motorun kendi adımları (`setter`, `condition`, `microservice`, `code`, `functi
|
|
|
11
11
|
|
|
12
12
|
| Adım | Mobilde |
|
|
13
13
|
|---|---|
|
|
14
|
-
| `redirect` | yığına iter (göreli yol) ya da dışarıda açar (tam adres) |
|
|
14
|
+
| `redirect` | yığına iter (göreli yol) ya da dışarıda açar (tam adres); sekme sayfası hedefi sekmeye geçer, bulunulan sekmenin kökü yığını köke indirir (`platform_guide('mobile')` §Gezinme) |
|
|
15
15
|
| `navigate` | eski mobil şemaların adı — `redirect`'in takma adıdır; gateway `redirect`'e çevirir |
|
|
16
16
|
| `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 }`) |
|
|
17
|
-
| `confirm` | `yes` / `no` dalları; `message`/`title`/`confirmText`/`cancelText` metin şablonudur (`{{ }}`, çeviri `t('anahtar')`) |
|
|
17
|
+
| `confirm` | `yes` / `no` dalları; `message`/`title`/`confirmText`/`cancelText` metin şablonudur (`{{ }}`, çeviri `t('anahtar')`). `page_screenshot`'ta pencere `awaiting-user`'dır: `preview-dialog__confirm` → `yes`, `preview-dialog__cancel` → `no` (SCR-023) |
|
|
18
18
|
| `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) |
|
|
19
19
|
| `storage` | `args.name` `state.page.<key>` / `state.app.<key>` ise o state'i yazar (`platform_guide('mobile')` §Kalıcı state), değilse cihaz deposuna |
|
|
20
20
|
| `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 |
|
|
@@ -26,10 +26,50 @@ Motorun kendi adımları (`setter`, `condition`, `microservice`, `code`, `functi
|
|
|
26
26
|
| `notification-cancel` | `args.name` kimlikli planlı bildirimi siler (planlamadaki gibi metin şablonu: `'ilac-{{ item.id }}-0800'`), `'*'` hepsini; hep `always` |
|
|
27
27
|
| `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) |
|
|
28
28
|
| `open-settings` | uygulamanın sistem ayar sayfasını açar; `Steps.<adım>.data.opened`. Argüman almaz |
|
|
29
|
-
| `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 |
|
|
29
|
+
| `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 ve telefonun defterine `back-unavailable` düşer (`mobile_error_list`); oynatıcıda ve mağaza kabuğunda aynı çalışır (eski mağaza kabuğu sürümlerinde boştu). Studio önizlemesinde runtime'ın kendi gezinmesi yoksa yine hiçbir şey, web uygulamasında tarayıcının geri'sidir. Sekme sayfasına dönmek için `redirect` de olur: hedef sekme sayfasıysa sekmeye geçer, bulunulan sekmenin köküyse yığın köke iner (kopya itmez). 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 |
|
|
30
30
|
| `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 |
|
|
31
31
|
| `biometric-auth` | kullanıcıyı Face ID / parmak izi (yedekle cihaz parolası) ile doğrular (kök ADR 0021). `args.title` sistem penceresindeki gerekçe, `args.cancelText` vazgeç düğmesi — ikisi metin şablonu, boşsa uygulama dilindeki varsayılan; `args.value` yedek, düz metin: `device` (biyometri olmazsa cihaz parolası; boşsa bu) ya da `none` (`FLOW-042`; `fallback` / `reason` / `cancelLabel` anahtarı sessizce düşer). Başarı: `Steps.<adım>.data = { verified: true }` → `always`. **Vazgeçmek hatadır**: `error.status` `cancelled` · `not-enrolled` · `lockout` · `failed` · `unavailable` (donanım yok ya da derleme taşımıyor) · `unsupported` (web uygulaması, production `/view`) · `invalid`; `error` dalı → hata akışı → **o da yoksa akış bu adımda biter**, `always`'e bağlı adım koşmaz. Yerel bir kapıdır, sunucu için kanıt değildir. Paletten yalnız mobilde |
|
|
32
32
|
| `scan-code` | tam ekran kod tarayıcı (kök ADR 0017): tek kod okur ve kapanır. `args.value` izinli türler, **virgüllü düz metin** (`FLOW-042`): `aztec` · `codabar` · `code39` · `code93` · `code128` · `datamatrix` · `ean8` · `ean13` · `itf14` · `pdf417` · `qr` · `upc_e`; boş → hepsi. `upc_a` yok: UPC-A etiketi `ean13` ve on üç haneyle (başında `0`) döner. `args.title` ekrandaki yönerge, metin şablonu (`{{ t('anahtar') }}`). Sonuç `Steps.<adım>.data`: `{ value, type, source }` — `source` `camera` ya da `manual` (ekrandaki "elle gir"; tür bilinmiyorsa `type: null`). `error` dalı: `status` `cancelled` (kullanıcı kapattı — **vazgeçme bir hatadır, `error` dalını bağla**) · `denied` (kamera izni yok) · `unavailable` (derleme kamerayı taşımıyor ya da tarayıcı zaten açık) · `unsupported` (web uygulaması) · `invalid`. Kamera izni kendiliğinden derlemeye girer (adım production'daysa). Önizleme (`/dev`, `page_screenshot`) kamera açmaz, "kod gir" penceresi çizer: `__scan-value`'ya yaz, `__scan-confirm` ya da `__scan-cancel` (çok türde `__scan-type-<tür>`). `page_screenshot` pencereyi yalnız `captureUnready: true` ile sürer (akış pencereyi beklerken "koşuyor" sayılır; her eylem 15 sn bekler). Tam gövde: `ngapps://examples/mobile-scan-code` |
|
|
33
|
+
| `purchase` | uygulama içi satın alma (kök ADR 0027). `args.value` ürünün Zeyrek **anahtarı**, metin şablonu: düz `premium_yillik` ya da `{{ plan_secim.value }}` (`FLOW-045`: katalogda olmayan düz anahtar yazılmaz; `{{ }}` dışındaki ifade harfi harfine anahtar sayılır, `productKey`/`sku` anahtarı sessizce düşer). Kabuk mağazanın penceresini açar, sunucu makbuzu mağazaya sorar, hak açılır: `Steps.<adım>.data = { productKey, entitlements }` → `always`; `Entitlements` kendiliğinden güncellenir. **Kapıdır**: `error.status` `cancelled` (vazgeçti) · `pending` (Ask to Buy, bekleyen ödeme ya da sunucuya ulaşılamadı — hak sonra kendiliğinden açılır) · `failed` · `unavailable` (mağaza yok, ürün satışta değil, ikili satın almayı taşımıyor) · `unsupported` (web uygulaması, production `/view`, kabuk < 0.9.0) · `invalid` (anahtar katalogda/manifestte yok); `error` dalı → hata akışı → **o da yoksa akış bu adımda biter**. Paletten yalnız mobilde |
|
|
34
|
+
| `restore-purchases` | mağaza hesabındaki satın almaları geri getirir (telefon değişti, uygulama yeniden kuruldu); argümansız. Başarı `Steps.<adım>.data = { entitlements }` — geri gelecek bir şey yoksa da başarıdır, sonucu hakka bakarak söyleyin. Hata sözlüğü `purchase`'la aynı ve o da kapıdır. Satın alma ekranında bir "Satın almaları geri yükle" düğmesi beklenir (`MOB-034`); yalnız tüketilen ürün satan ekranda beklenmez, tüketilen ürün geri yüklenmez |
|
|
35
|
+
| `balance-spend` | tüketilen ürünün bakiyesinden harcar (kredi, jeton; kök ADR 0027 bakiye bölümü). `args.value` bakiyenin **adı**, `args.title` **miktar** — ikisi de metin şablonu: düz `kredi` / `10` ya da `{{ adet.value }}` (`FLOW-046`: boş değer 422; katalogda tüketilen ürünün `balance`'ı olmayan düz ad yazılmaz; `{{ }}` dışındaki ifade harfi harfine metin sayılır; `balance`/`amount` anahtarı sessizce düşer). Harcama sunucuda atomiktir (aynı sahibin eşzamanlı harcaması sıralanır, bakiye aşılamaz). Başarı `Steps.<adım>.data = { balances }` → `always`; `Balances` kendiliğinden güncellenir, ayrıca okumayın. **Kapıdır**: `error.status` `insufficient` (bakiye yetmiyor — negatif bakiye de bu) · `unavailable` (çevrimdışı ya da sunucuya ulaşılamadı; çevrimdışı yazma kuyruğuna GİRMEZ) · `unsupported` (web uygulaması, production `/view`, kabuk satın almayı taşımıyor) · `invalid` (ad ya da miktar geçersiz; miktar 1 ve üstü tam sayı) · `failed`; `error` dalı → hata akışı → **o da yoksa akış bu adımda biter**. Başarılı her tıklama ayrı harcamadır; sonucu belirsiz kalan (`unavailable`) harcamanın anahtarı cihazda durur, aynı bakiye ve miktarla yeniden dokunuş iki kez düşmez. Anahtarı akışın verdiği harcama workflow `balance` adımındadır. Paletten yalnız mobilde |
|
|
36
|
+
|
|
37
|
+
## Sayfa kapanınca
|
|
38
|
+
|
|
39
|
+
Sayfa söküldüğünde (geri, yığının köke inmesi, çıkış — sekme değişimi sayfayı sökmez) açık akış yarıda kalmaz (kök ADR 0030): yeni koşum başlamaz, açık
|
|
40
|
+
koşumlar telefona yazan adımlarla sonuna kadar sürer. 30 sn'de bitmeyen koşum kapatılır.
|
|
41
|
+
|
|
42
|
+
| Adımlar | Sayfa kapalıyken |
|
|
43
|
+
|---|---|
|
|
44
|
+
| `microservice`, `storage`, `setcookie`, `code`, `function`, `condition`, `validation`, `query`, `flow-trigger`, `bpm`, `notification-schedule`, `notification-cancel`, `haptic`, `balance-spend` | koşar |
|
|
45
|
+
| `setter`, `alert`, `confirm`, `navigate`, `redirect`, `back`, `share`, `download` | atlanır; akış `always`'tan (`confirm`'de `no`) sürer |
|
|
46
|
+
| `scan-code`, `location`, `permission`, `notification-permission`, `open-settings`, `ad-show`, `purchase`, `restore-purchases` | `error.status: 'page-unmounted'` ile hata yolu |
|
|
47
|
+
| `biometric-auth` | hata yolu; hata yolu yoksa akış biter (kapı doğrulamasız açılmaz) |
|
|
48
|
+
|
|
49
|
+
Atlanan her adım telefonun defterine `page-unmounted` kaydı düşer (`mobile_error_list`). `state.*` yazımı `storage` ya da
|
|
50
|
+
`code` adımıyla yapılır — `setter` atlanır. "İsteği göndermeden önce ekranı değiştir", "önce yaz sonra çık" gibi geçici
|
|
51
|
+
çözümler gerekmez. Studio mobil önizlemesi ve `/view` de sayfa geçişinde boşaltır; "Dene"nin yeniden başlatması ve
|
|
52
|
+
önizlemenin kapanması akışı keser. Web (Svelte) uygulaması akışı sayfayla birlikte keser.
|
|
53
|
+
|
|
54
|
+
## Önizlemede çekim hedefleri
|
|
55
|
+
|
|
56
|
+
Bileşenin iç kontrolü `<elementId>__<rol>` ile hedeflenir; rol görünen metinden değil kayıttaki anahtardan türer.
|
|
57
|
+
Mobil roller `component_get` çıktısının mobil notlarındadır (`internalRoles` ve bulunamayan hedefin notu yalnız web
|
|
58
|
+
rollerini sayar):
|
|
59
|
+
|
|
60
|
+
| Kimlik | Ne |
|
|
61
|
+
|---|---|
|
|
62
|
+
| `<id>__press` | tek basışlı denetimin basış yüzeyi (kök kendisi basılabilir değilse `click` buna basar) |
|
|
63
|
+
| `<id>__row-<i>`, `<id>__row-<i>__handle` | liste satırı, sürükleme tutamacı (`swipe` hedefi) |
|
|
64
|
+
| `<id>__chip-<değer>` | çip seçeneği; tablonun filtre çipi `__chip-<kolon>` |
|
|
65
|
+
| `<id>__tab-<anahtar>` | `tabs` bileşeninin sekmesi (`tab` eylemi) |
|
|
66
|
+
| `<id>__day-<yyyy-MM-dd>`, `<id>__prev`, `<id>__next` | `calendar`/`datepicker` günü ve ay oku (`click`) |
|
|
67
|
+
| `__tabs`, `__tabs__item-<sayfa slug'ı>` | kabuğun sekme çubuğu ve öğesi |
|
|
68
|
+
| `preview-dialog__confirm`, `preview-dialog__cancel` | `confirm`/`alert` penceresi (SCR-023) |
|
|
69
|
+
| `preview-biometric__verify`, `preview-biometric__cancel` | `biometric-auth` penceresi (SCR-018) |
|
|
70
|
+
| `__scan-value`, `__scan-type-<tür>`, `__scan-confirm`, `__scan-cancel` | `scan-code` "kod gir" penceresi (SCR-019; önek yok) |
|
|
71
|
+
|
|
72
|
+
Pencere açıkken altındaki sayfa öğesine `click` uygulanmaz (SCR-023).
|
|
33
73
|
|
|
34
74
|
## Bildirim tarifi
|
|
35
75
|
|
|
@@ -78,6 +118,54 @@ sürer: adımı tetikleyen tıklamanın raporu `pageState: 'awaiting-user'` der,
|
|
|
78
118
|
'preview-biometric__verify' }` (Doğrulandı say) ya da `'preview-biometric__cancel'`. Tam gövde:
|
|
79
119
|
`ngapps://examples/flow-biometric-confirm`.
|
|
80
120
|
|
|
121
|
+
## Satın alma tarifi
|
|
122
|
+
|
|
123
|
+
**Satın alma tarifi** (kök ADR 0027): (1) ürünler kataloğa — `mobile_product_upsert { key: 'premium_yillik', kind:
|
|
124
|
+
'subscription', entitlement: 'premium', priceTry, period: 'year', trial, texts }`; aynı hakkı veren aylık ve yıllık
|
|
125
|
+
abonelik mağazada bir gruptur. Hazırlık ve mağazaya gönderim `platform_guide('mobile-publish')` §Satın almalar. (2) Ekran
|
|
126
|
+
ürüne değil **hakka** bakar: teklif kökü `isHidden: {{ Entitlements.premium }}`, "Premium açıldı" kökü
|
|
127
|
+
`isHidden: {{ !Entitlements.premium }}` — ayrı sayfa gerekmez. Fiyatı yazmayın: `{{ Products.premium_yillik.price }}`
|
|
128
|
+
mağazanın yerel fiyat metnidir (`₺949,99`), `period` / `trial` ISO 8601 süre (`P1Y`, `P7D`) ya da `null`; plan seçimi
|
|
129
|
+
`choice-chips` ise `{{ Products[plan_secim.value]?.price ?? '' }}`. (3) Devam et düğmesinin akışı `start` → `purchase`
|
|
130
|
+
`{ value: '{{ plan_secim.value }}' }`; `always` teşekkür, `error` dalında `condition`: `pending` → "onay bekleniyor",
|
|
131
|
+
`cancelled` dışı → hata iletisi, `else` (vazgeçti) → hiçbir şey. (4) "Satın almaları geri yükle" düğmesi → `restore-purchases`
|
|
132
|
+
— bu akışı satın alma akışından ÖNCE yazın: `MOB-034` ekranın geri yükleme yolunu yalnız satın alma akışı yazılırken arar. (5) Abonelik satan ekranda süre ve fiyat, otomatik yenileme ve iptal yolu metni, kullanım koşulları ve
|
|
133
|
+
gizlilik bağlantısı (`href` taşıyan düğme; adres `mobile_store_get` `urls.privacyPolicy`) — App Store 3.1.2 ve Play
|
|
134
|
+
politikası ister, yoksa ekran incelemeden döner (`MOB-042`). Tam gövde: `ngapps://examples/flow-purchase-paywall`.
|
|
135
|
+
|
|
136
|
+
Tuzaklar (Studio'nun "Teklif ekranı oluştur" şablonu ilk ölçümde üçüne düştü): akış `start` adımıyla açılmazsa motor
|
|
137
|
+
onu sessizce bitirir, düğme hiçbir şey yapmaz; kod bağı `{{ }}` ile sarılmazsa (`isHidden: 'Entitlements.premium'`)
|
|
138
|
+
düz metin sayılır ve iki kök birden gizlenir; hata dalı yalnız `pending`'i karşılarsa öteki hatalar sessiz kalır.
|
|
139
|
+
Statik anahtar (`{{ }}` şablonu olmayan değer) katalogda değilse yazım `FLOW-045` ile **durur**, hata katalogdaki
|
|
140
|
+
anahtarları sayar: ürünü önce `mobile_product_upsert` ile açın (akış doğrulayıcısı kataloğu okumaz; Studio'da yazılan
|
|
141
|
+
bilinmeyen anahtarı mağaza yayını ön kontrolü P-23 uyarır). Şablon değer yazımda denetlenmez. Nerede ne olur: Studio önizlemesi, canlı kip, `/dev`,
|
|
142
|
+
`/branch` ve Zeyrek Mobile oynatıcısı gerçek satın alma yapmaz, dört sonuçlu **deneme penceresi** açar (`preview-purchase__success` ·
|
|
143
|
+
`__cancel` · `__pending` · `__error`, gönder `preview-purchase__send`; başarı yalnız o oturumda hakkı açar, Haklar çipi
|
|
144
|
+
de açar); production `/view` ve web uygulaması `unsupported`. `page_screenshot` pencereyi biyometri penceresi gibi
|
|
145
|
+
tanımaz — `scan-code`'daki gibi `captureUnready: true` ile sürün (satın alma penceresinde ölçülmedi). Gerçek satın alma
|
|
146
|
+
kabuk 0.9.0+ mağaza derlemesinde (TestFlight sandbox, Play iç test): hak sunucuda doğrulanır, telefonun söylediğine
|
|
147
|
+
güvenilmez; cihazın doğrulama/geri yükleme uçlarını kabuk çağırır, akıştan çağrılmaz. Satışlar `mobile_purchase_list`.
|
|
148
|
+
|
|
149
|
+
## Kredi harcama tarifi
|
|
150
|
+
|
|
151
|
+
**Kredi harcama tarifi** (kök ADR 0027 bakiye bölümü): (1) kredi ürünü kataloğa — `mobile_product_upsert { key: 'kredi_100',
|
|
152
|
+
kind: 'consumable', balance: 'kredi', grantsAmount: 100, priceTry, texts }`; aynı `balance`'ı birden çok ürün paylaşır
|
|
153
|
+
(`kredi_100`, `kredi_500` → `Balances.kredi`), `entitlement` verilmez. Satın alma teklif ekranıyla aynıdır (`purchase`
|
|
154
|
+
`{ value: 'kredi_100' }`): doğrulanınca bakiye `grantsAmount` kadar artar. (2) Ekran bakiyeyi okur:
|
|
155
|
+
`{{ 'Kredin: ' + (Balances.kredi ?? 0) }}` — bilinmeyen bakiye `undefined`, `?? 0` yazın; `{{ }}` ile sarın. (3) Harcayan
|
|
156
|
+
düğmenin akışı `start` → `balance-spend { value: 'kredi', title: '10' }`; `always` işi yapar, `error` dalında
|
|
157
|
+
`Steps.<ad>.error.status === 'insufficient'` → "Krediniz yetmiyor" ve satın alma ekranına yönlendirme, `unavailable` →
|
|
158
|
+
"Bağlantı gerekli" (çevrimdışıyken harcanamaz, kuyruğa girmez). (4) Sunucu tarafı iş (rapor üretmek) harcamadan SONRA
|
|
159
|
+
koşar: harcamayı önce yapan akış, iş düşerse bakiyeyi iade etmez — sunucuda "düş, üret, düşerse geri ekle" gerekiyorsa
|
|
160
|
+
workflow `balance` adımıyla kurun (`platform_guide('workflow')` §Balance adımı). Tuzaklar: miktar `args.title`'dır
|
|
161
|
+
(`args.amount` sessizce düşer, `FLOW-046` block); bakiye adı katalogdaki `balance`'tır, ürün anahtarı değil; **girişsiz
|
|
162
|
+
uygulamada bakiye kuruluma yazılır ve tüketilen ürün mağazada geri yüklenemez** — telefon değişince kredi gider
|
|
163
|
+
(`MOB-036` yazımda uyarır); girişli uygulamada hesaba taşınır. Nerede ne olur: production `/view` ve web uygulaması
|
|
164
|
+
`unsupported` döner; gerçek harcama kabuk 0.9.0+ mağaza derlemesinde sunucuda doğrulanır. Studio önizlemesinde kredi
|
|
165
|
+
satın alma/harcama davranışı için frontend 11 (kabukta tüketme ve önizleme) bitene kadar bu rehberde ölçülmüş bir şey
|
|
166
|
+
yoktur — önizlemede `unsupported` ya da deneme değeri görürseniz hata değildir. Tam gövde:
|
|
167
|
+
`ngapps://examples/flow-credit-spend`.
|
|
168
|
+
|
|
81
169
|
## Konum ve harita
|
|
82
170
|
|
|
83
171
|
`style`, `accuracy`, `reverseGeocode` gibi anahtarlar sözleşmede yoktur ve platform onları sessizce düşürür
|
|
@@ -17,7 +17,8 @@ anlamları ayrı.
|
|
|
17
17
|
1. **Uygulama ve kabuk.** Tek çağrıda: `mobile_app_create` (uygulama + sayfalar + kabuk layout'u +
|
|
18
18
|
sekme ayarı + ana sayfa bağı, `dryRun` ile önce planı görün). Tekil araçlarla aynı sıra:
|
|
19
19
|
`application_create({ type: 'mobile' })` → `page_create` × N → `layout_create` (kabuk) →
|
|
20
|
-
`page_update({ layoutId })` × N → `application_update({ homepageId, properties.mobile.tabs })
|
|
20
|
+
`page_update({ layoutId })` × N → `application_update({ homepageId, properties.mobile.tabs })` (ayar yalnız
|
|
21
|
+
development'a yazılır, production'a dağıtımla geçer — APP-001).
|
|
21
22
|
Sekme ayarı sayfa **id**'si ister; bu yüzden sayfalardan SONRA yazılır.
|
|
22
23
|
2. **Veri.** Entity, microservice ve workflow web'dekiyle aynıdır (`platform_guide('microservice')`).
|
|
23
24
|
Sayfa akışı `microservice` adımıyla aynı uçları çağırır. Veri bağlamanın telefon
|
|
@@ -368,7 +369,8 @@ etiketi ailesini `typographyStyle`'dan alır, alan etiketleri platform yazı tip
|
|
|
368
369
|
|
|
369
370
|
## Gezinme
|
|
370
371
|
|
|
371
|
-
- **Yığın varsayılandır; ayar yoktur.** `redirect` adımı hedef sayfayı yığına iter
|
|
372
|
+
- **Yığın varsayılandır; ayar yoktur.** `redirect` adımı hedef sayfayı yığına iter (sekme sayfası hedefi sekmeye
|
|
373
|
+
geçer, aşağıda), geri hareketi
|
|
372
374
|
(cihaz, app-bar ya da tarayıcı geri) önceki ekrana döner. Göreli yol uygulama
|
|
373
375
|
köküne eklenir; tam adres (`https://…`) dışarıda açılır.
|
|
374
376
|
- **Sekmeler uygulama ayarıdır, layout'ta değil.** `application_update({ properties: { mobile: { tabs } } })`:
|
|
@@ -398,6 +400,13 @@ etiketi ailesini `typographyStyle`'dan alır, alan etiketleri platform yazı tip
|
|
|
398
400
|
gizlenir; alan yoksa görünür). `true`/`false`'tan başka değer (ör. metin `"false"`) yok sayılır ve `MOB-007` uyarır.
|
|
399
401
|
İkinci dokunuş (`reselect`) ile sekme değişimi (`keepStackOnSwitch`) ayrı ayardır; eski runtime anahtarları bilmez ve
|
|
400
402
|
varsayılan davranışta kalır.
|
|
403
|
+
- **Sekme sayfasına `redirect` sekmeye geçer, kopya itmez.** Hedef (`pageId` ya da sayfanın yolu) bir sekme sayfasıysa
|
|
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
|
+
yazılır. Başka sayfa bulunulan sekmenin yığınına itilir.
|
|
406
|
+
- **`back` adımı iki kabukta da yığında bir geri gider.** Gidemezse (yığının ilk ekranı) hiçbir şey yapmaz, akış yine
|
|
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**: telefona yazan adımlar sonuna kadar koşar, ekrana dokunanlar atlanır
|
|
409
|
+
(`platform_guide('mobile-steps')` §Sayfa kapanınca).
|
|
401
410
|
- **Çekmece bir bileşendir.** `drawer` widget'ı + `app-bar`'ın `menuTarget` prop'u çekmeceyi açar.
|
|
402
411
|
Çekmece layout'un kök dizisindedir (`views: [shell_root, nav_drawer]`), kabuk kabının içinde değil.
|
|
403
412
|
- Kabuk örnekleri: `ngapps://examples/mobile-app-shell-stack` (yığın), `ngapps://examples/mobile-app-shell-tab`
|
|
@@ -473,10 +482,11 @@ Sunucudan gönderilen bildirim yerel `notification-schedule`'dan ayrıdır. Kana
|
|
|
473
482
|
|
|
474
483
|
## Kalıcı state
|
|
475
484
|
|
|
476
|
-
State varsayılan olarak **bellektedir**: sayfa her açıldığında `variable_create`'
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
485
|
+
State varsayılan olarak **bellektedir**: `state.page` sayfa her açıldığında `variable_create`'teki `value`'dan başlar,
|
|
486
|
+
ekran kapanınca kaybolur. **Mobil kabukta `state.app` uygulama + ortam başına tek depodur** (kök ADR 0031): bir
|
|
487
|
+
sayfanın yazdığı arkadaki sayfada da anında görünür; açılan sayfanın `value`'su yalnız depoda olmayan anahtarı
|
|
488
|
+
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).
|
|
489
|
+
Uygulama kapanıp açıldığında kaldığı yerden sürmesi gereken değer için değişkene saklama türü verin:
|
|
480
490
|
|
|
481
491
|
| `storage` | Depo | Ne için |
|
|
482
492
|
|---|---|---|
|
|
@@ -496,8 +506,8 @@ variable_create({ applicationId, pageId, variables: [
|
|
|
496
506
|
**ilk açılış** değeridir. Saklı değerin biçimi sonradan değiştirdiğiniz `value` ile uyuşmuyorsa saklı
|
|
497
507
|
olan kazanır; sıfırlamak için anahtarı yeniden adlandırın.
|
|
498
508
|
- `pageId` verilmeden tanımlanan değişken (`state.app`) uygulama kapsamındadır: her sayfa açılışta aynı
|
|
499
|
-
saklı değeri okur.
|
|
500
|
-
- Çıkışta (kullanıcının çıkışı ya da oturumun düşmesi) iki depodaki state değerleri silinir.
|
|
509
|
+
saklı değeri okur. Mobil kabukta açık sayfalar birbirinin yazdığını canlı görür; web'de değer sayfa açılırken okunur.
|
|
510
|
+
- Çıkışta (kullanıcının çıkışı ya da oturumun düşmesi) iki depodaki state değerleri ve bellekteki `state.app` silinir.
|
|
501
511
|
- Ortamlar ayrıdır: `development`'ta saklanan değer `production`'da okunmaz.
|
|
502
512
|
- `variable_update` `storage` vermezse kayıttaki tür korunur; belleğe çevirmek için `storage: 'memory'`.
|
|
503
513
|
`value` vermezseniz kayıttaki değer de korunur — yalnız türü değiştirmek için `{ key, storage }` yeter.
|
|
@@ -524,6 +534,20 @@ variable_create({ applicationId, pageId, variables: [
|
|
|
524
534
|
- `flowRuns: 0` + `requests: 0` bir tıklamada akışın koşmadığını söyler: olay bağını
|
|
525
535
|
`page_runtime_get`, akışı `flow_get` ile doğrulayın.
|
|
526
536
|
- Kuru doğrulama: `page_schema_validate` mobil kataloğa karşı doğrular (`MOB-003`).
|
|
537
|
+
- **Telefonda düşen hata** `mobile_error_list({ applicationId })` ile okunur (NTF-006): kabuk akış hatalarını sunucuya
|
|
538
|
+
gönderir, uçak kipindekiler ağ dönünce gelir. `page-unmounted` (sayfa kapanınca atlanan adım) ve
|
|
539
|
+
`back-unavailable` (geri gidilemedi) kayıtları da buradadır; önizlemede `page_screenshot` `runtimeErrors`'ta görünür.
|
|
540
|
+
- **Önizlemede ağsızlık yalnız hata yolunu sınar** (SCR-020…022): `{ action: 'network', value: 'offline' }` ağı keser,
|
|
541
|
+
`'online'` açar (`actionsReport.online`). Önizlemede önbellek ve yazma kuyruğu YOKTUR: okuma da yazma da `error`
|
|
542
|
+
dalından gider (`queued`, `offlineQueueChange` koşmaz, `error.network` yazılmaz). Kuyruklu senaryo (kaydet → listede
|
|
543
|
+
bekleyen → eşitleme) yalnız cihazda (uçak kipi). İki çağrıya yayılan senaryo `session` ile kurulur — çerez,
|
|
544
|
+
localStorage (`storage: 'device'` state) ve IndexedDB taşınır; `secure` (sessionStorage) ve ağ taşınmaz. Klavye açıkken
|
|
545
|
+
yazılan hâl için `set` + `blur: false` (`changed` koşmaz). Önizleme her çağrıda tek sayfa açar.
|
|
546
|
+
- **Onaylı akış sürülür** (SCR-023): `confirm`/`alert` adımı önizlemede pencere açar; onu açan `click`'in raporu
|
|
547
|
+
`pageState: 'awaiting-user'` der, sonraki eylem `{ action: 'click', target: 'preview-dialog__confirm' }` (onay dalı)
|
|
548
|
+
ya da `'preview-dialog__cancel'` (red dalı; uyarıda yok). Geçici onaysız sürüm yazmaya gerek yoktur.
|
|
549
|
+
- **Çekim hedef kimlikleri** (`__press`, `__row-<i>`, `__chip-<değer>`, `__tab-<anahtar>`, önizleme pencereleri) tek
|
|
550
|
+
tabloda: `platform_guide('mobile-steps')` §Önizlemede çekim hedefleri.
|
|
527
551
|
- Kare **donuktur**: bağlam "hareketi azalt" ile açılır, bileşen hareketi (giriş, döngü, geçiş, iskelet parıltısı,
|
|
528
552
|
kabuğun yükleme göstergesi) durur, aynı sayfanın iki karesi aynı çıkar. Hareketin kendisini görmek için
|
|
529
553
|
`motion: 'play'` (kare çekildiği ana bağlıdır; yanıt `motionNote` taşır).
|
|
@@ -547,6 +571,8 @@ Mobile App Store'dan ya da Google Play'den yüklenir (pencere mağaza bağlantı
|
|
|
547
571
|
çıkan kod okutulur (120 sn geçerli). **Senin Mac'inde:** kullanıcı kendi Mac'ini bir kez ekler, simülatör o Mac'te
|
|
548
572
|
açılır; Mac'i olmayan için Zeyrek'in Mac'inde açma henüz yoktur. **Kaydettiğinde güncellenir:** yazdığın yama
|
|
549
573
|
telefonda ve simülatörde yenilenir. Ajan kod üretmez, önizleme başlatmaz; kullanıcıyı bu pencereye yönlendirir.
|
|
574
|
+
Uygulamanın kendi derlemesi ayrı yoldur: iOS `target: 'build'` imzalama kimliği yokken simülatör `.app`'i,
|
|
575
|
+
kimlik tamsa simülatörde açılmayan `.ipa` üretir (`platform_guide('mobile-publish')` §Hedefler).
|
|
550
576
|
|
|
551
577
|
## Yayın
|
|
552
578
|
|
|
@@ -568,5 +594,9 @@ yaptıkları. Derlenen uygulama
|
|
|
568
594
|
- Dar ekran kırılımı (`hideOn: 'mobile'`) mobil uygulamada anlamsızdır: ekran zaten tek boydur.
|
|
569
595
|
- Ağ yokken ekranlar ve (açıksa) son okunan veri cihazdaki kopyadan gelir: `properties.mobile.offline`
|
|
570
596
|
(MOB-033), `Steps.<ad>.cache.at`, `error.network` — `platform_guide('page')` §Kural 1g-4. Önizleme önbellek kullanmaz.
|
|
597
|
+
Yalnız GET okuma saklanır (POST okuma ağsız açılmaz → GET + query-string, FLOW-044); kuyruk değişimi sayfanın
|
|
598
|
+
`offlineQueueChange` olayıdır, yoklama gerekmez (§Kural 1g-4).
|
|
599
|
+
Ayar (`properties`, ana sayfa, bulunamadı sayfası) `application_update` ile yalnız **development**'a yazılır; production'da
|
|
600
|
+
`deployment_start`'tan sonra geçerli olur (APP-001) — önce development'ta deneyin.
|
|
571
601
|
"Uygulama ağsızken de çalışsın" isteğinin tarifi (okuma GET, yazma kuyruğu, liste birleştirme, düğme kilidi, korumasız
|
|
572
602
|
açılış, bant kapalı): §Kural 1g-4 "Ağsızken fark göstermeyen uygulama", örnek `ngapps://examples/mobile-offline-seamless`.
|
|
@@ -1537,7 +1537,9 @@ Kayıtlar kullanıcıya, ortama ve veri diline bağlıdır; çıkışta ve kulla
|
|
|
1537
1537
|
yazıp yazmadığını bildirimi veren söyler. Okuyan uca (tablo `get`/`count`, görünüm kaynağı) verilirse **MS-025** uyarır.
|
|
1538
1538
|
- **Akış:** `microservice` adımına `queued` dalı (yalnız mobil). Ağ hatasında (yanıt hiç yok) dal bağlıysa, yöntem
|
|
1539
1539
|
yazansa (POST/PUT/DELETE) ve dosya parametresi yoksa kayıt kuyruğa girer ve `queued` koşar; `Steps.<ad>.data` `null`,
|
|
1540
|
-
`Steps.<ad>.queued = { clientRequestId, at }`.
|
|
1540
|
+
`Steps.<ad>.queued = { clientRequestId, at }`. Barındırıcı ağın olmadığını biliyorsa (`Device.isOnline === false`)
|
|
1541
|
+
bu koşullardaki istek hiç denenmez, doğrudan kuyruğa girer (kök ADR 0029) — uçak kipinde `queued` beklemeden koşar;
|
|
1542
|
+
durum bilinmiyorsa istek denenir. Dal bağlı değilse adım bugünkü gibi `error`'a gider; dosya
|
|
1541
1543
|
parametresinde `Steps.<ad>.error.code = 'fileNotQueued'`. HTTP hatası — önündeki vekil sunucunun 502/503'ü dahil —
|
|
1542
1544
|
kuyruğa girmez. Web uygulamasında ya da bildirimsiz servise bağlanan `queued` için yazım **FLOW-043** uyarısı alır.
|
|
1543
1545
|
- **Sunucu karar verir:** cihaz servisin bildirimini bilmez. `queued` dalı bağlı yazmanın İLK isteği de
|
|
@@ -1554,8 +1556,14 @@ Kayıtlar kullanıcıya, ortama ve veri diline bağlıdır; çıkışta ve kulla
|
|
|
1554
1556
|
- **Görünürlük:** kaydın sonucu akışa dönmez. `Device.offline.queue = { pending, rejected }` ve
|
|
1555
1557
|
`Device.offline.rejected` (`id`, `service`, `at`, `status`, `code`, `message`); kod adımında `Page.offline.list()`,
|
|
1556
1558
|
`.count()`, `.sync()`, `.resolve(id, 'retry' | 'discard')`. Süresi dolan kayıt yeniden denenmez.
|
|
1557
|
-
-
|
|
1558
|
-
|
|
1559
|
+
- **Kuyruk değişince ekran tazelenir:** mobil sayfanın `offlineQueueChange` olayı (`page.events`, `mount`/`destroy`/`focus`
|
|
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
|
+
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
|
+
koşarken kuyruğun değişmesi olayı yeniden tetiklemez (kuyruğa yazan akış döngüye girmez). Listeyi bekleyenlerle
|
|
1563
|
+
yeniden kuran yükleme akışını bu olaya da bağlayın — yoklama (`setInterval`, birkaç saniyede bir okuma) gerekmez.
|
|
1564
|
+
- **Önizleme:** Studio ve web'de kuyruk yoktur, `queued` ve `offlineQueueChange` hiç koşmaz. `page_screenshot`
|
|
1565
|
+
`{ action: 'network', value: 'offline' }` ile ağı keser (SCR-020) ama önbellek ve kuyruk olmadığı için okuma da yazma
|
|
1566
|
+
da hata yolundan gider. Açık ekran ilk çizildiği kopyayla kalır — akışa eklenen dal ekran bir sonraki açılışta etkindir.
|
|
1559
1567
|
|
|
1560
1568
|
Örnek: `ngapps://examples/mobile-offline-queue` (doz işaretleme: servis bildirimi + `queued` dalı).
|
|
1561
1569
|
|
|
@@ -1577,8 +1585,8 @@ Kayıtlar kullanıcıya, ortama ve veri diline bağlıdır; çıkışta ve kulla
|
|
|
1577
1585
|
hiç açılmamış sayfa ağ yokken gelmez.
|
|
1578
1586
|
|
|
1579
1587
|
**Doğrulama sınırı:** önizleme (`/dev`, Studio kanvası) ve `page_screenshot` önbellek KULLANMAZ — ayar açık olsa da her
|
|
1580
|
-
okuma ağdan gider, `cache` ve `cachedAt` hiç yazılmaz. `page_screenshot` ağı
|
|
1581
|
-
cihazda görülür (uçak modu). Ölçüldü 2026-10-20 (simülatör, mağaza kabuğu): GET servis okuması ağ varken 149 ms'de
|
|
1588
|
+
okuma ağdan gider, `cache` ve `cachedAt` hiç yazılmaz. `page_screenshot`'ın `network` eylemi ağı keser ama önbellek
|
|
1589
|
+
olmadığından okuma hata yolundan döner; önbellekli ve kuyruklu davranış yalnız cihazda görülür (uçak modu). Ölçüldü 2026-10-20 (simülatör, mağaza kabuğu): GET servis okuması ağ varken 149 ms'de
|
|
1582
1590
|
ağdan, sunucu yanıt vermezken 8,03 sn'de kesilip kayıttan döndü.
|
|
1583
1591
|
|
|
1584
1592
|
Örnek: `ngapps://examples/mobile-offline-list` (liste + "son güncelleme" satırı + bağlantı yok uyarısı). Web
|
|
@@ -1602,7 +1610,11 @@ AYNI; veri bağlantı gelince Zeyrek'e kendiliğinden gider. Altı adım birlikt
|
|
|
1602
1610
|
`Page.offline.resolve(id, 'retry' | 'discard')` ile karar verdirin; göstermemek bilinçli bir ödünleşimdir.
|
|
1603
1611
|
4. **Liste bekleyenleri gösterir:** yükleme akışı sunucunun yanıtına `await Page.offline.list()`'teki
|
|
1604
1612
|
`status === 'pending'` kayıtları katar (`service` yazma ucunun slug'ı, alan değeri `parameters[].value`'dan); ağ
|
|
1605
|
-
yokluğu hata metni göstermez (`Steps.<ad>.error && !Steps.<ad>.error.network`).
|
|
1613
|
+
yokluğu hata metni göstermez (`Steps.<ad>.error && !Steps.<ad>.error.network`). Aynı akış sayfanın
|
|
1614
|
+
`offlineQueueChange` olayına da bağlanır: kuyruğa giren kayıt listeye hemen, gönderilen kayıt sunucudan gelir.
|
|
1615
|
+
Kaydı yazan sayfadan çıkılsa da yazma akışı tamamlanır (kök ADR 0030) ve `state.app` mobil kabukta sayfalar arası
|
|
1616
|
+
ortaktır (kök ADR 0031): "istekten önce yaz", "kuyruktan oku" ve "birkaç saniyede bir yokla" geçici çözümleri
|
|
1617
|
+
gerekmez (Defter saha raporu, 2026-10-30).
|
|
1606
1618
|
5. **Düğme kilidi:** yazan düğme istek süresince `isLoading` + `isDisabled`; her dalın sonunda açılır. Kilitsiz düğmede
|
|
1607
1619
|
hızlı ikinci dokunuş yeni bir istek ve yeni bir `Idempotency-Key`'dir — sunucu ikisini de uygular (ölçüm: 1,6 sn'de
|
|
1608
1620
|
üç dokunuş, üç kayıt).
|
|
@@ -1613,8 +1625,9 @@ AYNI; veri bağlantı gelince Zeyrek'e kendiliğinden gider. Altı adım birlikt
|
|
|
1613
1625
|
`refresh` olayı yükleme akışına bağlıdır — bağlantı dönünce açık ekranın mount akışı kendiliğinden koşmaz (kök ADR
|
|
1614
1626
|
0025 taslak). İki karar kapanınca bu adım sadeleşir.
|
|
1615
1627
|
|
|
1616
|
-
Fark yalnız
|
|
1617
|
-
|
|
1628
|
+
Fark yalnız iki yerde kalır: giriş (ağ ister) ve hiç açılmamış ekran (cihazda kopyası yok). Yükleme akışı sayfanın
|
|
1629
|
+
`offlineQueueChange` olayına bağlıysa (4. adım) bağlantı dönünce gönderilen kayıt listeden düşmez: gönderim kuyruğu
|
|
1630
|
+
değiştirir, akış yeniden koşar ve kayıt sunucudan gelir (kök ADR 0029). Bağ yoksa kayıt liste aşağı çekilene kadar görünmez. Önizleme ve
|
|
1618
1631
|
`page_screenshot` önbellek ve kuyruk kullanmaz — davranış yalnız cihazda görülür. Tam gövde (iki ekran, beş akış, iki
|
|
1619
1632
|
uç, ayar ve değişken): `ngapps://examples/mobile-offline-seamless`.
|
|
1620
1633
|
|
|
@@ -2035,6 +2048,24 @@ page_schema_patch(ops:[{ op:'set_event', target:'button_1', event:'click', event
|
|
|
2035
2048
|
|
|
2036
2049
|
`flow_create`'e `flowId` verirsen id'yi önceden bilir, iki adımı tek planda kurabilirsin.
|
|
2037
2050
|
|
|
2051
|
+
### Uzun `code` gövdesi ayrı dosyada: `bodyFile` + `$file`
|
|
2052
|
+
|
|
2053
|
+
Kodu JSON dizgisine kaçışlayarak yazma — bir ters tırnak ya da tırnak hatası adımı bozar. Adımları
|
|
2054
|
+
`bodyFile` ile ver; kod alanına `{ "$file": "<yol>" }` yaz, gateway o dosyanın **metnini** oraya koyar
|
|
2055
|
+
(kod çalıştırılmaz, satır sonu korunur, sonuç satır içi gövdeyle aynı doğrulamadan geçer):
|
|
2056
|
+
|
|
2057
|
+
```jsonc
|
|
2058
|
+
// ./akislar/kaydet.steps.json → flow_create(host:'page', hostId, name, slug, bodyFile:'./akislar/kaydet.steps.json')
|
|
2059
|
+
[ { "stepId": "yaz", "name": "yaz", "type": "code", "kind": "normal",
|
|
2060
|
+
"args": { "value": { "$file": "./kaydet.js" } },
|
|
2061
|
+
"branches": [{ "id": "<GUID>", "name": "always", "nextStepId": null }] } ]
|
|
2062
|
+
```
|
|
2063
|
+
|
|
2064
|
+
`./kaydet.js` düz JS'dir. Yol bodyFile'ın klasörüne göre çözülür; göreli `bodyFile` `MCP_INPUT_DIR`'e
|
|
2065
|
+
(yoksa gateway'in çalışma dizinine) göre. Sınırlar: `.js .mjs .ts .txt .sql .html .css`, UTF-8, dosya
|
|
2066
|
+
başına 1 MB, toplam 5 MB, en çok 64 ekleme; eklenen dosyanın içindeki `$file` işlenmez. Yanıtın
|
|
2067
|
+
`bodyFile.included` alanı neyin nereye konduğunu söyler.
|
|
2068
|
+
|
|
2038
2069
|
### Sayfada bağlı akış varken YENİ akış eklenemez — ve çözümü
|
|
2039
2070
|
|
|
2040
2071
|
Bir sayfanın herhangi bir olayı zaten bir akışa bağlıysa, o sayfaya ikinci bir `flow_create`
|
|
@@ -137,6 +137,7 @@ tek dilli uygulamada da geçerlidir: yazı düzeltilince aynı kırılma olur. *
|
|
|
137
137
|
| `colorMap: [{ value: 'Aktif', color: 'success' }]` | `value` kod, görünen yazı `label`'da (çeviri sarmalı olabilir) |
|
|
138
138
|
| `currentItem.title === 'Dokümanlar'` (breadcrumb tıklaması) | Öğeye ek alan: `{ key: 'klasor', title }` → `currentItem.key === 'klasor'` |
|
|
139
139
|
| Sekmeyi ya da hata satırını yazısıyla tanımak | Sekme `key`'i / kimliği (`<tabs>__tab-<key>`), bağlamdaki kod |
|
|
140
|
+
| Yer tutucu metni kaydedip ona bakmak: `title === 'Başlıksız not'` (boş başlıklı notu ayırmak) | Ham alanı boş bırak, karar ham değerde: `!note.title`; gösterim ifadede `{{ note.title \|\| t('note.untitled') }}`. Yer tutucuyu kayda yazmak hem dili kayda gömer hem kararı metne bağlar |
|
|
140
141
|
|
|
141
142
|
Gateway akış, sayfa, layout ve widget yazımında denetler (`flow_*`, `page_schema_patch` / `_validate`, `page_create`,
|
|
142
143
|
`page_update`, `layout_*`, `component_schema_patch`, `component_create` / `_update`):
|
|
@@ -153,7 +154,8 @@ Gateway akış, sayfa, layout ve widget yazımında denetler (`flow_*`, `page_sc
|
|
|
153
154
|
- **TR-006 (warn):** tek kelimelik metin (`'Aktif'`, `'Evet'`, `'Anasayfa'`) biçimden koddan ayrılmaz; projenin varsayılan
|
|
154
155
|
dilindeki sözlük değeriyle ya da sayfanın yazısıyla aynıysa uyarır. Bir kod değeriyse yok sayın.
|
|
155
156
|
- Kaçış: sunucu yalnız metin döndürüyor ve karşılaştırılacak kod yoksa aynı çağrıya `policyOverride: true` — bulgu
|
|
156
|
-
yanıtta uyarı olarak kalır. Kalıcı çözüm sunucudan kod istemektir (`platform_gap_report`).
|
|
157
|
+
yanıtta uyarı olarak kalır. Kalıcı çözüm sunucudan kod istemektir (`platform_gap_report`). Karar kendi verinizdeyse
|
|
158
|
+
(yer tutucu, durum adı) kaçış yerine tablodaki doğru şekle çevirin: kaçış yalnız denetimi susturur, kırılma sürer.
|
|
157
159
|
|
|
158
160
|
Denetim sözdizimseldir: kodun içindeki karşılaştırmayı arar, etiket ve mesaj gibi düz metni kanca saymaz. Sunucu tarafı
|
|
159
161
|
(workflow, microservice, Studio fonksiyonu) denetlenmez — orada da aynı kural geçerlidir.
|