@appaflytech/zeyrek-ai-mcp 0.0.28 → 0.0.30
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 +494 -34
- package/dist/knowledge/componentEvents.js +132 -7
- package/dist/knowledge/connectDataBind.js +819 -0
- package/dist/knowledge/connectDataDefinition.js +341 -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 +59 -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 +859 -0
- package/dist/knowledge/mobileComponentEvents.js +239 -0
- package/dist/knowledge/normalize.js +236 -67
- 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 +319 -21
- package/dist/knowledge/schemaGuard.js +11 -0
- package/dist/knowledge/styleResolve.js +50 -4
- package/dist/knowledge/styleResolveNative.js +527 -0
- package/dist/knowledge/themeResolve.js +3 -3
- package/dist/observability/trace.js +6 -3
- package/dist/platform/argumentAliases.js +128 -0
- package/dist/platform/bodyFile.js +103 -0
- package/dist/platform/capture/actions.js +333 -0
- package/dist/platform/capture/browser.js +1 -0
- package/dist/platform/{screenshot.js → capture/capture.js} +105 -519
- 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 +162 -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 +17 -1
- 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/mobile-app-shell-drawer.json +243 -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 +672 -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 +796 -0
- package/dist/resources/guides/bpm.md +686 -0
- package/dist/resources/guides/case.md +153 -0
- package/dist/resources/guides/component.md +91 -4
- 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/microservice.md +81 -2
- package/dist/resources/guides/middleware.md +44 -2
- package/dist/resources/guides/mobile-layout.md +193 -0
- package/dist/resources/guides/mobile.md +495 -0
- package/dist/resources/guides/page.md +454 -75
- 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 +143 -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 +21 -1
- package/dist/resources/schemas/MicroserviceDto.json +15 -3
- package/dist/resources/schemas/OrganizationProjectDto.json +335 -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 +22 -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/StepDto.json +22 -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 +143 -1
- package/dist/resources/schemas/ValidateProcessDefinitionQuery.json +1339 -0
- package/dist/server.js +27 -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 +60 -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 +7 -0
- package/dist/tools/components.js +118 -14
- package/dist/tools/connectData.js +449 -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 +2 -2
- package/dist/tools/designSchemaPatch.js +34 -9
- package/dist/tools/dtos.js +36 -5
- package/dist/tools/entities.js +79 -12
- package/dist/tools/externalComponents.js +26 -5
- package/dist/tools/flows.js +122 -14
- 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 +1 -1
- package/dist/tools/microservices.js +77 -25
- package/dist/tools/mobile.js +221 -0
- package/dist/tools/modules.js +48 -0
- package/dist/tools/organizationStructure.js +137 -0
- package/dist/tools/pages.js +465 -98
- package/dist/tools/platformInfo.js +27 -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 +37 -8
- 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/errors.js
CHANGED
|
@@ -7,6 +7,11 @@ export class PlatformApiError extends Error {
|
|
|
7
7
|
* için sayıya ihtiyaç duyar, çağıran hangi satırı düzelteceğini ancak öyle bilir.
|
|
8
8
|
*/
|
|
9
9
|
rowIndex;
|
|
10
|
+
/**
|
|
11
|
+
* Platformun ham `message`'ı (yalnız 403'te doldurulur). `message` gateway ipucunu da taşır ve ipucunda
|
|
12
|
+
* izin slug'ları geçer ('entity.reveal grant'i'); `accessDecision` izni ondan okursa yanlış izin yazar.
|
|
13
|
+
*/
|
|
14
|
+
apiMessage;
|
|
10
15
|
constructor(message, status, code) {
|
|
11
16
|
super(message);
|
|
12
17
|
this.status = status;
|
|
@@ -32,9 +37,12 @@ function collectValidationMessages(payload) {
|
|
|
32
37
|
continue;
|
|
33
38
|
const record = entry;
|
|
34
39
|
const isPageSchemaShape = 'rule' in record || 'componentId' in record || 'suggestions' in record;
|
|
40
|
+
const isDefinitionFindingShape = 'path' in record && 'message' in record && 'code' in record;
|
|
35
41
|
messages.push(isPageSchemaShape
|
|
36
42
|
? formatPageSchemaError(record)
|
|
37
|
-
:
|
|
43
|
+
: isDefinitionFindingShape
|
|
44
|
+
? `${String(record.path)} [${String(record.code)}]: ${String(record.message)}`
|
|
45
|
+
: `${record.key ?? '?'}: ${record.value ?? ''}`);
|
|
38
46
|
}
|
|
39
47
|
}
|
|
40
48
|
else if (payload.errors && typeof payload.errors === 'object') {
|
|
@@ -106,6 +114,39 @@ function importHint(code, path) {
|
|
|
106
114
|
}
|
|
107
115
|
return '';
|
|
108
116
|
}
|
|
117
|
+
function processHint(code, path) {
|
|
118
|
+
if (code === 'DefinitionInvalid' && /\/bpm\/definitions\/[^/]+\/override(\/|$)/.test(path))
|
|
119
|
+
return " Uyarlama platform doğrulamasından geçmedi; yukarıdaki path [kod] satırlarına bakın. RegionLocked/ParamNotEditable: yol uyarlamaya kapalı — process_override_get yanıtındaki editableScopes'a göre kurun (bölge çocuğu '/nodes[id=<bölge>]/nodes[id=<adım>]/<alan>'); RoleUnknown/WorkflowUnknown: referans bu ortamda yok. Kurallar platform_guide('bpm') §Uyarlama.";
|
|
120
|
+
if (code === 'TaskAlreadyClaimed')
|
|
121
|
+
return ' Görev başkası tarafından üstlenildi ya da siz okuduktan sonra değişti. process_task_get ile yeniden okuyup güncel version değeriyle tekrar deneyin; görev başkasındaysa eylem sizin değildir.';
|
|
122
|
+
if (code === 'TaskStateTransitionNotAllowed')
|
|
123
|
+
return " Görevin durumu bu eylemi almıyor (ör. waiting-info iken approve, tamamlanmış görevde claim). process_task_get'teki state ve actions listesine bakın.";
|
|
124
|
+
if (code === 'TaskCommentRequired')
|
|
125
|
+
return " Bu karar yorum ister (düğümün commentRequired kuralı). Aynı çağrıyı 'comment' ile tekrarlayın.";
|
|
126
|
+
if (code === 'UndoWindowClosed')
|
|
127
|
+
return " Geri alma penceresi kapandı ya da karardan sonra başka bir insan görevi açıldı; geri alma yerine process_instance_update ya da yeni karar gerekir.";
|
|
128
|
+
if (code === 'InstanceStateTransitionNotAllowed')
|
|
129
|
+
return " Örnek bu durumda bu müdahaleyi almaz (ör. tamamlanmış örneği askıya alma). process_instance_get ile state'i okuyun.";
|
|
130
|
+
if (code === 'WithdrawNotAllowed')
|
|
131
|
+
return " Tanımın withdrawPolicy'si geri çekmeye izin vermiyor ya da ilk karar verilmiş.";
|
|
132
|
+
if (code === 'DefinitionInvalid')
|
|
133
|
+
return " Tanım platform doğrulamasından geçmedi; yukarıdaki path [kod] satırlarını düzeltip process_definition_preview ile yeniden doğrulayın.";
|
|
134
|
+
if (code === 'OpenTasksExist')
|
|
135
|
+
return " Kullanıcının ya da birimin açık süreç görevi var. process_handover_preview ile kalemleri görün, process_handover_create ile devredin; sonra işlemi tekrarlayın.";
|
|
136
|
+
if (code === 'HandoverIncomplete')
|
|
137
|
+
return " Kararı olmayan görev grupları mesajda. Her assigned-tasks / candidate-tasks kalemi için karar verin ya da force:true ile geri dönüşü kabul edin: assigned-tasks yöneticiye (yoksa admin-queue), candidate-tasks gruba (aday kalmazsa admin-queue).";
|
|
138
|
+
if (code === 'DelegationOverlap')
|
|
139
|
+
return " Aynı kişinin bu aralık ve süreç filtresiyle çakışan vekaleti var. process_delegation_list ile görüp bitirin ya da aralığı değiştirin.";
|
|
140
|
+
if (code === 'DelegationChainNotAllowed')
|
|
141
|
+
return " Vekalet zincirlenemez: ya vekil aynı aralıkta kendi görevlerini devretmiş ya da vekaleti veren kişi o aralıkta başkasının vekili. Başka vekil ya da aralık seçin.";
|
|
142
|
+
if (code === 'PublishApprovalRequired')
|
|
143
|
+
return " Tanım dört göz yayın politikasında (publishPolicy: four-eyes) ve ortamda process-publish süreci kurulu. Aynı çağrıyı process_definition_publish { reason } ile tekrarlayın: yayın isteği açılır, onaylanınca sürüm aktif olur.";
|
|
144
|
+
if (code === 'ScenarioFailed')
|
|
145
|
+
return " Tanım requireGreenScenarios: true ve adı geçen senaryo bu taslağın sağlamasında yeşil koşmadı. process_scenario_execute { definitionId } ile koşun, sapmaları giderin (ya da senaryoyu process_scenario_update ile güncelleyin), sonra yeniden yayınlayın.";
|
|
146
|
+
if (code === 'BatchOperationNotUndoable')
|
|
147
|
+
return " Yalnız tamamlanmış reassign / extend-due işlemleri bir kez geri alınır; start ve cancel geri alınamaz.";
|
|
148
|
+
return '';
|
|
149
|
+
}
|
|
109
150
|
function pageSchemaHint(code, path) {
|
|
110
151
|
if (code === 'PageSchemaVersionConflictException') {
|
|
111
152
|
return ' Sayfa siz okuduktan sonra değişti ve HİÇBİR op uygulanmadı. page_schema_get ile yeniden okuyup planı tazeleyin, yeni `version` değerini `baseVersion` olarak gönderin.';
|
|
@@ -142,12 +183,20 @@ function bulkRowHint(code) {
|
|
|
142
183
|
function versionConflictHint(code, path) {
|
|
143
184
|
if (code !== 'AggregateVersionConflictException')
|
|
144
185
|
return '';
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
186
|
+
if (/\/bpm\/tasks(\/|$)/.test(path)) {
|
|
187
|
+
return ' Görev siz okuduktan sonra değişti (başka biri üstlendi ya da karar verdi) ve eylem UYGULANMADI. process_task_get ile yeniden okuyun; görev hâlâ size açıksa güncel `version` değeriyle eylemi tekrarlayın, değilse eylem sizin değildir.';
|
|
188
|
+
}
|
|
189
|
+
if (/\/bpm\/definitions\/[^/]+\/override(\/|$)/.test(path)) {
|
|
190
|
+
return ' Uyarlama ya da aktif sürüm siz okuduktan sonra değişti (başka bir yönetici kaydetti ya da geliştirici yeni sürüm yayınladı) ve HİÇBİR op yazılmadı. process_override_get ile yeniden okuyun; conflictState conflicted ise patch yeni sürüme göre kurulmalı. process_override_update aynı çağrıda güncel sürümü okur — patch güncel uyarlamaya uyuyorsa çağrıyı tekrarlayın.';
|
|
191
|
+
}
|
|
192
|
+
const readTool = /\/bpm\/definitions(\/|$)/.test(path) ? 'process_definition_get'
|
|
193
|
+
: /\/bpm\/instances(\/|$)/.test(path) ? 'process_instance_get'
|
|
194
|
+
: /\/entities(\/|$)/.test(path) ? 'entity_get'
|
|
195
|
+
: /\/workflows(\/|$)/.test(path) ? 'workflow_get'
|
|
196
|
+
: /\/flows(\/|$)/.test(path) ? 'flow_get'
|
|
197
|
+
: /\/layouts(\/|$)/.test(path) ? 'layout_get'
|
|
198
|
+
: /\/microservices(\/|$)/.test(path) ? 'microservice_get'
|
|
199
|
+
: 'ilgili *_get';
|
|
151
200
|
return ` Kayıt siz okuduktan sonra değişti ve HİÇBİR op uygulanmadı (mesajdaki beklenen/mevcut sürüm). ${readTool} ile yeniden okuyup patch'i güncel hâle göre kurun, yeni \`version\` değerini \`baseVersion\` olarak gönderin — aynı ops'u eski sürümle tekrar göndermek aradaki değişikliği ezer.`;
|
|
152
201
|
}
|
|
153
202
|
/**
|
|
@@ -177,6 +226,69 @@ function diagnoseKnownFailure(status, message, code, context) {
|
|
|
177
226
|
}
|
|
178
227
|
return '';
|
|
179
228
|
}
|
|
229
|
+
/**
|
|
230
|
+
* Erişim politikası ve alan koruması hataları `InvalidParameterException` kodu altında gelir ve
|
|
231
|
+
* ayırt edici anahtar MESAJDADIR (`AccessScopeNotSupported: entity.create`); koşul ve koruma
|
|
232
|
+
* hatalarının bir kısmı ise kendi exception adını `code`'da taşır. İkisi de burada tek sözlükle
|
|
233
|
+
* bir sonraki adıma çevrilir (rol yönetim şeması g-21).
|
|
234
|
+
*/
|
|
235
|
+
const accessErrorHints = [
|
|
236
|
+
['ConnectDataDefinitionInvalid', " Bağlantı tanımı platform şemasına uymuyor (bilinmeyen anahtar, bozuk enum ya da eksik alan) — mesajdaki yolu düzeltin; kural adları connect-data-* (PAGE-035 · CD-004). Tanım şekli: platform_guide('page') §Kural 1c-3."],
|
|
237
|
+
['ConnectDataPropNotConnected', " Bu prop veri kaynağına bağlı değil — önce connect_data_bind (ya da page_schema_patch set_connect_data) ile bağlayın; bir prop'un bağlantısını connect_data_get okur."],
|
|
238
|
+
['ConnectDataFilterNotAllowed', " Filtre, arama ya da sıralama bu bağlantıda izinli değil: alan tanımın behavior.filterable/searchable listesinde olmalı ve işleç connect_data_describe'daki operators içinde; korumalı alanda yalnız eq · neq · in · notin, korumalı alan sıralanmaz; tanım filtresindeki $parametre params ile verilmeli (CD-002)."],
|
|
239
|
+
['ConnectDataFieldNotEditable', " Alan tanımda seçili ama bağlantı ucundan yazılamaz: edit.editable listesinde değil ya da dosya alanı (dosya bu uçtan hiç yazılmaz). Yazılacaksa connect_data_bind { edit: { editable: [..., '<alan>'] } }; dosyayı file_upload ile yükleyip satırı row_update ile güncelleyin."],
|
|
240
|
+
['ConnectDataFieldNotSelected', " İstenen alan tanımın seçtiği alanlardan değil (fields/expand), lookup ilişki alanında değil ya da yazılan alan edit.editable listesinde değil (expand yolu hiç yazılamaz) — connect_data_get ile tanıma bakın; gerekirse connect_data_bind ile alanı ekleyin ya da edit.editable'a koyun."],
|
|
241
|
+
['ConnectDataWriteDisabled', " Tanımda bu yazma eylemi kapalı (behavior.edit) ya da edit.bulk kapalıyken birden çok satır/kimlik gönderildi — politika izin verse bile motor reddeder. Açmak için connect_data_bind { edit: { <op>: true } } (toplu için bulk: true)."],
|
|
242
|
+
['ConnectDataWriteModeNotAllowed', " Tanımın edit.writeMode'u atomic: satırların hepsi ya da hiçbiri yazılır. Kısmi yazma (satır başına errors[]) için connect_data_bind { edit: { writeMode: 'partial' } }, ya da isteği mode göndermeden atın."],
|
|
243
|
+
['yet: partial.', " Kısmi yazma (mode partial) yalnız built-in veritabanındaki entity'de çalışır — savepoint harici datasource bağlantısını kapsamaz. Bu kaynakta atomic yazın (mode göndermeyin, tanımda writeMode: 'atomic')."],
|
|
244
|
+
['yet: create.', " Bu kaynak türü YAZMAZ: create/update/delete yalnız entity kaynağında çalışır (query · view · workflow · microservice okur). Yazma gereken ekranı entity kaynağına bağlayın, ya da yazmayı bir flow adımına bırakın."],
|
|
245
|
+
['yet: update.', " Bu kaynak türü YAZMAZ: create/update/delete yalnız entity kaynağında çalışır (query · view · workflow · microservice okur). Yazma gereken ekranı entity kaynağına bağlayın, ya da yazmayı bir flow adımına bırakın."],
|
|
246
|
+
['yet: delete.', " Bu kaynak türü YAZMAZ: create/update/delete yalnız entity kaynağında çalışır (query · view · workflow · microservice okur). Yazma gereken ekranı entity kaynağına bağlayın, ya da yazmayı bir flow adımına bırakın."],
|
|
247
|
+
['yet: lookup.', " Lookup yalnız entity kaynağında çalışır: ilişki hücresinin hedef tablosu entity'dir. Query/view/workflow/microservice kaynağında ilişki seçimi yoktur."],
|
|
248
|
+
['yet: datasource.', " Harici datasource (REST) kaynağı motorda YOK (b-33 kapsam dışı): platformda kayıtlı yürütücüsü yoktur. entity · query · view · workflow · microservice türlerinden birini seçin."],
|
|
249
|
+
['ConnectDataSourceUnsupported', " Kaynak bağlanabilir değil. Başlıca nedenler: uca middleware bağlı, yazan tablo/sistem ucu, dönüş şeması yok (workflow'a dizi dönen çıktı DTO'su, query'ye çalıştırılmış örnek gerekir), sorgu salt-okunur tek SELECT değil ya da kaynak zaten connectData microservice'i (tanımı doğrudan bağlayın, connect_data_get ile okuyun). Mesajın sonundaki değer bir op adıysa (create/update/delete/lookup) neden kaynağın bağlanabilirliği değil o işlemi desteklememesidir. connect_data_describe kaynağın ne yapabildiğini söyler."],
|
|
250
|
+
['ConnectDataMicroserviceInUse', " Bu connectData microservice'i başka bir prop'ta, başka bir sayfada ya da bir flow'da kullanılıyor: connect_data_detach tanımı geri koyup microservice'i silmek ister. Kullanımları dependency_graph ile bulup kaldırın, sonra yeniden deneyin."],
|
|
251
|
+
['ConnectDataRelationTargetNotFound', " İlişki alanına yazılan hedef kimlik hedef tabloda yok ya da bu kullanıcı onu göremiyor — yanıtın field'ı düşen alanı söyler. Var olan bir kimliği alanın lookup'ıyla seçin (connect_data_execute op: 'lookup', field: '<alan>'); m2m listesinde her kimlik ayrı denetlenir."],
|
|
252
|
+
['ConnectDataSourceFailed', " Kaynak (query/view/workflow/microservice) çalışırken düştü — hata kaynağın kendisinden gelir: microservice_execute / query_preview / workflow_preview ile kaynağı tek başına koşturup gerçek nedeni görün."],
|
|
253
|
+
['ProtectionOperatorNotAllowed', " Korumalı alanda yalnız eq · neq · in · notin · isnull · isnotnull kullanılır; şifreli modda eşitlik için alanın protection.exactSearch'ü açık olmalı (PROT-001). Kısmi arama için türetilmiş alan — platform_guide('access') §'Arama ve sıralama'."],
|
|
254
|
+
['ProtectionSortNotAllowed', " Korumalı alana göre sıralanamaz (PROT-002); başka bir alana ya da türetilmiş alana göre sıralayın."],
|
|
255
|
+
['ProtectionFieldTypeNotSupported', " Koruma yalnız düz METİN alana konur — ilişki, sayı, çok dilli, birincil anahtar ve identity alanı korunamaz (PROT-003)."],
|
|
256
|
+
['ProtectionDisplayFieldNotAllowed', " Display alanı korunamaz: ilişkilerde görünen ad olarak her yerde düz dönerdi (PROT-003). Önce başka bir alanı display yapın."],
|
|
257
|
+
['ProtectionDerived', " Türetilmiş alan derivedFromFieldId + derivationRule ister; slice length > 0, regex pattern derlenmeli (PROT-004)."],
|
|
258
|
+
['ProtectionChangeNotAllowed', " Var olan alanın korumasını entity_update / entity_patch değiştiremez; dolu satırlar arka plan işiyle dönüştürülür — entity_protection_update (202 + jobId, ilerleme import_status)."],
|
|
259
|
+
['ProtectionMigrationInProgress', " Bu entity'de koruma işi sürüyor; ilerlemeyi GET /{proje}/DataJobs/{id} ile izleyin. İş hata verdiyse AYNI isteği aynı gövdeyle tekrar göndermek kaldığı yerden devam ettirir."],
|
|
260
|
+
['ProtectionKeyUnavailable', " Ortamda alan koruması anahtarı yapılandırılmamış — insan işi: docs/ortam.md §Alan koruması anahtarları."],
|
|
261
|
+
['AccessScopeRequiresOwnerFields', " Entity'de sys_owner_user_id / sys_owner_unit_id yok (ACC-003): owner-fields işi (POST /{proje}/DataJobs/owner-fields/{datasourceId}) ya da scope: 'all' + koşul."],
|
|
262
|
+
['AccessResourceUnknown', " Anahtar kaynak kataloğunda yok — access_resource_list ile 'key' değerini alın (K-31); katalog entity/sayfa yazıldıkça kendiliğinden eşitlenir."],
|
|
263
|
+
['AccessConditionInvalid', " Koşul alanı grant'in entity'sinin korumasız, kataloğa girmiş bir alanı olmalı ve değer işlece uymalı (ACC-001/ACC-002) — alanlar: access_resource_list { type: 'field' }."],
|
|
264
|
+
['AccessScopeNotSupported', " Kapsam yalnız entity.read/update/delete/export/reveal/reassign izinlerinde (ACC-001)."],
|
|
265
|
+
['AccessFieldAccessNotSupported', " fieldAccess yalnız 'field.read' izninde (ACC-001)."],
|
|
266
|
+
['AccessDenyFieldAccessNotSupported', " Reddet grant'i alan erişimi taşımaz; kapsam ve koşul taşıyabilir, o zaman yalnız satır süzer (ACC-001) — koşulsuz scope: 'all' hariç, o kapsamsız gibi keser (ACC-042)."],
|
|
267
|
+
['AccessOverrideRequiresUserWithEndDate', " overridesDeny yalnız subjectType 'user' ve dolu endsAt ile yazılır (ACC-009)."],
|
|
268
|
+
['OrganizationPositionSeatOccupied', " Tek koltuklu pozisyonda aynı dönemde başka bir kalıcı atama var; vekâlet için assignmentType 'acting', kalıcı ikinci kişi için pozisyonu isMultiSeat yapın (TB-96, ORG-003)."],
|
|
269
|
+
['OrganizationStructureNodeInUse', " Düğüm DOLU. BİRİM için: altında aktif alt birim ya da aktif pozisyon var. POZİSYON için ÜÇ sebepten biri: koltukta oturan kullanıcı, o pozisyona bağlı (idari ya da işlevsel) başka bir aktif pozisyon, ya da pozisyonu managerPositionId olarak gösteren bir birim — mesaj hangisi olduğunu söylemez. Sıra yapraktan köke: user_position_replace ile koltukları boşalt → yöneticiyi gösteren birimi organization_unit_update ile managerPositionId'siz yaz → ast pozisyonları organization_position_deactivate → alt birimler → organization_unit_deactivate. Durumu organization_tree_get (includeInactive: true) ve user_position_list gösterir."],
|
|
270
|
+
['UserPositionPeriodInvalid', " Atamanın endsAt'i startsAt'ten sonra olmalı (ORG-003)."],
|
|
271
|
+
['UserPositionPrimaryDuplicate', " Bir kullanıcının en çok bir birincil ataması olur (ORG-003)."],
|
|
272
|
+
['RevealReasonTooShort', " Gerekçe projenin asgari uzunluğunun altında; project_get → revealReasonMinLength (TB-98, ACC-010). Reddedilen deneme denetim defterine REVEAL-DENIED olarak düşer."],
|
|
273
|
+
['EnforcementOffRequiresConfirmation', " off'a geçiş korumalı alanları herkese açık yapar ve satır kapsamını kaldırır; bilinçliyseniz confirm: true (TB-99, ACC-007)."],
|
|
274
|
+
['AccessConditionRequiresScope', " Koşul yalnız kapsamla birlikte yazılır; saf koşul için scope: 'all' (K-11, ACC-001)."],
|
|
275
|
+
['AccessPermissionUnknown', " İzin slug'ı katalogda yok — access_permission_list (katalog yalnız kompleks projede dolu)."],
|
|
276
|
+
['AccessPolicySlugDuplicate', ' Politika slug\'ı projede tekildir; access_policy_list ile mevcut politikayı bulun.'],
|
|
277
|
+
['EntityHasNoOwnerFields', " Entity'de sahiplik alanı (sys_owner_user_id / sys_owner_unit_id) yok, devredilecek bir sahiplik de yok: owner-fields işi (POST /{proje}/DataJobs/owner-fields/{datasourceId}) alanları açar. Basit projede bu alanlar hiç kurulmaz."],
|
|
278
|
+
['OwnershipFieldNotWritable', " Sahiplik alanı satır güncellemesiyle yazılmaz (ORG-006): row_reassign kullanın, ya da uygulama kullanıcısının kapsamıyla devretmek için systemOperation 'entity.row.reassign' olan bir uç + microservice_execute { asUser }."],
|
|
279
|
+
['UserCannotTransferToSelf', " Silinen kullanıcının kayıtları yine kendisine devredilemez: transferToUserId başka bir kullanıcı olmalı, ya da hiç verilmemeli (o zaman sahiplik birime düşer)."],
|
|
280
|
+
['OrganizationStructureParentInactive', " Üst düğüm pasifken alt düğüm etkinleştirilemez: kökten yaprağa ilerleyin — önce üst birimi organization_unit_activate ile, pozisyonda ise önce birimi, sonra idari/işlevsel üst pozisyonu organization_position_activate ile açın."],
|
|
281
|
+
['OrganizationSchemaRootCount', " Şema taslağında TEK kök birim olur: parentId'siz ikinci birim reddedilir. organization_schema_get ile kökü bulup yeni birimi onun (ya da bir altının) parentId'si ile açın."],
|
|
282
|
+
['OrganizationSchemaCycle', " Taslakta döngü var: bir birim/pozisyon kendi atasını ebeveyn gösteriyor. organization_schema_get ile zinciri izleyin."],
|
|
283
|
+
['OrganizationSchemaDuplicate', " Taslakta aynı kod (birim/pozisyon) ya da aynı rol slug'ı iki kez geçiyor; kodlar şema genelinde tekildir."],
|
|
284
|
+
['OrganizationSchemaReference', " Taslakta çözülemeyen referans var: pozisyonun unitId/roleId'si ya da birimin managerPositionId'si taslakta olmayan bir düğümü gösteriyor. Önce hedefi ekleyin, sonra gösteren düğümü yazın."],
|
|
285
|
+
['SystemEntityWriteNotAllowed', " Organizasyon ve erişim tabloları genel satır yoluyla yazılmaz (ORG-001) — platform_guide('access') §'Organizasyon ağacı'."]
|
|
286
|
+
];
|
|
287
|
+
function accessHint(code, message) {
|
|
288
|
+
const text = `${code ?? ''} ${message ?? ''}`;
|
|
289
|
+
const found = accessErrorHints.find(([key]) => text.includes(key));
|
|
290
|
+
return found ? found[1] : '';
|
|
291
|
+
}
|
|
180
292
|
const MaxErrorCauseWalk = 32;
|
|
181
293
|
const MaxErrorPathShown = 6;
|
|
182
294
|
const MaxErrorTextLength = 400;
|
|
@@ -262,16 +374,45 @@ export function mapPlatformError(status, payload, context) {
|
|
|
262
374
|
if (validationMessages.length > 0) {
|
|
263
375
|
return new PlatformApiError(`Doğrulama hatası (${location}): ${validationMessages.join(' ; ')}. Gövde şeması için ilgili ngapps://schemas kaynağına ya da mevcut bir kaydın *_get çıktısına bakın.`, status, code);
|
|
264
376
|
}
|
|
265
|
-
return new PlatformApiError(`Geçersiz istek (${location}): ${apiMessage ?? 'API gövdeyi reddetti.'}${mergeRevertHint(code)}${importHint(code, context.path)}`, status, code);
|
|
377
|
+
return new PlatformApiError(`Geçersiz istek (${location}): ${apiMessage ?? 'API gövdeyi reddetti.'}${mergeRevertHint(code)}${importHint(code, context.path)}${accessHint(code, apiMessage)}`, status, code);
|
|
266
378
|
}
|
|
267
379
|
if (status === 401) {
|
|
380
|
+
// Uygulama oturumu 401'i gateway kimliğinin değil uygulama kullanıcısının sorunudur; USERNAME/PASSWORD
|
|
381
|
+
// ipucu burada yanlış yere baktırırdı (g-31).
|
|
382
|
+
if (code === 'AppSessionRequiredException' || code === 'AppSessionInvalidException') {
|
|
383
|
+
return new PlatformApiError(`Uygulama oturumu ${code === 'AppSessionRequiredException' ? 'yok' : 'geçersiz ya da süresi dolmuş'} (${location}): ${apiMessage ?? ''} Kompleks proje + enforce ortamda oturumsuz her uç ve sayfa isteği 401 döner. Parolasız kullanıcı gözü için aynı çağrıyı asUserId: '<users satır id'si>' ile tekrarlayın (development/dal); oturum açılmış üye için access_session_create ile giriş yapıp tokenRefs'teki tok_… değerini asUser olarak verin.`, status, code);
|
|
384
|
+
}
|
|
385
|
+
// Yanlış e-posta/parola 2026-09-22'den beri kendi kodunu taşır (backend ADR 0004, K-3): oturum
|
|
386
|
+
// hatası değildir, yenileme ya da gateway parolası ipucu yanlış yere baktırırdı.
|
|
387
|
+
if (code === 'AppSignInFailedException') {
|
|
388
|
+
return new PlatformApiError(`Uygulama girişi reddedildi (${location}): e-posta ya da parola yanlış, ya da kullanıcı bu projenin 'users' tablosunda yok. ${apiMessage ?? ''} Yeni kompleks projede users boş gelir — platform_guide('getting-started') §'Kompleks projede ilk kullanıcı'. Hız sınırı 5 dk'da 10 denemedir; tekrar denemeden önce kimlik bilgisini doğrulayın.`, status, code);
|
|
389
|
+
}
|
|
390
|
+
if (code === 'ReauthenticationRequiredException') {
|
|
391
|
+
return new PlatformApiError(`İmza için yeniden kimlik doğrulaması reddedildi (${location}): password (verildiyse email de) görevi imzalayan kullanıcının gerçek parolası olmalı; gateway token'ı yenilemez, istek tekrarlanmadı (BPM-018).`, status, code);
|
|
392
|
+
}
|
|
268
393
|
const hint = context.authMode === 'local'
|
|
269
394
|
? 'Gateway konfigürasyonundaki USERNAME/PASSWORD doğru mu kontrol edin.'
|
|
270
395
|
: 'İstemciden geçirilen Authorization header süresi dolmuş olabilir; yeni token alıp MCP kaydını güncelleyin.';
|
|
271
396
|
return new PlatformApiError(`Kimlik doğrulanamadı (${location}). ${hint}`, status, code);
|
|
272
397
|
}
|
|
273
398
|
if (status === 403) {
|
|
274
|
-
|
|
399
|
+
if (code === 'PublishApprovalRequired') {
|
|
400
|
+
return new PlatformApiError(`Yayın dört göz onayı istiyor (${location}) [PublishApprovalRequired].${processHint(code, context.path)}`, status, code);
|
|
401
|
+
}
|
|
402
|
+
const runtimeHint = code === 'CsrfTokenMismatchException'
|
|
403
|
+
? " Oturum çerezle geldi ve durum değiştiren istekte X-ZY-CSRF yok: asUser kullanın (Bearer'lı istek CSRF istemez)."
|
|
404
|
+
: code === 'AccessQueryNotScopedException'
|
|
405
|
+
? ' Sorgu kapsamlı/korumalı bir tabloya dokunuyor: kullanıcıya özel veri için table kaynağı, tüm veriyi özetleyen rapor için sorguda isUnrestricted (ACC-005).'
|
|
406
|
+
: code === 'AccessFieldWriteDeniedException' || code === 'AccessFilterDeniedException' || code === 'AccessRevealDeniedException'
|
|
407
|
+
? ' Alan erişimi reddetti: access_preview fieldAccess (canWrite · canFilter); reveal için kapsamlı entity.reveal grant\'i.'
|
|
408
|
+
: code === 'AccessOverridePermissionRequiredException'
|
|
409
|
+
? " Bu istek bir kullanıcı istisnasını yazıyor, genişletiyor ya da istisna taşıyan politikanın grant'lerine dokunuyor; access.override.manage izni gerekir (ACC-009)."
|
|
410
|
+
: context.api === 'tenant' && code === 'ForbiddenException'
|
|
411
|
+
? ' Karar motoru reddetti; mesaj HANGİ iznin reddettiğini bilinçli olarak söylemez. Nedenini access_preview { userId, permission, resourceKey } ile sorun.'
|
|
412
|
+
: '';
|
|
413
|
+
const forbidden = new PlatformApiError(`Yetki yok (${location}). Token'ın sahibi bu işlem için yetkili değil.${runtimeHint}${runtimeHint ? '' : accessHint(code, apiMessage)}`, status, code);
|
|
414
|
+
forbidden.apiMessage = apiMessage;
|
|
415
|
+
return forbidden;
|
|
275
416
|
}
|
|
276
417
|
if (status === 404) {
|
|
277
418
|
const environmentHint = context.environment
|
|
@@ -280,6 +421,17 @@ export function mapPlatformError(status, payload, context) {
|
|
|
280
421
|
if (context.method !== 'GET' && /\/flows(\/|$)/.test(context.path)) {
|
|
281
422
|
return new PlatformApiError(`Adımda referans verilen kayıt çözümlenemedi (${location}). Bu uçta 404 akışın kendisini DEĞİL bir adım argümanını kastediyor: flow-trigger adımının args'ında ne flowId ne triggerElementId var (ya da triggerElementId sayfadaki hiçbir elementId ile eşleşmiyor), setter adımının iteratees[].element'i bulunamıyor, ya da bir adım silinmiş bir komponente bakıyor. Adım argümanlarını page_schema_get'teki elementId'lerle karşılaştırın; flow-trigger'ın iki geçerli şekli için platform_guide('page') Kural 1c.`, status, code);
|
|
282
423
|
}
|
|
424
|
+
// Yalnız ConnectData kodlarının ipucu bu dala girer: Access*/Protection* kodlarıyla gelen bir 404
|
|
425
|
+
// buradan geçerse environment/import ipuçlarını ve '*_list ile doğrulayın' yönlendirmesini kaybeder (S8 incelemesi M-10).
|
|
426
|
+
const connectDataHint = code?.startsWith('ConnectData') ? accessHint(code, apiMessage) : '';
|
|
427
|
+
if (connectDataHint || (context.method === 'POST' && /\/connect-data\/[^/]+\/[^/]+\//.test(context.path))) {
|
|
428
|
+
return new PlatformApiError(`Kayıt bulunamadı (${location}): ${apiMessage ?? ''}${connectDataHint || " Bağlantı ucunda 404: uygulama/sayfa slug'ı ya da elementId yolu çözmüyor, veya yazılan satır ya da ilişki hedefi bu öznenin kapsamında değil (K-16). Kapsamdaki satırları connect_data_execute op: 'list' (aynı asUser) ile görün; ilişki hedefini op: 'lookup' ile seçin."}`, status, code);
|
|
429
|
+
}
|
|
430
|
+
// İlişki hedefi yok (o2m RelationTargetRowNotFound, m2m ManyToManyTargetRowNotFound): kayıt/environment değil,
|
|
431
|
+
// gövdedeki bir ilişki kimliği düşüyor — mesaj alanın adını taşır (38/U-6).
|
|
432
|
+
if (code && /^(RelationTargetRowNotFound|ManyToManyTargetRowNotFound)(Exception)?$/.test(code)) {
|
|
433
|
+
return new PlatformApiError(`İlişki hedefi bulunamadı (${location}): ${apiMessage ?? ''} Gövdedeki ilişki alanına yazılan kimlik hedef tabloda yok; var olan kimlikleri hedef entity'de row_list ile alın (m2m alanı tam liste bekler: ['<id>', …]). Hiçbir satır yazılmadı.`, status, code);
|
|
434
|
+
}
|
|
283
435
|
return new PlatformApiError(`Kayıt bulunamadı (${location}).${environmentHint} id/slug değerini ilgili *_list tool'uyla doğrulayın.${importHint(code, context.path)}`, status, code);
|
|
284
436
|
}
|
|
285
437
|
if (status === 409 || status === 422) {
|
|
@@ -288,7 +440,7 @@ export function mapPlatformError(status, payload, context) {
|
|
|
288
440
|
: '';
|
|
289
441
|
const validationMessages = collectValidationMessages(body);
|
|
290
442
|
const detail = validationMessages.length > 0 ? ` → ${validationMessages.join(' ; ')}` : '';
|
|
291
|
-
const rejected = new PlatformApiError(`İş kuralı reddetti (${location}): ${apiMessage ?? 'çakışma'}${code ? ` [${code}]` : ''}${detail}${projectConflictHint}${pageSchemaHint(code, context.path)}${bulkRowHint(code)}${importHint(code, context.path)}${versionConflictHint(code, context.path)}${mergeHint(context.path)}`, status, code);
|
|
443
|
+
const rejected = new PlatformApiError(`İş kuralı reddetti (${location}): ${apiMessage ?? 'çakışma'}${code ? ` [${code}]` : ''}${detail}${projectConflictHint}${pageSchemaHint(code, context.path)}${processHint(code, context.path)}${bulkRowHint(code)}${importHint(code, context.path)}${versionConflictHint(code, context.path)}${mergeHint(context.path)}${accessHint(code, apiMessage)}`, status, code);
|
|
292
444
|
if (typeof body.rowIndex === 'number')
|
|
293
445
|
rejected.rowIndex = body.rowIndex;
|
|
294
446
|
return rejected;
|
package/dist/platform/http.js
CHANGED
|
@@ -6,6 +6,20 @@ import { randomUUID } from 'node:crypto';
|
|
|
6
6
|
import { request } from 'undici';
|
|
7
7
|
export const requestAuthStorage = new AsyncLocalStorage();
|
|
8
8
|
const hiddenResponseHeaders = new Set(['set-cookie', 'authorization', 'proxy-authorization']);
|
|
9
|
+
/**
|
|
10
|
+
* g-08: yürütme yanıtının varsayılan başlıkları — işin okuduğu olanlar. `total-count` sayfalı tablo
|
|
11
|
+
* akışlarının sözleşmesidir (İK koşusu M-8), `location` yönlendirmenin, `x-request-id`/`traceparent`
|
|
12
|
+
* platform günlüğünde aynı isteği bulmanın anahtarıdır. Cloudflare ve sunucu başlıkları (`cf-ray`,
|
|
13
|
+
* `server`, `alt-svc`, `date` …) ajan bağlamında yer kaplıyordu ve hiçbir karar onlara bakmıyordu.
|
|
14
|
+
*/
|
|
15
|
+
const essentialResponseHeaders = new Set(['content-type', 'content-length', 'content-disposition', 'location', 'total-count', 'x-request-id', 'traceparent']);
|
|
16
|
+
export function headersForMode(headers, mode) {
|
|
17
|
+
if (mode === 'none')
|
|
18
|
+
return undefined;
|
|
19
|
+
if (mode === 'all')
|
|
20
|
+
return headers;
|
|
21
|
+
return Object.fromEntries(Object.entries(headers).filter(([name]) => essentialResponseHeaders.has(name)));
|
|
22
|
+
}
|
|
9
23
|
/**
|
|
10
24
|
* Sayfalı tablo akışları `Total-Count` header'ına bağlı ama execute ailesi yalnız gövdeyi
|
|
11
25
|
* döndürüyordu; sözleşme hiçbir tool'dan doğrulanamıyordu (İK koşusu M-8). Gizli header'lar
|
|
@@ -153,13 +167,15 @@ export class Platform {
|
|
|
153
167
|
body = JSON.stringify(options.body);
|
|
154
168
|
}
|
|
155
169
|
const startedAt = Date.now();
|
|
156
|
-
logStep('istek →', `${method} ${url.href} · ${api} api${options.environment ? ` · env=${options.environment}` : ''}${options.anonymous ? ' · ANONİM (Authorization header YOK)' : ''}${isRetry ? ' · 401 sonrası tekrar' : ''}`,
|
|
157
|
-
? [
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
?
|
|
161
|
-
: options.
|
|
162
|
-
|
|
170
|
+
logStep('istek →', `${method} ${url.href} · ${api} api${options.environment ? ` · env=${options.environment}` : ''}${options.anonymous ? ' · ANONİM (Authorization header YOK)' : ''}${isRetry ? ' · 401 sonrası tekrar' : ''}`, options.hideRequestBody === true
|
|
171
|
+
? ['gövde: (gizli — kimlik bilgisi taşıyor, günlüğe yazılmaz)']
|
|
172
|
+
: logsDetail() && (options.multipart || options.form || options.body !== undefined)
|
|
173
|
+
? [`gövde: ${renderJson(options.multipart
|
|
174
|
+
? describeMultipart(options.multipart)
|
|
175
|
+
: options.form
|
|
176
|
+
? Object.fromEntries(options.form)
|
|
177
|
+
: options.body)}`]
|
|
178
|
+
: []);
|
|
163
179
|
let statusCode;
|
|
164
180
|
let payload;
|
|
165
181
|
let responseBytes = 0;
|
|
@@ -196,7 +212,12 @@ export class Platform {
|
|
|
196
212
|
logStep('yanıt ←', `HTTP ${statusCode} · ${Date.now() - startedAt} ms · ${responseBytes} B`, statusCode >= 400 || logsDetail()
|
|
197
213
|
? [`gövde: ${options.expectBinary && statusCode < 400 ? `<ikili içerik · ${responseBytes} B>` : renderJson(payload)}`]
|
|
198
214
|
: []);
|
|
199
|
-
|
|
215
|
+
// Çağıranın kendi Authorization'ı (asUser), imza yeniden kimlik doğrulaması ya da uygulama girişinin yanlış
|
|
216
|
+
// parolası (AppSignInFailedException, access.session.create — 2026-09-22) 401'i gateway token'ının süresiyle
|
|
217
|
+
// ilgili değildir: tekrar aynı reddi alır, üstelik parola denemesi iki kez sayılır (Faz 8 incelemesi).
|
|
218
|
+
const retriesWithFreshToken = callerHeaders.authorization === undefined
|
|
219
|
+
&& !['ReauthenticationRequiredException', 'AppSignInFailedException'].includes(String(payload?.code));
|
|
220
|
+
if (statusCode === 401 && this.config.authMode === 'local' && !isRetry && !options.anonymous && retriesWithFreshToken) {
|
|
200
221
|
this.tokens.invalidate();
|
|
201
222
|
logStep('token ⟳', 'token reddedildi — önbellek boşaltıldı, istek yeni token ile bir kez daha denenecek');
|
|
202
223
|
return this.send(api, method, path, options, raw, true);
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { PlatformApiError } from './errors.js';
|
|
2
|
+
/** `NgApps.Infrastructure/Domain/HttpHeaders.cs` `ImpersonatedUserId`. */
|
|
3
|
+
export const IMPERSONATED_USER_HEADER = 'impersonated-user-id';
|
|
4
|
+
/**
|
|
5
|
+
* 38/M-1 — kompleks projede parolasız kullanıcı gözü. `AppSessionMiddleware` (Tenant.API, satır 76)
|
|
6
|
+
* production DIŞINDA, kimliği doğrulanmış platform isteği bu başlığı taşırsa ve istekte geçerli bir
|
|
7
|
+
* uygulama jetonu YOKSA uygulama öznesini o kullanıcı olarak kurar; `UserMiddleware` başlığı
|
|
8
|
+
* production'da Execute eyleminde düşürür. Bu yüzden:
|
|
9
|
+
*
|
|
10
|
+
* - `asUser` (uygulama jetonu) ile birlikte verilemez: jeton geçerliyse platform başlığı hiç okumaz ve
|
|
11
|
+
* istek jetonun sahibi olarak gider — hangi kimliğin kazandığı çağırana belirsiz kalırdı.
|
|
12
|
+
* - Runtime aracında production hedefi istek gitmeden durur: başlık orada düşürülür ve istek SESSİZCE
|
|
13
|
+
* gateway'in yönetici kimliğiyle giderdi.
|
|
14
|
+
* - Kullanıcı projede yoksa platform başlığı yine sessizce yok sayar (`userRepository.FindByIdAsync`
|
|
15
|
+
* null) — aynı sessiz düşme; gateway kimliği bir kez okur (GET /users/{id}).
|
|
16
|
+
*/
|
|
17
|
+
export async function impersonationHeaders(platform, tool, project, environment, input, options) {
|
|
18
|
+
const { asUserId } = input;
|
|
19
|
+
if (asUserId === undefined)
|
|
20
|
+
return undefined;
|
|
21
|
+
if (input.asUser !== undefined) {
|
|
22
|
+
throw new PlatformApiError(`${tool}: asUserId ile asUser birlikte verilemez — asUser geçerli bir uygulama jetonuysa platform impersonated-user-id başlığını okumaz ve istek jetonun sahibi olarak gider. Parolasız kullanıcı gözü için yalnız asUserId, oturum açılmış üye için yalnız asUser verin.`, 400);
|
|
23
|
+
}
|
|
24
|
+
if (input.anonymous === true) {
|
|
25
|
+
throw new PlatformApiError(`${tool}: asUserId ile anonymous: true birlikte verilemez — biri kimliksiz isteği, diğeri belirli bir kullanıcının gözünü ister.`, 400);
|
|
26
|
+
}
|
|
27
|
+
if (input.ownAuthorization === true) {
|
|
28
|
+
throw new PlatformApiError(`${tool}: asUserId ile headers.Authorization birlikte verilemez — kendi jetonunuzla giden istekte platform başlığı değil jetonun sahibini özne yapar. Birini seçin.`, 400);
|
|
29
|
+
}
|
|
30
|
+
if (options.runtime && platform.resolveEnvironment(environment) === 'production') {
|
|
31
|
+
throw new PlatformApiError(`${tool}: asUserId production'da kullanılamaz — platform impersonated-user-id başlığını production'da düşürür ve istek sessizce gateway'in yönetici kimliğiyle giderdi. Kullanıcı gözüyle doğrulamayı development'ta (ya da bir dalda) yapın; production'da o kullanıcı olarak sınamak için access_session_create + asUser.`, 400);
|
|
32
|
+
}
|
|
33
|
+
let user;
|
|
34
|
+
try {
|
|
35
|
+
user = (await platform.tenant('GET', project, `/users/${encodeURIComponent(asUserId)}`, { environment }))?.data;
|
|
36
|
+
}
|
|
37
|
+
catch (error) {
|
|
38
|
+
if (!(error instanceof PlatformApiError) || error.status !== 404)
|
|
39
|
+
throw error;
|
|
40
|
+
}
|
|
41
|
+
if (!user) {
|
|
42
|
+
throw new PlatformApiError(`${tool}: asUserId '${asUserId}' bu projede bir kullanıcı değil — platform tanımadığı kimliği sessizce yok sayar ve istek gateway'in kimliğiyle giderdi. Kullanıcı id'sini user_list ile doğrulayın.`, 404);
|
|
43
|
+
}
|
|
44
|
+
return { [IMPERSONATED_USER_HEADER]: asUserId };
|
|
45
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
/*
|
|
3
|
+
* Bazı MCP istemcileri dizi parametresini JSON METNİ olarak gönderir ve bunu parametre adına göre
|
|
4
|
+
* tutarsız yapar: `page_schema_patch` aynı gövdeyi `ops` ile kabul edip `operations` ile
|
|
5
|
+
* "Expected array, received string" diye reddediyordu; `flow_create` `steps`'i kabul edip
|
|
6
|
+
* `errorSteps`'i reddediyordu (Senaryo 02 koşusu, S2-26 / TB-118). Şema tarafında metni çözersek
|
|
7
|
+
* hangi adın kullanıldığı ve istemcinin ne yaptığı önemsizleşir.
|
|
8
|
+
*/
|
|
9
|
+
export function jsonArray(schema) {
|
|
10
|
+
return z.preprocess(value => {
|
|
11
|
+
if (typeof value !== 'string')
|
|
12
|
+
return value;
|
|
13
|
+
const trimmed = value.trim();
|
|
14
|
+
if (!trimmed.startsWith('['))
|
|
15
|
+
return value;
|
|
16
|
+
try {
|
|
17
|
+
const parsed = JSON.parse(trimmed);
|
|
18
|
+
return Array.isArray(parsed) ? parsed : value;
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
return value;
|
|
22
|
+
}
|
|
23
|
+
}, schema);
|
|
24
|
+
}
|
|
@@ -1,8 +1,36 @@
|
|
|
1
1
|
import { compactDiagnostics } from './diagnostics.js';
|
|
2
|
+
import { argumentNotes } from './argumentAliases.js';
|
|
3
|
+
import { tokenTtlMinutes } from './tokenVault.js';
|
|
2
4
|
import { logError, logStep } from '../observability/trace.js';
|
|
3
5
|
import { PlatformApiError, describeErrorBody } from './errors.js';
|
|
4
6
|
import { PolicyBlockError } from '../knowledge/policy.js';
|
|
5
7
|
import { maskSecrets } from './secrets.js';
|
|
8
|
+
const STACK_FRAMES_KEPT = 3;
|
|
9
|
+
/**
|
|
10
|
+
* Platformun hata gövdesindeki `stackTrace` yüzlerce satırdır ve çağıranın işine yarayan kısmı
|
|
11
|
+
* ilk birkaç karedir: hatayı atan servis ve satır. Tamamını taşımak uzun bir koşuda bağlamın
|
|
12
|
+
* büyük bölümünü yer (2026-09-19 senaryo koşusunda tek 404 ~4 bin token). Gateway izi ilk
|
|
13
|
+
* STACK_FRAMES_KEPT kareye indirir ve kaç karenin düştüğünü söyler; tam iz platform günlüğündedir.
|
|
14
|
+
*/
|
|
15
|
+
export function collapseStackTraces(value) {
|
|
16
|
+
if (Array.isArray(value))
|
|
17
|
+
return value.map(collapseStackTraces);
|
|
18
|
+
if (value && typeof value === 'object') {
|
|
19
|
+
const result = {};
|
|
20
|
+
for (const [key, entry] of Object.entries(value)) {
|
|
21
|
+
if (key === 'stackTrace' && typeof entry === 'string') {
|
|
22
|
+
const frames = entry.split('\n');
|
|
23
|
+
result[key] = frames.length <= STACK_FRAMES_KEPT
|
|
24
|
+
? entry
|
|
25
|
+
: `${frames.slice(0, STACK_FRAMES_KEPT).join('\n')}\n … (+${frames.length - STACK_FRAMES_KEPT} kare gateway tarafından kırpıldı; tam iz platform günlüğünde)`;
|
|
26
|
+
continue;
|
|
27
|
+
}
|
|
28
|
+
result[key] = collapseStackTraces(entry);
|
|
29
|
+
}
|
|
30
|
+
return result;
|
|
31
|
+
}
|
|
32
|
+
return value;
|
|
33
|
+
}
|
|
6
34
|
export function prune(value) {
|
|
7
35
|
if (Array.isArray(value))
|
|
8
36
|
return value.map(prune);
|
|
@@ -140,7 +168,7 @@ function asStructured(payload) {
|
|
|
140
168
|
return { data: payload };
|
|
141
169
|
}
|
|
142
170
|
export function toolResult(payload, config, expectFailure) {
|
|
143
|
-
const pruned = prune(compactDiagnostics(payload));
|
|
171
|
+
const pruned = collapseStackTraces(prune(compactDiagnostics(payload)));
|
|
144
172
|
const { payload: safe, masked, tokenRefs } = maskSecrets(pruned);
|
|
145
173
|
const runtimeErrors = runtimeErrorSummary(safe);
|
|
146
174
|
if (runtimeErrors.length > 0) {
|
|
@@ -162,15 +190,17 @@ export function toolResult(payload, config, expectFailure) {
|
|
|
162
190
|
...(runtimeErrors.length > 0 ? { runtimeErrors } : {}),
|
|
163
191
|
...(runtimeErrors.length > 0 && expectFailure === true ? { expectedFailure: true } : {}),
|
|
164
192
|
...(masked.length > 0 ? { gatewayMasked: masked } : {}),
|
|
165
|
-
...(Object.keys(tokenRefs).length > 0 ? { tokenRefs, tokenRefNote:
|
|
166
|
-
...(truncated ? { truncated: true, truncationNote: 'Liste, yanıt boyutu sınırı nedeniyle kırpıldı; sayfalama ya da daha dar filtre kullanın.' } : {})
|
|
193
|
+
...(Object.keys(tokenRefs).length > 0 ? { tokenRefs, tokenRefNote: `Maskelenen JWT'ler gateway içinde tutuldu; bu referansları microservice_execute / microservice_debug / page_runtime_get / page_screenshot 'asUser' alanına verebilirsiniz (${tokenTtlMinutes()} dk geçerli; kompleks projede referans access_session_create'ten gelir). Jetonun kendisi yanıta ve günlüğe hiç girmez.` } : {}),
|
|
194
|
+
...(truncated ? { truncated: true, truncationNote: 'Liste, yanıt boyutu sınırı nedeniyle kırpıldı; sayfalama ya da daha dar filtre kullanın.' } : {}),
|
|
195
|
+
...(argumentNotes().aliasesApplied.length > 0 ? { aliasesApplied: argumentNotes().aliasesApplied } : {}),
|
|
196
|
+
...(argumentNotes().bodyFile ? { bodyFile: argumentNotes().bodyFile } : {})
|
|
167
197
|
};
|
|
168
198
|
return {
|
|
169
199
|
content: [{ type: 'text', text: JSON.stringify(enriched) }],
|
|
170
200
|
structuredContent: enriched
|
|
171
201
|
};
|
|
172
202
|
}
|
|
173
|
-
export function toolError(error) {
|
|
203
|
+
export function toolError(error, expectFailure) {
|
|
174
204
|
logError(error);
|
|
175
205
|
// Politika durdurması bilinçli bir karardır, "beklenmeyen" değil (33/S-5).
|
|
176
206
|
const message = error instanceof PlatformApiError || error instanceof PolicyBlockError
|
|
@@ -178,8 +208,12 @@ export function toolError(error) {
|
|
|
178
208
|
: error instanceof Error
|
|
179
209
|
? `Beklenmeyen gateway hatası: ${error.message}`
|
|
180
210
|
: `Beklenmeyen gateway hatası: ${String(error)}`;
|
|
211
|
+
// Kasıtlı negatif testte (bayat version 409, yetkisiz kişi 403) platform hatası da bir KANITTIR:
|
|
212
|
+
// ölçüm aracı bu satırı plansız hata saymasın diye işaret taşınır.
|
|
213
|
+
const isExpectedPlatformError = expectFailure === true && (error instanceof PlatformApiError || error instanceof PolicyBlockError);
|
|
181
214
|
return {
|
|
182
215
|
content: [{ type: 'text', text: message }],
|
|
216
|
+
...(isExpectedPlatformError ? { structuredContent: { expectedFailure: true } } : {}),
|
|
183
217
|
isError: true
|
|
184
218
|
};
|
|
185
219
|
}
|
|
@@ -195,7 +229,7 @@ export function handle(config, run) {
|
|
|
195
229
|
return toolResult(await run(args), config, expectFailure);
|
|
196
230
|
}
|
|
197
231
|
catch (error) {
|
|
198
|
-
return toolError(error);
|
|
232
|
+
return toolError(error, args?.expectFailure === true);
|
|
199
233
|
}
|
|
200
234
|
};
|
|
201
235
|
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sayfa okumalarının belleği. İki soru cevaplar:
|
|
3
|
+
*
|
|
4
|
+
* - "Bu sayfa sürümü kırpık okundu mu" (ZM-28, PAGE-042): `page_schema_get(mode:'full',
|
|
5
|
+
* allowTruncated:true)` sığmayan şemayı kırparak döndüğünde anahtar işaretlenir; aynı sürümü
|
|
6
|
+
* `baseVersion` olarak veren `page_schema_patch` uyarı taşır — plan, düşen düğümleri görmeden
|
|
7
|
+
* yapılmış olabilir.
|
|
8
|
+
* - "Bu gateway sayfanın hangi sürümünü en son gördü" (PAGE-043, `plans/mobil-plantcare-acigi`
|
|
9
|
+
* g-05): `page_get`, `page_schema_get`, `page_schema_patch`'in `newVersion`'ı ve `page_update`'in
|
|
10
|
+
* kendi yazdığı sürüm hatırlanır; `page_update` `baseVersion` verilmediğinde onu gönderir. Hiç
|
|
11
|
+
* okunmamış sayfaya sürümsüz tam gövde yazılmaz.
|
|
12
|
+
*
|
|
13
|
+
* SÜREÇ İÇİNDEDİR ve yalnız anahtar/sürüm tutar (gövde yok); `CaptureMemory` ile aynı halka:
|
|
14
|
+
* yeniden yazılan anahtar sona gider, en eskisi düşer.
|
|
15
|
+
*/
|
|
16
|
+
export function schemaReadKey(project, environment, pageId, version) {
|
|
17
|
+
return `${project}/${environment}/${pageId}@${version}`;
|
|
18
|
+
}
|
|
19
|
+
export function pageReadKey(project, environment, pageId) {
|
|
20
|
+
return `${project}/${environment}/${pageId}`;
|
|
21
|
+
}
|
|
22
|
+
export class SchemaReadMemory {
|
|
23
|
+
limit;
|
|
24
|
+
truncated = new Set();
|
|
25
|
+
versions = new Map();
|
|
26
|
+
constructor(limit = 200) {
|
|
27
|
+
this.limit = limit;
|
|
28
|
+
}
|
|
29
|
+
rememberVersion(key, version) {
|
|
30
|
+
this.versions.delete(key);
|
|
31
|
+
this.versions.set(key, version);
|
|
32
|
+
while (this.versions.size > this.limit) {
|
|
33
|
+
const oldest = this.versions.keys().next().value;
|
|
34
|
+
if (oldest === undefined)
|
|
35
|
+
break;
|
|
36
|
+
this.versions.delete(oldest);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
lastVersion(key) {
|
|
40
|
+
return this.versions.get(key);
|
|
41
|
+
}
|
|
42
|
+
markTruncated(key) {
|
|
43
|
+
this.truncated.delete(key);
|
|
44
|
+
this.truncated.add(key);
|
|
45
|
+
while (this.truncated.size > this.limit) {
|
|
46
|
+
const oldest = this.truncated.values().next().value;
|
|
47
|
+
if (oldest === undefined)
|
|
48
|
+
break;
|
|
49
|
+
this.truncated.delete(oldest);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
wasTruncated(key) {
|
|
53
|
+
return this.truncated.has(key);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* SÜREÇ düzeyinde tek bellek: HTTP taşıması her POST için yeni bir gateway sunucusu kurduğundan
|
|
58
|
+
* (`sharedCaptureMemory` notu) okuma ile yazma ayrı `registerPageTools` çağrılarında koşar.
|
|
59
|
+
*/
|
|
60
|
+
export const sharedSchemaReadMemory = new SchemaReadMemory();
|
package/dist/platform/secrets.js
CHANGED
|
@@ -3,6 +3,13 @@
|
|
|
3
3
|
* anahtarları da yakalıyordu. Günlükte zararsızdı, tool yanıtında veri kaybı olurdu.
|
|
4
4
|
*/
|
|
5
5
|
export const secretKeyPattern = /password|passwd|secret|token|authorization|api[-_]?key|credential|connection[-_]?string/i;
|
|
6
|
+
/**
|
|
7
|
+
* Sırrın DEĞERİNİ değil adını, yerini ya da türünü taşıyan anahtarlar: `credentialRef`, `tokenUrl`,
|
|
8
|
+
* `apiKeyHeader`, `secretFields`, `credentialKind`. Maskelenirlerse süreç tanımındaki
|
|
9
|
+
* `connector.credentialRef` get → update turunda `***` olarak geri yazılır ve düğüm kasayı bulamaz
|
|
10
|
+
* (BPM-017, Faz 8 incelemesi). Yalnız yanıt maskesinde uygulanır; günlük maskesi (`trace.ts`) geniş kalır.
|
|
11
|
+
*/
|
|
12
|
+
export const secretNameKeyPattern = /(ref|refs|url|uri|header|fields|kind|name|names|id|ids)$/i;
|
|
6
13
|
import { looksLikeJwt, rememberToken } from './tokenVault.js';
|
|
7
14
|
const maskedValue = '***';
|
|
8
15
|
const maxPaths = 20;
|
|
@@ -16,7 +23,7 @@ function walk(value, path, masked, refs, depth) {
|
|
|
16
23
|
const result = {};
|
|
17
24
|
for (const [key, entry] of Object.entries(value)) {
|
|
18
25
|
const child = path === '' ? key : `${path}.${key}`;
|
|
19
|
-
if (secretKeyPattern.test(key) && entry !== null && entry !== undefined && entry !== '') {
|
|
26
|
+
if (secretKeyPattern.test(key) && !secretNameKeyPattern.test(key) && entry !== null && entry !== undefined && entry !== '') {
|
|
20
27
|
if (masked.length < maxPaths)
|
|
21
28
|
masked.push(child);
|
|
22
29
|
// Maskelenen değer bir JWT ise kimlik testinin yolu kapanmasın: jeton kasada tutulur ve
|
|
@@ -38,3 +45,12 @@ export function maskSecrets(payload) {
|
|
|
38
45
|
const tokenRefs = {};
|
|
39
46
|
return { payload: walk(payload, '', masked, tokenRefs, 0), masked, tokenRefs };
|
|
40
47
|
}
|
|
48
|
+
/**
|
|
49
|
+
* Serbest METİNDEKİ kimlik parçaları (konsol satırı, hata yığını): JWT biçimli dize ve `Bearer …`.
|
|
50
|
+
* `maskSecrets` anahtar adına bakar; tarayıcı konsolu anahtarsız düz metin taşır (g-06).
|
|
51
|
+
*/
|
|
52
|
+
export function maskSecretText(text) {
|
|
53
|
+
return text
|
|
54
|
+
.replace(/Bearer\s+[A-Za-z0-9._~+/=-]+/gi, `Bearer ${maskedValue}`)
|
|
55
|
+
.replace(/\b[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\b/g, maskedValue);
|
|
56
|
+
}
|