diff --git a/src/consola.ts b/src/consola.ts index baefd8fb..f61cabdf 100644 --- a/src/consola.ts +++ b/src/consola.ts @@ -441,7 +441,8 @@ export class Consola { } } } catch { - // Circular References + // Circular References - prevent stale serialized comparisons + this._lastLog.serialized = undefined; } } diff --git a/test/consola.test.ts b/test/consola.test.ts index ae9e177f..20ed31c2 100644 --- a/test/consola.test.ts +++ b/test/consola.test.ts @@ -56,9 +56,51 @@ describe("consola", () => { expect(logs.at(-1)!.args).toEqual(["SPAM", "(repeated 4 times)"]); }); + + test("stale serialized does not cause same-log comparison", async () => { + // This test verifies that when JSON.stringify throws (circular ref), + // _lastLog.serialized is reset, preventing the next log from being + // incorrectly treated as a repeat of an earlier one. + const logs: LogObject[] = []; + const TestReporter: ConsolaReporter = { + log(logObj) { + logs.push(logObj); + }, + }; + + const consola = createConsola({ + throttle: 100, + throttleMin: 5, + level: LogLevels.info, + reporters: [TestReporter], + }); + + // 1. Log a normal message - sets _lastLog.serialized + consola.log("Control message"); + + // 2. Log an object with circular reference - triggers catch block + const circular: { value: string; self?: any } = { value: "circular data" }; + circular.self = circular; + consola.log(circular); + + // 3. Log another normal message - should NOT be treated as repeat + consola.log("Different message"); + + await wait(300); + + // All three messages should appear distinctly + expect(logs.length).toBe(3); + expect(logs[0].args[0]).toBe("Control message"); + expect(logs[2].args[0]).toBe("Different message"); + + // Verify the circular object made it through (even though JSON serialization failed) + expect(logs[1].args[0]).toEqual( + expect.objectContaining({ value: "circular data" }), + ); + }); }); -function wait(delay) { +function wait(delay: number) { return new Promise((resolve) => { setTimeout(resolve, delay); });