Skip to content
Merged
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
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 0 additions & 2 deletions src/client/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
37 changes: 37 additions & 0 deletions src/server/logTailer.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
});
});
8 changes: 4 additions & 4 deletions src/server/logTailer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -433,10 +433,10 @@ async function readLastLines(filePath: string, maxLines: number): Promise<TailRe
// normalizing and splitting the whole accumulated tail on every iteration
// (issue #66). Each line terminator marks one complete line, which is all
// we need to decide when enough lines have been collected.
for (const byte of chunk) {
if (byte === 0x0a) {
newlineCount += 1;
}
let searchIndex = chunk.indexOf(0x0a);
while (searchIndex !== -1) {
newlineCount += 1;
searchIndex = chunk.indexOf(0x0a, searchIndex + 1);
}

if (newlineCount >= maxLines) {
Expand Down