From d74b394adb0d2b02e1ab7b71653ee30980e6640e Mon Sep 17 00:00:00 2001 From: Brennan Benson <79079362+brennanb2025@users.noreply.github.com> Date: Wed, 29 Jul 2026 19:12:54 -0700 Subject: [PATCH] fix(feedback): honor atomic image delivery response MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Production’s single-message feedback endpoint uploads text and images atomically, then returns 202 {"ok":true} without an imagesDelivered field. Treating that omission as false warned users that every successful production attachment had failed. Treat a settled successful JSON response with ok: true and no image field as delivered. Explicit imagesDelivered: false still surfaces partial delivery, while malformed, oversized, aborted, and stalled bodies remain unconfirmed or fail through the existing response bound and timeout path. --- src/main/ipc/feedback-image-attachments.ts | 8 ++++++-- src/main/ipc/feedback.test.ts | 10 +++------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/main/ipc/feedback-image-attachments.ts b/src/main/ipc/feedback-image-attachments.ts index 413c57fc4b9..f574a7129ab 100644 --- a/src/main/ipc/feedback-image-attachments.ts +++ b/src/main/ipc/feedback-image-attachments.ts @@ -77,16 +77,20 @@ export function appendFeedbackImagesToFormData( } } -/** A 2xx confirms the text; only the response contract can confirm its images. */ +/** Atomic servers omit the image field after both the text and images land. */ export async function readFeedbackImagesDelivered(response: Response): Promise { try { const parsed: unknown = await readFetchResponseJsonWithinLimit( response, MAX_FEEDBACK_IMAGE_RESPONSE_BYTES ) - if (typeof parsed === 'object' && parsed !== null && 'imagesDelivered' in parsed) { + if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) { + return false + } + if ('imagesDelivered' in parsed) { return (parsed as { imagesDelivered?: unknown }).imagesDelivered === true } + return (parsed as { ok?: unknown }).ok === true } catch {} return false } diff --git a/src/main/ipc/feedback.test.ts b/src/main/ipc/feedback.test.ts index b73873e5e96..4646ee8a364 100644 --- a/src/main/ipc/feedback.test.ts +++ b/src/main/ipc/feedback.test.ts @@ -412,11 +412,7 @@ describe('submitFeedback', () => { } function jsonResponse(body: unknown): Response { - return { - ok: true, - status: 202, - json: async () => body - } as unknown as Response + return Response.json(body, { status: 202 }) } it('sends attached images as multipart form parts', async () => { @@ -447,12 +443,12 @@ describe('submitFeedback', () => { }) }) - it('reports unconfirmed delivery when a 2xx omits the image result', async () => { + it('accepts the production atomic-success response when it omits the image result', async () => { fetchMock.mockResolvedValue(jsonResponse({ ok: true })) await expect(submitFeedback(imageSubmitArgs([pngImage()]))).resolves.toEqual({ ok: true, - imagesDelivered: false + imagesDelivered: true }) })