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