@appaflytech/zeyrek-ai-mcp 0.0.36 → 0.0.40
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 +15 -0
- package/README.md +5 -5
- package/dist/config.js +37 -0
- package/dist/index.js +2 -0
- package/dist/knowledge/access.js +802 -0
- package/dist/knowledge/backendConstraints.js +18 -1
- package/dist/knowledge/case.js +41 -0
- package/dist/knowledge/componentCatalog.js +524 -53
- package/dist/knowledge/componentEvents.js +132 -7
- package/dist/knowledge/connectDataBind.js +851 -0
- package/dist/knowledge/connectDataDefinition.js +344 -0
- package/dist/knowledge/connectDataExecute.js +93 -0
- package/dist/knowledge/connectDataRules.js +128 -0
- package/dist/knowledge/decision.js +121 -0
- package/dist/knowledge/expressionPrelude.js +60 -0
- package/dist/knowledge/layoutSummary.js +140 -2
- package/dist/knowledge/microserviceParameters.js +58 -0
- package/dist/knowledge/mining.js +94 -0
- package/dist/knowledge/mobileComponentCatalog.js +876 -0
- package/dist/knowledge/mobileComponentEvents.js +259 -0
- package/dist/knowledge/mobileStore.js +396 -0
- package/dist/knowledge/normalize.js +240 -72
- package/dist/knowledge/outlines.js +36 -0
- package/dist/knowledge/platform/lint.js +385 -0
- package/dist/knowledge/platform/mobile.js +142 -0
- package/dist/knowledge/platform/mobileLayoutLint.js +324 -0
- package/dist/knowledge/platform/mobileRules.js +231 -0
- package/dist/knowledge/platform/mobileSpacing.js +82 -0
- package/dist/knowledge/platform/mobileTabs.js +108 -0
- package/dist/knowledge/platform/nativeBoxModel.js +44 -0
- package/dist/knowledge/platform/profile.js +25 -0
- package/dist/knowledge/platform/resolve.js +122 -0
- package/dist/knowledge/platform/web.js +39 -0
- package/dist/knowledge/policy.js +18 -4
- package/dist/knowledge/process.js +510 -0
- package/dist/knowledge/reservedTables.js +79 -0
- package/dist/knowledge/rowFilter.js +43 -0
- package/dist/knowledge/rules.js +485 -22
- package/dist/knowledge/schemaGuard.js +11 -0
- package/dist/knowledge/styleResolve.js +64 -10
- package/dist/knowledge/styleResolveNative.js +529 -0
- package/dist/knowledge/themeResolve.js +3 -3
- package/dist/observability/trace.js +13 -5
- package/dist/platform/argumentAliases.js +128 -0
- package/dist/platform/bodyFile.js +103 -0
- package/dist/platform/capture/actions.js +398 -0
- package/dist/platform/capture/browser.js +1 -0
- package/dist/platform/capture/capture.js +683 -0
- package/dist/platform/capture/console-log.js +122 -0
- package/dist/platform/capture/index.js +11 -0
- package/dist/platform/capture/readiness.js +103 -0
- package/dist/platform/capture/summary.js +77 -0
- package/dist/platform/capture/target.js +5 -0
- package/dist/platform/capture/targets/studio-mobile.js +15 -0
- package/dist/platform/capture/targets/studio-web.js +5 -0
- package/dist/platform/diagnostics.js +10 -4
- package/dist/platform/domSummary.js +1 -1
- package/dist/platform/errors.js +163 -10
- package/dist/platform/http.js +29 -8
- package/dist/platform/impersonation.js +45 -0
- package/dist/platform/jsonArray.js +24 -0
- package/dist/platform/responses.js +39 -5
- package/dist/platform/schemaReadMemory.js +60 -0
- package/dist/platform/secrets.js +68 -2
- package/dist/platform/strictArguments.js +68 -12
- package/dist/platform/tokenVault.js +187 -4
- package/dist/resources/examples/bpm-gorev-kontrol.json +294 -0
- package/dist/resources/examples/bpm-iso-dokuman.json +228 -0
- package/dist/resources/examples/bpm-satinalma.json +376 -0
- package/dist/resources/examples/connect-data-convert-verify.json +107 -0
- package/dist/resources/examples/connect-data-dropdown-roles.json +66 -0
- package/dist/resources/examples/connect-data-edit-m2m.json +73 -0
- package/dist/resources/examples/connect-data-query-params.json +50 -0
- package/dist/resources/examples/connect-data-table-entity.json +147 -0
- package/dist/resources/examples/connect-data-workflow-microservice.json +65 -0
- package/dist/resources/examples/middleware-authenticate.json +1 -1
- package/dist/resources/examples/mobile-app-shell-drawer.json +244 -0
- package/dist/resources/examples/mobile-app-shell-stack.json +157 -0
- package/dist/resources/examples/mobile-app-shell-tab.json +208 -0
- package/dist/resources/examples/mobile-form-screen.json +365 -0
- package/dist/resources/examples/mobile-list-detail-form.json +703 -0
- package/dist/resources/examples/page-detail-profile.json +2 -1
- package/dist/resources/examples/page-dynamic-form.json +3 -2
- package/dist/resources/examples/page-flex-sections.json +1 -1
- package/dist/resources/examples/page-html-embed.json +1 -1
- package/dist/resources/examples/workflow-basic.json +8 -2
- package/dist/resources/guides/access.md +810 -0
- package/dist/resources/guides/bpm.md +691 -0
- package/dist/resources/guides/case.md +153 -0
- package/dist/resources/guides/component.md +92 -5
- package/dist/resources/guides/decision.md +185 -0
- package/dist/resources/guides/entity.md +50 -8
- package/dist/resources/guides/expressions.md +14 -5
- package/dist/resources/guides/functions.md +1 -1
- package/dist/resources/guides/getting-started.md +38 -1
- package/dist/resources/guides/layout.md +11 -7
- package/dist/resources/guides/merge.md +42 -7
- package/dist/resources/guides/microservice.md +88 -3
- package/dist/resources/guides/middleware.md +69 -5
- package/dist/resources/guides/mobile-layout.md +193 -0
- package/dist/resources/guides/mobile-publish.md +148 -0
- package/dist/resources/guides/mobile.md +565 -0
- package/dist/resources/guides/page.md +469 -82
- package/dist/resources/guides/theme.md +8 -2
- package/dist/resources/guides/translation.md +1 -1
- package/dist/resources/guides/workflow.md +58 -1
- package/dist/resources/read.js +41 -0
- package/dist/resources/rules.js +11 -5
- package/dist/resources/schema-fingerprint.json +7 -5
- package/dist/resources/schemas/AccessExplanationDto.json +144 -0
- package/dist/resources/schemas/AccessMatrixDto.json +170 -0
- package/dist/resources/schemas/AccessPermissionDto.json +57 -0
- package/dist/resources/schemas/AccessPolicyDto.json +223 -0
- package/dist/resources/schemas/AccessResourceDto.json +86 -0
- package/dist/resources/schemas/AppSessionDto.json +53 -0
- package/dist/resources/schemas/ConnectDataDescribeDto.json +344 -0
- package/dist/resources/schemas/ConnectDataRequestModel.json +202 -0
- package/dist/resources/schemas/ConnectDataResultDto.json +421 -0
- package/dist/resources/schemas/ConnectorCredentialDto.json +70 -0
- package/dist/resources/schemas/ConvertConnectDataToMicroserviceCommand.json +37 -0
- package/dist/resources/schemas/CreateAccessPolicyCommand.json +209 -0
- package/dist/resources/schemas/CreateDecisionCommand.json +221 -0
- package/dist/resources/schemas/CreateEntityCommand.json +118 -0
- package/dist/resources/schemas/CreateMicroserviceCommand.json +13 -1
- package/dist/resources/schemas/CreateProcessDefinitionCommand.json +1337 -0
- package/dist/resources/schemas/CreateQueryCommand.json +4 -0
- package/dist/resources/schemas/CreateWorkflowCommand.json +146 -1
- package/dist/resources/schemas/DataJobDto.json +6 -1
- package/dist/resources/schemas/DecisionDto.json +302 -0
- package/dist/resources/schemas/DetachConnectDataMicroserviceCommand.json +28 -0
- package/dist/resources/schemas/ExplainAccessQuery.json +33 -0
- package/dist/resources/schemas/GenerateProcessDocumentCommand.json +116 -0
- package/dist/resources/schemas/GlobalFunctionUpdateResultDto.json +24 -0
- package/dist/resources/schemas/MicroserviceDto.json +15 -3
- package/dist/resources/schemas/OrganizationProjectDto.json +343 -0
- package/dist/resources/schemas/OrganizationSchemaDto.json +196 -0
- package/dist/resources/schemas/OrganizationSchemaVersionDto.json +207 -0
- package/dist/resources/schemas/PatchProcessDefinitionCommand.json +76 -0
- package/dist/resources/schemas/PreviewMicroserviceCommand.json +10 -1
- package/dist/resources/schemas/PreviewWorkflowCommand.json +25 -1
- package/dist/resources/schemas/ProcessDefinitionDto.json +1480 -0
- package/dist/resources/schemas/ProcessSimulationDto.json +96 -0
- package/dist/resources/schemas/SaveConnectorCredentialCommand.json +90 -0
- package/dist/resources/schemas/SaveProcessEventSourceCommand.json +61 -0
- package/dist/resources/schemas/SaveProcessMailSourceCommand.json +89 -0
- package/dist/resources/schemas/SignProcessTaskCommand.json +73 -0
- package/dist/resources/schemas/SimulateProcessDefinitionQuery.json +92 -0
- package/dist/resources/schemas/StartMobileBuildCommand.json +54 -0
- package/dist/resources/schemas/StepDto.json +25 -1
- package/dist/resources/schemas/UpdateAccessPolicyCommand.json +209 -0
- package/dist/resources/schemas/UpdateDecisionCommand.json +216 -0
- package/dist/resources/schemas/UpdateEntityCommand.json +118 -0
- package/dist/resources/schemas/UpdateMicroserviceCommand.json +13 -1
- package/dist/resources/schemas/UpdateProcessDefinitionCommand.json +1357 -0
- package/dist/resources/schemas/UpdateQueryCommand.json +4 -0
- package/dist/resources/schemas/UpdateViewCommand.json +0 -2
- package/dist/resources/schemas/UpdateWorkflowCommand.json +146 -1
- package/dist/resources/schemas/UpsertMobileStoreSettingsCommand.json +333 -0
- package/dist/resources/schemas/ValidateProcessDefinitionQuery.json +1339 -0
- package/dist/server.js +31 -1
- package/dist/tools/access.js +369 -2
- package/dist/tools/accessOperations.js +135 -0
- package/dist/tools/accessPolicies.js +170 -0
- package/dist/tools/accessRuntime.js +203 -0
- package/dist/tools/applications.js +63 -3
- package/dist/tools/bpm.js +343 -0
- package/dist/tools/bpmGovernance.js +266 -0
- package/dist/tools/bpmOperations.js +230 -0
- package/dist/tools/bpmReporting.js +154 -0
- package/dist/tools/bpmRouting.js +150 -0
- package/dist/tools/bpmRuntime.js +396 -0
- package/dist/tools/case.js +178 -0
- package/dist/tools/caseRuntime.js +89 -0
- package/dist/tools/common.js +19 -6
- package/dist/tools/components.js +118 -14
- package/dist/tools/connectData.js +450 -0
- package/dist/tools/connectors.js +193 -0
- package/dist/tools/datasources.js +3 -3
- package/dist/tools/decisions.js +206 -0
- package/dist/tools/dependencies.js +1 -1
- package/dist/tools/deployments.js +38 -3
- package/dist/tools/designSchemaPatch.js +34 -9
- package/dist/tools/dtos.js +36 -5
- package/dist/tools/entities.js +79 -12
- package/dist/tools/environments.js +1 -1
- package/dist/tools/externalComponents.js +26 -5
- package/dist/tools/flows.js +119 -13
- package/dist/tools/folders.js +1 -1
- package/dist/tools/imports.js +5 -2
- package/dist/tools/layouts.js +50 -17
- package/dist/tools/merges.js +2 -2
- package/dist/tools/microservices.js +77 -25
- package/dist/tools/mobile.js +280 -0
- package/dist/tools/mobileStore.js +572 -0
- package/dist/tools/modules.js +48 -0
- package/dist/tools/organizationStructure.js +137 -0
- package/dist/tools/pages.js +467 -100
- package/dist/tools/platformInfo.js +28 -8
- package/dist/tools/profileWrite.js +92 -0
- package/dist/tools/projects.js +71 -9
- package/dist/tools/rows.js +69 -13
- package/dist/tools/schemaOps.js +61 -1
- package/dist/tools/screenWrite.js +36 -7
- package/dist/tools/screens.js +95 -5
- package/dist/tools/templates.js +26 -1
- package/dist/tools/translations.js +1 -1
- package/dist/tools/variables.js +11 -5
- package/dist/tools/workflows.js +10 -5
- package/package.json +2 -2
- package/dist/platform/screenshot.js +0 -1162
package/dist/tools/dtos.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { guid, projectParam, environmentParam, definitionParam, pagingParams, readOnly, writes, updates, destructive } from './common.js';
|
|
2
2
|
import { writeDiff, withWriteDiff } from '../platform/writeDiff.js';
|
|
3
|
+
import { withNormalization } from '../knowledge/normalize.js';
|
|
3
4
|
import { handle } from '../platform/responses.js';
|
|
4
5
|
import { z } from 'zod';
|
|
5
6
|
import { evaluatePolicy, enforcePolicy, attachFindings } from '../knowledge/policy.js';
|
|
@@ -29,16 +30,46 @@ export function registerDtoTools(server, platform) {
|
|
|
29
30
|
}, handle(config, ({ project, environment, id }) => platform.tenant('GET', project, `/datatransferobjects/${id}/scheme`, { environment })));
|
|
30
31
|
server.registerTool('dto_create', {
|
|
31
32
|
title: 'DTO oluştur',
|
|
32
|
-
description: "Yeni DataTransferObject tanımlar (definition = CreateDataTransferObjectCommand). Workflow'un parameterDtoId/returnDtoId sözleşmeleri buradan çıkar; şablon için dto_get kullanın. " + constraintSummary('CreateDataTransferObjectCommand'),
|
|
33
|
+
description: "Yeni DataTransferObject tanımlar (definition = CreateDataTransferObjectCommand). Workflow'un parameterDtoId/returnDtoId sözleşmeleri buradan çıkar; şablon için dto_get kullanın. ALAN LİSTESİ 'children' alanındadır ve create gövdesinde YAZILIR; eşanlamlı olarak 'fields' de kabul edilir (gateway 'children'a çevirir — komut şemasında 'fields' yoktur ve platform onu sessizce düşürürdü, DTO-003). " + constraintSummary('CreateDataTransferObjectCommand'),
|
|
33
34
|
inputSchema: { project: projectParam, environment: environmentParam, definition: definitionParam, policyOverride: policyOverrideParam },
|
|
34
35
|
annotations: writes
|
|
35
36
|
}, handle(config, async ({ project, environment, definition, policyOverride }) => {
|
|
37
|
+
const body = { ...definition };
|
|
38
|
+
const notes = [];
|
|
39
|
+
// DTO-003 / S2-28 — alan listesi 'fields' adıyla gelirse SESSİZCE kaybolurdu: komut şemasında
|
|
40
|
+
// öyle bir üye yok, platform bilinmeyen üyeyi atıyor ve yanıt children:[] dönüyor. Ad eşlenir.
|
|
41
|
+
if (Object.hasOwn(body, 'fields')) {
|
|
42
|
+
const alias = body.fields;
|
|
43
|
+
delete body.fields;
|
|
44
|
+
if (body.children === undefined && alias !== undefined && alias !== null) {
|
|
45
|
+
body.children = alias;
|
|
46
|
+
notes.push("'fields' → 'children' olarak yazıldı: create gövdesinde alan listesinin adı 'children'dır, 'fields' sessizce düşerdi (DTO-003)");
|
|
47
|
+
}
|
|
48
|
+
else if (alias !== undefined && alias !== null) {
|
|
49
|
+
notes.push("'fields' yok sayıldı: gövde zaten 'children' taşıyor (DTO-003)");
|
|
50
|
+
}
|
|
51
|
+
}
|
|
36
52
|
// G-23: 09-05 koşusunda çocuk alanın `type: "text"` değeri sessizce `object`'e çevrildi ve
|
|
37
53
|
// bu ancak platformun kendi ürettiği bir DTO okunarak fark edildi. Doğrusu `"string"` —
|
|
38
|
-
// DTO-001 tip sözlüğünü artık istek gitmeden denetler.
|
|
39
|
-
|
|
40
|
-
const
|
|
41
|
-
|
|
54
|
+
// DTO-001 tip sözlüğünü artık istek gitmeden denetler. Denetim eşlemeden SONRA koşar ki
|
|
55
|
+
// 'fields' ile gelen çocuklar da (slug zorunluluğu dâhil) ilk adımda yakalansın.
|
|
56
|
+
const findings = enforcePolicy(evaluatePolicy(config, policyRules, 'dto', body, { operation: 'create' }, policyOverride));
|
|
57
|
+
let payload = await platform.tenant('POST', project, '/datatransferobjects', { environment, body });
|
|
58
|
+
// İkinci adım EMNİYET KEMERİDİR, kural değil: 2026-09-20 ölçümünde create çocukları yazdı.
|
|
59
|
+
// Yazmayan bir platform sürümünde (ya da bir alt kümeyi düşürdüğünde) gateway aynı yanıt
|
|
60
|
+
// içinde update ile tamamlar — çağıran iki adımı görmez.
|
|
61
|
+
const requested = Array.isArray(body.children) ? body.children : [];
|
|
62
|
+
const created = (payload?.data ?? payload);
|
|
63
|
+
const savedChildren = created?.children;
|
|
64
|
+
const createdId = typeof created?.id === 'string' ? created.id : undefined;
|
|
65
|
+
if (requested.length > 0 && createdId !== undefined && (!Array.isArray(savedChildren) || savedChildren.length < requested.length)) {
|
|
66
|
+
payload = await platform.tenant('PUT', project, `/datatransferobjects/${createdId}`, {
|
|
67
|
+
environment,
|
|
68
|
+
body: { ...body, id: createdId }
|
|
69
|
+
});
|
|
70
|
+
notes.push(`alan listesi ikinci adımda update ile yazıldı: create ${requested.length} çocuğun ${Array.isArray(savedChildren) ? savedChildren.length : 0} tanesini kaydetti (DTO-003)`);
|
|
71
|
+
}
|
|
72
|
+
return attachFindings(withNormalization(withWriteDiff(payload, writeDiff(body, payload)), notes), findings);
|
|
42
73
|
}));
|
|
43
74
|
server.registerTool('dto_update', {
|
|
44
75
|
title: 'DTO güncelle',
|
package/dist/tools/entities.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { guid, entityRef, resolveEntityId, withResolved, projectParam, environmentParam, definitionParam, pagingParams, readOnly, writes, updates, destructive, microOps } from './common.js';
|
|
2
2
|
import { RefResolver, isGuid } from '../platform/refs.js';
|
|
3
|
-
import { evaluatePolicy, enforcePolicy, attachFindings } from '../knowledge/policy.js';
|
|
3
|
+
import { evaluatePolicy, enforcePolicy, attachFindings, applyPolicyMode } from '../knowledge/policy.js';
|
|
4
|
+
import { changedExistingProtections, fieldProtectionFindings, protectionMaskTypes } from '../knowledge/access.js';
|
|
5
|
+
import { PlatformApiError } from '../platform/errors.js';
|
|
4
6
|
import { splitJunctionFields, withNormalization } from '../knowledge/normalize.js';
|
|
5
7
|
import { unmappedMembers, introducedUnmappedMembers, withUnmappedMembers } from '../knowledge/schemaGuard.js';
|
|
6
8
|
import { entityOutline, resolvePart, resolvePatchOperations, patchedSections } from '../knowledge/outlines.js';
|
|
@@ -8,10 +10,11 @@ import { patchResult, baseVersionOf } from '../platform/patch.js';
|
|
|
8
10
|
import { isJunctionField, policyRules } from '../knowledge/rules.js';
|
|
9
11
|
import { handle } from '../platform/responses.js';
|
|
10
12
|
import fastJsonPatch from 'fast-json-patch';
|
|
13
|
+
import { jsonArray } from '../platform/jsonArray.js';
|
|
11
14
|
import { z } from 'zod';
|
|
12
15
|
const policyOverrideParam = z.boolean().optional()
|
|
13
16
|
.describe('true: politika block bulgularını warn\'a indirir (bilinçli istisna; yanıtta işaretlenir)');
|
|
14
|
-
const patchOperationsParam = z.array(z.record(z.unknown())).min(1)
|
|
17
|
+
const patchOperationsParam = jsonArray(z.array(z.record(z.unknown())).min(1))
|
|
15
18
|
.describe("RFC 6902 JSON Patch işlemleri; path'leri view:'outline'/'part' ile keşfedin");
|
|
16
19
|
function preserveApplicationBinding(definition) {
|
|
17
20
|
if (Array.isArray(definition.applicationIds))
|
|
@@ -113,7 +116,7 @@ export function registerEntityTools(server, platform) {
|
|
|
113
116
|
}));
|
|
114
117
|
server.registerTool('entity_create', {
|
|
115
118
|
title: 'Entity oluştur',
|
|
116
|
-
description: "Datasource altında yeni entity oluşturur (definition = CreateEntityCommand: name, fields[], applicationIds, ...). İlişkiler ve alan tipi kararı için platform_guide('entity') okuyun. Gövde politika kontrolünden geçer. applicationIds create'te platformca bağlanır (CreateEntityCommandHandler entityApplications'ı Entity ctor'una geçirir); satır uçları bu bağa İHTİYAÇ DUYMAZ — bağ yalnız Studio'nun application'a göre entity listesini ve bağımlılık sorgularını etkiler. `fields[].isLocalized: true` alanı ana tablodan çıkarıp companion `<slug>_localization` entity'sine taşır (yalnız düz METİN alanı; anahtar/unique/ilişki olamaz) — platform_guide('entity') §'Çoklu dilli alan'. SEÇİM LİSTESİNE (enum) TEK DEĞER: listeyi yeniden göndermeyin — ekleme `{op:'add', path:'/fields[slug=status]/valueOptions/-', value:{value:'Onayda', order:4}}`, silme `{op:'remove', path:'/fields[slug=status]/valueOptions[value=Eski]'}`, yeniden adlandırma `{op:'replace', path:'/fields[slug=status]/valueOptions[value=Eski]/value', value:'Yeni'}`; `isEnum` türetilmiştir, ayrıca yazılmaz. Silme/yeniden adlandırmadan ÖNCE o değeri taşıyan satırları taşıyın — yoksa istek reddedilir ve hiçbir şey yazılmaz ('check constraint … is violated by some row') (platform_guide('entity') §'Alan tipine karar verme').",
|
|
119
|
+
description: "Datasource altında yeni entity oluşturur (definition = CreateEntityCommand: name, fields[], applicationIds, ...). İlişkiler ve alan tipi kararı için platform_guide('entity') okuyun. Alan koruması (fields[].protection: masked · encrypted · masked-encrypted) ve türetilmiş sorgu alanı (fields[].derivedFromFieldId + derivationRule) aynı gövdede geçirgen gider — korunamayan tip/display (PROT-003) ve geçersiz türetme kuralı (PROT-004) istek gitmeden durur; kurallar platform_guide('access') §'Alan koruması'. Gövde politika kontrolünden geçer. applicationIds create'te platformca bağlanır (CreateEntityCommandHandler entityApplications'ı Entity ctor'una geçirir); satır uçları bu bağa İHTİYAÇ DUYMAZ — bağ yalnız Studio'nun application'a göre entity listesini ve bağımlılık sorgularını etkiler. `fields[].isLocalized: true` alanı ana tablodan çıkarıp companion `<slug>_localization` entity'sine taşır (yalnız düz METİN alanı; anahtar/unique/ilişki olamaz) — platform_guide('entity') §'Çoklu dilli alan'. SEÇİM LİSTESİNE (enum) TEK DEĞER: listeyi yeniden göndermeyin — ekleme `{op:'add', path:'/fields[slug=status]/valueOptions/-', value:{value:'Onayda', order:4}}`, silme `{op:'remove', path:'/fields[slug=status]/valueOptions[value=Eski]'}`, yeniden adlandırma `{op:'replace', path:'/fields[slug=status]/valueOptions[value=Eski]/value', value:'Yeni'}`; `isEnum` türetilmiştir, ayrıca yazılmaz. Silme/yeniden adlandırmadan ÖNCE o değeri taşıyan satırları taşıyın — yoksa istek reddedilir ve hiçbir şey yazılmaz ('check constraint … is violated by some row') (platform_guide('entity') §'Alan tipine karar verme').",
|
|
117
120
|
inputSchema: { project: projectParam, environment: environmentParam, datasourceId: guid, definition: definitionParam, policyOverride: policyOverrideParam },
|
|
118
121
|
annotations: writes
|
|
119
122
|
}, handle(config, async ({ project, environment, datasourceId, definition, policyOverride }) => {
|
|
@@ -128,12 +131,28 @@ export function registerEntityTools(server, platform) {
|
|
|
128
131
|
if (!createdId) {
|
|
129
132
|
throw new Error(`Entity kuruldu ama id okunamadı; m2m alanları (${deferred.map(field => field.slug ?? field.name).join(', ')}) eklenmedi — entity_get ile kaydı bulup entity_patch ile ekleyin.`);
|
|
130
133
|
}
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
134
|
+
// 38/U-15: ertelenen alanlar TEK TEK eklenir. Hepsi tek PUT'ta gidince çoklu enum (VARCHAR + many-to-many +
|
|
135
|
+
// valueOptions) başka bir junction alanıyla birlikteyken platform 400 RowHasExistingRelation ("Related table:
|
|
136
|
+
// 'Entities', column: 'Relation Id'") döndü: çoklu enum alanının relationId'si aynı güncellemede açılan değer
|
|
137
|
+
// entity'sini gösterir ve iki junction'lı yazmada alan kaydı o entity'den önce düşüyor (EntityService.UpdateAsync
|
|
138
|
+
// fieldRepository.CreateRange → manyToManyFieldProcessor/entityRepository.Create). Ölçüldü 2026-09-21 cdt-mcp:
|
|
139
|
+
// m2m+dosya-m2m+çoklu enum, dosya-m2m+çoklu enum, çoklu enum×2 düştü; m2m×2 geçti; tek tek hepsi geçti.
|
|
140
|
+
let updated;
|
|
141
|
+
for (const field of deferred) {
|
|
142
|
+
const current = await platform.tenant('GET', project, `/entities/${datasourceId}/${createdId}`, { environment });
|
|
143
|
+
const full = structuredClone((current?.data ?? current));
|
|
144
|
+
full.fields = [...(Array.isArray(full.fields) ? full.fields : []), field];
|
|
145
|
+
preserveApplicationBinding(full);
|
|
146
|
+
try {
|
|
147
|
+
updated = await platform.tenant('PUT', project, `/entities/${createdId}`, { environment, body: full });
|
|
148
|
+
}
|
|
149
|
+
catch (error) {
|
|
150
|
+
const added = deferred.slice(0, deferred.indexOf(field)).map(item => item.slug ?? item.name);
|
|
151
|
+
const missing = deferred.slice(deferred.indexOf(field)).map(item => item.slug ?? item.name);
|
|
152
|
+
throw new PlatformApiError(`Entity ${createdId} kuruldu${added.length ? `, ${added.join(', ')} eklendi` : ''}; '${String(field.slug ?? field.name)}' eklenirken platform reddetti: ${error instanceof Error ? error.message : String(error)} — eklenmeyen alanlar (${missing.join(', ')}) entity_patch ile tek tek eklenebilir.`, error instanceof PlatformApiError ? error.status : 400, error instanceof PlatformApiError ? error.code : undefined);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
const note = `m2m/çoklu alanlar (${deferred.map(field => field.slug ?? field.name).join(', ')}) ayrı güncelleme turlarında TEK TEK eklendi: create yolu junction kaydını açar ama fiziksel tabloyu kurmaz (platform bug'ı P-01), update yolu kurar; çoklu enum başka bir junction alanıyla aynı güncellemede platformda düşer (38/U-15).`;
|
|
137
156
|
return attachFindings(withUnmappedMembers(withNormalization(updated, [note]), drift), findings);
|
|
138
157
|
}));
|
|
139
158
|
server.registerTool('entity_create_discovered', {
|
|
@@ -144,7 +163,7 @@ export function registerEntityTools(server, platform) {
|
|
|
144
163
|
}, handle(config, ({ project, environment, datasourceId, definition }) => platform.tenant('POST', project, `/entities/${datasourceId}/discovered`, { environment, body: definition })));
|
|
145
164
|
server.registerTool('entity_update', {
|
|
146
165
|
title: 'Entity güncelle',
|
|
147
|
-
description: "Entity tanımını günceller (tam gövde). Hedefli değişiklik için entity_patch tercih edin. Gövde politika kontrolünden geçer. DİKKAT: gövdede 'indexes' YOKSA platform entity'nin bütün index'lerini siler — gateway alan verilmemişse mevcut index'leri taşır ve bunu bildirir; silmek için 'indexes: []' yazın. `fields[].isLocalized: true` alanı ana tablodan çıkarıp companion `<slug>_localization` entity'sine taşır (yalnız düz METİN alanı; anahtar/unique/ilişki olamaz) — platform_guide('entity') §'Çoklu dilli alan'.",
|
|
166
|
+
description: "Entity tanımını günceller (tam gövde). VAR OLAN alanın korumasını bu araç değiştiremez (platform 400 ProtectionChangeNotAllowed) — entity_protection_update; yeni alan korumayı gövdeyle alır (PROT-003/PROT-004). Türetilmiş alan eklemek ya da kaynağını/kuralını değiştirmek dolu satırları arka planda dolduran bir derived-fill işi açar (tablo kilitlenmez); bitişi import_history { entityId } ile izleyin. Hedefli değişiklik için entity_patch tercih edin. Gövde politika kontrolünden geçer. DİKKAT: gövdede 'indexes' YOKSA platform entity'nin bütün index'lerini siler — gateway alan verilmemişse mevcut index'leri taşır ve bunu bildirir; silmek için 'indexes: []' yazın. `fields[].isLocalized: true` alanı ana tablodan çıkarıp companion `<slug>_localization` entity'sine taşır (yalnız düz METİN alanı; anahtar/unique/ilişki olamaz) — platform_guide('entity') §'Çoklu dilli alan'.",
|
|
148
167
|
inputSchema: { project: projectParam, environment: environmentParam, datasourceId: guid.optional().describe('Verilmezse gateway datasource ağacından bulur'), id: entityRef, definition: definitionParam, policyOverride: policyOverrideParam },
|
|
149
168
|
annotations: updates
|
|
150
169
|
}, handle(config, async ({ project, environment, datasourceId, id, definition, policyOverride }) => {
|
|
@@ -159,7 +178,7 @@ export function registerEntityTools(server, platform) {
|
|
|
159
178
|
}));
|
|
160
179
|
server.registerTool('entity_patch', {
|
|
161
180
|
title: 'Entity\'ye patch uygula',
|
|
162
|
-
description: "Tam gövdeyi bağlama almadan hedefli değişiklik: gateway mevcut tanımı okur, yolları çözer ve op'ları platformun PATCH /entities/{id} ucuna `{ baseVersion, ops }` olarak geçirir — tam gövde PUT'u ATILMAZ, yalnız fark yazılır. Yanıt outline özetidir. YOLLARDA SEÇİCİ KULLANIN, indeks değil: '/fields[slug=email]/nullable' — indeksle yazmak alan sırası değişince SESSİZCE yanlış alanı yazar. Gateway seçiciyi indekse çevirir ve hangi kayda indiğini yanıttaki patchTargets ile söyler. `baseVersion` okunan kaydın `version` alanından gelir; kayıt bu arada değiştiyse platform 409 döner ve HİÇBİR op uygulanmaz. 'applications' → 'applicationIds' çevirisini artık platform yapar. `fields[].isLocalized: true` alanı ana tablodan çıkarıp companion `<slug>_localization` entity'sine taşır (yalnız düz METİN alanı; anahtar/unique/ilişki olamaz) — platform_guide('entity') §'Çoklu dilli alan'.",
|
|
181
|
+
description: "YENİ alanı korumayla ya da türetilmiş alan olarak eklemek için de kullanılır (PROT-003/PROT-004); eklenen ya da kuralı değişen türetilmiş alanı dolu satırlarda derived-fill işi doldurur (tablo kilitlenmez, bitiş import_history { entityId }). VAR OLAN alanın korumasını değiştirmez — platform 400 ProtectionChangeNotAllowed döner, gateway istek göndermeden durdurur ve yol entity_protection_update'tir. Tam gövdeyi bağlama almadan hedefli değişiklik: gateway mevcut tanımı okur, yolları çözer ve op'ları platformun PATCH /entities/{id} ucuna `{ baseVersion, ops }` olarak geçirir — tam gövde PUT'u ATILMAZ, yalnız fark yazılır. Yanıt outline özetidir. YOLLARDA SEÇİCİ KULLANIN, indeks değil: '/fields[slug=email]/nullable' — indeksle yazmak alan sırası değişince SESSİZCE yanlış alanı yazar. Gateway seçiciyi indekse çevirir ve hangi kayda indiğini yanıttaki patchTargets ile söyler. `baseVersion` okunan kaydın `version` alanından gelir; kayıt bu arada değiştiyse platform 409 döner ve HİÇBİR op uygulanmaz. 'applications' → 'applicationIds' çevirisini artık platform yapar. `fields[].isLocalized: true` alanı ana tablodan çıkarıp companion `<slug>_localization` entity'sine taşır (yalnız düz METİN alanı; anahtar/unique/ilişki olamaz) — platform_guide('entity') §'Çoklu dilli alan'.",
|
|
163
182
|
inputSchema: { project: projectParam, environment: environmentParam, datasourceId: guid.optional().describe('Verilmezse gateway datasource ağacından bulur'), id: entityRef, operations: patchOperationsParam.optional(), ops: patchOperationsParam.optional().describe("'operations' ile aynı alan (eşanlamlı) — sayfa micro-op tool'larıyla aynı adı kullanabilmeniz için"), policyOverride: policyOverrideParam },
|
|
164
183
|
annotations: updates
|
|
165
184
|
}, handle(config, async ({ project, environment, datasourceId, id, operations: givenOperations, ops, policyOverride }) => {
|
|
@@ -171,6 +190,10 @@ export function registerEntityTools(server, platform) {
|
|
|
171
190
|
const resolvedOps = resolvePatchOperations(definition, operations, 'entity_get');
|
|
172
191
|
// Politika, yazmadan önce sonucu görmeli; yerel kopya YALNIZ değerlendirme için, gövde olarak gönderilmez.
|
|
173
192
|
const preview = fastJsonPatch.applyPatch(definition, resolvedOps.operations, true, false).newDocument;
|
|
193
|
+
const protectionChanges = changedExistingProtections(current?.data ?? current, preview);
|
|
194
|
+
if (protectionChanges.length > 0) {
|
|
195
|
+
throw new PlatformApiError(`entity_patch: var olan alanın korumasını değiştiriyor (${protectionChanges.join(', ')}) — platform bunu 400 ProtectionChangeNotAllowed ile reddeder, çünkü dolu satırlar arka plan işiyle dönüştürülmelidir. entity_protection_update { entityId, field: '${protectionChanges[0]}', protection } kullanın; istek gönderilmedi. Yeni bir alanı korumayla eklemek entity_patch ile serbesttir.`, 400);
|
|
196
|
+
}
|
|
174
197
|
const findings = [
|
|
175
198
|
...enforcePolicy(evaluatePolicy(config, policyRules, 'entity', preview, { operation: 'update' }, policyOverride)),
|
|
176
199
|
...inPlaceIndexEdits(current?.data ?? current, preview)
|
|
@@ -195,6 +218,48 @@ export function registerEntityTools(server, platform) {
|
|
|
195
218
|
outline: entityOutline(written.data)
|
|
196
219
|
}, resolver.resolved), drift), findings);
|
|
197
220
|
}));
|
|
221
|
+
server.registerTool('entity_protection_update', {
|
|
222
|
+
title: 'Alan korumasını değiştir',
|
|
223
|
+
description: "VAR OLAN bir alanın koruma modunu değiştirir (none · masked · encrypted · masked-encrypted): platform gizli kolonları ekler ve dolu satırları arka plan işiyle dönüştürür — yanıt 202 + jobId; ilerleme import_status { jobId } (aynı DataJobs ucu, tür protection-apply / protection-remove). entity_update / entity_patch var olan alanın korumasını DEĞİŞTİREMEZ (400 ProtectionChangeNotAllowed). İş sürerken aynı entity'ye yazım 409; iş hata verirse AYNI çağrı kaldığı yerden sürdürür. PROT-003 istek gitmeden uygulanır; kurallar platform_guide('access') §'Alan koruması'.",
|
|
224
|
+
inputSchema: {
|
|
225
|
+
project: projectParam,
|
|
226
|
+
environment: environmentParam,
|
|
227
|
+
entityId: entityRef,
|
|
228
|
+
field: z.string().min(1).describe("Alanın id'si (GUID) ya da slug'ı"),
|
|
229
|
+
protection: z.object({
|
|
230
|
+
mode: z.enum(['none', 'masked', 'encrypted', 'masked-encrypted']).describe("'none' korumayı kaldırır"),
|
|
231
|
+
mask: z.object({
|
|
232
|
+
type: z.enum(protectionMaskTypes()),
|
|
233
|
+
visible: z.number().int().min(0).optional().describe('last-n / first-n için > 0; phone varsayılanı 2 hane; first-n-last-n\'de baştan görünen karakter sayısı (> 0)'),
|
|
234
|
+
visibleEnd: z.number().int().min(0).optional().describe('Yalnız first-n-last-n: sondan görünen karakter sayısı (> 0) — 10000000146, visible 3 + visibleEnd 2 → 100******46'),
|
|
235
|
+
char: z.string().length(1).optional().describe("Varsayılan '*'; phone maskesinde ayırıcı ya da boşluk (+-()./) olamaz"),
|
|
236
|
+
pattern: z.string().optional().describe("pattern türünde: '#' açık karakter, diğerleri olduğu gibi yazılır")
|
|
237
|
+
}).optional().describe('masked ve masked-encrypted modunda zorunlu'),
|
|
238
|
+
exactSearch: z.boolean().optional().describe('Şifreli modda tam eşleşme araması için blind index (HMAC) kolonu'),
|
|
239
|
+
normalize: z.enum(['none', 'digits', 'lower', 'trim']).optional().describe('Eşleşme ve blind index öncesi değeri normalleştirir (TCKN için digits)')
|
|
240
|
+
}),
|
|
241
|
+
policyOverride: z.boolean().optional().describe('true: politika block bulgularını warn\'a indirir (bilinçli istisna; yanıtta işaretlenir)')
|
|
242
|
+
},
|
|
243
|
+
annotations: updates
|
|
244
|
+
}, handle(config, async ({ project, environment, entityId, field, protection, policyOverride }) => {
|
|
245
|
+
const resolver = new RefResolver(platform, project, environment);
|
|
246
|
+
const entity = await resolver.entity(entityId);
|
|
247
|
+
const record = await resolver.entityRecord(entity);
|
|
248
|
+
const fields = (Array.isArray(record.fields) ? record.fields : []);
|
|
249
|
+
const target = fields.find(entry => entry.id === field || entry.slug === field);
|
|
250
|
+
if (!target) {
|
|
251
|
+
throw new PlatformApiError(`entity_protection_update: '${field}' alanı '${entity.slug}' entity'sinde yok. Alanları entity_get view:'outline' ile görün.`, 404);
|
|
252
|
+
}
|
|
253
|
+
const findings = config.policyMode === 'off' ? [] : enforcePolicy(applyPolicyMode(config, fieldProtectionFindings([{ ...target, protection }]), policyOverride));
|
|
254
|
+
const payload = await platform.tenant('POST', project, `/entities/${entity.entityId}/fields/${String(target.id)}/protection`, { environment, body: { protection } });
|
|
255
|
+
const jobId = payload?.data?.jobId ?? payload?.data?.dataJobId;
|
|
256
|
+
return attachFindings(withResolved({
|
|
257
|
+
jobId,
|
|
258
|
+
field: target.slug,
|
|
259
|
+
mode: protection.mode,
|
|
260
|
+
note: `Koruma işi kuyruğa alındı. İlerleme: import_status { jobId: '${String(jobId)}' }. İş bitene kadar '${entity.slug}' entity'sine yazım 409 döner ve okumalar geçiş durumundadır (işlenmiş satırlar maskeli, işlenmemişler düz). Anahtar yapılandırılmamış ortamda iş ProtectionKeyUnavailable ile düşer.`
|
|
261
|
+
}, resolver.resolved), findings);
|
|
262
|
+
}));
|
|
198
263
|
server.registerTool('entity_delete', {
|
|
199
264
|
title: 'Entity sil',
|
|
200
265
|
description: 'Entity tanımını siler; önce entity_dependencies ile bağımlılıklarını kontrol edin.',
|
|
@@ -237,13 +302,15 @@ export function registerEntityTools(server, platform) {
|
|
|
237
302
|
}, handle(config, ({ project, environment, id }) => platform.tenant('GET', project, `/entities/${id}/dependencies`, { environment })));
|
|
238
303
|
server.registerTool('entity_audit_trails', {
|
|
239
304
|
title: 'Audit kayıtları',
|
|
240
|
-
description: 'Entity\'nin satır bazlı audit trail kayıtlarını listeler (audit trail açık olmalı).',
|
|
305
|
+
description: 'Entity\'nin satır bazlı audit trail kayıtlarını listeler (audit trail açık olmalı). `source: CONNECT_DATA` bağlı bileşenden (connectData ucu) gelen yazmaları süzer.',
|
|
241
306
|
inputSchema: {
|
|
242
307
|
project: projectParam,
|
|
243
308
|
environment: environmentParam,
|
|
244
309
|
entityId: guid,
|
|
245
310
|
rowId: z.string().optional(),
|
|
246
311
|
userId: z.string().optional(),
|
|
312
|
+
action: z.enum(['CREATE', 'UPDATE', 'DELETE', 'REVEAL', 'UNRESTRICTED-QUERY']).optional(),
|
|
313
|
+
source: z.enum(['PLATFORM', 'APPLICATION', 'CONNECT_DATA']).optional(),
|
|
247
314
|
...pagingParams
|
|
248
315
|
},
|
|
249
316
|
annotations: readOnly
|
|
@@ -4,7 +4,7 @@ import { handle } from '../platform/responses.js';
|
|
|
4
4
|
import { z } from 'zod';
|
|
5
5
|
/** Aggregate kuralının aynası (`Environment` ctor); gerçek koruma backend'dedir. Kural `common.ts`'te. */
|
|
6
6
|
const branchName = z.string().regex(branchNamePattern)
|
|
7
|
-
.describe("Dal adı: küçük harfle başlar
|
|
7
|
+
.describe("Dal adı: küçük harfle başlar; küçük harf/rakam/alt çizgi/tire ve '/' ile ayrılmış parçalar (ör. feature/odeme), 3-31 karakter. Bu ad PG şema adı olur ve değişmez; adres parçası (slug) '/' yerine '-' taşır.");
|
|
8
8
|
function assertBranchName(name) {
|
|
9
9
|
if (isReservedEnvironmentName(name)) {
|
|
10
10
|
throw new PlatformApiError(`environment_create: '${name}' rezerve bir şema adı — dal adı olarak kullanılamaz. Başka bir ad seçin.`, 400);
|
|
@@ -7,6 +7,23 @@ 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). Katalogda karşılığı olmayan küçük görünüm için geçici yol 'html' bileşenidir: içerik korumalı belgede tema değişkenleriyle çizilir, fireEvent/ngApps.emit ile akışa bağlanır (component_get('html'))."
|
|
25
|
+
})}`, 400);
|
|
26
|
+
}
|
|
10
27
|
/** Platformun köprü protokolü; scaffold ve publish bu sürüme göre üretir/doğrular (backend ExternalComponentRules.CurrentProtocolVersion) */
|
|
11
28
|
const PROTOCOL_VERSION = EXTERNAL_COMPONENT_PROTOCOL_VERSION;
|
|
12
29
|
/** Backend `ExternalComponentRules.MaxBundleSizeBytes` ile aynı sınır */
|
|
@@ -199,6 +216,7 @@ export function registerExternalComponentTools(server, platform) {
|
|
|
199
216
|
},
|
|
200
217
|
annotations: writes
|
|
201
218
|
}, handle(config, async ({ project, environment, applicationId, bundlePath, manifestPath, version }) => {
|
|
219
|
+
await assertExternalComponentsAllowed(platform, applicationId, 'external_component_publish');
|
|
202
220
|
const manifestFile = resolve(manifestPath);
|
|
203
221
|
const bundleFile = resolve(bundlePath);
|
|
204
222
|
let manifest;
|
|
@@ -224,7 +242,7 @@ export function registerExternalComponentTools(server, platform) {
|
|
|
224
242
|
}
|
|
225
243
|
// EXT-001: protocolVersion, definition.blocks, ayrılmış prop adları ve prop tipleri — kayıt
|
|
226
244
|
// ucu 422 dönmeden manifest'te yakalanır; paket yükleme ikinci istek olduğu için yarım kayıt kalmaz.
|
|
227
|
-
enforcePolicy(evaluatePolicy(config, policyRules, 'externalComponent', { ...manifest, slug }, { operation: 'create' }));
|
|
245
|
+
enforcePolicy(evaluatePolicy(config, policyRules, 'externalComponent', { ...manifest, slug }, { operation: 'create' }), false);
|
|
228
246
|
const record = await platform.tenant('POST', project, `/components/${applicationId}/external`, {
|
|
229
247
|
environment,
|
|
230
248
|
body: {
|
|
@@ -262,10 +280,13 @@ export function registerExternalComponentTools(server, platform) {
|
|
|
262
280
|
acknowledgeWarnings: z.boolean().optional().describe('Doğrulama uyarılarını okuyup kabul ettiğinizi bildirir; uyarısı olan sürümlerde zorunlu')
|
|
263
281
|
},
|
|
264
282
|
annotations: updates
|
|
265
|
-
}, handle(config, ({ project, environment, applicationId, slug, version, acknowledgeWarnings }) =>
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
283
|
+
}, handle(config, async ({ project, environment, applicationId, slug, version, acknowledgeWarnings }) => {
|
|
284
|
+
await assertExternalComponentsAllowed(platform, applicationId, 'external_component_activate');
|
|
285
|
+
return platform.tenant('POST', project, `/components/${applicationId}/external/${slug}/activate`, {
|
|
286
|
+
environment,
|
|
287
|
+
body: { version, acknowledgeWarnings: acknowledgeWarnings === true }
|
|
288
|
+
});
|
|
289
|
+
}));
|
|
269
290
|
server.registerTool('external_component_versions', {
|
|
270
291
|
title: 'Paket sürümlerini listele',
|
|
271
292
|
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
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { fetchHost, flowPath, hostParam, unwrap } from './screens.js';
|
|
2
2
|
import { guid, projectParam, environmentParam, readOnly, writes, updates, destructive, microOps } from './common.js';
|
|
3
|
-
import { evaluatePolicy, enforcePolicy, attachFindings } from '../knowledge/policy.js';
|
|
3
|
+
import { applyPolicyMode, evaluatePolicy, enforcePolicy, attachFindings } from '../knowledge/policy.js';
|
|
4
|
+
import { connectedSetterFindings } from '../knowledge/connectDataDefinition.js';
|
|
4
5
|
import { flowBindings, normalizeFlowSteps, targetlessSetterWarnings, unknownSetterTargetWarnings, withNormalization } from '../knowledge/normalize.js';
|
|
5
6
|
import { flowBodyWarnings } from '../knowledge/coverage.js';
|
|
6
7
|
import { PlatformApiError } from '../platform/errors.js';
|
|
@@ -13,6 +14,8 @@ import { policyRules, flowExpressionSites } from '../knowledge/rules.js';
|
|
|
13
14
|
import { functionCallRefs } from '../knowledge/functionGuards.js';
|
|
14
15
|
import { existingMatch, ifExistsParam, platformRename, withRename } from '../platform/ifExists.js';
|
|
15
16
|
import { randomUUID } from 'node:crypto';
|
|
17
|
+
import { flowProfileWarnings } from './profileWrite.js';
|
|
18
|
+
import { jsonArray } from '../platform/jsonArray.js';
|
|
16
19
|
import { z } from 'zod';
|
|
17
20
|
/**
|
|
18
21
|
* Akış yazması yanıtında gövdeyi geri döndürmüyor, bu yüzden diff için akışı bir kez okuruz.
|
|
@@ -34,9 +37,9 @@ async function flowWriteDrops(platform, host, project, environment, applicationI
|
|
|
34
37
|
return emptyWriteDiff();
|
|
35
38
|
}
|
|
36
39
|
}
|
|
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')).");
|
|
40
|
+
const stepsParam = jsonArray(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')).");
|
|
38
41
|
const policyOverrideParam = z.boolean().optional();
|
|
39
|
-
const patchOperationsParam = z.array(z.record(z.unknown())).min(1).describe("RFC 6902 JSON Patch işlemleri; yolları flow_get çıktısıyla keşfedin. Dizi öğelerinde indeks yerine seçici kullanılabilir: '/steps[stepId=paint]/args/iteratees[element=tbl_recruiters]/mapping/source' — seçici gövde platforma gitmeden indekse çevrilir, çözülemezse istek hiç gönderilmez.");
|
|
42
|
+
const patchOperationsParam = jsonArray(z.array(z.record(z.unknown())).min(1)).describe("RFC 6902 JSON Patch işlemleri; yolları flow_get çıktısıyla keşfedin. Dizi öğelerinde indeks yerine seçici kullanılabilir: '/steps[stepId=paint]/args/iteratees[element=tbl_recruiters]/mapping/source' — seçici gövde platforma gitmeden indekse çevrilir, çözülemezse istek hiç gönderilmez.");
|
|
40
43
|
const flowIdParam = guid.describe("Akış id'si; şemadaki events[].value bu id'dir");
|
|
41
44
|
function flowSummary(flow) {
|
|
42
45
|
return {
|
|
@@ -56,6 +59,27 @@ function hostNodes(record) {
|
|
|
56
59
|
return record.schema ?? record.views ?? [];
|
|
57
60
|
}
|
|
58
61
|
const BINDINGS_UNAVAILABLE = 'Barındırıcı kaydı okunamadığı için olay bağları hesaplanamadı; akış listesi yine de günceldir.';
|
|
62
|
+
/**
|
|
63
|
+
* TB-126 / S2-36 — satır aksiyonu taşıyabilen komponentler. Bu tiplerin `actions` prop'unu çoğu
|
|
64
|
+
* zaman YÜKLEME AKIŞI çalışma zamanında yazar (aksiyon role göre eklenip çıkarılıyorsa başka yolu
|
|
65
|
+
* da yoktur), yani bağ statik şemada GÖRÜNMEZ. Böyle bir sayfada "bağsız akışları temizle" refleksi
|
|
66
|
+
* canlı aksiyonu siler; `unboundFlowIds` bu yüzden dürüstçe işaretlenir.
|
|
67
|
+
*/
|
|
68
|
+
const RUNTIME_ACTION_TYPES = new Set(['table', 'datatable']);
|
|
69
|
+
const RUNTIME_BINDING_NOTE = 'Bu barındırıcıda satır aksiyonu taşıyan bir tablo var: satır aksiyonları çalışma zamanında yazılabilir, '
|
|
70
|
+
+ 'silmeden önce doğrulayın — unboundFlowIds statik şemadan hesaplanır ve çalışma zamanı bağını göremez.';
|
|
71
|
+
function hasRuntimeActionHost(nodes) {
|
|
72
|
+
for (const node of Array.isArray(nodes) ? nodes : []) {
|
|
73
|
+
if (!node || typeof node !== 'object')
|
|
74
|
+
continue;
|
|
75
|
+
const record = node;
|
|
76
|
+
if (typeof record.type === 'string' && RUNTIME_ACTION_TYPES.has(record.type))
|
|
77
|
+
return true;
|
|
78
|
+
if (hasRuntimeActionHost(record.children))
|
|
79
|
+
return true;
|
|
80
|
+
}
|
|
81
|
+
return false;
|
|
82
|
+
}
|
|
59
83
|
/**
|
|
60
84
|
* Akış listesine bağları ekler. `danglingBindings` ters yönü söyler: şemada id'si geçen ama
|
|
61
85
|
* barındırıcıda karşılığı olmayan akış — olay tetiklenir, hiçbir şey koşmaz.
|
|
@@ -68,9 +92,12 @@ function flowListResult(flows, record) {
|
|
|
68
92
|
const danglingBindings = [...bindings.entries()]
|
|
69
93
|
.filter(([flowId]) => !known.has(flowId))
|
|
70
94
|
.flatMap(([flowId, entries]) => entries.map(entry => ({ flowId, ...entry })));
|
|
95
|
+
const unboundFlowIds = flows.map(flow => String(flow.id)).filter(flowId => !bindings.has(flowId.toLowerCase()));
|
|
96
|
+
const mayBeBoundAtRuntime = unboundFlowIds.length > 0 && hasRuntimeActionHost(hostNodes(record));
|
|
71
97
|
return {
|
|
72
98
|
flows: flows.map(flow => ({ ...flowSummary(flow), bindings: bindings.get(String(flow.id).toLowerCase()) ?? [] })),
|
|
73
|
-
unboundFlowIds
|
|
99
|
+
unboundFlowIds,
|
|
100
|
+
...(mayBeBoundAtRuntime ? { mayBeBoundAtRuntime: true, mayBeBoundAtRuntimeNote: RUNTIME_BINDING_NOTE } : {}),
|
|
74
101
|
...(danglingBindings.length > 0 ? { danglingBindings } : {})
|
|
75
102
|
};
|
|
76
103
|
}
|
|
@@ -274,7 +301,7 @@ async function functionWarnings(platform, host, project, environment, applicatio
|
|
|
274
301
|
}
|
|
275
302
|
const missing = fn.parameters.filter(parameter => parameter.required && !given.includes(parameter.name)).map(parameter => parameter.name);
|
|
276
303
|
if (missing.length > 0) {
|
|
277
|
-
warnings.add(`FLOW-024: '${label(step)}' adımında '${fn.name}' fonksiyonunun zorunlu parametresi eksik: ${missing.join(', ')} — çağrı 'Functions.${fn.name}: "${missing[0]}" is required' ile düşer, Steps.${String(step.name ?? label(step))}.error dolar ve
|
|
304
|
+
warnings.add(`FLOW-024: '${label(step)}' adımında '${fn.name}' fonksiyonunun zorunlu parametresi eksik: ${missing.join(', ')} — çağrı 'Functions.${fn.name}: "${missing[0]}" is required' ile düşer, Steps.${String(step.name ?? label(step))}.error.message dolar ve .data null'dur; errorSteps yoksa akış 'always' dalından devam eder.`);
|
|
278
305
|
}
|
|
279
306
|
}
|
|
280
307
|
for (const { step, origin, ref } of callSites) {
|
|
@@ -295,11 +322,65 @@ async function functionWarnings(platform, host, project, environment, applicatio
|
|
|
295
322
|
}
|
|
296
323
|
return [...warnings];
|
|
297
324
|
}
|
|
325
|
+
/**
|
|
326
|
+
* FLOW-038 (g-02, ADR 0004 — ZM-07). `microservice.ts` `.finally` üç yoldan birini dener: adımın
|
|
327
|
+
* kendi `error` dalı bağlıysa oradan sürer, yoksa flow `errorSteps`'i (`engine.onError`) devralır,
|
|
328
|
+
* ikisi de yoksa `always` koşar ve storage artık `{ data: null, headers: null, error }`'dır — hata
|
|
329
|
+
* gövdesi ARTIK `data`'da DEĞİL. İkisi de bağlı değilken bu adımın `.data`'sını okuyan bir
|
|
330
|
+
* setter/condition varsa hata anında NULL üzerinden koşar ve yazar hiç fark etmez. Aynı yürüyüş
|
|
331
|
+
* FLOW-024/025'in kullandığı `flowExpressionSites` taramasıdır; setter'ın STORAGE (kod olmayan)
|
|
332
|
+
* referansı ayrıca kendi düz alanından okunur (FLOW-008/009 deseni, `rowsTargetsByStepName`).
|
|
333
|
+
*/
|
|
334
|
+
function microserviceAlwaysDataWarnings(steps, errorSteps) {
|
|
335
|
+
const mainSteps = (Array.isArray(steps) ? steps : []).filter(isRecord);
|
|
336
|
+
const hasErrorFlow = (Array.isArray(errorSteps) ? errorSteps : []).filter(isRecord).some(step => String(step.type ?? '') !== 'start');
|
|
337
|
+
const label = (step) => String(step.stepId ?? step.name ?? '?');
|
|
338
|
+
const escapeRegExp = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
339
|
+
const warnings = new Set();
|
|
340
|
+
for (const step of mainSteps) {
|
|
341
|
+
if (String(step.type ?? '') !== 'microservice')
|
|
342
|
+
continue;
|
|
343
|
+
const name = String(step.name ?? step.stepId ?? '');
|
|
344
|
+
if (name === '')
|
|
345
|
+
continue;
|
|
346
|
+
const hasErrorBranch = (Array.isArray(step.branches) ? step.branches : [])
|
|
347
|
+
.filter(isRecord)
|
|
348
|
+
.some(branch => String(branch.name ?? '') === 'error' && Boolean(branch.nextStepId));
|
|
349
|
+
if (hasErrorBranch || hasErrorFlow)
|
|
350
|
+
continue;
|
|
351
|
+
const pattern = new RegExp(`Steps\\s*(?:\\.\\s*${escapeRegExp(name)}\\b|\\[\\s*['"]${escapeRegExp(name)}['"]\\s*\\])\\s*\\??\\.\\s*data\\b`);
|
|
352
|
+
const message = (other) => `FLOW-038: '${label(other)}' adımı 'always' dalında '${name}.data' okuyor ama '${name}' adımının ne bağlı bir 'error' dalı ne akışın 'errorSteps'i var — uç hata dönerse bu adımın 'data'sı NULL olur (eskiden hata gövdesiydi, ADR 0004) ve '${label(other)}' boş/null veriyle koşar. '${name}' adımına 'error' dalı bağlayın ya da flow'a errorSteps verin.`;
|
|
353
|
+
for (const other of mainSteps) {
|
|
354
|
+
if (other === step)
|
|
355
|
+
continue;
|
|
356
|
+
if (String(other.type ?? '') === 'setter') {
|
|
357
|
+
const iteratees = Array.isArray(other.args?.iteratees)
|
|
358
|
+
? other.args.iteratees.filter(isRecord)
|
|
359
|
+
: [];
|
|
360
|
+
for (const iteratee of iteratees) {
|
|
361
|
+
const mapping = (iteratee.mapping ?? {});
|
|
362
|
+
const value = (iteratee.value ?? {});
|
|
363
|
+
const reference = typeof mapping.source === 'string' && mapping.source.length > 0
|
|
364
|
+
? mapping.source
|
|
365
|
+
: typeof value.ref === 'string' ? value.ref : '';
|
|
366
|
+
if (value.type === 'storage' && (reference === `${name}.data` || reference.startsWith(`${name}.data.`) || reference.startsWith(`${name}.data[`))) {
|
|
367
|
+
warnings.add(message(other));
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
for (const site of flowExpressionSites(other)) {
|
|
372
|
+
if (pattern.test(site.expression))
|
|
373
|
+
warnings.add(message(other));
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
return [...warnings];
|
|
378
|
+
}
|
|
298
379
|
export function registerFlowTools(server, platform) {
|
|
299
380
|
const config = platform.config;
|
|
300
381
|
server.registerTool('flow_list', {
|
|
301
382
|
title: 'Akışları listele',
|
|
302
|
-
description: "Barındırıcının olay akışlarını, her birine bağlı olaylarla (bindings: [{ target: elementId | 'page', event }]) listeler. unboundFlowIds hiçbir olaya bağlı olmayan (sahipsiz) akışlardır; danglingBindings şemada id'si geçen ama barındırıcıda olmayan akışlardır. Sayfa ve layout akışları önbelleksiz liste ucundan, widget akışları kaydın kendi gövdesinden okunur; bağlar barındırıcının şemasından hesaplanır.",
|
|
383
|
+
description: "Barındırıcının olay akışlarını, her birine bağlı olaylarla (bindings: [{ target: elementId | 'page', event }]) listeler. unboundFlowIds hiçbir olaya bağlı olmayan (sahipsiz) akışlardır; danglingBindings şemada id'si geçen ama barındırıcıda olmayan akışlardır. Yanıtta mayBeBoundAtRuntime: true varsa sayfada table/datatable vardır ve satır aksiyonları (actions[].flow) çalışma zamanında yazılmış olabilir: unboundFlowIds o hâlde KESİN DEĞİLDİR, silmeden önce doğrulayın (TB-126). Sayfa ve layout akışları önbelleksiz liste ucundan, widget akışları kaydın kendi gövdesinden okunur; bağlar barındırıcının şemasından hesaplanır.",
|
|
303
384
|
inputSchema: { project: projectParam, environment: environmentParam, host: hostParam, applicationId: guid, hostId: guid },
|
|
304
385
|
annotations: readOnly
|
|
305
386
|
}, handle(config, async ({ project, environment, host, applicationId, hostId }) => {
|
|
@@ -361,14 +442,18 @@ export function registerFlowTools(server, platform) {
|
|
|
361
442
|
...(host === 'page' ? { pageId: hostId } : host === 'layout' ? { layoutId: hostId } : { componentId: hostId })
|
|
362
443
|
};
|
|
363
444
|
const normalized = [...normalizeFlowSteps(flow.steps), ...normalizeFlowSteps(flow.errorSteps)];
|
|
364
|
-
const findings =
|
|
445
|
+
const findings = [
|
|
446
|
+
...enforcePolicy(evaluatePolicy(config, policyRules, 'flow', flow, { operation: 'create' }, policyOverride)),
|
|
447
|
+
...enforcePolicy(config.policyMode === 'off' ? [] : applyPolicyMode(config, connectedSetterFindings(steps, errorSteps ?? [], hostNodes(hostRecord)), policyOverride))
|
|
448
|
+
];
|
|
365
449
|
const existing = await existingFlowMatch(platform, host, project, environment, applicationId, hostId, hostRecord, name, slug, flowId, ifExists);
|
|
366
450
|
const writtenFlowId = existing === undefined ? resolvedFlowId : String(existing.id);
|
|
367
451
|
const parameterWarnings = [
|
|
368
452
|
...await requiredParameterWarnings(platform, project, environment, applicationId, steps),
|
|
369
453
|
...await functionWarnings(platform, host, project, environment, applicationId, hostId, steps),
|
|
370
454
|
...flowBodyWarnings(steps),
|
|
371
|
-
...unknownSetterTargetWarnings(steps, errorSteps ?? [], hostNodes(hostRecord))
|
|
455
|
+
...unknownSetterTargetWarnings(steps, errorSteps ?? [], hostNodes(hostRecord)),
|
|
456
|
+
...microserviceAlwaysDataWarnings(steps, errorSteps ?? [])
|
|
372
457
|
];
|
|
373
458
|
// Gövde yalnız akıştır (designer-partial-update K1): barındırıcı zarfı platformda hâlâ isteğe bağlı
|
|
374
459
|
// ama gönderilirse okunan tam kaydı yeniden yazar, açık Studio designer'ının kayıtlı tabanını
|
|
@@ -382,6 +467,7 @@ export function registerFlowTools(server, platform) {
|
|
|
382
467
|
// Diff YALNIZ çağıranın yazdığı alanları kapsar; `id` ve barındırıcı bağı (`pageId` vb.)
|
|
383
468
|
// gövdeye gateway koyar, kullanıcının düzeltebileceği bir şey değildir.
|
|
384
469
|
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');
|
|
470
|
+
const platformWarnings = await flowProfileWarnings(platform, applicationId, [...(flow.steps ?? []), ...(flow.errorSteps ?? [])]);
|
|
385
471
|
return attachFindings(withWriteDiff(withRename(withNormalization({
|
|
386
472
|
host,
|
|
387
473
|
hostId,
|
|
@@ -393,6 +479,7 @@ export function registerFlowTools(server, platform) {
|
|
|
393
479
|
steps: steps.length
|
|
394
480
|
},
|
|
395
481
|
...(parameterWarnings.length > 0 ? { parameterWarnings } : {}),
|
|
482
|
+
...(platformWarnings.length > 0 ? { platformWarnings } : {}),
|
|
396
483
|
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
484
|
}, normalized), drops.renamed), drops), findings);
|
|
398
485
|
}));
|
|
@@ -434,6 +521,7 @@ export function registerFlowTools(server, platform) {
|
|
|
434
521
|
const findings = steps || errorSteps
|
|
435
522
|
? [
|
|
436
523
|
...enforcePolicy(evaluatePolicy(config, policyRules, 'flow', authored, { operation: 'update' }, policyOverride)),
|
|
524
|
+
...enforcePolicy(config.policyMode === 'off' ? [] : applyPolicyMode(config, connectedSetterFindings(authored.steps, authored.errorSteps, hostNodes(hostRecord)), policyOverride)),
|
|
437
525
|
...enforcePolicy(evaluatePolicy(config, policyRules, 'flow', flow, { operation: 'update' }, policyOverride).filter(finding => finding.ruleId === 'FLOW-027'))
|
|
438
526
|
]
|
|
439
527
|
: [];
|
|
@@ -441,7 +529,8 @@ export function registerFlowTools(server, platform) {
|
|
|
441
529
|
...await requiredParameterWarnings(platform, project, environment, applicationId, flow.steps),
|
|
442
530
|
...await functionWarnings(platform, host, project, environment, applicationId, hostId, flow.steps),
|
|
443
531
|
...flowBodyWarnings(flow.steps),
|
|
444
|
-
...unknownSetterTargetWarnings(flow.steps, flow.errorSteps, hostNodes(hostRecord))
|
|
532
|
+
...unknownSetterTargetWarnings(flow.steps, flow.errorSteps, hostNodes(hostRecord)),
|
|
533
|
+
...microserviceAlwaysDataWarnings(flow.steps, flow.errorSteps)
|
|
445
534
|
];
|
|
446
535
|
await platform.tenant('PUT', project, flowPath(host, applicationId, hostId, flowId), { environment, body: { flow } });
|
|
447
536
|
// Kısmi güncellemede yalnız GÖNDERİLEN alanlar karşılaştırılır; verilmeyen alan için
|
|
@@ -452,7 +541,9 @@ export function registerFlowTools(server, platform) {
|
|
|
452
541
|
...(steps === undefined ? {} : { steps }),
|
|
453
542
|
...(errorSteps === undefined ? {} : { errorSteps })
|
|
454
543
|
});
|
|
455
|
-
|
|
544
|
+
const platformWarnings = await flowProfileWarnings(platform, applicationId, [...(flow.steps ?? []), ...(flow.errorSteps ?? [])]);
|
|
545
|
+
return attachFindings(withWriteDiff(withNormalization({ host, hostId, flow: flowSummary(flow), ...(parameterWarnings.length > 0 ? { parameterWarnings } : {}),
|
|
546
|
+
...(platformWarnings.length > 0 ? { platformWarnings } : {}) }, normalized), drops), findings);
|
|
456
547
|
}));
|
|
457
548
|
server.registerTool('flow_patch', {
|
|
458
549
|
title: "Akışa patch uygula",
|
|
@@ -486,12 +577,16 @@ export function registerFlowTools(server, platform) {
|
|
|
486
577
|
...(host === 'page' ? { pageId: hostId } : host === 'layout' ? { layoutId: hostId } : { componentId: hostId })
|
|
487
578
|
};
|
|
488
579
|
const normalized = [...normalizeFlowSteps(flow.steps), ...normalizeFlowSteps(flow.errorSteps)];
|
|
489
|
-
const findings =
|
|
580
|
+
const findings = [
|
|
581
|
+
...enforcePolicy(evaluatePolicy(config, policyRules, 'flow', flow, { operation: 'update' }, policyOverride)),
|
|
582
|
+
...enforcePolicy(config.policyMode === 'off' ? [] : applyPolicyMode(config, connectedSetterFindings(flow.steps, flow.errorSteps, hostNodes(hostRecord)), policyOverride))
|
|
583
|
+
];
|
|
490
584
|
const parameterWarnings = [
|
|
491
585
|
...await requiredParameterWarnings(platform, project, environment, applicationId, flow.steps),
|
|
492
586
|
...await functionWarnings(platform, host, project, environment, applicationId, hostId, flow.steps),
|
|
493
587
|
...flowBodyWarnings(flow.steps),
|
|
494
|
-
...unknownSetterTargetWarnings(flow.steps, flow.errorSteps, hostNodes(hostRecord))
|
|
588
|
+
...unknownSetterTargetWarnings(flow.steps, flow.errorSteps, hostNodes(hostRecord)),
|
|
589
|
+
...microserviceAlwaysDataWarnings(flow.steps, flow.errorSteps)
|
|
495
590
|
];
|
|
496
591
|
// Gövde değil FARK gönderilir; platform PATCH ucu akışı tek başına yazar. Net diff okunan kayıt ile son hâl arasından hesaplanır.
|
|
497
592
|
const netOps = fastJsonPatch.compare(current, flow);
|
|
@@ -501,6 +596,7 @@ export function registerFlowTools(server, platform) {
|
|
|
501
596
|
});
|
|
502
597
|
const written = patchResult(saved, flow);
|
|
503
598
|
const drops = await flowWriteDrops(platform, host, project, environment, applicationId, hostId, flowId, { steps: flow.steps, errorSteps: flow.errorSteps });
|
|
599
|
+
const platformWarnings = await flowProfileWarnings(platform, applicationId, [...(flow.steps ?? []), ...(flow.errorSteps ?? [])]);
|
|
504
600
|
return attachFindings(withWriteDiff(withNormalization({
|
|
505
601
|
host,
|
|
506
602
|
hostId,
|
|
@@ -508,7 +604,8 @@ export function registerFlowTools(server, platform) {
|
|
|
508
604
|
newVersion: written.newVersion,
|
|
509
605
|
patchTargets: resolved.targets,
|
|
510
606
|
flow: flowSummary(written.data),
|
|
511
|
-
...(parameterWarnings.length > 0 ? { parameterWarnings } : {})
|
|
607
|
+
...(parameterWarnings.length > 0 ? { parameterWarnings } : {}),
|
|
608
|
+
...(platformWarnings.length > 0 ? { platformWarnings } : {})
|
|
512
609
|
}, [...normalized, ...resolved.notes.map(note => `patch yolu seçiciden indekse çözüldü: ${note}`)]), drops), findings);
|
|
513
610
|
}));
|
|
514
611
|
server.registerTool('flow_delete', {
|
|
@@ -520,4 +617,13 @@ export function registerFlowTools(server, platform) {
|
|
|
520
617
|
await platform.tenant('DELETE', project, flowPath(host, applicationId, hostId, flowId), { environment, body: { id: flowId } });
|
|
521
618
|
return { deleted: flowId, host, hostId };
|
|
522
619
|
}));
|
|
620
|
+
server.registerTool('flow_restore', {
|
|
621
|
+
title: 'Silinen akışı geri yükle',
|
|
622
|
+
description: "Silinmiş bir akışı revizyon defterindeki son içeriğiyle (ad, slug, adımlar, barındırıcı) AYNI id ile yeniden kurar. Sayfa kaydı bağı kalmayan akışı kalıcı siler (FLOW-002, allowFlowDeletion) — Studio'nun eski sekmesi boş şema kaydettiğinde de (ZM-27). Akışı olaya YENİDEN BAĞLAMAZ: geri geldikten sonra set_event ile bağlayın. SINIR: adımları sayfanın bir komponentini hedefliyorsa (tetikleyen element, iterate/parametre komponenti) o komponent de şemada olmalıdır, yoksa geri yükleme reddedilir — sayfa boşaldıysa (ZM-27) page_restore kullanın: sayfayı ve akışlarını birlikte geri kurar. Akış hâlâ varsa 409, saklama süresi dolup revizyonu budanmışsa 404 döner. Silinen akışı revision_list(module: 'flow') satırlarında operation: delete olarak bulun; recordId akış id'sidir.",
|
|
623
|
+
inputSchema: { project: projectParam, environment: environmentParam, flowId: flowIdParam },
|
|
624
|
+
annotations: writes
|
|
625
|
+
}, handle(config, async ({ project, environment, flowId }) => {
|
|
626
|
+
const restored = unwrap(await platform.tenant('POST', project, `/flows/${flowId}/restore`, { environment }));
|
|
627
|
+
return { restored: flowId, flow: restored };
|
|
628
|
+
}));
|
|
523
629
|
}
|
package/dist/tools/folders.js
CHANGED
|
@@ -23,7 +23,7 @@ export function registerFolderTools(server, platform) {
|
|
|
23
23
|
inputSchema: { project: projectParam, environment: environmentParam, definition: definitionParam },
|
|
24
24
|
annotations: writes
|
|
25
25
|
}, handle(config, async ({ project, environment, definition }) => {
|
|
26
|
-
const findings = enforcePolicy(evaluatePolicy(config, policyRules, 'folder', definition, { operation: 'create' }));
|
|
26
|
+
const findings = enforcePolicy(evaluatePolicy(config, policyRules, 'folder', definition, { operation: 'create' }), false);
|
|
27
27
|
return attachFindings(await platform.tenant('POST', project, '/folders', { environment, body: definition }), findings);
|
|
28
28
|
}));
|
|
29
29
|
server.registerTool('folder_update', {
|
package/dist/tools/imports.js
CHANGED
|
@@ -182,7 +182,10 @@ export function registerImportTools(server, platform) {
|
|
|
182
182
|
return { ...payload, note: "Plan henüz çalıştırılmadı: import_run { datasourceId, planId } ile başlatın — planId bu jobId'dir ve plan bir saat geçerlidir." };
|
|
183
183
|
}
|
|
184
184
|
if (status === 'failed') {
|
|
185
|
-
|
|
185
|
+
// Koruma işi (entity_protection_update) düşerse alan geçişte kalır ve entity yazımı 409'a kilitlenir;
|
|
186
|
+
// otomatik yeniden deneme yok, kurtarma aynı isteği aynı gövdeyle tekrarlamaktır (TB-31).
|
|
187
|
+
const isProtection = job?.kind === 'protection-apply' || job?.kind === 'protection-remove';
|
|
188
|
+
return { ...payload, note: `İş düştü; \`error\` alanı nedeni söyler.${isImport ? ' Yazılmış partiler yerinde kalır; bugün geri alınamaz.' : ''}${isProtection ? " Koruma işi: alan geçiş durumunda kalır ve entity'ye yazım 409 döner. Nedeni giderdikten sonra AYNI entity_protection_update çağrısını AYNI gövdeyle tekrarlayın — iş kaldığı yerden sürer (TB-31)." : ''}` };
|
|
186
189
|
}
|
|
187
190
|
if (status === 'cancelled') {
|
|
188
191
|
return { ...payload, note: `İş iptal edildi.${isImport ? ' Yazılmış partiler yerinde kalır; bugün geri alınamaz.' : ''}` };
|
|
@@ -191,7 +194,7 @@ export function registerImportTools(server, platform) {
|
|
|
191
194
|
}));
|
|
192
195
|
server.registerTool('import_history', {
|
|
193
196
|
title: 'Veri işleri geçmişi',
|
|
194
|
-
description: "Projedeki içe/dışa aktarma
|
|
197
|
+
description: "Projedeki içe/dışa aktarma, geri alma, alan koruması (protection-*) ve türetilmiş alan doldurma (derived-fill) işlerini sayfalı listeler; order verilmezse en yeni iş önce gelir. Her satır kind, status, entityIds ve result taşır. kind: 'import' çalıştırılmamış planları (planned) da içerir; datasource filtresi yoktur. Geri alınabilir bir iş aramak (kind: 'import', status: 'completed', undoAvailableUntil geçmemiş) ya da düşen bir işin nedenini bulmak için kullanılır.",
|
|
195
198
|
inputSchema: {
|
|
196
199
|
project: projectParam,
|
|
197
200
|
environment: environmentParam,
|