@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,239 +0,0 @@
1
- /**
2
- * MOBİLE ÖZGÜ olay yükleri. Mobil komponentlerin ÇOĞU web şemasını genişletir ve
3
- * olayları web sözleşmesindedir (`componentEvents.ts`) — bu dosya yalnız web'de
4
- * karşılığı OLMAYAN komponentler içindir (`app-bar` gibi kabuk komponentleri).
5
- *
6
- * Okuyucu önce buraya, sonra web sözlüğüne bakar; parity testi
7
- * (`test/mobile-component-parity.test.ts`) her mobil olayın iki sözlükten
8
- * birinde karşılanmasını zorunlu tutar.
9
- */
10
- const NO_DATA = { payload: {} };
11
- export const mobileComponentEventContracts = {
12
- 'choice-chips': {
13
- change: {
14
- payload: {
15
- value: 'string | number | boolean | Array — seçenek ÖZGÜN tipinde (değeri `34` yazılmış seçenek sayı yayar). `multiple` açıkken seçili değerlerin DİZİSİ (`[34, "a"]`); tek kipte seçili çipe yeniden dokunmak boş metin (`""`) yayar. Devre dışı seçenek olay yaymaz.'
16
- }
17
- }
18
- },
19
- // Web ikizinin yükü metindir (`e.currentTarget.value`); mobil seçeneği ÖZGÜN tipinde yayar (f-06, `optionEmitValues`).
20
- // İki platformda aynı koşul için karşılaştırmayı metne çevirin: `String(self.value) === '34'`.
21
- radio: {
22
- change: {
23
- payload: {
24
- value: 'string | number | boolean — seçilen seçeneğin ÖZGÜN değeri (değeri `34` yazılmış seçenek sayı yayar; web aynı seçimi `"34"` metni olarak yayar)'
25
- }
26
- }
27
- },
28
- checkboxgroup: {
29
- change: {
30
- payload: {
31
- value: 'Array — seçili seçeneklerin ÖZGÜN değerleri (`["su", 9]`; web metin dizisi yayar). Seçeneklerde olmayan eski değer metin kalır'
32
- }
33
- }
34
- },
35
- tooltip: {
36
- open: NO_DATA
37
- },
38
- calendar: {
39
- mount: NO_DATA,
40
- change: {
41
- payload: {
42
- value: "string 'yyyy-MM-ddT00:00:00' | null — seçilen gün (aralıkta başlangıç); web datepicker'ın biçimi: yerel duvar saati, gece yarısı, dilim eki yok",
43
- endValue: "string 'yyyy-MM-ddT00:00:00' | null — YALNIZ mode: 'range'; ilk basışta null, ikinci basışta bitiş günü"
44
- }
45
- }
46
- },
47
- stack: {
48
- mount: NO_DATA
49
- },
50
- 'list-item': {
51
- click: NO_DATA
52
- },
53
- fab: {
54
- click: NO_DATA
55
- },
56
- 'photo-viewer': {
57
- change: { payload: { index: 'number — gösterilen fotoğrafın sırası (0 tabanlı); makine index prop\'una yazar' } },
58
- close: { payload: {}, reads: 'kapat düğmesi ya da geri; makine isOpen\'ı kapatır' },
59
- share: { payload: { row: 'gösterilen satırın tamamı (images dizisindeki nesne)', index: 'number — satırın sırası' }, reads: 'Paylaş düğmesi yalnız bu olay bir akışa bağlıysa çizilir; paylaşmak akışın işidir' },
60
- delete: { payload: { row: 'gösterilen satırın tamamı', index: 'number — satırın sırası' }, reads: 'Sil düğmesi yalnız bu olay bir akışa bağlıysa çizilir; satırı silmek ve listeyi yenilemek akışın işidir' }
61
- },
62
- timer: {
63
- mount: NO_DATA,
64
- tick: { payload: { value: 'number — gösterilen saniye (geri sayımda kalan, kronometrede geçen); makine value prop\'una yazar' } },
65
- finish: { payload: { isRunning: 'false — geri sayım 0\'a indi; makine isRunning\'i kapatır' } }
66
- },
67
- audio: {
68
- mount: NO_DATA,
69
- play: { payload: { isPlaying: "true — çalma başladı (denetim ya da akış); makine isPlaying'i yazar" } },
70
- pause: { payload: { isPlaying: "false — çalma durdu (denetim, akış, bitiş ya da tarayıcının otomatik oynatma reddi); makine isPlaying'i yazar" } },
71
- ended: { payload: { isPlaying: "false — ses sona vardı (loop kapalıyken, pause'tan sonra); makine isPlaying'i kapatır" } }
72
- },
73
- lottie: {
74
- mount: NO_DATA,
75
- finish: { payload: { isPlaying: "false — döngüsüz animasyon bitti; makine isPlaying'i kapatır, yeniden true yazmak baştan oynatır" } }
76
- },
77
- webview: {
78
- mount: NO_DATA,
79
- load: { payload: {}, reads: "her tamamlanan yüklemede yayılır (izin verilen gezinmeden sonra da); cihazda yüklenemeyen sayfada yayılmaz (yerine error), web'de tarayıcı engellenen çerçevede de yayar" },
80
- navigate: { payload: { url: "string — sayfanın gitmek istediği adres; makine url prop'una yazar (self.url). allowNavigation kapalıyken (varsayılan) gezinme engellenir; adres yalnız kullanıcının dokunduğu bağlantıda sistemde açılır" } },
81
- message: {
82
- payload: { data: "string — sayfanın window.ReactNativeWebView.postMessage(metin) ile yolladığı metin, olduğu gibi; makine data prop'una yazar (self.data)" },
83
- reads: "iki yönlü köprü: sayfa window.ngApps.emit(name, data) çağırırsa yük olayın params'ında gelir — akış event.name ve event.data okur (self.data yazılmaz)"
84
- },
85
- error: { payload: {}, reads: "cihazda sayfa yüklenemeyince (ağ hatası) yayılır, görünüm yer tutucu çizer; web'de pratikte gelmez (iframe gibi)" }
86
- },
87
- html: {
88
- mount: NO_DATA,
89
- message: {
90
- payload: {},
91
- reads: "belgenin window.ngApps.emit(name, data) çağrısı; yük olayın params'ındadır — akış event.name (ileti adı) ve event.data (veri) okur. actions satırının akışı ise fireEvent(key, data) ile koşar: event.name = key, event.data = veri"
92
- }
93
- },
94
- map: {
95
- mount: NO_DATA,
96
- markerClick: { payload: { markerId: "string — dokunulan işaretçinin `id`si (boşsa listedeki sırası); makine markerId prop'una yazar (self.markerId). Başlığı ya da açıklaması olan işaretçide balon da açılır" } },
97
- regionChange: {
98
- payload: {
99
- latitude: 'number — kullanıcının bıraktığı merkezin enlemi (6 basamak)',
100
- longitude: 'number — merkezin boylamı (6 basamak, −180…180)',
101
- zoom: 'number — web harita ölçeği (2 basamak, 1–19)'
102
- },
103
- reads: "yalnız kullanıcının sürüklemesi ya da yakınlaştırması bitince yayılır; akışın yazdığı görünüm ve kutu boyu değişimi yaymaz. Makine üç prop'u da yazar (self.latitude, self.longitude, self.zoom)"
104
- },
105
- press: {
106
- payload: {
107
- latitude: 'number — yerin enlemi: tek işaretçi varsa o işaretçi, yoksa görünümün merkezi',
108
- longitude: 'number — yerin boylamı',
109
- mapsUrl: "string — yeri cihazın harita uygulamasında açan adres (iOS Apple Haritalar https://maps.apple.com/?ll=…&q=<ad>, Android geo:<lat>,<lng>?q=…(<ad>), web Google Haritalar araması)"
110
- },
111
- reads: "işaretçi dışına dokunuşta yayılır (interactive: false önizlemede bütün yüzey). Makine YALNIZ mapsUrl'i düğüme yazar (self.mapsUrl) — koordinat yazılsaydı harita kayardı; koordinat event.data'dadır. Tarif: redirect isRaw: true, path: '<harita>.mapsUrl'"
112
- }
113
- },
114
- markdown: {
115
- mount: NO_DATA,
116
- linkClick: {
117
- payload: {
118
- href: "string — dokunulan bağlantının ham adresi; şema süzülmeden gelir (dışarıda yalnız http(s), mailto, tel açılır)"
119
- }
120
- }
121
- },
122
- 'app-bar': {
123
- clickBack: NO_DATA,
124
- clickMenu: NO_DATA,
125
- clickAction: { payload: { key: 'string (eylemin key alanı; yoksa dizideki sırası)' } }
126
- },
127
- 'safe-area': {
128
- mount: NO_DATA
129
- },
130
- // Web `container` yalnız `mount` yayar; `click` mobile-only (plans/mobil-plantcare-acigi f-04, K1).
131
- container: {
132
- click: NO_DATA
133
- },
134
- // Arama varyantının "Vazgeç"i (plans/mobil-plantcare-acigi f-05): değer temizlenir, klavye kapanır.
135
- textinput: {
136
- cancel: NO_DATA
137
- },
138
- 'scroll-view': {
139
- mount: NO_DATA,
140
- scrollEnd: NO_DATA,
141
- refresh: NO_DATA,
142
- scroll: {
143
- payload: {
144
- y: 'number — dikey kaydırma konumu (yatay kaydırıcıda x)',
145
- contentHeight: 'number — içeriğin boyu (yatayda contentWidth)',
146
- viewportHeight: 'number — görünen alanın boyu (yatayda viewportWidth)'
147
- },
148
- reads: '100 ms seyreltmeli; yalnız bu olaya akış bağlıyken yayılır. Düğüme scrollY (scrollX) de yazılır'
149
- }
150
- },
151
- 'grouped-list': {
152
- mount: NO_DATA,
153
- change: {
154
- payload: {
155
- selectedIndex: 'number — dokunulan öğenin rows içindeki DÜZ indeksi (bölüm içi değil); aynı öğeye yeniden dokununca yayılmaz. Makine selectedIndex\'i yazar, akışta tıklanan satır <liste>.selectedItem'
156
- }
157
- },
158
- itemPress: {
159
- payload: {
160
- index: 'number (öğenin rows içindeki düz indeksi, 0 tabanlı)',
161
- row: 'object (öğenin satır verisi)'
162
- },
163
- reads: 'yalnız itemPressable açıkken; önce change { selectedIndex } yayılır. Öğe şablonu basılabilir bir bileşense (list-item, button) dokunuş onun click\'ine gider, itemPress yayılmaz'
164
- },
165
- swipe: {
166
- payload: {
167
- key: 'string (kaydırma eyleminin key alanı)',
168
- index: 'number (öğenin rows içindeki düz indeksi, 0 tabanlı)',
169
- row: 'object (öğenin satır verisi)'
170
- }
171
- },
172
- sectionPress: {
173
- payload: {
174
- key: 'string — bölüm anahtarı (tarih değerinde yerel gün YYYY-AA-GG)',
175
- title: 'string — bölüm başlığı (anahtar; boş alanlı satırların bölümünde otherSectionTitle)',
176
- count: 'number — bölümdeki öğe sayısı',
177
- items: 'object[] — bölümdeki satırların verisi'
178
- },
179
- reads: 'bölüm başlığına dokununca; yük başlık şablonunun Section ifadesiyle aynı nesnedir'
180
- },
181
- selectionChange: {
182
- payload: {
183
- selectedIndices: 'number[] — seçili satırların rows içindeki DÜZ indeksleri, sıralı',
184
- count: 'number — seçili satır sayısı'
185
- },
186
- reads: 'yalnız selectionMode: multiple (ya da longPressSelects ile açılınca); akış event.data.selectedIndices / event.data.count okur, düğümün selectedIndices/selectedItems değeri de yazılır'
187
- }
188
- },
189
- eventcalendar: {
190
- mount: NO_DATA,
191
- dateClick: {
192
- payload: { date: "string 'yyyy-MM-dd'", rows: 'object[] — o güne düşen kaynak satırlar (mobil)' },
193
- reads: "self'e yazılmaz — event.data ile oku; ay ve hafta görünümünde güne dokununca"
194
- },
195
- eventClick: {
196
- payload: { id: 'string', title: 'string', row: 'kaynak satırın TAMAMI' },
197
- reads: "self'e yazılmaz — event.data ile oku; gün çizelgesinde bloğa dokununca"
198
- },
199
- monthChange: {
200
- payload: {
201
- month: 'number (1-TABANLI)',
202
- year: 'number',
203
- view: "'month' | 'week' | 'day'",
204
- start: "string 'yyyy-MM-dd' (görünen ilk gün)",
205
- end: "string 'yyyy-MM-dd' (görünen son gün)"
206
- },
207
- reads: "self'e yazılmaz — event.data ile oku; oklar, yatay kaydırma ve Bugün ile her görünümde"
208
- },
209
- viewChange: {
210
- payload: { view: "'month' | 'week' | 'day'", start: "string 'yyyy-MM-dd'", end: "string 'yyyy-MM-dd'" },
211
- reads: "self'e yazılmaz — event.data ile oku; görünüm düğmesiyle"
212
- }
213
- },
214
- 'floating-container': {
215
- mount: NO_DATA
216
- },
217
- repeater: {
218
- swipe: {
219
- payload: {
220
- key: 'string (kaydırma eyleminin key alanı)',
221
- index: 'number (satırın sırası, 0 tabanlı)',
222
- row: 'object (satırın verisi)'
223
- }
224
- },
225
- itemPress: {
226
- payload: {
227
- index: 'number (satırın sırası, 0 tabanlı)',
228
- row: 'object (satırın verisi)'
229
- }
230
- },
231
- selectionChange: {
232
- payload: {
233
- selectedIndices: 'number[] — seçili satırların rows içindeki DÜZ indeksleri, sıralı',
234
- count: 'number — seçili satır sayısı'
235
- },
236
- reads: 'yalnız selectionMode: multiple (ya da longPressSelects ile açılınca); akış event.data.selectedIndices / event.data.count okur, düğümün selectedIndices/selectedItems değeri de yazılır'
237
- }
238
- }
239
- };
@@ -1,385 +0,0 @@
1
- import { IMAGE_SOURCE_PROPS, IMAGE_SOURCE_PROP_NAMES, MOBILE_RULES, REDIRECT_PRESENTATIONS, fileIdImageExpression, missingComponentAdvice, styleReplacement } from './mobileRules.js';
2
- import { findingLine } from './profile.js';
3
- import { SURFACE_KEYS } from '../styleResolveNative.js';
4
- import { DOCUMENT_STYLE_TYPES } from '../styleResolve.js';
5
- import { listThemeVarNames } from '../themeResolve.js';
6
- import { closestArgument } from '../../platform/argumentAliases.js';
7
- function asRecord(value) {
8
- return value && typeof value === 'object' && !Array.isArray(value) ? value : undefined;
9
- }
10
- function walk(value, visit) {
11
- if (!Array.isArray(value)) {
12
- const single = asRecord(value);
13
- if (single)
14
- walk([single], visit);
15
- return;
16
- }
17
- for (const entry of value) {
18
- const node = asRecord(entry);
19
- if (!node)
20
- continue;
21
- visit(node);
22
- walk(node.children, visit);
23
- }
24
- }
25
- /**
26
- * Koda ya da çeviriye bağlı prop: değeri çalışma anında `ref`'ten gelir, `data` yalnız tasarım yedeğidir.
27
- * Belge CSS'i alan tipte (`DOCUMENT_STYLE_TYPES`) bağlı `style` metin sayılır — şema onu uzun metin ilan eder.
28
- */
29
- function boundWrapper(value) {
30
- const wrapped = asRecord(value);
31
- return wrapped !== undefined && (wrapped.type === 'code' || wrapped.type === 'translation');
32
- }
33
- function propValue(value) {
34
- const wrapped = asRecord(value);
35
- return wrapped && 'data' in wrapped && typeof wrapped.type === 'string' ? wrapped.data : value;
36
- }
37
- /** Prop'un bağlı ifadesi: code sarmalının `ref`i (yoksa `data`sı) ya da çıplak şablon metni. */
38
- function boundExpression(value) {
39
- if (typeof value === 'string')
40
- return value;
41
- const wrapped = asRecord(value);
42
- if (!wrapped || wrapped.type !== 'code')
43
- return undefined;
44
- const expression = wrapped.ref ?? wrapped.data;
45
- return typeof expression === 'string' ? expression : undefined;
46
- }
47
- function label(node) {
48
- return String(node.elementId ?? node.name ?? node.type ?? '?');
49
- }
50
- const GRID_LAYOUTS = new Set(['grid', 'cssgrid']);
51
- const RELATIVE_UNIT = /\b\d*\.?\d+(em|rem)\b|calc\(/i;
52
- /**
53
- * THEME-012: mobil çizici `var(--ng-…)`'yi yalnız temanın yaydığı değişkenlerden çözer
54
- * (`compose-native-style.ts` `resolveThemeToken`); web'deki bileşen yerel değişkenleri (`--ng-nav-bg` …)
55
- * cihazda yoktur. Temada olmayan, yedeksiz bir ad çözülmez: zemin saydam kalır ve iOS gölgesi her çocuğa
56
- * düşer (koşu 37 K37-c, `var(--ng-surface)`). Web profili bu kuralı koşmaz.
57
- */
58
- const STYLE_GROUP_PROPS = new Set(['surfaceStyle', 'typographyStyle', 'headerStyle']);
59
- const UNRESOLVED_TOKEN = /var\(\s*(--ng-[\w-]+)\s*\)/g;
60
- let themeVarNames;
61
- function nearestThemeVar(name) {
62
- themeVarNames ??= listThemeVarNames();
63
- const tail = `-${name.replace(/^--ng-/, '')}`;
64
- const bySuffix = themeVarNames.filter(known => known.endsWith(tail)).sort((left, right) => left.length - right.length)[0];
65
- return bySuffix ?? closestArgument(name, themeVarNames);
66
- }
67
- function unknownThemeTokens(value) {
68
- themeVarNames ??= listThemeVarNames();
69
- const found = [];
70
- const visit = (entry) => {
71
- const unwrapped = propValue(entry);
72
- if (typeof unwrapped === 'string') {
73
- for (const match of unwrapped.matchAll(UNRESOLVED_TOKEN)) {
74
- const name = match[1] ?? '';
75
- if (!themeVarNames.includes(name))
76
- found.push(name);
77
- }
78
- return;
79
- }
80
- for (const nested of Object.values(asRecord(unwrapped) ?? {}))
81
- visit(nested);
82
- };
83
- visit(value);
84
- return [...new Set(found)];
85
- }
86
- /**
87
- * Profilin taşınabilirlik bulguları. Web profilinde HER ZAMAN boş dizi döner — web davranışı
88
- * değişmez (G1 kilidi); bulgular yalnız mobil profilde üretilir.
89
- */
90
- export function profileFindings(profile, input) {
91
- if (profile.platform === 'web')
92
- return [];
93
- const findings = [];
94
- const nodes = [];
95
- const blockNodes = [];
96
- const gridNodes = [];
97
- const styleHits = new Map();
98
- const unknownTypes = new Map();
99
- const shellNodes = new Map();
100
- const legacyNodes = [];
101
- const fileIdSources = [];
102
- const nodeTypes = new Map();
103
- const floorImages = [];
104
- const namelessIconButtons = [];
105
- const unknownTokens = [];
106
- const hit = (map, key, target) => map.set(key, [...(map.get(key) ?? []), target]);
107
- const inspectTokens = (target, value) => {
108
- for (const name of unknownThemeTokens(value)) {
109
- const nearest = nearestThemeVar(name);
110
- unknownTokens.push(nearest ? `${target}: ${name} → ${nearest}` : `${target}: ${name}`);
111
- }
112
- };
113
- const inspectAppearance = (target, appearance) => {
114
- if (!appearance)
115
- return;
116
- for (const key of profile.appearance.ignoredKeys) {
117
- const value = appearance[key];
118
- if (value !== undefined && value !== null && value !== '' && value !== 0)
119
- hit(styleHits, key, target);
120
- }
121
- for (const key of ['widthString', 'heightString']) {
122
- const value = appearance[key];
123
- if (typeof value === 'string' && RELATIVE_UNIT.test(value))
124
- hit(styleHits, `${key} (${value})`, target);
125
- }
126
- };
127
- // Mobil derleyici yüzey grubunun yalnız bilinen alanlarını okur; kalan alt anahtar cihazda düşer.
128
- // `data`sız sarmal (`{ type: 'code', ref }`) bütün grubu bir ifadeye bağlar: denetlenecek alt anahtar yoktur.
129
- const inspectSurface = (target, value) => {
130
- const raw = asRecord(value);
131
- if (raw && typeof raw.type === 'string' && !('data' in raw))
132
- return;
133
- for (const [key, wrapped] of Object.entries(asRecord(propValue(value)) ?? {})) {
134
- const value = propValue(wrapped);
135
- if (!SURFACE_KEYS.has(key) && value !== undefined && value !== null && value !== '')
136
- hit(styleHits, `surfaceStyle.${key}`, target);
137
- }
138
- };
139
- const inspectNode = (node) => {
140
- const target = label(node);
141
- const type = String(node.type ?? '');
142
- const props = asRecord(node.props) ?? {};
143
- nodes.push(node);
144
- nodeTypes.set(target, type);
145
- for (const prop of IMAGE_SOURCE_PROPS[type] ?? []) {
146
- const expression = fileIdImageExpression(boundExpression(props[prop]) ?? '');
147
- if (expression)
148
- fileIdSources.push({ target: `${target}.${prop}`, expression });
149
- }
150
- if (Array.isArray(node.blocks) && node.blocks.length > 0 && !profile.layout.blocksMeaningful)
151
- blockNodes.push(target);
152
- const layout = propValue(props.layout);
153
- if (typeof layout === 'string' && GRID_LAYOUTS.has(layout))
154
- gridNodes.push(`${target} (layout:${layout})`);
155
- inspectAppearance(target, asRecord(node.appearance));
156
- inspectSurface(target, props.surfaceStyle);
157
- for (const group of STYLE_GROUP_PROPS)
158
- inspectTokens(target, props[group]);
159
- if (type === 'image' && !imageHasDefiniteHeight(asRecord(node.appearance)))
160
- floorImages.push(target);
161
- if (type === 'button' && propValue(props.size) === 'icon' && !String(propValue(props.ariaLabel) ?? '').trim() && !String(propValue(props.text) ?? '').trim()) {
162
- namelessIconButtons.push(target);
163
- }
164
- if (!profile.style.freeStyle) {
165
- const style = propValue(props.style);
166
- const documentCss = DOCUMENT_STYLE_TYPES.has(type) && (typeof style === 'string' || boundWrapper(props.style));
167
- if (!documentCss && ((asRecord(style) && Object.keys(style).length > 0) || (typeof style === 'string' && style.trim() !== '')))
168
- hit(styleHits, 'style', target);
169
- }
170
- if (!profile.style.hoverStyle) {
171
- const hover = propValue(props.hoverStyle);
172
- if (asRecord(hover) && Object.keys(hover).length > 0)
173
- hit(styleHits, 'hoverStyle', target);
174
- }
175
- if (type && input.catalog && input.catalog.size > 0 && !input.catalog.has(type) && !profile.shell.legacyShellNodes.has(type))
176
- hit(unknownTypes, type, target);
177
- if (type && profile.shell.suggestions[type])
178
- hit(shellNodes, type, target);
179
- if (type && profile.shell.legacyShellNodes.has(type))
180
- legacyNodes.push(`${target} (${type})`);
181
- };
182
- walk(input.nodes, inspectNode);
183
- const savedTypes = new Map();
184
- walk(input.savedNodes, node => savedTypes.set(label(node), String(node.type ?? '')));
185
- for (const op of input.ops ?? []) {
186
- const target = typeof op.target === 'string' ? op.target : '?';
187
- if (op.op === 'add_component')
188
- walk(op.component, inspectNode);
189
- if (op.op === 'set_block' && !profile.layout.blocksMeaningful)
190
- blockNodes.push(`${target} (set_block)`);
191
- if (op.op === 'set_appearance')
192
- inspectAppearance(target, asRecord(op.appearance));
193
- if (op.op === 'replace_prop' || op.op === 'set_binding') {
194
- const prop = String(op.prop ?? '');
195
- const value = propValue(op.value);
196
- const targetType = nodeTypes.get(target) ?? savedTypes.get(target) ?? '';
197
- const documentCss = DOCUMENT_STYLE_TYPES.has(targetType) && (typeof value === 'string' || op.op === 'set_binding' || boundWrapper(op.value));
198
- if (prop === 'layout' && typeof value === 'string' && GRID_LAYOUTS.has(value))
199
- gridNodes.push(`${target} (layout:${value})`);
200
- if (prop === 'style' && !profile.style.freeStyle && !documentCss)
201
- hit(styleHits, 'style', target);
202
- if (prop === 'hoverStyle' && !profile.style.hoverStyle)
203
- hit(styleHits, 'hoverStyle', target);
204
- if (prop === 'surfaceStyle')
205
- inspectSurface(target, op.value);
206
- if (STYLE_GROUP_PROPS.has(prop))
207
- inspectTokens(target, op.value);
208
- // MOB-011: var olan düğümün tipi op'ta yok — tip biliniyorsa (aynı yazımda eklendi) ona, yoksa prop adına bakılır.
209
- const knownType = nodeTypes.get(target);
210
- const imageProp = knownType === undefined ? IMAGE_SOURCE_PROP_NAMES.has(prop) : (IMAGE_SOURCE_PROPS[knownType] ?? []).includes(prop);
211
- const expression = imageProp ? fileIdImageExpression(boundExpression(typeof op.expression === 'string' ? op.expression : op.value) ?? '') : undefined;
212
- if (expression)
213
- fileIdSources.push({ target: `${target}.${prop}`, expression });
214
- }
215
- }
216
- if (blockNodes.length > 0 || gridNodes.length > 0) {
217
- findings.push({
218
- code: MOBILE_RULES['MOB-001'].id,
219
- severity: 'warn',
220
- message: [
221
- blockNodes.length > 0 ? `blok (12 sütun ızgara) mobil çizerde yok sayılır: ${[...new Set(blockNodes)].join(', ')}` : '',
222
- gridNodes.length > 0 ? `grid/cssgrid yerleşimi yok, kap flex çizilir: ${gridNodes.join(', ')}` : ''
223
- ].filter(Boolean).join(' · ') + ". Sıra dizi indeksinden, yerleşim flex'ten gelir (direction/gap/horizontalAlign). Yazım platforma gitti.",
224
- guide: "platform_guide('mobile')"
225
- });
226
- }
227
- for (const [key, targets] of styleHits) {
228
- findings.push({
229
- code: MOBILE_RULES['MOB-002'].id,
230
- severity: 'warn',
231
- target: [...new Set(targets)].join(', '),
232
- message: `'${key}' mobilde çizilmez — ${styleReplacement(key.split(' ')[0] ?? key)}. Yazım platforma gitti.`
233
- });
234
- }
235
- if (unknownTokens.length > 0) {
236
- findings.push({
237
- code: 'THEME-012',
238
- severity: 'warn',
239
- target: [...new Set(unknownTokens.map(entry => entry.split(':')[0] ?? entry))].join(', '),
240
- message: `temada tanımlı olmayan jeton cihazda çözülmez (zemin saydam kalır, iOS gölgesi içeriğe düşer): ${[...new Set(unknownTokens)].join(' · ')}. Geçerli adlar temanın yaydığı değişkenlerdir (theme_get); bilerek kullanıyorsanız yedek verin: var(--ad, #fff). Yazım platforma gitti.`,
241
- guide: "platform_guide('theme')"
242
- });
243
- }
244
- for (const [type, targets] of unknownTypes) {
245
- findings.push({
246
- code: MOBILE_RULES['MOB-003'].id,
247
- severity: 'warn',
248
- target: [...new Set(targets)].join(', '),
249
- message: missingComponentAdvice(type)
250
- });
251
- }
252
- for (const [type, targets] of shellNodes) {
253
- findings.push({
254
- code: MOBILE_RULES['MOB-004'].id,
255
- severity: 'warn',
256
- target: [...new Set(targets)].join(', '),
257
- message: `'${type}' web kabuğudur, mobil uygulamada karşılığı: ${profile.shell.suggestions[type]}`,
258
- guide: "examples/mobile-app-shell-{tab,stack,drawer}.json"
259
- });
260
- }
261
- if (floorImages.length > 0) {
262
- findings.push({
263
- code: MOBILE_RULES['MOB-012'].id,
264
- severity: 'info',
265
- target: floorImages.join(', '),
266
- message: "görselin boyu belirsiz, 96 dp taban boyla çizilir — boy verin (heightType: 'fixed' + heightString ya da 'fill': görsel kabuğu doldurur) ya da oran verin (appearance.aspectRatio + heightType: 'auto' + widthType 'fill'/'fixed')",
267
- guide: "platform_guide('mobile')"
268
- });
269
- }
270
- if (namelessIconButtons.length > 0) {
271
- findings.push({
272
- code: MOBILE_RULES['MOB-013'].id,
273
- severity: 'info',
274
- target: namelessIconButtons.join(', '),
275
- message: "ikon boyundaki düğmenin adı yok — size: 'icon' metni çizmez ve ekran okuyucu ikonun ligatür adını (ör. 'notifications') okur; ariaLabel verin (ör. 'Bildirimler')",
276
- guide: "platform_guide('mobile')"
277
- });
278
- }
279
- if (legacyNodes.length > 0) {
280
- findings.push({
281
- code: MOBILE_RULES['MOB-007'].id,
282
- severity: 'warn',
283
- target: legacyNodes.join(', '),
284
- message: "sekme çubuğu ve çekmece menüsü katalog bileşeni değildir, çizer bu düğümleri yok sayar — sekmeler Ayarlar › Sekmeler'den (application_update properties.mobile.tabs), çekmece 'drawer' widget'ı + app-bar.menuTarget"
285
- });
286
- }
287
- if (fileIdSources.length > 0) {
288
- const sample = fileIdSources[0]?.expression ?? '';
289
- const object = sample.replace(/\??\.value(\s*\}\})$/, '$1');
290
- findings.push({
291
- code: MOBILE_RULES['MOB-011'].id,
292
- severity: 'warn',
293
- target: fileIdSources.map(source => source.target).join(', '),
294
- message: `${[...new Set(fileIdSources.map(source => `'${source.expression}'`))].join(', ')} '.value' ile bitiyor — kayıttan okunan FILE alanı { type: 'ref', value: <dosya id>, absolute_path: <url> } döner ve '.value' dosya KİMLİĞİDİR (GUID), görsel çizilmez. Nesnenin kendisini verin ('${object}') ya da '.absolute_path' yazın. Gateway alan tipini sayfa şemasından bilemez: yol FILE alanı değilse (ör. URL taşıyan metin alanı) uyarıyı yok sayın. Yazım platforma gitti.`,
295
- guide: "platform_guide('mobile') §Dosya ve fotoğraf"
296
- });
297
- }
298
- const infoSteps = new Map();
299
- const badPresentation = new Map();
300
- walkSteps(input.steps, step => {
301
- const type = String(step.type ?? '');
302
- if (profile.steps.infoOnly.has(type))
303
- hit(infoSteps, type, String(step.stepId ?? step.name ?? '?'));
304
- // MOB-008: sunum biçimi yalnız `redirect`te anlamlı ve kümesi kapalı.
305
- // Alan YOKSA bulgu yok — varsayılan `push` ve yazımların çoğu onu yazmaz.
306
- if (type !== 'redirect')
307
- return;
308
- const args = asRecord(step.args);
309
- const presentation = args?.presentation;
310
- if (presentation === undefined || presentation === null || presentation === '')
311
- return;
312
- if (!REDIRECT_PRESENTATIONS.has(String(presentation))) {
313
- hit(badPresentation, String(presentation), String(step.stepId ?? step.name ?? '?'));
314
- }
315
- });
316
- for (const [value, stepIds] of badPresentation) {
317
- findings.push({
318
- code: MOBILE_RULES['MOB-008'].id,
319
- severity: 'warn',
320
- target: stepIds.join(', '),
321
- message: `redirect.presentation '${value}' tanınmıyor — ${[...REDIRECT_PRESENTATIONS].join(' | ')} bekleniyor. Backend doğrulayıcısı 422 döner; geçse bile çizer 'push'a düşer.`,
322
- guide: "platform_guide('mobile')"
323
- });
324
- }
325
- for (const [type, stepIds] of infoSteps) {
326
- findings.push({
327
- code: MOBILE_RULES['MOB-005'].id,
328
- severity: 'info',
329
- target: stepIds.join(', '),
330
- message: type === 'download'
331
- ? "mobil uygulamada 'download' tarayıcı indirmesi yapmaz: adres dönen yanıt Linking.openURL ile açılır, ikili gövde (JSON değil) önbelleğe yazılıp paylaşım sayfasında (expo-sharing) açılır"
332
- : "mobil uygulamada 'setcookie' tarayıcı çerezi yazmaz, değeri güvenli cihaz deposuna (SecureStore) koyar; sonraki her istekte X-Flow-Cookie başlığıyla gider ve middleware/workflow onu web'deki gibi cookie.<ad> olarak okur — okuyan ifadeler aynı çalışır"
333
- });
334
- }
335
- return findings;
336
- }
337
- function walkSteps(value, visit) {
338
- if (!Array.isArray(value))
339
- return;
340
- for (const entry of value) {
341
- const step = asRecord(entry);
342
- if (!step)
343
- continue;
344
- visit(step);
345
- walkSteps(step.steps, visit);
346
- }
347
- }
348
- /** Bulguları `layoutWarnings[]` satırlarına çevirir. */
349
- export function findingLines(findings) {
350
- return findings.map(findingLine);
351
- }
352
- /**
353
- * MOB-014: yazılan değişkenlerden, profilin barındırıcısının depo vermediği saklama türünü taşıyanlar.
354
- * Kararı platform adı değil profilin `state.storageKinds` ekseni verir; `profileFindings`'ten ayrı
355
- * durur çünkü o yalnız mobil profilde bulgu üretir, bu kural kümenin boş olduğu profilde tetiklenir.
356
- */
357
- export function stateStorageFindings(profile, entries) {
358
- const ignored = entries.filter(entry => entry.storage !== undefined && !profile.state.storageKinds.has(entry.storage));
359
- if (ignored.length === 0)
360
- return [];
361
- return [{
362
- code: MOBILE_RULES['MOB-014'].id,
363
- severity: MOBILE_RULES['MOB-014'].severity,
364
- target: ignored.map(entry => `'${entry.key}'`).join(', '),
365
- message: `saklama türü (${[...new Set(ignored.map(entry => entry.storage))].join(', ')}) ${profile.platform} uygulamasında uygulanmıyor: tür kayda yazıldı ama bu platformun çalışma zamanı depo vermez — state bellekte çalışır, sayfa yeniden açılınca varsayılana döner`,
366
- guide: "platform_guide('mobile') §Kalıcı state"
367
- }];
368
- }
369
- /**
370
- * Görselin boyu belli mi — mobil `imageFrameStyle` sırası: kabuktan (`fixed` + ölçülebilir `heightString`,
371
- * ya da `fill`) ya da orandan (`aspectRatio` + `heightType: 'auto'` + genişlik `fill`/`fixed`+ölçü).
372
- * Değilse görsel 96 dp taban boyla çizilir (MOB-012). Koşu bulgusu: `fixed`le kurulan görseller boşluk
373
- * bıraktı, doğru kalıp yalnız bileşen açıklamasında yazıyordu (plans/mobil-plantcare-acigi g-10 adım 3).
374
- */
375
- function imageHasDefiniteHeight(appearance) {
376
- if (!appearance)
377
- return false;
378
- // Çizicinin `cssLength`'i: sayı, `Npx` ya da `N%` — birimsiz dize ölçülemez.
379
- const measurable = (value) => typeof value === 'number' || (typeof value === 'string' && /^\d+(\.\d+)?(px|%)$/.test(value.trim()));
380
- const heightType = appearance.heightType;
381
- if (heightType === 'fill' || (heightType === 'fixed' && measurable(appearance.heightString)))
382
- return true;
383
- const definiteWidth = appearance.widthType === 'fill' || (appearance.widthType === 'fixed' && measurable(appearance.widthString));
384
- return Number(appearance.aspectRatio) > 0 && heightType === 'auto' && definiteWidth;
385
- }