























@@ -1,3 +1,5 @@
1+import JSON5 from "json5";
2+13export function cloneConfigObject<T>(value: T): T {
24return structuredClone(value);
35}
@@ -6,6 +8,119 @@ export function serializeConfigForm(form: Record<string, unknown>): string {
68return `${JSON.stringify(form, null, 2).trimEnd()}\n`;
79}
81011+const REDACTED_SENTINEL = "__OPENCLAW_REDACTED__";
12+type SanitizeResult = { omitted: true } | { omitted: false; value: unknown };
13+14+const OMIT_VALUE: SanitizeResult = { omitted: true };
15+16+function keepValue(value: unknown): SanitizeResult {
17+return { omitted: false, value };
18+}
19+20+function isRecord(value: unknown): value is Record<string, unknown> {
21+return Boolean(value) && typeof value === "object" && !Array.isArray(value);
22+}
23+24+function hasOwnRecordValue(record: Record<string, unknown> | null, key: string): boolean {
25+return record != null && Object.prototype.hasOwnProperty.call(record, key);
26+}
27+28+function sanitizeRedactedValue(params: {
29+value: unknown;
30+originalFormValue: unknown;
31+originalRawValue: unknown;
32+originalRawPathExists: boolean;
33+canOmit: boolean;
34+}): SanitizeResult {
35+if (params.value === REDACTED_SENTINEL) {
36+if (params.originalFormValue !== REDACTED_SENTINEL) {
37+return keepValue(params.value);
38+}
39+if (params.originalRawPathExists) {
40+return keepValue(params.value);
41+}
42+return params.canOmit ? OMIT_VALUE : keepValue(params.value);
43+}
44+45+if (Array.isArray(params.value)) {
46+const originalFormItems = Array.isArray(params.originalFormValue)
47+ ? params.originalFormValue
48+ : [];
49+const originalRawItems = Array.isArray(params.originalRawValue) ? params.originalRawValue : [];
50+return keepValue(
51+params.value.map((item, index) => {
52+const sanitized = sanitizeRedactedValue({
53+value: item,
54+originalFormValue: originalFormItems[index],
55+originalRawValue: originalRawItems[index],
56+originalRawPathExists: index in originalRawItems,
57+canOmit: false,
58+});
59+return sanitized.omitted ? item : sanitized.value;
60+}),
61+);
62+}
63+64+if (!isRecord(params.value)) {
65+return keepValue(params.value);
66+}
67+68+const originalFormRecord = isRecord(params.originalFormValue) ? params.originalFormValue : null;
69+const originalRawRecord = isRecord(params.originalRawValue) ? params.originalRawValue : null;
70+const next: Record<string, unknown> = {};
71+for (const [key, item] of Object.entries(params.value)) {
72+const originalFormValue =
73+originalFormRecord != null && Object.prototype.hasOwnProperty.call(originalFormRecord, key)
74+ ? originalFormRecord[key]
75+ : undefined;
76+const originalRawPathExists = hasOwnRecordValue(originalRawRecord, key);
77+const sanitized = sanitizeRedactedValue({
78+value: item,
79+ originalFormValue,
80+originalRawValue: originalRawPathExists ? originalRawRecord?.[key] : undefined,
81+ originalRawPathExists,
82+canOmit: true,
83+});
84+if (!sanitized.omitted) {
85+next[key] = sanitized.value;
86+}
87+}
88+89+if (params.canOmit && Object.keys(next).length === 0 && !params.originalRawPathExists) {
90+return OMIT_VALUE;
91+}
92+return keepValue(next);
93+}
94+95+export function sanitizeRedactedFormForSubmit(
96+form: Record<string, unknown>,
97+originalForm: Record<string, unknown> | null | undefined,
98+originalRaw: string,
99+): Record<string, unknown> {
100+if (!originalForm || !originalRaw) {
101+return form;
102+}
103+104+let parsedOriginalRaw: unknown;
105+try {
106+parsedOriginalRaw = JSON5.parse(originalRaw);
107+} catch {
108+return form;
109+}
110+if (!isRecord(parsedOriginalRaw)) {
111+return form;
112+}
113+114+const sanitized = sanitizeRedactedValue({
115+value: form,
116+originalFormValue: originalForm,
117+originalRawValue: parsedOriginalRaw,
118+originalRawPathExists: true,
119+canOmit: false,
120+});
121+return !sanitized.omitted && isRecord(sanitized.value) ? sanitized.value : form;
122+}
123+9124const FORBIDDEN_KEYS = new Set(["__proto__", "prototype", "constructor"]);
1012511126function isForbiddenKey(key: string | number): boolean {
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。