Split loader.ts (3163 LOC) into categories/ subdir (8 files, each <500 LOC): - access.ts (ACCESS_CONTROL + ADMISSION_CONTROL + ACCESS_AUTHORIZATION) - transfer-input.ts (TRANSFER_CONTROL + INPUT_CONTROL) - order-availability.ts (ORDER_CONTROL + AVAILABILITY) - separation-encryption.ts (SEPARATION incl. DL-* + ENCRYPTION) - pseudonymization.ts (PSEUDONYMIZATION) - resilience-recovery.ts (RESILIENCE + RECOVERY) - review.ts (REVIEW + training/TR-* controls) - category-map.ts (category metadata Map) Split controls-library.ts (943 LOC) into domain files: - transfer-audit.ts (TRANSFER + AUDIT) - deletion-incident.ts (DELETION + INCIDENT) - subprocessor-tom.ts (SUBPROCESSOR + TOM) - contract-data-subject.ts (CONTRACT + DATA_SUBJECT) - security-governance.ts (SECURITY + GOVERNANCE) Both barrel files preserved their full public API. No consumer imports changed. Zero new TypeScript errors introduced (305 pre-existing errors unchanged). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
144 lines
4.1 KiB
TypeScript
144 lines
4.1 KiB
TypeScript
/**
|
|
* Controls Library
|
|
*
|
|
* Standard controls for vendor and processing activity compliance.
|
|
* Data is split by domain into sibling files — each <500 LOC.
|
|
* This file re-assembles them and provides all helper functions.
|
|
*/
|
|
|
|
import { Control, ControlDomain, ReviewFrequency, LocalizedText } from '../types'
|
|
|
|
import { TRANSFER_CONTROLS, AUDIT_CONTROLS } from './transfer-audit'
|
|
import { DELETION_CONTROLS, INCIDENT_CONTROLS } from './deletion-incident'
|
|
import { SUBPROCESSOR_CONTROLS, TOM_CONTROLS } from './subprocessor-tom'
|
|
import { CONTRACT_CONTROLS, DATA_SUBJECT_CONTROLS } from './contract-data-subject'
|
|
import { SECURITY_CONTROLS, GOVERNANCE_CONTROLS } from './security-governance'
|
|
|
|
// ==========================================
|
|
// ASSEMBLED CONTROLS LIBRARY
|
|
// ==========================================
|
|
|
|
export const CONTROLS_LIBRARY: Control[] = [
|
|
...TRANSFER_CONTROLS,
|
|
...AUDIT_CONTROLS,
|
|
...DELETION_CONTROLS,
|
|
...INCIDENT_CONTROLS,
|
|
...SUBPROCESSOR_CONTROLS,
|
|
...TOM_CONTROLS,
|
|
...CONTRACT_CONTROLS,
|
|
...DATA_SUBJECT_CONTROLS,
|
|
...SECURITY_CONTROLS,
|
|
...GOVERNANCE_CONTROLS,
|
|
]
|
|
|
|
// ==========================================
|
|
// HELPER FUNCTIONS
|
|
// ==========================================
|
|
|
|
/**
|
|
* Get all controls
|
|
*/
|
|
export function getAllControls(): Control[] {
|
|
return CONTROLS_LIBRARY
|
|
}
|
|
|
|
/**
|
|
* Get controls by domain
|
|
*/
|
|
export function getControlsByDomain(domain: ControlDomain): Control[] {
|
|
return CONTROLS_LIBRARY.filter((c) => c.domain === domain)
|
|
}
|
|
|
|
/**
|
|
* Get control by ID
|
|
*/
|
|
export function getControlById(id: string): Control | undefined {
|
|
return CONTROLS_LIBRARY.find((c) => c.id === id)
|
|
}
|
|
|
|
/**
|
|
* Get required controls
|
|
*/
|
|
export function getRequiredControls(): Control[] {
|
|
return CONTROLS_LIBRARY.filter((c) => c.isRequired)
|
|
}
|
|
|
|
/**
|
|
* Get controls by frequency
|
|
*/
|
|
export function getControlsByFrequency(frequency: ReviewFrequency): Control[] {
|
|
return CONTROLS_LIBRARY.filter((c) => c.defaultFrequency === frequency)
|
|
}
|
|
|
|
/**
|
|
* Get controls applicable to vendors
|
|
*/
|
|
export function getVendorControls(): Control[] {
|
|
return CONTROLS_LIBRARY.filter((c) =>
|
|
['TRANSFER', 'AUDIT', 'DELETION', 'INCIDENT', 'SUBPROCESSOR', 'TOM', 'CONTRACT', 'DATA_SUBJECT', 'SECURITY', 'GOVERNANCE'].includes(c.domain)
|
|
)
|
|
}
|
|
|
|
/**
|
|
* Get controls applicable to processing activities
|
|
*/
|
|
export function getProcessingActivityControls(): Control[] {
|
|
return CONTROLS_LIBRARY.filter((c) =>
|
|
['TOM', 'DATA_SUBJECT', 'GOVERNANCE', 'SECURITY'].includes(c.domain)
|
|
)
|
|
}
|
|
|
|
/**
|
|
* Group controls by domain
|
|
*/
|
|
export function getControlsGroupedByDomain(): Map<ControlDomain, Control[]> {
|
|
const grouped = new Map<ControlDomain, Control[]>()
|
|
|
|
for (const control of CONTROLS_LIBRARY) {
|
|
const existing = grouped.get(control.domain) || []
|
|
grouped.set(control.domain, [...existing, control])
|
|
}
|
|
|
|
return grouped
|
|
}
|
|
|
|
/**
|
|
* Get domain metadata
|
|
*/
|
|
export function getControlDomainMeta(domain: ControlDomain): LocalizedText {
|
|
const meta: Record<ControlDomain, LocalizedText> = {
|
|
TRANSFER: { de: 'Drittlandtransfer', en: 'Third Country Transfer' },
|
|
AUDIT: { de: 'Audit & Prüfung', en: 'Audit & Review' },
|
|
DELETION: { de: 'Löschung', en: 'Deletion' },
|
|
INCIDENT: { de: 'Incident Response', en: 'Incident Response' },
|
|
SUBPROCESSOR: { de: 'Unterauftragnehmer', en: 'Sub-Processors' },
|
|
TOM: { de: 'Technische/Org. Maßnahmen', en: 'Technical/Org. Measures' },
|
|
CONTRACT: { de: 'Vertragliche Grundlagen', en: 'Contractual Basics' },
|
|
DATA_SUBJECT: { de: 'Betroffenenrechte', en: 'Data Subject Rights' },
|
|
SECURITY: { de: 'Sicherheit', en: 'Security' },
|
|
GOVERNANCE: { de: 'Governance', en: 'Governance' },
|
|
}
|
|
|
|
return meta[domain]
|
|
}
|
|
|
|
/**
|
|
* Calculate control coverage
|
|
*/
|
|
export function calculateControlCoverage(
|
|
controlIds: string[],
|
|
domain?: ControlDomain
|
|
): { covered: number; total: number; percentage: number } {
|
|
const targetControls = domain
|
|
? getControlsByDomain(domain)
|
|
: getRequiredControls()
|
|
|
|
const covered = targetControls.filter((c) => controlIds.includes(c.id)).length
|
|
|
|
return {
|
|
covered,
|
|
total: targetControls.length,
|
|
percentage: targetControls.length > 0 ? Math.round((covered / targetControls.length) * 100) : 0,
|
|
}
|
|
}
|