From 2c5e9a80b9ee6ba43dde7cd4166addc573c0d535 Mon Sep 17 00:00:00 2001 From: haritowa <6107211+haritowa@users.noreply.github.com> Date: Fri, 23 Oct 2026 18:35:17 +0000 Subject: [PATCH] refactor(documents): type the text read with pdfjs' own declarations extractPdfText cast the dynamic import to a hand-written shape (`as unknown as`), so the compiler could not check the call against pdfjs. It now uses pdfjs' own types: PDFDocumentLoadingTask, TextItem and TextMarkedContent, all type-only imports that are erased at build. That check found a dead option at once: pdfjs 6 has no isEvalSupported. The option went away with the font-program eval path it used to switch off, so it did nothing here; it is removed. --- src/lib/documents/local-extract.ts | 45 ++++++++---------------------- 1 file changed, 11 insertions(+), 34 deletions(-) diff --git a/src/lib/documents/local-extract.ts b/src/lib/documents/local-extract.ts index 39b33df09..98afd9935 100644 --- a/src/lib/documents/local-extract.ts +++ b/src/lib/documents/local-extract.ts @@ -28,6 +28,12 @@ */ import { Buffer } from "node:buffer"; +import type { PDFDocumentLoadingTask } from "pdfjs-dist"; +import type { + TextItem, + TextMarkedContent, +} from "pdfjs-dist/types/src/display/api"; + import { annotate } from "@/lib/logging/context"; // pdfjs' module top-level references the browser-only `DOMMatrix` global. A @@ -35,42 +41,15 @@ import { annotate } from "@/lib/logging/context"; // instantiation and throw `DOMMatrix is not defined`, taking down every // route/worker that shares the chunk (the document routes among them). The // module is therefore loaded lazily inside `extractPdfText` via a runtime -// `import()`, and only the slice of its API used here is typed, structurally. -interface PdfTextItem { - str: string; - /** [a, b, c, d, x, y]: the run's text matrix; x and y place it on the page. */ - transform: number[]; - width: number; - height: number; - hasEOL: boolean; -} -interface PdfTextPage { - getTextContent(): Promise<{ items: Array }>; - cleanup(): void; -} -interface PdfTextDocument { - numPages: number; - getPage(pageNumber: number): Promise; -} -interface PdfTextLoadingTask { - promise: Promise; - destroy(): Promise; -} -interface PdfTextModule { - getDocument(opts: { - data: Uint8Array; - verbosity?: number; - isEvalSupported?: boolean; - useSystemFonts?: boolean; - }): PdfTextLoadingTask; -} +// `import()`. The type imports above are erased at build, so they load +// nothing; they let the compiler check this file against pdfjs' own API. /** * One page's text runs as text: a new line where a run ends its line or the * next one sits on another, a space where two runs on one line leave a gap, * nothing where they touch (a word split across two fonts stays one word). */ -function pageText(items: Array): string { +function pageText(items: Array): string { let text = ""; let last: { end: number; y: number; size: number } | null = null; for (const item of items) { @@ -128,16 +107,14 @@ export type LocalExtractResult = export async function extractPdfText( buffer: Buffer, ): Promise { - let task: PdfTextLoadingTask | null = null; + let task: PDFDocumentLoadingTask | null = null; try { // Lazy load: keeps pdfjs/DOMMatrix out of the eager server chunk (see the // import-site note). Resolved at first PDF extraction, then module-cached. - const pdfjs = - (await import("pdfjs-dist/legacy/build/pdf.mjs")) as unknown as PdfTextModule; + const pdfjs = await import("pdfjs-dist/legacy/build/pdf.mjs"); task = pdfjs.getDocument({ data: new Uint8Array(buffer), verbosity: 0, // VerbosityLevel.ERRORS — no console spam on odd PDFs. - isEvalSupported: false, // never eval font programs (defence in depth). useSystemFonts: false, }); const doc = await task.promise;