@appaflytech/zeyrek-ai-mcp 0.0.6 → 0.0.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +2 -2
- package/.env.production.example +8 -0
- package/README.md +9 -8
- package/dist/config.js +18 -3
- package/dist/knowledge/backendConstraints.js +85 -0
- package/dist/knowledge/componentCatalog.js +357 -54
- package/dist/knowledge/componentEvents.js +120 -25
- package/dist/knowledge/functionGuards.js +134 -0
- package/dist/knowledge/normalize.js +53 -2
- package/dist/knowledge/outlines.js +31 -0
- package/dist/knowledge/policy.js +14 -5
- package/dist/knowledge/rules.js +343 -1
- package/dist/platform/csv.js +123 -0
- package/dist/platform/errors.js +62 -3
- package/dist/platform/http.js +1 -0
- package/dist/platform/responses.js +16 -4
- package/dist/platform/screenshot.js +18 -1
- package/dist/resources/examples/flow-call-function.json +66 -0
- package/dist/resources/examples/function-total.json +70 -0
- package/dist/resources/examples/microfunction-node-sum.json +56 -0
- package/dist/resources/examples/page-dynamic-form.json +1 -1
- package/dist/resources/examples/page-sign-in.json +1 -1
- package/dist/resources/guides/component.md +13 -1
- package/dist/resources/guides/entity.md +321 -17
- package/dist/resources/guides/expressions.md +3 -0
- package/dist/resources/guides/functions.md +262 -0
- package/dist/resources/guides/getting-started.md +5 -0
- package/dist/resources/guides/merge.md +231 -0
- package/dist/resources/guides/microfunction.md +166 -0
- package/dist/resources/guides/microservice.md +61 -2
- package/dist/resources/guides/modify-existing.md +23 -0
- package/dist/resources/guides/page.md +20 -4
- package/dist/resources/guides/translation.md +24 -0
- package/dist/resources/guides/workflow.md +1 -1
- package/dist/resources/schema-fingerprint.json +4 -4
- package/dist/resources/schemas/AnalyzeImportQuery.json +51 -0
- package/dist/resources/schemas/ApplyMergePlanCommand.json +19 -0
- package/dist/resources/schemas/CreateBulkRowCommand.json +3 -0
- package/dist/resources/schemas/CreateEnvironmentCommand.json +31 -0
- package/dist/resources/schemas/CreateGlobalFunctionCommand.json +95 -0
- package/dist/resources/schemas/CreateImportPlanCommand.json +278 -0
- package/dist/resources/schemas/CreateMergePlanCommand.json +34 -0
- package/dist/resources/schemas/CreateMicrofunctionCommand.json +117 -0
- package/dist/resources/schemas/CreateMicrofunctionRuntimeCommand.json +51 -0
- package/dist/resources/schemas/CreateMicroserviceCommand.json +119 -1
- package/dist/resources/schemas/CreateWorkflowCommand.json +13 -2
- package/dist/resources/schemas/DataJobDto.json +267 -0
- package/dist/resources/schemas/ExportRowsCommand.json +172 -0
- package/dist/resources/schemas/Filter.json +3 -1
- package/dist/resources/schemas/GetRowsQuery.json +3 -1
- package/dist/resources/schemas/GlobalFunctionDto.json +89 -0
- package/dist/resources/schemas/GlobalFunctionUpdateResultDto.json +249 -0
- package/dist/resources/schemas/ImportAnalysisDto.json +401 -0
- package/dist/resources/schemas/ImportPlanDto.json +260 -0
- package/dist/resources/schemas/ImportSampleDto.json +90 -0
- package/dist/resources/schemas/InvokeMicrofunctionCommand.json +25 -0
- package/dist/resources/schemas/PreviewMicroserviceCommand.json +119 -1
- package/dist/resources/schemas/PreviewWorkflowCommand.json +13 -2
- package/dist/resources/schemas/ResolveMergeConflictsCommand.json +58 -0
- package/dist/resources/schemas/RevertMergePlanCommand.json +19 -0
- package/dist/resources/schemas/RunImportCommand.json +62 -0
- package/dist/resources/schemas/StepDto.json +13 -2
- package/dist/resources/schemas/UpdateBulkRowCommand.json +3 -0
- package/dist/resources/schemas/UpdateEntityCommand.json +3 -1
- package/dist/resources/schemas/UpdateGlobalFunctionCommand.json +66 -0
- package/dist/resources/schemas/UpdateMicrofunctionCommand.json +121 -0
- package/dist/resources/schemas/UpdateMicrofunctionRuntimeCommand.json +54 -0
- package/dist/resources/schemas/UpdateMicroserviceCommand.json +119 -1
- package/dist/resources/schemas/UpdateWorkflowCommand.json +13 -2
- package/dist/server.js +15 -3
- package/dist/tools/activities.js +40 -0
- package/dist/tools/automations.js +14 -5
- package/dist/tools/common.js +16 -0
- package/dist/tools/dependencies.js +1 -1
- package/dist/tools/environments.js +52 -0
- package/dist/tools/files.js +51 -50
- package/dist/tools/flows.js +95 -2
- package/dist/tools/functions.js +331 -0
- package/dist/tools/importSchemas.js +79 -0
- package/dist/tools/imports.js +241 -0
- package/dist/tools/merges.js +113 -0
- package/dist/tools/microfunctions.js +155 -0
- package/dist/tools/microservices.js +42 -3
- package/dist/tools/pages.js +62 -10
- package/dist/tools/platformInfo.js +4 -1
- package/dist/tools/revisions.js +94 -0
- package/dist/tools/rows.js +342 -22
- package/dist/tools/screenWrite.js +11 -2
- package/dist/tools/templates.js +32 -5
- package/dist/tools/workflows.js +24 -6
- package/package.json +3 -1
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { logError, logStep } from '../observability/trace.js';
|
|
2
2
|
import { PlatformApiError } from './errors.js';
|
|
3
|
+
import { PolicyBlockError } from '../knowledge/policy.js';
|
|
3
4
|
import { maskSecrets } from './secrets.js';
|
|
4
5
|
export function prune(value) {
|
|
5
6
|
if (Array.isArray(value))
|
|
@@ -126,12 +127,15 @@ function asStructured(payload) {
|
|
|
126
127
|
return payload;
|
|
127
128
|
return { data: payload };
|
|
128
129
|
}
|
|
129
|
-
export function toolResult(payload, config) {
|
|
130
|
+
export function toolResult(payload, config, expectFailure) {
|
|
130
131
|
const pruned = prune(payload);
|
|
131
132
|
const { payload: safe, masked, tokenRefs } = maskSecrets(pruned);
|
|
132
133
|
const runtimeErrors = runtimeErrorSummary(safe);
|
|
133
134
|
if (runtimeErrors.length > 0) {
|
|
134
|
-
|
|
135
|
+
// Kasıtlı negatif test (expectFailure:true) bir kusur değil bir KANIT: çağıran beklediğini
|
|
136
|
+
// söylediyse SONUÇ satırı 'başarılı' yazılır ve ölçüm aracının plansız hata oranını şişirmez
|
|
137
|
+
// (pages.ts'teki page_screenshot expectFailure deseniyle aynı; artık her handle()'lı tool'da geçerli).
|
|
138
|
+
logStep('akış hatası ✕', `${runtimeErrors.length} adım hata ile bitti — platform HTTP 200 döndürdüğü için çağrı başarılı görünüyordu${expectFailure === true ? ' (expectFailure:true — kasıtlı negatif test)' : ''}`, runtimeErrors);
|
|
135
139
|
}
|
|
136
140
|
if (masked.length > 0) {
|
|
137
141
|
logStep('maskeleme', `${masked.length} gizli alan yanıttan çıkarıldı — değer *** ile değiştirildi, yolu gatewayMasked'de`, masked);
|
|
@@ -144,6 +148,7 @@ export function toolResult(payload, config) {
|
|
|
144
148
|
const enriched = {
|
|
145
149
|
...structured,
|
|
146
150
|
...(runtimeErrors.length > 0 ? { runtimeErrors } : {}),
|
|
151
|
+
...(runtimeErrors.length > 0 && expectFailure === true ? { expectedFailure: true } : {}),
|
|
147
152
|
...(masked.length > 0 ? { gatewayMasked: masked } : {}),
|
|
148
153
|
...(Object.keys(tokenRefs).length > 0 ? { tokenRefs, tokenRefNote: "Maskelenen JWT'ler gateway içinde tutuldu; bu referansları page_screenshot / page_runtime_get 'asUser' alanına verebilirsiniz (15 dk geçerli). Jetonun kendisi yanıta ve günlüğe hiç girmez." } : {}),
|
|
149
154
|
...(truncated ? { truncated: true, truncationNote: 'Liste, yanıt boyutu sınırı nedeniyle kırpıldı; sayfalama ya da daha dar filtre kullanın.' } : {})
|
|
@@ -155,7 +160,8 @@ export function toolResult(payload, config) {
|
|
|
155
160
|
}
|
|
156
161
|
export function toolError(error) {
|
|
157
162
|
logError(error);
|
|
158
|
-
|
|
163
|
+
// Politika durdurması bilinçli bir karardır, "beklenmeyen" değil (33/S-5).
|
|
164
|
+
const message = error instanceof PlatformApiError || error instanceof PolicyBlockError
|
|
159
165
|
? error.message
|
|
160
166
|
: error instanceof Error
|
|
161
167
|
? `Beklenmeyen gateway hatası: ${error.message}`
|
|
@@ -168,7 +174,13 @@ export function toolError(error) {
|
|
|
168
174
|
export function handle(config, run) {
|
|
169
175
|
return async (args) => {
|
|
170
176
|
try {
|
|
171
|
-
|
|
177
|
+
// Tool'un kendi input şeması `expectFailure: z.boolean().optional()` taşıyorsa (ör.
|
|
178
|
+
// microservice_execute, automation_preview) burada otomatik okunur ve toolResult'a geçirilir
|
|
179
|
+
// — kasıtlı negatif testler (yanlış parola, yetkisiz erişim, iş kuralı ihlali) ölçüm
|
|
180
|
+
// aracının plansız hata oranını şişirmez. Şemasında bu alan olmayan tool'larda args'ta
|
|
181
|
+
// anahtar hiç bulunmaz, davranış değişmez.
|
|
182
|
+
const expectFailure = args?.expectFailure === true;
|
|
183
|
+
return toolResult(await run(args), config, expectFailure);
|
|
172
184
|
}
|
|
173
185
|
catch (error) {
|
|
174
186
|
return toolError(error);
|
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
import { PlatformApiError } from './errors.js';
|
|
2
2
|
import { INTERNAL_ROLE_SEPARATOR, knownInternalRoles } from '../knowledge/componentCatalog.js';
|
|
3
3
|
import { logStep } from '../observability/trace.js';
|
|
4
|
+
/**
|
|
5
|
+
* Ziyaretçinin tema tercihinin yaşadığı localStorage anahtarı — `theme-toggle` bunu yazar,
|
|
6
|
+
* `Theme` okur ve projenin kendi modunun önüne geçirir
|
|
7
|
+
* (`zeyrek-frontend/packages/ng-apps-ui/src/shared/theme/stores.ts`). Anahtar uygulama başına
|
|
8
|
+
* DEĞİL tarayıcı başına tektir.
|
|
9
|
+
*/
|
|
10
|
+
const VIEWER_MODE_STORAGE_KEY = 'zeyrek-viewer-theme-mode';
|
|
4
11
|
/**
|
|
5
12
|
* G-24 — gateway sayfayı kurabiliyor, okuyabiliyor ve çizdirebiliyordu ama üzerinde OLAY
|
|
6
13
|
* TETİKLEYEMİYORDU: "filtre değişince veri yenileniyor" iddiası yalnız şema okumasıyla
|
|
@@ -505,7 +512,7 @@ export class StudioScreenshotter {
|
|
|
505
512
|
});
|
|
506
513
|
context = await browser.newContext({ viewport: request.viewport, deviceScaleFactor: 1 });
|
|
507
514
|
}
|
|
508
|
-
logStep('tarayıcı →', `GET ${request.url} · viewport ${request.viewport.width}x${request.viewport.height}${request.fullPage ? ' · fullPage' : ''}${request.token ? '' : ' · gateway jetonu YOK'}${(request.cookies ?? []).length > 0 ? ` · caller çerezleri: ${(request.cookies ?? []).map(cookie => cookie.name).join(', ')}` : ''}${request.token || (request.cookies ?? []).length > 0 ? '' : ' · ANONİM (çerez YOK)'}`);
|
|
515
|
+
logStep('tarayıcı →', `GET ${request.url} · viewport ${request.viewport.width}x${request.viewport.height}${request.fullPage ? ' · fullPage' : ''}${request.token ? '' : ' · gateway jetonu YOK'}${(request.cookies ?? []).length > 0 ? ` · caller çerezleri: ${(request.cookies ?? []).map(cookie => cookie.name).join(', ')}` : ''}${request.token || (request.cookies ?? []).length > 0 ? '' : ' · ANONİM (çerez YOK)'}${request.colorScheme ? ` · tema ${request.colorScheme}` : ''}`);
|
|
509
516
|
try {
|
|
510
517
|
const cookies = new Map();
|
|
511
518
|
if (request.token)
|
|
@@ -515,6 +522,16 @@ export class StudioScreenshotter {
|
|
|
515
522
|
if (cookies.size > 0) {
|
|
516
523
|
await context.addCookies([...cookies].map(([name, value]) => ({ name, value, url: request.cookieUrl, httpOnly: false })));
|
|
517
524
|
}
|
|
525
|
+
// Tema tercihi çerezde değil localStorage'da yaşar ve tema store'u değerini modül İLK
|
|
526
|
+
// değerlendirildiğinde okur: sayfa açıldıktan sonra yazmak bir sonraki yüklemeye kadar
|
|
527
|
+
// etkisizdir. Bu yüzden tercih, sayfanın script'lerinden önce koşan bağlam script'iyle
|
|
528
|
+
// ekilir. Yeteneği olmayan bağlamda çağrı SESSİZCE açık temada kare almaz, hata verir.
|
|
529
|
+
if (request.colorScheme) {
|
|
530
|
+
if (!context.addInitScript) {
|
|
531
|
+
throw new PlatformApiError(`colorScheme='${request.colorScheme}' uygulanamadı: tarayıcı bağlamı addInitScript desteklemiyor. Tema tercihi sayfa açılmadan ekilmek zorunda; desteksiz bağlamda kare projenin varsayılan modunda çıkardı ve bu sessizce yanlış olurdu.`);
|
|
532
|
+
}
|
|
533
|
+
await context.addInitScript(`try { localStorage.setItem(${JSON.stringify(VIEWER_MODE_STORAGE_KEY)}, ${JSON.stringify(request.colorScheme)}) } catch (error) { /* localStorage kapalıysa proje varsayılanı çizilir */ }`);
|
|
534
|
+
}
|
|
518
535
|
const page = await context.newPage();
|
|
519
536
|
// Soğuk Vite derlemesinde sayfanın kendi veri isteği 15 sn'yi aşabiliyor; delayMs bu
|
|
520
537
|
// bütçeyi de uzatır (09-02 E-2). Yanıt hazırlık döngüsünü BLOKLAMAZ: runtime dışına
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
{
|
|
2
|
+
"_meta": {
|
|
3
|
+
"synthetic": true,
|
|
4
|
+
"note": "Bir butonun click olayına bağlanan akış: 'function' adımı kullanıcı fonksiyonunu çağırır (fiyat komponent prop'undan, adet sabit), sonraki setter sonucu Steps.<adımAdı>.data'dan okuyup metne yazar. Canlı kanıt: 2026-09-08/09 fonksiyon-demo sayfası (kabul senaryosu 2 → 30). Şekil backend StepManagementSchema + ng-apps-flow tasks/function/function.ts okunarak kuruldu.",
|
|
5
|
+
"harvestedFrom": "kod + canlı doğrulama: zeyrek-frontend packages/ng-apps-flow/src/tasks/function, zeyrek-backend Contexts/Studio/Flows/StepArgument (UiFunctionId/UiFunctionName) (2026-09-09)"
|
|
6
|
+
},
|
|
7
|
+
"_rules": {
|
|
8
|
+
"functionId": "args.functionId ZORUNLU ve uygulamada var olmalı — backend 422 UiFunctionNotFound, gateway FLOW-024 (block). Id'yi function_list'ten alın. Runtime fonksiyonu ÖNCE id ile çözer (K9); functionName gösterim ve yedektir.",
|
|
9
|
+
"parameters": "parameters[].name bildirimdeki parametre adıyla eşleşir; value şekli microservice adımıyla aynı (static · property · storage · state · variable · code · page · item). Eksik zorunlu parametre adımı 'Functions.<ad>: \"<param>\" is required' ile düşürür; tanınmayan ad sessizce düşer — FLOW-024 ikisini de uyarır.",
|
|
10
|
+
"output": "Çıktı Steps.<adım ADI>.data (stepId değil — machine.setStorage(task.name, …)); hata Steps.<ad>.error.message (nesne). Setter'ın storage okuması mapping.source: 'hesapla.data' ile yapılır (setter.ts:161 mapping.source doluysa value.ref okunmaz).",
|
|
11
|
+
"branchName": "Dal adı 'always' (motor anahtarı, FLOW-004). Son adımın dalı nextStepId: null ile biter; 'end' adımı koyma (FLOW-023).",
|
|
12
|
+
"binding": "Akışı olaya bağlamak ayrı adımdır: page_schema_patch set_event { target: 'btn_hesapla', event: 'click', eventValue: '<flow id>' }.",
|
|
13
|
+
"async": "isAsync=true fonksiyon bu adımdan güvenle çağrılır (Promise beklenir); {{ }} bağlamasında ise '[object Promise]' olur."
|
|
14
|
+
},
|
|
15
|
+
"flowCreateArgs": {
|
|
16
|
+
"host": "page",
|
|
17
|
+
"hostId": "<page id>",
|
|
18
|
+
"name": "btn_hesapla - click",
|
|
19
|
+
"slug": "btn-hesapla-click",
|
|
20
|
+
"steps": [
|
|
21
|
+
{
|
|
22
|
+
"stepId": "start",
|
|
23
|
+
"name": "start",
|
|
24
|
+
"type": "start",
|
|
25
|
+
"kind": "normal",
|
|
26
|
+
"args": {},
|
|
27
|
+
"branches": [ { "name": "always", "nextStepId": "hesapla" } ]
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
"stepId": "hesapla",
|
|
31
|
+
"name": "hesapla",
|
|
32
|
+
"type": "function",
|
|
33
|
+
"kind": "normal",
|
|
34
|
+
"args": {
|
|
35
|
+
"functionId": "<function_list id — toplamHesapla>",
|
|
36
|
+
"functionName": "toplamHesapla",
|
|
37
|
+
"parameters": [
|
|
38
|
+
{ "name": "fiyat", "value": { "type": "property", "ref": { "source": "component", "element": "num_fiyat", "property": "value" } } },
|
|
39
|
+
{ "name": "adet", "value": { "type": "static", "ref": 3 } }
|
|
40
|
+
]
|
|
41
|
+
},
|
|
42
|
+
"branches": [ { "name": "always", "nextStepId": "yaz" } ]
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
"stepId": "yaz",
|
|
46
|
+
"name": "yaz",
|
|
47
|
+
"type": "setter",
|
|
48
|
+
"kind": "normal",
|
|
49
|
+
"args": {
|
|
50
|
+
"iteratees": [
|
|
51
|
+
{
|
|
52
|
+
"type": "change",
|
|
53
|
+
"element": "txt_sonuc",
|
|
54
|
+
"property": "content",
|
|
55
|
+
"source": "component",
|
|
56
|
+
"mapping": { "source": "hesapla.data", "mappings": {} },
|
|
57
|
+
"value": { "type": "storage", "ref": "" }
|
|
58
|
+
}
|
|
59
|
+
]
|
|
60
|
+
},
|
|
61
|
+
"branches": [ { "name": "always", "nextStepId": null } ]
|
|
62
|
+
}
|
|
63
|
+
]
|
|
64
|
+
},
|
|
65
|
+
"bindEvent": { "op": "set_event", "target": "btn_hesapla", "event": "click", "eventValue": "<flow id>" }
|
|
66
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
{
|
|
2
|
+
"_meta": {
|
|
3
|
+
"synthetic": true,
|
|
4
|
+
"note": "toplamHesapla(fiyat, adet = 1) = fiyat * adet — aynı gövde İKİ KAYNAKTA: source:'studio' (uygulama kapsamlı, tarayıcı: {{ }} bağlaması ve UI flow 'code'/'function' adımı) ve source:'workflow' (sunucu: workflow/middleware/automation ifadesinde çıplak ad). Kod ve parametre biçimi aynıdır, fark nerede çalıştığıdır. Canlı kanıt: 2026-09-10 fonksiyon-demo sayfası ({{ Functions.toplamHesapla(10, 3) }} → 30) ve workflow_preview code adımı (return toplamHesapla(10, 3) → 30).",
|
|
5
|
+
"harvestedFrom": "kod + canlı doğrulama: zeyrek-backend Contexts/Studio/GlobalFunctions, zeyrek-frontend packages/ng-apps-flow/src/functions (2026-09-10)"
|
|
6
|
+
},
|
|
7
|
+
"_rules": {
|
|
8
|
+
"body": "Gövde DÜZ deyimlerdir, iki kaynakta aynı: 'return fiyat * adet;'. function (…) { } sarmalı ya da arrow yazılmaz. return serbesttir; dönüşsüz gövde undefined döner. Platform kaydetmeden sözdizimini ayrıştırır (422 GlobalFunctionBodyInvalid).",
|
|
9
|
+
"parameters": "Bildirim SIRALIDIR ve çağrı KONUMSALDIR (K5): toplamHesapla(10, 3) → fiyat=10, adet=3. defaultValue argüman undefined kalınca uygulanır; required kontrolü ondan SONRA çalışır ve 'toplamHesapla: \"fiyat\" is required' (studio'da 'Functions.toplamHesapla: …') fırlatır. type yalnız Studio imzası içindir, çalışma zamanı tip denetlemez.",
|
|
10
|
+
"scope": "Yalnız source:'studio'. application: her sayfada (tarayıcı). page: yalnız o sayfanın tarayıcı bağlamında, pageId zorunlu. Ad uygulama içinde kapsamlar arası benzersizdir (K3); workflow adı tenant içinde benzersizdir. Aynı ad iki kaynakta yan yana olabilir.",
|
|
11
|
+
"isAsync": "Yalnız studio'da anlamlı; false bırakın: true olsaydı {{ }} bağlaması '[object Promise]' çizerdi. await gerekmeyen gövdede isAsync:true FN-004 uyarısı alır. workflow kaynağında true FN-004 block (motor senkron).",
|
|
12
|
+
"naming": "Ad JS tanımlayıcısı; ayrılmış sözcük olamaz. studio'da 'Functions' yasak (FN-001, block); workflow'da motor global adları (system, input, Now, crypto…) yasak (FN-001, block). Uygulama fonksiyonunda sayfaya özgü ad (komponent adı, state.page), workflow fonksiyonunda adım adı kullanmayın — çağrıldığı yerde yoksa 'X is not defined'.",
|
|
13
|
+
"source": "workflow: SUNUCU, tenant geneli, applicationId/scope/pageId VERİLMEZ, isAsync olamaz, çıplak adla çağrılır. studio: TARAYICI, applicationId + scope zorunlu (application: her sayfada; page: yalnız o sayfa, pageId zorunlu), Functions.<ad>() ile çağrılır. Kaynak sonradan değişmez; sunucu ifadesinde Functions.<ad>( → WF-030 block."
|
|
14
|
+
},
|
|
15
|
+
"functionCreateArgs": {
|
|
16
|
+
"source": "studio",
|
|
17
|
+
"applicationId": "<application id>",
|
|
18
|
+
"scope": "application",
|
|
19
|
+
"name": "toplamHesapla",
|
|
20
|
+
"description": "fiyat × adet; adet verilmezse 1 sayılır",
|
|
21
|
+
"isAsync": false,
|
|
22
|
+
"parameters": [
|
|
23
|
+
{
|
|
24
|
+
"name": "fiyat",
|
|
25
|
+
"type": "number",
|
|
26
|
+
"required": true,
|
|
27
|
+
"description": "Birim fiyat"
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
"name": "adet",
|
|
31
|
+
"type": "number",
|
|
32
|
+
"defaultValue": 1,
|
|
33
|
+
"description": "Adet; verilmezse 1"
|
|
34
|
+
}
|
|
35
|
+
],
|
|
36
|
+
"body": "return fiyat * adet;"
|
|
37
|
+
},
|
|
38
|
+
"workflowFunctionCreateArgs": {
|
|
39
|
+
"source": "workflow",
|
|
40
|
+
"name": "toplamHesapla",
|
|
41
|
+
"description": "fiyat × adet; adet verilmezse 1 sayılır",
|
|
42
|
+
"parameters": [
|
|
43
|
+
{
|
|
44
|
+
"name": "fiyat",
|
|
45
|
+
"type": "number",
|
|
46
|
+
"required": true,
|
|
47
|
+
"description": "Birim fiyat"
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
"name": "adet",
|
|
51
|
+
"type": "number",
|
|
52
|
+
"defaultValue": 1,
|
|
53
|
+
"description": "Adet; verilmezse 1"
|
|
54
|
+
}
|
|
55
|
+
],
|
|
56
|
+
"body": "return fiyat * adet;"
|
|
57
|
+
},
|
|
58
|
+
"usage": {
|
|
59
|
+
"propBinding": {
|
|
60
|
+
"op": "set_binding",
|
|
61
|
+
"target": "txt_toplam",
|
|
62
|
+
"prop": "content",
|
|
63
|
+
"expression": "{{ Functions.toplamHesapla(10, 3) }}"
|
|
64
|
+
},
|
|
65
|
+
"uiFlowCodeStep": "const toplam = Functions.toplamHesapla(num_fiyat.value, num_adet.value);\nstate.page.sonuc = toplam;",
|
|
66
|
+
"uiFlowFunctionStep": "ngapps://examples/flow-call-function",
|
|
67
|
+
"workflowCodeStep": "return { toplam: toplamHesapla(10, 3) };",
|
|
68
|
+
"automationCondition": "toplamHesapla(row.fiyat, row.adet) > 1000"
|
|
69
|
+
}
|
|
70
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
{
|
|
2
|
+
"_meta": {
|
|
3
|
+
"synthetic": false,
|
|
4
|
+
"note": "test-project/development ortamında gerçek uçta doğrulandı (2026-09-11): publish:true ile kayıt 'publishing' döndü ve 'published' oldu (publishedReference zf-test-project-development-...), microfunction_invoke {a:1,b:2} → ok:true output {total:3, environment:'development'}, aynı akışa bağlı microservice_execute → 200 {total:3}. Kurallar: inline kaynakta entrypoint GÖNDERİLMEZ (sabit function.handler); timeoutSeconds ve memoryMb non-nullable int'tir, eksik gönderilirse platform 422 döner; adım rule.microfunctionId YALNIZ published kaydı gösterebilir (WF-MF-001)."
|
|
5
|
+
},
|
|
6
|
+
"microfunction": {
|
|
7
|
+
"name": "toplam",
|
|
8
|
+
"description": "İki sayıyı toplar ve hangi ortamda koştuğunu döner.",
|
|
9
|
+
"language": "node",
|
|
10
|
+
"provider": "built-in",
|
|
11
|
+
"sourceKind": "inline",
|
|
12
|
+
"code": "exports.handler = async (args, context) => {\n return { total: (args.a ?? 0) + (args.b ?? 0), environment: context.environment }\n}\n",
|
|
13
|
+
"parameterDtoId": "<girdi DTO id: { a: number, b: number }>",
|
|
14
|
+
"parameterBehavior": "object",
|
|
15
|
+
"returnDtoId": "<çıktı DTO id: { total: number, environment: string }>",
|
|
16
|
+
"returnBehavior": "object",
|
|
17
|
+
"timeoutSeconds": 30,
|
|
18
|
+
"memoryMb": 256,
|
|
19
|
+
"publish": true
|
|
20
|
+
},
|
|
21
|
+
"workflowStep": {
|
|
22
|
+
"id": "<GUID-3>",
|
|
23
|
+
"name": "toplam",
|
|
24
|
+
"type": "microfunction",
|
|
25
|
+
"description": "Girdideki iki sayıyı microfunction'da toplar.",
|
|
26
|
+
"rule": {
|
|
27
|
+
"microfunctionId": "<yukarıdaki kaydın id'si; status published olmalı>",
|
|
28
|
+
"expression": "({ a: input.a, b: input.b })"
|
|
29
|
+
},
|
|
30
|
+
"branches": [
|
|
31
|
+
{
|
|
32
|
+
"id": "<GUID-4>",
|
|
33
|
+
"name": "success",
|
|
34
|
+
"type": "success",
|
|
35
|
+
"nextStepId": "<response adımının id'si>"
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
"id": "<GUID-5>",
|
|
39
|
+
"name": "fail",
|
|
40
|
+
"type": "fail",
|
|
41
|
+
"nextStepId": null
|
|
42
|
+
}
|
|
43
|
+
]
|
|
44
|
+
},
|
|
45
|
+
"responseStep": {
|
|
46
|
+
"id": "<GUID-6>",
|
|
47
|
+
"name": "Respond",
|
|
48
|
+
"type": "response",
|
|
49
|
+
"description": "Toplamı çağırana döner.",
|
|
50
|
+
"rule": {
|
|
51
|
+
"dataTransferObjectId": "<çıktı DTO id>",
|
|
52
|
+
"expression": "({ total: Steps.toplam.data.total })"
|
|
53
|
+
},
|
|
54
|
+
"branches": []
|
|
55
|
+
}
|
|
56
|
+
}
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
"page_container (container, isWrapper:true — tema değişkenleri ve ortalanmış sütun BURADA)",
|
|
26
26
|
"├─ t_title (text, h1)",
|
|
27
27
|
"├─ t_desc (text, p)",
|
|
28
|
-
"├─ q_form (form, isWrapper:false,
|
|
28
|
+
"├─ q_form (form, isWrapper:false, style: border none + padding 0)",
|
|
29
29
|
"│ └─ q_list (repeater, isWrapper:false, rows:[], autoRowSize:true, verticalSpacing:14)",
|
|
30
30
|
"│ └─ q_card (container, isWrapper:false) ← ŞABLONUN KÖKÜ",
|
|
31
31
|
"│ ├─ q_head (text) y:0 content: {{ item.no + '. ' + item.text + (item.required ? ' *' : '') }}",
|
|
@@ -64,7 +64,7 @@
|
|
|
64
64
|
"parent": "auth_layout",
|
|
65
65
|
"component": {
|
|
66
66
|
"elementId": "sign_in_form", "kind": "component", "type": "form", "name": "sign_in_form",
|
|
67
|
-
"props": { "title": { "type": "static", "data": "Giriş" }, "
|
|
67
|
+
"props": { "title": { "type": "static", "data": "Giriş" }, "style": { "type": "static", "data": { "border": { "type": "static", "data": "none" } } } },
|
|
68
68
|
"states": { "isSticky": false, "isDroppable": true, "isScrollable": false, "isWrapper": true },
|
|
69
69
|
"blocks": [{ "breakpoint": 1024, "size": { "h": 24, "w": 12 }, "position": { "x": 0, "y": 4 } }],
|
|
70
70
|
"children": [], "events": [], "validations": [], "stateBindings": []
|
|
@@ -96,7 +96,7 @@ Her şema düğümünde iki sözlük vardır; ikisi de ortak render katmanında
|
|
|
96
96
|
|---|---|
|
|
97
97
|
| `isDroppable` | İçine komponent alır (container/form/repeater true) |
|
|
98
98
|
| `isScrollable` | İçeriği kendi alanında kaydırır (sabit yükseklikte `overflow-y: auto`) |
|
|
99
|
-
| `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`
|
|
99
|
+
| `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 |
|
|
100
100
|
| `isSticky` | Kaydırmada yerinde kalır (header/sidebar; z-index'i otomatik) |
|
|
101
101
|
| `isFloating` | Düğümü akıştan çıkarıp yüzdürür (katman davranışı; nadir) |
|
|
102
102
|
| `isQuickStack` | Studio'nun Quick Stack iskelet hücresi işareti — editörde kesikli çerçeve çizer, **runtime'da hiçbir etkisi yoktur**. MCP'den iskelet kurarken yazmak serbesttir ama gerekmez |
|
|
@@ -163,6 +163,18 @@ düz URL string'i değil **dosya nesnesi** ister: `{ absolute_path, display_name
|
|
|
163
163
|
`{ absolute_path: '<url>', display_name: '<ad>' }` biçimine çevir. Komponentin kendi sözleşmesi
|
|
164
164
|
için `component_get` `propNotes`.
|
|
165
165
|
|
|
166
|
+
## Tasarımcı toolbar'ı: `designer.toolbar` (studio'ya özel, kataloğa girmez)
|
|
167
|
+
|
|
168
|
+
Studio kanvasında seçili komponentin yanında çıkan bant, komponentin **kendi 2–4 prop'unu**
|
|
169
|
+
tek tıkla değiştirir (`textinput`: Disabled · Readonly · Size). Liste komponentin kendi
|
|
170
|
+
`schema.ts`'inde `designer.toolbar` olarak durur; kontrol prop tipinden türer (switch → toggle,
|
|
171
|
+
≤ 4 seçenekli radio/dropdown/switcher → kısa metin grubu, fazlası açılır liste, number → sayı
|
|
172
|
+
alanı). Kural üretimde denetlenir: en fazla 4 ad, ad şemada var, tip izinli, `designer.hide`
|
|
173
|
+
ve `layout` grubundaki adlar giremez. **MCP tarafında karşılığı yoktur:** `designer` bloğu
|
|
174
|
+
`components.json`'a ve `component_get` yanıtına girmez; toolbar'daki her prop zaten sıradan
|
|
175
|
+
bir prop'tur, `set_prop` ile aynı şekilde yazılır. Bu bilgi yalnız "studio'da neden o
|
|
176
|
+
düğmeler var" sorusuna cevaptır.
|
|
177
|
+
|
|
166
178
|
## Komponent seçimi
|
|
167
179
|
|
|
168
180
|
"Hangi işe hangi komponent" tablosu ve `table` / `datatable` karşılaştırması:
|