Skip to content

fix(orders): count PDF pages with pdf-lib (reliable server-side parsing)#104

Merged
choden-dev merged 2 commits into
mainfrom
fix/pdf-page-count-runtime
Jul 20, 2026
Merged

fix(orders): count PDF pages with pdf-lib (reliable server-side parsing)#104
choden-dev merged 2 commits into
mainfrom
fix/pdf-page-count-runtime

Conversation

@choden-dev

@choden-dev choden-dev commented Jul 20, 2026

Copy link
Copy Markdown
Owner

Follow-up to #103. After that PR switched server-side page counting to pdfjs-dist, valid PDFs started being rejected as "Unable to read PDF / invalid PDF" in production.

Why pdfjs failed on the server

pdfjs-dist is built for the browser:

  • Its default entry references DOM-only globals (DOMMatrix) and throws ReferenceError: DOMMatrix is not defined under Node.
  • Its legacy (Node) build was not reliably traced into the Next.js output: "standalone" container bundle (dynamic subpath import), so at runtime the import failed. The error was then swallowed and surfaced to users as "invalid PDF".

Fix: use pdf-lib

Swap the server-side counter to pdf-lib — a dependency-free, pure-TypeScript PDF library that runs anywhere Node runs:

  • No native addons, no DOM globals, no web worker, no bundler/file-tracing special-casing.
  • Parses the document object graph and returns an authoritative count via getPageCount().
  • Accurate on the real-world PDFs that made the original byte-scanner overcount (200 → 467) — annotations, incremental updates, object streams, etc.
  • ignoreEncryption: true lets us still count pages of password/permission-protected PDFs instead of falsely rejecting them.
  • Corrupt/non-PDF input is caught and logged, returning 0 so callers reject it with a clear message.

Also removes the outputFileTracingIncludes pdfjs workaround (no longer needed). pdfjs-dist remains a dependency for the client-side PdfOrder preview only.

Changes

  • package.json / lockfile — add pdf-lib.
  • lib/pdf.ts — reimplement countPdfPages on pdf-lib.
  • next.config.ts — drop the pdfjs legacy-build tracing workaround.

Verification

  • pnpm typecheck ✅, pnpm lint (biome) ✅
  • Functional: { r200: 200, r1: 1, rbad: 0 } — 200-page and 1-page docs count exactly; garbage input returns 0 (logged).
  • Annotated / encrypted PDFs also return the correct count.

Note: two commits on the branch — the first was the interim pdfjs standalone-tracing fix, the second replaces it with the pdf-lib approach. Can squash on merge.

Follow-up to #103. After switching page counting to pdfjs, valid PDFs
were being rejected as invalid in production.

Two root causes:

1. The pdfjs-dist *default* entry references browser-only globals
   (DOMMatrix) and throws 'ReferenceError: DOMMatrix is not defined'
   under Node. We already import the legacy build, but Next.js's
   standalone output-file tracing does not follow the dynamic subpath
   import, so the legacy build was missing from the deployed container.
   The import then threw and was swallowed as 'invalid PDF'. Fix: force
   the legacy build into the bundle via outputFileTracingIncludes.

2. countPdfPages swallowed *all* errors and returned 0, so a failure to
   load the parser (a server misconfiguration) was indistinguishable
   from a genuinely corrupt file. Fix: load the parser outside the
   try/catch and throw a distinct PdfParserUnavailableError so it
   surfaces as a 500 instead of telling the user their file is invalid;
   log genuine parse failures so real bad files can be told apart from
   parser regressions.
Replace the server-side pdfjs-based page counter with pdf-lib.

pdfjs-dist is designed for the browser: its default build references
DOM-only globals (DOMMatrix) and crashes under Node, and its legacy
build was unreliable to trace into the Next.js standalone/container
bundle — so valid PDFs were being rejected as invalid in production.

pdf-lib is a dependency-free, pure-TypeScript PDF library that runs
anywhere Node runs (no native addons, no DOM globals, no web worker, no
bundler/file-tracing special-casing). It parses the document object
graph and returns an authoritative page count via getPageCount().

- Accurate on real-world PDFs (annotations/incremental updates that made
  the original byte-scanner overcount 200 -> 467).
- ignoreEncryption: true lets us still count pages of password/permission
  -protected PDFs instead of falsely rejecting them.
- Invalid/corrupt input is caught and logged, returning 0 so callers
  reject it with a clear message.

Also drops the now-unnecessary outputFileTracingIncludes workaround for
the pdfjs legacy build. pdfjs-dist remains a dependency for the
client-side PdfOrder preview only.
@choden-dev choden-dev changed the title fix(orders): make server-side PDF parsing work in the standalone build fix(orders): count PDF pages with pdf-lib (reliable server-side parsing) Jul 20, 2026
@choden-dev
choden-dev merged commit 662217f into main Jul 20, 2026
4 checks passed
@choden-dev
choden-dev deleted the fix/pdf-page-count-runtime branch July 20, 2026 12:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant