← back to Dw Signup Fulfillment

verification/tk10836/verify-app-inventory.js

43 lines

#!/usr/bin/env node
// TK-11366 HUNT: what changed around 2026-08-18? Enumerate installed apps + their timestamps and
// access scopes, looking for anything shipping/delivery/checkout-related installed or updated in
// the window. READ-ONLY.
const fs=require('fs'),os=require('os');
for (const line of fs.readFileSync(os.homedir()+'/Projects/secrets-manager/.env','utf8').split('\n')) {
  const m=line.match(/^\s*([A-Z0-9_]+)\s*=\s*(.*)$/); if(!m) continue;
  let v=m[2].trim().replace(/^['"]|['"]$/g,''); if(!process.env[m[1]]) process.env[m[1]]=v;
}
const STORE=process.env.SHOPIFY_STORE_DOMAIN, TOKEN=process.env.SHOPIFY_FULL_ACCESS_TOKEN, API='2024-10';
async function gql(q,v={}){const r=await fetch(`https://${STORE}/admin/api/${API}/graphql.json`,{method:'POST',
  headers:{'X-Shopify-Access-Token':TOKEN,'Content-Type':'application/json'},body:JSON.stringify({query:q,variables:v})});
  const j=await r.json(); if(j.errors)console.error('ERR:',JSON.stringify(j.errors).slice(0,500)); return j.data;}
(async()=>{
  const d=await gql(`query{ currentAppInstallation{ id app{ title } } }`);
  console.log('current app:', JSON.stringify(d?.currentAppInstallation?.app||null));

  // Full app installation inventory
  let c=null, apps=[];
  do{
    const r=await gql(`query($c:String){ appInstallations(first:50, after:$c){
      pageInfo{hasNextPage endCursor}
      nodes{ id
        app{ title handle developerName embedded webhookApiVersion }
        accessScopes{ handle }
        launchUrl } } }`,{c});
    if(!r?.appInstallations){ console.log('appInstallations unavailable to this token'); break; }
    apps.push(...r.appInstallations.nodes);
    c=r.appInstallations.pageInfo.hasNextPage?r.appInstallations.pageInfo.endCursor:null;
  }while(c);

  console.log(`\n### ${apps.length} app installation(s) visible ###`);
  const SHIP=/ship|deliver|checkout|rate|cart|carrier|post|freight/i;
  for(const a of apps){
    const scopes=(a.accessScopes||[]).map(s=>s.handle);
    const shipScopes=scopes.filter(s=>SHIP.test(s));
    const flag = SHIP.test(a.app?.title||'') || SHIP.test(a.app?.handle||'') || shipScopes.length;
    console.log(`${flag?'>>> ':'    '}${a.app?.title} [${a.app?.handle}] by ${a.app?.developerName||'?'}`);
    if(shipScopes.length) console.log(`        shipping/checkout scopes: ${shipScopes.join(', ')}`);
  }
  console.log('\n(">>>" = app whose name/handle or scopes touch shipping/delivery/checkout/rates)');
})();