8ca0608b21
Foundation for append-only log rendering and HTTP log streaming. - FormattedLine gains a stable, monotonically increasing `id` (new lineId.ts sequence), assigned centrally in formatLogs. Internal formatters now return id-less FormattedLineContent. This lets the viewer use `track by log.id` so already-rendered rows are never re-bound (fixes the text-selection collapse). - formatJSONLine: runtime guard so a bare JSON string/array log line falls back to plain text instead of rendering Object.keys as `0=h 1=e ...`. - createLogStreamProcessor: stateful demuxer that buffers streamed text, emits only complete lines (carrying the partial remainder), and reuses formatLogs/stripHeadersFunc for Docker 8-byte frame demux. - Unit tests for the demuxer, stable-id assignment and the JSON guard. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
30 lines
1.0 KiB
TypeScript
30 lines
1.0 KiB
TypeScript
import { formatLogs } from './formatLogs';
|
|
import { resetLineIdSequence } from './lineId';
|
|
|
|
beforeEach(() => {
|
|
resetLineIdSequence();
|
|
});
|
|
|
|
describe('formatLogs stable ids', () => {
|
|
it('gives every line a unique id within a call', () => {
|
|
const lines = formatLogs('one\ntwo\nthree\n');
|
|
const ids = lines.map((l) => l.id);
|
|
expect(new Set(ids).size).toBe(ids.length);
|
|
});
|
|
|
|
it('continues the id sequence across calls (so appended chunks never collide)', () => {
|
|
const first = formatLogs('a\nb\n');
|
|
const second = formatLogs('c\nd\n');
|
|
const allIds = [...first, ...second].map((l) => l.id);
|
|
expect(new Set(allIds).size).toBe(allIds.length);
|
|
// every id in the second batch is greater than every id in the first
|
|
const maxFirst = Math.max(...first.map((l) => l.id));
|
|
expect(Math.min(...second.map((l) => l.id))).toBeGreaterThan(maxFirst);
|
|
});
|
|
|
|
it('preserves the parsed text content', () => {
|
|
const lines = formatLogs('hello\nworld\n');
|
|
expect(lines.map((l) => l.line)).toEqual(['hello', 'world']);
|
|
});
|
|
});
|