diff --git a/packages/better-auth/src/client/proxy.test.ts b/packages/better-auth/src/client/proxy.test.ts new file mode 100644 index 0000000000..8ecf7cd630 --- /dev/null +++ b/packages/better-auth/src/client/proxy.test.ts @@ -0,0 +1,65 @@ +import { describe, expect, it, vi } from "vitest"; +import { createDynamicPathProxy } from "./proxy"; + +describe("createDynamicPathProxy", () => { + it("should avoid duplicate signal processing", async () => { + const client = vi.fn(async (path, options) => { + if (options?.onSuccess) { + await options.onSuccess({} as any); + } + return { + data: {}, + error: null, + }; + }); + const knownPathMethods = { + "/test": "POST", + } as const; + + const signalSet = vi.fn(); + const signalGet = vi.fn(() => false); + + const atoms = { + testSignal: { + get: signalGet, + set: signalSet, + subscribe: () => () => {}, + listen: () => () => {}, + off: () => {}, + value: false, + } as any, + }; + const atomListeners = [ + { + matcher: (path: string) => path === "/test", + signal: "testSignal", + }, + { + matcher: (path: string) => path === "/test", + signal: "testSignal", + }, + ]; + + const proxy = createDynamicPathProxy( + { + test: {}, + }, + client as any, + knownPathMethods, + atoms, + atomListeners, + ); + + vi.useFakeTimers(); + + await (proxy.test as any)(); + + expect(client).toHaveBeenCalled(); + + vi.runAllTimers(); + + expect(signalSet).toHaveBeenCalledTimes(1); + + vi.useRealTimers(); + }); +}); diff --git a/packages/better-auth/src/client/proxy.ts b/packages/better-auth/src/client/proxy.ts index 0c26dfb01a..46d8f8be6f 100644 --- a/packages/better-auth/src/client/proxy.ts +++ b/packages/better-auth/src/client/proxy.ts @@ -101,6 +101,7 @@ export function createDynamicPathProxy>( */ const matches = atomListeners.filter((s) => s.matcher(routePath)); if (!matches.length) return; + const visited = new Set(); for (const match of matches) { const signal = atoms[match.signal as any];