From da982fe4c416ea17ecf10594456a50eeeed66f3c Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 17 Mar 2026 20:37:18 +0000 Subject: [PATCH 1/3] fix(chunkify): correct trailing chunk boundary condition The condition `offset !== text.length - 1` incorrectly skipped the trailing uncovered chunk when exactly one byte remained, silently dropping the last byte. Changed to `offset < text.length`. https://claude.ai/code/session_01CyibEZDpRhyvg4fw3edy1Z --- src/lib/chunkify.test.ts | 33 +++++++++++++++++++++++++++++++++ src/lib/chunkify.ts | 2 +- 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/src/lib/chunkify.test.ts b/src/lib/chunkify.test.ts index 3de5bb4..31e7b4f 100644 --- a/src/lib/chunkify.test.ts +++ b/src/lib/chunkify.test.ts @@ -103,3 +103,36 @@ test('creates a single chunk when none is covered', () => { ], } satisfies ChunkedCoverage) }) + +test('includes a trailing uncovered chunk when the last byte is not covered', () => { + // text length = 4; range covers first 3 bytes, leaving the last byte uncovered + let coverage = { + text: 'abcd', + ranges: [{ start: 0, end: 3 }], + url: 'https://example.com', + } + let result = chunkify(coverage) + delete coverage.ranges + expect(result).toEqual({ + ...coverage, + chunks: [ + { start_offset: 0, end_offset: 3, is_covered: true }, + { start_offset: 3, end_offset: 4, is_covered: false }, + ], + } satisfies ChunkedCoverage) +}) + +test('does not emit a spurious empty chunk when the last byte is covered', () => { + // range covers the full text — no trailing chunk should appear + let coverage = { + text: 'abcd', + ranges: [{ start: 0, end: 4 }], + url: 'https://example.com', + } + let result = chunkify(coverage) + delete coverage.ranges + expect(result).toEqual({ + ...coverage, + chunks: [{ start_offset: 0, end_offset: 4, is_covered: true }], + } satisfies ChunkedCoverage) +}) diff --git a/src/lib/chunkify.ts b/src/lib/chunkify.ts index d1466c9..e1d918e 100644 --- a/src/lib/chunkify.ts +++ b/src/lib/chunkify.ts @@ -78,7 +78,7 @@ export function chunkify(stylesheet: Coverage): ChunkedCoverage { } // fill up last chunk if necessary: - if (offset !== stylesheet.text.length - 1) { + if (offset < stylesheet.text.length) { chunks.push({ start_offset: offset, end_offset: stylesheet.text.length, From 7343eae998eae13aaee2e81405c40c5c23f38de3 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 17 Mar 2026 20:40:38 +0000 Subject: [PATCH 2/3] fix(chunkify): remove dead whitespace check from merge else-if branch MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The WHITESPACE_ONLY_REGEX.test() in the else-if was unreachable — the early continue at the top of the loop already handles all whitespace-only chunks. Keep only the empty-chunk (start === end) guard, which is the only reachable condition in that branch. Added tests for whitespace-gap merging and zero-length range absorption. https://claude.ai/code/session_01CyibEZDpRhyvg4fw3edy1Z --- src/lib/chunkify.test.ts | 37 +++++++++++++++++++++++++++++++++++++ src/lib/chunkify.ts | 7 ++----- 2 files changed, 39 insertions(+), 5 deletions(-) diff --git a/src/lib/chunkify.test.ts b/src/lib/chunkify.test.ts index 31e7b4f..8fe0b62 100644 --- a/src/lib/chunkify.test.ts +++ b/src/lib/chunkify.test.ts @@ -136,3 +136,40 @@ test('does not emit a spurious empty chunk when the last byte is covered', () => chunks: [{ start_offset: 0, end_offset: 4, is_covered: true }], } satisfies ChunkedCoverage) }) + +test('merges adjacent same-coverage chunks separated by whitespace-only gap', () => { + // The whitespace-only uncovered chunk between two covered chunks should be + // absorbed so the two covered chunks merge into one. This is handled by the + // early `continue` at the top of merge(), not the else-if branch. + let coverage = { + text: 'a{color:red}\n\nb{color:blue}', + // ^12 ^14 — the \n\n gap is whitespace-only + ranges: [ + { start: 0, end: 12 }, + { start: 14, end: 26 }, + ], + url: 'https://example.com', + } + let result = chunkify(coverage) + delete coverage.ranges + expect(result).toEqual({ + ...coverage, + chunks: [{ start_offset: 0, end_offset: 26, is_covered: true }], + } satisfies ChunkedCoverage) +}) + +test('absorbs a zero-length covered chunk into the surrounding uncovered chunk', () => { + // A zero-length range (start === end) produces an empty chunk. + // The empty chunk should not appear in the output. + let coverage = { + text: 'a{color:red}', + ranges: [{ start: 5, end: 5 }], + url: 'https://example.com', + } + let result = chunkify(coverage) + delete coverage.ranges + expect(result).toEqual({ + ...coverage, + chunks: [{ start_offset: 0, end_offset: 12, is_covered: false }], + } satisfies ChunkedCoverage) +}) diff --git a/src/lib/chunkify.ts b/src/lib/chunkify.ts index e1d918e..b0e9860 100644 --- a/src/lib/chunkify.ts +++ b/src/lib/chunkify.ts @@ -33,11 +33,8 @@ function merge(stylesheet: ChunkedCoverage): ChunkedCoverage { previous_chunk = chunk continue } - // If the current chunk is only whitespace or empty, add it to the previous - else if ( - WHITESPACE_ONLY_REGEX.test(stylesheet.text.slice(chunk.start_offset, chunk.end_offset)) || - chunk.end_offset === chunk.start_offset - ) { + // If the current chunk is empty (zero-length range), absorb it into the previous + else if (chunk.end_offset === chunk.start_offset) { latest_chunk.end_offset = chunk.end_offset // do not update previous_chunk continue From d8652fd767499fb7a93053d898b028ea2edc237a Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 17 Mar 2026 20:46:22 +0000 Subject: [PATCH 3/3] fix(chunkify): correct off-by-one in whitespace-merge test 'a{color:red}\n\nb{color:blue}' is 27 chars (12+2+13), so the second range end and expected end_offset should be 27, not 26. https://claude.ai/code/session_01CyibEZDpRhyvg4fw3edy1Z --- src/lib/chunkify.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib/chunkify.test.ts b/src/lib/chunkify.test.ts index 8fe0b62..6eb69ec 100644 --- a/src/lib/chunkify.test.ts +++ b/src/lib/chunkify.test.ts @@ -146,7 +146,7 @@ test('merges adjacent same-coverage chunks separated by whitespace-only gap', () // ^12 ^14 — the \n\n gap is whitespace-only ranges: [ { start: 0, end: 12 }, - { start: 14, end: 26 }, + { start: 14, end: 27 }, ], url: 'https://example.com', } @@ -154,7 +154,7 @@ test('merges adjacent same-coverage chunks separated by whitespace-only gap', () delete coverage.ranges expect(result).toEqual({ ...coverage, - chunks: [{ start_offset: 0, end_offset: 26, is_covered: true }], + chunks: [{ start_offset: 0, end_offset: 27, is_covered: true }], } satisfies ChunkedCoverage) })