@appaflytech/zeyrek-ai-mcp 0.0.31 → 0.0.32
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 +0 -15
- package/README.md +5 -5
- package/dist/config.js +0 -37
- package/dist/index.js +0 -2
- package/dist/knowledge/backendConstraints.js +1 -18
- package/dist/knowledge/boxSections.js +184 -0
- package/dist/knowledge/componentCatalog.js +233 -568
- package/dist/knowledge/componentEvents.js +21 -141
- package/dist/knowledge/layoutSummary.js +2 -140
- package/dist/knowledge/normalize.js +67 -236
- package/dist/knowledge/outlines.js +0 -36
- package/dist/knowledge/policy.js +4 -18
- package/dist/knowledge/reservedTables.js +0 -79
- package/dist/knowledge/rules.js +21 -366
- package/dist/knowledge/schemaGuard.js +0 -11
- package/dist/knowledge/styleResolve.js +38 -55
- package/dist/knowledge/themeResolve.js +3 -3
- package/dist/observability/trace.js +3 -6
- package/dist/platform/diagnostics.js +4 -10
- package/dist/platform/domSummary.js +1 -1
- package/dist/platform/errors.js +10 -162
- package/dist/platform/http.js +8 -29
- package/dist/platform/responses.js +5 -39
- package/dist/platform/{capture/capture.js → screenshot.js} +519 -105
- package/dist/platform/secrets.js +1 -17
- package/dist/platform/strictArguments.js +12 -68
- package/dist/platform/tokenVault.js +4 -187
- package/dist/resources/examples/layout-app-shell.json +707 -229
- package/dist/resources/examples/middleware-authenticate.json +1 -1
- package/dist/resources/examples/page-detail-profile.json +1 -2
- package/dist/resources/examples/page-dynamic-form.json +2 -3
- 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 +2 -8
- package/dist/resources/guides/component.md +5 -92
- package/dist/resources/guides/entity.md +8 -50
- package/dist/resources/guides/expressions.md +5 -14
- package/dist/resources/guides/functions.md +1 -1
- package/dist/resources/guides/getting-started.md +1 -38
- package/dist/resources/guides/layout.md +58 -55
- package/dist/resources/guides/merge.md +3 -37
- package/dist/resources/guides/microservice.md +2 -87
- package/dist/resources/guides/middleware.md +4 -68
- package/dist/resources/guides/page.md +75 -456
- package/dist/resources/guides/theme.md +2 -8
- package/dist/resources/guides/translation.md +1 -1
- package/dist/resources/guides/workflow.md +1 -58
- package/dist/resources/rules.js +5 -11
- package/dist/resources/schema-fingerprint.json +5 -7
- package/dist/resources/schemas/CreateEntityCommand.json +0 -118
- package/dist/resources/schemas/CreateMicroserviceCommand.json +1 -13
- package/dist/resources/schemas/CreateQueryCommand.json +0 -4
- package/dist/resources/schemas/CreateWorkflowCommand.json +1 -146
- package/dist/resources/schemas/DataJobDto.json +1 -6
- package/dist/resources/schemas/GlobalFunctionUpdateResultDto.json +1 -21
- package/dist/resources/schemas/MicroserviceDto.json +3 -15
- package/dist/resources/schemas/PreviewMicroserviceCommand.json +1 -10
- package/dist/resources/schemas/PreviewWorkflowCommand.json +1 -25
- package/dist/resources/schemas/StepDto.json +1 -25
- package/dist/resources/schemas/UpdateEntityCommand.json +0 -118
- package/dist/resources/schemas/UpdateMicroserviceCommand.json +1 -13
- package/dist/resources/schemas/UpdateQueryCommand.json +0 -4
- package/dist/resources/schemas/UpdateViewCommand.json +2 -0
- package/dist/resources/schemas/UpdateWorkflowCommand.json +1 -146
- package/dist/server.js +1 -27
- package/dist/tools/access.js +2 -369
- package/dist/tools/applications.js +3 -60
- package/dist/tools/common.js +0 -7
- package/dist/tools/components.js +14 -118
- package/dist/tools/datasources.js +3 -3
- package/dist/tools/dependencies.js +1 -1
- package/dist/tools/deployments.js +3 -38
- package/dist/tools/designSchemaPatch.js +18 -34
- package/dist/tools/dtos.js +5 -36
- package/dist/tools/entities.js +12 -79
- package/dist/tools/externalComponents.js +5 -26
- package/dist/tools/flows.js +14 -122
- package/dist/tools/folders.js +1 -1
- package/dist/tools/imports.js +2 -5
- package/dist/tools/layouts.js +17 -50
- package/dist/tools/merges.js +2 -2
- package/dist/tools/microservices.js +25 -77
- package/dist/tools/pages.js +112 -465
- package/dist/tools/platformInfo.js +8 -27
- package/dist/tools/projects.js +9 -71
- package/dist/tools/rows.js +13 -69
- package/dist/tools/schemaOps.js +1 -61
- package/dist/tools/screenWrite.js +8 -37
- package/dist/tools/screens.js +5 -95
- package/dist/tools/templates.js +1 -26
- package/dist/tools/translations.js +1 -1
- package/dist/tools/variables.js +5 -11
- package/dist/tools/workflows.js +5 -10
- package/package.json +1 -1
- package/dist/knowledge/access.js +0 -802
- package/dist/knowledge/case.js +0 -41
- package/dist/knowledge/connectDataBind.js +0 -819
- package/dist/knowledge/connectDataDefinition.js +0 -341
- package/dist/knowledge/connectDataExecute.js +0 -93
- package/dist/knowledge/connectDataRules.js +0 -128
- package/dist/knowledge/decision.js +0 -121
- package/dist/knowledge/expressionPrelude.js +0 -59
- package/dist/knowledge/microserviceParameters.js +0 -58
- package/dist/knowledge/mining.js +0 -94
- package/dist/knowledge/mobileComponentCatalog.js +0 -859
- package/dist/knowledge/mobileComponentEvents.js +0 -239
- package/dist/knowledge/platform/lint.js +0 -385
- package/dist/knowledge/platform/mobile.js +0 -142
- package/dist/knowledge/platform/mobileLayoutLint.js +0 -324
- package/dist/knowledge/platform/mobileRules.js +0 -231
- package/dist/knowledge/platform/mobileSpacing.js +0 -82
- package/dist/knowledge/platform/mobileTabs.js +0 -108
- package/dist/knowledge/platform/nativeBoxModel.js +0 -44
- package/dist/knowledge/platform/profile.js +0 -25
- package/dist/knowledge/platform/resolve.js +0 -122
- package/dist/knowledge/platform/web.js +0 -39
- package/dist/knowledge/process.js +0 -510
- package/dist/knowledge/rowFilter.js +0 -43
- package/dist/knowledge/styleResolveNative.js +0 -527
- package/dist/platform/argumentAliases.js +0 -128
- package/dist/platform/bodyFile.js +0 -103
- package/dist/platform/capture/actions.js +0 -333
- package/dist/platform/capture/browser.js +0 -1
- package/dist/platform/capture/console-log.js +0 -122
- package/dist/platform/capture/index.js +0 -11
- 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/platform/impersonation.js +0 -45
- package/dist/platform/jsonArray.js +0 -24
- package/dist/platform/schemaReadMemory.js +0 -60
- package/dist/resources/examples/bpm-gorev-kontrol.json +0 -294
- package/dist/resources/examples/bpm-iso-dokuman.json +0 -228
- package/dist/resources/examples/bpm-satinalma.json +0 -376
- package/dist/resources/examples/connect-data-convert-verify.json +0 -107
- package/dist/resources/examples/connect-data-dropdown-roles.json +0 -66
- package/dist/resources/examples/connect-data-edit-m2m.json +0 -73
- package/dist/resources/examples/connect-data-query-params.json +0 -50
- package/dist/resources/examples/connect-data-table-entity.json +0 -147
- package/dist/resources/examples/connect-data-workflow-microservice.json +0 -65
- package/dist/resources/examples/mobile-app-shell-drawer.json +0 -243
- package/dist/resources/examples/mobile-app-shell-stack.json +0 -157
- package/dist/resources/examples/mobile-app-shell-tab.json +0 -208
- package/dist/resources/examples/mobile-form-screen.json +0 -365
- package/dist/resources/examples/mobile-list-detail-form.json +0 -672
- package/dist/resources/guides/access.md +0 -810
- package/dist/resources/guides/bpm.md +0 -691
- package/dist/resources/guides/case.md +0 -153
- package/dist/resources/guides/decision.md +0 -185
- package/dist/resources/guides/mobile-layout.md +0 -193
- package/dist/resources/guides/mobile.md +0 -495
- package/dist/resources/read.js +0 -41
- package/dist/resources/schemas/AccessExplanationDto.json +0 -144
- package/dist/resources/schemas/AccessMatrixDto.json +0 -170
- package/dist/resources/schemas/AccessPermissionDto.json +0 -57
- package/dist/resources/schemas/AccessPolicyDto.json +0 -223
- package/dist/resources/schemas/AccessResourceDto.json +0 -86
- package/dist/resources/schemas/AppSessionDto.json +0 -53
- package/dist/resources/schemas/ConnectDataDescribeDto.json +0 -344
- package/dist/resources/schemas/ConnectDataRequestModel.json +0 -202
- package/dist/resources/schemas/ConnectDataResultDto.json +0 -421
- package/dist/resources/schemas/ConnectorCredentialDto.json +0 -70
- package/dist/resources/schemas/ConvertConnectDataToMicroserviceCommand.json +0 -37
- package/dist/resources/schemas/CreateAccessPolicyCommand.json +0 -209
- package/dist/resources/schemas/CreateDecisionCommand.json +0 -221
- package/dist/resources/schemas/CreateProcessDefinitionCommand.json +0 -1337
- package/dist/resources/schemas/DecisionDto.json +0 -302
- package/dist/resources/schemas/DetachConnectDataMicroserviceCommand.json +0 -28
- package/dist/resources/schemas/ExplainAccessQuery.json +0 -33
- package/dist/resources/schemas/GenerateProcessDocumentCommand.json +0 -116
- package/dist/resources/schemas/OrganizationProjectDto.json +0 -335
- package/dist/resources/schemas/OrganizationSchemaDto.json +0 -196
- package/dist/resources/schemas/OrganizationSchemaVersionDto.json +0 -207
- package/dist/resources/schemas/PatchProcessDefinitionCommand.json +0 -76
- package/dist/resources/schemas/ProcessDefinitionDto.json +0 -1480
- package/dist/resources/schemas/ProcessSimulationDto.json +0 -96
- package/dist/resources/schemas/SaveConnectorCredentialCommand.json +0 -90
- package/dist/resources/schemas/SaveProcessEventSourceCommand.json +0 -61
- package/dist/resources/schemas/SaveProcessMailSourceCommand.json +0 -89
- package/dist/resources/schemas/SignProcessTaskCommand.json +0 -73
- package/dist/resources/schemas/SimulateProcessDefinitionQuery.json +0 -92
- package/dist/resources/schemas/UpdateAccessPolicyCommand.json +0 -209
- package/dist/resources/schemas/UpdateDecisionCommand.json +0 -216
- package/dist/resources/schemas/UpdateProcessDefinitionCommand.json +0 -1357
- package/dist/resources/schemas/ValidateProcessDefinitionQuery.json +0 -1339
- package/dist/tools/accessOperations.js +0 -135
- package/dist/tools/accessPolicies.js +0 -170
- package/dist/tools/accessRuntime.js +0 -203
- package/dist/tools/bpm.js +0 -343
- package/dist/tools/bpmGovernance.js +0 -266
- package/dist/tools/bpmOperations.js +0 -230
- package/dist/tools/bpmReporting.js +0 -154
- package/dist/tools/bpmRouting.js +0 -150
- package/dist/tools/bpmRuntime.js +0 -396
- package/dist/tools/case.js +0 -178
- package/dist/tools/caseRuntime.js +0 -89
- package/dist/tools/connectData.js +0 -449
- package/dist/tools/connectors.js +0 -193
- package/dist/tools/decisions.js +0 -206
- package/dist/tools/mobile.js +0 -221
- package/dist/tools/modules.js +0 -48
- package/dist/tools/organizationStructure.js +0 -137
- package/dist/tools/profileWrite.js +0 -92
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
import { guid, projectParam, environmentParam, pagingParams, readOnly, writes } from './common.js';
|
|
2
|
-
import { identityHeaders } from '../platform/tokenVault.js';
|
|
3
|
-
import { PlatformApiError } from '../platform/errors.js';
|
|
4
|
-
import { assertProcessNotProduction } from './bpm.js';
|
|
5
|
-
import { handle } from '../platform/responses.js';
|
|
6
|
-
import { z } from 'zod';
|
|
7
|
-
const asUserParam = z.string().min(1).optional()
|
|
8
|
-
.describe("Uygulama kullanıcısının kimliği (ham JWT ya da access_session_create'in tokenRefs'teki tok_… referansı). Vaka başlatan ve plan öğesini değiştiren bu kişi olur; verilmezse gateway'in kendi kullanıcısı.");
|
|
9
|
-
const expectFailureParam = z.boolean().optional()
|
|
10
|
-
.describe('true: bu çağrıda hata BEKLENİYOR (kasıtlı negatif test: sentry sağlanmadan enable 422). Ölçüm plansız hata saymaz.');
|
|
11
|
-
const planItemOperations = ['enable', 'start', 'complete', 'disable'];
|
|
12
|
-
const caseInstanceOperations = [...planItemOperations, 'add-discretionary', 'update-case-file'];
|
|
13
|
-
export function registerCaseRuntimeTools(server, platform) {
|
|
14
|
-
const config = platform.config;
|
|
15
|
-
server.registerTool('case_execute', {
|
|
16
|
-
title: 'Vaka başlat',
|
|
17
|
-
description: "Yayındaki vaka tanımından vaka başlatır (POST bpm/cases/{slug}/start): subjectRowId iş satırı, caseFile başlangıç değerleri (tanımdaki varsayılanların üstüne yazılır). Yanıt vaka + planItems + availableActions. process.start izni ister. İnsan görevleri görev kutusuna kind 'case-task' olarak düşer ve process_task_update ile tamamlanır. production hedefi durur (BPM-001).",
|
|
18
|
-
inputSchema: {
|
|
19
|
-
project: projectParam,
|
|
20
|
-
environment: environmentParam,
|
|
21
|
-
slug: z.string().min(1).max(100),
|
|
22
|
-
applicationId: guid.optional(),
|
|
23
|
-
subjectRowId: z.string().max(64).optional(),
|
|
24
|
-
caseFile: z.record(z.unknown()).optional().describe('vaka dosyası öğe id → değer'),
|
|
25
|
-
asUser: asUserParam
|
|
26
|
-
},
|
|
27
|
-
annotations: writes
|
|
28
|
-
}, handle(config, async ({ project, environment, slug, applicationId, subjectRowId, caseFile, asUser }) => {
|
|
29
|
-
assertProcessNotProduction('case_execute', environment, config);
|
|
30
|
-
return platform.tenant('POST', project, `/bpm/cases/${encodeURIComponent(slug)}/start`, {
|
|
31
|
-
environment,
|
|
32
|
-
body: { applicationId, subjectRowId, caseFile },
|
|
33
|
-
headers: identityHeaders(asUser, undefined, undefined)
|
|
34
|
-
});
|
|
35
|
-
}));
|
|
36
|
-
server.registerTool('case_instance_list', {
|
|
37
|
-
title: 'Vakaları listele',
|
|
38
|
-
description: "Vakaları listeler (GET bpm/case-instances; process.view-all ister): definitionId ve state (active | completed | terminated) süzgeci. Tek vakanın plan öğeleri, defteri ve availableActions için case_instance_get.",
|
|
39
|
-
inputSchema: {
|
|
40
|
-
project: projectParam,
|
|
41
|
-
environment: environmentParam,
|
|
42
|
-
definitionId: guid.optional(),
|
|
43
|
-
state: z.enum(['active', 'completed', 'terminated']).optional(),
|
|
44
|
-
...pagingParams
|
|
45
|
-
},
|
|
46
|
-
annotations: readOnly
|
|
47
|
-
}, handle(config, ({ project, environment, ...query }) => platform.tenant('GET', project, '/bpm/case-instances', { environment, query })));
|
|
48
|
-
server.registerTool('case_instance_get', {
|
|
49
|
-
title: 'Vakayı getir',
|
|
50
|
-
description: "Vakayı getirir (GET bpm/case-instances/{id}): state, caseFile, planItems[] (id — eylemlerdeki planItemId budur —, itemId, type, state, occurrence, taskId), events[] (vaka defteri) ve availableActions[] { action: start | complete | disable | discretionary, itemId, planItemId }. Katılımcı (başlatan, atanan, tamamlayan) ya da process.view-all görür. case_instance_update'ten önce okuyun: planItemId planItems[].id ya da availableActions[].planItemId'dir.",
|
|
51
|
-
inputSchema: { project: projectParam, environment: environmentParam, id: guid, asUser: asUserParam },
|
|
52
|
-
annotations: readOnly
|
|
53
|
-
}, handle(config, ({ project, environment, id, asUser }) => platform.tenant('GET', project, `/bpm/case-instances/${id}`, { environment, headers: identityHeaders(asUser, undefined, undefined) })));
|
|
54
|
-
server.registerTool('case_instance_update', {
|
|
55
|
-
title: 'Vakada plan öğesi eylemi',
|
|
56
|
-
description: "Vaka eylemlerinin tek kapısı: operation enable | start | complete | disable (planItemId ZORUNLU: case_instance_get planItems[].id ya da availableActions[].planItemId — enable availableActions'ta hiç yoktur, kimliği planItems[].id'den okuyun; POST …/plan-items/{planItemId}/{operation}) | add-discretionary (itemDefinitionId: isteğe bağlı plan öğesinin tanımdaki id'si; POST …/discretionary) | update-case-file (itemId ve value ZORUNLU, silmek için value: null; PUT …/case-file — yerleşimi yeniden çalıştırır, ifPart'lar yeniden değerlendirilir). Hatalar: giriş sentry'si sağlanmadan enable 422 CaseSentryNotSatisfied (lint değil, çalışma zamanı: availableActions enable listelemez); yanlış durumdaki öğe ya da kapanmış vaka 409 InstanceStateTransitionNotAllowed; human-task görev ucundan (process_task_update) tamamlanır. production hedefi durur (BPM-001).",
|
|
57
|
-
inputSchema: {
|
|
58
|
-
project: projectParam,
|
|
59
|
-
environment: environmentParam,
|
|
60
|
-
id: guid,
|
|
61
|
-
operation: z.enum(caseInstanceOperations),
|
|
62
|
-
planItemId: guid.optional().describe('enable | start | complete | disable için case_instance_get planItems[].id (ya da availableActions[].planItemId)'),
|
|
63
|
-
itemDefinitionId: z.string().max(100).optional().describe('add-discretionary için tanımdaki plan öğesi id'),
|
|
64
|
-
itemId: z.string().max(100).optional().describe('update-case-file için vaka dosyası öğe id'),
|
|
65
|
-
value: z.unknown().optional().describe('update-case-file için yeni değer (herhangi bir JSON)'),
|
|
66
|
-
asUser: asUserParam,
|
|
67
|
-
expectFailure: expectFailureParam
|
|
68
|
-
},
|
|
69
|
-
annotations: writes
|
|
70
|
-
}, handle(config, async ({ project, environment, id, operation, planItemId, itemDefinitionId, itemId, value, asUser }) => {
|
|
71
|
-
assertProcessNotProduction('case_instance_update', environment, config);
|
|
72
|
-
const headers = identityHeaders(asUser, undefined, undefined);
|
|
73
|
-
if (operation === 'add-discretionary') {
|
|
74
|
-
if (!itemDefinitionId)
|
|
75
|
-
throw new PlatformApiError("case_instance_update: 'add-discretionary' itemDefinitionId ister (istek gönderilmedi).", 400);
|
|
76
|
-
return platform.tenant('POST', project, `/bpm/case-instances/${id}/discretionary`, { environment, body: { itemDefinitionId }, headers });
|
|
77
|
-
}
|
|
78
|
-
if (operation === 'update-case-file') {
|
|
79
|
-
if (!itemId)
|
|
80
|
-
throw new PlatformApiError("case_instance_update: 'update-case-file' itemId ister (istek gönderilmedi).", 400);
|
|
81
|
-
if (value === undefined)
|
|
82
|
-
throw new PlatformApiError("case_instance_update: 'update-case-file' value ister — verilmeyen değer öğeyi siler; silmek istiyorsanız value: null gönderin (istek gönderilmedi).", 400);
|
|
83
|
-
return platform.tenant('PUT', project, `/bpm/case-instances/${id}/case-file`, { environment, body: { itemId, value }, headers });
|
|
84
|
-
}
|
|
85
|
-
if (!planItemId)
|
|
86
|
-
throw new PlatformApiError(`case_instance_update: '${operation}' planItemId ister — case_instance_get planItems[].id ya da availableActions[].planItemId (istek gönderilmedi).`, 400);
|
|
87
|
-
return platform.tenant('POST', project, `/bpm/case-instances/${id}/plan-items/${planItemId}/${operation}`, { environment, headers });
|
|
88
|
-
}));
|
|
89
|
-
}
|
|
@@ -1,449 +0,0 @@
|
|
|
1
|
-
import { z } from 'zod';
|
|
2
|
-
import { guid, projectParam, environmentParam, asUserIdParam, readOnly, updates, writes } from './common.js';
|
|
3
|
-
import { listComponents } from './components.js';
|
|
4
|
-
import { fetchHost, hostPath } from './screens.js';
|
|
5
|
-
import { applicationSlug } from './microservices.js';
|
|
6
|
-
import { identityHeaders } from '../platform/tokenVault.js';
|
|
7
|
-
import { impersonationHeaders } from '../platform/impersonation.js';
|
|
8
|
-
import { handle } from '../platform/responses.js';
|
|
9
|
-
import { PlatformApiError } from '../platform/errors.js';
|
|
10
|
-
import { applyPolicyMode, enforcePolicy, attachFindings } from '../knowledge/policy.js';
|
|
11
|
-
import { connectDataOpIssues, CONNECT_DATA_OPERATORS, CONNECT_DATA_SOURCE_KINDS, selectedPaths } from '../knowledge/connectDataDefinition.js';
|
|
12
|
-
import { CONNECT_DATA_EXECUTE_OPS, CONNECT_DATA_WRITE_OPS, CONNECT_DATA_WRITE_ROW_LIMIT, enrichRowErrors, executeFindings } from '../knowledge/connectDataExecute.js';
|
|
13
|
-
import { bindOps, buildDefinition, enumOptionItems, capabilityUnderRecordedNames, carryFieldDetails, anonymousWriteFindings, lookupReadabilityFindings, userEyeFindings, sameSourceFindings, editFindings, CONNECT_DATA_PREVIEW_LIMIT, fieldListItemProps, inputFromExisting, readAllPageSize, invertRenames, filterFindings, findSchemaNode, readPropValue, renameBindInput, renameColumnItems, renameDefinitionPaths, renamesFromDrift, resolveConnectable, mergeOptionItems, microserviceReferenceId, stampExpandIds, stateReferenceFindings, stateReferenceNames, unboundStateNames, previewStateFindings } from '../knowledge/connectDataBind.js';
|
|
14
|
-
import { parseVariableEntries } from '../knowledge/normalize.js';
|
|
15
|
-
import { projectAccessContext } from '../knowledge/access.js';
|
|
16
|
-
const sourceParam = z.object({
|
|
17
|
-
kind: z.enum(CONNECT_DATA_SOURCE_KINDS),
|
|
18
|
-
id: guid,
|
|
19
|
-
slug: z.string().min(1).optional()
|
|
20
|
-
}).strict().describe("Veri kaynağı: { kind, id } — kaynaklar connect_data_sources ile listelenir. Okunan türler: entity (tablo), query (okuyan query), view (arama indeksi), workflow (çıktı DTO'lu), microservice (uç); datasource türü yok.");
|
|
21
|
-
/** Sayfa: `offset` ya da imleç (`after` — tanım `page.mode: cursor`, önceki yanıtın `meta.page.nextCursor`'ı); ikisi birlikte olmaz. */
|
|
22
|
-
const pageParam = (maxLimit) => z.object({
|
|
23
|
-
limit: z.number().int().min(1).max(maxLimit).optional(),
|
|
24
|
-
offset: z.number().int().min(0).optional(),
|
|
25
|
-
after: z.string().min(1).optional().describe("İmleç sayfalama (tanım page.mode 'cursor'): önceki yanıtın meta.page.nextCursor'ı. offset ile birlikte verilmez")
|
|
26
|
-
}).strict().refine(page => page.after === undefined || page.offset === undefined, { message: "page.after ile page.offset birlikte verilmez (imleç ya da offset)" }).optional();
|
|
27
|
-
const filterParam = z.lazy(() => z.object({
|
|
28
|
-
filterType: z.enum(['single', 'group']),
|
|
29
|
-
field: z.string().min(1).optional(),
|
|
30
|
-
operator: z.enum(CONNECT_DATA_OPERATORS).optional(),
|
|
31
|
-
value: z.unknown().optional(),
|
|
32
|
-
conditionType: z.enum(['and', 'or']).optional(),
|
|
33
|
-
children: z.array(filterParam).optional()
|
|
34
|
-
}).strict()).describe("Backend Filter ağacı: tekil { filterType: 'single', field, operator, value } ya da grup { filterType: 'group', conditionType: 'and'|'or', children }. İşleçler kaynağın connect_data_describe yanıtındaki fields[].operators'tır; korumalı alanda yalnız eq · neq · in · notin (CD-002). Değer '$ad' yazılırsa istekteki params.ad'dan bağlanır.");
|
|
35
|
-
const sortParam = z.array(z.object({ field: z.string().min(1), ascending: z.boolean() }).strict());
|
|
36
|
-
const policyOverrideParam = z.boolean().optional()
|
|
37
|
-
.describe("true: block bulgularını warn'a indirir (bilinçli istisna; yanıtta işaretlenir)");
|
|
38
|
-
function unwrapData(payload) {
|
|
39
|
-
return (payload && typeof payload === 'object' && 'data' in payload ? payload.data : payload);
|
|
40
|
-
}
|
|
41
|
-
export function registerConnectDataTools(server, platform) {
|
|
42
|
-
const config = platform.config;
|
|
43
|
-
const describeSource = async (project, environment, body, impersonation) => unwrapData(await platform.tenant('POST', project, '/connect-data/describe', {
|
|
44
|
-
environment,
|
|
45
|
-
body,
|
|
46
|
-
...(impersonation ? { headers: impersonation } : {})
|
|
47
|
-
}));
|
|
48
|
-
const studioEye = (tool, project, environment, asUserId) => impersonationHeaders(platform, tool, project, environment, { asUserId }, { runtime: false });
|
|
49
|
-
const readPage = async (project, environment, applicationId, pageId) => unwrapData(await platform.tenant('GET', project, `/pages/${applicationId}/${pageId}`, { environment })) ?? {};
|
|
50
|
-
const readConnectedDefinition = async (project, environment, applicationId, pageId, element, prop) => {
|
|
51
|
-
const page = await readPage(project, environment, applicationId, pageId);
|
|
52
|
-
const node = findSchemaNode(page.schema, element);
|
|
53
|
-
if (!node)
|
|
54
|
-
throw new PlatformApiError(`Sayfada '${element}' elementId'li düğüm yok. page_schema_get ile elementId'leri listeleyin.`, 404);
|
|
55
|
-
const value = readPropValue(node.props, prop);
|
|
56
|
-
if (value?.type !== 'connectData') {
|
|
57
|
-
throw new PlatformApiError(`'${element}.${prop}' veri kaynağına bağlı değil (tip: ${String(value?.type ?? 'yok')}). Bağlamak için connect_data_bind.`, 400);
|
|
58
|
-
}
|
|
59
|
-
const microserviceId = microserviceReferenceId(value.ref);
|
|
60
|
-
if (!microserviceId)
|
|
61
|
-
return { page, node, value, definition: value.ref, reference: null };
|
|
62
|
-
const microservice = unwrapData(await platform.tenant('GET', project, `/microservices/${applicationId}/${microserviceId}`, { environment })) ?? {};
|
|
63
|
-
if (!microservice.connectDataDefinition)
|
|
64
|
-
throw new PlatformApiError(`'${element}.${prop}' microservice ${microserviceId}'e çevrilmiş ama microservice tanım taşımıyor (silinmiş ya da connectData kaynaklı değil).`, 404);
|
|
65
|
-
return { page, node, value, definition: microservice.connectDataDefinition, reference: { microserviceId, microserviceSlug: microservice.slug ?? null } };
|
|
66
|
-
};
|
|
67
|
-
/** Sayfa değişkenleri önce, uygulama değişkenleri sonra (runtime `getState` sırası: page → app); okunamazsa null. */
|
|
68
|
-
const readStateEntries = async (project, environment, applicationId, pageId) => {
|
|
69
|
-
try {
|
|
70
|
-
const read = async (path) => parseVariableEntries(unwrapData(await platform.tenant('GET', project, path, { environment }))?.data);
|
|
71
|
-
const entries = [...(await read(`/variables/${applicationId}/${pageId}`)), ...(await read(`/variables/${applicationId}`))];
|
|
72
|
-
return entries.filter((entry, index) => entries.findIndex(other => other.key === entry.key) === index);
|
|
73
|
-
}
|
|
74
|
-
catch {
|
|
75
|
-
return null;
|
|
76
|
-
}
|
|
77
|
-
};
|
|
78
|
-
server.registerTool('connect_data_sources', {
|
|
79
|
-
title: 'Bağlanabilir veri kaynaklarını listele',
|
|
80
|
-
description: "Bir prop'a bağlanabilecek kaynaklar: { kind, id, slug, name, group, isSystem }. kind: entity (group = datasource), query (yalnız okuyan query'ler; group = datasource), view (group = datasource), workflow, microservice (group = uç kaynağı: table · query · view · workflow · system). kind verilmezse hepsi. Bağlamadan önce connect_data_describe ile kaynağın alanlarını ve kabiliyetini okuyun: query/view/workflow sunucuda süzmez (CD-013), microservice yalnız tablo ucunda dinamik filtre açıksa süzer.",
|
|
81
|
-
inputSchema: {
|
|
82
|
-
project: projectParam,
|
|
83
|
-
environment: environmentParam,
|
|
84
|
-
kind: z.enum(CONNECT_DATA_SOURCE_KINDS).optional(),
|
|
85
|
-
search: z.string().min(1).optional().describe('Ad ya da slug içinde arama')
|
|
86
|
-
},
|
|
87
|
-
annotations: readOnly
|
|
88
|
-
}, handle(config, async ({ project, environment, kind, search }) => ({
|
|
89
|
-
sources: unwrapData(await platform.tenant('GET', project, '/connect-data/sources', { environment, query: { kind, searchTerm: search } }))
|
|
90
|
-
})));
|
|
91
|
-
server.registerTool('connect_data_describe', {
|
|
92
|
-
title: 'Kaynağın kabiliyetini oku',
|
|
93
|
-
description: "Kaynağın bu özne için EFEKTİF kabiliyeti: fields[] (slug, type, access plain|masked|hidden, canWrite, canFilter, sortable, operators, isKey, isProtected, isLocalized, ilişki alanında relation { kind o2m|m2m|self, targetEntityId, targetSlug, targetDisplay, targetReadable }, enum[], file), hidden[] (politikanın gizlediği alan adları), write { create, update, delete, bulk }, source { primaryKey, display, isSystem, pageMode offset|client, textSearch } ve drift[] (definition verildiyse tanımdaki silinmiş/yeniden adlanmış alanlar). Filtre işleci, sıralama ve yazma kararları buradan verilir — alan tipinden tahmin etmeyin. pageMode client: kaynak tüm satırları tek yanıtta verir (CD-007); textSearch true (view): request.search sunucuda arar, aramasız liste boş döner. Query/view/workflow/microservice yazmaz (write hepsi false). Middleware'li microservice, yazan tablo/sistem ucu ve dönüş şeması olmayan uç 422 ConnectDataSourceUnsupported.",
|
|
94
|
-
inputSchema: {
|
|
95
|
-
project: projectParam,
|
|
96
|
-
environment: environmentParam,
|
|
97
|
-
source: sourceParam.optional(),
|
|
98
|
-
definition: z.record(z.unknown()).optional().describe('Kayıtlı bir tanım (connect_data_get): drift yalnız tanımla hesaplanır'),
|
|
99
|
-
asUserId: asUserIdParam
|
|
100
|
-
},
|
|
101
|
-
annotations: readOnly
|
|
102
|
-
}, handle(config, async ({ project, environment, source, definition, asUserId }) => {
|
|
103
|
-
if (!source && !definition)
|
|
104
|
-
throw new PlatformApiError("'source' ya da 'definition' verin.", 400);
|
|
105
|
-
return describeSource(project, environment, definition ? { definition } : { source }, await studioEye('connect_data_describe', project, environment, asUserId));
|
|
106
|
-
}));
|
|
107
|
-
server.registerTool('connect_data_get', {
|
|
108
|
-
title: 'Bağlı prop tanımını oku',
|
|
109
|
-
description: "Sayfadaki bağlı bir prop'un tanımı (ref) ve kaynağın güncel kabiliyeti; drift[] tanımdaki alanların kaynaktan silinip silinmediğini söyler. CD-006: tanımda açık düzenleme eylemine ya da editable alana politika izin vermiyorsa warn — kabiliyet asUserId verilirse o kullanıcının gözüyle hesaplanır (gateway kimliği genelde yöneticidir, uyarı üretmez). Tanımın bir parçasını değiştirmek için connect_data_bind'i aynı prop ve kaynakla yalnız o parçayı vererek çağırın — verilmeyen parçalar kayıtlı tanımdan taşınır.",
|
|
110
|
-
inputSchema: {
|
|
111
|
-
project: projectParam,
|
|
112
|
-
environment: environmentParam,
|
|
113
|
-
applicationId: guid,
|
|
114
|
-
pageId: guid,
|
|
115
|
-
element: z.string().min(1).describe('Bileşenin elementId\'si'),
|
|
116
|
-
prop: z.string().min(1).describe("Prop yolu: 'rows', 'config.series' ya da 'series.1.data'"),
|
|
117
|
-
asUserId: asUserIdParam
|
|
118
|
-
},
|
|
119
|
-
annotations: readOnly
|
|
120
|
-
}, handle(config, async ({ project, environment, applicationId, pageId, element, prop, asUserId }) => {
|
|
121
|
-
const eye = await studioEye('connect_data_get', project, environment, asUserId);
|
|
122
|
-
const { node, value, definition, reference } = await readConnectedDefinition(project, environment, applicationId, pageId, element, prop);
|
|
123
|
-
const capability = await describeSource(project, environment, { definition }, eye);
|
|
124
|
-
const findings = config.policyMode === 'off' ? [] : editFindings(definition.behavior?.edit, capability);
|
|
125
|
-
return attachFindings({
|
|
126
|
-
element,
|
|
127
|
-
type: node.type,
|
|
128
|
-
prop,
|
|
129
|
-
...(asUserId ? { asUserId } : {}),
|
|
130
|
-
...(reference ? { microservice: reference, microserviceNote: "Prop microservice'e çevrilmiş: tanım microservice'te durur (burada gösterilen). Düzenlemek için connect_data_detach ile ayırın, sonra connect_data_bind." } : {}),
|
|
131
|
-
definition,
|
|
132
|
-
sampleRows: Array.isArray(value.data) ? value.data.length : 0,
|
|
133
|
-
capability: { fields: capability.fields, hidden: capability.hidden, write: capability.write, source: capability.source },
|
|
134
|
-
drift: capability.drift ?? []
|
|
135
|
-
}, findings);
|
|
136
|
-
}));
|
|
137
|
-
server.registerTool('connect_data_preview', {
|
|
138
|
-
title: 'Bağlantı tanımını önizle',
|
|
139
|
-
description: `Tanımı kaydetmeden gerçek kaynaktan okur ve en çok ${CONNECT_DATA_PREVIEW_LIMIT} satır döner ({ data, meta { total, fields, hidden, write } }). Tanım doğrudan (definition) ya da sayfadaki bağlı prop'tan (applicationId + pageId + element + prop) alınır. request.filter istek gitmeden kabiliyete karşı denetlenir: alan behavior.filterable'da ve işleç describe listesinde olmalı (CD-002). asUserId ile başka kullanıcının gördüğü satırlar. YAN ETKİSİZ: yalnız okur, transaction açmaz. Tanım şekli: platform_guide('page') §Kural 1c-3.`,
|
|
140
|
-
inputSchema: {
|
|
141
|
-
project: projectParam,
|
|
142
|
-
environment: environmentParam,
|
|
143
|
-
definition: z.record(z.unknown()).optional(),
|
|
144
|
-
applicationId: guid.optional(),
|
|
145
|
-
pageId: guid.optional(),
|
|
146
|
-
element: z.string().min(1).optional(),
|
|
147
|
-
prop: z.string().min(1).optional(),
|
|
148
|
-
request: z.object({
|
|
149
|
-
filter: filterParam.optional(),
|
|
150
|
-
search: z.string().optional(),
|
|
151
|
-
sort: sortParam.optional(),
|
|
152
|
-
page: pageParam(CONNECT_DATA_PREVIEW_LIMIT),
|
|
153
|
-
params: z.record(z.unknown()).optional(),
|
|
154
|
-
fields: z.array(z.string().min(1)).optional()
|
|
155
|
-
}).strict().optional(),
|
|
156
|
-
asUserId: asUserIdParam,
|
|
157
|
-
policyOverride: policyOverrideParam
|
|
158
|
-
},
|
|
159
|
-
annotations: readOnly
|
|
160
|
-
}, handle(config, async ({ project, environment, definition: given, applicationId, pageId, element, prop, request, asUserId, policyOverride }) => {
|
|
161
|
-
const eye = await studioEye('connect_data_preview', project, environment, asUserId);
|
|
162
|
-
let definition = given;
|
|
163
|
-
if (!definition) {
|
|
164
|
-
if (!applicationId || !pageId || !element || !prop)
|
|
165
|
-
throw new PlatformApiError("'definition' ya da applicationId + pageId + element + prop verin.", 400);
|
|
166
|
-
definition = (await readConnectedDefinition(project, environment, applicationId, pageId, element, prop)).definition;
|
|
167
|
-
}
|
|
168
|
-
const limit = request?.page?.limit ?? Math.min(definition.page?.limit ?? 25, CONNECT_DATA_PREVIEW_LIMIT);
|
|
169
|
-
const body = { op: 'list', ...request, page: request?.page?.after ? { limit, after: request.page.after } : { limit, offset: request?.page?.offset ?? 0 } };
|
|
170
|
-
let findings = [];
|
|
171
|
-
if (request?.filter && config.policyMode !== 'off') {
|
|
172
|
-
const capability = await describeSource(project, environment, { definition }, eye);
|
|
173
|
-
findings = enforcePolicy(applyPolicyMode(config, filterFindings(request.filter, capabilityUnderRecordedNames(capability), definition.behavior?.filterable ?? []), policyOverride));
|
|
174
|
-
}
|
|
175
|
-
const unbound = config.policyMode === 'off' ? [] : unboundStateNames(definition, request?.params);
|
|
176
|
-
if (unbound.length > 0) {
|
|
177
|
-
const state = applicationId && pageId ? await readStateEntries(project, environment, applicationId, pageId) : null;
|
|
178
|
-
findings = [...findings, ...applyPolicyMode(config, previewStateFindings(unbound, state), policyOverride)];
|
|
179
|
-
}
|
|
180
|
-
const result = await platform.tenant('POST', project, '/connect-data/preview', {
|
|
181
|
-
environment,
|
|
182
|
-
body: { definition, request: body },
|
|
183
|
-
...(eye ? { headers: eye } : {})
|
|
184
|
-
});
|
|
185
|
-
return attachFindings(unwrapData(result), findings);
|
|
186
|
-
}));
|
|
187
|
-
server.registerTool('connect_data_execute', {
|
|
188
|
-
title: 'Bağlı prop\'u runtime yolundan yürüt (oku / yaz)',
|
|
189
|
-
description: `Bileşenin runtime'da yapacağı isteğin AYNISI: POST {project}/connect-data/{application}/{page}/{element}/{prop}. Tanım istekte gitmez; platform sayfadan çözer, sayfa kapısını (page.view + middleware) ve PEP'i uygular. op: list · describe · lookup (field ile ilişki hedefi) · create (rows) · update (rows, her satırda anahtar) · delete (ids). YAN ETKİLİDİR: create/update/delete gerçekten yazar (tanım writeMode atomic: tek transaction, hepsi ya da hiçbiri; partial: yazılabilenler yazılır, düşenler errors[]; ilişki alanı '<id>' ya da { id }, m2m tam liste ['<id>' | { id }, …]). İstek gitmeden CD-012 (tanımda kapalı op, bulk kapalıyken çok satır, editable dışı / expand alanı, anahtarsız update (kısmi kipte warn), tanım atomikken mode partial, boş gövde; anahtar alanı describe'daki source.primaryKey) ve filtrede CD-002 denetlenir — ön denetim kabiliyeti asUserId verilirse o kullanıcının, yoksa gateway kimliğinin gözüyle okur, son kararı platform verir; lookup filtresi ilişki hedefine uygulandığı için CD-002'ye girmez. KİMLİK (üçünden biri): asUserId — parolasız kullanıcı gözü, kompleks enforce projede doğrulamanın olağan yolu (istek gateway kimliği + impersonated-user-id başlığıyla gider; yalnız development/dal, production'da istek gönderilmez); asUser — oturum açılmış üye (access_session_create'in tok_… referansı ya da ham JWT; Bearer + NG_AccessToken çerezi); anonymous: true — kimliksiz ziyaretçi. Hiçbiri verilmezse gateway'in kimliği gider ve kompleks enforce projede 401 AppSessionRequired döner. Yanıt { data, meta, errors[] } — errors öğelerine gönderilen satırın özeti (rowSummary; delete'te kimlik) eklenir. Satır/kimlik tavanı ${CONNECT_DATA_WRITE_ROW_LIMIT}; gateway bölmez. Yazma kaynağı denetim izinde CONNECT_DATA (entity_audit_trails source).`,
|
|
190
|
-
inputSchema: {
|
|
191
|
-
project: projectParam,
|
|
192
|
-
environment: environmentParam,
|
|
193
|
-
applicationId: guid,
|
|
194
|
-
pageId: guid,
|
|
195
|
-
element: z.string().min(1).describe("Bileşenin elementId'si"),
|
|
196
|
-
prop: z.string().min(1).describe("Bağlı prop yolu: 'rows', 'options', 'series.1.data'"),
|
|
197
|
-
op: z.enum(CONNECT_DATA_EXECUTE_OPS),
|
|
198
|
-
field: z.string().min(1).optional().describe("op: 'lookup' için tanımda seçili ilişki alanı"),
|
|
199
|
-
filter: filterParam.optional(),
|
|
200
|
-
search: z.string().optional(),
|
|
201
|
-
sort: sortParam.optional(),
|
|
202
|
-
page: pageParam(1000),
|
|
203
|
-
params: z.record(z.unknown()).optional().describe("Tanım filtresindeki ve params'ındaki '$ad' değerleri (runtime sayfa durumundan doldurur)"),
|
|
204
|
-
fields: z.array(z.string().min(1)).optional().describe('Tanımın seçtiği yollar içinden daraltma'),
|
|
205
|
-
rows: z.array(z.record(z.unknown())).max(CONNECT_DATA_WRITE_ROW_LIMIT).optional().describe("create/update satırları; update'te her satır anahtar alanını (id) taşır, yalnız edit.editable alanları"),
|
|
206
|
-
ids: z.array(z.unknown()).max(CONNECT_DATA_WRITE_ROW_LIMIT).optional().describe('delete için anahtar değerleri'),
|
|
207
|
-
mode: z.enum(['atomic', 'partial']).optional(),
|
|
208
|
-
asUser: z.string().min(1).optional().describe("Uygulama üyesinin kimliği: access_session_create yanıtının tok_… referansı ya da ham JWT"),
|
|
209
|
-
asUserId: asUserIdParam,
|
|
210
|
-
anonymous: z.boolean().optional().describe('true: hiçbir kimlik taşımadan (anonim sayfa ziyaretçisi)'),
|
|
211
|
-
policyOverride: policyOverrideParam,
|
|
212
|
-
expectFailure: z.boolean().optional().describe("true: platformun bu isteği reddetmesi BEKLENİYOR (kasıtlı negatif test — olmayan ilişki hedefi 404, yetkisiz kullanıcı 403, dosya alanına yazma 422). Hata yine döner, ama ölçüm aracı bu satırı plansız hata saymaz (microservice_execute'taki bayrakla aynı); istek gövdesine girmez.")
|
|
213
|
-
},
|
|
214
|
-
annotations: { ...writes, destructiveHint: true }
|
|
215
|
-
}, handle(config, async ({ project, environment, applicationId, pageId, element, prop, op, asUser, asUserId, anonymous, policyOverride, expectFailure: _expectFailure, ...request }) => {
|
|
216
|
-
if (anonymous === true && asUser !== undefined)
|
|
217
|
-
throw new PlatformApiError('anonymous: true ile asUser birlikte verilemez.', 400);
|
|
218
|
-
const eye = await impersonationHeaders(platform, 'connect_data_execute', project, environment, { asUserId, asUser, anonymous }, { runtime: true });
|
|
219
|
-
const { page, definition } = await readConnectedDefinition(project, environment, applicationId, pageId, element, prop);
|
|
220
|
-
const isWrite = CONNECT_DATA_WRITE_OPS.includes(op);
|
|
221
|
-
const checksFilter = request.filter !== undefined && op !== 'lookup';
|
|
222
|
-
const described = isWrite || (config.policyMode !== 'off' && checksFilter) ? await describeSource(project, environment, { definition }, eye) : null;
|
|
223
|
-
const capability = described ? capabilityUnderRecordedNames(described) : null;
|
|
224
|
-
const keyField = capability?.source?.primaryKey ?? 'id';
|
|
225
|
-
let findings = config.policyMode === 'off' ? [] : applyPolicyMode(config, executeFindings({ op, rows: request.rows, ids: request.ids, mode: request.mode, field: request.field }, definition, keyField, capability), policyOverride);
|
|
226
|
-
if (checksFilter && capability && request.filter) {
|
|
227
|
-
findings = [...findings, ...applyPolicyMode(config, filterFindings(request.filter, capability, definition.behavior?.filterable ?? []), policyOverride)];
|
|
228
|
-
}
|
|
229
|
-
enforcePolicy(findings);
|
|
230
|
-
if (!page.slug)
|
|
231
|
-
throw new PlatformApiError("Sayfanın slug'ı okunamadı; runtime yolu kurulamadı.", 404);
|
|
232
|
-
const pageSlug = page.slug;
|
|
233
|
-
const body = Object.fromEntries(Object.entries({ op, ...request }).filter(([, value]) => value !== undefined));
|
|
234
|
-
const runtimePath = (application) => `/connect-data/${[application, pageSlug, element, prop].map(encodeURIComponent).join('/')}`;
|
|
235
|
-
const send = (application) => platform.tenant('POST', project, runtimePath(application), {
|
|
236
|
-
environment,
|
|
237
|
-
body,
|
|
238
|
-
headers: eye ?? identityHeaders(asUser, undefined, undefined),
|
|
239
|
-
anonymous,
|
|
240
|
-
mirrorAuthorizationCookie: true
|
|
241
|
-
});
|
|
242
|
-
let application = await applicationSlug(platform, applicationId);
|
|
243
|
-
if (!application)
|
|
244
|
-
throw new PlatformApiError(`Uygulama ${applicationId} bulunamadı; runtime yolu kurulamadı.`, 404);
|
|
245
|
-
let raw;
|
|
246
|
-
try {
|
|
247
|
-
raw = await send(application);
|
|
248
|
-
}
|
|
249
|
-
catch (error) {
|
|
250
|
-
if (!(error instanceof PlatformApiError) || error.status !== 404)
|
|
251
|
-
throw error;
|
|
252
|
-
const fresh = await applicationSlug(platform, applicationId, { fresh: true });
|
|
253
|
-
if (!fresh || fresh === application)
|
|
254
|
-
throw error;
|
|
255
|
-
application = fresh;
|
|
256
|
-
raw = await send(fresh);
|
|
257
|
-
}
|
|
258
|
-
const result = unwrapData(raw) ?? {};
|
|
259
|
-
const sentRows = op === 'delete' ? request.ids?.map(id => ({ [keyField]: id })) : request.rows;
|
|
260
|
-
return attachFindings({ ...result, errors: enrichRowErrors(result.errors, sentRows, keyField), request: { path: runtimePath(application), op } }, findings);
|
|
261
|
-
}));
|
|
262
|
-
server.registerTool('connect_data_bind', {
|
|
263
|
-
title: 'Prop\'u veri kaynağına bağla',
|
|
264
|
-
description: "Tek çağrıda bağlar: bileşenin bağlanabilir prop kaydını (component_get.connectData) ve kaynağın kabiliyetini okur, tanımı kurar ve CD-001…004/006/007/008/009/010/011/013/014/015/016/017/018 bulgularını istek gitmeden üretir (CD-018: dosya alanı edit.editable'a konamaz, platform kaydı 422 connect-data-editable-file ile reddederdi; kolonların isFilterable'ı yalnız filterable listesindeki alanlarda açılır); bileşen bir layout'taysa pageId yerine layoutId, widget içindeyse componentId verilir (yazma o kaydın şema ucuna gider, sürüm koşulu yoktur, CD-014 koşmaz); block bulgusu policyOverride ile de yazılmaz: CD-004/CD-013 şekil kuralları op gövdesinde yine reddedilir; set_connect_data (+ kolon listesi, + alan adı prop'ları, + pano kolonu isteyen bileşende rolün enum değerlerinden seçenek listesi) tek atomik PATCH olarak yazılır, sonra şema yeniden okunup prop'un connectData olarak kaydedildiği doğrulanır. Roller verilmezse yalnız ZORUNLU roller önerilir (value ← birincil anahtar, label ← görünen alan) — isteğe bağlı roller (caption, color, isDisabled…) açıkça verilmedikçe eşlenmez. Liste bileşeninde (fieldsFrom) kolonlar bileşenin kendi kolon şemasından yalnız KÖK alanlardan üretilir (expand yolu satıra okunur, kolon olmaz). VAR OLAN BAĞLANTIDA aynı kaynakla çağrılırsa verilmeyen parçalar (alanlar, expand, roller, filtre, sıralama, sayfa boyu, filterable/searchable, edit) ve alanların lookup ayarları kayıtlı tanımdan taşınır; alan listesi değişmediyse ve columns verilmediyse kolon listesi yeniden yazılmaz, değiştiyse kayıtlı listeyle birleştirilir (kalan kolonların sırası ve ayarı korunur, çıkan alan düşer, yeni alan sona eklenir). Kaynakta yeniden adlanan alan id ile eşleşir: girdide kayıtlı ya da güncel ad verilebilir, tanım güncel adlarla kurulur ve sayfanın gördüğü kayıtlı adla yazılır (rebind.renamed). Expand alt alanlarına hedef alan id'si yazılır; kayıtlı lookup ayarları kimlikleriyle taşınır. Block bulgusu varsa hiçbir şey yazılmaz; dryRun: true yalnız tanımı, op'ları ve bulguları döner.",
|
|
265
|
-
inputSchema: {
|
|
266
|
-
project: projectParam,
|
|
267
|
-
environment: environmentParam,
|
|
268
|
-
applicationId: guid,
|
|
269
|
-
pageId: guid.optional().describe("Bağlanan bileşenin sayfası. Layout'taki bileşen için layoutId, widget içindeki için componentId — üçünden tam olarak biri"),
|
|
270
|
-
layoutId: guid.optional().describe("Bileşen bir layout'taysa layout id'si (S9 W-1; yazma PATCH /layouts/{app}/{id}/schema)"),
|
|
271
|
-
componentId: guid.optional().describe("Bileşen bir widget'ın içindeyse widget kaydının id'si (yazma PATCH /components/{app}/{id}/schema)"),
|
|
272
|
-
element: z.string().min(1).describe("Bileşenin elementId'si"),
|
|
273
|
-
prop: z.string().min(1).describe("Bağlanacak prop yolu: 'rows', 'options', 'config.series' ya da 'series.1.data'"),
|
|
274
|
-
source: sourceParam,
|
|
275
|
-
roles: z.record(z.string().min(1)).optional().describe("Rol → alan yolu ({ label: 'ad', value: 'id' }). Verilmezse önerilir; zorunlu rol doldurulamazsa CD-001 block aday listesiyle döner."),
|
|
276
|
-
fields: z.array(z.string().min(1)).optional().describe('Seçilecek kök alanlar. Liste bileşeninde görünen kolonların sırası; verilmezse bütün görünür alanlar (liste) ya da anahtar + rol kökleri (diğerleri).'),
|
|
277
|
-
expand: z.record(z.array(z.string().min(1)).min(1)).optional().describe("İlişki alanı → hedefte okunacak alanlar ({ sube: ['ad'] }); satırda sube: { id, ad } gelir"),
|
|
278
|
-
filter: filterParam.optional(),
|
|
279
|
-
sort: sortParam.optional().describe('Verilmezse görünen alan artan (seçiliyse)'),
|
|
280
|
-
pageSize: z.number().int().min(1).max(1000).optional(),
|
|
281
|
-
pageMode: z.enum(['offset', 'cursor', 'client']).optional().describe("Sayfalama kipi; verilmezse kaynak sunucuda sayfalayabiliyorsa 'offset', sayfalayamıyorsa 'client'. 'cursor' (keyset) yalnız entity kaynağında ve tek sıralama anahtarıyla; 'client' runtime'ın satırları tek okumada (en çok 1000) alıp tarayıcıda süzmesidir."),
|
|
282
|
-
language: z.string().regex(/^(request|default|[a-z]{2})$/).optional(),
|
|
283
|
-
filterable: z.array(z.string().min(1)).optional().describe('Ekranda filtrelenebilir alanlar (seçili yollar içinden)'),
|
|
284
|
-
searchable: z.array(z.string().min(1)).optional(),
|
|
285
|
-
edit: z.object({
|
|
286
|
-
create: z.boolean().optional(),
|
|
287
|
-
update: z.boolean().optional(),
|
|
288
|
-
delete: z.boolean().optional(),
|
|
289
|
-
bulk: z.boolean().optional(),
|
|
290
|
-
saveMode: z.enum(['row', 'cell', 'batch']).optional(),
|
|
291
|
-
editable: z.array(z.string().min(1)).optional(),
|
|
292
|
-
defaults: z.record(z.unknown()).optional(),
|
|
293
|
-
writeMode: z.enum(['atomic', 'partial']).optional()
|
|
294
|
-
}).strict().optional(),
|
|
295
|
-
refresh: z.object({ afterWrite: z.enum(['reload', 'merge']).optional(), poll: z.number().int().min(1).nullable().optional() }).strict().optional(),
|
|
296
|
-
columns: z.union([z.literal('auto'), z.array(z.record(z.unknown()))]).optional().describe("Liste bileşeninde (fieldsFrom) kolon listesi: 'auto' bileşenin kolon şemasından ve kabiliyetten üretir; dizi verilirse öğe değerleri her seviyede static Prop'a sarılarak yazılır (sarmal değerlere dokunulmaz). Var olan bağlantıda verilmezse ve alanlar değişmediyse kolonlar korunur"),
|
|
297
|
-
fixDrift: z.boolean().optional().describe('true: kaynaktan silinmiş alanları tanımdan düşürür (CD-008)'),
|
|
298
|
-
params: z.record(z.unknown()).optional().describe("Kaynak parametreleri (query/workflow/microservice): sabit değer ya da '$ad' sayfa değişkeni ({ yil: 2026, sube: '$secilenSube' }). Adlar connect_data_describe source.parameters'tan (CD-013); '$ad' page_state'te olmalı (CD-014). Entity/view parametre almaz — filtrede '$ad' kullanın."),
|
|
299
|
-
baseVersion: z.string().optional().describe('Sayfa sürümü; değiştiyse 409 ve hiçbir op yazılmaz. Verilmezse bu çağrıda okunan sürüm'),
|
|
300
|
-
dryRun: z.boolean().optional(),
|
|
301
|
-
asUserId: asUserIdParam.describe("CD-006 ve CD-011 uyarılarını bu kullanıcının gözüyle üretir (users satır id'si): düzenleme izni ve lookup hedefinin okunabilirliği ikinci bir describe ile o kullanıcının politikasından okunur. Tanımın kendisi (alanlar, kolonlar) yine gateway kimliğinin kabiliyetinden kurulur — bağlantı bütün kullanıcılar içindir. Gateway kimliği genelde yöneticidir ve bu iki uyarıyı hiç üretmez (38/M-13)."),
|
|
302
|
-
policyOverride: policyOverrideParam
|
|
303
|
-
},
|
|
304
|
-
annotations: updates
|
|
305
|
-
}, handle(config, async ({ project, environment, applicationId, pageId, layoutId, componentId, element, prop, baseVersion, dryRun, asUserId, policyOverride, ...input }) => {
|
|
306
|
-
const host = layoutId ? 'layout' : componentId ? 'component' : 'page';
|
|
307
|
-
const hostId = layoutId ?? componentId ?? pageId;
|
|
308
|
-
if ([pageId, layoutId, componentId].filter(Boolean).length !== 1 || !hostId) {
|
|
309
|
-
throw new PlatformApiError('Bağlanan bileşenin yerini tam olarak bir kimlikle verin: pageId, layoutId ya da componentId (widget).', 400);
|
|
310
|
-
}
|
|
311
|
-
const eye = await studioEye('connect_data_bind', project, environment, asUserId);
|
|
312
|
-
const readHost = async () => {
|
|
313
|
-
if (host === 'page')
|
|
314
|
-
return readPage(project, environment, applicationId, hostId);
|
|
315
|
-
const record = await fetchHost(platform, host, project, environment, applicationId, hostId);
|
|
316
|
-
if (host === 'component' && record.kind !== 'widget' && !record.widgetType) {
|
|
317
|
-
throw new PlatformApiError(`componentId bir widget kaydı olmalı; '${String(record.slug ?? hostId)}' widget değil.`, 400);
|
|
318
|
-
}
|
|
319
|
-
return { schema: ((host === 'layout' ? record.views : record.schema) ?? []) };
|
|
320
|
-
};
|
|
321
|
-
const page = await readHost();
|
|
322
|
-
const node = findSchemaNode(page.schema, element);
|
|
323
|
-
if (!node)
|
|
324
|
-
throw new PlatformApiError(`Sayfada '${element}' elementId'li düğüm yok. page_schema_get ile elementId'leri listeleyin.`, 404);
|
|
325
|
-
const existingValue = readPropValue(node.props, prop);
|
|
326
|
-
if (existingValue?.type === 'connectData' && microserviceReferenceId(existingValue.ref)) {
|
|
327
|
-
throw new PlatformApiError(`'${element}.${prop}' microservice ${microserviceReferenceId(existingValue.ref)}'e çevrilmiş: tanım microservice'te durur. Önce connect_data_detach ile ayırın, sonra bağlayın (üzerine yazmak microservice'i sahipsiz bırakırdı).`, 409);
|
|
328
|
-
}
|
|
329
|
-
const components = await listComponents(platform, project, environment, applicationId);
|
|
330
|
-
const component = components.find(entry => entry.slug === node.type);
|
|
331
|
-
const componentDefinition = (component?.definition ?? {});
|
|
332
|
-
const connectData = componentDefinition.connectData ?? null;
|
|
333
|
-
const connectable = resolveConnectable(connectData, prop);
|
|
334
|
-
const existing = existingValue?.type === 'connectData' ? existingValue.ref : null;
|
|
335
|
-
const isSameSource = Boolean(existing && existing.source?.id === input.source.id);
|
|
336
|
-
const existingDrift = isSameSource ? (await describeSource(project, environment, { definition: existing })).drift ?? [] : [];
|
|
337
|
-
const renames = renamesFromDrift(existingDrift);
|
|
338
|
-
const working = existing && isSameSource ? renameDefinitionPaths(existing, renames) : existing;
|
|
339
|
-
const readAll = readAllPageSize(componentDefinition.props, input.pageSize);
|
|
340
|
-
const { input: carriedInput, keepColumns } = inputFromExisting(isSameSource ? renameBindInput(input, renames) : input, working);
|
|
341
|
-
const effectiveInput = readAll === undefined ? carriedInput : { ...carriedInput, pageSize: readAll };
|
|
342
|
-
const sourceCapability = await describeSource(project, environment, { source: input.source });
|
|
343
|
-
const capability = isSameSource ? { ...sourceCapability, drift: existingDrift } : sourceCapability;
|
|
344
|
-
const built = buildDefinition(effectiveInput, capability, connectable, Object.keys(connectData?.props ?? {}), fieldListItemProps(componentDefinition.props, connectable?.fieldsFrom));
|
|
345
|
-
const carried = carryFieldDetails(built.definition, isSameSource ? working : null);
|
|
346
|
-
const stamped = input.source.kind === 'entity' && carried.fields.some(field => field.expand?.some(child => !child.id))
|
|
347
|
-
? stampExpandIds(carried, await describeSource(project, environment, { definition: carried }))
|
|
348
|
-
: carried;
|
|
349
|
-
const definition = renameDefinitionPaths(stamped, invertRenames(renames));
|
|
350
|
-
const columns = renameColumnItems(built.columns, invertRenames(renames));
|
|
351
|
-
// Layout/widget bağlantısının `$ad`'ı onu gösteren sayfanın durumundan çözülür; o sayfa burada bilinmez.
|
|
352
|
-
const stateFindings = config.policyMode === 'off' || host !== 'page' || stateReferenceNames(definition).length === 0 ? [] : stateReferenceFindings(definition, (await readStateEntries(project, environment, applicationId, hostId))?.map(entry => entry.key) ?? null);
|
|
353
|
-
const userCapability = eye && config.policyMode !== 'off' ? await describeSource(project, environment, { source: input.source }, eye) : null;
|
|
354
|
-
const findings = config.policyMode === 'off' ? [] : applyPolicyMode(config, [...built.findings, ...stateFindings, ...anonymousWriteFindings(page.isAnonymous, definition.behavior?.edit), ...sameSourceFindings(page.schema, input.source.id, element, prop), ...lookupReadabilityFindings(definition, sourceCapability), ...(userCapability ? userEyeFindings(definition, userCapability, asUserId ?? '') : [])], policyOverride);
|
|
355
|
-
const merge = isSameSource && existing && connectable?.fieldsFrom && input.columns === undefined
|
|
356
|
-
? { saved: readPropValue(node.props, connectable.fieldsFrom), sourceNames: [...sourceCapability.fields.map(field => field.slug), ...selectedPaths(existing)] }
|
|
357
|
-
: undefined;
|
|
358
|
-
const optionItems = enumOptionItems(connectData, prop, definition, sourceCapability);
|
|
359
|
-
const options = optionItems ? { prop: optionItems.prop, items: mergeOptionItems(readPropValue(node.props, optionItems.prop), optionItems.items) } : undefined;
|
|
360
|
-
const ops = bindOps(element, prop, definition, connectable, columns, !keepColumns, merge, options);
|
|
361
|
-
if (dryRun)
|
|
362
|
-
return { dryRun: true, definition, columns: keepColumns ? null : columns, ...(existing ? { rebind: { keptFromExisting: true, columnsKept: keepColumns, ...(Object.keys(renames).length ? { renamed: renames } : {}) } } : {}), ops, findings };
|
|
363
|
-
enforcePolicy(findings);
|
|
364
|
-
const opIssues = ops.flatMap(op => connectDataOpIssues(op).map(issue => `${issue.code} — ${issue.message}`));
|
|
365
|
-
if (opIssues.length > 0)
|
|
366
|
-
throw new PlatformApiError(`Bağlama op'ları geçersiz (istek gönderilmedi): ${opIssues.join(' · ')}`, 400);
|
|
367
|
-
const response = unwrapData(await platform.tenant('PATCH', project, `${hostPath(host, applicationId, hostId)}/schema`, {
|
|
368
|
-
environment,
|
|
369
|
-
body: host === 'page' ? { ops, baseVersion: baseVersion ?? page.version ?? undefined } : { ops }
|
|
370
|
-
})) ?? {};
|
|
371
|
-
const saved = findSchemaNode((await readHost()).schema, element);
|
|
372
|
-
const stored = readPropValue(saved?.props, prop);
|
|
373
|
-
return attachFindings({
|
|
374
|
-
definition,
|
|
375
|
-
touched: response.touched,
|
|
376
|
-
newVersion: response.newVersion,
|
|
377
|
-
runtime: {
|
|
378
|
-
propType: stored?.type ?? null,
|
|
379
|
-
fields: stored?.ref?.fields?.length ?? 0,
|
|
380
|
-
...(connectable?.fieldsFrom && !keepColumns ? { [connectable.fieldsFrom]: columns?.length ?? 0 } : {}),
|
|
381
|
-
...(existing ? { columnsKept: keepColumns } : {})
|
|
382
|
-
}
|
|
383
|
-
}, findings);
|
|
384
|
-
}));
|
|
385
|
-
server.registerTool('connect_data_convert', {
|
|
386
|
-
title: "Bağlantıyı microservice'e çevir",
|
|
387
|
-
description: "Sayfadaki bağlı prop'un KAYDEDİLMİŞ tanımını connectData kaynaklı bir microservice'e taşır (POST /pages/{app}/{page}/connect-data/convert, tek transaction): microservice oluşur, prop kısa biçime { version: 1, microserviceId } geçer. Sonra flow'lar, microservice_execute, dış istemciler ve zamanlanmış işler m/{application}/{slug} ucundan AYNI kabiliyet damgalı yanıtı alır; sayfanın runtime'ı değişmez. Doğrulama: microservice_execute { application, microservice: slug, method: 'POST', body: { op: 'list', page: { limit: 5 } } } → { data, meta, errors }. Çevrilmiş prop'ta connect_data_get tanımı microservice'ten okur; connect_data_bind reddeder — düzenlemek için connect_data_detach. Slug küçük harf-tire.",
|
|
388
|
-
inputSchema: {
|
|
389
|
-
project: projectParam,
|
|
390
|
-
environment: environmentParam,
|
|
391
|
-
applicationId: guid,
|
|
392
|
-
pageId: guid,
|
|
393
|
-
element: z.string().min(1).describe("Bileşenin elementId'si"),
|
|
394
|
-
prop: z.string().min(1).describe("Bağlı prop yolu: 'rows', 'series.1.data'"),
|
|
395
|
-
name: z.string().min(1).describe('Microservice adı'),
|
|
396
|
-
slug: z.string().regex(/^[a-z0-9]+(-[a-z0-9]+)*$/).describe("Microservice slug'ı (m/{application}/{slug})"),
|
|
397
|
-
baseVersion: z.string().optional().describe('Sayfa sürümü; değiştiyse 409 ve hiçbir şey yazılmaz. Verilmezse bu çağrıda okunan sürüm gönderilir')
|
|
398
|
-
},
|
|
399
|
-
annotations: writes
|
|
400
|
-
}, handle(config, async ({ project, environment, applicationId, pageId, element, prop, name, slug, baseVersion }) => {
|
|
401
|
-
const { page, reference } = await readConnectedDefinition(project, environment, applicationId, pageId, element, prop);
|
|
402
|
-
if (reference)
|
|
403
|
-
throw new PlatformApiError(`'${element}.${prop}' zaten microservice '${reference.microserviceSlug ?? reference.microserviceId}'e çevrilmiş.`, 409);
|
|
404
|
-
const version = baseVersion ?? page.version;
|
|
405
|
-
const result = unwrapData(await platform.tenant('POST', project, `/pages/${applicationId}/${pageId}/connect-data/convert`, {
|
|
406
|
-
environment,
|
|
407
|
-
body: { elementId: element, propPath: prop, name, slug, ...(version ? { baseVersion: version } : {}) }
|
|
408
|
-
}));
|
|
409
|
-
const application = await applicationSlug(platform, applicationId);
|
|
410
|
-
// Kompleks + enforce projede kimliksiz çağrı 401 AppSessionRequired döner ve yeni uç kullanıcının hiçbir
|
|
411
|
-
// politikasında yoktur (39/M-15): tarif kullanıcı gözünü ve eksik grant'i söyler.
|
|
412
|
-
const access = await projectAccessContext(platform, project, environment);
|
|
413
|
-
const enforced = access?.accessModel === 'complex' && access.enforcementMode === 'enforce';
|
|
414
|
-
const verifyArguments = { application: application ?? '<application slug>', microservice: result.microserviceSlug, method: 'POST', body: { op: 'list', page: { limit: 5 } }, ...(enforced ? { asUserId: "<users satır id'si>" } : {}) };
|
|
415
|
-
return {
|
|
416
|
-
...result,
|
|
417
|
-
reference: { version: 1, microserviceId: result.microserviceId },
|
|
418
|
-
verify: {
|
|
419
|
-
tool: 'microservice_execute',
|
|
420
|
-
arguments: verifyArguments,
|
|
421
|
-
...(enforced ? { note: `Kompleks + enforce proje: kimliksiz çağrı 401 döner, asUserId verin. Yeni uç hiçbir politikada yok — kullanıcının politikasına microservice.execute grant'i ekleyin (resourceKey 'microservice:${result.microserviceId}'), yoksa 403. Sayfanın kendi bağlantısı bu grant'i istemez; connect_data_detach sonrası grant yetim kalır.` } : {})
|
|
422
|
-
}
|
|
423
|
-
};
|
|
424
|
-
}));
|
|
425
|
-
server.registerTool('connect_data_detach', {
|
|
426
|
-
title: 'Microservice\'e çevrilmiş bağlantıyı ayır',
|
|
427
|
-
description: "connect_data_convert'in tersi (POST /pages/{app}/{page}/connect-data/detach): microservice'teki tanım prop'a geri kopyalanır, kaynağı doğrulanır ve microservice SİLİNİR. Aynı microservice sayfada başka prop'ta, başka sayfada ya da bir flow'da kullanılıyorsa 409 ConnectDataMicroserviceInUse — önce o kullanımları kaldırın (dependency_graph). Sonra tanım connect_data_bind ile düzenlenebilir.",
|
|
428
|
-
inputSchema: {
|
|
429
|
-
project: projectParam,
|
|
430
|
-
environment: environmentParam,
|
|
431
|
-
applicationId: guid,
|
|
432
|
-
pageId: guid,
|
|
433
|
-
element: z.string().min(1).describe("Bileşenin elementId'si"),
|
|
434
|
-
prop: z.string().min(1).describe('Çevrilmiş prop yolu'),
|
|
435
|
-
baseVersion: z.string().optional().describe('Sayfa sürümü; değiştiyse 409 ve hiçbir şey yazılmaz. Verilmezse bu çağrıda okunan sürüm gönderilir')
|
|
436
|
-
},
|
|
437
|
-
annotations: { ...writes, destructiveHint: true }
|
|
438
|
-
}, handle(config, async ({ project, environment, applicationId, pageId, element, prop, baseVersion }) => {
|
|
439
|
-
const { page, reference } = await readConnectedDefinition(project, environment, applicationId, pageId, element, prop);
|
|
440
|
-
if (!reference)
|
|
441
|
-
throw new PlatformApiError(`'${element}.${prop}' microservice'e çevrilmemiş; ayrılacak bir şey yok.`, 400);
|
|
442
|
-
const version = baseVersion ?? page.version;
|
|
443
|
-
const result = unwrapData(await platform.tenant('POST', project, `/pages/${applicationId}/${pageId}/connect-data/detach`, {
|
|
444
|
-
environment,
|
|
445
|
-
body: { elementId: element, propPath: prop, ...(version ? { baseVersion: version } : {}) }
|
|
446
|
-
}));
|
|
447
|
-
return { ...result, detached: true };
|
|
448
|
-
}));
|
|
449
|
-
}
|