@appaflytech/zeyrek-ai-mcp 0.0.37 → 0.0.41
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/knowledge/componentCatalog.js +457 -156
- package/dist/knowledge/componentEvents.js +49 -8
- package/dist/knowledge/connectDataBind.js +10 -2
- package/dist/knowledge/expressionPrelude.js +1 -0
- package/dist/knowledge/mobileComponentCatalog.js +21 -9
- package/dist/knowledge/mobileComponentEvents.js +21 -1
- package/dist/knowledge/mobileStore.js +75 -0
- package/dist/knowledge/normalize.js +52 -17
- package/dist/knowledge/platform/mobile.js +1 -1
- package/dist/knowledge/platform/mobileRules.js +1 -1
- package/dist/knowledge/platform/web.js +1 -1
- package/dist/knowledge/rules.js +244 -25
- package/dist/knowledge/styleResolve.js +27 -8
- package/dist/knowledge/styleResolveNative.js +4 -2
- package/dist/knowledge/themeResolve.js +1 -1
- package/dist/knowledge/themeSchema.js +2 -1
- package/dist/observability/trace.js +7 -2
- package/dist/platform/capture/actions.js +24 -1
- package/dist/platform/capture/summary.js +4 -1
- package/dist/platform/secrets.js +50 -2
- package/dist/resources/examples/mobile-app-shell-drawer.json +1 -0
- package/dist/resources/examples/mobile-list-detail-form.json +3 -0
- package/dist/resources/guides/component.md +1 -1
- package/dist/resources/guides/entity.md +16 -0
- package/dist/resources/guides/expressions.md +7 -6
- package/dist/resources/guides/merge.md +5 -4
- package/dist/resources/guides/microservice.md +2 -2
- package/dist/resources/guides/middleware.md +3 -1
- package/dist/resources/guides/mobile-publish.md +24 -0
- package/dist/resources/guides/mobile.md +50 -25
- package/dist/resources/guides/page.md +61 -23
- package/dist/resources/guides/theme.md +1 -0
- package/dist/resources/guides/translation.md +41 -1
- package/dist/resources/guides/workflow.md +31 -1
- package/dist/resources/schema-fingerprint.json +3 -3
- package/dist/resources/schemas/GlobalFunctionUpdateResultDto.json +2 -1
- package/dist/resources/schemas/OrganizationProjectDto.json +8 -0
- package/dist/resources/schemas/UpsertMobileStoreSettingsCommand.json +59 -0
- package/dist/tools/applications.js +6 -3
- package/dist/tools/common.js +9 -5
- package/dist/tools/entities.js +41 -4
- package/dist/tools/environments.js +1 -1
- package/dist/tools/flows.js +5 -1
- package/dist/tools/layouts.js +22 -7
- package/dist/tools/mobile.js +15 -1
- package/dist/tools/mobileStore.js +21 -5
- package/dist/tools/pages.js +31 -45
- package/dist/tools/queries.js +1 -1
- package/dist/tools/rows.js +14 -3
- package/dist/tools/screenWrite.js +2 -2
- package/dist/tools/screens.js +39 -0
- package/dist/tools/translations.js +148 -6
- package/package.json +1 -1
|
@@ -45,7 +45,10 @@ export function domSummaryScript(viewportHeight) {
|
|
|
45
45
|
var field = node.querySelector('input, select, textarea');
|
|
46
46
|
var chosen = node.querySelector('.selected-item');
|
|
47
47
|
var holder = field || chosen;
|
|
48
|
-
|
|
48
|
+
// 37 S-23: OTP'nin gizli girdisi type=text'tir ve kod özet satırında açık yazıyordu; sır sinyali
|
|
49
|
+
// tarayıcının otomatik tamamlama ipucudur (one-time-code · current-password · new-password).
|
|
50
|
+
var secret = !!field && (field.type === 'password' || /(^|\\s)(one-time-code|[\\w-]*-password)(\\s|$)/i.test(String(field.getAttribute('autocomplete') || '')));
|
|
51
|
+
var value = !owned(holder) ? '' : (field ? (secret ? '***' : clean(field.value)) : clean(chosen.textContent));
|
|
49
52
|
var media = node.querySelector('img, svg, canvas, video, iframe');
|
|
50
53
|
// İç içe <li> hem grubu hem alt mesajları sayıyordu: tek grup + 2 alt hata 3 görünüyordu (B-7).
|
|
51
54
|
var errorItems = Array.prototype.slice.call(node.querySelectorAll('.form-validation-errors li:not(:has(li))'))
|
package/dist/platform/secrets.js
CHANGED
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Desen `trace.ts`'in eski kopyasından DARALTILDI: `pass` parçası `passed`/`bypass` gibi
|
|
3
3
|
* anahtarları da yakalıyordu. Günlükte zararsızdı, tool yanıtında veri kaybı olurdu.
|
|
4
|
+
*
|
|
5
|
+
* `cookie` yalnız TAM ad olarak eşleşir (g-01, 37 S-24): paketli şemalarda `StepRuleDto.cookieKey`
|
|
6
|
+
* çerezin ADINI taşır; parça eşleşmesi onu `***` yapıp workflow_get → update turunu bozardı (GW-001).
|
|
7
|
+
* `setcookie` de bilinçli dışarıda: workflow adım tipinin adıdır, sır değildir.
|
|
4
8
|
*/
|
|
5
|
-
export const secretKeyPattern = /password|passwd|secret|token|authorization|api[-_]?key|credential|connection[-_]?string
|
|
9
|
+
export const secretKeyPattern = /password|passwd|secret|token|authorization|api[-_]?key|credential|connection[-_]?string|^cookies?$/i;
|
|
6
10
|
/**
|
|
7
11
|
* Sırrın DEĞERİNİ değil adını, yerini ya da türünü taşıyan anahtarlar: `credentialRef`, `tokenUrl`,
|
|
8
12
|
* `apiKeyHeader`, `secretFields`, `credentialKind`. Maskelenirlerse süreç tanımındaki
|
|
@@ -14,18 +18,62 @@ import { looksLikeJwt, rememberToken } from './tokenVault.js';
|
|
|
14
18
|
const maskedValue = '***';
|
|
15
19
|
const maxPaths = 20;
|
|
16
20
|
const maxDepth = 12;
|
|
21
|
+
/**
|
|
22
|
+
* Ad/değer çiftinde `value`'nun yerine geçen ad (g-01, 37 S-24). Debug girdisi isteğin başlık ve
|
|
23
|
+
* çerezlerini `{ expression: 'Authorization', value: 'Bearer …', source, member }` olarak yazar; sırrın
|
|
24
|
+
* adı anahtarda değil kardeş alanda durduğu için anahtar kuralı hiçbirini görmüyordu.
|
|
25
|
+
*
|
|
26
|
+
* `type` ya da `order` taşıyan satır platform TANIMIDIR — `StepQueryParameterDto {name,value,type}`,
|
|
27
|
+
* `StepCustomParameterDto {name,value,order}`, `QueryParameter {name,type,value}`: değer bağlama
|
|
28
|
+
* ifadesidir (`input.token`), maskelenirse get → update turunda `***` geri yazılır (GW-001).
|
|
29
|
+
* `expression` + `value` hiçbir paketli şemada geçmez, o yüzden bu ayrımın dışındadır.
|
|
30
|
+
*/
|
|
31
|
+
export function secretPairName(record) {
|
|
32
|
+
if (!('value' in record))
|
|
33
|
+
return undefined;
|
|
34
|
+
if (typeof record.expression === 'string')
|
|
35
|
+
return record.expression;
|
|
36
|
+
if ('type' in record || 'order' in record)
|
|
37
|
+
return undefined;
|
|
38
|
+
if (typeof record.name === 'string')
|
|
39
|
+
return record.name;
|
|
40
|
+
return typeof record.key === 'string' ? record.key : undefined;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Anahtarsız string yapraktaki kimlik (g-01): `eyJ…` ile başlayan JWT ve `Bearer <jeton>`.
|
|
44
|
+
* `maskSecretText`'ten DAR: o konsol metni içindir ve her üç parçalı noktalı adı JWT sayar —
|
|
45
|
+
* yanıtta `organization.something.localhost` gibi bir adı körleştirirdi. Bearer'dan sonra en az
|
|
46
|
+
* 16 karakter aranır; rehber metnindeki "Bearer olarak" maskelenmez.
|
|
47
|
+
*/
|
|
48
|
+
export function maskSecretLeaf(text) {
|
|
49
|
+
return text
|
|
50
|
+
.replace(/Bearer\s+[A-Za-z0-9._~+/=-]{16,}/gi, `Bearer ${maskedValue}`)
|
|
51
|
+
.replace(/\beyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]*/g, maskedValue);
|
|
52
|
+
}
|
|
17
53
|
function walk(value, path, masked, refs, depth) {
|
|
18
54
|
if (depth > maxDepth)
|
|
19
55
|
return value;
|
|
56
|
+
if (typeof value === 'string') {
|
|
57
|
+
const safe = maskSecretLeaf(value);
|
|
58
|
+
if (safe === value)
|
|
59
|
+
return value;
|
|
60
|
+
if (masked.length < maxPaths)
|
|
61
|
+
masked.push(path);
|
|
62
|
+
if (looksLikeJwt(value) && Object.keys(refs).length < maxPaths)
|
|
63
|
+
refs[path] = rememberToken(value);
|
|
64
|
+
return safe;
|
|
65
|
+
}
|
|
20
66
|
if (Array.isArray(value))
|
|
21
67
|
return value.map((entry, index) => walk(entry, `${path}[${index}]`, masked, refs, depth + 1));
|
|
22
68
|
if (value && typeof value === 'object') {
|
|
23
69
|
const result = {};
|
|
70
|
+
const pairName = secretPairName(value);
|
|
24
71
|
for (const [key, entry] of Object.entries(value)) {
|
|
25
72
|
const child = path === '' ? key : `${path}.${key}`;
|
|
73
|
+
const name = key === 'value' && pairName !== undefined ? pairName : key;
|
|
26
74
|
// Boole değer sır taşıyamaz: `hasExpoAccessToken: true` maskelenince ajan token'ın kayıtlı
|
|
27
75
|
// olup olmadığını öğrenemiyordu (push bildirimleri g-01, 2026-10-02; GW-001 ailesi).
|
|
28
|
-
if (secretKeyPattern.test(
|
|
76
|
+
if (secretKeyPattern.test(name) && !secretNameKeyPattern.test(name) && typeof entry !== 'boolean' && entry !== null && entry !== undefined && entry !== '') {
|
|
29
77
|
if (masked.length < maxPaths)
|
|
30
78
|
masked.push(child);
|
|
31
79
|
// Maskelenen değer bir JWT ise kimlik testinin yolu kapanmasın: jeton kasada tutulur ve
|
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
"catalog": "app-bar, slot ve drawer mobil kataloğun Dalga 1 bileşenleridir. component_list mobil uygulamada bunları döndürmüyorsa katalog henüz yazılmamıştır ve layout_create 422 unknown-type alır (MOB-003).",
|
|
13
13
|
"verify": "Kabuğu layout'a bağlı bir sayfada page_screenshot ile doğrula (mobilde varsayılan device: 'phone').",
|
|
14
14
|
"menuTarget": "app-bar.menuTarget çekmecenin elementId'sidir (nav_drawer); iç rol app_bar__menu düğmesidir — page_screenshot actions:[{ action:'click', target:'app_bar__menu' }] çekmeceyi açar.",
|
|
15
|
+
"drawerButtons": "Çekmecedeki düğmeler ŞABLONDUR: mobile_app_create her sayfa için bir düğme üretir — elementId menu_<slug> (slug'daki tire ve diğer işaretler alt çizgiye döner: ana-sayfa → menu_ana_sayfa), metin pageName. Buradaki iki düğme bu örneğin kendi pages listesine (gorevler, profil) karşılıktır; başka uygulamaya kopyalanmaz (37 S-9).",
|
|
15
16
|
"drawerFlows": "Menü düğmelerine akış ayrı adımda bağlanır: flow_create (host: 'layout', adım redirect { path: '<sayfa slug'ı>' }) → layout_schema_patch set_event { target: 'menu_gorevler', event: 'click' }.",
|
|
16
17
|
"drawerNode": "Çekmece mobil kataloğun `drawer` bileşenidir (kind 'component', çocuk alır): layout'a doğrudan yazılır, prop'ları katalogdakilerdir (`position`, `size`, `title`, `isDismissDisabled`); kapalı doğar, `app-bar` menüsü `isOpen: true` yazarak açar. Katalogda olmayan prop (ör. `width`, `overlay`) ya da seçenek dışı değer (düğmede `variant: 'ghost'` — geçerli olanlar `solid`/`text`/`outline`) layout_create'i 422 ile düşürür.",
|
|
17
18
|
"layoutContract": "Mizanpaj sözleşmesi: üst güvenli alan tek yerde, kabukta (app-bar 'safe-area' edges:'top' içinde; sayfa ikinci pay vermez). Ekran kenarı boşluğu tek değer (16) ve tek yerde: slot'un yatay ve üst margin'i — içerik app-bar'ın 16 dp altından başlar, kabuksuz sayfadaki gibi. shell_root 'shrink' ve padding'siz — düz container olduğu için tip yazılmazsa runtime 10 dp padding verir ve app-bar kenardan kopar.",
|
|
@@ -85,6 +85,7 @@
|
|
|
85
85
|
"type": "view",
|
|
86
86
|
"kind": "component",
|
|
87
87
|
"props": {},
|
|
88
|
+
"blocks": [],
|
|
88
89
|
"children": [
|
|
89
90
|
{
|
|
90
91
|
"elementId": "row_card",
|
|
@@ -118,6 +119,7 @@
|
|
|
118
119
|
"type": "view",
|
|
119
120
|
"kind": "component",
|
|
120
121
|
"props": {},
|
|
122
|
+
"blocks": [],
|
|
121
123
|
"children": [
|
|
122
124
|
{
|
|
123
125
|
"elementId": "lst_tasks_bos",
|
|
@@ -130,6 +132,7 @@
|
|
|
130
132
|
"data": "Kayıt yok"
|
|
131
133
|
}
|
|
132
134
|
},
|
|
135
|
+
"blocks": [],
|
|
133
136
|
"children": []
|
|
134
137
|
}
|
|
135
138
|
]
|
|
@@ -197,7 +197,7 @@ Her şema düğümünde iki sözlük vardır; ikisi de ortak render katmanında
|
|
|
197
197
|
|
|
198
198
|
| `appearance` | Değerler | Anlamı |
|
|
199
199
|
|---|---|---|
|
|
200
|
-
| `heightType` | `auto` · `fixed` · `fill` | `auto`: içerik kadar (önerilen — Kural 1d); `fixed`: `heightString` varsa o, yoksa `blocks.h × 10px`; `fill`: **kalanı doldur** — ebeveynin bıraktığı yer (ızgarada `height:100%`, flex sütununda `flex:1 1 auto`, flex satırında `align-self:stretch`). Tam ekran yerleşim kabuğu bununla kurulur, bkz. `platform_guide('layout')` |
|
|
200
|
+
| `heightType` | `auto` · `fixed` · `fill` | `auto`: içerik kadar (önerilen — Kural 1d); `fixed`: `heightString` varsa o, yoksa `blocks.h × 10px`; `fill`: **kalanı doldur** — ebeveynin bıraktığı yer (ızgarada `height:100%`, flex sütununda `flex:1 1 auto`, flex satırında `align-self:stretch`). Tam ekran yerleşim kabuğu bununla kurulur, bkz. `platform_guide('layout')`. **Mobilde (≤ 767 px) mobil bloğu olmayan her düğüme `height: auto !important` yazılır**: yüksekliği `height` olarak yazılan `fixed` düğüm (flex ya da CSS grid ebeveyni, stretch yokken) içeriğine göre çizilir; stretch'li satırdaki `min-height` ve 12 kolonlu eski ebeveynin `grid-row` yüksekliği bundan bağımsızdır. Yazılı yüksekliği mobilde de istiyorsan (`heightType: 'fixed'` iken) `appearance.className`'e ayrılmış **`ng-keep-height-mobile`** belirtecini yaz (`className: "hero ng-keep-height-mobile"`) |
|
|
201
201
|
| `widthType` | `cover` · `contain` · `auto` · `fixed` · `fill` | İlk ikisi görsel/medya sığdırma. Kalanı **flex (ve eski cssgrid) kabın çocuğunun genişlik modu** (2026-09-01): `auto` içerik kadar, `fixed` `widthString`'den, `fill` kabın tüm genişliği (satırda `flex:1 1 100%`, sütunda `align-self:stretch`). **Yazılmazsa eski 12 kolon `blocks.size.w` payı geçerlidir** — öncelik: `fill` > `fixed` > `auto` > span |
|
|
202
202
|
| `widthString` / `heightString` | CSS uzunluğu | Sabit ölçü; **yalnız ilgili `*Type: 'fixed'` iken okunur** (aksi PAGE-017 uyarısı). Kabul: çıplak sayı (px sayılır) · `px % vw vh vmin vmax rem em ch` birimli değer · `calc(...)`. Negatif/`pt`/`fr`/`auto` GEÇMEZ — platform kaydeder ama panel ve runtime sessizce düşürür (PAGE-018). Eski `widthPx` alanı 2026-09-01'de kaldırıldı |
|
|
203
203
|
| `marginType` / `marginValue` | `normal` · `none` · `custom` + px | Dış boşluk: **yazılmazsa `normal` sayılır** = her kenara 4px (`spacing.ts` `CANVAS_NORMAL_MARGIN`); custom=`marginValue`. Bileşenleri dip dibe dizmek için `none` ŞART — `gap:'none'` olsa bile normal margin komşular arasında 8px bırakır (sıkı dizilim tarifi: `platform_guide('page')` Kural 1f-2) |
|
|
@@ -453,6 +453,8 @@ Companion'ı adından değil **ana entity'nin `localizationEntityId` alanından*
|
|
|
453
453
|
UUIDv7) anahtar doğru kurulmuştur.
|
|
454
454
|
|
|
455
455
|
Gateway bu iki kuralı ENT-006 (PK identity/tip uyumu) ve ENT-007 (enum şekli) olarak denetler.
|
|
456
|
+
`identity: true` olup `properties.generationType` 0/1 taşımayan anahtar ENT-006 **block**'tur: platform
|
|
457
|
+
create'te de update'te de 400 "Please select a valid generation type" döner (38 S-1).
|
|
456
458
|
|
|
457
459
|
> **Çok dilli bir entity TEK adımda silinir.** `entity_delete` ana entity'ye uygulanır, companion
|
|
458
460
|
> da onunla düşer. (Aynı gün daha erken bir ölçümdeki "önce companion'ı sil" adımı artık geçersiz;
|
|
@@ -486,6 +488,20 @@ boş bırakmak geçerli bir tercihtir** (ENT-002 warn'ı bunu hatırlatır, zoru
|
|
|
486
488
|
> yolunda bu çeviriyi **platform** yapar; ham PUT atarken **siz yapmazsanız her güncelleme bağı
|
|
487
489
|
> sessizce siler.**
|
|
488
490
|
|
|
491
|
+
Var olan entity'yi `entity_patch` ile uygulamaya bağlamanın çalışan şekli — yamalanan belge
|
|
492
|
+
`entity_get`'in okuma şeklidir, yol `/applications`, öğe `{ applicationId }`:
|
|
493
|
+
|
|
494
|
+
```jsonc
|
|
495
|
+
// Bağ listesini yazar (var olan bağlar da listede olmalı — replace tam listedir)
|
|
496
|
+
{ "op": "replace", "path": "/applications", "value": [{ "applicationId": "<application id>" }] }
|
|
497
|
+
// Tek bağ ekler
|
|
498
|
+
{ "op": "add", "path": "/applications/-", "value": { "applicationId": "<application id>" } }
|
|
499
|
+
```
|
|
500
|
+
|
|
501
|
+
`/applicationIds` yolu belgede yoktur; çıplak guid öğesi platformda 500 `JsonException`, `{ id }`
|
|
502
|
+
öğesi boş `applicationId` ile yanıltıcı "This record is in use. Related table: 'Applications'" verir
|
|
503
|
+
(37 S-1). Gateway üçünü de yukarıdaki şekle çevirir ve `gatewayNormalized` ile söyler.
|
|
504
|
+
|
|
489
505
|
## İlişkiler (doğru şekil — 57 alanla doğrulandı)
|
|
490
506
|
|
|
491
507
|
```jsonc
|
|
@@ -98,10 +98,10 @@ dalından devam eder.
|
|
|
98
98
|
| Global | İçerik |
|
|
99
99
|
|---|---|
|
|
100
100
|
| `Page` | `Page.user`, `Page.variables`, `Page.components`, `Page.properties`, `Page.queryParams`, `Page.authorized(roles)` |
|
|
101
|
-
| **Komponent adları** | Her komponent adıyla doğrudan değişkendir: `specialties_table.rows = data`
|
|
101
|
+
| **Komponent adları** | Her komponent adıyla doğrudan değişkendir: `specialties_table.rows = data` prop'u yazar ve UI'ı günceller; `setter` adımı gibi bileşenin `change` akışını **koşturmaz** — `change` akışı yalnız kullanıcı etkileşiminde koşar (2026-10-08; öncesindeki runtime her kod yazımında `change` akışını koştururdu). Addaki `-` silinir: `tickets-table` → `ticketstable` |
|
|
102
102
|
| `state.page` / `state.app` | Sayfa/uygulama state'i (oku/yaz): `state.page.selectedId = row.id`. **Bellekte yaşar** — sayfa her açıldığında tanımlı `value`'dan başlar; mobil uygulamada kalıcı olması için değişkene `storage` verilir (`platform_guide('mobile')` §Kalıcı state). **Tanımlı değişkenler de buradadır:** `variable_create` ile yazılan her `key` sayfa düzeyinde `state.page.<key>`, uygulama düzeyinde `state.app.<key>` olur; `value` nesneyse alanlarına inilir (`state.page.MyTest.isActive`) |
|
|
103
|
-
| `Steps` | Adım çıktıları: `Steps.load_specialties` — anahtar adımın **ADIdır**, `stepId` değil (`setStorage(task.name, …)`); `Steps.<adım>.error` o adımın
|
|
104
|
-
| `self` · `item` · `Row` / `RowIndex` | Tetikleyen komponent (yazılabilir) · repeater/tablo satırı (yazmak YALNIZ o satırı günceller). Satır AKIŞINDA (satırdaki düğümün olayı) ARC-121 düzeltmesini (2026-09-27) taşıyan runtime gerekir, eskisinde boştu — sürümden bağımsız yol `<liste>.selectedItem` / `.selectedIndex`. Tekrarlayıcının KENDİ `change`/`select` akışında satır bağlamı yoktur. `item.x = …` yazımı
|
|
103
|
+
| `Steps` | Adım çıktıları: `Steps.load_specialties` — anahtar adımın **ADIdır**, `stepId` değil (`setStorage(task.name, …)`); `Steps.<adım>.error` o adımın hatası: `{ status, message, body }` — `message` ucun kendi mesajı (`err.reason`, boşsa HTTP durum metni), `status` HTTP kodu, `body` ucun hata gövdesi (`microservice.ts` `.catch`; tablo `platform_guide('page')`). `Steps.error` ise hata akışı için doldurulur (`onError` koşmadan önce `setStorage('error', …)` yapar) ve **yalnız orada okunur**: storage sayfa başına tek `FlowMachine`'de yaşar, ama dışarıdan başlayan her akış başlarken `'error'`'u ve kendi adımlarının önceki değerlerini temizler (`flow-engine.ts` `run`, 2026-09-25) — normal akışta `Steps.error` boştur; hata akışı ve onun tetiklediği akış (`nested`) onu görür ve şekli `{ message, code, step: { id, name, type, data } }`'dir: kullanıcıya gösterilecek metin `Steps.error.message`'tır (`err.response.data.message`, boşsa durum metni). **Başarılı bir adımda `Steps.<adım>.data` gerçek bir `Array`'dir** — `.map` · `.filter` · `.concat` · `.length` çalışır. Adım HATA dönerse `Steps.<adım>.data` `null`'dır, gövde `.error.body`'dedir; aşağıdaki kutuya bakın |
|
|
104
|
+
| `self` · `item` · `Row` / `RowIndex` | Tetikleyen komponent (yazılabilir) · repeater/tablo satırı (yazmak YALNIZ o satırı günceller). Satır AKIŞINDA (satırdaki düğümün olayı) ARC-121 düzeltmesini (2026-09-27) taşıyan runtime gerekir, eskisinde boştu — sürümden bağımsız yol `<liste>.selectedItem` / `.selectedIndex`. Tekrarlayıcının KENDİ `change`/`select` akışında satır bağlamı yoktur. `item.x = …` yazımı da aynı kuraldadır: o satırı günceller, tekrarlayıcının `change` akışını koşturmaz |
|
|
105
105
|
| `event` | `{name, data, source: {id, props}}` |
|
|
106
106
|
| `cookie` | `cookie.get(name)` / `cookie.set(name, value, {expiresInHours, path})` / `cookie.remove(name)` — tarayıcı cookie'si |
|
|
107
107
|
| `setComponentProperty(name, key, value)` / `getComponentProperty(name, key)` | fonksiyon alternatifi |
|
|
@@ -116,9 +116,10 @@ dalından devam eder.
|
|
|
116
116
|
> **`.map is not a function` bir dizi sözleşmesi sorunu DEĞİL, düşmüş bir adımdır.**
|
|
117
117
|
> Ölçüldü 2026-09-05 (`f09-repro/probe`, yerel kurulum): başarılı bir microservice adımında
|
|
118
118
|
> `Steps.<adım>.data` `[object Array]`'dir, `Array.isArray` true döner ve `.map`/`.filter`/
|
|
119
|
-
> `.concat` çalışır. Adım hata döndüğünde (401, 500, geçersiz parametre)
|
|
120
|
-
> **hata gövdesini**
|
|
121
|
-
> `(Steps.x.data || []).map is not a function` tam olarak burada
|
|
119
|
+
> `.concat` çalışır. Adım hata döndüğünde (401, 500, geçersiz parametre) eski runtime aynı alana ucun
|
|
120
|
+
> **hata gövdesini** yazıyordu — o bir nesnedir, dizi metodları yoktur ve
|
|
121
|
+
> `(Steps.x.data || []).map is not a function` tam olarak burada çıkıyordu. Bugünkü runtime `data`'yı
|
|
122
|
+
> `null` yazar ve gövdeyi `Steps.<adım>.error.body`'ye taşır (`|| []` koruması artık yeter). 2026-09-05 koşusunda
|
|
122
123
|
> boş kalan şirket filtresinin sebebi buydu: adım korumalı bir uca 401 ile düşmüştü.
|
|
123
124
|
> **Teşhis sırası:** önce `Steps.<adım>.error`'a bak, sonra ifadeyi suçla. Bir dizi bekliyorsan
|
|
124
125
|
> `Array.isArray(Steps.<adım>.data)` ile koru; boş listeye düşmek, hata gövdesi üzerinde
|
|
@@ -60,14 +60,15 @@ Studio'nun runtime rotasının üç şeridi vardır ve **ortamı URL belirler**:
|
|
|
60
60
|
|---|---|
|
|
61
61
|
| `production` | `/view/{proje}/{app}/{sayfa}` |
|
|
62
62
|
| `development` | `/dev/{proje}/{app}/{sayfa}` |
|
|
63
|
-
| dal | `/branch/{
|
|
63
|
+
| dal | `/branch/{slug}/{proje}/{app}/{sayfa}` — slug, adın `/` yerine `-` taşıyan hâli (`feature/odeme` → `feature-odeme`) |
|
|
64
64
|
|
|
65
65
|
Sayfanın **bütün** istekleri o dala gider: sayfa şeması da, akışların uç çağrıları da. Yani kare
|
|
66
66
|
"yarı doğru" değildir — gördüğünüz ekran dalın gerçek ekranıdır.
|
|
67
67
|
|
|
68
|
-
Reddedilen tek şey **dal adı olamayacak bir ad**: harfle başlamayan, küçük harf/rakam/alt çizgi
|
|
69
|
-
dışında karakter taşıyan, 3-31 aralığının dışına çıkan ya da rezerve bir
|
|
70
|
-
`pg_*` …). Projedeki dalları `environment_list` verir
|
|
68
|
+
Reddedilen tek şey **dal adı olamayacak bir ad**: harfle başlamayan, küçük harf/rakam/alt çizgi/tire
|
|
69
|
+
ve `/` ile ayrılmış parçalar dışında karakter taşıyan, 3-31 aralığının dışına çıkan ya da rezerve bir
|
|
70
|
+
ad (`public`, `api`, `pg_*` …). Projedeki dalları ve slug'larını `environment_list` verir; tenant
|
|
71
|
+
çağrılarına `environment` olarak adı da slug'ı da geçebilirsiniz.
|
|
71
72
|
|
|
72
73
|
2026-09-09 öncesinde bu çağrı **reddediliyordu**: runtime rotası dal adı taşımıyordu, ortam
|
|
73
74
|
modül içi bir store'dan geliyordu ve dal adıyla istenen kare development'ı gösterirdi. O günlerin
|
|
@@ -25,7 +25,7 @@ söyler. Hiçbir kural zorlamaz — ucun gerçek bir tuzağı yoksa alanı hiç
|
|
|
25
25
|
- **Sessiz boş dönüş** — "filtre eşleşmezse 404 değil boş dizi döner"
|
|
26
26
|
|
|
27
27
|
```jsonc
|
|
28
|
-
{ "name": "urun-listesi", "httpMethod": "
|
|
28
|
+
{ "name": "urun-listesi", "httpMethod": "get",
|
|
29
29
|
"description": "Aktif ürünleri ada göre sıralı döner.",
|
|
30
30
|
"caution": "Sayfalama yok; katalog büyüdükçe yanıt boyutu doğrusal büyür." }
|
|
31
31
|
```
|
|
@@ -515,7 +515,7 @@ hem `joins` ile getirin hem `selectFields`'a koyun; ondan sonra `filters[0][fiel
|
|
|
515
515
|
çalışır. m2m alanlar da desteklenir (`EXISTS` alt sorgusuna çevrilir).
|
|
516
516
|
|
|
517
517
|
> **Expression filtresi istek parametresi TAŞIYAMAZ.** Microservice'te expression'lar
|
|
518
|
-
> `workflowContext.
|
|
518
|
+
> `workflowContext.CreateJavascriptEngine()` ile eval edilir ve o bağlamda `input` global'i
|
|
519
519
|
> **yoktur** — `value: "input.doctor_id"` yazarsan uç `500 JavaScriptException: input is not
|
|
520
520
|
> defined` verir (`input` workflow table step'inin sözlüğüdür, microservice'in değil).
|
|
521
521
|
> Expression'ın okuyabildiği iki şey vardır: **sabit değerler** (`"'Aktif'"`, `new Date()`) ve
|
|
@@ -52,7 +52,7 @@ cevabı adımın ifadesinde gizlidir. Ayrıntı ve yazım ölçütü: `platform_
|
|
|
52
52
|
3. **Değişkenler istek boyunca yaşar.** `set-user` / `variable` step'lerinin kurduğu
|
|
53
53
|
değişkenler istek kapsamındaki `WorkflowContext`'e yazılır (`AddVariable`) ve her akış
|
|
54
54
|
koşusunun JS motoru o anki değişkenlerden kurulur (`WorkflowRunner` →
|
|
55
|
-
`
|
|
55
|
+
`CreateJavascriptEngine()`). Hedef microservice'in **expression filtrelerinde/alanlarında**
|
|
56
56
|
ve step ifadelerinde doğrudan okunur (`user.id`, `settings.auth_method`). Ayrıca microservice
|
|
57
57
|
yolunda middleware'in response gövdesindeki `user` / `data` anahtarları da değişkene
|
|
58
58
|
terfi eder (`ExecuteMicroserviceBehavior.cs:105` — sayfa yolunda bu terfi YOKTUR).
|
|
@@ -297,6 +297,8 @@ değişkenlerini `Page.variables` olarak taşır; tarayıcıda ve cihazda okunur
|
|
|
297
297
|
**`rule.isPrivate: true`** ver (Studio: "Sunucuda tut"). Aynı istekteki sonraki middleware'ler
|
|
298
298
|
onu okumaya devam eder; istemciye gitmez. Sayfanın akışları istemcide koşar ve `Page.variables`'ı
|
|
299
299
|
okur — işaretli değişken orada `undefined`'dır; akışın ihtiyaç duyduğu alanı işaretleme.
|
|
300
|
+
`IsPrivate` değişken Studio otomatik tamamlamasında önerilmez ve `workflow_middleware_variables`
|
|
301
|
+
çıktısında da yer almaz (sonraki middleware'in editörü dahil).
|
|
300
302
|
- İstemcinin gerçekten ihtiyaç duyduğu alanları ayrı, işaretsiz bir değişkende **izin listesiyle**
|
|
301
303
|
kur (yukarıdaki `settings`). Aynı ad sonra işaretsiz bir adımla yeniden yazılırsa istemciye açılır.
|
|
302
304
|
- Yalnız microservice uçlarına bağlı middleware'de değişken yanıtla dönmez; işaret yine de zararsızdır.
|
|
@@ -70,6 +70,7 @@ metni yeni numarayla yeniden dener.
|
|
|
70
70
|
| P-10 | `manual declarations_pending` | kullanıcı onayıyla `mobile_store_update { declarations }` (engellemez) |
|
|
71
71
|
| P-11 | push (yalnız `testflight` / `internal`): `warn push_incomplete` (kimlik eksik — derleme push'suz çıkar); `push_unreadable` (kimliğin şifresi çözülemiyor), `push_profile_stale` (profil APNs anahtarından önce üretilmiş), `push_firebase_mismatch` (`google-services.json` başka Firebase projesinin), `push_package_mismatch` (dosya yüklendikten sonra Android paket adı değişmiş), `push_shell_outdated` (kabuk paketi push taşımıyor) | Studio › Yayın › Hesaplar ve kimlik bilgileri (insan yükler); `push_profile_stale`: profili yeniden üret; `push_shell_outdated`: platform ayarı (destek). Push kapalı ya da hazırsa satır `ok`'tur ve dryRun'da görünmez — hazır olduğunu `mobile_store_get { manifest: true }` → `manifest.push` söyler |
|
|
72
72
|
| P-12 | izinler: `warn permissions_shell_outdated` (yayındaki kabuk izin kümesini taşımıyor: sabit izinler bildirilir, kamera çalışmaz, kipler uygulanmaz — başka P-12 satırı çıkmaz); kabuk taşıyorsa `fail permission_off_in_use` (kip `off` ama production o izni yazılı olarak kullanıyor) ya da `warn launch_permissions_shell_outdated` (açılışta sorulan izin var, kabuk açılışta sormuyor; kabuk kümeyi de taşımıyorsa bu cümle ilk uyarının iletisine eklenir) | `mobile_store_update { settings: { permissionModes } }` ya da kullanımı kaldırıp dağıtım; açılış uyarısında daha yeni kabuk ya da izni akış adımından isteme |
|
|
73
|
+
| P-13 | reklam (yalnız `ads.enabled`): `warn ads_app_id_missing` (bu platformun AdMob App ID'si yok — derleme reklamsız çıkar), `warn ads_shell_outdated` (yayındaki kabuk reklam taşımıyor), yükleme hedefinde `manual ads_test_mode` (test kipi açık: mağazada test reklamı görünür), değilse `ok ads_ready` (yerleşim sayısıyla) | `mobile_store_update { settings: { ads } }`; test kipini yayından önce `testMode: false` |
|
|
73
74
|
|
|
74
75
|
Satırın `message` ve `fixHint`'i backend'den, istek dilinde gelir.
|
|
75
76
|
|
|
@@ -98,9 +99,32 @@ sayfa · bileşen ya da akış adımı), `build` (production: `carried`, `reason
|
|
|
98
99
|
taşımayan son mağaza derlemelerini (TestFlight / Play iç test) listeler; izin kullanılıyorsa yanıt
|
|
99
100
|
`permissionWarnings` taşır — production kullanıyorsa yeni derleme şimdi, yalnız okunan ortam kullanıyorsa
|
|
100
101
|
dağıtımdan sonra gerekir.
|
|
102
|
+
- **İzleme izni (`tracking`, iOS ATT, kök ADR 0011):** derleme taşıyorsa App Store Connect App Privacy'de "Device
|
|
103
|
+
ID — Used to Track You", Play Console Data safety'de "Advertising ID" beyanı insanın işidir; izin penceresinden önce
|
|
104
|
+
teşvik ya da kendi ön pencerenizi göstermeyin (App Store 5.1.2). Görünüm bunu `permissionWarnings`'te söyler
|
|
105
|
+
(`MOB-027`). Taşınmayan ikiliye `expo-tracking-transparency` hiç bağlanmaz. Reklam açıksa izin, App ID'si olan platformun derlemesinde taşınır (yalnız Android'de reklam varsa iOS ikilisi ATT taşımaz).
|
|
101
106
|
- Tarama `setter` ile yazılan tetikleyici prop'u da sayar; `code` adımı, fonksiyon gövdesi, `webview` / `html`
|
|
102
107
|
içeriği ve dış bileşen görünmez — onlar için kip `on`.
|
|
103
108
|
|
|
109
|
+
## Reklamlar
|
|
110
|
+
|
|
111
|
+
Google Mobile Ads (kök ADR 0012) mağaza ayarıdır: `settings.ads = { enabled, iosAppId, androidAppId, testMode,
|
|
112
|
+
consentForm, appOpenUnit, units: [{ name, format, ios, android }] }`. App ID `ca-app-pub-…~…`, birim kimliği
|
|
113
|
+
`ca-app-pub-…/…`; `name` `^[a-z][A-Za-z0-9]{0,39}$` ve tekil (bileşen `adBanner.unit` ve adım `ad-show.value` bu adla
|
|
114
|
+
anar); `format` `banner` · `interstitial` · `rewarded` · `rewardedInterstitial` · `appOpen`; bir platformun kimliği boşsa
|
|
115
|
+
yerleşim o platformda yoktur. Biçim hatası `MOB-022` ile yazılmadan durur.
|
|
116
|
+
|
|
117
|
+
- SDK ikiliye yalnız `enabled` iken ve o platformun App ID'si varken bağlanır; o platformun derlemesine `tracking`'i de taşıtır (kip
|
|
118
|
+
`off` olamaz, 422).
|
|
119
|
+
- `testMode: true` (varsayılan) gerçek birim yerine Google test birimini gösterir — geliştirirken gerçek birime
|
|
120
|
+
tıklamak AdMob hesabını kapattırabilir. Yayından önce `false` (P-13 `manual`).
|
|
121
|
+
- `consentForm: true` (varsayılan) açılışta AdMob UMP rıza formunu gerekiyorsa gösterir; rıza mesajının kendisi AdMob
|
|
122
|
+
konsolunda kurulur (insan). Sıra: rıza → açılış izinleri (ATT) → SDK başlatma; rıza reklama izin vermezse uygulama
|
|
123
|
+
reklamsız sürer.
|
|
124
|
+
- `appOpenUnit` `appOpen` biçimli bir yerleşimin adı; soğuk açılışta bir kez gösterilir.
|
|
125
|
+
- `mobile_store_get` reklam açıkken `adsWarnings` döner (`MOB-026`): test kipi, App ID'si olan platformda birimi olmayan
|
|
126
|
+
yerleşim, konsol beyanları (App Privacy, Data safety, AdMob rıza mesajı — insanın işi).
|
|
127
|
+
|
|
104
128
|
## Hata kodları
|
|
105
129
|
|
|
106
130
|
`mobile_build_status` → `error.code`.
|
|
@@ -37,7 +37,7 @@ Mobil uygulamanın bileşen kataloğu ayrıdır (backend satırları `(slug, typ
|
|
|
37
37
|
mobil farkları: düşen prop, değişen etkileşim, mobile özgü prop notları) ve `mobileNotes` (bütün
|
|
38
38
|
mobil bileşenlere geçen notlar). Web'de karşılığı olmayan bileşende (`app-bar`, `safe-area`,
|
|
39
39
|
`scroll-view`, `choice-chips`, `tooltip`, `markdown`, `calendar`, `stack`, `list-item`, `fab`, `timer`,
|
|
40
|
-
`audio`, `lottie`, `webview`, `map`, `grouped-list`, `floating-container`, `skeleton`, `photo-viewer`) bilgi yalnız
|
|
40
|
+
`audio`, `lottie`, `webview`, `map`, `adBanner`, `grouped-list`, `floating-container`, `skeleton`, `photo-viewer`) bilgi yalnız
|
|
41
41
|
`mobileKnowledge`'tadır. Olay yükleri
|
|
42
42
|
(`events[].payload`) önce mobile özgü sözleşmeden okunur (`app-bar.clickAction`, `repeater.swipe`,
|
|
43
43
|
`choice-chips.change`…), yoksa web'inkinden.
|
|
@@ -91,6 +91,11 @@ mobil bileşenlere geçen notlar). Web'de karşılığı olmayan bileşende (`ap
|
|
|
91
91
|
OpenStreetMap): görünüm `latitude`/`longitude`/`zoom` sayılarıdır ve akış yazınca harita oraya gider;
|
|
92
92
|
kullanıcı haritayı hareket ettirince `regionChange { latitude, longitude, zoom }` düğüme geri yazılır,
|
|
93
93
|
işaretçiye dokunmak `markerClick { markerId }` yayar.
|
|
94
|
+
`adBanner` mobile özgüdür (Google Mobile Ads banner'ı, kök ADR 0012): `unit` mağaza ayarındaki reklam
|
|
95
|
+
yerleşiminin **adıdır** (birim kimliği değil), `size` `banner` 320×50 · `largeBanner` · `mediumRectangle` 300×250 ·
|
|
96
|
+
`fullBanner` · `leaderboard` · `adaptive` · `inlineAdaptive`. Reklamı yalnız mağaza sürümü çizer; kanvas, önizleme ve
|
|
97
|
+
oynatıcı boyutlu yer tutucu çizer ve çalışma kipinde `fail { code: 'unavailable' }` yayar. Mağaza sürümünde reklam
|
|
98
|
+
hazır değilken (rıza, SDK) yer kaplamaz; yerleşim yoksa ya da yüklenemezse `fail` yayar ve yer kaplamaz.
|
|
94
99
|
Dördüncü adım: `datepicker`, `timepicker` ve `datetimepicker` web ikizleridir — kutuya dokununca cihazda
|
|
95
100
|
işletim sisteminin seçicisi (iOS alttan açılan sayfa, seçim Tamam'la yazılır; Android iletişim kutusu),
|
|
96
101
|
önizlemede tarayıcının seçicisi açılır; değer ve `change` yükü web ile aynıdır. Sistem seçicisi tek tek
|
|
@@ -174,12 +179,12 @@ istekle gider.
|
|
|
174
179
|
Gateway alan tipini şemadan bilemediği için uyarı metinseldir: `.value` ile biten en az üç parçalı
|
|
175
180
|
yol (`urlInput.value` gibi bileşen değeri ve `fileInput1.value.value` uyarı almaz).
|
|
176
181
|
|
|
177
|
-
Workflow'un
|
|
178
|
-
|
|
182
|
+
Workflow'un `file-upload` adımı da aynı nesneyi döndürür, adres `<adım>.data.absolute_path`
|
|
183
|
+
(`platform_guide('workflow')`). `page_screenshot` ile listede görseli doğrulayın.
|
|
179
184
|
|
|
180
185
|
## Liste ve boş durumu
|
|
181
186
|
|
|
182
|
-
Mobil `repeater`'ın çocukları TAM İKİ `view`'dır (web
|
|
187
|
+
Mobil `repeater`'ın çocukları TAM İKİ `view`'dır (web'de tek şablon) — satır şablonu ve boş görünüm (ADR
|
|
183
188
|
docs/adr/0005; başka şekil 422 `fixed-views`). Satır yokken boş görünüm bir kez, tam genişlikte
|
|
184
189
|
çizilir; boş ekranı oraya tasarla. `emptyTitle`/`emptyDescription`/`emptyIcon` prop'ları ve kardeş
|
|
185
190
|
`emptystate` yolu 2026-09-30'da kaldırıldı. Mobil `view` çocuklarını sütunda dizer. Tam gövde:
|
|
@@ -199,15 +204,15 @@ bölümleri bileşen render anında çıkarır.
|
|
|
199
204
|
3. **Çocuklar konumsaldır:** çocuk 0 bölüm başlığı, çocuk 1 öğe şablonu; fazlası çizilmez.
|
|
200
205
|
|
|
201
206
|
```json
|
|
202
|
-
{ "elementId": "tasks", "name": "tasks", "type": "grouped-list",
|
|
207
|
+
{ "elementId": "tasks", "name": "tasks", "type": "grouped-list", "blocks": [],
|
|
203
208
|
"props": { "rows": { "type": "code", "ref": "{{ q_tasks.data }}" },
|
|
204
|
-
|
|
205
|
-
|
|
209
|
+
"groupBy": { "type": "static", "data": "dueDate" },
|
|
210
|
+
"itemPressable": { "type": "static", "data": true } },
|
|
206
211
|
"children": [
|
|
207
|
-
{ "elementId": "day_title", "name": "day_title", "type": "text",
|
|
212
|
+
{ "elementId": "day_title", "name": "day_title", "type": "text", "blocks": [],
|
|
208
213
|
"props": { "content": { "type": "code", "ref": "{{ Section.title }} · {{ Section.count }}" } } },
|
|
209
|
-
{ "elementId": "task_row", "name": "task_row", "type": "container", "props": {},
|
|
210
|
-
"children": [ { "elementId": "task_title", "name": "task_title", "type": "text",
|
|
214
|
+
{ "elementId": "task_row", "name": "task_row", "type": "container", "props": {}, "blocks": [],
|
|
215
|
+
"children": [ { "elementId": "task_title", "name": "task_title", "type": "text", "blocks": [],
|
|
211
216
|
"props": { "content": { "type": "code", "ref": "{{ item.title }}" } } } ] } ] }
|
|
212
217
|
```
|
|
213
218
|
|
|
@@ -215,8 +220,8 @@ bölümleri bileşen render anında çıkarır.
|
|
|
215
220
|
`Section` boş nesnedir (`Row` gibi). Öğe repeater satırıyla aynıdır: `Row`, `RowIndex`,
|
|
216
221
|
`{{ item.<alan> }}`; akışta tıklanan satır `tasks.selectedItem`. Şablon çocukları setter hedefi
|
|
217
222
|
değildir — satır dışına (sayfadaki bir metne, modala) yazın.
|
|
218
|
-
4. **Olaylar.** `itemPress { index, row }` (yalnız `itemPressable`; öğe şablonu `
|
|
219
|
-
ise dokunuş onun `click`'ine gider), `swipe { key, index, row }` (`swipeActions`),
|
|
223
|
+
4. **Olaylar.** `itemPress { index, row }` (yalnız `itemPressable`; öğe şablonu `button` ya da `click`'i
|
|
224
|
+
bağlı `list-item` ise dokunuş onun `click`'ine gider — bağsız `list-item` basılamaz), `swipe { key, index, row }` (`swipeActions`),
|
|
220
225
|
`change { selectedIndex }`, `sectionPress` (yük `Section`). `index` `rows` içindeki DÜZ indekstir.
|
|
221
226
|
5. **Yapışkan gün başlığı.** `stickySectionHeaders: true` + `appearance.heightType: 'fixed'` +
|
|
222
227
|
`heightString`. Sayfanın kendisi kaydırıcıdır; `auto` ya da `fill` liste içerik kadar uzar ve başlık
|
|
@@ -236,10 +241,10 @@ düğümü sayfa kök kabının SONUNA koyun (akıştaki yeri boş kalır).
|
|
|
236
241
|
sayılır), `respectSafeArea` (varsayılan açık). Alt eylem çubuğu:
|
|
237
242
|
|
|
238
243
|
```json
|
|
239
|
-
{ "elementId": "action_bar", "name": "action_bar", "type": "floating-container",
|
|
244
|
+
{ "elementId": "action_bar", "name": "action_bar", "type": "floating-container", "blocks": [],
|
|
240
245
|
"props": { "anchor": { "type": "static", "data": "bottom-center" },
|
|
241
|
-
|
|
242
|
-
"children": [ { "elementId": "bar", "name": "bar", "type": "container",
|
|
246
|
+
"fullWidth": { "type": "static", "data": true } },
|
|
247
|
+
"children": [ { "elementId": "bar", "name": "bar", "type": "container", "blocks": [],
|
|
243
248
|
"props": { "direction": { "type": "static", "data": "row" } }, "children": [] } ] }
|
|
244
249
|
```
|
|
245
250
|
|
|
@@ -406,8 +411,8 @@ Motorun kendi adımları (`setter`, `condition`, `microservice`, `code`, `functi
|
|
|
406
411
|
|---|---|
|
|
407
412
|
| `redirect` | yığına iter (göreli yol) ya da dışarıda açar (tam adres) |
|
|
408
413
|
| `navigate` | eski mobil şemaların adı — `redirect`'in takma adıdır; gateway `redirect`'e çevirir |
|
|
409
|
-
| `alert` | tipli uyarı (`success`/`error`/`info`/`warning`) toast'tur ve beklemez; düz uyarı onay bekler. Eylem düğmeli bildirim ("Silindi · Geri al") `alert`le kurulmaz: sayfaya bir `toast` bileşeni koyun (`actions: [{ text, flow }]`, gerekirse `keepOpen`), akış setter'la `isOpen: true` yazar; basış eylemin kendi akışını koşar (`actionClick { text, index }`) |
|
|
410
|
-
| `confirm` | `yes` / `no`
|
|
414
|
+
| `alert` | tipli uyarı (`success`/`error`/`info`/`warning`) toast'tur ve beklemez; düz uyarı onay bekler. `message`/`description` metin şablonudur (`"{{ t('kayit.tamam') }}"`; `platform_guide('page')` §alert). Eylem düğmeli bildirim ("Silindi · Geri al") `alert`le kurulmaz: sayfaya bir `toast` bileşeni koyun (`actions: [{ text, flow }]`, gerekirse `keepOpen`), akış setter'la `isOpen: true` yazar; basış eylemin kendi akışını koşar (`actionClick { text, index }`) |
|
|
415
|
+
| `confirm` | `yes` / `no` dalları; `message`/`title`/`confirmText`/`cancelText` metin şablonudur (`{{ }}`, çeviri `t('anahtar')`) |
|
|
411
416
|
| `download` | adres cihazın açma sayfasında açılır; ikili gövde (JSON değil) önbelleğe dosya olarak yazılıp paylaşım sayfasında (`expo-sharing`) açılır, Studio mobil önizlemesi dosyayı indirir (`MOB-005`, bilgi) |
|
|
412
417
|
| `storage` | `args.name` `state.page.<key>` / `state.app.<key>` ise o state'i yazar (§Kalıcı state), değilse cihaz deposuna |
|
|
413
418
|
| `setcookie` | güvenli cihaz deposuna (SecureStore) yazar; değer sonraki her istekte `X-Flow-Cookie` başlığıyla gider ve middleware/workflow onu web'deki gibi `cookie.<ad>` olarak okur — `Cookie` başlığıyla gitmez, çünkü iOS onu yerel çerez kavanozuyla virgülle birleştirip bozuyordu (`MOB-005`, bilgi); aynı adlı sunucu çerezi varsa o kazanır. Studio paleti mobilde de sunar |
|
|
@@ -416,10 +421,12 @@ Motorun kendi adımları (`setter`, `condition`, `microservice`, `code`, `functi
|
|
|
416
421
|
| `location` | cihaz konumu (`expo-location`; ilk kullanımda izin sorulur; derleme konumu taşımıyorsa sorulmaz, `error` dalı `status: 'unavailable'`). `args.value` doğruluk, düz metin: `low` · `balanced` · `high` (boş → `balanced`); `args.isRaw: true` → adres çözülmez. Sonuç `Steps.<adım>.data = { latitude, longitude, accuracy, address }`, `address = { country, region, subregion, city, district, street, postalCode }` ya da `null` (Türkiye'de il `region`, ilçe `subregion`, yoksa `city`). İzin reddi `Steps.<adım>.error = { status: 'denied', message }`, okunamayan konum `unavailable`: adımın `error` dalı bağlıysa o koşar (`always` koşmaz), yoksa akışın hata akışı, o da yoksa `always` (`data: null`). Web'de tarayıcı konumu, adres hep `null` |
|
|
417
422
|
|
|
418
423
|
| `notification-permission` | bildirim izni ister (sorulmuşsa yeniden sormaz, kalıcı red yeniden sorulmaz). Sonuç `Steps.<adım>.data.status`: `granted` · `denied` · `undetermined`; red hata değildir, hep `always` |
|
|
419
|
-
| `notification-schedule` | yerel bildirim planlar (`expo-notifications`; uzak push değil). `args.name` kimlik
|
|
420
|
-
| `notification-cancel` | `args.name` kimlikli planlı bildirimi siler, `'*'` hepsini; hep `always` |
|
|
421
|
-
| `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. Red hata değildir, hep `always`. Türler: `mobile_store_get { detail: ['permissions'] }` (`support: supported` satırlarının `key`'i) |
|
|
424
|
+
| `notification-schedule` | yerel bildirim planlar (`expo-notifications`; uzak push değil). `args.name` kimlik ve `args.title` başlık **metin şablonudur** — yalnız `{{ }}` içi çözülür (`'ilac-{{ item.id }}-0800'` kayıt başına ayrı kimlik, `"{{ t('ilac.vakti') }}"` çeviri; `{{ }}` dışındaki ifade çözülmez, FLOW-022); aynı kimlik eskisinin yerini alır. `args.message` gövde (**ifade**), `args.value` zaman (**ifade**): sonucu `'HH:mm'` → her gün, `{ weekday: 1–7, time: 'HH:mm' }` → her hafta (1 = Pazartesi), ISO tarih-saat → bir kez. `args.path` dokununca açılacak sayfanın slug'ı, düz metin — şablon çözmez (+ `args.query` ifade), boşsa uygulama açılır. İzin yoksa `error.status: 'denied'`, geçmiş ya da okunamayan zaman `invalid`: `error` dalı → hata akışı → `always`. Web'de desteklenmez (`unsupported`) — sayfa kapalıyken tarayıcı gösteremez |
|
|
425
|
+
| `notification-cancel` | `args.name` kimlikli planlı bildirimi siler (planlamadaki gibi metin şablonu: `'ilac-{{ item.id }}-0800'`), `'*'` hepsini; hep `always` |
|
|
426
|
+
| `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) |
|
|
422
427
|
| `open-settings` | uygulamanın sistem ayar sayfasını açar; `Steps.<adım>.data.opened`. Argüman almaz |
|
|
428
|
+
| `back` | gezinme yığınında bir geri (iOS geri kaydırmasıyla aynı sonuç; kök ADR 0013). Argüman almaz (yazılan okunmaz), sonuç yazmaz, hata üretmez, hep `always`. Yığının ilk ekranında hiçbir şey yapmaz; Studio önizlemesinde runtime'ın kendi gezinmesi yoksa yine hiçbir şey, web uygulamasında tarayıcının geri'sidir. Formu kaydedip önceki ekrana dönmek için `redirect` yerine bunu kullanın: `redirect` yığına yeni ekran iter, geri kaydırması kaydedilen forma döner. 2026-10-08 öncesi runtime ve backend `back`'i tanımaz |
|
|
429
|
+
| `ad-show` | tam ekran reklam (kök ADR 0012). `args.value` mağaza ayarındaki yerleşimin **adı**, düz metin (`FLOW-039`); biçimi `interstitial` · `rewarded` · `rewardedInterstitial` olmalı. Reklam yüklenip gösterilir, kullanıcı kapatınca `Steps.<adım>.data = { shown: true, reward }` ile sürer — `reward` ödüllü reklamı sonuna kadar izleyende `{ type, amount }`, değilse `null` (ödül ayrı dal değil, `condition` ile okunur). 10 sn'de reklam yoksa, SDK hata verirse, derleme reklam taşımıyorsa ya da rıza yoksa `error.status: 'unavailable'`; tanımsız ya da banner yerleşimi `invalid`: `error` dalı → hata akışı → `always`. Oynatıcı ve web önizlemesi her zaman `unavailable` — reklamsız dalı orada sınayın. Paletten yalnız mobilde |
|
|
423
430
|
|
|
424
431
|
Bildirim adımları paletten yalnız mobil uygulamada sunulur. Tarif — S04 "her gün hatırlat": önce
|
|
425
432
|
`notification-permission` (S03 düğmesi), sonra `timepicker`'ın değeriyle `notification-schedule`
|
|
@@ -434,13 +441,26 @@ kamera/galeri kaynağı izni kendileri de ister — `permission` adımı *ne zam
|
|
|
434
441
|
okumak içindir. `unavailable` bir hata değil, derlemenin o izni taşımadığının haberidir
|
|
435
442
|
(`platform_guide('mobile-publish')` §İzinler).
|
|
436
443
|
|
|
444
|
+
**Reklam tarifi** (kök ADR 0012): (1) `mobile_store_update { settings: { ads: { enabled: true, iosAppId, androidAppId,
|
|
445
|
+
testMode: true, units: [{ name, format, ios, android }] } } }` — reklam, App ID'si olan platformun derlemesine `tracking`'i de sokar (ATT metni
|
|
446
|
+
gider, kip `off` olamaz); (2) banner için sayfaya `adBanner` `unit: '<banner yerleşimi>'`; (3) geçiş/ödüllü reklam için
|
|
447
|
+
düğme akışına `ad-show` `value: '<yerleşim>'`, `error` dalında reklamsız devam; (4) ödül `condition`:
|
|
448
|
+
`Steps.<adım>.data?.reward` doluysa ödülü ver. Açılış reklamı adım değil ayardır (`ads.appOpenUnit`). Sıra cihazda:
|
|
449
|
+
rıza formu → açılış izinleri (ATT) → SDK; iOS'ta izleme izni yoksa reklam kişiselleştirilmeden istenir. Yerleşim adının
|
|
450
|
+
ayarda tanımlı olduğu sayfa/akış yazımında denetlenemez — `mobile_store_get` `settings.ads.units` ve `adsWarnings`
|
|
451
|
+
(`MOB-026`); yayın ayrıntısı `platform_guide('mobile-publish')` §Reklamlar.
|
|
452
|
+
|
|
437
453
|
`style`, `accuracy`, `reverseGeocode` gibi anahtarlar sözleşmede yoktur ve platform onları sessizce düşürür
|
|
438
454
|
(`FLOW-029`). "Haritada aç" için adım yoktur: `map`'in `press` olayı düğüme `mapsUrl` yazar, akış
|
|
439
455
|
`redirect` `isRaw: true`, `path: '<harita>.mapsUrl'` ile açar. Tarif — L05 "mevcut konumumu kullan":
|
|
440
456
|
düğmenin `click`'i → `location` (`always` → setter `il.value = Steps.konum.data.address?.region`, `error` →
|
|
441
457
|
setter hata metni). Uzak push (sunucudan gelen bildirim) ayrı bölümde: §Uzak push.
|
|
442
458
|
|
|
443
|
-
Sayfa ve layout `mount` akışları ilk çizimden sonra, `destroy` akışları sayfa kalkarken koşar.
|
|
459
|
+
Sayfa ve layout `mount` akışları ilk çizimden sonra, `destroy` akışları sayfa kalkarken koşar. Yığındaki
|
|
460
|
+
ve sekmedeki ekran bağlı kalır: geri dönüşte `mount` KOŞMAZ. Listeyi yeniden okumak için sayfanın
|
|
461
|
+
**`focus`** olayı vardır (layout'ta yok): ilk görünüşte `mount`'tan sonra ve her geri dönüşte / sekme
|
|
462
|
+
değişiminde koşar. Bağ: `page_update` `events: [{ name: 'focus', value: <flow id> }]`. Form doldurma
|
|
463
|
+
`mount`'ta, liste okuması `focus`'ta — aynı okuma ikisine bağlanırsa ilk açılışta çift istek gider.
|
|
444
464
|
`Functions.<ad>()` uygulama ve sayfa fonksiyonlarını görür (`platform_guide('functions')`).
|
|
445
465
|
|
|
446
466
|
|
|
@@ -473,7 +493,8 @@ Sunucudan gönderilen bildirim yerel `notification-schedule`'dan ayrıdır. Kana
|
|
|
473
493
|
## Kalıcı state
|
|
474
494
|
|
|
475
495
|
State varsayılan olarak **bellektedir**: sayfa her açıldığında `variable_create`'te yazılan `value`'dan
|
|
476
|
-
başlar, akışın yazdığı değer ekran kapanınca kaybolur
|
|
496
|
+
başlar, akışın yazdığı değer ekran kapanınca kaybolur — `state.app` dahil: sayfalar arası
|
|
497
|
+
taşımak da `storage` ister (38 S-5). Uygulama kapanıp açıldığında kaldığı yerden
|
|
477
498
|
sürmesi gereken değer için değişkene saklama türü verin:
|
|
478
499
|
|
|
479
500
|
| `storage` | Depo | Ne için |
|
|
@@ -511,7 +532,10 @@ variable_create({ applicationId, pageId, variables: [
|
|
|
511
532
|
780x1688 px) çeker; `'tablet'` 820x1180 @2 ya da `{ width, height, scale? }`. `viewport` verilirse
|
|
512
533
|
cihaz yok sayılır (`deviceNote`). Yanıt `applicationType: 'mobile'` ve `platformNote` taşır: kare
|
|
513
534
|
react-native-web çizimidir, cihazdakiyle yapısal olarak (düğüm, sıra, ölçü, renk) aynıdır.
|
|
514
|
-
- Eylemler aynı sözlük: `click`
|
|
535
|
+
- Eylemler aynı sözlük: `click` önce düğümün kendisine (basılabilirse: `role="button"`/`"switch"`/
|
|
536
|
+
`"combobox"`), sonra `<elementId>__press` yüzeyine, en son içteki ilk düğmeye basar — anahtar ve
|
|
537
|
+
onay kutusu kök kimlikle çevrilir, çoklu seçimi `<elementId>__trigger` açar (çipin "kaldır"ına
|
|
538
|
+
basmaz); `set` alana odaklanır, yazar ve odaktan çıkar (`changed` koşar); `submit` runtime
|
|
515
539
|
köprüsüyle gider (`submitVia: 'bridge'`); sekme öğesi `__tabs__item-<sayfa slug'ı>`, çubuk `__tabs`
|
|
516
540
|
özet satırıdır. `select` açılır listeyi açar ve seçeneği değerle ya da görünen metinle seçer
|
|
517
541
|
(liste katmanda açılır, `optionsSeen` kaç seçenek görüldüğünü söyler); `tab` bir `tabs`
|
|
@@ -544,6 +568,7 @@ yaptıkları. Derlenen uygulama
|
|
|
544
568
|
- Uygulama içi tema seçimi (Sistem / Açık / Koyu) bir ayardır, bileşen değil: akış `storage` adımıyla
|
|
545
569
|
`zeyrek:theme-mode` anahtarına `system` · `light` · `dark` yazar (ör. segmentli `choice-chips`'in `change`'i,
|
|
546
570
|
`value: '<çipler>.value'`); barındırıcı anahtarı dinler, tema anında döner ve cihazda kalıcıdır — durum çubuğu da
|
|
547
|
-
izler.
|
|
548
|
-
`
|
|
571
|
+
izler. Seçim yazılmadıysa (ilk açılış) tema **açıktır**; cihazın şeması yalnız `system` açıkça seçilince izlenir.
|
|
572
|
+
Web'in `theme-toggle`'ı mobilde yoktur. Seçili düğme saklanan seçimi `LocalStorage`'dan okur:
|
|
573
|
+
`{{ LocalStorage['zeyrek:theme-mode'] || 'light' }}` (ARC-147).
|
|
549
574
|
- Dar ekran kırılımı (`hideOn: 'mobile'`) mobil uygulamada anlamsızdır: ekran zaten tek boydur.
|