From 5f9f72959fe82ae671e84f498e205546e06143fb Mon Sep 17 00:00:00 2001 From: Jakob Wennberg <149234542+jakobwennberg@users.noreply.github.com> Date: Thu, 7 May 2026 10:53:45 +0200 Subject: [PATCH] fix(invoice-inbox): stub DOM globals so pdfjs runs on Vercel (#407) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix(invoice-inbox): stub DOM globals so pdfjs text extraction works on Vercel pdfjs-dist's legacy build references DOMMatrix/ImageData/Path2D at module load. On Vercel's Node runtime those globals don't exist, so the dynamic import threw "DOMMatrix is not defined" before getDocument() ran — every inbox item came back with empty extracted_data. We only call getTextContent (no rendering), so empty-class stubs are sufficient. Also mark pdfjs-dist as a server external package so Next.js doesn't try to bundle it (the bundling step pulls in @napi-rs/canvas references that aren't installed and fails silently with the same downstream error). Co-Authored-By: Claude Opus 4.7 (1M context) * refactor(invoice-inbox): hoist DOM stubs to module scope Per Greptile review on #407: stubs run once at module load instead of per-call. Same effect, clearer intent. Wrapped in a block to scope the local variable. Co-Authored-By: Claude Opus 4.7 (1M context) --------- Co-authored-by: Claude Opus 4.7 (1M context) --- .../invoice-inbox/lib/extract-invoice-fields.ts | 12 ++++++++++++ next.config.ts | 5 +++++ 2 files changed, 17 insertions(+) diff --git a/extensions/general/invoice-inbox/lib/extract-invoice-fields.ts b/extensions/general/invoice-inbox/lib/extract-invoice-fields.ts index c33861b1..8ac91e3e 100644 --- a/extensions/general/invoice-inbox/lib/extract-invoice-fields.ts +++ b/extensions/general/invoice-inbox/lib/extract-invoice-fields.ts @@ -13,6 +13,18 @@ import type { InvoiceExtractionResult } from '@/types' import { normalizeOrgNumber } from '@/lib/company-lookup/normalize-org-number' import { validateOcrReference, validateBankgiroNumber } from '@/lib/bankgiro/luhn' +// pdfjs-dist references DOMMatrix/ImageData/Path2D at module load. On +// Vercel's Node runtime these globals don't exist; without stubs the +// dynamic import throws "DOMMatrix is not defined" before getDocument() +// runs. We only call getTextContent (no rendering), so empty-class stubs +// are enough — pdfjs never invokes any methods on them. +{ + const g = globalThis as unknown as { DOMMatrix?: unknown; ImageData?: unknown; Path2D?: unknown } + if (typeof g.DOMMatrix === 'undefined') g.DOMMatrix = class {} + if (typeof g.ImageData === 'undefined') g.ImageData = class {} + if (typeof g.Path2D === 'undefined') g.Path2D = class {} +} + // Below this we treat the document as image-only / unreadable and skip // regex extraction. pdfjs-dist returns near-zero text for scanned PDFs. const MIN_TEXT_CHARS_FOR_EXTRACTION = 10 diff --git a/next.config.ts b/next.config.ts index 723d11bb..2b4b4b2e 100644 --- a/next.config.ts +++ b/next.config.ts @@ -20,6 +20,11 @@ const cspDirectives = [ const nextConfig: NextConfig = { output: 'standalone', + // Keep pdfjs-dist out of the bundle. The legacy build references + // @napi-rs/canvas at module load and breaks Vercel's bundling step. + // Text extraction works in pure Node once DOM globals are stubbed at + // the call site (see extensions/general/invoice-inbox/lib/extract-invoice-fields.ts). + serverExternalPackages: ['pdfjs-dist'], async redirects() { return [ {