@appaflytech/zeyrek-ai-mcp 0.0.5 → 0.0.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +2 -2
- package/.env.production.example +9 -1
- package/README.md +14 -38
- package/dist/config.js +36 -3
- package/dist/knowledge/backendConstraints.js +982 -0
- package/dist/knowledge/componentCatalog.js +549 -92
- package/dist/knowledge/componentEvents.js +120 -25
- package/dist/knowledge/coverage.js +43 -0
- package/dist/knowledge/flowGuards.js +30 -0
- package/dist/knowledge/functionGuards.js +134 -0
- package/dist/knowledge/guideSections.js +49 -0
- package/dist/knowledge/hydrate.js +97 -0
- package/dist/knowledge/languageCodes.js +36 -0
- package/dist/knowledge/languages.js +105 -0
- package/dist/knowledge/layoutSummary.js +10 -0
- package/dist/knowledge/localization.js +51 -0
- package/dist/knowledge/normalize.js +500 -16
- package/dist/knowledge/outlines.js +167 -12
- package/dist/knowledge/policy.js +14 -5
- package/dist/knowledge/propTypes.js +80 -0
- package/dist/knowledge/rules.js +961 -44
- package/dist/knowledge/schemaGuard.js +199 -0
- package/dist/knowledge/styleResolve.js +63 -4
- package/dist/observability/trace.js +27 -3
- package/dist/platform/csv.js +123 -0
- package/dist/platform/errors.js +107 -6
- package/dist/platform/http.js +34 -4
- package/dist/platform/patch.js +27 -0
- package/dist/platform/refs.js +104 -0
- package/dist/platform/responses.js +25 -6
- package/dist/platform/screenshot.js +544 -16
- package/dist/platform/secrets.js +40 -0
- package/dist/platform/tokenVault.js +89 -0
- package/dist/platform/writeDiff.js +281 -0
- package/dist/resources/examples/flow-call-function.json +66 -0
- package/dist/resources/examples/function-total.json +70 -0
- package/dist/resources/examples/layout-app-shell.json +1 -1
- package/dist/resources/examples/microfunction-node-sum.json +56 -0
- package/dist/resources/examples/microservice-table-list.json +2 -4
- package/dist/resources/examples/page-dynamic-form.json +3 -3
- package/dist/resources/examples/page-flex-sections.json +99 -0
- package/dist/resources/examples/page-sign-in.json +4 -3
- package/dist/resources/guides/component.md +81 -8
- package/dist/resources/guides/entity.md +712 -20
- package/dist/resources/guides/expressions.md +47 -2
- package/dist/resources/guides/external-component.md +9 -0
- package/dist/resources/guides/functions.md +262 -0
- package/dist/resources/guides/getting-started.md +82 -7
- package/dist/resources/guides/layout.md +53 -10
- package/dist/resources/guides/merge.md +231 -0
- package/dist/resources/guides/microfunction.md +166 -0
- package/dist/resources/guides/microservice.md +287 -15
- package/dist/resources/guides/middleware.md +89 -1
- package/dist/resources/guides/modify-existing.md +23 -0
- package/dist/resources/guides/page.md +402 -49
- package/dist/resources/guides/translation.md +150 -8
- package/dist/resources/guides/view.md +16 -1
- package/dist/resources/guides/workflow.md +199 -28
- package/dist/resources/knowledge.js +5 -0
- package/dist/resources/rules.js +27 -0
- package/dist/resources/schema-fingerprint.json +8 -0
- package/dist/resources/schemas/AnalyzeImportQuery.json +51 -0
- package/dist/resources/schemas/ApplyMergePlanCommand.json +19 -0
- package/dist/resources/schemas/CreateBulkRowCommand.json +36 -0
- package/dist/resources/schemas/CreateDatasourceCommand.json +15 -6
- package/dist/resources/schemas/CreateDiscoveredEntitiesCommand.json +19 -8
- package/dist/resources/schemas/CreateEntityCommand.json +52 -12
- package/dist/resources/schemas/CreateEnvironmentCommand.json +31 -0
- package/dist/resources/schemas/CreateGlobalFunctionCommand.json +95 -0
- package/dist/resources/schemas/CreateImportPlanCommand.json +278 -0
- package/dist/resources/schemas/CreateMergePlanCommand.json +34 -0
- package/dist/resources/schemas/CreateMicrofunctionCommand.json +117 -0
- package/dist/resources/schemas/CreateMicrofunctionRuntimeCommand.json +51 -0
- package/dist/resources/schemas/CreateMicroserviceCommand.json +145 -3
- package/dist/resources/schemas/CreateQueryCommand.json +56 -3
- package/dist/resources/schemas/CreateRowCommand.json +38 -0
- package/dist/resources/schemas/CreateViewCommand.json +13 -10
- package/dist/resources/schemas/CreateWorkflowCommand.json +106 -14
- package/dist/resources/schemas/DataJobDto.json +267 -0
- package/dist/resources/schemas/ExportRowsCommand.json +172 -0
- package/dist/resources/schemas/Filter.json +3 -1
- package/dist/resources/schemas/GetRowsQuery.json +6 -1
- package/dist/resources/schemas/GlobalFunctionDto.json +89 -0
- package/dist/resources/schemas/GlobalFunctionUpdateResultDto.json +249 -0
- package/dist/resources/schemas/ImportAnalysisDto.json +401 -0
- package/dist/resources/schemas/ImportPlanDto.json +260 -0
- package/dist/resources/schemas/ImportSampleDto.json +90 -0
- package/dist/resources/schemas/InvokeMicrofunctionCommand.json +25 -0
- package/dist/resources/schemas/PreviewMicroserviceCommand.json +135 -1
- package/dist/resources/schemas/PreviewQueryCommand.json +5 -2
- package/dist/resources/schemas/PreviewWorkflowCommand.json +45 -9
- package/dist/resources/schemas/ResolveMergeConflictsCommand.json +58 -0
- package/dist/resources/schemas/RevertMergePlanCommand.json +19 -0
- package/dist/resources/schemas/RunImportCommand.json +62 -0
- package/dist/resources/schemas/StepDto.json +34 -3
- package/dist/resources/schemas/UpdateBulkRowCommand.json +36 -0
- package/dist/resources/schemas/UpdateDatasourceCommand.json +15 -6
- package/dist/resources/schemas/UpdateEntityCommand.json +52 -11
- package/dist/resources/schemas/UpdateGlobalFunctionCommand.json +66 -0
- package/dist/resources/schemas/UpdateMicrofunctionCommand.json +121 -0
- package/dist/resources/schemas/UpdateMicrofunctionRuntimeCommand.json +54 -0
- package/dist/resources/schemas/UpdateMicroserviceCommand.json +147 -4
- package/dist/resources/schemas/UpdateQueryCommand.json +13 -3
- package/dist/resources/schemas/UpdateRowCommand.json +40 -0
- package/dist/resources/schemas/UpdateViewCommand.json +15 -10
- package/dist/resources/schemas/UpdateWorkflowCommand.json +106 -14
- package/dist/server.js +17 -1
- package/dist/tools/access.js +3 -3
- package/dist/tools/activities.js +40 -0
- package/dist/tools/applications.js +1 -1
- package/dist/tools/automations.js +76 -34
- package/dist/tools/common.js +55 -0
- package/dist/tools/components.js +16 -7
- package/dist/tools/datasources.js +83 -21
- package/dist/tools/dependencies.js +1 -1
- package/dist/tools/deployments.js +34 -2
- package/dist/tools/dtos.js +25 -12
- package/dist/tools/entities.js +113 -28
- package/dist/tools/environments.js +52 -0
- package/dist/tools/externalComponents.js +9 -3
- package/dist/tools/files.js +57 -42
- package/dist/tools/flows.js +205 -13
- package/dist/tools/folders.js +7 -2
- package/dist/tools/functions.js +331 -0
- package/dist/tools/globalFunctions.js +63 -0
- package/dist/tools/importSchemas.js +79 -0
- package/dist/tools/imports.js +241 -0
- package/dist/tools/layouts.js +103 -29
- package/dist/tools/merges.js +113 -0
- package/dist/tools/microfunctions.js +155 -0
- package/dist/tools/microservices.js +270 -40
- package/dist/tools/organizations.js +5 -6
- package/dist/tools/pages.js +421 -104
- package/dist/tools/platformInfo.js +155 -11
- package/dist/tools/projects.js +183 -33
- package/dist/tools/queries.js +32 -10
- package/dist/tools/revisions.js +94 -0
- package/dist/tools/rows.js +484 -50
- package/dist/tools/screenWrite.js +68 -0
- package/dist/tools/screens.js +28 -0
- package/dist/tools/templates.js +32 -5
- package/dist/tools/translations.js +24 -8
- package/dist/tools/views.js +20 -10
- package/dist/tools/workflows.js +56 -21
- package/package.json +7 -2
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
import { unstoredKeys } from '../platform/writeDiff.js';
|
|
2
|
+
import { logStep } from '../observability/trace.js';
|
|
3
|
+
import { fileURLToPath } from 'node:url';
|
|
4
|
+
import { readFileSync } from 'node:fs';
|
|
5
|
+
import { join, dirname } from 'node:path';
|
|
6
|
+
const schemasDirectory = join(dirname(fileURLToPath(import.meta.url)), '..', 'resources', 'schemas');
|
|
7
|
+
const documents = new Map();
|
|
8
|
+
/**
|
|
9
|
+
* Yazma gövdesindeki TANINMAYAN anahtarı platform sessizce yok sayar: `isUnique` gönderilen bir
|
|
10
|
+
* index UNIQUE OLMADAN kurulur, istek 200 döner (ölçüldü 2026-09-07, `ENT-013`).
|
|
11
|
+
*
|
|
12
|
+
* Backend bunun için `X-Unmapped-Members` başlığını yazıyor ama kanal olarak KULLANILAMAZ:
|
|
13
|
+
* yalnız İLK eşleşmeyen anahtarı bildiriyor ve gateway'in GET→PUT gidiş-dönüşü gövdeye yedi
|
|
14
|
+
* yapısal anahtar koyuyor (`slug` · `datasourceId` · `primaryKey` · `type` · `applications` ·
|
|
15
|
+
* `automationCount` · `audit`). Sırada `slug` önce geldiği için başlık sıradan bir `entity_patch`
|
|
16
|
+
* çağrısında hep `slug` der ve ajanın yazdığı `indexes[0].isUnique` ASLA görünmez — kanal
|
|
17
|
+
* sinyali maskeler (g-03 Adım 0 ölçümü).
|
|
18
|
+
*
|
|
19
|
+
* Bu yüzden denetim gateway'in kendi taşıdığı şemalardan yapılır: çevrimdışı çalışır, TÜM
|
|
20
|
+
* anahtarları görür ve `introducedUnmappedMembers` sayesinde ajanın yazdığını gateway'in
|
|
21
|
+
* taşıdığından ayırabilir.
|
|
22
|
+
*/
|
|
23
|
+
function schemaDocument(command) {
|
|
24
|
+
if (documents.has(command))
|
|
25
|
+
return documents.get(command);
|
|
26
|
+
// Ad dosya yoluna giriyor: dizin dışına çıkan ya da uzantı taşıyan bir değer okunmaz.
|
|
27
|
+
const document = /^[A-Za-z0-9_]+$/.test(command)
|
|
28
|
+
? (() => {
|
|
29
|
+
try {
|
|
30
|
+
return JSON.parse(readFileSync(join(schemasDirectory, `${command}.json`), 'utf8'));
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
return undefined;
|
|
34
|
+
}
|
|
35
|
+
})()
|
|
36
|
+
: undefined;
|
|
37
|
+
documents.set(command, document);
|
|
38
|
+
return document;
|
|
39
|
+
}
|
|
40
|
+
/** `$ref` ya da tek elemanlı `allOf: [{ $ref }]` sarmalının hedef adı; ikincisi Swashbuckle'ın kısıt/nullable eklenmiş referans için ürettiği biçimdir. */
|
|
41
|
+
function referenceName(node) {
|
|
42
|
+
if (!node || typeof node !== 'object')
|
|
43
|
+
return undefined;
|
|
44
|
+
const record = node;
|
|
45
|
+
if (typeof record.$ref === 'string')
|
|
46
|
+
return record.$ref.replace('#/components/schemas/', '');
|
|
47
|
+
const allOf = record.allOf;
|
|
48
|
+
if (Array.isArray(allOf) && allOf.length === 1)
|
|
49
|
+
return referenceName(allOf[0]);
|
|
50
|
+
return undefined;
|
|
51
|
+
}
|
|
52
|
+
function resolve(document, node) {
|
|
53
|
+
if (!node || typeof node !== 'object')
|
|
54
|
+
return undefined;
|
|
55
|
+
const record = node;
|
|
56
|
+
const reference = record.$ref;
|
|
57
|
+
if (typeof reference === 'string') {
|
|
58
|
+
// Üretilen şemalarda tek biçim var: '#/components/schemas/<tam ad>'.
|
|
59
|
+
const name = reference.replace('#/components/schemas/', '');
|
|
60
|
+
const components = document.components?.schemas;
|
|
61
|
+
const target = components?.[name];
|
|
62
|
+
return target && typeof target === 'object' ? target : undefined;
|
|
63
|
+
}
|
|
64
|
+
// FluentValidation kısıtları swagger'a aktığından beri (2026-09-08) referanslı bir özellik
|
|
65
|
+
// `allOf: [{ $ref }]` + kendi anahtarları (nullable, minLength…) şeklinde gelebiliyor; hedef
|
|
66
|
+
// çözülür, özelliğin kendi anahtarları üstüne yazılır.
|
|
67
|
+
const allOf = record.allOf;
|
|
68
|
+
if (Array.isArray(allOf) && allOf.length === 1) {
|
|
69
|
+
const inner = resolve(document, allOf[0]);
|
|
70
|
+
const { allOf: _dropped, ...own } = record;
|
|
71
|
+
return inner ? { ...inner, ...own } : undefined;
|
|
72
|
+
}
|
|
73
|
+
return record;
|
|
74
|
+
}
|
|
75
|
+
function walk(document, schema, value, path, into) {
|
|
76
|
+
if (!schema || !value || typeof value !== 'object')
|
|
77
|
+
return;
|
|
78
|
+
if (Array.isArray(value)) {
|
|
79
|
+
const items = resolve(document, schema.items);
|
|
80
|
+
value.forEach((entry, index) => walk(document, items, entry, `${path}[${index}]`, into));
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
const properties = schema.properties;
|
|
84
|
+
if (!properties)
|
|
85
|
+
return;
|
|
86
|
+
for (const [key, entry] of Object.entries(value)) {
|
|
87
|
+
const child = path === '' ? key : `${path}.${key}`;
|
|
88
|
+
// `additionalProperties: false` olmayan bir düğümde serbest anahtar meşrudur (ör. `properties`
|
|
89
|
+
// alanı gibi şemasız sözlükler) — orada susmak yanlış alarmı önler.
|
|
90
|
+
if (!Object.hasOwn(properties, key)) {
|
|
91
|
+
// `writeDiff`'in gerekçeli istisnaları burada da geçerli: gateway'in bilerek gönderdiği,
|
|
92
|
+
// platformun `fieldId`'den çözüp saklamadığı kolaylık alanları hata değildir. Tek küme
|
|
93
|
+
// tutulur — ikiye ayrılırsa ayrışırlar.
|
|
94
|
+
if (schema.additionalProperties === false && !unstoredKeys.has(key))
|
|
95
|
+
into.push(child);
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
walk(document, resolve(document, properties[key]), entry, child, into);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* `command` üretilen şema dosyasının adıdır (`ngapps://schemas/<command>`); bilinmeyen ad
|
|
103
|
+
* sessizce boş liste döner — şema yoksa iddia da yoktur.
|
|
104
|
+
*/
|
|
105
|
+
export function unmappedMembers(command, body) {
|
|
106
|
+
const document = schemaDocument(command);
|
|
107
|
+
if (!document)
|
|
108
|
+
return [];
|
|
109
|
+
const found = [];
|
|
110
|
+
walk(document, document.schema, body, '', found);
|
|
111
|
+
return found;
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Yamanın GETİRDİĞİ eşleşmeyen anahtarları döner. Gateway'in GET'ten taşıdığı yapısal alanlar iki
|
|
115
|
+
* tarafta da bulunduğu için elenir; geriye yalnız çağıranın eklediği kalır.
|
|
116
|
+
*/
|
|
117
|
+
export function introducedUnmappedMembers(command, before, after) {
|
|
118
|
+
const existing = new Set(unmappedMembers(command, before));
|
|
119
|
+
return unmappedMembers(command, after).filter(member => !existing.has(member));
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Yanıt yan kanalı (`runtimeErrors` · `gatewayMasked` · `silentlyDropped` ile aynı desen).
|
|
123
|
+
* Alan adı `secretKeyPattern` ile eşleşmemeli, yoksa değeri sessizce `***` olur — GW-001 dersi;
|
|
124
|
+
* `entities.test.ts` bunu yanıt üzerinde doğruluyor.
|
|
125
|
+
*/
|
|
126
|
+
export function withUnmappedMembers(payload, members) {
|
|
127
|
+
if (members.length === 0 || !payload || typeof payload !== 'object')
|
|
128
|
+
return payload;
|
|
129
|
+
logStep('şema ✕', `${members.length} anahtar komut şemasında yok — platform onları sessizce yok sayar`, members);
|
|
130
|
+
return {
|
|
131
|
+
...payload,
|
|
132
|
+
unmappedMembers: members,
|
|
133
|
+
unmappedMembersNote: "Bu anahtarlar gönderdiğiniz komutun şemasında YOK: platform onları hata vermeden YOK SAYAR, yani o alanlar kaydedilmedi. Doğru adlar için ngapps://schemas kaynağına bakın."
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
const numericKinds = ['minLength', 'maxLength', 'minimum', 'maximum', 'minItems'];
|
|
137
|
+
function limitOf(path, schema, required) {
|
|
138
|
+
const limit = { path };
|
|
139
|
+
if (required)
|
|
140
|
+
limit.required = true;
|
|
141
|
+
for (const kind of numericKinds) {
|
|
142
|
+
if (typeof schema[kind] === 'number')
|
|
143
|
+
limit[kind] = schema[kind];
|
|
144
|
+
}
|
|
145
|
+
if (typeof schema.pattern === 'string' && schema.pattern !== '')
|
|
146
|
+
limit.pattern = schema.pattern;
|
|
147
|
+
return Object.keys(limit).length > 1 ? limit : undefined;
|
|
148
|
+
}
|
|
149
|
+
function collectSchemaLimits(document, schema, path, chain, into) {
|
|
150
|
+
const properties = schema?.properties;
|
|
151
|
+
if (!schema || !properties)
|
|
152
|
+
return;
|
|
153
|
+
const required = new Set(Array.isArray(schema.required) ? schema.required.map(String) : []);
|
|
154
|
+
for (const [key, raw] of Object.entries(properties)) {
|
|
155
|
+
const childPath = path === '' ? key : `${path}.${key}`;
|
|
156
|
+
const child = resolve(document, raw);
|
|
157
|
+
if (!child)
|
|
158
|
+
continue;
|
|
159
|
+
const limit = limitOf(childPath, child, required.has(key));
|
|
160
|
+
if (limit)
|
|
161
|
+
into.push(limit);
|
|
162
|
+
if (child.type === 'array') {
|
|
163
|
+
const items = resolve(document, child.items);
|
|
164
|
+
if (!items)
|
|
165
|
+
continue;
|
|
166
|
+
const itemLimit = limitOf(`${childPath}[]`, items, false);
|
|
167
|
+
if (itemLimit)
|
|
168
|
+
into.push(itemLimit);
|
|
169
|
+
const itemsName = referenceName(child.items);
|
|
170
|
+
// Özyineli tip (FieldDto.fields → FieldDto) bir kez yürünür; katalog da yalnız ilk seviyeyi tanır.
|
|
171
|
+
if (itemsName !== undefined && chain.includes(itemsName))
|
|
172
|
+
continue;
|
|
173
|
+
collectSchemaLimits(document, items, `${childPath}[]`, itemsName !== undefined ? [...chain, itemsName] : chain, into);
|
|
174
|
+
continue;
|
|
175
|
+
}
|
|
176
|
+
const childName = referenceName(raw);
|
|
177
|
+
if (childName !== undefined && chain.includes(childName))
|
|
178
|
+
continue;
|
|
179
|
+
collectSchemaLimits(document, child, childPath, childName !== undefined ? [...chain, childName] : chain, into);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
/** Şema dokümanındaki (`{ schema, components }`) bütün alan kısıtlarını katalog yol notasyonuyla (`fields[].name`) döner. */
|
|
183
|
+
export function schemaLimitsOf(document) {
|
|
184
|
+
if (!document)
|
|
185
|
+
return [];
|
|
186
|
+
const found = [];
|
|
187
|
+
const rootName = referenceName(document.schema);
|
|
188
|
+
collectSchemaLimits(document, resolve(document, document.schema), '', rootName !== undefined ? [rootName] : [], found);
|
|
189
|
+
return found;
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Paketlenmiş swagger şemasından FluentValidation kaynaklı kısıtlar (`MicroElements.Swashbuckle.FluentValidation`,
|
|
193
|
+
* backend 2026-09-08): NotEmpty → required (+ minLength 1), MaximumLength → maxLength, Matches → pattern,
|
|
194
|
+
* InclusiveBetween → minimum/maximum. `npm run generate-schemas` yenilenmeden eski şemada kısıt yoktur ve
|
|
195
|
+
* bu liste boş döner — o durumda `backendConstraints.ts` kataloğu tek başına konuşur.
|
|
196
|
+
*/
|
|
197
|
+
export function schemaLimits(command) {
|
|
198
|
+
return schemaLimitsOf(schemaDocument(command));
|
|
199
|
+
}
|
|
@@ -156,7 +156,8 @@ export const STYLE_GROUP_FIELDS = {
|
|
|
156
156
|
'borderBottom',
|
|
157
157
|
'borderLeft',
|
|
158
158
|
'shadow',
|
|
159
|
-
'contentOverflow'
|
|
159
|
+
'contentOverflow',
|
|
160
|
+
'opacity'
|
|
160
161
|
],
|
|
161
162
|
typographyStyle: [
|
|
162
163
|
'textColor',
|
|
@@ -171,6 +172,8 @@ export const STYLE_GROUP_FIELDS = {
|
|
|
171
172
|
],
|
|
172
173
|
hoverStyle: ['background', 'borderColor', 'shadow', 'lift', 'transitionMs']
|
|
173
174
|
};
|
|
175
|
+
/** `overflowTarget` için "kökün kendisi" işareti (frontend `ROOT_TARGET` kopyası). */
|
|
176
|
+
export const ROOT_TARGET = '&';
|
|
174
177
|
export function composeTypedStyle(input, ext) {
|
|
175
178
|
const groups = {};
|
|
176
179
|
if (isPlainObject(input.surfaceStyle)) {
|
|
@@ -198,8 +201,19 @@ export function composeTypedStyle(input, ext) {
|
|
|
198
201
|
groups.border = `1px ${surface.borderStyle || 'solid'} ${surface.borderColor || DEFAULT_DIVIDER_COLOR}`;
|
|
199
202
|
}
|
|
200
203
|
}
|
|
204
|
+
// Opaklık yüzdedir (eski Surface bloğuyla aynı sözleşme): 100 ve boş
|
|
205
|
+
// yazılmaz, aralık dışı yok sayılır.
|
|
206
|
+
const opacity = asNumber(surface.opacity);
|
|
207
|
+
if (opacity !== undefined && opacity >= 0 && opacity < 100) {
|
|
208
|
+
groups.opacity = String(opacity / 100);
|
|
209
|
+
}
|
|
210
|
+
// `ROOT_TARGET` (`&`) kökün kendisidir: kendi derleyicisi olmayan kaplarda
|
|
211
|
+
// yüzey düğüm kutusuna yazılır ve kaydırılan da odur.
|
|
201
212
|
if (isSet(surface.contentOverflow) && ext?.overflowTarget) {
|
|
202
|
-
|
|
213
|
+
if (ext.overflowTarget === ROOT_TARGET)
|
|
214
|
+
groups.overflow = surface.contentOverflow;
|
|
215
|
+
else
|
|
216
|
+
nest(groups, ext.overflowTarget, { overflow: surface.contentOverflow });
|
|
203
217
|
}
|
|
204
218
|
}
|
|
205
219
|
if (isPlainObject(input.typographyStyle)) {
|
|
@@ -414,6 +428,44 @@ export const TYPED_STYLE_COMPONENTS = {
|
|
|
414
428
|
omit: { hoverStyle: ['borderColor'] }
|
|
415
429
|
}
|
|
416
430
|
};
|
|
431
|
+
/**
|
|
432
|
+
* Kendi derleyicisi OLMAYAN kap bileşenleri (2026-09-04): şemalarında
|
|
433
|
+
* `surfaceStyle` grubu var ama bileşen prop'u okumaz — render katmanı yüzeyi
|
|
434
|
+
* düğüm kutusuna (`.ng-app__node-item`) genel yolla yazar, taşma kökün
|
|
435
|
+
* kendisine gider (`packages/ng-apps-ui/src/style-groups/node-surface.ts`).
|
|
436
|
+
* Bu tipler `TYPED_STYLE_COMPONENTS`'a GİRMEZ: `style` kaçış kanalı onlarda
|
|
437
|
+
* eski birleşik kaskadla (tema+preset+own) çözülmeye devam eder.
|
|
438
|
+
*/
|
|
439
|
+
export const NODE_SURFACE_TYPES = new Set([
|
|
440
|
+
'table',
|
|
441
|
+
'datatable',
|
|
442
|
+
'form',
|
|
443
|
+
'collapsible-container',
|
|
444
|
+
'drawer',
|
|
445
|
+
'modal',
|
|
446
|
+
'popover',
|
|
447
|
+
'tabs',
|
|
448
|
+
'stepper',
|
|
449
|
+
'repeater',
|
|
450
|
+
'html',
|
|
451
|
+
'file-viewer',
|
|
452
|
+
'document-editor',
|
|
453
|
+
'layout',
|
|
454
|
+
'layout-region',
|
|
455
|
+
'layout-shell',
|
|
456
|
+
'qm-content',
|
|
457
|
+
'qm-sidebar',
|
|
458
|
+
'auth-layout'
|
|
459
|
+
]);
|
|
460
|
+
const NODE_SURFACE_EXTENSION = { overflowTarget: ROOT_TARGET };
|
|
461
|
+
/** Düğüm kutusuna inen yüzey; kap tipi değilse ya da grup boşsa yok. */
|
|
462
|
+
export function composeNodeSurfaceStyle(type, surfaceStyle) {
|
|
463
|
+
if (!type || !NODE_SURFACE_TYPES.has(type))
|
|
464
|
+
return undefined;
|
|
465
|
+
if (!isPlainObject(surfaceStyle))
|
|
466
|
+
return undefined;
|
|
467
|
+
return composeTypedStyle({ surfaceStyle }, NODE_SURFACE_EXTENSION);
|
|
468
|
+
}
|
|
417
469
|
/** text derlemesi — testler ve dış tüketiciler için sarmalayıcı. */
|
|
418
470
|
export function composeTextStyle(input) {
|
|
419
471
|
return composeTypedStyle(input, TYPED_STYLE_COMPONENTS.text.ext);
|
|
@@ -467,9 +519,16 @@ export function resolveNodeStyleReport(node, source) {
|
|
|
467
519
|
own: isPlainObject(ownStyle) ? Object.keys(ownStyle).filter(key => key !== STYLE_PRESET_KEY).length > 0 : Boolean(ownStyle)
|
|
468
520
|
};
|
|
469
521
|
if (!config) {
|
|
470
|
-
// Kayıtsız tip: eski birleşik kaskad.
|
|
522
|
+
// Kayıtsız tip: eski birleşik kaskad. Kap tiplerinde tipli yüzey grubu
|
|
523
|
+
// düğüm kutusuna ayrıca iner; rapor onu kaskadın ÜSTÜNE koyar (kutu,
|
|
524
|
+
// bileşenin kökünü sarar).
|
|
471
525
|
const cascaded = resolveNodeStyle(ownStyle, source, node.type);
|
|
472
|
-
|
|
526
|
+
const nodeSurface = composeNodeSurfaceStyle(node.type, rawProp(props, 'surfaceStyle'));
|
|
527
|
+
const typedLayers = nodeSurface ? { ...layers, typedGroups: ['surfaceStyle'] } : layers;
|
|
528
|
+
const resolved = cascaded === undefined && nodeSurface === undefined
|
|
529
|
+
? undefined
|
|
530
|
+
: deepMergeStyle((cascaded ?? {}), nodeSurface ?? {});
|
|
531
|
+
return resolved === undefined ? { styleLayers: typedLayers } : { resolvedStyle: resolved, styleLayers: typedLayers };
|
|
473
532
|
}
|
|
474
533
|
// Tipli komponent gruplar boşken de tipli yoldan geçer: uzantı emekli
|
|
475
534
|
// prop'ları okuyabilir (text'in isBold/align/color takma adı) ve K4 sırası
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { AsyncLocalStorage } from 'node:async_hooks';
|
|
2
2
|
import { appendFileSync, existsSync, mkdirSync } from 'node:fs';
|
|
3
3
|
import { dirname, join } from 'node:path';
|
|
4
|
+
import { secretKeyPattern } from '../platform/secrets.js';
|
|
4
5
|
const defaultDirectory = join(process.cwd(), 'logs');
|
|
5
6
|
const activeTrace = new AsyncLocalStorage();
|
|
6
7
|
const activeRequest = new AsyncLocalStorage();
|
|
7
8
|
const inFlight = new Set();
|
|
8
|
-
const secretKeyPattern = /pass|secret|token|authorization|api[-_]?key|credential|connectionstring/i;
|
|
9
9
|
const detailIndent = 26;
|
|
10
10
|
let level = 'off';
|
|
11
11
|
let logFile;
|
|
@@ -180,6 +180,25 @@ function renderTrace(trace, outcome) {
|
|
|
180
180
|
lines.push('');
|
|
181
181
|
return `${lines.join('\n')}\n`;
|
|
182
182
|
}
|
|
183
|
+
/**
|
|
184
|
+
* `runtimeErrors` kayıtlarını DÜZ METNE çevirir. Eskiden `runtimeErrors.map(String)` yazılıyordu
|
|
185
|
+
* ve nesne `[object Object]`e düşüyordu: 2026-09-06 koşusunda tool mesajı hangi adımın neden
|
|
186
|
+
* düştüğünü söylemiyordu, oysa doğru cümle aynı yanıtın içinde duruyordu (`S-4`).
|
|
187
|
+
*
|
|
188
|
+
* Yalnız İLK hata yazılır ve kalan sayılır — mesaj bir özet satırıdır, liste değil. Kullanıcı
|
|
189
|
+
* verisi taşıyabildiği için cümle 300 karakterde kesilir; tamamı günlüktedir.
|
|
190
|
+
*/
|
|
191
|
+
export function describeRuntimeErrors(errors) {
|
|
192
|
+
const first = (errors[0] ?? {});
|
|
193
|
+
const where = [first.step?.name, first.element ? `${first.element}${first.property ? `.${first.property}` : ''}` : undefined]
|
|
194
|
+
.filter(Boolean)
|
|
195
|
+
.join(' → ');
|
|
196
|
+
const message = typeof first.message === 'string' && first.message.length > 0
|
|
197
|
+
? first.message
|
|
198
|
+
: JSON.stringify(first);
|
|
199
|
+
const rest = errors.length > 1 ? ` (+${errors.length - 1} hata daha)` : '';
|
|
200
|
+
return `${where ? `${where} — ` : ''}${clip(message, 300)}${rest} · tamamı günlükte`;
|
|
201
|
+
}
|
|
183
202
|
function describeResult(result) {
|
|
184
203
|
const call = result;
|
|
185
204
|
const text = Array.isArray(call?.content)
|
|
@@ -187,9 +206,14 @@ function describeResult(result) {
|
|
|
187
206
|
: '';
|
|
188
207
|
if (call?.isError)
|
|
189
208
|
return { ok: false, summary: clip(text, 400) };
|
|
190
|
-
const
|
|
209
|
+
const structured = call?.structuredContent;
|
|
210
|
+
const runtimeErrors = structured?.runtimeErrors;
|
|
191
211
|
if (Array.isArray(runtimeErrors) && runtimeErrors.length > 0) {
|
|
192
|
-
|
|
212
|
+
// Kasıtlı negatif test bir kusur değil bir KANIT: çağıran beklediğini söylediyse satır
|
|
213
|
+
// başarılı sayılır ve ölçüm aracının plansız hata oranını şişirmez (S-4).
|
|
214
|
+
return structured?.expectedFailure === true
|
|
215
|
+
? { ok: true, summary: `beklenen çalışma anı hatası (negatif test): ${describeRuntimeErrors(runtimeErrors)}` }
|
|
216
|
+
: { ok: false, summary: `çalışma anı hatası (platform HTTP 200 döndü): ${describeRuntimeErrors(runtimeErrors)}` };
|
|
193
217
|
}
|
|
194
218
|
const effect = summarizeEffect(text);
|
|
195
219
|
return { ok: true, summary: `${Buffer.byteLength(text, 'utf8')} B yanıt${effect ? ` · ${effect}` : ''}` };
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Gateway'in kendi minimal RFC 4180 okuyucusu. `packages/ng-apps-ui`'daki `csv-parse.ts`
|
|
3
|
+
* kopyalanmaz: o dosya tarayıcı için yazılmıştır ve bu proje frontend workspace'ine dahil
|
|
4
|
+
* değildir (sözleşme D5) — kopya iki yerde birden bozulurdu.
|
|
5
|
+
*
|
|
6
|
+
* Değerler HEP string döner. Tip dönüşümü backend'in işidir: `1.234,56` ile `1,234.56` arasındaki
|
|
7
|
+
* fark kültüre bağlıdır ve gateway istek kültürünü bilmez.
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* Ayraç ilk satırdan tahmin edilir. Türkçe Excel `;` ile yazar ve o dosyalarda virgül ONDALIK
|
|
11
|
+
* AYRACIDIR — yanlış tahmin `1,5` değerini iki sütuna böler, hem de sessizce.
|
|
12
|
+
*/
|
|
13
|
+
export function detectDelimiter(firstLine) {
|
|
14
|
+
let semicolons = 0;
|
|
15
|
+
let commas = 0;
|
|
16
|
+
let quoted = false;
|
|
17
|
+
for (const character of firstLine) {
|
|
18
|
+
if (character === '"')
|
|
19
|
+
quoted = !quoted;
|
|
20
|
+
else if (quoted)
|
|
21
|
+
continue;
|
|
22
|
+
else if (character === ';')
|
|
23
|
+
semicolons += 1;
|
|
24
|
+
else if (character === ',')
|
|
25
|
+
commas += 1;
|
|
26
|
+
}
|
|
27
|
+
return semicolons > commas ? ';' : ',';
|
|
28
|
+
}
|
|
29
|
+
function stripBom(text) {
|
|
30
|
+
return text.charCodeAt(0) === 0xfeff ? text.slice(1) : text;
|
|
31
|
+
}
|
|
32
|
+
function firstLineOf(text) {
|
|
33
|
+
let quoted = false;
|
|
34
|
+
for (let index = 0; index < text.length; index += 1) {
|
|
35
|
+
const character = text[index];
|
|
36
|
+
if (character === '"')
|
|
37
|
+
quoted = !quoted;
|
|
38
|
+
else if (!quoted && (character === '\n' || character === '\r'))
|
|
39
|
+
return text.slice(0, index);
|
|
40
|
+
}
|
|
41
|
+
return text;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Satır satır hücre dizisi. Tırnaklı hücre ayraç ve satır sonu taşıyabilir, `""` tek tırnaktır;
|
|
45
|
+
* tamamen boş satırlar atılır — Excel'in dosya sonuna bıraktığı boş satır aksi hâlde her alanı
|
|
46
|
+
* boş bir kayıt üretirdi.
|
|
47
|
+
*/
|
|
48
|
+
export function parseCsv(text, delimiter) {
|
|
49
|
+
const content = stripBom(text);
|
|
50
|
+
const separator = delimiter ?? detectDelimiter(firstLineOf(content));
|
|
51
|
+
const rows = [];
|
|
52
|
+
let row = [];
|
|
53
|
+
let value = '';
|
|
54
|
+
let quoted = false;
|
|
55
|
+
let index = 0;
|
|
56
|
+
const endValue = () => {
|
|
57
|
+
row.push(value);
|
|
58
|
+
value = '';
|
|
59
|
+
};
|
|
60
|
+
const endRow = () => {
|
|
61
|
+
endValue();
|
|
62
|
+
if (row.some(cell => cell.length > 0))
|
|
63
|
+
rows.push(row);
|
|
64
|
+
row = [];
|
|
65
|
+
};
|
|
66
|
+
while (index < content.length) {
|
|
67
|
+
const character = content[index];
|
|
68
|
+
if (quoted) {
|
|
69
|
+
if (character === '"') {
|
|
70
|
+
if (content[index + 1] === '"') {
|
|
71
|
+
value += '"';
|
|
72
|
+
index += 2;
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
quoted = false;
|
|
76
|
+
index += 1;
|
|
77
|
+
continue;
|
|
78
|
+
}
|
|
79
|
+
value += character;
|
|
80
|
+
index += 1;
|
|
81
|
+
continue;
|
|
82
|
+
}
|
|
83
|
+
if (character === '"' && value.length === 0) {
|
|
84
|
+
quoted = true;
|
|
85
|
+
index += 1;
|
|
86
|
+
continue;
|
|
87
|
+
}
|
|
88
|
+
if (character === separator) {
|
|
89
|
+
endValue();
|
|
90
|
+
index += 1;
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
if (character === '\r' || character === '\n') {
|
|
94
|
+
endRow();
|
|
95
|
+
index += character === '\r' && content[index + 1] === '\n' ? 2 : 1;
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
value += character;
|
|
99
|
+
index += 1;
|
|
100
|
+
}
|
|
101
|
+
if (value.length > 0 || row.length > 0)
|
|
102
|
+
endRow();
|
|
103
|
+
return rows;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Başlık satırını anahtar yapan satır sözlükleri. Başlık boş olan sütun atlanır (Excel'in sağa
|
|
107
|
+
* doğru uzattığı boş sütunlar aksi hâlde `''` anahtarlı bir alan üretirdi) ve fazladan hücre
|
|
108
|
+
* taşıyan satır kırpılır.
|
|
109
|
+
*/
|
|
110
|
+
export function csvRows(text, delimiter) {
|
|
111
|
+
const grid = parseCsv(text, delimiter);
|
|
112
|
+
if (grid.length === 0)
|
|
113
|
+
return [];
|
|
114
|
+
const headers = (grid[0] ?? []).map(header => header.trim());
|
|
115
|
+
return grid.slice(1).map(cells => {
|
|
116
|
+
const row = {};
|
|
117
|
+
headers.forEach((header, column) => {
|
|
118
|
+
if (header.length > 0)
|
|
119
|
+
row[header] = cells[column] ?? '';
|
|
120
|
+
});
|
|
121
|
+
return row;
|
|
122
|
+
});
|
|
123
|
+
}
|
package/dist/platform/errors.js
CHANGED
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
export class PlatformApiError extends Error {
|
|
2
2
|
status;
|
|
3
3
|
code;
|
|
4
|
+
/**
|
|
5
|
+
* Toplu satır yazmasında düşen satırın parti içi indeksi (`BulkRowErrorResponse.rowIndex`).
|
|
6
|
+
* Mesajda da geçer ama metinden ayıklanamaz: parti döngüsü dosya satır numarasını hesaplamak
|
|
7
|
+
* için sayıya ihtiyaç duyar, çağıran hangi satırı düzelteceğini ancak öyle bilir.
|
|
8
|
+
*/
|
|
9
|
+
rowIndex;
|
|
4
10
|
constructor(message, status, code) {
|
|
5
11
|
super(message);
|
|
6
12
|
this.status = status;
|
|
@@ -45,8 +51,62 @@ function collectValidationMessages(payload) {
|
|
|
45
51
|
* Sayfa şeması patch'inin üç reddi de "hiçbir op uygulanmadı" anlamına gelir (uygulayıcı önce tüm
|
|
46
52
|
* op'ları bellekte işler, tek hata bile tüm komutu düşürür) — ajanın kısmi uygulama varsayıp
|
|
47
53
|
* planın kalanını göndermesi bu yüzden yanlış olur.
|
|
54
|
+
*
|
|
55
|
+
* Aynı duvar 2026-09-08'den beri sayfa/layout KURMA yolunda da koşuyor (P1-2: layout hattı aynı
|
|
56
|
+
* hatalı prop'ları sessizce kaydediyordu). Orada op listesi yoktur ve `page_schema_validate` de
|
|
57
|
+
* uygulanmaz — ipucu bu yüzden yola göre ayrışır.
|
|
48
58
|
*/
|
|
49
|
-
|
|
59
|
+
/**
|
|
60
|
+
* Merge apply'ın iki 409'u da aynı şeyi söyler: plan artık uygulanabilir değil. Ajan bunu
|
|
61
|
+
* 'geçici hata, tekrar dene' diye okuyup aynı çağrıyı yollamasın diye yol açıkça yazılır.
|
|
62
|
+
*/
|
|
63
|
+
function mergeHint(path) {
|
|
64
|
+
if (!/\/merges\//.test(path))
|
|
65
|
+
return '';
|
|
66
|
+
return " Üç sebepten biri: çözülmemiş çakışma, ya da plan alındığından beri DALIN veya development’ın değişmesi (MergePlanStaleException) — ikincisinde planın taşıdığı liste eskidir ve uygulansaydı sonradan yapılan iş sessizce düşerdi. merge_get ile planı yeniden okuyun (isStale), bayatsa merge_refresh ile tazeleyin — yeni plan açmanız gerekmez. Aynı merge_apply çağrısını tekrarlamak sonucu değiştirmez.";
|
|
67
|
+
}
|
|
68
|
+
function mergeRevertHint(code) {
|
|
69
|
+
if (code === 'MergeRevertWindowExpiredException') {
|
|
70
|
+
return " Geri alma penceresi doldu: pencere kapanınca merge öncesi anlık görüntü de silinir, bu yüzden plan artık geri alınamaz. merge_list status:'applied' ile pencere içindeki bir plan seçin.";
|
|
71
|
+
}
|
|
72
|
+
if (code === 'MergePlanNotRevertableException') {
|
|
73
|
+
return ' Bu plan bir GERİ ALMA planı (kind=revert) ve geri almanın geri alması yoktur. Geri almayı iptal etmek istiyorsanız dalı yeniden merge edin.';
|
|
74
|
+
}
|
|
75
|
+
if (code === 'MergePlanNotAppliedException') {
|
|
76
|
+
return " Plan uygulanmış DEĞİL — durumu mesajda yazıyor (failed / discarded / reverted / planned…). Yalnız 'applied' ve 'applied-with-drift' geri alınabilir; merge_list status:'applied' ile uygun planı seçin. Zaten geri alınmış bir planın durumu 'reverted'dır ve ikinci kez geri alınamaz.";
|
|
77
|
+
}
|
|
78
|
+
return '';
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* İçe aktarma zinciri dört çağrıdır ve her hata farklı bir adıma geri gönderir: plan süresi
|
|
82
|
+
* dolduysa analizden, anahtar benzersiz değilse plan gövdesinden, hata oranı yüksekse dosyanın
|
|
83
|
+
* kendisinden. Kod olmadan mesajlar "422" diye okunur ve ajan aynı çağrıyı tekrarlar.
|
|
84
|
+
*/
|
|
85
|
+
function importHint(code, path) {
|
|
86
|
+
if (code === 'ImportPlanExpiredException') {
|
|
87
|
+
return ' Plan bir saat geçerlidir ve süresi doldu (ya da başka bir kullanıcıya ait). import_analyze ile dosyayı yeniden okuyup import_plan ile yeni bir plan üretin — aynı planId bir daha çalışmaz.';
|
|
88
|
+
}
|
|
89
|
+
if (code === 'ImportTooManyErrorsException') {
|
|
90
|
+
return " Satırların yarısından fazlası kuru koşuda düştü. Önce planın `errors` listesine bakın: çoğu zaman tek bir yanlış `locale` ya da yanlış sütun tipi bütün satırları düşürür. Dosya gerçekten böyleyse import_run'ı `force: true` ile çağırın.";
|
|
91
|
+
}
|
|
92
|
+
if (code === 'ImportKeyFieldNotUniqueException') {
|
|
93
|
+
return " Upsert/update anahtarı benzersiz olmalı: alanın kendisi unique/primary key olmalı, ya da tek alanlı bir unique index taşımalı, ya da 'id'/'code' olmalı. entity_get ile alanın `unique` bayrağını doğrulayın.";
|
|
94
|
+
}
|
|
95
|
+
if (code === 'ImportRelationMatchFieldNotUniqueException') {
|
|
96
|
+
return ' Ad → id eşlemesi yalnız hedef tablonun display alanı ya da benzersiz bir alanıyla yapılır; benzersiz olmayan bir alanda aynı ad iki satıra gidebilir ve hangisinin yazılacağı belirsiz olur.';
|
|
97
|
+
}
|
|
98
|
+
if (code === 'ImportSheetEmptyException' || code === 'ImportRowLimitExceededException') {
|
|
99
|
+
return ' `headerRow` doğru mu kontrol edin (0 = başlık yok); satır sınırı aşıldıysa dosyayı sayfalara bölün.';
|
|
100
|
+
}
|
|
101
|
+
if (code === 'DataJobNotUndoableException' && path.endsWith('/cancel')) {
|
|
102
|
+
return ' İş zaten bitmiş (completed / failed / undone / cancelled); iptal edilecek bir şey yok. Tamamlanmış işi geri almak için import_undo.';
|
|
103
|
+
}
|
|
104
|
+
if (code === 'DataJobNotUndoableException' && path.endsWith('/undo')) {
|
|
105
|
+
return ' Yalnız tamamlanmış ve undoAvailableUntil geçmemiş bir içe aktarma geri alınır; mevcut tabloya yalnız ekleme yapan işin penceresi hiç açılmaz. Süren işi durdurmak için import_cancel.';
|
|
106
|
+
}
|
|
107
|
+
return '';
|
|
108
|
+
}
|
|
109
|
+
function pageSchemaHint(code, path) {
|
|
50
110
|
if (code === 'PageSchemaVersionConflictException') {
|
|
51
111
|
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.';
|
|
52
112
|
}
|
|
@@ -54,10 +114,42 @@ function pageSchemaHint(code) {
|
|
|
54
114
|
return ' Sayfa şeması başka bir aktörün kilidinde (kilit sahibi mesajda; kilit 60 sn sonra kendiliğinden düşer). Bekleyip tekrar deneyin — aynı anda iki yazar olduğunda kaybeden taraf sessizce ezilmesin diye kilit var.';
|
|
55
115
|
}
|
|
56
116
|
if (code === 'PageSchemaValidationException') {
|
|
57
|
-
return '
|
|
117
|
+
return path.endsWith('/schema')
|
|
118
|
+
? ' Op listesi doğrulama duvarına takıldı ve HİÇBİR op uygulanmadı (hepsi ya da hiçbiri). Alan hatalarını düzeltip tek çağrıda tekrar gönderin; planı önce page_schema_validate ile kuru koşturabilirsiniz.'
|
|
119
|
+
: ' Gövde doğrulama duvarına takıldı ve HİÇBİR ŞEY KAYDEDİLMEDİ. Yukarıdaki her satır bir düğüm/prop adresi ve kabul edilen değer listesi taşır — prop adlarını component_get ile doğrulayıp gövdeyi tek çağrıda düzeltin.';
|
|
58
120
|
}
|
|
59
121
|
return '';
|
|
60
122
|
}
|
|
123
|
+
/**
|
|
124
|
+
* Toplu satır yazma tek transaction: platform hangi satırın düştüğünü mesajda söyler
|
|
125
|
+
* (`Row {n} could not be written: …`), ama "geri kalanı yazıldı mı" sorusunu söylemez. Ajan
|
|
126
|
+
* kısmi yazma varsayıp yalnız düşen satırı yeniden göndermesin diye all-or-nothing burada yazılı.
|
|
127
|
+
*/
|
|
128
|
+
function bulkRowHint(code) {
|
|
129
|
+
if (code === 'BulkRowFailedException') {
|
|
130
|
+
return " Toplu yazma TEK TRANSACTION: bu çağrıdan HİÇBİR satır yazılmadı (yalnız id sekansı ilerledi). Mesajdaki satır numarası 0'dan başlar ve gönderdiğiniz `rows` dizisinin indeksidir. O satırı düzeltip TÜM diziyi yeniden gönderin.";
|
|
131
|
+
}
|
|
132
|
+
if (code === 'BulkRowLimitException') {
|
|
133
|
+
return ' Diziyi 1000 satırlık parçalara bölüp sırayla gönderin; her parça kendi transaction\'ında yazılır.';
|
|
134
|
+
}
|
|
135
|
+
return '';
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* PATCH uçlarında `baseVersion` zorunlu ve kayıt okumanızdan beri değiştiyse HİÇBİR op uygulanmaz.
|
|
139
|
+
* Ajanın "tekrar dene" refleksi burada yanlış: aynı ops'u yeni sürümle göndermek başkasının
|
|
140
|
+
* yazdığını ezer, önce yeniden okunmalı.
|
|
141
|
+
*/
|
|
142
|
+
function versionConflictHint(code, path) {
|
|
143
|
+
if (code !== 'AggregateVersionConflictException')
|
|
144
|
+
return '';
|
|
145
|
+
const readTool = /\/entities(\/|$)/.test(path) ? 'entity_get'
|
|
146
|
+
: /\/workflows(\/|$)/.test(path) ? 'workflow_get'
|
|
147
|
+
: /\/flows(\/|$)/.test(path) ? 'flow_get'
|
|
148
|
+
: /\/layouts(\/|$)/.test(path) ? 'layout_get'
|
|
149
|
+
: /\/microservices(\/|$)/.test(path) ? 'microservice_get'
|
|
150
|
+
: 'ilgili *_get';
|
|
151
|
+
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
|
+
}
|
|
61
153
|
/**
|
|
62
154
|
* Bilinen platform bug'larını ajanın tool yüzeyinden teşhis edebilmesi için desen eşlemesi
|
|
63
155
|
* (klinik turu dersi: teşhis DB'ye/koda inmeden yapılabilmeli — görev G-09).
|
|
@@ -65,6 +157,12 @@ function pageSchemaHint(code) {
|
|
|
65
157
|
function diagnoseKnownFailure(status, message, code, context) {
|
|
66
158
|
if (status !== 500)
|
|
67
159
|
return '';
|
|
160
|
+
// 2026-09-08'e kadar ExceptionHandler bu istisnayı eşlemiyordu ve slug çakışması 500 dönüyordu
|
|
161
|
+
// (3. koşu S-1 / P3-7); yeni build 409 verir, gateway de POST'tan önce project_list ile arar
|
|
162
|
+
// (PRJ-001). Bu dal eski build'e karşı kalır.
|
|
163
|
+
if (code === 'DatabaseNameAlreadyExistsException' || message.includes('already has this database name')) {
|
|
164
|
+
return " Sebep: proje slug'ı bu organizasyonda zaten kullanımda (veritabanı adı zeyrek_<org>_<slug> tekildir). Çalışan backend bu durumu 409 yerine 500 ile bildiren eski bir build; başka bir slug seçin ya da project_list ile mevcut projeyi bulun (PRJ-001).";
|
|
165
|
+
}
|
|
68
166
|
if (context.path.includes('/row') && message.includes('Sequence contains no matching element')) {
|
|
69
167
|
return " Muhtemel sebep: entity bir application'a bağlı değil — entity_patch ile applicationIds ekleyin (PLT-004).";
|
|
70
168
|
}
|
|
@@ -72,7 +170,7 @@ function diagnoseKnownFailure(status, message, code, context) {
|
|
|
72
170
|
return " Bilinen sınır (K-005/P-01): m2m, çoklu dosya ya da çoklu enum alanı entity_create gövdesindeyken platform junction KAYDINI açar ama fiziksel tabloyu kurmaz. Alanı entity_patch ile yeniden ekleyin; gateway'in entity_create'i bunu artık kendisi iki aşamaya bölüyor, elle kurulmuş eski kayıtlar etkilenir.";
|
|
73
171
|
}
|
|
74
172
|
if (message.includes('cannot be tracked') || message.includes('already being tracked')) {
|
|
75
|
-
return " Bilinen sınır (K-006/P-02): operation:'get' tipli microservice'te PUT, izlenen Application grafiği yüzünden 500
|
|
173
|
+
return " Bilinen sınır (K-006/P-02): operation:'get' tipli microservice'te PUT, izlenen Application grafiği yüzünden 500 veriyordu. Regresyon 2026-08-16'da düzeltildi ve 2026-09-01'de canlı doğrulandı (İK koşusu M-9) — bu hatayı hâlâ görüyorsanız çalışan sürüm düzeltmeden eskidir. O sürümde geçici yol: microservice_delete + microservice_create ile yeniden kurun (gövdeyi önce microservice_get ile alın).";
|
|
76
174
|
}
|
|
77
175
|
if (code === 'RowValidationFailed' || message.includes('RowValidationFailed')) {
|
|
78
176
|
return ' Gövdedeki alanlar doğru görünüyorsa muhtemel sebep istemcinin body alanını düşürmesidir (K-002): microservice_execute/debug çağrısında body bir NESNE ya da nesne DİZİSİ olmalı; boş gövdeyle gönderildiyse istemci şemayı desteklemiyor olabilir.';
|
|
@@ -91,7 +189,7 @@ export function mapPlatformError(status, payload, context) {
|
|
|
91
189
|
if (validationMessages.length > 0) {
|
|
92
190
|
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);
|
|
93
191
|
}
|
|
94
|
-
return new PlatformApiError(`Geçersiz istek (${location}): ${apiMessage ?? 'API gövdeyi reddetti.'}`, status, code);
|
|
192
|
+
return new PlatformApiError(`Geçersiz istek (${location}): ${apiMessage ?? 'API gövdeyi reddetti.'}${mergeRevertHint(code)}${importHint(code, context.path)}`, status, code);
|
|
95
193
|
}
|
|
96
194
|
if (status === 401) {
|
|
97
195
|
const hint = context.authMode === 'local'
|
|
@@ -109,7 +207,7 @@ export function mapPlatformError(status, payload, context) {
|
|
|
109
207
|
if (context.method !== 'GET' && /\/flows(\/|$)/.test(context.path)) {
|
|
110
208
|
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);
|
|
111
209
|
}
|
|
112
|
-
return new PlatformApiError(`Kayıt bulunamadı (${location}).${environmentHint} id/slug değerini ilgili *_list tool'uyla doğrulayın
|
|
210
|
+
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);
|
|
113
211
|
}
|
|
114
212
|
if (status === 409 || status === 422) {
|
|
115
213
|
const projectConflictHint = status === 409 && context.method === 'POST' && /\/projects\/?$/.test(context.path)
|
|
@@ -117,7 +215,10 @@ export function mapPlatformError(status, payload, context) {
|
|
|
117
215
|
: '';
|
|
118
216
|
const validationMessages = collectValidationMessages(body);
|
|
119
217
|
const detail = validationMessages.length > 0 ? ` → ${validationMessages.join(' ; ')}` : '';
|
|
120
|
-
|
|
218
|
+
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);
|
|
219
|
+
if (typeof body.rowIndex === 'number')
|
|
220
|
+
rejected.rowIndex = body.rowIndex;
|
|
221
|
+
return rejected;
|
|
121
222
|
}
|
|
122
223
|
const fallbackValidationMessages = collectValidationMessages(body);
|
|
123
224
|
const fallbackDetail = fallbackValidationMessages.length > 0 ? ` → ${fallbackValidationMessages.join(' ; ')}` : '';
|