fix: 修复 Workbench L1 页面启动卡死
This commit is contained in:
@@ -0,0 +1,22 @@
|
||||
import { describe, expect, test } from "bun:test";
|
||||
|
||||
import { parseWorkbenchNativeRuntimeConfig } from "../vite.config";
|
||||
|
||||
const validConfig = {
|
||||
displayTime: { timeZone: "Asia/Shanghai", locale: "zh-CN", label: "北京时间" },
|
||||
workbench: {
|
||||
realtimeFeatures: { liveKafkaSse: false, kafkaRefreshReplay: false, projectionRealtime: true },
|
||||
debugCapabilities: { isolatedKafka: false, rawHwlabEventWindow: { enabled: false, maxEntries: 1, maxRetainedBytes: 1 } },
|
||||
traceTimeline: { autoExpandRunning: false, autoCollapseTerminal: false }
|
||||
}
|
||||
};
|
||||
|
||||
describe("Workbench native Vite runtime config", () => {
|
||||
test("accepts the YAML-rendered runtime contract", () => {
|
||||
expect(parseWorkbenchNativeRuntimeConfig(JSON.stringify(validConfig))).toEqual(validConfig);
|
||||
});
|
||||
|
||||
test("fails closed when required runtime capabilities are missing", () => {
|
||||
expect(() => parseWorkbenchNativeRuntimeConfig(JSON.stringify({ displayTime: validConfig.displayTime, workbench: {} }))).toThrow("workbench.realtimeFeatures");
|
||||
});
|
||||
});
|
||||
@@ -2,11 +2,12 @@ import { Agent } from "node:http";
|
||||
import { fileURLToPath, URL } from "node:url";
|
||||
|
||||
import vue from "@vitejs/plugin-vue";
|
||||
import { defineConfig, loadEnv } from "vite";
|
||||
import { defineConfig, loadEnv, type Plugin } from "vite";
|
||||
|
||||
export default defineConfig(({ mode }) => {
|
||||
const env = loadEnv(mode, process.cwd(), "");
|
||||
const nativeApiUrl = String(process.env.WORKBENCH_NATIVE_API_URL ?? env.WORKBENCH_NATIVE_API_URL ?? "").trim();
|
||||
const nativeRuntimeConfig = String(process.env.WORKBENCH_WEB_RUNTIME_CONFIG ?? env.WORKBENCH_WEB_RUNTIME_CONFIG ?? "").trim();
|
||||
const nativeCaseRunEnabled = (process.env.HWLAB_CASERUN_NATIVE_TEST ?? env.HWLAB_CASERUN_NATIVE_TEST) === "1";
|
||||
const nativeCaseRunTarget = process.env.HWLAB_CASERUN_NATIVE_URL ?? env.HWLAB_CASERUN_NATIVE_URL ?? "http://127.0.0.1:4316";
|
||||
const nativeCaseRunAgent = new Agent({ keepAlive: true });
|
||||
@@ -14,9 +15,10 @@ export default defineConfig(({ mode }) => {
|
||||
if (nativeApiUrl && nativeCaseRunEnabled) {
|
||||
throw new Error("Workbench and CaseRun native modes cannot share one Vite server");
|
||||
}
|
||||
if (nativeApiUrl && !nativeRuntimeConfig) throw new Error("WORKBENCH_WEB_RUNTIME_CONFIG is required in Workbench native mode");
|
||||
|
||||
return {
|
||||
plugins: [vue()],
|
||||
plugins: [...(nativeApiUrl ? [nativeRuntimeConfigPlugin(nativeRuntimeConfig)] : []), vue()],
|
||||
resolve: {
|
||||
alias: {
|
||||
"@": fileURLToPath(new URL("./src", import.meta.url))
|
||||
@@ -64,3 +66,48 @@ export default defineConfig(({ mode }) => {
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
function nativeRuntimeConfigPlugin(serialized: string): Plugin {
|
||||
const config = parseWorkbenchNativeRuntimeConfig(serialized);
|
||||
const inlineJson = JSON.stringify(config).replace(/</gu, "\\u003c");
|
||||
return {
|
||||
name: "hwlab-workbench-native-runtime-config",
|
||||
transformIndexHtml() {
|
||||
return [{ tag: "script", injectTo: "head-prepend", children: `window.HWLAB_CLOUD_WEB_CONFIG=${inlineJson};` }];
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export function parseWorkbenchNativeRuntimeConfig(serialized: string): Record<string, unknown> {
|
||||
let value: unknown;
|
||||
try { value = JSON.parse(serialized); }
|
||||
catch { throw new Error("WORKBENCH_WEB_RUNTIME_CONFIG must be valid JSON"); }
|
||||
const config = record(value, "WORKBENCH_WEB_RUNTIME_CONFIG");
|
||||
const displayTime = record(config.displayTime, "WORKBENCH_WEB_RUNTIME_CONFIG.displayTime");
|
||||
requiredString(displayTime.timeZone, "displayTime.timeZone");
|
||||
requiredString(displayTime.locale, "displayTime.locale");
|
||||
requiredString(displayTime.label, "displayTime.label");
|
||||
const workbench = record(config.workbench, "WORKBENCH_WEB_RUNTIME_CONFIG.workbench");
|
||||
const realtime = record(workbench.realtimeFeatures, "workbench.realtimeFeatures");
|
||||
requiredBoolean(realtime.liveKafkaSse, "workbench.realtimeFeatures.liveKafkaSse");
|
||||
requiredBoolean(realtime.kafkaRefreshReplay, "workbench.realtimeFeatures.kafkaRefreshReplay");
|
||||
requiredBoolean(realtime.projectionRealtime, "workbench.realtimeFeatures.projectionRealtime");
|
||||
const debug = record(workbench.debugCapabilities, "workbench.debugCapabilities");
|
||||
requiredBoolean(debug.isolatedKafka, "workbench.debugCapabilities.isolatedKafka");
|
||||
const rawWindow = record(debug.rawHwlabEventWindow, "workbench.debugCapabilities.rawHwlabEventWindow");
|
||||
requiredBoolean(rawWindow.enabled, "workbench.debugCapabilities.rawHwlabEventWindow.enabled");
|
||||
requiredPositiveInteger(rawWindow.maxEntries, "workbench.debugCapabilities.rawHwlabEventWindow.maxEntries");
|
||||
requiredPositiveInteger(rawWindow.maxRetainedBytes, "workbench.debugCapabilities.rawHwlabEventWindow.maxRetainedBytes");
|
||||
const traceTimeline = record(workbench.traceTimeline, "workbench.traceTimeline");
|
||||
requiredBoolean(traceTimeline.autoExpandRunning, "workbench.traceTimeline.autoExpandRunning");
|
||||
requiredBoolean(traceTimeline.autoCollapseTerminal, "workbench.traceTimeline.autoCollapseTerminal");
|
||||
return config;
|
||||
}
|
||||
|
||||
function record(value: unknown, path: string): Record<string, unknown> {
|
||||
if (!value || typeof value !== "object" || Array.isArray(value)) throw new Error(`${path} must be an object`);
|
||||
return value as Record<string, unknown>;
|
||||
}
|
||||
function requiredString(value: unknown, path: string): void { if (typeof value !== "string" || !value.trim()) throw new Error(`${path} must be a non-empty string`); }
|
||||
function requiredBoolean(value: unknown, path: string): void { if (typeof value !== "boolean") throw new Error(`${path} must be a boolean`); }
|
||||
function requiredPositiveInteger(value: unknown, path: string): void { if (!Number.isInteger(value) || Number(value) <= 0) throw new Error(`${path} must be a positive integer`); }
|
||||
|
||||
Reference in New Issue
Block a user