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
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
export type SelaSubsystem = "SafetyGuard" | "IntentAuditor" | "ASTUpdater" | "Proxy" | "LLMService" | "Registry" | "ChainValidator" | "Engine" | "Report";
|
|
2
|
+
export interface SelaErrorContext {
|
|
3
|
+
filePath?: string;
|
|
4
|
+
line?: number;
|
|
5
|
+
column?: number;
|
|
6
|
+
selector?: string;
|
|
7
|
+
healedSelector?: string;
|
|
8
|
+
stableId?: string;
|
|
9
|
+
testTitle?: string;
|
|
10
|
+
testFile?: string;
|
|
11
|
+
branch?: string;
|
|
12
|
+
/** SafetyGuard verdict level when the error originated in SafetyGuard. */
|
|
13
|
+
safetyLevel?: "BLOCKED" | "FAIL_HARD" | "REQUIRES_REVIEW" | "SAFE";
|
|
14
|
+
/** Auditor verdict surfaced in the context for SafetyGuard rejections. */
|
|
15
|
+
auditorVerdict?: "CONSISTENT" | "INCONSISTENT" | "SUSPICIOUS";
|
|
16
|
+
/** Raw + adjusted LLM confidence so downstream telemetry can plot drift. */
|
|
17
|
+
aiConfidence?: number;
|
|
18
|
+
auditorConfidence?: number;
|
|
19
|
+
/** Free-form extension slot. Prefer adding a named field above when possible. */
|
|
20
|
+
[key: string]: unknown;
|
|
21
|
+
}
|
|
22
|
+
export interface SelaErrorJSON {
|
|
23
|
+
name: string;
|
|
24
|
+
subsystem: SelaSubsystem;
|
|
25
|
+
code: string;
|
|
26
|
+
reason: string;
|
|
27
|
+
message: string;
|
|
28
|
+
context: SelaErrorContext;
|
|
29
|
+
stack?: string;
|
|
30
|
+
cause?: string;
|
|
31
|
+
}
|
|
32
|
+
export interface SelaErrorInit {
|
|
33
|
+
subsystem: SelaSubsystem;
|
|
34
|
+
/**
|
|
35
|
+
* Stable machine-readable error code. Must match `[A-Z][A-Z0-9_]+`.
|
|
36
|
+
* Recommended convention: short subsystem-prefix + verb/noun.
|
|
37
|
+
* See SELA_ERROR_CODES below for the canonical catalogue.
|
|
38
|
+
*/
|
|
39
|
+
code: string;
|
|
40
|
+
/** Human-readable explanation. Becomes the tail of the formatted message. */
|
|
41
|
+
reason: string;
|
|
42
|
+
context?: SelaErrorContext;
|
|
43
|
+
/** Original error wrapped by this SelaError, if any. */
|
|
44
|
+
cause?: unknown;
|
|
45
|
+
}
|
|
46
|
+
export declare class SelaError extends Error {
|
|
47
|
+
readonly subsystem: SelaSubsystem;
|
|
48
|
+
readonly code: string;
|
|
49
|
+
readonly reason: string;
|
|
50
|
+
readonly context: SelaErrorContext;
|
|
51
|
+
constructor(init: SelaErrorInit);
|
|
52
|
+
static formatMessage(subsystem: SelaSubsystem, code: string, reason: string): string;
|
|
53
|
+
toJSON(): SelaErrorJSON;
|
|
54
|
+
}
|
|
55
|
+
export declare function isSelaError(err: unknown): err is SelaError;
|
|
56
|
+
export declare function isSelaErrorFrom(err: unknown, subsystem: SelaSubsystem): err is SelaError;
|
|
57
|
+
export declare const SAFETY_GUARD_CODES: {
|
|
58
|
+
readonly FAIL_HARD_VISIBILITY_GHOST: "SG_FAIL_HARD_VISIBILITY_GHOST";
|
|
59
|
+
readonly FAIL_HARD_VISIBILITY_OCCLUDED: "SG_FAIL_HARD_VISIBILITY_OCCLUDED";
|
|
60
|
+
readonly BLOCKED_LOW_CONFIDENCE: "SG_BLOCKED_LOW_CONFIDENCE";
|
|
61
|
+
readonly FAIL_HARD_SEMANTIC_INVERSION: "SG_FAIL_HARD_SEMANTIC_INVERSION";
|
|
62
|
+
readonly FAIL_HARD_STATUS_INVERSION: "SG_FAIL_HARD_STATUS_INVERSION";
|
|
63
|
+
readonly BLOCKED_ROLE_SWAP_ASSERTION: "SG_BLOCKED_ROLE_SWAP_ASSERTION";
|
|
64
|
+
readonly FAIL_HARD_AUDITOR_INCONSISTENT: "SG_FAIL_HARD_AUDITOR_INCONSISTENT";
|
|
65
|
+
readonly BLOCKED_AUDITOR_SUSPICIOUS: "SG_BLOCKED_AUDITOR_SUSPICIOUS";
|
|
66
|
+
/** Generic dispatcher when SafetyDecision arrives without an explicit code. */
|
|
67
|
+
readonly GENERIC_REJECTION: "SG_GENERIC_REJECTION";
|
|
68
|
+
};
|
|
69
|
+
export type SafetyGuardCode = (typeof SAFETY_GUARD_CODES)[keyof typeof SAFETY_GUARD_CODES];
|
|
70
|
+
export declare const INTENT_AUDITOR_CODES: {
|
|
71
|
+
/** Caller asked for an audit but no Anthropic client was configured. */
|
|
72
|
+
readonly DISABLED: "IA_DISABLED";
|
|
73
|
+
readonly INVALID_JSON: "IA_INVALID_JSON";
|
|
74
|
+
readonly CALL_FAILED: "IA_CALL_FAILED";
|
|
75
|
+
};
|
|
76
|
+
export type IntentAuditorCode = (typeof INTENT_AUDITOR_CODES)[keyof typeof INTENT_AUDITOR_CODES];
|
|
77
|
+
export declare const LLM_SERVICE_CODES: {
|
|
78
|
+
readonly NO_API_KEY: "LLM_NO_API_KEY";
|
|
79
|
+
readonly INVALID_JSON: "LLM_INVALID_JSON";
|
|
80
|
+
readonly MISSING_SEGMENTS: "LLM_MISSING_SEGMENTS";
|
|
81
|
+
readonly NOT_FOUND: "LLM_NOT_FOUND";
|
|
82
|
+
readonly API_CALL_FAILED: "LLM_API_CALL_FAILED";
|
|
83
|
+
};
|
|
84
|
+
export type LLMServiceCode = (typeof LLM_SERVICE_CODES)[keyof typeof LLM_SERVICE_CODES];
|
|
85
|
+
export declare const AST_UPDATER_CODES: {
|
|
86
|
+
readonly UNSUPPORTED_NODE: "AST_UNSUPPORTED_NODE";
|
|
87
|
+
readonly TEMPLATE_LITERAL_SKIP: "AST_TEMPLATE_LITERAL_SKIP";
|
|
88
|
+
readonly CIRCULAR_RECEIVER: "AST_CIRCULAR_RECEIVER";
|
|
89
|
+
readonly DEFINITION_NOT_FOUND: "AST_DEFINITION_NOT_FOUND";
|
|
90
|
+
readonly MAX_DEPTH_EXCEEDED: "AST_MAX_DEPTH_EXCEEDED";
|
|
91
|
+
readonly SAVE_FAILED: "AST_SAVE_FAILED";
|
|
92
|
+
/**
|
|
93
|
+
* Developer pinned the failing statement with `// sela-fail-fast`.
|
|
94
|
+
* Heal pipeline is aborted before any LLM call; FailedEvent is
|
|
95
|
+
* recorded with category "Skipped by Developer".
|
|
96
|
+
*/
|
|
97
|
+
readonly HEAL_DISABLED_BY_DIRECTIVE: "AST_HEAL_DISABLED_BY_DIRECTIVE";
|
|
98
|
+
};
|
|
99
|
+
export type ASTUpdaterCode = (typeof AST_UPDATER_CODES)[keyof typeof AST_UPDATER_CODES];
|
|
100
|
+
export declare const PROXY_CODES: {
|
|
101
|
+
readonly FRAME_SCOPED: "PRX_FRAME_SCOPED";
|
|
102
|
+
readonly REBUILD_FAILED: "PRX_REBUILD_FAILED";
|
|
103
|
+
readonly NON_LOCATOR_TARGET: "PRX_NON_LOCATOR_TARGET";
|
|
104
|
+
};
|
|
105
|
+
export type ProxyCode = (typeof PROXY_CODES)[keyof typeof PROXY_CODES];
|
|
106
|
+
export declare const REGISTRY_CODES: {
|
|
107
|
+
readonly CONTEXT_MISMATCH: "REG_CONTEXT_MISMATCH";
|
|
108
|
+
readonly FINGERPRINT_COLLISION: "REG_FINGERPRINT_COLLISION";
|
|
109
|
+
};
|
|
110
|
+
export type RegistryCode = (typeof REGISTRY_CODES)[keyof typeof REGISTRY_CODES];
|
|
111
|
+
export declare const REPORT_CODES: {
|
|
112
|
+
/** Event passed to the validator was null, undefined, or not an object. */
|
|
113
|
+
readonly MALFORMED_EVENT: "REP_MALFORMED_EVENT";
|
|
114
|
+
/** Event.kind is not one of HEALED|FAILED|PROTECTED. */
|
|
115
|
+
readonly UNKNOWN_KIND: "REP_UNKNOWN_KIND";
|
|
116
|
+
/** Event is missing a field declared mandatory for its kind. */
|
|
117
|
+
readonly MISSING_FIELD: "REP_MISSING_FIELD";
|
|
118
|
+
/** Event contains a field of the wrong type. */
|
|
119
|
+
readonly INVALID_FIELD_TYPE: "REP_INVALID_FIELD_TYPE";
|
|
120
|
+
/** Disk write failed during flushToDisk(). */
|
|
121
|
+
readonly FLUSH_FAILED: "REP_FLUSH_FAILED";
|
|
122
|
+
};
|
|
123
|
+
export type ReportCode = (typeof REPORT_CODES)[keyof typeof REPORT_CODES];
|
|
124
|
+
export declare const CHAIN_VALIDATOR_CODES: {
|
|
125
|
+
readonly TYPE_FLOW: "CV_TYPE_FLOW";
|
|
126
|
+
readonly SCHEMA: "CV_SCHEMA";
|
|
127
|
+
readonly RECEIVER: "CV_RECEIVER";
|
|
128
|
+
readonly PLACEMENT: "CV_PLACEMENT";
|
|
129
|
+
readonly SEMANTIC_DOWNGRADE: "CV_SEMANTIC_DOWNGRADE";
|
|
130
|
+
};
|
|
131
|
+
export type ChainValidatorCode = (typeof CHAIN_VALIDATOR_CODES)[keyof typeof CHAIN_VALIDATOR_CODES];
|
|
132
|
+
//# sourceMappingURL=SelaError.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SelaError.d.ts","sourceRoot":"","sources":["../../src/errors/SelaError.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// src/errors/SelaError.ts
|
|
3
|
+
//
|
|
4
|
+
// Structured error hierarchy for Sela.
|
|
5
|
+
//
|
|
6
|
+
// Every error thrown across the heal pipeline MUST be a SelaError so that:
|
|
7
|
+
// 1. Callers can branch on `err.subsystem` and `err.code` without
|
|
8
|
+
// string-parsing `err.message`.
|
|
9
|
+
// 2. Telemetry / report layers get a machine-readable JSON shape.
|
|
10
|
+
// 3. The canonical message format is enforced by the constructor and
|
|
11
|
+
// cannot drift over time: `[Sela-<Subsystem>] <CODE>: <reason>`
|
|
12
|
+
//
|
|
13
|
+
// Public message regex (used by tests/unit/engine/error-indicativity.test.ts):
|
|
14
|
+
// /^\[Sela-(SafetyGuard|IntentAuditor|ASTUpdater|Proxy|LLMService|Registry|ChainValidator|Engine)\] [A-Z][A-Z0-9_]+: .+$/
|
|
15
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
16
|
+
exports.CHAIN_VALIDATOR_CODES = exports.REPORT_CODES = exports.REGISTRY_CODES = exports.PROXY_CODES = exports.AST_UPDATER_CODES = exports.LLM_SERVICE_CODES = exports.INTENT_AUDITOR_CODES = exports.SAFETY_GUARD_CODES = exports.SelaError = void 0;
|
|
17
|
+
exports.isSelaError = isSelaError;
|
|
18
|
+
exports.isSelaErrorFrom = isSelaErrorFrom;
|
|
19
|
+
// ───────────────────────────────────────────────────────────────────
|
|
20
|
+
// SelaError - single class for the whole hierarchy.
|
|
21
|
+
//
|
|
22
|
+
// We intentionally avoid a tree of subclasses; `instanceof SelaError` +
|
|
23
|
+
// `err.subsystem === "X"` is more flexible for callers and keeps the
|
|
24
|
+
// surface area tiny.
|
|
25
|
+
// ───────────────────────────────────────────────────────────────────
|
|
26
|
+
const CODE_PATTERN = /^[A-Z][A-Z0-9_]+$/;
|
|
27
|
+
class SelaError extends Error {
|
|
28
|
+
subsystem;
|
|
29
|
+
code;
|
|
30
|
+
reason;
|
|
31
|
+
context;
|
|
32
|
+
constructor(init) {
|
|
33
|
+
if (!CODE_PATTERN.test(init.code)) {
|
|
34
|
+
throw new Error(`[SelaError] invalid code "${init.code}" - must match ${CODE_PATTERN}`);
|
|
35
|
+
}
|
|
36
|
+
const formatted = SelaError.formatMessage(init.subsystem, init.code, init.reason);
|
|
37
|
+
super(formatted);
|
|
38
|
+
this.name = `SelaError(${init.subsystem})`;
|
|
39
|
+
this.subsystem = init.subsystem;
|
|
40
|
+
this.code = init.code;
|
|
41
|
+
this.reason = init.reason;
|
|
42
|
+
this.context = init.context ?? {};
|
|
43
|
+
if (init.cause !== undefined) {
|
|
44
|
+
this.cause = init.cause;
|
|
45
|
+
}
|
|
46
|
+
// Maintain prototype chain through transpilation (ES5 target).
|
|
47
|
+
Object.setPrototypeOf(this, new.target.prototype);
|
|
48
|
+
if (typeof Error.captureStackTrace ===
|
|
49
|
+
"function") {
|
|
50
|
+
Error.captureStackTrace(this, new.target);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
static formatMessage(subsystem, code, reason) {
|
|
54
|
+
return `[Sela-${subsystem}] ${code}: ${reason}`;
|
|
55
|
+
}
|
|
56
|
+
toJSON() {
|
|
57
|
+
return {
|
|
58
|
+
name: this.name,
|
|
59
|
+
subsystem: this.subsystem,
|
|
60
|
+
code: this.code,
|
|
61
|
+
reason: this.reason,
|
|
62
|
+
message: this.message,
|
|
63
|
+
context: this.context,
|
|
64
|
+
stack: this.stack,
|
|
65
|
+
cause: this.cause instanceof Error
|
|
66
|
+
? this.cause.message
|
|
67
|
+
: undefined,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
exports.SelaError = SelaError;
|
|
72
|
+
// ───────────────────────────────────────────────────────────────────
|
|
73
|
+
// Type guards
|
|
74
|
+
// ───────────────────────────────────────────────────────────────────
|
|
75
|
+
function isSelaError(err) {
|
|
76
|
+
return err instanceof SelaError;
|
|
77
|
+
}
|
|
78
|
+
function isSelaErrorFrom(err, subsystem) {
|
|
79
|
+
return err instanceof SelaError && err.subsystem === subsystem;
|
|
80
|
+
}
|
|
81
|
+
// ───────────────────────────────────────────────────────────────────
|
|
82
|
+
// Canonical code catalogue
|
|
83
|
+
//
|
|
84
|
+
// Tests assert against these constants - never hand-write code strings
|
|
85
|
+
// at throw sites. Adding a new code requires adding it here first so
|
|
86
|
+
// the catalogue stays in sync with the error-indicativity contract.
|
|
87
|
+
// ───────────────────────────────────────────────────────────────────
|
|
88
|
+
exports.SAFETY_GUARD_CODES = {
|
|
89
|
+
FAIL_HARD_VISIBILITY_GHOST: "SG_FAIL_HARD_VISIBILITY_GHOST",
|
|
90
|
+
FAIL_HARD_VISIBILITY_OCCLUDED: "SG_FAIL_HARD_VISIBILITY_OCCLUDED",
|
|
91
|
+
BLOCKED_LOW_CONFIDENCE: "SG_BLOCKED_LOW_CONFIDENCE",
|
|
92
|
+
FAIL_HARD_SEMANTIC_INVERSION: "SG_FAIL_HARD_SEMANTIC_INVERSION",
|
|
93
|
+
FAIL_HARD_STATUS_INVERSION: "SG_FAIL_HARD_STATUS_INVERSION",
|
|
94
|
+
BLOCKED_ROLE_SWAP_ASSERTION: "SG_BLOCKED_ROLE_SWAP_ASSERTION",
|
|
95
|
+
FAIL_HARD_AUDITOR_INCONSISTENT: "SG_FAIL_HARD_AUDITOR_INCONSISTENT",
|
|
96
|
+
BLOCKED_AUDITOR_SUSPICIOUS: "SG_BLOCKED_AUDITOR_SUSPICIOUS",
|
|
97
|
+
/** Generic dispatcher when SafetyDecision arrives without an explicit code. */
|
|
98
|
+
GENERIC_REJECTION: "SG_GENERIC_REJECTION",
|
|
99
|
+
};
|
|
100
|
+
exports.INTENT_AUDITOR_CODES = {
|
|
101
|
+
/** Caller asked for an audit but no Anthropic client was configured. */
|
|
102
|
+
DISABLED: "IA_DISABLED",
|
|
103
|
+
INVALID_JSON: "IA_INVALID_JSON",
|
|
104
|
+
CALL_FAILED: "IA_CALL_FAILED",
|
|
105
|
+
};
|
|
106
|
+
exports.LLM_SERVICE_CODES = {
|
|
107
|
+
NO_API_KEY: "LLM_NO_API_KEY",
|
|
108
|
+
INVALID_JSON: "LLM_INVALID_JSON",
|
|
109
|
+
MISSING_SEGMENTS: "LLM_MISSING_SEGMENTS",
|
|
110
|
+
NOT_FOUND: "LLM_NOT_FOUND",
|
|
111
|
+
API_CALL_FAILED: "LLM_API_CALL_FAILED",
|
|
112
|
+
};
|
|
113
|
+
exports.AST_UPDATER_CODES = {
|
|
114
|
+
UNSUPPORTED_NODE: "AST_UNSUPPORTED_NODE",
|
|
115
|
+
TEMPLATE_LITERAL_SKIP: "AST_TEMPLATE_LITERAL_SKIP",
|
|
116
|
+
CIRCULAR_RECEIVER: "AST_CIRCULAR_RECEIVER",
|
|
117
|
+
DEFINITION_NOT_FOUND: "AST_DEFINITION_NOT_FOUND",
|
|
118
|
+
MAX_DEPTH_EXCEEDED: "AST_MAX_DEPTH_EXCEEDED",
|
|
119
|
+
SAVE_FAILED: "AST_SAVE_FAILED",
|
|
120
|
+
/**
|
|
121
|
+
* Developer pinned the failing statement with `// sela-fail-fast`.
|
|
122
|
+
* Heal pipeline is aborted before any LLM call; FailedEvent is
|
|
123
|
+
* recorded with category "Skipped by Developer".
|
|
124
|
+
*/
|
|
125
|
+
HEAL_DISABLED_BY_DIRECTIVE: "AST_HEAL_DISABLED_BY_DIRECTIVE",
|
|
126
|
+
};
|
|
127
|
+
exports.PROXY_CODES = {
|
|
128
|
+
FRAME_SCOPED: "PRX_FRAME_SCOPED",
|
|
129
|
+
REBUILD_FAILED: "PRX_REBUILD_FAILED",
|
|
130
|
+
NON_LOCATOR_TARGET: "PRX_NON_LOCATOR_TARGET",
|
|
131
|
+
};
|
|
132
|
+
exports.REGISTRY_CODES = {
|
|
133
|
+
CONTEXT_MISMATCH: "REG_CONTEXT_MISMATCH",
|
|
134
|
+
FINGERPRINT_COLLISION: "REG_FINGERPRINT_COLLISION",
|
|
135
|
+
};
|
|
136
|
+
exports.REPORT_CODES = {
|
|
137
|
+
/** Event passed to the validator was null, undefined, or not an object. */
|
|
138
|
+
MALFORMED_EVENT: "REP_MALFORMED_EVENT",
|
|
139
|
+
/** Event.kind is not one of HEALED|FAILED|PROTECTED. */
|
|
140
|
+
UNKNOWN_KIND: "REP_UNKNOWN_KIND",
|
|
141
|
+
/** Event is missing a field declared mandatory for its kind. */
|
|
142
|
+
MISSING_FIELD: "REP_MISSING_FIELD",
|
|
143
|
+
/** Event contains a field of the wrong type. */
|
|
144
|
+
INVALID_FIELD_TYPE: "REP_INVALID_FIELD_TYPE",
|
|
145
|
+
/** Disk write failed during flushToDisk(). */
|
|
146
|
+
FLUSH_FAILED: "REP_FLUSH_FAILED",
|
|
147
|
+
};
|
|
148
|
+
exports.CHAIN_VALIDATOR_CODES = {
|
|
149
|
+
TYPE_FLOW: "CV_TYPE_FLOW",
|
|
150
|
+
SCHEMA: "CV_SCHEMA",
|
|
151
|
+
RECEIVER: "CV_RECEIVER",
|
|
152
|
+
PLACEMENT: "CV_PLACEMENT",
|
|
153
|
+
SEMANTIC_DOWNGRADE: "CV_SEMANTIC_DOWNGRADE",
|
|
154
|
+
};
|
|
@@ -1,7 +1,7 @@
|
|
|
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
6
|
* @param resolveProxy Optional callback that unwraps a Fixwright proxy
|
|
7
7
|
* to its current live Locator. Supplied by
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"expectProxy.d.ts","sourceRoot":"","sources":["../../src/fixtures/expectProxy.ts"],"names":[],"mappings":"AAEA,OAAO,EAEL,OAAO,EAER,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"expectProxy.d.ts","sourceRoot":"","sources":["../../src/fixtures/expectProxy.ts"],"names":[],"mappings":"AAEA,OAAO,EAEL,OAAO,EAER,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAiOlD;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,UAAU,EAClB,IAAI,EAAE,OAAO,kBAAkB,EAAE,IAAI,EACrC,QAAQ,EAAE,MAAM,EAChB,WAAW,EAAE,MAAM,EAAE,6CAA6C;AAClE,YAAY,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,OAAO,GAAG,IAAI,GAChD,CAAC,cAAc,EAAE,OAAO,KAAK,GAAG,CAsElC"}
|
|
@@ -1,9 +1,10 @@
|
|
|
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");
|
|
6
6
|
const HealingRegistry_1 = require("../engine/HealingRegistry");
|
|
7
|
+
const proxyTag_1 = require("./proxyTag");
|
|
7
8
|
const ELEMENT_NOT_FOUND_PATTERNS = [
|
|
8
9
|
/element.*not found/i,
|
|
9
10
|
/locator.*resolved to.*\d+ element/i,
|
|
@@ -16,7 +17,7 @@ function isElementNotFoundError(err) {
|
|
|
16
17
|
return ELEMENT_NOT_FOUND_PATTERNS.some((p) => p.test(err.message));
|
|
17
18
|
}
|
|
18
19
|
/**
|
|
19
|
-
* Returns true only for genuine Playwright Locator instances
|
|
20
|
+
* Returns true only for genuine Playwright Locator instances -
|
|
20
21
|
* not FrameLocator proxies or Fixwright proxy objects.
|
|
21
22
|
*
|
|
22
23
|
* A real Locator has _frame internally.
|
|
@@ -30,7 +31,7 @@ function isRealLocator(value) {
|
|
|
30
31
|
if (typeof value !== "object")
|
|
31
32
|
return false;
|
|
32
33
|
// Fixwright locator proxies expose `_fixwrightSelector`. The proxy forwards
|
|
33
|
-
// `_frame` via Reflect.get so `hasFrame` would lie
|
|
34
|
+
// `_frame` via Reflect.get so `hasFrame` would lie - explicitly reject them
|
|
34
35
|
// here. Callers should unwrap via `resolveProxy` first.
|
|
35
36
|
if (value._fixwrightSelector !== undefined)
|
|
36
37
|
return false;
|
|
@@ -62,7 +63,7 @@ function extractSelectorFromLocator(locator) {
|
|
|
62
63
|
* frame segments (e.g. "#modern-frame-container >> #status-label-v2").
|
|
63
64
|
*
|
|
64
65
|
* A plain page.locator() call with a frame >> element string is NOT
|
|
65
|
-
* a valid locator for frame content
|
|
66
|
+
* a valid locator for frame content - Playwright rejects it with
|
|
66
67
|
* "toHaveText can be only used with Locator object".
|
|
67
68
|
*
|
|
68
69
|
* This function correctly chains frameLocator() calls for each frame
|
|
@@ -127,13 +128,13 @@ function wrapMatcher(matcher, locator, matcherName, engine, page, ctx) {
|
|
|
127
128
|
if (!rawSelector)
|
|
128
129
|
throw err;
|
|
129
130
|
console.log(`[Sela] ⚠️ Assertion failed for "${rawSelector}", attempting heal…`);
|
|
130
|
-
// Fast path
|
|
131
|
+
// Fast path - already healed in this test via registry
|
|
131
132
|
const resolved = HealingRegistry_1.HealingRegistry.getInstance().resolveSelector(rawSelector, ctx);
|
|
132
133
|
let healedSelector = resolved;
|
|
133
134
|
if (resolved === rawSelector) {
|
|
134
|
-
// Slow path
|
|
135
|
+
// Slow path - ask the engine + LLM.
|
|
135
136
|
// engine.heal() writes the fix to disk AND returns the canonical
|
|
136
|
-
// healedLocatorString from SourceUpdater
|
|
137
|
+
// healedLocatorString from SourceUpdater - the exact same string
|
|
137
138
|
// that was saved to the file. This is the handshake that guarantees
|
|
138
139
|
// disk and runtime are in sync (no "ghost failures").
|
|
139
140
|
const stableId = require("crypto")
|
|
@@ -181,7 +182,7 @@ const WRAPPED_MATCHERS = [
|
|
|
181
182
|
"toBeFocused",
|
|
182
183
|
];
|
|
183
184
|
/**
|
|
184
|
-
* createHealingExpect
|
|
185
|
+
* createHealingExpect - drop-in replacement for Playwright's expect().
|
|
185
186
|
*
|
|
186
187
|
* @param resolveProxy Optional callback that unwraps a Fixwright proxy
|
|
187
188
|
* to its current live Locator. Supplied by
|
|
@@ -203,7 +204,7 @@ resolveProxy) {
|
|
|
203
204
|
return initialLine;
|
|
204
205
|
};
|
|
205
206
|
const actualLine = getCallerLine(); // כאן אנחנו מקבלים את השורה הנכונה!
|
|
206
|
-
// Step 1
|
|
207
|
+
// Step 1 - unwrap Fixwright proxy to real active Locator
|
|
207
208
|
let resolved = locatorOrValue;
|
|
208
209
|
if (resolveProxy) {
|
|
209
210
|
const unwrapped = resolveProxy(locatorOrValue);
|
|
@@ -216,7 +217,7 @@ resolveProxy) {
|
|
|
216
217
|
if (base === null || typeof base !== "object") {
|
|
217
218
|
return base;
|
|
218
219
|
}
|
|
219
|
-
// Step 2
|
|
220
|
+
// Step 2 - only wrap matchers for genuine page-level Locator objects.
|
|
220
221
|
if (!isRealLocator(resolved)) {
|
|
221
222
|
return base;
|
|
222
223
|
}
|
|
@@ -225,6 +226,12 @@ resolveProxy) {
|
|
|
225
226
|
const ctx = { filePath, line: actualLine };
|
|
226
227
|
const proxy = new Proxy(base, {
|
|
227
228
|
get(target, prop) {
|
|
229
|
+
// Sela proxy identity - honored before any string-only branch so
|
|
230
|
+
// callers can detect a Sela expect proxy via Symbol.for("sela.proxy").
|
|
231
|
+
if (prop === proxyTag_1.SELA_PROXY)
|
|
232
|
+
return "expect";
|
|
233
|
+
if (typeof prop !== "string")
|
|
234
|
+
return target[prop];
|
|
228
235
|
const val = target[prop];
|
|
229
236
|
if (typeof val === "function" &&
|
|
230
237
|
WRAPPED_MATCHERS.includes(prop)) {
|
package/dist/fixtures/index.d.ts
CHANGED
|
@@ -1,6 +1,12 @@
|
|
|
1
|
-
import { Locator } from "@playwright/test";
|
|
1
|
+
import { Locator, Page } from "@playwright/test";
|
|
2
2
|
import { createHealingExpect } from "./expectProxy";
|
|
3
3
|
export declare function resolveFixwrightProxy(value: unknown): Locator | null;
|
|
4
|
+
export declare function createFrameLocatorProxy(rawFrameLocator: any, frameSelector: string, rawPage: Page, testTitle: string, actionCounter: {
|
|
5
|
+
value: number;
|
|
6
|
+
}, parentChain: string): any;
|
|
7
|
+
export declare function createLocatorProxy(rawLocator: Locator, selector: string, rawPage: Page, testTitle: string, actionCounter: {
|
|
8
|
+
value: number;
|
|
9
|
+
}, elementSelectorOnly?: string): any;
|
|
4
10
|
export declare const test: import("@playwright/test").TestType<import("@playwright/test").PlaywrightTestArgs & import("@playwright/test").PlaywrightTestOptions & {
|
|
5
11
|
expect: ReturnType<typeof createHealingExpect>;
|
|
6
12
|
}, import("@playwright/test").PlaywrightWorkerArgs & import("@playwright/test").PlaywrightWorkerOptions>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/fixtures/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,OAAO,
|
|
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,qBAAqB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,GAAG,IAAI,CAIpE;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;YACP,UAAU,CAAC,OAAO,mBAAmB,CAAC;wGAkG9C,CAAC;AAGH,OAAO,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC"}
|
package/dist/fixtures/index.js
CHANGED
|
@@ -35,16 +35,19 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
36
|
exports.createHealingExpect = exports.test = void 0;
|
|
37
37
|
exports.resolveFixwrightProxy = resolveFixwrightProxy;
|
|
38
|
+
exports.createFrameLocatorProxy = createFrameLocatorProxy;
|
|
39
|
+
exports.createLocatorProxy = createLocatorProxy;
|
|
38
40
|
const test_1 = require("@playwright/test");
|
|
39
41
|
const StackUtils_1 = require("../utils/StackUtils");
|
|
40
42
|
const HealingRegistry_1 = require("../engine/HealingRegistry");
|
|
41
43
|
const singleton_1 = require("../engine/singleton");
|
|
42
44
|
const expectProxy_1 = require("./expectProxy");
|
|
45
|
+
const proxyTag_1 = require("./proxyTag");
|
|
43
46
|
const fs = __importStar(require("fs"));
|
|
44
47
|
const path = __importStar(require("path"));
|
|
45
48
|
const registry = HealingRegistry_1.HealingRegistry.getInstance();
|
|
46
49
|
const proxyToActiveLocator = new WeakMap();
|
|
47
|
-
// Module-level resolver
|
|
50
|
+
// Module-level resolver - moduleExpect.ts uses this to unwrap a Fixwright
|
|
48
51
|
// locator proxy back to its live underlying Playwright Locator without
|
|
49
52
|
// needing access to the WeakMap directly.
|
|
50
53
|
function resolveFixwrightProxy(value) {
|
|
@@ -154,7 +157,7 @@ const GET_BY_METHODS = {
|
|
|
154
157
|
getByTitle: (args) => `internal:attr=[title=${JSON.stringify(args[0])}]`,
|
|
155
158
|
};
|
|
156
159
|
// ─────────────────────────────────────────────────────────────────
|
|
157
|
-
// buildLiveLocator
|
|
160
|
+
// buildLiveLocator - registry-aware
|
|
158
161
|
//
|
|
159
162
|
// NEW: resolves the selector through the HealingRegistry before
|
|
160
163
|
// constructing the Playwright Locator. Zero cost when no heal has
|
|
@@ -237,7 +240,7 @@ function buildHealedArgs(prop, args, timeout = 8000) {
|
|
|
237
240
|
// runWithHealArgument (unchanged from original)
|
|
238
241
|
// ─────────────────────────────────────────────────────────────────
|
|
239
242
|
async function runWithHealArgument(rawPage, prop, args, fullSelector, stableId, callerInfo, originalError) {
|
|
240
|
-
console.log(`[Sela] 💡 Element visible, action '${prop}' failed
|
|
243
|
+
console.log(`[Sela] 💡 Element visible, action '${prop}' failed - healing argument…`);
|
|
241
244
|
const oldArgument = typeof args[0] === "string" ? args[0] : JSON.stringify(args[0]);
|
|
242
245
|
const newArgument = await singleton_1.sharedEngine.healArgument(rawPage, fullSelector, prop, oldArgument, stableId, callerInfo.filePath, callerInfo.line);
|
|
243
246
|
if (!newArgument)
|
|
@@ -247,11 +250,11 @@ async function runWithHealArgument(rawPage, prop, args, fullSelector, stableId,
|
|
|
247
250
|
return await locator[prop](...healedArgs);
|
|
248
251
|
}
|
|
249
252
|
// ─────────────────────────────────────────────────────────────────
|
|
250
|
-
// runWithHeal
|
|
253
|
+
// runWithHeal - pre-flight registry check added
|
|
251
254
|
//
|
|
252
255
|
// NEW: Before the first Playwright attempt, resolve the selector
|
|
253
256
|
// through the registry. If a previous heal already mapped it, the
|
|
254
|
-
// first attempt uses the healed selector and succeeds immediately
|
|
257
|
+
// first attempt uses the healed selector and succeeds immediately -
|
|
255
258
|
// no timeout, no AI call, no file write.
|
|
256
259
|
// ─────────────────────────────────────────────────────────────────
|
|
257
260
|
async function runWithHeal(rawPage, prop, args, fullSelector, elementSelectorOnly, stableId, callerInfo) {
|
|
@@ -448,6 +451,10 @@ function createFrameLocatorProxy(rawFrameLocator, frameSelector, rawPage, testTi
|
|
|
448
451
|
: frameSelector;
|
|
449
452
|
return new Proxy(rawFrameLocator, {
|
|
450
453
|
get(fTarget, fProp) {
|
|
454
|
+
// Sela proxy identity - honor before the string-only fast path so
|
|
455
|
+
// symbol probes from tests / debuggers return the kind tag.
|
|
456
|
+
if (fProp === proxyTag_1.SELA_PROXY)
|
|
457
|
+
return "framelocator";
|
|
451
458
|
if (typeof fProp !== "string")
|
|
452
459
|
return Reflect.get(fTarget, fProp);
|
|
453
460
|
if (fProp === "frameLocator") {
|
|
@@ -461,13 +468,13 @@ function createFrameLocatorProxy(rawFrameLocator, frameSelector, rawPage, testTi
|
|
|
461
468
|
});
|
|
462
469
|
}
|
|
463
470
|
// ─────────────────────────────────────────────────────────────────
|
|
464
|
-
// createLocatorProxy
|
|
471
|
+
// createLocatorProxy - Runtime Selector Injection added
|
|
465
472
|
//
|
|
466
473
|
// NEW additions vs original:
|
|
467
474
|
// 1. Resolves selector through registry on creation (handles proxies
|
|
468
475
|
// born after a heal has already broadcast).
|
|
469
476
|
// 2. Subscribes to registry so any future broadcast swaps
|
|
470
|
-
// activeLocator on the same tick
|
|
477
|
+
// activeLocator on the same tick - before the next await.
|
|
471
478
|
// 3. Exposes _fixwrightUnsubscribe so the fixture can clean up.
|
|
472
479
|
// ─────────────────────────────────────────────────────────────────
|
|
473
480
|
function createLocatorProxy(rawLocator, selector, rawPage, testTitle, actionCounter, elementSelectorOnly) {
|
|
@@ -487,7 +494,7 @@ function createLocatorProxy(rawLocator, selector, rawPage, testTitle, actionCoun
|
|
|
487
494
|
? buildLiveLocator(rawPage, initialResolved) // already healed
|
|
488
495
|
: rawLocator; // fresh, use as-is
|
|
489
496
|
// ── Subscribe: swap activeLocator when a heal fires ──────────
|
|
490
|
-
// Inside createLocatorProxy
|
|
497
|
+
// Inside createLocatorProxy - full updated subscribe block
|
|
491
498
|
// בתוך createLocatorProxy - תחליף את הבלוק של ה-subscribe בזה:
|
|
492
499
|
const unsubscribe = registry.subscribe((fp, oldSel, newSel) => {
|
|
493
500
|
// בדיקה אם הסלקטור הנוכחי מכיל את הישן או שהוא הישן בעצמו
|
|
@@ -517,6 +524,10 @@ function createLocatorProxy(rawLocator, selector, rawPage, testTitle, actionCoun
|
|
|
517
524
|
});
|
|
518
525
|
const proxy = new Proxy(rawLocator, {
|
|
519
526
|
get(target, prop, receiver) {
|
|
527
|
+
// Sela proxy identity - must be honored before any string-only branch
|
|
528
|
+
// so callers can reliably detect a Sela proxy via Symbol.for("sela.proxy").
|
|
529
|
+
if (prop === proxyTag_1.SELA_PROXY)
|
|
530
|
+
return "locator";
|
|
520
531
|
if (typeof prop !== "string")
|
|
521
532
|
return Reflect.get(target, prop, receiver);
|
|
522
533
|
// ── Internal accessors ───────────────────────────────────
|
|
@@ -646,9 +657,9 @@ function createLocatorProxy(rawLocator, selector, rawPage, testTitle, actionCoun
|
|
|
646
657
|
return proxy;
|
|
647
658
|
}
|
|
648
659
|
// ─────────────────────────────────────────────────────────────────
|
|
649
|
-
// test fixture
|
|
660
|
+
// test fixture - teardown added
|
|
650
661
|
// ─────────────────────────────────────────────────────────────────
|
|
651
|
-
// ─── expect
|
|
662
|
+
// ─── expect - healing proxy replacing the plain baseExpect proxy ──────────────
|
|
652
663
|
exports.test = test_1.test.extend({
|
|
653
664
|
page: async ({ page }, use, testInfo) => {
|
|
654
665
|
page.on("console", (msg) => {
|
|
@@ -661,6 +672,10 @@ exports.test = test_1.test.extend({
|
|
|
661
672
|
const unsubscribers = [];
|
|
662
673
|
const pageProxy = new Proxy(page, {
|
|
663
674
|
get(target, prop) {
|
|
675
|
+
// Sela proxy identity - honored before any other branch so callers
|
|
676
|
+
// can reliably detect a Sela page proxy via Symbol.for("sela.proxy").
|
|
677
|
+
if (prop === proxyTag_1.SELA_PROXY)
|
|
678
|
+
return "page";
|
|
664
679
|
if (prop === "_rawPage")
|
|
665
680
|
return target;
|
|
666
681
|
if (typeof prop !== "string")
|
|
@@ -699,7 +714,7 @@ exports.test = test_1.test.extend({
|
|
|
699
714
|
},
|
|
700
715
|
expect: async ({ page }, use, testInfo) => {
|
|
701
716
|
const healingExpect = (0, expectProxy_1.createHealingExpect)(singleton_1.sharedEngine, page, testInfo.file, 0,
|
|
702
|
-
// Inject proxy resolver
|
|
717
|
+
// Inject proxy resolver - gives expectProxy.ts access to the live
|
|
703
718
|
// active Locator without exposing the WeakMap directly.
|
|
704
719
|
(value) => {
|
|
705
720
|
if (value && typeof value === "object") {
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export declare const SELA_PROXY: unique symbol;
|
|
2
|
+
export type SelaProxyKind = "locator" | "framelocator" | "page" | "expect";
|
|
3
|
+
/**
|
|
4
|
+
* Returns the proxy kind if `v` is a Sela proxy, otherwise `null`.
|
|
5
|
+
*
|
|
6
|
+
* Performs no instanceof checks against Playwright internals - relies
|
|
7
|
+
* solely on the `SELA_PROXY` symbol contract. Always safe to call on
|
|
8
|
+
* arbitrary values (null/undefined/primitive → null).
|
|
9
|
+
*/
|
|
10
|
+
export declare function selaProxyKind(v: unknown): SelaProxyKind | null;
|
|
11
|
+
export declare function isSelaProxy(v: unknown, kind?: SelaProxyKind): boolean;
|
|
12
|
+
//# sourceMappingURL=proxyTag.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"proxyTag.d.ts","sourceRoot":"","sources":["../../src/fixtures/proxyTag.ts"],"names":[],"mappings":"AAaA,eAAO,MAAM,UAAU,eAA2B,CAAC;AAEnD,MAAM,MAAM,aAAa,GAAG,SAAS,GAAG,cAAc,GAAG,MAAM,GAAG,QAAQ,CAAC;AAE3E;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,CAAC,EAAE,OAAO,GAAG,aAAa,GAAG,IAAI,CAa9D;AAED,wBAAgB,WAAW,CAAC,CAAC,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,aAAa,GAAG,OAAO,CAIrE"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// src/fixtures/proxyTag.ts
|
|
3
|
+
//
|
|
4
|
+
// Single source of truth for the Sela proxy tag.
|
|
5
|
+
//
|
|
6
|
+
// Every Sela-created Proxy (locator, frame-locator, page, expect) honors a
|
|
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 "_fixwrightSelector" heuristic.
|
|
10
|
+
//
|
|
11
|
+
// We use Symbol.for() (a registered symbol) so the identity survives
|
|
12
|
+
// across module boundaries when multiple bundles end up in the same
|
|
13
|
+
// process (vitest worker pools, side-loaded fixtures, etc.).
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.SELA_PROXY = void 0;
|
|
16
|
+
exports.selaProxyKind = selaProxyKind;
|
|
17
|
+
exports.isSelaProxy = isSelaProxy;
|
|
18
|
+
exports.SELA_PROXY = Symbol.for("sela.proxy");
|
|
19
|
+
/**
|
|
20
|
+
* Returns the proxy kind if `v` is a Sela proxy, otherwise `null`.
|
|
21
|
+
*
|
|
22
|
+
* Performs no instanceof checks against Playwright internals - relies
|
|
23
|
+
* solely on the `SELA_PROXY` symbol contract. Always safe to call on
|
|
24
|
+
* arbitrary values (null/undefined/primitive → null).
|
|
25
|
+
*/
|
|
26
|
+
function selaProxyKind(v) {
|
|
27
|
+
if (v === null || v === undefined)
|
|
28
|
+
return null;
|
|
29
|
+
if (typeof v !== "object" && typeof v !== "function")
|
|
30
|
+
return null;
|
|
31
|
+
const tag = v[exports.SELA_PROXY];
|
|
32
|
+
if (tag === "locator" ||
|
|
33
|
+
tag === "framelocator" ||
|
|
34
|
+
tag === "page" ||
|
|
35
|
+
tag === "expect") {
|
|
36
|
+
return tag;
|
|
37
|
+
}
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
function isSelaProxy(v, kind) {
|
|
41
|
+
const k = selaProxyKind(v);
|
|
42
|
+
if (k === null)
|
|
43
|
+
return false;
|
|
44
|
+
return kind === undefined ? true : k === kind;
|
|
45
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +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
|
|
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";
|
|
7
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"}
|