@veraxhq/verax 0.2.0 → 0.3.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 (217) hide show
  1. package/README.md +14 -18
  2. package/bin/verax.js +7 -0
  3. package/package.json +15 -5
  4. package/src/cli/commands/baseline.js +104 -0
  5. package/src/cli/commands/default.js +323 -111
  6. package/src/cli/commands/doctor.js +36 -4
  7. package/src/cli/commands/ga.js +243 -0
  8. package/src/cli/commands/gates.js +95 -0
  9. package/src/cli/commands/inspect.js +131 -2
  10. package/src/cli/commands/release-check.js +213 -0
  11. package/src/cli/commands/run.js +498 -103
  12. package/src/cli/commands/security-check.js +211 -0
  13. package/src/cli/commands/truth.js +114 -0
  14. package/src/cli/entry.js +305 -68
  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 +546 -0
  20. package/src/cli/util/ast-network-detector.js +603 -0
  21. package/src/cli/util/ast-usestate-detector.js +602 -0
  22. package/src/cli/util/bootstrap-guard.js +86 -0
  23. package/src/cli/util/detection-engine.js +4 -3
  24. package/src/cli/util/determinism-runner.js +123 -0
  25. package/src/cli/util/determinism-writer.js +129 -0
  26. package/src/cli/util/env-url.js +4 -0
  27. package/src/cli/util/events.js +76 -0
  28. package/src/cli/util/expectation-extractor.js +380 -74
  29. package/src/cli/util/findings-writer.js +126 -15
  30. package/src/cli/util/learn-writer.js +3 -1
  31. package/src/cli/util/observation-engine.js +69 -23
  32. package/src/cli/util/observe-writer.js +3 -1
  33. package/src/cli/util/paths.js +6 -14
  34. package/src/cli/util/project-discovery.js +23 -0
  35. package/src/cli/util/project-writer.js +3 -1
  36. package/src/cli/util/redact.js +2 -2
  37. package/src/cli/util/run-resolver.js +64 -0
  38. package/src/cli/util/runtime-budget.js +147 -0
  39. package/src/cli/util/source-requirement.js +55 -0
  40. package/src/cli/util/summary-writer.js +13 -1
  41. package/src/cli/util/svelte-navigation-detector.js +163 -0
  42. package/src/cli/util/svelte-network-detector.js +80 -0
  43. package/src/cli/util/svelte-sfc-extractor.js +147 -0
  44. package/src/cli/util/svelte-state-detector.js +243 -0
  45. package/src/cli/util/vue-navigation-detector.js +177 -0
  46. package/src/cli/util/vue-sfc-extractor.js +162 -0
  47. package/src/cli/util/vue-state-detector.js +215 -0
  48. package/src/types/global.d.ts +28 -0
  49. package/src/types/ts-ast.d.ts +24 -0
  50. package/src/verax/cli/doctor.js +2 -2
  51. package/src/verax/cli/finding-explainer.js +56 -3
  52. package/src/verax/cli/init.js +1 -1
  53. package/src/verax/cli/url-safety.js +12 -2
  54. package/src/verax/cli/wizard.js +13 -2
  55. package/src/verax/core/artifacts/registry.js +154 -0
  56. package/src/verax/core/artifacts/verifier.js +980 -0
  57. package/src/verax/core/baseline/baseline.enforcer.js +137 -0
  58. package/src/verax/core/baseline/baseline.snapshot.js +231 -0
  59. package/src/verax/core/budget-engine.js +1 -1
  60. package/src/verax/core/capabilities/gates.js +499 -0
  61. package/src/verax/core/capabilities/registry.js +475 -0
  62. package/src/verax/core/confidence/confidence-compute.js +137 -0
  63. package/src/verax/core/confidence/confidence-invariants.js +234 -0
  64. package/src/verax/core/confidence/confidence-report-writer.js +112 -0
  65. package/src/verax/core/confidence/confidence-weights.js +44 -0
  66. package/src/verax/core/confidence/confidence.defaults.js +65 -0
  67. package/src/verax/core/confidence/confidence.loader.js +79 -0
  68. package/src/verax/core/confidence/confidence.schema.js +94 -0
  69. package/src/verax/core/confidence-engine-refactor.js +484 -0
  70. package/src/verax/core/confidence-engine.js +486 -0
  71. package/src/verax/core/confidence-engine.js.backup +471 -0
  72. package/src/verax/core/contracts/index.js +29 -0
  73. package/src/verax/core/contracts/types.js +185 -0
  74. package/src/verax/core/contracts/validators.js +381 -0
  75. package/src/verax/core/decision-snapshot.js +31 -4
  76. package/src/verax/core/decisions/decision.trace.js +276 -0
  77. package/src/verax/core/determinism/contract-writer.js +89 -0
  78. package/src/verax/core/determinism/contract.js +139 -0
  79. package/src/verax/core/determinism/diff.js +364 -0
  80. package/src/verax/core/determinism/engine.js +221 -0
  81. package/src/verax/core/determinism/finding-identity.js +148 -0
  82. package/src/verax/core/determinism/normalize.js +438 -0
  83. package/src/verax/core/determinism/report-writer.js +92 -0
  84. package/src/verax/core/determinism/run-fingerprint.js +118 -0
  85. package/src/verax/core/determinism-model.js +35 -6
  86. package/src/verax/core/dynamic-route-intelligence.js +528 -0
  87. package/src/verax/core/evidence/evidence-capture-service.js +307 -0
  88. package/src/verax/core/evidence/evidence-intent-ledger.js +165 -0
  89. package/src/verax/core/evidence-builder.js +487 -0
  90. package/src/verax/core/execution-mode-context.js +77 -0
  91. package/src/verax/core/execution-mode-detector.js +190 -0
  92. package/src/verax/core/failures/exit-codes.js +86 -0
  93. package/src/verax/core/failures/failure-summary.js +76 -0
  94. package/src/verax/core/failures/failure.factory.js +225 -0
  95. package/src/verax/core/failures/failure.ledger.js +132 -0
  96. package/src/verax/core/failures/failure.types.js +196 -0
  97. package/src/verax/core/failures/index.js +10 -0
  98. package/src/verax/core/ga/ga-report-writer.js +43 -0
  99. package/src/verax/core/ga/ga.artifact.js +49 -0
  100. package/src/verax/core/ga/ga.contract.js +434 -0
  101. package/src/verax/core/ga/ga.enforcer.js +86 -0
  102. package/src/verax/core/guardrails/guardrails-report-writer.js +109 -0
  103. package/src/verax/core/guardrails/policy.defaults.js +210 -0
  104. package/src/verax/core/guardrails/policy.loader.js +83 -0
  105. package/src/verax/core/guardrails/policy.schema.js +110 -0
  106. package/src/verax/core/guardrails/truth-reconciliation.js +136 -0
  107. package/src/verax/core/guardrails-engine.js +505 -0
  108. package/src/verax/core/incremental-store.js +15 -7
  109. package/src/verax/core/observe/run-timeline.js +316 -0
  110. package/src/verax/core/perf/perf.contract.js +186 -0
  111. package/src/verax/core/perf/perf.display.js +65 -0
  112. package/src/verax/core/perf/perf.enforcer.js +91 -0
  113. package/src/verax/core/perf/perf.monitor.js +209 -0
  114. package/src/verax/core/perf/perf.report.js +198 -0
  115. package/src/verax/core/pipeline-tracker.js +238 -0
  116. package/src/verax/core/product-definition.js +127 -0
  117. package/src/verax/core/release/provenance.builder.js +271 -0
  118. package/src/verax/core/release/release-report-writer.js +40 -0
  119. package/src/verax/core/release/release.enforcer.js +159 -0
  120. package/src/verax/core/release/reproducibility.check.js +221 -0
  121. package/src/verax/core/release/sbom.builder.js +283 -0
  122. package/src/verax/core/replay-validator.js +4 -4
  123. package/src/verax/core/replay.js +1 -1
  124. package/src/verax/core/report/cross-index.js +192 -0
  125. package/src/verax/core/report/human-summary.js +222 -0
  126. package/src/verax/core/route-intelligence.js +419 -0
  127. package/src/verax/core/security/secrets.scan.js +326 -0
  128. package/src/verax/core/security/security-report.js +50 -0
  129. package/src/verax/core/security/security.enforcer.js +124 -0
  130. package/src/verax/core/security/supplychain.defaults.json +38 -0
  131. package/src/verax/core/security/supplychain.policy.js +326 -0
  132. package/src/verax/core/security/vuln.scan.js +265 -0
  133. package/src/verax/core/silence-impact.js +1 -1
  134. package/src/verax/core/silence-model.js +9 -7
  135. package/src/verax/core/truth/truth.certificate.js +250 -0
  136. package/src/verax/core/ui-feedback-intelligence.js +515 -0
  137. package/src/verax/detect/comparison.js +8 -3
  138. package/src/verax/detect/confidence-engine.js +645 -57
  139. package/src/verax/detect/confidence-helper.js +33 -0
  140. package/src/verax/detect/detection-engine.js +19 -2
  141. package/src/verax/detect/dynamic-route-findings.js +335 -0
  142. package/src/verax/detect/evidence-index.js +15 -65
  143. package/src/verax/detect/expectation-chain-detector.js +417 -0
  144. package/src/verax/detect/expectation-model.js +56 -3
  145. package/src/verax/detect/explanation-helpers.js +1 -1
  146. package/src/verax/detect/finding-detector.js +2 -2
  147. package/src/verax/detect/findings-writer.js +149 -20
  148. package/src/verax/detect/flow-detector.js +4 -4
  149. package/src/verax/detect/index.js +265 -15
  150. package/src/verax/detect/interactive-findings.js +3 -4
  151. package/src/verax/detect/journey-stall-detector.js +558 -0
  152. package/src/verax/detect/route-findings.js +218 -0
  153. package/src/verax/detect/signal-mapper.js +2 -2
  154. package/src/verax/detect/skip-classifier.js +4 -4
  155. package/src/verax/detect/ui-feedback-findings.js +207 -0
  156. package/src/verax/detect/verdict-engine.js +61 -9
  157. package/src/verax/detect/view-switch-correlator.js +242 -0
  158. package/src/verax/flow/flow-engine.js +3 -2
  159. package/src/verax/flow/flow-spec.js +1 -2
  160. package/src/verax/index.js +413 -33
  161. package/src/verax/intel/effect-detector.js +1 -1
  162. package/src/verax/intel/index.js +2 -2
  163. package/src/verax/intel/route-extractor.js +3 -3
  164. package/src/verax/intel/vue-navigation-extractor.js +81 -18
  165. package/src/verax/intel/vue-router-extractor.js +4 -2
  166. package/src/verax/learn/action-contract-extractor.js +684 -66
  167. package/src/verax/learn/ast-contract-extractor.js +53 -1
  168. package/src/verax/learn/index.js +36 -2
  169. package/src/verax/learn/manifest-writer.js +28 -14
  170. package/src/verax/learn/route-extractor.js +1 -1
  171. package/src/verax/learn/route-validator.js +12 -8
  172. package/src/verax/learn/state-extractor.js +1 -1
  173. package/src/verax/learn/static-extractor-navigation.js +1 -1
  174. package/src/verax/learn/static-extractor-validation.js +2 -2
  175. package/src/verax/learn/static-extractor.js +8 -7
  176. package/src/verax/learn/ts-contract-resolver.js +14 -12
  177. package/src/verax/observe/browser.js +22 -3
  178. package/src/verax/observe/console-sensor.js +2 -2
  179. package/src/verax/observe/expectation-executor.js +2 -1
  180. package/src/verax/observe/focus-sensor.js +1 -1
  181. package/src/verax/observe/human-driver.js +29 -10
  182. package/src/verax/observe/index.js +92 -844
  183. package/src/verax/observe/interaction-discovery.js +27 -15
  184. package/src/verax/observe/interaction-runner.js +31 -14
  185. package/src/verax/observe/loading-sensor.js +6 -0
  186. package/src/verax/observe/navigation-sensor.js +1 -1
  187. package/src/verax/observe/observe-context.js +205 -0
  188. package/src/verax/observe/observe-helpers.js +191 -0
  189. package/src/verax/observe/observe-runner.js +226 -0
  190. package/src/verax/observe/observers/budget-observer.js +185 -0
  191. package/src/verax/observe/observers/console-observer.js +102 -0
  192. package/src/verax/observe/observers/coverage-observer.js +107 -0
  193. package/src/verax/observe/observers/interaction-observer.js +471 -0
  194. package/src/verax/observe/observers/navigation-observer.js +132 -0
  195. package/src/verax/observe/observers/network-observer.js +87 -0
  196. package/src/verax/observe/observers/safety-observer.js +82 -0
  197. package/src/verax/observe/observers/ui-feedback-observer.js +99 -0
  198. package/src/verax/observe/settle.js +1 -0
  199. package/src/verax/observe/state-sensor.js +8 -4
  200. package/src/verax/observe/state-ui-sensor.js +7 -1
  201. package/src/verax/observe/traces-writer.js +27 -16
  202. package/src/verax/observe/ui-feedback-detector.js +742 -0
  203. package/src/verax/observe/ui-signal-sensor.js +155 -2
  204. package/src/verax/scan-summary-writer.js +46 -9
  205. package/src/verax/shared/artifact-manager.js +9 -6
  206. package/src/verax/shared/budget-profiles.js +2 -2
  207. package/src/verax/shared/caching.js +1 -1
  208. package/src/verax/shared/config-loader.js +1 -2
  209. package/src/verax/shared/css-spinner-rules.js +204 -0
  210. package/src/verax/shared/dynamic-route-utils.js +12 -6
  211. package/src/verax/shared/retry-policy.js +1 -6
  212. package/src/verax/shared/root-artifacts.js +1 -1
  213. package/src/verax/shared/view-switch-rules.js +208 -0
  214. package/src/verax/shared/zip-artifacts.js +1 -0
  215. package/src/verax/validate/context-validator.js +1 -1
  216. package/src/verax/observe/index.js.backup +0 -1
  217. package/src/verax/validate/context-validator.js.bak +0 -0
@@ -0,0 +1,221 @@
1
+ /**
2
+ * PHASE 21.7 — Reproducibility Check
3
+ *
4
+ * Verifies that same commit + same policies = same hashes.
5
+ * Difference = NON_REPRODUCIBLE (BLOCKING for GA).
6
+ */
7
+
8
+ import { readFileSync, existsSync, writeFileSync, mkdirSync } from 'fs';
9
+ import { resolve } from 'path';
10
+ import { createHash } from 'crypto';
11
+ import { execSync } from 'child_process';
12
+
13
+ /**
14
+ * Get current git commit
15
+ *
16
+ * @param {string} projectDir - Project directory
17
+ * @returns {string|null} Commit hash or null
18
+ */
19
+ function getGitCommit(projectDir) {
20
+ try {
21
+ const result = execSync('git rev-parse HEAD', {
22
+ cwd: projectDir,
23
+ encoding: 'utf-8',
24
+ stdio: ['ignore', 'pipe', 'ignore']
25
+ });
26
+ return result.trim();
27
+ } catch {
28
+ return null;
29
+ }
30
+ }
31
+
32
+ /**
33
+ * Get policy hashes
34
+ *
35
+ * @param {string} projectDir - Project directory
36
+ * @returns {Promise<Object>} Policy hashes
37
+ */
38
+ async function getPolicyHashes(projectDir) {
39
+ try {
40
+ const { loadGuardrailsPolicy } = await import('../guardrails/policy.loader.js');
41
+ const { loadConfidencePolicy } = await import('../confidence/confidence.loader.js');
42
+
43
+ const guardrails = loadGuardrailsPolicy(null, projectDir);
44
+ const confidence = loadConfidencePolicy(null, projectDir);
45
+
46
+ const guardrailsHash = createHash('sha256')
47
+ .update(JSON.stringify(guardrails, null, 0))
48
+ .digest('hex');
49
+
50
+ const confidenceHash = createHash('sha256')
51
+ .update(JSON.stringify(confidence, null, 0))
52
+ .digest('hex');
53
+
54
+ return {
55
+ guardrails: guardrailsHash,
56
+ confidence: confidenceHash
57
+ };
58
+ } catch {
59
+ return {
60
+ guardrails: null,
61
+ confidence: null
62
+ };
63
+ }
64
+ }
65
+
66
+ /**
67
+ * Get artifact hashes
68
+ *
69
+ * @param {string} projectDir - Project directory
70
+ * @returns {Object} Artifact hashes
71
+ */
72
+ function getArtifactHashes(projectDir) {
73
+ const hashes = {};
74
+
75
+ // Hash key files
76
+ const keyFiles = [
77
+ 'package.json',
78
+ 'bin/verax.js',
79
+ 'src/cli/entry.js'
80
+ ];
81
+
82
+ for (const file of keyFiles) {
83
+ const filePath = resolve(projectDir, file);
84
+ if (existsSync(filePath)) {
85
+ try {
86
+ const content = readFileSync(filePath);
87
+ hashes[file] = createHash('sha256').update(content).digest('hex');
88
+ } catch {
89
+ hashes[file] = null;
90
+ }
91
+ } else {
92
+ hashes[file] = null;
93
+ }
94
+ }
95
+
96
+ return hashes;
97
+ }
98
+
99
+ /**
100
+ * Load previous reproducibility report
101
+ *
102
+ * @param {string} projectDir - Project directory
103
+ * @returns {Object|null} Previous report or null
104
+ */
105
+ function loadPreviousReport(projectDir) {
106
+ const reportPath = resolve(projectDir, 'release', 'reproducibility.report.json');
107
+ if (!existsSync(reportPath)) {
108
+ return null;
109
+ }
110
+
111
+ try {
112
+ const content = readFileSync(reportPath, 'utf-8');
113
+ return JSON.parse(content);
114
+ } catch {
115
+ return null;
116
+ }
117
+ }
118
+
119
+ /**
120
+ * Check reproducibility
121
+ *
122
+ * @param {string} projectDir - Project directory
123
+ * @returns {Promise<Object>} Reproducibility check result
124
+ */
125
+ export async function checkReproducibility(projectDir) {
126
+ const gitCommit = getGitCommit(projectDir);
127
+ const policyHashes = await getPolicyHashes(projectDir);
128
+ const artifactHashes = getArtifactHashes(projectDir);
129
+
130
+ const current = {
131
+ gitCommit,
132
+ policies: policyHashes,
133
+ artifacts: artifactHashes,
134
+ checkedAt: new Date().toISOString()
135
+ };
136
+
137
+ const previous = loadPreviousReport(projectDir);
138
+
139
+ let reproducible = true;
140
+ const differences = [];
141
+
142
+ if (previous) {
143
+ // Compare with previous build
144
+ if (previous.gitCommit !== gitCommit) {
145
+ reproducible = false;
146
+ differences.push({
147
+ type: 'git_commit',
148
+ previous: previous.gitCommit,
149
+ current: gitCommit,
150
+ message: 'Git commit changed'
151
+ });
152
+ }
153
+
154
+ if (previous.policies?.guardrails !== policyHashes.guardrails) {
155
+ reproducible = false;
156
+ differences.push({
157
+ type: 'guardrails_policy',
158
+ previous: previous.policies?.guardrails,
159
+ current: policyHashes.guardrails,
160
+ message: 'Guardrails policy changed'
161
+ });
162
+ }
163
+
164
+ if (previous.policies?.confidence !== policyHashes.confidence) {
165
+ reproducible = false;
166
+ differences.push({
167
+ type: 'confidence_policy',
168
+ previous: previous.policies?.confidence,
169
+ current: policyHashes.confidence,
170
+ message: 'Confidence policy changed'
171
+ });
172
+ }
173
+
174
+ // Compare artifact hashes
175
+ for (const [file, hash] of Object.entries(artifactHashes)) {
176
+ if (previous.artifacts?.[file] !== hash) {
177
+ reproducible = false;
178
+ differences.push({
179
+ type: 'artifact',
180
+ file,
181
+ previous: previous.artifacts?.[file],
182
+ current: hash,
183
+ message: `Artifact ${file} changed`
184
+ });
185
+ }
186
+ }
187
+ }
188
+
189
+ const verdict = reproducible ? 'REPRODUCIBLE' : 'NON_REPRODUCIBLE';
190
+
191
+ const report = {
192
+ verdict,
193
+ reproducible,
194
+ differences,
195
+ current,
196
+ previous: previous || null,
197
+ checkedAt: new Date().toISOString()
198
+ };
199
+
200
+ return report;
201
+ }
202
+
203
+ /**
204
+ * Write reproducibility report
205
+ *
206
+ * @param {string} projectDir - Project directory
207
+ * @param {Object} report - Reproducibility report
208
+ * @returns {string} Path to written file
209
+ */
210
+ export function writeReproducibilityReport(projectDir, report) {
211
+ const outputDir = resolve(projectDir, 'release');
212
+ if (!existsSync(outputDir)) {
213
+ mkdirSync(outputDir, { recursive: true });
214
+ }
215
+
216
+ const outputPath = resolve(outputDir, 'reproducibility.report.json');
217
+ writeFileSync(outputPath, JSON.stringify(report, null, 2), 'utf-8');
218
+
219
+ return outputPath;
220
+ }
221
+
@@ -0,0 +1,283 @@
1
+ /**
2
+ * PHASE 21.7 — SBOM Builder
3
+ *
4
+ * Generates Software Bill of Materials (SBOM) in CycloneDX format.
5
+ * Missing SBOM = BLOCKING.
6
+ */
7
+
8
+ import { readFileSync, existsSync, writeFileSync, mkdirSync, readdirSync } from 'fs';
9
+ import { resolve } from 'path';
10
+ import { createHash } from 'crypto';
11
+ import { execSync } from 'child_process';
12
+
13
+ /**
14
+ * Get package.json dependencies
15
+ *
16
+ * @param {string} projectDir - Project directory
17
+ * @returns {Object} Dependencies object
18
+ */
19
+ function getDependencies(projectDir) {
20
+ try {
21
+ const pkgPath = resolve(projectDir, 'package.json');
22
+ if (!existsSync(pkgPath)) {
23
+ return { dependencies: {}, devDependencies: {} };
24
+ }
25
+ const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));
26
+ return {
27
+ dependencies: pkg.dependencies || {},
28
+ devDependencies: pkg.devDependencies || {}
29
+ };
30
+ } catch {
31
+ return { dependencies: {}, devDependencies: {} };
32
+ }
33
+ }
34
+
35
+ /**
36
+ * Get transitive dependencies from node_modules
37
+ *
38
+ * @param {string} projectDir - Project directory
39
+ * @returns {Array} Array of package info
40
+ */
41
+ function getTransitiveDependencies(projectDir) {
42
+ const packages = [];
43
+ const nodeModulesPath = resolve(projectDir, 'node_modules');
44
+
45
+ if (!existsSync(nodeModulesPath)) {
46
+ return packages;
47
+ }
48
+
49
+ try {
50
+ // Use npm ls to get dependency tree
51
+ const result = execSync('npm ls --all --json', {
52
+ cwd: projectDir,
53
+ encoding: 'utf-8',
54
+ stdio: ['ignore', 'pipe', 'ignore']
55
+ });
56
+
57
+ const tree = JSON.parse(result);
58
+
59
+ function traverseDeps(deps, parent = null) {
60
+ if (!deps || typeof deps !== 'object') {
61
+ return;
62
+ }
63
+
64
+ for (const [name, info] of Object.entries(deps)) {
65
+ if (info && typeof info === 'object' && info.version) {
66
+ packages.push({
67
+ name,
68
+ version: info.version,
69
+ parent: parent || null
70
+ });
71
+
72
+ if (info.dependencies) {
73
+ traverseDeps(info.dependencies, name);
74
+ }
75
+ }
76
+ }
77
+ }
78
+
79
+ if (tree.dependencies) {
80
+ traverseDeps(tree.dependencies);
81
+ }
82
+ } catch {
83
+ // Fallback: scan node_modules directory
84
+ try {
85
+ const entries = readdirSync(nodeModulesPath, { withFileTypes: true });
86
+
87
+ for (const entry of entries) {
88
+ if (entry.isDirectory() && !entry.name.startsWith('.')) {
89
+ const pkgPath = resolve(nodeModulesPath, entry.name, 'package.json');
90
+ if (existsSync(pkgPath)) {
91
+ try {
92
+ const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));
93
+ packages.push({
94
+ name: pkg.name || entry.name,
95
+ version: pkg.version || 'unknown',
96
+ parent: null
97
+ });
98
+ } catch {
99
+ // Skip invalid packages
100
+ }
101
+ }
102
+ }
103
+ }
104
+ } catch {
105
+ // If scanning fails, return empty
106
+ }
107
+ }
108
+
109
+ return packages;
110
+ }
111
+
112
+ /**
113
+ * Get license from package.json
114
+ *
115
+ * @param {string} packagePath - Path to package.json
116
+ * @returns {string|null} License or null
117
+ */
118
+ function getPackageLicense(packagePath) {
119
+ try {
120
+ if (!existsSync(packagePath)) {
121
+ return null;
122
+ }
123
+ const pkg = JSON.parse(readFileSync(packagePath, 'utf-8'));
124
+ if (typeof pkg.license === 'string') {
125
+ return pkg.license;
126
+ } else if (pkg.license && pkg.license.type) {
127
+ return pkg.license.type;
128
+ }
129
+ return null;
130
+ } catch {
131
+ return null;
132
+ }
133
+ }
134
+
135
+ /**
136
+ * Compute integrity hash for a package
137
+ *
138
+ * @param {string} projectDir - Project directory
139
+ * @param {string} packageName - Package name
140
+ * @returns {string|null} SHA256 hash or null
141
+ */
142
+ function getPackageIntegrity(projectDir, packageName) {
143
+ try {
144
+ const packagePath = resolve(projectDir, 'node_modules', packageName);
145
+ if (!existsSync(packagePath)) {
146
+ return null;
147
+ }
148
+
149
+ // Hash the package.json as a proxy for package integrity
150
+ const pkgPath = resolve(packagePath, 'package.json');
151
+ if (existsSync(pkgPath)) {
152
+ const content = readFileSync(pkgPath);
153
+ return createHash('sha256').update(content).digest('hex');
154
+ }
155
+
156
+ return null;
157
+ } catch {
158
+ return null;
159
+ }
160
+ }
161
+
162
+ /**
163
+ * Build SBOM in CycloneDX format
164
+ *
165
+ * @param {string} projectDir - Project directory
166
+ * @returns {Promise<Object>} SBOM object
167
+ */
168
+ export async function buildSBOM(projectDir) {
169
+ const pkgPath = resolve(projectDir, 'package.json');
170
+ if (!existsSync(pkgPath)) {
171
+ throw new Error('Cannot build SBOM: package.json not found');
172
+ }
173
+
174
+ const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));
175
+ const deps = getDependencies(projectDir);
176
+ const transitive = getTransitiveDependencies(projectDir);
177
+
178
+ // Build components list
179
+ const components = [];
180
+
181
+ // Add main package
182
+ components.push({
183
+ type: 'application',
184
+ name: pkg.name || 'unknown',
185
+ version: pkg.version || 'unknown',
186
+ purl: `pkg:npm/${pkg.name}@${pkg.version}`,
187
+ licenses: pkg.license ? [{ license: { id: pkg.license } }] : []
188
+ });
189
+
190
+ // Add direct dependencies
191
+ for (const [name, version] of Object.entries(deps.dependencies)) {
192
+ const integrity = getPackageIntegrity(projectDir, name);
193
+ const license = getPackageLicense(resolve(projectDir, 'node_modules', name, 'package.json'));
194
+
195
+ components.push({
196
+ type: 'library',
197
+ name,
198
+ version: version.replace(/^[\^~]/, ''),
199
+ purl: `pkg:npm/${name}@${version.replace(/^[\^~]/, '')}`,
200
+ hashes: integrity ? [{ alg: 'SHA-256', content: integrity }] : [],
201
+ licenses: license ? [{ license: { id: license } }] : []
202
+ });
203
+ }
204
+
205
+ // Add dev dependencies (marked as development)
206
+ for (const [name, version] of Object.entries(deps.devDependencies)) {
207
+ const integrity = getPackageIntegrity(projectDir, name);
208
+ const license = getPackageLicense(resolve(projectDir, 'node_modules', name, 'package.json'));
209
+
210
+ components.push({
211
+ type: 'library',
212
+ name,
213
+ version: version.replace(/^[\^~]/, ''),
214
+ purl: `pkg:npm/${name}@${version.replace(/^[\^~]/, '')}`,
215
+ hashes: integrity ? [{ alg: 'SHA-256', content: integrity }] : [],
216
+ licenses: license ? [{ license: { id: license } }] : [],
217
+ scope: 'development'
218
+ });
219
+ }
220
+
221
+ // Add transitive dependencies (simplified - in production, use proper dependency resolution)
222
+ const transitiveMap = new Map();
223
+ for (const trans of transitive) {
224
+ const key = `${trans.name}@${trans.version}`;
225
+ if (!transitiveMap.has(key)) {
226
+ transitiveMap.set(key, trans);
227
+
228
+ const integrity = getPackageIntegrity(projectDir, trans.name);
229
+ const license = getPackageLicense(resolve(projectDir, 'node_modules', trans.name, 'package.json'));
230
+
231
+ components.push({
232
+ type: 'library',
233
+ name: trans.name,
234
+ version: trans.version,
235
+ purl: `pkg:npm/${trans.name}@${trans.version}`,
236
+ hashes: integrity ? [{ alg: 'SHA-256', content: integrity }] : [],
237
+ licenses: license ? [{ license: { id: license } }] : []
238
+ });
239
+ }
240
+ }
241
+
242
+ const sbom = {
243
+ bomFormat: 'CycloneDX',
244
+ specVersion: '1.4',
245
+ version: 1,
246
+ metadata: {
247
+ timestamp: new Date().toISOString(),
248
+ tools: [{
249
+ vendor: 'VERAX',
250
+ name: 'SBOM Builder',
251
+ version: '1.0.0'
252
+ }],
253
+ component: {
254
+ type: 'application',
255
+ name: pkg.name || 'unknown',
256
+ version: pkg.version || 'unknown'
257
+ }
258
+ },
259
+ components: components
260
+ };
261
+
262
+ return sbom;
263
+ }
264
+
265
+ /**
266
+ * Write SBOM to file
267
+ *
268
+ * @param {string} projectDir - Project directory
269
+ * @param {Object} sbom - SBOM object
270
+ * @returns {string} Path to written file
271
+ */
272
+ export function writeSBOM(projectDir, sbom) {
273
+ const outputDir = resolve(projectDir, 'release');
274
+ if (!existsSync(outputDir)) {
275
+ mkdirSync(outputDir, { recursive: true });
276
+ }
277
+
278
+ const outputPath = resolve(outputDir, 'sbom.json');
279
+ writeFileSync(outputPath, JSON.stringify(sbom, null, 2), 'utf-8');
280
+
281
+ return outputPath;
282
+ }
283
+
@@ -13,7 +13,7 @@
13
13
  * - Categorize deviations: truncation_difference, timeout_difference, environment_difference
14
14
  */
15
15
 
16
- import { DecisionRecorder, DECISION_IDS } from './determinism-model.js';
16
+ import { DecisionRecorder } from './determinism-model.js';
17
17
 
18
18
  /**
19
19
  * Compare two decision values and determine if they're equivalent
@@ -47,10 +47,10 @@ function areValuesEquivalent(value1, value2) {
47
47
  /**
48
48
  * Categorize a decision deviation
49
49
  * @param {Object} baseline - Baseline decision
50
- * @param {Object} current - Current decision
50
+ * @param {Object} _current - Current decision (unused parameter, kept for API compatibility)
51
51
  * @returns {string} - Deviation category
52
52
  */
53
- function categorizeDeviation(baseline, current) {
53
+ function categorizeDeviation(baseline, _current) {
54
54
  const category = baseline.category;
55
55
 
56
56
  switch (category) {
@@ -78,7 +78,7 @@ function categorizeDeviation(baseline, current) {
78
78
  * @returns {string} - Human-readable explanation
79
79
  */
80
80
  function explainDeviation(baseline, current) {
81
- const category = baseline.category;
81
+ const _category = baseline.category;
82
82
  const devCategory = categorizeDeviation(baseline, current);
83
83
 
84
84
  // Build explanation based on decision type
@@ -141,7 +141,7 @@ export function loadRunArtifacts(runDir) {
141
141
  * Replay a previous run from artifacts
142
142
  *
143
143
  * @param {string} runDir - Path to run directory
144
- * @returns {Object} Replay result with observation summary
144
+ * @returns {Promise<any>} Replay result with observation summary
145
145
  */
146
146
  export async function replayRun(runDir) {
147
147
  // Load artifacts