sela-core 1.0.6 → 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/README.md +1 -1
- 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 +3 -2
- package/dist/config/ConfigLoader.d.ts.map +1 -1
- package/dist/config/ConfigLoader.js +23 -13
- package/dist/config/DryRunGuard.d.ts +14 -0
- package/dist/config/DryRunGuard.d.ts.map +1 -0
- package/dist/config/DryRunGuard.js +79 -0
- package/dist/config/SelaConfig.d.ts +26 -16
- package/dist/config/SelaConfig.d.ts.map +1 -1
- package/dist/config/SelaConfig.js +54 -22
- package/dist/engine/HealingRegistry.d.ts +1 -1
- package/dist/engine/HealingRegistry.js +3 -3
- package/dist/engine/SelaEngine.d.ts +13 -10
- package/dist/engine/SelaEngine.d.ts.map +1 -1
- package/dist/engine/SelaEngine.js +292 -101
- package/dist/errors/SelaError.d.ts +132 -0
- package/dist/errors/SelaError.d.ts.map +1 -0
- package/dist/errors/SelaError.js +154 -0
- package/dist/fixtures/expectProxy.d.ts +1 -1
- package/dist/fixtures/expectProxy.d.ts.map +1 -1
- package/dist/fixtures/expectProxy.js +17 -10
- package/dist/fixtures/index.d.ts +7 -1
- package/dist/fixtures/index.d.ts.map +1 -1
- package/dist/fixtures/index.js +26 -11
- package/dist/fixtures/proxyTag.d.ts +12 -0
- package/dist/fixtures/proxyTag.d.ts.map +1 -0
- package/dist/fixtures/proxyTag.js +45 -0
- package/dist/index.d.ts +8 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9 -4
- package/dist/reporter/SelaReporter.d.ts +143 -0
- package/dist/reporter/SelaReporter.d.ts.map +1 -0
- package/dist/reporter/SelaReporter.js +428 -0
- package/dist/services/ASTSourceUpdater.d.ts +20 -1
- package/dist/services/ASTSourceUpdater.d.ts.map +1 -1
- package/dist/services/ASTSourceUpdater.js +257 -89
- 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 +43 -1
- package/dist/services/HealReportService.d.ts.map +1 -1
- package/dist/services/HealReportService.js +108 -25
- package/dist/services/HealingAdvisory.js +1 -1
- package/dist/services/HealingCacheService.d.ts +135 -0
- package/dist/services/HealingCacheService.d.ts.map +1 -0
- package/dist/services/HealingCacheService.js +460 -0
- 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 +20 -1
- package/dist/services/IntentAuditor.d.ts +18 -1
- package/dist/services/IntentAuditor.d.ts.map +1 -1
- package/dist/services/IntentAuditor.js +50 -26
- package/dist/services/InteractiveReview.d.ts +15 -0
- package/dist/services/InteractiveReview.d.ts.map +1 -0
- package/dist/services/InteractiveReview.js +125 -0
- package/dist/services/LLMService.d.ts +18 -2
- package/dist/services/LLMService.d.ts.map +1 -1
- package/dist/services/LLMService.js +33 -23
- package/dist/services/PRAutomationService.d.ts +14 -1
- package/dist/services/PRAutomationService.d.ts.map +1 -1
- package/dist/services/PRAutomationService.js +287 -92
- package/dist/services/PendingPromptLedger.d.ts +44 -0
- package/dist/services/PendingPromptLedger.d.ts.map +1 -0
- package/dist/services/PendingPromptLedger.js +180 -0
- package/dist/services/ReportGenerator.d.ts +70 -0
- package/dist/services/ReportGenerator.d.ts.map +1 -0
- package/dist/services/ReportGenerator.js +191 -0
- package/dist/services/SafetyGuard.d.ts +30 -7
- package/dist/services/SafetyGuard.d.ts.map +1 -1
- package/dist/services/SafetyGuard.js +85 -116
- 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 +66 -34
- package/dist/services/TemplateDiffService.d.ts.map +1 -1
- package/dist/services/TemplateDiffService.js +18 -15
- package/dist/services/WorkspaceSnapshotService.d.ts +71 -0
- package/dist/services/WorkspaceSnapshotService.d.ts.map +1 -0
- package/dist/services/WorkspaceSnapshotService.js +200 -0
- package/dist/types/index.d.ts +1 -1
- package/dist/utils/IsolatedDiff.d.ts +45 -0
- package/dist/utils/IsolatedDiff.d.ts.map +1 -0
- package/dist/utils/IsolatedDiff.js +382 -0
- package/package.json +71 -67
package/dist/index.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
// sela-core
|
|
2
|
+
// sela-core - AI self-healing Playwright wrapper
|
|
3
3
|
//
|
|
4
4
|
// Drop-in replacement for @playwright/test:
|
|
5
5
|
// import { test, expect } from 'sela-core'
|
|
6
6
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
-
exports.expect = exports.test = exports.selectors = exports.devices = exports.webkit = exports.firefox = exports.chromium = exports.defineConfig = void 0;
|
|
7
|
+
exports.SelaReporter = exports.expect = exports.test = exports.selectors = exports.devices = exports.webkit = exports.firefox = exports.chromium = exports.defineConfig = void 0;
|
|
8
8
|
// Config DSL exported FIRST so `sela.config.ts` can `import { defineConfig }`
|
|
9
9
|
// without triggering the heavier fixtures/engine graph during config load.
|
|
10
10
|
var defineConfig_js_1 = require("./config/defineConfig.js");
|
|
@@ -16,11 +16,16 @@ Object.defineProperty(exports, "firefox", { enumerable: true, get: function () {
|
|
|
16
16
|
Object.defineProperty(exports, "webkit", { enumerable: true, get: function () { return test_1.webkit; } });
|
|
17
17
|
Object.defineProperty(exports, "devices", { enumerable: true, get: function () { return test_1.devices; } });
|
|
18
18
|
Object.defineProperty(exports, "selectors", { enumerable: true, get: function () { return test_1.selectors; } });
|
|
19
|
-
// Sela's extended test
|
|
19
|
+
// Sela's extended test - includes DNA capture, trace collection,
|
|
20
20
|
// locator proxy, and fixture-scoped healing expect
|
|
21
21
|
var index_js_1 = require("./fixtures/index.js");
|
|
22
22
|
Object.defineProperty(exports, "test", { enumerable: true, get: function () { return index_js_1.test; } });
|
|
23
|
-
// Sela's healing expect
|
|
23
|
+
// Sela's healing expect - works standalone (import { expect } from 'sela-core')
|
|
24
24
|
// Detects page from locator, falls back to raw PW expect for non-locators
|
|
25
25
|
var moduleExpect_js_1 = require("./fixtures/moduleExpect.js");
|
|
26
26
|
Object.defineProperty(exports, "expect", { enumerable: true, get: function () { return moduleExpect_js_1.expect; } });
|
|
27
|
+
// Playwright Reporter - drop into `reporter: [['sela-core/reporter']]`
|
|
28
|
+
// in playwright.config.ts to drive heal-and-report at the reporter layer
|
|
29
|
+
// (safety net for worker crashes + Playwright failure screenshot capture).
|
|
30
|
+
var SelaReporter_js_1 = require("./reporter/SelaReporter.js");
|
|
31
|
+
Object.defineProperty(exports, "SelaReporter", { enumerable: true, get: function () { return SelaReporter_js_1.SelaReporter; } });
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import type { Reporter, TestCase, TestResult, FullConfig, Suite, FullResult } from "@playwright/test/reporter";
|
|
2
|
+
import { HealReportService } from "../services/HealReportService.js";
|
|
3
|
+
import type { SelaEngine } from "../engine/SelaEngine.js";
|
|
4
|
+
import { type PendingPromptEntry } from "../services/PendingPromptLedger.js";
|
|
5
|
+
import { type PrintReviewDeps } from "../services/InteractiveReview.js";
|
|
6
|
+
export interface ReporterEngine {
|
|
7
|
+
commitUpdates(): Promise<void>;
|
|
8
|
+
}
|
|
9
|
+
export interface ReporterLogger {
|
|
10
|
+
info: (msg: string) => void;
|
|
11
|
+
warn: (msg: string) => void;
|
|
12
|
+
error: (msg: string) => void;
|
|
13
|
+
}
|
|
14
|
+
export interface SelaReporterOpts {
|
|
15
|
+
/**
|
|
16
|
+
* Skip the `ANTHROPIC_API_KEY` presence check. Tests inject this so
|
|
17
|
+
* the reporter does not surface a noisy warning when no real key is
|
|
18
|
+
* required to drive the lifecycle.
|
|
19
|
+
*/
|
|
20
|
+
skipApiKeyCheck?: boolean;
|
|
21
|
+
/** Override the engine. Defaults to `sharedEngine`. */
|
|
22
|
+
engine?: ReporterEngine | SelaEngine;
|
|
23
|
+
/** Override the report buffer. Defaults to `sharedHealReport`. */
|
|
24
|
+
report?: HealReportService;
|
|
25
|
+
/** Override the logger (defaults to `console`). */
|
|
26
|
+
logger?: ReporterLogger;
|
|
27
|
+
/** Override cwd used to locate the pending-prompts ledger. Defaults to process.cwd(). */
|
|
28
|
+
cwd?: string;
|
|
29
|
+
/**
|
|
30
|
+
* Inject the ledger reader - tests stub this so they don't have to
|
|
31
|
+
* touch the real filesystem.
|
|
32
|
+
*/
|
|
33
|
+
readLedger?: (cwd: string) => PendingPromptEntry[];
|
|
34
|
+
/** Inject the ledger cleaner. */
|
|
35
|
+
clearLedger?: (cwd: string) => void;
|
|
36
|
+
/**
|
|
37
|
+
* Inject the review printer. Tests stub this to assert that the
|
|
38
|
+
* reporter wires the ledger entries + HTML report path through unchanged.
|
|
39
|
+
*/
|
|
40
|
+
printReview?: (deps: PrintReviewDeps) => void;
|
|
41
|
+
/**
|
|
42
|
+
* Override the resolution of the HTML report path. Returns `null` when
|
|
43
|
+
* no report exists. Defaults to `<cwd>/sela-report.html` when the file
|
|
44
|
+
* is on disk.
|
|
45
|
+
*/
|
|
46
|
+
resolveReportPath?: (cwd: string) => string | null;
|
|
47
|
+
/**
|
|
48
|
+
* Override the CI detection - true means "behave as CI" (skip the
|
|
49
|
+
* post-run notify). Defaults to checking `process.env.CI`.
|
|
50
|
+
*/
|
|
51
|
+
isCi?: () => boolean;
|
|
52
|
+
/**
|
|
53
|
+
* Install process-level SIGINT/SIGTERM handlers in onBegin. Defaults
|
|
54
|
+
* to true. Tests pass `false` so vitest's own signal lifecycle isn't
|
|
55
|
+
* polluted by leftover handlers between cases.
|
|
56
|
+
*/
|
|
57
|
+
installSignalHandlers?: boolean;
|
|
58
|
+
}
|
|
59
|
+
export declare class SelaReporter implements Reporter {
|
|
60
|
+
private readonly engine;
|
|
61
|
+
private readonly report;
|
|
62
|
+
private readonly logger;
|
|
63
|
+
private readonly skipApiKeyCheck;
|
|
64
|
+
private readonly cwd;
|
|
65
|
+
private readonly readLedger;
|
|
66
|
+
private readonly clearLedger;
|
|
67
|
+
private readonly printReview;
|
|
68
|
+
private readonly resolveReportPath;
|
|
69
|
+
private readonly isCi;
|
|
70
|
+
private readonly shouldInstallSignalHandlers;
|
|
71
|
+
private shutdownHandler;
|
|
72
|
+
private shuttingDown;
|
|
73
|
+
/**
|
|
74
|
+
* Screenshots that arrived before a matching FailedEvent existed in
|
|
75
|
+
* the shared report. Re-flushed once at onEnd time so worker-crash
|
|
76
|
+
* scenarios (no fixture teardown ⇒ no engine-side FailedEvent yet)
|
|
77
|
+
* still get their visual context attached.
|
|
78
|
+
*
|
|
79
|
+
* Key shape: `${normalisedSourceFile}::${testTitle}`.
|
|
80
|
+
*/
|
|
81
|
+
private readonly pendingScreenshots;
|
|
82
|
+
/**
|
|
83
|
+
* Mirror of the failures the reporter observed, kept so external
|
|
84
|
+
* consumers (and tests) can audit what the reporter saw without
|
|
85
|
+
* having to subscribe to the entire Playwright Reporter API.
|
|
86
|
+
*/
|
|
87
|
+
private readonly failures;
|
|
88
|
+
constructor(opts?: SelaReporterOpts);
|
|
89
|
+
onBegin(_config?: FullConfig, _suite?: Suite): void;
|
|
90
|
+
onTestEnd(test: TestCase, result: TestResult): void;
|
|
91
|
+
onEnd(_result?: FullResult): Promise<void>;
|
|
92
|
+
/** Reporter prints nothing of its own - Playwright stdio stays clean. */
|
|
93
|
+
printsToStdio(): boolean;
|
|
94
|
+
/**
|
|
95
|
+
* Install the SIGINT/SIGTERM listener once per reporter. First signal
|
|
96
|
+
* triggers an orderly drain; second signal hard-exits with code 130
|
|
97
|
+
* (SIGINT) / 143 (SIGTERM) so the user can always escape if the drain
|
|
98
|
+
* itself wedges (e.g. a stuck LLM call).
|
|
99
|
+
*/
|
|
100
|
+
private installShutdownHandler;
|
|
101
|
+
private uninstallShutdownHandler;
|
|
102
|
+
/**
|
|
103
|
+
* Best-effort drain on signal. Mirrors the orderly onEnd path but
|
|
104
|
+
* swallows every error - we never want to throw inside a signal
|
|
105
|
+
* handler because Node prints raw stack traces otherwise.
|
|
106
|
+
*
|
|
107
|
+
* Exposed for tests so they can drive the shutdown without raising
|
|
108
|
+
* a real signal in the vitest process.
|
|
109
|
+
*/
|
|
110
|
+
gracefulShutdown(): Promise<void>;
|
|
111
|
+
/**
|
|
112
|
+
* Walk every buffered pending screenshot and try to attach it to a
|
|
113
|
+
* now-existing FailedEvent. Successfully-attached entries are
|
|
114
|
+
* removed; unmatched entries are dropped (the matching FailedEvent
|
|
115
|
+
* never arrived - most likely worker crashed before the engine
|
|
116
|
+
* could record one).
|
|
117
|
+
*
|
|
118
|
+
* Returns the number of FailedEvents touched.
|
|
119
|
+
*/
|
|
120
|
+
flushPendingScreenshots(): number;
|
|
121
|
+
/** Test hook - read-only snapshot of recorded failures. */
|
|
122
|
+
getRecordedFailures(): ReadonlyArray<{
|
|
123
|
+
sourceFile: string;
|
|
124
|
+
sourceLine?: number;
|
|
125
|
+
testTitle: string;
|
|
126
|
+
status: string;
|
|
127
|
+
hasScreenshot: boolean;
|
|
128
|
+
}>;
|
|
129
|
+
/** Test hook - number of screenshots still waiting for a FailedEvent. */
|
|
130
|
+
getPendingScreenshotCount(): number;
|
|
131
|
+
private pendingKey;
|
|
132
|
+
private normalisePath;
|
|
133
|
+
/**
|
|
134
|
+
* Convert the first PNG attachment on a TestResult into a Base64
|
|
135
|
+
* string immediately. Prefers an in-memory `body` when Playwright
|
|
136
|
+
* already inlined the bytes (faster, no IO); falls back to reading
|
|
137
|
+
* the attachment `path` synchronously BEFORE Playwright has a chance
|
|
138
|
+
* to clean up its temp folder.
|
|
139
|
+
*/
|
|
140
|
+
private extractScreenshotBase64;
|
|
141
|
+
}
|
|
142
|
+
export default SelaReporter;
|
|
143
|
+
//# sourceMappingURL=SelaReporter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SelaReporter.d.ts","sourceRoot":"","sources":["../../src/reporter/SelaReporter.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,428 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// src/reporter/SelaReporter.ts
|
|
3
|
+
//
|
|
4
|
+
// SelaReporter - Playwright Reporter implementation that drives the
|
|
5
|
+
// Sela heal-and-report pipeline at the reporter layer instead of the
|
|
6
|
+
// fixture teardown layer. The fixture path remains the primary trigger
|
|
7
|
+
// for in-test heals; this reporter is the SAFETY NET for worker crashes,
|
|
8
|
+
// timeouts, and any failure mode that skips fixture teardown.
|
|
9
|
+
//
|
|
10
|
+
// Responsibilities:
|
|
11
|
+
// 1. onBegin - flips SELA_REPORTER_ACTIVE, installs SIGINT/SIGTERM
|
|
12
|
+
// graceful-shutdown handlers.
|
|
13
|
+
// 2. onTestEnd - extracts the first PNG screenshot from
|
|
14
|
+
// TestResult.attachments and converts it to Base64 IMMEDIATELY.
|
|
15
|
+
// Playwright's temp folder is cleaned up between runs, so the
|
|
16
|
+
// bytes MUST be captured into memory before the reporter loop ends.
|
|
17
|
+
// 3. onEnd - checks for ANTHROPIC_API_KEY, flushes pending screenshots,
|
|
18
|
+
// runs SelaEngine.commitUpdates(), then prints a *non-blocking*
|
|
19
|
+
// write-and-notify summary (unified diff per healed file +
|
|
20
|
+
// vscode://file deeplink + sela-report.html deeplink). MVP flow -
|
|
21
|
+
// no Y/N prompt, no rollback. Mutations are already on disk by the
|
|
22
|
+
// time we reach this hook.
|
|
23
|
+
// 4. SIGINT / SIGTERM - runs gracefulShutdown() once, second signal
|
|
24
|
+
// hard-exits. Prevents stranded child processes from showing raw
|
|
25
|
+
// Node unhandledRejection stack traces on Ctrl+C.
|
|
26
|
+
//
|
|
27
|
+
// Clean Architecture: zero business logic - IO + aggregation only.
|
|
28
|
+
// Heal decisions live in SelaEngine, validation in ReportGenerator,
|
|
29
|
+
// rendering in HealReportService.
|
|
30
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
31
|
+
if (k2 === undefined) k2 = k;
|
|
32
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
33
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
34
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
35
|
+
}
|
|
36
|
+
Object.defineProperty(o, k2, desc);
|
|
37
|
+
}) : (function(o, m, k, k2) {
|
|
38
|
+
if (k2 === undefined) k2 = k;
|
|
39
|
+
o[k2] = m[k];
|
|
40
|
+
}));
|
|
41
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
42
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
43
|
+
}) : function(o, v) {
|
|
44
|
+
o["default"] = v;
|
|
45
|
+
});
|
|
46
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
47
|
+
var ownKeys = function(o) {
|
|
48
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
49
|
+
var ar = [];
|
|
50
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
51
|
+
return ar;
|
|
52
|
+
};
|
|
53
|
+
return ownKeys(o);
|
|
54
|
+
};
|
|
55
|
+
return function (mod) {
|
|
56
|
+
if (mod && mod.__esModule) return mod;
|
|
57
|
+
var result = {};
|
|
58
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
59
|
+
__setModuleDefault(result, mod);
|
|
60
|
+
return result;
|
|
61
|
+
};
|
|
62
|
+
})();
|
|
63
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
64
|
+
exports.SelaReporter = void 0;
|
|
65
|
+
const fs = __importStar(require("fs"));
|
|
66
|
+
const path = __importStar(require("path"));
|
|
67
|
+
const HealReportService_js_1 = require("../services/HealReportService.js");
|
|
68
|
+
const singleton_js_1 = require("../engine/singleton.js");
|
|
69
|
+
const SelaError_js_1 = require("../errors/SelaError.js");
|
|
70
|
+
const PendingPromptLedger_js_1 = require("../services/PendingPromptLedger.js");
|
|
71
|
+
const InteractiveReview_js_1 = require("../services/InteractiveReview.js");
|
|
72
|
+
// ───────────────────────────────────────────────────────────────────
|
|
73
|
+
// SelaReporter
|
|
74
|
+
// ───────────────────────────────────────────────────────────────────
|
|
75
|
+
class SelaReporter {
|
|
76
|
+
engine;
|
|
77
|
+
report;
|
|
78
|
+
logger;
|
|
79
|
+
skipApiKeyCheck;
|
|
80
|
+
cwd;
|
|
81
|
+
readLedger;
|
|
82
|
+
clearLedger;
|
|
83
|
+
printReview;
|
|
84
|
+
resolveReportPath;
|
|
85
|
+
isCi;
|
|
86
|
+
shouldInstallSignalHandlers;
|
|
87
|
+
shutdownHandler = null;
|
|
88
|
+
shuttingDown = false;
|
|
89
|
+
/**
|
|
90
|
+
* Screenshots that arrived before a matching FailedEvent existed in
|
|
91
|
+
* the shared report. Re-flushed once at onEnd time so worker-crash
|
|
92
|
+
* scenarios (no fixture teardown ⇒ no engine-side FailedEvent yet)
|
|
93
|
+
* still get their visual context attached.
|
|
94
|
+
*
|
|
95
|
+
* Key shape: `${normalisedSourceFile}::${testTitle}`.
|
|
96
|
+
*/
|
|
97
|
+
pendingScreenshots = new Map();
|
|
98
|
+
/**
|
|
99
|
+
* Mirror of the failures the reporter observed, kept so external
|
|
100
|
+
* consumers (and tests) can audit what the reporter saw without
|
|
101
|
+
* having to subscribe to the entire Playwright Reporter API.
|
|
102
|
+
*/
|
|
103
|
+
failures = [];
|
|
104
|
+
constructor(opts = {}) {
|
|
105
|
+
this.engine = (opts.engine ?? singleton_js_1.sharedEngine);
|
|
106
|
+
this.report = opts.report ?? HealReportService_js_1.sharedHealReport;
|
|
107
|
+
this.skipApiKeyCheck = opts.skipApiKeyCheck ?? false;
|
|
108
|
+
this.logger = opts.logger ?? {
|
|
109
|
+
info: (m) => console.log(m),
|
|
110
|
+
warn: (m) => console.warn(m),
|
|
111
|
+
error: (m) => console.error(m),
|
|
112
|
+
};
|
|
113
|
+
this.cwd = opts.cwd ?? process.cwd();
|
|
114
|
+
this.readLedger = opts.readLedger ?? PendingPromptLedger_js_1.readPendingPromptsLedger;
|
|
115
|
+
this.clearLedger = opts.clearLedger ?? PendingPromptLedger_js_1.clearPendingPromptsLedger;
|
|
116
|
+
this.printReview = opts.printReview ?? InteractiveReview_js_1.printHealReview;
|
|
117
|
+
this.resolveReportPath = opts.resolveReportPath ?? defaultResolveReportPath;
|
|
118
|
+
this.isCi = opts.isCi ?? defaultIsCi;
|
|
119
|
+
this.shouldInstallSignalHandlers = opts.installSignalHandlers ?? true;
|
|
120
|
+
}
|
|
121
|
+
// ─────────────────────────────────────────────────────────────
|
|
122
|
+
// Playwright Reporter API
|
|
123
|
+
// ─────────────────────────────────────────────────────────────
|
|
124
|
+
onBegin(_config, _suite) {
|
|
125
|
+
// Signal to future fixture-aware code that a reporter is driving
|
|
126
|
+
// finalisation. Today the fixture still calls engine.commitUpdates()
|
|
127
|
+
// itself; the env flag is reserved for the planned dedup guard.
|
|
128
|
+
process.env.SELA_REPORTER_ACTIVE = "1";
|
|
129
|
+
if (this.shouldInstallSignalHandlers) {
|
|
130
|
+
this.installShutdownHandler();
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
onTestEnd(test, result) {
|
|
134
|
+
if (result.status !== "failed" && result.status !== "timedOut")
|
|
135
|
+
return;
|
|
136
|
+
const sourceFile = test.location?.file ?? "";
|
|
137
|
+
const sourceLine = test.location?.line;
|
|
138
|
+
const testTitle = test.title;
|
|
139
|
+
const base64Png = this.extractScreenshotBase64(result);
|
|
140
|
+
if (base64Png) {
|
|
141
|
+
// First try to attach immediately - the engine may have already
|
|
142
|
+
// recorded the FailedEvent during fixture teardown for in-test
|
|
143
|
+
// heal failures. This is the happy path.
|
|
144
|
+
const matched = this.report.attachFailureScreenshot({ sourceFile: this.normalisePath(sourceFile), testTitle }, base64Png);
|
|
145
|
+
if (matched === 0) {
|
|
146
|
+
// No matching FailedEvent yet - buffer for the onEnd retry. This
|
|
147
|
+
// covers worker-crash and fixture-skip cases where the engine
|
|
148
|
+
// never got a chance to record the FailedEvent in-flight.
|
|
149
|
+
const key = this.pendingKey(sourceFile, testTitle);
|
|
150
|
+
this.pendingScreenshots.set(key, base64Png);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
this.failures.push({
|
|
154
|
+
sourceFile,
|
|
155
|
+
sourceLine,
|
|
156
|
+
testTitle,
|
|
157
|
+
status: result.status,
|
|
158
|
+
hasScreenshot: !!base64Png,
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
async onEnd(_result) {
|
|
162
|
+
// Detach signal handlers first - a natural onEnd is the orderly path,
|
|
163
|
+
// so we don't want a late SIGINT to double-invoke shutdown logic.
|
|
164
|
+
this.uninstallShutdownHandler();
|
|
165
|
+
if (!this.skipApiKeyCheck && !process.env.ANTHROPIC_API_KEY) {
|
|
166
|
+
const err = new SelaError_js_1.SelaError({
|
|
167
|
+
subsystem: "LLMService",
|
|
168
|
+
code: SelaError_js_1.LLM_SERVICE_CODES.NO_API_KEY,
|
|
169
|
+
reason: "ANTHROPIC_API_KEY not set - SelaReporter cannot drive the heal pipeline. " +
|
|
170
|
+
"Set the env var (or remove the reporter entry from playwright.config.ts).",
|
|
171
|
+
});
|
|
172
|
+
// Graceful degradation: surface the error and continue flushing
|
|
173
|
+
// any report content the fixture path already produced.
|
|
174
|
+
this.logger.warn(err.message);
|
|
175
|
+
}
|
|
176
|
+
// Late-bind any screenshots that arrived before their FailedEvents.
|
|
177
|
+
this.flushPendingScreenshots();
|
|
178
|
+
try {
|
|
179
|
+
await this.engine.commitUpdates();
|
|
180
|
+
}
|
|
181
|
+
catch (err) {
|
|
182
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
183
|
+
this.logger.error(`[Sela] reporter commitUpdates failed: ${msg}`);
|
|
184
|
+
}
|
|
185
|
+
// ── Local DX - Write-and-notify summary ─────────────────────
|
|
186
|
+
//
|
|
187
|
+
// Workers serialise their mutated FileSnapshots to
|
|
188
|
+
// `<cwd>/.sela/pending-prompts/*.json`. The reporter drains that
|
|
189
|
+
// ledger here and prints a non-blocking summary (diff + vscode
|
|
190
|
+
// deeplink + HTML report deeplink). No Y/N prompt - the files are
|
|
191
|
+
// already mutated on disk by the time we get here.
|
|
192
|
+
//
|
|
193
|
+
// CI runs skip the notify entirely - PR automation already handled
|
|
194
|
+
// the mutations on its isolated branch.
|
|
195
|
+
if (!this.isCi()) {
|
|
196
|
+
try {
|
|
197
|
+
const entries = this.readLedger(this.cwd);
|
|
198
|
+
const reportHtmlPath = this.resolveReportPath(this.cwd);
|
|
199
|
+
if (entries.length > 0 || reportHtmlPath) {
|
|
200
|
+
this.printReview({ entries, reportHtmlPath });
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
catch (err) {
|
|
204
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
205
|
+
this.logger.error(`[Sela] review summary failed: ${msg}`);
|
|
206
|
+
}
|
|
207
|
+
finally {
|
|
208
|
+
try {
|
|
209
|
+
this.clearLedger(this.cwd);
|
|
210
|
+
}
|
|
211
|
+
catch {
|
|
212
|
+
// Cleanup failures are non-fatal - the next run will overwrite.
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
else {
|
|
217
|
+
// CI path: drop the ledger so a follow-up local run doesn't replay
|
|
218
|
+
// mutations from a previous CI session.
|
|
219
|
+
try {
|
|
220
|
+
this.clearLedger(this.cwd);
|
|
221
|
+
}
|
|
222
|
+
catch {
|
|
223
|
+
// ignore
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
/** Reporter prints nothing of its own - Playwright stdio stays clean. */
|
|
228
|
+
printsToStdio() {
|
|
229
|
+
return false;
|
|
230
|
+
}
|
|
231
|
+
// ─────────────────────────────────────────────────────────────
|
|
232
|
+
// Graceful shutdown - SIGINT / SIGTERM
|
|
233
|
+
// ─────────────────────────────────────────────────────────────
|
|
234
|
+
/**
|
|
235
|
+
* Install the SIGINT/SIGTERM listener once per reporter. First signal
|
|
236
|
+
* triggers an orderly drain; second signal hard-exits with code 130
|
|
237
|
+
* (SIGINT) / 143 (SIGTERM) so the user can always escape if the drain
|
|
238
|
+
* itself wedges (e.g. a stuck LLM call).
|
|
239
|
+
*/
|
|
240
|
+
installShutdownHandler() {
|
|
241
|
+
if (this.shutdownHandler)
|
|
242
|
+
return;
|
|
243
|
+
const handler = (signal) => {
|
|
244
|
+
if (this.shuttingDown) {
|
|
245
|
+
process.stderr.write(`\n[Sela] ⏹ Force exit on second ${signal}.\n`);
|
|
246
|
+
process.exit(signal === "SIGTERM" ? 143 : 130);
|
|
247
|
+
return;
|
|
248
|
+
}
|
|
249
|
+
this.shuttingDown = true;
|
|
250
|
+
process.stderr.write(`\n[Sela] 🛑 ${signal} received - flushing state and shutting down...\n`);
|
|
251
|
+
this.gracefulShutdown()
|
|
252
|
+
.catch((err) => {
|
|
253
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
254
|
+
process.stderr.write(`[Sela] Shutdown error: ${msg}\n`);
|
|
255
|
+
})
|
|
256
|
+
.finally(() => {
|
|
257
|
+
process.stderr.write(`[Sela] 👋 Goodbye.\n`);
|
|
258
|
+
process.exit(signal === "SIGTERM" ? 143 : 130);
|
|
259
|
+
});
|
|
260
|
+
};
|
|
261
|
+
this.shutdownHandler = handler;
|
|
262
|
+
process.on("SIGINT", handler);
|
|
263
|
+
process.on("SIGTERM", handler);
|
|
264
|
+
}
|
|
265
|
+
uninstallShutdownHandler() {
|
|
266
|
+
if (!this.shutdownHandler)
|
|
267
|
+
return;
|
|
268
|
+
process.off("SIGINT", this.shutdownHandler);
|
|
269
|
+
process.off("SIGTERM", this.shutdownHandler);
|
|
270
|
+
this.shutdownHandler = null;
|
|
271
|
+
}
|
|
272
|
+
/**
|
|
273
|
+
* Best-effort drain on signal. Mirrors the orderly onEnd path but
|
|
274
|
+
* swallows every error - we never want to throw inside a signal
|
|
275
|
+
* handler because Node prints raw stack traces otherwise.
|
|
276
|
+
*
|
|
277
|
+
* Exposed for tests so they can drive the shutdown without raising
|
|
278
|
+
* a real signal in the vitest process.
|
|
279
|
+
*/
|
|
280
|
+
async gracefulShutdown() {
|
|
281
|
+
try {
|
|
282
|
+
this.flushPendingScreenshots();
|
|
283
|
+
}
|
|
284
|
+
catch {
|
|
285
|
+
// ignore
|
|
286
|
+
}
|
|
287
|
+
try {
|
|
288
|
+
await this.engine.commitUpdates();
|
|
289
|
+
}
|
|
290
|
+
catch {
|
|
291
|
+
// ignore
|
|
292
|
+
}
|
|
293
|
+
try {
|
|
294
|
+
const entries = this.isCi() ? [] : this.readLedger(this.cwd);
|
|
295
|
+
const reportHtmlPath = this.resolveReportPath(this.cwd);
|
|
296
|
+
if (entries.length > 0 || reportHtmlPath) {
|
|
297
|
+
this.printReview({ entries, reportHtmlPath });
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
catch {
|
|
301
|
+
// ignore
|
|
302
|
+
}
|
|
303
|
+
try {
|
|
304
|
+
this.clearLedger(this.cwd);
|
|
305
|
+
}
|
|
306
|
+
catch {
|
|
307
|
+
// ignore
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
// ─────────────────────────────────────────────────────────────
|
|
311
|
+
// Internals (exposed for testing - prefixed underscore is the
|
|
312
|
+
// convention used elsewhere in the codebase for proxy back-doors).
|
|
313
|
+
// ─────────────────────────────────────────────────────────────
|
|
314
|
+
/**
|
|
315
|
+
* Walk every buffered pending screenshot and try to attach it to a
|
|
316
|
+
* now-existing FailedEvent. Successfully-attached entries are
|
|
317
|
+
* removed; unmatched entries are dropped (the matching FailedEvent
|
|
318
|
+
* never arrived - most likely worker crashed before the engine
|
|
319
|
+
* could record one).
|
|
320
|
+
*
|
|
321
|
+
* Returns the number of FailedEvents touched.
|
|
322
|
+
*/
|
|
323
|
+
flushPendingScreenshots() {
|
|
324
|
+
let total = 0;
|
|
325
|
+
for (const [key, base64] of Array.from(this.pendingScreenshots.entries())) {
|
|
326
|
+
const sepIndex = key.indexOf("::");
|
|
327
|
+
if (sepIndex < 0) {
|
|
328
|
+
this.pendingScreenshots.delete(key);
|
|
329
|
+
continue;
|
|
330
|
+
}
|
|
331
|
+
const filePart = key.slice(0, sepIndex);
|
|
332
|
+
const titlePart = key.slice(sepIndex + 2);
|
|
333
|
+
const matched = this.report.attachFailureScreenshot({ sourceFile: filePart, testTitle: titlePart }, base64);
|
|
334
|
+
if (matched > 0) {
|
|
335
|
+
total += matched;
|
|
336
|
+
this.pendingScreenshots.delete(key);
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
return total;
|
|
340
|
+
}
|
|
341
|
+
/** Test hook - read-only snapshot of recorded failures. */
|
|
342
|
+
getRecordedFailures() {
|
|
343
|
+
return this.failures.slice();
|
|
344
|
+
}
|
|
345
|
+
/** Test hook - number of screenshots still waiting for a FailedEvent. */
|
|
346
|
+
getPendingScreenshotCount() {
|
|
347
|
+
return this.pendingScreenshots.size;
|
|
348
|
+
}
|
|
349
|
+
// ─────────────────────────────────────────────────────────────
|
|
350
|
+
// Private helpers
|
|
351
|
+
// ─────────────────────────────────────────────────────────────
|
|
352
|
+
pendingKey(sourceFile, testTitle) {
|
|
353
|
+
return `${this.normalisePath(sourceFile)}::${testTitle}`;
|
|
354
|
+
}
|
|
355
|
+
normalisePath(p) {
|
|
356
|
+
if (!p)
|
|
357
|
+
return "";
|
|
358
|
+
try {
|
|
359
|
+
const abs = path.isAbsolute(p) ? p : path.resolve(process.cwd(), p);
|
|
360
|
+
const rel = path.relative(process.cwd(), abs);
|
|
361
|
+
return (rel || abs).split("\\").join("/");
|
|
362
|
+
}
|
|
363
|
+
catch {
|
|
364
|
+
return p.split("\\").join("/");
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
/**
|
|
368
|
+
* Convert the first PNG attachment on a TestResult into a Base64
|
|
369
|
+
* string immediately. Prefers an in-memory `body` when Playwright
|
|
370
|
+
* already inlined the bytes (faster, no IO); falls back to reading
|
|
371
|
+
* the attachment `path` synchronously BEFORE Playwright has a chance
|
|
372
|
+
* to clean up its temp folder.
|
|
373
|
+
*/
|
|
374
|
+
extractScreenshotBase64(result) {
|
|
375
|
+
const attachments = result.attachments ?? [];
|
|
376
|
+
for (const att of attachments) {
|
|
377
|
+
const contentType = (att.contentType ?? "").toLowerCase();
|
|
378
|
+
const looksLikePng = contentType.includes("png") ||
|
|
379
|
+
(att.name ?? "").toLowerCase().includes("screenshot") ||
|
|
380
|
+
(att.path ?? "").toLowerCase().endsWith(".png");
|
|
381
|
+
if (!looksLikePng)
|
|
382
|
+
continue;
|
|
383
|
+
// Playwright >= 1.46 sometimes inlines the body for small images.
|
|
384
|
+
const inline = att.body;
|
|
385
|
+
if (inline && inline.length > 0) {
|
|
386
|
+
return Buffer.isBuffer(inline)
|
|
387
|
+
? inline.toString("base64")
|
|
388
|
+
: Buffer.from(inline).toString("base64");
|
|
389
|
+
}
|
|
390
|
+
if (att.path) {
|
|
391
|
+
try {
|
|
392
|
+
const bytes = fs.readFileSync(att.path);
|
|
393
|
+
return bytes.toString("base64");
|
|
394
|
+
}
|
|
395
|
+
catch {
|
|
396
|
+
// Temp file already gone - nothing we can do.
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
return undefined;
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
exports.SelaReporter = SelaReporter;
|
|
404
|
+
/**
|
|
405
|
+
* Mirror of the CI detection used by `SelaEngine.commitUpdates()`.
|
|
406
|
+
* Accepts the standard `CI=1`/`CI=true` convention; everything else
|
|
407
|
+
* (empty string, undefined, `0`, `false`) is Local DX mode.
|
|
408
|
+
*/
|
|
409
|
+
function defaultIsCi() {
|
|
410
|
+
const raw = process.env.CI;
|
|
411
|
+
if (raw === undefined || raw === null)
|
|
412
|
+
return false;
|
|
413
|
+
const v = String(raw).trim().toLowerCase();
|
|
414
|
+
return v !== "" && v !== "0" && v !== "false";
|
|
415
|
+
}
|
|
416
|
+
/**
|
|
417
|
+
* Default HTML report locator. Returns the absolute path when
|
|
418
|
+
* `<cwd>/sela-report.html` exists on disk after `commitUpdates()` has
|
|
419
|
+
* had its chance to flush, otherwise null so the printer can skip the
|
|
420
|
+
* "Insights report:" footer entirely.
|
|
421
|
+
*/
|
|
422
|
+
function defaultResolveReportPath(cwd) {
|
|
423
|
+
const candidate = path.join(cwd, "sela-report.html");
|
|
424
|
+
return fs.existsSync(candidate) ? candidate : null;
|
|
425
|
+
}
|
|
426
|
+
// Default export so Playwright can resolve `reporter: [['sela-core/reporter']]`
|
|
427
|
+
// without needing the consumer to spell out the named import.
|
|
428
|
+
exports.default = SelaReporter;
|
|
@@ -25,6 +25,25 @@ export declare class ASTSourceUpdater {
|
|
|
25
25
|
private project;
|
|
26
26
|
readonly advisoryBuffer: AdvisoryBuffer;
|
|
27
27
|
flushAdvisories(): void;
|
|
28
|
+
/**
|
|
29
|
+
* Centralised write hook for every ts-morph `saveSync` call in this class.
|
|
30
|
+
* When `SELA_DRY_RUN` is active the AST mutation stays in memory but the
|
|
31
|
+
* on-disk file is left untouched - the heal pipeline can still compute a
|
|
32
|
+
* candidate diff via `sourceFile.getFullText()` and the report layer
|
|
33
|
+
* tags the event with `dryRun: true`.
|
|
34
|
+
*/
|
|
35
|
+
private _persistChanges;
|
|
36
|
+
scanFailFastDirective(caller: CallerContext): {
|
|
37
|
+
failFast: boolean;
|
|
38
|
+
matchedComment?: string;
|
|
39
|
+
matchedLine?: number;
|
|
40
|
+
};
|
|
41
|
+
/**
|
|
42
|
+
* Convenience helper - runs `scanFailFastDirective` and throws the
|
|
43
|
+
* canonical SelaError when the directive is present. Used by SelaEngine
|
|
44
|
+
* as the very first gate inside `heal()`.
|
|
45
|
+
*/
|
|
46
|
+
enforceFailFastDirective(caller: CallerContext): void;
|
|
28
47
|
constructor();
|
|
29
48
|
private readonly WRAPPER_IDENTIFIERS;
|
|
30
49
|
private readonly STRUCTURAL_ROLES;
|
|
@@ -48,7 +67,7 @@ export declare class ASTSourceUpdater {
|
|
|
48
67
|
/**
|
|
49
68
|
* Guard: returns false when replacing oldNode with proposedNewText would
|
|
50
69
|
* substitute a hardcoded string literal for an Identifier or
|
|
51
|
-
* PropertyAccessExpression
|
|
70
|
+
* PropertyAccessExpression - which is the definition-site mutation bug.
|
|
52
71
|
* All other replacements are allowed.
|
|
53
72
|
*/
|
|
54
73
|
private static isSafeReplacement;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ASTSourceUpdater.d.ts","sourceRoot":"","sources":["../../src/services/ASTSourceUpdater.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"ASTSourceUpdater.d.ts","sourceRoot":"","sources":["../../src/services/ASTSourceUpdater.ts"],"names":[],"mappings":"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"}
|