mindheal 1.0.0

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 (255) hide show
  1. package/.env.example +48 -0
  2. package/CHANGELOG.md +27 -0
  3. package/LICENSE +21 -0
  4. package/README.md +481 -0
  5. package/dist/cjs/ai/ai-provider.js +46 -0
  6. package/dist/cjs/ai/ai-provider.js.map +1 -0
  7. package/dist/cjs/ai/anthropic-provider.js +106 -0
  8. package/dist/cjs/ai/anthropic-provider.js.map +1 -0
  9. package/dist/cjs/ai/azure-openai-provider.js +130 -0
  10. package/dist/cjs/ai/azure-openai-provider.js.map +1 -0
  11. package/dist/cjs/ai/bedrock-provider.js +183 -0
  12. package/dist/cjs/ai/bedrock-provider.js.map +1 -0
  13. package/dist/cjs/ai/deepseek-provider.js +118 -0
  14. package/dist/cjs/ai/deepseek-provider.js.map +1 -0
  15. package/dist/cjs/ai/gemini-provider.js +129 -0
  16. package/dist/cjs/ai/gemini-provider.js.map +1 -0
  17. package/dist/cjs/ai/groq-provider.js +118 -0
  18. package/dist/cjs/ai/groq-provider.js.map +1 -0
  19. package/dist/cjs/ai/meta-provider.js +118 -0
  20. package/dist/cjs/ai/meta-provider.js.map +1 -0
  21. package/dist/cjs/ai/ollama-provider.js +127 -0
  22. package/dist/cjs/ai/ollama-provider.js.map +1 -0
  23. package/dist/cjs/ai/openai-provider.js +117 -0
  24. package/dist/cjs/ai/openai-provider.js.map +1 -0
  25. package/dist/cjs/ai/perplexity-provider.js +118 -0
  26. package/dist/cjs/ai/perplexity-provider.js.map +1 -0
  27. package/dist/cjs/ai/prompt-templates.js +174 -0
  28. package/dist/cjs/ai/prompt-templates.js.map +1 -0
  29. package/dist/cjs/ai/qwen-provider.js +118 -0
  30. package/dist/cjs/ai/qwen-provider.js.map +1 -0
  31. package/dist/cjs/analytics/healing-analytics.js +263 -0
  32. package/dist/cjs/analytics/healing-analytics.js.map +1 -0
  33. package/dist/cjs/cli/init.js +517 -0
  34. package/dist/cjs/cli/init.js.map +1 -0
  35. package/dist/cjs/config/config-loader.js +135 -0
  36. package/dist/cjs/config/config-loader.js.map +1 -0
  37. package/dist/cjs/config/defaults.js +109 -0
  38. package/dist/cjs/config/defaults.js.map +1 -0
  39. package/dist/cjs/core/dom-snapshot.js +280 -0
  40. package/dist/cjs/core/dom-snapshot.js.map +1 -0
  41. package/dist/cjs/core/enterprise-strategy.js +702 -0
  42. package/dist/cjs/core/enterprise-strategy.js.map +1 -0
  43. package/dist/cjs/core/healer.js +283 -0
  44. package/dist/cjs/core/healer.js.map +1 -0
  45. package/dist/cjs/core/interceptor.js +945 -0
  46. package/dist/cjs/core/interceptor.js.map +1 -0
  47. package/dist/cjs/core/locator-analyzer.js +172 -0
  48. package/dist/cjs/core/locator-analyzer.js.map +1 -0
  49. package/dist/cjs/core/locator-strategies.js +891 -0
  50. package/dist/cjs/core/locator-strategies.js.map +1 -0
  51. package/dist/cjs/core/self-heal-cache.js +178 -0
  52. package/dist/cjs/core/self-heal-cache.js.map +1 -0
  53. package/dist/cjs/core/smart-retry.js +248 -0
  54. package/dist/cjs/core/smart-retry.js.map +1 -0
  55. package/dist/cjs/core/visual-verification.js +262 -0
  56. package/dist/cjs/core/visual-verification.js.map +1 -0
  57. package/dist/cjs/git/code-modifier.js +184 -0
  58. package/dist/cjs/git/code-modifier.js.map +1 -0
  59. package/dist/cjs/git/git-operations.js +145 -0
  60. package/dist/cjs/git/git-operations.js.map +1 -0
  61. package/dist/cjs/git/pr-creator.js +190 -0
  62. package/dist/cjs/git/pr-creator.js.map +1 -0
  63. package/dist/cjs/index.js +97 -0
  64. package/dist/cjs/index.js.map +1 -0
  65. package/dist/cjs/rag/context-retriever.js +289 -0
  66. package/dist/cjs/rag/context-retriever.js.map +1 -0
  67. package/dist/cjs/rag/embeddings.js +82 -0
  68. package/dist/cjs/rag/embeddings.js.map +1 -0
  69. package/dist/cjs/rag/knowledge-store.js +159 -0
  70. package/dist/cjs/rag/knowledge-store.js.map +1 -0
  71. package/dist/cjs/reporters/heal-report.js +279 -0
  72. package/dist/cjs/reporters/heal-report.js.map +1 -0
  73. package/dist/cjs/reporters/heal-reporter.js +294 -0
  74. package/dist/cjs/reporters/heal-reporter.js.map +1 -0
  75. package/dist/cjs/server/review-server.js +166 -0
  76. package/dist/cjs/server/review-server.js.map +1 -0
  77. package/dist/cjs/server/routes.js +92 -0
  78. package/dist/cjs/server/routes.js.map +1 -0
  79. package/dist/cjs/utils/environment.js +57 -0
  80. package/dist/cjs/utils/environment.js.map +1 -0
  81. package/dist/cjs/utils/file-lock.js +136 -0
  82. package/dist/cjs/utils/file-lock.js.map +1 -0
  83. package/dist/cjs/utils/file-utils.js +49 -0
  84. package/dist/cjs/utils/file-utils.js.map +1 -0
  85. package/dist/cjs/utils/logger.js +78 -0
  86. package/dist/cjs/utils/logger.js.map +1 -0
  87. package/dist/esm/ai/ai-provider.js +44 -0
  88. package/dist/esm/ai/ai-provider.js.map +1 -0
  89. package/dist/esm/ai/anthropic-provider.js +104 -0
  90. package/dist/esm/ai/anthropic-provider.js.map +1 -0
  91. package/dist/esm/ai/azure-openai-provider.js +128 -0
  92. package/dist/esm/ai/azure-openai-provider.js.map +1 -0
  93. package/dist/esm/ai/bedrock-provider.js +181 -0
  94. package/dist/esm/ai/bedrock-provider.js.map +1 -0
  95. package/dist/esm/ai/deepseek-provider.js +116 -0
  96. package/dist/esm/ai/deepseek-provider.js.map +1 -0
  97. package/dist/esm/ai/gemini-provider.js +127 -0
  98. package/dist/esm/ai/gemini-provider.js.map +1 -0
  99. package/dist/esm/ai/groq-provider.js +116 -0
  100. package/dist/esm/ai/groq-provider.js.map +1 -0
  101. package/dist/esm/ai/meta-provider.js +116 -0
  102. package/dist/esm/ai/meta-provider.js.map +1 -0
  103. package/dist/esm/ai/ollama-provider.js +125 -0
  104. package/dist/esm/ai/ollama-provider.js.map +1 -0
  105. package/dist/esm/ai/openai-provider.js +115 -0
  106. package/dist/esm/ai/openai-provider.js.map +1 -0
  107. package/dist/esm/ai/perplexity-provider.js +116 -0
  108. package/dist/esm/ai/perplexity-provider.js.map +1 -0
  109. package/dist/esm/ai/prompt-templates.js +171 -0
  110. package/dist/esm/ai/prompt-templates.js.map +1 -0
  111. package/dist/esm/ai/qwen-provider.js +116 -0
  112. package/dist/esm/ai/qwen-provider.js.map +1 -0
  113. package/dist/esm/analytics/healing-analytics.js +261 -0
  114. package/dist/esm/analytics/healing-analytics.js.map +1 -0
  115. package/dist/esm/cli/init.js +495 -0
  116. package/dist/esm/cli/init.js.map +1 -0
  117. package/dist/esm/config/config-loader.js +132 -0
  118. package/dist/esm/config/config-loader.js.map +1 -0
  119. package/dist/esm/config/defaults.js +107 -0
  120. package/dist/esm/config/defaults.js.map +1 -0
  121. package/dist/esm/core/dom-snapshot.js +278 -0
  122. package/dist/esm/core/dom-snapshot.js.map +1 -0
  123. package/dist/esm/core/enterprise-strategy.js +695 -0
  124. package/dist/esm/core/enterprise-strategy.js.map +1 -0
  125. package/dist/esm/core/healer.js +281 -0
  126. package/dist/esm/core/healer.js.map +1 -0
  127. package/dist/esm/core/interceptor.js +940 -0
  128. package/dist/esm/core/interceptor.js.map +1 -0
  129. package/dist/esm/core/locator-analyzer.js +169 -0
  130. package/dist/esm/core/locator-analyzer.js.map +1 -0
  131. package/dist/esm/core/locator-strategies.js +882 -0
  132. package/dist/esm/core/locator-strategies.js.map +1 -0
  133. package/dist/esm/core/self-heal-cache.js +176 -0
  134. package/dist/esm/core/self-heal-cache.js.map +1 -0
  135. package/dist/esm/core/smart-retry.js +246 -0
  136. package/dist/esm/core/smart-retry.js.map +1 -0
  137. package/dist/esm/core/visual-verification.js +260 -0
  138. package/dist/esm/core/visual-verification.js.map +1 -0
  139. package/dist/esm/git/code-modifier.js +182 -0
  140. package/dist/esm/git/code-modifier.js.map +1 -0
  141. package/dist/esm/git/git-operations.js +143 -0
  142. package/dist/esm/git/git-operations.js.map +1 -0
  143. package/dist/esm/git/pr-creator.js +188 -0
  144. package/dist/esm/git/pr-creator.js.map +1 -0
  145. package/dist/esm/index.js +37 -0
  146. package/dist/esm/index.js.map +1 -0
  147. package/dist/esm/rag/context-retriever.js +287 -0
  148. package/dist/esm/rag/context-retriever.js.map +1 -0
  149. package/dist/esm/rag/embeddings.js +77 -0
  150. package/dist/esm/rag/embeddings.js.map +1 -0
  151. package/dist/esm/rag/knowledge-store.js +157 -0
  152. package/dist/esm/rag/knowledge-store.js.map +1 -0
  153. package/dist/esm/reporters/heal-report.js +277 -0
  154. package/dist/esm/reporters/heal-report.js.map +1 -0
  155. package/dist/esm/reporters/heal-reporter.js +290 -0
  156. package/dist/esm/reporters/heal-reporter.js.map +1 -0
  157. package/dist/esm/server/review-server.js +164 -0
  158. package/dist/esm/server/review-server.js.map +1 -0
  159. package/dist/esm/server/routes.js +90 -0
  160. package/dist/esm/server/routes.js.map +1 -0
  161. package/dist/esm/utils/environment.js +53 -0
  162. package/dist/esm/utils/environment.js.map +1 -0
  163. package/dist/esm/utils/file-lock.js +134 -0
  164. package/dist/esm/utils/file-lock.js.map +1 -0
  165. package/dist/esm/utils/file-utils.js +43 -0
  166. package/dist/esm/utils/file-utils.js.map +1 -0
  167. package/dist/esm/utils/logger.js +75 -0
  168. package/dist/esm/utils/logger.js.map +1 -0
  169. package/dist/types/ai/ai-provider.d.ts +4 -0
  170. package/dist/types/ai/ai-provider.d.ts.map +1 -0
  171. package/dist/types/ai/anthropic-provider.d.ts +11 -0
  172. package/dist/types/ai/anthropic-provider.d.ts.map +1 -0
  173. package/dist/types/ai/azure-openai-provider.d.ts +13 -0
  174. package/dist/types/ai/azure-openai-provider.d.ts.map +1 -0
  175. package/dist/types/ai/bedrock-provider.d.ts +14 -0
  176. package/dist/types/ai/bedrock-provider.d.ts.map +1 -0
  177. package/dist/types/ai/deepseek-provider.d.ts +12 -0
  178. package/dist/types/ai/deepseek-provider.d.ts.map +1 -0
  179. package/dist/types/ai/gemini-provider.d.ts +12 -0
  180. package/dist/types/ai/gemini-provider.d.ts.map +1 -0
  181. package/dist/types/ai/groq-provider.d.ts +12 -0
  182. package/dist/types/ai/groq-provider.d.ts.map +1 -0
  183. package/dist/types/ai/meta-provider.d.ts +12 -0
  184. package/dist/types/ai/meta-provider.d.ts.map +1 -0
  185. package/dist/types/ai/ollama-provider.d.ts +10 -0
  186. package/dist/types/ai/ollama-provider.d.ts.map +1 -0
  187. package/dist/types/ai/openai-provider.d.ts +11 -0
  188. package/dist/types/ai/openai-provider.d.ts.map +1 -0
  189. package/dist/types/ai/perplexity-provider.d.ts +12 -0
  190. package/dist/types/ai/perplexity-provider.d.ts.map +1 -0
  191. package/dist/types/ai/prompt-templates.d.ts +11 -0
  192. package/dist/types/ai/prompt-templates.d.ts.map +1 -0
  193. package/dist/types/ai/qwen-provider.d.ts +12 -0
  194. package/dist/types/ai/qwen-provider.d.ts.map +1 -0
  195. package/dist/types/analytics/healing-analytics.d.ts +36 -0
  196. package/dist/types/analytics/healing-analytics.d.ts.map +1 -0
  197. package/dist/types/cli/init.d.ts +15 -0
  198. package/dist/types/cli/init.d.ts.map +1 -0
  199. package/dist/types/config/config-loader.d.ts +4 -0
  200. package/dist/types/config/config-loader.d.ts.map +1 -0
  201. package/dist/types/config/defaults.d.ts +3 -0
  202. package/dist/types/config/defaults.d.ts.map +1 -0
  203. package/dist/types/core/dom-snapshot.d.ts +12 -0
  204. package/dist/types/core/dom-snapshot.d.ts.map +1 -0
  205. package/dist/types/core/enterprise-strategy.d.ts +56 -0
  206. package/dist/types/core/enterprise-strategy.d.ts.map +1 -0
  207. package/dist/types/core/healer.d.ts +52 -0
  208. package/dist/types/core/healer.d.ts.map +1 -0
  209. package/dist/types/core/interceptor.d.ts +64 -0
  210. package/dist/types/core/interceptor.d.ts.map +1 -0
  211. package/dist/types/core/locator-analyzer.d.ts +31 -0
  212. package/dist/types/core/locator-analyzer.d.ts.map +1 -0
  213. package/dist/types/core/locator-strategies.d.ts +45 -0
  214. package/dist/types/core/locator-strategies.d.ts.map +1 -0
  215. package/dist/types/core/self-heal-cache.d.ts +51 -0
  216. package/dist/types/core/self-heal-cache.d.ts.map +1 -0
  217. package/dist/types/core/smart-retry.d.ts +64 -0
  218. package/dist/types/core/smart-retry.d.ts.map +1 -0
  219. package/dist/types/core/visual-verification.d.ts +46 -0
  220. package/dist/types/core/visual-verification.d.ts.map +1 -0
  221. package/dist/types/git/code-modifier.d.ts +51 -0
  222. package/dist/types/git/code-modifier.d.ts.map +1 -0
  223. package/dist/types/git/git-operations.d.ts +40 -0
  224. package/dist/types/git/git-operations.d.ts.map +1 -0
  225. package/dist/types/git/pr-creator.d.ts +27 -0
  226. package/dist/types/git/pr-creator.d.ts.map +1 -0
  227. package/dist/types/index.d.ts +40 -0
  228. package/dist/types/index.d.ts.map +1 -0
  229. package/dist/types/rag/context-retriever.d.ts +69 -0
  230. package/dist/types/rag/context-retriever.d.ts.map +1 -0
  231. package/dist/types/rag/embeddings.d.ts +32 -0
  232. package/dist/types/rag/embeddings.d.ts.map +1 -0
  233. package/dist/types/rag/index.d.ts +12 -0
  234. package/dist/types/rag/index.d.ts.map +1 -0
  235. package/dist/types/rag/knowledge-store.d.ts +38 -0
  236. package/dist/types/rag/knowledge-store.d.ts.map +1 -0
  237. package/dist/types/reporters/heal-report.d.ts +29 -0
  238. package/dist/types/reporters/heal-report.d.ts.map +1 -0
  239. package/dist/types/reporters/heal-reporter.d.ts +49 -0
  240. package/dist/types/reporters/heal-reporter.d.ts.map +1 -0
  241. package/dist/types/server/review-server.d.ts +20 -0
  242. package/dist/types/server/review-server.d.ts.map +1 -0
  243. package/dist/types/server/routes.d.ts +4 -0
  244. package/dist/types/server/routes.d.ts.map +1 -0
  245. package/dist/types/types/index.d.ts +433 -0
  246. package/dist/types/types/index.d.ts.map +1 -0
  247. package/dist/types/utils/environment.d.ts +10 -0
  248. package/dist/types/utils/environment.d.ts.map +1 -0
  249. package/dist/types/utils/file-lock.d.ts +37 -0
  250. package/dist/types/utils/file-lock.d.ts.map +1 -0
  251. package/dist/types/utils/file-utils.d.ts +7 -0
  252. package/dist/types/utils/file-utils.d.ts.map +1 -0
  253. package/dist/types/utils/logger.d.ts +9 -0
  254. package/dist/types/utils/logger.d.ts.map +1 -0
  255. package/package.json +106 -0
@@ -0,0 +1,262 @@
1
+ 'use strict';
2
+
3
+ var logger = require('../utils/logger.js');
4
+ var fileUtils = require('../utils/file-utils.js');
5
+
6
+ /**
7
+ * Visual Verification
8
+ *
9
+ * After healing a locator, captures a screenshot of the healed element and
10
+ * verifies it's visually the correct element. This prevents "wrong element"
11
+ * heals where the selector matches something in a different part of the page.
12
+ *
13
+ * Checks:
14
+ * 1. Element is visible on the page
15
+ * 2. Element is within the viewport (not scrolled off-screen)
16
+ * 3. Element has a reasonable bounding box (not 0x0)
17
+ * 4. Screenshot capture succeeds (element is rendered)
18
+ */
19
+ /**
20
+ * Visual Verification engine for healed locators.
21
+ */
22
+ class VisualVerifier {
23
+ constructor(config) {
24
+ this.config = config;
25
+ }
26
+ /**
27
+ * Verify that a healed locator points to a visually valid element.
28
+ *
29
+ * Returns a VisualVerificationResult with:
30
+ * - `verified`: true if the element passes all visual checks
31
+ * - `elementScreenshotPath`: path to element screenshot (if captured)
32
+ * - `boundingBox`: element's position and size on page
33
+ * - `elementVisible`: whether the element is visible
34
+ * - `elementInViewport`: whether the element is within the viewport
35
+ */
36
+ async verify(page, healedLocator, eventId) {
37
+ if (!this.config.enabled) {
38
+ return {
39
+ verified: true, // Skip verification, assume valid
40
+ elementScreenshotPath: null,
41
+ fullPageScreenshotPath: null,
42
+ boundingBox: null,
43
+ elementVisible: true,
44
+ elementInViewport: true,
45
+ timestamp: Date.now(),
46
+ };
47
+ }
48
+ const result = {
49
+ verified: false,
50
+ elementScreenshotPath: null,
51
+ fullPageScreenshotPath: null,
52
+ boundingBox: null,
53
+ elementVisible: false,
54
+ elementInViewport: false,
55
+ timestamp: Date.now(),
56
+ };
57
+ try {
58
+ const locator = page.locator(healedLocator.selector);
59
+ // 1. Check element count
60
+ const count = await locator.count();
61
+ if (count === 0) {
62
+ logger.logger.warn(`[Visual] Healed locator resolved to 0 elements: ${healedLocator.selector}`);
63
+ return result;
64
+ }
65
+ const element = locator.first();
66
+ // 2. Check visibility
67
+ try {
68
+ result.elementVisible = await element.isVisible();
69
+ }
70
+ catch {
71
+ result.elementVisible = false;
72
+ }
73
+ if (!result.elementVisible) {
74
+ logger.logger.warn(`[Visual] Healed element is not visible: ${healedLocator.selector}`);
75
+ // Still partially verified — element exists but not visible
76
+ }
77
+ // 3. Get bounding box
78
+ try {
79
+ const box = await element.boundingBox();
80
+ if (box) {
81
+ result.boundingBox = {
82
+ x: box.x,
83
+ y: box.y,
84
+ width: box.width,
85
+ height: box.height,
86
+ };
87
+ // Check for zero-size elements
88
+ if (box.width === 0 || box.height === 0) {
89
+ logger.logger.warn(`[Visual] Healed element has zero dimensions: ${box.width}x${box.height}`);
90
+ return result;
91
+ }
92
+ // Check if element is in viewport
93
+ const viewport = page.viewportSize();
94
+ if (viewport) {
95
+ result.elementInViewport =
96
+ box.x >= -box.width &&
97
+ box.y >= -box.height &&
98
+ box.x < viewport.width + box.width &&
99
+ box.y < viewport.height + box.height;
100
+ }
101
+ else {
102
+ result.elementInViewport = true; // Can't check, assume true
103
+ }
104
+ }
105
+ }
106
+ catch (err) {
107
+ const msg = err instanceof Error ? err.message : String(err);
108
+ logger.logger.debug(`[Visual] Could not get bounding box: ${msg}`);
109
+ }
110
+ // 4. Capture element screenshot
111
+ if (this.config.captureElement) {
112
+ try {
113
+ fileUtils.ensureDirectory(this.config.screenshotDir);
114
+ const sanitizedId = eventId.replace(/[^a-zA-Z0-9_-]/g, '_');
115
+ const elementPath = `${this.config.screenshotDir}/${sanitizedId}_element.png`;
116
+ await element.screenshot({
117
+ path: elementPath,
118
+ timeout: 5000,
119
+ });
120
+ result.elementScreenshotPath = elementPath;
121
+ logger.logger.debug(`[Visual] Element screenshot saved: ${elementPath}`);
122
+ }
123
+ catch (err) {
124
+ const msg = err instanceof Error ? err.message : String(err);
125
+ logger.logger.debug(`[Visual] Element screenshot failed: ${msg}`);
126
+ }
127
+ }
128
+ // 5. Capture full page screenshot for context
129
+ if (this.config.captureFullPage) {
130
+ try {
131
+ fileUtils.ensureDirectory(this.config.screenshotDir);
132
+ const sanitizedId = eventId.replace(/[^a-zA-Z0-9_-]/g, '_');
133
+ const pagePath = `${this.config.screenshotDir}/${sanitizedId}_page.png`;
134
+ await page.screenshot({
135
+ path: pagePath,
136
+ fullPage: false,
137
+ timeout: 10000,
138
+ });
139
+ result.fullPageScreenshotPath = pagePath;
140
+ logger.logger.debug(`[Visual] Full page screenshot saved: ${pagePath}`);
141
+ }
142
+ catch (err) {
143
+ const msg = err instanceof Error ? err.message : String(err);
144
+ logger.logger.debug(`[Visual] Full page screenshot failed: ${msg}`);
145
+ }
146
+ }
147
+ // 6. Final verification decision
148
+ result.verified = result.elementVisible && result.boundingBox !== null;
149
+ if (result.verified) {
150
+ logger.logger.info(`[Visual] Verification passed for "${healedLocator.selector}" ` +
151
+ `(${result.boundingBox.width}x${result.boundingBox.height} at ${result.boundingBox.x},${result.boundingBox.y})`);
152
+ }
153
+ else {
154
+ logger.logger.warn(`[Visual] Verification failed for "${healedLocator.selector}" ` +
155
+ `(visible=${result.elementVisible}, box=${result.boundingBox !== null})`);
156
+ }
157
+ return result;
158
+ }
159
+ catch (err) {
160
+ const msg = err instanceof Error ? err.message : String(err);
161
+ logger.logger.warn(`[Visual] Verification error: ${msg}`);
162
+ return result;
163
+ }
164
+ }
165
+ /**
166
+ * Compare two screenshots at the pixel level.
167
+ * Returns a similarity score (0-1).
168
+ *
169
+ * This is a lightweight comparison using a sampling approach
170
+ * (no external image processing libraries required).
171
+ */
172
+ async compareScreenshots(page, screenshotA, screenshotB) {
173
+ // Use page.evaluate to leverage canvas API for comparison
174
+ try {
175
+ const similarity = await page.evaluate(async ({ a, b }) => {
176
+ // Create images from buffers
177
+ const loadImage = (data) => {
178
+ return new Promise((resolve, reject) => {
179
+ const blob = new Blob([new Uint8Array(data)], { type: 'image/png' });
180
+ const url = URL.createObjectURL(blob);
181
+ const img = new Image();
182
+ img.onload = () => {
183
+ URL.revokeObjectURL(url);
184
+ resolve(img);
185
+ };
186
+ img.onerror = reject;
187
+ img.src = url;
188
+ });
189
+ };
190
+ const imgA = await loadImage(a);
191
+ const imgB = await loadImage(b);
192
+ // Create canvas for comparison
193
+ const canvas = document.createElement('canvas');
194
+ const ctx = canvas.getContext('2d');
195
+ if (!ctx)
196
+ return 0;
197
+ const width = Math.min(imgA.width, imgB.width);
198
+ const height = Math.min(imgA.height, imgB.height);
199
+ canvas.width = width;
200
+ canvas.height = height;
201
+ // Draw and get pixel data for A
202
+ ctx.drawImage(imgA, 0, 0);
203
+ const dataA = ctx.getImageData(0, 0, width, height).data;
204
+ // Draw and get pixel data for B
205
+ ctx.clearRect(0, 0, width, height);
206
+ ctx.drawImage(imgB, 0, 0);
207
+ const dataB = ctx.getImageData(0, 0, width, height).data;
208
+ // Sample pixels for comparison (every 4th pixel for speed)
209
+ let matchCount = 0;
210
+ let sampleCount = 0;
211
+ const step = 16; // Sample every 16 bytes (4 RGBA channels × 4 pixels)
212
+ for (let i = 0; i < dataA.length; i += step) {
213
+ const diffR = Math.abs(dataA[i] - dataB[i]);
214
+ const diffG = Math.abs(dataA[i + 1] - dataB[i + 1]);
215
+ const diffB = Math.abs(dataA[i + 2] - dataB[i + 2]);
216
+ const avgDiff = (diffR + diffG + diffB) / (3 * 255);
217
+ if (avgDiff < 0.1)
218
+ matchCount++;
219
+ sampleCount++;
220
+ }
221
+ return sampleCount > 0 ? matchCount / sampleCount : 0;
222
+ }, { a: Array.from(screenshotA), b: Array.from(screenshotB) });
223
+ return similarity;
224
+ }
225
+ catch (err) {
226
+ const msg = err instanceof Error ? err.message : String(err);
227
+ logger.logger.debug(`[Visual] Screenshot comparison failed: ${msg}`);
228
+ return 0;
229
+ }
230
+ }
231
+ /**
232
+ * Cleanup old screenshots if keepScreenshots is false.
233
+ */
234
+ cleanup() {
235
+ if (this.config.keepScreenshots)
236
+ return;
237
+ try {
238
+ const { readdirSync, unlinkSync } = require('fs');
239
+ const { join } = require('path');
240
+ if (!require('fs').existsSync(this.config.screenshotDir))
241
+ return;
242
+ const files = readdirSync(this.config.screenshotDir);
243
+ for (const file of files) {
244
+ if (file.endsWith('.png')) {
245
+ try {
246
+ unlinkSync(join(this.config.screenshotDir, file));
247
+ }
248
+ catch {
249
+ // Ignore cleanup errors
250
+ }
251
+ }
252
+ }
253
+ logger.logger.debug('[Visual] Cleaned up verification screenshots');
254
+ }
255
+ catch {
256
+ // Ignore
257
+ }
258
+ }
259
+ }
260
+
261
+ exports.VisualVerifier = VisualVerifier;
262
+ //# sourceMappingURL=visual-verification.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"visual-verification.js","sources":["../../../../src/core/visual-verification.ts"],"sourcesContent":[null],"names":["logger","ensureDirectory"],"mappings":"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"}
@@ -0,0 +1,184 @@
1
+ 'use strict';
2
+
3
+ var tsMorph = require('ts-morph');
4
+ var logger = require('../utils/logger.js');
5
+
6
+ /**
7
+ * AST-based source code modifier that updates Playwright locators in test files.
8
+ * Uses ts-morph to parse and manipulate TypeScript ASTs so that only the target
9
+ * locator expression is replaced while surrounding code is fully preserved.
10
+ */
11
+ class CodeModifier {
12
+ constructor() {
13
+ this.project = new tsMorph.Project({
14
+ compilerOptions: { strict: true },
15
+ useInMemoryFileSystem: false,
16
+ });
17
+ }
18
+ // ── Public API ──────────────────────────────────────────────────────────────
19
+ /**
20
+ * Modify a single locator in the source file described by `modification`.
21
+ */
22
+ async modifyLocator(modification) {
23
+ const { filePath, line, originalCode, modifiedCode } = modification;
24
+ logger.logger.info(`Modifying locator in ${filePath}:${line}`);
25
+ logger.logger.debug('Original locator', originalCode);
26
+ logger.logger.debug('New locator', modifiedCode);
27
+ try {
28
+ const sourceFile = this.getOrAddSourceFile(filePath);
29
+ const text = sourceFile.getFullText();
30
+ // Strategy 1: Try AST-based replacement for known Playwright patterns.
31
+ const replaced = this.tryASTReplace(text, line, originalCode, modifiedCode);
32
+ if (replaced !== null) {
33
+ sourceFile.replaceWithText(replaced);
34
+ }
35
+ else {
36
+ // Strategy 2: Fall back to direct text replacement scoped to the target line.
37
+ const lineReplaced = this.replaceAtLine(text, line, originalCode, modifiedCode);
38
+ sourceFile.replaceWithText(lineReplaced);
39
+ }
40
+ await sourceFile.save();
41
+ logger.logger.info(`Locator updated in ${filePath}:${line}`);
42
+ }
43
+ catch (error) {
44
+ const message = error instanceof Error ? error.message : String(error);
45
+ logger.logger.error(`[MindHeal] Failed to modify locator in ${filePath}:${line}: ${message}`);
46
+ throw new Error(`[MindHeal] Failed to modify locator in ${filePath}:${line}: ${message}`);
47
+ }
48
+ }
49
+ /**
50
+ * Generate a human-readable unified-diff-style string for a single modification.
51
+ */
52
+ generateDiff(modification) {
53
+ const { filePath, line, originalCode, modifiedCode } = modification;
54
+ const header = `--- a/${filePath}\n+++ b/${filePath}`;
55
+ const hunk = `@@ -${line},1 +${line},1 @@`;
56
+ const removal = `- ${originalCode}`;
57
+ const addition = `+ ${modifiedCode}`;
58
+ return [header, hunk, removal, addition].join('\n');
59
+ }
60
+ /**
61
+ * Apply multiple modifications, grouping them by file to minimize I/O.
62
+ * Modifications within the same file are applied from bottom to top
63
+ * (descending line number) so that earlier line numbers stay valid.
64
+ */
65
+ async applyAllModifications(modifications) {
66
+ if (modifications.length === 0) {
67
+ logger.logger.warn('[MindHeal] No modifications to apply');
68
+ return;
69
+ }
70
+ // Group by file path.
71
+ const grouped = new Map();
72
+ for (const mod of modifications) {
73
+ const existing = grouped.get(mod.filePath) ?? [];
74
+ existing.push(mod);
75
+ grouped.set(mod.filePath, existing);
76
+ }
77
+ logger.logger.info(`Applying ${modifications.length} modification(s) across ${grouped.size} file(s)`);
78
+ for (const [filePath, fileMods] of grouped) {
79
+ // Sort descending by line so replacements don't shift earlier positions.
80
+ const sorted = [...fileMods].sort((a, b) => b.line - a.line);
81
+ for (const mod of sorted) {
82
+ try {
83
+ await this.modifyLocator(mod);
84
+ }
85
+ catch (error) {
86
+ const message = error instanceof Error ? error.message : String(error);
87
+ logger.logger.error(`[MindHeal] Skipping failed modification in ${filePath}:${mod.line}: ${message}`);
88
+ // Continue with remaining modifications in the same file.
89
+ }
90
+ }
91
+ }
92
+ logger.logger.info('All modifications applied');
93
+ }
94
+ // ── Private helpers ─────────────────────────────────────────────────────────
95
+ /**
96
+ * Add (or retrieve) a ts-morph SourceFile for the given path.
97
+ */
98
+ getOrAddSourceFile(filePath) {
99
+ const existing = this.project.getSourceFile(filePath);
100
+ if (existing) {
101
+ // Refresh from disk in case a prior modification changed it.
102
+ existing.refreshFromFileSystemSync();
103
+ return existing;
104
+ }
105
+ return this.project.addSourceFileAtPath(filePath);
106
+ }
107
+ /**
108
+ * Attempt to locate the original locator expression in the AST at the given
109
+ * line and replace it. Returns the new full text on success or `null` if the
110
+ * expression could not be found via AST traversal.
111
+ *
112
+ * Supports Playwright patterns:
113
+ * page.locator('selector')
114
+ * page.getByRole('role', { name: 'value' })
115
+ * page.getByText('text')
116
+ * page.getByTestId('id')
117
+ * Chained: page.locator('parent').locator('child')
118
+ */
119
+ tryASTReplace(fullText, targetLine, originalCode, modifiedCode) {
120
+ // Create a temporary in-memory source for AST analysis without side-effects.
121
+ const tempFile = this.project.createSourceFile('__temp_analysis__.ts', fullText, {
122
+ overwrite: true,
123
+ });
124
+ try {
125
+ const calls = tempFile.getDescendantsOfKind(tsMorph.SyntaxKind.CallExpression);
126
+ for (const call of calls) {
127
+ const startLine = call.getStartLineNumber();
128
+ if (startLine !== targetLine)
129
+ continue;
130
+ const callText = call.getText();
131
+ // Check if this call (or its ancestor chain) matches the original code.
132
+ if (this.normalizeWhitespace(callText) === this.normalizeWhitespace(originalCode)) {
133
+ const start = call.getStart();
134
+ const end = call.getEnd();
135
+ return fullText.substring(0, start) + modifiedCode + fullText.substring(end);
136
+ }
137
+ // The original code might be a parent expression that contains this call
138
+ // (e.g., chained locators). Walk upward.
139
+ let ancestor = call.getParent();
140
+ while (ancestor) {
141
+ if (ancestor.getStartLineNumber() === targetLine &&
142
+ this.normalizeWhitespace(ancestor.getText()) ===
143
+ this.normalizeWhitespace(originalCode)) {
144
+ const start = ancestor.getStart();
145
+ const end = ancestor.getEnd();
146
+ return fullText.substring(0, start) + modifiedCode + fullText.substring(end);
147
+ }
148
+ ancestor = ancestor.getParent();
149
+ }
150
+ }
151
+ return null;
152
+ }
153
+ finally {
154
+ this.project.removeSourceFile(tempFile);
155
+ }
156
+ }
157
+ /**
158
+ * Fallback: perform a direct string replacement on the specific line of `fullText`.
159
+ * Only the first occurrence on the target line is replaced to avoid unintended changes.
160
+ */
161
+ replaceAtLine(fullText, targetLine, originalCode, modifiedCode) {
162
+ const lines = fullText.split('\n');
163
+ const lineIndex = targetLine - 1; // lines are 1-based
164
+ if (lineIndex < 0 || lineIndex >= lines.length) {
165
+ throw new Error(`[MindHeal] Line ${targetLine} is out of range (file has ${lines.length} lines)`);
166
+ }
167
+ const lineText = lines[lineIndex];
168
+ if (!lineText.includes(originalCode)) {
169
+ throw new Error(`[MindHeal] Original locator not found on line ${targetLine}. ` +
170
+ `Expected to find: ${originalCode}`);
171
+ }
172
+ lines[lineIndex] = lineText.replace(originalCode, modifiedCode);
173
+ return lines.join('\n');
174
+ }
175
+ /**
176
+ * Collapse all whitespace to single spaces for comparison purposes.
177
+ */
178
+ normalizeWhitespace(str) {
179
+ return str.replace(/\s+/g, ' ').trim();
180
+ }
181
+ }
182
+
183
+ exports.CodeModifier = CodeModifier;
184
+ //# sourceMappingURL=code-modifier.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"code-modifier.js","sources":["../../../../src/git/code-modifier.ts"],"sourcesContent":[null],"names":["Project","logger","SyntaxKind"],"mappings":"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"}
@@ -0,0 +1,145 @@
1
+ 'use strict';
2
+
3
+ var simpleGit = require('simple-git');
4
+ var logger = require('../utils/logger.js');
5
+
6
+ class GitOperations {
7
+ constructor(config) {
8
+ this.config = config;
9
+ this.git = simpleGit();
10
+ }
11
+ /**
12
+ * Generate a branch name using the configured prefix and current timestamp.
13
+ * Format: {prefix}-{YYYYMMDD}-{HHmmss}
14
+ */
15
+ generateBranchName() {
16
+ const prefix = this.config.branchPrefix ?? 'mindheal/auto-fix';
17
+ const now = new Date();
18
+ const pad = (n) => String(n).padStart(2, '0');
19
+ const timestamp = [
20
+ now.getFullYear(),
21
+ pad(now.getMonth() + 1),
22
+ pad(now.getDate()),
23
+ '-',
24
+ pad(now.getHours()),
25
+ pad(now.getMinutes()),
26
+ pad(now.getSeconds()),
27
+ ].join('');
28
+ return `${prefix}-${timestamp}`;
29
+ }
30
+ /**
31
+ * Create a new branch and check it out.
32
+ */
33
+ async createBranch(branchName) {
34
+ try {
35
+ logger.logger.info(`Creating and checking out branch: ${branchName}`);
36
+ await this.git.checkoutLocalBranch(branchName);
37
+ logger.logger.info(`Branch created: ${branchName}`);
38
+ }
39
+ catch (error) {
40
+ const message = error instanceof Error ? error.message : String(error);
41
+ logger.logger.error(`[MindHeal] Failed to create branch "${branchName}": ${message}`);
42
+ throw new Error(`[MindHeal] Failed to create branch "${branchName}": ${message}`);
43
+ }
44
+ }
45
+ /**
46
+ * Stage specified files and create a commit.
47
+ */
48
+ async commitChanges(files, message) {
49
+ if (files.length === 0) {
50
+ logger.logger.warn('[MindHeal] No files provided for commit, skipping');
51
+ return;
52
+ }
53
+ try {
54
+ const prefix = this.config.commitMessagePrefix ?? '[MindHeal]';
55
+ const fullMessage = `${prefix} ${message}`;
56
+ logger.logger.info(`Staging ${files.length} file(s) for commit`);
57
+ await this.git.add(files);
58
+ logger.logger.info(`Committing with message: ${fullMessage}`);
59
+ await this.git.commit(fullMessage);
60
+ logger.logger.info('Commit successful');
61
+ }
62
+ catch (error) {
63
+ const message_ = error instanceof Error ? error.message : String(error);
64
+ logger.logger.error(`[MindHeal] Failed to commit changes: ${message_}`);
65
+ throw new Error(`[MindHeal] Failed to commit changes: ${message_}`);
66
+ }
67
+ }
68
+ /**
69
+ * Push a branch to the remote origin.
70
+ */
71
+ async pushBranch(branchName) {
72
+ try {
73
+ logger.logger.info(`Pushing branch "${branchName}" to origin`);
74
+ await this.git.push('origin', branchName, ['--set-upstream']);
75
+ logger.logger.info(`Branch "${branchName}" pushed successfully`);
76
+ }
77
+ catch (error) {
78
+ const message = error instanceof Error ? error.message : String(error);
79
+ logger.logger.error(`[MindHeal] Failed to push branch "${branchName}": ${message}`);
80
+ throw new Error(`[MindHeal] Failed to push branch "${branchName}": ${message}`);
81
+ }
82
+ }
83
+ /**
84
+ * Get the name of the currently checked-out branch.
85
+ */
86
+ async getCurrentBranch() {
87
+ try {
88
+ const branchSummary = await this.git.branchLocal();
89
+ return branchSummary.current;
90
+ }
91
+ catch (error) {
92
+ const message = error instanceof Error ? error.message : String(error);
93
+ logger.logger.error(`[MindHeal] Failed to get current branch: ${message}`);
94
+ throw new Error(`[MindHeal] Failed to get current branch: ${message}`);
95
+ }
96
+ }
97
+ /**
98
+ * Switch to an existing branch.
99
+ */
100
+ async switchBranch(branchName) {
101
+ try {
102
+ logger.logger.info(`Switching to branch: ${branchName}`);
103
+ await this.git.checkout(branchName);
104
+ logger.logger.info(`Switched to branch: ${branchName}`);
105
+ }
106
+ catch (error) {
107
+ const message = error instanceof Error ? error.message : String(error);
108
+ logger.logger.error(`[MindHeal] Failed to switch to branch "${branchName}": ${message}`);
109
+ throw new Error(`[MindHeal] Failed to switch to branch "${branchName}": ${message}`);
110
+ }
111
+ }
112
+ /**
113
+ * Stash all uncommitted changes (tracked and untracked).
114
+ */
115
+ async stashChanges() {
116
+ try {
117
+ logger.logger.info('Stashing uncommitted changes');
118
+ await this.git.stash(['push', '--include-untracked']);
119
+ logger.logger.info('Changes stashed');
120
+ }
121
+ catch (error) {
122
+ const message = error instanceof Error ? error.message : String(error);
123
+ logger.logger.error(`[MindHeal] Failed to stash changes: ${message}`);
124
+ throw new Error(`[MindHeal] Failed to stash changes: ${message}`);
125
+ }
126
+ }
127
+ /**
128
+ * Pop the most recent stash entry.
129
+ */
130
+ async popStash() {
131
+ try {
132
+ logger.logger.info('Popping stashed changes');
133
+ await this.git.stash(['pop']);
134
+ logger.logger.info('Stash applied and dropped');
135
+ }
136
+ catch (error) {
137
+ const message = error instanceof Error ? error.message : String(error);
138
+ logger.logger.error(`[MindHeal] Failed to pop stash: ${message}`);
139
+ throw new Error(`[MindHeal] Failed to pop stash: ${message}`);
140
+ }
141
+ }
142
+ }
143
+
144
+ exports.GitOperations = GitOperations;
145
+ //# sourceMappingURL=git-operations.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"git-operations.js","sources":["../../../../src/git/git-operations.ts"],"sourcesContent":[null],"names":["logger"],"mappings":";;;;;MAIa,aAAa,CAAA;AAIxB,IAAA,WAAA,CAAY,MAAiB,EAAA;AAC3B,QAAA,IAAI,CAAC,MAAM,GAAG,MAAM;AACpB,QAAA,IAAI,CAAC,GAAG,GAAG,SAAS,EAAE;IACxB;AAEA;;;AAGG;IACI,kBAAkB,GAAA;QACvB,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,YAAY,IAAI,mBAAmB;AAC9D,QAAA,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE;AACtB,QAAA,MAAM,GAAG,GAAG,CAAC,CAAS,KAAa,MAAM,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC;AAC7D,QAAA,MAAM,SAAS,GAAG;YAChB,GAAG,CAAC,WAAW,EAAE;AACjB,YAAA,GAAG,CAAC,GAAG,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC;AACvB,YAAA,GAAG,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC;YAClB,GAAG;AACH,YAAA,GAAG,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAC;AACnB,YAAA,GAAG,CAAC,GAAG,CAAC,UAAU,EAAE,CAAC;AACrB,YAAA,GAAG,CAAC,GAAG,CAAC,UAAU,EAAE,CAAC;AACtB,SAAA,CAAC,IAAI,CAAC,EAAE,CAAC;AAEV,QAAA,OAAO,CAAA,EAAG,MAAM,CAAA,CAAA,EAAI,SAAS,EAAE;IACjC;AAEA;;AAEG;IACI,MAAM,YAAY,CAAC,UAAkB,EAAA;AAC1C,QAAA,IAAI;AACF,YAAAA,aAAM,CAAC,IAAI,CAAC,qCAAqC,UAAU,CAAA,CAAE,CAAC;YAC9D,MAAM,IAAI,CAAC,GAAG,CAAC,mBAAmB,CAAC,UAAU,CAAC;AAC9C,YAAAA,aAAM,CAAC,IAAI,CAAC,mBAAmB,UAAU,CAAA,CAAE,CAAC;QAC9C;QAAE,OAAO,KAAK,EAAE;AACd,YAAA,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,GAAG,KAAK,CAAC,OAAO,GAAG,MAAM,CAAC,KAAK,CAAC;YACtEA,aAAM,CAAC,KAAK,CAAC,CAAA,oCAAA,EAAuC,UAAU,CAAA,GAAA,EAAM,OAAO,CAAA,CAAE,CAAC;YAC9E,MAAM,IAAI,KAAK,CAAC,CAAA,oCAAA,EAAuC,UAAU,CAAA,GAAA,EAAM,OAAO,CAAA,CAAE,CAAC;QACnF;IACF;AAEA;;AAEG;AACI,IAAA,MAAM,aAAa,CAAC,KAAe,EAAE,OAAe,EAAA;AACzD,QAAA,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE;AACtB,YAAAA,aAAM,CAAC,IAAI,CAAC,mDAAmD,CAAC;YAChE;QACF;AAEA,QAAA,IAAI;YACF,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,mBAAmB,IAAI,YAAY;AAC9D,YAAA,MAAM,WAAW,GAAG,CAAA,EAAG,MAAM,CAAA,CAAA,EAAI,OAAO,EAAE;YAE1CA,aAAM,CAAC,IAAI,CAAC,CAAA,QAAA,EAAW,KAAK,CAAC,MAAM,CAAA,mBAAA,CAAqB,CAAC;YACzD,MAAM,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC;AAEzB,YAAAA,aAAM,CAAC,IAAI,CAAC,4BAA4B,WAAW,CAAA,CAAE,CAAC;YACtD,MAAM,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,WAAW,CAAC;AAClC,YAAAA,aAAM,CAAC,IAAI,CAAC,mBAAmB,CAAC;QAClC;QAAE,OAAO,KAAK,EAAE;AACd,YAAA,MAAM,QAAQ,GAAG,KAAK,YAAY,KAAK,GAAG,KAAK,CAAC,OAAO,GAAG,MAAM,CAAC,KAAK,CAAC;AACvE,YAAAA,aAAM,CAAC,KAAK,CAAC,wCAAwC,QAAQ,CAAA,CAAE,CAAC;AAChE,YAAA,MAAM,IAAI,KAAK,CAAC,wCAAwC,QAAQ,CAAA,CAAE,CAAC;QACrE;IACF;AAEA;;AAEG;IACI,MAAM,UAAU,CAAC,UAAkB,EAAA;AACxC,QAAA,IAAI;AACF,YAAAA,aAAM,CAAC,IAAI,CAAC,mBAAmB,UAAU,CAAA,WAAA,CAAa,CAAC;AACvD,YAAA,MAAM,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,QAAQ,EAAE,UAAU,EAAE,CAAC,gBAAgB,CAAC,CAAC;AAC7D,YAAAA,aAAM,CAAC,IAAI,CAAC,WAAW,UAAU,CAAA,qBAAA,CAAuB,CAAC;QAC3D;QAAE,OAAO,KAAK,EAAE;AACd,YAAA,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,GAAG,KAAK,CAAC,OAAO,GAAG,MAAM,CAAC,KAAK,CAAC;YACtEA,aAAM,CAAC,KAAK,CAAC,CAAA,kCAAA,EAAqC,UAAU,CAAA,GAAA,EAAM,OAAO,CAAA,CAAE,CAAC;YAC5E,MAAM,IAAI,KAAK,CAAC,CAAA,kCAAA,EAAqC,UAAU,CAAA,GAAA,EAAM,OAAO,CAAA,CAAE,CAAC;QACjF;IACF;AAEA;;AAEG;AACI,IAAA,MAAM,gBAAgB,GAAA;AAC3B,QAAA,IAAI;YACF,MAAM,aAAa,GAAG,MAAM,IAAI,CAAC,GAAG,CAAC,WAAW,EAAE;YAClD,OAAO,aAAa,CAAC,OAAO;QAC9B;QAAE,OAAO,KAAK,EAAE;AACd,YAAA,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,GAAG,KAAK,CAAC,OAAO,GAAG,MAAM,CAAC,KAAK,CAAC;AACtE,YAAAA,aAAM,CAAC,KAAK,CAAC,4CAA4C,OAAO,CAAA,CAAE,CAAC;AACnE,YAAA,MAAM,IAAI,KAAK,CAAC,4CAA4C,OAAO,CAAA,CAAE,CAAC;QACxE;IACF;AAEA;;AAEG;IACI,MAAM,YAAY,CAAC,UAAkB,EAAA;AAC1C,QAAA,IAAI;AACF,YAAAA,aAAM,CAAC,IAAI,CAAC,wBAAwB,UAAU,CAAA,CAAE,CAAC;YACjD,MAAM,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,UAAU,CAAC;AACnC,YAAAA,aAAM,CAAC,IAAI,CAAC,uBAAuB,UAAU,CAAA,CAAE,CAAC;QAClD;QAAE,OAAO,KAAK,EAAE;AACd,YAAA,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,GAAG,KAAK,CAAC,OAAO,GAAG,MAAM,CAAC,KAAK,CAAC;YACtEA,aAAM,CAAC,KAAK,CAAC,CAAA,uCAAA,EAA0C,UAAU,CAAA,GAAA,EAAM,OAAO,CAAA,CAAE,CAAC;YACjF,MAAM,IAAI,KAAK,CAAC,CAAA,uCAAA,EAA0C,UAAU,CAAA,GAAA,EAAM,OAAO,CAAA,CAAE,CAAC;QACtF;IACF;AAEA;;AAEG;AACI,IAAA,MAAM,YAAY,GAAA;AACvB,QAAA,IAAI;AACF,YAAAA,aAAM,CAAC,IAAI,CAAC,8BAA8B,CAAC;AAC3C,YAAA,MAAM,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,MAAM,EAAE,qBAAqB,CAAC,CAAC;AACrD,YAAAA,aAAM,CAAC,IAAI,CAAC,iBAAiB,CAAC;QAChC;QAAE,OAAO,KAAK,EAAE;AACd,YAAA,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,GAAG,KAAK,CAAC,OAAO,GAAG,MAAM,CAAC,KAAK,CAAC;AACtE,YAAAA,aAAM,CAAC,KAAK,CAAC,uCAAuC,OAAO,CAAA,CAAE,CAAC;AAC9D,YAAA,MAAM,IAAI,KAAK,CAAC,uCAAuC,OAAO,CAAA,CAAE,CAAC;QACnE;IACF;AAEA;;AAEG;AACI,IAAA,MAAM,QAAQ,GAAA;AACnB,QAAA,IAAI;AACF,YAAAA,aAAM,CAAC,IAAI,CAAC,yBAAyB,CAAC;YACtC,MAAM,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC;AAC7B,YAAAA,aAAM,CAAC,IAAI,CAAC,2BAA2B,CAAC;QAC1C;QAAE,OAAO,KAAK,EAAE;AACd,YAAA,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,GAAG,KAAK,CAAC,OAAO,GAAG,MAAM,CAAC,KAAK,CAAC;AACtE,YAAAA,aAAM,CAAC,KAAK,CAAC,mCAAmC,OAAO,CAAA,CAAE,CAAC;AAC1D,YAAA,MAAM,IAAI,KAAK,CAAC,mCAAmC,OAAO,CAAA,CAAE,CAAC;QAC/D;IACF;AACD;;;;"}