sela-core 1.0.7 → 1.0.9
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 +27 -17
- 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/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/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 +12 -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 +13 -17
- 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 +117 -51
- package/dist/errors/SelaError.d.ts +8 -1
- package/dist/errors/SelaError.d.ts.map +1 -1
- package/dist/errors/SelaError.js +14 -7
- package/dist/fixtures/expectProxy.d.ts +2 -2
- package/dist/fixtures/expectProxy.js +16 -16
- package/dist/fixtures/index.d.ts +18 -2
- package/dist/fixtures/index.d.ts.map +1 -1
- package/dist/fixtures/index.js +47 -27
- package/dist/fixtures/moduleExpect.js +7 -7
- package/dist/fixtures/proxyTag.d.ts +1 -1
- package/dist/fixtures/proxyTag.js +4 -4
- 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 +48 -3
- package/dist/services/ASTSourceUpdater.d.ts.map +1 -1
- package/dist/services/ASTSourceUpdater.js +283 -105
- 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.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/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 +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 +31 -55
- package/dist/services/HealReportService.d.ts.map +1 -1
- package/dist/services/HealReportService.js +864 -1240
- package/dist/services/HealingAdvisory.d.ts +9 -1
- package/dist/services/HealingAdvisory.d.ts.map +1 -1
- package/dist/services/HealingAdvisory.js +9 -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.d.ts.map +1 -1
- package/dist/services/InitializerUpdater.js +12 -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 +34 -124
- package/dist/services/LLMService.d.ts +1 -1
- package/dist/services/LLMService.js +16 -16
- package/dist/services/MutationApplier.d.ts +55 -0
- package/dist/services/MutationApplier.d.ts.map +1 -0
- package/dist/services/MutationApplier.js +322 -0
- 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 +9 -2
- package/dist/services/PendingPromptLedger.d.ts.map +1 -1
- package/dist/services/PendingPromptLedger.js +31 -6
- package/dist/services/ReportGenerator.d.ts +116 -30
- package/dist/services/ReportGenerator.d.ts.map +1 -1
- package/dist/services/ReportGenerator.js +149 -62
- 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 +4 -7
- package/dist/services/SafetyGuard.d.ts.map +1 -1
- package/dist/services/SafetyGuard.js +29 -106
- 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.js +6 -6
- 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 +44 -26
- package/dist/services/TemplateDiffService.d.ts.map +1 -1
- package/dist/services/TemplateDiffService.js +18 -15
- 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 +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/DOMUtils.d.ts +18 -2
- package/dist/utils/DOMUtils.d.ts.map +1 -1
- package/dist/utils/DOMUtils.js +335 -49
- 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
|
@@ -41,7 +41,8 @@ const ASTSourceUpdater_1 = require("./ASTSourceUpdater");
|
|
|
41
41
|
const DryRunGuard_1 = require("../config/DryRunGuard");
|
|
42
42
|
const WorkspaceSnapshotService_1 = require("./WorkspaceSnapshotService");
|
|
43
43
|
const SelaError_1 = require("../errors/SelaError");
|
|
44
|
-
|
|
44
|
+
const SelectorSanitizer_1 = require("./SelectorSanitizer");
|
|
45
|
+
// Centralised write gate - every disk-mutating `fs.writeFileSync` call in
|
|
45
46
|
// this module routes through `persistFile` so the SELA_DRY_RUN bypass is
|
|
46
47
|
// enforced in one place, and the Clean-Room WorkspaceSnapshotService
|
|
47
48
|
// records contentBefore/contentAfter for isolated in-memory diffing.
|
|
@@ -310,19 +311,19 @@ function updateTemplateLiteralPreservingVars(line, oldSelector, newSelector, dom
|
|
|
310
311
|
// GIT HELPER (used by branch strategy and autoCommit)
|
|
311
312
|
//
|
|
312
313
|
// Design goals:
|
|
313
|
-
// • Lean
|
|
314
|
-
// • Commit-based
|
|
314
|
+
// • Lean - no stash, no branch-switching, no complex state management.
|
|
315
|
+
// • Commit-based - write first (inPlace), then commit. If git fails,
|
|
315
316
|
// the fix is still on disk (silent inPlace fallback).
|
|
316
|
-
// • Detached HEAD aware
|
|
317
|
+
// • Detached HEAD aware - skips silently in CI environments that check
|
|
317
318
|
// out a specific SHA without a branch ref.
|
|
318
319
|
// ─────────────────────────────────────────────────────────────────
|
|
319
320
|
function runGitOperations(absoluteFilePath, createBranch, relativeFilePath) {
|
|
320
321
|
const filename = path.basename(relativeFilePath);
|
|
321
|
-
// Dry-run gate
|
|
322
|
+
// Dry-run gate - suppress branch creation, `git add`, and auto-commit.
|
|
322
323
|
// Mirrors the persistFile() skip so a dry run is fully observable in CI
|
|
323
324
|
// without ever producing a commit.
|
|
324
325
|
if (DryRunGuard_1.DryRunGuard.active()) {
|
|
325
|
-
console.log(`[SourceUpdater] 🌵 Dry Run
|
|
326
|
+
console.log(`[SourceUpdater] 🌵 Dry Run - skip git operations for ${filename}`);
|
|
326
327
|
return;
|
|
327
328
|
}
|
|
328
329
|
try {
|
|
@@ -331,7 +332,7 @@ function runGitOperations(absoluteFilePath, createBranch, relativeFilePath) {
|
|
|
331
332
|
(0, child_process_1.execSync)("git symbolic-ref HEAD", { stdio: "pipe" });
|
|
332
333
|
}
|
|
333
334
|
catch {
|
|
334
|
-
console.warn(`[SourceUpdater] ⚠️ Detached HEAD
|
|
335
|
+
console.warn(`[SourceUpdater] ⚠️ Detached HEAD - git operations skipped (fix is saved to disk)`);
|
|
335
336
|
return;
|
|
336
337
|
}
|
|
337
338
|
if (createBranch) {
|
|
@@ -346,7 +347,7 @@ function runGitOperations(absoluteFilePath, createBranch, relativeFilePath) {
|
|
|
346
347
|
console.log(`[SourceUpdater] ✅ Auto-committed fix in: ${filename}`);
|
|
347
348
|
}
|
|
348
349
|
catch (err) {
|
|
349
|
-
console.warn(`[SourceUpdater] ⚠️ Git operation failed: ${err.message}
|
|
350
|
+
console.warn(`[SourceUpdater] ⚠️ Git operation failed: ${err.message} - fix written to disk (inPlace fallback)`);
|
|
350
351
|
}
|
|
351
352
|
}
|
|
352
353
|
class SourceUpdater {
|
|
@@ -376,7 +377,7 @@ class SourceUpdater {
|
|
|
376
377
|
return result;
|
|
377
378
|
}
|
|
378
379
|
// ─────────────────────────────────────────────────────────────
|
|
379
|
-
// commentOnly helper
|
|
380
|
+
// commentOnly helper - inserts a TODO comment above the failing
|
|
380
381
|
// call site and returns success:false so the engine knows the
|
|
381
382
|
// source was NOT rewritten (no retry suppression needed).
|
|
382
383
|
// ─────────────────────────────────────────────────────────────
|
|
@@ -402,12 +403,12 @@ class SourceUpdater {
|
|
|
402
403
|
catch (err) {
|
|
403
404
|
return {
|
|
404
405
|
success: false,
|
|
405
|
-
reason: `COMMENT_ONLY: write failed
|
|
406
|
+
reason: `COMMENT_ONLY: write failed - ${err.message}`,
|
|
406
407
|
};
|
|
407
408
|
}
|
|
408
409
|
}
|
|
409
410
|
// ─────────────────────────────────────────────────────────────
|
|
410
|
-
// CORE UPDATE
|
|
411
|
+
// CORE UPDATE - Layered Healing (AST primary, Regex fallback)
|
|
411
412
|
// Called by update(); never call directly from outside the class.
|
|
412
413
|
// ─────────────────────────────────────────────────────────────
|
|
413
414
|
static _coreUpdate(caller, oldSelector, newSelector, domContext, dnaAttrHint, fullSelectorContext, aiSegments, contentChange, smartChainSegments, originalChainHint) {
|
|
@@ -442,7 +443,7 @@ class SourceUpdater {
|
|
|
442
443
|
if (astResult.success) {
|
|
443
444
|
console.log(`[SourceUpdater] ✅ AST Strategy ${astResult.strategy} SUCCESS` +
|
|
444
445
|
(astResult.lineUpdated !== undefined
|
|
445
|
-
? `
|
|
446
|
+
? ` - line ${astResult.lineUpdated + 1}`
|
|
446
447
|
: ""));
|
|
447
448
|
return {
|
|
448
449
|
success: true,
|
|
@@ -458,28 +459,45 @@ class SourceUpdater {
|
|
|
458
459
|
// PROPERTY_ACCESS / TEMPLATE_LITERAL) but could not safely resolve the new
|
|
459
460
|
// value. Regex cannot do better and will corrupt the definition site.
|
|
460
461
|
if (astResult.strategy === "semantic-guard") {
|
|
461
|
-
console.warn(`[SourceUpdater] 🛑 Regex blocked
|
|
462
|
+
console.warn(`[SourceUpdater] 🛑 Regex blocked - semantic guard fired. ` +
|
|
462
463
|
`Source unchanged. Reason: ${astResult.reason}`);
|
|
463
464
|
return { success: false, reason: astResult.reason };
|
|
464
465
|
}
|
|
465
466
|
console.log(`[SourceUpdater] ⚠️ AST engine exhausted (${astResult.reason}), falling back to Regex`);
|
|
466
467
|
}
|
|
467
468
|
catch (astError) {
|
|
468
|
-
// Developer pinned the failing statement with `// sela-fail-fast`
|
|
469
|
+
// Developer pinned the failing statement with `// sela-fail-fast` -
|
|
469
470
|
// propagate the directive instead of swallowing into the Regex
|
|
470
471
|
// fallback chain (which would defeat the whole point of the gate).
|
|
471
472
|
if ((0, SelaError_1.isSelaErrorFrom)(astError, "ASTUpdater") &&
|
|
472
|
-
astError.code ===
|
|
473
|
+
astError.code ===
|
|
474
|
+
SelaError_1.AST_UPDATER_CODES.HEAL_DISABLED_BY_DIRECTIVE) {
|
|
473
475
|
throw astError;
|
|
474
476
|
}
|
|
475
477
|
console.warn(`[SourceUpdater] ⚠️ AST engine error: ${astError.message}. Falling back to Regex`);
|
|
476
478
|
}
|
|
479
|
+
// ── ABSOLUTE GUARD: internal engine selector ───────────────────
|
|
480
|
+
// The AST engine declined to mutate (could not method-shift). The regex
|
|
481
|
+
// fallback only does RAW string substitution of `oldSelector → newSelector`,
|
|
482
|
+
// which would stamp the private `internal:role=…` engine string into the
|
|
483
|
+
// developer's source (the bug this guard kills). A `.locator()` string
|
|
484
|
+
// cannot express a semantic locator, so there is no safe regex outcome:
|
|
485
|
+
// refuse, leave source untouched. The in-memory proxy swap already keeps
|
|
486
|
+
// this run green; the correct method-shift is the new composeHeal pipeline.
|
|
487
|
+
if ((0, SelectorSanitizer_1.isInternalEngineSelector)(effectiveNewSelector)) {
|
|
488
|
+
console.warn(`[SourceUpdater] 🛑 Regex blocked - internal engine selector cannot be ` +
|
|
489
|
+
`string-substituted into source. Method-shift required. Source unchanged.`);
|
|
490
|
+
return {
|
|
491
|
+
success: false,
|
|
492
|
+
reason: "INTERNAL_SELECTOR_GUARD: refusing regex string-substitution of an internal engine selector (method-shift required)",
|
|
493
|
+
};
|
|
494
|
+
}
|
|
477
495
|
// ── 2. Regex Fallback ──────────────────────────────────────────
|
|
478
496
|
console.log(`[SourceUpdater] 🔄 Using Regex fallback strategies...`);
|
|
479
497
|
const raw = fs.readFileSync(filePath, "utf8");
|
|
480
498
|
const lines = raw.split(/\r?\n/);
|
|
481
499
|
if (caller.line <= 0) {
|
|
482
|
-
console.log(`[SourceUpdater] ⚠️ line ${caller.line} invalid
|
|
500
|
+
console.log(`[SourceUpdater] ⚠️ line ${caller.line} invalid - running Regex Strategy G only`);
|
|
483
501
|
if (contentChange) {
|
|
484
502
|
const assertResult = SourceUpdater.strategyAssertionHealing(lines, 1, contentChange, true);
|
|
485
503
|
if (assertResult.success) {
|
|
@@ -511,7 +529,7 @@ class SourceUpdater {
|
|
|
511
529
|
if (layeredResult.success) {
|
|
512
530
|
tryAssertionHealing();
|
|
513
531
|
persistFile(filePath, lines.join("\n"));
|
|
514
|
-
console.log(`[SourceUpdater] ✅ Strategy 0 (Layered Healing) SUCCESS
|
|
532
|
+
console.log(`[SourceUpdater] ✅ Strategy 0 (Layered Healing) SUCCESS - line ${layeredResult.lineUpdated + 1}`);
|
|
515
533
|
return layeredResult;
|
|
516
534
|
}
|
|
517
535
|
}
|
|
@@ -587,7 +605,7 @@ class SourceUpdater {
|
|
|
587
605
|
};
|
|
588
606
|
}
|
|
589
607
|
// ─────────────────────────────────────────────────────────────
|
|
590
|
-
// STRATEGY F (Regex)
|
|
608
|
+
// STRATEGY F (Regex) - Full Chain Collapsing
|
|
591
609
|
// ─────────────────────────────────────────────────────────────
|
|
592
610
|
static strategyChainCollapse_Regex(lines, callerLine, oldSelector, newSelector) {
|
|
593
611
|
console.log(`[Strategy-F] ⛓️ Starting Regex Chain Collapse...`);
|
|
@@ -638,7 +656,7 @@ class SourceUpdater {
|
|
|
638
656
|
return { success: false, reason: "chain detected but could not replace" };
|
|
639
657
|
}
|
|
640
658
|
// ─────────────────────────────────────────────────────────────
|
|
641
|
-
// STRATEGY G (Regex)
|
|
659
|
+
// STRATEGY G (Regex) - Assertion Healing
|
|
642
660
|
// ─────────────────────────────────────────────────────────────
|
|
643
661
|
static strategyAssertionHealing(lines, callerLine, contentChange, wholeFile = false) {
|
|
644
662
|
const { oldText, newText } = contentChange;
|
|
@@ -693,7 +711,7 @@ class SourceUpdater {
|
|
|
693
711
|
return { success: false, reason: "no assertions to heal" };
|
|
694
712
|
}
|
|
695
713
|
// ─────────────────────────────────────────────────────────────
|
|
696
|
-
// STRATEGY 0
|
|
714
|
+
// STRATEGY 0 - Layered Healing (Upstream Variable Tracing)
|
|
697
715
|
// ─────────────────────────────────────────────────────────────
|
|
698
716
|
static strategyLayeredHealing(lines, callerLine, oldElementSelector, aiSegments, fullSelectorContext) {
|
|
699
717
|
console.log(`[SourceUpdater] 🧠 Strategy 0: Starting Layered Healing...`);
|
|
@@ -746,7 +764,7 @@ class SourceUpdater {
|
|
|
746
764
|
return null;
|
|
747
765
|
}
|
|
748
766
|
// ─────────────────────────────────────────────────────────────
|
|
749
|
-
// STRATEGY 1
|
|
767
|
+
// STRATEGY 1 - Chain Collapse (leaf-only precision replacement)
|
|
750
768
|
// ─────────────────────────────────────────────────────────────
|
|
751
769
|
static strategyChainCollapse(lines, callerLine, oldSelector, newSelector) {
|
|
752
770
|
const oldLeaf = oldSelector
|
|
@@ -788,7 +806,7 @@ class SourceUpdater {
|
|
|
788
806
|
};
|
|
789
807
|
}
|
|
790
808
|
// ─────────────────────────────────────────────────────────────
|
|
791
|
-
// STRATEGY 2
|
|
809
|
+
// STRATEGY 2 - Direct Literal
|
|
792
810
|
// ─────────────────────────────────────────────────────────────
|
|
793
811
|
static strategyDirectLiteral(lines, callerLine, oldSelector, newSelector) {
|
|
794
812
|
const windowSize = 10;
|
|
@@ -821,7 +839,7 @@ class SourceUpdater {
|
|
|
821
839
|
return { success: false, reason: "no direct literal" };
|
|
822
840
|
}
|
|
823
841
|
// ─────────────────────────────────────────────────────────────
|
|
824
|
-
// STRATEGY 3
|
|
842
|
+
// STRATEGY 3 - Upstream Variable/Const
|
|
825
843
|
// ─────────────────────────────────────────────────────────────
|
|
826
844
|
static strategyUpstreamVariable(lines, callerLine, oldSelector, newSelector) {
|
|
827
845
|
const callBlock = lines
|
|
@@ -858,7 +876,7 @@ class SourceUpdater {
|
|
|
858
876
|
return { success: false, reason: "no upstream variable" };
|
|
859
877
|
}
|
|
860
878
|
// ─────────────────────────────────────────────────────────────
|
|
861
|
-
// STRATEGY 4
|
|
879
|
+
// STRATEGY 4 - Chained Locator Dedup
|
|
862
880
|
// ─────────────────────────────────────────────────────────────
|
|
863
881
|
static strategyChainedLocator(lines, callerLine, oldSelector, newSelector) {
|
|
864
882
|
const blockStart = Math.max(0, callerLine - 10);
|
|
@@ -885,7 +903,7 @@ class SourceUpdater {
|
|
|
885
903
|
return { success: false, reason: "dedup found but literal not replaced" };
|
|
886
904
|
}
|
|
887
905
|
// ─────────────────────────────────────────────────────────────
|
|
888
|
-
// STRATEGY 5
|
|
906
|
+
// STRATEGY 5 - Function/Template with Variable Preservation
|
|
889
907
|
// ─────────────────────────────────────────────────────────────
|
|
890
908
|
static strategyFunctionReturn(lines, callerLine, oldSelector, newSelector, domContext, dnaAttrHint) {
|
|
891
909
|
const callBlock = lines
|
|
@@ -970,7 +988,7 @@ class SourceUpdater {
|
|
|
970
988
|
return { success: false, reason: "function strategy exhausted" };
|
|
971
989
|
}
|
|
972
990
|
// ─────────────────────────────────────────────────────────────
|
|
973
|
-
// STRATEGY 6
|
|
991
|
+
// STRATEGY 6 - Global Scan
|
|
974
992
|
// ─────────────────────────────────────────────────────────────
|
|
975
993
|
static strategyGlobalScan(lines, oldSelector, newSelector) {
|
|
976
994
|
const lastOldPart = oldSelector.split(">>").pop().trim();
|
|
@@ -1 +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;
|
|
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"}
|
|
@@ -5,13 +5,13 @@ exports.escapeForTemplate = escapeForTemplate;
|
|
|
5
5
|
// ═══════════════════════════════════════════════════════════════════
|
|
6
6
|
// TemplateDiffService
|
|
7
7
|
//
|
|
8
|
-
// T-8: String-based API
|
|
8
|
+
// T-8: String-based API - diffAndPatch()
|
|
9
9
|
// Given a template source like `input[data-role="${role}_legacy"]`,
|
|
10
10
|
// its resolved old string, and an AI-proposed new string, compute
|
|
11
11
|
// the patched template that preserves ${...} spans while updating
|
|
12
12
|
// only the changed static segments.
|
|
13
13
|
//
|
|
14
|
-
// T-8.1: Node-based API
|
|
14
|
+
// T-8.1: Node-based API - computeNodePatch()
|
|
15
15
|
// Wraps diffAndPatch() using the TemplateExpression AST node as the
|
|
16
16
|
// source of truth (avoids text-parsing ambiguity). Returns the full
|
|
17
17
|
// patched template text for use with templateNode.replaceWithText().
|
|
@@ -34,7 +34,7 @@ class TemplateDiffService {
|
|
|
34
34
|
const exprSegs = segments
|
|
35
35
|
.filter((s) => s.type === "expr")
|
|
36
36
|
.map((s) => s.text);
|
|
37
|
-
// No spans
|
|
37
|
+
// No spans - the "template" is just a plain string.
|
|
38
38
|
if (exprSegs.length === 0) {
|
|
39
39
|
if (resolvedOld !== resolvedNew) {
|
|
40
40
|
return {
|
|
@@ -43,18 +43,21 @@ class TemplateDiffService {
|
|
|
43
43
|
changedSegmentIndices: [0],
|
|
44
44
|
};
|
|
45
45
|
}
|
|
46
|
-
return {
|
|
46
|
+
return {
|
|
47
|
+
ok: false,
|
|
48
|
+
reason: "resolvedOld === resolvedNew - no change needed",
|
|
49
|
+
};
|
|
47
50
|
}
|
|
48
51
|
// Extract span values from resolvedOld using static segments as anchors.
|
|
49
52
|
const spanValues = extractSpanValues(staticSegs, resolvedOld);
|
|
50
53
|
if (!spanValues) {
|
|
51
54
|
return {
|
|
52
55
|
ok: false,
|
|
53
|
-
reason: "template static segments don't align with resolvedOld
|
|
56
|
+
reason: "template static segments don't align with resolvedOld - possible desync. " +
|
|
54
57
|
`template="${templateText}", resolvedOld="${resolvedOld}"`,
|
|
55
58
|
};
|
|
56
59
|
}
|
|
57
|
-
// Verify reconstruction
|
|
60
|
+
// Verify reconstruction - extractSpanValues already guarantees this
|
|
58
61
|
// implicitly, but an explicit check catches any edge-case bugs early.
|
|
59
62
|
{
|
|
60
63
|
let check = "";
|
|
@@ -76,13 +79,13 @@ class TemplateDiffService {
|
|
|
76
79
|
return {
|
|
77
80
|
ok: false,
|
|
78
81
|
reason: `dynamic span value(s) [${spanValues.map((v) => `"${v}"`).join(", ")}] ` +
|
|
79
|
-
`not found in resolvedNew="${resolvedNew}"
|
|
82
|
+
`not found in resolvedNew="${resolvedNew}" - structural change, manual review required`,
|
|
80
83
|
};
|
|
81
84
|
}
|
|
82
85
|
if (newStaticSegs.length !== staticSegs.length) {
|
|
83
86
|
return {
|
|
84
87
|
ok: false,
|
|
85
|
-
reason: "static segment count mismatch after alignment
|
|
88
|
+
reason: "static segment count mismatch after alignment - structural change",
|
|
86
89
|
};
|
|
87
90
|
}
|
|
88
91
|
// Diff: find which static segments changed.
|
|
@@ -94,14 +97,14 @@ class TemplateDiffService {
|
|
|
94
97
|
if (changedIndices.length === 0) {
|
|
95
98
|
return {
|
|
96
99
|
ok: false,
|
|
97
|
-
reason: "no static segment changed
|
|
100
|
+
reason: "no static segment changed - the difference is in a dynamic span. " +
|
|
98
101
|
"Update the argument passed to the function instead.",
|
|
99
102
|
};
|
|
100
103
|
}
|
|
101
104
|
if (changedIndices.length > 1) {
|
|
102
105
|
return {
|
|
103
106
|
ok: false,
|
|
104
|
-
reason: `${changedIndices.length} static segments changed simultaneously
|
|
107
|
+
reason: `${changedIndices.length} static segments changed simultaneously - ` +
|
|
105
108
|
"ambiguous, manual review required",
|
|
106
109
|
};
|
|
107
110
|
}
|
|
@@ -119,7 +122,7 @@ class TemplateDiffService {
|
|
|
119
122
|
//
|
|
120
123
|
// Uses the TemplateExpression AST node as the authoritative source
|
|
121
124
|
// of static/expr segments (avoids text-parsing edge cases).
|
|
122
|
-
// Returns `patchedText`
|
|
125
|
+
// Returns `patchedText` - pass directly to templateNode.replaceWithText().
|
|
123
126
|
// ─────────────────────────────────────────────────────────────────
|
|
124
127
|
static computeNodePatch(templateNode, resolvedOld, resolvedNew) {
|
|
125
128
|
// Extract static segments and expr texts directly from the AST.
|
|
@@ -172,13 +175,13 @@ class TemplateDiffService {
|
|
|
172
175
|
if (changedIndices.length === 0) {
|
|
173
176
|
return {
|
|
174
177
|
ok: false,
|
|
175
|
-
reason: "difference is in dynamic span
|
|
178
|
+
reason: "difference is in dynamic span - update call-site argument",
|
|
176
179
|
};
|
|
177
180
|
}
|
|
178
181
|
if (changedIndices.length > 1) {
|
|
179
182
|
return {
|
|
180
183
|
ok: false,
|
|
181
|
-
reason: `${changedIndices.length} static segments changed
|
|
184
|
+
reason: `${changedIndices.length} static segments changed - ambiguous`,
|
|
182
185
|
};
|
|
183
186
|
}
|
|
184
187
|
const patchedStatics = [...staticSegs];
|
|
@@ -238,7 +241,7 @@ function parseTemplateSource(text) {
|
|
|
238
241
|
*
|
|
239
242
|
* Algorithm: iterate over all static segments in order. After consuming
|
|
240
243
|
* each static segment, find the next static as the delimiter for the
|
|
241
|
-
* span value
|
|
244
|
+
* span value - then advance pos to the start of that next static so
|
|
242
245
|
* the following iteration can consume it. This ensures pos is always
|
|
243
246
|
* consistent at the start of each static segment.
|
|
244
247
|
*
|
|
@@ -265,7 +268,7 @@ function extractSpanValues(staticSegments, resolved) {
|
|
|
265
268
|
break;
|
|
266
269
|
}
|
|
267
270
|
if (nextStatic === "") {
|
|
268
|
-
// Empty non-trailing static: adjacent spans
|
|
271
|
+
// Empty non-trailing static: adjacent spans - boundary is ambiguous.
|
|
269
272
|
return null;
|
|
270
273
|
}
|
|
271
274
|
const delimIdx = resolved.indexOf(nextStatic, pos);
|
|
@@ -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"}
|