/* DOCX/XLSX parsing is isolated from the UI thread. No network URLs are used;
importScripts only reads the vendored files beside this application. */
"use strict";
const MAX_CELLS = 50000;
self.addEventListener("message", async (event) => {
const { id, kind, buffer } = event.data || {};
try {
if (kind === "docx") {
importScripts("vendor/mammoth.browser.min.js");
const result = await self.mammoth.convertToHtml(
{ arrayBuffer: buffer },
{ externalFileAccess: false }
);
self.postMessage({
id,
ok: true,
result: {
html: result.value,
warnings: result.messages.map((message) => message.message || String(message)),
},
});
return;
}
if (kind === "xlsx") {
importScripts("vendor/xlsx.full.min.js");
const workbook = self.XLSX.read(buffer, {
type: "array",
cellDates: false,
cellNF: true,
cellStyles: true,
});
const sheetMeta = new Map(
(workbook.Workbook?.Sheets || []).map((sheet) => [sheet.name, sheet])
);
const sheets = [];
let cellCount = 0;
for (const name of workbook.SheetNames) {
if (sheetMeta.get(name)?.Hidden) continue;
const worksheet = workbook.Sheets[name];
const ref = worksheet["!ref"];
if (!ref) {
sheets.push({ name, rows: [], merges: [], widths: [] });
continue;
}
const range = self.XLSX.utils.decode_range(ref);
const rowCount = range.e.r - range.s.r + 1;
const columnCount = range.e.c - range.s.c + 1;
cellCount += rowCount * columnCount;
if (cellCount > MAX_CELLS) {
throw new Error(`has more than ${MAX_CELLS.toLocaleString()} rendered cells.`);
}
const rows = [];
for (let rowIndex = range.s.r; rowIndex <= range.e.r; rowIndex += 1) {
const row = [];
for (let columnIndex = range.s.c; columnIndex <= range.e.c; columnIndex += 1) {
const address = self.XLSX.utils.encode_cell({ r: rowIndex, c: columnIndex });
const cell = worksheet[address];
if (!cell) row.push("");
else if (cell.w !== undefined) row.push(String(cell.w));
else if (cell.v !== undefined && cell.v !== null) row.push(String(cell.v));
else if (cell.f) row.push(`=${cell.f}`);
else row.push("");
}
rows.push(row);
}
const merges = (worksheet["!merges"] || []).map((merge) => ({
startRow: merge.s.r - range.s.r,
startColumn: merge.s.c - range.s.c,
rowSpan: merge.e.r - merge.s.r + 1,
colSpan: merge.e.c - merge.s.c + 1,
}));
const widths = Array.from({ length: columnCount }, (_, index) => {
const column = worksheet["!cols"]?.[range.s.c + index];
return column?.wpx || (column?.wch ? column.wch * 7 : null);
});
sheets.push({ name, rows, merges, widths });
}
if (!sheets.length) throw new Error("has no visible worksheets.");
self.postMessage({ id, ok: true, result: { sheets, cellCount } });
return;
}
throw new Error("uses an unsupported Office format.");
} catch (error) {
self.postMessage({ id, ok: false, error: error.message || String(error) });
}
});