@vibecheckai/cli 3.5.0 → 3.5.2
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/bin/registry.js +214 -237
- package/bin/runners/cli-utils.js +33 -2
- package/bin/runners/context/analyzer.js +52 -1
- package/bin/runners/context/generators/cursor.js +2 -49
- package/bin/runners/context/git-context.js +3 -1
- package/bin/runners/context/team-conventions.js +33 -7
- package/bin/runners/lib/analysis-core.js +25 -5
- package/bin/runners/lib/analyzers.js +431 -481
- package/bin/runners/lib/default-config.js +127 -0
- package/bin/runners/lib/doctor/modules/security.js +3 -1
- package/bin/runners/lib/engine/ast-cache.js +210 -0
- package/bin/runners/lib/engine/auth-extractor.js +211 -0
- package/bin/runners/lib/engine/billing-extractor.js +112 -0
- package/bin/runners/lib/engine/enforcement-extractor.js +100 -0
- package/bin/runners/lib/engine/env-extractor.js +207 -0
- package/bin/runners/lib/engine/express-extractor.js +208 -0
- package/bin/runners/lib/engine/extractors.js +849 -0
- package/bin/runners/lib/engine/index.js +207 -0
- package/bin/runners/lib/engine/repo-index.js +514 -0
- package/bin/runners/lib/engine/types.js +124 -0
- package/bin/runners/lib/engines/accessibility-engine.js +18 -218
- package/bin/runners/lib/engines/api-consistency-engine.js +30 -335
- package/bin/runners/lib/engines/cross-file-analysis-engine.js +27 -292
- package/bin/runners/lib/engines/empty-catch-engine.js +17 -127
- package/bin/runners/lib/engines/mock-data-engine.js +10 -53
- package/bin/runners/lib/engines/performance-issues-engine.js +36 -176
- package/bin/runners/lib/engines/security-vulnerabilities-engine.js +54 -382
- package/bin/runners/lib/engines/type-aware-engine.js +39 -263
- package/bin/runners/lib/engines/vibecheck-engines/index.js +13 -122
- package/bin/runners/lib/engines/vibecheck-engines/lib/ast-cache.js +164 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/code-quality-engine.js +291 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/console-logs-engine.js +83 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/dead-code-engine.js +198 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/deprecated-api-engine.js +275 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/empty-catch-engine.js +167 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/file-filter.js +217 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/hardcoded-secrets-engine.js +73 -373
- package/bin/runners/lib/engines/vibecheck-engines/lib/mock-data-engine.js +140 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/parallel-processor.js +164 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/performance-issues-engine.js +234 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/type-aware-engine.js +217 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/unsafe-regex-engine.js +78 -0
- package/bin/runners/lib/entitlements-v2.js +73 -97
- package/bin/runners/lib/error-handler.js +44 -3
- package/bin/runners/lib/error-messages.js +289 -0
- package/bin/runners/lib/evidence-pack.js +7 -1
- package/bin/runners/lib/finding-id.js +69 -0
- package/bin/runners/lib/finding-sorter.js +89 -0
- package/bin/runners/lib/html-proof-report.js +700 -350
- package/bin/runners/lib/missions/plan.js +6 -46
- package/bin/runners/lib/missions/templates.js +0 -232
- package/bin/runners/lib/next-action.js +560 -0
- package/bin/runners/lib/prerequisites.js +149 -0
- package/bin/runners/lib/route-detection.js +137 -68
- package/bin/runners/lib/scan-output.js +91 -76
- package/bin/runners/lib/scan-runner.js +135 -0
- package/bin/runners/lib/schemas/ajv-validator.js +464 -0
- package/bin/runners/lib/schemas/error-envelope.schema.json +105 -0
- package/bin/runners/lib/schemas/finding-v3.schema.json +151 -0
- package/bin/runners/lib/schemas/report-artifact.schema.json +120 -0
- package/bin/runners/lib/schemas/run-request.schema.json +108 -0
- package/bin/runners/lib/schemas/validator.js +27 -0
- package/bin/runners/lib/schemas/verdict.schema.json +140 -0
- package/bin/runners/lib/ship-output-enterprise.js +23 -23
- package/bin/runners/lib/ship-output.js +75 -31
- package/bin/runners/lib/terminal-ui.js +6 -113
- package/bin/runners/lib/truth.js +351 -10
- package/bin/runners/lib/unified-cli-output.js +430 -603
- package/bin/runners/lib/unified-output.js +13 -9
- package/bin/runners/runAIAgent.js +10 -5
- package/bin/runners/runAgent.js +0 -3
- package/bin/runners/runAllowlist.js +389 -0
- package/bin/runners/runApprove.js +0 -33
- package/bin/runners/runAuth.js +73 -45
- package/bin/runners/runCheckpoint.js +51 -11
- package/bin/runners/runClassify.js +85 -21
- package/bin/runners/runContext.js +0 -3
- package/bin/runners/runDoctor.js +41 -28
- package/bin/runners/runEvidencePack.js +362 -0
- package/bin/runners/runFirewall.js +0 -3
- package/bin/runners/runFirewallHook.js +0 -3
- package/bin/runners/runFix.js +66 -76
- package/bin/runners/runGuard.js +18 -411
- package/bin/runners/runInit.js +113 -30
- package/bin/runners/runLabs.js +424 -0
- package/bin/runners/runMcp.js +19 -25
- package/bin/runners/runPolish.js +64 -240
- package/bin/runners/runPromptFirewall.js +12 -5
- package/bin/runners/runProve.js +57 -22
- package/bin/runners/runQuickstart.js +531 -0
- package/bin/runners/runReality.js +59 -68
- package/bin/runners/runReport.js +38 -33
- package/bin/runners/runRuntime.js +8 -5
- package/bin/runners/runScan.js +1413 -190
- package/bin/runners/runShip.js +113 -719
- package/bin/runners/runTruth.js +0 -3
- package/bin/runners/runValidate.js +13 -9
- package/bin/runners/runWatch.js +23 -14
- package/bin/scan.js +6 -1
- package/bin/vibecheck.js +204 -185
- package/mcp-server/deprecation-middleware.js +282 -0
- package/mcp-server/handlers/index.ts +15 -0
- package/mcp-server/handlers/tool-handler.ts +554 -0
- package/mcp-server/index-v1.js +698 -0
- package/mcp-server/index.js +210 -238
- package/mcp-server/lib/cache-wrapper.cjs +383 -0
- package/mcp-server/lib/error-envelope.js +138 -0
- package/mcp-server/lib/executor.ts +499 -0
- package/mcp-server/lib/index.ts +19 -0
- package/mcp-server/lib/rate-limiter.js +166 -0
- package/mcp-server/lib/sandbox.test.ts +519 -0
- package/mcp-server/lib/sandbox.ts +395 -0
- package/mcp-server/lib/types.ts +267 -0
- package/mcp-server/package.json +12 -3
- package/mcp-server/registry/tool-registry.js +794 -0
- package/mcp-server/registry/tools.json +605 -0
- package/mcp-server/registry.test.ts +334 -0
- package/mcp-server/tests/tier-gating.test.js +297 -0
- package/mcp-server/tier-auth.js +378 -45
- package/mcp-server/tools-v3.js +353 -442
- package/mcp-server/tsconfig.json +37 -0
- package/mcp-server/vibecheck-2.0-tools.js +14 -1
- package/package.json +1 -1
- package/bin/runners/lib/agent-firewall/learning/learning-engine.js +0 -849
- package/bin/runners/lib/audit-logger.js +0 -532
- package/bin/runners/lib/authority/authorities/architecture.js +0 -364
- package/bin/runners/lib/authority/authorities/compliance.js +0 -341
- package/bin/runners/lib/authority/authorities/human.js +0 -343
- package/bin/runners/lib/authority/authorities/quality.js +0 -420
- package/bin/runners/lib/authority/authorities/security.js +0 -228
- package/bin/runners/lib/authority/index.js +0 -293
- package/bin/runners/lib/bundle/bundle-intelligence.js +0 -846
- package/bin/runners/lib/cli-charts.js +0 -368
- package/bin/runners/lib/cli-config-display.js +0 -405
- package/bin/runners/lib/cli-demo.js +0 -275
- package/bin/runners/lib/cli-errors.js +0 -438
- package/bin/runners/lib/cli-help-formatter.js +0 -439
- package/bin/runners/lib/cli-interactive-menu.js +0 -509
- package/bin/runners/lib/cli-prompts.js +0 -441
- package/bin/runners/lib/cli-scan-cards.js +0 -362
- package/bin/runners/lib/compliance-reporter.js +0 -710
- package/bin/runners/lib/conductor/index.js +0 -671
- package/bin/runners/lib/easy/README.md +0 -123
- package/bin/runners/lib/easy/index.js +0 -140
- package/bin/runners/lib/easy/interactive-wizard.js +0 -788
- package/bin/runners/lib/easy/one-click-firewall.js +0 -564
- package/bin/runners/lib/easy/zero-config-reality.js +0 -714
- package/bin/runners/lib/engines/async-patterns-engine.js +0 -444
- package/bin/runners/lib/engines/bundle-size-engine.js +0 -433
- package/bin/runners/lib/engines/confidence-scoring.js +0 -276
- package/bin/runners/lib/engines/context-detection.js +0 -264
- package/bin/runners/lib/engines/database-patterns-engine.js +0 -429
- package/bin/runners/lib/engines/duplicate-code-engine.js +0 -354
- package/bin/runners/lib/engines/env-variables-engine.js +0 -458
- package/bin/runners/lib/engines/error-handling-engine.js +0 -437
- package/bin/runners/lib/engines/false-positive-prevention.js +0 -630
- package/bin/runners/lib/engines/framework-adapters/index.js +0 -607
- package/bin/runners/lib/engines/framework-detection.js +0 -508
- package/bin/runners/lib/engines/import-order-engine.js +0 -429
- package/bin/runners/lib/engines/naming-conventions-engine.js +0 -544
- package/bin/runners/lib/engines/noise-reduction-engine.js +0 -452
- package/bin/runners/lib/engines/orchestrator.js +0 -334
- package/bin/runners/lib/engines/react-patterns-engine.js +0 -457
- package/bin/runners/lib/engines/vibecheck-engines/lib/ai-hallucination-engine.js +0 -806
- package/bin/runners/lib/engines/vibecheck-engines/lib/smart-fix-engine.js +0 -577
- package/bin/runners/lib/engines/vibecheck-engines/lib/vibe-score-engine.js +0 -543
- package/bin/runners/lib/engines/vibecheck-engines.js +0 -514
- package/bin/runners/lib/enhanced-features/index.js +0 -305
- package/bin/runners/lib/enhanced-output.js +0 -631
- package/bin/runners/lib/enterprise.js +0 -300
- package/bin/runners/lib/firewall/command-validator.js +0 -351
- package/bin/runners/lib/firewall/config.js +0 -341
- package/bin/runners/lib/firewall/content-validator.js +0 -519
- package/bin/runners/lib/firewall/index.js +0 -101
- package/bin/runners/lib/firewall/path-validator.js +0 -256
- package/bin/runners/lib/intelligence/cross-repo-intelligence.js +0 -817
- package/bin/runners/lib/mcp-utils.js +0 -425
- package/bin/runners/lib/output/index.js +0 -1022
- package/bin/runners/lib/policy-engine.js +0 -652
- package/bin/runners/lib/polish/autofix/accessibility-fixes.js +0 -333
- package/bin/runners/lib/polish/autofix/async-handlers.js +0 -273
- package/bin/runners/lib/polish/autofix/dead-code.js +0 -280
- package/bin/runners/lib/polish/autofix/imports-optimizer.js +0 -344
- package/bin/runners/lib/polish/autofix/index.js +0 -200
- package/bin/runners/lib/polish/autofix/remove-consoles.js +0 -209
- package/bin/runners/lib/polish/autofix/strengthen-types.js +0 -245
- package/bin/runners/lib/polish/backend-checks.js +0 -148
- package/bin/runners/lib/polish/documentation-checks.js +0 -111
- package/bin/runners/lib/polish/frontend-checks.js +0 -168
- package/bin/runners/lib/polish/index.js +0 -71
- package/bin/runners/lib/polish/infrastructure-checks.js +0 -131
- package/bin/runners/lib/polish/library-detection.js +0 -175
- package/bin/runners/lib/polish/performance-checks.js +0 -100
- package/bin/runners/lib/polish/security-checks.js +0 -148
- package/bin/runners/lib/polish/utils.js +0 -203
- package/bin/runners/lib/prompt-builder.js +0 -540
- package/bin/runners/lib/proof-certificate.js +0 -634
- package/bin/runners/lib/reality/accessibility-audit.js +0 -946
- package/bin/runners/lib/reality/api-contract-validator.js +0 -1012
- package/bin/runners/lib/reality/chaos-engineering.js +0 -1084
- package/bin/runners/lib/reality/performance-tracker.js +0 -1077
- package/bin/runners/lib/reality/scenario-generator.js +0 -1404
- package/bin/runners/lib/reality/visual-regression.js +0 -852
- package/bin/runners/lib/reality-profiler.js +0 -717
- package/bin/runners/lib/replay/flight-recorder-viewer.js +0 -1160
- package/bin/runners/lib/review/ai-code-review.js +0 -832
- package/bin/runners/lib/rules/custom-rule-engine.js +0 -985
- package/bin/runners/lib/sbom-generator.js +0 -641
- package/bin/runners/lib/scan-output-enhanced.js +0 -512
- package/bin/runners/lib/security/owasp-scanner.js +0 -939
- package/bin/runners/lib/validators/contract-validator.js +0 -283
- package/bin/runners/lib/validators/dead-export-detector.js +0 -279
- package/bin/runners/lib/validators/dep-audit.js +0 -245
- package/bin/runners/lib/validators/env-validator.js +0 -319
- package/bin/runners/lib/validators/index.js +0 -120
- package/bin/runners/lib/validators/license-checker.js +0 -252
- package/bin/runners/lib/validators/route-validator.js +0 -290
- package/bin/runners/runAuthority.js +0 -528
- package/bin/runners/runConductor.js +0 -772
- package/bin/runners/runContainer.js +0 -366
- package/bin/runners/runEasy.js +0 -410
- package/bin/runners/runIaC.js +0 -372
- package/bin/runners/runVibe.js +0 -791
- package/mcp-server/tools.js +0 -495
|
@@ -1,283 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Contract Validator - Ship-only check
|
|
3
|
-
* Validates API contracts match implementation
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
"use strict";
|
|
7
|
-
|
|
8
|
-
const path = require("path");
|
|
9
|
-
const fs = require("fs");
|
|
10
|
-
const fg = require("fast-glob");
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* Validate API contracts against implementation
|
|
14
|
-
* Checks:
|
|
15
|
-
* - OpenAPI/Swagger specs match actual routes
|
|
16
|
-
* - TypeScript types match API responses
|
|
17
|
-
* - tRPC routers match client usage
|
|
18
|
-
*/
|
|
19
|
-
async function validateContracts(projectPath) {
|
|
20
|
-
const findings = [];
|
|
21
|
-
const startTime = Date.now();
|
|
22
|
-
|
|
23
|
-
try {
|
|
24
|
-
// Check for OpenAPI/Swagger specs
|
|
25
|
-
const openApiFindings = await validateOpenApiContracts(projectPath);
|
|
26
|
-
findings.push(...openApiFindings);
|
|
27
|
-
|
|
28
|
-
// Check for tRPC contracts
|
|
29
|
-
const trpcFindings = await validateTrpcContracts(projectPath);
|
|
30
|
-
findings.push(...trpcFindings);
|
|
31
|
-
|
|
32
|
-
// Check for GraphQL schemas
|
|
33
|
-
const graphqlFindings = await validateGraphqlContracts(projectPath);
|
|
34
|
-
findings.push(...graphqlFindings);
|
|
35
|
-
|
|
36
|
-
} catch (error) {
|
|
37
|
-
findings.push({
|
|
38
|
-
id: "CONTRACT_VALIDATION_ERROR",
|
|
39
|
-
category: "ContractValidation",
|
|
40
|
-
severity: "INFO",
|
|
41
|
-
title: "Contract validation incomplete",
|
|
42
|
-
message: `Could not fully validate contracts: ${error.message}`,
|
|
43
|
-
confidence: "low",
|
|
44
|
-
type: "validation_error",
|
|
45
|
-
});
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
return {
|
|
49
|
-
findings,
|
|
50
|
-
duration: Date.now() - startTime,
|
|
51
|
-
type: "contract-validator",
|
|
52
|
-
};
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
async function validateOpenApiContracts(projectPath) {
|
|
56
|
-
const findings = [];
|
|
57
|
-
|
|
58
|
-
// Find OpenAPI/Swagger spec files
|
|
59
|
-
const specFiles = fg.sync([
|
|
60
|
-
"**/openapi.{json,yaml,yml}",
|
|
61
|
-
"**/swagger.{json,yaml,yml}",
|
|
62
|
-
"**/api-spec.{json,yaml,yml}",
|
|
63
|
-
], {
|
|
64
|
-
cwd: projectPath,
|
|
65
|
-
absolute: true,
|
|
66
|
-
ignore: ["**/node_modules/**"],
|
|
67
|
-
});
|
|
68
|
-
|
|
69
|
-
if (specFiles.length === 0) {
|
|
70
|
-
// No OpenAPI spec - not an error, just no contract to validate
|
|
71
|
-
return findings;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
for (const specFile of specFiles) {
|
|
75
|
-
try {
|
|
76
|
-
const content = fs.readFileSync(specFile, "utf8");
|
|
77
|
-
const relativePath = path.relative(projectPath, specFile).replace(/\\/g, "/");
|
|
78
|
-
|
|
79
|
-
// Basic validation - check if it parses
|
|
80
|
-
let spec;
|
|
81
|
-
if (specFile.endsWith(".json")) {
|
|
82
|
-
spec = JSON.parse(content);
|
|
83
|
-
} else {
|
|
84
|
-
// YAML parsing would require yaml package
|
|
85
|
-
continue;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
// Check for paths without implementations
|
|
89
|
-
const paths = spec.paths || {};
|
|
90
|
-
|
|
91
|
-
for (const [routePath, methods] of Object.entries(paths)) {
|
|
92
|
-
for (const method of Object.keys(methods)) {
|
|
93
|
-
if (["parameters", "servers", "$ref"].includes(method)) continue;
|
|
94
|
-
|
|
95
|
-
// Check if this route exists in codebase
|
|
96
|
-
const routeExists = await checkRouteExists(projectPath, routePath, method);
|
|
97
|
-
|
|
98
|
-
if (!routeExists) {
|
|
99
|
-
findings.push({
|
|
100
|
-
id: `CONTRACT_MISSING_IMPL_${hashString(routePath + method)}`,
|
|
101
|
-
category: "ContractValidation",
|
|
102
|
-
severity: "WARN",
|
|
103
|
-
title: `API spec defines route without implementation: ${method.toUpperCase()} ${routePath}`,
|
|
104
|
-
message: `OpenAPI spec defines ${method.toUpperCase()} ${routePath} but no matching handler found`,
|
|
105
|
-
file: relativePath,
|
|
106
|
-
confidence: "medium",
|
|
107
|
-
type: "missing_implementation",
|
|
108
|
-
});
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
} catch (error) {
|
|
114
|
-
findings.push({
|
|
115
|
-
id: `CONTRACT_PARSE_ERROR_${hashString(specFile)}`,
|
|
116
|
-
category: "ContractValidation",
|
|
117
|
-
severity: "WARN",
|
|
118
|
-
title: `Could not parse API spec: ${path.basename(specFile)}`,
|
|
119
|
-
message: error.message,
|
|
120
|
-
file: path.relative(projectPath, specFile).replace(/\\/g, "/"),
|
|
121
|
-
confidence: "high",
|
|
122
|
-
type: "parse_error",
|
|
123
|
-
});
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
return findings;
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
async function validateTrpcContracts(projectPath) {
|
|
131
|
-
const findings = [];
|
|
132
|
-
|
|
133
|
-
// Find tRPC router files
|
|
134
|
-
const trpcFiles = fg.sync([
|
|
135
|
-
"**/trpc/**/*.{ts,tsx}",
|
|
136
|
-
"**/server/routers/**/*.{ts,tsx}",
|
|
137
|
-
"**/*.router.{ts,tsx}",
|
|
138
|
-
], {
|
|
139
|
-
cwd: projectPath,
|
|
140
|
-
absolute: true,
|
|
141
|
-
ignore: ["**/node_modules/**", "**/*.test.*", "**/*.spec.*"],
|
|
142
|
-
});
|
|
143
|
-
|
|
144
|
-
if (trpcFiles.length === 0) return findings;
|
|
145
|
-
|
|
146
|
-
// Extract procedure names from routers
|
|
147
|
-
const definedProcedures = new Set();
|
|
148
|
-
|
|
149
|
-
for (const file of trpcFiles) {
|
|
150
|
-
try {
|
|
151
|
-
const content = fs.readFileSync(file, "utf8");
|
|
152
|
-
|
|
153
|
-
// Find procedure definitions
|
|
154
|
-
const procedureMatches = content.matchAll(/\.(?:query|mutation|subscription)\s*\(/g);
|
|
155
|
-
const nameMatches = content.matchAll(/(\w+):\s*(?:router|publicProcedure|protectedProcedure)/g);
|
|
156
|
-
|
|
157
|
-
for (const match of nameMatches) {
|
|
158
|
-
definedProcedures.add(match[1]);
|
|
159
|
-
}
|
|
160
|
-
} catch {
|
|
161
|
-
// Skip files that can't be read
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
// Find tRPC client usage
|
|
166
|
-
const clientFiles = fg.sync(["**/*.{ts,tsx}"], {
|
|
167
|
-
cwd: projectPath,
|
|
168
|
-
absolute: true,
|
|
169
|
-
ignore: ["**/node_modules/**", "**/server/**", "**/*.test.*"],
|
|
170
|
-
});
|
|
171
|
-
|
|
172
|
-
for (const file of clientFiles) {
|
|
173
|
-
try {
|
|
174
|
-
const content = fs.readFileSync(file, "utf8");
|
|
175
|
-
const relativePath = path.relative(projectPath, file).replace(/\\/g, "/");
|
|
176
|
-
|
|
177
|
-
// Find trpc client calls
|
|
178
|
-
const clientMatches = content.matchAll(/trpc\.(\w+)\.(?:useQuery|useMutation|useSubscription)/g);
|
|
179
|
-
|
|
180
|
-
for (const match of clientMatches) {
|
|
181
|
-
const procedureName = match[1];
|
|
182
|
-
const line = content.substring(0, match.index).split("\n").length;
|
|
183
|
-
|
|
184
|
-
if (!definedProcedures.has(procedureName) && procedureName !== "useContext") {
|
|
185
|
-
findings.push({
|
|
186
|
-
id: `TRPC_MISSING_PROCEDURE_${hashString(procedureName + file)}`,
|
|
187
|
-
category: "ContractValidation",
|
|
188
|
-
severity: "WARN",
|
|
189
|
-
title: `tRPC procedure may not exist: ${procedureName}`,
|
|
190
|
-
message: `Client calls trpc.${procedureName} but no matching router procedure found`,
|
|
191
|
-
file: relativePath,
|
|
192
|
-
line,
|
|
193
|
-
confidence: "medium",
|
|
194
|
-
type: "missing_procedure",
|
|
195
|
-
});
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
} catch {
|
|
199
|
-
// Skip files that can't be read
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
return findings;
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
async function validateGraphqlContracts(projectPath) {
|
|
207
|
-
const findings = [];
|
|
208
|
-
|
|
209
|
-
// Find GraphQL schema files
|
|
210
|
-
const schemaFiles = fg.sync([
|
|
211
|
-
"**/*.graphql",
|
|
212
|
-
"**/*.gql",
|
|
213
|
-
"**/schema.{ts,js}",
|
|
214
|
-
], {
|
|
215
|
-
cwd: projectPath,
|
|
216
|
-
absolute: true,
|
|
217
|
-
ignore: ["**/node_modules/**"],
|
|
218
|
-
});
|
|
219
|
-
|
|
220
|
-
if (schemaFiles.length === 0) return findings;
|
|
221
|
-
|
|
222
|
-
// Basic check: schema files should have resolvers
|
|
223
|
-
const resolverFiles = fg.sync([
|
|
224
|
-
"**/resolvers/**/*.{ts,js}",
|
|
225
|
-
"**/*.resolvers.{ts,js}",
|
|
226
|
-
], {
|
|
227
|
-
cwd: projectPath,
|
|
228
|
-
absolute: true,
|
|
229
|
-
ignore: ["**/node_modules/**"],
|
|
230
|
-
});
|
|
231
|
-
|
|
232
|
-
if (schemaFiles.length > 0 && resolverFiles.length === 0) {
|
|
233
|
-
findings.push({
|
|
234
|
-
id: "GRAPHQL_NO_RESOLVERS",
|
|
235
|
-
category: "ContractValidation",
|
|
236
|
-
severity: "WARN",
|
|
237
|
-
title: "GraphQL schema found but no resolvers detected",
|
|
238
|
-
message: "GraphQL schema files exist but no resolver implementations found",
|
|
239
|
-
confidence: "medium",
|
|
240
|
-
type: "missing_resolvers",
|
|
241
|
-
});
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
return findings;
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
async function checkRouteExists(projectPath, routePath, method) {
|
|
248
|
-
// Convert OpenAPI path to file path pattern
|
|
249
|
-
const normalizedPath = routePath
|
|
250
|
-
.replace(/\{(\w+)\}/g, "[$1]") // {id} -> [id]
|
|
251
|
-
.replace(/^\//, "");
|
|
252
|
-
|
|
253
|
-
// Check for Next.js API routes
|
|
254
|
-
const patterns = [
|
|
255
|
-
`**/pages/api/${normalizedPath}.{ts,js}`,
|
|
256
|
-
`**/pages/api/${normalizedPath}/index.{ts,js}`,
|
|
257
|
-
`**/app/api/${normalizedPath}/route.{ts,js}`,
|
|
258
|
-
];
|
|
259
|
-
|
|
260
|
-
for (const pattern of patterns) {
|
|
261
|
-
const files = fg.sync([pattern], {
|
|
262
|
-
cwd: projectPath,
|
|
263
|
-
ignore: ["**/node_modules/**"],
|
|
264
|
-
});
|
|
265
|
-
if (files.length > 0) return true;
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
return false;
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
function hashString(str) {
|
|
272
|
-
let hash = 0;
|
|
273
|
-
for (let i = 0; i < str.length; i++) {
|
|
274
|
-
const char = str.charCodeAt(i);
|
|
275
|
-
hash = ((hash << 5) - hash) + char;
|
|
276
|
-
hash = hash & hash;
|
|
277
|
-
}
|
|
278
|
-
return Math.abs(hash).toString(36).substring(0, 8);
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
module.exports = {
|
|
282
|
-
validateContracts,
|
|
283
|
-
};
|
|
@@ -1,279 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Dead Export Detector - Ship-only check
|
|
3
|
-
* Detects exports that are never imported anywhere
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
"use strict";
|
|
7
|
-
|
|
8
|
-
const path = require("path");
|
|
9
|
-
const fs = require("fs");
|
|
10
|
-
const fg = require("fast-glob");
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* Detect exports that are never imported
|
|
14
|
-
* This helps identify dead code that should be removed
|
|
15
|
-
*/
|
|
16
|
-
async function detectDeadExports(projectPath) {
|
|
17
|
-
const findings = [];
|
|
18
|
-
const startTime = Date.now();
|
|
19
|
-
|
|
20
|
-
try {
|
|
21
|
-
// Get all source files
|
|
22
|
-
const files = fg.sync(["**/*.{ts,tsx,js,jsx}"], {
|
|
23
|
-
cwd: projectPath,
|
|
24
|
-
absolute: true,
|
|
25
|
-
ignore: [
|
|
26
|
-
"**/node_modules/**",
|
|
27
|
-
"**/dist/**",
|
|
28
|
-
"**/build/**",
|
|
29
|
-
"**/.next/**",
|
|
30
|
-
"**/*.test.*",
|
|
31
|
-
"**/*.spec.*",
|
|
32
|
-
"**/coverage/**",
|
|
33
|
-
"**/__tests__/**",
|
|
34
|
-
"**/tests/**",
|
|
35
|
-
"**/index.ts",
|
|
36
|
-
"**/index.js",
|
|
37
|
-
"**/index.tsx",
|
|
38
|
-
"**/index.jsx",
|
|
39
|
-
],
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
// Phase 1: Collect all exports
|
|
43
|
-
const exports = new Map(); // export name -> { file, line, type }
|
|
44
|
-
|
|
45
|
-
for (const file of files) {
|
|
46
|
-
try {
|
|
47
|
-
const content = fs.readFileSync(file, "utf8");
|
|
48
|
-
const relativePath = path.relative(projectPath, file).replace(/\\/g, "/");
|
|
49
|
-
|
|
50
|
-
const fileExports = extractExports(content, relativePath);
|
|
51
|
-
for (const exp of fileExports) {
|
|
52
|
-
const key = `${relativePath}:${exp.name}`;
|
|
53
|
-
exports.set(key, exp);
|
|
54
|
-
}
|
|
55
|
-
} catch {
|
|
56
|
-
// Skip files that can't be read
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
// Phase 2: Collect all imports
|
|
61
|
-
const imports = new Set(); // imported names
|
|
62
|
-
|
|
63
|
-
for (const file of files) {
|
|
64
|
-
try {
|
|
65
|
-
const content = fs.readFileSync(file, "utf8");
|
|
66
|
-
const fileImports = extractImports(content);
|
|
67
|
-
for (const imp of fileImports) {
|
|
68
|
-
imports.add(imp);
|
|
69
|
-
}
|
|
70
|
-
} catch {
|
|
71
|
-
// Skip files that can't be read
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
// Phase 3: Find dead exports
|
|
76
|
-
let deadCount = 0;
|
|
77
|
-
const MAX_DEAD_EXPORTS = 20; // Limit to prevent noise
|
|
78
|
-
|
|
79
|
-
for (const [key, exp] of exports.entries()) {
|
|
80
|
-
// Skip entry points and common patterns
|
|
81
|
-
if (isLikelyEntryPoint(exp.file, exp.name)) continue;
|
|
82
|
-
|
|
83
|
-
// Check if this export is imported anywhere
|
|
84
|
-
if (!imports.has(exp.name)) {
|
|
85
|
-
// Double-check: search for the name in all files
|
|
86
|
-
const isUsed = await isExportUsedAnywhere(exp.name, files, exp.file);
|
|
87
|
-
|
|
88
|
-
if (!isUsed) {
|
|
89
|
-
deadCount++;
|
|
90
|
-
|
|
91
|
-
if (deadCount <= MAX_DEAD_EXPORTS) {
|
|
92
|
-
findings.push({
|
|
93
|
-
id: `DEAD_EXPORT_${hashString(key)}`,
|
|
94
|
-
category: "DeadExport",
|
|
95
|
-
severity: "INFO",
|
|
96
|
-
title: `Potentially unused export: ${exp.name}`,
|
|
97
|
-
message: `${exp.name} is exported from ${exp.file} but may not be imported anywhere`,
|
|
98
|
-
file: exp.file,
|
|
99
|
-
line: exp.line,
|
|
100
|
-
confidence: "low",
|
|
101
|
-
type: "dead_export",
|
|
102
|
-
});
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
// Summary if there are many dead exports
|
|
109
|
-
if (deadCount > MAX_DEAD_EXPORTS) {
|
|
110
|
-
findings.push({
|
|
111
|
-
id: "DEAD_EXPORT_SUMMARY",
|
|
112
|
-
category: "DeadExport",
|
|
113
|
-
severity: "WARN",
|
|
114
|
-
title: `${deadCount} potentially unused exports found`,
|
|
115
|
-
message: `Consider removing unused exports to reduce bundle size and improve maintainability`,
|
|
116
|
-
confidence: "low",
|
|
117
|
-
type: "dead_export_summary",
|
|
118
|
-
});
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
} catch (error) {
|
|
122
|
-
findings.push({
|
|
123
|
-
id: "DEAD_EXPORT_ERROR",
|
|
124
|
-
category: "DeadExport",
|
|
125
|
-
severity: "INFO",
|
|
126
|
-
title: "Dead export detection incomplete",
|
|
127
|
-
message: `Could not complete dead export detection: ${error.message}`,
|
|
128
|
-
confidence: "low",
|
|
129
|
-
type: "detection_error",
|
|
130
|
-
});
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
return {
|
|
134
|
-
findings,
|
|
135
|
-
duration: Date.now() - startTime,
|
|
136
|
-
type: "dead-export-detector",
|
|
137
|
-
};
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
function extractExports(content, file) {
|
|
141
|
-
const exports = [];
|
|
142
|
-
const lines = content.split("\n");
|
|
143
|
-
|
|
144
|
-
for (let i = 0; i < lines.length; i++) {
|
|
145
|
-
const line = lines[i];
|
|
146
|
-
|
|
147
|
-
// export const/let/var/function/class name
|
|
148
|
-
const namedExportMatch = line.match(/export\s+(?:const|let|var|function|class|type|interface|enum)\s+([A-Za-z_$][A-Za-z0-9_$]*)/);
|
|
149
|
-
if (namedExportMatch) {
|
|
150
|
-
exports.push({
|
|
151
|
-
name: namedExportMatch[1],
|
|
152
|
-
file,
|
|
153
|
-
line: i + 1,
|
|
154
|
-
type: "named",
|
|
155
|
-
});
|
|
156
|
-
continue;
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
// export { name, name2 }
|
|
160
|
-
const bracedExportMatch = line.match(/export\s*\{([^}]+)\}/);
|
|
161
|
-
if (bracedExportMatch) {
|
|
162
|
-
const names = bracedExportMatch[1].split(",").map(n => {
|
|
163
|
-
const asMatch = n.trim().match(/(\w+)\s+as\s+(\w+)/);
|
|
164
|
-
return asMatch ? asMatch[2] : n.trim().split(" ")[0];
|
|
165
|
-
}).filter(Boolean);
|
|
166
|
-
|
|
167
|
-
for (const name of names) {
|
|
168
|
-
exports.push({
|
|
169
|
-
name,
|
|
170
|
-
file,
|
|
171
|
-
line: i + 1,
|
|
172
|
-
type: "named",
|
|
173
|
-
});
|
|
174
|
-
}
|
|
175
|
-
continue;
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
// export default (we track this specially)
|
|
179
|
-
if (/export\s+default/.test(line)) {
|
|
180
|
-
exports.push({
|
|
181
|
-
name: "default",
|
|
182
|
-
file,
|
|
183
|
-
line: i + 1,
|
|
184
|
-
type: "default",
|
|
185
|
-
});
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
return exports;
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
function extractImports(content) {
|
|
193
|
-
const imports = new Set();
|
|
194
|
-
|
|
195
|
-
// import { name, name2 } from 'module'
|
|
196
|
-
const bracedImportMatches = content.matchAll(/import\s*\{([^}]+)\}\s*from/g);
|
|
197
|
-
for (const match of bracedImportMatches) {
|
|
198
|
-
const names = match[1].split(",").map(n => {
|
|
199
|
-
const asMatch = n.trim().match(/(\w+)\s+as\s+(\w+)/);
|
|
200
|
-
return asMatch ? asMatch[1] : n.trim().split(" ")[0];
|
|
201
|
-
}).filter(Boolean);
|
|
202
|
-
|
|
203
|
-
for (const name of names) {
|
|
204
|
-
imports.add(name);
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
// import name from 'module' (default import)
|
|
209
|
-
const defaultImportMatches = content.matchAll(/import\s+(\w+)\s+from/g);
|
|
210
|
-
for (const match of defaultImportMatches) {
|
|
211
|
-
imports.add(match[1]);
|
|
212
|
-
imports.add("default");
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
// Dynamic imports
|
|
216
|
-
const dynamicImportMatches = content.matchAll(/import\s*\(\s*['"`]([^'"`]+)['"`]\s*\)/g);
|
|
217
|
-
for (const match of dynamicImportMatches) {
|
|
218
|
-
// Mark the default export as potentially used
|
|
219
|
-
imports.add("default");
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
return imports;
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
function isLikelyEntryPoint(file, name) {
|
|
226
|
-
// Entry point files
|
|
227
|
-
if (file.includes("page.") || file.includes("route.") || file.includes("layout.")) return true;
|
|
228
|
-
if (file.includes("middleware.")) return true;
|
|
229
|
-
if (file.endsWith("config.ts") || file.endsWith("config.js")) return true;
|
|
230
|
-
|
|
231
|
-
// Common exports that are used externally
|
|
232
|
-
const externalExports = ["handler", "GET", "POST", "PUT", "DELETE", "PATCH", "middleware"];
|
|
233
|
-
if (externalExports.includes(name)) return true;
|
|
234
|
-
|
|
235
|
-
// API route handlers
|
|
236
|
-
if (file.includes("/api/")) return true;
|
|
237
|
-
|
|
238
|
-
// Default exports are often consumed by frameworks
|
|
239
|
-
if (name === "default") return true;
|
|
240
|
-
|
|
241
|
-
return false;
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
async function isExportUsedAnywhere(name, files, sourceFile) {
|
|
245
|
-
// Quick regex check across all files
|
|
246
|
-
for (const file of files) {
|
|
247
|
-
if (file.includes(sourceFile)) continue; // Skip the source file
|
|
248
|
-
|
|
249
|
-
try {
|
|
250
|
-
const content = fs.readFileSync(file, "utf8");
|
|
251
|
-
|
|
252
|
-
// Check for import of this name
|
|
253
|
-
const importPattern = new RegExp(`import[^}]*\\b${name}\\b[^}]*from`, "g");
|
|
254
|
-
if (importPattern.test(content)) return true;
|
|
255
|
-
|
|
256
|
-
// Check for dynamic import and usage
|
|
257
|
-
if (content.includes(name) && content.includes("import(")) return true;
|
|
258
|
-
|
|
259
|
-
} catch {
|
|
260
|
-
// Skip files that can't be read
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
return false;
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
function hashString(str) {
|
|
268
|
-
let hash = 0;
|
|
269
|
-
for (let i = 0; i < str.length; i++) {
|
|
270
|
-
const char = str.charCodeAt(i);
|
|
271
|
-
hash = ((hash << 5) - hash) + char;
|
|
272
|
-
hash = hash & hash;
|
|
273
|
-
}
|
|
274
|
-
return Math.abs(hash).toString(36).substring(0, 8);
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
module.exports = {
|
|
278
|
-
detectDeadExports,
|
|
279
|
-
};
|