> */ private const DOMAIN_MAPPERS = [ BadgeDomain::AuditOutcome->value => Domains\AuditOutcomeBadge::class, BadgeDomain::AuditActorType->value => Domains\AuditActorTypeBadge::class, BadgeDomain::BaselineSnapshotFidelity->value => Domains\BaselineSnapshotFidelityBadge::class, BadgeDomain::BaselineSnapshotGapStatus->value => Domains\BaselineSnapshotGapStatusBadge::class, BadgeDomain::OperationRunStatus->value => Domains\OperationRunStatusBadge::class, BadgeDomain::OperationRunOutcome->value => Domains\OperationRunOutcomeBadge::class, BadgeDomain::BackupSetStatus->value => Domains\BackupSetStatusBadge::class, BadgeDomain::RestoreRunStatus->value => Domains\RestoreRunStatusBadge::class, BadgeDomain::RestoreCheckSeverity->value => Domains\RestoreCheckSeverityBadge::class, BadgeDomain::FindingStatus->value => Domains\FindingStatusBadge::class, BadgeDomain::FindingSeverity->value => Domains\FindingSeverityBadge::class, BadgeDomain::FindingExceptionStatus->value => Domains\FindingExceptionStatusBadge::class, BadgeDomain::FindingRiskGovernanceValidity->value => Domains\FindingRiskGovernanceValidityBadge::class, BadgeDomain::BooleanEnabled->value => Domains\BooleanEnabledBadge::class, BadgeDomain::BooleanHasErrors->value => Domains\BooleanHasErrorsBadge::class, BadgeDomain::TenantStatus->value => Domains\TenantStatusBadge::class, BadgeDomain::TenantAppStatus->value => Domains\TenantAppStatusBadge::class, BadgeDomain::TenantRbacStatus->value => Domains\TenantRbacStatusBadge::class, BadgeDomain::TenantPermissionStatus->value => Domains\TenantPermissionStatusBadge::class, BadgeDomain::PolicySnapshotMode->value => Domains\PolicySnapshotModeBadge::class, BadgeDomain::PolicyRestoreMode->value => Domains\PolicyRestoreModeBadge::class, BadgeDomain::PolicyRisk->value => Domains\PolicyRiskBadge::class, BadgeDomain::IgnoredAt->value => Domains\IgnoredAtBadge::class, BadgeDomain::RestorePreviewDecision->value => Domains\RestorePreviewDecisionBadge::class, BadgeDomain::RestoreResultStatus->value => Domains\RestoreResultStatusBadge::class, BadgeDomain::ProviderConnectionStatus->value => Domains\ProviderConnectionStatusBadge::class, BadgeDomain::ProviderConnectionHealth->value => Domains\ProviderConnectionHealthBadge::class, BadgeDomain::ManagedTenantOnboardingVerificationStatus->value => Domains\ManagedTenantOnboardingVerificationStatusBadge::class, BadgeDomain::VerificationCheckStatus->value => Domains\VerificationCheckStatusBadge::class, BadgeDomain::VerificationCheckSeverity->value => Domains\VerificationCheckSeverityBadge::class, BadgeDomain::VerificationReportOverall->value => Domains\VerificationReportOverallBadge::class, BadgeDomain::AlertDeliveryStatus->value => Domains\AlertDeliveryStatusBadge::class, BadgeDomain::AlertDestinationLastTestStatus->value => Domains\AlertDestinationLastTestStatusBadge::class, BadgeDomain::BaselineProfileStatus->value => Domains\BaselineProfileStatusBadge::class, BadgeDomain::FindingType->value => Domains\FindingTypeBadge::class, BadgeDomain::ReviewPackStatus->value => Domains\ReviewPackStatusBadge::class, BadgeDomain::EvidenceSnapshotStatus->value => Domains\EvidenceSnapshotStatusBadge::class, BadgeDomain::EvidenceCompleteness->value => Domains\EvidenceCompletenessBadge::class, BadgeDomain::TenantReviewStatus->value => Domains\TenantReviewStatusBadge::class, BadgeDomain::TenantReviewCompleteness->value => Domains\TenantReviewCompletenessStateBadge::class, BadgeDomain::SystemHealth->value => Domains\SystemHealthBadge::class, BadgeDomain::ReferenceResolutionState->value => Domains\ReferenceResolutionStateBadge::class, BadgeDomain::DiffRowStatus->value => Domains\DiffRowStatusBadge::class, ]; /** * @var array */ private static array $mapperCache = []; public static function spec(BadgeDomain $domain, mixed $value): BadgeSpec { $mapper = self::mapper($domain); if (! $mapper) { return BadgeSpec::unknown(); } try { return $mapper->spec($value); } catch (Throwable) { return BadgeSpec::unknown(); } } public static function mapper(BadgeDomain $domain): ?BadgeMapper { $key = $domain->value; if (array_key_exists($key, self::$mapperCache)) { return self::$mapperCache[$key]; } $mapper = self::buildMapper($domain); self::$mapperCache[$key] = $mapper; return $mapper; } public static function normalizeState(mixed $value): ?string { if ($value === null) { return null; } if ($value instanceof BackedEnum) { $value = $value->value; } if ($value instanceof Stringable) { $value = (string) $value; } if (is_bool($value)) { return $value ? 'true' : 'false'; } if (is_int($value) || is_float($value)) { return (string) $value; } if (! is_string($value)) { return null; } $normalized = strtolower(trim($value)); $normalized = str_replace([' ', '-'], '_', $normalized); return $normalized === '' ? null : $normalized; } public static function normalizeProviderConnectionStatus(mixed $value): ?string { $state = self::normalizeState($value); return match ($state) { 'granted', 'connected' => 'connected', 'consent_required', 'required', 'needs_admin_consent', 'needs_consent', 'unknown' => 'needs_consent', 'failed', 'revoked', 'blocked' => 'error', default => $state, }; } public static function normalizeProviderConnectionHealth(mixed $value): ?string { $state = self::normalizeState($value); return match ($state) { 'healthy' => 'ok', 'blocked', 'error' => 'down', default => $state, }; } public static function normalizeManagedTenantOnboardingVerificationStatus(mixed $value): ?string { $state = self::normalizeState($value); return match ($state) { 'unknown', 'not_started' => 'not_started', 'pending', 'in_progress' => 'in_progress', 'degraded', 'needs_attention' => 'needs_attention', 'blocked', 'error' => 'blocked', 'healthy', 'ready' => 'ready', default => $state, }; } public static function normalizeTenantLifecycle(mixed $value): ?string { $presentation = self::tenantLifecyclePresentation($value); return $presentation->isInvalidFallback ? null : $presentation->value; } public static function tenantLifecyclePresentation(mixed $value): TenantLifecyclePresentation { return TenantLifecyclePresentation::fromValue($value); } private static function buildMapper(BadgeDomain $domain): ?BadgeMapper { $mapperClass = self::DOMAIN_MAPPERS[$domain->value] ?? null; if (! $mapperClass) { return null; } if (! class_exists($mapperClass)) { return null; } $mapper = new $mapperClass; return $mapper instanceof BadgeMapper ? $mapper : null; } }