mirror of
https://github.com/openclaw/openclaw.git
synced 2026-03-15 03:50:40 +00:00
95 lines
2.2 KiB
TypeScript
95 lines
2.2 KiB
TypeScript
import { DIFF_INDICATORS, DIFF_LAYOUTS, DIFF_THEMES } from "./types.js";
|
|
import type { DiffViewerPayload } from "./types.js";
|
|
|
|
const OVERFLOW_VALUES = ["scroll", "wrap"] as const;
|
|
|
|
export function parseViewerPayloadJson(raw: string): DiffViewerPayload {
|
|
let parsed: unknown;
|
|
try {
|
|
parsed = JSON.parse(raw);
|
|
} catch {
|
|
throw new Error("Diff payload is not valid JSON.");
|
|
}
|
|
|
|
if (!isDiffViewerPayload(parsed)) {
|
|
throw new Error("Diff payload has invalid shape.");
|
|
}
|
|
|
|
return parsed;
|
|
}
|
|
|
|
function isDiffViewerPayload(value: unknown): value is DiffViewerPayload {
|
|
if (!isRecord(value)) {
|
|
return false;
|
|
}
|
|
|
|
if (typeof value.prerenderedHTML !== "string") {
|
|
return false;
|
|
}
|
|
|
|
if (!Array.isArray(value.langs) || !value.langs.every((lang) => typeof lang === "string")) {
|
|
return false;
|
|
}
|
|
|
|
if (!isViewerOptions(value.options)) {
|
|
return false;
|
|
}
|
|
|
|
const hasFileDiff = isRecord(value.fileDiff);
|
|
const hasBeforeAfterFiles = isRecord(value.oldFile) && isRecord(value.newFile);
|
|
if (!hasFileDiff && !hasBeforeAfterFiles) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
function isViewerOptions(value: unknown): boolean {
|
|
if (!isRecord(value)) {
|
|
return false;
|
|
}
|
|
|
|
if (!isRecord(value.theme)) {
|
|
return false;
|
|
}
|
|
if (value.theme.light !== "pierre-light" || value.theme.dark !== "pierre-dark") {
|
|
return false;
|
|
}
|
|
|
|
if (!includesValue(DIFF_LAYOUTS, value.diffStyle)) {
|
|
return false;
|
|
}
|
|
if (!includesValue(DIFF_INDICATORS, value.diffIndicators)) {
|
|
return false;
|
|
}
|
|
if (!includesValue(DIFF_THEMES, value.themeType)) {
|
|
return false;
|
|
}
|
|
if (!includesValue(OVERFLOW_VALUES, value.overflow)) {
|
|
return false;
|
|
}
|
|
|
|
if (typeof value.disableLineNumbers !== "boolean") {
|
|
return false;
|
|
}
|
|
if (typeof value.expandUnchanged !== "boolean") {
|
|
return false;
|
|
}
|
|
if (typeof value.backgroundEnabled !== "boolean") {
|
|
return false;
|
|
}
|
|
if (typeof value.unsafeCSS !== "string") {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
return typeof value === "object" && value !== null;
|
|
}
|
|
|
|
function includesValue<T extends readonly string[]>(values: T, value: unknown): value is T[number] {
|
|
return typeof value === "string" && values.includes(value as T[number]);
|
|
}
|