refactor(admin): split 8 oversized lib/ files into focused modules under 500 LOC
Split these files that exceeded the 500-line hard cap: - privacy-policy.ts (965 LOC) -> sections + renderers - academy/api.ts (787 LOC) -> courses + mock-data - whistleblower/api.ts (755 LOC) -> operations + mock-data - vvt-profiling.ts (659 LOC) -> data + logic - cookie-banner.ts (595 LOC) -> config + embed - dsr/types.ts (581 LOC) -> core + api types - tom-generator/rules-engine.ts (560 LOC) -> evaluator + gap-analysis - datapoint-helpers.ts (548 LOC) -> generators + validators Each original file becomes a barrel re-export for backward compatibility. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
171
admin-compliance/lib/sdk/tom-generator/gap-analysis.ts
Normal file
171
admin-compliance/lib/sdk/tom-generator/gap-analysis.ts
Normal file
@@ -0,0 +1,171 @@
|
||||
/**
|
||||
* TOM Rules Engine — Gap Analysis & Helper Functions
|
||||
*
|
||||
* Singleton instance, convenience functions, and gap analysis logic.
|
||||
*/
|
||||
|
||||
import {
|
||||
DerivedTOM,
|
||||
EvidenceDocument,
|
||||
GapAnalysisResult,
|
||||
MissingControl,
|
||||
PartialControl,
|
||||
MissingEvidence,
|
||||
RulesEngineResult,
|
||||
RulesEngineEvaluationContext,
|
||||
} from './types'
|
||||
import { getControlById } from './controls/loader'
|
||||
import { TOMRulesEngine } from './rules-evaluator'
|
||||
|
||||
// =============================================================================
|
||||
// SINGLETON INSTANCE
|
||||
// =============================================================================
|
||||
|
||||
let rulesEngineInstance: TOMRulesEngine | null = null
|
||||
|
||||
export function getTOMRulesEngine(): TOMRulesEngine {
|
||||
if (!rulesEngineInstance) {
|
||||
rulesEngineInstance = new TOMRulesEngine()
|
||||
}
|
||||
return rulesEngineInstance
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// GAP ANALYSIS
|
||||
// =============================================================================
|
||||
|
||||
export function performGapAnalysis(
|
||||
derivedTOMs: DerivedTOM[],
|
||||
documents: EvidenceDocument[]
|
||||
): GapAnalysisResult {
|
||||
const missingControls: MissingControl[] = []
|
||||
const partialControls: PartialControl[] = []
|
||||
const missingEvidence: MissingEvidence[] = []
|
||||
const recommendations: string[] = []
|
||||
|
||||
let totalScore = 0
|
||||
let totalWeight = 0
|
||||
|
||||
const applicableTOMs = derivedTOMs.filter(
|
||||
(tom) => tom.applicability === 'REQUIRED' || tom.applicability === 'RECOMMENDED'
|
||||
)
|
||||
|
||||
for (const tom of applicableTOMs) {
|
||||
const control = getControlById(tom.controlId)
|
||||
if (!control) continue
|
||||
|
||||
const weight = tom.applicability === 'REQUIRED' ? 3 : 1
|
||||
totalWeight += weight
|
||||
|
||||
if (tom.implementationStatus === 'NOT_IMPLEMENTED') {
|
||||
missingControls.push({
|
||||
controlId: tom.controlId,
|
||||
reason: `${control.name.de} ist nicht implementiert`,
|
||||
priority: control.priority,
|
||||
})
|
||||
} else if (tom.implementationStatus === 'PARTIAL') {
|
||||
partialControls.push({
|
||||
controlId: tom.controlId,
|
||||
missingAspects: tom.evidenceGaps,
|
||||
})
|
||||
totalScore += weight * 0.5
|
||||
} else {
|
||||
totalScore += weight
|
||||
}
|
||||
|
||||
const linkedEvidenceIds = tom.linkedEvidence
|
||||
const requiredEvidence = control.evidenceRequirements
|
||||
const providedEvidence = documents.filter((doc) =>
|
||||
linkedEvidenceIds.includes(doc.id)
|
||||
)
|
||||
|
||||
if (providedEvidence.length < requiredEvidence.length) {
|
||||
const missing = requiredEvidence.filter(
|
||||
(req) =>
|
||||
!providedEvidence.some(
|
||||
(doc) =>
|
||||
doc.documentType === 'POLICY' ||
|
||||
doc.documentType === 'CERTIFICATE' ||
|
||||
doc.originalName.toLowerCase().includes(req.toLowerCase())
|
||||
)
|
||||
)
|
||||
|
||||
if (missing.length > 0) {
|
||||
missingEvidence.push({
|
||||
controlId: tom.controlId,
|
||||
requiredEvidence: missing,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const overallScore =
|
||||
totalWeight > 0 ? Math.round((totalScore / totalWeight) * 100) : 0
|
||||
|
||||
if (missingControls.length > 0) {
|
||||
const criticalMissing = missingControls.filter((mc) => mc.priority === 'CRITICAL')
|
||||
if (criticalMissing.length > 0) {
|
||||
recommendations.push(
|
||||
`${criticalMissing.length} kritische Kontrollen sind nicht implementiert. Diese sollten priorisiert werden.`
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if (partialControls.length > 0) {
|
||||
recommendations.push(
|
||||
`${partialControls.length} Kontrollen sind nur teilweise implementiert. Vervollstaendigen Sie die Implementierung.`
|
||||
)
|
||||
}
|
||||
|
||||
if (missingEvidence.length > 0) {
|
||||
recommendations.push(
|
||||
`Fuer ${missingEvidence.length} Kontrollen fehlen Nachweisdokumente. Laden Sie die entsprechenden Dokumente hoch.`
|
||||
)
|
||||
}
|
||||
|
||||
if (overallScore >= 80) {
|
||||
recommendations.push(
|
||||
'Ihr TOM-Compliance-Score ist gut. Fuehren Sie regelmaessige Ueberpruefungen durch.'
|
||||
)
|
||||
} else if (overallScore >= 50) {
|
||||
recommendations.push(
|
||||
'Ihr TOM-Compliance-Score erfordert Verbesserungen. Fokussieren Sie sich auf die kritischen Luecken.'
|
||||
)
|
||||
} else {
|
||||
recommendations.push(
|
||||
'Ihr TOM-Compliance-Score ist niedrig. Eine systematische Ueberarbeitung der Massnahmen wird empfohlen.'
|
||||
)
|
||||
}
|
||||
|
||||
return {
|
||||
overallScore,
|
||||
missingControls,
|
||||
partialControls,
|
||||
missingEvidence,
|
||||
recommendations,
|
||||
generatedAt: new Date(),
|
||||
}
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// HELPER FUNCTIONS
|
||||
// =============================================================================
|
||||
|
||||
export function evaluateControlsForContext(
|
||||
context: RulesEngineEvaluationContext
|
||||
): RulesEngineResult[] {
|
||||
return getTOMRulesEngine().evaluateControls(context)
|
||||
}
|
||||
|
||||
export function deriveTOMsForContext(
|
||||
context: RulesEngineEvaluationContext
|
||||
): DerivedTOM[] {
|
||||
return getTOMRulesEngine().deriveAllTOMs(context)
|
||||
}
|
||||
|
||||
export function performQuickGapAnalysis(
|
||||
derivedTOMs: DerivedTOM[],
|
||||
documents: EvidenceDocument[]
|
||||
): GapAnalysisResult {
|
||||
return performGapAnalysis(derivedTOMs, documents)
|
||||
}
|
||||
Reference in New Issue
Block a user