From 951db4947e1f20bb74374b3a47be4b5962d4d42e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Bombeck?= Date: Thu, 9 Jul 2026 09:29:35 +0200 Subject: [PATCH] fix(documents): load the local PDF parser lazily so the vault routes stop crashing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The local content-extraction reader imported pdf-parse at module top level. pdf-parse pulls in pdfjs, whose module evaluation references the browser-only DOMMatrix global; on the server that reference throws during chunk instantiation and takes down every route sharing the chunk — the document list, upload, and search among them, so old and new documents alike fail to load. Move the parser to a lazy import() inside the extraction function (surface type kept as a type-only import). Verified under plain Node that the lazy path still extracts a real PDF text layer, so local indexing is unaffected. --- CHANGELOG.md | 10 ++++++++++ docs/api/openapi.yaml | 2 +- package.json | 2 +- public/sw.js | 2 +- src/lib/documents/local-extract.ts | 14 +++++++++++++- 5 files changed, 26 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e2ec3b060..f1bf08364 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,16 @@ ## [Unreleased] +## [1.28.1] — 2026-07-09 — Document vault load fix + +Fixes documents failing to load — old and newly uploaded alike — on +deployments where local content extraction is present. The local PDF text +reader imported its parser at module load, which pulled a browser-only global +into the server bundle and threw during evaluation, taking the document routes +down with it. The parser now loads lazily on first extraction; the document +list, upload, and content search work again, and local text extraction is +unchanged. + ## [1.28.0] — 2026-07-09 — Document intelligence A milestone that closes the document line end to end. The vault stopped being a diff --git a/docs/api/openapi.yaml b/docs/api/openapi.yaml index f97b2277b..61dcb3525 100644 --- a/docs/api/openapi.yaml +++ b/docs/api/openapi.yaml @@ -1,7 +1,7 @@ openapi: 3.1.0 info: title: HealthLog API - version: 1.28.0 + version: 1.28.1 description: >- Self-hosted personal-health-tracking PWA — public API surface for the iOS native client and external ingest. diff --git a/package.json b/package.json index 7a3e4b911..250ed1262 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "healthlog", - "version": "1.28.0", + "version": "1.28.1", "description": "Self-hosted personal-health-tracking PWA with Withings integration, AI insights, and doctor-report PDF export.", "license": "PolyForm-Noncommercial-1.0.0", "homepage": "https://healthlog.dev", diff --git a/public/sw.js b/public/sw.js index 9aba131b3..c7cd66bcf 100644 --- a/public/sw.js +++ b/public/sw.js @@ -36,7 +36,7 @@ try { // v1.4.38.4 → v1.4.42. Do not hand-edit; bump `package.json` and rebuild. const CACHE_VERSION = (typeof self !== "undefined" && self.__APP_VERSION__) || - /* @sw-version-fallback */ "v1.28.0"; + /* @sw-version-fallback */ "v1.28.1"; const STATIC_CACHE = `healthlog-static-${CACHE_VERSION}`; const PAGE_CACHE = `healthlog-pages-${CACHE_VERSION}`; // v1.18.6 — read-only data cache for a curated allowlist of safe GET `/api/*` diff --git a/src/lib/documents/local-extract.ts b/src/lib/documents/local-extract.ts index e87aa2d96..d473bd043 100644 --- a/src/lib/documents/local-extract.ts +++ b/src/lib/documents/local-extract.ts @@ -20,7 +20,16 @@ */ import { Buffer } from "node:buffer"; -import { PDFParse } from "pdf-parse"; +// `pdf-parse` pulls in pdfjs, whose module top-level references the browser-only +// `DOMMatrix` global. A STATIC import makes the Turbopack server chunk evaluate +// that reference at instantiation and throw `DOMMatrix is not defined`, taking +// down every route/worker that shares the chunk (the document routes among +// them). The value is therefore loaded lazily inside `extractPdfText` via a +// runtime `import()`, which lets pdf-parse initialise pdfjs on its own terms and +// never touches DOMMatrix at load — verified to extract a real text layer under +// plain Node. Keep the surface type as a type-only import (fully erased at +// build, no runtime module evaluation). +import type { PDFParse } from "pdf-parse"; import { annotate } from "@/lib/logging/context"; @@ -61,6 +70,9 @@ export async function extractPdfText( ): Promise { let parser: PDFParse | 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 { PDFParse } = await import("pdf-parse"); parser = new PDFParse({ data: new Uint8Array(buffer) }); const result = await parser.getText({ // Bound the page walk and drop the default "-- N of M --" page markers so