// Reads a ZIP the way a spreadsheet application does: from the central directory at the end. It is // the test's independent reader of what `buildWorkbook` wrote, so the writer is not checked by itself. import { inflateRawSync } from "node:zlib" export function unzip(bytes: Uint8Array): Map { const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength) let eocd = bytes.length - 22 while (eocd >= 0 && view.getUint32(eocd, true) !== 0x06054b50) eocd -= 1 if (eocd < 0) throw new Error("no end-of-central-directory record") const entries = view.getUint16(eocd + 10, true) let at = view.getUint32(eocd + 16, true) const files = new Map() for (let i = 0; i < entries; i += 1) { if (view.getUint32(at, true) !== 0x02014b50) throw new Error("a bad central directory entry") const method = view.getUint16(at + 10, true) const size = view.getUint32(at + 20, true) const nameLength = view.getUint16(at + 28, true) const extraLength = view.getUint16(at + 30, true) const commentLength = view.getUint16(at + 32, true) const local = view.getUint32(at + 42, true) const name = new TextDecoder().decode(bytes.subarray(at + 46, at + 46 + nameLength)) const dataAt = local + 30 + view.getUint16(local + 26, true) + view.getUint16(local + 28, true) const raw = bytes.subarray(dataAt, dataAt + size) files.set(name, new TextDecoder().decode(method === 8 ? inflateRawSync(raw) : raw)) at += 46 + nameLength + extraLength + commentLength } return files } /** The text and number cells of the first sheet, row by row. */ export function sheetRows(bytes: Uint8Array): Array> { const sheet = unzip(bytes).get("xl/worksheets/sheet1.xml") ?? "" return [...sheet.matchAll(/]*>(.*?)<\/row>/gs)].map(([, row]) => [...(row as string).matchAll(/]*)>(.*?)<\/c>/gs)].map(([, attributes, body]) => { if (!(attributes as string).includes('t="inlineStr"')) return Number(/(.*?)<\/v>/s.exec(body as string)?.[1]) const text = /]*>(.*?)<\/t>/s.exec(body as string)?.[1] ?? "" return text .replace(/"/g, '"') .replace(/>/g, ">") .replace(/</g, "<") .replace(/&/g, "&") }), ) }