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.
Files changed (76) hide show
  1. package/README.md +123 -117
  2. package/dist/anti-pattern-detector.js +22 -0
  3. package/dist/anti-pattern-detector.js.map +1 -1
  4. package/dist/architecture-detector.js +33 -29
  5. package/dist/architecture-detector.js.map +1 -1
  6. package/dist/ast-parser.js +31 -3
  7. package/dist/ast-parser.js.map +1 -1
  8. package/dist/benchmark/scorer.js +3 -1
  9. package/dist/benchmark/scorer.js.map +1 -1
  10. package/dist/bin/autodocs-engine.js +7 -0
  11. package/dist/bin/autodocs-engine.js.map +1 -1
  12. package/dist/bin/serve.d.ts +1 -0
  13. package/dist/bin/serve.js +5 -1
  14. package/dist/bin/serve.js.map +1 -1
  15. package/dist/bin/setup-hooks.d.ts +1 -0
  16. package/dist/bin/setup-hooks.js +59 -0
  17. package/dist/bin/setup-hooks.js.map +1 -0
  18. package/dist/config.d.ts +1 -0
  19. package/dist/config.js +4 -0
  20. package/dist/config.js.map +1 -1
  21. package/dist/convention-extractor.js +10 -0
  22. package/dist/convention-extractor.js.map +1 -1
  23. package/dist/detectors/api-patterns.d.ts +2 -0
  24. package/dist/detectors/api-patterns.js +101 -0
  25. package/dist/detectors/api-patterns.js.map +1 -0
  26. package/dist/detectors/async-patterns.d.ts +2 -0
  27. package/dist/detectors/async-patterns.js +79 -0
  28. package/dist/detectors/async-patterns.js.map +1 -0
  29. package/dist/detectors/error-handling.js +82 -30
  30. package/dist/detectors/error-handling.js.map +1 -1
  31. package/dist/detectors/state-management.d.ts +2 -0
  32. package/dist/detectors/state-management.js +75 -0
  33. package/dist/detectors/state-management.js.map +1 -0
  34. package/dist/execution-flow.d.ts +23 -0
  35. package/dist/execution-flow.js +286 -0
  36. package/dist/execution-flow.js.map +1 -0
  37. package/dist/git-history.js +18 -7
  38. package/dist/git-history.js.map +1 -1
  39. package/dist/implicit-coupling.d.ts +7 -0
  40. package/dist/implicit-coupling.js +40 -0
  41. package/dist/implicit-coupling.js.map +1 -0
  42. package/dist/import-chain.js +12 -3
  43. package/dist/import-chain.js.map +1 -1
  44. package/dist/index.js +20 -1
  45. package/dist/index.js.map +1 -1
  46. package/dist/llm/serializer.js +8 -6
  47. package/dist/llm/serializer.js.map +1 -1
  48. package/dist/mcp/cache.d.ts +11 -1
  49. package/dist/mcp/cache.js +49 -7
  50. package/dist/mcp/cache.js.map +1 -1
  51. package/dist/mcp/queries.d.ts +19 -3
  52. package/dist/mcp/queries.js +342 -62
  53. package/dist/mcp/queries.js.map +1 -1
  54. package/dist/mcp/server.d.ts +1 -0
  55. package/dist/mcp/server.js +42 -1
  56. package/dist/mcp/server.js.map +1 -1
  57. package/dist/mcp/tools.d.ts +1 -0
  58. package/dist/mcp/tools.js +162 -20
  59. package/dist/mcp/tools.js.map +1 -1
  60. package/dist/output-validator.js +73 -0
  61. package/dist/output-validator.js.map +1 -1
  62. package/dist/pipeline.js +37 -0
  63. package/dist/pipeline.js.map +1 -1
  64. package/dist/symbol-graph.js +4 -0
  65. package/dist/symbol-graph.js.map +1 -1
  66. package/dist/type-enricher.d.ts +17 -0
  67. package/dist/type-enricher.js +127 -0
  68. package/dist/type-enricher.js.map +1 -0
  69. package/dist/type-resolver.d.ts +13 -0
  70. package/dist/type-resolver.js +75 -0
  71. package/dist/type-resolver.js.map +1 -0
  72. package/dist/types.d.ts +37 -4
  73. package/dist/types.js +7 -2
  74. package/dist/types.js.map +1 -1
  75. package/hooks/autodocs-hook.cjs +227 -0
  76. 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,2 @@
1
+ import type { ConventionDetector } from "../types.js";
2
+ export declare const asyncPatternsDetector: ConventionDetector;
@@ -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":"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"}
@@ -1,47 +1,99 @@
1
- import { sourceParsedFiles } from "../convention-extractor.js";
2
- export const errorHandlingDetector = (files, tiers, _warnings) => {
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
- let tryCatchTotal = 0;
8
- let filesWithTryCatch = 0;
9
- let errorBoundaryCount = 0;
10
- for (const f of sourceFiles) {
11
- if (f.contentSignals.tryCatchCount > 0) {
12
- tryCatchTotal += f.contentSignals.tryCatchCount;
13
- filesWithTryCatch++;
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 (tryCatchTotal > 0) {
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
- name: "Try-catch error handling",
22
- description: `Uses try-catch blocks for error handling`,
23
- confidence: conf(filesWithTryCatch, sourceFiles.length),
24
- examples: [`${tryCatchTotal} try-catch blocks in ${filesWithTryCatch} files`],
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
- if (errorBoundaryCount > 0) {
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
- name: "Error boundaries",
31
- description: `Uses React Error Boundaries for UI error handling`,
32
- confidence: conf(errorBoundaryCount, sourceFiles.length),
33
- examples: [`${errorBoundaryCount} files reference ErrorBoundary`],
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":"AACA,OAAO,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AAE/D,MAAM,CAAC,MAAM,qBAAqB,GAAuB,CAAC,KAAK,EAAE,KAAK,EAAE,SAAS,EAAE,EAAE;IACnF,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,IAAI,aAAa,GAAG,CAAC,CAAC;IACtB,IAAI,iBAAiB,GAAG,CAAC,CAAC;IAC1B,IAAI,kBAAkB,GAAG,CAAC,CAAC;IAE3B,KAAK,MAAM,CAAC,IAAI,WAAW,EAAE,CAAC;QAC5B,IAAI,CAAC,CAAC,cAAc,CAAC,aAAa,GAAG,CAAC,EAAE,CAAC;YACvC,aAAa,IAAI,CAAC,CAAC,cAAc,CAAC,aAAa,CAAC;YAChD,iBAAiB,EAAE,CAAC;QACtB,CAAC;QACD,IAAI,CAAC,CAAC,cAAc,CAAC,gBAAgB;YAAE,kBAAkB,EAAE,CAAC;IAC9D,CAAC;IAED,IAAI,aAAa,GAAG,CAAC,EAAE,CAAC;QACtB,WAAW,CAAC,IAAI,CAAC;YACf,QAAQ,EAAE,gBAAgB;YAC1B,IAAI,EAAE,0BAA0B;YAChC,WAAW,EAAE,0CAA0C;YACvD,UAAU,EAAE,IAAI,CAAC,iBAAiB,EAAE,WAAW,CAAC,MAAM,CAAC;YACvD,QAAQ,EAAE,CAAC,GAAG,aAAa,wBAAwB,iBAAiB,QAAQ,CAAC;SAC9E,CAAC,CAAC;IACL,CAAC;IAED,IAAI,kBAAkB,GAAG,CAAC,EAAE,CAAC;QAC3B,WAAW,CAAC,IAAI,CAAC;YACf,QAAQ,EAAE,gBAAgB;YAC1B,IAAI,EAAE,kBAAkB;YACxB,WAAW,EAAE,mDAAmD;YAChE,UAAU,EAAE,IAAI,CAAC,kBAAkB,EAAE,WAAW,CAAC,MAAM,CAAC;YACxD,QAAQ,EAAE,CAAC,GAAG,kBAAkB,gCAAgC,CAAC;SAClE,CAAC,CAAC;IACL,CAAC;IAED,OAAO,WAAW,CAAC;AACrB,CAAC,CAAC;AAEF,SAAS,IAAI,CAAC,OAAe,EAAE,KAAa;IAC1C,MAAM,UAAU,GAAG,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,OAAO,GAAG,KAAK,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACvE,OAAO;QACL,OAAO;QACP,KAAK;QACL,UAAU;QACV,WAAW,EAAE,GAAG,OAAO,OAAO,KAAK,KAAK,UAAU,IAAI;KACvD,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"error-handling.js","sourceRoot":"","sources":["../../src/detectors/error-handling.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import type { ConventionDetector } from "../types.js";
2
+ export declare const stateManagementDetector: ConventionDetector;
@@ -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":"AAAA,wEAAwE;AACxE,wFAAwF;AACxF,oFAAoF;AAEpF,OAAO,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AAGhF,MAAM,eAAe,GAA0D;IAC7E,KAAK,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAC,kBAAkB,EAAE,aAAa,EAAE,OAAO,CAAC,EAAE;IACjF,OAAO,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,CAAC,SAAS,CAAC,EAAE;IACpD,KAAK,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAC,OAAO,CAAC,EAAE;IAC9C,MAAM,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAC,QAAQ,CAAC,EAAE;IACjD,IAAI,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,MAAM,EAAE,YAAY,EAAE,iBAAiB,CAAC,EAAE;IAC5E,OAAO,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,CAAC,iBAAiB,EAAE,uBAAuB,EAAE,eAAe,CAAC,EAAE;IACtG,MAAM,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAC,QAAQ,CAAC,EAAE;IACjD,MAAM,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAC,QAAQ,EAAE,eAAe,CAAC,EAAE;CACnE,CAAC;AAEF,MAAM,CAAC,MAAM,uBAAuB,GAAuB,CAAC,KAAK,EAAE,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,EAAE;IAC/F,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,sDAAsD;IACtD,MAAM,aAAa,GAAG,IAAI,GAAG,EAAkB,CAAC,CAAC,2BAA2B;IAE5E,KAAK,MAAM,IAAI,IAAI,WAAW,EAAE,CAAC;QAC/B,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAU,CAAC,CAAC,kBAAkB;QACtD,KAAK,MAAM,GAAG,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YAC/B,IAAI,GAAG,CAAC,UAAU;gBAAE,SAAS;YAC7B,KAAK,MAAM,CAAC,GAAG,EAAE,EAAE,QAAQ,EAAE,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,eAAe,CAAC,EAAE,CAAC;gBAClE,IAAI,QAAQ,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,eAAe,KAAK,GAAG,IAAI,GAAG,CAAC,eAAe,CAAC,UAAU,CAAC,GAAG,GAAG,GAAG,CAAC,CAAC,EAAE,CAAC;oBACrG,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;gBACpB,CAAC;YACH,CAAC;QACH,CAAC;QACD,KAAK,MAAM,GAAG,IAAI,QAAQ,EAAE,CAAC;YAC3B,aAAa,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,aAAa,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;QAC5D,CAAC;IACH,CAAC;IAED,gEAAgE;IAChE,IAAI,eAAe,GAAG,CAAC,CAAC;IACxB,KAAK,MAAM,IAAI,IAAI,WAAW,EAAE,CAAC;QAC/B,KAAK,MAAM,GAAG,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YAC/B,IAAI,GAAG,CAAC,UAAU;gBAAE,SAAS;YAC7B,IAAI,GAAG,CAAC,eAAe,KAAK,OAAO,IAAI,GAAG,CAAC,aAAa,CAAC,QAAQ,CAAC,eAAe,CAAC,EAAE,CAAC;gBACnF,eAAe,EAAE,CAAC;gBAClB,MAAM;YACR,CAAC;QACH,CAAC;IACH,CAAC;IACD,IAAI,eAAe,GAAG,CAAC,EAAE,CAAC;QACxB,aAAa,CAAC,GAAG,CAAC,SAAS,EAAE,eAAe,CAAC,CAAC;IAChD,CAAC;IAED,IAAI,aAAa,CAAC,IAAI,KAAK,CAAC;QAAE,OAAO,WAAW,CAAC;IAEjD,gCAAgC;IAChC,MAAM,MAAM,GAAG,CAAC,GAAG,aAAa,CAAC,OAAO,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACxE,MAAM,UAAU,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,KAAK,EAAE,CAAC,CAAC,CAAC;IAErE,8DAA8D;IAC9D,MAAM,QAAQ,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;IAC3B,MAAM,aAAa,GACjB,QAAQ,CAAC,CAAC,CAAC,KAAK,SAAS,CAAC,CAAC,CAAC,mBAAmB,CAAC,CAAC,CAAC,CAAC,eAAe,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,EAAE,KAAK,IAAI,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC;IAEzG,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,EAAE,CAAC;QAClC,MAAM,KAAK,GAAG,GAAG,KAAK,SAAS,CAAC,CAAC,CAAC,mBAAmB,CAAC,CAAC,CAAC,CAAC,eAAe,CAAC,GAAG,CAAC,EAAE,KAAK,IAAI,GAAG,CAAC,CAAC;QAC7F,KAAK,CAAC,IAAI,CAAC,GAAG,KAAK,KAAK,KAAK,SAAS,CAAC,CAAC;IAC1C,CAAC;IAED,WAAW,CAAC,IAAI,CAAC;QACf,QAAQ,EAAE,kBAAkB;QAC5B,MAAM,EAAE,iBAAiB;QACzB,IAAI,EAAE,MAAM,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,aAAa,mBAAmB,CAAC,CAAC,CAAC,GAAG,aAAa,MAAM,MAAM,CAAC,MAAM,GAAG,CAAC,OAAO;QAChH,WAAW,EACT,MAAM,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,QAAQ,aAAa,uBAAuB,CAAC,CAAC,CAAC,qBAAqB,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE;QAC9G,UAAU,EAAE,eAAe,CAAC,UAAU,EAAE,WAAW,CAAC,MAAM,CAAC;QAC3D,QAAQ,EAAE,KAAK;KAChB,CAAC,CAAC;IAEH,OAAO,WAAW,CAAC;AACrB,CAAC,CAAC"}
@@ -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 {};