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.
- package/README.md +103 -0
- package/bin/sela.js +3 -0
- package/dist/cli/ErrorHandler.d.ts +10 -0
- package/dist/cli/ErrorHandler.d.ts.map +1 -0
- package/dist/cli/ErrorHandler.js +70 -0
- package/dist/cli/commands/bulk.d.ts +3 -0
- package/dist/cli/commands/bulk.d.ts.map +1 -0
- package/dist/cli/commands/bulk.js +158 -0
- package/dist/cli/commands/find.d.ts +3 -0
- package/dist/cli/commands/find.d.ts.map +1 -0
- package/dist/cli/commands/find.js +51 -0
- package/dist/cli/commands/init.d.ts +3 -0
- package/dist/cli/commands/init.d.ts.map +1 -0
- package/dist/cli/commands/init.js +166 -0
- package/dist/cli/commands/list.d.ts +3 -0
- package/dist/cli/commands/list.d.ts.map +1 -0
- package/dist/cli/commands/list.js +56 -0
- package/dist/cli/commands/merge.d.ts +13 -0
- package/dist/cli/commands/merge.d.ts.map +1 -0
- package/dist/cli/commands/merge.js +119 -0
- package/dist/cli/commands/refactor.d.ts +3 -0
- package/dist/cli/commands/refactor.d.ts.map +1 -0
- package/dist/cli/commands/refactor.js +30 -0
- package/dist/cli/commands/showReport.d.ts +3 -0
- package/dist/cli/commands/showReport.d.ts.map +1 -0
- package/dist/cli/commands/showReport.js +82 -0
- package/dist/cli/commands/status.d.ts +3 -0
- package/dist/cli/commands/status.d.ts.map +1 -0
- package/dist/cli/commands/status.js +51 -0
- package/dist/cli/commands/sync.d.ts +3 -0
- package/dist/cli/commands/sync.d.ts.map +1 -0
- package/dist/cli/commands/sync.js +131 -0
- package/dist/cli/index.d.ts +2 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +94 -0
- package/dist/cli/ui/DnaTable.d.ts +3 -0
- package/dist/cli/ui/DnaTable.d.ts.map +1 -0
- package/dist/cli/ui/DnaTable.js +77 -0
- package/dist/cli/ui/LocatorPicker.d.ts +8 -0
- package/dist/cli/ui/LocatorPicker.d.ts.map +1 -0
- package/dist/cli/ui/LocatorPicker.js +33 -0
- package/dist/cli/ui/ProgressReporter.d.ts +11 -0
- package/dist/cli/ui/ProgressReporter.d.ts.map +1 -0
- package/dist/cli/ui/ProgressReporter.js +33 -0
- package/dist/cli/ui/RefactorWizard.d.ts +26 -0
- package/dist/cli/ui/RefactorWizard.d.ts.map +1 -0
- package/dist/cli/ui/RefactorWizard.js +295 -0
- package/dist/config/ConfigLoader.d.ts +16 -0
- package/dist/config/ConfigLoader.d.ts.map +1 -0
- package/dist/config/ConfigLoader.js +185 -0
- package/dist/config/DryRunGuard.d.ts +14 -0
- package/dist/config/DryRunGuard.d.ts.map +1 -0
- package/dist/config/DryRunGuard.js +80 -0
- package/dist/config/PathScopeGuard.d.ts +42 -0
- package/dist/config/PathScopeGuard.d.ts.map +1 -0
- package/dist/config/PathScopeGuard.js +192 -0
- package/dist/config/SelaConfig.d.ts +178 -0
- package/dist/config/SelaConfig.d.ts.map +1 -0
- package/dist/config/SelaConfig.js +141 -0
- package/dist/config/defineConfig.d.ts +3 -0
- package/dist/config/defineConfig.d.ts.map +1 -0
- package/dist/config/defineConfig.js +9 -0
- package/dist/engine/FixwrightEngine.d.ts +24 -0
- package/dist/engine/FixwrightEngine.d.ts.map +1 -0
- package/dist/engine/FixwrightEngine.js +403 -0
- package/dist/engine/HealingRegistry.d.ts +40 -0
- package/dist/engine/HealingRegistry.d.ts.map +1 -0
- package/dist/engine/HealingRegistry.js +99 -0
- package/dist/engine/SelaEngine.d.ts +57 -0
- package/dist/engine/SelaEngine.d.ts.map +1 -0
- package/dist/engine/SelaEngine.js +1146 -0
- package/dist/engine/singleton.d.ts +3 -0
- package/dist/engine/singleton.d.ts.map +1 -0
- package/dist/engine/singleton.js +5 -0
- package/dist/errors/SelaError.d.ts +146 -0
- package/dist/errors/SelaError.d.ts.map +1 -0
- package/dist/errors/SelaError.js +168 -0
- package/dist/fixtures/expectProxy.d.ts +12 -0
- package/dist/fixtures/expectProxy.d.ts.map +1 -0
- package/dist/fixtures/expectProxy.js +246 -0
- package/dist/fixtures/index.d.ts +30 -0
- package/dist/fixtures/index.d.ts.map +1 -0
- package/dist/fixtures/index.js +765 -0
- package/dist/fixtures/moduleExpect.d.ts +2 -0
- package/dist/fixtures/moduleExpect.d.ts.map +1 -0
- package/dist/fixtures/moduleExpect.js +54 -0
- package/dist/fixtures/proxyTag.d.ts +12 -0
- package/dist/fixtures/proxyTag.d.ts.map +1 -0
- package/dist/fixtures/proxyTag.js +45 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +31 -0
- package/dist/reporter/SelaReporter.d.ts +165 -0
- package/dist/reporter/SelaReporter.d.ts.map +1 -0
- package/dist/reporter/SelaReporter.js +517 -0
- package/dist/services/ASTSourceUpdater.d.ts +143 -0
- package/dist/services/ASTSourceUpdater.d.ts.map +1 -0
- package/dist/services/ASTSourceUpdater.js +3514 -0
- package/dist/services/AnchorResolver.d.ts +157 -0
- package/dist/services/AnchorResolver.d.ts.map +1 -0
- package/dist/services/AnchorResolver.js +289 -0
- package/dist/services/ArgumentTypeAnalyzer.d.ts +26 -0
- package/dist/services/ArgumentTypeAnalyzer.d.ts.map +1 -0
- package/dist/services/ArgumentTypeAnalyzer.js +92 -0
- package/dist/services/BlastRadiusAnalyzer.d.ts +15 -0
- package/dist/services/BlastRadiusAnalyzer.d.ts.map +1 -0
- package/dist/services/BlastRadiusAnalyzer.js +107 -0
- package/dist/services/ChainValidator.d.ts +76 -0
- package/dist/services/ChainValidator.d.ts.map +1 -0
- package/dist/services/ChainValidator.js +570 -0
- package/dist/services/CrossFileHealer.d.ts +6 -0
- package/dist/services/CrossFileHealer.d.ts.map +1 -0
- package/dist/services/CrossFileHealer.js +135 -0
- package/dist/services/DecisionEngine.d.ts +51 -0
- package/dist/services/DecisionEngine.d.ts.map +1 -0
- package/dist/services/DecisionEngine.js +260 -0
- package/dist/services/DefinitionTracer.d.ts +41 -0
- package/dist/services/DefinitionTracer.d.ts.map +1 -0
- package/dist/services/DefinitionTracer.js +366 -0
- package/dist/services/DnaEditorService.d.ts +31 -0
- package/dist/services/DnaEditorService.d.ts.map +1 -0
- package/dist/services/DnaEditorService.js +217 -0
- package/dist/services/DnaIndexService.d.ts +24 -0
- package/dist/services/DnaIndexService.d.ts.map +1 -0
- package/dist/services/DnaIndexService.js +131 -0
- package/dist/services/HealBudget.d.ts +32 -0
- package/dist/services/HealBudget.d.ts.map +1 -0
- package/dist/services/HealBudget.js +85 -0
- package/dist/services/HealReportService.d.ts +161 -0
- package/dist/services/HealReportService.d.ts.map +1 -0
- package/dist/services/HealReportService.js +1039 -0
- package/dist/services/HealingAdvisory.d.ts +30 -0
- package/dist/services/HealingAdvisory.d.ts.map +1 -0
- package/dist/services/HealingAdvisory.js +51 -0
- package/dist/services/HealingCacheService.d.ts +135 -0
- package/dist/services/HealingCacheService.d.ts.map +1 -0
- package/dist/services/HealingCacheService.js +404 -0
- package/dist/services/HealthReportService.d.ts +10 -0
- package/dist/services/HealthReportService.d.ts.map +1 -0
- package/dist/services/HealthReportService.js +84 -0
- package/dist/services/InitializerUpdater.d.ts +16 -0
- package/dist/services/InitializerUpdater.d.ts.map +1 -0
- package/dist/services/InitializerUpdater.js +73 -0
- package/dist/services/IntentAuditor.d.ts +56 -0
- package/dist/services/IntentAuditor.d.ts.map +1 -0
- package/dist/services/IntentAuditor.js +326 -0
- package/dist/services/InteractiveReview.d.ts +15 -0
- package/dist/services/InteractiveReview.d.ts.map +1 -0
- package/dist/services/InteractiveReview.js +128 -0
- package/dist/services/LLMService.d.ts +121 -0
- package/dist/services/LLMService.d.ts.map +1 -0
- package/dist/services/LLMService.js +475 -0
- package/dist/services/MutationApplier.d.ts +69 -0
- package/dist/services/MutationApplier.d.ts.map +1 -0
- package/dist/services/MutationApplier.js +360 -0
- package/dist/services/PRAutomationService.d.ts +50 -0
- package/dist/services/PRAutomationService.d.ts.map +1 -0
- package/dist/services/PRAutomationService.js +760 -0
- package/dist/services/PendingPromptLedger.d.ts +51 -0
- package/dist/services/PendingPromptLedger.d.ts.map +1 -0
- package/dist/services/PendingPromptLedger.js +205 -0
- package/dist/services/ReportGenerator.d.ts +156 -0
- package/dist/services/ReportGenerator.d.ts.map +1 -0
- package/dist/services/ReportGenerator.js +278 -0
- package/dist/services/ReportMergeService.d.ts +95 -0
- package/dist/services/ReportMergeService.d.ts.map +1 -0
- package/dist/services/ReportMergeService.js +0 -0
- package/dist/services/SafetyGuard.d.ts +112 -0
- package/dist/services/SafetyGuard.d.ts.map +1 -0
- package/dist/services/SafetyGuard.js +87 -0
- package/dist/services/Sanitizer.d.ts +28 -0
- package/dist/services/Sanitizer.d.ts.map +1 -0
- package/dist/services/Sanitizer.js +142 -0
- package/dist/services/SanitizingAnthropic.d.ts +19 -0
- package/dist/services/SanitizingAnthropic.d.ts.map +1 -0
- package/dist/services/SanitizingAnthropic.js +84 -0
- package/dist/services/SelectorSanitizer.d.ts +52 -0
- package/dist/services/SelectorSanitizer.d.ts.map +1 -0
- package/dist/services/SelectorSanitizer.js +318 -0
- package/dist/services/SnapshotService.d.ts +11 -0
- package/dist/services/SnapshotService.d.ts.map +1 -0
- package/dist/services/SnapshotService.js +352 -0
- package/dist/services/SourceLinkService.d.ts +26 -0
- package/dist/services/SourceLinkService.d.ts.map +1 -0
- package/dist/services/SourceLinkService.js +165 -0
- package/dist/services/SourceUpdater.d.ts +45 -0
- package/dist/services/SourceUpdater.d.ts.map +1 -0
- package/dist/services/SourceUpdater.js +1091 -0
- package/dist/services/TemplateDiffService.d.ts +25 -0
- package/dist/services/TemplateDiffService.d.ts.map +1 -0
- package/dist/services/TemplateDiffService.js +334 -0
- package/dist/services/TraceBackEngine.d.ts +67 -0
- package/dist/services/TraceBackEngine.d.ts.map +1 -0
- package/dist/services/TraceBackEngine.js +672 -0
- package/dist/services/WorkspaceSnapshotService.d.ts +71 -0
- package/dist/services/WorkspaceSnapshotService.d.ts.map +1 -0
- package/dist/services/WorkspaceSnapshotService.js +200 -0
- package/dist/services/safety/PipelineContext.d.ts +63 -0
- package/dist/services/safety/PipelineContext.d.ts.map +1 -0
- package/dist/services/safety/PipelineContext.js +34 -0
- package/dist/services/safety/roleClassifier.d.ts +4 -0
- package/dist/services/safety/roleClassifier.d.ts.map +1 -0
- package/dist/services/safety/roleClassifier.js +233 -0
- package/dist/services/safety/rules/ConfidenceTierRule.d.ts +3 -0
- package/dist/services/safety/rules/ConfidenceTierRule.d.ts.map +1 -0
- package/dist/services/safety/rules/ConfidenceTierRule.js +32 -0
- package/dist/services/safety/rules/DangerousSinkRule.d.ts +10 -0
- package/dist/services/safety/rules/DangerousSinkRule.d.ts.map +1 -0
- package/dist/services/safety/rules/DangerousSinkRule.js +53 -0
- package/dist/services/safety/rules/FunctionalRoleRule.d.ts +3 -0
- package/dist/services/safety/rules/FunctionalRoleRule.d.ts.map +1 -0
- package/dist/services/safety/rules/FunctionalRoleRule.js +64 -0
- package/dist/services/safety/rules/HardConfidenceRule.d.ts +3 -0
- package/dist/services/safety/rules/HardConfidenceRule.d.ts.map +1 -0
- package/dist/services/safety/rules/HardConfidenceRule.js +22 -0
- package/dist/services/safety/rules/IntentAuditorRule.d.ts +3 -0
- package/dist/services/safety/rules/IntentAuditorRule.d.ts.map +1 -0
- package/dist/services/safety/rules/IntentAuditorRule.js +97 -0
- package/dist/services/safety/rules/SemanticOppositeRule.d.ts +3 -0
- package/dist/services/safety/rules/SemanticOppositeRule.d.ts.map +1 -0
- package/dist/services/safety/rules/SemanticOppositeRule.js +30 -0
- package/dist/services/safety/rules/StructuralOnlyRule.d.ts +3 -0
- package/dist/services/safety/rules/StructuralOnlyRule.d.ts.map +1 -0
- package/dist/services/safety/rules/StructuralOnlyRule.js +38 -0
- package/dist/services/safety/rules/VisibilityRule.d.ts +3 -0
- package/dist/services/safety/rules/VisibilityRule.d.ts.map +1 -0
- package/dist/services/safety/rules/VisibilityRule.js +53 -0
- package/dist/services/safety/rules/ZeroTrustRule.d.ts +3 -0
- package/dist/services/safety/rules/ZeroTrustRule.d.ts.map +1 -0
- package/dist/services/safety/rules/ZeroTrustRule.js +37 -0
- package/dist/services/safety/rules/index.d.ts +3 -0
- package/dist/services/safety/rules/index.d.ts.map +1 -0
- package/dist/services/safety/rules/index.js +34 -0
- package/dist/services/types.d.ts +59 -0
- package/dist/services/types.d.ts.map +1 -0
- package/dist/services/types.js +2 -0
- package/dist/storage/SnapshotManager.d.ts +5 -0
- package/dist/storage/SnapshotManager.d.ts.map +1 -0
- package/dist/storage/SnapshotManager.js +32 -0
- package/dist/types/index.d.ts +95 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +7 -0
- package/dist/utils/DOMUtils.d.ts +49 -0
- package/dist/utils/DOMUtils.d.ts.map +1 -0
- package/dist/utils/DOMUtils.js +466 -0
- package/dist/utils/FileLock.d.ts +44 -0
- package/dist/utils/FileLock.d.ts.map +1 -0
- package/dist/utils/FileLock.js +189 -0
- package/dist/utils/IsolatedDiff.d.ts +45 -0
- package/dist/utils/IsolatedDiff.d.ts.map +1 -0
- package/dist/utils/IsolatedDiff.js +382 -0
- package/dist/utils/StackUtils.d.ts +11 -0
- package/dist/utils/StackUtils.d.ts.map +1 -0
- package/dist/utils/StackUtils.js +120 -0
- package/dist/utils/SummaryTable.d.ts +17 -0
- package/dist/utils/SummaryTable.d.ts.map +1 -0
- package/dist/utils/SummaryTable.js +37 -0
- package/dist/utils/atomicWrite.d.ts +9 -0
- package/dist/utils/atomicWrite.d.ts.map +1 -0
- package/dist/utils/atomicWrite.js +83 -0
- package/dist/utils/logger.d.ts +59 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +123 -0
- package/dist/vendor/enquirer.d.ts +33 -0
- package/dist/vendor/enquirer.d.ts.map +1 -0
- package/dist/vendor/enquirer.js +11 -0
- 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"}
|