fix(canvas): prevent AbortSignal cloning in render worker
This commit is contained in:
@@ -21,7 +21,7 @@ type PreviewWorkerPayload = {
|
|||||||
featureFlags?: BackendFeatureFlags;
|
featureFlags?: BackendFeatureFlags;
|
||||||
};
|
};
|
||||||
|
|
||||||
type FullWorkerPayload = RenderFullOptions & {
|
type FullWorkerPayload = Omit<RenderFullOptions, "signal"> & {
|
||||||
featureFlags?: BackendFeatureFlags;
|
featureFlags?: BackendFeatureFlags;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ type PreviewWorkerPayload = {
|
|||||||
featureFlags?: BackendFeatureFlags;
|
featureFlags?: BackendFeatureFlags;
|
||||||
};
|
};
|
||||||
|
|
||||||
type FullWorkerPayload = RenderFullOptions & {
|
type FullWorkerPayload = Omit<RenderFullOptions, "signal"> & {
|
||||||
featureFlags?: BackendFeatureFlags;
|
featureFlags?: BackendFeatureFlags;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -323,7 +323,7 @@ function runWorkerRequest<TResponse extends PreviewRenderResult | RenderFullResu
|
|||||||
worker.postMessage({
|
worker.postMessage({
|
||||||
kind: "full",
|
kind: "full",
|
||||||
requestId,
|
requestId,
|
||||||
payload: args.payload as RenderFullOptions,
|
payload: args.payload as FullWorkerPayload,
|
||||||
} satisfies WorkerRequestMessage);
|
} satisfies WorkerRequestMessage);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -501,14 +501,16 @@ export async function renderPreviewWithWorkerFallback(options: {
|
|||||||
export async function renderFullWithWorkerFallback(
|
export async function renderFullWithWorkerFallback(
|
||||||
options: RenderFullOptions,
|
options: RenderFullOptions,
|
||||||
): Promise<RenderFullResult> {
|
): Promise<RenderFullResult> {
|
||||||
|
const { signal, ...serializableOptions } = options;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return await runWorkerRequest<RenderFullResult>({
|
return await runWorkerRequest<RenderFullResult>({
|
||||||
kind: "full",
|
kind: "full",
|
||||||
payload: {
|
payload: {
|
||||||
...options,
|
...serializableOptions,
|
||||||
featureFlags: getWorkerFeatureFlagsSnapshot(),
|
featureFlags: getWorkerFeatureFlagsSnapshot(),
|
||||||
},
|
},
|
||||||
signal: options.signal,
|
signal,
|
||||||
});
|
});
|
||||||
} catch (error: unknown) {
|
} catch (error: unknown) {
|
||||||
if (isAbortError(error)) {
|
if (isAbortError(error)) {
|
||||||
|
|||||||
@@ -199,6 +199,48 @@ describe("worker-client fallbacks", () => {
|
|||||||
expect(bridgeMocks.renderFull).not.toHaveBeenCalled();
|
expect(bridgeMocks.renderFull).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("does not include AbortSignal in full worker payload serialization", async () => {
|
||||||
|
const workerMessages: WorkerMessage[] = [];
|
||||||
|
FakeWorker.behavior = (worker, message) => {
|
||||||
|
workerMessages.push(message);
|
||||||
|
if (message.kind !== "full") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
queueMicrotask(() => {
|
||||||
|
worker.onmessage?.({
|
||||||
|
data: {
|
||||||
|
kind: "full-result",
|
||||||
|
requestId: message.requestId,
|
||||||
|
payload: createFullResult(),
|
||||||
|
},
|
||||||
|
} as MessageEvent);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
vi.stubGlobal("Worker", FakeWorker as unknown as typeof Worker);
|
||||||
|
|
||||||
|
const { renderFullWithWorkerFallback } = await import("@/lib/image-pipeline/worker-client");
|
||||||
|
|
||||||
|
await renderFullWithWorkerFallback({
|
||||||
|
sourceUrl: "https://cdn.example.com/source.png",
|
||||||
|
steps: [],
|
||||||
|
render: {
|
||||||
|
resolution: "original",
|
||||||
|
format: "png",
|
||||||
|
},
|
||||||
|
signal: new AbortController().signal,
|
||||||
|
});
|
||||||
|
|
||||||
|
const fullMessage = workerMessages.find((message) => message.kind === "full") as
|
||||||
|
| (WorkerMessage & {
|
||||||
|
payload?: Record<string, unknown>;
|
||||||
|
})
|
||||||
|
| undefined;
|
||||||
|
|
||||||
|
expect(fullMessage).toBeDefined();
|
||||||
|
expect(fullMessage?.payload).not.toHaveProperty("signal");
|
||||||
|
});
|
||||||
|
|
||||||
it("still falls back to the main thread when the Worker API is unavailable", async () => {
|
it("still falls back to the main thread when the Worker API is unavailable", async () => {
|
||||||
vi.stubGlobal("Worker", undefined);
|
vi.stubGlobal("Worker", undefined);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user