@bluenath/engage 2.0.3 → 2.0.4
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/core/Analytics.d.ts +13 -1
- package/dist/core/EventQueue.d.ts +26 -1
- package/dist/core/Transport.d.ts +24 -1
- package/dist/index.cjs +1 -1
- package/dist/index.d.ts +17 -0
- package/dist/index.js +574 -30
- package/dist/lib/timezone-country.d.ts +6 -0
- package/dist/types/index.d.ts +23 -0
- package/package.json +1 -1
package/dist/core/Analytics.d.ts
CHANGED
|
@@ -1,15 +1,27 @@
|
|
|
1
|
-
import { EngageConfig, AnalyticsEvent } from '../types';
|
|
1
|
+
import { EngageConfig, AnalyticsEvent, SentimentValue } from '../types';
|
|
2
2
|
|
|
3
3
|
export declare class EngagePro {
|
|
4
4
|
private config;
|
|
5
5
|
private context;
|
|
6
6
|
private queue;
|
|
7
7
|
private transport;
|
|
8
|
+
private sessionId;
|
|
9
|
+
private countryCode;
|
|
10
|
+
private currentSentiment;
|
|
8
11
|
constructor(config: EngageConfig);
|
|
9
12
|
identify: (userId: string, traits?: Record<string, unknown>) => void;
|
|
10
13
|
page: (name?: string, properties?: Record<string, unknown>) => void;
|
|
11
14
|
track: (eventName: string, properties?: Record<string, unknown>, intelligence?: Partial<AnalyticsEvent>) => void;
|
|
12
15
|
trackRevenue: (amount: number | string, properties?: Record<string, unknown>, intelligence?: Partial<AnalyticsEvent>) => void;
|
|
16
|
+
/**
|
|
17
|
+
* Spec §3.3: Set sentiment for the current session.
|
|
18
|
+
* Attached to the next emitted event.
|
|
19
|
+
*/
|
|
20
|
+
setSentiment: (sentiment: SentimentValue) => void;
|
|
21
|
+
/**
|
|
22
|
+
* Emit a new-format EngageEvent to the ingest pipeline (Spec §3.1).
|
|
23
|
+
*/
|
|
24
|
+
private emitEngageEvent;
|
|
13
25
|
private parseRef;
|
|
14
26
|
private processEvent;
|
|
15
27
|
}
|
|
@@ -1,18 +1,43 @@
|
|
|
1
|
-
import { AnalyticsEvent } from '../types';
|
|
1
|
+
import { AnalyticsEvent, EngageEvent } from '../types';
|
|
2
2
|
import { Transport } from './Transport';
|
|
3
3
|
|
|
4
4
|
export declare class EventQueue {
|
|
5
5
|
private queue;
|
|
6
|
+
private engageQueue;
|
|
6
7
|
private storage;
|
|
7
8
|
private transport;
|
|
8
9
|
private readonly STORAGE_KEY;
|
|
10
|
+
private readonly ENGAGE_STORAGE_KEY;
|
|
9
11
|
private isFlushing;
|
|
10
12
|
private flushInterval;
|
|
11
13
|
private retryCount;
|
|
14
|
+
private readonly MAX_RETRIES;
|
|
12
15
|
constructor(transport: Transport);
|
|
13
16
|
private startTimer;
|
|
17
|
+
/**
|
|
18
|
+
* Enqueue a legacy AnalyticsEvent (backward compatibility)
|
|
19
|
+
*/
|
|
14
20
|
enqueue(event: AnalyticsEvent): void;
|
|
21
|
+
/**
|
|
22
|
+
* Enqueue a new EngageEvent (Spec §3.1)
|
|
23
|
+
*/
|
|
24
|
+
enqueueEngageEvent(event: EngageEvent): void;
|
|
15
25
|
private save;
|
|
26
|
+
private saveEngage;
|
|
16
27
|
private load;
|
|
28
|
+
/**
|
|
29
|
+
* Flush legacy events (existing behavior)
|
|
30
|
+
*/
|
|
17
31
|
flush(): Promise<void>;
|
|
32
|
+
/**
|
|
33
|
+
* Spec §3.4: Flush EngageEvent[] with exponential backoff retry.
|
|
34
|
+
* Max 3 retries with 1s/2s/4s backoff. Drop batch after 3 failures.
|
|
35
|
+
* Do not retry on 401/422 (permanent failures).
|
|
36
|
+
*/
|
|
37
|
+
flushEngageEvents(): Promise<void>;
|
|
38
|
+
/**
|
|
39
|
+
* Spec §3.4: Use navigator.sendBeacon() to flush remaining events on page unload.
|
|
40
|
+
* This is a best-effort fire-and-forget — no retry possible.
|
|
41
|
+
*/
|
|
42
|
+
private flushBeacon;
|
|
18
43
|
}
|
package/dist/core/Transport.d.ts
CHANGED
|
@@ -1,7 +1,30 @@
|
|
|
1
|
-
import { AnalyticsEvent } from '../types';
|
|
1
|
+
import { AnalyticsEvent, EngageEvent } from '../types';
|
|
2
2
|
|
|
3
|
+
export interface TransportResult {
|
|
4
|
+
success: boolean;
|
|
5
|
+
permanent: boolean;
|
|
6
|
+
status?: number;
|
|
7
|
+
}
|
|
3
8
|
export declare class Transport {
|
|
4
9
|
private apiEndpoint;
|
|
10
|
+
private apiKey;
|
|
5
11
|
constructor(apiEndpoint: string);
|
|
12
|
+
setApiKey(key: string): void;
|
|
13
|
+
/**
|
|
14
|
+
* Sends legacy batched events (existing format).
|
|
15
|
+
* Used by the old EventQueue for backward compatibility.
|
|
16
|
+
*/
|
|
6
17
|
send(events: AnalyticsEvent[]): Promise<boolean>;
|
|
18
|
+
/**
|
|
19
|
+
* Sends new EngageEvent[] to the ingest endpoint (Spec §4.1).
|
|
20
|
+
* Returns detailed result for retry logic.
|
|
21
|
+
*/
|
|
22
|
+
sendEngageEvents(events: EngageEvent[]): Promise<TransportResult>;
|
|
23
|
+
private sendRaw;
|
|
24
|
+
/**
|
|
25
|
+
* Uses sendBeacon for last-resort page unload flush.
|
|
26
|
+
* Cannot send auth headers — the ingest endpoint must also accept
|
|
27
|
+
* the apiKey in the body payload as fallback.
|
|
28
|
+
*/
|
|
29
|
+
beaconFlush(payload: string): boolean;
|
|
7
30
|
}
|
package/dist/index.cjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";var t=Object.defineProperty,e=(e,n,i)=>((e,n,i)=>n in e?t(e,n,{enumerable:!0,configurable:!0,writable:!0,value:i}):e[n]=i)(e,"symbol"!=typeof n?n+"":n,i);Object.defineProperties(exports,{t:{value:!0},[Symbol.toStringTag]:{value:"Module"}});const n=require("react/jsx-runtime"),i=require("react");let s;const r=new Uint8Array(16);function o(){if(!s&&(s="undefined"!=typeof crypto&&crypto.getRandomValues&&crypto.getRandomValues.bind(crypto),!s))throw new Error("crypto.getRandomValues() not supported. See https://github.com/uuidjs/uuid#getrandomvalues-not-supported");return s(r)}const a=[];for(let S=0;S<256;++S)a.push((S+256).toString(16).slice(1));const c={randomUUID:"undefined"!=typeof crypto&&crypto.randomUUID&&crypto.randomUUID.bind(crypto)};function d(t,e,n){if(c.randomUUID&&!t)return c.randomUUID();const i=(t=t||{}).random||(t.rng||o)();return i[6]=15&i[6]|64,i[8]=63&i[8]|128,function(t,e=0){return a[t[e+0]]+a[t[e+1]]+a[t[e+2]]+a[t[e+3]]+"-"+a[t[e+4]]+a[t[e+5]]+"-"+a[t[e+6]]+a[t[e+7]]+"-"+a[t[e+8]]+a[t[e+9]]+"-"+a[t[e+10]]+a[t[e+11]]+a[t[e+12]]+a[t[e+13]]+a[t[e+14]]+a[t[e+15]]}(i)}const h=t=>{let e=2166136261;const n=t.length;for(let i=0;i<n;i++)e^=t.charCodeAt(i),e+=(e<<1)+(e<<4)+(e<<7)+(e<<8)+(e<<24);return("0000000"+(e>>>0).toString(16)).substr(-8)};class u{constructor(t="local"){e(this,"memoryStore",{}),e(this,"keyPrefix","engage_"),e(this,"domain"),this.type=t,this.domain=this.getCookieDomain()}getItem(t){const e=this.keyPrefix+t;if(this.memoryStore.hasOwnProperty(e))return this.memoryStore[e];try{if("local"===this.type&&this.isBrowser())return window.localStorage.getItem(e);if("cookie"===this.type&&this.isBrowser())return this.getCookie(e)}catch(n){return null}return null}setItem(t,e){const n=this.keyPrefix+t;this.memoryStore[n]=e;try{"local"===this.type&&this.isBrowser()?window.localStorage.setItem(n,e):"cookie"===this.type&&this.isBrowser()&&this.setCookie(n,e,365)}catch(i){this.isQuotaError(i)&&(this.type="memory")}}removeItem(t){const e=this.keyPrefix+t;delete this.memoryStore[e];try{"local"===this.type&&this.isBrowser()?window.localStorage.removeItem(e):"cookie"===this.type&&this.isBrowser()&&this.setCookie(e,"",-1)}catch(n){}}getCookie(t){const e=t+"=",n=document.cookie.split(";");for(let i=0;i<n.length;i++){let t=n[i];for(;" "===t.charAt(0);)t=t.substring(1,t.length);if(0===t.indexOf(e))return decodeURIComponent(t.substring(e.length,t.length))}return null}setCookie(t,e,n){let i="";if(n){const t=new Date;t.setTime(t.getTime()+24*n*60*60*1e3),i="; expires="+t.toUTCString()}document.cookie=`${t}=${encodeURIComponent(e)}${i}; path=/; domain=${this.domain}; SameSite=Lax; Secure`}getCookieDomain(){if(!this.isBrowser())return"";const t=window.location.hostname,e=t.split(".");return 1===e.length||"localhost"===t?"":e.length>2?"."+e.slice(-2).join("."):"."+t}isBrowser(){try{return"undefined"!=typeof window&&void 0!==window.document}catch(t){return!1}}isQuotaError(t){return t instanceof DOMException&&(22===t.code||1014===t.code||"QuotaExceededError"===t.name||"NS_ERROR_DOM_QUOTA_REACHED"===t.name)}}class l{constructor(t){e(this,"storage"),e(this,"SESSION_TIMEOUT",18e5),e(this,"deviceId"),e(this,"sessionId"),e(this,"userId",null),e(this,"currentUrl"),e(this,"referrer"),this.storage=new u(t.persistence),this.deviceId=this.getOrSetDeviceId(),this.sessionId="",this.manageSession(),"undefined"!=typeof window?(this.currentUrl=window.location.href,this.referrer=document.referrer,this.listenToHistory()):(this.currentUrl="",this.referrer="")}getOrSetDeviceId(){const t=this.storage.getItem("device_id");if(t)return t;const e=(()=>{if("undefined"==typeof window)return"server-side-id";const t=navigator,e=window.screen,n={userAgent:t.userAgent||"",screenRes:`${e.width}x${e.height}`,colorDepth:e.colorDepth||0,timezone:(new Date).getTimezoneOffset(),language:t.language||"en-US",platform:t.platform||"unknown",hardwareConcurrency:t.hardwareConcurrency||1,deviceMemory:t.deviceMemory||0},i=[n.platform,n.language,n.screenRes,n.colorDepth,n.timezone,n.hardwareConcurrency,n.deviceMemory].join("|");return`${h(i)}-${h(n.userAgent)}`})();return this.storage.setItem("device_id",e),e}manageSession(){const t=Date.now(),e=this.storage.getItem("session_id"),n=parseInt(this.storage.getItem("last_activity")||"0");if(!e||t-n>this.SESSION_TIMEOUT?(this.sessionId=`sess_${t}_${Math.random().toString(36).substr(2,9)}`,this.storage.setItem("session_id",this.sessionId)):this.sessionId=e,this.storage.setItem("last_activity",t.toString()),"undefined"!=typeof window){const t=()=>this.storage.setItem("last_activity",Date.now().toString());window.addEventListener("click",t),window.addEventListener("scroll",t)}}listenToHistory(){const t=history.pushState;history.pushState=(...e)=>{t.apply(history,e),this.handleUrlChange()},window.addEventListener("popstate",()=>this.handleUrlChange())}handleUrlChange(){const t=window.location.href;t!==this.currentUrl&&(this.referrer=this.currentUrl,this.currentUrl=t)}getPayload(){var t;return{library:{name:"@engagepro/analytics",version:"2.0.0"},user:{anonymousId:this.deviceId,id:this.userId},session:{id:this.sessionId,startTime:parseInt(this.sessionId.split("_")[1]||Date.now().toString())},page:{path:"undefined"!=typeof window?window.location.pathname:"",referrer:this.referrer,title:"undefined"!=typeof document?document.title:"",search:"undefined"!=typeof window?window.location.search:"",url:this.currentUrl},network:{online:"undefined"==typeof navigator||navigator.onLine,downlink:null==(t=navigator.connection)?void 0:t.downlink},screen:{width:"undefined"!=typeof screen?screen.width:0,height:"undefined"!=typeof screen?screen.height:0,density:"undefined"!=typeof window?window.devicePixelRatio:1},device:{fingerprint:this.deviceId,type:this.getDeviceType(),userAgent:"undefined"!=typeof navigator?navigator.userAgent:"server"},locale:"undefined"!=typeof navigator?navigator.language:"en-US",timezone:Intl.DateTimeFormat().resolvedOptions().timeZone}}getDeviceType(){if("undefined"==typeof navigator)return"desktop";const t=navigator.userAgent;return/(tablet|ipad|playbook|silk)|(android(?!.*mobi))/i.test(t)?"tablet":/Mobile|Android|iP(hone|od)|IEMobile|BlackBerry|Kindle|Silk-Accelerated/.test(t)?"mobile":"desktop"}}class f{constructor(t){e(this,"queue",[]),e(this,"storage"),e(this,"transport"),e(this,"STORAGE_KEY","engage_queue_v1"),e(this,"isFlushing",!1),e(this,"flushInterval"),e(this,"retryCount",0),this.transport=t,this.storage=new u("local"),this.load(),"undefined"!=typeof window&&(window.addEventListener("online",()=>{this.retryCount=0,this.flush()}),window.addEventListener("visibilitychange",()=>{"hidden"===document.visibilityState&&this.flush()})),this.startTimer()}startTimer(){this.flushInterval&&clearInterval(this.flushInterval);const t=3e3*Math.pow(2,this.retryCount);this.flushInterval=setInterval(()=>{this.flush()},Math.min(t,3e4))}enqueue(t){this.queue.push(t),this.save(),(this.queue.length>=10||"PURCHASE"===t.standardEvent)&&this.flush()}save(){this.storage.setItem(this.STORAGE_KEY,JSON.stringify(this.queue))}load(){const t=this.storage.getItem(this.STORAGE_KEY);if(t)try{this.queue=JSON.parse(t)}catch(e){this.queue=[]}}async flush(){if(0===this.queue.length||this.isFlushing)return;if("undefined"!=typeof navigator&&!navigator.onLine)return;this.isFlushing=!0;const t=[...this.queue];this.queue=[],this.save();try{if(!(await this.transport.send(t)))throw new Error("Server rejected batch");this.retryCount=0,this.startTimer()}catch(e){this.queue=[...t,...this.queue],this.save(),this.retryCount++,this.startTimer()}finally{this.isFlushing=!1}}}class p{constructor(t){this.apiEndpoint=t}async send(t){const e=JSON.stringify({batch:t,sentAt:(new Date).toISOString()});if("undefined"!=typeof navigator&&navigator.sendBeacon&&e.length<6e4){const t=new Blob([e],{type:"application/json"});if(navigator.sendBeacon(this.apiEndpoint,t))return!0}try{return(await fetch(this.apiEndpoint,{method:"POST",headers:{"Content-Type":"application/json"},body:e,keepalive:!0})).ok}catch(n){return!1}}}const w="https://engage-api.bluenath.com/api/v1/tracking/ingest",y=new Set(["localhost","127.0.0.1"]),v={$:"USD",US$:"USD",USD:"USD","₹":"INR",RS:"INR","RS.":"INR",INR:"INR","€":"EUR",EUR:"EUR","£":"GBP",GBP:"GBP","¥":"JPY",JPY:"JPY"},m=t=>{if("string"!=typeof t)return;const e=t.trim().toUpperCase();return e?v[e]?v[e]:e.includes("₹")||e.startsWith("RS")?"INR":e.includes("$")&&!e.includes("CAD")?"USD":e.includes("€")?"EUR":e.includes("£")?"GBP":e.includes("¥")?"JPY":/^[A-Z]{3}$/.test(e)?e:void 0:void 0};class g{constructor(t){e(this,"config"),e(this,"context"),e(this,"queue"),e(this,"transport"),e(this,"identify",(t,e)=>{this.context.userId=t,this.processEvent({event:"Identify",properties:{traits:e},standardEvent:"LOGIN",intent:"identity",confidence:1})}),e(this,"page",(t,e)=>{const n=this.context.getPayload().page;this.processEvent({event:t||n.title||"Unknown Page",properties:{path:n.path,referrer:n.referrer,title:n.title,...e},standardEvent:"PAGE_VIEW",intent:"navigation",confidence:1})}),e(this,"track",(t,e,n)=>{this.processEvent({event:t,properties:e||{},...n})}),e(this,"trackRevenue",(t,e,n)=>{const i=Math.max(0,(t=>{if("number"==typeof t)return Number.isFinite(t)?t:0;const e=t.trim().replace(/[^0-9.+-]/g,""),n=Number(e);return Number.isFinite(n)?n:0})(t)),s=m(null==e?void 0:e.currency)||m(null==e?void 0:e.currencyCode)||m(null==e?void 0:e.currencySymbol)||("string"==typeof t?m(t):void 0)||"USD";this.track("PURCHASE",{...e||{},value:i,amount:i,valuePaise:Math.round(100*i),amountPaise:Math.round(100*i),currency:s,currencyCode:s},{standardEvent:"PURCHASE",intent:"commerce",confidence:1,...n})}),this.config=t,this.transport=new p((t=>{if(!t)return w;try{const e=new URL(t);return e.hostname.endsWith("bluenath.com")||y.has(e.hostname)?(e.pathname="/api/v1/tracking/ingest",e.search="",e.hash="",e.toString()):w}catch{return w}})(t.apiHost)),this.context=new l({persistence:t.tracking.useCookies?"cookie":"local"}),this.queue=new f(this.transport)}parseRef(t){if(!t)return{};const e=t.match(/^camp_([^_]+)_cr_([^_]+)$/);return e?{campaignId:e[1],creatorId:e[2]}:{}}processEvent(t){const e=this.context.getPayload(),n=t.properties.ref,i=("string"==typeof n?n:void 0)||new URLSearchParams(e.page.search||"").get("ref")||void 0,s=this.parseRef(i),r=t.properties.value??t.properties.amount??t.properties.valuePaise??t.properties.amountPaise,o=Number(r),a=t.properties.campaignId,c=t.properties.creatorId,h=t.properties.currency,u={event:t.event,properties:t.properties,standardEvent:t.standardEvent,intent:t.intent,confidence:t.confidence,rawLabel:t.rawLabel,timestamp:(new Date).toISOString(),messageId:d(),writeKey:this.config.writeKey,ref:i,campaignId:("string"==typeof a?a:void 0)||s.campaignId,creatorId:("string"==typeof c?c:void 0)||s.creatorId,value:Number.isFinite(o)?Math.round(o):void 0,valuePaise:Number.isFinite(o)?Math.round(100*o):void 0,currency:("string"==typeof h?h:void 0)||"USD",userId:e.user.id||void 0,anonymousId:e.user.anonymousId,context:e};this.config.debug,this.queue.enqueue(u)}}class b{constructor(t){e(this,"analytics"),e(this,"metrics",{}),this.analytics=t,"undefined"!=typeof window&&"PerformanceObserver"in window&&this.observe()}observe(){try{new PerformanceObserver(t=>{for(const e of t.getEntries()){const t=e;t.hadRecentInput||(this.metrics.cls=(this.metrics.cls||0)+t.value)}}).observe({type:"layout-shift",buffered:!0}),new PerformanceObserver(t=>{const e=t.getEntries(),n=e[e.length-1];this.metrics.lcp=n.renderTime||n.loadTime,this.logMetric("LCP",this.metrics.lcp)}).observe({type:"largest-contentful-paint",buffered:!0}),new PerformanceObserver(t=>{const e=t.getEntries()[0];e&&(this.metrics.fid=e.processingStart-e.startTime,this.logMetric("FID",this.metrics.fid))}).observe({type:"first-input",buffered:!0}),window.addEventListener("visibilitychange",()=>{"hidden"===document.visibilityState&&this.metrics.cls&&this.logMetric("CLS",this.metrics.cls)})}catch(t){}}logMetric(t,e){e<0||this.analytics.track(`Core Web Vital: ${t}`,{metric:t,value:Math.round(e)},{standardEvent:"PERFORMANCE",intent:"performance",confidence:1,rawLabel:`${t}: ${Math.round(e)}ms`})}}const E=i.createContext(null);let R=null;exports.EngageProProvider=({children:t,...e})=>{const s=i.useRef(null),r=i.useRef(null);s.current||(s.current=new g(e),"undefined"!=typeof window&&new b(s.current));const o=s.current;return i.useEffect(()=>{if(!e.tracking.autoTrack)return;const t=t=>{const e=t.target.closest('button, a, input[type="submit"], [data-track], .clickable');if(e){const t=Date.now(),n=r.current;n&&n.el===e&&t-n.ts<500?(n.count++,n.ts=t,3===n.count&&(o.track("Rage Click detected",{element:e.tagName},{standardEvent:"RAGE_CLICK",intent:"frustration",confidence:1}),r.current=null)):r.current={el:e,count:1,ts:t};const i=(t=>{let e=(t.innerText||t.value||t.getAttribute("aria-label")||"").trim();if(!e){const n=t.querySelector("img");n&&n.alt&&(e=n.alt);const i=t.querySelector("title");i&&(e=i.textContent||"")}const n=e.slice(0,100),i=n.toLowerCase(),s=(t.id||"").toLowerCase(),r=t.href||"";return/add to (cart|bag)|buy now/.test(i)||s.includes("add-to-cart")?{standard:"ADD_TO_CART",intent:"commerce",label:n,confidence:.9}:/checkout|proceed/.test(i)||r.includes("/checkout")?{standard:"INITIATE_CHECKOUT",intent:"commerce",label:n,confidence:.9}:/place order|pay now/.test(i)||s.includes("place-order")?{standard:"PURCHASE",intent:"commerce",label:n,confidence:.95}:/cancel order/.test(i)||s.includes("cancel")?{standard:"ORDER_CANCEL",intent:"lifecycle",label:n,confidence:.85}:/refund|return/.test(i)||s.includes("refund")?{standard:"ORDER_REFUND",intent:"lifecycle",label:n,confidence:.85}:/track package|shipping/.test(i)?{standard:"TRACK_PACKAGE",intent:"lifecycle",label:n,confidence:.8}:/write review/.test(i)||i.includes("star")&&/^[1-5]/.test(i)?{standard:"RATE_PRODUCT",intent:"engagement",label:n,confidence:.8}:i.includes("search")||s.includes("search")?{standard:"SEARCH",intent:"search",label:n,confidence:.7}:{standard:"GENERIC",intent:"interaction",label:n,confidence:.5}})(e),s="A"===e.tagName;o.track("Interaction",{element:e.tagName.toLowerCase(),id:e.id,destination:s?e.href:void 0},{standardEvent:i.standard,intent:i.intent,rawLabel:i.label,confidence:i.confidence})}},n=t=>{const e=t.target;(t=>"password"===t.getAttribute("type")||"hidden"===t.getAttribute("type")||/password|cvc|card|cc-num|ssn|credit|hidden/i.test(t.getAttribute("name")||t.id||""))(e)||"focusin"!==t.type||e.dataset.tracked||(e.dataset.tracked="true",o.track("Form Start",{field:e.name||e.id},{standardEvent:"FORM_START",intent:"identity"}))},i=()=>{const t=new URLSearchParams(window.location.search);t.has("q")&&o.track("Search Query",{query:t.get("q")},{standardEvent:"SEARCH",intent:"search"}),requestAnimationFrame(()=>o.page())},s=history.pushState;return history.pushState=(...t)=>{s.apply(history,t),i()},window.addEventListener("popstate",i),window.addEventListener("click",t,!0),window.addEventListener("focusin",n,!0),i(),()=>{history.pushState=s,window.removeEventListener("popstate",i),window.removeEventListener("click",t,!0),window.removeEventListener("focusin",n,!0)}},[e.tracking.autoTrack]),n.jsx(E.Provider,{value:o,children:t})},exports.default=g,exports.init=t=>(R||(R=new g(t)),R),exports.useAnalytics=()=>{const t=i.useContext(E);if(!t)throw new Error("useAnalytics must be used within EngageProProvider");return t};
|
|
1
|
+
"use strict";var e=Object.defineProperty,i=(i,t,a)=>((i,t,a)=>t in i?e(i,t,{enumerable:!0,configurable:!0,writable:!0,value:a}):i[t]=a)(i,"symbol"!=typeof t?t+"":t,a);Object.defineProperties(exports,{i:{value:!0},[Symbol.toStringTag]:{value:"Module"}});const t=require("react/jsx-runtime"),a=require("react");let n;const r=new Uint8Array(16);function s(){if(!n&&(n="undefined"!=typeof crypto&&crypto.getRandomValues&&crypto.getRandomValues.bind(crypto),!n))throw new Error("crypto.getRandomValues() not supported. See https://github.com/uuidjs/uuid#getrandomvalues-not-supported");return n(r)}const o=[];for(let _=0;_<256;++_)o.push((_+256).toString(16).slice(1));const c={randomUUID:"undefined"!=typeof crypto&&crypto.randomUUID&&crypto.randomUUID.bind(crypto)};function u(e,i,t){if(c.randomUUID&&!e)return c.randomUUID();const a=(e=e||{}).random||(e.rng||s)();return a[6]=15&a[6]|64,a[8]=63&a[8]|128,function(e,i=0){return o[e[i+0]]+o[e[i+1]]+o[e[i+2]]+o[e[i+3]]+"-"+o[e[i+4]]+o[e[i+5]]+"-"+o[e[i+6]]+o[e[i+7]]+"-"+o[e[i+8]]+o[e[i+9]]+"-"+o[e[i+10]]+o[e[i+11]]+o[e[i+12]]+o[e[i+13]]+o[e[i+14]]+o[e[i+15]]}(a)}const d=e=>{let i=2166136261;const t=e.length;for(let a=0;a<t;a++)i^=e.charCodeAt(a),i+=(i<<1)+(i<<4)+(i<<7)+(i<<8)+(i<<24);return("0000000"+(i>>>0).toString(16)).substr(-8)};class h{constructor(e="local"){i(this,"memoryStore",{}),i(this,"keyPrefix","engage_"),i(this,"domain"),this.type=e,this.domain=this.getCookieDomain()}getItem(e){const i=this.keyPrefix+e;if(this.memoryStore.hasOwnProperty(i))return this.memoryStore[i];try{if("local"===this.type&&this.isBrowser())return window.localStorage.getItem(i);if("cookie"===this.type&&this.isBrowser())return this.getCookie(i)}catch(t){return null}return null}setItem(e,i){const t=this.keyPrefix+e;this.memoryStore[t]=i;try{"local"===this.type&&this.isBrowser()?window.localStorage.setItem(t,i):"cookie"===this.type&&this.isBrowser()&&this.setCookie(t,i,365)}catch(a){this.isQuotaError(a)&&(this.type="memory")}}removeItem(e){const i=this.keyPrefix+e;delete this.memoryStore[i];try{"local"===this.type&&this.isBrowser()?window.localStorage.removeItem(i):"cookie"===this.type&&this.isBrowser()&&this.setCookie(i,"",-1)}catch(t){}}getCookie(e){const i=e+"=",t=document.cookie.split(";");for(let a=0;a<t.length;a++){let e=t[a];for(;" "===e.charAt(0);)e=e.substring(1,e.length);if(0===e.indexOf(i))return decodeURIComponent(e.substring(i.length,e.length))}return null}setCookie(e,i,t){let a="";if(t){const e=new Date;e.setTime(e.getTime()+24*t*60*60*1e3),a="; expires="+e.toUTCString()}document.cookie=`${e}=${encodeURIComponent(i)}${a}; path=/; domain=${this.domain}; SameSite=Lax; Secure`}getCookieDomain(){if(!this.isBrowser())return"";const e=window.location.hostname,i=e.split(".");return 1===i.length||"localhost"===e?"":i.length>2?"."+i.slice(-2).join("."):"."+e}isBrowser(){try{return"undefined"!=typeof window&&void 0!==window.document}catch(e){return!1}}isQuotaError(e){return e instanceof DOMException&&(22===e.code||1014===e.code||"QuotaExceededError"===e.name||"NS_ERROR_DOM_QUOTA_REACHED"===e.name)}}class l{constructor(e){i(this,"storage"),i(this,"SESSION_TIMEOUT",18e5),i(this,"deviceId"),i(this,"sessionId"),i(this,"userId",null),i(this,"currentUrl"),i(this,"referrer"),this.storage=new h(e.persistence),this.deviceId=this.getOrSetDeviceId(),this.sessionId="",this.manageSession(),"undefined"!=typeof window?(this.currentUrl=window.location.href,this.referrer=document.referrer,this.listenToHistory()):(this.currentUrl="",this.referrer="")}getOrSetDeviceId(){const e=this.storage.getItem("device_id");if(e)return e;const i=(()=>{if("undefined"==typeof window)return"server-side-id";const e=navigator,i=window.screen,t={userAgent:e.userAgent||"",screenRes:`${i.width}x${i.height}`,colorDepth:i.colorDepth||0,timezone:(new Date).getTimezoneOffset(),language:e.language||"en-US",platform:e.platform||"unknown",hardwareConcurrency:e.hardwareConcurrency||1,deviceMemory:e.deviceMemory||0},a=[t.platform,t.language,t.screenRes,t.colorDepth,t.timezone,t.hardwareConcurrency,t.deviceMemory].join("|");return`${d(a)}-${d(t.userAgent)}`})();return this.storage.setItem("device_id",i),i}manageSession(){const e=Date.now(),i=this.storage.getItem("session_id"),t=parseInt(this.storage.getItem("last_activity")||"0");if(!i||e-t>this.SESSION_TIMEOUT?(this.sessionId=`sess_${e}_${Math.random().toString(36).substr(2,9)}`,this.storage.setItem("session_id",this.sessionId)):this.sessionId=i,this.storage.setItem("last_activity",e.toString()),"undefined"!=typeof window){const e=()=>this.storage.setItem("last_activity",Date.now().toString());window.addEventListener("click",e),window.addEventListener("scroll",e)}}listenToHistory(){const e=history.pushState;history.pushState=(...i)=>{e.apply(history,i),this.handleUrlChange()},window.addEventListener("popstate",()=>this.handleUrlChange())}handleUrlChange(){const e=window.location.href;e!==this.currentUrl&&(this.referrer=this.currentUrl,this.currentUrl=e)}getPayload(){var e;return{library:{name:"@engagepro/analytics",version:"2.0.0"},user:{anonymousId:this.deviceId,id:this.userId},session:{id:this.sessionId,startTime:parseInt(this.sessionId.split("_")[1]||Date.now().toString())},page:{path:"undefined"!=typeof window?window.location.pathname:"",referrer:this.referrer,title:"undefined"!=typeof document?document.title:"",search:"undefined"!=typeof window?window.location.search:"",url:this.currentUrl},network:{online:"undefined"==typeof navigator||navigator.onLine,downlink:null==(e=navigator.connection)?void 0:e.downlink},screen:{width:"undefined"!=typeof screen?screen.width:0,height:"undefined"!=typeof screen?screen.height:0,density:"undefined"!=typeof window?window.devicePixelRatio:1},device:{fingerprint:this.deviceId,type:this.getDeviceType(),userAgent:"undefined"!=typeof navigator?navigator.userAgent:"server"},locale:"undefined"!=typeof navigator?navigator.language:"en-US",timezone:Intl.DateTimeFormat().resolvedOptions().timeZone}}getDeviceType(){if("undefined"==typeof navigator)return"desktop";const e=navigator.userAgent;return/(tablet|ipad|playbook|silk)|(android(?!.*mobi))/i.test(e)?"tablet":/Mobile|Android|iP(hone|od)|IEMobile|BlackBerry|Kindle|Silk-Accelerated/.test(e)?"mobile":"desktop"}}class A{constructor(e){i(this,"queue",[]),i(this,"engageQueue",[]),i(this,"storage"),i(this,"transport"),i(this,"STORAGE_KEY","engage_queue_v1"),i(this,"ENGAGE_STORAGE_KEY","engage_events_v2"),i(this,"isFlushing",!1),i(this,"flushInterval"),i(this,"retryCount",0),i(this,"MAX_RETRIES",3),this.transport=e,this.storage=new h("local"),this.load(),"undefined"!=typeof window&&(window.addEventListener("online",()=>{this.retryCount=0,this.flush()}),window.addEventListener("visibilitychange",()=>{"hidden"===document.visibilityState&&this.flushBeacon()}),window.addEventListener("beforeunload",()=>{this.flushBeacon()})),this.startTimer()}startTimer(){this.flushInterval&&clearInterval(this.flushInterval),this.flushInterval=setInterval(()=>{this.flush()},5e3)}enqueue(e){this.queue.push(e),this.save(),(this.queue.length>=10||"PURCHASE"===e.standardEvent)&&this.flush()}enqueueEngageEvent(e){this.engageQueue.push(e),this.saveEngage(),this.engageQueue.length>=10&&this.flushEngageEvents()}save(){this.storage.setItem(this.STORAGE_KEY,JSON.stringify(this.queue))}saveEngage(){this.storage.setItem(this.ENGAGE_STORAGE_KEY,JSON.stringify(this.engageQueue))}load(){const e=this.storage.getItem(this.STORAGE_KEY);if(e)try{this.queue=JSON.parse(e)}catch(t){this.queue=[]}const i=this.storage.getItem(this.ENGAGE_STORAGE_KEY);if(i)try{this.engageQueue=JSON.parse(i)}catch(t){this.engageQueue=[]}}async flush(){if(0===this.queue.length||this.isFlushing)return void(this.engageQueue.length>0&&!this.isFlushing&&await this.flushEngageEvents());if("undefined"!=typeof navigator&&!navigator.onLine)return;this.isFlushing=!0;const e=[...this.queue];this.queue=[],this.save();try{if(!(await this.transport.send(e)))throw new Error("Server rejected batch");this.retryCount=0}catch(i){this.queue=[...e,...this.queue],this.save(),this.retryCount++}finally{this.isFlushing=!1}this.engageQueue.length>0&&await this.flushEngageEvents()}async flushEngageEvents(){if(0===this.engageQueue.length||this.isFlushing)return;if("undefined"!=typeof navigator&&!navigator.onLine)return;this.isFlushing=!0;const e=[...this.engageQueue];this.engageQueue=[],this.saveEngage();let i=0,t=!1;for(;i<this.MAX_RETRIES&&!t;){const a=await this.transport.sendEngageEvents(e);if(a.success){t=!0;break}if(a.permanent)return void(this.isFlushing=!1);if(i++,i<this.MAX_RETRIES){const e=1e3*Math.pow(2,i-1);await new Promise(i=>setTimeout(i,e))}}this.isFlushing=!1}flushBeacon(){if(this.queue.length>0){const e=JSON.stringify({batch:this.queue,sentAt:(new Date).toISOString()});this.transport.beaconFlush(e)&&(this.queue=[],this.save())}if(this.engageQueue.length>0){const e=JSON.stringify({events:this.engageQueue});this.transport.beaconFlush(e)&&(this.engageQueue=[],this.saveEngage())}}}class f{constructor(e){i(this,"apiKey",""),this.apiEndpoint=e}setApiKey(e){this.apiKey=e}async send(e){return(await this.sendRaw(JSON.stringify({batch:e,sentAt:(new Date).toISOString()}))).success}async sendEngageEvents(e){const i=JSON.stringify({events:e});return this.sendRaw(i)}async sendRaw(e){const i={"Content-Type":"application/json"};if(this.apiKey&&(i.Authorization=`Bearer ${this.apiKey}`),!this.apiKey&&"undefined"!=typeof navigator&&navigator.sendBeacon&&e.length<6e4){const i=new Blob([e],{type:"application/json"});if(navigator.sendBeacon(this.apiEndpoint,i))return{success:!0,permanent:!1}}try{const t=await fetch(this.apiEndpoint,{method:"POST",headers:i,body:e,keepalive:!0});return 401===t.status||422===t.status?{success:!1,permanent:!0,status:t.status}:{success:t.ok||202===t.status,permanent:!1,status:t.status}}catch(t){return{success:!1,permanent:!1}}}beaconFlush(e){if("undefined"==typeof navigator||!navigator.sendBeacon)return!1;const i=new Blob([e],{type:"application/json"});return navigator.sendBeacon(this.apiEndpoint,i)}}const p={"Asia/Kolkata":"IN","Asia/Calcutta":"IN","Asia/Mumbai":"IN","Asia/Dhaka":"BD","Asia/Kathmandu":"NP","Asia/Colombo":"LK","Asia/Karachi":"PK","Asia/Kabul":"AF","Asia/Tehran":"IR","Asia/Dubai":"AE","Asia/Muscat":"OM","Asia/Bahrain":"BH","Asia/Qatar":"QA","Asia/Kuwait":"KW","Asia/Riyadh":"SA","Asia/Aden":"YE","Asia/Baghdad":"IQ","Asia/Amman":"JO","Asia/Beirut":"LB","Asia/Damascus":"SY","Asia/Jerusalem":"IL","Asia/Tel_Aviv":"IL","Asia/Nicosia":"CY","Asia/Tokyo":"JP","Asia/Seoul":"KR","Asia/Pyongyang":"KP","Asia/Shanghai":"CN","Asia/Chongqing":"CN","Asia/Harbin":"CN","Asia/Urumqi":"CN","Asia/Hong_Kong":"HK","Asia/Macau":"MO","Asia/Taipei":"TW","Asia/Singapore":"SG","Asia/Kuala_Lumpur":"MY","Asia/Brunei":"BN","Asia/Jakarta":"ID","Asia/Makassar":"ID","Asia/Jayapura":"ID","Asia/Bangkok":"TH","Asia/Ho_Chi_Minh":"VN","Asia/Saigon":"VN","Asia/Phnom_Penh":"KH","Asia/Vientiane":"LA","Asia/Yangon":"MM","Asia/Rangoon":"MM","Asia/Manila":"PH","Asia/Ulaanbaatar":"MN","Asia/Hovd":"MN","Asia/Tbilisi":"GE","Asia/Baku":"AZ","Asia/Yerevan":"AM","Asia/Almaty":"KZ","Asia/Bishkek":"KG","Asia/Tashkent":"UZ","Asia/Ashgabat":"TM","Asia/Dushanbe":"TJ","Asia/Thimphu":"BT","Asia/Dili":"TL","Europe/London":"GB","Europe/Dublin":"IE","Europe/Lisbon":"PT","Europe/Madrid":"ES","Europe/Paris":"FR","Europe/Brussels":"BE","Europe/Amsterdam":"NL","Europe/Luxembourg":"LU","Europe/Berlin":"DE","Europe/Zurich":"CH","Europe/Vienna":"AT","Europe/Rome":"IT","Europe/Monaco":"MC","Europe/Vatican":"VA","Europe/Malta":"MT","Europe/Prague":"CZ","Europe/Budapest":"HU","Europe/Warsaw":"PL","Europe/Bratislava":"SK","Europe/Ljubljana":"SI","Europe/Zagreb":"HR","Europe/Belgrade":"RS","Europe/Sarajevo":"BA","Europe/Podgorica":"ME","Europe/Skopje":"MK","Europe/Tirane":"AL","Europe/Sofia":"BG","Europe/Bucharest":"RO","Europe/Chisinau":"MD","Europe/Athens":"GR","Europe/Istanbul":"TR","Europe/Helsinki":"FI","Europe/Stockholm":"SE","Europe/Oslo":"NO","Europe/Copenhagen":"DK","Europe/Tallinn":"EE","Europe/Riga":"LV","Europe/Vilnius":"LT","Europe/Minsk":"BY","Europe/Moscow":"RU","Europe/Kaliningrad":"RU","Europe/Samara":"RU","Europe/Kiev":"UA","Europe/Kyiv":"UA","Europe/Reykjavik":"IS","Europe/Andorra":"AD","Europe/Gibraltar":"GI","Europe/San_Marino":"SM","America/New_York":"US","America/Chicago":"US","America/Denver":"US","America/Los_Angeles":"US","America/Phoenix":"US","America/Anchorage":"US","Pacific/Honolulu":"US","America/Detroit":"US","America/Indianapolis":"US","America/Boise":"US","America/Juneau":"US","America/Adak":"US","America/Toronto":"CA","America/Vancouver":"CA","America/Montreal":"CA","America/Winnipeg":"CA","America/Edmonton":"CA","America/Halifax":"CA","America/St_Johns":"CA","America/Regina":"CA","America/Mexico_City":"MX","America/Cancun":"MX","America/Tijuana":"MX","America/Monterrey":"MX","America/Hermosillo":"MX","America/Guatemala":"GT","America/Belize":"BZ","America/El_Salvador":"SV","America/Tegucigalpa":"HN","America/Managua":"NI","America/Costa_Rica":"CR","America/Panama":"PA","America/Bogota":"CO","America/Lima":"PE","America/Guayaquil":"EC","America/Caracas":"VE","America/La_Paz":"BO","America/Asuncion":"PY","America/Montevideo":"UY","America/Buenos_Aires":"AR","America/Argentina/Buenos_Aires":"AR","America/Santiago":"CL","America/Sao_Paulo":"BR","America/Recife":"BR","America/Manaus":"BR","America/Fortaleza":"BR","America/Bahia":"BR","America/Havana":"CU","America/Jamaica":"JM","America/Port-au-Prince":"HT","America/Santo_Domingo":"DO","America/Puerto_Rico":"PR","America/Port_of_Spain":"TT","America/Barbados":"BB","America/Martinique":"MQ","America/Guyana":"GY","America/Paramaribo":"SR","America/Cayenne":"GF","America/Curacao":"CW","Africa/Cairo":"EG","Africa/Casablanca":"MA","Africa/Tunis":"TN","Africa/Algiers":"DZ","Africa/Tripoli":"LY","Africa/Khartoum":"SD","Africa/Addis_Ababa":"ET","Africa/Nairobi":"KE","Africa/Dar_es_Salaam":"TZ","Africa/Kampala":"UG","Africa/Mogadishu":"SO","Africa/Lagos":"NG","Africa/Accra":"GH","Africa/Abidjan":"CI","Africa/Dakar":"SN","Africa/Bamako":"ML","Africa/Ouagadougou":"BF","Africa/Conakry":"GN","Africa/Freetown":"SL","Africa/Monrovia":"LR","Africa/Lome":"TG","Africa/Porto-Novo":"BJ","Africa/Niamey":"NE","Africa/Douala":"CM","Africa/Libreville":"GA","Africa/Bangui":"CF","Africa/Brazzaville":"CG","Africa/Kinshasa":"CD","Africa/Lubumbashi":"CD","Africa/Luanda":"AO","Africa/Maputo":"MZ","Africa/Harare":"ZW","Africa/Lusaka":"ZM","Africa/Lilongwe":"MW","Africa/Johannesburg":"ZA","Africa/Windhoek":"NA","Africa/Gaborone":"BW","Africa/Maseru":"LS","Africa/Mbabane":"SZ","Indian/Antananarivo":"MG","Indian/Mauritius":"MU","Indian/Reunion":"RE","Indian/Comoro":"KM","Indian/Mayotte":"YT","Africa/Djibouti":"DJ","Africa/Asmara":"ER","Australia/Sydney":"AU","Australia/Melbourne":"AU","Australia/Brisbane":"AU","Australia/Perth":"AU","Australia/Adelaide":"AU","Australia/Hobart":"AU","Australia/Darwin":"AU","Australia/Lord_Howe":"AU","Pacific/Auckland":"NZ","Pacific/Chatham":"NZ","Pacific/Fiji":"FJ","Pacific/Tongatapu":"TO","Pacific/Apia":"WS","Pacific/Port_Moresby":"PG","Pacific/Noumea":"NC","Pacific/Guam":"GU","Pacific/Pago_Pago":"AS","Pacific/Tahiti":"PF","Atlantic/Reykjavik":"IS","Atlantic/Azores":"PT","Atlantic/Canary":"ES","Atlantic/Madeira":"PT","Atlantic/Cape_Verde":"CV","Atlantic/Bermuda":"BM","Indian/Maldives":"MV","Indian/Chagos":"IO","Indian/Christmas":"CX","Indian/Cocos":"CC"},m=new Set(["localhost","127.0.0.1"]),g="engage_session_id",y={$:"USD",US$:"USD",USD:"USD","₹":"INR",RS:"INR","RS.":"INR",INR:"INR","€":"EUR",EUR:"EUR","£":"GBP",GBP:"GBP","¥":"JPY",JPY:"JPY"},v=e=>{if("string"!=typeof e)return;const i=e.trim().toUpperCase();return i?y[i]?y[i]:i.includes("₹")||i.startsWith("RS")?"INR":i.includes("$")&&!i.includes("CAD")?"USD":i.includes("€")?"EUR":i.includes("£")?"GBP":i.includes("¥")?"JPY":/^[A-Z]{3}$/.test(i)?i:void 0:void 0};function w(e){if(!e||"object"!=typeof e)return;const i={},t=Object.keys(e).slice(0,20);for(const a of t){const t=e[a];"boolean"==typeof t||"number"==typeof t?i[a]=t:"string"==typeof t&&(i[a]=t.slice(0,100))}return Object.keys(i).length>0?i:void 0}class E{constructor(e){i(this,"config"),i(this,"context"),i(this,"queue"),i(this,"transport"),i(this,"sessionId"),i(this,"countryCode"),i(this,"currentSentiment"),i(this,"identify",(e,i)=>{this.context.userId=e,this.processEvent({event:"Identify",properties:{traits:i},standardEvent:"LOGIN",intent:"identity",confidence:1})}),i(this,"page",(e,i)=>{const t=this.context.getPayload().page;this.processEvent({event:e||t.title||"Unknown Page",properties:{path:t.path,referrer:t.referrer,title:t.title,...i},standardEvent:"PAGE_VIEW",intent:"navigation",confidence:1}),this.emitEngageEvent("pageview",i)}),i(this,"track",(e,i,t)=>{this.processEvent({event:e,properties:i||{},...t});const a={purchase:"purchase",PURCHASE:"purchase",signup:"signup",SIGNUP:"signup",register:"signup",REGISTER:"signup",add_to_cart:"add_to_cart",ADD_TO_CART:"add_to_cart",product_view:"product_view",VIEW_CONTENT:"product_view",session_start:"session_start",session_end:"session_end"},n=a[e]||a[(null==t?void 0:t.standardEvent)||""]||"custom";this.emitEngageEvent(n,i)}),i(this,"trackRevenue",(e,i,t)=>{const a=Math.max(0,(e=>{if("number"==typeof e)return Number.isFinite(e)?e:0;const i=e.trim().replace(/[^0-9.+-]/g,""),t=Number(i);return Number.isFinite(t)?t:0})(e)),n=v(null==i?void 0:i.currency)||v(null==i?void 0:i.currencyCode)||v(null==i?void 0:i.currencySymbol)||("string"==typeof e?v(e):void 0)||"USD";this.track("PURCHASE",{...i||{},value:a,amount:a,valuePaise:Math.round(100*a),amountPaise:Math.round(100*a),currency:n,currencyCode:n},{standardEvent:"PURCHASE",intent:"commerce",confidence:1,...t})}),i(this,"setSentiment",e=>{this.currentSentiment=e}),this.config=e;const t=e.apiKey||e.writeKey,a=((e,i,t)=>{if(!e)return t;try{const i=new URL(e);return i.hostname.endsWith("bluenath.com")||m.has(i.hostname)?(i.pathname="/api/v1/tracking/ingest",i.search="",i.hash="",i.toString()):t}catch{return t}})(e.apiHost,0,"https://engage-api.bluenath.com/api/v1/tracking/ingest");this.transport=new f(a),this.transport.setApiKey(t),this.context=new l({persistence:e.tracking.useCookies?"cookie":"local"}),this.queue=new A(this.transport),this.sessionId=function(){if("undefined"==typeof sessionStorage)return u();let e=sessionStorage.getItem(g);return e||(e=u(),sessionStorage.setItem(g,e)),e}(),this.countryCode=function(){try{const e=Intl.DateTimeFormat().resolvedOptions().timeZone;if(!e)return;return p[e]}catch{return}}(),e.tracking.autoTrack&&"undefined"!=typeof window&&this.page()}emitEngageEvent(e,i){const t=this.context.getPayload().page,a={type:e,timestamp:(new Date).toISOString(),sessionId:this.sessionId,userId:this.context.getPayload().user.id||void 0,countryCode:this.countryCode,sentiment:this.currentSentiment,referrer:(t.referrer||"").slice(0,500),path:t.path||("undefined"!=typeof window?window.location.pathname:"/"),currency:v(null==i?void 0:i.currency)||v(null==i?void 0:i.currencyCode)||void 0,amount:"number"==typeof(null==i?void 0:i.amountPaise)?i.amountPaise:"number"==typeof(null==i?void 0:i.amount)?Math.round(100*i.amount):void 0,productId:"string"==typeof(null==i?void 0:i.productId)?i.productId:void 0,productName:"string"==typeof(null==i?void 0:i.productName)?i.productName:void 0,productCategory:"string"==typeof(null==i?void 0:i.productCategory)?i.productCategory:void 0,metadata:w(null==i?void 0:i.metadata)};this.queue.enqueueEngageEvent(a),this.currentSentiment&&(this.currentSentiment=void 0)}parseRef(e){if(!e)return{};const i=e.match(/^camp_([^_]+)_cr_([^_]+)$/);return i?{campaignId:i[1],creatorId:i[2]}:{}}processEvent(e){const i=this.context.getPayload(),t=e.properties.ref,a=("string"==typeof t?t:void 0)||new URLSearchParams(i.page.search||"").get("ref")||void 0,n=this.parseRef(a),r=e.properties.value??e.properties.amount??e.properties.valuePaise??e.properties.amountPaise,s=Number(r),o=e.properties.campaignId,c=e.properties.creatorId,d=e.properties.currency,h={event:e.event,properties:e.properties,standardEvent:e.standardEvent,intent:e.intent,confidence:e.confidence,rawLabel:e.rawLabel,timestamp:(new Date).toISOString(),messageId:u(),writeKey:this.config.writeKey||this.config.apiKey||"",ref:a,campaignId:("string"==typeof o?o:void 0)||n.campaignId,creatorId:("string"==typeof c?c:void 0)||n.creatorId,value:Number.isFinite(s)?Math.round(s):void 0,valuePaise:Number.isFinite(s)?Math.round(100*s):void 0,currency:("string"==typeof d?d:void 0)||"USD",userId:i.user.id||void 0,anonymousId:i.user.anonymousId,context:i};this.config.debug,this.queue.enqueue(h)}}class S{constructor(e){i(this,"analytics"),i(this,"metrics",{}),this.analytics=e,"undefined"!=typeof window&&"PerformanceObserver"in window&&this.observe()}observe(){try{new PerformanceObserver(e=>{for(const i of e.getEntries()){const e=i;e.hadRecentInput||(this.metrics.cls=(this.metrics.cls||0)+e.value)}}).observe({type:"layout-shift",buffered:!0}),new PerformanceObserver(e=>{const i=e.getEntries(),t=i[i.length-1];this.metrics.lcp=t.renderTime||t.loadTime,this.logMetric("LCP",this.metrics.lcp)}).observe({type:"largest-contentful-paint",buffered:!0}),new PerformanceObserver(e=>{const i=e.getEntries()[0];i&&(this.metrics.fid=i.processingStart-i.startTime,this.logMetric("FID",this.metrics.fid))}).observe({type:"first-input",buffered:!0}),window.addEventListener("visibilitychange",()=>{"hidden"===document.visibilityState&&this.metrics.cls&&this.logMetric("CLS",this.metrics.cls)})}catch(e){}}logMetric(e,i){i<0||this.analytics.track(`Core Web Vital: ${e}`,{metric:e,value:Math.round(i)},{standardEvent:"PERFORMANCE",intent:"performance",confidence:1,rawLabel:`${e}: ${Math.round(i)}ms`})}}const b=a.createContext(null);let C=null;const R=e=>{if(!C){const i={...e,writeKey:e.writeKey||e.apiKey||"",apiKey:e.apiKey||e.writeKey||""};C=new E(i)}return C},M={init:e=>R({writeKey:e.apiKey,apiKey:e.apiKey,apiHost:e.apiHost,tracking:e.tracking||{useCookies:!0,fingerprint:!0,autoTrack:!0},debug:e.debug}),track(e,i){C&&C.track(e,i)},page(e,i){C&&C.page(e,i)},identify(e,i){C&&C.identify(e,i)},trackRevenue(e,i){C&&C.trackRevenue(e,i)},setSentiment(e){C&&C.setSentiment(e)}};exports.EngageProProvider=({children:e,...i})=>{const n=a.useRef(null),r=a.useRef(null);n.current||(n.current=new E(i),"undefined"!=typeof window&&new S(n.current));const s=n.current;return a.useEffect(()=>{if(!i.tracking.autoTrack)return;const e=e=>{const i=e.target.closest('button, a, input[type="submit"], [data-track], .clickable');if(i){const e=Date.now(),t=r.current;t&&t.el===i&&e-t.ts<500?(t.count++,t.ts=e,3===t.count&&(s.track("Rage Click detected",{element:i.tagName},{standardEvent:"RAGE_CLICK",intent:"frustration",confidence:1}),r.current=null)):r.current={el:i,count:1,ts:e};const a=(e=>{let i=(e.innerText||e.value||e.getAttribute("aria-label")||"").trim();if(!i){const t=e.querySelector("img");t&&t.alt&&(i=t.alt);const a=e.querySelector("title");a&&(i=a.textContent||"")}const t=i.slice(0,100),a=t.toLowerCase(),n=(e.id||"").toLowerCase(),r=e.href||"";return/add to (cart|bag)|buy now/.test(a)||n.includes("add-to-cart")?{standard:"ADD_TO_CART",intent:"commerce",label:t,confidence:.9}:/checkout|proceed/.test(a)||r.includes("/checkout")?{standard:"INITIATE_CHECKOUT",intent:"commerce",label:t,confidence:.9}:/place order|pay now/.test(a)||n.includes("place-order")?{standard:"PURCHASE",intent:"commerce",label:t,confidence:.95}:/cancel order/.test(a)||n.includes("cancel")?{standard:"ORDER_CANCEL",intent:"lifecycle",label:t,confidence:.85}:/refund|return/.test(a)||n.includes("refund")?{standard:"ORDER_REFUND",intent:"lifecycle",label:t,confidence:.85}:/track package|shipping/.test(a)?{standard:"TRACK_PACKAGE",intent:"lifecycle",label:t,confidence:.8}:/write review/.test(a)||a.includes("star")&&/^[1-5]/.test(a)?{standard:"RATE_PRODUCT",intent:"engagement",label:t,confidence:.8}:a.includes("search")||n.includes("search")?{standard:"SEARCH",intent:"search",label:t,confidence:.7}:{standard:"GENERIC",intent:"interaction",label:t,confidence:.5}})(i),n="A"===i.tagName;s.track("Interaction",{element:i.tagName.toLowerCase(),id:i.id,destination:n?i.href:void 0},{standardEvent:a.standard,intent:a.intent,rawLabel:a.label,confidence:a.confidence})}},t=e=>{const i=e.target;(e=>"password"===e.getAttribute("type")||"hidden"===e.getAttribute("type")||/password|cvc|card|cc-num|ssn|credit|hidden/i.test(e.getAttribute("name")||e.id||""))(i)||"focusin"!==e.type||i.dataset.tracked||(i.dataset.tracked="true",s.track("Form Start",{field:i.name||i.id},{standardEvent:"FORM_START",intent:"identity"}))},a=()=>{const e=new URLSearchParams(window.location.search);e.has("q")&&s.track("Search Query",{query:e.get("q")},{standardEvent:"SEARCH",intent:"search"}),requestAnimationFrame(()=>s.page())},n=history.pushState;return history.pushState=(...e)=>{n.apply(history,e),a()},window.addEventListener("popstate",a),window.addEventListener("click",e,!0),window.addEventListener("focusin",t,!0),a(),()=>{history.pushState=n,window.removeEventListener("popstate",a),window.removeEventListener("click",e,!0),window.removeEventListener("focusin",t,!0)}},[i.tracking.autoTrack]),t.jsx(b.Provider,{value:s,children:e})},exports.default=E,exports.engage=M,exports.init=R,exports.useAnalytics=()=>{const e=a.useContext(b);if(!e)throw new Error("useAnalytics must be used within EngageProProvider");return e};
|
package/dist/index.d.ts
CHANGED
|
@@ -5,3 +5,20 @@ export default EngagePro;
|
|
|
5
5
|
export { EngageProProvider, useAnalytics } from './react/EngageProProvider';
|
|
6
6
|
export * from './types';
|
|
7
7
|
export declare const init: (config: EngageConfig) => EngagePro;
|
|
8
|
+
/**
|
|
9
|
+
* Spec §3.5 — The `engage` singleton object for non-React apps.
|
|
10
|
+
* Brands integrate like:
|
|
11
|
+
* import { engage } from '@bluenath/engage';
|
|
12
|
+
* engage.init({ apiKey: 'ep_live_xxxxx' });
|
|
13
|
+
* engage.track('purchase', { amount: 49900, currency: 'INR' });
|
|
14
|
+
*/
|
|
15
|
+
export declare const engage: {
|
|
16
|
+
init(config: Partial<EngageConfig> & {
|
|
17
|
+
apiKey: string;
|
|
18
|
+
}): EngagePro;
|
|
19
|
+
track(eventName: string, properties?: Record<string, unknown>): void;
|
|
20
|
+
page(name?: string, properties?: Record<string, unknown>): void;
|
|
21
|
+
identify(userId: string, traits?: Record<string, unknown>): void;
|
|
22
|
+
trackRevenue(amount: number | string, properties?: Record<string, unknown>): void;
|
|
23
|
+
setSentiment(sentiment: "positive" | "neutral" | "negative"): void;
|
|
24
|
+
};
|
package/dist/index.js
CHANGED
|
@@ -299,14 +299,18 @@ class Context {
|
|
|
299
299
|
}
|
|
300
300
|
}
|
|
301
301
|
class EventQueue {
|
|
302
|
+
// Spec §3.4
|
|
302
303
|
constructor(transport) {
|
|
303
304
|
__publicField(this, "queue", []);
|
|
305
|
+
__publicField(this, "engageQueue", []);
|
|
304
306
|
__publicField(this, "storage");
|
|
305
307
|
__publicField(this, "transport");
|
|
306
308
|
__publicField(this, "STORAGE_KEY", "engage_queue_v1");
|
|
309
|
+
__publicField(this, "ENGAGE_STORAGE_KEY", "engage_events_v2");
|
|
307
310
|
__publicField(this, "isFlushing", false);
|
|
308
311
|
__publicField(this, "flushInterval");
|
|
309
312
|
__publicField(this, "retryCount", 0);
|
|
313
|
+
__publicField(this, "MAX_RETRIES", 3);
|
|
310
314
|
this.transport = transport;
|
|
311
315
|
this.storage = new StorageEngine("local");
|
|
312
316
|
this.load();
|
|
@@ -316,21 +320,23 @@ class EventQueue {
|
|
|
316
320
|
this.flush();
|
|
317
321
|
});
|
|
318
322
|
window.addEventListener("visibilitychange", () => {
|
|
319
|
-
if (document.visibilityState === "hidden") this.
|
|
323
|
+
if (document.visibilityState === "hidden") this.flushBeacon();
|
|
324
|
+
});
|
|
325
|
+
window.addEventListener("beforeunload", () => {
|
|
326
|
+
this.flushBeacon();
|
|
320
327
|
});
|
|
321
328
|
}
|
|
322
329
|
this.startTimer();
|
|
323
330
|
}
|
|
324
331
|
startTimer() {
|
|
325
332
|
if (this.flushInterval) clearInterval(this.flushInterval);
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
this.flush();
|
|
330
|
-
},
|
|
331
|
-
Math.min(delay, 3e4)
|
|
332
|
-
);
|
|
333
|
+
this.flushInterval = setInterval(() => {
|
|
334
|
+
this.flush();
|
|
335
|
+
}, 5e3);
|
|
333
336
|
}
|
|
337
|
+
/**
|
|
338
|
+
* Enqueue a legacy AnalyticsEvent (backward compatibility)
|
|
339
|
+
*/
|
|
334
340
|
enqueue(event) {
|
|
335
341
|
this.queue.push(event);
|
|
336
342
|
this.save();
|
|
@@ -338,9 +344,25 @@ class EventQueue {
|
|
|
338
344
|
this.flush();
|
|
339
345
|
}
|
|
340
346
|
}
|
|
347
|
+
/**
|
|
348
|
+
* Enqueue a new EngageEvent (Spec §3.1)
|
|
349
|
+
*/
|
|
350
|
+
enqueueEngageEvent(event) {
|
|
351
|
+
this.engageQueue.push(event);
|
|
352
|
+
this.saveEngage();
|
|
353
|
+
if (this.engageQueue.length >= 10) {
|
|
354
|
+
this.flushEngageEvents();
|
|
355
|
+
}
|
|
356
|
+
}
|
|
341
357
|
save() {
|
|
342
358
|
this.storage.setItem(this.STORAGE_KEY, JSON.stringify(this.queue));
|
|
343
359
|
}
|
|
360
|
+
saveEngage() {
|
|
361
|
+
this.storage.setItem(
|
|
362
|
+
this.ENGAGE_STORAGE_KEY,
|
|
363
|
+
JSON.stringify(this.engageQueue)
|
|
364
|
+
);
|
|
365
|
+
}
|
|
344
366
|
load() {
|
|
345
367
|
const data = this.storage.getItem(this.STORAGE_KEY);
|
|
346
368
|
if (data) {
|
|
@@ -350,9 +372,25 @@ class EventQueue {
|
|
|
350
372
|
this.queue = [];
|
|
351
373
|
}
|
|
352
374
|
}
|
|
375
|
+
const engageData = this.storage.getItem(this.ENGAGE_STORAGE_KEY);
|
|
376
|
+
if (engageData) {
|
|
377
|
+
try {
|
|
378
|
+
this.engageQueue = JSON.parse(engageData);
|
|
379
|
+
} catch (e) {
|
|
380
|
+
this.engageQueue = [];
|
|
381
|
+
}
|
|
382
|
+
}
|
|
353
383
|
}
|
|
384
|
+
/**
|
|
385
|
+
* Flush legacy events (existing behavior)
|
|
386
|
+
*/
|
|
354
387
|
async flush() {
|
|
355
|
-
if (this.queue.length === 0 || this.isFlushing)
|
|
388
|
+
if (this.queue.length === 0 || this.isFlushing) {
|
|
389
|
+
if (this.engageQueue.length > 0 && !this.isFlushing) {
|
|
390
|
+
await this.flushEngageEvents();
|
|
391
|
+
}
|
|
392
|
+
return;
|
|
393
|
+
}
|
|
356
394
|
if (typeof navigator !== "undefined" && !navigator.onLine) return;
|
|
357
395
|
this.isFlushing = true;
|
|
358
396
|
const batch = [...this.queue];
|
|
@@ -362,7 +400,6 @@ class EventQueue {
|
|
|
362
400
|
const success = await this.transport.send(batch);
|
|
363
401
|
if (success) {
|
|
364
402
|
this.retryCount = 0;
|
|
365
|
-
this.startTimer();
|
|
366
403
|
} else {
|
|
367
404
|
throw new Error("Server rejected batch");
|
|
368
405
|
}
|
|
@@ -370,45 +407,410 @@ class EventQueue {
|
|
|
370
407
|
this.queue = [...batch, ...this.queue];
|
|
371
408
|
this.save();
|
|
372
409
|
this.retryCount++;
|
|
373
|
-
this.startTimer();
|
|
374
410
|
} finally {
|
|
375
411
|
this.isFlushing = false;
|
|
376
412
|
}
|
|
413
|
+
if (this.engageQueue.length > 0) {
|
|
414
|
+
await this.flushEngageEvents();
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
/**
|
|
418
|
+
* Spec §3.4: Flush EngageEvent[] with exponential backoff retry.
|
|
419
|
+
* Max 3 retries with 1s/2s/4s backoff. Drop batch after 3 failures.
|
|
420
|
+
* Do not retry on 401/422 (permanent failures).
|
|
421
|
+
*/
|
|
422
|
+
async flushEngageEvents() {
|
|
423
|
+
if (this.engageQueue.length === 0 || this.isFlushing) return;
|
|
424
|
+
if (typeof navigator !== "undefined" && !navigator.onLine) return;
|
|
425
|
+
this.isFlushing = true;
|
|
426
|
+
const batch = [...this.engageQueue];
|
|
427
|
+
this.engageQueue = [];
|
|
428
|
+
this.saveEngage();
|
|
429
|
+
let attempt = 0;
|
|
430
|
+
let success = false;
|
|
431
|
+
while (attempt < this.MAX_RETRIES && !success) {
|
|
432
|
+
const result = await this.transport.sendEngageEvents(batch);
|
|
433
|
+
if (result.success) {
|
|
434
|
+
success = true;
|
|
435
|
+
break;
|
|
436
|
+
}
|
|
437
|
+
if (result.permanent) {
|
|
438
|
+
console.warn(
|
|
439
|
+
`[EngagePro] Permanent failure (${result.status}). Dropping ${batch.length} events.`
|
|
440
|
+
);
|
|
441
|
+
this.isFlushing = false;
|
|
442
|
+
return;
|
|
443
|
+
}
|
|
444
|
+
attempt++;
|
|
445
|
+
if (attempt < this.MAX_RETRIES) {
|
|
446
|
+
const delay = Math.pow(2, attempt - 1) * 1e3;
|
|
447
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
if (!success) {
|
|
451
|
+
console.warn(
|
|
452
|
+
`[EngagePro] Dropped ${batch.length} events after ${this.MAX_RETRIES} failed retries.`
|
|
453
|
+
);
|
|
454
|
+
}
|
|
455
|
+
this.isFlushing = false;
|
|
456
|
+
}
|
|
457
|
+
/**
|
|
458
|
+
* Spec §3.4: Use navigator.sendBeacon() to flush remaining events on page unload.
|
|
459
|
+
* This is a best-effort fire-and-forget — no retry possible.
|
|
460
|
+
*/
|
|
461
|
+
flushBeacon() {
|
|
462
|
+
if (this.queue.length > 0) {
|
|
463
|
+
const payload = JSON.stringify({
|
|
464
|
+
batch: this.queue,
|
|
465
|
+
sentAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
466
|
+
});
|
|
467
|
+
const sent = this.transport.beaconFlush(payload);
|
|
468
|
+
if (sent) {
|
|
469
|
+
this.queue = [];
|
|
470
|
+
this.save();
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
if (this.engageQueue.length > 0) {
|
|
474
|
+
const payload = JSON.stringify({ events: this.engageQueue });
|
|
475
|
+
const sent = this.transport.beaconFlush(payload);
|
|
476
|
+
if (sent) {
|
|
477
|
+
this.engageQueue = [];
|
|
478
|
+
this.saveEngage();
|
|
479
|
+
}
|
|
480
|
+
}
|
|
377
481
|
}
|
|
378
482
|
}
|
|
379
483
|
class Transport {
|
|
380
484
|
constructor(apiEndpoint) {
|
|
485
|
+
__publicField(this, "apiKey", "");
|
|
381
486
|
this.apiEndpoint = apiEndpoint;
|
|
382
487
|
}
|
|
488
|
+
setApiKey(key) {
|
|
489
|
+
this.apiKey = key;
|
|
490
|
+
}
|
|
491
|
+
/**
|
|
492
|
+
* Sends legacy batched events (existing format).
|
|
493
|
+
* Used by the old EventQueue for backward compatibility.
|
|
494
|
+
*/
|
|
383
495
|
async send(events) {
|
|
384
|
-
const
|
|
385
|
-
batch: events,
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
496
|
+
const result = await this.sendRaw(
|
|
497
|
+
JSON.stringify({ batch: events, sentAt: (/* @__PURE__ */ new Date()).toISOString() })
|
|
498
|
+
);
|
|
499
|
+
return result.success;
|
|
500
|
+
}
|
|
501
|
+
/**
|
|
502
|
+
* Sends new EngageEvent[] to the ingest endpoint (Spec §4.1).
|
|
503
|
+
* Returns detailed result for retry logic.
|
|
504
|
+
*/
|
|
505
|
+
async sendEngageEvents(events) {
|
|
506
|
+
const payload = JSON.stringify({ events });
|
|
507
|
+
return this.sendRaw(payload);
|
|
508
|
+
}
|
|
509
|
+
async sendRaw(payload) {
|
|
510
|
+
const headers = {
|
|
511
|
+
"Content-Type": "application/json"
|
|
512
|
+
};
|
|
513
|
+
if (this.apiKey) {
|
|
514
|
+
headers["Authorization"] = `Bearer ${this.apiKey}`;
|
|
515
|
+
}
|
|
516
|
+
if (!this.apiKey && typeof navigator !== "undefined" && navigator.sendBeacon && payload.length < 6e4) {
|
|
389
517
|
const blob = new Blob([payload], { type: "application/json" });
|
|
390
518
|
const success = navigator.sendBeacon(this.apiEndpoint, blob);
|
|
391
|
-
if (success) return true;
|
|
519
|
+
if (success) return { success: true, permanent: false };
|
|
392
520
|
}
|
|
393
521
|
try {
|
|
394
522
|
const response = await fetch(this.apiEndpoint, {
|
|
395
523
|
method: "POST",
|
|
396
|
-
headers
|
|
524
|
+
headers,
|
|
397
525
|
body: payload,
|
|
398
526
|
keepalive: true
|
|
399
|
-
// Tries to keep request alive during navigation
|
|
400
527
|
});
|
|
401
|
-
|
|
528
|
+
if (response.status === 401 || response.status === 422) {
|
|
529
|
+
return { success: false, permanent: true, status: response.status };
|
|
530
|
+
}
|
|
531
|
+
return {
|
|
532
|
+
success: response.ok || response.status === 202,
|
|
533
|
+
permanent: false,
|
|
534
|
+
status: response.status
|
|
535
|
+
};
|
|
402
536
|
} catch (e) {
|
|
403
537
|
console.error("[EngagePro] Transport Failed:", e);
|
|
404
|
-
return false;
|
|
538
|
+
return { success: false, permanent: false };
|
|
405
539
|
}
|
|
406
540
|
}
|
|
541
|
+
/**
|
|
542
|
+
* Uses sendBeacon for last-resort page unload flush.
|
|
543
|
+
* Cannot send auth headers — the ingest endpoint must also accept
|
|
544
|
+
* the apiKey in the body payload as fallback.
|
|
545
|
+
*/
|
|
546
|
+
beaconFlush(payload) {
|
|
547
|
+
if (typeof navigator === "undefined" || !navigator.sendBeacon) return false;
|
|
548
|
+
const blob = new Blob([payload], { type: "application/json" });
|
|
549
|
+
return navigator.sendBeacon(this.apiEndpoint, blob);
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
const TIMEZONE_TO_COUNTRY = {
|
|
553
|
+
// Asia
|
|
554
|
+
"Asia/Kolkata": "IN",
|
|
555
|
+
"Asia/Calcutta": "IN",
|
|
556
|
+
"Asia/Mumbai": "IN",
|
|
557
|
+
"Asia/Dhaka": "BD",
|
|
558
|
+
"Asia/Kathmandu": "NP",
|
|
559
|
+
"Asia/Colombo": "LK",
|
|
560
|
+
"Asia/Karachi": "PK",
|
|
561
|
+
"Asia/Kabul": "AF",
|
|
562
|
+
"Asia/Tehran": "IR",
|
|
563
|
+
"Asia/Dubai": "AE",
|
|
564
|
+
"Asia/Muscat": "OM",
|
|
565
|
+
"Asia/Bahrain": "BH",
|
|
566
|
+
"Asia/Qatar": "QA",
|
|
567
|
+
"Asia/Kuwait": "KW",
|
|
568
|
+
"Asia/Riyadh": "SA",
|
|
569
|
+
"Asia/Aden": "YE",
|
|
570
|
+
"Asia/Baghdad": "IQ",
|
|
571
|
+
"Asia/Amman": "JO",
|
|
572
|
+
"Asia/Beirut": "LB",
|
|
573
|
+
"Asia/Damascus": "SY",
|
|
574
|
+
"Asia/Jerusalem": "IL",
|
|
575
|
+
"Asia/Tel_Aviv": "IL",
|
|
576
|
+
"Asia/Nicosia": "CY",
|
|
577
|
+
"Asia/Tokyo": "JP",
|
|
578
|
+
"Asia/Seoul": "KR",
|
|
579
|
+
"Asia/Pyongyang": "KP",
|
|
580
|
+
"Asia/Shanghai": "CN",
|
|
581
|
+
"Asia/Chongqing": "CN",
|
|
582
|
+
"Asia/Harbin": "CN",
|
|
583
|
+
"Asia/Urumqi": "CN",
|
|
584
|
+
"Asia/Hong_Kong": "HK",
|
|
585
|
+
"Asia/Macau": "MO",
|
|
586
|
+
"Asia/Taipei": "TW",
|
|
587
|
+
"Asia/Singapore": "SG",
|
|
588
|
+
"Asia/Kuala_Lumpur": "MY",
|
|
589
|
+
"Asia/Brunei": "BN",
|
|
590
|
+
"Asia/Jakarta": "ID",
|
|
591
|
+
"Asia/Makassar": "ID",
|
|
592
|
+
"Asia/Jayapura": "ID",
|
|
593
|
+
"Asia/Bangkok": "TH",
|
|
594
|
+
"Asia/Ho_Chi_Minh": "VN",
|
|
595
|
+
"Asia/Saigon": "VN",
|
|
596
|
+
"Asia/Phnom_Penh": "KH",
|
|
597
|
+
"Asia/Vientiane": "LA",
|
|
598
|
+
"Asia/Yangon": "MM",
|
|
599
|
+
"Asia/Rangoon": "MM",
|
|
600
|
+
"Asia/Manila": "PH",
|
|
601
|
+
"Asia/Ulaanbaatar": "MN",
|
|
602
|
+
"Asia/Hovd": "MN",
|
|
603
|
+
"Asia/Tbilisi": "GE",
|
|
604
|
+
"Asia/Baku": "AZ",
|
|
605
|
+
"Asia/Yerevan": "AM",
|
|
606
|
+
"Asia/Almaty": "KZ",
|
|
607
|
+
"Asia/Bishkek": "KG",
|
|
608
|
+
"Asia/Tashkent": "UZ",
|
|
609
|
+
"Asia/Ashgabat": "TM",
|
|
610
|
+
"Asia/Dushanbe": "TJ",
|
|
611
|
+
"Asia/Thimphu": "BT",
|
|
612
|
+
"Asia/Dili": "TL",
|
|
613
|
+
// Europe
|
|
614
|
+
"Europe/London": "GB",
|
|
615
|
+
"Europe/Dublin": "IE",
|
|
616
|
+
"Europe/Lisbon": "PT",
|
|
617
|
+
"Europe/Madrid": "ES",
|
|
618
|
+
"Europe/Paris": "FR",
|
|
619
|
+
"Europe/Brussels": "BE",
|
|
620
|
+
"Europe/Amsterdam": "NL",
|
|
621
|
+
"Europe/Luxembourg": "LU",
|
|
622
|
+
"Europe/Berlin": "DE",
|
|
623
|
+
"Europe/Zurich": "CH",
|
|
624
|
+
"Europe/Vienna": "AT",
|
|
625
|
+
"Europe/Rome": "IT",
|
|
626
|
+
"Europe/Monaco": "MC",
|
|
627
|
+
"Europe/Vatican": "VA",
|
|
628
|
+
"Europe/Malta": "MT",
|
|
629
|
+
"Europe/Prague": "CZ",
|
|
630
|
+
"Europe/Budapest": "HU",
|
|
631
|
+
"Europe/Warsaw": "PL",
|
|
632
|
+
"Europe/Bratislava": "SK",
|
|
633
|
+
"Europe/Ljubljana": "SI",
|
|
634
|
+
"Europe/Zagreb": "HR",
|
|
635
|
+
"Europe/Belgrade": "RS",
|
|
636
|
+
"Europe/Sarajevo": "BA",
|
|
637
|
+
"Europe/Podgorica": "ME",
|
|
638
|
+
"Europe/Skopje": "MK",
|
|
639
|
+
"Europe/Tirane": "AL",
|
|
640
|
+
"Europe/Sofia": "BG",
|
|
641
|
+
"Europe/Bucharest": "RO",
|
|
642
|
+
"Europe/Chisinau": "MD",
|
|
643
|
+
"Europe/Athens": "GR",
|
|
644
|
+
"Europe/Istanbul": "TR",
|
|
645
|
+
"Europe/Helsinki": "FI",
|
|
646
|
+
"Europe/Stockholm": "SE",
|
|
647
|
+
"Europe/Oslo": "NO",
|
|
648
|
+
"Europe/Copenhagen": "DK",
|
|
649
|
+
"Europe/Tallinn": "EE",
|
|
650
|
+
"Europe/Riga": "LV",
|
|
651
|
+
"Europe/Vilnius": "LT",
|
|
652
|
+
"Europe/Minsk": "BY",
|
|
653
|
+
"Europe/Moscow": "RU",
|
|
654
|
+
"Europe/Kaliningrad": "RU",
|
|
655
|
+
"Europe/Samara": "RU",
|
|
656
|
+
"Europe/Kiev": "UA",
|
|
657
|
+
"Europe/Kyiv": "UA",
|
|
658
|
+
"Europe/Reykjavik": "IS",
|
|
659
|
+
"Europe/Andorra": "AD",
|
|
660
|
+
"Europe/Gibraltar": "GI",
|
|
661
|
+
"Europe/San_Marino": "SM",
|
|
662
|
+
// Americas
|
|
663
|
+
"America/New_York": "US",
|
|
664
|
+
"America/Chicago": "US",
|
|
665
|
+
"America/Denver": "US",
|
|
666
|
+
"America/Los_Angeles": "US",
|
|
667
|
+
"America/Phoenix": "US",
|
|
668
|
+
"America/Anchorage": "US",
|
|
669
|
+
"Pacific/Honolulu": "US",
|
|
670
|
+
"America/Detroit": "US",
|
|
671
|
+
"America/Indianapolis": "US",
|
|
672
|
+
"America/Boise": "US",
|
|
673
|
+
"America/Juneau": "US",
|
|
674
|
+
"America/Adak": "US",
|
|
675
|
+
"America/Toronto": "CA",
|
|
676
|
+
"America/Vancouver": "CA",
|
|
677
|
+
"America/Montreal": "CA",
|
|
678
|
+
"America/Winnipeg": "CA",
|
|
679
|
+
"America/Edmonton": "CA",
|
|
680
|
+
"America/Halifax": "CA",
|
|
681
|
+
"America/St_Johns": "CA",
|
|
682
|
+
"America/Regina": "CA",
|
|
683
|
+
"America/Mexico_City": "MX",
|
|
684
|
+
"America/Cancun": "MX",
|
|
685
|
+
"America/Tijuana": "MX",
|
|
686
|
+
"America/Monterrey": "MX",
|
|
687
|
+
"America/Hermosillo": "MX",
|
|
688
|
+
"America/Guatemala": "GT",
|
|
689
|
+
"America/Belize": "BZ",
|
|
690
|
+
"America/El_Salvador": "SV",
|
|
691
|
+
"America/Tegucigalpa": "HN",
|
|
692
|
+
"America/Managua": "NI",
|
|
693
|
+
"America/Costa_Rica": "CR",
|
|
694
|
+
"America/Panama": "PA",
|
|
695
|
+
"America/Bogota": "CO",
|
|
696
|
+
"America/Lima": "PE",
|
|
697
|
+
"America/Guayaquil": "EC",
|
|
698
|
+
"America/Caracas": "VE",
|
|
699
|
+
"America/La_Paz": "BO",
|
|
700
|
+
"America/Asuncion": "PY",
|
|
701
|
+
"America/Montevideo": "UY",
|
|
702
|
+
"America/Buenos_Aires": "AR",
|
|
703
|
+
"America/Argentina/Buenos_Aires": "AR",
|
|
704
|
+
"America/Santiago": "CL",
|
|
705
|
+
"America/Sao_Paulo": "BR",
|
|
706
|
+
"America/Recife": "BR",
|
|
707
|
+
"America/Manaus": "BR",
|
|
708
|
+
"America/Fortaleza": "BR",
|
|
709
|
+
"America/Bahia": "BR",
|
|
710
|
+
"America/Havana": "CU",
|
|
711
|
+
"America/Jamaica": "JM",
|
|
712
|
+
"America/Port-au-Prince": "HT",
|
|
713
|
+
"America/Santo_Domingo": "DO",
|
|
714
|
+
"America/Puerto_Rico": "PR",
|
|
715
|
+
"America/Port_of_Spain": "TT",
|
|
716
|
+
"America/Barbados": "BB",
|
|
717
|
+
"America/Martinique": "MQ",
|
|
718
|
+
"America/Guyana": "GY",
|
|
719
|
+
"America/Paramaribo": "SR",
|
|
720
|
+
"America/Cayenne": "GF",
|
|
721
|
+
"America/Curacao": "CW",
|
|
722
|
+
// Africa
|
|
723
|
+
"Africa/Cairo": "EG",
|
|
724
|
+
"Africa/Casablanca": "MA",
|
|
725
|
+
"Africa/Tunis": "TN",
|
|
726
|
+
"Africa/Algiers": "DZ",
|
|
727
|
+
"Africa/Tripoli": "LY",
|
|
728
|
+
"Africa/Khartoum": "SD",
|
|
729
|
+
"Africa/Addis_Ababa": "ET",
|
|
730
|
+
"Africa/Nairobi": "KE",
|
|
731
|
+
"Africa/Dar_es_Salaam": "TZ",
|
|
732
|
+
"Africa/Kampala": "UG",
|
|
733
|
+
"Africa/Mogadishu": "SO",
|
|
734
|
+
"Africa/Lagos": "NG",
|
|
735
|
+
"Africa/Accra": "GH",
|
|
736
|
+
"Africa/Abidjan": "CI",
|
|
737
|
+
"Africa/Dakar": "SN",
|
|
738
|
+
"Africa/Bamako": "ML",
|
|
739
|
+
"Africa/Ouagadougou": "BF",
|
|
740
|
+
"Africa/Conakry": "GN",
|
|
741
|
+
"Africa/Freetown": "SL",
|
|
742
|
+
"Africa/Monrovia": "LR",
|
|
743
|
+
"Africa/Lome": "TG",
|
|
744
|
+
"Africa/Porto-Novo": "BJ",
|
|
745
|
+
"Africa/Niamey": "NE",
|
|
746
|
+
"Africa/Douala": "CM",
|
|
747
|
+
"Africa/Libreville": "GA",
|
|
748
|
+
"Africa/Bangui": "CF",
|
|
749
|
+
"Africa/Brazzaville": "CG",
|
|
750
|
+
"Africa/Kinshasa": "CD",
|
|
751
|
+
"Africa/Lubumbashi": "CD",
|
|
752
|
+
"Africa/Luanda": "AO",
|
|
753
|
+
"Africa/Maputo": "MZ",
|
|
754
|
+
"Africa/Harare": "ZW",
|
|
755
|
+
"Africa/Lusaka": "ZM",
|
|
756
|
+
"Africa/Lilongwe": "MW",
|
|
757
|
+
"Africa/Johannesburg": "ZA",
|
|
758
|
+
"Africa/Windhoek": "NA",
|
|
759
|
+
"Africa/Gaborone": "BW",
|
|
760
|
+
"Africa/Maseru": "LS",
|
|
761
|
+
"Africa/Mbabane": "SZ",
|
|
762
|
+
"Indian/Antananarivo": "MG",
|
|
763
|
+
"Indian/Mauritius": "MU",
|
|
764
|
+
"Indian/Reunion": "RE",
|
|
765
|
+
"Indian/Comoro": "KM",
|
|
766
|
+
"Indian/Mayotte": "YT",
|
|
767
|
+
"Africa/Djibouti": "DJ",
|
|
768
|
+
"Africa/Asmara": "ER",
|
|
769
|
+
// Oceania
|
|
770
|
+
"Australia/Sydney": "AU",
|
|
771
|
+
"Australia/Melbourne": "AU",
|
|
772
|
+
"Australia/Brisbane": "AU",
|
|
773
|
+
"Australia/Perth": "AU",
|
|
774
|
+
"Australia/Adelaide": "AU",
|
|
775
|
+
"Australia/Hobart": "AU",
|
|
776
|
+
"Australia/Darwin": "AU",
|
|
777
|
+
"Australia/Lord_Howe": "AU",
|
|
778
|
+
"Pacific/Auckland": "NZ",
|
|
779
|
+
"Pacific/Chatham": "NZ",
|
|
780
|
+
"Pacific/Fiji": "FJ",
|
|
781
|
+
"Pacific/Tongatapu": "TO",
|
|
782
|
+
"Pacific/Apia": "WS",
|
|
783
|
+
"Pacific/Port_Moresby": "PG",
|
|
784
|
+
"Pacific/Noumea": "NC",
|
|
785
|
+
"Pacific/Guam": "GU",
|
|
786
|
+
"Pacific/Pago_Pago": "AS",
|
|
787
|
+
"Pacific/Tahiti": "PF",
|
|
788
|
+
// Atlantic / Indian Ocean
|
|
789
|
+
"Atlantic/Reykjavik": "IS",
|
|
790
|
+
"Atlantic/Azores": "PT",
|
|
791
|
+
"Atlantic/Canary": "ES",
|
|
792
|
+
"Atlantic/Madeira": "PT",
|
|
793
|
+
"Atlantic/Cape_Verde": "CV",
|
|
794
|
+
"Atlantic/Bermuda": "BM",
|
|
795
|
+
"Indian/Maldives": "MV",
|
|
796
|
+
"Indian/Chagos": "IO",
|
|
797
|
+
"Indian/Christmas": "CX",
|
|
798
|
+
"Indian/Cocos": "CC"
|
|
799
|
+
};
|
|
800
|
+
function detectCountryFromTimezone() {
|
|
801
|
+
try {
|
|
802
|
+
const tz = Intl.DateTimeFormat().resolvedOptions().timeZone;
|
|
803
|
+
if (!tz) return void 0;
|
|
804
|
+
return TIMEZONE_TO_COUNTRY[tz];
|
|
805
|
+
} catch {
|
|
806
|
+
return void 0;
|
|
807
|
+
}
|
|
407
808
|
}
|
|
408
809
|
const DEFAULT_INGEST_ENDPOINT = "https://engage-api.bluenath.com/api/v1/tracking/ingest";
|
|
409
810
|
const FIXED_INGEST_PATH = "/api/v1/tracking/ingest";
|
|
410
811
|
const ALLOWED_DEV_HOSTS = /* @__PURE__ */ new Set(["localhost", "127.0.0.1"]);
|
|
411
812
|
const DEFAULT_CURRENCY = "USD";
|
|
813
|
+
const SESSION_KEY = "engage_session_id";
|
|
412
814
|
const SYMBOL_TO_CURRENCY = {
|
|
413
815
|
$: "USD",
|
|
414
816
|
US$: "USD",
|
|
@@ -424,8 +826,8 @@ const SYMBOL_TO_CURRENCY = {
|
|
|
424
826
|
"¥": "JPY",
|
|
425
827
|
JPY: "JPY"
|
|
426
828
|
};
|
|
427
|
-
const
|
|
428
|
-
if (!apiHost) return
|
|
829
|
+
const resolveEndpoint = (apiHost, path, defaultUrl) => {
|
|
830
|
+
if (!apiHost) return defaultUrl;
|
|
429
831
|
try {
|
|
430
832
|
const parsed = new URL(apiHost);
|
|
431
833
|
const isAllowedHost = parsed.hostname.endsWith("bluenath.com") || ALLOWED_DEV_HOSTS.has(parsed.hostname);
|
|
@@ -433,14 +835,14 @@ const resolveIngestEndpoint = (apiHost) => {
|
|
|
433
835
|
console.warn(
|
|
434
836
|
`[EngagePro] Ignoring unsupported apiHost "${parsed.hostname}". Falling back to managed endpoint.`
|
|
435
837
|
);
|
|
436
|
-
return
|
|
838
|
+
return defaultUrl;
|
|
437
839
|
}
|
|
438
|
-
parsed.pathname =
|
|
840
|
+
parsed.pathname = path;
|
|
439
841
|
parsed.search = "";
|
|
440
842
|
parsed.hash = "";
|
|
441
843
|
return parsed.toString();
|
|
442
844
|
} catch {
|
|
443
|
-
return
|
|
845
|
+
return defaultUrl;
|
|
444
846
|
}
|
|
445
847
|
};
|
|
446
848
|
const parseCurrencyToken = (value) => {
|
|
@@ -462,6 +864,29 @@ const parseRevenueAmount = (value) => {
|
|
|
462
864
|
const parsed = Number(numeric);
|
|
463
865
|
return Number.isFinite(parsed) ? parsed : 0;
|
|
464
866
|
};
|
|
867
|
+
function getOrCreateSessionId() {
|
|
868
|
+
if (typeof sessionStorage === "undefined") return v4();
|
|
869
|
+
let sessionId = sessionStorage.getItem(SESSION_KEY);
|
|
870
|
+
if (!sessionId) {
|
|
871
|
+
sessionId = v4();
|
|
872
|
+
sessionStorage.setItem(SESSION_KEY, sessionId);
|
|
873
|
+
}
|
|
874
|
+
return sessionId;
|
|
875
|
+
}
|
|
876
|
+
function validateMetadata(meta) {
|
|
877
|
+
if (!meta || typeof meta !== "object") return void 0;
|
|
878
|
+
const result = {};
|
|
879
|
+
const keys = Object.keys(meta).slice(0, 20);
|
|
880
|
+
for (const key of keys) {
|
|
881
|
+
const val = meta[key];
|
|
882
|
+
if (typeof val === "boolean" || typeof val === "number") {
|
|
883
|
+
result[key] = val;
|
|
884
|
+
} else if (typeof val === "string") {
|
|
885
|
+
result[key] = val.slice(0, 100);
|
|
886
|
+
}
|
|
887
|
+
}
|
|
888
|
+
return Object.keys(result).length > 0 ? result : void 0;
|
|
889
|
+
}
|
|
465
890
|
class EngagePro {
|
|
466
891
|
constructor(config) {
|
|
467
892
|
__publicField(this, "config");
|
|
@@ -469,6 +894,11 @@ class EngagePro {
|
|
|
469
894
|
__publicField(this, "context");
|
|
470
895
|
__publicField(this, "queue");
|
|
471
896
|
__publicField(this, "transport");
|
|
897
|
+
// Spec §3.2: Auto-detected fields
|
|
898
|
+
__publicField(this, "sessionId");
|
|
899
|
+
__publicField(this, "countryCode");
|
|
900
|
+
// Spec §3.3: Current sentiment (set by brand, attached to next event)
|
|
901
|
+
__publicField(this, "currentSentiment");
|
|
472
902
|
// ✅ ARROW FUNCTION (Auto-bound 'this')
|
|
473
903
|
__publicField(this, "identify", (userId, traits) => {
|
|
474
904
|
this.context.userId = userId;
|
|
@@ -495,6 +925,7 @@ class EngagePro {
|
|
|
495
925
|
intent: "navigation",
|
|
496
926
|
confidence: 1
|
|
497
927
|
});
|
|
928
|
+
this.emitEngageEvent("pageview", properties);
|
|
498
929
|
});
|
|
499
930
|
// ✅ ARROW FUNCTION
|
|
500
931
|
__publicField(this, "track", (eventName, properties, intelligence) => {
|
|
@@ -503,6 +934,22 @@ class EngagePro {
|
|
|
503
934
|
properties: properties || {},
|
|
504
935
|
...intelligence
|
|
505
936
|
});
|
|
937
|
+
const typeMap = {
|
|
938
|
+
purchase: "purchase",
|
|
939
|
+
PURCHASE: "purchase",
|
|
940
|
+
signup: "signup",
|
|
941
|
+
SIGNUP: "signup",
|
|
942
|
+
register: "signup",
|
|
943
|
+
REGISTER: "signup",
|
|
944
|
+
add_to_cart: "add_to_cart",
|
|
945
|
+
ADD_TO_CART: "add_to_cart",
|
|
946
|
+
product_view: "product_view",
|
|
947
|
+
VIEW_CONTENT: "product_view",
|
|
948
|
+
session_start: "session_start",
|
|
949
|
+
session_end: "session_end"
|
|
950
|
+
};
|
|
951
|
+
const engageType = typeMap[eventName] || typeMap[(intelligence == null ? void 0 : intelligence.standardEvent) || ""] || "custom";
|
|
952
|
+
this.emitEngageEvent(engageType, properties);
|
|
506
953
|
});
|
|
507
954
|
__publicField(this, "trackRevenue", (amount, properties, intelligence) => {
|
|
508
955
|
const parsedAmount = Math.max(0, parseRevenueAmount(amount));
|
|
@@ -526,12 +973,53 @@ class EngagePro {
|
|
|
526
973
|
}
|
|
527
974
|
);
|
|
528
975
|
});
|
|
976
|
+
/**
|
|
977
|
+
* Spec §3.3: Set sentiment for the current session.
|
|
978
|
+
* Attached to the next emitted event.
|
|
979
|
+
*/
|
|
980
|
+
__publicField(this, "setSentiment", (sentiment) => {
|
|
981
|
+
this.currentSentiment = sentiment;
|
|
982
|
+
});
|
|
529
983
|
this.config = config;
|
|
530
|
-
|
|
984
|
+
const apiKey = config.apiKey || config.writeKey;
|
|
985
|
+
const legacyEndpoint = resolveEndpoint(config.apiHost, FIXED_INGEST_PATH, DEFAULT_INGEST_ENDPOINT);
|
|
986
|
+
this.transport = new Transport(legacyEndpoint);
|
|
987
|
+
this.transport.setApiKey(apiKey);
|
|
531
988
|
this.context = new Context({
|
|
532
989
|
persistence: config.tracking.useCookies ? "cookie" : "local"
|
|
533
990
|
});
|
|
534
991
|
this.queue = new EventQueue(this.transport);
|
|
992
|
+
this.sessionId = getOrCreateSessionId();
|
|
993
|
+
this.countryCode = detectCountryFromTimezone();
|
|
994
|
+
if (config.tracking.autoTrack && typeof window !== "undefined") {
|
|
995
|
+
this.page();
|
|
996
|
+
}
|
|
997
|
+
}
|
|
998
|
+
/**
|
|
999
|
+
* Emit a new-format EngageEvent to the ingest pipeline (Spec §3.1).
|
|
1000
|
+
*/
|
|
1001
|
+
emitEngageEvent(type, properties) {
|
|
1002
|
+
const pageCtx = this.context.getPayload().page;
|
|
1003
|
+
const event = {
|
|
1004
|
+
type,
|
|
1005
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1006
|
+
sessionId: this.sessionId,
|
|
1007
|
+
userId: this.context.getPayload().user.id || void 0,
|
|
1008
|
+
countryCode: this.countryCode,
|
|
1009
|
+
sentiment: this.currentSentiment,
|
|
1010
|
+
referrer: (pageCtx.referrer || "").slice(0, 500),
|
|
1011
|
+
path: pageCtx.path || (typeof window !== "undefined" ? window.location.pathname : "/"),
|
|
1012
|
+
currency: parseCurrencyToken(properties == null ? void 0 : properties.currency) || parseCurrencyToken(properties == null ? void 0 : properties.currencyCode) || void 0,
|
|
1013
|
+
amount: typeof (properties == null ? void 0 : properties.amountPaise) === "number" ? properties.amountPaise : typeof (properties == null ? void 0 : properties.amount) === "number" ? Math.round(properties.amount * 100) : void 0,
|
|
1014
|
+
productId: typeof (properties == null ? void 0 : properties.productId) === "string" ? properties.productId : void 0,
|
|
1015
|
+
productName: typeof (properties == null ? void 0 : properties.productName) === "string" ? properties.productName : void 0,
|
|
1016
|
+
productCategory: typeof (properties == null ? void 0 : properties.productCategory) === "string" ? properties.productCategory : void 0,
|
|
1017
|
+
metadata: validateMetadata(properties == null ? void 0 : properties.metadata)
|
|
1018
|
+
};
|
|
1019
|
+
this.queue.enqueueEngageEvent(event);
|
|
1020
|
+
if (this.currentSentiment) {
|
|
1021
|
+
this.currentSentiment = void 0;
|
|
1022
|
+
}
|
|
535
1023
|
}
|
|
536
1024
|
parseRef(ref) {
|
|
537
1025
|
if (!ref) return {};
|
|
@@ -539,7 +1027,7 @@ class EngagePro {
|
|
|
539
1027
|
if (!m) return {};
|
|
540
1028
|
return { campaignId: m[1], creatorId: m[2] };
|
|
541
1029
|
}
|
|
542
|
-
// Internal Helper
|
|
1030
|
+
// Internal Helper (legacy event processing)
|
|
543
1031
|
processEvent(data) {
|
|
544
1032
|
const baseContext = this.context.getPayload();
|
|
545
1033
|
const refValue = data.properties.ref;
|
|
@@ -561,7 +1049,7 @@ class EngagePro {
|
|
|
561
1049
|
// Metadata
|
|
562
1050
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
563
1051
|
messageId: v4(),
|
|
564
|
-
writeKey: this.config.writeKey,
|
|
1052
|
+
writeKey: this.config.writeKey || this.config.apiKey || "",
|
|
565
1053
|
ref,
|
|
566
1054
|
campaignId: (typeof campaignIdValue === "string" ? campaignIdValue : void 0) || refParts.campaignId,
|
|
567
1055
|
creatorId: (typeof creatorIdValue === "string" ? creatorIdValue : void 0) || refParts.creatorId,
|
|
@@ -823,13 +1311,69 @@ const EngageProProvider = ({
|
|
|
823
1311
|
let instance = null;
|
|
824
1312
|
const init = (config) => {
|
|
825
1313
|
if (!instance) {
|
|
826
|
-
|
|
1314
|
+
const normalizedConfig = {
|
|
1315
|
+
...config,
|
|
1316
|
+
writeKey: config.writeKey || config.apiKey || "",
|
|
1317
|
+
apiKey: config.apiKey || config.writeKey || ""
|
|
1318
|
+
};
|
|
1319
|
+
instance = new EngagePro(normalizedConfig);
|
|
827
1320
|
}
|
|
828
1321
|
return instance;
|
|
829
1322
|
};
|
|
1323
|
+
const engage = {
|
|
1324
|
+
init(config) {
|
|
1325
|
+
return init({
|
|
1326
|
+
writeKey: config.apiKey,
|
|
1327
|
+
apiKey: config.apiKey,
|
|
1328
|
+
apiHost: config.apiHost,
|
|
1329
|
+
tracking: config.tracking || {
|
|
1330
|
+
useCookies: true,
|
|
1331
|
+
fingerprint: true,
|
|
1332
|
+
autoTrack: true
|
|
1333
|
+
},
|
|
1334
|
+
debug: config.debug
|
|
1335
|
+
});
|
|
1336
|
+
},
|
|
1337
|
+
track(eventName, properties) {
|
|
1338
|
+
if (!instance) {
|
|
1339
|
+
console.warn("[EngagePro] Not initialized. Call engage.init() first.");
|
|
1340
|
+
return;
|
|
1341
|
+
}
|
|
1342
|
+
instance.track(eventName, properties);
|
|
1343
|
+
},
|
|
1344
|
+
page(name, properties) {
|
|
1345
|
+
if (!instance) {
|
|
1346
|
+
console.warn("[EngagePro] Not initialized. Call engage.init() first.");
|
|
1347
|
+
return;
|
|
1348
|
+
}
|
|
1349
|
+
instance.page(name, properties);
|
|
1350
|
+
},
|
|
1351
|
+
identify(userId, traits) {
|
|
1352
|
+
if (!instance) {
|
|
1353
|
+
console.warn("[EngagePro] Not initialized. Call engage.init() first.");
|
|
1354
|
+
return;
|
|
1355
|
+
}
|
|
1356
|
+
instance.identify(userId, traits);
|
|
1357
|
+
},
|
|
1358
|
+
trackRevenue(amount, properties) {
|
|
1359
|
+
if (!instance) {
|
|
1360
|
+
console.warn("[EngagePro] Not initialized. Call engage.init() first.");
|
|
1361
|
+
return;
|
|
1362
|
+
}
|
|
1363
|
+
instance.trackRevenue(amount, properties);
|
|
1364
|
+
},
|
|
1365
|
+
setSentiment(sentiment) {
|
|
1366
|
+
if (!instance) {
|
|
1367
|
+
console.warn("[EngagePro] Not initialized. Call engage.init() first.");
|
|
1368
|
+
return;
|
|
1369
|
+
}
|
|
1370
|
+
instance.setSentiment(sentiment);
|
|
1371
|
+
}
|
|
1372
|
+
};
|
|
830
1373
|
export {
|
|
831
1374
|
EngageProProvider,
|
|
832
1375
|
EngagePro as default,
|
|
1376
|
+
engage,
|
|
833
1377
|
init,
|
|
834
1378
|
useAnalytics
|
|
835
1379
|
};
|
package/dist/types/index.d.ts
CHANGED
|
@@ -64,6 +64,7 @@ export interface AnalyticsEvent {
|
|
|
64
64
|
}
|
|
65
65
|
export interface EngageConfig {
|
|
66
66
|
writeKey: string;
|
|
67
|
+
apiKey?: string;
|
|
67
68
|
apiHost?: string;
|
|
68
69
|
tracking: {
|
|
69
70
|
useCookies: boolean;
|
|
@@ -72,3 +73,25 @@ export interface EngageConfig {
|
|
|
72
73
|
};
|
|
73
74
|
debug?: boolean;
|
|
74
75
|
}
|
|
76
|
+
export type EngageEventType = "pageview" | "product_view" | "add_to_cart" | "purchase" | "signup" | "session_start" | "session_end" | "custom";
|
|
77
|
+
export type SentimentValue = "positive" | "neutral" | "negative";
|
|
78
|
+
/**
|
|
79
|
+
* Spec §3.1 — The event schema emitted by the engage module
|
|
80
|
+
* to POST /api/v1/analytics/ingest. Backend validates against this.
|
|
81
|
+
*/
|
|
82
|
+
export interface EngageEvent {
|
|
83
|
+
type: EngageEventType;
|
|
84
|
+
timestamp: string;
|
|
85
|
+
sessionId: string;
|
|
86
|
+
userId?: string;
|
|
87
|
+
currency?: string;
|
|
88
|
+
amount?: number;
|
|
89
|
+
productId?: string;
|
|
90
|
+
productName?: string;
|
|
91
|
+
productCategory?: string;
|
|
92
|
+
countryCode?: string;
|
|
93
|
+
sentiment?: SentimentValue;
|
|
94
|
+
referrer?: string;
|
|
95
|
+
path: string;
|
|
96
|
+
metadata?: Record<string, string | number | boolean>;
|
|
97
|
+
}
|