@behindthescenes/analytics 0.0.2 → 0.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- var D=["page_view","view_content","search","lead","sign_up","begin_checkout","add_payment_info","purchase","outbound_click","button_click","form_submit"],O=new Set(D),R=new Set(["lead","sign_up","begin_checkout","add_payment_info","purchase"]),T={$pageview:"page_view",checkout_started:"begin_checkout",lead_capture:"lead",purchase_completed:"purchase",registration_complete:"sign_up"};function z(c){return O.has(c)}function J(c){let B=c.trim();if(B.length===0)return{eventName:B,canonicalEventName:B,isStandard:!1};let S=B.toLowerCase(),A=T[S];if(A)return{eventName:A,canonicalEventName:A,aliasOf:A,isStandard:!0};return{eventName:B,canonicalEventName:B,isStandard:z(B)}}function Q(c,B){if(B)return B;if(c==="page_view")return"page_view";if(c==="identify")return"identify";if(R.has(c))return"conversion";return"custom"}function Z(){return[...D]}var H="bts_analytics_vid",G="bts_analytics_sid",P="bts_analytics_jt",Y="bts_analytics_jid",U="bts_analytics_attr",g="bts_site",m="bts_jt",v=300,x="https://api.bts.dev/v2/website/analytics",l={pageviews:!0,history:!0,outboundLinks:!0,buttonClicks:!0,formSubmissions:!0,search:!0,viewContent:!0},y=["q","query","search"],p=["utm_source","utm_medium","utm_campaign","utm_term","utm_content","fbclid","gclid","gbraid","li_fat_id","msclkid","ttclid","wbraid"];function N(){return typeof window>"u"?null:window}function M(){return typeof document>"u"?null:document}function $(){return N()?.localStorage??null}function w(){if(typeof crypto<"u"&&typeof crypto.randomUUID==="function")return crypto.randomUUID();return`v_${Math.random().toString(36).slice(2)}${Date.now().toString(36)}`}function L(c){return c!==null&&typeof c==="object"&&!Array.isArray(c)}function h(c){if(!c)return null;try{let B=JSON.parse(c);if(!L(B))return null;let S=L(B.utm)?Object.fromEntries(Object.entries(B.utm).filter((_)=>typeof _[1]==="string")):{},A=L(B.clickIds)?Object.fromEntries(Object.entries(B.clickIds).filter((_)=>typeof _[1]==="string")):{};return{utm:S,clickIds:A,landingUrl:typeof B.landingUrl==="string"?B.landingUrl:void 0,lastSeenAt:typeof B.lastSeenAt==="string"?B.lastSeenAt:new Date().toISOString(),referrer:typeof B.referrer==="string"?B.referrer:void 0}}catch{return null}}function I(){let c=$();return h(c?.getItem(U)??null)}function b(c){let B=$();if(!B)return;B.setItem(U,JSON.stringify(c))}function C(){let c=N();if(!c)return;return`${c.location.pathname}${c.location.search}`}function k(){return N()?.location.href}function K(){return N()?.location.origin??"https://behindthescenes.com"}function f(){return M()?.referrer||void 0}function d(){let c=N();if(!c)return;let B=typeof Intl<"u"?V(Intl.DateTimeFormat().resolvedOptions().timeZone):void 0,S=c.navigator,A=V(S?.language),_=Array.isArray(S?.languages)?S.languages.filter((F)=>typeof F==="string").slice(0,10):[],q=V(S?.userAgent),E={timezone:B,language:A,languages:_.length>0?_:void 0,userAgent:q};return Object.values(E).some((F)=>F!==void 0)?E:void 0}function W(){let c=N();if(!c)return I();let B=new URLSearchParams(c.location.search),S={utm:{},clickIds:{},landingUrl:c.location.href,lastSeenAt:new Date().toISOString(),referrer:f()};for(let q of p){let E=B.get(q);if(!E)continue;if(q.startsWith("utm_")){S.utm[q]=E;continue}S.clickIds[q]=E}let A=I(),_={utm:{...A?.utm??{},...S.utm},clickIds:{...A?.clickIds??{},...S.clickIds},landingUrl:S.landingUrl??A?.landingUrl,lastSeenAt:S.lastSeenAt,referrer:S.referrer??A?.referrer};return b(_),_}function V(c){let B=c?.trim();return B?B.slice(0,512):void 0}function u(c){if(!c)return{};if(c instanceof Headers){let B={};return c.forEach((S,A)=>{B[A]=S}),B}if(Array.isArray(c))return Object.fromEntries(c.map(([B,S])=>[B,String(S)]));return Object.fromEntries(Object.entries(c).map(([B,S])=>[B,String(S)]))}function s(c){let B={...l};if(c.autoTrack===!1)return{pageviews:!1,history:!1,outboundLinks:!1,buttonClicks:!1,formSubmissions:!1,search:!1,viewContent:!1};if(c.autoTrack&&typeof c.autoTrack==="object")return{pageviews:c.autoTrack.pageviews??B.pageviews,history:c.autoTrack.pageviews===!1?!1:c.autoTrack.history??B.history,outboundLinks:c.autoTrack.outboundLinks??B.outboundLinks,buttonClicks:c.autoTrack.buttonClicks??B.buttonClicks,formSubmissions:c.autoTrack.formSubmissions??B.formSubmissions,search:c.autoTrack.search??B.search,viewContent:c.autoTrack.viewContent??B.viewContent};if(c.autoPageviews===!1)return{...B,pageviews:!1,history:!1};if(c.autoPageviews===!0)return{...B,pageviews:!0};return B}class X{siteKey;endpoint;debug;flushIntervalMs;autoTrack;requestHeaders;queue=[];flushTimer=null;destroyed=!1;lastTrackedUrl=null;unpatchHistory=null;clickHandler=null;submitHandler=null;pagehideHandler=null;popstateHandler=null;viewContentObserver=null;observedViewContentElements=new WeakSet;trackedViewContentElements=new WeakSet;constructor(c){if(this.siteKey=c.siteKey,this.endpoint=(c.endpoint??x).replace(/\/$/,""),this.debug=c.debug??!1,this.flushIntervalMs=c.flushIntervalMs??v,this.autoTrack=s(c),this.requestHeaders=c.requestHeaders,W(),N())this.installAutoTracking()}static init(c){return new X(c)}getVisitorId(){let c=$(),B=c?.getItem(H);if(B)return B;let S=w();return c?.setItem(H,S),S}getSessionId(){let c=$(),B=c?.getItem(G);if(B)return B;let S=w();return c?.setItem(G,S),S}getPersistedJourneyId(){return $()?.getItem(Y)??null}log(...c){if(this.debug)console.log("[@bts/analytics]",...c)}installAutoTracking(){let c=N(),B=M();if(!c||!B)return;if(this.autoTrack.pageviews)this.recordPageView();if(this.autoTrack.history)this.unpatchHistory=this.patchHistory(c),this.popstateHandler=()=>{this.recordPageView()},c.addEventListener("popstate",this.popstateHandler);if(this.clickHandler=(S)=>{this.handleAutoClick(S)},this.submitHandler=(S)=>{this.handleAutoSubmit(S)},this.pagehideHandler=()=>{this.flushWithBeacon()},this.autoTrack.outboundLinks||this.autoTrack.buttonClicks)B.addEventListener("click",this.clickHandler,!0);if(this.autoTrack.formSubmissions||this.autoTrack.search)B.addEventListener("submit",this.submitHandler,!0);if(this.autoTrack.viewContent)this.installViewContentTracking(B);c.addEventListener("pagehide",this.pagehideHandler)}installViewContentTracking(c){if(typeof IntersectionObserver>"u")return;let B=Array.from(c.querySelectorAll("[data-bts-view-content]"));if(B.length===0)return;this.viewContentObserver=new IntersectionObserver((S,A)=>{for(let _ of S){if(!_.isIntersecting||!(_.target instanceof Element))continue;this.trackViewContentElement(_.target),A.unobserve(_.target)}});for(let S of B){if(this.observedViewContentElements.has(S))continue;this.observedViewContentElements.add(S),this.viewContentObserver.observe(S)}}trackViewContentElement(c){if(this.trackedViewContentElements.has(c))return;this.trackedViewContentElements.add(c);let B=V(c.getAttribute("data-bts-view-content")||c.getAttribute("data-bts-content-id")||c.id);this.trackStandard("view_content",{autoCaptured:!0,contentId:B,contentTitle:V(c.getAttribute("data-bts-content-title")||c.textContent),contentType:V(c.getAttribute("data-bts-content-type")),elementId:V(c.id)})}patchHistory(c){let B=c.history,S=B.pushState.bind(B),A=B.replaceState.bind(B),_=()=>{W(),this.recordPageView()};return B.pushState=(...q)=>{S(...q),_()},B.replaceState=(...q)=>{A(...q),_()},()=>{B.pushState=S,B.replaceState=A}}handleAutoClick(c){if(c.defaultPrevented)return;let B=c.target;if(!(B instanceof Element))return;if(this.autoTrack.outboundLinks){let A=B.closest("a[href]");if(A instanceof HTMLAnchorElement){let _=V(A.href);if(!_)return;let q=new URL(_,K());if(q.origin!==K()){this.trackStandard("outbound_click",{elementHref:_,elementId:V(A.id),elementText:V(A.textContent),linkHost:q.hostname});return}}}if(!this.autoTrack.buttonClicks)return;let S=B.closest("button,[role='button'],[data-bts-track-click]");if(!(S instanceof Element))return;this.trackStandard("button_click",{elementId:V(S.id),elementName:V(S.getAttribute("name")),elementRole:V(S.getAttribute("role")),elementText:V(S.textContent)})}handleAutoSubmit(c){if(c.defaultPrevented||!this.autoTrack.formSubmissions&&!this.autoTrack.search)return;let B=c.target;if(!(B instanceof HTMLFormElement))return;if(this.autoTrack.search)this.trackSearchForm(B);if(!this.autoTrack.formSubmissions)return;this.trackStandard("form_submit",{formAction:V(B.action),formId:V(B.id),formMethod:V(B.method),formName:V(B.getAttribute("name"))})}trackSearchForm(c){if((c.method||"get").toLowerCase()!=="get")return;let S=this.extractSearchQuery(c);if(!S)return;this.trackStandard("search",{autoCaptured:!0,formAction:V(c.action),formId:V(c.id),formName:V(c.getAttribute("name")),queryKey:S.key,searchQuery:S.value})}extractSearchQuery(c){try{let S=new FormData(c);for(let A of y){let _=S.get(A);if(typeof _==="string"){let q=V(_);if(q)return{key:A,value:q}}}}catch{}let B=c.elements;for(let S of y){let A=B?.namedItem?.(S),_=A&&"value"in A?A.value:void 0;if(typeof _==="string"){let q=V(_);if(q)return{key:S,value:q}}}return null}buildEvent(c,B,S,A){let _=W(),q=A?.path??C(),E=f();return{eventName:c,eventType:B,path:q,referrer:E,occurredAt:new Date().toISOString(),journeyId:this.getPersistedJourneyId()??void 0,visitorId:this.getVisitorId(),sessionId:this.getSessionId(),properties:{...S??{},attribution:_?{utm:_.utm,clickIds:_.clickIds,landingUrl:_.landingUrl,referrer:_.referrer}:void 0,client:d(),page:{title:M()?.title,url:k()}}}}queueEvent(c,B,S){let A=J(c),_=Q(A.canonicalEventName,S),q={...B??{}};if(A.aliasOf)q.originalEventName=c;if(this.queue.push(this.buildEvent(A.eventName,_,q)),this.queue.length>=50){this.flushNow();return}this.scheduleFlush()}scheduleFlush(){if(this.flushTimer)return;this.flushTimer=setTimeout(()=>{this.flushTimer=null,this.flushNow()},this.flushIntervalMs)}async postJson(c,B){let S=`${this.endpoint}${c}`;this.log("POST",S,B);let A=JSON.stringify(B),_=await this.resolveRequestHeaders(c,S,B,A);return fetch(S,{method:"POST",headers:_,body:A})}async postWebsiteJson(c,B){let A=`${this.endpoint.replace(/\/analytics$/,"")}${c}`;this.log("POST",A,B);let _=JSON.stringify(B),q=await this.resolveRequestHeaders(c,A,B,_);return fetch(A,{method:"POST",headers:q,body:_})}async postJsonKeepalive(c,B){let S=`${this.endpoint}${c}`;this.log("POST keepalive",S,B);let A=JSON.stringify(B),_=await this.resolveRequestHeaders(c,S,B,A);return fetch(S,{method:"POST",headers:_,body:A,keepalive:!0})}async resolveRequestHeaders(c,B,S,A){let _={"Content-Type":"application/json"};if(!this.requestHeaders)return _;let q=typeof this.requestHeaders==="function"?await this.requestHeaders({body:S,bodyText:A,endpoint:this.endpoint,headers:{..._},path:c,siteKey:this.siteKey,url:B}):this.requestHeaders;return{..._,...u(q)}}async flushBatch(c,B=!1){try{let S=B?await this.postJsonKeepalive("/ingest/batch",{siteKey:this.siteKey,events:c}):await this.postJson("/ingest/batch",{siteKey:this.siteKey,events:c});if(!S.ok)return this.log("flush failed",S.status),!1;return!0}catch(S){return this.log("flush error",S),!1}}flushWithBeacon(){if(this.queue.length===0)return;if(this.requestHeaders){let E=[...this.queue];this.queue=[],this.flushBatch(E,!0).then((F)=>{if(!F&&!this.destroyed)this.queue.unshift(...E),this.scheduleFlush()});return}let c=N(),B=c?.navigator?.sendBeacon?.bind(c.navigator);if(!B)return;let S=[...this.queue];this.queue=[];let A=`${this.endpoint}/ingest/batch`,_=JSON.stringify({siteKey:this.siteKey,events:S}),q=new Blob([_],{type:"application/json"});B(A,q)}async flushNow(){if(this.queue.length===0)return;let c=[...this.queue];if(this.queue=[],!await this.flushBatch(c)&&!this.destroyed)this.queue.unshift(...c),this.scheduleFlush()}recordPageView(c){let B=c??C(),S=k()??B??"/";if(S===this.lastTrackedUrl)return;this.lastTrackedUrl=S,this.queue.push(this.buildEvent("page_view","page_view",{autoCaptured:!0},{path:B})),this.scheduleFlush()}page(c){this.lastTrackedUrl=null,this.recordPageView(c)}track(c,B){this.queueEvent(c,B)}trackStandard(c,B){this.queueEvent(c,B)}identify(c,B){this.queue.push(this.buildEvent("identify","identify",{traits:B??{},userId:c})),this.scheduleFlush()}listStandardEvents(){return Z()}async submitContactForm(c){let B=await this.postWebsiteJson("/ghl/leads",{siteKey:this.siteKey,locationId:c.locationId,email:c.email,phone:c.phone,subject:c.subject,body:c.body,name:c.name,firstName:c.firstName,lastName:c.lastName,source:c.source,tags:c.tags,customFields:c.customFields,metadata:c.metadata});if(!B.ok)throw Error(`contact form submit failed: ${B.status}`);return await B.json()}async startFunnel(c){let B=await this.postJson("/journey/start",{siteKey:this.siteKey,entryPath:c??C()});if(!B.ok)throw Error(`journey/start failed: ${B.status}`);let S=await B.json();return $()?.setItem(P,S.journeyToken),$()?.setItem(Y,S.journeyId),S}getPersistedJourneyToken(){return $()?.getItem(P)??null}decorateUrl(c,B){let S=B??this.getPersistedJourneyToken(),A=new URL(c,K());if(A.searchParams.set(g,this.siteKey),S)A.searchParams.set(m,S);return A.toString()}async notifyHandoff(c,B){let S=await this.postJson("/journey/handoff",{journeyToken:c,context:B});if(!S.ok)throw Error(`journey/handoff failed: ${S.status}`)}destroy(){this.destroyed=!0;let c=N(),B=M();if(B&&this.clickHandler&&(this.autoTrack.outboundLinks||this.autoTrack.buttonClicks))B.removeEventListener("click",this.clickHandler,!0);if(B&&this.submitHandler&&(this.autoTrack.formSubmissions||this.autoTrack.search))B.removeEventListener("submit",this.submitHandler,!0);if(this.viewContentObserver?.disconnect(),this.viewContentObserver=null,c&&this.popstateHandler)c.removeEventListener("popstate",this.popstateHandler);if(c&&this.pagehideHandler)c.removeEventListener("pagehide",this.pagehideHandler);if(this.flushTimer)clearTimeout(this.flushTimer),this.flushTimer=null;this.unpatchHistory?.(),this.unpatchHistory=null,this.flushWithBeacon()}}function j(c){return X.init(c)}if(typeof window<"u")window.BTSAnalytics={BTSAnalytics:X,createBTSAnalytics:j,listStandardWebEvents:Z},window.createBTSAnalytics=j;export{Z as listStandardWebEvents,j as createBTSAnalytics,X as BTSAnalytics};
1
+ var J=["page_view","view_content","search","lead","sign_up","begin_checkout","add_payment_info","purchase","outbound_click","button_click","form_submit"],g=new Set(J),u=new Set(["lead","sign_up","begin_checkout","add_payment_info","purchase"]),v={$pageview:"page_view",checkout_started:"begin_checkout",lead_capture:"lead",purchase_completed:"purchase",registration_complete:"sign_up"};function m(S){return g.has(S)}function X(S){let A=S.trim();if(A.length===0)return{eventName:A,canonicalEventName:A,isStandard:!1};let f=A.toLowerCase(),c=v[f];if(c)return{eventName:c,canonicalEventName:c,aliasOf:c,isStandard:!0};return{eventName:A,canonicalEventName:A,isStandard:m(A)}}function Z(S,A){if(A)return A;if(S==="page_view")return"page_view";if(S==="identify")return"identify";if(u.has(S))return"conversion";return"custom"}function C(){return[...J]}var j="bts_analytics_vid",G="bts_analytics_sid",M="bts_analytics_jt",O="bts_analytics_jid",U="bts_analytics_attr",b="bts_site",h="bts_jt",l=300,s="https://api.bts.dev/v2/website/analytics",d={pageviews:!0,history:!0,outboundLinks:!0,buttonClicks:!0,formSubmissions:!0,search:!0,viewContent:!0},Q=["q","query","search"],p=["utm_source","utm_medium","utm_campaign","utm_term","utm_content","fbclid","gclid","gbraid","li_fat_id","msclkid","ttclid","wbraid"],a=["_fbp","_fbc"];function N(){return typeof window>"u"?null:window}function D(){return typeof document>"u"?null:document}function L(){return N()?.localStorage??null}function V(){if(typeof crypto<"u"&&typeof crypto.randomUUID==="function")return crypto.randomUUID();return`v_${Math.random().toString(36).slice(2)}${Date.now().toString(36)}`}function q(S){return S!==null&&typeof S==="object"&&!Array.isArray(S)}function n(S){if(!S)return null;try{let A=JSON.parse(S);if(!q(A))return null;let f=q(A.utm)?Object.fromEntries(Object.entries(A.utm).filter((B)=>typeof B[1]==="string")):{},c=q(A.clickIds)?Object.fromEntries(Object.entries(A.clickIds).filter((B)=>typeof B[1]==="string")):{};return{utm:f,clickIds:c,landingUrl:typeof A.landingUrl==="string"?A.landingUrl:void 0,lastSeenAt:typeof A.lastSeenAt==="string"?A.lastSeenAt:new Date().toISOString(),referrer:typeof A.referrer==="string"?A.referrer:void 0}}catch{return null}}function K(){let S=L();return n(S?.getItem(U)??null)}function o(S){let A=L();if(!A)return;A.setItem(U,JSON.stringify(S))}function F(){let S=N();if(!S)return;return`${S.location.pathname}${S.location.search}`}function H(){return N()?.location.href}function R(){return N()?.location.origin??"https://behindthescenes.com"}function P(){return D()?.referrer||void 0}function z(S){let A=D();if(!A?.cookie)return;let f=`${S}=`,c=A.cookie.split(";").map((E)=>E.trim()).find((E)=>E.startsWith(f));if(!c)return;let B=c.slice(f.length);if(B==="")return;try{return decodeURIComponent(B)}catch{return B}}function r(){let S=z("_ga");if(!S)return;let A=S.split(".");if(A.length>=4&&/^GA\d+$/i.test(A[0]??""))return A.slice(-2).join(".");return _(S)}function t(){let A=D()?.querySelector?.('script[src*="googletagmanager.com/gtag/js?id="]'),f=A&&"src"in A&&typeof A.src==="string"?A.src:void 0;if(!f)return;try{return _(new URL(f).searchParams.get("id"))}catch{return}}function i(){let S=N(),A=r(),f=t(),c=typeof S?.gtag==="function"||!!f||!!A;if(!c&&!A&&!f)return;return{tagInstalled:c,clientId:A,measurementId:f}}function k(S){if(!S.googleAnalytics||typeof S.googleAnalytics==="boolean")return;return _(S.googleAnalytics.measurementId)}function e(S){if(!S.googleAnalytics)return!1;if(S.googleAnalytics===!0)return!1;return S.googleAnalytics.loadTag!==!1&&!!k(S)}function SS(S){let A=N(),f=D();if(!A||!f)return;A.dataLayer=Array.isArray(A.dataLayer)?A.dataLayer:[],A.gtag??=(...B)=>{A.dataLayer?.push(B)};let c=`script[src*="googletagmanager.com/gtag/js?id=${S}"]`;if(!f.querySelector?.(c)){let B=f.createElement("script");B.async=!0,B.src=`https://www.googletagmanager.com/gtag/js?id=${encodeURIComponent(S)}`,B.setAttribute("data-bts-ga-proxy","true"),(f.head??f.documentElement).appendChild(B)}A.gtag("js",new Date),A.gtag("config",S,{send_page_view:!1})}function AS(){let S=N();if(!S)return;let A=typeof Intl<"u"?_(Intl.DateTimeFormat().resolvedOptions().timeZone):void 0,f=S.navigator,c=_(f?.language),B=Array.isArray(f?.languages)?f.languages.filter((y)=>typeof y==="string").slice(0,10):[],E=_(f?.userAgent),T={timezone:A,language:c,languages:B.length>0?B:void 0,userAgent:E};return Object.values(T).some((y)=>y!==void 0)?T:void 0}function Y(S){let A=$(),f=i();return{event_id:S,ga_client_id:typeof f?.clientId==="string"?f.clientId:void 0,ga_tag_installed:typeof f?.tagInstalled==="boolean"?f.tagInstalled:void 0,attribution:A?{utm:A.utm,clickIds:A.clickIds,...A.clickIds,landingUrl:A.landingUrl,referrer:A.referrer}:void 0,client:AS(),googleAnalytics:f,page:{title:D()?.title,url:H()}}}function $(){let S=N();if(!S)return K();let A=new URLSearchParams(S.location.search),f={utm:{},clickIds:{},landingUrl:S.location.href,lastSeenAt:new Date().toISOString(),referrer:P()};for(let E of p){let T=A.get(E);if(!T)continue;if(E.startsWith("utm_")){f.utm[E]=T;continue}f.clickIds[E]=T}for(let E of a){let T=z(E);if(T)f.clickIds[E.replace(/^_/,"")]=T}let c=K(),B={utm:{...c?.utm??{},...f.utm},clickIds:{...c?.clickIds??{},...f.clickIds},landingUrl:f.landingUrl??c?.landingUrl,lastSeenAt:f.lastSeenAt,referrer:f.referrer??c?.referrer};return o(B),B}function _(S){let A=S?.trim();return A?A.slice(0,512):void 0}function fS(S){if(!S)return{};if(S instanceof Headers){let A={};return S.forEach((f,c)=>{A[c]=f}),A}if(Array.isArray(S))return Object.fromEntries(S.map(([A,f])=>[A,String(f)]));return Object.fromEntries(Object.entries(S).map(([A,f])=>[A,String(f)]))}function cS(S){let A={...d};if(S.autoTrack===!1)return{pageviews:!1,history:!1,outboundLinks:!1,buttonClicks:!1,formSubmissions:!1,search:!1,viewContent:!1};if(S.autoTrack&&typeof S.autoTrack==="object")return{pageviews:S.autoTrack.pageviews??A.pageviews,history:S.autoTrack.pageviews===!1?!1:S.autoTrack.history??A.history,outboundLinks:S.autoTrack.outboundLinks??A.outboundLinks,buttonClicks:S.autoTrack.buttonClicks??A.buttonClicks,formSubmissions:S.autoTrack.formSubmissions??A.formSubmissions,search:S.autoTrack.search??A.search,viewContent:S.autoTrack.viewContent??A.viewContent};if(S.autoPageviews===!1)return{...A,pageviews:!1,history:!1};if(S.autoPageviews===!0)return{...A,pageviews:!0};return A}class W{siteKey;endpoint;debug;flushIntervalMs;autoTrack;requestHeaders;queue=[];flushTimer=null;destroyed=!1;lastTrackedUrl=null;unpatchHistory=null;clickHandler=null;submitHandler=null;pagehideHandler=null;popstateHandler=null;viewContentObserver=null;viewContentMutationObserver=null;viewContentRescanPending=!1;viewContentListenersTornDown=!1;observedViewContentElements=new WeakSet;trackedViewContentElements=new WeakSet;constructor(S){if(this.siteKey=S.siteKey,this.endpoint=(S.endpoint??s).replace(/\/$/,""),this.debug=S.debug??!1,this.flushIntervalMs=S.flushIntervalMs??l,this.autoTrack=cS(S),this.requestHeaders=S.requestHeaders,e(S)){let A=k(S);if(A)SS(A)}if($(),N())this.installAutoTracking()}static init(S){return new W(S)}getVisitorId(){let S=L(),A=S?.getItem(j);if(A)return A;let f=V();return S?.setItem(j,f),f}getSessionId(){let S=L(),A=S?.getItem(G);if(A)return A;let f=V();return S?.setItem(G,f),f}getPersistedJourneyId(){return L()?.getItem(O)??null}log(...S){if(this.debug)console.log("[@bts/analytics]",...S)}installAutoTracking(){let S=N(),A=D();if(!S||!A)return;if(this.autoTrack.pageviews)this.recordPageView();if(this.autoTrack.history)this.unpatchHistory=this.patchHistory(S),this.popstateHandler=()=>{this.recordPageView(),this.rescanViewContentAfterNavigation()},S.addEventListener("popstate",this.popstateHandler);if(this.clickHandler=(f)=>{this.handleAutoClick(f)},this.submitHandler=(f)=>{this.handleAutoSubmit(f)},this.pagehideHandler=()=>{this.viewContentListenersTornDown=!0,this.flushWithBeacon(),this.viewContentObserver?.disconnect(),this.viewContentObserver=null,this.viewContentMutationObserver?.disconnect(),this.viewContentMutationObserver=null},this.autoTrack.outboundLinks||this.autoTrack.buttonClicks)A.addEventListener("click",this.clickHandler,!0);if(this.autoTrack.formSubmissions||this.autoTrack.search)A.addEventListener("submit",this.submitHandler,!0);if(this.autoTrack.viewContent)this.installViewContentTracking(A);S.addEventListener("pagehide",this.pagehideHandler)}getOrCreateViewContentIntersectionObserver(){if(typeof IntersectionObserver>"u"||this.viewContentListenersTornDown)return null;if(this.viewContentObserver)return this.viewContentObserver;return this.viewContentObserver=new IntersectionObserver((S,A)=>{for(let f of S){if(!f.isIntersecting||!(f.target instanceof Element))continue;this.trackViewContentElement(f.target),A.unobserve(f.target)}}),this.viewContentObserver}observeViewContentElements(S){if(this.viewContentListenersTornDown||this.destroyed)return;let A=this.getOrCreateViewContentIntersectionObserver();if(!A)return;for(let f of Array.from(S.querySelectorAll("[data-bts-view-content]"))){if(this.observedViewContentElements.has(f))continue;this.observedViewContentElements.add(f),A.observe(f)}}scheduleViewContentRescanFromMutations(){if(this.viewContentListenersTornDown)return;if(this.viewContentRescanPending)return;this.viewContentRescanPending=!0,queueMicrotask(()=>{if(this.viewContentRescanPending=!1,this.viewContentListenersTornDown||this.destroyed)return;let S=D();if(S)this.observeViewContentElements(S)})}rescanViewContentAfterNavigation(){if(!this.autoTrack.viewContent)return;if(this.viewContentListenersTornDown)return;let S=D();if(S)this.observeViewContentElements(S)}installViewContentTracking(S){if(typeof IntersectionObserver>"u")return;if(this.observeViewContentElements(S),typeof MutationObserver>"u")return;let A=S.body??S.documentElement;if(!A)return;this.viewContentMutationObserver=new MutationObserver(()=>{this.scheduleViewContentRescanFromMutations()}),this.viewContentMutationObserver.observe(A,{childList:!0,subtree:!0})}trackViewContentElement(S){if(this.trackedViewContentElements.has(S))return;this.trackedViewContentElements.add(S);let A=_(S.getAttribute("data-bts-view-content")||S.getAttribute("data-bts-content-id")||S.id);this.trackStandard("view_content",{autoCaptured:!0,contentId:A,contentTitle:_(S.getAttribute("data-bts-content-title")||S.textContent),contentType:_(S.getAttribute("data-bts-content-type")),elementId:_(S.id)})}patchHistory(S){let A=S.history,f=A.pushState.bind(A),c=A.replaceState.bind(A),B=()=>{$(),this.recordPageView(),this.rescanViewContentAfterNavigation()};return A.pushState=(...E)=>{f(...E),B()},A.replaceState=(...E)=>{c(...E),B()},()=>{A.pushState=f,A.replaceState=c}}handleAutoClick(S){if(S.defaultPrevented)return;let A=S.target;if(!(A instanceof Element))return;if(this.autoTrack.outboundLinks){let c=A.closest("a[href]");if(c instanceof HTMLAnchorElement){let B=_(c.href);if(!B)return;let E=new URL(B,R());if(E.origin!==R()){this.trackStandard("outbound_click",{elementHref:B,elementId:_(c.id),elementText:_(c.textContent),linkHost:E.hostname});return}}}if(!this.autoTrack.buttonClicks)return;let f=A.closest("button,[role='button'],[data-bts-track-click]");if(!(f instanceof Element))return;this.trackStandard("button_click",{elementId:_(f.id),elementName:_(f.getAttribute("name")),elementRole:_(f.getAttribute("role")),elementText:_(f.textContent)})}handleAutoSubmit(S){if(S.defaultPrevented||!this.autoTrack.formSubmissions&&!this.autoTrack.search)return;let A=S.target;if(!(A instanceof HTMLFormElement))return;if(this.autoTrack.search)this.trackSearchForm(A);if(!this.autoTrack.formSubmissions)return;this.trackStandard("form_submit",{formAction:_(A.action),formId:_(A.id),formMethod:_(A.method),formName:_(A.getAttribute("name"))})}trackSearchForm(S){if((S.method||"get").toLowerCase()!=="get")return;let f=this.extractSearchQuery(S);if(!f)return;this.trackStandard("search",{autoCaptured:!0,formAction:_(S.action),formId:_(S.id),formName:_(S.getAttribute("name")),queryKey:f.key,searchQuery:f.value})}extractSearchQuery(S){try{let f=new FormData(S);for(let c of Q){let B=f.get(c);if(typeof B==="string"){let E=_(B);if(E)return{key:c,value:E}}}}catch{}let A=S.elements;for(let f of Q){let c=A?.namedItem?.(f),B=c&&"value"in c?c.value:void 0;if(typeof B==="string"){let E=_(B);if(E)return{key:f,value:E}}}return null}buildEvent(S,A,f,c){let B=c?.path??F(),E=P(),T=V(),y=Y(T);return{eventName:S,eventType:A,path:B,referrer:E,occurredAt:new Date().toISOString(),journeyId:this.getPersistedJourneyId()??void 0,visitorId:this.getVisitorId(),sessionId:this.getSessionId(),properties:{...f??{},...y,event_id:f?.event_id??f?.eventId??T}}}queueEvent(S,A,f){let c=X(S),B=Z(c.canonicalEventName,f),E={...A??{}};if(c.aliasOf)E.originalEventName=S;if(this.queue.push(this.buildEvent(c.eventName,B,E)),this.queue.length>=50){this.flushNow();return}this.scheduleFlush()}scheduleFlush(){if(this.flushTimer)return;this.flushTimer=setTimeout(()=>{this.flushTimer=null,this.flushNow()},this.flushIntervalMs)}async postJson(S,A){let f=`${this.endpoint}${S}`;this.log("POST",f,A);let c=JSON.stringify(A),B=await this.resolveRequestHeaders(S,f,A,c,this.endpoint);return fetch(f,{method:"POST",headers:B,body:c})}async postWebsiteJson(S,A){let f=this.endpoint.replace(/\/analytics$/,""),c=`${f}${S}`;this.log("POST",c,A);let B=JSON.stringify(A),E=await this.resolveRequestHeaders(S,c,A,B,f);return fetch(c,{method:"POST",headers:E,body:B})}async postJsonKeepalive(S,A){let f=`${this.endpoint}${S}`;this.log("POST keepalive",f,A);let c=JSON.stringify(A),B=await this.resolveRequestHeaders(S,f,A,c,this.endpoint);return fetch(f,{method:"POST",headers:B,body:c,keepalive:!0})}async resolveRequestHeaders(S,A,f,c,B=this.endpoint){let E={"Content-Type":"application/json"};if(!this.requestHeaders)return E;let T=typeof this.requestHeaders==="function"?await this.requestHeaders({body:f,bodyText:c,endpoint:B,headers:{...E},path:S,siteKey:this.siteKey,url:A}):this.requestHeaders;return{...E,...fS(T)}}async flushBatch(S,A=!1){try{let f=A?await this.postJsonKeepalive("/ingest/batch",{siteKey:this.siteKey,events:S}):await this.postJson("/ingest/batch",{siteKey:this.siteKey,events:S});if(!f.ok)return this.log("flush failed",f.status),!1;return!0}catch(f){return this.log("flush error",f),!1}}flushWithBeacon(){if(this.queue.length===0)return;if(this.requestHeaders){let T=[...this.queue];this.queue=[],this.flushBatch(T,!0).then((y)=>{if(!y&&!this.destroyed)this.queue.unshift(...T),this.scheduleFlush()});return}let S=N(),A=S?.navigator?.sendBeacon?.bind(S.navigator);if(!A)return;let f=[...this.queue];this.queue=[];let c=`${this.endpoint}/ingest/batch`,B=JSON.stringify({siteKey:this.siteKey,events:f}),E=new Blob([B],{type:"application/json"});A(c,E)}async flushNow(){if(this.queue.length===0)return;let S=[...this.queue];if(this.queue=[],!await this.flushBatch(S)&&!this.destroyed)this.queue.unshift(...S),this.scheduleFlush()}recordPageView(S){let A=S??F(),f=H()??A??"/";if(f===this.lastTrackedUrl)return;this.lastTrackedUrl=f,this.queue.push(this.buildEvent("page_view","page_view",{autoCaptured:!0},{path:A})),this.scheduleFlush()}page(S){this.lastTrackedUrl=null,this.recordPageView(S)}track(S,A){this.queueEvent(S,A)}trackStandard(S,A){this.queueEvent(S,A)}identify(S,A){this.queue.push(this.buildEvent("identify","identify",{traits:A??{},userId:S})),this.scheduleFlush()}listStandardEvents(){return C()}async submitContactForm(S){let A=V(),f=Y(A),c=await this.postWebsiteJson("/ghl/leads",{siteKey:this.siteKey,locationId:S.locationId,email:S.email,phone:S.phone,subject:S.subject,body:S.body,name:S.name,firstName:S.firstName,lastName:S.lastName,source:S.source,tags:S.tags,customFields:S.customFields,metadata:{...S.metadata??{},...f}});if(!c.ok)throw Error(`contact form submit failed: ${c.status}`);return await c.json()}async startFunnel(S){let A=await this.postJson("/journey/start",{siteKey:this.siteKey,entryPath:S??F()});if(!A.ok)throw Error(`journey/start failed: ${A.status}`);let f=await A.json();return L()?.setItem(M,f.journeyToken),L()?.setItem(O,f.journeyId),f}getPersistedJourneyToken(){return L()?.getItem(M)??null}decorateUrl(S,A){let f=A??this.getPersistedJourneyToken(),c=new URL(S,R());if(c.searchParams.set(b,this.siteKey),f)c.searchParams.set(h,f);return c.toString()}async notifyHandoff(S,A){let f=await this.postJson("/journey/handoff",{journeyToken:S,context:A});if(!f.ok)throw Error(`journey/handoff failed: ${f.status}`)}destroy(){this.destroyed=!0,this.viewContentListenersTornDown=!0;let S=N(),A=D();if(A&&this.clickHandler&&(this.autoTrack.outboundLinks||this.autoTrack.buttonClicks))A.removeEventListener("click",this.clickHandler,!0);if(A&&this.submitHandler&&(this.autoTrack.formSubmissions||this.autoTrack.search))A.removeEventListener("submit",this.submitHandler,!0);if(this.viewContentObserver?.disconnect(),this.viewContentObserver=null,this.viewContentMutationObserver?.disconnect(),this.viewContentMutationObserver=null,S&&this.popstateHandler)S.removeEventListener("popstate",this.popstateHandler);if(S&&this.pagehideHandler)S.removeEventListener("pagehide",this.pagehideHandler);if(this.flushTimer)clearTimeout(this.flushTimer),this.flushTimer=null;this.unpatchHistory?.(),this.unpatchHistory=null,this.flushWithBeacon()}}function w(S){return W.init(S)}function I(S){return typeof S==="object"&&S!==null&&!Array.isArray(S)}function BS(S){return Array.from(S)}function x(S){let A=typeof window>"u"?null:window;if(!A)return;let[f,c,B]=S;if(typeof f!=="string")return;if(f==="js")return;if(f==="config"){if(typeof c!=="string"||c.trim().length===0)return;let y=I(B)?B:{};A.btsAnalytics=w({...y,siteKey:c});return}let E=A.btsAnalytics;if(!E)return;let T=I(B)?B:void 0;if(f==="event"&&typeof c==="string"){E.track(c,T);return}if(f==="identify"&&typeof c==="string"){E.identify(c,T);return}if(f==="page"){E.page(typeof c==="string"?c:void 0);return}if(f==="flush")E.flushNow()}if(typeof window<"u"){let S=Array.isArray(window.btsDataLayer)?[...window.btsDataLayer]:[];window.btsDataLayer=Array.isArray(window.btsDataLayer)?window.btsDataLayer:[],window.BTSAnalytics={BTSAnalytics:W,createBTSAnalytics:w,listStandardWebEvents:C},window.createBTSAnalytics=w;for(let A of S)x(BS(A));window.bts=(...A)=>{window.btsDataLayer?.push(A),x(A)}}export{C as listStandardWebEvents,w as createBTSAnalytics,W as BTSAnalytics};
package/dist/cjs/index.js CHANGED
@@ -1 +1 @@
1
- var{defineProperty:D,getOwnPropertyNames:R,getOwnPropertyDescriptor:g}=Object,m=Object.prototype.hasOwnProperty;var C=new WeakMap,v=(_)=>{var V=C.get(_),B;if(V)return V;if(V=D({},"__esModule",{value:!0}),_&&typeof _==="object"||typeof _==="function")R(_).map(($)=>!m.call(V,$)&&D(V,$,{get:()=>_[$],enumerable:!(B=g(_,$))||B.enumerable}));return C.set(_,V),V};var x=(_,V)=>{for(var B in V)D(_,B,{get:V[B],enumerable:!0,configurable:!0,set:($)=>V[B]=()=>$})};var __={};x(__,{listStandardWebEvents:()=>E,createBTSAnalytics:()=>e,BTSAnalytics:()=>j});module.exports=v(__);var G=["page_view","view_content","search","lead","sign_up","begin_checkout","add_payment_info","purchase","outbound_click","button_click","form_submit"],h=new Set(G),T=new Set(["lead","sign_up","begin_checkout","add_payment_info","purchase"]),y={$pageview:"page_view",checkout_started:"begin_checkout",lead_capture:"lead",purchase_completed:"purchase",registration_complete:"sign_up"};function b(_){return h.has(_)}function H(_){let V=_.trim();if(V.length===0)return{eventName:V,canonicalEventName:V,isStandard:!1};let B=V.toLowerCase(),$=y[B];if($)return{eventName:$,canonicalEventName:$,aliasOf:$,isStandard:!0};return{eventName:V,canonicalEventName:V,isStandard:b(V)}}function P(_,V){if(V)return V;if(_==="page_view")return"page_view";if(_==="identify")return"identify";if(T.has(_))return"conversion";return"custom"}function E(){return[...G]}var Y="bts_analytics_vid",A="bts_analytics_sid",S="bts_analytics_jt",k="bts_analytics_jid",f="bts_analytics_attr",p="bts_site",l="bts_jt",u=300,d="https://api.bts.dev/v2/website/analytics",s={pageviews:!0,history:!0,outboundLinks:!0,buttonClicks:!0,formSubmissions:!0,search:!0,viewContent:!0},U=["q","query","search"],o=["utm_source","utm_medium","utm_campaign","utm_term","utm_content","fbclid","gclid","gbraid","li_fat_id","msclkid","ttclid","wbraid"];function M(){return typeof window>"u"?null:window}function K(){return typeof document>"u"?null:document}function L(){return M()?.localStorage??null}function I(){if(typeof crypto<"u"&&typeof crypto.randomUUID==="function")return crypto.randomUUID();return`v_${Math.random().toString(36).slice(2)}${Date.now().toString(36)}`}function J(_){return _!==null&&typeof _==="object"&&!Array.isArray(_)}function a(_){if(!_)return null;try{let V=JSON.parse(_);if(!J(V))return null;let B=J(V.utm)?Object.fromEntries(Object.entries(V.utm).filter((q)=>typeof q[1]==="string")):{},$=J(V.clickIds)?Object.fromEntries(Object.entries(V.clickIds).filter((q)=>typeof q[1]==="string")):{};return{utm:B,clickIds:$,landingUrl:typeof V.landingUrl==="string"?V.landingUrl:void 0,lastSeenAt:typeof V.lastSeenAt==="string"?V.lastSeenAt:new Date().toISOString(),referrer:typeof V.referrer==="string"?V.referrer:void 0}}catch{return null}}function O(){let _=L();return a(_?.getItem(f)??null)}function n(_){let V=L();if(!V)return;V.setItem(f,JSON.stringify(_))}function Q(){let _=M();if(!_)return;return`${_.location.pathname}${_.location.search}`}function w(){return M()?.location.href}function c(){return M()?.location.origin??"https://behindthescenes.com"}function z(){return K()?.referrer||void 0}function r(){let _=M();if(!_)return;let V=typeof Intl<"u"?X(Intl.DateTimeFormat().resolvedOptions().timeZone):void 0,B=_.navigator,$=X(B?.language),q=Array.isArray(B?.languages)?B.languages.filter((F)=>typeof F==="string").slice(0,10):[],N=X(B?.userAgent),Z={timezone:V,language:$,languages:q.length>0?q:void 0,userAgent:N};return Object.values(Z).some((F)=>F!==void 0)?Z:void 0}function W(){let _=M();if(!_)return O();let V=new URLSearchParams(_.location.search),B={utm:{},clickIds:{},landingUrl:_.location.href,lastSeenAt:new Date().toISOString(),referrer:z()};for(let N of o){let Z=V.get(N);if(!Z)continue;if(N.startsWith("utm_")){B.utm[N]=Z;continue}B.clickIds[N]=Z}let $=O(),q={utm:{...$?.utm??{},...B.utm},clickIds:{...$?.clickIds??{},...B.clickIds},landingUrl:B.landingUrl??$?.landingUrl,lastSeenAt:B.lastSeenAt,referrer:B.referrer??$?.referrer};return n(q),q}function X(_){let V=_?.trim();return V?V.slice(0,512):void 0}function i(_){if(!_)return{};if(_ instanceof Headers){let V={};return _.forEach((B,$)=>{V[$]=B}),V}if(Array.isArray(_))return Object.fromEntries(_.map(([V,B])=>[V,String(B)]));return Object.fromEntries(Object.entries(_).map(([V,B])=>[V,String(B)]))}function t(_){let V={...s};if(_.autoTrack===!1)return{pageviews:!1,history:!1,outboundLinks:!1,buttonClicks:!1,formSubmissions:!1,search:!1,viewContent:!1};if(_.autoTrack&&typeof _.autoTrack==="object")return{pageviews:_.autoTrack.pageviews??V.pageviews,history:_.autoTrack.pageviews===!1?!1:_.autoTrack.history??V.history,outboundLinks:_.autoTrack.outboundLinks??V.outboundLinks,buttonClicks:_.autoTrack.buttonClicks??V.buttonClicks,formSubmissions:_.autoTrack.formSubmissions??V.formSubmissions,search:_.autoTrack.search??V.search,viewContent:_.autoTrack.viewContent??V.viewContent};if(_.autoPageviews===!1)return{...V,pageviews:!1,history:!1};if(_.autoPageviews===!0)return{...V,pageviews:!0};return V}class j{siteKey;endpoint;debug;flushIntervalMs;autoTrack;requestHeaders;queue=[];flushTimer=null;destroyed=!1;lastTrackedUrl=null;unpatchHistory=null;clickHandler=null;submitHandler=null;pagehideHandler=null;popstateHandler=null;viewContentObserver=null;observedViewContentElements=new WeakSet;trackedViewContentElements=new WeakSet;constructor(_){if(this.siteKey=_.siteKey,this.endpoint=(_.endpoint??d).replace(/\/$/,""),this.debug=_.debug??!1,this.flushIntervalMs=_.flushIntervalMs??u,this.autoTrack=t(_),this.requestHeaders=_.requestHeaders,W(),M())this.installAutoTracking()}static init(_){return new j(_)}getVisitorId(){let _=L(),V=_?.getItem(Y);if(V)return V;let B=I();return _?.setItem(Y,B),B}getSessionId(){let _=L(),V=_?.getItem(A);if(V)return V;let B=I();return _?.setItem(A,B),B}getPersistedJourneyId(){return L()?.getItem(k)??null}log(..._){if(this.debug)console.log("[@bts/analytics]",..._)}installAutoTracking(){let _=M(),V=K();if(!_||!V)return;if(this.autoTrack.pageviews)this.recordPageView();if(this.autoTrack.history)this.unpatchHistory=this.patchHistory(_),this.popstateHandler=()=>{this.recordPageView()},_.addEventListener("popstate",this.popstateHandler);if(this.clickHandler=(B)=>{this.handleAutoClick(B)},this.submitHandler=(B)=>{this.handleAutoSubmit(B)},this.pagehideHandler=()=>{this.flushWithBeacon()},this.autoTrack.outboundLinks||this.autoTrack.buttonClicks)V.addEventListener("click",this.clickHandler,!0);if(this.autoTrack.formSubmissions||this.autoTrack.search)V.addEventListener("submit",this.submitHandler,!0);if(this.autoTrack.viewContent)this.installViewContentTracking(V);_.addEventListener("pagehide",this.pagehideHandler)}installViewContentTracking(_){if(typeof IntersectionObserver>"u")return;let V=Array.from(_.querySelectorAll("[data-bts-view-content]"));if(V.length===0)return;this.viewContentObserver=new IntersectionObserver((B,$)=>{for(let q of B){if(!q.isIntersecting||!(q.target instanceof Element))continue;this.trackViewContentElement(q.target),$.unobserve(q.target)}});for(let B of V){if(this.observedViewContentElements.has(B))continue;this.observedViewContentElements.add(B),this.viewContentObserver.observe(B)}}trackViewContentElement(_){if(this.trackedViewContentElements.has(_))return;this.trackedViewContentElements.add(_);let V=X(_.getAttribute("data-bts-view-content")||_.getAttribute("data-bts-content-id")||_.id);this.trackStandard("view_content",{autoCaptured:!0,contentId:V,contentTitle:X(_.getAttribute("data-bts-content-title")||_.textContent),contentType:X(_.getAttribute("data-bts-content-type")),elementId:X(_.id)})}patchHistory(_){let V=_.history,B=V.pushState.bind(V),$=V.replaceState.bind(V),q=()=>{W(),this.recordPageView()};return V.pushState=(...N)=>{B(...N),q()},V.replaceState=(...N)=>{$(...N),q()},()=>{V.pushState=B,V.replaceState=$}}handleAutoClick(_){if(_.defaultPrevented)return;let V=_.target;if(!(V instanceof Element))return;if(this.autoTrack.outboundLinks){let $=V.closest("a[href]");if($ instanceof HTMLAnchorElement){let q=X($.href);if(!q)return;let N=new URL(q,c());if(N.origin!==c()){this.trackStandard("outbound_click",{elementHref:q,elementId:X($.id),elementText:X($.textContent),linkHost:N.hostname});return}}}if(!this.autoTrack.buttonClicks)return;let B=V.closest("button,[role='button'],[data-bts-track-click]");if(!(B instanceof Element))return;this.trackStandard("button_click",{elementId:X(B.id),elementName:X(B.getAttribute("name")),elementRole:X(B.getAttribute("role")),elementText:X(B.textContent)})}handleAutoSubmit(_){if(_.defaultPrevented||!this.autoTrack.formSubmissions&&!this.autoTrack.search)return;let V=_.target;if(!(V instanceof HTMLFormElement))return;if(this.autoTrack.search)this.trackSearchForm(V);if(!this.autoTrack.formSubmissions)return;this.trackStandard("form_submit",{formAction:X(V.action),formId:X(V.id),formMethod:X(V.method),formName:X(V.getAttribute("name"))})}trackSearchForm(_){if((_.method||"get").toLowerCase()!=="get")return;let B=this.extractSearchQuery(_);if(!B)return;this.trackStandard("search",{autoCaptured:!0,formAction:X(_.action),formId:X(_.id),formName:X(_.getAttribute("name")),queryKey:B.key,searchQuery:B.value})}extractSearchQuery(_){try{let B=new FormData(_);for(let $ of U){let q=B.get($);if(typeof q==="string"){let N=X(q);if(N)return{key:$,value:N}}}}catch{}let V=_.elements;for(let B of U){let $=V?.namedItem?.(B),q=$&&"value"in $?$.value:void 0;if(typeof q==="string"){let N=X(q);if(N)return{key:B,value:N}}}return null}buildEvent(_,V,B,$){let q=W(),N=$?.path??Q(),Z=z();return{eventName:_,eventType:V,path:N,referrer:Z,occurredAt:new Date().toISOString(),journeyId:this.getPersistedJourneyId()??void 0,visitorId:this.getVisitorId(),sessionId:this.getSessionId(),properties:{...B??{},attribution:q?{utm:q.utm,clickIds:q.clickIds,landingUrl:q.landingUrl,referrer:q.referrer}:void 0,client:r(),page:{title:K()?.title,url:w()}}}}queueEvent(_,V,B){let $=H(_),q=P($.canonicalEventName,B),N={...V??{}};if($.aliasOf)N.originalEventName=_;if(this.queue.push(this.buildEvent($.eventName,q,N)),this.queue.length>=50){this.flushNow();return}this.scheduleFlush()}scheduleFlush(){if(this.flushTimer)return;this.flushTimer=setTimeout(()=>{this.flushTimer=null,this.flushNow()},this.flushIntervalMs)}async postJson(_,V){let B=`${this.endpoint}${_}`;this.log("POST",B,V);let $=JSON.stringify(V),q=await this.resolveRequestHeaders(_,B,V,$);return fetch(B,{method:"POST",headers:q,body:$})}async postWebsiteJson(_,V){let $=`${this.endpoint.replace(/\/analytics$/,"")}${_}`;this.log("POST",$,V);let q=JSON.stringify(V),N=await this.resolveRequestHeaders(_,$,V,q);return fetch($,{method:"POST",headers:N,body:q})}async postJsonKeepalive(_,V){let B=`${this.endpoint}${_}`;this.log("POST keepalive",B,V);let $=JSON.stringify(V),q=await this.resolveRequestHeaders(_,B,V,$);return fetch(B,{method:"POST",headers:q,body:$,keepalive:!0})}async resolveRequestHeaders(_,V,B,$){let q={"Content-Type":"application/json"};if(!this.requestHeaders)return q;let N=typeof this.requestHeaders==="function"?await this.requestHeaders({body:B,bodyText:$,endpoint:this.endpoint,headers:{...q},path:_,siteKey:this.siteKey,url:V}):this.requestHeaders;return{...q,...i(N)}}async flushBatch(_,V=!1){try{let B=V?await this.postJsonKeepalive("/ingest/batch",{siteKey:this.siteKey,events:_}):await this.postJson("/ingest/batch",{siteKey:this.siteKey,events:_});if(!B.ok)return this.log("flush failed",B.status),!1;return!0}catch(B){return this.log("flush error",B),!1}}flushWithBeacon(){if(this.queue.length===0)return;if(this.requestHeaders){let Z=[...this.queue];this.queue=[],this.flushBatch(Z,!0).then((F)=>{if(!F&&!this.destroyed)this.queue.unshift(...Z),this.scheduleFlush()});return}let _=M(),V=_?.navigator?.sendBeacon?.bind(_.navigator);if(!V)return;let B=[...this.queue];this.queue=[];let $=`${this.endpoint}/ingest/batch`,q=JSON.stringify({siteKey:this.siteKey,events:B}),N=new Blob([q],{type:"application/json"});V($,N)}async flushNow(){if(this.queue.length===0)return;let _=[...this.queue];if(this.queue=[],!await this.flushBatch(_)&&!this.destroyed)this.queue.unshift(..._),this.scheduleFlush()}recordPageView(_){let V=_??Q(),B=w()??V??"/";if(B===this.lastTrackedUrl)return;this.lastTrackedUrl=B,this.queue.push(this.buildEvent("page_view","page_view",{autoCaptured:!0},{path:V})),this.scheduleFlush()}page(_){this.lastTrackedUrl=null,this.recordPageView(_)}track(_,V){this.queueEvent(_,V)}trackStandard(_,V){this.queueEvent(_,V)}identify(_,V){this.queue.push(this.buildEvent("identify","identify",{traits:V??{},userId:_})),this.scheduleFlush()}listStandardEvents(){return E()}async submitContactForm(_){let V=await this.postWebsiteJson("/ghl/leads",{siteKey:this.siteKey,locationId:_.locationId,email:_.email,phone:_.phone,subject:_.subject,body:_.body,name:_.name,firstName:_.firstName,lastName:_.lastName,source:_.source,tags:_.tags,customFields:_.customFields,metadata:_.metadata});if(!V.ok)throw Error(`contact form submit failed: ${V.status}`);return await V.json()}async startFunnel(_){let V=await this.postJson("/journey/start",{siteKey:this.siteKey,entryPath:_??Q()});if(!V.ok)throw Error(`journey/start failed: ${V.status}`);let B=await V.json();return L()?.setItem(S,B.journeyToken),L()?.setItem(k,B.journeyId),B}getPersistedJourneyToken(){return L()?.getItem(S)??null}decorateUrl(_,V){let B=V??this.getPersistedJourneyToken(),$=new URL(_,c());if($.searchParams.set(p,this.siteKey),B)$.searchParams.set(l,B);return $.toString()}async notifyHandoff(_,V){let B=await this.postJson("/journey/handoff",{journeyToken:_,context:V});if(!B.ok)throw Error(`journey/handoff failed: ${B.status}`)}destroy(){this.destroyed=!0;let _=M(),V=K();if(V&&this.clickHandler&&(this.autoTrack.outboundLinks||this.autoTrack.buttonClicks))V.removeEventListener("click",this.clickHandler,!0);if(V&&this.submitHandler&&(this.autoTrack.formSubmissions||this.autoTrack.search))V.removeEventListener("submit",this.submitHandler,!0);if(this.viewContentObserver?.disconnect(),this.viewContentObserver=null,_&&this.popstateHandler)_.removeEventListener("popstate",this.popstateHandler);if(_&&this.pagehideHandler)_.removeEventListener("pagehide",this.pagehideHandler);if(this.flushTimer)clearTimeout(this.flushTimer),this.flushTimer=null;this.unpatchHistory?.(),this.unpatchHistory=null,this.flushWithBeacon()}}function e(_){return j.init(_)}
1
+ var{defineProperty:q,getOwnPropertyNames:g,getOwnPropertyDescriptor:v}=Object,h=Object.prototype.hasOwnProperty;var j=new WeakMap,b=(_)=>{var E=j.get(_),S;if(E)return E;if(E=q({},"__esModule",{value:!0}),_&&typeof _==="object"||typeof _==="function")g(_).map((N)=>!h.call(E,N)&&q(E,N,{get:()=>_[N],enumerable:!(S=v(_,N))||S.enumerable}));return j.set(_,E),E};var m=(_,E)=>{for(var S in E)q(_,S,{get:E[S],enumerable:!0,configurable:!0,set:(N)=>E[S]=()=>N})};var $_={};m($_,{listStandardWebEvents:()=>G,createBTSAnalytics:()=>L_,BTSAnalytics:()=>F});module.exports=b($_);var A=["page_view","view_content","search","lead","sign_up","begin_checkout","add_payment_info","purchase","outbound_click","button_click","form_submit"],y=new Set(A),u=new Set(["lead","sign_up","begin_checkout","add_payment_info","purchase"]),l={$pageview:"page_view",checkout_started:"begin_checkout",lead_capture:"lead",purchase_completed:"purchase",registration_complete:"sign_up"};function p(_){return y.has(_)}function Y(_){let E=_.trim();if(E.length===0)return{eventName:E,canonicalEventName:E,isStandard:!1};let S=E.toLowerCase(),N=l[S];if(N)return{eventName:N,canonicalEventName:N,aliasOf:N,isStandard:!0};return{eventName:E,canonicalEventName:E,isStandard:p(E)}}function f(_,E){if(E)return E;if(_==="page_view")return"page_view";if(_==="identify")return"identify";if(u.has(_))return"conversion";return"custom"}function G(){return[...A]}var K="bts_analytics_vid",U="bts_analytics_sid",R="bts_analytics_jt",H="bts_analytics_jid",k="bts_analytics_attr",d="bts_site",s="bts_jt",o=300,a="https://api.bts.dev/v2/website/analytics",r={pageviews:!0,history:!0,outboundLinks:!0,buttonClicks:!0,formSubmissions:!0,search:!0,viewContent:!0},P=["q","query","search"],n=["utm_source","utm_medium","utm_campaign","utm_term","utm_content","fbclid","gclid","gbraid","li_fat_id","msclkid","ttclid","wbraid"],i=["_fbp","_fbc"];function L(){return typeof window>"u"?null:window}function $(){return typeof document>"u"?null:document}function J(){return L()?.localStorage??null}function Z(){if(typeof crypto<"u"&&typeof crypto.randomUUID==="function")return crypto.randomUUID();return`v_${Math.random().toString(36).slice(2)}${Date.now().toString(36)}`}function M(_){return _!==null&&typeof _==="object"&&!Array.isArray(_)}function t(_){if(!_)return null;try{let E=JSON.parse(_);if(!M(E))return null;let S=M(E.utm)?Object.fromEntries(Object.entries(E.utm).filter((V)=>typeof V[1]==="string")):{},N=M(E.clickIds)?Object.fromEntries(Object.entries(E.clickIds).filter((V)=>typeof V[1]==="string")):{};return{utm:S,clickIds:N,landingUrl:typeof E.landingUrl==="string"?E.landingUrl:void 0,lastSeenAt:typeof E.lastSeenAt==="string"?E.lastSeenAt:new Date().toISOString(),referrer:typeof E.referrer==="string"?E.referrer:void 0}}catch{return null}}function T(){let _=J();return t(_?.getItem(k)??null)}function e(_){let E=J();if(!E)return;E.setItem(k,JSON.stringify(_))}function O(){let _=L();if(!_)return;return`${_.location.pathname}${_.location.search}`}function I(){return L()?.location.href}function Q(){return L()?.location.origin??"https://behindthescenes.com"}function c(){return $()?.referrer||void 0}function x(_){let E=$();if(!E?.cookie)return;let S=`${_}=`,N=E.cookie.split(";").map((D)=>D.trim()).find((D)=>D.startsWith(S));if(!N)return;let V=N.slice(S.length);if(V==="")return;try{return decodeURIComponent(V)}catch{return V}}function __(){let _=x("_ga");if(!_)return;let E=_.split(".");if(E.length>=4&&/^GA\d+$/i.test(E[0]??""))return E.slice(-2).join(".");return W(_)}function E_(){let E=$()?.querySelector?.('script[src*="googletagmanager.com/gtag/js?id="]'),S=E&&"src"in E&&typeof E.src==="string"?E.src:void 0;if(!S)return;try{return W(new URL(S).searchParams.get("id"))}catch{return}}function S_(){let _=L(),E=__(),S=E_(),N=typeof _?.gtag==="function"||!!S||!!E;if(!N&&!E&&!S)return;return{tagInstalled:N,clientId:E,measurementId:S}}function w(_){if(!_.googleAnalytics||typeof _.googleAnalytics==="boolean")return;return W(_.googleAnalytics.measurementId)}function N_(_){if(!_.googleAnalytics)return!1;if(_.googleAnalytics===!0)return!1;return _.googleAnalytics.loadTag!==!1&&!!w(_)}function V_(_){let E=L(),S=$();if(!E||!S)return;E.dataLayer=Array.isArray(E.dataLayer)?E.dataLayer:[],E.gtag??=(...V)=>{E.dataLayer?.push(V)};let N=`script[src*="googletagmanager.com/gtag/js?id=${_}"]`;if(!S.querySelector?.(N)){let V=S.createElement("script");V.async=!0,V.src=`https://www.googletagmanager.com/gtag/js?id=${encodeURIComponent(_)}`,V.setAttribute("data-bts-ga-proxy","true"),(S.head??S.documentElement).appendChild(V)}E.gtag("js",new Date),E.gtag("config",_,{send_page_view:!1})}function D_(){let _=L();if(!_)return;let E=typeof Intl<"u"?W(Intl.DateTimeFormat().resolvedOptions().timeZone):void 0,S=_.navigator,N=W(S?.language),V=Array.isArray(S?.languages)?S.languages.filter((X)=>typeof X==="string").slice(0,10):[],D=W(S?.userAgent),B={timezone:E,language:N,languages:V.length>0?V:void 0,userAgent:D};return Object.values(B).some((X)=>X!==void 0)?B:void 0}function z(_){let E=C(),S=S_();return{event_id:_,ga_client_id:typeof S?.clientId==="string"?S.clientId:void 0,ga_tag_installed:typeof S?.tagInstalled==="boolean"?S.tagInstalled:void 0,attribution:E?{utm:E.utm,clickIds:E.clickIds,...E.clickIds,landingUrl:E.landingUrl,referrer:E.referrer}:void 0,client:D_(),googleAnalytics:S,page:{title:$()?.title,url:I()}}}function C(){let _=L();if(!_)return T();let E=new URLSearchParams(_.location.search),S={utm:{},clickIds:{},landingUrl:_.location.href,lastSeenAt:new Date().toISOString(),referrer:c()};for(let D of n){let B=E.get(D);if(!B)continue;if(D.startsWith("utm_")){S.utm[D]=B;continue}S.clickIds[D]=B}for(let D of i){let B=x(D);if(B)S.clickIds[D.replace(/^_/,"")]=B}let N=T(),V={utm:{...N?.utm??{},...S.utm},clickIds:{...N?.clickIds??{},...S.clickIds},landingUrl:S.landingUrl??N?.landingUrl,lastSeenAt:S.lastSeenAt,referrer:S.referrer??N?.referrer};return e(V),V}function W(_){let E=_?.trim();return E?E.slice(0,512):void 0}function W_(_){if(!_)return{};if(_ instanceof Headers){let E={};return _.forEach((S,N)=>{E[N]=S}),E}if(Array.isArray(_))return Object.fromEntries(_.map(([E,S])=>[E,String(S)]));return Object.fromEntries(Object.entries(_).map(([E,S])=>[E,String(S)]))}function B_(_){let E={...r};if(_.autoTrack===!1)return{pageviews:!1,history:!1,outboundLinks:!1,buttonClicks:!1,formSubmissions:!1,search:!1,viewContent:!1};if(_.autoTrack&&typeof _.autoTrack==="object")return{pageviews:_.autoTrack.pageviews??E.pageviews,history:_.autoTrack.pageviews===!1?!1:_.autoTrack.history??E.history,outboundLinks:_.autoTrack.outboundLinks??E.outboundLinks,buttonClicks:_.autoTrack.buttonClicks??E.buttonClicks,formSubmissions:_.autoTrack.formSubmissions??E.formSubmissions,search:_.autoTrack.search??E.search,viewContent:_.autoTrack.viewContent??E.viewContent};if(_.autoPageviews===!1)return{...E,pageviews:!1,history:!1};if(_.autoPageviews===!0)return{...E,pageviews:!0};return E}class F{siteKey;endpoint;debug;flushIntervalMs;autoTrack;requestHeaders;queue=[];flushTimer=null;destroyed=!1;lastTrackedUrl=null;unpatchHistory=null;clickHandler=null;submitHandler=null;pagehideHandler=null;popstateHandler=null;viewContentObserver=null;viewContentMutationObserver=null;viewContentRescanPending=!1;viewContentListenersTornDown=!1;observedViewContentElements=new WeakSet;trackedViewContentElements=new WeakSet;constructor(_){if(this.siteKey=_.siteKey,this.endpoint=(_.endpoint??a).replace(/\/$/,""),this.debug=_.debug??!1,this.flushIntervalMs=_.flushIntervalMs??o,this.autoTrack=B_(_),this.requestHeaders=_.requestHeaders,N_(_)){let E=w(_);if(E)V_(E)}if(C(),L())this.installAutoTracking()}static init(_){return new F(_)}getVisitorId(){let _=J(),E=_?.getItem(K);if(E)return E;let S=Z();return _?.setItem(K,S),S}getSessionId(){let _=J(),E=_?.getItem(U);if(E)return E;let S=Z();return _?.setItem(U,S),S}getPersistedJourneyId(){return J()?.getItem(H)??null}log(..._){if(this.debug)console.log("[@bts/analytics]",..._)}installAutoTracking(){let _=L(),E=$();if(!_||!E)return;if(this.autoTrack.pageviews)this.recordPageView();if(this.autoTrack.history)this.unpatchHistory=this.patchHistory(_),this.popstateHandler=()=>{this.recordPageView(),this.rescanViewContentAfterNavigation()},_.addEventListener("popstate",this.popstateHandler);if(this.clickHandler=(S)=>{this.handleAutoClick(S)},this.submitHandler=(S)=>{this.handleAutoSubmit(S)},this.pagehideHandler=()=>{this.viewContentListenersTornDown=!0,this.flushWithBeacon(),this.viewContentObserver?.disconnect(),this.viewContentObserver=null,this.viewContentMutationObserver?.disconnect(),this.viewContentMutationObserver=null},this.autoTrack.outboundLinks||this.autoTrack.buttonClicks)E.addEventListener("click",this.clickHandler,!0);if(this.autoTrack.formSubmissions||this.autoTrack.search)E.addEventListener("submit",this.submitHandler,!0);if(this.autoTrack.viewContent)this.installViewContentTracking(E);_.addEventListener("pagehide",this.pagehideHandler)}getOrCreateViewContentIntersectionObserver(){if(typeof IntersectionObserver>"u"||this.viewContentListenersTornDown)return null;if(this.viewContentObserver)return this.viewContentObserver;return this.viewContentObserver=new IntersectionObserver((_,E)=>{for(let S of _){if(!S.isIntersecting||!(S.target instanceof Element))continue;this.trackViewContentElement(S.target),E.unobserve(S.target)}}),this.viewContentObserver}observeViewContentElements(_){if(this.viewContentListenersTornDown||this.destroyed)return;let E=this.getOrCreateViewContentIntersectionObserver();if(!E)return;for(let S of Array.from(_.querySelectorAll("[data-bts-view-content]"))){if(this.observedViewContentElements.has(S))continue;this.observedViewContentElements.add(S),E.observe(S)}}scheduleViewContentRescanFromMutations(){if(this.viewContentListenersTornDown)return;if(this.viewContentRescanPending)return;this.viewContentRescanPending=!0,queueMicrotask(()=>{if(this.viewContentRescanPending=!1,this.viewContentListenersTornDown||this.destroyed)return;let _=$();if(_)this.observeViewContentElements(_)})}rescanViewContentAfterNavigation(){if(!this.autoTrack.viewContent)return;if(this.viewContentListenersTornDown)return;let _=$();if(_)this.observeViewContentElements(_)}installViewContentTracking(_){if(typeof IntersectionObserver>"u")return;if(this.observeViewContentElements(_),typeof MutationObserver>"u")return;let E=_.body??_.documentElement;if(!E)return;this.viewContentMutationObserver=new MutationObserver(()=>{this.scheduleViewContentRescanFromMutations()}),this.viewContentMutationObserver.observe(E,{childList:!0,subtree:!0})}trackViewContentElement(_){if(this.trackedViewContentElements.has(_))return;this.trackedViewContentElements.add(_);let E=W(_.getAttribute("data-bts-view-content")||_.getAttribute("data-bts-content-id")||_.id);this.trackStandard("view_content",{autoCaptured:!0,contentId:E,contentTitle:W(_.getAttribute("data-bts-content-title")||_.textContent),contentType:W(_.getAttribute("data-bts-content-type")),elementId:W(_.id)})}patchHistory(_){let E=_.history,S=E.pushState.bind(E),N=E.replaceState.bind(E),V=()=>{C(),this.recordPageView(),this.rescanViewContentAfterNavigation()};return E.pushState=(...D)=>{S(...D),V()},E.replaceState=(...D)=>{N(...D),V()},()=>{E.pushState=S,E.replaceState=N}}handleAutoClick(_){if(_.defaultPrevented)return;let E=_.target;if(!(E instanceof Element))return;if(this.autoTrack.outboundLinks){let N=E.closest("a[href]");if(N instanceof HTMLAnchorElement){let V=W(N.href);if(!V)return;let D=new URL(V,Q());if(D.origin!==Q()){this.trackStandard("outbound_click",{elementHref:V,elementId:W(N.id),elementText:W(N.textContent),linkHost:D.hostname});return}}}if(!this.autoTrack.buttonClicks)return;let S=E.closest("button,[role='button'],[data-bts-track-click]");if(!(S instanceof Element))return;this.trackStandard("button_click",{elementId:W(S.id),elementName:W(S.getAttribute("name")),elementRole:W(S.getAttribute("role")),elementText:W(S.textContent)})}handleAutoSubmit(_){if(_.defaultPrevented||!this.autoTrack.formSubmissions&&!this.autoTrack.search)return;let E=_.target;if(!(E instanceof HTMLFormElement))return;if(this.autoTrack.search)this.trackSearchForm(E);if(!this.autoTrack.formSubmissions)return;this.trackStandard("form_submit",{formAction:W(E.action),formId:W(E.id),formMethod:W(E.method),formName:W(E.getAttribute("name"))})}trackSearchForm(_){if((_.method||"get").toLowerCase()!=="get")return;let S=this.extractSearchQuery(_);if(!S)return;this.trackStandard("search",{autoCaptured:!0,formAction:W(_.action),formId:W(_.id),formName:W(_.getAttribute("name")),queryKey:S.key,searchQuery:S.value})}extractSearchQuery(_){try{let S=new FormData(_);for(let N of P){let V=S.get(N);if(typeof V==="string"){let D=W(V);if(D)return{key:N,value:D}}}}catch{}let E=_.elements;for(let S of P){let N=E?.namedItem?.(S),V=N&&"value"in N?N.value:void 0;if(typeof V==="string"){let D=W(V);if(D)return{key:S,value:D}}}return null}buildEvent(_,E,S,N){let V=N?.path??O(),D=c(),B=Z(),X=z(B);return{eventName:_,eventType:E,path:V,referrer:D,occurredAt:new Date().toISOString(),journeyId:this.getPersistedJourneyId()??void 0,visitorId:this.getVisitorId(),sessionId:this.getSessionId(),properties:{...S??{},...X,event_id:S?.event_id??S?.eventId??B}}}queueEvent(_,E,S){let N=Y(_),V=f(N.canonicalEventName,S),D={...E??{}};if(N.aliasOf)D.originalEventName=_;if(this.queue.push(this.buildEvent(N.eventName,V,D)),this.queue.length>=50){this.flushNow();return}this.scheduleFlush()}scheduleFlush(){if(this.flushTimer)return;this.flushTimer=setTimeout(()=>{this.flushTimer=null,this.flushNow()},this.flushIntervalMs)}async postJson(_,E){let S=`${this.endpoint}${_}`;this.log("POST",S,E);let N=JSON.stringify(E),V=await this.resolveRequestHeaders(_,S,E,N,this.endpoint);return fetch(S,{method:"POST",headers:V,body:N})}async postWebsiteJson(_,E){let S=this.endpoint.replace(/\/analytics$/,""),N=`${S}${_}`;this.log("POST",N,E);let V=JSON.stringify(E),D=await this.resolveRequestHeaders(_,N,E,V,S);return fetch(N,{method:"POST",headers:D,body:V})}async postJsonKeepalive(_,E){let S=`${this.endpoint}${_}`;this.log("POST keepalive",S,E);let N=JSON.stringify(E),V=await this.resolveRequestHeaders(_,S,E,N,this.endpoint);return fetch(S,{method:"POST",headers:V,body:N,keepalive:!0})}async resolveRequestHeaders(_,E,S,N,V=this.endpoint){let D={"Content-Type":"application/json"};if(!this.requestHeaders)return D;let B=typeof this.requestHeaders==="function"?await this.requestHeaders({body:S,bodyText:N,endpoint:V,headers:{...D},path:_,siteKey:this.siteKey,url:E}):this.requestHeaders;return{...D,...W_(B)}}async flushBatch(_,E=!1){try{let S=E?await this.postJsonKeepalive("/ingest/batch",{siteKey:this.siteKey,events:_}):await this.postJson("/ingest/batch",{siteKey:this.siteKey,events:_});if(!S.ok)return this.log("flush failed",S.status),!1;return!0}catch(S){return this.log("flush error",S),!1}}flushWithBeacon(){if(this.queue.length===0)return;if(this.requestHeaders){let B=[...this.queue];this.queue=[],this.flushBatch(B,!0).then((X)=>{if(!X&&!this.destroyed)this.queue.unshift(...B),this.scheduleFlush()});return}let _=L(),E=_?.navigator?.sendBeacon?.bind(_.navigator);if(!E)return;let S=[...this.queue];this.queue=[];let N=`${this.endpoint}/ingest/batch`,V=JSON.stringify({siteKey:this.siteKey,events:S}),D=new Blob([V],{type:"application/json"});E(N,D)}async flushNow(){if(this.queue.length===0)return;let _=[...this.queue];if(this.queue=[],!await this.flushBatch(_)&&!this.destroyed)this.queue.unshift(..._),this.scheduleFlush()}recordPageView(_){let E=_??O(),S=I()??E??"/";if(S===this.lastTrackedUrl)return;this.lastTrackedUrl=S,this.queue.push(this.buildEvent("page_view","page_view",{autoCaptured:!0},{path:E})),this.scheduleFlush()}page(_){this.lastTrackedUrl=null,this.recordPageView(_)}track(_,E){this.queueEvent(_,E)}trackStandard(_,E){this.queueEvent(_,E)}identify(_,E){this.queue.push(this.buildEvent("identify","identify",{traits:E??{},userId:_})),this.scheduleFlush()}listStandardEvents(){return G()}async submitContactForm(_){let E=Z(),S=z(E),N=await this.postWebsiteJson("/ghl/leads",{siteKey:this.siteKey,locationId:_.locationId,email:_.email,phone:_.phone,subject:_.subject,body:_.body,name:_.name,firstName:_.firstName,lastName:_.lastName,source:_.source,tags:_.tags,customFields:_.customFields,metadata:{..._.metadata??{},...S}});if(!N.ok)throw Error(`contact form submit failed: ${N.status}`);return await N.json()}async startFunnel(_){let E=await this.postJson("/journey/start",{siteKey:this.siteKey,entryPath:_??O()});if(!E.ok)throw Error(`journey/start failed: ${E.status}`);let S=await E.json();return J()?.setItem(R,S.journeyToken),J()?.setItem(H,S.journeyId),S}getPersistedJourneyToken(){return J()?.getItem(R)??null}decorateUrl(_,E){let S=E??this.getPersistedJourneyToken(),N=new URL(_,Q());if(N.searchParams.set(d,this.siteKey),S)N.searchParams.set(s,S);return N.toString()}async notifyHandoff(_,E){let S=await this.postJson("/journey/handoff",{journeyToken:_,context:E});if(!S.ok)throw Error(`journey/handoff failed: ${S.status}`)}destroy(){this.destroyed=!0,this.viewContentListenersTornDown=!0;let _=L(),E=$();if(E&&this.clickHandler&&(this.autoTrack.outboundLinks||this.autoTrack.buttonClicks))E.removeEventListener("click",this.clickHandler,!0);if(E&&this.submitHandler&&(this.autoTrack.formSubmissions||this.autoTrack.search))E.removeEventListener("submit",this.submitHandler,!0);if(this.viewContentObserver?.disconnect(),this.viewContentObserver=null,this.viewContentMutationObserver?.disconnect(),this.viewContentMutationObserver=null,_&&this.popstateHandler)_.removeEventListener("popstate",this.popstateHandler);if(_&&this.pagehideHandler)_.removeEventListener("pagehide",this.pagehideHandler);if(this.flushTimer)clearTimeout(this.flushTimer),this.flushTimer=null;this.unpatchHistory?.(),this.unpatchHistory=null,this.flushWithBeacon()}}function L_(_){return F.init(_)}
package/dist/esm/index.js CHANGED
@@ -1 +1 @@
1
- var c=["page_view","view_content","search","lead","sign_up","begin_checkout","add_payment_info","purchase","outbound_click","button_click","form_submit"],f=new Set(c),z=new Set(["lead","sign_up","begin_checkout","add_payment_info","purchase"]),R={$pageview:"page_view",checkout_started:"begin_checkout",lead_capture:"lead",purchase_completed:"purchase",registration_complete:"sign_up"};function g(_){return f.has(_)}function W(_){let V=_.trim();if(V.length===0)return{eventName:V,canonicalEventName:V,isStandard:!1};let B=V.toLowerCase(),$=R[B];if($)return{eventName:$,canonicalEventName:$,aliasOf:$,isStandard:!0};return{eventName:V,canonicalEventName:V,isStandard:g(V)}}function C(_,V){if(V)return V;if(_==="page_view")return"page_view";if(_==="identify")return"identify";if(z.has(_))return"conversion";return"custom"}function G(){return[...c]}var H="bts_analytics_vid",P="bts_analytics_sid",Y="bts_analytics_jt",A="bts_analytics_jid",O="bts_analytics_attr",m="bts_site",v="bts_jt",x=300,h="https://api.bts.dev/v2/website/analytics",T={pageviews:!0,history:!0,outboundLinks:!0,buttonClicks:!0,formSubmissions:!0,search:!0,viewContent:!0},S=["q","query","search"],y=["utm_source","utm_medium","utm_campaign","utm_term","utm_content","fbclid","gclid","gbraid","li_fat_id","msclkid","ttclid","wbraid"];function M(){return typeof window>"u"?null:window}function K(){return typeof document>"u"?null:document}function L(){return M()?.localStorage??null}function k(){if(typeof crypto<"u"&&typeof crypto.randomUUID==="function")return crypto.randomUUID();return`v_${Math.random().toString(36).slice(2)}${Date.now().toString(36)}`}function j(_){return _!==null&&typeof _==="object"&&!Array.isArray(_)}function b(_){if(!_)return null;try{let V=JSON.parse(_);if(!j(V))return null;let B=j(V.utm)?Object.fromEntries(Object.entries(V.utm).filter((q)=>typeof q[1]==="string")):{},$=j(V.clickIds)?Object.fromEntries(Object.entries(V.clickIds).filter((q)=>typeof q[1]==="string")):{};return{utm:B,clickIds:$,landingUrl:typeof V.landingUrl==="string"?V.landingUrl:void 0,lastSeenAt:typeof V.lastSeenAt==="string"?V.lastSeenAt:new Date().toISOString(),referrer:typeof V.referrer==="string"?V.referrer:void 0}}catch{return null}}function U(){let _=L();return b(_?.getItem(O)??null)}function p(_){let V=L();if(!V)return;V.setItem(O,JSON.stringify(_))}function D(){let _=M();if(!_)return;return`${_.location.pathname}${_.location.search}`}function I(){return M()?.location.href}function E(){return M()?.location.origin??"https://behindthescenes.com"}function w(){return K()?.referrer||void 0}function l(){let _=M();if(!_)return;let V=typeof Intl<"u"?X(Intl.DateTimeFormat().resolvedOptions().timeZone):void 0,B=_.navigator,$=X(B?.language),q=Array.isArray(B?.languages)?B.languages.filter((F)=>typeof F==="string").slice(0,10):[],N=X(B?.userAgent),Z={timezone:V,language:$,languages:q.length>0?q:void 0,userAgent:N};return Object.values(Z).some((F)=>F!==void 0)?Z:void 0}function J(){let _=M();if(!_)return U();let V=new URLSearchParams(_.location.search),B={utm:{},clickIds:{},landingUrl:_.location.href,lastSeenAt:new Date().toISOString(),referrer:w()};for(let N of y){let Z=V.get(N);if(!Z)continue;if(N.startsWith("utm_")){B.utm[N]=Z;continue}B.clickIds[N]=Z}let $=U(),q={utm:{...$?.utm??{},...B.utm},clickIds:{...$?.clickIds??{},...B.clickIds},landingUrl:B.landingUrl??$?.landingUrl,lastSeenAt:B.lastSeenAt,referrer:B.referrer??$?.referrer};return p(q),q}function X(_){let V=_?.trim();return V?V.slice(0,512):void 0}function u(_){if(!_)return{};if(_ instanceof Headers){let V={};return _.forEach((B,$)=>{V[$]=B}),V}if(Array.isArray(_))return Object.fromEntries(_.map(([V,B])=>[V,String(B)]));return Object.fromEntries(Object.entries(_).map(([V,B])=>[V,String(B)]))}function d(_){let V={...T};if(_.autoTrack===!1)return{pageviews:!1,history:!1,outboundLinks:!1,buttonClicks:!1,formSubmissions:!1,search:!1,viewContent:!1};if(_.autoTrack&&typeof _.autoTrack==="object")return{pageviews:_.autoTrack.pageviews??V.pageviews,history:_.autoTrack.pageviews===!1?!1:_.autoTrack.history??V.history,outboundLinks:_.autoTrack.outboundLinks??V.outboundLinks,buttonClicks:_.autoTrack.buttonClicks??V.buttonClicks,formSubmissions:_.autoTrack.formSubmissions??V.formSubmissions,search:_.autoTrack.search??V.search,viewContent:_.autoTrack.viewContent??V.viewContent};if(_.autoPageviews===!1)return{...V,pageviews:!1,history:!1};if(_.autoPageviews===!0)return{...V,pageviews:!0};return V}class Q{siteKey;endpoint;debug;flushIntervalMs;autoTrack;requestHeaders;queue=[];flushTimer=null;destroyed=!1;lastTrackedUrl=null;unpatchHistory=null;clickHandler=null;submitHandler=null;pagehideHandler=null;popstateHandler=null;viewContentObserver=null;observedViewContentElements=new WeakSet;trackedViewContentElements=new WeakSet;constructor(_){if(this.siteKey=_.siteKey,this.endpoint=(_.endpoint??h).replace(/\/$/,""),this.debug=_.debug??!1,this.flushIntervalMs=_.flushIntervalMs??x,this.autoTrack=d(_),this.requestHeaders=_.requestHeaders,J(),M())this.installAutoTracking()}static init(_){return new Q(_)}getVisitorId(){let _=L(),V=_?.getItem(H);if(V)return V;let B=k();return _?.setItem(H,B),B}getSessionId(){let _=L(),V=_?.getItem(P);if(V)return V;let B=k();return _?.setItem(P,B),B}getPersistedJourneyId(){return L()?.getItem(A)??null}log(..._){if(this.debug)console.log("[@bts/analytics]",..._)}installAutoTracking(){let _=M(),V=K();if(!_||!V)return;if(this.autoTrack.pageviews)this.recordPageView();if(this.autoTrack.history)this.unpatchHistory=this.patchHistory(_),this.popstateHandler=()=>{this.recordPageView()},_.addEventListener("popstate",this.popstateHandler);if(this.clickHandler=(B)=>{this.handleAutoClick(B)},this.submitHandler=(B)=>{this.handleAutoSubmit(B)},this.pagehideHandler=()=>{this.flushWithBeacon()},this.autoTrack.outboundLinks||this.autoTrack.buttonClicks)V.addEventListener("click",this.clickHandler,!0);if(this.autoTrack.formSubmissions||this.autoTrack.search)V.addEventListener("submit",this.submitHandler,!0);if(this.autoTrack.viewContent)this.installViewContentTracking(V);_.addEventListener("pagehide",this.pagehideHandler)}installViewContentTracking(_){if(typeof IntersectionObserver>"u")return;let V=Array.from(_.querySelectorAll("[data-bts-view-content]"));if(V.length===0)return;this.viewContentObserver=new IntersectionObserver((B,$)=>{for(let q of B){if(!q.isIntersecting||!(q.target instanceof Element))continue;this.trackViewContentElement(q.target),$.unobserve(q.target)}});for(let B of V){if(this.observedViewContentElements.has(B))continue;this.observedViewContentElements.add(B),this.viewContentObserver.observe(B)}}trackViewContentElement(_){if(this.trackedViewContentElements.has(_))return;this.trackedViewContentElements.add(_);let V=X(_.getAttribute("data-bts-view-content")||_.getAttribute("data-bts-content-id")||_.id);this.trackStandard("view_content",{autoCaptured:!0,contentId:V,contentTitle:X(_.getAttribute("data-bts-content-title")||_.textContent),contentType:X(_.getAttribute("data-bts-content-type")),elementId:X(_.id)})}patchHistory(_){let V=_.history,B=V.pushState.bind(V),$=V.replaceState.bind(V),q=()=>{J(),this.recordPageView()};return V.pushState=(...N)=>{B(...N),q()},V.replaceState=(...N)=>{$(...N),q()},()=>{V.pushState=B,V.replaceState=$}}handleAutoClick(_){if(_.defaultPrevented)return;let V=_.target;if(!(V instanceof Element))return;if(this.autoTrack.outboundLinks){let $=V.closest("a[href]");if($ instanceof HTMLAnchorElement){let q=X($.href);if(!q)return;let N=new URL(q,E());if(N.origin!==E()){this.trackStandard("outbound_click",{elementHref:q,elementId:X($.id),elementText:X($.textContent),linkHost:N.hostname});return}}}if(!this.autoTrack.buttonClicks)return;let B=V.closest("button,[role='button'],[data-bts-track-click]");if(!(B instanceof Element))return;this.trackStandard("button_click",{elementId:X(B.id),elementName:X(B.getAttribute("name")),elementRole:X(B.getAttribute("role")),elementText:X(B.textContent)})}handleAutoSubmit(_){if(_.defaultPrevented||!this.autoTrack.formSubmissions&&!this.autoTrack.search)return;let V=_.target;if(!(V instanceof HTMLFormElement))return;if(this.autoTrack.search)this.trackSearchForm(V);if(!this.autoTrack.formSubmissions)return;this.trackStandard("form_submit",{formAction:X(V.action),formId:X(V.id),formMethod:X(V.method),formName:X(V.getAttribute("name"))})}trackSearchForm(_){if((_.method||"get").toLowerCase()!=="get")return;let B=this.extractSearchQuery(_);if(!B)return;this.trackStandard("search",{autoCaptured:!0,formAction:X(_.action),formId:X(_.id),formName:X(_.getAttribute("name")),queryKey:B.key,searchQuery:B.value})}extractSearchQuery(_){try{let B=new FormData(_);for(let $ of S){let q=B.get($);if(typeof q==="string"){let N=X(q);if(N)return{key:$,value:N}}}}catch{}let V=_.elements;for(let B of S){let $=V?.namedItem?.(B),q=$&&"value"in $?$.value:void 0;if(typeof q==="string"){let N=X(q);if(N)return{key:B,value:N}}}return null}buildEvent(_,V,B,$){let q=J(),N=$?.path??D(),Z=w();return{eventName:_,eventType:V,path:N,referrer:Z,occurredAt:new Date().toISOString(),journeyId:this.getPersistedJourneyId()??void 0,visitorId:this.getVisitorId(),sessionId:this.getSessionId(),properties:{...B??{},attribution:q?{utm:q.utm,clickIds:q.clickIds,landingUrl:q.landingUrl,referrer:q.referrer}:void 0,client:l(),page:{title:K()?.title,url:I()}}}}queueEvent(_,V,B){let $=W(_),q=C($.canonicalEventName,B),N={...V??{}};if($.aliasOf)N.originalEventName=_;if(this.queue.push(this.buildEvent($.eventName,q,N)),this.queue.length>=50){this.flushNow();return}this.scheduleFlush()}scheduleFlush(){if(this.flushTimer)return;this.flushTimer=setTimeout(()=>{this.flushTimer=null,this.flushNow()},this.flushIntervalMs)}async postJson(_,V){let B=`${this.endpoint}${_}`;this.log("POST",B,V);let $=JSON.stringify(V),q=await this.resolveRequestHeaders(_,B,V,$);return fetch(B,{method:"POST",headers:q,body:$})}async postWebsiteJson(_,V){let $=`${this.endpoint.replace(/\/analytics$/,"")}${_}`;this.log("POST",$,V);let q=JSON.stringify(V),N=await this.resolveRequestHeaders(_,$,V,q);return fetch($,{method:"POST",headers:N,body:q})}async postJsonKeepalive(_,V){let B=`${this.endpoint}${_}`;this.log("POST keepalive",B,V);let $=JSON.stringify(V),q=await this.resolveRequestHeaders(_,B,V,$);return fetch(B,{method:"POST",headers:q,body:$,keepalive:!0})}async resolveRequestHeaders(_,V,B,$){let q={"Content-Type":"application/json"};if(!this.requestHeaders)return q;let N=typeof this.requestHeaders==="function"?await this.requestHeaders({body:B,bodyText:$,endpoint:this.endpoint,headers:{...q},path:_,siteKey:this.siteKey,url:V}):this.requestHeaders;return{...q,...u(N)}}async flushBatch(_,V=!1){try{let B=V?await this.postJsonKeepalive("/ingest/batch",{siteKey:this.siteKey,events:_}):await this.postJson("/ingest/batch",{siteKey:this.siteKey,events:_});if(!B.ok)return this.log("flush failed",B.status),!1;return!0}catch(B){return this.log("flush error",B),!1}}flushWithBeacon(){if(this.queue.length===0)return;if(this.requestHeaders){let Z=[...this.queue];this.queue=[],this.flushBatch(Z,!0).then((F)=>{if(!F&&!this.destroyed)this.queue.unshift(...Z),this.scheduleFlush()});return}let _=M(),V=_?.navigator?.sendBeacon?.bind(_.navigator);if(!V)return;let B=[...this.queue];this.queue=[];let $=`${this.endpoint}/ingest/batch`,q=JSON.stringify({siteKey:this.siteKey,events:B}),N=new Blob([q],{type:"application/json"});V($,N)}async flushNow(){if(this.queue.length===0)return;let _=[...this.queue];if(this.queue=[],!await this.flushBatch(_)&&!this.destroyed)this.queue.unshift(..._),this.scheduleFlush()}recordPageView(_){let V=_??D(),B=I()??V??"/";if(B===this.lastTrackedUrl)return;this.lastTrackedUrl=B,this.queue.push(this.buildEvent("page_view","page_view",{autoCaptured:!0},{path:V})),this.scheduleFlush()}page(_){this.lastTrackedUrl=null,this.recordPageView(_)}track(_,V){this.queueEvent(_,V)}trackStandard(_,V){this.queueEvent(_,V)}identify(_,V){this.queue.push(this.buildEvent("identify","identify",{traits:V??{},userId:_})),this.scheduleFlush()}listStandardEvents(){return G()}async submitContactForm(_){let V=await this.postWebsiteJson("/ghl/leads",{siteKey:this.siteKey,locationId:_.locationId,email:_.email,phone:_.phone,subject:_.subject,body:_.body,name:_.name,firstName:_.firstName,lastName:_.lastName,source:_.source,tags:_.tags,customFields:_.customFields,metadata:_.metadata});if(!V.ok)throw Error(`contact form submit failed: ${V.status}`);return await V.json()}async startFunnel(_){let V=await this.postJson("/journey/start",{siteKey:this.siteKey,entryPath:_??D()});if(!V.ok)throw Error(`journey/start failed: ${V.status}`);let B=await V.json();return L()?.setItem(Y,B.journeyToken),L()?.setItem(A,B.journeyId),B}getPersistedJourneyToken(){return L()?.getItem(Y)??null}decorateUrl(_,V){let B=V??this.getPersistedJourneyToken(),$=new URL(_,E());if($.searchParams.set(m,this.siteKey),B)$.searchParams.set(v,B);return $.toString()}async notifyHandoff(_,V){let B=await this.postJson("/journey/handoff",{journeyToken:_,context:V});if(!B.ok)throw Error(`journey/handoff failed: ${B.status}`)}destroy(){this.destroyed=!0;let _=M(),V=K();if(V&&this.clickHandler&&(this.autoTrack.outboundLinks||this.autoTrack.buttonClicks))V.removeEventListener("click",this.clickHandler,!0);if(V&&this.submitHandler&&(this.autoTrack.formSubmissions||this.autoTrack.search))V.removeEventListener("submit",this.submitHandler,!0);if(this.viewContentObserver?.disconnect(),this.viewContentObserver=null,_&&this.popstateHandler)_.removeEventListener("popstate",this.popstateHandler);if(_&&this.pagehideHandler)_.removeEventListener("pagehide",this.pagehideHandler);if(this.flushTimer)clearTimeout(this.flushTimer),this.flushTimer=null;this.unpatchHistory?.(),this.unpatchHistory=null,this.flushWithBeacon()}}function a(_){return Q.init(_)}export{G as listStandardWebEvents,a as createBTSAnalytics,Q as BTSAnalytics};
1
+ var Q=["page_view","view_content","search","lead","sign_up","begin_checkout","add_payment_info","purchase","outbound_click","button_click","form_submit"],x=new Set(Q),w=new Set(["lead","sign_up","begin_checkout","add_payment_info","purchase"]),g={$pageview:"page_view",checkout_started:"begin_checkout",lead_capture:"lead",purchase_completed:"purchase",registration_complete:"sign_up"};function v(_){return x.has(_)}function C(_){let E=_.trim();if(E.length===0)return{eventName:E,canonicalEventName:E,isStandard:!1};let S=E.toLowerCase(),N=g[S];if(N)return{eventName:N,canonicalEventName:N,aliasOf:N,isStandard:!0};return{eventName:E,canonicalEventName:E,isStandard:v(E)}}function j(_,E){if(E)return E;if(_==="page_view")return"page_view";if(_==="identify")return"identify";if(w.has(_))return"conversion";return"custom"}function A(){return[...Q]}var Y="bts_analytics_vid",f="bts_analytics_sid",K="bts_analytics_jt",U="bts_analytics_jid",T="bts_analytics_attr",h="bts_site",b="bts_jt",m=300,y="https://api.bts.dev/v2/website/analytics",u={pageviews:!0,history:!0,outboundLinks:!0,buttonClicks:!0,formSubmissions:!0,search:!0,viewContent:!0},R=["q","query","search"],l=["utm_source","utm_medium","utm_campaign","utm_term","utm_content","fbclid","gclid","gbraid","li_fat_id","msclkid","ttclid","wbraid"],p=["_fbp","_fbc"];function L(){return typeof window>"u"?null:window}function $(){return typeof document>"u"?null:document}function J(){return L()?.localStorage??null}function Z(){if(typeof crypto<"u"&&typeof crypto.randomUUID==="function")return crypto.randomUUID();return`v_${Math.random().toString(36).slice(2)}${Date.now().toString(36)}`}function F(_){return _!==null&&typeof _==="object"&&!Array.isArray(_)}function d(_){if(!_)return null;try{let E=JSON.parse(_);if(!F(E))return null;let S=F(E.utm)?Object.fromEntries(Object.entries(E.utm).filter((V)=>typeof V[1]==="string")):{},N=F(E.clickIds)?Object.fromEntries(Object.entries(E.clickIds).filter((V)=>typeof V[1]==="string")):{};return{utm:S,clickIds:N,landingUrl:typeof E.landingUrl==="string"?E.landingUrl:void 0,lastSeenAt:typeof E.lastSeenAt==="string"?E.lastSeenAt:new Date().toISOString(),referrer:typeof E.referrer==="string"?E.referrer:void 0}}catch{return null}}function H(){let _=J();return d(_?.getItem(T)??null)}function s(_){let E=J();if(!E)return;E.setItem(T,JSON.stringify(_))}function q(){let _=L();if(!_)return;return`${_.location.pathname}${_.location.search}`}function z(){return L()?.location.href}function G(){return L()?.location.origin??"https://behindthescenes.com"}function k(){return $()?.referrer||void 0}function I(_){let E=$();if(!E?.cookie)return;let S=`${_}=`,N=E.cookie.split(";").map((D)=>D.trim()).find((D)=>D.startsWith(S));if(!N)return;let V=N.slice(S.length);if(V==="")return;try{return decodeURIComponent(V)}catch{return V}}function o(){let _=I("_ga");if(!_)return;let E=_.split(".");if(E.length>=4&&/^GA\d+$/i.test(E[0]??""))return E.slice(-2).join(".");return W(_)}function a(){let E=$()?.querySelector?.('script[src*="googletagmanager.com/gtag/js?id="]'),S=E&&"src"in E&&typeof E.src==="string"?E.src:void 0;if(!S)return;try{return W(new URL(S).searchParams.get("id"))}catch{return}}function r(){let _=L(),E=o(),S=a(),N=typeof _?.gtag==="function"||!!S||!!E;if(!N&&!E&&!S)return;return{tagInstalled:N,clientId:E,measurementId:S}}function c(_){if(!_.googleAnalytics||typeof _.googleAnalytics==="boolean")return;return W(_.googleAnalytics.measurementId)}function n(_){if(!_.googleAnalytics)return!1;if(_.googleAnalytics===!0)return!1;return _.googleAnalytics.loadTag!==!1&&!!c(_)}function i(_){let E=L(),S=$();if(!E||!S)return;E.dataLayer=Array.isArray(E.dataLayer)?E.dataLayer:[],E.gtag??=(...V)=>{E.dataLayer?.push(V)};let N=`script[src*="googletagmanager.com/gtag/js?id=${_}"]`;if(!S.querySelector?.(N)){let V=S.createElement("script");V.async=!0,V.src=`https://www.googletagmanager.com/gtag/js?id=${encodeURIComponent(_)}`,V.setAttribute("data-bts-ga-proxy","true"),(S.head??S.documentElement).appendChild(V)}E.gtag("js",new Date),E.gtag("config",_,{send_page_view:!1})}function t(){let _=L();if(!_)return;let E=typeof Intl<"u"?W(Intl.DateTimeFormat().resolvedOptions().timeZone):void 0,S=_.navigator,N=W(S?.language),V=Array.isArray(S?.languages)?S.languages.filter((X)=>typeof X==="string").slice(0,10):[],D=W(S?.userAgent),B={timezone:E,language:N,languages:V.length>0?V:void 0,userAgent:D};return Object.values(B).some((X)=>X!==void 0)?B:void 0}function P(_){let E=M(),S=r();return{event_id:_,ga_client_id:typeof S?.clientId==="string"?S.clientId:void 0,ga_tag_installed:typeof S?.tagInstalled==="boolean"?S.tagInstalled:void 0,attribution:E?{utm:E.utm,clickIds:E.clickIds,...E.clickIds,landingUrl:E.landingUrl,referrer:E.referrer}:void 0,client:t(),googleAnalytics:S,page:{title:$()?.title,url:z()}}}function M(){let _=L();if(!_)return H();let E=new URLSearchParams(_.location.search),S={utm:{},clickIds:{},landingUrl:_.location.href,lastSeenAt:new Date().toISOString(),referrer:k()};for(let D of l){let B=E.get(D);if(!B)continue;if(D.startsWith("utm_")){S.utm[D]=B;continue}S.clickIds[D]=B}for(let D of p){let B=I(D);if(B)S.clickIds[D.replace(/^_/,"")]=B}let N=H(),V={utm:{...N?.utm??{},...S.utm},clickIds:{...N?.clickIds??{},...S.clickIds},landingUrl:S.landingUrl??N?.landingUrl,lastSeenAt:S.lastSeenAt,referrer:S.referrer??N?.referrer};return s(V),V}function W(_){let E=_?.trim();return E?E.slice(0,512):void 0}function e(_){if(!_)return{};if(_ instanceof Headers){let E={};return _.forEach((S,N)=>{E[N]=S}),E}if(Array.isArray(_))return Object.fromEntries(_.map(([E,S])=>[E,String(S)]));return Object.fromEntries(Object.entries(_).map(([E,S])=>[E,String(S)]))}function __(_){let E={...u};if(_.autoTrack===!1)return{pageviews:!1,history:!1,outboundLinks:!1,buttonClicks:!1,formSubmissions:!1,search:!1,viewContent:!1};if(_.autoTrack&&typeof _.autoTrack==="object")return{pageviews:_.autoTrack.pageviews??E.pageviews,history:_.autoTrack.pageviews===!1?!1:_.autoTrack.history??E.history,outboundLinks:_.autoTrack.outboundLinks??E.outboundLinks,buttonClicks:_.autoTrack.buttonClicks??E.buttonClicks,formSubmissions:_.autoTrack.formSubmissions??E.formSubmissions,search:_.autoTrack.search??E.search,viewContent:_.autoTrack.viewContent??E.viewContent};if(_.autoPageviews===!1)return{...E,pageviews:!1,history:!1};if(_.autoPageviews===!0)return{...E,pageviews:!0};return E}class O{siteKey;endpoint;debug;flushIntervalMs;autoTrack;requestHeaders;queue=[];flushTimer=null;destroyed=!1;lastTrackedUrl=null;unpatchHistory=null;clickHandler=null;submitHandler=null;pagehideHandler=null;popstateHandler=null;viewContentObserver=null;viewContentMutationObserver=null;viewContentRescanPending=!1;viewContentListenersTornDown=!1;observedViewContentElements=new WeakSet;trackedViewContentElements=new WeakSet;constructor(_){if(this.siteKey=_.siteKey,this.endpoint=(_.endpoint??y).replace(/\/$/,""),this.debug=_.debug??!1,this.flushIntervalMs=_.flushIntervalMs??m,this.autoTrack=__(_),this.requestHeaders=_.requestHeaders,n(_)){let E=c(_);if(E)i(E)}if(M(),L())this.installAutoTracking()}static init(_){return new O(_)}getVisitorId(){let _=J(),E=_?.getItem(Y);if(E)return E;let S=Z();return _?.setItem(Y,S),S}getSessionId(){let _=J(),E=_?.getItem(f);if(E)return E;let S=Z();return _?.setItem(f,S),S}getPersistedJourneyId(){return J()?.getItem(U)??null}log(..._){if(this.debug)console.log("[@bts/analytics]",..._)}installAutoTracking(){let _=L(),E=$();if(!_||!E)return;if(this.autoTrack.pageviews)this.recordPageView();if(this.autoTrack.history)this.unpatchHistory=this.patchHistory(_),this.popstateHandler=()=>{this.recordPageView(),this.rescanViewContentAfterNavigation()},_.addEventListener("popstate",this.popstateHandler);if(this.clickHandler=(S)=>{this.handleAutoClick(S)},this.submitHandler=(S)=>{this.handleAutoSubmit(S)},this.pagehideHandler=()=>{this.viewContentListenersTornDown=!0,this.flushWithBeacon(),this.viewContentObserver?.disconnect(),this.viewContentObserver=null,this.viewContentMutationObserver?.disconnect(),this.viewContentMutationObserver=null},this.autoTrack.outboundLinks||this.autoTrack.buttonClicks)E.addEventListener("click",this.clickHandler,!0);if(this.autoTrack.formSubmissions||this.autoTrack.search)E.addEventListener("submit",this.submitHandler,!0);if(this.autoTrack.viewContent)this.installViewContentTracking(E);_.addEventListener("pagehide",this.pagehideHandler)}getOrCreateViewContentIntersectionObserver(){if(typeof IntersectionObserver>"u"||this.viewContentListenersTornDown)return null;if(this.viewContentObserver)return this.viewContentObserver;return this.viewContentObserver=new IntersectionObserver((_,E)=>{for(let S of _){if(!S.isIntersecting||!(S.target instanceof Element))continue;this.trackViewContentElement(S.target),E.unobserve(S.target)}}),this.viewContentObserver}observeViewContentElements(_){if(this.viewContentListenersTornDown||this.destroyed)return;let E=this.getOrCreateViewContentIntersectionObserver();if(!E)return;for(let S of Array.from(_.querySelectorAll("[data-bts-view-content]"))){if(this.observedViewContentElements.has(S))continue;this.observedViewContentElements.add(S),E.observe(S)}}scheduleViewContentRescanFromMutations(){if(this.viewContentListenersTornDown)return;if(this.viewContentRescanPending)return;this.viewContentRescanPending=!0,queueMicrotask(()=>{if(this.viewContentRescanPending=!1,this.viewContentListenersTornDown||this.destroyed)return;let _=$();if(_)this.observeViewContentElements(_)})}rescanViewContentAfterNavigation(){if(!this.autoTrack.viewContent)return;if(this.viewContentListenersTornDown)return;let _=$();if(_)this.observeViewContentElements(_)}installViewContentTracking(_){if(typeof IntersectionObserver>"u")return;if(this.observeViewContentElements(_),typeof MutationObserver>"u")return;let E=_.body??_.documentElement;if(!E)return;this.viewContentMutationObserver=new MutationObserver(()=>{this.scheduleViewContentRescanFromMutations()}),this.viewContentMutationObserver.observe(E,{childList:!0,subtree:!0})}trackViewContentElement(_){if(this.trackedViewContentElements.has(_))return;this.trackedViewContentElements.add(_);let E=W(_.getAttribute("data-bts-view-content")||_.getAttribute("data-bts-content-id")||_.id);this.trackStandard("view_content",{autoCaptured:!0,contentId:E,contentTitle:W(_.getAttribute("data-bts-content-title")||_.textContent),contentType:W(_.getAttribute("data-bts-content-type")),elementId:W(_.id)})}patchHistory(_){let E=_.history,S=E.pushState.bind(E),N=E.replaceState.bind(E),V=()=>{M(),this.recordPageView(),this.rescanViewContentAfterNavigation()};return E.pushState=(...D)=>{S(...D),V()},E.replaceState=(...D)=>{N(...D),V()},()=>{E.pushState=S,E.replaceState=N}}handleAutoClick(_){if(_.defaultPrevented)return;let E=_.target;if(!(E instanceof Element))return;if(this.autoTrack.outboundLinks){let N=E.closest("a[href]");if(N instanceof HTMLAnchorElement){let V=W(N.href);if(!V)return;let D=new URL(V,G());if(D.origin!==G()){this.trackStandard("outbound_click",{elementHref:V,elementId:W(N.id),elementText:W(N.textContent),linkHost:D.hostname});return}}}if(!this.autoTrack.buttonClicks)return;let S=E.closest("button,[role='button'],[data-bts-track-click]");if(!(S instanceof Element))return;this.trackStandard("button_click",{elementId:W(S.id),elementName:W(S.getAttribute("name")),elementRole:W(S.getAttribute("role")),elementText:W(S.textContent)})}handleAutoSubmit(_){if(_.defaultPrevented||!this.autoTrack.formSubmissions&&!this.autoTrack.search)return;let E=_.target;if(!(E instanceof HTMLFormElement))return;if(this.autoTrack.search)this.trackSearchForm(E);if(!this.autoTrack.formSubmissions)return;this.trackStandard("form_submit",{formAction:W(E.action),formId:W(E.id),formMethod:W(E.method),formName:W(E.getAttribute("name"))})}trackSearchForm(_){if((_.method||"get").toLowerCase()!=="get")return;let S=this.extractSearchQuery(_);if(!S)return;this.trackStandard("search",{autoCaptured:!0,formAction:W(_.action),formId:W(_.id),formName:W(_.getAttribute("name")),queryKey:S.key,searchQuery:S.value})}extractSearchQuery(_){try{let S=new FormData(_);for(let N of R){let V=S.get(N);if(typeof V==="string"){let D=W(V);if(D)return{key:N,value:D}}}}catch{}let E=_.elements;for(let S of R){let N=E?.namedItem?.(S),V=N&&"value"in N?N.value:void 0;if(typeof V==="string"){let D=W(V);if(D)return{key:S,value:D}}}return null}buildEvent(_,E,S,N){let V=N?.path??q(),D=k(),B=Z(),X=P(B);return{eventName:_,eventType:E,path:V,referrer:D,occurredAt:new Date().toISOString(),journeyId:this.getPersistedJourneyId()??void 0,visitorId:this.getVisitorId(),sessionId:this.getSessionId(),properties:{...S??{},...X,event_id:S?.event_id??S?.eventId??B}}}queueEvent(_,E,S){let N=C(_),V=j(N.canonicalEventName,S),D={...E??{}};if(N.aliasOf)D.originalEventName=_;if(this.queue.push(this.buildEvent(N.eventName,V,D)),this.queue.length>=50){this.flushNow();return}this.scheduleFlush()}scheduleFlush(){if(this.flushTimer)return;this.flushTimer=setTimeout(()=>{this.flushTimer=null,this.flushNow()},this.flushIntervalMs)}async postJson(_,E){let S=`${this.endpoint}${_}`;this.log("POST",S,E);let N=JSON.stringify(E),V=await this.resolveRequestHeaders(_,S,E,N,this.endpoint);return fetch(S,{method:"POST",headers:V,body:N})}async postWebsiteJson(_,E){let S=this.endpoint.replace(/\/analytics$/,""),N=`${S}${_}`;this.log("POST",N,E);let V=JSON.stringify(E),D=await this.resolveRequestHeaders(_,N,E,V,S);return fetch(N,{method:"POST",headers:D,body:V})}async postJsonKeepalive(_,E){let S=`${this.endpoint}${_}`;this.log("POST keepalive",S,E);let N=JSON.stringify(E),V=await this.resolveRequestHeaders(_,S,E,N,this.endpoint);return fetch(S,{method:"POST",headers:V,body:N,keepalive:!0})}async resolveRequestHeaders(_,E,S,N,V=this.endpoint){let D={"Content-Type":"application/json"};if(!this.requestHeaders)return D;let B=typeof this.requestHeaders==="function"?await this.requestHeaders({body:S,bodyText:N,endpoint:V,headers:{...D},path:_,siteKey:this.siteKey,url:E}):this.requestHeaders;return{...D,...e(B)}}async flushBatch(_,E=!1){try{let S=E?await this.postJsonKeepalive("/ingest/batch",{siteKey:this.siteKey,events:_}):await this.postJson("/ingest/batch",{siteKey:this.siteKey,events:_});if(!S.ok)return this.log("flush failed",S.status),!1;return!0}catch(S){return this.log("flush error",S),!1}}flushWithBeacon(){if(this.queue.length===0)return;if(this.requestHeaders){let B=[...this.queue];this.queue=[],this.flushBatch(B,!0).then((X)=>{if(!X&&!this.destroyed)this.queue.unshift(...B),this.scheduleFlush()});return}let _=L(),E=_?.navigator?.sendBeacon?.bind(_.navigator);if(!E)return;let S=[...this.queue];this.queue=[];let N=`${this.endpoint}/ingest/batch`,V=JSON.stringify({siteKey:this.siteKey,events:S}),D=new Blob([V],{type:"application/json"});E(N,D)}async flushNow(){if(this.queue.length===0)return;let _=[...this.queue];if(this.queue=[],!await this.flushBatch(_)&&!this.destroyed)this.queue.unshift(..._),this.scheduleFlush()}recordPageView(_){let E=_??q(),S=z()??E??"/";if(S===this.lastTrackedUrl)return;this.lastTrackedUrl=S,this.queue.push(this.buildEvent("page_view","page_view",{autoCaptured:!0},{path:E})),this.scheduleFlush()}page(_){this.lastTrackedUrl=null,this.recordPageView(_)}track(_,E){this.queueEvent(_,E)}trackStandard(_,E){this.queueEvent(_,E)}identify(_,E){this.queue.push(this.buildEvent("identify","identify",{traits:E??{},userId:_})),this.scheduleFlush()}listStandardEvents(){return A()}async submitContactForm(_){let E=Z(),S=P(E),N=await this.postWebsiteJson("/ghl/leads",{siteKey:this.siteKey,locationId:_.locationId,email:_.email,phone:_.phone,subject:_.subject,body:_.body,name:_.name,firstName:_.firstName,lastName:_.lastName,source:_.source,tags:_.tags,customFields:_.customFields,metadata:{..._.metadata??{},...S}});if(!N.ok)throw Error(`contact form submit failed: ${N.status}`);return await N.json()}async startFunnel(_){let E=await this.postJson("/journey/start",{siteKey:this.siteKey,entryPath:_??q()});if(!E.ok)throw Error(`journey/start failed: ${E.status}`);let S=await E.json();return J()?.setItem(K,S.journeyToken),J()?.setItem(U,S.journeyId),S}getPersistedJourneyToken(){return J()?.getItem(K)??null}decorateUrl(_,E){let S=E??this.getPersistedJourneyToken(),N=new URL(_,G());if(N.searchParams.set(h,this.siteKey),S)N.searchParams.set(b,S);return N.toString()}async notifyHandoff(_,E){let S=await this.postJson("/journey/handoff",{journeyToken:_,context:E});if(!S.ok)throw Error(`journey/handoff failed: ${S.status}`)}destroy(){this.destroyed=!0,this.viewContentListenersTornDown=!0;let _=L(),E=$();if(E&&this.clickHandler&&(this.autoTrack.outboundLinks||this.autoTrack.buttonClicks))E.removeEventListener("click",this.clickHandler,!0);if(E&&this.submitHandler&&(this.autoTrack.formSubmissions||this.autoTrack.search))E.removeEventListener("submit",this.submitHandler,!0);if(this.viewContentObserver?.disconnect(),this.viewContentObserver=null,this.viewContentMutationObserver?.disconnect(),this.viewContentMutationObserver=null,_&&this.popstateHandler)_.removeEventListener("popstate",this.popstateHandler);if(_&&this.pagehideHandler)_.removeEventListener("pagehide",this.pagehideHandler);if(this.flushTimer)clearTimeout(this.flushTimer),this.flushTimer=null;this.unpatchHistory?.(),this.unpatchHistory=null,this.flushWithBeacon()}}function N_(_){return O.init(_)}export{A as listStandardWebEvents,N_ as createBTSAnalytics,O as BTSAnalytics};
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@behindthescenes/analytics",
3
- "version": "0.0.2",
4
- "generatedAt": "2026-04-29T03:01:23.243Z",
3
+ "version": "0.0.5",
4
+ "generatedAt": "2026-04-29T23:17:34.940Z",
5
5
  "artifacts": {
6
6
  "browser": "browser/browser.js",
7
7
  "cjs": "cjs/index.js",
@@ -7,6 +7,9 @@ declare global {
7
7
  listStandardWebEvents: typeof listStandardWebEvents;
8
8
  };
9
9
  createBTSAnalytics?: typeof createBTSAnalytics;
10
+ btsAnalytics?: BTSAnalytics;
11
+ btsDataLayer?: Array<ArrayLike<unknown>>;
12
+ bts?: (...args: unknown[]) => void;
10
13
  }
11
14
  }
12
15
  export { BTSAnalytics, createBTSAnalytics, listStandardWebEvents, type BTSAnalyticsEventType, type BTSAnalyticsContactFormInput, type BTSAnalyticsContactFormResult, type BTSAnalyticsInit, type BTSAnalyticsRequestContext, type BTSAnalyticsRequestHeaders, type BTSAnalyticsStandardEventName, };
@@ -11,6 +11,7 @@ type AutoTrackConfig = {
11
11
  export type BTSAnalyticsRequestContext = {
12
12
  body: unknown;
13
13
  bodyText: string;
14
+ /** Base URL for the outgoing request (e.g. analytics `.../website/analytics` vs website `.../website`). */
14
15
  endpoint: string;
15
16
  headers: Record<string, string>;
16
17
  path: string;
@@ -26,6 +27,14 @@ export type BTSAnalyticsInit = {
26
27
  autoTrack?: boolean | Partial<AutoTrackConfig>;
27
28
  debug?: boolean;
28
29
  flushIntervalMs?: number;
30
+ /**
31
+ * Optional GA4 compatibility mode. When a measurement ID is provided, the SDK loads the Google tag with
32
+ * `send_page_view: false` so GA scanners can detect an installed tag while BTS forwards events server-side.
33
+ */
34
+ googleAnalytics?: boolean | {
35
+ loadTag?: boolean;
36
+ measurementId?: string;
37
+ };
29
38
  requestHeaders?: BTSAnalyticsRequestHeaders;
30
39
  };
31
40
  export type BTSAnalyticsPayloadProperties = Record<string, unknown>;
@@ -69,6 +78,9 @@ export declare class BTSAnalytics {
69
78
  private pagehideHandler;
70
79
  private popstateHandler;
71
80
  private viewContentObserver;
81
+ private viewContentMutationObserver;
82
+ private viewContentRescanPending;
83
+ private viewContentListenersTornDown;
72
84
  private observedViewContentElements;
73
85
  private trackedViewContentElements;
74
86
  constructor(init: BTSAnalyticsInit);
@@ -78,6 +90,11 @@ export declare class BTSAnalytics {
78
90
  private getPersistedJourneyId;
79
91
  private log;
80
92
  private installAutoTracking;
93
+ private getOrCreateViewContentIntersectionObserver;
94
+ /** Re-query `[data-bts-view-content]` and register any new elements with the intersection observer. */
95
+ private observeViewContentElements;
96
+ private scheduleViewContentRescanFromMutations;
97
+ private rescanViewContentAfterNavigation;
81
98
  private installViewContentTracking;
82
99
  private trackViewContentElement;
83
100
  private patchHistory;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@behindthescenes/analytics",
3
- "version": "0.0.2",
3
+ "version": "0.0.5",
4
4
  "description": "Browser analytics SDK for BTS external-site event tracking and attribution.",
5
5
  "license": "UNLICENSED",
6
6
  "type": "module",