67 lines
2.6 KiB
TypeScript
67 lines
2.6 KiB
TypeScript
export function asRecord(value: unknown): Record<string, unknown> | null {
|
|
return value && typeof value === "object" ? value as Record<string, unknown> : null;
|
|
}
|
|
|
|
export function nonEmptyString(value: unknown): string | null {
|
|
if (typeof value !== "string") return null;
|
|
const text = value.trim();
|
|
return text ? text : null;
|
|
}
|
|
|
|
export function firstNonEmptyString(...values: unknown[]): string | null {
|
|
for (const value of values) {
|
|
const text = nonEmptyString(value);
|
|
if (text) return text;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
export function normalizeWorkbenchConversationId(value: unknown): string | null {
|
|
const text = nonEmptyString(value);
|
|
if (!text) return null;
|
|
return /^cnv_[A-Za-z0-9_.:-]+$/u.test(text) ? text : null;
|
|
}
|
|
|
|
export function normalizeWorkbenchSessionId(value: unknown): string | null {
|
|
const text = nonEmptyString(value);
|
|
if (!text) return null;
|
|
return /^ses_[A-Za-z0-9_.:-]+$/u.test(text) ? text : null;
|
|
}
|
|
|
|
export function normalizeWorkbenchSessionRouteId(value: unknown): string | null {
|
|
const text = nonEmptyString(value);
|
|
if (!text) return null;
|
|
return normalizeWorkbenchSessionId(text) ?? normalizeWorkbenchConversationId(text);
|
|
}
|
|
|
|
export function workbenchSessionPath(sessionId: string): string {
|
|
return `/workbench/sessions/${encodeURIComponent(sessionId)}`;
|
|
}
|
|
|
|
export function nextProtocolId(prefix: string): string {
|
|
const cryptoApi = typeof crypto !== "undefined" ? crypto : null;
|
|
const suffix = cryptoApi?.randomUUID ? cryptoApi.randomUUID().replace(/-/gu, "").slice(0, 16) : `${Date.now().toString(36)}${Math.random().toString(36).slice(2, 8)}`;
|
|
return `${prefix}_${suffix}`;
|
|
}
|
|
|
|
export function statusTone(status: string | undefined | null): string {
|
|
const text = String(status ?? "").toLowerCase();
|
|
if (["completed", "ok", "healthy", "ready", "active"].includes(text)) return "ok";
|
|
if (["running", "accepted", "pending", "processing"].includes(text)) return "pending";
|
|
if (["warn", "warning", "degraded", "build_time_unavailable"].includes(text)) return "warn";
|
|
if (["blocked", "stale"].includes(text)) return "blocked";
|
|
if (["failed", "error", "timeout", "canceled", "cancelled"].includes(text)) return "error";
|
|
return "pending";
|
|
}
|
|
|
|
export function formatRelativeTime(value: string | null | undefined): string {
|
|
if (!value) return "未记录";
|
|
const date = new Date(value);
|
|
if (!Number.isFinite(date.getTime())) return value;
|
|
const delta = Date.now() - date.getTime();
|
|
if (delta < 60_000) return "刚刚";
|
|
if (delta < 3_600_000) return `${Math.round(delta / 60_000)} 分钟前`;
|
|
if (delta < 86_400_000) return `${Math.round(delta / 3_600_000)} 小时前`;
|
|
return date.toLocaleString();
|
|
}
|