@appaflytech/zeyrek-ai-mcp 0.0.20 → 0.0.21
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 +14 -13
- package/dist/config.js +0 -2
- package/dist/knowledge/componentCatalog.js +940 -215
- package/dist/knowledge/componentEvents.js +368 -75
- package/dist/knowledge/normalize.js +66 -63
- package/dist/knowledge/rules.js +29 -6
- package/dist/knowledge/styleResolve.js +1 -1
- package/dist/knowledge/themeResolve.js +3 -3
- package/dist/platform/diagnostics.js +85 -0
- package/dist/platform/domSummary.js +1 -1
- package/dist/platform/ifExists.js +129 -0
- package/dist/platform/responses.js +2 -1
- package/dist/platform/{capture/capture.js → screenshot.js} +509 -23
- package/dist/platform/strictArguments.js +22 -2
- package/dist/platform/writeDiff.js +58 -31
- package/dist/platform/writeView.js +17 -0
- package/dist/resources/guides/component.md +9 -6
- package/dist/resources/guides/functions.md +32 -3
- package/dist/resources/guides/getting-started.md +35 -0
- package/dist/resources/guides/layout.md +7 -11
- package/dist/resources/guides/page.md +49 -71
- package/dist/resources/guides/translation.md +5 -6
- package/dist/resources/schema-fingerprint.json +2 -2
- package/dist/resources/schemas/GlobalFunctionUpdateResultDto.json +18 -0
- package/dist/server.js +0 -2
- package/dist/tools/applications.js +3 -60
- package/dist/tools/components.js +10 -50
- package/dist/tools/designSchemaPatch.js +13 -18
- package/dist/tools/externalComponents.js +4 -25
- package/dist/tools/flows.js +38 -51
- package/dist/tools/functions.js +20 -10
- package/dist/tools/layouts.js +9 -41
- package/dist/tools/microservices.js +39 -11
- package/dist/tools/pages.js +95 -127
- package/dist/tools/platformInfo.js +1 -2
- package/dist/tools/schemaOps.js +2 -2
- package/dist/tools/screenWrite.js +2 -2
- package/dist/tools/screens.js +3 -9
- package/dist/tools/workflows.js +39 -12
- package/package.json +7 -2
- package/dist/knowledge/mobileComponentCatalog.js +0 -331
- package/dist/knowledge/mobileComponentEvents.js +0 -50
- package/dist/knowledge/platform/lint.js +0 -205
- package/dist/knowledge/platform/mobile.js +0 -78
- package/dist/knowledge/platform/mobileRules.js +0 -121
- package/dist/knowledge/platform/mobileTabs.js +0 -108
- package/dist/knowledge/platform/profile.js +0 -20
- package/dist/knowledge/platform/resolve.js +0 -122
- package/dist/knowledge/platform/web.js +0 -38
- package/dist/knowledge/styleResolveNative.js +0 -410
- package/dist/platform/capture/actions.js +0 -333
- package/dist/platform/capture/browser.js +0 -1
- package/dist/platform/capture/index.js +0 -10
- package/dist/platform/capture/readiness.js +0 -103
- package/dist/platform/capture/summary.js +0 -77
- package/dist/platform/capture/target.js +0 -5
- package/dist/platform/capture/targets/studio-mobile.js +0 -15
- package/dist/platform/capture/targets/studio-web.js +0 -5
- package/dist/resources/examples/mobile-app-shell-drawer.json +0 -209
- package/dist/resources/examples/mobile-app-shell-stack.json +0 -123
- package/dist/resources/examples/mobile-app-shell-tab.json +0 -174
- package/dist/resources/examples/mobile-list-detail-form.json +0 -656
- package/dist/resources/guides/mobile.md +0 -215
- package/dist/tools/mobile.js +0 -203
- package/dist/tools/profileWrite.js +0 -45
|
@@ -1,4 +1,15 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
+
import { diagnosticsParam, runWithDiagnostics } from './diagnostics.js';
|
|
3
|
+
/**
|
|
4
|
+
* `writes`/`updates` açıklamalı ama KAYIT yazmayan tool'lar: çalıştırma, önizleme, dışa aktarma, indirme,
|
|
5
|
+
* bağlantı testi. Yanıtlarında yazma tanılaması (silentlyDropped) yoktur; parametre yalnız tool listesini
|
|
6
|
+
* büyütür. Yeni böyle bir tool eklenirse buraya yazılır — `test/diagnostics.test.ts` listeyi kilitler.
|
|
7
|
+
*/
|
|
8
|
+
const NON_WRITING_TOOLS = new Set([
|
|
9
|
+
'microservice_execute', 'microservice_preview', 'microservice_debug', 'query_preview', 'workflow_preview',
|
|
10
|
+
'automation_preview', 'automation_trigger', 'row_export', 'file_download', 'microfunction_invoke',
|
|
11
|
+
'microfunction_runtime_test_connection'
|
|
12
|
+
]);
|
|
2
13
|
function isRawShape(value) {
|
|
3
14
|
if (!value || typeof value !== 'object' || '_def' in value)
|
|
4
15
|
return false;
|
|
@@ -19,14 +30,23 @@ export function rejectUnknownArguments(server) {
|
|
|
19
30
|
server.registerTool = ((name, config, callback) => {
|
|
20
31
|
if (!isRawShape(config.inputSchema))
|
|
21
32
|
return registerTool(name, config, callback);
|
|
22
|
-
|
|
33
|
+
// Yazma tool'ları tanılama kipini alır; parametre handler'a ULAŞMAZ (gövdeyi `...rest` ile
|
|
34
|
+
// platforma geçiren tool'larda sızardı), yalnız toolResult'ın okuduğu bağlama konur.
|
|
35
|
+
const writesDiagnostics = config.annotations?.readOnlyHint === false && config.annotations.destructiveHint !== true && !NON_WRITING_TOOLS.has(name);
|
|
36
|
+
const shape = writesDiagnostics ? { ...config.inputSchema, diagnostics: diagnosticsParam } : config.inputSchema;
|
|
37
|
+
const run = writesDiagnostics
|
|
38
|
+
? ((args, extra) => {
|
|
39
|
+
const { diagnostics, ...rest } = args ?? {};
|
|
40
|
+
return runWithDiagnostics(diagnostics === 'full' ? 'full' : 'compact', () => callback(rest, extra));
|
|
41
|
+
})
|
|
42
|
+
: callback;
|
|
23
43
|
const accepted = Object.keys(shape).join(', ');
|
|
24
44
|
const inputSchema = z.object(shape, {
|
|
25
45
|
errorMap: (issue, context) => issue.code === 'unrecognized_keys'
|
|
26
46
|
? { message: `${name} şu üst düzey argümanları tanımıyor: ${issue.keys.map(key => `'${key}'`).join(', ')}. Tanınmayan alan sessizce yok sayılmaz — çağrı hiç gönderilmedi. Bu tool'un okuduğu alanlar: ${accepted}.` }
|
|
27
47
|
: { message: context.defaultError }
|
|
28
48
|
}).strict();
|
|
29
|
-
return registerTool(name, { ...config, inputSchema },
|
|
49
|
+
return registerTool(name, { ...config, inputSchema }, run);
|
|
30
50
|
});
|
|
31
51
|
return server;
|
|
32
52
|
}
|
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
import { PROP_VALUE_TYPES } from '../knowledge/normalize.js';
|
|
2
2
|
import { logStep } from '../observability/trace.js';
|
|
3
3
|
const maxDrops = 20;
|
|
4
|
+
const maxCollected = 1000;
|
|
4
5
|
const maxDepth = 12;
|
|
5
6
|
const missing = Symbol('missing');
|
|
6
7
|
/**
|
|
7
8
|
* Platformun kendi doldurduğu alanlar. Yalnız gönderilen tarafta da varsalar (patch yolunda gövde
|
|
8
9
|
* okunup geri yazılır) fark üretirlerdi ve bu fark hep gürültüdür: `audit.updatedAt` her yazmada
|
|
9
|
-
* değişir. 09-05 koşusunda `microservice_patch` yanıtındaki iki satırdan biri buydu.
|
|
10
|
+
* değişir. 09-05 koşusunda `microservice_patch` yanıtındaki iki satırdan biri buydu. `version` iyimser
|
|
11
|
+
* eşzamanlılık sayacıdır: okunan belge geri yazılınca platform onu artırır, fark her zaman gürültüdür.
|
|
10
12
|
*/
|
|
11
13
|
const serverManagedKeys = new Set([
|
|
12
|
-
'audit', 'createdAt', 'updatedAt', 'createdUserId', 'createdUserName', 'updatedUserId', 'updatedUserName'
|
|
14
|
+
'audit', 'createdAt', 'updatedAt', 'createdUserId', 'createdUserName', 'updatedUserId', 'updatedUserName', 'version'
|
|
13
15
|
]);
|
|
14
16
|
/**
|
|
15
17
|
* Platformun KENDİ ürettiği kimlik. Gönderilen id kaydedilenden farklıysa bu bir kayıp değildir:
|
|
@@ -50,6 +52,29 @@ export const unstoredKeys = new Set(['fieldSlug']);
|
|
|
50
52
|
const canonicalValues = {
|
|
51
53
|
type: [['string', 'text']]
|
|
52
54
|
};
|
|
55
|
+
export function emptyWriteDiff() {
|
|
56
|
+
return { dropped: [], normalized: [], droppedCount: 0, droppedCountCapped: false, notes: [] };
|
|
57
|
+
}
|
|
58
|
+
function addDrop(sink, line) {
|
|
59
|
+
if (sink.droppedCount >= maxCollected) {
|
|
60
|
+
sink.droppedCountCapped = true;
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
sink.dropped.push(line);
|
|
64
|
+
sink.droppedCount++;
|
|
65
|
+
}
|
|
66
|
+
/** Listeyi `maxDrops` satırda keser; sayı ve tavan bilgisi notlara yazılır. */
|
|
67
|
+
function finishReport(sink) {
|
|
68
|
+
if (sink.dropped.length > maxDrops) {
|
|
69
|
+
const total = sink.droppedCountCapped ? `en az ${sink.droppedCount}` : `toplam ${sink.droppedCount}`;
|
|
70
|
+
sink.dropped = sink.dropped.slice(0, maxDrops);
|
|
71
|
+
sink.notes.push(`ilk ${maxDrops} fark listelendi, ${total} — gövdeyi *_get ile okuyup karşılaştırın.`);
|
|
72
|
+
}
|
|
73
|
+
else if (sink.droppedCountCapped) {
|
|
74
|
+
sink.notes.push(`sayım en az ${sink.droppedCount} farkta durdu.`);
|
|
75
|
+
}
|
|
76
|
+
return sink;
|
|
77
|
+
}
|
|
53
78
|
/**
|
|
54
79
|
* Yolun son alan adı; `tags[0]` ya da `fields[fieldId=x]` gibi indeks/anahtar ekini atar.
|
|
55
80
|
*/
|
|
@@ -81,11 +106,11 @@ function lookup(saved, key) {
|
|
|
81
106
|
return Object.hasOwn(record, key) ? record[key] : missing;
|
|
82
107
|
}
|
|
83
108
|
function compare(sent, saved, path, sink, depth) {
|
|
84
|
-
if (sink.
|
|
109
|
+
if (sink.droppedCountCapped || depth > maxDepth)
|
|
85
110
|
return;
|
|
86
111
|
if (Array.isArray(sent)) {
|
|
87
112
|
if (saved === missing) {
|
|
88
|
-
sink
|
|
113
|
+
addDrop(sink, `${path}: gönderilen ${render(sent)} ↔ kaydedilen yok`);
|
|
89
114
|
return;
|
|
90
115
|
}
|
|
91
116
|
const key = Array.isArray(saved) ? identityKeyFor(sent) : undefined;
|
|
@@ -103,18 +128,18 @@ function compare(sent, saved, path, sink, depth) {
|
|
|
103
128
|
}
|
|
104
129
|
return;
|
|
105
130
|
}
|
|
106
|
-
const before = sink.
|
|
131
|
+
const before = sink.droppedCount;
|
|
107
132
|
sent.forEach((entry, index) => compare(entry, lookup(saved, index), `${path}[${index}]`, sink, depth + 1));
|
|
108
133
|
// Sıra tabanlı karşılaştırmada "değişmiş" görünen öğe aslında yer değiştirmiş olabilir. Tek
|
|
109
134
|
// elemanlı dizide sıra farkı imkânsız olduğu için not düşülmez (2026-09-06 · S-1).
|
|
110
|
-
if (Array.isArray(saved) && sent.length > 1 && saved.length > 1 && sink.
|
|
111
|
-
sink.
|
|
135
|
+
if (Array.isArray(saved) && sent.length > 1 && saved.length > 1 && sink.droppedCount > before) {
|
|
136
|
+
sink.notes.push(`${path}: (sıra tabanlı karşılaştırma — anahtar yok) aşağıdaki farklar platform diziyi başka sırada sakladıysa yanıltıcıdır`);
|
|
112
137
|
}
|
|
113
138
|
return;
|
|
114
139
|
}
|
|
115
140
|
if (sent && typeof sent === 'object') {
|
|
116
141
|
if (saved === missing) {
|
|
117
|
-
sink
|
|
142
|
+
addDrop(sink, `${path}: gönderilen ${render(sent)} ↔ kaydedilen yok`);
|
|
118
143
|
return;
|
|
119
144
|
}
|
|
120
145
|
for (const [key, entry] of Object.entries(sent)) {
|
|
@@ -130,7 +155,7 @@ function compare(sent, saved, path, sink, depth) {
|
|
|
130
155
|
if (unstoredKeys.has(field) && (saved === missing || saved === null))
|
|
131
156
|
return;
|
|
132
157
|
if (saved === missing) {
|
|
133
|
-
sink
|
|
158
|
+
addDrop(sink, `${path}: gönderilen ${render(sent)} ↔ kaydedilen yok`);
|
|
134
159
|
return;
|
|
135
160
|
}
|
|
136
161
|
if (sent === saved)
|
|
@@ -141,7 +166,7 @@ function compare(sent, saved, path, sink, depth) {
|
|
|
141
166
|
sink.normalized.push(`${path}: gönderilen ${render(sent)} platformun kanonik adıyla ${render(saved)} olarak saklandı — eşdeğer, kayıp değil`);
|
|
142
167
|
return;
|
|
143
168
|
}
|
|
144
|
-
sink
|
|
169
|
+
addDrop(sink, `${path}: gönderilen ${render(sent)} ↔ kaydedilen ${render(saved)}`);
|
|
145
170
|
}
|
|
146
171
|
/**
|
|
147
172
|
* Sol taraf platforma GİDEN gövdedir (gateway normalizasyonu uygulandıktan sonrası), sağ taraf
|
|
@@ -152,8 +177,8 @@ function compare(sent, saved, path, sink, depth) {
|
|
|
152
177
|
* İki liste döner: `dropped` gerçekten kaybolan alanlar, `normalized` platformun kanonik adıyla
|
|
153
178
|
* sakladığı eşdeğerler. İkincisi `gatewayNormalized`a gider, uyarıya değil.
|
|
154
179
|
*/
|
|
155
|
-
export function writeDiff(sent, saved, aliases = {}) {
|
|
156
|
-
const sink =
|
|
180
|
+
export function writeDiff(sent, saved, aliases = {}, finish = true) {
|
|
181
|
+
const sink = emptyWriteDiff();
|
|
157
182
|
if (!sent || typeof sent !== 'object')
|
|
158
183
|
return sink;
|
|
159
184
|
const wrapper = saved && typeof saved === 'object' && !Array.isArray(saved)
|
|
@@ -171,19 +196,16 @@ export function writeDiff(sent, saved, aliases = {}) {
|
|
|
171
196
|
if (keys.length > 0 && shared.length === 0)
|
|
172
197
|
return sink;
|
|
173
198
|
compare(comparable, root, '', sink, 0);
|
|
174
|
-
|
|
175
|
-
sink.dropped.push(`… ilk ${maxDrops} fark listelendi, daha fazla fark var — gövdeyi *_get ile okuyup karşılaştırın.`);
|
|
176
|
-
}
|
|
177
|
-
return sink;
|
|
199
|
+
return finish ? finishReport(sink) : sink;
|
|
178
200
|
}
|
|
179
201
|
export function silentlyDropped(sent, saved, aliases = {}) {
|
|
180
202
|
return writeDiff(sent, saved, aliases).dropped;
|
|
181
203
|
}
|
|
182
204
|
export function withWriteDiff(payload, diff) {
|
|
183
|
-
const drops =
|
|
184
|
-
const normalized =
|
|
205
|
+
const drops = diff.dropped;
|
|
206
|
+
const normalized = diff.normalized ?? [];
|
|
185
207
|
if (drops.length > 0) {
|
|
186
|
-
logStep('sessiz düşme ⚠', `${
|
|
208
|
+
logStep('sessiz düşme ⚠', `${diff.droppedCount}${diff.droppedCountCapped ? '+' : ''} alan gönderildi ama kayda geçmedi — platform hata vermeden yok saydı, yanıta silentlyDropped olarak eklenir`, [...drops, ...diff.notes]);
|
|
187
209
|
}
|
|
188
210
|
if (normalized.length > 0) {
|
|
189
211
|
logStep('düzeltme', `platform ${normalized.length} alanı kanonik adıyla sakladı — eşdeğer, yanıta gatewayNormalized olarak eklenir`, normalized);
|
|
@@ -195,7 +217,9 @@ export function withWriteDiff(payload, diff) {
|
|
|
195
217
|
const existing = payload.gatewayNormalized;
|
|
196
218
|
return {
|
|
197
219
|
...payload,
|
|
198
|
-
...(drops.length > 0 ? { silentlyDropped: drops } : {}),
|
|
220
|
+
...(drops.length > 0 ? { silentlyDropped: drops, droppedCount: diff.droppedCount } : {}),
|
|
221
|
+
...(drops.length > 0 && diff.droppedCountCapped ? { droppedCountCapped: true } : {}),
|
|
222
|
+
...(drops.length > 0 && diff.notes.length > 0 ? { silentlyDroppedNotes: diff.notes } : {}),
|
|
199
223
|
...(normalized.length > 0
|
|
200
224
|
? { gatewayNormalized: [...(Array.isArray(existing) ? existing : []), ...normalized] }
|
|
201
225
|
: {})
|
|
@@ -247,9 +271,9 @@ function findByElementId(nodes, elementId) {
|
|
|
247
271
|
* yanıt yalnız `appliedPatch` olduğu için karşılaştırma yamadan SONRA okunan şemaya dayanır.
|
|
248
272
|
*/
|
|
249
273
|
export function schemaOpDrops(schema, ops) {
|
|
274
|
+
const drops = { dropped: [], droppedCount: 0, droppedCountCapped: false, notes: [] };
|
|
250
275
|
if (!Array.isArray(ops))
|
|
251
|
-
return
|
|
252
|
-
const drops = [];
|
|
276
|
+
return drops;
|
|
253
277
|
for (const op of ops) {
|
|
254
278
|
if (op.op === 'add_component') {
|
|
255
279
|
const component = (op.component ?? {});
|
|
@@ -258,12 +282,10 @@ export function schemaOpDrops(schema, ops) {
|
|
|
258
282
|
if (!elementId)
|
|
259
283
|
continue;
|
|
260
284
|
if (!saved) {
|
|
261
|
-
drops
|
|
285
|
+
addDrop(drops, `add_component '${elementId}': komponent şemada bulunamadı — yama 200 döndü ama düğüm kaydedilmemiş.`);
|
|
262
286
|
continue;
|
|
263
287
|
}
|
|
264
|
-
|
|
265
|
-
drops.push(`add_component '${elementId}' → ${entry}`);
|
|
266
|
-
}
|
|
288
|
+
mergeNested(drops, writeDiff(unwrapProps(component), unwrapProps(saved), {}, false), `add_component '${elementId}' → `);
|
|
267
289
|
continue;
|
|
268
290
|
}
|
|
269
291
|
if (op.op === 'replace_prop' && typeof op.target === 'string' && typeof op.prop === 'string') {
|
|
@@ -272,13 +294,18 @@ export function schemaOpDrops(schema, ops) {
|
|
|
272
294
|
continue;
|
|
273
295
|
const props = (saved.props ?? {});
|
|
274
296
|
if (!Object.hasOwn(props, op.prop)) {
|
|
275
|
-
drops
|
|
297
|
+
addDrop(drops, `replace_prop '${op.target}.${op.prop}': prop şemada yok — değer kaydedilmemiş.`);
|
|
276
298
|
continue;
|
|
277
299
|
}
|
|
278
|
-
|
|
279
|
-
drops.push(`replace_prop '${op.target}' → ${entry}`);
|
|
280
|
-
}
|
|
300
|
+
mergeNested(drops, writeDiff({ [op.prop]: unwrapProps(op.value) }, { [op.prop]: unwrapProps(props[op.prop]) }, {}, false), `replace_prop '${op.target}' → `);
|
|
281
301
|
}
|
|
282
302
|
}
|
|
283
|
-
return drops
|
|
303
|
+
return finishReport(drops);
|
|
304
|
+
}
|
|
305
|
+
/** İç karşılaştırmanın satırlarını, sayısını ve notlarını dış rapora önekle taşır; kesme en dışta yapılır. */
|
|
306
|
+
function mergeNested(target, nested, prefix) {
|
|
307
|
+
for (const line of nested.dropped)
|
|
308
|
+
addDrop(target, `${prefix}${line}`);
|
|
309
|
+
target.droppedCountCapped = target.droppedCountCapped || nested.droppedCountCapped;
|
|
310
|
+
target.notes.push(...nested.notes.map(note => `${prefix}${note}`));
|
|
284
311
|
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export const recordWriteViewParam = z.enum(['outline', 'full']).optional().describe("Yanıttaki kaydın biçimi. outline (VARSAYILAN): data yalnız kimlik alanlarını taşır (id, name, slug, version ve türüne göre url/httpMethod/source/type) — gönderdiğiniz gövdenin yankısı bağlama tekrar girmez; 'kaydedildi mi' kanıtı silentlyDropped/droppedCount'tadır. full: platformun döndürdüğü gövde aynen gelir.");
|
|
3
|
+
/**
|
|
4
|
+
* Create/update yanıtı platformun kaydettiği tanımın TAMAMINI döner; ajan aynı turda o gövdeyi
|
|
5
|
+
* kendisi göndermiştir. Kırpma yalnız bağımsız doğrulama yolu olan yerde yapılır: bu tool'larda
|
|
6
|
+
* `writeDiff` tam yanıt üzerinden ÖNCE hesaplanır, projeksiyon ondan sonra uygulanır.
|
|
7
|
+
*/
|
|
8
|
+
export function outlineWrite(payload, view, keys) {
|
|
9
|
+
if (view === 'full' || !payload || typeof payload !== 'object' || Array.isArray(payload))
|
|
10
|
+
return payload;
|
|
11
|
+
const record = payload;
|
|
12
|
+
const data = record.data;
|
|
13
|
+
if (!data || typeof data !== 'object' || Array.isArray(data))
|
|
14
|
+
return payload;
|
|
15
|
+
const source = data;
|
|
16
|
+
return { ...record, data: Object.fromEntries(['id', 'name', 'slug', 'version', ...keys].filter(key => Object.hasOwn(source, key)).map(key => [key, source[key]])) };
|
|
17
|
+
}
|
|
@@ -3,11 +3,6 @@
|
|
|
3
3
|
Sayfaya konan her şeyin ortak kuralları. Kuruluş sırası ve şema gövdesi için
|
|
4
4
|
`platform_guide('page')`; HTML kaçış kapısının derin sözleşmesi için `platform_guide('html')`.
|
|
5
5
|
|
|
6
|
-
> **Mobil uygulamada** katalog ayrıdır: `component_list`/`component_get` mobil satırları okur,
|
|
7
|
-
> `component_get` `mobileKnowledge` (bileşenin mobil farkları) ve `mobileNotes` ekler; katalogda
|
|
8
|
-
> olmayan tip `MOB-003` alır. Stil CSS değil React Native'dir — `page_runtime_get(resolveStyles)`
|
|
9
|
-
> `nativeStyle` döner (`platform_guide('mobile')`).
|
|
10
|
-
|
|
11
6
|
## Keşif: iki tool, üç bilgi katmanı
|
|
12
7
|
|
|
13
8
|
```
|
|
@@ -34,6 +29,14 @@ component_get → tek tipin TAM sözleşmesi, üç katman birleşik:
|
|
|
34
29
|
- **Boyut:** `blocks[].size` katalogdaki `maxSize`'ı aşamaz; yükseklikte
|
|
35
30
|
`appearance.heightType: 'auto'` tercih edilir. Katalog varsayılanı bazı tiplerde kendi
|
|
36
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.
|
|
37
40
|
|
|
38
41
|
### Arayüz metni ve biçimler uygulama dilini izler
|
|
39
42
|
|
|
@@ -105,7 +108,7 @@ Her şema düğümünde iki sözlük vardır; ikisi de ortak render katmanında
|
|
|
105
108
|
|
|
106
109
|
| `states` | Anlamı |
|
|
107
110
|
|---|---|
|
|
108
|
-
| `isDroppable` | İçine komponent alır (container/form/repeater true)
|
|
111
|
+
| `isDroppable` | İçine komponent alır (container/form/repeater true) |
|
|
109
112
|
| `isScrollable` | İçeriği kendi alanında kaydırır (sabit yükseklikte `overflow-y: auto`) |
|
|
110
113
|
| `isWrapper` | Sayfanın/layout'un/widget'ın ANA kabı — salt düzen elemanı: `height:100%` + **`min-height:80vh`**, kap başlığı ÇİZİLMEZ. **Yalnız sayfa boyu kaplaması gereken tek kap için kullan:** iç içe kutuya verirsen 720px'lik pencerede o kutu 36px içerikle bile 576px çizilir (ölçüldü, `platform_guide('page')` Kural 1f). Çerçeve/başlık gizlemek için wrapper değil `style` (`border:'none'`) kullan. Kurulmuş bir düğümün bayrağını `resize_component` micro-op'unun `states` alanı değiştirir — düğümü yeniden kurmak gerekmez |
|
|
111
114
|
| `isSticky` | Kaydırmada yerinde kalır (header/sidebar; z-index'i otomatik) |
|
|
@@ -105,14 +105,16 @@ globalThis["toplamHesapla"] = function (fiyat, adet) { // studio: Function
|
|
|
105
105
|
"name": "toplamHesapla", "description": "fiyat × adet; adet verilmezse 1",
|
|
106
106
|
"parameters": [ { "name": "fiyat", "type": "number", "required": true },
|
|
107
107
|
{ "name": "adet", "type": "number", "defaultValue": 1 } ],
|
|
108
|
-
"body": "
|
|
108
|
+
"body": "// fiyat × adet; adet verilmezse 1 sayılır
|
|
109
|
+
return fiyat * adet;" }
|
|
109
110
|
|
|
110
111
|
// tarayıcı
|
|
111
112
|
{ "source": "studio", "applicationId": "<app id>", "scope": "application",
|
|
112
113
|
"name": "toplamHesapla", "description": "fiyat × adet; adet verilmezse 1",
|
|
113
114
|
"isAsync": false,
|
|
114
115
|
"parameters": [ … aynı liste … ],
|
|
115
|
-
"body": "
|
|
116
|
+
"body": "// fiyat × adet; adet verilmezse 1 sayılır
|
|
117
|
+
return fiyat * adet;" }
|
|
116
118
|
```
|
|
117
119
|
|
|
118
120
|
Gövde **düz deyimlerdir** — `function (…) { }` sarmalı ya da arrow yazılmaz; `return` serbesttir,
|
|
@@ -128,6 +130,33 @@ sözdizimi hatası `422 GlobalFunctionBodyInvalid` ile ve ayrıştırıcı mesaj
|
|
|
128
130
|
| FN-004 | `source:'workflow'` + `isAsync:true` | **block** — motor senkron, 422 `GlobalFunctionAsyncNotAllowed` |
|
|
129
131
|
| FN-004 | studio: `isAsync:true` ama `await` yok · `isAsync:false` ama `await` var | warn — ilki gereksiz async (inline `[object Promise]`), ikincisi **derlenmeyen gövde**: fonksiyon atlanır |
|
|
130
132
|
| FN-005 | Kaynak/kapsam tutarsız: workflow'a `applicationId`/`scope`/`pageId`; studio'ya `applicationId` ya da `scope` yok; `page` kapsamına `pageId` yok; `application` kapsamına `pageId` var | **block** — platform 422 |
|
|
133
|
+
| FN-006 | 5+ dolu satırlı gövdede hiç yorum yok | warn — baş yorumu ekleyin (§Gövdede yorum) |
|
|
134
|
+
|
|
135
|
+
### Gövdede yorum (FN-006)
|
|
136
|
+
|
|
137
|
+
Gövdeyi okuyan bir sonraki kişi (insan ya da ajan) fonksiyonu Studio'nun Functions drawer'ında
|
|
138
|
+
açar; yorumlar orada **yeşil italik** çizilir ve koddan ayrı okunur. Bu yüzden her gövde yorum taşır:
|
|
139
|
+
|
|
140
|
+
- **Baş yorumu** — ilk satırlar: fonksiyon ne yapar, parametrelerin anlamı ve birimi, ne döner.
|
|
141
|
+
Tek satırlık `return` gövdesinde bile bir satır yeterlidir.
|
|
142
|
+
- **Adım yorumu** — yalnız açık olmayan adımların üstüne: iş kuralı, sınır durumu, neden bu yol.
|
|
143
|
+
Kodu tekrar eden yorum yazılmaz (`// toplamı döndür` + `return toplam` gürültüdür).
|
|
144
|
+
- `//` kullanın; `/* */` çok satırlı açıklama için serbest. Gövde kendi satırına sarıldığı için
|
|
145
|
+
son satırdaki `//` kapanış parantezini yutmaz.
|
|
146
|
+
|
|
147
|
+
```js
|
|
148
|
+
// KDV dahil sepet toplamı. satirlar: [{ fiyat, adet }], oran: 0.2 gibi ondalık.
|
|
149
|
+
// Boş sepette 0 döner; tutar kuruş hassasiyetinde yuvarlanır.
|
|
150
|
+
const araToplam = satirlar.reduce((toplam, satir) => toplam + satir.fiyat * satir.adet, 0)
|
|
151
|
+
|
|
152
|
+
// İndirim KDV'den ÖNCE uygulanır (muhasebe kuralı)
|
|
153
|
+
const indirimli = araToplam * (1 - (indirim ?? 0))
|
|
154
|
+
|
|
155
|
+
return Math.round(indirimli * (1 + oran) * 100) / 100
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
5 ya da daha fazla dolu satırlı ve hiç yorumu olmayan gövde **FN-006** uyarısı alır; kayıt yine
|
|
159
|
+
yapılır.
|
|
131
160
|
|
|
132
161
|
Studio kaydı sonrası bağlı sayfaların çalışma paketi `Pages.Updated` ile yeniden kurulur (B-11):
|
|
133
162
|
`/dev` yolu yeni gövdeyi hemen, `/view` yolu önbellek tazelenince gösterir. Workflow kaydı önbellek
|
|
@@ -244,7 +273,7 @@ toplamHesapla(row.fiyat, row.adet) > 1000
|
|
|
244
273
|
serbesttir — sunucuda o ad alanı yok.
|
|
245
274
|
- **Önekli hata:** gövde hatası `Functions.<ad>: <mesaj>` (studio) ya da `<ad>: <mesaj>` (workflow)
|
|
246
275
|
olarak gelir; `Steps.<adım>.error.message` ya da `runtimeErrors`'ta bu öneki ara.
|
|
247
|
-
- **Mobil
|
|
276
|
+
- **Mobil yok** (K11): `apps/mobile` runtime'ı `Functions`'ı boş nesne kurar.
|
|
248
277
|
- **Önbellek:** `/view` yolu eski gövdeyi birkaç saniye gösterebilir; `/dev` yolu anlıktır.
|
|
249
278
|
- **Parametre konumsal:** `parameters` sırasını değiştirmek mevcut çağrıların anlamını değiştirir;
|
|
250
279
|
`function` adımları ada göre eşlendiği için etkilenmez.
|
|
@@ -153,6 +153,21 @@ koda baktığını gizlemek yerine raporlamak.
|
|
|
153
153
|
(GW-005). Bir argüman kabul edildiyse gateway onu okuyor demektir.
|
|
154
154
|
|
|
155
155
|
|
|
156
|
+
## Aynı çağrıyı iki kez yapmak kopya açmaz: `ifExists`
|
|
157
|
+
|
|
158
|
+
Platform aynı adla ikinci kaydı hata vermeden `"<ad> (1)"` olarak açar. `flow_create`,
|
|
159
|
+
`microservice_create`, `workflow_create` ve `page_create` bu yüzden yazmadan önce aynı ad ya da
|
|
160
|
+
slug'ı (harf duyarsız) arar:
|
|
161
|
+
|
|
162
|
+
| `ifExists` | Kayıt zaten varsa |
|
|
163
|
+
|---|---|
|
|
164
|
+
| `error` (varsayılan) | Yazmaz; hata mevcut id'yi ve varsa `(n)` sayaçlı kopyaları söyler |
|
|
165
|
+
| `update` | Mevcut kaydı günceller, yanıtta `outcome: 'updated'`. `page_create`'te yalnız verilen alanlar yazılır, şema korunur. `workflow_create`'te mevcut kaydın `type`'ı farklıysa (middleware ↔ automation) yazmaz — tür değişirdi |
|
|
166
|
+
| `duplicate` | Platform davranışı: ikinci kayıt açılır. Platformun eklediği sayaç (ad `' (1)'`, slug `-1`; akışta ikisi de `' (1)'`) kayıp değildir, yanıtta `renamedByPlatform`'dadır |
|
|
167
|
+
|
|
168
|
+
Betiği yeniden koşturacaksan `ifExists: 'update'` ver; kopyaları elle ayıklayan yardımcı kod
|
|
169
|
+
yazma. Mevcut liste okunamazsa çağrı yazmadan düşer — tekrar dene.
|
|
170
|
+
|
|
156
171
|
## Yazdıktan sonra `silentlyDropped` alanına bak
|
|
157
172
|
|
|
158
173
|
Platformun bir alanı **hatasız yutması** bu depodaki en pahalı hata sınıfı: çağrı 200 döner,
|
|
@@ -169,6 +184,26 @@ Gateway yazma tool'larında gönderdiğin gövdeyi kaydedilenle karşılaştır
|
|
|
169
184
|
]
|
|
170
185
|
```
|
|
171
186
|
|
|
187
|
+
Yazma yanıtında bu liste ve `gatewayNormalized` **kısaltılmış** gelir: aynı yolun düşmeleri tek
|
|
188
|
+
satırda sayıyla toplanır (`selectFields[*].path · kaydedilmedi: 25 öğe — ör. …`). Kayıp sayısı
|
|
189
|
+
`droppedCount`'tadır; sayım 1000 farkta durursa `droppedCountCapped: true` gelir ve sayı "en az"
|
|
190
|
+
demektir. Liste dışı bilgiler (20 satır kesmesi, sıra tabanlı karşılaştırma uyarısı)
|
|
191
|
+
`silentlyDroppedNotes`'tadır ve sayıya girmez. Tam liste gerekiyorsa yazmayı tekrarlama — kaydı `*_get` ile oku,
|
|
192
|
+
sonraki yazmalarda `diagnostics: 'full'` ver. `microservice_*` ve `workflow_*` create/update
|
|
193
|
+
yanıtında `data` yalnız kimlik alanlarını taşır; gövdenin tamamı için `view: 'full'`.
|
|
194
|
+
|
|
195
|
+
**Betikten toplu yazarken yanıtı olduğu gibi basma.** Otuz yazmanın tam yanıtı çıktı sınırını aşar
|
|
196
|
+
ve dosyaya düşer. Her çağrı için tek satır bas; ayrıntıyı yalnız sorunlu çağrıda bas:
|
|
197
|
+
|
|
198
|
+
```js
|
|
199
|
+
const r = JSON.parse(result.content[0].text)
|
|
200
|
+
if (result.isError || r.droppedCount > 0) console.log(JSON.stringify({ slug, error: result.isError ? r : undefined, droppedCount: r.droppedCount, silentlyDropped: r.silentlyDropped }))
|
|
201
|
+
else console.log(JSON.stringify({ slug, id: r.data?.id, outcome: r.outcome }))
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
Yalnız `isError`'a bakma: platform alanı hatasız yutunca çağrı başarılıdır ve kayıp yalnız
|
|
205
|
+
`droppedCount`'ta görünür.
|
|
206
|
+
|
|
172
207
|
Diff dizi öğelerini indeksle değil KİMLİKLE eşler (`stepId`, `elementId`, `name`, `id`), çünkü
|
|
173
208
|
platform diziyi kendi sırasında geri döndürür. Diff üretilemeyen tek durum, yanıtın kayıt değil
|
|
174
209
|
kabul gövdesi olmasıdır; o zaman alan hiç görünmez ve doğrulamayı `*_get` ile kendin yaparsın.
|
|
@@ -4,10 +4,6 @@ Sayfaların paylaştığı kabuk: yan menü, üst şerit, kullanıcı menüsü v
|
|
|
4
4
|
gireceği delik. Şema gövdesi ve koordinat modeli için `platform_guide('page')` (Kural 1,
|
|
5
5
|
1d, 1f); komponent sözleşmeleri için `platform_guide('component')`.
|
|
6
6
|
|
|
7
|
-
> **Mobil uygulamada** kabuk `app-bar` + tek `slot`'tur; sekmeler uygulama ayarı
|
|
8
|
-
> (`properties.mobile.tabs`), çekmece `drawer` widget'ıdır. Web kabuk bileşenleri (`navigations`,
|
|
9
|
-
> `sidebar`, `header`) `MOB-004` alır — `platform_guide('mobile')` §Gezinme.
|
|
10
|
-
|
|
11
7
|
## Karar kuralları
|
|
12
8
|
|
|
13
9
|
**1. Layout, tek `slot` içeren SIRADAN bir komponent ağacıdır.** Ayrı bir dil yoktur:
|
|
@@ -65,7 +61,7 @@ yapılan eski numaraya artık gerek yok.
|
|
|
65
61
|
NEREDE durduğunu değil, BİÇİMİNİ söyler (dikey kolon / yatay şerit / dolgu yüzeyi). Hem
|
|
66
62
|
runtime hem Studio tuvali bölgeleri 12 kolonluk ızgarayla yerleştirir.
|
|
67
63
|
|
|
68
|
-
**7.
|
|
64
|
+
**7. Mobilde flex YIĞILMAZ.** Runtime'ın mobil kuralı yalnız ızgara özelliklerini ezer.
|
|
69
65
|
Telefonda alt alta düşmesi gereken satırı `layout:'grid'` ile kur; `layout:'flex'` +
|
|
70
66
|
`direction:'row'` telefonda da yan yana kalır (375px'te ölçüldü: ızgara çocukları 375/375px
|
|
71
67
|
alt alta, flex çocukları 49/310px yan yana). Not: `wrap` varsayılanı artık açık (2026-09-01) —
|
|
@@ -144,8 +140,8 @@ bağlantı nesnesi ya da tam yol).
|
|
|
144
140
|
"direction": { "type": "static", "data": "row" } },
|
|
145
141
|
"blocks": [{ "breakpoint": 1024, "size": { "w": 12, "h": 80 }, "position": { "x": 0, "y": 0 } }] }
|
|
146
142
|
|
|
147
|
-
// KÖTÜ — telefonda 49px'lik yan menü: row flex
|
|
148
|
-
//
|
|
143
|
+
// KÖTÜ — telefonda 49px'lik yan menü: row flex mobilde yığılmaz (Kural 7)
|
|
144
|
+
// Mobilde alt alta düşmesi gerekiyorsa kökü layout:'grid' kur — bedeli, fill'in
|
|
149
145
|
// flex anlamının devreye girmemesi.
|
|
150
146
|
```
|
|
151
147
|
|
|
@@ -183,9 +179,9 @@ bunlardan biri olabilir:
|
|
|
183
179
|
|---|---|
|
|
184
180
|
| ~~`heightType` yalnız `auto` \| `fixed`~~ | **28 Ağustos'ta kapandı:** üçüncü değer `fill` (Kural 5b) |
|
|
185
181
|
| ~~Kök kap viewport'a kilitlenmez~~ | **28 Ağustos'ta kapandı:** `fill` verilmiş kök 100vh (Kural 5b) |
|
|
186
|
-
| Flex
|
|
187
|
-
| Kırılıma duyarlı prop yok | `direction`/`gap` masaüstü ve
|
|
188
|
-
|
|
|
182
|
+
| Flex mobilde yığılmaz | Kural 7 — kabuk bugün bir MASAÜSTÜ kabuğudur |
|
|
183
|
+
| Kırılıma duyarlı prop yok | `direction`/`gap` masaüstü ve mobilde AYNI; yalnız `blocks` ve `appearance.hideOn` kırılım bilir |
|
|
184
|
+
| Mobil çekmece (overlay) yok | Telefonda içeriğin üstüne binen panel kurulamaz |
|
|
189
185
|
|
|
190
186
|
---
|
|
191
187
|
|
|
@@ -193,7 +189,7 @@ bunlardan biri olabilir:
|
|
|
193
189
|
(`injectPageSchemaIntoSlots`, `getResolvedLayoutViews`), `apps/studio/src/features/page/api/page.mappers.ts`
|
|
194
190
|
(`mapPageLayoutToPreview`), `packages/ng-apps-ui/src/shared/render/render.functions.ts`
|
|
195
191
|
(`resolveNodeBoxStyle` — slot'un `y / auto` satırı, flex çocuk genişliği),
|
|
196
|
-
`packages/ng-apps-ui/src/shared/render/RenderComponent.svelte` (
|
|
192
|
+
`packages/ng-apps-ui/src/shared/render/RenderComponent.svelte` (mobil media kuralı),
|
|
197
193
|
`packages/ng-apps-screen-builder/src/flex-layout.ts` (`getFlexChildStyle` — `widthType`
|
|
198
194
|
fill/fixed/auto dalları, `normalizeCssSize` kabul kuralı; tuval ve runtime aynı fonksiyonu okur),
|
|
199
195
|
`apps/studio/src/features/page/components/appearance/appearance-size.functions.ts` (panelin
|