Files
claude code agent 8ca0608b21 fix(logs): stable line ids, JSON string guard, streaming demuxer helper
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>
2026-06-29 07:03:10 +03:00

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']);
});
});