sela-core 1.0.8 → 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/commands/init.js +1 -1
- 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/index.js +4 -1
- package/dist/config/SelaConfig.d.ts +1 -1
- package/dist/config/SelaConfig.d.ts.map +1 -1
- package/dist/config/SelaConfig.js +1 -1
- package/dist/engine/SelaEngine.d.ts.map +1 -1
- package/dist/engine/SelaEngine.js +54 -2
- package/dist/errors/SelaError.d.ts +8 -0
- package/dist/errors/SelaError.d.ts.map +1 -1
- package/dist/errors/SelaError.js +8 -0
- package/dist/fixtures/expectProxy.d.ts +1 -1
- package/dist/fixtures/expectProxy.js +6 -6
- package/dist/fixtures/index.d.ts +18 -2
- package/dist/fixtures/index.d.ts.map +1 -1
- package/dist/fixtures/index.js +34 -14
- package/dist/fixtures/moduleExpect.js +7 -7
- package/dist/fixtures/proxyTag.js +1 -1
- package/dist/services/ASTSourceUpdater.d.ts +45 -0
- package/dist/services/ASTSourceUpdater.d.ts.map +1 -1
- package/dist/services/ASTSourceUpdater.js +201 -47
- 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/DecisionEngine.d.ts +51 -0
- package/dist/services/DecisionEngine.d.ts.map +1 -0
- package/dist/services/DecisionEngine.js +260 -0
- package/dist/services/HealReportService.d.ts +31 -55
- package/dist/services/HealReportService.d.ts.map +1 -1
- package/dist/services/HealReportService.js +863 -1243
- package/dist/services/HealingAdvisory.d.ts +9 -1
- package/dist/services/HealingAdvisory.d.ts.map +1 -1
- package/dist/services/HealingAdvisory.js +8 -0
- package/dist/services/InitializerUpdater.d.ts.map +1 -1
- package/dist/services/InitializerUpdater.js +10 -0
- package/dist/services/InteractiveReview.d.ts.map +1 -1
- package/dist/services/InteractiveReview.js +4 -1
- 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/PendingPromptLedger.d.ts +7 -0
- package/dist/services/PendingPromptLedger.d.ts.map +1 -1
- package/dist/services/PendingPromptLedger.js +25 -0
- package/dist/services/ReportGenerator.d.ts +116 -30
- package/dist/services/ReportGenerator.d.ts.map +1 -1
- package/dist/services/ReportGenerator.js +150 -63
- 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 +1 -1
- package/dist/services/SafetyGuard.d.ts.map +1 -1
- 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 +1 -1
- package/dist/services/SourceUpdater.d.ts.map +1 -1
- package/dist/services/SourceUpdater.js +17 -0
- 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/utils/DOMUtils.d.ts +18 -2
- package/dist/utils/DOMUtils.d.ts.map +1 -1
- package/dist/utils/DOMUtils.js +335 -49
- package/package.json +1 -1
|
@@ -1,4 +1,77 @@
|
|
|
1
1
|
import type { HealEvent, HealEventKind } from "./HealReportService";
|
|
2
|
+
export declare const TIME_SAVED_CONSTANTS: {
|
|
3
|
+
/** A healed selector saved an engineer from manually hunting + patching. */
|
|
4
|
+
readonly PER_HEAL: 12;
|
|
5
|
+
/**
|
|
6
|
+
* Catching a masked regression is worth more — it would have silently
|
|
7
|
+
* changed test semantics and required a full debugging cycle to uncover.
|
|
8
|
+
*/
|
|
9
|
+
readonly PER_PROTECT: 18;
|
|
10
|
+
/**
|
|
11
|
+
* Partial value: Sela narrowed the debug scope even when it couldn't fix.
|
|
12
|
+
*/
|
|
13
|
+
readonly PER_FAILED: 4;
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* Persisted after every run. Kept in a rolling window of up to 14 entries
|
|
17
|
+
* by HealReportService. Used to render the Management trend chart.
|
|
18
|
+
*
|
|
19
|
+
* **Backend addition required:** HealReportService.flushToDisk() reads
|
|
20
|
+
* `.sela-history.json`, appends the result of
|
|
21
|
+
* `ReportGenerator.buildCurrentHistoricalRun()`, trims to 14 entries, writes
|
|
22
|
+
* it back, and passes the array into `ReportGenerator.generate()`.
|
|
23
|
+
*/
|
|
24
|
+
export interface HistoricalRun {
|
|
25
|
+
/** Stable identifier — defaults to ISO timestamp but can be CI run ID. */
|
|
26
|
+
id: string;
|
|
27
|
+
/** Human-readable x-axis label shown in the trend chart (e.g. "Jan 15"). */
|
|
28
|
+
label: string;
|
|
29
|
+
healed: number;
|
|
30
|
+
protected: number;
|
|
31
|
+
failed: number;
|
|
32
|
+
/**
|
|
33
|
+
* Mean `aiConfidence` across all events in the run (0–100).
|
|
34
|
+
* Computed by `buildCurrentHistoricalRun()`.
|
|
35
|
+
* 0 when no events carried a confidence value.
|
|
36
|
+
*/
|
|
37
|
+
avg_confidence: number;
|
|
38
|
+
/** Estimated developer minutes saved this run. */
|
|
39
|
+
time_saved_min: number;
|
|
40
|
+
/** ISO 8601 — used for deduplication and display sorting. */
|
|
41
|
+
timestamp: string;
|
|
42
|
+
}
|
|
43
|
+
export interface RoiMetrics {
|
|
44
|
+
/**
|
|
45
|
+
* Estimated minutes of developer time saved in THIS run.
|
|
46
|
+
* Mirrors stats.timeSavedMin — kept here so callers don't need to import
|
|
47
|
+
* both interfaces.
|
|
48
|
+
*/
|
|
49
|
+
timeSavedThisRunMin: number;
|
|
50
|
+
/**
|
|
51
|
+
* Cumulative minutes saved across all historical runs (including current).
|
|
52
|
+
* The Management Dashboard renders this as "Total Time Saved This Month"
|
|
53
|
+
* when the history window covers ~30 days.
|
|
54
|
+
*/
|
|
55
|
+
timeSavedCumulativeMin: number;
|
|
56
|
+
/**
|
|
57
|
+
* Number of PROTECTED events in this run.
|
|
58
|
+
* Each represents a regression that would have silently mutated test
|
|
59
|
+
* semantics had Sela auto-applied the AI fix.
|
|
60
|
+
*/
|
|
61
|
+
bugsPrevented: number;
|
|
62
|
+
/**
|
|
63
|
+
* Percentage of all healing attempts (HEALED + PROTECTED) where the
|
|
64
|
+
* Intent Auditor's final confidence was ≥ 80. Rendered as the
|
|
65
|
+
* "High Confidence Rate" KPI in the Management view.
|
|
66
|
+
* `null` when there are no audited events.
|
|
67
|
+
*/
|
|
68
|
+
highConfidenceRatePct: number | null;
|
|
69
|
+
/**
|
|
70
|
+
* Mean aiConfidence across HEALED events only (0–100).
|
|
71
|
+
* `null` when there are no healed events with a confidence value.
|
|
72
|
+
*/
|
|
73
|
+
avgHealConfidence: number | null;
|
|
74
|
+
}
|
|
2
75
|
export interface ReportStats {
|
|
3
76
|
total: number;
|
|
4
77
|
healed: number;
|
|
@@ -6,64 +79,77 @@ export interface ReportStats {
|
|
|
6
79
|
protectedCnt: number;
|
|
7
80
|
iframeCnt: number;
|
|
8
81
|
shadowCnt: number;
|
|
9
|
-
/** Estimated developer
|
|
82
|
+
/** Estimated developer minutes saved this run. */
|
|
10
83
|
timeSavedMin: number;
|
|
11
84
|
}
|
|
12
|
-
/**
|
|
13
|
-
* The canonical JSON payload embedded into sela-report.html and
|
|
14
|
-
* mirrored verbatim into sela-report.json. Treat this as a public
|
|
15
|
-
* contract - additive changes only.
|
|
16
|
-
*/
|
|
17
85
|
export interface ReportData {
|
|
18
86
|
generatedAt: string;
|
|
19
87
|
startedAt: string;
|
|
20
88
|
cwd: string;
|
|
21
89
|
events: HealEvent[];
|
|
22
90
|
stats: ReportStats;
|
|
91
|
+
roi: RoiMetrics;
|
|
92
|
+
/**
|
|
93
|
+
* Rolling window of up to 14 runs injected from `.sela-history.json`.
|
|
94
|
+
* Empty array when no history file exists yet (first run).
|
|
95
|
+
*/
|
|
96
|
+
historical_runs: HistoricalRun[];
|
|
23
97
|
}
|
|
24
|
-
/**
|
|
25
|
-
* Time-saved heuristics (minutes per event). Exposed as a constant
|
|
26
|
-
* so tests pin the math against the same source as production.
|
|
27
|
-
*/
|
|
28
|
-
export declare const TIME_SAVED_CONSTANTS: {
|
|
29
|
-
readonly PER_HEAL: 12;
|
|
30
|
-
readonly PER_PROTECT: 18;
|
|
31
|
-
readonly PER_FAILED: 4;
|
|
32
|
-
};
|
|
33
98
|
export declare const MANDATORY_FIELDS_BY_KIND: Readonly<Record<HealEventKind, ReadonlyArray<string>>>;
|
|
34
99
|
export declare class ReportGenerator {
|
|
35
100
|
/**
|
|
36
|
-
* Validates a single event against MANDATORY_FIELDS_BY_KIND.
|
|
37
|
-
* a SelaError(subsystem="Report") on the first violation
|
|
38
|
-
* partial diagnosis: the report is all-or-nothing.
|
|
101
|
+
* Validates a single event against MANDATORY_FIELDS_BY_KIND.
|
|
102
|
+
* Throws a SelaError(subsystem="Report") on the first violation.
|
|
39
103
|
*/
|
|
40
104
|
static validateEvent(event: unknown): asserts event is HealEvent;
|
|
41
105
|
/**
|
|
42
|
-
* Validates every event in the buffer.
|
|
43
|
-
*
|
|
106
|
+
* Validates every event in the buffer. Returns the typed array when all
|
|
107
|
+
* events pass. Throws on the first violation.
|
|
44
108
|
*/
|
|
45
109
|
static validateAll(events: ReadonlyArray<unknown>): HealEvent[];
|
|
46
110
|
/**
|
|
47
|
-
* Pure
|
|
111
|
+
* Pure aggregate counters. No clock, no IO — trivially pinnable in tests.
|
|
48
112
|
*/
|
|
49
113
|
static computeStats(events: ReadonlyArray<HealEvent>): ReportStats;
|
|
50
114
|
/**
|
|
51
|
-
*
|
|
52
|
-
*
|
|
53
|
-
*
|
|
115
|
+
* Derives Management Dashboard KPIs from the event buffer + historical runs.
|
|
116
|
+
*
|
|
117
|
+
* `historicalRuns` should already include the current run if you want
|
|
118
|
+
* cumulative totals to include this run — pass the array AFTER appending
|
|
119
|
+
* via `buildCurrentHistoricalRun()`.
|
|
120
|
+
*/
|
|
121
|
+
static computeRoiMetrics(events: ReadonlyArray<HealEvent>, historicalRuns: ReadonlyArray<HistoricalRun>, stats: ReportStats): RoiMetrics;
|
|
122
|
+
/**
|
|
123
|
+
* Builds the `HistoricalRun` entry for the current run so the caller can
|
|
124
|
+
* append it to the persisted sidecar file.
|
|
125
|
+
*
|
|
126
|
+
* `runId` — optional override; defaults to ISO timestamp. Pass a CI run ID
|
|
127
|
+
* (e.g. `process.env.GITHUB_RUN_ID`) when available so deduplication is
|
|
128
|
+
* reliable across retries.
|
|
129
|
+
*/
|
|
130
|
+
static buildCurrentHistoricalRun(events: ReadonlyArray<HealEvent>, stats: ReportStats, opts?: {
|
|
131
|
+
runId?: string;
|
|
132
|
+
now?: () => Date;
|
|
133
|
+
}): HistoricalRun;
|
|
134
|
+
/**
|
|
135
|
+
* Validates every event, computes all metrics, and assembles the
|
|
136
|
+
* `ReportData` payload ready for serialisation.
|
|
137
|
+
*
|
|
138
|
+
* `historicalRuns` — pass the array from `.sela-history.json` BEFORE
|
|
139
|
+
* appending the current run; this method appends it internally so the
|
|
140
|
+
* trend chart always includes the present run.
|
|
54
141
|
*/
|
|
55
142
|
static generate(events: ReadonlyArray<unknown>, opts?: {
|
|
56
143
|
cwd?: string;
|
|
57
144
|
startedAt?: string;
|
|
145
|
+
historicalRuns?: HistoricalRun[];
|
|
146
|
+
runId?: string;
|
|
58
147
|
now?: () => Date;
|
|
59
148
|
}): ReportData;
|
|
60
149
|
/**
|
|
61
|
-
* Format a confidence value for UI consumption.
|
|
62
|
-
* percentage string ("87%") for finite numbers, "n/a" for
|
|
63
|
-
* else.
|
|
64
|
-
*
|
|
65
|
-
* Public so the HTML template's `fmtPct()` can be replaced by this
|
|
66
|
-
* single source of truth in a future refactor.
|
|
150
|
+
* Format a confidence value for UI consumption.
|
|
151
|
+
* Returns a percentage string ("87%") for finite numbers, "n/a" for
|
|
152
|
+
* anything else. NEVER emits literal "undefined" / "null" / "NaN".
|
|
67
153
|
*/
|
|
68
154
|
static formatConfidence(value: unknown): string;
|
|
69
155
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ReportGenerator.d.ts","sourceRoot":"","sources":["../../src/services/ReportGenerator.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"ReportGenerator.d.ts","sourceRoot":"","sources":["../../src/services/ReportGenerator.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,49 +1,46 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
// src/services/ReportGenerator.ts
|
|
3
3
|
//
|
|
4
|
-
// Pure (no-IO) report layer.
|
|
5
|
-
// and produces a stable, validated JSON shape that the HTML template
|
|
6
|
-
// and any external consumer (CI dashboards, telemetry forwarders)
|
|
7
|
-
// can rely on.
|
|
4
|
+
// Sela Insights — Pure (no-IO) report layer.
|
|
8
5
|
//
|
|
9
|
-
//
|
|
10
|
-
// 1. SCHEMA
|
|
11
|
-
//
|
|
12
|
-
//
|
|
13
|
-
//
|
|
14
|
-
//
|
|
15
|
-
//
|
|
16
|
-
//
|
|
17
|
-
//
|
|
18
|
-
//
|
|
19
|
-
// uses fmtPct() for the same reason; this module is the
|
|
20
|
-
// authoritative implementation.
|
|
21
|
-
//
|
|
22
|
-
// 3. STATS DETERMINISTIC. computeStats() exposes the time-saved
|
|
23
|
-
// constants via TIME_SAVED_CONSTANTS so tests can pin the math
|
|
24
|
-
// without duplicating the multipliers.
|
|
25
|
-
//
|
|
26
|
-
// 4. NO IO. Disk writes still live in HealReportService.flushToDisk
|
|
27
|
-
// so this module stays trivially unit-testable.
|
|
6
|
+
// Responsibilities:
|
|
7
|
+
// 1. SCHEMA VALIDATION — every event is validated against
|
|
8
|
+
// MANDATORY_FIELDS_BY_KIND before the HTML template ever sees it.
|
|
9
|
+
// 2. STATS + METRICS — computeStats(), computeRoiMetrics(), and
|
|
10
|
+
// buildCurrentHistoricalRun() are the single source of truth for
|
|
11
|
+
// every number rendered in the Management Dashboard.
|
|
12
|
+
// 3. CONFIDENCE SAFETY — formatConfidence() always emits "<n>%" or
|
|
13
|
+
// "n/a"; never "undefined", "null", or "NaN".
|
|
14
|
+
// 4. NO IO — disk operations stay in HealReportService so
|
|
15
|
+
// this module is trivially unit-testable without mocking fs.
|
|
28
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
29
17
|
exports.ReportGenerator = exports.MANDATORY_FIELDS_BY_KIND = exports.TIME_SAVED_CONSTANTS = void 0;
|
|
30
18
|
const SelaError_1 = require("../errors/SelaError");
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
19
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
20
|
+
// TIME-SAVED HEURISTICS (minutes per event kind)
|
|
21
|
+
// Exposed as a constant so tests can pin the math against the same multipliers
|
|
22
|
+
// as production without duplicating them.
|
|
23
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
35
24
|
exports.TIME_SAVED_CONSTANTS = {
|
|
25
|
+
/** A healed selector saved an engineer from manually hunting + patching. */
|
|
36
26
|
PER_HEAL: 12,
|
|
27
|
+
/**
|
|
28
|
+
* Catching a masked regression is worth more — it would have silently
|
|
29
|
+
* changed test semantics and required a full debugging cycle to uncover.
|
|
30
|
+
*/
|
|
37
31
|
PER_PROTECT: 18,
|
|
32
|
+
/**
|
|
33
|
+
* Partial value: Sela narrowed the debug scope even when it couldn't fix.
|
|
34
|
+
*/
|
|
38
35
|
PER_FAILED: 4,
|
|
39
36
|
};
|
|
40
|
-
//
|
|
41
|
-
//
|
|
37
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
38
|
+
// FIELD MANIFESTS — mandatory per event kind
|
|
42
39
|
//
|
|
43
|
-
// "Mandatory"
|
|
44
|
-
// without this field.
|
|
45
|
-
//
|
|
46
|
-
//
|
|
40
|
+
// "Mandatory" means the HTML card renderer produces a broken / misleading UI
|
|
41
|
+
// without this field. Optional fields belong in the interface declarations in
|
|
42
|
+
// HealReportService.ts but NOT here.
|
|
43
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
47
44
|
const COMMON_MANDATORY = [
|
|
48
45
|
"kind",
|
|
49
46
|
"stableId",
|
|
@@ -71,9 +68,9 @@ exports.MANDATORY_FIELDS_BY_KIND = {
|
|
|
71
68
|
PROTECTED: [...COMMON_MANDATORY, ...PROTECTED_MANDATORY],
|
|
72
69
|
};
|
|
73
70
|
const VALID_KINDS = new Set(["HEALED", "FAILED", "PROTECTED"]);
|
|
74
|
-
//
|
|
75
|
-
//
|
|
76
|
-
//
|
|
71
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
72
|
+
// HELPERS
|
|
73
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
77
74
|
function throwReport(code, reason, ctx = {}) {
|
|
78
75
|
throw new SelaError_1.SelaError({ subsystem: "Report", code, reason, context: ctx });
|
|
79
76
|
}
|
|
@@ -84,14 +81,28 @@ function isPresent(value) {
|
|
|
84
81
|
return false;
|
|
85
82
|
return true;
|
|
86
83
|
}
|
|
87
|
-
|
|
84
|
+
function shortDateLabel(iso) {
|
|
85
|
+
try {
|
|
86
|
+
const d = new Date(iso);
|
|
87
|
+
return d.toLocaleDateString("en-US", { month: "short", day: "numeric" });
|
|
88
|
+
}
|
|
89
|
+
catch {
|
|
90
|
+
return iso.slice(0, 10);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
function mean(values) {
|
|
94
|
+
if (values.length === 0)
|
|
95
|
+
return null;
|
|
96
|
+
return Math.round(values.reduce((a, b) => a + b, 0) / values.length);
|
|
97
|
+
}
|
|
98
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
88
99
|
// ReportGenerator
|
|
89
|
-
//
|
|
100
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
90
101
|
class ReportGenerator {
|
|
102
|
+
// ── Validation ────────────────────────────────────────────────────────────
|
|
91
103
|
/**
|
|
92
|
-
* Validates a single event against MANDATORY_FIELDS_BY_KIND.
|
|
93
|
-
* a SelaError(subsystem="Report") on the first violation
|
|
94
|
-
* partial diagnosis: the report is all-or-nothing.
|
|
104
|
+
* Validates a single event against MANDATORY_FIELDS_BY_KIND.
|
|
105
|
+
* Throws a SelaError(subsystem="Report") on the first violation.
|
|
95
106
|
*/
|
|
96
107
|
static validateEvent(event) {
|
|
97
108
|
if (!event || typeof event !== "object") {
|
|
@@ -99,7 +110,7 @@ class ReportGenerator {
|
|
|
99
110
|
}
|
|
100
111
|
const ev = event;
|
|
101
112
|
if (!ev.kind || !VALID_KINDS.has(ev.kind)) {
|
|
102
|
-
throwReport(SelaError_1.REPORT_CODES.UNKNOWN_KIND, `Unknown event.kind "${String(ev.kind)}"
|
|
113
|
+
throwReport(SelaError_1.REPORT_CODES.UNKNOWN_KIND, `Unknown event.kind "${String(ev.kind)}" — must be HEALED|FAILED|PROTECTED`, { stableId: ev.stableId });
|
|
103
114
|
}
|
|
104
115
|
const required = exports.MANDATORY_FIELDS_BY_KIND[ev.kind];
|
|
105
116
|
for (const field of required) {
|
|
@@ -108,29 +119,25 @@ class ReportGenerator {
|
|
|
108
119
|
throwReport(SelaError_1.REPORT_CODES.MISSING_FIELD, `${ev.kind} event missing mandatory field "${field}"`, { stableId: ev.stableId, kind: ev.kind, field });
|
|
109
120
|
}
|
|
110
121
|
}
|
|
111
|
-
// Field-type sanity for the fields we publicly mandate.
|
|
112
122
|
if (typeof ev.sourceLine !== "number") {
|
|
113
123
|
throwReport(SelaError_1.REPORT_CODES.INVALID_FIELD_TYPE, `Field "sourceLine" must be a number, received ${typeof ev.sourceLine}`, { stableId: ev.stableId, kind: ev.kind, field: "sourceLine" });
|
|
114
124
|
}
|
|
115
125
|
}
|
|
116
126
|
/**
|
|
117
|
-
* Validates every event in the buffer.
|
|
118
|
-
*
|
|
127
|
+
* Validates every event in the buffer. Returns the typed array when all
|
|
128
|
+
* events pass. Throws on the first violation.
|
|
119
129
|
*/
|
|
120
130
|
static validateAll(events) {
|
|
121
131
|
for (const e of events)
|
|
122
132
|
ReportGenerator.validateEvent(e);
|
|
123
133
|
return events;
|
|
124
134
|
}
|
|
135
|
+
// ── Stats ─────────────────────────────────────────────────────────────────
|
|
125
136
|
/**
|
|
126
|
-
* Pure
|
|
137
|
+
* Pure aggregate counters. No clock, no IO — trivially pinnable in tests.
|
|
127
138
|
*/
|
|
128
139
|
static computeStats(events) {
|
|
129
|
-
let healed = 0;
|
|
130
|
-
let failed = 0;
|
|
131
|
-
let protectedCnt = 0;
|
|
132
|
-
let iframeCnt = 0;
|
|
133
|
-
let shadowCnt = 0;
|
|
140
|
+
let healed = 0, failed = 0, protectedCnt = 0, iframeCnt = 0, shadowCnt = 0;
|
|
134
141
|
for (const e of events) {
|
|
135
142
|
if (e.kind === "HEALED")
|
|
136
143
|
healed++;
|
|
@@ -156,34 +163,114 @@ class ReportGenerator {
|
|
|
156
163
|
timeSavedMin,
|
|
157
164
|
};
|
|
158
165
|
}
|
|
166
|
+
// ── ROI Metrics ───────────────────────────────────────────────────────────
|
|
159
167
|
/**
|
|
160
|
-
*
|
|
161
|
-
*
|
|
162
|
-
*
|
|
168
|
+
* Derives Management Dashboard KPIs from the event buffer + historical runs.
|
|
169
|
+
*
|
|
170
|
+
* `historicalRuns` should already include the current run if you want
|
|
171
|
+
* cumulative totals to include this run — pass the array AFTER appending
|
|
172
|
+
* via `buildCurrentHistoricalRun()`.
|
|
173
|
+
*/
|
|
174
|
+
static computeRoiMetrics(events, historicalRuns, stats) {
|
|
175
|
+
// bugs prevented = all PROTECTED events this run
|
|
176
|
+
const bugsPrevented = stats.protectedCnt;
|
|
177
|
+
// cumulative time saved across all historical runs (includes current if
|
|
178
|
+
// caller appended it before passing here)
|
|
179
|
+
const timeSavedCumulativeMin = historicalRuns.reduce((sum, r) => sum + r.time_saved_min, 0);
|
|
180
|
+
// high-confidence rate: among events that have an auditor confidence,
|
|
181
|
+
// what fraction scored ≥ 80?
|
|
182
|
+
const audited = events.filter((e) => (e.kind === "HEALED" || e.kind === "PROTECTED") &&
|
|
183
|
+
e.auditor != null &&
|
|
184
|
+
typeof e.auditor.confidence === "number" &&
|
|
185
|
+
isFinite(e.auditor.confidence));
|
|
186
|
+
const highConfidenceRatePct = audited.length === 0
|
|
187
|
+
? null
|
|
188
|
+
: Math.round((audited.filter((e) => (e.auditor.confidence ?? 0) >= 80).length /
|
|
189
|
+
audited.length) *
|
|
190
|
+
100);
|
|
191
|
+
// avg heal confidence (HEALED events only)
|
|
192
|
+
const healedWithConf = events.filter((e) => e.kind === "HEALED")
|
|
193
|
+
.map((e) => e.aiConfidence)
|
|
194
|
+
.filter((v) => typeof v === "number" && isFinite(v));
|
|
195
|
+
const avgHealConfidence = mean(healedWithConf);
|
|
196
|
+
return {
|
|
197
|
+
timeSavedThisRunMin: stats.timeSavedMin,
|
|
198
|
+
timeSavedCumulativeMin,
|
|
199
|
+
bugsPrevented,
|
|
200
|
+
highConfidenceRatePct,
|
|
201
|
+
avgHealConfidence,
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
// ── Historical run builder ────────────────────────────────────────────────
|
|
205
|
+
/**
|
|
206
|
+
* Builds the `HistoricalRun` entry for the current run so the caller can
|
|
207
|
+
* append it to the persisted sidecar file.
|
|
208
|
+
*
|
|
209
|
+
* `runId` — optional override; defaults to ISO timestamp. Pass a CI run ID
|
|
210
|
+
* (e.g. `process.env.GITHUB_RUN_ID`) when available so deduplication is
|
|
211
|
+
* reliable across retries.
|
|
212
|
+
*/
|
|
213
|
+
static buildCurrentHistoricalRun(events, stats, opts = {}) {
|
|
214
|
+
const now = opts.now ? opts.now() : new Date();
|
|
215
|
+
const ts = now.toISOString();
|
|
216
|
+
const confidenceValues = events
|
|
217
|
+
.filter((e) => typeof e.aiConfidence === "number")
|
|
218
|
+
.map((e) => e.aiConfidence)
|
|
219
|
+
.filter((v) => isFinite(v));
|
|
220
|
+
const avg_confidence = confidenceValues.length > 0
|
|
221
|
+
? Math.round(confidenceValues.reduce((a, b) => a + b, 0) /
|
|
222
|
+
confidenceValues.length)
|
|
223
|
+
: 0;
|
|
224
|
+
return {
|
|
225
|
+
id: opts.runId ?? ts,
|
|
226
|
+
label: shortDateLabel(ts),
|
|
227
|
+
healed: stats.healed,
|
|
228
|
+
protected: stats.protectedCnt,
|
|
229
|
+
failed: stats.failed,
|
|
230
|
+
avg_confidence,
|
|
231
|
+
time_saved_min: stats.timeSavedMin,
|
|
232
|
+
timestamp: ts,
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
// ── Full payload builder ──────────────────────────────────────────────────
|
|
236
|
+
/**
|
|
237
|
+
* Validates every event, computes all metrics, and assembles the
|
|
238
|
+
* `ReportData` payload ready for serialisation.
|
|
239
|
+
*
|
|
240
|
+
* `historicalRuns` — pass the array from `.sela-history.json` BEFORE
|
|
241
|
+
* appending the current run; this method appends it internally so the
|
|
242
|
+
* trend chart always includes the present run.
|
|
163
243
|
*/
|
|
164
244
|
static generate(events, opts = {}) {
|
|
165
245
|
const validated = ReportGenerator.validateAll(events);
|
|
166
246
|
const now = opts.now ?? (() => new Date());
|
|
247
|
+
const stats = ReportGenerator.computeStats(validated);
|
|
248
|
+
const currentRun = ReportGenerator.buildCurrentHistoricalRun(validated, stats, { runId: opts.runId, now });
|
|
249
|
+
// Merge history: drop any prior entry with the same id, append current,
|
|
250
|
+
// then keep only the most recent HISTORY_WINDOW entries.
|
|
251
|
+
const HISTORY_WINDOW = 14;
|
|
252
|
+
const prior = (opts.historicalRuns ?? []).filter((r) => r.id !== currentRun.id);
|
|
253
|
+
const historical_runs = [...prior, currentRun].slice(-HISTORY_WINDOW);
|
|
254
|
+
const roi = ReportGenerator.computeRoiMetrics(validated, historical_runs, stats);
|
|
167
255
|
return {
|
|
168
256
|
generatedAt: now().toISOString(),
|
|
169
257
|
startedAt: opts.startedAt ?? now().toISOString(),
|
|
170
258
|
cwd: opts.cwd ?? process.cwd(),
|
|
171
259
|
events: validated,
|
|
172
|
-
stats
|
|
260
|
+
stats,
|
|
261
|
+
roi,
|
|
262
|
+
historical_runs,
|
|
173
263
|
};
|
|
174
264
|
}
|
|
265
|
+
// ── Confidence formatter ──────────────────────────────────────────────────
|
|
175
266
|
/**
|
|
176
|
-
* Format a confidence value for UI consumption.
|
|
177
|
-
* percentage string ("87%") for finite numbers, "n/a" for
|
|
178
|
-
* else.
|
|
179
|
-
*
|
|
180
|
-
* Public so the HTML template's `fmtPct()` can be replaced by this
|
|
181
|
-
* single source of truth in a future refactor.
|
|
267
|
+
* Format a confidence value for UI consumption.
|
|
268
|
+
* Returns a percentage string ("87%") for finite numbers, "n/a" for
|
|
269
|
+
* anything else. NEVER emits literal "undefined" / "null" / "NaN".
|
|
182
270
|
*/
|
|
183
271
|
static formatConfidence(value) {
|
|
184
272
|
if (typeof value === "number" && Number.isFinite(value)) {
|
|
185
|
-
|
|
186
|
-
return `${clamped}%`;
|
|
273
|
+
return `${Math.max(0, Math.min(100, Math.round(value)))}%`;
|
|
187
274
|
}
|
|
188
275
|
return "n/a";
|
|
189
276
|
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { type HistoricalRun, type ReportData } from "./ReportGenerator.js";
|
|
2
|
+
import { type HealEvent } from "./HealReportService.js";
|
|
3
|
+
export interface MergeOptions {
|
|
4
|
+
/**
|
|
5
|
+
* Explicit shard report files (`sela-report.json` shape, or a bare event
|
|
6
|
+
* array). When empty, the caller is expected to have discovered files via
|
|
7
|
+
* {@link discoverShardFiles}.
|
|
8
|
+
*/
|
|
9
|
+
shardFiles: string[];
|
|
10
|
+
/** Where the merged report + rolling history are written. */
|
|
11
|
+
targetDir: string;
|
|
12
|
+
/**
|
|
13
|
+
* Single dedup key for the merged history row. Defaults to
|
|
14
|
+
* GITHUB_RUN_ID ?? CI_PIPELINE_ID ?? ISO timestamp (resolved by
|
|
15
|
+
* ReportGenerator). Pass explicitly in CI so retries overwrite cleanly.
|
|
16
|
+
*/
|
|
17
|
+
runId?: string;
|
|
18
|
+
/**
|
|
19
|
+
* Number of shards the pipeline launched. When provided and more than the
|
|
20
|
+
* number of readable shard files, the merge is flagged as partial.
|
|
21
|
+
*/
|
|
22
|
+
expectedShards?: number;
|
|
23
|
+
/**
|
|
24
|
+
* Optional logger seam (tests inject a spy). Defaults to console.
|
|
25
|
+
*/
|
|
26
|
+
logger?: {
|
|
27
|
+
warn: (msg: string) => void;
|
|
28
|
+
info: (msg: string) => void;
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
export interface MergeResult {
|
|
32
|
+
htmlPath: string;
|
|
33
|
+
/** Total events read across all shard files BEFORE dedup. */
|
|
34
|
+
totalEvents: number;
|
|
35
|
+
/** Events surviving content-identity dedup (== payload event count). */
|
|
36
|
+
uniqueEvents: number;
|
|
37
|
+
/** totalEvents - uniqueEvents. */
|
|
38
|
+
duplicatesDropped: number;
|
|
39
|
+
/** Shard files successfully parsed. */
|
|
40
|
+
shardsRead: number;
|
|
41
|
+
/** Shard files that existed/were passed but failed to parse. */
|
|
42
|
+
shardsSkipped: number;
|
|
43
|
+
/** expectedShards echoed back, or null when not supplied. */
|
|
44
|
+
shardsExpected: number | null;
|
|
45
|
+
/** max(0, expectedShards - shardsRead); 0 when not supplied. */
|
|
46
|
+
shardsMissing: number;
|
|
47
|
+
/** True when shardsMissing > 0 — the report is built from partial data. */
|
|
48
|
+
partial: boolean;
|
|
49
|
+
/** Human-readable advisories surfaced during the merge. */
|
|
50
|
+
warnings: string[];
|
|
51
|
+
/** The full computed payload (handy for tests / programmatic callers). */
|
|
52
|
+
data: ReportData;
|
|
53
|
+
}
|
|
54
|
+
export declare class ReportMergeService {
|
|
55
|
+
/**
|
|
56
|
+
* Discover shard report files inside `dir` whose filename matches `glob`.
|
|
57
|
+
* Only a single `*` wildcard is supported (e.g. `sela-shard-*.json`) — we
|
|
58
|
+
* deliberately avoid pulling in a glob dependency. Returns absolute paths,
|
|
59
|
+
* sorted for deterministic ordering.
|
|
60
|
+
*/
|
|
61
|
+
static discoverShardFiles(dir: string, glob?: string): string[];
|
|
62
|
+
/**
|
|
63
|
+
* Content identity for an event. A retried test replayed on a different
|
|
64
|
+
* shard yields an identical identity and is collapsed to one. Timestamp is
|
|
65
|
+
* intentionally EXCLUDED — clock skew across runners must not defeat dedup.
|
|
66
|
+
*/
|
|
67
|
+
static eventIdentity(e: HealEvent): string;
|
|
68
|
+
/**
|
|
69
|
+
* Read one shard file and return its events array.
|
|
70
|
+
* Accepts the `sela-report.json` shape (`{ events: [...] }`) or a bare
|
|
71
|
+
* `HealEvent[]`. Throws SelaError(SHARD_UNREADABLE) on malformed input so
|
|
72
|
+
* the caller can decide whether a single bad shard is fatal (it is not —
|
|
73
|
+
* `merge()` catches and flags it).
|
|
74
|
+
*/
|
|
75
|
+
static readShardEvents(file: string): HealEvent[];
|
|
76
|
+
/**
|
|
77
|
+
* Read the prior rolling history with a STRICT integrity guard.
|
|
78
|
+
* • Absent file → [] (first run / clean bootstrap).
|
|
79
|
+
* • Present, valid JSON array → the array.
|
|
80
|
+
* • Present but NOT a JSON array (corrupt / truncated / wrong shape)
|
|
81
|
+
* → THROW.
|
|
82
|
+
*
|
|
83
|
+
* The throw is the whole point: in the artifact flow the prior history was
|
|
84
|
+
* just downloaded; if it is corrupt we must NOT overwrite the durable copy
|
|
85
|
+
* with a fresh 1-entry window. Aborting forces an operator to look instead
|
|
86
|
+
* of silently destroying the trend.
|
|
87
|
+
*/
|
|
88
|
+
static readPriorHistory(targetDir: string): HistoricalRun[];
|
|
89
|
+
/**
|
|
90
|
+
* Merge N shard reports into a single coherent history row + report.
|
|
91
|
+
* See the file header for the full rationale.
|
|
92
|
+
*/
|
|
93
|
+
static merge(opts: MergeOptions): MergeResult;
|
|
94
|
+
}
|
|
95
|
+
//# sourceMappingURL=ReportMergeService.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ReportMergeService.d.ts","sourceRoot":"","sources":["../../src/services/ReportMergeService.ts"],"names":[],"mappings":"AAyCA,OAAO,EAEL,KAAK,aAAa,EAClB,KAAK,UAAU,EAChB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAqB,KAAK,SAAS,EAAE,MAAM,wBAAwB,CAAC;AAQ3E,MAAM,WAAW,YAAY;IAC3B;;;;OAIG;IACH,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,6DAA6D;IAC7D,SAAS,EAAE,MAAM,CAAC;IAClB;;;;OAIG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;;OAGG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB;;OAEG;IACH,MAAM,CAAC,EAAE;QAAE,IAAI,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;QAAC,IAAI,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAA;KAAE,CAAC;CACvE;AAED,MAAM,WAAW,WAAW;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,6DAA6D;IAC7D,WAAW,EAAE,MAAM,CAAC;IACpB,wEAAwE;IACxE,YAAY,EAAE,MAAM,CAAC;IACrB,kCAAkC;IAClC,iBAAiB,EAAE,MAAM,CAAC;IAC1B,uCAAuC;IACvC,UAAU,EAAE,MAAM,CAAC;IACnB,gEAAgE;IAChE,aAAa,EAAE,MAAM,CAAC;IACtB,6DAA6D;IAC7D,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,gEAAgE;IAChE,aAAa,EAAE,MAAM,CAAC;IACtB,2EAA2E;IAC3E,OAAO,EAAE,OAAO,CAAC;IACjB,2DAA2D;IAC3D,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,0EAA0E;IAC1E,IAAI,EAAE,UAAU,CAAC;CAClB;AAMD,qBAAa,kBAAkB;IAC7B;;;;;OAKG;IACH,MAAM,CAAC,kBAAkB,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,SAAsB,GAAG,MAAM,EAAE;IAe5E;;;;OAIG;IACH,MAAM,CAAC,aAAa,CAAC,CAAC,EAAE,SAAS,GAAG,MAAM;IAO1C;;;;;;OAMG;IACH,MAAM,CAAC,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,EAAE;IA2CjD;;;;;;;;;;;OAWG;IACH,MAAM,CAAC,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,aAAa,EAAE;IAuC3D;;;OAGG;IACH,MAAM,CAAC,KAAK,CAAC,IAAI,EAAE,YAAY,GAAG,WAAW;CA6F9C"}
|
|
Binary file
|
|
@@ -51,7 +51,7 @@ export interface SafetyDecision {
|
|
|
51
51
|
* REQUIRES_REVIEW) may omit it.
|
|
52
52
|
*/
|
|
53
53
|
code?: SafetyGuardCode;
|
|
54
|
-
/** Structured info for policy-aware log messages in
|
|
54
|
+
/** Structured info for policy-aware log messages in SelaEngine. */
|
|
55
55
|
meta?: {
|
|
56
56
|
auditorVerdict?: "CONSISTENT" | "SUSPICIOUS" | "INCONSISTENT";
|
|
57
57
|
auditorConfidence?: number;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SafetyGuard.d.ts","sourceRoot":"","sources":["../../src/services/SafetyGuard.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AACpE,OAAO,EAAsB,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAM1E,MAAM,MAAM,QAAQ,GAAG,WAAW,GAAG,QAAQ,CAAC;AAE9C,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,QAAQ,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,yEAAyE;IACzE,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,YAAY;IAC3B;;;;;OAKG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,aAAa,CAAC,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;IACrD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,4EAA4E;IAC5E,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,4EAA4E;IAC5E,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,oEAAoE;IACpE,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB,0EAA0E;IAC1E,cAAc,CAAC,EAAE,cAAc,CAAC;IAChC,uEAAuE;IACvE,YAAY,CAAC,EAAE,YAAY,EAAE,CAAC;IAC9B,6DAA6D;IAC7D,WAAW,CAAC,EAAE,UAAU,CAAC;IACzB,4CAA4C;IAC5C,WAAW,CAAC,EAAE,YAAY,EAAE,CAAC;IAC7B,+CAA+C;IAC/C,UAAU,CAAC,EAAE,UAAU,CAAC;CACzB;AAED,MAAM,MAAM,SAAS,GAAG,MAAM,GAAG,iBAAiB,GAAG,WAAW,GAAG,SAAS,CAAC;AAE7E,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,SAAS,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,OAAO,CAAC;IACpB;;;;;OAKG;IACH,IAAI,CAAC,EAAE,eAAe,CAAC;IACvB,
|
|
1
|
+
{"version":3,"file":"SafetyGuard.d.ts","sourceRoot":"","sources":["../../src/services/SafetyGuard.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AACpE,OAAO,EAAsB,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAM1E,MAAM,MAAM,QAAQ,GAAG,WAAW,GAAG,QAAQ,CAAC;AAE9C,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,QAAQ,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,yEAAyE;IACzE,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,YAAY;IAC3B;;;;;OAKG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,aAAa,CAAC,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;IACrD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,4EAA4E;IAC5E,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,4EAA4E;IAC5E,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,oEAAoE;IACpE,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB,0EAA0E;IAC1E,cAAc,CAAC,EAAE,cAAc,CAAC;IAChC,uEAAuE;IACvE,YAAY,CAAC,EAAE,YAAY,EAAE,CAAC;IAC9B,6DAA6D;IAC7D,WAAW,CAAC,EAAE,UAAU,CAAC;IACzB,4CAA4C;IAC5C,WAAW,CAAC,EAAE,YAAY,EAAE,CAAC;IAC7B,+CAA+C;IAC/C,UAAU,CAAC,EAAE,UAAU,CAAC;CACzB;AAED,MAAM,MAAM,SAAS,GAAG,MAAM,GAAG,iBAAiB,GAAG,WAAW,GAAG,SAAS,CAAC;AAE7E,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,SAAS,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,OAAO,CAAC;IACpB;;;;;OAKG;IACH,IAAI,CAAC,EAAE,eAAe,CAAC;IACvB,mEAAmE;IACnE,IAAI,CAAC,EAAE;QACL,cAAc,CAAC,EAAE,YAAY,GAAG,YAAY,GAAG,cAAc,CAAC;QAC9D,iBAAiB,CAAC,EAAE,MAAM,CAAC;QAC3B,qFAAqF;QACrF,mBAAmB,CAAC,EAAE;YACpB,aAAa,EAAE,MAAM,CAAC;YACtB,OAAO,EAAE,MAAM,CAAC;YAChB,KAAK,EAAE,MAAM,CAAC;YACd,kBAAkB,EAAE,MAAM,CAAC;SAC5B,CAAC;KACH,CAAC;CACH;AA4DD,KAAK,cAAc,GACf,QAAQ,GACR,QAAQ,GACR,QAAQ,GACR,UAAU,GACV,MAAM,GACN,iBAAiB,GACjB,iBAAiB,GACjB,SAAS,CAAC;AA6Jd;;;;;;GAMG;AACH,MAAM,WAAW,kBAAkB;IACjC,UAAU,CAAC,EAAE,kBAAkB,CAAC;IAChC,aAAa,CAAC,EAAE,aAAa,CAAC;CAC/B;AAED,qBAAa,WAAW;IACtB,OAAO,CAAC,aAAa,CAAgB;IACrC,OAAO,CAAC,UAAU,CAAqB;IAEvC;;;;OAIG;gBACS,GAAG,CAAC,EAAE,kBAAkB,GAAG,kBAAkB;IAMzD,OAAO,CAAC,MAAM,CAAC,aAAa;IAgBtB,QAAQ,CACZ,OAAO,EAAE,WAAW,EACpB,KAAK,EAAE,YAAY,GAClB,OAAO,CAAC,cAAc,CAAC;IAyS1B,OAAO,CAAC,kBAAkB;IAwB1B,sBAAsB,CAAC,IAAI,EAAE,MAAM,GAAG,cAAc;CAUrD"}
|