sela-core 0.1.0-alpha.1

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 (267) hide show
  1. package/README.md +103 -0
  2. package/bin/sela.js +3 -0
  3. package/dist/cli/ErrorHandler.d.ts +10 -0
  4. package/dist/cli/ErrorHandler.d.ts.map +1 -0
  5. package/dist/cli/ErrorHandler.js +70 -0
  6. package/dist/cli/commands/bulk.d.ts +3 -0
  7. package/dist/cli/commands/bulk.d.ts.map +1 -0
  8. package/dist/cli/commands/bulk.js +158 -0
  9. package/dist/cli/commands/find.d.ts +3 -0
  10. package/dist/cli/commands/find.d.ts.map +1 -0
  11. package/dist/cli/commands/find.js +51 -0
  12. package/dist/cli/commands/init.d.ts +3 -0
  13. package/dist/cli/commands/init.d.ts.map +1 -0
  14. package/dist/cli/commands/init.js +166 -0
  15. package/dist/cli/commands/list.d.ts +3 -0
  16. package/dist/cli/commands/list.d.ts.map +1 -0
  17. package/dist/cli/commands/list.js +56 -0
  18. package/dist/cli/commands/merge.d.ts +13 -0
  19. package/dist/cli/commands/merge.d.ts.map +1 -0
  20. package/dist/cli/commands/merge.js +119 -0
  21. package/dist/cli/commands/refactor.d.ts +3 -0
  22. package/dist/cli/commands/refactor.d.ts.map +1 -0
  23. package/dist/cli/commands/refactor.js +30 -0
  24. package/dist/cli/commands/showReport.d.ts +3 -0
  25. package/dist/cli/commands/showReport.d.ts.map +1 -0
  26. package/dist/cli/commands/showReport.js +82 -0
  27. package/dist/cli/commands/status.d.ts +3 -0
  28. package/dist/cli/commands/status.d.ts.map +1 -0
  29. package/dist/cli/commands/status.js +51 -0
  30. package/dist/cli/commands/sync.d.ts +3 -0
  31. package/dist/cli/commands/sync.d.ts.map +1 -0
  32. package/dist/cli/commands/sync.js +131 -0
  33. package/dist/cli/index.d.ts +2 -0
  34. package/dist/cli/index.d.ts.map +1 -0
  35. package/dist/cli/index.js +94 -0
  36. package/dist/cli/ui/DnaTable.d.ts +3 -0
  37. package/dist/cli/ui/DnaTable.d.ts.map +1 -0
  38. package/dist/cli/ui/DnaTable.js +77 -0
  39. package/dist/cli/ui/LocatorPicker.d.ts +8 -0
  40. package/dist/cli/ui/LocatorPicker.d.ts.map +1 -0
  41. package/dist/cli/ui/LocatorPicker.js +33 -0
  42. package/dist/cli/ui/ProgressReporter.d.ts +11 -0
  43. package/dist/cli/ui/ProgressReporter.d.ts.map +1 -0
  44. package/dist/cli/ui/ProgressReporter.js +33 -0
  45. package/dist/cli/ui/RefactorWizard.d.ts +26 -0
  46. package/dist/cli/ui/RefactorWizard.d.ts.map +1 -0
  47. package/dist/cli/ui/RefactorWizard.js +295 -0
  48. package/dist/config/ConfigLoader.d.ts +16 -0
  49. package/dist/config/ConfigLoader.d.ts.map +1 -0
  50. package/dist/config/ConfigLoader.js +185 -0
  51. package/dist/config/DryRunGuard.d.ts +14 -0
  52. package/dist/config/DryRunGuard.d.ts.map +1 -0
  53. package/dist/config/DryRunGuard.js +80 -0
  54. package/dist/config/PathScopeGuard.d.ts +42 -0
  55. package/dist/config/PathScopeGuard.d.ts.map +1 -0
  56. package/dist/config/PathScopeGuard.js +192 -0
  57. package/dist/config/SelaConfig.d.ts +178 -0
  58. package/dist/config/SelaConfig.d.ts.map +1 -0
  59. package/dist/config/SelaConfig.js +141 -0
  60. package/dist/config/defineConfig.d.ts +3 -0
  61. package/dist/config/defineConfig.d.ts.map +1 -0
  62. package/dist/config/defineConfig.js +9 -0
  63. package/dist/engine/FixwrightEngine.d.ts +24 -0
  64. package/dist/engine/FixwrightEngine.d.ts.map +1 -0
  65. package/dist/engine/FixwrightEngine.js +403 -0
  66. package/dist/engine/HealingRegistry.d.ts +40 -0
  67. package/dist/engine/HealingRegistry.d.ts.map +1 -0
  68. package/dist/engine/HealingRegistry.js +99 -0
  69. package/dist/engine/SelaEngine.d.ts +57 -0
  70. package/dist/engine/SelaEngine.d.ts.map +1 -0
  71. package/dist/engine/SelaEngine.js +1146 -0
  72. package/dist/engine/singleton.d.ts +3 -0
  73. package/dist/engine/singleton.d.ts.map +1 -0
  74. package/dist/engine/singleton.js +5 -0
  75. package/dist/errors/SelaError.d.ts +146 -0
  76. package/dist/errors/SelaError.d.ts.map +1 -0
  77. package/dist/errors/SelaError.js +168 -0
  78. package/dist/fixtures/expectProxy.d.ts +12 -0
  79. package/dist/fixtures/expectProxy.d.ts.map +1 -0
  80. package/dist/fixtures/expectProxy.js +246 -0
  81. package/dist/fixtures/index.d.ts +30 -0
  82. package/dist/fixtures/index.d.ts.map +1 -0
  83. package/dist/fixtures/index.js +765 -0
  84. package/dist/fixtures/moduleExpect.d.ts +2 -0
  85. package/dist/fixtures/moduleExpect.d.ts.map +1 -0
  86. package/dist/fixtures/moduleExpect.js +54 -0
  87. package/dist/fixtures/proxyTag.d.ts +12 -0
  88. package/dist/fixtures/proxyTag.d.ts.map +1 -0
  89. package/dist/fixtures/proxyTag.js +45 -0
  90. package/dist/index.d.ts +9 -0
  91. package/dist/index.d.ts.map +1 -0
  92. package/dist/index.js +31 -0
  93. package/dist/reporter/SelaReporter.d.ts +165 -0
  94. package/dist/reporter/SelaReporter.d.ts.map +1 -0
  95. package/dist/reporter/SelaReporter.js +517 -0
  96. package/dist/services/ASTSourceUpdater.d.ts +143 -0
  97. package/dist/services/ASTSourceUpdater.d.ts.map +1 -0
  98. package/dist/services/ASTSourceUpdater.js +3514 -0
  99. package/dist/services/AnchorResolver.d.ts +157 -0
  100. package/dist/services/AnchorResolver.d.ts.map +1 -0
  101. package/dist/services/AnchorResolver.js +289 -0
  102. package/dist/services/ArgumentTypeAnalyzer.d.ts +26 -0
  103. package/dist/services/ArgumentTypeAnalyzer.d.ts.map +1 -0
  104. package/dist/services/ArgumentTypeAnalyzer.js +92 -0
  105. package/dist/services/BlastRadiusAnalyzer.d.ts +15 -0
  106. package/dist/services/BlastRadiusAnalyzer.d.ts.map +1 -0
  107. package/dist/services/BlastRadiusAnalyzer.js +107 -0
  108. package/dist/services/ChainValidator.d.ts +76 -0
  109. package/dist/services/ChainValidator.d.ts.map +1 -0
  110. package/dist/services/ChainValidator.js +570 -0
  111. package/dist/services/CrossFileHealer.d.ts +6 -0
  112. package/dist/services/CrossFileHealer.d.ts.map +1 -0
  113. package/dist/services/CrossFileHealer.js +135 -0
  114. package/dist/services/DecisionEngine.d.ts +51 -0
  115. package/dist/services/DecisionEngine.d.ts.map +1 -0
  116. package/dist/services/DecisionEngine.js +260 -0
  117. package/dist/services/DefinitionTracer.d.ts +41 -0
  118. package/dist/services/DefinitionTracer.d.ts.map +1 -0
  119. package/dist/services/DefinitionTracer.js +366 -0
  120. package/dist/services/DnaEditorService.d.ts +31 -0
  121. package/dist/services/DnaEditorService.d.ts.map +1 -0
  122. package/dist/services/DnaEditorService.js +217 -0
  123. package/dist/services/DnaIndexService.d.ts +24 -0
  124. package/dist/services/DnaIndexService.d.ts.map +1 -0
  125. package/dist/services/DnaIndexService.js +131 -0
  126. package/dist/services/HealBudget.d.ts +32 -0
  127. package/dist/services/HealBudget.d.ts.map +1 -0
  128. package/dist/services/HealBudget.js +85 -0
  129. package/dist/services/HealReportService.d.ts +161 -0
  130. package/dist/services/HealReportService.d.ts.map +1 -0
  131. package/dist/services/HealReportService.js +1039 -0
  132. package/dist/services/HealingAdvisory.d.ts +30 -0
  133. package/dist/services/HealingAdvisory.d.ts.map +1 -0
  134. package/dist/services/HealingAdvisory.js +51 -0
  135. package/dist/services/HealingCacheService.d.ts +135 -0
  136. package/dist/services/HealingCacheService.d.ts.map +1 -0
  137. package/dist/services/HealingCacheService.js +404 -0
  138. package/dist/services/HealthReportService.d.ts +10 -0
  139. package/dist/services/HealthReportService.d.ts.map +1 -0
  140. package/dist/services/HealthReportService.js +84 -0
  141. package/dist/services/InitializerUpdater.d.ts +16 -0
  142. package/dist/services/InitializerUpdater.d.ts.map +1 -0
  143. package/dist/services/InitializerUpdater.js +73 -0
  144. package/dist/services/IntentAuditor.d.ts +56 -0
  145. package/dist/services/IntentAuditor.d.ts.map +1 -0
  146. package/dist/services/IntentAuditor.js +326 -0
  147. package/dist/services/InteractiveReview.d.ts +15 -0
  148. package/dist/services/InteractiveReview.d.ts.map +1 -0
  149. package/dist/services/InteractiveReview.js +128 -0
  150. package/dist/services/LLMService.d.ts +121 -0
  151. package/dist/services/LLMService.d.ts.map +1 -0
  152. package/dist/services/LLMService.js +475 -0
  153. package/dist/services/MutationApplier.d.ts +69 -0
  154. package/dist/services/MutationApplier.d.ts.map +1 -0
  155. package/dist/services/MutationApplier.js +360 -0
  156. package/dist/services/PRAutomationService.d.ts +50 -0
  157. package/dist/services/PRAutomationService.d.ts.map +1 -0
  158. package/dist/services/PRAutomationService.js +760 -0
  159. package/dist/services/PendingPromptLedger.d.ts +51 -0
  160. package/dist/services/PendingPromptLedger.d.ts.map +1 -0
  161. package/dist/services/PendingPromptLedger.js +205 -0
  162. package/dist/services/ReportGenerator.d.ts +156 -0
  163. package/dist/services/ReportGenerator.d.ts.map +1 -0
  164. package/dist/services/ReportGenerator.js +278 -0
  165. package/dist/services/ReportMergeService.d.ts +95 -0
  166. package/dist/services/ReportMergeService.d.ts.map +1 -0
  167. package/dist/services/ReportMergeService.js +0 -0
  168. package/dist/services/SafetyGuard.d.ts +112 -0
  169. package/dist/services/SafetyGuard.d.ts.map +1 -0
  170. package/dist/services/SafetyGuard.js +87 -0
  171. package/dist/services/Sanitizer.d.ts +28 -0
  172. package/dist/services/Sanitizer.d.ts.map +1 -0
  173. package/dist/services/Sanitizer.js +142 -0
  174. package/dist/services/SanitizingAnthropic.d.ts +19 -0
  175. package/dist/services/SanitizingAnthropic.d.ts.map +1 -0
  176. package/dist/services/SanitizingAnthropic.js +84 -0
  177. package/dist/services/SelectorSanitizer.d.ts +52 -0
  178. package/dist/services/SelectorSanitizer.d.ts.map +1 -0
  179. package/dist/services/SelectorSanitizer.js +318 -0
  180. package/dist/services/SnapshotService.d.ts +11 -0
  181. package/dist/services/SnapshotService.d.ts.map +1 -0
  182. package/dist/services/SnapshotService.js +352 -0
  183. package/dist/services/SourceLinkService.d.ts +26 -0
  184. package/dist/services/SourceLinkService.d.ts.map +1 -0
  185. package/dist/services/SourceLinkService.js +165 -0
  186. package/dist/services/SourceUpdater.d.ts +45 -0
  187. package/dist/services/SourceUpdater.d.ts.map +1 -0
  188. package/dist/services/SourceUpdater.js +1091 -0
  189. package/dist/services/TemplateDiffService.d.ts +25 -0
  190. package/dist/services/TemplateDiffService.d.ts.map +1 -0
  191. package/dist/services/TemplateDiffService.js +334 -0
  192. package/dist/services/TraceBackEngine.d.ts +67 -0
  193. package/dist/services/TraceBackEngine.d.ts.map +1 -0
  194. package/dist/services/TraceBackEngine.js +672 -0
  195. package/dist/services/WorkspaceSnapshotService.d.ts +71 -0
  196. package/dist/services/WorkspaceSnapshotService.d.ts.map +1 -0
  197. package/dist/services/WorkspaceSnapshotService.js +200 -0
  198. package/dist/services/safety/PipelineContext.d.ts +63 -0
  199. package/dist/services/safety/PipelineContext.d.ts.map +1 -0
  200. package/dist/services/safety/PipelineContext.js +34 -0
  201. package/dist/services/safety/roleClassifier.d.ts +4 -0
  202. package/dist/services/safety/roleClassifier.d.ts.map +1 -0
  203. package/dist/services/safety/roleClassifier.js +233 -0
  204. package/dist/services/safety/rules/ConfidenceTierRule.d.ts +3 -0
  205. package/dist/services/safety/rules/ConfidenceTierRule.d.ts.map +1 -0
  206. package/dist/services/safety/rules/ConfidenceTierRule.js +32 -0
  207. package/dist/services/safety/rules/DangerousSinkRule.d.ts +10 -0
  208. package/dist/services/safety/rules/DangerousSinkRule.d.ts.map +1 -0
  209. package/dist/services/safety/rules/DangerousSinkRule.js +53 -0
  210. package/dist/services/safety/rules/FunctionalRoleRule.d.ts +3 -0
  211. package/dist/services/safety/rules/FunctionalRoleRule.d.ts.map +1 -0
  212. package/dist/services/safety/rules/FunctionalRoleRule.js +64 -0
  213. package/dist/services/safety/rules/HardConfidenceRule.d.ts +3 -0
  214. package/dist/services/safety/rules/HardConfidenceRule.d.ts.map +1 -0
  215. package/dist/services/safety/rules/HardConfidenceRule.js +22 -0
  216. package/dist/services/safety/rules/IntentAuditorRule.d.ts +3 -0
  217. package/dist/services/safety/rules/IntentAuditorRule.d.ts.map +1 -0
  218. package/dist/services/safety/rules/IntentAuditorRule.js +97 -0
  219. package/dist/services/safety/rules/SemanticOppositeRule.d.ts +3 -0
  220. package/dist/services/safety/rules/SemanticOppositeRule.d.ts.map +1 -0
  221. package/dist/services/safety/rules/SemanticOppositeRule.js +30 -0
  222. package/dist/services/safety/rules/StructuralOnlyRule.d.ts +3 -0
  223. package/dist/services/safety/rules/StructuralOnlyRule.d.ts.map +1 -0
  224. package/dist/services/safety/rules/StructuralOnlyRule.js +38 -0
  225. package/dist/services/safety/rules/VisibilityRule.d.ts +3 -0
  226. package/dist/services/safety/rules/VisibilityRule.d.ts.map +1 -0
  227. package/dist/services/safety/rules/VisibilityRule.js +53 -0
  228. package/dist/services/safety/rules/ZeroTrustRule.d.ts +3 -0
  229. package/dist/services/safety/rules/ZeroTrustRule.d.ts.map +1 -0
  230. package/dist/services/safety/rules/ZeroTrustRule.js +37 -0
  231. package/dist/services/safety/rules/index.d.ts +3 -0
  232. package/dist/services/safety/rules/index.d.ts.map +1 -0
  233. package/dist/services/safety/rules/index.js +34 -0
  234. package/dist/services/types.d.ts +59 -0
  235. package/dist/services/types.d.ts.map +1 -0
  236. package/dist/services/types.js +2 -0
  237. package/dist/storage/SnapshotManager.d.ts +5 -0
  238. package/dist/storage/SnapshotManager.d.ts.map +1 -0
  239. package/dist/storage/SnapshotManager.js +32 -0
  240. package/dist/types/index.d.ts +95 -0
  241. package/dist/types/index.d.ts.map +1 -0
  242. package/dist/types/index.js +7 -0
  243. package/dist/utils/DOMUtils.d.ts +49 -0
  244. package/dist/utils/DOMUtils.d.ts.map +1 -0
  245. package/dist/utils/DOMUtils.js +466 -0
  246. package/dist/utils/FileLock.d.ts +44 -0
  247. package/dist/utils/FileLock.d.ts.map +1 -0
  248. package/dist/utils/FileLock.js +189 -0
  249. package/dist/utils/IsolatedDiff.d.ts +45 -0
  250. package/dist/utils/IsolatedDiff.d.ts.map +1 -0
  251. package/dist/utils/IsolatedDiff.js +382 -0
  252. package/dist/utils/StackUtils.d.ts +11 -0
  253. package/dist/utils/StackUtils.d.ts.map +1 -0
  254. package/dist/utils/StackUtils.js +120 -0
  255. package/dist/utils/SummaryTable.d.ts +17 -0
  256. package/dist/utils/SummaryTable.d.ts.map +1 -0
  257. package/dist/utils/SummaryTable.js +37 -0
  258. package/dist/utils/atomicWrite.d.ts +9 -0
  259. package/dist/utils/atomicWrite.d.ts.map +1 -0
  260. package/dist/utils/atomicWrite.js +83 -0
  261. package/dist/utils/logger.d.ts +59 -0
  262. package/dist/utils/logger.d.ts.map +1 -0
  263. package/dist/utils/logger.js +123 -0
  264. package/dist/vendor/enquirer.d.ts +33 -0
  265. package/dist/vendor/enquirer.d.ts.map +1 -0
  266. package/dist/vendor/enquirer.js +11 -0
  267. package/package.json +71 -0
@@ -0,0 +1,95 @@
1
+ export interface AncestorNode {
2
+ tag: string;
3
+ id: string | null;
4
+ classes: string[];
5
+ /** "{tag}#{id}[{classes_sorted_asc}]" - stable fingerprint for drift comparison */
6
+ fingerprint: string;
7
+ }
8
+ export type GhostCondition = "G1_DISPLAY_NONE" | "G2_VISIBILITY_HIDDEN" | "G3_OPACITY_ZERO" | "G4_POINTER_EVENTS_NONE" | "G5_ZERO_AREA" | "G6_CONTENT_VISIBILITY_HIDDEN";
9
+ export interface VisibilityInfo {
10
+ isGhost: boolean;
11
+ ghostReason: GhostCondition | null;
12
+ isOccluded: boolean;
13
+ /** Computed opacity at capture time (0–1). Ghost threshold: < 0.05. */
14
+ opacity: number;
15
+ }
16
+ export interface AnchorInfo {
17
+ /** Resolved via: aria-labelledby → aria-label → label[for] → implicit label → preceding sibling */
18
+ closestLabel: string | null;
19
+ /** Longest unique sibling cell text when element lives in a table row; null otherwise */
20
+ rowAnchor: string | null;
21
+ /** Up to 5 direct-sibling texts (< 100 chars each). Stored for inspection; not scored in v2. */
22
+ neighborTexts: string[];
23
+ }
24
+ /**
25
+ * Disk format for element snapshots.
26
+ *
27
+ * v1 snapshots (no schemaVersion field) carry only the original fields.
28
+ * v2 snapshots (schemaVersion: 2) add ancestry, visibility, anchors, and
29
+ * isInShadowDom. v1 snapshots are migrated to v2 on load by SnapshotService.
30
+ */
31
+ export interface ElementSnapshot {
32
+ tagName: string;
33
+ id: string | null;
34
+ text: string | null;
35
+ classes: string[];
36
+ attributes: Record<string, string>;
37
+ role: string | null;
38
+ parentContext: string;
39
+ timestamp: string;
40
+ rect?: {
41
+ x: number;
42
+ y: number;
43
+ width: number;
44
+ height: number;
45
+ };
46
+ schemaVersion?: 1 | 2;
47
+ ancestry?: AncestorNode[];
48
+ visibility?: VisibilityInfo;
49
+ anchors?: AnchorInfo;
50
+ isInShadowDom?: boolean;
51
+ }
52
+ /** Narrowed type asserting that all v2 fields are present. */
53
+ export type ElementSnapshotV2 = ElementSnapshot & {
54
+ schemaVersion: 2;
55
+ ancestry: AncestorNode[];
56
+ visibility: VisibilityInfo;
57
+ anchors: AnchorInfo;
58
+ isInShadowDom: boolean;
59
+ };
60
+ export declare function isSnapshotV2(s: ElementSnapshot): s is ElementSnapshotV2;
61
+ export interface FixRequest {
62
+ targetIntent: string;
63
+ failedSelector: string;
64
+ previousState: object;
65
+ currentDom: string;
66
+ }
67
+ export interface FixResponse {
68
+ status: "FIXED" | "NOT_FOUND";
69
+ new_selector: string | null;
70
+ confidence: number;
71
+ explanation: string;
72
+ }
73
+ export interface ElementDNA {
74
+ tagName: string;
75
+ text: string;
76
+ attributes: Record<string, string>;
77
+ styles: {
78
+ backgroundColor: string;
79
+ color: string;
80
+ display: string;
81
+ visibility: string;
82
+ };
83
+ rect: {
84
+ x: number;
85
+ y: number;
86
+ width: number;
87
+ height: number;
88
+ };
89
+ hierarchy: {
90
+ parentTag: string;
91
+ parentClass: string;
92
+ neighborTexts: string[];
93
+ };
94
+ }
95
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ "use strict";
2
+ // ── DNA v2 Sub-types ─────────────────────────────────────────────────────────
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ exports.isSnapshotV2 = isSnapshotV2;
5
+ function isSnapshotV2(s) {
6
+ return s.schemaVersion === 2 && Array.isArray(s.ancestry);
7
+ }
@@ -0,0 +1,49 @@
1
+ import { Page } from "@playwright/test";
2
+ import { ElementDNA } from "../types";
3
+ export declare class DOMUtils {
4
+ /**
5
+ * Sanitizes a Playwright selector (which may contain `>>`) into a CSS-safe
6
+ * selector by extracting only the last segment. Returns the last meaningful
7
+ * CSS part so querySelector can be used inside the correct context.
8
+ */
9
+ static sanitizeSelector(selector: string): string;
10
+ /**
11
+ * Given a Playwright `>>` selector, returns each segment in order.
12
+ * e.g. "#my-iframe >> #shadow-host >> #btn"
13
+ * → ["#my-iframe", "#shadow-host", "#btn"]
14
+ */
15
+ static parseSegments(selector: string): string[];
16
+ static getNeighborhoodDom(page: Page, fullSelector: string): Promise<{
17
+ dom: string;
18
+ successfulPath: string[];
19
+ }>;
20
+ /**
21
+ * When a selector has multiple ">>" segments, the first segments are
22
+ * typically iframe selectors. This helper drills into the correct frame
23
+ * before running the DOM extraction.
24
+ */
25
+ private static _getNeighborhoodFromFrame;
26
+ /**
27
+ * Runs the "Smart Neighborhood" DOM-extraction script inside the given
28
+ * execution context (a Page or a Frame). Implements asymmetric scope:
29
+ *
30
+ * - FULL semantic ancestry spine (anchor → root, crossing shadow→host)
31
+ * is always kept — this is cheap and carries the critical context
32
+ * (form/fieldset/dialog/landmark/id anchors).
33
+ * - SIBLINGS are pruned to a fixed K-window around the spine child; the
34
+ * number dropped is surfaced via `data-sibling-total` so the AI knows
35
+ * pruning occurred (and never assumes a positional selector is safe).
36
+ * - GLOBAL uniqueness is probed in the SAME evaluate pass; when the
37
+ * anchor's primary signal matches >1 element page-wide, the count is
38
+ * attached as `fx-global-matches` — this defeats the "neighborhood is
39
+ * unique but the page is not" trap without shipping the whole DOM.
40
+ * - Char-budget (cheap proxy for tokens) bounds the final payload.
41
+ *
42
+ * Generic wrappers (div/span) with no semantic signal are collapsed; visual
43
+ * noise (class/style) is stripped; script/style/svg and hidden elements are
44
+ * dropped. Shadow DOM and same-origin iframe content are pierced and inlined.
45
+ */
46
+ private static _evaluateNeighborhood;
47
+ static getElementDNA: (page: Page, selector: string) => Promise<ElementDNA>;
48
+ }
49
+ //# sourceMappingURL=DOMUtils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DOMUtils.d.ts","sourceRoot":"","sources":["../../src/utils/DOMUtils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AACxC,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAGtC,qBAAa,QAAQ;IACnB;;;;OAIG;IACH,MAAM,CAAC,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM;IAKjD;;;;OAIG;IACH,MAAM,CAAC,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,EAAE;WAM5B,kBAAkB,CACpC,IAAI,EAAE,IAAI,EACV,YAAY,EAAE,MAAM,GACnB,OAAO,CAAC;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,cAAc,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC;IAerD;;;;OAIG;mBACkB,yBAAyB;IAoD9C;;;;;;;;;;;;;;;;;;;OAmBG;mBACkB,qBAAqB;IAoW1C,MAAM,CAAC,aAAa,GAClB,MAAM,IAAI,EACV,UAAU,MAAM,KACf,OAAO,CAAC,UAAU,CAAC,CAqCpB;CACH"}
@@ -0,0 +1,466 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DOMUtils = void 0;
4
+ const logger_1 = require("./logger");
5
+ class DOMUtils {
6
+ /**
7
+ * Sanitizes a Playwright selector (which may contain `>>`) into a CSS-safe
8
+ * selector by extracting only the last segment. Returns the last meaningful
9
+ * CSS part so querySelector can be used inside the correct context.
10
+ */
11
+ static sanitizeSelector(selector) {
12
+ const parts = selector.split(">>").map((s) => s.trim());
13
+ return parts[parts.length - 1];
14
+ }
15
+ /**
16
+ * Given a Playwright `>>` selector, returns each segment in order.
17
+ * e.g. "#my-iframe >> #shadow-host >> #btn"
18
+ * → ["#my-iframe", "#shadow-host", "#btn"]
19
+ */
20
+ static parseSegments(selector) {
21
+ return selector.split(">>").map((s) => s.trim());
22
+ }
23
+ // src/utils/DOMUtils.ts (חלק רלוונטי)
24
+ // בתוך DOMUtils.ts
25
+ static async getNeighborhoodDom(page, fullSelector) {
26
+ // וודא שה-Return Type מעודכן כאן!
27
+ const segments = fullSelector.split(" >> ").map((s) => s.trim());
28
+ // כאן קוראים לפונקציה הפרטית שכתבנו קודם
29
+ const result = await this._getNeighborhoodFromFrame(page, segments);
30
+ // השגיאה שהצגת (Property length does not exist) קרתה כאן כנראה בלוג או בבדיקה
31
+ if (!result.dom || result.dom.length === 0) {
32
+ logger_1.logger.warn("No DOM content extracted");
33
+ }
34
+ return result; // מחזירים את האובייקט המלא { dom, successfulPath }
35
+ }
36
+ /**
37
+ * When a selector has multiple ">>" segments, the first segments are
38
+ * typically iframe selectors. This helper drills into the correct frame
39
+ * before running the DOM extraction.
40
+ */
41
+ static async _getNeighborhoodFromFrame(page, segments) {
42
+ const targetSelector = segments[segments.length - 1];
43
+ let currentContext = page;
44
+ const successfulPath = [];
45
+ try {
46
+ // עוברים על כל הפריימים בשרשרת
47
+ for (let i = 0; i < segments.length - 1; i++) {
48
+ const selector = segments[i];
49
+ logger_1.logger.debug(`Attempting to enter frame: ${selector}`);
50
+ let frameHandle = await currentContext
51
+ .locator(selector)
52
+ .elementHandle({ timeout: 2000 })
53
+ .catch(() => null);
54
+ if (!frameHandle) {
55
+ logger_1.logger.debug(`Frame ${selector} not found. Stopping drill at: ${successfulPath.join(" >> ") || "root"}`);
56
+ break; // עוצרים ומחלצים מאיפה שהצלחנו להגיע
57
+ }
58
+ const frame = await frameHandle.contentFrame();
59
+ if (frame) {
60
+ currentContext = frame;
61
+ successfulPath.push(selector); // שומרים את ה-Breadcrumb
62
+ logger_1.logger.debug(`Successfully entered context: ${selector}`);
63
+ }
64
+ else {
65
+ break;
66
+ }
67
+ }
68
+ const dom = await DOMUtils._evaluateNeighborhood(currentContext, targetSelector);
69
+ return { dom, successfulPath };
70
+ }
71
+ catch (e) {
72
+ logger_1.logger.debug(`DOM extraction error: ${e.message}`);
73
+ return {
74
+ dom: await DOMUtils._evaluateNeighborhood(page, targetSelector),
75
+ successfulPath: [],
76
+ };
77
+ }
78
+ }
79
+ /**
80
+ * Runs the "Smart Neighborhood" DOM-extraction script inside the given
81
+ * execution context (a Page or a Frame). Implements asymmetric scope:
82
+ *
83
+ * - FULL semantic ancestry spine (anchor → root, crossing shadow→host)
84
+ * is always kept — this is cheap and carries the critical context
85
+ * (form/fieldset/dialog/landmark/id anchors).
86
+ * - SIBLINGS are pruned to a fixed K-window around the spine child; the
87
+ * number dropped is surfaced via `data-sibling-total` so the AI knows
88
+ * pruning occurred (and never assumes a positional selector is safe).
89
+ * - GLOBAL uniqueness is probed in the SAME evaluate pass; when the
90
+ * anchor's primary signal matches >1 element page-wide, the count is
91
+ * attached as `fx-global-matches` — this defeats the "neighborhood is
92
+ * unique but the page is not" trap without shipping the whole DOM.
93
+ * - Char-budget (cheap proxy for tokens) bounds the final payload.
94
+ *
95
+ * Generic wrappers (div/span) with no semantic signal are collapsed; visual
96
+ * noise (class/style) is stripped; script/style/svg and hidden elements are
97
+ * dropped. Shadow DOM and same-origin iframe content are pierced and inlined.
98
+ */
99
+ static async _evaluateNeighborhood(context, selector) {
100
+ logger_1.logger.debug(`Smart-Neighborhood extraction for: "${selector}"`);
101
+ const result = await context.evaluate((cfg) => {
102
+ const { selector, K, maxText, budget } = cfg;
103
+ // ---- Tag taxonomies ------------------------------------------------
104
+ const SKIP = new Set([
105
+ "script",
106
+ "style",
107
+ "link",
108
+ "svg",
109
+ "noscript",
110
+ "template",
111
+ "meta",
112
+ ]);
113
+ // Kept verbatim but never directly selectable → no fx-id index.
114
+ const CONTAINER = new Set([
115
+ "html",
116
+ "body",
117
+ "form",
118
+ "nav",
119
+ "section",
120
+ "header",
121
+ "footer",
122
+ "main",
123
+ "article",
124
+ "aside",
125
+ "ul",
126
+ "ol",
127
+ "dl",
128
+ "table",
129
+ "thead",
130
+ "tbody",
131
+ "tfoot",
132
+ "tr",
133
+ "colgroup",
134
+ "fieldset",
135
+ "figure",
136
+ "figcaption",
137
+ "caption",
138
+ "menu",
139
+ "details",
140
+ ]);
141
+ // Generic layout wrappers: collapsed when they carry no semantic signal.
142
+ const COLLAPSIBLE = new Set(["div", "span"]);
143
+ const SIGNAL_ATTRS = ["id", "data-testid", "name", "role", "aria-label"];
144
+ // Output attribute allow-list (class/style intentionally excluded).
145
+ const PRESERVED = [
146
+ "id",
147
+ "data-testid",
148
+ "name",
149
+ "role",
150
+ "type",
151
+ "href",
152
+ "alt",
153
+ "title",
154
+ "placeholder",
155
+ "value",
156
+ "aria-label",
157
+ "aria-labelledby",
158
+ "aria-describedby",
159
+ "for",
160
+ "list",
161
+ "src",
162
+ "srcdoc",
163
+ ];
164
+ const isTransparent = (tag) => tag === "body" || tag === "html";
165
+ // ---- Deep query: pierce shadow roots + same-origin iframes ---------
166
+ const deepQuery = (root, sel) => {
167
+ let found = null;
168
+ try {
169
+ found = root.querySelector(sel);
170
+ }
171
+ catch {
172
+ found = null;
173
+ }
174
+ if (found)
175
+ return found;
176
+ let nodes = [];
177
+ try {
178
+ nodes = Array.from(root.querySelectorAll("*"));
179
+ }
180
+ catch {
181
+ nodes = [];
182
+ }
183
+ for (const e of nodes) {
184
+ if (e.shadowRoot) {
185
+ const r = deepQuery(e.shadowRoot, sel);
186
+ if (r)
187
+ return r;
188
+ }
189
+ if (e.tagName === "IFRAME") {
190
+ try {
191
+ const d = e.contentDocument;
192
+ if (d) {
193
+ const r = deepQuery(d, sel);
194
+ if (r)
195
+ return r;
196
+ }
197
+ }
198
+ catch {
199
+ /* cross-origin frame — skip */
200
+ }
201
+ }
202
+ }
203
+ return null;
204
+ };
205
+ // ---- Visibility ----------------------------------------------------
206
+ const isHidden = (el) => {
207
+ const e = el;
208
+ if (e.getAttribute && e.getAttribute("aria-hidden") === "true")
209
+ return true;
210
+ if (e.hasAttribute && e.hasAttribute("hidden"))
211
+ return true;
212
+ if (el.tagName === "INPUT" && e.getAttribute("type") === "hidden")
213
+ return true;
214
+ if (e.style &&
215
+ (e.style.display === "none" || e.style.visibility === "hidden"))
216
+ return true;
217
+ try {
218
+ const view = el.ownerDocument?.defaultView || window;
219
+ const cs = view.getComputedStyle(e);
220
+ if (cs && (cs.display === "none" || cs.visibility === "hidden"))
221
+ return true;
222
+ }
223
+ catch {
224
+ /* getComputedStyle across docs may throw — ignore */
225
+ }
226
+ return false;
227
+ };
228
+ const hasSignal = (el) => SIGNAL_ATTRS.some((a) => !!el.getAttribute?.(a));
229
+ const containsHeader = (el) => el.tagName === "TH" || !!el.querySelector?.("th");
230
+ // ---- Anchor + ancestry spine --------------------------------------
231
+ const anchor = deepQuery(document, selector);
232
+ const spine = new Set();
233
+ // Walk an element + all its ancestors into the spine, crossing shadow
234
+ // boundaries (ShadowRoot, nodeType 11 → host element).
235
+ const addLineage = (start) => {
236
+ let cur = start;
237
+ while (cur) {
238
+ spine.add(cur);
239
+ if (!cur.parentNode)
240
+ break;
241
+ cur =
242
+ cur.parentNode.nodeType === 11
243
+ ? cur.parentNode.host || null
244
+ : cur.parentNode;
245
+ }
246
+ };
247
+ if (anchor) {
248
+ addLineage(anchor);
249
+ // Detached semantic refs (#14-16): the accessible name/description
250
+ // may live anywhere in the document, linked only by id. Pull those
251
+ // nodes (and their lineage) into the spine so pruning never drops
252
+ // the context the AI needs for getByLabel / aria-based selectors.
253
+ const refIds = [];
254
+ ["aria-labelledby", "aria-describedby", "list"].forEach((a) => {
255
+ const v = anchor.getAttribute(a);
256
+ if (v)
257
+ refIds.push(...v.split(/\s+/).filter(Boolean));
258
+ });
259
+ refIds.forEach((id) => {
260
+ const e = document.getElementById(id);
261
+ if (e)
262
+ addLineage(e);
263
+ });
264
+ // Reverse link: <label for="anchorId"> sitting elsewhere in the DOM.
265
+ const aid = anchor.getAttribute("id");
266
+ if (aid) {
267
+ try {
268
+ document
269
+ .querySelectorAll(`label[for="${aid.replace(/(["\\])/g, "\\$1")}"]`)
270
+ .forEach((l) => addLineage(l));
271
+ }
272
+ catch {
273
+ /* ignore malformed id */
274
+ }
275
+ }
276
+ }
277
+ // ---- Global uniqueness probe (same evaluate pass) -----------------
278
+ const globalMatches = (el) => {
279
+ const esc = (v) => v.replace(/(["\\])/g, "\\$1");
280
+ const testid = el.getAttribute("data-testid");
281
+ const id = el.getAttribute("id");
282
+ const name = el.getAttribute("name");
283
+ const role = el.getAttribute("role");
284
+ const tag = el.tagName.toLowerCase();
285
+ let sel = "";
286
+ if (testid)
287
+ sel = `[data-testid="${esc(testid)}"]`;
288
+ else if (id)
289
+ sel = `#${window.CSS?.escape ? CSS.escape(id) : id}`;
290
+ else if (name)
291
+ sel = `${tag}[name="${esc(name)}"]`;
292
+ else if (role)
293
+ sel = `${tag}[role="${esc(role)}"]`;
294
+ else {
295
+ // No stable attr — count same-tag elements sharing trimmed text.
296
+ const txt = (el.textContent || "").trim();
297
+ if (!txt)
298
+ return 1;
299
+ let n = 0;
300
+ document.querySelectorAll(tag).forEach((c) => {
301
+ if ((c.textContent || "").trim() === txt)
302
+ n++;
303
+ });
304
+ return n;
305
+ }
306
+ try {
307
+ return document.querySelectorAll(sel).length;
308
+ }
309
+ catch {
310
+ return 1;
311
+ }
312
+ };
313
+ const counter = { v: 0 };
314
+ const truncate = (s) => s.length > maxText ? s.slice(0, maxText) + "…" : s;
315
+ const renderNode = (node) => {
316
+ // --- Text node ---
317
+ if (node.nodeType === Node.TEXT_NODE) {
318
+ const t = node.textContent ? node.textContent.trim() : "";
319
+ return t ? truncate(t) : null;
320
+ }
321
+ const isShadow = typeof ShadowRoot !== "undefined" && node instanceof ShadowRoot;
322
+ if (node.nodeType !== Node.ELEMENT_NODE && !isShadow)
323
+ return null;
324
+ const el = isShadow
325
+ ? node.host
326
+ : node;
327
+ const tag = isShadow ? "shadow-root" : el.tagName.toLowerCase();
328
+ if (SKIP.has(tag))
329
+ return null;
330
+ if (!isShadow && isHidden(el))
331
+ return null;
332
+ // --- Gather children: shadow root + light DOM + same-doc iframe ---
333
+ let childNodes = [];
334
+ if (!isShadow && el.shadowRoot) {
335
+ childNodes.push(...Array.from(el.shadowRoot.childNodes));
336
+ }
337
+ childNodes.push(...Array.from(node.childNodes));
338
+ if (tag === "iframe") {
339
+ try {
340
+ const d = el.contentDocument;
341
+ if (d && d.body)
342
+ childNodes.push(...Array.from(d.body.childNodes));
343
+ }
344
+ catch {
345
+ /* cross-origin frame — handled by the >> drill instead */
346
+ }
347
+ }
348
+ // --- Asymmetric scope: K-window siblings around the spine child ---
349
+ // Header rows (containing <th>) are always pinned so table column
350
+ // context survives even when data rows are pruned.
351
+ let siblingTotal = -1;
352
+ if (anchor && spine.has(el) && el !== anchor) {
353
+ const elemKids = childNodes.filter((n) => n.nodeType === Node.ELEMENT_NODE);
354
+ const pivot = elemKids.findIndex((k) => spine.has(k));
355
+ if (pivot >= 0) {
356
+ const keep = new Set();
357
+ let pruned = false;
358
+ elemKids.forEach((k, i) => {
359
+ if (spine.has(k) ||
360
+ (i >= pivot - K && i <= pivot + K) ||
361
+ containsHeader(k))
362
+ keep.add(k);
363
+ else
364
+ pruned = true;
365
+ });
366
+ if (pruned) {
367
+ siblingTotal = elemKids.length;
368
+ childNodes = childNodes.filter((n) => n.nodeType !== Node.ELEMENT_NODE ||
369
+ keep.has(n));
370
+ }
371
+ }
372
+ }
373
+ // Generic wrappers with no semantic signal collapse to their
374
+ // children; transparent roots (body/html) emit children only.
375
+ // Neither emits a tag or consumes an fx-id index.
376
+ const collapses = COLLAPSIBLE.has(tag) && !hasSignal(el);
377
+ if (collapses || isTransparent(tag)) {
378
+ return childNodes.map(renderNode).filter(Boolean).join("") || null;
379
+ }
380
+ // Reserve this element's fx-id BEFORE rendering children so the
381
+ // index reflects document order (parent indexed before its kids).
382
+ const isContainer = CONTAINER.has(tag);
383
+ const fxId = isContainer ? -1 : counter.v++;
384
+ const childHtml = childNodes.map(renderNode).filter(Boolean).join("");
385
+ // --- Build attributes ---
386
+ const attrs = [];
387
+ if (fxId >= 0)
388
+ attrs.push(`fx-id="${fxId}"`);
389
+ if (anchor && el === anchor) {
390
+ const n = globalMatches(el);
391
+ if (n > 1)
392
+ attrs.push(`fx-global-matches="${n}"`);
393
+ }
394
+ if (siblingTotal > 0)
395
+ attrs.push(`data-sibling-total="${siblingTotal}"`);
396
+ for (const a of PRESERVED) {
397
+ const v = el.getAttribute?.(a);
398
+ if (v)
399
+ attrs.push(`${a}="${a === "srcdoc" ? v.substring(0, 200) : v}"`);
400
+ }
401
+ // Image filename fallback: even without alt/title the basename of the
402
+ // src often carries intent (e.g. trash-can-icon.svg).
403
+ if (tag === "img") {
404
+ const src = el.getAttribute("src");
405
+ if (src) {
406
+ const base = src
407
+ .split("?")[0]
408
+ .split("#")[0]
409
+ .split("/")
410
+ .filter(Boolean)
411
+ .pop();
412
+ if (base)
413
+ attrs.push(`data-src-name="${base}"`);
414
+ }
415
+ }
416
+ const inner = childHtml ||
417
+ (node.textContent ? truncate(node.textContent.trim()) : "");
418
+ const attrStr = attrs.length ? " " + attrs.join(" ") : "";
419
+ return `<${tag}${attrStr}>${inner}</${tag}>`;
420
+ };
421
+ const root = document.body || document.documentElement;
422
+ let out = renderNode(root) || "";
423
+ // Char-budget hard cap (cheap token proxy) — degrade, never overflow.
424
+ if (out.length > budget)
425
+ out = out.slice(0, budget) + "<!--fx-truncated-->";
426
+ return out;
427
+ }, { selector, K: 3, maxText: 200, budget: 25000 });
428
+ return result;
429
+ }
430
+ static getElementDNA = async (page, selector) => {
431
+ return await page
432
+ .locator(selector)
433
+ .first()
434
+ .evaluate((el) => {
435
+ const rect = el.getBoundingClientRect();
436
+ const style = window.getComputedStyle(el);
437
+ const neighbors = Array.from(el.parentElement?.children || [])
438
+ .filter((child) => child !== el)
439
+ .map((child) => child.innerText?.substring(0, 20))
440
+ .filter(Boolean);
441
+ return {
442
+ tagName: el.tagName,
443
+ text: el.innerText,
444
+ attributes: Object.fromEntries(Array.from(el.attributes).map((a) => [a.name, a.value])),
445
+ styles: {
446
+ backgroundColor: style.backgroundColor,
447
+ color: style.color,
448
+ display: style.display,
449
+ visibility: style.visibility,
450
+ },
451
+ rect: {
452
+ x: rect.x,
453
+ y: rect.y,
454
+ width: rect.width,
455
+ height: rect.height,
456
+ },
457
+ hierarchy: {
458
+ parentTag: el.parentElement?.tagName || "",
459
+ parentClass: el.parentElement?.className || "",
460
+ neighborTexts: neighbors,
461
+ },
462
+ };
463
+ });
464
+ };
465
+ }
466
+ exports.DOMUtils = DOMUtils;
@@ -0,0 +1,44 @@
1
+ export interface FileLockOptions {
2
+ /** Backoff delays (ms) between async acquire attempts. */
3
+ delaysMs?: number[];
4
+ /** Busy-wait delays (ms) between sync acquire attempts. */
5
+ syncDelaysMs?: number[];
6
+ /**
7
+ * If the lock dir's mtime is older than this many ms, treat it as
8
+ * abandoned (the holder crashed without releasing) and reclaim it.
9
+ * 0 / undefined = never reclaim — prefer skipping a write over risking a
10
+ * double-writer. New callers that own their lock namespace should opt in.
11
+ */
12
+ staleMs?: number;
13
+ logger?: {
14
+ warn: (m: string) => void;
15
+ };
16
+ }
17
+ export declare class FileLock {
18
+ private readonly lockDir;
19
+ private readonly opts;
20
+ constructor(lockDir: string, opts?: FileLockOptions);
21
+ private warn;
22
+ /** Ensure the lock dir's PARENT exists (never the lock dir itself — that
23
+ * would defeat the EEXIST mutex). */
24
+ private ensureParent;
25
+ private reclaimIfStale;
26
+ /** One attempt, no wait. True iff this call now holds the lock. */
27
+ tryAcquire(): boolean;
28
+ /** Async acquire with exponential backoff. */
29
+ acquire(): Promise<boolean>;
30
+ release(): void;
31
+ /**
32
+ * Run `fn` while holding the lock. Returns `fn`'s result, or `undefined`
33
+ * (and warns) when the lock could not be acquired within the backoff
34
+ * window. Mirrors the cache contract: a contended write is skipped, never
35
+ * thrown — the lock guards an optimisation/idempotent write, not a
36
+ * correctness invariant the caller cannot recover from.
37
+ */
38
+ withLock<T>(fn: () => T | Promise<T>): Promise<T | undefined>;
39
+ /** Synchronous variant — busy-waits between attempts. */
40
+ withLockSync<T>(fn: () => T): T | undefined;
41
+ }
42
+ /** Derive the conventional lock-dir path for a guarded file. */
43
+ export declare function lockDirFor(filePath: string): string;
44
+ //# sourceMappingURL=FileLock.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FileLock.d.ts","sourceRoot":"","sources":["../../src/utils/FileLock.ts"],"names":[],"mappings":"AAmBA,MAAM,WAAW,eAAe;IAC9B,0DAA0D;IAC1D,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,2DAA2D;IAC3D,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB;;;;;OAKG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE;QAAE,IAAI,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,IAAI,CAAA;KAAE,CAAC;CACxC;AAKD,qBAAa,QAAQ;IAEjB,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,IAAI;gBADJ,OAAO,EAAE,MAAM,EACf,IAAI,GAAE,eAAoB;IAG7C,OAAO,CAAC,IAAI;IAIZ;0CACsC;IACtC,OAAO,CAAC,YAAY;IAOpB,OAAO,CAAC,cAAc;IAkBtB,mEAAmE;IACnE,UAAU,IAAI,OAAO;IAyBrB,8CAA8C;IACxC,OAAO,IAAI,OAAO,CAAC,OAAO,CAAC;IAUjC,OAAO,IAAI,IAAI;IAQf;;;;;;OAMG;IACG,QAAQ,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC;IAYnE,yDAAyD;IACzD,YAAY,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,CAAC,GAAG,CAAC,GAAG,SAAS;CAgB5C;AAED,gEAAgE;AAChE,wBAAgB,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAEnD"}