← back to Tk 11331 Exec
lib.mjs
31 lines
// TK-11331 shared lib — Shopify Admin GraphQL, with retry on 429/5xx.
import fs from 'fs';
export const SHOP='designer-laboratory-sandbox.myshopify.com', VER='2024-10';
export const TOK=fs.readFileSync(process.env.HOME+'/Projects/secrets-manager/.env','utf8')
.match(/^SHOPIFY_ADMIN_TOKEN=(.+)$/m)[1].trim().replace(/^["']|["']$/g,'');
const EP=`https://${SHOP}/admin/api/${VER}/graphql.json`;
export const sleep=ms=>new Promise(r=>setTimeout(r,ms));
export async function gql(query, variables={}, tries=6){
let lastErr;
for(let i=0;i<tries;i++){
try{
const r=await fetch(EP,{method:'POST',headers:{'X-Shopify-Access-Token':TOK,'Content-Type':'application/json'},
body:JSON.stringify({query,variables})});
if(r.status===429||r.status>=500){ lastErr=`HTTP ${r.status}`; await sleep(2000*(i+1)); continue; }
const j=await r.json();
if(j.errors){
const throttled=JSON.stringify(j.errors).includes('THROTTLED');
if(throttled){ lastErr='THROTTLED'; await sleep(3000*(i+1)); continue; }
throw new Error('GQL errors: '+JSON.stringify(j.errors));
}
// throttle courtesy from cost extensions
const c=j.extensions?.cost?.throttleStatus;
if(c && c.currentlyAvailable < 200) await sleep(600);
return j.data;
}catch(e){ lastErr=e.message; await sleep(1500*(i+1)); }
}
throw new Error('gql failed after retries: '+lastErr);
}
export function j(o){return JSON.stringify(o);}