mirror of
https://github.com/openclaw/openclaw.git
synced 2026-07-21 10:16:44 +00:00
refactor: consolidate safe json parsing (#99688)
This commit is contained in:
@@ -2,6 +2,7 @@
|
||||
|
||||
export * from "./boolean-coercion.js";
|
||||
export * from "./error-coercion.js";
|
||||
export * from "./json-coercion.js";
|
||||
export * from "./number-coercion.js";
|
||||
export * from "./record-coerce.js";
|
||||
export * from "./string-coerce.js";
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { safeParseJson } from "./json-coercion.js";
|
||||
|
||||
describe("json-coercion", () => {
|
||||
it.each<[string, unknown]>([
|
||||
['{"ok":true}', { ok: true }],
|
||||
["[1]", [1]],
|
||||
['"text"', "text"],
|
||||
["null", null],
|
||||
["{", undefined],
|
||||
])("parses %s", (value, expected) => expect(safeParseJson(value)).toEqual(expected));
|
||||
});
|
||||
@@ -0,0 +1,8 @@
|
||||
/** Parses JSON without throwing, returning undefined for invalid input. */
|
||||
export function safeParseJson(value: string): unknown {
|
||||
try {
|
||||
return JSON.parse(value) as unknown;
|
||||
} catch {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
/** SQLite-backed ACP session metadata storage keyed through session-store entries. */
|
||||
import type { DatabaseSync } from "node:sqlite";
|
||||
import { safeParseJson } from "@openclaw/normalization-core";
|
||||
import { asOptionalRecord } from "@openclaw/normalization-core/record-coerce";
|
||||
import { normalizeLowercaseStringOrEmpty } from "@openclaw/normalization-core/string-coerce";
|
||||
import type { Insertable, Selectable } from "kysely";
|
||||
import { getRuntimeConfig } from "../../config/config.js";
|
||||
@@ -29,7 +31,6 @@ import {
|
||||
type OpenClawStateDatabaseOptions,
|
||||
runOpenClawStateWriteTransaction,
|
||||
} from "../../state/openclaw-state-db.js";
|
||||
import { isRecord } from "../../utils.js";
|
||||
|
||||
/** ACP metadata joined with its legacy session-store row and config context. */
|
||||
export type AcpSessionStoreEntry = {
|
||||
@@ -89,21 +90,11 @@ function getAcpSessionKysely(db: DatabaseSync) {
|
||||
return getNodeSqliteKysely<AcpSessionMetaDatabase>(db);
|
||||
}
|
||||
|
||||
function parseOptionalJsonRecord(raw: string | null): Record<string, unknown> | undefined {
|
||||
if (raw == null || raw === "") {
|
||||
return undefined;
|
||||
}
|
||||
try {
|
||||
const parsed = JSON.parse(raw) as unknown;
|
||||
return isRecord(parsed) ? parsed : undefined;
|
||||
} catch {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
function rowToAcpSessionMeta(row: AcpSessionRow): SessionAcpMeta {
|
||||
const identity = parseOptionalJsonRecord(row.identity_json) as SessionAcpIdentity | undefined;
|
||||
const runtimeOptions = parseOptionalJsonRecord(row.runtime_options_json) as
|
||||
const identity = asOptionalRecord(safeParseJson(row.identity_json ?? "")) as
|
||||
| SessionAcpIdentity
|
||||
| undefined;
|
||||
const runtimeOptions = asOptionalRecord(safeParseJson(row.runtime_options_json ?? "")) as
|
||||
| AcpSessionRuntimeOptions
|
||||
| undefined;
|
||||
return {
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
/**
|
||||
* Detects message-tool sends that delivered a visible reply to the current source.
|
||||
*/
|
||||
import { safeParseJson } from "@openclaw/normalization-core";
|
||||
import { asOptionalRecord } from "@openclaw/normalization-core/record-coerce";
|
||||
import type { SourceReplyDeliveryMode } from "../auto-reply/get-reply-options.types.js";
|
||||
import {
|
||||
isMessageToolConversationCreateActionName,
|
||||
@@ -70,14 +72,7 @@ function isBareSentDeliveryStatus(value: unknown): boolean {
|
||||
}
|
||||
|
||||
function parseJsonRecord(value: string): Record<string, unknown> | undefined {
|
||||
try {
|
||||
const parsed = JSON.parse(value);
|
||||
return parsed && typeof parsed === "object" && !Array.isArray(parsed)
|
||||
? (parsed as Record<string, unknown>)
|
||||
: undefined;
|
||||
} catch {
|
||||
return undefined;
|
||||
}
|
||||
return asOptionalRecord(safeParseJson(value));
|
||||
}
|
||||
|
||||
function recordHasDeliveredMessageId(record: Record<string, unknown>): boolean {
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
*
|
||||
* Accepts provider-specific tool-call and tool-result shapes used by transcript repair and announce capture.
|
||||
*/
|
||||
import { safeParseJson } from "@openclaw/normalization-core";
|
||||
import { asOptionalRecord } from "@openclaw/normalization-core/record-coerce";
|
||||
import { readTrimmedStringAlias } from "../utils/string-readers.js";
|
||||
|
||||
@@ -50,11 +51,7 @@ function parseJsonObject(text: string): Record<string, unknown> | undefined {
|
||||
if (!trimmed.startsWith("{")) {
|
||||
return undefined;
|
||||
}
|
||||
try {
|
||||
return asOptionalRecord(JSON.parse(trimmed));
|
||||
} catch {
|
||||
return undefined;
|
||||
}
|
||||
return asOptionalRecord(safeParseJson(trimmed));
|
||||
}
|
||||
|
||||
function readStructuredToolPayload(content: unknown): Record<string, unknown> | undefined {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
// Renders chat canvas payloads into text and metadata for transcript output.
|
||||
import { safeParseJson } from "@openclaw/normalization-core";
|
||||
import { asFiniteNumber } from "@openclaw/normalization-core/number-coercion";
|
||||
import { asOptionalRecord } from "@openclaw/normalization-core/record-coerce";
|
||||
import { parseFenceSpans } from "../../packages/markdown-core/src/fences.js";
|
||||
@@ -19,18 +20,6 @@ type CanvasPreview = {
|
||||
style?: string;
|
||||
};
|
||||
|
||||
function tryParseJsonRecord(value: string | undefined): Record<string, unknown> | undefined {
|
||||
if (typeof value !== "string") {
|
||||
return undefined;
|
||||
}
|
||||
try {
|
||||
const parsed = JSON.parse(value);
|
||||
return asOptionalRecord(parsed);
|
||||
} catch {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
function getRecordStringField(
|
||||
record: Record<string, unknown> | undefined,
|
||||
key: string,
|
||||
@@ -184,7 +173,7 @@ export function extractCanvasFromText(
|
||||
outputText: string | undefined,
|
||||
_toolName?: string,
|
||||
): CanvasPreview | undefined {
|
||||
const parsed = tryParseJsonRecord(outputText);
|
||||
const parsed = outputText ? asOptionalRecord(safeParseJson(outputText)) : undefined;
|
||||
return coerceCanvasPreview(parsed);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user