A split plan for a PDF is a list of parts, and each part is an array of zero-based page indices. When the parts are meant to divide a four-page document without overlap, counting four page references is not enough to establish coverage. [[0,1],[1,3]] contains four references, but index 1 appears twice and index 2 is absent.
For a well-formed plan like that, the function below returns the evidence instead of throwing: a missing list and a duplicates list that the caller can show or log before anything consumes the parts. It answers one small question: does every index from zero through pageCount - 1 occur exactly once across the parts? It does not establish that the parts are in the intended order or that a resulting PDF is correct.
Return the evidence, not just a count
The function takes arrays of zero-based indices. It records seen indices and repeated indices separately, then builds a sorted list of duplicates and a list of missing indices. A well-formed but incomplete plan returns those details. Malformed input throws instead: an empty part, a non-integer index, or an index outside the document is not treated as an ordinary coverage result.
The bounds belong to this teaching example: a page count from 1 to 1,000, one to 100 nonempty parts, and at most 2,000 total occurrences. They limit the inputs this function accepts and do not describe the limits of a PDF product. The function reads the arrays without changing them.
export function partitionCoverage(parts, pageCount) {
if (!Number.isSafeInteger(pageCount) || pageCount < 1 || pageCount > 1000 ||
!Array.isArray(parts) || parts.length < 1 || parts.length > 100) {
throw new TypeError('invalid partition input');
}
const seen = new Set(), repeated = new Set();
let occurrences = 0;
for (const part of parts) {
if (!Array.isArray(part) || !part.length || (occurrences += part.length) > 2000) {
throw new TypeError('invalid part');
}
for (const page of part) {
if (!Number.isSafeInteger(page) || page < 0 || page >= pageCount) {
throw new TypeError('invalid zero-based page index');
}
if (seen.has(page)) repeated.add(page);
seen.add(page);
}
}
const missing = Array.from({length: pageCount}, (_, i) => i).filter(i => !seen.has(i));
const duplicates = [...repeated].sort((a, b) => a - b);
return {complete: missing.length === 0 && duplicates.length === 0, missing, duplicates};
}
A complete result is not an order check
For [[0,1],[1,3]], the result reports missing: [2] and duplicates: [1]. [[3,2],[1,0]], on the other hand, is a complete partition under this contract: each index occurs once, even though the sequence is reversed. If the application needs consecutive parts in source order, it needs a separate, explicitly defined order check.
Paste these assertions after the function in the same .mjs file. The valid cases include incomplete results; “valid” here means the function can evaluate the input, not that the partition is complete. The assertions also check that the inputs remain unchanged.
import assert from 'node:assert/strict';
const valid = [
[[[0,1],[2,3]],4,{complete:true,missing:[],duplicates:[]}],
[[[0,1],[1,3]],4,{complete:false,missing:[2],duplicates:[1]}],
[[[3,2],[1,0]],4,{complete:true,missing:[],duplicates:[]}],
[[[0,0]],1,{complete:false,missing:[],duplicates:[0]}],
[[[0]],3,{complete:false,missing:[1,2],duplicates:[]}]
];
for (const [parts,count,expected] of valid) {
const before=JSON.stringify(parts);assert.deepEqual(partitionCoverage(parts,count),expected);
assert.equal(JSON.stringify(parts),before);
}
const invalid = [[[],3],[[[]],3],[[[3]],3],[[[-1]],3],[[['1']],3],[[[0.5]],3],[[[0]],0],[[[0]],1001],[[Array(2001).fill(0)],1],[Array(101).fill([0]),1]];
for(const [parts,count] of invalid) assert.throws(()=>partitionCoverage(parts,count));
console.log(JSON.stringify({valid:valid.length,rejected:invalid.length,boundary:'coverage only, not order or real PDF output'}));
The saved November 1 Node v22.23.4 run records five valid inputs and 10 rejected inputs. The stub exited with code 1 and the implementation with code 0. These synthetic assertions do not inspect page identity, printed numbering, rendering, navigation, or a real output file. No PDF decoding or writing, ZIP creation, browser or device behavior, or conversion was tested.
Disclosure: Drafted with GPT-6.1 Terra for Yan Wang / LensUp; Yan Wang is affiliated with LensUp. Polished by an independent editorial AI (lensup 主编); Codex (AI) fact-checked the text and checked the code against the retained synthetic runs. No human editor or owner reviewed or tested it. The background is November 1 source and public-page inspection plus the saved synthetic Node run described above.
Top comments (0)