@appaflytech/zeyrek-ai-mcp 0.0.20 → 0.0.21

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 (65) hide show
  1. package/README.md +14 -13
  2. package/dist/config.js +0 -2
  3. package/dist/knowledge/componentCatalog.js +940 -215
  4. package/dist/knowledge/componentEvents.js +368 -75
  5. package/dist/knowledge/normalize.js +66 -63
  6. package/dist/knowledge/rules.js +29 -6
  7. package/dist/knowledge/styleResolve.js +1 -1
  8. package/dist/knowledge/themeResolve.js +3 -3
  9. package/dist/platform/diagnostics.js +85 -0
  10. package/dist/platform/domSummary.js +1 -1
  11. package/dist/platform/ifExists.js +129 -0
  12. package/dist/platform/responses.js +2 -1
  13. package/dist/platform/{capture/capture.js → screenshot.js} +509 -23
  14. package/dist/platform/strictArguments.js +22 -2
  15. package/dist/platform/writeDiff.js +58 -31
  16. package/dist/platform/writeView.js +17 -0
  17. package/dist/resources/guides/component.md +9 -6
  18. package/dist/resources/guides/functions.md +32 -3
  19. package/dist/resources/guides/getting-started.md +35 -0
  20. package/dist/resources/guides/layout.md +7 -11
  21. package/dist/resources/guides/page.md +49 -71
  22. package/dist/resources/guides/translation.md +5 -6
  23. package/dist/resources/schema-fingerprint.json +2 -2
  24. package/dist/resources/schemas/GlobalFunctionUpdateResultDto.json +18 -0
  25. package/dist/server.js +0 -2
  26. package/dist/tools/applications.js +3 -60
  27. package/dist/tools/components.js +10 -50
  28. package/dist/tools/designSchemaPatch.js +13 -18
  29. package/dist/tools/externalComponents.js +4 -25
  30. package/dist/tools/flows.js +38 -51
  31. package/dist/tools/functions.js +20 -10
  32. package/dist/tools/layouts.js +9 -41
  33. package/dist/tools/microservices.js +39 -11
  34. package/dist/tools/pages.js +95 -127
  35. package/dist/tools/platformInfo.js +1 -2
  36. package/dist/tools/schemaOps.js +2 -2
  37. package/dist/tools/screenWrite.js +2 -2
  38. package/dist/tools/screens.js +3 -9
  39. package/dist/tools/workflows.js +39 -12
  40. package/package.json +7 -2
  41. package/dist/knowledge/mobileComponentCatalog.js +0 -331
  42. package/dist/knowledge/mobileComponentEvents.js +0 -50
  43. package/dist/knowledge/platform/lint.js +0 -205
  44. package/dist/knowledge/platform/mobile.js +0 -78
  45. package/dist/knowledge/platform/mobileRules.js +0 -121
  46. package/dist/knowledge/platform/mobileTabs.js +0 -108
  47. package/dist/knowledge/platform/profile.js +0 -20
  48. package/dist/knowledge/platform/resolve.js +0 -122
  49. package/dist/knowledge/platform/web.js +0 -38
  50. package/dist/knowledge/styleResolveNative.js +0 -410
  51. package/dist/platform/capture/actions.js +0 -333
  52. package/dist/platform/capture/browser.js +0 -1
  53. package/dist/platform/capture/index.js +0 -10
  54. package/dist/platform/capture/readiness.js +0 -103
  55. package/dist/platform/capture/summary.js +0 -77
  56. package/dist/platform/capture/target.js +0 -5
  57. package/dist/platform/capture/targets/studio-mobile.js +0 -15
  58. package/dist/platform/capture/targets/studio-web.js +0 -5
  59. package/dist/resources/examples/mobile-app-shell-drawer.json +0 -209
  60. package/dist/resources/examples/mobile-app-shell-stack.json +0 -123
  61. package/dist/resources/examples/mobile-app-shell-tab.json +0 -174
  62. package/dist/resources/examples/mobile-list-detail-form.json +0 -656
  63. package/dist/resources/guides/mobile.md +0 -215
  64. package/dist/tools/mobile.js +0 -203
  65. package/dist/tools/profileWrite.js +0 -45
@@ -1,333 +0,0 @@
1
- import { INTERNAL_ROLE_SEPARATOR } from '../../knowledge/componentCatalog.js';
2
- import { studioWebTarget } from './targets/studio-web.js';
3
- /**
4
- * G-24 — gateway sayfayı kurabiliyor, okuyabiliyor ve çizdirebiliyordu ama üzerinde OLAY
5
- * TETİKLEYEMİYORDU: "filtre değişince veri yenileniyor" iddiası yalnız şema okumasıyla
6
- * kanıtlanabiliyordu. 09-05 koşusunda üç olay bağının ikisi şemada "bağlı" görünüp gerçekte
7
- * çalışmıyordu (form submit hiç doğmuyordu, `code` adımının gövdesi kaydedilmemişti) ve ikisi de
8
- * yalnız gerçek tıklamayla görüldü.
9
- *
10
- * Değer yazarken NATIVE setter kullanılır ve `input`+`change` olayları bubbles ile atılır: DOM
11
- * value'sunu düz atamak framework'ün duyduğu olayı üretmez, alan dolu görünür ama akış çalışmaz.
12
- * `resource` sayacı "istek gerçekten gitti mi" sorusunu cevaplar — koşuda kusuru kesinleştiren
13
- * gözlem tam olarak buydu (ağ kaydında hiç istek yoktu).
14
- */
15
- export function actionScript(action, captureTarget = studioWebTarget) {
16
- const selector = JSON.stringify(`[data-element-id="${String(action.target ?? '')}"]`);
17
- const value = JSON.stringify(action.value ?? '');
18
- // Tek tırnakla gömülür: seçiciler sabittir ve çift tırnak taşır; web betiğinin metni değişmesin.
19
- const optionSelector = `'${captureTarget.optionSelector}'`;
20
- // Seçeneklerin arandığı kök: web'de kutunun kabı (`scope`), mobilde belge (liste `Modal` katmanında).
21
- const optionRoot = captureTarget.optionsOutsideNode ? 'document' : 'scope';
22
- if (action.action === 'click') {
23
- return `(function () {
24
- var count = performance.getEntriesByType('resource').length;
25
- var node = document.querySelector(${selector});
26
- if (!node) return { found: false, applied: false, requests: count };
27
- var target = node.matches('button, a, [role="button"]') ? node : (node.querySelector('button, a, [role="button"]') || node);
28
- // S-2: sekme grubunun KÖKÜNE click, DOM sırasındaki ilk sekmeye gider ve "uygulandı" der —
29
- // ajan ikinci sekmeyi hiç seçemiyordu (2026-09-16 raporu madde 6). Sekme seçmek 'tab'in işi.
30
- if (target.closest && target.closest('[role="tablist"]') && !node.matches('[role="tab"]')) {
31
- 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 };
32
- }
33
- target.click();
34
- return { found: true, applied: true, requests: count };
35
- })()`;
36
- }
37
- // S-2 — sekme SEÇİLEMİYORDU: kimlik `<elementId>__tab-<key>` (frontend f-02) önce denenir,
38
- // runtime onu basmıyorsa görünen metinle eşlenir ve rapor hangisiyle eşlediğini söyler —
39
- // metin çeviriye bağlıdır, dil değişince kırılır.
40
- if (action.action === 'tab') {
41
- const roleSelector = JSON.stringify(`[data-element-id="${String(action.target ?? '')}__tab-${String(action.value ?? '')}"]`);
42
- return `(function () {
43
- var count = performance.getEntriesByType('resource').length;
44
- var root = document.querySelector(${selector});
45
- if (!root) return { found: false, applied: false, requests: count };
46
- var wanted = String(${value});
47
- var byRole = document.querySelector(${roleSelector});
48
- var buttons = Array.prototype.slice.call(root.querySelectorAll('[role="tab"]'));
49
- var byText = buttons.filter(function (button) { return (button.textContent || '').trim() === wanted; })[0]
50
- || buttons.filter(function (button) { return (button.textContent || '').trim().toLowerCase() === wanted.toLowerCase(); })[0];
51
- var target = byRole || byText;
52
-
53
- if (!target) {
54
- var names = buttons.map(function (button) { return (button.textContent || '').trim(); }).join(' · ');
55
- return { found: true, applied: false, optionsSeen: buttons.length, requests: count, note: buttons.length === 0
56
- ? "bu düğümde sekme yok (role=tab) — hedef gerçekten bir sekme grubu mu? page_schema_get ile tipe bakın"
57
- : "sekme bulunamadı: " + wanted + " — sekme ANAHTARINI ya da görünen metnini verin; görülenler: " + names };
58
- }
59
-
60
- if (target.disabled === true || target.getAttribute('aria-disabled') === 'true') {
61
- return { found: true, applied: false, note: "sekme devre dışı (disabled): " + wanted, requests: count };
62
- }
63
-
64
- target.click();
65
- return { found: true, applied: true, matchedBy: byRole ? 'role' : 'text', requests: count };
66
- })()`;
67
- }
68
- // S-3 — açılır kutuda seçim. 'set' ile aynı gövde; buradaki fark yalnız prop izi tutulmamasıdır
69
- // (seçim kutunun kendi değerini yazar, DOM'a elle yazılan bir değer yoktur).
70
- if (action.action === 'select') {
71
- return `(function () {
72
- var count = performance.getEntriesByType('resource').length;
73
- var node = document.querySelector(${selector});
74
- if (!node) return { found: false, applied: false, requests: count };
75
- var wanted = String(${value});
76
- var finish = function (result) { return result; };
77
-
78
- var combobox = node.querySelector('[role="combobox"], .ng-select__value-container, .ng-multiselect__value-container');
79
- if (combobox) {
80
- // Liste BU kutunun kabının içindedir. Sorgular belge kapsamlı kaldığında açık duran başka
81
- // bir kutunun (multiselect seçimden sonra kapanmıyor) seçeneğine tıklanabiliyordu ve adım
82
- // "uygulandı" diyordu; komşunun yükleme göstergesi de bu kutunun kararını blokluyordu (B-5).
83
- var scope = combobox.closest('.ng-select, .ng-multiselect') || node;
84
-
85
- combobox.click();
86
-
87
- return new Promise(function (resolve) {
88
- var waited = 0;
89
- var step = 100;
90
- var limit = 3000;
91
-
92
- var attempt = function () {
93
- var loading = scope.querySelector('.ng-select__loading, .ng-multiselect__loading');
94
- var options = Array.prototype.slice.call(${optionRoot}.querySelectorAll(${optionSelector}));
95
- var byValue = options.filter(function (option) { return option.getAttribute('data-value') === wanted; })[0];
96
- var byText = options.filter(function (option) { return (option.textContent || '').trim() === wanted; })[0];
97
- var match = byValue || byText;
98
-
99
- if (match) {
100
- var button = match.querySelector('button');
101
- // Seçilemeyen seçeneğe tıklamak hiçbir şey yapmaz; "uygulandı" demek yanlış olurdu (B-6).
102
- if (match.getAttribute('aria-disabled') === 'true' || (button && button.disabled)) {
103
- resolve(finish({ found: true, applied: false, optionsSeen: options.length, waitedMs: waited, requests: count, note: "seçenek devre dışı (aria-disabled): " + wanted }));
104
- return;
105
- }
106
-
107
- (button || match).click();
108
- resolve(finish({ found: true, applied: true, matchedBy: byValue ? 'value' : 'text', optionsSeen: options.length, waitedMs: waited, requests: count }));
109
- return;
110
- }
111
-
112
- var settled = !loading && (options.length > 0 || !!scope.querySelector('.ng-select__empty, .ng-multiselect__empty'));
113
-
114
- if (settled || waited >= limit) {
115
- var seen = options.slice(0, 10).map(function (option) { return (option.textContent || '').trim(); }).join(' · ');
116
- // Liste HİÇ açılmadıysa kusur sorguda değil kutunun kendisinde olabilir:
117
- // handleContainerClick kilitli/salt-okunur/yükleniyor kutuda erken döner.
118
- var locked = !scope.querySelector('.ng-select__list, .ng-multiselect__list');
119
- var note = options.length > 0
120
- ? "açılır kutuda bu değere karşılık gelen seçenek yok: " + wanted + " — görülen " + options.length + " seçenek: " + seen
121
- : locked
122
- ? "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"
123
- : "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";
124
- resolve(finish({ found: true, applied: false, optionsSeen: options.length, waitedMs: waited, requests: count, note: note }));
125
- return;
126
- }
127
-
128
- waited += step;
129
- setTimeout(attempt, step);
130
- };
131
-
132
- attempt();
133
- });
134
- }
135
- 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 };
136
- })()`;
137
- }
138
- // S-4 — form gönderme. Gönder butonu olmayan ya da Enter ile gönderilen form başsız
139
- // doğrulanamıyordu; `requestSubmit` formun kendi submit olayını doğduğu gibi doğurur.
140
- if (action.action === 'submit') {
141
- // Mobil hedefte ağaçta `<form>` yoktur: runtime köprüsü form düğümünün `submit` olayını
142
- // makineye iletir. Köprü düğümü tanımıyorsa (eski runtime, yanlış hedef) DOM yoluna düşülür.
143
- const bridge = captureTarget.submitBridge === undefined
144
- ? ''
145
- : `var bridge = window[${JSON.stringify(captureTarget.submitBridge)}];
146
- if (typeof bridge === 'function') {
147
- var bridged = bridge(${JSON.stringify(String(action.target ?? ''))});
148
- if (bridged && bridged.found) return { found: true, applied: bridged.applied === true, submitVia: 'bridge', requests: count, note: bridged.note };
149
- }
150
- `;
151
- return `(function () {
152
- var count = performance.getEntriesByType('resource').length;
153
- var node = document.querySelector(${selector});
154
- if (!node) return { found: false, applied: false, requests: count };
155
- ${bridge}var form = node.matches('form') ? node : (node.querySelector('form') || node.closest('form'));
156
-
157
- if (form) {
158
- if (typeof form.requestSubmit === 'function') {
159
- form.requestSubmit();
160
- return { found: true, applied: true, submitVia: 'requestSubmit', requests: count };
161
- }
162
-
163
- form.dispatchEvent(new Event('submit', { bubbles: true, cancelable: true }));
164
- return { found: true, applied: true, submitVia: 'dispatch', requests: count };
165
- }
166
-
167
- var button = node.querySelector('button[type="submit"]');
168
-
169
- if (button) {
170
- button.click();
171
- return { found: true, applied: true, submitVia: 'button', requests: count };
172
- }
173
-
174
- 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 };
175
- })()`;
176
- }
177
- // G-09 — kanban kartı ve takvim etkinliği HTML5 DnD ile taşınır. Olaylar sentetik atılır ve
178
- // TEK DataTransfer'i paylaşır; koordinat bırakma hedefinin merkezidir, çünkü takvimin saat
179
- // kolonu bırakılan saati `clientY`'den hesaplar (slot hedefi o saatin ortasına düşer).
180
- // Playwright `dragTo` gerçek fare sürer ama ScreenshotPage bu yeteneği taşımaz; betik yolu
181
- // öteki eylemlerle aynı ölçüm döngüsünden (requests · flowRuns) geçer.
182
- if (action.action === 'drag') {
183
- const destination = JSON.stringify(`[data-element-id="${String(action.to ?? '')}"]`);
184
- const destinationName = JSON.stringify(String(action.to ?? ''));
185
- return `(function () {
186
- var count = performance.getEntriesByType('resource').length;
187
- var node = document.querySelector(${selector});
188
- if (!node) return { found: false, applied: false, requests: count };
189
- var drop = document.querySelector(${destination});
190
- 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 };
191
- 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 };
192
- var transfer = new DataTransfer();
193
- var center = function (element) { var rect = element.getBoundingClientRect(); return { x: rect.left + rect.width / 2, y: rect.top + rect.height / 2 }; };
194
- 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 })); };
195
- var from = center(node);
196
- var to = center(drop);
197
- fire(node, 'dragstart', from);
198
- fire(drop, 'dragenter', to);
199
- fire(drop, 'dragover', to);
200
- fire(drop, 'drop', to);
201
- fire(node, 'dragend', to);
202
- return { found: true, applied: true, requests: count };
203
- })()`;
204
- }
205
- const target = String(action.target ?? '');
206
- const root = rootElementId(target);
207
- // G-14 — `applied` yalnız "DOM'a yazıldı" demekti: tarih seçici olayı dinlemiyordu, kutu dolu
208
- // görünüyor, prop boş kalıyordu. Kökün prop parmak izi eylemden ÖNCE alınır; runActions
209
- // yerleşmeden sonra yeniden okuyup `propWritten`'ı çıkarır. Çok alanlı bileşenin kökünde tek
210
- // değer yoktur — ilk kutuya yazmak yarım değeri "uygulandı" diye raporluyordu, kök reddedilir.
211
- return `(function () {
212
- var count = performance.getEntriesByType('resource').length;
213
- var node = document.querySelector(${selector});
214
- if (!node) return { found: false, applied: false, requests: count };
215
- var wanted = String(${value});
216
- var propsBefore = ${runtimePropsScript(root)};
217
- var finish = function (result) { if (propsBefore !== undefined) result.propsBefore = propsBefore; return result; };
218
-
219
- if (${JSON.stringify(root === target)}) {
220
- var parts = Array.prototype.slice.call(node.querySelectorAll(${JSON.stringify(`[data-element-id^="${root}${INTERNAL_ROLE_SEPARATOR}"]`)}))
221
- .filter(function (part) { return part.querySelector('input, select, textarea'); })
222
- .map(function (part) { return part.getAttribute('data-element-id'); });
223
- 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 });
224
- }
225
-
226
- // Açılır kutu (dropdown / multi-select) NATIVE alan değildir: değeri yazmak hiçbir şey
227
- // yapmaz, seçeneğe TIKLANMASI gerekir. 'set' bu noktada 'select' fiiliyle AYNI yola girer —
228
- // iki kopya seçim mantığı tutulmaz.
229
-
230
- var combobox = node.querySelector('[role="combobox"], .ng-select__value-container, .ng-multiselect__value-container');
231
- if (combobox) {
232
- // Liste BU kutunun kabının içindedir. Sorgular belge kapsamlı kaldığında açık duran başka
233
- // bir kutunun (multiselect seçimden sonra kapanmıyor) seçeneğine tıklanabiliyordu ve adım
234
- // "uygulandı" diyordu; komşunun yükleme göstergesi de bu kutunun kararını blokluyordu (B-5).
235
- var scope = combobox.closest('.ng-select, .ng-multiselect') || node;
236
-
237
- combobox.click();
238
-
239
- return new Promise(function (resolve) {
240
- var waited = 0;
241
- var step = 100;
242
- var limit = 3000;
243
-
244
- var attempt = function () {
245
- var loading = scope.querySelector('.ng-select__loading, .ng-multiselect__loading');
246
- var options = Array.prototype.slice.call(${optionRoot}.querySelectorAll(${optionSelector}));
247
- var byValue = options.filter(function (option) { return option.getAttribute('data-value') === wanted; })[0];
248
- var byText = options.filter(function (option) { return (option.textContent || '').trim() === wanted; })[0];
249
- var match = byValue || byText;
250
-
251
- if (match) {
252
- var button = match.querySelector('button');
253
- // Seçilemeyen seçeneğe tıklamak hiçbir şey yapmaz; "uygulandı" demek yanlış olurdu (B-6).
254
- if (match.getAttribute('aria-disabled') === 'true' || (button && button.disabled)) {
255
- resolve(finish({ found: true, applied: false, optionsSeen: options.length, waitedMs: waited, requests: count, note: "seçenek devre dışı (aria-disabled): " + wanted }));
256
- return;
257
- }
258
-
259
- (button || match).click();
260
- resolve(finish({ found: true, applied: true, matchedBy: byValue ? 'value' : 'text', optionsSeen: options.length, waitedMs: waited, requests: count }));
261
- return;
262
- }
263
-
264
- var settled = !loading && (options.length > 0 || !!scope.querySelector('.ng-select__empty, .ng-multiselect__empty'));
265
-
266
- if (settled || waited >= limit) {
267
- var seen = options.slice(0, 10).map(function (option) { return (option.textContent || '').trim(); }).join(' · ');
268
- // Liste HİÇ açılmadıysa kusur sorguda değil kutunun kendisinde olabilir:
269
- // handleContainerClick kilitli/salt-okunur/yükleniyor kutuda erken döner.
270
- var locked = !scope.querySelector('.ng-select__list, .ng-multiselect__list');
271
- var note = options.length > 0
272
- ? "açılır kutuda bu değere karşılık gelen seçenek yok: " + wanted + " — görülen " + options.length + " seçenek: " + seen
273
- : locked
274
- ? "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"
275
- : "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";
276
- resolve(finish({ found: true, applied: false, optionsSeen: options.length, waitedMs: waited, requests: count, note: note }));
277
- return;
278
- }
279
-
280
- waited += step;
281
- setTimeout(attempt, step);
282
- };
283
-
284
- attempt();
285
- });
286
- }
287
-
288
- var field = node.matches('input, select, textarea') ? node : node.querySelector('input, select, textarea');
289
- 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 });
290
- var proto = field instanceof HTMLSelectElement ? HTMLSelectElement.prototype
291
- : (field instanceof HTMLTextAreaElement ? HTMLTextAreaElement.prototype : HTMLInputElement.prototype);
292
- if (field instanceof HTMLInputElement && (field.type === 'checkbox' || field.type === 'radio')) {
293
- var shouldCheck = wanted === 'true' || wanted === '1' || wanted === 'on';
294
- if (field.checked !== shouldCheck) field.click();
295
- return finish({ found: true, applied: true, requests: count });
296
- }
297
- var setter = Object.getOwnPropertyDescriptor(proto, 'value').set;
298
- setter.call(field, ${value});
299
- field.dispatchEvent(new Event('input', { bubbles: true }));
300
- field.dispatchEvent(new Event('change', { bubbles: true }));
301
- return finish({ found: true, applied: true, requests: count });
302
- })()`;
303
- }
304
- /** `rf_range__start` → `rf_range`: iç rol hedefinin prop'ları kök düğümde yaşar. */
305
- export function rootElementId(target) {
306
- const separator = target.indexOf(INTERNAL_ROLE_SEPARATOR);
307
- return separator > 0 ? target.slice(0, separator) : target;
308
- }
309
- /**
310
- * Düğümün runtime prop'larının parmak izi (`@ng-apps/flow` → flow-runtime-props.ts,
311
- * `window.__ngAppsReadProps`). `undefined` okuyucu yayınlanmıyor (eski runtime) ya da prop'lar
312
- * serileşmiyor; `null` hiçbir makine düğümü tanımıyor; dize karşılaştırılabilir iz.
313
- */
314
- export function runtimePropsScript(elementId) {
315
- return `(function () { /* runtimePropsSnapshot */
316
- var read = window.__ngAppsReadProps;
317
- if (typeof read !== 'function') return undefined;
318
- try { var props = read(${JSON.stringify(elementId)}); return props === null ? null : JSON.stringify(props); } catch (error) { return undefined; }
319
- })()`;
320
- }
321
- /**
322
- * Hangi sekme AÇIK? `tab` adımı tıklamanın işe yarayıp yaramadığını ancak bununla kanıtlar:
323
- * tıklama "uygulandı" dese de bileşen seçimi kendi `selected` prop'una yazmamış olabilir.
324
- * `undefined` düğüm yok, `null` sekme grubu var ama hiçbiri seçili değil.
325
- */
326
- export function activeTabScript(elementId) {
327
- return `(function () { /* activeTabSnapshot */
328
- var root = document.querySelector(${JSON.stringify(`[data-element-id="${elementId}"]`)});
329
- if (!root) return undefined;
330
- var active = root.querySelector('[role="tab"][aria-selected="true"]');
331
- return active ? String(active.textContent || '').replace(/\\s+/g, ' ').trim() : null;
332
- })()`;
333
- }
@@ -1 +0,0 @@
1
- export {};
@@ -1,10 +0,0 @@
1
- /**
2
- * Ekran görüntüsü çekirdeği (plans/mobil-uygulama-expo G3): tarayıcı sözleşmesi, hazırlık kararı,
3
- * etkileşim eylemleri, DOM özeti ve çekim döngüsü ayrı modüllerde; platforma göre değişen
4
- * kısım yakalama HEDEFİDİR (`target.ts`, `targets/*`).
5
- */
6
- export * from './browser.js';
7
- export * from './actions.js';
8
- export * from './summary.js';
9
- export * from './readiness.js';
10
- export * from './capture.js';
@@ -1,103 +0,0 @@
1
- /**
2
- * Kare ne zaman hazır? (G-30 → B-6, 2026-09-07)
3
- *
4
- * `delayMs` TAHMİNDİ; 09-05'te runtime koşan akış sayacını yayınlamaya başladı (`@ng-apps/flow`
5
- * → flow-runtime-errors.ts) ve bekleme "running sıfır, completed > 0" olunca bitiyordu. Bu şart
6
- * mount akışı OLMAYAN sayfada (giriş ekranı) hiç sağlanmaz; akış tetiklemeyen `set` eylemi de
7
- * `completed`'ı artırmaz. 09-06·2 koşusunda üç eylemli giriş denemesi bu yüzden 47 sn sürdü;
8
- * sentetik ölçümde sayfa 0.3 sn'de hazırken aynı çağrı 48 sn'ydi — süre sayfanın değil, bu
9
- * döngünün. Hazırlık artık TEK anlık görüntüden okunur: akış sayacı + Studio'nun yükleme
10
- * iskeleti (`ViewDetail.svelte` → `.view-detail > .bx--skeleton__placeholder`) + runtime
11
- * düğümleri (`[data-element-id]`) + hata/bulunamadı hâli + adres. Karar `decideReadiness`'ta.
12
- *
13
- * Yüklenmemiş kare ASLA dönmez: bütçe dolduğunda sayfa hâlâ hazır değilse çağrı hatayla düşer
14
- * (`captureUnready: true` teşhis kipi dışında). Bozuk sayfayı görmek istiyorsan hata mesajı
15
- * neyin görüldüğünü zaten söyler.
16
- */
17
- export const readinessScript = `(function () { /* readinessSnapshot */
18
- var activity = window.__ngAppsFlowActivity;
19
- return {
20
- activity: activity && typeof activity.running === 'number' && typeof activity.completed === 'number' ? { running: activity.running, completed: activity.completed } : null,
21
- runtimeNodes: document.querySelectorAll('[data-element-id]').length,
22
- skeleton: !!document.querySelector('.view-detail > .bx--skeleton__placeholder'),
23
- viewDetail: !!document.querySelector('.view-detail'),
24
- errorState: !!document.querySelector('.view-detail__error, .view-detail .error-page'),
25
- readyState: String(document.readyState),
26
- href: String(location.href)
27
- };
28
- })()`;
29
- /** Okunan değer gerçekten sayaç mı; değilse sayfa sayaç yayınlamıyordur (eski runtime). */
30
- export function isFlowActivity(activity) {
31
- if (!activity || typeof activity !== 'object')
32
- return false;
33
- const { running, completed } = activity;
34
- return typeof running === 'number' && typeof completed === 'number';
35
- }
36
- /** 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. */
37
- export function parseReadiness(raw) {
38
- if (!raw || typeof raw !== 'object')
39
- return undefined;
40
- const value = raw;
41
- if (typeof value.runtimeNodes !== 'number' || typeof value.href !== 'string')
42
- return undefined;
43
- return {
44
- activity: isFlowActivity(value.activity) ? { running: value.activity.running, completed: value.activity.completed } : null,
45
- runtimeNodes: value.runtimeNodes,
46
- skeleton: value.skeleton === true,
47
- viewDetail: value.viewDetail === true,
48
- errorState: value.errorState === true,
49
- readyState: String(value.readyState ?? ''),
50
- href: value.href
51
- };
52
- }
53
- /** `/dev` ve `/view` aynı sayfadır (Studio `hooks.ts` reroute); yönlendirme kararı yalnız yoldan verilir. */
54
- export function runtimePath(url) {
55
- try {
56
- return new URL(url).pathname.replace(/^\/dev\//, '/view/').replace(/\/+$/, '');
57
- }
58
- catch {
59
- return url;
60
- }
61
- }
62
- /** "Hiç akış koşmadı" kararı için DOM'un kaç yoklama değişmemesi gerekir (~600 ms = eski settleMs). */
63
- const quietPolls = 5;
64
- /** Sayaç yayınlanmayan runtime'da tek kanıt DOM durulmasıdır; daha uzun beklenir (~1 sn). */
65
- const legacyQuietPolls = 8;
66
- /** Runtime dışına yönlendirme: hedef sayfanın kendi JS'i çizmeyi bitirsin (~1.5 sn). */
67
- const redirectQuietPolls = 12;
68
- /**
69
- * Tek anlık görüntüden "kare alınabilir" kararı; iki uçta da yanlış beklememek için tek yerde.
70
- * `quiet` görüntünün kaç yoklamadır değişmediğidir, `completedBefore` bu beklemenin başındaki
71
- * bitmiş akış sayısı (sayfa değiştiyse çağıran sıfırlar). İskelet görünüyorsa hiçbir karar yok.
72
- */
73
- export function decideReadiness(snapshot, quiet, completedBefore, baselineUrl) {
74
- if (snapshot.skeleton)
75
- return undefined;
76
- const { activity } = snapshot;
77
- const idle = activity !== null && activity.running === 0;
78
- if (idle && activity.completed > completedBefore && snapshot.runtimeNodes > 0)
79
- return 'ready';
80
- if (idle && snapshot.runtimeNodes > 0 && quiet >= quietPolls)
81
- return 'ready-no-flows';
82
- if (activity === null && snapshot.runtimeNodes > 0 && quiet >= legacyQuietPolls)
83
- return 'ready-no-counter';
84
- if (idle && snapshot.errorState && quiet >= quietPolls)
85
- return 'error';
86
- if (!snapshot.viewDetail && snapshot.readyState === 'complete' && baselineUrl !== undefined && runtimePath(snapshot.href) !== runtimePath(baselineUrl) && quiet >= redirectQuietPolls)
87
- return 'redirected';
88
- return undefined;
89
- }
90
- /** Hata mesajı için: bütçe dolduğunda sayfada NE görüldü. */
91
- export function describeUnready(readiness, apiNote) {
92
- const snapshot = readiness.snapshot;
93
- const suffix = apiNote === undefined ? '' : ` · ${apiNote}`;
94
- if (!snapshot)
95
- return `tarayıcı sayfayı hiç değerlendiremedi (sürekli yönlendirme ya da bağlam düştü)${suffix}`;
96
- if (snapshot.skeleton)
97
- return `sayfa hâlâ yükleme iskeletinde${suffix}`;
98
- if (snapshot.activity && snapshot.activity.running > 0)
99
- return `${snapshot.activity.running} akış hâlâ koşuyor (${snapshot.activity.completed} bitti)${suffix}`;
100
- if (snapshot.runtimeNodes === 0)
101
- return `runtime hiç render olmadı — data-element-id taşıyan düğüm yok${snapshot.viewDetail ? '' : ', .view-detail kabı da yok'}${suffix}`;
102
- return `sayfa durulmadı (${snapshot.runtimeNodes} düğüm, sayaç ${snapshot.activity ? `${snapshot.activity.running} koşan / ${snapshot.activity.completed} biten` : 'yok'})${suffix}`;
103
- }
@@ -1,77 +0,0 @@
1
- /**
2
- * NE GÖRÜNÜYOR — resimsiz (S-5). Aynı sayfa yüklemesinden çıkan üçüncü çıktı: her KÖK runtime
3
- * düğümünün görünürlüğü, metni, satır sayısı, açık sekmesi, değeri ve hata metinleri.
4
- *
5
- * Kök düğüm = kimliğinde `__` OLMAYAN düğüm; iç roller (`tbl__row-3`) satır sayısına dönüşür,
6
- * listeye girmez. Tavan 300 düğüm, metin 80 karakter (K3) — özet bir kareyi değiştirmeli, onun
7
- * kadar pahalı olmamalı. Tip `data-component-type`'tan gelir (frontend f-02); runtime basmıyorsa
8
- * alan YAZILMAZ ve `typed: false` döner — `layoutReportUnavailable` ile aynı dürüstlük kuralı.
9
- */
10
- export function domSummaryScript(viewportHeight) {
11
- return `(function () { /* domSummary */
12
- var MAX = 300;
13
- var CUT = 80;
14
- // Görünür alanın GERÇEK boyu: fullPage ve clip yolları viewport'u içerik boyuna büyütür ve
15
- // o anda window.innerHeight = içerik yüksekliği olur — her düğüm "ekran içinde" görünürdü ve
16
- // "kart katlanmanın altında mı" sorusu HER ZAMAN 'visible' cevabı alırdı (B-10).
17
- var VH = ${Math.max(Math.round(viewportHeight), 1)};
18
- var clean = function (value) { return String(value || '').replace(/\\s+/g, ' ').trim(); };
19
- // Mobil sekme çubuğu '__tabs' bir KÖK satırdır (öğeleri '__tabs__item-<slug>' iç roldür, K14);
20
- // web runtime bu kimliği hiç basmaz.
21
- var ROOT_SELECTOR = '[data-element-id]:not([data-element-id*="__"]), [data-element-id="__tabs"]';
22
- var isRoot = function (element) { var id = String(element.getAttribute('data-element-id') || ''); return id.indexOf('__') < 0 || id === '__tabs'; };
23
- var all = Array.prototype.slice.call(document.querySelectorAll('[data-element-id]'));
24
- var roots = all.filter(isRoot);
25
- var typed = false;
26
- var nodes = roots.slice(0, MAX).map(function (node) {
27
- var id = node.getAttribute('data-element-id');
28
- var type = node.getAttribute('data-component-type');
29
- if (type) typed = true;
30
- // Bir olgu, ONU TAŞIYAN düğüme yazılır: en yakın kök ata bu düğüm değilse o olgu
31
- // çocuğundur ve orada zaten listelenecektir (B-8). Aksi hâlde aynı değer üç satırda çıkar.
32
- var owned = function (element) { return !!element && element.closest(ROOT_SELECTOR) === node; };
33
- var rect = node.getBoundingClientRect();
34
- var visible = typeof node.checkVisibility === 'function'
35
- ? !!node.checkVisibility({ checkVisibilityCSS: true, checkOpacity: true })
36
- : (rect.width > 0 && rect.height > 0);
37
- var inViewport = rect.bottom > 0 && rect.right > 0 && rect.top < VH && rect.left < (window.innerWidth || 0);
38
- // Kap düğümün innerText'i ÇOCUKLARININ metnidir; metin yalnız YAPRAK köklerde yazılır.
39
- var nested = Array.prototype.slice.call(node.querySelectorAll('[data-element-id]'))
40
- .some(isRoot);
41
- var text = nested ? '' : clean(node.innerText);
42
- if (text.length > CUT) text = text.slice(0, CUT) + '…';
43
- var rows = document.querySelectorAll('[data-element-id^="' + id + '__row-"]').length;
44
- var tab = node.querySelector('[role="tab"][aria-selected="true"]');
45
- var field = node.querySelector('input, select, textarea');
46
- var chosen = node.querySelector('.selected-item');
47
- var holder = field || chosen;
48
- var value = !owned(holder) ? '' : (field ? (field.type === 'password' ? '***' : clean(field.value)) : clean(chosen.textContent));
49
- var media = node.querySelector('img, svg, canvas, video, iframe');
50
- // İç içe <li> hem grubu hem alt mesajları sayıyordu: tek grup + 2 alt hata 3 görünüyordu (B-7).
51
- var errorItems = Array.prototype.slice.call(node.querySelectorAll('.form-validation-errors li:not(:has(li))'))
52
- .filter(owned).map(function (item) { return clean(item.textContent); });
53
- // aria-invalid hem köke hem input'a yazılabiliyor (Password.svelte); yalnız ALANLAR sayılır.
54
- var invalid = Array.prototype.slice.call(node.querySelectorAll('input[aria-invalid="true"], select[aria-invalid="true"], textarea[aria-invalid="true"]')).filter(owned).length;
55
- var summary = { elementId: id, visible: !!visible, inViewport: !!inViewport };
56
- if (type) summary.type = type;
57
- if (text) summary.text = text;
58
- if (rows > 0) summary.rows = rows;
59
- if (owned(tab)) summary.activeTab = clean(tab.textContent);
60
- if (value) summary.value = value;
61
- if (visible && !text && !media && !nested) summary.empty = true;
62
- if (errorItems.length > 0) summary.errors = errorItems;
63
- else if (invalid > 0) summary.errors = ['aria-invalid: ' + invalid];
64
- return summary;
65
- });
66
- var messages = [];
67
- Array.prototype.slice.call(document.querySelectorAll('.ng-toast')).forEach(function (toast) {
68
- var name = String(toast.className || '');
69
- var kind = name.indexOf('ng-toast--error') >= 0 ? 'toast/error' : (name.indexOf('ng-toast--success') >= 0 ? 'toast/success' : 'toast');
70
- messages.push({ kind: kind, text: clean(toast.innerText).slice(0, 160) });
71
- });
72
- Array.prototype.slice.call(document.querySelectorAll('.view-detail__error, .error-page')).forEach(function (node) {
73
- messages.push({ kind: 'error', text: clean(node.innerText).slice(0, 160) });
74
- });
75
- return { nodes: nodes, messages: messages, truncated: roots.length > MAX, typed: typed };
76
- })()`;
77
- }
@@ -1,5 +0,0 @@
1
- import { studioMobileTarget } from './targets/studio-mobile.js';
2
- import { studioWebTarget } from './targets/studio-web.js';
3
- export function captureTargetFor(name) {
4
- return name === 'studio-mobile' ? studioMobileTarget : studioWebTarget;
5
- }
@@ -1,15 +0,0 @@
1
- /**
2
- * Mobil uygulama: `@ng-apps/ui-native` `NativeRuntime`, Studio'da react-native-web ile çizilir
3
- * (plans/mobil-uygulama-expo F5). Ağaçta `<form>` yoktur — `submit` runtime'ın yayımladığı köprüyle
4
- * form düğümünün `submit` olayını makineye iletir (`web-runtime-host.tsx` `__ngAppsSubmitForm`).
5
- * Sekme çubuğu `__tabs`, öğeleri `__tabs__item-<sayfa slug'ı>` kimliğini taşır (K14). Açılır
6
- * listenin tetikleyicisi `role="combobox"`, seçenekleri `role="option"` + `data-value` taşır ve liste
7
- * `Modal` katmanında açılır (`ng-apps-ui-native` `dropdown`).
8
- */
9
- export const studioMobileTarget = Object.freeze({
10
- name: 'studio-mobile',
11
- submitBridge: '__ngAppsSubmitForm',
12
- optionSelector: '[role="option"][data-value], [role="treeitem"][data-value]',
13
- optionsOutsideNode: true,
14
- platformNote: 'Mobil uygulama: kare Studio\'daki react-native-web çizimidir (/dev|/view) — cihazdaki native çizimle yapısal olarak (düğüm, sıra, ölçü modu, renk) aynı, piksel olarak değil.'
15
- });
@@ -1,5 +0,0 @@
1
- /** Web uygulaması: Svelte runtime. Seçenekler `ng-select` ağacı, form gerçek `<form>`. */
2
- export const studioWebTarget = Object.freeze({
3
- name: 'studio-web',
4
- optionSelector: '[role="treeitem"][data-value]'
5
- });