From 30ec6017b5884d84bb18dfe7eb941ea1c7b5d5f9 Mon Sep 17 00:00:00 2001 From: ndycode <405533+ndycode@users.noreply.github.com> Date: Sun, 22 Mar 2026 09:31:12 +0800 Subject: [PATCH] refactor: extract experimental target loader wrapper --- .../experimental-sync-target-entry.ts | 64 +++++++++++++++++++ lib/codex-manager/settings-hub.ts | 20 ++---- test/experimental-sync-target-entry.test.ts | 27 ++++++++ 3 files changed, 96 insertions(+), 15 deletions(-) create mode 100644 lib/codex-manager/experimental-sync-target-entry.ts create mode 100644 test/experimental-sync-target-entry.test.ts diff --git a/lib/codex-manager/experimental-sync-target-entry.ts b/lib/codex-manager/experimental-sync-target-entry.ts new file mode 100644 index 00000000..f72201a3 --- /dev/null +++ b/lib/codex-manager/experimental-sync-target-entry.ts @@ -0,0 +1,64 @@ +import type { AccountStorageV3 } from "../storage.js"; + +export async function loadExperimentalSyncTargetEntry(params: { + loadExperimentalSyncTargetState: (args: { + detectTarget: () => ReturnType< + typeof import("../oc-chatgpt-target-detection.js").detectOcChatgptMultiAuthTarget + >; + readJson: (path: string) => Promise; + normalizeAccountStorage: (value: unknown) => AccountStorageV3 | null; + }) => Promise< + | { + kind: "blocked-ambiguous"; + detection: ReturnType< + typeof import("../oc-chatgpt-target-detection.js").detectOcChatgptMultiAuthTarget + >; + } + | { + kind: "blocked-none"; + detection: ReturnType< + typeof import("../oc-chatgpt-target-detection.js").detectOcChatgptMultiAuthTarget + >; + } + | { kind: "error"; message: string } + | { + kind: "target"; + detection: ReturnType< + typeof import("../oc-chatgpt-target-detection.js").detectOcChatgptMultiAuthTarget + >; + destination: AccountStorageV3 | null; + } + >; + detectTarget: () => ReturnType< + typeof import("../oc-chatgpt-target-detection.js").detectOcChatgptMultiAuthTarget + >; + readFileWithRetry: ( + path: string, + options: { + retryableCodes: Set; + maxAttempts: number; + sleep: (ms: number) => Promise; + }, + ) => Promise; + normalizeAccountStorage: (value: unknown) => AccountStorageV3 | null; + sleep: (ms: number) => Promise; +}): ReturnType { + return params.loadExperimentalSyncTargetState({ + detectTarget: params.detectTarget, + readJson: async (path) => + JSON.parse( + await params.readFileWithRetry(path, { + retryableCodes: new Set([ + "EBUSY", + "EPERM", + "EAGAIN", + "ENOTEMPTY", + "EACCES", + ]), + maxAttempts: 4, + sleep: params.sleep, + }), + ), + normalizeAccountStorage: params.normalizeAccountStorage, + }); +} diff --git a/lib/codex-manager/settings-hub.ts b/lib/codex-manager/settings-hub.ts index 4e1cc2cc..072e2cac 100644 --- a/lib/codex-manager/settings-hub.ts +++ b/lib/codex-manager/settings-hub.ts @@ -70,6 +70,7 @@ import { mapExperimentalStatusHotkey, } from "./experimental-settings-schema.js"; import { loadExperimentalSyncTargetState } from "./experimental-sync-target.js"; +import { loadExperimentalSyncTargetEntry } from "./experimental-sync-target-entry.js"; import { promptSettingsHubEntry } from "./settings-hub-entry.js"; import { buildSettingsHubItems, @@ -648,23 +649,12 @@ async function loadExperimentalSyncTarget(): Promise< destination: import("../storage.js").AccountStorageV3 | null; } > { - return loadExperimentalSyncTargetState({ + return loadExperimentalSyncTargetEntry({ + loadExperimentalSyncTargetState, detectTarget: detectOcChatgptMultiAuthTarget, - readJson: async (path) => - JSON.parse( - await readFileWithRetry(path, { - retryableCodes: new Set([ - "EBUSY", - "EPERM", - "EAGAIN", - "ENOTEMPTY", - "EACCES", - ]), - maxAttempts: 4, - sleep, - }), - ), + readFileWithRetry, normalizeAccountStorage, + sleep, }); } diff --git a/test/experimental-sync-target-entry.test.ts b/test/experimental-sync-target-entry.test.ts new file mode 100644 index 00000000..942740bb --- /dev/null +++ b/test/experimental-sync-target-entry.test.ts @@ -0,0 +1,27 @@ +import { describe, expect, it, vi } from "vitest"; +import { loadExperimentalSyncTargetEntry } from "../lib/codex-manager/experimental-sync-target-entry.js"; + +describe("experimental sync target entry", () => { + it("delegates retrying file read and normalization through the target loader", async () => { + const loadExperimentalSyncTargetState = vi.fn(async () => ({ + kind: "target", + detection: { kind: "target" }, + destination: null, + })); + + const result = await loadExperimentalSyncTargetEntry({ + loadExperimentalSyncTargetState, + detectTarget: () => ({ kind: "target" }) as never, + readFileWithRetry: vi.fn(async () => "{}"), + normalizeAccountStorage: vi.fn(() => null), + sleep: vi.fn(async () => undefined), + }); + + expect(loadExperimentalSyncTargetState).toHaveBeenCalled(); + expect(result).toEqual({ + kind: "target", + detection: { kind: "target" }, + destination: null, + }); + }); +});