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,672 @@
1
+ "use strict";
2
+ // src/services/TraceBackEngine.ts
3
+ //
4
+ // Ticket #2 — Stage 2: The AST Trace-Back Engine.
5
+ //
6
+ // Given a node at (or under) a failing call-site — e.g. a token inside
7
+ // `await targetButton.nth(3).click()` — climb the PropertyAccess / Call
8
+ // spine to the leftmost receiver, resolve that receiver's DEFINITION, and
9
+ // classify it:
10
+ //
11
+ // • VariableDeclaration / PropertyDeclaration (POM) → mutation TARGET
12
+ // • Parameter / ArrowFunction / FunctionDeclaration → factory → ABORT(hard)
13
+ // • TemplateExpression w/ substitution, ?? / || / ?: init → ABORT(hard)
14
+ // • destructuring binding, reassigned let/var → ABORT(hard)
15
+ // • imported / unresolved symbol → ABORT(soft, cross-file)
16
+ //
17
+ // Playwright semantics handled natively: locator / getByRole / getByText /
18
+ // getByTestId / …, frameLocator (iframes), `.parent.locator('.child')` and
19
+ // `'.parent >> .child'` chaining (shadow DOM), action modifiers
20
+ // (.nth/.first/.last/.filter), and awaited execution (await/parens unwrap).
21
+ //
22
+ // The engine is PURE and never writes. It composes with Stage 1: every
23
+ // successful target carries an AnchorKey (computeAnchor) so the drain loop
24
+ // can relocate it later without line numbers.
25
+ Object.defineProperty(exports, "__esModule", { value: true });
26
+ exports.TraceBackEngine = void 0;
27
+ exports.categorizeMethod = categorizeMethod;
28
+ exports.climbToTopCall = climbToTopCall;
29
+ exports.getLeftmostReceiver = getLeftmostReceiver;
30
+ exports.resolveValueDeclaration = resolveValueDeclaration;
31
+ exports.stripTrailingActions = stripTrailingActions;
32
+ const ts_morph_1 = require("ts-morph");
33
+ const AnchorResolver_1 = require("./AnchorResolver");
34
+ // ═══════════════════════════════════════════════════════════════════
35
+ // PLAYWRIGHT METHOD TAXONOMY
36
+ // ═══════════════════════════════════════════════════════════════════
37
+ const SELECTOR_METHODS = new Set([
38
+ "locator",
39
+ "getByRole",
40
+ "getByText",
41
+ "getByLabel",
42
+ "getByPlaceholder",
43
+ "getByTestId",
44
+ "getByAltText",
45
+ "getByTitle",
46
+ ]);
47
+ /** Iframe / frame traversal — must be climbed through seamlessly. */
48
+ const FRAME_METHODS = new Set(["frameLocator", "contentFrame"]);
49
+ /** Sub-selection that does NOT change the locator's identity root. */
50
+ const MODIFIER_METHODS = new Set([
51
+ "nth",
52
+ "first",
53
+ "last",
54
+ "filter",
55
+ "and",
56
+ "or",
57
+ ]);
58
+ /** Terminal actions / queries — strippable trailing calls. */
59
+ const ACTION_METHODS = new Set([
60
+ "click",
61
+ "dblclick",
62
+ "fill",
63
+ "type",
64
+ "pressSequentially",
65
+ "press",
66
+ "check",
67
+ "uncheck",
68
+ "setChecked",
69
+ "hover",
70
+ "focus",
71
+ "blur",
72
+ "tap",
73
+ "selectOption",
74
+ "selectText",
75
+ "setInputFiles",
76
+ "clear",
77
+ "dragTo",
78
+ "screenshot",
79
+ "scrollIntoViewIfNeeded",
80
+ "waitFor",
81
+ "isVisible",
82
+ "isHidden",
83
+ "isEnabled",
84
+ "isDisabled",
85
+ "isChecked",
86
+ "isEditable",
87
+ "textContent",
88
+ "innerText",
89
+ "innerHTML",
90
+ "inputValue",
91
+ "getAttribute",
92
+ "count",
93
+ "all",
94
+ "allInnerTexts",
95
+ "allTextContents",
96
+ "boundingBox",
97
+ "evaluate",
98
+ "evaluateHandle",
99
+ "elementHandle",
100
+ "highlight",
101
+ "dispatchEvent",
102
+ ]);
103
+ /** Stable chain roots that are NOT user variables (no trace-back needed). */
104
+ const PAGE_ROOT_NAMES = new Set([
105
+ "page",
106
+ "context",
107
+ "browser",
108
+ "playwright",
109
+ "request",
110
+ "frame",
111
+ ]);
112
+ function categorizeMethod(name) {
113
+ if (FRAME_METHODS.has(name))
114
+ return "frame";
115
+ if (SELECTOR_METHODS.has(name))
116
+ return "selector";
117
+ if (MODIFIER_METHODS.has(name))
118
+ return "modifier";
119
+ if (ACTION_METHODS.has(name))
120
+ return "action";
121
+ return "unknown";
122
+ }
123
+ const MAX_DEPTH = 12;
124
+ // ═══════════════════════════════════════════════════════════════════
125
+ // ENTRY POINTS
126
+ // ═══════════════════════════════════════════════════════════════════
127
+ class TraceBackEngine {
128
+ /** Phase 0 convenience: resolve (1-based line, 0-based col) → node → trace. */
129
+ static traceFromPosition(sourceFile, line, column) {
130
+ let pos;
131
+ try {
132
+ pos = sourceFile.compilerNode.getPositionOfLineAndCharacter(line - 1, column);
133
+ }
134
+ catch {
135
+ return abort("hard", "DEFINITION_NOT_FOUND", `bad position ${line}:${column}`);
136
+ }
137
+ const node = sourceFile.getDescendantAtPos(pos);
138
+ if (!node) {
139
+ return abort("hard", "DEFINITION_NOT_FOUND", `no node at ${line}:${column}`);
140
+ }
141
+ return TraceBackEngine.traceFromNode(node);
142
+ }
143
+ /** Phases 1–5. */
144
+ static traceFromNode(start) {
145
+ // ── Phase 1: climb to the top-level call of the failing statement ──
146
+ const topCall = climbToTopCall(start);
147
+ if (!topCall) {
148
+ return abort("hard", "UNSUPPORTED", `no enclosing call expression for ${start.getKindName()}`);
149
+ }
150
+ // ── Phase 2: descend the spine → leftmost receiver + chain ──────────
151
+ const { leftmost, chain } = getLeftmostReceiver(topCall);
152
+ // Case C/D: receiver is a property access (POM `this.x` / `obj.x`).
153
+ if (ts_morph_1.Node.isPropertyAccessExpression(leftmost)) {
154
+ return tracePropertyReceiver(leftmost, chain, topCall);
155
+ }
156
+ // Case A: receiver is a page-root identifier → inline or var-initializer.
157
+ if (ts_morph_1.Node.isIdentifier(leftmost) && PAGE_ROOT_NAMES.has(leftmost.getText())) {
158
+ return traceRootedChain(leftmost.getText(), topCall, chain);
159
+ }
160
+ // Case B: receiver is a local variable → resolve + classify.
161
+ if (ts_morph_1.Node.isIdentifier(leftmost)) {
162
+ return traceVariableReceiver(leftmost, chain, 0);
163
+ }
164
+ return abort("hard", "UNSUPPORTED", `leftmost receiver is ${leftmost.getKindName()}`, leftmost);
165
+ }
166
+ }
167
+ exports.TraceBackEngine = TraceBackEngine;
168
+ // ═══════════════════════════════════════════════════════════════════
169
+ // PHASE 1 — climb to the statement's top call
170
+ // ═══════════════════════════════════════════════════════════════════
171
+ function climbToTopCall(start) {
172
+ let n = start;
173
+ let top = null;
174
+ let enclosingStmt;
175
+ while (n) {
176
+ if (ts_morph_1.Node.isCallExpression(n))
177
+ top = n;
178
+ if (ts_morph_1.Node.isStatement(n))
179
+ enclosingStmt = n;
180
+ const p = n.getParent();
181
+ if (!p)
182
+ break;
183
+ // Stop at the statement/scope boundary — we want the OUTERMOST call of
184
+ // this expression, not a call further up the tree.
185
+ if (ts_morph_1.Node.isBlock(p) || ts_morph_1.Node.isSourceFile(p))
186
+ break;
187
+ n = p;
188
+ }
189
+ if (top)
190
+ return top;
191
+ // Fallback: `start` was a SIBLING of the call, not an ancestor — e.g. the
192
+ // `await` keyword in `await btn.click()` (climbing never reaches the
193
+ // CallExpression, which is a CHILD of the AwaitExpression). Descend the
194
+ // enclosing statement and return its OUTERMOST call (largest end offset =
195
+ // the last-closing call in a chain like `btn.nth(3).click()`).
196
+ const scope = enclosingStmt ?? start;
197
+ const calls = scope.getDescendantsOfKind(ts_morph_1.SyntaxKind.CallExpression);
198
+ if (calls.length === 0)
199
+ return null;
200
+ let best = calls[0];
201
+ for (const c of calls) {
202
+ if (c.getEnd() > best.getEnd())
203
+ best = c;
204
+ }
205
+ return best;
206
+ }
207
+ // ═══════════════════════════════════════════════════════════════════
208
+ // PHASE 2 — leftmost receiver + chain extraction
209
+ // ═══════════════════════════════════════════════════════════════════
210
+ function getLeftmostReceiver(topCall) {
211
+ const chain = [];
212
+ let expr = topCall;
213
+ // Bound the descent so a pathological tree can't loop forever.
214
+ for (let i = 0; i < 256; i++) {
215
+ expr = unwrap(expr);
216
+ if (ts_morph_1.Node.isCallExpression(expr)) {
217
+ const callee = unwrap(expr.getExpression());
218
+ if (ts_morph_1.Node.isPropertyAccessExpression(callee)) {
219
+ chain.push({
220
+ name: callee.getName(),
221
+ category: categorizeMethod(callee.getName()),
222
+ });
223
+ expr = callee.getExpression();
224
+ continue;
225
+ }
226
+ // Call on a non-property (e.g. a factory invocation getRow()) — descend.
227
+ expr = callee;
228
+ continue;
229
+ }
230
+ if (ts_morph_1.Node.isPropertyAccessExpression(expr)) {
231
+ // Property access NOT followed by a call at the leftmost position:
232
+ // `this.submitBtn` / `loginPage.submitBtn`. The base is the receiver.
233
+ break;
234
+ }
235
+ break;
236
+ }
237
+ chain.reverse();
238
+ return { leftmost: expr, chain };
239
+ }
240
+ // ═══════════════════════════════════════════════════════════════════
241
+ // CASE A — chain rooted directly at page / context / …
242
+ // ═══════════════════════════════════════════════════════════════════
243
+ function traceRootedChain(rootReceiver, topCall, chain) {
244
+ const locatorChain = stripTrailingActions(topCall);
245
+ // Dynamic-selector guard: page.locator(`#row-${id}`) is not safely
246
+ // mutable at source.
247
+ if (containsDynamicSelectorArg(locatorChain)) {
248
+ return abort("hard", "DYNAMIC_SELECTOR", "selector built from a template literal with substitutions", locatorChain);
249
+ }
250
+ // If the chain is the initializer of a variable, the real target is that
251
+ // declaration (start happened to land inside the declaration). Otherwise
252
+ // it is a bare inline call-site.
253
+ const enclosingDecl = getEnclosingVariableDeclaration(locatorChain);
254
+ if (enclosingDecl &&
255
+ enclosingDecl.getInitializer() &&
256
+ isWithin(locatorChain, enclosingDecl.getInitializer())) {
257
+ const target = makeTarget("VariableDeclaration", enclosingDecl.getInitializer(), enclosingDecl, rootReceiver);
258
+ return { status: "target", target, callSiteChain: chain, dependencyChain: [target] };
259
+ }
260
+ const target = makeTarget("InlineCallSite", locatorChain, null, rootReceiver);
261
+ return { status: "target", target, callSiteChain: chain, dependencyChain: [] };
262
+ }
263
+ // ═══════════════════════════════════════════════════════════════════
264
+ // CASE B — local variable receiver: resolve + classify (Phase 3–5)
265
+ // ═══════════════════════════════════════════════════════════════════
266
+ function traceVariableReceiver(idNode, chain, depth) {
267
+ if (depth > MAX_DEPTH) {
268
+ return abort("hard", "MAX_DEPTH", "alias chain exceeded max depth");
269
+ }
270
+ const name = idNode.getText();
271
+ const sourceFile = idNode.getSourceFile();
272
+ const useOffset = idNode.getStart();
273
+ // Phase 3: resolve the definition in lexical scope.
274
+ const decl = resolveValueDeclaration(idNode, name, useOffset);
275
+ if (!decl) {
276
+ // Imported / unresolved → cross-file (soft) vs truly missing.
277
+ if (isImported(sourceFile, name)) {
278
+ return abort("soft", "CROSS_FILE", `"${name}" is imported`, idNode);
279
+ }
280
+ if (findDestructuringBinding(sourceFile, name, useOffset)) {
281
+ return abort("hard", "DESTRUCTURED", `"${name}" comes from a destructuring binding`);
282
+ }
283
+ return abort("hard", "DEFINITION_NOT_FOUND", `"${name}" not resolved in scope`, idNode);
284
+ }
285
+ // Phase 4: classify the declaration kind.
286
+ if (ts_morph_1.Node.isParameterDeclaration(decl)) {
287
+ return abort("hard", "PARAMETER", `"${name}" is a function parameter`, decl);
288
+ }
289
+ if (ts_morph_1.Node.isFunctionDeclaration(decl)) {
290
+ return abort("hard", "FACTORY", `"${name}" is a function declaration`, decl);
291
+ }
292
+ if (!ts_morph_1.Node.isVariableDeclaration(decl)) {
293
+ return abort("hard", "UNSUPPORTED", `"${name}" resolves to ${decl.getKindName()}`, decl);
294
+ }
295
+ return classifyVariableDeclaration(decl, chain, depth);
296
+ }
297
+ function classifyVariableDeclaration(decl, chain, depth) {
298
+ const name = decl.getName();
299
+ const init = decl.getInitializer();
300
+ if (!init) {
301
+ // No initializer. The standard Playwright fixture pattern is
302
+ // let submit; test.beforeEach(() => { submit = page.locator('.btn'); });
303
+ // A SINGLE standalone assignment is the de-facto definition — resolve it.
304
+ // Multiple assignments = genuinely flow-sensitive → abort.
305
+ const assigns = findStandaloneAssignments(decl);
306
+ if (assigns.length === 0) {
307
+ return abort("hard", "UNSUPPORTED", `"${name}" has no initializer`, decl);
308
+ }
309
+ if (assigns.length > 1) {
310
+ return abort("hard", "REASSIGNED", `"${name}" is assigned in ${assigns.length} places`, decl);
311
+ }
312
+ return classifyInitializerExpr(assigns[0], decl, chain, depth, name, true);
313
+ }
314
+ // Has an initializer AND a later standalone assignment → reassigned.
315
+ if (isReassigned(decl)) {
316
+ return abort("hard", "REASSIGNED", `"${name}" is reassigned after init`, decl);
317
+ }
318
+ return classifyInitializerExpr(init, decl, chain, depth, name, false);
319
+ }
320
+ /**
321
+ * Classify the value expression bound to a variable (its initializer, or the
322
+ * RHS of its single hook-style assignment) and build the target / cascade.
323
+ */
324
+ function classifyInitializerExpr(expr, decl, chain, depth, name, fromAssignment) {
325
+ // Factory: const f = (…) => … / function expression.
326
+ if (ts_morph_1.Node.isArrowFunction(expr) || ts_morph_1.Node.isFunctionExpression(expr)) {
327
+ return abort("hard", "FACTORY", `"${name}" is a factory function`, decl);
328
+ }
329
+ // Branchy init: cond ? a : b / a ?? b / a || b.
330
+ if (ts_morph_1.Node.isConditionalExpression(expr) ||
331
+ (ts_morph_1.Node.isBinaryExpression(expr) && isNullishOrLogical(expr))) {
332
+ return abort("hard", "DYNAMIC_SELECTOR", `"${name}" has a conditional / branchy initializer`, decl);
333
+ }
334
+ // Alias: const a = b → recurse on b.
335
+ if (ts_morph_1.Node.isIdentifier(expr)) {
336
+ return traceVariableReceiver(expr, chain, depth + 1);
337
+ }
338
+ if (ts_morph_1.Node.isCallExpression(expr)) {
339
+ if (containsDynamicSelectorArg(expr)) {
340
+ return abort("hard", "DYNAMIC_SELECTOR", `"${name}" selector uses a template literal with substitutions`, decl);
341
+ }
342
+ if (!isPlaywrightChain(expr)) {
343
+ return abort("hard", "UNSUPPORTED", `"${name}" initializer is not a Playwright locator chain`, decl);
344
+ }
345
+ const { rootReceiver, receiverVar } = analyzeChainRoot(expr);
346
+ // The mutation target is the value expression; the owning declaration is
347
+ // the `let`/`const` so the reference census keys off the variable name.
348
+ const target = makeTarget("VariableDeclaration", expr, decl, rootReceiver, fromAssignment ? name : undefined);
349
+ let dependencyChain = [target];
350
+ if (receiverVar) {
351
+ const upstream = traceVariableReceiver(receiverVar, [], depth + 1);
352
+ if (upstream.status === "target") {
353
+ dependencyChain = [...upstream.dependencyChain, target];
354
+ target.rootReceiver = upstream.target.rootReceiver;
355
+ }
356
+ // Upstream abort → keep THIS decl as a valid leaf target.
357
+ }
358
+ return { status: "target", target, callSiteChain: chain, dependencyChain };
359
+ }
360
+ return abort("hard", "UNSUPPORTED", `"${name}" initializer is ${expr.getKindName()}`, decl);
361
+ }
362
+ /** Standalone `name = …` assignments in the file (excludes the decl init). */
363
+ function findStandaloneAssignments(decl) {
364
+ const name = decl.getName();
365
+ const sf = decl.getSourceFile();
366
+ const out = [];
367
+ for (const bin of sf.getDescendantsOfKind(ts_morph_1.SyntaxKind.BinaryExpression)) {
368
+ if (bin.getOperatorToken().getKind() !== ts_morph_1.SyntaxKind.EqualsToken)
369
+ continue;
370
+ const left = bin.getLeft();
371
+ if (ts_morph_1.Node.isIdentifier(left) && left.getText() === name) {
372
+ out.push(bin.getRight());
373
+ }
374
+ }
375
+ return out;
376
+ }
377
+ // ═══════════════════════════════════════════════════════════════════
378
+ // CASE C/D — property-access receiver (POM)
379
+ // ═══════════════════════════════════════════════════════════════════
380
+ function tracePropertyReceiver(prop, chain, _topCall) {
381
+ const base = unwrap(prop.getExpression());
382
+ const propName = prop.getName();
383
+ // C: this.submitBtn → resolve same-file class member.
384
+ if (base.getKind() === ts_morph_1.SyntaxKind.ThisKeyword) {
385
+ const cls = prop.getFirstAncestorByKind(ts_morph_1.SyntaxKind.ClassDeclaration);
386
+ if (!cls) {
387
+ return abort("hard", "DEFINITION_NOT_FOUND", `this.${propName} has no enclosing class`);
388
+ }
389
+ // Property with an initializer: btn = page.locator('#go').
390
+ const propertyDecl = cls.getProperty(propName);
391
+ if (propertyDecl && ts_morph_1.Node.isPropertyDeclaration(propertyDecl)) {
392
+ const init = propertyDecl.getInitializer();
393
+ if (init && ts_morph_1.Node.isCallExpression(init)) {
394
+ if (containsDynamicSelectorArg(init)) {
395
+ return abort("hard", "DYNAMIC_SELECTOR", `this.${propName} uses a dynamic selector`, propertyDecl);
396
+ }
397
+ if (isPlaywrightChain(init)) {
398
+ const { rootReceiver } = analyzeChainRoot(init);
399
+ const target = makeTarget("PropertyDeclaration", init, propertyDecl, rootReceiver);
400
+ return {
401
+ status: "target",
402
+ target,
403
+ callSiteChain: chain,
404
+ dependencyChain: [target],
405
+ };
406
+ }
407
+ }
408
+ }
409
+ // Constructor assignment: this.submitBtn = page.locator('#submit').
410
+ const assignRhs = findConstructorAssignment(cls, propName);
411
+ if (assignRhs && ts_morph_1.Node.isCallExpression(assignRhs)) {
412
+ if (containsDynamicSelectorArg(assignRhs)) {
413
+ return abort("hard", "DYNAMIC_SELECTOR", `this.${propName} uses a dynamic selector`, assignRhs);
414
+ }
415
+ if (isPlaywrightChain(assignRhs)) {
416
+ const { rootReceiver } = analyzeChainRoot(assignRhs);
417
+ const target = makeTarget("PropertyAssignment", assignRhs, assignRhs.getParent() ?? null, rootReceiver);
418
+ return {
419
+ status: "target",
420
+ target,
421
+ callSiteChain: chain,
422
+ dependencyChain: [target],
423
+ };
424
+ }
425
+ }
426
+ return abort("hard", "DEFINITION_NOT_FOUND", `this.${propName} not found as a locator on the class`);
427
+ }
428
+ // D: obj.submitBtn where obj is a local var (often `new SomeClass()`).
429
+ // Same-file class resolution is out of Stage-2 scope; if obj is imported
430
+ // or its class is imported, this is a cross-file POM (soft abort).
431
+ if (ts_morph_1.Node.isIdentifier(base)) {
432
+ return abort("soft", "CROSS_FILE", `"${base.getText()}.${propName}" is a page-object reference (cross-file resolution required)`, prop);
433
+ }
434
+ return abort("hard", "UNSUPPORTED", `property receiver base is ${base.getKindName()}`, prop);
435
+ }
436
+ // ═══════════════════════════════════════════════════════════════════
437
+ // SCOPE-AWARE DEFINITION RESOLUTION
438
+ // ═══════════════════════════════════════════════════════════════════
439
+ /**
440
+ * Lexical-scope resolution of `name` as seen from `useOffset`. Walks
441
+ * enclosing scopes outward: parameters of enclosing functions, then
442
+ * variable/function declarations introduced in each enclosing block /
443
+ * source file. Pure compiler-API traversal — no language service.
444
+ */
445
+ function resolveValueDeclaration(fromNode, name, useOffset) {
446
+ let cur = fromNode.getParent();
447
+ while (cur) {
448
+ if (isFunctionLike(cur)) {
449
+ const params = cur.getParameters?.();
450
+ const param = params?.find((p) => p.getName() === name);
451
+ if (param)
452
+ return param;
453
+ }
454
+ if (ts_morph_1.Node.isBlock(cur) || ts_morph_1.Node.isSourceFile(cur)) {
455
+ const found = scanScopeForDecl(cur, name, useOffset);
456
+ if (found)
457
+ return found;
458
+ }
459
+ cur = cur.getParent();
460
+ }
461
+ return null;
462
+ }
463
+ function scanScopeForDecl(scope, name, beforeOffset) {
464
+ // getStatements() returns the scope's direct statements (it descends the
465
+ // synthetic SyntaxList that getChildrenOfKind would stop at).
466
+ const stmts = scope.getStatements?.();
467
+ if (!stmts)
468
+ return null;
469
+ // Function declarations are hoisted — match regardless of position.
470
+ for (const stmt of stmts) {
471
+ if (ts_morph_1.Node.isFunctionDeclaration(stmt) && stmt.getName() === name) {
472
+ return stmt;
473
+ }
474
+ }
475
+ // Variable declarations: closest one declared before the use site.
476
+ let best = null;
477
+ let bestStart = -1;
478
+ for (const stmt of stmts) {
479
+ if (!ts_morph_1.Node.isVariableStatement(stmt))
480
+ continue;
481
+ for (const d of stmt.getDeclarations()) {
482
+ if (d.getName() !== name)
483
+ continue;
484
+ const s = d.getStart();
485
+ if (s < beforeOffset && s > bestStart) {
486
+ best = d;
487
+ bestStart = s;
488
+ }
489
+ }
490
+ }
491
+ return best;
492
+ }
493
+ function findDestructuringBinding(sourceFile, name, beforeOffset) {
494
+ for (const be of sourceFile.getDescendantsOfKind(ts_morph_1.SyntaxKind.BindingElement)) {
495
+ if (be.getName() === name && be.getStart() < beforeOffset)
496
+ return be;
497
+ }
498
+ return null;
499
+ }
500
+ function isImported(sourceFile, name) {
501
+ for (const imp of sourceFile.getImportDeclarations()) {
502
+ if (imp.getDefaultImport()?.getText() === name)
503
+ return true;
504
+ if (imp.getNamespaceImport()?.getText() === name)
505
+ return true;
506
+ for (const ni of imp.getNamedImports()) {
507
+ if ((ni.getAliasNode()?.getText() ?? ni.getName()) === name)
508
+ return true;
509
+ }
510
+ }
511
+ return false;
512
+ }
513
+ // ═══════════════════════════════════════════════════════════════════
514
+ // CHAIN ANALYSIS HELPERS
515
+ // ═══════════════════════════════════════════════════════════════════
516
+ /** True when the call (or any call in its chain) is a Playwright selector/frame. */
517
+ function isPlaywrightChain(call) {
518
+ let expr = call;
519
+ for (let i = 0; i < 256; i++) {
520
+ expr = unwrap(expr);
521
+ if (ts_morph_1.Node.isCallExpression(expr)) {
522
+ const callee = unwrap(expr.getExpression());
523
+ if (ts_morph_1.Node.isPropertyAccessExpression(callee)) {
524
+ const cat = categorizeMethod(callee.getName());
525
+ if (cat === "selector" || cat === "frame")
526
+ return true;
527
+ expr = callee.getExpression();
528
+ continue;
529
+ }
530
+ expr = callee;
531
+ continue;
532
+ }
533
+ break;
534
+ }
535
+ return false;
536
+ }
537
+ /**
538
+ * Walk a chain to its leftmost receiver. Returns the root-name when the
539
+ * receiver is a page-root, and the receiver Identifier when the chain hangs
540
+ * off another VARIABLE (a cascade link).
541
+ */
542
+ function analyzeChainRoot(call) {
543
+ const { leftmost } = getLeftmostReceiver(call);
544
+ if (ts_morph_1.Node.isIdentifier(leftmost)) {
545
+ const txt = leftmost.getText();
546
+ if (PAGE_ROOT_NAMES.has(txt))
547
+ return { rootReceiver: txt, receiverVar: null };
548
+ return { rootReceiver: txt, receiverVar: leftmost };
549
+ }
550
+ return { rootReceiver: "page", receiverVar: null };
551
+ }
552
+ /** True when any selector arg in the chain is a template literal w/ ${…}. */
553
+ function containsDynamicSelectorArg(node) {
554
+ for (const call of [
555
+ ...(ts_morph_1.Node.isCallExpression(node) ? [node] : []),
556
+ ...node.getDescendantsOfKind(ts_morph_1.SyntaxKind.CallExpression),
557
+ ]) {
558
+ const arg = call.getArguments()[0];
559
+ if (arg && ts_morph_1.Node.isTemplateExpression(arg))
560
+ return true; // has substitutions
561
+ }
562
+ return false;
563
+ }
564
+ /** Strip trailing action/query calls, returning the locator-chain node. */
565
+ function stripTrailingActions(topCall) {
566
+ let expr = unwrap(topCall);
567
+ for (let i = 0; i < 256; i++) {
568
+ if (!ts_morph_1.Node.isCallExpression(expr))
569
+ break;
570
+ const callee = unwrap(expr.getExpression());
571
+ if (ts_morph_1.Node.isPropertyAccessExpression(callee) &&
572
+ categorizeMethod(callee.getName()) === "action") {
573
+ expr = unwrap(callee.getExpression());
574
+ continue;
575
+ }
576
+ break;
577
+ }
578
+ return expr;
579
+ }
580
+ // ═══════════════════════════════════════════════════════════════════
581
+ // SMALL AST UTILITIES
582
+ // ═══════════════════════════════════════════════════════════════════
583
+ function unwrap(node) {
584
+ let n = node;
585
+ for (let i = 0; i < 64; i++) {
586
+ if (ts_morph_1.Node.isAwaitExpression(n) ||
587
+ ts_morph_1.Node.isParenthesizedExpression(n) ||
588
+ ts_morph_1.Node.isNonNullExpression(n) ||
589
+ ts_morph_1.Node.isAsExpression(n)) {
590
+ n = n.getExpression();
591
+ continue;
592
+ }
593
+ break;
594
+ }
595
+ return n;
596
+ }
597
+ function isFunctionLike(node) {
598
+ return (ts_morph_1.Node.isArrowFunction(node) ||
599
+ ts_morph_1.Node.isFunctionDeclaration(node) ||
600
+ ts_morph_1.Node.isFunctionExpression(node) ||
601
+ ts_morph_1.Node.isMethodDeclaration(node) ||
602
+ ts_morph_1.Node.isConstructorDeclaration(node) ||
603
+ ts_morph_1.Node.isGetAccessorDeclaration(node) ||
604
+ ts_morph_1.Node.isSetAccessorDeclaration(node));
605
+ }
606
+ function isNullishOrLogical(bin) {
607
+ if (!ts_morph_1.Node.isBinaryExpression(bin))
608
+ return false;
609
+ const op = bin.getOperatorToken().getKind();
610
+ return (op === ts_morph_1.SyntaxKind.QuestionQuestionToken ||
611
+ op === ts_morph_1.SyntaxKind.BarBarToken ||
612
+ op === ts_morph_1.SyntaxKind.AmpersandAmpersandToken);
613
+ }
614
+ function getEnclosingVariableDeclaration(node) {
615
+ let cur = node.getParent();
616
+ while (cur) {
617
+ if (ts_morph_1.Node.isVariableDeclaration(cur))
618
+ return cur;
619
+ if (ts_morph_1.Node.isBlock(cur) || ts_morph_1.Node.isSourceFile(cur) || isFunctionLike(cur)) {
620
+ return null;
621
+ }
622
+ cur = cur.getParent();
623
+ }
624
+ return null;
625
+ }
626
+ function isWithin(node, container) {
627
+ return (node.getStart() >= container.getStart() &&
628
+ node.getEnd() <= container.getEnd());
629
+ }
630
+ /** Detects a standalone reassignment of a let/var-declared name after init. */
631
+ function isReassigned(decl) {
632
+ const kind = decl.getVariableStatement()?.getDeclarationKind();
633
+ // DeclarationKind: "const" is not reassignable.
634
+ if (!kind || kind === "const")
635
+ return false;
636
+ const name = decl.getName();
637
+ const sf = decl.getSourceFile();
638
+ for (const bin of sf.getDescendantsOfKind(ts_morph_1.SyntaxKind.BinaryExpression)) {
639
+ if (bin.getOperatorToken().getKind() !== ts_morph_1.SyntaxKind.EqualsToken)
640
+ continue;
641
+ const left = bin.getLeft();
642
+ if (ts_morph_1.Node.isIdentifier(left) && left.getText() === name)
643
+ return true;
644
+ }
645
+ return false;
646
+ }
647
+ function findConstructorAssignment(cls, propName) {
648
+ for (const bin of cls.getDescendantsOfKind(ts_morph_1.SyntaxKind.BinaryExpression)) {
649
+ if (bin.getOperatorToken().getKind() !== ts_morph_1.SyntaxKind.EqualsToken)
650
+ continue;
651
+ const left = bin.getLeft();
652
+ if (ts_morph_1.Node.isPropertyAccessExpression(left) &&
653
+ left.getExpression().getKind() === ts_morph_1.SyntaxKind.ThisKeyword &&
654
+ left.getName() === propName) {
655
+ return bin.getRight();
656
+ }
657
+ }
658
+ return null;
659
+ }
660
+ function makeTarget(declKind, targetNode, declaration, rootReceiver, symbolNameOverride) {
661
+ return {
662
+ declKind,
663
+ targetNode,
664
+ declaration,
665
+ symbolName: symbolNameOverride ?? (0, AnchorResolver_1.enclosingSymbolName)(targetNode),
666
+ rootReceiver,
667
+ anchor: (0, AnchorResolver_1.computeAnchor)(targetNode),
668
+ };
669
+ }
670
+ function abort(severity, code, reason, node) {
671
+ return { status: "abort", severity, code, reason, node };
672
+ }