@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.
- package/package.json +14 -4
- package/src/cli/commands/default.js +244 -86
- package/src/cli/commands/doctor.js +36 -4
- package/src/cli/commands/run.js +253 -69
- package/src/cli/entry.js +5 -5
- package/src/cli/util/detection-engine.js +4 -3
- package/src/cli/util/events.js +76 -0
- package/src/cli/util/expectation-extractor.js +11 -1
- package/src/cli/util/findings-writer.js +1 -0
- package/src/cli/util/observation-engine.js +69 -23
- package/src/cli/util/paths.js +3 -2
- package/src/cli/util/project-discovery.js +20 -0
- package/src/cli/util/redact.js +2 -2
- package/src/cli/util/runtime-budget.js +147 -0
- package/src/cli/util/summary-writer.js +12 -1
- package/src/types/global.d.ts +28 -0
- package/src/types/ts-ast.d.ts +24 -0
- package/src/verax/cli/doctor.js +2 -2
- package/src/verax/cli/init.js +1 -1
- package/src/verax/cli/url-safety.js +12 -2
- package/src/verax/cli/wizard.js +13 -2
- package/src/verax/core/budget-engine.js +1 -1
- package/src/verax/core/decision-snapshot.js +2 -2
- package/src/verax/core/determinism-model.js +35 -6
- package/src/verax/core/incremental-store.js +15 -7
- package/src/verax/core/replay-validator.js +4 -4
- package/src/verax/core/replay.js +1 -1
- package/src/verax/core/silence-impact.js +1 -1
- package/src/verax/core/silence-model.js +9 -7
- package/src/verax/detect/comparison.js +8 -3
- package/src/verax/detect/confidence-engine.js +17 -17
- package/src/verax/detect/detection-engine.js +1 -1
- package/src/verax/detect/evidence-index.js +15 -65
- package/src/verax/detect/expectation-model.js +54 -3
- package/src/verax/detect/explanation-helpers.js +1 -1
- package/src/verax/detect/finding-detector.js +2 -2
- package/src/verax/detect/findings-writer.js +9 -16
- package/src/verax/detect/flow-detector.js +4 -4
- package/src/verax/detect/index.js +37 -11
- package/src/verax/detect/interactive-findings.js +3 -4
- package/src/verax/detect/signal-mapper.js +2 -2
- package/src/verax/detect/skip-classifier.js +4 -4
- package/src/verax/detect/verdict-engine.js +4 -6
- package/src/verax/flow/flow-engine.js +3 -2
- package/src/verax/flow/flow-spec.js +1 -2
- package/src/verax/index.js +15 -3
- package/src/verax/intel/effect-detector.js +1 -1
- package/src/verax/intel/index.js +2 -2
- package/src/verax/intel/route-extractor.js +3 -3
- package/src/verax/intel/vue-navigation-extractor.js +81 -18
- package/src/verax/intel/vue-router-extractor.js +4 -2
- package/src/verax/learn/action-contract-extractor.js +3 -3
- package/src/verax/learn/ast-contract-extractor.js +53 -1
- package/src/verax/learn/index.js +36 -2
- package/src/verax/learn/manifest-writer.js +28 -14
- package/src/verax/learn/route-extractor.js +1 -1
- package/src/verax/learn/route-validator.js +8 -7
- package/src/verax/learn/state-extractor.js +1 -1
- package/src/verax/learn/static-extractor-navigation.js +1 -1
- package/src/verax/learn/static-extractor-validation.js +2 -2
- package/src/verax/learn/static-extractor.js +8 -7
- package/src/verax/learn/ts-contract-resolver.js +14 -12
- package/src/verax/observe/browser.js +22 -3
- package/src/verax/observe/console-sensor.js +2 -2
- package/src/verax/observe/expectation-executor.js +2 -1
- package/src/verax/observe/focus-sensor.js +1 -1
- package/src/verax/observe/human-driver.js +29 -10
- package/src/verax/observe/index.js +10 -7
- package/src/verax/observe/interaction-discovery.js +27 -15
- package/src/verax/observe/interaction-runner.js +6 -6
- package/src/verax/observe/loading-sensor.js +6 -0
- package/src/verax/observe/navigation-sensor.js +1 -1
- package/src/verax/observe/settle.js +1 -0
- package/src/verax/observe/state-sensor.js +8 -4
- package/src/verax/observe/state-ui-sensor.js +7 -1
- package/src/verax/observe/traces-writer.js +27 -16
- package/src/verax/observe/ui-signal-sensor.js +7 -0
- package/src/verax/scan-summary-writer.js +5 -2
- package/src/verax/shared/artifact-manager.js +1 -1
- package/src/verax/shared/budget-profiles.js +2 -2
- package/src/verax/shared/caching.js +1 -1
- package/src/verax/shared/config-loader.js +1 -2
- package/src/verax/shared/dynamic-route-utils.js +12 -6
- package/src/verax/shared/retry-policy.js +1 -6
- package/src/verax/shared/root-artifacts.js +1 -1
- package/src/verax/shared/zip-artifacts.js +1 -0
- package/src/verax/validate/context-validator.js +1 -1
- package/src/verax/observe/index.js.backup +0 -1
- 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,
|
|
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
|
|
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,
|
|
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
|
|
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.
|
|
8
|
-
*
|
|
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
|
|
16
|
+
* @param {string} runDirOpt - Required absolute run directory path
|
|
18
17
|
*/
|
|
19
|
-
export function writeFindings(projectDir, url, findings, coverageGaps = [], runDirOpt
|
|
20
|
-
|
|
21
|
-
|
|
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
|
-
|
|
136
|
+
const _hasSilentFailure = false;
|
|
137
137
|
let failedStepIndex = -1;
|
|
138
|
-
|
|
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
|
-
|
|
251
|
+
const _hasSilentFailure = true;
|
|
252
252
|
failedStepIndex = i;
|
|
253
|
-
|
|
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
|
-
|
|
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(/[
|
|
77
|
-
const normalizedInteractionSelector = interactionSelector.replace(/[
|
|
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
|
-
|
|
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,
|
|
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,
|
|
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
|
|
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
|
|
140
|
-
const
|
|
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(/[
|
|
58
|
-
const normalizedInteractionSelector = interactionSelector.replace(/[
|
|
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(/[
|
|
80
|
-
const normalizedInteractionSelector = interactionSelector.replace(/[
|
|
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
|
|
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 (
|
|
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
|
|
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,
|
|
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: '
|
|
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,
|
|
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
|
package/src/verax/index.js
CHANGED
|
@@ -53,8 +53,18 @@ export async function scan(projectDir, url, manifestPath = null, scanBudgetOverr
|
|
|
53
53
|
manifestPath: manifest.manifestPath
|
|
54
54
|
};
|
|
55
55
|
|
|
56
|
-
|
|
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.
|
|
68
|
+
if (expression && expression.kind === ts.SyntaxKind.FalseKeyword) {
|
|
69
69
|
const location = getNodeLocation(sourceFile, node, projectRoot);
|
|
70
70
|
return {
|
|
71
71
|
type: 'validation_block',
|
package/src/verax/intel/index.js
CHANGED
|
@@ -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
|
|
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,
|
|
15
|
+
import { resolve, relative, basename, extname } from 'path';
|
|
16
16
|
import { existsSync, readdirSync, statSync } from 'fs';
|
|
17
|
-
import { parseFile, findNodes,
|
|
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
|
|
30
|
+
function _isInternalRoute(path) {
|
|
31
31
|
return INTERNAL_PATH_PATTERNS.some(pattern => pattern.test(path));
|
|
32
32
|
}
|
|
33
33
|
|