@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,15 +1,13 @@
|
|
|
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';
|
|
5
5
|
import { assertScreenWriteGuards, screenCatalogNotes } from './screenWrite.js';
|
|
6
|
-
import { schemaOpDrops, withWriteDiff } from '../platform/writeDiff.js';
|
|
6
|
+
import { emptyWriteDiff, schemaOpDrops, withWriteDiff } from '../platform/writeDiff.js';
|
|
7
7
|
import { fetchComponentEventNames } from './components.js';
|
|
8
8
|
import { PlatformApiError } from '../platform/errors.js';
|
|
9
9
|
import { handle } from '../platform/responses.js';
|
|
10
10
|
import { slotWarning } from './layouts.js';
|
|
11
|
-
import { applicationTypeField, resolveProfile } from '../knowledge/platform/resolve.js';
|
|
12
|
-
import { boundFlowSteps, screenProfileWarnings } from './profileWrite.js';
|
|
13
11
|
import { z } from 'zod';
|
|
14
12
|
const HOSTS = {
|
|
15
13
|
layout: {
|
|
@@ -70,7 +68,10 @@ export function registerDesignSchemaPatchTool(server, platform, host) {
|
|
|
70
68
|
}
|
|
71
69
|
const nodes = (record[spec.tree] ?? []);
|
|
72
70
|
const events = host === 'layout' ? record.events : undefined;
|
|
73
|
-
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);
|
|
74
75
|
const typesByElementId = new Map();
|
|
75
76
|
collectTypes(nodes, typesByElementId);
|
|
76
77
|
collectTypes(opRecords.map(entry => entry.component).filter(Boolean), typesByElementId);
|
|
@@ -79,6 +80,7 @@ export function registerDesignSchemaPatchTool(server, platform, host) {
|
|
|
79
80
|
if (unknown.length > 0)
|
|
80
81
|
throw new PlatformApiError(`PAGE-008 — geçersiz olay adı (istek gönderilmedi): ${unknown.join(' · ')}`, 400);
|
|
81
82
|
}
|
|
83
|
+
stripGatewayEventFields(opRecords);
|
|
82
84
|
const deletedFlows = orphanedFlowPlan(nodes, events, [...opRecords, ...removeEventOps]);
|
|
83
85
|
if (deletedFlows.length > 0 && allowFlowDeletion !== true)
|
|
84
86
|
throw new PlatformApiError(orphanedFlowMessage(deletedFlows), 400);
|
|
@@ -99,18 +101,12 @@ export function registerDesignSchemaPatchTool(server, platform, host) {
|
|
|
99
101
|
const nested = simulatable ? nestedRepeaterNodes(simulated) : [];
|
|
100
102
|
if (nested.length > 0)
|
|
101
103
|
throw new PlatformApiError(nestedRepeaterMessage(nested), 400);
|
|
102
|
-
const resolved = await resolveProfile(platform, applicationId);
|
|
103
104
|
const reshaped = simulatable || opRecords.some(entry => entry.op === 'remove_component');
|
|
104
|
-
const profileWarnings = await screenProfileWarnings(platform, resolved, project, environment, applicationId, {
|
|
105
|
-
kind: spec.kind,
|
|
106
|
-
ops: [...opRecords, ...propTypeOps],
|
|
107
|
-
steps: await boundFlowSteps(platform, resolved, project, environment, host, applicationId, id, opRecords)
|
|
108
|
-
});
|
|
109
105
|
const layoutWarnings = reshaped
|
|
110
106
|
? [
|
|
111
|
-
...
|
|
107
|
+
...verticalGapWarnings(simulated, spec.codes.gap, touched),
|
|
112
108
|
...stringStyleWarning(stringStyleNodes(simulated)),
|
|
113
|
-
...
|
|
109
|
+
...inertFlexPropWarning(inertFlexPropNodes(simulated)),
|
|
114
110
|
...flexSizeWarnings(simulated, spec.codes.flexMismatch, spec.codes.flexInvalid),
|
|
115
111
|
...(host === 'layout' ? slotWarning(simulated) : [])
|
|
116
112
|
]
|
|
@@ -137,18 +133,17 @@ export function registerDesignSchemaPatchTool(server, platform, host) {
|
|
|
137
133
|
response.deletedFlows = deletedFlows;
|
|
138
134
|
if (flowWarnings.length > 0)
|
|
139
135
|
response.flowWarnings = flowWarnings;
|
|
140
|
-
if (layoutWarnings.length > 0
|
|
141
|
-
response.layoutWarnings =
|
|
142
|
-
Object.assign(response, applicationTypeField(resolved));
|
|
136
|
+
if (layoutWarnings.length > 0)
|
|
137
|
+
response.layoutWarnings = layoutWarnings;
|
|
143
138
|
const contentOps = opRecords.filter(entry => entry.op === 'add_component' || entry.op === 'replace_prop');
|
|
144
|
-
let opDrops =
|
|
139
|
+
let opDrops = emptyWriteDiff();
|
|
145
140
|
if (contentOps.length > 0) {
|
|
146
141
|
try {
|
|
147
142
|
const saved = await fetchHost(platform, host, project, environment, applicationId, id);
|
|
148
143
|
opDrops = schemaOpDrops(saved[spec.tree], contentOps);
|
|
149
144
|
}
|
|
150
145
|
catch {
|
|
151
|
-
opDrops =
|
|
146
|
+
opDrops = emptyWriteDiff();
|
|
152
147
|
}
|
|
153
148
|
}
|
|
154
149
|
return withWriteDiff(withNormalization(response, [
|
|
@@ -7,23 +7,6 @@ import { z } from 'zod';
|
|
|
7
7
|
import { evaluatePolicy, enforcePolicy } from '../knowledge/policy.js';
|
|
8
8
|
import { policyRules } from '../knowledge/rules.js';
|
|
9
9
|
import { EXTERNAL_COMPONENT_PROTOCOL_VERSION } from '../knowledge/backendConstraints.js';
|
|
10
|
-
import { resolveProfile } from '../knowledge/platform/resolve.js';
|
|
11
|
-
import { findingLine } from '../knowledge/platform/profile.js';
|
|
12
|
-
/**
|
|
13
|
-
* MOB-006 — harici bileşen protokolü v1 tarayıcıda koşan `.mjs` custom element'tir; mobil
|
|
14
|
-
* uygulamanın native çizeri onu yükleyemez (mobil protokol v2 Faz 3). Profil kapalı derse çağrı
|
|
15
|
-
* platforma GİTMEZ: yüklenen paket hiçbir ekranda çizilmeyecekti.
|
|
16
|
-
*/
|
|
17
|
-
async function assertExternalComponentsAllowed(platform, applicationId, tool) {
|
|
18
|
-
const resolved = await resolveProfile(platform, applicationId);
|
|
19
|
-
if (resolved.profile.externalComponents)
|
|
20
|
-
return;
|
|
21
|
-
throw new PlatformApiError(`${tool}: ${findingLine({
|
|
22
|
-
code: 'MOB-006',
|
|
23
|
-
severity: 'warn',
|
|
24
|
-
message: "harici bileşen mobil uygulamada desteklenmiyor — protokol v1 tarayıcıda koşan .mjs custom element'tir, native çizer onu yükleyemez (mobil protokol v2 Faz 3). Çağrı platforma gönderilmedi; mobil ekranı katalog bileşenleriyle kurun (component_list)."
|
|
25
|
-
})}`, 400);
|
|
26
|
-
}
|
|
27
10
|
/** Platformun köprü protokolü; scaffold ve publish bu sürüme göre üretir/doğrular (backend ExternalComponentRules.CurrentProtocolVersion) */
|
|
28
11
|
const PROTOCOL_VERSION = EXTERNAL_COMPONENT_PROTOCOL_VERSION;
|
|
29
12
|
/** Backend `ExternalComponentRules.MaxBundleSizeBytes` ile aynı sınır */
|
|
@@ -216,7 +199,6 @@ export function registerExternalComponentTools(server, platform) {
|
|
|
216
199
|
},
|
|
217
200
|
annotations: writes
|
|
218
201
|
}, handle(config, async ({ project, environment, applicationId, bundlePath, manifestPath, version }) => {
|
|
219
|
-
await assertExternalComponentsAllowed(platform, applicationId, 'external_component_publish');
|
|
220
202
|
const manifestFile = resolve(manifestPath);
|
|
221
203
|
const bundleFile = resolve(bundlePath);
|
|
222
204
|
let manifest;
|
|
@@ -280,13 +262,10 @@ export function registerExternalComponentTools(server, platform) {
|
|
|
280
262
|
acknowledgeWarnings: z.boolean().optional().describe('Doğrulama uyarılarını okuyup kabul ettiğinizi bildirir; uyarısı olan sürümlerde zorunlu')
|
|
281
263
|
},
|
|
282
264
|
annotations: updates
|
|
283
|
-
}, handle(config,
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
body: { version, acknowledgeWarnings: acknowledgeWarnings === true }
|
|
288
|
-
});
|
|
289
|
-
}));
|
|
265
|
+
}, handle(config, ({ project, environment, applicationId, slug, version, acknowledgeWarnings }) => platform.tenant('POST', project, `/components/${applicationId}/external/${slug}/activate`, {
|
|
266
|
+
environment,
|
|
267
|
+
body: { version, acknowledgeWarnings: acknowledgeWarnings === true }
|
|
268
|
+
})));
|
|
290
269
|
server.registerTool('external_component_versions', {
|
|
291
270
|
title: 'Paket sürümlerini listele',
|
|
292
271
|
description: 'Bir harici komponentin yayınlanmış paket sürümlerini, hangisinin aktif olduğunu ve her sürümün doğrulama raporunu listeler. Geri alınacak sürümü buradan seçin.',
|
package/dist/tools/flows.js
CHANGED
|
@@ -5,14 +5,14 @@ import { flowBindings, normalizeFlowSteps, targetlessSetterWarnings, unknownSett
|
|
|
5
5
|
import { flowBodyWarnings } from '../knowledge/coverage.js';
|
|
6
6
|
import { PlatformApiError } from '../platform/errors.js';
|
|
7
7
|
import { handle } from '../platform/responses.js';
|
|
8
|
-
import { writeDiff, withWriteDiff } from '../platform/writeDiff.js';
|
|
8
|
+
import { emptyWriteDiff, writeDiff, withWriteDiff } from '../platform/writeDiff.js';
|
|
9
9
|
import { resolvePatchOperations } from '../knowledge/outlines.js';
|
|
10
10
|
import { patchResult, baseVersionOf } from '../platform/patch.js';
|
|
11
11
|
import fastJsonPatch from 'fast-json-patch';
|
|
12
12
|
import { policyRules, flowExpressionSites } from '../knowledge/rules.js';
|
|
13
13
|
import { functionCallRefs } from '../knowledge/functionGuards.js';
|
|
14
|
+
import { existingMatch, ifExistsParam, platformRename, withRename } from '../platform/ifExists.js';
|
|
14
15
|
import { randomUUID } from 'node:crypto';
|
|
15
|
-
import { flowProfileWarnings } from './profileWrite.js';
|
|
16
16
|
import { z } from 'zod';
|
|
17
17
|
/**
|
|
18
18
|
* Akış yazması yanıtında gövdeyi geri döndürmüyor, bu yüzden diff için akışı bir kez okuruz.
|
|
@@ -20,14 +20,18 @@ import { z } from 'zod';
|
|
|
20
20
|
* `flow_create` 200 döndü, `flow_get` adımı gösterdi ama `args` içinde kodu taşıyan alan yoktu —
|
|
21
21
|
* teşhis ~10 dakika sürdü ve tek belirti filtrenin hiçbir şey yapmamasıydı. Okuma başarısız
|
|
22
22
|
* olursa yazma yine başarılıdır: diff bir teşhis eklentisidir, yazmanın sonucu değil.
|
|
23
|
+
*
|
|
24
|
+
* Okunan kayıt da döner: `ifExists:'duplicate'` yazmasında platform ada/slug'a ' (1)' ekler ve yanıtın
|
|
25
|
+
* gönderilen adı değil KAYDEDİLEN adı göstermesi gerekir (flow-upsert-ve-baglar S-10).
|
|
23
26
|
*/
|
|
24
|
-
async function flowWriteDrops(platform, host, project, environment, applicationId, hostId, flowId, sent) {
|
|
27
|
+
async function flowWriteDrops(platform, host, project, environment, applicationId, hostId, flowId, sent, allowRename = false) {
|
|
25
28
|
try {
|
|
26
29
|
const saved = unwrap(await platform.tenant('GET', project, flowPath(host, applicationId, hostId, flowId), { environment }));
|
|
27
|
-
|
|
30
|
+
const rename = allowRename ? platformRename(sent, saved, 'paren') : { sent };
|
|
31
|
+
return { ...writeDiff(rename.sent, saved), saved, ...(rename.renamed ? { renamed: rename.renamed } : {}) };
|
|
28
32
|
}
|
|
29
33
|
catch {
|
|
30
|
-
return
|
|
34
|
+
return emptyWriteDiff();
|
|
31
35
|
}
|
|
32
36
|
}
|
|
33
37
|
const stepsParam = z.array(z.record(z.unknown())).describe("Adım listesi (StepManagementSchema: { stepId, name, type, kind, args, branches }). DİKKAT: branches[].nextStepId bir GUID değil, hedef adımın stepId STRING'idir; dal ADI motorun anahtarıdır ve start/setter/code/function adımlarında 'always' olmak zorundadır. Tam şablon: ngapps://examples/flow-load-table (ya da flow_get çıktısı); adım tipleri ve setter/parameters şekli platform_guide('page') Kural 1c. Kullanıcı fonksiyonu çağırmak için 'function' adımı: { type:'function', args:{ functionId:'<function_list id>', functionName:'<ad>', parameters:[{ name:'<bildirimdeki ad>', value:{ type:'static'|'property'|'storage'|'state'|'code', ref } }] } } → çıktı Steps.<adımAdı>.data (ngapps://examples/flow-call-function, platform_guide('functions')).");
|
|
@@ -70,8 +74,7 @@ function flowListResult(flows, record) {
|
|
|
70
74
|
...(danglingBindings.length > 0 ? { danglingBindings } : {})
|
|
71
75
|
};
|
|
72
76
|
}
|
|
73
|
-
const
|
|
74
|
-
const upper = (value) => String(value ?? '').toUpperCase();
|
|
77
|
+
const flowIfExistsParam = ifExistsParam('akış', 'mevcut akışı bu gövdeyle günceller (id\'si ve olay bağları korunur)');
|
|
75
78
|
/**
|
|
76
79
|
* Barındırıcının akışları, önbelleksiz liste ucundan (`GET /pages|layouts/{id}/flows`). Layout ve
|
|
77
80
|
* sayfa tasarım kaydı önbellekten gelir ve zarfsız akış yazmasından sonra akışı hemen taşımaz;
|
|
@@ -104,36 +107,21 @@ async function listHostFlows(platform, host, project, environment, applicationId
|
|
|
104
107
|
* tam da zaman aşımında kopya üretirdi.
|
|
105
108
|
*/
|
|
106
109
|
async function existingFlowMatch(platform, host, project, environment, applicationId, hostId, hostRecord, name, slug, flowId, ifExists) {
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
const copies = flows.filter(flow => copyPattern.test(String(flow.name ?? '')));
|
|
123
|
-
const copyNote = copies.length > 0
|
|
124
|
-
? ` Barındırıcıda bu adın sayaçlı eşleri de var: ${copies.map(describe).join(' · ')} — bağı olup olmadığını flow_list (bindings) ile kontrol edip gerekirse silin.`
|
|
125
|
-
: '';
|
|
126
|
-
if (ifExists === 'error') {
|
|
127
|
-
const where = host === 'page' ? 'sayfada' : host === 'layout' ? "layout'ta" : "widget'ta";
|
|
128
|
-
throw new PlatformApiError(`FLOW-026: bu ${where} aynı ad ya da slug'da akış zaten var: ${matches.map(describe).join(' · ')}. İstek gönderilmedi — gönderilseydi platform ürün kuralı gereği ikinci bir akış açardı (çakışan ad ya da slug'a ' (1)' sayacı eklenerek; yalnız harf farkı varsa sayaçsız). Mevcut akışı güncellemek için aynı çağrıyı ifExists:'update' ile tekrarlayın; bilerek ikinci akış istiyorsanız ifExists:'duplicate'.${copyNote}`, 409);
|
|
129
|
-
}
|
|
130
|
-
if (matches.length > 1) {
|
|
131
|
-
throw new PlatformApiError(`FLOW-026: ad ve slug farklı akışlara çıkıyor (${matches.map(describe).join(' · ')}); hangisinin güncelleneceği belirsiz, istek gönderilmedi. flowId ile flow_update kullanın.${copyNote}`, 409);
|
|
132
|
-
}
|
|
133
|
-
if (flowId !== undefined && flowId.toLowerCase() !== String(match.id).toLowerCase()) {
|
|
134
|
-
throw new PlatformApiError(`FLOW-026: verilen flowId (${flowId}) aynı adlı mevcut akışla (${describe(match)}) çelişiyor; istek gönderilmedi. Mevcut akışı güncellemek için flowId'yi vermeyin ya da ${String(match.id)} verin.`, 409);
|
|
135
|
-
}
|
|
136
|
-
return match;
|
|
110
|
+
return await existingMatch({
|
|
111
|
+
code: 'FLOW-026',
|
|
112
|
+
noun: 'akış',
|
|
113
|
+
where: host === 'page' ? 'bu sayfada' : host === 'layout' ? "bu layout'ta" : "bu widget'ta",
|
|
114
|
+
name,
|
|
115
|
+
slug,
|
|
116
|
+
id: flowId,
|
|
117
|
+
idParam: 'flowId',
|
|
118
|
+
ifExists,
|
|
119
|
+
list: () => listHostFlows(platform, host, project, environment, applicationId, hostId, async () => hostRecord),
|
|
120
|
+
listTool: 'flow_list',
|
|
121
|
+
copyCheck: 'bağı olup olmadığını flow_list (bindings) ile kontrol edip gerekirse silin',
|
|
122
|
+
updateTool: 'flowId ile flow_update',
|
|
123
|
+
slugSuffix: 'paren'
|
|
124
|
+
});
|
|
137
125
|
}
|
|
138
126
|
async function requiredParameterWarnings(platform, project, environment, applicationId, steps) {
|
|
139
127
|
const warnings = [];
|
|
@@ -357,7 +345,7 @@ export function registerFlowTools(server, platform) {
|
|
|
357
345
|
steps: stepsParam,
|
|
358
346
|
errorSteps: stepsParam.optional(),
|
|
359
347
|
flowId: guid.optional().describe('Verilmezse platform üretir; şemaya önceden bağlamak için kendiniz üretebilirsiniz'),
|
|
360
|
-
ifExists:
|
|
348
|
+
ifExists: flowIfExistsParam,
|
|
361
349
|
policyOverride: policyOverrideParam
|
|
362
350
|
},
|
|
363
351
|
annotations: writes
|
|
@@ -393,17 +381,20 @@ export function registerFlowTools(server, platform) {
|
|
|
393
381
|
}
|
|
394
382
|
// Diff YALNIZ çağıranın yazdığı alanları kapsar; `id` ve barındırıcı bağı (`pageId` vb.)
|
|
395
383
|
// gövdeye gateway koyar, kullanıcının düzeltebileceği bir şey değildir.
|
|
396
|
-
const drops = await flowWriteDrops(platform, host, project, environment, applicationId, hostId, writtenFlowId, { name: flow.name, slug: flow.slug, steps: flow.steps, errorSteps: flow.errorSteps });
|
|
397
|
-
|
|
398
|
-
return attachFindings(withWriteDiff(withNormalization({
|
|
384
|
+
const drops = await flowWriteDrops(platform, host, project, environment, applicationId, hostId, writtenFlowId, { name: flow.name, slug: flow.slug, steps: flow.steps, errorSteps: flow.errorSteps }, ifExists === 'duplicate');
|
|
385
|
+
return attachFindings(withWriteDiff(withRename(withNormalization({
|
|
399
386
|
host,
|
|
400
387
|
hostId,
|
|
401
388
|
outcome: existing === undefined ? 'created' : 'updated',
|
|
402
|
-
flow: {
|
|
389
|
+
flow: {
|
|
390
|
+
id: writtenFlowId,
|
|
391
|
+
name: typeof drops.saved?.name === 'string' ? drops.saved.name : name,
|
|
392
|
+
slug: typeof drops.saved?.slug === 'string' ? drops.saved.slug : slug,
|
|
393
|
+
steps: steps.length
|
|
394
|
+
},
|
|
403
395
|
...(parameterWarnings.length > 0 ? { parameterWarnings } : {}),
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
}, normalized), drops), findings);
|
|
396
|
+
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."
|
|
397
|
+
}, normalized), drops.renamed), drops), findings);
|
|
407
398
|
}));
|
|
408
399
|
server.registerTool('flow_update', {
|
|
409
400
|
title: 'Akış güncelle',
|
|
@@ -455,9 +446,7 @@ export function registerFlowTools(server, platform) {
|
|
|
455
446
|
...(steps === undefined ? {} : { steps }),
|
|
456
447
|
...(errorSteps === undefined ? {} : { errorSteps })
|
|
457
448
|
});
|
|
458
|
-
|
|
459
|
-
return attachFindings(withWriteDiff(withNormalization({ host, hostId, flow: flowSummary(flow), ...(parameterWarnings.length > 0 ? { parameterWarnings } : {}),
|
|
460
|
-
...(platformWarnings.length > 0 ? { platformWarnings } : {}) }, normalized), drops), findings);
|
|
449
|
+
return attachFindings(withWriteDiff(withNormalization({ host, hostId, flow: flowSummary(flow), ...(parameterWarnings.length > 0 ? { parameterWarnings } : {}) }, normalized), drops), findings);
|
|
461
450
|
}));
|
|
462
451
|
server.registerTool('flow_patch', {
|
|
463
452
|
title: "Akışa patch uygula",
|
|
@@ -506,7 +495,6 @@ export function registerFlowTools(server, platform) {
|
|
|
506
495
|
});
|
|
507
496
|
const written = patchResult(saved, flow);
|
|
508
497
|
const drops = await flowWriteDrops(platform, host, project, environment, applicationId, hostId, flowId, { steps: flow.steps, errorSteps: flow.errorSteps });
|
|
509
|
-
const platformWarnings = await flowProfileWarnings(platform, applicationId, [...(flow.steps ?? []), ...(flow.errorSteps ?? [])]);
|
|
510
498
|
return attachFindings(withWriteDiff(withNormalization({
|
|
511
499
|
host,
|
|
512
500
|
hostId,
|
|
@@ -514,8 +502,7 @@ export function registerFlowTools(server, platform) {
|
|
|
514
502
|
newVersion: written.newVersion,
|
|
515
503
|
patchTargets: resolved.targets,
|
|
516
504
|
flow: flowSummary(written.data),
|
|
517
|
-
...(parameterWarnings.length > 0 ? { parameterWarnings } : {})
|
|
518
|
-
...(platformWarnings.length > 0 ? { platformWarnings } : {})
|
|
505
|
+
...(parameterWarnings.length > 0 ? { parameterWarnings } : {})
|
|
519
506
|
}, [...normalized, ...resolved.notes.map(note => `patch yolu seçiciden indekse çözüldü: ${note}`)]), drops), findings);
|
|
520
507
|
}));
|
|
521
508
|
server.registerTool('flow_delete', {
|
package/dist/tools/functions.js
CHANGED
|
@@ -16,7 +16,7 @@ import { z } from 'zod';
|
|
|
16
16
|
const sourceParam = z.enum(['workflow', 'studio']).describe("Nerede çalışır: 'workflow' → SUNUCU (workflow/middleware/automation ifadeleri, Jint), tenant geneli, çıplak adla çağrılır (toplamHesapla(1, 2)), applicationId/scope/pageId VERİLMEZ, isAsync olamaz. 'studio' → TARAYICI ({{ }} bağlaması, UI flow code/function adımı), uygulamaya ait, Functions.toplamHesapla(1, 2) ile çağrılır, applicationId + scope zorunlu. Kaynak sonradan DEĞİŞMEZ");
|
|
17
17
|
const scopeParam = z.enum(['application', 'page']).describe("Yalnız source:'studio'. application: uygulamanın her sayfasında görünür. page: yalnız o sayfanın tarayıcı bağlamında, pageId zorunlu (K2). Ad uygulama içinde kapsamlar arası benzersizdir (K3)");
|
|
18
18
|
const nameParam = z.string().min(1).describe("Fonksiyon adı; studio'da Functions.<ad>(…), workflow'da <ad>(…) ile çağrılır. JS tanımlayıcısı olmalı, ayrılmış sözcük olamaz; studio'da 'Functions', workflow'da motor global adları (system, input, Now…) yasaktır (FN-001, istek gitmeden). Aynı ad iki kaynakta yan yana yaşayabilir (ayrı ad alanları); aynı kaynak içinde tekrarı platform 422 ile reddeder");
|
|
19
|
-
const bodyParam = z.string().min(1).max(65536).describe("Fonksiyon GÖVDESİ, iki kaynakta aynı biçim: düz JavaScript deyimleri, 'return' içerebilir — 'function' sarmalı ya da arrow YAZILMAZ ('return fiyat * adet'). Gövde çağrıldığı yerin bağlamını kapanış olarak görür: tarayıcıda Page/state/Steps/komponent adları, sunucuda input/adım çıktıları/system.* (K7). Parametre adları gövdede doğrudan kullanılır. Platform kaydetmeden önce SÖZDİZİMİNİ denetler (422 GlobalFunctionBodyInvalid, ayrıştırıcı mesajıyla). En çok 64 KB");
|
|
19
|
+
const bodyParam = z.string().min(1).max(65536).describe("Fonksiyon GÖVDESİ, iki kaynakta aynı biçim: düz JavaScript deyimleri, 'return' içerebilir — 'function' sarmalı ya da arrow YAZILMAZ ('return fiyat * adet'). Gövde çağrıldığı yerin bağlamını kapanış olarak görür: tarayıcıda Page/state/Steps/komponent adları, sunucuda input/adım çıktıları/system.* (K7). Parametre adları gövdede doğrudan kullanılır. Gövde YORUMLU yazılır: başta ne yaptığını/parametreleri/dönüşü söyleyen // baş yorumu, açık olmayan adımlarda kısa satır yorumu; 5+ dolu satırlı yorumsuz gövde FN-006 uyarısı alır (Studio yorumu yeşil çizer). Platform kaydetmeden önce SÖZDİZİMİNİ denetler (422 GlobalFunctionBodyInvalid, ayrıştırıcı mesajıyla). En çok 64 KB");
|
|
20
20
|
const parameterParam = z.object({
|
|
21
21
|
name: z.string().min(1).describe('Parametre adı — JS tanımlayıcısı, fonksiyon içinde benzersiz; gövdede bu adla okunur'),
|
|
22
22
|
type: z.enum(['text', 'number', 'boolean', 'date', 'object', 'array', 'any']).optional()
|
|
@@ -64,7 +64,10 @@ export function describeSignatureChange(current, next) {
|
|
|
64
64
|
};
|
|
65
65
|
}
|
|
66
66
|
function signatureWarning(name, source, change, dependents) {
|
|
67
|
-
const callers = [
|
|
67
|
+
const callers = ['flows', 'workflows', 'bindings', 'functions']
|
|
68
|
+
.map(bucket => (dependents[bucket].length ? `${bucket}: ${dependents[bucket].join(', ')}` : ''))
|
|
69
|
+
.filter(Boolean)
|
|
70
|
+
.join('; ');
|
|
68
71
|
const what = [
|
|
69
72
|
change.name ? `ad '${change.name[0]}' → '${change.name[1]}'` : '',
|
|
70
73
|
change.parameters ? `parametreler [${change.parameters[0].join(', ')}] → [${change.parameters[1].join(', ')}]` : ''
|
|
@@ -75,11 +78,12 @@ function signatureWarning(name, source, change, dependents) {
|
|
|
75
78
|
return `İmza değişti (${what}) ve fonksiyonu ${dependents.total} kayıt kullanıyor (${callers}): ${effect}. Güncelleme yapıldı; çağıran ifadeleri gözden geçirin — dependency_graph(module:'global-function').`;
|
|
76
79
|
}
|
|
77
80
|
/**
|
|
78
|
-
* `GET global-functions/{id}/dependencies` KOVALI grafiği döner (`{ flows, workflows }`); düz dizi
|
|
81
|
+
* `GET global-functions/{id}/dependencies` KOVALI grafiği döner (`{ flows, workflows, bindings, functions }`); düz dizi
|
|
79
82
|
* şekli de düzleştirilir — yapısal (`args.functionId`) ve metinsel (`Functions.<ad>(` / çıplak `<ad>(`)
|
|
80
83
|
* bağımlılıklar aynı listede gelir. Etiket Studio rozetleriyle aynıdır: komponent olayı `Sahip › Komponent · olay`,
|
|
81
84
|
* sayfa/layout olayı `Sahip · olay`, bağı olmayan akış `Sahip › akış adı` (sahip sayfa/layout/widget; eski yanıtta
|
|
82
|
-
* sahipsiz); automation düğümü `ad (automation)`.
|
|
85
|
+
* sahipsiz); automation düğümü `ad (automation)`. Kod tipli prop bağlaması `Sahip › Komponent · prop`, gövdesinde
|
|
86
|
+
* çağıran fonksiyon adıyla yazılır.
|
|
83
87
|
*/
|
|
84
88
|
export function summarizeDependents(payload) {
|
|
85
89
|
const data = (() => {
|
|
@@ -94,7 +98,7 @@ export function summarizeDependents(payload) {
|
|
|
94
98
|
const name = String(entry.name ?? entry.slug ?? entry.id ?? '?');
|
|
95
99
|
const host = text(entry.hostName);
|
|
96
100
|
const component = text(entry.componentName);
|
|
97
|
-
const event = text(entry.eventTitle) ?? text(entry.eventName);
|
|
101
|
+
const event = text(entry.eventTitle) ?? text(entry.eventName) ?? text(entry.propertyName);
|
|
98
102
|
const label = component
|
|
99
103
|
? [host, [component, event].filter(Boolean).join(' · ')].filter(Boolean).join(' › ')
|
|
100
104
|
: event
|
|
@@ -106,14 +110,20 @@ export function summarizeDependents(payload) {
|
|
|
106
110
|
const entries = data.filter(isRecord);
|
|
107
111
|
const moduleOf = (entry) => String(entry.module ?? entry.type ?? entry.kind ?? '').toLowerCase();
|
|
108
112
|
const isWorkflow = (entry) => moduleOf(entry).includes('workflow') || moduleOf(entry) === 'automation';
|
|
113
|
+
const isFunction = (entry) => moduleOf(entry) === 'global-function';
|
|
114
|
+
const isBinding = (entry) => text(entry.propertyName) !== undefined;
|
|
109
115
|
const workflows = names(entries.filter(isWorkflow));
|
|
110
|
-
const
|
|
111
|
-
|
|
116
|
+
const functions = names(entries.filter(isFunction));
|
|
117
|
+
const bindings = names(entries.filter(entry => !isWorkflow(entry) && !isFunction(entry) && isBinding(entry)));
|
|
118
|
+
const flows = names(entries.filter(entry => !isWorkflow(entry) && !isFunction(entry) && !isBinding(entry)));
|
|
119
|
+
return { flows, workflows, bindings, functions, total: flows.length + workflows.length + bindings.length + functions.length };
|
|
112
120
|
}
|
|
113
121
|
const record = isRecord(data) ? data : {};
|
|
114
122
|
const flows = names(record.flows);
|
|
115
123
|
const workflows = names(record.workflows);
|
|
116
|
-
|
|
124
|
+
const bindings = names(record.bindings);
|
|
125
|
+
const functions = names(record.functions);
|
|
126
|
+
return { flows, workflows, bindings, functions, total: flows.length + workflows.length + bindings.length + functions.length };
|
|
117
127
|
}
|
|
118
128
|
function usageHint(name, source, scope) {
|
|
119
129
|
if (source === 'workflow') {
|
|
@@ -268,7 +278,7 @@ export function registerFunctionTools(server, platform) {
|
|
|
268
278
|
data: saved,
|
|
269
279
|
signature: functionSignature(saved.name ? saved : definition),
|
|
270
280
|
signatureChange,
|
|
271
|
-
dependents: dependents ?? { flows: [], workflows: [], total: 0 },
|
|
281
|
+
dependents: dependents ?? { flows: [], workflows: [], bindings: [], functions: [], total: 0 },
|
|
272
282
|
...(dependents === undefined ? { dependentsNote: 'Bağımlılık ucu okunamadı; çağıran akış olup olmadığı bilinmiyor.' } : {}),
|
|
273
283
|
...(renamedReferences !== undefined ? { renamedReferences } : {}),
|
|
274
284
|
...(dependents !== undefined && dependents.total > 0 && callersBreak
|
|
@@ -321,7 +331,7 @@ export function registerFunctionTools(server, platform) {
|
|
|
321
331
|
return {
|
|
322
332
|
deleted: id,
|
|
323
333
|
source,
|
|
324
|
-
dependents: dependents ?? { flows: [], workflows: [], total: 0 },
|
|
334
|
+
dependents: dependents ?? { flows: [], workflows: [], bindings: [], functions: [], total: 0 },
|
|
325
335
|
...(dependents === undefined ? { dependentsNote: 'Bağımlılık ucu okunamadı; çağıran akış olup olmadığı bilinmiyor.' } : {}),
|
|
326
336
|
...(dependents !== undefined && dependents.total > 0
|
|
327
337
|
? { note: `${dependents.total} çağıran kırıldı (acknowledgeDependents): ${[...dependents.flows, ...dependents.workflows].join(', ')} — ifadeleri düzeltin ya da fonksiyonu aynı adla yeniden tanımlayın.` }
|
package/dist/tools/layouts.js
CHANGED
|
@@ -9,8 +9,6 @@ import { applyPropTypesToSchema, isSetPropTypeOp, setPropTypeErrors } from '../k
|
|
|
9
9
|
import { writeDiff, withWriteDiff } from '../platform/writeDiff.js';
|
|
10
10
|
import { PlatformApiError } from '../platform/errors.js';
|
|
11
11
|
import { handle } from '../platform/responses.js';
|
|
12
|
-
import { applicationTypeField, resolveProfile } from '../knowledge/platform/resolve.js';
|
|
13
|
-
import { screenProfileWarnings } from './profileWrite.js';
|
|
14
12
|
import { z } from 'zod';
|
|
15
13
|
const patchOperationsParam = z.array(z.record(z.unknown())).min(1)
|
|
16
14
|
.describe("JSON Patch listesi (RFC 6902): { op, path, value }. Yol seçici alabilir — '/views/0/children[elementId=baslik]/props/content/data'.");
|
|
@@ -48,13 +46,7 @@ export function registerLayoutTools(server, platform) {
|
|
|
48
46
|
description: "Layout'u komponent ağacı (views), widget'ları, akışları ve olaylarıyla getirir.",
|
|
49
47
|
inputSchema: { project: projectParam, environment: environmentParam, applicationId: guid, id: guid },
|
|
50
48
|
annotations: readOnly
|
|
51
|
-
}, handle(config,
|
|
52
|
-
const response = await platform.tenant('GET', project, `/layouts/${applicationId}/${id}`, { environment });
|
|
53
|
-
const typeField = applicationTypeField(await resolveProfile(platform, applicationId));
|
|
54
|
-
return response && typeof response === 'object' && !Array.isArray(response) && Object.keys(typeField).length > 0
|
|
55
|
-
? { ...response, ...typeField }
|
|
56
|
-
: response;
|
|
57
|
-
}));
|
|
49
|
+
}, handle(config, ({ project, environment, applicationId, id }) => platform.tenant('GET', project, `/layouts/${applicationId}/${id}`, { environment })));
|
|
58
50
|
server.registerTool('layout_schema_get', {
|
|
59
51
|
title: "Layout komponent ağacını oku",
|
|
60
52
|
description: "Layout'un komponent ağacını (views) okur — page_schema_get'in layout karşılığı. Ağaçta hedefli değişiklik için layout_schema_patch (sayfa ile aynı micro-op sözlüğü, PATCH /layouts/{app}/{id}/schema). Ad/parametre/olay alanlarında ya da düz JSON Patch yolu gerektiğinde layout_patch — o araç buradaki updatedAt'i expectedUpdatedAt olarak kabul eder. Tam ağacı kendiniz yazacaksanız layout_update.",
|
|
@@ -69,21 +61,20 @@ export function registerLayoutTools(server, platform) {
|
|
|
69
61
|
annotations: readOnly
|
|
70
62
|
}, handle(config, async ({ project, environment, applicationId, id, mode, componentId }) => {
|
|
71
63
|
const layout = await fetchHost(platform, 'layout', project, environment, applicationId, id);
|
|
72
|
-
const typeField = applicationTypeField(await resolveProfile(platform, applicationId));
|
|
73
64
|
const audit = layout.audit;
|
|
74
65
|
const updatedAt = audit?.updatedAt ?? audit?.createdAt;
|
|
75
66
|
const views = (layout.views ?? []);
|
|
76
67
|
if (mode === 'full')
|
|
77
|
-
return { updatedAt,
|
|
68
|
+
return { updatedAt, views };
|
|
78
69
|
if (mode === 'subtree') {
|
|
79
70
|
if (!componentId)
|
|
80
71
|
throw new PlatformApiError("mode='subtree' için componentId zorunludur.");
|
|
81
72
|
const subtree = findNode(views, componentId);
|
|
82
73
|
if (!subtree)
|
|
83
74
|
throw new PlatformApiError(`Layout'ta '${componentId}' id'li bir komponent yok; layout_schema_get'i summary kipinde çağırıp hedefi doğrulayın.`);
|
|
84
|
-
return { updatedAt,
|
|
75
|
+
return { updatedAt, views: subtree };
|
|
85
76
|
}
|
|
86
|
-
return { updatedAt,
|
|
77
|
+
return { updatedAt, views: views.map(node => summarize(node)) };
|
|
87
78
|
}));
|
|
88
79
|
server.registerTool('layout_create', {
|
|
89
80
|
title: 'Layout oluştur',
|
|
@@ -100,27 +91,18 @@ export function registerLayoutTools(server, platform) {
|
|
|
100
91
|
annotations: writes
|
|
101
92
|
}, handle(config, async ({ project, environment, applicationId, name, views, events, parameters }) => {
|
|
102
93
|
assertScreenWriteGuards('layout', { schema: views, events });
|
|
103
|
-
const resolved = await resolveProfile(platform, applicationId);
|
|
104
94
|
const fixed = normalizeComponentDefaults(views);
|
|
105
95
|
const catalogNotes = await screenCatalogNotes(platform, project, environment, applicationId, views);
|
|
106
|
-
const gaps = [
|
|
107
|
-
...(resolved.profile.layout.blocksMeaningful ? verticalGapWarnings(views ?? [], 'LAY-003 (uyarı)') : []),
|
|
108
|
-
...flexSizeWarnings(views ?? [], 'LAY-007 (uyarı)', 'LAY-008 (uyarı)'),
|
|
109
|
-
...spacingLayerWarnings(views ?? [], 'LAY-010 (uyarı)', 'LAY-011 (uyarı)'),
|
|
110
|
-
...await screenProfileWarnings(platform, resolved, project, environment, applicationId, { kind: 'layout', nodes: views ?? [] })
|
|
111
|
-
];
|
|
96
|
+
const gaps = [...verticalGapWarnings(views ?? [], 'LAY-003 (uyarı)'), ...flexSizeWarnings(views ?? [], 'LAY-007 (uyarı)', 'LAY-008 (uyarı)'), ...spacingLayerWarnings(views ?? [], 'LAY-010 (uyarı)', 'LAY-011 (uyarı)')];
|
|
112
97
|
const response = await platform.tenant('POST', project, `/layouts/${applicationId}`, {
|
|
113
98
|
environment,
|
|
114
99
|
body: { name, views: views ?? [], events: events ?? [], parameters: parameters ?? null }
|
|
115
100
|
});
|
|
116
101
|
const drops = writeDiff({ name, views: views ?? [], events: events ?? [] }, response);
|
|
117
102
|
const normalized = withWriteDiff(withNormalization(response, [...componentDefaultsNote(fixed), ...catalogNotes, ...slotWarning(views)]), drops);
|
|
118
|
-
|
|
119
|
-
if (!normalized || typeof normalized !== 'object')
|
|
103
|
+
if (gaps.length === 0 || !normalized || typeof normalized !== 'object')
|
|
120
104
|
return normalized;
|
|
121
|
-
|
|
122
|
-
return Object.keys(typeField).length === 0 ? normalized : { ...normalized, ...typeField };
|
|
123
|
-
return { ...normalized, ...typeField, layoutWarnings: gaps };
|
|
105
|
+
return { ...normalized, layoutWarnings: gaps };
|
|
124
106
|
}));
|
|
125
107
|
server.registerTool('layout_update', {
|
|
126
108
|
title: 'Layout güncelle',
|
|
@@ -144,15 +126,7 @@ export function registerLayoutTools(server, platform) {
|
|
|
144
126
|
const catalogNotes = await screenCatalogNotes(platform, project, environment, applicationId, overrides.views);
|
|
145
127
|
const body = layoutUpdateBody({ ...current, applicationId }, overrides);
|
|
146
128
|
await platform.tenant('PUT', project, `/layouts/${applicationId}/${id}`, { environment, body });
|
|
147
|
-
const
|
|
148
|
-
const gaps = overrides.views === undefined || !resolved
|
|
149
|
-
? []
|
|
150
|
-
: [
|
|
151
|
-
...(resolved.profile.layout.blocksMeaningful ? verticalGapWarnings(body.views, 'LAY-003 (uyarı)') : []),
|
|
152
|
-
...flexSizeWarnings(body.views, 'LAY-007 (uyarı)', 'LAY-008 (uyarı)'),
|
|
153
|
-
...spacingLayerWarnings(body.views, 'LAY-010 (uyarı)', 'LAY-011 (uyarı)'),
|
|
154
|
-
...await screenProfileWarnings(platform, resolved, project, environment, applicationId, { kind: 'layout', nodes: body.views })
|
|
155
|
-
];
|
|
129
|
+
const gaps = overrides.views === undefined ? [] : [...verticalGapWarnings(body.views, 'LAY-003 (uyarı)'), ...flexSizeWarnings(body.views, 'LAY-007 (uyarı)', 'LAY-008 (uyarı)'), ...spacingLayerWarnings(body.views, 'LAY-010 (uyarı)', 'LAY-011 (uyarı)')];
|
|
156
130
|
const summary = { id, changed: Object.keys(overrides), preserved: { viewNodes: body.views.length } };
|
|
157
131
|
if (gaps.length > 0)
|
|
158
132
|
summary.layoutWarnings = gaps;
|
|
@@ -219,13 +193,7 @@ export function registerLayoutTools(server, platform) {
|
|
|
219
193
|
body: { baseVersion: baseVersionOf(document, 'layout_get'), ops: netOps }
|
|
220
194
|
});
|
|
221
195
|
const written = patchResult(saved, finalDocument);
|
|
222
|
-
const
|
|
223
|
-
const gaps = [
|
|
224
|
-
...(profile.profile.layout.blocksMeaningful ? verticalGapWarnings(body.views, 'LAY-003 (uyarı)') : []),
|
|
225
|
-
...flexSizeWarnings(body.views, 'LAY-007 (uyarı)', 'LAY-008 (uyarı)'),
|
|
226
|
-
...spacingLayerWarnings(body.views, 'LAY-010 (uyarı)', 'LAY-011 (uyarı)'),
|
|
227
|
-
...await screenProfileWarnings(platform, profile, project, environment, applicationId, { kind: 'layout', nodes: body.views })
|
|
228
|
-
];
|
|
196
|
+
const gaps = [...verticalGapWarnings(body.views, 'LAY-003 (uyarı)'), ...flexSizeWarnings(body.views, 'LAY-007 (uyarı)', 'LAY-008 (uyarı)'), ...spacingLayerWarnings(body.views, 'LAY-010 (uyarı)', 'LAY-011 (uyarı)')];
|
|
229
197
|
const summary = {
|
|
230
198
|
id,
|
|
231
199
|
applied: patchOps.length,
|
|
@@ -10,11 +10,15 @@ import { policyRules } from '../knowledge/rules.js';
|
|
|
10
10
|
import { writeDiff, withWriteDiff } from '../platform/writeDiff.js';
|
|
11
11
|
import { identityHeaders } from '../platform/tokenVault.js';
|
|
12
12
|
import { PlatformApiError } from '../platform/errors.js';
|
|
13
|
+
import { existingMatch, ifExistsParam, listAll, platformRename, withOutcome, withRename } from '../platform/ifExists.js';
|
|
14
|
+
import { outlineWrite, recordWriteViewParam } from '../platform/writeView.js';
|
|
13
15
|
import { handle } from '../platform/responses.js';
|
|
14
16
|
import fastJsonPatch from 'fast-json-patch';
|
|
15
17
|
import { z } from 'zod';
|
|
16
18
|
import { constraintSummary } from '../knowledge/backendConstraints.js';
|
|
17
19
|
import { unmappedMembers, introducedUnmappedMembers, withUnmappedMembers } from '../knowledge/schemaGuard.js';
|
|
20
|
+
const MICROSERVICE_OUTLINE_KEYS = ['url', 'httpMethod', 'source', 'type'];
|
|
21
|
+
const microserviceIfExistsParam = ifExistsParam('uç', 'mevcut ucu bu tanımla tam gövde günceller (id\'si korunur; microservice_update ile aynı yazma)');
|
|
18
22
|
const policyOverrideParam = z.boolean().optional()
|
|
19
23
|
.describe('true: politika block bulgularını warn\'a indirir (bilinçli istisna; yanıtta işaretlenir)');
|
|
20
24
|
/**
|
|
@@ -287,28 +291,52 @@ export function registerMicroserviceTools(server, platform) {
|
|
|
287
291
|
}));
|
|
288
292
|
server.registerTool('microservice_create', {
|
|
289
293
|
title: 'Microservice oluştur',
|
|
290
|
-
description: "Yeni microservice tanımlar (definition = CreateMicroserviceCommand). İlk kullanımdan önce platform_guide('microservice') okuyun: kaynak seçimi (table > query) ve selectFields disiplini. 'description' ZORUNLUDUR (MS-014, block): çağıranın bu uçtan ne elde ettiğini yazın. Auth/oturum gerektiren uçlarda definition.middlewareIds ile middleware bağlayın; kimlik kapsaması filtrede source:'expression' + value:'user.id' ile yapılır (platform_guide('middleware')). BU YALNIZ source:'table' UÇLARDA VAR: query kaynaklı ucun parametreleri ÇAĞRIDAN gelir (body → query-string → header) ve middleware'in kurduğu user o yola girmez — kapsamı gövdeye bırakan uç sahtelenebilir (ölçüldü 2026-09-07). Kapsanmış agregasyon ucu sorguyu bir workflow query adımından çağırır: platform_guide('microservice') §'Kimlik kapsaması × uç kaynağı'. Ucun gerçek bir tuzağı varsa (auth istemiyor, sayfalama yok, atomik değil) 'caution' alanına yazın — OPSİYONELDİR, tuzak yoksa boş bırakın. Gövde politika kontrolünden geçer. " + constraintSummary('CreateMicroserviceCommand'),
|
|
291
|
-
inputSchema: { project: projectParam, environment: environmentParam, applicationId: appRef, definition: definitionParam, policyOverride: policyOverrideParam },
|
|
294
|
+
description: "Yeni microservice tanımlar (definition = CreateMicroserviceCommand). Aynı ad ya da slug'da uç varsa varsayılan olarak YAZMAZ ve mevcut ucun id'sini döner (MS-023 — gönderilseydi platform '<ad> (1)' adıyla ikinci bir uç açardı); tekrar çalıştırılabilir yazma için ifExists:'update' verin, yanıttaki outcome 'created' | 'updated' olur. Mevcut uçlar okunamazsa da yazmaz. İlk kullanımdan önce platform_guide('microservice') okuyun: kaynak seçimi (table > query) ve selectFields disiplini. 'description' ZORUNLUDUR (MS-014, block): çağıranın bu uçtan ne elde ettiğini yazın. Auth/oturum gerektiren uçlarda definition.middlewareIds ile middleware bağlayın; kimlik kapsaması filtrede source:'expression' + value:'user.id' ile yapılır (platform_guide('middleware')). BU YALNIZ source:'table' UÇLARDA VAR: query kaynaklı ucun parametreleri ÇAĞRIDAN gelir (body → query-string → header) ve middleware'in kurduğu user o yola girmez — kapsamı gövdeye bırakan uç sahtelenebilir (ölçüldü 2026-09-07). Kapsanmış agregasyon ucu sorguyu bir workflow query adımından çağırır: platform_guide('microservice') §'Kimlik kapsaması × uç kaynağı'. Ucun gerçek bir tuzağı varsa (auth istemiyor, sayfalama yok, atomik değil) 'caution' alanına yazın — OPSİYONELDİR, tuzak yoksa boş bırakın. Gövde politika kontrolünden geçer. " + constraintSummary('CreateMicroserviceCommand'),
|
|
295
|
+
inputSchema: { project: projectParam, environment: environmentParam, applicationId: appRef, definition: definitionParam, ifExists: microserviceIfExistsParam, view: recordWriteViewParam, policyOverride: policyOverrideParam },
|
|
292
296
|
annotations: writes
|
|
293
|
-
}, handle(config, async ({ project, environment, applicationId, definition, policyOverride }) => {
|
|
297
|
+
}, handle(config, async ({ project, environment, applicationId, definition, ifExists, view, policyOverride }) => {
|
|
294
298
|
const resolver = new RefResolver(platform, project, environment);
|
|
295
299
|
const resolvedApplicationId = await resolver.applicationId(applicationId);
|
|
296
300
|
const hydrated = await hydrateMicroserviceRefs(definition, resolver);
|
|
297
301
|
const normalized = normalizeMicroserviceDefinition(definition);
|
|
302
|
+
// Politika ÖNCE koşar: block alan istek platforma hiç gitmez, var-mı listesi de okunmaz.
|
|
303
|
+
const createFindings = enforcePolicy(evaluatePolicy(config, policyRules, 'microservice', definition, { operation: 'create' }, policyOverride));
|
|
304
|
+
const scopeFindings = await queryScopeFindings(platform, project, environment, definition);
|
|
305
|
+
const existing = await existingMatch({
|
|
306
|
+
code: 'MS-023',
|
|
307
|
+
noun: 'uç',
|
|
308
|
+
where: 'bu uygulamada',
|
|
309
|
+
name: typeof definition.name === 'string' ? definition.name : undefined,
|
|
310
|
+
slug: typeof definition.slug === 'string' ? definition.slug : undefined,
|
|
311
|
+
id: typeof definition.id === 'string' ? definition.id : undefined,
|
|
312
|
+
idParam: 'definition.id',
|
|
313
|
+
ifExists,
|
|
314
|
+
list: () => listAll(platform, project, environment, `/microservices/${resolvedApplicationId}`),
|
|
315
|
+
listTool: 'microservice_list',
|
|
316
|
+
copyCheck: 'kullanılıp kullanılmadığını dependency_graph ile kontrol edip gerekirse silin',
|
|
317
|
+
updateTool: 'id ile microservice_update'
|
|
318
|
+
});
|
|
319
|
+
// Kayıt bulunduysa yazma bir UPDATE'tir: gövde kuralları microservice_update ile aynı kipte (R-1).
|
|
298
320
|
const findings = [
|
|
299
|
-
...
|
|
300
|
-
|
|
321
|
+
...(existing === undefined
|
|
322
|
+
? createFindings
|
|
323
|
+
: enforcePolicy(evaluatePolicy(config, policyRules, 'microservice', definition, { operation: 'update' }, policyOverride))),
|
|
324
|
+
...scopeFindings
|
|
301
325
|
];
|
|
302
|
-
const drift = unmappedMembers('CreateMicroserviceCommand', definition);
|
|
303
|
-
const payload =
|
|
304
|
-
|
|
326
|
+
const drift = unmappedMembers(existing === undefined ? 'CreateMicroserviceCommand' : 'UpdateMicroserviceCommand', definition);
|
|
327
|
+
const payload = existing === undefined
|
|
328
|
+
? await platform.tenant('POST', project, `/microservices/${resolvedApplicationId}`, { environment, body: definition })
|
|
329
|
+
: await platform.tenant('PUT', project, `/microservices/${resolvedApplicationId}/${String(existing.id)}`, { environment, body: definition });
|
|
330
|
+
const saved = payload?.data;
|
|
331
|
+
const rename = ifExists === 'duplicate' ? platformRename(definition, saved, 'dash') : { sent: definition };
|
|
332
|
+
return attachFindings(withUnmappedMembers(withWriteDiff(withResolved(withRename(withNormalization(withOutcome(outlineWrite(payload, view, MICROSERVICE_OUTLINE_KEYS), existing), normalized), rename.renamed), [...resolver.resolved, ...hydrated]), writeDiff(rename.sent, payload)), drift), findings);
|
|
305
333
|
}));
|
|
306
334
|
server.registerTool('microservice_update', {
|
|
307
335
|
title: 'Microservice güncelle',
|
|
308
336
|
description: 'Microservice tanımını günceller (tam gövde). Hedefli değişiklik için microservice_patch tercih edin. \'description\' ZORUNLUDUR (MS-014, block). Ucun gerçek bir tuzağı varsa (auth istemiyor, sayfalama yok, atomik değil) \'caution\' alanına yazın — OPSİYONELDİR, tuzak yoksa boş bırakın. Gövde politika kontrolünden geçer. ' + constraintSummary('UpdateMicroserviceCommand'),
|
|
309
|
-
inputSchema: { project: projectParam, environment: environmentParam, applicationId: appRef, id: guid, definition: definitionParam, policyOverride: policyOverrideParam },
|
|
337
|
+
inputSchema: { project: projectParam, environment: environmentParam, applicationId: appRef, id: guid, definition: definitionParam, view: recordWriteViewParam, policyOverride: policyOverrideParam },
|
|
310
338
|
annotations: updates
|
|
311
|
-
}, handle(config, async ({ project, environment, applicationId, id, definition, policyOverride }) => {
|
|
339
|
+
}, handle(config, async ({ project, environment, applicationId, id, definition, view, policyOverride }) => {
|
|
312
340
|
const resolver = new RefResolver(platform, project, environment);
|
|
313
341
|
const resolvedApplicationId = await resolver.applicationId(applicationId);
|
|
314
342
|
const hydrated = await hydrateMicroserviceRefs(definition, resolver);
|
|
@@ -319,7 +347,7 @@ export function registerMicroserviceTools(server, platform) {
|
|
|
319
347
|
];
|
|
320
348
|
const drift = unmappedMembers('UpdateMicroserviceCommand', definition);
|
|
321
349
|
const payload = await platform.tenant('PUT', project, `/microservices/${resolvedApplicationId}/${id}`, { environment, body: definition });
|
|
322
|
-
return attachFindings(withUnmappedMembers(withWriteDiff(withResolved(withNormalization(payload, normalized), [...resolver.resolved, ...hydrated]), writeDiff(definition, payload)), drift), findings);
|
|
350
|
+
return attachFindings(withUnmappedMembers(withWriteDiff(withResolved(withNormalization(outlineWrite(payload, view, MICROSERVICE_OUTLINE_KEYS), normalized), [...resolver.resolved, ...hydrated]), writeDiff(definition, payload)), drift), findings);
|
|
323
351
|
}));
|
|
324
352
|
server.registerTool('microservice_patch', {
|
|
325
353
|
title: 'Microservice\'e patch uygula',
|