fix(invoice-inbox): stub DOM globals so pdfjs runs on Vercel (#407)

* 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) <noreply@anthropic.com>

* 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) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Jakob Wennberg
2026-05-07 10:53:45 +02:00
committed by GitHub
co-authored by Claude Opus 4.7
parent 30f5877e57
commit 5f9f72959f
2 changed files with 17 additions and 0 deletions
@@ -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
+5
View File
@@ -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 [
{