mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-09-10 02:25:56 +00:00
Centralize trimmed string normalization
This commit is contained in:
@@ -14,6 +14,7 @@ import {
|
||||
} from '@/components/shared/Table';
|
||||
import { EmptyState } from '@/components/shared/EmptyState';
|
||||
import { getSimpleStatusIndicator } from '@/utils/status';
|
||||
import { asTrimmedString } from '@/utils/stringUtils';
|
||||
import {
|
||||
formatSwarmClusterId,
|
||||
formatSwarmClusterSummary,
|
||||
@@ -77,8 +78,6 @@ type ResourcesListResponse = {
|
||||
};
|
||||
};
|
||||
|
||||
const normalize = (value?: string | null) => (value || '').trim();
|
||||
|
||||
const buildServicesUrl = (cluster: string, page: number) => {
|
||||
const params = new URLSearchParams();
|
||||
params.set('type', 'docker-service');
|
||||
@@ -120,8 +119,8 @@ const fetchAllServices = async (cluster: string): Promise<DockerServiceResource[
|
||||
};
|
||||
|
||||
const formatUpdate = (update?: DockerServiceUpdate) => {
|
||||
const state = normalize(update?.state);
|
||||
const message = normalize(update?.message);
|
||||
const state = asTrimmedString(update?.state) ?? '';
|
||||
const message = asTrimmedString(update?.message) ?? '';
|
||||
if (!state && !message) return '—';
|
||||
if (state && message) return `${state}: ${message}`;
|
||||
return state || message;
|
||||
@@ -133,7 +132,7 @@ const formatPorts = (ports?: DockerServicePort[]) => {
|
||||
.map((p) => {
|
||||
const target = typeof p.targetPort === 'number' ? String(p.targetPort) : '';
|
||||
const published = typeof p.publishedPort === 'number' ? String(p.publishedPort) : '';
|
||||
const proto = normalize(p.protocol) || 'tcp';
|
||||
const proto = asTrimmedString(p.protocol) || 'tcp';
|
||||
if (published && target) return `${published}->${target}/${proto}`;
|
||||
if (target) return `${target}/${proto}`;
|
||||
return '';
|
||||
@@ -146,7 +145,7 @@ export const SwarmServicesDrawer: Component<{ cluster: string; swarm?: SwarmInfo
|
||||
const [search, setSearch] = createSignal('');
|
||||
const drawerPresentation = getSwarmDrawerPresentation();
|
||||
|
||||
const clusterKey = createMemo(() => normalize(props.cluster));
|
||||
const clusterKey = createMemo(() => asTrimmedString(props.cluster) ?? '');
|
||||
|
||||
const [services] = createResource(
|
||||
clusterKey,
|
||||
@@ -158,18 +157,18 @@ export const SwarmServicesDrawer: Component<{ cluster: string; swarm?: SwarmInfo
|
||||
);
|
||||
|
||||
const filteredServices = createMemo(() => {
|
||||
const term = normalize(search()).toLowerCase();
|
||||
const term = (asTrimmedString(search()) ?? '').toLowerCase();
|
||||
return services()
|
||||
.filter((svc) => {
|
||||
if (!term) return true;
|
||||
const name = normalize(svc.name) || svc.id;
|
||||
const stack = normalize(svc.docker?.stack);
|
||||
const image = normalize(svc.docker?.image);
|
||||
const name = asTrimmedString(svc.name) || svc.id;
|
||||
const stack = asTrimmedString(svc.docker?.stack) ?? '';
|
||||
const image = asTrimmedString(svc.docker?.image) ?? '';
|
||||
return [name, stack, image].some((value) => value.toLowerCase().includes(term));
|
||||
})
|
||||
.sort((a, b) => {
|
||||
const aName = (normalize(a.name) || a.id).toLowerCase();
|
||||
const bName = (normalize(b.name) || b.id).toLowerCase();
|
||||
const aName = (asTrimmedString(a.name) || a.id).toLowerCase();
|
||||
const bName = (asTrimmedString(b.name) || b.id).toLowerCase();
|
||||
if (aName !== bName) return aName < bName ? -1 : 1;
|
||||
return a.id < b.id ? -1 : a.id > b.id ? 1 : 0;
|
||||
});
|
||||
@@ -177,9 +176,9 @@ export const SwarmServicesDrawer: Component<{ cluster: string; swarm?: SwarmInfo
|
||||
|
||||
const swarm = createMemo(() => props.swarm);
|
||||
const clusterName = createMemo(
|
||||
() => normalize(swarm()?.clusterName) || normalize(swarm()?.clusterId) || clusterKey(),
|
||||
() => asTrimmedString(swarm()?.clusterName) || asTrimmedString(swarm()?.clusterId) || clusterKey(),
|
||||
);
|
||||
const clusterId = createMemo(() => normalize(swarm()?.clusterId));
|
||||
const clusterId = createMemo(() => asTrimmedString(swarm()?.clusterId) ?? '');
|
||||
|
||||
return (
|
||||
<div class="space-y-3">
|
||||
@@ -211,20 +210,20 @@ export const SwarmServicesDrawer: Component<{ cluster: string; swarm?: SwarmInfo
|
||||
|
||||
<Show
|
||||
when={
|
||||
normalize(swarm()?.nodeRole) ||
|
||||
normalize(swarm()?.localState) ||
|
||||
asTrimmedString(swarm()?.nodeRole) ||
|
||||
asTrimmedString(swarm()?.localState) ||
|
||||
typeof swarm()?.controlAvailable === 'boolean'
|
||||
}
|
||||
>
|
||||
<div class="mt-2 flex flex-wrap gap-2 text-[11px]">
|
||||
<Show when={normalize(swarm()?.nodeRole)}>
|
||||
<Show when={asTrimmedString(swarm()?.nodeRole)}>
|
||||
<span class="inline-flex items-center rounded bg-surface-alt px-2 py-0.5 text-base-content">
|
||||
{formatSwarmRoleLabel(normalize(swarm()?.nodeRole))}
|
||||
{formatSwarmRoleLabel(asTrimmedString(swarm()?.nodeRole))}
|
||||
</span>
|
||||
</Show>
|
||||
<Show when={normalize(swarm()?.localState)}>
|
||||
<Show when={asTrimmedString(swarm()?.localState)}>
|
||||
<span class="inline-flex items-center rounded bg-surface-alt px-2 py-0.5 text-base-content">
|
||||
{formatSwarmStateLabel(normalize(swarm()?.localState))}
|
||||
{formatSwarmStateLabel(asTrimmedString(swarm()?.localState))}
|
||||
</span>
|
||||
</Show>
|
||||
<Show when={typeof swarm()?.controlAvailable === 'boolean'}>
|
||||
@@ -235,9 +234,9 @@ export const SwarmServicesDrawer: Component<{ cluster: string; swarm?: SwarmInfo
|
||||
</div>
|
||||
</Show>
|
||||
|
||||
<Show when={normalize(swarm()?.error)}>
|
||||
<Show when={asTrimmedString(swarm()?.error)}>
|
||||
<div class="mt-2 rounded border border-amber-200 bg-amber-50 px-2 py-1.5 text-[10px] text-amber-800 dark:border-amber-700 dark:bg-amber-900 dark:text-amber-200">
|
||||
{normalize(swarm()?.error)}
|
||||
{asTrimmedString(swarm()?.error)}
|
||||
</div>
|
||||
</Show>
|
||||
</Card>
|
||||
@@ -287,10 +286,10 @@ export const SwarmServicesDrawer: Component<{ cluster: string; swarm?: SwarmInfo
|
||||
<TableBody class="divide-y divide-border-subtle">
|
||||
<For each={filteredServices()}>
|
||||
{(svc) => {
|
||||
const name = () => normalize(svc.name) || svc.id;
|
||||
const stack = () => normalize(svc.docker?.stack) || '—';
|
||||
const image = () => normalize(svc.docker?.image) || '—';
|
||||
const mode = () => normalize(svc.docker?.mode) || '—';
|
||||
const name = () => asTrimmedString(svc.name) || svc.id;
|
||||
const stack = () => asTrimmedString(svc.docker?.stack) || '—';
|
||||
const image = () => asTrimmedString(svc.docker?.image) || '—';
|
||||
const mode = () => asTrimmedString(svc.docker?.mode) || '—';
|
||||
const desired = () => svc.docker?.desiredTasks ?? 0;
|
||||
const running = () => svc.docker?.runningTasks ?? 0;
|
||||
const update = () => formatUpdate(svc.docker?.serviceUpdate);
|
||||
|
||||
@@ -22,6 +22,7 @@ import {
|
||||
getK8sDeploymentsLoadingState,
|
||||
} from '@/utils/k8sDeploymentPresentation';
|
||||
import { getSimpleStatusIndicator } from '@/utils/status';
|
||||
import { asTrimmedString } from '@/utils/stringUtils';
|
||||
|
||||
const PAGE_LIMIT = 100;
|
||||
const MAX_PAGES = 20;
|
||||
@@ -46,8 +47,6 @@ type ResourcesListResponse = {
|
||||
};
|
||||
};
|
||||
|
||||
const normalize = (value?: string | null) => (value || '').trim();
|
||||
|
||||
const buildDeploymentsUrl = (cluster: string, page: number) => {
|
||||
const params = new URLSearchParams();
|
||||
params.set('type', 'k8s-deployment');
|
||||
@@ -100,7 +99,7 @@ export const K8sDeploymentsDrawer: Component<{
|
||||
const [lastAppliedNamespace, setLastAppliedNamespace] = createSignal('');
|
||||
const drawerPresentation = getK8sDeploymentsDrawerPresentation();
|
||||
|
||||
const clusterName = createMemo(() => normalize(props.cluster));
|
||||
const clusterName = createMemo(() => asTrimmedString(props.cluster) ?? '');
|
||||
|
||||
const [deployments] = createResource(
|
||||
clusterName,
|
||||
@@ -114,7 +113,7 @@ export const K8sDeploymentsDrawer: Component<{
|
||||
const namespaceOptions = createMemo(() => {
|
||||
const set = new Set<string>();
|
||||
for (const dep of deployments()) {
|
||||
const ns = normalize(dep.kubernetes?.namespace);
|
||||
const ns = asTrimmedString(dep.kubernetes?.namespace) ?? '';
|
||||
if (ns) set.add(ns);
|
||||
}
|
||||
return Array.from(set).sort((a, b) => a.localeCompare(b));
|
||||
@@ -122,9 +121,10 @@ export const K8sDeploymentsDrawer: Component<{
|
||||
|
||||
createEffect(() => {
|
||||
// Allow other drawer tabs (e.g., Namespaces) to prefill the namespace filter.
|
||||
const next = normalize(props.initialNamespace);
|
||||
const next = asTrimmedString(props.initialNamespace) ?? '';
|
||||
if (!next) return;
|
||||
if (next.toLowerCase() === normalize(lastAppliedNamespace()).toLowerCase()) return;
|
||||
if (next.toLowerCase() === (asTrimmedString(lastAppliedNamespace()) ?? '').toLowerCase())
|
||||
return;
|
||||
const exists = namespaceOptions().some((ns) => ns.toLowerCase() === next.toLowerCase());
|
||||
if (exists) {
|
||||
setNamespace(next);
|
||||
@@ -133,7 +133,7 @@ export const K8sDeploymentsDrawer: Component<{
|
||||
});
|
||||
|
||||
createEffect(() => {
|
||||
const current = normalize(namespace());
|
||||
const current = asTrimmedString(namespace()) ?? '';
|
||||
if (!current) return;
|
||||
const exists = namespaceOptions().some((ns) => ns.toLowerCase() === current.toLowerCase());
|
||||
if (!exists) {
|
||||
@@ -142,19 +142,19 @@ export const K8sDeploymentsDrawer: Component<{
|
||||
});
|
||||
|
||||
const filteredDeployments = createMemo(() => {
|
||||
const ns = normalize(namespace());
|
||||
const term = normalize(search()).toLowerCase();
|
||||
const ns = asTrimmedString(namespace()) ?? '';
|
||||
const term = (asTrimmedString(search()) ?? '').toLowerCase();
|
||||
|
||||
return deployments()
|
||||
.filter((dep) => {
|
||||
if (ns && normalize(dep.kubernetes?.namespace) !== ns) return false;
|
||||
if (ns && (asTrimmedString(dep.kubernetes?.namespace) ?? '') !== ns) return false;
|
||||
if (!term) return true;
|
||||
const name = normalize(dep.name) || dep.id;
|
||||
const name = asTrimmedString(dep.name) || dep.id;
|
||||
return name.toLowerCase().includes(term);
|
||||
})
|
||||
.sort((a, b) => {
|
||||
const aName = (normalize(a.name) || a.id).toLowerCase();
|
||||
const bName = (normalize(b.name) || b.id).toLowerCase();
|
||||
const aName = (asTrimmedString(a.name) || a.id).toLowerCase();
|
||||
const bName = (asTrimmedString(b.name) || b.id).toLowerCase();
|
||||
if (aName !== bName) return aName < bName ? -1 : 1;
|
||||
return a.id < b.id ? -1 : a.id > b.id ? 1 : 0;
|
||||
});
|
||||
@@ -167,7 +167,7 @@ export const K8sDeploymentsDrawer: Component<{
|
||||
buildWorkloadsPath({
|
||||
type: 'pod',
|
||||
context: cluster,
|
||||
namespace: normalize(ns) || null,
|
||||
namespace: asTrimmedString(ns) || null,
|
||||
}),
|
||||
);
|
||||
};
|
||||
@@ -256,8 +256,8 @@ export const K8sDeploymentsDrawer: Component<{
|
||||
<TableBody class="divide-y divide-border-subtle">
|
||||
<For each={filteredDeployments()}>
|
||||
{(dep) => {
|
||||
const name = () => normalize(dep.name) || dep.id;
|
||||
const ns = () => normalize(dep.kubernetes?.namespace) || '—';
|
||||
const name = () => asTrimmedString(dep.name) || dep.id;
|
||||
const ns = () => asTrimmedString(dep.kubernetes?.namespace) || '—';
|
||||
const desired = () => dep.kubernetes?.desiredReplicas ?? 0;
|
||||
const updated = () => dep.kubernetes?.updatedReplicas ?? 0;
|
||||
const ready = () => dep.kubernetes?.readyReplicas ?? 0;
|
||||
|
||||
@@ -22,6 +22,7 @@ import {
|
||||
getK8sNamespacesLoadingState,
|
||||
} from '@/utils/k8sNamespacePresentation';
|
||||
import { getNamespaceCountsIndicator, type NamespaceCounts } from '@/utils/k8sStatusPresentation';
|
||||
import { asTrimmedString } from '@/utils/stringUtils';
|
||||
|
||||
type NamespaceRow = {
|
||||
namespace: string;
|
||||
@@ -34,8 +35,6 @@ type NamespacesResponse = {
|
||||
data: NamespaceRow[];
|
||||
};
|
||||
|
||||
const normalize = (value?: string | null) => (value || '').trim();
|
||||
|
||||
const formatInteger = (value?: number | null): string => {
|
||||
const n = Number(value ?? 0);
|
||||
if (!Number.isFinite(n)) return '0';
|
||||
@@ -50,7 +49,7 @@ export const K8sNamespacesDrawer: Component<{
|
||||
const [search, setSearch] = createSignal('');
|
||||
const drawerPresentation = getK8sNamespacesDrawerPresentation();
|
||||
|
||||
const clusterName = createMemo(() => normalize(props.cluster));
|
||||
const clusterName = createMemo(() => asTrimmedString(props.cluster) ?? '');
|
||||
|
||||
const [namespaces] = createResource(
|
||||
clusterName,
|
||||
@@ -75,7 +74,7 @@ export const K8sNamespacesDrawer: Component<{
|
||||
const rows = createMemo(() => (Array.isArray(namespaces()?.data) ? namespaces()!.data : []));
|
||||
|
||||
const filteredRows = createMemo(() => {
|
||||
const term = normalize(search()).toLowerCase();
|
||||
const term = (asTrimmedString(search()) ?? '').toLowerCase();
|
||||
if (!term) return rows();
|
||||
return rows().filter((row) => row.namespace.toLowerCase().includes(term));
|
||||
});
|
||||
@@ -87,7 +86,7 @@ export const K8sNamespacesDrawer: Component<{
|
||||
buildWorkloadsPath({
|
||||
type: 'pod',
|
||||
context: cluster,
|
||||
namespace: namespace ? normalize(namespace) : null,
|
||||
namespace: asTrimmedString(namespace) ?? null,
|
||||
}),
|
||||
);
|
||||
};
|
||||
|
||||
@@ -14,6 +14,7 @@ import {
|
||||
import { EmptyState } from '@/components/shared/EmptyState';
|
||||
import { formatBytes, formatRelativeTime } from '@/utils/format';
|
||||
import { getServiceHealthPresentation } from '@/utils/serviceHealthPresentation';
|
||||
import { asTrimmedString } from '@/utils/stringUtils';
|
||||
import {
|
||||
getPMGDetailsDrawerPresentation,
|
||||
PMG_DETAILS_FAILURE_STATE_TITLE,
|
||||
@@ -121,8 +122,6 @@ type PMGInstanceDrawerProps = {
|
||||
resourceName?: string;
|
||||
};
|
||||
|
||||
const normalize = (value?: string | null) => (value || '').trim();
|
||||
|
||||
const formatCompact = (value?: number | null): string => {
|
||||
if (value === undefined || value === null || Number.isNaN(value)) return '—';
|
||||
if (value >= 1_000_000) return `${(value / 1_000_000).toFixed(1)}M`;
|
||||
@@ -135,7 +134,7 @@ export const PMGInstanceDrawer: Component<PMGInstanceDrawerProps> = (props) => {
|
||||
const [searchDomain, setSearchDomain] = createSignal('');
|
||||
const drawerPresentation = getPMGDetailsDrawerPresentation();
|
||||
|
||||
const resourceId = createMemo(() => normalize(props.resourceId));
|
||||
const resourceId = createMemo(() => asTrimmedString(props.resourceId) ?? '');
|
||||
|
||||
const [resource] = createResource(
|
||||
resourceId,
|
||||
@@ -174,7 +173,7 @@ export const PMGInstanceDrawer: Component<PMGInstanceDrawerProps> = (props) => {
|
||||
|
||||
const relayDomains = createMemo(() => {
|
||||
const rows = pmg()?.relayDomains ?? [];
|
||||
const term = normalize(searchRelay()).toLowerCase();
|
||||
const term = (asTrimmedString(searchRelay()) ?? '').toLowerCase();
|
||||
if (!term) return rows;
|
||||
return rows.filter(
|
||||
(row) =>
|
||||
@@ -184,7 +183,7 @@ export const PMGInstanceDrawer: Component<PMGInstanceDrawerProps> = (props) => {
|
||||
|
||||
const domainStats = createMemo(() => {
|
||||
const rows = pmg()?.domainStats ?? [];
|
||||
const term = normalize(searchDomain()).toLowerCase();
|
||||
const term = (asTrimmedString(searchDomain()) ?? '').toLowerCase();
|
||||
const filtered = term ? rows.filter((row) => row.domain.toLowerCase().includes(term)) : rows;
|
||||
return [...filtered].sort((a, b) => (b.mailCount || 0) - (a.mailCount || 0));
|
||||
});
|
||||
@@ -192,7 +191,7 @@ export const PMGInstanceDrawer: Component<PMGInstanceDrawerProps> = (props) => {
|
||||
const spamBuckets = createMemo(() => {
|
||||
const rows = pmg()?.spamDistribution ?? [];
|
||||
const parsed = rows
|
||||
.map((row) => ({ bucket: normalize(row.bucket), count: Number(row.count || 0) }))
|
||||
.map((row) => ({ bucket: asTrimmedString(row.bucket) ?? '', count: Number(row.count || 0) }))
|
||||
.filter((row) => row.bucket.length > 0);
|
||||
return parsed.sort((a, b) => a.bucket.localeCompare(b.bucket));
|
||||
});
|
||||
@@ -240,7 +239,7 @@ export const PMGInstanceDrawer: Component<PMGInstanceDrawerProps> = (props) => {
|
||||
<div class="flex items-start justify-between gap-4">
|
||||
<div class="min-w-0">
|
||||
<div class="text-sm font-semibold text-base-content truncate">
|
||||
{normalize(props.resourceName) ||
|
||||
{asTrimmedString(props.resourceName) ||
|
||||
resource()?.name ||
|
||||
drawerPresentation.defaultResourceName}
|
||||
</div>
|
||||
|
||||
@@ -1917,7 +1917,7 @@ describe('frontend resource type boundaries', () => {
|
||||
expect(resourceRelationshipPresentationSource).toContain('formatConfidencePercentage');
|
||||
expect(resourceCorrelationPresentationSource).toContain('formatConfidencePercentage');
|
||||
expect(resourceCorrelationPresentationSource).toContain('humanizeArrowDelimitedLabel');
|
||||
expect(resourceCorrelationPresentationSource).toContain('formatTrimmedLabel');
|
||||
expect(resourceCorrelationPresentationSource).toContain('asTrimmedString');
|
||||
expect(confidencePresentationSource).toContain('formatConfidencePercentage');
|
||||
expect(confidencePresentationSource).toContain('formatConfidenceLabel');
|
||||
expect(approvalPresentationSource).toContain('getResourceApprovalLevelLabel');
|
||||
@@ -1926,7 +1926,6 @@ describe('frontend resource type boundaries', () => {
|
||||
expect(resourceChangePresentationSource).toContain('humanizeToken');
|
||||
expect(resourceRelationshipPresentationSource).toContain('humanizeToken');
|
||||
expect(textPresentationSource).toContain('humanizeArrowDelimitedLabel');
|
||||
expect(textPresentationSource).toContain('formatTrimmedLabel');
|
||||
expect(resourceCorrelationPresentationSource).not.toContain('formatResourceCorrelationEndpointLabel');
|
||||
expect(resourceCorrelationPresentationSource).not.toContain(
|
||||
"replace(/\\s*->\\s*/g, ' → ')",
|
||||
@@ -1934,6 +1933,15 @@ describe('frontend resource type boundaries', () => {
|
||||
expect(resourceDetailDrawerSource).toContain('humanizeToken');
|
||||
expect(textPresentationSource).toContain('humanizeToken');
|
||||
expect(textPresentationSource).toContain('formatIdentifierLabel');
|
||||
expect(resourceCorrelationPresentationSource).not.toContain('formatTrimmedLabel');
|
||||
expect(swarmServicesDrawerSource).toContain('asTrimmedString');
|
||||
expect(swarmServicesDrawerSource).not.toContain("const normalize = (value?: string | null) => (value || '').trim();");
|
||||
expect(pmgInstanceDrawerSource).toContain('asTrimmedString');
|
||||
expect(pmgInstanceDrawerSource).not.toContain("const normalize = (value?: string | null) => (value || '').trim();");
|
||||
expect(k8sNamespacesDrawerSource).toContain('asTrimmedString');
|
||||
expect(k8sNamespacesDrawerSource).not.toContain("const normalize = (value?: string | null) => (value || '').trim();");
|
||||
expect(k8sDeploymentsDrawerSource).toContain('asTrimmedString');
|
||||
expect(k8sDeploymentsDrawerSource).not.toContain("const normalize = (value?: string | null) => (value || '').trim();");
|
||||
expect(messageItemSource).toContain('formatIdentifierLabel');
|
||||
expect(toolExecutionBlockSource).toContain('formatIdentifierLabel');
|
||||
expect(aiChatSource).toContain('formatIdentifierLabel');
|
||||
|
||||
@@ -2,7 +2,6 @@ import { describe, expect, it } from 'vitest';
|
||||
|
||||
import {
|
||||
formatIdentifierLabel,
|
||||
formatTrimmedLabel,
|
||||
humanizeArrowDelimitedLabel,
|
||||
humanizeToken,
|
||||
titleCaseDelimitedLabel,
|
||||
@@ -32,11 +31,6 @@ describe('textPresentation', () => {
|
||||
expect(formatIdentifierLabel(undefined, { fallback: 'Unknown' })).toBe('Unknown');
|
||||
});
|
||||
|
||||
it('formats trimmed labels with a fallback', () => {
|
||||
expect(formatTrimmedLabel(' endpoint ', { fallback: 'Unknown resource' })).toBe('endpoint');
|
||||
expect(formatTrimmedLabel('', { fallback: 'Unknown resource' })).toBe('Unknown resource');
|
||||
});
|
||||
|
||||
it('title-cases delimited labels with configurable separators', () => {
|
||||
expect(titleCaseDelimitedLabel('docker_host')).toBe('Docker Host');
|
||||
expect(titleCaseDelimitedLabel('IP_address', { preserveShortAllCaps: true })).toBe(
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import type { ResourceCorrelation } from '@/types/aiIntelligence';
|
||||
import { formatDurationMs } from '@/utils/patrolFormat';
|
||||
import { formatConfidencePercentage } from '@/utils/confidencePresentation';
|
||||
import { formatTrimmedLabel, humanizeArrowDelimitedLabel } from '@/utils/textPresentation';
|
||||
import { asTrimmedString } from '@/utils/stringUtils';
|
||||
import { humanizeArrowDelimitedLabel } from '@/utils/textPresentation';
|
||||
|
||||
const parseGoDurationMs = (value: string): number | null => {
|
||||
const normalized = value.trim();
|
||||
@@ -35,11 +36,12 @@ export function formatResourceCorrelationEndpoint(
|
||||
correlation: ResourceCorrelation,
|
||||
role: 'source' | 'target',
|
||||
): string {
|
||||
return formatTrimmedLabel(
|
||||
role === 'source'
|
||||
? correlation.source_name || correlation.source_id
|
||||
: correlation.target_name || correlation.target_id,
|
||||
{ fallback: 'Unknown resource' },
|
||||
return (
|
||||
asTrimmedString(
|
||||
role === 'source'
|
||||
? correlation.source_name || correlation.source_id
|
||||
: correlation.target_name || correlation.target_id,
|
||||
) || 'Unknown resource'
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -15,10 +15,6 @@ export interface TitleCaseLabelOptions {
|
||||
separators?: RegExp;
|
||||
}
|
||||
|
||||
export interface TrimmedLabelOptions {
|
||||
fallback?: string;
|
||||
}
|
||||
|
||||
export function humanizeToken(value?: string, options?: HumanizeTokenOptions): string {
|
||||
const normalized = (value || '').trim();
|
||||
if (!normalized) {
|
||||
@@ -87,14 +83,6 @@ export function titleCaseDelimitedLabel(
|
||||
.join(' ');
|
||||
}
|
||||
|
||||
export function formatTrimmedLabel(
|
||||
value?: string,
|
||||
options?: TrimmedLabelOptions,
|
||||
): string {
|
||||
const normalized = (value || '').trim();
|
||||
return normalized || options?.fallback || '';
|
||||
}
|
||||
|
||||
export function humanizeArrowDelimitedLabel(
|
||||
value?: string,
|
||||
options?: HumanizeTokenOptions,
|
||||
|
||||
Reference in New Issue
Block a user