Files
pikasTech-HWLAB/web/hwlab-cloud-web/src/config/runtime.ts
T
2026-07-14 12:43:46 +02:00

194 lines
6.8 KiB
TypeScript

import type { WorkbenchTraceTimelinePolicy } from "@/components/workbench/trace-lifecycle";
export interface DisplayTimeConfig {
timeZone: string;
locale: string;
label: string;
}
export interface OpenCodeFrameConfig {
url: string;
}
export interface WorkbenchDebugCapabilities {
isolatedKafka: boolean;
rawHwlabEventWindow: WorkbenchRawHwlabEventWindowCapability;
}
export interface WorkbenchRealtimeCapabilities {
liveKafkaSse: boolean;
kafkaRefreshReplay: boolean;
projectionRealtime: boolean;
}
export interface WorkbenchRawHwlabEventWindowCapability {
enabled: boolean;
maxEntries: number;
maxRetainedBytes: number;
}
const DEFAULT_DISPLAY_DATE_TIME_OPTIONS: Intl.DateTimeFormatOptions = {
year: "numeric",
month: "2-digit",
day: "2-digit",
hour: "2-digit",
minute: "2-digit",
second: "2-digit",
hour12: false
};
const TRACE_ID_PATTERN = /^[A-Za-z0-9_.:-]{3,160}$/u;
const TRACE_EXPLORER_TEMPLATE_TOKEN = "{trace_id}";
const TRACE_EXPLORER_TEMPLATE_PLACEHOLDER_PATTERN = /\{([A-Za-z0-9_:-]+)\}/gu;
export function displayTimeConfig(): DisplayTimeConfig {
const config = window.HWLAB_CLOUD_WEB_CONFIG?.displayTime;
const timeZone = requiredDisplayTimeField(config?.timeZone, "timeZone");
const locale = requiredDisplayTimeField(config?.locale, "locale");
const label = requiredDisplayTimeField(config?.label, "label");
validateDisplayTimeZone(timeZone);
validateDisplayLocale(locale);
return { timeZone, locale, label };
}
export function displayTimeLabel(): string {
return displayTimeConfig().label;
}
export function formatDisplayDateTime(value: string | Date, options: Intl.DateTimeFormatOptions = {}): string {
const date = value instanceof Date ? value : new Date(value);
if (!Number.isFinite(date.getTime())) return String(value);
const config = displayTimeConfig();
const dateTimeOptions = Object.keys(options).length > 0 ? options : DEFAULT_DISPLAY_DATE_TIME_OPTIONS;
return new Intl.DateTimeFormat(config.locale, { hour12: false, ...dateTimeOptions, timeZone: config.timeZone }).format(date);
}
export function formatDisplayClock(value: string | Date): string {
return formatDisplayDateTime(value, { hour: "2-digit", minute: "2-digit", second: "2-digit" });
}
function requiredDisplayTimeField(value: unknown, field: string): string {
if (typeof value !== "string" || value.trim().length === 0) {
throw new Error(`HWLAB Cloud Web displayTime.${field} is required`);
}
return value.trim();
}
function validateDisplayTimeZone(timeZone: string): void {
try {
new Intl.DateTimeFormat("en-US", { timeZone }).format(new Date(0));
} catch {
throw new Error(`HWLAB Cloud Web displayTime.timeZone is invalid: ${timeZone}`);
}
}
function validateDisplayLocale(locale: string): void {
try {
new Intl.DateTimeFormat(locale).format(new Date(0));
} catch {
throw new Error(`HWLAB Cloud Web displayTime.locale is invalid: ${locale}`);
}
}
export function workbenchTraceTimelinePolicy(): WorkbenchTraceTimelinePolicy {
const config = window.HWLAB_CLOUD_WEB_CONFIG?.workbench?.traceTimeline;
return {
autoExpandRunning: config?.autoExpandRunning === true,
autoCollapseTerminal: config?.autoCollapseTerminal === true
};
}
export function workbenchDebugCapabilities(): WorkbenchDebugCapabilities {
const features = window.HWLAB_CLOUD_WEB_CONFIG?.workbench?.debugCapabilities;
const rawWindow = features?.rawHwlabEventWindow;
if (
typeof features?.isolatedKafka !== "boolean" ||
typeof rawWindow?.enabled !== "boolean" ||
!Number.isInteger(rawWindow.maxEntries) || Number(rawWindow.maxEntries) <= 0 ||
!Number.isInteger(rawWindow.maxRetainedBytes) || Number(rawWindow.maxRetainedBytes) <= 0
) {
throw new Error("HWLAB Cloud Web workbench.debugCapabilities and rawHwlabEventWindow policy are required");
}
return {
isolatedKafka: features.isolatedKafka,
rawHwlabEventWindow: {
enabled: rawWindow.enabled,
maxEntries: Number(rawWindow.maxEntries),
maxRetainedBytes: Number(rawWindow.maxRetainedBytes)
}
};
}
export function workbenchRealtimeCapabilities(): WorkbenchRealtimeCapabilities {
const features = window.HWLAB_CLOUD_WEB_CONFIG?.workbench?.realtimeFeatures;
if (
typeof features?.liveKafkaSse !== "boolean" ||
typeof features?.kafkaRefreshReplay !== "boolean" ||
typeof features?.projectionRealtime !== "boolean"
) {
throw new Error("HWLAB Cloud Web workbench.realtimeFeatures are required");
}
return {
liveKafkaSse: features.liveKafkaSse,
kafkaRefreshReplay: features.kafkaRefreshReplay,
projectionRealtime: features.projectionRealtime
};
}
export function traceExplorerHref(traceId: string | null | undefined): string | null {
const safeTraceId = normalizedTraceId(traceId);
if (!safeTraceId) return null;
const template = window.HWLAB_CLOUD_WEB_CONFIG?.workbench?.traceExplorerUrlTemplate;
if (typeof template !== "string" || !isSafeTraceExplorerUrlTemplate(template)) return null;
const rendered = template.trim().replaceAll(TRACE_EXPLORER_TEMPLATE_TOKEN, encodeURIComponent(safeTraceId));
try {
const url = new URL(rendered, runtimeOrigin());
if (url.protocol !== "http:" && url.protocol !== "https:") return null;
return rendered;
} catch {
return null;
}
}
export function opencodeFrameUrl(): string | null {
const value = window.HWLAB_CLOUD_WEB_CONFIG?.opencode?.url;
if (typeof value !== "string") return null;
const trimmed = value.trim();
if (!isSafeOpencodeFrameUrl(trimmed)) return null;
return trimmed;
}
function normalizedTraceId(value: string | null | undefined): string | null {
const traceId = typeof value === "string" ? value.trim() : "";
return TRACE_ID_PATTERN.test(traceId) ? traceId : null;
}
function isSafeTraceExplorerUrlTemplate(template: string): boolean {
const trimmed = template.trim();
if (!trimmed.includes(TRACE_EXPLORER_TEMPLATE_TOKEN) || trimmed.startsWith("//")) return false;
const placeholders = Array.from(trimmed.matchAll(TRACE_EXPLORER_TEMPLATE_PLACEHOLDER_PATTERN), (match) => match[0]);
if (placeholders.length === 0 || placeholders.some((placeholder) => placeholder !== TRACE_EXPLORER_TEMPLATE_TOKEN)) return false;
try {
const rendered = trimmed.replaceAll(TRACE_EXPLORER_TEMPLATE_TOKEN, "trc_template_probe");
const url = new URL(rendered, runtimeOrigin());
return url.protocol === "http:" || url.protocol === "https:";
} catch {
return false;
}
}
function isSafeOpencodeFrameUrl(value: string): boolean {
if (!value || value.startsWith("//")) return false;
try {
const url = new URL(value, runtimeOrigin());
return url.protocol === "http:" || url.protocol === "https:";
} catch {
return false;
}
}
function runtimeOrigin(): string {
return typeof window.location?.origin === "string" && window.location.origin.length > 0
? window.location.origin
: "http://hwlab-cloud-web.local";
}