guardrail-core 1.0.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/dist/__tests__/autopilot.test.d.ts +7 -0
- package/dist/__tests__/autopilot.test.d.ts.map +1 -0
- package/dist/__tests__/autopilot.test.js +156 -0
- package/dist/__tests__/tier-config.test.d.ts +9 -0
- package/dist/__tests__/tier-config.test.d.ts.map +1 -0
- package/dist/__tests__/tier-config.test.js +230 -0
- package/dist/__tests__/utils/hash-inline.test.d.ts +2 -0
- package/dist/__tests__/utils/hash-inline.test.d.ts.map +1 -0
- package/dist/__tests__/utils/hash-inline.test.js +62 -0
- package/dist/__tests__/utils/hash.test.d.ts +3 -0
- package/dist/__tests__/utils/hash.test.d.ts.map +1 -0
- package/dist/__tests__/utils/hash.test.js +95 -0
- package/dist/__tests__/utils/simple.test.d.ts +1 -0
- package/dist/__tests__/utils/simple.test.d.ts.map +1 -0
- package/dist/__tests__/utils/simple.test.js +10 -0
- package/dist/__tests__/utils/utils-simple.test.d.ts +1 -0
- package/dist/__tests__/utils/utils-simple.test.d.ts.map +1 -0
- package/dist/__tests__/utils/utils-simple.test.js +6 -0
- package/dist/__tests__/utils/utils.test.d.ts +15 -0
- package/dist/__tests__/utils/utils.test.d.ts.map +1 -0
- package/dist/__tests__/utils/utils.test.js +172 -0
- package/dist/autopilot/autopilot-runner.d.ts +33 -0
- package/dist/autopilot/autopilot-runner.d.ts.map +1 -0
- package/dist/autopilot/autopilot-runner.js +479 -0
- package/dist/autopilot/index.d.ts +6 -0
- package/dist/autopilot/index.d.ts.map +1 -0
- package/dist/autopilot/index.js +25 -0
- package/dist/autopilot/types.d.ts +102 -0
- package/dist/autopilot/types.d.ts.map +1 -0
- package/dist/autopilot/types.js +18 -0
- package/dist/cache/index.d.ts +7 -0
- package/dist/cache/index.d.ts.map +1 -0
- package/dist/cache/index.js +22 -0
- package/dist/cache/redis-cache.d.ts +145 -0
- package/dist/cache/redis-cache.d.ts.map +1 -0
- package/dist/cache/redis-cache.js +459 -0
- package/dist/ci/github-actions.d.ts +77 -0
- package/dist/ci/github-actions.d.ts.map +1 -0
- package/dist/ci/github-actions.js +277 -0
- package/dist/ci/index.d.ts +12 -0
- package/dist/ci/index.d.ts.map +1 -0
- package/dist/ci/index.js +27 -0
- package/dist/ci/pre-commit.d.ts +65 -0
- package/dist/ci/pre-commit.d.ts.map +1 -0
- package/dist/ci/pre-commit.js +286 -0
- package/dist/entitlements.d.ts +149 -0
- package/dist/entitlements.d.ts.map +1 -0
- package/dist/entitlements.js +464 -0
- package/dist/env.d.ts +113 -0
- package/dist/env.d.ts.map +1 -0
- package/dist/env.js +204 -0
- package/dist/fix-packs/__tests__/generate-fix-packs.test.d.ts +7 -0
- package/dist/fix-packs/__tests__/generate-fix-packs.test.d.ts.map +1 -0
- package/dist/fix-packs/__tests__/generate-fix-packs.test.js +250 -0
- package/dist/fix-packs/generate-fix-packs.d.ts +15 -0
- package/dist/fix-packs/generate-fix-packs.d.ts.map +1 -0
- package/dist/fix-packs/generate-fix-packs.js +505 -0
- package/dist/fix-packs/index.d.ts +8 -0
- package/dist/fix-packs/index.d.ts.map +1 -0
- package/dist/fix-packs/index.js +23 -0
- package/dist/fix-packs/types.d.ts +113 -0
- package/dist/fix-packs/types.d.ts.map +1 -0
- package/dist/fix-packs/types.js +71 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +28 -0
- package/dist/metrics/prometheus.d.ts +99 -0
- package/dist/metrics/prometheus.d.ts.map +1 -0
- package/dist/metrics/prometheus.js +306 -0
- package/dist/quota-ledger.d.ts +119 -0
- package/dist/quota-ledger.d.ts.map +1 -0
- package/dist/quota-ledger.js +462 -0
- package/dist/rbac/__tests__/permissions.test.d.ts +8 -0
- package/dist/rbac/__tests__/permissions.test.d.ts.map +1 -0
- package/dist/rbac/__tests__/permissions.test.js +350 -0
- package/dist/rbac/index.d.ts +9 -0
- package/dist/rbac/index.d.ts.map +1 -0
- package/dist/rbac/index.js +32 -0
- package/dist/rbac/permissions.d.ts +71 -0
- package/dist/rbac/permissions.d.ts.map +1 -0
- package/dist/rbac/permissions.js +247 -0
- package/dist/rbac/types.d.ts +69 -0
- package/dist/rbac/types.d.ts.map +1 -0
- package/dist/rbac/types.js +213 -0
- package/dist/tier-config.d.ts +203 -0
- package/dist/tier-config.d.ts.map +1 -0
- package/dist/tier-config.js +675 -0
- package/dist/types.d.ts +365 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +5 -0
- package/dist/utils.d.ts +36 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +127 -0
- package/dist/verified-autofix/__tests__/format-validator.test.d.ts +11 -0
- package/dist/verified-autofix/__tests__/format-validator.test.d.ts.map +1 -0
- package/dist/verified-autofix/__tests__/format-validator.test.js +285 -0
- package/dist/verified-autofix/__tests__/pipeline.test.d.ts +11 -0
- package/dist/verified-autofix/__tests__/pipeline.test.d.ts.map +1 -0
- package/dist/verified-autofix/__tests__/pipeline.test.js +389 -0
- package/dist/verified-autofix/__tests__/repo-fingerprint.test.d.ts +11 -0
- package/dist/verified-autofix/__tests__/repo-fingerprint.test.d.ts.map +1 -0
- package/dist/verified-autofix/__tests__/repo-fingerprint.test.js +236 -0
- package/dist/verified-autofix/__tests__/workspace.test.d.ts +11 -0
- package/dist/verified-autofix/__tests__/workspace.test.d.ts.map +1 -0
- package/dist/verified-autofix/__tests__/workspace.test.js +314 -0
- package/dist/verified-autofix/format-validator.d.ts +101 -0
- package/dist/verified-autofix/format-validator.d.ts.map +1 -0
- package/dist/verified-autofix/format-validator.js +446 -0
- package/dist/verified-autofix/index.d.ts +14 -0
- package/dist/verified-autofix/index.d.ts.map +1 -0
- package/dist/verified-autofix/index.js +39 -0
- package/dist/verified-autofix/pipeline.d.ts +68 -0
- package/dist/verified-autofix/pipeline.d.ts.map +1 -0
- package/dist/verified-autofix/pipeline.js +330 -0
- package/dist/verified-autofix/repo-fingerprint.d.ts +56 -0
- package/dist/verified-autofix/repo-fingerprint.d.ts.map +1 -0
- package/dist/verified-autofix/repo-fingerprint.js +396 -0
- package/dist/verified-autofix/workspace.d.ts +83 -0
- package/dist/verified-autofix/workspace.d.ts.map +1 -0
- package/dist/verified-autofix/workspace.js +454 -0
- package/dist/verified-autofix.d.ts +182 -0
- package/dist/verified-autofix.d.ts.map +1 -0
- package/dist/verified-autofix.js +1021 -0
- package/dist/visualization/dependency-graph.d.ts +79 -0
- package/dist/visualization/dependency-graph.d.ts.map +1 -0
- package/dist/visualization/dependency-graph.js +399 -0
- package/dist/visualization/index.d.ts +5 -0
- package/dist/visualization/index.d.ts.map +1 -0
- package/dist/visualization/index.js +20 -0
- package/package.json +29 -0
- package/src/__tests__/autopilot.test.ts +196 -0
- package/src/__tests__/tier-config.test.ts +289 -0
- package/src/__tests__/utils/hash-inline.test.ts +76 -0
- package/src/__tests__/utils/hash.test.ts +119 -0
- package/src/__tests__/utils/simple.test.ts +10 -0
- package/src/__tests__/utils/utils-simple.test.ts +5 -0
- package/src/__tests__/utils/utils.test.ts +203 -0
- package/src/autopilot/autopilot-runner.ts +503 -0
- package/src/autopilot/index.ts +6 -0
- package/src/autopilot/types.ts +119 -0
- package/src/cache/index.ts +7 -0
- package/src/cache/redis-cache.d.ts +155 -0
- package/src/cache/redis-cache.d.ts.map +1 -0
- package/src/cache/redis-cache.ts +517 -0
- package/src/ci/github-actions.ts +335 -0
- package/src/ci/index.ts +12 -0
- package/src/ci/pre-commit.ts +338 -0
- package/src/db/usage-schema.prisma +114 -0
- package/src/entitlements.ts +570 -0
- package/src/env.d.ts +68 -0
- package/src/env.d.ts.map +1 -0
- package/src/env.ts +247 -0
- package/src/fix-packs/__tests__/generate-fix-packs.test.ts +317 -0
- package/src/fix-packs/generate-fix-packs.ts +577 -0
- package/src/fix-packs/index.ts +8 -0
- package/src/fix-packs/types.ts +206 -0
- package/src/index.d.ts +7 -0
- package/src/index.d.ts.map +1 -0
- package/src/index.ts +12 -0
- package/src/metrics/prometheus.d.ts +104 -0
- package/src/metrics/prometheus.d.ts.map +1 -0
- package/src/metrics/prometheus.ts +446 -0
- package/src/quota-ledger.ts +548 -0
- package/src/rbac/__tests__/permissions.test.ts +446 -0
- package/src/rbac/index.ts +46 -0
- package/src/rbac/permissions.ts +301 -0
- package/src/rbac/types.ts +298 -0
- package/src/tier-config.json +157 -0
- package/src/tier-config.ts +815 -0
- package/src/types.d.ts +365 -0
- package/src/types.d.ts.map +1 -0
- package/src/types.ts +441 -0
- package/src/utils.d.ts +36 -0
- package/src/utils.d.ts.map +1 -0
- package/src/utils.ts +140 -0
- package/src/verified-autofix/__tests__/format-validator.test.ts +335 -0
- package/src/verified-autofix/__tests__/pipeline.test.ts +419 -0
- package/src/verified-autofix/__tests__/repo-fingerprint.test.ts +241 -0
- package/src/verified-autofix/__tests__/workspace.test.ts +373 -0
- package/src/verified-autofix/format-validator.ts +517 -0
- package/src/verified-autofix/index.ts +63 -0
- package/src/verified-autofix/pipeline.ts +403 -0
- package/src/verified-autofix/repo-fingerprint.ts +459 -0
- package/src/verified-autofix/workspace.ts +531 -0
- package/src/verified-autofix.ts +1187 -0
- package/src/visualization/dependency-graph.d.ts +85 -0
- package/src/visualization/dependency-graph.d.ts.map +1 -0
- package/src/visualization/dependency-graph.ts +495 -0
- package/src/visualization/index.ts +5 -0
|
@@ -0,0 +1,459 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Repo Fingerprint - Project Detection & Configuration
|
|
3
|
+
*
|
|
4
|
+
* Detects project characteristics:
|
|
5
|
+
* 1. Package manager (pnpm, yarn, npm)
|
|
6
|
+
* 2. Build tool (turbo, nx, none)
|
|
7
|
+
* 3. Framework (next, vite, cra, etc.)
|
|
8
|
+
* 4. Test runner (jest, vitest, mocha, etc.)
|
|
9
|
+
* 5. Language features (TypeScript, ESLint, etc.)
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import * as fs from 'fs';
|
|
13
|
+
import * as path from 'path';
|
|
14
|
+
|
|
15
|
+
// ============================================================================
|
|
16
|
+
// TYPES
|
|
17
|
+
// ============================================================================
|
|
18
|
+
|
|
19
|
+
export type PackageManager = 'pnpm' | 'yarn' | 'npm' | 'bun' | 'unknown';
|
|
20
|
+
export type BuildTool = 'turbo' | 'nx' | 'lerna' | 'none';
|
|
21
|
+
export type Framework = 'next' | 'vite' | 'cra' | 'remix' | 'gatsby' | 'nuxt' | 'svelte' | 'angular' | 'express' | 'fastify' | 'none';
|
|
22
|
+
export type TestRunner = 'jest' | 'vitest' | 'mocha' | 'ava' | 'playwright' | 'cypress' | 'none';
|
|
23
|
+
|
|
24
|
+
export interface RepoFingerprint {
|
|
25
|
+
packageManager: PackageManager;
|
|
26
|
+
buildTool: BuildTool;
|
|
27
|
+
framework: Framework;
|
|
28
|
+
testRunner: TestRunner;
|
|
29
|
+
hasTypeScript: boolean;
|
|
30
|
+
hasESLint: boolean;
|
|
31
|
+
hasPrettier: boolean;
|
|
32
|
+
hasBuildScript: boolean;
|
|
33
|
+
hasTestScript: boolean;
|
|
34
|
+
isMonorepo: boolean;
|
|
35
|
+
workspaces: string[];
|
|
36
|
+
nodeVersion?: string;
|
|
37
|
+
dependencies: Record<string, string>;
|
|
38
|
+
devDependencies: Record<string, string>;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export interface FingerprintResult {
|
|
42
|
+
fingerprint: RepoFingerprint;
|
|
43
|
+
confidence: number;
|
|
44
|
+
detectionNotes: string[];
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// ============================================================================
|
|
48
|
+
// DETECTION FUNCTIONS
|
|
49
|
+
// ============================================================================
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Detect package manager from lock files
|
|
53
|
+
*/
|
|
54
|
+
function detectPackageManager(projectPath: string): PackageManager {
|
|
55
|
+
if (fs.existsSync(path.join(projectPath, 'pnpm-lock.yaml'))) {
|
|
56
|
+
return 'pnpm';
|
|
57
|
+
}
|
|
58
|
+
if (fs.existsSync(path.join(projectPath, 'yarn.lock'))) {
|
|
59
|
+
return 'yarn';
|
|
60
|
+
}
|
|
61
|
+
if (fs.existsSync(path.join(projectPath, 'bun.lockb'))) {
|
|
62
|
+
return 'bun';
|
|
63
|
+
}
|
|
64
|
+
if (fs.existsSync(path.join(projectPath, 'package-lock.json'))) {
|
|
65
|
+
return 'npm';
|
|
66
|
+
}
|
|
67
|
+
return 'unknown';
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Detect build tool (monorepo orchestrator)
|
|
72
|
+
*/
|
|
73
|
+
function detectBuildTool(projectPath: string, pkg: PackageJson): BuildTool {
|
|
74
|
+
// Turbo
|
|
75
|
+
if (
|
|
76
|
+
fs.existsSync(path.join(projectPath, 'turbo.json')) ||
|
|
77
|
+
pkg.devDependencies?.['turbo'] ||
|
|
78
|
+
pkg.dependencies?.['turbo']
|
|
79
|
+
) {
|
|
80
|
+
return 'turbo';
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Nx
|
|
84
|
+
if (
|
|
85
|
+
fs.existsSync(path.join(projectPath, 'nx.json')) ||
|
|
86
|
+
pkg.devDependencies?.['nx'] ||
|
|
87
|
+
pkg.dependencies?.['nx']
|
|
88
|
+
) {
|
|
89
|
+
return 'nx';
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Lerna
|
|
93
|
+
if (
|
|
94
|
+
fs.existsSync(path.join(projectPath, 'lerna.json')) ||
|
|
95
|
+
pkg.devDependencies?.['lerna']
|
|
96
|
+
) {
|
|
97
|
+
return 'lerna';
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return 'none';
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Detect framework from dependencies
|
|
105
|
+
*/
|
|
106
|
+
function detectFramework(pkg: PackageJson): Framework {
|
|
107
|
+
const deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
108
|
+
|
|
109
|
+
// Next.js
|
|
110
|
+
if (deps['next']) {
|
|
111
|
+
return 'next';
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Remix
|
|
115
|
+
if (deps['@remix-run/react'] || deps['remix']) {
|
|
116
|
+
return 'remix';
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Gatsby
|
|
120
|
+
if (deps['gatsby']) {
|
|
121
|
+
return 'gatsby';
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Nuxt
|
|
125
|
+
if (deps['nuxt'] || deps['nuxt3']) {
|
|
126
|
+
return 'nuxt';
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// SvelteKit
|
|
130
|
+
if (deps['@sveltejs/kit'] || deps['svelte']) {
|
|
131
|
+
return 'svelte';
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// Angular
|
|
135
|
+
if (deps['@angular/core']) {
|
|
136
|
+
return 'angular';
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// Vite (generic)
|
|
140
|
+
if (deps['vite']) {
|
|
141
|
+
return 'vite';
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// Create React App
|
|
145
|
+
if (deps['react-scripts']) {
|
|
146
|
+
return 'cra';
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// Express
|
|
150
|
+
if (deps['express']) {
|
|
151
|
+
return 'express';
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// Fastify
|
|
155
|
+
if (deps['fastify']) {
|
|
156
|
+
return 'fastify';
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
return 'none';
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Detect test runner
|
|
164
|
+
*/
|
|
165
|
+
function detectTestRunner(projectPath: string, pkg: PackageJson): TestRunner {
|
|
166
|
+
const deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
167
|
+
|
|
168
|
+
// Vitest
|
|
169
|
+
if (deps['vitest']) {
|
|
170
|
+
return 'vitest';
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// Jest
|
|
174
|
+
if (deps['jest'] || fs.existsSync(path.join(projectPath, 'jest.config.js')) ||
|
|
175
|
+
fs.existsSync(path.join(projectPath, 'jest.config.ts'))) {
|
|
176
|
+
return 'jest';
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// Mocha
|
|
180
|
+
if (deps['mocha']) {
|
|
181
|
+
return 'mocha';
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// AVA
|
|
185
|
+
if (deps['ava']) {
|
|
186
|
+
return 'ava';
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// Playwright
|
|
190
|
+
if (deps['@playwright/test'] || deps['playwright']) {
|
|
191
|
+
return 'playwright';
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// Cypress
|
|
195
|
+
if (deps['cypress']) {
|
|
196
|
+
return 'cypress';
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
return 'none';
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Detect if project is a monorepo
|
|
204
|
+
*/
|
|
205
|
+
function detectMonorepo(projectPath: string, pkg: PackageJson): { isMonorepo: boolean; workspaces: string[] } {
|
|
206
|
+
// Check package.json workspaces
|
|
207
|
+
if (pkg.workspaces) {
|
|
208
|
+
const workspaces = Array.isArray(pkg.workspaces)
|
|
209
|
+
? pkg.workspaces
|
|
210
|
+
: pkg.workspaces.packages || [];
|
|
211
|
+
return { isMonorepo: true, workspaces };
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// Check pnpm-workspace.yaml
|
|
215
|
+
const pnpmWorkspacePath = path.join(projectPath, 'pnpm-workspace.yaml');
|
|
216
|
+
if (fs.existsSync(pnpmWorkspacePath)) {
|
|
217
|
+
try {
|
|
218
|
+
const content = fs.readFileSync(pnpmWorkspacePath, 'utf8');
|
|
219
|
+
const workspaces: string[] = [];
|
|
220
|
+
const matches = content.match(/packages:\s*\n((?:\s+-\s*['"]?[^\n]+['"]?\n?)+)/);
|
|
221
|
+
if (matches && matches[1]) {
|
|
222
|
+
const lines = matches[1].split('\n');
|
|
223
|
+
for (const line of lines) {
|
|
224
|
+
const match = line.match(/^\s*-\s*['"]?([^'"]+)['"]?/);
|
|
225
|
+
if (match && match[1]) {
|
|
226
|
+
workspaces.push(match[1]);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
return { isMonorepo: true, workspaces };
|
|
231
|
+
} catch {
|
|
232
|
+
return { isMonorepo: true, workspaces: [] };
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// Check for common monorepo directories
|
|
237
|
+
const monorepoIndicators = ['packages', 'apps', 'libs', 'modules'];
|
|
238
|
+
for (const dir of monorepoIndicators) {
|
|
239
|
+
const dirPath = path.join(projectPath, dir);
|
|
240
|
+
if (fs.existsSync(dirPath) && fs.statSync(dirPath).isDirectory()) {
|
|
241
|
+
// Check if any subdirectory has a package.json
|
|
242
|
+
try {
|
|
243
|
+
const subdirs = fs.readdirSync(dirPath);
|
|
244
|
+
for (const subdir of subdirs) {
|
|
245
|
+
if (fs.existsSync(path.join(dirPath, subdir, 'package.json'))) {
|
|
246
|
+
return { isMonorepo: true, workspaces: [`${dir}/*`] };
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
} catch {
|
|
250
|
+
// Ignore read errors
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
return { isMonorepo: false, workspaces: [] };
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* Read Node.js version from .nvmrc or similar
|
|
260
|
+
*/
|
|
261
|
+
function readNodeVersion(projectPath: string): string | undefined {
|
|
262
|
+
const versionFiles = ['.nvmrc', '.node-version', '.tool-versions'];
|
|
263
|
+
|
|
264
|
+
for (const file of versionFiles) {
|
|
265
|
+
const filePath = path.join(projectPath, file);
|
|
266
|
+
if (fs.existsSync(filePath)) {
|
|
267
|
+
try {
|
|
268
|
+
const content = fs.readFileSync(filePath, 'utf8').trim();
|
|
269
|
+
if (file === '.tool-versions') {
|
|
270
|
+
const match = content.match(/nodejs\s+([^\s]+)/);
|
|
271
|
+
return match ? match[1] : undefined;
|
|
272
|
+
}
|
|
273
|
+
return content.replace(/^v/, '');
|
|
274
|
+
} catch {
|
|
275
|
+
// Ignore read errors
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
return undefined;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
// ============================================================================
|
|
284
|
+
// TYPES
|
|
285
|
+
// ============================================================================
|
|
286
|
+
|
|
287
|
+
interface PackageJson {
|
|
288
|
+
name?: string;
|
|
289
|
+
scripts?: Record<string, string>;
|
|
290
|
+
dependencies?: Record<string, string>;
|
|
291
|
+
devDependencies?: Record<string, string>;
|
|
292
|
+
workspaces?: string[] | { packages: string[] };
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
// ============================================================================
|
|
296
|
+
// MAIN FINGERPRINT FUNCTION
|
|
297
|
+
// ============================================================================
|
|
298
|
+
|
|
299
|
+
/**
|
|
300
|
+
* Analyze a project and return its fingerprint
|
|
301
|
+
*/
|
|
302
|
+
export function fingerprintRepo(projectPath: string): FingerprintResult {
|
|
303
|
+
const notes: string[] = [];
|
|
304
|
+
let confidence = 100;
|
|
305
|
+
|
|
306
|
+
// Read package.json
|
|
307
|
+
const pkgPath = path.join(projectPath, 'package.json');
|
|
308
|
+
let pkg: PackageJson = {};
|
|
309
|
+
|
|
310
|
+
if (fs.existsSync(pkgPath)) {
|
|
311
|
+
try {
|
|
312
|
+
pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
|
|
313
|
+
} catch (e) {
|
|
314
|
+
notes.push(`Failed to parse package.json: ${(e as Error).message}`);
|
|
315
|
+
confidence -= 20;
|
|
316
|
+
}
|
|
317
|
+
} else {
|
|
318
|
+
notes.push('No package.json found');
|
|
319
|
+
confidence -= 30;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
const packageManager = detectPackageManager(projectPath);
|
|
323
|
+
const buildTool = detectBuildTool(projectPath, pkg);
|
|
324
|
+
const framework = detectFramework(pkg);
|
|
325
|
+
const testRunner = detectTestRunner(projectPath, pkg);
|
|
326
|
+
const { isMonorepo, workspaces } = detectMonorepo(projectPath, pkg);
|
|
327
|
+
const nodeVersion = readNodeVersion(projectPath);
|
|
328
|
+
|
|
329
|
+
// TypeScript detection
|
|
330
|
+
const hasTypeScript = !!(
|
|
331
|
+
fs.existsSync(path.join(projectPath, 'tsconfig.json')) ||
|
|
332
|
+
pkg.devDependencies?.['typescript'] ||
|
|
333
|
+
pkg.dependencies?.['typescript']
|
|
334
|
+
);
|
|
335
|
+
|
|
336
|
+
// ESLint detection
|
|
337
|
+
const hasESLint = !!(
|
|
338
|
+
fs.existsSync(path.join(projectPath, '.eslintrc.js')) ||
|
|
339
|
+
fs.existsSync(path.join(projectPath, '.eslintrc.json')) ||
|
|
340
|
+
fs.existsSync(path.join(projectPath, '.eslintrc.yml')) ||
|
|
341
|
+
fs.existsSync(path.join(projectPath, 'eslint.config.js')) ||
|
|
342
|
+
pkg.devDependencies?.['eslint']
|
|
343
|
+
);
|
|
344
|
+
|
|
345
|
+
// Prettier detection
|
|
346
|
+
const hasPrettier = !!(
|
|
347
|
+
fs.existsSync(path.join(projectPath, '.prettierrc')) ||
|
|
348
|
+
fs.existsSync(path.join(projectPath, '.prettierrc.js')) ||
|
|
349
|
+
fs.existsSync(path.join(projectPath, '.prettierrc.json')) ||
|
|
350
|
+
fs.existsSync(path.join(projectPath, 'prettier.config.js')) ||
|
|
351
|
+
pkg.devDependencies?.['prettier']
|
|
352
|
+
);
|
|
353
|
+
|
|
354
|
+
// Script detection
|
|
355
|
+
const hasBuildScript = !!(pkg.scripts?.['build']);
|
|
356
|
+
const hasTestScript = !!(pkg.scripts?.['test']);
|
|
357
|
+
|
|
358
|
+
// Add detection notes
|
|
359
|
+
if (packageManager === 'unknown') {
|
|
360
|
+
notes.push('Could not detect package manager');
|
|
361
|
+
confidence -= 10;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
if (framework === 'none') {
|
|
365
|
+
notes.push('No major framework detected');
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
if (testRunner === 'none') {
|
|
369
|
+
notes.push('No test runner detected');
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
const fingerprint: RepoFingerprint = {
|
|
373
|
+
packageManager,
|
|
374
|
+
buildTool,
|
|
375
|
+
framework,
|
|
376
|
+
testRunner,
|
|
377
|
+
hasTypeScript,
|
|
378
|
+
hasESLint,
|
|
379
|
+
hasPrettier,
|
|
380
|
+
hasBuildScript,
|
|
381
|
+
hasTestScript,
|
|
382
|
+
isMonorepo,
|
|
383
|
+
workspaces,
|
|
384
|
+
nodeVersion,
|
|
385
|
+
dependencies: pkg.dependencies || {},
|
|
386
|
+
devDependencies: pkg.devDependencies || {},
|
|
387
|
+
};
|
|
388
|
+
|
|
389
|
+
return {
|
|
390
|
+
fingerprint,
|
|
391
|
+
confidence: Math.max(0, confidence),
|
|
392
|
+
detectionNotes: notes,
|
|
393
|
+
};
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
/**
|
|
397
|
+
* Get appropriate install command for package manager
|
|
398
|
+
*/
|
|
399
|
+
export function getInstallCommand(packageManager: PackageManager): string {
|
|
400
|
+
switch (packageManager) {
|
|
401
|
+
case 'pnpm':
|
|
402
|
+
return 'pnpm install --frozen-lockfile';
|
|
403
|
+
case 'yarn':
|
|
404
|
+
return 'yarn install --frozen-lockfile';
|
|
405
|
+
case 'bun':
|
|
406
|
+
return 'bun install --frozen-lockfile';
|
|
407
|
+
case 'npm':
|
|
408
|
+
default:
|
|
409
|
+
return 'npm ci';
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
/**
|
|
414
|
+
* Get appropriate build command
|
|
415
|
+
*/
|
|
416
|
+
export function getBuildCommand(fingerprint: RepoFingerprint): string {
|
|
417
|
+
if (fingerprint.buildTool === 'turbo') {
|
|
418
|
+
return 'npx turbo run build';
|
|
419
|
+
}
|
|
420
|
+
if (fingerprint.buildTool === 'nx') {
|
|
421
|
+
return 'npx nx run-many --target=build';
|
|
422
|
+
}
|
|
423
|
+
if (fingerprint.hasBuildScript) {
|
|
424
|
+
return 'npm run build';
|
|
425
|
+
}
|
|
426
|
+
return 'echo "No build script"';
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
/**
|
|
430
|
+
* Get appropriate test command
|
|
431
|
+
*/
|
|
432
|
+
export function getTestCommand(fingerprint: RepoFingerprint): string {
|
|
433
|
+
switch (fingerprint.testRunner) {
|
|
434
|
+
case 'vitest':
|
|
435
|
+
return 'npx vitest run';
|
|
436
|
+
case 'jest':
|
|
437
|
+
return 'npx jest --passWithNoTests';
|
|
438
|
+
case 'mocha':
|
|
439
|
+
return 'npx mocha';
|
|
440
|
+
case 'ava':
|
|
441
|
+
return 'npx ava';
|
|
442
|
+
case 'playwright':
|
|
443
|
+
return 'npx playwright test';
|
|
444
|
+
case 'cypress':
|
|
445
|
+
return 'npx cypress run';
|
|
446
|
+
default:
|
|
447
|
+
return fingerprint.hasTestScript ? 'npm test' : 'echo "No test runner"';
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
/**
|
|
452
|
+
* Get typecheck command if TypeScript is present
|
|
453
|
+
*/
|
|
454
|
+
export function getTypecheckCommand(fingerprint: RepoFingerprint): string | null {
|
|
455
|
+
if (!fingerprint.hasTypeScript) {
|
|
456
|
+
return null;
|
|
457
|
+
}
|
|
458
|
+
return 'npx tsc --noEmit';
|
|
459
|
+
}
|