sela-core 0.1.0-alpha.41 → 0.1.0-alpha.43
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/commands/dnaSync.d.ts +3 -0
- package/dist/cli/commands/dnaSync.d.ts.map +1 -0
- package/dist/cli/commands/dnaSync.js +200 -0
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli/commands/init.js +51 -23
- package/dist/cli/commands/sync.d.ts.map +1 -1
- package/dist/cli/commands/sync.js +18 -0
- package/dist/cli/conventionsInit.d.ts +6 -0
- package/dist/cli/conventionsInit.d.ts.map +1 -1
- package/dist/cli/conventionsInit.js +2 -1
- package/dist/cli/index.js +3 -0
- package/dist/config/ConfigLoader.d.ts.map +1 -1
- package/dist/config/ConfigLoader.js +70 -29
- package/dist/config/Conventions.d.ts +8 -2
- package/dist/config/Conventions.d.ts.map +1 -1
- package/dist/config/Conventions.js +6 -3
- package/dist/config/HealingGovernance.d.ts +35 -0
- package/dist/config/HealingGovernance.d.ts.map +1 -0
- package/dist/config/HealingGovernance.js +187 -0
- package/dist/config/PathScopeGuard.d.ts +8 -0
- package/dist/config/PathScopeGuard.d.ts.map +1 -1
- package/dist/config/PathScopeGuard.js +4 -1
- package/dist/config/SelaConfig.d.ts +97 -1
- package/dist/config/SelaConfig.d.ts.map +1 -1
- package/dist/config/SelaConfig.js +84 -1
- package/dist/engine/HealingRegistry.d.ts +13 -0
- package/dist/engine/HealingRegistry.d.ts.map +1 -1
- package/dist/engine/HealingRegistry.js +21 -0
- package/dist/engine/SelaEngine.d.ts +59 -3
- package/dist/engine/SelaEngine.d.ts.map +1 -1
- package/dist/engine/SelaEngine.js +699 -36
- package/dist/engine/healing/AncestryValidator.d.ts +68 -0
- package/dist/engine/healing/AncestryValidator.d.ts.map +1 -0
- package/dist/engine/healing/AncestryValidator.js +57 -0
- package/dist/engine/healing/MethodParityGuard.d.ts +48 -0
- package/dist/engine/healing/MethodParityGuard.d.ts.map +1 -0
- package/dist/engine/healing/MethodParityGuard.js +204 -0
- package/dist/engine/lifecycle/SelaLifecycleStateManager.d.ts +124 -0
- package/dist/engine/lifecycle/SelaLifecycleStateManager.d.ts.map +1 -0
- package/dist/engine/lifecycle/SelaLifecycleStateManager.js +241 -0
- package/dist/engine/singleton.d.ts +2 -0
- package/dist/engine/singleton.d.ts.map +1 -1
- package/dist/engine/singleton.js +43 -1
- package/dist/errors/FailureClassifier.d.ts +62 -0
- package/dist/errors/FailureClassifier.d.ts.map +1 -0
- package/dist/errors/FailureClassifier.js +280 -0
- package/dist/errors/SelectorForensics.d.ts +38 -0
- package/dist/errors/SelectorForensics.d.ts.map +1 -0
- package/dist/errors/SelectorForensics.js +282 -0
- package/dist/fixtures/ancestryHeal.d.ts +42 -0
- package/dist/fixtures/ancestryHeal.d.ts.map +1 -0
- package/dist/fixtures/ancestryHeal.js +190 -0
- package/dist/fixtures/errorClassification.d.ts +21 -0
- package/dist/fixtures/errorClassification.d.ts.map +1 -0
- package/dist/fixtures/errorClassification.js +28 -0
- package/dist/fixtures/expectProxy.d.ts +31 -7
- package/dist/fixtures/expectProxy.d.ts.map +1 -1
- package/dist/fixtures/expectProxy.js +332 -127
- package/dist/fixtures/index.d.ts +3 -2
- package/dist/fixtures/index.d.ts.map +1 -1
- package/dist/fixtures/index.js +242 -65
- package/dist/fixtures/lineage.d.ts +99 -0
- package/dist/fixtures/lineage.d.ts.map +1 -0
- package/dist/fixtures/lineage.js +259 -0
- package/dist/fixtures/moduleExpect.d.ts +2 -1
- package/dist/fixtures/moduleExpect.d.ts.map +1 -1
- package/dist/fixtures/moduleExpect.js +32 -23
- package/dist/index.cjs +26 -0
- package/dist/index.d.cts +3 -0
- package/dist/index.d.cts.map +1 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.esm.d.mts +12 -0
- package/dist/index.esm.d.mts.map +1 -0
- package/dist/index.esm.mjs +36 -0
- package/dist/index.js +9 -2
- package/dist/services/ConventionsScanner.d.ts.map +1 -1
- package/dist/services/ConventionsScanner.js +2 -12
- package/dist/services/DnaEditorService.d.ts.map +1 -1
- package/dist/services/DnaEditorService.js +3 -2
- package/dist/services/DnaSyncService.d.ts +77 -0
- package/dist/services/DnaSyncService.d.ts.map +1 -0
- package/dist/services/DnaSyncService.js +484 -0
- package/dist/services/HealJournal.d.ts +22 -0
- package/dist/services/HealJournal.d.ts.map +1 -0
- package/dist/services/HealJournal.js +127 -0
- package/dist/services/HealReportService.d.ts +6 -0
- package/dist/services/HealReportService.d.ts.map +1 -1
- package/dist/services/HealReportService.js +106 -10
- package/dist/services/HealingCacheService.d.ts +7 -0
- package/dist/services/HealingCacheService.d.ts.map +1 -1
- package/dist/services/HealingCacheService.js +21 -0
- package/dist/services/LLMService.d.ts.map +1 -1
- package/dist/services/LLMService.js +133 -7
- package/dist/services/LedgerAnchor.d.ts +35 -0
- package/dist/services/LedgerAnchor.d.ts.map +1 -0
- package/dist/services/LedgerAnchor.js +154 -0
- package/dist/services/SafetyGuard.d.ts +2 -0
- package/dist/services/SafetyGuard.d.ts.map +1 -1
- package/dist/services/SmartDOMPruner.d.ts +58 -27
- package/dist/services/SmartDOMPruner.d.ts.map +1 -1
- package/dist/services/SmartDOMPruner.js +545 -122
- package/dist/services/SnapshotService.d.ts +28 -4
- package/dist/services/SnapshotService.d.ts.map +1 -1
- package/dist/services/SnapshotService.js +244 -11
- package/dist/services/SourceUpdater.d.ts.map +1 -1
- package/dist/services/SourceUpdater.js +25 -0
- package/dist/services/confidence/BusinessContextParser.d.ts +15 -1
- package/dist/services/confidence/BusinessContextParser.d.ts.map +1 -1
- package/dist/services/confidence/BusinessContextParser.js +54 -0
- package/dist/services/confidence/ConfidenceVectorEngine.d.ts.map +1 -1
- package/dist/services/confidence/ConfidenceVectorEngine.js +5 -1
- package/dist/services/confidence/types.d.ts +18 -0
- package/dist/services/confidence/types.d.ts.map +1 -1
- package/dist/services/safety/rules/ConfidenceVectorRule.d.ts.map +1 -1
- package/dist/services/safety/rules/ConfidenceVectorRule.js +1 -0
- package/dist/types/index.d.ts +202 -4
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +6 -1
- package/dist/utils/DOMUtils.d.ts +10 -0
- package/dist/utils/DOMUtils.d.ts.map +1 -1
- package/dist/utils/DOMUtils.js +47 -26
- package/dist/utils/StackUtils.d.ts.map +1 -1
- package/dist/utils/StackUtils.js +10 -1
- package/dist/utils/implicitAriaRole.d.ts +5 -0
- package/dist/utils/implicitAriaRole.d.ts.map +1 -0
- package/dist/utils/implicitAriaRole.js +113 -0
- package/dist/utils/sourcePathGuard.d.ts +10 -0
- package/dist/utils/sourcePathGuard.d.ts.map +1 -0
- package/dist/utils/sourcePathGuard.js +59 -0
- package/dist/vendor/chart.umd.min.js +14 -0
- package/package.json +19 -7
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dnaSync.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/dnaSync.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAmCzC,wBAAgB,eAAe,CAAC,GAAG,EAAE,OAAO,GAAG,IAAI,CA+JlD"}
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// src/cli/commands/dnaSync.ts
|
|
3
|
+
//
|
|
4
|
+
// The PR & Sync Engine CLI surface (Blueprint B):
|
|
5
|
+
// sela dna sync — reconcile the ledger against merged source
|
|
6
|
+
// sela dna status — read-only drift report (CI gate exit codes)
|
|
7
|
+
// sela dna clear-tombstone — human override for a rejected-selector memory
|
|
8
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
9
|
+
if (k2 === undefined) k2 = k;
|
|
10
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
11
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
12
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
13
|
+
}
|
|
14
|
+
Object.defineProperty(o, k2, desc);
|
|
15
|
+
}) : (function(o, m, k, k2) {
|
|
16
|
+
if (k2 === undefined) k2 = k;
|
|
17
|
+
o[k2] = m[k];
|
|
18
|
+
}));
|
|
19
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
20
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
21
|
+
}) : function(o, v) {
|
|
22
|
+
o["default"] = v;
|
|
23
|
+
});
|
|
24
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
25
|
+
var ownKeys = function(o) {
|
|
26
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
27
|
+
var ar = [];
|
|
28
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
29
|
+
return ar;
|
|
30
|
+
};
|
|
31
|
+
return ownKeys(o);
|
|
32
|
+
};
|
|
33
|
+
return function (mod) {
|
|
34
|
+
if (mod && mod.__esModule) return mod;
|
|
35
|
+
var result = {};
|
|
36
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
37
|
+
__setModuleDefault(result, mod);
|
|
38
|
+
return result;
|
|
39
|
+
};
|
|
40
|
+
})();
|
|
41
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
42
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
43
|
+
};
|
|
44
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
45
|
+
exports.registerDnaSync = registerDnaSync;
|
|
46
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
47
|
+
const fs = __importStar(require("fs"));
|
|
48
|
+
const path = __importStar(require("path"));
|
|
49
|
+
const ConfigLoader_js_1 = require("../../config/ConfigLoader.js");
|
|
50
|
+
const DnaSyncService_js_1 = require("../../services/DnaSyncService.js");
|
|
51
|
+
const SnapshotService_js_1 = require("../../services/SnapshotService.js");
|
|
52
|
+
const HealingCacheService_js_1 = require("../../services/HealingCacheService.js");
|
|
53
|
+
const LedgerAnchor_js_1 = require("../../services/LedgerAnchor.js");
|
|
54
|
+
const ProgressReporter_js_1 = require("../ui/ProgressReporter.js");
|
|
55
|
+
const STATE_COLORS = {
|
|
56
|
+
CONFIRMED: chalk_1.default.green,
|
|
57
|
+
REJECTED: chalk_1.default.red,
|
|
58
|
+
MANUAL_REFACTOR: chalk_1.default.yellow,
|
|
59
|
+
DRIFTED: chalk_1.default.yellow,
|
|
60
|
+
ORPHANED: chalk_1.default.gray,
|
|
61
|
+
};
|
|
62
|
+
function samePath(a, b) {
|
|
63
|
+
return (path.resolve(a).split(path.sep).join("/").toLowerCase() ===
|
|
64
|
+
path.resolve(b).split(path.sep).join("/").toLowerCase());
|
|
65
|
+
}
|
|
66
|
+
function registerDnaSync(dna) {
|
|
67
|
+
dna
|
|
68
|
+
.command("sync")
|
|
69
|
+
.description("Reconcile the DNA ledger against merged source (partial-PR safe): " +
|
|
70
|
+
"CONFIRM merged heals, REJECT + tombstone reverted ones, GC orphans")
|
|
71
|
+
.option("--dry-run", "Report outcomes without writing anything")
|
|
72
|
+
.option("--json", "Emit the raw JSON report")
|
|
73
|
+
.option("--prune", "Delete ORPHANED records and their visual baselines")
|
|
74
|
+
.option("--strict", "Exit 1 when any REJECTED or DRIFTED record is found (CI gate)")
|
|
75
|
+
.option("--dir <path>", "Override snapshot directory")
|
|
76
|
+
.action(async (opts) => {
|
|
77
|
+
const config = ConfigLoader_js_1.ConfigLoader.getInstance();
|
|
78
|
+
const snapshotDir = path.resolve(opts.dir ?? config.dnaStoragePath);
|
|
79
|
+
const progress = new ProgressReporter_js_1.ProgressReporter(opts.json);
|
|
80
|
+
const cache = new HealingCacheService_js_1.HealingCacheService();
|
|
81
|
+
const service = new DnaSyncService_js_1.DnaSyncService(snapshotDir);
|
|
82
|
+
const report = await progress.run("Reconciling ledger against source…", () => service.sync({
|
|
83
|
+
dryRun: opts.dryRun,
|
|
84
|
+
prune: opts.prune,
|
|
85
|
+
decidedBy: "cli",
|
|
86
|
+
purgeCache: (r) => cache.purgeWhere((e) => e.healedSelector === r.healedSelector &&
|
|
87
|
+
samePath(e.filePath, r.filePath)),
|
|
88
|
+
}));
|
|
89
|
+
if (opts.json) {
|
|
90
|
+
process.stdout.write(JSON.stringify(report, null, 2) + "\n");
|
|
91
|
+
}
|
|
92
|
+
else {
|
|
93
|
+
for (const o of report.outcomes) {
|
|
94
|
+
const paint = STATE_COLORS[o.state] ?? chalk_1.default.white;
|
|
95
|
+
const loc = o.line ? `${o.file}:${o.line}` : o.file;
|
|
96
|
+
process.stdout.write(` ${paint(o.state.padEnd(15))} ${o.key.slice(0, 12)} ${loc}\n` +
|
|
97
|
+
` ${chalk_1.default.dim(o.reason)}` +
|
|
98
|
+
(o.actions.length ? chalk_1.default.dim(` [${o.actions.join(", ")}]`) : "") +
|
|
99
|
+
"\n");
|
|
100
|
+
}
|
|
101
|
+
const c = report.counts;
|
|
102
|
+
process.stdout.write(`\n ${chalk_1.default.bold("Sync summary")}${report.dryRun ? chalk_1.default.dim(" (dry run)") : ""}: ` +
|
|
103
|
+
`${chalk_1.default.green(`${c.CONFIRMED} confirmed`)}, ` +
|
|
104
|
+
`${chalk_1.default.red(`${c.REJECTED} rejected`)}, ` +
|
|
105
|
+
`${chalk_1.default.yellow(`${c.MANUAL_REFACTOR} refactored`)}, ` +
|
|
106
|
+
`${chalk_1.default.yellow(`${c.DRIFTED} drifted`)}, ` +
|
|
107
|
+
`${chalk_1.default.gray(`${c.ORPHANED} orphaned`)} ` +
|
|
108
|
+
chalk_1.default.dim(`(${report.scanned} scanned, ${report.skipped} skipped)`) +
|
|
109
|
+
"\n");
|
|
110
|
+
}
|
|
111
|
+
if (opts.strict && (report.counts.REJECTED > 0 || report.counts.DRIFTED > 0)) {
|
|
112
|
+
process.exit(1);
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
dna
|
|
116
|
+
.command("status")
|
|
117
|
+
.description("Read-only ledger drift report (exit 0 clean / 2 drift)")
|
|
118
|
+
.option("--json", "Emit raw JSON")
|
|
119
|
+
.option("--dir <path>", "Override snapshot directory")
|
|
120
|
+
.action(async (opts) => {
|
|
121
|
+
const config = ConfigLoader_js_1.ConfigLoader.getInstance();
|
|
122
|
+
const snapshotDir = path.resolve(opts.dir ?? config.dnaStoragePath);
|
|
123
|
+
const service = new DnaSyncService_js_1.DnaSyncService(snapshotDir);
|
|
124
|
+
const snapshots = new SnapshotService_js_1.SnapshotService(snapshotDir);
|
|
125
|
+
const counts = {
|
|
126
|
+
PENDING_REVIEW: 0,
|
|
127
|
+
CONFIRMED: 0,
|
|
128
|
+
REJECTED: 0,
|
|
129
|
+
MANUAL_REFACTOR: 0,
|
|
130
|
+
DRIFTED: 0,
|
|
131
|
+
ORPHANED: 0,
|
|
132
|
+
LEGACY: 0,
|
|
133
|
+
};
|
|
134
|
+
let aliases = 0;
|
|
135
|
+
try {
|
|
136
|
+
for (const f of fs
|
|
137
|
+
.readdirSync(snapshotDir)
|
|
138
|
+
.filter((x) => x.endsWith(".json") && x !== "_meta.json")) {
|
|
139
|
+
const rec = await snapshots.load(f.slice(0, -".json".length));
|
|
140
|
+
if (!rec?.healedSelector)
|
|
141
|
+
continue;
|
|
142
|
+
aliases++;
|
|
143
|
+
counts[rec.review?.state ?? "LEGACY"]++;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
catch {
|
|
147
|
+
/* empty ledger */
|
|
148
|
+
}
|
|
149
|
+
const meta = service.readMeta();
|
|
150
|
+
const head = (0, LedgerAnchor_js_1.captureGitInfo)().commitSha;
|
|
151
|
+
const stale = !service.isFresh();
|
|
152
|
+
const drift = stale || counts.REJECTED > 0 || counts.DRIFTED > 0;
|
|
153
|
+
if (opts.json) {
|
|
154
|
+
process.stdout.write(JSON.stringify({ aliases, counts, lastSyncedSha: meta?.lastSyncedSha ?? null, head, stale, drift }, null, 2) + "\n");
|
|
155
|
+
}
|
|
156
|
+
else {
|
|
157
|
+
process.stdout.write(` Ledger aliases: ${aliases}\n` +
|
|
158
|
+
` Review states: ${Object.entries(counts)
|
|
159
|
+
.filter(([, n]) => n > 0)
|
|
160
|
+
.map(([k, n]) => `${k}=${n}`)
|
|
161
|
+
.join(", ") || "none"}\n` +
|
|
162
|
+
` Last synced: ${meta?.lastSyncedSha?.slice(0, 12) ?? chalk_1.default.yellow("never")}` +
|
|
163
|
+
` (HEAD ${head?.slice(0, 12) ?? "n/a"})\n` +
|
|
164
|
+
(stale
|
|
165
|
+
? chalk_1.default.yellow(" ⚠ Ledger is stale — run `sela dna sync`.\n")
|
|
166
|
+
: chalk_1.default.green(" ✓ Ledger is in sync with HEAD.\n")));
|
|
167
|
+
}
|
|
168
|
+
if (drift)
|
|
169
|
+
process.exit(2);
|
|
170
|
+
});
|
|
171
|
+
dna
|
|
172
|
+
.command("clear-tombstone <key>")
|
|
173
|
+
.description("Remove a rejected-selector memory so it may be proposed again")
|
|
174
|
+
.option("--selector <s>", "Remove only this tombstoned selector")
|
|
175
|
+
.option("--dir <path>", "Override snapshot directory")
|
|
176
|
+
.action(async (key, opts) => {
|
|
177
|
+
const config = ConfigLoader_js_1.ConfigLoader.getInstance();
|
|
178
|
+
const snapshotDir = path.resolve(opts.dir ?? config.dnaStoragePath);
|
|
179
|
+
const snapshots = new SnapshotService_js_1.SnapshotService(snapshotDir);
|
|
180
|
+
const rec = await snapshots.load(key);
|
|
181
|
+
if (!rec) {
|
|
182
|
+
const err = new Error(`DNA not found: ${key}`);
|
|
183
|
+
err.code = "DNA_NOT_FOUND";
|
|
184
|
+
throw err;
|
|
185
|
+
}
|
|
186
|
+
const before = rec.tombstones?.length ?? 0;
|
|
187
|
+
if (before === 0) {
|
|
188
|
+
process.stdout.write(chalk_1.default.yellow(` No tombstones on "${key}".\n`));
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
const remaining = opts.selector
|
|
192
|
+
? (rec.tombstones ?? []).filter((t) => t.rejectedSelector !== opts.selector)
|
|
193
|
+
: [];
|
|
194
|
+
await snapshots.save(key, {
|
|
195
|
+
...rec,
|
|
196
|
+
tombstones: remaining.length > 0 ? remaining : undefined,
|
|
197
|
+
});
|
|
198
|
+
process.stdout.write(chalk_1.default.green(` Cleared ${before - remaining.length} tombstone(s) on "${key}".\n`));
|
|
199
|
+
});
|
|
200
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/init.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/init.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AA2BpC,MAAM,WAAW,WAAW;IAC1B,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,kDAAkD;IAClD,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,4EAA4E;IAC5E,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,wBAAgB,YAAY,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAoBnD"}
|
|
@@ -32,10 +32,14 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
32
32
|
return result;
|
|
33
33
|
};
|
|
34
34
|
})();
|
|
35
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
+
};
|
|
35
38
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
39
|
exports.registerInit = registerInit;
|
|
37
40
|
const fs = __importStar(require("fs"));
|
|
38
41
|
const path = __importStar(require("path"));
|
|
42
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
39
43
|
const logger_js_1 = require("../../utils/logger.js");
|
|
40
44
|
const conventionsInit_js_1 = require("../conventionsInit.js");
|
|
41
45
|
const PACKAGE_NAME = "sela-core";
|
|
@@ -45,7 +49,6 @@ export default defineConfig({
|
|
|
45
49
|
healingPolicy: 'balanced',
|
|
46
50
|
updateStrategy: 'inPlace',
|
|
47
51
|
autoCommit: false,
|
|
48
|
-
dnaStoragePath: 'sela-snapshots',
|
|
49
52
|
});
|
|
50
53
|
`;
|
|
51
54
|
const CONFIG_TEMPLATE_JS = `const { defineConfig } = require('${PACKAGE_NAME}');
|
|
@@ -54,10 +57,8 @@ module.exports = defineConfig({
|
|
|
54
57
|
healingPolicy: 'balanced',
|
|
55
58
|
updateStrategy: 'inPlace',
|
|
56
59
|
autoCommit: false,
|
|
57
|
-
dnaStoragePath: 'sela-snapshots',
|
|
58
60
|
});
|
|
59
61
|
`;
|
|
60
|
-
const REQUIRED_ENV_VARS = ["ANTHROPIC_API_KEY"];
|
|
61
62
|
function registerInit(program) {
|
|
62
63
|
program
|
|
63
64
|
.command("init")
|
|
@@ -90,17 +91,11 @@ async function runInit(opts) {
|
|
|
90
91
|
fs.writeFileSync(configPath, isTs ? CONFIG_TEMPLATE_TS : CONFIG_TEMPLATE_JS, "utf8");
|
|
91
92
|
logger_js_1.logger.success(`Created ${configFile}`);
|
|
92
93
|
}
|
|
93
|
-
// ── Environment
|
|
94
|
+
// ── Environment — resolve API key presence (non-fatal) ──────────
|
|
94
95
|
loadDotEnv(cwd);
|
|
95
|
-
const
|
|
96
|
-
if (
|
|
97
|
-
logger_js_1.logger.
|
|
98
|
-
for (const k of missing)
|
|
99
|
-
logger_js_1.logger.info(` - ${k}`);
|
|
100
|
-
logger_js_1.logger.info(` Add them to .env or your shell profile before running tests.\n`);
|
|
101
|
-
}
|
|
102
|
-
else {
|
|
103
|
-
logger_js_1.logger.success(`Environment variables OK`);
|
|
96
|
+
const hasApiKey = Boolean(process.env.ANTHROPIC_API_KEY);
|
|
97
|
+
if (hasApiKey) {
|
|
98
|
+
logger_js_1.logger.success(`ANTHROPIC_API_KEY detected`);
|
|
104
99
|
}
|
|
105
100
|
// ── Import swap (default ON; opt out with --no-update-imports) ──
|
|
106
101
|
if (opts.updateImports !== false) {
|
|
@@ -117,21 +112,54 @@ async function runInit(opts) {
|
|
|
117
112
|
// ── Conventions auto-discovery (default ON; opt out --no-conventions) ──
|
|
118
113
|
if (opts.conventions !== false) {
|
|
119
114
|
logger_js_1.logger.info(`Discovering testing conventions…`);
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
115
|
+
try {
|
|
116
|
+
await (0, conventionsInit_js_1.runConventionsWizard)(cwd, {
|
|
117
|
+
llm: opts.llm,
|
|
118
|
+
hasApiKey,
|
|
119
|
+
yes: opts.yes,
|
|
120
|
+
dryRun: opts.dryRun,
|
|
121
|
+
glob: opts.glob,
|
|
122
|
+
maxFiles: opts.maxFiles ? Number(opts.maxFiles) : undefined,
|
|
123
|
+
fileTimeoutMs: opts.fileTimeout ? Number(opts.fileTimeout) : undefined,
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
catch (err) {
|
|
127
|
+
logger_js_1.logger.warn(`Conventions discovery failed: ${err.message ?? err}`);
|
|
128
|
+
logger_js_1.logger.info(` Continuing — conventions can be re-run with: sela init\n`);
|
|
129
|
+
}
|
|
128
130
|
}
|
|
129
131
|
// ── .npmrc node-options upsert ───────────────────────────────────
|
|
130
132
|
if (!opts.dryRun) {
|
|
131
133
|
upsertNpmrcHook(cwd);
|
|
132
134
|
}
|
|
135
|
+
// ── Keyless-mode advisory (rendered LAST so it's the final read) ─
|
|
136
|
+
if (!hasApiKey) {
|
|
137
|
+
renderKeylessModeBlock();
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
function renderKeylessModeBlock() {
|
|
141
|
+
const sep = chalk_1.default.yellow(" " + "─".repeat(62));
|
|
142
|
+
logger_js_1.logger.info("");
|
|
143
|
+
logger_js_1.logger.info(sep);
|
|
144
|
+
logger_js_1.logger.info(` ${chalk_1.default.bold(chalk_1.default.cyan("Sela initialized — Deterministic Local Mode"))}`);
|
|
145
|
+
logger_js_1.logger.info(sep);
|
|
146
|
+
logger_js_1.logger.info("");
|
|
147
|
+
logger_js_1.logger.info(` ${chalk_1.default.yellow("⚠")} ${chalk_1.default.yellow("No ANTHROPIC_API_KEY detected. LLM healing is disabled.")}`);
|
|
148
|
+
logger_js_1.logger.info("");
|
|
149
|
+
logger_js_1.logger.info(" All bootstrapping steps completed successfully. Static scanning");
|
|
150
|
+
logger_js_1.logger.info(" and deterministic convention discovery are fully operational.");
|
|
151
|
+
logger_js_1.logger.info("");
|
|
152
|
+
logger_js_1.logger.info(chalk_1.default.bold(" To enable AI-powered healing, add your key:"));
|
|
153
|
+
logger_js_1.logger.info(` ${chalk_1.default.dim("echo 'ANTHROPIC_API_KEY=sk-ant-...' >> .env")}`);
|
|
154
|
+
logger_js_1.logger.info("");
|
|
155
|
+
logger_js_1.logger.info(chalk_1.default.bold(" For local / private models, configure sela.config.ts:"));
|
|
156
|
+
logger_js_1.logger.info(` ${chalk_1.default.dim("llm: { healingModel: { provider: 'openai-compatible',")}`);
|
|
157
|
+
logger_js_1.logger.info(` ${chalk_1.default.dim(" modelId: 'llama3', baseUrl: 'http://localhost:11434/v1' } }")}`);
|
|
158
|
+
logger_js_1.logger.info("");
|
|
159
|
+
logger_js_1.logger.info(sep);
|
|
160
|
+
logger_js_1.logger.info("");
|
|
133
161
|
}
|
|
134
|
-
const NPMRC_HOOK = '--import sela/register';
|
|
162
|
+
const NPMRC_HOOK = '--import sela-core/register';
|
|
135
163
|
const NPMRC_NODE_OPTIONS_RE = /^node-options\s*=\s*(.*)$/m;
|
|
136
164
|
function upsertNpmrcHook(cwd) {
|
|
137
165
|
const npmrcPath = path.join(cwd, '.npmrc');
|
|
@@ -178,7 +206,7 @@ function walkDir(dir, exts) {
|
|
|
178
206
|
"node_modules",
|
|
179
207
|
"dist",
|
|
180
208
|
".git",
|
|
181
|
-
"sela
|
|
209
|
+
".sela",
|
|
182
210
|
"sela-snapshots",
|
|
183
211
|
"test-results",
|
|
184
212
|
"playwright-report",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sync.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/sync.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"sync.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/sync.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAkBzC,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,OAAO,GAAG,IAAI,CAuNrD"}
|
|
@@ -9,6 +9,7 @@ const path_1 = __importDefault(require("path"));
|
|
|
9
9
|
const ConfigLoader_js_1 = require("../../config/ConfigLoader.js");
|
|
10
10
|
const DnaIndexService_js_1 = require("../../services/DnaIndexService.js");
|
|
11
11
|
const SourceLinkService_js_1 = require("../../services/SourceLinkService.js");
|
|
12
|
+
const SnapshotService_js_1 = require("../../services/SnapshotService.js");
|
|
12
13
|
const ProgressReporter_js_1 = require("../ui/ProgressReporter.js");
|
|
13
14
|
function registerSyncSource(dna) {
|
|
14
15
|
dna
|
|
@@ -18,6 +19,7 @@ function registerSyncSource(dna) {
|
|
|
18
19
|
.option("--dry-run", "Preview without writing")
|
|
19
20
|
.option("--json", "Emit JSON result")
|
|
20
21
|
.option("--dir <path>", "Override snapshot directory")
|
|
22
|
+
.option("--force", "Write even when the record's review state is not CONFIRMED (dangerous)")
|
|
21
23
|
.action(async (key, opts) => {
|
|
22
24
|
const config = ConfigLoader_js_1.ConfigLoader.getInstance();
|
|
23
25
|
const snapshotDir = opts.dir ?? config.dnaStoragePath;
|
|
@@ -35,6 +37,22 @@ function registerSyncSource(dna) {
|
|
|
35
37
|
err.code = "DNA_NOT_FOUND";
|
|
36
38
|
throw err;
|
|
37
39
|
}
|
|
40
|
+
// Guard (PR & Sync Engine): pushing DNA→source over a record the team
|
|
41
|
+
// has not CONFIRMED would re-impose a heal reviewers may have rejected
|
|
42
|
+
// — the exact resurrection this engine exists to prevent.
|
|
43
|
+
const rawSnap = await new SnapshotService_js_1.SnapshotService(snapshotDir).load(key);
|
|
44
|
+
const reviewState = rawSnap?.review?.state;
|
|
45
|
+
if (reviewState !== undefined && reviewState !== "CONFIRMED" && !opts.force) {
|
|
46
|
+
const reason = `DNA "${key}" review state is ${reviewState} — refusing to write it into source. ` +
|
|
47
|
+
"Run `sela dna sync` first, or pass --force to override.";
|
|
48
|
+
if (opts.json) {
|
|
49
|
+
process.stdout.write(JSON.stringify({ success: false, reason }, null, 2) + "\n");
|
|
50
|
+
process.exit(4);
|
|
51
|
+
}
|
|
52
|
+
const err = new Error(reason);
|
|
53
|
+
err.code = "REVIEW_STATE_GUARD";
|
|
54
|
+
throw err;
|
|
55
|
+
}
|
|
38
56
|
if (!record.sourceFile || record.sourceLine <= 0) {
|
|
39
57
|
if (opts.json) {
|
|
40
58
|
process.stdout.write(JSON.stringify({
|
|
@@ -13,6 +13,12 @@ export interface ConventionsInitOptions {
|
|
|
13
13
|
maxFiles?: number;
|
|
14
14
|
/** Run the optional LLM polish step. Default: false. */
|
|
15
15
|
llm?: boolean;
|
|
16
|
+
/**
|
|
17
|
+
* Explicit API-key presence flag. When false, LLM polish is skipped without
|
|
18
|
+
* consulting process.env. Falls back to Boolean(process.env.ANTHROPIC_API_KEY)
|
|
19
|
+
* when omitted, preserving backward-compatible behaviour for standalone calls.
|
|
20
|
+
*/
|
|
21
|
+
hasApiKey?: boolean;
|
|
16
22
|
/** Injected Anthropic client (tests). Falls back to a sanitizing client. */
|
|
17
23
|
client?: Anthropic;
|
|
18
24
|
model?: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"conventionsInit.d.ts","sourceRoot":"","sources":["../../src/cli/conventionsInit.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,SAAS,MAAM,mBAAmB,CAAC;AAO/C,OAAO,EAGL,KAAK,WAAW,EACjB,MAAM,mCAAmC,CAAC;AAC3C,OAAO,EACL,KAAK,WAAW,EAChB,KAAK,SAAS,EAEf,MAAM,0BAA0B,CAAC;AAelC,MAAM,MAAM,aAAa,GAAG,OAAO,GAAG,SAAS,GAAG,MAAM,CAAC;AAEzD,MAAM,WAAW,sBAAsB;IACrC,8BAA8B;IAC9B,IAAI,CAAC,EAAE,aAAa,CAAC;IACrB,sDAAsD;IACtD,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,yCAAyC;IACzC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,wDAAwD;IACxD,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,4EAA4E;IAC5E,MAAM,CAAC,EAAE,SAAS,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,2CAA2C;IAC3C,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAkCD,iFAAiF;AACjF,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CA0BhD;AAED,wEAAwE;AACxE,wBAAgB,gBAAgB,CAC9B,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,IAAI,CAAC,sBAAsB,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAAC,GAC9D,MAAM,EAAE,CAcV;AAMD,MAAM,WAAW,gBAAgB;IAC/B,WAAW,EAAE,WAAW,CAAC;IACzB,KAAK,EAAE,SAAS,CAAC;CAClB;AAED,wBAAsB,mBAAmB,CACvC,KAAK,EAAE,MAAM,EAAE,EACf,IAAI,GAAE,sBAAsB,GAAG;IAAE,UAAU,CAAC,EAAE,WAAW,CAAC,YAAY,CAAC,CAAA;CAAO,GAC7E,OAAO,CAAC,gBAAgB,CAAC,CAsB3B;AAQD;;;;;GAKG;AACH,wBAAsB,iBAAiB,CACrC,KAAK,EAAE,WAAW,EAClB,KAAK,EAAE,SAAS,EAChB,MAAM,EAAE,SAAS,EACjB,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC,CA4C7B;AAMD,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,wBAAgB,wBAAwB,CACtC,GAAG,EAAE,MAAM,EACX,WAAW,EAAE,WAAW,EACxB,IAAI,GAAE;IAAE,MAAM,CAAC,EAAE,OAAO,CAAA;CAAO,GAC9B,WAAW,CAQb;AAMD,wBAAsB,oBAAoB,CACxC,GAAG,EAAE,MAAM,EACX,IAAI,GAAE,sBAAsB,GAAG;IAAE,GAAG,CAAC,EAAE,OAAO,CAAA;CAAO,GACpD,OAAO,CAAC,IAAI,CAAC,
|
|
1
|
+
{"version":3,"file":"conventionsInit.d.ts","sourceRoot":"","sources":["../../src/cli/conventionsInit.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,SAAS,MAAM,mBAAmB,CAAC;AAO/C,OAAO,EAGL,KAAK,WAAW,EACjB,MAAM,mCAAmC,CAAC;AAC3C,OAAO,EACL,KAAK,WAAW,EAChB,KAAK,SAAS,EAEf,MAAM,0BAA0B,CAAC;AAelC,MAAM,MAAM,aAAa,GAAG,OAAO,GAAG,SAAS,GAAG,MAAM,CAAC;AAEzD,MAAM,WAAW,sBAAsB;IACrC,8BAA8B;IAC9B,IAAI,CAAC,EAAE,aAAa,CAAC;IACrB,sDAAsD;IACtD,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,yCAAyC;IACzC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,wDAAwD;IACxD,GAAG,CAAC,EAAE,OAAO,CAAC;IACd;;;;OAIG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,4EAA4E;IAC5E,MAAM,CAAC,EAAE,SAAS,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,2CAA2C;IAC3C,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAkCD,iFAAiF;AACjF,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CA0BhD;AAED,wEAAwE;AACxE,wBAAgB,gBAAgB,CAC9B,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,IAAI,CAAC,sBAAsB,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAAC,GAC9D,MAAM,EAAE,CAcV;AAMD,MAAM,WAAW,gBAAgB;IAC/B,WAAW,EAAE,WAAW,CAAC;IACzB,KAAK,EAAE,SAAS,CAAC;CAClB;AAED,wBAAsB,mBAAmB,CACvC,KAAK,EAAE,MAAM,EAAE,EACf,IAAI,GAAE,sBAAsB,GAAG;IAAE,UAAU,CAAC,EAAE,WAAW,CAAC,YAAY,CAAC,CAAA;CAAO,GAC7E,OAAO,CAAC,gBAAgB,CAAC,CAsB3B;AAQD;;;;;GAKG;AACH,wBAAsB,iBAAiB,CACrC,KAAK,EAAE,WAAW,EAClB,KAAK,EAAE,SAAS,EAChB,MAAM,EAAE,SAAS,EACjB,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC,CA4C7B;AAMD,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,wBAAgB,wBAAwB,CACtC,GAAG,EAAE,MAAM,EACX,WAAW,EAAE,WAAW,EACxB,IAAI,GAAE;IAAE,MAAM,CAAC,EAAE,OAAO,CAAA;CAAO,GAC9B,WAAW,CAQb;AAMD,wBAAsB,oBAAoB,CACxC,GAAG,EAAE,MAAM,EACX,IAAI,GAAE,sBAAsB,GAAG;IAAE,GAAG,CAAC,EAAE,OAAO,CAAA;CAAO,GACpD,OAAO,CAAC,IAAI,CAAC,CAkHf"}
|
|
@@ -285,7 +285,8 @@ async function runConventionsWizard(cwd, opts = {}) {
|
|
|
285
285
|
}
|
|
286
286
|
// ── Optional LLM polish ───────────────────────────────────────
|
|
287
287
|
let final = conventions;
|
|
288
|
-
const
|
|
288
|
+
const apiKeyPresent = opts.hasApiKey ?? Boolean(process.env.ANTHROPIC_API_KEY);
|
|
289
|
+
const canPolish = opts.llm !== false && apiKeyPresent;
|
|
289
290
|
const wantPolish = interactive
|
|
290
291
|
? canPolish &&
|
|
291
292
|
(await new enquirer_js_1.Confirm({
|
package/dist/cli/index.js
CHANGED
|
@@ -52,6 +52,7 @@ const find_js_1 = require("./commands/find.js");
|
|
|
52
52
|
const refactor_js_1 = require("./commands/refactor.js");
|
|
53
53
|
const bulk_js_1 = require("./commands/bulk.js");
|
|
54
54
|
const sync_js_1 = require("./commands/sync.js");
|
|
55
|
+
const dnaSync_js_1 = require("./commands/dnaSync.js");
|
|
55
56
|
const showReport_js_1 = require("./commands/showReport.js");
|
|
56
57
|
const merge_js_1 = require("./commands/merge.js");
|
|
57
58
|
const shadowRegression_js_1 = require("./commands/shadowRegression.js");
|
|
@@ -96,5 +97,7 @@ const dna = new commander_1.Command("dna")
|
|
|
96
97
|
(0, bulk_js_1.registerBulkUpdate)(dna);
|
|
97
98
|
// ── sela dna sync-source (T-15) ───────────────────────────────────────────────
|
|
98
99
|
(0, sync_js_1.registerSyncSource)(dna);
|
|
100
|
+
// ── sela dna sync / status / clear-tombstone — PR & Sync Engine ───────────────
|
|
101
|
+
(0, dnaSync_js_1.registerDnaSync)(dna);
|
|
99
102
|
program.addCommand(dna);
|
|
100
103
|
program.parseAsync(process.argv).catch(ErrorHandler_js_1.handleError);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ConfigLoader.d.ts","sourceRoot":"","sources":["../../src/config/ConfigLoader.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,UAAU,EAAE,cAAc,EAAiB,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"ConfigLoader.d.ts","sourceRoot":"","sources":["../../src/config/ConfigLoader.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,UAAU,EAAE,cAAc,EAAiB,MAAM,cAAc,CAAC;AAgSzE,qBAAa,YAAY;IACvB,OAAO,CAAC,MAAM,CAAC,QAAQ,CAA+B;IACtD,OAAO,CAAC,MAAM,CAAC,MAAM,CAAuB;IAK5C,OAAO,CAAC,MAAM,CAAC,aAAa,CAA2B;IAEvD;;;;;;;;OAQG;IACH,MAAM,CAAC,WAAW,IAAI,cAAc;IAqCpC;;;;OAIG;IACH,MAAM,CAAC,YAAY,IAAI,UAAU;IAKjC,OAAO,CAAC,MAAM,CAAC,aAAa;IAsB5B,yEAAyE;IACzE,MAAM,CAAC,SAAS,IAAI,MAAM,GAAG,IAAI;IAKjC,uDAAuD;IACvD,MAAM,CAAC,KAAK,IAAI,IAAI;CAKrB"}
|
|
@@ -55,11 +55,14 @@ const KNOWN_CONFIG_KEYS = new Set([
|
|
|
55
55
|
"cacheStoragePath",
|
|
56
56
|
"allowedPaths",
|
|
57
57
|
"maxHealsPerRun",
|
|
58
|
+
"smartPrune",
|
|
59
|
+
"largeDomNodeThreshold",
|
|
58
60
|
"sanitization",
|
|
59
61
|
"playwrightRules",
|
|
60
62
|
"blastRadiusProtection",
|
|
61
63
|
"branchOverrides",
|
|
62
64
|
"llm",
|
|
65
|
+
"healing",
|
|
63
66
|
]);
|
|
64
67
|
// ─────────────────────────────────────────────────────────────────
|
|
65
68
|
// FILE DISCOVERY
|
|
@@ -126,6 +129,51 @@ function validateConfigKeys(config, filePath) {
|
|
|
126
129
|
`Valid top-level keys: ${[...KNOWN_CONFIG_KEYS].join(", ")}.`);
|
|
127
130
|
}
|
|
128
131
|
}
|
|
132
|
+
let jitiInstance = null;
|
|
133
|
+
function getJiti() {
|
|
134
|
+
if (jitiInstance)
|
|
135
|
+
return jitiInstance;
|
|
136
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
137
|
+
const mod = require("jiti");
|
|
138
|
+
// jiti v2 exposes `createJiti`; the module root is also callable as a
|
|
139
|
+
// factory for backward compatibility. Support both without version-pinning.
|
|
140
|
+
const createJiti = (mod && mod.createJiti) || mod;
|
|
141
|
+
jitiInstance = createJiti(__filename, {
|
|
142
|
+
// Return the module namespace (with `.default`) untouched — we unwrap the
|
|
143
|
+
// default export ourselves, exactly as the native `require` path does.
|
|
144
|
+
interopDefault: false,
|
|
145
|
+
// Persist transpile output to disk so warm loads stay well under budget.
|
|
146
|
+
fsCache: true,
|
|
147
|
+
moduleCache: true,
|
|
148
|
+
});
|
|
149
|
+
return jitiInstance;
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Loads a single config module, transpiling on the fly when needed.
|
|
153
|
+
*
|
|
154
|
+
* • `.ts` / `.mts` / `.cts` → always via jiti. Native `require` of a `.ts`
|
|
155
|
+
* file is non-deterministic across Node versions (fails pre-22, type-strips
|
|
156
|
+
* with caveats on ≥22), so we never rely on it for TypeScript.
|
|
157
|
+
* • `.js` / `.cjs` / `.mjs` → native `require` first (fast, zero overhead for
|
|
158
|
+
* CommonJS configs). Only if that fails — e.g. the file is authored in ESM
|
|
159
|
+
* (`import`/`export`) inside a CJS project — do we fall back to jiti, which
|
|
160
|
+
* transpiles ESM↔CJS transparently.
|
|
161
|
+
*/
|
|
162
|
+
function loadConfigModule(candidate) {
|
|
163
|
+
const isTs = /\.[mc]?ts$/.test(candidate);
|
|
164
|
+
if (isTs) {
|
|
165
|
+
return getJiti()(candidate);
|
|
166
|
+
}
|
|
167
|
+
try {
|
|
168
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
169
|
+
return require(candidate);
|
|
170
|
+
}
|
|
171
|
+
catch {
|
|
172
|
+
// CJS require choked (most often an ESM config in a CJS project). Retry
|
|
173
|
+
// through jiti, which handles the ESM/CJS interop boundary deterministically.
|
|
174
|
+
return getJiti()(candidate);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
129
177
|
// ─────────────────────────────────────────────────────────────────
|
|
130
178
|
// CONFIG FILE LOADING
|
|
131
179
|
// ─────────────────────────────────────────────────────────────────
|
|
@@ -133,9 +181,9 @@ function validateConfigKeys(config, filePath) {
|
|
|
133
181
|
* Loads and validates the raw config from `configPath`.
|
|
134
182
|
*
|
|
135
183
|
* Load strategy (in order):
|
|
136
|
-
* 1. `configPath` as-is —
|
|
137
|
-
* (
|
|
138
|
-
* 2. `.js` sibling — for compiled
|
|
184
|
+
* 1. `configPath` as-is — transpiled via the embedded jiti loader for TS,
|
|
185
|
+
* native `require` (jiti fallback) for JS. No host loader assumed.
|
|
186
|
+
* 2. `.js` sibling — for compiled builds where only the transpiled file
|
|
139
187
|
* is present alongside the source `.ts`.
|
|
140
188
|
*
|
|
141
189
|
* Throws (never silently returns {}) when the file is found but every load
|
|
@@ -151,8 +199,7 @@ function loadRawConfig(configPath) {
|
|
|
151
199
|
if (!fs.existsSync(candidate))
|
|
152
200
|
continue;
|
|
153
201
|
try {
|
|
154
|
-
|
|
155
|
-
const mod = require(candidate);
|
|
202
|
+
const mod = loadConfigModule(candidate);
|
|
156
203
|
const config = mod?.default ?? mod ?? {};
|
|
157
204
|
if (typeof config !== "object" || config === null || Array.isArray(config)) {
|
|
158
205
|
throw new Error(`${path.basename(candidate)} must export a plain object — ` +
|
|
@@ -170,23 +217,18 @@ function loadRawConfig(configPath) {
|
|
|
170
217
|
}
|
|
171
218
|
// Every attempt failed. Two cases:
|
|
172
219
|
//
|
|
173
|
-
// 1.
|
|
174
|
-
//
|
|
175
|
-
//
|
|
176
|
-
// .
|
|
177
|
-
//
|
|
178
|
-
//
|
|
179
|
-
//
|
|
180
|
-
//
|
|
181
|
-
//
|
|
182
|
-
//
|
|
183
|
-
//
|
|
184
|
-
//
|
|
185
|
-
// • ERR_REQUIRE_ESM / "require() of ES Module" — Node.js detected the
|
|
186
|
-
// file is an ES module and refused the synchronous require() call.
|
|
187
|
-
// All of these are environment/loader mismatches, not config content bugs.
|
|
188
|
-
// Gracefully fall back to defaults so the CLI command (e.g.
|
|
189
|
-
// `sela shadow-regression`) still runs rather than crashing.
|
|
220
|
+
// 1. Loader-environment error. With the embedded jiti loader this is now
|
|
221
|
+
// RARE — jiti transpiles TS and bridges the ESM/CJS boundary itself, so
|
|
222
|
+
// the classic "no TypeScript loader" failures no longer occur under
|
|
223
|
+
// normal operation. This branch survives only as a defensive last resort
|
|
224
|
+
// for pathological hosts where even jiti cannot execute the file (e.g. a
|
|
225
|
+
// sandbox that blocks its transpile cache). The signatures below all stem
|
|
226
|
+
// from the same root cause — the file could not be executed as a module:
|
|
227
|
+
// • ERR_UNKNOWN_FILE_EXTENSION / "Unknown file extension .ts"
|
|
228
|
+
// • "Cannot use import statement outside a module"
|
|
229
|
+
// • ERR_REQUIRE_ESM / "require() of ES Module"
|
|
230
|
+
// These are environment issues, not config-content bugs, so fall back to
|
|
231
|
+
// defaults and warn rather than crashing the CLI command.
|
|
190
232
|
//
|
|
191
233
|
// 2. Genuine config error (syntax, key validation, etc.) → hard throw so CI
|
|
192
234
|
// surfaces the problem instead of silently running on stale defaults.
|
|
@@ -202,16 +244,15 @@ function loadRawConfig(configPath) {
|
|
|
202
244
|
msg.includes("require() of ES Module") ||
|
|
203
245
|
msg.includes("ERR_REQUIRE_ESM"));
|
|
204
246
|
if (isLoaderEnvError) {
|
|
205
|
-
logger_1.logger.warn(`[Sela] sela.config.ts found but
|
|
206
|
-
`
|
|
207
|
-
`
|
|
208
|
-
`
|
|
209
|
-
`(e.g. tsc --module commonjs) or set SELA_CONFIG_PATH to the compiled file.`);
|
|
247
|
+
logger_1.logger.warn(`[Sela] sela.config.ts found but could not be executed even with the ` +
|
|
248
|
+
`embedded loader in this Node.js context. Running on defaults. ` +
|
|
249
|
+
`This is unexpected — please report it, or set SELA_CONFIG_PATH to a ` +
|
|
250
|
+
`precompiled sela.config.js as a workaround.`);
|
|
210
251
|
return {};
|
|
211
252
|
}
|
|
212
253
|
throw new Error(`[Sela] Failed to load ${path.basename(configPath)}: ${msg || "unknown error"}. ` +
|
|
213
|
-
`
|
|
214
|
-
`or
|
|
254
|
+
`Sela transpiles TS/ESM configs internally, so this is a genuine config ` +
|
|
255
|
+
`error (syntax, bad export, or a throw in the file) — fix the file and retry.`);
|
|
215
256
|
}
|
|
216
257
|
// ─────────────────────────────────────────────────────────────────
|
|
217
258
|
// DEEP MERGE (plain objects only - arrays are replaced, not merged)
|
|
@@ -53,7 +53,13 @@ export interface Conventions {
|
|
|
53
53
|
strategies: {
|
|
54
54
|
/** Ordered most→least observed (ties by STRATEGY_PRIORITY). */
|
|
55
55
|
preferred: SelectorStrategy[];
|
|
56
|
-
|
|
56
|
+
/**
|
|
57
|
+
* Custom test-id attribute. Omitted from auto-discovery output; set here
|
|
58
|
+
* only when the user explicitly declares it via sela.config.ts
|
|
59
|
+
* `playwrightRules.testIdAttribute` and it is applied via
|
|
60
|
+
* `applyConventionsOverride` at heal time.
|
|
61
|
+
*/
|
|
62
|
+
testIdAttribute?: string;
|
|
57
63
|
/** Rarely-seen strategies the healer should steer away from. */
|
|
58
64
|
avoid: SelectorStrategy[];
|
|
59
65
|
};
|
|
@@ -80,7 +86,7 @@ export declare function argmax<K extends string>(counts: Record<string, number>,
|
|
|
80
86
|
export declare function round2(n: number): number;
|
|
81
87
|
export interface CompactConventions {
|
|
82
88
|
/** preferred strategies */ s: SelectorStrategy[];
|
|
83
|
-
/** testIdAttribute */ t
|
|
89
|
+
/** testIdAttribute — omitted when not set */ t?: string;
|
|
84
90
|
/** architecture mode */ a: ArchitectureMode;
|
|
85
91
|
/** selectorSource — omitted when inline/null */ src?: string;
|
|
86
92
|
/** naming case */ n: NamingCase;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Conventions.d.ts","sourceRoot":"","sources":["../../src/config/Conventions.ts"],"names":[],"mappings":"AAuBA,sDAAsD;AACtD,MAAM,MAAM,gBAAgB,GAAG,QAAQ,GAAG,MAAM,GAAG,MAAM,GAAG,KAAK,GAAG,OAAO,CAAC;AAE5E,yDAAyD;AACzD,MAAM,MAAM,gBAAgB,GAAG,QAAQ,GAAG,UAAU,GAAG,YAAY,GAAG,OAAO,CAAC;AAE9E,0EAA0E;AAC1E,MAAM,MAAM,UAAU,GAClB,kBAAkB,GAClB,WAAW,GACX,YAAY,GACZ,OAAO,CAAC;AAKZ,eAAO,MAAM,iBAAiB,EAAE,SAAS,gBAAgB,EAMxD,CAAC;AACF,eAAO,MAAM,aAAa,EAAE,SAAS,OAAO,CAAC,gBAAgB,EAAE,OAAO,CAAC,EAItE,CAAC;AACF,eAAO,MAAM,aAAa,EAAE,SAAS,OAAO,CAAC,UAAU,EAAE,OAAO,CAAC,EAIhE,CAAC;AAEF,8EAA8E;AAC9E,eAAO,MAAM,sBAAsB,MAAM,CAAC;AAC1C,4EAA4E;AAC5E,eAAO,MAAM,sBAAsB,OAAO,CAAC;AAM3C;;;;GAIG;AACH,MAAM,WAAW,SAAS;IACxB,cAAc,EAAE,MAAM,CAAC,gBAAgB,EAAE,MAAM,CAAC,CAAC;IACjD,oEAAoE;IACpE,UAAU,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,CAAC;IACrE,+CAA+C;IAC/C,UAAU,EAAE,MAAM,CAAC,OAAO,CAAC,UAAU,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,GAAG;QACzD,KAAK,EAAE,MAAM,CAAC;KACf,CAAC;IACF,oEAAoE;IACpE,kBAAkB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC3C,sEAAsE;IACtE,gBAAgB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACzC,YAAY,EAAE,MAAM,CAAC;IACrB,gBAAgB,EAAE,MAAM,CAAC;IACzB,sEAAsE;IACtE,YAAY,EAAE,MAAM,EAAE,CAAC;CACxB;AAED,wBAAgB,cAAc,IAAI,SAAS,CAW1C;AAMD,MAAM,WAAW,eAAe;IAC9B,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,gBAAgB,EAAE,MAAM,CAAC;IACzB,iDAAiD;IACjD,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,eAAe,GAAG,cAAc,CAAC;IACzC,mEAAmE;IACnE,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;CACzB;AAED,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,CAAC,CAAC;IACX,UAAU,EAAE;QACV,+DAA+D;QAC/D,SAAS,EAAE,gBAAgB,EAAE,CAAC;QAC9B,eAAe,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"Conventions.d.ts","sourceRoot":"","sources":["../../src/config/Conventions.ts"],"names":[],"mappings":"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"}
|