@appaflytech/zeyrek-ai-mcp 0.0.30 → 0.0.32

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (203) hide show
  1. package/.env.example +0 -15
  2. package/README.md +5 -5
  3. package/dist/config.js +0 -37
  4. package/dist/index.js +0 -2
  5. package/dist/knowledge/backendConstraints.js +1 -18
  6. package/dist/knowledge/boxSections.js +184 -0
  7. package/dist/knowledge/componentCatalog.js +232 -562
  8. package/dist/knowledge/componentEvents.js +21 -141
  9. package/dist/knowledge/layoutSummary.js +2 -140
  10. package/dist/knowledge/normalize.js +67 -236
  11. package/dist/knowledge/outlines.js +0 -36
  12. package/dist/knowledge/policy.js +4 -18
  13. package/dist/knowledge/reservedTables.js +0 -79
  14. package/dist/knowledge/rules.js +21 -319
  15. package/dist/knowledge/schemaGuard.js +0 -11
  16. package/dist/knowledge/styleResolve.js +38 -55
  17. package/dist/knowledge/themeResolve.js +3 -3
  18. package/dist/observability/trace.js +3 -6
  19. package/dist/platform/diagnostics.js +4 -10
  20. package/dist/platform/domSummary.js +1 -1
  21. package/dist/platform/errors.js +10 -162
  22. package/dist/platform/http.js +8 -29
  23. package/dist/platform/responses.js +5 -39
  24. package/dist/platform/{capture/capture.js → screenshot.js} +519 -105
  25. package/dist/platform/secrets.js +1 -17
  26. package/dist/platform/strictArguments.js +12 -68
  27. package/dist/platform/tokenVault.js +4 -187
  28. package/dist/resources/examples/layout-app-shell.json +707 -229
  29. package/dist/resources/examples/page-detail-profile.json +1 -2
  30. package/dist/resources/examples/page-dynamic-form.json +2 -3
  31. package/dist/resources/examples/page-flex-sections.json +1 -1
  32. package/dist/resources/examples/page-html-embed.json +1 -1
  33. package/dist/resources/examples/workflow-basic.json +2 -8
  34. package/dist/resources/guides/component.md +4 -91
  35. package/dist/resources/guides/entity.md +8 -50
  36. package/dist/resources/guides/expressions.md +5 -14
  37. package/dist/resources/guides/functions.md +1 -1
  38. package/dist/resources/guides/getting-started.md +1 -38
  39. package/dist/resources/guides/layout.md +58 -55
  40. package/dist/resources/guides/microservice.md +2 -81
  41. package/dist/resources/guides/middleware.md +2 -44
  42. package/dist/resources/guides/page.md +75 -454
  43. package/dist/resources/guides/theme.md +2 -8
  44. package/dist/resources/guides/translation.md +1 -1
  45. package/dist/resources/guides/workflow.md +1 -58
  46. package/dist/resources/rules.js +5 -11
  47. package/dist/resources/schema-fingerprint.json +5 -7
  48. package/dist/resources/schemas/CreateEntityCommand.json +0 -118
  49. package/dist/resources/schemas/CreateMicroserviceCommand.json +1 -13
  50. package/dist/resources/schemas/CreateQueryCommand.json +0 -4
  51. package/dist/resources/schemas/CreateWorkflowCommand.json +1 -143
  52. package/dist/resources/schemas/DataJobDto.json +1 -6
  53. package/dist/resources/schemas/GlobalFunctionUpdateResultDto.json +1 -21
  54. package/dist/resources/schemas/MicroserviceDto.json +3 -15
  55. package/dist/resources/schemas/PreviewMicroserviceCommand.json +1 -10
  56. package/dist/resources/schemas/PreviewWorkflowCommand.json +1 -22
  57. package/dist/resources/schemas/StepDto.json +1 -22
  58. package/dist/resources/schemas/UpdateEntityCommand.json +0 -118
  59. package/dist/resources/schemas/UpdateMicroserviceCommand.json +1 -13
  60. package/dist/resources/schemas/UpdateQueryCommand.json +0 -4
  61. package/dist/resources/schemas/UpdateViewCommand.json +2 -0
  62. package/dist/resources/schemas/UpdateWorkflowCommand.json +1 -143
  63. package/dist/server.js +1 -27
  64. package/dist/tools/access.js +2 -369
  65. package/dist/tools/applications.js +3 -60
  66. package/dist/tools/common.js +0 -7
  67. package/dist/tools/components.js +14 -118
  68. package/dist/tools/datasources.js +3 -3
  69. package/dist/tools/dependencies.js +1 -1
  70. package/dist/tools/deployments.js +2 -2
  71. package/dist/tools/designSchemaPatch.js +18 -34
  72. package/dist/tools/dtos.js +5 -36
  73. package/dist/tools/entities.js +12 -79
  74. package/dist/tools/externalComponents.js +5 -26
  75. package/dist/tools/flows.js +14 -122
  76. package/dist/tools/folders.js +1 -1
  77. package/dist/tools/imports.js +2 -5
  78. package/dist/tools/layouts.js +17 -50
  79. package/dist/tools/merges.js +1 -1
  80. package/dist/tools/microservices.js +25 -77
  81. package/dist/tools/pages.js +112 -465
  82. package/dist/tools/platformInfo.js +8 -27
  83. package/dist/tools/projects.js +9 -71
  84. package/dist/tools/rows.js +13 -69
  85. package/dist/tools/schemaOps.js +1 -61
  86. package/dist/tools/screenWrite.js +8 -37
  87. package/dist/tools/screens.js +5 -95
  88. package/dist/tools/templates.js +1 -26
  89. package/dist/tools/translations.js +1 -1
  90. package/dist/tools/variables.js +5 -11
  91. package/dist/tools/workflows.js +5 -10
  92. package/package.json +2 -2
  93. package/dist/knowledge/access.js +0 -802
  94. package/dist/knowledge/case.js +0 -41
  95. package/dist/knowledge/connectDataBind.js +0 -819
  96. package/dist/knowledge/connectDataDefinition.js +0 -341
  97. package/dist/knowledge/connectDataExecute.js +0 -93
  98. package/dist/knowledge/connectDataRules.js +0 -128
  99. package/dist/knowledge/decision.js +0 -121
  100. package/dist/knowledge/expressionPrelude.js +0 -59
  101. package/dist/knowledge/microserviceParameters.js +0 -58
  102. package/dist/knowledge/mining.js +0 -94
  103. package/dist/knowledge/mobileComponentCatalog.js +0 -859
  104. package/dist/knowledge/mobileComponentEvents.js +0 -239
  105. package/dist/knowledge/platform/lint.js +0 -385
  106. package/dist/knowledge/platform/mobile.js +0 -142
  107. package/dist/knowledge/platform/mobileLayoutLint.js +0 -324
  108. package/dist/knowledge/platform/mobileRules.js +0 -231
  109. package/dist/knowledge/platform/mobileSpacing.js +0 -82
  110. package/dist/knowledge/platform/mobileTabs.js +0 -108
  111. package/dist/knowledge/platform/nativeBoxModel.js +0 -44
  112. package/dist/knowledge/platform/profile.js +0 -25
  113. package/dist/knowledge/platform/resolve.js +0 -122
  114. package/dist/knowledge/platform/web.js +0 -39
  115. package/dist/knowledge/process.js +0 -510
  116. package/dist/knowledge/rowFilter.js +0 -43
  117. package/dist/knowledge/styleResolveNative.js +0 -527
  118. package/dist/platform/argumentAliases.js +0 -128
  119. package/dist/platform/bodyFile.js +0 -103
  120. package/dist/platform/capture/actions.js +0 -333
  121. package/dist/platform/capture/browser.js +0 -1
  122. package/dist/platform/capture/console-log.js +0 -122
  123. package/dist/platform/capture/index.js +0 -11
  124. package/dist/platform/capture/readiness.js +0 -103
  125. package/dist/platform/capture/summary.js +0 -77
  126. package/dist/platform/capture/target.js +0 -5
  127. package/dist/platform/capture/targets/studio-mobile.js +0 -15
  128. package/dist/platform/capture/targets/studio-web.js +0 -5
  129. package/dist/platform/impersonation.js +0 -45
  130. package/dist/platform/jsonArray.js +0 -24
  131. package/dist/platform/schemaReadMemory.js +0 -60
  132. package/dist/resources/examples/bpm-gorev-kontrol.json +0 -294
  133. package/dist/resources/examples/bpm-iso-dokuman.json +0 -228
  134. package/dist/resources/examples/bpm-satinalma.json +0 -376
  135. package/dist/resources/examples/connect-data-convert-verify.json +0 -107
  136. package/dist/resources/examples/connect-data-dropdown-roles.json +0 -66
  137. package/dist/resources/examples/connect-data-edit-m2m.json +0 -73
  138. package/dist/resources/examples/connect-data-query-params.json +0 -50
  139. package/dist/resources/examples/connect-data-table-entity.json +0 -147
  140. package/dist/resources/examples/connect-data-workflow-microservice.json +0 -65
  141. package/dist/resources/examples/mobile-app-shell-drawer.json +0 -243
  142. package/dist/resources/examples/mobile-app-shell-stack.json +0 -157
  143. package/dist/resources/examples/mobile-app-shell-tab.json +0 -208
  144. package/dist/resources/examples/mobile-form-screen.json +0 -365
  145. package/dist/resources/examples/mobile-list-detail-form.json +0 -672
  146. package/dist/resources/guides/access.md +0 -796
  147. package/dist/resources/guides/bpm.md +0 -686
  148. package/dist/resources/guides/case.md +0 -153
  149. package/dist/resources/guides/decision.md +0 -185
  150. package/dist/resources/guides/mobile-layout.md +0 -193
  151. package/dist/resources/guides/mobile.md +0 -495
  152. package/dist/resources/read.js +0 -41
  153. package/dist/resources/schemas/AccessExplanationDto.json +0 -144
  154. package/dist/resources/schemas/AccessMatrixDto.json +0 -170
  155. package/dist/resources/schemas/AccessPermissionDto.json +0 -57
  156. package/dist/resources/schemas/AccessPolicyDto.json +0 -223
  157. package/dist/resources/schemas/AccessResourceDto.json +0 -86
  158. package/dist/resources/schemas/AppSessionDto.json +0 -53
  159. package/dist/resources/schemas/ConnectDataDescribeDto.json +0 -344
  160. package/dist/resources/schemas/ConnectDataRequestModel.json +0 -202
  161. package/dist/resources/schemas/ConnectDataResultDto.json +0 -421
  162. package/dist/resources/schemas/ConnectorCredentialDto.json +0 -70
  163. package/dist/resources/schemas/ConvertConnectDataToMicroserviceCommand.json +0 -37
  164. package/dist/resources/schemas/CreateAccessPolicyCommand.json +0 -209
  165. package/dist/resources/schemas/CreateDecisionCommand.json +0 -221
  166. package/dist/resources/schemas/CreateProcessDefinitionCommand.json +0 -1337
  167. package/dist/resources/schemas/DecisionDto.json +0 -302
  168. package/dist/resources/schemas/DetachConnectDataMicroserviceCommand.json +0 -28
  169. package/dist/resources/schemas/ExplainAccessQuery.json +0 -33
  170. package/dist/resources/schemas/GenerateProcessDocumentCommand.json +0 -116
  171. package/dist/resources/schemas/OrganizationProjectDto.json +0 -335
  172. package/dist/resources/schemas/OrganizationSchemaDto.json +0 -196
  173. package/dist/resources/schemas/OrganizationSchemaVersionDto.json +0 -207
  174. package/dist/resources/schemas/PatchProcessDefinitionCommand.json +0 -76
  175. package/dist/resources/schemas/ProcessDefinitionDto.json +0 -1480
  176. package/dist/resources/schemas/ProcessSimulationDto.json +0 -96
  177. package/dist/resources/schemas/SaveConnectorCredentialCommand.json +0 -90
  178. package/dist/resources/schemas/SaveProcessEventSourceCommand.json +0 -61
  179. package/dist/resources/schemas/SaveProcessMailSourceCommand.json +0 -89
  180. package/dist/resources/schemas/SignProcessTaskCommand.json +0 -73
  181. package/dist/resources/schemas/SimulateProcessDefinitionQuery.json +0 -92
  182. package/dist/resources/schemas/UpdateAccessPolicyCommand.json +0 -209
  183. package/dist/resources/schemas/UpdateDecisionCommand.json +0 -216
  184. package/dist/resources/schemas/UpdateProcessDefinitionCommand.json +0 -1357
  185. package/dist/resources/schemas/ValidateProcessDefinitionQuery.json +0 -1339
  186. package/dist/tools/accessOperations.js +0 -135
  187. package/dist/tools/accessPolicies.js +0 -170
  188. package/dist/tools/accessRuntime.js +0 -203
  189. package/dist/tools/bpm.js +0 -343
  190. package/dist/tools/bpmGovernance.js +0 -266
  191. package/dist/tools/bpmOperations.js +0 -230
  192. package/dist/tools/bpmReporting.js +0 -154
  193. package/dist/tools/bpmRouting.js +0 -150
  194. package/dist/tools/bpmRuntime.js +0 -396
  195. package/dist/tools/case.js +0 -178
  196. package/dist/tools/caseRuntime.js +0 -89
  197. package/dist/tools/connectData.js +0 -449
  198. package/dist/tools/connectors.js +0 -193
  199. package/dist/tools/decisions.js +0 -206
  200. package/dist/tools/mobile.js +0 -221
  201. package/dist/tools/modules.js +0 -48
  202. package/dist/tools/organizationStructure.js +0 -137
  203. package/dist/tools/profileWrite.js +0 -92
@@ -1,5 +1,4 @@
1
1
  import { listKnowledgeFiles, readKnowledgeFile } from '../resources/knowledge.js';
2
- import { readToolResource } from '../resources/read.js';
3
2
  import { splitGuideSections } from '../knowledge/guideSections.js';
4
3
  import { PlatformApiError } from '../platform/errors.js';
5
4
  import { logCapabilityGap } from '../observability/trace.js';
@@ -64,10 +63,8 @@ const guideRouting = {
64
63
  microservice: 'kaynak seçimi (table > query), selectFields disiplini',
65
64
  microfunction: 'kendi runtime\'ında koşan Node/Python fonksiyonu: protokol, yayın döngüsü, step kullanımı',
66
65
  entity: "alan tipleri, ilişkiler, hazır entity'ler",
67
- page: 'sayfa/flow işleri; prop\'u veri kaynağına bağlama (connectData, Kural 1c-3); flex düzen prop grubu ve boyut modları (Kural 1f-2)',
68
- layout: 'yerleşim kabuğu: tek slot, views, dar ekranda yığılma, widthType fill/fixed/auto',
69
- mobile: "mobil UYGULAMA (type:'mobile'): kurulum sırası (mobile_app_create), flex-only yerleşim, tipli stil, sekmeler (properties.mobile.tabs) ve çekmece, adım sözlüğü, MOB-* bulguları, page_screenshot device",
70
- 'mobile-layout': 'mobil ekranın mizanpajı — tek dil: güvenli alan, tek kaydırıcı, 16 dp kenar, gap ritmi, saydam/yüzeyli grup, genişlik, yarıçap/yazı ölçeği; MOB-015…021 reçeteleri ve layoutReport hiza ölçümü',
66
+ page: 'sayfa/flow işleri; flex düzen prop grubu ve boyut modları (Kural 1f-2)',
67
+ layout: 'yerleşim kabuğu: tek slot, views, mobil yığılma, widthType fill/fixed/auto',
71
68
  component: 'komponent modeli, olay sözleşmeleri, appearance boyut sözlüğü (widthString/heightString)',
72
69
  html: 'hazır komponent yetmediğinde tek seferlik kaçış kapısı (Mustache, fireEvent, script kuralları)',
73
70
  'external-component': 'kalıcı ihtiyaçta kendi komponentini yazıp yayınlama',
@@ -76,10 +73,7 @@ const guideRouting = {
76
73
  expressions: 'ifade fonksiyon/bağlam sözlüğü (system.*, file.*, Page.*, state.*, Steps…)',
77
74
  functions: "fonksiyonlar, iki kaynak tek biçim: source:'studio' tarayıcıda Functions.<ad>() (kapsam uygulama/sayfa, UI flow 'function' adımı), source:'workflow' sunucuda çıplak ad; parametre/varsayılan/required, tuzaklar; GÖVDE GÜNCELLEME function_update (yalnız değişen alanı gönder, patch aracı yok)",
78
75
  translation: 'sayfa/içerik çevirileri (çoklu dil)',
79
- access: 'kompleks proje yetkisi: organizasyon ağacı, izin kataloğu, kapsam, grant/atama, alan koruması (maske · şifre · türetilmiş alan)',
80
- merge: "dal aç, dalda çalış, development'a merge et; çakışma çözümü ve apply kapıları",
81
- bpm: 'süreç motoru: tanım modeli, düğümler, atama sözlüğü, bölge olayları, kutu ve görev eylemleri, bağlayıcı/belge/imza/kanallar (süreç ≠ workflow)',
82
- decision: 'DMN karar tabloları: model, isabet politikaları, FEEL alt kümesi, DRD, süreçte decision düğümü, DMN XML'
76
+ merge: "dal aç, dalda çalış, development'a merge et; çakışma çözümü ve apply kapıları"
83
77
  };
84
78
  // page.md 123 KB; section'sız dönerse yanıt tool çıktı sınırını aşıyor ve rehber MCP kanalından
85
79
  // HİÇ okunamıyor (27/05 E-1, 09-04 koşusu). Eşik ÖLÇÜLEN kopma noktasından türetilir, "bugün
@@ -98,8 +92,7 @@ export function registerPlatformTools(server, platform) {
98
92
  'Modülün iş kuralları rehberini döner. Bir modülde İLK yazma işleminden önce ilgili rehberi okuyun:',
99
93
  ...guides.map(name => `- ${name}${guideRouting[name] ? `: ${guideRouting[name]}` : ''}`),
100
94
  "Büyük rehberler (page ~120 KB) section'sız çağrıda TAM METİN DÖNMEZ: giriş + bölüm listesi (outline) döner, truncated: true olur. Gövdeyi section ile bölüm bölüm alın.",
101
- "Büyük gövdeyi (sayfa şeması, akış adımları, uç/workflow tanımı, patch op'ları) dosyaya yazıp bodyFile ile verin: gövde bağlamınızdan geçmez, satır içiyle aynı doğrulamadan geçer (page_*/flow_*/microservice_*/workflow_* create/update/patch). microservice_execute yanıt başlıkları varsayılan kısadır; ucun kendi başlığı için includeHeaders:'all'.",
102
- "Örnek gövdeler ve kural katalogları (ngapps://examples/<ad>, ngapps://rules/<ad>) istemci MCP resource okuyamıyorsa da bu araçla okunur: platform_guide({ resource: 'ngapps://rules/connect-data' }) — guide verilmez; dizin için 'ngapps://examples/index'."
95
+ 'Örnek gövdeler için ngapps://examples kaynaklarına bakın.'
103
96
  ].join('\n');
104
97
  server.registerTool('platform_whoami', {
105
98
  title: 'Oturum bilgisi',
@@ -111,23 +104,13 @@ export function registerPlatformTools(server, platform) {
111
104
  title: 'Platform rehberi oku',
112
105
  description: guideDescription,
113
106
  inputSchema: {
114
- guide: (guides.length > 0 ? z.enum(guides) : z.string()).optional()
115
- .describe('Rehber adı; bilinmeyen ad verilirse mevcut rehberler listelenir. resource ile birlikte verilmez'),
116
- resource: z.string().regex(/^ngapps:\/\//).optional()
117
- .describe("guide yerine bir ngapps:// kaynağı okur (examples ve rules): 'ngapps://examples/connect-data-table-entity', 'ngapps://rules/connect-data', dizinler '…/index'. Resource okuyamayan istemcinin rehberdeki ngapps:// yönlendirmelerini izleme yolu budur (38/M-14)."),
107
+ guide: (guides.length > 0 ? z.enum(guides) : z.string())
108
+ .describe('Rehber adı; bilinmeyen ad verilirse mevcut rehberler listelenir'),
118
109
  section: z.string().optional()
119
110
  .describe("Yalnız bu bölümü döner (yanıttaki 'sections' listesinden bir slug). Verilmezse rehber tam döner; ancak tool yanıt sınırını aşan büyük rehberlerde (page) yalnız giriş + outline döner")
120
111
  },
121
112
  annotations: readOnly
122
- }, handle(config, async ({ guide, section, resource }) => {
123
- if (resource !== undefined) {
124
- if (guide !== undefined || section !== undefined)
125
- throw new PlatformApiError("platform_guide: resource ile guide/section birlikte verilemez — biri ngapps:// kaynağını, diğeri rehber bölümünü okur.", 400);
126
- const read = readToolResource(resource);
127
- return 'error' in read ? read : { resource: read.uri, mimeType: read.mimeType, content: read.text };
128
- }
129
- if (guide === undefined)
130
- throw new PlatformApiError("platform_guide: 'guide' (rehber adı) ya da 'resource' (ngapps:// kaynağı) verin.", 400);
113
+ }, handle(config, async ({ guide, section }) => {
131
114
  const available = listKnowledgeFiles('guides').map(file => file.replace(/\.md$/, ''));
132
115
  if (!available.includes(guide)) {
133
116
  return { error: `'${guide}' diye bir rehber yok.`, available };
@@ -171,7 +154,6 @@ export function registerPlatformTools(server, platform) {
171
154
  title: 'Kabiliyet boşluğu bildir',
172
155
  description: "Yapmak İSTEDİĞİNİZ ama platform desteklemediği için yapamadığınız her noktada BUNU ÇAĞIRIN — tasarımı sessizce küçültmeyin. Bu çağrı platforma gitmez, yalnızca günlüğe 'KABİLİYET BOŞLUĞU' bloğu yazar; hata günlüğünde görünmeyen bu kayıtlar ürün yol haritasının girdisidir. Önce rehbere bakıp (platform_guide; hazır komponent yetmiyorsa tek seferlik için 'html' kaçış kapısı, kalıcı bir ihtiyaç içinse 'external-component' — kendi komponentinizi yazıp yayınlayabilirsiniz) gerçekten desteklenmediğinden emin olun: desteklenen bir şeyi 'yok' diye bildirmek yanlış yol haritası üretir.",
173
156
  inputSchema: {
174
- project: z.string().min(1).optional().describe("Boşluğun görüldüğü proje (ad ya da slug); kayda yazılır. Bu çağrı platforma gitmez, proje yalnız bağlamdır"),
175
157
  goal: z.string().min(1).optional().describe('Ne yapmak istediniz — kullanıcıya görünen sonuç olarak yazın ("randevu listesini hasta adına göre süzmek"). Eşanlamlı kabul edilir: title · summary · what.'),
176
158
  blocker: z.string().min(1).optional().describe('Neden yapamadınız — hangi kabiliyetin eksik olduğu ("uçta X yok", "komponentte Y prop\'u yok"). Eşanlamlı kabul edilir: reason · problem · missing · description.'),
177
159
  severity: z.string().optional().describe("YOL HARİTASINDAKİ SIRAYI BU ALAN BELİRLER, doldurun. blocker: özellik hiç yapılamadı · workaround: daha kötü bir yolla yapıldı · cosmetic: yalnız görsel/ergonomik kayıp. Eşanlamlılar çevrilir (critical/high/blocking→blocker, medium/major→workaround, low/minor/nit→cosmetic). TANINMAYAN değer reddedilir; verilmezse kayıt düşer ama yanıt 'incomplete' ile uyarır ve bildirim orta kovaya yazılır."),
@@ -189,10 +171,9 @@ export function registerPlatformTools(server, platform) {
189
171
  annotations: readOnly
190
172
  }, handle(config, async (input) => {
191
173
  const gap = normalizeGapReport(input);
192
- logCapabilityGap({ goal: gap.goal, blocker: gap.blocker, severity: gap.severity, module: gap.module, attempted: gap.attempted, workaround: gap.workaround, project: input.project });
174
+ logCapabilityGap({ goal: gap.goal, blocker: gap.blocker, severity: gap.severity, module: gap.module, attempted: gap.attempted, workaround: gap.workaround });
193
175
  return {
194
176
  recorded: true,
195
- ...(input.project ? { project: input.project } : {}),
196
177
  severity: gap.severity,
197
178
  module: gap.module ?? null,
198
179
  ...(Object.keys(gap.normalizedFrom).length > 0 ? { normalizedFrom: gap.normalizedFrom } : {}),
@@ -70,32 +70,6 @@ function rows(payload) {
70
70
  const data = payload?.data;
71
71
  return Array.isArray(data) ? data : [];
72
72
  }
73
- /**
74
- * 2026-09-15'ten eski backend'lerde `GET /p/{slug}` (`GetOrganizationProjectBySlugQuery`)
75
- * `OrganizationProjectDto.Map`'i yayın sürümü vermeden çağırıyordu: `organizationSchema.latestVersion` null
76
- * gelir, prune onu düşürür ve "latestVersion yoksa yayınlı şema yok" okuması yanlış olurdu (TB-67, backend'de
77
- * çözüldü). Slug yanıtı sürümü taşıyorsa olduğu gibi döner; taşımıyorsa (eski build) abonelik bloğu id ucundan
78
- * (`GetOrganizationProjectQuery`) alınır. İd ucu düşerse (o uç `RequirePermission` taşımaz, Organization.API onu
79
- * yalnız super-admin'e açar) slug yanıtı döner ve söylenir.
80
- */
81
- async function withPublishedSchemaVersion(platform, payload) {
82
- const data = payload?.data;
83
- if (!data || typeof data.id !== 'string' || data.accessModel !== 'complex')
84
- return payload;
85
- const latestVersion = data.organizationSchema?.latestVersion;
86
- if (latestVersion !== undefined && latestVersion !== null)
87
- return payload;
88
- try {
89
- const byId = await platform.org('GET', `/projects/${data.id}`);
90
- const organizationSchema = byId?.data?.organizationSchema;
91
- return organizationSchema && typeof organizationSchema === 'object'
92
- ? { ...payload, data: { ...data, organizationSchema } }
93
- : payload;
94
- }
95
- catch {
96
- return withNormalization(payload, ["organizationSchema.latestVersion doğrulanamadı (slug ucu GET /p/{slug} sürüm taşımaz, GET /projects/{id} okunamadı): yokluğu 'yayınlı şema yok' demek DEĞİLDİR — organization_schema_get ile doğrulayın (TB-67)."]);
97
- }
98
- }
99
73
  const applicationInput = z.object({
100
74
  slug: z.string().min(1),
101
75
  name: z.string().min(1),
@@ -150,35 +124,32 @@ export function registerProjectTools(server, platform) {
150
124
  const config = platform.config;
151
125
  server.registerTool('project_list', {
152
126
  title: 'Projeleri listele',
153
- description: "Organizasyondaki projeleri arar ve sayfalı listeler. Her satır accessModel ('simple' | 'complex') ve organizationSchema aboneliğini taşır (alanların anlamı project_get açıklamasında).",
127
+ description: 'Organizasyondaki projeleri arar ve sayfalı listeler.',
154
128
  inputSchema: { searchTerm: z.string().optional(), ...pagingParams },
155
129
  annotations: readOnly
156
130
  }, handle(config, args => platform.org('GET', '/projects', { query: args })));
157
131
  server.registerTool('project_get', {
158
132
  title: 'Proje getir',
159
- description: "Tek projeyi id ya da slug ile getirir; environment listesi de yanıttadır (her ortamda enforcementMode: off | shadow | enforce). Yanıttaki 'localization' bloğu gateway'in eklediği türetilmiş alandır: platform dil alanlarını null olduklarında serileştirmede düşürüyor, blok bunu görünür kılar ve runtime'ın çevirileri çözüp çözemeyeceğini söyler. accessModel projenin türüdür; organizationSchema { receiveUpdates, appliedVersion, latestVersion, customizedAt } kompleks projenin şema aboneliğidir — gateway null alanları düşürür: customizedAt YOKSA proje özelleştirilmemiştir, appliedVersion yoksa şema hiç uygulanmamıştır, latestVersion yoksa organizasyonda yayınlı şema yoktur (platform_guide('getting-started') §'Basit mi kompleks mi'). slug ile çağrıda platformun slug ucu latestVersion'ı taşımaz; gateway organizationSchema bloğunu GET /projects/{id}'den alır, o okuma düşerse yanıtta gatewayNormalized notu olur ve latestVersion'ın yokluğu bir şey söylemez (TB-67).",
133
+ description: "Tek projeyi id ya da slug ile getirir; environment listesi de yanıttadır. Yanıttaki 'localization' bloğu gateway'in eklediği türetilmiş alandır: platform dil alanlarını null olduklarında serileştirmede düşürüyor, blok bunu görünür kılar ve runtime'ın çevirileri çözüp çözemeyeceğini söyler.",
160
134
  inputSchema: { id: guid.optional(), slug: z.string().optional().describe('id verilmediyse zorunlu') },
161
135
  annotations: readOnly
162
136
  }, handle(config, async (args) => {
163
137
  if (!args.id && !args.slug) {
164
138
  throw new Error("'id' ya da 'slug' parametrelerinden biri zorunlu.");
165
139
  }
166
- if (args.id)
167
- return withLocalization(await platform.org('GET', `/projects/${args.id}`));
168
- return withLocalization(await withPublishedSchemaVersion(platform, await platform.org('GET', `/p/${args.slug}`)));
140
+ return withLocalization(await platform.org('GET', args.id ? `/projects/${args.id}` : `/p/${args.slug}`));
169
141
  }));
170
142
  server.registerTool('project_create', {
171
143
  title: 'Proje oluştur',
172
144
  description: "Yeni proje oluşturur. templateOptions verilmezse 'application' zorunludur (ilk application projeyle birlikte açılır). LoaderImage bu tool'da desteklenmez. " + constraintSummary('CreateOrganizationProjectCommand') + ` Gateway POST'tan önce project_list ile slug'ı arar; doluysa istek gitmez ve 409 anlamlı mesajla döner. DİL: create ucu artık languages/defaultLanguage alanlarını kendisi taşır ve gövdeyle birlikte gider. Vermezseniz gateway varsayılan [${SEED_LANGUAGES.join(', ')}] + '${SEED_DEFAULT_LANGUAGE}' tohumunu koyar ve bunu gatewayNormalized ile bildirir — dilsiz projede yazılan çeviriler runtime'da ham anahtar olarak çizilir (S-15). templateOptions verildiğinde TOHUM atlanır (template kendi dil ayarını taşır); açıkça verdiğiniz diller o yolda da gönderilir.`,
173
145
  inputSchema: {
174
146
  name: z.string().min(1),
175
- slug: z.string().min(1).describe("Veritabanı adı zeyrek_<org>_<slug> ve rol adı bundan türer: 2–40 karakter, yalnız küçük harf ve rakam, parçalar arasında tek '-' ya da '_' ('__' yasak); organizasyonlar arası tekil (PRJ-005). Sonradan değiştirmek bütün runtime rotalarını kırar"),
147
+ slug: z.string().min(1).describe('Organizasyonda tekil; veritabanı adı zeyrek_<org>_<slug> bundan türer ve sonradan değiştirmek bütün runtime rotalarını kırar'),
176
148
  theme: themeInput.optional().describe("Tema nesnesi (ya da JSON metni) — Studio tema panelinin yazdığı kayıtla aynı şekil: mode · palette · paletteDark · fontFamily · typeScale · shadow · presets · componentPresets. Verilmezse proje platformun varsayılan temasıyla (accent #0052CC, Inter 14px) doğar. Kurulmuş bir projenin temasını DEĞİŞTİRMEK için theme_update: kısmi günceller, şema/lint'ten geçirir ve hangi CSS değişkeninin değiştiğini söyler"),
177
149
  languages: z.array(z.string().min(1)).min(1).optional().describe(`Proje dilleri (ISO 639-1; 'tr-TR' gibi bölgeli değer kabul edilir ve 'tr'ye indirgenir, indirgeme gatewayNormalized ile bildirilir). Verilmezse gateway [${SEED_LANGUAGES.join(', ')}] tohumlar — projenin dilsiz doğmasına ve çevirilerin sessizce çözülmemesine izin verilmez`),
178
150
  defaultLanguage: z.string().min(1).optional().describe(`languages içinde olmalı (indirgenmiş hâliyle karşılaştırılır); yalnız defaultLanguage verirseniz languages onunla kurulur. İkisi de yoksa '${SEED_DEFAULT_LANGUAGE}'`),
179
151
  application: applicationInput.optional(),
180
- templateOptions: templateOptionsInput.optional().describe('Verilirse proje template\'ten kurulur (asenkron); sonucu project_list ile izleyin'),
181
- accessModel: z.enum(['simple', 'complex']).optional().describe("Proje türü; varsayılan 'simple' (bugünkü davranış: kimlik ve yetkiyi uygulamanın kendi middleware'i kurar). 'complex': proje organizasyonun YAYINLI SON rol şemasının kopyasıyla doğar (yayın yoksa organization_units/organization_positions boştur — ORG-002), platform yönetimli uygulama oturumu ve karar motoru devrededir, yeni ortamların zorlama modu 'enforce'tur; şema tablolarına genel satır yazımı kapalıdır (ORG-001). templateOptions ile verilirse yok sayılır — template kaynak projenin türünü taşır")
152
+ templateOptions: templateOptionsInput.optional().describe('Verilirse proje template\'ten kurulur (asenkron); sonucu project_list ile izleyin')
182
153
  },
183
154
  annotations: writes
184
155
  }, handle(config, async (args) => {
@@ -188,7 +159,7 @@ export function registerProjectTools(server, platform) {
188
159
  if (!languages.codes.includes(defaultLanguage.code)) {
189
160
  throw new PlatformApiError(`project_create: defaultLanguage '${defaultLanguage.code}' languages listesinde (${languages.codes.join(', ')}) yok — platform bunu 422 ile reddeder (PRJ-002). Kodu listeye ekleyin ya da listedeki bir kodu varsayılan yapın.`, 422);
190
161
  }
191
- const findings = enforcePolicy(evaluatePolicy(config, policyRules, 'project', args, { operation: 'create' }), false);
162
+ const findings = enforcePolicy(evaluatePolicy(config, policyRules, 'project', args, { operation: 'create' }));
192
163
  // PRJ-001: slug çakışması 2026-09-08 öncesi build'de 500 'DatabaseNameAlreadyExistsException'
193
164
  // dönüyordu (3. koşu S-1); arama ucu düşerse ön kontrol atlanır, karar platforma kalır.
194
165
  const existing = rows(await platform.org('GET', '/projects', { query: { searchTerm: args.slug, pageLength: 200 } }).catch(() => undefined))
@@ -218,14 +189,6 @@ export function registerProjectTools(server, platform) {
218
189
  form.set('TemplateOptions.TemplateReceiveUpdates', String(args.templateOptions.templateReceiveUpdates));
219
190
  }
220
191
  }
221
- // Template yolunda platform kaynak projenin türünü kullanır ve istemcinin gönderdiğini yok
222
- // sayar (05 §2); göndermek "complex istedim" yanılgısı üretirdi, gönderilmez ve söylenir.
223
- if (args.accessModel && args.templateOptions) {
224
- notes.push(`accessModel '${args.accessModel}' gönderilmedi: template yolunda proje türü template'in kaynak projesinden gelir. Sonucu project_get accessModel alanından doğrulayın.`);
225
- }
226
- else if (args.accessModel) {
227
- form.set('AccessModel', args.accessModel);
228
- }
229
192
  // Dil artık create ucunun kendi alanı (CreateOrganizationProjectCommand.Languages /
230
193
  // DefaultLanguage); ayrı bir PUT'a gerek yok. Template yolunda yalnız TOHUM atlanır: template
231
194
  // kendi dil ayarını getiriyor ve uydurulmuş bir varsayılanla onu ezmek yanlış olur — çağıranın
@@ -240,29 +203,7 @@ export function registerProjectTools(server, platform) {
240
203
  notes.push(`Proje dilleri gateway tarafından varsayılanla kuruldu: languages [${languages.codes.join(', ')}], defaultLanguage '${defaultLanguage.code}'. Dilsiz projede yazılan çeviriler runtime'da ham anahtar olarak çizilir (S-15). Başka dil istiyorsanız project_update ile değiştirin.`);
241
204
  }
242
205
  }
243
- const created = await platform.org('POST', '/projects', { form });
244
- const schema = (created?.data ?? {});
245
- if (schema.accessModel === 'complex') {
246
- // Oluşturma yanıtı 2026-09-15'ten beri sürümü taşır (TB-67 backend'de çözüldü); daha eski build'lerde
247
- // OrganizationProjectDto.Map sürümsüz çağrılıp alan null gelir — "yayın yok" demeden önce id ucu bir kez okunur.
248
- let latestVersion = schema.organizationSchema?.latestVersion;
249
- let versionUnknown = false;
250
- if ((latestVersion === undefined || latestVersion === null) && typeof schema.id === 'string') {
251
- try {
252
- const byId = await platform.org('GET', `/projects/${schema.id}`);
253
- latestVersion = byId?.data?.organizationSchema?.latestVersion;
254
- }
255
- catch {
256
- versionUnknown = true;
257
- }
258
- }
259
- notes.push(versionUnknown
260
- ? "Kompleks proje açıldı; organizasyonun yayınlı şema sürümü doğrulanamadı (oluşturma yanıtında latestVersion yok, GET /projects/{id} okunamadı). Organizasyon tablolarının dolu doğup doğmadığını organization_schema_get ve row_list('organization_units') ile doğrulayın (ORG-002, TB-67)."
261
- : latestVersion === undefined || latestVersion === null
262
- ? "Kompleks proje açıldı ama organizasyonda YAYINLI şema yok: organization_units / organization_positions tabloları boş doğdu (ORG-002). Şema yönetim konsolunda yayınlanınca abone projeye senkronla gelir; durumu organization_schema_get ve project_get organizationSchema ile izleyin."
263
- : `Kompleks proje organizasyon şemasının ${String(latestVersion)}. sürümüne abone doğdu; kopya her ortama senkronla yazılır (asenkron — project_get organizationSchema.appliedVersion dolunca tamamdır). Şema tablolarına row_* yazımı kapalıdır (ORG-001); ağacı row_list ile okuyun.`);
264
- }
265
- return attachFindings(withNormalization(created, notes), findings);
206
+ return attachFindings(withNormalization(await platform.org('POST', '/projects', { form }), notes), findings);
266
207
  }));
267
208
  server.registerTool('project_update', {
268
209
  title: 'Proje güncelle',
@@ -273,12 +214,11 @@ export function registerProjectTools(server, platform) {
273
214
  slug: z.string().optional(),
274
215
  theme: themeInput.optional().describe("Tema nesnesi (ya da JSON metni); verilen kayıt mevcut temanın YERİNE geçer — bir alanı değiştirmek için theme_update kullanın (kısmi birleştirir, lint'ten geçirir). VERİLMEZSE mevcut tema korunur — gateway projeden okuyup gövdeye geri koyar, çünkü platform Theme alanı gövdede yokken temayı siler ve silinen tema bütün ortamların runtime'ına iner (S-1). Temayı gerçekten temizlemek için theme: {} verin"),
275
216
  languages: z.array(languageCode).min(1).optional().describe("ISO 639-1 iki harfli kod listesi (proje dilleri, platformun 184 kodluk sözlüğünden — PRJ-002). Runtime çevirileri YALNIZ bu kodlarla çözer; liste kurulmadan yazılan çeviriler 200 döner ama ekrana ham anahtar basılır (S-15). Yeni proje bu liste BOŞ doğar — çeviri kullanacaksanız burayı kurmak ilk adımdır"),
276
- defaultLanguage: languageCode.optional().describe("languages içinde olmalı — proje dilleri hiç kurulmadıysa (ilk kurulumda) tek başına verilebilir. Runtime kayıtlı dil seçimi yokken buna düşer; boşsa aktif dil hiç seçilmez ve ilk açılışta bütün etiketler ham anahtar çizilir"),
277
- revealReasonMinLength: z.number().int().min(0).max(1000).optional().describe("Gerekçeli açmada (access_reveal) kabul edilen en kısa gerekçe; 0 kuralı kapatır. VERİLMEZSE mevcut değer korunur. Runtime reveal yolunda (access_reveal) yalnız kompleks projede etkilidir, Studio ızgara reveal'inde HER projede zorlanır; daha kısa gerekçe 400 RevealReasonTooShort alır ve denetim defterine REVEAL-DENIED düşer (TB-98)")
217
+ defaultLanguage: languageCode.optional().describe("languages içinde olmalı — proje dilleri hiç kurulmadıysa (ilk kurulumda) tek başına verilebilir. Runtime kayıtlı dil seçimi yokken buna düşer; boşsa aktif dil hiç seçilmez ve ilk açılışta bütün etiketler ham anahtar çizilir")
278
218
  },
279
219
  annotations: updates
280
220
  }, handle(config, async (args) => {
281
- const findings = enforcePolicy(evaluatePolicy(config, policyRules, 'project', args, { operation: 'update' }), false);
221
+ const findings = enforcePolicy(evaluatePolicy(config, policyRules, 'project', args, { operation: 'update' }));
282
222
  // Çağıranın hatası okumadan ÖNCE döner: bozuk tema metni için projeyi çekmenin anlamı yok.
283
223
  const requestedTheme = themeField(args.theme, 'project_update');
284
224
  // Platform gövdede slug yoksa onu ADDAN yeniden üretiyor. Yalnız dil ayarını değiştiren bir
@@ -302,8 +242,6 @@ export function registerProjectTools(server, platform) {
302
242
  languages?.codes.forEach(code => form.append('Languages', code));
303
243
  if (defaultLanguage)
304
244
  form.set('DefaultLanguage', defaultLanguage.code);
305
- if (args.revealReasonMinLength !== undefined)
306
- form.set('RevealReasonMinLength', String(args.revealReasonMinLength));
307
245
  const payload = await platform.org('PUT', `/projects/${args.id}`, { form });
308
246
  const notes = [
309
247
  ...(languages?.notes ?? []),
@@ -3,19 +3,16 @@ import { guid, entityRef, resolveEntityId, withResolved, projectParam, environme
3
3
  import { unmappedMembers, withUnmappedMembers } from '../knowledge/schemaGuard.js';
4
4
  import { withNormalization } from '../knowledge/normalize.js';
5
5
  import { RefResolver } from '../platform/refs.js';
6
- import { attachFindings, applyPolicyMode, evaluatePolicy, enforcePolicy } from '../knowledge/policy.js';
7
- import { protectedFilterFindings, systemEntityWriteFindings, systemManagedEntity } from '../knowledge/access.js';
8
- import { filterOperatorError, policyRules } from '../knowledge/rules.js';
6
+ import { attachFindings, evaluatePolicy, enforcePolicy } from '../knowledge/policy.js';
7
+ import { policyRules } from '../knowledge/rules.js';
9
8
  import { languageHeader } from '../knowledge/languageCodes.js';
10
9
  import { handle } from '../platform/responses.js';
11
10
  import { uploadFile } from './files.js';
12
11
  import { PlatformApiError } from '../platform/errors.js';
13
12
  import { secretKeyPattern } from '../platform/secrets.js';
14
13
  import { csvRows } from '../platform/csv.js';
15
- import { rowFilterSchema } from '../knowledge/rowFilter.js';
16
14
  import { readFile, stat, writeFile } from 'node:fs/promises';
17
15
  import { extname, resolve } from 'node:path';
18
- import { jsonArray } from '../platform/jsonArray.js';
19
16
  import { z } from 'zod';
20
17
  const rowValues = z.record(z.unknown()).describe("Alan SLUG'ı → değer sözlüğü; anahtarlar entity_get çıktısındaki field SLUG'larıdır (name değil — name kullanmak 'Please enter a valid value' hatası verir)");
21
18
  /**
@@ -38,17 +35,6 @@ function derivedRowFindings(rows) {
38
35
  guide: 'ngapps://guides/entity'
39
36
  }];
40
37
  }
41
- /**
42
- * ORG-001'in çözüm sonrası yarısı. Backend yalnız entity id'sine bakar (`SystemManagedEntityIds`); ön
43
- * denetim GUID'i, öneksiz slug'ı ve yerleşik datasource önekini istek gitmeden tanır, başka adla yazılmış
44
- * önek ise ancak çözülen id'yle anlaşılır. Dış datasource'taki aynı adlı tablo (`ik/user_positions`) başka
45
- * bir id'ye çözüldüğü için geçer. Ön denetim zaten eşleştiyse bulgu ikinci kez yazılmaz.
46
- */
47
- function resolvedSystemEntityFindings(config, entityId, resolvedEntityId, operation) {
48
- if (config.policyMode === 'off' || systemManagedEntity(entityId))
49
- return [];
50
- return enforcePolicy(applyPolicyMode(config, systemEntityWriteFindings(resolvedEntityId, operation)), false);
51
- }
52
38
  const bulkBatchSize = 1000;
53
39
  const maxPathBytes = 50 * 1024 * 1024;
54
40
  const maxPathRows = 100_000;
@@ -171,8 +157,8 @@ const templateFormatNotes = {
171
157
  csv: 'csv şablonda ipucu satırı ve `#zeyrek` satırı yoktur; başlıklar alan adıdır.',
172
158
  json: 'JSON şablon `{ meta, data }` gövdesidir; `meta` analizde atlanır, satırlar `data`dadır ve anahtarları alan slug\'ıdır.'
173
159
  };
174
- const rowFiltersParam = jsonArray(z.array(rowFilterSchema())).optional()
175
- .describe("Platform Filter dizisi: tekil { filterType: 'single', field, operator, value } ya da grup { filterType: 'group', conditionType: 'and'|'or', children: [...] }; işleçler platform enum'undan (ngapps://schemas/Filter)");
160
+ const rowFiltersParam = z.array(z.record(z.unknown())).optional()
161
+ .describe('Platform Filter dizisi; biçim için row_list\'i filtresiz çağırıp Studio\'daki bir filtreyi referans alın');
176
162
  const exportScopeParam = z.object({
177
163
  kind: z.enum(['view', 'all', 'selected']).describe("'view' filtre + sıralama ile, 'all' tüm satırlar, 'selected' rowIds listesi"),
178
164
  filters: rowFiltersParam,
@@ -230,7 +216,7 @@ export function registerRowTools(server, platform) {
230
216
  project: projectParam,
231
217
  environment: environmentParam,
232
218
  entityId: entityRef,
233
- selectedFields: z.array(z.string()).optional().describe('Dönen satırlar bu alan adlarına indirgenir, id her zaman dahildir; platform tam satırı hesapladığı için kırpma gateway tarafında yapılır. Bilinmeyen alan adı platformda 400 UnknownField döner (id ve sys_* bilinen sayılır) — sessizce düşmez.'),
219
+ selectedFields: z.array(z.string()).optional().describe('Dönen satırlar bu alan adlarına indirgenir, id her zaman dahildir; platform tam satırı hesapladığı için kırpma gateway tarafında yapılır'),
234
220
  filters: rowFiltersParam,
235
221
  language: languageParam,
236
222
  includeTranslations: includeTranslationsParam,
@@ -238,32 +224,26 @@ export function registerRowTools(server, platform) {
238
224
  },
239
225
  annotations: readOnly
240
226
  }, handle(config, async ({ project, environment, entityId, language, ...body }) => {
241
- const operatorError = filterOperatorError('row_list', body.filters);
242
- if (operatorError)
243
- throw new PlatformApiError(operatorError, 400);
244
227
  const notes = [];
245
228
  const headers = languageHeader(language, notes);
246
229
  const resolver = new RefResolver(platform, project, environment);
247
230
  const resolvedEntityId = await resolveEntityId(resolver, entityId);
248
- // PROT-001 / PROT-002: korumalı alanda metin araması ve sıralama platformda 400'dür; entity
249
- // yalnız riskli bir işleç ya da sıralama varsa okunur.
250
- const protectionFindings = enforcePolicy(applyPolicyMode(config, await protectedFilterFindings(platform, resolver, entityId, body)), false);
251
231
  const payload = await platform.tenant('POST', project, `/entities/${resolvedEntityId}/row/list`, { environment, body, headers });
252
232
  const wanted = body.selectedFields ?? [];
253
233
  const rows = payload && typeof payload === 'object' && !Array.isArray(payload) ? payload.data : undefined;
254
234
  if (!wanted.length || !Array.isArray(rows))
255
- return attachFindings(withNormalization(withResolved(payload, resolver.resolved), notes), protectionFindings);
235
+ return withNormalization(withResolved(payload, resolver.resolved), notes);
256
236
  /**
257
237
  * 'localizations' keep listesine girmezse includeTranslations'ın getirdiği çeviriler tam da
258
238
  * onları isteyen çağrıda kırpılırdı: iki parametre birlikte verildiğinde sessizce boşa çıkar.
259
239
  */
260
240
  const keep = new Set([...wanted.map(field => field.toLowerCase()), 'id', ...(body.includeTranslations ? ['localizations'] : [])]);
261
- return attachFindings(withNormalization(withResolved({
241
+ return withNormalization(withResolved({
262
242
  ...payload,
263
243
  data: rows.map(row => row && typeof row === 'object' && !Array.isArray(row)
264
244
  ? Object.fromEntries(Object.entries(row).filter(([key]) => keep.has(key.toLowerCase())))
265
245
  : row)
266
- }, resolver.resolved), notes), protectionFindings);
246
+ }, resolver.resolved), notes);
267
247
  }));
268
248
  server.registerTool('row_get', {
269
249
  title: 'Satır getir',
@@ -306,17 +286,17 @@ export function registerRowTools(server, platform) {
306
286
  const wantedLocalizations = localizations && Object.keys(localizations).length > 0 ? localizations : undefined;
307
287
  const fileRows = path ? await readRowsFromPath('row_create', path) : undefined;
308
288
  const bulkRows = rows ?? fileRows;
309
- const inputFindings = enforcePolicy(evaluatePolicy(config, policyRules, 'row', { entityId, ...(bulkRows ? { rows: bulkRows } : { row }) }, { operation: 'create' }), false);
289
+ const shapeFindings = enforcePolicy(evaluatePolicy(config, policyRules, 'row', bulkRows ? { rows: bulkRows } : { row }, { operation: 'create' }));
310
290
  const resolver = new RefResolver(platform, project, environment);
311
- const resolvedEntityId = await resolveEntityId(resolver, entityId);
312
- const shapeFindings = [...inputFindings, ...resolvedSystemEntityFindings(config, entityId, resolvedEntityId, 'create')];
313
291
  if (bulkRows) {
292
+ const bulkEntityId = await resolveEntityId(resolver, entityId);
314
293
  const bulkDrift = unmappedMembers('CreateBulkRowCommand', { rows: bulkRows, ...(rowContext ? { rowContext } : {}), ...(relationsByName ? { relationsByName } : {}) });
315
- const outcome = await writeInBatches(platform, 'POST', project, environment, headers, resolvedEntityId, bulkRows, rowContext, relationsByName);
294
+ const outcome = await writeInBatches(platform, 'POST', project, environment, headers, bulkEntityId, bulkRows, rowContext, relationsByName);
316
295
  if (fileRows)
317
296
  notes.push(`${path} okundu: ${fileRows.length} satır, ${Math.ceil(fileRows.length / bulkBatchSize)} parti.`);
318
297
  return attachFindings(withUnmappedMembers(withNormalization(withResolved(outcome, resolver.resolved), notes), bulkDrift), [...shapeFindings, ...(fileRows ? [] : derivedRowFindings(bulkRows))]);
319
298
  }
299
+ const resolvedEntityId = await resolveEntityId(resolver, entityId);
320
300
  const body = { row, ...(wantedLocalizations ? { localizations: wantedLocalizations } : {}), ...(rowContext ? { rowContext } : {}) };
321
301
  const drift = unmappedMembers('CreateRowCommand', body);
322
302
  const response = await platform.tenantWithHeaders('POST', project, `/entities/${resolvedEntityId}/row`, { environment, headers, body });
@@ -374,11 +354,10 @@ export function registerRowTools(server, platform) {
374
354
  const notes = [];
375
355
  const fileRows = path ? await readRowsFromPath('row_update', path) : undefined;
376
356
  const bulkRows = rows ?? fileRows;
377
- const inputFindings = enforcePolicy(evaluatePolicy(config, policyRules, 'row', { entityId, ...(bulkRows ? { rows: bulkRows } : { row }) }, { operation: 'update' }), false);
357
+ const shapeFindings = enforcePolicy(evaluatePolicy(config, policyRules, 'row', bulkRows ? { rows: bulkRows } : { row }, { operation: 'update' }));
378
358
  const headers = languageHeader(language, notes);
379
359
  const resolver = new RefResolver(platform, project, environment);
380
360
  const resolvedEntityId = await resolveEntityId(resolver, entityId);
381
- const shapeFindings = [...inputFindings, ...resolvedSystemEntityFindings(config, entityId, resolvedEntityId, 'update')];
382
361
  if (bulkRows) {
383
362
  const bulkDrift = unmappedMembers('UpdateBulkRowCommand', { rows: bulkRows, ...(rowContext ? { rowContext } : {}), ...(relationsByName ? { relationsByName } : {}) });
384
363
  const outcome = await writeInBatches(platform, 'PUT', project, environment, headers, resolvedEntityId, bulkRows, rowContext, relationsByName);
@@ -390,36 +369,6 @@ export function registerRowTools(server, platform) {
390
369
  const drift = unmappedMembers('UpdateRowCommand', body);
391
370
  return attachFindings(withUnmappedMembers(withNormalization(withResolved(await platform.tenant('PUT', project, `/entities/${resolvedEntityId}/row`, { environment, headers, body }), resolver.resolved), notes), drift), shapeFindings);
392
371
  }));
393
- server.registerTool('row_reassign', {
394
- title: 'Satırların sahibini değiştir',
395
- description: "Satırların SAHİPLİK alanlarını (sys_owner_unit_id · sys_owner_user_id) değiştirir (POST /{project}/entities/{entityId}/row/reassign). Bu alanlar row_update ile YAZILMAZ: gövdede görülürse istek 400 OwnershipFieldNotWritable alır (eskiden sessizce düşüyordu, S2-7). Hedef satırlar 'rowIds' ile ya da 'filter' ile seçilir; en az biri zorunlu. En az bir hedef sahip verilmelidir: ownerUnitId birime, ownerUserId kişiye devreder — yalnız ownerUserId verilirse birim de o kişinin aktif birincil pozisyonundan türetilir (pozisyonu yoksa satırın birimi olduğu gibi kalır). Her satıra REASSIGN denetim kaydı düşer (eski sahip → yeni sahip). KİMLİK: bu uç öteki row_* araçları gibi bir STUDIO ucudur ve gateway'in platform kimliğiyle koşar — uygulama jetonu (asUser) burada geçmez, 401 alır. Uygulama KULLANICISININ kapsamıyla devretmek (ve 'entity.reassign' kapsamını gerçekten ölçmek) için aynı işi yapan sistem operasyonunu kullanın: systemOperation 'entity.row.reassign' olan bir uç açıp microservice_execute { asUser } ile çağırın. Kapsam kuralı orada işler: çağıran yalnız 'entity.reassign' grant'inin satır kapsamındaki satırları devredebilir; kapsamı unit ya da unit-tree olan çağıran için SAHİPSİZ satırlar (sys_owner_unit_id NULL) da kapsam içi sayılır — şubesi kapanan kayıtlara başka türlü kimse ulaşamazdı (06 §A).",
396
- inputSchema: {
397
- project: projectParam,
398
- environment: environmentParam,
399
- entityId: entityRef,
400
- rowIds: z.array(z.string().min(1)).optional().describe("Devredilecek satırların birincil anahtar değerleri; 'filter' ile birlikte verilmez"),
401
- filter: rowFiltersParam.describe("Devredilecek satırları seçen Platform Filter dizisi (row_list ile aynı biçim); 'rowIds' ile birlikte verilmez"),
402
- ownerUnitId: guid.optional().describe("Yeni sahip birim (organization_tree_get); verilmezse ownerUserId'nin birimi yazılır"),
403
- ownerUserId: guid.optional().describe('Yeni sahip kullanıcı (user_list); verilmezse satırın kullanıcı sahibi olduğu gibi kalır')
404
- },
405
- annotations: updates
406
- }, handle(config, async ({ project, environment, entityId, rowIds, filter, ownerUnitId, ownerUserId }) => {
407
- if (rowIds?.length && filter?.length)
408
- throw new Error("row_reassign: 'rowIds' ve 'filter' birlikte verilmez — satırları ya id listesiyle ya filtreyle seçin.");
409
- if (!rowIds?.length && !filter?.length)
410
- throw new Error("row_reassign: hedef satır seçilmedi — 'rowIds' ya da 'filter' verin.");
411
- if (!ownerUnitId && !ownerUserId)
412
- throw new Error("row_reassign: hedef sahip verilmedi — 'ownerUnitId' ve/veya 'ownerUserId' verin.");
413
- const resolver = new RefResolver(platform, project, environment);
414
- const resolvedEntityId = await resolveEntityId(resolver, entityId);
415
- const body = {
416
- ...(rowIds?.length ? { rowIds } : {}),
417
- ...(filter?.length ? { filter } : {}),
418
- ...(ownerUnitId ? { ownerUnitId } : {}),
419
- ...(ownerUserId ? { ownerUserId } : {})
420
- };
421
- return withResolved(await platform.tenant('POST', project, `/entities/${resolvedEntityId}/row/reassign`, { environment, body }), resolver.resolved);
422
- }));
423
372
  server.registerTool('row_export', {
424
373
  title: 'Satırları dışa aktar',
425
374
  description: "Bir tablonun satırlarını xlsx / csv / json dosyasına yazar ve dosyayı projenin SÜRÜCÜSÜNE ('Dışa aktarmalar' klasörü) koyar; yanıt yalnız fileId ve sayılardır — dosya içeriği ajanın bağlamına HİÇ girmez, içeriği görmek için file_download kullanın. Kapsam üç şekildedir: 'view' (filtre + sıralama), 'all', 'selected' (rowIds). 5.000 satırın üstünde platform işi arka plana alır ve 202 ile jobId döner; import_status { jobId } ile izlenir, biten işin dosyası result.reportFileId'dedir. CSV UTF-8 BOM'lu yazılır; ayraç ve ondalık bir yerel ayar profilinden gelir (tr-TR `;` · `1,5`, en-US `,` · `1.5`): dışa aktarmada projenin dili seçer, şablonda 'locale' ile seçilir. 'reimportable: true' dosyaya id sütununu ve _zeyrek meta sayfasını ekler: dosya düzenlenip import_analyze → import_plan { mode: 'upsert', keyField: 'id' } ile geri yüklenebilir (Akış C). 'template: true' tablo satırı yazmaz: tablonun alanlarından İKİ SENTETİK örnek satırlı bir şablon üretir ve değiştirilmeden geri yüklenebilir. 'template: true, inline: true' şablonun şeklini (sütunlar, anahtar önerisi, iki satır) yanıta da koyar — toplu yazımdan önce dosyanın nasıl görünmesi gerektiğini indirmeden görmenin yolu budur; satır satır row_create yerine şablon başlıklarıyla CSV → file_upload { text } → import_analyze → import_plan → import_run, reçete platform_guide('entity') §'Dışa aktarma ve geri yükleme'.",
@@ -451,9 +400,6 @@ export function registerRowTools(server, platform) {
451
400
  if ((scope?.rowIds?.length ?? 0) > maxSelectedRowIds) {
452
401
  throw new PlatformApiError(`row_export: ${scope?.rowIds?.length} satır id'si, ${maxSelectedRowIds} sınırının üstünde. Bu kadar satırı seçerek değil filtreyle verin: scope { kind: 'view', filters: [...] }.`);
453
402
  }
454
- const operatorError = filterOperatorError('row_export', scope?.filters);
455
- if (operatorError)
456
- throw new PlatformApiError(operatorError, 400);
457
403
  const resolvedFormat = format ?? 'xlsx';
458
404
  const notes = [];
459
405
  const resolver = new RefResolver(platform, project, environment);
@@ -575,10 +521,8 @@ export function registerRowTools(server, platform) {
575
521
  throw new Error("'id' ve 'ids' birlikte verilemez.");
576
522
  if (!id && !ids)
577
523
  throw new Error("'id' ya da 'ids' parametrelerinden biri zorunlu.");
578
- enforcePolicy(evaluatePolicy(config, policyRules, 'row', { entityId }, { operation: 'delete' }), false);
579
524
  const resolver = new RefResolver(platform, project, environment);
580
525
  const resolvedEntityId = await resolveEntityId(resolver, entityId);
581
- resolvedSystemEntityFindings(config, entityId, resolvedEntityId, 'delete');
582
526
  if (ids)
583
527
  return withResolved(await platform.tenant('POST', project, `/entities/${resolvedEntityId}/row/bulk/delete`, { environment, body: { rowIds: ids } }), resolver.resolved);
584
528
  return withResolved(await platform.tenant('DELETE', project, `/entities/${resolvedEntityId}/row/${encodeURIComponent(String(id))}`, { environment }), resolver.resolved);
@@ -1,9 +1,7 @@
1
1
  import { isFlexContainer, schemaOpValidationErrors, FLEX_LAYOUT_PROP_NAMES, PAGE_CONTAINER_ELEMENT_ID } from '../knowledge/normalize.js';
2
2
  import { findNode, outlineAppliedPatch } from './screens.js';
3
3
  import { setPropTypeErrors } from '../knowledge/propTypes.js';
4
- import { connectDataOpIssues } from '../knowledge/connectDataDefinition.js';
5
4
  import { PlatformApiError } from '../platform/errors.js';
6
- import { jsonArray } from '../platform/jsonArray.js';
7
5
  import { z } from 'zod';
8
6
  /*
9
7
  * Şema micro-op'larının gateway tarafı: sayfa (`page_schema_patch`), layout (`layout_schema_patch`)
@@ -26,7 +24,7 @@ export function outlineWriteResponse(response) {
26
24
  if (Array.isArray(container.appliedPatch))
27
25
  container.appliedPatch = outlineAppliedPatch(container.appliedPatch);
28
26
  }
29
- export const opsParam = jsonArray(z.array(z.record(z.unknown()))).describe("Micro-op listesi. Op sözlüğü: replace_prop {target, prop, value} (value ham değer, code sarmalı {type: 'code', ref: '{{ … }}'} ya da translation sarmalı {type: 'translation', ref: '<çeviri anahtarı>'} — prop tipini değiştirmek için komponenti silip yeniden EKLEMEYİN, ona yazan setter'ların hedefi silinir (FLOW-018); platform replace_prop'ta expression alanını OKUMAZ — gateway onu code sarmalına çevirir) | set_binding {target, prop, expression: '{{ Page.variables.x }}'} | add_component {parent (üst elementId ya da 'root'), index?, component} | remove_component {target} | move_component {target, parent, index?, position: {x, y}} (index ÇİZİM sırası değildir, runtime çocukları blocks.position'a göre (y, sonra x) çizer. page_schema_patch'te flex kapta gateway index'i görünen sıraya çevirir ve kardeş konumlarını yeniden yazar; ızgara kapta sırayı position.y verir, sona eklemeyen index PAGE-033 uyarısıdır. layout_schema_patch ve component_schema_patch bu çeviriyi YAPMAZ: orada sırayı position.y ile verin) | set_event {target, event, eventValue, replaceBoundFlows?} (olayın BÜTÜN bağlarını bu tek akışa indirir; olayda başka akış bağlıysa gateway PAGE-034 ile reddeder — bilerek değiştirmek için replaceBoundFlows: true) | add_event {target, event, eventValue} (olaya bir akış DAHA bağlar; bağlı akışların hepsi paralel ve birbirinden bağımsız koşar, sıra garantisi yoktur — sıra gerekiyorsa flow-trigger. Aynı akışı ikinci kez bağlamak etkisizdir (unchanged; tekrar güvenli). Mobil runtime yalnız İLK akışı koşar) | remove_event {target, event, eventValue?} (olayı diziden çıkarır; target: 'page' sayfa düzeyi olaylar. Olaya birden fazla akış bağlıysa eventValue ile hangisi olduğunu verin, yoksa 422 ambiguous-event. set_event ile eventValue: null olay SİLMEZ, değersiz ölü kayıt bırakır. Olmayan olay 422 missing-event) | set_validations {target, validations: [{type, message, value, isActive}]} (komponentin validation listesini TAMAMEN değiştirir; boş dizi hepsini siler) | set_appearance {target, appearance: {heightType?, widthType?, tooltip?, background?, …}} (kurulu düğümün appearance'ına YALNIZ verilen alanları yazar, gerisini korur; null alanı temizler; bilinmeyen alan unknown-appearance-field ile reddedilir. GÖRÜNÜRLÜK appearance'ta DEĞİLDİR: gizlemek için replace_prop {target, prop: 'isHidden', value: true}, koşul için set_binding {prop: 'isHidden'} — add_component gövdesindeki appearance.isHidden'ı gateway props.isHidden'a taşır) | resize_component {target, size?: {w,h}, appearance?: {widthType,widthString,heightType,heightString}, states?: {isWrapper,isScrollable,isSticky,isDroppable,isFloating,isQuickStack}, breakpoint?: 1024} (gateway adı: platforma set_block {target, breakpoint, size} + set_appearance {target, appearance} + set_states {target, states} olarak gider — o üç op'u doğrudan da gönderebilirsiniz. move_component'e konan size reddedilir. Izgarada blocks[].size, flex'te appearance geçerlidir; ebeveynin düzeni yazılanı etkisiz kılıyorsa PAGE-019 uyarısı çıkar. states bir PROP DEĞİL düğüm alanıdır ve replace_prop onu yazamaz — düğümü silmeden isWrapper bayrağını değiştirmenin yolu budur). set_prop_type {target, prop, propType: 'static'|'code'|'translation', ref?, data?} (replace_prop sarmalıyla aynı sonucu verir ve mevcut data'yı tasarım yedeği olarak taşır. Bu op prop'un TİPİNİ yerinde değiştirir. Taşıyıcı alan tipe göre: static → data, code ve translation → ref (runtime data'yı her turda ezer, anahtar yalnız ref'te yaşar). Eksik taşıyıcı istek gönderilmeden reddedilir; page_schema_validate aynı kararı verir) | reorder {parent ('root' ya da elementId), order: [çocuk elementId'lerinin TAMAMI, yeni sırada]} (yalnız DİZİ sırasını değiştirir; çizim sırası blocks.position'dır — görünen sırayı değiştirmek için konumları set_block/move_component ile de yazın) | set_state_binding {target, binding: {name, expression, targets}} (ada göre ekler/değiştirir) | remove_state_binding {target, name} | set_connect_data {target, path, ref, data?} (prop'u veri kaynağına BAĞLAR: değer { type: 'connectData', ref: <tanım>, data: <son satırlar> } olur; path düz prop 'rows', nesne içi 'config.series' ya da dizi öğesi 'series.1.data'; data verilmezse bağlı prop'un satırları korunur, bağlı olmayan prop'un statik satırları (iç içe sarmallar açılarak) son satır olarak taşınır — D-11; tanım şekli ve alt küme kuralları istek gitmeden denetlenir — PAGE-035 · CD-004; platform_guide('page') §Kural 1c-3) | remove_connect_data {target, path} (bağlantıyı kaldırır, değer static olur; son satırlar static öğelere sarılarak korunur) — bağlı prop'a replace_prop ile connectData sarmalı, set_binding ya da set_prop_type yazılmaz; replace_prop { value: [ham satırlar] } yalnız örnek satırları değiştirir (CD-005) | batch {gestureId?, label?, ops: [...]} (iç içe batch yok). target = elementId. İstek atomiktir: tek op reddedilirse hiçbiri yazılmaz.");
27
+ export const opsParam = z.array(z.record(z.unknown())).describe("Micro-op listesi. Op sözlüğü: replace_prop {target, prop, value} (value ham değer, code sarmalı {type: 'code', ref: '{{ … }}'} ya da translation sarmalı {type: 'translation', ref: '<çeviri anahtarı>'} — prop tipini değiştirmek için komponenti silip yeniden EKLEMEYİN, ona yazan setter'ların hedefi silinir (FLOW-018); platform replace_prop'ta expression alanını OKUMAZ — gateway onu code sarmalına çevirir) | set_binding {target, prop, expression: '{{ Page.variables.x }}'} | add_component {parent (üst elementId ya da 'root'), index?, component} | remove_component {target} | move_component {target, parent, index?, position: {x, y}} (index ÇİZİM sırası değildir, runtime çocukları blocks.position'a göre (y, sonra x) çizer. page_schema_patch'te flex kapta gateway index'i görünen sıraya çevirir ve kardeş konumlarını yeniden yazar; ızgara kapta sırayı position.y verir, sona eklemeyen index PAGE-033 uyarısıdır. layout_schema_patch ve component_schema_patch bu çeviriyi YAPMAZ: orada sırayı position.y ile verin) | set_event {target, event, eventValue, replaceBoundFlows?} (olayın BÜTÜN bağlarını bu tek akışa indirir; olayda başka akış bağlıysa gateway PAGE-034 ile reddeder — bilerek değiştirmek için replaceBoundFlows: true) | add_event {target, event, eventValue} (olaya bir akış DAHA bağlar; bağlı akışların hepsi paralel ve birbirinden bağımsız koşar, sıra garantisi yoktur — sıra gerekiyorsa flow-trigger. Aynı akış iki kez 422 duplicate-event. Mobil runtime yalnız İLK akışı koşar) | remove_event {target, event, eventValue?} (olayı diziden çıkarır; target: 'page' sayfa düzeyi olaylar. Olaya birden fazla akış bağlıysa eventValue ile hangisi olduğunu verin, yoksa 422 ambiguous-event. set_event ile eventValue: null olay SİLMEZ, değersiz ölü kayıt bırakır. Olmayan olay 422 missing-event) | set_validations {target, validations: [{type, message, value, isActive}]} (komponentin validation listesini TAMAMEN değiştirir; boş dizi hepsini siler) | set_appearance {target, appearance: {heightType?, widthType?, tooltip?, background?, …}} (kurulu düğümün appearance'ına YALNIZ verilen alanları yazar, gerisini korur; null alanı temizler; bilinmeyen alan unknown-appearance-field ile reddedilir. GÖRÜNÜRLÜK appearance'ta DEĞİLDİR: gizlemek için replace_prop {target, prop: 'isHidden', value: true}, koşul için set_binding {prop: 'isHidden'} — add_component gövdesindeki appearance.isHidden'ı gateway props.isHidden'a taşır) | resize_component {target, size?: {w,h}, appearance?: {widthType,widthString,heightType,heightString}, states?: {isWrapper,isScrollable,isSticky,isDroppable,isFloating,isQuickStack}, breakpoint?: 1024} (gateway adı: platforma set_block {target, breakpoint, size} + set_appearance {target, appearance} + set_states {target, states} olarak gider — o üç op'u doğrudan da gönderebilirsiniz. move_component'e konan size reddedilir. Izgarada blocks[].size, flex'te appearance geçerlidir; ebeveynin düzeni yazılanı etkisiz kılıyorsa PAGE-019 uyarısı çıkar. states bir PROP DEĞİL düğüm alanıdır ve replace_prop onu yazamaz — düğümü silmeden isWrapper bayrağını değiştirmenin yolu budur). set_prop_type {target, prop, propType: 'static'|'code'|'translation', ref?, data?} (replace_prop sarmalıyla aynı sonucu verir ve mevcut data'yı tasarım yedeği olarak taşır. Bu op prop'un TİPİNİ yerinde değiştirir. Taşıyıcı alan tipe göre: static → data, code ve translation → ref (runtime data'yı her turda ezer, anahtar yalnız ref'te yaşar). Eksik taşıyıcı istek gönderilmeden reddedilir; page_schema_validate aynı kararı verir) | reorder {parent ('root' ya da elementId), order: [çocuk elementId'lerinin TAMAMI, yeni sırada]} (yalnız DİZİ sırasını değiştirir; çizim sırası blocks.position'dır — görünen sırayı değiştirmek için konumları set_block/move_component ile de yazın) | set_state_binding {target, binding: {name, expression, targets}} (ada göre ekler/değiştirir) | remove_state_binding {target, name} | batch {gestureId?, label?, ops: [...]} (iç içe batch yok). target = elementId. İstek atomiktir: tek op reddedilirse hiçbiri yazılmaz.");
30
28
  /**
31
29
  * PAGE-008 — `set_event` uydurma bir olay adıyla 200 döner ve bağ SESSİZCE ölü kalır: motor olayı
32
30
  * komponentin yaydığı adla eşleştirir, eşleşme yoksa akış hiç çalışmaz. Kontrol yalnız hedefin
@@ -69,29 +67,6 @@ export function collectTypes(nodes, into) {
69
67
  collectTypes(node.children, into);
70
68
  }
71
69
  }
72
- /**
73
- * TB-129 — `replace_prop` yalnız elementId taşır; stil anahtarı çevirisinin komponent TİPİNE
74
- * bakabilmesi için hedeflerin tipi okunan ağaçtan çıkarılır. Harita YALNIZ `style` prop'u yazan
75
- * bir op varsa kurulur: başka türlü fazladan bir okuma maliyeti doğardı.
76
- */
77
- export function styleTargetTypesFrom(ops, nodes) {
78
- if (!ops.some(entry => entry.op === 'replace_prop' && String(entry.prop ?? '') === 'style'))
79
- return undefined;
80
- const types = new Map();
81
- collectTypes(nodes, types);
82
- return types;
83
- }
84
- /** Aynı harita, ağacı tembel okuyan yazma yolu için; okuma düşerse çeviri eski davranışa döner. */
85
- export async function styleTargetTypes(ops, readHost) {
86
- if (!ops.some(entry => entry.op === 'replace_prop' && String(entry.prop ?? '') === 'style'))
87
- return undefined;
88
- try {
89
- return styleTargetTypesFrom(ops, (await readHost()).schema);
90
- }
91
- catch {
92
- return undefined;
93
- }
94
- }
95
70
  export function removeNodeFromTree(nodes, target, parent) {
96
71
  for (let index = 0; index < nodes.length; index++) {
97
72
  if (nodes[index].elementId === target)
@@ -297,44 +272,9 @@ export function assertOpBodies(opRecords, propTypeOps) {
297
272
  if (sizedMoves.length > 0) {
298
273
  throw new PlatformApiError(`move_component yalnız 'position' uygular; gövdeye konan 'size' platformda SESSİZCE düşer (ölçüldü 2026-09-04, appliedPatch'te yalnız /blocks/0/position görünür). Boyut için resize_component kullanın: { op: 'resize_component', target: '<elementId>', size: { w, h } } — flex kapsayıcı içindeyse 'appearance'. Etkilenen hedefler: ${sizedMoves.map(entry => entry.target).join(' · ')}`, 400);
299
274
  }
300
- const connectDataIssues = opRecords.flatMap(entry => connectDataOpIssues(entry).map(issue => `${issue.code} — ${issue.message}`));
301
- if (connectDataIssues.length > 0)
302
- throw new PlatformApiError(`Connect Data op gövdesi geçersiz (istek gönderilmedi): ${connectDataIssues.join(' · ')}`, 400);
303
275
  const validationIssues = schemaOpValidationErrors(opRecords);
304
276
  if (validationIssues.length > 0) {
305
277
  throw new PlatformApiError(`PAGE-016 — validation gövdesi eksik (istek gönderilmedi): ${validationIssues.join(' · ')}. `
306
278
  + "Çalışan biçim dört alanı da taşır: { type: 'required'|'email'|'regex'|'max-length'|'min-length'|'max-value'|'min-value'|'value-options', message: '<kullanıcıya görünen metin>', value: <eşik/desen/true>, isActive: true }.", 400);
307
279
  }
308
280
  }
309
- /** İskelet kurallarının okuduğu prop'lar (düzen grubu dışında). */
310
- const SKELETON_PROPS = new Set(['edges', 'horizontal', 'refreshable', 'stickyHeader', 'isHidden']);
311
- /** Yama iskeleti değiştirebilir mi: yalnız o zaman kabuk okunur ve fark hesaplanır. */
312
- export function reshapesSkeleton(ops) {
313
- return ops.some(entry => ['add_component', 'move_component', 'remove_component', 'set_appearance', 'set_states'].includes(String(entry.op))
314
- || ((entry.op === 'replace_prop' || entry.op === 'set_binding') && (FLEX_LAYOUT_PROP_NAMES.has(String(entry.prop ?? '')) || SKELETON_PROPS.has(String(entry.prop ?? '')))));
315
- }
316
- /**
317
- * `simulateLayoutOps`'un uygulamadığı ama iskeleti değiştiren op'lar: silme, görünüm, durum bayrakları ve
318
- * iskelet prop'ları. Uygulanmasa yama "sabit boy verilen scroll-view"ü gerekçesiz, silinen app-bar'ı ikinci
319
- * sayardı.
320
- */
321
- export function applySkeletonOps(tree, ops) {
322
- for (const entry of ops) {
323
- const target = String(entry.target ?? '');
324
- if (entry.op === 'remove_component')
325
- removeNodeFromTree(tree, target);
326
- if (entry.op === 'set_appearance' || entry.op === 'set_states') {
327
- const node = findNode(tree, target);
328
- const field = entry.op === 'set_appearance' ? 'appearance' : 'states';
329
- const patch = entry[field];
330
- if (node && patch && typeof patch === 'object')
331
- node[field] = { ...node[field], ...patch };
332
- }
333
- if ((entry.op === 'replace_prop' || entry.op === 'set_binding') && SKELETON_PROPS.has(String(entry.prop ?? ''))) {
334
- const node = findNode(tree, target);
335
- const value = entry.op === 'set_binding' ? { type: 'code', ref: entry.expression } : entry.value;
336
- if (node)
337
- node.props = { ...node.props, [String(entry.prop)]: value && typeof value === 'object' ? value : { type: 'static', data: value } };
338
- }
339
- }
340
- }