diff --git a/README.md b/README.md index c6aefa2..e3bcdee 100644 --- a/README.md +++ b/README.md @@ -56,6 +56,8 @@ dist/ server/ ``` +Both bundles are built with source maps enabled, and `dist/client/main.js.map` is served alongside the client bundle. This is intentional: it eases in-browser debugging, and the source is public anyway. (The server-side `dist/server/index.cjs.map` is not exposed, since `express.static` only serves `dist/client`.) + Start locally: ```bash diff --git a/src/client/main.ts b/src/client/main.ts index 3a0f801..9e56ea0 100644 --- a/src/client/main.ts +++ b/src/client/main.ts @@ -482,14 +482,12 @@ function connectStream(): void { return; } - let displayedLines: number | null = null; if (!state.paused) { scheduleLiveRender({ logLines: 1 }); } completeSseTiming({ bufferedAfter: state.lines.length, - displayedLines, paused: state.paused, renderScheduled: !state.paused, source: line.source diff --git a/src/server/logTailer.test.ts b/src/server/logTailer.test.ts index 162af89..7328ce8 100644 --- a/src/server/logTailer.test.ts +++ b/src/server/logTailer.test.ts @@ -397,4 +397,41 @@ describe('LogTailer', () => { await cleanup(h); } }); + + it('normalizes CRLF line endings the same way as LF on bootstrap', async () => { + const h = makeHarness(); + try { + writeFileSync(h.filePath, 'a\r\nb\r\nc\r\n'); + const initial = await h.tailer.start(); + assert.deepEqual(initial.map((l) => l.rawLine), ['a', 'b', 'c']); + } finally { + await cleanup(h); + } + }); + + it('returns the trailing line even when the file does not end with a newline', async () => { + const h = makeHarness(); + try { + writeFileSync(h.filePath, 'a\nb\nc'); + const initial = await h.tailer.start(); + assert.deepEqual(initial.map((l) => l.rawLine), ['a', 'b', 'c']); + } finally { + await cleanup(h); + } + }); + + it('counts newlines correctly across multiple 64 KB read chunks (regression for #139)', async () => { + const h = makeHarness(); + try { + // 150 KB with no newlines: spans more than two 64 KB read chunks but + // stays well under MAX_TAIL_SCAN_BYTES (8 MB), so the backward scan + // must run to the start of the file without ever finding a newline. + const content = 'A'.repeat(150 * 1024); + writeFileSync(h.filePath, content); + const initial = await h.tailer.start(); + assert.deepEqual(initial.map((l) => l.rawLine), [content]); + } finally { + await cleanup(h); + } + }); }); diff --git a/src/server/logTailer.ts b/src/server/logTailer.ts index 767318a..a614eb2 100644 --- a/src/server/logTailer.ts +++ b/src/server/logTailer.ts @@ -433,10 +433,10 @@ async function readLastLines(filePath: string, maxLines: number): Promise= maxLines) {