Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/consola.ts
Original file line number Diff line number Diff line change
Expand Up @@ -441,7 +441,8 @@ export class Consola {
}
}
} catch {
// Circular References
// Circular References - prevent stale serialized comparisons
this._lastLog.serialized = undefined;
}
}

Expand Down
44 changes: 43 additions & 1 deletion test/consola.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Comment on lines +78 to +94

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Make the regression test fail without the fix.

"Control message" and "Different message" serialize differently, so the stale "Control message" serialization cannot classify the final log as a repeat. This test therefore passes against the old implementation too. Assert _lastLog.serialized is cleared after the circular log, or repeat the original message with enough throttling state to trigger the old suppression behavior.

Suggested assertion
     consola.log(circular);
+    expect(consola._lastLog.serialized).toBeUndefined();
     consola.log("Different message");
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// 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");
// 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);
expect(consola._lastLog.serialized).toBeUndefined();
// 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");
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/consola.test.ts` around lines 78 - 94, Make the regression test around
consola.log explicitly fail on the old implementation by verifying that the
circular-reference log clears consola’s _lastLog.serialized state, or change the
final message to repeat "Control message" with the required throttling setup so
stale serialization would suppress it. Keep the existing assertions for distinct
output where they remain applicable.


// 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);
});
Expand Down