UNPKG

9.45 kBJavaScriptView Raw
1/*! shopify/storefront-api-client@1.0.3 -- Copyright (c) 2023-present, Shopify Inc. -- license (MIT): https://github.com/Shopify/shopify-app-js/blob/main/LICENSE.md */
2!function(e,r){"object"==typeof exports&&"undefined"!=typeof module?r(exports):"function"==typeof define&&define.amd?define(["exports"],r):r((e="undefined"!=typeof globalThis?globalThis:e||self).ShopifyStorefrontAPIClient={})}(this,(function(e){"use strict";const r="GraphQL Client",t=0,n=3,o="An error occurred while fetching from the API. Review 'graphQLErrors' for details.",s="Response returned unexpected Content-Type:",i="An unknown error has occurred. The API did not return a data object or any errors in its response.",a={json:"application/json",multipart:"multipart/mixed"},c="X-SDK-Variant",u="X-SDK-Version",l="shopify-graphql-client",p="1.2.1",d=1e3,f=[429,503],h=/@(defer)\b/i,y=/boundary="?([^=";]+)"?/i,m="\r\n\r\n";function g(e,t=r){return e.startsWith(`${t}`)?e:`${t}: ${e}`}function w(e){return e instanceof Error?e.message:JSON.stringify(e)}function b(e){return e instanceof Error&&e.cause?e.cause:void 0}function $(e){return e.flatMap((({errors:e})=>e??[]))}function A({client:e,retries:r}){if(void 0!==r&&("number"!=typeof r||r<t||r>n))throw new Error(`${e}: The provided "retries" value (${r}) is invalid - it cannot be less than ${t} or greater than ${n}`)}function S(e,r){return r&&("object"!=typeof r||Array.isArray(r)||"object"==typeof r&&Object.keys(r).length>0)?{[e]:r}:{}}function v(e,r){if(0===e.length)return r;const t={[e.pop()]:r};return 0===e.length?t:v(e,t)}function x(e,r){return Object.keys(r||{}).reduce(((t,n)=>("object"==typeof r[n]||Array.isArray(r[n]))&&e[n]?(t[n]=x(e[n],r[n]),t):(t[n]=r[n],t)),Array.isArray(e)?[...e]:{...e})}function E([e,...r]){return r.reduce(x,{...e})}function T({clientLogger:e,customFetchApi:t=fetch,client:n=r,defaultRetryWaitTime:o=d,retriableCodes:s=f}){const i=async(r,a,c)=>{const u=a+1,l=c+1;let p;try{if(p=await t(...r),e({type:"HTTP-Response",content:{requestParams:r,response:p}}),!p.ok&&s.includes(p.status)&&u<=l)throw new Error;return p}catch(t){if(u<=l){const t=p?.headers.get("Retry-After");return await async function(e){return new Promise((r=>setTimeout(r,e)))}(t?parseInt(t,10):o),e({type:"HTTP-Retry",content:{requestParams:r,lastResponse:p,retryAttempt:a,maxRetries:c}}),i(r,u,c)}throw new Error(g(`${c>0?`Attempted maximum number of ${c} network retries. Last message - `:""}${w(t)}`,n))}};return i}function k({headers:e,url:t,customFetchApi:n=fetch,retries:o=0,logger:i}){A({client:r,retries:o});const f={headers:e,url:t,retries:o},v=function(e){return r=>{e&&e(r)}}(i),x=function(e,{url:t,headers:n,retries:o}){return async(s,i={})=>{const{variables:a,headers:d,url:f,retries:h,signal:y}=i,m=JSON.stringify({query:s,variables:a});A({client:r,retries:h});const g=Object.entries({...n,...d}).reduce(((e,[r,t])=>(e[r]=Array.isArray(t)?t.join(", "):t.toString(),e)),{});g[c]||g[u]||(g[c]=l,g[u]=p);return e([f??t,{method:"POST",headers:g,body:m,signal:y}],1,h??o)}}(T({customFetchApi:n,clientLogger:v,defaultRetryWaitTime:d}),f),k=function(e){return async(...r)=>{if(h.test(r[0]))throw new Error(g("This operation will result in a streamable response - use requestStream() instead."));try{const t=await e(...r),{status:n,statusText:o}=t,i=t.headers.get("content-type")||"";return t.ok?i.includes(a.json)?j(t):{errors:{networkStatusCode:n,message:g(`${s} ${i}`),response:t}}:{errors:{networkStatusCode:n,message:g(o),response:t}}}catch(e){return{errors:{message:w(e)}}}}}(x),C=function(e){return async(...r)=>{if(!h.test(r[0]))throw new Error(g("This operation does not result in a streamable response - use request() instead."));try{const t=await e(...r),{statusText:n}=t;if(!t.ok)throw new Error(n,{cause:t});const o=t.headers.get("content-type")||"";switch(!0){case o.includes(a.json):return function(e){return{async*[Symbol.asyncIterator](){const r=await j(e);yield{...r,hasNext:!1}}}}(t);case o.includes(a.multipart):return function(e,r){const t=(r??"").match(y),n=`--${t?t[1]:"-"}`;if(!e.body?.getReader&&!e.body[Symbol.asyncIterator])throw new Error("API multipart response did not return an iterable body",{cause:e});const o=async function*(e){const r=new TextDecoder;if(e.body[Symbol.asyncIterator])for await(const t of e.body)yield r.decode(t);else{const t=e.body.getReader();let n;try{for(;!(n=await t.read()).done;)yield r.decode(n.value)}finally{t.cancel()}}}(e);let s,i={};return{async*[Symbol.asyncIterator](){try{let e=!0;for await(const r of function(e,r){return{async*[Symbol.asyncIterator](){try{let t="";for await(const n of e)if(t+=n,t.indexOf(r)>-1){const e=t.lastIndexOf(r),n=t.slice(0,e).split(r).filter((e=>e.trim().length>0)).map((e=>e.slice(e.indexOf(m)+m.length).trim()));n.length>0&&(yield n),t=t.slice(e+r.length),"--"===t.trim()&&(t="")}}catch(e){throw new Error(`Error occured while processing stream payload - ${w(e)}`)}}}}(o,n)){const t=V(r);s=t.find((e=>e.extensions))?.extensions??s;const n=$(t);i=E([i,...t.map((({data:e})=>e))]),e=t.slice(-1)[0].hasNext,q(n,i),yield{...S("data",i),...S("extensions",s),hasNext:e}}if(e)throw new Error("Response stream terminated unexpectedly")}catch(r){const t=b(r);yield{...S("data",i),...S("extensions",s),errors:{message:g(w(r)),networkStatusCode:e.status,...S("graphQLErrors",t?.graphQLErrors),response:e},hasNext:!1}}}}}(t,o);default:throw new Error(`${s} ${o}`,{cause:t})}}catch(e){return{async*[Symbol.asyncIterator](){const r=b(e);yield{errors:{message:g(w(e)),...S("networkStatusCode",r?.status),...S("response",r)},hasNext:!1}}}}}}(x);return{config:f,fetch:x,request:k,requestStream:C}}async function j(e){const{errors:r,data:t,extensions:n}=await e.json();return{...S("data",t),...S("extensions",n),headers:e.headers,...r||!t?{errors:{networkStatusCode:e.status,message:g(r?o:i),...S("graphQLErrors",r),response:e}}:{}}}function V(e){return e.map((e=>{try{return JSON.parse(e)}catch(e){throw new Error(`Error in parsing multipart response - ${w(e)}`)}})).map((e=>{const{data:r,incremental:t,hasNext:n,extensions:o,errors:s}=e;if(!t)return{data:r||{},...S("errors",s),...S("extensions",o),hasNext:n};const i=t.map((({data:e,path:r,errors:t})=>({data:e&&r?v(r,e):{},...S("errors",t)})));return{data:1===i.length?i[0].data:E([...i.map((({data:e})=>e))]),...S("errors",$(i)),hasNext:n}}))}function q(e,r){if(e.length>0)throw new Error(o,{cause:{graphQLErrors:e}});if(0===Object.keys(r).length)throw new Error(i)}function C({client:e,currentSupportedApiVersions:r,apiVersion:t,logger:n}){const o=`${e}: the provided apiVersion ("${t}")`,s=`Currently supported API versions: ${r.join(", ")}`;if(!t||"string"!=typeof t)throw new Error(`${o} is invalid. ${s}`);const i=t.trim();r.includes(i)||(n?n({type:"Unsupported_Api_Version",content:{apiVersion:t,supportedApiVersions:r}}):console.warn(`${o} is likely deprecated or not supported. ${s}`))}function I(e){const r=3*e-2;return 10===r?r:`0${r}`}function P(e,r,t){const n=r-t;return n<=0?`${e-1}-${I(n+4)}`:`${e}-${I(n)}`}function N(){const{year:e,quarter:r,version:t}=function(){const e=new Date,r=e.getUTCMonth(),t=e.getUTCFullYear(),n=Math.floor(r/3+1);return{year:t,quarter:n,version:`${t}-${I(n)}`}}(),n=4===r?`${e+1}-01`:`${e}-${I(r+1)}`;return[P(e,r,3),P(e,r,2),P(e,r,1),t,n,"unstable"]}const O="application/json",R="Storefront API Client";e.createStorefrontApiClient=function({storeDomain:e,apiVersion:r,publicAccessToken:t,privateAccessToken:n,clientName:o,retries:s=0,customFetchApi:i,logger:a}){const c=N(),u=function({client:e,storeDomain:r}){try{if(!r||"string"!=typeof r)throw new Error;const e=r.trim(),t=e.match(/^https?:/)?e:`https://${e}`,n=new URL(t);return n.protocol="https",n.origin}catch(t){throw new Error(`${e}: a valid store domain ("${r}") must be provided`,{cause:t})}}({client:R,storeDomain:e}),l={client:R,currentSupportedApiVersions:c,logger:a};C({...l,apiVersion:r}),function(e,r){if(!e&&!r)throw new Error(`${R}: a public or private access token must be provided`);if(e&&r)throw new Error(`${R}: only provide either a public or private access token`)}(t,n),function(e){if(e&&"undefined"!=typeof window)throw new Error(`${R}: private access tokens and headers should only be used in a server-to-server implementation. Use the public API access token in nonserver environments.`)}(n);const p=function(e,r,t){return n=>{n&&C({...t,apiVersion:n});const o=(n??r).trim();return`${e}/api/${o}/graphql.json`}}(u,r,l),d={storeDomain:u,apiVersion:r,...t?{publicAccessToken:t}:{privateAccessToken:n},headers:{"Content-Type":O,Accept:O,"X-SDK-Variant":"storefront-api-client","X-SDK-Version":"1.0.3",...o?{"X-SDK-Variant-Source":o}:{},...t?{"X-Shopify-Storefront-Access-Token":t}:{"Shopify-Storefront-Private-Token":n}},apiUrl:p(),clientName:o},f=k({headers:d.headers,url:d.apiUrl,retries:s,customFetchApi:i,logger:a}),h=function(e){return r=>({...r??{},...e.headers})}(d),y=function(e,r){return t=>t?r(t):e.apiUrl}(d,p),m=function({getHeaders:e,getApiUrl:r}){return(t,n)=>{const o=[t];if(n&&Object.keys(n).length>0){const{variables:t,apiVersion:s,headers:i,retries:a}=n;o.push({...t?{variables:t}:{},...i?{headers:e(i)}:{},...s?{url:r(s)}:{},...a?{retries:a}:{}})}return o}}({getHeaders:h,getApiUrl:y}),g={config:d,getHeaders:h,getApiUrl:y,fetch:(...e)=>f.fetch(...m(...e)),request:(...e)=>f.request(...m(...e)),requestStream:(...e)=>f.requestStream(...m(...e))};return Object.freeze(g)}}));
3//# sourceMappingURL=storefront-api-client.min.js.map