diff --git a/packages/logger/src/index.test.ts b/packages/logger/src/index.test.ts index 076fd74c293..ae9013b7361 100644 --- a/packages/logger/src/index.test.ts +++ b/packages/logger/src/index.test.ts @@ -46,6 +46,36 @@ describe('Logger', () => { }) }) + describe('browser suppression in production', () => { + const realProcess = globalThis.process + + afterEach(() => { + Reflect.deleteProperty(globalThis, 'window') + globalThis.process = realProcess + }) + + test('should keep logging when the server installs a DOM', () => { + globalThis.process = { + ...realProcess, + env: { ...realProcess.env, NODE_ENV: 'production' }, + } as typeof realProcess + Object.assign(globalThis, { window: { document: {} } }) + + createLogger('Test').error('server still logs') + + expect(consoleErrorSpy).toHaveBeenCalled() + }) + + test('should stay silent in a real browser', () => { + globalThis.process = { env: { NODE_ENV: 'production' } } as unknown as typeof realProcess + Object.assign(globalThis, { window: { document: {} } }) + + createLogger('Test').error('browser stays quiet') + + expect(consoleErrorSpy).not.toHaveBeenCalled() + }) + }) + describe('LogLevel enum', () => { test('should have correct log levels', () => { expect(LogLevel.DEBUG).toBe('DEBUG') diff --git a/packages/logger/src/index.ts b/packages/logger/src/index.ts index 25e6e3f9ae3..7092e19095b 100644 --- a/packages/logger/src/index.ts +++ b/packages/logger/src/index.ts @@ -48,6 +48,21 @@ const getNodeEnv = (): string => { return 'development' } +/** + * True only in a real browser. + * + * Server code can legitimately install a DOM — `ensureDomForTipTap` in the + * collab-doc converter mounts a jsdom `window` so TipTap runs headless — so the + * presence of `window` alone does not mean the browser. Node always exposes + * `process.versions.node` and a browser never does, which keeps a server-side + * DOM from silencing the logger for the rest of the process's life. + */ +const isBrowserRuntime = (): boolean => { + if (typeof (globalThis as { window?: unknown }).window === 'undefined') return false + const runtime = (globalThis as { process?: { versions?: { node?: unknown } } }).process + return typeof runtime?.versions?.node !== 'string' +} + const getLogLevel = (): string | undefined => { if (typeof process !== 'undefined' && process.env) { return process.env.LOG_LEVEL @@ -201,10 +216,7 @@ export class Logger { private shouldLog(level: LogLevel): boolean { if (!this.config.enabled) return false - if ( - getNodeEnv() === 'production' && - typeof (globalThis as { window?: unknown }).window !== 'undefined' - ) { + if (getNodeEnv() === 'production' && isBrowserRuntime()) { return false }