@veraxhq/verax 0.3.0 → 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 +28 -20
- package/bin/verax.js +11 -18
- package/package.json +28 -7
- package/src/cli/commands/baseline.js +1 -2
- package/src/cli/commands/default.js +72 -81
- package/src/cli/commands/doctor.js +29 -0
- package/src/cli/commands/ga.js +3 -0
- package/src/cli/commands/gates.js +1 -1
- package/src/cli/commands/inspect.js +6 -133
- package/src/cli/commands/release-check.js +2 -0
- package/src/cli/commands/run.js +74 -246
- package/src/cli/commands/security-check.js +2 -1
- package/src/cli/commands/truth.js +0 -1
- package/src/cli/entry.js +82 -309
- package/src/cli/util/angular-component-extractor.js +2 -2
- package/src/cli/util/angular-navigation-detector.js +2 -2
- package/src/cli/util/ast-interactive-detector.js +4 -6
- package/src/cli/util/ast-network-detector.js +3 -3
- package/src/cli/util/ast-promise-extractor.js +581 -0
- package/src/cli/util/ast-usestate-detector.js +3 -3
- package/src/cli/util/atomic-write.js +12 -1
- 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 +2 -1
- package/src/cli/util/determinism-writer.js +1 -1
- package/src/cli/util/digest-engine.js +359 -0
- package/src/cli/util/dom-diff.js +226 -0
- package/src/cli/util/env-url.js +0 -4
- package/src/cli/util/evidence-engine.js +287 -0
- package/src/cli/util/expectation-extractor.js +217 -367
- package/src/cli/util/findings-writer.js +19 -126
- 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 -2
- 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 -2
- package/src/cli/util/paths.js +12 -3
- package/src/cli/util/project-discovery.js +284 -3
- package/src/cli/util/project-writer.js +2 -2
- package/src/cli/util/run-id.js +23 -27
- package/src/cli/util/run-result.js +778 -0
- package/src/cli/util/selector-resolver.js +235 -0
- package/src/cli/util/summary-writer.js +2 -1
- package/src/cli/util/svelte-navigation-detector.js +3 -3
- package/src/cli/util/svelte-sfc-extractor.js +0 -1
- package/src/cli/util/svelte-state-detector.js +1 -2
- 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 +4 -3
- package/src/cli/util/vue-sfc-extractor.js +1 -2
- package/src/cli/util/vue-state-detector.js +1 -1
- 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/finding-explainer.js +3 -56
- package/src/verax/cli/init.js +4 -18
- package/src/verax/core/action-classifier.js +4 -3
- package/src/verax/core/artifacts/registry.js +0 -15
- package/src/verax/core/artifacts/verifier.js +18 -8
- package/src/verax/core/baseline/baseline.snapshot.js +2 -0
- package/src/verax/core/capabilities/gates.js +7 -1
- package/src/verax/core/confidence/confidence-compute.js +14 -7
- package/src/verax/core/confidence/confidence.loader.js +1 -0
- package/src/verax/core/confidence-engine-refactor.js +8 -3
- package/src/verax/core/confidence-engine.js +162 -23
- package/src/verax/core/contracts/types.js +1 -0
- package/src/verax/core/contracts/validators.js +79 -4
- package/src/verax/core/decision-snapshot.js +3 -30
- package/src/verax/core/decisions/decision.trace.js +2 -0
- package/src/verax/core/determinism/contract-writer.js +2 -2
- package/src/verax/core/determinism/contract.js +1 -1
- package/src/verax/core/determinism/diff.js +42 -1
- package/src/verax/core/determinism/engine.js +7 -6
- package/src/verax/core/determinism/finding-identity.js +3 -2
- package/src/verax/core/determinism/normalize.js +32 -4
- package/src/verax/core/determinism/report-writer.js +1 -0
- package/src/verax/core/determinism/run-fingerprint.js +7 -2
- package/src/verax/core/dynamic-route-intelligence.js +8 -7
- package/src/verax/core/evidence/evidence-capture-service.js +1 -0
- package/src/verax/core/evidence/evidence-intent-ledger.js +2 -1
- package/src/verax/core/evidence-builder.js +2 -2
- package/src/verax/core/execution-mode-context.js +1 -1
- package/src/verax/core/execution-mode-detector.js +5 -3
- package/src/verax/core/failures/exit-codes.js +39 -37
- package/src/verax/core/failures/failure-summary.js +1 -1
- package/src/verax/core/failures/failure.factory.js +3 -3
- package/src/verax/core/failures/failure.ledger.js +3 -2
- package/src/verax/core/ga/ga.artifact.js +1 -1
- package/src/verax/core/ga/ga.contract.js +3 -2
- package/src/verax/core/ga/ga.enforcer.js +1 -0
- package/src/verax/core/guardrails/policy.loader.js +1 -0
- package/src/verax/core/guardrails/truth-reconciliation.js +1 -1
- package/src/verax/core/guardrails-engine.js +2 -2
- 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 +2 -0
- package/src/verax/core/perf/perf.report.js +2 -0
- package/src/verax/core/pipeline-tracker.js +5 -0
- package/src/verax/core/release/provenance.builder.js +73 -214
- package/src/verax/core/release/release.enforcer.js +14 -9
- package/src/verax/core/release/reproducibility.check.js +1 -0
- package/src/verax/core/release/sbom.builder.js +32 -23
- 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 +6 -3
- package/src/verax/core/report/human-summary.js +141 -1
- package/src/verax/core/route-intelligence.js +4 -3
- 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 +10 -7
- package/src/verax/core/security/security.enforcer.js +4 -0
- package/src/verax/core/security/supplychain.policy.js +9 -1
- package/src/verax/core/security/vuln.scan.js +2 -2
- package/src/verax/core/truth/truth.certificate.js +3 -1
- package/src/verax/core/ui-feedback-intelligence.js +12 -46
- package/src/verax/detect/conditional-ui-silent-failure.js +84 -0
- package/src/verax/detect/confidence-engine.js +100 -660
- package/src/verax/detect/confidence-helper.js +1 -0
- package/src/verax/detect/detection-engine.js +1 -18
- package/src/verax/detect/dynamic-route-findings.js +17 -14
- package/src/verax/detect/expectation-chain-detector.js +1 -1
- package/src/verax/detect/expectation-model.js +3 -5
- package/src/verax/detect/failure-cause-inference.js +293 -0
- package/src/verax/detect/findings-writer.js +126 -166
- 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 +51 -234
- package/src/verax/detect/invariants-enforcer.js +147 -0
- package/src/verax/detect/journey-stall-detector.js +4 -4
- 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 +7 -6
- 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 +18 -18
- package/src/verax/detect/verdict-engine.js +3 -57
- package/src/verax/detect/view-switch-correlator.js +2 -2
- package/src/verax/flow/flow-engine.js +2 -1
- package/src/verax/flow/flow-spec.js +0 -6
- package/src/verax/index.js +48 -412
- 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 +67 -682
- 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/route-validator.js +1 -4
- 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 +735 -84
- package/src/verax/observe/interaction-executor.js +192 -0
- package/src/verax/observe/interaction-runner.js +782 -530
- 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 +1 -1
- package/src/verax/observe/observe-helpers.js +2 -1
- package/src/verax/observe/observe-runner.js +28 -24
- package/src/verax/observe/observers/budget-observer.js +3 -3
- package/src/verax/observe/observers/console-observer.js +4 -4
- package/src/verax/observe/observers/coverage-observer.js +4 -4
- package/src/verax/observe/observers/interaction-observer.js +3 -3
- package/src/verax/observe/observers/navigation-observer.js +4 -4
- package/src/verax/observe/observers/network-observer.js +4 -4
- package/src/verax/observe/observers/safety-observer.js +1 -1
- package/src/verax/observe/observers/ui-feedback-observer.js +4 -4
- package/src/verax/observe/page-traversal.js +138 -0
- package/src/verax/observe/snapshot-ops.js +94 -0
- package/src/verax/observe/ui-signal-sensor.js +2 -148
- package/src/verax/scan-summary-writer.js +10 -42
- package/src/verax/shared/artifact-manager.js +30 -13
- package/src/verax/shared/caching.js +1 -0
- package/src/verax/shared/expectation-tracker.js +1 -0
- package/src/verax/shared/zip-artifacts.js +6 -0
- package/src/verax/core/confidence-engine.js.backup +0 -471
- package/src/verax/shared/config-loader.js +0 -169
- /package/src/verax/shared/{expectation-proof.js → expectation-validation.js} +0 -0
|
@@ -0,0 +1,572 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PHASE H6 - Framework Detection Module
|
|
3
|
+
*
|
|
4
|
+
* Robustly identifies framework type with confidence scoring.
|
|
5
|
+
* Returns deterministic output including evidence trail for auditing.
|
|
6
|
+
*
|
|
7
|
+
* Supported frameworks (in priority order):
|
|
8
|
+
* 1. Next.js
|
|
9
|
+
* 2. Vue + Vite
|
|
10
|
+
* 3. Vue CLI (legacy)
|
|
11
|
+
* 4. Svelte + SvelteKit
|
|
12
|
+
* 5. Angular
|
|
13
|
+
* 6. Remix
|
|
14
|
+
* 7. React + Vite
|
|
15
|
+
* 8. Create React App
|
|
16
|
+
* 9. Vite (generic)
|
|
17
|
+
* 10. Generic/Static
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
import { existsSync, readFileSync, readdirSync } from 'fs';
|
|
21
|
+
import { resolve } from 'path';
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Detection result structure
|
|
25
|
+
*/
|
|
26
|
+
export function createDetectionResult() {
|
|
27
|
+
return {
|
|
28
|
+
framework: 'unknown',
|
|
29
|
+
confidence: 0, // 0-100
|
|
30
|
+
evidence: [],
|
|
31
|
+
devCommand: null,
|
|
32
|
+
defaultPortCandidates: [3000, 5173, 4200, 8080],
|
|
33
|
+
appRootCandidates: [],
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Load and parse package.json safely
|
|
39
|
+
*/
|
|
40
|
+
function loadPackageJson(projectRoot) {
|
|
41
|
+
try {
|
|
42
|
+
const pkgPath = resolve(projectRoot, 'package.json');
|
|
43
|
+
if (!existsSync(pkgPath)) return null;
|
|
44
|
+
const content = readFileSync(pkgPath, 'utf8');
|
|
45
|
+
// @ts-expect-error - readFileSync with encoding returns string
|
|
46
|
+
return JSON.parse(content);
|
|
47
|
+
} catch {
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Check for file existence (deterministic ordering)
|
|
54
|
+
*/
|
|
55
|
+
function filesExist(projectRoot, patterns) {
|
|
56
|
+
const found = [];
|
|
57
|
+
for (const pattern of patterns) {
|
|
58
|
+
const path = resolve(projectRoot, pattern);
|
|
59
|
+
if (existsSync(path)) {
|
|
60
|
+
found.push(pattern);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return found;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* List directories at path (sorted deterministically)
|
|
68
|
+
*/
|
|
69
|
+
function listDirs(projectRoot) {
|
|
70
|
+
try {
|
|
71
|
+
const dirs = readdirSync(projectRoot);
|
|
72
|
+
return dirs
|
|
73
|
+
.filter(name => {
|
|
74
|
+
try {
|
|
75
|
+
const stat = require('fs').statSync(resolve(projectRoot, name));
|
|
76
|
+
return stat.isDirectory();
|
|
77
|
+
} catch {
|
|
78
|
+
return false;
|
|
79
|
+
}
|
|
80
|
+
})
|
|
81
|
+
.sort();
|
|
82
|
+
} catch {
|
|
83
|
+
return [];
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Detect Next.js
|
|
89
|
+
*/
|
|
90
|
+
function detectNextJs(projectRoot, pkg) {
|
|
91
|
+
const result = createDetectionResult();
|
|
92
|
+
let score = 0;
|
|
93
|
+
|
|
94
|
+
// Check dependencies
|
|
95
|
+
const deps = { ...pkg?.dependencies, ...pkg?.devDependencies };
|
|
96
|
+
if (deps.next) {
|
|
97
|
+
score += 40;
|
|
98
|
+
result.evidence.push('next dependency found in package.json');
|
|
99
|
+
}
|
|
100
|
+
if (deps['react'] && deps.next) {
|
|
101
|
+
score += 10;
|
|
102
|
+
result.evidence.push('react + next detected');
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Check next.config.js/mjs
|
|
106
|
+
const configFiles = filesExist(projectRoot, ['next.config.js', 'next.config.mjs']);
|
|
107
|
+
if (configFiles.length > 0) {
|
|
108
|
+
score += 30;
|
|
109
|
+
result.evidence.push(`next config file found: ${configFiles.join(', ')}`);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Check .next directory (build artifact)
|
|
113
|
+
if (existsSync(resolve(projectRoot, '.next'))) {
|
|
114
|
+
score += 5;
|
|
115
|
+
result.evidence.push('.next build directory found');
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Check src/pages or app directory (Next.js structure)
|
|
119
|
+
if (existsSync(resolve(projectRoot, 'src', 'pages')) ||
|
|
120
|
+
existsSync(resolve(projectRoot, 'pages')) ||
|
|
121
|
+
existsSync(resolve(projectRoot, 'src', 'app')) ||
|
|
122
|
+
existsSync(resolve(projectRoot, 'app'))) {
|
|
123
|
+
score += 10;
|
|
124
|
+
result.evidence.push('Next.js pages or app directory found');
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Check dev script
|
|
128
|
+
if (pkg?.scripts?.dev?.includes('next')) {
|
|
129
|
+
score += 5;
|
|
130
|
+
result.evidence.push('next command in dev script');
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
if (score > 0) {
|
|
134
|
+
result.framework = 'next';
|
|
135
|
+
result.confidence = Math.min(score, 100);
|
|
136
|
+
result.devCommand = pkg?.scripts?.dev || 'next dev';
|
|
137
|
+
result.defaultPortCandidates = [3000];
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
return result;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Detect Vue (Vite or CLI)
|
|
145
|
+
*/
|
|
146
|
+
function detectVue(projectRoot, pkg) {
|
|
147
|
+
const result = createDetectionResult();
|
|
148
|
+
let score = 0;
|
|
149
|
+
let isVite = false;
|
|
150
|
+
|
|
151
|
+
// Check dependencies
|
|
152
|
+
const deps = { ...pkg?.dependencies, ...pkg?.devDependencies };
|
|
153
|
+
if (deps.vue) {
|
|
154
|
+
score += 40;
|
|
155
|
+
result.evidence.push('vue dependency found');
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// Check for Vite
|
|
159
|
+
if (deps.vite) {
|
|
160
|
+
score += 20;
|
|
161
|
+
result.evidence.push('vite found (Vue + Vite setup)');
|
|
162
|
+
isVite = true;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// Check vite.config.js/ts
|
|
166
|
+
const viteConfigs = filesExist(projectRoot, ['vite.config.js', 'vite.config.ts', 'vite.config.mjs']);
|
|
167
|
+
if (viteConfigs.length > 0) {
|
|
168
|
+
score += 15;
|
|
169
|
+
result.evidence.push(`vite config found: ${viteConfigs.join(', ')}`);
|
|
170
|
+
isVite = true;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// Check vue.config.js (Vue CLI)
|
|
174
|
+
if (existsSync(resolve(projectRoot, 'vue.config.js'))) {
|
|
175
|
+
score += 15;
|
|
176
|
+
result.evidence.push('vue.config.js found (Vue CLI)');
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// Check src/App.vue or src/components directory
|
|
180
|
+
if (existsSync(resolve(projectRoot, 'src', 'App.vue')) ||
|
|
181
|
+
existsSync(resolve(projectRoot, 'src', 'components'))) {
|
|
182
|
+
score += 10;
|
|
183
|
+
result.evidence.push('Vue component structure found');
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// Check dev script
|
|
187
|
+
if (pkg?.scripts?.dev?.includes('vue') || pkg?.scripts?.dev?.includes('vite')) {
|
|
188
|
+
score += 5;
|
|
189
|
+
result.evidence.push('vue or vite in dev script');
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
if (score > 0) {
|
|
193
|
+
result.framework = 'vue';
|
|
194
|
+
result.confidence = Math.min(score, 100);
|
|
195
|
+
result.devCommand = pkg?.scripts?.dev || (isVite ? 'vite' : 'vue-cli-service serve');
|
|
196
|
+
result.defaultPortCandidates = isVite ? [5173] : [8080];
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
return result;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Detect Svelte (SvelteKit)
|
|
204
|
+
*/
|
|
205
|
+
function detectSvelte(projectRoot, pkg) {
|
|
206
|
+
const result = createDetectionResult();
|
|
207
|
+
let score = 0;
|
|
208
|
+
|
|
209
|
+
const deps = { ...pkg?.dependencies, ...pkg?.devDependencies };
|
|
210
|
+
|
|
211
|
+
// Check for SvelteKit
|
|
212
|
+
if (deps['@sveltejs/kit']) {
|
|
213
|
+
score += 50;
|
|
214
|
+
result.evidence.push('@sveltejs/kit found (SvelteKit)');
|
|
215
|
+
} else if (deps.svelte) {
|
|
216
|
+
score += 30;
|
|
217
|
+
result.evidence.push('svelte dependency found');
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// Check svelte.config.js
|
|
221
|
+
if (existsSync(resolve(projectRoot, 'svelte.config.js'))) {
|
|
222
|
+
score += 20;
|
|
223
|
+
result.evidence.push('svelte.config.js found');
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// Check src directory structure
|
|
227
|
+
if (existsSync(resolve(projectRoot, 'src', 'routes'))) {
|
|
228
|
+
score += 15;
|
|
229
|
+
result.evidence.push('SvelteKit routes structure found');
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// Check dev script
|
|
233
|
+
if (pkg?.scripts?.dev?.includes('svelte') || pkg?.scripts?.dev?.includes('vite')) {
|
|
234
|
+
score += 5;
|
|
235
|
+
result.evidence.push('svelte or vite in dev script');
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
if (score > 0) {
|
|
239
|
+
result.framework = 'svelte';
|
|
240
|
+
result.confidence = Math.min(score, 100);
|
|
241
|
+
result.devCommand = pkg?.scripts?.dev || 'vite dev';
|
|
242
|
+
result.defaultPortCandidates = [5173];
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
return result;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* Detect Angular
|
|
250
|
+
*/
|
|
251
|
+
function detectAngular(projectRoot, pkg) {
|
|
252
|
+
const result = createDetectionResult();
|
|
253
|
+
let score = 0;
|
|
254
|
+
|
|
255
|
+
// Check angular.json (strongest signal)
|
|
256
|
+
if (existsSync(resolve(projectRoot, 'angular.json'))) {
|
|
257
|
+
score += 50;
|
|
258
|
+
result.evidence.push('angular.json found');
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
const deps = { ...pkg?.dependencies, ...pkg?.devDependencies };
|
|
262
|
+
if (deps['@angular/core']) {
|
|
263
|
+
score += 40;
|
|
264
|
+
result.evidence.push('@angular/core dependency found');
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// Check CLI
|
|
268
|
+
if (deps['@angular/cli']) {
|
|
269
|
+
score += 10;
|
|
270
|
+
result.evidence.push('@angular/cli found');
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// Check src/main.ts (Angular entry point)
|
|
274
|
+
if (existsSync(resolve(projectRoot, 'src', 'main.ts'))) {
|
|
275
|
+
score += 10;
|
|
276
|
+
result.evidence.push('Angular src/main.ts found');
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
if (pkg?.scripts?.serve?.includes('ng serve')) {
|
|
280
|
+
score += 5;
|
|
281
|
+
result.evidence.push('ng serve in scripts');
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
if (score > 0) {
|
|
285
|
+
result.framework = 'angular';
|
|
286
|
+
result.confidence = Math.min(score, 100);
|
|
287
|
+
result.devCommand = pkg?.scripts?.serve || 'ng serve';
|
|
288
|
+
result.defaultPortCandidates = [4200];
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
return result;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
/**
|
|
295
|
+
* Detect Remix (or remix-like Node.js framework)
|
|
296
|
+
*/
|
|
297
|
+
function detectRemix(projectRoot, pkg) {
|
|
298
|
+
const result = createDetectionResult();
|
|
299
|
+
let score = 0;
|
|
300
|
+
|
|
301
|
+
const deps = { ...pkg?.dependencies, ...pkg?.devDependencies };
|
|
302
|
+
if (deps['@remix-run/react'] || deps['remix']) {
|
|
303
|
+
score += 50;
|
|
304
|
+
result.evidence.push('@remix-run/react or remix dependency found');
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
// Check remix config
|
|
308
|
+
if (existsSync(resolve(projectRoot, 'remix.config.js'))) {
|
|
309
|
+
score += 30;
|
|
310
|
+
result.evidence.push('remix.config.js found');
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
// Check app directory (Remix routing)
|
|
314
|
+
if (existsSync(resolve(projectRoot, 'app'))) {
|
|
315
|
+
score += 10;
|
|
316
|
+
result.evidence.push('app directory found (Remix structure)');
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
// Check for Express or Node.js server.js (remix-like framework)
|
|
320
|
+
if (existsSync(resolve(projectRoot, 'server.js')) && (deps['express'])) {
|
|
321
|
+
score += 45;
|
|
322
|
+
result.evidence.push('Express server.js found (remix-like Node.js framework)');
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
if (score > 0) {
|
|
326
|
+
result.framework = 'remix';
|
|
327
|
+
result.confidence = Math.min(score, 100);
|
|
328
|
+
result.devCommand = pkg?.scripts?.dev || 'remix dev';
|
|
329
|
+
result.defaultPortCandidates = [3000, 5173];
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
return result;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
/**
|
|
336
|
+
* Detect React (Vite or CRA)
|
|
337
|
+
*/
|
|
338
|
+
function detectReact(projectRoot, pkg) {
|
|
339
|
+
const result = createDetectionResult();
|
|
340
|
+
let score = 0;
|
|
341
|
+
|
|
342
|
+
const deps = { ...pkg?.dependencies, ...pkg?.devDependencies };
|
|
343
|
+
if (deps['react']) {
|
|
344
|
+
score += 40;
|
|
345
|
+
result.evidence.push('react dependency found');
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
if (deps['react-dom']) {
|
|
349
|
+
score += 10;
|
|
350
|
+
result.evidence.push('react-dom found');
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
// Check for CRA (react-scripts)
|
|
354
|
+
if (deps['react-scripts']) {
|
|
355
|
+
score += 30;
|
|
356
|
+
result.evidence.push('react-scripts found (Create React App)');
|
|
357
|
+
result.devCommand = pkg?.scripts?.start || 'react-scripts start';
|
|
358
|
+
result.defaultPortCandidates = [3000];
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
// Check for Vite + React
|
|
362
|
+
if (deps['vite'] && deps['@vitejs/plugin-react']) {
|
|
363
|
+
score += 25;
|
|
364
|
+
result.evidence.push('vite + @vitejs/plugin-react found');
|
|
365
|
+
result.devCommand = pkg?.scripts?.dev || 'vite';
|
|
366
|
+
result.defaultPortCandidates = [5173];
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
// Check vite.config.js with react plugin
|
|
370
|
+
const viteConfigs = filesExist(projectRoot, ['vite.config.js', 'vite.config.ts']);
|
|
371
|
+
if (viteConfigs.length > 0) {
|
|
372
|
+
score += 10;
|
|
373
|
+
result.evidence.push(`vite config found: ${viteConfigs.join(', ')}`);
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
// Check src/App.jsx or src/App.tsx
|
|
377
|
+
if (existsSync(resolve(projectRoot, 'src', 'App.jsx')) ||
|
|
378
|
+
existsSync(resolve(projectRoot, 'src', 'App.tsx'))) {
|
|
379
|
+
score += 10;
|
|
380
|
+
result.evidence.push('React App component found');
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
if (score > 0) {
|
|
384
|
+
result.framework = 'react';
|
|
385
|
+
result.confidence = Math.min(score, 100);
|
|
386
|
+
result.devCommand = result.devCommand || pkg?.scripts?.dev || 'vite';
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
return result;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
/**
|
|
393
|
+
* Detect generic Vite
|
|
394
|
+
*/
|
|
395
|
+
function detectVite(projectRoot, pkg) {
|
|
396
|
+
const result = createDetectionResult();
|
|
397
|
+
let score = 0;
|
|
398
|
+
|
|
399
|
+
const deps = { ...pkg?.dependencies, ...pkg?.devDependencies };
|
|
400
|
+
if (deps['vite']) {
|
|
401
|
+
score += 40;
|
|
402
|
+
result.evidence.push('vite dependency found (generic Vite project)');
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
const viteConfigs = filesExist(projectRoot, ['vite.config.js', 'vite.config.ts', 'vite.config.mjs']);
|
|
406
|
+
if (viteConfigs.length > 0) {
|
|
407
|
+
score += 30;
|
|
408
|
+
result.evidence.push(`vite config: ${viteConfigs.join(', ')}`);
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
if (pkg?.scripts?.dev?.includes('vite')) {
|
|
412
|
+
score += 10;
|
|
413
|
+
result.evidence.push('vite in dev script');
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
if (score > 0) {
|
|
417
|
+
result.framework = 'vite';
|
|
418
|
+
result.confidence = Math.min(score, 100);
|
|
419
|
+
result.devCommand = pkg?.scripts?.dev || 'vite';
|
|
420
|
+
result.defaultPortCandidates = [5173];
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
return result;
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
/**
|
|
427
|
+
* Detect generic static/HTML project
|
|
428
|
+
*/
|
|
429
|
+
function detectStatic(projectRoot, pkg) {
|
|
430
|
+
const result = createDetectionResult();
|
|
431
|
+
let score = 0;
|
|
432
|
+
|
|
433
|
+
// Check for index.html
|
|
434
|
+
if (existsSync(resolve(projectRoot, 'index.html'))) {
|
|
435
|
+
score += 30;
|
|
436
|
+
result.evidence.push('index.html found (static site)');
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
// Check for dist (built static)
|
|
440
|
+
if (existsSync(resolve(projectRoot, 'dist'))) {
|
|
441
|
+
score += 10;
|
|
442
|
+
result.evidence.push('dist directory found');
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
// No framework detected but has HTML
|
|
446
|
+
if (score > 0 && (!pkg || Object.keys(pkg?.dependencies || {}).length === 0)) {
|
|
447
|
+
result.framework = 'static';
|
|
448
|
+
result.confidence = Math.min(score, 100);
|
|
449
|
+
result.devCommand = pkg?.scripts?.serve || 'http-server .';
|
|
450
|
+
result.defaultPortCandidates = [8000, 8080, 5000];
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
return result;
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
/**
|
|
457
|
+
* Main framework detection function
|
|
458
|
+
*
|
|
459
|
+
* Returns most confident match, ordered by detection priority
|
|
460
|
+
*/
|
|
461
|
+
export function detectFramework(projectRoot) {
|
|
462
|
+
const pkg = loadPackageJson(projectRoot);
|
|
463
|
+
|
|
464
|
+
// Priority-ordered detection (deterministic order)
|
|
465
|
+
const detections = [
|
|
466
|
+
detectNextJs(projectRoot, pkg),
|
|
467
|
+
detectVue(projectRoot, pkg),
|
|
468
|
+
detectSvelte(projectRoot, pkg),
|
|
469
|
+
detectAngular(projectRoot, pkg),
|
|
470
|
+
detectRemix(projectRoot, pkg),
|
|
471
|
+
detectReact(projectRoot, pkg),
|
|
472
|
+
detectVite(projectRoot, pkg),
|
|
473
|
+
detectStatic(projectRoot, pkg),
|
|
474
|
+
];
|
|
475
|
+
|
|
476
|
+
// Filter out results with 0 confidence
|
|
477
|
+
const scored = detections.filter(d => d.confidence > 0);
|
|
478
|
+
|
|
479
|
+
// Sort by confidence descending (deterministic tie-break by framework name)
|
|
480
|
+
scored.sort((a, b) => {
|
|
481
|
+
if (b.confidence !== a.confidence) {
|
|
482
|
+
return b.confidence - a.confidence;
|
|
483
|
+
}
|
|
484
|
+
return a.framework.localeCompare(b.framework);
|
|
485
|
+
});
|
|
486
|
+
|
|
487
|
+
// Return highest confidence, or unknown
|
|
488
|
+
if (scored.length > 0) {
|
|
489
|
+
return scored[0];
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
const result = createDetectionResult();
|
|
493
|
+
if (pkg) {
|
|
494
|
+
result.evidence.push('package.json found but no framework detected');
|
|
495
|
+
result.defaultPortCandidates = [3000, 5173, 8080];
|
|
496
|
+
} else {
|
|
497
|
+
result.evidence.push('No package.json found; assuming static site');
|
|
498
|
+
}
|
|
499
|
+
return result;
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
/**
|
|
503
|
+
* Find app root candidates in a monorepo
|
|
504
|
+
* Scans workspace for package.json + framework markers
|
|
505
|
+
* Returns deterministically ordered list of candidates with evidence
|
|
506
|
+
*/
|
|
507
|
+
export function findAppRootCandidates(workspaceRoot, maxDepth = 3) {
|
|
508
|
+
const candidates = [];
|
|
509
|
+
|
|
510
|
+
function scanDir(currentPath, depth) {
|
|
511
|
+
if (depth > maxDepth) return;
|
|
512
|
+
|
|
513
|
+
const dirs = listDirs(currentPath);
|
|
514
|
+
for (const dir of dirs) {
|
|
515
|
+
// Skip common non-app directories
|
|
516
|
+
if (['node_modules', '.git', '.next', 'dist', 'build', '.svelte-kit'].includes(dir)) {
|
|
517
|
+
continue;
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
const appPath = resolve(currentPath, dir);
|
|
521
|
+
const pkg = loadPackageJson(appPath);
|
|
522
|
+
|
|
523
|
+
// If has package.json with scripts.dev or framework markers, it's a candidate
|
|
524
|
+
if (pkg?.scripts?.dev || detectFramework(appPath).confidence > 20) {
|
|
525
|
+
const detection = detectFramework(appPath);
|
|
526
|
+
candidates.push({
|
|
527
|
+
path: appPath,
|
|
528
|
+
framework: detection.framework,
|
|
529
|
+
confidence: detection.confidence,
|
|
530
|
+
hasDevScript: Boolean(pkg?.scripts?.dev),
|
|
531
|
+
depth,
|
|
532
|
+
});
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
// Recurse
|
|
536
|
+
scanDir(appPath, depth + 1);
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
scanDir(workspaceRoot, 0);
|
|
541
|
+
|
|
542
|
+
// Sort candidates deterministically:
|
|
543
|
+
// 1. Highest framework confidence
|
|
544
|
+
// 2. Has dev script (true > false)
|
|
545
|
+
// 3. Shallowest depth
|
|
546
|
+
// 4. Alphabetical path
|
|
547
|
+
candidates.sort((a, b) => {
|
|
548
|
+
if (b.confidence !== a.confidence) return b.confidence - a.confidence;
|
|
549
|
+
if (b.hasDevScript !== a.hasDevScript) return b.hasDevScript ? 1 : -1;
|
|
550
|
+
if (a.depth !== b.depth) return a.depth - b.depth;
|
|
551
|
+
return a.path.localeCompare(b.path);
|
|
552
|
+
});
|
|
553
|
+
|
|
554
|
+
return candidates;
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
/**
|
|
558
|
+
* Export for testing
|
|
559
|
+
*/
|
|
560
|
+
export const _internal = {
|
|
561
|
+
loadPackageJson,
|
|
562
|
+
filesExist,
|
|
563
|
+
listDirs,
|
|
564
|
+
detectNextJs,
|
|
565
|
+
detectVue,
|
|
566
|
+
detectSvelte,
|
|
567
|
+
detectAngular,
|
|
568
|
+
detectRemix,
|
|
569
|
+
detectReact,
|
|
570
|
+
detectVite,
|
|
571
|
+
detectStatic,
|
|
572
|
+
};
|
package/src/cli/util/idgen.js
CHANGED
|
@@ -35,7 +35,7 @@ export function expIdFromHash(file, line, column, kind, value) {
|
|
|
35
35
|
const hash = crypto.createHash('sha256').update(hashInput).digest('hex');
|
|
36
36
|
|
|
37
37
|
// Use first 6 characters for brevity while maintaining low collision risk
|
|
38
|
-
const hashSuffix = hash.substring(0, 6);
|
|
38
|
+
const hashSuffix = String(hash).substring(0, 6);
|
|
39
39
|
|
|
40
40
|
return `exp_${hashSuffix}`;
|
|
41
41
|
}
|