fix(plugin-sdk): avoid leaking queue rejection cleanup

This commit is contained in:
Vincent Koc
2026-04-13 16:24:16 +01:00
parent a08fbfb1ae
commit a945605b3c
2 changed files with 29 additions and 2 deletions

View File

@@ -78,6 +78,32 @@ describe("enqueueKeyedTask", () => {
await expect(runs[1]()).resolves.toBe("ok");
});
it("does not leak unhandled rejections when a task failure is already awaited", async () => {
const tails = new Map<string, Promise<void>>();
const unhandled: unknown[] = [];
const onUnhandledRejection = (reason: unknown) => {
unhandled.push(reason);
};
process.on("unhandledRejection", onUnhandledRejection);
try {
await expect(
enqueueKeyedTask({
tails,
key: "a",
task: async () => {
throw new Error("boom");
},
}),
).rejects.toThrow("boom");
await new Promise((resolve) => setTimeout(resolve, 0));
expect(unhandled).toEqual([]);
} finally {
process.off("unhandledRejection", onUnhandledRejection);
}
});
it("runs enqueue/settle hooks once per task", async () => {
const tails = new Map<string, Promise<void>>();
const onEnqueue = vi.fn();

View File

@@ -23,11 +23,12 @@ export function enqueueKeyedTask<T>(params: {
() => undefined,
);
params.tails.set(params.key, tail);
void tail.finally(() => {
const cleanup = () => {
if (params.tails.get(params.key) === tail) {
params.tails.delete(params.key);
}
});
};
tail.then(cleanup, cleanup);
return current;
}