@cocartheadless/sdk
Advanced tools
| "use strict";var CoCartSDK=(()=>{var $=Object.defineProperty;var O=Object.getOwnPropertyDescriptor;var V=Object.getOwnPropertyNames;var J=Object.prototype.hasOwnProperty;var H=(i,e)=>{for(var t in e)$(i,t,{get:e[t],enumerable:!0})},D=(i,e,t,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let s of V(e))!J.call(i,s)&&s!==t&&$(i,s,{get:()=>e[s],enumerable:!(r=O(e,s))||r.enumerable});return i};var L=i=>D($({},"__esModule",{value:!0}),i);var U={};H(U,{AuthenticationError:()=>g,Cart:()=>b,CoCart:()=>I,CoCartError:()=>c,CurrencyFormatter:()=>j,EncryptedStorage:()=>P,Endpoint:()=>h,JwtManager:()=>d,LocalStorage:()=>w,MemoryStorage:()=>R,Paginator:()=>C,Products:()=>k,Response:()=>y,SessionManager:()=>_,Sessions:()=>v,Store:()=>T,TimezoneHelper:()=>E,ValidationError:()=>m,VersionError:()=>f,validateEmail:()=>N,validateProductId:()=>K,validateQuantity:()=>x});var y=class{statusCode;headers;body;data=null;constructor(e,t,r){this.statusCode=e,this.headers=t,this.body=r}toObject(){if(this.data===null)try{this.data=JSON.parse(this.body)}catch{this.data={}}return this.data}toJson(e=!0){return JSON.stringify(this.toObject(),null,e?2:void 0)}isSuccessful(){return this.statusCode>=200&&this.statusCode<300}isError(){return this.statusCode>=400}get(e,t){let r=this.toObject(),s=e.split("."),n=r;for(let a of s){if(n==null||typeof n!="object")return t;n=n[a]}return n!==void 0?n:t}has(e){let t=this.toObject(),r=e.split("."),s=t;for(let n of r){if(s==null||typeof s!="object"||!(n in s))return!1;s=s[n]}return!0}getHeader(e,t=null){return this.headers.get(e)??t}getCartKey(){return this.getHeader("Cart-Key")}getCartHash(){return this.get("cart_hash",null)}getItems(){return this.get("items",[])}getTotals(){return this.get("totals",{})}getItemCount(){return this.get("item_count",0)}hasItems(){return this.getItemCount()>0}isEmpty(){return this.getItemCount()===0}getNotices(){return this.get("notices",[])}getCoupons(){return this.get("coupons",[])}hasCoupons(){return this.getCoupons().length>0}getCustomer(){return this.get("customer",{})}getCurrency(){return this.get("currency",{})}getShippingMethods(){return this.get("shipping",[])}getFees(){return this.get("fees",[])}getCrossSells(){return this.get("cross_sells",[])}getTotalResults(){let e=this.getHeader("X-WP-Total");return e!==null?parseInt(e,10):null}getTotalPages(){let e=this.getHeader("X-WP-TotalPages");return e!==null?parseInt(e,10):null}getETag(){return this.getHeader("ETag")}isNotModified(){return this.statusCode===304}getCacheStatus(){return this.getHeader("CoCart-Cache")}getErrorCode(){return this.isError()?this.get("code",null):null}getErrorMessage(){return this.isError()?this.get("message",null):null}};var c=class extends Error{httpCode;errorCode;responseData;constructor(e,t=0,r=null,s={}){super(e),this.name="CoCartError",this.httpCode=t,this.errorCode=r,this.responseData=s}};var g=class extends c{constructor(e,t=401,r=null,s={}){super(e,t,r,s),this.name="AuthenticationError"}};var m=class extends c{constructor(e,t=400,r=null,s={}){super(e,t,r,s),this.name="ValidationError"}};var f=class extends c{constructor(e){super(`${e}() requires CoCart Basic. Please upgrade from the legacy CoCart plugin to use this feature.`,0,"cocart_version_required"),this.name="VersionError"}};var R=class{store=new Map;get(e){return this.store.get(e)??null}set(e,t){this.store.set(e,t)}delete(e){this.store.delete(e)}};var w=class{prefix;constructor(e="cocart_"){this.prefix=e}get(e){return globalThis.localStorage.getItem(this.prefix+e)}set(e,t){globalThis.localStorage.setItem(this.prefix+e,t)}delete(e){globalThis.localStorage.removeItem(this.prefix+e)}};var P=class{prefix;cryptoKey=null;keyPromise=null;encryptionKey;constructor(e,t){this.encryptionKey=e,this.prefix=t?.prefix??"cocart_enc_"}async get(e){let t=globalThis.localStorage.getItem(this.prefix+e);if(t===null)return null;try{let r=await this.getKey(),s=this.base64ToBytes(t),n=s.slice(0,12),a=s.slice(12),o=await globalThis.crypto.subtle.decrypt({name:"AES-GCM",iv:n},r,a);return new TextDecoder().decode(o)}catch{return globalThis.localStorage.removeItem(this.prefix+e),null}}async set(e,t){let r=await this.getKey(),s=globalThis.crypto.getRandomValues(new Uint8Array(12)),n=new TextEncoder().encode(t),a=await globalThis.crypto.subtle.encrypt({name:"AES-GCM",iv:s},r,n),o=new Uint8Array(s.length+a.byteLength);o.set(s,0),o.set(new Uint8Array(a),s.length),globalThis.localStorage.setItem(this.prefix+e,this.bytesToBase64(o))}delete(e){globalThis.localStorage.removeItem(this.prefix+e)}async getKey(){return this.cryptoKey?this.cryptoKey:(this.keyPromise||(this.keyPromise=this.deriveKey()),this.cryptoKey=await this.keyPromise,this.cryptoKey)}async deriveKey(){let e=this.getOrCreateSalt(),t=await globalThis.crypto.subtle.importKey("raw",new TextEncoder().encode(this.encryptionKey),"PBKDF2",!1,["deriveKey"]);return globalThis.crypto.subtle.deriveKey({name:"PBKDF2",salt:e.buffer,iterations:1e5,hash:"SHA-256"},t,{name:"AES-GCM",length:256},!1,["encrypt","decrypt"])}getOrCreateSalt(){let e=this.prefix+"_salt",t=globalThis.localStorage.getItem(e);if(t)return this.base64ToBytes(t);let r=globalThis.crypto.getRandomValues(new Uint8Array(16));return globalThis.localStorage.setItem(e,this.bytesToBase64(r)),r}bytesToBase64(e){let t="";for(let r=0;r<e.length;r++)t+=String.fromCharCode(e[r]);return btoa(t)}base64ToBytes(e){let t=atob(e),r=new Uint8Array(t.length);for(let s=0;s<t.length;s++)r[s]=t.charCodeAt(s);return r}};var h=class{client;endpoint="";constructor(e){this.client=e}buildPath(e=""){return e?this.endpoint.replace(/\/+$/,"")+"/"+e.replace(/^\/+/,""):this.endpoint}async get(e,t){let r=typeof e=="string"?e:"",s=typeof e=="object"?e:t;try{return await this.client.get(this.buildPath(r),s)}catch(n){this.handleNoRoute(n)}}async post(e="",t,r){try{return await this.client.post(this.buildPath(e),t,r)}catch(s){this.handleNoRoute(s)}}async put(e="",t,r){try{return await this.client.put(this.buildPath(e),t,r)}catch(s){this.handleNoRoute(s)}}async delete(e="",t){try{return await this.client.delete(this.buildPath(e),t)}catch(r){this.handleNoRoute(r)}}handleNoRoute(e){throw e instanceof c&&e.errorCode==="rest_no_route"?new c("This method is only available with another CoCart plugin. Please ask support for assistance!",404,"cocart_plugin_required"):e}};function K(i){let e=typeof i=="string"?parseInt(i,10):i;if(!Number.isFinite(e)||e<1||Math.floor(e)!==e)throw new m("Product ID must be a positive integer",0,"cocart_invalid_product_id")}function x(i){if(!Number.isFinite(i)||i<1)throw new m("Quantity must be a positive number",0,"cocart_invalid_quantity")}function N(i){if(!i||!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(i))throw new m("A valid email address is required",0,"cocart_invalid_email")}var b=class extends h{endpoint="cart";async get(e,t){return typeof e=="object"||e===void 0?this.client.get(this.endpoint,e?this.stringifyParams(e):void 0):super.get(e,t)}async getFiltered(e){return this.client.get(this.endpoint,{_fields:e.join(",")})}async addItem(e,t=1,r={}){K(e),x(t);let s={id:String(e),quantity:String(t),...r};return this.post("add-item",s)}async addItems(e){let t=e.map(r=>({...r,id:String(r.id),quantity:String(r.quantity)}));return this.post("add-items",{items:t})}async updateItem(e,t,r={}){x(t);let s={quantity:String(t),...r};return this.post(`item/${e}`,s)}async updateItems(e){let t;return Array.isArray(e)?t=e.map(r=>({...r,quantity:String(r.quantity)})):t=Object.entries(e).map(([r,s])=>({item_key:r,quantity:String(s)})),this.post("update",{items:t})}async removeItem(e){return this.delete(`item/${e}`)}async removeItems(e){let t=e.map(r=>({item_key:r,quantity:"0"}));return this.post("update",{items:t})}async restoreItem(e){return this.put(`item/${e}`)}async getRemovedItems(){return super.get("",{_fields:"removed_items"})}async clear(){return this.post("clear")}async empty(){return this.clear()}async calculate(e={}){return this.post("calculate",e)}async getTotals(e=!1){let t=e?{html:"true"}:void 0;return this.client.get("cart/totals",t)}async getItemCount(){return this.client.get("cart/items/count")}async create(){return this.client.requiresBasic("cart()->create"),this.post("")}async getItems(e){return super.get("items",e)}async getItem(e,t){return super.get("item/"+e,t)}async update(e){return this.post("update",e)}async applyCoupon(e){return this.post("apply-coupon",{coupon:e})}async removeCoupon(e){return this.delete(`coupons/${e}`)}async getCoupons(){return super.get("",{_fields:"coupons"})}async checkCoupons(){return super.get("coupons/validate")}async updateCustomer(e={},t={}){let r={};for(let[s,n]of Object.entries(e))r[`billing_${s}`]=n;for(let[s,n]of Object.entries(t))r[`shipping_${s}`]=n;return this.post("update",r)}async getCustomer(){return super.get("",{_fields:"customer"})}async getShippingMethods(){return super.get("",{_fields:"shipping"})}async setShippingMethod(e){return this.post("set-shipping-method",{method_key:e})}async calculateShipping(e){return this.post("calculate/shipping",e)}async getFees(){return super.get("",{_fields:"fees"})}async addFee(e,t,r=!1){return this.post("add-fee",{name:e,amount:t,taxable:r})}async removeFees(){return this.post("remove-fees")}async getCrossSells(){return super.get("",{_fields:"cross_sells"})}async add(e,t=1){return this.addItem(e,t)}async addVariation(e,t=1,r={}){return this.addItem(e,t,{variation:r})}stringifyParams(e){let t={};for(let[r,s]of Object.entries(e))s!=null&&(t[r]=String(s));return t}};var C=class{fetchPage;startPage;constructor(e,t=1){this.fetchPage=e,this.startPage=t}async*[Symbol.asyncIterator](){let e=this.startPage;for(;;){let t=await this.fetchPage(e);yield t;let r=t.getTotalPages();if(r===null||e>=r)break;e++}}async toArray(){let e=[];for await(let t of this)e.push(t);return e}};var k=class extends h{endpoint="products";async all(e){return this.get("",e?this.stringifyParams(e):void 0)}async find(e,t){return this.get(String(e),t?this.stringifyParams(t):void 0)}async findBySlug(e,t){return this.client.requiresBasic("products()->findBySlug"),this.get(e,t?this.stringifyParams(t):void 0)}async search(e,t={}){return this.all({...t,search:e})}async byCategory(e,t={}){return this.all({...t,category:e})}async byTag(e,t={}){return this.all({...t,tag:e})}async featured(e={}){return this.all({...e,featured:!0})}async onSale(e={}){return this.all({...e,on_sale:!0})}async byPriceRange(e,t,r={}){let s={...r};return e!=null&&(s.min_price=e),t!=null&&(s.max_price=t),this.all(s)}async sortBy(e,t="asc",r={}){return this.all({...r,orderby:e,order:t})}async byStockStatus(e,t={}){return this.all({...t,stock_status:e})}async paginate(e=1,t=10,r={}){return this.all({...r,page:e,per_page:t})}allPaginated(e={}){return new C(t=>this.all({...e,page:t}))}async variations(e,t){return this.get(`${e}/variations`,t)}async variation(e,t,r){return this.client.requiresBasic("products()->variation"),this.get(`${e}/variations/${t}`,r)}async categories(e){return this.get("categories",e)}async category(e,t){return this.client.requiresBasic("products()->category"),this.get(`categories/${e}`,t)}async tags(e){return this.get("tags",e)}async tag(e,t){return this.client.requiresBasic("products()->tag"),this.get(`tags/${e}`,t)}async attributes(e){return this.get("attributes",e)}async attribute(e,t){return this.get(`attributes/${e}`,t)}async attributeTerms(e,t){return this.get(`attributes/${e}/terms`,t)}async attributeTerm(e,t,r){return this.get(`attributes/${e}/terms/${t}`,r)}async attributeBySlug(e,t){return this.client.requiresBasic("products()->attributeBySlug"),this.get(`attributes/${e}`,t)}async attributeTermsBySlug(e,t){return this.client.requiresBasic("products()->attributeTermsBySlug"),this.get(`attributes/${e}/terms`,t)}async attributeTermBySlug(e,t,r){return this.client.requiresBasic("products()->attributeTermBySlug"),this.get(`attributes/${e}/terms/${t}`,r)}async brands(e){return this.client.requiresBasic("products()->brands"),this.get("brands",e)}async brand(e,t){return this.client.requiresBasic("products()->brand"),this.get(`brands/${e}`,t)}async byBrand(e,t={}){return this.client.requiresBasic("products()->byBrand"),this.all({...t,brand:e})}async reviews(e){return this.get("reviews",e)}async productReviews(e,t={}){return this.reviews({...t,product:String(e)})}async myReviews(e){return this.client.requiresBasic("products()->myReviews"),this.get("reviews/mine",e)}async seo(e){return this.get(`${e}/seo`)}async seoBySlug(e){return this.get(`${e}/seo`)}stringifyParams(e){let t={};for(let[r,s]of Object.entries(e))s!=null&&(t[r]=String(s));return t}};var T=class extends h{endpoint="store";async info(e){return this.get("",e)}};var v=class extends h{endpoint="sessions";async all(e){return this.get("",e)}async find(e,t){return this.client.get("session/"+e,t)}async destroy(e){return this.client.delete("session/"+e)}async getItems(e){return this.client.get("session/"+e+"/items")}async bySession(e){return this.client.get("session/"+String(e))}async destroySession(e){return this.client.delete("session/"+String(e))}};var d=class{client;storage;tokenStorageKey="cocart_jwt_token";refreshTokenStorageKey="cocart_jwt_refresh_token";autoRefresh=!1;isRefreshing=!1;constructor(e,t=null,r={}){this.client=e,this.storage=t,r.autoRefresh!==void 0&&(this.autoRefresh=r.autoRefresh),r.tokenStorageKey&&(this.tokenStorageKey=r.tokenStorageKey),r.refreshTokenStorageKey&&(this.refreshTokenStorageKey=r.refreshTokenStorageKey)}async restoreTokensFromStorage(){if(!this.storage)return;let e=await this.storage.get(this.tokenStorageKey),t=await this.storage.get(this.refreshTokenStorageKey);e&&this.client.setJwtToken(e),t&&this.client.setRefreshToken(t)}async login(e,t){let r=await this.client.post("login",{username:e,password:t}),n=r.toObject().extras,a=n?.jwt_token,o=n?.jwt_refresh;if(a)this.client.setJwtToken(a),o&&this.client.setRefreshToken(o),await this.persistTokens();else throw new g("JWT token not found in login response. Is the CoCart JWT Authentication plugin installed?",0,"cocart_jwt_missing");return r}async refresh(e){let t=e??this.client.getRefreshToken();if(!t)throw new g("No refresh token available. Please login first.",0,"cocart_jwt_no_refresh_token");let r=`${this.client.getNamespace()}/jwt/refresh-token`,s=await this.client.requestRaw("POST",r,void 0,{refresh_token:t}),n=s.toObject(),a=n.token,o=n.refresh_token;return a&&this.client.setJwtToken(a),o&&this.client.setRefreshToken(o),await this.persistTokens(),s}async validate(){if(!this.client.hasJwtToken())return!1;try{let e=`${this.client.getNamespace()}/jwt/validate-token`;return(await this.client.requestRaw("POST",e)).isSuccessful()}catch(e){if(e instanceof g)return!1;throw e}}async withAutoRefresh(e){try{return await e(this.client)}catch(t){if(t instanceof g&&!this.isRefreshing&&this.client.getRefreshToken()){this.isRefreshing=!0;try{return await this.refresh(),await e(this.client)}catch{throw t}finally{this.isRefreshing=!1}}throw t}}async clearTokens(){return this.client.clearJwtToken(),this.storage&&(await this.storage.delete(this.tokenStorageKey),await this.storage.delete(this.refreshTokenStorageKey)),this}hasTokens(){return this.client.hasJwtToken()}isTokenExpired(e=30){let t=this.client.getJwtToken();if(!t)return!0;let r=this.decodeTokenPayload(t);return r?r.exp?Date.now()/1e3>=r.exp-e:!1:!0}getTokenExpiry(){let e=this.client.getJwtToken();return e?this.decodeTokenPayload(e)?.exp??null:null}setAutoRefresh(e){return this.autoRefresh=e,this}isAutoRefreshEnabled(){return this.autoRefresh}getClient(){return this.client}decodeTokenPayload(e){let t=e.split(".");if(t.length!==3)return null;try{let r=atob(t[1].replace(/-/g,"+").replace(/_/g,"/"));return JSON.parse(r)}catch{return null}}async persistTokens(){if(!this.storage)return;let e=this.client.getJwtToken(),t=this.client.getRefreshToken();e&&await this.storage.set(this.tokenStorageKey,e),t&&await this.storage.set(this.refreshTokenStorageKey,t)}};function F(){return typeof globalThis<"u"&&typeof globalThis.localStorage<"u"?new w:new R}var I=class i{static VERSION="1.0.0";static API_VERSION="v2";storeUrl;restPrefix="wp-json";namespace="cocart";storageKey="cocart_cart_key";cartKey=null;auth=null;jwtToken=null;refreshTokenValue=null;consumerKey=null;consumerSecret=null;maxRetries=0;timeout=3e4;customHeaders={};lastResponseValue=null;storage;debug=!1;authHeaderName="Authorization";responseTransformer=null;etagEnabled=!0;etagCache=new Map;mainPlugin="basic";listeners={};jwtManagerInstance=null;cartInstance=null;productsInstance=null;storeInstance=null;sessionsInstance=null;constructor(e,t={}){this.storeUrl=e.replace(/\/+$/,""),t.storage?this.storage=t.storage:t.encryptionKey?this.storage=new P(t.encryptionKey):this.storage=F(),t.cartKey&&(this.cartKey=t.cartKey),t.username&&t.password&&(this.auth={username:t.username,password:t.password}),t.jwtToken&&(this.jwtToken=t.jwtToken),t.jwtRefreshToken&&(this.refreshTokenValue=t.jwtRefreshToken),t.consumerKey&&t.consumerSecret&&(this.consumerKey=t.consumerKey,this.consumerSecret=t.consumerSecret),t.timeout!==void 0&&(this.timeout=t.timeout),t.restPrefix!==void 0&&(this.restPrefix=t.restPrefix.replace(/^\/+|\/+$/g,"")),t.namespace!==void 0&&(this.namespace=t.namespace.replace(/^\/+|\/+$/g,"")),t.headers&&(this.customHeaders={...t.headers}),t.storageKey&&(this.storageKey=t.storageKey),t.maxRetries!==void 0&&(this.maxRetries=Math.max(0,t.maxRetries)),t.debug!==void 0&&(this.debug=t.debug),t.authHeaderName&&(this.authHeaderName=t.authHeaderName),t.responseTransformer&&(this.responseTransformer=t.responseTransformer),t.etag!==void 0&&(this.etagEnabled=t.etag),t.mainPlugin&&(this.mainPlugin=t.mainPlugin)}static create(e){return new i(e)}async restoreSession(){if(this.cartKey===null){let e=await this.storage.get(this.storageKey);e&&(this.cartKey=e)}}setCartKey(e){return this.cartKey=e,this}getCartKey(){return this.cartKey}setAuth(e,t){return this.auth={username:e,password:t},this.jwtToken=null,this}setJwtToken(e){return this.jwtToken=e,this.auth=null,this}getJwtToken(){return this.jwtToken}setRefreshToken(e){return this.refreshTokenValue=e,this}getRefreshToken(){return this.refreshTokenValue}hasJwtToken(){return this.jwtToken!==null&&this.jwtToken!==""}clearJwtToken(){return this.jwtToken=null,this.refreshTokenValue=null,this}setWooCommerceCredentials(e,t){return this.consumerKey=e,this.consumerSecret=t,this}setTimeout(e){return this.timeout=e,this}setMaxRetries(e){return this.maxRetries=Math.max(0,e),this}setRestPrefix(e){return this.restPrefix=e.replace(/^\/+|\/+$/g,""),this}getRestPrefix(){return this.restPrefix}setNamespace(e){return this.namespace=e.replace(/^\/+|\/+$/g,""),this}getNamespace(){return this.namespace}addHeader(e,t){return this.customHeaders[e]=t,this}setStorage(e){return this.storage=e,this}getStorage(){return this.storage}setDebug(e){return this.debug=e,this}setAuthHeaderName(e){return this.authHeaderName=e,this}setETag(e){return this.etagEnabled=e,this}clearETagCache(){return this.etagCache.clear(),this}getMainPlugin(){return this.mainPlugin}setMainPlugin(e){return this.mainPlugin=e,this}requiresBasic(e){if(this.mainPlugin==="legacy")throw new f(e)}setResponseTransformer(e){return this.responseTransformer=e,this}on(e,t){return this.listeners[e]||(this.listeners[e]=new Set),this.listeners[e].add(t),this}off(e,t){return this.listeners[e]?.delete(t),this}emit(e,t){if(this.debug&&this.logDebug(e,t),this.listeners[e])for(let r of this.listeners[e])try{r(t)}catch{}}logDebug(e,t){let r="[CoCart]",s=t;switch(e){case"request":console.log(`${r} ${s.method} ${s.url}`);break;case"response":console.log(`${r} ${s.method} ${s.url} \u2192 ${s.status} (${s.duration}ms)`);break;case"error":console.error(`${r} ${s.method} ${s.url} \u2192 Error:`,s.error);break;case"retry":console.log(`${r} Retry ${s.attempt}/${s.maxRetries} after ${s.delay}ms (${s.reason})`);break;case"auth:refresh":console.log(`${r} JWT token refresh ${s.success?"succeeded":"failed"}`);break}}jwt(){return this.jwtManagerInstance===null&&(this.jwtManagerInstance=new d(this,this.storage,{autoRefresh:!0})),this.jwtManagerInstance}async login(e,t){return this.jwt().login(e,t)}async logout(){try{await this.post("logout")}catch{}return await this.jwt().clearTokens(),this}isAuthenticated(){return this.auth!==null||this.jwtToken!==null&&this.jwtToken!==""}isGuest(){return!this.isAuthenticated()}async clearSession(){return this.auth=null,this.jwtToken=null,this.refreshTokenValue=null,this.cartKey=null,await this.storage.delete(this.storageKey),this}async transferCartToCustomer(e,t){let r=this.cartKey;return this.setAuth(e,t),r?this.cart().get({cart_key:r}):this.cart().get()}cart(){return this.cartInstance===null&&(this.cartInstance=new b(this)),this.cartInstance}products(){return this.productsInstance===null&&(this.productsInstance=new k(this)),this.productsInstance}store(){return this.storeInstance===null&&(this.storeInstance=new T(this)),this.storeInstance}sessions(){return this.sessionsInstance===null&&(this.sessionsInstance=new v(this)),this.sessionsInstance}async get(e,t){return this.request("GET",e,t)}async post(e,t,r){return this.request("POST",e,r,t)}async put(e,t,r){return this.request("PUT",e,r,t)}async delete(e,t){return this.request("DELETE",e,t)}async request(e,t,r,s){try{return await this.executeRequest(e,t,r,s)}catch(n){if(n instanceof g&&this.jwtManagerInstance?.isAutoRefreshEnabled()&&this.refreshTokenValue!==null)try{return await this.jwtManagerInstance.refresh(),this.emit("auth:refresh",{success:!0}),await this.executeRequest(e,t,r,s)}catch{throw this.emit("auth:refresh",{success:!1}),n}throw n}}async requestRaw(e,t,r,s){let n=`${this.storeUrl}/${this.restPrefix}/${t.replace(/^\/+/,"")}`;r&&Object.keys(r).length>0&&(n+="?"+new URLSearchParams(r).toString());let a=this.buildHeaders(),o=s?JSON.stringify(s):void 0,u=await this.fetchWithTimeout(e,n,a,o),S=await u.text();return this.lastResponseValue=new y(u.status,u.headers,S),await this.extractCartKeyFromHeaders(this.lastResponseValue),u.status>=400&&this.handleErrorResponse(this.lastResponseValue),this.applyTransformer(this.lastResponseValue)}getLastResponse(){return this.lastResponseValue}getStoreUrl(){return this.storeUrl}async executeRequest(e,t,r,s){let n=this.buildUrl(t,r),a=this.buildHeaders(),o=s?JSON.stringify(s):void 0;if(e==="GET"&&this.etagEnabled&&!r?._skip_cache){let l=this.etagCache.get(n);l&&(a["If-None-Match"]=l)}this.emit("request",{method:e,url:n,headers:a,body:o});let u=0,S=Date.now();for(;;){let l;try{l=await this.fetchWithTimeout(e,n,a,o)}catch(p){if(u<this.maxRetries&&this.isTransientError(p)){u++;let q=this.getRetryDelay(u);this.emit("retry",{method:e,url:n,attempt:u,maxRetries:this.maxRetries,delay:q,reason:"transient_error"}),await this.retrySleep(u);continue}let M=p instanceof c?p:new c(p instanceof Error?p.message:"Network error",0,"network_error");throw this.emit("error",{method:e,url:n,error:M}),M}let B=await l.text(),A=Date.now()-S;if(this.lastResponseValue=new y(l.status,l.headers,B),await this.extractCartKeyFromHeaders(this.lastResponseValue),e==="GET"&&this.etagEnabled){let p=this.lastResponseValue.getETag();p&&this.etagCache.set(n,p)}if(u<this.maxRetries&&this.isRetryableStatus(l.status)){u++;let p=this.getRetryDelay(u,this.lastResponseValue);this.emit("retry",{method:e,url:n,attempt:u,maxRetries:this.maxRetries,delay:p,reason:`http_${l.status}`}),await this.retrySleep(u,this.lastResponseValue);continue}return l.status>=400&&(this.emit("response",{method:e,url:n,status:l.status,duration:A}),this.handleErrorResponse(this.lastResponseValue,e,n)),this.emit("response",{method:e,url:n,status:l.status,duration:A}),this.applyTransformer(this.lastResponseValue)}}async fetchWithTimeout(e,t,r,s){let n=new AbortController,a=setTimeout(()=>n.abort(),this.timeout);try{return await fetch(t,{method:e,headers:r,body:s,signal:n.signal})}catch(o){throw o instanceof DOMException&&o.name==="AbortError"?new c(`Request timed out after ${this.timeout}ms`,0,"request_timeout"):o}finally{clearTimeout(a)}}buildUrl(e,t){let r={...t??{}};this.cartKey&&!this.isAuthenticated()&&(r.cart_key=this.cartKey),this.mainPlugin==="legacy"?"_fields"in r&&!("fields"in r)&&(r.fields=r._fields,delete r._fields):"fields"in r&&!("_fields"in r)&&(r._fields=r.fields,delete r.fields);let s=`${this.storeUrl}/${this.restPrefix}/${this.namespace}/${i.API_VERSION}/${e.replace(/^\/+/,"")}`;return Object.keys(r).length>0&&(s+="?"+new URLSearchParams(r).toString()),s}buildHeaders(){let e={Accept:"application/json","Content-Type":"application/json","User-Agent":`CoCart-TS-SDK/${i.VERSION}`};return this.jwtToken?e[this.authHeaderName]=`Bearer ${this.jwtToken}`:this.auth?e[this.authHeaderName]=`Basic ${btoa(`${this.auth.username}:${this.auth.password}`)}`:this.consumerKey&&this.consumerSecret&&(e[this.authHeaderName]=`Basic ${btoa(`${this.consumerKey}:${this.consumerSecret}`)}`),this.cartKey&&!this.isAuthenticated()&&(e["Cart-Key"]=this.cartKey),{...e,...this.customHeaders}}async extractCartKeyFromHeaders(e){let t=e.getHeader("Cart-Key");t!==null&&(this.cartKey=t,await this.storage.set(this.storageKey,t))}handleErrorResponse(e,t,r){let s=e.toObject(),n=s.code??"unknown_error",a=s.message??"An unknown error occurred",o=e.statusCode,u=t&&r?`${t} ${r}: `:"",S=n!=="unknown_error"?` [${n}]`:"",l=`${u}${a}${S}`;throw o===401||o===403||typeof n=="string"&&n.includes("authenticat")?new g(l,o,n,s):o===400||typeof n=="string"&&(n.includes("invalid")||n.includes("missing"))?new m(l,o,n,s):new c(l,o,n,s)}isTransientError(e){if(e instanceof c){let t=e.message.toLowerCase();return t.includes("timeout")||t.includes("timed out")||t.includes("connection")}return!1}isRetryableStatus(e){return e===429||e===503}getRetryDelay(e,t){if(t){let r=t.getHeader("Retry-After");if(r!==null&&!isNaN(Number(r)))return Math.min(Number(r),60)*1e3}return Math.min(Math.pow(2,e-1),30)*1e3}async retrySleep(e,t){if(t){let r=t.getHeader("Retry-After");if(r!==null&&!isNaN(Number(r))){await this.sleep(Math.min(Number(r),60)*1e3);return}}await this.sleep(Math.min(Math.pow(2,e-1),30)*1e3)}applyTransformer(e){return this.responseTransformer?this.responseTransformer(e):e}sleep(e){return new Promise(t=>setTimeout(t,e))}};var _=class{client;storage;storageKey="cocart_cart_key";jwtManagerInstance=null;constructor(e,t=null){this.client=e,this.storage=t}setStorageKey(e){return this.storageKey=e,this}getCartKey(){return this.client.getCartKey()}async setCartKey(e){return this.client.setCartKey(e),this.storage&&await this.storage.set(this.storageKey,e),this}async initializeCart(){let t=(await this.client.cart().get()).getCartKey()??this.client.getCartKey();return t&&this.storage&&await this.storage.set(this.storageKey,t),t}async login(e,t,r=!0){let s=this.client.getCartKey();return this.client.setAuth(e,t),await this.clearStoredCartKey(),r&&s?this.client.cart().get({cart_key:s}):this.client.cart().get()}async loginWithToken(e){let t=this.client.getCartKey();return this.client.setJwtToken(e),await this.clearStoredCartKey(),t?this.client.cart().get({cart_key:t}):this.client.cart().get()}jwt(e={}){return this.jwtManagerInstance===null&&(this.jwtManagerInstance=new d(this.client,this.storage,e)),this.jwtManagerInstance}async loginWithJwt(e,t,r=!0){let s=this.client.getCartKey(),n=await this.jwt().login(e,t);return await this.clearStoredCartKey(),r&&s&&await this.client.cart().get({cart_key:s}),n}async logout(){return this.jwtManagerInstance&&await this.jwtManagerInstance.clearTokens(),await this.client.clearSession(),await this.clearStoredCartKey(),this}isAuthenticated(){return this.client.isAuthenticated()}isGuest(){return this.client.isGuest()}getClient(){return this.client}async clearStoredCartKey(){this.storage&&await this.storage.delete(this.storageKey)}};var j=class{format(e,t){let r=e/Math.pow(10,t.currency_minor_unit);try{return new Intl.NumberFormat(void 0,{style:"currency",currency:t.currency_code,minimumFractionDigits:t.currency_minor_unit,maximumFractionDigits:t.currency_minor_unit}).format(r)}catch{return`${t.currency_prefix}${r.toFixed(t.currency_minor_unit)}${t.currency_suffix}`}}formatDecimal(e,t){return(e/Math.pow(10,t.currency_minor_unit)).toFixed(t.currency_minor_unit)}};var E=class{detectTimezone(){return Intl.DateTimeFormat().resolvedOptions().timeZone}convert(e,t,r){let s=new Date(e);if(!e.includes("Z")&&!e.includes("+")&&!/\d{2}:\d{2}$/.test(e)){let n=new Intl.DateTimeFormat("en-US",{timeZone:t,year:"numeric",month:"2-digit",day:"2-digit",hour:"2-digit",minute:"2-digit",second:"2-digit",hour12:!1}).format(s),a=new Date(n);if(!isNaN(a.getTime()))return this.formatInTimezone(a,r)}return this.formatInTimezone(s,r)}toLocal(e,t="UTC"){return this.convert(e,t,this.detectTimezone())}formatInTimezone(e,t){return new Intl.DateTimeFormat("sv-SE",{timeZone:t,year:"numeric",month:"2-digit",day:"2-digit",hour:"2-digit",minute:"2-digit",second:"2-digit",hour12:!1}).format(e).replace(" ","T")}};return L(U);})(); | ||
| if(typeof CoCartSDK!=="undefined"){Object.assign(typeof window!=="undefined"?window:globalThis,CoCartSDK);} | ||
| //# sourceMappingURL=index.global.js.map |
Sorry, the diff of this file is too big to display
+4
-2
| { | ||
| "name": "@cocartheadless/sdk", | ||
| "version": "1.0.0", | ||
| "version": "1.1.0", | ||
| "description": "Official TypeScript SDK for the CoCart REST API", | ||
@@ -9,2 +9,4 @@ "type": "module", | ||
| "types": "./dist/index.d.ts", | ||
| "unpkg": "./dist/index.global.js", | ||
| "jsdelivr": "./dist/index.global.js", | ||
| "exports": { | ||
@@ -86,3 +88,3 @@ ".": { | ||
| "scripts": { | ||
| "build": "tsup --config tsup.config.ts && tsup --config tsup.adapters.ts", | ||
| "build": "tsup --config tsup.config.ts && tsup --config tsup.adapters.ts && tsup --config tsup.cdn.ts", | ||
| "dev": "tsup --watch", | ||
@@ -89,0 +91,0 @@ "typecheck": "tsc --noEmit", |
+43
-1
| # @cocartheadless/sdk | ||
| [](https://www.typescriptlang.org/) | ||
| [](https://www.npmjs.com/package/@cocartheadless/sdk) | ||
| [](https://www.jsdelivr.com/package/npm/@cocartheadless/sdk) | ||
| [](https://www.npmjs.com/package/@cocartheadless/sdk) | ||
| [](https://github.com/cocart-headless/cocart-js/actions/workflows/tests.yml) | ||
@@ -9,3 +13,3 @@ [](https://github.com/cocart-headless/cocart-js/blob/main/LICENSE) | ||
| > [!IMPORTANT] | ||
| > This SDK is still in development and not yet ready for production use. Provide feedback if you experience a bug. | ||
| > This SDK is looking for feedback, if you experience a bug please report it. | ||
@@ -83,2 +87,38 @@ ## TODO to complete the SDK | ||
| ### Via CDN (Framer, Webflow, plain HTML) | ||
| For platforms like **Framer**, **Webflow**, or any environment where you just need a `<script>` tag — no npm required: | ||
| **jsDelivr:** | ||
| ```html | ||
| <script src="https://cdn.jsdelivr.net/npm/@cocartheadless/sdk/dist/index.global.js"></script> | ||
| ``` | ||
| **unpkg:** | ||
| ```html | ||
| <script src="https://unpkg.com/@cocartheadless/sdk/dist/index.global.js"></script> | ||
| ``` | ||
| Then use it: | ||
| ```html | ||
| <script> | ||
| const client = new CoCart('https://your-store.com'); | ||
| </script> | ||
| ``` | ||
| This loads a single minified file that exposes all SDK exports under the `CoCart` global. See the dedicated guides for [Framer](docs/framer.md) and [Webflow](docs/webflow.md). | ||
| You can also pin a specific version: | ||
| ```html | ||
| <!-- jsDelivr --> | ||
| <script src="https://cdn.jsdelivr.net/npm/@cocartheadless/sdk@1.1.0/dist/index.global.js"></script> | ||
| <!-- unpkg --> | ||
| <script src="https://unpkg.com/@cocartheadless/sdk@1.1.0/dist/index.global.js"></script> | ||
| ``` | ||
| ## Quick Start | ||
@@ -145,2 +185,4 @@ | ||
| | [Deno](src/adapters/deno/README.md) | `Deno.serve()`, Fresh islands | | ||
| | [Framer](docs/framer.md) | CDN script, Code Overrides, product display | | ||
| | [Webflow](docs/webflow.md) | CDN script, custom code, dynamic elements | | ||
@@ -147,0 +189,0 @@ ## Features |
Network access
Supply chain riskThis module accesses the network.
Found 1 instance in 1 package
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
Minified code
QualityThis package contains minified code. This may be harmless in some cases where minified code is included in packaged libraries, however packages on npm should not minify code.
Found 1 instance in 1 package
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
Found 1 instance in 1 package
Network access
Supply chain riskThis module accesses the network.
Found 1 instance in 1 package
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
Found 1 instance in 1 package
690066
28.36%40
5.26%6086
1.87%316
15.33%2
100%32
3.23%