@appaflytech/zeyrek-ai-mcp 0.0.13 → 0.0.14

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.
@@ -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 = hostEvents.find(event => String(event.name ?? '') === eventName);
2051
- if (typeof removed?.value !== 'string' || removed.value === '')
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
- if (!surviving.has(removed.value))
2056
- plan.push({ target, op: 'remove_event', flowIds: [removed.value] });
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 current = asObjects(node?.events).find(event => String(event.name ?? '') === eventName);
2061
- const currentValue = typeof current?.value === 'string' ? current.value : '';
2062
- if (currentValue === '' || currentValue === String(op.eventValue ?? ''))
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
- if (!surviving.has(currentValue))
2066
- plan.push({ target, op: String(op.op), flowIds: [currentValue] });
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(' · ')}. `
@@ -167,11 +167,23 @@ ve `datatable` yalnız `mount` yayar (`datatable`'ınki bilinçli — Kural 1e).
167
167
  sessizce ölü kalır** — önce `component_get`. Gateway `set_event`'i katalogla karşılaştırır ve
168
168
  uymayan adı **PAGE-008** ile istek gitmeden reddeder.
169
169
 
170
+ **Bir olaya birden fazla akış bağlanabilir — ikinciyi `add_event` ile ekle.** `set_event` olayın
171
+ 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ı
172
+ öneriyi `set_event` ile bağlamak makale önerisini devre dışı bırakır. Gateway bunu **PAGE-034** ile
173
+ istek gitmeden reddeder. Doğru op `{ "op": "add_event", "target": "<elementId>", "event": "change",
174
+ "eventValue": "<ikinci flow id>" }`'dir. Olay tetiklenince bağlı akışların hepsi **paralel ve
175
+ birbirinden bağımsız** koşar: biri hata verirse diğeri etkilenmez, **sıra garantisi yoktur**. Biri
176
+ diğerinin sonucunu bekliyorsa ya da ikisi aynı değişkene/prop'a yazıyorsa ayrı bağ değil,
177
+ `flow-trigger` adımıyla zincir kur (Kural 1c). Mevcut bağı bilerek değiştirmek için `set_event`
178
+ op'una `replaceBoundFlows: true` ekle. **Mobil runtime yalnız ilk akışı koşar** — mobil
179
+ uygulamada olay başına tek akış bağla.
180
+
170
181
  **Olay KALDIRMAK için `remove_event` kullan.** Platformun `set_event` micro-op'u olay silemez:
171
182
  `eventValue: null` verildiğinde kayıt `{ name: 'click' }` olarak (değersiz) kalır, diziden
172
183
  çıkmaz — şemada ölü bağ artığı bırakır. `{ "op": "remove_event", "target": "<elementId>",
173
184
  "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. Sayfa düzeyi olaylar için
185
+ bir olayı kaldırmaya çalışmak `missing-event` ile 422 döner. Olaya birden fazla akış bağlıysa
186
+ kaldırılacak akışı `eventValue` ile ver; vermezsen `ambiguous-event` ile 422 döner. Sayfa düzeyi olaylar için
175
187
  `target: "page"`. Referanssız kalan akış silineceği için `allowFlowDeletion` kuralı (G-21) bu op'ta
176
188
  da geçerlidir.
177
189
 
@@ -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);
@@ -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', {
@@ -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
- collectTypes((await readPage()).schema, typesByElementId);
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) {
@@ -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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@appaflytech/zeyrek-ai-mcp",
3
- "version": "0.0.13",
3
+ "version": "0.0.14",
4
4
  "type": "module",
5
5
  "description": "Zeyrek (NgApps) platform APIs as MCP tools — data, logic and Studio design surface in one gateway",
6
6
  "license": "UNLICENSED",