code-auditor-mcp 3.4.11 → 3.4.13

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 (254) hide show
  1. package/.claude-plugin/marketplace.json +2 -2
  2. package/CHANGELOG.md +119 -1
  3. package/dist/analyzers/__verify_self_probe.d.ts +2 -0
  4. package/dist/analyzers/__verify_self_probe.d.ts.map +1 -0
  5. package/dist/analyzers/__verify_self_probe.js +5 -0
  6. package/dist/analyzers/__verify_self_probe.js.map +1 -0
  7. package/dist/analyzers/analyzerUtils.d.ts +34 -1
  8. package/dist/analyzers/analyzerUtils.d.ts.map +1 -1
  9. package/dist/analyzers/analyzerUtils.js +61 -22
  10. package/dist/analyzers/analyzerUtils.js.map +1 -1
  11. package/dist/analyzers/cross-language/APIContractAnalyzer.d.ts +18 -23
  12. package/dist/analyzers/cross-language/APIContractAnalyzer.d.ts.map +1 -1
  13. package/dist/analyzers/cross-language/APIContractAnalyzer.js +198 -186
  14. package/dist/analyzers/cross-language/APIContractAnalyzer.js.map +1 -1
  15. package/dist/analyzers/cross-language/DependencyGraphBuilder.d.ts +78 -55
  16. package/dist/analyzers/cross-language/DependencyGraphBuilder.d.ts.map +1 -1
  17. package/dist/analyzers/cross-language/DependencyGraphBuilder.js +244 -243
  18. package/dist/analyzers/cross-language/DependencyGraphBuilder.js.map +1 -1
  19. package/dist/analyzers/cross-language/SchemaValidator.d.ts +27 -37
  20. package/dist/analyzers/cross-language/SchemaValidator.d.ts.map +1 -1
  21. package/dist/analyzers/cross-language/SchemaValidator.js +314 -275
  22. package/dist/analyzers/cross-language/SchemaValidator.js.map +1 -1
  23. package/dist/analyzers/crossDomain/CrossDomainAnalyzer.d.ts +17 -46
  24. package/dist/analyzers/crossDomain/CrossDomainAnalyzer.d.ts.map +1 -1
  25. package/dist/analyzers/crossDomain/CrossDomainAnalyzer.js +620 -523
  26. package/dist/analyzers/crossDomain/CrossDomainAnalyzer.js.map +1 -1
  27. package/dist/analyzers/documentationAnalyzer.d.ts +7 -2
  28. package/dist/analyzers/documentationAnalyzer.d.ts.map +1 -1
  29. package/dist/analyzers/documentationAnalyzer.js +259 -198
  30. package/dist/analyzers/documentationAnalyzer.js.map +1 -1
  31. package/dist/analyzers/invariantsAnalyzer.d.ts.map +1 -1
  32. package/dist/analyzers/invariantsAnalyzer.js +59 -46
  33. package/dist/analyzers/invariantsAnalyzer.js.map +1 -1
  34. package/dist/analyzers/orm/adapterRegistry.d.ts +10 -0
  35. package/dist/analyzers/orm/adapterRegistry.d.ts.map +1 -1
  36. package/dist/analyzers/orm/adapterRegistry.js +10 -0
  37. package/dist/analyzers/orm/adapterRegistry.js.map +1 -1
  38. package/dist/analyzers/orm/drizzleAdapter.d.ts +22 -0
  39. package/dist/analyzers/orm/drizzleAdapter.d.ts.map +1 -1
  40. package/dist/analyzers/orm/drizzleAdapter.js +42 -44
  41. package/dist/analyzers/orm/drizzleAdapter.js.map +1 -1
  42. package/dist/analyzers/orm/prismaAdapter.d.ts +30 -0
  43. package/dist/analyzers/orm/prismaAdapter.d.ts.map +1 -1
  44. package/dist/analyzers/orm/prismaAdapter.js +30 -0
  45. package/dist/analyzers/orm/prismaAdapter.js.map +1 -1
  46. package/dist/analyzers/provenance.d.ts +72 -2
  47. package/dist/analyzers/provenance.d.ts.map +1 -1
  48. package/dist/analyzers/provenance.js +462 -338
  49. package/dist/analyzers/provenance.js.map +1 -1
  50. package/dist/analyzers/reactAnalyzer.d.ts +11 -0
  51. package/dist/analyzers/reactAnalyzer.d.ts.map +1 -1
  52. package/dist/analyzers/reactAnalyzer.js +188 -125
  53. package/dist/analyzers/reactAnalyzer.js.map +1 -1
  54. package/dist/analyzers/ruleRegistry.d.ts +118 -1
  55. package/dist/analyzers/ruleRegistry.d.ts.map +1 -1
  56. package/dist/analyzers/ruleRegistry.js +1717 -104
  57. package/dist/analyzers/ruleRegistry.js.map +1 -1
  58. package/dist/analyzers/ruleTiming.d.ts +54 -0
  59. package/dist/analyzers/ruleTiming.d.ts.map +1 -0
  60. package/dist/analyzers/ruleTiming.js +71 -0
  61. package/dist/analyzers/ruleTiming.js.map +1 -0
  62. package/dist/analyzers/universal/UniversalConventionsAnalyzer.d.ts +13 -0
  63. package/dist/analyzers/universal/UniversalConventionsAnalyzer.d.ts.map +1 -1
  64. package/dist/analyzers/universal/UniversalConventionsAnalyzer.js +362 -283
  65. package/dist/analyzers/universal/UniversalConventionsAnalyzer.js.map +1 -1
  66. package/dist/analyzers/universal/UniversalDRYAnalyzer.d.ts +26 -68
  67. package/dist/analyzers/universal/UniversalDRYAnalyzer.d.ts.map +1 -1
  68. package/dist/analyzers/universal/UniversalDRYAnalyzer.js +430 -391
  69. package/dist/analyzers/universal/UniversalDRYAnalyzer.js.map +1 -1
  70. package/dist/analyzers/universal/UniversalDataAccessAnalyzer.d.ts +24 -186
  71. package/dist/analyzers/universal/UniversalDataAccessAnalyzer.d.ts.map +1 -1
  72. package/dist/analyzers/universal/UniversalDataAccessAnalyzer.js +1081 -1061
  73. package/dist/analyzers/universal/UniversalDataAccessAnalyzer.js.map +1 -1
  74. package/dist/analyzers/universal/UniversalDocumentationAnalyzer.d.ts +3 -49
  75. package/dist/analyzers/universal/UniversalDocumentationAnalyzer.d.ts.map +1 -1
  76. package/dist/analyzers/universal/UniversalDocumentationAnalyzer.js +414 -340
  77. package/dist/analyzers/universal/UniversalDocumentationAnalyzer.js.map +1 -1
  78. package/dist/analyzers/universal/UniversalSOLIDAnalyzer.d.ts +48 -5
  79. package/dist/analyzers/universal/UniversalSOLIDAnalyzer.d.ts.map +1 -1
  80. package/dist/analyzers/universal/UniversalSOLIDAnalyzer.js +262 -134
  81. package/dist/analyzers/universal/UniversalSOLIDAnalyzer.js.map +1 -1
  82. package/dist/analyzers/universal/UniversalSchemaAnalyzer.d.ts +23 -212
  83. package/dist/analyzers/universal/UniversalSchemaAnalyzer.d.ts.map +1 -1
  84. package/dist/analyzers/universal/UniversalSchemaAnalyzer.js +139 -1518
  85. package/dist/analyzers/universal/UniversalSchemaAnalyzer.js.map +1 -1
  86. package/dist/analyzers/universal/UniversalStylesAnalyzer.d.ts +78 -79
  87. package/dist/analyzers/universal/UniversalStylesAnalyzer.d.ts.map +1 -1
  88. package/dist/analyzers/universal/UniversalStylesAnalyzer.js +613 -472
  89. package/dist/analyzers/universal/UniversalStylesAnalyzer.js.map +1 -1
  90. package/dist/analyzers/universal/schema/codeAnalysis.d.ts +307 -0
  91. package/dist/analyzers/universal/schema/codeAnalysis.d.ts.map +1 -0
  92. package/dist/analyzers/universal/schema/codeAnalysis.js +965 -0
  93. package/dist/analyzers/universal/schema/codeAnalysis.js.map +1 -0
  94. package/dist/analyzers/universal/schema/config.d.ts +31 -0
  95. package/dist/analyzers/universal/schema/config.d.ts.map +1 -0
  96. package/dist/analyzers/universal/schema/config.js +57 -0
  97. package/dist/analyzers/universal/schema/config.js.map +1 -0
  98. package/dist/analyzers/universal/schema/discovery.d.ts +167 -0
  99. package/dist/analyzers/universal/schema/discovery.d.ts.map +1 -0
  100. package/dist/analyzers/universal/schema/discovery.js +616 -0
  101. package/dist/analyzers/universal/schema/discovery.js.map +1 -0
  102. package/dist/analyzers/universal/schema/jsonSchema.d.ts +27 -0
  103. package/dist/analyzers/universal/schema/jsonSchema.d.ts.map +1 -0
  104. package/dist/analyzers/universal/schema/jsonSchema.js +368 -0
  105. package/dist/analyzers/universal/schema/jsonSchema.js.map +1 -0
  106. package/dist/analyzers/universal/schema/migrations.d.ts +90 -0
  107. package/dist/analyzers/universal/schema/migrations.d.ts.map +1 -0
  108. package/dist/analyzers/universal/schema/migrations.js +238 -0
  109. package/dist/analyzers/universal/schema/migrations.js.map +1 -0
  110. package/dist/analyzers/universal/schema/types.d.ts +131 -0
  111. package/dist/analyzers/universal/schema/types.d.ts.map +1 -0
  112. package/dist/analyzers/universal/schema/types.js +10 -0
  113. package/dist/analyzers/universal/schema/types.js.map +1 -0
  114. package/dist/analyzers/universal/schema/violations.d.ts +44 -0
  115. package/dist/analyzers/universal/schema/violations.d.ts.map +1 -0
  116. package/dist/analyzers/universal/schema/violations.js +54 -0
  117. package/dist/analyzers/universal/schema/violations.js.map +1 -0
  118. package/dist/auditRunner.d.ts.map +1 -1
  119. package/dist/auditRunner.js +101 -39
  120. package/dist/auditRunner.js.map +1 -1
  121. package/dist/cli.js +348 -40
  122. package/dist/cli.js.map +1 -1
  123. package/dist/config/configLoader.d.ts.map +1 -1
  124. package/dist/config/configLoader.js +9 -7
  125. package/dist/config/configLoader.js.map +1 -1
  126. package/dist/config/defaults.d.ts +12 -3
  127. package/dist/config/defaults.d.ts.map +1 -1
  128. package/dist/config/defaults.js +15 -5
  129. package/dist/config/defaults.js.map +1 -1
  130. package/dist/config/effectiveConfig.d.ts +75 -0
  131. package/dist/config/effectiveConfig.d.ts.map +1 -0
  132. package/dist/config/effectiveConfig.js +213 -0
  133. package/dist/config/effectiveConfig.js.map +1 -0
  134. package/dist/config/pathProfiles.d.ts +7 -4
  135. package/dist/config/pathProfiles.d.ts.map +1 -1
  136. package/dist/config/pathProfiles.js +8 -10
  137. package/dist/config/pathProfiles.js.map +1 -1
  138. package/dist/config/thresholdRationales.d.ts +45 -0
  139. package/dist/config/thresholdRationales.d.ts.map +1 -0
  140. package/dist/config/thresholdRationales.js +67 -0
  141. package/dist/config/thresholdRationales.js.map +1 -0
  142. package/dist/conventions/conventionMiner.d.ts.map +1 -1
  143. package/dist/conventions/conventionMiner.js +6 -2
  144. package/dist/conventions/conventionMiner.js.map +1 -1
  145. package/dist/enforcement/diffGate.d.ts +55 -0
  146. package/dist/enforcement/diffGate.d.ts.map +1 -0
  147. package/dist/enforcement/diffGate.js +136 -0
  148. package/dist/enforcement/diffGate.js.map +1 -0
  149. package/dist/enforcement/gate.d.ts +36 -0
  150. package/dist/enforcement/gate.d.ts.map +1 -0
  151. package/dist/enforcement/gate.js +72 -0
  152. package/dist/enforcement/gate.js.map +1 -0
  153. package/dist/enforcement/suppressions.d.ts +41 -0
  154. package/dist/enforcement/suppressions.d.ts.map +1 -0
  155. package/dist/enforcement/suppressions.js +170 -0
  156. package/dist/enforcement/suppressions.js.map +1 -0
  157. package/dist/fingerprint.d.ts.map +1 -1
  158. package/dist/fingerprint.js +4 -1
  159. package/dist/fingerprint.js.map +1 -1
  160. package/dist/grammars/tree-sitter-scss.wasm +0 -0
  161. package/dist/invariants/ruleEngine.d.ts +3 -4
  162. package/dist/invariants/ruleEngine.d.ts.map +1 -1
  163. package/dist/invariants/ruleEngine.js +2 -2
  164. package/dist/invariants/ruleEngine.js.map +1 -1
  165. package/dist/languages/LanguageOrchestrator.d.ts +14 -0
  166. package/dist/languages/LanguageOrchestrator.d.ts.map +1 -1
  167. package/dist/languages/LanguageOrchestrator.js +93 -55
  168. package/dist/languages/LanguageOrchestrator.js.map +1 -1
  169. package/dist/languages/LanguageRegistry.d.ts +8 -0
  170. package/dist/languages/LanguageRegistry.d.ts.map +1 -1
  171. package/dist/languages/LanguageRegistry.js +8 -0
  172. package/dist/languages/LanguageRegistry.js.map +1 -1
  173. package/dist/languages/RuntimeManager.d.ts +82 -60
  174. package/dist/languages/RuntimeManager.d.ts.map +1 -1
  175. package/dist/languages/RuntimeManager.js +404 -349
  176. package/dist/languages/RuntimeManager.js.map +1 -1
  177. package/dist/languages/UniversalAnalyzer.d.ts +51 -6
  178. package/dist/languages/UniversalAnalyzer.d.ts.map +1 -1
  179. package/dist/languages/UniversalAnalyzer.js +127 -88
  180. package/dist/languages/UniversalAnalyzer.js.map +1 -1
  181. package/dist/languages/adapterBridge.d.ts +36 -0
  182. package/dist/languages/adapterBridge.d.ts.map +1 -1
  183. package/dist/languages/adapterBridge.js +89 -35
  184. package/dist/languages/adapterBridge.js.map +1 -1
  185. package/dist/languages/go/GoAdapter.d.ts +66 -23
  186. package/dist/languages/go/GoAdapter.d.ts.map +1 -1
  187. package/dist/languages/go/GoAdapter.js +553 -474
  188. package/dist/languages/go/GoAdapter.js.map +1 -1
  189. package/dist/languages/tree-sitter/TreeSitterCssAdapter.d.ts +38 -9
  190. package/dist/languages/tree-sitter/TreeSitterCssAdapter.d.ts.map +1 -1
  191. package/dist/languages/tree-sitter/TreeSitterCssAdapter.js +111 -74
  192. package/dist/languages/tree-sitter/TreeSitterCssAdapter.js.map +1 -1
  193. package/dist/languages/tree-sitter/converter.d.ts +18 -0
  194. package/dist/languages/tree-sitter/converter.d.ts.map +1 -1
  195. package/dist/languages/tree-sitter/converter.js +18 -0
  196. package/dist/languages/tree-sitter/converter.js.map +1 -1
  197. package/dist/languages/tree-sitter/parser.d.ts +39 -2
  198. package/dist/languages/tree-sitter/parser.d.ts.map +1 -1
  199. package/dist/languages/tree-sitter/parser.js +95 -12
  200. package/dist/languages/tree-sitter/parser.js.map +1 -1
  201. package/dist/languages/types.d.ts +67 -2
  202. package/dist/languages/types.d.ts.map +1 -1
  203. package/dist/languages/typescript/TreeSitterTypeScriptAdapter.d.ts +253 -62
  204. package/dist/languages/typescript/TreeSitterTypeScriptAdapter.d.ts.map +1 -1
  205. package/dist/languages/typescript/TreeSitterTypeScriptAdapter.js +1461 -791
  206. package/dist/languages/typescript/TreeSitterTypeScriptAdapter.js.map +1 -1
  207. package/dist/mcp-tools/whitelistTools.d.ts +1 -1
  208. package/dist/mcp-tools-shared.d.ts.map +1 -1
  209. package/dist/mcp-tools-shared.js +1 -0
  210. package/dist/mcp-tools-shared.js.map +1 -1
  211. package/dist/mcpAuditJobs.d.ts.map +1 -1
  212. package/dist/mcpAuditJobs.js +1 -0
  213. package/dist/mcpAuditJobs.js.map +1 -1
  214. package/dist/pipeline.d.ts +30 -1
  215. package/dist/pipeline.d.ts.map +1 -1
  216. package/dist/pipeline.js +527 -203
  217. package/dist/pipeline.js.map +1 -1
  218. package/dist/pipelineAdapters.d.ts +5 -12
  219. package/dist/pipelineAdapters.d.ts.map +1 -1
  220. package/dist/pipelineAdapters.js +175 -100
  221. package/dist/pipelineAdapters.js.map +1 -1
  222. package/dist/presets/presets.d.ts +54 -0
  223. package/dist/presets/presets.d.ts.map +1 -0
  224. package/dist/presets/presets.js +181 -0
  225. package/dist/presets/presets.js.map +1 -0
  226. package/dist/reporting/sarifReportGenerator.d.ts.map +1 -1
  227. package/dist/reporting/sarifReportGenerator.js +0 -2
  228. package/dist/reporting/sarifReportGenerator.js.map +1 -1
  229. package/dist/ruleAliases.d.ts +67 -0
  230. package/dist/ruleAliases.d.ts.map +1 -0
  231. package/dist/ruleAliases.js +79 -0
  232. package/dist/ruleAliases.js.map +1 -0
  233. package/dist/styles/cssAstExtractor.d.ts +20 -5
  234. package/dist/styles/cssAstExtractor.d.ts.map +1 -1
  235. package/dist/styles/cssAstExtractor.js +247 -9
  236. package/dist/styles/cssAstExtractor.js.map +1 -1
  237. package/dist/styles/styleIndexer.js +13 -4
  238. package/dist/styles/styleIndexer.js.map +1 -1
  239. package/dist/styles/tailwindConfigLoader.d.ts +8 -6
  240. package/dist/styles/tailwindConfigLoader.d.ts.map +1 -1
  241. package/dist/styles/tailwindConfigLoader.js +61 -15
  242. package/dist/styles/tailwindConfigLoader.js.map +1 -1
  243. package/dist/styles/tailwindUtilityExpander.d.ts.map +1 -1
  244. package/dist/styles/tailwindUtilityExpander.js +13 -1
  245. package/dist/styles/tailwindUtilityExpander.js.map +1 -1
  246. package/dist/types.d.ts +201 -1
  247. package/dist/types.d.ts.map +1 -1
  248. package/dist/types.js +8 -0
  249. package/dist/types.js.map +1 -1
  250. package/package.json +7 -4
  251. package/plugin/.claude-plugin/plugin.json +1 -1
  252. package/plugin/hooks/hooks.json +1 -1
  253. package/plugin/scripts/hook-audit.sh +25 -7
  254. package/plugin/skills/code-auditor/SKILL.md +5 -5
@@ -21,7 +21,9 @@ import { RULE_REGISTRY } from './analyzers/ruleRegistry.js';
21
21
  import { walkAST, isExported, getLineAndColumn, calculateComplexity, getFunctionBody, } from './languages/adapterBridge.js';
22
22
  import { buildImportMap, extractFunctionCalls, } from './utils/dependencyExtractor.js';
23
23
  import { isReactComponent, detectComponentType, getComponentName, } from './utils/reactDetection.js';
24
- import { ALL_EXTENSIONS } from './utils/fileDiscovery.js';
24
+ import { findTableReferences, checkNamingConventions, checkQueryPatterns, checkSQLInjection, getNearestTableSuggestions, } from './analyzers/universal/schema/codeAnalysis.js';
25
+ import { passesFileGate, extractTablesFromRegistry, } from './analyzers/universal/schema/discovery.js';
26
+ import { applyMigrationOps } from './analyzers/universal/schema/migrations.js';
25
27
  // ── Rule ID helpers ──────────────────────────────────────────────────────────
26
28
  function getRuleIdsFor(name) {
27
29
  return Object.entries(RULE_REGISTRY)
@@ -37,29 +39,6 @@ function lazySingleton(loader) {
37
39
  return promise;
38
40
  };
39
41
  }
40
- // ── File-sources infrastructure visitor ──────────────────────────────────────
41
- /**
42
- * Infrastructure visitor that records every file's source code so Stage 3
43
- * reducers can access it without calling readFileSync(). Always registered;
44
- * declares all known extensions so it covers both parsed and raw tuples.
45
- */
46
- export function createFileSourcesVisitor() {
47
- return {
48
- name: 'file-sources',
49
- stage: 'visitor',
50
- extensions: ALL_EXTENSIONS,
51
- getRuleIds: () => [],
52
- async visit(_ast, _adapter, context, sourceCode) {
53
- return {
54
- violations: [],
55
- facts: { [context.filePath]: sourceCode },
56
- };
57
- },
58
- defaultConfig: {},
59
- description: 'Records every file source for Stage 3 reducers (infrastructure)',
60
- category: 'infrastructure',
61
- };
62
- }
63
42
  // ── SOLID visitor ────────────────────────────────────────────────────────────
64
43
  export function createSolidVisitor() {
65
44
  const getAnalyzer = lazySingleton(() => import('./analyzers/universal/UniversalSOLIDAnalyzer.js').then((m) => new m.UniversalSOLIDAnalyzer()));
@@ -521,16 +500,14 @@ export function createReactVisitor() {
521
500
  // ── Styles CSS visitor (Spec 26 Phase 2) ───────────────────────────────────────
522
501
  /**
523
502
  * Styles CSS visitor — extracts declarations, tokens, and class usage from
524
- * tree-sitter-css parsed ASTs. Replaces the regex-based CSS extraction in
525
- * styleExtractor.ts for .css files only.
526
- *
527
- * tree-sitter-css is NOT an SCSS grammar — .scss files stay on the regex path.
503
+ * tree-sitter-css and tree-sitter-scss parsed ASTs. Replaces the regex-based
504
+ * CSS/SCSS extraction in styleExtractor.ts for .css and .scss files.
528
505
  */
529
506
  export function createStylesCssVisitor() {
530
507
  return {
531
508
  name: 'styles-css',
532
509
  stage: 'visitor',
533
- extensions: ['.css'],
510
+ extensions: ['.css', '.scss'],
534
511
  getRuleIds: () => [],
535
512
  async visit(ast, adapter, context, sourceCode) {
536
513
  // Lazy-load to avoid circular dependency issues at module load time
@@ -550,7 +527,7 @@ export function createStylesCssVisitor() {
550
527
  };
551
528
  },
552
529
  defaultConfig: {},
553
- description: 'Extracts CSS declarations from tree-sitter-css ASTs (.css only)',
530
+ description: 'Extracts CSS declarations from tree-sitter ASTs (.css and .scss)',
554
531
  category: 'style',
555
532
  };
556
533
  }
@@ -635,11 +612,6 @@ export function createConventionsReducer() {
635
612
  try {
636
613
  const { UniversalConventionsAnalyzer } = await import('./analyzers/universal/UniversalConventionsAnalyzer.js');
637
614
  const analyzer = new UniversalConventionsAnalyzer();
638
- // Extract per-file source map from the file-sources infrastructure visitor
639
- const fileSources = _allFacts['file-sources'];
640
- const sourceMap = fileSources
641
- ? new Map(Object.entries(fileSources))
642
- : undefined;
643
615
  // B2: Build exports map from function-index visitor facts (AST-extracted)
644
616
  const functionIndexFacts = _allFacts['function-index'];
645
617
  const exportsMap = functionIndexFacts
@@ -647,7 +619,7 @@ export function createConventionsReducer() {
647
619
  .filter(([, data]) => data?.exports?.length)
648
620
  .map(([filePath, data]) => [filePath, data.exports]))
649
621
  : undefined;
650
- const config = { ...context.config, indexHandle: context.indexHandle, projectRoot: context.projectRoot, sourceMap, exportsMap };
622
+ const config = { ...context.config, indexHandle: context.indexHandle, projectRoot: context.projectRoot, readSource: context.readSource, exportsMap };
651
623
  const result = await analyzer.analyze([], config);
652
624
  const factsConsumed = context.indexHandle.count('conventions');
653
625
  return { violations: result.violations ?? [], facts: {}, factsConsumed };
@@ -717,17 +689,22 @@ export function createInvariantsReducer() {
717
689
  getRuleIds: () => getRuleIdsFor('invariants'),
718
690
  async reduce(_allFacts, context) {
719
691
  try {
692
+ // Auto-disable when no rules are configured — signal notRun to the
693
+ // pipeline so coverage reports invariants rules as notApplicable with
694
+ // an accurate reason (Spec 05 R3.1, Spec 27 criterion 5).
695
+ const rules = context.config.rules;
696
+ if (!rules || (Array.isArray(rules) && rules.length === 0)) {
697
+ return {
698
+ violations: [],
699
+ facts: {},
700
+ notRunReason: 'invariants disabled — no rules configured',
701
+ };
702
+ }
720
703
  const { analyzeInvariants } = await import('./analyzers/invariantsAnalyzer.js');
721
704
  // Full file list is in _infra.files (merged into context.config via pipeline)
722
705
  const files = context.config.files ?? [];
723
- // Extract per-file source map from the file-sources infrastructure visitor
724
- const fileSources = _allFacts['file-sources'];
725
- const sourceMap = fileSources
726
- ? new Map(Object.entries(fileSources))
727
- : undefined;
728
- const knownFiles = sourceMap
729
- ? new Set(sourceMap.keys())
730
- : undefined;
706
+ // Absolute paths format-equivalent to the old sourceMap.keys().
707
+ const knownFiles = new Set(files);
731
708
  // B1: Build fileData from the function-index visitor's AST-extracted
732
709
  // imports/exports — replaces regex-based extractImports/extractExportedSymbols
733
710
  const functionIndexFacts = _allFacts['function-index'];
@@ -757,7 +734,7 @@ export function createInvariantsReducer() {
757
734
  }
758
735
  }
759
736
  }
760
- const result = await analyzeInvariants(files, { ...context.config, sourceMap, knownFiles, fileData }, { projectRoot: context.projectRoot, indexHandle: context.indexHandle });
737
+ const result = await analyzeInvariants(files, { ...context.config, readSource: context.readSource, knownFiles, fileData }, { projectRoot: context.projectRoot, indexHandle: context.indexHandle });
761
738
  return {
762
739
  violations: result.violations ?? [],
763
740
  facts: {},
@@ -783,23 +760,32 @@ async function _getProvenanceModule() {
783
760
  return _provenanceModule;
784
761
  }
785
762
  /**
786
- * Schema SQL visitor (.sql files) — extracts raw DDL source for the Stage 3
787
- * reducer to replay into the known-tables catalog.
763
+ * Schema SQL visitor (.sql files) — extracts ordered DDL operations for the
764
+ * Stage 3 reducer to replay into the known-tables catalog. Emits the parsed
765
+ * ops, not raw source, so the reducer retains only state transitions.
788
766
  */
789
767
  export function createSchemaSqlVisitor() {
768
+ const getMigrationExtractor = lazySingleton(() => import('./analyzers/universal/UniversalSchemaAnalyzer.js'));
790
769
  return {
791
770
  name: 'schema-sql',
792
771
  stage: 'visitor',
793
772
  extensions: ['.sql'],
794
773
  getRuleIds: () => [],
795
774
  async visit(_ast, _adapter, context, sourceCode) {
775
+ const { extractMigrationOpsFromFile } = await getMigrationExtractor();
776
+ const { ops, skipped, bytes } = await extractMigrationOpsFromFile(context.filePath, sourceCode);
796
777
  return {
797
778
  violations: [],
798
- facts: { [context.filePath]: { ddlSource: sourceCode } },
779
+ facts: {
780
+ [context.filePath]: {
781
+ ddlOps: ops,
782
+ ...(skipped && { skipped: true, bytes }),
783
+ },
784
+ },
799
785
  };
800
786
  },
801
787
  defaultConfig: {},
802
- description: 'Captures SQL DDL source for known-table catalog',
788
+ description: 'Extracts SQL DDL operations for known-table catalog',
803
789
  category: 'database',
804
790
  };
805
791
  }
@@ -811,6 +797,7 @@ export function createSchemaCodeVisitor() {
811
797
  const getAnalyzer = lazySingleton(() => import('./analyzers/universal/UniversalSchemaAnalyzer.js').then((m) => ({
812
798
  analyzer: new m.UniversalSchemaAnalyzer(),
813
799
  defaults: m.DEFAULT_SCHEMA_CONFIG,
800
+ parseMigrationOps: m.parseMigrationOps,
814
801
  })));
815
802
  return {
816
803
  name: 'schema-code',
@@ -818,16 +805,34 @@ export function createSchemaCodeVisitor() {
818
805
  extensions: ['.ts', '.tsx', '.js', '.jsx'],
819
806
  getRuleIds: () => getRuleIdsFor('schema'),
820
807
  async visit(ast, adapter, context, sourceCode) {
821
- const { analyzer: a, defaults } = await getAnalyzer();
808
+ const { analyzer: a, defaults, parseMigrationOps } = await getAnalyzer();
822
809
  const pm = await _getProvenanceModule();
823
810
  const violations = [];
824
811
  const indexFacts = [];
825
- // Extract ORM table names (Drizzle) from source
812
+ // Pipeline config for this analyzer (moved before table extraction, needed
813
+ // by the table-source registry and provenance context).
814
+ const schemaConfig = (context.config ?? {});
815
+ // Spec 29 R2: Extract ORM table names via declarative table-source registry.
816
+ // Adding an ORM is now a config entry, not code. Falls back to Drizzle
817
+ // entries when no user-specified tableSources are configured.
826
818
  const ormTables = [];
827
- const drizzleRe = /(?:pgTable|mysqlTable|sqliteTable)\s*\(\s*['"]([^'"]+)['"]/g;
828
- let dm;
829
- while ((dm = drizzleRe.exec(sourceCode)) !== null) {
830
- ormTables.push(dm[1]);
819
+ const tableProvenances = [];
820
+ const tableSources = schemaConfig.tableSources ?? [
821
+ { kind: 'callee', name: 'pgTable', arg: 0, description: 'Drizzle PostgreSQL table' },
822
+ { kind: 'callee', name: 'mysqlTable', arg: 0, description: 'Drizzle MySQL table' },
823
+ { kind: 'callee', name: 'sqliteTable', arg: 0, description: 'Drizzle SQLite table' },
824
+ ];
825
+ if (tableSources.length > 0) {
826
+ const registered = extractTablesFromRegistry(tableSources, {
827
+ ast: ast,
828
+ adapter: adapter,
829
+ sourceCode,
830
+ filePath: context.filePath,
831
+ });
832
+ for (const { table, source } of registered) {
833
+ ormTables.push(table);
834
+ tableProvenances.push({ table, source });
835
+ }
831
836
  }
832
837
  // Extract DDL from sql.exec(...) string literals inside Durable Object classes.
833
838
  // These are CREATE TABLE / DROP TABLE statements at runtime that migration
@@ -848,19 +853,19 @@ export function createSchemaCodeVisitor() {
848
853
  doDDL.push(sql);
849
854
  }
850
855
  // Build provenance context for this file — defaults from DEFAULT_SCHEMA_CONFIG
851
- const schemaConfig = (context.config ?? {});
852
856
  const detectionMode = schemaConfig.detection?.mode ?? 'hybrid';
853
857
  const provenanceContext = pm.buildProvenanceContext(ast, adapter, sourceCode, {
854
858
  mode: detectionMode,
855
859
  dbReceiverNames: schemaConfig.dbReceiverNames ?? defaults.dbReceiverNames,
856
860
  dbBindingNames: schemaConfig.dbBindingNames ?? defaults.dbBindingNames,
857
861
  dbCallMethods: schemaConfig.dbCallMethods ?? defaults.dbCallMethods,
862
+ dbWrapperNames: schemaConfig.dbWrapperNames ?? defaults.dbWrapperNames,
858
863
  });
859
864
  // File gate — skip files without DB usage
860
- if (!a.passesFileGate(context.filePath, sourceCode, schemaConfig, provenanceContext)) {
861
- const facts = { [context.filePath]: { tableRefs: [], ormTables } };
865
+ if (!passesFileGate(context.filePath, sourceCode, schemaConfig, provenanceContext)) {
866
+ const facts = { [context.filePath]: { tableRefs: [], ormTables, tableProvenance: tableProvenances } };
862
867
  if (doDDL.length > 0)
863
- facts[context.filePath].ddlSource = doDDL.join(';\n');
868
+ facts[context.filePath].ddlOps = parseMigrationOps(doDDL.join(';\n'));
864
869
  return { violations: [], facts };
865
870
  }
866
871
  // Build known-tables set from schemas config (pre-pipeline + DB-loaded schemas)
@@ -875,7 +880,7 @@ export function createSchemaCodeVisitor() {
875
880
  }
876
881
  }
877
882
  // Find table references (per-file, uses allTables for short-id false-positive filtering)
878
- const tableRefs = a.findTableReferences(ast, adapter, sourceCode, schemaConfig, provenanceContext, allTables);
883
+ const tableRefs = findTableReferences(ast, adapter, sourceCode, { config: schemaConfig, provenanceContext, allTables });
879
884
  // Record schema usage → emit as indexFacts via the shared instance
880
885
  a.recordTableUsage(ast, adapter, context.filePath, tableRefs);
881
886
  const pending = a.getPendingSchemaRecords();
@@ -901,14 +906,14 @@ export function createSchemaCodeVisitor() {
901
906
  }
902
907
  // Check naming conventions
903
908
  if (schemaConfig.checkNamingConventions !== false) {
904
- violations.push(...a.checkNamingConventions(tableRefs, context.filePath));
909
+ violations.push(...checkNamingConventions(tableRefs, context.filePath));
905
910
  }
906
911
  // Check query patterns
907
912
  if (schemaConfig.validateQueryPatterns !== false) {
908
- violations.push(...a.checkQueryPatterns(ast, adapter, sourceCode, schemaConfig));
913
+ violations.push(...checkQueryPatterns(ast, adapter, sourceCode, schemaConfig));
909
914
  }
910
915
  // Check SQL injection
911
- violations.push(...a.checkSQLInjection(ast, adapter, sourceCode));
916
+ violations.push(...checkSQLInjection(ast, adapter, sourceCode));
912
917
  // Emit facts for the Stage 3 reducer
913
918
  const fileFacts = {
914
919
  tableRefs: tableRefs.map((r) => ({
@@ -919,9 +924,10 @@ export function createSchemaCodeVisitor() {
919
924
  context: r.context,
920
925
  })),
921
926
  ormTables,
927
+ tableProvenance: tableProvenances,
922
928
  };
923
929
  if (doDDL.length > 0)
924
- fileFacts.ddlSource = doDDL.join(';\n');
930
+ fileFacts.ddlOps = parseMigrationOps(doDDL.join(';\n'));
925
931
  return {
926
932
  violations,
927
933
  facts: { [context.filePath]: fileFacts },
@@ -947,7 +953,23 @@ export function createSchemaPrismaVisitor() {
947
953
  const modelRe = /model\s+(\w+)\s*\{/g;
948
954
  let m;
949
955
  while ((m = modelRe.exec(sourceCode)) !== null) {
950
- models.push(m[1]);
956
+ const modelName = m[1];
957
+ // Scope @@map to this model's block by matching braces.
958
+ // Depth starts at 1 for the opening { already consumed by the regex.
959
+ const blockStart = m.index + m[0].length;
960
+ let depth = 1;
961
+ let blockEnd = blockStart;
962
+ for (; blockEnd < sourceCode.length && depth > 0; blockEnd++) {
963
+ if (sourceCode[blockEnd] === '{')
964
+ depth++;
965
+ else if (sourceCode[blockEnd] === '}')
966
+ depth--;
967
+ }
968
+ const blockContent = sourceCode.slice(blockStart, blockEnd - 1);
969
+ // @@map (block-level, double @) renames the table. @map (single @)
970
+ // is field-level — column rename — and must not be treated as a table name.
971
+ const mapMatch = blockContent.match(/@@map\s*\(\s*"([^"]+)"\s*\)/);
972
+ models.push(mapMatch ? mapMatch[1] : modelName);
951
973
  }
952
974
  return {
953
975
  violations: [],
@@ -968,17 +990,13 @@ export function createSchemaJsonVisitor() {
968
990
  stage: 'visitor',
969
991
  extensions: ['.json'],
970
992
  getRuleIds: () => [],
971
- async visit(_ast, _adapter, context, sourceCode) {
972
- let parsed = null;
973
- try {
974
- parsed = JSON.parse(sourceCode);
975
- }
976
- catch {
977
- // Invalid JSON — skip
978
- }
993
+ async visit(_ast, _adapter, context, _sourceCode) {
994
+ // Emit only a lightweight marker — the Stage 3 reducer reads and parses
995
+ // each JSON file on demand via context.readSource, so parsed objects are
996
+ // transient rather than retained in allFacts through stage 4.
979
997
  return {
980
998
  violations: [],
981
- facts: { [context.filePath]: { jsonParsed: parsed, jsonRaw: sourceCode } },
999
+ facts: { [context.filePath]: { isJson: true } },
982
1000
  };
983
1001
  },
984
1002
  defaultConfig: {},
@@ -996,6 +1014,7 @@ export function createSchemaReducer() {
996
1014
  const getAnalyzer = lazySingleton(() => import('./analyzers/universal/UniversalSchemaAnalyzer.js').then((m) => ({
997
1015
  analyzer: new m.UniversalSchemaAnalyzer(),
998
1016
  defaults: m.DEFAULT_SCHEMA_CONFIG,
1017
+ analyzeJsonSchemas: m.analyzeJsonSchemas,
999
1018
  })));
1000
1019
  return {
1001
1020
  name: 'schema',
@@ -1003,7 +1022,7 @@ export function createSchemaReducer() {
1003
1022
  consumes: [],
1004
1023
  getRuleIds: () => getRuleIdsFor('schema'),
1005
1024
  async reduce(allFacts, context) {
1006
- const { analyzer: a } = await getAnalyzer();
1025
+ const { analyzer: a, analyzeJsonSchemas } = await getAnalyzer();
1007
1026
  const violations = [];
1008
1027
  const schemaConfig = (context.config ?? {});
1009
1028
  // allFacts is keyed by visitor name, each value is { [filePath]: { ...per-file facts } }
@@ -1021,11 +1040,12 @@ export function createSchemaReducer() {
1021
1040
  };
1022
1041
  // ── 1. Build known-tables catalog ──────────────────────────────────────
1023
1042
  const knownTables = new Set();
1024
- // 1a. SQL DDL replay collect all .sql file facts, sort by numeric prefix, replay
1043
+ const tableProvenances = new Map(); // table name origins
1044
+ // 1a. SQL DDL replay — collect all DDL facts, sort by numeric prefix, replay
1025
1045
  const sqlFiles = [];
1026
1046
  for (const [filePath, fact] of perFile()) {
1027
- if ('ddlSource' in fact) {
1028
- sqlFiles.push({ filePath, source: fact.ddlSource });
1047
+ if ('ddlOps' in fact) {
1048
+ sqlFiles.push({ filePath, ops: fact.ddlOps });
1029
1049
  }
1030
1050
  }
1031
1051
  // Sort by numeric prefix in basename: "009_something" < "0010_rename"
@@ -1043,19 +1063,38 @@ export function createSchemaReducer() {
1043
1063
  return a.filePath.localeCompare(b.filePath);
1044
1064
  });
1045
1065
  for (const sqlFile of sqlFiles) {
1046
- a.processMigrationSource(sqlFile.source, knownTables);
1066
+ const before = new Set(knownTables);
1067
+ applyMigrationOps(sqlFile.ops, knownTables);
1068
+ // Record provenance for newly created tables
1069
+ for (const table of knownTables) {
1070
+ if (!before.has(table)) {
1071
+ const sources = tableProvenances.get(table) ?? [];
1072
+ sources.push({ table, tier: 'sql-migration', sourceFile: sqlFile.filePath, description: 'SQL migration' });
1073
+ tableProvenances.set(table, sources);
1074
+ }
1075
+ }
1047
1076
  }
1048
- // 1b. ORM tables from code files
1049
- for (const [, fact] of perFile()) {
1077
+ // 1b. ORM tables from code files — with provenance from the table-source registry
1078
+ for (const [filePath, fact] of perFile()) {
1050
1079
  const ormTables = fact.ormTables ?? [];
1051
1080
  for (const t of ormTables)
1052
1081
  knownTables.add(t);
1082
+ const provenances = fact.tableProvenance ?? [];
1083
+ for (const p of provenances) {
1084
+ const sources = tableProvenances.get(p.table) ?? [];
1085
+ sources.push(p.source);
1086
+ tableProvenances.set(p.table, sources);
1087
+ }
1053
1088
  }
1054
1089
  // 1c. Prisma models
1055
- for (const [, fact] of perFile()) {
1090
+ for (const [filePath, fact] of perFile()) {
1056
1091
  const prismaModels = fact.prismaModels ?? [];
1057
- for (const m of prismaModels)
1092
+ for (const m of prismaModels) {
1058
1093
  knownTables.add(m);
1094
+ const sources = tableProvenances.get(m) ?? [];
1095
+ sources.push({ table: m, tier: 'prisma-model', sourceFile: filePath, description: 'Prisma model' });
1096
+ tableProvenances.set(m, sources);
1097
+ }
1059
1098
  }
1060
1099
  // ── 2. Unknown-table detection ────────────────────────────────────────
1061
1100
  //
@@ -1074,8 +1113,25 @@ export function createSchemaReducer() {
1074
1113
  // never enter the catalog (table refs are for checking, not building).
1075
1114
  // Merge external tables from config (bonus, not required).
1076
1115
  const externalKnownTables = schemaConfig.knownTables ?? [];
1077
- for (const t of externalKnownTables)
1116
+ for (const t of externalKnownTables) {
1078
1117
  knownTables.add(t);
1118
+ const sources = tableProvenances.get(t) ?? [];
1119
+ sources.push({ table: t, tier: 'external-config', description: 'External configuration' });
1120
+ tableProvenances.set(t, sources);
1121
+ }
1122
+ // Also read the documented schemas config (structured {name, tables} objects).
1123
+ // The standalone UniversalSchemaAnalyzer.analyze() path reads schemas; the
1124
+ // pipeline reducer must read it too, otherwise configuring only schemas
1125
+ // silently triggers fail-open (knownTables.size === 0).
1126
+ const externalSchemas = schemaConfig.schemas ?? [];
1127
+ for (const schema of externalSchemas) {
1128
+ for (const table of schema.tables) {
1129
+ knownTables.add(table.name);
1130
+ const sources = tableProvenances.get(table.name) ?? [];
1131
+ sources.push({ table: table.name, tier: 'external-config', description: `Schema: ${schema.name}` });
1132
+ tableProvenances.set(table.name, sources);
1133
+ }
1134
+ }
1079
1135
  if (knownTables.size > 0) {
1080
1136
  // Collect all table references across all files
1081
1137
  const allTableRefs = [];
@@ -1089,10 +1145,11 @@ export function createSchemaReducer() {
1089
1145
  const unknownRefs = allTableRefs.filter(ref => !knownTables.has(ref.table));
1090
1146
  if (unknownRefs.length / Math.max(knownTables.size, 1) <= 10) {
1091
1147
  for (const ref of unknownRefs) {
1092
- const suggestions = a.getNearestTableSuggestions(ref.table, knownTables, 2);
1148
+ const suggestions = getNearestTableSuggestions(ref.table, knownTables, 2);
1093
1149
  const msg = suggestions.length > 0
1094
1150
  ? `Reference to unknown table '${ref.table}' (${ref.type}). Did you mean: ${suggestions.join(', ')}?`
1095
1151
  : `Reference to unknown table '${ref.table}' (${ref.type})`;
1152
+ const suggestionNames = suggestions.map((s) => s.replace(/^'|'$/g, ''));
1096
1153
  violations.push({
1097
1154
  file: ref.file,
1098
1155
  line: ref.line,
@@ -1102,6 +1159,15 @@ export function createSchemaReducer() {
1102
1159
  rule: 'unknown-table',
1103
1160
  analyzer: 'schema',
1104
1161
  symbol: ref.table,
1162
+ resolution: {
1163
+ action: suggestionNames.length > 0 ? 'use-known-table' : 'register-or-fix-table',
1164
+ summary: suggestionNames.length > 0
1165
+ ? `Rename the table reference '${ref.table}' to the nearest known table: ${suggestions.join(', ')}.`
1166
+ : `The table '${ref.table}' is not in the known catalog — register it, or fix the reference to a known table.`,
1167
+ symbols: suggestionNames.length > 0 ? suggestionNames : [ref.table],
1168
+ files: [ref.file],
1169
+ lines: [ref.line],
1170
+ },
1105
1171
  });
1106
1172
  }
1107
1173
  }
@@ -1109,32 +1175,41 @@ export function createSchemaReducer() {
1109
1175
  // ── 3. JSON schema validation ──────────────────────────────────────────
1110
1176
  if (schemaConfig.validateJsonSchemas !== false) {
1111
1177
  try {
1112
- // Build content Map from json facts for the refactored analyzeJsonSchemas
1113
- const jsonContents = new Map();
1114
- let jsonFactCount = 0;
1178
+ // Collect JSON file paths from the lightweight visitor markers, then
1179
+ // parse each on demand so parsed objects never survive past this loop.
1180
+ const jsonFiles = [];
1115
1181
  for (const [filePath, fact] of perFile()) {
1116
- if ('jsonParsed' in fact) {
1117
- jsonFactCount++;
1118
- const raw = (fact.jsonParsed ?? null);
1119
- // parsed is the result of JSON.parse: null on failure, or the
1120
- // deserialized value. Only accept objects as usable parsed content.
1121
- const parsed = raw !== null && typeof raw === 'object' ? raw : null;
1122
- jsonContents.set(filePath, {
1123
- parsed,
1124
- raw: (fact.jsonRaw ?? fact.sourceCode ?? ''),
1125
- });
1182
+ if ('isJson' in fact) {
1183
+ jsonFiles.push(filePath);
1126
1184
  }
1127
1185
  }
1128
- const jsonResult = a.analyzeJsonSchemas(jsonContents, schemaConfig);
1186
+ const readJson = (filePath) => {
1187
+ const raw = context.readSource?.(filePath);
1188
+ if (raw === undefined)
1189
+ return null;
1190
+ try {
1191
+ const parsed = JSON.parse(raw);
1192
+ return parsed !== null && typeof parsed === 'object' ? parsed : null;
1193
+ }
1194
+ catch {
1195
+ return null;
1196
+ }
1197
+ };
1198
+ const jsonResult = analyzeJsonSchemas(jsonFiles, readJson, schemaConfig);
1129
1199
  violations.push(...(jsonResult?.violations ?? []));
1130
1200
  }
1131
1201
  catch (e) {
1132
1202
  // JSON schema validation is best-effort (non-fatal)
1133
1203
  }
1134
1204
  }
1205
+ // ── 4. Build table catalog for metadata ───────────────────────────────
1206
+ const catalogEntries = [];
1207
+ for (const [table, sources] of tableProvenances) {
1208
+ catalogEntries.push({ table, sources });
1209
+ }
1135
1210
  return {
1136
1211
  violations,
1137
- facts: {},
1212
+ facts: { tableCatalog: catalogEntries },
1138
1213
  factsConsumed: [...perFile()].length,
1139
1214
  };
1140
1215
  },