@shopkit/cart 0.1.0 → 0.1.1

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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,16 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.1.1
4
+
5
+ ### Patch Changes
6
+
7
+ - fix(cart): rename cartId to cartToken and fix variant_id handling
8
+ - Renamed `cartId` to `cartToken` throughout cart package for clarity
9
+ - Added support for snake_case fields (`variant_id`, `product_id`) from API responses
10
+ - Fixed variant_id extraction in removeItem and updateQuantity operations
11
+ - Updated cart storage methods: getCartId → getCartToken, setCartId → setCartToken
12
+ - Fixed @shopkit/discounts to use cartToken instead of cartId
13
+
3
14
  All notable changes to this project will be documented in this file.
4
15
 
5
16
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
package/dist/index.d.mts CHANGED
@@ -27,10 +27,14 @@ import * as zustand from 'zustand';
27
27
  interface CartItem {
28
28
  /** Unique identifier for the cart line item */
29
29
  id: string;
30
- /** Product identifier */
30
+ /** Product identifier (camelCase) */
31
31
  productId: string;
32
- /** Optional variant identifier */
32
+ /** Optional variant identifier (camelCase) */
33
33
  variantId?: string;
34
+ /** Product identifier (snake_case - from API response) */
35
+ product_id?: string;
36
+ /** Variant identifier (snake_case - from API response) */
37
+ variant_id?: string;
34
38
  /** Product title */
35
39
  title: string;
36
40
  /** Current price of the item */
@@ -69,6 +73,8 @@ interface CartItemRequest {
69
73
  interface CartResponse {
70
74
  /** Cart identifier */
71
75
  id: string;
76
+ /** Cart token for API authentication (used in X-Cart-Token header) */
77
+ token?: string;
72
78
  /** Items currently in the cart */
73
79
  items: CartItem[];
74
80
  /** Subtotal before discounts */
@@ -140,16 +146,16 @@ interface ICartService {
140
146
  }
141
147
  /**
142
148
  * Interface for cart storage implementations.
143
- * Implement this interface to customize how cart IDs are persisted.
149
+ * Implement this interface to customize how cart tokens are persisted.
144
150
  *
145
151
  * @example
146
152
  * ```typescript
147
153
  * class CookieCartStorage implements ICartStorage {
148
- * getCartId(): string | null {
149
- * return getCookie('cart-id');
154
+ * getCartToken(): string | null {
155
+ * return getCookie('cart-token');
150
156
  * }
151
- * setCartId(cartId: string): void {
152
- * setCookie('cart-id', cartId);
157
+ * setCartToken(cartToken: string): void {
158
+ * setCookie('cart-token', cartToken);
153
159
  * }
154
160
  * // ... other methods
155
161
  * }
@@ -157,19 +163,19 @@ interface ICartService {
157
163
  */
158
164
  interface ICartStorage {
159
165
  /**
160
- * Get the stored cart ID.
161
- * @returns The cart ID or null if not found
166
+ * Get the stored cart token.
167
+ * @returns The cart token or null if not found
162
168
  */
163
- getCartId(): string | null;
169
+ getCartToken(): string | null;
164
170
  /**
165
- * Store the cart ID.
166
- * @param cartId - The cart ID to store
171
+ * Store the cart token.
172
+ * @param cartToken - The cart token to store
167
173
  */
168
- setCartId(cartId: string): void;
174
+ setCartToken(cartToken: string): void;
169
175
  /**
170
- * Remove the stored cart ID.
176
+ * Remove the stored cart token.
171
177
  */
172
- removeCartId(): void;
178
+ removeCartToken(): void;
173
179
  /**
174
180
  * Clear all cart-related storage.
175
181
  */
@@ -209,7 +215,7 @@ interface CartConfig {
209
215
  */
210
216
  type CartEvent = {
211
217
  type: "CART_INITIALIZED";
212
- cartId: string;
218
+ cartToken: string;
213
219
  } | {
214
220
  type: "ADD_TO_CART";
215
221
  item: CartItem;
@@ -245,8 +251,8 @@ interface CartState {
245
251
  isLoading: boolean;
246
252
  /** Current error message, if any */
247
253
  error: string | null;
248
- /** Current cart identifier */
249
- cartId: string | null;
254
+ /** Cart token for API authentication (used in X-Cart-Token header) */
255
+ cartToken: string | null;
250
256
  /** URL to proceed to checkout */
251
257
  checkoutUrl?: string;
252
258
  /** Server-calculated total amount */
@@ -390,7 +396,7 @@ declare const useCartToggle: () => () => Promise<void>;
390
396
  /**
391
397
  * Hook to get cart status information.
392
398
  *
393
- * @returns Object with isOpen, isLoading, error, cartId, and isInitialized
399
+ * @returns Object with isOpen, isLoading, error, cartToken, and isInitialized
394
400
  *
395
401
  * @example
396
402
  * ```tsx
@@ -406,7 +412,7 @@ declare const useCartStatus: () => {
406
412
  isOpen: boolean;
407
413
  isLoading: boolean;
408
414
  error: string | null;
409
- cartId: string | null;
415
+ cartToken: string | null;
410
416
  isInitialized: boolean;
411
417
  };
412
418
  /**
@@ -537,9 +543,9 @@ declare class DefaultCartStorage implements ICartStorage {
537
543
  private get;
538
544
  private set;
539
545
  private remove;
540
- getCartId(): string | null;
541
- setCartId(cartId: string): void;
542
- removeCartId(): void;
546
+ getCartToken(): string | null;
547
+ setCartToken(cartToken: string): void;
548
+ removeCartToken(): void;
543
549
  clear(): void;
544
550
  }
545
551
 
package/dist/index.d.ts CHANGED
@@ -27,10 +27,14 @@ import * as zustand from 'zustand';
27
27
  interface CartItem {
28
28
  /** Unique identifier for the cart line item */
29
29
  id: string;
30
- /** Product identifier */
30
+ /** Product identifier (camelCase) */
31
31
  productId: string;
32
- /** Optional variant identifier */
32
+ /** Optional variant identifier (camelCase) */
33
33
  variantId?: string;
34
+ /** Product identifier (snake_case - from API response) */
35
+ product_id?: string;
36
+ /** Variant identifier (snake_case - from API response) */
37
+ variant_id?: string;
34
38
  /** Product title */
35
39
  title: string;
36
40
  /** Current price of the item */
@@ -69,6 +73,8 @@ interface CartItemRequest {
69
73
  interface CartResponse {
70
74
  /** Cart identifier */
71
75
  id: string;
76
+ /** Cart token for API authentication (used in X-Cart-Token header) */
77
+ token?: string;
72
78
  /** Items currently in the cart */
73
79
  items: CartItem[];
74
80
  /** Subtotal before discounts */
@@ -140,16 +146,16 @@ interface ICartService {
140
146
  }
141
147
  /**
142
148
  * Interface for cart storage implementations.
143
- * Implement this interface to customize how cart IDs are persisted.
149
+ * Implement this interface to customize how cart tokens are persisted.
144
150
  *
145
151
  * @example
146
152
  * ```typescript
147
153
  * class CookieCartStorage implements ICartStorage {
148
- * getCartId(): string | null {
149
- * return getCookie('cart-id');
154
+ * getCartToken(): string | null {
155
+ * return getCookie('cart-token');
150
156
  * }
151
- * setCartId(cartId: string): void {
152
- * setCookie('cart-id', cartId);
157
+ * setCartToken(cartToken: string): void {
158
+ * setCookie('cart-token', cartToken);
153
159
  * }
154
160
  * // ... other methods
155
161
  * }
@@ -157,19 +163,19 @@ interface ICartService {
157
163
  */
158
164
  interface ICartStorage {
159
165
  /**
160
- * Get the stored cart ID.
161
- * @returns The cart ID or null if not found
166
+ * Get the stored cart token.
167
+ * @returns The cart token or null if not found
162
168
  */
163
- getCartId(): string | null;
169
+ getCartToken(): string | null;
164
170
  /**
165
- * Store the cart ID.
166
- * @param cartId - The cart ID to store
171
+ * Store the cart token.
172
+ * @param cartToken - The cart token to store
167
173
  */
168
- setCartId(cartId: string): void;
174
+ setCartToken(cartToken: string): void;
169
175
  /**
170
- * Remove the stored cart ID.
176
+ * Remove the stored cart token.
171
177
  */
172
- removeCartId(): void;
178
+ removeCartToken(): void;
173
179
  /**
174
180
  * Clear all cart-related storage.
175
181
  */
@@ -209,7 +215,7 @@ interface CartConfig {
209
215
  */
210
216
  type CartEvent = {
211
217
  type: "CART_INITIALIZED";
212
- cartId: string;
218
+ cartToken: string;
213
219
  } | {
214
220
  type: "ADD_TO_CART";
215
221
  item: CartItem;
@@ -245,8 +251,8 @@ interface CartState {
245
251
  isLoading: boolean;
246
252
  /** Current error message, if any */
247
253
  error: string | null;
248
- /** Current cart identifier */
249
- cartId: string | null;
254
+ /** Cart token for API authentication (used in X-Cart-Token header) */
255
+ cartToken: string | null;
250
256
  /** URL to proceed to checkout */
251
257
  checkoutUrl?: string;
252
258
  /** Server-calculated total amount */
@@ -390,7 +396,7 @@ declare const useCartToggle: () => () => Promise<void>;
390
396
  /**
391
397
  * Hook to get cart status information.
392
398
  *
393
- * @returns Object with isOpen, isLoading, error, cartId, and isInitialized
399
+ * @returns Object with isOpen, isLoading, error, cartToken, and isInitialized
394
400
  *
395
401
  * @example
396
402
  * ```tsx
@@ -406,7 +412,7 @@ declare const useCartStatus: () => {
406
412
  isOpen: boolean;
407
413
  isLoading: boolean;
408
414
  error: string | null;
409
- cartId: string | null;
415
+ cartToken: string | null;
410
416
  isInitialized: boolean;
411
417
  };
412
418
  /**
@@ -537,9 +543,9 @@ declare class DefaultCartStorage implements ICartStorage {
537
543
  private get;
538
544
  private set;
539
545
  private remove;
540
- getCartId(): string | null;
541
- setCartId(cartId: string): void;
542
- removeCartId(): void;
546
+ getCartToken(): string | null;
547
+ setCartToken(cartToken: string): void;
548
+ removeCartToken(): void;
543
549
  clear(): void;
544
550
  }
545
551
 
package/dist/index.js CHANGED
@@ -1,2 +1,2 @@
1
1
  "use client";
2
- 'use strict';var zustand=require('zustand'),react=require('react');var C=null;function b(t){C=t;}function l(){if(!C)throw new Error("Cart not configured. Call configureCart() first.");return C}function E(){return C!==null}var u=zustand.create((t,e)=>({items:[],isOpen:false,isLoading:false,error:null,cartId:null,totalAmount:void 0,isInitialized:false,initializeCart:async r=>{let{isInitialized:a}=e();if(a)return;let i=l(),{cartService:n,cartStorage:s}=i,c=r??i.merchantName;try{let d=s.getCartId();if(d)try{let o=await n.getCart(d);t({items:o.items||[],cartId:d,checkoutUrl:o.checkoutUrl,totalAmount:o.totalAmount,isInitialized:!0}),i.onCartEvent?.({type:"CART_INITIALIZED",cartId:d});}catch{s.removeCartId();let o=await n.createCart(c);s.setCartId(o.token),t({items:o.items||[],cartId:o.token,checkoutUrl:o.checkoutUrl,totalAmount:o.totalAmount,isInitialized:!0}),i.onCartEvent?.({type:"CART_INITIALIZED",cartId:o.token});}else {let o=await n.createCart(c);s.setCartId(o.token),t({items:o.items||[],cartId:o.token,checkoutUrl:o.checkoutUrl,totalAmount:o.totalAmount,isInitialized:!0}),i.onCartEvent?.({type:"CART_INITIALIZED",cartId:o.token});}}catch{t({error:"Failed to initialize cart",isInitialized:true}),i.onCartEvent?.({type:"CART_ERROR",action:"initializeCart",error:"Failed to initialize cart"});}},addItem:async r=>{let{cartId:a}=e();if(!a)return;let{cartService:i,onCartEvent:n}=l();try{t({isLoading:!0,error:null});let s=[{productId:r.productId||r.id,variantId:r.variantId||r.id,quantity:r.quantity}];await i.addToCart(a,s);let c=await i.getCart(a);t({items:c.items||[],checkoutUrl:c.checkoutUrl,totalAmount:c.totalAmount,isLoading:!1}),n?.({type:"ADD_TO_CART",item:r});}catch{t({error:"Failed to add item",isLoading:false}),n?.({type:"CART_ERROR",action:"addItem",error:"Failed to add item"});}},addItems:async r=>{let{cartId:a}=e();if(!a)return;let{cartService:i,onCartEvent:n}=l();try{t({isLoading:!0,error:null}),await i.addToCart(a,r);let s=await i.getCart(a);t({items:s.items||[],checkoutUrl:s.checkoutUrl,totalAmount:s.totalAmount,isLoading:!1});}catch{t({error:"Failed to add items",isLoading:false}),n?.({type:"CART_ERROR",action:"addItems",error:"Failed to add items"});}},removeItem:async(r,a)=>{let{cartId:i,items:n}=e();if(!i)return;let{cartService:s,onCartEvent:c}=l();try{t({isLoading:!0,error:null});let d=n.find(p=>a?p.id===r||p.productId===r&&p.variantId===a:p.id===r);if(!d){t({isLoading:!1});return}await s.removeFromCart(i,[d.id]);let o=await s.getCart(i);t({items:o.items||[],checkoutUrl:o.checkoutUrl,totalAmount:o.totalAmount,isLoading:!1}),c?.({type:"REMOVE_FROM_CART",item:d});}catch{t({error:"Failed to remove item",isLoading:false}),c?.({type:"CART_ERROR",action:"removeItem",error:"Failed to remove item"});}},updateItemQuantity:async(r,a,i)=>{let{cartId:n,items:s}=e();if(!n)return;if(a<=0)return e().removeItem(r,i);let{cartService:c,onCartEvent:d}=l();try{t({isLoading:!0,error:null});let o=s.find(m=>i?m.id===r||m.productId===r&&m.variantId===i:m.id===r);if(!o){t({isLoading:!1});return}let p=o.quantity,L=s.map(m=>({productId:m.productId,variantId:m.variantId||"",quantity:m.id===o.id?a:m.quantity}));await c.updateCart(n,L);let h=await c.getCart(n);t({items:h.items||[],checkoutUrl:h.checkoutUrl,totalAmount:h.totalAmount,isLoading:!1}),d?.({type:"UPDATE_QUANTITY",item:{...o,quantity:a},previousQuantity:p});}catch{t({error:"Failed to update quantity",isLoading:false}),d?.({type:"CART_ERROR",action:"updateItemQuantity",error:"Failed to update quantity"});}},clearCart:async()=>{let{cartId:r,items:a}=e();if(!r)return;let{cartService:i,onCartEvent:n}=l();try{t({isLoading:!0,error:null}),await i.updateCart(r,[]);let s=a.length;t({items:[],isLoading:!1}),n?.({type:"CLEAR_CART",itemCount:s});}catch{t({error:"Failed to clear cart",isLoading:false}),n?.({type:"CART_ERROR",action:"clearCart",error:"Failed to clear cart"});}},openCart:async()=>{let{cartId:r}=e(),{cartService:a,onCartEvent:i}=l();if(r)try{let n=await a.getCart(r);t({items:n.items||[],checkoutUrl:n.checkoutUrl,totalAmount:n.totalAmount,isOpen:!0});}catch{t({isOpen:true});}else t({isOpen:true});i?.({type:"CART_OPENED"});},closeCart:()=>{t({isOpen:false}),l().onCartEvent?.({type:"CART_CLOSED"});},toggleCart:async()=>{let{isOpen:r}=e();r?e().closeCart():await e().openCart();}}));var O=()=>u(t=>t.isInitialized?t.items.length:0),D=()=>u(t=>t.isInitialized?t.items.reduce((e,r)=>e+r.quantity,0):0),U=()=>u(t=>t.items),k=()=>u(t=>t.toggleCart),q=()=>u(t=>({isOpen:t.isOpen,isLoading:t.isLoading,error:t.error,cartId:t.cartId,isInitialized:t.isInitialized})),F=()=>u(t=>t.items.reduce((e,r)=>e+r.price.amount*r.quantity,0)),z=()=>u(t=>t.totalAmount),_=(t,e)=>u(r=>r.items.some(a=>e?a.productId===t&&a.variantId===e:a.productId===t)),N=(t,e)=>u(r=>r.items.find(a=>e?a.productId===t&&a.variantId===e:a.productId===t)),$=()=>u(t=>t.checkoutUrl);function f(t,e,r,a="USD"){let i=e?t.variants?.find(o=>o.id===e)||t.variants?.[0]:t.variants?.find(o=>o.available)||t.variants?.[0],n=i?.price||t.price,s=typeof n=="number"?{amount:n,currencyCode:a}:n,c=i?.compareAtPrice||t.compareAtPrice,d=c?typeof c=="number"?{amount:c,currencyCode:a}:c:void 0;return {id:`${t.id}-${e||i?.id||"default"}`,productId:t.id,variantId:e||i?.id,title:t.title,price:s,compareAtPrice:d,quantity:r||t.quantity||1,image:t.images?.[0]?.url||t.image,vendor:t.vendor}}function v(t){let[e,r]=react.useState({}),{addItem:a}=u();return {addToCart:async(s,c,d)=>{if(s){r(o=>({...o,[s.id]:true}));try{let o=f(s,c,d,t?.defaultCurrencyCode);await a(o),t?.onSuccess?.(o);}catch(o){let p=o instanceof Error?o:new Error("Failed to add product to cart");t?.onError?.(p);}finally{r(o=>({...o,[s.id]:false}));}}},loadingStates:e,isLoading:s=>e[s]??false}}var y=class{constructor(e="/api/v1/cart"){this.baseUrl=e;}async makeRequest(e,r){return fetch(e,{headers:{"Content-Type":"application/json",...r.headers},...r})}async handleResponse(e,r){if(!e.ok){let a=await e.text();throw new Error(`${r}: ${e.status} ${a}`)}return e.json()}async createCart(e){let r=await this.makeRequest(this.baseUrl,{method:"POST",body:JSON.stringify({merchantName:e})});return this.handleResponse(r,"Failed to create cart")}async getCart(e){let r=await this.makeRequest(`${this.baseUrl}/get`,{method:"POST",body:JSON.stringify({cartId:e})});return this.handleResponse(r,"Failed to get cart")}async addToCart(e,r){let a=await this.makeRequest(`${this.baseUrl}/add`,{method:"POST",body:JSON.stringify({cartId:e,items:r})});await this.handleResponse(a,"Failed to add item to cart");}async removeFromCart(e,r){let a=await this.makeRequest(`${this.baseUrl}/remove`,{method:"POST",body:JSON.stringify({cartId:e,itemIds:r})});await this.handleResponse(a,"Failed to remove item from cart");}async updateCart(e,r){let a=await this.makeRequest(`${this.baseUrl}/update`,{method:"POST",body:JSON.stringify({cartId:e,items:r})});await this.handleResponse(a,"Failed to update cart");}};var g=class{constructor(e="default"){this.merchantName=e;}getStorageKey(e){return `${this.merchantName?`merchant_${this.merchantName}`:"default"}_${e}`}get(e){if(typeof window>"u")return null;try{return localStorage.getItem(this.getStorageKey(e))}catch{return null}}set(e,r){if(!(typeof window>"u"))try{localStorage.setItem(this.getStorageKey(e),r);}catch{}}remove(e){if(!(typeof window>"u"))try{localStorage.removeItem(this.getStorageKey(e));}catch{}}getCartId(){return this.get("cartId")}setCartId(e){this.set("cartId",e);}removeCartId(){this.remove("cartId");}clear(){if(!(typeof window>"u"))try{let e=Object.keys(localStorage),r=this.getStorageKey("");e.forEach(a=>{a.startsWith(r)&&localStorage.removeItem(a);});}catch{}}};var I=class{constructor(e){this.client=e;}unwrapResponse(e){if(!e.success)throw new Error(e.message||"Cart operation failed");if(e.data===null)throw new Error("Cart data is null");return e.data}async createCart(e){let r=await this.client.createCart({merchantName:e});return this.unwrapResponse(r)}async getCart(e){let r=await this.client.getCart({id:e});return this.unwrapResponse(r)}async addToCart(e,r){let a=await this.client.addToCart({id:e,items:r});this.unwrapResponse(a);}async removeFromCart(e,r){let a=await this.client.removeFromCart({id:e,itemIds:r});this.unwrapResponse(a);}async updateCart(e,r){let a=await this.client.updateCart({id:e,items:r});this.unwrapResponse(a);}};function R(t){return t&&typeof t.getCart=="function"&&typeof t.createCart=="function"&&typeof t.addToCart=="function"}function T(t,e="USD",r){try{return new Intl.NumberFormat(r,{style:"currency",currency:e}).format(t)}catch{return `${e} ${t.toFixed(2)}`}}function A(t){return t.reduce((e,r)=>{let a=typeof r.price=="number"?r.price:r.price.amount;return e+a*r.quantity},0)}function S(t){return t.reduce((e,r)=>{if(!r.compareAtPrice)return e;let a=typeof r.price=="number"?r.price:r.price.amount,n=(typeof r.compareAtPrice=="number"?r.compareAtPrice:r.compareAtPrice.amount)-a;return e+Math.max(0,n)*r.quantity},0)}function x({merchantName:t}){let e=u(a=>a.initializeCart),r=u(a=>a.isOpen);return react.useEffect(()=>{e(t);},[t,e]),react.useEffect(()=>(document.body.style.overflow=r?"hidden":"",()=>{document.body.style.overflow="";}),[r]),null}exports.CartInitializer=x;exports.DataLayerCartService=I;exports.DefaultCartService=y;exports.DefaultCartStorage=g;exports.calculateCartTotal=A;exports.calculateSavings=S;exports.configureCart=b;exports.formatPrice=T;exports.getCartConfig=l;exports.isCartConfigured=E;exports.isDataLayerClient=R;exports.mapProductToCartItem=f;exports.useAddToCart=v;exports.useCartCount=O;exports.useCartItem=N;exports.useCartItems=U;exports.useCartStatus=q;exports.useCartStore=u;exports.useCartToggle=k;exports.useCartTotal=F;exports.useCartTotalAmount=z;exports.useCartTotalQuantity=D;exports.useCheckoutUrl=$;exports.useIsInCart=_;
2
+ 'use strict';var zustand=require('zustand'),react=require('react');var y=null;function E(t){y=t;}function p(){if(!y)throw new Error("Cart not configured. Call configureCart() first.");return y}function O(){return y!==null}var d=zustand.create((t,e)=>({items:[],isOpen:false,isLoading:false,error:null,cartToken:null,totalAmount:void 0,isInitialized:false,initializeCart:async r=>{let{isInitialized:a}=e();if(a)return;let n=p(),{cartService:i,cartStorage:s}=n,c=r??n.merchantName;try{let u=s.getCartToken();if(u)try{let o=await i.getCart(u);t({items:o.items||[],cartToken:u,checkoutUrl:o.checkoutUrl,totalAmount:o.totalAmount,isInitialized:!0}),n.onCartEvent?.({type:"CART_INITIALIZED",cartToken:u});}catch{s.removeCartToken();let o=await i.createCart(c),l=o.token||o.id;s.setCartToken(l),t({items:o.items||[],cartToken:l,checkoutUrl:o.checkoutUrl,totalAmount:o.totalAmount,isInitialized:!0}),n.onCartEvent?.({type:"CART_INITIALIZED",cartToken:l});}else {let o=await i.createCart(c),l=o.token||o.id;s.setCartToken(l),t({items:o.items||[],cartToken:l,checkoutUrl:o.checkoutUrl,totalAmount:o.totalAmount,isInitialized:!0}),n.onCartEvent?.({type:"CART_INITIALIZED",cartToken:l});}}catch{t({error:"Failed to initialize cart",isInitialized:true}),n.onCartEvent?.({type:"CART_ERROR",action:"initializeCart",error:"Failed to initialize cart"});}},addItem:async r=>{let{cartToken:a}=e();if(!a)return;let{cartService:n,onCartEvent:i}=p();try{t({isLoading:!0,error:null});let s=[{productId:r.productId||r.id,variantId:r.variantId||r.id,quantity:r.quantity}];await n.addToCart(a,s);let c=await n.getCart(a);t({items:c.items||[],checkoutUrl:c.checkoutUrl,totalAmount:c.totalAmount,isLoading:!1}),i?.({type:"ADD_TO_CART",item:r});}catch{t({error:"Failed to add item",isLoading:false}),i?.({type:"CART_ERROR",action:"addItem",error:"Failed to add item"});}},addItems:async r=>{let{cartToken:a}=e();if(!a)return;let{cartService:n,onCartEvent:i}=p();try{t({isLoading:!0,error:null}),await n.addToCart(a,r);let s=await n.getCart(a);t({items:s.items||[],checkoutUrl:s.checkoutUrl,totalAmount:s.totalAmount,isLoading:!1});}catch{t({error:"Failed to add items",isLoading:false}),i?.({type:"CART_ERROR",action:"addItems",error:"Failed to add items"});}},removeItem:async(r,a)=>{let{cartToken:n,items:i}=e();if(!n)return;let{cartService:s,onCartEvent:c}=p();try{t({isLoading:!0,error:null});let u=i.find(C=>{let f=C.variant_id||C.variantId,m=C.product_id||C.productId;return a?C.id===r||m===r&&f===a:C.id===r});if(!u){t({isLoading:!1});return}let o=u.variant_id||u.variantId||u.id;await s.removeFromCart(n,[o]);let l=await s.getCart(n);t({items:l.items||[],checkoutUrl:l.checkoutUrl,totalAmount:l.totalAmount,isLoading:!1}),c?.({type:"REMOVE_FROM_CART",item:u});}catch{t({error:"Failed to remove item",isLoading:false}),c?.({type:"CART_ERROR",action:"removeItem",error:"Failed to remove item"});}},updateItemQuantity:async(r,a,n)=>{let{cartToken:i,items:s}=e();if(!i)return;if(a<=0)return e().removeItem(r,n);let{cartService:c,onCartEvent:u}=p();try{t({isLoading:!0,error:null});let o=s.find(m=>{let P=m.variant_id||m.variantId,L=m.product_id||m.productId;return n?m.id===r||L===r&&P===n:m.id===r});if(!o){t({isLoading:!1});return}let l=o.quantity,C=s.map(m=>({productId:m.product_id||m.productId,variantId:m.variant_id||m.variantId||m.id,quantity:m.id===o.id?a:m.quantity}));await c.updateCart(i,C);let f=await c.getCart(i);t({items:f.items||[],checkoutUrl:f.checkoutUrl,totalAmount:f.totalAmount,isLoading:!1}),u?.({type:"UPDATE_QUANTITY",item:{...o,quantity:a},previousQuantity:l});}catch{t({error:"Failed to update quantity",isLoading:false}),u?.({type:"CART_ERROR",action:"updateItemQuantity",error:"Failed to update quantity"});}},clearCart:async()=>{let{cartToken:r,items:a}=e();if(!r)return;let{cartService:n,onCartEvent:i}=p();try{t({isLoading:!0,error:null}),await n.updateCart(r,[]);let s=a.length;t({items:[],isLoading:!1}),i?.({type:"CLEAR_CART",itemCount:s});}catch{t({error:"Failed to clear cart",isLoading:false}),i?.({type:"CART_ERROR",action:"clearCart",error:"Failed to clear cart"});}},openCart:async()=>{let{cartToken:r}=e(),{cartService:a,onCartEvent:n}=p();if(r)try{let i=await a.getCart(r);t({items:i.items||[],checkoutUrl:i.checkoutUrl,totalAmount:i.totalAmount,isOpen:!0});}catch{t({isOpen:true});}else t({isOpen:true});n?.({type:"CART_OPENED"});},closeCart:()=>{t({isOpen:false}),p().onCartEvent?.({type:"CART_CLOSED"});},toggleCart:async()=>{let{isOpen:r}=e();r?e().closeCart():await e().openCart();}}));var D=()=>d(t=>t.isInitialized?t.items.length:0),U=()=>d(t=>t.isInitialized?t.items.reduce((e,r)=>e+r.quantity,0):0),q=()=>d(t=>t.items),_=()=>d(t=>t.toggleCart),F=()=>d(t=>({isOpen:t.isOpen,isLoading:t.isLoading,error:t.error,cartToken:t.cartToken,isInitialized:t.isInitialized})),z=()=>d(t=>t.items.reduce((e,r)=>e+r.price.amount*r.quantity,0)),N=()=>d(t=>t.totalAmount),$=(t,e)=>d(r=>r.items.some(a=>e?a.productId===t&&a.variantId===e:a.productId===t)),Q=(t,e)=>d(r=>r.items.find(a=>e?a.productId===t&&a.variantId===e:a.productId===t)),J=()=>d(t=>t.checkoutUrl);function g(t,e,r,a="USD"){let n=e?t.variants?.find(o=>o.id===e)||t.variants?.[0]:t.variants?.find(o=>o.available)||t.variants?.[0],i=n?.price||t.price,s=typeof i=="number"?{amount:i,currencyCode:a}:i,c=n?.compareAtPrice||t.compareAtPrice,u=c?typeof c=="number"?{amount:c,currencyCode:a}:c:void 0;return {id:`${t.id}-${e||n?.id||"default"}`,productId:t.id,variantId:e||n?.id,title:t.title,price:s,compareAtPrice:u,quantity:r||t.quantity||1,image:t.images?.[0]?.url||t.image,vendor:t.vendor}}function v(t){let[e,r]=react.useState({}),{addItem:a}=d();return {addToCart:async(s,c,u)=>{if(s){r(o=>({...o,[s.id]:true}));try{let o=g(s,c,u,t?.defaultCurrencyCode);await a(o),t?.onSuccess?.(o);}catch(o){let l=o instanceof Error?o:new Error("Failed to add product to cart");t?.onError?.(l);}finally{r(o=>({...o,[s.id]:false}));}}},loadingStates:e,isLoading:s=>e[s]??false}}var h=class{constructor(e="/api/v1/cart"){this.baseUrl=e;}async makeRequest(e,r){return fetch(e,{headers:{"Content-Type":"application/json",...r.headers},...r})}async handleResponse(e,r){if(!e.ok){let a=await e.text();throw new Error(`${r}: ${e.status} ${a}`)}return e.json()}async createCart(e){let r=await this.makeRequest(this.baseUrl,{method:"POST",body:JSON.stringify({merchantName:e})});return this.handleResponse(r,"Failed to create cart")}async getCart(e){let r=await this.makeRequest(`${this.baseUrl}/get`,{method:"POST",body:JSON.stringify({cartId:e})});return this.handleResponse(r,"Failed to get cart")}async addToCart(e,r){let a=await this.makeRequest(`${this.baseUrl}/add`,{method:"POST",body:JSON.stringify({cartId:e,items:r})});await this.handleResponse(a,"Failed to add item to cart");}async removeFromCart(e,r){let a=await this.makeRequest(`${this.baseUrl}/remove`,{method:"POST",body:JSON.stringify({cartId:e,itemIds:r})});await this.handleResponse(a,"Failed to remove item from cart");}async updateCart(e,r){let a=await this.makeRequest(`${this.baseUrl}/update`,{method:"POST",body:JSON.stringify({cartId:e,items:r})});await this.handleResponse(a,"Failed to update cart");}};var I=class{constructor(e="default"){this.merchantName=e;}getStorageKey(e){return `${this.merchantName?`merchant_${this.merchantName}`:"default"}_${e}`}get(e){if(typeof window>"u")return null;try{return localStorage.getItem(this.getStorageKey(e))}catch{return null}}set(e,r){if(!(typeof window>"u"))try{localStorage.setItem(this.getStorageKey(e),r);}catch{}}remove(e){if(!(typeof window>"u"))try{localStorage.removeItem(this.getStorageKey(e));}catch{}}getCartToken(){return this.get("cartToken")}setCartToken(e){this.set("cartToken",e);}removeCartToken(){this.remove("cartToken");}clear(){if(!(typeof window>"u"))try{let e=Object.keys(localStorage),r=this.getStorageKey("");e.forEach(a=>{a.startsWith(r)&&localStorage.removeItem(a);});}catch{}}};var T=class{constructor(e){this.client=e;}unwrapResponse(e){if(!e.success)throw new Error(e.message||"Cart operation failed");if(e.data===null)throw new Error("Cart data is null");return e.data}async createCart(e){let r=await this.client.createCart({merchantName:e});return this.unwrapResponse(r)}async getCart(e){let r=await this.client.getCart({id:e});return this.unwrapResponse(r)}async addToCart(e,r){let a=await this.client.addToCart({id:e,items:r});this.unwrapResponse(a);}async removeFromCart(e,r){let a=await this.client.removeFromCart({id:e,itemIds:r});this.unwrapResponse(a);}async updateCart(e,r){let a=await this.client.updateCart({id:e,items:r});this.unwrapResponse(a);}};function R(t){return t&&typeof t.getCart=="function"&&typeof t.createCart=="function"&&typeof t.addToCart=="function"}function A(t,e="USD",r){try{return new Intl.NumberFormat(r,{style:"currency",currency:e}).format(t)}catch{return `${e} ${t.toFixed(2)}`}}function S(t){return t.reduce((e,r)=>{let a=typeof r.price=="number"?r.price:r.price.amount;return e+a*r.quantity},0)}function k(t){return t.reduce((e,r)=>{if(!r.compareAtPrice)return e;let a=typeof r.price=="number"?r.price:r.price.amount,i=(typeof r.compareAtPrice=="number"?r.compareAtPrice:r.compareAtPrice.amount)-a;return e+Math.max(0,i)*r.quantity},0)}function x({merchantName:t}){let e=d(a=>a.initializeCart),r=d(a=>a.isOpen);return react.useEffect(()=>{e(t);},[t,e]),react.useEffect(()=>(document.body.style.overflow=r?"hidden":"",()=>{document.body.style.overflow="";}),[r]),null}exports.CartInitializer=x;exports.DataLayerCartService=T;exports.DefaultCartService=h;exports.DefaultCartStorage=I;exports.calculateCartTotal=S;exports.calculateSavings=k;exports.configureCart=E;exports.formatPrice=A;exports.getCartConfig=p;exports.isCartConfigured=O;exports.isDataLayerClient=R;exports.mapProductToCartItem=g;exports.useAddToCart=v;exports.useCartCount=D;exports.useCartItem=Q;exports.useCartItems=q;exports.useCartStatus=F;exports.useCartStore=d;exports.useCartToggle=_;exports.useCartTotal=z;exports.useCartTotalAmount=N;exports.useCartTotalQuantity=U;exports.useCheckoutUrl=J;exports.useIsInCart=$;
package/dist/index.mjs CHANGED
@@ -1,2 +1,2 @@
1
1
  "use client";
2
- import {create}from'zustand';import {useState,useEffect}from'react';var C=null;function b(t){C=t;}function l(){if(!C)throw new Error("Cart not configured. Call configureCart() first.");return C}function E(){return C!==null}var u=create((t,e)=>({items:[],isOpen:false,isLoading:false,error:null,cartId:null,totalAmount:void 0,isInitialized:false,initializeCart:async r=>{let{isInitialized:a}=e();if(a)return;let i=l(),{cartService:n,cartStorage:s}=i,c=r??i.merchantName;try{let d=s.getCartId();if(d)try{let o=await n.getCart(d);t({items:o.items||[],cartId:d,checkoutUrl:o.checkoutUrl,totalAmount:o.totalAmount,isInitialized:!0}),i.onCartEvent?.({type:"CART_INITIALIZED",cartId:d});}catch{s.removeCartId();let o=await n.createCart(c);s.setCartId(o.token),t({items:o.items||[],cartId:o.token,checkoutUrl:o.checkoutUrl,totalAmount:o.totalAmount,isInitialized:!0}),i.onCartEvent?.({type:"CART_INITIALIZED",cartId:o.token});}else {let o=await n.createCart(c);s.setCartId(o.token),t({items:o.items||[],cartId:o.token,checkoutUrl:o.checkoutUrl,totalAmount:o.totalAmount,isInitialized:!0}),i.onCartEvent?.({type:"CART_INITIALIZED",cartId:o.token});}}catch{t({error:"Failed to initialize cart",isInitialized:true}),i.onCartEvent?.({type:"CART_ERROR",action:"initializeCart",error:"Failed to initialize cart"});}},addItem:async r=>{let{cartId:a}=e();if(!a)return;let{cartService:i,onCartEvent:n}=l();try{t({isLoading:!0,error:null});let s=[{productId:r.productId||r.id,variantId:r.variantId||r.id,quantity:r.quantity}];await i.addToCart(a,s);let c=await i.getCart(a);t({items:c.items||[],checkoutUrl:c.checkoutUrl,totalAmount:c.totalAmount,isLoading:!1}),n?.({type:"ADD_TO_CART",item:r});}catch{t({error:"Failed to add item",isLoading:false}),n?.({type:"CART_ERROR",action:"addItem",error:"Failed to add item"});}},addItems:async r=>{let{cartId:a}=e();if(!a)return;let{cartService:i,onCartEvent:n}=l();try{t({isLoading:!0,error:null}),await i.addToCart(a,r);let s=await i.getCart(a);t({items:s.items||[],checkoutUrl:s.checkoutUrl,totalAmount:s.totalAmount,isLoading:!1});}catch{t({error:"Failed to add items",isLoading:false}),n?.({type:"CART_ERROR",action:"addItems",error:"Failed to add items"});}},removeItem:async(r,a)=>{let{cartId:i,items:n}=e();if(!i)return;let{cartService:s,onCartEvent:c}=l();try{t({isLoading:!0,error:null});let d=n.find(p=>a?p.id===r||p.productId===r&&p.variantId===a:p.id===r);if(!d){t({isLoading:!1});return}await s.removeFromCart(i,[d.id]);let o=await s.getCart(i);t({items:o.items||[],checkoutUrl:o.checkoutUrl,totalAmount:o.totalAmount,isLoading:!1}),c?.({type:"REMOVE_FROM_CART",item:d});}catch{t({error:"Failed to remove item",isLoading:false}),c?.({type:"CART_ERROR",action:"removeItem",error:"Failed to remove item"});}},updateItemQuantity:async(r,a,i)=>{let{cartId:n,items:s}=e();if(!n)return;if(a<=0)return e().removeItem(r,i);let{cartService:c,onCartEvent:d}=l();try{t({isLoading:!0,error:null});let o=s.find(m=>i?m.id===r||m.productId===r&&m.variantId===i:m.id===r);if(!o){t({isLoading:!1});return}let p=o.quantity,L=s.map(m=>({productId:m.productId,variantId:m.variantId||"",quantity:m.id===o.id?a:m.quantity}));await c.updateCart(n,L);let h=await c.getCart(n);t({items:h.items||[],checkoutUrl:h.checkoutUrl,totalAmount:h.totalAmount,isLoading:!1}),d?.({type:"UPDATE_QUANTITY",item:{...o,quantity:a},previousQuantity:p});}catch{t({error:"Failed to update quantity",isLoading:false}),d?.({type:"CART_ERROR",action:"updateItemQuantity",error:"Failed to update quantity"});}},clearCart:async()=>{let{cartId:r,items:a}=e();if(!r)return;let{cartService:i,onCartEvent:n}=l();try{t({isLoading:!0,error:null}),await i.updateCart(r,[]);let s=a.length;t({items:[],isLoading:!1}),n?.({type:"CLEAR_CART",itemCount:s});}catch{t({error:"Failed to clear cart",isLoading:false}),n?.({type:"CART_ERROR",action:"clearCart",error:"Failed to clear cart"});}},openCart:async()=>{let{cartId:r}=e(),{cartService:a,onCartEvent:i}=l();if(r)try{let n=await a.getCart(r);t({items:n.items||[],checkoutUrl:n.checkoutUrl,totalAmount:n.totalAmount,isOpen:!0});}catch{t({isOpen:true});}else t({isOpen:true});i?.({type:"CART_OPENED"});},closeCart:()=>{t({isOpen:false}),l().onCartEvent?.({type:"CART_CLOSED"});},toggleCart:async()=>{let{isOpen:r}=e();r?e().closeCart():await e().openCart();}}));var O=()=>u(t=>t.isInitialized?t.items.length:0),D=()=>u(t=>t.isInitialized?t.items.reduce((e,r)=>e+r.quantity,0):0),U=()=>u(t=>t.items),k=()=>u(t=>t.toggleCart),q=()=>u(t=>({isOpen:t.isOpen,isLoading:t.isLoading,error:t.error,cartId:t.cartId,isInitialized:t.isInitialized})),F=()=>u(t=>t.items.reduce((e,r)=>e+r.price.amount*r.quantity,0)),z=()=>u(t=>t.totalAmount),_=(t,e)=>u(r=>r.items.some(a=>e?a.productId===t&&a.variantId===e:a.productId===t)),N=(t,e)=>u(r=>r.items.find(a=>e?a.productId===t&&a.variantId===e:a.productId===t)),$=()=>u(t=>t.checkoutUrl);function f(t,e,r,a="USD"){let i=e?t.variants?.find(o=>o.id===e)||t.variants?.[0]:t.variants?.find(o=>o.available)||t.variants?.[0],n=i?.price||t.price,s=typeof n=="number"?{amount:n,currencyCode:a}:n,c=i?.compareAtPrice||t.compareAtPrice,d=c?typeof c=="number"?{amount:c,currencyCode:a}:c:void 0;return {id:`${t.id}-${e||i?.id||"default"}`,productId:t.id,variantId:e||i?.id,title:t.title,price:s,compareAtPrice:d,quantity:r||t.quantity||1,image:t.images?.[0]?.url||t.image,vendor:t.vendor}}function v(t){let[e,r]=useState({}),{addItem:a}=u();return {addToCart:async(s,c,d)=>{if(s){r(o=>({...o,[s.id]:true}));try{let o=f(s,c,d,t?.defaultCurrencyCode);await a(o),t?.onSuccess?.(o);}catch(o){let p=o instanceof Error?o:new Error("Failed to add product to cart");t?.onError?.(p);}finally{r(o=>({...o,[s.id]:false}));}}},loadingStates:e,isLoading:s=>e[s]??false}}var y=class{constructor(e="/api/v1/cart"){this.baseUrl=e;}async makeRequest(e,r){return fetch(e,{headers:{"Content-Type":"application/json",...r.headers},...r})}async handleResponse(e,r){if(!e.ok){let a=await e.text();throw new Error(`${r}: ${e.status} ${a}`)}return e.json()}async createCart(e){let r=await this.makeRequest(this.baseUrl,{method:"POST",body:JSON.stringify({merchantName:e})});return this.handleResponse(r,"Failed to create cart")}async getCart(e){let r=await this.makeRequest(`${this.baseUrl}/get`,{method:"POST",body:JSON.stringify({cartId:e})});return this.handleResponse(r,"Failed to get cart")}async addToCart(e,r){let a=await this.makeRequest(`${this.baseUrl}/add`,{method:"POST",body:JSON.stringify({cartId:e,items:r})});await this.handleResponse(a,"Failed to add item to cart");}async removeFromCart(e,r){let a=await this.makeRequest(`${this.baseUrl}/remove`,{method:"POST",body:JSON.stringify({cartId:e,itemIds:r})});await this.handleResponse(a,"Failed to remove item from cart");}async updateCart(e,r){let a=await this.makeRequest(`${this.baseUrl}/update`,{method:"POST",body:JSON.stringify({cartId:e,items:r})});await this.handleResponse(a,"Failed to update cart");}};var g=class{constructor(e="default"){this.merchantName=e;}getStorageKey(e){return `${this.merchantName?`merchant_${this.merchantName}`:"default"}_${e}`}get(e){if(typeof window>"u")return null;try{return localStorage.getItem(this.getStorageKey(e))}catch{return null}}set(e,r){if(!(typeof window>"u"))try{localStorage.setItem(this.getStorageKey(e),r);}catch{}}remove(e){if(!(typeof window>"u"))try{localStorage.removeItem(this.getStorageKey(e));}catch{}}getCartId(){return this.get("cartId")}setCartId(e){this.set("cartId",e);}removeCartId(){this.remove("cartId");}clear(){if(!(typeof window>"u"))try{let e=Object.keys(localStorage),r=this.getStorageKey("");e.forEach(a=>{a.startsWith(r)&&localStorage.removeItem(a);});}catch{}}};var I=class{constructor(e){this.client=e;}unwrapResponse(e){if(!e.success)throw new Error(e.message||"Cart operation failed");if(e.data===null)throw new Error("Cart data is null");return e.data}async createCart(e){let r=await this.client.createCart({merchantName:e});return this.unwrapResponse(r)}async getCart(e){let r=await this.client.getCart({id:e});return this.unwrapResponse(r)}async addToCart(e,r){let a=await this.client.addToCart({id:e,items:r});this.unwrapResponse(a);}async removeFromCart(e,r){let a=await this.client.removeFromCart({id:e,itemIds:r});this.unwrapResponse(a);}async updateCart(e,r){let a=await this.client.updateCart({id:e,items:r});this.unwrapResponse(a);}};function R(t){return t&&typeof t.getCart=="function"&&typeof t.createCart=="function"&&typeof t.addToCart=="function"}function T(t,e="USD",r){try{return new Intl.NumberFormat(r,{style:"currency",currency:e}).format(t)}catch{return `${e} ${t.toFixed(2)}`}}function A(t){return t.reduce((e,r)=>{let a=typeof r.price=="number"?r.price:r.price.amount;return e+a*r.quantity},0)}function S(t){return t.reduce((e,r)=>{if(!r.compareAtPrice)return e;let a=typeof r.price=="number"?r.price:r.price.amount,n=(typeof r.compareAtPrice=="number"?r.compareAtPrice:r.compareAtPrice.amount)-a;return e+Math.max(0,n)*r.quantity},0)}function x({merchantName:t}){let e=u(a=>a.initializeCart),r=u(a=>a.isOpen);return useEffect(()=>{e(t);},[t,e]),useEffect(()=>(document.body.style.overflow=r?"hidden":"",()=>{document.body.style.overflow="";}),[r]),null}export{x as CartInitializer,I as DataLayerCartService,y as DefaultCartService,g as DefaultCartStorage,A as calculateCartTotal,S as calculateSavings,b as configureCart,T as formatPrice,l as getCartConfig,E as isCartConfigured,R as isDataLayerClient,f as mapProductToCartItem,v as useAddToCart,O as useCartCount,N as useCartItem,U as useCartItems,q as useCartStatus,u as useCartStore,k as useCartToggle,F as useCartTotal,z as useCartTotalAmount,D as useCartTotalQuantity,$ as useCheckoutUrl,_ as useIsInCart};
2
+ import {create}from'zustand';import {useState,useEffect}from'react';var y=null;function E(t){y=t;}function p(){if(!y)throw new Error("Cart not configured. Call configureCart() first.");return y}function O(){return y!==null}var d=create((t,e)=>({items:[],isOpen:false,isLoading:false,error:null,cartToken:null,totalAmount:void 0,isInitialized:false,initializeCart:async r=>{let{isInitialized:a}=e();if(a)return;let n=p(),{cartService:i,cartStorage:s}=n,c=r??n.merchantName;try{let u=s.getCartToken();if(u)try{let o=await i.getCart(u);t({items:o.items||[],cartToken:u,checkoutUrl:o.checkoutUrl,totalAmount:o.totalAmount,isInitialized:!0}),n.onCartEvent?.({type:"CART_INITIALIZED",cartToken:u});}catch{s.removeCartToken();let o=await i.createCart(c),l=o.token||o.id;s.setCartToken(l),t({items:o.items||[],cartToken:l,checkoutUrl:o.checkoutUrl,totalAmount:o.totalAmount,isInitialized:!0}),n.onCartEvent?.({type:"CART_INITIALIZED",cartToken:l});}else {let o=await i.createCart(c),l=o.token||o.id;s.setCartToken(l),t({items:o.items||[],cartToken:l,checkoutUrl:o.checkoutUrl,totalAmount:o.totalAmount,isInitialized:!0}),n.onCartEvent?.({type:"CART_INITIALIZED",cartToken:l});}}catch{t({error:"Failed to initialize cart",isInitialized:true}),n.onCartEvent?.({type:"CART_ERROR",action:"initializeCart",error:"Failed to initialize cart"});}},addItem:async r=>{let{cartToken:a}=e();if(!a)return;let{cartService:n,onCartEvent:i}=p();try{t({isLoading:!0,error:null});let s=[{productId:r.productId||r.id,variantId:r.variantId||r.id,quantity:r.quantity}];await n.addToCart(a,s);let c=await n.getCart(a);t({items:c.items||[],checkoutUrl:c.checkoutUrl,totalAmount:c.totalAmount,isLoading:!1}),i?.({type:"ADD_TO_CART",item:r});}catch{t({error:"Failed to add item",isLoading:false}),i?.({type:"CART_ERROR",action:"addItem",error:"Failed to add item"});}},addItems:async r=>{let{cartToken:a}=e();if(!a)return;let{cartService:n,onCartEvent:i}=p();try{t({isLoading:!0,error:null}),await n.addToCart(a,r);let s=await n.getCart(a);t({items:s.items||[],checkoutUrl:s.checkoutUrl,totalAmount:s.totalAmount,isLoading:!1});}catch{t({error:"Failed to add items",isLoading:false}),i?.({type:"CART_ERROR",action:"addItems",error:"Failed to add items"});}},removeItem:async(r,a)=>{let{cartToken:n,items:i}=e();if(!n)return;let{cartService:s,onCartEvent:c}=p();try{t({isLoading:!0,error:null});let u=i.find(C=>{let f=C.variant_id||C.variantId,m=C.product_id||C.productId;return a?C.id===r||m===r&&f===a:C.id===r});if(!u){t({isLoading:!1});return}let o=u.variant_id||u.variantId||u.id;await s.removeFromCart(n,[o]);let l=await s.getCart(n);t({items:l.items||[],checkoutUrl:l.checkoutUrl,totalAmount:l.totalAmount,isLoading:!1}),c?.({type:"REMOVE_FROM_CART",item:u});}catch{t({error:"Failed to remove item",isLoading:false}),c?.({type:"CART_ERROR",action:"removeItem",error:"Failed to remove item"});}},updateItemQuantity:async(r,a,n)=>{let{cartToken:i,items:s}=e();if(!i)return;if(a<=0)return e().removeItem(r,n);let{cartService:c,onCartEvent:u}=p();try{t({isLoading:!0,error:null});let o=s.find(m=>{let P=m.variant_id||m.variantId,L=m.product_id||m.productId;return n?m.id===r||L===r&&P===n:m.id===r});if(!o){t({isLoading:!1});return}let l=o.quantity,C=s.map(m=>({productId:m.product_id||m.productId,variantId:m.variant_id||m.variantId||m.id,quantity:m.id===o.id?a:m.quantity}));await c.updateCart(i,C);let f=await c.getCart(i);t({items:f.items||[],checkoutUrl:f.checkoutUrl,totalAmount:f.totalAmount,isLoading:!1}),u?.({type:"UPDATE_QUANTITY",item:{...o,quantity:a},previousQuantity:l});}catch{t({error:"Failed to update quantity",isLoading:false}),u?.({type:"CART_ERROR",action:"updateItemQuantity",error:"Failed to update quantity"});}},clearCart:async()=>{let{cartToken:r,items:a}=e();if(!r)return;let{cartService:n,onCartEvent:i}=p();try{t({isLoading:!0,error:null}),await n.updateCart(r,[]);let s=a.length;t({items:[],isLoading:!1}),i?.({type:"CLEAR_CART",itemCount:s});}catch{t({error:"Failed to clear cart",isLoading:false}),i?.({type:"CART_ERROR",action:"clearCart",error:"Failed to clear cart"});}},openCart:async()=>{let{cartToken:r}=e(),{cartService:a,onCartEvent:n}=p();if(r)try{let i=await a.getCart(r);t({items:i.items||[],checkoutUrl:i.checkoutUrl,totalAmount:i.totalAmount,isOpen:!0});}catch{t({isOpen:true});}else t({isOpen:true});n?.({type:"CART_OPENED"});},closeCart:()=>{t({isOpen:false}),p().onCartEvent?.({type:"CART_CLOSED"});},toggleCart:async()=>{let{isOpen:r}=e();r?e().closeCart():await e().openCart();}}));var D=()=>d(t=>t.isInitialized?t.items.length:0),U=()=>d(t=>t.isInitialized?t.items.reduce((e,r)=>e+r.quantity,0):0),q=()=>d(t=>t.items),_=()=>d(t=>t.toggleCart),F=()=>d(t=>({isOpen:t.isOpen,isLoading:t.isLoading,error:t.error,cartToken:t.cartToken,isInitialized:t.isInitialized})),z=()=>d(t=>t.items.reduce((e,r)=>e+r.price.amount*r.quantity,0)),N=()=>d(t=>t.totalAmount),$=(t,e)=>d(r=>r.items.some(a=>e?a.productId===t&&a.variantId===e:a.productId===t)),Q=(t,e)=>d(r=>r.items.find(a=>e?a.productId===t&&a.variantId===e:a.productId===t)),J=()=>d(t=>t.checkoutUrl);function g(t,e,r,a="USD"){let n=e?t.variants?.find(o=>o.id===e)||t.variants?.[0]:t.variants?.find(o=>o.available)||t.variants?.[0],i=n?.price||t.price,s=typeof i=="number"?{amount:i,currencyCode:a}:i,c=n?.compareAtPrice||t.compareAtPrice,u=c?typeof c=="number"?{amount:c,currencyCode:a}:c:void 0;return {id:`${t.id}-${e||n?.id||"default"}`,productId:t.id,variantId:e||n?.id,title:t.title,price:s,compareAtPrice:u,quantity:r||t.quantity||1,image:t.images?.[0]?.url||t.image,vendor:t.vendor}}function v(t){let[e,r]=useState({}),{addItem:a}=d();return {addToCart:async(s,c,u)=>{if(s){r(o=>({...o,[s.id]:true}));try{let o=g(s,c,u,t?.defaultCurrencyCode);await a(o),t?.onSuccess?.(o);}catch(o){let l=o instanceof Error?o:new Error("Failed to add product to cart");t?.onError?.(l);}finally{r(o=>({...o,[s.id]:false}));}}},loadingStates:e,isLoading:s=>e[s]??false}}var h=class{constructor(e="/api/v1/cart"){this.baseUrl=e;}async makeRequest(e,r){return fetch(e,{headers:{"Content-Type":"application/json",...r.headers},...r})}async handleResponse(e,r){if(!e.ok){let a=await e.text();throw new Error(`${r}: ${e.status} ${a}`)}return e.json()}async createCart(e){let r=await this.makeRequest(this.baseUrl,{method:"POST",body:JSON.stringify({merchantName:e})});return this.handleResponse(r,"Failed to create cart")}async getCart(e){let r=await this.makeRequest(`${this.baseUrl}/get`,{method:"POST",body:JSON.stringify({cartId:e})});return this.handleResponse(r,"Failed to get cart")}async addToCart(e,r){let a=await this.makeRequest(`${this.baseUrl}/add`,{method:"POST",body:JSON.stringify({cartId:e,items:r})});await this.handleResponse(a,"Failed to add item to cart");}async removeFromCart(e,r){let a=await this.makeRequest(`${this.baseUrl}/remove`,{method:"POST",body:JSON.stringify({cartId:e,itemIds:r})});await this.handleResponse(a,"Failed to remove item from cart");}async updateCart(e,r){let a=await this.makeRequest(`${this.baseUrl}/update`,{method:"POST",body:JSON.stringify({cartId:e,items:r})});await this.handleResponse(a,"Failed to update cart");}};var I=class{constructor(e="default"){this.merchantName=e;}getStorageKey(e){return `${this.merchantName?`merchant_${this.merchantName}`:"default"}_${e}`}get(e){if(typeof window>"u")return null;try{return localStorage.getItem(this.getStorageKey(e))}catch{return null}}set(e,r){if(!(typeof window>"u"))try{localStorage.setItem(this.getStorageKey(e),r);}catch{}}remove(e){if(!(typeof window>"u"))try{localStorage.removeItem(this.getStorageKey(e));}catch{}}getCartToken(){return this.get("cartToken")}setCartToken(e){this.set("cartToken",e);}removeCartToken(){this.remove("cartToken");}clear(){if(!(typeof window>"u"))try{let e=Object.keys(localStorage),r=this.getStorageKey("");e.forEach(a=>{a.startsWith(r)&&localStorage.removeItem(a);});}catch{}}};var T=class{constructor(e){this.client=e;}unwrapResponse(e){if(!e.success)throw new Error(e.message||"Cart operation failed");if(e.data===null)throw new Error("Cart data is null");return e.data}async createCart(e){let r=await this.client.createCart({merchantName:e});return this.unwrapResponse(r)}async getCart(e){let r=await this.client.getCart({id:e});return this.unwrapResponse(r)}async addToCart(e,r){let a=await this.client.addToCart({id:e,items:r});this.unwrapResponse(a);}async removeFromCart(e,r){let a=await this.client.removeFromCart({id:e,itemIds:r});this.unwrapResponse(a);}async updateCart(e,r){let a=await this.client.updateCart({id:e,items:r});this.unwrapResponse(a);}};function R(t){return t&&typeof t.getCart=="function"&&typeof t.createCart=="function"&&typeof t.addToCart=="function"}function A(t,e="USD",r){try{return new Intl.NumberFormat(r,{style:"currency",currency:e}).format(t)}catch{return `${e} ${t.toFixed(2)}`}}function S(t){return t.reduce((e,r)=>{let a=typeof r.price=="number"?r.price:r.price.amount;return e+a*r.quantity},0)}function k(t){return t.reduce((e,r)=>{if(!r.compareAtPrice)return e;let a=typeof r.price=="number"?r.price:r.price.amount,i=(typeof r.compareAtPrice=="number"?r.compareAtPrice:r.compareAtPrice.amount)-a;return e+Math.max(0,i)*r.quantity},0)}function x({merchantName:t}){let e=d(a=>a.initializeCart),r=d(a=>a.isOpen);return useEffect(()=>{e(t);},[t,e]),useEffect(()=>(document.body.style.overflow=r?"hidden":"",()=>{document.body.style.overflow="";}),[r]),null}export{x as CartInitializer,T as DataLayerCartService,h as DefaultCartService,I as DefaultCartStorage,S as calculateCartTotal,k as calculateSavings,E as configureCart,A as formatPrice,p as getCartConfig,O as isCartConfigured,R as isDataLayerClient,g as mapProductToCartItem,v as useAddToCart,D as useCartCount,Q as useCartItem,q as useCartItems,F as useCartStatus,d as useCartStore,_ as useCartToggle,z as useCartTotal,N as useCartTotalAmount,U as useCartTotalQuantity,J as useCheckoutUrl,$ as useIsInCart};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shopkit/cart",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "Cart state management, hooks, and services for e-commerce storefronts",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",