Comparing version 0.2.25 to 0.2.26
@@ -31,11 +31,24 @@ import EventQueue from "./eventQueue"; | ||
const getTimeOfDay = () => { | ||
const hour = new Date().getHours(); | ||
if (hour >= 5 && hour < 12) { | ||
return "morning"; | ||
} else if (hour >= 12 && hour < 14) { | ||
return "noon"; | ||
} else if (hour >= 14 && hour < 18) { | ||
return "afternoon"; | ||
} else if (hour >= 18 && hour < 21) { | ||
return "evening"; | ||
} else { | ||
return "night"; | ||
} | ||
}; | ||
const getTimeZoneData = () => { | ||
const timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone; | ||
const timeZoneOffset = new Date().getTimezoneOffset(); | ||
return { timeZone, timeZoneOffset }; | ||
}; | ||
const getAnalyticsEvents = (widgetState = {}) => { | ||
const { | ||
widgetOpen, | ||
widgetRoute, | ||
garmentId, | ||
recommendedSize, | ||
widgetLoaded, | ||
hasPrediction, | ||
} = widgetState; | ||
const createEvent = (category, type, props) => { | ||
@@ -48,2 +61,6 @@ const event = { | ||
timestamp: new Date().toISOString(), | ||
localTimestamp: new Date().toLocaleString(), | ||
timeZone: getTimeZoneData().timeZone, | ||
timeZoneOffset: getTimeZoneData().timeZoneOffset, | ||
timeOfDay: getTimeOfDay(), | ||
pageUrl: window.location.href, | ||
@@ -58,9 +75,3 @@ referrerUrl: document.referrer, | ||
browser: getBrowserName(navigator.userAgent), | ||
// TODO: Add the widget route to the metadata | ||
widgetLoaded, | ||
widgetHasPrediction: hasPrediction, | ||
widgetRoute, | ||
widgetOpen, | ||
widgetGarmentId: garmentId, | ||
widgetRecommendedSize: recommendedSize, | ||
...widgetState | ||
}, | ||
@@ -67,0 +78,0 @@ }; |
@@ -1,1 +0,1 @@ | ||
!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.truetoformApi=t():e.truetoformApi=t()}(this,(()=>(()=>{"use strict";var e={d:(t,r)=>{for(var n in r)e.o(r,n)&&!e.o(t,n)&&Object.defineProperty(t,n,{enumerable:!0,get:r[n]})},o:(e,t)=>Object.prototype.hasOwnProperty.call(e,t),r:e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})}},t={};e.r(t),e.d(t,{default:()=>h});const r="ttf-analytics-session-id",n="ttf-analytics-user-id",o=e=>{const t="localhost"===window.location.hostname||"https://truetoform-test-store.myshopify.com"===window.location.origin?"https://api.truetoform.online/v1":"https://api.truetoform.fit/v1",o=async(o,s,i=null,a=null,c=null)=>{const d=(()=>{const t={"Content-Type":"application/json","X-TTF-API-KEY":e},o=localStorage.getItem(r),s=localStorage.getItem(n);return o&&(t["X-TTF-SESSION-ID"]=o),s&&(t["X-TTF-USER-ID"]=s),t})();c&&(d["If-None-Match"]=c);const u={method:o,headers:d,body:i?JSON.stringify(i):null,credentials:"include",signal:a},l=await fetch(`${t}/${s}`,u);if(!l.ok){const e=await l.json();throw new Error(`Error ${l.status}: ${e?.message}`)}const g=l.headers.get("X-New-Session-ID"),h=l.headers.get("X-New-User-ID");if(g&&localStorage.setItem(r,g),h&&localStorage.setItem(n,h),204===l.status)return null;const f=l.headers.get("content-type");if(f&&f.includes("application/json")){return await l.json()}if(f&&f.includes("text/event-stream"))return l.body;if(304===l.status)return{currentETag:localStorage.getItem("ttf-sdk-eTag")};return await l.text()};return{get:(e,t,r)=>o("GET",e,null,t,r),post:(e,t,r)=>o("POST",e,t,r),put:(e,t,r)=>o("PUT",e,t,r),delete:(e,t)=>o("DELETE",e,null,t)}};class s{constructor(){if(s.instance)return s.instance;this.eventQueue=[],this.MAX_BATCH_SIZE=50,this.MAX_TIME_INTERVAL=5e3,this.sendEventsTimer=null,this.loadQueueFromLocalStorage(),window.addEventListener("beforeunload",(()=>{this.sendRemainingEvents()})),window.addEventListener("storage",(e=>{console.log("Storage event:",e),"analyticsEventQueue"===e.key&&this.loadQueueFromLocalStorage()})),s.instance=this}static getInstance(){return s.instance||(s.instance=new s),s.instance}loadQueueFromLocalStorage(){try{const e=localStorage.getItem("analyticsEventQueue");e&&(this.eventQueue=JSON.parse(e))}catch(e){console.error("Failed to load event queue from localStorage:",e)}}saveQueueToLocalStorage(){try{localStorage.setItem("analyticsEventQueue",JSON.stringify(this.eventQueue))}catch(e){console.error("Failed to save event queue to localStorage:",e)}}enqueueEvent(e){this.eventQueue.push(e),this.saveQueueToLocalStorage(),this.checkAndSendEvents()}checkAndSendEvents(){console.log("Checking and sending events"),this.eventQueue.length>=this.MAX_BATCH_SIZE?this.sendEvents():this.sendEventsTimer||(this.sendEventsTimer=setTimeout((()=>{this.sendEvents()}),this.MAX_TIME_INTERVAL))}async sendEvents(){if(this.sendEventsTimer&&(clearTimeout(this.sendEventsTimer),this.sendEventsTimer=null),0===this.eventQueue.length)return;const e=[...this.eventQueue];this.eventQueue=[],this.saveQueueToLocalStorage(),console.log("Sending events:",e)}sendRemainingEvents(){if(this.eventQueue.length>0){const e=localStorage.getItem("userIdKey"),t=localStorage.getItem("sessionIdKey");console.log("Send remaining events",e,t,this.eventQueue)}}}const i=s.getInstance(),a="widget_interaction",c="host_interaction",d="user_update",u="conversion",l="custom",g=(e={})=>{const{widgetOpen:t,widgetRoute:r,garmentId:n,recommendedSize:o,widgetLoaded:s,hasPrediction:g}=e,h=(e,a,c)=>{const d={category:e,type:a,props:c,metadata:{timestamp:(new Date).toISOString(),pageUrl:window.location.href,referrerUrl:document.referrer,device:/Mobi|Android/i.test(navigator.userAgent)?"mobile":"desktop",screenWidth:window.screen.width,screenHeight:window.screen.height,browserLanguage:navigator.language,browser:(u=navigator.userAgent,u.includes("Firefox")?"Firefox":u.includes("Edg")?"Edge":u.includes("Chrome")&&!u.includes("Chromium")?"Chrome":u.includes("Safari")&&!u.includes("Chrome")?"Safari":u.includes("Opera")||u.includes("OPR")?"Opera":u.includes("MSIE")||u.includes("Trident")?"Internet Explorer":"Unknown"),widgetLoaded:s,widgetHasPrediction:g,widgetRoute:r,widgetOpen:t,widgetGarmentId:n,widgetRecommendedSize:o}};var u;return i.enqueueEvent(d),d};return{addWidgetInteractionEvent:(e,t)=>h(a,e,t),addHostInteractionEvent:(e,t)=>h(c,e,t),addUserUpdateEvent:(e,t)=>h(d,e,t),addConversionEvent:(e,t)=>h(u,e,t),addCustomEvent:(e,t)=>h(l,e,t)}},h=e=>{const t=o(e),s=e=>{if(!e)throw new Error("survey data is required");return t.post("surveys",e)},i=e=>{if(!e)throw new Error("session ID is required");return t.post("scans",{sessionId:e})};return{isStale:async()=>{const e=localStorage.getItem("ttf-sdk-eTag"),r=await t.get("stale-check",null,e),{currentETag:n}=r;return localStorage.setItem("ttf-sdk-eTag",n),e!==n},initAnalytics:async()=>t.post("analytics/init").then((e=>{const{userId:t,sessionId:o}=e;return localStorage.setItem(n,t),localStorage.setItem(r,o),e})),getGarment:e=>{if(!e)throw new Error("garment ID is required");return t.get(`garments/${e}`)},createSurvey:s,getSurvey:e=>{if(!e)throw new Error("survey ID is required");return t.get(`surveys/${e}`)},createScan:i,getScan:e=>{if(!e)throw new Error("scan ID is required");return t.get(`scans/${e}`)},getPrediction:e=>{if(!e)throw new Error("prediction ID is required");return t.get(`predictions/${e}`)},createSession:()=>t.post("sessions"),createScanPrediction:async(e,r)=>{if(!e)throw new Error("garment ID is required");if(!r)throw new Error("session ID is required");const n=await i(r);if(!n||!n.id)throw new Error("Failed to create scan");return t.post("predictions",{garmentId:e,creatorId:n.id})},createSurveyPrediction:async(e,r)=>{if(!e)throw new Error("garment ID is required");if(!r)throw new Error("survey data is required");const n=await s(r);if(!n||!n.id)throw new Error("Failed to create survey");return t.post("predictions",{garmentId:e,creatorId:n.id})},createPredictionFromId:(e,r)=>{if(!e)throw new Error("garment ID is required");if(!r)throw new Error("creator ID is required");return t.post("predictions",{garmentId:e,creatorId:r})},getRealtimeSession:(e,{onChange:r,onSuccess:n,onError:o})=>{let s=()=>{};return async function(){try{const i=new AbortController,a=i.signal;s=()=>i.abort();const c=await t.get(`sessions/${e}`,a),d=c?.getReader(),u=new TextDecoder;let{done:l,value:g}=await d.read();for(;!l;){const e=u.decode(g,{stream:!0});try{const t=JSON.parse(e);switch(console.log("SSE data:",t),t.type){case"SESSION_DATA":if(r&&"function"==typeof r)try{r(t.payload?.progress)}catch(e){console.error("Error calling onChange:",e)}if(n&&"function"==typeof n){if("READY"===t.payload?.progress)try{n(t.payload)}catch(e){console.error("Error calling onSuccess:",e)}}if(o&&"function"==typeof o){if("FAILED"===t.payload?.progress)try{o(t.payload)}catch(e){console.error("Error calling onError:",e)}}break;case"ERROR":if(console.error("SSE error:",t),o&&"function"==typeof o)try{o(t)}catch(e){console.error("Error calling onError:",e)}}}catch(e){if(console.error("Error parsing SSE data:",e),o&&"function"==typeof o)try{o({type:"ERROR",message:"Parsing error"})}catch(e){console.error("Error calling onError:",e)}}({done:l,value:g}=await d.read())}}catch(e){console.error("Stream error:",e),o&&"function"==typeof o&&o({type:"ERROR",message:"Stream error"})}}(),s},getSessionStatus:e=>t.get(`sessions/${e}/status`),getAnalyticsEvents:g}};return t})())); | ||
!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.truetoformApi=t():e.truetoformApi=t()}(this,(()=>(()=>{"use strict";var e={d:(t,r)=>{for(var n in r)e.o(r,n)&&!e.o(t,n)&&Object.defineProperty(t,n,{enumerable:!0,get:r[n]})},o:(e,t)=>Object.prototype.hasOwnProperty.call(e,t),r:e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})}},t={};e.r(t),e.d(t,{default:()=>m});const r="ttf-analytics-session-id",n="ttf-analytics-user-id",o=e=>{const t="localhost"===window.location.hostname||"https://truetoform-test-store.myshopify.com"===window.location.origin?"https://api.truetoform.online/v1":"https://api.truetoform.fit/v1",o=async(o,s,i=null,a=null,c=null)=>{const u=(()=>{const t={"Content-Type":"application/json","X-TTF-API-KEY":e},o=localStorage.getItem(r),s=localStorage.getItem(n);return o&&(t["X-TTF-SESSION-ID"]=o),s&&(t["X-TTF-USER-ID"]=s),t})();c&&(u["If-None-Match"]=c);const l={method:o,headers:u,body:i?JSON.stringify(i):null,credentials:"include",signal:a},d=await fetch(`${t}/${s}`,l);if(!d.ok){const e=await d.json();throw new Error(`Error ${d.status}: ${e?.message}`)}const g=d.headers.get("X-New-Session-ID"),f=d.headers.get("X-New-User-ID");if(g&&localStorage.setItem(r,g),f&&localStorage.setItem(n,f),204===d.status)return null;const h=d.headers.get("content-type");if(h&&h.includes("application/json")){return await d.json()}if(h&&h.includes("text/event-stream"))return d.body;if(304===d.status)return{currentETag:localStorage.getItem("ttf-sdk-eTag")};return await d.text()};return{get:(e,t,r)=>o("GET",e,null,t,r),post:(e,t,r)=>o("POST",e,t,r),put:(e,t,r)=>o("PUT",e,t,r),delete:(e,t)=>o("DELETE",e,null,t)}};class s{constructor(){if(s.instance)return s.instance;this.eventQueue=[],this.MAX_BATCH_SIZE=50,this.MAX_TIME_INTERVAL=5e3,this.sendEventsTimer=null,this.loadQueueFromLocalStorage(),window.addEventListener("beforeunload",(()=>{this.sendRemainingEvents()})),window.addEventListener("storage",(e=>{console.log("Storage event:",e),"analyticsEventQueue"===e.key&&this.loadQueueFromLocalStorage()})),s.instance=this}static getInstance(){return s.instance||(s.instance=new s),s.instance}loadQueueFromLocalStorage(){try{const e=localStorage.getItem("analyticsEventQueue");e&&(this.eventQueue=JSON.parse(e))}catch(e){console.error("Failed to load event queue from localStorage:",e)}}saveQueueToLocalStorage(){try{localStorage.setItem("analyticsEventQueue",JSON.stringify(this.eventQueue))}catch(e){console.error("Failed to save event queue to localStorage:",e)}}enqueueEvent(e){this.eventQueue.push(e),this.saveQueueToLocalStorage(),this.checkAndSendEvents()}checkAndSendEvents(){console.log("Checking and sending events"),this.eventQueue.length>=this.MAX_BATCH_SIZE?this.sendEvents():this.sendEventsTimer||(this.sendEventsTimer=setTimeout((()=>{this.sendEvents()}),this.MAX_TIME_INTERVAL))}async sendEvents(){if(this.sendEventsTimer&&(clearTimeout(this.sendEventsTimer),this.sendEventsTimer=null),0===this.eventQueue.length)return;const e=[...this.eventQueue];this.eventQueue=[],this.saveQueueToLocalStorage(),console.log("Sending events:",e)}sendRemainingEvents(){if(this.eventQueue.length>0){const e=localStorage.getItem("userIdKey"),t=localStorage.getItem("sessionIdKey");console.log("Send remaining events",e,t,this.eventQueue)}}}const i=s.getInstance(),a="widget_interaction",c="host_interaction",u="user_update",l="conversion",d="custom",g=()=>{const e=(new Date).getHours();return e>=5&&e<12?"morning":e>=12&&e<14?"noon":e>=14&&e<18?"afternoon":e>=18&&e<21?"evening":"night"},f=()=>({timeZone:Intl.DateTimeFormat().resolvedOptions().timeZone,timeZoneOffset:(new Date).getTimezoneOffset()}),h=(e={})=>{const t=(t,r,n)=>{const o={category:t,type:r,props:n,metadata:{timestamp:(new Date).toISOString(),localTimestamp:(new Date).toLocaleString(),timeZone:f().timeZone,timeZoneOffset:f().timeZoneOffset,timeOfDay:g(),pageUrl:window.location.href,referrerUrl:document.referrer,device:/Mobi|Android/i.test(navigator.userAgent)?"mobile":"desktop",screenWidth:window.screen.width,screenHeight:window.screen.height,browserLanguage:navigator.language,browser:(s=navigator.userAgent,s.includes("Firefox")?"Firefox":s.includes("Edg")?"Edge":s.includes("Chrome")&&!s.includes("Chromium")?"Chrome":s.includes("Safari")&&!s.includes("Chrome")?"Safari":s.includes("Opera")||s.includes("OPR")?"Opera":s.includes("MSIE")||s.includes("Trident")?"Internet Explorer":"Unknown"),...e}};var s;return i.enqueueEvent(o),o};return{addWidgetInteractionEvent:(e,r)=>t(a,e,r),addHostInteractionEvent:(e,r)=>t(c,e,r),addUserUpdateEvent:(e,r)=>t(u,e,r),addConversionEvent:(e,r)=>t(l,e,r),addCustomEvent:(e,r)=>t(d,e,r)}},m=e=>{const t=o(e),s=e=>{if(!e)throw new Error("survey data is required");return t.post("surveys",e)},i=e=>{if(!e)throw new Error("session ID is required");return t.post("scans",{sessionId:e})};return{isStale:async()=>{const e=localStorage.getItem("ttf-sdk-eTag"),r=await t.get("stale-check",null,e),{currentETag:n}=r;return localStorage.setItem("ttf-sdk-eTag",n),e!==n},initAnalytics:async()=>t.post("analytics/init").then((e=>{const{userId:t,sessionId:o}=e;return localStorage.setItem(n,t),localStorage.setItem(r,o),e})),getGarment:e=>{if(!e)throw new Error("garment ID is required");return t.get(`garments/${e}`)},createSurvey:s,getSurvey:e=>{if(!e)throw new Error("survey ID is required");return t.get(`surveys/${e}`)},createScan:i,getScan:e=>{if(!e)throw new Error("scan ID is required");return t.get(`scans/${e}`)},getPrediction:e=>{if(!e)throw new Error("prediction ID is required");return t.get(`predictions/${e}`)},createSession:()=>t.post("sessions"),createScanPrediction:async(e,r)=>{if(!e)throw new Error("garment ID is required");if(!r)throw new Error("session ID is required");const n=await i(r);if(!n||!n.id)throw new Error("Failed to create scan");return t.post("predictions",{garmentId:e,creatorId:n.id})},createSurveyPrediction:async(e,r)=>{if(!e)throw new Error("garment ID is required");if(!r)throw new Error("survey data is required");const n=await s(r);if(!n||!n.id)throw new Error("Failed to create survey");return t.post("predictions",{garmentId:e,creatorId:n.id})},createPredictionFromId:(e,r)=>{if(!e)throw new Error("garment ID is required");if(!r)throw new Error("creator ID is required");return t.post("predictions",{garmentId:e,creatorId:r})},getRealtimeSession:(e,{onChange:r,onSuccess:n,onError:o})=>{let s=()=>{};return async function(){try{const i=new AbortController,a=i.signal;s=()=>i.abort();const c=await t.get(`sessions/${e}`,a),u=c?.getReader(),l=new TextDecoder;let{done:d,value:g}=await u.read();for(;!d;){const e=l.decode(g,{stream:!0});try{const t=JSON.parse(e);switch(console.log("SSE data:",t),t.type){case"SESSION_DATA":if(r&&"function"==typeof r)try{r(t.payload?.progress)}catch(e){console.error("Error calling onChange:",e)}if(n&&"function"==typeof n){if("READY"===t.payload?.progress)try{n(t.payload)}catch(e){console.error("Error calling onSuccess:",e)}}if(o&&"function"==typeof o){if("FAILED"===t.payload?.progress)try{o(t.payload)}catch(e){console.error("Error calling onError:",e)}}break;case"ERROR":if(console.error("SSE error:",t),o&&"function"==typeof o)try{o(t)}catch(e){console.error("Error calling onError:",e)}}}catch(e){if(console.error("Error parsing SSE data:",e),o&&"function"==typeof o)try{o({type:"ERROR",message:"Parsing error"})}catch(e){console.error("Error calling onError:",e)}}({done:d,value:g}=await u.read())}}catch(e){console.error("Stream error:",e),o&&"function"==typeof o&&o({type:"ERROR",message:"Stream error"})}}(),s},getSessionStatus:e=>t.get(`sessions/${e}/status`),getAnalyticsEvents:h}};return t})())); |
{ | ||
"name": "ttf-api", | ||
"version": "0.2.25", | ||
"version": "0.2.26", | ||
"description": "The TrueToForm API SDK", | ||
@@ -5,0 +5,0 @@ "main": "dist/index.js", |
34567
12
871