Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 | 3x 3x 3x 3x 53x 32x 21x 21x 21x 21x 20x 20x 20x 20x 20x 1x 1x 21x 53x 53x 52x 52x 52x 205x 205x 1490x 205x 51x 51x 51x 51x 204x 204x 1482x 1482x 27x 43x 39x 39x 39x 4x 39x 4x 4x 51x 51x 39x 39x 39x 39x 39x 39x 37x 39x 39x 51x 51x 51x 51x 2x 2x 21x 21x 3x | // Rules service - the main rules engine for processing headers
import { HeaderModel } from "../HeaderModel";
import { headerValidationRules } from "./engine/HeaderValidationRules";
import { getRules, ruleStore } from "./loaders/GetRules";
import { AnalysisResult, RuleViolation, ViolationGroup } from "./types/AnalysisTypes";
import { HeaderSection, IValidationRule } from "./types/interfaces";
class RulesService {
private rulesLoaded = false;
private loadingPromise: Promise<void> | null = null;
/**
* Load rules once at application startup
* Can be called multiple times safely - subsequent calls return the same promise
*/
private async loadRules(): Promise<void> {
if (this.rulesLoaded) {
return Promise.resolve();
}
Iif (this.loadingPromise) {
return this.loadingPromise;
}
this.loadingPromise = (async () => {
try {
await getRules(
() => {
console.log("🔍 RulesService: Rules loaded successfully");
console.log("🔍 RulesService: SimpleRules:", ruleStore.simpleRuleSet?.length || 0);
console.log("🔍 RulesService: AndRules:", ruleStore.andRuleSet?.length || 0);
headerValidationRules.setRules(ruleStore.simpleRuleSet, ruleStore.andRuleSet);
this.rulesLoaded = true;
}
);
} catch (error) {
console.error("🔍 RulesService: Failed to load rules:", error);
throw error;
}
})();
return this.loadingPromise;
}
/**
* Analyze headers for rule violations - main entry point
*/
public async analyzeHeaders(headerModel: HeaderModel): Promise<AnalysisResult> {
try {
// Load rules once (safe to call multiple times)
await this.loadRules();
// Create sections array for header processing
const headerSections = [
headerModel.summary.rows,
headerModel.forefrontAntiSpamReport.rows,
headerModel.antiSpamReport.rows,
headerModel.otherHeaders.rows
];
console.log("🔍 RulesService: Processing", headerSections.length, "sections");
// Clear flags and run simple rules on each section
headerSections.forEach(section => {
Eif (Array.isArray(section)) {
// Clear existing flags
section.forEach((headerSection: HeaderSection) => {
delete headerSection.rulesFlagged;
});
// Run simple rules on this section
headerValidationRules.flagAllRowsWithViolations(section, headerSections);
}
});
// Run complex rule validation (operates on all sections)
headerValidationRules.findComplexViolations(headerSections);
// Extract violations and build groups directly during evaluation
// Use a Map to ensure each rule only creates one violation
const violationMap = new Map<IValidationRule, RuleViolation>();
const groupMap = new Map<string, ViolationGroup>();
headerSections.forEach(section => {
Eif (Array.isArray(section)) {
section.forEach((headerSection: HeaderSection) => {
const rulesFlagged = headerSection.rulesFlagged;
if (rulesFlagged && rulesFlagged.length > 0) {
rulesFlagged.forEach((rule: IValidationRule) => {
// Check if we've already created a violation for this rule
if (!violationMap.has(rule)) {
// First time seeing this rule - create the violation
const parentAndRule = rule.parentAndRule;
const violation: RuleViolation = {
rule: rule,
affectedSections: [headerSection],
highlightPattern: rule.errorPattern
};
if (parentAndRule?.message) {
violation.parentMessage = parentAndRule.message;
}
violationMap.set(rule, violation);
} else {
// We've seen this rule - add this section to affected sections
const existing = violationMap.get(rule)!;
existing.affectedSections.push(headerSection);
}
});
}
});
}
});
// Convert violation map to array and build groups
const violations = Array.from(violationMap.values());
violations.forEach((violation) => {
const rule = violation.rule;
const isAndRule = !!rule.parentAndRule;
const displayName = isAndRule ? rule.parentAndRule!.message : rule.errorMessage;
const severity = isAndRule ? rule.parentAndRule!.severity : rule.severity;
const groupKey = displayName;
if (!groupMap.has(groupKey)) {
groupMap.set(groupKey, {
groupId: `group-${groupKey.replace(/\s+/g, "-").toLowerCase()}`,
displayName,
severity,
isAndRule,
violations: []
});
}
const group = groupMap.get(groupKey)!;
group.violations.push(violation);
});
const violationGroups = Array.from(groupMap.values());
console.log("Rule violations found:", violations.length);
console.log("Violation groups found:", violationGroups.length);
return {
success: true,
enrichedHeaders: headerModel,
violations,
violationGroups
};
} catch (error) {
console.error("Rules analysis failed:", error);
return {
success: false,
error: error instanceof Error ? error.message : "Unknown analysis error",
enrichedHeaders: headerModel,
violations: [],
violationGroups: []
};
}
}
/**
* Reset service state for testing
*/
public resetForTesting(): void {
this.rulesLoaded = false;
this.loadingPromise = null;
}
}
// Export singleton instance
export const rulesService = new RulesService(); |