fix(client): prevent duplicate signal processing in atom listeners (#7018)

This commit is contained in:
Alex Yang
2025-12-31 21:36:19 +08:00
parent 8e6ad93049
commit 016ba1df7e
2 changed files with 72 additions and 0 deletions
@@ -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();
});
});
+7
View File
@@ -1,5 +1,6 @@
import type {
BetterAuthClientPlugin,
ClientAtomListener,
ClientFetchOption,
} from "@better-auth/core";
import type { BetterFetch } from "@better-fetch/fetch";
@@ -100,9 +101,15 @@ export function createDynamicPathProxy<T extends Record<string, any>>(
*/
const matches = atomListeners.filter((s) => s.matcher(routePath));
if (!matches.length) return;
const visited = new Set<ClientAtomListener["signal"]>();
for (const match of matches) {
const signal = atoms[match.signal as any];
if (!signal) return;
if (visited.has(match.signal)) {
continue;
}
visited.add(match.signal);
/**
* To avoid race conditions we set the signal in a setTimeout
*/