@gamifyio/core 0.1.4 → 0.1.5

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/dist/index.cjs CHANGED
@@ -1,3 +1,3 @@
1
- 'use strict';var k=Object.defineProperty;var C=(n,e,t)=>e in n?k(n,e,{enumerable:true,configurable:true,writable:true,value:t}):n[e]=t;var r=(n,e,t)=>C(n,typeof e!="symbol"?e+"":e,t);var y=class{constructor(e){this.prefix=e;}getKey(e){return `${this.prefix}${e}`}get(e){try{let t=localStorage.getItem(this.getKey(e));return t===null?null:JSON.parse(t)}catch{return null}}set(e,t){try{localStorage.setItem(this.getKey(e),JSON.stringify(t));}catch{}}remove(e){try{localStorage.removeItem(this.getKey(e));}catch{}}clear(){try{let e=Object.keys(localStorage);for(let t of e)t.startsWith(this.prefix)&&localStorage.removeItem(t);}catch{}}},m=class{constructor(e){this.prefix=e;r(this,"store",new Map);}getKey(e){return `${this.prefix}${e}`}get(e){let t=this.store.get(this.getKey(e));if(t===void 0)return null;try{return JSON.parse(t)}catch{return null}}set(e,t){this.store.set(this.getKey(e),JSON.stringify(t));}remove(e){this.store.delete(this.getKey(e));}clear(){let e=[];for(let t of this.store.keys())t.startsWith(this.prefix)&&e.push(t);for(let t of e)this.store.delete(t);}};function L(){try{let n="__gamify_test__";return localStorage.setItem(n,"test"),localStorage.removeItem(n),!0}catch{return false}}function p(n){return typeof window<"u"&&L()?new y(n):new m(n)}var A="event_queue";function x(){return `${Date.now()}-${Math.random().toString(36).substring(2,11)}`}var l=class{constructor(e,t=100){r(this,"queue",[]);r(this,"maxSize");r(this,"storage");this.storage=e,this.maxSize=t,this.loadFromStorage();}loadFromStorage(){let e=this.storage.get(A);e&&Array.isArray(e)&&(this.queue=e);}saveToStorage(){this.storage.set(A,this.queue);}enqueue(e){let t={id:x(),event:e,attempts:0,createdAt:Date.now()};for(this.queue.push(t);this.queue.length>this.maxSize;)this.queue.shift();return this.saveToStorage(),t.id}peek(e){return this.queue.filter(t=>t.attempts<3).slice(0,e)}acknowledge(e){let t=new Set(e);this.queue=this.queue.filter(s=>!t.has(s.id)),this.saveToStorage();}nack(e){let t=new Set(e);for(let s of this.queue)t.has(s.id)&&s.attempts++;this.queue=this.queue.filter(s=>s.attempts<3),this.saveToStorage();}size(){return this.queue.length}hasPending(){return this.queue.some(e=>e.attempts<3)}clear(){this.queue=[],this.saveToStorage();}};var d=class{constructor(e,t,s=false){r(this,"endpoint");r(this,"apiKey");r(this,"debug");this.endpoint=e,this.apiKey=t,this.debug=s;}log(e,t){this.debug&&console.log(`[Gamify] ${e}`,t??"");}async get(e){this.log(`GET ${e}`);try{let t=await fetch(`${this.endpoint}${e}`,{method:"GET",headers:{"Content-Type":"application/json","X-API-Key":this.apiKey}});if(!t.ok){let o=await t.text();return this.log(`GET error: ${t.status}`,o),{success:!1,error:`HTTP ${t.status}: ${o}`}}return {success:!0,data:await t.json()}}catch(t){let s=t instanceof Error?t.message:"Unknown error";return this.log("GET network error",s),{success:false,error:s}}}async post(e,t){this.log(`POST ${e}`,t);try{let s=await fetch(`${this.endpoint}${e}`,{method:"POST",headers:{"Content-Type":"application/json","X-API-Key":this.apiKey},body:JSON.stringify(t)});if(!s.ok){let i=await s.text();return this.log(`POST error: ${s.status}`,i),{success:!1,error:`HTTP ${s.status}: ${i}`}}return {success:!0,data:await s.json()}}catch(s){let o=s instanceof Error?s.message:"Unknown error";return this.log("POST network error",o),{success:false,error:o}}}async sendEvents(e){if(e.length===0)return {success:true};this.log(`Sending ${e.length} events`,e);try{let t=await fetch(`${this.endpoint}/events/batch`,{method:"POST",headers:{"Content-Type":"application/json","X-API-Key":this.apiKey},body:JSON.stringify({events:e}),keepalive:!0});if(!t.ok){let s=await t.text();return this.log(`API error: ${t.status}`,s),{success:!1,error:`HTTP ${t.status}: ${s}`}}return this.log("Events sent successfully"),{success:!0}}catch(t){let s=t instanceof Error?t.message:"Unknown error";return this.log("Network error",s),{success:false,error:s}}}sendBeacon(e){if(e.length===0)return true;if(this.log(`Sending ${e.length} events via beacon`),typeof navigator<"u"&&navigator.sendBeacon){let t=new Blob([JSON.stringify({events:e,apiKey:this.apiKey})],{type:"application/json"});return navigator.sendBeacon(`${this.endpoint}/events/batch`,t)}return fetch(`${this.endpoint}/events/batch`,{method:"POST",headers:{"Content-Type":"application/json","X-API-Key":this.apiKey},body:JSON.stringify({events:e}),keepalive:true}).catch(()=>{}),true}};var S="session_token",h="session_data",g=class{constructor(e,t,s){r(this,"client");r(this,"storage");r(this,"debug");r(this,"sessionToken",null);r(this,"cachedSession",null);this.client=t,this.storage=s,this.debug=e.debug,this.sessionToken=this.storage.get(S),this.cachedSession=this.storage.get(h);}async updateCart(e,t,s,o){let i={userId:e,items:t,coupons:s,currency:o};this.log("Updating cart",i);let a=await this.client.post("/v1/sessions",i);if(a.success&&a.data)return this.sessionToken=a.data.sessionToken,this.cachedSession=a.data,this.storage.set(S,this.sessionToken),this.storage.set(h,this.cachedSession),this.log("Cart updated",a.data),a.data;throw new Error(a.error||"Failed to update cart")}async getSession(){if(!this.sessionToken)return null;this.log("Getting session",this.sessionToken);let e=await this.client.get(`/v1/sessions/${this.sessionToken}`);return e.success&&e.data?(this.cachedSession=e.data,this.storage.set(h,this.cachedSession),e.data):(e.error?.includes("not found")&&this.clearSession(),null)}async applyCoupon(e){if(!this.sessionToken)throw new Error("No active session");this.log("Applying coupon",e);let t=await this.client.post(`/v1/sessions/${this.sessionToken}/coupons`,{code:e});if(t.success&&t.data)return this.cachedSession=t.data,this.storage.set(h,this.cachedSession),this.log("Coupon applied",t.data),t.data;throw new Error(t.error||"Failed to apply coupon")}async complete(){if(!this.sessionToken)throw new Error("No active session");this.log("Completing session");let e=await this.client.post(`/v1/sessions/${this.sessionToken}/complete`,{});if(e.success&&e.data)return this.cachedSession=e.data,this.storage.set(h,this.cachedSession),this.log("Session completed",e.data),e.data;throw new Error(e.error||"Failed to complete session")}clearSession(){this.sessionToken=null,this.cachedSession=null,this.storage.remove(S),this.storage.remove(h),this.log("Session cleared");}getCachedSession(){return this.cachedSession}getSessionToken(){return this.sessionToken}hasActiveSession(){return this.sessionToken!==null&&this.cachedSession?.status==="active"}log(e,t){this.debug&&console.log(`[Gamify:Session] ${e}`,t??"");}};var v="loyalty_profile",f=class{constructor(e,t,s){r(this,"client");r(this,"storage");r(this,"debug");r(this,"cachedProfile",null);this.client=t,this.storage=s,this.debug=e.debug,this.cachedProfile=this.storage.get(v);}async getProfile(e){this.log("Getting loyalty profile",e);let t=await this.client.get(`/v1/customer/profile?userId=${encodeURIComponent(e)}`);if(t.success&&t.data)return this.cachedProfile=t.data,this.storage.set(v,this.cachedProfile),this.log("Profile loaded",t.data),t.data;throw new Error(t.error||"Failed to get loyalty profile")}async getHistory(e,t,s){this.log("Getting loyalty history",{userId:e,limit:t,offset:s});let o=new URLSearchParams;o.set("userId",e),t!==void 0&&o.set("limit",String(t)),s!==void 0&&o.set("offset",String(s));let i=await this.client.get(`/v1/customer/history?${o.toString()}`);if(i.success&&i.data)return this.log("History loaded",i.data),i.data;throw new Error(i.error||"Failed to get loyalty history")}getCachedProfile(){return this.cachedProfile}getPoints(){return this.cachedProfile?.points??0}getTier(){return this.cachedProfile?.tier??null}getNextTier(){return this.cachedProfile?.nextTier??null}clearCache(){this.cachedProfile=null,this.storage.remove(v),this.log("Cache cleared");}async refresh(e){return this.getProfile(e)}log(e,t){this.debug&&console.log(`[Gamify:Loyalty] ${e}`,t??"");}};var w="__gamify_referrer",R="__gamify_referrer_detected_at",c=class{constructor(e,t){r(this,"config");r(this,"storage");r(this,"referrerCode",null);this.config=e,this.storage=t,this.referrerCode=this.storage.get(w),this.detectReferrerFromUrl();}log(e,t){this.config.debug&&console.log(`[Gamify:Referral] ${e}`,t??"");}detectReferrerFromUrl(){if(typeof window>"u")return null;try{let t=new URLSearchParams(window.location.search).get("ref");if(t&&t.length>0)return this.log("Detected referrer from URL",{refCode:t}),this.setReferrer(t),t}catch(e){this.log("Error detecting referrer from URL",e);}return null}setReferrer(e){if(!e||e.length===0){this.log("Invalid referrer code provided");return}this.referrerCode=e,this.storage.set(w,e),this.storage.set(R,new Date().toISOString()),this.log("Referrer code stored",{code:e});}getReferrer(){return this.referrerCode}hasReferrer(){return this.referrerCode!==null}clearReferrer(){this.referrerCode=null,this.storage.remove(w),this.storage.remove(R),this.log("Referrer code cleared");}getReferrerProperties(){return this.referrerCode?{referrer:this.referrerCode,referrer_detected_at:this.storage.get(R)}:{}}};var T="affiliate_stats";var u=class{constructor(e,t,s){r(this,"config");r(this,"client");r(this,"storage");r(this,"cachedStats",null);r(this,"lastFetchTime",0);this.config=e,this.client=t,this.storage=s;let o=this.storage.get(T);o&&(this.cachedStats=o.stats,this.lastFetchTime=o.fetchedAt);}log(e,t){this.config.debug&&console.log(`[Gamify:Affiliate] ${e}`,t??"");}async getStats(e,t=false){let s=Date.now();if(!t&&this.cachedStats&&s-this.lastFetchTime<6e4&&this.cachedStats)return this.log("Returning cached affiliate stats"),this.cachedStats;this.log("Fetching affiliate stats from API",{userId:e});try{let i=await this.client.get(`/v1/customer/affiliate/profile?userId=${encodeURIComponent(e)}`);if(i.success&&i.data?.stats){let a=i.data.stats;return this.cachedStats=a,this.lastFetchTime=s,this.storage.set(T,{stats:a,fetchedAt:s}),this.log("Affiliate stats fetched",a),a}throw new Error(i.error??"Invalid response from affiliate stats API")}catch(i){throw this.log("Error fetching affiliate stats",i),i}}getCachedStats(){return this.cachedStats}async getLeaderboard(e=10){this.log("Fetching leaderboard",{limit:e});try{let t=await this.client.get(`/v1/customer/affiliate/leaderboard?limit=${e}`);if(t.success&&t.data?.entries)return this.log("Leaderboard fetched",{count:t.data.entries.length}),t.data;throw new Error(t.error??"Invalid response from leaderboard API")}catch(t){throw this.log("Error fetching leaderboard",t),t}}clearCache(){this.cachedStats=null,this.lastFetchTime=0,this.storage.remove(T),this.log("Affiliate stats cache cleared");}};var G="https://boostapi-production.up.railway.app",U=1e4,$=10,q="gamify_",E="user_id",I="anonymous_id",b="user_traits",F=["purchase","checkout_complete","checkout_success","commission.created","referral_success","user.leveled_up","step.completed","quest.completed"];function P(){return `anon_${Date.now()}_${Math.random().toString(36).substring(2,11)}`}var _=class{constructor(e){r(this,"config");r(this,"storage");r(this,"queue");r(this,"client");r(this,"_session");r(this,"_loyalty");r(this,"_referral");r(this,"_affiliate");r(this,"flushTimer",null);r(this,"anonymousId");r(this,"userId",null);r(this,"userTraits",null);r(this,"initialized",false);if(!e.apiKey)throw new Error("[Gamify] API key is required");if(e.apiKey.startsWith("sk_"))throw new Error("[Gamify] Secret keys (sk_live_*) cannot be used in the browser. Use a publishable key (pk_live_*) for client-side tracking. For server-side tracking, use @gamifyio/node instead.");this.config={apiKey:e.apiKey,endpoint:e.endpoint??G,debug:e.debug??false,flushInterval:e.flushInterval??U,maxBatchSize:e.maxBatchSize??$,storagePrefix:e.storagePrefix??q},this.storage=p(this.config.storagePrefix),this.queue=new l(this.storage),this.client=new d(this.config.endpoint,this.config.apiKey,this.config.debug),this._session=new g(this.config,this.client,this.storage),this._loyalty=new f(this.config,this.client,this.storage),this._referral=new c(this.config,this.storage),this._affiliate=new u(this.config,this.client,this.storage);let t=this.storage.get(I);this.anonymousId=t??P(),t||this.storage.set(I,this.anonymousId),this.userId=this.storage.get(E),this.userTraits=this.storage.get(b),this.initialize();}initialize(){this.initialized||(this.log("Initializing SDK"),this.startFlushTimer(),typeof window<"u"&&(window.addEventListener("beforeunload",()=>this.onBeforeUnload()),window.addEventListener("pagehide",()=>this.onBeforeUnload())),this.initialized=true,this.log("SDK initialized",{anonymousId:this.anonymousId,userId:this.userId}));}log(e,t){this.config.debug&&console.log(`[Gamify] ${e}`,t??"");}startFlushTimer(){this.flushTimer&&clearInterval(this.flushTimer),this.flushTimer=setInterval(()=>{this.flush();},this.config.flushInterval);}onBeforeUnload(){this.flushTimer&&(clearInterval(this.flushTimer),this.flushTimer=null);let e=this.queue.peek(this.config.maxBatchSize);if(e.length>0){let t=e.map(s=>s.event);this.client.sendBeacon(t);}}identify(e,t){if(!e||typeof e!="string"){this.log("Invalid user ID provided");return}this.log("Identifying user",{userId:e,traits:t}),this.userId=e,this.storage.set(E,e),t&&(this.userTraits={...this.userTraits,...t},this.storage.set(b,this.userTraits)),this.track("$identify",{userId:e,traits:t??{}});}track(e,t){if(!e||typeof e!="string"){this.log("Invalid event type provided");return}F.includes(e)&&console.warn(`[Gamify] Event "${e}" requires a secret key and will be rejected. Use @gamifyio/node on your server to track this event.`);let s=this._referral.getReferrerProperties(),o={type:e,properties:{...s,...t},timestamp:new Date().toISOString(),anonymousId:this.anonymousId,...this.userId&&{userId:this.userId}};this.log("Tracking event",o),this.queue.enqueue(o),this.queue.size()>=this.config.maxBatchSize&&this.flush();}async flush(){let e=this.queue.peek(this.config.maxBatchSize);if(e.length===0)return;let t=e.map(i=>i.event),s=e.map(i=>i.id);this.log(`Flushing ${t.length} events`),(await this.client.sendEvents(t)).success?this.queue.acknowledge(s):this.queue.nack(s);}reset(){this.log("Resetting SDK state"),this.userId=null,this.userTraits=null,this.storage.remove(E),this.storage.remove(b),this.anonymousId=P(),this.storage.set(I,this.anonymousId);}getUserId(){return this.userId}getAnonymousId(){return this.anonymousId}getPendingCount(){return this.queue.size()}shutdown(){this.log("Shutting down SDK"),this.flushTimer&&(clearInterval(this.flushTimer),this.flushTimer=null);let e=this.queue.peek(this.config.maxBatchSize);if(e.length>0){let t=e.map(s=>s.event);this.client.sendBeacon(t);}}get session(){return this._session}async updateCart(e,t,s){if(!this.userId)throw new Error("[Gamify] User must be identified before updating cart");return this._session.updateCart(this.userId,e,t,s)}get loyalty(){return this._loyalty}async getLoyaltyProfile(){if(!this.userId)throw new Error("[Gamify] User must be identified before getting loyalty profile");return this._loyalty.getProfile(this.userId)}async getLoyaltyHistory(e,t){if(!this.userId)throw new Error("[Gamify] User must be identified before getting loyalty history");return this._loyalty.getHistory(this.userId,e,t)}get referral(){return this._referral}getReferrer(){return this._referral.getReferrer()}setReferrer(e){this._referral.setReferrer(e);}get affiliate(){return this._affiliate}async getAffiliateStats(e=false){if(!this.userId)throw new Error("[Gamify] User must be identified before getting affiliate stats");return this._affiliate.getStats(this.userId,e)}async getLeaderboard(e=10){return this._affiliate.getLeaderboard(e)}async getQuests(){if(!this.userId)throw new Error("[Gamify] User must be identified before getting quests");let e=await this.client.get(`/v1/customer/quests?userId=${encodeURIComponent(this.userId)}`);if(e.success&&e.data)return e.data;throw new Error(e.error||"Failed to get quests")}async getStreaks(){if(!this.userId)throw new Error("[Gamify] User must be identified before getting streaks");let e=await this.client.get(`/v1/customer/streaks?userId=${encodeURIComponent(this.userId)}`);if(e.success&&e.data)return e.data;throw new Error(e.error||"Failed to get streaks")}async useStreakFreeze(e){if(!this.userId)throw new Error("[Gamify] User must be identified before using freeze");let t=await this.client.post(`/v1/customer/streaks/${encodeURIComponent(e)}/freeze`,{userId:this.userId});if(t.success&&t.data)return t.data;throw new Error(t.error||"Failed to use freeze")}async getBadges(e){if(!this.userId)throw new Error("[Gamify] User must be identified before getting badges");let t=`/v1/customer/badges?userId=${encodeURIComponent(this.userId)}`;e&&(t+=`&category=${encodeURIComponent(e)}`);let s=await this.client.get(t);if(s.success&&s.data)return s.data;throw new Error(s.error||"Failed to get badges")}async getBadgeCategories(){let e=await this.client.get("/v1/customer/badges/categories");if(e.success&&e.data)return e.data;throw new Error(e.error||"Failed to get badge categories")}async getRewardsStore(){if(!this.userId)throw new Error("[Gamify] User must be identified before getting rewards");let e=await this.client.get(`/v1/customer/store?userId=${encodeURIComponent(this.userId)}`);if(e.success&&e.data)return e.data;throw new Error(e.error||"Failed to get rewards store")}async redeemReward(e){if(!this.userId)throw new Error("[Gamify] User must be identified before redeeming rewards");let t=await this.client.post("/v1/customer/store/redeem",{userId:this.userId,itemId:e});if(t.success&&t.data)return t.data;throw new Error(t.error||"Failed to redeem reward")}};
2
- exports.AffiliateManager=u;exports.EventQueue=l;exports.Gamify=_;exports.HttpClient=d;exports.LoyaltyManager=f;exports.ReferralManager=c;exports.SessionManager=g;exports.createStorage=p;//# sourceMappingURL=index.cjs.map
1
+ 'use strict';var P=Object.defineProperty;var x=(o,e,t)=>e in o?P(o,e,{enumerable:true,configurable:true,writable:true,value:t}):o[e]=t;var s=(o,e,t)=>x(o,typeof e!="symbol"?e+"":e,t);var m=class{constructor(e){this.prefix=e;}getKey(e){return `${this.prefix}${e}`}get(e){try{let t=localStorage.getItem(this.getKey(e));return t===null?null:JSON.parse(t)}catch{return null}}set(e,t){try{localStorage.setItem(this.getKey(e),JSON.stringify(t));}catch{}}remove(e){try{localStorage.removeItem(this.getKey(e));}catch{}}clear(){try{let e=Object.keys(localStorage);for(let t of e)t.startsWith(this.prefix)&&localStorage.removeItem(t);}catch{}}},y=class{constructor(e){this.prefix=e;s(this,"store",new Map);}getKey(e){return `${this.prefix}${e}`}get(e){let t=this.store.get(this.getKey(e));if(t===void 0)return null;try{return JSON.parse(t)}catch{return null}}set(e,t){this.store.set(this.getKey(e),JSON.stringify(t));}remove(e){this.store.delete(this.getKey(e));}clear(){let e=[];for(let t of this.store.keys())t.startsWith(this.prefix)&&e.push(t);for(let t of e)this.store.delete(t);}};function _(){try{let o="__gamify_test__";return localStorage.setItem(o,"test"),localStorage.removeItem(o),!0}catch{return false}}function p(o){return typeof window<"u"&&_()?new m(o):new y(o)}var k="event_queue";function L(){return `${Date.now()}-${Math.random().toString(36).substring(2,11)}`}var l=class{constructor(e,t=100){s(this,"queue",[]);s(this,"maxSize");s(this,"storage");this.storage=e,this.maxSize=t,this.loadFromStorage();}loadFromStorage(){let e=this.storage.get(k);e&&Array.isArray(e)&&(this.queue=e);}saveToStorage(){this.storage.set(k,this.queue);}enqueue(e){let t={id:L(),event:e,attempts:0,createdAt:Date.now()};for(this.queue.push(t);this.queue.length>this.maxSize;)this.queue.shift();return this.saveToStorage(),t.id}peek(e){return this.queue.filter(t=>t.attempts<3).slice(0,e)}acknowledge(e){let t=new Set(e);this.queue=this.queue.filter(r=>!t.has(r.id)),this.saveToStorage();}nack(e){let t=new Set(e);for(let r of this.queue)t.has(r.id)&&r.attempts++;this.queue=this.queue.filter(r=>r.attempts<3),this.saveToStorage();}size(){return this.queue.length}hasPending(){return this.queue.some(e=>e.attempts<3)}clear(){this.queue=[],this.saveToStorage();}};var d=class{constructor(e,t,r=false){s(this,"endpoint");s(this,"apiKey");s(this,"debug");this.endpoint=e,this.apiKey=t,this.debug=r;}log(e,t){this.debug&&console.log(`[Gamify] ${e}`,t??"");}async get(e){this.log(`GET ${e}`);try{let t=await fetch(`${this.endpoint}${e}`,{method:"GET",headers:{"Content-Type":"application/json","X-API-Key":this.apiKey}});if(!t.ok){let n=await t.text();return this.log(`GET error: ${t.status}`,n),{success:!1,error:`HTTP ${t.status}: ${n}`}}return {success:!0,data:await t.json()}}catch(t){let r=t instanceof Error?t.message:"Unknown error";return this.log("GET network error",r),{success:false,error:r}}}async post(e,t){this.log(`POST ${e}`,t);try{let r=await fetch(`${this.endpoint}${e}`,{method:"POST",headers:{"Content-Type":"application/json","X-API-Key":this.apiKey},body:JSON.stringify(t)});if(!r.ok){let i=await r.text();return this.log(`POST error: ${r.status}`,i),{success:!1,error:`HTTP ${r.status}: ${i}`}}return {success:!0,data:await r.json()}}catch(r){let n=r instanceof Error?r.message:"Unknown error";return this.log("POST network error",n),{success:false,error:n}}}async sendEvents(e){if(e.length===0)return {success:true};this.log(`Sending ${e.length} events`,e);try{let t=await fetch(`${this.endpoint}/events/batch`,{method:"POST",headers:{"Content-Type":"application/json","X-API-Key":this.apiKey},body:JSON.stringify({events:e}),keepalive:!0});if(!t.ok){let r=await t.text();return this.log(`API error: ${t.status}`,r),{success:!1,error:`HTTP ${t.status}: ${r}`}}return this.log("Events sent successfully"),{success:!0}}catch(t){let r=t instanceof Error?t.message:"Unknown error";return this.log("Network error",r),{success:false,error:r}}}sendBeacon(e){if(e.length===0)return true;if(this.log(`Sending ${e.length} events via beacon`),typeof navigator<"u"&&navigator.sendBeacon){let t=new Blob([JSON.stringify({events:e,apiKey:this.apiKey})],{type:"application/json"});return navigator.sendBeacon(`${this.endpoint}/events/batch`,t)}return fetch(`${this.endpoint}/events/batch`,{method:"POST",headers:{"Content-Type":"application/json","X-API-Key":this.apiKey},body:JSON.stringify({events:e}),keepalive:true}).catch(()=>{}),true}};var S="session_token",g="session_data",f=class{constructor(e,t,r){s(this,"client");s(this,"storage");s(this,"debug");s(this,"sessionToken",null);s(this,"cachedSession",null);this.client=t,this.storage=r,this.debug=e.debug,this.sessionToken=this.storage.get(S),this.cachedSession=this.storage.get(g);}async updateCart(e,t,r,n){let i={userId:e,items:t,coupons:r,currency:n};this.log("Updating cart",i);let a=await this.client.post("/v1/sessions",i);if(a.success&&a.data)return this.sessionToken=a.data.sessionToken,this.cachedSession=a.data,this.storage.set(S,this.sessionToken),this.storage.set(g,this.cachedSession),this.log("Cart updated",a.data),a.data;throw new Error(a.error||"Failed to update cart")}async getSession(){if(!this.sessionToken)return null;this.log("Getting session",this.sessionToken);let e=await this.client.get(`/v1/sessions/${this.sessionToken}`);return e.success&&e.data?(this.cachedSession=e.data,this.storage.set(g,this.cachedSession),e.data):(e.error?.includes("not found")&&this.clearSession(),null)}async applyCoupon(e){if(!this.sessionToken)throw new Error("No active session");this.log("Applying coupon",e);let t=await this.client.post(`/v1/sessions/${this.sessionToken}/coupons`,{code:e});if(t.success&&t.data)return this.cachedSession=t.data,this.storage.set(g,this.cachedSession),this.log("Coupon applied",t.data),t.data;throw new Error(t.error||"Failed to apply coupon")}async complete(){if(!this.sessionToken)throw new Error("No active session");this.log("Completing session");let e=await this.client.post(`/v1/sessions/${this.sessionToken}/complete`,{});if(e.success&&e.data)return this.cachedSession=e.data,this.storage.set(g,this.cachedSession),this.log("Session completed",e.data),e.data;throw new Error(e.error||"Failed to complete session")}clearSession(){this.sessionToken=null,this.cachedSession=null,this.storage.remove(S),this.storage.remove(g),this.log("Session cleared");}getCachedSession(){return this.cachedSession}getSessionToken(){return this.sessionToken}hasActiveSession(){return this.sessionToken!==null&&this.cachedSession?.status==="active"}log(e,t){this.debug&&console.log(`[Gamify:Session] ${e}`,t??"");}};var v="loyalty_profile",h=class{constructor(e,t,r){s(this,"client");s(this,"storage");s(this,"debug");s(this,"cachedProfile",null);this.client=t,this.storage=r,this.debug=e.debug,this.cachedProfile=this.storage.get(v);}async getProfile(e){this.log("Getting loyalty profile",e);let t=await this.client.get(`/v1/customer/profile?userId=${encodeURIComponent(e)}`);if(t.success&&t.data)return this.cachedProfile=t.data,this.storage.set(v,this.cachedProfile),this.log("Profile loaded",t.data),t.data;throw new Error(t.error||"Failed to get loyalty profile")}async getHistory(e,t,r){this.log("Getting loyalty history",{userId:e,limit:t,offset:r});let n=new URLSearchParams;n.set("userId",e),t!==void 0&&n.set("limit",String(t)),r!==void 0&&n.set("offset",String(r));let i=await this.client.get(`/v1/customer/history?${n.toString()}`);if(i.success&&i.data)return this.log("History loaded",i.data),i.data;throw new Error(i.error||"Failed to get loyalty history")}getCachedProfile(){return this.cachedProfile}getPoints(){return this.cachedProfile?.points??0}getTier(){return this.cachedProfile?.tier??null}getNextTier(){return this.cachedProfile?.nextTier??null}clearCache(){this.cachedProfile=null,this.storage.remove(v),this.log("Cache cleared");}async refresh(e){return this.getProfile(e)}log(e,t){this.debug&&console.log(`[Gamify:Loyalty] ${e}`,t??"");}};var b="__gamify_referrer",R="__gamify_referrer_detected_at",u=class{constructor(e,t){s(this,"config");s(this,"storage");s(this,"referrerCode",null);this.config=e,this.storage=t,this.referrerCode=this.storage.get(b),this.detectReferrerFromUrl();}log(e,t){this.config.debug&&console.log(`[Gamify:Referral] ${e}`,t??"");}detectReferrerFromUrl(){if(typeof window>"u")return null;try{let t=new URLSearchParams(window.location.search).get("ref");if(t&&t.length>0)return this.log("Detected referrer from URL",{refCode:t}),this.setReferrer(t),t}catch(e){this.log("Error detecting referrer from URL",e);}return null}setReferrer(e){if(!e||e.length===0){this.log("Invalid referrer code provided");return}this.referrerCode=e,this.storage.set(b,e),this.storage.set(R,new Date().toISOString()),this.log("Referrer code stored",{code:e});}getReferrer(){return this.referrerCode}hasReferrer(){return this.referrerCode!==null}clearReferrer(){this.referrerCode=null,this.storage.remove(b),this.storage.remove(R),this.log("Referrer code cleared");}getReferrerProperties(){return this.referrerCode?{referrer:this.referrerCode,referrer_detected_at:this.storage.get(R)}:{}}};var w="affiliate_stats";var c=class{constructor(e,t,r){s(this,"config");s(this,"client");s(this,"storage");s(this,"cachedStats",null);s(this,"lastFetchTime",0);this.config=e,this.client=t,this.storage=r;let n=this.storage.get(w);n&&(this.cachedStats=n.stats,this.lastFetchTime=n.fetchedAt);}log(e,t){this.config.debug&&console.log(`[Gamify:Affiliate] ${e}`,t??"");}async getStats(e,t=false){let r=Date.now();if(!t&&this.cachedStats&&r-this.lastFetchTime<6e4&&this.cachedStats)return this.log("Returning cached affiliate stats"),this.cachedStats;this.log("Fetching affiliate stats from API",{userId:e});try{let i=await this.client.get(`/v1/customer/affiliate/profile?userId=${encodeURIComponent(e)}`);if(i.success&&i.data?.stats){let a=i.data.stats;return this.cachedStats=a,this.lastFetchTime=r,this.storage.set(w,{stats:a,fetchedAt:r}),this.log("Affiliate stats fetched",a),a}throw new Error(i.error??"Invalid response from affiliate stats API")}catch(i){throw this.log("Error fetching affiliate stats",i),i}}getCachedStats(){return this.cachedStats}async getLeaderboard(e=10){this.log("Fetching leaderboard",{limit:e});try{let t=await this.client.get(`/v1/customer/affiliate/leaderboard?limit=${e}`);if(t.success&&t.data?.entries)return this.log("Leaderboard fetched",{count:t.data.entries.length}),t.data;throw new Error(t.error??"Invalid response from leaderboard API")}catch(t){throw this.log("Error fetching leaderboard",t),t}}clearCache(){this.cachedStats=null,this.lastFetchTime=0,this.storage.remove(w),this.log("Affiliate stats cache cleared");}};var F="https://boostapi-production.up.railway.app",U=1e4,G=10,q="gamify_",T="user_id",E="anonymous_id",I="user_traits",$=["purchase","checkout_complete","checkout_success","commission.created","referral_success","user.leveled_up","step.completed","quest.completed"];function C(){return `anon_${Date.now()}_${Math.random().toString(36).substring(2,11)}`}var A=class{constructor(e){s(this,"config");s(this,"storage");s(this,"queue");s(this,"client");s(this,"_session");s(this,"_loyalty");s(this,"_referral");s(this,"_affiliate");s(this,"flushTimer",null);s(this,"anonymousId");s(this,"userId",null);s(this,"userTraits",null);s(this,"initialized",false);if(!e.apiKey)throw new Error("[Gamify] API key is required");if(e.apiKey.startsWith("sk_"))throw new Error("[Gamify] Secret keys (sk_live_*) cannot be used in the browser. Use a publishable key (pk_live_*) for client-side tracking. For server-side tracking, use @gamifyio/node instead.");this.config={apiKey:e.apiKey,endpoint:e.endpoint??F,debug:e.debug??false,flushInterval:e.flushInterval??U,maxBatchSize:e.maxBatchSize??G,storagePrefix:e.storagePrefix??q},this.storage=p(this.config.storagePrefix),this.queue=new l(this.storage),this.client=new d(this.config.endpoint,this.config.apiKey,this.config.debug),this._session=new f(this.config,this.client,this.storage),this._loyalty=new h(this.config,this.client,this.storage),this._referral=new u(this.config,this.storage),this._affiliate=new c(this.config,this.client,this.storage);let t=this.storage.get(E);this.anonymousId=t??C(),t||this.storage.set(E,this.anonymousId),this.userId=this.storage.get(T),this.userTraits=this.storage.get(I),this.initialize();}initialize(){this.initialized||(this.log("Initializing SDK"),this.startFlushTimer(),typeof window<"u"&&(window.addEventListener("beforeunload",()=>this.onBeforeUnload()),window.addEventListener("pagehide",()=>this.onBeforeUnload())),this.initialized=true,this.log("SDK initialized",{anonymousId:this.anonymousId,userId:this.userId}));}log(e,t){this.config.debug&&console.log(`[Gamify] ${e}`,t??"");}startFlushTimer(){this.flushTimer&&clearInterval(this.flushTimer),this.flushTimer=setInterval(()=>{this.flush();},this.config.flushInterval);}onBeforeUnload(){this.flushTimer&&(clearInterval(this.flushTimer),this.flushTimer=null);let e=this.queue.peek(this.config.maxBatchSize);if(e.length>0){let t=e.map(r=>r.event);this.client.sendBeacon(t);}}identify(e,t){if(!e||typeof e!="string"){this.log("Invalid user ID provided");return}this.log("Identifying user",{userId:e,traits:t}),this.userId=e,this.storage.set(T,e),t&&(this.userTraits={...this.userTraits,...t},this.storage.set(I,this.userTraits)),this.track("$identify",{userId:e,traits:t??{}});}track(e,t){if(!e||typeof e!="string"){this.log("Invalid event type provided");return}$.includes(e)&&console.warn(`[Gamify] Event "${e}" requires a secret key and will be rejected. Use @gamifyio/node on your server to track this event.`);let r=this._referral.getReferrerProperties(),n={type:e,properties:{...r,...t},timestamp:new Date().toISOString(),anonymousId:this.anonymousId,...this.userId&&{userId:this.userId}};this.log("Tracking event",n),this.queue.enqueue(n),this.queue.size()>=this.config.maxBatchSize&&this.flush();}async flush(){let e=this.queue.peek(this.config.maxBatchSize);if(e.length===0)return;let t=e.map(i=>i.event),r=e.map(i=>i.id);this.log(`Flushing ${t.length} events`),(await this.client.sendEvents(t)).success?this.queue.acknowledge(r):this.queue.nack(r);}reset(){this.log("Resetting SDK state"),this.userId=null,this.userTraits=null,this.storage.remove(T),this.storage.remove(I),this.anonymousId=C(),this.storage.set(E,this.anonymousId);}getUserId(){return this.userId}getAnonymousId(){return this.anonymousId}getPendingCount(){return this.queue.size()}shutdown(){this.log("Shutting down SDK"),this.flushTimer&&(clearInterval(this.flushTimer),this.flushTimer=null);let e=this.queue.peek(this.config.maxBatchSize);if(e.length>0){let t=e.map(r=>r.event);this.client.sendBeacon(t);}}get session(){return this._session}async updateCart(e,t,r){if(!this.userId)throw new Error("[Gamify] User must be identified before updating cart");return this._session.updateCart(this.userId,e,t,r)}get loyalty(){return this._loyalty}async getLoyaltyProfile(){if(!this.userId)throw new Error("[Gamify] User must be identified before getting loyalty profile");return this._loyalty.getProfile(this.userId)}async getLoyaltyHistory(e,t){if(!this.userId)throw new Error("[Gamify] User must be identified before getting loyalty history");return this._loyalty.getHistory(this.userId,e,t)}get referral(){return this._referral}getReferrer(){return this._referral.getReferrer()}setReferrer(e){this._referral.setReferrer(e);}get affiliate(){return this._affiliate}async getAffiliateStats(e=false){if(!this.userId)throw new Error("[Gamify] User must be identified before getting affiliate stats");return this._affiliate.getStats(this.userId,e)}async getLeaderboard(e=10){return this._affiliate.getLeaderboard(e)}async getQuests(){if(!this.userId)throw new Error("[Gamify] User must be identified before getting quests");let e=await this.client.get(`/v1/customer/quests?userId=${encodeURIComponent(this.userId)}`);if(e.success&&e.data)return e.data;throw new Error(e.error||"Failed to get quests")}async getStreaks(){if(!this.userId)throw new Error("[Gamify] User must be identified before getting streaks");let e=await this.client.get(`/v1/customer/streaks?userId=${encodeURIComponent(this.userId)}`);if(e.success&&e.data)return e.data;throw new Error(e.error||"Failed to get streaks")}async useStreakFreeze(e){if(!this.userId)throw new Error("[Gamify] User must be identified before using freeze");let t=await this.client.post(`/v1/customer/streaks/${encodeURIComponent(e)}/freeze`,{userId:this.userId});if(t.success&&t.data)return t.data;throw new Error(t.error||"Failed to use freeze")}async getBadges(e){if(!this.userId)throw new Error("[Gamify] User must be identified before getting badges");let t=`/v1/customer/badges?userId=${encodeURIComponent(this.userId)}`;e&&(t+=`&category=${encodeURIComponent(e)}`);let r=await this.client.get(t);if(r.success&&r.data)return r.data;throw new Error(r.error||"Failed to get badges")}async getBadgeCategories(){let e=await this.client.get("/v1/customer/badges/categories");if(e.success&&e.data)return e.data;throw new Error(e.error||"Failed to get badge categories")}async getRewardsStore(){if(!this.userId)throw new Error("[Gamify] User must be identified before getting rewards");let e=await this.client.get(`/v1/customer/store?userId=${encodeURIComponent(this.userId)}`);if(e.success&&e.data)return e.data;throw new Error(e.error||"Failed to get rewards store")}async redeemReward(e){if(!this.userId)throw new Error("[Gamify] User must be identified before redeeming rewards");let t=await this.client.post("/v1/customer/store/redeem",{userId:this.userId,itemId:e});if(t.success&&t.data)return t.data;throw new Error(t.error||"Failed to redeem reward")}};var B={background:"rgba(255, 255, 255, 0.05)",backgroundSecondary:"rgba(255, 255, 255, 0.1)",foreground:"#ffffff",foregroundSecondary:"rgba(255, 255, 255, 0.7)",border:"rgba(255, 255, 255, 0.1)",primary:"#8B5CF6",primaryForeground:"#ffffff",success:"#22C55E",warning:"#F59E0B",error:"#EF4444",rarityCommon:"#9CA3AF",rarityRare:"#3B82F6",rarityEpic:"#8B5CF6",rarityLegendary:"#F59E0B",medalGold:"#FFD700",medalSilver:"#C0C0C0",medalBronze:"#CD7F32"};
2
+ exports.AffiliateManager=c;exports.EventQueue=l;exports.Gamify=A;exports.HttpClient=d;exports.LoyaltyManager=h;exports.ReferralManager=u;exports.SessionManager=f;exports.createStorage=p;exports.defaultTheme=B;//# sourceMappingURL=index.cjs.map
3
3
  //# sourceMappingURL=index.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/storage/storage.ts","../src/queue/event-queue.ts","../src/network/http-client.ts","../src/session/session-manager.ts","../src/loyalty/loyalty-manager.ts","../src/referral/referral-manager.ts","../src/affiliate/affiliate-manager.ts","../src/gamify.ts"],"names":["LocalStorageAdapter","prefix","key","item","value","keys","MemoryStorageAdapter","__publicField","keysToDelete","isLocalStorageAvailable","testKey","createStorage","QUEUE_STORAGE_KEY","generateId","EventQueue","storage","maxSize","persisted","event","queuedEvent","count","ids","idSet","HttpClient","endpoint","apiKey","debug","message","data","path","response","errorText","error","body","events","blob","SESSION_TOKEN_KEY","SESSION_DATA_KEY","SessionManager","config","client","userId","items","coupons","currency","request","code","LOYALTY_PROFILE_KEY","LoyaltyManager","limit","offset","params","REFERRER_KEY","REFERRER_DETECTED_AT_KEY","ReferralManager","refCode","AFFILIATE_STATS_CACHE_KEY","AffiliateManager","cached","forceRefresh","now","stats","DEFAULT_ENDPOINT","DEFAULT_FLUSH_INTERVAL","DEFAULT_MAX_BATCH_SIZE","DEFAULT_STORAGE_PREFIX","USER_ID_KEY","ANONYMOUS_ID_KEY","USER_TRAITS_KEY","TRUSTED_EVENTS","generateAnonymousId","Gamify","storedAnonymousId","pending","traits","eventType","properties","referrerProps","ruleId","category","url","itemId"],"mappings":"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","file":"index.cjs","sourcesContent":["import type { StorageAdapter } from '../types.js';\n\n/**\n * LocalStorage adapter with automatic JSON serialization\n */\nclass LocalStorageAdapter implements StorageAdapter {\n constructor(private readonly prefix: string) {}\n\n private getKey(key: string): string {\n return `${this.prefix}${key}`;\n }\n\n get<T>(key: string): T | null {\n try {\n const item = localStorage.getItem(this.getKey(key));\n if (item === null) return null;\n return JSON.parse(item) as T;\n } catch {\n return null;\n }\n }\n\n set<T>(key: string, value: T): void {\n try {\n localStorage.setItem(this.getKey(key), JSON.stringify(value));\n } catch {\n // Storage quota exceeded or unavailable - silently fail\n }\n }\n\n remove(key: string): void {\n try {\n localStorage.removeItem(this.getKey(key));\n } catch {\n // Ignore errors\n }\n }\n\n clear(): void {\n try {\n const keys = Object.keys(localStorage);\n for (const key of keys) {\n if (key.startsWith(this.prefix)) {\n localStorage.removeItem(key);\n }\n }\n } catch {\n // Ignore errors\n }\n }\n}\n\n/**\n * In-memory storage fallback for environments without localStorage\n */\nclass MemoryStorageAdapter implements StorageAdapter {\n private readonly store = new Map<string, string>();\n\n constructor(private readonly prefix: string) {}\n\n private getKey(key: string): string {\n return `${this.prefix}${key}`;\n }\n\n get<T>(key: string): T | null {\n const item = this.store.get(this.getKey(key));\n if (item === undefined) return null;\n try {\n return JSON.parse(item) as T;\n } catch {\n return null;\n }\n }\n\n set<T>(key: string, value: T): void {\n this.store.set(this.getKey(key), JSON.stringify(value));\n }\n\n remove(key: string): void {\n this.store.delete(this.getKey(key));\n }\n\n clear(): void {\n const keysToDelete: string[] = [];\n for (const key of this.store.keys()) {\n if (key.startsWith(this.prefix)) {\n keysToDelete.push(key);\n }\n }\n for (const key of keysToDelete) {\n this.store.delete(key);\n }\n }\n}\n\n/**\n * Check if localStorage is available\n */\nfunction isLocalStorageAvailable(): boolean {\n try {\n const testKey = '__gamify_test__';\n localStorage.setItem(testKey, 'test');\n localStorage.removeItem(testKey);\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Create appropriate storage adapter based on environment\n */\nexport function createStorage(prefix: string): StorageAdapter {\n if (typeof window !== 'undefined' && isLocalStorageAvailable()) {\n return new LocalStorageAdapter(prefix);\n }\n return new MemoryStorageAdapter(prefix);\n}\n","import type { QueuedEvent, GamifyEvent, StorageAdapter } from '../types.js';\n\nconst QUEUE_STORAGE_KEY = 'event_queue';\nconst MAX_RETRY_ATTEMPTS = 3;\n\n/**\n * Generates a unique ID for queued events\n */\nfunction generateId(): string {\n return `${Date.now()}-${Math.random().toString(36).substring(2, 11)}`;\n}\n\n/**\n * Event queue for reliable event delivery with offline support\n */\nexport class EventQueue {\n private queue: QueuedEvent[] = [];\n private readonly maxSize: number;\n private readonly storage: StorageAdapter;\n\n constructor(storage: StorageAdapter, maxSize: number = 100) {\n this.storage = storage;\n this.maxSize = maxSize;\n this.loadFromStorage();\n }\n\n /**\n * Load persisted queue from storage\n */\n private loadFromStorage(): void {\n const persisted = this.storage.get<QueuedEvent[]>(QUEUE_STORAGE_KEY);\n if (persisted && Array.isArray(persisted)) {\n this.queue = persisted;\n }\n }\n\n /**\n * Persist queue to storage\n */\n private saveToStorage(): void {\n this.storage.set(QUEUE_STORAGE_KEY, this.queue);\n }\n\n /**\n * Add an event to the queue\n */\n enqueue(event: GamifyEvent): string {\n const queuedEvent: QueuedEvent = {\n id: generateId(),\n event,\n attempts: 0,\n createdAt: Date.now(),\n };\n\n this.queue.push(queuedEvent);\n\n // Trim queue if it exceeds max size (FIFO - remove oldest)\n while (this.queue.length > this.maxSize) {\n this.queue.shift();\n }\n\n this.saveToStorage();\n return queuedEvent.id;\n }\n\n /**\n * Get events ready for sending (batch)\n */\n peek(count: number): QueuedEvent[] {\n return this.queue\n .filter((item) => item.attempts < MAX_RETRY_ATTEMPTS)\n .slice(0, count);\n }\n\n /**\n * Mark events as successfully sent and remove from queue\n */\n acknowledge(ids: string[]): void {\n const idSet = new Set(ids);\n this.queue = this.queue.filter((item) => !idSet.has(item.id));\n this.saveToStorage();\n }\n\n /**\n * Mark events as failed (increment retry count)\n */\n nack(ids: string[]): void {\n const idSet = new Set(ids);\n for (const item of this.queue) {\n if (idSet.has(item.id)) {\n item.attempts++;\n }\n }\n // Remove events that exceeded max retries\n this.queue = this.queue.filter((item) => item.attempts < MAX_RETRY_ATTEMPTS);\n this.saveToStorage();\n }\n\n /**\n * Get current queue size\n */\n size(): number {\n return this.queue.length;\n }\n\n /**\n * Check if queue has pending events\n */\n hasPending(): boolean {\n return this.queue.some((item) => item.attempts < MAX_RETRY_ATTEMPTS);\n }\n\n /**\n * Clear all events from queue\n */\n clear(): void {\n this.queue = [];\n this.saveToStorage();\n }\n}\n","import type { GamifyEvent, ApiResponse } from '../types.js';\n\ninterface HttpResponse<T> {\n success: boolean;\n data?: T;\n error?: string;\n}\n\n/**\n * HTTP client for sending events to the API\n * Uses fetch with keepalive for reliable delivery during page unload\n */\nexport class HttpClient {\n private readonly endpoint: string;\n private readonly apiKey: string;\n private readonly debug: boolean;\n\n constructor(endpoint: string, apiKey: string, debug: boolean = false) {\n this.endpoint = endpoint;\n this.apiKey = apiKey;\n this.debug = debug;\n }\n\n /**\n * Log debug messages\n */\n private log(message: string, data?: unknown): void {\n if (this.debug) {\n console.log(`[Gamify] ${message}`, data ?? '');\n }\n }\n\n /**\n * Generic GET request\n */\n async get<T>(path: string): Promise<HttpResponse<T>> {\n this.log(`GET ${path}`);\n\n try {\n const response = await fetch(`${this.endpoint}${path}`, {\n method: 'GET',\n headers: {\n 'Content-Type': 'application/json',\n 'X-API-Key': this.apiKey,\n },\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n this.log(`GET error: ${response.status}`, errorText);\n return { success: false, error: `HTTP ${response.status}: ${errorText}` };\n }\n\n const data = await response.json();\n return { success: true, data };\n } catch (error) {\n const message = error instanceof Error ? error.message : 'Unknown error';\n this.log('GET network error', message);\n return { success: false, error: message };\n }\n }\n\n /**\n * Generic POST request\n */\n async post<T>(path: string, body: unknown): Promise<HttpResponse<T>> {\n this.log(`POST ${path}`, body);\n\n try {\n const response = await fetch(`${this.endpoint}${path}`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'X-API-Key': this.apiKey,\n },\n body: JSON.stringify(body),\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n this.log(`POST error: ${response.status}`, errorText);\n return { success: false, error: `HTTP ${response.status}: ${errorText}` };\n }\n\n const data = await response.json();\n return { success: true, data };\n } catch (error) {\n const message = error instanceof Error ? error.message : 'Unknown error';\n this.log('POST network error', message);\n return { success: false, error: message };\n }\n }\n\n /**\n * Send a batch of events to the API\n */\n async sendEvents(events: GamifyEvent[]): Promise<ApiResponse> {\n if (events.length === 0) {\n return { success: true };\n }\n\n this.log(`Sending ${events.length} events`, events);\n\n try {\n const response = await fetch(`${this.endpoint}/events/batch`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'X-API-Key': this.apiKey,\n },\n body: JSON.stringify({ events }),\n keepalive: true,\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n this.log(`API error: ${response.status}`, errorText);\n return { success: false, error: `HTTP ${response.status}: ${errorText}` };\n }\n\n this.log('Events sent successfully');\n return { success: true };\n } catch (error) {\n const message = error instanceof Error ? error.message : 'Unknown error';\n this.log('Network error', message);\n return { success: false, error: message };\n }\n }\n\n /**\n * Send a single event using beacon API (for page unload)\n * Falls back to fetch if beacon is not available\n */\n sendBeacon(events: GamifyEvent[]): boolean {\n if (events.length === 0) {\n return true;\n }\n\n this.log(`Sending ${events.length} events via beacon`);\n\n if (typeof navigator !== 'undefined' && navigator.sendBeacon) {\n const blob = new Blob(\n [JSON.stringify({ events, apiKey: this.apiKey })],\n { type: 'application/json' }\n );\n return navigator.sendBeacon(`${this.endpoint}/events/batch`, blob);\n }\n\n // Fallback: fire-and-forget fetch with keepalive\n fetch(`${this.endpoint}/events/batch`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'X-API-Key': this.apiKey,\n },\n body: JSON.stringify({ events }),\n keepalive: true,\n }).catch(() => {\n // Ignore errors during unload\n });\n\n return true;\n }\n}\n","import type {\n GamifyConfig,\n SessionRequest,\n SessionResponse,\n CartItem,\n StorageAdapter,\n} from '../types.js';\nimport { HttpClient } from '../network/index.js';\n\nconst SESSION_TOKEN_KEY = 'session_token';\nconst SESSION_DATA_KEY = 'session_data';\n\n/**\n * SessionManager - Manages cart sessions with discount calculations\n */\nexport class SessionManager {\n private readonly client: HttpClient;\n private readonly storage: StorageAdapter;\n private readonly debug: boolean;\n private sessionToken: string | null = null;\n private cachedSession: SessionResponse | null = null;\n\n constructor(\n config: Required<GamifyConfig>,\n client: HttpClient,\n storage: StorageAdapter,\n ) {\n this.client = client;\n this.storage = storage;\n this.debug = config.debug;\n\n // Load persisted session token\n this.sessionToken = this.storage.get<string>(SESSION_TOKEN_KEY);\n this.cachedSession = this.storage.get<SessionResponse>(SESSION_DATA_KEY);\n }\n\n /**\n * Create or update a session with cart items\n */\n async updateCart(\n userId: string,\n items: CartItem[],\n coupons?: string[],\n currency?: string,\n ): Promise<SessionResponse> {\n const request: SessionRequest = {\n userId,\n items,\n coupons,\n currency,\n };\n\n this.log('Updating cart', request);\n\n const response = await this.client.post<SessionResponse>(\n '/v1/sessions',\n request,\n );\n\n if (response.success && response.data) {\n this.sessionToken = response.data.sessionToken;\n this.cachedSession = response.data;\n this.storage.set(SESSION_TOKEN_KEY, this.sessionToken);\n this.storage.set(SESSION_DATA_KEY, this.cachedSession);\n\n this.log('Cart updated', response.data);\n return response.data;\n }\n\n throw new Error(response.error || 'Failed to update cart');\n }\n\n /**\n * Get current session by token\n */\n async getSession(): Promise<SessionResponse | null> {\n if (!this.sessionToken) {\n return null;\n }\n\n this.log('Getting session', this.sessionToken);\n\n const response = await this.client.get<SessionResponse>(\n `/v1/sessions/${this.sessionToken}`,\n );\n\n if (response.success && response.data) {\n this.cachedSession = response.data;\n this.storage.set(SESSION_DATA_KEY, this.cachedSession);\n return response.data;\n }\n\n // Session not found, clear local state\n if (response.error?.includes('not found')) {\n this.clearSession();\n }\n\n return null;\n }\n\n /**\n * Apply a coupon to the current session\n */\n async applyCoupon(code: string): Promise<SessionResponse> {\n if (!this.sessionToken) {\n throw new Error('No active session');\n }\n\n this.log('Applying coupon', code);\n\n const response = await this.client.post<SessionResponse>(\n `/v1/sessions/${this.sessionToken}/coupons`,\n { code },\n );\n\n if (response.success && response.data) {\n this.cachedSession = response.data;\n this.storage.set(SESSION_DATA_KEY, this.cachedSession);\n\n this.log('Coupon applied', response.data);\n return response.data;\n }\n\n throw new Error(response.error || 'Failed to apply coupon');\n }\n\n /**\n * Complete the session (checkout)\n */\n async complete(): Promise<SessionResponse> {\n if (!this.sessionToken) {\n throw new Error('No active session');\n }\n\n this.log('Completing session');\n\n const response = await this.client.post<SessionResponse>(\n `/v1/sessions/${this.sessionToken}/complete`,\n {},\n );\n\n if (response.success && response.data) {\n this.cachedSession = response.data;\n this.storage.set(SESSION_DATA_KEY, this.cachedSession);\n\n this.log('Session completed', response.data);\n return response.data;\n }\n\n throw new Error(response.error || 'Failed to complete session');\n }\n\n /**\n * Clear the current session\n */\n clearSession(): void {\n this.sessionToken = null;\n this.cachedSession = null;\n this.storage.remove(SESSION_TOKEN_KEY);\n this.storage.remove(SESSION_DATA_KEY);\n\n this.log('Session cleared');\n }\n\n /**\n * Get cached session data\n */\n getCachedSession(): SessionResponse | null {\n return this.cachedSession;\n }\n\n /**\n * Get current session token\n */\n getSessionToken(): string | null {\n return this.sessionToken;\n }\n\n /**\n * Check if there's an active session\n */\n hasActiveSession(): boolean {\n return (\n this.sessionToken !== null &&\n this.cachedSession?.status === 'active'\n );\n }\n\n private log(message: string, data?: unknown): void {\n if (this.debug) {\n console.log(`[Gamify:Session] ${message}`, data ?? '');\n }\n }\n}\n","import type {\n GamifyConfig,\n LoyaltyProfile,\n LoyaltyHistoryResponse,\n StorageAdapter,\n} from '../types.js';\nimport { HttpClient } from '../network/index.js';\n\nconst LOYALTY_PROFILE_KEY = 'loyalty_profile';\n\n/**\n * LoyaltyManager - Manages customer loyalty points and tiers\n */\nexport class LoyaltyManager {\n private readonly client: HttpClient;\n private readonly storage: StorageAdapter;\n private readonly debug: boolean;\n private cachedProfile: LoyaltyProfile | null = null;\n\n constructor(\n config: Required<GamifyConfig>,\n client: HttpClient,\n storage: StorageAdapter,\n ) {\n this.client = client;\n this.storage = storage;\n this.debug = config.debug;\n\n // Load cached profile\n this.cachedProfile = this.storage.get<LoyaltyProfile>(LOYALTY_PROFILE_KEY);\n }\n\n /**\n * Get customer loyalty profile\n */\n async getProfile(userId: string): Promise<LoyaltyProfile> {\n this.log('Getting loyalty profile', userId);\n\n const response = await this.client.get<LoyaltyProfile>(\n `/v1/customer/profile?userId=${encodeURIComponent(userId)}`,\n );\n\n if (response.success && response.data) {\n this.cachedProfile = response.data;\n this.storage.set(LOYALTY_PROFILE_KEY, this.cachedProfile);\n\n this.log('Profile loaded', response.data);\n return response.data;\n }\n\n throw new Error(response.error || 'Failed to get loyalty profile');\n }\n\n /**\n * Get customer transaction history\n */\n async getHistory(\n userId: string,\n limit?: number,\n offset?: number,\n ): Promise<LoyaltyHistoryResponse> {\n this.log('Getting loyalty history', { userId, limit, offset });\n\n const params = new URLSearchParams();\n params.set('userId', userId);\n if (limit !== undefined) params.set('limit', String(limit));\n if (offset !== undefined) params.set('offset', String(offset));\n\n const response = await this.client.get<LoyaltyHistoryResponse>(\n `/v1/customer/history?${params.toString()}`,\n );\n\n if (response.success && response.data) {\n this.log('History loaded', response.data);\n return response.data;\n }\n\n throw new Error(response.error || 'Failed to get loyalty history');\n }\n\n /**\n * Get cached loyalty profile\n */\n getCachedProfile(): LoyaltyProfile | null {\n return this.cachedProfile;\n }\n\n /**\n * Get current points balance from cache\n */\n getPoints(): number {\n return this.cachedProfile?.points ?? 0;\n }\n\n /**\n * Get current tier from cache\n */\n getTier(): LoyaltyProfile['tier'] {\n return this.cachedProfile?.tier ?? null;\n }\n\n /**\n * Get next tier info from cache\n */\n getNextTier(): LoyaltyProfile['nextTier'] {\n return this.cachedProfile?.nextTier ?? null;\n }\n\n /**\n * Clear cached profile\n */\n clearCache(): void {\n this.cachedProfile = null;\n this.storage.remove(LOYALTY_PROFILE_KEY);\n this.log('Cache cleared');\n }\n\n /**\n * Refresh profile from server\n */\n async refresh(userId: string): Promise<LoyaltyProfile> {\n return this.getProfile(userId);\n }\n\n private log(message: string, data?: unknown): void {\n if (this.debug) {\n console.log(`[Gamify:Loyalty] ${message}`, data ?? '');\n }\n }\n}\n","import type { GamifyConfig, StorageAdapter } from '../types.js';\n\nconst REFERRER_KEY = '__gamify_referrer';\nconst REFERRER_DETECTED_AT_KEY = '__gamify_referrer_detected_at';\n\n/**\n * Referral Manager - Handles URL parameter detection and referrer storage\n *\n * Automatically detects `?ref=code` parameter from URLs and stores\n * the referrer code in localStorage for attribution tracking.\n */\nexport class ReferralManager {\n private readonly config: Required<GamifyConfig>;\n private readonly storage: StorageAdapter;\n private referrerCode: string | null = null;\n\n constructor(\n config: Required<GamifyConfig>,\n storage: StorageAdapter\n ) {\n this.config = config;\n this.storage = storage;\n\n // Load stored referrer\n this.referrerCode = this.storage.get<string>(REFERRER_KEY);\n\n // Auto-detect referrer from URL on initialization\n this.detectReferrerFromUrl();\n }\n\n /**\n * Log debug messages\n */\n private log(message: string, data?: unknown): void {\n if (this.config.debug) {\n console.log(`[Gamify:Referral] ${message}`, data ?? '');\n }\n }\n\n /**\n * Detect referrer code from current URL\n * Looks for `?ref=` parameter\n */\n detectReferrerFromUrl(): string | null {\n if (typeof window === 'undefined') {\n return null;\n }\n\n try {\n const urlParams = new URLSearchParams(window.location.search);\n const refCode = urlParams.get('ref');\n\n if (refCode && refCode.length > 0) {\n this.log('Detected referrer from URL', { refCode });\n this.setReferrer(refCode);\n return refCode;\n }\n } catch (error) {\n this.log('Error detecting referrer from URL', error);\n }\n\n return null;\n }\n\n /**\n * Manually set the referrer code\n */\n setReferrer(code: string): void {\n if (!code || code.length === 0) {\n this.log('Invalid referrer code provided');\n return;\n }\n\n this.referrerCode = code;\n this.storage.set(REFERRER_KEY, code);\n this.storage.set(REFERRER_DETECTED_AT_KEY, new Date().toISOString());\n\n this.log('Referrer code stored', { code });\n }\n\n /**\n * Get the current referrer code\n */\n getReferrer(): string | null {\n return this.referrerCode;\n }\n\n /**\n * Check if a referrer code is present\n */\n hasReferrer(): boolean {\n return this.referrerCode !== null;\n }\n\n /**\n * Clear the referrer code\n */\n clearReferrer(): void {\n this.referrerCode = null;\n this.storage.remove(REFERRER_KEY);\n this.storage.remove(REFERRER_DETECTED_AT_KEY);\n this.log('Referrer code cleared');\n }\n\n /**\n * Get referrer data to include in event properties\n */\n getReferrerProperties(): Record<string, unknown> {\n if (!this.referrerCode) {\n return {};\n }\n\n return {\n referrer: this.referrerCode,\n referrer_detected_at: this.storage.get<string>(REFERRER_DETECTED_AT_KEY),\n };\n }\n}\n","import type {\n GamifyConfig,\n StorageAdapter,\n AffiliateStats,\n AffiliateStatsResponse,\n LeaderboardResponse,\n} from '../types.js';\nimport type { HttpClient } from '../network/index.js';\n\nconst AFFILIATE_STATS_CACHE_KEY = 'affiliate_stats';\nconst CACHE_TTL_MS = 60000; // 1 minute\n\n/**\n * Affiliate Manager - Handles fetching affiliate stats and leaderboard\n */\nexport class AffiliateManager {\n private readonly config: Required<GamifyConfig>;\n private readonly client: HttpClient;\n private readonly storage: StorageAdapter;\n private cachedStats: AffiliateStats | null = null;\n private lastFetchTime: number = 0;\n\n constructor(\n config: Required<GamifyConfig>,\n client: HttpClient,\n storage: StorageAdapter\n ) {\n this.config = config;\n this.client = client;\n this.storage = storage;\n\n // Load cached stats\n const cached = this.storage.get<{ stats: AffiliateStats; fetchedAt: number }>(\n AFFILIATE_STATS_CACHE_KEY\n );\n if (cached) {\n this.cachedStats = cached.stats;\n this.lastFetchTime = cached.fetchedAt;\n }\n }\n\n /**\n * Log debug messages\n */\n private log(message: string, data?: unknown): void {\n if (this.config.debug) {\n console.log(`[Gamify:Affiliate] ${message}`, data ?? '');\n }\n }\n\n /**\n * Get affiliate stats for the current user\n * @param userId - User ID to fetch stats for\n * @param forceRefresh - Force refresh from API\n */\n async getStats(userId: string, forceRefresh = false): Promise<AffiliateStats> {\n // Check cache validity\n const now = Date.now();\n const cacheValid = !forceRefresh && this.cachedStats && now - this.lastFetchTime < CACHE_TTL_MS;\n\n if (cacheValid && this.cachedStats) {\n this.log('Returning cached affiliate stats');\n return this.cachedStats;\n }\n\n this.log('Fetching affiliate stats from API', { userId });\n\n try {\n const response = await this.client.get<AffiliateStatsResponse>(\n `/v1/customer/affiliate/profile?userId=${encodeURIComponent(userId)}`\n );\n\n if (response.success && response.data?.stats) {\n const stats = response.data.stats;\n\n this.cachedStats = stats;\n this.lastFetchTime = now;\n\n // Persist to storage\n this.storage.set(AFFILIATE_STATS_CACHE_KEY, {\n stats,\n fetchedAt: now,\n });\n\n this.log('Affiliate stats fetched', stats);\n return stats;\n }\n\n throw new Error(response.error ?? 'Invalid response from affiliate stats API');\n } catch (error) {\n this.log('Error fetching affiliate stats', error);\n throw error;\n }\n }\n\n /**\n * Get cached affiliate stats (if available)\n */\n getCachedStats(): AffiliateStats | null {\n return this.cachedStats;\n }\n\n /**\n * Get leaderboard data\n * @param limit - Number of entries to fetch (default: 10)\n */\n async getLeaderboard(limit = 10): Promise<LeaderboardResponse> {\n this.log('Fetching leaderboard', { limit });\n\n try {\n const response = await this.client.get<LeaderboardResponse>(\n `/v1/customer/affiliate/leaderboard?limit=${limit}`\n );\n\n if (response.success && response.data?.entries) {\n this.log('Leaderboard fetched', { count: response.data.entries.length });\n return response.data;\n }\n\n throw new Error(response.error ?? 'Invalid response from leaderboard API');\n } catch (error) {\n this.log('Error fetching leaderboard', error);\n throw error;\n }\n }\n\n /**\n * Clear cached affiliate stats\n */\n clearCache(): void {\n this.cachedStats = null;\n this.lastFetchTime = 0;\n this.storage.remove(AFFILIATE_STATS_CACHE_KEY);\n this.log('Affiliate stats cache cleared');\n }\n}\n","import type {\n GamifyConfig,\n GamifyEvent,\n UserTraits,\n StorageAdapter,\n CartItem,\n AffiliateStats,\n LeaderboardResponse,\n // Gamification types\n QuestsResponse,\n StreaksResponse,\n FreezeResponse,\n BadgesResponse,\n RewardsStoreResponse,\n RedemptionResult,\n} from './types.js';\nimport { createStorage } from './storage/index.js';\nimport { EventQueue } from './queue/index.js';\nimport { HttpClient } from './network/index.js';\nimport { SessionManager } from './session/index.js';\nimport { LoyaltyManager } from './loyalty/index.js';\nimport { ReferralManager } from './referral/index.js';\nimport { AffiliateManager } from './affiliate/index.js';\n\nconst DEFAULT_ENDPOINT = 'https://boostapi-production.up.railway.app';\nconst DEFAULT_FLUSH_INTERVAL = 10000; // 10 seconds\nconst DEFAULT_MAX_BATCH_SIZE = 10;\nconst DEFAULT_STORAGE_PREFIX = 'gamify_';\nconst USER_ID_KEY = 'user_id';\nconst ANONYMOUS_ID_KEY = 'anonymous_id';\nconst USER_TRAITS_KEY = 'user_traits';\n\n/**\n * Events that require a secret key (server-side only)\n * These will be rejected by the API when sent with a publishable key\n */\nconst TRUSTED_EVENTS = [\n 'purchase',\n 'checkout_complete',\n 'checkout_success',\n 'commission.created',\n 'referral_success',\n 'user.leveled_up',\n 'step.completed',\n 'quest.completed',\n] as const;\n\n/**\n * Generate anonymous ID for unidentified users\n */\nfunction generateAnonymousId(): string {\n return `anon_${Date.now()}_${Math.random().toString(36).substring(2, 11)}`;\n}\n\n/**\n * Gamify SDK - Framework-agnostic event tracking\n */\nexport class Gamify {\n private readonly config: Required<GamifyConfig>;\n private readonly storage: StorageAdapter;\n private readonly queue: EventQueue;\n private readonly client: HttpClient;\n private readonly _session: SessionManager;\n private readonly _loyalty: LoyaltyManager;\n private readonly _referral: ReferralManager;\n private readonly _affiliate: AffiliateManager;\n private flushTimer: ReturnType<typeof setInterval> | null = null;\n private anonymousId: string;\n private userId: string | null = null;\n private userTraits: UserTraits | null = null;\n private initialized = false;\n\n constructor(config: GamifyConfig) {\n if (!config.apiKey) {\n throw new Error('[Gamify] API key is required');\n }\n\n // Reject secret keys in client-side SDK\n if (config.apiKey.startsWith('sk_')) {\n throw new Error(\n '[Gamify] Secret keys (sk_live_*) cannot be used in the browser. ' +\n 'Use a publishable key (pk_live_*) for client-side tracking. ' +\n 'For server-side tracking, use @gamifyio/node instead.'\n );\n }\n\n this.config = {\n apiKey: config.apiKey,\n endpoint: config.endpoint ?? DEFAULT_ENDPOINT,\n debug: config.debug ?? false,\n flushInterval: config.flushInterval ?? DEFAULT_FLUSH_INTERVAL,\n maxBatchSize: config.maxBatchSize ?? DEFAULT_MAX_BATCH_SIZE,\n storagePrefix: config.storagePrefix ?? DEFAULT_STORAGE_PREFIX,\n };\n\n this.storage = createStorage(this.config.storagePrefix);\n this.queue = new EventQueue(this.storage);\n this.client = new HttpClient(\n this.config.endpoint,\n this.config.apiKey,\n this.config.debug\n );\n\n // Initialize Session and Loyalty managers\n this._session = new SessionManager(this.config, this.client, this.storage);\n this._loyalty = new LoyaltyManager(this.config, this.client, this.storage);\n\n // Issue #22: Initialize Referral and Affiliate managers\n this._referral = new ReferralManager(this.config, this.storage);\n this._affiliate = new AffiliateManager(this.config, this.client, this.storage);\n\n // Load or generate anonymous ID\n const storedAnonymousId = this.storage.get<string>(ANONYMOUS_ID_KEY);\n this.anonymousId = storedAnonymousId ?? generateAnonymousId();\n if (!storedAnonymousId) {\n this.storage.set(ANONYMOUS_ID_KEY, this.anonymousId);\n }\n\n // Load persisted user identity\n this.userId = this.storage.get<string>(USER_ID_KEY);\n this.userTraits = this.storage.get<UserTraits>(USER_TRAITS_KEY);\n\n this.initialize();\n }\n\n /**\n * Initialize the SDK\n */\n private initialize(): void {\n if (this.initialized) return;\n\n this.log('Initializing SDK');\n\n // Start flush timer\n this.startFlushTimer();\n\n // Handle page unload - flush remaining events\n if (typeof window !== 'undefined') {\n window.addEventListener('beforeunload', () => this.onBeforeUnload());\n window.addEventListener('pagehide', () => this.onBeforeUnload());\n }\n\n this.initialized = true;\n this.log('SDK initialized', {\n anonymousId: this.anonymousId,\n userId: this.userId,\n });\n }\n\n /**\n * Log debug messages\n */\n private log(message: string, data?: unknown): void {\n if (this.config.debug) {\n console.log(`[Gamify] ${message}`, data ?? '');\n }\n }\n\n /**\n * Start the automatic flush timer\n */\n private startFlushTimer(): void {\n if (this.flushTimer) {\n clearInterval(this.flushTimer);\n }\n this.flushTimer = setInterval(() => {\n void this.flush();\n }, this.config.flushInterval);\n }\n\n /**\n * Handle page unload - send remaining events via beacon\n */\n private onBeforeUnload(): void {\n if (this.flushTimer) {\n clearInterval(this.flushTimer);\n this.flushTimer = null;\n }\n\n const pending = this.queue.peek(this.config.maxBatchSize);\n if (pending.length > 0) {\n const events = pending.map((item) => item.event);\n this.client.sendBeacon(events);\n }\n }\n\n /**\n * Identify a user with optional traits\n * User ID persists across page reloads\n */\n identify(userId: string, traits?: UserTraits): void {\n if (!userId || typeof userId !== 'string') {\n this.log('Invalid user ID provided');\n return;\n }\n\n this.log('Identifying user', { userId, traits });\n\n this.userId = userId;\n this.storage.set(USER_ID_KEY, userId);\n\n if (traits) {\n this.userTraits = { ...this.userTraits, ...traits };\n this.storage.set(USER_TRAITS_KEY, this.userTraits);\n }\n\n // Track identify event\n this.track('$identify', {\n userId,\n traits: traits ?? {},\n });\n }\n\n /**\n * Track an event\n */\n track(eventType: string, properties?: Record<string, unknown>): void {\n if (!eventType || typeof eventType !== 'string') {\n this.log('Invalid event type provided');\n return;\n }\n\n // Warn about trusted events that require server-side tracking\n if ((TRUSTED_EVENTS as readonly string[]).includes(eventType)) {\n console.warn(\n `[Gamify] Event \"${eventType}\" requires a secret key and will be rejected. ` +\n `Use @gamifyio/node on your server to track this event.`\n );\n }\n\n // Issue #22: Inject referrer properties into event\n const referrerProps = this._referral.getReferrerProperties();\n\n const event: GamifyEvent = {\n type: eventType,\n properties: { ...referrerProps, ...properties },\n timestamp: new Date().toISOString(),\n anonymousId: this.anonymousId,\n ...(this.userId && { userId: this.userId }),\n };\n\n this.log('Tracking event', event);\n\n this.queue.enqueue(event);\n\n // Auto-flush if batch size reached\n if (this.queue.size() >= this.config.maxBatchSize) {\n void this.flush();\n }\n }\n\n /**\n * Flush pending events to the server\n */\n async flush(): Promise<void> {\n const pending = this.queue.peek(this.config.maxBatchSize);\n if (pending.length === 0) {\n return;\n }\n\n const events = pending.map((item) => item.event);\n const ids = pending.map((item) => item.id);\n\n this.log(`Flushing ${events.length} events`);\n\n const result = await this.client.sendEvents(events);\n\n if (result.success) {\n this.queue.acknowledge(ids);\n } else {\n this.queue.nack(ids);\n }\n }\n\n /**\n * Reset the SDK state (logout user)\n */\n reset(): void {\n this.log('Resetting SDK state');\n\n this.userId = null;\n this.userTraits = null;\n this.storage.remove(USER_ID_KEY);\n this.storage.remove(USER_TRAITS_KEY);\n\n // Generate new anonymous ID\n this.anonymousId = generateAnonymousId();\n this.storage.set(ANONYMOUS_ID_KEY, this.anonymousId);\n }\n\n /**\n * Get current user ID (null if not identified)\n */\n getUserId(): string | null {\n return this.userId;\n }\n\n /**\n * Get anonymous ID\n */\n getAnonymousId(): string {\n return this.anonymousId;\n }\n\n /**\n * Get pending event count\n */\n getPendingCount(): number {\n return this.queue.size();\n }\n\n /**\n * Shutdown the SDK gracefully\n */\n shutdown(): void {\n this.log('Shutting down SDK');\n\n if (this.flushTimer) {\n clearInterval(this.flushTimer);\n this.flushTimer = null;\n }\n\n // Attempt final flush via beacon\n const pending = this.queue.peek(this.config.maxBatchSize);\n if (pending.length > 0) {\n const events = pending.map((item) => item.event);\n this.client.sendBeacon(events);\n }\n }\n\n // ============================================\n // Session Module (Issue #14)\n // ============================================\n\n /**\n * Get the session manager\n */\n get session(): SessionManager {\n return this._session;\n }\n\n /**\n * Convenience method: Update cart and get discounts\n */\n async updateCart(\n items: CartItem[],\n coupons?: string[],\n currency?: string,\n ) {\n if (!this.userId) {\n throw new Error('[Gamify] User must be identified before updating cart');\n }\n return this._session.updateCart(this.userId, items, coupons, currency);\n }\n\n // ============================================\n // Loyalty Module (Issue #15)\n // ============================================\n\n /**\n * Get the loyalty manager\n */\n get loyalty(): LoyaltyManager {\n return this._loyalty;\n }\n\n /**\n * Convenience method: Get loyalty profile for current user\n */\n async getLoyaltyProfile() {\n if (!this.userId) {\n throw new Error('[Gamify] User must be identified before getting loyalty profile');\n }\n return this._loyalty.getProfile(this.userId);\n }\n\n /**\n * Convenience method: Get loyalty history for current user\n */\n async getLoyaltyHistory(limit?: number, offset?: number) {\n if (!this.userId) {\n throw new Error('[Gamify] User must be identified before getting loyalty history');\n }\n return this._loyalty.getHistory(this.userId, limit, offset);\n }\n\n // ============================================\n // Referral Module (Issue #22)\n // ============================================\n\n /**\n * Get the referral manager\n */\n get referral(): ReferralManager {\n return this._referral;\n }\n\n /**\n * Convenience method: Get current referrer code\n */\n getReferrer(): string | null {\n return this._referral.getReferrer();\n }\n\n /**\n * Convenience method: Set referrer code manually\n */\n setReferrer(code: string): void {\n this._referral.setReferrer(code);\n }\n\n // ============================================\n // Affiliate Module (Issue #22)\n // ============================================\n\n /**\n * Get the affiliate manager\n */\n get affiliate(): AffiliateManager {\n return this._affiliate;\n }\n\n /**\n * Convenience method: Get affiliate stats for current user\n */\n async getAffiliateStats(forceRefresh = false): Promise<AffiliateStats> {\n if (!this.userId) {\n throw new Error('[Gamify] User must be identified before getting affiliate stats');\n }\n return this._affiliate.getStats(this.userId, forceRefresh);\n }\n\n /**\n * Convenience method: Get leaderboard\n */\n async getLeaderboard(limit = 10): Promise<LeaderboardResponse> {\n return this._affiliate.getLeaderboard(limit);\n }\n\n // ============================================\n // Quests Module (Issue #25-28)\n // ============================================\n\n /**\n * Get user's quest progress\n */\n async getQuests(): Promise<QuestsResponse> {\n if (!this.userId) {\n throw new Error('[Gamify] User must be identified before getting quests');\n }\n const response = await this.client.get<QuestsResponse>(\n `/v1/customer/quests?userId=${encodeURIComponent(this.userId)}`\n );\n if (response.success && response.data) {\n return response.data;\n }\n throw new Error(response.error || 'Failed to get quests');\n }\n\n // ============================================\n // Streaks Module (Issue #32)\n // ============================================\n\n /**\n * Get user's streaks with progress\n */\n async getStreaks(): Promise<StreaksResponse> {\n if (!this.userId) {\n throw new Error('[Gamify] User must be identified before getting streaks');\n }\n const response = await this.client.get<StreaksResponse>(\n `/v1/customer/streaks?userId=${encodeURIComponent(this.userId)}`\n );\n if (response.success && response.data) {\n return response.data;\n }\n throw new Error(response.error || 'Failed to get streaks');\n }\n\n /**\n * Use a freeze token for a streak\n */\n async useStreakFreeze(ruleId: string): Promise<FreezeResponse> {\n if (!this.userId) {\n throw new Error('[Gamify] User must be identified before using freeze');\n }\n const response = await this.client.post<FreezeResponse>(\n `/v1/customer/streaks/${encodeURIComponent(ruleId)}/freeze`,\n { userId: this.userId }\n );\n if (response.success && response.data) {\n return response.data;\n }\n throw new Error(response.error || 'Failed to use freeze');\n }\n\n // ============================================\n // Badges Module (Issue #33)\n // ============================================\n\n /**\n * Get user's badge collection\n */\n async getBadges(category?: string): Promise<BadgesResponse> {\n if (!this.userId) {\n throw new Error('[Gamify] User must be identified before getting badges');\n }\n let url = `/v1/customer/badges?userId=${encodeURIComponent(this.userId)}`;\n if (category) {\n url += `&category=${encodeURIComponent(category)}`;\n }\n const response = await this.client.get<BadgesResponse>(url);\n if (response.success && response.data) {\n return response.data;\n }\n throw new Error(response.error || 'Failed to get badges');\n }\n\n /**\n * Get available badge categories\n */\n async getBadgeCategories(): Promise<string[]> {\n const response = await this.client.get<string[]>('/v1/customer/badges/categories');\n if (response.success && response.data) {\n return response.data;\n }\n throw new Error(response.error || 'Failed to get badge categories');\n }\n\n // ============================================\n // Rewards Module (Issue #34)\n // ============================================\n\n /**\n * Get rewards store items\n */\n async getRewardsStore(): Promise<RewardsStoreResponse> {\n if (!this.userId) {\n throw new Error('[Gamify] User must be identified before getting rewards');\n }\n const response = await this.client.get<RewardsStoreResponse>(\n `/v1/customer/store?userId=${encodeURIComponent(this.userId)}`\n );\n if (response.success && response.data) {\n return response.data;\n }\n throw new Error(response.error || 'Failed to get rewards store');\n }\n\n /**\n * Redeem a reward item\n */\n async redeemReward(itemId: string): Promise<RedemptionResult> {\n if (!this.userId) {\n throw new Error('[Gamify] User must be identified before redeeming rewards');\n }\n const response = await this.client.post<RedemptionResult>(\n '/v1/customer/store/redeem',\n { userId: this.userId, itemId }\n );\n if (response.success && response.data) {\n return response.data;\n }\n throw new Error(response.error || 'Failed to redeem reward');\n }\n}\n"]}
1
+ {"version":3,"sources":["../src/storage/storage.ts","../src/queue/event-queue.ts","../src/network/http-client.ts","../src/session/session-manager.ts","../src/loyalty/loyalty-manager.ts","../src/referral/referral-manager.ts","../src/affiliate/affiliate-manager.ts","../src/gamify.ts","../src/types.ts"],"names":["LocalStorageAdapter","prefix","key","item","value","keys","MemoryStorageAdapter","__publicField","keysToDelete","isLocalStorageAvailable","testKey","createStorage","QUEUE_STORAGE_KEY","generateId","EventQueue","storage","maxSize","persisted","event","queuedEvent","count","ids","idSet","HttpClient","endpoint","apiKey","debug","message","data","path","response","errorText","error","body","events","blob","SESSION_TOKEN_KEY","SESSION_DATA_KEY","SessionManager","config","client","userId","items","coupons","currency","request","code","LOYALTY_PROFILE_KEY","LoyaltyManager","limit","offset","params","REFERRER_KEY","REFERRER_DETECTED_AT_KEY","ReferralManager","refCode","AFFILIATE_STATS_CACHE_KEY","AffiliateManager","cached","forceRefresh","now","stats","DEFAULT_ENDPOINT","DEFAULT_FLUSH_INTERVAL","DEFAULT_MAX_BATCH_SIZE","DEFAULT_STORAGE_PREFIX","USER_ID_KEY","ANONYMOUS_ID_KEY","USER_TRAITS_KEY","TRUSTED_EVENTS","generateAnonymousId","Gamify","storedAnonymousId","pending","traits","eventType","properties","referrerProps","ruleId","category","url","itemId","defaultTheme"],"mappings":"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","file":"index.cjs","sourcesContent":["import type { StorageAdapter } from '../types.js';\n\n/**\n * LocalStorage adapter with automatic JSON serialization\n */\nclass LocalStorageAdapter implements StorageAdapter {\n constructor(private readonly prefix: string) {}\n\n private getKey(key: string): string {\n return `${this.prefix}${key}`;\n }\n\n get<T>(key: string): T | null {\n try {\n const item = localStorage.getItem(this.getKey(key));\n if (item === null) return null;\n return JSON.parse(item) as T;\n } catch {\n return null;\n }\n }\n\n set<T>(key: string, value: T): void {\n try {\n localStorage.setItem(this.getKey(key), JSON.stringify(value));\n } catch {\n // Storage quota exceeded or unavailable - silently fail\n }\n }\n\n remove(key: string): void {\n try {\n localStorage.removeItem(this.getKey(key));\n } catch {\n // Ignore errors\n }\n }\n\n clear(): void {\n try {\n const keys = Object.keys(localStorage);\n for (const key of keys) {\n if (key.startsWith(this.prefix)) {\n localStorage.removeItem(key);\n }\n }\n } catch {\n // Ignore errors\n }\n }\n}\n\n/**\n * In-memory storage fallback for environments without localStorage\n */\nclass MemoryStorageAdapter implements StorageAdapter {\n private readonly store = new Map<string, string>();\n\n constructor(private readonly prefix: string) {}\n\n private getKey(key: string): string {\n return `${this.prefix}${key}`;\n }\n\n get<T>(key: string): T | null {\n const item = this.store.get(this.getKey(key));\n if (item === undefined) return null;\n try {\n return JSON.parse(item) as T;\n } catch {\n return null;\n }\n }\n\n set<T>(key: string, value: T): void {\n this.store.set(this.getKey(key), JSON.stringify(value));\n }\n\n remove(key: string): void {\n this.store.delete(this.getKey(key));\n }\n\n clear(): void {\n const keysToDelete: string[] = [];\n for (const key of this.store.keys()) {\n if (key.startsWith(this.prefix)) {\n keysToDelete.push(key);\n }\n }\n for (const key of keysToDelete) {\n this.store.delete(key);\n }\n }\n}\n\n/**\n * Check if localStorage is available\n */\nfunction isLocalStorageAvailable(): boolean {\n try {\n const testKey = '__gamify_test__';\n localStorage.setItem(testKey, 'test');\n localStorage.removeItem(testKey);\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Create appropriate storage adapter based on environment\n */\nexport function createStorage(prefix: string): StorageAdapter {\n if (typeof window !== 'undefined' && isLocalStorageAvailable()) {\n return new LocalStorageAdapter(prefix);\n }\n return new MemoryStorageAdapter(prefix);\n}\n","import type { QueuedEvent, GamifyEvent, StorageAdapter } from '../types.js';\n\nconst QUEUE_STORAGE_KEY = 'event_queue';\nconst MAX_RETRY_ATTEMPTS = 3;\n\n/**\n * Generates a unique ID for queued events\n */\nfunction generateId(): string {\n return `${Date.now()}-${Math.random().toString(36).substring(2, 11)}`;\n}\n\n/**\n * Event queue for reliable event delivery with offline support\n */\nexport class EventQueue {\n private queue: QueuedEvent[] = [];\n private readonly maxSize: number;\n private readonly storage: StorageAdapter;\n\n constructor(storage: StorageAdapter, maxSize: number = 100) {\n this.storage = storage;\n this.maxSize = maxSize;\n this.loadFromStorage();\n }\n\n /**\n * Load persisted queue from storage\n */\n private loadFromStorage(): void {\n const persisted = this.storage.get<QueuedEvent[]>(QUEUE_STORAGE_KEY);\n if (persisted && Array.isArray(persisted)) {\n this.queue = persisted;\n }\n }\n\n /**\n * Persist queue to storage\n */\n private saveToStorage(): void {\n this.storage.set(QUEUE_STORAGE_KEY, this.queue);\n }\n\n /**\n * Add an event to the queue\n */\n enqueue(event: GamifyEvent): string {\n const queuedEvent: QueuedEvent = {\n id: generateId(),\n event,\n attempts: 0,\n createdAt: Date.now(),\n };\n\n this.queue.push(queuedEvent);\n\n // Trim queue if it exceeds max size (FIFO - remove oldest)\n while (this.queue.length > this.maxSize) {\n this.queue.shift();\n }\n\n this.saveToStorage();\n return queuedEvent.id;\n }\n\n /**\n * Get events ready for sending (batch)\n */\n peek(count: number): QueuedEvent[] {\n return this.queue\n .filter((item) => item.attempts < MAX_RETRY_ATTEMPTS)\n .slice(0, count);\n }\n\n /**\n * Mark events as successfully sent and remove from queue\n */\n acknowledge(ids: string[]): void {\n const idSet = new Set(ids);\n this.queue = this.queue.filter((item) => !idSet.has(item.id));\n this.saveToStorage();\n }\n\n /**\n * Mark events as failed (increment retry count)\n */\n nack(ids: string[]): void {\n const idSet = new Set(ids);\n for (const item of this.queue) {\n if (idSet.has(item.id)) {\n item.attempts++;\n }\n }\n // Remove events that exceeded max retries\n this.queue = this.queue.filter((item) => item.attempts < MAX_RETRY_ATTEMPTS);\n this.saveToStorage();\n }\n\n /**\n * Get current queue size\n */\n size(): number {\n return this.queue.length;\n }\n\n /**\n * Check if queue has pending events\n */\n hasPending(): boolean {\n return this.queue.some((item) => item.attempts < MAX_RETRY_ATTEMPTS);\n }\n\n /**\n * Clear all events from queue\n */\n clear(): void {\n this.queue = [];\n this.saveToStorage();\n }\n}\n","import type { GamifyEvent, ApiResponse } from '../types.js';\n\ninterface HttpResponse<T> {\n success: boolean;\n data?: T;\n error?: string;\n}\n\n/**\n * HTTP client for sending events to the API\n * Uses fetch with keepalive for reliable delivery during page unload\n */\nexport class HttpClient {\n private readonly endpoint: string;\n private readonly apiKey: string;\n private readonly debug: boolean;\n\n constructor(endpoint: string, apiKey: string, debug: boolean = false) {\n this.endpoint = endpoint;\n this.apiKey = apiKey;\n this.debug = debug;\n }\n\n /**\n * Log debug messages\n */\n private log(message: string, data?: unknown): void {\n if (this.debug) {\n console.log(`[Gamify] ${message}`, data ?? '');\n }\n }\n\n /**\n * Generic GET request\n */\n async get<T>(path: string): Promise<HttpResponse<T>> {\n this.log(`GET ${path}`);\n\n try {\n const response = await fetch(`${this.endpoint}${path}`, {\n method: 'GET',\n headers: {\n 'Content-Type': 'application/json',\n 'X-API-Key': this.apiKey,\n },\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n this.log(`GET error: ${response.status}`, errorText);\n return { success: false, error: `HTTP ${response.status}: ${errorText}` };\n }\n\n const data = await response.json();\n return { success: true, data };\n } catch (error) {\n const message = error instanceof Error ? error.message : 'Unknown error';\n this.log('GET network error', message);\n return { success: false, error: message };\n }\n }\n\n /**\n * Generic POST request\n */\n async post<T>(path: string, body: unknown): Promise<HttpResponse<T>> {\n this.log(`POST ${path}`, body);\n\n try {\n const response = await fetch(`${this.endpoint}${path}`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'X-API-Key': this.apiKey,\n },\n body: JSON.stringify(body),\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n this.log(`POST error: ${response.status}`, errorText);\n return { success: false, error: `HTTP ${response.status}: ${errorText}` };\n }\n\n const data = await response.json();\n return { success: true, data };\n } catch (error) {\n const message = error instanceof Error ? error.message : 'Unknown error';\n this.log('POST network error', message);\n return { success: false, error: message };\n }\n }\n\n /**\n * Send a batch of events to the API\n */\n async sendEvents(events: GamifyEvent[]): Promise<ApiResponse> {\n if (events.length === 0) {\n return { success: true };\n }\n\n this.log(`Sending ${events.length} events`, events);\n\n try {\n const response = await fetch(`${this.endpoint}/events/batch`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'X-API-Key': this.apiKey,\n },\n body: JSON.stringify({ events }),\n keepalive: true,\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n this.log(`API error: ${response.status}`, errorText);\n return { success: false, error: `HTTP ${response.status}: ${errorText}` };\n }\n\n this.log('Events sent successfully');\n return { success: true };\n } catch (error) {\n const message = error instanceof Error ? error.message : 'Unknown error';\n this.log('Network error', message);\n return { success: false, error: message };\n }\n }\n\n /**\n * Send a single event using beacon API (for page unload)\n * Falls back to fetch if beacon is not available\n */\n sendBeacon(events: GamifyEvent[]): boolean {\n if (events.length === 0) {\n return true;\n }\n\n this.log(`Sending ${events.length} events via beacon`);\n\n if (typeof navigator !== 'undefined' && navigator.sendBeacon) {\n const blob = new Blob(\n [JSON.stringify({ events, apiKey: this.apiKey })],\n { type: 'application/json' }\n );\n return navigator.sendBeacon(`${this.endpoint}/events/batch`, blob);\n }\n\n // Fallback: fire-and-forget fetch with keepalive\n fetch(`${this.endpoint}/events/batch`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'X-API-Key': this.apiKey,\n },\n body: JSON.stringify({ events }),\n keepalive: true,\n }).catch(() => {\n // Ignore errors during unload\n });\n\n return true;\n }\n}\n","import type {\n GamifyConfig,\n SessionRequest,\n SessionResponse,\n CartItem,\n StorageAdapter,\n} from '../types.js';\nimport { HttpClient } from '../network/index.js';\n\nconst SESSION_TOKEN_KEY = 'session_token';\nconst SESSION_DATA_KEY = 'session_data';\n\n/**\n * SessionManager - Manages cart sessions with discount calculations\n */\nexport class SessionManager {\n private readonly client: HttpClient;\n private readonly storage: StorageAdapter;\n private readonly debug: boolean;\n private sessionToken: string | null = null;\n private cachedSession: SessionResponse | null = null;\n\n constructor(\n config: Required<GamifyConfig>,\n client: HttpClient,\n storage: StorageAdapter,\n ) {\n this.client = client;\n this.storage = storage;\n this.debug = config.debug;\n\n // Load persisted session token\n this.sessionToken = this.storage.get<string>(SESSION_TOKEN_KEY);\n this.cachedSession = this.storage.get<SessionResponse>(SESSION_DATA_KEY);\n }\n\n /**\n * Create or update a session with cart items\n */\n async updateCart(\n userId: string,\n items: CartItem[],\n coupons?: string[],\n currency?: string,\n ): Promise<SessionResponse> {\n const request: SessionRequest = {\n userId,\n items,\n coupons,\n currency,\n };\n\n this.log('Updating cart', request);\n\n const response = await this.client.post<SessionResponse>(\n '/v1/sessions',\n request,\n );\n\n if (response.success && response.data) {\n this.sessionToken = response.data.sessionToken;\n this.cachedSession = response.data;\n this.storage.set(SESSION_TOKEN_KEY, this.sessionToken);\n this.storage.set(SESSION_DATA_KEY, this.cachedSession);\n\n this.log('Cart updated', response.data);\n return response.data;\n }\n\n throw new Error(response.error || 'Failed to update cart');\n }\n\n /**\n * Get current session by token\n */\n async getSession(): Promise<SessionResponse | null> {\n if (!this.sessionToken) {\n return null;\n }\n\n this.log('Getting session', this.sessionToken);\n\n const response = await this.client.get<SessionResponse>(\n `/v1/sessions/${this.sessionToken}`,\n );\n\n if (response.success && response.data) {\n this.cachedSession = response.data;\n this.storage.set(SESSION_DATA_KEY, this.cachedSession);\n return response.data;\n }\n\n // Session not found, clear local state\n if (response.error?.includes('not found')) {\n this.clearSession();\n }\n\n return null;\n }\n\n /**\n * Apply a coupon to the current session\n */\n async applyCoupon(code: string): Promise<SessionResponse> {\n if (!this.sessionToken) {\n throw new Error('No active session');\n }\n\n this.log('Applying coupon', code);\n\n const response = await this.client.post<SessionResponse>(\n `/v1/sessions/${this.sessionToken}/coupons`,\n { code },\n );\n\n if (response.success && response.data) {\n this.cachedSession = response.data;\n this.storage.set(SESSION_DATA_KEY, this.cachedSession);\n\n this.log('Coupon applied', response.data);\n return response.data;\n }\n\n throw new Error(response.error || 'Failed to apply coupon');\n }\n\n /**\n * Complete the session (checkout)\n */\n async complete(): Promise<SessionResponse> {\n if (!this.sessionToken) {\n throw new Error('No active session');\n }\n\n this.log('Completing session');\n\n const response = await this.client.post<SessionResponse>(\n `/v1/sessions/${this.sessionToken}/complete`,\n {},\n );\n\n if (response.success && response.data) {\n this.cachedSession = response.data;\n this.storage.set(SESSION_DATA_KEY, this.cachedSession);\n\n this.log('Session completed', response.data);\n return response.data;\n }\n\n throw new Error(response.error || 'Failed to complete session');\n }\n\n /**\n * Clear the current session\n */\n clearSession(): void {\n this.sessionToken = null;\n this.cachedSession = null;\n this.storage.remove(SESSION_TOKEN_KEY);\n this.storage.remove(SESSION_DATA_KEY);\n\n this.log('Session cleared');\n }\n\n /**\n * Get cached session data\n */\n getCachedSession(): SessionResponse | null {\n return this.cachedSession;\n }\n\n /**\n * Get current session token\n */\n getSessionToken(): string | null {\n return this.sessionToken;\n }\n\n /**\n * Check if there's an active session\n */\n hasActiveSession(): boolean {\n return (\n this.sessionToken !== null &&\n this.cachedSession?.status === 'active'\n );\n }\n\n private log(message: string, data?: unknown): void {\n if (this.debug) {\n console.log(`[Gamify:Session] ${message}`, data ?? '');\n }\n }\n}\n","import type {\n GamifyConfig,\n LoyaltyProfile,\n LoyaltyHistoryResponse,\n StorageAdapter,\n} from '../types.js';\nimport { HttpClient } from '../network/index.js';\n\nconst LOYALTY_PROFILE_KEY = 'loyalty_profile';\n\n/**\n * LoyaltyManager - Manages customer loyalty points and tiers\n */\nexport class LoyaltyManager {\n private readonly client: HttpClient;\n private readonly storage: StorageAdapter;\n private readonly debug: boolean;\n private cachedProfile: LoyaltyProfile | null = null;\n\n constructor(\n config: Required<GamifyConfig>,\n client: HttpClient,\n storage: StorageAdapter,\n ) {\n this.client = client;\n this.storage = storage;\n this.debug = config.debug;\n\n // Load cached profile\n this.cachedProfile = this.storage.get<LoyaltyProfile>(LOYALTY_PROFILE_KEY);\n }\n\n /**\n * Get customer loyalty profile\n */\n async getProfile(userId: string): Promise<LoyaltyProfile> {\n this.log('Getting loyalty profile', userId);\n\n const response = await this.client.get<LoyaltyProfile>(\n `/v1/customer/profile?userId=${encodeURIComponent(userId)}`,\n );\n\n if (response.success && response.data) {\n this.cachedProfile = response.data;\n this.storage.set(LOYALTY_PROFILE_KEY, this.cachedProfile);\n\n this.log('Profile loaded', response.data);\n return response.data;\n }\n\n throw new Error(response.error || 'Failed to get loyalty profile');\n }\n\n /**\n * Get customer transaction history\n */\n async getHistory(\n userId: string,\n limit?: number,\n offset?: number,\n ): Promise<LoyaltyHistoryResponse> {\n this.log('Getting loyalty history', { userId, limit, offset });\n\n const params = new URLSearchParams();\n params.set('userId', userId);\n if (limit !== undefined) params.set('limit', String(limit));\n if (offset !== undefined) params.set('offset', String(offset));\n\n const response = await this.client.get<LoyaltyHistoryResponse>(\n `/v1/customer/history?${params.toString()}`,\n );\n\n if (response.success && response.data) {\n this.log('History loaded', response.data);\n return response.data;\n }\n\n throw new Error(response.error || 'Failed to get loyalty history');\n }\n\n /**\n * Get cached loyalty profile\n */\n getCachedProfile(): LoyaltyProfile | null {\n return this.cachedProfile;\n }\n\n /**\n * Get current points balance from cache\n */\n getPoints(): number {\n return this.cachedProfile?.points ?? 0;\n }\n\n /**\n * Get current tier from cache\n */\n getTier(): LoyaltyProfile['tier'] {\n return this.cachedProfile?.tier ?? null;\n }\n\n /**\n * Get next tier info from cache\n */\n getNextTier(): LoyaltyProfile['nextTier'] {\n return this.cachedProfile?.nextTier ?? null;\n }\n\n /**\n * Clear cached profile\n */\n clearCache(): void {\n this.cachedProfile = null;\n this.storage.remove(LOYALTY_PROFILE_KEY);\n this.log('Cache cleared');\n }\n\n /**\n * Refresh profile from server\n */\n async refresh(userId: string): Promise<LoyaltyProfile> {\n return this.getProfile(userId);\n }\n\n private log(message: string, data?: unknown): void {\n if (this.debug) {\n console.log(`[Gamify:Loyalty] ${message}`, data ?? '');\n }\n }\n}\n","import type { GamifyConfig, StorageAdapter } from '../types.js';\n\nconst REFERRER_KEY = '__gamify_referrer';\nconst REFERRER_DETECTED_AT_KEY = '__gamify_referrer_detected_at';\n\n/**\n * Referral Manager - Handles URL parameter detection and referrer storage\n *\n * Automatically detects `?ref=code` parameter from URLs and stores\n * the referrer code in localStorage for attribution tracking.\n */\nexport class ReferralManager {\n private readonly config: Required<GamifyConfig>;\n private readonly storage: StorageAdapter;\n private referrerCode: string | null = null;\n\n constructor(\n config: Required<GamifyConfig>,\n storage: StorageAdapter\n ) {\n this.config = config;\n this.storage = storage;\n\n // Load stored referrer\n this.referrerCode = this.storage.get<string>(REFERRER_KEY);\n\n // Auto-detect referrer from URL on initialization\n this.detectReferrerFromUrl();\n }\n\n /**\n * Log debug messages\n */\n private log(message: string, data?: unknown): void {\n if (this.config.debug) {\n console.log(`[Gamify:Referral] ${message}`, data ?? '');\n }\n }\n\n /**\n * Detect referrer code from current URL\n * Looks for `?ref=` parameter\n */\n detectReferrerFromUrl(): string | null {\n if (typeof window === 'undefined') {\n return null;\n }\n\n try {\n const urlParams = new URLSearchParams(window.location.search);\n const refCode = urlParams.get('ref');\n\n if (refCode && refCode.length > 0) {\n this.log('Detected referrer from URL', { refCode });\n this.setReferrer(refCode);\n return refCode;\n }\n } catch (error) {\n this.log('Error detecting referrer from URL', error);\n }\n\n return null;\n }\n\n /**\n * Manually set the referrer code\n */\n setReferrer(code: string): void {\n if (!code || code.length === 0) {\n this.log('Invalid referrer code provided');\n return;\n }\n\n this.referrerCode = code;\n this.storage.set(REFERRER_KEY, code);\n this.storage.set(REFERRER_DETECTED_AT_KEY, new Date().toISOString());\n\n this.log('Referrer code stored', { code });\n }\n\n /**\n * Get the current referrer code\n */\n getReferrer(): string | null {\n return this.referrerCode;\n }\n\n /**\n * Check if a referrer code is present\n */\n hasReferrer(): boolean {\n return this.referrerCode !== null;\n }\n\n /**\n * Clear the referrer code\n */\n clearReferrer(): void {\n this.referrerCode = null;\n this.storage.remove(REFERRER_KEY);\n this.storage.remove(REFERRER_DETECTED_AT_KEY);\n this.log('Referrer code cleared');\n }\n\n /**\n * Get referrer data to include in event properties\n */\n getReferrerProperties(): Record<string, unknown> {\n if (!this.referrerCode) {\n return {};\n }\n\n return {\n referrer: this.referrerCode,\n referrer_detected_at: this.storage.get<string>(REFERRER_DETECTED_AT_KEY),\n };\n }\n}\n","import type {\n GamifyConfig,\n StorageAdapter,\n AffiliateStats,\n AffiliateStatsResponse,\n LeaderboardResponse,\n} from '../types.js';\nimport type { HttpClient } from '../network/index.js';\n\nconst AFFILIATE_STATS_CACHE_KEY = 'affiliate_stats';\nconst CACHE_TTL_MS = 60000; // 1 minute\n\n/**\n * Affiliate Manager - Handles fetching affiliate stats and leaderboard\n */\nexport class AffiliateManager {\n private readonly config: Required<GamifyConfig>;\n private readonly client: HttpClient;\n private readonly storage: StorageAdapter;\n private cachedStats: AffiliateStats | null = null;\n private lastFetchTime: number = 0;\n\n constructor(\n config: Required<GamifyConfig>,\n client: HttpClient,\n storage: StorageAdapter\n ) {\n this.config = config;\n this.client = client;\n this.storage = storage;\n\n // Load cached stats\n const cached = this.storage.get<{ stats: AffiliateStats; fetchedAt: number }>(\n AFFILIATE_STATS_CACHE_KEY\n );\n if (cached) {\n this.cachedStats = cached.stats;\n this.lastFetchTime = cached.fetchedAt;\n }\n }\n\n /**\n * Log debug messages\n */\n private log(message: string, data?: unknown): void {\n if (this.config.debug) {\n console.log(`[Gamify:Affiliate] ${message}`, data ?? '');\n }\n }\n\n /**\n * Get affiliate stats for the current user\n * @param userId - User ID to fetch stats for\n * @param forceRefresh - Force refresh from API\n */\n async getStats(userId: string, forceRefresh = false): Promise<AffiliateStats> {\n // Check cache validity\n const now = Date.now();\n const cacheValid = !forceRefresh && this.cachedStats && now - this.lastFetchTime < CACHE_TTL_MS;\n\n if (cacheValid && this.cachedStats) {\n this.log('Returning cached affiliate stats');\n return this.cachedStats;\n }\n\n this.log('Fetching affiliate stats from API', { userId });\n\n try {\n const response = await this.client.get<AffiliateStatsResponse>(\n `/v1/customer/affiliate/profile?userId=${encodeURIComponent(userId)}`\n );\n\n if (response.success && response.data?.stats) {\n const stats = response.data.stats;\n\n this.cachedStats = stats;\n this.lastFetchTime = now;\n\n // Persist to storage\n this.storage.set(AFFILIATE_STATS_CACHE_KEY, {\n stats,\n fetchedAt: now,\n });\n\n this.log('Affiliate stats fetched', stats);\n return stats;\n }\n\n throw new Error(response.error ?? 'Invalid response from affiliate stats API');\n } catch (error) {\n this.log('Error fetching affiliate stats', error);\n throw error;\n }\n }\n\n /**\n * Get cached affiliate stats (if available)\n */\n getCachedStats(): AffiliateStats | null {\n return this.cachedStats;\n }\n\n /**\n * Get leaderboard data\n * @param limit - Number of entries to fetch (default: 10)\n */\n async getLeaderboard(limit = 10): Promise<LeaderboardResponse> {\n this.log('Fetching leaderboard', { limit });\n\n try {\n const response = await this.client.get<LeaderboardResponse>(\n `/v1/customer/affiliate/leaderboard?limit=${limit}`\n );\n\n if (response.success && response.data?.entries) {\n this.log('Leaderboard fetched', { count: response.data.entries.length });\n return response.data;\n }\n\n throw new Error(response.error ?? 'Invalid response from leaderboard API');\n } catch (error) {\n this.log('Error fetching leaderboard', error);\n throw error;\n }\n }\n\n /**\n * Clear cached affiliate stats\n */\n clearCache(): void {\n this.cachedStats = null;\n this.lastFetchTime = 0;\n this.storage.remove(AFFILIATE_STATS_CACHE_KEY);\n this.log('Affiliate stats cache cleared');\n }\n}\n","import type {\n GamifyConfig,\n GamifyEvent,\n UserTraits,\n StorageAdapter,\n CartItem,\n AffiliateStats,\n LeaderboardResponse,\n // Gamification types\n QuestsResponse,\n StreaksResponse,\n FreezeResponse,\n BadgesResponse,\n RewardsStoreResponse,\n RedemptionResult,\n} from './types.js';\nimport { createStorage } from './storage/index.js';\nimport { EventQueue } from './queue/index.js';\nimport { HttpClient } from './network/index.js';\nimport { SessionManager } from './session/index.js';\nimport { LoyaltyManager } from './loyalty/index.js';\nimport { ReferralManager } from './referral/index.js';\nimport { AffiliateManager } from './affiliate/index.js';\n\nconst DEFAULT_ENDPOINT = 'https://boostapi-production.up.railway.app';\nconst DEFAULT_FLUSH_INTERVAL = 10000; // 10 seconds\nconst DEFAULT_MAX_BATCH_SIZE = 10;\nconst DEFAULT_STORAGE_PREFIX = 'gamify_';\nconst USER_ID_KEY = 'user_id';\nconst ANONYMOUS_ID_KEY = 'anonymous_id';\nconst USER_TRAITS_KEY = 'user_traits';\n\n/**\n * Events that require a secret key (server-side only)\n * These will be rejected by the API when sent with a publishable key\n */\nconst TRUSTED_EVENTS = [\n 'purchase',\n 'checkout_complete',\n 'checkout_success',\n 'commission.created',\n 'referral_success',\n 'user.leveled_up',\n 'step.completed',\n 'quest.completed',\n] as const;\n\n/**\n * Generate anonymous ID for unidentified users\n */\nfunction generateAnonymousId(): string {\n return `anon_${Date.now()}_${Math.random().toString(36).substring(2, 11)}`;\n}\n\n/**\n * Gamify SDK - Framework-agnostic event tracking\n */\nexport class Gamify {\n private readonly config: Required<GamifyConfig>;\n private readonly storage: StorageAdapter;\n private readonly queue: EventQueue;\n private readonly client: HttpClient;\n private readonly _session: SessionManager;\n private readonly _loyalty: LoyaltyManager;\n private readonly _referral: ReferralManager;\n private readonly _affiliate: AffiliateManager;\n private flushTimer: ReturnType<typeof setInterval> | null = null;\n private anonymousId: string;\n private userId: string | null = null;\n private userTraits: UserTraits | null = null;\n private initialized = false;\n\n constructor(config: GamifyConfig) {\n if (!config.apiKey) {\n throw new Error('[Gamify] API key is required');\n }\n\n // Reject secret keys in client-side SDK\n if (config.apiKey.startsWith('sk_')) {\n throw new Error(\n '[Gamify] Secret keys (sk_live_*) cannot be used in the browser. ' +\n 'Use a publishable key (pk_live_*) for client-side tracking. ' +\n 'For server-side tracking, use @gamifyio/node instead.'\n );\n }\n\n this.config = {\n apiKey: config.apiKey,\n endpoint: config.endpoint ?? DEFAULT_ENDPOINT,\n debug: config.debug ?? false,\n flushInterval: config.flushInterval ?? DEFAULT_FLUSH_INTERVAL,\n maxBatchSize: config.maxBatchSize ?? DEFAULT_MAX_BATCH_SIZE,\n storagePrefix: config.storagePrefix ?? DEFAULT_STORAGE_PREFIX,\n };\n\n this.storage = createStorage(this.config.storagePrefix);\n this.queue = new EventQueue(this.storage);\n this.client = new HttpClient(\n this.config.endpoint,\n this.config.apiKey,\n this.config.debug\n );\n\n // Initialize Session and Loyalty managers\n this._session = new SessionManager(this.config, this.client, this.storage);\n this._loyalty = new LoyaltyManager(this.config, this.client, this.storage);\n\n // Issue #22: Initialize Referral and Affiliate managers\n this._referral = new ReferralManager(this.config, this.storage);\n this._affiliate = new AffiliateManager(this.config, this.client, this.storage);\n\n // Load or generate anonymous ID\n const storedAnonymousId = this.storage.get<string>(ANONYMOUS_ID_KEY);\n this.anonymousId = storedAnonymousId ?? generateAnonymousId();\n if (!storedAnonymousId) {\n this.storage.set(ANONYMOUS_ID_KEY, this.anonymousId);\n }\n\n // Load persisted user identity\n this.userId = this.storage.get<string>(USER_ID_KEY);\n this.userTraits = this.storage.get<UserTraits>(USER_TRAITS_KEY);\n\n this.initialize();\n }\n\n /**\n * Initialize the SDK\n */\n private initialize(): void {\n if (this.initialized) return;\n\n this.log('Initializing SDK');\n\n // Start flush timer\n this.startFlushTimer();\n\n // Handle page unload - flush remaining events\n if (typeof window !== 'undefined') {\n window.addEventListener('beforeunload', () => this.onBeforeUnload());\n window.addEventListener('pagehide', () => this.onBeforeUnload());\n }\n\n this.initialized = true;\n this.log('SDK initialized', {\n anonymousId: this.anonymousId,\n userId: this.userId,\n });\n }\n\n /**\n * Log debug messages\n */\n private log(message: string, data?: unknown): void {\n if (this.config.debug) {\n console.log(`[Gamify] ${message}`, data ?? '');\n }\n }\n\n /**\n * Start the automatic flush timer\n */\n private startFlushTimer(): void {\n if (this.flushTimer) {\n clearInterval(this.flushTimer);\n }\n this.flushTimer = setInterval(() => {\n void this.flush();\n }, this.config.flushInterval);\n }\n\n /**\n * Handle page unload - send remaining events via beacon\n */\n private onBeforeUnload(): void {\n if (this.flushTimer) {\n clearInterval(this.flushTimer);\n this.flushTimer = null;\n }\n\n const pending = this.queue.peek(this.config.maxBatchSize);\n if (pending.length > 0) {\n const events = pending.map((item) => item.event);\n this.client.sendBeacon(events);\n }\n }\n\n /**\n * Identify a user with optional traits\n * User ID persists across page reloads\n */\n identify(userId: string, traits?: UserTraits): void {\n if (!userId || typeof userId !== 'string') {\n this.log('Invalid user ID provided');\n return;\n }\n\n this.log('Identifying user', { userId, traits });\n\n this.userId = userId;\n this.storage.set(USER_ID_KEY, userId);\n\n if (traits) {\n this.userTraits = { ...this.userTraits, ...traits };\n this.storage.set(USER_TRAITS_KEY, this.userTraits);\n }\n\n // Track identify event\n this.track('$identify', {\n userId,\n traits: traits ?? {},\n });\n }\n\n /**\n * Track an event\n */\n track(eventType: string, properties?: Record<string, unknown>): void {\n if (!eventType || typeof eventType !== 'string') {\n this.log('Invalid event type provided');\n return;\n }\n\n // Warn about trusted events that require server-side tracking\n if ((TRUSTED_EVENTS as readonly string[]).includes(eventType)) {\n console.warn(\n `[Gamify] Event \"${eventType}\" requires a secret key and will be rejected. ` +\n `Use @gamifyio/node on your server to track this event.`\n );\n }\n\n // Issue #22: Inject referrer properties into event\n const referrerProps = this._referral.getReferrerProperties();\n\n const event: GamifyEvent = {\n type: eventType,\n properties: { ...referrerProps, ...properties },\n timestamp: new Date().toISOString(),\n anonymousId: this.anonymousId,\n ...(this.userId && { userId: this.userId }),\n };\n\n this.log('Tracking event', event);\n\n this.queue.enqueue(event);\n\n // Auto-flush if batch size reached\n if (this.queue.size() >= this.config.maxBatchSize) {\n void this.flush();\n }\n }\n\n /**\n * Flush pending events to the server\n */\n async flush(): Promise<void> {\n const pending = this.queue.peek(this.config.maxBatchSize);\n if (pending.length === 0) {\n return;\n }\n\n const events = pending.map((item) => item.event);\n const ids = pending.map((item) => item.id);\n\n this.log(`Flushing ${events.length} events`);\n\n const result = await this.client.sendEvents(events);\n\n if (result.success) {\n this.queue.acknowledge(ids);\n } else {\n this.queue.nack(ids);\n }\n }\n\n /**\n * Reset the SDK state (logout user)\n */\n reset(): void {\n this.log('Resetting SDK state');\n\n this.userId = null;\n this.userTraits = null;\n this.storage.remove(USER_ID_KEY);\n this.storage.remove(USER_TRAITS_KEY);\n\n // Generate new anonymous ID\n this.anonymousId = generateAnonymousId();\n this.storage.set(ANONYMOUS_ID_KEY, this.anonymousId);\n }\n\n /**\n * Get current user ID (null if not identified)\n */\n getUserId(): string | null {\n return this.userId;\n }\n\n /**\n * Get anonymous ID\n */\n getAnonymousId(): string {\n return this.anonymousId;\n }\n\n /**\n * Get pending event count\n */\n getPendingCount(): number {\n return this.queue.size();\n }\n\n /**\n * Shutdown the SDK gracefully\n */\n shutdown(): void {\n this.log('Shutting down SDK');\n\n if (this.flushTimer) {\n clearInterval(this.flushTimer);\n this.flushTimer = null;\n }\n\n // Attempt final flush via beacon\n const pending = this.queue.peek(this.config.maxBatchSize);\n if (pending.length > 0) {\n const events = pending.map((item) => item.event);\n this.client.sendBeacon(events);\n }\n }\n\n // ============================================\n // Session Module (Issue #14)\n // ============================================\n\n /**\n * Get the session manager\n */\n get session(): SessionManager {\n return this._session;\n }\n\n /**\n * Convenience method: Update cart and get discounts\n */\n async updateCart(\n items: CartItem[],\n coupons?: string[],\n currency?: string,\n ) {\n if (!this.userId) {\n throw new Error('[Gamify] User must be identified before updating cart');\n }\n return this._session.updateCart(this.userId, items, coupons, currency);\n }\n\n // ============================================\n // Loyalty Module (Issue #15)\n // ============================================\n\n /**\n * Get the loyalty manager\n */\n get loyalty(): LoyaltyManager {\n return this._loyalty;\n }\n\n /**\n * Convenience method: Get loyalty profile for current user\n */\n async getLoyaltyProfile() {\n if (!this.userId) {\n throw new Error('[Gamify] User must be identified before getting loyalty profile');\n }\n return this._loyalty.getProfile(this.userId);\n }\n\n /**\n * Convenience method: Get loyalty history for current user\n */\n async getLoyaltyHistory(limit?: number, offset?: number) {\n if (!this.userId) {\n throw new Error('[Gamify] User must be identified before getting loyalty history');\n }\n return this._loyalty.getHistory(this.userId, limit, offset);\n }\n\n // ============================================\n // Referral Module (Issue #22)\n // ============================================\n\n /**\n * Get the referral manager\n */\n get referral(): ReferralManager {\n return this._referral;\n }\n\n /**\n * Convenience method: Get current referrer code\n */\n getReferrer(): string | null {\n return this._referral.getReferrer();\n }\n\n /**\n * Convenience method: Set referrer code manually\n */\n setReferrer(code: string): void {\n this._referral.setReferrer(code);\n }\n\n // ============================================\n // Affiliate Module (Issue #22)\n // ============================================\n\n /**\n * Get the affiliate manager\n */\n get affiliate(): AffiliateManager {\n return this._affiliate;\n }\n\n /**\n * Convenience method: Get affiliate stats for current user\n */\n async getAffiliateStats(forceRefresh = false): Promise<AffiliateStats> {\n if (!this.userId) {\n throw new Error('[Gamify] User must be identified before getting affiliate stats');\n }\n return this._affiliate.getStats(this.userId, forceRefresh);\n }\n\n /**\n * Convenience method: Get leaderboard\n */\n async getLeaderboard(limit = 10): Promise<LeaderboardResponse> {\n return this._affiliate.getLeaderboard(limit);\n }\n\n // ============================================\n // Quests Module (Issue #25-28)\n // ============================================\n\n /**\n * Get user's quest progress\n */\n async getQuests(): Promise<QuestsResponse> {\n if (!this.userId) {\n throw new Error('[Gamify] User must be identified before getting quests');\n }\n const response = await this.client.get<QuestsResponse>(\n `/v1/customer/quests?userId=${encodeURIComponent(this.userId)}`\n );\n if (response.success && response.data) {\n return response.data;\n }\n throw new Error(response.error || 'Failed to get quests');\n }\n\n // ============================================\n // Streaks Module (Issue #32)\n // ============================================\n\n /**\n * Get user's streaks with progress\n */\n async getStreaks(): Promise<StreaksResponse> {\n if (!this.userId) {\n throw new Error('[Gamify] User must be identified before getting streaks');\n }\n const response = await this.client.get<StreaksResponse>(\n `/v1/customer/streaks?userId=${encodeURIComponent(this.userId)}`\n );\n if (response.success && response.data) {\n return response.data;\n }\n throw new Error(response.error || 'Failed to get streaks');\n }\n\n /**\n * Use a freeze token for a streak\n */\n async useStreakFreeze(ruleId: string): Promise<FreezeResponse> {\n if (!this.userId) {\n throw new Error('[Gamify] User must be identified before using freeze');\n }\n const response = await this.client.post<FreezeResponse>(\n `/v1/customer/streaks/${encodeURIComponent(ruleId)}/freeze`,\n { userId: this.userId }\n );\n if (response.success && response.data) {\n return response.data;\n }\n throw new Error(response.error || 'Failed to use freeze');\n }\n\n // ============================================\n // Badges Module (Issue #33)\n // ============================================\n\n /**\n * Get user's badge collection\n */\n async getBadges(category?: string): Promise<BadgesResponse> {\n if (!this.userId) {\n throw new Error('[Gamify] User must be identified before getting badges');\n }\n let url = `/v1/customer/badges?userId=${encodeURIComponent(this.userId)}`;\n if (category) {\n url += `&category=${encodeURIComponent(category)}`;\n }\n const response = await this.client.get<BadgesResponse>(url);\n if (response.success && response.data) {\n return response.data;\n }\n throw new Error(response.error || 'Failed to get badges');\n }\n\n /**\n * Get available badge categories\n */\n async getBadgeCategories(): Promise<string[]> {\n const response = await this.client.get<string[]>('/v1/customer/badges/categories');\n if (response.success && response.data) {\n return response.data;\n }\n throw new Error(response.error || 'Failed to get badge categories');\n }\n\n // ============================================\n // Rewards Module (Issue #34)\n // ============================================\n\n /**\n * Get rewards store items\n */\n async getRewardsStore(): Promise<RewardsStoreResponse> {\n if (!this.userId) {\n throw new Error('[Gamify] User must be identified before getting rewards');\n }\n const response = await this.client.get<RewardsStoreResponse>(\n `/v1/customer/store?userId=${encodeURIComponent(this.userId)}`\n );\n if (response.success && response.data) {\n return response.data;\n }\n throw new Error(response.error || 'Failed to get rewards store');\n }\n\n /**\n * Redeem a reward item\n */\n async redeemReward(itemId: string): Promise<RedemptionResult> {\n if (!this.userId) {\n throw new Error('[Gamify] User must be identified before redeeming rewards');\n }\n const response = await this.client.post<RedemptionResult>(\n '/v1/customer/store/redeem',\n { userId: this.userId, itemId }\n );\n if (response.success && response.data) {\n return response.data;\n }\n throw new Error(response.error || 'Failed to redeem reward');\n }\n}\n","/**\n * Configuration options for the Gamify SDK\n */\nexport interface GamifyConfig {\n /** API key for authentication */\n apiKey: string;\n /** Base URL for the API endpoint */\n endpoint?: string;\n /** Enable debug logging */\n debug?: boolean;\n /** Flush interval in milliseconds (default: 10000) */\n flushInterval?: number;\n /** Maximum events to batch before auto-flush (default: 10) */\n maxBatchSize?: number;\n /** Storage key prefix (default: 'gamify_') */\n storagePrefix?: string;\n}\n\n/**\n * Event payload structure\n */\nexport interface GamifyEvent {\n /** Event type (e.g., 'page_view', 'click', 'purchase') */\n type: string;\n /** Event properties */\n properties?: Record<string, unknown>;\n /** Timestamp when event occurred */\n timestamp: string;\n /** User ID if identified */\n userId?: string;\n /** Anonymous ID for unidentified users */\n anonymousId: string;\n}\n\n/**\n * User traits for identify calls\n */\nexport interface UserTraits {\n email?: string;\n name?: string;\n [key: string]: unknown;\n}\n\n/**\n * Internal queued event with metadata\n */\nexport interface QueuedEvent {\n id: string;\n event: GamifyEvent;\n attempts: number;\n createdAt: number;\n}\n\n/**\n * API response structure\n */\nexport interface ApiResponse {\n success: boolean;\n error?: string;\n}\n\n/**\n * Storage interface for persistence layer\n */\nexport interface StorageAdapter {\n get<T>(key: string): T | null;\n set<T>(key: string, value: T): void;\n remove(key: string): void;\n clear(): void;\n}\n\n// ============================================\n// Issue #14: Session Types\n// ============================================\n\n/**\n * Cart item structure for sessions\n */\nexport interface CartItem {\n sku: string;\n name: string;\n quantity: number;\n unitPrice: number;\n category?: string;\n brand?: string;\n metadata?: Record<string, unknown>;\n}\n\n/**\n * Session request to create/update cart\n */\nexport interface SessionRequest {\n userId: string;\n items: CartItem[];\n coupons?: string[];\n currency?: string;\n}\n\n/**\n * Applied effect from rule engine\n */\nexport interface AppliedEffect {\n type: string;\n ruleId?: string;\n params: Record<string, unknown>;\n discountAmount?: number;\n}\n\n/**\n * Session response from API\n */\nexport interface SessionResponse {\n sessionToken: string;\n items: CartItem[];\n subtotal: number;\n discount: number;\n total: number;\n coupons: string[];\n appliedEffects: AppliedEffect[];\n rejectedCoupons?: string[];\n currency: string;\n status: string;\n}\n\n// ============================================\n// Issue #15: Loyalty Types\n// ============================================\n\n/**\n * Loyalty tier information\n */\nexport interface LoyaltyTier {\n id: string;\n name: string;\n level: number;\n benefits: Record<string, unknown>;\n color?: string | null;\n iconUrl?: string | null;\n}\n\n/**\n * Next tier information\n */\nexport interface NextTierInfo {\n id: string;\n name: string;\n minPoints: number;\n pointsNeeded: number;\n}\n\n/**\n * Loyalty summary statistics\n */\nexport interface LoyaltySummary {\n totalEarned: number;\n totalRedeemed: number;\n transactionCount: number;\n}\n\n/**\n * Customer loyalty profile\n */\nexport interface LoyaltyProfile {\n userId: string;\n points: number;\n tier: LoyaltyTier | null;\n nextTier: NextTierInfo | null;\n summary: LoyaltySummary;\n}\n\n/**\n * Loyalty transaction entry\n */\nexport interface LoyaltyTransaction {\n id: string;\n amount: number;\n balance: number;\n type: 'earn' | 'redeem' | 'expire' | 'adjust' | 'bonus';\n referenceId?: string;\n referenceType?: string;\n description?: string;\n createdAt: string;\n}\n\n/**\n * Loyalty history response\n */\nexport interface LoyaltyHistoryResponse {\n transactions: LoyaltyTransaction[];\n total: number;\n}\n\n// ============================================\n// Issue #22: Affiliate Types\n// ============================================\n\n/**\n * Affiliate tier/plan information\n */\nexport interface AffiliateTier {\n id: string;\n name: string;\n type: 'PERCENTAGE' | 'FIXED';\n value: number;\n}\n\n/**\n * Referral information\n */\nexport interface ReferralInfo {\n id: string;\n referredExternalId: string;\n referralCode: string;\n createdAt: string;\n}\n\n/**\n * Affiliate earnings summary\n */\nexport interface AffiliateEarnings {\n totalEarned: number;\n totalPending: number;\n totalPaid: number;\n transactionCount: number;\n currency: string;\n}\n\n/**\n * Affiliate statistics profile\n */\nexport interface AffiliateStats {\n userId: string;\n referralCode: string | null;\n referralCount: number;\n earnings: AffiliateEarnings;\n tier: AffiliateTier | null;\n}\n\n/**\n * Affiliate stats API response\n */\nexport interface AffiliateStatsResponse {\n stats: AffiliateStats;\n}\n\n/**\n * Leaderboard entry\n */\nexport interface LeaderboardEntry {\n rank: number;\n userId: string;\n displayName?: string;\n referralCount: number;\n totalEarnings: number;\n tier?: AffiliateTier | null;\n}\n\n/**\n * Leaderboard response\n */\nexport interface LeaderboardResponse {\n entries: LeaderboardEntry[];\n total: number;\n currentUserRank?: number;\n}\n\n// ============================================\n// Issue #25-28: Quest Types\n// ============================================\n\n/**\n * Quest step definition\n */\nexport interface QuestStep {\n id: string;\n name: string;\n description?: string;\n eventName: string;\n requiredCount: number;\n order: number;\n currentCount: number;\n completed: boolean;\n}\n\n/**\n * Quest with user progress\n */\nexport interface QuestWithProgress {\n id: string;\n name: string;\n description?: string;\n xpReward: number;\n badgeReward?: string;\n status: 'not_started' | 'in_progress' | 'completed';\n percentComplete: number;\n steps: QuestStep[];\n startedAt?: string;\n completedAt?: string;\n}\n\n/**\n * Quests response from API\n */\nexport interface QuestsResponse {\n quests: QuestWithProgress[];\n}\n\n// ============================================\n// Issue #32: Streak Types\n// ============================================\n\n/**\n * Streak milestone definition\n */\nexport interface StreakMilestone {\n day: number;\n rewardXp: number;\n badgeId?: string;\n reached: boolean;\n}\n\n/**\n * User streak with progress\n */\nexport interface StreakWithProgress {\n id: string;\n name: string;\n description?: string;\n eventType: string;\n frequency: string;\n currentCount: number;\n maxStreak: number;\n status: string;\n lastActivityDate?: string;\n freezeInventory: number;\n freezeUsedToday: boolean;\n nextMilestone?: {\n day: number;\n rewardXp: number;\n badgeId?: string;\n };\n milestones: StreakMilestone[];\n}\n\n/**\n * Streaks response from API\n */\nexport interface StreaksResponse {\n streaks: StreakWithProgress[];\n stats: {\n totalActive: number;\n longestCurrent: number;\n longestEver: number;\n };\n}\n\n/**\n * Freeze response\n */\nexport interface FreezeResponse {\n success: boolean;\n remainingFreezes: number;\n message: string;\n}\n\n// ============================================\n// Issue #33: Badge Types\n// ============================================\n\n/**\n * Badge rarity levels\n */\nexport type BadgeRarity = 'COMMON' | 'RARE' | 'EPIC' | 'LEGENDARY';\n\n/**\n * Badge with unlock status\n */\nexport interface BadgeWithStatus {\n id: string;\n name: string;\n description?: string;\n iconUrl?: string;\n imageUrl?: string;\n rarity: BadgeRarity;\n visibility: 'PUBLIC' | 'HIDDEN';\n category?: string;\n isUnlocked: boolean;\n unlockedAt?: string;\n}\n\n/**\n * Badges response from API\n */\nexport interface BadgesResponse {\n badges: BadgeWithStatus[];\n stats: {\n total: number;\n unlocked: number;\n byRarity: Record<string, { total: number; unlocked: number }>;\n };\n}\n\n// ============================================\n// Issue #34: Rewards Types\n// ============================================\n\n/**\n * Reward item from store\n */\nexport interface RewardItem {\n id: string;\n name: string;\n description?: string;\n pointsCost: number;\n imageUrl?: string;\n category?: string;\n stock?: number;\n requiredBadgeId?: string;\n requiredBadgeName?: string;\n canAfford: boolean;\n hasBadge: boolean;\n isAvailable: boolean;\n}\n\n/**\n * Rewards store response\n */\nexport interface RewardsStoreResponse {\n items: RewardItem[];\n userPoints: number;\n}\n\n/**\n * Redemption result\n */\nexport interface RedemptionResult {\n success: boolean;\n transactionId?: string;\n message: string;\n remainingPoints?: number;\n}\n\n// ============================================\n// Theme System\n// ============================================\n\n/**\n * Theme configuration for SDK components\n * All colors should be valid CSS color values\n */\nexport interface Theme {\n // Base colors\n /** Card/container background color */\n background: string;\n /** Secondary background (inputs, nested elements) */\n backgroundSecondary: string;\n /** Primary text color */\n foreground: string;\n /** Secondary/muted text color */\n foregroundSecondary: string;\n /** Border color */\n border: string;\n\n // Accent colors\n /** Primary accent color (buttons, highlights) */\n primary: string;\n /** Text on primary color */\n primaryForeground: string;\n\n // Status colors\n /** Success/positive states */\n success: string;\n /** Warning/pending states */\n warning: string;\n /** Error states */\n error: string;\n\n // Rarity colors (for badges/achievements)\n rarityCommon: string;\n rarityRare: string;\n rarityEpic: string;\n rarityLegendary: string;\n\n // Medal colors (for leaderboard)\n medalGold: string;\n medalSilver: string;\n medalBronze: string;\n\n // Component-specific (optional overrides)\n cardBackground?: string;\n cardBorder?: string;\n inputBackground?: string;\n inputBorder?: string;\n buttonBackground?: string;\n buttonForeground?: string;\n}\n\n/**\n * Default dark theme matching the playground UI\n */\nexport const defaultTheme: Theme = {\n // Base colors (dark theme with glass effect)\n background: 'rgba(255, 255, 255, 0.05)',\n backgroundSecondary: 'rgba(255, 255, 255, 0.1)',\n foreground: '#ffffff',\n foregroundSecondary: 'rgba(255, 255, 255, 0.7)',\n border: 'rgba(255, 255, 255, 0.1)',\n\n // Accent colors (purple like playground)\n primary: '#8B5CF6',\n primaryForeground: '#ffffff',\n\n // Status colors\n success: '#22C55E',\n warning: '#F59E0B',\n error: '#EF4444',\n\n // Rarity colors (matching playground)\n rarityCommon: '#9CA3AF',\n rarityRare: '#3B82F6',\n rarityEpic: '#8B5CF6',\n rarityLegendary: '#F59E0B',\n\n // Medal colors\n medalGold: '#FFD700',\n medalSilver: '#C0C0C0',\n medalBronze: '#CD7F32',\n};\n"]}
package/dist/index.d.cts CHANGED
@@ -379,6 +379,49 @@ interface RedemptionResult {
379
379
  message: string;
380
380
  remainingPoints?: number;
381
381
  }
382
+ /**
383
+ * Theme configuration for SDK components
384
+ * All colors should be valid CSS color values
385
+ */
386
+ interface Theme {
387
+ /** Card/container background color */
388
+ background: string;
389
+ /** Secondary background (inputs, nested elements) */
390
+ backgroundSecondary: string;
391
+ /** Primary text color */
392
+ foreground: string;
393
+ /** Secondary/muted text color */
394
+ foregroundSecondary: string;
395
+ /** Border color */
396
+ border: string;
397
+ /** Primary accent color (buttons, highlights) */
398
+ primary: string;
399
+ /** Text on primary color */
400
+ primaryForeground: string;
401
+ /** Success/positive states */
402
+ success: string;
403
+ /** Warning/pending states */
404
+ warning: string;
405
+ /** Error states */
406
+ error: string;
407
+ rarityCommon: string;
408
+ rarityRare: string;
409
+ rarityEpic: string;
410
+ rarityLegendary: string;
411
+ medalGold: string;
412
+ medalSilver: string;
413
+ medalBronze: string;
414
+ cardBackground?: string;
415
+ cardBorder?: string;
416
+ inputBackground?: string;
417
+ inputBorder?: string;
418
+ buttonBackground?: string;
419
+ buttonForeground?: string;
420
+ }
421
+ /**
422
+ * Default dark theme matching the playground UI
423
+ */
424
+ declare const defaultTheme: Theme;
382
425
 
383
426
  interface HttpResponse<T> {
384
427
  success: boolean;
@@ -775,4 +818,4 @@ declare class EventQueue {
775
818
  clear(): void;
776
819
  }
777
820
 
778
- export { type AffiliateEarnings, AffiliateManager, type AffiliateStats, type AffiliateStatsResponse, type AffiliateTier, type ApiResponse, type AppliedEffect, type BadgeRarity, type BadgeWithStatus, type BadgesResponse, type CartItem, EventQueue, type FreezeResponse, Gamify, type GamifyConfig, type GamifyEvent, HttpClient, type LeaderboardEntry, type LeaderboardResponse, type LoyaltyHistoryResponse, LoyaltyManager, type LoyaltyProfile, type LoyaltySummary, type LoyaltyTier, type LoyaltyTransaction, type NextTierInfo, type QuestStep, type QuestWithProgress, type QuestsResponse, type QueuedEvent, type RedemptionResult, type ReferralInfo, ReferralManager, type RewardItem, type RewardsStoreResponse, SessionManager, type SessionRequest, type SessionResponse, type StorageAdapter, type StreakMilestone, type StreakWithProgress, type StreaksResponse, type UserTraits, createStorage };
821
+ export { type AffiliateEarnings, AffiliateManager, type AffiliateStats, type AffiliateStatsResponse, type AffiliateTier, type ApiResponse, type AppliedEffect, type BadgeRarity, type BadgeWithStatus, type BadgesResponse, type CartItem, EventQueue, type FreezeResponse, Gamify, type GamifyConfig, type GamifyEvent, HttpClient, type LeaderboardEntry, type LeaderboardResponse, type LoyaltyHistoryResponse, LoyaltyManager, type LoyaltyProfile, type LoyaltySummary, type LoyaltyTier, type LoyaltyTransaction, type NextTierInfo, type QuestStep, type QuestWithProgress, type QuestsResponse, type QueuedEvent, type RedemptionResult, type ReferralInfo, ReferralManager, type RewardItem, type RewardsStoreResponse, SessionManager, type SessionRequest, type SessionResponse, type StorageAdapter, type StreakMilestone, type StreakWithProgress, type StreaksResponse, type Theme, type UserTraits, createStorage, defaultTheme };
package/dist/index.d.ts CHANGED
@@ -379,6 +379,49 @@ interface RedemptionResult {
379
379
  message: string;
380
380
  remainingPoints?: number;
381
381
  }
382
+ /**
383
+ * Theme configuration for SDK components
384
+ * All colors should be valid CSS color values
385
+ */
386
+ interface Theme {
387
+ /** Card/container background color */
388
+ background: string;
389
+ /** Secondary background (inputs, nested elements) */
390
+ backgroundSecondary: string;
391
+ /** Primary text color */
392
+ foreground: string;
393
+ /** Secondary/muted text color */
394
+ foregroundSecondary: string;
395
+ /** Border color */
396
+ border: string;
397
+ /** Primary accent color (buttons, highlights) */
398
+ primary: string;
399
+ /** Text on primary color */
400
+ primaryForeground: string;
401
+ /** Success/positive states */
402
+ success: string;
403
+ /** Warning/pending states */
404
+ warning: string;
405
+ /** Error states */
406
+ error: string;
407
+ rarityCommon: string;
408
+ rarityRare: string;
409
+ rarityEpic: string;
410
+ rarityLegendary: string;
411
+ medalGold: string;
412
+ medalSilver: string;
413
+ medalBronze: string;
414
+ cardBackground?: string;
415
+ cardBorder?: string;
416
+ inputBackground?: string;
417
+ inputBorder?: string;
418
+ buttonBackground?: string;
419
+ buttonForeground?: string;
420
+ }
421
+ /**
422
+ * Default dark theme matching the playground UI
423
+ */
424
+ declare const defaultTheme: Theme;
382
425
 
383
426
  interface HttpResponse<T> {
384
427
  success: boolean;
@@ -775,4 +818,4 @@ declare class EventQueue {
775
818
  clear(): void;
776
819
  }
777
820
 
778
- export { type AffiliateEarnings, AffiliateManager, type AffiliateStats, type AffiliateStatsResponse, type AffiliateTier, type ApiResponse, type AppliedEffect, type BadgeRarity, type BadgeWithStatus, type BadgesResponse, type CartItem, EventQueue, type FreezeResponse, Gamify, type GamifyConfig, type GamifyEvent, HttpClient, type LeaderboardEntry, type LeaderboardResponse, type LoyaltyHistoryResponse, LoyaltyManager, type LoyaltyProfile, type LoyaltySummary, type LoyaltyTier, type LoyaltyTransaction, type NextTierInfo, type QuestStep, type QuestWithProgress, type QuestsResponse, type QueuedEvent, type RedemptionResult, type ReferralInfo, ReferralManager, type RewardItem, type RewardsStoreResponse, SessionManager, type SessionRequest, type SessionResponse, type StorageAdapter, type StreakMilestone, type StreakWithProgress, type StreaksResponse, type UserTraits, createStorage };
821
+ export { type AffiliateEarnings, AffiliateManager, type AffiliateStats, type AffiliateStatsResponse, type AffiliateTier, type ApiResponse, type AppliedEffect, type BadgeRarity, type BadgeWithStatus, type BadgesResponse, type CartItem, EventQueue, type FreezeResponse, Gamify, type GamifyConfig, type GamifyEvent, HttpClient, type LeaderboardEntry, type LeaderboardResponse, type LoyaltyHistoryResponse, LoyaltyManager, type LoyaltyProfile, type LoyaltySummary, type LoyaltyTier, type LoyaltyTransaction, type NextTierInfo, type QuestStep, type QuestWithProgress, type QuestsResponse, type QueuedEvent, type RedemptionResult, type ReferralInfo, ReferralManager, type RewardItem, type RewardsStoreResponse, SessionManager, type SessionRequest, type SessionResponse, type StorageAdapter, type StreakMilestone, type StreakWithProgress, type StreaksResponse, type Theme, type UserTraits, createStorage, defaultTheme };
package/dist/index.js CHANGED
@@ -1,3 +1,3 @@
1
- var k=Object.defineProperty;var C=(n,e,t)=>e in n?k(n,e,{enumerable:true,configurable:true,writable:true,value:t}):n[e]=t;var r=(n,e,t)=>C(n,typeof e!="symbol"?e+"":e,t);var y=class{constructor(e){this.prefix=e;}getKey(e){return `${this.prefix}${e}`}get(e){try{let t=localStorage.getItem(this.getKey(e));return t===null?null:JSON.parse(t)}catch{return null}}set(e,t){try{localStorage.setItem(this.getKey(e),JSON.stringify(t));}catch{}}remove(e){try{localStorage.removeItem(this.getKey(e));}catch{}}clear(){try{let e=Object.keys(localStorage);for(let t of e)t.startsWith(this.prefix)&&localStorage.removeItem(t);}catch{}}},m=class{constructor(e){this.prefix=e;r(this,"store",new Map);}getKey(e){return `${this.prefix}${e}`}get(e){let t=this.store.get(this.getKey(e));if(t===void 0)return null;try{return JSON.parse(t)}catch{return null}}set(e,t){this.store.set(this.getKey(e),JSON.stringify(t));}remove(e){this.store.delete(this.getKey(e));}clear(){let e=[];for(let t of this.store.keys())t.startsWith(this.prefix)&&e.push(t);for(let t of e)this.store.delete(t);}};function L(){try{let n="__gamify_test__";return localStorage.setItem(n,"test"),localStorage.removeItem(n),!0}catch{return false}}function p(n){return typeof window<"u"&&L()?new y(n):new m(n)}var A="event_queue";function x(){return `${Date.now()}-${Math.random().toString(36).substring(2,11)}`}var l=class{constructor(e,t=100){r(this,"queue",[]);r(this,"maxSize");r(this,"storage");this.storage=e,this.maxSize=t,this.loadFromStorage();}loadFromStorage(){let e=this.storage.get(A);e&&Array.isArray(e)&&(this.queue=e);}saveToStorage(){this.storage.set(A,this.queue);}enqueue(e){let t={id:x(),event:e,attempts:0,createdAt:Date.now()};for(this.queue.push(t);this.queue.length>this.maxSize;)this.queue.shift();return this.saveToStorage(),t.id}peek(e){return this.queue.filter(t=>t.attempts<3).slice(0,e)}acknowledge(e){let t=new Set(e);this.queue=this.queue.filter(s=>!t.has(s.id)),this.saveToStorage();}nack(e){let t=new Set(e);for(let s of this.queue)t.has(s.id)&&s.attempts++;this.queue=this.queue.filter(s=>s.attempts<3),this.saveToStorage();}size(){return this.queue.length}hasPending(){return this.queue.some(e=>e.attempts<3)}clear(){this.queue=[],this.saveToStorage();}};var d=class{constructor(e,t,s=false){r(this,"endpoint");r(this,"apiKey");r(this,"debug");this.endpoint=e,this.apiKey=t,this.debug=s;}log(e,t){this.debug&&console.log(`[Gamify] ${e}`,t??"");}async get(e){this.log(`GET ${e}`);try{let t=await fetch(`${this.endpoint}${e}`,{method:"GET",headers:{"Content-Type":"application/json","X-API-Key":this.apiKey}});if(!t.ok){let o=await t.text();return this.log(`GET error: ${t.status}`,o),{success:!1,error:`HTTP ${t.status}: ${o}`}}return {success:!0,data:await t.json()}}catch(t){let s=t instanceof Error?t.message:"Unknown error";return this.log("GET network error",s),{success:false,error:s}}}async post(e,t){this.log(`POST ${e}`,t);try{let s=await fetch(`${this.endpoint}${e}`,{method:"POST",headers:{"Content-Type":"application/json","X-API-Key":this.apiKey},body:JSON.stringify(t)});if(!s.ok){let i=await s.text();return this.log(`POST error: ${s.status}`,i),{success:!1,error:`HTTP ${s.status}: ${i}`}}return {success:!0,data:await s.json()}}catch(s){let o=s instanceof Error?s.message:"Unknown error";return this.log("POST network error",o),{success:false,error:o}}}async sendEvents(e){if(e.length===0)return {success:true};this.log(`Sending ${e.length} events`,e);try{let t=await fetch(`${this.endpoint}/events/batch`,{method:"POST",headers:{"Content-Type":"application/json","X-API-Key":this.apiKey},body:JSON.stringify({events:e}),keepalive:!0});if(!t.ok){let s=await t.text();return this.log(`API error: ${t.status}`,s),{success:!1,error:`HTTP ${t.status}: ${s}`}}return this.log("Events sent successfully"),{success:!0}}catch(t){let s=t instanceof Error?t.message:"Unknown error";return this.log("Network error",s),{success:false,error:s}}}sendBeacon(e){if(e.length===0)return true;if(this.log(`Sending ${e.length} events via beacon`),typeof navigator<"u"&&navigator.sendBeacon){let t=new Blob([JSON.stringify({events:e,apiKey:this.apiKey})],{type:"application/json"});return navigator.sendBeacon(`${this.endpoint}/events/batch`,t)}return fetch(`${this.endpoint}/events/batch`,{method:"POST",headers:{"Content-Type":"application/json","X-API-Key":this.apiKey},body:JSON.stringify({events:e}),keepalive:true}).catch(()=>{}),true}};var S="session_token",h="session_data",g=class{constructor(e,t,s){r(this,"client");r(this,"storage");r(this,"debug");r(this,"sessionToken",null);r(this,"cachedSession",null);this.client=t,this.storage=s,this.debug=e.debug,this.sessionToken=this.storage.get(S),this.cachedSession=this.storage.get(h);}async updateCart(e,t,s,o){let i={userId:e,items:t,coupons:s,currency:o};this.log("Updating cart",i);let a=await this.client.post("/v1/sessions",i);if(a.success&&a.data)return this.sessionToken=a.data.sessionToken,this.cachedSession=a.data,this.storage.set(S,this.sessionToken),this.storage.set(h,this.cachedSession),this.log("Cart updated",a.data),a.data;throw new Error(a.error||"Failed to update cart")}async getSession(){if(!this.sessionToken)return null;this.log("Getting session",this.sessionToken);let e=await this.client.get(`/v1/sessions/${this.sessionToken}`);return e.success&&e.data?(this.cachedSession=e.data,this.storage.set(h,this.cachedSession),e.data):(e.error?.includes("not found")&&this.clearSession(),null)}async applyCoupon(e){if(!this.sessionToken)throw new Error("No active session");this.log("Applying coupon",e);let t=await this.client.post(`/v1/sessions/${this.sessionToken}/coupons`,{code:e});if(t.success&&t.data)return this.cachedSession=t.data,this.storage.set(h,this.cachedSession),this.log("Coupon applied",t.data),t.data;throw new Error(t.error||"Failed to apply coupon")}async complete(){if(!this.sessionToken)throw new Error("No active session");this.log("Completing session");let e=await this.client.post(`/v1/sessions/${this.sessionToken}/complete`,{});if(e.success&&e.data)return this.cachedSession=e.data,this.storage.set(h,this.cachedSession),this.log("Session completed",e.data),e.data;throw new Error(e.error||"Failed to complete session")}clearSession(){this.sessionToken=null,this.cachedSession=null,this.storage.remove(S),this.storage.remove(h),this.log("Session cleared");}getCachedSession(){return this.cachedSession}getSessionToken(){return this.sessionToken}hasActiveSession(){return this.sessionToken!==null&&this.cachedSession?.status==="active"}log(e,t){this.debug&&console.log(`[Gamify:Session] ${e}`,t??"");}};var v="loyalty_profile",f=class{constructor(e,t,s){r(this,"client");r(this,"storage");r(this,"debug");r(this,"cachedProfile",null);this.client=t,this.storage=s,this.debug=e.debug,this.cachedProfile=this.storage.get(v);}async getProfile(e){this.log("Getting loyalty profile",e);let t=await this.client.get(`/v1/customer/profile?userId=${encodeURIComponent(e)}`);if(t.success&&t.data)return this.cachedProfile=t.data,this.storage.set(v,this.cachedProfile),this.log("Profile loaded",t.data),t.data;throw new Error(t.error||"Failed to get loyalty profile")}async getHistory(e,t,s){this.log("Getting loyalty history",{userId:e,limit:t,offset:s});let o=new URLSearchParams;o.set("userId",e),t!==void 0&&o.set("limit",String(t)),s!==void 0&&o.set("offset",String(s));let i=await this.client.get(`/v1/customer/history?${o.toString()}`);if(i.success&&i.data)return this.log("History loaded",i.data),i.data;throw new Error(i.error||"Failed to get loyalty history")}getCachedProfile(){return this.cachedProfile}getPoints(){return this.cachedProfile?.points??0}getTier(){return this.cachedProfile?.tier??null}getNextTier(){return this.cachedProfile?.nextTier??null}clearCache(){this.cachedProfile=null,this.storage.remove(v),this.log("Cache cleared");}async refresh(e){return this.getProfile(e)}log(e,t){this.debug&&console.log(`[Gamify:Loyalty] ${e}`,t??"");}};var w="__gamify_referrer",R="__gamify_referrer_detected_at",c=class{constructor(e,t){r(this,"config");r(this,"storage");r(this,"referrerCode",null);this.config=e,this.storage=t,this.referrerCode=this.storage.get(w),this.detectReferrerFromUrl();}log(e,t){this.config.debug&&console.log(`[Gamify:Referral] ${e}`,t??"");}detectReferrerFromUrl(){if(typeof window>"u")return null;try{let t=new URLSearchParams(window.location.search).get("ref");if(t&&t.length>0)return this.log("Detected referrer from URL",{refCode:t}),this.setReferrer(t),t}catch(e){this.log("Error detecting referrer from URL",e);}return null}setReferrer(e){if(!e||e.length===0){this.log("Invalid referrer code provided");return}this.referrerCode=e,this.storage.set(w,e),this.storage.set(R,new Date().toISOString()),this.log("Referrer code stored",{code:e});}getReferrer(){return this.referrerCode}hasReferrer(){return this.referrerCode!==null}clearReferrer(){this.referrerCode=null,this.storage.remove(w),this.storage.remove(R),this.log("Referrer code cleared");}getReferrerProperties(){return this.referrerCode?{referrer:this.referrerCode,referrer_detected_at:this.storage.get(R)}:{}}};var T="affiliate_stats";var u=class{constructor(e,t,s){r(this,"config");r(this,"client");r(this,"storage");r(this,"cachedStats",null);r(this,"lastFetchTime",0);this.config=e,this.client=t,this.storage=s;let o=this.storage.get(T);o&&(this.cachedStats=o.stats,this.lastFetchTime=o.fetchedAt);}log(e,t){this.config.debug&&console.log(`[Gamify:Affiliate] ${e}`,t??"");}async getStats(e,t=false){let s=Date.now();if(!t&&this.cachedStats&&s-this.lastFetchTime<6e4&&this.cachedStats)return this.log("Returning cached affiliate stats"),this.cachedStats;this.log("Fetching affiliate stats from API",{userId:e});try{let i=await this.client.get(`/v1/customer/affiliate/profile?userId=${encodeURIComponent(e)}`);if(i.success&&i.data?.stats){let a=i.data.stats;return this.cachedStats=a,this.lastFetchTime=s,this.storage.set(T,{stats:a,fetchedAt:s}),this.log("Affiliate stats fetched",a),a}throw new Error(i.error??"Invalid response from affiliate stats API")}catch(i){throw this.log("Error fetching affiliate stats",i),i}}getCachedStats(){return this.cachedStats}async getLeaderboard(e=10){this.log("Fetching leaderboard",{limit:e});try{let t=await this.client.get(`/v1/customer/affiliate/leaderboard?limit=${e}`);if(t.success&&t.data?.entries)return this.log("Leaderboard fetched",{count:t.data.entries.length}),t.data;throw new Error(t.error??"Invalid response from leaderboard API")}catch(t){throw this.log("Error fetching leaderboard",t),t}}clearCache(){this.cachedStats=null,this.lastFetchTime=0,this.storage.remove(T),this.log("Affiliate stats cache cleared");}};var G="https://boostapi-production.up.railway.app",U=1e4,$=10,q="gamify_",E="user_id",I="anonymous_id",b="user_traits",F=["purchase","checkout_complete","checkout_success","commission.created","referral_success","user.leveled_up","step.completed","quest.completed"];function P(){return `anon_${Date.now()}_${Math.random().toString(36).substring(2,11)}`}var _=class{constructor(e){r(this,"config");r(this,"storage");r(this,"queue");r(this,"client");r(this,"_session");r(this,"_loyalty");r(this,"_referral");r(this,"_affiliate");r(this,"flushTimer",null);r(this,"anonymousId");r(this,"userId",null);r(this,"userTraits",null);r(this,"initialized",false);if(!e.apiKey)throw new Error("[Gamify] API key is required");if(e.apiKey.startsWith("sk_"))throw new Error("[Gamify] Secret keys (sk_live_*) cannot be used in the browser. Use a publishable key (pk_live_*) for client-side tracking. For server-side tracking, use @gamifyio/node instead.");this.config={apiKey:e.apiKey,endpoint:e.endpoint??G,debug:e.debug??false,flushInterval:e.flushInterval??U,maxBatchSize:e.maxBatchSize??$,storagePrefix:e.storagePrefix??q},this.storage=p(this.config.storagePrefix),this.queue=new l(this.storage),this.client=new d(this.config.endpoint,this.config.apiKey,this.config.debug),this._session=new g(this.config,this.client,this.storage),this._loyalty=new f(this.config,this.client,this.storage),this._referral=new c(this.config,this.storage),this._affiliate=new u(this.config,this.client,this.storage);let t=this.storage.get(I);this.anonymousId=t??P(),t||this.storage.set(I,this.anonymousId),this.userId=this.storage.get(E),this.userTraits=this.storage.get(b),this.initialize();}initialize(){this.initialized||(this.log("Initializing SDK"),this.startFlushTimer(),typeof window<"u"&&(window.addEventListener("beforeunload",()=>this.onBeforeUnload()),window.addEventListener("pagehide",()=>this.onBeforeUnload())),this.initialized=true,this.log("SDK initialized",{anonymousId:this.anonymousId,userId:this.userId}));}log(e,t){this.config.debug&&console.log(`[Gamify] ${e}`,t??"");}startFlushTimer(){this.flushTimer&&clearInterval(this.flushTimer),this.flushTimer=setInterval(()=>{this.flush();},this.config.flushInterval);}onBeforeUnload(){this.flushTimer&&(clearInterval(this.flushTimer),this.flushTimer=null);let e=this.queue.peek(this.config.maxBatchSize);if(e.length>0){let t=e.map(s=>s.event);this.client.sendBeacon(t);}}identify(e,t){if(!e||typeof e!="string"){this.log("Invalid user ID provided");return}this.log("Identifying user",{userId:e,traits:t}),this.userId=e,this.storage.set(E,e),t&&(this.userTraits={...this.userTraits,...t},this.storage.set(b,this.userTraits)),this.track("$identify",{userId:e,traits:t??{}});}track(e,t){if(!e||typeof e!="string"){this.log("Invalid event type provided");return}F.includes(e)&&console.warn(`[Gamify] Event "${e}" requires a secret key and will be rejected. Use @gamifyio/node on your server to track this event.`);let s=this._referral.getReferrerProperties(),o={type:e,properties:{...s,...t},timestamp:new Date().toISOString(),anonymousId:this.anonymousId,...this.userId&&{userId:this.userId}};this.log("Tracking event",o),this.queue.enqueue(o),this.queue.size()>=this.config.maxBatchSize&&this.flush();}async flush(){let e=this.queue.peek(this.config.maxBatchSize);if(e.length===0)return;let t=e.map(i=>i.event),s=e.map(i=>i.id);this.log(`Flushing ${t.length} events`),(await this.client.sendEvents(t)).success?this.queue.acknowledge(s):this.queue.nack(s);}reset(){this.log("Resetting SDK state"),this.userId=null,this.userTraits=null,this.storage.remove(E),this.storage.remove(b),this.anonymousId=P(),this.storage.set(I,this.anonymousId);}getUserId(){return this.userId}getAnonymousId(){return this.anonymousId}getPendingCount(){return this.queue.size()}shutdown(){this.log("Shutting down SDK"),this.flushTimer&&(clearInterval(this.flushTimer),this.flushTimer=null);let e=this.queue.peek(this.config.maxBatchSize);if(e.length>0){let t=e.map(s=>s.event);this.client.sendBeacon(t);}}get session(){return this._session}async updateCart(e,t,s){if(!this.userId)throw new Error("[Gamify] User must be identified before updating cart");return this._session.updateCart(this.userId,e,t,s)}get loyalty(){return this._loyalty}async getLoyaltyProfile(){if(!this.userId)throw new Error("[Gamify] User must be identified before getting loyalty profile");return this._loyalty.getProfile(this.userId)}async getLoyaltyHistory(e,t){if(!this.userId)throw new Error("[Gamify] User must be identified before getting loyalty history");return this._loyalty.getHistory(this.userId,e,t)}get referral(){return this._referral}getReferrer(){return this._referral.getReferrer()}setReferrer(e){this._referral.setReferrer(e);}get affiliate(){return this._affiliate}async getAffiliateStats(e=false){if(!this.userId)throw new Error("[Gamify] User must be identified before getting affiliate stats");return this._affiliate.getStats(this.userId,e)}async getLeaderboard(e=10){return this._affiliate.getLeaderboard(e)}async getQuests(){if(!this.userId)throw new Error("[Gamify] User must be identified before getting quests");let e=await this.client.get(`/v1/customer/quests?userId=${encodeURIComponent(this.userId)}`);if(e.success&&e.data)return e.data;throw new Error(e.error||"Failed to get quests")}async getStreaks(){if(!this.userId)throw new Error("[Gamify] User must be identified before getting streaks");let e=await this.client.get(`/v1/customer/streaks?userId=${encodeURIComponent(this.userId)}`);if(e.success&&e.data)return e.data;throw new Error(e.error||"Failed to get streaks")}async useStreakFreeze(e){if(!this.userId)throw new Error("[Gamify] User must be identified before using freeze");let t=await this.client.post(`/v1/customer/streaks/${encodeURIComponent(e)}/freeze`,{userId:this.userId});if(t.success&&t.data)return t.data;throw new Error(t.error||"Failed to use freeze")}async getBadges(e){if(!this.userId)throw new Error("[Gamify] User must be identified before getting badges");let t=`/v1/customer/badges?userId=${encodeURIComponent(this.userId)}`;e&&(t+=`&category=${encodeURIComponent(e)}`);let s=await this.client.get(t);if(s.success&&s.data)return s.data;throw new Error(s.error||"Failed to get badges")}async getBadgeCategories(){let e=await this.client.get("/v1/customer/badges/categories");if(e.success&&e.data)return e.data;throw new Error(e.error||"Failed to get badge categories")}async getRewardsStore(){if(!this.userId)throw new Error("[Gamify] User must be identified before getting rewards");let e=await this.client.get(`/v1/customer/store?userId=${encodeURIComponent(this.userId)}`);if(e.success&&e.data)return e.data;throw new Error(e.error||"Failed to get rewards store")}async redeemReward(e){if(!this.userId)throw new Error("[Gamify] User must be identified before redeeming rewards");let t=await this.client.post("/v1/customer/store/redeem",{userId:this.userId,itemId:e});if(t.success&&t.data)return t.data;throw new Error(t.error||"Failed to redeem reward")}};
2
- export{u as AffiliateManager,l as EventQueue,_ as Gamify,d as HttpClient,f as LoyaltyManager,c as ReferralManager,g as SessionManager,p as createStorage};//# sourceMappingURL=index.js.map
1
+ var P=Object.defineProperty;var x=(o,e,t)=>e in o?P(o,e,{enumerable:true,configurable:true,writable:true,value:t}):o[e]=t;var s=(o,e,t)=>x(o,typeof e!="symbol"?e+"":e,t);var m=class{constructor(e){this.prefix=e;}getKey(e){return `${this.prefix}${e}`}get(e){try{let t=localStorage.getItem(this.getKey(e));return t===null?null:JSON.parse(t)}catch{return null}}set(e,t){try{localStorage.setItem(this.getKey(e),JSON.stringify(t));}catch{}}remove(e){try{localStorage.removeItem(this.getKey(e));}catch{}}clear(){try{let e=Object.keys(localStorage);for(let t of e)t.startsWith(this.prefix)&&localStorage.removeItem(t);}catch{}}},y=class{constructor(e){this.prefix=e;s(this,"store",new Map);}getKey(e){return `${this.prefix}${e}`}get(e){let t=this.store.get(this.getKey(e));if(t===void 0)return null;try{return JSON.parse(t)}catch{return null}}set(e,t){this.store.set(this.getKey(e),JSON.stringify(t));}remove(e){this.store.delete(this.getKey(e));}clear(){let e=[];for(let t of this.store.keys())t.startsWith(this.prefix)&&e.push(t);for(let t of e)this.store.delete(t);}};function _(){try{let o="__gamify_test__";return localStorage.setItem(o,"test"),localStorage.removeItem(o),!0}catch{return false}}function p(o){return typeof window<"u"&&_()?new m(o):new y(o)}var k="event_queue";function L(){return `${Date.now()}-${Math.random().toString(36).substring(2,11)}`}var l=class{constructor(e,t=100){s(this,"queue",[]);s(this,"maxSize");s(this,"storage");this.storage=e,this.maxSize=t,this.loadFromStorage();}loadFromStorage(){let e=this.storage.get(k);e&&Array.isArray(e)&&(this.queue=e);}saveToStorage(){this.storage.set(k,this.queue);}enqueue(e){let t={id:L(),event:e,attempts:0,createdAt:Date.now()};for(this.queue.push(t);this.queue.length>this.maxSize;)this.queue.shift();return this.saveToStorage(),t.id}peek(e){return this.queue.filter(t=>t.attempts<3).slice(0,e)}acknowledge(e){let t=new Set(e);this.queue=this.queue.filter(r=>!t.has(r.id)),this.saveToStorage();}nack(e){let t=new Set(e);for(let r of this.queue)t.has(r.id)&&r.attempts++;this.queue=this.queue.filter(r=>r.attempts<3),this.saveToStorage();}size(){return this.queue.length}hasPending(){return this.queue.some(e=>e.attempts<3)}clear(){this.queue=[],this.saveToStorage();}};var d=class{constructor(e,t,r=false){s(this,"endpoint");s(this,"apiKey");s(this,"debug");this.endpoint=e,this.apiKey=t,this.debug=r;}log(e,t){this.debug&&console.log(`[Gamify] ${e}`,t??"");}async get(e){this.log(`GET ${e}`);try{let t=await fetch(`${this.endpoint}${e}`,{method:"GET",headers:{"Content-Type":"application/json","X-API-Key":this.apiKey}});if(!t.ok){let n=await t.text();return this.log(`GET error: ${t.status}`,n),{success:!1,error:`HTTP ${t.status}: ${n}`}}return {success:!0,data:await t.json()}}catch(t){let r=t instanceof Error?t.message:"Unknown error";return this.log("GET network error",r),{success:false,error:r}}}async post(e,t){this.log(`POST ${e}`,t);try{let r=await fetch(`${this.endpoint}${e}`,{method:"POST",headers:{"Content-Type":"application/json","X-API-Key":this.apiKey},body:JSON.stringify(t)});if(!r.ok){let i=await r.text();return this.log(`POST error: ${r.status}`,i),{success:!1,error:`HTTP ${r.status}: ${i}`}}return {success:!0,data:await r.json()}}catch(r){let n=r instanceof Error?r.message:"Unknown error";return this.log("POST network error",n),{success:false,error:n}}}async sendEvents(e){if(e.length===0)return {success:true};this.log(`Sending ${e.length} events`,e);try{let t=await fetch(`${this.endpoint}/events/batch`,{method:"POST",headers:{"Content-Type":"application/json","X-API-Key":this.apiKey},body:JSON.stringify({events:e}),keepalive:!0});if(!t.ok){let r=await t.text();return this.log(`API error: ${t.status}`,r),{success:!1,error:`HTTP ${t.status}: ${r}`}}return this.log("Events sent successfully"),{success:!0}}catch(t){let r=t instanceof Error?t.message:"Unknown error";return this.log("Network error",r),{success:false,error:r}}}sendBeacon(e){if(e.length===0)return true;if(this.log(`Sending ${e.length} events via beacon`),typeof navigator<"u"&&navigator.sendBeacon){let t=new Blob([JSON.stringify({events:e,apiKey:this.apiKey})],{type:"application/json"});return navigator.sendBeacon(`${this.endpoint}/events/batch`,t)}return fetch(`${this.endpoint}/events/batch`,{method:"POST",headers:{"Content-Type":"application/json","X-API-Key":this.apiKey},body:JSON.stringify({events:e}),keepalive:true}).catch(()=>{}),true}};var S="session_token",g="session_data",f=class{constructor(e,t,r){s(this,"client");s(this,"storage");s(this,"debug");s(this,"sessionToken",null);s(this,"cachedSession",null);this.client=t,this.storage=r,this.debug=e.debug,this.sessionToken=this.storage.get(S),this.cachedSession=this.storage.get(g);}async updateCart(e,t,r,n){let i={userId:e,items:t,coupons:r,currency:n};this.log("Updating cart",i);let a=await this.client.post("/v1/sessions",i);if(a.success&&a.data)return this.sessionToken=a.data.sessionToken,this.cachedSession=a.data,this.storage.set(S,this.sessionToken),this.storage.set(g,this.cachedSession),this.log("Cart updated",a.data),a.data;throw new Error(a.error||"Failed to update cart")}async getSession(){if(!this.sessionToken)return null;this.log("Getting session",this.sessionToken);let e=await this.client.get(`/v1/sessions/${this.sessionToken}`);return e.success&&e.data?(this.cachedSession=e.data,this.storage.set(g,this.cachedSession),e.data):(e.error?.includes("not found")&&this.clearSession(),null)}async applyCoupon(e){if(!this.sessionToken)throw new Error("No active session");this.log("Applying coupon",e);let t=await this.client.post(`/v1/sessions/${this.sessionToken}/coupons`,{code:e});if(t.success&&t.data)return this.cachedSession=t.data,this.storage.set(g,this.cachedSession),this.log("Coupon applied",t.data),t.data;throw new Error(t.error||"Failed to apply coupon")}async complete(){if(!this.sessionToken)throw new Error("No active session");this.log("Completing session");let e=await this.client.post(`/v1/sessions/${this.sessionToken}/complete`,{});if(e.success&&e.data)return this.cachedSession=e.data,this.storage.set(g,this.cachedSession),this.log("Session completed",e.data),e.data;throw new Error(e.error||"Failed to complete session")}clearSession(){this.sessionToken=null,this.cachedSession=null,this.storage.remove(S),this.storage.remove(g),this.log("Session cleared");}getCachedSession(){return this.cachedSession}getSessionToken(){return this.sessionToken}hasActiveSession(){return this.sessionToken!==null&&this.cachedSession?.status==="active"}log(e,t){this.debug&&console.log(`[Gamify:Session] ${e}`,t??"");}};var v="loyalty_profile",h=class{constructor(e,t,r){s(this,"client");s(this,"storage");s(this,"debug");s(this,"cachedProfile",null);this.client=t,this.storage=r,this.debug=e.debug,this.cachedProfile=this.storage.get(v);}async getProfile(e){this.log("Getting loyalty profile",e);let t=await this.client.get(`/v1/customer/profile?userId=${encodeURIComponent(e)}`);if(t.success&&t.data)return this.cachedProfile=t.data,this.storage.set(v,this.cachedProfile),this.log("Profile loaded",t.data),t.data;throw new Error(t.error||"Failed to get loyalty profile")}async getHistory(e,t,r){this.log("Getting loyalty history",{userId:e,limit:t,offset:r});let n=new URLSearchParams;n.set("userId",e),t!==void 0&&n.set("limit",String(t)),r!==void 0&&n.set("offset",String(r));let i=await this.client.get(`/v1/customer/history?${n.toString()}`);if(i.success&&i.data)return this.log("History loaded",i.data),i.data;throw new Error(i.error||"Failed to get loyalty history")}getCachedProfile(){return this.cachedProfile}getPoints(){return this.cachedProfile?.points??0}getTier(){return this.cachedProfile?.tier??null}getNextTier(){return this.cachedProfile?.nextTier??null}clearCache(){this.cachedProfile=null,this.storage.remove(v),this.log("Cache cleared");}async refresh(e){return this.getProfile(e)}log(e,t){this.debug&&console.log(`[Gamify:Loyalty] ${e}`,t??"");}};var b="__gamify_referrer",R="__gamify_referrer_detected_at",u=class{constructor(e,t){s(this,"config");s(this,"storage");s(this,"referrerCode",null);this.config=e,this.storage=t,this.referrerCode=this.storage.get(b),this.detectReferrerFromUrl();}log(e,t){this.config.debug&&console.log(`[Gamify:Referral] ${e}`,t??"");}detectReferrerFromUrl(){if(typeof window>"u")return null;try{let t=new URLSearchParams(window.location.search).get("ref");if(t&&t.length>0)return this.log("Detected referrer from URL",{refCode:t}),this.setReferrer(t),t}catch(e){this.log("Error detecting referrer from URL",e);}return null}setReferrer(e){if(!e||e.length===0){this.log("Invalid referrer code provided");return}this.referrerCode=e,this.storage.set(b,e),this.storage.set(R,new Date().toISOString()),this.log("Referrer code stored",{code:e});}getReferrer(){return this.referrerCode}hasReferrer(){return this.referrerCode!==null}clearReferrer(){this.referrerCode=null,this.storage.remove(b),this.storage.remove(R),this.log("Referrer code cleared");}getReferrerProperties(){return this.referrerCode?{referrer:this.referrerCode,referrer_detected_at:this.storage.get(R)}:{}}};var w="affiliate_stats";var c=class{constructor(e,t,r){s(this,"config");s(this,"client");s(this,"storage");s(this,"cachedStats",null);s(this,"lastFetchTime",0);this.config=e,this.client=t,this.storage=r;let n=this.storage.get(w);n&&(this.cachedStats=n.stats,this.lastFetchTime=n.fetchedAt);}log(e,t){this.config.debug&&console.log(`[Gamify:Affiliate] ${e}`,t??"");}async getStats(e,t=false){let r=Date.now();if(!t&&this.cachedStats&&r-this.lastFetchTime<6e4&&this.cachedStats)return this.log("Returning cached affiliate stats"),this.cachedStats;this.log("Fetching affiliate stats from API",{userId:e});try{let i=await this.client.get(`/v1/customer/affiliate/profile?userId=${encodeURIComponent(e)}`);if(i.success&&i.data?.stats){let a=i.data.stats;return this.cachedStats=a,this.lastFetchTime=r,this.storage.set(w,{stats:a,fetchedAt:r}),this.log("Affiliate stats fetched",a),a}throw new Error(i.error??"Invalid response from affiliate stats API")}catch(i){throw this.log("Error fetching affiliate stats",i),i}}getCachedStats(){return this.cachedStats}async getLeaderboard(e=10){this.log("Fetching leaderboard",{limit:e});try{let t=await this.client.get(`/v1/customer/affiliate/leaderboard?limit=${e}`);if(t.success&&t.data?.entries)return this.log("Leaderboard fetched",{count:t.data.entries.length}),t.data;throw new Error(t.error??"Invalid response from leaderboard API")}catch(t){throw this.log("Error fetching leaderboard",t),t}}clearCache(){this.cachedStats=null,this.lastFetchTime=0,this.storage.remove(w),this.log("Affiliate stats cache cleared");}};var F="https://boostapi-production.up.railway.app",U=1e4,G=10,q="gamify_",T="user_id",E="anonymous_id",I="user_traits",$=["purchase","checkout_complete","checkout_success","commission.created","referral_success","user.leveled_up","step.completed","quest.completed"];function C(){return `anon_${Date.now()}_${Math.random().toString(36).substring(2,11)}`}var A=class{constructor(e){s(this,"config");s(this,"storage");s(this,"queue");s(this,"client");s(this,"_session");s(this,"_loyalty");s(this,"_referral");s(this,"_affiliate");s(this,"flushTimer",null);s(this,"anonymousId");s(this,"userId",null);s(this,"userTraits",null);s(this,"initialized",false);if(!e.apiKey)throw new Error("[Gamify] API key is required");if(e.apiKey.startsWith("sk_"))throw new Error("[Gamify] Secret keys (sk_live_*) cannot be used in the browser. Use a publishable key (pk_live_*) for client-side tracking. For server-side tracking, use @gamifyio/node instead.");this.config={apiKey:e.apiKey,endpoint:e.endpoint??F,debug:e.debug??false,flushInterval:e.flushInterval??U,maxBatchSize:e.maxBatchSize??G,storagePrefix:e.storagePrefix??q},this.storage=p(this.config.storagePrefix),this.queue=new l(this.storage),this.client=new d(this.config.endpoint,this.config.apiKey,this.config.debug),this._session=new f(this.config,this.client,this.storage),this._loyalty=new h(this.config,this.client,this.storage),this._referral=new u(this.config,this.storage),this._affiliate=new c(this.config,this.client,this.storage);let t=this.storage.get(E);this.anonymousId=t??C(),t||this.storage.set(E,this.anonymousId),this.userId=this.storage.get(T),this.userTraits=this.storage.get(I),this.initialize();}initialize(){this.initialized||(this.log("Initializing SDK"),this.startFlushTimer(),typeof window<"u"&&(window.addEventListener("beforeunload",()=>this.onBeforeUnload()),window.addEventListener("pagehide",()=>this.onBeforeUnload())),this.initialized=true,this.log("SDK initialized",{anonymousId:this.anonymousId,userId:this.userId}));}log(e,t){this.config.debug&&console.log(`[Gamify] ${e}`,t??"");}startFlushTimer(){this.flushTimer&&clearInterval(this.flushTimer),this.flushTimer=setInterval(()=>{this.flush();},this.config.flushInterval);}onBeforeUnload(){this.flushTimer&&(clearInterval(this.flushTimer),this.flushTimer=null);let e=this.queue.peek(this.config.maxBatchSize);if(e.length>0){let t=e.map(r=>r.event);this.client.sendBeacon(t);}}identify(e,t){if(!e||typeof e!="string"){this.log("Invalid user ID provided");return}this.log("Identifying user",{userId:e,traits:t}),this.userId=e,this.storage.set(T,e),t&&(this.userTraits={...this.userTraits,...t},this.storage.set(I,this.userTraits)),this.track("$identify",{userId:e,traits:t??{}});}track(e,t){if(!e||typeof e!="string"){this.log("Invalid event type provided");return}$.includes(e)&&console.warn(`[Gamify] Event "${e}" requires a secret key and will be rejected. Use @gamifyio/node on your server to track this event.`);let r=this._referral.getReferrerProperties(),n={type:e,properties:{...r,...t},timestamp:new Date().toISOString(),anonymousId:this.anonymousId,...this.userId&&{userId:this.userId}};this.log("Tracking event",n),this.queue.enqueue(n),this.queue.size()>=this.config.maxBatchSize&&this.flush();}async flush(){let e=this.queue.peek(this.config.maxBatchSize);if(e.length===0)return;let t=e.map(i=>i.event),r=e.map(i=>i.id);this.log(`Flushing ${t.length} events`),(await this.client.sendEvents(t)).success?this.queue.acknowledge(r):this.queue.nack(r);}reset(){this.log("Resetting SDK state"),this.userId=null,this.userTraits=null,this.storage.remove(T),this.storage.remove(I),this.anonymousId=C(),this.storage.set(E,this.anonymousId);}getUserId(){return this.userId}getAnonymousId(){return this.anonymousId}getPendingCount(){return this.queue.size()}shutdown(){this.log("Shutting down SDK"),this.flushTimer&&(clearInterval(this.flushTimer),this.flushTimer=null);let e=this.queue.peek(this.config.maxBatchSize);if(e.length>0){let t=e.map(r=>r.event);this.client.sendBeacon(t);}}get session(){return this._session}async updateCart(e,t,r){if(!this.userId)throw new Error("[Gamify] User must be identified before updating cart");return this._session.updateCart(this.userId,e,t,r)}get loyalty(){return this._loyalty}async getLoyaltyProfile(){if(!this.userId)throw new Error("[Gamify] User must be identified before getting loyalty profile");return this._loyalty.getProfile(this.userId)}async getLoyaltyHistory(e,t){if(!this.userId)throw new Error("[Gamify] User must be identified before getting loyalty history");return this._loyalty.getHistory(this.userId,e,t)}get referral(){return this._referral}getReferrer(){return this._referral.getReferrer()}setReferrer(e){this._referral.setReferrer(e);}get affiliate(){return this._affiliate}async getAffiliateStats(e=false){if(!this.userId)throw new Error("[Gamify] User must be identified before getting affiliate stats");return this._affiliate.getStats(this.userId,e)}async getLeaderboard(e=10){return this._affiliate.getLeaderboard(e)}async getQuests(){if(!this.userId)throw new Error("[Gamify] User must be identified before getting quests");let e=await this.client.get(`/v1/customer/quests?userId=${encodeURIComponent(this.userId)}`);if(e.success&&e.data)return e.data;throw new Error(e.error||"Failed to get quests")}async getStreaks(){if(!this.userId)throw new Error("[Gamify] User must be identified before getting streaks");let e=await this.client.get(`/v1/customer/streaks?userId=${encodeURIComponent(this.userId)}`);if(e.success&&e.data)return e.data;throw new Error(e.error||"Failed to get streaks")}async useStreakFreeze(e){if(!this.userId)throw new Error("[Gamify] User must be identified before using freeze");let t=await this.client.post(`/v1/customer/streaks/${encodeURIComponent(e)}/freeze`,{userId:this.userId});if(t.success&&t.data)return t.data;throw new Error(t.error||"Failed to use freeze")}async getBadges(e){if(!this.userId)throw new Error("[Gamify] User must be identified before getting badges");let t=`/v1/customer/badges?userId=${encodeURIComponent(this.userId)}`;e&&(t+=`&category=${encodeURIComponent(e)}`);let r=await this.client.get(t);if(r.success&&r.data)return r.data;throw new Error(r.error||"Failed to get badges")}async getBadgeCategories(){let e=await this.client.get("/v1/customer/badges/categories");if(e.success&&e.data)return e.data;throw new Error(e.error||"Failed to get badge categories")}async getRewardsStore(){if(!this.userId)throw new Error("[Gamify] User must be identified before getting rewards");let e=await this.client.get(`/v1/customer/store?userId=${encodeURIComponent(this.userId)}`);if(e.success&&e.data)return e.data;throw new Error(e.error||"Failed to get rewards store")}async redeemReward(e){if(!this.userId)throw new Error("[Gamify] User must be identified before redeeming rewards");let t=await this.client.post("/v1/customer/store/redeem",{userId:this.userId,itemId:e});if(t.success&&t.data)return t.data;throw new Error(t.error||"Failed to redeem reward")}};var B={background:"rgba(255, 255, 255, 0.05)",backgroundSecondary:"rgba(255, 255, 255, 0.1)",foreground:"#ffffff",foregroundSecondary:"rgba(255, 255, 255, 0.7)",border:"rgba(255, 255, 255, 0.1)",primary:"#8B5CF6",primaryForeground:"#ffffff",success:"#22C55E",warning:"#F59E0B",error:"#EF4444",rarityCommon:"#9CA3AF",rarityRare:"#3B82F6",rarityEpic:"#8B5CF6",rarityLegendary:"#F59E0B",medalGold:"#FFD700",medalSilver:"#C0C0C0",medalBronze:"#CD7F32"};
2
+ export{c as AffiliateManager,l as EventQueue,A as Gamify,d as HttpClient,h as LoyaltyManager,u as ReferralManager,f as SessionManager,p as createStorage,B as defaultTheme};//# sourceMappingURL=index.js.map
3
3
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/storage/storage.ts","../src/queue/event-queue.ts","../src/network/http-client.ts","../src/session/session-manager.ts","../src/loyalty/loyalty-manager.ts","../src/referral/referral-manager.ts","../src/affiliate/affiliate-manager.ts","../src/gamify.ts"],"names":["LocalStorageAdapter","prefix","key","item","value","keys","MemoryStorageAdapter","__publicField","keysToDelete","isLocalStorageAvailable","testKey","createStorage","QUEUE_STORAGE_KEY","generateId","EventQueue","storage","maxSize","persisted","event","queuedEvent","count","ids","idSet","HttpClient","endpoint","apiKey","debug","message","data","path","response","errorText","error","body","events","blob","SESSION_TOKEN_KEY","SESSION_DATA_KEY","SessionManager","config","client","userId","items","coupons","currency","request","code","LOYALTY_PROFILE_KEY","LoyaltyManager","limit","offset","params","REFERRER_KEY","REFERRER_DETECTED_AT_KEY","ReferralManager","refCode","AFFILIATE_STATS_CACHE_KEY","AffiliateManager","cached","forceRefresh","now","stats","DEFAULT_ENDPOINT","DEFAULT_FLUSH_INTERVAL","DEFAULT_MAX_BATCH_SIZE","DEFAULT_STORAGE_PREFIX","USER_ID_KEY","ANONYMOUS_ID_KEY","USER_TRAITS_KEY","TRUSTED_EVENTS","generateAnonymousId","Gamify","storedAnonymousId","pending","traits","eventType","properties","referrerProps","ruleId","category","url","itemId"],"mappings":"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","file":"index.js","sourcesContent":["import type { StorageAdapter } from '../types.js';\n\n/**\n * LocalStorage adapter with automatic JSON serialization\n */\nclass LocalStorageAdapter implements StorageAdapter {\n constructor(private readonly prefix: string) {}\n\n private getKey(key: string): string {\n return `${this.prefix}${key}`;\n }\n\n get<T>(key: string): T | null {\n try {\n const item = localStorage.getItem(this.getKey(key));\n if (item === null) return null;\n return JSON.parse(item) as T;\n } catch {\n return null;\n }\n }\n\n set<T>(key: string, value: T): void {\n try {\n localStorage.setItem(this.getKey(key), JSON.stringify(value));\n } catch {\n // Storage quota exceeded or unavailable - silently fail\n }\n }\n\n remove(key: string): void {\n try {\n localStorage.removeItem(this.getKey(key));\n } catch {\n // Ignore errors\n }\n }\n\n clear(): void {\n try {\n const keys = Object.keys(localStorage);\n for (const key of keys) {\n if (key.startsWith(this.prefix)) {\n localStorage.removeItem(key);\n }\n }\n } catch {\n // Ignore errors\n }\n }\n}\n\n/**\n * In-memory storage fallback for environments without localStorage\n */\nclass MemoryStorageAdapter implements StorageAdapter {\n private readonly store = new Map<string, string>();\n\n constructor(private readonly prefix: string) {}\n\n private getKey(key: string): string {\n return `${this.prefix}${key}`;\n }\n\n get<T>(key: string): T | null {\n const item = this.store.get(this.getKey(key));\n if (item === undefined) return null;\n try {\n return JSON.parse(item) as T;\n } catch {\n return null;\n }\n }\n\n set<T>(key: string, value: T): void {\n this.store.set(this.getKey(key), JSON.stringify(value));\n }\n\n remove(key: string): void {\n this.store.delete(this.getKey(key));\n }\n\n clear(): void {\n const keysToDelete: string[] = [];\n for (const key of this.store.keys()) {\n if (key.startsWith(this.prefix)) {\n keysToDelete.push(key);\n }\n }\n for (const key of keysToDelete) {\n this.store.delete(key);\n }\n }\n}\n\n/**\n * Check if localStorage is available\n */\nfunction isLocalStorageAvailable(): boolean {\n try {\n const testKey = '__gamify_test__';\n localStorage.setItem(testKey, 'test');\n localStorage.removeItem(testKey);\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Create appropriate storage adapter based on environment\n */\nexport function createStorage(prefix: string): StorageAdapter {\n if (typeof window !== 'undefined' && isLocalStorageAvailable()) {\n return new LocalStorageAdapter(prefix);\n }\n return new MemoryStorageAdapter(prefix);\n}\n","import type { QueuedEvent, GamifyEvent, StorageAdapter } from '../types.js';\n\nconst QUEUE_STORAGE_KEY = 'event_queue';\nconst MAX_RETRY_ATTEMPTS = 3;\n\n/**\n * Generates a unique ID for queued events\n */\nfunction generateId(): string {\n return `${Date.now()}-${Math.random().toString(36).substring(2, 11)}`;\n}\n\n/**\n * Event queue for reliable event delivery with offline support\n */\nexport class EventQueue {\n private queue: QueuedEvent[] = [];\n private readonly maxSize: number;\n private readonly storage: StorageAdapter;\n\n constructor(storage: StorageAdapter, maxSize: number = 100) {\n this.storage = storage;\n this.maxSize = maxSize;\n this.loadFromStorage();\n }\n\n /**\n * Load persisted queue from storage\n */\n private loadFromStorage(): void {\n const persisted = this.storage.get<QueuedEvent[]>(QUEUE_STORAGE_KEY);\n if (persisted && Array.isArray(persisted)) {\n this.queue = persisted;\n }\n }\n\n /**\n * Persist queue to storage\n */\n private saveToStorage(): void {\n this.storage.set(QUEUE_STORAGE_KEY, this.queue);\n }\n\n /**\n * Add an event to the queue\n */\n enqueue(event: GamifyEvent): string {\n const queuedEvent: QueuedEvent = {\n id: generateId(),\n event,\n attempts: 0,\n createdAt: Date.now(),\n };\n\n this.queue.push(queuedEvent);\n\n // Trim queue if it exceeds max size (FIFO - remove oldest)\n while (this.queue.length > this.maxSize) {\n this.queue.shift();\n }\n\n this.saveToStorage();\n return queuedEvent.id;\n }\n\n /**\n * Get events ready for sending (batch)\n */\n peek(count: number): QueuedEvent[] {\n return this.queue\n .filter((item) => item.attempts < MAX_RETRY_ATTEMPTS)\n .slice(0, count);\n }\n\n /**\n * Mark events as successfully sent and remove from queue\n */\n acknowledge(ids: string[]): void {\n const idSet = new Set(ids);\n this.queue = this.queue.filter((item) => !idSet.has(item.id));\n this.saveToStorage();\n }\n\n /**\n * Mark events as failed (increment retry count)\n */\n nack(ids: string[]): void {\n const idSet = new Set(ids);\n for (const item of this.queue) {\n if (idSet.has(item.id)) {\n item.attempts++;\n }\n }\n // Remove events that exceeded max retries\n this.queue = this.queue.filter((item) => item.attempts < MAX_RETRY_ATTEMPTS);\n this.saveToStorage();\n }\n\n /**\n * Get current queue size\n */\n size(): number {\n return this.queue.length;\n }\n\n /**\n * Check if queue has pending events\n */\n hasPending(): boolean {\n return this.queue.some((item) => item.attempts < MAX_RETRY_ATTEMPTS);\n }\n\n /**\n * Clear all events from queue\n */\n clear(): void {\n this.queue = [];\n this.saveToStorage();\n }\n}\n","import type { GamifyEvent, ApiResponse } from '../types.js';\n\ninterface HttpResponse<T> {\n success: boolean;\n data?: T;\n error?: string;\n}\n\n/**\n * HTTP client for sending events to the API\n * Uses fetch with keepalive for reliable delivery during page unload\n */\nexport class HttpClient {\n private readonly endpoint: string;\n private readonly apiKey: string;\n private readonly debug: boolean;\n\n constructor(endpoint: string, apiKey: string, debug: boolean = false) {\n this.endpoint = endpoint;\n this.apiKey = apiKey;\n this.debug = debug;\n }\n\n /**\n * Log debug messages\n */\n private log(message: string, data?: unknown): void {\n if (this.debug) {\n console.log(`[Gamify] ${message}`, data ?? '');\n }\n }\n\n /**\n * Generic GET request\n */\n async get<T>(path: string): Promise<HttpResponse<T>> {\n this.log(`GET ${path}`);\n\n try {\n const response = await fetch(`${this.endpoint}${path}`, {\n method: 'GET',\n headers: {\n 'Content-Type': 'application/json',\n 'X-API-Key': this.apiKey,\n },\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n this.log(`GET error: ${response.status}`, errorText);\n return { success: false, error: `HTTP ${response.status}: ${errorText}` };\n }\n\n const data = await response.json();\n return { success: true, data };\n } catch (error) {\n const message = error instanceof Error ? error.message : 'Unknown error';\n this.log('GET network error', message);\n return { success: false, error: message };\n }\n }\n\n /**\n * Generic POST request\n */\n async post<T>(path: string, body: unknown): Promise<HttpResponse<T>> {\n this.log(`POST ${path}`, body);\n\n try {\n const response = await fetch(`${this.endpoint}${path}`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'X-API-Key': this.apiKey,\n },\n body: JSON.stringify(body),\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n this.log(`POST error: ${response.status}`, errorText);\n return { success: false, error: `HTTP ${response.status}: ${errorText}` };\n }\n\n const data = await response.json();\n return { success: true, data };\n } catch (error) {\n const message = error instanceof Error ? error.message : 'Unknown error';\n this.log('POST network error', message);\n return { success: false, error: message };\n }\n }\n\n /**\n * Send a batch of events to the API\n */\n async sendEvents(events: GamifyEvent[]): Promise<ApiResponse> {\n if (events.length === 0) {\n return { success: true };\n }\n\n this.log(`Sending ${events.length} events`, events);\n\n try {\n const response = await fetch(`${this.endpoint}/events/batch`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'X-API-Key': this.apiKey,\n },\n body: JSON.stringify({ events }),\n keepalive: true,\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n this.log(`API error: ${response.status}`, errorText);\n return { success: false, error: `HTTP ${response.status}: ${errorText}` };\n }\n\n this.log('Events sent successfully');\n return { success: true };\n } catch (error) {\n const message = error instanceof Error ? error.message : 'Unknown error';\n this.log('Network error', message);\n return { success: false, error: message };\n }\n }\n\n /**\n * Send a single event using beacon API (for page unload)\n * Falls back to fetch if beacon is not available\n */\n sendBeacon(events: GamifyEvent[]): boolean {\n if (events.length === 0) {\n return true;\n }\n\n this.log(`Sending ${events.length} events via beacon`);\n\n if (typeof navigator !== 'undefined' && navigator.sendBeacon) {\n const blob = new Blob(\n [JSON.stringify({ events, apiKey: this.apiKey })],\n { type: 'application/json' }\n );\n return navigator.sendBeacon(`${this.endpoint}/events/batch`, blob);\n }\n\n // Fallback: fire-and-forget fetch with keepalive\n fetch(`${this.endpoint}/events/batch`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'X-API-Key': this.apiKey,\n },\n body: JSON.stringify({ events }),\n keepalive: true,\n }).catch(() => {\n // Ignore errors during unload\n });\n\n return true;\n }\n}\n","import type {\n GamifyConfig,\n SessionRequest,\n SessionResponse,\n CartItem,\n StorageAdapter,\n} from '../types.js';\nimport { HttpClient } from '../network/index.js';\n\nconst SESSION_TOKEN_KEY = 'session_token';\nconst SESSION_DATA_KEY = 'session_data';\n\n/**\n * SessionManager - Manages cart sessions with discount calculations\n */\nexport class SessionManager {\n private readonly client: HttpClient;\n private readonly storage: StorageAdapter;\n private readonly debug: boolean;\n private sessionToken: string | null = null;\n private cachedSession: SessionResponse | null = null;\n\n constructor(\n config: Required<GamifyConfig>,\n client: HttpClient,\n storage: StorageAdapter,\n ) {\n this.client = client;\n this.storage = storage;\n this.debug = config.debug;\n\n // Load persisted session token\n this.sessionToken = this.storage.get<string>(SESSION_TOKEN_KEY);\n this.cachedSession = this.storage.get<SessionResponse>(SESSION_DATA_KEY);\n }\n\n /**\n * Create or update a session with cart items\n */\n async updateCart(\n userId: string,\n items: CartItem[],\n coupons?: string[],\n currency?: string,\n ): Promise<SessionResponse> {\n const request: SessionRequest = {\n userId,\n items,\n coupons,\n currency,\n };\n\n this.log('Updating cart', request);\n\n const response = await this.client.post<SessionResponse>(\n '/v1/sessions',\n request,\n );\n\n if (response.success && response.data) {\n this.sessionToken = response.data.sessionToken;\n this.cachedSession = response.data;\n this.storage.set(SESSION_TOKEN_KEY, this.sessionToken);\n this.storage.set(SESSION_DATA_KEY, this.cachedSession);\n\n this.log('Cart updated', response.data);\n return response.data;\n }\n\n throw new Error(response.error || 'Failed to update cart');\n }\n\n /**\n * Get current session by token\n */\n async getSession(): Promise<SessionResponse | null> {\n if (!this.sessionToken) {\n return null;\n }\n\n this.log('Getting session', this.sessionToken);\n\n const response = await this.client.get<SessionResponse>(\n `/v1/sessions/${this.sessionToken}`,\n );\n\n if (response.success && response.data) {\n this.cachedSession = response.data;\n this.storage.set(SESSION_DATA_KEY, this.cachedSession);\n return response.data;\n }\n\n // Session not found, clear local state\n if (response.error?.includes('not found')) {\n this.clearSession();\n }\n\n return null;\n }\n\n /**\n * Apply a coupon to the current session\n */\n async applyCoupon(code: string): Promise<SessionResponse> {\n if (!this.sessionToken) {\n throw new Error('No active session');\n }\n\n this.log('Applying coupon', code);\n\n const response = await this.client.post<SessionResponse>(\n `/v1/sessions/${this.sessionToken}/coupons`,\n { code },\n );\n\n if (response.success && response.data) {\n this.cachedSession = response.data;\n this.storage.set(SESSION_DATA_KEY, this.cachedSession);\n\n this.log('Coupon applied', response.data);\n return response.data;\n }\n\n throw new Error(response.error || 'Failed to apply coupon');\n }\n\n /**\n * Complete the session (checkout)\n */\n async complete(): Promise<SessionResponse> {\n if (!this.sessionToken) {\n throw new Error('No active session');\n }\n\n this.log('Completing session');\n\n const response = await this.client.post<SessionResponse>(\n `/v1/sessions/${this.sessionToken}/complete`,\n {},\n );\n\n if (response.success && response.data) {\n this.cachedSession = response.data;\n this.storage.set(SESSION_DATA_KEY, this.cachedSession);\n\n this.log('Session completed', response.data);\n return response.data;\n }\n\n throw new Error(response.error || 'Failed to complete session');\n }\n\n /**\n * Clear the current session\n */\n clearSession(): void {\n this.sessionToken = null;\n this.cachedSession = null;\n this.storage.remove(SESSION_TOKEN_KEY);\n this.storage.remove(SESSION_DATA_KEY);\n\n this.log('Session cleared');\n }\n\n /**\n * Get cached session data\n */\n getCachedSession(): SessionResponse | null {\n return this.cachedSession;\n }\n\n /**\n * Get current session token\n */\n getSessionToken(): string | null {\n return this.sessionToken;\n }\n\n /**\n * Check if there's an active session\n */\n hasActiveSession(): boolean {\n return (\n this.sessionToken !== null &&\n this.cachedSession?.status === 'active'\n );\n }\n\n private log(message: string, data?: unknown): void {\n if (this.debug) {\n console.log(`[Gamify:Session] ${message}`, data ?? '');\n }\n }\n}\n","import type {\n GamifyConfig,\n LoyaltyProfile,\n LoyaltyHistoryResponse,\n StorageAdapter,\n} from '../types.js';\nimport { HttpClient } from '../network/index.js';\n\nconst LOYALTY_PROFILE_KEY = 'loyalty_profile';\n\n/**\n * LoyaltyManager - Manages customer loyalty points and tiers\n */\nexport class LoyaltyManager {\n private readonly client: HttpClient;\n private readonly storage: StorageAdapter;\n private readonly debug: boolean;\n private cachedProfile: LoyaltyProfile | null = null;\n\n constructor(\n config: Required<GamifyConfig>,\n client: HttpClient,\n storage: StorageAdapter,\n ) {\n this.client = client;\n this.storage = storage;\n this.debug = config.debug;\n\n // Load cached profile\n this.cachedProfile = this.storage.get<LoyaltyProfile>(LOYALTY_PROFILE_KEY);\n }\n\n /**\n * Get customer loyalty profile\n */\n async getProfile(userId: string): Promise<LoyaltyProfile> {\n this.log('Getting loyalty profile', userId);\n\n const response = await this.client.get<LoyaltyProfile>(\n `/v1/customer/profile?userId=${encodeURIComponent(userId)}`,\n );\n\n if (response.success && response.data) {\n this.cachedProfile = response.data;\n this.storage.set(LOYALTY_PROFILE_KEY, this.cachedProfile);\n\n this.log('Profile loaded', response.data);\n return response.data;\n }\n\n throw new Error(response.error || 'Failed to get loyalty profile');\n }\n\n /**\n * Get customer transaction history\n */\n async getHistory(\n userId: string,\n limit?: number,\n offset?: number,\n ): Promise<LoyaltyHistoryResponse> {\n this.log('Getting loyalty history', { userId, limit, offset });\n\n const params = new URLSearchParams();\n params.set('userId', userId);\n if (limit !== undefined) params.set('limit', String(limit));\n if (offset !== undefined) params.set('offset', String(offset));\n\n const response = await this.client.get<LoyaltyHistoryResponse>(\n `/v1/customer/history?${params.toString()}`,\n );\n\n if (response.success && response.data) {\n this.log('History loaded', response.data);\n return response.data;\n }\n\n throw new Error(response.error || 'Failed to get loyalty history');\n }\n\n /**\n * Get cached loyalty profile\n */\n getCachedProfile(): LoyaltyProfile | null {\n return this.cachedProfile;\n }\n\n /**\n * Get current points balance from cache\n */\n getPoints(): number {\n return this.cachedProfile?.points ?? 0;\n }\n\n /**\n * Get current tier from cache\n */\n getTier(): LoyaltyProfile['tier'] {\n return this.cachedProfile?.tier ?? null;\n }\n\n /**\n * Get next tier info from cache\n */\n getNextTier(): LoyaltyProfile['nextTier'] {\n return this.cachedProfile?.nextTier ?? null;\n }\n\n /**\n * Clear cached profile\n */\n clearCache(): void {\n this.cachedProfile = null;\n this.storage.remove(LOYALTY_PROFILE_KEY);\n this.log('Cache cleared');\n }\n\n /**\n * Refresh profile from server\n */\n async refresh(userId: string): Promise<LoyaltyProfile> {\n return this.getProfile(userId);\n }\n\n private log(message: string, data?: unknown): void {\n if (this.debug) {\n console.log(`[Gamify:Loyalty] ${message}`, data ?? '');\n }\n }\n}\n","import type { GamifyConfig, StorageAdapter } from '../types.js';\n\nconst REFERRER_KEY = '__gamify_referrer';\nconst REFERRER_DETECTED_AT_KEY = '__gamify_referrer_detected_at';\n\n/**\n * Referral Manager - Handles URL parameter detection and referrer storage\n *\n * Automatically detects `?ref=code` parameter from URLs and stores\n * the referrer code in localStorage for attribution tracking.\n */\nexport class ReferralManager {\n private readonly config: Required<GamifyConfig>;\n private readonly storage: StorageAdapter;\n private referrerCode: string | null = null;\n\n constructor(\n config: Required<GamifyConfig>,\n storage: StorageAdapter\n ) {\n this.config = config;\n this.storage = storage;\n\n // Load stored referrer\n this.referrerCode = this.storage.get<string>(REFERRER_KEY);\n\n // Auto-detect referrer from URL on initialization\n this.detectReferrerFromUrl();\n }\n\n /**\n * Log debug messages\n */\n private log(message: string, data?: unknown): void {\n if (this.config.debug) {\n console.log(`[Gamify:Referral] ${message}`, data ?? '');\n }\n }\n\n /**\n * Detect referrer code from current URL\n * Looks for `?ref=` parameter\n */\n detectReferrerFromUrl(): string | null {\n if (typeof window === 'undefined') {\n return null;\n }\n\n try {\n const urlParams = new URLSearchParams(window.location.search);\n const refCode = urlParams.get('ref');\n\n if (refCode && refCode.length > 0) {\n this.log('Detected referrer from URL', { refCode });\n this.setReferrer(refCode);\n return refCode;\n }\n } catch (error) {\n this.log('Error detecting referrer from URL', error);\n }\n\n return null;\n }\n\n /**\n * Manually set the referrer code\n */\n setReferrer(code: string): void {\n if (!code || code.length === 0) {\n this.log('Invalid referrer code provided');\n return;\n }\n\n this.referrerCode = code;\n this.storage.set(REFERRER_KEY, code);\n this.storage.set(REFERRER_DETECTED_AT_KEY, new Date().toISOString());\n\n this.log('Referrer code stored', { code });\n }\n\n /**\n * Get the current referrer code\n */\n getReferrer(): string | null {\n return this.referrerCode;\n }\n\n /**\n * Check if a referrer code is present\n */\n hasReferrer(): boolean {\n return this.referrerCode !== null;\n }\n\n /**\n * Clear the referrer code\n */\n clearReferrer(): void {\n this.referrerCode = null;\n this.storage.remove(REFERRER_KEY);\n this.storage.remove(REFERRER_DETECTED_AT_KEY);\n this.log('Referrer code cleared');\n }\n\n /**\n * Get referrer data to include in event properties\n */\n getReferrerProperties(): Record<string, unknown> {\n if (!this.referrerCode) {\n return {};\n }\n\n return {\n referrer: this.referrerCode,\n referrer_detected_at: this.storage.get<string>(REFERRER_DETECTED_AT_KEY),\n };\n }\n}\n","import type {\n GamifyConfig,\n StorageAdapter,\n AffiliateStats,\n AffiliateStatsResponse,\n LeaderboardResponse,\n} from '../types.js';\nimport type { HttpClient } from '../network/index.js';\n\nconst AFFILIATE_STATS_CACHE_KEY = 'affiliate_stats';\nconst CACHE_TTL_MS = 60000; // 1 minute\n\n/**\n * Affiliate Manager - Handles fetching affiliate stats and leaderboard\n */\nexport class AffiliateManager {\n private readonly config: Required<GamifyConfig>;\n private readonly client: HttpClient;\n private readonly storage: StorageAdapter;\n private cachedStats: AffiliateStats | null = null;\n private lastFetchTime: number = 0;\n\n constructor(\n config: Required<GamifyConfig>,\n client: HttpClient,\n storage: StorageAdapter\n ) {\n this.config = config;\n this.client = client;\n this.storage = storage;\n\n // Load cached stats\n const cached = this.storage.get<{ stats: AffiliateStats; fetchedAt: number }>(\n AFFILIATE_STATS_CACHE_KEY\n );\n if (cached) {\n this.cachedStats = cached.stats;\n this.lastFetchTime = cached.fetchedAt;\n }\n }\n\n /**\n * Log debug messages\n */\n private log(message: string, data?: unknown): void {\n if (this.config.debug) {\n console.log(`[Gamify:Affiliate] ${message}`, data ?? '');\n }\n }\n\n /**\n * Get affiliate stats for the current user\n * @param userId - User ID to fetch stats for\n * @param forceRefresh - Force refresh from API\n */\n async getStats(userId: string, forceRefresh = false): Promise<AffiliateStats> {\n // Check cache validity\n const now = Date.now();\n const cacheValid = !forceRefresh && this.cachedStats && now - this.lastFetchTime < CACHE_TTL_MS;\n\n if (cacheValid && this.cachedStats) {\n this.log('Returning cached affiliate stats');\n return this.cachedStats;\n }\n\n this.log('Fetching affiliate stats from API', { userId });\n\n try {\n const response = await this.client.get<AffiliateStatsResponse>(\n `/v1/customer/affiliate/profile?userId=${encodeURIComponent(userId)}`\n );\n\n if (response.success && response.data?.stats) {\n const stats = response.data.stats;\n\n this.cachedStats = stats;\n this.lastFetchTime = now;\n\n // Persist to storage\n this.storage.set(AFFILIATE_STATS_CACHE_KEY, {\n stats,\n fetchedAt: now,\n });\n\n this.log('Affiliate stats fetched', stats);\n return stats;\n }\n\n throw new Error(response.error ?? 'Invalid response from affiliate stats API');\n } catch (error) {\n this.log('Error fetching affiliate stats', error);\n throw error;\n }\n }\n\n /**\n * Get cached affiliate stats (if available)\n */\n getCachedStats(): AffiliateStats | null {\n return this.cachedStats;\n }\n\n /**\n * Get leaderboard data\n * @param limit - Number of entries to fetch (default: 10)\n */\n async getLeaderboard(limit = 10): Promise<LeaderboardResponse> {\n this.log('Fetching leaderboard', { limit });\n\n try {\n const response = await this.client.get<LeaderboardResponse>(\n `/v1/customer/affiliate/leaderboard?limit=${limit}`\n );\n\n if (response.success && response.data?.entries) {\n this.log('Leaderboard fetched', { count: response.data.entries.length });\n return response.data;\n }\n\n throw new Error(response.error ?? 'Invalid response from leaderboard API');\n } catch (error) {\n this.log('Error fetching leaderboard', error);\n throw error;\n }\n }\n\n /**\n * Clear cached affiliate stats\n */\n clearCache(): void {\n this.cachedStats = null;\n this.lastFetchTime = 0;\n this.storage.remove(AFFILIATE_STATS_CACHE_KEY);\n this.log('Affiliate stats cache cleared');\n }\n}\n","import type {\n GamifyConfig,\n GamifyEvent,\n UserTraits,\n StorageAdapter,\n CartItem,\n AffiliateStats,\n LeaderboardResponse,\n // Gamification types\n QuestsResponse,\n StreaksResponse,\n FreezeResponse,\n BadgesResponse,\n RewardsStoreResponse,\n RedemptionResult,\n} from './types.js';\nimport { createStorage } from './storage/index.js';\nimport { EventQueue } from './queue/index.js';\nimport { HttpClient } from './network/index.js';\nimport { SessionManager } from './session/index.js';\nimport { LoyaltyManager } from './loyalty/index.js';\nimport { ReferralManager } from './referral/index.js';\nimport { AffiliateManager } from './affiliate/index.js';\n\nconst DEFAULT_ENDPOINT = 'https://boostapi-production.up.railway.app';\nconst DEFAULT_FLUSH_INTERVAL = 10000; // 10 seconds\nconst DEFAULT_MAX_BATCH_SIZE = 10;\nconst DEFAULT_STORAGE_PREFIX = 'gamify_';\nconst USER_ID_KEY = 'user_id';\nconst ANONYMOUS_ID_KEY = 'anonymous_id';\nconst USER_TRAITS_KEY = 'user_traits';\n\n/**\n * Events that require a secret key (server-side only)\n * These will be rejected by the API when sent with a publishable key\n */\nconst TRUSTED_EVENTS = [\n 'purchase',\n 'checkout_complete',\n 'checkout_success',\n 'commission.created',\n 'referral_success',\n 'user.leveled_up',\n 'step.completed',\n 'quest.completed',\n] as const;\n\n/**\n * Generate anonymous ID for unidentified users\n */\nfunction generateAnonymousId(): string {\n return `anon_${Date.now()}_${Math.random().toString(36).substring(2, 11)}`;\n}\n\n/**\n * Gamify SDK - Framework-agnostic event tracking\n */\nexport class Gamify {\n private readonly config: Required<GamifyConfig>;\n private readonly storage: StorageAdapter;\n private readonly queue: EventQueue;\n private readonly client: HttpClient;\n private readonly _session: SessionManager;\n private readonly _loyalty: LoyaltyManager;\n private readonly _referral: ReferralManager;\n private readonly _affiliate: AffiliateManager;\n private flushTimer: ReturnType<typeof setInterval> | null = null;\n private anonymousId: string;\n private userId: string | null = null;\n private userTraits: UserTraits | null = null;\n private initialized = false;\n\n constructor(config: GamifyConfig) {\n if (!config.apiKey) {\n throw new Error('[Gamify] API key is required');\n }\n\n // Reject secret keys in client-side SDK\n if (config.apiKey.startsWith('sk_')) {\n throw new Error(\n '[Gamify] Secret keys (sk_live_*) cannot be used in the browser. ' +\n 'Use a publishable key (pk_live_*) for client-side tracking. ' +\n 'For server-side tracking, use @gamifyio/node instead.'\n );\n }\n\n this.config = {\n apiKey: config.apiKey,\n endpoint: config.endpoint ?? DEFAULT_ENDPOINT,\n debug: config.debug ?? false,\n flushInterval: config.flushInterval ?? DEFAULT_FLUSH_INTERVAL,\n maxBatchSize: config.maxBatchSize ?? DEFAULT_MAX_BATCH_SIZE,\n storagePrefix: config.storagePrefix ?? DEFAULT_STORAGE_PREFIX,\n };\n\n this.storage = createStorage(this.config.storagePrefix);\n this.queue = new EventQueue(this.storage);\n this.client = new HttpClient(\n this.config.endpoint,\n this.config.apiKey,\n this.config.debug\n );\n\n // Initialize Session and Loyalty managers\n this._session = new SessionManager(this.config, this.client, this.storage);\n this._loyalty = new LoyaltyManager(this.config, this.client, this.storage);\n\n // Issue #22: Initialize Referral and Affiliate managers\n this._referral = new ReferralManager(this.config, this.storage);\n this._affiliate = new AffiliateManager(this.config, this.client, this.storage);\n\n // Load or generate anonymous ID\n const storedAnonymousId = this.storage.get<string>(ANONYMOUS_ID_KEY);\n this.anonymousId = storedAnonymousId ?? generateAnonymousId();\n if (!storedAnonymousId) {\n this.storage.set(ANONYMOUS_ID_KEY, this.anonymousId);\n }\n\n // Load persisted user identity\n this.userId = this.storage.get<string>(USER_ID_KEY);\n this.userTraits = this.storage.get<UserTraits>(USER_TRAITS_KEY);\n\n this.initialize();\n }\n\n /**\n * Initialize the SDK\n */\n private initialize(): void {\n if (this.initialized) return;\n\n this.log('Initializing SDK');\n\n // Start flush timer\n this.startFlushTimer();\n\n // Handle page unload - flush remaining events\n if (typeof window !== 'undefined') {\n window.addEventListener('beforeunload', () => this.onBeforeUnload());\n window.addEventListener('pagehide', () => this.onBeforeUnload());\n }\n\n this.initialized = true;\n this.log('SDK initialized', {\n anonymousId: this.anonymousId,\n userId: this.userId,\n });\n }\n\n /**\n * Log debug messages\n */\n private log(message: string, data?: unknown): void {\n if (this.config.debug) {\n console.log(`[Gamify] ${message}`, data ?? '');\n }\n }\n\n /**\n * Start the automatic flush timer\n */\n private startFlushTimer(): void {\n if (this.flushTimer) {\n clearInterval(this.flushTimer);\n }\n this.flushTimer = setInterval(() => {\n void this.flush();\n }, this.config.flushInterval);\n }\n\n /**\n * Handle page unload - send remaining events via beacon\n */\n private onBeforeUnload(): void {\n if (this.flushTimer) {\n clearInterval(this.flushTimer);\n this.flushTimer = null;\n }\n\n const pending = this.queue.peek(this.config.maxBatchSize);\n if (pending.length > 0) {\n const events = pending.map((item) => item.event);\n this.client.sendBeacon(events);\n }\n }\n\n /**\n * Identify a user with optional traits\n * User ID persists across page reloads\n */\n identify(userId: string, traits?: UserTraits): void {\n if (!userId || typeof userId !== 'string') {\n this.log('Invalid user ID provided');\n return;\n }\n\n this.log('Identifying user', { userId, traits });\n\n this.userId = userId;\n this.storage.set(USER_ID_KEY, userId);\n\n if (traits) {\n this.userTraits = { ...this.userTraits, ...traits };\n this.storage.set(USER_TRAITS_KEY, this.userTraits);\n }\n\n // Track identify event\n this.track('$identify', {\n userId,\n traits: traits ?? {},\n });\n }\n\n /**\n * Track an event\n */\n track(eventType: string, properties?: Record<string, unknown>): void {\n if (!eventType || typeof eventType !== 'string') {\n this.log('Invalid event type provided');\n return;\n }\n\n // Warn about trusted events that require server-side tracking\n if ((TRUSTED_EVENTS as readonly string[]).includes(eventType)) {\n console.warn(\n `[Gamify] Event \"${eventType}\" requires a secret key and will be rejected. ` +\n `Use @gamifyio/node on your server to track this event.`\n );\n }\n\n // Issue #22: Inject referrer properties into event\n const referrerProps = this._referral.getReferrerProperties();\n\n const event: GamifyEvent = {\n type: eventType,\n properties: { ...referrerProps, ...properties },\n timestamp: new Date().toISOString(),\n anonymousId: this.anonymousId,\n ...(this.userId && { userId: this.userId }),\n };\n\n this.log('Tracking event', event);\n\n this.queue.enqueue(event);\n\n // Auto-flush if batch size reached\n if (this.queue.size() >= this.config.maxBatchSize) {\n void this.flush();\n }\n }\n\n /**\n * Flush pending events to the server\n */\n async flush(): Promise<void> {\n const pending = this.queue.peek(this.config.maxBatchSize);\n if (pending.length === 0) {\n return;\n }\n\n const events = pending.map((item) => item.event);\n const ids = pending.map((item) => item.id);\n\n this.log(`Flushing ${events.length} events`);\n\n const result = await this.client.sendEvents(events);\n\n if (result.success) {\n this.queue.acknowledge(ids);\n } else {\n this.queue.nack(ids);\n }\n }\n\n /**\n * Reset the SDK state (logout user)\n */\n reset(): void {\n this.log('Resetting SDK state');\n\n this.userId = null;\n this.userTraits = null;\n this.storage.remove(USER_ID_KEY);\n this.storage.remove(USER_TRAITS_KEY);\n\n // Generate new anonymous ID\n this.anonymousId = generateAnonymousId();\n this.storage.set(ANONYMOUS_ID_KEY, this.anonymousId);\n }\n\n /**\n * Get current user ID (null if not identified)\n */\n getUserId(): string | null {\n return this.userId;\n }\n\n /**\n * Get anonymous ID\n */\n getAnonymousId(): string {\n return this.anonymousId;\n }\n\n /**\n * Get pending event count\n */\n getPendingCount(): number {\n return this.queue.size();\n }\n\n /**\n * Shutdown the SDK gracefully\n */\n shutdown(): void {\n this.log('Shutting down SDK');\n\n if (this.flushTimer) {\n clearInterval(this.flushTimer);\n this.flushTimer = null;\n }\n\n // Attempt final flush via beacon\n const pending = this.queue.peek(this.config.maxBatchSize);\n if (pending.length > 0) {\n const events = pending.map((item) => item.event);\n this.client.sendBeacon(events);\n }\n }\n\n // ============================================\n // Session Module (Issue #14)\n // ============================================\n\n /**\n * Get the session manager\n */\n get session(): SessionManager {\n return this._session;\n }\n\n /**\n * Convenience method: Update cart and get discounts\n */\n async updateCart(\n items: CartItem[],\n coupons?: string[],\n currency?: string,\n ) {\n if (!this.userId) {\n throw new Error('[Gamify] User must be identified before updating cart');\n }\n return this._session.updateCart(this.userId, items, coupons, currency);\n }\n\n // ============================================\n // Loyalty Module (Issue #15)\n // ============================================\n\n /**\n * Get the loyalty manager\n */\n get loyalty(): LoyaltyManager {\n return this._loyalty;\n }\n\n /**\n * Convenience method: Get loyalty profile for current user\n */\n async getLoyaltyProfile() {\n if (!this.userId) {\n throw new Error('[Gamify] User must be identified before getting loyalty profile');\n }\n return this._loyalty.getProfile(this.userId);\n }\n\n /**\n * Convenience method: Get loyalty history for current user\n */\n async getLoyaltyHistory(limit?: number, offset?: number) {\n if (!this.userId) {\n throw new Error('[Gamify] User must be identified before getting loyalty history');\n }\n return this._loyalty.getHistory(this.userId, limit, offset);\n }\n\n // ============================================\n // Referral Module (Issue #22)\n // ============================================\n\n /**\n * Get the referral manager\n */\n get referral(): ReferralManager {\n return this._referral;\n }\n\n /**\n * Convenience method: Get current referrer code\n */\n getReferrer(): string | null {\n return this._referral.getReferrer();\n }\n\n /**\n * Convenience method: Set referrer code manually\n */\n setReferrer(code: string): void {\n this._referral.setReferrer(code);\n }\n\n // ============================================\n // Affiliate Module (Issue #22)\n // ============================================\n\n /**\n * Get the affiliate manager\n */\n get affiliate(): AffiliateManager {\n return this._affiliate;\n }\n\n /**\n * Convenience method: Get affiliate stats for current user\n */\n async getAffiliateStats(forceRefresh = false): Promise<AffiliateStats> {\n if (!this.userId) {\n throw new Error('[Gamify] User must be identified before getting affiliate stats');\n }\n return this._affiliate.getStats(this.userId, forceRefresh);\n }\n\n /**\n * Convenience method: Get leaderboard\n */\n async getLeaderboard(limit = 10): Promise<LeaderboardResponse> {\n return this._affiliate.getLeaderboard(limit);\n }\n\n // ============================================\n // Quests Module (Issue #25-28)\n // ============================================\n\n /**\n * Get user's quest progress\n */\n async getQuests(): Promise<QuestsResponse> {\n if (!this.userId) {\n throw new Error('[Gamify] User must be identified before getting quests');\n }\n const response = await this.client.get<QuestsResponse>(\n `/v1/customer/quests?userId=${encodeURIComponent(this.userId)}`\n );\n if (response.success && response.data) {\n return response.data;\n }\n throw new Error(response.error || 'Failed to get quests');\n }\n\n // ============================================\n // Streaks Module (Issue #32)\n // ============================================\n\n /**\n * Get user's streaks with progress\n */\n async getStreaks(): Promise<StreaksResponse> {\n if (!this.userId) {\n throw new Error('[Gamify] User must be identified before getting streaks');\n }\n const response = await this.client.get<StreaksResponse>(\n `/v1/customer/streaks?userId=${encodeURIComponent(this.userId)}`\n );\n if (response.success && response.data) {\n return response.data;\n }\n throw new Error(response.error || 'Failed to get streaks');\n }\n\n /**\n * Use a freeze token for a streak\n */\n async useStreakFreeze(ruleId: string): Promise<FreezeResponse> {\n if (!this.userId) {\n throw new Error('[Gamify] User must be identified before using freeze');\n }\n const response = await this.client.post<FreezeResponse>(\n `/v1/customer/streaks/${encodeURIComponent(ruleId)}/freeze`,\n { userId: this.userId }\n );\n if (response.success && response.data) {\n return response.data;\n }\n throw new Error(response.error || 'Failed to use freeze');\n }\n\n // ============================================\n // Badges Module (Issue #33)\n // ============================================\n\n /**\n * Get user's badge collection\n */\n async getBadges(category?: string): Promise<BadgesResponse> {\n if (!this.userId) {\n throw new Error('[Gamify] User must be identified before getting badges');\n }\n let url = `/v1/customer/badges?userId=${encodeURIComponent(this.userId)}`;\n if (category) {\n url += `&category=${encodeURIComponent(category)}`;\n }\n const response = await this.client.get<BadgesResponse>(url);\n if (response.success && response.data) {\n return response.data;\n }\n throw new Error(response.error || 'Failed to get badges');\n }\n\n /**\n * Get available badge categories\n */\n async getBadgeCategories(): Promise<string[]> {\n const response = await this.client.get<string[]>('/v1/customer/badges/categories');\n if (response.success && response.data) {\n return response.data;\n }\n throw new Error(response.error || 'Failed to get badge categories');\n }\n\n // ============================================\n // Rewards Module (Issue #34)\n // ============================================\n\n /**\n * Get rewards store items\n */\n async getRewardsStore(): Promise<RewardsStoreResponse> {\n if (!this.userId) {\n throw new Error('[Gamify] User must be identified before getting rewards');\n }\n const response = await this.client.get<RewardsStoreResponse>(\n `/v1/customer/store?userId=${encodeURIComponent(this.userId)}`\n );\n if (response.success && response.data) {\n return response.data;\n }\n throw new Error(response.error || 'Failed to get rewards store');\n }\n\n /**\n * Redeem a reward item\n */\n async redeemReward(itemId: string): Promise<RedemptionResult> {\n if (!this.userId) {\n throw new Error('[Gamify] User must be identified before redeeming rewards');\n }\n const response = await this.client.post<RedemptionResult>(\n '/v1/customer/store/redeem',\n { userId: this.userId, itemId }\n );\n if (response.success && response.data) {\n return response.data;\n }\n throw new Error(response.error || 'Failed to redeem reward');\n }\n}\n"]}
1
+ {"version":3,"sources":["../src/storage/storage.ts","../src/queue/event-queue.ts","../src/network/http-client.ts","../src/session/session-manager.ts","../src/loyalty/loyalty-manager.ts","../src/referral/referral-manager.ts","../src/affiliate/affiliate-manager.ts","../src/gamify.ts","../src/types.ts"],"names":["LocalStorageAdapter","prefix","key","item","value","keys","MemoryStorageAdapter","__publicField","keysToDelete","isLocalStorageAvailable","testKey","createStorage","QUEUE_STORAGE_KEY","generateId","EventQueue","storage","maxSize","persisted","event","queuedEvent","count","ids","idSet","HttpClient","endpoint","apiKey","debug","message","data","path","response","errorText","error","body","events","blob","SESSION_TOKEN_KEY","SESSION_DATA_KEY","SessionManager","config","client","userId","items","coupons","currency","request","code","LOYALTY_PROFILE_KEY","LoyaltyManager","limit","offset","params","REFERRER_KEY","REFERRER_DETECTED_AT_KEY","ReferralManager","refCode","AFFILIATE_STATS_CACHE_KEY","AffiliateManager","cached","forceRefresh","now","stats","DEFAULT_ENDPOINT","DEFAULT_FLUSH_INTERVAL","DEFAULT_MAX_BATCH_SIZE","DEFAULT_STORAGE_PREFIX","USER_ID_KEY","ANONYMOUS_ID_KEY","USER_TRAITS_KEY","TRUSTED_EVENTS","generateAnonymousId","Gamify","storedAnonymousId","pending","traits","eventType","properties","referrerProps","ruleId","category","url","itemId","defaultTheme"],"mappings":"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","file":"index.js","sourcesContent":["import type { StorageAdapter } from '../types.js';\n\n/**\n * LocalStorage adapter with automatic JSON serialization\n */\nclass LocalStorageAdapter implements StorageAdapter {\n constructor(private readonly prefix: string) {}\n\n private getKey(key: string): string {\n return `${this.prefix}${key}`;\n }\n\n get<T>(key: string): T | null {\n try {\n const item = localStorage.getItem(this.getKey(key));\n if (item === null) return null;\n return JSON.parse(item) as T;\n } catch {\n return null;\n }\n }\n\n set<T>(key: string, value: T): void {\n try {\n localStorage.setItem(this.getKey(key), JSON.stringify(value));\n } catch {\n // Storage quota exceeded or unavailable - silently fail\n }\n }\n\n remove(key: string): void {\n try {\n localStorage.removeItem(this.getKey(key));\n } catch {\n // Ignore errors\n }\n }\n\n clear(): void {\n try {\n const keys = Object.keys(localStorage);\n for (const key of keys) {\n if (key.startsWith(this.prefix)) {\n localStorage.removeItem(key);\n }\n }\n } catch {\n // Ignore errors\n }\n }\n}\n\n/**\n * In-memory storage fallback for environments without localStorage\n */\nclass MemoryStorageAdapter implements StorageAdapter {\n private readonly store = new Map<string, string>();\n\n constructor(private readonly prefix: string) {}\n\n private getKey(key: string): string {\n return `${this.prefix}${key}`;\n }\n\n get<T>(key: string): T | null {\n const item = this.store.get(this.getKey(key));\n if (item === undefined) return null;\n try {\n return JSON.parse(item) as T;\n } catch {\n return null;\n }\n }\n\n set<T>(key: string, value: T): void {\n this.store.set(this.getKey(key), JSON.stringify(value));\n }\n\n remove(key: string): void {\n this.store.delete(this.getKey(key));\n }\n\n clear(): void {\n const keysToDelete: string[] = [];\n for (const key of this.store.keys()) {\n if (key.startsWith(this.prefix)) {\n keysToDelete.push(key);\n }\n }\n for (const key of keysToDelete) {\n this.store.delete(key);\n }\n }\n}\n\n/**\n * Check if localStorage is available\n */\nfunction isLocalStorageAvailable(): boolean {\n try {\n const testKey = '__gamify_test__';\n localStorage.setItem(testKey, 'test');\n localStorage.removeItem(testKey);\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Create appropriate storage adapter based on environment\n */\nexport function createStorage(prefix: string): StorageAdapter {\n if (typeof window !== 'undefined' && isLocalStorageAvailable()) {\n return new LocalStorageAdapter(prefix);\n }\n return new MemoryStorageAdapter(prefix);\n}\n","import type { QueuedEvent, GamifyEvent, StorageAdapter } from '../types.js';\n\nconst QUEUE_STORAGE_KEY = 'event_queue';\nconst MAX_RETRY_ATTEMPTS = 3;\n\n/**\n * Generates a unique ID for queued events\n */\nfunction generateId(): string {\n return `${Date.now()}-${Math.random().toString(36).substring(2, 11)}`;\n}\n\n/**\n * Event queue for reliable event delivery with offline support\n */\nexport class EventQueue {\n private queue: QueuedEvent[] = [];\n private readonly maxSize: number;\n private readonly storage: StorageAdapter;\n\n constructor(storage: StorageAdapter, maxSize: number = 100) {\n this.storage = storage;\n this.maxSize = maxSize;\n this.loadFromStorage();\n }\n\n /**\n * Load persisted queue from storage\n */\n private loadFromStorage(): void {\n const persisted = this.storage.get<QueuedEvent[]>(QUEUE_STORAGE_KEY);\n if (persisted && Array.isArray(persisted)) {\n this.queue = persisted;\n }\n }\n\n /**\n * Persist queue to storage\n */\n private saveToStorage(): void {\n this.storage.set(QUEUE_STORAGE_KEY, this.queue);\n }\n\n /**\n * Add an event to the queue\n */\n enqueue(event: GamifyEvent): string {\n const queuedEvent: QueuedEvent = {\n id: generateId(),\n event,\n attempts: 0,\n createdAt: Date.now(),\n };\n\n this.queue.push(queuedEvent);\n\n // Trim queue if it exceeds max size (FIFO - remove oldest)\n while (this.queue.length > this.maxSize) {\n this.queue.shift();\n }\n\n this.saveToStorage();\n return queuedEvent.id;\n }\n\n /**\n * Get events ready for sending (batch)\n */\n peek(count: number): QueuedEvent[] {\n return this.queue\n .filter((item) => item.attempts < MAX_RETRY_ATTEMPTS)\n .slice(0, count);\n }\n\n /**\n * Mark events as successfully sent and remove from queue\n */\n acknowledge(ids: string[]): void {\n const idSet = new Set(ids);\n this.queue = this.queue.filter((item) => !idSet.has(item.id));\n this.saveToStorage();\n }\n\n /**\n * Mark events as failed (increment retry count)\n */\n nack(ids: string[]): void {\n const idSet = new Set(ids);\n for (const item of this.queue) {\n if (idSet.has(item.id)) {\n item.attempts++;\n }\n }\n // Remove events that exceeded max retries\n this.queue = this.queue.filter((item) => item.attempts < MAX_RETRY_ATTEMPTS);\n this.saveToStorage();\n }\n\n /**\n * Get current queue size\n */\n size(): number {\n return this.queue.length;\n }\n\n /**\n * Check if queue has pending events\n */\n hasPending(): boolean {\n return this.queue.some((item) => item.attempts < MAX_RETRY_ATTEMPTS);\n }\n\n /**\n * Clear all events from queue\n */\n clear(): void {\n this.queue = [];\n this.saveToStorage();\n }\n}\n","import type { GamifyEvent, ApiResponse } from '../types.js';\n\ninterface HttpResponse<T> {\n success: boolean;\n data?: T;\n error?: string;\n}\n\n/**\n * HTTP client for sending events to the API\n * Uses fetch with keepalive for reliable delivery during page unload\n */\nexport class HttpClient {\n private readonly endpoint: string;\n private readonly apiKey: string;\n private readonly debug: boolean;\n\n constructor(endpoint: string, apiKey: string, debug: boolean = false) {\n this.endpoint = endpoint;\n this.apiKey = apiKey;\n this.debug = debug;\n }\n\n /**\n * Log debug messages\n */\n private log(message: string, data?: unknown): void {\n if (this.debug) {\n console.log(`[Gamify] ${message}`, data ?? '');\n }\n }\n\n /**\n * Generic GET request\n */\n async get<T>(path: string): Promise<HttpResponse<T>> {\n this.log(`GET ${path}`);\n\n try {\n const response = await fetch(`${this.endpoint}${path}`, {\n method: 'GET',\n headers: {\n 'Content-Type': 'application/json',\n 'X-API-Key': this.apiKey,\n },\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n this.log(`GET error: ${response.status}`, errorText);\n return { success: false, error: `HTTP ${response.status}: ${errorText}` };\n }\n\n const data = await response.json();\n return { success: true, data };\n } catch (error) {\n const message = error instanceof Error ? error.message : 'Unknown error';\n this.log('GET network error', message);\n return { success: false, error: message };\n }\n }\n\n /**\n * Generic POST request\n */\n async post<T>(path: string, body: unknown): Promise<HttpResponse<T>> {\n this.log(`POST ${path}`, body);\n\n try {\n const response = await fetch(`${this.endpoint}${path}`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'X-API-Key': this.apiKey,\n },\n body: JSON.stringify(body),\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n this.log(`POST error: ${response.status}`, errorText);\n return { success: false, error: `HTTP ${response.status}: ${errorText}` };\n }\n\n const data = await response.json();\n return { success: true, data };\n } catch (error) {\n const message = error instanceof Error ? error.message : 'Unknown error';\n this.log('POST network error', message);\n return { success: false, error: message };\n }\n }\n\n /**\n * Send a batch of events to the API\n */\n async sendEvents(events: GamifyEvent[]): Promise<ApiResponse> {\n if (events.length === 0) {\n return { success: true };\n }\n\n this.log(`Sending ${events.length} events`, events);\n\n try {\n const response = await fetch(`${this.endpoint}/events/batch`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'X-API-Key': this.apiKey,\n },\n body: JSON.stringify({ events }),\n keepalive: true,\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n this.log(`API error: ${response.status}`, errorText);\n return { success: false, error: `HTTP ${response.status}: ${errorText}` };\n }\n\n this.log('Events sent successfully');\n return { success: true };\n } catch (error) {\n const message = error instanceof Error ? error.message : 'Unknown error';\n this.log('Network error', message);\n return { success: false, error: message };\n }\n }\n\n /**\n * Send a single event using beacon API (for page unload)\n * Falls back to fetch if beacon is not available\n */\n sendBeacon(events: GamifyEvent[]): boolean {\n if (events.length === 0) {\n return true;\n }\n\n this.log(`Sending ${events.length} events via beacon`);\n\n if (typeof navigator !== 'undefined' && navigator.sendBeacon) {\n const blob = new Blob(\n [JSON.stringify({ events, apiKey: this.apiKey })],\n { type: 'application/json' }\n );\n return navigator.sendBeacon(`${this.endpoint}/events/batch`, blob);\n }\n\n // Fallback: fire-and-forget fetch with keepalive\n fetch(`${this.endpoint}/events/batch`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'X-API-Key': this.apiKey,\n },\n body: JSON.stringify({ events }),\n keepalive: true,\n }).catch(() => {\n // Ignore errors during unload\n });\n\n return true;\n }\n}\n","import type {\n GamifyConfig,\n SessionRequest,\n SessionResponse,\n CartItem,\n StorageAdapter,\n} from '../types.js';\nimport { HttpClient } from '../network/index.js';\n\nconst SESSION_TOKEN_KEY = 'session_token';\nconst SESSION_DATA_KEY = 'session_data';\n\n/**\n * SessionManager - Manages cart sessions with discount calculations\n */\nexport class SessionManager {\n private readonly client: HttpClient;\n private readonly storage: StorageAdapter;\n private readonly debug: boolean;\n private sessionToken: string | null = null;\n private cachedSession: SessionResponse | null = null;\n\n constructor(\n config: Required<GamifyConfig>,\n client: HttpClient,\n storage: StorageAdapter,\n ) {\n this.client = client;\n this.storage = storage;\n this.debug = config.debug;\n\n // Load persisted session token\n this.sessionToken = this.storage.get<string>(SESSION_TOKEN_KEY);\n this.cachedSession = this.storage.get<SessionResponse>(SESSION_DATA_KEY);\n }\n\n /**\n * Create or update a session with cart items\n */\n async updateCart(\n userId: string,\n items: CartItem[],\n coupons?: string[],\n currency?: string,\n ): Promise<SessionResponse> {\n const request: SessionRequest = {\n userId,\n items,\n coupons,\n currency,\n };\n\n this.log('Updating cart', request);\n\n const response = await this.client.post<SessionResponse>(\n '/v1/sessions',\n request,\n );\n\n if (response.success && response.data) {\n this.sessionToken = response.data.sessionToken;\n this.cachedSession = response.data;\n this.storage.set(SESSION_TOKEN_KEY, this.sessionToken);\n this.storage.set(SESSION_DATA_KEY, this.cachedSession);\n\n this.log('Cart updated', response.data);\n return response.data;\n }\n\n throw new Error(response.error || 'Failed to update cart');\n }\n\n /**\n * Get current session by token\n */\n async getSession(): Promise<SessionResponse | null> {\n if (!this.sessionToken) {\n return null;\n }\n\n this.log('Getting session', this.sessionToken);\n\n const response = await this.client.get<SessionResponse>(\n `/v1/sessions/${this.sessionToken}`,\n );\n\n if (response.success && response.data) {\n this.cachedSession = response.data;\n this.storage.set(SESSION_DATA_KEY, this.cachedSession);\n return response.data;\n }\n\n // Session not found, clear local state\n if (response.error?.includes('not found')) {\n this.clearSession();\n }\n\n return null;\n }\n\n /**\n * Apply a coupon to the current session\n */\n async applyCoupon(code: string): Promise<SessionResponse> {\n if (!this.sessionToken) {\n throw new Error('No active session');\n }\n\n this.log('Applying coupon', code);\n\n const response = await this.client.post<SessionResponse>(\n `/v1/sessions/${this.sessionToken}/coupons`,\n { code },\n );\n\n if (response.success && response.data) {\n this.cachedSession = response.data;\n this.storage.set(SESSION_DATA_KEY, this.cachedSession);\n\n this.log('Coupon applied', response.data);\n return response.data;\n }\n\n throw new Error(response.error || 'Failed to apply coupon');\n }\n\n /**\n * Complete the session (checkout)\n */\n async complete(): Promise<SessionResponse> {\n if (!this.sessionToken) {\n throw new Error('No active session');\n }\n\n this.log('Completing session');\n\n const response = await this.client.post<SessionResponse>(\n `/v1/sessions/${this.sessionToken}/complete`,\n {},\n );\n\n if (response.success && response.data) {\n this.cachedSession = response.data;\n this.storage.set(SESSION_DATA_KEY, this.cachedSession);\n\n this.log('Session completed', response.data);\n return response.data;\n }\n\n throw new Error(response.error || 'Failed to complete session');\n }\n\n /**\n * Clear the current session\n */\n clearSession(): void {\n this.sessionToken = null;\n this.cachedSession = null;\n this.storage.remove(SESSION_TOKEN_KEY);\n this.storage.remove(SESSION_DATA_KEY);\n\n this.log('Session cleared');\n }\n\n /**\n * Get cached session data\n */\n getCachedSession(): SessionResponse | null {\n return this.cachedSession;\n }\n\n /**\n * Get current session token\n */\n getSessionToken(): string | null {\n return this.sessionToken;\n }\n\n /**\n * Check if there's an active session\n */\n hasActiveSession(): boolean {\n return (\n this.sessionToken !== null &&\n this.cachedSession?.status === 'active'\n );\n }\n\n private log(message: string, data?: unknown): void {\n if (this.debug) {\n console.log(`[Gamify:Session] ${message}`, data ?? '');\n }\n }\n}\n","import type {\n GamifyConfig,\n LoyaltyProfile,\n LoyaltyHistoryResponse,\n StorageAdapter,\n} from '../types.js';\nimport { HttpClient } from '../network/index.js';\n\nconst LOYALTY_PROFILE_KEY = 'loyalty_profile';\n\n/**\n * LoyaltyManager - Manages customer loyalty points and tiers\n */\nexport class LoyaltyManager {\n private readonly client: HttpClient;\n private readonly storage: StorageAdapter;\n private readonly debug: boolean;\n private cachedProfile: LoyaltyProfile | null = null;\n\n constructor(\n config: Required<GamifyConfig>,\n client: HttpClient,\n storage: StorageAdapter,\n ) {\n this.client = client;\n this.storage = storage;\n this.debug = config.debug;\n\n // Load cached profile\n this.cachedProfile = this.storage.get<LoyaltyProfile>(LOYALTY_PROFILE_KEY);\n }\n\n /**\n * Get customer loyalty profile\n */\n async getProfile(userId: string): Promise<LoyaltyProfile> {\n this.log('Getting loyalty profile', userId);\n\n const response = await this.client.get<LoyaltyProfile>(\n `/v1/customer/profile?userId=${encodeURIComponent(userId)}`,\n );\n\n if (response.success && response.data) {\n this.cachedProfile = response.data;\n this.storage.set(LOYALTY_PROFILE_KEY, this.cachedProfile);\n\n this.log('Profile loaded', response.data);\n return response.data;\n }\n\n throw new Error(response.error || 'Failed to get loyalty profile');\n }\n\n /**\n * Get customer transaction history\n */\n async getHistory(\n userId: string,\n limit?: number,\n offset?: number,\n ): Promise<LoyaltyHistoryResponse> {\n this.log('Getting loyalty history', { userId, limit, offset });\n\n const params = new URLSearchParams();\n params.set('userId', userId);\n if (limit !== undefined) params.set('limit', String(limit));\n if (offset !== undefined) params.set('offset', String(offset));\n\n const response = await this.client.get<LoyaltyHistoryResponse>(\n `/v1/customer/history?${params.toString()}`,\n );\n\n if (response.success && response.data) {\n this.log('History loaded', response.data);\n return response.data;\n }\n\n throw new Error(response.error || 'Failed to get loyalty history');\n }\n\n /**\n * Get cached loyalty profile\n */\n getCachedProfile(): LoyaltyProfile | null {\n return this.cachedProfile;\n }\n\n /**\n * Get current points balance from cache\n */\n getPoints(): number {\n return this.cachedProfile?.points ?? 0;\n }\n\n /**\n * Get current tier from cache\n */\n getTier(): LoyaltyProfile['tier'] {\n return this.cachedProfile?.tier ?? null;\n }\n\n /**\n * Get next tier info from cache\n */\n getNextTier(): LoyaltyProfile['nextTier'] {\n return this.cachedProfile?.nextTier ?? null;\n }\n\n /**\n * Clear cached profile\n */\n clearCache(): void {\n this.cachedProfile = null;\n this.storage.remove(LOYALTY_PROFILE_KEY);\n this.log('Cache cleared');\n }\n\n /**\n * Refresh profile from server\n */\n async refresh(userId: string): Promise<LoyaltyProfile> {\n return this.getProfile(userId);\n }\n\n private log(message: string, data?: unknown): void {\n if (this.debug) {\n console.log(`[Gamify:Loyalty] ${message}`, data ?? '');\n }\n }\n}\n","import type { GamifyConfig, StorageAdapter } from '../types.js';\n\nconst REFERRER_KEY = '__gamify_referrer';\nconst REFERRER_DETECTED_AT_KEY = '__gamify_referrer_detected_at';\n\n/**\n * Referral Manager - Handles URL parameter detection and referrer storage\n *\n * Automatically detects `?ref=code` parameter from URLs and stores\n * the referrer code in localStorage for attribution tracking.\n */\nexport class ReferralManager {\n private readonly config: Required<GamifyConfig>;\n private readonly storage: StorageAdapter;\n private referrerCode: string | null = null;\n\n constructor(\n config: Required<GamifyConfig>,\n storage: StorageAdapter\n ) {\n this.config = config;\n this.storage = storage;\n\n // Load stored referrer\n this.referrerCode = this.storage.get<string>(REFERRER_KEY);\n\n // Auto-detect referrer from URL on initialization\n this.detectReferrerFromUrl();\n }\n\n /**\n * Log debug messages\n */\n private log(message: string, data?: unknown): void {\n if (this.config.debug) {\n console.log(`[Gamify:Referral] ${message}`, data ?? '');\n }\n }\n\n /**\n * Detect referrer code from current URL\n * Looks for `?ref=` parameter\n */\n detectReferrerFromUrl(): string | null {\n if (typeof window === 'undefined') {\n return null;\n }\n\n try {\n const urlParams = new URLSearchParams(window.location.search);\n const refCode = urlParams.get('ref');\n\n if (refCode && refCode.length > 0) {\n this.log('Detected referrer from URL', { refCode });\n this.setReferrer(refCode);\n return refCode;\n }\n } catch (error) {\n this.log('Error detecting referrer from URL', error);\n }\n\n return null;\n }\n\n /**\n * Manually set the referrer code\n */\n setReferrer(code: string): void {\n if (!code || code.length === 0) {\n this.log('Invalid referrer code provided');\n return;\n }\n\n this.referrerCode = code;\n this.storage.set(REFERRER_KEY, code);\n this.storage.set(REFERRER_DETECTED_AT_KEY, new Date().toISOString());\n\n this.log('Referrer code stored', { code });\n }\n\n /**\n * Get the current referrer code\n */\n getReferrer(): string | null {\n return this.referrerCode;\n }\n\n /**\n * Check if a referrer code is present\n */\n hasReferrer(): boolean {\n return this.referrerCode !== null;\n }\n\n /**\n * Clear the referrer code\n */\n clearReferrer(): void {\n this.referrerCode = null;\n this.storage.remove(REFERRER_KEY);\n this.storage.remove(REFERRER_DETECTED_AT_KEY);\n this.log('Referrer code cleared');\n }\n\n /**\n * Get referrer data to include in event properties\n */\n getReferrerProperties(): Record<string, unknown> {\n if (!this.referrerCode) {\n return {};\n }\n\n return {\n referrer: this.referrerCode,\n referrer_detected_at: this.storage.get<string>(REFERRER_DETECTED_AT_KEY),\n };\n }\n}\n","import type {\n GamifyConfig,\n StorageAdapter,\n AffiliateStats,\n AffiliateStatsResponse,\n LeaderboardResponse,\n} from '../types.js';\nimport type { HttpClient } from '../network/index.js';\n\nconst AFFILIATE_STATS_CACHE_KEY = 'affiliate_stats';\nconst CACHE_TTL_MS = 60000; // 1 minute\n\n/**\n * Affiliate Manager - Handles fetching affiliate stats and leaderboard\n */\nexport class AffiliateManager {\n private readonly config: Required<GamifyConfig>;\n private readonly client: HttpClient;\n private readonly storage: StorageAdapter;\n private cachedStats: AffiliateStats | null = null;\n private lastFetchTime: number = 0;\n\n constructor(\n config: Required<GamifyConfig>,\n client: HttpClient,\n storage: StorageAdapter\n ) {\n this.config = config;\n this.client = client;\n this.storage = storage;\n\n // Load cached stats\n const cached = this.storage.get<{ stats: AffiliateStats; fetchedAt: number }>(\n AFFILIATE_STATS_CACHE_KEY\n );\n if (cached) {\n this.cachedStats = cached.stats;\n this.lastFetchTime = cached.fetchedAt;\n }\n }\n\n /**\n * Log debug messages\n */\n private log(message: string, data?: unknown): void {\n if (this.config.debug) {\n console.log(`[Gamify:Affiliate] ${message}`, data ?? '');\n }\n }\n\n /**\n * Get affiliate stats for the current user\n * @param userId - User ID to fetch stats for\n * @param forceRefresh - Force refresh from API\n */\n async getStats(userId: string, forceRefresh = false): Promise<AffiliateStats> {\n // Check cache validity\n const now = Date.now();\n const cacheValid = !forceRefresh && this.cachedStats && now - this.lastFetchTime < CACHE_TTL_MS;\n\n if (cacheValid && this.cachedStats) {\n this.log('Returning cached affiliate stats');\n return this.cachedStats;\n }\n\n this.log('Fetching affiliate stats from API', { userId });\n\n try {\n const response = await this.client.get<AffiliateStatsResponse>(\n `/v1/customer/affiliate/profile?userId=${encodeURIComponent(userId)}`\n );\n\n if (response.success && response.data?.stats) {\n const stats = response.data.stats;\n\n this.cachedStats = stats;\n this.lastFetchTime = now;\n\n // Persist to storage\n this.storage.set(AFFILIATE_STATS_CACHE_KEY, {\n stats,\n fetchedAt: now,\n });\n\n this.log('Affiliate stats fetched', stats);\n return stats;\n }\n\n throw new Error(response.error ?? 'Invalid response from affiliate stats API');\n } catch (error) {\n this.log('Error fetching affiliate stats', error);\n throw error;\n }\n }\n\n /**\n * Get cached affiliate stats (if available)\n */\n getCachedStats(): AffiliateStats | null {\n return this.cachedStats;\n }\n\n /**\n * Get leaderboard data\n * @param limit - Number of entries to fetch (default: 10)\n */\n async getLeaderboard(limit = 10): Promise<LeaderboardResponse> {\n this.log('Fetching leaderboard', { limit });\n\n try {\n const response = await this.client.get<LeaderboardResponse>(\n `/v1/customer/affiliate/leaderboard?limit=${limit}`\n );\n\n if (response.success && response.data?.entries) {\n this.log('Leaderboard fetched', { count: response.data.entries.length });\n return response.data;\n }\n\n throw new Error(response.error ?? 'Invalid response from leaderboard API');\n } catch (error) {\n this.log('Error fetching leaderboard', error);\n throw error;\n }\n }\n\n /**\n * Clear cached affiliate stats\n */\n clearCache(): void {\n this.cachedStats = null;\n this.lastFetchTime = 0;\n this.storage.remove(AFFILIATE_STATS_CACHE_KEY);\n this.log('Affiliate stats cache cleared');\n }\n}\n","import type {\n GamifyConfig,\n GamifyEvent,\n UserTraits,\n StorageAdapter,\n CartItem,\n AffiliateStats,\n LeaderboardResponse,\n // Gamification types\n QuestsResponse,\n StreaksResponse,\n FreezeResponse,\n BadgesResponse,\n RewardsStoreResponse,\n RedemptionResult,\n} from './types.js';\nimport { createStorage } from './storage/index.js';\nimport { EventQueue } from './queue/index.js';\nimport { HttpClient } from './network/index.js';\nimport { SessionManager } from './session/index.js';\nimport { LoyaltyManager } from './loyalty/index.js';\nimport { ReferralManager } from './referral/index.js';\nimport { AffiliateManager } from './affiliate/index.js';\n\nconst DEFAULT_ENDPOINT = 'https://boostapi-production.up.railway.app';\nconst DEFAULT_FLUSH_INTERVAL = 10000; // 10 seconds\nconst DEFAULT_MAX_BATCH_SIZE = 10;\nconst DEFAULT_STORAGE_PREFIX = 'gamify_';\nconst USER_ID_KEY = 'user_id';\nconst ANONYMOUS_ID_KEY = 'anonymous_id';\nconst USER_TRAITS_KEY = 'user_traits';\n\n/**\n * Events that require a secret key (server-side only)\n * These will be rejected by the API when sent with a publishable key\n */\nconst TRUSTED_EVENTS = [\n 'purchase',\n 'checkout_complete',\n 'checkout_success',\n 'commission.created',\n 'referral_success',\n 'user.leveled_up',\n 'step.completed',\n 'quest.completed',\n] as const;\n\n/**\n * Generate anonymous ID for unidentified users\n */\nfunction generateAnonymousId(): string {\n return `anon_${Date.now()}_${Math.random().toString(36).substring(2, 11)}`;\n}\n\n/**\n * Gamify SDK - Framework-agnostic event tracking\n */\nexport class Gamify {\n private readonly config: Required<GamifyConfig>;\n private readonly storage: StorageAdapter;\n private readonly queue: EventQueue;\n private readonly client: HttpClient;\n private readonly _session: SessionManager;\n private readonly _loyalty: LoyaltyManager;\n private readonly _referral: ReferralManager;\n private readonly _affiliate: AffiliateManager;\n private flushTimer: ReturnType<typeof setInterval> | null = null;\n private anonymousId: string;\n private userId: string | null = null;\n private userTraits: UserTraits | null = null;\n private initialized = false;\n\n constructor(config: GamifyConfig) {\n if (!config.apiKey) {\n throw new Error('[Gamify] API key is required');\n }\n\n // Reject secret keys in client-side SDK\n if (config.apiKey.startsWith('sk_')) {\n throw new Error(\n '[Gamify] Secret keys (sk_live_*) cannot be used in the browser. ' +\n 'Use a publishable key (pk_live_*) for client-side tracking. ' +\n 'For server-side tracking, use @gamifyio/node instead.'\n );\n }\n\n this.config = {\n apiKey: config.apiKey,\n endpoint: config.endpoint ?? DEFAULT_ENDPOINT,\n debug: config.debug ?? false,\n flushInterval: config.flushInterval ?? DEFAULT_FLUSH_INTERVAL,\n maxBatchSize: config.maxBatchSize ?? DEFAULT_MAX_BATCH_SIZE,\n storagePrefix: config.storagePrefix ?? DEFAULT_STORAGE_PREFIX,\n };\n\n this.storage = createStorage(this.config.storagePrefix);\n this.queue = new EventQueue(this.storage);\n this.client = new HttpClient(\n this.config.endpoint,\n this.config.apiKey,\n this.config.debug\n );\n\n // Initialize Session and Loyalty managers\n this._session = new SessionManager(this.config, this.client, this.storage);\n this._loyalty = new LoyaltyManager(this.config, this.client, this.storage);\n\n // Issue #22: Initialize Referral and Affiliate managers\n this._referral = new ReferralManager(this.config, this.storage);\n this._affiliate = new AffiliateManager(this.config, this.client, this.storage);\n\n // Load or generate anonymous ID\n const storedAnonymousId = this.storage.get<string>(ANONYMOUS_ID_KEY);\n this.anonymousId = storedAnonymousId ?? generateAnonymousId();\n if (!storedAnonymousId) {\n this.storage.set(ANONYMOUS_ID_KEY, this.anonymousId);\n }\n\n // Load persisted user identity\n this.userId = this.storage.get<string>(USER_ID_KEY);\n this.userTraits = this.storage.get<UserTraits>(USER_TRAITS_KEY);\n\n this.initialize();\n }\n\n /**\n * Initialize the SDK\n */\n private initialize(): void {\n if (this.initialized) return;\n\n this.log('Initializing SDK');\n\n // Start flush timer\n this.startFlushTimer();\n\n // Handle page unload - flush remaining events\n if (typeof window !== 'undefined') {\n window.addEventListener('beforeunload', () => this.onBeforeUnload());\n window.addEventListener('pagehide', () => this.onBeforeUnload());\n }\n\n this.initialized = true;\n this.log('SDK initialized', {\n anonymousId: this.anonymousId,\n userId: this.userId,\n });\n }\n\n /**\n * Log debug messages\n */\n private log(message: string, data?: unknown): void {\n if (this.config.debug) {\n console.log(`[Gamify] ${message}`, data ?? '');\n }\n }\n\n /**\n * Start the automatic flush timer\n */\n private startFlushTimer(): void {\n if (this.flushTimer) {\n clearInterval(this.flushTimer);\n }\n this.flushTimer = setInterval(() => {\n void this.flush();\n }, this.config.flushInterval);\n }\n\n /**\n * Handle page unload - send remaining events via beacon\n */\n private onBeforeUnload(): void {\n if (this.flushTimer) {\n clearInterval(this.flushTimer);\n this.flushTimer = null;\n }\n\n const pending = this.queue.peek(this.config.maxBatchSize);\n if (pending.length > 0) {\n const events = pending.map((item) => item.event);\n this.client.sendBeacon(events);\n }\n }\n\n /**\n * Identify a user with optional traits\n * User ID persists across page reloads\n */\n identify(userId: string, traits?: UserTraits): void {\n if (!userId || typeof userId !== 'string') {\n this.log('Invalid user ID provided');\n return;\n }\n\n this.log('Identifying user', { userId, traits });\n\n this.userId = userId;\n this.storage.set(USER_ID_KEY, userId);\n\n if (traits) {\n this.userTraits = { ...this.userTraits, ...traits };\n this.storage.set(USER_TRAITS_KEY, this.userTraits);\n }\n\n // Track identify event\n this.track('$identify', {\n userId,\n traits: traits ?? {},\n });\n }\n\n /**\n * Track an event\n */\n track(eventType: string, properties?: Record<string, unknown>): void {\n if (!eventType || typeof eventType !== 'string') {\n this.log('Invalid event type provided');\n return;\n }\n\n // Warn about trusted events that require server-side tracking\n if ((TRUSTED_EVENTS as readonly string[]).includes(eventType)) {\n console.warn(\n `[Gamify] Event \"${eventType}\" requires a secret key and will be rejected. ` +\n `Use @gamifyio/node on your server to track this event.`\n );\n }\n\n // Issue #22: Inject referrer properties into event\n const referrerProps = this._referral.getReferrerProperties();\n\n const event: GamifyEvent = {\n type: eventType,\n properties: { ...referrerProps, ...properties },\n timestamp: new Date().toISOString(),\n anonymousId: this.anonymousId,\n ...(this.userId && { userId: this.userId }),\n };\n\n this.log('Tracking event', event);\n\n this.queue.enqueue(event);\n\n // Auto-flush if batch size reached\n if (this.queue.size() >= this.config.maxBatchSize) {\n void this.flush();\n }\n }\n\n /**\n * Flush pending events to the server\n */\n async flush(): Promise<void> {\n const pending = this.queue.peek(this.config.maxBatchSize);\n if (pending.length === 0) {\n return;\n }\n\n const events = pending.map((item) => item.event);\n const ids = pending.map((item) => item.id);\n\n this.log(`Flushing ${events.length} events`);\n\n const result = await this.client.sendEvents(events);\n\n if (result.success) {\n this.queue.acknowledge(ids);\n } else {\n this.queue.nack(ids);\n }\n }\n\n /**\n * Reset the SDK state (logout user)\n */\n reset(): void {\n this.log('Resetting SDK state');\n\n this.userId = null;\n this.userTraits = null;\n this.storage.remove(USER_ID_KEY);\n this.storage.remove(USER_TRAITS_KEY);\n\n // Generate new anonymous ID\n this.anonymousId = generateAnonymousId();\n this.storage.set(ANONYMOUS_ID_KEY, this.anonymousId);\n }\n\n /**\n * Get current user ID (null if not identified)\n */\n getUserId(): string | null {\n return this.userId;\n }\n\n /**\n * Get anonymous ID\n */\n getAnonymousId(): string {\n return this.anonymousId;\n }\n\n /**\n * Get pending event count\n */\n getPendingCount(): number {\n return this.queue.size();\n }\n\n /**\n * Shutdown the SDK gracefully\n */\n shutdown(): void {\n this.log('Shutting down SDK');\n\n if (this.flushTimer) {\n clearInterval(this.flushTimer);\n this.flushTimer = null;\n }\n\n // Attempt final flush via beacon\n const pending = this.queue.peek(this.config.maxBatchSize);\n if (pending.length > 0) {\n const events = pending.map((item) => item.event);\n this.client.sendBeacon(events);\n }\n }\n\n // ============================================\n // Session Module (Issue #14)\n // ============================================\n\n /**\n * Get the session manager\n */\n get session(): SessionManager {\n return this._session;\n }\n\n /**\n * Convenience method: Update cart and get discounts\n */\n async updateCart(\n items: CartItem[],\n coupons?: string[],\n currency?: string,\n ) {\n if (!this.userId) {\n throw new Error('[Gamify] User must be identified before updating cart');\n }\n return this._session.updateCart(this.userId, items, coupons, currency);\n }\n\n // ============================================\n // Loyalty Module (Issue #15)\n // ============================================\n\n /**\n * Get the loyalty manager\n */\n get loyalty(): LoyaltyManager {\n return this._loyalty;\n }\n\n /**\n * Convenience method: Get loyalty profile for current user\n */\n async getLoyaltyProfile() {\n if (!this.userId) {\n throw new Error('[Gamify] User must be identified before getting loyalty profile');\n }\n return this._loyalty.getProfile(this.userId);\n }\n\n /**\n * Convenience method: Get loyalty history for current user\n */\n async getLoyaltyHistory(limit?: number, offset?: number) {\n if (!this.userId) {\n throw new Error('[Gamify] User must be identified before getting loyalty history');\n }\n return this._loyalty.getHistory(this.userId, limit, offset);\n }\n\n // ============================================\n // Referral Module (Issue #22)\n // ============================================\n\n /**\n * Get the referral manager\n */\n get referral(): ReferralManager {\n return this._referral;\n }\n\n /**\n * Convenience method: Get current referrer code\n */\n getReferrer(): string | null {\n return this._referral.getReferrer();\n }\n\n /**\n * Convenience method: Set referrer code manually\n */\n setReferrer(code: string): void {\n this._referral.setReferrer(code);\n }\n\n // ============================================\n // Affiliate Module (Issue #22)\n // ============================================\n\n /**\n * Get the affiliate manager\n */\n get affiliate(): AffiliateManager {\n return this._affiliate;\n }\n\n /**\n * Convenience method: Get affiliate stats for current user\n */\n async getAffiliateStats(forceRefresh = false): Promise<AffiliateStats> {\n if (!this.userId) {\n throw new Error('[Gamify] User must be identified before getting affiliate stats');\n }\n return this._affiliate.getStats(this.userId, forceRefresh);\n }\n\n /**\n * Convenience method: Get leaderboard\n */\n async getLeaderboard(limit = 10): Promise<LeaderboardResponse> {\n return this._affiliate.getLeaderboard(limit);\n }\n\n // ============================================\n // Quests Module (Issue #25-28)\n // ============================================\n\n /**\n * Get user's quest progress\n */\n async getQuests(): Promise<QuestsResponse> {\n if (!this.userId) {\n throw new Error('[Gamify] User must be identified before getting quests');\n }\n const response = await this.client.get<QuestsResponse>(\n `/v1/customer/quests?userId=${encodeURIComponent(this.userId)}`\n );\n if (response.success && response.data) {\n return response.data;\n }\n throw new Error(response.error || 'Failed to get quests');\n }\n\n // ============================================\n // Streaks Module (Issue #32)\n // ============================================\n\n /**\n * Get user's streaks with progress\n */\n async getStreaks(): Promise<StreaksResponse> {\n if (!this.userId) {\n throw new Error('[Gamify] User must be identified before getting streaks');\n }\n const response = await this.client.get<StreaksResponse>(\n `/v1/customer/streaks?userId=${encodeURIComponent(this.userId)}`\n );\n if (response.success && response.data) {\n return response.data;\n }\n throw new Error(response.error || 'Failed to get streaks');\n }\n\n /**\n * Use a freeze token for a streak\n */\n async useStreakFreeze(ruleId: string): Promise<FreezeResponse> {\n if (!this.userId) {\n throw new Error('[Gamify] User must be identified before using freeze');\n }\n const response = await this.client.post<FreezeResponse>(\n `/v1/customer/streaks/${encodeURIComponent(ruleId)}/freeze`,\n { userId: this.userId }\n );\n if (response.success && response.data) {\n return response.data;\n }\n throw new Error(response.error || 'Failed to use freeze');\n }\n\n // ============================================\n // Badges Module (Issue #33)\n // ============================================\n\n /**\n * Get user's badge collection\n */\n async getBadges(category?: string): Promise<BadgesResponse> {\n if (!this.userId) {\n throw new Error('[Gamify] User must be identified before getting badges');\n }\n let url = `/v1/customer/badges?userId=${encodeURIComponent(this.userId)}`;\n if (category) {\n url += `&category=${encodeURIComponent(category)}`;\n }\n const response = await this.client.get<BadgesResponse>(url);\n if (response.success && response.data) {\n return response.data;\n }\n throw new Error(response.error || 'Failed to get badges');\n }\n\n /**\n * Get available badge categories\n */\n async getBadgeCategories(): Promise<string[]> {\n const response = await this.client.get<string[]>('/v1/customer/badges/categories');\n if (response.success && response.data) {\n return response.data;\n }\n throw new Error(response.error || 'Failed to get badge categories');\n }\n\n // ============================================\n // Rewards Module (Issue #34)\n // ============================================\n\n /**\n * Get rewards store items\n */\n async getRewardsStore(): Promise<RewardsStoreResponse> {\n if (!this.userId) {\n throw new Error('[Gamify] User must be identified before getting rewards');\n }\n const response = await this.client.get<RewardsStoreResponse>(\n `/v1/customer/store?userId=${encodeURIComponent(this.userId)}`\n );\n if (response.success && response.data) {\n return response.data;\n }\n throw new Error(response.error || 'Failed to get rewards store');\n }\n\n /**\n * Redeem a reward item\n */\n async redeemReward(itemId: string): Promise<RedemptionResult> {\n if (!this.userId) {\n throw new Error('[Gamify] User must be identified before redeeming rewards');\n }\n const response = await this.client.post<RedemptionResult>(\n '/v1/customer/store/redeem',\n { userId: this.userId, itemId }\n );\n if (response.success && response.data) {\n return response.data;\n }\n throw new Error(response.error || 'Failed to redeem reward');\n }\n}\n","/**\n * Configuration options for the Gamify SDK\n */\nexport interface GamifyConfig {\n /** API key for authentication */\n apiKey: string;\n /** Base URL for the API endpoint */\n endpoint?: string;\n /** Enable debug logging */\n debug?: boolean;\n /** Flush interval in milliseconds (default: 10000) */\n flushInterval?: number;\n /** Maximum events to batch before auto-flush (default: 10) */\n maxBatchSize?: number;\n /** Storage key prefix (default: 'gamify_') */\n storagePrefix?: string;\n}\n\n/**\n * Event payload structure\n */\nexport interface GamifyEvent {\n /** Event type (e.g., 'page_view', 'click', 'purchase') */\n type: string;\n /** Event properties */\n properties?: Record<string, unknown>;\n /** Timestamp when event occurred */\n timestamp: string;\n /** User ID if identified */\n userId?: string;\n /** Anonymous ID for unidentified users */\n anonymousId: string;\n}\n\n/**\n * User traits for identify calls\n */\nexport interface UserTraits {\n email?: string;\n name?: string;\n [key: string]: unknown;\n}\n\n/**\n * Internal queued event with metadata\n */\nexport interface QueuedEvent {\n id: string;\n event: GamifyEvent;\n attempts: number;\n createdAt: number;\n}\n\n/**\n * API response structure\n */\nexport interface ApiResponse {\n success: boolean;\n error?: string;\n}\n\n/**\n * Storage interface for persistence layer\n */\nexport interface StorageAdapter {\n get<T>(key: string): T | null;\n set<T>(key: string, value: T): void;\n remove(key: string): void;\n clear(): void;\n}\n\n// ============================================\n// Issue #14: Session Types\n// ============================================\n\n/**\n * Cart item structure for sessions\n */\nexport interface CartItem {\n sku: string;\n name: string;\n quantity: number;\n unitPrice: number;\n category?: string;\n brand?: string;\n metadata?: Record<string, unknown>;\n}\n\n/**\n * Session request to create/update cart\n */\nexport interface SessionRequest {\n userId: string;\n items: CartItem[];\n coupons?: string[];\n currency?: string;\n}\n\n/**\n * Applied effect from rule engine\n */\nexport interface AppliedEffect {\n type: string;\n ruleId?: string;\n params: Record<string, unknown>;\n discountAmount?: number;\n}\n\n/**\n * Session response from API\n */\nexport interface SessionResponse {\n sessionToken: string;\n items: CartItem[];\n subtotal: number;\n discount: number;\n total: number;\n coupons: string[];\n appliedEffects: AppliedEffect[];\n rejectedCoupons?: string[];\n currency: string;\n status: string;\n}\n\n// ============================================\n// Issue #15: Loyalty Types\n// ============================================\n\n/**\n * Loyalty tier information\n */\nexport interface LoyaltyTier {\n id: string;\n name: string;\n level: number;\n benefits: Record<string, unknown>;\n color?: string | null;\n iconUrl?: string | null;\n}\n\n/**\n * Next tier information\n */\nexport interface NextTierInfo {\n id: string;\n name: string;\n minPoints: number;\n pointsNeeded: number;\n}\n\n/**\n * Loyalty summary statistics\n */\nexport interface LoyaltySummary {\n totalEarned: number;\n totalRedeemed: number;\n transactionCount: number;\n}\n\n/**\n * Customer loyalty profile\n */\nexport interface LoyaltyProfile {\n userId: string;\n points: number;\n tier: LoyaltyTier | null;\n nextTier: NextTierInfo | null;\n summary: LoyaltySummary;\n}\n\n/**\n * Loyalty transaction entry\n */\nexport interface LoyaltyTransaction {\n id: string;\n amount: number;\n balance: number;\n type: 'earn' | 'redeem' | 'expire' | 'adjust' | 'bonus';\n referenceId?: string;\n referenceType?: string;\n description?: string;\n createdAt: string;\n}\n\n/**\n * Loyalty history response\n */\nexport interface LoyaltyHistoryResponse {\n transactions: LoyaltyTransaction[];\n total: number;\n}\n\n// ============================================\n// Issue #22: Affiliate Types\n// ============================================\n\n/**\n * Affiliate tier/plan information\n */\nexport interface AffiliateTier {\n id: string;\n name: string;\n type: 'PERCENTAGE' | 'FIXED';\n value: number;\n}\n\n/**\n * Referral information\n */\nexport interface ReferralInfo {\n id: string;\n referredExternalId: string;\n referralCode: string;\n createdAt: string;\n}\n\n/**\n * Affiliate earnings summary\n */\nexport interface AffiliateEarnings {\n totalEarned: number;\n totalPending: number;\n totalPaid: number;\n transactionCount: number;\n currency: string;\n}\n\n/**\n * Affiliate statistics profile\n */\nexport interface AffiliateStats {\n userId: string;\n referralCode: string | null;\n referralCount: number;\n earnings: AffiliateEarnings;\n tier: AffiliateTier | null;\n}\n\n/**\n * Affiliate stats API response\n */\nexport interface AffiliateStatsResponse {\n stats: AffiliateStats;\n}\n\n/**\n * Leaderboard entry\n */\nexport interface LeaderboardEntry {\n rank: number;\n userId: string;\n displayName?: string;\n referralCount: number;\n totalEarnings: number;\n tier?: AffiliateTier | null;\n}\n\n/**\n * Leaderboard response\n */\nexport interface LeaderboardResponse {\n entries: LeaderboardEntry[];\n total: number;\n currentUserRank?: number;\n}\n\n// ============================================\n// Issue #25-28: Quest Types\n// ============================================\n\n/**\n * Quest step definition\n */\nexport interface QuestStep {\n id: string;\n name: string;\n description?: string;\n eventName: string;\n requiredCount: number;\n order: number;\n currentCount: number;\n completed: boolean;\n}\n\n/**\n * Quest with user progress\n */\nexport interface QuestWithProgress {\n id: string;\n name: string;\n description?: string;\n xpReward: number;\n badgeReward?: string;\n status: 'not_started' | 'in_progress' | 'completed';\n percentComplete: number;\n steps: QuestStep[];\n startedAt?: string;\n completedAt?: string;\n}\n\n/**\n * Quests response from API\n */\nexport interface QuestsResponse {\n quests: QuestWithProgress[];\n}\n\n// ============================================\n// Issue #32: Streak Types\n// ============================================\n\n/**\n * Streak milestone definition\n */\nexport interface StreakMilestone {\n day: number;\n rewardXp: number;\n badgeId?: string;\n reached: boolean;\n}\n\n/**\n * User streak with progress\n */\nexport interface StreakWithProgress {\n id: string;\n name: string;\n description?: string;\n eventType: string;\n frequency: string;\n currentCount: number;\n maxStreak: number;\n status: string;\n lastActivityDate?: string;\n freezeInventory: number;\n freezeUsedToday: boolean;\n nextMilestone?: {\n day: number;\n rewardXp: number;\n badgeId?: string;\n };\n milestones: StreakMilestone[];\n}\n\n/**\n * Streaks response from API\n */\nexport interface StreaksResponse {\n streaks: StreakWithProgress[];\n stats: {\n totalActive: number;\n longestCurrent: number;\n longestEver: number;\n };\n}\n\n/**\n * Freeze response\n */\nexport interface FreezeResponse {\n success: boolean;\n remainingFreezes: number;\n message: string;\n}\n\n// ============================================\n// Issue #33: Badge Types\n// ============================================\n\n/**\n * Badge rarity levels\n */\nexport type BadgeRarity = 'COMMON' | 'RARE' | 'EPIC' | 'LEGENDARY';\n\n/**\n * Badge with unlock status\n */\nexport interface BadgeWithStatus {\n id: string;\n name: string;\n description?: string;\n iconUrl?: string;\n imageUrl?: string;\n rarity: BadgeRarity;\n visibility: 'PUBLIC' | 'HIDDEN';\n category?: string;\n isUnlocked: boolean;\n unlockedAt?: string;\n}\n\n/**\n * Badges response from API\n */\nexport interface BadgesResponse {\n badges: BadgeWithStatus[];\n stats: {\n total: number;\n unlocked: number;\n byRarity: Record<string, { total: number; unlocked: number }>;\n };\n}\n\n// ============================================\n// Issue #34: Rewards Types\n// ============================================\n\n/**\n * Reward item from store\n */\nexport interface RewardItem {\n id: string;\n name: string;\n description?: string;\n pointsCost: number;\n imageUrl?: string;\n category?: string;\n stock?: number;\n requiredBadgeId?: string;\n requiredBadgeName?: string;\n canAfford: boolean;\n hasBadge: boolean;\n isAvailable: boolean;\n}\n\n/**\n * Rewards store response\n */\nexport interface RewardsStoreResponse {\n items: RewardItem[];\n userPoints: number;\n}\n\n/**\n * Redemption result\n */\nexport interface RedemptionResult {\n success: boolean;\n transactionId?: string;\n message: string;\n remainingPoints?: number;\n}\n\n// ============================================\n// Theme System\n// ============================================\n\n/**\n * Theme configuration for SDK components\n * All colors should be valid CSS color values\n */\nexport interface Theme {\n // Base colors\n /** Card/container background color */\n background: string;\n /** Secondary background (inputs, nested elements) */\n backgroundSecondary: string;\n /** Primary text color */\n foreground: string;\n /** Secondary/muted text color */\n foregroundSecondary: string;\n /** Border color */\n border: string;\n\n // Accent colors\n /** Primary accent color (buttons, highlights) */\n primary: string;\n /** Text on primary color */\n primaryForeground: string;\n\n // Status colors\n /** Success/positive states */\n success: string;\n /** Warning/pending states */\n warning: string;\n /** Error states */\n error: string;\n\n // Rarity colors (for badges/achievements)\n rarityCommon: string;\n rarityRare: string;\n rarityEpic: string;\n rarityLegendary: string;\n\n // Medal colors (for leaderboard)\n medalGold: string;\n medalSilver: string;\n medalBronze: string;\n\n // Component-specific (optional overrides)\n cardBackground?: string;\n cardBorder?: string;\n inputBackground?: string;\n inputBorder?: string;\n buttonBackground?: string;\n buttonForeground?: string;\n}\n\n/**\n * Default dark theme matching the playground UI\n */\nexport const defaultTheme: Theme = {\n // Base colors (dark theme with glass effect)\n background: 'rgba(255, 255, 255, 0.05)',\n backgroundSecondary: 'rgba(255, 255, 255, 0.1)',\n foreground: '#ffffff',\n foregroundSecondary: 'rgba(255, 255, 255, 0.7)',\n border: 'rgba(255, 255, 255, 0.1)',\n\n // Accent colors (purple like playground)\n primary: '#8B5CF6',\n primaryForeground: '#ffffff',\n\n // Status colors\n success: '#22C55E',\n warning: '#F59E0B',\n error: '#EF4444',\n\n // Rarity colors (matching playground)\n rarityCommon: '#9CA3AF',\n rarityRare: '#3B82F6',\n rarityEpic: '#8B5CF6',\n rarityLegendary: '#F59E0B',\n\n // Medal colors\n medalGold: '#FFD700',\n medalSilver: '#C0C0C0',\n medalBronze: '#CD7F32',\n};\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gamifyio/core",
3
- "version": "0.1.4",
3
+ "version": "0.1.5",
4
4
  "description": "Framework-agnostic TypeScript SDK for Gamify event tracking",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",