lib(renderer): add safeJson helper; migrate 6 JSON.parse try/catch call sites
This commit is contained in:
1 parent
a07b42e493
commit
be0ff5a041
8 files changed
+46
-48
No files matched your search
@@ -11,6 +11,7 @@ import {
|
||||
} from "@phosphor-icons/react"
|
||||
import type { AgentMessageRecord, AgentRecord, CommentRecord, DepartmentId } from "@shared/types"
|
||||
import { getAgentInitials, formatMessageTime } from "../../lib/formatters"
|
||||
import { safeJson } from "../../lib/safe-json"
|
||||
import { DEPARTMENT_COLORS } from "@shared/constants"
|
||||
|
||||
const HUMAN_SENDER_ID = "__board__";
|
||||
@@ -196,12 +197,7 @@ export function TaskCommentBubble({
|
||||
}
|
||||
|
||||
function AttachmentList({ json }: { json: string }) {
|
||||
let items: Array<{ type: string; id: string; title: string }> = []
|
||||
try {
|
||||
items = JSON.parse(json)
|
||||
} catch {
|
||||
return null
|
||||
}
|
||||
const items = safeJson<Array<{ type: string; id: string; title: string }>>(json, [])
|
||||
if (!items.length) return null
|
||||
|
||||
return (
|
||||
|
||||
@@ -60,13 +60,10 @@ export function splitTags(value: string) {
|
||||
return JSON.stringify(value.split(",").map((item) => item.trim()).filter(Boolean));
|
||||
}
|
||||
|
||||
import { safeJson } from "../../lib/safe-json";
|
||||
|
||||
export function parseTags(value: string) {
|
||||
try {
|
||||
const tags = JSON.parse(value) as string[];
|
||||
return tags.join(", ");
|
||||
} catch {
|
||||
return "";
|
||||
}
|
||||
return safeJson<string[]>(value, []).join(", ");
|
||||
}
|
||||
|
||||
export function parseParticipantIds(value: string) {
|
||||
@@ -79,12 +76,7 @@ export function parseParticipantIds(value: string) {
|
||||
import type { AgentRecord } from "@shared/types";
|
||||
|
||||
export function formatParticipantIds(raw: string, agents: AgentRecord[]) {
|
||||
try {
|
||||
const ids = JSON.parse(raw) as string[];
|
||||
return ids
|
||||
.map((id) => agents.find((agent) => agent.id === id)?.name ?? id.slice(0, 8))
|
||||
.join(", ");
|
||||
} catch {
|
||||
return "";
|
||||
}
|
||||
return safeJson<string[]>(raw, [])
|
||||
.map((id) => agents.find((agent) => agent.id === id)?.name ?? id.slice(0, 8))
|
||||
.join(", ");
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
import { Clock, Link as LinkIcon } from "@phosphor-icons/react"
|
||||
import { platformConfig } from "../social/social-config"
|
||||
import type { SocialPlatform } from "@shared/types"
|
||||
import { safeJson } from "../../lib/safe-json"
|
||||
|
||||
const PLATFORM_CHAR_LIMITS: Record<string, number> = {
|
||||
twitter: 280,
|
||||
@@ -26,19 +27,16 @@ interface DraftPayload {
|
||||
}
|
||||
|
||||
function parseDraft(payloadJson: string): DraftPayload | null {
|
||||
try {
|
||||
const parsed = JSON.parse(payloadJson)
|
||||
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
||||
return {
|
||||
content: typeof parsed.content === "string" ? parsed.content : "",
|
||||
mediaUrls: Array.isArray(parsed.mediaUrls) ? parsed.mediaUrls : [],
|
||||
scheduledAt: typeof parsed.scheduledAt === "string" ? parsed.scheduledAt : null,
|
||||
socialAccountId: typeof parsed.socialAccountId === "string" ? parsed.socialAccountId : "",
|
||||
platform: typeof parsed.platform === "string" ? parsed.platform : "other",
|
||||
}
|
||||
}
|
||||
} catch { /* invalid JSON */ }
|
||||
return null
|
||||
const parsed = safeJson<unknown>(payloadJson, null)
|
||||
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return null
|
||||
const obj = parsed as Record<string, unknown>
|
||||
return {
|
||||
content: typeof obj.content === "string" ? obj.content : "",
|
||||
mediaUrls: Array.isArray(obj.mediaUrls) ? (obj.mediaUrls as string[]) : [],
|
||||
scheduledAt: typeof obj.scheduledAt === "string" ? obj.scheduledAt : null,
|
||||
socialAccountId: typeof obj.socialAccountId === "string" ? obj.socialAccountId : "",
|
||||
platform: typeof obj.platform === "string" ? obj.platform : "other",
|
||||
}
|
||||
}
|
||||
|
||||
function formatScheduledDate(isoDate: string): string {
|
||||
|
||||
@@ -1,18 +1,15 @@
|
||||
import { useMemo } from "react"
|
||||
import type { ApprovalRecord } from "@shared/types"
|
||||
import { safeJson } from "../../../lib/safe-json"
|
||||
|
||||
export function useApprovalPayload(approval: ApprovalRecord): Record<string, unknown> | null {
|
||||
return useMemo(() => {
|
||||
if (!approval.payloadJson) return null
|
||||
try {
|
||||
const parsed = JSON.parse(approval.payloadJson)
|
||||
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
||||
return parsed as Record<string, unknown>
|
||||
}
|
||||
return null
|
||||
} catch {
|
||||
return null
|
||||
const parsed = safeJson<unknown>(approval.payloadJson, null)
|
||||
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
||||
return parsed as Record<string, unknown>
|
||||
}
|
||||
return null
|
||||
}, [approval.payloadJson])
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ import type { ApprovalRecord } from "@shared/types"
|
||||
import {
|
||||
Robot, CheckSquare, Play, ShieldCheck, Gear, FolderOpen, Key, Target, Briefcase, Buildings,
|
||||
} from "@phosphor-icons/react"
|
||||
import { safeJson } from "../../lib/safe-json"
|
||||
|
||||
export function describeActionError(error: unknown) {
|
||||
return error instanceof Error ? error.message : "The action could not be completed."
|
||||
@@ -9,10 +10,8 @@ export function describeActionError(error: unknown) {
|
||||
|
||||
export function parseApprovalPayloadJson(approval: ApprovalRecord): Record<string, unknown> | null {
|
||||
if (!approval.payloadJson) return null
|
||||
try {
|
||||
const parsed = JSON.parse(approval.payloadJson)
|
||||
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) return parsed as Record<string, unknown>
|
||||
} catch { return null }
|
||||
const parsed = safeJson<unknown>(approval.payloadJson, null)
|
||||
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) return parsed as Record<string, unknown>
|
||||
return null
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ import type { AgentRecord, ApprovalRecord } from "@shared/types";
|
||||
import { useT } from "../../i18n";
|
||||
import { getAgentInitials } from "../../lib/formatters";
|
||||
import { unwrap } from "../../lib/desktop";
|
||||
import { safeJson } from "../../lib/safe-json";
|
||||
import { ActionButton, EmptyState, Panel, StatusPill } from "../ui";
|
||||
import {
|
||||
type HiringStage,
|
||||
@@ -44,7 +45,8 @@ export function HiringPipeline({ agents, approvals, onRefresh, onNavigateToAgent
|
||||
const approval = hireApprovals.find(a => {
|
||||
if (a.relatedAgentId === agent.id) return true;
|
||||
if (!a.payloadJson) return false;
|
||||
try { return JSON.parse(a.payloadJson).agentId === agent.id; } catch { return false; }
|
||||
const payload = safeJson<{ agentId?: string }>(a.payloadJson, {});
|
||||
return payload.agentId === agent.id;
|
||||
}) ?? null;
|
||||
return {
|
||||
agent,
|
||||
|
||||
@@ -4,6 +4,7 @@ import { DEPARTMENT_OPTIONS } from "@shared/constants";
|
||||
import type { AgentRecord, BudgetPolicyRecord, ConnectorRecord, DepartmentId, WorkspaceRecord } from "@shared/types";
|
||||
import { getConnectorExecutionReadinessIssue, isConnectorExecutionReady } from "@shared/connector-policy";
|
||||
import { unwrap } from "../../lib/desktop";
|
||||
import { safeJson } from "../../lib/safe-json";
|
||||
import { buildBudgetPolicyInput, getBudgetPolicyAmountDollars } from "../../lib/budget-policy-input";
|
||||
import { useAppStore } from "../../state/store";
|
||||
import { useT } from "../../i18n";
|
||||
@@ -43,8 +44,13 @@ export function AgentEditor({
|
||||
const [reportsTo, setReportsTo] = useState(agent.reportsTo ?? "");
|
||||
const [confirmingDelete, setConfirmingDelete] = useState(false);
|
||||
const [saveError, setSaveError] = useState<string | null>(null);
|
||||
const existingMeta = useMemo(() => { try { return JSON.parse(agent.metadataJson || "{}"); } catch { return {}; } }, [agent.metadataJson]);
|
||||
const [thinkingEffort, setThinkingEffort] = useState<string>(existingMeta.thinkingEffort ?? "");
|
||||
const existingMeta = useMemo(
|
||||
() => safeJson<Record<string, unknown>>(agent.metadataJson, {}),
|
||||
[agent.metadataJson],
|
||||
);
|
||||
const [thinkingEffort, setThinkingEffort] = useState<string>(
|
||||
typeof existingMeta.thinkingEffort === "string" ? existingMeta.thinkingEffort : "",
|
||||
);
|
||||
const supportsEffort = connectorId === "claude_local" || connectorId === "codex_local";
|
||||
const pendingApproval = agent.status === "pending_approval";
|
||||
const executionReadyConnectors = useMemo(
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
export function safeJson<T>(text: string | null | undefined, fallback: T): T {
|
||||
if (text == null || text === "") return fallback;
|
||||
try {
|
||||
return JSON.parse(text) as T;
|
||||
} catch {
|
||||
return fallback;
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user