@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.
Files changed (145) hide show
  1. package/.env.example +2 -2
  2. package/.env.production.example +9 -1
  3. package/README.md +14 -38
  4. package/dist/config.js +36 -3
  5. package/dist/knowledge/backendConstraints.js +982 -0
  6. package/dist/knowledge/componentCatalog.js +549 -92
  7. package/dist/knowledge/componentEvents.js +120 -25
  8. package/dist/knowledge/coverage.js +43 -0
  9. package/dist/knowledge/flowGuards.js +30 -0
  10. package/dist/knowledge/functionGuards.js +134 -0
  11. package/dist/knowledge/guideSections.js +49 -0
  12. package/dist/knowledge/hydrate.js +97 -0
  13. package/dist/knowledge/languageCodes.js +36 -0
  14. package/dist/knowledge/languages.js +105 -0
  15. package/dist/knowledge/layoutSummary.js +10 -0
  16. package/dist/knowledge/localization.js +51 -0
  17. package/dist/knowledge/normalize.js +500 -16
  18. package/dist/knowledge/outlines.js +167 -12
  19. package/dist/knowledge/policy.js +14 -5
  20. package/dist/knowledge/propTypes.js +80 -0
  21. package/dist/knowledge/rules.js +961 -44
  22. package/dist/knowledge/schemaGuard.js +199 -0
  23. package/dist/knowledge/styleResolve.js +63 -4
  24. package/dist/observability/trace.js +27 -3
  25. package/dist/platform/csv.js +123 -0
  26. package/dist/platform/errors.js +107 -6
  27. package/dist/platform/http.js +34 -4
  28. package/dist/platform/patch.js +27 -0
  29. package/dist/platform/refs.js +104 -0
  30. package/dist/platform/responses.js +25 -6
  31. package/dist/platform/screenshot.js +544 -16
  32. package/dist/platform/secrets.js +40 -0
  33. package/dist/platform/tokenVault.js +89 -0
  34. package/dist/platform/writeDiff.js +281 -0
  35. package/dist/resources/examples/flow-call-function.json +66 -0
  36. package/dist/resources/examples/function-total.json +70 -0
  37. package/dist/resources/examples/layout-app-shell.json +1 -1
  38. package/dist/resources/examples/microfunction-node-sum.json +56 -0
  39. package/dist/resources/examples/microservice-table-list.json +2 -4
  40. package/dist/resources/examples/page-dynamic-form.json +3 -3
  41. package/dist/resources/examples/page-flex-sections.json +99 -0
  42. package/dist/resources/examples/page-sign-in.json +4 -3
  43. package/dist/resources/guides/component.md +81 -8
  44. package/dist/resources/guides/entity.md +712 -20
  45. package/dist/resources/guides/expressions.md +47 -2
  46. package/dist/resources/guides/external-component.md +9 -0
  47. package/dist/resources/guides/functions.md +262 -0
  48. package/dist/resources/guides/getting-started.md +82 -7
  49. package/dist/resources/guides/layout.md +53 -10
  50. package/dist/resources/guides/merge.md +231 -0
  51. package/dist/resources/guides/microfunction.md +166 -0
  52. package/dist/resources/guides/microservice.md +287 -15
  53. package/dist/resources/guides/middleware.md +89 -1
  54. package/dist/resources/guides/modify-existing.md +23 -0
  55. package/dist/resources/guides/page.md +402 -49
  56. package/dist/resources/guides/translation.md +150 -8
  57. package/dist/resources/guides/view.md +16 -1
  58. package/dist/resources/guides/workflow.md +199 -28
  59. package/dist/resources/knowledge.js +5 -0
  60. package/dist/resources/rules.js +27 -0
  61. package/dist/resources/schema-fingerprint.json +8 -0
  62. package/dist/resources/schemas/AnalyzeImportQuery.json +51 -0
  63. package/dist/resources/schemas/ApplyMergePlanCommand.json +19 -0
  64. package/dist/resources/schemas/CreateBulkRowCommand.json +36 -0
  65. package/dist/resources/schemas/CreateDatasourceCommand.json +15 -6
  66. package/dist/resources/schemas/CreateDiscoveredEntitiesCommand.json +19 -8
  67. package/dist/resources/schemas/CreateEntityCommand.json +52 -12
  68. package/dist/resources/schemas/CreateEnvironmentCommand.json +31 -0
  69. package/dist/resources/schemas/CreateGlobalFunctionCommand.json +95 -0
  70. package/dist/resources/schemas/CreateImportPlanCommand.json +278 -0
  71. package/dist/resources/schemas/CreateMergePlanCommand.json +34 -0
  72. package/dist/resources/schemas/CreateMicrofunctionCommand.json +117 -0
  73. package/dist/resources/schemas/CreateMicrofunctionRuntimeCommand.json +51 -0
  74. package/dist/resources/schemas/CreateMicroserviceCommand.json +145 -3
  75. package/dist/resources/schemas/CreateQueryCommand.json +56 -3
  76. package/dist/resources/schemas/CreateRowCommand.json +38 -0
  77. package/dist/resources/schemas/CreateViewCommand.json +13 -10
  78. package/dist/resources/schemas/CreateWorkflowCommand.json +106 -14
  79. package/dist/resources/schemas/DataJobDto.json +267 -0
  80. package/dist/resources/schemas/ExportRowsCommand.json +172 -0
  81. package/dist/resources/schemas/Filter.json +3 -1
  82. package/dist/resources/schemas/GetRowsQuery.json +6 -1
  83. package/dist/resources/schemas/GlobalFunctionDto.json +89 -0
  84. package/dist/resources/schemas/GlobalFunctionUpdateResultDto.json +249 -0
  85. package/dist/resources/schemas/ImportAnalysisDto.json +401 -0
  86. package/dist/resources/schemas/ImportPlanDto.json +260 -0
  87. package/dist/resources/schemas/ImportSampleDto.json +90 -0
  88. package/dist/resources/schemas/InvokeMicrofunctionCommand.json +25 -0
  89. package/dist/resources/schemas/PreviewMicroserviceCommand.json +135 -1
  90. package/dist/resources/schemas/PreviewQueryCommand.json +5 -2
  91. package/dist/resources/schemas/PreviewWorkflowCommand.json +45 -9
  92. package/dist/resources/schemas/ResolveMergeConflictsCommand.json +58 -0
  93. package/dist/resources/schemas/RevertMergePlanCommand.json +19 -0
  94. package/dist/resources/schemas/RunImportCommand.json +62 -0
  95. package/dist/resources/schemas/StepDto.json +34 -3
  96. package/dist/resources/schemas/UpdateBulkRowCommand.json +36 -0
  97. package/dist/resources/schemas/UpdateDatasourceCommand.json +15 -6
  98. package/dist/resources/schemas/UpdateEntityCommand.json +52 -11
  99. package/dist/resources/schemas/UpdateGlobalFunctionCommand.json +66 -0
  100. package/dist/resources/schemas/UpdateMicrofunctionCommand.json +121 -0
  101. package/dist/resources/schemas/UpdateMicrofunctionRuntimeCommand.json +54 -0
  102. package/dist/resources/schemas/UpdateMicroserviceCommand.json +147 -4
  103. package/dist/resources/schemas/UpdateQueryCommand.json +13 -3
  104. package/dist/resources/schemas/UpdateRowCommand.json +40 -0
  105. package/dist/resources/schemas/UpdateViewCommand.json +15 -10
  106. package/dist/resources/schemas/UpdateWorkflowCommand.json +106 -14
  107. package/dist/server.js +17 -1
  108. package/dist/tools/access.js +3 -3
  109. package/dist/tools/activities.js +40 -0
  110. package/dist/tools/applications.js +1 -1
  111. package/dist/tools/automations.js +76 -34
  112. package/dist/tools/common.js +55 -0
  113. package/dist/tools/components.js +16 -7
  114. package/dist/tools/datasources.js +83 -21
  115. package/dist/tools/dependencies.js +1 -1
  116. package/dist/tools/deployments.js +34 -2
  117. package/dist/tools/dtos.js +25 -12
  118. package/dist/tools/entities.js +113 -28
  119. package/dist/tools/environments.js +52 -0
  120. package/dist/tools/externalComponents.js +9 -3
  121. package/dist/tools/files.js +57 -42
  122. package/dist/tools/flows.js +205 -13
  123. package/dist/tools/folders.js +7 -2
  124. package/dist/tools/functions.js +331 -0
  125. package/dist/tools/globalFunctions.js +63 -0
  126. package/dist/tools/importSchemas.js +79 -0
  127. package/dist/tools/imports.js +241 -0
  128. package/dist/tools/layouts.js +103 -29
  129. package/dist/tools/merges.js +113 -0
  130. package/dist/tools/microfunctions.js +155 -0
  131. package/dist/tools/microservices.js +270 -40
  132. package/dist/tools/organizations.js +5 -6
  133. package/dist/tools/pages.js +421 -104
  134. package/dist/tools/platformInfo.js +155 -11
  135. package/dist/tools/projects.js +183 -33
  136. package/dist/tools/queries.js +32 -10
  137. package/dist/tools/revisions.js +94 -0
  138. package/dist/tools/rows.js +484 -50
  139. package/dist/tools/screenWrite.js +68 -0
  140. package/dist/tools/screens.js +28 -0
  141. package/dist/tools/templates.js +32 -5
  142. package/dist/tools/translations.js +24 -8
  143. package/dist/tools/views.js +20 -10
  144. package/dist/tools/workflows.js +56 -21
  145. package/package.json +7 -2
@@ -1,3 +1,4 @@
1
+ import { functionSignature } from './functionGuards.js';
1
2
  function asArray(value) {
2
3
  return Array.isArray(value) ? value.filter((entry) => Boolean(entry) && typeof entry === 'object') : [];
3
4
  }
@@ -62,6 +63,8 @@ export function entityOutline(definition) {
62
63
  slug: definition.slug,
63
64
  name: definition.name,
64
65
  datasourceId: definition.datasourceId,
66
+ localizationEntityId: definition.localizationEntityId ?? null,
67
+ localizationParentId: definition.localizationParentId ?? null,
65
68
  fieldCount: asArray(definition.fields).length,
66
69
  fields: asArray(definition.fields).map(field => ({
67
70
  id: field.id,
@@ -73,10 +76,116 @@ export function entityOutline(definition) {
73
76
  relationId: field.relationId ?? null,
74
77
  isLocalized: Boolean(field.isLocalized)
75
78
  })),
79
+ indexCount: asArray(definition.indexes).length,
80
+ // Index'ler alanlardan çok daha az sayıdadır; tam projeksiyon outline'ı şişirmez ve yazılan
81
+ // index'in kanıtı ayrı bir tur istemez (B-45'in `entity_get`/`entity_create` ayağı).
82
+ ...(asArray(definition.indexes).length > 0
83
+ ? {
84
+ indexes: asArray(definition.indexes).map(index => ({
85
+ id: index.id,
86
+ name: index.name,
87
+ unique: Boolean(index.unique),
88
+ fieldIds: Array.isArray(index.fieldIds) ? index.fieldIds : []
89
+ }))
90
+ }
91
+ : {}),
76
92
  view: 'outline',
77
93
  hint: "Bir alanın tam gövdesi için view:'part', part:'fields[slug=<slug>]'; değişiklik için entity_patch. isLocalized alanlar için platform_guide('entity') §'Çoklu dilli alan'."
78
94
  };
79
95
  }
96
+ const identityKeys = ['id', 'slug', 'name', 'elementId', 'stepId'];
97
+ /**
98
+ * Eklenen öğeyi FİNAL gövdede kimliğinden bulur. Konumsal ("dizinin son elemanı") arama
99
+ * platform diziye kendi öğesini eklediğinde yanlış kaydı gösterir: audit trail açık bir
100
+ * entity'ye alan eklendiğinde platform `fields`in sonuna dört `sys_*` alanı ekler ve rapor
101
+ * eklenen alan yerine `sys_updated_user_id`'yi gösteriyordu (ölçüldü 2026-09-08).
102
+ */
103
+ function appendedPosition(section, value) {
104
+ if (!value || typeof value !== 'object')
105
+ return -1;
106
+ const source = value;
107
+ for (const key of identityKeys) {
108
+ const wanted = source[key];
109
+ if (wanted === undefined || wanted === null || wanted === '')
110
+ continue;
111
+ const position = section.findIndex(item => Boolean(item) && typeof item === 'object' && item[key] === wanted);
112
+ if (position >= 0)
113
+ return position;
114
+ }
115
+ return -1;
116
+ }
117
+ /**
118
+ * Patch'in DOKUNDUĞU bölümleri kayıttan çıkarır (S-8 · P2-1).
119
+ *
120
+ * `entity_patch` yanıtı bir outline'dır ve outline `indexes` taşımıyordu: 2026-09-06 koşusunda
121
+ * iki index yazıldı, "yazdım" demenin kanıtı yanıtta olmadığı için her entity için ayrı bir
122
+ * `entity_get(view:'part', part:'indexes[0]')` turu atıldı. Keşif çağrısı oranı o koşuda %30.1'di.
123
+ *
124
+ * Outline'ın kısa kalma amacı korunur: TAM gövde değil, yalnız yamanın adreslediği bölüm döner —
125
+ * dizide de yalnız dokunulan eleman. Yollar `resolvePatchOperations`'tan geçmiş, yani indekse
126
+ * çevrilmiş olmalıdır; `-` (dizi sonu) eklenen son elemana çözülür.
127
+ */
128
+ export function patchedSections(document, operations) {
129
+ const explicit = new Map();
130
+ const appends = new Map();
131
+ const whole = new Set();
132
+ for (const operation of operations) {
133
+ const path = typeof operation.path === 'string' ? operation.path : '';
134
+ if (!path.startsWith('/'))
135
+ continue;
136
+ const [key, index] = path.split('/').slice(1);
137
+ if (!key || !Object.hasOwn(document, key))
138
+ continue;
139
+ if (!Array.isArray(document[key]) || index === undefined) {
140
+ whole.add(key);
141
+ continue;
142
+ }
143
+ // Dizi sonu (`-`) yolu FİNAL gövdede tek bir indekse çözülemez: aynı çağrıda iki `add`
144
+ // varsa ikisi de son elemanı gösterir ve rapor bir tanesini yutardı (ölçüldü 2026-09-06,
145
+ // gerçek uçta: iki index eklendi, yanıt birini gösterdi). Eklenen gövde saklanır ve önce
146
+ // KİMLİKTEN aranır; kimlik tutmazsa son N eleman alınır.
147
+ if (index === '-') {
148
+ appends.set(key, [...(appends.get(key) ?? []), operation.value]);
149
+ continue;
150
+ }
151
+ const position = Number(index);
152
+ if (!Number.isInteger(position) || position < 0)
153
+ continue;
154
+ const positions = explicit.get(key) ?? new Set();
155
+ positions.add(position);
156
+ explicit.set(key, positions);
157
+ }
158
+ const touched = {};
159
+ for (const key of whole)
160
+ touched[key] = document[key];
161
+ for (const key of new Set([...explicit.keys(), ...appends.keys()])) {
162
+ if (whole.has(key))
163
+ continue;
164
+ const section = document[key];
165
+ const wanted = new Set();
166
+ for (const position of explicit.get(key) ?? []) {
167
+ if (position < section.length)
168
+ wanted.add(position);
169
+ }
170
+ let unresolved = 0;
171
+ for (const value of appends.get(key) ?? []) {
172
+ const position = appendedPosition(section, value);
173
+ if (position >= 0)
174
+ wanted.add(position);
175
+ else
176
+ unresolved++;
177
+ }
178
+ for (let step = 0; step < unresolved; step++) {
179
+ const position = section.length - 1 - step;
180
+ if (position >= 0)
181
+ wanted.add(position);
182
+ }
183
+ if (wanted.size === 0)
184
+ continue;
185
+ touched[key] = [...wanted].sort((left, right) => left - right).map(position => section[position]);
186
+ }
187
+ return touched;
188
+ }
80
189
  const segmentPattern = /^(\w+)(?:\[(?:(\d+)|(\w+)=([^\]]+))\])?$/;
81
190
  export function resolvePart(definition, part) {
82
191
  let current = definition;
@@ -111,7 +220,7 @@ function unescapePointer(segment) {
111
220
  * `*_get`'in `part` sözdizimindeki seçiciyi (`steps[id=…]`, `fields[slug=…]`) patch yollarında da
112
221
  * kabul edip indekse çeviriyoruz; çözülemeyen seçici, gövde platforma GİTMEDEN hata verir.
113
222
  */
114
- export function resolvePatchPointer(document, path) {
223
+ export function resolvePatchPointer(document, path, readTool = 'workflow_get', op = '') {
115
224
  if (!path.startsWith('/'))
116
225
  throw new Error(`JSON Patch yolu '/' ile başlamalı: '${path}'`);
117
226
  const segments = path.split('/').slice(1);
@@ -131,11 +240,21 @@ export function resolvePatchPointer(document, path) {
131
240
  const [, key, field, value] = match;
132
241
  const collection = current && typeof current === 'object' ? current[key] : undefined;
133
242
  if (!Array.isArray(collection)) {
134
- throw new Error(`Patch yolu çözülemedi: '${path}' — '${key}' bir dizi değil. Mevcut yapıyı workflow_get view:'outline' ile okuyun.`);
243
+ throw new Error(`Patch yolu çözülemedi: '${path}' — '${key}' bir dizi değil. Mevcut yapıyı ${readTool} view:'outline' ile okuyun.`);
135
244
  }
136
245
  const index = collection.findIndex(entry => entry && typeof entry === 'object' && String(entry[field]) === value);
137
246
  if (index === -1) {
138
- throw new Error(`Patch yolu çözülemedi: '${path}' — '${key}' dizisinde ${field}='${value}' yok. Mevcut id/ad değerlerini workflow_get view:'outline' ile okuyun.`);
247
+ const available = collection
248
+ .map(entry => entry && typeof entry === 'object' ? entry[field] : undefined)
249
+ .filter(entry => entry !== undefined && entry !== null)
250
+ .map(entry => `'${String(entry)}'`);
251
+ // Seçici VAR OLAN bir öğeyi adresler; yeni öğe eklemenin yolu dizi sonu (`-`) segmentidir.
252
+ // 2026-09-06 koşusunda ajan eksik bir iteratee'yi `add` ile eklemeye çalıştı, mesaj yalnız
253
+ // "yok" dedi ve ajan diziyi 13 girdiyle baştan yazmak zorunda kaldı (S-5 · P0-2 Step 6).
254
+ const addHint = op === 'add'
255
+ ? ` EKLEMEK istiyorsanız seçici değil dizi sonu yolunu kullanın: '${path.slice(0, path.indexOf(`${key}[`) + key.length)}/-' — seçici yalnız VAR OLAN öğeyi adresler.`
256
+ : '';
257
+ throw new Error(`Patch yolu çözülemedi: '${path}' — '${key}' dizisinde ${field}='${value}' yok. Mevcut değerler: ${available.length > 0 ? available.join(', ') : '(hiç)'}.${addHint} Tam listeyi ${readTool} view:'outline' ile okuyun.`);
139
258
  }
140
259
  resolved.push(key, String(index));
141
260
  current = collection[index];
@@ -145,24 +264,30 @@ export function resolvePatchPointer(document, path) {
145
264
  return rewritten ? { path: resolvedPath, note: `${path} → ${resolvedPath}` } : { path: resolvedPath };
146
265
  }
147
266
  /**
148
- * Yanıtın `applied: N` demekle yetinmemesi için: her op'un hangi step'e indiğini adıyla söyler.
267
+ * Yanıtın `applied: N` demekle yetinmemesi için: her op'un hangi öğeye indiğini adıyla söyler.
268
+ * Seçiciyle yazılan yol indekse çevrildiği için, indeksin hangi kayda denk geldiğini görmek
269
+ * sessiz veri bozulmasını önleyen tek sinyaldir (entity'de alan sırası değişebilir).
149
270
  */
150
271
  export function describePatchTarget(document, path) {
151
- const match = /^\/steps\/(\d+)(\/.*)?$/.exec(path);
272
+ const match = /^\/(steps|fields|indexes)\/(\d+)(\/.*)?$/.exec(path);
152
273
  if (!match)
153
274
  return path;
154
- const step = asArray(document.steps)[Number(match[1])];
155
- return step
156
- ? `${path} → '${String(step.name ?? step.id ?? '?')}' (${String(step.type ?? '?')}, id ${String(step.id ?? '?')})`
157
- : `${path} → (dizide ${match[1]}. eleman YOK)`;
275
+ const collection = asArray(document[match[1]]);
276
+ const entry = collection[Number(match[2])];
277
+ if (!entry)
278
+ return `${path} → (dizide ${match[2]}. eleman YOK)`;
279
+ const label = String(entry.name ?? entry.slug ?? entry.id ?? '?');
280
+ const kind = entry.type ?? entry.dbType;
281
+ return `${path} → '${label}'${kind ? ` (${String(kind)}` + `, id ${String(entry.id ?? '?')})` : `, id ${String(entry.id ?? '?')}`}`;
158
282
  }
159
- export function resolvePatchOperations(document, operations) {
283
+ export function resolvePatchOperations(document, operations, readTool = 'workflow_get') {
160
284
  const notes = [];
161
285
  const targets = [];
162
286
  const resolved = operations.map(operation => {
163
287
  const path = typeof operation.path === 'string' ? operation.path : '';
164
- const outcome = resolvePatchPointer(document, path);
165
- const from = typeof operation.from === 'string' ? resolvePatchPointer(document, operation.from) : undefined;
288
+ const op = typeof operation.op === 'string' ? operation.op : '';
289
+ const outcome = resolvePatchPointer(document, path, readTool, op);
290
+ const from = typeof operation.from === 'string' ? resolvePatchPointer(document, operation.from, readTool, op) : undefined;
166
291
  if (outcome.note)
167
292
  notes.push(outcome.note);
168
293
  if (from?.note)
@@ -172,3 +297,33 @@ export function resolvePatchOperations(document, operations) {
172
297
  });
173
298
  return { operations: resolved, notes, targets };
174
299
  }
300
+ /**
301
+ * Fonksiyon özeti (G-07): kaynak, ad, kapsam, imza, parametre listesi, gövde satır sayısı —
302
+ * gövdenin kendisi TAŞINMAZ (liste yanıtı MAX_RESPONSE_BYTES'ı şişirmesin). `function_patch`
303
+ * AÇILMADI: fonksiyonda tek büyük alan gövdedir ve gövde bütün olarak yazılır; kısmi güncellemeyi
304
+ * `function_update` mevcut kaydı okuyup eksik alanları doldurarak sağlar.
305
+ */
306
+ export function functionOutline(definition) {
307
+ const parameters = asArray(definition.parameters);
308
+ const source = definition.source === 'workflow' ? 'workflow' : 'studio';
309
+ return {
310
+ id: definition.id,
311
+ source,
312
+ name: definition.name,
313
+ applicationId: definition.applicationId ?? null,
314
+ scope: source === 'workflow' ? null : (definition.scope ?? 'application'),
315
+ pageId: definition.pageId ?? null,
316
+ isAsync: definition.isAsync === true,
317
+ signature: functionSignature(definition),
318
+ parameters: parameters.map(parameter => ({
319
+ name: parameter.name,
320
+ type: parameter.type ?? 'any',
321
+ required: parameter.required === true,
322
+ defaultValue: parameter.defaultValue ?? null,
323
+ description: parameter.description ?? null
324
+ })),
325
+ description: definition.description ?? null,
326
+ bodyLines: typeof definition.body === 'string' && definition.body.length > 0 ? definition.body.split('\n').length : 0,
327
+ view: 'outline'
328
+ };
329
+ }
@@ -11,6 +11,19 @@ export class PolicyBlockError extends Error {
11
11
  this.name = 'PolicyBlockError';
12
12
  }
13
13
  }
14
+ /**
15
+ * `POLICY=warn` ve `policyOverride` kaçışları kuralın nereden geldiğine bakmaz: senkron
16
+ * `check`'lerden gelen bulgular da, platformdan okunarak üretilen bulgular da (WF-MF-001'in
17
+ * yayın durumu ön-denetimi) aynı kapıdan geçer. İki yerde iki farklı indirgeme olursa bir
18
+ * bulgu `policyOverride: true` ile de isteği durdurur ve kaçış yalanı çıkar.
19
+ */
20
+ export function applyPolicyMode(config, findings, policyOverride = false) {
21
+ if (config.policyMode !== 'warn' && !policyOverride)
22
+ return findings;
23
+ return findings.map(finding => finding.severity === 'block'
24
+ ? { ...finding, severity: 'warn', message: `${finding.message} [block → warn: ${policyOverride ? 'policyOverride' : 'POLICY=warn'}]` }
25
+ : finding);
26
+ }
14
27
  export function evaluatePolicy(config, rules, module, definition, context, policyOverride = false) {
15
28
  if (config.policyMode === 'off') {
16
29
  logStep('politika', `${module}/${context.operation} · POLICY=off — lint çalıştırılmadı`);
@@ -18,11 +31,7 @@ export function evaluatePolicy(config, rules, module, definition, context, polic
18
31
  }
19
32
  const applicable = rules.filter(rule => rule.module === module && rule.appliesTo.includes(context.operation));
20
33
  const raw = applicable.flatMap(rule => rule.check(definition, context));
21
- const findings = config.policyMode === 'warn' || policyOverride
22
- ? raw.map(finding => finding.severity === 'block'
23
- ? { ...finding, severity: 'warn', message: `${finding.message} [block → warn: ${policyOverride ? 'policyOverride' : 'POLICY=warn'}]` }
24
- : finding)
25
- : raw;
34
+ const findings = applyPolicyMode(config, raw, policyOverride);
26
35
  logStep('politika', `${module}/${context.operation}${context.workflowKind ? ` (${context.workflowKind})` : ''} · ${applicable.length} kural denendi · ${findings.length === 0 ? 'bulgu yok' : `${findings.length} bulgu`}${policyOverride ? ' · policyOverride açık' : ''}`, findings.map(finding => `[${finding.ruleId}] ${finding.severity}: ${finding.message}`));
27
36
  return findings;
28
37
  }
@@ -0,0 +1,80 @@
1
+ import { findNode } from '../tools/screens.js';
2
+ /**
3
+ * Prop değerinin `type` alanı üç değer alır ve her biri taşıyıcısı farklıdır.
4
+ *
5
+ * Platformun kendi micro-op applier'ı `replace_prop`'ta gönderilen ham değeri HER ZAMAN `Static`
6
+ * (ifade verilmişse `Code`) Prop'una sarar (`PageSchemaMicroOpApplier.cs:77`), yani kurulu bir
7
+ * etiketi çeviriye bağlamanın platform tarafında bir yolu yoktur. Bu op o boşluğu kapatır:
8
+ * şemayı okur, prop'un TİPİNİ yerinde değiştirir, gövdeyi tam yazar.
9
+ *
10
+ * Alternatifi `remove_component` + `add_component`'tı ve pahalıydı: 2026-09-06 koşusunda on bir
11
+ * etiket o yolla yeniden kuruldu, o düğümlere yazan DOKUZ setter iteratee'si platform tarafından
12
+ * sessizce silindi ve üç KPI kartı boş kaldı (S-5/G-2).
13
+ *
14
+ * Taşıyıcı: `static` değeri `data`'da; `code` ve `translation` ifadeyi/anahtarı `ref`'te taşır —
15
+ * runtime `data`'yı her turda çözümlenmiş değerle ezer, o yüzden anahtar yalnız `ref`'te yaşar
16
+ * (`platform_guide('translation')`).
17
+ */
18
+ export const PROP_TYPE_CARRIER = { static: 'data', code: 'ref', translation: 'ref' };
19
+ export const SET_PROP_TYPE_OP = 'set_prop_type';
20
+ export function isSetPropTypeOp(op) {
21
+ return Boolean(op) && typeof op === 'object' && String(op.op ?? '') === SET_PROP_TYPE_OP;
22
+ }
23
+ export function setPropTypeErrors(ops) {
24
+ const issues = [];
25
+ for (const op of ops) {
26
+ const target = String(op.target ?? '');
27
+ const prop = String(op.prop ?? '');
28
+ const propType = String(op.propType ?? '');
29
+ if (!target || !prop) {
30
+ issues.push("set_prop_type için 'target' (elementId) ve 'prop' (prop adı) zorunludur.");
31
+ continue;
32
+ }
33
+ const carrier = PROP_TYPE_CARRIER[propType];
34
+ if (!carrier) {
35
+ issues.push(`set_prop_type '${target}.${prop}': geçersiz propType '${propType || '(boş)'}'. Yazılabilir tipler: ${Object.keys(PROP_TYPE_CARRIER).join(' · ')}.`);
36
+ continue;
37
+ }
38
+ if (carrier === 'ref' && String(op.ref ?? '').trim().length === 0) {
39
+ issues.push(`set_prop_type '${target}.${prop}': propType '${propType}' değeri 'ref' alanında taşır ve 'ref' boş. ${propType === 'translation' ? "translation_list ile çeviri anahtarını bulun (ör. ref: 'dashboard.kpi.total')" : "ref: '<ifade>' verin (ör. 'Page.variables.total')"} — runtime 'data'yı her turda ezer, anahtar yalnız ref'te yaşar.`);
40
+ }
41
+ if (carrier === 'data' && op.data === undefined && op.value === undefined) {
42
+ issues.push(`set_prop_type '${target}.${prop}': propType 'static' değeri 'data' alanında taşır ve 'data' verilmedi. Mevcut değeri korumak istiyorsanız data'yı açıkça yazın.`);
43
+ }
44
+ }
45
+ return issues;
46
+ }
47
+ /**
48
+ * Op'ları verilen ağaca uygular. Ağacı YERİNDE değiştirir — çağıran kopyayı kendisi alır, çünkü
49
+ * sayfa ve layout hatları gövdeyi farklı biçimde geri yazıyor (`pageUpdateBody` / `layoutUpdateBody`).
50
+ * Bulunamayan hedefler atılmaz, `missingTargets` ile döner: çağıran kendi aracının adını içeren
51
+ * hata mesajını kurar.
52
+ */
53
+ export function applyPropTypesToSchema(schema, ops) {
54
+ const applied = [];
55
+ const missingTargets = [];
56
+ for (const op of ops) {
57
+ const target = String(op.target ?? '');
58
+ const prop = String(op.prop ?? '');
59
+ const propType = String(op.propType ?? '');
60
+ const node = findNode(schema, target);
61
+ if (!node) {
62
+ missingTargets.push(target);
63
+ continue;
64
+ }
65
+ const props = (node.props ?? {});
66
+ const existing = props[prop];
67
+ const current = existing && typeof existing === 'object' && !Array.isArray(existing)
68
+ ? existing
69
+ : undefined;
70
+ // `data` tasarım zamanı yedeğidir: `code`/`translation`'a geçerken mevcut metni koruyoruz ki
71
+ // Studio'da boş kutu görünmesin (runtime onu zaten çözümlenmiş değerle ezer).
72
+ const carried = op.data !== undefined ? op.data : op.value !== undefined ? op.value : current?.data ?? null;
73
+ props[prop] = propType === 'static'
74
+ ? { type: 'static', ref: null, data: carried }
75
+ : { type: propType, ref: String(op.ref), data: carried };
76
+ node.props = props;
77
+ applied.push(`${target}.${prop}: ${current?.type ?? '(yok)'} → ${propType}`);
78
+ }
79
+ return { applied, missingTargets };
80
+ }