From 7de8b691206b50bde5a8c87c373ce55cd03b04e4 Mon Sep 17 00:00:00 2001 From: rory Date: Mon, 2 Feb 2026 22:57:01 -0800 Subject: [PATCH 1/5] Capture user email per log line to prevent dissociation on Onyx.clear Co-authored-by: Cursor --- lib/Logger.ts | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/lib/Logger.ts b/lib/Logger.ts index 9009e32d..2db3638a 100644 --- a/lib/Logger.ts +++ b/lib/Logger.ts @@ -2,8 +2,14 @@ type Parameters = string | Record | Array Promise<{requestID: string}> | undefined; type ClientLoggingCallBack = (message: string, extraData: Parameters) => void; -type LogLine = {message: string; parameters: Parameters; onlyFlushWithOthers?: boolean; timestamp: Date}; -type LoggerOptions = {serverLoggingCallback: ServerLoggingCallback; isDebug: boolean; clientLoggingCallback: ClientLoggingCallBack; maxLogLinesBeforeFlush?: number}; +type LogLine = {message: string; parameters: Parameters; onlyFlushWithOthers?: boolean; timestamp: Date; email?: string | null}; +type LoggerOptions = { + serverLoggingCallback: ServerLoggingCallback; + isDebug: boolean; + clientLoggingCallback: ClientLoggingCallBack; + maxLogLinesBeforeFlush?: number; + getContextEmail?: () => string | null; +}; const MAX_LOG_LINES_BEFORE_FLUSH = 50; @@ -18,13 +24,16 @@ export default class Logger { maxLogLinesBeforeFlush: number; - constructor({serverLoggingCallback, isDebug, clientLoggingCallback, maxLogLinesBeforeFlush}: LoggerOptions) { + getContextEmail?: () => string | null; + + constructor({serverLoggingCallback, isDebug, clientLoggingCallback, maxLogLinesBeforeFlush, getContextEmail}: LoggerOptions) { // An array of log lines that limits itself to a certain number of entries (deleting the oldest) this.logLines = []; this.serverLoggingCallback = serverLoggingCallback; this.clientLoggingCallback = clientLoggingCallback; this.isDebug = isDebug; this.maxLogLinesBeforeFlush = maxLogLinesBeforeFlush || MAX_LOG_LINES_BEFORE_FLUSH; + this.getContextEmail = getContextEmail; // Public Methods this.info = this.info.bind(this); @@ -70,11 +79,16 @@ export default class Logger { * @param onlyFlushWithOthers A request will never be sent to the server if all loglines have this set to true */ add(message: string, parameters: Parameters, forceFlushToServer: boolean, onlyFlushWithOthers = false, extraData: Parameters = '') { + // Capture the user's email at the moment this specific log line is created + // This ensures the log retains user context even if the session is cleared before sending + const email = this.getContextEmail ? this.getContextEmail() : null; + const length = this.logLines.push({ message, parameters, onlyFlushWithOthers, timestamp: new Date(), + email, }); if (this.isDebug) { From ce05e3ebb73a036bdb7b29eca58467a33e517a6b Mon Sep 17 00:00:00 2001 From: rory Date: Mon, 2 Feb 2026 22:59:25 -0800 Subject: [PATCH 2/5] Update Logger tests to expect email field in log packets Co-authored-by: Cursor --- __tests__/Logger-test.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/__tests__/Logger-test.js b/__tests__/Logger-test.js index fb3f7f39..d194d5be 100644 --- a/__tests__/Logger-test.js +++ b/__tests__/Logger-test.js @@ -30,8 +30,8 @@ test('Test Log.info()', () => { delete packet[0].timestamp; delete packet[1].timestamp; expect(packet).toEqual([ - {message: "[info] Test1", parameters: "",}, - {message: "[info] Test2", parameters: "",}, + {message: "[info] Test1", parameters: "", email: null}, + {message: "[info] Test2", parameters: "", email: null}, ]); // Test the case where `isDebug` is `true` in `Log` instance and we pass `extraData` parameter @@ -53,7 +53,7 @@ test('Test Log.alert()', () => { ); const packet = JSON.parse(mockServerLoggingCallback.mock.calls[0][1].logPacket); delete packet[0].timestamp; - expect(packet).toEqual([{message: "[alrt] Test2", parameters: {}}]); + expect(packet).toEqual([{message: "[alrt] Test2", parameters: {}, email: null}]); }); test('Test Log.warn()', () => { @@ -69,7 +69,7 @@ test('Test Log.warn()', () => { ); const packet = JSON.parse(mockServerLoggingCallback.mock.calls[0][1].logPacket); delete packet[0].timestamp; - expect(packet).toEqual([{message: "[warn] Test2", parameters: ''}]); + expect(packet).toEqual([{message: "[warn] Test2", parameters: '', email: null}]); }); test('Test Log.hmmm()', () => { @@ -88,8 +88,8 @@ test('Test Log.hmmm()', () => { delete packet[0].timestamp; delete packet[1].timestamp; expect(packet).toEqual([ - {message: "[hmmm] Test", parameters: ''}, - {message: "[info] Test", parameters: ''} + {message: "[hmmm] Test", parameters: '', email: null}, + {message: "[info] Test", parameters: '', email: null} ]); }); From fe960ee44852d92a8556861985c574000d181801 Mon Sep 17 00:00:00 2001 From: rory Date: Tue, 3 Feb 2026 17:47:06 -0800 Subject: [PATCH 3/5] Add test for getContextEmail capturing email per log line Co-authored-by: Cursor --- __tests__/Logger-test.js | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/__tests__/Logger-test.js b/__tests__/Logger-test.js index d194d5be..b61d0945 100644 --- a/__tests__/Logger-test.js +++ b/__tests__/Logger-test.js @@ -98,3 +98,19 @@ test('Test Log.client()', () => { expect(mockClientLoggingCallback).toHaveBeenCalled(); expect(mockClientLoggingCallback).toHaveBeenCalledWith('Test', ''); }); + +test('Test getContextEmail captures email per log line', () => { + const mockCallback = jest.fn(); + const LogWithEmail = new Logger({ + serverLoggingCallback: mockCallback, + clientLoggingCallback: jest.fn(), + getContextEmail: () => 'test@example.com', + }); + + LogWithEmail.info('Test message', true); + expect(mockCallback).toHaveBeenCalled(); + + const packet = JSON.parse(mockCallback.mock.calls[0][1].logPacket); + delete packet[0].timestamp; + expect(packet).toEqual([{message: "[info] Test message", parameters: '', email: 'test@example.com'}]); +}); From 0078ade483946b7050f90df734005cacdb8ea602 Mon Sep 17 00:00:00 2001 From: rory Date: Tue, 3 Feb 2026 17:48:49 -0800 Subject: [PATCH 4/5] Wrap getContextEmail call in try/catch to prevent logging failures Co-authored-by: Cursor --- lib/Logger.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/Logger.ts b/lib/Logger.ts index 2db3638a..1985d6d4 100644 --- a/lib/Logger.ts +++ b/lib/Logger.ts @@ -81,7 +81,12 @@ export default class Logger { add(message: string, parameters: Parameters, forceFlushToServer: boolean, onlyFlushWithOthers = false, extraData: Parameters = '') { // Capture the user's email at the moment this specific log line is created // This ensures the log retains user context even if the session is cleared before sending - const email = this.getContextEmail ? this.getContextEmail() : null; + let email: string | null = null; + try { + email = this.getContextEmail ? this.getContextEmail() : null; + } catch { + // Silently fail if getContextEmail throws - logging should not crash + } const length = this.logLines.push({ message, From 362ba69b228d8e70b2b6ac249e66e56e979489a8 Mon Sep 17 00:00:00 2001 From: rory Date: Tue, 3 Feb 2026 17:49:28 -0800 Subject: [PATCH 5/5] Add test for getContextEmail throwing Co-authored-by: Cursor --- __tests__/Logger-test.js | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/__tests__/Logger-test.js b/__tests__/Logger-test.js index b61d0945..1e0cb4d5 100644 --- a/__tests__/Logger-test.js +++ b/__tests__/Logger-test.js @@ -114,3 +114,22 @@ test('Test getContextEmail captures email per log line', () => { delete packet[0].timestamp; expect(packet).toEqual([{message: "[info] Test message", parameters: '', email: 'test@example.com'}]); }); + +test('Test getContextEmail throwing does not break logging', () => { + const mockCallback = jest.fn(); + const LogWithThrowingEmail = new Logger({ + serverLoggingCallback: mockCallback, + clientLoggingCallback: jest.fn(), + getContextEmail: () => { + throw new Error('Failed to get email'); + }, + }); + + // Should not throw + LogWithThrowingEmail.info('Test message', true); + expect(mockCallback).toHaveBeenCalled(); + + const packet = JSON.parse(mockCallback.mock.calls[0][1].logPacket); + delete packet[0].timestamp; + expect(packet).toEqual([{message: "[info] Test message", parameters: '', email: null}]); +});