@swishapp/node
Version:
JS library to integrate Swish on a Node.js server.
2 lines (1 loc) • 4.78 kB
JavaScript
var d=Object.defineProperty;var h=(a,e)=>d(a,"name",{value:e,configurable:!0});import{createApiClient as p}from"@swishapp/api-client";const w="https://swish.app/api/2025-04",f=process.env.SWISH_BROWSER_PACKAGE_HOST??"https://cdn.jsdelivr.net/npm/@swishapp/browser@0.14.0/dist",g=process.env.SWISH_UI_HOST??"https://storage.googleapis.com/swi-prod-swish-ui/__default/assets",y=["shop","logged_in_customer_id","timestamp","signature","path_prefix"],k=["accept","accept-encoding","accept-language","cache-control","content-type","pragma","user-agent","x-forwarded-for","x-forwarded-host","x-forwarded-proto","x-request-id","x-shopify-client-ip"],S=h(a=>new T(a),"createAppProxy");class T{static{h(this,"AppProxy")}constructor({authToken:e,storage:t,basePath:r,apiHost:s,authenticate:o,onError:n}){this.storage=t,this.apiProxyPath=r+"/api",this.assetProxyPath=r+"/assets",this.onError=n,this.authenticate=o,this.authToken=e,this.apiHost=s??w}async forward(e){if(new URL(e.url).pathname.startsWith(this.assetProxyPath))return await this.forwardAssetRequest(e);try{const r=await this.authenticate(e)??null;return await this.forwardApiRequest(e,r)}catch(r){this.onError?.(r)}return new Response("Unauthorized",{status:401})}async forwardAssetRequest(e){const r=new URL(e.url).pathname.replace(this.assetProxyPath,"");try{return await this.getFileContent(r)}catch(s){return this.onError?.(s),new Response("File not found",{status:404})}}async getFileContent(e){let t="";const r={"Cache-Control":"max-age=60, stale-while-revalidate=3600","Content-Type":this.getContentType(e)};return e.startsWith("/browser/")&&(t=await this.loadAsset(f,e.replace("/browser",""))),e.startsWith("/ui/")&&(e.endsWith("/manifest.json")||(r["Cache-Control"]="public, max-age=31536000, immutable"),t=await this.loadAsset(g,e.replace("/ui",""))),new Response(t,{headers:r})}async loadAsset(e,t){return console.log("Loading asset",`${e}${t}`),await(await fetch(`${e}${t}`)).text()}async forwardApiRequest(e,t){try{const r=this.extractHeaderSid(e),s=await this.getToken(e,t),o=this.parseToken(s),n=this.createForwardingUrl(e),l=this.createForwardingHeaders(e,{authToken:s}),u=await e.text(),i=await fetch(n,{body:u||void 0,method:e.method,headers:l}),c=new Headers(i.headers);return!r&&!t?c.set("Set-Profile",o.sub):r&&t&&c.set("Set-Profile",""),new Response(i.body,{status:i.status,headers:c})}catch(r){return this.onError?.(r),r.statusCode?new Response(JSON.stringify({error:r}),{status:r.statusCode,headers:{"Content-Type":"application/json"}}):new Response("Bad Request",{status:400})}}createForwardingHeaders(e,t){const r=new Headers(Array.from(e.headers.entries()).filter(([s])=>k.includes(s)));return t?.authToken&&r.set("authorization",`Bearer ${t.authToken}`),r}createForwardingUrl(e){const t=new URL(e.url),r=new URL(`${this.apiHost}${t.pathname.replace(this.apiProxyPath,"")}`);return r.search=new URLSearchParams(Array.from(t.searchParams.entries()).filter(([s])=>!y.includes(s))).toString(),r}getContentType(e){return e.endsWith(".js")?"application/javascript":e.endsWith(".json")?"application/json":"text/plain"}async getToken(e,t){const r=this.extractHeaderSid(e),s=await this.getCachedToken(r,t);if(!s){const n=await this.createToken(e,{sessionId:r,customerId:t});return await this.storage.storeToken(n.profile,n.token),n.token}const o=this.parseToken(s);if(o.sub.startsWith("gid://swish/Session")&&t){const n=await this.createToken(e,{sessionId:o.sub,customerId:t});return await this.storage.deleteToken(o.sub),await this.storage.storeToken(n.profile,n.token),n.token}return s}async getCachedToken(e,t){let r=null;if(e&&(r=await this.storage.loadToken(this.createGid("session",e))),t&&(r=await this.storage.loadToken(this.createGid("customer",t))),!r)return null;const s=this.parseToken(r);return this.isTokenExpired(s)?(await this.storage.deleteToken(s.sub),null):r}async createToken(e,t){const r=p({config:{baseUrl:this.apiHost},authToken:typeof this.authToken=="function"?await this.authToken(e):this.authToken}),{data:s,error:o}=await r.profiles.createToken({session:t?.sessionId?this.createGid("session",t.sessionId):void 0,customer:t?.customerId?this.createGid("customer",t.customerId):void 0});if(o)throw o;if(!s)throw new Error("No data returned from createToken");return s}extractHeaderSid(e){const t=e.headers.get("Profile");return t?.startsWith("gid://swish/Session")&&t||null}parseToken(e){try{return JSON.parse(atob(e.split(".")[1]))}catch(t){return this.onError?.(t),null}}createGid(e,t){return e==="session"?`gid://swish/Session/${t.split("/").pop()}`:`gid://shopify/Customer/${t.split("/").pop()}`}isTokenExpired(e){try{if(!e?.exp)return!0;const t=Math.floor(Date.now()/1e3);return e.exp<=t+3600}catch(t){return this.onError?.(t),!0}}}export{T as AppProxy,S as createAppProxy};