@hifilabs/pixel 0.9.0 → 0.9.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/browser.js CHANGED
@@ -8,7 +8,7 @@ var BalancePixel = (() => {
8
8
 
9
9
  // src/browser.ts
10
10
  (function() {
11
- const PIXEL_VERSION = "0.9.0";
11
+ const PIXEL_VERSION = "0.9.1";
12
12
  function parseUserAgent(ua) {
13
13
  let device_type = "desktop";
14
14
  if (/ipad|tablet|android(?!.*mobile)/i.test(ua))
@@ -66,7 +66,7 @@ var BalancePixel = (() => {
66
66
  }
67
67
  const useEmulator = currentScript?.dataset.emulator === "true";
68
68
  const debug = currentScript?.dataset.debug === "true";
69
- const heartbeatInterval = parseInt(currentScript?.dataset.heartbeatInterval || "30000", 10);
69
+ const heartbeatInterval = parseInt(currentScript?.dataset.heartbeatInterval || "120000", 10);
70
70
  const heartbeatEnabled = currentScript?.dataset.heartbeat !== "false";
71
71
  const explicitSource = currentScript?.dataset.source;
72
72
  const customEndpoint = currentScript?.dataset.endpoint;
@@ -181,7 +181,7 @@ var BalancePixel = (() => {
181
181
  logError("Failed to track file download:", e);
182
182
  }
183
183
  }
184
- const PIXEL_PROXY_URL = "https://balance-pixel-proxy.jassin.workers.dev";
184
+ const PIXEL_PROXY_URL = "https://balance-pixel-proxy.hifilabs.workers.dev";
185
185
  const FIREBASE_DIRECT_URL = "https://us-central1-artist-os-distro.cloudfunctions.net/ingestEvents";
186
186
  const API_ENDPOINT = customEndpoint ? customEndpoint : useEmulator ? `http://localhost:5001/artist-os-distro/us-central1/ingestEvents` : PIXEL_PROXY_URL;
187
187
  let sessionId = null;
@@ -197,6 +197,8 @@ var BalancePixel = (() => {
197
197
  let activeTime = 0;
198
198
  let lastActiveTimestamp = 0;
199
199
  let isPageVisible = true;
200
+ let heartbeatCount = 0;
201
+ let summarySent = false;
200
202
  const IDLE_TIMEOUT = 2 * 60 * 1e3;
201
203
  let lastActivityTime = Date.now();
202
204
  let isIdle = false;
@@ -751,6 +753,7 @@ var BalancePixel = (() => {
751
753
  }
752
754
  });
753
755
  enqueueEvent(event);
756
+ heartbeatCount++;
754
757
  log("Heartbeat sent:", timeOnPageSeconds, "seconds active");
755
758
  }
756
759
  function startHeartbeat() {
@@ -767,14 +770,35 @@ var BalancePixel = (() => {
767
770
  log("Heartbeat started with interval:", heartbeatInterval, "ms");
768
771
  }
769
772
  function stopHeartbeat() {
773
+ if (summarySent)
774
+ return;
775
+ summarySent = true;
770
776
  if (heartbeatTimer) {
771
777
  clearInterval(heartbeatTimer);
772
778
  heartbeatTimer = null;
773
779
  }
774
780
  if (heartbeatEnabled) {
775
- sendHeartbeat();
776
- log("Heartbeat stopped, final time sent");
781
+ const totalTimeMs = pageStartTime ? Date.now() - pageStartTime : 0;
782
+ const activeTimeMs = getCumulativeActiveTime();
783
+ const event = buildEvent({
784
+ event_name: "engagement_summary",
785
+ metadata: {
786
+ total_time_ms: totalTimeMs,
787
+ active_time_ms: activeTimeMs,
788
+ heartbeat_samples: heartbeatCount,
789
+ page_path: window.location.pathname
790
+ }
791
+ });
792
+ if (navigator.sendBeacon && API_ENDPOINT) {
793
+ const payload = JSON.stringify({ events: [event] });
794
+ navigator.sendBeacon(API_ENDPOINT, payload);
795
+ log("Engagement summary sent via sendBeacon:", Math.round(activeTimeMs / 1e3), "seconds active");
796
+ } else {
797
+ enqueueEvent(event);
798
+ log("Engagement summary enqueued:", Math.round(activeTimeMs / 1e3), "seconds active");
799
+ }
777
800
  }
801
+ heartbeatCount = 0;
778
802
  }
779
803
  function trackPageView(options = {}) {
780
804
  const pageUrl = options.url || window.location.href;
@@ -1016,18 +1040,24 @@ var BalancePixel = (() => {
1016
1040
  });
1017
1041
  log("Hash routing tracking enabled");
1018
1042
  }
1019
- window.addEventListener("beforeunload", () => {
1020
- stopHeartbeat();
1021
- flush();
1022
- });
1023
1043
  document.addEventListener("visibilitychange", () => {
1024
1044
  if (document.visibilityState === "hidden") {
1025
1045
  pauseActiveTimeTracking();
1046
+ stopHeartbeat();
1026
1047
  flush();
1027
1048
  } else {
1028
1049
  startActiveTimeTracking();
1050
+ summarySent = false;
1029
1051
  }
1030
1052
  });
1053
+ window.addEventListener("pagehide", () => {
1054
+ stopHeartbeat();
1055
+ flush();
1056
+ });
1057
+ window.addEventListener("beforeunload", () => {
1058
+ stopHeartbeat();
1059
+ flush();
1060
+ });
1031
1061
  }
1032
1062
  if (window.balance?._version && window.balance._version !== PIXEL_VERSION) {
1033
1063
  console.warn(
@@ -1,4 +1,4 @@
1
- var BalancePixel=(()=>{var it=Object.defineProperty;var rt=(g,p,u)=>p in g?it(g,p,{enumerable:!0,configurable:!0,writable:!0,value:u}):g[p]=u;var F=(g,p,u)=>(rt(g,typeof p!="symbol"?p+"":p,u),u);(function(){let g="0.9.0";function p(e){let t="desktop";/ipad|tablet|android(?!.*mobile)/i.test(e)?t="tablet":/mobile|iphone|android.*mobile|blackberry|iemobile/i.test(e)&&(t="mobile");let n="Unknown";/edg/i.test(e)?n="Edge":/opr|opera/i.test(e)?n="Opera":/firefox/i.test(e)?n="Firefox":/chrome/i.test(e)?n="Chrome":/safari/i.test(e)&&(n="Safari");let r="Unknown";return/iphone|ipad/i.test(e)?r="iOS":/android/i.test(e)?r="Android":/windows/i.test(e)?r="Windows":/mac os/i.test(e)?r="macOS":/linux/i.test(e)?r="Linux":/cros/i.test(e)&&(r="ChromeOS"),{device_type:t,browser:n,os:r}}let u=null;function Ie(){if(!u)try{u=p(navigator.userAgent)}catch{u={device_type:"desktop",browser:"Unknown",os:"Unknown"}}return u}let l=document.currentScript,H=l?.dataset.artistId,M=l?.dataset.projectId,z=M?Se(M):void 0;function Se(e){return!e||e.startsWith("release_")||e.startsWith("merch_")||e.startsWith("link_")||e.startsWith("custom_")?e:`custom_${e}`}let j=l?.dataset.emulator==="true",ee=l?.dataset.debug==="true",$=parseInt(l?.dataset.heartbeatInterval||"30000",10),te=l?.dataset.heartbeat!=="false",ne=l?.dataset.source,ie=l?.dataset.endpoint,B=l?.dataset.consentUi==="true",Ee=l?.dataset.consentStyle||"brutalist",Ce=l?.dataset.primaryColor,Te=l?.dataset.bannerPosition||"bottom",re=l?.dataset.excludePages?.split(",").filter(Boolean)||[],oe=l?.dataset.trackFileDownloads==="true",Le=l?.dataset.hashRouting==="true";function De(){if(ne)return ne;let e=typeof window.dataLayer<"u"&&Array.isArray(window.dataLayer),t=typeof window.gtag=="function";return e&&t?"gtm":"pixel"}let P="pixel";if(!H){w(" Error: data-artist-id attribute is required");return}let ae="session_id",W="session_timestamp",se="attribution",ce="fan_id_hash",V="visitor_id",R="balance_consent",Pe=365*24*60*60*1e3,Re=30*24*60*60*1e3,Oe=60*60*1e3,x="balance_";function Ae(e){let t=e.replace(/[.+?^${}()|[\]\\]/g,"\\$&").replace(/\*/g,".*");return new RegExp("^"+t+"$")}function Ne(e){if(re.length===0)return!1;try{let t=new URL(e,window.location.origin).pathname;return re.some(n=>Ae(n).test(t))}catch{return!1}}let Ue=[".pdf",".zip",".tar",".gz",".rar",".7z",".doc",".docx",".xls",".xlsx",".ppt",".pptx",".mp3",".wav",".flac",".aac",".ogg",".mp4",".mov",".avi",".mkv",".webm",".exe",".dmg",".pkg",".deb",".rpm",".csv",".json",".xml",".txt"];function le(e){try{let t=new URL(e,window.location.origin).pathname.toLowerCase();return Ue.some(n=>t.endsWith(n))}catch{return!1}}function Fe(e,t){try{let r=new URL(e,window.location.origin).pathname.split("/").pop()||"unknown",f=r.includes(".")?r.split(".").pop():"unknown";i("File download tracked:",{url:e,fileName:r,fileExtension:f});let L=y({event_name:"custom",metadata:{event_type:"file_download",file_url:e,file_name:r,file_extension:f,link_text:t?.textContent?.trim().substring(0,100)||""}});v(L)}catch(n){w("Failed to track file download:",n)}}let He="https://balance-pixel-proxy.jassin.workers.dev",de="https://us-central1-artist-os-distro.cloudfunctions.net/ingestEvents",O=ie||(j?"http://localhost:5001/artist-os-distro/us-central1/ingestEvents":He),A=null,d=null,m=null,s=null,k={},h=[],q=null,b="session",_=null,Me=0,K=0,I=0,S=!0,ze=2*60*1e3,ue=Date.now(),E=!1,je=!ee,i=(...e)=>{ee&&console.log("[BLN]",...e)},w=(...e)=>{je||console.error("[BLN]",...e)},Y={base:`
1
+ var BalancePixel=(()=>{var at=Object.defineProperty;var rt=(g,p,u)=>p in g?at(g,p,{enumerable:!0,configurable:!0,writable:!0,value:u}):g[p]=u;var M=(g,p,u)=>(rt(g,typeof p!="symbol"?p+"":p,u),u);(function(){let g="0.9.1";function p(e){let t="desktop";/ipad|tablet|android(?!.*mobile)/i.test(e)?t="tablet":/mobile|iphone|android.*mobile|blackberry|iemobile/i.test(e)&&(t="mobile");let n="Unknown";/edg/i.test(e)?n="Edge":/opr|opera/i.test(e)?n="Opera":/firefox/i.test(e)?n="Firefox":/chrome/i.test(e)?n="Chrome":/safari/i.test(e)&&(n="Safari");let o="Unknown";return/iphone|ipad/i.test(e)?o="iOS":/android/i.test(e)?o="Android":/windows/i.test(e)?o="Windows":/mac os/i.test(e)?o="macOS":/linux/i.test(e)?o="Linux":/cros/i.test(e)&&(o="ChromeOS"),{device_type:t,browser:n,os:o}}let u=null;function Te(){if(!u)try{u=p(navigator.userAgent)}catch{u={device_type:"desktop",browser:"Unknown",os:"Unknown"}}return u}let l=document.currentScript,F=l?.dataset.artistId,H=l?.dataset.projectId,z=H?Le(H):void 0;function Le(e){return!e||e.startsWith("release_")||e.startsWith("merch_")||e.startsWith("link_")||e.startsWith("custom_")?e:`custom_${e}`}let B=l?.dataset.emulator==="true",oe=l?.dataset.debug==="true",j=parseInt(l?.dataset.heartbeatInterval||"120000",10),ae=l?.dataset.heartbeat!=="false",re=l?.dataset.source,se=l?.dataset.endpoint,$=l?.dataset.consentUi==="true",De=l?.dataset.consentStyle||"brutalist",Pe=l?.dataset.primaryColor,Re=l?.dataset.bannerPosition||"bottom",ce=l?.dataset.excludePages?.split(",").filter(Boolean)||[],le=l?.dataset.trackFileDownloads==="true",Oe=l?.dataset.hashRouting==="true";function Ae(){if(re)return re;let e=typeof window.dataLayer<"u"&&Array.isArray(window.dataLayer),t=typeof window.gtag=="function";return e&&t?"gtm":"pixel"}let O="pixel";if(!F){v(" Error: data-artist-id attribute is required");return}let de="session_id",W="session_timestamp",ue="attribution",fe="fan_id_hash",q="visitor_id",A="balance_consent",Ne=365*24*60*60*1e3,Ue=30*24*60*60*1e3,Me=60*60*1e3,x="balance_";function Fe(e){let t=e.replace(/[.+?^${}()|[\]\\]/g,"\\$&").replace(/\*/g,".*");return new RegExp("^"+t+"$")}function He(e){if(ce.length===0)return!1;try{let t=new URL(e,window.location.origin).pathname;return ce.some(n=>Fe(n).test(t))}catch{return!1}}let ze=[".pdf",".zip",".tar",".gz",".rar",".7z",".doc",".docx",".xls",".xlsx",".ppt",".pptx",".mp3",".wav",".flac",".aac",".ogg",".mp4",".mov",".avi",".mkv",".webm",".exe",".dmg",".pkg",".deb",".rpm",".csv",".json",".xml",".txt"];function ge(e){try{let t=new URL(e,window.location.origin).pathname.toLowerCase();return ze.some(n=>t.endsWith(n))}catch{return!1}}function Be(e,t){try{let o=new URL(e,window.location.origin).pathname.split("/").pop()||"unknown",f=o.includes(".")?o.split(".").pop():"unknown";i("File download tracked:",{url:e,fileName:o,fileExtension:f});let P=w({event_name:"custom",metadata:{event_type:"file_download",file_url:e,file_name:o,file_extension:f,link_text:t?.textContent?.trim().substring(0,100)||""}});y(P)}catch(n){v("Failed to track file download:",n)}}let je="https://balance-pixel-proxy.hifilabs.workers.dev",pe="https://us-central1-artist-os-distro.cloudfunctions.net/ingestEvents",k=se||(B?"http://localhost:5001/artist-os-distro/us-central1/ingestEvents":je),N=null,d=null,m=null,s=null,_={},h=[],V=null,b="session",I=null,K=0,Y=0,S=0,E=!0,J=0,X=!1,$e=2*60*1e3,me=Date.now(),C=!1,We=!oe,i=(...e)=>{oe&&console.log("[BLN]",...e)},v=(...e)=>{We||console.error("[BLN]",...e)},G={base:`
2
2
  :host {
3
3
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
4
4
  position: fixed;
@@ -83,12 +83,12 @@ var BalancePixel=(()=>{var it=Object.defineProperty;var rt=(g,p,u)=>p in g?it(g,
83
83
  }
84
84
  .btn.accept { background: #fff; color: #000; border-color: #fff; }
85
85
  .btn:hover { opacity: 0.8; }
86
- `};class $e{constructor(t){F(this,"container",null);F(this,"shadow",null);F(this,"config");if(this.config=t,this.hasStoredConsent()){i("ConsentManager: Consent already exists, not showing banner");return}this.container=document.createElement("div"),this.container.id="balance-consent-manager",this.shadow=this.container.attachShadow({mode:"closed"}),this.render(),document.body.appendChild(this.container),i("ConsentManager: Banner rendered")}hasStoredConsent(){try{return window._balanceConsentNeedsRefresh?!1:localStorage.getItem(R)!==null}catch{return!1}}render(){if(!this.shadow)return;let t=this.config.style||"brutalist",n=Y.base,r=Y[t]||Y.brutalist,f=this.config.position==="top"?"top: 0;":"bottom: 0;",L=this.config.primaryColor?`.btn.accept { background: ${this.config.primaryColor} !important; border-color: ${this.config.primaryColor} !important; color: #fff !important; }`:"";this.shadow.innerHTML=`
86
+ `};class qe{constructor(t){M(this,"container",null);M(this,"shadow",null);M(this,"config");if(this.config=t,this.hasStoredConsent()){i("ConsentManager: Consent already exists, not showing banner");return}this.container=document.createElement("div"),this.container.id="balance-consent-manager",this.shadow=this.container.attachShadow({mode:"closed"}),this.render(),document.body.appendChild(this.container),i("ConsentManager: Banner rendered")}hasStoredConsent(){try{return window._balanceConsentNeedsRefresh?!1:localStorage.getItem(A)!==null}catch{return!1}}render(){if(!this.shadow)return;let t=this.config.style||"brutalist",n=G.base,o=G[t]||G.brutalist,f=this.config.position==="top"?"top: 0;":"bottom: 0;",P=this.config.primaryColor?`.btn.accept { background: ${this.config.primaryColor} !important; border-color: ${this.config.primaryColor} !important; color: #fff !important; }`:"";this.shadow.innerHTML=`
87
87
  <style>
88
88
  ${n}
89
89
  :host { ${f} }
90
- ${r}
91
- ${L}
90
+ ${o}
91
+ ${P}
92
92
  </style>
93
93
  <div class="banner" role="dialog" aria-label="Cookie consent" aria-modal="false">
94
94
  <p class="text">
@@ -99,4 +99,4 @@ var BalancePixel=(()=>{var it=Object.defineProperty;var rt=(g,p,u)=>p in g?it(g,
99
99
  <button id="accept" class="btn accept">Accept</button>
100
100
  </div>
101
101
  </div>
102
- `,this.shadow.getElementById("accept")?.addEventListener("click",()=>this.handleConsent(!0)),this.shadow.getElementById("decline")?.addEventListener("click",()=>this.handleConsent(!1))}handleConsent(t){window.balance?.setConsent&&window.balance.setConsent({analytics:t,marketing:t,personalization:t,timestamp:new Date().toISOString()}),t&&!window._balanceInitialPageviewFired&&(window._balanceInitialPageviewFired=!0,window.balance?.page&&window.balance.page(),G(),i("Initial pageview fired after consent granted")),this.remove()}remove(){this.container&&(this.container.remove(),this.container=null,this.shadow=null,i("ConsentManager: Banner removed"))}}function fe(){try{return b==="local"?localStorage:sessionStorage}catch{return null}}function N(e){let t=fe();if(!t)return null;try{let n=x+e,r=t.getItem(n);if(!r&&b==="session")try{r=localStorage.getItem(n)}catch{}return r}catch{return null}}function C(e,t){let n=fe();if(n)try{n.setItem(x+e,t)}catch{}}function ge(){if(b!=="local"){i("Upgrading storage tier: session -> local");try{let e=[];for(let t=0;t<sessionStorage.length;t++){let n=sessionStorage.key(t);n?.startsWith(x)&&e.push(n)}for(let t of e){let n=sessionStorage.getItem(t);n&&localStorage.setItem(t,n)}for(let t of e)sessionStorage.removeItem(t);b="local",i(`Storage tier upgraded, migrated ${e.length} items`)}catch(e){w(" Storage migration failed:",e)}}}function J(){return crypto&&crypto.randomUUID?crypto.randomUUID():"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,e=>{let t=Math.random()*16|0;return(e==="x"?t:t&3|8).toString(16)})}function Be(){try{let e=N(ae),t=N(W);if(e&&t&&Date.now()-parseInt(t,10)<Oe)return C(W,Date.now().toString()),e;let n=J();return C(ae,n),C(W,Date.now().toString()),n}catch{return J()}}function We(){let e=new URLSearchParams(window.location.search),t={};return["source","medium","campaign","content","term"].forEach(n=>{let r=e.get(`utm_${n}`);r&&(t[`utm_${n}`]=r)}),t}function Ve(){try{let e=N(se);if(e){k=JSON.parse(e),i("Loaded attribution:",k);return}let t=We();Object.keys(t).length>0&&(k=t,C(se,JSON.stringify(t)),i("Captured attribution:",k))}catch{}}function qe(){try{m=N(ce)}catch{}}function pe(){if(!s?.analytics)return null;try{let e=localStorage.getItem(x+V);if(e)return e;let t=J();return localStorage.setItem(x+V,t),i("Created persistent visitor ID:",t.substring(0,8)+"..."),t}catch{return null}}function Ke(){if(!s?.analytics){d=null;return}try{d=localStorage.getItem(x+V),d&&i("Loaded visitor ID:",d.substring(0,8)+"...")}catch{}}function Ye(){try{let e=localStorage.getItem(R);if(e){let t=JSON.parse(e);if(t.expiresAt&&Date.now()>t.expiresAt){i("Consent expired - clearing stored consent"),localStorage.removeItem(R),s=null;return}if(t.expiresAt){let n=t.expiresAt-Date.now();n>0&&n<Re&&(i("Consent nearing expiration - will prompt for refresh"),window._balanceConsentNeedsRefresh=!0)}s=t.preferences||null,i("Loaded consent:",s)}}catch{}}function me(e){let t=s;s=e;try{let r={preferences:e,method:"explicit",version:1,expiresAt:Date.now()+Pe};localStorage.setItem(R,JSON.stringify(r)),i("Consent saved with TTL:",e),window._balanceConsentNeedsRefresh=!1}catch(r){w(" Could not save consent:",r)}e.analytics===!0&&(ge(),d||(d=pe()));let n=y({event_name:"consent_updated",metadata:{consent_preferences:e,consent_method:"explicit",previous_consent:t||void 0}});v(n);try{window.dispatchEvent(new CustomEvent("balance:consent:updated",{detail:e})),i("DOM event balance:consent:updated dispatched")}catch{}}function Je(){return s}function Xe(e){return s?.[e]===!0}async function Ge(e){let t=e.toLowerCase().trim(),r=new TextEncoder().encode(t),f=await crypto.subtle.digest("SHA-256",r);return Array.from(new Uint8Array(f)).map(a=>a.toString(16).padStart(2,"0")).join("")}function y(e){let t=Ie(),n={artist_id:H,fan_session_id:A,visitor_id:d||void 0,fan_id_hash:m||void 0,timestamp:new Date().toISOString(),source_url:window.location.href,referrer_url:document.referrer||void 0,user_agent:navigator.userAgent,device_type:t.device_type,browser:t.browser,os:t.os,tracking_source:P,...e,...k};return z&&!e.projectId&&(n.projectId=z),n}function v(e){h.push(e),i("Event queued:",e.event_name,"(queue:",h.length,")"),h.length>=10&&U()}async function U(){if(h.length===0)return;let e=[...h];h=[],i("Flushing",e.length,"events to",O);try{let t=await fetch(O,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({events:e}),keepalive:!0});if(!t.ok)throw new Error(`HTTP ${t.status}`);i("Events sent successfully")}catch(t){if(w(" Failed to send events, trying fallback:",t),O!==de&&!j)try{if((await fetch(de,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({events:e}),keepalive:!0})).ok){i("Events sent via fallback (no geo)");return}}catch(n){w(" Fallback also failed:",n)}h.length<50&&h.push(...e)}}function Qe(){q&&clearInterval(q),q=window.setInterval(()=>{h.length>0&&U()},5e3)}function X(){I||(Me=Date.now()),I=Date.now(),S=!0,i("Active time tracking started/resumed")}function he(){I&&S&&(K+=Date.now()-I),S=!1,i("Active time tracking paused, accumulated:",K,"ms")}function Ze(){let e=K;return S&&I&&(e+=Date.now()-I),e}function et(){ue=Date.now(),E&&(E=!1,X(),i("User returned from idle"))}function be(){if(document.visibilityState==="hidden"){i("Skipping heartbeat - tab hidden");return}if(Date.now()-ue>ze){E||(E=!0,he(),i("User idle - pausing heartbeat"));return}let e=Ze(),t=Math.round(e/1e3),n=y({event_name:"engagement_heartbeat",metadata:{time_on_page_seconds:t,time_on_page_ms:e,heartbeat_interval_ms:$,is_active:S&&!E}});v(n),i("Heartbeat sent:",t,"seconds active")}function G(){if(!te){i('Heartbeat disabled via data-heartbeat="false"');return}_&&clearInterval(_),X(),_=window.setInterval(()=>{be()},$),i("Heartbeat started with interval:",$,"ms")}function tt(){_&&(clearInterval(_),_=null),te&&(be(),i("Heartbeat stopped, final time sent"))}function T(e={}){let t=e.url||window.location.href;if(Ne(t)){i("Page excluded from tracking:",t);return}let n=y({event_name:"pageview",page_title:e.title||document.title,source_url:t});v(n)}function Q(e,t={}){let n=y({event_name:"custom",metadata:{event_type:e,...t}});v(n)}async function we(e,t={}){try{if(s&&s.analytics===!1){i("Identify skipped - user declined analytics consent");return}m=await Ge(e),s?.analytics===!0&&ge(),C(ce,m);let n=e.split("@"),r=n[0].charAt(0)+"***@"+(n[1]||"");i("Fan identified:",{name:t.name||"(no name)",email:r,hash:m.substring(0,16)+"...",traits:t,storageTier:b});let f=y({event_name:"identify",fan_id_hash:m,metadata:{email_sha256:m,email_display:r,traits:t,consent_preferences:s||void 0,storage_tier:b}});v(f)}catch(n){w(" Failed to identify:",n)}}function ye(e,t="USD",n={}){let r=y({event_name:"purchase",metadata:{revenue:e,currency:t,...n}});v(r)}function ve(){P=De(),i("Tracking source detected:",P),Ye(),s?.analytics===!0?(b="local",i("Storage tier: local (analytics consent granted)")):(b="session",i("Storage tier: session (privacy by default)")),A=Be(),qe(),Ke(),s||i(B?"Consent UI enabled, waiting for explicit user consent (Tier 0 mode)":"No consent - operating in privacy-first mode (session storage only, limited tracking)"),s?.analytics&&!d&&(d=pe()),Ve(),Qe(),i("Initialized",{artistId:H,projectId:z||"(none - will track to all projects)",rawProjectId:M||"(none)",sessionId:A,visitorId:d?d.substring(0,8)+"...":null,fanIdHash:m,consent:s,storageTier:b,trackingSource:P,useEmulator:j,endpoint:O}),s?.analytics===!0?(window._balanceInitialPageviewFired=!0,T(),G(),i("Full tracking enabled (user consented)")):B?i("Tracking dormant - waiting for explicit consent via ConsentManager"):(window._balanceInitialPageviewFired=!0,T(),G(),i("Privacy-first tracking enabled (session-scoped, no persistent IDs)")),["mousemove","keydown","scroll","touchstart"].forEach(a=>{document.addEventListener(a,et,{passive:!0})});let e=window.location.hostname;function t(a){try{let o=new URL(a).hostname.toLowerCase();return o.includes("spotify")?"spotify":o.includes("apple")||o.includes("music.apple")?"apple_music":o.includes("youtube")||o.includes("youtu.be")?"youtube":o.includes("soundcloud")?"soundcloud":o.includes("tidal")?"tidal":o.includes("deezer")?"deezer":o.includes("amazon")||o.includes("music.amazon")?"amazon_music":o.includes("bandcamp")?"bandcamp":o.includes("lnk.to")||o.includes("linkfire")?"linkfire":o.includes("linktr.ee")?"linktree":o.includes("shop")||o.includes("store")||o.includes("merch")?"shop":o.includes("ticketmaster")||o.includes("eventbrite")||o.includes("dice.fm")?"tickets":o.includes("instagram")?"instagram":o.includes("twitter")||o.includes("x.com")?"twitter":o.includes("tiktok")?"tiktok":o.includes("facebook")?"facebook":"external"}catch{return"external"}}function n(a){try{return new URL(a,window.location.origin).hostname!==e}catch{return!1}}function r(a,o){let c=t(a),D=o?.textContent?.trim().substring(0,100)||"",nt=o?.id||o?.getAttribute("data-link-id")||void 0;i("External link clicked:",{url:a,platform:c,linkText:D}),Q("page_link_click",{link_url:a,link_text:D,link_id:nt,platform:c,link_type:"external"})}let f=window.open;if(window.open=function(a,o,c){if(a){let D=a.toString();n(D)&&r(D)}return f.call(window,a,o,c)},i("window.open interception enabled (always on)"),l?.dataset.autoTrackLinks==="true"&&(document.addEventListener("click",a=>{let c=a.target.closest("a");if(c&&c.href&&n(c.href)){if(oe&&(le(c.href)||c.hasAttribute("download")))return;r(c.href,c)}},{capture:!0,passive:!0}),i("External link click listener enabled (opt-in)")),oe&&(document.addEventListener("click",a=>{let c=a.target.closest("a");c?.href&&(le(c.href)||c.hasAttribute("download"))&&Fe(c.href,c)},{capture:!0,passive:!0}),i("File download tracking enabled")),Le){let a=window.location.href;window.addEventListener("hashchange",()=>{let o=window.location.href;o!==a&&(a=o,setTimeout(()=>{T({title:document.title,url:o})},0))}),i("Hash routing tracking enabled")}window.addEventListener("beforeunload",()=>{tt(),U()}),document.addEventListener("visibilitychange",()=>{document.visibilityState==="hidden"?(he(),U()):X()})}if(window.balance?._version&&window.balance._version!==g){console.warn(`[BLN] Version conflict: ${window.balance._version} already loaded, skipping ${g}`);return}let Z=window.balance?.q||[];function xe(){if(Z.length>0){i("Processing",Z.length,"queued commands");for(let e of Z){let[t,...n]=e;switch(t){case"track":Q(n[0],n[1]);break;case"identify":we(n[0],n[1]);break;case"page":T(n[0]);break;case"purchase":ye(n[0],n[1],n[2]);break;case"setConsent":me(n[0]);break;default:i("Unknown queued command:",t)}}}}window.balance={track:Q,identify:we,page:T,purchase:ye,getSessionId:()=>A,getVisitorId:()=>d,getFanIdHash:()=>m,getAttribution:()=>k,setConsent:me,getConsent:Je,hasConsent:Xe,_version:g};function ke(){B&&!s&&new $e({style:Ee,primaryColor:Ce,position:Te})}let _e=window.requestIdleCallback||(e=>setTimeout(e,1));document.readyState==="loading"?document.addEventListener("DOMContentLoaded",()=>{ve(),xe(),_e(()=>ke())}):(ve(),xe(),_e(()=>ke())),i("Pixel script loaded")})();})();
102
+ `,this.shadow.getElementById("accept")?.addEventListener("click",()=>this.handleConsent(!0)),this.shadow.getElementById("decline")?.addEventListener("click",()=>this.handleConsent(!1))}handleConsent(t){window.balance?.setConsent&&window.balance.setConsent({analytics:t,marketing:t,personalization:t,timestamp:new Date().toISOString()}),t&&!window._balanceInitialPageviewFired&&(window._balanceInitialPageviewFired=!0,window.balance?.page&&window.balance.page(),ee(),i("Initial pageview fired after consent granted")),this.remove()}remove(){this.container&&(this.container.remove(),this.container=null,this.shadow=null,i("ConsentManager: Banner removed"))}}function he(){try{return b==="local"?localStorage:sessionStorage}catch{return null}}function U(e){let t=he();if(!t)return null;try{let n=x+e,o=t.getItem(n);if(!o&&b==="session")try{o=localStorage.getItem(n)}catch{}return o}catch{return null}}function T(e,t){let n=he();if(n)try{n.setItem(x+e,t)}catch{}}function be(){if(b!=="local"){i("Upgrading storage tier: session -> local");try{let e=[];for(let t=0;t<sessionStorage.length;t++){let n=sessionStorage.key(t);n?.startsWith(x)&&e.push(n)}for(let t of e){let n=sessionStorage.getItem(t);n&&localStorage.setItem(t,n)}for(let t of e)sessionStorage.removeItem(t);b="local",i(`Storage tier upgraded, migrated ${e.length} items`)}catch(e){v(" Storage migration failed:",e)}}}function Q(){return crypto&&crypto.randomUUID?crypto.randomUUID():"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,e=>{let t=Math.random()*16|0;return(e==="x"?t:t&3|8).toString(16)})}function Ve(){try{let e=U(de),t=U(W);if(e&&t&&Date.now()-parseInt(t,10)<Me)return T(W,Date.now().toString()),e;let n=Q();return T(de,n),T(W,Date.now().toString()),n}catch{return Q()}}function Ke(){let e=new URLSearchParams(window.location.search),t={};return["source","medium","campaign","content","term"].forEach(n=>{let o=e.get(`utm_${n}`);o&&(t[`utm_${n}`]=o)}),t}function Ye(){try{let e=U(ue);if(e){_=JSON.parse(e),i("Loaded attribution:",_);return}let t=Ke();Object.keys(t).length>0&&(_=t,T(ue,JSON.stringify(t)),i("Captured attribution:",_))}catch{}}function Je(){try{m=U(fe)}catch{}}function we(){if(!s?.analytics)return null;try{let e=localStorage.getItem(x+q);if(e)return e;let t=Q();return localStorage.setItem(x+q,t),i("Created persistent visitor ID:",t.substring(0,8)+"..."),t}catch{return null}}function Xe(){if(!s?.analytics){d=null;return}try{d=localStorage.getItem(x+q),d&&i("Loaded visitor ID:",d.substring(0,8)+"...")}catch{}}function Ge(){try{let e=localStorage.getItem(A);if(e){let t=JSON.parse(e);if(t.expiresAt&&Date.now()>t.expiresAt){i("Consent expired - clearing stored consent"),localStorage.removeItem(A),s=null;return}if(t.expiresAt){let n=t.expiresAt-Date.now();n>0&&n<Ue&&(i("Consent nearing expiration - will prompt for refresh"),window._balanceConsentNeedsRefresh=!0)}s=t.preferences||null,i("Loaded consent:",s)}}catch{}}function ye(e){let t=s;s=e;try{let o={preferences:e,method:"explicit",version:1,expiresAt:Date.now()+Ne};localStorage.setItem(A,JSON.stringify(o)),i("Consent saved with TTL:",e),window._balanceConsentNeedsRefresh=!1}catch(o){v(" Could not save consent:",o)}e.analytics===!0&&(be(),d||(d=we()));let n=w({event_name:"consent_updated",metadata:{consent_preferences:e,consent_method:"explicit",previous_consent:t||void 0}});y(n);try{window.dispatchEvent(new CustomEvent("balance:consent:updated",{detail:e})),i("DOM event balance:consent:updated dispatched")}catch{}}function Qe(){return s}function Ze(e){return s?.[e]===!0}async function et(e){let t=e.toLowerCase().trim(),o=new TextEncoder().encode(t),f=await crypto.subtle.digest("SHA-256",o);return Array.from(new Uint8Array(f)).map(r=>r.toString(16).padStart(2,"0")).join("")}function w(e){let t=Te(),n={artist_id:F,fan_session_id:N,visitor_id:d||void 0,fan_id_hash:m||void 0,timestamp:new Date().toISOString(),source_url:window.location.href,referrer_url:document.referrer||void 0,user_agent:navigator.userAgent,device_type:t.device_type,browser:t.browser,os:t.os,tracking_source:O,...e,..._};return z&&!e.projectId&&(n.projectId=z),n}function y(e){h.push(e),i("Event queued:",e.event_name,"(queue:",h.length,")"),h.length>=10&&L()}async function L(){if(h.length===0)return;let e=[...h];h=[],i("Flushing",e.length,"events to",k);try{let t=await fetch(k,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({events:e}),keepalive:!0});if(!t.ok)throw new Error(`HTTP ${t.status}`);i("Events sent successfully")}catch(t){if(v(" Failed to send events, trying fallback:",t),k!==pe&&!B)try{if((await fetch(pe,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({events:e}),keepalive:!0})).ok){i("Events sent via fallback (no geo)");return}}catch(n){v(" Fallback also failed:",n)}h.length<50&&h.push(...e)}}function tt(){V&&clearInterval(V),V=window.setInterval(()=>{h.length>0&&L()},5e3)}function Z(){S||(K=Date.now()),S=Date.now(),E=!0,i("Active time tracking started/resumed")}function ve(){S&&E&&(Y+=Date.now()-S),E=!1,i("Active time tracking paused, accumulated:",Y,"ms")}function xe(){let e=Y;return E&&S&&(e+=Date.now()-S),e}function nt(){me=Date.now(),C&&(C=!1,Z(),i("User returned from idle"))}function it(){if(document.visibilityState==="hidden"){i("Skipping heartbeat - tab hidden");return}if(Date.now()-me>$e){C||(C=!0,ve(),i("User idle - pausing heartbeat"));return}let e=xe(),t=Math.round(e/1e3),n=w({event_name:"engagement_heartbeat",metadata:{time_on_page_seconds:t,time_on_page_ms:e,heartbeat_interval_ms:j,is_active:E&&!C}});y(n),J++,i("Heartbeat sent:",t,"seconds active")}function ee(){if(!ae){i('Heartbeat disabled via data-heartbeat="false"');return}I&&clearInterval(I),Z(),I=window.setInterval(()=>{it()},j),i("Heartbeat started with interval:",j,"ms")}function te(){if(!X){if(X=!0,I&&(clearInterval(I),I=null),ae){let e=K?Date.now()-K:0,t=xe(),n=w({event_name:"engagement_summary",metadata:{total_time_ms:e,active_time_ms:t,heartbeat_samples:J,page_path:window.location.pathname}});if(navigator.sendBeacon&&k){let o=JSON.stringify({events:[n]});navigator.sendBeacon(k,o),i("Engagement summary sent via sendBeacon:",Math.round(t/1e3),"seconds active")}else y(n),i("Engagement summary enqueued:",Math.round(t/1e3),"seconds active")}J=0}}function D(e={}){let t=e.url||window.location.href;if(He(t)){i("Page excluded from tracking:",t);return}let n=w({event_name:"pageview",page_title:e.title||document.title,source_url:t});y(n)}function ne(e,t={}){let n=w({event_name:"custom",metadata:{event_type:e,...t}});y(n)}async function ke(e,t={}){try{if(s&&s.analytics===!1){i("Identify skipped - user declined analytics consent");return}m=await et(e),s?.analytics===!0&&be(),T(fe,m);let n=e.split("@"),o=n[0].charAt(0)+"***@"+(n[1]||"");i("Fan identified:",{name:t.name||"(no name)",email:o,hash:m.substring(0,16)+"...",traits:t,storageTier:b});let f=w({event_name:"identify",fan_id_hash:m,metadata:{email_sha256:m,email_display:o,traits:t,consent_preferences:s||void 0,storage_tier:b}});y(f)}catch(n){v(" Failed to identify:",n)}}function _e(e,t="USD",n={}){let o=w({event_name:"purchase",metadata:{revenue:e,currency:t,...n}});y(o)}function Ie(){O=Ae(),i("Tracking source detected:",O),Ge(),s?.analytics===!0?(b="local",i("Storage tier: local (analytics consent granted)")):(b="session",i("Storage tier: session (privacy by default)")),N=Ve(),Je(),Xe(),s||i($?"Consent UI enabled, waiting for explicit user consent (Tier 0 mode)":"No consent - operating in privacy-first mode (session storage only, limited tracking)"),s?.analytics&&!d&&(d=we()),Ye(),tt(),i("Initialized",{artistId:F,projectId:z||"(none - will track to all projects)",rawProjectId:H||"(none)",sessionId:N,visitorId:d?d.substring(0,8)+"...":null,fanIdHash:m,consent:s,storageTier:b,trackingSource:O,useEmulator:B,endpoint:k}),s?.analytics===!0?(window._balanceInitialPageviewFired=!0,D(),ee(),i("Full tracking enabled (user consented)")):$?i("Tracking dormant - waiting for explicit consent via ConsentManager"):(window._balanceInitialPageviewFired=!0,D(),ee(),i("Privacy-first tracking enabled (session-scoped, no persistent IDs)")),["mousemove","keydown","scroll","touchstart"].forEach(r=>{document.addEventListener(r,nt,{passive:!0})});let e=window.location.hostname;function t(r){try{let a=new URL(r).hostname.toLowerCase();return a.includes("spotify")?"spotify":a.includes("apple")||a.includes("music.apple")?"apple_music":a.includes("youtube")||a.includes("youtu.be")?"youtube":a.includes("soundcloud")?"soundcloud":a.includes("tidal")?"tidal":a.includes("deezer")?"deezer":a.includes("amazon")||a.includes("music.amazon")?"amazon_music":a.includes("bandcamp")?"bandcamp":a.includes("lnk.to")||a.includes("linkfire")?"linkfire":a.includes("linktr.ee")?"linktree":a.includes("shop")||a.includes("store")||a.includes("merch")?"shop":a.includes("ticketmaster")||a.includes("eventbrite")||a.includes("dice.fm")?"tickets":a.includes("instagram")?"instagram":a.includes("twitter")||a.includes("x.com")?"twitter":a.includes("tiktok")?"tiktok":a.includes("facebook")?"facebook":"external"}catch{return"external"}}function n(r){try{return new URL(r,window.location.origin).hostname!==e}catch{return!1}}function o(r,a){let c=t(r),R=a?.textContent?.trim().substring(0,100)||"",ot=a?.id||a?.getAttribute("data-link-id")||void 0;i("External link clicked:",{url:r,platform:c,linkText:R}),ne("page_link_click",{link_url:r,link_text:R,link_id:ot,platform:c,link_type:"external"})}let f=window.open;if(window.open=function(r,a,c){if(r){let R=r.toString();n(R)&&o(R)}return f.call(window,r,a,c)},i("window.open interception enabled (always on)"),l?.dataset.autoTrackLinks==="true"&&(document.addEventListener("click",r=>{let c=r.target.closest("a");if(c&&c.href&&n(c.href)){if(le&&(ge(c.href)||c.hasAttribute("download")))return;o(c.href,c)}},{capture:!0,passive:!0}),i("External link click listener enabled (opt-in)")),le&&(document.addEventListener("click",r=>{let c=r.target.closest("a");c?.href&&(ge(c.href)||c.hasAttribute("download"))&&Be(c.href,c)},{capture:!0,passive:!0}),i("File download tracking enabled")),Oe){let r=window.location.href;window.addEventListener("hashchange",()=>{let a=window.location.href;a!==r&&(r=a,setTimeout(()=>{D({title:document.title,url:a})},0))}),i("Hash routing tracking enabled")}document.addEventListener("visibilitychange",()=>{document.visibilityState==="hidden"?(ve(),te(),L()):(Z(),X=!1)}),window.addEventListener("pagehide",()=>{te(),L()}),window.addEventListener("beforeunload",()=>{te(),L()})}if(window.balance?._version&&window.balance._version!==g){console.warn(`[BLN] Version conflict: ${window.balance._version} already loaded, skipping ${g}`);return}let ie=window.balance?.q||[];function Se(){if(ie.length>0){i("Processing",ie.length,"queued commands");for(let e of ie){let[t,...n]=e;switch(t){case"track":ne(n[0],n[1]);break;case"identify":ke(n[0],n[1]);break;case"page":D(n[0]);break;case"purchase":_e(n[0],n[1],n[2]);break;case"setConsent":ye(n[0]);break;default:i("Unknown queued command:",t)}}}}window.balance={track:ne,identify:ke,page:D,purchase:_e,getSessionId:()=>N,getVisitorId:()=>d,getFanIdHash:()=>m,getAttribution:()=>_,setConsent:ye,getConsent:Qe,hasConsent:Ze,_version:g};function Ee(){$&&!s&&new qe({style:De,primaryColor:Pe,position:Re})}let Ce=window.requestIdleCallback||(e=>setTimeout(e,1));document.readyState==="loading"?document.addEventListener("DOMContentLoaded",()=>{Ie(),Se(),Ce(()=>Ee())}):(Ie(),Se(),Ce(()=>Ee())),i("Pixel script loaded")})();})();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hifilabs/pixel",
3
- "version": "0.9.0",
3
+ "version": "0.9.1",
4
4
  "description": "BALANCE Pixel - Lightweight browser tracking script for artist fan analytics",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",