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.
- package/.claude-plugin/marketplace.json +2 -2
- package/CHANGELOG.md +119 -1
- package/dist/analyzers/__verify_self_probe.d.ts +2 -0
- package/dist/analyzers/__verify_self_probe.d.ts.map +1 -0
- package/dist/analyzers/__verify_self_probe.js +5 -0
- package/dist/analyzers/__verify_self_probe.js.map +1 -0
- package/dist/analyzers/analyzerUtils.d.ts +34 -1
- package/dist/analyzers/analyzerUtils.d.ts.map +1 -1
- package/dist/analyzers/analyzerUtils.js +61 -22
- package/dist/analyzers/analyzerUtils.js.map +1 -1
- package/dist/analyzers/cross-language/APIContractAnalyzer.d.ts +18 -23
- package/dist/analyzers/cross-language/APIContractAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/cross-language/APIContractAnalyzer.js +198 -186
- package/dist/analyzers/cross-language/APIContractAnalyzer.js.map +1 -1
- package/dist/analyzers/cross-language/DependencyGraphBuilder.d.ts +78 -55
- package/dist/analyzers/cross-language/DependencyGraphBuilder.d.ts.map +1 -1
- package/dist/analyzers/cross-language/DependencyGraphBuilder.js +244 -243
- package/dist/analyzers/cross-language/DependencyGraphBuilder.js.map +1 -1
- package/dist/analyzers/cross-language/SchemaValidator.d.ts +27 -37
- package/dist/analyzers/cross-language/SchemaValidator.d.ts.map +1 -1
- package/dist/analyzers/cross-language/SchemaValidator.js +314 -275
- package/dist/analyzers/cross-language/SchemaValidator.js.map +1 -1
- package/dist/analyzers/crossDomain/CrossDomainAnalyzer.d.ts +17 -46
- package/dist/analyzers/crossDomain/CrossDomainAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/crossDomain/CrossDomainAnalyzer.js +620 -523
- package/dist/analyzers/crossDomain/CrossDomainAnalyzer.js.map +1 -1
- package/dist/analyzers/documentationAnalyzer.d.ts +7 -2
- package/dist/analyzers/documentationAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/documentationAnalyzer.js +259 -198
- package/dist/analyzers/documentationAnalyzer.js.map +1 -1
- package/dist/analyzers/invariantsAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/invariantsAnalyzer.js +59 -46
- package/dist/analyzers/invariantsAnalyzer.js.map +1 -1
- package/dist/analyzers/orm/adapterRegistry.d.ts +10 -0
- package/dist/analyzers/orm/adapterRegistry.d.ts.map +1 -1
- package/dist/analyzers/orm/adapterRegistry.js +10 -0
- package/dist/analyzers/orm/adapterRegistry.js.map +1 -1
- package/dist/analyzers/orm/drizzleAdapter.d.ts +22 -0
- package/dist/analyzers/orm/drizzleAdapter.d.ts.map +1 -1
- package/dist/analyzers/orm/drizzleAdapter.js +42 -44
- package/dist/analyzers/orm/drizzleAdapter.js.map +1 -1
- package/dist/analyzers/orm/prismaAdapter.d.ts +30 -0
- package/dist/analyzers/orm/prismaAdapter.d.ts.map +1 -1
- package/dist/analyzers/orm/prismaAdapter.js +30 -0
- package/dist/analyzers/orm/prismaAdapter.js.map +1 -1
- package/dist/analyzers/provenance.d.ts +72 -2
- package/dist/analyzers/provenance.d.ts.map +1 -1
- package/dist/analyzers/provenance.js +462 -338
- package/dist/analyzers/provenance.js.map +1 -1
- package/dist/analyzers/reactAnalyzer.d.ts +11 -0
- package/dist/analyzers/reactAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/reactAnalyzer.js +188 -125
- package/dist/analyzers/reactAnalyzer.js.map +1 -1
- package/dist/analyzers/ruleRegistry.d.ts +118 -1
- package/dist/analyzers/ruleRegistry.d.ts.map +1 -1
- package/dist/analyzers/ruleRegistry.js +1717 -104
- package/dist/analyzers/ruleRegistry.js.map +1 -1
- package/dist/analyzers/ruleTiming.d.ts +54 -0
- package/dist/analyzers/ruleTiming.d.ts.map +1 -0
- package/dist/analyzers/ruleTiming.js +71 -0
- package/dist/analyzers/ruleTiming.js.map +1 -0
- package/dist/analyzers/universal/UniversalConventionsAnalyzer.d.ts +13 -0
- package/dist/analyzers/universal/UniversalConventionsAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/universal/UniversalConventionsAnalyzer.js +362 -283
- package/dist/analyzers/universal/UniversalConventionsAnalyzer.js.map +1 -1
- package/dist/analyzers/universal/UniversalDRYAnalyzer.d.ts +26 -68
- package/dist/analyzers/universal/UniversalDRYAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/universal/UniversalDRYAnalyzer.js +430 -391
- package/dist/analyzers/universal/UniversalDRYAnalyzer.js.map +1 -1
- package/dist/analyzers/universal/UniversalDataAccessAnalyzer.d.ts +24 -186
- package/dist/analyzers/universal/UniversalDataAccessAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/universal/UniversalDataAccessAnalyzer.js +1081 -1061
- package/dist/analyzers/universal/UniversalDataAccessAnalyzer.js.map +1 -1
- package/dist/analyzers/universal/UniversalDocumentationAnalyzer.d.ts +3 -49
- package/dist/analyzers/universal/UniversalDocumentationAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/universal/UniversalDocumentationAnalyzer.js +414 -340
- package/dist/analyzers/universal/UniversalDocumentationAnalyzer.js.map +1 -1
- package/dist/analyzers/universal/UniversalSOLIDAnalyzer.d.ts +48 -5
- package/dist/analyzers/universal/UniversalSOLIDAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/universal/UniversalSOLIDAnalyzer.js +262 -134
- package/dist/analyzers/universal/UniversalSOLIDAnalyzer.js.map +1 -1
- package/dist/analyzers/universal/UniversalSchemaAnalyzer.d.ts +23 -212
- package/dist/analyzers/universal/UniversalSchemaAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/universal/UniversalSchemaAnalyzer.js +139 -1518
- package/dist/analyzers/universal/UniversalSchemaAnalyzer.js.map +1 -1
- package/dist/analyzers/universal/UniversalStylesAnalyzer.d.ts +78 -79
- package/dist/analyzers/universal/UniversalStylesAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/universal/UniversalStylesAnalyzer.js +613 -472
- package/dist/analyzers/universal/UniversalStylesAnalyzer.js.map +1 -1
- package/dist/analyzers/universal/schema/codeAnalysis.d.ts +307 -0
- package/dist/analyzers/universal/schema/codeAnalysis.d.ts.map +1 -0
- package/dist/analyzers/universal/schema/codeAnalysis.js +965 -0
- package/dist/analyzers/universal/schema/codeAnalysis.js.map +1 -0
- package/dist/analyzers/universal/schema/config.d.ts +31 -0
- package/dist/analyzers/universal/schema/config.d.ts.map +1 -0
- package/dist/analyzers/universal/schema/config.js +57 -0
- package/dist/analyzers/universal/schema/config.js.map +1 -0
- package/dist/analyzers/universal/schema/discovery.d.ts +167 -0
- package/dist/analyzers/universal/schema/discovery.d.ts.map +1 -0
- package/dist/analyzers/universal/schema/discovery.js +616 -0
- package/dist/analyzers/universal/schema/discovery.js.map +1 -0
- package/dist/analyzers/universal/schema/jsonSchema.d.ts +27 -0
- package/dist/analyzers/universal/schema/jsonSchema.d.ts.map +1 -0
- package/dist/analyzers/universal/schema/jsonSchema.js +368 -0
- package/dist/analyzers/universal/schema/jsonSchema.js.map +1 -0
- package/dist/analyzers/universal/schema/migrations.d.ts +90 -0
- package/dist/analyzers/universal/schema/migrations.d.ts.map +1 -0
- package/dist/analyzers/universal/schema/migrations.js +238 -0
- package/dist/analyzers/universal/schema/migrations.js.map +1 -0
- package/dist/analyzers/universal/schema/types.d.ts +131 -0
- package/dist/analyzers/universal/schema/types.d.ts.map +1 -0
- package/dist/analyzers/universal/schema/types.js +10 -0
- package/dist/analyzers/universal/schema/types.js.map +1 -0
- package/dist/analyzers/universal/schema/violations.d.ts +44 -0
- package/dist/analyzers/universal/schema/violations.d.ts.map +1 -0
- package/dist/analyzers/universal/schema/violations.js +54 -0
- package/dist/analyzers/universal/schema/violations.js.map +1 -0
- package/dist/auditRunner.d.ts.map +1 -1
- package/dist/auditRunner.js +101 -39
- package/dist/auditRunner.js.map +1 -1
- package/dist/cli.js +348 -40
- package/dist/cli.js.map +1 -1
- package/dist/config/configLoader.d.ts.map +1 -1
- package/dist/config/configLoader.js +9 -7
- package/dist/config/configLoader.js.map +1 -1
- package/dist/config/defaults.d.ts +12 -3
- package/dist/config/defaults.d.ts.map +1 -1
- package/dist/config/defaults.js +15 -5
- package/dist/config/defaults.js.map +1 -1
- package/dist/config/effectiveConfig.d.ts +75 -0
- package/dist/config/effectiveConfig.d.ts.map +1 -0
- package/dist/config/effectiveConfig.js +213 -0
- package/dist/config/effectiveConfig.js.map +1 -0
- package/dist/config/pathProfiles.d.ts +7 -4
- package/dist/config/pathProfiles.d.ts.map +1 -1
- package/dist/config/pathProfiles.js +8 -10
- package/dist/config/pathProfiles.js.map +1 -1
- package/dist/config/thresholdRationales.d.ts +45 -0
- package/dist/config/thresholdRationales.d.ts.map +1 -0
- package/dist/config/thresholdRationales.js +67 -0
- package/dist/config/thresholdRationales.js.map +1 -0
- package/dist/conventions/conventionMiner.d.ts.map +1 -1
- package/dist/conventions/conventionMiner.js +6 -2
- package/dist/conventions/conventionMiner.js.map +1 -1
- package/dist/enforcement/diffGate.d.ts +55 -0
- package/dist/enforcement/diffGate.d.ts.map +1 -0
- package/dist/enforcement/diffGate.js +136 -0
- package/dist/enforcement/diffGate.js.map +1 -0
- package/dist/enforcement/gate.d.ts +36 -0
- package/dist/enforcement/gate.d.ts.map +1 -0
- package/dist/enforcement/gate.js +72 -0
- package/dist/enforcement/gate.js.map +1 -0
- package/dist/enforcement/suppressions.d.ts +41 -0
- package/dist/enforcement/suppressions.d.ts.map +1 -0
- package/dist/enforcement/suppressions.js +170 -0
- package/dist/enforcement/suppressions.js.map +1 -0
- package/dist/fingerprint.d.ts.map +1 -1
- package/dist/fingerprint.js +4 -1
- package/dist/fingerprint.js.map +1 -1
- package/dist/grammars/tree-sitter-scss.wasm +0 -0
- package/dist/invariants/ruleEngine.d.ts +3 -4
- package/dist/invariants/ruleEngine.d.ts.map +1 -1
- package/dist/invariants/ruleEngine.js +2 -2
- package/dist/invariants/ruleEngine.js.map +1 -1
- package/dist/languages/LanguageOrchestrator.d.ts +14 -0
- package/dist/languages/LanguageOrchestrator.d.ts.map +1 -1
- package/dist/languages/LanguageOrchestrator.js +93 -55
- package/dist/languages/LanguageOrchestrator.js.map +1 -1
- package/dist/languages/LanguageRegistry.d.ts +8 -0
- package/dist/languages/LanguageRegistry.d.ts.map +1 -1
- package/dist/languages/LanguageRegistry.js +8 -0
- package/dist/languages/LanguageRegistry.js.map +1 -1
- package/dist/languages/RuntimeManager.d.ts +82 -60
- package/dist/languages/RuntimeManager.d.ts.map +1 -1
- package/dist/languages/RuntimeManager.js +404 -349
- package/dist/languages/RuntimeManager.js.map +1 -1
- package/dist/languages/UniversalAnalyzer.d.ts +51 -6
- package/dist/languages/UniversalAnalyzer.d.ts.map +1 -1
- package/dist/languages/UniversalAnalyzer.js +127 -88
- package/dist/languages/UniversalAnalyzer.js.map +1 -1
- package/dist/languages/adapterBridge.d.ts +36 -0
- package/dist/languages/adapterBridge.d.ts.map +1 -1
- package/dist/languages/adapterBridge.js +89 -35
- package/dist/languages/adapterBridge.js.map +1 -1
- package/dist/languages/go/GoAdapter.d.ts +66 -23
- package/dist/languages/go/GoAdapter.d.ts.map +1 -1
- package/dist/languages/go/GoAdapter.js +553 -474
- package/dist/languages/go/GoAdapter.js.map +1 -1
- package/dist/languages/tree-sitter/TreeSitterCssAdapter.d.ts +38 -9
- package/dist/languages/tree-sitter/TreeSitterCssAdapter.d.ts.map +1 -1
- package/dist/languages/tree-sitter/TreeSitterCssAdapter.js +111 -74
- package/dist/languages/tree-sitter/TreeSitterCssAdapter.js.map +1 -1
- package/dist/languages/tree-sitter/converter.d.ts +18 -0
- package/dist/languages/tree-sitter/converter.d.ts.map +1 -1
- package/dist/languages/tree-sitter/converter.js +18 -0
- package/dist/languages/tree-sitter/converter.js.map +1 -1
- package/dist/languages/tree-sitter/parser.d.ts +39 -2
- package/dist/languages/tree-sitter/parser.d.ts.map +1 -1
- package/dist/languages/tree-sitter/parser.js +95 -12
- package/dist/languages/tree-sitter/parser.js.map +1 -1
- package/dist/languages/types.d.ts +67 -2
- package/dist/languages/types.d.ts.map +1 -1
- package/dist/languages/typescript/TreeSitterTypeScriptAdapter.d.ts +253 -62
- package/dist/languages/typescript/TreeSitterTypeScriptAdapter.d.ts.map +1 -1
- package/dist/languages/typescript/TreeSitterTypeScriptAdapter.js +1461 -791
- package/dist/languages/typescript/TreeSitterTypeScriptAdapter.js.map +1 -1
- package/dist/mcp-tools/whitelistTools.d.ts +1 -1
- package/dist/mcp-tools-shared.d.ts.map +1 -1
- package/dist/mcp-tools-shared.js +1 -0
- package/dist/mcp-tools-shared.js.map +1 -1
- package/dist/mcpAuditJobs.d.ts.map +1 -1
- package/dist/mcpAuditJobs.js +1 -0
- package/dist/mcpAuditJobs.js.map +1 -1
- package/dist/pipeline.d.ts +30 -1
- package/dist/pipeline.d.ts.map +1 -1
- package/dist/pipeline.js +527 -203
- package/dist/pipeline.js.map +1 -1
- package/dist/pipelineAdapters.d.ts +5 -12
- package/dist/pipelineAdapters.d.ts.map +1 -1
- package/dist/pipelineAdapters.js +175 -100
- package/dist/pipelineAdapters.js.map +1 -1
- package/dist/presets/presets.d.ts +54 -0
- package/dist/presets/presets.d.ts.map +1 -0
- package/dist/presets/presets.js +181 -0
- package/dist/presets/presets.js.map +1 -0
- package/dist/reporting/sarifReportGenerator.d.ts.map +1 -1
- package/dist/reporting/sarifReportGenerator.js +0 -2
- package/dist/reporting/sarifReportGenerator.js.map +1 -1
- package/dist/ruleAliases.d.ts +67 -0
- package/dist/ruleAliases.d.ts.map +1 -0
- package/dist/ruleAliases.js +79 -0
- package/dist/ruleAliases.js.map +1 -0
- package/dist/styles/cssAstExtractor.d.ts +20 -5
- package/dist/styles/cssAstExtractor.d.ts.map +1 -1
- package/dist/styles/cssAstExtractor.js +247 -9
- package/dist/styles/cssAstExtractor.js.map +1 -1
- package/dist/styles/styleIndexer.js +13 -4
- package/dist/styles/styleIndexer.js.map +1 -1
- package/dist/styles/tailwindConfigLoader.d.ts +8 -6
- package/dist/styles/tailwindConfigLoader.d.ts.map +1 -1
- package/dist/styles/tailwindConfigLoader.js +61 -15
- package/dist/styles/tailwindConfigLoader.js.map +1 -1
- package/dist/styles/tailwindUtilityExpander.d.ts.map +1 -1
- package/dist/styles/tailwindUtilityExpander.js +13 -1
- package/dist/styles/tailwindUtilityExpander.js.map +1 -1
- package/dist/types.d.ts +201 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +8 -0
- package/dist/types.js.map +1 -1
- package/package.json +7 -4
- package/plugin/.claude-plugin/plugin.json +1 -1
- package/plugin/hooks/hooks.json +1 -1
- package/plugin/scripts/hook-audit.sh +25 -7
- package/plugin/skills/code-auditor/SKILL.md +5 -5
package/dist/pipelineAdapters.js
CHANGED
|
@@ -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 {
|
|
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
|
|
525
|
-
* styleExtractor.ts for .css
|
|
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
|
|
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,
|
|
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
|
-
//
|
|
724
|
-
const
|
|
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,
|
|
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
|
|
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: {
|
|
779
|
+
facts: {
|
|
780
|
+
[context.filePath]: {
|
|
781
|
+
ddlOps: ops,
|
|
782
|
+
...(skipped && { skipped: true, bytes }),
|
|
783
|
+
},
|
|
784
|
+
},
|
|
799
785
|
};
|
|
800
786
|
},
|
|
801
787
|
defaultConfig: {},
|
|
802
|
-
description: '
|
|
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
|
-
//
|
|
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
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
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 (!
|
|
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].
|
|
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 =
|
|
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(...
|
|
909
|
+
violations.push(...checkNamingConventions(tableRefs, context.filePath));
|
|
905
910
|
}
|
|
906
911
|
// Check query patterns
|
|
907
912
|
if (schemaConfig.validateQueryPatterns !== false) {
|
|
908
|
-
violations.push(...
|
|
913
|
+
violations.push(...checkQueryPatterns(ast, adapter, sourceCode, schemaConfig));
|
|
909
914
|
}
|
|
910
915
|
// Check SQL injection
|
|
911
|
-
violations.push(...
|
|
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.
|
|
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
|
-
|
|
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,
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
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]: {
|
|
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
|
-
|
|
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 ('
|
|
1028
|
-
sqlFiles.push({ filePath,
|
|
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
|
-
|
|
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 =
|
|
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
|
-
//
|
|
1113
|
-
|
|
1114
|
-
|
|
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 ('
|
|
1117
|
-
|
|
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
|
|
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
|
},
|