@appaflytech/zeyrek-ai-mcp 0.0.23 → 0.0.24
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +15 -0
- package/dist/config.js +9 -0
- package/dist/index.js +2 -0
- package/dist/knowledge/componentCatalog.js +1 -1
- package/dist/knowledge/mobileComponentCatalog.js +231 -58
- package/dist/knowledge/mobileComponentEvents.js +118 -3
- package/dist/knowledge/normalize.js +16 -6
- package/dist/knowledge/platform/lint.js +110 -3
- package/dist/knowledge/platform/mobileRules.js +64 -8
- package/dist/knowledge/rules.js +56 -7
- package/dist/knowledge/styleResolve.js +49 -3
- package/dist/knowledge/styleResolveNative.js +104 -4
- package/dist/observability/trace.js +1 -1
- package/dist/platform/argumentAliases.js +128 -0
- package/dist/platform/bodyFile.js +103 -0
- package/dist/platform/capture/capture.js +29 -5
- package/dist/platform/capture/console-log.js +80 -0
- package/dist/platform/capture/index.js +1 -0
- package/dist/platform/diagnostics.js +10 -4
- package/dist/platform/http.js +23 -7
- package/dist/platform/responses.js +6 -2
- package/dist/platform/schemaReadMemory.js +60 -0
- package/dist/platform/secrets.js +9 -0
- package/dist/platform/strictArguments.js +68 -12
- package/dist/platform/tokenVault.js +187 -4
- package/dist/resources/guides/expressions.md +1 -1
- package/dist/resources/guides/microservice.md +9 -2
- package/dist/resources/guides/middleware.md +5 -1
- package/dist/resources/guides/mobile.md +150 -14
- package/dist/resources/guides/page.md +146 -39
- package/dist/resources/guides/theme.md +4 -1
- package/dist/resources/guides/workflow.md +7 -1
- package/dist/resources/schema-fingerprint.json +2 -2
- package/dist/server.js +1 -1
- package/dist/tools/datasources.js +1 -1
- package/dist/tools/designSchemaPatch.js +4 -3
- package/dist/tools/externalComponents.js +1 -1
- package/dist/tools/flows.js +70 -4
- package/dist/tools/layouts.js +5 -5
- package/dist/tools/microservices.js +26 -10
- package/dist/tools/pages.js +192 -36
- package/dist/tools/platformInfo.js +4 -1
- package/dist/tools/rows.js +7 -1
- package/dist/tools/schemaOps.js +1 -1
- package/dist/tools/screenWrite.js +4 -3
- package/dist/tools/screens.js +81 -0
- package/package.json +1 -1
|
@@ -57,7 +57,11 @@ cevabı adımın ifadesinde gizlidir. Ayrıntı ve yazım ölçütü: `platform_
|
|
|
57
57
|
Gateway `set-user` adımı olmadan `user` okuyan middleware'i **MW-003** ile uyarır.
|
|
58
58
|
4. **`input` YOKTUR.** Middleware girdisini HTTP bağlamından okur: `cookie`, `header`,
|
|
59
59
|
`queryString`, `body`, `triggerSource` (hangi microservice/sayfa tetikledi: type, name,
|
|
60
|
-
slug, roles, impersonatedUserId).
|
|
60
|
+
slug, roles, impersonatedUserId). `impersonatedUserId` ve start adımının `impersonation`
|
|
61
|
+
dalı yalnız Studio önizlemesindeki doğrulanmış platform kullanıcısı için dolar; anonim ya da
|
|
62
|
+
yetkisiz istekte başlık platformda silinir. `impersonation` dalı bağlı değilse start
|
|
63
|
+
`success`'e düşer — uygulamanın kendi auth yolu koşar (ZM-22, 2026-09-26). Hazır
|
|
64
|
+
fonksiyonlar: `system.validateToken(...)`,
|
|
61
65
|
`system.generateToken(...)`, `crypto.JWT.encode/validate`, `Authorized(user.roles)`,
|
|
62
66
|
`request.url.searchParams`, `GenerateGuid()`, `UtcNow()`, `system.getPageUrl(slug)` —
|
|
63
67
|
**tam sözlük ve gerçek örnekler: `platform_guide('expressions')`**.
|
|
@@ -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`) bilgi yalnız `mobileKnowledge`'tadır. Olay yükleri
|
|
40
|
+
`audio`, `lottie`, `webview`, `map`, `grouped-list`, `floating-container`) bilgi yalnız `mobileKnowledge`'tadır. Olay yükleri
|
|
41
41
|
(`events[].payload`) önce mobile özgü sözleşmeden okunur (`app-bar.clickAction`, `repeater.swipe`,
|
|
42
42
|
`choice-chips.change`…), yoksa web'inkinden.
|
|
43
43
|
|
|
@@ -46,7 +46,7 @@ mobil bileşenlere geçen notlar). Web'de karşılığı olmayan bileşende (`ap
|
|
|
46
46
|
olup mobilde henüz olmayan için ne zaman geleceğini söyler (Dalga 2, Faz 3).
|
|
47
47
|
- **Katalog A** (FlutterFlow paritesi, 2026-09-24) on bileşen ekledi: `badge`, `avatar`, `progress`,
|
|
48
48
|
`radio`, `checkboxgroup`, `choice-chips`, `collapsible-container`, `slider`, `tabs`, `tooltip`;
|
|
49
|
-
`button` mobilde `secondary`/`ghost`/`link` varyantlarını ve `icon` boyunu alır. Tam liste
|
|
49
|
+
`button` mobilde `secondary`/`ghost`/`link`/`tonal` varyantlarını ve `icon` boyunu alır. Tam liste
|
|
50
50
|
`component_list`'tir.
|
|
51
51
|
- **Katalog B** (FlutterFlow paritesi) önce dört prop'la başladı; hepsi YALNIZ mobil şemadadır:
|
|
52
52
|
`scroll-view.stickyHeader` (ilk çocuk dikey kaydırmada üstte kalır), `numberinput.variant: stepper`
|
|
@@ -90,12 +90,26 @@ mobil bileşenlere geçen notlar). Web'de karşılığı olmayan bileşende (`ap
|
|
|
90
90
|
ö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
|
|
91
91
|
saati kapatamadığı için `minTime`/`maxTime`/`disabledTimes` dışındaki seçim izinli en yakın saate yaslanır;
|
|
92
92
|
`datetimepicker` tek kutudur (`timePlaceholder` yok). `page_screenshot` `set` kökte değil
|
|
93
|
-
`<elementId>__input`ta, HTML biçimiyle çalışır (`2026-08-01`, `09:30`, `2026-08-01T09:30`).
|
|
94
|
-
|
|
93
|
+
`<elementId>__input`ta, HTML biçimiyle çalışır (`2026-08-01`, `09:30`, `2026-08-01T09:30`). Tasarım takvim
|
|
94
|
+
istiyorsa (Pazartesi başlangıcı, "Bugün / Dün" çipleri, `24.09.2026`): `datepicker` `presentation: calendar` +
|
|
95
|
+
`quickPicks` + `displayFormat`; aralık için `daterangepicker` (web ikizi, `valueFrom`/`valueTo`, hep takvim
|
|
96
|
+
sayfası). `fileInput` ve `filebutton` de web ikizidir:
|
|
97
|
+
değer web gibi base64 veri URL'sidir — bileşen kendisi yüklemez, kayda Table microservice'in FILE
|
|
98
|
+
parametresiyle yazılır (§Dosya ve fotoğraf); tür/boyut/adet kapısı ve uyarılar web kuralı.
|
|
95
99
|
Seçiciyi mobile özgü `source` seçer — `files` (belge seçici), `library` (fotoğraf arşivi), `camera`,
|
|
96
|
-
`ask` (her seferinde sorar; `filebutton`'ın varsayılanı); fotoğraflar `
|
|
97
|
-
iOS'ta HEIC JPEG'e çevrilir.
|
|
98
|
-
kırpma yoktur.
|
|
100
|
+
`ask` (her seferinde sorar; `filebutton`'ın varsayılanı); fotoğraflar `maxImageDimension` (varsayılan
|
|
101
|
+
1600 px uzun kenar) ile küçültülüp `quality` ile sıkıştırılır ve iOS'ta HEIC JPEG'e çevrilir.
|
|
102
|
+
Önizlemede tarayıcının dosya penceresi açılır (kanvasta alan kilitli); kırpma yoktur.
|
|
103
|
+
- **Liste ve sabit konum** (2026-09-26): `grouped-list` (başlıklı gruplu liste — §Güne göre görev
|
|
104
|
+
listesi) ve `floating-container` (ekranın 9 noktasından birine sabit kap — §Ekrana sabit içerik)
|
|
105
|
+
mobile özgüdür. `scroll-view`'de ilk çocuk dışındaki doğrudan çocuklar `states.isSticky: true` ile
|
|
106
|
+
kaydırırken üstte kalır (Studio panelinde alanı yok; şemaya MCP yazar).
|
|
107
|
+
- **Faz 10** (FlutterFlow D listesi): `datatable` web ikizidir — kolon biçimi, sıralama ve sayfa hesabı web'in
|
|
108
|
+
modülünden, akış sözleşmesi aynı (sıralama/sayfa değişince `sortField`/`sortType`/`offset` düğüme yazılır ve
|
|
109
|
+
`mount` yeniden koşar; satır eylemi `selectedRow`u yazıp eylemin akışını koşturur). Mobile özgü `variant`:
|
|
110
|
+
`table` (varsayılan, yatay kaydırılan tablo; başlığa dokunuş `__sort-<kolon>`) ya da `cards` (satır başına
|
|
111
|
+
kart). Eylemler `__action-<satır>-<sıra>`, `dropdown` kipinde `__more-<satır>` → alttan eylem sayfası;
|
|
112
|
+
sayfa çubuğu `__page-prev`/`__page-next`. Filtre düzenleyicisi, özet satırı ve sabit eylem sütunu yok.
|
|
99
113
|
- Web'e özgü bileşenlerin mobil karşılığı:
|
|
100
114
|
|
|
101
115
|
| Web | Mobilde |
|
|
@@ -109,7 +123,121 @@ mobil bileşenlere geçen notlar). Web'de karşılığı olmayan bileşende (`ap
|
|
|
109
123
|
| `fileUpload` | `fileInput` (alan) ya da `filebutton` (düğme) — sürükle-bırak yok |
|
|
110
124
|
|
|
111
125
|
**Harici bileşen mobilde yoktur** (`MOB-006`): protokol v1 tarayıcı `.mjs` paketidir;
|
|
112
|
-
`external_component_*` araçları mobil uygulamada platforma gitmez.
|
|
126
|
+
`external_component_*` araçları mobil uygulamada platforma gitmez. **Geçici yol `html`** (2026-09-28):
|
|
127
|
+
katalogda karşılığı olmayan küçük görünüm (özel rozet, SVG, biçimli metin) `html` ile konur — içerik
|
|
128
|
+
kendi korumalı belgesinde çizilir (uygulamanın çerezine/jetonuna ulaşamaz), tema `:root` değişkeni
|
|
129
|
+
olarak gelir (`hsl(var(--primary))`, `var(--ng-…)`), boy içeriğe göre ayarlanır, bağlantı sistem
|
|
130
|
+
tarayıcısında açılır. Akışla iki yön: belgede `fireEvent(key, data)` → `actions` satırının akışı,
|
|
131
|
+
`window.ngApps.emit(name, data)` → `message` olayı (`event.name`/`event.data`); akışın `message`
|
|
132
|
+
prop'una yazdığı değer belgede `window.ngApps.on(name, fn)` ile alınır. Aynı köprü `webview`de de var.
|
|
133
|
+
- **Etkileşim yüzeyleri** (2026-09-27): bileşik kart `container` + `click` ile kurulur (mobile-only olay;
|
|
134
|
+
bağlıysa kap runtime'da basılır, içteki düğme kabın `click`ini tetiklemez) — `card` çocuk almaz.
|
|
135
|
+
`button` ikonu (`icon` + `iconPosition: left|right|top`) ve rozeti (`badgeValue`, `'•'` nokta) çizer;
|
|
136
|
+
`iconPosition: top` + `heightType: fixed` + `variant: tonal` dikey hızlı eylem karosudur. `size: icon`
|
|
137
|
+
yalnız glif çizer: `ariaLabel` verin (MOB-013). `icon` `click`/`href` ile basılır, `isDisabled` soldurur.
|
|
138
|
+
`modal`/`drawer` `actions` düğme satırı çizer (`action` olayı, `currentItem`; `type: submit` formu
|
|
139
|
+
gönderir). Dış bağlantı (`href`) sistem tarayıcısında açılır; uygulama içi yol akışın `redirect`'idir.
|
|
140
|
+
|
|
141
|
+
## Dosya ve fotoğraf
|
|
142
|
+
|
|
143
|
+
"Fotoğraf çek → kaydet → listede göster" üç parçadır; yükleme ucu yoktur, dosya kaydın yazıldığı
|
|
144
|
+
istekle gider.
|
|
145
|
+
|
|
146
|
+
1. **Tablo.** Entity'ye `dbType: 'FILE'` + `commonType: 'file'` alanı (ör. `foto`; tekil dosya,
|
|
147
|
+
`relationId` verilmez — `platform_guide('entity')`).
|
|
148
|
+
2. **Yazma ucu.** Table microservice `operation: 'create'` (ya da `upsert`); `fields`'ta alanı
|
|
149
|
+
`{ fieldSlug: 'foto' }` ile listeleyin. Uç gövdede alanın slug'ıyla `type: 'file'` bir parametre
|
|
150
|
+
bekler ve değeri `{ type, value, fileName }` biçiminde okur (`TableFileParameter`): `type: 'file'`
|
|
151
|
+
base64'ü depoya yazar ve kayda dosya kimliğini koyar, `type: 'ref'` var olan dosyayı (`value` =
|
|
152
|
+
dosya id'si) bağlar, `value: null` alanı boşaltır. Dosya alanının DEĞERİ bu biçimdedir — dönüştürmeyin.
|
|
153
|
+
3. **Form sayfası.** `fileInput` (`source: 'camera' | 'library' | 'ask'`) ya da `filebutton` + kaydet
|
|
154
|
+
düğmesi. Düğmenin akışında `microservice` adımı, parametre alanın değerini olduğu gibi taşır:
|
|
155
|
+
|
|
156
|
+
```json
|
|
157
|
+
{ "name": "foto", "type": "file", "source": "body",
|
|
158
|
+
"value": { "type": "property", "ref": { "element": "foto_in", "property": "value" } } }
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
Base64 gövdeyle gider: fotoğraflar istemcide `maxImageDimension` (1600 px) ile küçültülür,
|
|
162
|
+
`maxFileSizeMb` mobilde varsayılan 10'dur — ikisini de sıfırlamayın. Kamera izni reddedilirse alan
|
|
163
|
+
Ayarlar'a yönlendiren bir uyarı gösterir.
|
|
164
|
+
4. **Liste.** `get` ucu FILE alanını `{ id, type: 'ref', value: <dosya id>, absolute_path: <url>,
|
|
165
|
+
fileName }` olarak döndürür. `image.src`, `avatar.src`, `card.image`, `list-item.leadingImage`'a
|
|
166
|
+
**nesnenin kendisini** verin: `{ "type": "code", "ref": "{{ item.foto }}" }` (`.absolute_path` da
|
|
167
|
+
çalışır). `{{ item.foto.value }}` dosya **kimliğidir** (GUID), görsel çizilmez — `MOB-011` uyarır.
|
|
168
|
+
Gateway alan tipini şemadan bilemediği için uyarı metinseldir: `.value` ile biten en az üç parçalı
|
|
169
|
+
yol (`urlInput.value` gibi bileşen değeri ve `fileInput1.value.value` uyarı almaz).
|
|
170
|
+
|
|
171
|
+
Workflow'un dosya yükleme adımı yalnız `{ id }` döndürür (URL yok); görsel adresi kaydı okuyan uçtan
|
|
172
|
+
gelir. `page_screenshot` ile listede görseli doğrulayın.
|
|
173
|
+
|
|
174
|
+
## Güne göre görev listesi
|
|
175
|
+
|
|
176
|
+
Başlıklar altında gruplanmış liste (güne, odaya, kategoriye göre) **tek `grouped-list`** ile kurulur.
|
|
177
|
+
İç içe repeater `PAGE-013` ile reddedilir; `textarea` hilesi de gerekmez — liste düz satır dizisidir,
|
|
178
|
+
bölümleri bileşen render anında çıkarır.
|
|
179
|
+
|
|
180
|
+
1. **Veri.** Düz satırlar (ör. `q_tasks` sorgusu: `title`, `dueDate`, `done`). Sıralama sorguda
|
|
181
|
+
değil bileşende: `sectionOrder` `asc` (varsayılan) · `desc` · `asis`.
|
|
182
|
+
2. **Düğüm.** `rows: { type: 'code', ref: '{{ q_tasks.data }}' }`, `groupBy: 'dueDate'` (noktalı yol
|
|
183
|
+
olabilir: `plant.room`). Tarih/saat değeri cihazın yerel gününe (`2026-09-26`) indirgenir; alanı
|
|
184
|
+
boş satırlar en sonda `otherSectionTitle` (varsayılan `Other`) bölümündedir.
|
|
185
|
+
3. **Çocuklar konumsaldır:** çocuk 0 bölüm başlığı, çocuk 1 öğe şablonu; fazlası çizilmez.
|
|
186
|
+
|
|
187
|
+
```json
|
|
188
|
+
{ "elementId": "tasks", "name": "tasks", "type": "grouped-list",
|
|
189
|
+
"props": { "rows": { "type": "code", "ref": "{{ q_tasks.data }}" },
|
|
190
|
+
"groupBy": { "type": "static", "data": "dueDate" },
|
|
191
|
+
"itemPressable": { "type": "static", "data": true } },
|
|
192
|
+
"children": [
|
|
193
|
+
{ "elementId": "day_title", "name": "day_title", "type": "text",
|
|
194
|
+
"props": { "content": { "type": "code", "ref": "{{ Section.title }} · {{ Section.count }}" } } },
|
|
195
|
+
{ "elementId": "task_row", "name": "task_row", "type": "container", "props": {},
|
|
196
|
+
"children": [ { "elementId": "task_title", "name": "task_title", "type": "text",
|
|
197
|
+
"props": { "content": { "type": "code", "ref": "{{ item.title }}" } } } ] } ] }
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
Başlık `Section` okur (`{ key, title, count, items }`), satır kapsamı taşımaz; bölüm dışında
|
|
201
|
+
`Section` boş nesnedir (`Row` gibi). Öğe repeater satırıyla aynıdır: `Row`, `RowIndex`,
|
|
202
|
+
`{{ item.<alan> }}`; akışta tıklanan satır `tasks.selectedItem`. Şablon çocukları setter hedefi
|
|
203
|
+
değildir — satır dışına (sayfadaki bir metne, modala) yazın.
|
|
204
|
+
4. **Olaylar.** `itemPress { index, row }` (yalnız `itemPressable`; öğe şablonu `list-item`/`button`
|
|
205
|
+
ise dokunuş onun `click`'ine gider), `swipe { key, index, row }` (`swipeActions`),
|
|
206
|
+
`change { selectedIndex }`, `sectionPress` (yük `Section`). `index` `rows` içindeki DÜZ indekstir.
|
|
207
|
+
5. **Yapışkan gün başlığı.** `stickySectionHeaders: true` + `appearance.heightType: 'fixed'` +
|
|
208
|
+
`heightString`. Sayfanın kendisi kaydırıcıdır; `auto` ya da `fill` liste içerik kadar uzar ve başlık
|
|
209
|
+
yapışmaz. Başlık kabına kendi zeminini verin.
|
|
210
|
+
|
|
211
|
+
## Ekrana sabit içerik — FAB, alt eylem çubuğu, üst şerit
|
|
212
|
+
|
|
213
|
+
Mobil şemada `style.position`/`zIndex` yoktur (`MOB-002`); sayfa kayarken yerinde duran içerik iki
|
|
214
|
+
bileşenle kurulur. İkisi de sayfanın neresine konursa konsun runtime'da ekran katmanına taşınır —
|
|
215
|
+
düğümü sayfa kök kabının SONUNA koyun (akıştaki yeri boş kalır).
|
|
216
|
+
|
|
217
|
+
- **Sağ alt tek düğme:** `fab` (`position: bottom-right | bottom-center | bottom-left`, `icon`,
|
|
218
|
+
`click`). Sekme çubuğu varsa onun üstünde, kenardan 16 dp.
|
|
219
|
+
- **Alt eylem çubuğu / birden çok düğme / üst ya da orta çapa:** `floating-container`.
|
|
220
|
+
`anchor` 9 nokta (`top-left` … `bottom-right`, varsayılan `bottom-right`), `offsetX`/`offsetY` (dp,
|
|
221
|
+
varsayılan 16, çapalı kenardan içeri), `fullWidth` (alt/üst orta satırı kaplar; `offsetX` yok
|
|
222
|
+
sayılır), `respectSafeArea` (varsayılan açık). Alt eylem çubuğu:
|
|
223
|
+
|
|
224
|
+
```json
|
|
225
|
+
{ "elementId": "action_bar", "name": "action_bar", "type": "floating-container",
|
|
226
|
+
"props": { "anchor": { "type": "static", "data": "bottom-center" },
|
|
227
|
+
"fullWidth": { "type": "static", "data": true } },
|
|
228
|
+
"children": [ { "elementId": "bar", "name": "bar", "type": "container",
|
|
229
|
+
"props": { "direction": { "type": "static", "data": "row" } }, "children": [] } ] }
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
Kabın kendi yüzeyi yoktur: zemin, köşe, gölge ve iç boşluk içerideki kaba (`surfaceStyle`) verilir.
|
|
233
|
+
Dip sekme çubuğunun tepesidir — `aboveTabBar` diye bir prop YOKTUR. App-bar sayfayla kayar; üst
|
|
234
|
+
çapalı kap app-bar'ın üstüne biner, sabit app-bar'ın altına `offsetY` ile inilir (ör. 72).
|
|
235
|
+
- **Kaydırırken üstte kalan başlık:** `scroll-view` içinde ilk çocuk `stickyHeader: true`, diğer
|
|
236
|
+
doğrudan çocuklar düğüm durumu `states: { isSticky: true }` ile (yalnız dikey; kaydırıcıya `fixed`
|
|
237
|
+
boy verin). Sayfayı örten katman için `modal` / `drawer` widget'ı.
|
|
238
|
+
|
|
239
|
+
`page_screenshot` (viewport karesi, `layoutReport: true`) sabit içeriğin çapalı noktada, sekme çubuğunun
|
|
240
|
+
üstünde durduğunu gösterir; uzun sayfada içerik kabın altından akar.
|
|
113
241
|
|
|
114
242
|
## Yerleşim — yalnız flex
|
|
115
243
|
|
|
@@ -127,6 +255,9 @@ Mobil çizer 12 sütunlu ızgarayı **okumaz**: sıra dizi indeksidir, yerleşim
|
|
|
127
255
|
alır (`container`, `form`, `safe-area`, `scroll-view`) ve akışları oranlı boyu doldurur; öteki
|
|
128
256
|
bırakılabilir tipler, widget örneği, sayfa kökü ve `modal`/`drawer` almaz; `image` oranı kendi
|
|
129
257
|
çizimine yazar.
|
|
258
|
+
- `image` boyu: `heightType: 'fixed'` + `heightString` (`px`/`%`) ya da `'fill'` → görsel kabuğu doldurur
|
|
259
|
+
(`%` ve `fill`de 96 dp taban boyun altına inmez: atası içerik boyundaki kapta yüzde çözülmez); yoksa `aspectRatio` + `heightType: 'auto'` + belli genişlik → boy orandan; ikisi de yoksa 96 dp taban boy
|
|
260
|
+
ve MOB-012 bilgi satırı. Küçük resim için `widthType: 'fixed'` + `widthString` + oran `1` kullanın.
|
|
130
261
|
- Hiza (`verticalAlign` `top | middle | bottom`, `horizontalAlign` `left | center | right`) web ile
|
|
131
262
|
aynıdır. Dikey hizanın boşluğu kabın boyundan gelir: `heightType` `fill`, `fixed` ya da oranlı kapta
|
|
132
263
|
akış kabın boyunu doldurur; boyu içerikten gelen sütun kapta dikey hiza etkisizdir (satır kapta farklı
|
|
@@ -139,7 +270,11 @@ Mobil çizer 12 sütunlu ızgarayı **okumaz**: sıra dizi indeksidir, yerleşim
|
|
|
139
270
|
React Native'de CSS yoktur; tipli stil grupları **derlenir** (web'in `composeTypedStyle` ikizi,
|
|
140
271
|
aynı katman sırası: tema taban → gruplar → serbest `style`).
|
|
141
272
|
|
|
142
|
-
- `surfaceStyle` → zemin, köşe, kenarlık, gölge (tek katman), opaklık, taşma
|
|
273
|
+
- `surfaceStyle` → zemin, köşe, kenarlık, gölge (tek katman), opaklık, taşma; döndürme `rotate`
|
|
274
|
+
(derece), ölçek `scale` (yüzde, 100 aynı boy), kaydırma `translateX`/`translateY` (px). Sıra web
|
|
275
|
+
ile aynı (kaydır → döndür → ölçekle, köken merkez), düğüm çocuklarıyla döner. Değer birimsiz sayıdır
|
|
276
|
+
(`'15deg'` yazılmaz); aralık dışı (`rotate` ±360, `scale` 10–500, kaydırma ±1000) yazılmaz ve
|
|
277
|
+
`nativeStyle.warnings`'e düşer. Katman ve kabukta (`modal`, `drawer`, `app-bar`) bu dört alan yoktur.
|
|
143
278
|
- `typographyStyle` → renk, aile (tek ad), punto, kalınlık, hizalama, satır yüksekliği.
|
|
144
279
|
- Tema token'ları (`var(--ng-*)`) sayılara çözülür; proje teması değişince mobil de değişir.
|
|
145
280
|
|
|
@@ -148,10 +283,11 @@ aynı katman sırası: tema taban → gruplar → serbest `style`).
|
|
|
148
283
|
| Alan | Yerine |
|
|
149
284
|
|---|---|
|
|
150
285
|
| `className` | `surfaceStyle` / `typographyStyle` |
|
|
151
|
-
| serbest `style` nesnesi (alt seçici `&:hover`, `&__body`) | tipli gruplar; düz anahtarlar (ör. `padding`) çevrilir, alt seçici düşer |
|
|
286
|
+
| serbest `style` nesnesi (alt seçici `&:hover`, `&__body`) | tipli gruplar; düz anahtarlar (ör. `padding`) çevrilir, alt seçici düşer. İstisna `html`: metin (ya da koda bağlı) `style` kendi belgesinin CSS'idir ve çizilir; statik nesne `style` orada da düşer |
|
|
152
287
|
| `hoverStyle` | yok — dokunmatikte üzerine gelme durumu yoktur |
|
|
153
288
|
| `tooltip`, `tooltipPosition` | düğümü `tooltip` bileşenine sarın (dokunuşla açılır, yanı `side`) ya da açıklamayı metin olarak koyun |
|
|
154
|
-
| `zIndex` | katman için `
|
|
289
|
+
| `zIndex`, `style.position` | görsel üstüne bindirme (rozet, hap, tarih etiketi, açıklama bandı, arka plan görseli): `stack` — ilk çocuk taban, ötekiler üst katman; her köşe için `fill`×`fill` bir `container` katmanı kendi hizasıyla; görselin altına binen kart: kartın üst boşluğu negatif (`marginValues.top: -28`); ekrana sabit: `floating-container` / `fab`; yapışkan başlık: `scroll-view` çocuğunda `states.isSticky`; örten katman: `modal` / `drawer` (§Ekrana sabit içerik) |
|
|
290
|
+
| `surfaceStyle`'ın tanımadığı alt anahtar (ör. CSS `transform` metni, `padding`) | grubun tipli alanları; döndürme/ölçek/kaydırma `rotate`, `scale`, `translateX`/`translateY` |
|
|
155
291
|
|
|
156
292
|
Düğümün gerçek stilini görmek: `page_runtime_get({ applicationSlug, pageSlug, resolveStyles: true })`
|
|
157
293
|
→ her düğümde `nativeStyle: { shell, text, warnings }` (`shell` kutunun, `text` içine akan metnin
|
|
@@ -217,8 +353,8 @@ Mobil uygulama web ile **aynı sözlüğü ve aynı motoru** kullanır (plans/mo
|
|
|
217
353
|
→ `translation_update`. Kurulum ve fallback zinciri `platform_guide('translation')`.
|
|
218
354
|
|
|
219
355
|
- **Dil değiştirici:** `language-selector` mobil katalogdadır (`component_get` → `mobileKnowledge`):
|
|
220
|
-
`dropdown`un
|
|
221
|
-
(`tr` → Türkçe). Seçim `machine.setLanguage` ile bütün prop'ları yerinde çözer ve cihazda uygulama
|
|
356
|
+
`dropdown`un listesi (az dilde alanın altında, çok dilde alt sayfada), tetikleyici `role="combobox"`,
|
|
357
|
+
seçenekler `displayMode`'a göre dilin yerel adıyla (`tr` → Türkçe), koduyla (`TR`) ya da ikisiyle. Seçim `machine.setLanguage` ile bütün prop'ları yerinde çözer ve cihazda uygulama
|
|
222
358
|
çapında yayılır: öteki sekmelerin ekranları ve sekme etiketleri de değişir, makine yeniden kurulmaz.
|
|
223
359
|
- **Cihazda boot sırası:** uygulama içi seçim → cihazda kayıtlı seçim (`zeyrek:<app>:lang`) → cihaz dili
|
|
224
360
|
→ projenin varsayılanı. `?lang` CİHAZDA YOKTUR; `/preview`'da vardır, bu yüzden `page_screenshot(language:)`
|
|
@@ -243,7 +379,7 @@ Motorun kendi adımları (`setter`, `condition`, `microservice`, `code`, `functi
|
|
|
243
379
|
|---|---|
|
|
244
380
|
| `redirect` | yığına iter (göreli yol) ya da dışarıda açar (tam adres) |
|
|
245
381
|
| `navigate` | eski mobil şemaların adı — `redirect`'in takma adıdır; gateway `redirect`'e çevirir |
|
|
246
|
-
| `alert` | tipli uyarı (`success`/`error`/`info`/`warning`) toast'tur ve beklemez; düz uyarı onay bekler |
|
|
382
|
+
| `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 }`) |
|
|
247
383
|
| `confirm` | `yes` / `no` dalları |
|
|
248
384
|
| `download` | adres cihazın açma sayfasında açılır, gövde paylaşım sayfasına gider (`MOB-005`, bilgi) |
|
|
249
385
|
| `storage` | cihaz deposu |
|
|
@@ -226,7 +226,7 @@ döndürür; hata yok, sayfalama yok. (Bağımsız `pagination` komponenti bu tu
|
|
|
226
226
|
| `dropdown.search` · `multi-select.search` | `{ searchText }` | `self.searchText` |
|
|
227
227
|
| `tabs.tab` | `{ selected }` | `self.selected` ya da `event.data.selected` |
|
|
228
228
|
| `chart.dataPointClick` | `{ name, seriesName, value, dataIndex, row }` | `self.row` (kaynak satırın tamamı) |
|
|
229
|
-
| `repeater.change` · `repeater.select` | `{ selectedIndex }` | `self.selectedIndex` · `self.selectedItem` (akış satır bağlamında koşmaz, `item`/`Row` boş) |
|
|
229
|
+
| `repeater.change` · `repeater.select` | `{ selectedIndex }` | `self.selectedIndex` · `self.selectedItem` (akış satır bağlamında koşmaz, `item`/`Row` boş); satırdaki bir düğmenin akışında setter'ın yazım kapsamı OLAYIN kaynağı değil HEDEFİN ağaçtaki yeriyle belirlenir — satırın şablonu içindeki bir hedefe satır bazlı (`_repeaterValues`), dışındaki bir hedefe (modal, sayfa düzeyi metin) sayfa kapsamında yazılır (ZM-08, 2026-09-26); iç içe repeater zaten `PAGE-013` ile reddedilir, ama bir DIŞ repeater'ın şablonuna İÇ satırdan yazan bir akış varsa yazım dış satırın değil İÇ satırın kimliğiyle anahtarlanır — bilinen açık, PAGE-013 riski düşük tutar |
|
|
230
230
|
| `html` aksiyonu (`fireEvent`) | ikinci argüman | **`event.data`** |
|
|
231
231
|
| `kanban.cardMove` | `{ id, row, from, to }` | **`event.data`** (self'e yazılmaz) |
|
|
232
232
|
| `kanban.cardAdd` · `kanban.cardRemove` | `{ column }` · `{ id, row, column }` | **`event.data`** (self'e yazılmaz; kart eklenmez/gizlenmez, akış yazıp `rows`'u tazeler) |
|
|
@@ -249,7 +249,8 @@ dalı bulamaz ve akışı **sessizce durdurur** (`flow-engine.ts:79`); Studio da
|
|
|
249
249
|
`sourceBranch === 'always'` ile çizdiği için panelde **Start kartından sonrası boş görünür**.
|
|
250
250
|
İstisnalar: `condition` → dal adı rule id'sidir (ve o id bir **GUID**'dir — aşağıdaki tuzak 6),
|
|
251
251
|
`confirm` → `yes|no|always`, `microservice` →
|
|
252
|
-
`always` (eski akışlarda `success` de çalışır)
|
|
252
|
+
`always` (eski akışlarda `success` de çalışır) **+ `error`** — bağlarsan hata anında `always`
|
|
253
|
+
yerine ondan sürer (2026-09-26, ADR 0004; ayrıntı aşağıdaki §"4xx de hatadır").
|
|
253
254
|
|
|
254
255
|
**2. `iteratees[].type` yalnız `change` | `fill`.** `setter.ts` başka değerde hiçbir dala
|
|
255
256
|
girmez — adım çalışır, hiçbir şey yapmaz. `change` tek prop'a değer yazar, `fill` diziyi
|
|
@@ -375,16 +376,22 @@ Satır eşlemeli tablo yüklemesinin tam şekli (yükleniyor bayrağı → uç
|
|
|
375
376
|
göstermek error flow'un işidir.
|
|
376
377
|
|
|
377
378
|
> **4xx de hatadır ve `always` dalı seni KURTARMAZ.** `microservice.ts` `.catch` bloğu
|
|
378
|
-
> reddedilen her istekte koşar — 401/403/404/422 dâhil
|
|
379
|
-
>
|
|
379
|
+
> reddedilen her istekte koşar — 401/403/404/422 dâhil — ve storage'a HER ZAMAN
|
|
380
|
+
> `{ data: null, headers: null, error: { status, message, body } }` yazar (ADR 0004,
|
|
381
|
+
> 2026-09-26 — eskiden hata gövdesi `data`'ya yazılıyordu). `.finally` sonra ÜÇ yoldan birini
|
|
382
|
+
> dener, SIRAYLA:
|
|
380
383
|
>
|
|
381
|
-
> 1. **
|
|
382
|
-
> `
|
|
383
|
-
>
|
|
384
|
-
>
|
|
385
|
-
>
|
|
386
|
-
> `!!Steps.signIn.data.token` gibi bir
|
|
387
|
-
>
|
|
384
|
+
> 1. **Adımın kendi `error` dalı BAĞLIYSA** akış oradan sürer — `always` **hiç koşmaz**
|
|
385
|
+
> (`hasBranch('error')`). Bu dal `Steps.<ad>.error` okuyabilir; ana akışın kalanı hiç çalışmaz.
|
|
386
|
+
> 2. **`error` dalı yoksa, `errorSteps` VARSA** `engine.onError` true döner ve `.finally` `return`
|
|
387
|
+
> eder — yine `always` koşmaz, flow'un hata akışı devralır (aşağıdaki karşılaştırma tablosu).
|
|
388
|
+
> 3. **İkisi de yoksa** `always` koşar, ama `Steps.<ad>.data` artık **NULL**dır — başarı gövdesi
|
|
389
|
+
> değil, hiç gövde değil. Sıradaki bir `condition` `!!Steps.signIn.data.token` gibi bir
|
|
390
|
+
> ifadeyi `null.token` üzerinden dener; `runCode` hatayı yutar (K2), ifade `undefined` döner ve
|
|
391
|
+
> akış sessizce diğer dala düşer — `else` dalı bağlı değilse akış orada biter. `data`yı bir
|
|
392
|
+
> liste prop'una yazan bir setter artık patlayan bir nesne değil `null` görür (repeater/table
|
|
393
|
+
> `null`'ı boş liste sayar), ama hata OLDUĞUNU fark etmeden geçer — gateway bunu **FLOW-028**
|
|
394
|
+
> ile uyarır.
|
|
388
395
|
>
|
|
389
396
|
> **`errorSteps` yalnız `start` taşıyorsa hata yolu YOKTUR:** `hasErrorFlow`
|
|
390
397
|
> `errorTasks.some(t => t.type !== 'start')` arar. Start'ın ardına en az bir adım bağla —
|
|
@@ -393,15 +400,18 @@ göstermek error flow'un işidir.
|
|
|
393
400
|
> **Mobil uygulama:** mobil runtime (`@ng-apps/ui-native` `NativeRuntime`) `errorSteps`'i web gibi
|
|
394
401
|
> `errorTasks`'a eşler — hata yolu iki platformda aynı koşar.
|
|
395
402
|
|
|
396
|
-
> **Hata metni
|
|
403
|
+
> **Hata metni birden çok yerde durur, hepsi aynı şey değildir (2026-09-26 itibarıyla, ADR 0004).**
|
|
397
404
|
> `onError` hata akışını koşturmadan önce `machine.setStorage('error', errorInfo)` yapar
|
|
398
|
-
> (`flow-engine.ts`), yani hata akışının adımları `Steps.error`'ı okuyabilir
|
|
405
|
+
> (`flow-engine.ts`), yani hata akışının adımları `Steps.error`'ı okuyabilir; patlayan adımın
|
|
406
|
+
> KENDİ storage'ı ise (bağlı `error` dalı da `always` da) `Steps.<adımAdı>.error` nesnesini taşır:
|
|
399
407
|
>
|
|
400
408
|
> | İfade | Ne taşır | Kaynak |
|
|
401
409
|
> |---|---|---|
|
|
402
|
-
> | **`Steps.error.message`** | **
|
|
403
|
-
> | `Steps.<adımAdı>.error` |
|
|
404
|
-
> | `Steps.<adımAdı>.
|
|
410
|
+
> | **`Steps.error.message`** | **Yalnız `errorSteps` içinde** — ucun kendi mesajı, `err.reason` → HTTP durum metni → `'Service request failed'`. Yanında `Steps.error.code` · `Steps.error.step.name` (patlayan adım) · `Steps.error.step.data` (ucun hata gövdesi; parametre ifadesi patladıysa `null`) gelir | `microservice.ts` `err.reason \|\| HttpStatusText[err.code]` → `flow-engine.ts` `onError` |
|
|
411
|
+
> | `Steps.<adımAdı>.error.message` | Patlayan adımın KENDİ storage'ında ucun mesajı — `error` dalında da `always`'te de okunur, sıra aynı: `err.reason` → HTTP durum metni → `'Service request failed'` | `microservice.ts` `.catch` |
|
|
412
|
+
> | `Steps.<adımAdı>.error.status` | HTTP durum KODU (sayı) — parametre ifadesi patladıysa `null` | `microservice.ts` `.catch` |
|
|
413
|
+
> | `Steps.<adımAdı>.error.body` | Ucun hata gövdesi — eski `Steps.<adımAdı>.data`'nın taşındığı yer | `microservice.ts` `.catch` |
|
|
414
|
+
> | `Steps.<adımAdı>.data` | **HER ZAMAN `null`**'dır — başarı gövdesi değil, hiçbir gövde değil (eskiden hata gövdesiydi) | `microservice.ts` `.catch` |
|
|
405
415
|
>
|
|
406
416
|
> **`alert` adımının `message`'ı DÜZ METİNDİR — ifade yazarsan ekranda o ifade görünür.**
|
|
407
417
|
> `alert.ts` `task.argument.message`'ı doğrudan `toast.error()`'a geçirir ve `args` hiçbir yerde
|
|
@@ -429,15 +439,24 @@ göstermek error flow'un işidir.
|
|
|
429
439
|
> `{ "type": "confirm", "args": { "message": "Bu risk ve 4 aksiyonu silinecek.", "title": "Riski sil",
|
|
430
440
|
> "confirmText": "Sil", "cancelText": "Vazgeç" } }`.
|
|
431
441
|
|
|
432
|
-
> **Hangi yolu seçeceksin — `errorSteps` mi, ana akışta
|
|
433
|
-
>
|
|
442
|
+
> **Hangi yolu seçeceksin — adımın kendi `error` dalı mı, `errorSteps` mi, ana akışta
|
|
443
|
+
> `condition` mı?** Bir hatada sırayla denenir (yukarıdaki 4xx bölümü) — üçü birden TANIMLI
|
|
444
|
+
> olabilir ama aynı anda yalnız biri koşar: **`error` dalı bağlıysa o kazanır**, yoksa
|
|
445
|
+
> `errorSteps`, ikisi de yoksa `always` + `condition`:
|
|
434
446
|
>
|
|
435
|
-
> | | `errorSteps` | ana akışta `condition Steps.<adımAdı>.error` |
|
|
436
|
-
>
|
|
437
|
-
> |
|
|
438
|
-
> |
|
|
439
|
-
> |
|
|
440
|
-
> |
|
|
447
|
+
> | | adımın kendi `error` dalı (g-02) | `errorSteps` | ana akışta `condition !!Steps.<adımAdı>.error` |
|
|
448
|
+
> |---|---|---|---|
|
|
449
|
+
> | Kapsam | yalnız BU adımın hatası | akıştaki HERHANGİ bir adımın hatası | yalnız BU adımın hatası |
|
|
450
|
+
> | Ana akışın kalanı | **koşmaz** — bu dal devralır | **koşmaz** — hata akışı devralır | koşar, dalı sen seçersin |
|
|
451
|
+
> | Yükleniyor bayrağı · modal kapatma · tablo tazeleme | dalda **yeniden kurmak zorundasın** | hata akışında **yeniden kurmak zorundasın**; kurmazsan bayrak açık kalır | zaten ana akışta duruyor |
|
|
452
|
+
> | Hata metni | `Steps.<adımAdı>.error.message` | `Steps.error.message` (yukarıdaki tablo) | `Steps.<adımAdı>.error.message`; hata olduğunu `!!Steps.<adımAdı>.error` söyler — `.error.status` parametre ifadesi patladıysa `null`dur, o yolu KAÇIRIR |
|
|
453
|
+
> | Ne zaman doğru | hata anında BU adımın sonrası **tamamen başka bir yola** sapacaksa | hata anında akış **tamamen başka bir yola** sapacaksa (başarısız giriş, yetkisiz sayfa) | kalan adımların çoğu hata hâlinde de koşacaksa — **form gönderimlerinin varsayılanı budur** (Kural 7) |
|
|
454
|
+
>
|
|
455
|
+
> **"Ana akışta condition" sütunu `.error`'u okumalı, `.data`'yı değil.** `Steps.<adımAdı>.data`'nın
|
|
456
|
+
> DOLU/BOŞ olmasına bakarak başarı/hata ayırmak (`data.token` gibi bir alanın varlığını yoklamak)
|
|
457
|
+
> artık yanıltıcıdır — `data` hata anında zaten `null`'dur (yukarıdaki §"4xx de hatadır"). Böyle bir
|
|
458
|
+
> `condition`'ı gateway **FLOW-028** ile uyarır; adıma ayrıca bir `error` dalı bağlayın (aşağıdaki
|
|
459
|
+
> 7c örneği) — `error` dalı bağlıysa uyarı düşer, çünkü hata artık `always`'e hiç uğramaz.
|
|
441
460
|
|
|
442
461
|
Zorunlu filtresi olan uçlarda (`patient-*`, `doctor-appointments` gibi `isRequired: true`
|
|
443
462
|
filtreli microservice'ler) parametreyi adımda ver, yoksa uç `MissingParameterException` atar:
|
|
@@ -1094,15 +1113,22 @@ UYGULAMAZ** — koordinatları aynen kaydeder (aşağı itme yalnız `page_creat
|
|
|
1094
1113
|
> SONRAKİ ağaca bakarak. Layout'ta da geçerlidir: kırılma
|
|
1095
1114
|
> `_repeaterValues` geri yazmasındadır, barındıranın sayfa mı layout mu olduğuna bakmaz.
|
|
1096
1115
|
>
|
|
1116
|
+
> **Mobil uygulamada** başlıklar altında gruplu liste (güne/kategoriye göre) iç içe repeater
|
|
1117
|
+
> gerektirmez: tek `grouped-list` (`groupBy` + başlık `{{ Section.title }}` + öğe şablonu) —
|
|
1118
|
+
> `platform_guide('mobile')` §Güne göre görev listesi. PAGE-013 mesajı mobilde bunu önerir.
|
|
1119
|
+
>
|
|
1097
1120
|
> **Yapılacak:** satır içi listeyi tek bir `textarea`'da **satır başına bir öğe** olarak topla
|
|
1098
1121
|
> (`options_text`), kaydederken `split('\n')`'le böl. Ölçülmüş çalışan sürüm: anket editörü
|
|
1099
1122
|
> (`anket-admin/anket-duzenle`). Gerçekten satır satır düzenleme gerekiyorsa iç listeyi AYRI bir
|
|
1100
1123
|
> sayfada/modalda yönet — iç içe repeater bunu karşılamıyor.
|
|
1101
1124
|
|
|
1102
1125
|
> **Repeater satırını akıştan silmek/eklemek:** repeater ÇOCUĞUNDAKİ bir butonun click akışında
|
|
1103
|
-
>
|
|
1104
|
-
> (`isRepeaterSelf` dalı
|
|
1105
|
-
>
|
|
1126
|
+
> tıklanan satır olay makineye gitmeden SEÇİLİR, yani `q_list.selectedIndex` o satırdır — her runtime
|
|
1127
|
+
> sürümünde. Hedef repeater'ın KENDİSİ olduğu için `rows`'a yazmak serbesttir (`isRepeaterSelf` dalı
|
|
1128
|
+
> satır-değeri yoluna girmez). Desen:
|
|
1129
|
+
> `q_list.rows = (q_list.rows || []).filter(function(r, i){ return i !== q_list.selectedIndex; })`.
|
|
1130
|
+
> `RowIndex` de aynı satırı verir, ama yalnız ARC-121 düzeltmesini (2026-09-27) taşıyan runtime'da;
|
|
1131
|
+
> eskisinde akışta tanımsızdı.
|
|
1106
1132
|
> Ekleme aynı şekilde `.slice()` + `push` ile yapılır; satır numarasını ekledikten sonra yeniden ata.
|
|
1107
1133
|
|
|
1108
1134
|
**Gönderimi saklamak:** satır başına normalize kayıt yerine tek `TEXT` kolonda anlık görüntü
|
|
@@ -1376,6 +1402,16 @@ alanları silinir. Gateway bu yüzden `page_update`'te sayfayı önce okur, yaln
|
|
|
1376
1402
|
alanları değiştirip tam gövdeyi geri yazar (`layout_update` ve `component_update` de aynı).
|
|
1377
1403
|
Yine de `schema` parametresini elle doldurursan okuduğun hâli değil, **verdiğin liste** yazılır.
|
|
1378
1404
|
|
|
1405
|
+
**`page_update` sürümsüz yazmaz (PAGE-039).** Platform sayfa PUT'unu `baseVersion` olmadan reddeder.
|
|
1406
|
+
`baseVersion` vermezsen gateway bu sayfada en son gördüğü sürümü gönderir: `page_get` ya da
|
|
1407
|
+
`page_schema_get` yanıtındaki `version`, `page_schema_patch`'in `newVersion`'ı ya da önceki
|
|
1408
|
+
`page_update`'in yazdığı sürüm. Hiçbiri yoksa istek **gönderilmez** — önce oku. Sayfa o sürümden beri
|
|
1409
|
+
değiştiyse (Studio'da açık sekme, başka ajan) 409 döner; mesaj güncel sürümü ve son yazmayı (zaman,
|
|
1410
|
+
kullanıcı, kaynak, revizyon) söyler ve hiçbir şey yazılmaz. Doğru yol yeniden okuyup değişikliği
|
|
1411
|
+
`page_schema_patch` ile yapmaktır; aradaki değişikliği bilerek ezmek için `force: true` son sürümü
|
|
1412
|
+
okuyup onun üstüne yazar ve yanıt `forced: true` taşır. Sebep: Bitki Bakım koşusunda sürümsüz bir
|
|
1413
|
+
tam gövde yazımı ana sayfayı ve profili boşalttı, akışları sildirdi (14:02).
|
|
1414
|
+
|
|
1379
1415
|
### Atomiklik: 500 KB'lık şemada tek komponente dokunmak
|
|
1380
1416
|
|
|
1381
1417
|
`page_schema_patch` şemanın tamamını göndermez — yalnız op'ları gönderir. Tam gövde ne LLM
|
|
@@ -1385,7 +1421,8 @@ ve geri yazar. Sözleşme (`PatchPageSchemaCommandHandler`):
|
|
|
1385
1421
|
| Aşama | Ne yapar |
|
|
1386
1422
|
|---|---|
|
|
1387
1423
|
| Kilit | Sayfa başına 60 sn'lik dağıtık kilit; başka bir aktör yazıyorsa istek `PageSchemaLockedException` ile döner (409) |
|
|
1388
|
-
| Sürüm | `baseVersion` verdiysen `UpdatedAt.Ticks` ile karşılaştırılır; tutmazsa `PageSchemaVersionConflictException` (409) ve **hiçbir op uygulanmaz** |
|
|
1424
|
+
| Sürüm | `baseVersion` verdiysen `UpdatedAt.Ticks` ile karşılaştırılır; tutmazsa `PageSchemaVersionConflictException` (409) ve **hiçbir op uygulanmaz** — bütün op'lar etkisizse hariç (aşağıdaki "Tekrar" satırı) |
|
|
1425
|
+
| Tekrar | `add_component`'in `elementId`'si zaten varsa ve düğüm istenen ebeveynde, istenen içerikle duruyorsa op **etkisizdir** (hata değil; konum karşılaştırılmaz). Hedef düğümü değiştirmeyen `replace_prop`/`set_*`/`add_event` da etkisizdir. Bütün op'lar etkisizse yama yazılmaz, sürüm artmaz ve eski `baseVersion`'a rağmen 200 `noop: true` döner; sürüm eskiyse `stale: true` ve `newVersion` sizin gönderdiğinizdir. Aynı id'de farklı düğüm hâlâ 422 `duplicate-id`'dir |
|
|
1389
1426
|
| Uygulama | Her op hedefini ağaçta arar (`Find` → JSON Pointer) ve **yalnız o düğüme** dokunur; kardeşler, üst düğümler ve diğer alt ağaçlar bit düzeyinde korunur |
|
|
1390
1427
|
| Hep-ya-hiç | Op'lar önce bellekte uygulanır; **tek bir hata** (bilinmeyen op, bulunamayan hedef, tekrar eden elementId, doğrulama) tüm komutu düşürür — kısmi yazma yoktur |
|
|
1391
1428
|
| Doğrulama | Tam şema `PageSchemaValidator`'dan geçer (422 + alan bazlı `errors`) |
|
|
@@ -1394,7 +1431,18 @@ ve geri yazar. Sözleşme (`PatchPageSchemaCommandHandler`):
|
|
|
1394
1431
|
|
|
1395
1432
|
Yanıt üç alan döner: `applied` (uygulanan op sayısı), **`appliedPatch`** (her op'un şemada tam
|
|
1396
1433
|
olarak hangi JSON Pointer'a ne yaptığı — RFC 6902 biçiminde; "gerçekten yalnız o komponente mi
|
|
1397
|
-
dokunuldu" sorusunun kanıtı) ve **`newVersion`** (`UpdatedAt.Ticks`).
|
|
1434
|
+
dokunuldu" sorusunun kanıtı) ve **`newVersion`** (`UpdatedAt.Ticks`). Etkisiz op varsa `unchanged`
|
|
1435
|
+
(elementId listesi) ve `alreadyApplied` cümlesi gelir; hepsi etkisizse `noop: true`.
|
|
1436
|
+
|
|
1437
|
+
**Yanıt kaybolursa aynı gövdeyle tekrarla.** Ağ koptu, zaman aşımı oldu ve yamanın uygulanıp
|
|
1438
|
+
uygulanmadığını bilmiyorsan aynı `page_schema_patch`'i **aynı gövde ve aynı `baseVersion`** ile
|
|
1439
|
+
yeniden gönder. Uygulanmışsa `noop: true` + `alreadyApplied` döner, hiçbir şey ikinci kez yazılmaz;
|
|
1440
|
+
uygulanmamışsa yama şimdi uygulanır. `add_component` (gövdede `id` verme — platform her istekte yeni
|
|
1441
|
+
`id` üretir, karşılaştırma onu yok sayar), aynı değeri yazan `replace_prop`/`set_*` ve aynı bağı kuran
|
|
1442
|
+
`add_event` tekrarda etkisizdir. İki istisna: `remove_component` ikinci kez `unknown-target` döner — o
|
|
1443
|
+
durumda `page_schema_get` ile bak; eklediğin düğümün bir prop'unu aynı yamanın sonraki op'u
|
|
1444
|
+
değiştirdiyse tekrar `duplicate-id` alır. Yanıt `stale: true` taşıyorsa sayfa sürümünden beri başka bir
|
|
1445
|
+
yazımla da değişmiştir: devam etmeden önce yeniden oku. `idempotencyKey` yoktur ve gerekmez.
|
|
1398
1446
|
|
|
1399
1447
|
> **`appliedPatch` gövdesi VARSAYILAN OLARAK iskelettir.** Girdi `{ op, path }` döner; eklenen
|
|
1400
1448
|
> düğüm `component` altında yalnız `id`/`elementId`/`name`/`type`/`children` taşır. Kanıt zaten
|
|
@@ -1414,12 +1462,29 @@ yamanın `baseVersion`'ıdır. Kontrol platformda atomiktir (satır kilidi + sü
|
|
|
1414
1462
|
`UPDATE`'te), yani iki çağrı arasına giren yazarı da yakalar. `baseVersion` vermezsen gateway yalnız
|
|
1415
1463
|
bu çağrıda zaten okuduğu sayfanın sürümünü gönderir; hiç okumadıysa yama sürümsüz gider ve son
|
|
1416
1464
|
yazan kazanır. Eski `expectedUpdatedAt` hâlâ çalışır ama `baseVersion`'ın yerini tutmaz.
|
|
1465
|
+
|
|
1466
|
+
**Studio da sürüm gönderir (2026-09-26, ZM-27).** Önceden Studio'da açık kalan bir sekme, yayını
|
|
1467
|
+
kaçırdığı yamalarının üstüne kendi eski (bir kez boş) şemasını yazıyor ve bağı kalmayan akışları
|
|
1468
|
+
sildiriyordu. Artık Studio'nun her kaydı sürüm taşır: eski sekme 409 alır ve taslağını senin
|
|
1469
|
+
yazdığının üstüne birleştirir. `revision_list`'te yamandan sonra `source: studio` satırı görürsen bu
|
|
1470
|
+
artık bir üzerine yazma değil, birleştirilmiş bir kayıttır — yine de `revision_diff` ile bak.
|
|
1417
1471
|
Yanıttaki `touched`, kaydı değişen elementId'leri listeler (eklenen alt ağaç dâhil).
|
|
1418
1472
|
|
|
1419
1473
|
> **Büyük şemada çalışma sırası:** `mode:'summary'` ile ağacı çıkar (props yok, 500 KB'lık sayfada
|
|
1420
1474
|
> bile küçük) → hedefin `elementId`'sini al → gerekiyorsa `mode:'subtree'` ile yalnız o dalı oku →
|
|
1421
1475
|
> op'ları `page_schema_validate` ile kuru koştur → `page_schema_patch`. `mode:'full'` yalnız
|
|
1422
1476
|
> gerçekten tüm şema gerektiğinde çağrılır.
|
|
1477
|
+
>
|
|
1478
|
+
> **`full` sığmazsa hata verir, kırpmaz.** Yanıt boyut sınırını aşan şemada `mode:'full'` hata döner
|
|
1479
|
+
> ve düğüm sayısını söyler. Tamamı gerekiyorsa `cursor:'0'` ile oku ve `nextCursor` gelmeyene kadar
|
|
1480
|
+
> sür: her parça `items[]` taşır, kalem `parentElementId` düğümünün `children[index]`'inin yerine
|
|
1481
|
+
> geçer (`parentElementId` yoksa kök dizinin `[index]`'i); alt ağacı sığmayan düğüm kabuk gelir
|
|
1482
|
+
> (`children: [{ elementId, deferred: true }]`) ve sonraki kalemler onu doldurur. Toplanan düğüm sayısı
|
|
1483
|
+
> `nodeCount`'a eşit değilse okuma eksiktir. Okuma arasında sayfa değişirse 409 — baştan başla.
|
|
1484
|
+
> `allowTruncated:true` kırpılmış gövdeyi açıkça verir (`truncated: true`); o gövdeye dayanan
|
|
1485
|
+
> `page_schema_patch` PAGE-038 uyarısı taşır. **`page_update`'e kırpık ya da yarım şema gönderme:**
|
|
1486
|
+
> PUT tam REPLACE'tir, eksik düğümler geri dönüşsüz silinir — gateway düğüm eksilten şemayı
|
|
1487
|
+
> `allowNodeLoss:true` olmadan PAGE-037 ile reddeder.
|
|
1423
1488
|
|
|
1424
1489
|
> **`remove_component`'in yan etkisi — tek istisna.** Sayfa kaydedilirken artık hiçbir komponent
|
|
1425
1490
|
> olayının, prop'unun ya da sayfa olayının referans vermediği akışlar platformca **kalıcı olarak
|
|
@@ -1844,15 +1909,24 @@ page_container
|
|
|
1844
1909
|
```
|
|
1845
1910
|
start → [setter] btn.isLoading = true, btn.isDisabled = true, notification gizle
|
|
1846
1911
|
→ [microservice] sign-in (POST; email + password body'den)
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1912
|
+
├─ error → [setter] btn.isLoading=false, btn.isDisabled=false,
|
|
1913
|
+
│ notification.content =
|
|
1914
|
+
│ { type:'code', ref:'Steps.signIn.error?.message ?? "Giriş başarısız"' }
|
|
1915
|
+
└─ always → [condition] !!Steps.signIn.data.token
|
|
1916
|
+
├─ always → [setter] buton durumunu sıfırla
|
|
1917
|
+
├─ if → [setcookie] { name:'APP_Token', value:'Steps.signIn.data.token',
|
|
1918
|
+
│ expiresInHours: 240 }
|
|
1919
|
+
│ → [redirect] hedef sayfaya
|
|
1920
|
+
└─ else → [setter] notification.content =
|
|
1921
|
+
{ type:'code', ref:'Steps.signIn.error?.message ?? "Giriş başarısız"' }
|
|
1854
1922
|
```
|
|
1855
1923
|
|
|
1924
|
+
- **`sign-in` adımının `error` dalı burada ZORUNLUDUR, süs değil.** Dal bağlı değilken bu şablonun
|
|
1925
|
+
`condition`'ı `Steps.signIn.data.token`'ı okur ve gateway'i **FLOW-028** ile uyarır (yukarıdaki
|
|
1926
|
+
"Hangi yolu seçeceksin" notu): hata anında `data` `null`'dur, token okuması sessizce `undefined`
|
|
1927
|
+
döner. Dal bağlanınca hata onun üzerinden sürer — `always`/`condition` yalnız BAŞARIDA çalışır,
|
|
1928
|
+
yani `data.token` orada gerçek bir iş kuralıdır (uç 200 dönüp token'sız yanıt verirse), hata
|
|
1929
|
+
algılama YÖNTEMİ değil.
|
|
1856
1930
|
- **Cookie adı middleware'in okuduğu adla birebir aynı olmalı** (`cookie.APP_Token` —
|
|
1857
1931
|
`platform_guide('middleware')` Tarif 1). Uyuşmazlık sessizce "hep 401" demektir.
|
|
1858
1932
|
- `redirect` adımı `isRaw: true` iken `path` **çok satırlı JS bloğudur** (son ifade değeri döner) —
|
|
@@ -2042,9 +2116,14 @@ hiçbiri** bağlı değildi; üç portalın da rol menüsü boş çıkıyor, pan
|
|
|
2042
2116
|
> (`dto_get` ile bak: genelde `role`, `role_name`, `roles_csv` vardır). Sayfa tarafında CSV
|
|
2043
2117
|
> kullan — ayrıntı ve şablon `platform_guide('expressions')` §2.
|
|
2044
2118
|
>
|
|
2045
|
-
> **Uç hata dönerse `.data`
|
|
2046
|
-
>
|
|
2047
|
-
> `(items ?? []).map is not a function
|
|
2119
|
+
> **Uç hata dönerse `.data` NULL'dur, hata gövdesi değil (ADR 0004, 2026-09-26).** 2026-09-26'ya
|
|
2120
|
+
> kadar `Steps.x.data` 401/404 sonrası hata gövdesiydi ve `rows || []` bunu yakalamıyordu (nesne
|
|
2121
|
+
> truthy) → `rows.filter is not a function` / `(items ?? []).map is not a function`; artık uç hata
|
|
2122
|
+
> dönünce `data` **null**'dur ve bu adım boş veriyle koşar — `rows` gibi bir listeye bağlarsan
|
|
2123
|
+
> bileşen `null`'ı boş liste sayar, `Array.isArray(...) ? ... : []` sarmalamaya artık gerek yok.
|
|
2124
|
+
> Hata OLDUĞUNU fark etmeden geçmemek için `error` dalı bağlayın (yukarıdaki §"4xx de hatadır",
|
|
2125
|
+
> gateway bunu bağlı değilken **FLOW-028** ile uyarır); hata gövdesini görmek istiyorsan
|
|
2126
|
+
> `Steps.x.error.body`'yi okuyun.
|
|
2048
2127
|
|
|
2049
2128
|
## ⚠️ `page_schema_patch` `html` komponentini reddediyor — katalogda mükerrer kayıt (2026-08-19)
|
|
2050
2129
|
|
|
@@ -2107,6 +2186,31 @@ micro-op hattına dönün.
|
|
|
2107
2186
|
> uyarı yoktu. Silinmesini bilerek istiyorsanız çağrıyı `allowFlowDeletion: true` ile tekrarlayın;
|
|
2108
2187
|
> yanıt `deletedFlows` ile hangi akışların gittiğini listeler.
|
|
2109
2188
|
>
|
|
2189
|
+
> **Silinen akış geri kurulabilir (2026-09-26, ZM-27).** Sayfa kaydının sildiği akış artık revizyon
|
|
2190
|
+
> defterine `operation: delete` satırı olarak düşer (kaynağı `studio` ya da `mcp`). `flow_restore`
|
|
2191
|
+
> akışı son revizyonundaki ad, slug ve adımlarla AYNI id ile yeniden kurar; olaya bağlamaz —
|
|
2192
|
+
> geri geldikten sonra `set_event`. İki sınır: revizyon saklama süresi (budanan akış 404) ve
|
|
2193
|
+
> adımların hedeflediği komponentler — akış bir komponenti tetikleyen/parametre olarak
|
|
2194
|
+
> gösteriyorsa o komponent şemada durmalıdır (canlı ölçüldü: `flow_restore` "Trigger Element Id"
|
|
2195
|
+
> dış anahtarına çarpar).
|
|
2196
|
+
>
|
|
2197
|
+
> **Boşalan sayfa: `page_restore` (2026-09-26).** Sayfayı ve akışlarını birlikte geri kurar:
|
|
2198
|
+
> `revision_list(module: 'page', recordId: <pageId>)` → boşalmadan önceki revizyonu bul (`source` ve
|
|
2199
|
+
> `correlationId` yardım eder; `revision_get` ile içeriğine bak) → `page_restore({ revisionNo })`.
|
|
2200
|
+
> Platform önce eksik akışları adımsız kurar, sonra şemayı eski komponent id'leriyle yazar, sonra
|
|
2201
|
+
> adımları yazar — akış→komponent ve olay→akış dış anahtarları bu sırayla tek transaction'da çözülür.
|
|
2202
|
+
> Yanıt `restoredFlows` ve `deletedFlows` (N'den sonra bağlanmış, şimdi yetim kalan) döner.
|
|
2203
|
+
> Revizyonu kalmamış bir akış varsa 409 ve hiçbir şey yazılmaz.
|
|
2204
|
+
>
|
|
2205
|
+
> **Doğru revizyonu seç.** Şemayı yüklemeyen komutların (`UpdateProjectCommand`,
|
|
2206
|
+
> `UpdateApplicationCommand`…) yazdığı sayfa revizyonları komponent taşımaz; komponenti olan sayfayı
|
|
2207
|
+
> böyle bir revizyona döndürmek 409 `PageRevisionEmptyException` ile reddedilir (bilerek boşaltmak için
|
|
2208
|
+
> `allowEmpty: true`). `correlationId`'si `PatchPageSchemaCommand` ya da `UpdatePageCommand` olan
|
|
2209
|
+
> revizyonu seç. İki sınır daha: N'den beri silinen layout/rol/middleware/özel komponent ya da başka
|
|
2210
|
+
> sayfanın aldığı slug geri yüklemeyi dış anahtar/benzersizlik hatasıyla düşürür (transaction geri
|
|
2211
|
+
> alınır, veri kaybı yok); yalnız başka bir akışın adımından çağrılan (olaya bağlı olmayan) akışlar
|
|
2212
|
+
> geri kurulmaz — onlar için `flow_restore`.
|
|
2213
|
+
>
|
|
2110
2214
|
> **FLOW-018 artık silinen düğümün ALT AĞACINI da görüyor.** Eskiden yalnız op'un kendi
|
|
2111
2215
|
> `target`'ına bakılıyordu: bir kapsayıcı silinince ÇOCUĞUNA yazan setter sessizce kopuyor ve
|
|
2112
2216
|
> uyarı hiç çıkmıyordu (09-05, `sign_in_form` → `btn_sign_in`).
|
|
@@ -2150,6 +2254,9 @@ micro-op hattına dönün.
|
|
|
2150
2254
|
style + Kural 1h deseniyle yeniden kurulunca kart 400px'e ve ortaya oturdu. Gateway bunu **PAGE-014 (uyarı)** ile
|
|
2151
2255
|
yanıtın `layoutWarnings` alanında bildirir. **Düzeni `style` ile kurmaya hiç kalkışma:**
|
|
2152
2256
|
ortalama/yaslama/aralık işi kapsayıcının kendi `layout: 'flex'` prop grubuna aittir (Kural 1f-2).
|
|
2257
|
+
**İstisna `html`:** onun `style`'ı yazarın işaretlemesine kapsamlı giren CSS metnidir (web'de iç
|
|
2258
|
+
seçicili sınıf, mobilde belgenin stil bloğu) — string asıl biçimdir ve PAGE-014 onda susar. Mobilde
|
|
2259
|
+
`html`'e nesne `style` verme: çizilmez (MOB-002).
|
|
2153
2260
|
- **Menüde "bulunduğun sayfa" vurgusu: `sidebar`/`layout`'ta `activeHref`, `header`'da
|
|
2154
2261
|
`activeNavHref`.** `sidebar` ve `layout` menü ağacını ortak renderer'la çizer: `activeHref` boşken
|
|
2155
2262
|
tarayıcıdaki adrese uyan satırı kendileri vurgular, prop yazılırsa o satır vurgulanır (akıştan
|
|
@@ -98,7 +98,10 @@ theme_update({ theme: {
|
|
|
98
98
|
```
|
|
99
99
|
|
|
100
100
|
**Yazı tipi ve ölçek:** `fontFamily` panelin Google Fonts listesinden bir ad olmalı — runtime
|
|
101
|
-
fontu ADIYLA Google'dan çeker, tanınmayan ad sessizce sans-serif'e düşer (THEME-008).
|
|
101
|
+
fontu ADIYLA Google'dan çeker, tanınmayan ad sessizce sans-serif'e düşer (THEME-008). Mobilde de
|
|
102
|
+
aynı kural geçerlidir: cihaz aynı adı Google Fonts'tan TTF olarak indirip `expo-font` ile yükler
|
|
103
|
+
(400 ve 700 kalınlık) — Android'de kalın yüz SENTETİKTİR (iOS gerçek kalın dosyayı çizer,
|
|
104
|
+
ZM-10, 2026-09-26).
|
|
102
105
|
|
|
103
106
|
```jsonc
|
|
104
107
|
theme_update({ theme: {
|
|
@@ -648,9 +648,15 @@ Tam örnek, ölçülen çıktı şekli ve `code` çocuğu tuzağının çıkış
|
|
|
648
648
|
|
|
649
649
|
```js
|
|
650
650
|
// dizi (isSingle:false) · nesne (isSingle:true) · düz değer (yalnız pk dönen yazma)
|
|
651
|
-
(
|
|
651
|
+
(x.data && typeof x.data !== 'string' && typeof x.data.length === 'number' ? (x.data[0] && x.data[0].id) : (x.data && x.data.id !== undefined ? x.data.id : x.data))
|
|
652
652
|
```
|
|
653
653
|
|
|
654
|
+
Diziyi `Array.isArray` ile değil `.length` ile tanıyın: 2026-09-27'den önceki backend'de tablo
|
|
655
|
+
adımının listesi Jint'e sarılı bir CLR nesnesi olarak iniyordu ve `Array.isArray(x.data)` `false`
|
|
656
|
+
dönüyordu — koşuda (2026-09-24) giriş akışı bu yüzden her kullanıcıyı 401 ile reddetti
|
|
657
|
+
(`zeyrek-backend/docs/incident-log.md` #89). `.length`, `.map`, `.filter` o sürümde de çalışıyordu;
|
|
658
|
+
düzeltilmiş backend'de `Array.isArray` de doğrudur.
|
|
659
|
+
|
|
654
660
|
Şekli bilmiyorsan bu yazımı kullan; `workflow_preview` çıktısında `steps[].output`'a bakarak
|
|
655
661
|
hangi vakada olduğunu da görebilirsin.
|
|
656
662
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
|
-
"hash": "
|
|
3
|
-
"generatedAt": "2026-09-
|
|
2
|
+
"hash": "466761837eb9a463fe5984ca119fc04fbdc946cf363177b71efd1b195b8729ca",
|
|
3
|
+
"generatedAt": "2026-09-27T17:47:09.197Z",
|
|
4
4
|
"types": 186,
|
|
5
5
|
"files": 108,
|
|
6
6
|
"note": "Şemaları üreten backend kaynaklarının parmak izi. Elle düzenlemeyin: 'npm run generate-schemas' yazar, 'npm run schema:check' denetler.",
|