@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.
Files changed (213) hide show
  1. package/README.md +10 -6
  2. package/bin/verax.js +11 -11
  3. package/package.json +29 -8
  4. package/src/cli/commands/baseline.js +103 -0
  5. package/src/cli/commands/default.js +51 -6
  6. package/src/cli/commands/doctor.js +29 -0
  7. package/src/cli/commands/ga.js +246 -0
  8. package/src/cli/commands/gates.js +95 -0
  9. package/src/cli/commands/inspect.js +4 -2
  10. package/src/cli/commands/release-check.js +215 -0
  11. package/src/cli/commands/run.js +45 -6
  12. package/src/cli/commands/security-check.js +212 -0
  13. package/src/cli/commands/truth.js +113 -0
  14. package/src/cli/entry.js +30 -20
  15. package/src/cli/util/angular-component-extractor.js +179 -0
  16. package/src/cli/util/angular-navigation-detector.js +141 -0
  17. package/src/cli/util/angular-network-detector.js +161 -0
  18. package/src/cli/util/angular-state-detector.js +162 -0
  19. package/src/cli/util/ast-interactive-detector.js +544 -0
  20. package/src/cli/util/ast-network-detector.js +603 -0
  21. package/src/cli/util/ast-promise-extractor.js +581 -0
  22. package/src/cli/util/ast-usestate-detector.js +602 -0
  23. package/src/cli/util/atomic-write.js +12 -1
  24. package/src/cli/util/bootstrap-guard.js +86 -0
  25. package/src/cli/util/console-reporter.js +72 -0
  26. package/src/cli/util/detection-engine.js +105 -41
  27. package/src/cli/util/determinism-runner.js +124 -0
  28. package/src/cli/util/determinism-writer.js +129 -0
  29. package/src/cli/util/digest-engine.js +359 -0
  30. package/src/cli/util/dom-diff.js +226 -0
  31. package/src/cli/util/evidence-engine.js +287 -0
  32. package/src/cli/util/expectation-extractor.js +151 -5
  33. package/src/cli/util/findings-writer.js +3 -0
  34. package/src/cli/util/framework-detector.js +572 -0
  35. package/src/cli/util/idgen.js +1 -1
  36. package/src/cli/util/interaction-planner.js +529 -0
  37. package/src/cli/util/learn-writer.js +2 -0
  38. package/src/cli/util/ledger-writer.js +110 -0
  39. package/src/cli/util/monorepo-resolver.js +162 -0
  40. package/src/cli/util/observation-engine.js +127 -278
  41. package/src/cli/util/observe-writer.js +2 -0
  42. package/src/cli/util/project-discovery.js +284 -0
  43. package/src/cli/util/project-writer.js +2 -0
  44. package/src/cli/util/run-id.js +23 -27
  45. package/src/cli/util/run-resolver.js +64 -0
  46. package/src/cli/util/run-result.js +778 -0
  47. package/src/cli/util/selector-resolver.js +235 -0
  48. package/src/cli/util/source-requirement.js +55 -0
  49. package/src/cli/util/summary-writer.js +2 -0
  50. package/src/cli/util/svelte-navigation-detector.js +163 -0
  51. package/src/cli/util/svelte-network-detector.js +80 -0
  52. package/src/cli/util/svelte-sfc-extractor.js +146 -0
  53. package/src/cli/util/svelte-state-detector.js +242 -0
  54. package/src/cli/util/trust-activation-integration.js +496 -0
  55. package/src/cli/util/trust-activation-wrapper.js +85 -0
  56. package/src/cli/util/trust-integration-hooks.js +164 -0
  57. package/src/cli/util/types.js +153 -0
  58. package/src/cli/util/url-validation.js +40 -0
  59. package/src/cli/util/vue-navigation-detector.js +178 -0
  60. package/src/cli/util/vue-sfc-extractor.js +161 -0
  61. package/src/cli/util/vue-state-detector.js +215 -0
  62. package/src/types/fs-augment.d.ts +23 -0
  63. package/src/types/global.d.ts +137 -0
  64. package/src/types/internal-types.d.ts +35 -0
  65. package/src/verax/cli/init.js +4 -18
  66. package/src/verax/core/action-classifier.js +4 -3
  67. package/src/verax/core/artifacts/registry.js +139 -0
  68. package/src/verax/core/artifacts/verifier.js +990 -0
  69. package/src/verax/core/baseline/baseline.enforcer.js +137 -0
  70. package/src/verax/core/baseline/baseline.snapshot.js +233 -0
  71. package/src/verax/core/capabilities/gates.js +505 -0
  72. package/src/verax/core/capabilities/registry.js +475 -0
  73. package/src/verax/core/confidence/confidence-compute.js +144 -0
  74. package/src/verax/core/confidence/confidence-invariants.js +234 -0
  75. package/src/verax/core/confidence/confidence-report-writer.js +112 -0
  76. package/src/verax/core/confidence/confidence-weights.js +44 -0
  77. package/src/verax/core/confidence/confidence.defaults.js +65 -0
  78. package/src/verax/core/confidence/confidence.loader.js +80 -0
  79. package/src/verax/core/confidence/confidence.schema.js +94 -0
  80. package/src/verax/core/confidence-engine-refactor.js +489 -0
  81. package/src/verax/core/confidence-engine.js +625 -0
  82. package/src/verax/core/contracts/index.js +29 -0
  83. package/src/verax/core/contracts/types.js +186 -0
  84. package/src/verax/core/contracts/validators.js +456 -0
  85. package/src/verax/core/decisions/decision.trace.js +278 -0
  86. package/src/verax/core/determinism/contract-writer.js +89 -0
  87. package/src/verax/core/determinism/contract.js +139 -0
  88. package/src/verax/core/determinism/diff.js +405 -0
  89. package/src/verax/core/determinism/engine.js +222 -0
  90. package/src/verax/core/determinism/finding-identity.js +149 -0
  91. package/src/verax/core/determinism/normalize.js +466 -0
  92. package/src/verax/core/determinism/report-writer.js +93 -0
  93. package/src/verax/core/determinism/run-fingerprint.js +123 -0
  94. package/src/verax/core/dynamic-route-intelligence.js +529 -0
  95. package/src/verax/core/evidence/evidence-capture-service.js +308 -0
  96. package/src/verax/core/evidence/evidence-intent-ledger.js +166 -0
  97. package/src/verax/core/evidence-builder.js +487 -0
  98. package/src/verax/core/execution-mode-context.js +77 -0
  99. package/src/verax/core/execution-mode-detector.js +192 -0
  100. package/src/verax/core/failures/exit-codes.js +88 -0
  101. package/src/verax/core/failures/failure-summary.js +76 -0
  102. package/src/verax/core/failures/failure.factory.js +225 -0
  103. package/src/verax/core/failures/failure.ledger.js +133 -0
  104. package/src/verax/core/failures/failure.types.js +196 -0
  105. package/src/verax/core/failures/index.js +10 -0
  106. package/src/verax/core/ga/ga-report-writer.js +43 -0
  107. package/src/verax/core/ga/ga.artifact.js +49 -0
  108. package/src/verax/core/ga/ga.contract.js +435 -0
  109. package/src/verax/core/ga/ga.enforcer.js +87 -0
  110. package/src/verax/core/guardrails/guardrails-report-writer.js +109 -0
  111. package/src/verax/core/guardrails/policy.defaults.js +210 -0
  112. package/src/verax/core/guardrails/policy.loader.js +84 -0
  113. package/src/verax/core/guardrails/policy.schema.js +110 -0
  114. package/src/verax/core/guardrails/truth-reconciliation.js +136 -0
  115. package/src/verax/core/guardrails-engine.js +505 -0
  116. package/src/verax/core/incremental-store.js +1 -0
  117. package/src/verax/core/integrity/budget.js +138 -0
  118. package/src/verax/core/integrity/determinism.js +342 -0
  119. package/src/verax/core/integrity/integrity.js +208 -0
  120. package/src/verax/core/integrity/poisoning.js +108 -0
  121. package/src/verax/core/integrity/transaction.js +140 -0
  122. package/src/verax/core/observe/run-timeline.js +318 -0
  123. package/src/verax/core/perf/perf.contract.js +186 -0
  124. package/src/verax/core/perf/perf.display.js +65 -0
  125. package/src/verax/core/perf/perf.enforcer.js +91 -0
  126. package/src/verax/core/perf/perf.monitor.js +209 -0
  127. package/src/verax/core/perf/perf.report.js +200 -0
  128. package/src/verax/core/pipeline-tracker.js +243 -0
  129. package/src/verax/core/product-definition.js +127 -0
  130. package/src/verax/core/release/provenance.builder.js +130 -0
  131. package/src/verax/core/release/release-report-writer.js +40 -0
  132. package/src/verax/core/release/release.enforcer.js +164 -0
  133. package/src/verax/core/release/reproducibility.check.js +222 -0
  134. package/src/verax/core/release/sbom.builder.js +292 -0
  135. package/src/verax/core/replay-validator.js +2 -0
  136. package/src/verax/core/replay.js +4 -0
  137. package/src/verax/core/report/cross-index.js +195 -0
  138. package/src/verax/core/report/human-summary.js +362 -0
  139. package/src/verax/core/route-intelligence.js +420 -0
  140. package/src/verax/core/run-id.js +6 -3
  141. package/src/verax/core/run-manifest.js +4 -3
  142. package/src/verax/core/security/secrets.scan.js +329 -0
  143. package/src/verax/core/security/security-report.js +50 -0
  144. package/src/verax/core/security/security.enforcer.js +128 -0
  145. package/src/verax/core/security/supplychain.defaults.json +38 -0
  146. package/src/verax/core/security/supplychain.policy.js +334 -0
  147. package/src/verax/core/security/vuln.scan.js +265 -0
  148. package/src/verax/core/truth/truth.certificate.js +252 -0
  149. package/src/verax/core/ui-feedback-intelligence.js +481 -0
  150. package/src/verax/detect/conditional-ui-silent-failure.js +84 -0
  151. package/src/verax/detect/confidence-engine.js +62 -34
  152. package/src/verax/detect/confidence-helper.js +34 -0
  153. package/src/verax/detect/dynamic-route-findings.js +338 -0
  154. package/src/verax/detect/expectation-chain-detector.js +417 -0
  155. package/src/verax/detect/expectation-model.js +2 -2
  156. package/src/verax/detect/failure-cause-inference.js +293 -0
  157. package/src/verax/detect/findings-writer.js +131 -35
  158. package/src/verax/detect/flow-detector.js +2 -2
  159. package/src/verax/detect/form-silent-failure.js +98 -0
  160. package/src/verax/detect/index.js +46 -5
  161. package/src/verax/detect/invariants-enforcer.js +147 -0
  162. package/src/verax/detect/journey-stall-detector.js +558 -0
  163. package/src/verax/detect/navigation-silent-failure.js +82 -0
  164. package/src/verax/detect/problem-aggregator.js +361 -0
  165. package/src/verax/detect/route-findings.js +219 -0
  166. package/src/verax/detect/summary-writer.js +477 -0
  167. package/src/verax/detect/test-failure-cause-inference.js +314 -0
  168. package/src/verax/detect/ui-feedback-findings.js +207 -0
  169. package/src/verax/detect/view-switch-correlator.js +242 -0
  170. package/src/verax/flow/flow-engine.js +2 -1
  171. package/src/verax/flow/flow-spec.js +0 -6
  172. package/src/verax/index.js +4 -0
  173. package/src/verax/intel/ts-program.js +1 -0
  174. package/src/verax/intel/vue-navigation-extractor.js +3 -0
  175. package/src/verax/learn/action-contract-extractor.js +3 -0
  176. package/src/verax/learn/ast-contract-extractor.js +1 -1
  177. package/src/verax/learn/flow-extractor.js +1 -0
  178. package/src/verax/learn/project-detector.js +5 -0
  179. package/src/verax/learn/react-router-extractor.js +2 -0
  180. package/src/verax/learn/source-instrumenter.js +1 -0
  181. package/src/verax/learn/state-extractor.js +2 -1
  182. package/src/verax/learn/static-extractor.js +1 -0
  183. package/src/verax/observe/coverage-gaps.js +132 -0
  184. package/src/verax/observe/expectation-handler.js +126 -0
  185. package/src/verax/observe/incremental-skip.js +46 -0
  186. package/src/verax/observe/index.js +51 -155
  187. package/src/verax/observe/interaction-executor.js +192 -0
  188. package/src/verax/observe/interaction-runner.js +782 -513
  189. package/src/verax/observe/network-firewall.js +86 -0
  190. package/src/verax/observe/observation-builder.js +169 -0
  191. package/src/verax/observe/observe-context.js +205 -0
  192. package/src/verax/observe/observe-helpers.js +192 -0
  193. package/src/verax/observe/observe-runner.js +230 -0
  194. package/src/verax/observe/observers/budget-observer.js +185 -0
  195. package/src/verax/observe/observers/console-observer.js +102 -0
  196. package/src/verax/observe/observers/coverage-observer.js +107 -0
  197. package/src/verax/observe/observers/interaction-observer.js +471 -0
  198. package/src/verax/observe/observers/navigation-observer.js +132 -0
  199. package/src/verax/observe/observers/network-observer.js +87 -0
  200. package/src/verax/observe/observers/safety-observer.js +82 -0
  201. package/src/verax/observe/observers/ui-feedback-observer.js +99 -0
  202. package/src/verax/observe/page-traversal.js +138 -0
  203. package/src/verax/observe/snapshot-ops.js +94 -0
  204. package/src/verax/observe/ui-feedback-detector.js +742 -0
  205. package/src/verax/scan-summary-writer.js +2 -0
  206. package/src/verax/shared/artifact-manager.js +25 -5
  207. package/src/verax/shared/caching.js +1 -0
  208. package/src/verax/shared/css-spinner-rules.js +204 -0
  209. package/src/verax/shared/expectation-tracker.js +1 -0
  210. package/src/verax/shared/view-switch-rules.js +208 -0
  211. package/src/verax/shared/zip-artifacts.js +6 -0
  212. package/src/verax/shared/config-loader.js +0 -169
  213. /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
+