@behindthescenes/cart 0.0.3 → 0.0.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- var R="https://api.bts.it.com/v2/website/cart",v="bts-cart",F="default",G=1;class S extends Error{code;status;constructor(C,w,f){super(w);this.name="BTSCartError",this.code=C,this.status=f}}function M(){return typeof window>"u"?null:window}function Y(){try{return M()?.localStorage}catch{return}}function Z(C){return(C??R).replace(/\/$/,"")}function _(C){if(!Number.isFinite(C??1))return 1;return Math.max(1,Math.floor(C??1))}function J(C){return{items:C.map((w)=>({...w}))}}function $(C,w){let f=new Headers(C);if(!w)return f;return new Headers(w).forEach((r,B)=>f.set(B,r)),f}function z(C){let w=C.checkoutLinkId.trim();try{let f=new URL(w.includes("://")?w:w.startsWith("/")?`https://behindthescenes.com${w}`:w),r=f.pathname.split("/").filter(Boolean),B=r.indexOf("c"),b=B>=0?r[B+1]:r.at(-1);if(b)return{checkoutLinkId:b,discountCode:C.discountCode??f.searchParams.get("discountCode")??void 0}}catch{}return{checkoutLinkId:w,discountCode:C.discountCode}}class T{siteKey;endpoint;debug;persist;storageKey;storage;checkoutLinks;defaultCheckoutLinkKey;requestHeaders;items=[];listeners=new Set;constructor(C){this.siteKey=C.siteKey,this.endpoint=Z(C.endpoint),this.debug=C.debug??!1,this.persist=C.persist??!0,this.storageKey=C.storageKey??v,this.storage=C.storage??Y(),this.checkoutLinks=C.checkoutLinks??{},this.defaultCheckoutLinkKey=C.defaultCheckoutLinkKey??Object.keys(this.checkoutLinks)[0]??F,this.requestHeaders=C.requestHeaders,this.rehydrate()}static init(C){return new T(C)}getItems(){return J(this.items).items}getState(){return J(this.items)}getItemCount(){return this.items.reduce((C,w)=>C+w.quantity,0)}getSubtotal(){return this.items.reduce((C,w)=>C+(w.fullPrice??w.unitPrice)*w.quantity,0)}getTotal(){return this.items.reduce((C,w)=>C+w.unitPrice*w.quantity,0)}subscribe(C){return this.listeners.add(C),C(this.getState()),()=>{this.listeners.delete(C)}}destroy(){this.listeners.clear()}rehydrate(){if(!this.persist||!this.storage)return;try{let C=this.storage.getItem(this.storageKey);if(!C)return;let w=JSON.parse(C),f=Array.isArray(w)?w:w.items;if(!Array.isArray(f))return;this.items=f.map((r)=>this.normalizeItem(r)).filter((r)=>Boolean(r)),this.emit()}catch(C){this.log("Failed to hydrate cart",C)}}addItem(C){let w=this.normalizeItem(C);if(!w)return;let f=this.items.find((r)=>r.id===w.id);if(f){this.setQuantity(f.id,f.quantity+w.quantity);return}this.items=[...this.items,w],this.commit()}removeItem(C){this.items=this.items.filter((w)=>w.id!==C),this.commit()}setQuantity(C,w){let f=Math.floor(w);this.items=f<=0?this.items.filter((r)=>r.id!==C):this.items.map((r)=>r.id===C?{...r,quantity:f}:r),this.commit()}increment(C,w=1){let f=this.items.find((r)=>r.id===C);if(f)this.setQuantity(C,f.quantity+Math.max(1,Math.floor(w)))}decrement(C,w=1){let f=this.items.find((r)=>r.id===C);if(f)this.setQuantity(C,f.quantity-Math.max(1,Math.floor(w)))}clear(){if(this.items=[],this.persist&&this.storage)try{this.storage.removeItem(this.storageKey)}catch(C){this.log("Failed to clear cart storage",C)}this.emit()}async resolveCheckoutUrl(C=this.defaultCheckoutLinkKey){let w=this.checkoutLinks[C];if(!w)throw new S("checkout_link_not_found",`Unknown checkout link key: ${C}`);let f=z(w),r=this.itemsForCheckout(C,w);return(await this.post("/checkout-url",{siteKey:this.siteKey,checkoutLinkId:f.checkoutLinkId,mode:w.mode,discountCode:f.discountCode,items:r})).checkoutUrl}async checkout(C=this.defaultCheckoutLinkKey){let w=await this.resolveCheckoutUrl(C),f=M();if(f)f.location.assign(w);return w}itemsForCheckout(C,w){if(w.mode==="static")return[];let f=this.items.filter((r)=>r.checkoutLinkKey===C).map((r)=>({lineItemId:r.lineItemId,quantity:r.quantity,...r.discount?{discount:r.discount}:{}}));if(f.length===0)throw new S("cart_empty","Your cart is empty.");return f}normalizeItem(C){if(!C.id||!C.lineItemId||!C.name||typeof C.unitPrice!=="number")return null;return{...C,id:C.id,lineItemId:C.lineItemId,checkoutLinkKey:C.checkoutLinkKey??this.defaultCheckoutLinkKey,name:C.name,unitPrice:C.unitPrice,quantity:_(C.quantity)}}async post(C,w){let f=`${this.endpoint}${C}`,r=JSON.stringify(w),B={"Content-Type":"application/json"},b={body:w,bodyText:r,endpoint:this.endpoint,headers:B,path:C,siteKey:this.siteKey,url:f},P=typeof this.requestHeaders==="function"?await this.requestHeaders(b):this.requestHeaders,U=$(B,P),D=await fetch(f,{method:"POST",headers:U,body:r}),A=await D.json().catch(()=>null);if(!D.ok){let V=typeof A==="object"&&A&&"code"in A?A.code:"request_failed",X=typeof A==="object"&&A&&"message"in A?A.message:"Cart request failed";throw new S(V??"request_failed",X??"Cart request failed",D.status)}return A}commit(){if(this.persist&&this.storage)try{let C={version:G,items:this.items};this.storage.setItem(this.storageKey,JSON.stringify(C))}catch(C){this.log("Failed to persist cart",C)}this.emit()}emit(){let C=this.getState();for(let w of this.listeners)w(C)}log(...C){if(this.debug)console.log("[@behindthescenes/cart]",...C)}}function I(C){return T.init(C)}function N(C){return typeof C==="object"&&C!==null&&!Array.isArray(C)}function O(C){let w=typeof window>"u"?null:window;if(!w)return;let[f,r,B]=C;if(typeof f!=="string")return;if(f==="config"){if(!N(r))return;w.btsCart?.destroy?.(),w.btsCart=I(r);return}let b=w.btsCart;if(!b)return;if(f==="add"&&N(r)){b.addItem(r);return}if(f==="remove"&&typeof r==="string"){b.removeItem(r);return}if(f==="quantity"&&typeof r==="string"&&typeof B==="number"){b.setQuantity(r,B);return}if(f==="clear"){b.clear();return}if(f==="checkout")b.checkout(typeof r==="string"?r:void 0)}if(typeof window<"u"){let C=Array.isArray(window.btsCartDataLayer)?[...window.btsCartDataLayer]:[];window.btsCartDataLayer=Array.isArray(window.btsCartDataLayer)?window.btsCartDataLayer:[],window.BTSCart={BTSCart:T,createBTSCart:I},window.createBTSCart=I;for(let w of C)O(Array.from(w));window.btsCartCommand=(...w)=>{window.btsCartDataLayer?.push(w),O(w)}}export{I as createBTSCart,T as BTSCart};
1
+ var P="https://api.bts.it.com/v2/website/cart",v="bts-cart",F="default",G=1;class w extends Error{code;status;constructor(C,r,f){super(r);this.name="BTSCartError",this.code=C,this.status=f}}function N(){try{return globalThis.window??null}catch{return null}}function $(){try{return N()?.localStorage}catch{return}}function U(C){return(C??P).replace(/\/$/,"")}function z(C){if(!Number.isFinite(C??1))return 1;return Math.min(99,Math.max(1,Math.floor(C??1)))}function J(C){return{items:C.map((r)=>({...r}))}}function Q(C){if(C.discountEligibleTierOrProductIds?.length)return[...new Set(C.discountEligibleTierOrProductIds.filter(Boolean))];return[...new Set([C.accessTierId,C.productId,C.lineItemId].filter((r)=>Boolean(r)))]}function M(C){let r=new Set;for(let f of C)for(let B of[f.lineItemId,f.accessTierId,f.productId])if(B)r.add(B);return C.map((f)=>{if(!f.discount)return f;let B=Q(f);if(B.length===0||!B.some((A)=>r.has(A))){let A={...f};return delete A.discount,delete A.discountEligibleTierOrProductIds,A}return f})}function q(C,r){let f=new Headers(C);if(!r)return f;return new Headers(r).forEach((B,A)=>f.set(A,B)),f}function H(C){let r=C.checkoutLinkId.trim();try{let f=r.includes("://")?r:r.startsWith("/")?`https://bts-cart.invalid${r}`:r,B=new URL(f),A=B.pathname.split("/").filter(Boolean),T=A.indexOf("c"),R=T>=0?A[T+1]:A.at(-1);if(R)return{checkoutLinkId:R,discountCode:C.discountCode??B.searchParams.get("discountCode")??void 0}}catch{}return{checkoutLinkId:r,discountCode:C.discountCode}}class I{siteKey;endpoint;debug;persist;storageKey;storage;checkoutLinks;defaultCheckoutLinkKey;requestHeaders;items=[];listeners=new Set;constructor(C){this.siteKey=C.siteKey,this.endpoint=U(C.endpoint),this.debug=C.debug??!1,this.persist=C.persist??!0,this.storageKey=C.storageKey??v,this.storage=C.storage??$(),this.checkoutLinks=C.checkoutLinks??{},this.defaultCheckoutLinkKey=C.defaultCheckoutLinkKey??Object.keys(this.checkoutLinks)[0]??F,this.requestHeaders=C.requestHeaders,this.rehydrate()}static init(C){return new I(C)}getItems(){return J(this.items).items}getState(){return J(this.items)}getItemCount(){return this.items.reduce((C,r)=>C+r.quantity,0)}getSubtotal(){return this.items.reduce((C,r)=>C+(r.fullPrice??r.unitPrice)*r.quantity,0)}getTotal(){return this.items.reduce((C,r)=>C+r.unitPrice*r.quantity,0)}subscribe(C){return this.listeners.add(C),C(this.getState()),()=>{this.listeners.delete(C)}}destroy(){this.listeners.clear()}rehydrate(){if(!this.persist||!this.storage)return;try{let C=this.storage.getItem(this.storageKey);if(!C)return;let r=JSON.parse(C),f=Array.isArray(r)?r:r.items;if(!Array.isArray(f))return;this.items=M(f.map((B)=>this.normalizeItem(B)).filter((B)=>Boolean(B))),this.emit()}catch(C){this.log("Failed to hydrate cart",C)}}addItem(C){let r=this.normalizeItem(C);if(!r)return;let f=this.items.find((B)=>B.id===r.id);if(f){this.setQuantity(f.id,f.quantity+r.quantity);return}this.items=[...this.items,r],this.commit()}removeItem(C){this.items=this.items.filter((r)=>r.id!==C),this.commit()}setQuantity(C,r){let f=Math.floor(r);this.items=f<=0?this.items.filter((B)=>B.id!==C):this.items.map((B)=>B.id===C?{...B,quantity:f}:B),this.commit()}increment(C,r=1){let f=this.items.find((B)=>B.id===C);if(f)this.setQuantity(C,f.quantity+Math.max(1,Math.floor(r)))}decrement(C,r=1){let f=this.items.find((B)=>B.id===C);if(f)this.setQuantity(C,f.quantity-Math.max(1,Math.floor(r)))}clear(){if(this.items=[],this.persist&&this.storage)try{this.storage.removeItem(this.storageKey)}catch(C){this.log("Failed to clear cart storage",C)}this.emit()}async resolveCheckoutUrl(C=this.defaultCheckoutLinkKey){let r=this.checkoutLinks[C];if(!r)throw new w("checkout_link_not_found",`Unknown checkout link key: ${C}`);let f=H(r),B=this.itemsForCheckout(C,r),A={siteKey:this.siteKey,checkoutLinkId:f.checkoutLinkId,mode:r.mode,...f.discountCode?{discountCode:f.discountCode}:{},...B.length>0?{items:B}:{}};return(await this.post("/checkout-url",A)).checkoutUrl}async checkout(C=this.defaultCheckoutLinkKey){let r=await this.resolveCheckoutUrl(C),f=N();if(f)f.location.assign(r);return r}itemsForCheckout(C,r){if(r.mode==="static")return[];let f=this.items.filter((B)=>B.checkoutLinkKey===C).map((B)=>({lineItemId:B.lineItemId,quantity:B.quantity,...B.discount?{discount:B.discount}:{}}));if(f.length===0)throw new w("cart_empty","Your cart is empty.");return f}normalizeItem(C){if(!C.id||!C.lineItemId||typeof C.unitPrice!=="number")return null;let r=typeof C.name==="string"&&C.name.trim().length>0?C.name.trim():"Item";return{...C,id:C.id,lineItemId:C.lineItemId,checkoutLinkKey:C.checkoutLinkKey??this.defaultCheckoutLinkKey,name:r,unitPrice:C.unitPrice,quantity:z(C.quantity)}}async post(C,r){let f=`${this.endpoint}${C}`,B=JSON.stringify(r),A={"Content-Type":"application/json"},T={body:r,bodyText:B,endpoint:this.endpoint,headers:A,path:C,siteKey:this.siteKey,url:f},R=typeof this.requestHeaders==="function"?await this.requestHeaders(T):this.requestHeaders,Y=q(A,R),W=await fetch(f,{method:"POST",headers:Y,body:B}),b=await W.json().catch(()=>null);if(!W.ok){let Z=typeof b?.code==="string"&&b.code.length>0?b.code:"request_failed",_=typeof b?.message==="string"&&b.message.length>0?b.message:"Cart request failed";throw new w(Z,_,W.status)}if(!b||typeof b!=="object"||b.ok!==!0||typeof b.checkoutUrl!=="string"||b.checkoutUrl.length===0)throw new w("request_failed","Cart response was invalid.",W.status);return b}commit(){if(this.items=M(this.items),this.persist&&this.storage)try{let C={version:G,items:this.items};this.storage.setItem(this.storageKey,JSON.stringify(C))}catch(C){this.log("Failed to persist cart",C)}this.emit()}emit(){let C=this.getState();for(let r of this.listeners)r(C)}log(...C){if(this.debug)console.log("[@behindthescenes/cart]",...C)}}function D(C){return I.init(C)}function O(C){return typeof C==="object"&&C!==null&&!Array.isArray(C)}function X(){try{return globalThis.window??null}catch{return null}}function V(C){let r=X();if(!r)return;let[f,B,A]=C;if(typeof f!=="string")return;if(f==="config"){if(!O(B))return;r.btsCart?.destroy?.(),r.btsCart=D(B);return}let T=r.btsCart;if(!T)return;if(f==="add"&&O(B)){T.addItem(B);return}if(f==="remove"&&typeof B==="string"){T.removeItem(B);return}if(f==="quantity"&&typeof B==="string"&&typeof A==="number"){T.setQuantity(B,A);return}if(f==="clear"){T.clear();return}if(f==="checkout")T.checkout(typeof B==="string"?B:void 0)}var S=X();if(S){let C=Array.isArray(S.btsCartDataLayer)?[...S.btsCartDataLayer]:[];S.btsCartDataLayer=Array.isArray(S.btsCartDataLayer)?S.btsCartDataLayer:[],S.BTSCart={BTSCart:I,createBTSCart:D},S.createBTSCart=D;for(let r of C)V(Array.from(r));S.btsCartCommand=(...r)=>{S.btsCartDataLayer?.push(r),V(r)}}export{D as createBTSCart,I as BTSCart};
package/dist/cjs/index.js CHANGED
@@ -1 +1 @@
1
- var{defineProperty:_,getOwnPropertyNames:H,getOwnPropertyDescriptor:b}=Object,q=Object.prototype.hasOwnProperty;function B(F){return this[F]}var I=(F)=>{var G=($??=new WeakMap).get(F),J;if(G)return G;if(G=_({},"__esModule",{value:!0}),F&&typeof F==="object"||typeof F==="function"){for(var M of H(F))if(!q.call(G,M))_(G,M,{get:B.bind(F,M),enumerable:!(J=b(F,M))||J.enumerable})}return $.set(F,G),G},$;var j=(F)=>F;function K(F,G){this[F]=j.bind(null,G)}var T=(F,G)=>{for(var J in G)_(F,J,{get:G[J],enumerable:!0,configurable:!0,set:K.bind(G,J)})};var g={};T(g,{createBTSCart:()=>f,BTSCartError:()=>V,BTSCart:()=>Y});module.exports=I(g);var A="https://api.bts.it.com/v2/website/cart",P="bts-cart",R="default",v=1;class V extends Error{code;status;constructor(F,G,J){super(G);this.name="BTSCartError",this.code=F,this.status=J}}function D(){return typeof window>"u"?null:window}function C(){try{return D()?.localStorage}catch{return}}function S(F){return(F??A).replace(/\/$/,"")}function w(F){if(!Number.isFinite(F??1))return 1;return Math.max(1,Math.floor(F??1))}function z(F){return{items:F.map((G)=>({...G}))}}function x(F,G){let J=new Headers(F);if(!G)return J;return new Headers(G).forEach((M,O)=>J.set(O,M)),J}function L(F){let G=F.checkoutLinkId.trim();try{let J=new URL(G.includes("://")?G:G.startsWith("/")?`https://behindthescenes.com${G}`:G),M=J.pathname.split("/").filter(Boolean),O=M.indexOf("c"),X=O>=0?M[O+1]:M.at(-1);if(X)return{checkoutLinkId:X,discountCode:F.discountCode??J.searchParams.get("discountCode")??void 0}}catch{}return{checkoutLinkId:G,discountCode:F.discountCode}}class Y{siteKey;endpoint;debug;persist;storageKey;storage;checkoutLinks;defaultCheckoutLinkKey;requestHeaders;items=[];listeners=new Set;constructor(F){this.siteKey=F.siteKey,this.endpoint=S(F.endpoint),this.debug=F.debug??!1,this.persist=F.persist??!0,this.storageKey=F.storageKey??P,this.storage=F.storage??C(),this.checkoutLinks=F.checkoutLinks??{},this.defaultCheckoutLinkKey=F.defaultCheckoutLinkKey??Object.keys(this.checkoutLinks)[0]??R,this.requestHeaders=F.requestHeaders,this.rehydrate()}static init(F){return new Y(F)}getItems(){return z(this.items).items}getState(){return z(this.items)}getItemCount(){return this.items.reduce((F,G)=>F+G.quantity,0)}getSubtotal(){return this.items.reduce((F,G)=>F+(G.fullPrice??G.unitPrice)*G.quantity,0)}getTotal(){return this.items.reduce((F,G)=>F+G.unitPrice*G.quantity,0)}subscribe(F){return this.listeners.add(F),F(this.getState()),()=>{this.listeners.delete(F)}}destroy(){this.listeners.clear()}rehydrate(){if(!this.persist||!this.storage)return;try{let F=this.storage.getItem(this.storageKey);if(!F)return;let G=JSON.parse(F),J=Array.isArray(G)?G:G.items;if(!Array.isArray(J))return;this.items=J.map((M)=>this.normalizeItem(M)).filter((M)=>Boolean(M)),this.emit()}catch(F){this.log("Failed to hydrate cart",F)}}addItem(F){let G=this.normalizeItem(F);if(!G)return;let J=this.items.find((M)=>M.id===G.id);if(J){this.setQuantity(J.id,J.quantity+G.quantity);return}this.items=[...this.items,G],this.commit()}removeItem(F){this.items=this.items.filter((G)=>G.id!==F),this.commit()}setQuantity(F,G){let J=Math.floor(G);this.items=J<=0?this.items.filter((M)=>M.id!==F):this.items.map((M)=>M.id===F?{...M,quantity:J}:M),this.commit()}increment(F,G=1){let J=this.items.find((M)=>M.id===F);if(J)this.setQuantity(F,J.quantity+Math.max(1,Math.floor(G)))}decrement(F,G=1){let J=this.items.find((M)=>M.id===F);if(J)this.setQuantity(F,J.quantity-Math.max(1,Math.floor(G)))}clear(){if(this.items=[],this.persist&&this.storage)try{this.storage.removeItem(this.storageKey)}catch(F){this.log("Failed to clear cart storage",F)}this.emit()}async resolveCheckoutUrl(F=this.defaultCheckoutLinkKey){let G=this.checkoutLinks[F];if(!G)throw new V("checkout_link_not_found",`Unknown checkout link key: ${F}`);let J=L(G),M=this.itemsForCheckout(F,G);return(await this.post("/checkout-url",{siteKey:this.siteKey,checkoutLinkId:J.checkoutLinkId,mode:G.mode,discountCode:J.discountCode,items:M})).checkoutUrl}async checkout(F=this.defaultCheckoutLinkKey){let G=await this.resolveCheckoutUrl(F),J=D();if(J)J.location.assign(G);return G}itemsForCheckout(F,G){if(G.mode==="static")return[];let J=this.items.filter((M)=>M.checkoutLinkKey===F).map((M)=>({lineItemId:M.lineItemId,quantity:M.quantity,...M.discount?{discount:M.discount}:{}}));if(J.length===0)throw new V("cart_empty","Your cart is empty.");return J}normalizeItem(F){if(!F.id||!F.lineItemId||!F.name||typeof F.unitPrice!=="number")return null;return{...F,id:F.id,lineItemId:F.lineItemId,checkoutLinkKey:F.checkoutLinkKey??this.defaultCheckoutLinkKey,name:F.name,unitPrice:F.unitPrice,quantity:w(F.quantity)}}async post(F,G){let J=`${this.endpoint}${F}`,M=JSON.stringify(G),O={"Content-Type":"application/json"},X={body:G,bodyText:M,endpoint:this.endpoint,headers:O,path:F,siteKey:this.siteKey,url:J},U=typeof this.requestHeaders==="function"?await this.requestHeaders(X):this.requestHeaders,W=x(O,U),Z=await fetch(J,{method:"POST",headers:W,body:M}),N=await Z.json().catch(()=>null);if(!Z.ok){let Q=typeof N==="object"&&N&&"code"in N?N.code:"request_failed",E=typeof N==="object"&&N&&"message"in N?N.message:"Cart request failed";throw new V(Q??"request_failed",E??"Cart request failed",Z.status)}return N}commit(){if(this.persist&&this.storage)try{let F={version:v,items:this.items};this.storage.setItem(this.storageKey,JSON.stringify(F))}catch(F){this.log("Failed to persist cart",F)}this.emit()}emit(){let F=this.getState();for(let G of this.listeners)G(F)}log(...F){if(this.debug)console.log("[@behindthescenes/cart]",...F)}}function f(F){return Y.init(F)}
1
+ var{defineProperty:Z,getOwnPropertyNames:q,getOwnPropertyDescriptor:B}=Object,I=Object.prototype.hasOwnProperty;function b(A){return this[A]}var T=(A)=>{var F=(_??=new WeakMap).get(A),G;if(F)return F;if(F=Z({},"__esModule",{value:!0}),A&&typeof A==="object"||typeof A==="function"){for(var J of q(A))if(!I.call(F,J))Z(F,J,{get:b.bind(A,J),enumerable:!(G=B(A,J))||G.enumerable})}return _.set(A,F),F},_;var j=(A)=>A;function C(A,F){this[A]=j.bind(null,F)}var K=(A,F)=>{for(var G in F)Z(A,G,{get:F[G],enumerable:!0,configurable:!0,set:C.bind(F,G)})};var k={};K(k,{createBTSCart:()=>h,BTSCartError:()=>O,BTSCart:()=>Y});module.exports=T(k);var $="https://api.bts.it.com/v2/website/cart",R="bts-cart",z="default",W=1;class O extends Error{code;status;constructor(A,F,G){super(F);this.name="BTSCartError",this.code=A,this.status=G}}function U(){try{return globalThis.window??null}catch{return null}}function S(){try{return U()?.localStorage}catch{return}}function w(A){return(A??$).replace(/\/$/,"")}function L(A){if(!Number.isFinite(A??1))return 1;return Math.min(99,Math.max(1,Math.floor(A??1)))}function v(A){return{items:A.map((F)=>({...F}))}}function x(A){if(A.discountEligibleTierOrProductIds?.length)return[...new Set(A.discountEligibleTierOrProductIds.filter(Boolean))];return[...new Set([A.accessTierId,A.productId,A.lineItemId].filter((F)=>Boolean(F)))]}function Q(A){let F=new Set;for(let G of A)for(let J of[G.lineItemId,G.accessTierId,G.productId])if(J)F.add(J);return A.map((G)=>{if(!G.discount)return G;let J=x(G);if(J.length===0||!J.some((M)=>F.has(M))){let M={...G};return delete M.discount,delete M.discountEligibleTierOrProductIds,M}return G})}function f(A,F){let G=new Headers(A);if(!F)return G;return new Headers(F).forEach((J,M)=>G.set(M,J)),G}function g(A){let F=A.checkoutLinkId.trim();try{let G=F.includes("://")?F:F.startsWith("/")?`https://bts-cart.invalid${F}`:F,J=new URL(G),M=J.pathname.split("/").filter(Boolean),P=M.indexOf("c"),V=P>=0?M[P+1]:M.at(-1);if(V)return{checkoutLinkId:V,discountCode:A.discountCode??J.searchParams.get("discountCode")??void 0}}catch{}return{checkoutLinkId:F,discountCode:A.discountCode}}class Y{siteKey;endpoint;debug;persist;storageKey;storage;checkoutLinks;defaultCheckoutLinkKey;requestHeaders;items=[];listeners=new Set;constructor(A){this.siteKey=A.siteKey,this.endpoint=w(A.endpoint),this.debug=A.debug??!1,this.persist=A.persist??!0,this.storageKey=A.storageKey??R,this.storage=A.storage??S(),this.checkoutLinks=A.checkoutLinks??{},this.defaultCheckoutLinkKey=A.defaultCheckoutLinkKey??Object.keys(this.checkoutLinks)[0]??z,this.requestHeaders=A.requestHeaders,this.rehydrate()}static init(A){return new Y(A)}getItems(){return v(this.items).items}getState(){return v(this.items)}getItemCount(){return this.items.reduce((A,F)=>A+F.quantity,0)}getSubtotal(){return this.items.reduce((A,F)=>A+(F.fullPrice??F.unitPrice)*F.quantity,0)}getTotal(){return this.items.reduce((A,F)=>A+F.unitPrice*F.quantity,0)}subscribe(A){return this.listeners.add(A),A(this.getState()),()=>{this.listeners.delete(A)}}destroy(){this.listeners.clear()}rehydrate(){if(!this.persist||!this.storage)return;try{let A=this.storage.getItem(this.storageKey);if(!A)return;let F=JSON.parse(A),G=Array.isArray(F)?F:F.items;if(!Array.isArray(G))return;this.items=Q(G.map((J)=>this.normalizeItem(J)).filter((J)=>Boolean(J))),this.emit()}catch(A){this.log("Failed to hydrate cart",A)}}addItem(A){let F=this.normalizeItem(A);if(!F)return;let G=this.items.find((J)=>J.id===F.id);if(G){this.setQuantity(G.id,G.quantity+F.quantity);return}this.items=[...this.items,F],this.commit()}removeItem(A){this.items=this.items.filter((F)=>F.id!==A),this.commit()}setQuantity(A,F){let G=Math.floor(F);this.items=G<=0?this.items.filter((J)=>J.id!==A):this.items.map((J)=>J.id===A?{...J,quantity:G}:J),this.commit()}increment(A,F=1){let G=this.items.find((J)=>J.id===A);if(G)this.setQuantity(A,G.quantity+Math.max(1,Math.floor(F)))}decrement(A,F=1){let G=this.items.find((J)=>J.id===A);if(G)this.setQuantity(A,G.quantity-Math.max(1,Math.floor(F)))}clear(){if(this.items=[],this.persist&&this.storage)try{this.storage.removeItem(this.storageKey)}catch(A){this.log("Failed to clear cart storage",A)}this.emit()}async resolveCheckoutUrl(A=this.defaultCheckoutLinkKey){let F=this.checkoutLinks[A];if(!F)throw new O("checkout_link_not_found",`Unknown checkout link key: ${A}`);let G=g(F),J=this.itemsForCheckout(A,F),M={siteKey:this.siteKey,checkoutLinkId:G.checkoutLinkId,mode:F.mode,...G.discountCode?{discountCode:G.discountCode}:{},...J.length>0?{items:J}:{}};return(await this.post("/checkout-url",M)).checkoutUrl}async checkout(A=this.defaultCheckoutLinkKey){let F=await this.resolveCheckoutUrl(A),G=U();if(G)G.location.assign(F);return F}itemsForCheckout(A,F){if(F.mode==="static")return[];let G=this.items.filter((J)=>J.checkoutLinkKey===A).map((J)=>({lineItemId:J.lineItemId,quantity:J.quantity,...J.discount?{discount:J.discount}:{}}));if(G.length===0)throw new O("cart_empty","Your cart is empty.");return G}normalizeItem(A){if(!A.id||!A.lineItemId||typeof A.unitPrice!=="number")return null;let F=typeof A.name==="string"&&A.name.trim().length>0?A.name.trim():"Item";return{...A,id:A.id,lineItemId:A.lineItemId,checkoutLinkKey:A.checkoutLinkKey??this.defaultCheckoutLinkKey,name:F,unitPrice:A.unitPrice,quantity:L(A.quantity)}}async post(A,F){let G=`${this.endpoint}${A}`,J=JSON.stringify(F),M={"Content-Type":"application/json"},P={body:F,bodyText:J,endpoint:this.endpoint,headers:M,path:A,siteKey:this.siteKey,url:G},V=typeof this.requestHeaders==="function"?await this.requestHeaders(P):this.requestHeaders,H=f(M,V),X=await fetch(G,{method:"POST",headers:H,body:J}),N=await X.json().catch(()=>null);if(!X.ok){let D=typeof N?.code==="string"&&N.code.length>0?N.code:"request_failed",E=typeof N?.message==="string"&&N.message.length>0?N.message:"Cart request failed";throw new O(D,E,X.status)}if(!N||typeof N!=="object"||N.ok!==!0||typeof N.checkoutUrl!=="string"||N.checkoutUrl.length===0)throw new O("request_failed","Cart response was invalid.",X.status);return N}commit(){if(this.items=Q(this.items),this.persist&&this.storage)try{let A={version:W,items:this.items};this.storage.setItem(this.storageKey,JSON.stringify(A))}catch(A){this.log("Failed to persist cart",A)}this.emit()}emit(){let A=this.getState();for(let F of this.listeners)F(A)}log(...A){if(this.debug)console.log("[@behindthescenes/cart]",...A)}}function h(A){return Y.init(A)}
package/dist/esm/index.js CHANGED
@@ -1 +1 @@
1
- var _="https://api.bts.it.com/v2/website/cart",$="bts-cart",A="default",P=1;class V extends Error{code;status;constructor(F,G,J){super(G);this.name="BTSCartError",this.code=F,this.status=J}}function v(){return typeof window>"u"?null:window}function Q(){try{return v()?.localStorage}catch{return}}function E(F){return(F??_).replace(/\/$/,"")}function H(F){if(!Number.isFinite(F??1))return 1;return Math.max(1,Math.floor(F??1))}function R(F){return{items:F.map((G)=>({...G}))}}function b(F,G){let J=new Headers(F);if(!G)return J;return new Headers(G).forEach((M,O)=>J.set(O,M)),J}function q(F){let G=F.checkoutLinkId.trim();try{let J=new URL(G.includes("://")?G:G.startsWith("/")?`https://behindthescenes.com${G}`:G),M=J.pathname.split("/").filter(Boolean),O=M.indexOf("c"),X=O>=0?M[O+1]:M.at(-1);if(X)return{checkoutLinkId:X,discountCode:F.discountCode??J.searchParams.get("discountCode")??void 0}}catch{}return{checkoutLinkId:G,discountCode:F.discountCode}}class Z{siteKey;endpoint;debug;persist;storageKey;storage;checkoutLinks;defaultCheckoutLinkKey;requestHeaders;items=[];listeners=new Set;constructor(F){this.siteKey=F.siteKey,this.endpoint=E(F.endpoint),this.debug=F.debug??!1,this.persist=F.persist??!0,this.storageKey=F.storageKey??$,this.storage=F.storage??Q(),this.checkoutLinks=F.checkoutLinks??{},this.defaultCheckoutLinkKey=F.defaultCheckoutLinkKey??Object.keys(this.checkoutLinks)[0]??A,this.requestHeaders=F.requestHeaders,this.rehydrate()}static init(F){return new Z(F)}getItems(){return R(this.items).items}getState(){return R(this.items)}getItemCount(){return this.items.reduce((F,G)=>F+G.quantity,0)}getSubtotal(){return this.items.reduce((F,G)=>F+(G.fullPrice??G.unitPrice)*G.quantity,0)}getTotal(){return this.items.reduce((F,G)=>F+G.unitPrice*G.quantity,0)}subscribe(F){return this.listeners.add(F),F(this.getState()),()=>{this.listeners.delete(F)}}destroy(){this.listeners.clear()}rehydrate(){if(!this.persist||!this.storage)return;try{let F=this.storage.getItem(this.storageKey);if(!F)return;let G=JSON.parse(F),J=Array.isArray(G)?G:G.items;if(!Array.isArray(J))return;this.items=J.map((M)=>this.normalizeItem(M)).filter((M)=>Boolean(M)),this.emit()}catch(F){this.log("Failed to hydrate cart",F)}}addItem(F){let G=this.normalizeItem(F);if(!G)return;let J=this.items.find((M)=>M.id===G.id);if(J){this.setQuantity(J.id,J.quantity+G.quantity);return}this.items=[...this.items,G],this.commit()}removeItem(F){this.items=this.items.filter((G)=>G.id!==F),this.commit()}setQuantity(F,G){let J=Math.floor(G);this.items=J<=0?this.items.filter((M)=>M.id!==F):this.items.map((M)=>M.id===F?{...M,quantity:J}:M),this.commit()}increment(F,G=1){let J=this.items.find((M)=>M.id===F);if(J)this.setQuantity(F,J.quantity+Math.max(1,Math.floor(G)))}decrement(F,G=1){let J=this.items.find((M)=>M.id===F);if(J)this.setQuantity(F,J.quantity-Math.max(1,Math.floor(G)))}clear(){if(this.items=[],this.persist&&this.storage)try{this.storage.removeItem(this.storageKey)}catch(F){this.log("Failed to clear cart storage",F)}this.emit()}async resolveCheckoutUrl(F=this.defaultCheckoutLinkKey){let G=this.checkoutLinks[F];if(!G)throw new V("checkout_link_not_found",`Unknown checkout link key: ${F}`);let J=q(G),M=this.itemsForCheckout(F,G);return(await this.post("/checkout-url",{siteKey:this.siteKey,checkoutLinkId:J.checkoutLinkId,mode:G.mode,discountCode:J.discountCode,items:M})).checkoutUrl}async checkout(F=this.defaultCheckoutLinkKey){let G=await this.resolveCheckoutUrl(F),J=v();if(J)J.location.assign(G);return G}itemsForCheckout(F,G){if(G.mode==="static")return[];let J=this.items.filter((M)=>M.checkoutLinkKey===F).map((M)=>({lineItemId:M.lineItemId,quantity:M.quantity,...M.discount?{discount:M.discount}:{}}));if(J.length===0)throw new V("cart_empty","Your cart is empty.");return J}normalizeItem(F){if(!F.id||!F.lineItemId||!F.name||typeof F.unitPrice!=="number")return null;return{...F,id:F.id,lineItemId:F.lineItemId,checkoutLinkKey:F.checkoutLinkKey??this.defaultCheckoutLinkKey,name:F.name,unitPrice:F.unitPrice,quantity:H(F.quantity)}}async post(F,G){let J=`${this.endpoint}${F}`,M=JSON.stringify(G),O={"Content-Type":"application/json"},X={body:G,bodyText:M,endpoint:this.endpoint,headers:O,path:F,siteKey:this.siteKey,url:J},z=typeof this.requestHeaders==="function"?await this.requestHeaders(X):this.requestHeaders,D=b(O,z),Y=await fetch(J,{method:"POST",headers:D,body:M}),N=await Y.json().catch(()=>null);if(!Y.ok){let U=typeof N==="object"&&N&&"code"in N?N.code:"request_failed",W=typeof N==="object"&&N&&"message"in N?N.message:"Cart request failed";throw new V(U??"request_failed",W??"Cart request failed",Y.status)}return N}commit(){if(this.persist&&this.storage)try{let F={version:P,items:this.items};this.storage.setItem(this.storageKey,JSON.stringify(F))}catch(F){this.log("Failed to persist cart",F)}this.emit()}emit(){let F=this.getState();for(let G of this.listeners)G(F)}log(...F){if(this.debug)console.log("[@behindthescenes/cart]",...F)}}function T(F){return Z.init(F)}export{T as createBTSCart,V as BTSCartError,Z as BTSCart};
1
+ var Z="https://api.bts.it.com/v2/website/cart",_="bts-cart",$="default",R=1;class O extends Error{code;status;constructor(A,F,G){super(F);this.name="BTSCartError",this.code=A,this.status=G}}function v(){try{return globalThis.window??null}catch{return null}}function D(){try{return v()?.localStorage}catch{return}}function E(A){return(A??Z).replace(/\/$/,"")}function q(A){if(!Number.isFinite(A??1))return 1;return Math.min(99,Math.max(1,Math.floor(A??1)))}function z(A){return{items:A.map((F)=>({...F}))}}function B(A){if(A.discountEligibleTierOrProductIds?.length)return[...new Set(A.discountEligibleTierOrProductIds.filter(Boolean))];return[...new Set([A.accessTierId,A.productId,A.lineItemId].filter((F)=>Boolean(F)))]}function W(A){let F=new Set;for(let G of A)for(let J of[G.lineItemId,G.accessTierId,G.productId])if(J)F.add(J);return A.map((G)=>{if(!G.discount)return G;let J=B(G);if(J.length===0||!J.some((M)=>F.has(M))){let M={...G};return delete M.discount,delete M.discountEligibleTierOrProductIds,M}return G})}function I(A,F){let G=new Headers(A);if(!F)return G;return new Headers(F).forEach((J,M)=>G.set(M,J)),G}function b(A){let F=A.checkoutLinkId.trim();try{let G=F.includes("://")?F:F.startsWith("/")?`https://bts-cart.invalid${F}`:F,J=new URL(G),M=J.pathname.split("/").filter(Boolean),P=M.indexOf("c"),V=P>=0?M[P+1]:M.at(-1);if(V)return{checkoutLinkId:V,discountCode:A.discountCode??J.searchParams.get("discountCode")??void 0}}catch{}return{checkoutLinkId:F,discountCode:A.discountCode}}class Y{siteKey;endpoint;debug;persist;storageKey;storage;checkoutLinks;defaultCheckoutLinkKey;requestHeaders;items=[];listeners=new Set;constructor(A){this.siteKey=A.siteKey,this.endpoint=E(A.endpoint),this.debug=A.debug??!1,this.persist=A.persist??!0,this.storageKey=A.storageKey??_,this.storage=A.storage??D(),this.checkoutLinks=A.checkoutLinks??{},this.defaultCheckoutLinkKey=A.defaultCheckoutLinkKey??Object.keys(this.checkoutLinks)[0]??$,this.requestHeaders=A.requestHeaders,this.rehydrate()}static init(A){return new Y(A)}getItems(){return z(this.items).items}getState(){return z(this.items)}getItemCount(){return this.items.reduce((A,F)=>A+F.quantity,0)}getSubtotal(){return this.items.reduce((A,F)=>A+(F.fullPrice??F.unitPrice)*F.quantity,0)}getTotal(){return this.items.reduce((A,F)=>A+F.unitPrice*F.quantity,0)}subscribe(A){return this.listeners.add(A),A(this.getState()),()=>{this.listeners.delete(A)}}destroy(){this.listeners.clear()}rehydrate(){if(!this.persist||!this.storage)return;try{let A=this.storage.getItem(this.storageKey);if(!A)return;let F=JSON.parse(A),G=Array.isArray(F)?F:F.items;if(!Array.isArray(G))return;this.items=W(G.map((J)=>this.normalizeItem(J)).filter((J)=>Boolean(J))),this.emit()}catch(A){this.log("Failed to hydrate cart",A)}}addItem(A){let F=this.normalizeItem(A);if(!F)return;let G=this.items.find((J)=>J.id===F.id);if(G){this.setQuantity(G.id,G.quantity+F.quantity);return}this.items=[...this.items,F],this.commit()}removeItem(A){this.items=this.items.filter((F)=>F.id!==A),this.commit()}setQuantity(A,F){let G=Math.floor(F);this.items=G<=0?this.items.filter((J)=>J.id!==A):this.items.map((J)=>J.id===A?{...J,quantity:G}:J),this.commit()}increment(A,F=1){let G=this.items.find((J)=>J.id===A);if(G)this.setQuantity(A,G.quantity+Math.max(1,Math.floor(F)))}decrement(A,F=1){let G=this.items.find((J)=>J.id===A);if(G)this.setQuantity(A,G.quantity-Math.max(1,Math.floor(F)))}clear(){if(this.items=[],this.persist&&this.storage)try{this.storage.removeItem(this.storageKey)}catch(A){this.log("Failed to clear cart storage",A)}this.emit()}async resolveCheckoutUrl(A=this.defaultCheckoutLinkKey){let F=this.checkoutLinks[A];if(!F)throw new O("checkout_link_not_found",`Unknown checkout link key: ${A}`);let G=b(F),J=this.itemsForCheckout(A,F),M={siteKey:this.siteKey,checkoutLinkId:G.checkoutLinkId,mode:F.mode,...G.discountCode?{discountCode:G.discountCode}:{},...J.length>0?{items:J}:{}};return(await this.post("/checkout-url",M)).checkoutUrl}async checkout(A=this.defaultCheckoutLinkKey){let F=await this.resolveCheckoutUrl(A),G=v();if(G)G.location.assign(F);return F}itemsForCheckout(A,F){if(F.mode==="static")return[];let G=this.items.filter((J)=>J.checkoutLinkKey===A).map((J)=>({lineItemId:J.lineItemId,quantity:J.quantity,...J.discount?{discount:J.discount}:{}}));if(G.length===0)throw new O("cart_empty","Your cart is empty.");return G}normalizeItem(A){if(!A.id||!A.lineItemId||typeof A.unitPrice!=="number")return null;let F=typeof A.name==="string"&&A.name.trim().length>0?A.name.trim():"Item";return{...A,id:A.id,lineItemId:A.lineItemId,checkoutLinkKey:A.checkoutLinkKey??this.defaultCheckoutLinkKey,name:F,unitPrice:A.unitPrice,quantity:q(A.quantity)}}async post(A,F){let G=`${this.endpoint}${A}`,J=JSON.stringify(F),M={"Content-Type":"application/json"},P={body:F,bodyText:J,endpoint:this.endpoint,headers:M,path:A,siteKey:this.siteKey,url:G},V=typeof this.requestHeaders==="function"?await this.requestHeaders(P):this.requestHeaders,Q=I(M,V),X=await fetch(G,{method:"POST",headers:Q,body:J}),N=await X.json().catch(()=>null);if(!X.ok){let U=typeof N?.code==="string"&&N.code.length>0?N.code:"request_failed",H=typeof N?.message==="string"&&N.message.length>0?N.message:"Cart request failed";throw new O(U,H,X.status)}if(!N||typeof N!=="object"||N.ok!==!0||typeof N.checkoutUrl!=="string"||N.checkoutUrl.length===0)throw new O("request_failed","Cart response was invalid.",X.status);return N}commit(){if(this.items=W(this.items),this.persist&&this.storage)try{let A={version:R,items:this.items};this.storage.setItem(this.storageKey,JSON.stringify(A))}catch(A){this.log("Failed to persist cart",A)}this.emit()}emit(){let A=this.getState();for(let F of this.listeners)F(A)}log(...A){if(this.debug)console.log("[@behindthescenes/cart]",...A)}}function S(A){return Y.init(A)}export{S as createBTSCart,O as BTSCartError,Y as BTSCart};
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@behindthescenes/cart",
3
- "version": "0.0.3",
4
- "generatedAt": "2026-05-14T11:25:11.511Z",
3
+ "version": "0.0.8",
4
+ "generatedAt": "2026-05-15T10:30:41.615Z",
5
5
  "artifacts": {
6
6
  "browser": "browser/browser.js",
7
7
  "cjs": "cjs/index.js",
@@ -11,17 +11,22 @@ export type BTSCartRequestContext = {
11
11
  };
12
12
  export type BTSCartRequestHeaders = HeadersInit | ((request: BTSCartRequestContext) => HeadersInit | Promise<HeadersInit>);
13
13
  export type BTSCartCheckoutLinkConfig = {
14
- /** Raw checkout link ID, or a full BTS checkout URL like `https://behindthescenes.com/c/<id>?discountCode=CODE`. */
14
+ /** Checkout link UUID, or a full checkout URL (`https://…/c/<uuid>?discountCode=…`). Relative `/c/<uuid>` is supported. */
15
15
  checkoutLinkId: string;
16
16
  mode: BTSCartMode;
17
17
  discountCode?: string;
18
18
  };
19
19
  export type BTSCartItemInput = {
20
20
  id: string;
21
+ /**
22
+ * Sent as `lineItemId` on `POST /v2/website/cart/checkout-url`. Must match a resolved line on the
23
+ * link: the row’s `id`, or its `product_id` / `access_tier_id` (see `resolveDynamicCheckoutCartLineItem` in `@bts/common`).
24
+ */
21
25
  lineItemId: string;
22
26
  checkoutLinkKey?: string;
23
27
  productId?: string;
24
28
  accessTierId?: string;
29
+ /** Display only in the SDK; defaults to `"Item"` if empty. Not sent to the cart API. */
25
30
  name: string;
26
31
  unitPrice: number;
27
32
  fullPrice?: number;
@@ -29,6 +34,12 @@ export type BTSCartItemInput = {
29
34
  quantity?: number;
30
35
  /** Internal promo-code ID or customer-facing promo code. Customer codes are resolved by the BTS cart API. */
31
36
  discount?: string;
37
+ /**
38
+ * Catalog identifiers (access tier id and/or product id) this promo applies to. Used when cart
39
+ * lines change so discounts can be cleared if no remaining line is still eligible. Defaults to
40
+ * {@link accessTierId}, {@link productId}, and {@link lineItemId} when omitted.
41
+ */
42
+ discountEligibleTierOrProductIds?: string[];
32
43
  };
33
44
  export type BTSCartItem = Required<Pick<BTSCartItemInput, "id" | "lineItemId" | "name" | "unitPrice" | "quantity">> & Omit<BTSCartItemInput, "quantity"> & {
34
45
  checkoutLinkKey: string;
@@ -67,7 +78,7 @@ export type BTSCartCheckoutUrlResult = {
67
78
  mode: BTSCartMode;
68
79
  items: BTSCartCheckoutItemPayload[];
69
80
  };
70
- export type BTSCartErrorCode = "unknown_site" | "domain_not_verified" | "analytics_disabled" | "checkout_link_not_found" | "checkout_link_inactive" | "checkout_link_expired" | "checkout_link_sold_out" | "checkout_link_mode_mismatch" | "cart_empty" | "line_item_not_found" | "invalid_quantity" | "line_item_sold_out" | "product_unavailable" | "discount_invalid" | "request_failed";
81
+ export type BTSCartErrorCode = "unknown_site" | "domain_not_verified" | "analytics_disabled" | "checkout_link_not_found" | "checkout_link_inactive" | "checkout_link_expired" | "checkout_link_sold_out" | "checkout_link_mode_mismatch" | "cart_empty" | "line_item_not_found" | "invalid_quantity" | "line_item_sold_out" | "quantity_exceeded" | "product_unavailable" | "discount_invalid" | "request_failed";
71
82
  export declare class BTSCartError extends Error {
72
83
  code: BTSCartErrorCode;
73
84
  status?: number;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@behindthescenes/cart",
3
- "version": "0.0.3",
3
+ "version": "0.0.8",
4
4
  "description": "Browser cart SDK for BTS external-site checkout links.",
5
5
  "license": "UNLICENSED",
6
6
  "type": "module",