refactor: replace JSON deep copy with deepClone helper and extract useTauriEvent hook (#3140)

* refactor: replace JSON.parse(JSON.stringify()) with structuredClone and extract useTauriEvent hook

Replace all `JSON.parse(JSON.stringify())` deep copy patterns with native
`structuredClone()` across production source (9 occurrences), tests (11
occurrences), and a hand-rolled `deepClone` utility in providerConfigUtils.ts.
Add "ES2022" to tsconfig lib for type support.

Extract a `useTauriEvent` hook to eliminate the repeated Tauri event listener
boilerplate (`useEffect` + `active/disposed` flag + async `listen`) that was
duplicated across App.tsx (3 listeners) and useUsageCacheBridge.ts. The hook
handles async registration, race-condition guards, and cleanup automatically.

* fix: add compatible deepClone helper

- Add a shared deepClone helper with a structuredClone runtime guard and fallback.
- Route clone call sites through the helper.
- Preserve universal-provider-synced listener ordering and drop the dead-directory diff.

* fix: harden Tauri event handling

- Guard WebDAV sync status events against missing payloads.
- Preserve settings query invalidation ordering before showing auto-sync errors.
- Simplify useTauriEvent subscriptions to avoid dependency-driven re-listens.

---------

Co-authored-by: zcb <zhangchongbiao@qiyuanlab.com>
Co-authored-by: Jason <farion1231@gmail.com>
This commit is contained in:
zcb
2026-05-26 23:39:16 +08:00
committed by GitHub
parent 05ba28016c
commit 8cdaf90d8d
12 changed files with 178 additions and 201 deletions
@@ -5,6 +5,7 @@ import type {
} from "@/config/claudeProviderPresets";
import type { CodexProviderPreset } from "@/config/codexProviderPresets";
import { applyTemplateValues } from "@/utils/providerConfigUtils";
import { deepClone } from "@/utils/deepClone";
type TemplatePath = Array<string | number>;
type TemplateValueMap = Record<string, TemplateValueConfig>;
@@ -133,7 +134,7 @@ const applyTemplateValuesToConfigString = (
if (Array.isArray(parsedConfig)) {
targetConfig = [...parsedConfig];
} else if (parsedConfig && typeof parsedConfig === "object") {
targetConfig = JSON.parse(JSON.stringify(parsedConfig));
targetConfig = deepClone(parsedConfig);
} else {
targetConfig = {};
}
@@ -15,6 +15,7 @@ import {
createUniversalProviderFromPreset,
type UniversalProviderPreset,
} from "@/config/universalProviderPresets";
import { deepClone } from "@/utils/deepClone";
interface UniversalProviderFormModalProps {
isOpen: boolean;
@@ -90,7 +91,7 @@ export function UniversalProviderFormModal({
setClaudeEnabled(defaultPreset.defaultApps.claude);
setCodexEnabled(defaultPreset.defaultApps.codex);
setGeminiEnabled(defaultPreset.defaultApps.gemini);
setModels(JSON.parse(JSON.stringify(defaultPreset.defaultModels)));
setModels(deepClone(defaultPreset.defaultModels));
}
}, [editingProvider, initialPreset, isOpen]);
@@ -103,7 +104,7 @@ export function UniversalProviderFormModal({
setClaudeEnabled(preset.defaultApps.claude);
setCodexEnabled(preset.defaultApps.codex);
setGeminiEnabled(preset.defaultApps.gemini);
setModels(JSON.parse(JSON.stringify(preset.defaultModels)));
setModels(deepClone(preset.defaultModels));
}
},
[isEditMode],
@@ -7,6 +7,7 @@ import { UniversalProviderCard } from "./UniversalProviderCard";
import { UniversalProviderFormModal } from "./UniversalProviderFormModal";
import { universalProvidersApi } from "@/lib/api";
import type { UniversalProvider, UniversalProvidersMap } from "@/types";
import { deepClone } from "@/utils/deepClone";
export function UniversalProviderPanel() {
const { t } = useTranslation();
@@ -169,7 +170,7 @@ export function UniversalProviderPanel() {
const handleDuplicate = useCallback(
async (provider: UniversalProvider) => {
const duplicated: UniversalProvider = {
...JSON.parse(JSON.stringify(provider)),
...deepClone(provider),
id: crypto.randomUUID(),
name: `${provider.name} copy`,
createdAt: Date.now(),