@appaflytech/zeyrek-ai-mcp 0.0.9 → 0.0.11

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/README.md CHANGED
@@ -4,7 +4,7 @@ Zeyrek (NgApps) platformunu **MCP tool'ları** olarak sunan bağımsız gateway.
4
4
  oturumu Studio arayüzüne hiç girmeden proje kurabilir, veri modeli çıkarabilir, mikroservis ve
5
5
  workflow yazıp çalıştırabilir, sayfa tasarlayabilir.
6
6
 
7
- - **208 tool · 34 modül** — veri/mantık tarafı (project, application, datasource, entity, row,
7
+ - **212 tool · 34 modül** — veri/mantık tarafı (project, application, datasource, entity, row,
8
8
  import, microservice, query, workflow, automation, function, dto, view, dependency), Studio
9
9
  tasarım tarafı (page, **theme**, component, external component, flow, layout, variable, function,
10
10
  folder, file, access, translation), yönetim (template, organization, deployment) ve proje dallanması
@@ -140,9 +140,9 @@ ya da ikisi karışık (`core,page`). `platform` modülü (3 tool) her kümede v
140
140
  | Küme | Tool | Modüller |
141
141
  |---|---|---|
142
142
  | `core` | 136 | platform, project, application, datasource, entity, row, import, microservice, microfunction, query, workflow, automation, function, dto, view, dependency, environment, merge, activity, revision |
143
- | `studio` | 78 | platform, page, theme, component, externalComponent, flow, layout, variable, function, folder, file, import, access, translation, revision |
143
+ | `studio` | 82 | platform, page, theme, component, externalComponent, flow, layout, variable, function, folder, file, import, access, translation, revision |
144
144
  | `admin` | 27 | platform, template, organization, deployment, environment, merge, activity |
145
- | (boş) | **208** | 34 modülün tamamı |
145
+ | (boş) | **212** | 34 modülün tamamı (ölçüm 2026-09-16, `listTools()`) |
146
146
 
147
147
  Bilinmeyen bir modül/küme adı verilirse sunucu açılmaz, geçerli adları listeleyen bir hata yazar.
148
148
 
@@ -281,9 +281,14 @@ kural envanteri [`docs/08-kural-envanteri.md`](docs/08-kural-envanteri.md).
281
281
  Yayın otomatiktir: `main`'e `zeyrek-mcp/` değişikliği girince CI işi `publish:mcp:npm`
282
282
  ([`scripts/ci-publish-npm.sh`](scripts/ci-publish-npm.sh)) paketi üretip npm'deki son sürümle
283
283
  içerik olarak karşılaştırır; pakete giren dosyalar değiştiyse yeni patch sürümünü yayınlar.
284
- Sürümün kaynağı npm'dir, CI `package.json`'ı geri commit'lemez. Minor/major için
285
- `package.json`'daki sürümü elle yükseltip merge edin. Yayını o commit için atlamak: commit
286
- mesajına `DoNotDeploy` (iş elle tetiklenir hâle gelir).
284
+ Yayından sonra CI yeni sürümü `package.json` + `package-lock.json`'a yazıp
285
+ `chore: zeyrek-mcp X.Y.Z [skip ci]` bot commit'iyle `main`'e push eder (yeni pipeline
286
+ başlatmaz); yayın olmazsa commit de olmaz. Push CI_JOB_TOKEN ile yapılır; gereken GitLab
287
+ ayarları `.gitlab-ci.yml`'daki `publish:mcp:npm` notunda. Commit yazılamayıp iş kırmızı düşerse
288
+ push yetkisi olan biri işi Retry eder: yayın tekrarlanmaz, yalnız sürüm `main`'e yazılır.
289
+ Minor/major için `package.json`'daki
290
+ sürümü elle yükseltip merge edin. Yayını o commit için atlamak: commit mesajına `DoNotDeploy`
291
+ (iş elle tetiklenir hâle gelir).
287
292
 
288
293
  Yerelde deneme (yayın yapmaz; `npm ci` çalıştırdığı için bir kopyada ya da Linux container'da):
289
294
 
@@ -0,0 +1,77 @@
1
+ import { z } from 'zod';
2
+ /**
3
+ * TASARIM ANLIK GÖRÜNTÜSÜ — bir tasarım dosyasının (Figma) TEK GEÇİŞTE okunup dosyaya alınmış
4
+ * hâli. `theme_discover`'ın girdisi budur; gateway Figma'ya kendisi gitmez (plan K1), süreç içi
5
+ * önbellek de tutmaz (HTTP taşımasında her POST yeni sunucu kurar — anlık görüntü DOSYADIR, K2).
6
+ *
7
+ * `.strict()` her seviyede: tanınmayan anahtar sessizce yaşamaz. Gerekçe tema şemasıyla aynı —
8
+ * ajan `variables` yerine `tokens` yazarsa bu bir yazım hatası değil, eşlemeye hiç girmeyen ve
9
+ * kimsenin görmediği bir veri kaybıdır. Şema onu istek anında adıyla reddeder.
10
+ *
11
+ * Ölçek: `textStyles.fontSize`, `components.*` uzunlukları ve `layout` px'tir; `effects.opacity`
12
+ * Figma'nın 0–1'idir (tema 0–100 ister, çeviri eşlemede — g-02). `variables` `get_variable_defs`
13
+ * çıktısının KENDİSİDİR: ad yeniden yazılmaz, değer olduğu gibi taşınır; eşleme adı okur.
14
+ */
15
+ const scalar = z.union([z.string(), z.number()]);
16
+ export const TEXT_STYLE_ROLES = ['h1', 'h2', 'h3', 'h4', 'label', 'p', 'small'];
17
+ export const SCREEN_ROLES = ['shell', 'list', 'form', 'detail', 'auth', 'other'];
18
+ /** `hints` değeri: bu Figma adı eşlemeye HİÇ girmesin (bilinçli karar; `unmapped` sebebi olarak döner). */
19
+ export const SNAPSHOT_HINT_IGNORE = 'ignore';
20
+ export const designSnapshotInput = z.object({
21
+ source: z.object({
22
+ kind: z.enum(['figma', 'manual']),
23
+ fileKey: z.string().optional(),
24
+ fileName: z.string().optional(),
25
+ nodeIds: z.array(z.string()).optional(),
26
+ fetchedAt: z.string().optional(),
27
+ calls: z.number().int().optional(),
28
+ characters: z.number().int().optional()
29
+ }).strict(),
30
+ variables: z.record(scalar),
31
+ variablesDark: z.record(scalar).optional(),
32
+ textStyles: z.array(z.object({
33
+ name: z.string(),
34
+ role: z.enum(TEXT_STYLE_ROLES).optional(),
35
+ fontFamily: z.string(),
36
+ fontSize: z.number(),
37
+ fontWeight: scalar.optional(),
38
+ lineHeight: z.number().optional()
39
+ }).strict()).optional(),
40
+ effects: z.array(z.object({
41
+ name: z.string(),
42
+ offsetX: z.number(),
43
+ offsetY: z.number(),
44
+ blur: z.number(),
45
+ spread: z.number(),
46
+ color: z.string(),
47
+ opacity: z.number()
48
+ }).strict()).optional(),
49
+ layout: z.object({
50
+ desktopWidth: z.number().optional(),
51
+ mobileWidth: z.number().optional(),
52
+ gutter: z.number().optional(),
53
+ sidebarWidth: z.number().optional(),
54
+ notes: z.array(z.string()).optional()
55
+ }).strict().optional(),
56
+ components: z.record(z.object({
57
+ background: z.string().optional(),
58
+ color: z.string().optional(),
59
+ border: z.string().optional(),
60
+ borderRadius: scalar.optional(),
61
+ paddingX: z.number().optional(),
62
+ paddingY: z.number().optional(),
63
+ height: z.number().optional(),
64
+ fontSize: z.number().optional(),
65
+ fontWeight: scalar.optional(),
66
+ shadow: z.string().optional()
67
+ }).strict()).optional(),
68
+ screens: z.array(z.object({
69
+ nodeId: z.string(),
70
+ name: z.string(),
71
+ role: z.enum(SCREEN_ROLES),
72
+ screenshot: z.string().optional(),
73
+ notes: z.array(z.string()).optional()
74
+ }).strict()).optional(),
75
+ /** Figma adı → tema yolu (`accent.muted`, `colors.blue.500`) ya da `ignore`; ajanın KALICI kararı. */
76
+ hints: z.record(z.string()).optional()
77
+ }).strict();
@@ -1905,6 +1905,53 @@ export function inertFlexPropWarning(entries) {
1905
1905
  + "sessizce yok sayılır (istek reddedilmez, prop katalogda vardır). Akış düzeni istiyorsan aynı düğüme "
1906
1906
  + "layout:'flex' de yaz, istemiyorsan bu prop'ları kaldır — platform_guide('page') Kural 1f-2."];
1907
1907
  }
1908
+ /**
1909
+ * Akış id'si → ona bağlı olaylar. Bağ üç yerde durur: düğümün `events[].value`'su, düğüm
1910
+ * prop'larındaki `type:'flow'` referansları (`event` alanı `prop:<ad>` olur) ve barındırıcının
1911
+ * düzey olayları (`target: 'page'` — `remove_event`'in aynı hedef adı). Akış tarafından bu
1912
+ * listeyi veren bir uç yok; 2026-09-16 raporunda uygulama kabuğunda hangi olayın hangi akışa
1913
+ * bağlı olduğu kaybolmuş, ikinci "zil rozeti" akışı sahipsiz kalmıştı.
1914
+ */
1915
+ export function flowBindings(schema, hostEvents) {
1916
+ const bindings = new Map();
1917
+ const add = (flowId, binding) => {
1918
+ if (flowId === '')
1919
+ return;
1920
+ bindings.set(flowId.toLowerCase(), [...(bindings.get(flowId.toLowerCase()) ?? []), binding]);
1921
+ };
1922
+ for (const event of asObjects(hostEvents)) {
1923
+ if (typeof event.value === 'string')
1924
+ add(event.value, { target: 'page', event: String(event.name ?? '') });
1925
+ }
1926
+ const visitProp = (target, propName, value) => {
1927
+ if (Array.isArray(value)) {
1928
+ for (const entry of value)
1929
+ visitProp(target, propName, entry);
1930
+ return;
1931
+ }
1932
+ if (!value || typeof value !== 'object')
1933
+ return;
1934
+ const record = value;
1935
+ if (record.type === 'flow' && typeof record.ref === 'string')
1936
+ add(record.ref, { target, event: `prop:${propName}` });
1937
+ for (const entry of Object.values(record))
1938
+ visitProp(target, propName, entry);
1939
+ };
1940
+ const visitNode = (node) => {
1941
+ const target = String(node.elementId ?? node.name ?? '');
1942
+ for (const event of asObjects(node.events)) {
1943
+ if (typeof event.value === 'string')
1944
+ add(event.value, { target, event: String(event.name ?? '') });
1945
+ }
1946
+ for (const [propName, value] of Object.entries((node.props ?? {})))
1947
+ visitProp(target, propName, value);
1948
+ for (const child of asObjects(node.children))
1949
+ visitNode(child);
1950
+ };
1951
+ for (const node of asObjects(schema))
1952
+ visitNode(node);
1953
+ return bindings;
1954
+ }
1908
1955
  /**
1909
1956
  * Bir yamanın SİLECEĞİ akışlar. Platform referanssız kalan akışı KALICI siler; uyarı yamadan sonra
1910
1957
  * geldiğinde "önce yedekle" tavsiyesi uygulanamaz hâle geliyordu (09-05 koşusu: sekiz adımlı giriş
@@ -1,3 +1,4 @@
1
+ import fastJsonPatch from 'fast-json-patch';
1
2
  import { functionSignature } from './functionGuards.js';
2
3
  function asArray(value) {
3
4
  return Array.isArray(value) ? value.filter((entry) => Boolean(entry) && typeof entry === 'object') : [];
@@ -280,22 +281,38 @@ export function describePatchTarget(document, path) {
280
281
  const kind = entry.type ?? entry.dbType;
281
282
  return `${path} → '${label}'${kind ? ` (${String(kind)}` + `, id ${String(entry.id ?? '?')})` : `, id ${String(entry.id ?? '?')}`}`;
282
283
  }
284
+ /**
285
+ * Her op, KENDİSİNDEN ÖNCEKİ op'ların uygulanmış hâlindeki belgeye karşı çözülür ve hemen
286
+ * ardından o tek op çalışan kopyaya uygulanır (S-3): aynı diziye giden birden çok `remove`
287
+ * seçicisi tek seferde ORİJİNAL belgeye göre indekse çevrilseydi, ilk silme diziyi kısaltıp
288
+ * sonraki op'ların indeksini kaydırırdı — yanıt yine de "applied: true" derdi çünkü hedef adı da
289
+ * aynı bayat belgeden okunuyordu. Dönen `operations` dizisi, orijinal belgeye karşı SIRAYLA
290
+ * uygulandığında (tek `fastJsonPatch.applyPatch` çağrısıyla dahi) bu adım adım simülasyonla aynı
291
+ * sonucu üretir; çağıranların uygulama biçimini değiştirmesi gerekmez.
292
+ */
283
293
  export function resolvePatchOperations(document, operations, readTool = 'workflow_get') {
284
294
  const notes = [];
285
295
  const targets = [];
286
- const resolved = operations.map(operation => {
296
+ const resolvedOperations = [];
297
+ let working = structuredClone(document);
298
+ for (const operation of operations) {
287
299
  const path = typeof operation.path === 'string' ? operation.path : '';
288
300
  const op = typeof operation.op === 'string' ? operation.op : '';
289
- const outcome = resolvePatchPointer(document, path, readTool, op);
290
- const from = typeof operation.from === 'string' ? resolvePatchPointer(document, operation.from, readTool, op) : undefined;
301
+ const outcome = resolvePatchPointer(working, path, readTool, op);
302
+ const from = typeof operation.from === 'string' ? resolvePatchPointer(working, operation.from, readTool, op) : undefined;
291
303
  if (outcome.note)
292
304
  notes.push(outcome.note);
293
305
  if (from?.note)
294
306
  notes.push(from.note);
295
- targets.push(describePatchTarget(document, outcome.path));
296
- return from ? { ...operation, path: outcome.path, from: from.path } : { ...operation, path: outcome.path };
297
- });
298
- return { operations: resolved, notes, targets };
307
+ targets.push(describePatchTarget(working, outcome.path));
308
+ const resolvedOperation = from ? { ...operation, path: outcome.path, from: from.path } : { ...operation, path: outcome.path };
309
+ resolvedOperations.push(resolvedOperation);
310
+ // validate:false — bu yalnız SONRAKİ op'ların indeksini doğru izlemek için ara bir simülasyon;
311
+ // gerçek RFC6902 doğrulaması (ör. 'replace' hedefinin önceden var olması) çağıranın az sonra
312
+ // yapacağı asıl uygulamada (validate:true) hâlâ çalışır, burada erken ve gereksiz patlamaz.
313
+ working = fastJsonPatch.applyPatch(working, [resolvedOperation], false, true).newDocument;
314
+ }
315
+ return { operations: resolvedOperations, notes, targets };
299
316
  }
300
317
  /**
301
318
  * Fonksiyon özeti (G-07): kaynak, ad, kapsam, imza, parametre listesi, gövde satır sayısı —
@@ -0,0 +1,151 @@
1
+ import { readKnowledgeFile } from '../resources/knowledge.js';
2
+ import { PlatformApiError } from '../platform/errors.js';
3
+ import { discoverTheme } from './themeDiscover.js';
4
+ const ARCHETYPES = {
5
+ form: { example: 'page-form-upsert', flows: { mount: 'mountFlowCreateArgs', submit: 'submitFlowCreateArgs' } },
6
+ detail: { example: 'page-detail-profile', flows: { mount: 'mountFlowCreateArgs' } },
7
+ auth: { example: 'page-sign-in', flows: { submit: 'submitFlowCreateArgs' } }
8
+ };
9
+ const LIST_ARCHETYPE = 'list (kod içi iskelet) + flow-list-to-form';
10
+ const SHELL_EXAMPLE = 'layout-app-shell';
11
+ const LIST_EVENTS_EXAMPLE = 'flow-list-to-form';
12
+ const turkishMap = { ç: 'c', ğ: 'g', ı: 'i', ö: 'o', ş: 's', ü: 'u', â: 'a', î: 'i', û: 'u' };
13
+ export function slugifyScreenName(name) {
14
+ return name.toLocaleLowerCase('tr')
15
+ .replace(/[çğıöşüâîû]/g, letter => turkishMap[letter] ?? letter)
16
+ .replace(/[^a-z0-9]+/g, '-')
17
+ .replace(/^-+|-+$/g, '');
18
+ }
19
+ const readExample = (name) => JSON.parse(readKnowledgeFile('examples', `${name}.json`));
20
+ const isPlaceholder = (value) => typeof value === 'string' && value.startsWith('<');
21
+ const staticProp = (data) => ({ type: 'static', data });
22
+ const node = (elementId, type, props, wrapper, block) => ({
23
+ elementId,
24
+ kind: 'component',
25
+ type,
26
+ name: elementId,
27
+ props,
28
+ states: { isSticky: false, isDroppable: wrapper, isScrollable: false, isWrapper: wrapper },
29
+ blocks: [{ breakpoint: 1024, size: { h: block.h, w: block.w }, position: { x: block.x, y: block.y } }],
30
+ children: [],
31
+ events: [],
32
+ validations: [],
33
+ stateBindings: []
34
+ });
35
+ /** Liste sayfası asgari iskeleti: araç çubuğu (Yeni) + kayıt tablosu; adlar `flow-list-to-form` ile aynı. */
36
+ function listSkeleton() {
37
+ return [
38
+ { op: 'add_component', parent: 'page_container', component: node('list_toolbar', 'container', { layout: staticProp('flex'), direction: staticProp('row') }, true, { h: 4, w: 12, x: 0, y: 0 }) },
39
+ { op: 'add_component', parent: 'list_toolbar', component: node('btn_new', 'button', { text: staticProp('Yeni') }, false, { h: 4, w: 3, x: 0, y: 0 }) },
40
+ { op: 'add_component', parent: 'page_container', component: node('records_table', 'table', {}, false, { h: 40, w: 12, x: 0, y: 4 }) }
41
+ ];
42
+ }
43
+ function pageArgs(screenName, base, options, caveats) {
44
+ const args = { ...base, name: screenName, slug: slugifyScreenName(screenName), browserTitle: screenName };
45
+ if (options.layoutId !== undefined) {
46
+ args.layoutId = options.layoutId;
47
+ }
48
+ else {
49
+ if (isPlaceholder(args.layoutId) || args.layoutId === undefined)
50
+ delete args.layoutId;
51
+ caveats.push("layoutId verilmedi — sayfa kabuksuz açılır; kabuk için 'shell' ekranını page_discover ile layout_create'e çevirip (ya da layout_list ile mevcut kabuğu bulup) pageCreateArgs.layoutId verin");
52
+ }
53
+ if (options.middlewareIds !== undefined) {
54
+ args.middlewareIds = options.middlewareIds;
55
+ }
56
+ else if (Array.isArray(args.middlewareIds) && args.middlewareIds.some(isPlaceholder)) {
57
+ delete args.middlewareIds;
58
+ caveats.push('middlewareIds verilmedi — örnek korumalı sayfa istiyor; auth middleware id için middleware_list, sonra pageCreateArgs.middlewareIds (bir-iki ucu bağlayıp gerisini açık bırakmak yasak)');
59
+ }
60
+ return args;
61
+ }
62
+ /** Tek aday preset bağlanır (`props.style = { $preset }`); birden çok aday karar ister (K4). */
63
+ function bindPresets(ops, presets, caveats) {
64
+ const bindings = [];
65
+ for (const op of ops) {
66
+ if (op.op !== 'add_component')
67
+ continue;
68
+ const component = op.component;
69
+ const type = typeof component?.type === 'string' ? component.type : undefined;
70
+ const elementId = typeof component?.elementId === 'string' ? component.elementId : undefined;
71
+ if (component === undefined || type === undefined || elementId === undefined)
72
+ continue;
73
+ const candidates = presets.filter(preset => preset === type || preset.startsWith(`${type}-`));
74
+ if (candidates.length === 1) {
75
+ component.props = { ...(component.props ?? {}), style: { $preset: candidates[0] } };
76
+ bindings.push({ elementId, type, preset: candidates[0] });
77
+ }
78
+ else if (candidates.length > 1) {
79
+ caveats.push(`${elementId} (${type}) için birden çok preset adayı var: ${candidates.join(', ')} — hangisi olacağına siz karar verin (page_schema_patch replace_prop style { $preset })`);
80
+ }
81
+ }
82
+ return bindings;
83
+ }
84
+ export function discoverPage(snapshot, screenRef, options = {}) {
85
+ const screens = snapshot.screens ?? [];
86
+ const screen = screens.find(entry => entry.nodeId === screenRef) ?? screens.find(entry => entry.name === screenRef);
87
+ if (screen === undefined) {
88
+ throw new PlatformApiError(`page_discover: '${screenRef}' anlık görüntünün screens listesinde yok (nodeId ya da name). Mevcut ekranlar: ${screens.map(entry => `${entry.name} (${entry.nodeId}, ${entry.role})`).join(' · ') || 'yok'}.`, 400);
89
+ }
90
+ const caveats = [];
91
+ const notes = [...(snapshot.layout?.notes ?? []), ...(screen.notes ?? [])];
92
+ const head = { screen: { nodeId: screen.nodeId, name: screen.name, role: screen.role }, notes };
93
+ const presets = Object.keys(discoverTheme(snapshot).theme.presets ?? {});
94
+ if (screen.role === 'shell') {
95
+ const example = readExample(SHELL_EXAMPLE);
96
+ return {
97
+ ...head,
98
+ archetype: SHELL_EXAMPLE,
99
+ layoutCreateArgs: { ...example.layoutCreate, name: screen.name },
100
+ schemaPatchOps: [],
101
+ flows: {},
102
+ presetBindings: [],
103
+ caveats: ['kabuk bir SAYFA değil LAYOUT kaydıdır; yan menü/üst şerit içeriğini ekran notlarına göre layout_schema_patch ile doldurun'],
104
+ next: 'layout_create(layoutCreateArgs) → layout_list ile id → diğer ekranların pageCreateArgs.layoutId'
105
+ };
106
+ }
107
+ if (screen.role === 'other') {
108
+ return {
109
+ ...head,
110
+ archetype: 'yok',
111
+ pageCreateArgs: pageArgs(screen.name, {}, options, caveats),
112
+ schemaPatchOps: [],
113
+ flows: {},
114
+ presetBindings: [],
115
+ caveats: [...caveats, "rol 'other' — arketip yok; ekran notlarına göre page_schema_patch ile elle kurun (platform_guide('page') §arketipler) ya da anlık görüntüde role list/form/detail/auth verin"],
116
+ next: "page_create(pageCreateArgs) → page_schema_patch(ops — elle) → page_screenshot"
117
+ };
118
+ }
119
+ if (screen.role === 'list') {
120
+ const events = readExample(LIST_EVENTS_EXAMPLE);
121
+ const ops = [...listSkeleton(), ...(events.schemaPatchOps ?? [])];
122
+ const presetBindings = bindPresets(ops, presets, caveats);
123
+ caveats.push("records_table props boş — kolonları ve veri kaynağını component_get('table') + flow-load-table örneğine göre verin; set_event op'larındaki '<… flow id>' yer tutucuları flow_create sonrası gerçek id ile değiştirilir");
124
+ return {
125
+ ...head,
126
+ archetype: LIST_ARCHETYPE,
127
+ pageCreateArgs: pageArgs(screen.name, {}, options, caveats),
128
+ schemaPatchOps: ops,
129
+ flows: { openRecord: events.openRecordFlowCreateArgs, newRecord: events.newRecordFlowCreateArgs },
130
+ presetBindings,
131
+ caveats,
132
+ next: 'page_create(pageCreateArgs) → flow_create(flows.openRecord, flows.newRecord) → set_event yer tutucularını flow id ile değiştir → page_schema_patch(schemaPatchOps) → page_screenshot'
133
+ };
134
+ }
135
+ const archetype = ARCHETYPES[screen.role];
136
+ const example = readExample(archetype.example);
137
+ const ops = structuredClone(example.schemaPatchOps ?? []);
138
+ const presetBindings = bindPresets(ops, presets, caveats);
139
+ const flows = Object.fromEntries(Object.entries(archetype.flows).map(([key, field]) => [key, example[field]]).filter(([, value]) => value !== undefined));
140
+ caveats.push(`iskelet '${archetype.example}' altın örneğinden — elementId'ler ve metinler örneğin domainindedir (${String(example.pageCreateArgs?.name ?? '')}); ekran notlarına göre alan adlarını, etiketleri ve akış adımlarını değiştirin`);
141
+ return {
142
+ ...head,
143
+ archetype: archetype.example,
144
+ pageCreateArgs: pageArgs(screen.name, example.pageCreateArgs ?? {}, options, caveats),
145
+ schemaPatchOps: ops,
146
+ flows,
147
+ presetBindings,
148
+ caveats,
149
+ next: 'page_create(pageCreateArgs) → page_schema_patch(pageId, schemaPatchOps) → flow_create(flows.*) ve set_event yer tutucularını flow id ile değiştir → page_screenshot'
150
+ };
151
+ }