@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,164 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PHASE 6B: ACTIVATION
|
|
3
|
+
*
|
|
4
|
+
* Activates Phase 6A by enforcing it as MANDATORY for all scan paths.
|
|
5
|
+
*
|
|
6
|
+
* This module wraps Phase 6A and integrates it with run.js to ensure:
|
|
7
|
+
* 1. All scans use Phase 6A (no bypass possible)
|
|
8
|
+
* 2. Artifacts can only be written to staging
|
|
9
|
+
* 3. Integrity is verified before commit
|
|
10
|
+
* 4. Poison markers prevent reading corrupted runs
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { initPhase6A as phase6aInit, completePhase6A as phase6aComplete, rollbackPhase6A as phase6aRollback, checkPoisonMarker } from './trust-activation-integration.js';
|
|
14
|
+
import { join } from 'path';
|
|
15
|
+
import { existsSync } from 'fs';
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Initialize Phase 6A (poison marker + staging directory)
|
|
19
|
+
* MANDATORY at run start - called before any artifact writing
|
|
20
|
+
*
|
|
21
|
+
* @param {string} runDir - Run directory (e.g., .verax/runs/<runId>)
|
|
22
|
+
* @returns {Promise<{ success: boolean, error?: Error }>}
|
|
23
|
+
*/
|
|
24
|
+
export async function initializePhase6A(runDir) {
|
|
25
|
+
try {
|
|
26
|
+
const result = await phase6aInit(runDir);
|
|
27
|
+
return result;
|
|
28
|
+
} catch (error) {
|
|
29
|
+
return { success: false, error };
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Get paths with staging directory redirection
|
|
35
|
+
* MANDATORY - replaces all artifact paths to point to staging directory
|
|
36
|
+
*
|
|
37
|
+
* This ensures ALL artifact writes go to staging instead of final location.
|
|
38
|
+
*
|
|
39
|
+
* @param {Object} paths - Original paths from getRunPaths()
|
|
40
|
+
* @returns {Object} Modified paths with staging redirection
|
|
41
|
+
*/
|
|
42
|
+
export function getStagingRedirectedPaths(paths) {
|
|
43
|
+
const stagingDir = join(paths.baseDir, '.staging');
|
|
44
|
+
|
|
45
|
+
const redirected = { ...paths };
|
|
46
|
+
|
|
47
|
+
// List of artifact path keys that should be redirected to staging
|
|
48
|
+
const artifactKeys = [
|
|
49
|
+
'summary', 'findings', 'ledger', 'learn', 'observe',
|
|
50
|
+
'summaryJson', 'findingsJson', 'learnJson', 'observeJson', 'ledgerJson'
|
|
51
|
+
];
|
|
52
|
+
|
|
53
|
+
for (const key of artifactKeys) {
|
|
54
|
+
if (redirected[key] && typeof redirected[key] === 'string') {
|
|
55
|
+
// Extract just the filename from the path (works on Windows and Unix)
|
|
56
|
+
// Split by both / and \ to handle any path separator
|
|
57
|
+
const parts = redirected[key].replace(/\\/g, '/').split('/');
|
|
58
|
+
const filename = parts[parts.length - 1];
|
|
59
|
+
|
|
60
|
+
if (filename) {
|
|
61
|
+
// Place in staging directory
|
|
62
|
+
redirected[key] = join(stagingDir, filename);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return redirected;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Complete Phase 6A - verify integrity and commit artifacts
|
|
72
|
+
* MANDATORY on success - generates manifest, verifies, commits staging to final
|
|
73
|
+
*
|
|
74
|
+
* @param {string} runDir - Run directory (base, not staging)
|
|
75
|
+
* @returns {Promise<{ success: boolean, verification?: any, error?: Error }>}
|
|
76
|
+
*/
|
|
77
|
+
export async function completePhase6A(runDir) {
|
|
78
|
+
try {
|
|
79
|
+
const result = await phase6aComplete(runDir);
|
|
80
|
+
return result;
|
|
81
|
+
} catch (error) {
|
|
82
|
+
return { success: false, error };
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Rollback Phase 6A on error
|
|
88
|
+
* MANDATORY in catch block - cleans staging but KEEPS poison marker
|
|
89
|
+
*
|
|
90
|
+
* @param {string} runDir - Run directory (base, not staging)
|
|
91
|
+
* @returns {Promise<{ success: boolean, error?: Error }>}
|
|
92
|
+
*/
|
|
93
|
+
export async function rollbackPhase6A(runDir) {
|
|
94
|
+
try {
|
|
95
|
+
// Pass generic error - the phase6a module will record it
|
|
96
|
+
const error = new Error('Scan execution failed or was interrupted');
|
|
97
|
+
const result = await phase6aRollback(runDir, error);
|
|
98
|
+
return result;
|
|
99
|
+
} catch (error) {
|
|
100
|
+
return { success: false, error };
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Enforce poison marker check before reading a run
|
|
106
|
+
* MANDATORY before inspect, before loading artifacts, etc
|
|
107
|
+
*
|
|
108
|
+
* Prevents reading from incomplete/corrupted previous runs.
|
|
109
|
+
*
|
|
110
|
+
* @param {string} runDir - Run directory
|
|
111
|
+
* @throws {Error} If poison marker exists
|
|
112
|
+
*/
|
|
113
|
+
export function enforcePoisonCheckBeforeRead(runDir) {
|
|
114
|
+
const poisonCheck = checkPoisonMarker(runDir);
|
|
115
|
+
|
|
116
|
+
if (poisonCheck.hasPoisonMarker) {
|
|
117
|
+
throw new Error(
|
|
118
|
+
`Cannot read from this run: poison marker present (incomplete/corrupted run). ` +
|
|
119
|
+
`The previous scan did not complete successfully. ` +
|
|
120
|
+
`Artifacts may be incomplete or corrupted. ` +
|
|
121
|
+
`Run 'verax doctor' to diagnose.`
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
return { safe: true };
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Verify artifacts before reading (check integrity manifest)
|
|
129
|
+
* RECOMMENDED before loading summary.json, findings.json, etc
|
|
130
|
+
*
|
|
131
|
+
* @param {string} runDir - Run directory
|
|
132
|
+
* @returns {{ ok: boolean, error?: string }}
|
|
133
|
+
*/
|
|
134
|
+
export function verifyArtifactsBeforeRead(runDir) {
|
|
135
|
+
try {
|
|
136
|
+
// Check that integrity manifest exists (in staging directory)
|
|
137
|
+
const stagingDir = join(runDir, '.staging');
|
|
138
|
+
const manifestPath = join(stagingDir, 'integrity.manifest.json');
|
|
139
|
+
|
|
140
|
+
if (!existsSync(manifestPath)) {
|
|
141
|
+
return {
|
|
142
|
+
ok: false,
|
|
143
|
+
error: 'Integrity manifest missing - run may be incomplete or from older version'
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// In future: verify checksums match manifest
|
|
148
|
+
|
|
149
|
+
return { ok: true };
|
|
150
|
+
} catch (error) {
|
|
151
|
+
return { ok: false, error: error.message };
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Check budget compliance during execution
|
|
157
|
+
* Placeholder for Phase 6C
|
|
158
|
+
*
|
|
159
|
+
* @returns {{ ok: boolean, violations?: string[] }}
|
|
160
|
+
*/
|
|
161
|
+
export function checkBudgetCompliance() {
|
|
162
|
+
// Placeholder for Phase 6C
|
|
163
|
+
return { ok: true };
|
|
164
|
+
}
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PHASE 2: Canonical Type System for VERAX
|
|
3
|
+
*
|
|
4
|
+
* Single source of truth for all valid types in the analysis pipeline:
|
|
5
|
+
* - EXPECTATION_TYPE: Types of expectations extracted from source code
|
|
6
|
+
* - FINDING_TYPE: Types of findings detected during analysis
|
|
7
|
+
* - SKIP_REASON: Structured reasons why expectations were skipped
|
|
8
|
+
*
|
|
9
|
+
* Validation happens at CREATION TIME, not at enforcement time.
|
|
10
|
+
* No object may be created with an invalid type.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Valid expectation types extracted from source code.
|
|
15
|
+
* These represent what the code is expected to do.
|
|
16
|
+
*/
|
|
17
|
+
export const EXPECTATION_TYPE = Object.freeze({
|
|
18
|
+
// Navigation: Expects a URL change or page navigation
|
|
19
|
+
NAVIGATION: 'navigation',
|
|
20
|
+
|
|
21
|
+
// Network: Expects a network call to be made
|
|
22
|
+
NETWORK: 'network',
|
|
23
|
+
|
|
24
|
+
// State: Expects a state change or promise fulfillment
|
|
25
|
+
STATE: 'state',
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Valid finding types detected during analysis.
|
|
30
|
+
* These represent what the code is NOT doing that it should be.
|
|
31
|
+
*/
|
|
32
|
+
export const FINDING_TYPE = Object.freeze({
|
|
33
|
+
// Silent Failures: Expectation not met with no observable effect
|
|
34
|
+
SILENT_FAILURE: 'silent_failure',
|
|
35
|
+
NETWORK_SILENT_FAILURE: 'network_silent_failure',
|
|
36
|
+
VALIDATION_SILENT_FAILURE: 'validation_silent_failure',
|
|
37
|
+
FLOW_SILENT_FAILURE: 'flow_silent_failure',
|
|
38
|
+
DYNAMIC_ROUTE_SILENT_FAILURE: 'dynamic_route_silent_failure',
|
|
39
|
+
|
|
40
|
+
// Observable Breaks: Expectation not met with visible effects
|
|
41
|
+
OBSERVED_BREAK: 'observed_break',
|
|
42
|
+
|
|
43
|
+
// UI Feedback: Missing user feedback after action
|
|
44
|
+
MISSING_FEEDBACK_FAILURE: 'missing_feedback_failure',
|
|
45
|
+
CSS_LOADING_FEEDBACK_FAILURE: 'css_loading_feedback_failure',
|
|
46
|
+
|
|
47
|
+
// Route Issues: Problems with dynamic route detection
|
|
48
|
+
DYNAMIC_ROUTE_MISMATCH: 'dynamic_route_mismatch',
|
|
49
|
+
|
|
50
|
+
// Interactive: Issues with interactive elements
|
|
51
|
+
NAVIGATION_SILENT_FAILURE: 'navigation_silent_failure',
|
|
52
|
+
JOURNEY_STALL_SILENT_FAILURE: 'journey_stall_silent_failure',
|
|
53
|
+
|
|
54
|
+
// Network: Issues with network calls
|
|
55
|
+
MISSING_NETWORK_ACTION: 'missing_network_action',
|
|
56
|
+
NETWORK_SUCCESS_NO_FEEDBACK: 'network_success_no_feedback',
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Structured reasons why expectations were skipped (not analyzed).
|
|
61
|
+
* Distinguish between systemic failures and intentional filtering.
|
|
62
|
+
*/
|
|
63
|
+
export const SKIP_REASON = Object.freeze({
|
|
64
|
+
// Systemic Failures: Pipeline cannot continue (force INCOMPLETE state)
|
|
65
|
+
NO_EXPECTATIONS_EXTRACTED: 'NO_EXPECTATIONS_EXTRACTED',
|
|
66
|
+
TIMEOUT_OBSERVE: 'TIMEOUT_OBSERVE',
|
|
67
|
+
TIMEOUT_DETECT: 'TIMEOUT_DETECT',
|
|
68
|
+
TIMEOUT_TOTAL: 'TIMEOUT_TOTAL',
|
|
69
|
+
BUDGET_EXCEEDED: 'BUDGET_EXCEEDED',
|
|
70
|
+
MISSING_SOURCE_DIR: 'MISSING_SOURCE_DIR',
|
|
71
|
+
UNREACHABLE_URL: 'UNREACHABLE_URL',
|
|
72
|
+
|
|
73
|
+
// Intentional Filtering: Skips that don't indicate truncation (COMPLETE is allowed)
|
|
74
|
+
DYNAMIC_ROUTE_UNSUPPORTED: 'DYNAMIC_ROUTE_UNSUPPORTED',
|
|
75
|
+
EXTERNAL_URL_SKIPPED: 'EXTERNAL_URL_SKIPPED',
|
|
76
|
+
PARSE_ERROR: 'PARSE_ERROR',
|
|
77
|
+
UNSUPPORTED_FILE: 'UNSUPPORTED_FILE',
|
|
78
|
+
OBSERVATION_FAILED: 'OBSERVATION_FAILED',
|
|
79
|
+
CONTRACT_VIOLATION: 'CONTRACT_VIOLATION',
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Validate that a type string is a valid expectation type.
|
|
84
|
+
* @param {string} type - The type to validate
|
|
85
|
+
* @returns {boolean} True if valid
|
|
86
|
+
* @throws {Error} If invalid
|
|
87
|
+
*/
|
|
88
|
+
export function validateExpectationType(type) {
|
|
89
|
+
// @ts-expect-error - Runtime string comparison against enum values
|
|
90
|
+
if (!Object.values(EXPECTATION_TYPE).includes(type)) {
|
|
91
|
+
throw new Error(
|
|
92
|
+
`Invalid expectation type: "${type}". Must be one of: ${Object.values(EXPECTATION_TYPE).join(', ')}`
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
return true;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Validate that a type string is a valid finding type.
|
|
100
|
+
* @param {string} type - The type to validate
|
|
101
|
+
* @returns {boolean} True if valid
|
|
102
|
+
* @throws {Error} If invalid
|
|
103
|
+
*/
|
|
104
|
+
export function validateFindingType(type) {
|
|
105
|
+
// @ts-expect-error - Runtime string comparison against enum values
|
|
106
|
+
if (!Object.values(FINDING_TYPE).includes(type)) {
|
|
107
|
+
throw new Error(
|
|
108
|
+
`Invalid finding type: "${type}". Must be one of: ${Object.values(FINDING_TYPE).join(', ')}`
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
return true;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Validate that a reason is a valid skip reason.
|
|
116
|
+
* @param {string} reason - The reason to validate
|
|
117
|
+
* @returns {string} Normalized valid skip reason
|
|
118
|
+
* @throws {Error} If invalid and cannot be normalized
|
|
119
|
+
*/
|
|
120
|
+
export function validateSkipReason(reason) {
|
|
121
|
+
// CRASH GUARD: Normalize undefined/invalid reasons instead of throwing
|
|
122
|
+
if (reason === undefined || reason === null || reason === 'undefined') {
|
|
123
|
+
console.warn(`[VERAX] Invalid skip reason "${reason}" normalized to OBSERVATION_FAILED`);
|
|
124
|
+
return SKIP_REASON.OBSERVATION_FAILED;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// @ts-expect-error - Runtime string comparison against enum values
|
|
128
|
+
if (!Object.values(SKIP_REASON).includes(reason)) {
|
|
129
|
+
console.warn(`[VERAX] Unknown skip reason "${reason}" normalized to OBSERVATION_FAILED`);
|
|
130
|
+
return SKIP_REASON.OBSERVATION_FAILED;
|
|
131
|
+
}
|
|
132
|
+
return reason;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Check if a skip reason is a systemic failure.
|
|
137
|
+
* Systemic failures force analysis state to INCOMPLETE.
|
|
138
|
+
* @param {string} reason - The skip reason
|
|
139
|
+
* @returns {boolean} True if this is a systemic failure
|
|
140
|
+
*/
|
|
141
|
+
export function isSystemicFailure(reason) {
|
|
142
|
+
const systemicFailures = [
|
|
143
|
+
SKIP_REASON.NO_EXPECTATIONS_EXTRACTED,
|
|
144
|
+
SKIP_REASON.TIMEOUT_OBSERVE,
|
|
145
|
+
SKIP_REASON.TIMEOUT_DETECT,
|
|
146
|
+
SKIP_REASON.TIMEOUT_TOTAL,
|
|
147
|
+
SKIP_REASON.BUDGET_EXCEEDED,
|
|
148
|
+
SKIP_REASON.MISSING_SOURCE_DIR,
|
|
149
|
+
SKIP_REASON.UNREACHABLE_URL,
|
|
150
|
+
];
|
|
151
|
+
// @ts-expect-error - Runtime string comparison against enum values
|
|
152
|
+
return systemicFailures.includes(reason);
|
|
153
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* URL Validation Utilities
|
|
3
|
+
* Ensures strict URL format validation to prevent false greens
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export function validateUrl(urlString) {
|
|
7
|
+
if (!urlString || typeof urlString !== 'string') {
|
|
8
|
+
throw new Error('URL must be a non-empty string');
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
try {
|
|
12
|
+
const url = new URL(urlString);
|
|
13
|
+
|
|
14
|
+
// Require http or https
|
|
15
|
+
if (!['http:', 'https:'].includes(url.protocol)) {
|
|
16
|
+
throw new Error(`Invalid protocol: ${url.protocol} (must be http or https)`);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// Require hostname
|
|
20
|
+
if (!url.hostname) {
|
|
21
|
+
throw new Error('URL must include a hostname');
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// Check for localhost without port (common misconfiguration)
|
|
25
|
+
if (url.hostname === 'localhost' && !url.port) {
|
|
26
|
+
// This is OK - localhost defaults to port 80/443
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return { valid: true, url };
|
|
30
|
+
} catch (error) {
|
|
31
|
+
throw new Error(`Invalid URL: ${error.message}`);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function validateFlags(flags, allowed) {
|
|
36
|
+
const invalidFlags = flags.filter(flag => !allowed.includes(flag));
|
|
37
|
+
if (invalidFlags.length > 0) {
|
|
38
|
+
throw new Error(`Unknown flag(s): ${invalidFlags.join(', ')}`);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PHASE 20 — Vue Navigation Promise Detection
|
|
3
|
+
*
|
|
4
|
+
* Detects Vue Router navigation promises:
|
|
5
|
+
* - router.push('/path'), router.replace('/path')
|
|
6
|
+
* - router.push({ name: 'X', params: { id: 1 }}) -> mark as dynamic/ambiguous
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { parse } from '@babel/parser';
|
|
10
|
+
import _traverse from '@babel/traverse';
|
|
11
|
+
|
|
12
|
+
const traverse = _traverse.default || _traverse;
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* PHASE 20: Detect Vue navigation promises
|
|
16
|
+
*
|
|
17
|
+
* @param {string} scriptContent - Script block content
|
|
18
|
+
* @param {string} filePath - File path
|
|
19
|
+
* @param {string} relPath - Relative path
|
|
20
|
+
* @param {Object} _scriptBlock - Script block metadata
|
|
21
|
+
* @param {Object} _templateBindings - Template bindings (optional)
|
|
22
|
+
* @ts-expect-error - JSDoc params documented but unused
|
|
23
|
+
* @returns {Array} Navigation promises
|
|
24
|
+
*/
|
|
25
|
+
export function detectVueNavigationPromises(scriptContent, filePath, relPath, _scriptBlock, _templateBindings) {
|
|
26
|
+
const promises = [];
|
|
27
|
+
|
|
28
|
+
try {
|
|
29
|
+
const ast = parse(scriptContent, {
|
|
30
|
+
sourceType: 'module',
|
|
31
|
+
plugins: [
|
|
32
|
+
'typescript',
|
|
33
|
+
'classProperties',
|
|
34
|
+
'optionalChaining',
|
|
35
|
+
'nullishCoalescingOperator',
|
|
36
|
+
'dynamicImport',
|
|
37
|
+
'topLevelAwait',
|
|
38
|
+
'objectRestSpread',
|
|
39
|
+
],
|
|
40
|
+
errorRecovery: true,
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
const lines = scriptContent.split('\n');
|
|
44
|
+
|
|
45
|
+
traverse(ast, {
|
|
46
|
+
CallExpression(path) {
|
|
47
|
+
const node = path.node;
|
|
48
|
+
const callee = node.callee;
|
|
49
|
+
|
|
50
|
+
// Detect router.push() and router.replace()
|
|
51
|
+
if (callee.type === 'MemberExpression') {
|
|
52
|
+
const object = callee.object;
|
|
53
|
+
const property = callee.property;
|
|
54
|
+
|
|
55
|
+
if (property.name === 'push' || property.name === 'replace') {
|
|
56
|
+
// Check if object is 'router' or 'this.$router' or 'useRouter()'
|
|
57
|
+
const isRouter =
|
|
58
|
+
(object.type === 'Identifier' && object.name === 'router') ||
|
|
59
|
+
(object.type === 'MemberExpression' &&
|
|
60
|
+
object.object.type === 'ThisExpression' &&
|
|
61
|
+
object.property.name === '$router') ||
|
|
62
|
+
(object.type === 'CallExpression' &&
|
|
63
|
+
callee.type === 'Identifier' &&
|
|
64
|
+
callee.name === 'useRouter');
|
|
65
|
+
|
|
66
|
+
if (isRouter && node.arguments.length > 0) {
|
|
67
|
+
const arg = node.arguments[0];
|
|
68
|
+
let targetPath = null;
|
|
69
|
+
let isDynamic = false;
|
|
70
|
+
|
|
71
|
+
// String literal: router.push('/path')
|
|
72
|
+
if (arg.type === 'StringLiteral') {
|
|
73
|
+
targetPath = arg.value;
|
|
74
|
+
}
|
|
75
|
+
// Object literal: router.push({ name: 'X', params: {} })
|
|
76
|
+
else if (arg.type === 'ObjectExpression') {
|
|
77
|
+
const nameProp = arg.properties.find(p =>
|
|
78
|
+
p.key && p.key.name === 'name'
|
|
79
|
+
);
|
|
80
|
+
const pathProp = arg.properties.find(p =>
|
|
81
|
+
p.key && p.key.name === 'path'
|
|
82
|
+
);
|
|
83
|
+
|
|
84
|
+
if (pathProp && pathProp.value && pathProp.value.type === 'StringLiteral') {
|
|
85
|
+
targetPath = pathProp.value.value;
|
|
86
|
+
} else if (nameProp) {
|
|
87
|
+
// Named route - mark as dynamic/ambiguous
|
|
88
|
+
isDynamic = true;
|
|
89
|
+
targetPath = '<named-route>';
|
|
90
|
+
} else {
|
|
91
|
+
isDynamic = true;
|
|
92
|
+
targetPath = '<dynamic>';
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
// Template literal or other dynamic
|
|
96
|
+
else {
|
|
97
|
+
isDynamic = true;
|
|
98
|
+
targetPath = '<dynamic>';
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
if (targetPath) {
|
|
102
|
+
const loc = node.loc;
|
|
103
|
+
const line = loc ? loc.start.line : 1;
|
|
104
|
+
const column = loc ? loc.start.column : 0;
|
|
105
|
+
|
|
106
|
+
// Extract AST source
|
|
107
|
+
const astSource = lines.slice(line - 1, loc ? loc.end.line : line)
|
|
108
|
+
.join('\n')
|
|
109
|
+
.substring(0, 200);
|
|
110
|
+
|
|
111
|
+
// Build context chain
|
|
112
|
+
const context = buildContext(path);
|
|
113
|
+
|
|
114
|
+
promises.push({
|
|
115
|
+
type: 'navigation',
|
|
116
|
+
promise: {
|
|
117
|
+
kind: 'navigate',
|
|
118
|
+
value: targetPath,
|
|
119
|
+
isDynamic,
|
|
120
|
+
},
|
|
121
|
+
source: {
|
|
122
|
+
file: relPath,
|
|
123
|
+
line,
|
|
124
|
+
column,
|
|
125
|
+
context,
|
|
126
|
+
astSource,
|
|
127
|
+
},
|
|
128
|
+
confidence: isDynamic ? 0.7 : 1.0,
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
},
|
|
135
|
+
});
|
|
136
|
+
} catch (error) {
|
|
137
|
+
// Parse error - skip
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
return promises;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Build context chain from AST path
|
|
145
|
+
*/
|
|
146
|
+
function buildContext(path) {
|
|
147
|
+
const context = [];
|
|
148
|
+
let current = path;
|
|
149
|
+
|
|
150
|
+
while (current) {
|
|
151
|
+
if (current.isFunctionDeclaration()) {
|
|
152
|
+
context.push({
|
|
153
|
+
type: 'function',
|
|
154
|
+
name: current.node.id?.name || '<anonymous>',
|
|
155
|
+
});
|
|
156
|
+
} else if (current.isArrowFunctionExpression()) {
|
|
157
|
+
context.push({
|
|
158
|
+
type: 'arrow-function',
|
|
159
|
+
name: '<arrow>',
|
|
160
|
+
});
|
|
161
|
+
} else if (current.isMethodDefinition()) {
|
|
162
|
+
context.push({
|
|
163
|
+
type: 'method',
|
|
164
|
+
name: current.node.key?.name || '<method>',
|
|
165
|
+
});
|
|
166
|
+
} else if (current.isObjectProperty()) {
|
|
167
|
+
context.push({
|
|
168
|
+
type: 'property',
|
|
169
|
+
name: current.node.key?.name || '<property>',
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
current = current.parentPath;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
return context.reverse().map(c => `${c.type}:${c.name}`).join(' > ');
|
|
177
|
+
}
|
|
178
|
+
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PHASE 20 — Vue SFC (Single File Component) Extractor
|
|
3
|
+
*
|
|
4
|
+
* Extracts <script>, <script setup>, and <template> content from .vue files.
|
|
5
|
+
* Deterministic and robust (no external runtime execution).
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* PHASE 20: Extract Vue SFC blocks
|
|
10
|
+
*
|
|
11
|
+
* @param {string} content - Full .vue file content
|
|
12
|
+
* @returns {Object} { scriptBlocks: [{content, lang, startLine}], template: {content, startLine} }
|
|
13
|
+
*/
|
|
14
|
+
export function extractVueSFC(content) {
|
|
15
|
+
const scriptBlocks = [];
|
|
16
|
+
let template = null;
|
|
17
|
+
|
|
18
|
+
// Extract <script> blocks (including <script setup>)
|
|
19
|
+
const scriptRegex = /<script(?:\s+setup)?(?:\s+lang=["']([^"']+)["'])?[^>]*>([\s\S]*?)<\/script>/gi;
|
|
20
|
+
let scriptMatch;
|
|
21
|
+
let _lineOffset = 1;
|
|
22
|
+
|
|
23
|
+
while ((scriptMatch = scriptRegex.exec(content)) !== null) {
|
|
24
|
+
const isSetup = scriptMatch[0].includes('setup');
|
|
25
|
+
const lang = scriptMatch[1] || 'js';
|
|
26
|
+
const scriptContent = scriptMatch[2];
|
|
27
|
+
|
|
28
|
+
// Calculate start line
|
|
29
|
+
const beforeMatch = content.substring(0, scriptMatch.index);
|
|
30
|
+
const startLine = (beforeMatch.match(/\n/g) || []).length + 1;
|
|
31
|
+
|
|
32
|
+
scriptBlocks.push({
|
|
33
|
+
content: scriptContent.trim(),
|
|
34
|
+
lang: lang.toLowerCase(),
|
|
35
|
+
startLine,
|
|
36
|
+
isSetup,
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Extract <template> block
|
|
41
|
+
const templateRegex = /<template[^>]*>([\s\S]*?)<\/template>/i;
|
|
42
|
+
const templateMatch = content.match(templateRegex);
|
|
43
|
+
|
|
44
|
+
if (templateMatch) {
|
|
45
|
+
const beforeTemplate = content.substring(0, templateMatch.index);
|
|
46
|
+
const templateStartLine = (beforeTemplate.match(/\n/g) || []).length + 1;
|
|
47
|
+
|
|
48
|
+
template = {
|
|
49
|
+
content: templateMatch[1].trim(),
|
|
50
|
+
startLine: templateStartLine,
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return {
|
|
55
|
+
scriptBlocks,
|
|
56
|
+
template,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* PHASE 20: Extract template bindings and references
|
|
62
|
+
*
|
|
63
|
+
* @param {string} templateContent - Template content
|
|
64
|
+
* @returns {Object} { bindings: string[], routerLinks: Array, eventHandlers: Array }
|
|
65
|
+
*/
|
|
66
|
+
export function extractTemplateBindings(templateContent) {
|
|
67
|
+
const bindings = [];
|
|
68
|
+
const routerLinks = [];
|
|
69
|
+
const eventHandlers = [];
|
|
70
|
+
|
|
71
|
+
// Extract variable bindings: {{ var }}, :prop="var", v-if="var", etc.
|
|
72
|
+
const bindingPatterns = [
|
|
73
|
+
/\{\{\s*([a-zA-Z_$][a-zA-Z0-9_$]*)\s*\}\}/g, // {{ var }}
|
|
74
|
+
/:([a-zA-Z-]+)=["']([^"']+)["']/g, // :prop="value"
|
|
75
|
+
/v-if=["']([^"']+)["']/g, // v-if="condition"
|
|
76
|
+
/v-show=["']([^"']+)["']/g, // v-show="condition"
|
|
77
|
+
/v-model=["']([^"']+)["']/g, // v-model="value"
|
|
78
|
+
];
|
|
79
|
+
|
|
80
|
+
for (const pattern of bindingPatterns) {
|
|
81
|
+
let match;
|
|
82
|
+
while ((match = pattern.exec(templateContent)) !== null) {
|
|
83
|
+
const binding = match[1] || match[2];
|
|
84
|
+
if (binding && !bindings.includes(binding)) {
|
|
85
|
+
bindings.push(binding);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Extract <router-link> usage
|
|
91
|
+
const routerLinkRegex = /<router-link[^>]*\s+to=["']([^"']+)["'][^>]*>/gi;
|
|
92
|
+
let routerLinkMatch;
|
|
93
|
+
while ((routerLinkMatch = routerLinkRegex.exec(templateContent)) !== null) {
|
|
94
|
+
routerLinks.push({
|
|
95
|
+
to: routerLinkMatch[1],
|
|
96
|
+
fullMatch: routerLinkMatch[0],
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Extract event handlers: @click="handler", @submit.prevent="handler"
|
|
101
|
+
const eventHandlerRegex = /@([a-z]+)(?:\.([a-z]+)*)?=["']([^"']+)["']/gi;
|
|
102
|
+
let eventMatch;
|
|
103
|
+
while ((eventMatch = eventHandlerRegex.exec(templateContent)) !== null) {
|
|
104
|
+
eventHandlers.push({
|
|
105
|
+
event: eventMatch[1],
|
|
106
|
+
modifiers: eventMatch[2] ? eventMatch[2].split('.') : [],
|
|
107
|
+
handler: eventMatch[3],
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
return {
|
|
112
|
+
bindings,
|
|
113
|
+
routerLinks,
|
|
114
|
+
eventHandlers,
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* PHASE 20: Map template handlers to script functions
|
|
120
|
+
*
|
|
121
|
+
* @param {Object} templateBindings - Result from extractTemplateBindings
|
|
122
|
+
* @param {Array} scriptBlocks - Script blocks from extractVueSFC
|
|
123
|
+
* @returns {Map} handlerName -> { scriptBlock, functionInfo }
|
|
124
|
+
*/
|
|
125
|
+
export function mapTemplateHandlersToScript(templateBindings, scriptBlocks) {
|
|
126
|
+
const handlerMap = new Map();
|
|
127
|
+
|
|
128
|
+
for (const handler of templateBindings.eventHandlers) {
|
|
129
|
+
const handlerName = handler.handler;
|
|
130
|
+
|
|
131
|
+
// Find handler in script blocks
|
|
132
|
+
for (const scriptBlock of scriptBlocks) {
|
|
133
|
+
const scriptContent = scriptBlock.content;
|
|
134
|
+
|
|
135
|
+
// Look for function declarations: function handlerName() {}
|
|
136
|
+
const functionRegex = new RegExp(`(?:function|const|let|var)\\s+${handlerName}\\s*[=(]`, 'g');
|
|
137
|
+
if (functionRegex.test(scriptContent)) {
|
|
138
|
+
handlerMap.set(handlerName, {
|
|
139
|
+
scriptBlock,
|
|
140
|
+
handler,
|
|
141
|
+
type: 'function',
|
|
142
|
+
});
|
|
143
|
+
break;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Look for method definitions: methods: { handlerName() {} }
|
|
147
|
+
const methodRegex = new RegExp(`(?:methods|setup)\\s*:\\s*\\{[^}]*${handlerName}\\s*[:(]`, 's');
|
|
148
|
+
if (methodRegex.test(scriptContent)) {
|
|
149
|
+
handlerMap.set(handlerName, {
|
|
150
|
+
scriptBlock,
|
|
151
|
+
handler,
|
|
152
|
+
type: 'method',
|
|
153
|
+
});
|
|
154
|
+
break;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
return handlerMap;
|
|
160
|
+
}
|
|
161
|
+
|