← back to Newmor Onboard
scripts/shopify.mjs
38 lines
import fs from 'node:fs';
export const ROOT = '/Users/macstudio3/Projects/newmor-onboard';
export const SHOP = 'designer-laboratory-sandbox.myshopify.com';
export const API = '2026-07';
const env = fs.readFileSync('/Users/macstudio3/Projects/secrets-manager/.env', 'utf8');
const token = env.match(/^SHOPIFY_ADMIN_TOKEN=(.+)$/m)?.[1].trim().replace(/^["']|["']$/g, '');
if (!token) throw new Error('Missing Shopify credential');
const themeToken = env.match(/^SHOPIFY_THEME_TOKEN=(.+)$/m)?.[1].trim().replace(/^["']|["']$/g, '');
export async function request(path, method = 'GET', body) {
const r = await fetch(`https://${SHOP}/admin/api/${API}/${path}`, {
method, headers: { 'X-Shopify-Access-Token': path.startsWith('themes') ? (themeToken || token) : token, 'Content-Type': 'application/json' },
body: body ? JSON.stringify(body) : undefined, signal: AbortSignal.timeout(60000),
});
const j = await r.json();
if (!r.ok || j.errors) throw new Error(`${method} ${path}: ${r.status} ${JSON.stringify(j.errors || j)}`);
return j;
}
export async function gql(query, variables = {}) {
return (await request('graphql.json', 'POST', { query, variables })).data;
}
export const fields = `id handle title vendor status createdAt updatedAt onlineStoreUrl onlineStorePreviewUrl templateSuffix tags descriptionHtml
showroom:metafield(namespace:"custom",key:"showroom_line"){id type value}
mf:metafields(first:100){nodes{id namespace key type value} pageInfo{hasNextPage}}
variants(first:100){nodes{id title sku price availableForSale inventoryPolicy inventoryItem{id tracked}} pageInfo{hasNextPage}}
images(first:100){nodes{id url altText} pageInfo{hasNextPage}}
resourcePublications(first:50){nodes{isPublished publication{id name}} pageInfo{hasNextPage}}
configuredPublications:resourcePublicationsV2(first:50,onlyPublished:false){nodes{isPublished publishDate publication{id name}}pageInfo{hasNextPage}}`;
export function complete(p) {
for (const k of ['mf','variants','images','resourcePublications','configuredPublications']) if (p[k].pageInfo.hasNextPage) throw new Error(`Incomplete ${p.id} ${k}`);
return p;
}
export async function product(id) { return complete((await gql(`query($id:ID!){product(id:$id){${fields}}}`,{id})).product); }
export const read = p => JSON.parse(fs.readFileSync(`${ROOT}/${p}`, 'utf8'));
export function save(p, value) {
fs.mkdirSync(`${ROOT}/${p.substring(0,p.lastIndexOf('/'))}`, {recursive:true});
fs.writeFileSync(`${ROOT}/${p}`, JSON.stringify(value,(key,v)=>key==='onlineStorePreviewUrl'?undefined:v,2)+'\n');
}