@veraxhq/verax 0.2.1 → 0.4.0
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 +10 -6
- package/bin/verax.js +11 -11
- package/package.json +29 -8
- package/src/cli/commands/baseline.js +103 -0
- package/src/cli/commands/default.js +51 -6
- package/src/cli/commands/doctor.js +29 -0
- package/src/cli/commands/ga.js +246 -0
- package/src/cli/commands/gates.js +95 -0
- package/src/cli/commands/inspect.js +4 -2
- package/src/cli/commands/release-check.js +215 -0
- package/src/cli/commands/run.js +45 -6
- package/src/cli/commands/security-check.js +212 -0
- package/src/cli/commands/truth.js +113 -0
- package/src/cli/entry.js +30 -20
- package/src/cli/util/angular-component-extractor.js +179 -0
- package/src/cli/util/angular-navigation-detector.js +141 -0
- package/src/cli/util/angular-network-detector.js +161 -0
- package/src/cli/util/angular-state-detector.js +162 -0
- package/src/cli/util/ast-interactive-detector.js +544 -0
- package/src/cli/util/ast-network-detector.js +603 -0
- package/src/cli/util/ast-promise-extractor.js +581 -0
- package/src/cli/util/ast-usestate-detector.js +602 -0
- package/src/cli/util/atomic-write.js +12 -1
- package/src/cli/util/bootstrap-guard.js +86 -0
- package/src/cli/util/console-reporter.js +72 -0
- package/src/cli/util/detection-engine.js +105 -41
- package/src/cli/util/determinism-runner.js +124 -0
- package/src/cli/util/determinism-writer.js +129 -0
- package/src/cli/util/digest-engine.js +359 -0
- package/src/cli/util/dom-diff.js +226 -0
- package/src/cli/util/evidence-engine.js +287 -0
- package/src/cli/util/expectation-extractor.js +151 -5
- package/src/cli/util/findings-writer.js +3 -0
- package/src/cli/util/framework-detector.js +572 -0
- package/src/cli/util/idgen.js +1 -1
- package/src/cli/util/interaction-planner.js +529 -0
- package/src/cli/util/learn-writer.js +2 -0
- package/src/cli/util/ledger-writer.js +110 -0
- package/src/cli/util/monorepo-resolver.js +162 -0
- package/src/cli/util/observation-engine.js +127 -278
- package/src/cli/util/observe-writer.js +2 -0
- package/src/cli/util/project-discovery.js +284 -0
- package/src/cli/util/project-writer.js +2 -0
- package/src/cli/util/run-id.js +23 -27
- package/src/cli/util/run-resolver.js +64 -0
- package/src/cli/util/run-result.js +778 -0
- package/src/cli/util/selector-resolver.js +235 -0
- package/src/cli/util/source-requirement.js +55 -0
- package/src/cli/util/summary-writer.js +2 -0
- package/src/cli/util/svelte-navigation-detector.js +163 -0
- package/src/cli/util/svelte-network-detector.js +80 -0
- package/src/cli/util/svelte-sfc-extractor.js +146 -0
- package/src/cli/util/svelte-state-detector.js +242 -0
- package/src/cli/util/trust-activation-integration.js +496 -0
- package/src/cli/util/trust-activation-wrapper.js +85 -0
- package/src/cli/util/trust-integration-hooks.js +164 -0
- package/src/cli/util/types.js +153 -0
- package/src/cli/util/url-validation.js +40 -0
- package/src/cli/util/vue-navigation-detector.js +178 -0
- package/src/cli/util/vue-sfc-extractor.js +161 -0
- package/src/cli/util/vue-state-detector.js +215 -0
- package/src/types/fs-augment.d.ts +23 -0
- package/src/types/global.d.ts +137 -0
- package/src/types/internal-types.d.ts +35 -0
- package/src/verax/cli/init.js +4 -18
- package/src/verax/core/action-classifier.js +4 -3
- package/src/verax/core/artifacts/registry.js +139 -0
- package/src/verax/core/artifacts/verifier.js +990 -0
- package/src/verax/core/baseline/baseline.enforcer.js +137 -0
- package/src/verax/core/baseline/baseline.snapshot.js +233 -0
- package/src/verax/core/capabilities/gates.js +505 -0
- package/src/verax/core/capabilities/registry.js +475 -0
- package/src/verax/core/confidence/confidence-compute.js +144 -0
- package/src/verax/core/confidence/confidence-invariants.js +234 -0
- package/src/verax/core/confidence/confidence-report-writer.js +112 -0
- package/src/verax/core/confidence/confidence-weights.js +44 -0
- package/src/verax/core/confidence/confidence.defaults.js +65 -0
- package/src/verax/core/confidence/confidence.loader.js +80 -0
- package/src/verax/core/confidence/confidence.schema.js +94 -0
- package/src/verax/core/confidence-engine-refactor.js +489 -0
- package/src/verax/core/confidence-engine.js +625 -0
- package/src/verax/core/contracts/index.js +29 -0
- package/src/verax/core/contracts/types.js +186 -0
- package/src/verax/core/contracts/validators.js +456 -0
- package/src/verax/core/decisions/decision.trace.js +278 -0
- package/src/verax/core/determinism/contract-writer.js +89 -0
- package/src/verax/core/determinism/contract.js +139 -0
- package/src/verax/core/determinism/diff.js +405 -0
- package/src/verax/core/determinism/engine.js +222 -0
- package/src/verax/core/determinism/finding-identity.js +149 -0
- package/src/verax/core/determinism/normalize.js +466 -0
- package/src/verax/core/determinism/report-writer.js +93 -0
- package/src/verax/core/determinism/run-fingerprint.js +123 -0
- package/src/verax/core/dynamic-route-intelligence.js +529 -0
- package/src/verax/core/evidence/evidence-capture-service.js +308 -0
- package/src/verax/core/evidence/evidence-intent-ledger.js +166 -0
- package/src/verax/core/evidence-builder.js +487 -0
- package/src/verax/core/execution-mode-context.js +77 -0
- package/src/verax/core/execution-mode-detector.js +192 -0
- package/src/verax/core/failures/exit-codes.js +88 -0
- package/src/verax/core/failures/failure-summary.js +76 -0
- package/src/verax/core/failures/failure.factory.js +225 -0
- package/src/verax/core/failures/failure.ledger.js +133 -0
- package/src/verax/core/failures/failure.types.js +196 -0
- package/src/verax/core/failures/index.js +10 -0
- package/src/verax/core/ga/ga-report-writer.js +43 -0
- package/src/verax/core/ga/ga.artifact.js +49 -0
- package/src/verax/core/ga/ga.contract.js +435 -0
- package/src/verax/core/ga/ga.enforcer.js +87 -0
- package/src/verax/core/guardrails/guardrails-report-writer.js +109 -0
- package/src/verax/core/guardrails/policy.defaults.js +210 -0
- package/src/verax/core/guardrails/policy.loader.js +84 -0
- package/src/verax/core/guardrails/policy.schema.js +110 -0
- package/src/verax/core/guardrails/truth-reconciliation.js +136 -0
- package/src/verax/core/guardrails-engine.js +505 -0
- package/src/verax/core/incremental-store.js +1 -0
- package/src/verax/core/integrity/budget.js +138 -0
- package/src/verax/core/integrity/determinism.js +342 -0
- package/src/verax/core/integrity/integrity.js +208 -0
- package/src/verax/core/integrity/poisoning.js +108 -0
- package/src/verax/core/integrity/transaction.js +140 -0
- package/src/verax/core/observe/run-timeline.js +318 -0
- package/src/verax/core/perf/perf.contract.js +186 -0
- package/src/verax/core/perf/perf.display.js +65 -0
- package/src/verax/core/perf/perf.enforcer.js +91 -0
- package/src/verax/core/perf/perf.monitor.js +209 -0
- package/src/verax/core/perf/perf.report.js +200 -0
- package/src/verax/core/pipeline-tracker.js +243 -0
- package/src/verax/core/product-definition.js +127 -0
- package/src/verax/core/release/provenance.builder.js +130 -0
- package/src/verax/core/release/release-report-writer.js +40 -0
- package/src/verax/core/release/release.enforcer.js +164 -0
- package/src/verax/core/release/reproducibility.check.js +222 -0
- package/src/verax/core/release/sbom.builder.js +292 -0
- package/src/verax/core/replay-validator.js +2 -0
- package/src/verax/core/replay.js +4 -0
- package/src/verax/core/report/cross-index.js +195 -0
- package/src/verax/core/report/human-summary.js +362 -0
- package/src/verax/core/route-intelligence.js +420 -0
- package/src/verax/core/run-id.js +6 -3
- package/src/verax/core/run-manifest.js +4 -3
- package/src/verax/core/security/secrets.scan.js +329 -0
- package/src/verax/core/security/security-report.js +50 -0
- package/src/verax/core/security/security.enforcer.js +128 -0
- package/src/verax/core/security/supplychain.defaults.json +38 -0
- package/src/verax/core/security/supplychain.policy.js +334 -0
- package/src/verax/core/security/vuln.scan.js +265 -0
- package/src/verax/core/truth/truth.certificate.js +252 -0
- package/src/verax/core/ui-feedback-intelligence.js +481 -0
- package/src/verax/detect/conditional-ui-silent-failure.js +84 -0
- package/src/verax/detect/confidence-engine.js +62 -34
- package/src/verax/detect/confidence-helper.js +34 -0
- package/src/verax/detect/dynamic-route-findings.js +338 -0
- package/src/verax/detect/expectation-chain-detector.js +417 -0
- package/src/verax/detect/expectation-model.js +2 -2
- package/src/verax/detect/failure-cause-inference.js +293 -0
- package/src/verax/detect/findings-writer.js +131 -35
- package/src/verax/detect/flow-detector.js +2 -2
- package/src/verax/detect/form-silent-failure.js +98 -0
- package/src/verax/detect/index.js +46 -5
- package/src/verax/detect/invariants-enforcer.js +147 -0
- package/src/verax/detect/journey-stall-detector.js +558 -0
- package/src/verax/detect/navigation-silent-failure.js +82 -0
- package/src/verax/detect/problem-aggregator.js +361 -0
- package/src/verax/detect/route-findings.js +219 -0
- package/src/verax/detect/summary-writer.js +477 -0
- package/src/verax/detect/test-failure-cause-inference.js +314 -0
- package/src/verax/detect/ui-feedback-findings.js +207 -0
- package/src/verax/detect/view-switch-correlator.js +242 -0
- package/src/verax/flow/flow-engine.js +2 -1
- package/src/verax/flow/flow-spec.js +0 -6
- package/src/verax/index.js +4 -0
- package/src/verax/intel/ts-program.js +1 -0
- package/src/verax/intel/vue-navigation-extractor.js +3 -0
- package/src/verax/learn/action-contract-extractor.js +3 -0
- package/src/verax/learn/ast-contract-extractor.js +1 -1
- package/src/verax/learn/flow-extractor.js +1 -0
- package/src/verax/learn/project-detector.js +5 -0
- package/src/verax/learn/react-router-extractor.js +2 -0
- package/src/verax/learn/source-instrumenter.js +1 -0
- package/src/verax/learn/state-extractor.js +2 -1
- package/src/verax/learn/static-extractor.js +1 -0
- package/src/verax/observe/coverage-gaps.js +132 -0
- package/src/verax/observe/expectation-handler.js +126 -0
- package/src/verax/observe/incremental-skip.js +46 -0
- package/src/verax/observe/index.js +51 -155
- package/src/verax/observe/interaction-executor.js +192 -0
- package/src/verax/observe/interaction-runner.js +782 -513
- package/src/verax/observe/network-firewall.js +86 -0
- package/src/verax/observe/observation-builder.js +169 -0
- package/src/verax/observe/observe-context.js +205 -0
- package/src/verax/observe/observe-helpers.js +192 -0
- package/src/verax/observe/observe-runner.js +230 -0
- package/src/verax/observe/observers/budget-observer.js +185 -0
- package/src/verax/observe/observers/console-observer.js +102 -0
- package/src/verax/observe/observers/coverage-observer.js +107 -0
- package/src/verax/observe/observers/interaction-observer.js +471 -0
- package/src/verax/observe/observers/navigation-observer.js +132 -0
- package/src/verax/observe/observers/network-observer.js +87 -0
- package/src/verax/observe/observers/safety-observer.js +82 -0
- package/src/verax/observe/observers/ui-feedback-observer.js +99 -0
- package/src/verax/observe/page-traversal.js +138 -0
- package/src/verax/observe/snapshot-ops.js +94 -0
- package/src/verax/observe/ui-feedback-detector.js +742 -0
- package/src/verax/scan-summary-writer.js +2 -0
- package/src/verax/shared/artifact-manager.js +25 -5
- package/src/verax/shared/caching.js +1 -0
- package/src/verax/shared/css-spinner-rules.js +204 -0
- package/src/verax/shared/expectation-tracker.js +1 -0
- package/src/verax/shared/view-switch-rules.js +208 -0
- package/src/verax/shared/zip-artifacts.js +6 -0
- package/src/verax/shared/config-loader.js +0 -169
- /package/src/verax/shared/{expectation-proof.js → expectation-validation.js} +0 -0
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PHASE 21.9 — Performance Enforcer
|
|
3
|
+
*
|
|
4
|
+
* Records performance violations in failure ledger and blocks GA/Release.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { loadPerformanceReport } from './perf.report.js';
|
|
8
|
+
import { createInternalFailure } from '../failures/failure.factory.js';
|
|
9
|
+
import { FAILURE_CODE, EXECUTION_PHASE } from '../failures/failure.types.js';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Record performance violations in failure ledger
|
|
13
|
+
*
|
|
14
|
+
* @param {string} projectDir - Project directory
|
|
15
|
+
* @param {string} runId - Run ID
|
|
16
|
+
* @param {Object} failureLedger - Failure ledger instance
|
|
17
|
+
*/
|
|
18
|
+
export function recordPerformanceViolations(projectDir, runId, failureLedger) {
|
|
19
|
+
const report = loadPerformanceReport(projectDir, runId);
|
|
20
|
+
|
|
21
|
+
if (!report) {
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Record BLOCKING violations
|
|
26
|
+
for (const violation of report.violations) {
|
|
27
|
+
const failure = createInternalFailure(
|
|
28
|
+
FAILURE_CODE.INTERNAL_UNEXPECTED_ERROR,
|
|
29
|
+
violation.message,
|
|
30
|
+
'perf.enforcer',
|
|
31
|
+
{
|
|
32
|
+
type: violation.type,
|
|
33
|
+
actual: violation.actual,
|
|
34
|
+
budget: violation.budget,
|
|
35
|
+
excess: violation.excess
|
|
36
|
+
},
|
|
37
|
+
EXECUTION_PHASE.RUNTIME
|
|
38
|
+
);
|
|
39
|
+
failureLedger.record(failure);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Record DEGRADED warnings
|
|
43
|
+
for (const warning of report.warnings) {
|
|
44
|
+
const failure = createInternalFailure(
|
|
45
|
+
FAILURE_CODE.INTERNAL_UNEXPECTED_ERROR,
|
|
46
|
+
warning.message,
|
|
47
|
+
'perf.enforcer',
|
|
48
|
+
{
|
|
49
|
+
type: warning.type,
|
|
50
|
+
actual: warning.actual,
|
|
51
|
+
budget: warning.budget,
|
|
52
|
+
excess: warning.excess
|
|
53
|
+
},
|
|
54
|
+
EXECUTION_PHASE.RUNTIME
|
|
55
|
+
);
|
|
56
|
+
failure.severity = 'DEGRADED';
|
|
57
|
+
failureLedger.record(failure);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Check performance status for GA/Release
|
|
63
|
+
*
|
|
64
|
+
* @param {string} projectDir - Project directory
|
|
65
|
+
* @param {string} runId - Run ID
|
|
66
|
+
* @returns {Object} Performance status
|
|
67
|
+
*/
|
|
68
|
+
export function checkPerformanceStatus(projectDir, runId) {
|
|
69
|
+
const report = loadPerformanceReport(projectDir, runId);
|
|
70
|
+
|
|
71
|
+
if (!report) {
|
|
72
|
+
return {
|
|
73
|
+
exists: false,
|
|
74
|
+
ok: false,
|
|
75
|
+
verdict: 'UNKNOWN',
|
|
76
|
+
blockers: ['Performance report not found']
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const hasBlocking = report.violations.length > 0;
|
|
81
|
+
const hasDegraded = report.warnings.length > 0;
|
|
82
|
+
|
|
83
|
+
return {
|
|
84
|
+
exists: true,
|
|
85
|
+
ok: !hasBlocking,
|
|
86
|
+
verdict: report.verdict,
|
|
87
|
+
blockers: hasBlocking ? report.violations.map(v => v.message) : [],
|
|
88
|
+
warnings: hasDegraded ? report.warnings.map(w => w.message) : []
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PHASE 21.9 — Performance Monitor
|
|
3
|
+
*
|
|
4
|
+
* Monitors runtime, memory, event loop delay, and execution counters.
|
|
5
|
+
* Records timestamped samples and peak values.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { performance } from 'perf_hooks';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Performance Monitor
|
|
12
|
+
*/
|
|
13
|
+
export class PerformanceMonitor {
|
|
14
|
+
constructor() {
|
|
15
|
+
this.startTime = performance.now();
|
|
16
|
+
this.startMemory = process.memoryUsage();
|
|
17
|
+
this.samples = [];
|
|
18
|
+
this.peakMemoryRSS = this.startMemory.rss;
|
|
19
|
+
this.peakMemoryHeapUsed = this.startMemory.heapUsed;
|
|
20
|
+
this.peakMemoryHeapTotal = this.startMemory.heapTotal;
|
|
21
|
+
this.peakEventLoopDelay = 0;
|
|
22
|
+
|
|
23
|
+
// Counters
|
|
24
|
+
this.pagesVisited = 0;
|
|
25
|
+
this.interactionsExecuted = 0;
|
|
26
|
+
|
|
27
|
+
// Phase tracking
|
|
28
|
+
this.phaseStartTimes = {};
|
|
29
|
+
this.phaseDurations = {};
|
|
30
|
+
|
|
31
|
+
// Event loop monitoring
|
|
32
|
+
this.eventLoopMonitor = null;
|
|
33
|
+
this.lastEventLoopCheck = performance.now();
|
|
34
|
+
this.eventLoopDelays = [];
|
|
35
|
+
|
|
36
|
+
this.startEventLoopMonitoring();
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Start event loop delay monitoring
|
|
41
|
+
*/
|
|
42
|
+
startEventLoopMonitoring() {
|
|
43
|
+
const checkInterval = 100; // Check every 100ms
|
|
44
|
+
|
|
45
|
+
this.eventLoopMonitor = setInterval(() => {
|
|
46
|
+
const now = performance.now();
|
|
47
|
+
const delay = now - this.lastEventLoopCheck - checkInterval;
|
|
48
|
+
|
|
49
|
+
if (delay > 0) {
|
|
50
|
+
this.eventLoopDelays.push(delay);
|
|
51
|
+
if (delay > this.peakEventLoopDelay) {
|
|
52
|
+
this.peakEventLoopDelay = delay;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
this.lastEventLoopCheck = now;
|
|
57
|
+
}, checkInterval);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Record a sample
|
|
62
|
+
*/
|
|
63
|
+
sample(phase = null) {
|
|
64
|
+
const now = performance.now();
|
|
65
|
+
const memory = process.memoryUsage();
|
|
66
|
+
|
|
67
|
+
// Update peaks
|
|
68
|
+
if (memory.rss > this.peakMemoryRSS) {
|
|
69
|
+
this.peakMemoryRSS = memory.rss;
|
|
70
|
+
}
|
|
71
|
+
if (memory.heapUsed > this.peakMemoryHeapUsed) {
|
|
72
|
+
this.peakMemoryHeapUsed = memory.heapUsed;
|
|
73
|
+
}
|
|
74
|
+
if (memory.heapTotal > this.peakMemoryHeapTotal) {
|
|
75
|
+
this.peakMemoryHeapTotal = memory.heapTotal;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Calculate average event loop delay
|
|
79
|
+
const avgEventLoopDelay = this.eventLoopDelays.length > 0
|
|
80
|
+
? this.eventLoopDelays.reduce((a, b) => a + b, 0) / this.eventLoopDelays.length
|
|
81
|
+
: 0;
|
|
82
|
+
|
|
83
|
+
this.samples.push({
|
|
84
|
+
timestamp: now,
|
|
85
|
+
elapsedMs: now - this.startTime,
|
|
86
|
+
phase,
|
|
87
|
+
memory: {
|
|
88
|
+
rss: memory.rss,
|
|
89
|
+
heapUsed: memory.heapUsed,
|
|
90
|
+
heapTotal: memory.heapTotal,
|
|
91
|
+
external: memory.external
|
|
92
|
+
},
|
|
93
|
+
eventLoopDelay: avgEventLoopDelay,
|
|
94
|
+
pagesVisited: this.pagesVisited,
|
|
95
|
+
interactionsExecuted: this.interactionsExecuted
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
// Keep only last 1000 samples to avoid memory bloat
|
|
99
|
+
if (this.samples.length > 1000) {
|
|
100
|
+
this.samples.shift();
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Reset event loop delays array periodically
|
|
104
|
+
if (this.eventLoopDelays.length > 100) {
|
|
105
|
+
this.eventLoopDelays = this.eventLoopDelays.slice(-50);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Start a phase
|
|
111
|
+
*/
|
|
112
|
+
startPhase(phaseName) {
|
|
113
|
+
this.phaseStartTimes[phaseName] = performance.now();
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* End a phase
|
|
118
|
+
*/
|
|
119
|
+
endPhase(phaseName) {
|
|
120
|
+
if (this.phaseStartTimes[phaseName]) {
|
|
121
|
+
const duration = performance.now() - this.phaseStartTimes[phaseName];
|
|
122
|
+
this.phaseDurations[phaseName] = (this.phaseDurations[phaseName] || 0) + duration;
|
|
123
|
+
delete this.phaseStartTimes[phaseName];
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Increment pages visited
|
|
129
|
+
*/
|
|
130
|
+
incrementPages() {
|
|
131
|
+
this.pagesVisited++;
|
|
132
|
+
this.sample('PAGE_VISIT');
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Increment interactions executed
|
|
137
|
+
*/
|
|
138
|
+
incrementInteractions() {
|
|
139
|
+
this.interactionsExecuted++;
|
|
140
|
+
this.sample('INTERACTION');
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Get current metrics
|
|
145
|
+
*/
|
|
146
|
+
getCurrentMetrics() {
|
|
147
|
+
const now = performance.now();
|
|
148
|
+
const memory = process.memoryUsage();
|
|
149
|
+
const avgEventLoopDelay = this.eventLoopDelays.length > 0
|
|
150
|
+
? this.eventLoopDelays.reduce((a, b) => a + b, 0) / this.eventLoopDelays.length
|
|
151
|
+
: 0;
|
|
152
|
+
|
|
153
|
+
return {
|
|
154
|
+
runtimeMs: now - this.startTime,
|
|
155
|
+
memoryRSS: memory.rss,
|
|
156
|
+
memoryHeapUsed: memory.heapUsed,
|
|
157
|
+
memoryHeapTotal: memory.heapTotal,
|
|
158
|
+
peakMemoryRSS: this.peakMemoryRSS,
|
|
159
|
+
peakMemoryHeapUsed: this.peakMemoryHeapUsed,
|
|
160
|
+
peakEventLoopDelay: this.peakEventLoopDelay,
|
|
161
|
+
avgEventLoopDelay: avgEventLoopDelay,
|
|
162
|
+
pagesVisited: this.pagesVisited,
|
|
163
|
+
interactionsExecuted: this.interactionsExecuted,
|
|
164
|
+
phaseDurations: { ...this.phaseDurations }
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Get final report
|
|
170
|
+
*/
|
|
171
|
+
getFinalReport() {
|
|
172
|
+
const metrics = this.getCurrentMetrics();
|
|
173
|
+
|
|
174
|
+
// Find slow phases
|
|
175
|
+
const slowPhases = Object.entries(this.phaseDurations)
|
|
176
|
+
.filter(([_, duration]) => duration > 5000) // Phases > 5s
|
|
177
|
+
.map(([phase, duration]) => ({
|
|
178
|
+
phase,
|
|
179
|
+
durationMs: duration,
|
|
180
|
+
percentage: (duration / metrics.runtimeMs) * 100
|
|
181
|
+
}))
|
|
182
|
+
.sort((a, b) => b.durationMs - a.durationMs);
|
|
183
|
+
|
|
184
|
+
return {
|
|
185
|
+
...metrics,
|
|
186
|
+
slowPhases,
|
|
187
|
+
sampleCount: this.samples.length,
|
|
188
|
+
samples: this.samples.slice(-100) // Last 100 samples only
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Stop monitoring
|
|
194
|
+
*/
|
|
195
|
+
stop() {
|
|
196
|
+
if (this.eventLoopMonitor) {
|
|
197
|
+
clearInterval(this.eventLoopMonitor);
|
|
198
|
+
this.eventLoopMonitor = null;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Create a performance monitor instance
|
|
205
|
+
*/
|
|
206
|
+
export function createPerformanceMonitor() {
|
|
207
|
+
return new PerformanceMonitor();
|
|
208
|
+
}
|
|
209
|
+
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PHASE 21.9 — Performance Report
|
|
3
|
+
*
|
|
4
|
+
* Generates performance.report.json with budgets, actual usage, peaks, and violations.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { readFileSync, existsSync, writeFileSync, mkdirSync, statSync, readdirSync } from 'fs';
|
|
8
|
+
import { resolve } from 'path';
|
|
9
|
+
import { getPerfBudget, evaluatePerfBudget } from './perf.contract.js';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Calculate artifacts size
|
|
13
|
+
*
|
|
14
|
+
* @param {string} runDir - Run directory
|
|
15
|
+
* @returns {number} Size in MB
|
|
16
|
+
*/
|
|
17
|
+
function calculateArtifactsSize(runDir) {
|
|
18
|
+
let totalSize = 0;
|
|
19
|
+
|
|
20
|
+
function scanDirectory(dir) {
|
|
21
|
+
try {
|
|
22
|
+
const entries = readdirSync(dir, { withFileTypes: true });
|
|
23
|
+
|
|
24
|
+
for (const entry of entries) {
|
|
25
|
+
const fullPath = resolve(dir, entry.name);
|
|
26
|
+
|
|
27
|
+
if (entry.isDirectory()) {
|
|
28
|
+
scanDirectory(fullPath);
|
|
29
|
+
} else if (entry.isFile()) {
|
|
30
|
+
try {
|
|
31
|
+
const stats = statSync(fullPath);
|
|
32
|
+
totalSize += stats.size;
|
|
33
|
+
} catch {
|
|
34
|
+
// Skip unreadable files
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
} catch {
|
|
39
|
+
// Skip inaccessible directories
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (existsSync(runDir)) {
|
|
44
|
+
scanDirectory(runDir);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return totalSize / (1024 * 1024); // Convert to MB
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Load pipeline stage timings from run.meta.json
|
|
52
|
+
*
|
|
53
|
+
* @param {string} projectDir - Project directory
|
|
54
|
+
* @param {string} runId - Run ID
|
|
55
|
+
* @returns {Object|null} Stage timings or null
|
|
56
|
+
*/
|
|
57
|
+
function loadPipelineStageTimings(projectDir, runId) {
|
|
58
|
+
const runDir = resolve(projectDir, '.verax', 'runs', runId);
|
|
59
|
+
const metaPath = resolve(runDir, 'run.meta.json');
|
|
60
|
+
|
|
61
|
+
if (!existsSync(metaPath)) {
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
try {
|
|
66
|
+
// @ts-expect-error - readFileSync with encoding returns string
|
|
67
|
+
const meta = JSON.parse(readFileSync(metaPath, 'utf-8'));
|
|
68
|
+
return meta.pipelineStages || null;
|
|
69
|
+
} catch {
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Generate performance report
|
|
76
|
+
*
|
|
77
|
+
* @param {string} projectDir - Project directory
|
|
78
|
+
* @param {string} runId - Run ID
|
|
79
|
+
* @param {Object} monitorReport - Monitor report from PerformanceMonitor
|
|
80
|
+
* @param {string} profile - Budget profile name
|
|
81
|
+
* @returns {Object} Performance report
|
|
82
|
+
*/
|
|
83
|
+
export function generatePerformanceReport(projectDir, runId, monitorReport, profile = 'STANDARD') {
|
|
84
|
+
const budget = getPerfBudget(profile);
|
|
85
|
+
const runDir = resolve(projectDir, '.verax', 'runs', runId);
|
|
86
|
+
const artifactsSizeMB = calculateArtifactsSize(runDir);
|
|
87
|
+
|
|
88
|
+
// Load pipeline stage timings
|
|
89
|
+
const pipelineStages = loadPipelineStageTimings(projectDir, runId);
|
|
90
|
+
|
|
91
|
+
const actual = {
|
|
92
|
+
runtimeMs: monitorReport.runtimeMs,
|
|
93
|
+
memoryRSS: monitorReport.memoryRSS,
|
|
94
|
+
pagesVisited: monitorReport.pagesVisited,
|
|
95
|
+
interactionsExecuted: monitorReport.interactionsExecuted,
|
|
96
|
+
artifactsSizeMB: artifactsSizeMB,
|
|
97
|
+
eventLoopDelayMs: monitorReport.avgEventLoopDelay || 0
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
const evaluation = evaluatePerfBudget(actual, budget);
|
|
101
|
+
|
|
102
|
+
// Extract stage timings if available
|
|
103
|
+
const stageTimings = {};
|
|
104
|
+
if (pipelineStages) {
|
|
105
|
+
for (const [stageName, stageData] of Object.entries(pipelineStages)) {
|
|
106
|
+
if (stageData.durationMs !== null && stageData.durationMs !== undefined) {
|
|
107
|
+
stageTimings[stageName] = {
|
|
108
|
+
durationMs: stageData.durationMs,
|
|
109
|
+
startedAt: stageData.startedAt,
|
|
110
|
+
completedAt: stageData.completedAt,
|
|
111
|
+
status: stageData.status
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const report = {
|
|
118
|
+
contractVersion: 1,
|
|
119
|
+
runId,
|
|
120
|
+
profile,
|
|
121
|
+
budget: {
|
|
122
|
+
maxRuntimeMs: budget.maxRuntimeMs,
|
|
123
|
+
maxMemoryRSS: budget.maxMemoryRSS,
|
|
124
|
+
maxPagesVisited: budget.maxPagesVisited,
|
|
125
|
+
maxInteractionsExecuted: budget.maxInteractionsExecuted,
|
|
126
|
+
maxArtifactsSizeMB: budget.maxArtifactsSizeMB,
|
|
127
|
+
maxEventLoopDelayMs: budget.maxEventLoopDelayMs
|
|
128
|
+
},
|
|
129
|
+
actual,
|
|
130
|
+
peaks: {
|
|
131
|
+
memoryRSS: monitorReport.peakMemoryRSS,
|
|
132
|
+
memoryHeapUsed: monitorReport.peakMemoryHeapUsed,
|
|
133
|
+
memoryHeapTotal: monitorReport.peakMemoryHeapTotal,
|
|
134
|
+
eventLoopDelay: monitorReport.peakEventLoopDelay
|
|
135
|
+
},
|
|
136
|
+
stageTimings: Object.keys(stageTimings).length > 0 ? stageTimings : null,
|
|
137
|
+
networkRequests: monitorReport.networkRequests || 0,
|
|
138
|
+
violations: evaluation.violations,
|
|
139
|
+
warnings: evaluation.warnings,
|
|
140
|
+
verdict: evaluation.verdict,
|
|
141
|
+
ok: evaluation.ok,
|
|
142
|
+
summary: {
|
|
143
|
+
...evaluation.summary,
|
|
144
|
+
runtimeMs: actual.runtimeMs,
|
|
145
|
+
memoryRSSMB: (actual.memoryRSS / (1024 * 1024)).toFixed(2),
|
|
146
|
+
pagesVisited: actual.pagesVisited,
|
|
147
|
+
networkRequests: monitorReport.networkRequests || 0,
|
|
148
|
+
interactionsExecuted: actual.interactionsExecuted,
|
|
149
|
+
artifactsSizeMB: actual.artifactsSizeMB.toFixed(2),
|
|
150
|
+
slowPhases: monitorReport.slowPhases || []
|
|
151
|
+
},
|
|
152
|
+
generatedAt: new Date().toISOString()
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
return report;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Write performance report
|
|
160
|
+
*
|
|
161
|
+
* @param {string} projectDir - Project directory
|
|
162
|
+
* @param {string} runId - Run ID
|
|
163
|
+
* @param {Object} report - Performance report
|
|
164
|
+
* @returns {string} Path to written file
|
|
165
|
+
*/
|
|
166
|
+
export function writePerformanceReport(projectDir, runId, report) {
|
|
167
|
+
const runDir = resolve(projectDir, '.verax', 'runs', runId);
|
|
168
|
+
if (!existsSync(runDir)) {
|
|
169
|
+
mkdirSync(runDir, { recursive: true });
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
const outputPath = resolve(runDir, 'performance.report.json');
|
|
173
|
+
writeFileSync(outputPath, JSON.stringify(report, null, 2), 'utf-8');
|
|
174
|
+
|
|
175
|
+
return outputPath;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Load performance report
|
|
180
|
+
*
|
|
181
|
+
* @param {string} projectDir - Project directory
|
|
182
|
+
* @param {string} runId - Run ID
|
|
183
|
+
* @returns {Object|null} Performance report or null
|
|
184
|
+
*/
|
|
185
|
+
export function loadPerformanceReport(projectDir, runId) {
|
|
186
|
+
const reportPath = resolve(projectDir, '.verax', 'runs', runId, 'performance.report.json');
|
|
187
|
+
|
|
188
|
+
if (!existsSync(reportPath)) {
|
|
189
|
+
return null;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
try {
|
|
193
|
+
const content = readFileSync(reportPath, 'utf-8');
|
|
194
|
+
// @ts-expect-error - readFileSync with encoding returns string
|
|
195
|
+
return JSON.parse(content);
|
|
196
|
+
} catch {
|
|
197
|
+
return null;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|