sheet-to-schema 1.1.0-rc.2 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -14,7 +14,7 @@ one record per row, or per group of rows, reporting every cell it could not use.
14
14
  - **The same reader on both sides.** `sheet-to-schema/browser` reads a CSV, TSV or `.xlsx`
15
15
  file in a browser into exactly the rows the server reads from the same bytes.
16
16
 
17
- > **Status: stable (1.0.0); 1.1.0-rc.2 on `next`.** It is the import engine of
17
+ > **Status: stable (1.1.0).** It is the import engine of
18
18
  > [Dynamic Entity](https://github.com/berserker5619/Dynamic-Entity#readme) 3.0, extracted so it can be used without it.
19
19
  > Members marked `@internal` are not covered by semver and may change in any release.
20
20
 
@@ -204,8 +204,20 @@ file is a workbook.
204
204
 
205
205
  Known limitations of the browser path:
206
206
 
207
- - **It needs `DecompressionStream`** (every current browser, and Node 20) to read a workbook;
208
- without it, a workbook fails with a message saying so. A CSV needs nothing.
207
+ - **A workbook needs `DecompressionStream` with the `deflate-raw` format.** A CSV needs
208
+ nothing.
209
+
210
+ | Runtime | Reads a workbook from |
211
+ | --- | --- |
212
+ | Chrome, Edge (desktop and Android) | 103 |
213
+ | Firefox (desktop and Android) | 113 |
214
+ | Safari (macOS and iOS) | 16.4 |
215
+ | Node | 20.12 |
216
+
217
+ The browser versions are MDN browser-compat-data 8.1.6's for `deflate-raw`; Node 20.12 is
218
+ tested (20.11 lacks it). On an older runtime a workbook fails with `UNSUPPORTED_RUNTIME`,
219
+ saying which versions can read one; a CSV still reads. `/node` and the main entry still need
220
+ only Node 20.
209
221
  - **A broken archive may be described differently.** Both readers refuse it, but the server
210
222
  reads entries front to back and the browser reads the zip's directory, so the message for a
211
223
  damaged `.xlsx` can differ. Every well-formed workbook in the parity fixtures reads the same.
@@ -279,10 +291,12 @@ Four things to know before deploying it:
279
291
  `PLAN_PROBLEM_CODES`, `CELL_PROBLEM_CODES` and `RECORD_PROBLEM_CODES` list them all.
280
292
 
281
293
  A grouped import can also stop as a whole, with an `ImportFailure` (`IMPORT_FAILURE_CODES`).
282
- `applyMapping` and `runImport` throw it; over HTTP it is the error body's `code`:
294
+ `applyMapping` and `runImport` throw it; over HTTP it is the error body's `code`. And the
295
+ browser reader can refuse to read a workbook at all, with an `ImportError`:
283
296
 
284
297
  | Code | HTTP | Raised when |
285
298
  | --- | --- | --- |
299
+ | `UNSUPPORTED_RUNTIME` | (browser only) | `sheet-to-schema/browser` is asked to read a workbook where there is no `DecompressionStream` with `deflate-raw` (see the browser section for versions). A CSV is not affected. |
286
300
  | `GROUP_NOT_CONTIGUOUS` | 422 | A group's key comes back after its rows ended. `details` has the key and both rows. |
287
301
  | `GROUP_TOO_LARGE` | 413 | A group has more than `maxGroupRows` rows. |
288
302
  | `TOO_MANY_GROUPS` | 413 | More than `maxGroups` groups, or more than `maxGroupKeyBytes` of group keys. |
@@ -1,6 +1,6 @@
1
1
  import { SheetGrid, CsvDelimiter } from 'sheet-to-schema';
2
- import { I as ImportLimits } from '../import-error-Co6lRAqv.mjs';
3
- export { a as ImportError, b as ImportErrorCode } from '../import-error-Co6lRAqv.mjs';
2
+ import { I as ImportLimits } from '../import-error-DkGXydP2.mjs';
3
+ export { a as ImportError, b as ImportErrorCode } from '../import-error-DkGXydP2.mjs';
4
4
 
5
5
  /** The limits a browser read enforces. Each one left out is the server's default. */
6
6
  type BrowserLimits = Pick<ImportLimits, 'maxBytes' | 'maxRows' | 'maxColumns' | 'maxCellLength' | 'maxZipEntries'>;
@@ -26,7 +26,9 @@ type SheetFileInput = Blob | ArrayBuffer | Uint8Array;
26
26
  * and the same refusals with the same codes and messages.
27
27
  *
28
28
  * @throws ImportError `TOO_LARGE`, `SHEET_TOO_LARGE`, `UNSUPPORTED_FORMAT`, `MALFORMED_FILE`,
29
- * `ARCHIVE_REFUSED`, as the server would. A workbook read without `exceljs` installed throws a
29
+ * `ARCHIVE_REFUSED`, as the server would; and for a workbook, `UNSUPPORTED_RUNTIME` where the
30
+ * runtime has no `DecompressionStream` with `deflate-raw` (before Node 20.12, Chrome or Edge
31
+ * 103, Firefox 113, Safari 16.4). A workbook read without `exceljs` installed throws a
30
32
  * plain `Error` saying what to install.
31
33
  */
32
34
  declare function readSheetFile(input: SheetFileInput, options?: ReadSheetFileOptions): Promise<SheetFile>;
@@ -1,6 +1,6 @@
1
1
  import { SheetGrid, CsvDelimiter } from 'sheet-to-schema';
2
- import { I as ImportLimits } from '../import-error-Co6lRAqv.js';
3
- export { a as ImportError, b as ImportErrorCode } from '../import-error-Co6lRAqv.js';
2
+ import { I as ImportLimits } from '../import-error-DkGXydP2.js';
3
+ export { a as ImportError, b as ImportErrorCode } from '../import-error-DkGXydP2.js';
4
4
 
5
5
  /** The limits a browser read enforces. Each one left out is the server's default. */
6
6
  type BrowserLimits = Pick<ImportLimits, 'maxBytes' | 'maxRows' | 'maxColumns' | 'maxCellLength' | 'maxZipEntries'>;
@@ -26,7 +26,9 @@ type SheetFileInput = Blob | ArrayBuffer | Uint8Array;
26
26
  * and the same refusals with the same codes and messages.
27
27
  *
28
28
  * @throws ImportError `TOO_LARGE`, `SHEET_TOO_LARGE`, `UNSUPPORTED_FORMAT`, `MALFORMED_FILE`,
29
- * `ARCHIVE_REFUSED`, as the server would. A workbook read without `exceljs` installed throws a
29
+ * `ARCHIVE_REFUSED`, as the server would; and for a workbook, `UNSUPPORTED_RUNTIME` where the
30
+ * runtime has no `DecompressionStream` with `deflate-raw` (before Node 20.12, Chrome or Edge
31
+ * 103, Firefox 113, Safari 16.4). A workbook read without `exceljs` installed throws a
30
32
  * plain `Error` saying what to install.
31
33
  */
32
34
  declare function readSheetFile(input: SheetFileInput, options?: ReadSheetFileOptions): Promise<SheetFile>;
@@ -44,6 +44,8 @@ var STATUS = {
44
44
  INVALID_PLAN: 400,
45
45
  TIMEOUT: 408,
46
46
  IMPORT_FAILED: 500,
47
+ // Not an HTTP answer: only the browser reader raises it. 501 if one ever has to be chosen.
48
+ UNSUPPORTED_RUNTIME: 501,
47
49
  INTERNAL: 500
48
50
  };
49
51
  var ImportError = class extends Error {
@@ -266,11 +268,17 @@ function filterFormulas(xml) {
266
268
  }
267
269
 
268
270
  // src/browser/inflate.ts
271
+ var UNSUPPORTED_RUNTIME_MESSAGE = 'Reading .xlsx files needs DecompressionStream with the "deflate-raw" format, which this runtime does not have. Use Node 20.12+, Chrome or Edge 103+, Firefox 113+ or Safari 16.4+, or save the sheet as CSV.';
272
+ function assertDeflateRaw() {
273
+ try {
274
+ new DecompressionStream("deflate-raw");
275
+ } catch (cause) {
276
+ throw new ImportError("UNSUPPORTED_RUNTIME", UNSUPPORTED_RUNTIME_MESSAGE, { cause });
277
+ }
278
+ }
269
279
  var ignore = () => void 0;
270
280
  async function inflate(data, max) {
271
- if (typeof DecompressionStream !== "function") {
272
- throw new Error("This browser cannot read .xlsx files: it has no DecompressionStream. Use a current browser, or save the sheet as CSV.");
273
- }
281
+ assertDeflateRaw();
274
282
  const stream = new DecompressionStream("deflate-raw");
275
283
  const writer = stream.writable.getWriter();
276
284
  void writer.write(data).then(() => writer.close()).catch(ignore);
@@ -546,6 +554,7 @@ function guardGrid(raw, limits) {
546
554
  return { headers, rows };
547
555
  }
548
556
  async function xlsxGrid(bytes, limits) {
557
+ assertDeflateRaw();
549
558
  const parts = pickParts(bytes, limits);
550
559
  const Excel = await loadExcel();
551
560
  const filtered = await withoutFormulas(parts[parts.length - 1], limits);
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/shared/import-error.ts","../../src/shared/limits.ts","../../src/shared/optional.ts","../../src/shared/sheet-guards.ts","../../src/shared/sheet-format.ts","../../src/shared/xlsx-cells.ts","../../src/shared/formula-filter.ts","../../src/browser/inflate.ts","../../src/shared/zip.ts","../../src/browser/read-sheet-file.ts"],"names":["DEFAULT_GROUP_LIMITS","padRow","at","completeFirstLine","detectDelimiter","createCsvReader","cellText"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * import-error.ts — one error type, a closed set of codes, and an envelope that says nothing else.\n *\n * Browser-safe and shared: the server's readers and `sheet-to-schema/browser`'s throw the same\n * codes and messages for the same file. Each of those entries bundles its own copy, so match\n * on `code`, not on `instanceof` across entries. Turning one into a response is `/node`'s\n * (`toErrorBody` in `node/errors.ts`).\n *\n * **Information disclosure is a threat, so the envelope is the guard.** A stack trace tells an\n * attacker the library versions in use and the filesystem layout of the host; a parser's own\n * message tells them which parser. So a response carries a machine-readable `code` and a\n * message this package wrote, and nothing that came from below it. Whatever the real cause\n * was stays on the server, reachable through `cause` for the consumer's logger.\n */\n\nimport type { ImportFailureCode } from 'sheet-to-schema';\n\n/**\n * Why a request failed, in a form a client can branch on.\n *\n * Closed on purpose: a client that has to match on message text is a client that breaks when\n * the text improves, and the wire shape is shared with every client of the router.\n */\nexport type ImportErrorCode =\n /** No file part in the multipart body. */\n | 'NO_FILE'\n /** More file parts than `limits.maxFiles`. */\n | 'TOO_MANY_FILES'\n /** The body exceeded `limits.maxBytes`, or a field exceeded its own bound. */\n | 'TOO_LARGE'\n /** The bytes are not a format this package reads. Decided by content, not by filename. */\n | 'UNSUPPORTED_FORMAT'\n /** The bytes claim a format and are not valid in it. */\n | 'MALFORMED_FILE'\n /** A zip that inflates out of proportion to its size, or has too many entries. */\n | 'ARCHIVE_REFUSED'\n /** Rows, columns or one cell past the configured bound. */\n | 'SHEET_TOO_LARGE'\n /**\n * No adapter is registered under the requested name. Dynamic Entity's name for it, kept\n * because it is on the wire: renaming a code breaks every client that branches on it.\n */\n | 'UNKNOWN_ENTITY'\n /** The `plan` field is missing, is not JSON, or does not describe this schema. */\n | 'INVALID_PLAN'\n /** The request took longer than `limits.totalTimeoutMs`, or stalled past the idle bound. */\n | 'TIMEOUT'\n /** The consumer's `onImport` threw. Nothing about *why* crosses the wire. */\n | 'IMPORT_FAILED'\n /** Anything else. Deliberately the least informative code there is. */\n | 'INTERNAL';\n\n/** The HTTP status each code answers with. Kept here so a route never has to decide. */\nconst STATUS: Record<ImportErrorCode, number> = {\n NO_FILE: 400,\n TOO_MANY_FILES: 400,\n TOO_LARGE: 413,\n UNSUPPORTED_FORMAT: 415,\n MALFORMED_FILE: 400,\n ARCHIVE_REFUSED: 400,\n SHEET_TOO_LARGE: 413,\n UNKNOWN_ENTITY: 404,\n INVALID_PLAN: 400,\n TIMEOUT: 408,\n IMPORT_FAILED: 500,\n INTERNAL: 500,\n};\n\n/**\n * What a failing route sends.\n *\n * `details` is present only for `INVALID_PLAN`, and carries the plan's own problems. Those\n * describe the client's own plan against a schema the client already has, so they disclose\n * nothing the caller did not send.\n */\nexport interface ImportErrorBody {\n error: {\n /** An `ImportErrorCode`, or for a grouped import that stopped, an `ImportFailureCode`. */\n code: ImportErrorCode | ImportFailureCode;\n message: string;\n details?: unknown;\n };\n}\n\nexport class ImportError extends Error {\n readonly code: ImportErrorCode;\n readonly status: number;\n readonly details?: unknown;\n\n constructor(code: ImportErrorCode, message: string, options: { cause?: unknown; details?: unknown } = {}) {\n super(message);\n this.name = 'ImportError';\n this.code = code;\n this.status = STATUS[code] ?? 500;\n this.details = options.details;\n if (options.cause !== undefined) (this as { cause?: unknown }).cause = options.cause;\n }\n}\n","/**\n * limits.ts — every bound this package enforces, and its default.\n *\n * **Every limit has a finite default.** A limit that is only a limit when a consumer\n * remembers to set one is documentation, not a guard, and this is the first\n * attacker-reachable code in the repository. A consumer who wants more raises the number;\n * nobody has to know the number exists to be protected by it.\n *\n * Each is enforced **during** streaming rather than after. A check that runs once the file is\n * in memory has already lost the thing it was checking.\n */\n\nimport { DEFAULT_GROUP_LIMITS, type GroupLimits } from 'sheet-to-schema';\n\n/**\n * Every bound, with the grouping limits (`maxGroupRows`, `maxGroups`, `maxGroupKeyBytes`) from\n * the main entry's `GroupLimits`, which `runImport` enforces while streaming exactly as\n * `applyMapping` does in memory.\n */\nexport interface ImportLimits extends GroupLimits {\n // ── The upload itself ──────────────────────────────────────────────────────\n\n /** Total bytes accepted from the request body. Counted as they arrive. */\n maxBytes: number;\n /** Multipart fields accepted, so a body of ten thousand tiny parts is not a parse loop. */\n maxFields: number;\n /** Bytes accepted in one non-file field — in practice the JSON `plan`. */\n maxFieldBytes: number;\n /** Bytes accepted in a field *name*, which is also attacker-chosen. */\n maxFieldNameBytes: number;\n /** Files accepted. One import is one file; more is either a mistake or an attack. */\n maxFiles: number;\n\n // ── The sheet ─────────────────────────────────────────────────────────────\n\n /** Data rows read. The header is not one of them. */\n maxRows: number;\n /** Columns read from the header row, and the width any row may reach. */\n maxColumns: number;\n /**\n * Characters in one cell.\n *\n * This is also the bound on a target's `pattern`: a schema-supplied regex with\n * catastrophic backtracking is CPU-bound in the length of what it is run against, and on a\n * server that input is attacker-chosen.\n */\n maxCellLength: number;\n\n // ── The zip an .xlsx actually is ──────────────────────────────────────────\n\n /** Total inflated bytes across every entry, checked *as they inflate*. */\n maxUncompressedBytes: number;\n /** Inflated-to-compressed ratio at which an entry is refused. */\n maxCompressionRatio: number;\n /** Entries in the archive. A workbook has tens; a bomb has thousands. */\n maxZipEntries: number;\n\n // ── The run ───────────────────────────────────────────────────────────────\n\n /**\n * Rows handed to `applyMapping`, and therefore to `onBatch`, at a time.\n *\n * Peak memory is a function of this number and not of the file's size. That is the entire\n * reason this package exists.\n */\n batchSize: number;\n /**\n * Row errors **retained**. The count and the truncation flag are always exact.\n *\n * A file where every row fails accumulates one error object per failing row, and fifty\n * thousand of those is the same unbounded growth as holding the file — the failure this\n * package exists to avoid, arrived at from the other direction.\n *\n * Lowering it costs a user *reasons*, never *counts*: `errorCount` and `failed` are tallied\n * as rows go past and do not depend on what was kept.\n */\n maxReportedErrors: number;\n /** Rows a preview returns as a sample. */\n sampleRows: number;\n\n // ── The clock ─────────────────────────────────────────────────────────────\n\n /** Milliseconds of silence on the request body before it is abandoned. Slowloris. */\n idleTimeoutMs: number;\n /** Milliseconds a whole request may take, however chatty it is. */\n totalTimeoutMs: number;\n}\n\n/**\n * Sized for \"a large legitimate export\", not for \"the largest thing imaginable\".\n *\n * 10 MB of xlsx is comfortably the fifty-thousand-row workbook that motivated the package;\n * `maxRows` is above it so the byte limit is the one a user meets first, and the row limit is\n * the backstop for a file that is mostly empty cells.\n */\nexport const DEFAULT_LIMITS: ImportLimits = {\n ...DEFAULT_GROUP_LIMITS,\n\n maxBytes: 10 * 1024 * 1024,\n maxFields: 16,\n maxFieldBytes: 1024 * 1024,\n maxFieldNameBytes: 200,\n maxFiles: 1,\n\n maxRows: 200_000,\n maxColumns: 512,\n maxCellLength: 32_768,\n\n maxUncompressedBytes: 200 * 1024 * 1024,\n maxCompressionRatio: 200,\n maxZipEntries: 512,\n\n batchSize: 500,\n maxReportedErrors: 200,\n sampleRows: 5,\n\n idleTimeoutMs: 30_000,\n totalTimeoutMs: 10 * 60_000,\n};\n\n/**\n * Fill in what a caller left out.\n *\n * A non-finite or non-positive override falls back to the default rather than being accepted:\n * `maxBytes: 0` from a mis-parsed environment variable would otherwise disable every upload,\n * and `maxBytes: Infinity` would disable the guard — the two ways a configuration mistake\n * turns a limit off, both of which look deliberate in a diff.\n */\nexport function resolveLimits(overrides?: Partial<ImportLimits>): ImportLimits {\n const out = { ...DEFAULT_LIMITS };\n if (!overrides) return out;\n\n for (const key of Object.keys(DEFAULT_LIMITS) as (keyof ImportLimits)[]) {\n const value = overrides[key];\n if (typeof value === 'number' && Number.isFinite(value) && value > 0) {\n out[key] = Math.floor(value);\n }\n }\n return out;\n}\n","/**\n * optional.ts — loading a package this one only needs for some of what an entry does.\n *\n * `exceljs` reads and writes workbooks and `busboy` reads a multipart upload. Neither is\n * needed to read a CSV, so both are optional peer dependencies and are loaded when first used,\n * by `/node` and, for `exceljs`, by `/browser`. When one is missing, the failure has to say so:\n * Node's own \"Cannot find module\" names a file deep inside this package and not what to do.\n */\n\n/** Node's two spellings of \"that package is not installed\", for `require` and for `import`. */\nconst MISSING = new Set(['MODULE_NOT_FOUND', 'ERR_MODULE_NOT_FOUND']);\n\n/** Whether `error` says the package `name` itself is absent, not something inside it. */\nfunction isMissing(error: unknown, name: string): boolean {\n if (typeof error !== 'object' || error === null) return false;\n const { code, message } = error as { code?: unknown; message?: unknown };\n return MISSING.has(String(code)) && String(message).includes(`'${name}'`);\n}\n\n/**\n * Load an optional peer dependency for `entry`, or fail with what to install and why.\n *\n * `load` is the import itself, written at the call site so a bundler can see it. Any other\n * failure, such as a broken install of the package, is rethrown as it is.\n */\nexport async function loadPeer<T>(entry: string, name: string, neededTo: string, load: () => Promise<T>): Promise<T> {\n try {\n return await load();\n } catch (error) {\n if (!isMissing(error, name)) throw error;\n throw new Error(\n `${entry} needs the \"${name}\" package to ${neededTo}, and it is not installed. ` +\n `It is an optional peer dependency: run \\`npm install ${name}\\`.`,\n { cause: error },\n );\n }\n}\n","/**\n * sheet-guards.ts — the row, column and cell bounds, checked one row at a time.\n *\n * Browser-safe and shared: the server's `guardRows` checks each streamed row with these, and\n * the browser reader checks each row of the file it holds, so both refuse the same sheet at the\n * same row with the same message.\n */\nimport { padRow } from 'sheet-to-schema';\nimport { ImportError } from './import-error';\nimport type { ImportLimits } from './limits';\n\ntype SheetLimits = Pick<ImportLimits, 'maxRows' | 'maxColumns' | 'maxCellLength'>;\n\n/** The header row, checked once it is read: its width is every later row's. */\nexport function guardHeaders(headers: readonly string[], limits: SheetLimits): void {\n if (headers.length > limits.maxColumns) {\n throw new ImportError('SHEET_TOO_LARGE', `The sheet has more than ${limits.maxColumns} columns.`);\n }\n}\n\n/**\n * Check data row number `seen` (1 for the row after the header) and pad it to `width`.\n *\n * Padding matches `parseCsv`, through the same `padRow` the main entry exports, so a file\n * imported on a server and the same file imported in a browser produce the same rows.\n */\nexport function guardRow(row: unknown[], seen: number, width: number, limits: SheetLimits): unknown[] {\n if (seen > limits.maxRows) {\n throw new ImportError('SHEET_TOO_LARGE', `The sheet has more than ${limits.maxRows} rows.`);\n }\n if (row.length > limits.maxColumns) {\n throw new ImportError('SHEET_TOO_LARGE', `Row ${seen + 1} has more than ${limits.maxColumns} columns.`);\n }\n for (const cell of row) {\n if (typeof cell === 'string' && cell.length > limits.maxCellLength) {\n throw new ImportError('SHEET_TOO_LARGE', `A cell in row ${seen + 1} is longer than ${limits.maxCellLength} characters.`);\n }\n }\n return padRow(row, width);\n}\n","/**\n * sheet-format.ts — what a file is, decided by its first bytes.\n *\n * Browser-safe and shared, so the server's `readSheet` and `sheet-to-schema/browser`'s\n * `readSheetFile` refuse the same files with the same messages and read the rest the same way.\n */\nimport { ImportError } from './import-error';\n\n/** The formats this package reads. Both are decided by magic bytes. */\nexport type SheetFormat = 'csv' | 'xlsx';\n\n/** Enough bytes to tell a zip from text, with room for a UTF-16 BOM. */\nexport const MAGIC_BYTES = 8;\n\nconst ZIP = [0x50, 0x4b]; // \"PK\"\nconst OLE2 = [0xd0, 0xcf, 0x11, 0xe0]; // a pre-2007 .xls, which is a compound file, not a zip\nconst UTF16_LE = [0xff, 0xfe];\nconst UTF16_BE = [0xfe, 0xff];\n\nconst startsWith = (bytes: Uint8Array, signature: number[]): boolean =>\n signature.every((byte, i) => bytes[i] === byte);\n\n/**\n * What these bytes actually are.\n *\n * Exported because the multipart layer wants the same answer for the same reason, and because\n * a second copy of a magic-byte table is a second table to be wrong.\n */\nexport function detectFormat(head: Uint8Array): SheetFormat {\n if (startsWith(head, ZIP)) return 'xlsx';\n\n if (startsWith(head, OLE2)) {\n throw new ImportError(\n 'UNSUPPORTED_FORMAT',\n 'This is a pre-2007 Excel file (.xls). Save it as .xlsx or .csv and try again.',\n );\n }\n if (startsWith(head, UTF16_LE) || startsWith(head, UTF16_BE)) {\n throw new ImportError(\n 'UNSUPPORTED_FORMAT',\n 'This file is UTF-16 encoded. Save it as UTF-8 CSV and try again.',\n );\n }\n\n // Anything else is read as delimited text. A binary file that reaches here produces a sheet\n // of nonsense rather than an error, which is the right trade: refusing everything that does\n // not look like text would refuse legitimately unusual encodings too, and nonsense rows are\n // reported per row by the mapping rather than as a mysterious rejection.\n return 'csv';\n}\n","/**\n * xlsx-cells.ts — what one ExcelJS cell value becomes in the grid.\n *\n * Browser-safe and shared, so the server's streaming reader and `sheet-to-schema/browser`'s\n * reader turn the same cell into the same value by construction, not by agreement.\n */\n\n/**\n * Turn one exceljs cell value into the value a record should be built from.\n *\n * A formula cell yields its **result**, never its formula: a sheet someone uploaded may\n * contain anything, and importing the text `=SUM(A1:A9)` into a field is importing a string\n * the user never saw. An error cell yields its error text, which fails coercion loudly rather\n * than importing as blank.\n */\nexport function cellValue(raw: unknown): unknown {\n if (raw === null || raw === undefined) return '';\n if (raw instanceof Date) return raw;\n if (typeof raw !== 'object') return raw;\n\n const cell = raw as Record<string, unknown>;\n if (Array.isArray(cell['richText'])) {\n return (cell['richText'] as { text?: unknown }[]).map(part => String(part?.text ?? '')).join('');\n }\n if ('formula' in cell || 'sharedFormula' in cell) return cellValue(cell['result']);\n if ('error' in cell) return String(cell['error']);\n if ('text' in cell) return String(cell['text']);\n return '';\n}\n\n/** `row.values` is one-based with a hole at index 0, and sparse wherever a cell was empty. */\nexport function rowCells(values: unknown): unknown[] {\n if (!Array.isArray(values)) return [];\n const out: unknown[] = [];\n for (let i = 1; i < values.length; i++) out.push(cellValue(values[i]));\n return out;\n}\n","/**\n * formula-filter.ts — worksheet XML with its formulas taken out, so each formula cell is read as\n * the typed cell its cached result is.\n *\n * Browser-safe and shared: the server filters the worksheet as it rebuilds the archive\n * (`node/strip-formulas.ts`), the browser before ExcelJS loads it, so both parse the same XML.\n *\n * Why: with its `<f>` in place, ExcelJS's streaming reader keeps a cached result as text when\n * Excel stored it as text and otherwise as `parseFloat` of it, unstyled (a `TRUE` read as `1`,\n * an error as nothing, a date as its serial), and `Workbook.xlsx.load` drops a cached `0` or\n * `false`. Without it, both read the cell by their ordinary path: `t=\"b\"` a boolean, `t=\"e\"` its\n * error text, a number in a date format a `Date`, `0` as `0`. An import reads a formula's result\n * and never its formula, so taking the formula out loses nothing it uses.\n *\n * Works on bytes: the markup is ASCII, and no byte of a multi-byte UTF-8 character is below\n * 0x80. Formula text cannot hold a `<` (XML escapes it), so `</f>` ends the body.\n */\n\nconst LT = 0x3c;\nconst GT = 0x3e;\nconst SLASH = 0x2f;\nconst F = 0x66;\nconst END_TAG = [LT, SLASH, F, GT];\n/** What may follow `<f` in an `f` element's tag: `<f>`, `<f/>`, `<f t=\"shared\" …>`. */\nconst AFTER_NAME = new Set([GT, SLASH, 0x20, 0x09, 0x0d, 0x0a]);\n\nconst enum State {\n Outside,\n StartTag,\n Body,\n}\n\n/** Removes every `<f …>…</f>` and `<f …/>` from a stream of XML bytes, across any chunking. */\nexport class FormulaFilter {\n private state = State.Outside;\n private carry: Uint8Array = new Uint8Array(0);\n /** The last byte of the start tag seen so far, to tell `<f …/>` from `<f …>`. */\n private previous = 0;\n\n /** The next chunk in; what of it is certain to be kept out. */\n push(chunk: Uint8Array): Uint8Array {\n const bytes = this.carry.length ? concat([this.carry, chunk]) : chunk;\n this.carry = new Uint8Array(0);\n const out: Uint8Array[] = [];\n let i = 0;\n\n while (i < bytes.length) {\n if (this.state === State.Outside) {\n const start = i;\n let j = i;\n let entered = false;\n for (; j < bytes.length; j++) {\n if (bytes[j] !== LT) continue;\n if (j + 2 >= bytes.length) break; // `<` or `<f` at the very end: the next chunk decides.\n if (bytes[j + 1] === F && AFTER_NAME.has(bytes[j + 2])) {\n out.push(bytes.subarray(start, j));\n this.state = State.StartTag;\n this.previous = 0;\n i = j + 2;\n entered = true;\n break;\n }\n }\n if (entered) continue;\n out.push(bytes.subarray(start, j));\n this.carry = bytes.slice(j);\n i = bytes.length;\n } else if (this.state === State.StartTag) {\n const end = bytes.indexOf(GT, i);\n if (end < 0) {\n this.previous = bytes[bytes.length - 1];\n break;\n }\n const before = end > i ? bytes[end - 1] : this.previous;\n this.state = before === SLASH ? State.Outside : State.Body;\n i = end + 1;\n } else {\n const end = indexOfTag(bytes, i);\n if (end < 0) {\n // The formula text is dropped; keep only a tail that could be the start of `</f>`.\n let keep = 0;\n for (let k = Math.min(3, bytes.length - i); k > 0 && !keep; k--) {\n if (END_TAG.slice(0, k).every((byte, n) => bytes[bytes.length - k + n] === byte)) keep = k;\n }\n this.carry = bytes.slice(bytes.length - keep);\n break;\n }\n this.state = State.Outside;\n i = end + END_TAG.length;\n }\n }\n return concat(out);\n }\n\n /** No more input: whatever was held back to see what came next. */\n end(): Uint8Array {\n const rest = this.state === State.Outside ? this.carry : new Uint8Array(0);\n this.carry = new Uint8Array(0);\n return rest;\n }\n}\n\nfunction indexOfTag(bytes: Uint8Array, from: number): number {\n for (let at = bytes.indexOf(LT, from); at >= 0 && at + 3 < bytes.length; at = bytes.indexOf(LT, at + 1)) {\n if (bytes[at + 1] === SLASH && bytes[at + 2] === F && bytes[at + 3] === GT) return at;\n }\n return -1;\n}\n\nexport function concat(parts: readonly Uint8Array[]): Uint8Array {\n if (parts.length === 1) return parts[0];\n const out = new Uint8Array(parts.reduce((sum, part) => sum + part.length, 0));\n let at = 0;\n for (const part of parts) {\n out.set(part, at);\n at += part.length;\n }\n return out;\n}\n\n/** Filter a whole worksheet held in memory. */\nexport function filterFormulas(xml: Uint8Array): Uint8Array {\n const filter = new FormulaFilter();\n return concat([filter.push(xml), filter.end()]);\n}\n","/**\n * inflate.ts — a raw-deflate entry inflated in a browser, bounded as it goes.\n *\n * `DecompressionStream` is the browser's own (and Node 20's). Inflating is where an archive's\n * real size shows, so the output is counted chunk by chunk and refused past `max` with the\n * server's message.\n */\nimport { concat } from '../shared/formula-filter';\nimport { ImportError } from '../shared/import-error';\n\n/** A failure to write is the same failure the reader reports; it is not reported twice. */\nconst ignore = (): void => undefined;\n\nexport async function inflate(data: Uint8Array, max: number): Promise<Uint8Array> {\n if (typeof DecompressionStream !== 'function') {\n throw new Error('This browser cannot read .xlsx files: it has no DecompressionStream. Use a current browser, or save the sheet as CSV.');\n }\n const stream = new DecompressionStream('deflate-raw');\n const writer = stream.writable.getWriter();\n // Not awaited: the reader below drains what the writer pushes, and awaiting first would wait\n // on a stream nothing is reading. A failure to inflate surfaces through the reader.\n void writer\n .write(data as Uint8Array<ArrayBuffer>)\n .then(() => writer.close())\n .catch(ignore);\n const reader = stream.readable.getReader();\n const chunks: Uint8Array[] = [];\n let size = 0;\n try {\n for (let next = await reader.read(); !next.done; next = await reader.read()) {\n size += next.value.length;\n if (size > max) {\n await reader.cancel();\n throw new ImportError('ARCHIVE_REFUSED', `The workbook inflates to more than the ${max} byte limit.`);\n }\n chunks.push(next.value);\n }\n } catch (error) {\n if (error instanceof ImportError) throw error;\n throw new ImportError('MALFORMED_FILE', 'This is not a readable workbook.', { cause: error });\n }\n return concat(chunks);\n}\n","/**\n * zip.ts — which parts of an `.xlsx` an import reads, and the archive that holds only those.\n *\n * Browser-safe and shared. The server's `guardZip` walks the upload entry by entry, inflating to\n * measure; the browser's reader reads the central directory instead (it holds the whole file,\n * and inflating is ExcelJS's job there). Both keep the **same parts** by the same rules and\n * repack them the same way, so ExcelJS on either side parses the same archive.\n *\n * What the browser checks here is what the directory *declares*: entry count, sizes, ratio. A\n * file can lie about those, and only inflating would catch it; that check stays on the server,\n * where the file is someone else's. In a browser the file is the user's own, and only their tab\n * is at risk.\n */\nimport { ImportError } from './import-error';\n\n/** `PK\\x03\\x04` — the start of a local file header. */\nexport const LOCAL_HEADER = 0x04034b50;\n/** `PK\\x01\\x02` — a central directory record. */\nexport const CENTRAL_HEADER = 0x02014b50;\n/** `PK\\x05\\x06` — the end-of-central-directory record. */\nexport const END_OF_CENTRAL = 0x06054b50;\n/** Bytes in a local file header before the name. */\nexport const HEADER_SIZE = 30;\n/** Bytes in a central directory record before the name. */\nconst CENTRAL_SIZE = 46;\n/** Bytes in the end-of-central-directory record, without its comment. */\nconst END_SIZE = 22;\n/** Bit 3 of the general-purpose flags: sizes follow the data rather than preceding it. */\nexport const HAS_DATA_DESCRIPTOR = 0x08;\n/** A size of exactly this means \"see the zip64 extra field\", which is a size we do not trust. */\nexport const ZIP64_SENTINEL = 0xffffffff;\n\nexport const STORED = 0;\nexport const DEFLATED = 8;\n\n/** An entry under this many inflated bytes is never refused for its ratio: its header dominates. */\nexport const RATIO_FLOOR = 64 * 1024;\n\n/**\n * The parts an import reads, in the order exceljs must meet them.\n *\n * Relationships, the workbook and the shared strings before any worksheet — that ordering is\n * what keeps exceljs's streaming reader off its deferral path. Everything not named here, and\n * every worksheet but the lowest-numbered, is dropped.\n */\nexport const WANTED = [\n '[Content_Types].xml',\n '_rels/.rels',\n 'xl/_rels/workbook.xml.rels',\n 'xl/workbook.xml',\n 'xl/styles.xml',\n 'xl/sharedStrings.xml',\n];\n\nconst WORKSHEET = /^xl\\/worksheets\\/sheet(\\d+)\\.xml$/;\n\n/**\n * The number in `sheetN.xml`, or `null` for anything that is not a worksheet.\n *\n * **Which worksheet an import reads is never decided by byte order.** A user means the first\n * *tab*, and an archive may list `sheet2.xml` before `sheet1.xml`.\n *\n * The lowest number is not *quite* tab order either: the real order lives in `workbook.xml`'s\n * `<sheets>` and the relationship map, which this does not parse. It is a great deal closer\n * than byte order, and it is the same number exceljs matches a worksheet by.\n */\nexport function sheetNumber(name: string): number | null {\n const match = WORKSHEET.exec(name);\n return match ? Number(match[1]) : null;\n}\n\nexport const refuse = (message: string): never => {\n throw new ImportError('ARCHIVE_REFUSED', message);\n};\n\n// ─── CRC-32, because a rebuilt entry needs a checksum the reader will accept ──────────────\n\nconst CRC_TABLE = (() => {\n const table = new Int32Array(256);\n for (let i = 0; i < 256; i++) {\n let c = i;\n for (let k = 0; k < 8; k++) c = c & 1 ? 0xedb88320 ^ (c >>> 1) : c >>> 1;\n table[i] = c;\n }\n return table;\n})();\n\nexport function crc32(current: number, bytes: Uint8Array): number {\n let c = ~current;\n for (let i = 0; i < bytes.length; i++) c = CRC_TABLE[(c ^ bytes[i]) & 0xff] ^ (c >>> 8);\n return ~c >>> 0;\n}\n\n/** One part of the rebuilt archive: its data exactly as it was, still compressed. */\nexport interface PackedEntry {\n name: string;\n method: number;\n data: Uint8Array;\n inflated: number;\n crc: number;\n}\n\n/**\n * The tracker both sides use to decide what to keep, entry by entry in archive order: the\n * wanted parts (first copy of each) and the lowest-numbered worksheet.\n */\nexport class PartPicker<E> {\n readonly kept = new Map<string, E>();\n /** The lowest-numbered worksheet kept so far, which is the one an import reads. */\n sheet: { name: string; number: number } | null = null;\n\n /** Whether to keep `name`. A worksheet that supersedes the kept one releases it first. */\n wants(name: string): { keep: boolean; released?: E } {\n const number = sheetNumber(name);\n if (number === null) return { keep: WANTED.includes(name) && !this.kept.has(name) };\n if (this.sheet !== null && number >= this.sheet.number) return { keep: false };\n const released = this.sheet ? this.kept.get(this.sheet.name) : undefined;\n if (this.sheet) this.kept.delete(this.sheet.name);\n this.sheet = null;\n return { keep: true, released };\n }\n\n keep(name: string, entry: E): void {\n this.kept.set(name, entry);\n const number = sheetNumber(name);\n if (number !== null) this.sheet = { name, number };\n }\n\n /**\n * The kept parts in the order the parser must meet them, or the refusal for an archive with\n * no worksheet.\n */\n ordered(): E[] {\n if (!this.sheet) {\n // Any zip starts `PK`, so a `.docx` or a plain archive reaches here too. Without a workbook\n // part it is not a broken workbook but a different kind of file, and saying so is what\n // tells the user to export the right thing rather than to repair this one.\n if (!this.kept.has('xl/workbook.xml')) {\n throw new ImportError(\n 'UNSUPPORTED_FORMAT',\n 'This is a zip archive that is not an Excel workbook. Upload an .xlsx or .csv file.',\n );\n }\n throw new ImportError('MALFORMED_FILE', 'This workbook has no worksheet.');\n }\n return [...WANTED, this.sheet.name].map(name => this.kept.get(name)).filter((entry): entry is E => entry !== undefined);\n }\n}\n\n/** Repack the kept entries as a zip, in the order given. Entry data is copied, never inflated. */\nexport function repack(entries: readonly PackedEntry[]): Uint8Array {\n const encoder = new TextEncoder();\n const names = entries.map(entry => encoder.encode(entry.name));\n const localSize = entries.reduce((sum, entry, i) => sum + HEADER_SIZE + names[i].length + entry.data.length, 0);\n const directorySize = entries.reduce((sum, _entry, i) => sum + CENTRAL_SIZE + names[i].length, 0);\n const out = new Uint8Array(localSize + directorySize + END_SIZE);\n const view = new DataView(out.buffer);\n\n let at = 0;\n const offsets: number[] = [];\n entries.forEach((entry, i) => {\n offsets.push(at);\n view.setUint32(at, LOCAL_HEADER, true);\n view.setUint16(at + 4, 20, true); // version needed\n view.setUint16(at + 6, 0, true); // flags: sizes are right here, so no data descriptor\n view.setUint16(at + 8, entry.method, true);\n view.setUint32(at + 14, entry.crc, true);\n view.setUint32(at + 18, entry.data.length, true);\n view.setUint32(at + 22, entry.inflated, true);\n view.setUint16(at + 26, names[i].length, true);\n out.set(names[i], at + HEADER_SIZE);\n out.set(entry.data, at + HEADER_SIZE + names[i].length);\n at += HEADER_SIZE + names[i].length + entry.data.length;\n });\n\n const directoryStart = at;\n entries.forEach((entry, i) => {\n view.setUint32(at, CENTRAL_HEADER, true);\n view.setUint16(at + 4, 20, true); // version made by\n view.setUint16(at + 6, 20, true); // version needed\n view.setUint16(at + 10, entry.method, true);\n view.setUint32(at + 16, entry.crc, true);\n view.setUint32(at + 20, entry.data.length, true);\n view.setUint32(at + 24, entry.inflated, true);\n view.setUint16(at + 28, names[i].length, true);\n view.setUint32(at + 42, offsets[i], true);\n out.set(names[i], at + CENTRAL_SIZE);\n at += CENTRAL_SIZE + names[i].length;\n });\n\n view.setUint32(at, END_OF_CENTRAL, true);\n view.setUint16(at + 8, entries.length, true);\n view.setUint16(at + 10, entries.length, true);\n view.setUint32(at + 12, at - directoryStart, true);\n view.setUint32(at + 16, directoryStart, true);\n return out;\n}\n\n/** The bounds the directory is checked against. */\nexport interface DirectoryLimits {\n maxZipEntries: number;\n maxUncompressedBytes: number;\n maxCompressionRatio: number;\n}\n\nconst malformed = (): never => {\n throw new ImportError('MALFORMED_FILE', 'The workbook could not be read.');\n};\n\n/**\n * Check a whole archive by its central directory, and return the parts an import reads,\n * repacked exactly as the server repacks them. Nothing is inflated.\n *\n * Refused with the server's codes and messages: too many entries, a declared total past\n * `maxUncompressedBytes`, a declared ratio past `maxCompressionRatio`, an unsupported method, a\n * stored entry without a declared size. A directory that cannot be read is `MALFORMED_FILE`.\n */\nexport function pickFromDirectory(bytes: Uint8Array, limits: DirectoryLimits): Uint8Array {\n return repack(pickParts(bytes, limits));\n}\n\n/**\n * The parts `pickFromDirectory` keeps, in the parser's order, before they are repacked: the\n * worksheet is the last. For a caller that rebuilds the worksheet first.\n */\nexport function pickParts(bytes: Uint8Array, limits: DirectoryLimits): PackedEntry[] {\n const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);\n const u16 = (at: number): number => view.getUint16(at, true);\n const u32 = (at: number): number => view.getUint32(at, true);\n\n // The end record is the last thing in the file, followed by a comment of up to 64 KB.\n let end = -1;\n for (let at = bytes.length - END_SIZE; at >= Math.max(0, bytes.length - END_SIZE - 0xffff); at--) {\n if (u32(at) === END_OF_CENTRAL) {\n end = at;\n break;\n }\n }\n if (end < 0) return malformed();\n\n const count = u16(end + 10);\n const directoryLength = u32(end + 12);\n const directoryStart = u32(end + 16);\n if (count === 0xffff || directoryStart === ZIP64_SENTINEL) return refuse('The workbook uses zip64, which is not supported.');\n if (count > limits.maxZipEntries) return refuse(`The workbook has more than ${limits.maxZipEntries} entries.`);\n if (directoryStart + directoryLength > end) return malformed();\n\n const decoder = new TextDecoder('utf-8');\n const picker = new PartPicker<PackedEntry>();\n let total = 0;\n let at = directoryStart;\n\n for (let i = 0; i < count; i++) {\n if (at + CENTRAL_SIZE > end || u32(at) !== CENTRAL_HEADER) return malformed();\n const flags = u16(at + 8);\n const method = u16(at + 10);\n const crc = u32(at + 16);\n const compressed = u32(at + 20);\n const inflated = u32(at + 24);\n const nameLength = u16(at + 28);\n const extraLength = u16(at + 30);\n const commentLength = u16(at + 32);\n const localOffset = u32(at + 42);\n if (at + CENTRAL_SIZE + nameLength > end) return malformed();\n const name = decoder.decode(bytes.subarray(at + CENTRAL_SIZE, at + CENTRAL_SIZE + nameLength));\n at += CENTRAL_SIZE + nameLength + extraLength + commentLength;\n\n if (compressed === ZIP64_SENTINEL || inflated === ZIP64_SENTINEL) {\n return refuse('The workbook uses zip64, which is not supported.');\n }\n if (method === STORED && (flags & HAS_DATA_DESCRIPTOR) !== 0) {\n return refuse(`Entry \"${name}\" is stored without a declared size.`);\n }\n if (method !== STORED && method !== DEFLATED) {\n return refuse(`Entry \"${name}\" uses compression method ${method}, which is not supported.`);\n }\n total += inflated;\n if (total > limits.maxUncompressedBytes) {\n return refuse(`The workbook inflates to more than the ${limits.maxUncompressedBytes} byte limit.`);\n }\n if (inflated > RATIO_FLOOR && inflated / Math.max(compressed, 1) > limits.maxCompressionRatio) {\n return refuse(`An entry in the workbook inflates more than ${limits.maxCompressionRatio} times — refusing it.`);\n }\n\n if (!picker.wants(name).keep) continue;\n if (localOffset + HEADER_SIZE > bytes.length || u32(localOffset) !== LOCAL_HEADER) return malformed();\n const start = localOffset + HEADER_SIZE + u16(localOffset + 26) + u16(localOffset + 28);\n if (start + compressed > bytes.length) return malformed();\n picker.keep(name, { name, method, data: bytes.subarray(start, start + compressed), inflated, crc });\n }\n\n return picker.ordered();\n}\n","/**\n * read-sheet-file.ts — a whole CSV, TSV or `.xlsx` file in, the server's grid out (1.1 spec, A).\n *\n * The server's `readSheet` streams; a browser already holds the file, so this reads it in one\n * pass. Everything that decides *what* the grid is comes from the same code the server runs:\n *\n * - the format, from the first bytes (`shared/sheet-format.ts`), never from the name;\n * - CSV: the main entry's reader and delimiter detection, fed the whole text at once, decoded\n * the server's way (UTF-8, not fatal, byte-order mark dropped);\n * - `.xlsx`: the same parts picked from the archive and repacked the same way\n * (`shared/zip.ts`), then each cell through the same rules (`shared/xlsx-cells.ts`);\n * - the limits, their defaults, the row checks and the error codes and messages\n * (`shared/limits.ts`, `shared/sheet-guards.ts`, `shared/import-error.ts`).\n *\n * The one difference is the parser's entry point. The server uses ExcelJS's streaming\n * `WorkbookReader`, which only its Node build has; a browser build has `Workbook.xlsx.load`.\n * Two things make `load` read what the streaming reader reads:\n *\n * - the worksheet's formulas are taken out first, as the server takes them out\n * (`shared/formula-filter.ts`), so a formula cell is its cached result, typed, on both sides;\n * - it is told to ignore every worksheet node but `sheetData`, as the streaming reader does.\n * Merges, hyperlinks and the rest are never applied (a merged range's covered cells stay\n * empty, a hyperlink cell stays its text), and a part never parsed cannot be hostile;\n */\nimport type ExcelJSModule from 'exceljs';\nimport { cellText, completeFirstLine, createCsvReader, detectDelimiter, type CsvDelimiter, type SheetGrid } from 'sheet-to-schema';\nimport { ImportError } from '../shared/import-error';\nimport { resolveLimits, type ImportLimits } from '../shared/limits';\nimport { loadPeer } from '../shared/optional';\nimport { guardHeaders, guardRow } from '../shared/sheet-guards';\nimport { detectFormat, MAGIC_BYTES } from '../shared/sheet-format';\nimport { rowCells } from '../shared/xlsx-cells';\nimport { filterFormulas } from '../shared/formula-filter';\nimport { inflate } from './inflate';\nimport { crc32, pickParts, repack, STORED, type PackedEntry } from '../shared/zip';\n\n/** The limits a browser read enforces. Each one left out is the server's default. */\nexport type BrowserLimits = Pick<ImportLimits, 'maxBytes' | 'maxRows' | 'maxColumns' | 'maxCellLength' | 'maxZipEntries'>;\n\nexport interface ReadSheetFileOptions {\n /**\n * The file's name. A delimiter hint only, as on the server: once the bytes have said the file\n * is text, a name ending in `.tsv` means tab. It never decides the format.\n */\n filename?: string;\n limits?: Partial<BrowserLimits>;\n}\n\n/** What `readSheetFile` reads: the grid, what the bytes turned out to be, and a CSV's separator. */\nexport interface SheetFile extends SheetGrid {\n format: 'csv' | 'xlsx';\n /** The separator a CSV turned out to use; pass it to `decimalMarkFor`. Absent for a workbook. */\n delimiter?: CsvDelimiter;\n}\n\n/** What a file can arrive as: a `File` or other `Blob`, or its bytes. */\nexport type SheetFileInput = Blob | ArrayBuffer | Uint8Array;\n\ntype ExcelJS = typeof ExcelJSModule;\n\n/** Loaded on the first workbook, never for a CSV: a CSV-only app never downloads it. */\nconst loadExcel = (): Promise<ExcelJS> =>\n loadPeer('sheet-to-schema/browser', 'exceljs', 'read .xlsx files', async () => (await import('exceljs')).default);\n\n/**\n * Read a CSV, TSV or `.xlsx` file into exactly the grid the server's `readSheet` reads from the\n * same bytes: the same headers, the same rows with the same typed cells, the same separator,\n * and the same refusals with the same codes and messages.\n *\n * @throws ImportError `TOO_LARGE`, `SHEET_TOO_LARGE`, `UNSUPPORTED_FORMAT`, `MALFORMED_FILE`,\n * `ARCHIVE_REFUSED`, as the server would. A workbook read without `exceljs` installed throws a\n * plain `Error` saying what to install.\n */\nexport async function readSheetFile(input: SheetFileInput, options: ReadSheetFileOptions = {}): Promise<SheetFile> {\n const limits = resolveLimits(options.limits);\n const bytes = await bytesOf(input, limits.maxBytes);\n const format = detectFormat(bytes.subarray(0, MAGIC_BYTES));\n if (format === 'xlsx') return { format, ...(await xlsxGrid(bytes, limits)) };\n return csvGrid(bytes, limits, /\\.tsv$/i.test(options.filename ?? '') ? '\\t' : undefined);\n}\n\n/**\n * Read an `.xlsx` file by the server's rules, for a caller that already knows the format.\n *\n * @throws ImportError `UNSUPPORTED_FORMAT` for anything that is not a zip, and whatever\n * `readSheetFile` throws for a workbook.\n */\nexport async function readXlsx(input: SheetFileInput, options: Omit<ReadSheetFileOptions, 'filename'> = {}): Promise<SheetGrid> {\n const limits = resolveLimits(options.limits);\n const bytes = await bytesOf(input, limits.maxBytes);\n if (detectFormat(bytes.subarray(0, MAGIC_BYTES)) !== 'xlsx') {\n throw new ImportError('UNSUPPORTED_FORMAT', 'This is not an .xlsx workbook. Read a CSV with readSheetFile.');\n }\n return xlsxGrid(bytes, limits);\n}\n\n// ─── Bytes ────────────────────────────────────────────────────────────────────────────────\n\nconst tooLarge = (maxBytes: number): never => {\n throw new ImportError('TOO_LARGE', `The file is larger than the ${maxBytes} byte limit.`);\n};\n\n/**\n * The input's bytes, refusing past `maxBytes` before reading a `Blob` at all.\n *\n * Told apart by shape, not `instanceof`: a typed array or a `Blob` from another realm (an\n * iframe, a test environment) fails `instanceof` and is still one.\n */\nasync function bytesOf(input: SheetFileInput, maxBytes: number): Promise<Uint8Array> {\n if (ArrayBuffer.isView(input)) {\n if (input.byteLength > maxBytes) tooLarge(maxBytes);\n return new Uint8Array(input.buffer, input.byteOffset, input.byteLength);\n }\n if (Object.prototype.toString.call(input) === '[object ArrayBuffer]') {\n const buffer = input as ArrayBuffer;\n if (buffer.byteLength > maxBytes) tooLarge(maxBytes);\n return new Uint8Array(buffer);\n }\n const blob = input as Blob;\n if (blob !== null && typeof blob === 'object' && typeof blob.size === 'number' && typeof blob.slice === 'function') {\n if (blob.size > maxBytes) tooLarge(maxBytes);\n return new Uint8Array(await blobBuffer(blob));\n }\n throw new TypeError('readSheetFile reads a Blob (such as a File), an ArrayBuffer or a Uint8Array.');\n}\n\n/** `Blob.arrayBuffer`, or `FileReader` where a browser or test environment lacks it. */\nfunction blobBuffer(blob: Blob): Promise<ArrayBuffer> {\n if (typeof blob.arrayBuffer === 'function') return blob.arrayBuffer();\n return new Promise((resolve, reject) => {\n const reader = new FileReader();\n reader.onload = () => resolve(reader.result as ArrayBuffer);\n reader.onerror = () => reject(reader.error);\n reader.readAsArrayBuffer(blob);\n });\n}\n\n// ─── CSV ──────────────────────────────────────────────────────────────────────────────────\n\n/** The server's `csvRows`, given the whole file as its one chunk. */\nfunction csvGrid(bytes: Uint8Array, limits: ImportLimits, forced: CsvDelimiter | undefined): SheetFile {\n // Not `fatal`, as on the server: one bad byte is one U+FFFD in one cell the importer can\n // report on, not a reason to refuse the file.\n const text = new TextDecoder('utf-8').decode(bytes);\n const line = completeFirstLine(text);\n if (line === null && text.length > limits.maxCellLength * limits.maxColumns + limits.maxColumns) {\n throw new ImportError('SHEET_TOO_LARGE', 'The header row is longer than any row may be.');\n }\n const delimiter = forced ?? detectDelimiter(line ?? text);\n const reader = createCsvReader({ delimiter });\n const { headers, rows } = guardGrid([...reader.push(text), ...reader.end()], limits);\n return { format: 'csv', headers, rows, delimiter };\n}\n\n/** Header row through `cellText`, then every data row through the server's checks, in order. */\nfunction guardGrid(raw: Iterable<unknown[]>, limits: ImportLimits): SheetGrid {\n const iterator = raw[Symbol.iterator]();\n const first = iterator.next();\n const headers = first.done ? [] : first.value.map(cellText);\n guardHeaders(headers, limits);\n const rows: unknown[][] = [];\n for (let next = iterator.next(); !next.done; next = iterator.next()) rows.push(guardRow(next.value, rows.length + 1, headers.length, limits));\n return { headers, rows };\n}\n\n// ─── xlsx ─────────────────────────────────────────────────────────────────────────────────\n\n/**\n * The archive is checked by its directory and cut down to the server's parts before ExcelJS is\n * even loaded, so an over-limit zip never reaches it.\n */\nasync function xlsxGrid(bytes: Uint8Array, limits: ImportLimits): Promise<SheetGrid> {\n const parts = pickParts(bytes, limits);\n const Excel = await loadExcel();\n const filtered = await withoutFormulas(parts[parts.length - 1], limits);\n const archive = repack([...parts.slice(0, -1), filtered]);\n const workbook = new Excel.Workbook();\n try {\n await workbook.xlsx.load(archive as unknown as Parameters<typeof workbook.xlsx.load>[0], { ignoreNodes: IGNORED_NODES } as object);\n } catch (error) {\n // What the parser says about itself is not for the caller to show, as on the server.\n throw new ImportError('MALFORMED_FILE', 'This is not a readable workbook.', { cause: error });\n }\n const sheet = workbook.worksheets[0];\n if (!sheet || sheet.rowCount === 0) {\n // As on the server: a sheet with not one row is not a file anyone meant to import, and\n // \"imported 0 rows\" is the wrong thing to say about it.\n throw new ImportError('MALFORMED_FILE', 'This workbook has no readable rows.');\n }\n return guardGrid(sheetRows(sheet, limits), limits);\n}\n\n/**\n * The worksheet without its formulas, stored rather than deflated: a browser has a standard\n * decompressor (`DecompressionStream`) and the archive is only read here, so there is nothing to\n * gain by compressing it again. Inflating is where an archive's real size shows, so it is\n * bounded by `maxUncompressedBytes` as it goes, with the server's message.\n */\nasync function withoutFormulas(sheet: PackedEntry, limits: ImportLimits): Promise<PackedEntry> {\n const xml = sheet.method === STORED ? sheet.data : await inflate(sheet.data, limits.maxUncompressedBytes);\n const data = filterFormulas(xml);\n return { name: sheet.name, method: STORED, data, inflated: data.length, crc: crc32(0, data) };\n}\n\n/** Every worksheet node `load` would parse but the server's streaming reader does not. */\nconst IGNORED_NODES = [\n 'sheetPr', 'dimension', 'sheetViews', 'sheetFormatPr', 'cols', 'autoFilter', 'mergeCells', 'rowBreaks',\n 'hyperlinks', 'pageMargins', 'dataValidations', 'pageSetup', 'headerFooter', 'printOptions', 'picture',\n 'drawing', 'sheetProtection', 'tableParts', 'conditionalFormatting', 'extLst',\n];\n\n/**\n * The server's `xlsxRows` over a loaded worksheet: every row from 1, a gap filled with blank\n * rows so later rows keep their numbers, a row past `maxRows` refused before its gap is made.\n */\nfunction* sheetRows(sheet: ExcelJSModule.Worksheet, limits: ImportLimits): Generator<unknown[]> {\n let expected = 1;\n for (let number = 1; number <= sheet.rowCount; number++) {\n const row = sheet.findRow(number);\n if (!row) continue;\n if (number - 1 > limits.maxRows) {\n throw new ImportError('SHEET_TOO_LARGE', `The sheet has more than ${limits.maxRows} rows.`);\n }\n while (expected < number) {\n yield [];\n expected++;\n }\n const values: unknown[] = [];\n row.eachCell((cell, column) => {\n values[column] = cell.value;\n });\n yield rowCells(values);\n expected = number + 1;\n }\n}\n"]}
1
+ {"version":3,"sources":["../../src/shared/import-error.ts","../../src/shared/limits.ts","../../src/shared/optional.ts","../../src/shared/sheet-guards.ts","../../src/shared/sheet-format.ts","../../src/shared/xlsx-cells.ts","../../src/shared/formula-filter.ts","../../src/browser/inflate.ts","../../src/shared/zip.ts","../../src/browser/read-sheet-file.ts"],"names":["DEFAULT_GROUP_LIMITS","padRow","at","completeFirstLine","detectDelimiter","createCsvReader","cellText"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * import-error.ts — one error type, a closed set of codes, and an envelope that says nothing else.\n *\n * Browser-safe and shared: the server's readers and `sheet-to-schema/browser`'s throw the same\n * codes and messages for the same file. Each of those entries bundles its own copy, so match\n * on `code`, not on `instanceof` across entries. Turning one into a response is `/node`'s\n * (`toErrorBody` in `node/errors.ts`).\n *\n * **Information disclosure is a threat, so the envelope is the guard.** A stack trace tells an\n * attacker the library versions in use and the filesystem layout of the host; a parser's own\n * message tells them which parser. So a response carries a machine-readable `code` and a\n * message this package wrote, and nothing that came from below it. Whatever the real cause\n * was stays on the server, reachable through `cause` for the consumer's logger.\n */\n\nimport type { ImportFailureCode } from 'sheet-to-schema';\n\n/**\n * Why a request failed, in a form a client can branch on.\n *\n * Closed on purpose: a client that has to match on message text is a client that breaks when\n * the text improves, and the wire shape is shared with every client of the router.\n */\nexport type ImportErrorCode =\n /** No file part in the multipart body. */\n | 'NO_FILE'\n /** More file parts than `limits.maxFiles`. */\n | 'TOO_MANY_FILES'\n /** The body exceeded `limits.maxBytes`, or a field exceeded its own bound. */\n | 'TOO_LARGE'\n /** The bytes are not a format this package reads. Decided by content, not by filename. */\n | 'UNSUPPORTED_FORMAT'\n /** The bytes claim a format and are not valid in it. */\n | 'MALFORMED_FILE'\n /** A zip that inflates out of proportion to its size, or has too many entries. */\n | 'ARCHIVE_REFUSED'\n /** Rows, columns or one cell past the configured bound. */\n | 'SHEET_TOO_LARGE'\n /**\n * No adapter is registered under the requested name. Dynamic Entity's name for it, kept\n * because it is on the wire: renaming a code breaks every client that branches on it.\n */\n | 'UNKNOWN_ENTITY'\n /** The `plan` field is missing, is not JSON, or does not describe this schema. */\n | 'INVALID_PLAN'\n /** The request took longer than `limits.totalTimeoutMs`, or stalled past the idle bound. */\n | 'TIMEOUT'\n /** The consumer's `onImport` threw. Nothing about *why* crosses the wire. */\n | 'IMPORT_FAILED'\n /**\n * The browser reader runs where a workbook cannot be read: no `DecompressionStream` with the\n * `deflate-raw` format. Raised by `sheet-to-schema/browser` only, never by the server.\n */\n | 'UNSUPPORTED_RUNTIME'\n /** Anything else. Deliberately the least informative code there is. */\n | 'INTERNAL';\n\n/** The HTTP status each code answers with. Kept here so a route never has to decide. */\nconst STATUS: Record<ImportErrorCode, number> = {\n NO_FILE: 400,\n TOO_MANY_FILES: 400,\n TOO_LARGE: 413,\n UNSUPPORTED_FORMAT: 415,\n MALFORMED_FILE: 400,\n ARCHIVE_REFUSED: 400,\n SHEET_TOO_LARGE: 413,\n UNKNOWN_ENTITY: 404,\n INVALID_PLAN: 400,\n TIMEOUT: 408,\n IMPORT_FAILED: 500,\n // Not an HTTP answer: only the browser reader raises it. 501 if one ever has to be chosen.\n UNSUPPORTED_RUNTIME: 501,\n INTERNAL: 500,\n};\n\n/**\n * What a failing route sends.\n *\n * `details` is present only for `INVALID_PLAN`, and carries the plan's own problems. Those\n * describe the client's own plan against a schema the client already has, so they disclose\n * nothing the caller did not send.\n */\nexport interface ImportErrorBody {\n error: {\n /** An `ImportErrorCode`, or for a grouped import that stopped, an `ImportFailureCode`. */\n code: ImportErrorCode | ImportFailureCode;\n message: string;\n details?: unknown;\n };\n}\n\nexport class ImportError extends Error {\n readonly code: ImportErrorCode;\n readonly status: number;\n readonly details?: unknown;\n\n constructor(code: ImportErrorCode, message: string, options: { cause?: unknown; details?: unknown } = {}) {\n super(message);\n this.name = 'ImportError';\n this.code = code;\n this.status = STATUS[code] ?? 500;\n this.details = options.details;\n if (options.cause !== undefined) (this as { cause?: unknown }).cause = options.cause;\n }\n}\n","/**\n * limits.ts — every bound this package enforces, and its default.\n *\n * **Every limit has a finite default.** A limit that is only a limit when a consumer\n * remembers to set one is documentation, not a guard, and this is the first\n * attacker-reachable code in the repository. A consumer who wants more raises the number;\n * nobody has to know the number exists to be protected by it.\n *\n * Each is enforced **during** streaming rather than after. A check that runs once the file is\n * in memory has already lost the thing it was checking.\n */\n\nimport { DEFAULT_GROUP_LIMITS, type GroupLimits } from 'sheet-to-schema';\n\n/**\n * Every bound, with the grouping limits (`maxGroupRows`, `maxGroups`, `maxGroupKeyBytes`) from\n * the main entry's `GroupLimits`, which `runImport` enforces while streaming exactly as\n * `applyMapping` does in memory.\n */\nexport interface ImportLimits extends GroupLimits {\n // ── The upload itself ──────────────────────────────────────────────────────\n\n /** Total bytes accepted from the request body. Counted as they arrive. */\n maxBytes: number;\n /** Multipart fields accepted, so a body of ten thousand tiny parts is not a parse loop. */\n maxFields: number;\n /** Bytes accepted in one non-file field — in practice the JSON `plan`. */\n maxFieldBytes: number;\n /** Bytes accepted in a field *name*, which is also attacker-chosen. */\n maxFieldNameBytes: number;\n /** Files accepted. One import is one file; more is either a mistake or an attack. */\n maxFiles: number;\n\n // ── The sheet ─────────────────────────────────────────────────────────────\n\n /** Data rows read. The header is not one of them. */\n maxRows: number;\n /** Columns read from the header row, and the width any row may reach. */\n maxColumns: number;\n /**\n * Characters in one cell.\n *\n * This is also the bound on a target's `pattern`: a schema-supplied regex with\n * catastrophic backtracking is CPU-bound in the length of what it is run against, and on a\n * server that input is attacker-chosen.\n */\n maxCellLength: number;\n\n // ── The zip an .xlsx actually is ──────────────────────────────────────────\n\n /** Total inflated bytes across every entry, checked *as they inflate*. */\n maxUncompressedBytes: number;\n /** Inflated-to-compressed ratio at which an entry is refused. */\n maxCompressionRatio: number;\n /** Entries in the archive. A workbook has tens; a bomb has thousands. */\n maxZipEntries: number;\n\n // ── The run ───────────────────────────────────────────────────────────────\n\n /**\n * Rows handed to `applyMapping`, and therefore to `onBatch`, at a time.\n *\n * Peak memory is a function of this number and not of the file's size. That is the entire\n * reason this package exists.\n */\n batchSize: number;\n /**\n * Row errors **retained**. The count and the truncation flag are always exact.\n *\n * A file where every row fails accumulates one error object per failing row, and fifty\n * thousand of those is the same unbounded growth as holding the file — the failure this\n * package exists to avoid, arrived at from the other direction.\n *\n * Lowering it costs a user *reasons*, never *counts*: `errorCount` and `failed` are tallied\n * as rows go past and do not depend on what was kept.\n */\n maxReportedErrors: number;\n /** Rows a preview returns as a sample. */\n sampleRows: number;\n\n // ── The clock ─────────────────────────────────────────────────────────────\n\n /** Milliseconds of silence on the request body before it is abandoned. Slowloris. */\n idleTimeoutMs: number;\n /** Milliseconds a whole request may take, however chatty it is. */\n totalTimeoutMs: number;\n}\n\n/**\n * Sized for \"a large legitimate export\", not for \"the largest thing imaginable\".\n *\n * 10 MB of xlsx is comfortably the fifty-thousand-row workbook that motivated the package;\n * `maxRows` is above it so the byte limit is the one a user meets first, and the row limit is\n * the backstop for a file that is mostly empty cells.\n */\nexport const DEFAULT_LIMITS: ImportLimits = {\n ...DEFAULT_GROUP_LIMITS,\n\n maxBytes: 10 * 1024 * 1024,\n maxFields: 16,\n maxFieldBytes: 1024 * 1024,\n maxFieldNameBytes: 200,\n maxFiles: 1,\n\n maxRows: 200_000,\n maxColumns: 512,\n maxCellLength: 32_768,\n\n maxUncompressedBytes: 200 * 1024 * 1024,\n maxCompressionRatio: 200,\n maxZipEntries: 512,\n\n batchSize: 500,\n maxReportedErrors: 200,\n sampleRows: 5,\n\n idleTimeoutMs: 30_000,\n totalTimeoutMs: 10 * 60_000,\n};\n\n/**\n * Fill in what a caller left out.\n *\n * A non-finite or non-positive override falls back to the default rather than being accepted:\n * `maxBytes: 0` from a mis-parsed environment variable would otherwise disable every upload,\n * and `maxBytes: Infinity` would disable the guard — the two ways a configuration mistake\n * turns a limit off, both of which look deliberate in a diff.\n */\nexport function resolveLimits(overrides?: Partial<ImportLimits>): ImportLimits {\n const out = { ...DEFAULT_LIMITS };\n if (!overrides) return out;\n\n for (const key of Object.keys(DEFAULT_LIMITS) as (keyof ImportLimits)[]) {\n const value = overrides[key];\n if (typeof value === 'number' && Number.isFinite(value) && value > 0) {\n out[key] = Math.floor(value);\n }\n }\n return out;\n}\n","/**\n * optional.ts — loading a package this one only needs for some of what an entry does.\n *\n * `exceljs` reads and writes workbooks and `busboy` reads a multipart upload. Neither is\n * needed to read a CSV, so both are optional peer dependencies and are loaded when first used,\n * by `/node` and, for `exceljs`, by `/browser`. When one is missing, the failure has to say so:\n * Node's own \"Cannot find module\" names a file deep inside this package and not what to do.\n */\n\n/** Node's two spellings of \"that package is not installed\", for `require` and for `import`. */\nconst MISSING = new Set(['MODULE_NOT_FOUND', 'ERR_MODULE_NOT_FOUND']);\n\n/** Whether `error` says the package `name` itself is absent, not something inside it. */\nfunction isMissing(error: unknown, name: string): boolean {\n if (typeof error !== 'object' || error === null) return false;\n const { code, message } = error as { code?: unknown; message?: unknown };\n return MISSING.has(String(code)) && String(message).includes(`'${name}'`);\n}\n\n/**\n * Load an optional peer dependency for `entry`, or fail with what to install and why.\n *\n * `load` is the import itself, written at the call site so a bundler can see it. Any other\n * failure, such as a broken install of the package, is rethrown as it is.\n */\nexport async function loadPeer<T>(entry: string, name: string, neededTo: string, load: () => Promise<T>): Promise<T> {\n try {\n return await load();\n } catch (error) {\n if (!isMissing(error, name)) throw error;\n throw new Error(\n `${entry} needs the \"${name}\" package to ${neededTo}, and it is not installed. ` +\n `It is an optional peer dependency: run \\`npm install ${name}\\`.`,\n { cause: error },\n );\n }\n}\n","/**\n * sheet-guards.ts — the row, column and cell bounds, checked one row at a time.\n *\n * Browser-safe and shared: the server's `guardRows` checks each streamed row with these, and\n * the browser reader checks each row of the file it holds, so both refuse the same sheet at the\n * same row with the same message.\n */\nimport { padRow } from 'sheet-to-schema';\nimport { ImportError } from './import-error';\nimport type { ImportLimits } from './limits';\n\ntype SheetLimits = Pick<ImportLimits, 'maxRows' | 'maxColumns' | 'maxCellLength'>;\n\n/** The header row, checked once it is read: its width is every later row's. */\nexport function guardHeaders(headers: readonly string[], limits: SheetLimits): void {\n if (headers.length > limits.maxColumns) {\n throw new ImportError('SHEET_TOO_LARGE', `The sheet has more than ${limits.maxColumns} columns.`);\n }\n}\n\n/**\n * Check data row number `seen` (1 for the row after the header) and pad it to `width`.\n *\n * Padding matches `parseCsv`, through the same `padRow` the main entry exports, so a file\n * imported on a server and the same file imported in a browser produce the same rows.\n */\nexport function guardRow(row: unknown[], seen: number, width: number, limits: SheetLimits): unknown[] {\n if (seen > limits.maxRows) {\n throw new ImportError('SHEET_TOO_LARGE', `The sheet has more than ${limits.maxRows} rows.`);\n }\n if (row.length > limits.maxColumns) {\n throw new ImportError('SHEET_TOO_LARGE', `Row ${seen + 1} has more than ${limits.maxColumns} columns.`);\n }\n for (const cell of row) {\n if (typeof cell === 'string' && cell.length > limits.maxCellLength) {\n throw new ImportError('SHEET_TOO_LARGE', `A cell in row ${seen + 1} is longer than ${limits.maxCellLength} characters.`);\n }\n }\n return padRow(row, width);\n}\n","/**\n * sheet-format.ts — what a file is, decided by its first bytes.\n *\n * Browser-safe and shared, so the server's `readSheet` and `sheet-to-schema/browser`'s\n * `readSheetFile` refuse the same files with the same messages and read the rest the same way.\n */\nimport { ImportError } from './import-error';\n\n/** The formats this package reads. Both are decided by magic bytes. */\nexport type SheetFormat = 'csv' | 'xlsx';\n\n/** Enough bytes to tell a zip from text, with room for a UTF-16 BOM. */\nexport const MAGIC_BYTES = 8;\n\nconst ZIP = [0x50, 0x4b]; // \"PK\"\nconst OLE2 = [0xd0, 0xcf, 0x11, 0xe0]; // a pre-2007 .xls, which is a compound file, not a zip\nconst UTF16_LE = [0xff, 0xfe];\nconst UTF16_BE = [0xfe, 0xff];\n\nconst startsWith = (bytes: Uint8Array, signature: number[]): boolean =>\n signature.every((byte, i) => bytes[i] === byte);\n\n/**\n * What these bytes actually are.\n *\n * Exported because the multipart layer wants the same answer for the same reason, and because\n * a second copy of a magic-byte table is a second table to be wrong.\n */\nexport function detectFormat(head: Uint8Array): SheetFormat {\n if (startsWith(head, ZIP)) return 'xlsx';\n\n if (startsWith(head, OLE2)) {\n throw new ImportError(\n 'UNSUPPORTED_FORMAT',\n 'This is a pre-2007 Excel file (.xls). Save it as .xlsx or .csv and try again.',\n );\n }\n if (startsWith(head, UTF16_LE) || startsWith(head, UTF16_BE)) {\n throw new ImportError(\n 'UNSUPPORTED_FORMAT',\n 'This file is UTF-16 encoded. Save it as UTF-8 CSV and try again.',\n );\n }\n\n // Anything else is read as delimited text. A binary file that reaches here produces a sheet\n // of nonsense rather than an error, which is the right trade: refusing everything that does\n // not look like text would refuse legitimately unusual encodings too, and nonsense rows are\n // reported per row by the mapping rather than as a mysterious rejection.\n return 'csv';\n}\n","/**\n * xlsx-cells.ts — what one ExcelJS cell value becomes in the grid.\n *\n * Browser-safe and shared, so the server's streaming reader and `sheet-to-schema/browser`'s\n * reader turn the same cell into the same value by construction, not by agreement.\n */\n\n/**\n * Turn one exceljs cell value into the value a record should be built from.\n *\n * A formula cell yields its **result**, never its formula: a sheet someone uploaded may\n * contain anything, and importing the text `=SUM(A1:A9)` into a field is importing a string\n * the user never saw. An error cell yields its error text, which fails coercion loudly rather\n * than importing as blank.\n */\nexport function cellValue(raw: unknown): unknown {\n if (raw === null || raw === undefined) return '';\n if (raw instanceof Date) return raw;\n if (typeof raw !== 'object') return raw;\n\n const cell = raw as Record<string, unknown>;\n if (Array.isArray(cell['richText'])) {\n return (cell['richText'] as { text?: unknown }[]).map(part => String(part?.text ?? '')).join('');\n }\n if ('formula' in cell || 'sharedFormula' in cell) return cellValue(cell['result']);\n if ('error' in cell) return String(cell['error']);\n if ('text' in cell) return String(cell['text']);\n return '';\n}\n\n/** `row.values` is one-based with a hole at index 0, and sparse wherever a cell was empty. */\nexport function rowCells(values: unknown): unknown[] {\n if (!Array.isArray(values)) return [];\n const out: unknown[] = [];\n for (let i = 1; i < values.length; i++) out.push(cellValue(values[i]));\n return out;\n}\n","/**\n * formula-filter.ts — worksheet XML with its formulas taken out, so each formula cell is read as\n * the typed cell its cached result is.\n *\n * Browser-safe and shared: the server filters the worksheet as it rebuilds the archive\n * (`node/strip-formulas.ts`), the browser before ExcelJS loads it, so both parse the same XML.\n *\n * Why: with its `<f>` in place, ExcelJS's streaming reader keeps a cached result as text when\n * Excel stored it as text and otherwise as `parseFloat` of it, unstyled (a `TRUE` read as `1`,\n * an error as nothing, a date as its serial), and `Workbook.xlsx.load` drops a cached `0` or\n * `false`. Without it, both read the cell by their ordinary path: `t=\"b\"` a boolean, `t=\"e\"` its\n * error text, a number in a date format a `Date`, `0` as `0`. An import reads a formula's result\n * and never its formula, so taking the formula out loses nothing it uses.\n *\n * Works on bytes: the markup is ASCII, and no byte of a multi-byte UTF-8 character is below\n * 0x80. Formula text cannot hold a `<` (XML escapes it), so `</f>` ends the body.\n */\n\nconst LT = 0x3c;\nconst GT = 0x3e;\nconst SLASH = 0x2f;\nconst F = 0x66;\nconst END_TAG = [LT, SLASH, F, GT];\n/** What may follow `<f` in an `f` element's tag: `<f>`, `<f/>`, `<f t=\"shared\" …>`. */\nconst AFTER_NAME = new Set([GT, SLASH, 0x20, 0x09, 0x0d, 0x0a]);\n\nconst enum State {\n Outside,\n StartTag,\n Body,\n}\n\n/** Removes every `<f …>…</f>` and `<f …/>` from a stream of XML bytes, across any chunking. */\nexport class FormulaFilter {\n private state = State.Outside;\n private carry: Uint8Array = new Uint8Array(0);\n /** The last byte of the start tag seen so far, to tell `<f …/>` from `<f …>`. */\n private previous = 0;\n\n /** The next chunk in; what of it is certain to be kept out. */\n push(chunk: Uint8Array): Uint8Array {\n const bytes = this.carry.length ? concat([this.carry, chunk]) : chunk;\n this.carry = new Uint8Array(0);\n const out: Uint8Array[] = [];\n let i = 0;\n\n while (i < bytes.length) {\n if (this.state === State.Outside) {\n const start = i;\n let j = i;\n let entered = false;\n for (; j < bytes.length; j++) {\n if (bytes[j] !== LT) continue;\n if (j + 2 >= bytes.length) break; // `<` or `<f` at the very end: the next chunk decides.\n if (bytes[j + 1] === F && AFTER_NAME.has(bytes[j + 2])) {\n out.push(bytes.subarray(start, j));\n this.state = State.StartTag;\n this.previous = 0;\n i = j + 2;\n entered = true;\n break;\n }\n }\n if (entered) continue;\n out.push(bytes.subarray(start, j));\n this.carry = bytes.slice(j);\n i = bytes.length;\n } else if (this.state === State.StartTag) {\n const end = bytes.indexOf(GT, i);\n if (end < 0) {\n this.previous = bytes[bytes.length - 1];\n break;\n }\n const before = end > i ? bytes[end - 1] : this.previous;\n this.state = before === SLASH ? State.Outside : State.Body;\n i = end + 1;\n } else {\n const end = indexOfTag(bytes, i);\n if (end < 0) {\n // The formula text is dropped; keep only a tail that could be the start of `</f>`.\n let keep = 0;\n for (let k = Math.min(3, bytes.length - i); k > 0 && !keep; k--) {\n if (END_TAG.slice(0, k).every((byte, n) => bytes[bytes.length - k + n] === byte)) keep = k;\n }\n this.carry = bytes.slice(bytes.length - keep);\n break;\n }\n this.state = State.Outside;\n i = end + END_TAG.length;\n }\n }\n return concat(out);\n }\n\n /** No more input: whatever was held back to see what came next. */\n end(): Uint8Array {\n const rest = this.state === State.Outside ? this.carry : new Uint8Array(0);\n this.carry = new Uint8Array(0);\n return rest;\n }\n}\n\nfunction indexOfTag(bytes: Uint8Array, from: number): number {\n for (let at = bytes.indexOf(LT, from); at >= 0 && at + 3 < bytes.length; at = bytes.indexOf(LT, at + 1)) {\n if (bytes[at + 1] === SLASH && bytes[at + 2] === F && bytes[at + 3] === GT) return at;\n }\n return -1;\n}\n\nexport function concat(parts: readonly Uint8Array[]): Uint8Array {\n if (parts.length === 1) return parts[0];\n const out = new Uint8Array(parts.reduce((sum, part) => sum + part.length, 0));\n let at = 0;\n for (const part of parts) {\n out.set(part, at);\n at += part.length;\n }\n return out;\n}\n\n/** Filter a whole worksheet held in memory. */\nexport function filterFormulas(xml: Uint8Array): Uint8Array {\n const filter = new FormulaFilter();\n return concat([filter.push(xml), filter.end()]);\n}\n","/**\n * inflate.ts — a raw-deflate entry inflated in a browser, bounded as it goes.\n *\n * `DecompressionStream` is the runtime's own, and its `deflate-raw` format is what a zip entry\n * is: Node 20.12+, Chrome and Edge 103+, Firefox 113+, Safari 16.4+. Inflating is where an\n * archive's real size shows, so the output is counted chunk by chunk and refused past `max` with\n * the server's message.\n */\nimport { concat } from '../shared/formula-filter';\nimport { ImportError } from '../shared/import-error';\n\n/** What a runtime without `deflate-raw` is told, with the versions that have it. */\nexport const UNSUPPORTED_RUNTIME_MESSAGE =\n 'Reading .xlsx files needs DecompressionStream with the \"deflate-raw\" format, which this runtime does not have. ' +\n 'Use Node 20.12+, Chrome or Edge 103+, Firefox 113+ or Safari 16.4+, or save the sheet as CSV.';\n\n/**\n * Refuse, before reading a workbook, a runtime that cannot inflate one.\n *\n * The format is what is checked, not only the class: Node 20.0 to 20.11 and Chrome 80 to 102 have\n * `DecompressionStream` and reject `\"deflate-raw\"` from its constructor, which escaped as a raw\n * `TypeError` in 1.1.0-rc.2 (R1-1 in docs/1.1-rc-feedback.md).\n */\nexport function assertDeflateRaw(): void {\n try {\n new DecompressionStream('deflate-raw');\n } catch (cause) {\n throw new ImportError('UNSUPPORTED_RUNTIME', UNSUPPORTED_RUNTIME_MESSAGE, { cause });\n }\n}\n\n/** A failure to write is the same failure the reader reports; it is not reported twice. */\nconst ignore = (): void => undefined;\n\nexport async function inflate(data: Uint8Array, max: number): Promise<Uint8Array> {\n assertDeflateRaw();\n const stream = new DecompressionStream('deflate-raw');\n const writer = stream.writable.getWriter();\n // Not awaited: the reader below drains what the writer pushes, and awaiting first would wait\n // on a stream nothing is reading. A failure to inflate surfaces through the reader.\n void writer\n .write(data as Uint8Array<ArrayBuffer>)\n .then(() => writer.close())\n .catch(ignore);\n const reader = stream.readable.getReader();\n const chunks: Uint8Array[] = [];\n let size = 0;\n try {\n for (let next = await reader.read(); !next.done; next = await reader.read()) {\n size += next.value.length;\n if (size > max) {\n await reader.cancel();\n throw new ImportError('ARCHIVE_REFUSED', `The workbook inflates to more than the ${max} byte limit.`);\n }\n chunks.push(next.value);\n }\n } catch (error) {\n if (error instanceof ImportError) throw error;\n throw new ImportError('MALFORMED_FILE', 'This is not a readable workbook.', { cause: error });\n }\n return concat(chunks);\n}\n","/**\n * zip.ts — which parts of an `.xlsx` an import reads, and the archive that holds only those.\n *\n * Browser-safe and shared. The server's `guardZip` walks the upload entry by entry, inflating to\n * measure; the browser's reader reads the central directory instead (it holds the whole file,\n * and inflating is ExcelJS's job there). Both keep the **same parts** by the same rules and\n * repack them the same way, so ExcelJS on either side parses the same archive.\n *\n * What the browser checks here is what the directory *declares*: entry count, sizes, ratio. A\n * file can lie about those, and only inflating would catch it; that check stays on the server,\n * where the file is someone else's. In a browser the file is the user's own, and only their tab\n * is at risk.\n */\nimport { ImportError } from './import-error';\n\n/** `PK\\x03\\x04` — the start of a local file header. */\nexport const LOCAL_HEADER = 0x04034b50;\n/** `PK\\x01\\x02` — a central directory record. */\nexport const CENTRAL_HEADER = 0x02014b50;\n/** `PK\\x05\\x06` — the end-of-central-directory record. */\nexport const END_OF_CENTRAL = 0x06054b50;\n/** Bytes in a local file header before the name. */\nexport const HEADER_SIZE = 30;\n/** Bytes in a central directory record before the name. */\nconst CENTRAL_SIZE = 46;\n/** Bytes in the end-of-central-directory record, without its comment. */\nconst END_SIZE = 22;\n/** Bit 3 of the general-purpose flags: sizes follow the data rather than preceding it. */\nexport const HAS_DATA_DESCRIPTOR = 0x08;\n/** A size of exactly this means \"see the zip64 extra field\", which is a size we do not trust. */\nexport const ZIP64_SENTINEL = 0xffffffff;\n\nexport const STORED = 0;\nexport const DEFLATED = 8;\n\n/** An entry under this many inflated bytes is never refused for its ratio: its header dominates. */\nexport const RATIO_FLOOR = 64 * 1024;\n\n/**\n * The parts an import reads, in the order exceljs must meet them.\n *\n * Relationships, the workbook and the shared strings before any worksheet — that ordering is\n * what keeps exceljs's streaming reader off its deferral path. Everything not named here, and\n * every worksheet but the lowest-numbered, is dropped.\n */\nexport const WANTED = [\n '[Content_Types].xml',\n '_rels/.rels',\n 'xl/_rels/workbook.xml.rels',\n 'xl/workbook.xml',\n 'xl/styles.xml',\n 'xl/sharedStrings.xml',\n];\n\nconst WORKSHEET = /^xl\\/worksheets\\/sheet(\\d+)\\.xml$/;\n\n/**\n * The number in `sheetN.xml`, or `null` for anything that is not a worksheet.\n *\n * **Which worksheet an import reads is never decided by byte order.** A user means the first\n * *tab*, and an archive may list `sheet2.xml` before `sheet1.xml`.\n *\n * The lowest number is not *quite* tab order either: the real order lives in `workbook.xml`'s\n * `<sheets>` and the relationship map, which this does not parse. It is a great deal closer\n * than byte order, and it is the same number exceljs matches a worksheet by.\n */\nexport function sheetNumber(name: string): number | null {\n const match = WORKSHEET.exec(name);\n return match ? Number(match[1]) : null;\n}\n\nexport const refuse = (message: string): never => {\n throw new ImportError('ARCHIVE_REFUSED', message);\n};\n\n// ─── CRC-32, because a rebuilt entry needs a checksum the reader will accept ──────────────\n\nconst CRC_TABLE = (() => {\n const table = new Int32Array(256);\n for (let i = 0; i < 256; i++) {\n let c = i;\n for (let k = 0; k < 8; k++) c = c & 1 ? 0xedb88320 ^ (c >>> 1) : c >>> 1;\n table[i] = c;\n }\n return table;\n})();\n\nexport function crc32(current: number, bytes: Uint8Array): number {\n let c = ~current;\n for (let i = 0; i < bytes.length; i++) c = CRC_TABLE[(c ^ bytes[i]) & 0xff] ^ (c >>> 8);\n return ~c >>> 0;\n}\n\n/** One part of the rebuilt archive: its data exactly as it was, still compressed. */\nexport interface PackedEntry {\n name: string;\n method: number;\n data: Uint8Array;\n inflated: number;\n crc: number;\n}\n\n/**\n * The tracker both sides use to decide what to keep, entry by entry in archive order: the\n * wanted parts (first copy of each) and the lowest-numbered worksheet.\n */\nexport class PartPicker<E> {\n readonly kept = new Map<string, E>();\n /** The lowest-numbered worksheet kept so far, which is the one an import reads. */\n sheet: { name: string; number: number } | null = null;\n\n /** Whether to keep `name`. A worksheet that supersedes the kept one releases it first. */\n wants(name: string): { keep: boolean; released?: E } {\n const number = sheetNumber(name);\n if (number === null) return { keep: WANTED.includes(name) && !this.kept.has(name) };\n if (this.sheet !== null && number >= this.sheet.number) return { keep: false };\n const released = this.sheet ? this.kept.get(this.sheet.name) : undefined;\n if (this.sheet) this.kept.delete(this.sheet.name);\n this.sheet = null;\n return { keep: true, released };\n }\n\n keep(name: string, entry: E): void {\n this.kept.set(name, entry);\n const number = sheetNumber(name);\n if (number !== null) this.sheet = { name, number };\n }\n\n /**\n * The kept parts in the order the parser must meet them, or the refusal for an archive with\n * no worksheet.\n */\n ordered(): E[] {\n if (!this.sheet) {\n // Any zip starts `PK`, so a `.docx` or a plain archive reaches here too. Without a workbook\n // part it is not a broken workbook but a different kind of file, and saying so is what\n // tells the user to export the right thing rather than to repair this one.\n if (!this.kept.has('xl/workbook.xml')) {\n throw new ImportError(\n 'UNSUPPORTED_FORMAT',\n 'This is a zip archive that is not an Excel workbook. Upload an .xlsx or .csv file.',\n );\n }\n throw new ImportError('MALFORMED_FILE', 'This workbook has no worksheet.');\n }\n return [...WANTED, this.sheet.name].map(name => this.kept.get(name)).filter((entry): entry is E => entry !== undefined);\n }\n}\n\n/** Repack the kept entries as a zip, in the order given. Entry data is copied, never inflated. */\nexport function repack(entries: readonly PackedEntry[]): Uint8Array {\n const encoder = new TextEncoder();\n const names = entries.map(entry => encoder.encode(entry.name));\n const localSize = entries.reduce((sum, entry, i) => sum + HEADER_SIZE + names[i].length + entry.data.length, 0);\n const directorySize = entries.reduce((sum, _entry, i) => sum + CENTRAL_SIZE + names[i].length, 0);\n const out = new Uint8Array(localSize + directorySize + END_SIZE);\n const view = new DataView(out.buffer);\n\n let at = 0;\n const offsets: number[] = [];\n entries.forEach((entry, i) => {\n offsets.push(at);\n view.setUint32(at, LOCAL_HEADER, true);\n view.setUint16(at + 4, 20, true); // version needed\n view.setUint16(at + 6, 0, true); // flags: sizes are right here, so no data descriptor\n view.setUint16(at + 8, entry.method, true);\n view.setUint32(at + 14, entry.crc, true);\n view.setUint32(at + 18, entry.data.length, true);\n view.setUint32(at + 22, entry.inflated, true);\n view.setUint16(at + 26, names[i].length, true);\n out.set(names[i], at + HEADER_SIZE);\n out.set(entry.data, at + HEADER_SIZE + names[i].length);\n at += HEADER_SIZE + names[i].length + entry.data.length;\n });\n\n const directoryStart = at;\n entries.forEach((entry, i) => {\n view.setUint32(at, CENTRAL_HEADER, true);\n view.setUint16(at + 4, 20, true); // version made by\n view.setUint16(at + 6, 20, true); // version needed\n view.setUint16(at + 10, entry.method, true);\n view.setUint32(at + 16, entry.crc, true);\n view.setUint32(at + 20, entry.data.length, true);\n view.setUint32(at + 24, entry.inflated, true);\n view.setUint16(at + 28, names[i].length, true);\n view.setUint32(at + 42, offsets[i], true);\n out.set(names[i], at + CENTRAL_SIZE);\n at += CENTRAL_SIZE + names[i].length;\n });\n\n view.setUint32(at, END_OF_CENTRAL, true);\n view.setUint16(at + 8, entries.length, true);\n view.setUint16(at + 10, entries.length, true);\n view.setUint32(at + 12, at - directoryStart, true);\n view.setUint32(at + 16, directoryStart, true);\n return out;\n}\n\n/** The bounds the directory is checked against. */\nexport interface DirectoryLimits {\n maxZipEntries: number;\n maxUncompressedBytes: number;\n maxCompressionRatio: number;\n}\n\nconst malformed = (): never => {\n throw new ImportError('MALFORMED_FILE', 'The workbook could not be read.');\n};\n\n/**\n * Check a whole archive by its central directory, and return the parts an import reads,\n * repacked exactly as the server repacks them. Nothing is inflated.\n *\n * Refused with the server's codes and messages: too many entries, a declared total past\n * `maxUncompressedBytes`, a declared ratio past `maxCompressionRatio`, an unsupported method, a\n * stored entry without a declared size. A directory that cannot be read is `MALFORMED_FILE`.\n */\nexport function pickFromDirectory(bytes: Uint8Array, limits: DirectoryLimits): Uint8Array {\n return repack(pickParts(bytes, limits));\n}\n\n/**\n * The parts `pickFromDirectory` keeps, in the parser's order, before they are repacked: the\n * worksheet is the last. For a caller that rebuilds the worksheet first.\n */\nexport function pickParts(bytes: Uint8Array, limits: DirectoryLimits): PackedEntry[] {\n const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);\n const u16 = (at: number): number => view.getUint16(at, true);\n const u32 = (at: number): number => view.getUint32(at, true);\n\n // The end record is the last thing in the file, followed by a comment of up to 64 KB.\n let end = -1;\n for (let at = bytes.length - END_SIZE; at >= Math.max(0, bytes.length - END_SIZE - 0xffff); at--) {\n if (u32(at) === END_OF_CENTRAL) {\n end = at;\n break;\n }\n }\n if (end < 0) return malformed();\n\n const count = u16(end + 10);\n const directoryLength = u32(end + 12);\n const directoryStart = u32(end + 16);\n if (count === 0xffff || directoryStart === ZIP64_SENTINEL) return refuse('The workbook uses zip64, which is not supported.');\n if (count > limits.maxZipEntries) return refuse(`The workbook has more than ${limits.maxZipEntries} entries.`);\n if (directoryStart + directoryLength > end) return malformed();\n\n const decoder = new TextDecoder('utf-8');\n const picker = new PartPicker<PackedEntry>();\n let total = 0;\n let at = directoryStart;\n\n for (let i = 0; i < count; i++) {\n if (at + CENTRAL_SIZE > end || u32(at) !== CENTRAL_HEADER) return malformed();\n const flags = u16(at + 8);\n const method = u16(at + 10);\n const crc = u32(at + 16);\n const compressed = u32(at + 20);\n const inflated = u32(at + 24);\n const nameLength = u16(at + 28);\n const extraLength = u16(at + 30);\n const commentLength = u16(at + 32);\n const localOffset = u32(at + 42);\n if (at + CENTRAL_SIZE + nameLength > end) return malformed();\n const name = decoder.decode(bytes.subarray(at + CENTRAL_SIZE, at + CENTRAL_SIZE + nameLength));\n at += CENTRAL_SIZE + nameLength + extraLength + commentLength;\n\n if (compressed === ZIP64_SENTINEL || inflated === ZIP64_SENTINEL) {\n return refuse('The workbook uses zip64, which is not supported.');\n }\n if (method === STORED && (flags & HAS_DATA_DESCRIPTOR) !== 0) {\n return refuse(`Entry \"${name}\" is stored without a declared size.`);\n }\n if (method !== STORED && method !== DEFLATED) {\n return refuse(`Entry \"${name}\" uses compression method ${method}, which is not supported.`);\n }\n total += inflated;\n if (total > limits.maxUncompressedBytes) {\n return refuse(`The workbook inflates to more than the ${limits.maxUncompressedBytes} byte limit.`);\n }\n if (inflated > RATIO_FLOOR && inflated / Math.max(compressed, 1) > limits.maxCompressionRatio) {\n return refuse(`An entry in the workbook inflates more than ${limits.maxCompressionRatio} times — refusing it.`);\n }\n\n if (!picker.wants(name).keep) continue;\n if (localOffset + HEADER_SIZE > bytes.length || u32(localOffset) !== LOCAL_HEADER) return malformed();\n const start = localOffset + HEADER_SIZE + u16(localOffset + 26) + u16(localOffset + 28);\n if (start + compressed > bytes.length) return malformed();\n picker.keep(name, { name, method, data: bytes.subarray(start, start + compressed), inflated, crc });\n }\n\n return picker.ordered();\n}\n","/**\n * read-sheet-file.ts — a whole CSV, TSV or `.xlsx` file in, the server's grid out (1.1 spec, A).\n *\n * The server's `readSheet` streams; a browser already holds the file, so this reads it in one\n * pass. Everything that decides *what* the grid is comes from the same code the server runs:\n *\n * - the format, from the first bytes (`shared/sheet-format.ts`), never from the name;\n * - CSV: the main entry's reader and delimiter detection, fed the whole text at once, decoded\n * the server's way (UTF-8, not fatal, byte-order mark dropped);\n * - `.xlsx`: the same parts picked from the archive and repacked the same way\n * (`shared/zip.ts`), then each cell through the same rules (`shared/xlsx-cells.ts`);\n * - the limits, their defaults, the row checks and the error codes and messages\n * (`shared/limits.ts`, `shared/sheet-guards.ts`, `shared/import-error.ts`).\n *\n * The one difference is the parser's entry point. The server uses ExcelJS's streaming\n * `WorkbookReader`, which only its Node build has; a browser build has `Workbook.xlsx.load`.\n * Two things make `load` read what the streaming reader reads:\n *\n * - the worksheet's formulas are taken out first, as the server takes them out\n * (`shared/formula-filter.ts`), so a formula cell is its cached result, typed, on both sides;\n * - it is told to ignore every worksheet node but `sheetData`, as the streaming reader does.\n * Merges, hyperlinks and the rest are never applied (a merged range's covered cells stay\n * empty, a hyperlink cell stays its text), and a part never parsed cannot be hostile;\n */\nimport type ExcelJSModule from 'exceljs';\nimport { cellText, completeFirstLine, createCsvReader, detectDelimiter, type CsvDelimiter, type SheetGrid } from 'sheet-to-schema';\nimport { ImportError } from '../shared/import-error';\nimport { resolveLimits, type ImportLimits } from '../shared/limits';\nimport { loadPeer } from '../shared/optional';\nimport { guardHeaders, guardRow } from '../shared/sheet-guards';\nimport { detectFormat, MAGIC_BYTES } from '../shared/sheet-format';\nimport { rowCells } from '../shared/xlsx-cells';\nimport { filterFormulas } from '../shared/formula-filter';\nimport { assertDeflateRaw, inflate } from './inflate';\nimport { crc32, pickParts, repack, STORED, type PackedEntry } from '../shared/zip';\n\n/** The limits a browser read enforces. Each one left out is the server's default. */\nexport type BrowserLimits = Pick<ImportLimits, 'maxBytes' | 'maxRows' | 'maxColumns' | 'maxCellLength' | 'maxZipEntries'>;\n\nexport interface ReadSheetFileOptions {\n /**\n * The file's name. A delimiter hint only, as on the server: once the bytes have said the file\n * is text, a name ending in `.tsv` means tab. It never decides the format.\n */\n filename?: string;\n limits?: Partial<BrowserLimits>;\n}\n\n/** What `readSheetFile` reads: the grid, what the bytes turned out to be, and a CSV's separator. */\nexport interface SheetFile extends SheetGrid {\n format: 'csv' | 'xlsx';\n /** The separator a CSV turned out to use; pass it to `decimalMarkFor`. Absent for a workbook. */\n delimiter?: CsvDelimiter;\n}\n\n/** What a file can arrive as: a `File` or other `Blob`, or its bytes. */\nexport type SheetFileInput = Blob | ArrayBuffer | Uint8Array;\n\ntype ExcelJS = typeof ExcelJSModule;\n\n/** Loaded on the first workbook, never for a CSV: a CSV-only app never downloads it. */\nconst loadExcel = (): Promise<ExcelJS> =>\n loadPeer('sheet-to-schema/browser', 'exceljs', 'read .xlsx files', async () => (await import('exceljs')).default);\n\n/**\n * Read a CSV, TSV or `.xlsx` file into exactly the grid the server's `readSheet` reads from the\n * same bytes: the same headers, the same rows with the same typed cells, the same separator,\n * and the same refusals with the same codes and messages.\n *\n * @throws ImportError `TOO_LARGE`, `SHEET_TOO_LARGE`, `UNSUPPORTED_FORMAT`, `MALFORMED_FILE`,\n * `ARCHIVE_REFUSED`, as the server would; and for a workbook, `UNSUPPORTED_RUNTIME` where the\n * runtime has no `DecompressionStream` with `deflate-raw` (before Node 20.12, Chrome or Edge\n * 103, Firefox 113, Safari 16.4). A workbook read without `exceljs` installed throws a\n * plain `Error` saying what to install.\n */\nexport async function readSheetFile(input: SheetFileInput, options: ReadSheetFileOptions = {}): Promise<SheetFile> {\n const limits = resolveLimits(options.limits);\n const bytes = await bytesOf(input, limits.maxBytes);\n const format = detectFormat(bytes.subarray(0, MAGIC_BYTES));\n if (format === 'xlsx') return { format, ...(await xlsxGrid(bytes, limits)) };\n return csvGrid(bytes, limits, /\\.tsv$/i.test(options.filename ?? '') ? '\\t' : undefined);\n}\n\n/**\n * Read an `.xlsx` file by the server's rules, for a caller that already knows the format.\n *\n * @throws ImportError `UNSUPPORTED_FORMAT` for anything that is not a zip, and whatever\n * `readSheetFile` throws for a workbook.\n */\nexport async function readXlsx(input: SheetFileInput, options: Omit<ReadSheetFileOptions, 'filename'> = {}): Promise<SheetGrid> {\n const limits = resolveLimits(options.limits);\n const bytes = await bytesOf(input, limits.maxBytes);\n if (detectFormat(bytes.subarray(0, MAGIC_BYTES)) !== 'xlsx') {\n throw new ImportError('UNSUPPORTED_FORMAT', 'This is not an .xlsx workbook. Read a CSV with readSheetFile.');\n }\n return xlsxGrid(bytes, limits);\n}\n\n// ─── Bytes ────────────────────────────────────────────────────────────────────────────────\n\nconst tooLarge = (maxBytes: number): never => {\n throw new ImportError('TOO_LARGE', `The file is larger than the ${maxBytes} byte limit.`);\n};\n\n/**\n * The input's bytes, refusing past `maxBytes` before reading a `Blob` at all.\n *\n * Told apart by shape, not `instanceof`: a typed array or a `Blob` from another realm (an\n * iframe, a test environment) fails `instanceof` and is still one.\n */\nasync function bytesOf(input: SheetFileInput, maxBytes: number): Promise<Uint8Array> {\n if (ArrayBuffer.isView(input)) {\n if (input.byteLength > maxBytes) tooLarge(maxBytes);\n return new Uint8Array(input.buffer, input.byteOffset, input.byteLength);\n }\n if (Object.prototype.toString.call(input) === '[object ArrayBuffer]') {\n const buffer = input as ArrayBuffer;\n if (buffer.byteLength > maxBytes) tooLarge(maxBytes);\n return new Uint8Array(buffer);\n }\n const blob = input as Blob;\n if (blob !== null && typeof blob === 'object' && typeof blob.size === 'number' && typeof blob.slice === 'function') {\n if (blob.size > maxBytes) tooLarge(maxBytes);\n return new Uint8Array(await blobBuffer(blob));\n }\n throw new TypeError('readSheetFile reads a Blob (such as a File), an ArrayBuffer or a Uint8Array.');\n}\n\n/** `Blob.arrayBuffer`, or `FileReader` where a browser or test environment lacks it. */\nfunction blobBuffer(blob: Blob): Promise<ArrayBuffer> {\n if (typeof blob.arrayBuffer === 'function') return blob.arrayBuffer();\n return new Promise((resolve, reject) => {\n const reader = new FileReader();\n reader.onload = () => resolve(reader.result as ArrayBuffer);\n reader.onerror = () => reject(reader.error);\n reader.readAsArrayBuffer(blob);\n });\n}\n\n// ─── CSV ──────────────────────────────────────────────────────────────────────────────────\n\n/** The server's `csvRows`, given the whole file as its one chunk. */\nfunction csvGrid(bytes: Uint8Array, limits: ImportLimits, forced: CsvDelimiter | undefined): SheetFile {\n // Not `fatal`, as on the server: one bad byte is one U+FFFD in one cell the importer can\n // report on, not a reason to refuse the file.\n const text = new TextDecoder('utf-8').decode(bytes);\n const line = completeFirstLine(text);\n if (line === null && text.length > limits.maxCellLength * limits.maxColumns + limits.maxColumns) {\n throw new ImportError('SHEET_TOO_LARGE', 'The header row is longer than any row may be.');\n }\n const delimiter = forced ?? detectDelimiter(line ?? text);\n const reader = createCsvReader({ delimiter });\n const { headers, rows } = guardGrid([...reader.push(text), ...reader.end()], limits);\n return { format: 'csv', headers, rows, delimiter };\n}\n\n/** Header row through `cellText`, then every data row through the server's checks, in order. */\nfunction guardGrid(raw: Iterable<unknown[]>, limits: ImportLimits): SheetGrid {\n const iterator = raw[Symbol.iterator]();\n const first = iterator.next();\n const headers = first.done ? [] : first.value.map(cellText);\n guardHeaders(headers, limits);\n const rows: unknown[][] = [];\n for (let next = iterator.next(); !next.done; next = iterator.next()) rows.push(guardRow(next.value, rows.length + 1, headers.length, limits));\n return { headers, rows };\n}\n\n// ─── xlsx ─────────────────────────────────────────────────────────────────────────────────\n\n/**\n * The archive is checked by its directory and cut down to the server's parts before ExcelJS is\n * even loaded, so an over-limit zip never reaches it.\n */\nasync function xlsxGrid(bytes: Uint8Array, limits: ImportLimits): Promise<SheetGrid> {\n // First, so a runtime that cannot read a workbook says so whatever the file is.\n assertDeflateRaw();\n const parts = pickParts(bytes, limits);\n const Excel = await loadExcel();\n const filtered = await withoutFormulas(parts[parts.length - 1], limits);\n const archive = repack([...parts.slice(0, -1), filtered]);\n const workbook = new Excel.Workbook();\n try {\n await workbook.xlsx.load(archive as unknown as Parameters<typeof workbook.xlsx.load>[0], { ignoreNodes: IGNORED_NODES } as object);\n } catch (error) {\n // What the parser says about itself is not for the caller to show, as on the server.\n throw new ImportError('MALFORMED_FILE', 'This is not a readable workbook.', { cause: error });\n }\n const sheet = workbook.worksheets[0];\n if (!sheet || sheet.rowCount === 0) {\n // As on the server: a sheet with not one row is not a file anyone meant to import, and\n // \"imported 0 rows\" is the wrong thing to say about it.\n throw new ImportError('MALFORMED_FILE', 'This workbook has no readable rows.');\n }\n return guardGrid(sheetRows(sheet, limits), limits);\n}\n\n/**\n * The worksheet without its formulas, stored rather than deflated: a browser has a standard\n * decompressor (`DecompressionStream`) and the archive is only read here, so there is nothing to\n * gain by compressing it again. Inflating is where an archive's real size shows, so it is\n * bounded by `maxUncompressedBytes` as it goes, with the server's message.\n */\nasync function withoutFormulas(sheet: PackedEntry, limits: ImportLimits): Promise<PackedEntry> {\n const xml = sheet.method === STORED ? sheet.data : await inflate(sheet.data, limits.maxUncompressedBytes);\n const data = filterFormulas(xml);\n return { name: sheet.name, method: STORED, data, inflated: data.length, crc: crc32(0, data) };\n}\n\n/** Every worksheet node `load` would parse but the server's streaming reader does not. */\nconst IGNORED_NODES = [\n 'sheetPr', 'dimension', 'sheetViews', 'sheetFormatPr', 'cols', 'autoFilter', 'mergeCells', 'rowBreaks',\n 'hyperlinks', 'pageMargins', 'dataValidations', 'pageSetup', 'headerFooter', 'printOptions', 'picture',\n 'drawing', 'sheetProtection', 'tableParts', 'conditionalFormatting', 'extLst',\n];\n\n/**\n * The server's `xlsxRows` over a loaded worksheet: every row from 1, a gap filled with blank\n * rows so later rows keep their numbers, a row past `maxRows` refused before its gap is made.\n */\nfunction* sheetRows(sheet: ExcelJSModule.Worksheet, limits: ImportLimits): Generator<unknown[]> {\n let expected = 1;\n for (let number = 1; number <= sheet.rowCount; number++) {\n const row = sheet.findRow(number);\n if (!row) continue;\n if (number - 1 > limits.maxRows) {\n throw new ImportError('SHEET_TOO_LARGE', `The sheet has more than ${limits.maxRows} rows.`);\n }\n while (expected < number) {\n yield [];\n expected++;\n }\n const values: unknown[] = [];\n row.eachCell((cell, column) => {\n values[column] = cell.value;\n });\n yield rowCells(values);\n expected = number + 1;\n }\n}\n"]}
@@ -15,6 +15,8 @@ var STATUS = {
15
15
  INVALID_PLAN: 400,
16
16
  TIMEOUT: 408,
17
17
  IMPORT_FAILED: 500,
18
+ // Not an HTTP answer: only the browser reader raises it. 501 if one ever has to be chosen.
19
+ UNSUPPORTED_RUNTIME: 501,
18
20
  INTERNAL: 500
19
21
  };
20
22
  var ImportError = class extends Error {
@@ -237,11 +239,17 @@ function filterFormulas(xml) {
237
239
  }
238
240
 
239
241
  // src/browser/inflate.ts
242
+ var UNSUPPORTED_RUNTIME_MESSAGE = 'Reading .xlsx files needs DecompressionStream with the "deflate-raw" format, which this runtime does not have. Use Node 20.12+, Chrome or Edge 103+, Firefox 113+ or Safari 16.4+, or save the sheet as CSV.';
243
+ function assertDeflateRaw() {
244
+ try {
245
+ new DecompressionStream("deflate-raw");
246
+ } catch (cause) {
247
+ throw new ImportError("UNSUPPORTED_RUNTIME", UNSUPPORTED_RUNTIME_MESSAGE, { cause });
248
+ }
249
+ }
240
250
  var ignore = () => void 0;
241
251
  async function inflate(data, max) {
242
- if (typeof DecompressionStream !== "function") {
243
- throw new Error("This browser cannot read .xlsx files: it has no DecompressionStream. Use a current browser, or save the sheet as CSV.");
244
- }
252
+ assertDeflateRaw();
245
253
  const stream = new DecompressionStream("deflate-raw");
246
254
  const writer = stream.writable.getWriter();
247
255
  void writer.write(data).then(() => writer.close()).catch(ignore);
@@ -517,6 +525,7 @@ function guardGrid(raw, limits) {
517
525
  return { headers, rows };
518
526
  }
519
527
  async function xlsxGrid(bytes, limits) {
528
+ assertDeflateRaw();
520
529
  const parts = pickParts(bytes, limits);
521
530
  const Excel = await loadExcel();
522
531
  const filtered = await withoutFormulas(parts[parts.length - 1], limits);