@appaflytech/zeyrek-ai-mcp 0.0.13 → 0.0.15
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 +831 -199
- package/dist/knowledge/componentEvents.js +369 -65
- package/dist/knowledge/normalize.js +55 -11
- package/dist/resources/guides/component.md +8 -0
- package/dist/resources/guides/page.md +33 -15
- package/dist/tools/designSchemaPatch.js +6 -2
- package/dist/tools/flows.js +1 -1
- package/dist/tools/pages.js +8 -4
- package/dist/tools/schemaOps.js +2 -2
- package/package.json +1 -1
|
@@ -2041,32 +2041,76 @@ export function orphanedFlowPlan(schema, pageEvents, ops) {
|
|
|
2041
2041
|
if (op.op !== 'set_event' && op.op !== 'remove_event')
|
|
2042
2042
|
continue;
|
|
2043
2043
|
const eventName = String(op.event ?? '');
|
|
2044
|
+
const eventValue = typeof op.eventValue === 'string' ? op.eventValue : undefined;
|
|
2045
|
+
const boundTo = (events) => asObjects(events)
|
|
2046
|
+
.filter(event => String(event.name ?? '') === eventName && typeof event.value === 'string' && event.value !== '');
|
|
2047
|
+
// Bir olaya birden fazla akış bağlı olabilir. Değersiz `remove_event` o durumda platformda
|
|
2048
|
+
// `ambiguous-event` ile düşer ve hiçbir bağ kalkmaz; değerli olan yalnız o akışı çözer.
|
|
2049
|
+
const removedBy = (named) => eventValue === undefined
|
|
2050
|
+
? (named.length === 1 ? named : [])
|
|
2051
|
+
: named.filter(event => event.value === eventValue);
|
|
2044
2052
|
// `remove_event target:'page'` düğümü değil barındırıcının düzey olaylarını hedefler
|
|
2045
2053
|
// (`PageSchemaMicroOpApplier.PageTarget`); düğüm aramasına düşseydi plan boş kalır ve yalnız
|
|
2046
2054
|
// oraya bağlı akış kapıdan geçip kalıcı silinirdi. Düzey olayı olmayan widget'ta applier da
|
|
2047
2055
|
// düğüm aramasına düşer.
|
|
2048
2056
|
if (op.op === 'remove_event' && target === 'page' && Array.isArray(pageEvents)) {
|
|
2049
2057
|
const hostEvents = asObjects(pageEvents);
|
|
2050
|
-
const removed =
|
|
2051
|
-
|
|
2052
|
-
continue;
|
|
2053
|
-
const surviving = new Set(hostEvents.filter(event => event !== removed && typeof event.value === 'string').map(event => String(event.value)));
|
|
2058
|
+
const removed = removedBy(boundTo(hostEvents));
|
|
2059
|
+
const surviving = new Set(hostEvents.filter(event => !removed.includes(event) && typeof event.value === 'string').map(event => String(event.value)));
|
|
2054
2060
|
collectFlowRefsExcept(nodes, '', surviving);
|
|
2055
|
-
|
|
2056
|
-
|
|
2061
|
+
const flowIds = [...new Set(removed.map(event => String(event.value)))].filter(flowId => !surviving.has(flowId));
|
|
2062
|
+
if (flowIds.length > 0)
|
|
2063
|
+
plan.push({ target, op: 'remove_event', flowIds });
|
|
2057
2064
|
continue;
|
|
2058
2065
|
}
|
|
2059
2066
|
const node = findNodeByElementId(nodes, target);
|
|
2060
|
-
const
|
|
2061
|
-
|
|
2062
|
-
|
|
2067
|
+
const named = boundTo(node?.events);
|
|
2068
|
+
// `set_event` adın BÜTÜN bağlarını tek kayda indirir; yeni değerden farklı her akış bağını kaybeder.
|
|
2069
|
+
const losing = op.op === 'set_event' ? named.filter(event => event.value !== eventValue) : removedBy(named);
|
|
2070
|
+
if (losing.length === 0)
|
|
2063
2071
|
continue;
|
|
2064
2072
|
const surviving = survivingRefs(target, eventName);
|
|
2065
|
-
|
|
2066
|
-
|
|
2073
|
+
for (const event of named) {
|
|
2074
|
+
if (!losing.includes(event))
|
|
2075
|
+
surviving.add(String(event.value));
|
|
2076
|
+
}
|
|
2077
|
+
const flowIds = [...new Set(losing.map(event => String(event.value)))].filter(flowId => !surviving.has(flowId));
|
|
2078
|
+
if (flowIds.length > 0)
|
|
2079
|
+
plan.push({ target, op: String(op.op), flowIds });
|
|
2067
2080
|
}
|
|
2068
2081
|
return plan;
|
|
2069
2082
|
}
|
|
2083
|
+
/**
|
|
2084
|
+
* PAGE-034 — dolu bir olaya farklı akışla `set_event` gelmesi. `set_event` adın bütün bağlarını
|
|
2085
|
+
* TEK kayda indirir, yani önceki akış bağını sessizce kaybeder; akış başka yerden de
|
|
2086
|
+
* referanslıysa `orphanedFlowPlan` da susar ve kayıp hiç görünmez (maddelerim 4: makale önerisi
|
|
2087
|
+
* akışı, akıllı öneri bağlanınca çalışmaz oldu). Olaya ikinci akış eklemek `add_event`'tir; bilerek
|
|
2088
|
+
* değiştirmek için op'a `replaceBoundFlows: true` verilir. Okunan ağaçta olmayan hedef denetlenmez.
|
|
2089
|
+
*/
|
|
2090
|
+
export function boundEventOps(schema, ops) {
|
|
2091
|
+
const nodes = asObjects(schema);
|
|
2092
|
+
return asObjects(ops)
|
|
2093
|
+
.filter(op => op.op === 'set_event' && op.replaceBoundFlows !== true)
|
|
2094
|
+
.flatMap(op => {
|
|
2095
|
+
const target = String(op.target ?? '');
|
|
2096
|
+
const event = String(op.event ?? '');
|
|
2097
|
+
const eventValue = typeof op.eventValue === 'string' ? op.eventValue : '';
|
|
2098
|
+
const bound = asObjects(findNodeByElementId(nodes, target)?.events)
|
|
2099
|
+
.filter(entry => String(entry.name ?? '') === event && typeof entry.value === 'string' && entry.value !== '')
|
|
2100
|
+
.map(entry => String(entry.value));
|
|
2101
|
+
const replaced = [...new Set(bound)].filter(flowId => flowId !== eventValue);
|
|
2102
|
+
if (replaced.length === 0)
|
|
2103
|
+
return [];
|
|
2104
|
+
return [`PAGE-034: '${target}' komponentinin '${event}' olayına zaten bağlı akış var (${replaced.join(', ')}). set_event olayın BÜTÜN bağlarını tek akışa indirir ve bu akışlar olay tetiklenince artık çalışmaz. `
|
|
2105
|
+
+ `Olaya bir akış daha eklemek için { op: 'add_event', target: '${target}', event: '${event}', eventValue: '${eventValue}' } kullanın — bağlı akışların hepsi paralel koşar. `
|
|
2106
|
+
+ `Mevcut bağı bilerek değiştirmek istiyorsanız aynı op'a replaceBoundFlows: true ekleyin.`];
|
|
2107
|
+
});
|
|
2108
|
+
}
|
|
2109
|
+
/** Gateway'e ait op alanları platforma gitmez. */
|
|
2110
|
+
export function stripGatewayEventFields(ops) {
|
|
2111
|
+
for (const op of ops)
|
|
2112
|
+
delete op.replaceBoundFlows;
|
|
2113
|
+
}
|
|
2070
2114
|
export function orphanedFlowMessage(plan) {
|
|
2071
2115
|
const lines = plan.map(entry => `'${entry.target}' (${entry.op}) → ${entry.flowIds.join(', ')}`);
|
|
2072
2116
|
return `İstek gönderilmedi: bu yama ${plan.reduce((total, entry) => total + entry.flowIds.length, 0)} akışı sayfada referanssız bırakıyor ve platform referanssız akışı KALICI siler — ${lines.join(' · ')}. `
|
|
@@ -29,6 +29,14 @@ component_get → tek tipin TAM sözleşmesi, üç katman birleşik:
|
|
|
29
29
|
- **Boyut:** `blocks[].size` katalogdaki `maxSize`'ı aşamaz; yükseklikte
|
|
30
30
|
`appearance.heightType: 'auto'` tercih edilir. Katalog varsayılanı bazı tiplerde kendi
|
|
31
31
|
sınırını aşar — körü körüne kopyalama, gateway kırpar ve bildirir.
|
|
32
|
+
- **Çocuk tarafı flex sözleşmesi — `mode: 'flex'` (2026-09-18):** flex kapta kalan alanı alan
|
|
33
|
+
öğe komponent değil onu taşıyan düğüm sarmalayıcısıdır ve sarmalayıcının `flex`i ortak
|
|
34
|
+
katmanda yazılır. `mode` prop'u `'flex'` olan bir çocuk (bugün yalnız `spacer`) kabın ANA
|
|
35
|
+
ekseninde kalanı ister: sütunda kalan yükseklik, satırda kalan genişlik, sıfır taban (wrap
|
|
36
|
+
açıkken satırdan düşmez). `appearance.widthType`/`heightType`'tan bağımsızdır ve ana
|
|
37
|
+
eksende onları yener; kabın ölçüsü o eksende belirli olmalıdır (auto/hug sütunda kalan
|
|
38
|
+
alan yoktur). Komponentin kendi `style`'ından istenemez — `style`'a `flex` yazma tablosuna
|
|
39
|
+
bkz. aşağıda.
|
|
32
40
|
|
|
33
41
|
### Arayüz metni ve biçimler uygulama dilini izler
|
|
34
42
|
|
|
@@ -93,8 +93,9 @@ mümkündür ama doğrulama duvarı ve canlı canvas yalnız `page_schema_patch`
|
|
|
93
93
|
komponentin iç kimliğidir (aşağıda Kural 2, `FLOW-018`).
|
|
94
94
|
|
|
95
95
|
- **`blocks[].size`'ı komponentin `component_get`'teki `minSize`..`maxSize` aralığında tut.** Dikkat:
|
|
96
|
-
katalog varsayılanı aşabiliyor (`
|
|
97
|
-
|
|
96
|
+
katalog varsayılanı aşabiliyor (`datepicker` default `h:8`, `maxSize` `h:5`; 18 şemada aynı
|
|
97
|
+
durum, liste zeyrek-frontend `docs/issues.md` ARC-013 — `page-header` 2026-09-18'de sınırı
|
|
98
|
+
12'ye açarak çıktı) — varsayılanı körü körüne kopyalama. Sınır grid kanvasının kuralıdır; API 2026-09-15'ten beri reddetmez, ama aşan
|
|
98
99
|
blok kanvasta boyutlandırılamaz (aşağıda Kural 7 "Boyut tuzağı").
|
|
99
100
|
- **Kurulmuş bir düğümün boyutunu `resize_component` micro-op'u değiştirir.**
|
|
100
101
|
`{ op: 'resize_component', target: '<elementId>', size: { w, h } }` ızgara boyutunu,
|
|
@@ -159,19 +160,32 @@ Sayfa açılışında veri çekmek için `mount` kullanılır ama tek seçenek o
|
|
|
159
160
|
|
|
160
161
|
Yani "satırı seçince detay getir" (`select`), "kolondan süzünce sunucudan yeniden çek"
|
|
161
162
|
(`filter`), "sayfa değişince yeni sayfayı çek" (`pagination`), "satıra çift tıklayınca formu aç"
|
|
162
|
-
(`doubleClick`) akışları da aynı `set_event` mekanizmasıyla kurulur. Buna karşılık `
|
|
163
|
-
|
|
163
|
+
(`doubleClick`) akışları da aynı `set_event` mekanizmasıyla kurulur. Buna karşılık `datatable`
|
|
164
|
+
yalnız `mount` yayar (bilinçli — Kural 1e); `page-header` 2026-09-18'den beri izdeki öğe
|
|
165
|
+
tıklamasını `clickItem` olarak yayar (`component_get` yükü verir).
|
|
164
166
|
|
|
165
167
|
**Olay adları `on` ÖNEKİ ALMAZ** (`button` → `click`, `form` → `submit`), tek istisna katalogda
|
|
166
168
|
`texteditor`'dur (`onMount`/`onChange`). **Komponentin desteklemediği bir olay adı verirsen bağ
|
|
167
169
|
sessizce ölü kalır** — önce `component_get`. Gateway `set_event`'i katalogla karşılaştırır ve
|
|
168
170
|
uymayan adı **PAGE-008** ile istek gitmeden reddeder.
|
|
169
171
|
|
|
172
|
+
**Bir olaya birden fazla akış bağlanabilir — ikinciyi `add_event` ile ekle.** `set_event` olayın
|
|
173
|
+
BÜTÜN bağlarını tek akışa indirir: başlık alanının `change` olayına makale önerisi bağlıyken akıllı
|
|
174
|
+
öneriyi `set_event` ile bağlamak makale önerisini devre dışı bırakır. Gateway bunu **PAGE-034** ile
|
|
175
|
+
istek gitmeden reddeder. Doğru op `{ "op": "add_event", "target": "<elementId>", "event": "change",
|
|
176
|
+
"eventValue": "<ikinci flow id>" }`'dir. Olay tetiklenince bağlı akışların hepsi **paralel ve
|
|
177
|
+
birbirinden bağımsız** koşar: biri hata verirse diğeri etkilenmez, **sıra garantisi yoktur**. Biri
|
|
178
|
+
diğerinin sonucunu bekliyorsa ya da ikisi aynı değişkene/prop'a yazıyorsa ayrı bağ değil,
|
|
179
|
+
`flow-trigger` adımıyla zincir kur (Kural 1c). Mevcut bağı bilerek değiştirmek için `set_event`
|
|
180
|
+
op'una `replaceBoundFlows: true` ekle. **Mobil runtime yalnız ilk akışı koşar** — mobil
|
|
181
|
+
uygulamada olay başına tek akış bağla.
|
|
182
|
+
|
|
170
183
|
**Olay KALDIRMAK için `remove_event` kullan.** Platformun `set_event` micro-op'u olay silemez:
|
|
171
184
|
`eventValue: null` verildiğinde kayıt `{ name: 'click' }` olarak (değersiz) kalır, diziden
|
|
172
185
|
çıkmaz — şemada ölü bağ artığı bırakır. `{ "op": "remove_event", "target": "<elementId>",
|
|
173
186
|
"event": "click" }` platformun op'udur: olayı diziden çıkarır (yanıtta `removedEvents`); olmayan
|
|
174
|
-
bir olayı kaldırmaya çalışmak `missing-event` ile 422 döner.
|
|
187
|
+
bir olayı kaldırmaya çalışmak `missing-event` ile 422 döner. Olaya birden fazla akış bağlıysa
|
|
188
|
+
kaldırılacak akışı `eventValue` ile ver; vermezsen `ambiguous-event` ile 422 döner. Sayfa düzeyi olaylar için
|
|
175
189
|
`target: "page"`. Referanssız kalan akış silineceği için `allowFlowDeletion` kuralı (G-21) bu op'ta
|
|
176
190
|
da geçerlidir.
|
|
177
191
|
|
|
@@ -185,12 +199,14 @@ olaylarında **boştur** (ölçüm 2026-08-25, kaynak okuması: `flow-machine.ts
|
|
|
185
199
|
Pratik kural: **`payload` anahtarları aynı zamanda prop adlarıdır; akışta `self.<anahtar>` ile
|
|
186
200
|
oku** (ya da `value { type:'property', ref:{ source:'component', element, property } }` ile).
|
|
187
201
|
`event.data` yalnız `params` ile gelen olaylarda dolar — `html` komponentinin `fireEvent`
|
|
188
|
-
aksiyonları (`platform_guide('html')`) ve
|
|
189
|
-
`importwizard` · `kanban` · `signature` · `stepper`
|
|
190
|
-
|
|
191
|
-
yazmaz (`component_get` çıktısındaki sözleşme notları ayrımı
|
|
192
|
-
|
|
193
|
-
|
|
202
|
+
aksiyonları (`platform_guide('html')`) ve **`params`'ı da gönderen komponentler**
|
|
203
|
+
(`eventcalendar` · `importwizard` · `kanban` · `signature` · `stepper` · `tabs` ·
|
|
204
|
+
`tree-view` · `pagination`): bunların yükü `event.data.<anahtar>` ile de okunur; `kanban` ve
|
|
205
|
+
`eventcalendar` yükü `self`'e HİÇ yazmaz (`component_get` çıktısındaki sözleşme notları ayrımı
|
|
206
|
+
söyler). Sessiz hata örneği: **tablonun kendi** sayfalama akışını (`table.pagination`)
|
|
207
|
+
`event.data.limit`'e bağlarsan sorgu `LIMIT null OFFSET null` ile koşar ve **tüm satırları**
|
|
208
|
+
döndürür; hata yok, sayfalama yok. (Bağımsız `pagination` komponenti bu tuzağa girmez, yükünü
|
|
209
|
+
`params` ile de gönderir.)
|
|
194
210
|
|
|
195
211
|
| Komponent · olay | Yük (aynı zamanda prop adları) | Akışta nereden okunur |
|
|
196
212
|
|---|---|---|
|
|
@@ -200,16 +216,18 @@ sayfalama akışını `event.data.limit`'e bağlarsan sorgu `LIMIT null OFFSET n
|
|
|
200
216
|
| `table.select` · `doubleClick` | — | `self.selectedRow` |
|
|
201
217
|
| `table.selectionChange` | — | `self.selectedRows` |
|
|
202
218
|
| `datatable.mount` | — | `self.filters` · `self.sortField` · `self.sortType` · `self.offset` |
|
|
203
|
-
| `pagination.change` | `{ offset, limit }` | `self.offset` · `self.limit` |
|
|
219
|
+
| `pagination.change` | `{ offset, limit }` | `self.offset` · `self.limit` ya da `event.data.offset` · `event.data.limit` |
|
|
204
220
|
| `textinput/textarea/password.change` | `{ value }` | `self.value` |
|
|
205
221
|
| `dropdown.change` · `multi-select.change` | `{ value }` | `self.value` |
|
|
206
222
|
| `dropdown.search` · `multi-select.search` | `{ searchText }` | `self.searchText` |
|
|
207
|
-
| `tabs.tab` | `{ selected }` | `self.selected` |
|
|
223
|
+
| `tabs.tab` | `{ selected }` | `self.selected` ya da `event.data.selected` |
|
|
208
224
|
| `chart.dataPointClick` | `{ name, seriesName, value, dataIndex, row }` | `self.row` (kaynak satırın tamamı) |
|
|
209
|
-
| `repeater.change` | `{ selectedIndex }` | `
|
|
225
|
+
| `repeater.change` · `repeater.select` | `{ selectedIndex }` | `self.selectedIndex` · `self.selectedItem` (akış satır bağlamında koşmaz, `item`/`Row` boş) |
|
|
210
226
|
| `html` aksiyonu (`fireEvent`) | ikinci argüman | **`event.data`** |
|
|
211
227
|
| `kanban.cardMove` | `{ id, row, from, to }` | **`event.data`** (self'e yazılmaz) |
|
|
228
|
+
| `kanban.cardAdd` · `kanban.cardRemove` | `{ column }` · `{ id, row, column }` | **`event.data`** (self'e yazılmaz; kart eklenmez/gizlenmez, akış yazıp `rows`'u tazeler) |
|
|
212
229
|
| `eventcalendar.eventMove` | `{ id, row, start, end, previousStart, previousEnd }` | **`event.data`** (self'e yazılmaz) |
|
|
230
|
+
| `eventcalendar.monthChange` · `eventcalendar.viewChange` · `eventcalendar.eventCreate` | `{ month, year, view, start, end }` · `{ view, start, end }` · `{ start, end, allDay }` | **`event.data`** (self'e yazılmaz; veri sorgusunu kesişimle bağla: başlangıç < `end`+1 gün ve bitiş >= `start`) |
|
|
213
231
|
| `importwizard.complete` | `{ rows, errors, fileName, total }` | `self.rows`… ya da `event.data` |
|
|
214
232
|
| `stepper.stepChange` | `{ currentStep, previous }` | `self.currentStep` ya da `event.data` |
|
|
215
233
|
| `signature.change` | `{ value }` | `self.value` ya da `event.data.value` |
|
|
@@ -1813,7 +1831,7 @@ bağlanır, filtre `source:'expression'` + `value:'user.id'` olur.
|
|
|
1813
1831
|
| Kimlik alanları | `email` · `password` |
|
|
1814
1832
|
| Dosya | `fileUpload` (dropzone) · `fileInput` · `filebutton` · `file-viewer` |
|
|
1815
1833
|
| Liste | `table` · `datatable` (Kural 1e) · `repeater` (kart listesi VE dinamik form — Kural 1g) · `tree-view` (hiyerarşi; `isSearchable` ile istemci içi arama) · `timeline` (tarihli olay akışı) |
|
|
1816
|
-
| Pano / takvim | `kanban` (durum kolonlarında sürüklenebilir kartlar; taşıma `cardMove` ile `event.data`'dan okunur, kalıcılık AKIŞIN işidir) · `eventcalendar` (ay/hafta/gün; veri `rows`'a akışla yazılır, `monthChange`
|
|
1834
|
+
| Pano / takvim | `kanban` (durum kolonlarında sürüklenebilir kartlar; taşıma `cardMove` ile `event.data`'dan okunur, kalıcılık AKIŞIN işidir) · `eventcalendar` (ay/hafta/gün/ajanda; veri `rows`'a akışla yazılır, `monthChange`/`viewChange` görünen `start`/`end` aralığıyla tazeleme kancasıdır) |
|
|
1817
1835
|
| Kayıt detayı | `detailview` — tek kaydı etiket/değer ızgarası basar (alan başına format, iç içe alan yolu); OLAY YAYMAZ, tıklanabilirlik gerekiyorsa kapsayıcıya sar |
|
|
1818
1836
|
| Adımlı akış | `stepper` — tabs gibi her adım kendi droppable görünümünü taşır; ileri gidiş `canProceed` kapısıyla (`{{ form1.isValid === true }}` biçimi), adım `currentStep` setter'ıyla sürülür |
|
|
1819
1837
|
| İmza / CSV içe aktarma | `signature` (değer PNG data-URL ya da ham SVG; `change` setter'lı) · `importwizard` (3 adımlı sihirbaz; temiz+hatalı satırlar `complete` yükünde — satırları uca yazmak akışın işidir) |
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { orphanedFlowWarnings, orphanedFlowPlan, orphanedFlowMessage, staleSetterWarnings, normalizeSchemaOps, validationNote, componentDefaultsNote, replacePropNote, flexSizeWarnings, nestedRepeaterNodes, nestedRepeaterMessage, stringStyleNodes, stringStyleWarning, inertFlexPropNodes, inertFlexPropWarning, verticalGapWarnings, withNormalization, FLEX_LAYOUT_PROP_NAMES } from '../knowledge/normalize.js';
|
|
1
|
+
import { orphanedFlowWarnings, orphanedFlowPlan, orphanedFlowMessage, boundEventOps, stripGatewayEventFields, staleSetterWarnings, normalizeSchemaOps, validationNote, componentDefaultsNote, replacePropNote, flexSizeWarnings, nestedRepeaterNodes, nestedRepeaterMessage, stringStyleNodes, stringStyleWarning, inertFlexPropNodes, inertFlexPropWarning, verticalGapWarnings, withNormalization, FLEX_LAYOUT_PROP_NAMES } from '../knowledge/normalize.js';
|
|
2
2
|
import { opsParam, writeViewParam, outlineWriteResponse, unknownEventOps, collectTypes, simulateLayoutOps, removeNodeFromTree, planEventRemovals, planResizes, planPropTypes, assertOpBodies } from './schemaOps.js';
|
|
3
3
|
import { guid, projectParam, environmentParam, updates, microOps } from './common.js';
|
|
4
4
|
import { fetchHost, hostPath } from './screens.js';
|
|
@@ -68,7 +68,10 @@ export function registerDesignSchemaPatchTool(server, platform, host) {
|
|
|
68
68
|
}
|
|
69
69
|
const nodes = (record[spec.tree] ?? []);
|
|
70
70
|
const events = host === 'layout' ? record.events : undefined;
|
|
71
|
-
if (opRecords.some(entry => entry.op === 'set_event')) {
|
|
71
|
+
if (opRecords.some(entry => entry.op === 'set_event' || entry.op === 'add_event')) {
|
|
72
|
+
const bound = boundEventOps(nodes, opRecords);
|
|
73
|
+
if (bound.length > 0)
|
|
74
|
+
throw new PlatformApiError(`PAGE-034 — dolu olaya set_event (istek gönderilmedi): ${bound.join(' · ')}`, 400);
|
|
72
75
|
const typesByElementId = new Map();
|
|
73
76
|
collectTypes(nodes, typesByElementId);
|
|
74
77
|
collectTypes(opRecords.map(entry => entry.component).filter(Boolean), typesByElementId);
|
|
@@ -77,6 +80,7 @@ export function registerDesignSchemaPatchTool(server, platform, host) {
|
|
|
77
80
|
if (unknown.length > 0)
|
|
78
81
|
throw new PlatformApiError(`PAGE-008 — geçersiz olay adı (istek gönderilmedi): ${unknown.join(' · ')}`, 400);
|
|
79
82
|
}
|
|
83
|
+
stripGatewayEventFields(opRecords);
|
|
80
84
|
const deletedFlows = orphanedFlowPlan(nodes, events, [...opRecords, ...removeEventOps]);
|
|
81
85
|
if (deletedFlows.length > 0 && allowFlowDeletion !== true)
|
|
82
86
|
throw new PlatformApiError(orphanedFlowMessage(deletedFlows), 400);
|
package/dist/tools/flows.js
CHANGED
|
@@ -399,7 +399,7 @@ export function registerFlowTools(server, platform) {
|
|
|
399
399
|
outcome: existing === undefined ? 'created' : 'updated',
|
|
400
400
|
flow: { id: writtenFlowId, name, slug, steps: steps.length },
|
|
401
401
|
...(parameterWarnings.length > 0 ? { parameterWarnings } : {}),
|
|
402
|
-
hint: "Akışı bir olaya bağlamak için: page_schema_patch (layout'ta layout_schema_patch, widget'ta component_schema_patch) ops:[{op:'set_event', target:<elementId>, event:'<component_get events listesindeki ad>', eventValue:<flow id>}]. Olay adları komponente özeldir ve 'on' ÖNEKİ ALMAZ (button → 'click', form → 'submit', table → 'mount'/'select'/'sort'…); uydurma ad 200 döner ama bağ sessizce ölü kalır — gateway PAGE-008 ile yakalar."
|
|
402
|
+
hint: "Akışı bir olaya bağlamak için: page_schema_patch (layout'ta layout_schema_patch, widget'ta component_schema_patch) ops:[{op:'set_event', target:<elementId>, event:'<component_get events listesindeki ad>', eventValue:<flow id>}]; olayda zaten akış bağlıysa ikinciyi add_event ile ekleyin (set_event bağlı akışı değiştirir, PAGE-034). Olay adları komponente özeldir ve 'on' ÖNEKİ ALMAZ (button → 'click', form → 'submit', table → 'mount'/'select'/'sort'…); uydurma ad 200 döner ama bağ sessizce ölü kalır — gateway PAGE-008 ile yakalar."
|
|
403
403
|
}, normalized), drops), findings);
|
|
404
404
|
}));
|
|
405
405
|
server.registerTool('flow_update', {
|
package/dist/tools/pages.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { guid, projectParam, environmentParam, pagingParams, readOnly, writes, updates, destructive, microOps, isBranchName } from './common.js';
|
|
2
|
-
import { orphanedFlowWarnings, orphanedFlowPlan, orphanedFlowMessage, staleSetterWarnings, lostSetterTargets, normalizeComponentDefaults, normalizeSchemaOps, schemaOpValidationErrors, codePropTemplateErrors, validationNote, componentDefaultsNote, replacePropNote, ensurePageContainer, retargetRootAdds, rootAddsNote, flexSizeWarnings, spacingLayerWarnings, reorderSchemaBlocks, reorderNote, nestedRepeaterNodes, nestedRepeaterMessage, stringStyleNodes, stringStyleWarning, inertFlexPropNodes, inertFlexPropWarning, FLEX_LAYOUT_PROP_NAMES, resetPageContainerPosition, pageContainerPositionWarning, verticalGapWarnings, flexIndexOrder, flexIndexOrderNote, withNormalization, parseVariableEntries, PAGE_CONTAINER_ELEMENT_ID } from '../knowledge/normalize.js';
|
|
2
|
+
import { orphanedFlowWarnings, orphanedFlowPlan, orphanedFlowMessage, boundEventOps, stripGatewayEventFields, staleSetterWarnings, lostSetterTargets, normalizeComponentDefaults, normalizeSchemaOps, schemaOpValidationErrors, codePropTemplateErrors, validationNote, componentDefaultsNote, replacePropNote, ensurePageContainer, retargetRootAdds, rootAddsNote, flexSizeWarnings, spacingLayerWarnings, reorderSchemaBlocks, reorderNote, nestedRepeaterNodes, nestedRepeaterMessage, stringStyleNodes, stringStyleWarning, inertFlexPropNodes, inertFlexPropWarning, FLEX_LAYOUT_PROP_NAMES, resetPageContainerPosition, pageContainerPositionWarning, verticalGapWarnings, flexIndexOrder, flexIndexOrderNote, withNormalization, parseVariableEntries, PAGE_CONTAINER_ELEMENT_ID } from '../knowledge/normalize.js';
|
|
3
3
|
import { functionSignature } from '../knowledge/functionGuards.js';
|
|
4
4
|
import { fetchHost, pageUpdateBody, schemaParam, eventsParam, unwrap, modeParam, summarize, findNode, flowPath } from './screens.js';
|
|
5
5
|
import { fetchComponentEventNames } from './components.js';
|
|
@@ -67,7 +67,7 @@ async function fetchPage(platform, project, environment, applicationId, pageId)
|
|
|
67
67
|
*/
|
|
68
68
|
export const SERVER_PAGE_OPS = [
|
|
69
69
|
'replace_prop', 'set_binding', 'set_prop_type', 'add_component', 'remove_component', 'move_component', 'reorder', 'set_block',
|
|
70
|
-
'set_appearance', 'set_states', 'set_event', 'remove_event', 'set_validations', 'set_state_binding', 'remove_state_binding', 'batch'
|
|
70
|
+
'set_appearance', 'set_states', 'set_event', 'add_event', 'remove_event', 'set_validations', 'set_state_binding', 'remove_state_binding', 'batch'
|
|
71
71
|
];
|
|
72
72
|
export function registerPageTools(server, platform, injectedScreenshotter, injectedMemory) {
|
|
73
73
|
const config = platform.config;
|
|
@@ -638,10 +638,13 @@ export function registerPageTools(server, platform, injectedScreenshotter, injec
|
|
|
638
638
|
reject('PAGE-016', entry, schemaOpValidationErrors([entry]));
|
|
639
639
|
for (const entry of opRecords)
|
|
640
640
|
reject('PAGE-032', entry, codePropTemplateErrors([entry]));
|
|
641
|
-
if (opRecords.some(entry => entry.op === 'set_event')) {
|
|
641
|
+
if (opRecords.some(entry => entry.op === 'set_event' || entry.op === 'add_event')) {
|
|
642
642
|
const typesByElementId = new Map();
|
|
643
643
|
try {
|
|
644
|
-
|
|
644
|
+
const page = await readPage();
|
|
645
|
+
collectTypes(page.schema, typesByElementId);
|
|
646
|
+
for (const entry of opRecords)
|
|
647
|
+
reject('PAGE-034', entry, boundEventOps(page.schema, [entry]));
|
|
645
648
|
}
|
|
646
649
|
catch {
|
|
647
650
|
typesByElementId.clear();
|
|
@@ -653,6 +656,7 @@ export function registerPageTools(server, platform, injectedScreenshotter, injec
|
|
|
653
656
|
}
|
|
654
657
|
if (rejected.length > 0)
|
|
655
658
|
throw new PlatformApiError(rejectedOpsMessage(rejected), 400);
|
|
659
|
+
stripGatewayEventFields(opRecords);
|
|
656
660
|
const wantsRootAdd = opRecords.some(entry => entry.op === 'add_component' && (entry.parent === undefined || entry.parent === null || entry.parent === 'root'));
|
|
657
661
|
let retargeted = [];
|
|
658
662
|
if (wantsRootAdd) {
|
package/dist/tools/schemaOps.js
CHANGED
|
@@ -24,7 +24,7 @@ export function outlineWriteResponse(response) {
|
|
|
24
24
|
if (Array.isArray(container.appliedPatch))
|
|
25
25
|
container.appliedPatch = outlineAppliedPatch(container.appliedPatch);
|
|
26
26
|
}
|
|
27
|
-
export const opsParam = z.array(z.record(z.unknown())).describe("Micro-op listesi. Op sözlüğü: replace_prop {target, prop, value} (value ham değer, code sarmalı {type: 'code', ref: '{{ … }}'} ya da translation sarmalı {type: 'translation', ref: '<çeviri anahtarı>'} — prop tipini değiştirmek için komponenti silip yeniden EKLEMEYİN, ona yazan setter'ların hedefi silinir (FLOW-018); platform replace_prop'ta expression alanını OKUMAZ — gateway onu code sarmalına çevirir) | set_binding {target, prop, expression: '{{ Page.variables.x }}'} | add_component {parent (üst elementId ya da 'root'), index?, component} | remove_component {target} | move_component {target, parent, index?, position: {x, y}} (index ÇİZİM sırası değildir, runtime çocukları blocks.position'a göre (y, sonra x) çizer. page_schema_patch'te flex kapta gateway index'i görünen sıraya çevirir ve kardeş konumlarını yeniden yazar; ızgara kapta sırayı position.y verir, sona eklemeyen index PAGE-033 uyarısıdır. layout_schema_patch ve component_schema_patch bu çeviriyi YAPMAZ: orada sırayı position.y ile verin) | set_event {target, event, eventValue} | remove_event {target, event} (olayı diziden çıkarır; target: 'page' sayfa düzeyi olaylar. set_event ile eventValue: null olay SİLMEZ, değersiz ölü kayıt bırakır. Olmayan olay 422 missing-event) | set_validations {target, validations: [{type, message, value, isActive}]} (komponentin validation listesini TAMAMEN değiştirir; boş dizi hepsini siler) | set_appearance {target, appearance: {heightType?, widthType?, tooltip?, background?, …}} (kurulu düğümün appearance'ına YALNIZ verilen alanları yazar, gerisini korur; null alanı temizler; bilinmeyen alan unknown-appearance-field ile reddedilir. GÖRÜNÜRLÜK appearance'ta DEĞİLDİR: gizlemek için replace_prop {target, prop: 'isHidden', value: true}, koşul için set_binding {prop: 'isHidden'} — add_component gövdesindeki appearance.isHidden'ı gateway props.isHidden'a taşır) | resize_component {target, size?: {w,h}, appearance?: {widthType,widthString,heightType,heightString}, states?: {isWrapper,isScrollable,isSticky,isDroppable,isFloating,isQuickStack}, breakpoint?: 1024} (gateway adı: platforma set_block {target, breakpoint, size} + set_appearance {target, appearance} + set_states {target, states} olarak gider — o üç op'u doğrudan da gönderebilirsiniz. move_component'e konan size reddedilir. Izgarada blocks[].size, flex'te appearance geçerlidir; ebeveynin düzeni yazılanı etkisiz kılıyorsa PAGE-019 uyarısı çıkar. states bir PROP DEĞİL düğüm alanıdır ve replace_prop onu yazamaz — düğümü silmeden isWrapper bayrağını değiştirmenin yolu budur). set_prop_type {target, prop, propType: 'static'|'code'|'translation', ref?, data?} (replace_prop sarmalıyla aynı sonucu verir ve mevcut data'yı tasarım yedeği olarak taşır. Bu op prop'un TİPİNİ yerinde değiştirir. Taşıyıcı alan tipe göre: static → data, code ve translation → ref (runtime data'yı her turda ezer, anahtar yalnız ref'te yaşar). Eksik taşıyıcı istek gönderilmeden reddedilir; page_schema_validate aynı kararı verir) | reorder {parent ('root' ya da elementId), order: [çocuk elementId'lerinin TAMAMI, yeni sırada]} (yalnız DİZİ sırasını değiştirir; çizim sırası blocks.position'dır — görünen sırayı değiştirmek için konumları set_block/move_component ile de yazın) | set_state_binding {target, binding: {name, expression, targets}} (ada göre ekler/değiştirir) | remove_state_binding {target, name} | batch {gestureId?, label?, ops: [...]} (iç içe batch yok). target = elementId. İstek atomiktir: tek op reddedilirse hiçbiri yazılmaz.");
|
|
27
|
+
export const opsParam = z.array(z.record(z.unknown())).describe("Micro-op listesi. Op sözlüğü: replace_prop {target, prop, value} (value ham değer, code sarmalı {type: 'code', ref: '{{ … }}'} ya da translation sarmalı {type: 'translation', ref: '<çeviri anahtarı>'} — prop tipini değiştirmek için komponenti silip yeniden EKLEMEYİN, ona yazan setter'ların hedefi silinir (FLOW-018); platform replace_prop'ta expression alanını OKUMAZ — gateway onu code sarmalına çevirir) | set_binding {target, prop, expression: '{{ Page.variables.x }}'} | add_component {parent (üst elementId ya da 'root'), index?, component} | remove_component {target} | move_component {target, parent, index?, position: {x, y}} (index ÇİZİM sırası değildir, runtime çocukları blocks.position'a göre (y, sonra x) çizer. page_schema_patch'te flex kapta gateway index'i görünen sıraya çevirir ve kardeş konumlarını yeniden yazar; ızgara kapta sırayı position.y verir, sona eklemeyen index PAGE-033 uyarısıdır. layout_schema_patch ve component_schema_patch bu çeviriyi YAPMAZ: orada sırayı position.y ile verin) | set_event {target, event, eventValue, replaceBoundFlows?} (olayın BÜTÜN bağlarını bu tek akışa indirir; olayda başka akış bağlıysa gateway PAGE-034 ile reddeder — bilerek değiştirmek için replaceBoundFlows: true) | add_event {target, event, eventValue} (olaya bir akış DAHA bağlar; bağlı akışların hepsi paralel ve birbirinden bağımsız koşar, sıra garantisi yoktur — sıra gerekiyorsa flow-trigger. Aynı akış iki kez 422 duplicate-event. Mobil runtime yalnız İLK akışı koşar) | remove_event {target, event, eventValue?} (olayı diziden çıkarır; target: 'page' sayfa düzeyi olaylar. Olaya birden fazla akış bağlıysa eventValue ile hangisi olduğunu verin, yoksa 422 ambiguous-event. set_event ile eventValue: null olay SİLMEZ, değersiz ölü kayıt bırakır. Olmayan olay 422 missing-event) | set_validations {target, validations: [{type, message, value, isActive}]} (komponentin validation listesini TAMAMEN değiştirir; boş dizi hepsini siler) | set_appearance {target, appearance: {heightType?, widthType?, tooltip?, background?, …}} (kurulu düğümün appearance'ına YALNIZ verilen alanları yazar, gerisini korur; null alanı temizler; bilinmeyen alan unknown-appearance-field ile reddedilir. GÖRÜNÜRLÜK appearance'ta DEĞİLDİR: gizlemek için replace_prop {target, prop: 'isHidden', value: true}, koşul için set_binding {prop: 'isHidden'} — add_component gövdesindeki appearance.isHidden'ı gateway props.isHidden'a taşır) | resize_component {target, size?: {w,h}, appearance?: {widthType,widthString,heightType,heightString}, states?: {isWrapper,isScrollable,isSticky,isDroppable,isFloating,isQuickStack}, breakpoint?: 1024} (gateway adı: platforma set_block {target, breakpoint, size} + set_appearance {target, appearance} + set_states {target, states} olarak gider — o üç op'u doğrudan da gönderebilirsiniz. move_component'e konan size reddedilir. Izgarada blocks[].size, flex'te appearance geçerlidir; ebeveynin düzeni yazılanı etkisiz kılıyorsa PAGE-019 uyarısı çıkar. states bir PROP DEĞİL düğüm alanıdır ve replace_prop onu yazamaz — düğümü silmeden isWrapper bayrağını değiştirmenin yolu budur). set_prop_type {target, prop, propType: 'static'|'code'|'translation', ref?, data?} (replace_prop sarmalıyla aynı sonucu verir ve mevcut data'yı tasarım yedeği olarak taşır. Bu op prop'un TİPİNİ yerinde değiştirir. Taşıyıcı alan tipe göre: static → data, code ve translation → ref (runtime data'yı her turda ezer, anahtar yalnız ref'te yaşar). Eksik taşıyıcı istek gönderilmeden reddedilir; page_schema_validate aynı kararı verir) | reorder {parent ('root' ya da elementId), order: [çocuk elementId'lerinin TAMAMI, yeni sırada]} (yalnız DİZİ sırasını değiştirir; çizim sırası blocks.position'dır — görünen sırayı değiştirmek için konumları set_block/move_component ile de yazın) | set_state_binding {target, binding: {name, expression, targets}} (ada göre ekler/değiştirir) | remove_state_binding {target, name} | batch {gestureId?, label?, ops: [...]} (iç içe batch yok). target = elementId. İstek atomiktir: tek op reddedilirse hiçbiri yazılmaz.");
|
|
28
28
|
/**
|
|
29
29
|
* PAGE-008 — `set_event` uydurma bir olay adıyla 200 döner ve bağ SESSİZCE ölü kalır: motor olayı
|
|
30
30
|
* komponentin yaydığı adla eşleştirir, eşleşme yoksa akış hiç çalışmaz. Kontrol yalnız hedefin
|
|
@@ -35,7 +35,7 @@ export const opsParam = z.array(z.record(z.unknown())).describe("Micro-op listes
|
|
|
35
35
|
export function unknownEventOps(ops, typesByElementId, eventsBySlug) {
|
|
36
36
|
return ops
|
|
37
37
|
.map(op => op)
|
|
38
|
-
.filter(op => op.op === 'set_event')
|
|
38
|
+
.filter(op => op.op === 'set_event' || op.op === 'add_event')
|
|
39
39
|
.flatMap(op => {
|
|
40
40
|
const target = String(op.target ?? '');
|
|
41
41
|
const event = String(op.event ?? '');
|
package/package.json
CHANGED