sela-core 1.0.7 → 1.0.8
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/dist/cli/ErrorHandler.d.ts +1 -1
- package/dist/cli/ErrorHandler.js +14 -14
- package/dist/cli/commands/bulk.d.ts +1 -1
- package/dist/cli/commands/bulk.d.ts.map +1 -1
- package/dist/cli/commands/bulk.js +51 -33
- package/dist/cli/commands/init.d.ts +1 -1
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli/commands/init.js +26 -18
- package/dist/cli/commands/showReport.d.ts +1 -1
- package/dist/cli/commands/showReport.d.ts.map +1 -1
- package/dist/cli/commands/showReport.js +12 -10
- package/dist/cli/commands/status.d.ts +1 -1
- package/dist/cli/commands/status.d.ts.map +1 -1
- package/dist/cli/commands/status.js +8 -8
- package/dist/cli/commands/sync.d.ts +1 -1
- package/dist/cli/commands/sync.d.ts.map +1 -1
- package/dist/cli/commands/sync.js +38 -30
- package/dist/cli/index.js +9 -9
- package/dist/cli/ui/DnaTable.d.ts +1 -1
- package/dist/cli/ui/DnaTable.d.ts.map +1 -1
- package/dist/cli/ui/DnaTable.js +23 -16
- package/dist/cli/ui/RefactorWizard.d.ts +3 -3
- package/dist/cli/ui/RefactorWizard.d.ts.map +1 -1
- package/dist/cli/ui/RefactorWizard.js +108 -82
- package/dist/config/ConfigLoader.d.ts +2 -2
- package/dist/config/ConfigLoader.d.ts.map +1 -1
- package/dist/config/ConfigLoader.js +15 -15
- package/dist/config/DryRunGuard.d.ts +1 -1
- package/dist/config/DryRunGuard.js +7 -7
- package/dist/config/SelaConfig.d.ts +12 -16
- package/dist/config/SelaConfig.d.ts.map +1 -1
- package/dist/config/SelaConfig.js +32 -26
- package/dist/engine/HealingRegistry.d.ts +1 -1
- package/dist/engine/HealingRegistry.js +3 -3
- package/dist/engine/SelaEngine.d.ts +1 -1
- package/dist/engine/SelaEngine.d.ts.map +1 -1
- package/dist/engine/SelaEngine.js +63 -49
- package/dist/errors/SelaError.d.ts +0 -1
- package/dist/errors/SelaError.d.ts.map +1 -1
- package/dist/errors/SelaError.js +6 -7
- package/dist/fixtures/expectProxy.d.ts +1 -1
- package/dist/fixtures/expectProxy.js +11 -11
- package/dist/fixtures/index.js +14 -14
- package/dist/fixtures/proxyTag.d.ts +1 -1
- package/dist/fixtures/proxyTag.js +3 -3
- package/dist/index.d.ts +8 -8
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -4
- package/dist/reporter/SelaReporter.d.ts +45 -12
- package/dist/reporter/SelaReporter.d.ts.map +1 -1
- package/dist/reporter/SelaReporter.js +139 -39
- package/dist/services/ASTSourceUpdater.d.ts +3 -3
- package/dist/services/ASTSourceUpdater.d.ts.map +1 -1
- package/dist/services/ASTSourceUpdater.js +92 -68
- package/dist/services/ArgumentTypeAnalyzer.js +7 -7
- package/dist/services/BlastRadiusAnalyzer.d.ts.map +1 -1
- package/dist/services/BlastRadiusAnalyzer.js +5 -2
- package/dist/services/ChainValidator.js +22 -22
- package/dist/services/CrossFileHealer.js +7 -7
- package/dist/services/DefinitionTracer.d.ts +1 -1
- package/dist/services/DefinitionTracer.d.ts.map +1 -1
- package/dist/services/DefinitionTracer.js +28 -13
- package/dist/services/DnaEditorService.d.ts +2 -2
- package/dist/services/DnaEditorService.d.ts.map +1 -1
- package/dist/services/DnaEditorService.js +81 -62
- package/dist/services/DnaIndexService.d.ts +1 -1
- package/dist/services/DnaIndexService.d.ts.map +1 -1
- package/dist/services/DnaIndexService.js +14 -14
- package/dist/services/HealReportService.d.ts +6 -6
- package/dist/services/HealReportService.d.ts.map +1 -1
- package/dist/services/HealReportService.js +33 -29
- package/dist/services/HealingAdvisory.js +1 -1
- package/dist/services/HealingCacheService.d.ts +8 -8
- package/dist/services/HealingCacheService.d.ts.map +1 -1
- package/dist/services/HealingCacheService.js +24 -24
- package/dist/services/HealthReportService.d.ts +1 -1
- package/dist/services/HealthReportService.d.ts.map +1 -1
- package/dist/services/HealthReportService.js +25 -25
- package/dist/services/InitializerUpdater.js +2 -2
- package/dist/services/IntentAuditor.d.ts.map +1 -1
- package/dist/services/IntentAuditor.js +32 -21
- package/dist/services/InteractiveReview.d.ts +8 -17
- package/dist/services/InteractiveReview.d.ts.map +1 -1
- package/dist/services/InteractiveReview.js +31 -124
- package/dist/services/LLMService.d.ts +1 -1
- package/dist/services/LLMService.js +16 -16
- package/dist/services/PRAutomationService.d.ts +2 -2
- package/dist/services/PRAutomationService.d.ts.map +1 -1
- package/dist/services/PRAutomationService.js +43 -39
- package/dist/services/PendingPromptLedger.d.ts +2 -2
- package/dist/services/PendingPromptLedger.js +6 -6
- package/dist/services/ReportGenerator.d.ts +2 -2
- package/dist/services/ReportGenerator.d.ts.map +1 -1
- package/dist/services/ReportGenerator.js +5 -5
- package/dist/services/SafetyGuard.d.ts +3 -6
- package/dist/services/SafetyGuard.d.ts.map +1 -1
- package/dist/services/SafetyGuard.js +29 -106
- package/dist/services/SnapshotService.js +5 -5
- package/dist/services/SourceLinkService.d.ts +2 -2
- package/dist/services/SourceLinkService.d.ts.map +1 -1
- package/dist/services/SourceLinkService.js +31 -22
- package/dist/services/SourceUpdater.d.ts +1 -1
- package/dist/services/SourceUpdater.d.ts.map +1 -1
- package/dist/services/SourceUpdater.js +27 -26
- package/dist/services/TemplateDiffService.d.ts.map +1 -1
- package/dist/services/TemplateDiffService.js +18 -15
- package/dist/services/WorkspaceSnapshotService.d.ts +6 -6
- package/dist/services/WorkspaceSnapshotService.d.ts.map +1 -1
- package/dist/services/WorkspaceSnapshotService.js +10 -12
- package/dist/types/index.d.ts +1 -1
- package/dist/utils/IsolatedDiff.d.ts +2 -2
- package/dist/utils/IsolatedDiff.d.ts.map +1 -1
- package/dist/utils/IsolatedDiff.js +13 -10
- package/package.json +2 -2
|
@@ -50,7 +50,7 @@ const CrossFileHealer_1 = require("./CrossFileHealer");
|
|
|
50
50
|
const HealingAdvisory_1 = require("./HealingAdvisory");
|
|
51
51
|
const ChainValidator_1 = require("./ChainValidator");
|
|
52
52
|
// ═══════════════════════════════════════════════════════════════════
|
|
53
|
-
// FIX #1
|
|
53
|
+
// FIX #1 - normalizeSelector utility
|
|
54
54
|
// ═══════════════════════════════════════════════════════════════════
|
|
55
55
|
function normalizeSelector(s) {
|
|
56
56
|
return s
|
|
@@ -70,7 +70,7 @@ function normalizeSelector(s) {
|
|
|
70
70
|
// of "varName", or null otherwise.
|
|
71
71
|
//
|
|
72
72
|
// Why this matters:
|
|
73
|
-
// const shadowBtn = shadowBtn.getByRole(…) ← CIRCULAR
|
|
73
|
+
// const shadowBtn = shadowBtn.getByRole(…) ← CIRCULAR - must be caught
|
|
74
74
|
// const shadowBtn = page.getByRole(…) ← CORRECT
|
|
75
75
|
//
|
|
76
76
|
// We walk UP the ancestor chain from the call node looking for a
|
|
@@ -83,7 +83,7 @@ function getEnclosingVariableDeclaration(node) {
|
|
|
83
83
|
if (ts_morph_1.Node.isVariableDeclaration(current)) {
|
|
84
84
|
return current;
|
|
85
85
|
}
|
|
86
|
-
// Stop climbing past statement boundaries
|
|
86
|
+
// Stop climbing past statement boundaries - we only want the immediate
|
|
87
87
|
// enclosing declaration, not a declaration somewhere further up the tree.
|
|
88
88
|
if (ts_morph_1.Node.isBlock(current) ||
|
|
89
89
|
ts_morph_1.Node.isSourceFile(current) ||
|
|
@@ -121,7 +121,7 @@ function isInsideOwnInitializer(callNode, varName) {
|
|
|
121
121
|
function resolveSafeReceiver(proposedReceiver, rootReceiver, callNode) {
|
|
122
122
|
if (isInsideOwnInitializer(callNode, proposedReceiver)) {
|
|
123
123
|
console.log(`[ScopeGuard] 🔒 Circular reference prevented: ` +
|
|
124
|
-
`"${proposedReceiver}" is being initialized
|
|
124
|
+
`"${proposedReceiver}" is being initialized - ` +
|
|
125
125
|
`falling back to root receiver "${rootReceiver}"`);
|
|
126
126
|
return rootReceiver;
|
|
127
127
|
}
|
|
@@ -554,7 +554,7 @@ class MethodMapper {
|
|
|
554
554
|
for (const seg of segments) {
|
|
555
555
|
const callText = MethodMapper.build(currentReceiver, seg);
|
|
556
556
|
if (callText === null) {
|
|
557
|
-
console.warn(`[MethodMapper] Cannot build segment type "${seg.type}"
|
|
557
|
+
console.warn(`[MethodMapper] Cannot build segment type "${seg.type}" - aborting chain`);
|
|
558
558
|
return null;
|
|
559
559
|
}
|
|
560
560
|
const methodPart = callText.slice(currentReceiver.length);
|
|
@@ -617,7 +617,7 @@ function deduplicateChainSegments(aiSegments, contextSegments) {
|
|
|
617
617
|
if (overlap === 0)
|
|
618
618
|
return { deduped: aiSegments, stripped: 0 };
|
|
619
619
|
console.log(`[ChainDedup] ✂️ Stripping ${overlap}/${contextSegments.length} duplicate leading ` +
|
|
620
|
-
`segment(s)
|
|
620
|
+
`segment(s) - context already encodes them`);
|
|
621
621
|
return { deduped: aiSegments.slice(overlap), stripped: overlap };
|
|
622
622
|
}
|
|
623
623
|
const PAGE_ROOT_NAMES = new Set([
|
|
@@ -731,15 +731,15 @@ function resolveVariableToSegments(sourceFile, varName, beforeLine, depth = 0) {
|
|
|
731
731
|
// Module-level write gate shared between `ASTSourceUpdater` and the
|
|
732
732
|
// internal `NodeTransformationStrategy`. Centralises three concerns
|
|
733
733
|
// every `saveSync` call site needs:
|
|
734
|
-
// 1. `DryRunGuard.active()` short-circuit
|
|
734
|
+
// 1. `DryRunGuard.active()` short-circuit - keeps the mutation in
|
|
735
735
|
// memory but suppresses the on-disk write under SELA_DRY_RUN.
|
|
736
|
-
// 2. `sharedWorkspaceSnapshot.preWrite()`
|
|
736
|
+
// 2. `sharedWorkspaceSnapshot.preWrite()` - captures `contentBefore`
|
|
737
737
|
// so the Clean-Room ledger / Local DX rollback can rewind.
|
|
738
|
-
// 3. `sharedWorkspaceSnapshot.postWrite()`
|
|
738
|
+
// 3. `sharedWorkspaceSnapshot.postWrite()` - refreshes `contentAfter`
|
|
739
739
|
// so `getMutatedSnapshots()` (driving the pending-prompts ledger
|
|
740
740
|
// and the interactive CLI prompt) sees the new disk state.
|
|
741
741
|
//
|
|
742
|
-
// Strategies do NOT receive the persistence service via DI
|
|
742
|
+
// Strategies do NOT receive the persistence service via DI - they call
|
|
743
743
|
// this helper directly. Same singleton, zero coupling, one code path.
|
|
744
744
|
// ═══════════════════════════════════════════════════════════════════
|
|
745
745
|
function persistSourceFile(sourceFile) {
|
|
@@ -753,7 +753,7 @@ function persistSourceFile(sourceFile) {
|
|
|
753
753
|
WorkspaceSnapshotService_1.sharedWorkspaceSnapshot.postWrite(fp);
|
|
754
754
|
}
|
|
755
755
|
// ═══════════════════════════════════════════════════════════════════
|
|
756
|
-
// NODE TRANSFORMATION STRATEGY
|
|
756
|
+
// NODE TRANSFORMATION STRATEGY - Scope-Aware, Zero-Redundancy
|
|
757
757
|
// ═══════════════════════════════════════════════════════════════════
|
|
758
758
|
class NodeTransformationStrategy {
|
|
759
759
|
chainValidator;
|
|
@@ -823,8 +823,10 @@ class NodeTransformationStrategy {
|
|
|
823
823
|
// When AI's first segment TYPE matches the receiver variable's defining type
|
|
824
824
|
// but with a different selector, the AI is replacing the receiver context.
|
|
825
825
|
// Strategy: update the receiver variable's definition, then use remaining
|
|
826
|
-
// segments with the receiver var
|
|
827
|
-
if (!forcedReceiver &&
|
|
826
|
+
// segments with the receiver var - preserving developer's variable structure.
|
|
827
|
+
if (!forcedReceiver &&
|
|
828
|
+
discoveredAstRoot &&
|
|
829
|
+
!PAGE_ROOT_NAMES.has(discoveredAstRoot)) {
|
|
828
830
|
const typeCheckRes = resolveVariableToSegments(sourceFile, discoveredAstRoot, caller.line);
|
|
829
831
|
if (typeCheckRes.segments.length > 0 && workingSegments.length > 0) {
|
|
830
832
|
const receiverDefType = typeCheckRes.segments[typeCheckRes.segments.length - 1].type;
|
|
@@ -923,8 +925,9 @@ class NodeTransformationStrategy {
|
|
|
923
925
|
// to prevent. Skip the candidate; the semantic gate owns this case.
|
|
924
926
|
const firstArg = call.getArguments()[0];
|
|
925
927
|
if (firstArg &&
|
|
926
|
-
(ts_morph_1.Node.isIdentifier(firstArg) ||
|
|
927
|
-
|
|
928
|
+
(ts_morph_1.Node.isIdentifier(firstArg) ||
|
|
929
|
+
ts_morph_1.Node.isPropertyAccessExpression(firstArg))) {
|
|
930
|
+
console.log(`[NodeTransform] 🛑 skip - first arg is ${firstArg.getKindName()} ` +
|
|
928
931
|
`"${firstArg.getText()}" (semantic dispatch owns definition-site mutations)`);
|
|
929
932
|
continue;
|
|
930
933
|
}
|
|
@@ -936,12 +939,12 @@ class NodeTransformationStrategy {
|
|
|
936
939
|
// Before we build code, we must verify that the proposed receiver
|
|
937
940
|
// does not create a circular self-reference.
|
|
938
941
|
//
|
|
939
|
-
// Case A
|
|
942
|
+
// Case A - forcedReceiver is set (strip-and-force path):
|
|
940
943
|
// Check if the chainTop lives inside the initializer of that var.
|
|
941
944
|
// If yes, fall back to the actual AST root (e.g. "page").
|
|
942
945
|
//
|
|
943
|
-
// Case B
|
|
944
|
-
// Use astRoot directly
|
|
946
|
+
// Case B - no forced receiver:
|
|
947
|
+
// Use astRoot directly - it was walked up from the call node and
|
|
945
948
|
// is always either "page" or a stable variable reference.
|
|
946
949
|
// ══════════════════════════════════════════════════════════════
|
|
947
950
|
let effectiveReceiver;
|
|
@@ -1191,7 +1194,7 @@ class NodeTransformationStrategy {
|
|
|
1191
1194
|
}
|
|
1192
1195
|
updateReceiverDefinition(sourceFile, varName, segType, newSelector, callerLine) {
|
|
1193
1196
|
// Collect all matching declarations, then pick the closest one that
|
|
1194
|
-
// appears strictly BEFORE callerLine
|
|
1197
|
+
// appears strictly BEFORE callerLine - prevents accidentally updating
|
|
1195
1198
|
// a same-named variable in a different test block that happens to be
|
|
1196
1199
|
// within the 100-line distance window.
|
|
1197
1200
|
const declarations = sourceFile.getDescendantsOfKind(ts_morph_1.SyntaxKind.VariableDeclaration);
|
|
@@ -1302,7 +1305,7 @@ class NodeTransformationStrategy {
|
|
|
1302
1305
|
.map((d) => `[TS${d.getCode()}] ${d.getMessageText()}`)
|
|
1303
1306
|
.join(", "));
|
|
1304
1307
|
if (/\.(getBy\w+|frameLocator)\s*\(/.test(code)) {
|
|
1305
|
-
console.warn(`[NodeTransform] ⚠️ Semantic method detected
|
|
1308
|
+
console.warn(`[NodeTransform] ⚠️ Semantic method detected - bypassing validation`);
|
|
1306
1309
|
return true;
|
|
1307
1310
|
}
|
|
1308
1311
|
return false;
|
|
@@ -1562,7 +1565,7 @@ class ASTSourceUpdater {
|
|
|
1562
1565
|
/**
|
|
1563
1566
|
* Centralised write hook for every ts-morph `saveSync` call in this class.
|
|
1564
1567
|
* When `SELA_DRY_RUN` is active the AST mutation stays in memory but the
|
|
1565
|
-
* on-disk file is left untouched
|
|
1568
|
+
* on-disk file is left untouched - the heal pipeline can still compute a
|
|
1566
1569
|
* candidate diff via `sourceFile.getFullText()` and the report layer
|
|
1567
1570
|
* tags the event with `dryRun: true`.
|
|
1568
1571
|
*/
|
|
@@ -1579,7 +1582,7 @@ class ASTSourceUpdater {
|
|
|
1579
1582
|
// BEFORE the LLM call and record a FailedEvent in the "Skipped by
|
|
1580
1583
|
// Developer" category.
|
|
1581
1584
|
//
|
|
1582
|
-
// Implementation note
|
|
1585
|
+
// Implementation note - ts-morph's `Node.getLeadingCommentRanges()`
|
|
1583
1586
|
// returns `ts.CommentRange[]` (offsets, not text). We slice the full
|
|
1584
1587
|
// source text by `(pos, end)` to recover the raw comment for each
|
|
1585
1588
|
// range, matching both `//` line comments and `/* */` block comments
|
|
@@ -1643,7 +1646,7 @@ class ASTSourceUpdater {
|
|
|
1643
1646
|
return { failFast: false };
|
|
1644
1647
|
}
|
|
1645
1648
|
/**
|
|
1646
|
-
* Convenience helper
|
|
1649
|
+
* Convenience helper - runs `scanFailFastDirective` and throws the
|
|
1647
1650
|
* canonical SelaError when the directive is present. Used by SelaEngine
|
|
1648
1651
|
* as the very first gate inside `heal()`.
|
|
1649
1652
|
*/
|
|
@@ -1790,7 +1793,7 @@ class ASTSourceUpdater {
|
|
|
1790
1793
|
sourceFile = this.project.addSourceFileAtPath(filePath);
|
|
1791
1794
|
}
|
|
1792
1795
|
if (caller.line <= 0) {
|
|
1793
|
-
console.log(`[ASTUpdater] ⚠️ line ${caller.line} invalid
|
|
1796
|
+
console.log(`[ASTUpdater] ⚠️ line ${caller.line} invalid - Attempting Global Template Strategy`);
|
|
1794
1797
|
const globalResult = this.strategyTemplateLiteral(sourceFile, caller, oldSelector, newSelector);
|
|
1795
1798
|
if (globalResult && globalResult.success) {
|
|
1796
1799
|
if (contentChange)
|
|
@@ -1814,7 +1817,7 @@ class ASTSourceUpdater {
|
|
|
1814
1817
|
// node when they differ) for `// sela-fail-fast`. When present, abort
|
|
1815
1818
|
// all heal work with SelaError(AST_HEAL_DISABLED_BY_DIRECTIVE) so the
|
|
1816
1819
|
// engine records a FailedEvent in the "Skipped by Developer" bucket.
|
|
1817
|
-
// Defense-in-depth
|
|
1820
|
+
// Defense-in-depth - SelaEngine also calls this at the very start of
|
|
1818
1821
|
// heal() to skip the LLM round-trip entirely.
|
|
1819
1822
|
this.enforceFailFastDirective(caller);
|
|
1820
1823
|
const suspectIdentifier = statement
|
|
@@ -1827,7 +1830,7 @@ class ASTSourceUpdater {
|
|
|
1827
1830
|
// HARD EXIT: any non-null result (success OR failure) stops all
|
|
1828
1831
|
// subsequent AST strategies. When the locator argument is an
|
|
1829
1832
|
// Identifier, PropertyAccess, or TemplateLiteral, the gate owns
|
|
1830
|
-
// the mutation
|
|
1833
|
+
// the mutation - NT/H must never override the symbolic reference.
|
|
1831
1834
|
const semanticResult = this.semanticDispatch(sourceFile, caller, oldSelector, newSelector);
|
|
1832
1835
|
if (semanticResult !== null) {
|
|
1833
1836
|
if (semanticResult.success) {
|
|
@@ -1836,7 +1839,7 @@ class ASTSourceUpdater {
|
|
|
1836
1839
|
}
|
|
1837
1840
|
return this.broadcastAndReturn(semanticResult, oldSelector, newSelector, caller);
|
|
1838
1841
|
}
|
|
1839
|
-
// Failure (blast-radius block, trace failed, syntax error)
|
|
1842
|
+
// Failure (blast-radius block, trace failed, syntax error) - hard stop.
|
|
1840
1843
|
console.log(`[SemanticDispatch] 🚫 Hard stop (${semanticResult.strategy}): ${semanticResult.reason}`);
|
|
1841
1844
|
return semanticResult;
|
|
1842
1845
|
}
|
|
@@ -1885,7 +1888,7 @@ class ASTSourceUpdater {
|
|
|
1885
1888
|
};
|
|
1886
1889
|
}
|
|
1887
1890
|
// ═══════════════════════════════════════════════════════════════
|
|
1888
|
-
// STRATEGY H
|
|
1891
|
+
// STRATEGY H - Smart Chain (Legacy fallback)
|
|
1889
1892
|
// ═══════════════════════════════════════════════════════════════
|
|
1890
1893
|
strategySmartChain(sourceFile, caller, oldSelector, oldSem, smartSegments, originalChainHint, suspectIdentifier) {
|
|
1891
1894
|
console.log(`[ASTUpdater] 🔗 Strategy H v2: Smart Chain (${smartSegments.length} segments)`);
|
|
@@ -1959,13 +1962,14 @@ class ASTSourceUpdater {
|
|
|
1959
1962
|
return b.chainDepth - a.chainDepth;
|
|
1960
1963
|
});
|
|
1961
1964
|
for (const { leafCall, chainTop, rootReceiver, chainDepth, originalText, } of candidates) {
|
|
1962
|
-
// Identifier/PropertyAccess guard
|
|
1965
|
+
// Identifier/PropertyAccess guard - mirrors the NT guard.
|
|
1963
1966
|
// Strategy H must never substitute a hardcoded string for a symbolic
|
|
1964
1967
|
// reference; the semantic dispatch gate owns definition-site mutations.
|
|
1965
1968
|
const firstArgH = leafCall.getArguments()[0];
|
|
1966
1969
|
if (firstArgH &&
|
|
1967
|
-
(ts_morph_1.Node.isIdentifier(firstArgH) ||
|
|
1968
|
-
|
|
1970
|
+
(ts_morph_1.Node.isIdentifier(firstArgH) ||
|
|
1971
|
+
ts_morph_1.Node.isPropertyAccessExpression(firstArgH))) {
|
|
1972
|
+
console.log(`[StrategyH] 🛑 skip - first arg is ${firstArgH.getKindName()} ` +
|
|
1969
1973
|
`"${firstArgH.getText()}" (semantic dispatch owns definition-site mutations)`);
|
|
1970
1974
|
continue;
|
|
1971
1975
|
}
|
|
@@ -2035,8 +2039,9 @@ class ASTSourceUpdater {
|
|
|
2035
2039
|
// Block H-partial from substituting a literal for an Identifier/PropertyAccess arg.
|
|
2036
2040
|
const targetFirstArg = targetCall.getArguments()[0];
|
|
2037
2041
|
if (targetFirstArg &&
|
|
2038
|
-
(ts_morph_1.Node.isIdentifier(targetFirstArg) ||
|
|
2039
|
-
|
|
2042
|
+
(ts_morph_1.Node.isIdentifier(targetFirstArg) ||
|
|
2043
|
+
ts_morph_1.Node.isPropertyAccessExpression(targetFirstArg))) {
|
|
2044
|
+
console.log(`[H-partial] 🛑 skip - arg is ${targetFirstArg.getKindName()} ` +
|
|
2040
2045
|
`"${targetFirstArg.getText()}" (semantic dispatch owns this)`);
|
|
2041
2046
|
return null;
|
|
2042
2047
|
}
|
|
@@ -2072,7 +2077,7 @@ class ASTSourceUpdater {
|
|
|
2072
2077
|
}
|
|
2073
2078
|
}
|
|
2074
2079
|
// ─────────────────────────────────────────────────────────────
|
|
2075
|
-
// STRATEGY F
|
|
2080
|
+
// STRATEGY F - Chain Collapse
|
|
2076
2081
|
// ─────────────────────────────────────────────────────────────
|
|
2077
2082
|
strategyChainCollapse(sourceFile, caller, oldSelector, newSelector, oldSem, newSem) {
|
|
2078
2083
|
console.log(`[ASTUpdater] ⛓️ Strategy F: Chain Collapse (upward traversal)`);
|
|
@@ -2138,7 +2143,7 @@ class ASTSourceUpdater {
|
|
|
2138
2143
|
};
|
|
2139
2144
|
}
|
|
2140
2145
|
// ─────────────────────────────────────────────────────────────
|
|
2141
|
-
// STRATEGY G
|
|
2146
|
+
// STRATEGY G - Assertion Healing
|
|
2142
2147
|
// ─────────────────────────────────────────────────────────────
|
|
2143
2148
|
runStrategyG(sourceFile, caller, locatorVarName, contentChange) {
|
|
2144
2149
|
if (!contentChange)
|
|
@@ -2170,7 +2175,7 @@ class ASTSourceUpdater {
|
|
|
2170
2175
|
return null;
|
|
2171
2176
|
}
|
|
2172
2177
|
// ─────────────────────────────────────────────────────────────
|
|
2173
|
-
// STRATEGY A
|
|
2178
|
+
// STRATEGY A - Frame Upstream Healing
|
|
2174
2179
|
// ─────────────────────────────────────────────────────────────
|
|
2175
2180
|
strategyFrameUpstream(sourceFile, caller, oldElementSelector, aiSegments, fullSelectorContext) {
|
|
2176
2181
|
console.log(`[ASTUpdater] 🧠 Strategy A: Frame Upstream`);
|
|
@@ -2246,7 +2251,7 @@ class ASTSourceUpdater {
|
|
|
2246
2251
|
};
|
|
2247
2252
|
}
|
|
2248
2253
|
// ─────────────────────────────────────────────────────────────
|
|
2249
|
-
// STRATEGY B
|
|
2254
|
+
// STRATEGY B - Deep Symbol Resolution
|
|
2250
2255
|
// ─────────────────────────────────────────────────────────────
|
|
2251
2256
|
strategyDeepSymbolResolution(sourceFile, caller, oldSelector, newSelector, oldSem, newSem, suspectIdentifier) {
|
|
2252
2257
|
console.log(`[ASTUpdater] 🔭 Strategy B: Deep Symbol Resolution`);
|
|
@@ -2430,7 +2435,7 @@ class ASTSourceUpdater {
|
|
|
2430
2435
|
};
|
|
2431
2436
|
}
|
|
2432
2437
|
// ─────────────────────────────────────────────────────────────
|
|
2433
|
-
// STRATEGY C
|
|
2438
|
+
// STRATEGY C - Inline Semantic Fix
|
|
2434
2439
|
// ─────────────────────────────────────────────────────────────
|
|
2435
2440
|
strategyInlineSemanticFix(sourceFile, caller, oldSelector, newSelector, oldSem, newSem) {
|
|
2436
2441
|
console.log(`[ASTUpdater] 🎯 Strategy C: Inline Semantic Fix`);
|
|
@@ -2488,7 +2493,7 @@ class ASTSourceUpdater {
|
|
|
2488
2493
|
};
|
|
2489
2494
|
}
|
|
2490
2495
|
// ─────────────────────────────────────────────────────────────
|
|
2491
|
-
// STRATEGY D
|
|
2496
|
+
// STRATEGY D - Template Literal
|
|
2492
2497
|
// ─────────────────────────────────────────────────────────────
|
|
2493
2498
|
strategyTemplateLiteral(sourceFile, caller, oldSelector, newSelector) {
|
|
2494
2499
|
console.log(`[ASTUpdater] 🔤 Strategy D: Template Literal`);
|
|
@@ -2545,7 +2550,7 @@ class ASTSourceUpdater {
|
|
|
2545
2550
|
};
|
|
2546
2551
|
}
|
|
2547
2552
|
// ─────────────────────────────────────────────────────────────
|
|
2548
|
-
// STRATEGY E
|
|
2553
|
+
// STRATEGY E - Semantic Fragment Scan
|
|
2549
2554
|
// ─────────────────────────────────────────────────────────────
|
|
2550
2555
|
strategySemanticFragmentScan(sourceFile, caller, oldSelector, newSelector, oldSem, newSem) {
|
|
2551
2556
|
console.log(`[ASTUpdater] 🔎 Strategy E: Semantic Fragment Scan`);
|
|
@@ -2666,7 +2671,7 @@ class ASTSourceUpdater {
|
|
|
2666
2671
|
/**
|
|
2667
2672
|
* Guard: returns false when replacing oldNode with proposedNewText would
|
|
2668
2673
|
* substitute a hardcoded string literal for an Identifier or
|
|
2669
|
-
* PropertyAccessExpression
|
|
2674
|
+
* PropertyAccessExpression - which is the definition-site mutation bug.
|
|
2670
2675
|
* All other replacements are allowed.
|
|
2671
2676
|
*/
|
|
2672
2677
|
static isSafeReplacement(oldNode, proposedNewText) {
|
|
@@ -2690,7 +2695,7 @@ class ASTSourceUpdater {
|
|
|
2690
2695
|
firstArg.replaceWithText(`"${safe}"`);
|
|
2691
2696
|
}
|
|
2692
2697
|
else {
|
|
2693
|
-
console.warn(`[ASTUpdater] ⚠️ replaceCallArgument: blocked unsafe replacement
|
|
2698
|
+
console.warn(`[ASTUpdater] ⚠️ replaceCallArgument: blocked unsafe replacement - ` +
|
|
2694
2699
|
`arg is ${firstArg.getKindName()}, not a string literal. ` +
|
|
2695
2700
|
`Heal at definition site instead.`);
|
|
2696
2701
|
return false;
|
|
@@ -2992,10 +2997,10 @@ class ASTSourceUpdater {
|
|
|
2992
2997
|
shape === "PROPERTY_ACCESS" ||
|
|
2993
2998
|
shape === "TEMPLATE_LITERAL") {
|
|
2994
2999
|
console.log(`[SemanticDispatch] 🛑 hard-block: "${node.getText()}" is ${shape} ` +
|
|
2995
|
-
`but trace failed
|
|
3000
|
+
`but trace failed - preserving call site`);
|
|
2996
3001
|
return {
|
|
2997
3002
|
success: false,
|
|
2998
|
-
reason: `semantic guard: could not trace "${node.getText()}"
|
|
3003
|
+
reason: `semantic guard: could not trace "${node.getText()}" - call site preserved`,
|
|
2999
3004
|
strategy: "semantic-guard",
|
|
3000
3005
|
};
|
|
3001
3006
|
}
|
|
@@ -3018,21 +3023,21 @@ class ASTSourceUpdater {
|
|
|
3018
3023
|
if (blast.risk === "high") {
|
|
3019
3024
|
this.advisoryBuffer.push({
|
|
3020
3025
|
code: HealingAdvisory_1.AdvisoryCode.BLAST_RADIUS_BLOCKED,
|
|
3021
|
-
message: `Mutation blocked
|
|
3026
|
+
message: `Mutation blocked - ${blast.testFunctionCount} tests reference this constant.`,
|
|
3022
3027
|
affectedFile: targetFile.getFilePath(),
|
|
3023
3028
|
affectedLine: targetNode.getStartLineNumber() - 1,
|
|
3024
3029
|
suggestedAction: "Review all referencing tests before changing this constant.",
|
|
3025
3030
|
});
|
|
3026
3031
|
return {
|
|
3027
3032
|
success: false,
|
|
3028
|
-
reason: `BlastRadius BLOCKED: ${blast.testFunctionCount} tests reference this constant
|
|
3033
|
+
reason: `BlastRadius BLOCKED: ${blast.testFunctionCount} tests reference this constant - manual review required`,
|
|
3029
3034
|
strategy: "semantic-identifier",
|
|
3030
3035
|
};
|
|
3031
3036
|
}
|
|
3032
3037
|
if (blast.risk === "medium") {
|
|
3033
3038
|
this.advisoryBuffer.push({
|
|
3034
3039
|
code: HealingAdvisory_1.AdvisoryCode.BLAST_RADIUS_WARNING,
|
|
3035
|
-
message: `Mutated constant used in ${blast.testFunctionCount} tests
|
|
3040
|
+
message: `Mutated constant used in ${blast.testFunctionCount} tests - verify no regressions.`,
|
|
3036
3041
|
affectedFile: targetFile.getFilePath(),
|
|
3037
3042
|
affectedLine: targetNode.getStartLineNumber() - 1,
|
|
3038
3043
|
suggestedAction: "Run the full test suite to confirm all references still pass.",
|
|
@@ -3044,7 +3049,7 @@ class ASTSourceUpdater {
|
|
|
3044
3049
|
if (!this.validateFileSyntax(targetFile)) {
|
|
3045
3050
|
return {
|
|
3046
3051
|
success: false,
|
|
3047
|
-
reason: "post-mutation syntax validation failed
|
|
3052
|
+
reason: "post-mutation syntax validation failed - reverted",
|
|
3048
3053
|
strategy: "semantic-identifier",
|
|
3049
3054
|
};
|
|
3050
3055
|
}
|
|
@@ -3054,7 +3059,10 @@ class ASTSourceUpdater {
|
|
|
3054
3059
|
strategy: "semantic-identifier",
|
|
3055
3060
|
lineUpdated: result.lineUpdated,
|
|
3056
3061
|
healedLocatorString: result.healedLocatorString,
|
|
3057
|
-
definitionSite: {
|
|
3062
|
+
definitionSite: {
|
|
3063
|
+
file: targetFile.getFilePath(),
|
|
3064
|
+
line: result.lineUpdated,
|
|
3065
|
+
},
|
|
3058
3066
|
blastRadius: blast.testFunctionCount,
|
|
3059
3067
|
};
|
|
3060
3068
|
}
|
|
@@ -3083,21 +3091,21 @@ class ASTSourceUpdater {
|
|
|
3083
3091
|
if (blast.risk === "high") {
|
|
3084
3092
|
this.advisoryBuffer.push({
|
|
3085
3093
|
code: HealingAdvisory_1.AdvisoryCode.BLAST_RADIUS_BLOCKED,
|
|
3086
|
-
message: `Mutation blocked
|
|
3094
|
+
message: `Mutation blocked - ${blast.testFunctionCount} tests reference this property.`,
|
|
3087
3095
|
affectedFile: targetFile.getFilePath(),
|
|
3088
3096
|
affectedLine: targetNode.getStartLineNumber() - 1,
|
|
3089
3097
|
suggestedAction: "Review all referencing tests before changing this property.",
|
|
3090
3098
|
});
|
|
3091
3099
|
return {
|
|
3092
3100
|
success: false,
|
|
3093
|
-
reason: `BlastRadius BLOCKED: ${blast.testFunctionCount} tests reference this property
|
|
3101
|
+
reason: `BlastRadius BLOCKED: ${blast.testFunctionCount} tests reference this property - manual review required`,
|
|
3094
3102
|
strategy: "semantic-property-access",
|
|
3095
3103
|
};
|
|
3096
3104
|
}
|
|
3097
3105
|
if (blast.risk === "medium") {
|
|
3098
3106
|
this.advisoryBuffer.push({
|
|
3099
3107
|
code: HealingAdvisory_1.AdvisoryCode.BLAST_RADIUS_WARNING,
|
|
3100
|
-
message: `Mutated property used in ${blast.testFunctionCount} tests
|
|
3108
|
+
message: `Mutated property used in ${blast.testFunctionCount} tests - verify no regressions.`,
|
|
3101
3109
|
affectedFile: targetFile.getFilePath(),
|
|
3102
3110
|
affectedLine: targetNode.getStartLineNumber() - 1,
|
|
3103
3111
|
suggestedAction: "Run the full test suite to confirm all references still pass.",
|
|
@@ -3109,7 +3117,7 @@ class ASTSourceUpdater {
|
|
|
3109
3117
|
if (!this.validateFileSyntax(targetFile)) {
|
|
3110
3118
|
return {
|
|
3111
3119
|
success: false,
|
|
3112
|
-
reason: "post-mutation syntax validation failed
|
|
3120
|
+
reason: "post-mutation syntax validation failed - reverted",
|
|
3113
3121
|
strategy: "semantic-property-access",
|
|
3114
3122
|
};
|
|
3115
3123
|
}
|
|
@@ -3119,7 +3127,10 @@ class ASTSourceUpdater {
|
|
|
3119
3127
|
strategy: "semantic-property-access",
|
|
3120
3128
|
lineUpdated: result.lineUpdated,
|
|
3121
3129
|
healedLocatorString: result.healedLocatorString,
|
|
3122
|
-
definitionSite: {
|
|
3130
|
+
definitionSite: {
|
|
3131
|
+
file: targetFile.getFilePath(),
|
|
3132
|
+
line: result.lineUpdated,
|
|
3133
|
+
},
|
|
3123
3134
|
blastRadius: blast.testFunctionCount,
|
|
3124
3135
|
};
|
|
3125
3136
|
}
|
|
@@ -3163,10 +3174,11 @@ class ASTSourceUpdater {
|
|
|
3163
3174
|
}
|
|
3164
3175
|
}
|
|
3165
3176
|
}
|
|
3166
|
-
if (patchResult.reason.includes("structural") ||
|
|
3177
|
+
if (patchResult.reason.includes("structural") ||
|
|
3178
|
+
patchResult.reason.includes("not found")) {
|
|
3167
3179
|
this.advisoryBuffer.push({
|
|
3168
3180
|
code: HealingAdvisory_1.AdvisoryCode.MANUAL_REVIEW_TEMPLATE_COMPLEX,
|
|
3169
|
-
message: `Template literal requires structural change
|
|
3181
|
+
message: `Template literal requires structural change - automated patch skipped.`,
|
|
3170
3182
|
affectedFile: sourceFile.getFilePath(),
|
|
3171
3183
|
affectedLine: node.getStartLineNumber() - 1,
|
|
3172
3184
|
suggestedAction: "Manually update the template literal and its callers.",
|
|
@@ -3180,7 +3192,7 @@ class ASTSourceUpdater {
|
|
|
3180
3192
|
if (!this.validateFileSyntax(sourceFile)) {
|
|
3181
3193
|
return {
|
|
3182
3194
|
success: false,
|
|
3183
|
-
reason: "post-mutation syntax validation failed
|
|
3195
|
+
reason: "post-mutation syntax validation failed - reverted",
|
|
3184
3196
|
strategy: "semantic-template",
|
|
3185
3197
|
};
|
|
3186
3198
|
}
|
|
@@ -3208,7 +3220,8 @@ class ASTSourceUpdater {
|
|
|
3208
3220
|
if (ts_morph_1.Node.isCallExpression(init)) {
|
|
3209
3221
|
return this.handleCallExpressionArg(init, sourceFile, oldSpanVal, newSpanVal);
|
|
3210
3222
|
}
|
|
3211
|
-
if (ts_morph_1.Node.isStringLiteral(init) ||
|
|
3223
|
+
if (ts_morph_1.Node.isStringLiteral(init) ||
|
|
3224
|
+
ts_morph_1.Node.isNoSubstitutionTemplateLiteral(init)) {
|
|
3212
3225
|
const result = InitializerUpdater_1.InitializerUpdater.apply(init, sourceFile, newSpanVal);
|
|
3213
3226
|
if (!result)
|
|
3214
3227
|
return null;
|
|
@@ -3219,7 +3232,10 @@ class ASTSourceUpdater {
|
|
|
3219
3232
|
strategy: "semantic-template",
|
|
3220
3233
|
lineUpdated: result.lineUpdated,
|
|
3221
3234
|
healedLocatorString: result.healedLocatorString,
|
|
3222
|
-
definitionSite: {
|
|
3235
|
+
definitionSite: {
|
|
3236
|
+
file: sourceFile.getFilePath(),
|
|
3237
|
+
line: result.lineUpdated,
|
|
3238
|
+
},
|
|
3223
3239
|
};
|
|
3224
3240
|
}
|
|
3225
3241
|
if (ts_morph_1.Node.isTemplateExpression(init)) {
|
|
@@ -3233,7 +3249,7 @@ class ASTSourceUpdater {
|
|
|
3233
3249
|
if (fnAnalysis.kind === "complex") {
|
|
3234
3250
|
this.advisoryBuffer.push({
|
|
3235
3251
|
code: HealingAdvisory_1.AdvisoryCode.MANUAL_REVIEW_CONDITIONAL_RETURN,
|
|
3236
|
-
message: `Function has conditional/complex return
|
|
3252
|
+
message: `Function has conditional/complex return - automated patch skipped. Reason: ${fnAnalysis.reason}`,
|
|
3237
3253
|
affectedFile: sourceFile.getFilePath(),
|
|
3238
3254
|
suggestedAction: "Manually update the function's return expression.",
|
|
3239
3255
|
});
|
|
@@ -3246,7 +3262,7 @@ class ASTSourceUpdater {
|
|
|
3246
3262
|
if (!this.validateFileSyntax(fnAnalysis.functionFile)) {
|
|
3247
3263
|
return {
|
|
3248
3264
|
success: false,
|
|
3249
|
-
reason: "post-mutation syntax validation failed
|
|
3265
|
+
reason: "post-mutation syntax validation failed - reverted",
|
|
3250
3266
|
strategy: "semantic-call-expression",
|
|
3251
3267
|
};
|
|
3252
3268
|
}
|
|
@@ -3256,7 +3272,10 @@ class ASTSourceUpdater {
|
|
|
3256
3272
|
strategy: "semantic-call-expression",
|
|
3257
3273
|
lineUpdated: result.lineUpdated,
|
|
3258
3274
|
healedLocatorString: result.healedLocatorString,
|
|
3259
|
-
definitionSite: {
|
|
3275
|
+
definitionSite: {
|
|
3276
|
+
file: fnAnalysis.functionFile.getFilePath(),
|
|
3277
|
+
line: result.lineUpdated,
|
|
3278
|
+
},
|
|
3260
3279
|
};
|
|
3261
3280
|
}
|
|
3262
3281
|
if (fnAnalysis.kind === "template") {
|
|
@@ -3266,7 +3285,7 @@ class ASTSourceUpdater {
|
|
|
3266
3285
|
if (patchResult.reason.includes("structural")) {
|
|
3267
3286
|
this.advisoryBuffer.push({
|
|
3268
3287
|
code: HealingAdvisory_1.AdvisoryCode.MANUAL_REVIEW_TEMPLATE_COMPLEX,
|
|
3269
|
-
message: `Function template return requires structural change
|
|
3288
|
+
message: `Function template return requires structural change - automated patch skipped.`,
|
|
3270
3289
|
affectedFile: fnAnalysis.functionFile.getFilePath(),
|
|
3271
3290
|
affectedLine: fnAnalysis.functionLine - 1,
|
|
3272
3291
|
suggestedAction: "Manually update the function's template literal.",
|
|
@@ -3280,7 +3299,7 @@ class ASTSourceUpdater {
|
|
|
3280
3299
|
if (!this.validateFileSyntax(fnAnalysis.functionFile)) {
|
|
3281
3300
|
return {
|
|
3282
3301
|
success: false,
|
|
3283
|
-
reason: "post-mutation syntax validation failed
|
|
3302
|
+
reason: "post-mutation syntax validation failed - reverted",
|
|
3284
3303
|
strategy: "semantic-call-expression",
|
|
3285
3304
|
};
|
|
3286
3305
|
}
|
|
@@ -3290,7 +3309,10 @@ class ASTSourceUpdater {
|
|
|
3290
3309
|
strategy: "semantic-call-expression",
|
|
3291
3310
|
lineUpdated,
|
|
3292
3311
|
healedLocatorString: newSelector,
|
|
3293
|
-
definitionSite: {
|
|
3312
|
+
definitionSite: {
|
|
3313
|
+
file: fnAnalysis.functionFile.getFilePath(),
|
|
3314
|
+
line: lineUpdated,
|
|
3315
|
+
},
|
|
3294
3316
|
};
|
|
3295
3317
|
}
|
|
3296
3318
|
return null;
|
|
@@ -3306,11 +3328,13 @@ class ASTSourceUpdater {
|
|
|
3306
3328
|
return !ALLOWED_CODES.has(d.getCode());
|
|
3307
3329
|
});
|
|
3308
3330
|
if (syntaxErrors.length > 0) {
|
|
3309
|
-
const msgs = syntaxErrors
|
|
3310
|
-
|
|
3331
|
+
const msgs = syntaxErrors
|
|
3332
|
+
.map((d) => String(d.getMessageText()))
|
|
3333
|
+
.join("; ");
|
|
3334
|
+
console.warn(`[SemanticDispatch] ⚠️ syntax errors after mutation: ${msgs} - reverting`);
|
|
3311
3335
|
this.advisoryBuffer.push({
|
|
3312
3336
|
code: HealingAdvisory_1.AdvisoryCode.POST_MUTATION_TYPE_ERROR,
|
|
3313
|
-
message: `Post-mutation type errors detected
|
|
3337
|
+
message: `Post-mutation type errors detected - mutation reverted. Errors: ${msgs}`,
|
|
3314
3338
|
affectedFile: sourceFile.getFilePath(),
|
|
3315
3339
|
suggestedAction: "Manually inspect and fix the type errors introduced by this heal.",
|
|
3316
3340
|
});
|
|
@@ -20,7 +20,7 @@ exports.ALL_LOCATOR_METHODS = new Set([
|
|
|
20
20
|
// ArgumentTypeAnalyzer
|
|
21
21
|
//
|
|
22
22
|
// Classifies the first argument of a Playwright locator CallExpression.
|
|
23
|
-
// Stateless
|
|
23
|
+
// Stateless - no file I/O, no side effects.
|
|
24
24
|
// ═══════════════════════════════════════════════════════════════════
|
|
25
25
|
class ArgumentTypeAnalyzer {
|
|
26
26
|
/**
|
|
@@ -64,28 +64,28 @@ class ArgumentTypeAnalyzer {
|
|
|
64
64
|
// Private helpers
|
|
65
65
|
// ─────────────────────────────────────────────────────────────────
|
|
66
66
|
static classifyNode(node) {
|
|
67
|
-
// Plain string
|
|
67
|
+
// Plain string - value is self-contained in this node.
|
|
68
68
|
if (ts_morph_1.Node.isStringLiteral(node) ||
|
|
69
69
|
ts_morph_1.Node.isNoSubstitutionTemplateLiteral(node)) {
|
|
70
70
|
return "STRING_LITERAL";
|
|
71
71
|
}
|
|
72
|
-
// Template with ${...} spans
|
|
72
|
+
// Template with ${...} spans - static segments are healable, spans are not.
|
|
73
73
|
if (ts_morph_1.Node.isTemplateExpression(node)) {
|
|
74
74
|
return "TEMPLATE_LITERAL";
|
|
75
75
|
}
|
|
76
|
-
// Bare variable name
|
|
76
|
+
// Bare variable name - must trace to declaration.
|
|
77
77
|
if (ts_morph_1.Node.isIdentifier(node)) {
|
|
78
78
|
return "IDENTIFIER";
|
|
79
79
|
}
|
|
80
|
-
// OBJECT.property
|
|
80
|
+
// OBJECT.property - must trace to object literal property value.
|
|
81
81
|
if (ts_morph_1.Node.isPropertyAccessExpression(node)) {
|
|
82
82
|
return "PROPERTY_ACCESS";
|
|
83
83
|
}
|
|
84
|
-
// fn(...)
|
|
84
|
+
// fn(...) - must enter function body to find return value.
|
|
85
85
|
if (ts_morph_1.Node.isCallExpression(node)) {
|
|
86
86
|
return "CALL_EXPRESSION";
|
|
87
87
|
}
|
|
88
|
-
// Binary expressions, conditionals, spreads, etc.
|
|
88
|
+
// Binary expressions, conditionals, spreads, etc. - not healable here.
|
|
89
89
|
return "COMPLEX";
|
|
90
90
|
}
|
|
91
91
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BlastRadiusAnalyzer.d.ts","sourceRoot":"","sources":["../../src/services/BlastRadiusAnalyzer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,OAAO,EAAc,MAAM,UAAU,CAAC;AAMjE,MAAM,MAAM,SAAS,GAAG,KAAK,GAAG,QAAQ,GAAG,MAAM,CAAC;AAElD,MAAM,WAAW,iBAAiB;IAChC,eAAe,EAAE,MAAM,CAAC;IACxB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,IAAI,EAAE,SAAS,CAAC;CACjB;AAWD,qBAAa,mBAAmB;IAC9B,MAAM,CAAC,OAAO,CACZ,eAAe,EAAE,IAAI,EACrB,WAAW,EAAE,UAAU,EACvB,QAAQ,EAAE,OAAO,GAChB,iBAAiB;IAgBpB,OAAO,CAAC,MAAM,CAAC,SAAS;
|
|
1
|
+
{"version":3,"file":"BlastRadiusAnalyzer.d.ts","sourceRoot":"","sources":["../../src/services/BlastRadiusAnalyzer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,OAAO,EAAc,MAAM,UAAU,CAAC;AAMjE,MAAM,MAAM,SAAS,GAAG,KAAK,GAAG,QAAQ,GAAG,MAAM,CAAC;AAElD,MAAM,WAAW,iBAAiB;IAChC,eAAe,EAAE,MAAM,CAAC;IACxB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,IAAI,EAAE,SAAS,CAAC;CACjB;AAWD,qBAAa,mBAAmB;IAC9B,MAAM,CAAC,OAAO,CACZ,eAAe,EAAE,IAAI,EACrB,WAAW,EAAE,UAAU,EACvB,QAAQ,EAAE,OAAO,GAChB,iBAAiB;IAgBpB,OAAO,CAAC,MAAM,CAAC,SAAS;IA2DxB,OAAO,CAAC,MAAM,CAAC,WAAW;IAY1B,OAAO,CAAC,MAAM,CAAC,UAAU;CAiB1B"}
|
|
@@ -8,7 +8,7 @@ const ts_morph_1 = require("ts-morph");
|
|
|
8
8
|
// Before mutating a definition-site node, determine how many tests
|
|
9
9
|
// reference it. Uses ts-morph's findReferences() for cross-file
|
|
10
10
|
// accuracy. On any analysis failure, defaults to risk:'low' and logs
|
|
11
|
-
// a warning
|
|
11
|
+
// a warning - never blocks healing due to analysis error.
|
|
12
12
|
// ═══════════════════════════════════════════════════════════════════
|
|
13
13
|
class BlastRadiusAnalyzer {
|
|
14
14
|
static analyze(declarationNode, _sourceFile, _project) {
|
|
@@ -35,7 +35,10 @@ class BlastRadiusAnalyzer {
|
|
|
35
35
|
risk: "low",
|
|
36
36
|
};
|
|
37
37
|
}
|
|
38
|
-
const refs = nameNode
|
|
38
|
+
const refs = nameNode
|
|
39
|
+
.getProject()
|
|
40
|
+
.getLanguageService()
|
|
41
|
+
.findReferences(nameNode);
|
|
39
42
|
const affectedFilesSet = new Set();
|
|
40
43
|
let totalRefs = 0;
|
|
41
44
|
// Key = filePath:startLine of the enclosing test() / it() call.
|