/** * Fire-and-forget runner for webhook side-effects. * * Shopify expects a 200 response within ~5 seconds, otherwise it considers * the delivery failed and retries it. Heavy automation work (PDF render, * Shopify Files upload, SMTP send) routinely exceeded that budget, which * caused duplicate invoice emails before we added the dedupe table. * * Returning the response immediately and letting the work finish in the * background keeps Shopify happy. Combined with the dedupe table this is * defence-in-depth: dedupe ensures *correctness* even if a retry sneaks * through, while async processing makes retries unlikely in the first * place. * * Errors are caught and logged \u2014 they cannot reach a dispatcher because * the HTTP response is already gone. */ export function runWebhookInBackground( description: string, work: () => Promise, ): void { // `void` so we don't accidentally `await` the floating promise; the // node event loop keeps the task alive until it settles. void work().catch((err) => { console.error(`background webhook task '${description}' failed:`, err); }); }