mirror of
https://github.com/AmruthPillai/Reactive-Resume.git
synced 2026-08-19 21:11:45 +10:00
fix(stylesheet): harden PDF preflight and surface worker failures (#3284)
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
This commit is contained in:
co-authored by
Cursor Agent
parent
4a8f87ab8f
commit
ba8e1be2ab
@@ -187,7 +187,7 @@ describe("stylesheet PDF preflight worker", () => {
|
||||
expect(runner.activeWorkerCount).toBe(0);
|
||||
}, 15_000);
|
||||
|
||||
it("does not expose internal errors from a failed worker", async () => {
|
||||
it("does not expose internal errors from a failed worker bootstrap", async () => {
|
||||
const runner = createStylesheetPreflightRunner({}, failedWorker);
|
||||
|
||||
const result = await runner.run(input);
|
||||
@@ -202,6 +202,31 @@ describe("stylesheet PDF preflight worker", () => {
|
||||
expect(runner.queuedPreflightCount).toBe(0);
|
||||
});
|
||||
|
||||
it("surfaces sanitized render failures from inside the worker catch path", async () => {
|
||||
const throwingWorker = new URL(
|
||||
`data:text/javascript,${encodeURIComponent(`
|
||||
import { parentPort } from "node:worker_threads";
|
||||
parentPort.postMessage({ type: "ready" });
|
||||
parentPort.postMessage({
|
||||
ok: false,
|
||||
code: "STYLESHEET_PREFLIGHT_WORKER_FAILED",
|
||||
message: "The PDF preflight worker failed. (Error: Canvas is already closed)",
|
||||
diagnostics: [],
|
||||
});
|
||||
`)}`,
|
||||
);
|
||||
const runner = createStylesheetPreflightRunner({ timeoutMs: 5_000 }, throwingWorker);
|
||||
|
||||
const result = await runner.run(input);
|
||||
|
||||
expect(result).toEqual({
|
||||
ok: false,
|
||||
code: "STYLESHEET_PREFLIGHT_WORKER_FAILED",
|
||||
message: "The PDF preflight worker failed. (Error: Canvas is already closed)",
|
||||
diagnostics: [],
|
||||
});
|
||||
});
|
||||
|
||||
it("bounds concurrent workers and queued requests without charging queue time to the worker deadline", async () => {
|
||||
const runner = createStylesheetPreflightRunner(
|
||||
{
|
||||
|
||||
@@ -75,9 +75,10 @@ const workerLocation = () => {
|
||||
url: source
|
||||
? new URL("../workers/stylesheet-preflight.ts", import.meta.url)
|
||||
: new URL("./stylesheet-preflight-worker.mjs", import.meta.url),
|
||||
// Production must not inherit parent execArgv (e.g. --import/--input-type); source workers need tsx.
|
||||
execArgv: source ? sourceWorkerExecArgv() : [],
|
||||
...(source
|
||||
? {
|
||||
execArgv: sourceWorkerExecArgv(),
|
||||
env: {
|
||||
...process.env,
|
||||
TSX_TSCONFIG_PATH: fileURLToPath(new URL("../../tsconfig.json", import.meta.url)),
|
||||
@@ -106,7 +107,7 @@ export function createStylesheetPreflightRunner(
|
||||
reject: (cause: unknown) => void,
|
||||
): boolean => {
|
||||
// The URL seam is internal to the server package and keeps worker failure tests independent from the PDF renderer.
|
||||
const location = testWorkerUrl ? { source: false, url: testWorkerUrl } : workerLocation();
|
||||
const location = testWorkerUrl ? { source: false, url: testWorkerUrl, execArgv: [] as string[] } : workerLocation();
|
||||
let worker: Worker;
|
||||
try {
|
||||
worker = new Worker(location.url, {
|
||||
@@ -116,7 +117,7 @@ export function createStylesheetPreflightRunner(
|
||||
// The source-only tsx compiler heap is outside the production render budget.
|
||||
maxOldGenerationSizeMb: limits.maxOldGenerationMb + (location.source ? SOURCE_WORKER_LOADER_HEAP_MB : 0),
|
||||
},
|
||||
...("execArgv" in location ? { execArgv: location.execArgv } : {}),
|
||||
execArgv: location.execArgv,
|
||||
...("env" in location ? { env: location.env } : {}),
|
||||
});
|
||||
} catch (error) {
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
import type { PdfPreflightPageLimits, PdfPreflightResult, RenderPreflightPdfResult } from "@reactive-resume/pdf/server";
|
||||
import type {
|
||||
PdfPreflightPageLimits,
|
||||
PdfPreflightResult,
|
||||
RenderPreflightPdfResult,
|
||||
} from "@reactive-resume/pdf/preflight";
|
||||
import { getDocument } from "pdfjs-dist/legacy/build/pdf.mjs";
|
||||
|
||||
type StylesheetPreflightInspectionLimits = PdfPreflightPageLimits & {
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
import type { PdfPreflightPageLimits, PdfPreflightResult, StylesheetPreflightInput } from "@reactive-resume/pdf/server";
|
||||
import type {
|
||||
PdfPreflightPageLimits,
|
||||
PdfPreflightResult,
|
||||
StylesheetPreflightInput,
|
||||
} from "@reactive-resume/pdf/preflight";
|
||||
import { parentPort, workerData } from "node:worker_threads";
|
||||
import * as React from "react";
|
||||
import { renderPreflightPdf } from "@reactive-resume/pdf/server";
|
||||
import { inspectPreflightPdf } from "./stylesheet-preflight-inspection";
|
||||
|
||||
(globalThis as typeof globalThis & { React: typeof React }).React = React;
|
||||
@@ -26,6 +29,13 @@ const send = (result: PdfPreflightResult) => {
|
||||
parentPort?.postMessage(result);
|
||||
};
|
||||
|
||||
const sanitizeWorkerCause = (cause: unknown): string => {
|
||||
if (!(cause instanceof Error)) return "The PDF preflight worker failed.";
|
||||
const detail = cause.message.replace(/\s+/g, " ").trim().slice(0, 200);
|
||||
if (!detail) return "The PDF preflight worker failed.";
|
||||
return `The PDF preflight worker failed. (${cause.name}: ${detail})`;
|
||||
};
|
||||
|
||||
const serializeZodCause = (cause: unknown): SerializedPreflightCause | undefined => {
|
||||
if (!(cause instanceof Error) || cause.name !== "ZodError" || !("issues" in cause) || !Array.isArray(cause.issues)) {
|
||||
return;
|
||||
@@ -33,9 +43,11 @@ const serializeZodCause = (cause: unknown): SerializedPreflightCause | undefined
|
||||
return { name: cause.name, message: cause.message, issues: cause.issues };
|
||||
};
|
||||
|
||||
parentPort?.postMessage({ type: "ready" });
|
||||
const initialization = import("@reactive-resume/pdf/preflight");
|
||||
void initialization.then(() => parentPort?.postMessage({ type: "ready" }));
|
||||
|
||||
async function run(): Promise<PdfPreflightResult> {
|
||||
const { renderPreflightPdf } = await initialization;
|
||||
const { input, limits } = workerData as StylesheetPreflightWorkerData;
|
||||
const rendered = await renderPreflightPdf(input, limits);
|
||||
return rendered.ok ? inspectPreflightPdf(rendered, limits) : rendered;
|
||||
@@ -50,10 +62,11 @@ if (parentPort) {
|
||||
parentPort?.postMessage({ type: "preflight_error", cause: serializedCause });
|
||||
return;
|
||||
}
|
||||
console.error("[stylesheet-preflight]", cause);
|
||||
send({
|
||||
ok: false,
|
||||
code: "STYLESHEET_PREFLIGHT_WORKER_FAILED",
|
||||
message: "The PDF preflight worker failed.",
|
||||
message: sanitizeWorkerCause(cause),
|
||||
diagnostics: [],
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user