Fix Invalid PDF structure after reload for uploaded documents.
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 2s

Stop inventing /fixtures/pdfs/<title> URLs when blob bytes are missing;
rely on IndexedDB-hydrated byte store. Seeded tests set an explicit fixture
uri. Session metadata already survived; PDF bytes now survive too.
This commit is contained in:
tegwick 2026-07-30 20:05:26 +02:00
parent 9dbe806487
commit a73d45bfac
3 changed files with 26 additions and 8 deletions

View file

@ -38,11 +38,16 @@ describe("resolvePdfViewerUrl", () => {
);
});
it("falls back to fixture path when only a stale blob uri is stored", () => {
it("returns null for stale blob uri without live bytes (no fixture guess)", () => {
const store = createPdfByteStore();
expect(
resolvePdfViewerUrl(doc({ uri: "blob:revoked", title: "a.pdf" }), store),
).toBe("/fixtures/pdfs/a.pdf");
).toBeNull();
});
it("returns null when uri was stripped on persist and bytes are gone", () => {
const store = createPdfByteStore();
expect(resolvePdfViewerUrl(doc({ uri: undefined, title: "upload.pdf" }), store)).toBeNull();
});
});
@ -51,4 +56,4 @@ describe("isEphemeralBlobUri", () => {
expect(isEphemeralBlobUri("blob:http://localhost/x")).toBe(true);
expect(isEphemeralBlobUri("/fixtures/pdfs/x.pdf")).toBe(false);
});
});
});

View file

@ -4,6 +4,10 @@
* Uploaded PDFs are served via ephemeral `blob:` URLs owned by
* `PdfByteStore`. Those URLs must not be persisted (see persistence
* sanitization) and must be re-resolved from live bytes on every render.
*
* After a reload, bytes come back via IndexedDB hydrate into the byte store.
* Never invent a `/fixtures/pdfs/<title>` URL for arbitrary uploads that
* path 404s (or returns HTML) and PDF.js reports "Invalid PDF structure".
*/
import type { Document } from "@shared/document";
@ -20,7 +24,8 @@ export function isEphemeralBlobUri(uri: string | undefined): boolean {
/**
* Prefer the byte store's live blob URL for uploaded documents; fall back
* to a stable HTTP fixture path or a non-blob `document.uri`.
* to a stable non-blob `document.uri` (e.g. fixture HTTP path). Returns
* `null` when bytes are not available ViewerShell shows re-upload copy.
*/
export function resolvePdfViewerUrl(
document: Document,
@ -33,8 +38,8 @@ export function resolvePdfViewerUrl(
return document.uri;
}
const titleOrId = document.title ?? document.id;
return `/fixtures/pdfs/${encodeURIComponent(titleOrId)}`;
// Stale blob URI and no in-memory/IDB bytes: do not guess a fixture path.
return null;
}
/** True when bytes exist in the store but the document record lacks a URI. */
@ -43,4 +48,4 @@ export function documentHasUploadedBytes(
byteStore: PdfByteStore,
): boolean {
return byteStore.has(documentId);
}
}

View file

@ -49,10 +49,18 @@ export function seedSessionWithDoc(opts: SeedOptions): SeedResult {
lastOpenedAt: now,
};
// Stable HTTP fixture URI so the viewer mounts without IndexedDB bytes.
// (resolvePdfViewerUrl no longer invents /fixtures paths from title alone —
// that produced "Invalid PDF structure" for arbitrary uploads after reload.)
const document: Document = {
id: documentId,
mediaType: "application/pdf",
...(opts.documentTitle ? { title: opts.documentTitle } : {}),
...(opts.documentTitle
? {
title: opts.documentTitle,
uri: `/fixtures/pdfs/${encodeURIComponent(opts.documentTitle)}`,
}
: {}),
fingerprint: "seed-fingerprint",
createdAt: now,
updatedAt: now,