@veraxhq/verax 0.2.0 → 0.2.1

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 (89) hide show
  1. package/package.json +14 -4
  2. package/src/cli/commands/default.js +244 -86
  3. package/src/cli/commands/doctor.js +36 -4
  4. package/src/cli/commands/run.js +253 -69
  5. package/src/cli/entry.js +5 -5
  6. package/src/cli/util/detection-engine.js +4 -3
  7. package/src/cli/util/events.js +76 -0
  8. package/src/cli/util/expectation-extractor.js +11 -1
  9. package/src/cli/util/findings-writer.js +1 -0
  10. package/src/cli/util/observation-engine.js +69 -23
  11. package/src/cli/util/paths.js +3 -2
  12. package/src/cli/util/project-discovery.js +20 -0
  13. package/src/cli/util/redact.js +2 -2
  14. package/src/cli/util/runtime-budget.js +147 -0
  15. package/src/cli/util/summary-writer.js +12 -1
  16. package/src/types/global.d.ts +28 -0
  17. package/src/types/ts-ast.d.ts +24 -0
  18. package/src/verax/cli/doctor.js +2 -2
  19. package/src/verax/cli/init.js +1 -1
  20. package/src/verax/cli/url-safety.js +12 -2
  21. package/src/verax/cli/wizard.js +13 -2
  22. package/src/verax/core/budget-engine.js +1 -1
  23. package/src/verax/core/decision-snapshot.js +2 -2
  24. package/src/verax/core/determinism-model.js +35 -6
  25. package/src/verax/core/incremental-store.js +15 -7
  26. package/src/verax/core/replay-validator.js +4 -4
  27. package/src/verax/core/replay.js +1 -1
  28. package/src/verax/core/silence-impact.js +1 -1
  29. package/src/verax/core/silence-model.js +9 -7
  30. package/src/verax/detect/comparison.js +8 -3
  31. package/src/verax/detect/confidence-engine.js +17 -17
  32. package/src/verax/detect/detection-engine.js +1 -1
  33. package/src/verax/detect/evidence-index.js +15 -65
  34. package/src/verax/detect/expectation-model.js +54 -3
  35. package/src/verax/detect/explanation-helpers.js +1 -1
  36. package/src/verax/detect/finding-detector.js +2 -2
  37. package/src/verax/detect/findings-writer.js +9 -16
  38. package/src/verax/detect/flow-detector.js +4 -4
  39. package/src/verax/detect/index.js +37 -11
  40. package/src/verax/detect/interactive-findings.js +3 -4
  41. package/src/verax/detect/signal-mapper.js +2 -2
  42. package/src/verax/detect/skip-classifier.js +4 -4
  43. package/src/verax/detect/verdict-engine.js +4 -6
  44. package/src/verax/flow/flow-engine.js +3 -2
  45. package/src/verax/flow/flow-spec.js +1 -2
  46. package/src/verax/index.js +15 -3
  47. package/src/verax/intel/effect-detector.js +1 -1
  48. package/src/verax/intel/index.js +2 -2
  49. package/src/verax/intel/route-extractor.js +3 -3
  50. package/src/verax/intel/vue-navigation-extractor.js +81 -18
  51. package/src/verax/intel/vue-router-extractor.js +4 -2
  52. package/src/verax/learn/action-contract-extractor.js +3 -3
  53. package/src/verax/learn/ast-contract-extractor.js +53 -1
  54. package/src/verax/learn/index.js +36 -2
  55. package/src/verax/learn/manifest-writer.js +28 -14
  56. package/src/verax/learn/route-extractor.js +1 -1
  57. package/src/verax/learn/route-validator.js +8 -7
  58. package/src/verax/learn/state-extractor.js +1 -1
  59. package/src/verax/learn/static-extractor-navigation.js +1 -1
  60. package/src/verax/learn/static-extractor-validation.js +2 -2
  61. package/src/verax/learn/static-extractor.js +8 -7
  62. package/src/verax/learn/ts-contract-resolver.js +14 -12
  63. package/src/verax/observe/browser.js +22 -3
  64. package/src/verax/observe/console-sensor.js +2 -2
  65. package/src/verax/observe/expectation-executor.js +2 -1
  66. package/src/verax/observe/focus-sensor.js +1 -1
  67. package/src/verax/observe/human-driver.js +29 -10
  68. package/src/verax/observe/index.js +10 -7
  69. package/src/verax/observe/interaction-discovery.js +27 -15
  70. package/src/verax/observe/interaction-runner.js +6 -6
  71. package/src/verax/observe/loading-sensor.js +6 -0
  72. package/src/verax/observe/navigation-sensor.js +1 -1
  73. package/src/verax/observe/settle.js +1 -0
  74. package/src/verax/observe/state-sensor.js +8 -4
  75. package/src/verax/observe/state-ui-sensor.js +7 -1
  76. package/src/verax/observe/traces-writer.js +27 -16
  77. package/src/verax/observe/ui-signal-sensor.js +7 -0
  78. package/src/verax/scan-summary-writer.js +5 -2
  79. package/src/verax/shared/artifact-manager.js +1 -1
  80. package/src/verax/shared/budget-profiles.js +2 -2
  81. package/src/verax/shared/caching.js +1 -1
  82. package/src/verax/shared/config-loader.js +1 -2
  83. package/src/verax/shared/dynamic-route-utils.js +12 -6
  84. package/src/verax/shared/retry-policy.js +1 -6
  85. package/src/verax/shared/root-artifacts.js +1 -1
  86. package/src/verax/shared/zip-artifacts.js +1 -0
  87. package/src/verax/validate/context-validator.js +1 -1
  88. package/src/verax/observe/index.js.backup +0 -1
  89. package/src/verax/validate/context-validator.js.bak +0 -0
@@ -8,7 +8,7 @@
8
8
  * Generate a human summary - one short sentence describing what the USER experienced.
9
9
  * NO technical jargon.
10
10
  */
11
- export function generateHumanSummary(finding, trace) {
11
+ export function generateHumanSummary(finding, _trace) {
12
12
  const findingType = finding.type || '';
13
13
  const interaction = finding.interaction || {};
14
14
  const label = interaction.label || interaction.text || 'the button';
@@ -16,7 +16,7 @@ import { hasMeaningfulUrlChange, hasVisibleChange, hasDomChange } from './compar
16
16
  import { computeConfidence } from './confidence-engine.js';
17
17
  import { generateHumanSummary, generateActionHint, deriveConfidenceExplanation } from './explanation-helpers.js';
18
18
  import { mapFindingTypeToOutcome } from '../core/canonical-outcomes.js';
19
- import { inferPromiseFromInteraction, PROMISE_TYPES } from '../core/promise-model.js';
19
+ import { inferPromiseFromInteraction } from '../core/promise-model.js';
20
20
 
21
21
  /**
22
22
  * Map finding type to confidence engine type.
@@ -110,7 +110,7 @@ export function bindPromiseToFinding(finding, trace) {
110
110
  * Create finding from expectation-driven execution outcome.
111
111
  * Called when expectation execution resulted in SILENT_FAILURE.
112
112
  */
113
- export function createFindingFromExpectationOutcome(expectation, trace, beforeUrl, afterUrl, beforeScreenshot, afterScreenshot, projectDir, manifest) {
113
+ export function createFindingFromExpectationOutcome(expectation, trace, beforeUrl, afterUrl, beforeScreenshot, afterScreenshot, projectDir, _manifest) {
114
114
  const interaction = trace.interaction;
115
115
  const sensors = trace.sensors || {};
116
116
 
@@ -1,11 +1,10 @@
1
- import { resolve, dirname } from 'path';
1
+ import { resolve } from 'path';
2
2
  import { mkdirSync, writeFileSync } from 'fs';
3
- import { getArtifactPath } from '../core/run-id.js';
4
3
  import { CANONICAL_OUTCOMES } from '../core/canonical-outcomes.js';
5
4
 
6
5
  /**
7
- * Write findings to canonical artifact root. If a runDir can be inferred,
8
- * write to .verax/runs/<runId>/findings.json; otherwise fall back to legacy path.
6
+ * Write findings to canonical artifact root.
7
+ * Writes to .verax/runs/<runId>/findings.json.
9
8
  *
10
9
  * PHASE 2: Includes outcome classification summary.
11
10
  * PHASE 3: Includes promise type summary.
@@ -14,20 +13,14 @@ import { CANONICAL_OUTCOMES } from '../core/canonical-outcomes.js';
14
13
  * @param {string} url
15
14
  * @param {Array} findings
16
15
  * @param {Array} coverageGaps
17
- * @param {string|null} runDirOpt - Optional absolute run directory path
16
+ * @param {string} runDirOpt - Required absolute run directory path
18
17
  */
19
- export function writeFindings(projectDir, url, findings, coverageGaps = [], runDirOpt = null) {
20
- let findingsPath;
21
- if (runDirOpt) {
22
- // Canonical location
23
- mkdirSync(runDirOpt, { recursive: true });
24
- findingsPath = resolve(runDirOpt, 'findings.json');
25
- } else {
26
- // Legacy location for backward compatibility (tests without runId)
27
- const detectDir = resolve(projectDir, '.veraxverax', 'detect');
28
- mkdirSync(detectDir, { recursive: true });
29
- findingsPath = resolve(detectDir, 'findings.json');
18
+ export function writeFindings(projectDir, url, findings, coverageGaps = [], runDirOpt) {
19
+ if (!runDirOpt) {
20
+ throw new Error('runDirOpt is required');
30
21
  }
22
+ mkdirSync(runDirOpt, { recursive: true });
23
+ const findingsPath = resolve(runDirOpt, 'findings.json');
31
24
 
32
25
  // PHASE 2: Compute outcome summary
33
26
  const outcomeSummary = {};
@@ -133,9 +133,9 @@ export function detectFlowSilentFailures(traces, manifest, findings, coverageGap
133
133
  }
134
134
 
135
135
  // Look for silent failures followed by lack of recovery
136
- let hasSilentFailure = false;
136
+ const _hasSilentFailure = false;
137
137
  let failedStepIndex = -1;
138
- let failedExpectation = null;
138
+ const _failedExpectation = null;
139
139
 
140
140
  for (let i = 0; i < flowTraces.length; i++) {
141
141
  const trace = flowTraces[i];
@@ -248,9 +248,9 @@ export function detectFlowSilentFailures(traces, manifest, findings, coverageGap
248
248
 
249
249
  if (isSilentFailure && matchedExpectation) {
250
250
  // Silent failure detected at this step
251
- hasSilentFailure = true;
251
+ const _hasSilentFailure = true;
252
252
  failedStepIndex = i;
253
- failedExpectation = matchedExpectation;
253
+ const _failedExpectation = matchedExpectation;
254
254
 
255
255
  // Check if subsequent steps show UI recovery
256
256
  let hasSubsequentRecovery = false;
@@ -1,5 +1,5 @@
1
1
  import { readFileSync, existsSync } from 'fs';
2
- import { dirname } from 'path';
2
+ import { dirname, basename } from 'path';
3
3
  import { expectsNavigation } from './expectation-model.js';
4
4
  import { hasMeaningfulUrlChange, hasVisibleChange, hasDomChange } from './comparison.js';
5
5
  import { writeFindings } from './findings-writer.js';
@@ -7,7 +7,13 @@ import { getUrlPath } from './evidence-validator.js';
7
7
  import { classifySkipReason, collectSkipReasons } from './skip-classifier.js';
8
8
  import { detectInteractiveFindings } from './interactive-findings.js';
9
9
 
10
- export async function detect(manifestPath, tracesPath, validation = null) {
10
+ /**
11
+ * @param {string} manifestPath
12
+ * @param {string} tracesPath
13
+ * @param {Object} [validation]
14
+ * @returns {Promise<any>}
15
+ */
16
+ export async function detect(manifestPath, tracesPath, validation = null, _expectationCoverageGaps = null, _silenceTracker = null) {
11
17
  if (!existsSync(manifestPath)) {
12
18
  throw new Error(`Manifest not found: ${manifestPath}`);
13
19
  }
@@ -25,8 +31,21 @@ export async function detect(manifestPath, tracesPath, validation = null) {
25
31
  const projectDir = manifest.projectDir;
26
32
  const findings = [];
27
33
 
34
+ // Extract runId from tracesPath: .verax/runs/<runId>/observation-traces.json
35
+ let runId = null;
36
+ try {
37
+ const runDir = dirname(tracesPath);
38
+ const runDirBasename = basename(runDir);
39
+ // Check if runDir is in .verax/runs/<runId> structure
40
+ const parentDir = dirname(runDir);
41
+ if (basename(parentDir) === 'runs' && basename(dirname(parentDir)) === '.verax') {
42
+ runId = runDirBasename;
43
+ }
44
+ } catch {
45
+ // Ignore path parsing errors
46
+ }
47
+
28
48
  let interactionsAnalyzed = 0;
29
- let interactionsSkippedNoExpectation = 0;
30
49
  const skips = [];
31
50
 
32
51
  for (const trace of observation.traces) {
@@ -39,7 +58,6 @@ export async function detect(manifestPath, tracesPath, validation = null) {
39
58
  const expectsNav = expectsNavigation(manifest, interaction, beforeUrl);
40
59
 
41
60
  if (!expectsNav) {
42
- interactionsSkippedNoExpectation++;
43
61
  const skipReason = classifySkipReason(manifest, interaction, beforeUrl, validation);
44
62
  if (skipReason) {
45
63
  skips.push({
@@ -73,8 +91,8 @@ export async function detect(manifestPath, tracesPath, validation = null) {
73
91
  const interactionSelector = interaction.selector || '';
74
92
 
75
93
  if (selectorHint && interactionSelector) {
76
- const normalizedSelectorHint = selectorHint.replace(/[\[\]()]/g, '');
77
- const normalizedInteractionSelector = interactionSelector.replace(/[\[\]()]/g, '');
94
+ const normalizedSelectorHint = selectorHint.replace(/[[\]()]/g, '');
95
+ const normalizedInteractionSelector = interactionSelector.replace(/[[\]()]/g, '');
78
96
 
79
97
  if (selectorHint === interactionSelector ||
80
98
  selectorHint.includes(interactionSelector) ||
@@ -117,7 +135,6 @@ export async function detect(manifestPath, tracesPath, validation = null) {
117
135
  label: interaction.label
118
136
  }
119
137
  });
120
- interactionsSkippedNoExpectation++;
121
138
  continue;
122
139
  }
123
140
  }
@@ -161,7 +178,6 @@ export async function detect(manifestPath, tracesPath, validation = null) {
161
178
  label: interaction.label
162
179
  }
163
180
  });
164
- interactionsSkippedNoExpectation++;
165
181
  continue;
166
182
  } else if (matchingRoutes.length === 1) {
167
183
  expectedTargetPath = matchingRoutes[0];
@@ -180,14 +196,22 @@ export async function detect(manifestPath, tracesPath, validation = null) {
180
196
  label: interaction.label
181
197
  }
182
198
  });
183
- interactionsSkippedNoExpectation++;
184
199
  continue;
185
200
  }
186
201
 
187
202
  interactionsAnalyzed++;
188
203
 
189
204
  const hasUrlChange = hasMeaningfulUrlChange(beforeUrl, afterUrl);
190
- const hasVisibleChangeResult = hasVisibleChange(beforeScreenshot, afterScreenshot, projectDir);
205
+ // hasVisibleChange requires runId, skip comparison if runId unavailable
206
+ let hasVisibleChangeResult = false;
207
+ if (runId) {
208
+ try {
209
+ hasVisibleChangeResult = hasVisibleChange(beforeScreenshot, afterScreenshot, projectDir, runId);
210
+ } catch (e) {
211
+ // If screenshot comparison fails, treat as no visible change
212
+ hasVisibleChangeResult = false;
213
+ }
214
+ }
191
215
  const hasDomChangeResult = hasDomChange(trace);
192
216
 
193
217
  if (expectedTargetPath) {
@@ -263,7 +287,9 @@ export async function detect(manifestPath, tracesPath, validation = null) {
263
287
  let runDir = null;
264
288
  try {
265
289
  runDir = dirname(tracesPath);
266
- } catch {}
290
+ } catch {
291
+ // Ignore path parsing errors
292
+ }
267
293
 
268
294
  const findingsResult = writeFindings(projectDir, observation.url, findings, [], runDir);
269
295
 
@@ -2,7 +2,7 @@
2
2
  // Handles keyboard, hover, file_upload, login, logout, auth_guard interactions
3
3
  // Plus accessibility detections: focus, ARIA, keyboard trap, feedback gap, freeze
4
4
 
5
- import { hasMeaningfulUrlChange, hasVisibleChange, hasDomChange } from './comparison.js';
5
+ import { hasMeaningfulUrlChange, hasDomChange } from './comparison.js';
6
6
  import { computeConfidence } from './confidence-engine.js';
7
7
  import { enrichFindingWithExplanations } from './finding-detector.js';
8
8
 
@@ -14,10 +14,9 @@ import { enrichFindingWithExplanations } from './finding-detector.js';
14
14
  * @param {Array} traces - Interaction traces to analyze
15
15
  * @param {Object} manifest - Project manifest (not used currently)
16
16
  * @param {Array} findings - Findings array to append to
17
- * @param {Object} helpers - Helper functions (not used currently)
18
17
  * @returns {Array} Array of detected interactive findings
19
18
  */
20
- export function detectInteractiveFindings(traces, manifest, findings, helpers = {}) {
19
+ export function detectInteractiveFindings(traces, manifest, findings, _helpers = {}) {
21
20
  const interactiveFindings = [];
22
21
 
23
22
  for (const trace of traces) {
@@ -165,7 +164,7 @@ export function detectInteractiveFindings(traces, manifest, findings, helpers =
165
164
  const domChanged = hasDomChange(trace);
166
165
  const urlChanged = hasMeaningfulUrlChange(beforeUrl, afterUrl);
167
166
  const network = sensors.network || {};
168
- const hasNetwork = (network.totalRequests || 0) > 0;
167
+ const _hasNetwork = (network.totalRequests || 0) > 0;
169
168
  const loading = sensors.loading || {};
170
169
  const stateData = sensors.state || {};
171
170
 
@@ -136,8 +136,8 @@ export function mapOwnership(finding, trace = {}) {
136
136
  const findingType = finding.type || 'unknown';
137
137
  const sensors = trace.sensors || {};
138
138
  const hasNetwork = (sensors.network?.totalRequests || 0) > 0;
139
- const hasAria = sensors.aria !== undefined;
140
- const hasFocus = sensors.focus !== undefined;
139
+ const _hasAria = sensors.aria !== undefined;
140
+ const _hasFocus = sensors.focus !== undefined;
141
141
  const hasTiming = sensors.timing !== undefined;
142
142
 
143
143
  // ACCESSIBILITY: Focus, ARIA, keyboard trap failures
@@ -54,8 +54,8 @@ export function classifySkipReason(manifest, interaction, beforeUrl, validation
54
54
  const interactionSelector = interaction.selector || '';
55
55
 
56
56
  if (selectorHint && interactionSelector) {
57
- const normalizedSelectorHint = selectorHint.replace(/[\[\]()]/g, '');
58
- const normalizedInteractionSelector = interactionSelector.replace(/[\[\]()]/g, '');
57
+ const normalizedSelectorHint = selectorHint.replace(/[[\]()]/g, '');
58
+ const normalizedInteractionSelector = interactionSelector.replace(/[[\]()]/g, '');
59
59
 
60
60
  if (selectorHint === interactionSelector ||
61
61
  selectorHint.includes(interactionSelector) ||
@@ -76,8 +76,8 @@ export function classifySkipReason(manifest, interaction, beforeUrl, validation
76
76
  const interactionSelector = interaction.selector || '';
77
77
 
78
78
  if (selectorHint && interactionSelector) {
79
- const normalizedSelectorHint = selectorHint.replace(/[\[\]()]/g, '');
80
- const normalizedInteractionSelector = interactionSelector.replace(/[\[\]()]/g, '');
79
+ const normalizedSelectorHint = selectorHint.replace(/[[\]()]/g, '');
80
+ const normalizedInteractionSelector = interactionSelector.replace(/[[\]()]/g, '');
81
81
 
82
82
  if (selectorHint === interactionSelector ||
83
83
  selectorHint.includes(interactionSelector) ||
@@ -14,9 +14,7 @@
14
14
  * PHASE 4: All observations include Silence lifecycle - type, trigger, evaluation status, confidence impact.
15
15
  */
16
16
 
17
- import fs from 'fs';
18
- import path from 'path';
19
- import { buildEvidenceIndex, writeEvidenceIndex } from './evidence-index.js';
17
+ import { buildEvidenceIndex } from './evidence-index.js';
20
18
  import { CANONICAL_OUTCOMES } from '../core/canonical-outcomes.js';
21
19
  import { SILENCE_TYPES, EVALUATION_STATUS } from '../core/silence-model.js';
22
20
  import { inferPromiseFromInteraction } from '../core/promise-model.js';
@@ -74,7 +72,7 @@ export function computeObservationSummary(findings, observeTruth, learnTruth, co
74
72
  const outcome = finding.outcome || CANONICAL_OUTCOMES.SILENT_FAILURE; // Default for legacy findings
75
73
  const promiseType = finding.promise?.type || 'UNKNOWN_PROMISE'; // PHASE 3
76
74
 
77
- if (findingsByConfidence.hasOwnProperty(confidence)) {
75
+ if (Object.prototype.hasOwnProperty.call(findingsByConfidence, confidence)) {
78
76
  findingsByConfidence[confidence]++;
79
77
  }
80
78
  findingsByType[type] = (findingsByType[type] || 0) + 1;
@@ -402,7 +400,7 @@ export function formatObservationSummary(observationSummary) {
402
400
  for (const finding of obs.findings.slice(0, 3)) {
403
401
  const outcome = finding.outcome ? ` [${finding.outcome}]` : '';
404
402
  const promiseInfo = finding.promise ? ` (${finding.promise.type.replace(/_PROMISE$/, '')})` : '';
405
- const confStr = finding.confidence?.level ? ` (${finding.confidence.level} confidence)` : '';
403
+ const _confStr = finding.confidence?.level ? ` (${finding.confidence.level} confidence)` : '';
406
404
  const userStmt = finding.what_happened ? `User: ${finding.what_happened}` : '';
407
405
  lines.push(` • ${finding.type}${outcome}${promiseInfo}`);
408
406
  if (userStmt) lines.push(` ${userStmt}`);
@@ -454,7 +452,7 @@ export function formatObservationSummary(observationSummary) {
454
452
  export function inferPromiseForSilence(silence) {
455
453
  if (!silence) return null;
456
454
 
457
- const { silence_type, scope, reason, context } = silence;
455
+ const { silence_type, scope: _scope, reason, context } = silence;
458
456
 
459
457
  // Navigation-related silences
460
458
  if (silence_type === SILENCE_TYPES.NAVIGATION_TIMEOUT ||
@@ -63,7 +63,7 @@ export async function executeFlow(page, spec, sensors = {}) {
63
63
  };
64
64
  }
65
65
 
66
- async function executeStep(page, step, idx, spec, sensors, secretValues) {
66
+ async function executeStep(page, step, idx, spec, _sensors, _secretValues) {
67
67
  const baseResult = {
68
68
  stepIndex: idx,
69
69
  type: step.type,
@@ -131,7 +131,8 @@ async function stepGoto(page, step, result, spec) {
131
131
  return result;
132
132
  }
133
133
 
134
- await page.goto(url, { waitUntil: 'networkidle' });
134
+ await page.goto(url, { waitUntil: 'domcontentloaded', timeout: 30000 });
135
+ await page.waitForLoadState('networkidle', { timeout: 10000 }).catch(() => {});
135
136
  await waitForSettle(page, { timeoutMs: 3000, settleMs: 500 });
136
137
 
137
138
  result.success = true;
@@ -111,10 +111,9 @@ export function validateFlowSpec(spec) {
111
111
  * Returns actual value or throws if not found.
112
112
  *
113
113
  * @param {string} value - String containing $ENV:VARNAME references
114
- * @param {Object} secrets - Map of secret keys to env var names
115
114
  * @returns {string} - Resolved value
116
115
  */
117
- export function resolveSecrets(value, secrets = {}) {
116
+ export function resolveSecrets(value, _secrets = {}) {
118
117
  if (typeof value !== 'string') return value;
119
118
 
120
119
  // Replace $ENV:VARNAME with actual env var value
@@ -53,8 +53,18 @@ export async function scan(projectDir, url, manifestPath = null, scanBudgetOverr
53
53
  manifestPath: manifest.manifestPath
54
54
  };
55
55
 
56
- const validation = await validateRoutes(manifestForValidation, url);
57
-
56
+ let validation = await validateRoutes(manifestForValidation, url);
57
+
58
+ if (!validation) {
59
+ // validateRoutes might return null if routes cannot be validated
60
+ validation = {
61
+ routesValidated: 0,
62
+ routesReachable: 0,
63
+ routesUnreachable: 0,
64
+ details: [],
65
+ warnings: []
66
+ };
67
+ }
58
68
  if (validation.warnings && validation.warnings.length > 0) {
59
69
  if (!manifest.learnTruth.warnings) {
60
70
  manifest.learnTruth.warnings = [];
@@ -94,7 +104,9 @@ export async function scan(projectDir, url, manifestPath = null, scanBudgetOverr
94
104
  const { writeFileSync } = await import('fs');
95
105
  const manifestCopyPath = getArtifactPath(projectDir, runId, 'manifest.json');
96
106
  writeFileSync(manifestCopyPath, JSON.stringify(manifest, null, 2));
97
- } catch {}
107
+ } catch {
108
+ // Ignore write errors
109
+ }
98
110
 
99
111
  // Create silence tracker from observation silences
100
112
  const silenceTracker = new SilenceTracker();
@@ -65,7 +65,7 @@ function analyzeNode(node, sourceFile, projectRoot, eventType = null) {
65
65
  // VALIDATION INTELLIGENCE v1: Check for return false in submit context
66
66
  if (ts.isReturnStatement(node) && eventType === 'onSubmit') {
67
67
  const expression = node.expression;
68
- if (expression && ts.isFalseKeyword(expression)) {
68
+ if (expression && expression.kind === ts.SyntaxKind.FalseKeyword) {
69
69
  const location = getNodeLocation(sourceFile, node, projectRoot);
70
70
  return {
71
71
  type: 'validation_block',
@@ -122,7 +122,7 @@ export async function runCodeIntelligence(projectRoot) {
122
122
  matchAttribute: el.attrs.to ? 'to' : (el.attrs.href ? 'href' : null),
123
123
  proof: 'PROVEN_EXPECTATION',
124
124
  sourceRef: el.sourceRef,
125
- selectorHint: extractSelectorHint({ attributes: { properties: [] } }) || `${el.tag}`,
125
+ selectorHint: extractSelectorHint(/** @type {any} */ ({ attributes: { properties: [] } })) || `${el.tag}`,
126
126
  metadata: {
127
127
  elementFile: el.file,
128
128
  elementLine: el.line,
@@ -267,7 +267,7 @@ function generateExpectation(mapping, effect) {
267
267
  * @param {Object} effect - Effect object
268
268
  * @returns {string} - Expectation type
269
269
  */
270
- function determineExpectationType(effect) {
270
+ function _determineExpectationType(effect) {
271
271
  switch (effect.type) {
272
272
  case 'navigation':
273
273
  return 'navigation';
@@ -12,9 +12,9 @@
12
12
  */
13
13
 
14
14
  import ts from 'typescript';
15
- import { resolve, relative, sep, basename, dirname, extname } from 'path';
15
+ import { resolve, relative, basename, extname } from 'path';
16
16
  import { existsSync, readdirSync, statSync } from 'fs';
17
- import { parseFile, findNodes, getStringLiteral, getNodeLocation } from './ts-program.js';
17
+ import { parseFile, findNodes, getNodeLocation } from './ts-program.js';
18
18
  import { extractVueRoutes } from './vue-router-extractor.js';
19
19
  import { normalizeDynamicRoute } from '../shared/dynamic-route-utils.js';
20
20
 
@@ -27,7 +27,7 @@ const INTERNAL_PATH_PATTERNS = [
27
27
  /\/private/
28
28
  ];
29
29
 
30
- function isInternalRoute(path) {
30
+ function _isInternalRoute(path) {
31
31
  return INTERNAL_PATH_PATTERNS.some(pattern => pattern.test(path));
32
32
  }
33
33