mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-19 17:11:42 -06:00
f98bcb7fa7
* perf(ui): drop zod and lazy-load json5 out of Control UI startup zod's only UI consumer was the custom-theme shape layer, whose deep CSS validators already re-check every token; replace the two shallow schemas with a plain record reader and delete the zod jitless CSP shim + test. Startup keeps one schema library total (typebox, protocol-owned, already lazy via the approval page). json5 now loads through a lazy runtime boundary: strict JSON.parse is the fast path, the parser warms with the config editor and whenever a config snapshot or raw draft actually needs JSON5 (comments, trailing commas). The redaction sanitize path parses the authoritative raw once at snapshot ingestion (async-safe) and submits against the carried parsed fact, so secret-placeholder handling never races the lazy parser. Startup JS: 321.8 -> 295.8 KiB gzip, 13 -> 12 requests; budgets ratchet to 310 KiB / 18 requests. * fix(ui): gate config submits on pending JSON5 original parse A JSON5 config racing the first parser load could reach the sanitize step with a null parsed original and pass redaction placeholders through. setConfigRawOriginal now parses synchronously whenever the parser is warm and tracks a pending promise otherwise; submit, auto-save, and teardown flush paths defer to that promise (teardown keeps its synchronous prefix when no parse is pending). * fix(ui): keep teardown config flush synchronous and JSON5 diff cache non-sticky Teardown flush must dispatch before unload destroys the context; the gateway's restore-or-reject sentinel contract backs the rare unsanitized window. Raw-diff parse failures no longer cache while the lazy JSON5 parser is still loading, so a transient cold-parser miss retries on the next render instead of pinning an empty diff. * fix(ui): harden lazy JSON5 boundary against double-submit and failed loads Claim the config busy flag before awaiting a pending JSON5 original parse so a second click cannot slip past the busy state (autosave overlap is already serialized by the in-flight registry and drain discipline). A rejected json5 chunk import now resets the loader for retry, the per-state pending promise is never-rejecting and self-clearing, and fire-and-forget warms swallow rejections. * docs(ui): note autosave entry serialization at the JSON5 parse await * fix(ui): fill raw pending-changes diff once the lazy JSON5 parser lands First diff open could race the parser chunk and render an empty list with nothing scheduling a retry; renderConfig now re-renders when the warm completes, and the browser test warms the parser in setup to assert the steady state the view module guarantees in prod.
45 lines
1.3 KiB
TypeScript
45 lines
1.3 KiB
TypeScript
// Lazy JSON5 boundary: strict JSON parses without the library, so json5 stays
|
|
// out of the startup graph and loads only for config text that needs it.
|
|
type Json5Module = { parse: (text: string) => unknown };
|
|
|
|
let json5: Json5Module | null = null;
|
|
let json5Loading: Promise<Json5Module> | null = null;
|
|
|
|
export function isJson5Warm(): boolean {
|
|
return json5 !== null;
|
|
}
|
|
|
|
export function warmJson5(): Promise<Json5Module> {
|
|
json5Loading ??= import("json5").then(
|
|
(mod) => {
|
|
json5 = mod.default;
|
|
return json5;
|
|
},
|
|
(error: unknown) => {
|
|
// Transient chunk-load failures must not pin a rejected loader forever;
|
|
// the next warm retries the import.
|
|
json5Loading = null;
|
|
throw error;
|
|
},
|
|
);
|
|
return json5Loading;
|
|
}
|
|
|
|
/**
|
|
* Strict-JSON fast path with a JSON5 fallback once the module is warmed.
|
|
* Callers on the config surfaces warm the module before raw drafts can exist;
|
|
* if a JSON5-only text races the warm-up, this throws like a parse failure and
|
|
* the caller's existing invalid-draft handling applies until retry.
|
|
*/
|
|
export function parseJson5Text(raw: string): unknown {
|
|
try {
|
|
return JSON.parse(raw) as unknown;
|
|
} catch (jsonError) {
|
|
if (json5) {
|
|
return json5.parse(raw);
|
|
}
|
|
void warmJson5().catch(() => undefined);
|
|
throw jsonError;
|
|
}
|
|
}
|