@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,27 +1,7 @@
1
- import { PlatformApiError } from '../errors.js';
2
- import { parseDomSummary } from '../domSummary.js';
3
- import { INTERNAL_ROLE_SEPARATOR, knownInternalRoles } from '../../knowledge/componentCatalog.js';
4
- import { logStep } from '../../observability/trace.js';
5
- import { actionScript, activeTabScript, rootElementId, runtimePropsScript } from './actions.js';
6
- import { domSummaryScript } from './summary.js';
7
- import { createPageConsoleLog, describePageFailure, isExpressionEcho, parseRenderErrors, renderErrorsScript, splitExpressionErrors } from './console-log.js';
8
- import { decideReadiness, describeUnready, parseReadiness, readinessScript, runtimePath } from './readiness.js';
9
- /** Runtime'ın Connect Data bağlantısı için bastığı sözde akış kimliği: hata bir akış adımından değil bağlı prop'un okumasından gelir. */
10
- const CONNECT_DATA_FLOW_ID = 'connect-data';
11
- /**
12
- * `runtimeErrors` yanında dönen okuma notu. Bağlantı hatasının (`flowId: 'connect-data'`) açılacak akışı yoktur:
13
- * not onu bağlantının araçlarına yönlendirir (39/M-18 — `flow_get` önerisi ajanı olmayan bir akışa götürüyordu).
14
- */
15
- export function runtimeErrorsNote(errors) {
16
- const connections = errors.filter(error => error.flowId === CONNECT_DATA_FLOW_ID);
17
- const flows = errors.length - connections.length;
18
- const head = `Sayfa ${errors.length} yapısal hata bıraktı; kare 'çalışıyor' görünse bile bu adımlar işini YAPMADI. element/property alanı hangi komponentin hangi prop'unun yazılamadığını söyler — o komponent eski/boş değerindedir.`;
19
- const flowHint = flows > 0 ? ` Akış hatasında (${flows}) adımı flow_get ile açıp ifadeyi düzeltin.` : '';
20
- const connectionHint = connections.length > 0
21
- ? ` Bağlantı hatasında (flowId 'connect-data': ${connections.map(error => `${error.element}.${error.property}`).join(', ')}) akış yoktur: code alanı reddin nedenidir — connect_data_get ile tanımı, connect_data_execute { op: 'list', asUserId } ile aynı isteği görün (kompleks projede 403 çoğunlukla kaynağın grant'i ya da ACC-005).`
22
- : '';
23
- return `${head}${flowHint}${connectionHint}`;
24
- }
1
+ import { PlatformApiError } from './errors.js';
2
+ import { parseDomSummary } from './domSummary.js';
3
+ import { INTERNAL_ROLE_SEPARATOR, knownInternalRoles } from '../knowledge/componentCatalog.js';
4
+ import { logStep } from '../observability/trace.js';
25
5
  /**
26
6
  * Ziyaretçinin tema tercihinin yaşadığı localStorage anahtarı — `theme-toggle` bunu yazar,
27
7
  * `Theme` okur ve projenin kendi modunun önüne geçirir
@@ -29,17 +9,328 @@ export function runtimeErrorsNote(errors) {
29
9
  * DEĞİL tarayıcı başına tektir.
30
10
  */
31
11
  const VIEWER_MODE_STORAGE_KEY = 'zeyrek-viewer-theme-mode';
12
+ /**
13
+ * G-24 — gateway sayfayı kurabiliyor, okuyabiliyor ve çizdirebiliyordu ama üzerinde OLAY
14
+ * TETİKLEYEMİYORDU: "filtre değişince veri yenileniyor" iddiası yalnız şema okumasıyla
15
+ * kanıtlanabiliyordu. 09-05 koşusunda üç olay bağının ikisi şemada "bağlı" görünüp gerçekte
16
+ * çalışmıyordu (form submit hiç doğmuyordu, `code` adımının gövdesi kaydedilmemişti) ve ikisi de
17
+ * yalnız gerçek tıklamayla görüldü.
18
+ *
19
+ * Değer yazarken NATIVE setter kullanılır ve `input`+`change` olayları bubbles ile atılır: DOM
20
+ * value'sunu düz atamak framework'ün duyduğu olayı üretmez, alan dolu görünür ama akış çalışmaz.
21
+ * `resource` sayacı "istek gerçekten gitti mi" sorusunu cevaplar — koşuda kusuru kesinleştiren
22
+ * gözlem tam olarak buydu (ağ kaydında hiç istek yoktu).
23
+ */
24
+ export function actionScript(action) {
25
+ const selector = JSON.stringify(`[data-element-id="${String(action.target ?? '')}"]`);
26
+ const value = JSON.stringify(action.value ?? '');
27
+ if (action.action === 'click') {
28
+ return `(function () {
29
+ var count = performance.getEntriesByType('resource').length;
30
+ var node = document.querySelector(${selector});
31
+ if (!node) return { found: false, applied: false, requests: count };
32
+ var target = node.matches('button, a, [role="button"]') ? node : (node.querySelector('button, a, [role="button"]') || node);
33
+ // S-2: sekme grubunun KÖKÜNE click, DOM sırasındaki ilk sekmeye gider ve "uygulandı" der —
34
+ // ajan ikinci sekmeyi hiç seçemiyordu (2026-09-16 raporu madde 6). Sekme seçmek 'tab'in işi.
35
+ if (target.closest && target.closest('[role="tablist"]') && !node.matches('[role="tab"]')) {
36
+ return { found: true, applied: false, note: "hedef bir sekme grubu: click İLK sekmeye giderdi — sekme seçmek için { action: 'tab', target: '" + ${JSON.stringify(String(action.target ?? ''))} + "', value: '<sekme anahtarı ya da metni>' } kullanın", requests: count };
37
+ }
38
+ target.click();
39
+ return { found: true, applied: true, requests: count };
40
+ })()`;
41
+ }
42
+ // S-2 — sekme SEÇİLEMİYORDU: kimlik `<elementId>__tab-<key>` (frontend f-02) önce denenir,
43
+ // runtime onu basmıyorsa görünen metinle eşlenir ve rapor hangisiyle eşlediğini söyler —
44
+ // metin çeviriye bağlıdır, dil değişince kırılır.
45
+ if (action.action === 'tab') {
46
+ const roleSelector = JSON.stringify(`[data-element-id="${String(action.target ?? '')}__tab-${String(action.value ?? '')}"]`);
47
+ return `(function () {
48
+ var count = performance.getEntriesByType('resource').length;
49
+ var root = document.querySelector(${selector});
50
+ if (!root) return { found: false, applied: false, requests: count };
51
+ var wanted = String(${value});
52
+ var byRole = document.querySelector(${roleSelector});
53
+ var buttons = Array.prototype.slice.call(root.querySelectorAll('[role="tab"]'));
54
+ var byText = buttons.filter(function (button) { return (button.textContent || '').trim() === wanted; })[0]
55
+ || buttons.filter(function (button) { return (button.textContent || '').trim().toLowerCase() === wanted.toLowerCase(); })[0];
56
+ var target = byRole || byText;
57
+
58
+ if (!target) {
59
+ var names = buttons.map(function (button) { return (button.textContent || '').trim(); }).join(' · ');
60
+ return { found: true, applied: false, optionsSeen: buttons.length, requests: count, note: buttons.length === 0
61
+ ? "bu düğümde sekme yok (role=tab) — hedef gerçekten bir sekme grubu mu? page_schema_get ile tipe bakın"
62
+ : "sekme bulunamadı: " + wanted + " — sekme ANAHTARINI ya da görünen metnini verin; görülenler: " + names };
63
+ }
64
+
65
+ if (target.disabled === true || target.getAttribute('aria-disabled') === 'true') {
66
+ return { found: true, applied: false, note: "sekme devre dışı (disabled): " + wanted, requests: count };
67
+ }
68
+
69
+ target.click();
70
+ return { found: true, applied: true, matchedBy: byRole ? 'role' : 'text', requests: count };
71
+ })()`;
72
+ }
73
+ // S-3 — açılır kutuda seçim. 'set' ile aynı gövde; buradaki fark yalnız prop izi tutulmamasıdır
74
+ // (seçim kutunun kendi değerini yazar, DOM'a elle yazılan bir değer yoktur).
75
+ if (action.action === 'select') {
76
+ return `(function () {
77
+ var count = performance.getEntriesByType('resource').length;
78
+ var node = document.querySelector(${selector});
79
+ if (!node) return { found: false, applied: false, requests: count };
80
+ var wanted = String(${value});
81
+ var finish = function (result) { return result; };
82
+
83
+ var combobox = node.querySelector('[role="combobox"], .ng-select__value-container, .ng-multiselect__value-container');
84
+ if (combobox) {
85
+ // Liste BU kutunun kabının içindedir. Sorgular belge kapsamlı kaldığında açık duran başka
86
+ // bir kutunun (multiselect seçimden sonra kapanmıyor) seçeneğine tıklanabiliyordu ve adım
87
+ // "uygulandı" diyordu; komşunun yükleme göstergesi de bu kutunun kararını blokluyordu (B-5).
88
+ var scope = combobox.closest('.ng-select, .ng-multiselect') || node;
89
+
90
+ combobox.click();
91
+
92
+ return new Promise(function (resolve) {
93
+ var waited = 0;
94
+ var step = 100;
95
+ var limit = 3000;
96
+
97
+ var attempt = function () {
98
+ var loading = scope.querySelector('.ng-select__loading, .ng-multiselect__loading');
99
+ var options = Array.prototype.slice.call(scope.querySelectorAll('[role="treeitem"][data-value]'));
100
+ var byValue = options.filter(function (option) { return option.getAttribute('data-value') === wanted; })[0];
101
+ var byText = options.filter(function (option) { return (option.textContent || '').trim() === wanted; })[0];
102
+ var match = byValue || byText;
103
+
104
+ if (match) {
105
+ var button = match.querySelector('button');
106
+ // Seçilemeyen seçeneğe tıklamak hiçbir şey yapmaz; "uygulandı" demek yanlış olurdu (B-6).
107
+ if (match.getAttribute('aria-disabled') === 'true' || (button && button.disabled)) {
108
+ resolve(finish({ found: true, applied: false, optionsSeen: options.length, waitedMs: waited, requests: count, note: "seçenek devre dışı (aria-disabled): " + wanted }));
109
+ return;
110
+ }
111
+
112
+ (button || match).click();
113
+ resolve(finish({ found: true, applied: true, matchedBy: byValue ? 'value' : 'text', optionsSeen: options.length, waitedMs: waited, requests: count }));
114
+ return;
115
+ }
116
+
117
+ var settled = !loading && (options.length > 0 || !!scope.querySelector('.ng-select__empty, .ng-multiselect__empty'));
118
+
119
+ if (settled || waited >= limit) {
120
+ var seen = options.slice(0, 10).map(function (option) { return (option.textContent || '').trim(); }).join(' · ');
121
+ // Liste HİÇ açılmadıysa kusur sorguda değil kutunun kendisinde olabilir:
122
+ // handleContainerClick kilitli/salt-okunur/yükleniyor kutuda erken döner.
123
+ var locked = !scope.querySelector('.ng-select__list, .ng-multiselect__list');
124
+ var note = options.length > 0
125
+ ? "açılır kutuda bu değere karşılık gelen seçenek yok: " + wanted + " — görülen " + options.length + " seçenek: " + seen
126
+ : locked
127
+ ? "açılır kutu AÇILMADI (" + waited + " ms): kutu devre dışı, salt okunur ya da hâlâ yükleniyor olabilir — page_runtime_get ile isDisabled/isReadonly/isLoading prop'larına bakın"
128
+ : "açılır kutu açıldı ama " + waited + " ms içinde hiç seçenek basılmadı — seçenekler bir sorgudan geliyorsa query_preview ile doğrulayın, yavaşsa önce bir wait adımı ekleyin";
129
+ resolve(finish({ found: true, applied: false, optionsSeen: options.length, waitedMs: waited, requests: count, note: note }));
130
+ return;
131
+ }
132
+
133
+ waited += step;
134
+ setTimeout(attempt, step);
135
+ };
136
+
137
+ attempt();
138
+ });
139
+ }
140
+ return { found: true, applied: false, note: "bu düğümde açılır kutu yok (role=combobox / .ng-select__value-container) — native alan için 'set', butona basmak için 'click' kullanın", requests: count };
141
+ })()`;
142
+ }
143
+ // S-4 — form gönderme. Gönder butonu olmayan ya da Enter ile gönderilen form başsız
144
+ // doğrulanamıyordu; `requestSubmit` formun kendi submit olayını doğduğu gibi doğurur.
145
+ if (action.action === 'submit') {
146
+ return `(function () {
147
+ var count = performance.getEntriesByType('resource').length;
148
+ var node = document.querySelector(${selector});
149
+ if (!node) return { found: false, applied: false, requests: count };
150
+ var form = node.matches('form') ? node : (node.querySelector('form') || node.closest('form'));
151
+
152
+ if (form) {
153
+ if (typeof form.requestSubmit === 'function') {
154
+ form.requestSubmit();
155
+ return { found: true, applied: true, submitVia: 'requestSubmit', requests: count };
156
+ }
157
+
158
+ form.dispatchEvent(new Event('submit', { bubbles: true, cancelable: true }));
159
+ return { found: true, applied: true, submitVia: 'dispatch', requests: count };
160
+ }
161
+
162
+ var button = node.querySelector('button[type="submit"]');
163
+
164
+ if (button) {
165
+ button.click();
166
+ return { found: true, applied: true, submitVia: 'button', requests: count };
167
+ }
168
+
169
+ return { found: true, applied: false, note: "bu düğüm bir form değil ve içinde submit butonu da yok — form komponentinin elementId'sini hedefleyin", requests: count };
170
+ })()`;
171
+ }
172
+ // G-09 — kanban kartı ve takvim etkinliği HTML5 DnD ile taşınır. Olaylar sentetik atılır ve
173
+ // TEK DataTransfer'i paylaşır; koordinat bırakma hedefinin merkezidir, çünkü takvimin saat
174
+ // kolonu bırakılan saati `clientY`'den hesaplar (slot hedefi o saatin ortasına düşer).
175
+ // Playwright `dragTo` gerçek fare sürer ama ScreenshotPage bu yeteneği taşımaz; betik yolu
176
+ // öteki eylemlerle aynı ölçüm döngüsünden (requests · flowRuns) geçer.
177
+ if (action.action === 'drag') {
178
+ const destination = JSON.stringify(`[data-element-id="${String(action.to ?? '')}"]`);
179
+ const destinationName = JSON.stringify(String(action.to ?? ''));
180
+ return `(function () {
181
+ var count = performance.getEntriesByType('resource').length;
182
+ var node = document.querySelector(${selector});
183
+ if (!node) return { found: false, applied: false, requests: count };
184
+ var drop = document.querySelector(${destination});
185
+ if (!drop) return { found: false, applied: false, note: "sürükleme kaynağı bulundu ama bırakma hedefi ('to') sayfada yok: " + ${destinationName} + " — kanban kolonu <elementId>__col-<value>, takvim günü <elementId>__day-<yyyy-MM-dd>, saat dilimi <elementId>__slot-<yyyy-MM-dd>T<HH>", requests: count };
186
+ if (node.getAttribute('draggable') !== 'true') return { found: true, applied: false, note: "kaynak sürüklenebilir değil (draggable != true) — kanban isDraggable ya da eventcalendar isEditable kapalı olabilir", requests: count };
187
+ var transfer = new DataTransfer();
188
+ var center = function (element) { var rect = element.getBoundingClientRect(); return { x: rect.left + rect.width / 2, y: rect.top + rect.height / 2 }; };
189
+ var fire = function (element, type, at) { element.dispatchEvent(new DragEvent(type, { bubbles: true, cancelable: true, composed: true, dataTransfer: transfer, clientX: at.x, clientY: at.y })); };
190
+ var from = center(node);
191
+ var to = center(drop);
192
+ fire(node, 'dragstart', from);
193
+ fire(drop, 'dragenter', to);
194
+ fire(drop, 'dragover', to);
195
+ fire(drop, 'drop', to);
196
+ fire(node, 'dragend', to);
197
+ return { found: true, applied: true, requests: count };
198
+ })()`;
199
+ }
200
+ const target = String(action.target ?? '');
201
+ const root = rootElementId(target);
202
+ // G-14 — `applied` yalnız "DOM'a yazıldı" demekti: tarih seçici olayı dinlemiyordu, kutu dolu
203
+ // görünüyor, prop boş kalıyordu. Kökün prop parmak izi eylemden ÖNCE alınır; runActions
204
+ // yerleşmeden sonra yeniden okuyup `propWritten`'ı çıkarır. Çok alanlı bileşenin kökünde tek
205
+ // değer yoktur — ilk kutuya yazmak yarım değeri "uygulandı" diye raporluyordu, kök reddedilir.
206
+ return `(function () {
207
+ var count = performance.getEntriesByType('resource').length;
208
+ var node = document.querySelector(${selector});
209
+ if (!node) return { found: false, applied: false, requests: count };
210
+ var wanted = String(${value});
211
+ var propsBefore = ${runtimePropsScript(root)};
212
+ var finish = function (result) { if (propsBefore !== undefined) result.propsBefore = propsBefore; return result; };
213
+
214
+ if (${JSON.stringify(root === target)}) {
215
+ var parts = Array.prototype.slice.call(node.querySelectorAll(${JSON.stringify(`[data-element-id^="${root}${INTERNAL_ROLE_SEPARATOR}"]`)}))
216
+ .filter(function (part) { return part.querySelector('input, select, textarea'); })
217
+ .map(function (part) { return part.getAttribute('data-element-id'); });
218
+ if (parts.length > 1) return finish({ found: true, applied: false, note: "çok alanlı bileşen: kökte tek değer yok, değeri kutulardan birine yazın — " + parts.join(' · '), requests: count });
219
+ }
220
+
221
+ // Açılır kutu (dropdown / multi-select) NATIVE alan değildir: değeri yazmak hiçbir şey
222
+ // yapmaz, seçeneğe TIKLANMASI gerekir. 'set' bu noktada 'select' fiiliyle AYNI yola girer —
223
+ // iki kopya seçim mantığı tutulmaz.
224
+
225
+ var combobox = node.querySelector('[role="combobox"], .ng-select__value-container, .ng-multiselect__value-container');
226
+ if (combobox) {
227
+ // Liste BU kutunun kabının içindedir. Sorgular belge kapsamlı kaldığında açık duran başka
228
+ // bir kutunun (multiselect seçimden sonra kapanmıyor) seçeneğine tıklanabiliyordu ve adım
229
+ // "uygulandı" diyordu; komşunun yükleme göstergesi de bu kutunun kararını blokluyordu (B-5).
230
+ var scope = combobox.closest('.ng-select, .ng-multiselect') || node;
231
+
232
+ combobox.click();
233
+
234
+ return new Promise(function (resolve) {
235
+ var waited = 0;
236
+ var step = 100;
237
+ var limit = 3000;
238
+
239
+ var attempt = function () {
240
+ var loading = scope.querySelector('.ng-select__loading, .ng-multiselect__loading');
241
+ var options = Array.prototype.slice.call(scope.querySelectorAll('[role="treeitem"][data-value]'));
242
+ var byValue = options.filter(function (option) { return option.getAttribute('data-value') === wanted; })[0];
243
+ var byText = options.filter(function (option) { return (option.textContent || '').trim() === wanted; })[0];
244
+ var match = byValue || byText;
245
+
246
+ if (match) {
247
+ var button = match.querySelector('button');
248
+ // Seçilemeyen seçeneğe tıklamak hiçbir şey yapmaz; "uygulandı" demek yanlış olurdu (B-6).
249
+ if (match.getAttribute('aria-disabled') === 'true' || (button && button.disabled)) {
250
+ resolve(finish({ found: true, applied: false, optionsSeen: options.length, waitedMs: waited, requests: count, note: "seçenek devre dışı (aria-disabled): " + wanted }));
251
+ return;
252
+ }
253
+
254
+ (button || match).click();
255
+ resolve(finish({ found: true, applied: true, matchedBy: byValue ? 'value' : 'text', optionsSeen: options.length, waitedMs: waited, requests: count }));
256
+ return;
257
+ }
258
+
259
+ var settled = !loading && (options.length > 0 || !!scope.querySelector('.ng-select__empty, .ng-multiselect__empty'));
260
+
261
+ if (settled || waited >= limit) {
262
+ var seen = options.slice(0, 10).map(function (option) { return (option.textContent || '').trim(); }).join(' · ');
263
+ // Liste HİÇ açılmadıysa kusur sorguda değil kutunun kendisinde olabilir:
264
+ // handleContainerClick kilitli/salt-okunur/yükleniyor kutuda erken döner.
265
+ var locked = !scope.querySelector('.ng-select__list, .ng-multiselect__list');
266
+ var note = options.length > 0
267
+ ? "açılır kutuda bu değere karşılık gelen seçenek yok: " + wanted + " — görülen " + options.length + " seçenek: " + seen
268
+ : locked
269
+ ? "açılır kutu AÇILMADI (" + waited + " ms): kutu devre dışı, salt okunur ya da hâlâ yükleniyor olabilir — page_runtime_get ile isDisabled/isReadonly/isLoading prop'larına bakın"
270
+ : "açılır kutu açıldı ama " + waited + " ms içinde hiç seçenek basılmadı — seçenekler bir sorgudan geliyorsa query_preview ile doğrulayın, yavaşsa önce bir wait adımı ekleyin";
271
+ resolve(finish({ found: true, applied: false, optionsSeen: options.length, waitedMs: waited, requests: count, note: note }));
272
+ return;
273
+ }
274
+
275
+ waited += step;
276
+ setTimeout(attempt, step);
277
+ };
278
+
279
+ attempt();
280
+ });
281
+ }
282
+
283
+ var field = node.matches('input, select, textarea') ? node : node.querySelector('input, select, textarea');
284
+ if (!field) return finish({ found: true, applied: false, note: 'düğümde input/select/textarea yok — bu komponent native alan kullanmıyor olabilir, click ile deneyin', requests: count });
285
+ var proto = field instanceof HTMLSelectElement ? HTMLSelectElement.prototype
286
+ : (field instanceof HTMLTextAreaElement ? HTMLTextAreaElement.prototype : HTMLInputElement.prototype);
287
+ if (field instanceof HTMLInputElement && (field.type === 'checkbox' || field.type === 'radio')) {
288
+ var shouldCheck = wanted === 'true' || wanted === '1' || wanted === 'on';
289
+ if (field.checked !== shouldCheck) field.click();
290
+ return finish({ found: true, applied: true, requests: count });
291
+ }
292
+ var setter = Object.getOwnPropertyDescriptor(proto, 'value').set;
293
+ setter.call(field, ${value});
294
+ field.dispatchEvent(new Event('input', { bubbles: true }));
295
+ field.dispatchEvent(new Event('change', { bubbles: true }));
296
+ return finish({ found: true, applied: true, requests: count });
297
+ })()`;
298
+ }
299
+ /** `rf_range__start` → `rf_range`: iç rol hedefinin prop'ları kök düğümde yaşar. */
300
+ export function rootElementId(target) {
301
+ const separator = target.indexOf(INTERNAL_ROLE_SEPARATOR);
302
+ return separator > 0 ? target.slice(0, separator) : target;
303
+ }
304
+ /**
305
+ * Düğümün runtime prop'larının parmak izi (`@ng-apps/flow` → flow-runtime-props.ts,
306
+ * `window.__ngAppsReadProps`). `undefined` okuyucu yayınlanmıyor (eski runtime) ya da prop'lar
307
+ * serileşmiyor; `null` hiçbir makine düğümü tanımıyor; dize karşılaştırılabilir iz.
308
+ */
309
+ export function runtimePropsScript(elementId) {
310
+ return `(function () { /* runtimePropsSnapshot */
311
+ var read = window.__ngAppsReadProps;
312
+ if (typeof read !== 'function') return undefined;
313
+ try { var props = read(${JSON.stringify(elementId)}); return props === null ? null : JSON.stringify(props); } catch (error) { return undefined; }
314
+ })()`;
315
+ }
316
+ /**
317
+ * Hangi sekme AÇIK? `tab` adımı tıklamanın işe yarayıp yaramadığını ancak bununla kanıtlar:
318
+ * tıklama "uygulandı" dese de bileşen seçimi kendi `selected` prop'una yazmamış olabilir.
319
+ * `undefined` düğüm yok, `null` sekme grubu var ama hiçbiri seçili değil.
320
+ */
321
+ export function activeTabScript(elementId) {
322
+ return `(function () { /* activeTabSnapshot */
323
+ var root = document.querySelector(${JSON.stringify(`[data-element-id="${elementId}"]`)});
324
+ if (!root) return undefined;
325
+ var active = root.querySelector('[role="tab"][aria-selected="true"]');
326
+ return active ? String(active.textContent || '').replace(/\\s+/g, ' ').trim() : null;
327
+ })()`;
328
+ }
32
329
  /**
33
330
  * fullPage karesi Chromium'un viewport ötesi yakalamasıyla değil, viewport'u içerik boyuna
34
331
  * büyüterek alınır: Studio runtime'ı kendi kaydırma kabını `100vh` üzerine kuruyor ve viewport
35
332
  * ötesi yakalama o kabı bir kez daha çiziyordu — aynı sayfa karede iki kez görünüyordu (09-04
36
333
  * Y-23). Tek karede çekilen görüntüde o birleştirme adımı hiç yok.
37
- *
38
- * İç kaydırıcı için gereken viewport `innerHeight + (scrollHeight − clientHeight)`'tir, kabın
39
- * `scrollHeight`'i DEĞİL: kap viewport'la birlikte büyür ama üstündeki/altındaki sabit parçalar
40
- * (mobil host'ta app bar + tab bar, ZM-23 f-06) büyümez. Yalnız `scrollHeight` itilince viewport
41
- * içerik boyuna çıkıyor, kap hâlâ o parçalar kadar kısa kalıyor ve uzun mobil sayfanın altı
42
- * karenin dışında kalıyordu. Kap viewport kadar yüksekse iki formül aynı sonucu verir.
43
334
  */
44
335
  export const contentHeightScript = [
45
336
  '(function () {',
@@ -50,7 +341,7 @@ export const contentHeightScript = [
50
341
  ' var nodes = document.querySelectorAll("*");',
51
342
  ' for (var index = 0; index < nodes.length && index < 4000; index += 1) {',
52
343
  ' var node = nodes[index];',
53
- ' if (node.clientHeight >= viewportHeight * 0.6 && node.scrollHeight > node.clientHeight + 1) heights.push(viewportHeight + node.scrollHeight - node.clientHeight);',
344
+ ' if (node.clientHeight >= viewportHeight * 0.6 && node.scrollHeight > node.clientHeight + 1) heights.push(node.scrollHeight);',
54
345
  ' }',
55
346
  ' return { contentHeight: Math.max.apply(null, heights), viewportHeight: viewportHeight };',
56
347
  '})()'
@@ -98,40 +389,96 @@ export function imageSize(buffer) {
98
389
  }
99
390
  return undefined;
100
391
  }
392
+ /**
393
+ * NE GÖRÜNÜYOR — resimsiz (S-5). Aynı sayfa yüklemesinden çıkan üçüncü çıktı: her KÖK runtime
394
+ * düğümünün görünürlüğü, metni, satır sayısı, açık sekmesi, değeri ve hata metinleri.
395
+ *
396
+ * Kök düğüm = kimliğinde `__` OLMAYAN düğüm; iç roller (`tbl__row-3`) satır sayısına dönüşür,
397
+ * listeye girmez. Tavan 300 düğüm, metin 80 karakter (K3) — özet bir kareyi değiştirmeli, onun
398
+ * kadar pahalı olmamalı. Tip `data-component-type`'tan gelir (frontend f-02); runtime basmıyorsa
399
+ * alan YAZILMAZ ve `typed: false` döner — `layoutReportUnavailable` ile aynı dürüstlük kuralı.
400
+ */
401
+ export function domSummaryScript(viewportHeight) {
402
+ return `(function () { /* domSummary */
403
+ var MAX = 300;
404
+ var CUT = 80;
405
+ // Görünür alanın GERÇEK boyu: fullPage ve clip yolları viewport'u içerik boyuna büyütür ve
406
+ // o anda window.innerHeight = içerik yüksekliği olur — her düğüm "ekran içinde" görünürdü ve
407
+ // "kart katlanmanın altında mı" sorusu HER ZAMAN 'visible' cevabı alırdı (B-10).
408
+ var VH = ${Math.max(Math.round(viewportHeight), 1)};
409
+ var clean = function (value) { return String(value || '').replace(/\\s+/g, ' ').trim(); };
410
+ var ROOT_SELECTOR = '[data-element-id]:not([data-element-id*="__"])';
411
+ var all = Array.prototype.slice.call(document.querySelectorAll('[data-element-id]'));
412
+ var roots = all.filter(function (node) { return String(node.getAttribute('data-element-id') || '').indexOf('__') < 0; });
413
+ var typed = false;
414
+ var nodes = roots.slice(0, MAX).map(function (node) {
415
+ var id = node.getAttribute('data-element-id');
416
+ var type = node.getAttribute('data-component-type');
417
+ if (type) typed = true;
418
+ // Bir olgu, ONU TAŞIYAN düğüme yazılır: en yakın kök ata bu düğüm değilse o olgu
419
+ // çocuğundur ve orada zaten listelenecektir (B-8). Aksi hâlde aynı değer üç satırda çıkar.
420
+ var owned = function (element) { return !!element && element.closest(ROOT_SELECTOR) === node; };
421
+ var rect = node.getBoundingClientRect();
422
+ var visible = typeof node.checkVisibility === 'function'
423
+ ? !!node.checkVisibility({ checkVisibilityCSS: true, checkOpacity: true })
424
+ : (rect.width > 0 && rect.height > 0);
425
+ var inViewport = rect.bottom > 0 && rect.right > 0 && rect.top < VH && rect.left < (window.innerWidth || 0);
426
+ // Kap düğümün innerText'i ÇOCUKLARININ metnidir; metin yalnız YAPRAK köklerde yazılır.
427
+ var nested = Array.prototype.slice.call(node.querySelectorAll('[data-element-id]'))
428
+ .some(function (child) { return String(child.getAttribute('data-element-id') || '').indexOf('__') < 0; });
429
+ var text = nested ? '' : clean(node.innerText);
430
+ if (text.length > CUT) text = text.slice(0, CUT) + '…';
431
+ var rows = document.querySelectorAll('[data-element-id^="' + id + '__row-"]').length;
432
+ var tab = node.querySelector('[role="tab"][aria-selected="true"]');
433
+ var field = node.querySelector('input, select, textarea');
434
+ var chosen = node.querySelector('.selected-item');
435
+ var holder = field || chosen;
436
+ var value = !owned(holder) ? '' : (field ? (field.type === 'password' ? '***' : clean(field.value)) : clean(chosen.textContent));
437
+ var media = node.querySelector('img, svg, canvas, video, iframe');
438
+ // İç içe <li> hem grubu hem alt mesajları sayıyordu: tek grup + 2 alt hata 3 görünüyordu (B-7).
439
+ var errorItems = Array.prototype.slice.call(node.querySelectorAll('.form-validation-errors li:not(:has(li))'))
440
+ .filter(owned).map(function (item) { return clean(item.textContent); });
441
+ // aria-invalid hem köke hem input'a yazılabiliyor (Password.svelte); yalnız ALANLAR sayılır.
442
+ var invalid = Array.prototype.slice.call(node.querySelectorAll('input[aria-invalid="true"], select[aria-invalid="true"], textarea[aria-invalid="true"]')).filter(owned).length;
443
+ var summary = { elementId: id, visible: !!visible, inViewport: !!inViewport };
444
+ if (type) summary.type = type;
445
+ if (text) summary.text = text;
446
+ if (rows > 0) summary.rows = rows;
447
+ if (owned(tab)) summary.activeTab = clean(tab.textContent);
448
+ if (value) summary.value = value;
449
+ if (visible && !text && !media && !nested) summary.empty = true;
450
+ if (errorItems.length > 0) summary.errors = errorItems;
451
+ else if (invalid > 0) summary.errors = ['aria-invalid: ' + invalid];
452
+ return summary;
453
+ });
454
+ var messages = [];
455
+ Array.prototype.slice.call(document.querySelectorAll('.ng-toast')).forEach(function (toast) {
456
+ var name = String(toast.className || '');
457
+ var kind = name.indexOf('ng-toast--error') >= 0 ? 'toast/error' : (name.indexOf('ng-toast--success') >= 0 ? 'toast/success' : 'toast');
458
+ messages.push({ kind: kind, text: clean(toast.innerText).slice(0, 160) });
459
+ });
460
+ Array.prototype.slice.call(document.querySelectorAll('.view-detail__error, .error-page')).forEach(function (node) {
461
+ messages.push({ kind: 'error', text: clean(node.innerText).slice(0, 160) });
462
+ });
463
+ return { nodes: nodes, messages: messages, truncated: roots.length > MAX, typed: typed };
464
+ })()`;
465
+ }
101
466
  /**
102
467
  * Kare taşmayı gösterir ama ölçmez: aynı sayfa yüklemesinden neredeyse bedavaya çıkan ikinci
103
468
  * çıktı her runtime düğümünün efektif piksel kutusudur. Düğümler `data-element-id` taşımıyorsa
104
469
  * (F-04 dev'e çıkana dek) liste boş döner — tool uydurmaz, layoutReportUnavailable der.
105
470
  */
106
471
  export const layoutReportScript = [
107
- '(function () {',
108
- 'var nodes = Array.from(document.querySelectorAll("[data-element-id]")).slice(0, 500);',
109
- 'return nodes.map(function (node) {',
472
+ 'Array.from(document.querySelectorAll("[data-element-id]")).slice(0, 500).map(function (node) {',
110
473
  ' var rect = node.getBoundingClientRect();',
111
- ' var parent = node.parentElement ? node.parentElement.closest("[data-element-id]") : null;',
112
- ' var parentIndex = parent ? nodes.indexOf(parent) : -1;',
113
- ' var style = window.getComputedStyle(node);',
114
- ' var painted = function (color) { return color !== "transparent" && !/rgba\\([^)]*,\\s*0\\)$/.test(color); };',
115
- ' var bordered = ["Top", "Right", "Bottom", "Left"].some(function (side) {',
116
- ' return parseFloat(style["border" + side + "Width"]) > 0 && style["border" + side + "Style"] !== "none" && painted(style["border" + side + "Color"]);',
117
- ' });',
118
474
  ' return {',
119
475
  ' elementId: node.getAttribute("data-element-id"),',
120
476
  ' x: Math.round(rect.x), y: Math.round(rect.y), width: Math.round(rect.width), height: Math.round(rect.height),',
121
477
  ' overflowX: node.scrollWidth > node.clientWidth + 1,',
122
- ' overflowY: node.scrollHeight > node.clientHeight + 1,',
123
- ' type: node.getAttribute("data-component-type"),',
124
- ' parentId: parent ? parent.getAttribute("data-element-id") : null,',
125
- ' parentIndex: parentIndex < 0 ? null : parentIndex,',
126
- ' surface: painted(style.backgroundColor) || bordered || style.boxShadow !== "none"',
478
+ ' overflowY: node.scrollHeight > node.clientHeight + 1',
127
479
  ' };',
128
- '});',
129
- '})()'
480
+ '})'
130
481
  ].join('\n');
131
- /** Mobil çizicinin hata sınırı işaretleri; betik düşerse boş (çekimi düşürmez). */
132
- async function readRenderErrors(page) {
133
- return page.evaluate(renderErrorsScript).then(parseRenderErrors).catch(() => []);
134
- }
135
482
  /**
136
483
  * F-09 — sayfa akışı yalnız tarayıcıda koşar, bu yüzden bir setter iteratee'sinin
137
484
  * patlamasının gateway'e ulaşan HİÇBİR yolu yoktu: akış 200 dönüyor, hedef prop
@@ -140,6 +487,67 @@ async function readRenderErrors(page) {
140
487
  * flow-runtime-errors.ts); kare çekilirken bedavaya okunur.
141
488
  */
142
489
  export const flowRuntimeErrorsScript = 'Array.isArray(window.__ngAppsFlowRuntimeErrors) ? window.__ngAppsFlowRuntimeErrors.slice(0, 50) : []';
490
+ /**
491
+ * Kare ne zaman hazır? (G-30 → B-6, 2026-09-07)
492
+ *
493
+ * `delayMs` TAHMİNDİ; 09-05'te runtime koşan akış sayacını yayınlamaya başladı (`@ng-apps/flow`
494
+ * → flow-runtime-errors.ts) ve bekleme "running sıfır, completed > 0" olunca bitiyordu. Bu şart
495
+ * mount akışı OLMAYAN sayfada (giriş ekranı) hiç sağlanmaz; akış tetiklemeyen `set` eylemi de
496
+ * `completed`'ı artırmaz. 09-06·2 koşusunda üç eylemli giriş denemesi bu yüzden 47 sn sürdü;
497
+ * sentetik ölçümde sayfa 0.3 sn'de hazırken aynı çağrı 48 sn'ydi — süre sayfanın değil, bu
498
+ * döngünün. Hazırlık artık TEK anlık görüntüden okunur: akış sayacı + Studio'nun yükleme
499
+ * iskeleti (`ViewDetail.svelte` → `.view-detail > .bx--skeleton__placeholder`) + runtime
500
+ * düğümleri (`[data-element-id]`) + hata/bulunamadı hâli + adres. Karar `decideReadiness`'ta.
501
+ *
502
+ * Yüklenmemiş kare ASLA dönmez: bütçe dolduğunda sayfa hâlâ hazır değilse çağrı hatayla düşer
503
+ * (`captureUnready: true` teşhis kipi dışında). Bozuk sayfayı görmek istiyorsan hata mesajı
504
+ * neyin görüldüğünü zaten söyler.
505
+ */
506
+ export const readinessScript = `(function () { /* readinessSnapshot */
507
+ var activity = window.__ngAppsFlowActivity;
508
+ return {
509
+ activity: activity && typeof activity.running === 'number' && typeof activity.completed === 'number' ? { running: activity.running, completed: activity.completed } : null,
510
+ runtimeNodes: document.querySelectorAll('[data-element-id]').length,
511
+ skeleton: !!document.querySelector('.view-detail > .bx--skeleton__placeholder'),
512
+ viewDetail: !!document.querySelector('.view-detail'),
513
+ errorState: !!document.querySelector('.view-detail__error, .view-detail .error-page'),
514
+ readyState: String(document.readyState),
515
+ href: String(location.href)
516
+ };
517
+ })()`;
518
+ /** Okunan değer gerçekten sayaç mı; değilse sayfa sayaç yayınlamıyordur (eski runtime). */
519
+ export function isFlowActivity(activity) {
520
+ if (!activity || typeof activity !== 'object')
521
+ return false;
522
+ const { running, completed } = activity;
523
+ return typeof running === 'number' && typeof completed === 'number';
524
+ }
525
+ /** Tarayıcıdan dönen değer anlık görüntü mü; yönlendirme sırasında evaluate düşer ve undefined kalır. */
526
+ export function parseReadiness(raw) {
527
+ if (!raw || typeof raw !== 'object')
528
+ return undefined;
529
+ const value = raw;
530
+ if (typeof value.runtimeNodes !== 'number' || typeof value.href !== 'string')
531
+ return undefined;
532
+ return {
533
+ activity: isFlowActivity(value.activity) ? { running: value.activity.running, completed: value.activity.completed } : null,
534
+ runtimeNodes: value.runtimeNodes,
535
+ skeleton: value.skeleton === true,
536
+ viewDetail: value.viewDetail === true,
537
+ errorState: value.errorState === true,
538
+ readyState: String(value.readyState ?? ''),
539
+ href: value.href
540
+ };
541
+ }
542
+ /** `/dev` ve `/view` aynı sayfadır (Studio `hooks.ts` reroute); yönlendirme kararı yalnız yoldan verilir. */
543
+ export function runtimePath(url) {
544
+ try {
545
+ return new URL(url).pathname.replace(/^\/dev\//, '/view/').replace(/\/+$/, '');
546
+ }
547
+ catch {
548
+ return url;
549
+ }
550
+ }
143
551
  const maxCaptureHeight = 16384;
144
552
  const navigationTimeoutMs = 20000;
145
553
  const apiResponseTimeoutMs = 15000;
@@ -147,6 +555,12 @@ const settleMs = 600;
147
555
  /** Akış bittikten sonra Svelte'in son render'ına bırakılan pay. */
148
556
  const flowSettleGraceMs = 250;
149
557
  const flowPollMs = 120;
558
+ /** "Hiç akış koşmadı" kararı için DOM'un kaç yoklama değişmemesi gerekir (~600 ms = eski settleMs). */
559
+ const quietPolls = 5;
560
+ /** Sayaç yayınlanmayan runtime'da tek kanıt DOM durulmasıdır; daha uzun beklenir (~1 sn). */
561
+ const legacyQuietPolls = 8;
562
+ /** Runtime dışına yönlendirme: hedef sayfanın kendi JS'i çizmeyi bitirsin (~1.5 sn). */
563
+ const redirectQuietPolls = 12;
150
564
  const idleCloseMs = 60000;
151
565
  /** Etkileşim adımındaki `wait` üst sınırı; delayMs tavanıyla aynı çizgide. */
152
566
  const maxActionWaitMs = 15000;
@@ -166,6 +580,42 @@ const navigationAttempts = 2;
166
580
  function isNavigationTimeout(message) {
167
581
  return /timeout/i.test(message);
168
582
  }
583
+ /**
584
+ * Tek anlık görüntüden "kare alınabilir" kararı; iki uçta da yanlış beklememek için tek yerde.
585
+ * `quiet` görüntünün kaç yoklamadır değişmediğidir, `completedBefore` bu beklemenin başındaki
586
+ * bitmiş akış sayısı (sayfa değiştiyse çağıran sıfırlar). İskelet görünüyorsa hiçbir karar yok.
587
+ */
588
+ export function decideReadiness(snapshot, quiet, completedBefore, baselineUrl) {
589
+ if (snapshot.skeleton)
590
+ return undefined;
591
+ const { activity } = snapshot;
592
+ const idle = activity !== null && activity.running === 0;
593
+ if (idle && activity.completed > completedBefore && snapshot.runtimeNodes > 0)
594
+ return 'ready';
595
+ if (idle && snapshot.runtimeNodes > 0 && quiet >= quietPolls)
596
+ return 'ready-no-flows';
597
+ if (activity === null && snapshot.runtimeNodes > 0 && quiet >= legacyQuietPolls)
598
+ return 'ready-no-counter';
599
+ if (idle && snapshot.errorState && quiet >= quietPolls)
600
+ return 'error';
601
+ if (!snapshot.viewDetail && snapshot.readyState === 'complete' && baselineUrl !== undefined && runtimePath(snapshot.href) !== runtimePath(baselineUrl) && quiet >= redirectQuietPolls)
602
+ return 'redirected';
603
+ return undefined;
604
+ }
605
+ /** Hata mesajı için: bütçe dolduğunda sayfada NE görüldü. */
606
+ export function describeUnready(readiness, apiNote) {
607
+ const snapshot = readiness.snapshot;
608
+ const suffix = apiNote === undefined ? '' : ` · ${apiNote}`;
609
+ if (!snapshot)
610
+ return `tarayıcı sayfayı hiç değerlendiremedi (sürekli yönlendirme ya da bağlam düştü)${suffix}`;
611
+ if (snapshot.skeleton)
612
+ return `sayfa hâlâ yükleme iskeletinde${suffix}`;
613
+ if (snapshot.activity && snapshot.activity.running > 0)
614
+ return `${snapshot.activity.running} akış hâlâ koşuyor (${snapshot.activity.completed} bitti)${suffix}`;
615
+ if (snapshot.runtimeNodes === 0)
616
+ return `runtime hiç render olmadı — data-element-id taşıyan düğüm yok${snapshot.viewDetail ? '' : ', .view-detail kabı da yok'}${suffix}`;
617
+ return `sayfa durulmadı (${snapshot.runtimeNodes} düğüm, sayaç ${snapshot.activity ? `${snapshot.activity.running} koşan / ${snapshot.activity.completed} biten` : 'yok'})${suffix}`;
618
+ }
169
619
  async function measureContentHeight(page) {
170
620
  const measured = await page.evaluate(contentHeightScript).catch(() => undefined);
171
621
  const height = measured?.contentHeight;
@@ -305,7 +755,7 @@ export class StudioScreenshotter {
305
755
  * işaretlenir ve kare yine çekilir, çünkü bozuk/boş sayfayı görmek de bir doğrulamadır. Veri
306
756
  * yanıtından sonra fontlar + kısa yerleşme beklemesi Svelte hydrate'ine alan bırakır.
307
757
  */
308
- async runActions(page, request, consoleLog) {
758
+ async runActions(page, request) {
309
759
  if (!request.actions || request.actions.length === 0)
310
760
  return undefined;
311
761
  const outcomes = [];
@@ -317,7 +767,7 @@ export class StudioScreenshotter {
317
767
  }
318
768
  const snapshotBefore = parseReadiness(await page.evaluate(readinessScript).catch(() => undefined));
319
769
  const activityBefore = snapshotBefore?.activity ?? null;
320
- const raw = await page.evaluate(actionScript(action, request.target)).catch(error => ({
770
+ const raw = await page.evaluate(actionScript(action)).catch(error => ({
321
771
  found: false,
322
772
  applied: false,
323
773
  note: error instanceof Error ? error.message : String(error),
@@ -328,11 +778,9 @@ export class StudioScreenshotter {
328
778
  // iç rol denemesidir: bilinen roller sayılır. Kök hedefi bulunamadıysa sorun elementId'nin
329
779
  // kendisindedir, şemayı okumaya yönlendirilir (P1-3 Step 3).
330
780
  if (before.found === false && !before.note && typeof action.target === 'string') {
331
- before.note = action.target === '__tabs' || action.target.startsWith(`__tabs${INTERNAL_ROLE_SEPARATOR}`)
332
- ? `'${action.target}' sayfada yok: bu sayfa sekme çubuğunda değil ya da uygulamada sekmeler kapalı (application_get → properties.mobile.tabs). Sekme öğesinin kimliği __tabs__item-<sayfa slug'ı>.`
333
- : action.target.includes(INTERNAL_ROLE_SEPARATOR)
334
- ? `'${action.target}' sayfada yok. Bu bir İÇ ROL hedefi: biçim <elementId>__<rol> ve bilinen roller — ${knownInternalRoles().join(' · ')}. Kolon rolleri kolonun SLUG'ından türer (başlığından değil); satır sırası 0 tabanlıdır ve sayfa değişince yeniden numaralanır.`
335
- : `'${action.target}' elementId'si sayfada yok. page_schema_get ile elementId'leri listeleyin; bir komponentin İÇ kontrolünü hedefliyorsanız <elementId>__<rol> biçimini kullanın (component_get çıktısındaki internalRoles).`;
781
+ before.note = action.target.includes(INTERNAL_ROLE_SEPARATOR)
782
+ ? `'${action.target}' sayfada yok. Bu bir İÇ ROL hedefi: biçim <elementId>__<rol> ve bilinen roller — ${knownInternalRoles().join(' · ')}. Kolon rolleri kolonun SLUG'ından türer (başlığından değil); satır sırası 0 tabanlıdır ve sayfa değişince yeniden numaralanır.`
783
+ : `'${action.target}' elementId'si sayfada yok. page_schema_get ile elementId'leri listeleyin; bir komponentin İÇ kontrolünü hedefliyorsanız <elementId>__<rol> biçimini kullanın (component_get çıktısındaki internalRoles).`;
336
784
  }
337
785
  // Olay akışı uç çağırıyor: yerleşme beklemeden sonraki istek sayısını ölçmek "istek gitti mi"
338
786
  // sorusunu cevaplar — koşuda kusuru kesinleştiren gözlem buydu.
@@ -347,8 +795,7 @@ export class StudioScreenshotter {
347
795
  completedBefore: activityBefore?.completed ?? Number.POSITIVE_INFINITY
348
796
  });
349
797
  if (readiness.state === 'not-ready' && request.captureUnready !== true) {
350
- const failure = consoleLog ? describePageFailure(consoleLog, await readRenderErrors(page)) : '';
351
- throw new PlatformApiError(`Etkileşim adımı ${index} (${action.action}${action.target === undefined ? '' : ` '${action.target}'`}) sonrasında sayfa ${readiness.waitedMs} ms içinde durulmadı, kare ALINMADI — ${describeUnready(readiness)}.${failure} Bekleme bütçesi ${apiResponseTimeoutMs + request.delayMs} ms; akış gerçekten uzun sürüyorsa delayMs ile büyütün. Yüklenmemiş hâli yine de görmek için captureUnready: true verin (kare pageState: 'not-ready' ile döner).`, 504);
798
+ throw new PlatformApiError(`Etkileşim adımı ${index} (${action.action}${action.target === undefined ? '' : ` '${action.target}'`}) sonrasında sayfa ${readiness.waitedMs} ms içinde durulmadı, kare ALINMADI — ${describeUnready(readiness)}. Bekleme bütçesi ${apiResponseTimeoutMs + request.delayMs} ms; akış gerçekten uzun sürüyorsa delayMs ile büyütün. Yüklenmemiş hâli yine de görmek için captureUnready: true verin (kare pageState: 'not-ready' ile döner).`, 504);
352
799
  }
353
800
  const after = await page.evaluate("performance.getEntriesByType('resource').length").catch(() => undefined);
354
801
  const requests = typeof after === 'number' && typeof before.requests === 'number' ? Math.max(after - before.requests, 0) : 0;
@@ -469,16 +916,8 @@ export class StudioScreenshotter {
469
916
  throw error;
470
917
  });
471
918
  let context;
472
- // Donuk kare (plans/mobil-animasyon-sistemi A6): sonsuz döngülü sayfanın iki karesi hiç aynı
473
- // çıkmıyordu. Runtime "hareketi azalt"ı dondurma sebebi sayar ve onu matchMedia'dan canlı okur
474
- // (A10); tercih bağlamda verilir ki ilk kareden itibaren geçerli olsun.
475
- const contextOptions = {
476
- viewport: request.viewport,
477
- deviceScaleFactor: (request.dpr ?? 1) * (request.scale ?? 1),
478
- ...(request.motion === 'play' ? {} : { reducedMotion: 'reduce' })
479
- };
480
919
  try {
481
- context = await browser.newContext(contextOptions);
920
+ context = await browser.newContext({ viewport: request.viewport, deviceScaleFactor: request.scale ?? 1 });
482
921
  }
483
922
  catch {
484
923
  this.browserPromise = this.launch();
@@ -486,9 +925,9 @@ export class StudioScreenshotter {
486
925
  this.browserPromise = undefined;
487
926
  throw error;
488
927
  });
489
- context = await browser.newContext(contextOptions);
928
+ context = await browser.newContext({ viewport: request.viewport, deviceScaleFactor: request.scale ?? 1 });
490
929
  }
491
- logStep('tarayıcı →', `GET ${request.url} · viewport ${request.viewport.width}x${request.viewport.height}${request.fullPage ? ' · fullPage' : ''}${request.token ? '' : ' · gateway jetonu YOK'}${(request.cookies ?? []).length > 0 ? ` · caller çerezleri: ${(request.cookies ?? []).map(cookie => cookie.name).join(', ')}` : ''}${request.token || (request.cookies ?? []).length > 0 ? '' : ' · ANONİM (çerez YOK)'}${request.colorScheme ? ` · tema ${request.colorScheme}` : ''}${request.motion === 'play' ? ' · hareket oynar' : ''}${request.headers ? ` · başlık: ${Object.keys(request.headers).join(', ')}` : ''}`);
930
+ logStep('tarayıcı →', `GET ${request.url} · viewport ${request.viewport.width}x${request.viewport.height}${request.fullPage ? ' · fullPage' : ''}${request.token ? '' : ' · gateway jetonu YOK'}${(request.cookies ?? []).length > 0 ? ` · caller çerezleri: ${(request.cookies ?? []).map(cookie => cookie.name).join(', ')}` : ''}${request.token || (request.cookies ?? []).length > 0 ? '' : ' · ANONİM (çerez YOK)'}${request.colorScheme ? ` · tema ${request.colorScheme}` : ''}`);
492
931
  try {
493
932
  const cookies = new Map();
494
933
  if (request.token)
@@ -498,14 +937,6 @@ export class StudioScreenshotter {
498
937
  if (cookies.size > 0) {
499
938
  await context.addCookies([...cookies].map(([name, value]) => ({ name, value, url: request.cookieUrl, httpOnly: false })));
500
939
  }
501
- // Kullanıcı gözü başlığı sayfa açılmadan kurulur: ilk veri isteği de o kullanıcının gözüyle gitmeli. Yeteneği
502
- // olmayan bağlamda kare SESSİZCE gateway kimliğiyle çekilmez, hata verir.
503
- if (request.headers && Object.keys(request.headers).length > 0) {
504
- if (!context.setExtraHTTPHeaders) {
505
- throw new PlatformApiError(`Başlıklar (${Object.keys(request.headers).join(', ')}) uygulanamadı: tarayıcı bağlamı setExtraHTTPHeaders desteklemiyor. Kare gateway kimliğiyle çekilir ve bu sessizce yanlış olurdu.`);
506
- }
507
- await context.setExtraHTTPHeaders(request.headers);
508
- }
509
940
  // Tema tercihi çerezde değil localStorage'da yaşar ve tema store'u değerini modül İLK
510
941
  // değerlendirildiğinde okur: sayfa açıldıktan sonra yazmak bir sonraki yüklemeye kadar
511
942
  // etkisizdir. Bu yüzden tercih, sayfanın script'lerinden önce koşan bağlam script'iyle
@@ -517,11 +948,6 @@ export class StudioScreenshotter {
517
948
  await context.addInitScript(`try { localStorage.setItem(${JSON.stringify(VIEWER_MODE_STORAGE_KEY)}, ${JSON.stringify(request.colorScheme)}) } catch (error) { /* localStorage kapalıysa proje varsayılanı çizilir */ }`);
518
949
  }
519
950
  const page = await context.newPage();
520
- // Konsol ve istisna dinleyicileri navigasyondan ÖNCE takılır: modül yüklenirken patlayan
521
- // bileşenin hatası ilk karede düşer ve sonradan takılan dinleyici onu kaçırırdı (g-06).
522
- const consoleLog = createPageConsoleLog();
523
- page.on?.('console', message => consoleLog.onConsole(message));
524
- page.on?.('pageerror', error => consoleLog.onPageError(error));
525
951
  // Soğuk Vite derlemesinde sayfanın kendi veri isteği 15 sn'yi aşabiliyor; delayMs bu
526
952
  // bütçeyi de uzatır (09-02 E-2). Yanıt hazırlık döngüsünü BLOKLAMAZ: runtime dışına
527
953
  // yönlendirilen sayfa bu isteği hiç atmaz ve eskiden 15 sn boşa bekleniyordu.
@@ -562,8 +988,7 @@ export class StudioScreenshotter {
562
988
  const apiStatus = await Promise.race([observedApiStatus, Promise.resolve(undefined)]);
563
989
  const apiNote = apiStatus === undefined ? 'sayfanın veri isteği gözlenmedi' : `veri isteği HTTP ${apiStatus}`;
564
990
  if (readiness.state === 'not-ready' && request.captureUnready !== true) {
565
- const failure = describePageFailure(consoleLog, await readRenderErrors(page));
566
- throw new PlatformApiError(`Sayfa ${readiness.waitedMs} ms içinde yüklenmedi, kare ALINMADI — ${describeUnready(readiness, apiNote)}.${failure} Bekleme bütçesi ${readyBudgetMs} ms; sayfa yalnız yavaşsa delayMs ile büyütün. Sayfa gerçekten bozuksa page_runtime_get ile şemayı, STUDIO_BASE_URL ve slug'ları doğrulayın. Yüklenmemiş hâli yine de görmek için captureUnready: true verin (kare pageState: 'not-ready' ile döner).`, 504);
991
+ throw new PlatformApiError(`Sayfa ${readiness.waitedMs} ms içinde yüklenmedi, kare ALINMADI — ${describeUnready(readiness, apiNote)}. Bekleme bütçesi ${readyBudgetMs} ms; sayfa yalnız yavaşsa delayMs ile büyütün. Sayfa gerçekten bozuksa page_runtime_get ile şemayı, STUDIO_BASE_URL ve slug'ları doğrulayın. Yüklenmemiş hâli yine de görmek için captureUnready: true verin (kare pageState: 'not-ready' ile döner).`, 504);
567
992
  }
568
993
  await page.evaluate('document.fonts.ready.then(() => true)').catch(() => undefined);
569
994
  const flowsSettled = readiness.state === 'ready' || readiness.state === 'ready-no-flows'
@@ -571,17 +996,14 @@ export class StudioScreenshotter {
571
996
  : readiness.state === 'not-ready' ? false : undefined;
572
997
  const finalHref = readiness.snapshot?.href;
573
998
  const finalUrl = finalHref !== undefined && runtimePath(finalHref) !== runtimePath(request.url) ? finalHref : undefined;
574
- const actions = await this.runActions(page, request, consoleLog);
999
+ const actions = await this.runActions(page, request);
575
1000
  // Ölçüm EYLEMLERDEN SONRA: eylem sekme değiştirir, filtre uygular, panel açar — içerik
576
1001
  // boyu o zaman kesinleşir. Eskiden ölçüm eylemlerden önceydi ve ikinci sekmede 40 satırlık
577
1002
  // tablo açan bir çekim viewport boyunda kalıyordu; `contentHeight` ilk sekmenin boyuydu
578
1003
  // (S-1, 2026-09-16 raporu madde 6). Yalnız YÜKSEKLİK büyür, genişlik sabittir: `hideOn`
579
1004
  // kırılım noktaları ve duyarlı yerleşim kareyi çekerken kaymaz (K4).
580
- // Doku sınırı RASTER pikseldir: mobil kare @2-3 çekilir, CSS tavanı ölçekle bölünür (web'de
581
- // ölçek ≤ 1, tavan 16384 kalır).
582
- const heightLimit = Math.floor(maxCaptureHeight / Math.max(1, (request.dpr ?? 1) * (request.scale ?? 1)));
583
1005
  const measured = request.fullPage ? await measureContentHeight(page) : undefined;
584
- const captureHeight = measured === undefined ? undefined : Math.min(measured, heightLimit);
1006
+ const captureHeight = measured === undefined ? undefined : Math.min(measured, maxCaptureHeight);
585
1007
  let viewportHeight = request.viewport.height;
586
1008
  if (captureHeight !== undefined && captureHeight > viewportHeight) {
587
1009
  await page.setViewportSize({ width: request.viewport.width, height: captureHeight });
@@ -596,10 +1018,10 @@ export class StudioScreenshotter {
596
1018
  clip = await resolveClip(page, request.clip);
597
1019
  if (clip.y + clip.height > viewportHeight) {
598
1020
  const wanted = Math.ceil(clip.y + clip.height) + 8;
599
- if (wanted > heightLimit) {
600
- throw new PlatformApiError(`page_screenshot: kırpma kutusu ${Math.round(clip.y)}+${Math.round(clip.height)} px'te bitiyor ve Chromium'un ${maxCaptureHeight} px'lik doku sınırının${heightLimit === maxCaptureHeight ? '' : ` (bu ölçekte ${heightLimit} CSS px)`} altına sığmıyor — viewport o kadar büyütülemez ve kare kutunun tamamını gösteremezdi. Kutuyu elle daraltın ({ x, y, width, height }) ya da sayfayı bölerek doğrulayın.`);
1021
+ if (wanted > maxCaptureHeight) {
1022
+ throw new PlatformApiError(`page_screenshot: kırpma kutusu ${Math.round(clip.y)}+${Math.round(clip.height)} px'te bitiyor ve Chromium'un ${maxCaptureHeight} px'lik doku sınırının altına sığmıyor — viewport o kadar büyütülemez ve kare kutunun tamamını gösteremezdi. Kutuyu elle daraltın ({ x, y, width, height }) ya da sayfayı bölerek doğrulayın.`);
601
1023
  }
602
- const grown = Math.min(wanted, heightLimit);
1024
+ const grown = Math.min(wanted, maxCaptureHeight);
603
1025
  await page.setViewportSize({ width: request.viewport.width, height: grown });
604
1026
  viewportHeight = grown;
605
1027
  await this.settleAfterResize(page, readiness.snapshot?.href ?? request.url);
@@ -609,13 +1031,9 @@ export class StudioScreenshotter {
609
1031
  const layout = request.layoutReport === true
610
1032
  ? await page.evaluate(layoutReportScript).then(value => Array.isArray(value) ? value : []).catch(() => [])
611
1033
  : undefined;
612
- const ledger = await page.evaluate(flowRuntimeErrorsScript)
1034
+ const runtimeErrors = await page.evaluate(flowRuntimeErrorsScript)
613
1035
  .then(value => Array.isArray(value) ? value : [])
614
1036
  .catch(() => []);
615
- const { flowErrors: runtimeErrors, expressionErrors } = splitExpressionErrors(ledger);
616
- const renderErrors = await readRenderErrors(page);
617
- const consoleErrors = consoleLog.consoleErrors(record => isExpressionEcho(expressionErrors, record.text));
618
- const pageErrors = consoleLog.pageErrors();
619
1037
  // Özet kareden ÖNCE ve büyütmeden SONRA: ikisi de aynı yerleşmiş sayfayı anlatsın.
620
1038
  const summaryRaw = request.summary === false
621
1039
  ? undefined
@@ -654,14 +1072,10 @@ export class StudioScreenshotter {
654
1072
  readyAfterMs: readiness.waitedMs,
655
1073
  ...(finalUrl === undefined ? {} : { finalUrl }),
656
1074
  ...(measured === undefined ? {} : { contentHeight: measured }),
657
- ...(measured !== undefined && measured > heightLimit ? { fullPageTruncated: true } : {}),
1075
+ ...(measured !== undefined && measured > maxCaptureHeight ? { fullPageTruncated: true } : {}),
658
1076
  ...(layout === undefined ? {} : { layout }),
659
1077
  ...(actions === undefined ? {} : { actions }),
660
1078
  ...(runtimeErrors.length > 0 ? { runtimeErrors } : {}),
661
- ...(expressionErrors.length > 0 ? { expressionErrors } : {}),
662
- ...(consoleErrors.length > 0 ? { consoleErrors } : {}),
663
- ...(pageErrors.length > 0 ? { pageErrors } : {}),
664
- ...(renderErrors.length > 0 ? { renderErrors } : {}),
665
1079
  ...(flowsSettled === undefined ? {} : { flowsSettled }),
666
1080
  ...(navigationRetried ? { navigationRetried: true } : {})
667
1081
  };