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
package/dist/errors/SelaError.js
CHANGED
|
@@ -17,7 +17,7 @@ exports.CHAIN_VALIDATOR_CODES = exports.REPORT_CODES = exports.REGISTRY_CODES =
|
|
|
17
17
|
exports.isSelaError = isSelaError;
|
|
18
18
|
exports.isSelaErrorFrom = isSelaErrorFrom;
|
|
19
19
|
// ───────────────────────────────────────────────────────────────────
|
|
20
|
-
// SelaError
|
|
20
|
+
// SelaError - single class for the whole hierarchy.
|
|
21
21
|
//
|
|
22
22
|
// We intentionally avoid a tree of subclasses; `instanceof SelaError` +
|
|
23
23
|
// `err.subsystem === "X"` is more flexible for callers and keeps the
|
|
@@ -31,7 +31,7 @@ class SelaError extends Error {
|
|
|
31
31
|
context;
|
|
32
32
|
constructor(init) {
|
|
33
33
|
if (!CODE_PATTERN.test(init.code)) {
|
|
34
|
-
throw new Error(`[SelaError] invalid code "${init.code}"
|
|
34
|
+
throw new Error(`[SelaError] invalid code "${init.code}" - must match ${CODE_PATTERN}`);
|
|
35
35
|
}
|
|
36
36
|
const formatted = SelaError.formatMessage(init.subsystem, init.code, init.reason);
|
|
37
37
|
super(formatted);
|
|
@@ -45,9 +45,9 @@ class SelaError extends Error {
|
|
|
45
45
|
}
|
|
46
46
|
// Maintain prototype chain through transpilation (ES5 target).
|
|
47
47
|
Object.setPrototypeOf(this, new.target.prototype);
|
|
48
|
-
if (typeof Error.captureStackTrace ===
|
|
49
|
-
|
|
50
|
-
|
|
48
|
+
if (typeof Error.captureStackTrace ===
|
|
49
|
+
"function") {
|
|
50
|
+
Error.captureStackTrace(this, new.target);
|
|
51
51
|
}
|
|
52
52
|
}
|
|
53
53
|
static formatMessage(subsystem, code, reason) {
|
|
@@ -81,7 +81,7 @@ function isSelaErrorFrom(err, subsystem) {
|
|
|
81
81
|
// ───────────────────────────────────────────────────────────────────
|
|
82
82
|
// Canonical code catalogue
|
|
83
83
|
//
|
|
84
|
-
// Tests assert against these constants
|
|
84
|
+
// Tests assert against these constants - never hand-write code strings
|
|
85
85
|
// at throw sites. Adding a new code requires adding it here first so
|
|
86
86
|
// the catalogue stays in sync with the error-indicativity contract.
|
|
87
87
|
// ───────────────────────────────────────────────────────────────────
|
|
@@ -94,7 +94,6 @@ exports.SAFETY_GUARD_CODES = {
|
|
|
94
94
|
BLOCKED_ROLE_SWAP_ASSERTION: "SG_BLOCKED_ROLE_SWAP_ASSERTION",
|
|
95
95
|
FAIL_HARD_AUDITOR_INCONSISTENT: "SG_FAIL_HARD_AUDITOR_INCONSISTENT",
|
|
96
96
|
BLOCKED_AUDITOR_SUSPICIOUS: "SG_BLOCKED_AUDITOR_SUSPICIOUS",
|
|
97
|
-
BLOCKED_SIMILARITY_FLOOR: "SG_BLOCKED_SIMILARITY_FLOOR",
|
|
98
97
|
/** Generic dispatcher when SafetyDecision arrives without an explicit code. */
|
|
99
98
|
GENERIC_REJECTION: "SG_GENERIC_REJECTION",
|
|
100
99
|
};
|
|
@@ -145,6 +144,14 @@ exports.REPORT_CODES = {
|
|
|
145
144
|
INVALID_FIELD_TYPE: "REP_INVALID_FIELD_TYPE",
|
|
146
145
|
/** Disk write failed during flushToDisk(). */
|
|
147
146
|
FLUSH_FAILED: "REP_FLUSH_FAILED",
|
|
147
|
+
/**
|
|
148
|
+
* A prior `.sela-history.json` existed on disk but could not be parsed as
|
|
149
|
+
* a JSON array. The merge path refuses to silently overwrite it with a
|
|
150
|
+
* fresh window (which would destroy the rolling history), so it aborts.
|
|
151
|
+
*/
|
|
152
|
+
HISTORY_CORRUPT: "REP_HISTORY_CORRUPT",
|
|
153
|
+
/** A shard report file existed but was not valid JSON / had no events array. */
|
|
154
|
+
SHARD_UNREADABLE: "REP_SHARD_UNREADABLE",
|
|
148
155
|
};
|
|
149
156
|
exports.CHAIN_VALIDATOR_CODES = {
|
|
150
157
|
TYPE_FLOW: "CV_TYPE_FLOW",
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { Locator } from "@playwright/test";
|
|
2
2
|
import { SelaEngine } from "../engine/SelaEngine";
|
|
3
3
|
/**
|
|
4
|
-
* createHealingExpect
|
|
4
|
+
* createHealingExpect - drop-in replacement for Playwright's expect().
|
|
5
5
|
*
|
|
6
|
-
* @param resolveProxy Optional callback that unwraps a
|
|
6
|
+
* @param resolveProxy Optional callback that unwraps a Sela proxy
|
|
7
7
|
* to its current live Locator. Supplied by
|
|
8
8
|
* fixtures/index.ts which owns proxyToActiveLocator.
|
|
9
9
|
*/
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
// src/fixtures/expectProxy.ts
|
|
2
|
+
// src/fixtures/expectProxy.ts - full replacement
|
|
3
3
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
4
|
exports.createHealingExpect = createHealingExpect;
|
|
5
5
|
const test_1 = require("@playwright/test");
|
|
@@ -17,8 +17,8 @@ function isElementNotFoundError(err) {
|
|
|
17
17
|
return ELEMENT_NOT_FOUND_PATTERNS.some((p) => p.test(err.message));
|
|
18
18
|
}
|
|
19
19
|
/**
|
|
20
|
-
* Returns true only for genuine Playwright Locator instances
|
|
21
|
-
* not FrameLocator proxies or
|
|
20
|
+
* Returns true only for genuine Playwright Locator instances -
|
|
21
|
+
* not FrameLocator proxies or Sela proxy objects.
|
|
22
22
|
*
|
|
23
23
|
* A real Locator has _frame internally.
|
|
24
24
|
* A FrameLocator has _frameSelector instead.
|
|
@@ -30,10 +30,10 @@ function isRealLocator(value) {
|
|
|
30
30
|
return false;
|
|
31
31
|
if (typeof value !== "object")
|
|
32
32
|
return false;
|
|
33
|
-
//
|
|
34
|
-
// `_frame` via Reflect.get so `hasFrame` would lie
|
|
33
|
+
// Sela locator proxies expose `_selaSelector`. The proxy forwards
|
|
34
|
+
// `_frame` via Reflect.get so `hasFrame` would lie - explicitly reject them
|
|
35
35
|
// here. Callers should unwrap via `resolveProxy` first.
|
|
36
|
-
if (value.
|
|
36
|
+
if (value._selaSelector !== undefined)
|
|
37
37
|
return false;
|
|
38
38
|
const hasFrame = value._frame !== undefined ||
|
|
39
39
|
value._impl?._frame !== undefined;
|
|
@@ -63,13 +63,13 @@ function extractSelectorFromLocator(locator) {
|
|
|
63
63
|
* frame segments (e.g. "#modern-frame-container >> #status-label-v2").
|
|
64
64
|
*
|
|
65
65
|
* A plain page.locator() call with a frame >> element string is NOT
|
|
66
|
-
* a valid locator for frame content
|
|
66
|
+
* a valid locator for frame content - Playwright rejects it with
|
|
67
67
|
* "toHaveText can be only used with Locator object".
|
|
68
68
|
*
|
|
69
69
|
* This function correctly chains frameLocator() calls for each frame
|
|
70
70
|
* segment, then calls .locator() for the final element segment.
|
|
71
71
|
*
|
|
72
|
-
* Convention used by
|
|
72
|
+
* Convention used by SelaEngine segments:
|
|
73
73
|
* - Frame segments look like CSS selectors for iframes (#id, .class)
|
|
74
74
|
* but produce a FrameLocator, not a Locator.
|
|
75
75
|
* - The last segment is always the element selector.
|
|
@@ -128,13 +128,13 @@ function wrapMatcher(matcher, locator, matcherName, engine, page, ctx) {
|
|
|
128
128
|
if (!rawSelector)
|
|
129
129
|
throw err;
|
|
130
130
|
console.log(`[Sela] ⚠️ Assertion failed for "${rawSelector}", attempting heal…`);
|
|
131
|
-
// Fast path
|
|
131
|
+
// Fast path - already healed in this test via registry
|
|
132
132
|
const resolved = HealingRegistry_1.HealingRegistry.getInstance().resolveSelector(rawSelector, ctx);
|
|
133
133
|
let healedSelector = resolved;
|
|
134
134
|
if (resolved === rawSelector) {
|
|
135
|
-
// Slow path
|
|
135
|
+
// Slow path - ask the engine + LLM.
|
|
136
136
|
// engine.heal() writes the fix to disk AND returns the canonical
|
|
137
|
-
// healedLocatorString from SourceUpdater
|
|
137
|
+
// healedLocatorString from SourceUpdater - the exact same string
|
|
138
138
|
// that was saved to the file. This is the handshake that guarantees
|
|
139
139
|
// disk and runtime are in sync (no "ghost failures").
|
|
140
140
|
const stableId = require("crypto")
|
|
@@ -182,9 +182,9 @@ const WRAPPED_MATCHERS = [
|
|
|
182
182
|
"toBeFocused",
|
|
183
183
|
];
|
|
184
184
|
/**
|
|
185
|
-
* createHealingExpect
|
|
185
|
+
* createHealingExpect - drop-in replacement for Playwright's expect().
|
|
186
186
|
*
|
|
187
|
-
* @param resolveProxy Optional callback that unwraps a
|
|
187
|
+
* @param resolveProxy Optional callback that unwraps a Sela proxy
|
|
188
188
|
* to its current live Locator. Supplied by
|
|
189
189
|
* fixtures/index.ts which owns proxyToActiveLocator.
|
|
190
190
|
*/
|
|
@@ -204,7 +204,7 @@ resolveProxy) {
|
|
|
204
204
|
return initialLine;
|
|
205
205
|
};
|
|
206
206
|
const actualLine = getCallerLine(); // כאן אנחנו מקבלים את השורה הנכונה!
|
|
207
|
-
// Step 1
|
|
207
|
+
// Step 1 - unwrap Sela proxy to real active Locator
|
|
208
208
|
let resolved = locatorOrValue;
|
|
209
209
|
if (resolveProxy) {
|
|
210
210
|
const unwrapped = resolveProxy(locatorOrValue);
|
|
@@ -217,7 +217,7 @@ resolveProxy) {
|
|
|
217
217
|
if (base === null || typeof base !== "object") {
|
|
218
218
|
return base;
|
|
219
219
|
}
|
|
220
|
-
// Step 2
|
|
220
|
+
// Step 2 - only wrap matchers for genuine page-level Locator objects.
|
|
221
221
|
if (!isRealLocator(resolved)) {
|
|
222
222
|
return base;
|
|
223
223
|
}
|
|
@@ -226,7 +226,7 @@ resolveProxy) {
|
|
|
226
226
|
const ctx = { filePath, line: actualLine };
|
|
227
227
|
const proxy = new Proxy(base, {
|
|
228
228
|
get(target, prop) {
|
|
229
|
-
// Sela proxy identity
|
|
229
|
+
// Sela proxy identity - honored before any string-only branch so
|
|
230
230
|
// callers can detect a Sela expect proxy via Symbol.for("sela.proxy").
|
|
231
231
|
if (prop === proxyTag_1.SELA_PROXY)
|
|
232
232
|
return "expect";
|
package/dist/fixtures/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Locator, Page } from "@playwright/test";
|
|
2
2
|
import { createHealingExpect } from "./expectProxy";
|
|
3
|
-
export declare function
|
|
3
|
+
export declare function resolveSelaProxy(value: unknown): Locator | null;
|
|
4
4
|
export declare function createFrameLocatorProxy(rawFrameLocator: any, frameSelector: string, rawPage: Page, testTitle: string, actionCounter: {
|
|
5
5
|
value: number;
|
|
6
6
|
}, parentChain: string): any;
|
|
@@ -9,6 +9,22 @@ export declare function createLocatorProxy(rawLocator: Locator, selector: string
|
|
|
9
9
|
}, elementSelectorOnly?: string): any;
|
|
10
10
|
export declare const test: import("@playwright/test").TestType<import("@playwright/test").PlaywrightTestArgs & import("@playwright/test").PlaywrightTestOptions & {
|
|
11
11
|
expect: ReturnType<typeof createHealingExpect>;
|
|
12
|
-
}, import("@playwright/test").PlaywrightWorkerArgs & import("@playwright/test").PlaywrightWorkerOptions
|
|
12
|
+
}, import("@playwright/test").PlaywrightWorkerArgs & import("@playwright/test").PlaywrightWorkerOptions & {
|
|
13
|
+
/**
|
|
14
|
+
* Worker-scoped lifecycle anchor. Its teardown runs EXACTLY ONCE per
|
|
15
|
+
* worker process - after every test in that worker has finished - which
|
|
16
|
+
* is the only correct place to flush the suite-wide healing report.
|
|
17
|
+
*
|
|
18
|
+
* Why a worker fixture and not the reporter's onEnd():
|
|
19
|
+
* Playwright runs tests in worker processes; the reporter lives in the
|
|
20
|
+
* main process. The `sharedHealReport` buffer, `HealingRegistry`, and
|
|
21
|
+
* `sharedWorkspaceSnapshot` singletons that accumulate heal events all
|
|
22
|
+
* live in the WORKER. The main-process reporter cannot see them, so the
|
|
23
|
+
* flush MUST happen worker-side. The reporter's cross-process
|
|
24
|
+
* PendingPromptLedger (written here, drained in onEnd) is what carries
|
|
25
|
+
* the DX-review summary back to the main process.
|
|
26
|
+
*/
|
|
27
|
+
_selaWorkerLifecycle: void;
|
|
28
|
+
}>;
|
|
13
29
|
export { createHealingExpect } from "./expectProxy";
|
|
14
30
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/fixtures/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,OAAO,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAI/D,OAAO,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AAYpD,wBAAgB,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/fixtures/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,OAAO,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAI/D,OAAO,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AAYpD,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,GAAG,IAAI,CAI/D;AA2jBD,wBAAgB,uBAAuB,CACrC,eAAe,EAAE,GAAG,EACpB,aAAa,EAAE,MAAM,EACrB,OAAO,EAAE,IAAI,EACb,SAAS,EAAE,MAAM,EACjB,aAAa,EAAE;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,EAChC,WAAW,EAAE,MAAM,GAClB,GAAG,CAuCL;AAaD,wBAAgB,kBAAkB,CAChC,UAAU,EAAE,OAAO,EACnB,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,IAAI,EACb,SAAS,EAAE,MAAM,EACjB,aAAa,EAAE;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,EAChC,mBAAmB,CAAC,EAAE,MAAM,GAC3B,GAAG,CAkPL;AAQD,eAAO,MAAM,IAAI;YAEL,UAAU,CAAC,OAAO,mBAAmB,CAAC;;IAG9C;;;;;;;;;;;;;OAaG;0BACmB,IAAI;EAwH5B,CAAC;AAGH,OAAO,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC"}
|
package/dist/fixtures/index.js
CHANGED
|
@@ -34,7 +34,7 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
36
|
exports.createHealingExpect = exports.test = void 0;
|
|
37
|
-
exports.
|
|
37
|
+
exports.resolveSelaProxy = resolveSelaProxy;
|
|
38
38
|
exports.createFrameLocatorProxy = createFrameLocatorProxy;
|
|
39
39
|
exports.createLocatorProxy = createLocatorProxy;
|
|
40
40
|
const test_1 = require("@playwright/test");
|
|
@@ -47,10 +47,10 @@ const fs = __importStar(require("fs"));
|
|
|
47
47
|
const path = __importStar(require("path"));
|
|
48
48
|
const registry = HealingRegistry_1.HealingRegistry.getInstance();
|
|
49
49
|
const proxyToActiveLocator = new WeakMap();
|
|
50
|
-
// Module-level resolver
|
|
50
|
+
// Module-level resolver - moduleExpect.ts uses this to unwrap a Sela
|
|
51
51
|
// locator proxy back to its live underlying Playwright Locator without
|
|
52
52
|
// needing access to the WeakMap directly.
|
|
53
|
-
function
|
|
53
|
+
function resolveSelaProxy(value) {
|
|
54
54
|
if (!value || typeof value !== "object")
|
|
55
55
|
return null;
|
|
56
56
|
const getActive = proxyToActiveLocator.get(value);
|
|
@@ -157,7 +157,7 @@ const GET_BY_METHODS = {
|
|
|
157
157
|
getByTitle: (args) => `internal:attr=[title=${JSON.stringify(args[0])}]`,
|
|
158
158
|
};
|
|
159
159
|
// ─────────────────────────────────────────────────────────────────
|
|
160
|
-
// buildLiveLocator
|
|
160
|
+
// buildLiveLocator - registry-aware
|
|
161
161
|
//
|
|
162
162
|
// NEW: resolves the selector through the HealingRegistry before
|
|
163
163
|
// constructing the Playwright Locator. Zero cost when no heal has
|
|
@@ -240,7 +240,7 @@ function buildHealedArgs(prop, args, timeout = 8000) {
|
|
|
240
240
|
// runWithHealArgument (unchanged from original)
|
|
241
241
|
// ─────────────────────────────────────────────────────────────────
|
|
242
242
|
async function runWithHealArgument(rawPage, prop, args, fullSelector, stableId, callerInfo, originalError) {
|
|
243
|
-
console.log(`[Sela] 💡 Element visible, action '${prop}' failed
|
|
243
|
+
console.log(`[Sela] 💡 Element visible, action '${prop}' failed - healing argument…`);
|
|
244
244
|
const oldArgument = typeof args[0] === "string" ? args[0] : JSON.stringify(args[0]);
|
|
245
245
|
const newArgument = await singleton_1.sharedEngine.healArgument(rawPage, fullSelector, prop, oldArgument, stableId, callerInfo.filePath, callerInfo.line);
|
|
246
246
|
if (!newArgument)
|
|
@@ -250,11 +250,11 @@ async function runWithHealArgument(rawPage, prop, args, fullSelector, stableId,
|
|
|
250
250
|
return await locator[prop](...healedArgs);
|
|
251
251
|
}
|
|
252
252
|
// ─────────────────────────────────────────────────────────────────
|
|
253
|
-
// runWithHeal
|
|
253
|
+
// runWithHeal - pre-flight registry check added
|
|
254
254
|
//
|
|
255
255
|
// NEW: Before the first Playwright attempt, resolve the selector
|
|
256
256
|
// through the registry. If a previous heal already mapped it, the
|
|
257
|
-
// first attempt uses the healed selector and succeeds immediately
|
|
257
|
+
// first attempt uses the healed selector and succeeds immediately -
|
|
258
258
|
// no timeout, no AI call, no file write.
|
|
259
259
|
// ─────────────────────────────────────────────────────────────────
|
|
260
260
|
async function runWithHeal(rawPage, prop, args, fullSelector, elementSelectorOnly, stableId, callerInfo) {
|
|
@@ -451,7 +451,7 @@ function createFrameLocatorProxy(rawFrameLocator, frameSelector, rawPage, testTi
|
|
|
451
451
|
: frameSelector;
|
|
452
452
|
return new Proxy(rawFrameLocator, {
|
|
453
453
|
get(fTarget, fProp) {
|
|
454
|
-
// Sela proxy identity
|
|
454
|
+
// Sela proxy identity - honor before the string-only fast path so
|
|
455
455
|
// symbol probes from tests / debuggers return the kind tag.
|
|
456
456
|
if (fProp === proxyTag_1.SELA_PROXY)
|
|
457
457
|
return "framelocator";
|
|
@@ -468,14 +468,14 @@ function createFrameLocatorProxy(rawFrameLocator, frameSelector, rawPage, testTi
|
|
|
468
468
|
});
|
|
469
469
|
}
|
|
470
470
|
// ─────────────────────────────────────────────────────────────────
|
|
471
|
-
// createLocatorProxy
|
|
471
|
+
// createLocatorProxy - Runtime Selector Injection added
|
|
472
472
|
//
|
|
473
473
|
// NEW additions vs original:
|
|
474
474
|
// 1. Resolves selector through registry on creation (handles proxies
|
|
475
475
|
// born after a heal has already broadcast).
|
|
476
476
|
// 2. Subscribes to registry so any future broadcast swaps
|
|
477
|
-
// activeLocator on the same tick
|
|
478
|
-
// 3. Exposes
|
|
477
|
+
// activeLocator on the same tick - before the next await.
|
|
478
|
+
// 3. Exposes _selaUnsubscribe so the fixture can clean up.
|
|
479
479
|
// ─────────────────────────────────────────────────────────────────
|
|
480
480
|
function createLocatorProxy(rawLocator, selector, rawPage, testTitle, actionCounter, elementSelectorOnly) {
|
|
481
481
|
// Defensive: Proxy ctor throws "Cannot create proxy with a non-object as target"
|
|
@@ -494,7 +494,7 @@ function createLocatorProxy(rawLocator, selector, rawPage, testTitle, actionCoun
|
|
|
494
494
|
? buildLiveLocator(rawPage, initialResolved) // already healed
|
|
495
495
|
: rawLocator; // fresh, use as-is
|
|
496
496
|
// ── Subscribe: swap activeLocator when a heal fires ──────────
|
|
497
|
-
// Inside createLocatorProxy
|
|
497
|
+
// Inside createLocatorProxy - full updated subscribe block
|
|
498
498
|
// בתוך createLocatorProxy - תחליף את הבלוק של ה-subscribe בזה:
|
|
499
499
|
const unsubscribe = registry.subscribe((fp, oldSel, newSel) => {
|
|
500
500
|
// בדיקה אם הסלקטור הנוכחי מכיל את הישן או שהוא הישן בעצמו
|
|
@@ -524,24 +524,24 @@ function createLocatorProxy(rawLocator, selector, rawPage, testTitle, actionCoun
|
|
|
524
524
|
});
|
|
525
525
|
const proxy = new Proxy(rawLocator, {
|
|
526
526
|
get(target, prop, receiver) {
|
|
527
|
-
// Sela proxy identity
|
|
527
|
+
// Sela proxy identity - must be honored before any string-only branch
|
|
528
528
|
// so callers can reliably detect a Sela proxy via Symbol.for("sela.proxy").
|
|
529
529
|
if (prop === proxyTag_1.SELA_PROXY)
|
|
530
530
|
return "locator";
|
|
531
531
|
if (typeof prop !== "string")
|
|
532
532
|
return Reflect.get(target, prop, receiver);
|
|
533
533
|
// ── Internal accessors ───────────────────────────────────
|
|
534
|
-
if (prop === "
|
|
534
|
+
if (prop === "_selaSelector")
|
|
535
535
|
return currentFullSelector;
|
|
536
|
-
if (prop === "
|
|
536
|
+
if (prop === "_selaPage")
|
|
537
537
|
return rawPage;
|
|
538
|
-
if (prop === "
|
|
538
|
+
if (prop === "_selaTestTitle")
|
|
539
539
|
return testTitle;
|
|
540
|
-
if (prop === "
|
|
540
|
+
if (prop === "_selaActionCounter")
|
|
541
541
|
return actionCounter;
|
|
542
|
-
if (prop === "
|
|
542
|
+
if (prop === "_selaElementSelector")
|
|
543
543
|
return currentElementSelector;
|
|
544
|
-
if (prop === "
|
|
544
|
+
if (prop === "_selaUnsubscribe")
|
|
545
545
|
return unsubscribe;
|
|
546
546
|
// ── getBy* ───────────────────────────────────────────────
|
|
547
547
|
if (prop in GET_BY_METHODS) {
|
|
@@ -657,10 +657,25 @@ function createLocatorProxy(rawLocator, selector, rawPage, testTitle, actionCoun
|
|
|
657
657
|
return proxy;
|
|
658
658
|
}
|
|
659
659
|
// ─────────────────────────────────────────────────────────────────
|
|
660
|
-
// test fixture
|
|
660
|
+
// test fixture - teardown added
|
|
661
661
|
// ─────────────────────────────────────────────────────────────────
|
|
662
|
-
// ─── expect
|
|
662
|
+
// ─── expect - healing proxy replacing the plain baseExpect proxy ──────────────
|
|
663
663
|
exports.test = test_1.test.extend({
|
|
664
|
+
// ── Worker-scoped flush anchor (auto) ───────────────────────────────
|
|
665
|
+
// Setup is a no-op; the teardown after `use()` is the suite-end flush.
|
|
666
|
+
// Aggregation spans the ENTIRE worker run because commitUpdates() is no
|
|
667
|
+
// longer called per test - the report buffer, registry heal-map, and
|
|
668
|
+
// workspace snapshot accumulate across all tests and flush once here.
|
|
669
|
+
_selaWorkerLifecycle: [
|
|
670
|
+
async ({}, use) => {
|
|
671
|
+
await use();
|
|
672
|
+
// Single suite-wide commit: saves DNA snapshots, flushes the Insights
|
|
673
|
+
// report + .sela-history.json ONCE, and writes the DX-review ledger
|
|
674
|
+
// with the full set of healed events from the whole worker run.
|
|
675
|
+
await singleton_1.sharedEngine.commitUpdates();
|
|
676
|
+
},
|
|
677
|
+
{ scope: "worker", auto: true },
|
|
678
|
+
],
|
|
664
679
|
page: async ({ page }, use, testInfo) => {
|
|
665
680
|
page.on("console", (msg) => {
|
|
666
681
|
if (msg.text().includes("[DOM-EXTRACT]")) {
|
|
@@ -672,7 +687,7 @@ exports.test = test_1.test.extend({
|
|
|
672
687
|
const unsubscribers = [];
|
|
673
688
|
const pageProxy = new Proxy(page, {
|
|
674
689
|
get(target, prop) {
|
|
675
|
-
// Sela proxy identity
|
|
690
|
+
// Sela proxy identity - honored before any other branch so callers
|
|
676
691
|
// can reliably detect a Sela page proxy via Symbol.for("sela.proxy").
|
|
677
692
|
if (prop === proxyTag_1.SELA_PROXY)
|
|
678
693
|
return "page";
|
|
@@ -683,7 +698,7 @@ exports.test = test_1.test.extend({
|
|
|
683
698
|
if (prop === "locator") {
|
|
684
699
|
return (selector) => {
|
|
685
700
|
const p = createLocatorProxy(target.locator(selector), selector, rawPage, testInfo.title, actionCounter, selector);
|
|
686
|
-
const u = p.
|
|
701
|
+
const u = p._selaUnsubscribe;
|
|
687
702
|
if (u)
|
|
688
703
|
unsubscribers.push(u);
|
|
689
704
|
return p;
|
|
@@ -696,7 +711,7 @@ exports.test = test_1.test.extend({
|
|
|
696
711
|
return (...args) => {
|
|
697
712
|
const semanticSelector = GET_BY_METHODS[prop](args);
|
|
698
713
|
const p = createLocatorProxy(target[prop](...args), semanticSelector, rawPage, testInfo.title, actionCounter, semanticSelector);
|
|
699
|
-
const u = p.
|
|
714
|
+
const u = p._selaUnsubscribe;
|
|
700
715
|
if (u)
|
|
701
716
|
unsubscribers.push(u);
|
|
702
717
|
return p;
|
|
@@ -706,15 +721,20 @@ exports.test = test_1.test.extend({
|
|
|
706
721
|
},
|
|
707
722
|
});
|
|
708
723
|
await use(pageProxy);
|
|
709
|
-
// ──
|
|
724
|
+
// ── Per-test teardown ─────────────────────────────────────────
|
|
725
|
+
// Release this test's proxy registry subscriptions and drop per-test
|
|
726
|
+
// listeners. We deliberately DO NOT call engine.commitUpdates() here -
|
|
727
|
+
// flushing per test is what produced the per-test report writes, the
|
|
728
|
+
// duplicated .sela-history.json entries (each "healed":1), and the
|
|
729
|
+
// last-test-only DX diff. The suite-wide flush lives in the
|
|
730
|
+
// worker-scoped `_selaWorkerLifecycle` teardown above.
|
|
710
731
|
for (const u of unsubscribers)
|
|
711
732
|
u();
|
|
712
733
|
registry.clearForTest();
|
|
713
|
-
await singleton_1.sharedEngine.commitUpdates();
|
|
714
734
|
},
|
|
715
735
|
expect: async ({ page }, use, testInfo) => {
|
|
716
736
|
const healingExpect = (0, expectProxy_1.createHealingExpect)(singleton_1.sharedEngine, page, testInfo.file, 0,
|
|
717
|
-
// Inject proxy resolver
|
|
737
|
+
// Inject proxy resolver - gives expectProxy.ts access to the live
|
|
718
738
|
// active Locator without exposing the WeakMap directly.
|
|
719
739
|
(value) => {
|
|
720
740
|
if (value && typeof value === "object") {
|
|
@@ -9,14 +9,14 @@ const singleton_js_1 = require("../engine/singleton.js");
|
|
|
9
9
|
const expectProxy_js_1 = require("./expectProxy.js");
|
|
10
10
|
const index_js_1 = require("./index.js");
|
|
11
11
|
function extractPage(value) {
|
|
12
|
-
if (!value || typeof value !==
|
|
12
|
+
if (!value || typeof value !== "object")
|
|
13
13
|
return null;
|
|
14
|
-
//
|
|
15
|
-
const proxied = value.
|
|
14
|
+
// Sela locator proxy exposes _selaPage directly
|
|
15
|
+
const proxied = value._selaPage;
|
|
16
16
|
if (proxied)
|
|
17
17
|
return proxied;
|
|
18
18
|
// Playwright public API: Locator.page()
|
|
19
|
-
if (typeof value.page ===
|
|
19
|
+
if (typeof value.page === "function") {
|
|
20
20
|
try {
|
|
21
21
|
return value.page();
|
|
22
22
|
}
|
|
@@ -27,7 +27,7 @@ function extractPage(value) {
|
|
|
27
27
|
return null;
|
|
28
28
|
}
|
|
29
29
|
function getCallerFile() {
|
|
30
|
-
const lines = new Error().stack?.split(
|
|
30
|
+
const lines = new Error().stack?.split("\n") ?? [];
|
|
31
31
|
// Match BOTH frame shapes V8 emits:
|
|
32
32
|
// " at fnName (D:\\path\\file.spec.ts:17:20)"
|
|
33
33
|
// " at D:\\path\\file.spec.ts:17:20"
|
|
@@ -42,13 +42,13 @@ function getCallerFile() {
|
|
|
42
42
|
if (m && m[1])
|
|
43
43
|
return m[1].trim();
|
|
44
44
|
}
|
|
45
|
-
return
|
|
45
|
+
return "";
|
|
46
46
|
}
|
|
47
47
|
function expect(value) {
|
|
48
48
|
const page = extractPage(value);
|
|
49
49
|
if (page) {
|
|
50
50
|
const filePath = getCallerFile();
|
|
51
|
-
return (0, expectProxy_js_1.createHealingExpect)(singleton_js_1.sharedEngine, page, filePath, 0, index_js_1.
|
|
51
|
+
return (0, expectProxy_js_1.createHealingExpect)(singleton_js_1.sharedEngine, page, filePath, 0, index_js_1.resolveSelaProxy)(value);
|
|
52
52
|
}
|
|
53
53
|
return (0, test_1.expect)(value);
|
|
54
54
|
}
|
|
@@ -3,7 +3,7 @@ export type SelaProxyKind = "locator" | "framelocator" | "page" | "expect";
|
|
|
3
3
|
/**
|
|
4
4
|
* Returns the proxy kind if `v` is a Sela proxy, otherwise `null`.
|
|
5
5
|
*
|
|
6
|
-
* Performs no instanceof checks against Playwright internals
|
|
6
|
+
* Performs no instanceof checks against Playwright internals - relies
|
|
7
7
|
* solely on the `SELA_PROXY` symbol contract. Always safe to call on
|
|
8
8
|
* arbitrary values (null/undefined/primitive → null).
|
|
9
9
|
*/
|
|
@@ -4,9 +4,9 @@
|
|
|
4
4
|
// Single source of truth for the Sela proxy tag.
|
|
5
5
|
//
|
|
6
6
|
// Every Sela-created Proxy (locator, frame-locator, page, expect) honors a
|
|
7
|
-
// `get` trap on this symbol and returns the kind string. Consumers
|
|
8
|
-
// surface-diff test, internal helpers, debuggers
|
|
9
|
-
// proxies without touching the brittle "
|
|
7
|
+
// `get` trap on this symbol and returns the kind string. Consumers - the
|
|
8
|
+
// surface-diff test, internal helpers, debuggers - can identify Sela
|
|
9
|
+
// proxies without touching the brittle "_selaSelector" heuristic.
|
|
10
10
|
//
|
|
11
11
|
// We use Symbol.for() (a registered symbol) so the identity survives
|
|
12
12
|
// across module boundaries when multiple bundles end up in the same
|
|
@@ -19,7 +19,7 @@ exports.SELA_PROXY = Symbol.for("sela.proxy");
|
|
|
19
19
|
/**
|
|
20
20
|
* Returns the proxy kind if `v` is a Sela proxy, otherwise `null`.
|
|
21
21
|
*
|
|
22
|
-
* Performs no instanceof checks against Playwright internals
|
|
22
|
+
* Performs no instanceof checks against Playwright internals - relies
|
|
23
23
|
* solely on the `SELA_PROXY` symbol contract. Always safe to call on
|
|
24
24
|
* arbitrary values (null/undefined/primitive → null).
|
|
25
25
|
*/
|
package/dist/index.d.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
export { defineConfig } from
|
|
2
|
-
export type { SelaConfig, ResolvedConfig } from
|
|
3
|
-
export type * from
|
|
4
|
-
export { chromium, firefox, webkit, devices, selectors } from
|
|
5
|
-
export { test } from
|
|
6
|
-
export { expect } from
|
|
7
|
-
export { SelaReporter } from
|
|
8
|
-
export type { SelaReporterOpts, ReporterEngine, ReporterLogger } from
|
|
1
|
+
export { defineConfig } from "./config/defineConfig.js";
|
|
2
|
+
export type { SelaConfig, ResolvedConfig } from "./config/SelaConfig.js";
|
|
3
|
+
export type * from "@playwright/test";
|
|
4
|
+
export { chromium, firefox, webkit, devices, selectors, } from "@playwright/test";
|
|
5
|
+
export { test } from "./fixtures/index.js";
|
|
6
|
+
export { expect } from "./fixtures/moduleExpect.js";
|
|
7
|
+
export { SelaReporter } from "./reporter/SelaReporter.js";
|
|
8
|
+
export type { SelaReporterOpts, ReporterEngine, ReporterLogger, } from "./reporter/SelaReporter.js";
|
|
9
9
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AACxD,YAAY,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAGzE,mBAAmB,kBAAkB,CAAC;AAGtC,OAAO,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AACxD,YAAY,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAGzE,mBAAmB,kBAAkB,CAAC;AAGtC,OAAO,EACL,QAAQ,EACR,OAAO,EACP,MAAM,EACN,OAAO,EACP,SAAS,GACV,MAAM,kBAAkB,CAAC;AAI1B,OAAO,EAAE,IAAI,EAAE,MAAM,qBAAqB,CAAC;AAI3C,OAAO,EAAE,MAAM,EAAE,MAAM,4BAA4B,CAAC;AAKpD,OAAO,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAC1D,YAAY,EACV,gBAAgB,EAChB,cAAc,EACd,cAAc,GACf,MAAM,4BAA4B,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
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'
|
|
@@ -16,15 +16,15 @@ 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
|
|
27
|
+
// Playwright Reporter - drop into `reporter: [['sela-core/reporter']]`
|
|
28
28
|
// in playwright.config.ts to drive heal-and-report at the reporter layer
|
|
29
29
|
// (safety net for worker crashes + Playwright failure screenshot capture).
|
|
30
30
|
var SelaReporter_js_1 = require("./reporter/SelaReporter.js");
|