@appaflytech/zeyrek-ai-mcp 0.0.31 → 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.
- package/.env.example +0 -15
- package/README.md +5 -5
- package/dist/config.js +0 -37
- package/dist/index.js +0 -2
- package/dist/knowledge/backendConstraints.js +1 -18
- package/dist/knowledge/boxSections.js +184 -0
- package/dist/knowledge/componentCatalog.js +233 -568
- package/dist/knowledge/componentEvents.js +21 -141
- package/dist/knowledge/layoutSummary.js +2 -140
- package/dist/knowledge/normalize.js +67 -236
- package/dist/knowledge/outlines.js +0 -36
- package/dist/knowledge/policy.js +4 -18
- package/dist/knowledge/reservedTables.js +0 -79
- package/dist/knowledge/rules.js +21 -366
- package/dist/knowledge/schemaGuard.js +0 -11
- package/dist/knowledge/styleResolve.js +38 -55
- package/dist/knowledge/themeResolve.js +3 -3
- package/dist/observability/trace.js +3 -6
- package/dist/platform/diagnostics.js +4 -10
- package/dist/platform/domSummary.js +1 -1
- package/dist/platform/errors.js +10 -162
- package/dist/platform/http.js +8 -29
- package/dist/platform/responses.js +5 -39
- package/dist/platform/{capture/capture.js → screenshot.js} +519 -105
- package/dist/platform/secrets.js +1 -17
- package/dist/platform/strictArguments.js +12 -68
- package/dist/platform/tokenVault.js +4 -187
- package/dist/resources/examples/layout-app-shell.json +707 -229
- package/dist/resources/examples/middleware-authenticate.json +1 -1
- package/dist/resources/examples/page-detail-profile.json +1 -2
- package/dist/resources/examples/page-dynamic-form.json +2 -3
- package/dist/resources/examples/page-flex-sections.json +1 -1
- package/dist/resources/examples/page-html-embed.json +1 -1
- package/dist/resources/examples/workflow-basic.json +2 -8
- package/dist/resources/guides/component.md +5 -92
- package/dist/resources/guides/entity.md +8 -50
- package/dist/resources/guides/expressions.md +5 -14
- package/dist/resources/guides/functions.md +1 -1
- package/dist/resources/guides/getting-started.md +1 -38
- package/dist/resources/guides/layout.md +58 -55
- package/dist/resources/guides/merge.md +3 -37
- package/dist/resources/guides/microservice.md +2 -87
- package/dist/resources/guides/middleware.md +4 -68
- package/dist/resources/guides/page.md +75 -456
- package/dist/resources/guides/theme.md +2 -8
- package/dist/resources/guides/translation.md +1 -1
- package/dist/resources/guides/workflow.md +1 -58
- package/dist/resources/rules.js +5 -11
- package/dist/resources/schema-fingerprint.json +5 -7
- package/dist/resources/schemas/CreateEntityCommand.json +0 -118
- package/dist/resources/schemas/CreateMicroserviceCommand.json +1 -13
- package/dist/resources/schemas/CreateQueryCommand.json +0 -4
- package/dist/resources/schemas/CreateWorkflowCommand.json +1 -146
- package/dist/resources/schemas/DataJobDto.json +1 -6
- package/dist/resources/schemas/GlobalFunctionUpdateResultDto.json +1 -21
- package/dist/resources/schemas/MicroserviceDto.json +3 -15
- package/dist/resources/schemas/PreviewMicroserviceCommand.json +1 -10
- package/dist/resources/schemas/PreviewWorkflowCommand.json +1 -25
- package/dist/resources/schemas/StepDto.json +1 -25
- package/dist/resources/schemas/UpdateEntityCommand.json +0 -118
- package/dist/resources/schemas/UpdateMicroserviceCommand.json +1 -13
- package/dist/resources/schemas/UpdateQueryCommand.json +0 -4
- package/dist/resources/schemas/UpdateViewCommand.json +2 -0
- package/dist/resources/schemas/UpdateWorkflowCommand.json +1 -146
- package/dist/server.js +1 -27
- package/dist/tools/access.js +2 -369
- package/dist/tools/applications.js +3 -60
- package/dist/tools/common.js +0 -7
- package/dist/tools/components.js +14 -118
- package/dist/tools/datasources.js +3 -3
- package/dist/tools/dependencies.js +1 -1
- package/dist/tools/deployments.js +3 -38
- package/dist/tools/designSchemaPatch.js +18 -34
- package/dist/tools/dtos.js +5 -36
- package/dist/tools/entities.js +12 -79
- package/dist/tools/externalComponents.js +5 -26
- package/dist/tools/flows.js +14 -122
- package/dist/tools/folders.js +1 -1
- package/dist/tools/imports.js +2 -5
- package/dist/tools/layouts.js +17 -50
- package/dist/tools/merges.js +2 -2
- package/dist/tools/microservices.js +25 -77
- package/dist/tools/pages.js +112 -465
- package/dist/tools/platformInfo.js +8 -27
- package/dist/tools/projects.js +9 -71
- package/dist/tools/rows.js +13 -69
- package/dist/tools/schemaOps.js +1 -61
- package/dist/tools/screenWrite.js +8 -37
- package/dist/tools/screens.js +5 -95
- package/dist/tools/templates.js +1 -26
- package/dist/tools/translations.js +1 -1
- package/dist/tools/variables.js +5 -11
- package/dist/tools/workflows.js +5 -10
- package/package.json +1 -1
- package/dist/knowledge/access.js +0 -802
- package/dist/knowledge/case.js +0 -41
- package/dist/knowledge/connectDataBind.js +0 -819
- package/dist/knowledge/connectDataDefinition.js +0 -341
- package/dist/knowledge/connectDataExecute.js +0 -93
- package/dist/knowledge/connectDataRules.js +0 -128
- package/dist/knowledge/decision.js +0 -121
- package/dist/knowledge/expressionPrelude.js +0 -59
- package/dist/knowledge/microserviceParameters.js +0 -58
- package/dist/knowledge/mining.js +0 -94
- package/dist/knowledge/mobileComponentCatalog.js +0 -859
- package/dist/knowledge/mobileComponentEvents.js +0 -239
- package/dist/knowledge/platform/lint.js +0 -385
- package/dist/knowledge/platform/mobile.js +0 -142
- package/dist/knowledge/platform/mobileLayoutLint.js +0 -324
- package/dist/knowledge/platform/mobileRules.js +0 -231
- package/dist/knowledge/platform/mobileSpacing.js +0 -82
- package/dist/knowledge/platform/mobileTabs.js +0 -108
- package/dist/knowledge/platform/nativeBoxModel.js +0 -44
- package/dist/knowledge/platform/profile.js +0 -25
- package/dist/knowledge/platform/resolve.js +0 -122
- package/dist/knowledge/platform/web.js +0 -39
- package/dist/knowledge/process.js +0 -510
- package/dist/knowledge/rowFilter.js +0 -43
- package/dist/knowledge/styleResolveNative.js +0 -527
- package/dist/platform/argumentAliases.js +0 -128
- package/dist/platform/bodyFile.js +0 -103
- package/dist/platform/capture/actions.js +0 -333
- package/dist/platform/capture/browser.js +0 -1
- package/dist/platform/capture/console-log.js +0 -122
- package/dist/platform/capture/index.js +0 -11
- package/dist/platform/capture/readiness.js +0 -103
- package/dist/platform/capture/summary.js +0 -77
- package/dist/platform/capture/target.js +0 -5
- package/dist/platform/capture/targets/studio-mobile.js +0 -15
- package/dist/platform/capture/targets/studio-web.js +0 -5
- package/dist/platform/impersonation.js +0 -45
- package/dist/platform/jsonArray.js +0 -24
- package/dist/platform/schemaReadMemory.js +0 -60
- package/dist/resources/examples/bpm-gorev-kontrol.json +0 -294
- package/dist/resources/examples/bpm-iso-dokuman.json +0 -228
- package/dist/resources/examples/bpm-satinalma.json +0 -376
- package/dist/resources/examples/connect-data-convert-verify.json +0 -107
- package/dist/resources/examples/connect-data-dropdown-roles.json +0 -66
- package/dist/resources/examples/connect-data-edit-m2m.json +0 -73
- package/dist/resources/examples/connect-data-query-params.json +0 -50
- package/dist/resources/examples/connect-data-table-entity.json +0 -147
- package/dist/resources/examples/connect-data-workflow-microservice.json +0 -65
- package/dist/resources/examples/mobile-app-shell-drawer.json +0 -243
- package/dist/resources/examples/mobile-app-shell-stack.json +0 -157
- package/dist/resources/examples/mobile-app-shell-tab.json +0 -208
- package/dist/resources/examples/mobile-form-screen.json +0 -365
- package/dist/resources/examples/mobile-list-detail-form.json +0 -672
- package/dist/resources/guides/access.md +0 -810
- package/dist/resources/guides/bpm.md +0 -691
- package/dist/resources/guides/case.md +0 -153
- package/dist/resources/guides/decision.md +0 -185
- package/dist/resources/guides/mobile-layout.md +0 -193
- package/dist/resources/guides/mobile.md +0 -495
- package/dist/resources/read.js +0 -41
- package/dist/resources/schemas/AccessExplanationDto.json +0 -144
- package/dist/resources/schemas/AccessMatrixDto.json +0 -170
- package/dist/resources/schemas/AccessPermissionDto.json +0 -57
- package/dist/resources/schemas/AccessPolicyDto.json +0 -223
- package/dist/resources/schemas/AccessResourceDto.json +0 -86
- package/dist/resources/schemas/AppSessionDto.json +0 -53
- package/dist/resources/schemas/ConnectDataDescribeDto.json +0 -344
- package/dist/resources/schemas/ConnectDataRequestModel.json +0 -202
- package/dist/resources/schemas/ConnectDataResultDto.json +0 -421
- package/dist/resources/schemas/ConnectorCredentialDto.json +0 -70
- package/dist/resources/schemas/ConvertConnectDataToMicroserviceCommand.json +0 -37
- package/dist/resources/schemas/CreateAccessPolicyCommand.json +0 -209
- package/dist/resources/schemas/CreateDecisionCommand.json +0 -221
- package/dist/resources/schemas/CreateProcessDefinitionCommand.json +0 -1337
- package/dist/resources/schemas/DecisionDto.json +0 -302
- package/dist/resources/schemas/DetachConnectDataMicroserviceCommand.json +0 -28
- package/dist/resources/schemas/ExplainAccessQuery.json +0 -33
- package/dist/resources/schemas/GenerateProcessDocumentCommand.json +0 -116
- package/dist/resources/schemas/OrganizationProjectDto.json +0 -335
- package/dist/resources/schemas/OrganizationSchemaDto.json +0 -196
- package/dist/resources/schemas/OrganizationSchemaVersionDto.json +0 -207
- package/dist/resources/schemas/PatchProcessDefinitionCommand.json +0 -76
- package/dist/resources/schemas/ProcessDefinitionDto.json +0 -1480
- package/dist/resources/schemas/ProcessSimulationDto.json +0 -96
- package/dist/resources/schemas/SaveConnectorCredentialCommand.json +0 -90
- package/dist/resources/schemas/SaveProcessEventSourceCommand.json +0 -61
- package/dist/resources/schemas/SaveProcessMailSourceCommand.json +0 -89
- package/dist/resources/schemas/SignProcessTaskCommand.json +0 -73
- package/dist/resources/schemas/SimulateProcessDefinitionQuery.json +0 -92
- package/dist/resources/schemas/UpdateAccessPolicyCommand.json +0 -209
- package/dist/resources/schemas/UpdateDecisionCommand.json +0 -216
- package/dist/resources/schemas/UpdateProcessDefinitionCommand.json +0 -1357
- package/dist/resources/schemas/ValidateProcessDefinitionQuery.json +0 -1339
- package/dist/tools/accessOperations.js +0 -135
- package/dist/tools/accessPolicies.js +0 -170
- package/dist/tools/accessRuntime.js +0 -203
- package/dist/tools/bpm.js +0 -343
- package/dist/tools/bpmGovernance.js +0 -266
- package/dist/tools/bpmOperations.js +0 -230
- package/dist/tools/bpmReporting.js +0 -154
- package/dist/tools/bpmRouting.js +0 -150
- package/dist/tools/bpmRuntime.js +0 -396
- package/dist/tools/case.js +0 -178
- package/dist/tools/caseRuntime.js +0 -89
- package/dist/tools/connectData.js +0 -449
- package/dist/tools/connectors.js +0 -193
- package/dist/tools/decisions.js +0 -206
- package/dist/tools/mobile.js +0 -221
- package/dist/tools/modules.js +0 -48
- package/dist/tools/organizationStructure.js +0 -137
- package/dist/tools/profileWrite.js +0 -92
|
@@ -1,103 +0,0 @@
|
|
|
1
|
-
import { open, realpath } from 'node:fs/promises';
|
|
2
|
-
import { constants } from 'node:fs';
|
|
3
|
-
import { extname, resolve, sep } from 'node:path';
|
|
4
|
-
import { z } from 'zod';
|
|
5
|
-
import { PlatformApiError } from './errors.js';
|
|
6
|
-
/**
|
|
7
|
-
* g-08 (`plans/mobil-plantcare-acigi`): tanım yazan araçlar gövdeyi dosyadan alabilir. Koşuda en
|
|
8
|
-
* büyük bağlam tasarrufu, ajanın kendi yazdığı "dosyadan MCP'ye" betiğinden geldi; o iş buraya
|
|
9
|
-
* taşındı. Her aracın TEK gövde argümanı vardır ve dosyanın JSON'u o argümanın değeri olur. Dosya
|
|
10
|
-
* SDK doğrulamasından ÖNCE okunur (`strictArguments.ts`), yani satır içi gövdeyle aynı şema ve aynı
|
|
11
|
-
* yol — dosyadan gelen gövde hiçbir kontrolü atlamaz.
|
|
12
|
-
*/
|
|
13
|
-
export const BODY_FILE_ARGUMENTS = {
|
|
14
|
-
page_create: 'schema',
|
|
15
|
-
page_update: 'schema',
|
|
16
|
-
page_schema_patch: 'ops',
|
|
17
|
-
flow_create: 'steps',
|
|
18
|
-
flow_update: 'steps',
|
|
19
|
-
flow_patch: 'operations',
|
|
20
|
-
microservice_create: 'definition',
|
|
21
|
-
microservice_update: 'definition',
|
|
22
|
-
microservice_patch: 'operations',
|
|
23
|
-
workflow_create: 'definition',
|
|
24
|
-
workflow_update: 'definition',
|
|
25
|
-
workflow_patch: 'operations'
|
|
26
|
-
};
|
|
27
|
-
/** `ops`/`operations` eşanlamlıdır (`microOps`); ikisinden biri satır içinde varsa gövde verilmiştir. */
|
|
28
|
-
const synonyms = { ops: ['ops', 'operations'], operations: ['ops', 'operations'] };
|
|
29
|
-
const maxBodyFileBytes = 5 * 1024 * 1024;
|
|
30
|
-
export function bodyFileParam(argument) {
|
|
31
|
-
return z.string().min(1).optional().describe(`'${argument}' yerine okunacak yerel .json dosyası (en çok 5 MB): gövde ajan bağlamından geçmez, gateway dosyayı okuyup '${argument}' olarak aynı doğrulamadan geçirir. '${argument}' ile birlikte verilemez. Yol gateway sürecinin çalışma dizininin ya da MCP_INPUT_DIR'in altında olmalı (sembolik bağ çözülür). Yanıtın bodyFile alanı hangi dosyanın okunduğunu söyler.`);
|
|
32
|
-
}
|
|
33
|
-
export async function loadBodyFile(tool, args, config) {
|
|
34
|
-
const argument = BODY_FILE_ARGUMENTS[tool];
|
|
35
|
-
if (!argument || !args || typeof args !== 'object' || Array.isArray(args))
|
|
36
|
-
return { args };
|
|
37
|
-
const { bodyFile, ...rest } = args;
|
|
38
|
-
if (bodyFile === undefined)
|
|
39
|
-
return { args };
|
|
40
|
-
if (typeof bodyFile !== 'string' || bodyFile.length === 0)
|
|
41
|
-
return { args };
|
|
42
|
-
const inline = (synonyms[argument] ?? [argument]).filter(name => rest[name] !== undefined);
|
|
43
|
-
if (inline.length > 0) {
|
|
44
|
-
throw new PlatformApiError(`${tool}: bodyFile ile '${inline.join("', '")}' birlikte verilemez — gövde ya dosyadan ya satır içinden gelir. Birini kaldırın. İstek gönderilmedi.`, 400);
|
|
45
|
-
}
|
|
46
|
-
const absolute = resolve(bodyFile);
|
|
47
|
-
if (extname(absolute).toLowerCase() !== '.json') {
|
|
48
|
-
throw new PlatformApiError(`${tool}: bodyFile yalnız .json okur — verilen: ${absolute}. İstek gönderilmedi.`, 400);
|
|
49
|
-
}
|
|
50
|
-
let real;
|
|
51
|
-
try {
|
|
52
|
-
real = await realpath(absolute);
|
|
53
|
-
}
|
|
54
|
-
catch (error) {
|
|
55
|
-
throw new PlatformApiError(`${tool}: bodyFile okunamadı — ${absolute} (${error instanceof Error ? error.message : String(error)}). Yol gateway sürecinin çalıştığı makineye göre çözülür.`, 400);
|
|
56
|
-
}
|
|
57
|
-
if (extname(real).toLowerCase() !== '.json') {
|
|
58
|
-
throw new PlatformApiError(`${tool}: bodyFile'ın hedefi .json değil — ${real} (${absolute} bağının hedefi). İstek gönderilmedi.`, 400);
|
|
59
|
-
}
|
|
60
|
-
const roots = await Promise.all([process.cwd(), config.inputDirectory]
|
|
61
|
-
.filter((root) => typeof root === 'string' && root.length > 0)
|
|
62
|
-
.map(root => realpath(resolve(root)).catch(() => undefined)));
|
|
63
|
-
const allowed = roots.some(root => root !== undefined && (real === root || real.startsWith(`${root}${sep}`)));
|
|
64
|
-
if (!allowed) {
|
|
65
|
-
throw new PlatformApiError(`${tool}: bodyFile izinli dizinlerin dışında — ${real}${real !== absolute ? ` (${absolute} bağının hedefi)` : ''}. Gateway yalnız çalışma dizininin (${process.cwd()}) ve .env'deki MCP_INPUT_DIR'in (${config.inputDirectory ?? 'tanımsız'}) altındaki dosyaları okur. İstek gönderilmedi.`, 400);
|
|
66
|
-
}
|
|
67
|
-
// Denetim ve okuma AYNI tutamaçtan: yol denetimden sonra değişse de okunan, denetlenen dosyadır.
|
|
68
|
-
// O_NOFOLLOW son bileşenin sonradan bağa çevrilmesini, O_NONBLOCK `.json` adlı FIFO'nun çağrıyı
|
|
69
|
-
// bekletmesini önler; boyut dosyanın büyüklüğüne değil okunan bayta bakar.
|
|
70
|
-
let handle;
|
|
71
|
-
try {
|
|
72
|
-
handle = await open(real, constants.O_RDONLY | constants.O_NOFOLLOW | constants.O_NONBLOCK);
|
|
73
|
-
}
|
|
74
|
-
catch (error) {
|
|
75
|
-
throw new PlatformApiError(`${tool}: bodyFile açılamadı — ${real} (${error instanceof Error ? error.message : String(error)}). İstek gönderilmedi.`, 400);
|
|
76
|
-
}
|
|
77
|
-
let bytes;
|
|
78
|
-
let text;
|
|
79
|
-
try {
|
|
80
|
-
const info = await handle.stat();
|
|
81
|
-
if (!info.isFile()) {
|
|
82
|
-
throw new PlatformApiError(`${tool}: bodyFile düzenli dosya değil — ${real}. İstek gönderilmedi.`, 400);
|
|
83
|
-
}
|
|
84
|
-
const buffer = Buffer.alloc(maxBodyFileBytes + 1);
|
|
85
|
-
const { bytesRead } = await handle.read(buffer, 0, buffer.length, 0);
|
|
86
|
-
bytes = bytesRead;
|
|
87
|
-
if (bytes > maxBodyFileBytes) {
|
|
88
|
-
throw new PlatformApiError(`${tool}: bodyFile 5 MB sınırını aşıyor. Tanımı parçalara bölün — sayfa için page_schema_patch'e birden çok batch, akış için flow_patch. İstek gönderilmedi.`, 400);
|
|
89
|
-
}
|
|
90
|
-
text = buffer.subarray(0, bytes).toString('utf8');
|
|
91
|
-
}
|
|
92
|
-
finally {
|
|
93
|
-
await handle.close();
|
|
94
|
-
}
|
|
95
|
-
let value;
|
|
96
|
-
try {
|
|
97
|
-
value = JSON.parse(text);
|
|
98
|
-
}
|
|
99
|
-
catch (error) {
|
|
100
|
-
throw new PlatformApiError(`${tool}: bodyFile JSON değil — ${real} (${error instanceof Error ? error.message : String(error)}). İstek gönderilmedi.`, 400);
|
|
101
|
-
}
|
|
102
|
-
return { args: { ...rest, [argument]: value }, note: `${bodyFile} → ${argument} (${bytes} B)` };
|
|
103
|
-
}
|
|
@@ -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,122 +0,0 @@
|
|
|
1
|
-
import { maskSecretText } from '../secrets.js';
|
|
2
|
-
const bufferLimit = 20;
|
|
3
|
-
const reportLimit = 5;
|
|
4
|
-
const textLimit = 300;
|
|
5
|
-
const stackLines = 5;
|
|
6
|
-
const clip = (text) => (text.length > textLimit ? `${text.slice(0, textLimit - 1)}…` : text);
|
|
7
|
-
/**
|
|
8
|
-
* Gateway'in KENDİ ayarının ürettiği satırlar: kare bağlamı "hareketi azalt" ile açılır (SCR-014) ve reanimated bunu
|
|
9
|
-
* geliştirme kipinde uyarır. Sayfanın kusuru değildir; her donuk karede `consoleErrors`'a düşüp ajanı yanıltıyordu.
|
|
10
|
-
*/
|
|
11
|
-
const selfInduced = [/^\[Reanimated\] Reduced motion setting is enabled/];
|
|
12
|
-
/**
|
|
13
|
-
* Çekim boyunca tarayıcı konsolunun hata/uyarı satırları ve yakalanmamış istisnalar. Koşuda sayfa
|
|
14
|
-
* çizilmediğinde `page_screenshot` yalnız "runtime hiç render olmadı" diyordu; nedeni bulmak için
|
|
15
|
-
* ajan kendi Playwright betiğini yazıp Studio paketinin sıkıştırılmış kaynağını okudu
|
|
16
|
-
* (plans/mobil-plantcare-acigi g-06). Aynı metin tekrarlanırsa sayaç artar, satır eklenmez; metin
|
|
17
|
-
* kırpılır ve kimlik parçaları maskelenir.
|
|
18
|
-
*/
|
|
19
|
-
export function createPageConsoleLog() {
|
|
20
|
-
const consoleRecords = [];
|
|
21
|
-
const pageErrorRecords = [];
|
|
22
|
-
return {
|
|
23
|
-
onConsole(message) {
|
|
24
|
-
const type = message.type();
|
|
25
|
-
const level = type === 'error' ? 'error' : type === 'warning' ? 'warning' : undefined;
|
|
26
|
-
if (!level)
|
|
27
|
-
return;
|
|
28
|
-
if (selfInduced.some(pattern => pattern.test(message.text())))
|
|
29
|
-
return;
|
|
30
|
-
const text = clip(maskSecretText(message.text()));
|
|
31
|
-
const existing = consoleRecords.find(record => record.level === level && record.text === text);
|
|
32
|
-
if (existing)
|
|
33
|
-
existing.count += 1;
|
|
34
|
-
else if (consoleRecords.length < bufferLimit)
|
|
35
|
-
consoleRecords.push({ level, text, count: 1 });
|
|
36
|
-
},
|
|
37
|
-
onPageError(error) {
|
|
38
|
-
if (pageErrorRecords.length >= bufferLimit)
|
|
39
|
-
return;
|
|
40
|
-
const message = clip(maskSecretText(error instanceof Error ? `${error.name}: ${error.message}` : String(error)));
|
|
41
|
-
const stack = error instanceof Error && error.stack
|
|
42
|
-
? maskSecretText(error.stack.split('\n').slice(1, stackLines + 1).map(line => line.trim()).join('\n'))
|
|
43
|
-
: undefined;
|
|
44
|
-
if (!pageErrorRecords.some(record => record.message === message))
|
|
45
|
-
pageErrorRecords.push({ message, ...(stack ? { stack } : {}) });
|
|
46
|
-
},
|
|
47
|
-
/** Önce hatalar, sonra uyarılar; en çok beş kayıt. `skip` sınırdan ÖNCE eler (ör. defterde zaten olan satır). */
|
|
48
|
-
consoleErrors(skip) {
|
|
49
|
-
const kept = skip ? consoleRecords.filter(record => !skip(record)) : consoleRecords;
|
|
50
|
-
return [...kept.filter(record => record.level === 'error'), ...kept.filter(record => record.level === 'warning')].slice(0, reportLimit);
|
|
51
|
-
},
|
|
52
|
-
pageErrors() {
|
|
53
|
-
return pageErrorRecords.slice(0, reportLimit);
|
|
54
|
-
}
|
|
55
|
-
};
|
|
56
|
-
}
|
|
57
|
-
const expressionsPerElement = 2;
|
|
58
|
-
/**
|
|
59
|
-
* Akış hata defterini ikiye ayırır (plans/zm-bulgulari-kapanisi g-06): akış adımı hataları `runtimeErrors`'ta kalır,
|
|
60
|
-
* prop ifadesi hataları `expressionErrors` olur — öğe başına en çok iki kayıt. Yalnız "Expression error" toast'ı
|
|
61
|
-
* vardı; ajan hangi öğenin hangi prop'unun patladığını DOM'daki toast metninden çıkaramıyordu.
|
|
62
|
-
*/
|
|
63
|
-
export function splitExpressionErrors(records) {
|
|
64
|
-
const perElement = new Map();
|
|
65
|
-
const expressionErrors = [];
|
|
66
|
-
for (const record of records) {
|
|
67
|
-
if (record.kind !== 'expression' || typeof record.element !== 'string')
|
|
68
|
-
continue;
|
|
69
|
-
const seen = perElement.get(record.element) ?? 0;
|
|
70
|
-
if (seen >= expressionsPerElement)
|
|
71
|
-
continue;
|
|
72
|
-
perElement.set(record.element, seen + 1);
|
|
73
|
-
expressionErrors.push({
|
|
74
|
-
elementId: record.element,
|
|
75
|
-
prop: record.property ?? '?',
|
|
76
|
-
...(typeof record.code === 'string' && record.code ? { expression: clip(maskSecretText(record.code)) } : {}),
|
|
77
|
-
message: clip(maskSecretText(record.message ?? '')),
|
|
78
|
-
count: record.count ?? 1
|
|
79
|
-
});
|
|
80
|
-
}
|
|
81
|
-
return { flowErrors: records.filter(record => record.kind !== 'expression'), expressionErrors };
|
|
82
|
-
}
|
|
83
|
-
/** Runtime'ın konsol satırı (`@ng-apps/flow` `expressionErrorLine`) defterdeki bir kaydın kopyası mı? Çift sayılmasın. */
|
|
84
|
-
export function isExpressionEcho(expressionErrors, text) {
|
|
85
|
-
return expressionErrors.some(record => text.startsWith(`[ng-apps] expression ${record.elementId}.${record.prop}:`));
|
|
86
|
-
}
|
|
87
|
-
export function expressionErrorsNote(expressionErrors) {
|
|
88
|
-
const names = expressionErrors.map(record => `${record.elementId}.${record.prop}${record.count > 1 ? ` (${record.count}×)` : ''}`);
|
|
89
|
-
return `${expressionErrors.length} ifade hatası: ${names.join(', ')} — bu prop'lar çizimde boş/varsayılan değerde kaldı ("Expression error" toast'ının nedeni). expression + message hangi bağlamanın neden patladığını söyler; ifadeyi page_schema_patch ile düzeltin (ör. null korumalı ?.).`;
|
|
90
|
-
}
|
|
91
|
-
const failureLimit = 3;
|
|
92
|
-
/**
|
|
93
|
-
* Çizilemeyen sayfanın hata metnine eklenen nedenler: çöken düğümler, yakalanmamış istisnalar, konsol
|
|
94
|
-
* hataları — bu sırayla, her birinden en çok üç; uyarılar eklenmez. Hiçbiri yoksa boş. Yalnız ilk neden
|
|
95
|
-
* yazılırken sayfa istisnası React'in bileşeni adlandıran konsol satırını gizliyordu.
|
|
96
|
-
*/
|
|
97
|
-
export function describePageFailure(log, renderErrors) {
|
|
98
|
-
const crashed = renderErrors.slice(0, failureLimit).map(record => `${record.elementId ?? '?'} — ${record.message}`);
|
|
99
|
-
const thrown = log.pageErrors().slice(0, failureLimit).map(record => `${record.message}${record.stack ? ` (${record.stack.split('\n')[0]})` : ''}`);
|
|
100
|
-
const logged = log.consoleErrors().filter(record => record.level === 'error').slice(0, failureLimit).map(record => record.text);
|
|
101
|
-
return [
|
|
102
|
-
crashed.length ? ` Çöken düğüm: ${crashed.join('; ')}.` : '',
|
|
103
|
-
thrown.length ? ` Sayfa istisnası: ${thrown.join('; ')}.` : '',
|
|
104
|
-
logged.length ? ` Konsol hatası: ${logged.join('; ')}.` : ''
|
|
105
|
-
].join('');
|
|
106
|
-
}
|
|
107
|
-
/**
|
|
108
|
-
* Mobil çizicinin hata sınırlarının bıraktığı işaretler (`data-ng-render-error`). Düğümün dış sınırı
|
|
109
|
-
* (kancaları çöken düğüm) yer tutucuyu kendi kabuğunun dışında çizer ve kimliği işaretin üstünde taşır
|
|
110
|
-
* (`data-ng-render-error-id`); taşımıyorsa en yakın kabuk düğümdür.
|
|
111
|
-
*/
|
|
112
|
-
export const renderErrorsScript = "Array.from(document.querySelectorAll('[data-ng-render-error]')).slice(0, 20).map(function (el) { var own = el.getAttribute('data-ng-render-error-id'); var owner = own ? null : el.closest('[data-element-id]'); return { elementId: own || (owner ? owner.getAttribute('data-element-id') : undefined), message: el.getAttribute('data-ng-render-error') || '' }; })";
|
|
113
|
-
export function parseRenderErrors(value) {
|
|
114
|
-
if (!Array.isArray(value))
|
|
115
|
-
return [];
|
|
116
|
-
return value
|
|
117
|
-
.filter((entry) => entry !== null && typeof entry === 'object')
|
|
118
|
-
.map(entry => ({
|
|
119
|
-
...(typeof entry.elementId === 'string' ? { elementId: entry.elementId } : {}),
|
|
120
|
-
message: clip(maskSecretText(typeof entry.message === 'string' ? entry.message : ''))
|
|
121
|
-
}));
|
|
122
|
-
}
|
|
@@ -1,11 +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';
|
|
11
|
-
export * from './console-log.js';
|