@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,113 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PHASE 21.11 — Truth Command
|
|
3
|
+
*
|
|
4
|
+
* `verax truth` - Shows truth certificate summary
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { findLatestRunId } from '../util/run-resolver.js';
|
|
8
|
+
import { generateTruthCertificate, loadTruthCertificate } from '../../verax/core/truth/truth.certificate.js';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Truth command
|
|
12
|
+
*
|
|
13
|
+
* @param {string} projectDir - Project directory
|
|
14
|
+
* @param {Object} options - Command options
|
|
15
|
+
*/
|
|
16
|
+
export async function truthCommand(projectDir, options = {}) {
|
|
17
|
+
const { json = false, runId: runIdOpt = null } = options;
|
|
18
|
+
|
|
19
|
+
const runId = runIdOpt || findLatestRunId(projectDir);
|
|
20
|
+
|
|
21
|
+
if (!runId) {
|
|
22
|
+
if (json) {
|
|
23
|
+
console.log(JSON.stringify({
|
|
24
|
+
error: 'NO_RUNS_FOUND',
|
|
25
|
+
message: 'No runs found. Run a scan first.'
|
|
26
|
+
}, null, 2));
|
|
27
|
+
} else {
|
|
28
|
+
console.log('\n=== Truth Certificate ===\n');
|
|
29
|
+
console.log('Error: No runs found. Run a scan first.\n');
|
|
30
|
+
}
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Try to load existing certificate, otherwise generate
|
|
35
|
+
let certificate = loadTruthCertificate(projectDir, runId);
|
|
36
|
+
|
|
37
|
+
if (!certificate) {
|
|
38
|
+
certificate = await generateTruthCertificate(projectDir, runId);
|
|
39
|
+
if (certificate) {
|
|
40
|
+
const { writeTruthCertificate } = await import('../../verax/core/truth/truth.certificate.js');
|
|
41
|
+
writeTruthCertificate(projectDir, runId, certificate);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (!certificate) {
|
|
46
|
+
if (json) {
|
|
47
|
+
console.log(JSON.stringify({
|
|
48
|
+
error: 'CERTIFICATE_GENERATION_FAILED',
|
|
49
|
+
message: 'Failed to generate truth certificate'
|
|
50
|
+
}, null, 2));
|
|
51
|
+
} else {
|
|
52
|
+
console.log('\n=== Truth Certificate ===\n');
|
|
53
|
+
console.log('Error: Failed to generate truth certificate\n');
|
|
54
|
+
}
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (json) {
|
|
59
|
+
console.log(JSON.stringify(certificate, null, 2));
|
|
60
|
+
} else {
|
|
61
|
+
console.log('\n=== Truth Certificate ===\n');
|
|
62
|
+
console.log(`Run ID: ${certificate.runId}`);
|
|
63
|
+
console.log(`URL: ${certificate.url || 'N/A'}`);
|
|
64
|
+
console.log(`Generated: ${certificate.generatedAt}`);
|
|
65
|
+
|
|
66
|
+
console.log('\nEvidence Law:');
|
|
67
|
+
console.log(` Status: ${certificate.evidenceLaw.status}`);
|
|
68
|
+
console.log(` Violated: ${certificate.evidenceLaw.violated ? 'YES' : 'NO'}`);
|
|
69
|
+
|
|
70
|
+
console.log('\nDeterminism:');
|
|
71
|
+
console.log(` Verdict: ${certificate.determinism.verdict}`);
|
|
72
|
+
console.log(` Message: ${certificate.determinism.message}`);
|
|
73
|
+
|
|
74
|
+
console.log('\nFailures:');
|
|
75
|
+
console.log(` Total: ${certificate.failures.total}`);
|
|
76
|
+
console.log(` Blocking: ${certificate.failures.blocking ? 'YES' : 'NO'}`);
|
|
77
|
+
console.log(` Degraded: ${certificate.failures.degraded ? 'YES' : 'NO'}`);
|
|
78
|
+
console.log(` By Severity: ${JSON.stringify(certificate.failures.bySeverity)}`);
|
|
79
|
+
|
|
80
|
+
console.log('\nGA:');
|
|
81
|
+
console.log(` Verdict: ${certificate.ga.verdict}`);
|
|
82
|
+
console.log(` Ready: ${certificate.ga.ready ? 'YES' : 'NO'}`);
|
|
83
|
+
console.log(` Blockers: ${certificate.ga.blockers}`);
|
|
84
|
+
console.log(` Warnings: ${certificate.ga.warnings}`);
|
|
85
|
+
|
|
86
|
+
console.log('\nSecurity:');
|
|
87
|
+
console.log(` Overall: ${certificate.security.overall}`);
|
|
88
|
+
console.log(` Secrets: ${certificate.security.secrets}`);
|
|
89
|
+
console.log(` Vulnerabilities: ${certificate.security.vulnerabilities}`);
|
|
90
|
+
|
|
91
|
+
console.log('\nPerformance:');
|
|
92
|
+
console.log(` Verdict: ${certificate.performance.verdict}`);
|
|
93
|
+
console.log(` OK: ${certificate.performance.ok ? 'YES' : 'NO'}`);
|
|
94
|
+
console.log(` Violations: ${certificate.performance.violations}`);
|
|
95
|
+
|
|
96
|
+
console.log('\nBaseline:');
|
|
97
|
+
console.log(` Hash: ${certificate.baseline.hash || 'N/A'}`);
|
|
98
|
+
console.log(` Frozen: ${certificate.baseline.frozen ? 'YES' : 'NO'}`);
|
|
99
|
+
console.log(` Version: ${certificate.baseline.version || 'N/A'}`);
|
|
100
|
+
|
|
101
|
+
console.log('\nProvenance:');
|
|
102
|
+
console.log(` Hash: ${certificate.provenance.hash || 'N/A'}`);
|
|
103
|
+
console.log(` Version: ${certificate.provenance.version || 'N/A'}`);
|
|
104
|
+
|
|
105
|
+
console.log('\nOverall Verdict:');
|
|
106
|
+
console.log(` Status: ${certificate.overallVerdict.status}`);
|
|
107
|
+
if (certificate.overallVerdict.reasons.length > 0) {
|
|
108
|
+
console.log(` Reasons: ${certificate.overallVerdict.reasons.join('; ')}`);
|
|
109
|
+
}
|
|
110
|
+
console.log('');
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
package/src/cli/entry.js
CHANGED
|
@@ -4,7 +4,6 @@
|
|
|
4
4
|
* VERAX CLI Entry Point
|
|
5
5
|
*
|
|
6
6
|
* Commands:
|
|
7
|
-
* - verax (smart default with URL detection/prompting)
|
|
8
7
|
* - verax run --url <url> (strict, non-interactive)
|
|
9
8
|
* - verax inspect <runPath> (read and display run summary)
|
|
10
9
|
*
|
|
@@ -13,25 +12,42 @@
|
|
|
13
12
|
* - 2: internal crash
|
|
14
13
|
* - 64: invalid CLI usage
|
|
15
14
|
* - 65: invalid input data
|
|
15
|
+
*
|
|
16
|
+
* DESIGN: Lazy imports for heavy modules
|
|
17
|
+
* --version and --help are fast and don't load Playwright or observation-engine
|
|
18
|
+
* Heavy modules are only loaded when running actual commands (run, inspect, doctor)
|
|
16
19
|
*/
|
|
17
20
|
|
|
18
21
|
import { fileURLToPath } from 'url';
|
|
19
22
|
import { dirname, resolve } from 'path';
|
|
20
23
|
import { readFileSync } from 'fs';
|
|
21
|
-
import { defaultCommand } from './commands/default.js';
|
|
22
|
-
import { runCommand } from './commands/run.js';
|
|
23
|
-
import { inspectCommand } from './commands/inspect.js';
|
|
24
|
-
import { doctorCommand } from './commands/doctor.js';
|
|
25
24
|
import { getExitCode, UsageError } from './util/errors.js';
|
|
26
25
|
|
|
27
26
|
const __filename = fileURLToPath(import.meta.url);
|
|
28
27
|
const __dirname = dirname(__filename);
|
|
29
28
|
|
|
29
|
+
// Lazy loaders for heavy modules (loaded only when needed)
|
|
30
|
+
async function loadRunCommand() {
|
|
31
|
+
const mod = await import('./commands/run.js');
|
|
32
|
+
return mod.runCommand;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
async function loadInspectCommand() {
|
|
36
|
+
const mod = await import('./commands/inspect.js');
|
|
37
|
+
return mod.inspectCommand;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
async function loadDoctorCommand() {
|
|
41
|
+
const mod = await import('./commands/doctor.js');
|
|
42
|
+
return mod.doctorCommand;
|
|
43
|
+
}
|
|
44
|
+
|
|
30
45
|
// Read package.json for version
|
|
31
46
|
function getVersion() {
|
|
32
47
|
try {
|
|
33
48
|
const pkgPath = resolve(__dirname, '../../package.json');
|
|
34
|
-
|
|
49
|
+
// @ts-expect-error - readFileSync with encoding returns string
|
|
50
|
+
const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));
|
|
35
51
|
return pkg.version;
|
|
36
52
|
} catch {
|
|
37
53
|
return 'unknown';
|
|
@@ -54,10 +70,10 @@ async function main() {
|
|
|
54
70
|
process.exit(0);
|
|
55
71
|
}
|
|
56
72
|
|
|
57
|
-
// If no args,
|
|
73
|
+
// If no args, show help (no interactive mode)
|
|
58
74
|
if (args.length === 0) {
|
|
59
|
-
|
|
60
|
-
process.exit(
|
|
75
|
+
showHelp();
|
|
76
|
+
process.exit(64);
|
|
61
77
|
}
|
|
62
78
|
|
|
63
79
|
const command = args[0];
|
|
@@ -74,6 +90,7 @@ async function main() {
|
|
|
74
90
|
throw new UsageError('run command requires --url <url> argument');
|
|
75
91
|
}
|
|
76
92
|
|
|
93
|
+
const runCommand = await loadRunCommand();
|
|
77
94
|
await runCommand({ url, src, out, json, verbose });
|
|
78
95
|
process.exit(0);
|
|
79
96
|
}
|
|
@@ -87,6 +104,7 @@ async function main() {
|
|
|
87
104
|
const runPath = args[1];
|
|
88
105
|
const json = args.includes('--json');
|
|
89
106
|
|
|
107
|
+
const inspectCommand = await loadInspectCommand();
|
|
90
108
|
await inspectCommand(runPath, { json });
|
|
91
109
|
process.exit(0);
|
|
92
110
|
}
|
|
@@ -96,6 +114,7 @@ async function main() {
|
|
|
96
114
|
const allowedFlags = new Set(['--json']);
|
|
97
115
|
const extraFlags = args.slice(1).filter((a) => a.startsWith('-') && !allowedFlags.has(a));
|
|
98
116
|
const json = args.includes('--json');
|
|
117
|
+
const doctorCommand = await loadDoctorCommand();
|
|
99
118
|
await doctorCommand({ json, extraFlags });
|
|
100
119
|
process.exit(0);
|
|
101
120
|
}
|
|
@@ -106,16 +125,8 @@ async function main() {
|
|
|
106
125
|
process.exit(0);
|
|
107
126
|
}
|
|
108
127
|
|
|
109
|
-
//
|
|
110
|
-
|
|
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');
|
|
116
|
-
|
|
117
|
-
await defaultCommand({ url, src, out, json, verbose });
|
|
118
|
-
process.exit(0);
|
|
128
|
+
// Interactive mode removed
|
|
129
|
+
throw new UsageError('Interactive mode is disabled. Use: verax run --url <url>');
|
|
119
130
|
} catch (error) {
|
|
120
131
|
// Print error message
|
|
121
132
|
if (error.message) {
|
|
@@ -135,7 +146,6 @@ verax ${version}
|
|
|
135
146
|
VERAX — Silent failure detection for websites
|
|
136
147
|
|
|
137
148
|
USAGE:
|
|
138
|
-
verax [options] Smart mode (detects/prompts for URL)
|
|
139
149
|
verax run --url <url> [options] Strict mode (non-interactive, CI-friendly)
|
|
140
150
|
verax inspect <runPath> [--json] Inspect an existing run
|
|
141
151
|
verax doctor [--json] Diagnose local environment
|
|
@@ -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: _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
|
+
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PHASE 20 — Angular Navigation Detector
|
|
3
|
+
*
|
|
4
|
+
* Detects navigation promises in Angular applications:
|
|
5
|
+
* - routerLink directive in templates
|
|
6
|
+
* - Router.navigate() calls in component methods
|
|
7
|
+
* - ActivatedRoute navigation
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { extractAngularComponent, extractTemplateBindings, mapTemplateHandlersToClass } from './angular-component-extractor.js'; // eslint-disable-line no-unused-vars
|
|
11
|
+
import { parse } from '@babel/parser';
|
|
12
|
+
import traverse from '@babel/traverse';
|
|
13
|
+
import { readFileSync, existsSync } from 'fs';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Detect navigation promises in Angular component
|
|
17
|
+
*
|
|
18
|
+
* @param {string} filePath - Path to .ts file
|
|
19
|
+
* @param {string} content - Full file content
|
|
20
|
+
* @param {string} projectRoot - Project root directory
|
|
21
|
+
* @returns {Array} Array of navigation expectations
|
|
22
|
+
*/
|
|
23
|
+
export function detectAngularNavigation(filePath, content, projectRoot) {
|
|
24
|
+
const expectations = [];
|
|
25
|
+
|
|
26
|
+
try {
|
|
27
|
+
const component = extractAngularComponent(content, filePath, projectRoot);
|
|
28
|
+
|
|
29
|
+
// Extract navigation from template (routerLink)
|
|
30
|
+
if (component.template) {
|
|
31
|
+
let templateContent = null;
|
|
32
|
+
|
|
33
|
+
if (component.template.isInline) {
|
|
34
|
+
templateContent = component.template.content;
|
|
35
|
+
} else if (existsSync(component.template.path)) {
|
|
36
|
+
templateContent = readFileSync(component.template.path, 'utf8');
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (templateContent) {
|
|
40
|
+
const _templateBindings = extractTemplateBindings(templateContent);
|
|
41
|
+
|
|
42
|
+
// Extract routerLink directives
|
|
43
|
+
const routerLinkRegex = /routerLink\s*=\s*["']([^"']+)["']/g;
|
|
44
|
+
let linkMatch;
|
|
45
|
+
while ((linkMatch = routerLinkRegex.exec(templateContent)) !== null) {
|
|
46
|
+
const href = linkMatch[1];
|
|
47
|
+
// Skip external links and hash-only links
|
|
48
|
+
if (href.startsWith('http://') || href.startsWith('https://') || href.startsWith('#')) {
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const beforeMatch = templateContent.substring(0, linkMatch.index);
|
|
53
|
+
const line = (beforeMatch.match(/\n/g) || []).length + 1;
|
|
54
|
+
|
|
55
|
+
expectations.push({
|
|
56
|
+
type: 'navigation',
|
|
57
|
+
target: href,
|
|
58
|
+
context: 'template',
|
|
59
|
+
sourceRef: {
|
|
60
|
+
file: component.template.isInline ? filePath : component.template.path,
|
|
61
|
+
line,
|
|
62
|
+
snippet: linkMatch[0],
|
|
63
|
+
},
|
|
64
|
+
proof: 'PROVEN_EXPECTATION',
|
|
65
|
+
metadata: {
|
|
66
|
+
navigationType: 'routerLink',
|
|
67
|
+
},
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Extract navigation from component class (Router.navigate())
|
|
74
|
+
if (component.componentClass && component.componentClass.content) {
|
|
75
|
+
try {
|
|
76
|
+
const ast = parse(component.componentClass.content, {
|
|
77
|
+
sourceType: 'module',
|
|
78
|
+
plugins: ['typescript', 'decorators-legacy', 'classProperties'],
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
traverse.default(ast, {
|
|
82
|
+
CallExpression(path) {
|
|
83
|
+
const { node } = path;
|
|
84
|
+
|
|
85
|
+
// Detect router.navigate() calls
|
|
86
|
+
if (
|
|
87
|
+
node.callee.type === 'MemberExpression' &&
|
|
88
|
+
node.callee.property.name === 'navigate' &&
|
|
89
|
+
node.arguments.length > 0
|
|
90
|
+
) {
|
|
91
|
+
const arg = node.arguments[0];
|
|
92
|
+
let target = null;
|
|
93
|
+
|
|
94
|
+
if (arg.type === 'StringLiteral') {
|
|
95
|
+
target = arg.value;
|
|
96
|
+
} else if (arg.type === 'ArrayExpression' && arg.elements.length > 0) {
|
|
97
|
+
// Router.navigate(['/path']) or Router.navigate(['/path', param])
|
|
98
|
+
const firstElement = arg.elements[0];
|
|
99
|
+
if (firstElement.type === 'StringLiteral') {
|
|
100
|
+
target = firstElement.value;
|
|
101
|
+
}
|
|
102
|
+
} else if (arg.type === 'TemplateLiteral' && arg.quasis.length === 1) {
|
|
103
|
+
target = arg.quasis[0].value.raw;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if (target && !target.startsWith('http://') && !target.startsWith('https://') && !target.startsWith('#')) {
|
|
107
|
+
const location = node.loc;
|
|
108
|
+
const line = component.componentClass.startLine + (location ? location.start.line - 1 : 0);
|
|
109
|
+
|
|
110
|
+
expectations.push({
|
|
111
|
+
type: 'navigation',
|
|
112
|
+
target,
|
|
113
|
+
context: 'router-navigate',
|
|
114
|
+
sourceRef: {
|
|
115
|
+
file: filePath,
|
|
116
|
+
line,
|
|
117
|
+
snippet: component.componentClass.content.substring(
|
|
118
|
+
node.start - (ast.program.body[0]?.start || 0),
|
|
119
|
+
node.end - (ast.program.body[0]?.start || 0)
|
|
120
|
+
),
|
|
121
|
+
},
|
|
122
|
+
proof: arg.type === 'StringLiteral' ? 'PROVEN_EXPECTATION' : 'LIKELY_EXPECTATION',
|
|
123
|
+
metadata: {
|
|
124
|
+
navigationType: 'router-navigate',
|
|
125
|
+
},
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
},
|
|
130
|
+
});
|
|
131
|
+
} catch (parseError) {
|
|
132
|
+
// Skip if parsing fails
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
} catch (error) {
|
|
136
|
+
// Skip if extraction fails
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
return expectations;
|
|
140
|
+
}
|
|
141
|
+
|