Reject non-ZIP input (e.g. a legacy binary .xls) with a stable error code

Problem

When a non-ZIP file is passed to readXlsxFile — most commonly a legacy binary .xls (an OLE2 Compound File, not a ZIP) — the error surfaced to the caller is an obscure low-level message leaked from the transitive unzip dependency, e.g. Error: invalid signature: 0xe011cfd0 (the file's first four bytes read little-endian). There's no stable, typed way to detect this, so consumers end up string-matching the message (Hyrum's law).

Change

Validate the input's leading bytes at the two zip-layer entry points and throw an InvalidInputError — a class exported from every entry point (/node, /browser, /universal, /web-worker) — carrying a documented, stable reason property, following the same convention as the existing schema-parsing InvalidError class:

  • XLS_NOT_SUPPORTED — OLE2/CFB signature (D0 CF 11 E0), i.e. a binary .xls file
  • NOT_A_ZIP_ARCHIVE — any other input that isn't a ZIP archive

Anything starting with the ZIP signature (PK) passes through untouched. The class also sets name = 'InvalidInputError' so the error stays detectable via error.name in setups where instanceof is unreliable (ESM + CommonJS builds loaded together).

  • unzipFromArrayBufferUsingFunction (browser / universal / sync / web-worker) — sync check on the first bytes.
  • unzipFromStream (Node) — a non-consuming stream peek that unshifts the inspected bytes back, so the archive is still read in full; it accumulates across chunks.

Also included: TypeScript declarations (InvalidInputError, InvalidInputErrorReason), a README "Errors" section, and an InvalidInputError entry in the exports test.

Cost & tests

A ≤4-byte comparison — no measurable perf impact. Pure unit tests for the detector + integration tests through both entry points (including a stream delivered one byte at a time). No binary fixture committed — signatures are built in memory.

Edited by Etienne Prothon

Merge request reports

Loading