Symptom
createLogger({ file: '/path/to.log' }) never creates or writes the log file when @objectstack/core/logger is consumed from an ESM entrypoint (which is what os serve / os dev run — the workspace is type: module). The same config works from a CJS consumer.
Repro (against built dist, Node v25)
// probe.mjsimport{createLogger}from'@objectstack/core/logger';importfsfrom'node:fs';constl=createLogger({format: 'pretty',file: '/tmp/probe.log'});l.info('esm file probe');awaitl.destroy();console.log('file exists:',fs.existsSync('/tmp/probe.log'));// → falseRoot cause
openFileStream() in packages/core/src/logger.ts does a lazy require('fs') ("Lazy require to avoid bundling issues" — the module is deliberately browser-safe for @objectstack/client). tsup/esbuild compiles that to the __require shim in the ESM output (dist/logger.js), which throwsDynamic require of "fs" is not supported at runtime; the surrounding try { … } catch { /* ignore — file logging is optional */ } swallows it, so file logging silently never activates. The CJS output (dist/logger.cjs) is unaffected because real require exists there.
Suggested fix
Keep the module browser-safe but stop relying on bundled require:
process.getBuiltinModule?.('node:fs') (Node ≥22.3, sync, already behind the existing typeof process !== 'undefined' guard and try/catch for older runtimes), orcreateRequire(import.meta.url) via a tsup shims-style banner, or an async import('node:fs').
Also consider logging a one-line warning instead of the bare catch {} when a file destination was explicitly configured but could not be opened — the current silence is exactly how this went unnoticed.
Found while fixing NO_COLOR/TTY handling in the same file (the fix PR keeps the file path plain-text but does not touch this).
Symptom
createLogger({ file: '/path/to.log' })never creates or writes the log file when@objectstack/core/loggeris consumed from an ESM entrypoint (which is whatos serve/os devrun — the workspace istype: module). The same config works from a CJS consumer.Repro (against built dist, Node v25)
Root cause
openFileStream()inpackages/core/src/logger.tsdoes a lazyrequire('fs')("Lazy require to avoid bundling issues" — the module is deliberately browser-safe for@objectstack/client). tsup/esbuild compiles that to the__requireshim in the ESM output (dist/logger.js), which throwsDynamic require of "fs" is not supportedat runtime; the surroundingtry { … } catch { /* ignore — file logging is optional */ }swallows it, so file logging silently never activates. The CJS output (dist/logger.cjs) is unaffected because realrequireexists there.Suggested fix
Keep the module browser-safe but stop relying on bundled
require:process.getBuiltinModule?.('node:fs')(Node ≥22.3, sync, already behind the existingtypeof process !== 'undefined'guard and try/catch for older runtimes), orcreateRequire(import.meta.url)via a tsupshims-style banner, or an asyncimport('node:fs').Also consider logging a one-line warning instead of the bare
catch {}when afiledestination was explicitly configured but could not be opened — the current silence is exactly how this went unnoticed.Found while fixing NO_COLOR/TTY handling in the same file (the fix PR keeps the file path plain-text but does not touch this).