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,25 @@
1
+ import { TemplateExpression } from "ts-morph";
2
+ export type DiffPatchResult = {
3
+ ok: true;
4
+ /** Full patched template string, backtick-wrapped, ready for replaceWithText(). */
5
+ patchedTemplate: string;
6
+ /** Zero-based indices of static segments that changed. */
7
+ changedSegmentIndices: number[];
8
+ } | {
9
+ ok: false;
10
+ reason: string;
11
+ /**
12
+ * Populated when alignWithNew fails due to structural simplification:
13
+ * what each span expression needs to evaluate to satisfy resolvedNew.
14
+ * Use for span-tracing fallback (resolve each span's source independently).
15
+ */
16
+ newSpanValues?: string[];
17
+ };
18
+ export declare class TemplateDiffService {
19
+ static diffAndPatch(templateText: string, resolvedOld: string, resolvedNew: string): DiffPatchResult;
20
+ static computeNodePatch(templateNode: TemplateExpression, resolvedOld: string, resolvedNew: string): DiffPatchResult;
21
+ static extractNodeSpanValues(templateNode: TemplateExpression, resolved: string): string[] | null;
22
+ }
23
+ /** Escape characters that would break a template literal. */
24
+ export declare function escapeForTemplate(text: string): string;
25
+ //# sourceMappingURL=TemplateDiffService.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TemplateDiffService.d.ts","sourceRoot":"","sources":["../../src/services/TemplateDiffService.ts"],"names":[],"mappings":"AAAA,OAAO,EAAQ,kBAAkB,EAAc,MAAM,UAAU,CAAC;AAMhE,MAAM,MAAM,eAAe,GACvB;IACE,EAAE,EAAE,IAAI,CAAC;IACT,mFAAmF;IACnF,eAAe,EAAE,MAAM,CAAC;IACxB,0DAA0D;IAC1D,qBAAqB,EAAE,MAAM,EAAE,CAAC;CACjC,GACD;IACE,EAAE,EAAE,KAAK,CAAC;IACV,MAAM,EAAE,MAAM,CAAC;IACf;;;;OAIG;IACH,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;CAC1B,CAAC;AA6BN,qBAAa,mBAAmB;IAW9B,MAAM,CAAC,YAAY,CACjB,YAAY,EAAE,MAAM,EACpB,WAAW,EAAE,MAAM,EACnB,WAAW,EAAE,MAAM,GAClB,eAAe;IAiHlB,MAAM,CAAC,gBAAgB,CACrB,YAAY,EAAE,kBAAkB,EAChC,WAAW,EAAE,MAAM,EACnB,WAAW,EAAE,MAAM,GAClB,eAAe;IAoFlB,MAAM,CAAC,qBAAqB,CAC1B,YAAY,EAAE,kBAAkB,EAChC,QAAQ,EAAE,MAAM,GACf,MAAM,EAAE,GAAG,IAAI;CASnB;AAgJD,6DAA6D;AAC7D,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAKtD"}
@@ -0,0 +1,334 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.TemplateDiffService = void 0;
4
+ exports.escapeForTemplate = escapeForTemplate;
5
+ // ═══════════════════════════════════════════════════════════════════
6
+ // TemplateDiffService
7
+ //
8
+ // T-8: String-based API - diffAndPatch()
9
+ // Given a template source like `input[data-role="${role}_legacy"]`,
10
+ // its resolved old string, and an AI-proposed new string, compute
11
+ // the patched template that preserves ${...} spans while updating
12
+ // only the changed static segments.
13
+ //
14
+ // T-8.1: Node-based API - computeNodePatch()
15
+ // Wraps diffAndPatch() using the TemplateExpression AST node as the
16
+ // source of truth (avoids text-parsing ambiguity). Returns the full
17
+ // patched template text for use with templateNode.replaceWithText().
18
+ // ═══════════════════════════════════════════════════════════════════
19
+ class TemplateDiffService {
20
+ // ─────────────────────────────────────────────────────────────────
21
+ // T-8: String-based API
22
+ //
23
+ // templateText: the template source (backticks optional), e.g.
24
+ // `input[data-role="${role}_legacy"]` or
25
+ // input[data-role="${role}_legacy"]
26
+ // resolvedOld: what the template evaluates to at the failing call site
27
+ // resolvedNew: the AI-proposed replacement string (flat, no ${...})
28
+ // ─────────────────────────────────────────────────────────────────
29
+ static diffAndPatch(templateText, resolvedOld, resolvedNew) {
30
+ const segments = parseTemplateSource(templateText);
31
+ const staticSegs = segments
32
+ .filter((s) => s.type === "static")
33
+ .map((s) => s.text);
34
+ const exprSegs = segments
35
+ .filter((s) => s.type === "expr")
36
+ .map((s) => s.text);
37
+ // No spans - the "template" is just a plain string.
38
+ if (exprSegs.length === 0) {
39
+ if (resolvedOld !== resolvedNew) {
40
+ return {
41
+ ok: true,
42
+ patchedTemplate: "`" + escapeForTemplate(resolvedNew) + "`",
43
+ changedSegmentIndices: [0],
44
+ };
45
+ }
46
+ return {
47
+ ok: false,
48
+ reason: "resolvedOld === resolvedNew - no change needed",
49
+ };
50
+ }
51
+ // Extract span values from resolvedOld using static segments as anchors.
52
+ const spanValues = extractSpanValues(staticSegs, resolvedOld);
53
+ if (!spanValues) {
54
+ return {
55
+ ok: false,
56
+ reason: "template static segments don't align with resolvedOld - possible desync. " +
57
+ `template="${templateText}", resolvedOld="${resolvedOld}"`,
58
+ };
59
+ }
60
+ // Verify reconstruction - extractSpanValues already guarantees this
61
+ // implicitly, but an explicit check catches any edge-case bugs early.
62
+ {
63
+ let check = "";
64
+ for (let i = 0; i < staticSegs.length; i++) {
65
+ check += staticSegs[i];
66
+ if (i < spanValues.length)
67
+ check += spanValues[i];
68
+ }
69
+ if (check !== resolvedOld) {
70
+ return {
71
+ ok: false,
72
+ reason: `reconstruction mismatch: expected "${resolvedOld}", got "${check}"`,
73
+ };
74
+ }
75
+ }
76
+ // Align resolvedNew against the extracted span values.
77
+ const newStaticSegs = alignWithNew(spanValues, resolvedNew);
78
+ if (!newStaticSegs) {
79
+ return {
80
+ ok: false,
81
+ reason: `dynamic span value(s) [${spanValues.map((v) => `"${v}"`).join(", ")}] ` +
82
+ `not found in resolvedNew="${resolvedNew}" - structural change, manual review required`,
83
+ };
84
+ }
85
+ if (newStaticSegs.length !== staticSegs.length) {
86
+ return {
87
+ ok: false,
88
+ reason: "static segment count mismatch after alignment - structural change",
89
+ };
90
+ }
91
+ // Diff: find which static segments changed.
92
+ const changedIndices = [];
93
+ for (let i = 0; i < staticSegs.length; i++) {
94
+ if (staticSegs[i] !== newStaticSegs[i])
95
+ changedIndices.push(i);
96
+ }
97
+ if (changedIndices.length === 0) {
98
+ return {
99
+ ok: false,
100
+ reason: "no static segment changed - the difference is in a dynamic span. " +
101
+ "Update the argument passed to the function instead.",
102
+ };
103
+ }
104
+ if (changedIndices.length > 1) {
105
+ return {
106
+ ok: false,
107
+ reason: `${changedIndices.length} static segments changed simultaneously - ` +
108
+ "ambiguous, manual review required",
109
+ };
110
+ }
111
+ // Build the patched template.
112
+ const patchedStatics = [...staticSegs];
113
+ patchedStatics[changedIndices[0]] = newStaticSegs[changedIndices[0]];
114
+ return {
115
+ ok: true,
116
+ patchedTemplate: buildTemplate(segments, patchedStatics),
117
+ changedSegmentIndices: changedIndices,
118
+ };
119
+ }
120
+ // ─────────────────────────────────────────────────────────────────
121
+ // T-8.1: Node-based API
122
+ //
123
+ // Uses the TemplateExpression AST node as the authoritative source
124
+ // of static/expr segments (avoids text-parsing edge cases).
125
+ // Returns `patchedText` - pass directly to templateNode.replaceWithText().
126
+ // ─────────────────────────────────────────────────────────────────
127
+ static computeNodePatch(templateNode, resolvedOld, resolvedNew) {
128
+ // Extract static segments and expr texts directly from the AST.
129
+ const head = templateNode.getHead();
130
+ const spans = templateNode.getTemplateSpans();
131
+ const staticSegs = [head.getLiteralText()];
132
+ const exprTexts = [];
133
+ for (const span of spans) {
134
+ exprTexts.push(span.getExpression().getText());
135
+ staticSegs.push(span.getLiteral().getLiteralText());
136
+ }
137
+ // Rebuild equivalent segments array for buildTemplate().
138
+ const segments = [];
139
+ for (let i = 0; i < staticSegs.length; i++) {
140
+ segments.push({ type: "static", text: staticSegs[i] });
141
+ if (i < exprTexts.length) {
142
+ segments.push({ type: "expr", text: exprTexts[i] });
143
+ }
144
+ }
145
+ // No spans case.
146
+ if (exprTexts.length === 0) {
147
+ return {
148
+ ok: true,
149
+ patchedTemplate: "`" + escapeForTemplate(resolvedNew) + "`",
150
+ changedSegmentIndices: [0],
151
+ };
152
+ }
153
+ const spanValues = extractSpanValues(staticSegs, resolvedOld);
154
+ if (!spanValues) {
155
+ return {
156
+ ok: false,
157
+ reason: `template AST doesn't reconstruct to "${resolvedOld}"`,
158
+ };
159
+ }
160
+ const newStaticSegs = alignWithNew(spanValues, resolvedNew);
161
+ if (!newStaticSegs) {
162
+ const newSpanFromNew = extractSpanValues(staticSegs, resolvedNew);
163
+ return {
164
+ ok: false,
165
+ reason: `span values [${spanValues.map((v) => `"${v}"`).join(", ")}] ` +
166
+ `not found in new selector "${resolvedNew}"`,
167
+ ...(newSpanFromNew ? { newSpanValues: newSpanFromNew } : {}),
168
+ };
169
+ }
170
+ const changedIndices = [];
171
+ for (let i = 0; i < staticSegs.length; i++) {
172
+ if (staticSegs[i] !== newStaticSegs[i])
173
+ changedIndices.push(i);
174
+ }
175
+ if (changedIndices.length === 0) {
176
+ return {
177
+ ok: false,
178
+ reason: "difference is in dynamic span - update call-site argument",
179
+ };
180
+ }
181
+ if (changedIndices.length > 1) {
182
+ return {
183
+ ok: false,
184
+ reason: `${changedIndices.length} static segments changed - ambiguous`,
185
+ };
186
+ }
187
+ const patchedStatics = [...staticSegs];
188
+ patchedStatics[changedIndices[0]] = newStaticSegs[changedIndices[0]];
189
+ return {
190
+ ok: true,
191
+ patchedTemplate: buildTemplate(segments, patchedStatics),
192
+ changedSegmentIndices: changedIndices,
193
+ };
194
+ }
195
+ // ─────────────────────────────────────────────────────────────────
196
+ // Extract span values from a TemplateExpression node and a resolved string.
197
+ // Public so callers can derive old/new span values for span tracing.
198
+ // ─────────────────────────────────────────────────────────────────
199
+ static extractNodeSpanValues(templateNode, resolved) {
200
+ const head = templateNode.getHead();
201
+ const spans = templateNode.getTemplateSpans();
202
+ const staticSegs = [head.getLiteralText()];
203
+ for (const span of spans) {
204
+ staticSegs.push(span.getLiteral().getLiteralText());
205
+ }
206
+ return extractSpanValues(staticSegs, resolved);
207
+ }
208
+ }
209
+ exports.TemplateDiffService = TemplateDiffService;
210
+ // ═══════════════════════════════════════════════════════════════════
211
+ // Private helpers
212
+ // ═══════════════════════════════════════════════════════════════════
213
+ /**
214
+ * Parse a template literal source string into alternating static/expr segments.
215
+ * Strips surrounding backticks if present.
216
+ *
217
+ * `` `head${role}_tail` `` → [static:"head", expr:"role", static:"_tail"]
218
+ */
219
+ function parseTemplateSource(text) {
220
+ let src = text.trim();
221
+ if (src.startsWith("`") && src.endsWith("`")) {
222
+ src = src.slice(1, -1);
223
+ }
224
+ const segments = [];
225
+ const exprPattern = /\$\{([^}]+)\}/g;
226
+ let lastIndex = 0;
227
+ let match;
228
+ while ((match = exprPattern.exec(src)) !== null) {
229
+ // Static segment before this expression.
230
+ segments.push({ type: "static", text: src.slice(lastIndex, match.index) });
231
+ segments.push({ type: "expr", text: match[1].trim() });
232
+ lastIndex = match.index + match[0].length;
233
+ }
234
+ // Trailing static segment (may be empty).
235
+ segments.push({ type: "static", text: src.slice(lastIndex) });
236
+ return segments;
237
+ }
238
+ /**
239
+ * Given the static segments of a template and a resolved string,
240
+ * extract the dynamic span values by matching statics as anchors.
241
+ *
242
+ * Algorithm: iterate over all static segments in order. After consuming
243
+ * each static segment, find the next static as the delimiter for the
244
+ * span value - then advance pos to the start of that next static so
245
+ * the following iteration can consume it. This ensures pos is always
246
+ * consistent at the start of each static segment.
247
+ *
248
+ * Returns null if alignment fails (desync or ambiguous adjacent spans).
249
+ */
250
+ function extractSpanValues(staticSegments, resolved) {
251
+ const spanValues = [];
252
+ let pos = 0;
253
+ for (let i = 0; i < staticSegments.length; i++) {
254
+ // Consume static[i].
255
+ const s = staticSegments[i];
256
+ if (resolved.slice(pos, pos + s.length) !== s)
257
+ return null;
258
+ pos += s.length;
259
+ // If this is the last static segment, we're done.
260
+ if (i === staticSegments.length - 1)
261
+ break;
262
+ // Find span[i] value: delimited by static[i+1].
263
+ const nextStatic = staticSegments[i + 1];
264
+ if (nextStatic === "" && i + 1 === staticSegments.length - 1) {
265
+ // Empty trailing static: span value extends to end of string.
266
+ spanValues.push(resolved.slice(pos));
267
+ pos = resolved.length;
268
+ break;
269
+ }
270
+ if (nextStatic === "") {
271
+ // Empty non-trailing static: adjacent spans - boundary is ambiguous.
272
+ return null;
273
+ }
274
+ const delimIdx = resolved.indexOf(nextStatic, pos);
275
+ if (delimIdx === -1)
276
+ return null;
277
+ spanValues.push(resolved.slice(pos, delimIdx));
278
+ // Advance pos to start of nextStatic so the next iteration consumes it.
279
+ pos = delimIdx;
280
+ }
281
+ if (pos !== resolved.length)
282
+ return null;
283
+ return spanValues;
284
+ }
285
+ /**
286
+ * Given known span values (anchors) and a new resolved string,
287
+ * extract what the static segments should be in the patched template.
288
+ *
289
+ * Uses each span value as a positional anchor, in order.
290
+ * Returns null if any span value is missing from resolvedNew.
291
+ */
292
+ function alignWithNew(spanValues, resolvedNew) {
293
+ const newStatics = [];
294
+ let pos = 0;
295
+ for (const spanVal of spanValues) {
296
+ if (spanVal.length === 0) {
297
+ // Can't anchor on empty span value.
298
+ return null;
299
+ }
300
+ const spanPos = resolvedNew.indexOf(spanVal, pos);
301
+ if (spanPos === -1)
302
+ return null;
303
+ newStatics.push(resolvedNew.slice(pos, spanPos));
304
+ pos = spanPos + spanVal.length;
305
+ }
306
+ // Last static segment: everything remaining in resolvedNew.
307
+ newStatics.push(resolvedNew.slice(pos));
308
+ return newStatics;
309
+ }
310
+ /**
311
+ * Reconstruct a full template literal string (with backticks) from
312
+ * alternating segments and new static values.
313
+ */
314
+ function buildTemplate(segments, newStaticValues) {
315
+ let result = "`";
316
+ let staticIdx = 0;
317
+ for (const seg of segments) {
318
+ if (seg.type === "static") {
319
+ result += escapeForTemplate(newStaticValues[staticIdx++]);
320
+ }
321
+ else {
322
+ result += "${" + seg.text + "}";
323
+ }
324
+ }
325
+ result += "`";
326
+ return result;
327
+ }
328
+ /** Escape characters that would break a template literal. */
329
+ function escapeForTemplate(text) {
330
+ return text
331
+ .replace(/\\/g, "\\\\")
332
+ .replace(/`/g, "\\`")
333
+ .replace(/\$\{/g, "\\${");
334
+ }
@@ -0,0 +1,67 @@
1
+ import { Node, SourceFile, CallExpression } from "ts-morph";
2
+ import { AnchorKey } from "./AnchorResolver";
3
+ export type MethodCategory = "selector" | "frame" | "modifier" | "action" | "unknown";
4
+ export declare function categorizeMethod(name: string): MethodCategory;
5
+ export type TraceAbortCode = "PARAMETER" | "FACTORY" | "DYNAMIC_SELECTOR" | "DESTRUCTURED" | "REASSIGNED" | "CROSS_FILE" | "DEFINITION_NOT_FOUND" | "MAX_DEPTH" | "UNSUPPORTED";
6
+ export type TraceSeverity = "soft" | "hard";
7
+ export interface ChainMethod {
8
+ name: string;
9
+ category: MethodCategory;
10
+ }
11
+ export type DeclKind = "VariableDeclaration" | "PropertyDeclaration" | "PropertyAssignment" | "InlineCallSite";
12
+ export interface TraceTarget {
13
+ declKind: DeclKind;
14
+ /**
15
+ * The node to mutate at source: the locator-chain expression (a
16
+ * VariableDeclaration's initializer, a POM property's value, or the inline
17
+ * selector chain with trailing actions stripped).
18
+ */
19
+ targetNode: Node;
20
+ /** The owning declaration node; null for an inline call-site. */
21
+ declaration: Node | null;
22
+ /** Variable / property name owning the target, or null. */
23
+ symbolName: string | null;
24
+ /** Stable chain root ("page", "context", …). */
25
+ rootReceiver: string;
26
+ /** Stage-1 identity anchor for the target node. */
27
+ anchor: AnchorKey;
28
+ }
29
+ export type TraceOutcome = {
30
+ status: "target";
31
+ target: TraceTarget;
32
+ /** Methods seen on the failing call-site chain, root→leaf. */
33
+ callSiteChain: ChainMethod[];
34
+ /**
35
+ * For chained cascades (§1.1) — every upstream VariableDeclaration the
36
+ * target depends on, root-most first, target last. Single-element when
37
+ * there is no cascade. Runtime evidence later picks the broken link.
38
+ */
39
+ dependencyChain: TraceTarget[];
40
+ } | {
41
+ status: "abort";
42
+ severity: TraceSeverity;
43
+ code: TraceAbortCode;
44
+ reason: string;
45
+ node?: Node;
46
+ };
47
+ export declare class TraceBackEngine {
48
+ /** Phase 0 convenience: resolve (1-based line, 0-based col) → node → trace. */
49
+ static traceFromPosition(sourceFile: SourceFile, line: number, column: number): TraceOutcome;
50
+ /** Phases 1–5. */
51
+ static traceFromNode(start: Node): TraceOutcome;
52
+ }
53
+ export declare function climbToTopCall(start: Node): CallExpression | null;
54
+ export declare function getLeftmostReceiver(topCall: CallExpression): {
55
+ leftmost: Node;
56
+ chain: ChainMethod[];
57
+ };
58
+ /**
59
+ * Lexical-scope resolution of `name` as seen from `useOffset`. Walks
60
+ * enclosing scopes outward: parameters of enclosing functions, then
61
+ * variable/function declarations introduced in each enclosing block /
62
+ * source file. Pure compiler-API traversal — no language service.
63
+ */
64
+ export declare function resolveValueDeclaration(fromNode: Node, name: string, useOffset: number): Node | null;
65
+ /** Strip trailing action/query calls, returning the locator-chain node. */
66
+ export declare function stripTrailingActions(topCall: CallExpression): Node;
67
+ //# sourceMappingURL=TraceBackEngine.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TraceBackEngine.d.ts","sourceRoot":"","sources":["../../src/services/TraceBackEngine.ts"],"names":[],"mappings":"AAwBA,OAAO,EACL,IAAI,EACJ,UAAU,EACV,cAAc,EAIf,MAAM,UAAU,CAAC;AAClB,OAAO,EAAE,SAAS,EAAsC,MAAM,kBAAkB,CAAC;AAsFjF,MAAM,MAAM,cAAc,GACtB,UAAU,GACV,OAAO,GACP,UAAU,GACV,QAAQ,GACR,SAAS,CAAC;AAEd,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,cAAc,CAM7D;AAMD,MAAM,MAAM,cAAc,GACtB,WAAW,GACX,SAAS,GACT,kBAAkB,GAClB,cAAc,GACd,YAAY,GACZ,YAAY,GACZ,sBAAsB,GACtB,WAAW,GACX,aAAa,CAAC;AAElB,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG,MAAM,CAAC;AAE5C,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,cAAc,CAAC;CAC1B;AAED,MAAM,MAAM,QAAQ,GAChB,qBAAqB,GACrB,qBAAqB,GACrB,oBAAoB,GACpB,gBAAgB,CAAC;AAErB,MAAM,WAAW,WAAW;IAC1B,QAAQ,EAAE,QAAQ,CAAC;IACnB;;;;OAIG;IACH,UAAU,EAAE,IAAI,CAAC;IACjB,iEAAiE;IACjE,WAAW,EAAE,IAAI,GAAG,IAAI,CAAC;IACzB,2DAA2D;IAC3D,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,gDAAgD;IAChD,YAAY,EAAE,MAAM,CAAC;IACrB,mDAAmD;IACnD,MAAM,EAAE,SAAS,CAAC;CACnB;AAED,MAAM,MAAM,YAAY,GACpB;IACE,MAAM,EAAE,QAAQ,CAAC;IACjB,MAAM,EAAE,WAAW,CAAC;IACpB,8DAA8D;IAC9D,aAAa,EAAE,WAAW,EAAE,CAAC;IAC7B;;;;OAIG;IACH,eAAe,EAAE,WAAW,EAAE,CAAC;CAChC,GACD;IACE,MAAM,EAAE,OAAO,CAAC;IAChB,QAAQ,EAAE,aAAa,CAAC;IACxB,IAAI,EAAE,cAAc,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,IAAI,CAAC;CACb,CAAC;AAQN,qBAAa,eAAe;IAC1B,+EAA+E;IAC/E,MAAM,CAAC,iBAAiB,CACtB,UAAU,EAAE,UAAU,EACtB,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,GACb,YAAY;IAqBf,kBAAkB;IAClB,MAAM,CAAC,aAAa,CAAC,KAAK,EAAE,IAAI,GAAG,YAAY;CAoChD;AAMD,wBAAgB,cAAc,CAAC,KAAK,EAAE,IAAI,GAAG,cAAc,GAAG,IAAI,CA6BjE;AAMD,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,cAAc,GAAG;IAC5D,QAAQ,EAAE,IAAI,CAAC;IACf,KAAK,EAAE,WAAW,EAAE,CAAC;CACtB,CA6BA;AAuWD;;;;;GAKG;AACH,wBAAgB,uBAAuB,CACrC,QAAQ,EAAE,IAAI,EACd,IAAI,EAAE,MAAM,EACZ,SAAS,EAAE,MAAM,GAChB,IAAI,GAAG,IAAI,CAmBb;AAmHD,2EAA2E;AAC3E,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,cAAc,GAAG,IAAI,CAelE"}