← back to Designer Wallcoverings
Harden Tres Tintas room generator: per-request fetch timeout + 5-min wall-clock poll cap (fixes hung-socket batch wedge)
c222b30cc1acf7f76f19392b32c5092c8972b27d · 2026-06-23 11:36:00 -0700 · Steve Abrams
Files touched
M shopify/scripts/tres-tintas-room-settings.js
Diff
commit c222b30cc1acf7f76f19392b32c5092c8972b27d
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Tue Jun 23 11:36:00 2026 -0700
Harden Tres Tintas room generator: per-request fetch timeout + 5-min wall-clock poll cap (fixes hung-socket batch wedge)
---
shopify/scripts/tres-tintas-room-settings.js | 18 ++++++++++++++----
1 file changed, 14 insertions(+), 4 deletions(-)
diff --git a/shopify/scripts/tres-tintas-room-settings.js b/shopify/scripts/tres-tintas-room-settings.js
index a13b6142..f65889ee 100644
--- a/shopify/scripts/tres-tintas-room-settings.js
+++ b/shopify/scripts/tres-tintas-room-settings.js
@@ -54,8 +54,16 @@ fs.mkdirSync(BACKUP_DIR, { recursive: true });
const done = new Set(fs.existsSync(LEDGER) ? fs.readFileSync(LEDGER, 'utf8').split('\n').filter(Boolean) : []);
const sleep = (ms) => new Promise(r => setTimeout(r, ms));
-async function fetchRetry(url, opts, tries = 3) {
- let e; for (let i = 0; i < tries; i++) { try { return await fetch(url, opts); } catch (err) { e = err; await sleep(800 * (i + 1)); } } throw e;
+// Per-request timeout so a half-open socket can never wedge the batch (the
+// 2026-06-23 25-min hang). Default 60s; uploads/large posts get more headroom.
+async function fetchRetry(url, opts, tries = 3, timeoutMs = 60000) {
+ let e;
+ for (let i = 0; i < tries; i++) {
+ try {
+ return await fetch(url, { ...opts, signal: AbortSignal.timeout(timeoutMs) });
+ } catch (err) { e = err; await sleep(800 * (i + 1)); }
+ }
+ throw e;
}
async function gql(query, variables) {
const r = await fetch(`https://${STORE}/admin/api/${API}/graphql.json`, { method: 'POST', headers: H, body: JSON.stringify({ query, variables }) });
@@ -75,10 +83,12 @@ async function replicateInpaint(initPath, maskPath, outPath) {
let j = await (await fetchRetry('https://api.replicate.com/v1/predictions', { method: 'POST', headers: { Authorization: `Bearer ${RTOKEN}`, 'Content-Type': 'application/json' }, body: JSON.stringify(body) })).json();
if (j.error) throw new Error('replicate create: ' + JSON.stringify(j.error));
const url = j.urls.get;
+ const deadline = Date.now() + 5 * 60 * 1000; // hard 5-min wall-clock cap per prediction
for (let i = 0; i < 90; i++) {
+ if (Date.now() > deadline) throw new Error('replicate wall-clock timeout');
await sleep(2000);
const pr = await (await fetchRetry(url, { headers: { Authorization: `Bearer ${RTOKEN}` } })).json();
- if (pr.status === 'succeeded') { const out = Array.isArray(pr.output) ? pr.output[0] : pr.output; const img = await (await fetchRetry(out, {})).arrayBuffer(); fs.writeFileSync(outPath, Buffer.from(img)); return; }
+ if (pr.status === 'succeeded') { const out = Array.isArray(pr.output) ? pr.output[0] : pr.output; const img = await (await fetchRetry(out, {}, 3, 120000)).arrayBuffer(); fs.writeFileSync(outPath, Buffer.from(img)); return; }
if (pr.status === 'failed' || pr.status === 'canceled') throw new Error('replicate ' + pr.status + ': ' + JSON.stringify(pr.error));
}
throw new Error('replicate poll timeout');
@@ -87,7 +97,7 @@ async function replicateInpaint(initPath, maskPath, outPath) {
async function uploadImage(productId, filePath, alt) {
const b64 = fs.readFileSync(filePath).toString('base64');
const r = await fetchRetry(`https://${STORE}/admin/api/${API}/products/${productId}/images.json`, {
- method: 'POST', headers: H, body: JSON.stringify({ image: { attachment: b64, filename: `${productId}-room-${ROOM}.png`, alt } }) });
+ method: 'POST', headers: H, body: JSON.stringify({ image: { attachment: b64, filename: `${productId}-room-${ROOM}.png`, alt } }) }, 3, 120000);
const j = await r.json();
if (!j.image) throw new Error('shopify upload: ' + JSON.stringify(j.errors || j));
return j.image.id;
← afe6ecc0 auto-save: 2026-06-23T11:21:18 (3 files) — shopify/scripts/c
·
back to Designer Wallcoverings
·
Harden Tres Tintas spec recrawl: per-request fetch timeout ( 6638fe05 →