@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.
Files changed (205) 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 +233 -568
  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 -366
  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/middleware-authenticate.json +1 -1
  30. package/dist/resources/examples/page-detail-profile.json +1 -2
  31. package/dist/resources/examples/page-dynamic-form.json +2 -3
  32. package/dist/resources/examples/page-flex-sections.json +1 -1
  33. package/dist/resources/examples/page-html-embed.json +1 -1
  34. package/dist/resources/examples/workflow-basic.json +2 -8
  35. package/dist/resources/guides/component.md +5 -92
  36. package/dist/resources/guides/entity.md +8 -50
  37. package/dist/resources/guides/expressions.md +5 -14
  38. package/dist/resources/guides/functions.md +1 -1
  39. package/dist/resources/guides/getting-started.md +1 -38
  40. package/dist/resources/guides/layout.md +58 -55
  41. package/dist/resources/guides/merge.md +3 -37
  42. package/dist/resources/guides/microservice.md +2 -87
  43. package/dist/resources/guides/middleware.md +4 -68
  44. package/dist/resources/guides/page.md +75 -456
  45. package/dist/resources/guides/theme.md +2 -8
  46. package/dist/resources/guides/translation.md +1 -1
  47. package/dist/resources/guides/workflow.md +1 -58
  48. package/dist/resources/rules.js +5 -11
  49. package/dist/resources/schema-fingerprint.json +5 -7
  50. package/dist/resources/schemas/CreateEntityCommand.json +0 -118
  51. package/dist/resources/schemas/CreateMicroserviceCommand.json +1 -13
  52. package/dist/resources/schemas/CreateQueryCommand.json +0 -4
  53. package/dist/resources/schemas/CreateWorkflowCommand.json +1 -146
  54. package/dist/resources/schemas/DataJobDto.json +1 -6
  55. package/dist/resources/schemas/GlobalFunctionUpdateResultDto.json +1 -21
  56. package/dist/resources/schemas/MicroserviceDto.json +3 -15
  57. package/dist/resources/schemas/PreviewMicroserviceCommand.json +1 -10
  58. package/dist/resources/schemas/PreviewWorkflowCommand.json +1 -25
  59. package/dist/resources/schemas/StepDto.json +1 -25
  60. package/dist/resources/schemas/UpdateEntityCommand.json +0 -118
  61. package/dist/resources/schemas/UpdateMicroserviceCommand.json +1 -13
  62. package/dist/resources/schemas/UpdateQueryCommand.json +0 -4
  63. package/dist/resources/schemas/UpdateViewCommand.json +2 -0
  64. package/dist/resources/schemas/UpdateWorkflowCommand.json +1 -146
  65. package/dist/server.js +1 -27
  66. package/dist/tools/access.js +2 -369
  67. package/dist/tools/applications.js +3 -60
  68. package/dist/tools/common.js +0 -7
  69. package/dist/tools/components.js +14 -118
  70. package/dist/tools/datasources.js +3 -3
  71. package/dist/tools/dependencies.js +1 -1
  72. package/dist/tools/deployments.js +3 -38
  73. package/dist/tools/designSchemaPatch.js +18 -34
  74. package/dist/tools/dtos.js +5 -36
  75. package/dist/tools/entities.js +12 -79
  76. package/dist/tools/externalComponents.js +5 -26
  77. package/dist/tools/flows.js +14 -122
  78. package/dist/tools/folders.js +1 -1
  79. package/dist/tools/imports.js +2 -5
  80. package/dist/tools/layouts.js +17 -50
  81. package/dist/tools/merges.js +2 -2
  82. package/dist/tools/microservices.js +25 -77
  83. package/dist/tools/pages.js +112 -465
  84. package/dist/tools/platformInfo.js +8 -27
  85. package/dist/tools/projects.js +9 -71
  86. package/dist/tools/rows.js +13 -69
  87. package/dist/tools/schemaOps.js +1 -61
  88. package/dist/tools/screenWrite.js +8 -37
  89. package/dist/tools/screens.js +5 -95
  90. package/dist/tools/templates.js +1 -26
  91. package/dist/tools/translations.js +1 -1
  92. package/dist/tools/variables.js +5 -11
  93. package/dist/tools/workflows.js +5 -10
  94. package/package.json +1 -1
  95. package/dist/knowledge/access.js +0 -802
  96. package/dist/knowledge/case.js +0 -41
  97. package/dist/knowledge/connectDataBind.js +0 -819
  98. package/dist/knowledge/connectDataDefinition.js +0 -341
  99. package/dist/knowledge/connectDataExecute.js +0 -93
  100. package/dist/knowledge/connectDataRules.js +0 -128
  101. package/dist/knowledge/decision.js +0 -121
  102. package/dist/knowledge/expressionPrelude.js +0 -59
  103. package/dist/knowledge/microserviceParameters.js +0 -58
  104. package/dist/knowledge/mining.js +0 -94
  105. package/dist/knowledge/mobileComponentCatalog.js +0 -859
  106. package/dist/knowledge/mobileComponentEvents.js +0 -239
  107. package/dist/knowledge/platform/lint.js +0 -385
  108. package/dist/knowledge/platform/mobile.js +0 -142
  109. package/dist/knowledge/platform/mobileLayoutLint.js +0 -324
  110. package/dist/knowledge/platform/mobileRules.js +0 -231
  111. package/dist/knowledge/platform/mobileSpacing.js +0 -82
  112. package/dist/knowledge/platform/mobileTabs.js +0 -108
  113. package/dist/knowledge/platform/nativeBoxModel.js +0 -44
  114. package/dist/knowledge/platform/profile.js +0 -25
  115. package/dist/knowledge/platform/resolve.js +0 -122
  116. package/dist/knowledge/platform/web.js +0 -39
  117. package/dist/knowledge/process.js +0 -510
  118. package/dist/knowledge/rowFilter.js +0 -43
  119. package/dist/knowledge/styleResolveNative.js +0 -527
  120. package/dist/platform/argumentAliases.js +0 -128
  121. package/dist/platform/bodyFile.js +0 -103
  122. package/dist/platform/capture/actions.js +0 -333
  123. package/dist/platform/capture/browser.js +0 -1
  124. package/dist/platform/capture/console-log.js +0 -122
  125. package/dist/platform/capture/index.js +0 -11
  126. package/dist/platform/capture/readiness.js +0 -103
  127. package/dist/platform/capture/summary.js +0 -77
  128. package/dist/platform/capture/target.js +0 -5
  129. package/dist/platform/capture/targets/studio-mobile.js +0 -15
  130. package/dist/platform/capture/targets/studio-web.js +0 -5
  131. package/dist/platform/impersonation.js +0 -45
  132. package/dist/platform/jsonArray.js +0 -24
  133. package/dist/platform/schemaReadMemory.js +0 -60
  134. package/dist/resources/examples/bpm-gorev-kontrol.json +0 -294
  135. package/dist/resources/examples/bpm-iso-dokuman.json +0 -228
  136. package/dist/resources/examples/bpm-satinalma.json +0 -376
  137. package/dist/resources/examples/connect-data-convert-verify.json +0 -107
  138. package/dist/resources/examples/connect-data-dropdown-roles.json +0 -66
  139. package/dist/resources/examples/connect-data-edit-m2m.json +0 -73
  140. package/dist/resources/examples/connect-data-query-params.json +0 -50
  141. package/dist/resources/examples/connect-data-table-entity.json +0 -147
  142. package/dist/resources/examples/connect-data-workflow-microservice.json +0 -65
  143. package/dist/resources/examples/mobile-app-shell-drawer.json +0 -243
  144. package/dist/resources/examples/mobile-app-shell-stack.json +0 -157
  145. package/dist/resources/examples/mobile-app-shell-tab.json +0 -208
  146. package/dist/resources/examples/mobile-form-screen.json +0 -365
  147. package/dist/resources/examples/mobile-list-detail-form.json +0 -672
  148. package/dist/resources/guides/access.md +0 -810
  149. package/dist/resources/guides/bpm.md +0 -691
  150. package/dist/resources/guides/case.md +0 -153
  151. package/dist/resources/guides/decision.md +0 -185
  152. package/dist/resources/guides/mobile-layout.md +0 -193
  153. package/dist/resources/guides/mobile.md +0 -495
  154. package/dist/resources/read.js +0 -41
  155. package/dist/resources/schemas/AccessExplanationDto.json +0 -144
  156. package/dist/resources/schemas/AccessMatrixDto.json +0 -170
  157. package/dist/resources/schemas/AccessPermissionDto.json +0 -57
  158. package/dist/resources/schemas/AccessPolicyDto.json +0 -223
  159. package/dist/resources/schemas/AccessResourceDto.json +0 -86
  160. package/dist/resources/schemas/AppSessionDto.json +0 -53
  161. package/dist/resources/schemas/ConnectDataDescribeDto.json +0 -344
  162. package/dist/resources/schemas/ConnectDataRequestModel.json +0 -202
  163. package/dist/resources/schemas/ConnectDataResultDto.json +0 -421
  164. package/dist/resources/schemas/ConnectorCredentialDto.json +0 -70
  165. package/dist/resources/schemas/ConvertConnectDataToMicroserviceCommand.json +0 -37
  166. package/dist/resources/schemas/CreateAccessPolicyCommand.json +0 -209
  167. package/dist/resources/schemas/CreateDecisionCommand.json +0 -221
  168. package/dist/resources/schemas/CreateProcessDefinitionCommand.json +0 -1337
  169. package/dist/resources/schemas/DecisionDto.json +0 -302
  170. package/dist/resources/schemas/DetachConnectDataMicroserviceCommand.json +0 -28
  171. package/dist/resources/schemas/ExplainAccessQuery.json +0 -33
  172. package/dist/resources/schemas/GenerateProcessDocumentCommand.json +0 -116
  173. package/dist/resources/schemas/OrganizationProjectDto.json +0 -335
  174. package/dist/resources/schemas/OrganizationSchemaDto.json +0 -196
  175. package/dist/resources/schemas/OrganizationSchemaVersionDto.json +0 -207
  176. package/dist/resources/schemas/PatchProcessDefinitionCommand.json +0 -76
  177. package/dist/resources/schemas/ProcessDefinitionDto.json +0 -1480
  178. package/dist/resources/schemas/ProcessSimulationDto.json +0 -96
  179. package/dist/resources/schemas/SaveConnectorCredentialCommand.json +0 -90
  180. package/dist/resources/schemas/SaveProcessEventSourceCommand.json +0 -61
  181. package/dist/resources/schemas/SaveProcessMailSourceCommand.json +0 -89
  182. package/dist/resources/schemas/SignProcessTaskCommand.json +0 -73
  183. package/dist/resources/schemas/SimulateProcessDefinitionQuery.json +0 -92
  184. package/dist/resources/schemas/UpdateAccessPolicyCommand.json +0 -209
  185. package/dist/resources/schemas/UpdateDecisionCommand.json +0 -216
  186. package/dist/resources/schemas/UpdateProcessDefinitionCommand.json +0 -1357
  187. package/dist/resources/schemas/ValidateProcessDefinitionQuery.json +0 -1339
  188. package/dist/tools/accessOperations.js +0 -135
  189. package/dist/tools/accessPolicies.js +0 -170
  190. package/dist/tools/accessRuntime.js +0 -203
  191. package/dist/tools/bpm.js +0 -343
  192. package/dist/tools/bpmGovernance.js +0 -266
  193. package/dist/tools/bpmOperations.js +0 -230
  194. package/dist/tools/bpmReporting.js +0 -154
  195. package/dist/tools/bpmRouting.js +0 -150
  196. package/dist/tools/bpmRuntime.js +0 -396
  197. package/dist/tools/case.js +0 -178
  198. package/dist/tools/caseRuntime.js +0 -89
  199. package/dist/tools/connectData.js +0 -449
  200. package/dist/tools/connectors.js +0 -193
  201. package/dist/tools/decisions.js +0 -206
  202. package/dist/tools/mobile.js +0 -221
  203. package/dist/tools/modules.js +0 -48
  204. package/dist/tools/organizationStructure.js +0 -137
  205. package/dist/tools/profileWrite.js +0 -92
@@ -1,142 +0,0 @@
1
- import { ENGINE_STEP_TYPES, STATE_STORAGE_KINDS, STEP_ALIASES } from './profile.js';
2
- import { PAGE_CONTAINER_ELEMENT_ID } from '../normalize.js';
3
- /**
4
- * Mobil uygulamanın kök kapsayıcısı. Web'in `pageContainerNode()`'undan üç farkı var:
5
- *
6
- * - `layout` prop'u YOK: mobil `container` şeması onu düşürür (`@ng-apps/ui-native`
7
- * `components/container/schema.ts` `omitProps(['layout', …])`) — mobil kanvas yalnız flex akışıdır
8
- * ve backend doğrulayıcısı mobil kataloğa bakar, yazılan `layout` bilinmeyen prop olurdu.
9
- * - `direction: 'column'`, `wrap: false`: telefon ekranı dikey akar; web kökü satır + sarma ile
10
- * 12 sütunu taklit eder.
11
- * - `blocks: []` ve `heightType: 'fill'`: çizer blokları yok sayar (D3), kök ekranı doldurur.
12
- *
13
- * Mizanpaj sözleşmesi (plans/mobil-mizanpaj-dili 02): kök `size: shrink` (S-16: `grow` çocukları ekran
14
- * boyunca eşit paya böler) ve `horizontalAlign: stretch` (Studio şablonu). Şablon kabuklu sayfanındır:
15
- * üst güvenli alan ve 16 dp yatay kenar boşluğu kabuğun işidir (`safe-area top` + slot margin'i). Kabuksuz
16
- * sayfayı `shellLessPageSkeleton` tamamlar — sayfanın layout'u ancak yazımda kesinleşir.
17
- */
18
- export function mobileRootContainer() {
19
- return {
20
- elementId: PAGE_CONTAINER_ELEMENT_ID,
21
- kind: 'component',
22
- name: PAGE_CONTAINER_ELEMENT_ID,
23
- type: 'container',
24
- props: {
25
- title: { type: 'static', data: '' },
26
- direction: { type: 'static', data: 'column' },
27
- horizontalAlign: { type: 'static', data: 'stretch' },
28
- verticalAlign: { type: 'static', data: 'top' },
29
- size: { type: 'static', data: 'shrink' },
30
- gap: { type: 'static', data: 'medium' },
31
- wrap: { type: 'static', data: false }
32
- },
33
- blocks: [],
34
- states: { isSticky: false, isDroppable: true, isScrollable: true, isWrapper: true },
35
- appearance: { heightType: 'fill', marginValue: 0, marginType: 'none', paddingType: 'none', paddingValue: 0 },
36
- events: [],
37
- validations: [],
38
- stateBindings: [],
39
- children: []
40
- };
41
- }
42
- /**
43
- * Kabuksuz sayfanın iskeleti (S1/S4): kök yatay 16 padding taşır — `isWrapper` yalnız margin'i sıfırlar,
44
- * kökün padding'i çizilir (01'de ölçüldü) — ve ilk çizilen öğe üst güvenli alan değilse tepeye
45
- * `safe_area_top` konur. Gateway'in kurduğu köke uygulanır; notu döner.
46
- */
47
- export function shellLessPageSkeleton(root) {
48
- const children = Array.isArray(root.children) ? root.children : [];
49
- const addsSafeArea = topSafeAreaMissing(children);
50
- root.appearance = { ...root.appearance, paddingType: 'custom', paddingValues: { top: 0, right: 16, bottom: 0, left: 16 } };
51
- root.children = addsSafeArea ? [topSafeArea(), ...children] : children;
52
- return [`mobil kabuksuz sayfa: köke yatay 16 padding yazıldı${addsSafeArea ? " ve tepeye üst güvenli alan (safe_area_top, edges:'top') eklendi" : ''} — ekran kenarı boşluğu ve durum çubuğu payı (mizanpaj S1/S4). Kabuklu sayfada ikisi kabuğun işidir: layoutId verin`];
53
- }
54
- /** Ekrana sabit katmanlar akışta değildir; üst payı onlar vermez (`topSafeAreaMissing`, Studio kanvası). */
55
- export const OVERLAY_TYPES = ['drawer', 'fab', 'floating-container', 'modal', 'toast'];
56
- /** `safe-area`'nın `edges`'i; yazılmamışsa `all` (Studio `KNOWN_SAFE_EDGES`). */
57
- export function safeAreaEdges(node) {
58
- const edges = node.props?.edges;
59
- const value = edges && typeof edges === 'object' ? edges.data : edges;
60
- return typeof value === 'string' && value !== '' ? value : 'all';
61
- }
62
- /** Akışta çizilen ilk öğe (kaplama katmanları ve yüzen düğümler atlanır). */
63
- export function firstDrawn(nodes) {
64
- return nodes.find(node => !OVERLAY_TYPES.includes(String(node.type ?? '')) && node.states?.isFloating !== true);
65
- }
66
- /**
67
- * İlk çizilen öğe üst payı veriyor mu — Studio'nun `topSafeAreaMissing`'iyle aynı anlam
68
- * (`apps/studio/src/features/mobile/mobile-native-canvas.functions.ts`): `edges` yazılmamışsa `all`dır,
69
- * yalnız `bottom` üst payı vermez; ilk öğe çocuklu bir kapsa onun ilk öğesine bakılır. Boş sayfa da pay alır.
70
- */
71
- export function topSafeAreaMissing(nodes, mode = 'skeleton') {
72
- const first = firstDrawn(nodes);
73
- if (!first)
74
- return true;
75
- if (first.type === 'safe-area')
76
- return safeAreaEdges(first) === 'bottom';
77
- const children = Array.isArray(first.children) ? first.children : [];
78
- if (children.length > 0)
79
- return topSafeAreaMissing(children, mode);
80
- // Studio: boş kap hiçbir şey çizmez, korunacak içerik de yoktur — uyarı yok. İskelet ise payı yine kurar.
81
- return mode === 'studio' ? first.states?.isDroppable !== true : true;
82
- }
83
- /** Studio'nun yeni sayfa şablonundaki payla aynı (`new-page-schema.functions.ts`). */
84
- function topSafeArea() {
85
- return {
86
- elementId: 'safe_area_top',
87
- kind: 'component',
88
- name: 'safe_area_top',
89
- type: 'safe-area',
90
- props: { edges: { type: 'static', data: 'top' } },
91
- blocks: [],
92
- states: { isSticky: false, isDroppable: true, isScrollable: false, isWrapper: false },
93
- appearance: { heightType: 'auto', widthType: 'fill' },
94
- events: [],
95
- validations: [],
96
- stateBindings: [],
97
- children: []
98
- };
99
- }
100
- /**
101
- * Mobil profil = taşınabilir alt küme (`plans/mobil-uygulama-expo/01-mimari-tasarim.md` §5, §7).
102
- * Yazım platforma YİNE gider (D3: şema aynı); alt kümenin dışı `MOB-*` bulgusu olarak uyarılır.
103
- */
104
- export const mobileProfile = Object.freeze({
105
- platform: 'mobile',
106
- catalogType: 'mobile',
107
- layout: {
108
- modes: new Set(['flex']),
109
- blocksMeaningful: false,
110
- rootContainer: mobileRootContainer,
111
- orderBy: 'array-index'
112
- },
113
- appearance: {
114
- ignoredKeys: new Set(['className', 'tooltip', 'tooltipPosition', 'zIndex']),
115
- sizeUnits: new Set(['px', '%'])
116
- },
117
- style: { freeStyle: false, hoverStyle: false, resolver: 'native' },
118
- steps: {
119
- registered: new Set([...ENGINE_STEP_TYPES, 'alert', 'confirm', 'redirect', 'navigate', 'storage', 'setcookie', 'download', 'share', 'haptic', 'location', 'notification-permission', 'notification-schedule', 'notification-cancel']),
120
- aliases: STEP_ALIASES,
121
- infoOnly: new Set(['download', 'setcookie'])
122
- },
123
- shell: {
124
- components: new Set(['app-bar']),
125
- suggestions: {
126
- navigations: "sekmeler: application_update ile properties.mobile.tabs (Ayarlar › Sekmeler); üst çubuk için 'app-bar'",
127
- sidebar: "çekmece: 'drawer' widget'ı + app-bar.leading:'menu' ve menuTarget",
128
- header: "'app-bar' (başlık, leading: auto | none | back | menu, sağ eylemler)"
129
- },
130
- legacyShellNodes: new Set(['tab-bar', 'drawer-menu'])
131
- },
132
- externalComponents: false,
133
- state: { storageKinds: new Set(STATE_STORAGE_KINDS) },
134
- capture: {
135
- target: 'studio-mobile',
136
- devices: {
137
- phone: { width: 390, height: 844, dpr: 2 },
138
- tablet: { width: 820, height: 1180, dpr: 2 }
139
- },
140
- defaultDevice: 'phone'
141
- }
142
- });
@@ -1,324 +0,0 @@
1
- import { MOBILE_DROPPABLE_TYPES, MOBILE_OUT_OF_FLOW_TYPES, MOBILE_TRANSPARENT_WRAPPER_TYPES } from '../mobileComponentCatalog.js';
2
- import { PAGE_CONTAINER_ELEMENT_ID } from '../normalize.js';
3
- import { OVERLAY_TYPES, firstDrawn, safeAreaEdges, topSafeAreaMissing } from './mobile.js';
4
- import { MOBILE_RULES } from './mobileRules.js';
5
- import { FULL_WIDTH_TYPES, nativeMarginLeft, nativePaddingLeft } from './nativeBoxModel.js';
6
- /**
7
- * Boş görünmesi hata olmayan bırakılabilir tipler: içeriği çalışma anında gelir (`widget-area`, `slot`), öğesini
8
- * prop'tan çizer (`carousel` items, `tabs` başlığı, `grouped-list` satırları) ya da akış dışı bir katmandır.
9
- */
10
- const EMPTY_ALLOWED_TYPES = ['widget-area', 'slot', 'carousel', 'tabs', 'grouped-list', ...OVERLAY_TYPES];
11
- export function describeShell(views) {
12
- const root = asNodes(views)[0];
13
- const children = asNodes(root?.children);
14
- return { kind: 'shell', topInset: children.length > 0 && !topSafeAreaMissing(children, 'studio'), appBar: someNode(asNodes(views), node => node.type === 'app-bar') };
15
- }
16
- /**
17
- * MOB-015…021 — mizanpaj sözleşmesinin iskelet (S1/S3/S12/S13) ve hiza/ölçek (S5/S6/S8/S9/S11) maddeleri (plans/mobil-mizanpaj-dili 03, S1/S3/S12/S13).
18
- * Ağacın tamamına bakar; yama yolunda yalnız yazımın getirdiği bulgular raporlanır (`newFindings`).
19
- */
20
- export function mobileLayoutFindings(input) {
21
- const roots = asNodes(input.tree);
22
- const screenRoot = input.kind === 'page' ? roots.find(node => node.elementId === PAGE_CONTAINER_ELEMENT_ID) : roots[0];
23
- const screen = asNodes(screenRoot?.children);
24
- const shell = input.kind === 'page' ? input.shell ?? { kind: 'none' } : { kind: 'none' };
25
- const findings = [];
26
- if (shell.kind === 'unknown') {
27
- findings.push({ code: MOBILE_RULES['MOB-015'].id, severity: 'info', message: 'sayfanın kabuğu (layout) okunamadı — üst pay ve app-bar kuralları (MOB-015, MOB-017) bu yazımda denetlenmedi' });
28
- }
29
- // MOB-015 — üst güvenli alan tek yerde.
30
- if (input.kind === 'layout' && screen.length > 0 && topSafeAreaMissing(screen, 'studio')) {
31
- findings.push(rule('MOB-015', label(screenRoot), "kabuğun ilk çizilen öğesi üst güvenli alan değil: app-bar ve sayfalar durum çubuğunun/çentiğin altına girer. app-bar'ı `safe-area` (edges: 'top') içine alın — sayfalar ikinci pay vermez"));
32
- }
33
- if (input.kind === 'page' && shell.kind === 'none' && screen.length > 0 && topSafeAreaMissing(screen, 'studio')) {
34
- findings.push(rule('MOB-015', label(screenRoot), "kabuksuz sayfanın ilk çizilen öğesi üst güvenli alan değil: içerik durum çubuğunun/çentiğin altına girer. Tepeye `safe-area` (edges: 'top') koyun ya da sayfayı üst payı veren bir kabuğa bağlayın (layoutId)"));
35
- }
36
- if (shell.kind === 'shell' && !shell.topInset) {
37
- findings.push(rule('MOB-015', 'layout', "sayfanın kabuğu üst payı vermiyor: kabukta app-bar'ı `safe-area` (edges: 'top') içine alın (layout_schema_patch); payı sayfaya koymayın, her sayfada tekrarlanır"));
38
- }
39
- if (shell.kind === 'shell' && shell.topInset) {
40
- // Katman (modal, drawer) kendi panelinde payı ayrıca çözer; ekranın akışındaki güvenli alanlar sayılır.
41
- for (const node of collect(roots, node => node.type === 'safe-area' && safeAreaEdges(node) !== 'bottom', OVERLAY_TYPES)) {
42
- findings.push(rule('MOB-015', label(node), `kabuk üst payı zaten veriyor; sayfadaki '${safeAreaEdges(node)}' güvenli alanı ikinci pay bırakır (içerik app-bar'dan kopar). Kaldırın; alt pay gerekiyorsa edges: 'bottom'`));
43
- }
44
- }
45
- // MOB-016 — tek kaydırıcı: kök kaydırır ve içerik boyundadır.
46
- if (screenRoot && propData(screenRoot, 'size') === 'grow') {
47
- findings.push(rule('MOB-016', label(screenRoot), "kök kap `size: 'grow'`: çocuklar ekran boyunu eşit paylaşır, kısa içerik gerilir ya da üst üste biner (koşu 37 S-6/S-16). replace_prop size: 'shrink'"));
48
- }
49
- walkWithAncestors(roots, (node, ancestors) => {
50
- if (node.type !== 'scroll-view' || setOrBound(node, 'horizontal'))
51
- return;
52
- const fixed = appearanceOf(node).heightType === 'fixed';
53
- if (ancestors.some(item => item.type === 'scroll-view' && !setOrBound(item, 'horizontal')) && !fixed) {
54
- findings.push(rule('MOB-016', label(node), "dikey kaydırıcı başka bir dikey kaydırıcının içinde: iç kaydırma jesti dışarıdakiyle çakışır. İçtekine sabit boy (heightType: 'fixed') verin ya da kaldırın"));
55
- }
56
- else if (!fixed && !setOrBound(node, 'refreshable') && !setOrBound(node, 'stickyHeader') && !asNodes(node.children).some(child => child.states?.isSticky === true)) {
57
- findings.push(rule('MOB-016', label(node), "gerekçesiz dikey `scroll-view`: sayfa kökü zaten kaydırır. Kaldırın ya da gerekçesini verin — refreshable (çekerek yenile), stickyHeader / states.isSticky (yapışkan başlık), heightType: 'fixed' (sabit boylu iç liste)"));
58
- }
59
- });
60
- // MOB-017 — ekranda tek app-bar, ilk çizilen öğe.
61
- const appBars = collect(roots, node => node.type === 'app-bar', OVERLAY_TYPES);
62
- if (shell.kind === 'shell' && shell.appBar) {
63
- for (const node of appBars)
64
- findings.push(rule('MOB-017', label(node), 'kabuk app-bar veriyor; sayfadaki ikinci app-bar ekranda çift başlık çubuğu çizer. Kaldırın; sayfa başlığını kabuğun app-bar\'ına bağlayın (title)'));
65
- }
66
- else if (shell.kind !== 'unknown' && appBars.length > 0) {
67
- // Tepedeki app-bar: ilk çizilen öğenin kendisi ya da onun içindeki ilk app-bar (safe-area içi, saydam
68
- // çubuk tarifi `stack` → görsel + kap → app-bar).
69
- // Boş `safe-area` (kabuksuz sayfanın pay boşluğu) çizim yapmaz; tepedeki ilk içerikli öğeye bakılır.
70
- const first = firstDrawn(screen.filter(item => !(item.type === 'safe-area' && asNodes(item.children).length === 0)));
71
- const leading = first ? collect([first], item => item.type === 'app-bar', OVERLAY_TYPES)[0] : undefined;
72
- for (const node of appBars.filter(item => item !== leading || leading?.type !== 'app-bar')) {
73
- findings.push(rule('MOB-017', label(node), appBars.length > 1 && node !== appBars[0] ? 'ekranda ikinci app-bar: tek başlık çubuğu olmalı, kaldırın' : 'app-bar ilk çizilen öğe değil: başlık çubuğu ekranın tepesinde durur. Onu ilk sıraya (ya da tepedeki safe-area\'nın içine) taşıyın'));
74
- }
75
- }
76
- // MOB-018 — boş kap bırakılmaz (tepedeki/dipteki güvenli alan bilinçli bir paydır).
77
- walkWithAncestors(screen, (node, ancestors) => {
78
- const type = String(node.type ?? '');
79
- if (!MOBILE_DROPPABLE_TYPES.includes(type) || EMPTY_ALLOWED_TYPES.includes(type) || node.kind === 'widget')
80
- return;
81
- if (asNodes(node.children).length > 0 || boundVisibility(node))
82
- return;
83
- if (type === 'safe-area' && ancestors.length === 0)
84
- return;
85
- findings.push({ ...rule('MOB-018', label(node), `boş ${type}: ekranda boş şerit bırakır${type === 'safe-area' ? ` (edges: '${safeAreaEdges(node)}' payı kadar)` : ''}. İçeriğini ekleyin ya da silin`), severity: 'info' });
86
- });
87
- if (screenRoot)
88
- findings.push(...alignmentFindings(screenRoot, input.kind), ...widthFindings(screenRoot), ...scaleFindings(screenRoot));
89
- return findings;
90
- }
91
- /** Sütunda dizilen içerik (form alanları, düğmeler); satırda yan yana olmaları hata değildir. */
92
- const FIELD_TYPES = ['textinput', 'textarea', 'password', 'numberinput', 'dropdown', 'multi-select', 'datepicker', 'daterangepicker', 'datetimepicker', 'timepicker', 'fileInput', 'slider'];
93
- const BUTTON_TYPES = ['button', 'filebutton'];
94
- /** Metin gibi görünen düğmeler içerik genişliğinde durur (S8'in istisnası). */
95
- const INLINE_BUTTON_VARIANTS = ['text', 'link', 'ghost'];
96
- /** Sütun akışını taşıyan kap tipleri ve yazılmamış yönleri (`containerDirection`: safe-area/scroll-view sütun doğar). */
97
- const FLOW_TYPES = { container: 'row', form: 'row', 'safe-area': 'column', 'scroll-view': 'column' };
98
- const SPACING_SCALE = [0, 4, 8, 16, 24, 32];
99
- const RADIUS_SCALE = ['none', 'sm', 'md', 'lg', 'pill', '0', 0];
100
- /**
101
- * MOB-019 — kenar değişmezi (S6/S8): aynı kapsamda (ekran ya da yüzey kabı) sütun akışındaki öğelerin sol
102
- * kenarı eşittir. Zeminsiz kap ve saydam sarmalayıcı (tooltip) içeriğini kendi margin + padding'i kadar
103
- * içeri iter; yüzey kabı yeni kapsam açar, içeriği bilerek içeride durur. Hesap runtime'ın kutu modeliyle
104
- * (`nativeBoxModel.ts`), 01'in çizimde ölçtüğü sapmanın şemadan öngörüsüdür.
105
- */
106
- function alignmentFindings(root, kind) {
107
- const findings = [];
108
- const scopes = [root];
109
- for (let index = 0; index < scopes.length; index++) {
110
- const owner = scopes[index];
111
- const items = [];
112
- collectFlowItems(asNodes(owner.children), 0, [], [], items, kind);
113
- for (const item of items)
114
- if (isSurface(item.node) && flowDirection(item.node) === 'column')
115
- scopes.push(item.node);
116
- // Ortalanmış ya da sağa hizalı kapsamda öğeler kendi genişliğince yerleşir; sol kenar eşitliği aranmaz.
117
- if (!['center', 'right'].includes(String(propData(owner, 'horizontalAlign') ?? 'left')))
118
- findings.push(...insetFindings(items));
119
- }
120
- return findings;
121
- }
122
- function collectFlowItems(nodes, inset, chain, shifts, items, kind) {
123
- for (const node of nodes) {
124
- const type = String(node.type ?? '');
125
- if (MOBILE_OUT_OF_FLOW_TYPES.includes(type) || type === 'slot' || propData(node, 'isHidden') === true)
126
- continue;
127
- const margin = nativeMarginLeft(node);
128
- if (transparent(node)) {
129
- const shift = margin + nativePaddingLeft(node);
130
- collectFlowItems(asNodes(node.children), inset + shift, [...chain, node], [...shifts, shift], items, kind);
131
- }
132
- else {
133
- items.push({ node, inset: inset + margin, chain, shifts });
134
- }
135
- }
136
- }
137
- /** İçeriğini sütunda, sola ya da gerilerek dizen zeminsiz kap — kendi kutusu görünmez, içeriği görünür. */
138
- function transparent(node) {
139
- const type = String(node.type ?? '');
140
- if (MOBILE_TRANSPARENT_WRAPPER_TYPES.includes(type))
141
- return true;
142
- if (!FLOW_TYPES[type] || isSurface(node))
143
- return false;
144
- return flowDirection(node) === 'column' && !['center', 'right'].includes(String(propData(node, 'horizontalAlign') ?? 'left'));
145
- }
146
- /**
147
- * Taban, kapsamın kendi sütunundaki (en sığ) öğelerdir — çoğunluk değil. Böylece "başlık + başlıklı grup"
148
- * düzeninde suçlu grubu içeri iten kaptır, başlık değil; gruba alan eklemek tabanı el değiştirmez.
149
- */
150
- function insetFindings(items) {
151
- if (items.length === 0)
152
- return [];
153
- const depth = Math.min(...items.map(item => item.chain.length));
154
- const counts = new Map();
155
- for (const item of items.filter(entry => entry.chain.length === depth))
156
- counts.set(item.inset, (counts.get(item.inset) ?? 0) + 1);
157
- const baseline = [...counts].sort((a, b) => b[1] - a[1] || a[0] - b[0])[0]?.[0] ?? 0;
158
- const reference = items.find(item => item.chain.length === depth && item.inset === baseline);
159
- const groups = new Map();
160
- for (const item of items.filter(entry => entry.inset !== baseline)) {
161
- // Suçlu: ortak zincirden sonra içeriği gerçekten iten ilk kap; hiçbiri itmiyorsa fark öğenin kendi margin'idir.
162
- const cause = item.chain.find((ancestor, index) => reference?.chain[index] !== ancestor && (item.shifts[index] ?? 0) !== 0) ?? item.node;
163
- groups.set(cause, [...(groups.get(cause) ?? []), item]);
164
- }
165
- return [...groups].map(([cause, members]) => {
166
- // Kabın kendi payı en az kayan üyededir; daha derindeki ek kayma iç kabın ayrı bulgusudur (kap düzelince görünür).
167
- const delta = members.map(member => member.inset - baseline).sort((a, b) => Math.abs(a) - Math.abs(b))[0] ?? 0;
168
- const where = `${Math.abs(delta)} dp ${delta > 0 ? 'içeride' : 'dışarıda'}`;
169
- const ids = members.slice(0, 4).map(member => label(member.node)).join(', ') + (members.length > 4 ? ', …' : '');
170
- if (members.length === 1 && cause === members[0]?.node) {
171
- return rule('MOB-019', label(cause), `kardeşlerinden ${where} başlıyor: kendi margin'i farklı. Aralık ebeveynin gap'inden gelsin — marginType'ı kardeşleriyle aynı yapın`);
172
- }
173
- const recipe = MOBILE_TRANSPARENT_WRAPPER_TYPES.includes(String(cause.type ?? ''))
174
- ? "paddingType:'none' ve marginType:'none' yazın"
175
- : "grup görünsün isteniyorsa yüzey verin (surfaceStyle zemin ya da kenarlık — içerik bilerek içeride durur), istenmiyorsa paddingType:'none' ve marginType:'none' yazın";
176
- return rule('MOB-019', label(cause), `${String(cause.type)} içeriği (${ids}) kardeşlerinden ${where} başlıyor — margin ve padding'i içeriği iter. ${recipe}`);
177
- });
178
- }
179
- /**
180
- * MOB-020 — genişlik (S8/S9): (1) sola hizalı sütunda tam genişlik sınıfındaki öğelerin bir kısmı `fill`, bir
181
- * kısmı değilse fill olmayanlar; (2) form alanı taşıyan satır kabı (yönü yazılmamış container/form satırdır).
182
- */
183
- function widthFindings(root) {
184
- const findings = [];
185
- walkWithAncestors([root], node => {
186
- const children = asNodes(node.children).filter(child => propData(child, 'isHidden') !== true);
187
- if (!FLOW_TYPES[String(node.type ?? '')])
188
- return;
189
- if (flowDirection(node) === 'row') {
190
- // Yazılmış `row` bilinçlidir (arama satırı: alan + düğme); yazılmamış yön unutulmuş sütundur.
191
- if (node !== root && propData(node, 'direction') === undefined && children.some(child => FIELD_TYPES.includes(String(child.type ?? '')))) {
192
- findings.push(rule('MOB-020', label(node), "yönü yazılmamış kap form alanı taşıyor — yazılmamış yön satırdır, alanlar yan yana sıkışır. direction:'column' ve horizontalAlign:'stretch' yazın"));
193
- }
194
- return;
195
- }
196
- if (propData(node, 'horizontalAlign') === 'stretch')
197
- return;
198
- const wide = children.filter(fullWidthClass);
199
- if (!wide.some(fills))
200
- return;
201
- for (const child of wide.filter(child => !fills(child))) {
202
- findings.push(rule('MOB-020', label(child), `kardeşleri tam genişlik (widthType: 'fill'), bu öğe değil: sağ kenarı hizadan kopar. widthType:'fill' yazın`));
203
- }
204
- });
205
- return findings;
206
- }
207
- /**
208
- * MOB-021 — ölçek (S5/S7/S11): özel boşluk ve sayısal gap ölçekte, yarıçap ölçek adıyla, yazı boyu seviyeyle.
209
- * Gap jetonları (`small`/`medium`/`large`) zaten ölçektir ve sözleşmenin ritmi üçünü de kullanır; sayılmaz.
210
- */
211
- function scaleFindings(root) {
212
- const findings = [];
213
- walkWithAncestors([root], node => {
214
- const appearance = appearanceOf(node);
215
- const offScale = ['margin', 'padding'].flatMap(layer => {
216
- if (appearance[`${layer}Type`] !== 'custom')
217
- return [];
218
- const values = appearance[`${layer}Values`];
219
- const sides = ['top', 'right', 'bottom', 'left'].map(side => values?.[side]);
220
- // `resolveSpacing`: tek değer yalnız yazılmamış kenarın yedeğidir.
221
- const written = [...sides, ...(sides.some(side => side === undefined || side === null) ? [appearance[`${layer}Value`]] : [])];
222
- const numbers = written.filter(value => value !== undefined && value !== null && value !== '').map(value => Number(String(value).replace(/px$/, '')));
223
- return [...new Set(numbers.filter(value => !SPACING_SCALE.includes(value)))].map(value => `${layer} ${value}`);
224
- });
225
- const radius = nested(node, 'surfaceStyle', 'radius');
226
- const fontSize = nested(node, 'typographyStyle', 'fontSize');
227
- const gap = propData(node, 'gap');
228
- const notes = [
229
- ...offScale,
230
- ...(radius !== undefined && !RADIUS_SCALE.includes(radius) && !/^var\(--ng-radius-/.test(String(radius)) ? [`radius ${String(radius)} (sm | md | lg | pill)`] : []),
231
- ...(typeof fontSize === 'number' || (typeof fontSize === 'string' && /^\d+(\.\d+)?(px)?$/.test(fontSize.trim())) ? [`fontSize ${String(fontSize)} (text seviyesi)`] : []),
232
- ...(typeof gap === 'number' && !SPACING_SCALE.includes(gap) ? [`gap ${gap}`] : [])
233
- ];
234
- if (notes.length > 0)
235
- findings.push({ ...rule('MOB-021', label(node), `ölçek dışı değer: ${notes.join(', ')} — boşluk 0/4/8/16/24/32, yarıçap ve yazı temadan gelir`), severity: 'info' });
236
- });
237
- return findings;
238
- }
239
- function fullWidthClass(node) {
240
- const type = String(node.type ?? '');
241
- if (FIELD_TYPES.includes(type))
242
- return true;
243
- if (!BUTTON_TYPES.includes(type))
244
- return false;
245
- return propData(node, 'size') !== 'icon' && !INLINE_BUTTON_VARIANTS.includes(String(propData(node, 'variant') ?? ''));
246
- }
247
- function fills(node) {
248
- return appearanceOf(node).widthType === 'fill' || FULL_WIDTH_TYPES.includes(String(node.type ?? ''));
249
- }
250
- function flowDirection(node) {
251
- if (node.type === 'scroll-view' && setOrBound(node, 'horizontal'))
252
- return 'row';
253
- const written = propData(node, 'direction');
254
- return written === 'row' || written === 'column' ? written : FLOW_TYPES[String(node.type ?? '')] ?? 'column';
255
- }
256
- /** Görünür yüzey: zemin, kenarlık ya da gölge taşıyan kap (`surfaceStyle`, `SURFACE_KEYS`). */
257
- function isSurface(node) {
258
- const style = propData(node, 'surfaceStyle');
259
- if (!style || typeof style !== 'object')
260
- return false;
261
- return Object.entries(style).some(([key, value]) => /^(background|border|shadow)/.test(key) && unwrap(value) !== undefined && unwrap(value) !== null && unwrap(value) !== '');
262
- }
263
- function nested(node, group, key) {
264
- const style = propData(node, group);
265
- return style && typeof style === 'object' ? unwrap(style[key]) : undefined;
266
- }
267
- function unwrap(value) {
268
- return value && typeof value === 'object' && 'data' in value ? value.data : value;
269
- }
270
- /**
271
- * Yazımdan önce de var olan bulgular tekrar edilmez: yama yalnız getirdiğini söyler. Aynı düğümün aynı kuralı
272
- * mesajı değişse de (ör. app-bar sayısı) yeni sayılmaz. Hedefsiz bulgu bir durum bilgisidir (kabuk okunamadı)
273
- * ve her yazımda söylenir — yoksa denetim sessizce atlanırdı.
274
- */
275
- export function newFindings(before, after) {
276
- const known = new Set(before.map(key));
277
- return after.filter(finding => finding.target === undefined || !known.has(key(finding)));
278
- }
279
- function key(finding) {
280
- return `${finding.code}|${finding.target ?? ''}`;
281
- }
282
- function rule(code, target, message) {
283
- return { code: MOBILE_RULES[code].id, severity: MOBILE_RULES[code].severity, target, message, guide: "platform_guide('mobile-layout')" };
284
- }
285
- function asNodes(value) {
286
- return Array.isArray(value) ? value.filter((item) => !!item && typeof item === 'object') : [];
287
- }
288
- function label(node) {
289
- return String(node?.elementId ?? node?.name ?? node?.type ?? '?');
290
- }
291
- function appearanceOf(node) {
292
- return node.appearance && typeof node.appearance === 'object' ? node.appearance : {};
293
- }
294
- function propData(node, prop) {
295
- const value = node.props?.[prop];
296
- return value && typeof value === 'object' && 'data' in value ? value.data : value;
297
- }
298
- /** Prop açık mı — ifadeye bağlıysa değeri çalışma anında belli olur, açık sayılır (yanlış uyarı vermemek için). */
299
- function setOrBound(node, prop) {
300
- const raw = node.props?.[prop];
301
- if (raw && typeof raw === 'object' && (raw.type === 'code' || raw.type === 'translation'))
302
- return true;
303
- const value = propData(node, prop);
304
- return value === true || value === 'true';
305
- }
306
- function boundVisibility(node) {
307
- const hidden = node.props?.isHidden;
308
- return !!hidden && typeof hidden === 'object' && (hidden.type === 'code' || hidden.type === 'translation');
309
- }
310
- function someNode(nodes, test) {
311
- return nodes.some(node => test(node) || someNode(asNodes(node.children), test));
312
- }
313
- function collect(nodes, test, skipInside = []) {
314
- return nodes.flatMap(node => [
315
- ...(test(node) ? [node] : []),
316
- ...(skipInside.includes(String(node.type ?? '')) ? [] : collect(asNodes(node.children), test, skipInside))
317
- ]);
318
- }
319
- function walkWithAncestors(nodes, visit, ancestors = []) {
320
- for (const node of nodes) {
321
- visit(node, ancestors);
322
- walkWithAncestors(asNodes(node.children), visit, [...ancestors, node]);
323
- }
324
- }