# open - the door Source: https://truecopy.dev/docs/open/ Module: truecopy/open The only way from bytes to rows, with the caps, the deadline and the engine released. And a refusal your application can say in its own language. ```ts import { openDocument, UnreadableDocument, DEFAULT_LIMITS } from 'truecopy/open'; const document = await openDocument(file, options?); ``` What lives here **forces**, and it forces for a reason no interface can match: there is no other route in. `file` is whatever was dropped. A PDF goes through pdf.js; **anything else is read as text**: a CSV, a TSV, a table pasted out of a viewer, an OCR dump. The text path loads no engine at all, and it picks its own ruler: see [documentFromText](https://truecopy.dev/docs/layout/). | | Default | Why it exists | | ---------------------- | ------- | ---------------------------------------------------------------------------- | | `maximumBytes` | 20 MB | past this it is not the document expected: refuse rather than freeze the tab | | `maximumPages` | 40 | a document with thousands is not a statement | | `deadlineMilliseconds` | 30 000 | a read that never returns leaves a screen with no button and no way out | No byte leaves the process. ## The refusal is named, so the sentence is yours ```ts try { await openDocument(file); } catch (error) { if (error instanceof UnreadableDocument) { // 'empty' | 'too-big' | 'no-text' | 'too-slow' | 'not-opened' showInYourOwnWords(error.reason); } } ``` The library used to ship English sentences and nothing else, which broke its own rule: it names the rule that broke, **your application writes the sentence in its own voice**. An application speaking anything but English had to keep a copy of this whole file to say "this file is over 20 MB". `reason` is for the code; `message` is there for whoever has no application to write the sentence for them. ## The pdf.js worker ```ts // Vite import workerSrc from 'pdfjs-dist/legacy/build/pdf.worker.mjs?url'; await openDocument(file, { workerSrc }); ``` **In a browser this is required**: pdf.js refuses to start without it. In Node you can leave it out and pdf.js runs inline: slower on a long document, correct everywhere, and it is what makes the whole chain testable outside a browser. truecopy deliberately does not resolve that URL. Vite wants `?url`, webpack wants `new URL(…, import.meta.url)`, a plain page wants a path it can serve. Picking one would lock every caller into that bundler for the sake of one line. ## Bringing your own engine ```ts import * as pdfjs from 'pdfjs-dist'; // the modern build await openDocument(file, { pdfjs }); ``` The default is the **legacy** build, which runs in Node and keeps the chain from real bytes to a parsed row testable. The modern one is smaller by over a hundred kilobytes brotli, which decides it for anything under a byte budget. Neither is right for both, so neither is chosen for you. `PdfEngine` is structural: both builds satisfy it without knowing the type exists, and so would another engine. ## Also here - **`withDeadline(promise, ms)`**: bound any read in time. The timer is always cleared, even when the read wins the race: a forgotten timer keeps the process awake and fails tests long after they passed. - **`positionedItems(items)`**: the one pure step between a PDF engine and this library's data, exported so a test driving a **real** engine over **real** bytes can prove the chain joins up. Everything that turns those items into rows and columns lives in [layout](https://truecopy.dev/docs/layout/), which needs no engine, no bytes and no clock. Map of this site for a model: https://truecopy.dev/llms.txt Every page in one file: https://truecopy.dev/llms-full.txt