@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
package/src/cli/entry.js CHANGED
@@ -3,6 +3,11 @@
3
3
  /**
4
4
  * VERAX CLI Entry Point
5
5
  *
6
+ * PHASE 21.6.1: Two-Phase CLI Bootstrap
7
+ *
8
+ * Phase A: Raw argv parsing with zero side effects
9
+ * Phase B: Dispatch - inspection commands bypass execution bootstrap
10
+ *
6
11
  * Commands:
7
12
  * - verax (smart default with URL detection/prompting)
8
13
  * - verax run --url <url> (strict, non-interactive)
@@ -22,11 +27,47 @@ import { defaultCommand } from './commands/default.js';
22
27
  import { runCommand } from './commands/run.js';
23
28
  import { inspectCommand } from './commands/inspect.js';
24
29
  import { doctorCommand } from './commands/doctor.js';
25
- import { getExitCode, UsageError, DataError, CrashError } from './util/errors.js';
30
+ import { gatesCommand } from './commands/gates.js';
31
+ import { gaCommand } from './commands/ga.js';
32
+ import { releaseCheckCommand } from './commands/release-check.js';
33
+ import { securityCheckCommand } from './commands/security-check.js';
34
+ import { baselineCommand } from './commands/baseline.js';
35
+ import { truthCommand } from './commands/truth.js';
36
+ import { getExitCode, UsageError } from './util/errors.js';
37
+ import { getSourceCodeRequirementBanner } from '../verax/core/product-definition.js';
38
+ import { enableInspectionMode, disableInspectionMode } from './util/bootstrap-guard.js';
26
39
 
27
40
  const __filename = fileURLToPath(import.meta.url);
28
41
  const __dirname = dirname(__filename);
29
42
 
43
+ /**
44
+ * PHASE 21.6.1: CLI Self-Identification
45
+ * Prints diagnostic information to identify which binary is executed
46
+ */
47
+ function printCLIIdentity(args) {
48
+ const verbose = args.includes('--verbose');
49
+ const debugCli = process.env.VERAX_DEBUG_CLI === '1';
50
+
51
+ if (!verbose && !debugCli) {
52
+ return;
53
+ }
54
+
55
+ console.error('=== VERAX CLI IDENTITY ===');
56
+ console.error(`process.argv[1]: ${process.argv[1]}`);
57
+ console.error(`__filename: ${__filename}`);
58
+
59
+ try {
60
+ const pkgPath = resolve(__dirname, '../../package.json');
61
+ const pkg = JSON.parse(readFileSync(pkgPath, 'utf8'));
62
+ console.error(`package.json version: ${pkg.version}`);
63
+ console.error(`package.json location: ${pkgPath}`);
64
+ } catch (error) {
65
+ console.error(`Failed to read package.json: ${error.message}`);
66
+ }
67
+
68
+ console.error('========================');
69
+ }
70
+
30
71
  // Read package.json for version
31
72
  function getVersion() {
32
73
  try {
@@ -38,84 +79,263 @@ function getVersion() {
38
79
  }
39
80
  }
40
81
 
41
- async function main() {
42
- const args = process.argv.slice(2);
82
+ /**
83
+ * PHASE A: Raw argv parsing with zero side effects
84
+ *
85
+ * @param {string[]} args - Raw command line arguments
86
+ * @returns {Object} Parsed command structure
87
+ */
88
+ function parseCommand(args) {
89
+ // Handle --version (no side effects)
90
+ if (args.includes('--version') || args.includes('-v')) {
91
+ return { type: 'version' };
92
+ }
43
93
 
44
- try {
45
- // Handle --version
46
- if (args.includes('--version') || args.includes('-v')) {
47
- console.log(`verax ${getVersion()}`);
48
- process.exit(0);
94
+ // Handle explicit --help (no side effects)
95
+ if (args.includes('--help') || args.includes('-h')) {
96
+ return { type: 'help' };
97
+ }
98
+
99
+ // If no args, default to smart mode
100
+ if (args.length === 0) {
101
+ return { type: 'default', options: {} };
102
+ }
103
+
104
+ const command = args[0];
105
+
106
+ // Inspection commands (must bypass execution bootstrap)
107
+ if (command === 'inspect') {
108
+ if (args.length < 2) {
109
+ throw new UsageError('inspect command requires a run path argument');
49
110
  }
50
-
51
- // Handle explicit --help
52
- if (args.includes('--help') || args.includes('-h')) {
53
- showHelp();
54
- process.exit(0);
111
+ return {
112
+ type: 'inspect',
113
+ runPath: args[1],
114
+ json: args.includes('--json'),
115
+ timeline: args.includes('--timeline'),
116
+ decisions: args.includes('--decisions'),
117
+ failures: args.includes('--failures'),
118
+ performance: args.includes('--performance'),
119
+ evidence: args.includes('--evidence')
120
+ };
121
+ }
122
+
123
+ if (command === 'gates') {
124
+ return {
125
+ type: 'gates',
126
+ json: args.includes('--json'),
127
+ verbose: args.includes('--verbose')
128
+ };
129
+ }
130
+
131
+ if (command === 'ga') {
132
+ return {
133
+ type: 'ga',
134
+ runId: parseArg(args, '--run-id'),
135
+ json: args.includes('--json')
136
+ };
137
+ }
138
+
139
+ if (command === 'release:check' || command === 'release-check') {
140
+ return {
141
+ type: 'release:check',
142
+ json: args.includes('--json')
143
+ };
144
+ }
145
+
146
+ if (command === 'security:check' || command === 'security-check') {
147
+ return {
148
+ type: 'security:check',
149
+ json: args.includes('--json')
150
+ };
151
+ }
152
+
153
+ if (command === 'baseline') {
154
+ return {
155
+ type: 'baseline',
156
+ json: args.includes('--json')
157
+ };
158
+ }
159
+
160
+ if (command === 'truth') {
161
+ return {
162
+ type: 'truth',
163
+ runId: parseArg(args, '--run-id'),
164
+ json: args.includes('--json')
165
+ };
166
+ }
167
+
168
+ if (command === 'doctor') {
169
+ const allowedFlags = new Set(['--json']);
170
+ const extraFlags = args.slice(1).filter((a) => a.startsWith('-') && !allowedFlags.has(a));
171
+ return {
172
+ type: 'doctor',
173
+ json: args.includes('--json'),
174
+ extraFlags
175
+ };
176
+ }
177
+
178
+ // Execution commands
179
+ if (command === 'run') {
180
+ const url = parseArg(args, '--url');
181
+ const fixture = parseArg(args, '--fixture');
182
+ if (!url && !fixture) {
183
+ throw new UsageError('run command requires --url <url> or --fixture <fixture> argument');
55
184
  }
56
-
57
- // If no args, run default command
58
- if (args.length === 0) {
59
- await defaultCommand({});
60
- process.exit(0);
185
+ return {
186
+ type: 'run',
187
+ url,
188
+ fixture,
189
+ src: parseArg(args, '--src') || '.',
190
+ out: parseArg(args, '--out') || '.verax',
191
+ json: args.includes('--json'),
192
+ verbose: args.includes('--verbose'),
193
+ determinism: args.includes('--determinism'),
194
+ determinismRuns: args.includes('--determinism') ? parseInt(parseArg(args, '--determinism-runs') || '2', 10) : 0
195
+ };
196
+ }
197
+
198
+ if (command === 'help' || command === '--help' || command === '-h') {
199
+ return { type: 'help' };
200
+ }
201
+
202
+ // Default command: smart scanning mode
203
+ return {
204
+ type: 'default',
205
+ options: {
206
+ url: parseArg(args, '--url'),
207
+ src: parseArg(args, '--src') || '.',
208
+ out: parseArg(args, '--out') || '.verax',
209
+ json: args.includes('--json'),
210
+ verbose: args.includes('--verbose')
61
211
  }
212
+ };
213
+ }
214
+
215
+ /**
216
+ * PHASE B: Dispatch with isolation enforcement
217
+ *
218
+ * @param {Object} parsed - Parsed command from Phase A
219
+ */
220
+ async function dispatch(parsed) {
221
+ try {
222
+ // Inspection commands: enable guard mode
223
+ const isInspectionCommand = ['inspect', 'gates', 'ga', 'doctor', 'release:check', 'security:check', 'baseline', 'truth'].includes(parsed.type);
62
224
 
63
- const command = args[0];
64
-
65
- // Handle 'run' command
66
- if (command === 'run') {
67
- const url = parseArg(args, '--url');
68
- const src = parseArg(args, '--src') || '.';
69
- const out = parseArg(args, '--out') || '.verax';
70
- const json = args.includes('--json');
71
- const verbose = args.includes('--verbose');
72
-
73
- if (!url) {
74
- throw new UsageError('run command requires --url <url> argument');
75
- }
76
-
77
- const result = await runCommand({ url, src, out, json, verbose });
78
- process.exit(0);
225
+ if (isInspectionCommand) {
226
+ enableInspectionMode();
79
227
  }
80
228
 
81
- // Handle 'inspect' command
82
- if (command === 'inspect') {
83
- if (args.length < 2) {
84
- throw new UsageError('inspect command requires a run path argument');
229
+ try {
230
+ switch (parsed.type) {
231
+ case 'version':
232
+ console.log(`verax ${getVersion()}`);
233
+ process.exit(0);
234
+ break;
235
+
236
+ case 'help':
237
+ showHelp();
238
+ process.exit(0);
239
+ break;
240
+
241
+ case 'inspect':
242
+ await inspectCommand(parsed.runPath, {
243
+ json: parsed.json,
244
+ timeline: parsed.timeline,
245
+ decisions: parsed.decisions,
246
+ failures: parsed.failures,
247
+ performance: parsed.performance,
248
+ evidence: parsed.evidence
249
+ });
250
+ process.exit(0);
251
+ break;
252
+
253
+ case 'gates':
254
+ await gatesCommand({ json: parsed.json, verbose: parsed.verbose });
255
+ // gatesCommand handles exit code internally
256
+ return;
257
+
258
+ case 'ga':
259
+ await gaCommand({ runId: parsed.runId, json: parsed.json });
260
+ // gaCommand handles exit code internally
261
+ return;
262
+
263
+ case 'release:check':
264
+ await releaseCheckCommand({ json: parsed.json });
265
+ // releaseCheckCommand handles exit code internally
266
+ return;
267
+
268
+ case 'security:check':
269
+ await securityCheckCommand({ json: parsed.json });
270
+ // securityCheckCommand handles exit code internally
271
+ return;
272
+
273
+ case 'baseline':
274
+ await baselineCommand(process.cwd(), { json: parsed.json });
275
+ process.exit(0);
276
+ break;
277
+
278
+ case 'truth':
279
+ await truthCommand(process.cwd(), { json: parsed.json, runId: parsed.runId });
280
+ process.exit(0);
281
+ break;
282
+
283
+ case 'doctor':
284
+ await doctorCommand({ json: parsed.json, extraFlags: parsed.extraFlags });
285
+ process.exit(0);
286
+ break;
287
+
288
+ case 'run':
289
+ await runCommand({
290
+ url: parsed.url,
291
+ fixture: parsed.fixture,
292
+ src: parsed.src,
293
+ out: parsed.out,
294
+ json: parsed.json,
295
+ verbose: parsed.verbose,
296
+ determinism: parsed.determinism,
297
+ determinismRuns: parsed.determinismRuns
298
+ });
299
+ process.exit(0);
300
+ break;
301
+
302
+ case 'default':
303
+ await defaultCommand(parsed.options);
304
+ process.exit(0);
305
+ break;
306
+
307
+ default:
308
+ throw new UsageError(`Unknown command: ${parsed.type}`);
309
+ }
310
+ } finally {
311
+ if (isInspectionCommand) {
312
+ disableInspectionMode();
85
313
  }
86
-
87
- const runPath = args[1];
88
- const json = args.includes('--json');
89
-
90
- const result = await inspectCommand(runPath, { json });
91
- process.exit(0);
92
- }
93
-
94
- // Handle 'doctor' command
95
- if (command === 'doctor') {
96
- const allowedFlags = new Set(['--json']);
97
- const extraFlags = args.slice(1).filter((a) => a.startsWith('-') && !allowedFlags.has(a));
98
- const json = args.includes('--json');
99
- const result = await doctorCommand({ json, extraFlags });
100
- process.exit(0);
101
314
  }
102
-
103
- // Handle 'help' command
104
- if (command === 'help' || command === '--help' || command === '-h') {
105
- showHelp();
106
- process.exit(0);
315
+ } catch (error) {
316
+ // Print error message
317
+ if (error.message) {
318
+ console.error(`Error: ${error.message}`);
107
319
  }
108
320
 
109
- // Default command: smart scanning mode
110
- // Options can be passed as flags before/after the default command position
111
- const url = parseArg(args, '--url');
112
- const src = parseArg(args, '--src') || '.';
113
- const out = parseArg(args, '--out') || '.verax';
114
- const json = args.includes('--json');
115
- const verbose = args.includes('--verbose');
321
+ // Get exit code
322
+ const exitCode = getExitCode(error);
323
+ process.exit(exitCode);
324
+ }
325
+ }
326
+
327
+ async function main() {
328
+ const args = process.argv.slice(2);
329
+
330
+ // PHASE 21.6.1: Print CLI identity for debugging
331
+ printCLIIdentity(args);
332
+
333
+ try {
334
+ // PHASE A: Parse command (zero side effects)
335
+ const parsed = parseCommand(args);
116
336
 
117
- const result = await defaultCommand({ url, src, out, json, verbose });
118
- process.exit(0);
337
+ // PHASE B: Dispatch with isolation enforcement
338
+ await dispatch(parsed);
119
339
  } catch (error) {
120
340
  // Print error message
121
341
  if (error.message) {
@@ -130,24 +350,41 @@ async function main() {
130
350
 
131
351
  function showHelp() {
132
352
  const version = getVersion();
353
+ const banner = getSourceCodeRequirementBanner();
133
354
  console.log(`
134
355
  verax ${version}
135
356
  VERAX — Silent failure detection for websites
136
357
 
358
+ ${banner}
359
+ Canonical product contract: docs/README.md
360
+
137
361
  USAGE:
138
362
  verax [options] Smart mode (detects/prompts for URL)
139
363
  verax run --url <url> [options] Strict mode (non-interactive, CI-friendly)
140
364
  verax inspect <runPath> [--json] Inspect an existing run
365
+ verax inspect <runPath> --timeline Show chronological timeline
366
+ verax inspect <runPath> --decisions Show decision trace
367
+ verax inspect <runPath> --failures Show failure ledger
368
+ verax inspect <runPath> --performance Show performance metrics
369
+ verax inspect <runPath> --evidence Show evidence cross-index
141
370
  verax doctor [--json] Diagnose local environment
371
+ verax gates [--json] [--verbose] Evaluate capability gates (PHASE 19)
372
+ verax ga [--run-id <id>] [--json] Evaluate GA readiness (PHASE 21.6)
373
+ verax release:check [--json] Check release readiness (PHASE 21.7)
374
+ verax security:check [--json] Check security baseline (PHASE 21.8)
375
+ verax baseline [--json] Show baseline hash and drift status (PHASE 21.11)
376
+ verax truth [--run-id <id>] [--json] Show truth certificate (PHASE 21.11)
142
377
  verax --version Show version
143
378
  verax --help Show this help
144
379
 
145
380
  OPTIONS:
146
381
  --url <url> Target URL to scan
147
- --src <path> Source directory (default: .)
382
+ --src <path> Source directory (default: .) — required; URL-only scans are blocked
148
383
  --out <path> Output directory for artifacts (default: .verax)
149
384
  --json Output as JSON lines (progress events)
150
385
  --verbose Verbose output
386
+ --determinism Run determinism check (executes scan multiple times and compares results)
387
+ --determinism-runs <N> Number of runs for determinism check (default: 2)
151
388
  --help Show this help
152
389
  --version Show version
153
390
 
@@ -0,0 +1,179 @@
1
+ /**
2
+ * PHASE 20 — Angular Component Extractor
3
+ *
4
+ * Extracts component metadata, template, and class content from Angular TypeScript files.
5
+ * Handles @Component decorators, template files, and component class methods.
6
+ * Deterministic and robust (no external runtime execution).
7
+ */
8
+
9
+ /**
10
+ * PHASE 20: Extract Angular component metadata
11
+ *
12
+ * @param {string} content - Full .ts file content
13
+ * @param {string} filePath - Path to the .ts file (for context)
14
+ * @param {string} projectRoot - Project root directory
15
+ * @returns {Object} { componentClass: {content, startLine}, template: {content, path, isInline}, decorator: {content, startLine} }
16
+ */
17
+ export function extractAngularComponent(content, filePath, projectRoot) {
18
+ const result = {
19
+ componentClass: null,
20
+ template: null,
21
+ decorator: null,
22
+ };
23
+
24
+ try {
25
+ // Extract @Component decorator
26
+ const decoratorRegex = /@Component\s*\(\s*\{([\s\S]*?)\}\s*\)/;
27
+ const decoratorMatch = content.match(decoratorRegex);
28
+
29
+ if (decoratorMatch) {
30
+ const decoratorContent = decoratorMatch[0];
31
+ const decoratorConfig = decoratorMatch[1];
32
+ const beforeDecorator = content.substring(0, decoratorMatch.index);
33
+ const decoratorStartLine = (beforeDecorator.match(/\n/g) || []).length + 1;
34
+
35
+ result.decorator = {
36
+ content: decoratorConfig,
37
+ startLine: decoratorStartLine,
38
+ };
39
+
40
+ // Extract template (inline or external file)
41
+ const templateMatch = decoratorConfig.match(/template\s*:\s*['"`]([\s\S]*?)['"`]/);
42
+ const templateUrlMatch = decoratorConfig.match(/templateUrl\s*:\s*['"`]([^'"`]+)['"`]/);
43
+
44
+ if (templateMatch) {
45
+ // Inline template
46
+ result.template = {
47
+ content: templateMatch[1],
48
+ path: filePath,
49
+ isInline: true,
50
+ };
51
+ } else if (templateUrlMatch) {
52
+ // External template file
53
+ const templateUrl = templateUrlMatch[1];
54
+ const templatePath = resolveTemplatePath(templateUrl, filePath, projectRoot);
55
+ result.template = {
56
+ path: templatePath,
57
+ isInline: false,
58
+ };
59
+ }
60
+ }
61
+
62
+ // Extract component class
63
+ const classRegex = /export\s+class\s+(\w+)\s*(?:extends\s+\w+)?\s*\{([\s\S]*?)\n\}/;
64
+ const classMatch = content.match(classRegex);
65
+
66
+ if (classMatch) {
67
+ const beforeClass = content.substring(0, classMatch.index);
68
+ const classStartLine = (beforeClass.match(/\n/g) || []).length + 1;
69
+
70
+ result.componentClass = {
71
+ content: classMatch[2],
72
+ className: classMatch[1],
73
+ startLine: classStartLine,
74
+ };
75
+ }
76
+ } catch (error) {
77
+ // Skip if extraction fails
78
+ }
79
+
80
+ return result;
81
+ }
82
+
83
+ /**
84
+ * Resolve template path from templateUrl
85
+ *
86
+ * @param {string} templateUrl - Template URL from decorator
87
+ * @param {string} componentPath - Path to component file
88
+ * @param {string} projectRoot - Project root directory
89
+ * @returns {string} Resolved template path
90
+ */
91
+ function resolveTemplatePath(templateUrl, componentPath, projectRoot) {
92
+ const { join, dirname, resolve } = require('path');
93
+
94
+ if (templateUrl.startsWith('./') || templateUrl.startsWith('../')) {
95
+ // Relative path
96
+ return resolve(dirname(componentPath), templateUrl);
97
+ } else {
98
+ // Absolute path from project root
99
+ return resolve(projectRoot, templateUrl);
100
+ }
101
+ }
102
+
103
+ /**
104
+ * Extract template bindings from Angular template
105
+ * Detects event handlers, property bindings, and structural directives
106
+ *
107
+ * @param {string} templateContent - Template content
108
+ * @returns {Object} { eventHandlers: [], propertyBindings: [], structuralDirectives: [] }
109
+ */
110
+ export function extractTemplateBindings(templateContent) {
111
+ const eventHandlers = [];
112
+ const propertyBindings = [];
113
+ const structuralDirectives = [];
114
+
115
+ // Extract event handlers: (click)="handler()", (submit)="onSubmit()"
116
+ const eventHandlerRegex = /\((\w+)\)\s*=\s*["']([^"']+)["']/g;
117
+ let handlerMatch;
118
+ while ((handlerMatch = eventHandlerRegex.exec(templateContent)) !== null) {
119
+ eventHandlers.push({
120
+ event: handlerMatch[1],
121
+ handler: handlerMatch[2],
122
+ line: (templateContent.substring(0, handlerMatch.index).match(/\n/g) || []).length + 1,
123
+ });
124
+ }
125
+
126
+ // Extract property bindings: [property]="value", [disabled]="isDisabled"
127
+ const propertyBindingRegex = /\[(\w+)\]\s*=\s*["']([^"']+)["']/g;
128
+ let bindingMatch;
129
+ while ((bindingMatch = propertyBindingRegex.exec(templateContent)) !== null) {
130
+ propertyBindings.push({
131
+ property: bindingMatch[1],
132
+ value: bindingMatch[2],
133
+ line: (templateContent.substring(0, bindingMatch.index).match(/\n/g) || []).length + 1,
134
+ });
135
+ }
136
+
137
+ // Extract structural directives: *ngIf="condition", *ngFor="item of items"
138
+ const structuralDirectiveRegex = /\*ng(If|For|Switch)\s*=\s*["']([^"']+)["']/g;
139
+ let directiveMatch;
140
+ while ((directiveMatch = structuralDirectiveRegex.exec(templateContent)) !== null) {
141
+ structuralDirectives.push({
142
+ directive: directiveMatch[1],
143
+ expression: directiveMatch[2],
144
+ line: (templateContent.substring(0, directiveMatch.index).match(/\n/g) || []).length + 1,
145
+ });
146
+ }
147
+
148
+ return {
149
+ eventHandlers,
150
+ propertyBindings,
151
+ structuralDirectives,
152
+ };
153
+ }
154
+
155
+ /**
156
+ * Map template handlers to component class methods
157
+ *
158
+ * @param {Array} eventHandlers - Event handlers from template
159
+ * @param {string} classContent - Component class content
160
+ * @returns {Array} Mapped handlers with method references
161
+ */
162
+ export function mapTemplateHandlersToClass(eventHandlers, classContent) {
163
+ return eventHandlers.map(handler => {
164
+ // Extract method name from handler expression
165
+ const methodName = handler.handler.split('(')[0].trim();
166
+
167
+ // Try to find method definition in class
168
+ const methodRegex = new RegExp(`(?:public|private|protected)?\\s*(?:async\\s+)?${methodName}\\s*\\(`, 'g');
169
+ const methodMatch = methodRegex.exec(classContent);
170
+
171
+ return {
172
+ ...handler,
173
+ methodName,
174
+ methodFound: !!methodMatch,
175
+ methodLine: methodMatch ? (classContent.substring(0, methodMatch.index).match(/\n/g) || []).length + 1 : null,
176
+ };
177
+ });
178
+ }
179
+