autodocs-engine 0.9.8 → 0.10.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 +123 -117
- package/dist/anti-pattern-detector.js +22 -0
- package/dist/anti-pattern-detector.js.map +1 -1
- package/dist/architecture-detector.js +33 -29
- package/dist/architecture-detector.js.map +1 -1
- package/dist/ast-parser.js +31 -3
- package/dist/ast-parser.js.map +1 -1
- package/dist/benchmark/scorer.js +3 -1
- package/dist/benchmark/scorer.js.map +1 -1
- package/dist/bin/autodocs-engine.js +7 -0
- package/dist/bin/autodocs-engine.js.map +1 -1
- package/dist/bin/serve.d.ts +1 -0
- package/dist/bin/serve.js +5 -1
- package/dist/bin/serve.js.map +1 -1
- package/dist/bin/setup-hooks.d.ts +1 -0
- package/dist/bin/setup-hooks.js +59 -0
- package/dist/bin/setup-hooks.js.map +1 -0
- package/dist/config.d.ts +1 -0
- package/dist/config.js +4 -0
- package/dist/config.js.map +1 -1
- package/dist/convention-extractor.js +10 -0
- package/dist/convention-extractor.js.map +1 -1
- package/dist/detectors/api-patterns.d.ts +2 -0
- package/dist/detectors/api-patterns.js +101 -0
- package/dist/detectors/api-patterns.js.map +1 -0
- package/dist/detectors/async-patterns.d.ts +2 -0
- package/dist/detectors/async-patterns.js +79 -0
- package/dist/detectors/async-patterns.js.map +1 -0
- package/dist/detectors/error-handling.js +82 -30
- package/dist/detectors/error-handling.js.map +1 -1
- package/dist/detectors/state-management.d.ts +2 -0
- package/dist/detectors/state-management.js +75 -0
- package/dist/detectors/state-management.js.map +1 -0
- package/dist/execution-flow.d.ts +23 -0
- package/dist/execution-flow.js +286 -0
- package/dist/execution-flow.js.map +1 -0
- package/dist/git-history.js +18 -7
- package/dist/git-history.js.map +1 -1
- package/dist/implicit-coupling.d.ts +7 -0
- package/dist/implicit-coupling.js +40 -0
- package/dist/implicit-coupling.js.map +1 -0
- package/dist/import-chain.js +12 -3
- package/dist/import-chain.js.map +1 -1
- package/dist/index.js +20 -1
- package/dist/index.js.map +1 -1
- package/dist/llm/serializer.js +8 -6
- package/dist/llm/serializer.js.map +1 -1
- package/dist/mcp/cache.d.ts +11 -1
- package/dist/mcp/cache.js +49 -7
- package/dist/mcp/cache.js.map +1 -1
- package/dist/mcp/queries.d.ts +19 -3
- package/dist/mcp/queries.js +342 -62
- package/dist/mcp/queries.js.map +1 -1
- package/dist/mcp/server.d.ts +1 -0
- package/dist/mcp/server.js +42 -1
- package/dist/mcp/server.js.map +1 -1
- package/dist/mcp/tools.d.ts +1 -0
- package/dist/mcp/tools.js +162 -20
- package/dist/mcp/tools.js.map +1 -1
- package/dist/output-validator.js +73 -0
- package/dist/output-validator.js.map +1 -1
- package/dist/pipeline.js +37 -0
- package/dist/pipeline.js.map +1 -1
- package/dist/symbol-graph.js +4 -0
- package/dist/symbol-graph.js.map +1 -1
- package/dist/type-enricher.d.ts +17 -0
- package/dist/type-enricher.js +127 -0
- package/dist/type-enricher.js.map +1 -0
- package/dist/type-resolver.d.ts +13 -0
- package/dist/type-resolver.js +75 -0
- package/dist/type-resolver.js.map +1 -0
- package/dist/types.d.ts +37 -4
- package/dist/types.js +7 -2
- package/dist/types.js.map +1 -1
- package/hooks/autodocs-hook.cjs +227 -0
- package/package.json +3 -2
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
// src/detectors/api-patterns.ts — API pattern detector
|
|
2
|
+
// Framework-aware detection: uses DependencyInsights to determine which framework
|
|
3
|
+
// is active, then looks for framework-specific patterns in exports/imports.
|
|
4
|
+
// Does NOT rely on directory name heuristics (routes/, api/, controllers/).
|
|
5
|
+
import { buildConfidence, sourceParsedFiles } from "../convention-extractor.js";
|
|
6
|
+
// Framework-specific detection strategies
|
|
7
|
+
const FRAMEWORK_PATTERNS = {
|
|
8
|
+
express: {
|
|
9
|
+
packages: ["express"],
|
|
10
|
+
label: "Express",
|
|
11
|
+
description: "Express.js HTTP framework",
|
|
12
|
+
},
|
|
13
|
+
fastify: {
|
|
14
|
+
packages: ["fastify"],
|
|
15
|
+
label: "Fastify",
|
|
16
|
+
description: "Fastify HTTP framework",
|
|
17
|
+
},
|
|
18
|
+
hono: {
|
|
19
|
+
packages: ["hono"],
|
|
20
|
+
label: "Hono",
|
|
21
|
+
description: "Hono edge-first HTTP framework",
|
|
22
|
+
},
|
|
23
|
+
koa: {
|
|
24
|
+
packages: ["koa", "@koa/router"],
|
|
25
|
+
label: "Koa",
|
|
26
|
+
description: "Koa HTTP framework",
|
|
27
|
+
},
|
|
28
|
+
nestjs: {
|
|
29
|
+
packages: ["@nestjs/core", "@nestjs/common"],
|
|
30
|
+
label: "NestJS",
|
|
31
|
+
description: "NestJS framework with decorators",
|
|
32
|
+
},
|
|
33
|
+
trpc: {
|
|
34
|
+
packages: ["@trpc/server"],
|
|
35
|
+
label: "tRPC",
|
|
36
|
+
description: "tRPC type-safe API framework",
|
|
37
|
+
},
|
|
38
|
+
graphql: {
|
|
39
|
+
packages: ["@apollo/server", "graphql-yoga", "type-graphql", "mercurius"],
|
|
40
|
+
label: "GraphQL server",
|
|
41
|
+
description: "GraphQL API server",
|
|
42
|
+
},
|
|
43
|
+
};
|
|
44
|
+
export const apiPatternsDetector = (files, tiers, _warnings, context) => {
|
|
45
|
+
const conventions = [];
|
|
46
|
+
const sourceFiles = sourceParsedFiles(files, tiers);
|
|
47
|
+
if (sourceFiles.length === 0)
|
|
48
|
+
return conventions;
|
|
49
|
+
// Strategy 1: Dependency-based detection (highest confidence)
|
|
50
|
+
const detectedFromDeps = new Set();
|
|
51
|
+
if (context?.dependencies?.frameworks) {
|
|
52
|
+
for (const fw of context.dependencies.frameworks) {
|
|
53
|
+
for (const [key, pattern] of Object.entries(FRAMEWORK_PATTERNS)) {
|
|
54
|
+
if (pattern.packages.some((pkg) => fw.name === pkg || fw.name.startsWith(`${pkg}/`))) {
|
|
55
|
+
detectedFromDeps.add(key);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
// Strategy 2: Import-based detection (fallback + validation)
|
|
61
|
+
const importCounts = new Map(); // framework key → file count
|
|
62
|
+
for (const file of sourceFiles) {
|
|
63
|
+
const fileFrameworks = new Set();
|
|
64
|
+
for (const imp of file.imports) {
|
|
65
|
+
if (imp.isTypeOnly)
|
|
66
|
+
continue;
|
|
67
|
+
for (const [key, pattern] of Object.entries(FRAMEWORK_PATTERNS)) {
|
|
68
|
+
if (pattern.packages.some((pkg) => imp.moduleSpecifier === pkg || imp.moduleSpecifier.startsWith(`${pkg}/`))) {
|
|
69
|
+
fileFrameworks.add(key);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
for (const key of fileFrameworks) {
|
|
74
|
+
importCounts.set(key, (importCounts.get(key) ?? 0) + 1);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
// Merge: deps detection + import validation
|
|
78
|
+
const detected = new Set([...detectedFromDeps, ...importCounts.keys()]);
|
|
79
|
+
if (detected.size === 0)
|
|
80
|
+
return conventions;
|
|
81
|
+
// Emit one convention per detected API framework
|
|
82
|
+
for (const key of detected) {
|
|
83
|
+
const pattern = FRAMEWORK_PATTERNS[key];
|
|
84
|
+
if (!pattern)
|
|
85
|
+
continue;
|
|
86
|
+
const fileCount = importCounts.get(key) ?? 0;
|
|
87
|
+
// Skip if detected only from deps but no actual imports (monorepo root dep noise)
|
|
88
|
+
if (fileCount === 0 && !detectedFromDeps.has(key))
|
|
89
|
+
continue;
|
|
90
|
+
conventions.push({
|
|
91
|
+
category: "api-patterns",
|
|
92
|
+
source: "apiPatterns",
|
|
93
|
+
name: `${pattern.label} API`,
|
|
94
|
+
description: `${pattern.description}${fileCount > 0 ? ` (${fileCount} files import from it)` : ""}`,
|
|
95
|
+
confidence: buildConfidence(Math.max(fileCount, 1), sourceFiles.length),
|
|
96
|
+
examples: fileCount > 0 ? [`${fileCount} files import from ${pattern.packages[0]}`] : [],
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
return conventions;
|
|
100
|
+
};
|
|
101
|
+
//# sourceMappingURL=api-patterns.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api-patterns.js","sourceRoot":"","sources":["../../src/detectors/api-patterns.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
// src/detectors/async-patterns.ts — Async pattern detector
|
|
2
|
+
// Detects: Promise.all/allSettled usage, sequential await anti-patterns, AbortController.
|
|
3
|
+
// Uses ContentSignals: promiseAllCount, asyncFunctionCount, awaitInLoopCount.
|
|
4
|
+
import { buildConfidence, sourceParsedFiles } from "../convention-extractor.js";
|
|
5
|
+
export const asyncPatternsDetector = (files, tiers, _warnings, _context) => {
|
|
6
|
+
const conventions = [];
|
|
7
|
+
const sourceFiles = sourceParsedFiles(files, tiers);
|
|
8
|
+
if (sourceFiles.length === 0)
|
|
9
|
+
return conventions;
|
|
10
|
+
// AbortController usage — cancellation pattern (not gated on async functions)
|
|
11
|
+
let abortControllerFiles = 0;
|
|
12
|
+
for (const file of sourceFiles) {
|
|
13
|
+
let found = false;
|
|
14
|
+
for (const imp of file.imports) {
|
|
15
|
+
if (imp.isTypeOnly)
|
|
16
|
+
continue;
|
|
17
|
+
if (imp.importedNames.includes("AbortController") || imp.importedNames.includes("AbortSignal")) {
|
|
18
|
+
found = true;
|
|
19
|
+
break;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
if (!found) {
|
|
23
|
+
for (const exp of file.exports) {
|
|
24
|
+
if (exp.signature?.includes("AbortSignal") || exp.signature?.includes("signal")) {
|
|
25
|
+
found = true;
|
|
26
|
+
break;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
if (found)
|
|
31
|
+
abortControllerFiles++;
|
|
32
|
+
}
|
|
33
|
+
if (abortControllerFiles >= 2) {
|
|
34
|
+
conventions.push({
|
|
35
|
+
category: "async-patterns",
|
|
36
|
+
source: "asyncPatterns",
|
|
37
|
+
name: "AbortController cancellation pattern",
|
|
38
|
+
description: `Uses AbortController/AbortSignal for cancellation (${abortControllerFiles} files)`,
|
|
39
|
+
confidence: buildConfidence(abortControllerFiles, sourceFiles.length),
|
|
40
|
+
examples: [],
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
// Async-specific patterns require async functions
|
|
44
|
+
const asyncFiles = sourceFiles.filter((f) => f.contentSignals.asyncFunctionCount > 0);
|
|
45
|
+
if (asyncFiles.length === 0)
|
|
46
|
+
return conventions;
|
|
47
|
+
// Concurrent patterns — Promise.all/allSettled/race
|
|
48
|
+
const promiseAllFiles = asyncFiles.filter((f) => f.contentSignals.promiseAllCount > 0);
|
|
49
|
+
if (promiseAllFiles.length > 0) {
|
|
50
|
+
const ratio = promiseAllFiles.length / asyncFiles.length;
|
|
51
|
+
if (ratio >= 0.15) {
|
|
52
|
+
conventions.push({
|
|
53
|
+
category: "async-patterns",
|
|
54
|
+
source: "asyncPatterns",
|
|
55
|
+
name: "Promise.all concurrent pattern",
|
|
56
|
+
description: `Uses Promise.all/allSettled for concurrent async operations (${promiseAllFiles.length} of ${asyncFiles.length} async files)`,
|
|
57
|
+
confidence: buildConfidence(promiseAllFiles.length, asyncFiles.length),
|
|
58
|
+
examples: promiseAllFiles.slice(0, 3).map((f) => f.relativePath),
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
// Sequential await in loops — anti-pattern signal (higher threshold: must be widespread, not incidental)
|
|
63
|
+
const awaitInLoopFiles = sourceFiles.filter((f) => f.contentSignals.awaitInLoopCount > 0);
|
|
64
|
+
if (awaitInLoopFiles.length >= 3) {
|
|
65
|
+
const ratio = awaitInLoopFiles.length / asyncFiles.length;
|
|
66
|
+
if (ratio >= 0.25) {
|
|
67
|
+
conventions.push({
|
|
68
|
+
category: "async-patterns",
|
|
69
|
+
source: "asyncPatterns",
|
|
70
|
+
name: "Sequential await in loops",
|
|
71
|
+
description: `${awaitInLoopFiles.length} file${awaitInLoopFiles.length === 1 ? "" : "s"} contain${awaitInLoopFiles.length === 1 ? "s" : ""} await inside loops — consider Promise.all for independent operations`,
|
|
72
|
+
confidence: buildConfidence(awaitInLoopFiles.length, asyncFiles.length),
|
|
73
|
+
examples: awaitInLoopFiles.slice(0, 3).map((f) => f.relativePath),
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return conventions;
|
|
78
|
+
};
|
|
79
|
+
//# sourceMappingURL=async-patterns.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"async-patterns.js","sourceRoot":"","sources":["../../src/detectors/async-patterns.ts"],"names":[],"mappings":"AAAA,2DAA2D;AAC3D,0FAA0F;AAC1F,8EAA8E;AAE9E,OAAO,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AAGhF,MAAM,CAAC,MAAM,qBAAqB,GAAuB,CAAC,KAAK,EAAE,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,EAAE;IAC7F,MAAM,WAAW,GAAiB,EAAE,CAAC;IACrC,MAAM,WAAW,GAAG,iBAAiB,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;IACpD,IAAI,WAAW,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,WAAW,CAAC;IAEjD,8EAA8E;IAC9E,IAAI,oBAAoB,GAAG,CAAC,CAAC;IAC7B,KAAK,MAAM,IAAI,IAAI,WAAW,EAAE,CAAC;QAC/B,IAAI,KAAK,GAAG,KAAK,CAAC;QAClB,KAAK,MAAM,GAAG,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YAC/B,IAAI,GAAG,CAAC,UAAU;gBAAE,SAAS;YAC7B,IAAI,GAAG,CAAC,aAAa,CAAC,QAAQ,CAAC,iBAAiB,CAAC,IAAI,GAAG,CAAC,aAAa,CAAC,QAAQ,CAAC,aAAa,CAAC,EAAE,CAAC;gBAC/F,KAAK,GAAG,IAAI,CAAC;gBACb,MAAM;YACR,CAAC;QACH,CAAC;QACD,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,KAAK,MAAM,GAAG,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;gBAC/B,IAAI,GAAG,CAAC,SAAS,EAAE,QAAQ,CAAC,aAAa,CAAC,IAAI,GAAG,CAAC,SAAS,EAAE,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;oBAChF,KAAK,GAAG,IAAI,CAAC;oBACb,MAAM;gBACR,CAAC;YACH,CAAC;QACH,CAAC;QACD,IAAI,KAAK;YAAE,oBAAoB,EAAE,CAAC;IACpC,CAAC;IACD,IAAI,oBAAoB,IAAI,CAAC,EAAE,CAAC;QAC9B,WAAW,CAAC,IAAI,CAAC;YACf,QAAQ,EAAE,gBAAgB;YAC1B,MAAM,EAAE,eAAe;YACvB,IAAI,EAAE,sCAAsC;YAC5C,WAAW,EAAE,sDAAsD,oBAAoB,SAAS;YAChG,UAAU,EAAE,eAAe,CAAC,oBAAoB,EAAE,WAAW,CAAC,MAAM,CAAC;YACrE,QAAQ,EAAE,EAAE;SACb,CAAC,CAAC;IACL,CAAC;IAED,kDAAkD;IAClD,MAAM,UAAU,GAAG,WAAW,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,cAAc,CAAC,kBAAkB,GAAG,CAAC,CAAC,CAAC;IACtF,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,WAAW,CAAC;IAEhD,oDAAoD;IACpD,MAAM,eAAe,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,cAAc,CAAC,eAAe,GAAG,CAAC,CAAC,CAAC;IACvF,IAAI,eAAe,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC/B,MAAM,KAAK,GAAG,eAAe,CAAC,MAAM,GAAG,UAAU,CAAC,MAAM,CAAC;QACzD,IAAI,KAAK,IAAI,IAAI,EAAE,CAAC;YAClB,WAAW,CAAC,IAAI,CAAC;gBACf,QAAQ,EAAE,gBAAgB;gBAC1B,MAAM,EAAE,eAAe;gBACvB,IAAI,EAAE,gCAAgC;gBACtC,WAAW,EAAE,gEAAgE,eAAe,CAAC,MAAM,OAAO,UAAU,CAAC,MAAM,eAAe;gBAC1I,UAAU,EAAE,eAAe,CAAC,eAAe,CAAC,MAAM,EAAE,UAAU,CAAC,MAAM,CAAC;gBACtE,QAAQ,EAAE,eAAe,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,YAAY,CAAC;aACjE,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,yGAAyG;IACzG,MAAM,gBAAgB,GAAG,WAAW,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,cAAc,CAAC,gBAAgB,GAAG,CAAC,CAAC,CAAC;IAC1F,IAAI,gBAAgB,CAAC,MAAM,IAAI,CAAC,EAAE,CAAC;QACjC,MAAM,KAAK,GAAG,gBAAgB,CAAC,MAAM,GAAG,UAAU,CAAC,MAAM,CAAC;QAC1D,IAAI,KAAK,IAAI,IAAI,EAAE,CAAC;YAClB,WAAW,CAAC,IAAI,CAAC;gBACf,QAAQ,EAAE,gBAAgB;gBAC1B,MAAM,EAAE,eAAe;gBACvB,IAAI,EAAE,2BAA2B;gBACjC,WAAW,EAAE,GAAG,gBAAgB,CAAC,MAAM,QAAQ,gBAAgB,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,WAAW,gBAAgB,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,uEAAuE;gBACjN,UAAU,EAAE,eAAe,CAAC,gBAAgB,CAAC,MAAM,EAAE,UAAU,CAAC,MAAM,CAAC;gBACvE,QAAQ,EAAE,gBAAgB,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,YAAY,CAAC;aAClE,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,OAAO,WAAW,CAAC;AACrB,CAAC,CAAC"}
|
|
@@ -1,47 +1,99 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
// src/detectors/error-handling.ts — Error handling pattern detector
|
|
2
|
+
// Detects: custom error classes, Result/Either patterns, error class hierarchies.
|
|
3
|
+
// Does NOT use try/catch density — per adversarial review, that's a false signal
|
|
4
|
+
// (mature codebases use middleware/Result types, not widespread try/catch).
|
|
5
|
+
import { buildConfidence, sourceParsedFiles } from "../convention-extractor.js";
|
|
6
|
+
// Libraries that provide typed error/result patterns
|
|
7
|
+
const RESULT_LIBRARIES = {
|
|
8
|
+
neverthrow: "neverthrow (Result<T, E>)",
|
|
9
|
+
"fp-ts": "fp-ts (Either<E, A>)",
|
|
10
|
+
"oxide.ts": "oxide.ts (Result<T, E>)",
|
|
11
|
+
effect: "Effect (typed errors via Effect<A, E, R>)",
|
|
12
|
+
"ts-results": "ts-results (Result<T, E>)",
|
|
13
|
+
"true-myth": "true-myth (Result<T, E>)",
|
|
14
|
+
};
|
|
15
|
+
// Names that suggest a custom Result/Either type
|
|
16
|
+
const RESULT_EXPORT_NAMES = new Set(["Result", "Ok", "Err", "Either", "Left", "Right", "Success", "Failure"]);
|
|
17
|
+
export const errorHandlingDetector = (files, tiers, _warnings, _context) => {
|
|
3
18
|
const conventions = [];
|
|
4
19
|
const sourceFiles = sourceParsedFiles(files, tiers);
|
|
5
20
|
if (sourceFiles.length === 0)
|
|
6
21
|
return conventions;
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
22
|
+
// 1. Custom error classes — exports with kind:"class" and name ending in Error/Exception
|
|
23
|
+
const errorClasses = [];
|
|
24
|
+
for (const file of sourceFiles) {
|
|
25
|
+
for (const exp of file.exports) {
|
|
26
|
+
if (exp.kind === "class" && (/Error$/.test(exp.name) || /Exception$/.test(exp.name))) {
|
|
27
|
+
errorClasses.push(exp.name);
|
|
28
|
+
}
|
|
14
29
|
}
|
|
15
|
-
if (f.contentSignals.hasErrorBoundary)
|
|
16
|
-
errorBoundaryCount++;
|
|
17
30
|
}
|
|
18
|
-
if (
|
|
31
|
+
if (errorClasses.length >= 2) {
|
|
32
|
+
// Check for hierarchy pattern: multiple error classes suggest a typed error hierarchy
|
|
33
|
+
const isHierarchy = errorClasses.length >= 3;
|
|
34
|
+
const name = isHierarchy ? "Typed error class hierarchy" : "Custom error classes";
|
|
35
|
+
const description = isHierarchy
|
|
36
|
+
? `Uses a hierarchy of ${errorClasses.length} typed error classes (${errorClasses.slice(0, 3).join(", ")}${errorClasses.length > 3 ? `, ...${errorClasses.length - 3} more` : ""})`
|
|
37
|
+
: `Defines custom error classes: ${errorClasses.join(", ")}`;
|
|
19
38
|
conventions.push({
|
|
20
39
|
category: "error-handling",
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
40
|
+
source: "errorHandling",
|
|
41
|
+
name,
|
|
42
|
+
description,
|
|
43
|
+
confidence: buildConfidence(errorClasses.length, errorClasses.length),
|
|
44
|
+
examples: errorClasses.slice(0, 5),
|
|
25
45
|
});
|
|
26
46
|
}
|
|
27
|
-
|
|
47
|
+
// 2. Result/Either pattern — check imports for known result-type libraries (count files, not imports)
|
|
48
|
+
const resultLibraryImports = new Map();
|
|
49
|
+
for (const file of sourceFiles) {
|
|
50
|
+
const fileLibs = new Set(); // dedupe per file
|
|
51
|
+
for (const imp of file.imports) {
|
|
52
|
+
if (imp.isTypeOnly)
|
|
53
|
+
continue;
|
|
54
|
+
for (const [pkg, label] of Object.entries(RESULT_LIBRARIES)) {
|
|
55
|
+
if (imp.moduleSpecifier === pkg || imp.moduleSpecifier.startsWith(`${pkg}/`)) {
|
|
56
|
+
fileLibs.add(label);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
for (const label of fileLibs) {
|
|
61
|
+
resultLibraryImports.set(label, (resultLibraryImports.get(label) ?? 0) + 1);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
if (resultLibraryImports.size > 0) {
|
|
65
|
+
const totalFiles = [...resultLibraryImports.values()].reduce((a, b) => a + b, 0);
|
|
66
|
+
const libs = [...resultLibraryImports.entries()].sort((a, b) => b[1] - a[1]);
|
|
28
67
|
conventions.push({
|
|
29
68
|
category: "error-handling",
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
69
|
+
source: "errorHandling",
|
|
70
|
+
name: "Result/Either pattern",
|
|
71
|
+
description: `Uses ${libs[0][0]} for typed error handling (${totalFiles} files)`,
|
|
72
|
+
confidence: buildConfidence(totalFiles, sourceFiles.length),
|
|
73
|
+
examples: libs.map(([lib, count]) => `${lib}: ${count} files`),
|
|
34
74
|
});
|
|
35
75
|
}
|
|
76
|
+
// 3. Custom Result types — exports named Result/Ok/Err/Either without a library
|
|
77
|
+
if (resultLibraryImports.size === 0) {
|
|
78
|
+
const resultExports = [];
|
|
79
|
+
for (const file of sourceFiles) {
|
|
80
|
+
for (const exp of file.exports) {
|
|
81
|
+
if (RESULT_EXPORT_NAMES.has(exp.name) && (exp.kind === "type" || exp.kind === "interface")) {
|
|
82
|
+
resultExports.push(exp.name);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
if (resultExports.length >= 2) {
|
|
87
|
+
conventions.push({
|
|
88
|
+
category: "error-handling",
|
|
89
|
+
source: "errorHandling",
|
|
90
|
+
name: "Custom Result type pattern",
|
|
91
|
+
description: `Defines custom Result types: ${[...new Set(resultExports)].join(", ")}`,
|
|
92
|
+
confidence: buildConfidence(resultExports.length, resultExports.length),
|
|
93
|
+
examples: [...new Set(resultExports)].slice(0, 5),
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
}
|
|
36
97
|
return conventions;
|
|
37
98
|
};
|
|
38
|
-
function conf(matched, total) {
|
|
39
|
-
const percentage = total > 0 ? Math.round((matched / total) * 100) : 0;
|
|
40
|
-
return {
|
|
41
|
-
matched,
|
|
42
|
-
total,
|
|
43
|
-
percentage,
|
|
44
|
-
description: `${matched} of ${total} (${percentage}%)`,
|
|
45
|
-
};
|
|
46
|
-
}
|
|
47
99
|
//# sourceMappingURL=error-handling.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"error-handling.js","sourceRoot":"","sources":["../../src/detectors/error-handling.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"error-handling.js","sourceRoot":"","sources":["../../src/detectors/error-handling.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
// src/detectors/state-management.ts — State management pattern detector
|
|
2
|
+
// Import-based detection for Redux, Zustand, Jotai, MobX, Recoil, Signals, Context API.
|
|
3
|
+
// Reports ALL detected libraries (dominant + supplementary) per adversarial review.
|
|
4
|
+
import { buildConfidence, sourceParsedFiles } from "../convention-extractor.js";
|
|
5
|
+
const STATE_LIBRARIES = {
|
|
6
|
+
redux: { label: "Redux", packages: ["@reduxjs/toolkit", "react-redux", "redux"] },
|
|
7
|
+
zustand: { label: "Zustand", packages: ["zustand"] },
|
|
8
|
+
jotai: { label: "Jotai", packages: ["jotai"] },
|
|
9
|
+
recoil: { label: "Recoil", packages: ["recoil"] },
|
|
10
|
+
mobx: { label: "MobX", packages: ["mobx", "mobx-react", "mobx-react-lite"] },
|
|
11
|
+
signals: { label: "Signals", packages: ["@preact/signals", "@preact/signals-react", "@angular/core"] },
|
|
12
|
+
valtio: { label: "Valtio", packages: ["valtio"] },
|
|
13
|
+
xstate: { label: "XState", packages: ["xstate", "@xstate/react"] },
|
|
14
|
+
};
|
|
15
|
+
export const stateManagementDetector = (files, tiers, _warnings, _context) => {
|
|
16
|
+
const conventions = [];
|
|
17
|
+
const sourceFiles = sourceParsedFiles(files, tiers);
|
|
18
|
+
if (sourceFiles.length === 0)
|
|
19
|
+
return conventions;
|
|
20
|
+
// Count files importing each state management library
|
|
21
|
+
const libFileCounts = new Map(); // library key → file count
|
|
22
|
+
for (const file of sourceFiles) {
|
|
23
|
+
const fileLibs = new Set(); // dedupe per file
|
|
24
|
+
for (const imp of file.imports) {
|
|
25
|
+
if (imp.isTypeOnly)
|
|
26
|
+
continue;
|
|
27
|
+
for (const [key, { packages }] of Object.entries(STATE_LIBRARIES)) {
|
|
28
|
+
if (packages.some((pkg) => imp.moduleSpecifier === pkg || imp.moduleSpecifier.startsWith(`${pkg}/`))) {
|
|
29
|
+
fileLibs.add(key);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
for (const key of fileLibs) {
|
|
34
|
+
libFileCounts.set(key, (libFileCounts.get(key) ?? 0) + 1);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
// Detect Context API usage: imports of createContext from react
|
|
38
|
+
let contextApiFiles = 0;
|
|
39
|
+
for (const file of sourceFiles) {
|
|
40
|
+
for (const imp of file.imports) {
|
|
41
|
+
if (imp.isTypeOnly)
|
|
42
|
+
continue;
|
|
43
|
+
if (imp.moduleSpecifier === "react" && imp.importedNames.includes("createContext")) {
|
|
44
|
+
contextApiFiles++;
|
|
45
|
+
break;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
if (contextApiFiles > 0) {
|
|
50
|
+
libFileCounts.set("context", contextApiFiles);
|
|
51
|
+
}
|
|
52
|
+
if (libFileCounts.size === 0)
|
|
53
|
+
return conventions;
|
|
54
|
+
// Sort by file count descending
|
|
55
|
+
const sorted = [...libFileCounts.entries()].sort((a, b) => b[1] - a[1]);
|
|
56
|
+
const totalFiles = sorted.reduce((sum, [, count]) => sum + count, 0);
|
|
57
|
+
// Build description showing all detected libraries with roles
|
|
58
|
+
const dominant = sorted[0];
|
|
59
|
+
const dominantLabel = dominant[0] === "context" ? "React Context API" : (STATE_LIBRARIES[dominant[0]]?.label ?? dominant[0]);
|
|
60
|
+
const parts = [];
|
|
61
|
+
for (const [key, count] of sorted) {
|
|
62
|
+
const label = key === "context" ? "React Context API" : (STATE_LIBRARIES[key]?.label ?? key);
|
|
63
|
+
parts.push(`${label} (${count} files)`);
|
|
64
|
+
}
|
|
65
|
+
conventions.push({
|
|
66
|
+
category: "state-management",
|
|
67
|
+
source: "stateManagement",
|
|
68
|
+
name: sorted.length === 1 ? `${dominantLabel} state management` : `${dominantLabel} + ${sorted.length - 1} more`,
|
|
69
|
+
description: sorted.length === 1 ? `Uses ${dominantLabel} for state management` : `State management: ${parts.join(", ")}`,
|
|
70
|
+
confidence: buildConfidence(totalFiles, sourceFiles.length),
|
|
71
|
+
examples: parts,
|
|
72
|
+
});
|
|
73
|
+
return conventions;
|
|
74
|
+
};
|
|
75
|
+
//# sourceMappingURL=state-management.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"state-management.js","sourceRoot":"","sources":["../../src/detectors/state-management.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { CallGraphEdge, CoChangeEdge, DependencyInsights, ExecutionFlow, PublicAPIEntry } from "./types.js";
|
|
2
|
+
type NodeId = string;
|
|
3
|
+
interface ScoredEntry {
|
|
4
|
+
nodeId: NodeId;
|
|
5
|
+
score: number;
|
|
6
|
+
}
|
|
7
|
+
export declare function scoreEntryPoints(callGraph: CallGraphEdge[], publicAPI: PublicAPIEntry[], dependencyInsights?: DependencyInsights): ScoredEntry[];
|
|
8
|
+
interface RawFlow {
|
|
9
|
+
nodeIds: NodeId[];
|
|
10
|
+
}
|
|
11
|
+
export declare function traceFlows(callGraph: CallGraphEdge[], entries: ScoredEntry[]): RawFlow[];
|
|
12
|
+
export declare function deduplicateFlows(candidates: RawFlow[], maxFlows: number): RawFlow[];
|
|
13
|
+
/**
|
|
14
|
+
* Enrich existing flows with co-change confidence scores.
|
|
15
|
+
* Called after git history is attached to the package analysis.
|
|
16
|
+
*/
|
|
17
|
+
export declare function enrichFlowConfidence(flows: ExecutionFlow[], coChangeEdges: CoChangeEdge[]): void;
|
|
18
|
+
/**
|
|
19
|
+
* Detect execution flows via forward BFS from scored entry points.
|
|
20
|
+
* Returns empty array if call graph is too sparse (<10 edges) or package is React-only.
|
|
21
|
+
*/
|
|
22
|
+
export declare function computeExecutionFlows(callGraph: CallGraphEdge[], publicAPI: PublicAPIEntry[], dependencyInsights?: DependencyInsights, coChangeEdges?: CoChangeEdge[]): ExecutionFlow[];
|
|
23
|
+
export {};
|