@product-intelligence-hub/sdk-web 0.3.0 → 0.4.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -1,3 +1,3 @@
1
- 'use strict';Object.defineProperty(exports,'__esModule',{value:true});var react=require('react');var N=Object.defineProperty;var D=(t,e,i)=>e in t?N(t,e,{enumerable:true,configurable:true,writable:true,value:i}):t[e]=i;var r=(t,e,i)=>D(t,typeof e!="symbol"?e+"":e,i);var V=Object.defineProperty,M=(t,e,i)=>e in t?V(t,e,{enumerable:true,configurable:true,writable:true,value:i}):t[e]=i,a=(t,e,i)=>M(t,typeof e!="symbol"?e+"":e,i),h=class d extends Error{constructor(e,i,s){super(e),a(this,"code"),a(this,"details"),this.name="PIHError",this.code=i,this.details=s,Error.captureStackTrace&&Error.captureStackTrace(this,d);}static networkError(e,i){return new d(e,"NETWORK_ERROR",i)}static invalidConfig(e,i){return new d(e,"INVALID_CONFIG",i)}static queueFull(e,i){return new d(e,"QUEUE_FULL",i)}static rateLimited(e,i){return new d(e,"RATE_LIMITED",i)}static invalidPayload(e,i){return new d(e,"INVALID_PAYLOAD",i)}static storageError(e,i){return new d(e,"STORAGE_ERROR",i)}static sessionError(e,i){return new d(e,"SESSION_ERROR",i)}static fromUnknown(e){return e instanceof d?e:e instanceof Error?new d(e.message,"UNKNOWN_ERROR",e):new d(String(e),"UNKNOWN_ERROR",e)}};function y(){if(typeof crypto<"u"&&typeof crypto.randomUUID=="function")return crypto.randomUUID();if(typeof crypto<"u"&&typeof crypto.getRandomValues=="function"){let t=new Uint8Array(16);crypto.getRandomValues(t),t[6]=t[6]&15|64,t[8]=t[8]&63|128;let e=Array.from(t,i=>i.toString(16).padStart(2,"0")).join("");return `${e.slice(0,8)}-${e.slice(8,12)}-${e.slice(12,16)}-${e.slice(16,20)}-${e.slice(20)}`}return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,t=>{let e=Math.random()*16|0;return (t==="x"?e:e&3|8).toString(16)})}function A(){return new Date().toISOString()}function j(t){return t.toISOString()}function q(t,e=1e3,i=16e3){let s=e*Math.pow(2,t);return Math.min(s,i)}var g="pih_",l={QUEUE:`${g}queue`,ANONYMOUS_ID:`${g}anonymous_id`,USER_ID:`${g}user_id`,USER_TRAITS:`${g}user_traits`,SESSION_ID:`${g}session_id`,SESSION_LAST_ACTIVITY:`${g}session_last_activity`,SESSION_NUMBER:`${g}session_number`,EVENT_INDEX:`${g}event_index`,FLAGS:`${g}flags`},u={API_URL:"https://repoingest-production.up.railway.app",FLUSH_INTERVAL:1e4,FLUSH_AT:25,MAX_QUEUE_SIZE:5e3,SESSION_TIMEOUT:18e5,MAX_BATCH_SIZE:100,MAX_RETRIES:5,BASE_BACKOFF_DELAY:1e3,MAX_BACKOFF_DELAY:16e3},z=class{constructor(t,e=false){a(this,"anonymousId"),a(this,"userId",null),a(this,"userTraits",{}),a(this,"storage"),a(this,"debug"),this.storage=t,this.debug=e,this.anonymousId=y();}async initialize(){if(!this.storage){this.log("No storage adapter, using in-memory identity only");return}try{let t=await this.storage.get(l.ANONYMOUS_ID);t?(this.anonymousId=t,this.log("Loaded anonymous ID from storage:",this.anonymousId)):(await this.storage.set(l.ANONYMOUS_ID,this.anonymousId),this.log("Generated new anonymous ID:",this.anonymousId));let e=await this.storage.get(l.USER_ID);e&&(this.userId=e,this.log("Loaded user ID from storage:",this.userId));let i=await this.storage.get(l.USER_TRAITS);if(i)try{this.userTraits=JSON.parse(i),this.log("Loaded user traits from storage:",this.userTraits);}catch{this.log("Failed to parse stored user traits");}}catch(t){this.log("Error loading identity from storage:",t);}}getAnonymousId(){return this.anonymousId}getUserId(){return this.userId}getUserTraits(){return {...this.userTraits}}async setUserId(t){if(this.userId=t,this.log("Set user ID:",t),this.storage)try{await this.storage.set(l.USER_ID,t);}catch(e){this.log("Error persisting user ID:",e);}}async setUserTraits(t){if(this.userTraits={...this.userTraits,...t},this.log("Set user traits:",this.userTraits),this.storage)try{await this.storage.set(l.USER_TRAITS,JSON.stringify(this.userTraits));}catch(e){this.log("Error persisting user traits:",e);}}async reset(){if(this.anonymousId=y(),this.userId=null,this.userTraits={},this.log("Identity reset, new anonymous ID:",this.anonymousId),this.storage)try{await Promise.all([this.storage.set(l.ANONYMOUS_ID,this.anonymousId),this.storage.remove(l.USER_ID),this.storage.remove(l.USER_TRAITS)]);}catch(t){this.log("Error resetting identity in storage:",t);}}setDebug(t){this.debug=t;}log(...t){this.debug&&[...t];}},B=class{constructor(t){a(this,"queue",[]),a(this,"isFlushing",false),a(this,"flushTimer",null),a(this,"retryTimer",null),a(this,"config"),a(this,"initialized",false),this.config={flushAt:t.flushAt??u.FLUSH_AT,flushInterval:t.flushInterval??u.FLUSH_INTERVAL,maxQueueSize:t.maxQueueSize??u.MAX_QUEUE_SIZE,storage:t.storage,transport:t.transport,onError:t.onError,debug:t.debug??false};}async initialize(){this.initialized||(await this.loadPersistedQueue(),this.startFlushTimer(),this.initialized=true,this.log("Queue initialized with",this.queue.length,"persisted events"));}async enqueue(t){for(;this.queue.length>=this.config.maxQueueSize;){let i=this.queue.shift();i&&(this.log("Queue full, dropping oldest event:",i.event.event_id),this.config.onError?.(h.queueFull("Queue full, dropped oldest event",{dropped_event_id:i.event.event_id}),[i.event]));}let e={event:t,attempts:0,firstAttempt:Date.now()};this.queue.push(e),this.log("Enqueued event:",t.event_id,"Queue size:",this.queue.length),await this.persistQueue(),this.queue.length>=this.config.flushAt&&(this.log("Queue reached flushAt threshold, flushing"),this.flush());}async flush(t){if(!(this.isFlushing||this.queue.length===0)){this.isFlushing=true,this.log("Flushing",this.queue.length,"events");try{let e=this.queue.splice(0,u.MAX_BATCH_SIZE),i=e.map(s=>s.event);try{let s=await this.config.transport.sendEvents(i,t);for(let n of s.rejected)if(n.reason==="rate_limited"){let o=e.find(c=>c.event.event_id===n.event_id);o&&o.attempts<u.MAX_RETRIES&&(o.attempts++,this.queue.unshift(o),this.log("Rate limited event re-queued:",n.event_id,"attempt:",o.attempts));}else this.log("Event permanently rejected:",n.event_id,n.reason,n.details);this.log("Flush complete. Accepted:",s.stats.accepted,"Rejected:",s.stats.rejected);}catch(s){let n=h.fromUnknown(s);for(let m of e)m.attempts<u.MAX_RETRIES?(m.attempts++,this.queue.unshift(m)):this.log("Event dropped after max retries:",m.event.event_id);let o=e[0]?.attempts??1,c=q(o);this.log("Network error, scheduling retry in",c,"ms"),this.retryTimer=setTimeout(()=>{this.retryTimer=null,this.flush(t);},c),this.config.onError?.(n,i);}await this.persistQueue(),this.queue.length>=this.config.flushAt&&setTimeout(()=>this.flush(t),0);}finally{this.isFlushing=false;}}}getQueueLength(){return this.queue.length}isEmpty(){return this.queue.length===0}isBusy(){return this.isFlushing}async clear(){this.queue=[],await this.persistQueue(),this.log("Queue cleared");}destroy(){this.flushTimer&&(clearInterval(this.flushTimer),this.flushTimer=null),this.retryTimer&&(clearTimeout(this.retryTimer),this.retryTimer=null),this.log("Queue destroyed");}setDebug(t){this.config.debug=t;}async persistQueue(){if(this.config.storage)try{await this.config.storage.set(l.QUEUE,JSON.stringify(this.queue));}catch(t){this.log("Error persisting queue:",t);}}async loadPersistedQueue(){if(this.config.storage)try{let t=await this.config.storage.get(l.QUEUE);if(t){let e=JSON.parse(t);Array.isArray(e)&&(this.queue=e.filter(i=>i&&typeof i=="object"&&"event"in i&&"attempts"in i));}}catch(t){this.log("Error loading persisted queue:",t),this.queue=[];}}startFlushTimer(){this.flushTimer||(this.flushTimer=setInterval(()=>{this.queue.length>0&&(this.log("Flush timer triggered"),this.flush());},this.config.flushInterval));}log(...t){this.config.debug&&[...t];}},$=class{constructor(t,e=u.SESSION_TIMEOUT,i={},s=false){a(this,"sessionId",null),a(this,"sessionStartTime",0),a(this,"lastActivity",0),a(this,"timeout"),a(this,"storage"),a(this,"callbacks"),a(this,"debug"),this.storage=t,this.timeout=e,this.callbacks=i,this.debug=s;}async initialize(){if(!this.storage){this.log("No storage adapter, using in-memory session only");return}try{let t=await this.storage.get(l.SESSION_ID),e=await this.storage.get(l.SESSION_LAST_ACTIVITY);if(t&&e){let i=parseInt(e,10);Date.now()-i<this.timeout?(this.sessionId=t,this.lastActivity=i,this.sessionStartTime=i,this.log("Restored session from storage:",this.sessionId)):(this.log("Stored session expired, will start new session"),await this.clearStoredSession());}}catch(t){this.log("Error loading session from storage:",t);}}getSessionId(){let t=Date.now();return this.sessionId&&t-this.lastActivity>this.timeout&&this.endSession(),this.sessionId||this.startSession(),this.lastActivity=t,this.persistLastActivity(),this.sessionId}touch(){this.sessionId&&(this.lastActivity=Date.now(),this.persistLastActivity());}hasActiveSession(){return this.sessionId?Date.now()-this.lastActivity<this.timeout:false}getCurrentSessionId(){return this.sessionId}getSessionDuration(){return this.sessionId?Date.now()-this.sessionStartTime:0}startSession(){let t=Date.now();this.sessionId=y(),this.sessionStartTime=t,this.lastActivity=t,this.log("Started new session:",this.sessionId),this.persistSession(),this.callbacks.onSessionStart?.(this.sessionId);}endSession(){if(!this.sessionId)return;let t=this.getSessionDuration(),e=this.sessionId;this.log("Ended session:",e,"duration:",t),this.sessionId=null,this.sessionStartTime=0,this.lastActivity=0,this.clearStoredSession(),this.callbacks.onSessionEnd?.(e,t);}forceEndSession(){this.sessionId&&this.endSession();}setDebug(t){this.debug=t;}setTimeout(t){this.timeout=t;}async persistSession(){if(!(!this.storage||!this.sessionId))try{await Promise.all([this.storage.set(l.SESSION_ID,this.sessionId),this.storage.set(l.SESSION_LAST_ACTIVITY,String(this.lastActivity))]);}catch(t){this.log("Error persisting session:",t);}}async persistLastActivity(){if(this.storage)try{await this.storage.set(l.SESSION_LAST_ACTIVITY,String(this.lastActivity));}catch(t){this.log("Error persisting last activity:",t);}}async clearStoredSession(){if(this.storage)try{await Promise.all([this.storage.remove(l.SESSION_ID),this.storage.remove(l.SESSION_LAST_ACTIVITY)]);}catch(t){this.log("Error clearing stored session:",t);}}log(...t){this.debug&&[...t];}},W={name:"pih-sdk-core",version:"0.3.0"},Q=class{constructor(t){a(this,"apiUrl"),a(this,"apiKey"),a(this,"tenantId"),a(this,"debug"),a(this,"sdkMeta"),this.apiUrl=t.apiUrl.replace(/\/$/,""),this.apiKey=t.apiKey,this.tenantId=t.tenantId,this.debug=t.debug??false,this.sdkMeta=t.sdkMeta??W;}async sendEvents(t,e){let i=`${this.apiUrl}/v1/track`,s=t.map(o=>this.toTrackApiPayload(o)),n=JSON.stringify({events:s});if(this.log("Sending",t.length,"events to",i),e?.useBeacon&&typeof navigator<"u"&&typeof navigator.sendBeacon=="function"&&this.sendViaBeacon(i,n))return this.log("Events sent via sendBeacon"),{accepted:t.map(o=>({event_id:o.event_id,timestamp:o.timestamp})),rejected:[],stats:{received:t.length,accepted:t.length,rejected:0,processing_time_ms:0}};try{let o=await this.fetch(i,{method:"POST",headers:this.getHeaders(),body:n,keepalive:e?.useBeacon});if(!o.ok)throw o.status===429?h.rateLimited(`Rate limited: ${o.statusText}`,{status:o.status}):h.networkError(`HTTP ${o.status}: ${o.statusText}`,{status:o.status});let c=await o.json();return this.log("Track response:",c),c}catch(o){throw o instanceof h?o:h.networkError(`Failed to send events: ${o instanceof Error?o.message:String(o)}`,o)}}async sendIdentify(t){let e=`${this.apiUrl}/v1/identify`,i=JSON.stringify(this.toIdentifyApiPayload(t));this.log("Sending identify to",e,t);try{let s=await this.fetch(e,{method:"POST",headers:this.getHeaders(),body:i});if(!s.ok)throw s.status===429?h.rateLimited(`Rate limited: ${s.statusText}`,{status:s.status}):h.networkError(`HTTP ${s.status}: ${s.statusText}`,{status:s.status});let n=await s.json();return this.log("Identify response:",n),n}catch(s){throw s instanceof h?s:h.networkError(`Failed to send identify: ${s instanceof Error?s.message:String(s)}`,s)}}async fetchFlags(){let t=`${this.apiUrl}/v1/flags`;this.log("Fetching flags from",t);try{let e=await this.fetch(t,{method:"GET",headers:this.getHeaders()});if(!e.ok)throw h.networkError(`HTTP ${e.status}: ${e.statusText}`);let i=await e.json();return this.log("Flags response:",i),i.flags}catch(e){throw e instanceof h?e:h.networkError(`Failed to fetch flags: ${e instanceof Error?e.message:String(e)}`)}}async evaluateFlags(t){let e=`${this.apiUrl}/v1/flags/evaluate`,i=JSON.stringify({userId:t.userId??void 0,anonymousId:t.anonymousId,userTraits:t.userTraits});this.log("Evaluating flags at",e);try{let s=await this.fetch(e,{method:"POST",headers:this.getHeaders(),body:i});if(!s.ok)throw h.networkError(`HTTP ${s.status}: ${s.statusText}`);let n=await s.json();return this.log("Evaluate flags response:",n),n.flags}catch(s){throw s instanceof h?s:h.networkError(`Failed to evaluate flags: ${s instanceof Error?s.message:String(s)}`)}}setTenantId(t){this.tenantId=t;}setDebug(t){this.debug=t;}setSDKMeta(t){this.sdkMeta=t;}getHeaders(){let t={"Content-Type":"application/json",Authorization:`Bearer ${this.apiKey}`,"X-SDK-Name":this.sdkMeta.name,"X-SDK-Version":this.sdkMeta.version};return this.tenantId&&(t["X-Tenant-Id"]=this.tenantId),t}toTrackApiPayload(t){return {eventId:t.event_id,eventName:t.event_name,timestamp:t.timestamp,anonymousId:t.anonymous_id,userId:t.user_id??void 0,sessionId:t.session_id??void 0,properties:t.properties,userTraits:t.user_traits,context:{platform:t.platform,appVersion:t.app_version??void 0,...t.context??{}}}}toIdentifyApiPayload(t){return {userId:t.user_id,anonymousId:t.anonymous_id,traits:t.traits,timestamp:t.timestamp}}sendViaBeacon(t,e){try{let i=new Blob([e],{type:"application/json"});return navigator.sendBeacon(t,i)}catch(i){return this.log("sendBeacon failed:",i),false}}async fetch(t,e){if(typeof fetch<"u")return fetch(t,e);throw h.networkError("fetch is not available in this environment")}log(...t){this.debug&&[...t];}},U=class{constructor(t,e=null){a(this,"config"),a(this,"storage",null),a(this,"identity"),a(this,"session"),a(this,"transport"),a(this,"queue"),a(this,"initialized",false),a(this,"tenantId"),a(this,"sessionNumber",0),a(this,"eventIndex",0),a(this,"lastSessionId",null),a(this,"flags",{}),a(this,"flagRefreshTimer",null),this.validateConfig(t),this.config={apiUrl:u.API_URL,debug:false,flushInterval:u.FLUSH_INTERVAL,flushAt:u.FLUSH_AT,maxQueueSize:u.MAX_QUEUE_SIZE,sessionTimeout:u.SESSION_TIMEOUT,...t},this.storage=e,this.tenantId=t.tenantId??"",this.identity=new z(e,this.config.debug),this.session=new $(e,this.config.sessionTimeout,{onSessionStart:i=>{this.log("Session started:",i),this.trackInternal("session_started",{session_id:i});},onSessionEnd:(i,s)=>{this.log("Session ended:",i,"duration:",s),this.trackInternal("session_ended",{session_id:i,duration_ms:s});}},this.config.debug),this.transport=new Q({apiUrl:this.config.apiUrl,apiKey:this.config.apiKey,tenantId:this.tenantId,debug:this.config.debug}),this.queue=new B({flushAt:this.config.flushAt,flushInterval:this.config.flushInterval,maxQueueSize:this.config.maxQueueSize,storage:e,transport:this.transport,onError:(i,s)=>{this.config.onError?.(i),this.log("Queue error:",i.message,"events:",s.length);},debug:this.config.debug});}async initialize(){if(!this.initialized&&(await Promise.all([this.identity.initialize(),this.session.initialize(),this.queue.initialize()]),await this.loadSessionEnrichment(),await this.loadCachedFlags(),this.initialized=true,this.log("Client initialized"),this.config.featureFlags?.autoFetch!==false)){this.refreshFlags().catch(e=>{this.log("Auto-fetch flags error:",e);});let t=this.config.featureFlags?.refreshInterval??3e5;t>0&&(this.flagRefreshTimer=setInterval(()=>{this.refreshFlags().catch(e=>{this.log("Flag refresh error:",e);});},t));}}setTenant(t){this.tenantId=t,this.transport.setTenantId(t),this.log("Tenant set:",t);}async identify(t,e){await this.ensureInitialized(),await this.identity.setUserId(t),e&&await this.identity.setUserTraits(e);try{await this.transport.sendIdentify({project_id:this.config.projectId,environment:this.config.environment,tenant_id:this.tenantId,anonymous_id:this.identity.getAnonymousId(),user_id:t,traits:e,timestamp:A()});}catch(i){let s=h.fromUnknown(i);this.config.onError?.(s),this.log("Identify error:",s.message);}this.config.featureFlags?.autoFetch!==false&&this.refreshFlags().catch(i=>{this.log("Post-identify flag refresh error:",i);});}async setUserTraits(t){await this.ensureInitialized(),await this.identity.setUserTraits(t);}async track(t,e,i){await this.ensureInitialized();let s=this.createEvent(t,e,i);if(this.session.touch(),i?.immediate)try{await this.transport.sendEvents([s]);}catch(n){let o=h.fromUnknown(n);this.config.onError?.(o),this.log("Immediate track error:",o.message);}else await this.queue.enqueue(s);}async flush(t){await this.ensureInitialized(),await this.queue.flush(t);}async reset(){await this.ensureInitialized(),this.session.forceEndSession(),await this.identity.reset(),this.log("Client reset");}setDebug(t){this.config.debug=t,this.identity.setDebug(t),this.session.setDebug(t),this.transport.setDebug(t),this.queue.setDebug(t);}getAnonymousId(){return this.identity.getAnonymousId()}getUserId(){return this.identity.getUserId()}getSessionId(){return this.session.getCurrentSessionId()}getQueueLength(){return this.queue.getQueueLength()}isFeatureEnabled(t,e=false){return this.flags[t]??e}getFeatureFlag(t,e=false){return this.flags[t]??e}getFeatureFlags(){return {...this.flags}}async refreshFlags(){try{let t=await this.transport.evaluateFlags({userId:this.identity.getUserId(),anonymousId:this.identity.getAnonymousId(),userTraits:this.identity.getUserTraits()});this.flags=t,this.persistFlags(),this.config.featureFlags?.onFlagsUpdated?.(t),this.log("Flags refreshed:",Object.keys(t).length,"flags");}catch(t){this.log("Flag refresh error:",t);}}destroy(){this.flagRefreshTimer&&(clearInterval(this.flagRefreshTimer),this.flagRefreshTimer=null),this.queue.destroy(),this.log("Client destroyed");}trackInternal(t,e){let i=this.createEvent(t,e);this.queue.enqueue(i).catch(s=>{this.log("Internal track error:",s);});}getContext(){return {}}createEvent(t,e,i){let s=i?.timestamp?j(i.timestamp):A(),n=this.session.getSessionId();this.updateSessionEnrichment();let o={...this.getContext(),sessionNumber:this.sessionNumber,eventIndex:this.eventIndex};return {event_id:y(),timestamp:s,project_id:this.config.projectId,environment:this.config.environment,tenant_id:this.tenantId,event_name:t,anonymous_id:this.identity.getAnonymousId(),user_id:this.identity.getUserId(),session_id:n,platform:this.config.platform,app_version:this.config.appVersion??null,properties:e??{},user_traits:this.identity.getUserTraits(),context:o}}async ensureInitialized(){this.initialized||await this.initialize();}validateConfig(t){if(!t.apiKey)throw h.invalidConfig("apiKey is required");if(!t.projectId)throw h.invalidConfig("projectId is required");if(!t.environment)throw h.invalidConfig("environment is required");if(!t.platform)throw h.invalidConfig("platform is required")}async loadSessionEnrichment(){if(this.storage)try{let t=await this.storage.get(l.SESSION_NUMBER),e=await this.storage.get(l.EVENT_INDEX);t&&(this.sessionNumber=parseInt(t,10)||0),e&&(this.eventIndex=parseInt(e,10)||0);}catch(t){this.log("Error loading session enrichment:",t);}}updateSessionEnrichment(){let t=this.session.getCurrentSessionId();t&&t!==this.lastSessionId&&(this.sessionNumber++,this.eventIndex=0,this.lastSessionId=t,this.persistSessionEnrichment()),this.eventIndex++,this.persistEventIndex();}persistSessionEnrichment(){this.storage&&this.storage.set(l.SESSION_NUMBER,String(this.sessionNumber)).catch(t=>{this.log("Error persisting session number:",t);});}persistEventIndex(){this.storage&&this.storage.set(l.EVENT_INDEX,String(this.eventIndex)).catch(t=>{this.log("Error persisting event index:",t);});}async loadCachedFlags(){if(this.storage)try{let t=await this.storage.get(l.FLAGS);t&&(this.flags=JSON.parse(t));}catch(t){this.log("Error loading cached flags:",t);}}persistFlags(){this.storage&&this.storage.set(l.FLAGS,JSON.stringify(this.flags)).catch(t=>{this.log("Error persisting flags:",t);});}log(...t){this.config.debug&&[...t];}};var H="pih_utm_params",K=["utm_source","utm_medium","utm_campaign","utm_term","utm_content"],p=class{constructor(e,i={}){r(this,"client");r(this,"config");r(this,"cachedUtmParams",null);r(this,"originalPushState",null);r(this,"originalReplaceState",null);r(this,"boundHandleClick");r(this,"boundHandlePopState");r(this,"boundHandleSubmit");this.client=e,this.config={pageViews:i.pageViews!==false,clicks:i.clicks??false,clickSelector:i.clickSelector??"[data-track]",forms:i.forms??false},this.boundHandleClick=this.handleClick.bind(this),this.boundHandlePopState=this.handlePopState.bind(this),this.boundHandleSubmit=this.handleSubmit.bind(this);}start(){typeof window>"u"||typeof document>"u"||(this.loadCachedUtmParams(),this.config.pageViews&&(this.setupPageViews(),this.trackPageView()),this.config.clicks&&this.setupClickTracking(),this.config.forms&&this.setupFormTracking());}stop(){this.originalPushState&&(history.pushState=this.originalPushState,this.originalPushState=null),this.originalReplaceState&&(history.replaceState=this.originalReplaceState,this.originalReplaceState=null),typeof window<"u"&&window.removeEventListener("popstate",this.boundHandlePopState),typeof document<"u"&&(document.removeEventListener("click",this.boundHandleClick,true),document.removeEventListener("submit",this.boundHandleSubmit,true));}trackPageView(){let e=new URL(window.location.href),i=this.extractUtmParams(e);Object.keys(i).length>0&&!this.cachedUtmParams&&(this.cachedUtmParams=i,this.persistUtmParams(i)),this.client.track("page_viewed",{path:e.pathname,search:e.search,hash:e.hash,referrer:document.referrer||null,title:document.title,url:window.location.href,...this.getUtmParams(e)});}setupPageViews(){this.originalPushState=history.pushState,this.originalReplaceState=history.replaceState;let e=this.originalPushState,i=this.originalReplaceState,s=()=>this.trackPageView();history.pushState=function(...n){e.apply(history,n),setTimeout(s,0);},history.replaceState=function(...n){i.apply(history,n),setTimeout(s,0);},window.addEventListener("popstate",this.boundHandlePopState);}handlePopState(){this.trackPageView();}setupClickTracking(){document.addEventListener("click",this.boundHandleClick,true);}handleClick(e){let i=e.target;if(!i)return;let s=i.closest(this.config.clickSelector);if(!s)return;let n=s.tagName.toLowerCase(),o=(s.textContent||"").trim().slice(0,100),c=s.id||null,m=s.className||null,L=s.getAttribute("data-track"),O=s instanceof HTMLAnchorElement?s.href:null;this.client.track("element_clicked",{element_tag:n,element_text:o,element_id:c,element_classes:m,data_track:L,href:O,path:window.location.pathname});}setupFormTracking(){document.addEventListener("submit",this.boundHandleSubmit,true);}handleSubmit(e){let i=e.target;!i||!(i instanceof HTMLFormElement)||this.client.track("form_submitted",{form_id:i.id||void 0,form_action:i.action||void 0,form_method:(i.method||"get").toUpperCase(),field_count:i.elements.length});}extractUtmParams(e){let i={};for(let s of K){let n=e.searchParams.get(s);n&&(i[s]=n);}return i}getUtmParams(e){let i=this.extractUtmParams(e);return Object.keys(i).length>0?i:this.cachedUtmParams??{}}loadCachedUtmParams(){try{let e=localStorage.getItem(H);e&&(this.cachedUtmParams=JSON.parse(e));}catch{}}persistUtmParams(e){try{localStorage.setItem(H,JSON.stringify(e));}catch{}}};var S=class{constructor(e){r(this,"client");r(this,"boundHandleVisibilityChange");r(this,"boundHandleBeforeUnload");this.client=e,this.boundHandleVisibilityChange=this.handleVisibilityChange.bind(this),this.boundHandleBeforeUnload=this.handleBeforeUnload.bind(this);}start(){typeof document<"u"&&document.addEventListener("visibilitychange",this.boundHandleVisibilityChange),typeof window<"u"&&(window.addEventListener("beforeunload",this.boundHandleBeforeUnload),window.addEventListener("pagehide",this.boundHandleBeforeUnload));}stop(){typeof document<"u"&&document.removeEventListener("visibilitychange",this.boundHandleVisibilityChange),typeof window<"u"&&(window.removeEventListener("beforeunload",this.boundHandleBeforeUnload),window.removeEventListener("pagehide",this.boundHandleBeforeUnload));}handleVisibilityChange(){document.visibilityState==="hidden"&&this.flushWithBeacon();}handleBeforeUnload(){this.flushWithBeacon();}flushWithBeacon(){this.client.flush({useBeacon:true}).catch(()=>{});}};function X(){if(typeof navigator>"u"||typeof window>"u")return "unknown";let t=navigator.userAgent.toLowerCase();if(/ipad|tablet|playbook|silk/.test(t))return "tablet";if(/mobile|iphone|ipod|android.*mobile|windows phone|blackberry|opera mini|opera mobi/.test(t))return "mobile";let e=window.screen?.width??0;return e>0&&e<768?"mobile":e>=768&&e<1024?"tablet":"desktop"}function Y(){return typeof navigator>"u"?void 0:navigator.connection?.effectiveType??void 0}function C(){if(typeof window>"u"||typeof document>"u")return {};let t={screenWidth:window.screen?.width??void 0,screenHeight:window.screen?.height??void 0,viewportWidth:window.innerWidth??void 0,viewportHeight:window.innerHeight??void 0,locale:navigator?.language??void 0,timezone:J(),deviceType:X(),pageUrl:window.location.href,pagePath:window.location.pathname,pageTitle:document.title||void 0,referrer:document.referrer||void 0},e=Y();return e&&(t.connectionType=e),Object.fromEntries(Object.entries(t).filter(([,i])=>i!==void 0))}function J(){try{return Intl.DateTimeFormat().resolvedOptions().timeZone}catch{return}}var G=[25,50,75,100],b=class{constructor(){r(this,"client",null);r(this,"started",false);r(this,"visibleStartTime",0);r(this,"totalVisibleTime",0);r(this,"reachedThresholds",new Set);r(this,"boundHandleVisibilityChange");r(this,"boundHandleScroll");this.boundHandleVisibilityChange=this.handleVisibilityChange.bind(this),this.boundHandleScroll=this.handleScroll.bind(this);}start(e){this.started||typeof window>"u"||typeof document>"u"||(this.client=e,this.started=true,this.visibleStartTime=Date.now(),this.totalVisibleTime=0,this.reachedThresholds.clear(),document.visibilityState==="visible"&&(this.visibleStartTime=Date.now()),document.addEventListener("visibilitychange",this.boundHandleVisibilityChange),window.addEventListener("scroll",this.boundHandleScroll,{passive:true}));}stop(){this.started&&(this.accumulateVisibleTime(),this.totalVisibleTime>0&&this.client&&this.client.track("page_engaged",{visible_duration_ms:this.totalVisibleTime,page_path:typeof window<"u"?window.location.pathname:void 0}),typeof document<"u"&&document.removeEventListener("visibilitychange",this.boundHandleVisibilityChange),typeof window<"u"&&window.removeEventListener("scroll",this.boundHandleScroll),this.started=false,this.client=null);}handleVisibilityChange(){!this.started||!this.client||(document.visibilityState==="hidden"?(this.accumulateVisibleTime(),this.totalVisibleTime>0&&(this.client.track("page_engaged",{visible_duration_ms:this.totalVisibleTime,page_path:window.location.pathname}),this.totalVisibleTime=0)):this.visibleStartTime=Date.now());}accumulateVisibleTime(){this.visibleStartTime>0&&(this.totalVisibleTime+=Date.now()-this.visibleStartTime,this.visibleStartTime=Date.now());}handleScroll(){if(!this.started||!this.client)return;let e=this.getScrollPercent();for(let i of G)e>=i&&!this.reachedThresholds.has(i)&&(this.reachedThresholds.add(i),this.client.track("scroll_depth_reached",{depth_percent:i,page_path:window.location.pathname}));}getScrollPercent(){let e=Math.max(document.body.scrollHeight,document.documentElement.scrollHeight,document.body.offsetHeight,document.documentElement.offsetHeight,document.body.clientHeight,document.documentElement.clientHeight),i=window.innerHeight,s=window.scrollY||document.documentElement.scrollTop,n=e-i;return n<=0?100:Math.min(100,Math.round(s/n*100))}};var I=class{constructor(){r(this,"client",null);r(this,"started",false);r(this,"errorCount",0);r(this,"boundHandleError");r(this,"boundHandleRejection");this.boundHandleError=this.handleError.bind(this),this.boundHandleRejection=this.handleRejection.bind(this);}start(e){this.started||typeof window>"u"||(this.client=e,this.started=true,this.errorCount=0,window.addEventListener("error",this.boundHandleError),window.addEventListener("unhandledrejection",this.boundHandleRejection));}stop(){this.started&&(typeof window<"u"&&(window.removeEventListener("error",this.boundHandleError),window.removeEventListener("unhandledrejection",this.boundHandleRejection)),this.started=false,this.client=null);}handleError(e){!this.started||!this.client||this.canReport()&&(this.errorCount++,this.client.track("js_error",{message:e.message||"Unknown error",filename:e.filename||void 0,lineno:e.lineno||void 0,colno:e.colno||void 0,stack:e.error?.stack||void 0}));}handleRejection(e){if(!this.started||!this.client||!this.canReport())return;this.errorCount++;let i=e.reason,s=i instanceof Error?i.message:typeof i=="string"?i:"Unhandled promise rejection",n=i instanceof Error?i.stack:void 0;this.client.track("js_error",{message:s,filename:void 0,lineno:void 0,colno:void 0,stack:n});}canReport(){return this.errorCount<10}};var w=class{constructor(){r(this,"client",null);r(this,"started",false);}start(e){this.started||(this.client=e,this.started=true,this.initWebVitals());}stop(){this.started=false,this.client=null;}async initWebVitals(){try{let i=await import('web-vitals'),s=n=>{!this.started||!this.client||this.client.track("web_vital",{name:n.name,value:n.value,rating:n.rating});};i.onLCP&&i.onLCP(s),i.onFID&&i.onFID(s),i.onCLS&&i.onCLS(s),i.onINP&&i.onINP(s),i.onTTFB&&i.onTTFB(s);}catch{}}};var v=class{constructor(){r(this,"data",new Map);}async get(e){return this.data.get(e)??null}async set(e,i){this.data.set(e,i);}async remove(e){this.data.delete(e);}};function Z(){try{let t="__pih_test__";return localStorage.setItem(t,t),localStorage.removeItem(t),!0}catch{return false}}var _=class{constructor(){r(this,"fallback",null);r(this,"useLocalStorage");this.useLocalStorage=Z(),this.useLocalStorage||(this.fallback=new v,console.warn("[PIH] localStorage unavailable, using in-memory storage. Data will not persist across page loads."));}async get(e){if(this.fallback)return this.fallback.get(e);try{return localStorage.getItem(e)}catch{return this.fallback||(this.fallback=new v),this.fallback.get(e)}}async set(e,i){if(this.fallback)return this.fallback.set(e,i);try{localStorage.setItem(e,i);}catch(s){return this.fallback||(this.fallback=new v,console.warn("[PIH] localStorage write failed, falling back to in-memory storage:",s)),this.fallback.set(e,i)}}async remove(e){if(this.fallback)return this.fallback.remove(e);try{localStorage.removeItem(e);}catch{}}};function k(){return new _}k();var P=new Set;function F(t){return P.add(t),()=>P.delete(t)}function tt(){for(let t of P)t();}function et(t,e=false){let i=react.useCallback(()=>E()?.isFeatureEnabled(t,e)??e,[t,e]);return react.useSyncExternalStore(F,i,()=>e)}function it(){let t=react.useCallback(()=>E()?.getFeatureFlags()??{},[]);return react.useSyncExternalStore(F,t,()=>({}))}var T=class extends U{constructor(i,s){let n={...i,platform:"web"};super(n,s??k());r(this,"autocapture",null);r(this,"beacon");r(this,"performanceTracker",null);r(this,"engagementTracker",null);r(this,"errorTracker",null);this.transport.setSDKMeta({name:"pih-sdk-web",version:"0.2.0"}),this.beacon=new S(this);}getContext(){return C()}async initialize(){await super.initialize(),this.beacon.start(),this.config.autocapture&&(this.autocapture=new p(this,this.config.autocapture),this.autocapture.start(),this.config.autocapture.performance&&(this.performanceTracker=new w,this.performanceTracker.start(this)),this.config.autocapture.engagement&&(this.engagementTracker=new b,this.engagementTracker.start(this)),this.config.autocapture.errorTracking&&(this.errorTracker=new I,this.errorTracker.start(this))),this.log("Web client initialized");}enableAutocapture(i){this.autocapture&&this.autocapture.stop(),this.autocapture=new p(this,i),this.autocapture.start();}disableAutocapture(){this.autocapture&&(this.autocapture.stop(),this.autocapture=null);}trackPageView(){if(this.autocapture)this.autocapture.trackPageView();else {let i=new URL(window.location.href);this.track("page_viewed",{path:i.pathname,search:i.search,hash:i.hash,referrer:document.referrer||null,title:document.title,url:window.location.href});}}destroy(){this.beacon.stop(),this.autocapture&&this.autocapture.stop(),this.performanceTracker&&(this.performanceTracker.stop(),this.performanceTracker=null),this.engagementTracker&&(this.engagementTracker.stop(),this.engagementTracker=null),this.errorTracker&&(this.errorTracker.stop(),this.errorTracker=null),super.destroy();}log(...i){this.config.debug&&[...i];}},f=null;function st(t){return f?(console.warn("[PIH] SDK already initialized. Returning existing instance."),f):(f=new T(t),f.initialize().catch(e=>{console.error("[PIH] Failed to initialize:",e);}),f)}function E(){return f}function nt(){f&&(f.destroy(),f=null);}var rt={init:st,getInstance:E,resetInstance:nt,WebPIHClient:T},Dt=rt;
2
- exports.WebPIHClient=T;exports.default=Dt;exports.getInstance=E;exports.init=st;exports.notifyFlagListeners=tt;exports.resetInstance=nt;exports.useFeatureFlag=et;exports.useFeatureFlags=it;//# sourceMappingURL=index.cjs.map
1
+ 'use strict';Object.defineProperty(exports,'__esModule',{value:true});var react=require('react');var j=Object.defineProperty;var q=(t,e,i)=>e in t?j(t,e,{enumerable:true,configurable:true,writable:true,value:i}):t[e]=i;var r=(t,e,i)=>q(t,typeof e!="symbol"?e+"":e,i);var $=Object.defineProperty,W=(t,e,i)=>e in t?$(t,e,{enumerable:true,configurable:true,writable:true,value:i}):t[e]=i,a=(t,e,i)=>W(t,typeof e!="symbol"?e+"":e,i),c=class u extends Error{constructor(e,i,s){super(e),a(this,"code"),a(this,"details"),this.name="PIHError",this.code=i,this.details=s,Error.captureStackTrace&&Error.captureStackTrace(this,u);}static networkError(e,i){return new u(e,"NETWORK_ERROR",i)}static invalidConfig(e,i){return new u(e,"INVALID_CONFIG",i)}static queueFull(e,i){return new u(e,"QUEUE_FULL",i)}static rateLimited(e,i){return new u(e,"RATE_LIMITED",i)}static invalidPayload(e,i){return new u(e,"INVALID_PAYLOAD",i)}static storageError(e,i){return new u(e,"STORAGE_ERROR",i)}static sessionError(e,i){return new u(e,"SESSION_ERROR",i)}static fromUnknown(e){return e instanceof u?e:e instanceof Error?new u(e.message,"UNKNOWN_ERROR",e):new u(String(e),"UNKNOWN_ERROR",e)}};function y(){if(typeof crypto<"u"&&typeof crypto.randomUUID=="function")return crypto.randomUUID();if(typeof crypto<"u"&&typeof crypto.getRandomValues=="function"){let t=new Uint8Array(16);crypto.getRandomValues(t),t[6]=t[6]&15|64,t[8]=t[8]&63|128;let e=Array.from(t,i=>i.toString(16).padStart(2,"0")).join("");return `${e.slice(0,8)}-${e.slice(8,12)}-${e.slice(12,16)}-${e.slice(16,20)}-${e.slice(20)}`}return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,t=>{let e=Math.random()*16|0;return (t==="x"?e:e&3|8).toString(16)})}function H(){return new Date().toISOString()}function z(t){return t.toISOString()}function K(t,e=1e3,i=16e3){let s=e*Math.pow(2,t);return Math.min(s,i)}var f="pih_",l={QUEUE:`${f}queue`,ANONYMOUS_ID:`${f}anonymous_id`,USER_ID:`${f}user_id`,USER_TRAITS:`${f}user_traits`,SESSION_ID:`${f}session_id`,SESSION_LAST_ACTIVITY:`${f}session_last_activity`,SESSION_NUMBER:`${f}session_number`,EVENT_INDEX:`${f}event_index`,FLAGS:`${f}flags`},d={API_URL:"https://repoingest-production.up.railway.app",FLUSH_INTERVAL:1e4,FLUSH_AT:25,MAX_QUEUE_SIZE:5e3,SESSION_TIMEOUT:18e5,MAX_BATCH_SIZE:100,MAX_RETRIES:5,BASE_BACKOFF_DELAY:1e3,MAX_BACKOFF_DELAY:16e3},Q=class{constructor(t,e=false){a(this,"anonymousId"),a(this,"userId",null),a(this,"userTraits",{}),a(this,"storage"),a(this,"debug"),this.storage=t,this.debug=e,this.anonymousId=y();}async initialize(){if(!this.storage){this.log("No storage adapter, using in-memory identity only");return}try{let t=await this.storage.get(l.ANONYMOUS_ID);t?(this.anonymousId=t,this.log("Loaded anonymous ID from storage:",this.anonymousId)):(await this.storage.set(l.ANONYMOUS_ID,this.anonymousId),this.log("Generated new anonymous ID:",this.anonymousId));let e=await this.storage.get(l.USER_ID);e&&(this.userId=e,this.log("Loaded user ID from storage:",this.userId));let i=await this.storage.get(l.USER_TRAITS);if(i)try{this.userTraits=JSON.parse(i),this.log("Loaded user traits from storage:",this.userTraits);}catch{this.log("Failed to parse stored user traits");}}catch(t){this.log("Error loading identity from storage:",t);}}getAnonymousId(){return this.anonymousId}getUserId(){return this.userId}getUserTraits(){return {...this.userTraits}}async setUserId(t){if(this.userId=t,this.log("Set user ID:",t),this.storage)try{await this.storage.set(l.USER_ID,t);}catch(e){this.log("Error persisting user ID:",e);}}async setUserTraits(t){if(this.userTraits={...this.userTraits,...t},this.log("Set user traits:",this.userTraits),this.storage)try{await this.storage.set(l.USER_TRAITS,JSON.stringify(this.userTraits));}catch(e){this.log("Error persisting user traits:",e);}}async reset(){if(this.anonymousId=y(),this.userId=null,this.userTraits={},this.log("Identity reset, new anonymous ID:",this.anonymousId),this.storage)try{await Promise.all([this.storage.set(l.ANONYMOUS_ID,this.anonymousId),this.storage.remove(l.USER_ID),this.storage.remove(l.USER_TRAITS)]);}catch(t){this.log("Error resetting identity in storage:",t);}}setDebug(t){this.debug=t;}log(...t){this.debug&&[...t];}},X=class{constructor(t){a(this,"queue",[]),a(this,"isFlushing",false),a(this,"flushTimer",null),a(this,"retryTimer",null),a(this,"config"),a(this,"initialized",false),this.config={flushAt:t.flushAt??d.FLUSH_AT,flushInterval:t.flushInterval??d.FLUSH_INTERVAL,maxQueueSize:t.maxQueueSize??d.MAX_QUEUE_SIZE,storage:t.storage,transport:t.transport,onError:t.onError,debug:t.debug??false};}async initialize(){this.initialized||(await this.loadPersistedQueue(),this.startFlushTimer(),this.initialized=true,this.log("Queue initialized with",this.queue.length,"persisted events"));}async enqueue(t){for(;this.queue.length>=this.config.maxQueueSize;){let i=this.queue.shift();i&&(this.log("Queue full, dropping oldest event:",i.event.event_id),this.config.onError?.(c.queueFull("Queue full, dropped oldest event",{dropped_event_id:i.event.event_id}),[i.event]));}let e={event:t,attempts:0,firstAttempt:Date.now()};this.queue.push(e),this.log("Enqueued event:",t.event_id,"Queue size:",this.queue.length),await this.persistQueue(),this.queue.length>=this.config.flushAt&&(this.log("Queue reached flushAt threshold, flushing"),this.flush());}async flush(t){if(!(this.isFlushing||this.queue.length===0)){this.isFlushing=true,this.log("Flushing",this.queue.length,"events");try{let e=this.queue.splice(0,d.MAX_BATCH_SIZE),i=e.map(s=>s.event);try{let s=await this.config.transport.sendEvents(i,t);for(let n of s.rejected)if(n.reason==="rate_limited"){let o=e.find(h=>h.event.event_id===n.event_id);o&&o.attempts<d.MAX_RETRIES&&(o.attempts++,this.queue.unshift(o),this.log("Rate limited event re-queued:",n.event_id,"attempt:",o.attempts));}else this.log("Event permanently rejected:",n.event_id,n.reason,n.details);this.log("Flush complete. Accepted:",s.stats.accepted,"Rejected:",s.stats.rejected);}catch(s){let n=c.fromUnknown(s);for(let g of e)g.attempts<d.MAX_RETRIES?(g.attempts++,this.queue.unshift(g)):this.log("Event dropped after max retries:",g.event.event_id);let o=e[0]?.attempts??1,h=K(o);this.log("Network error, scheduling retry in",h,"ms"),this.retryTimer=setTimeout(()=>{this.retryTimer=null,this.flush(t);},h),this.config.onError?.(n,i);}await this.persistQueue(),this.queue.length>=this.config.flushAt&&setTimeout(()=>this.flush(t),0);}finally{this.isFlushing=false;}}}getQueueLength(){return this.queue.length}isEmpty(){return this.queue.length===0}isBusy(){return this.isFlushing}async clear(){this.queue=[],await this.persistQueue(),this.log("Queue cleared");}destroy(){this.flushTimer&&(clearInterval(this.flushTimer),this.flushTimer=null),this.retryTimer&&(clearTimeout(this.retryTimer),this.retryTimer=null),this.log("Queue destroyed");}setDebug(t){this.config.debug=t;}async persistQueue(){if(this.config.storage)try{await this.config.storage.set(l.QUEUE,JSON.stringify(this.queue));}catch(t){this.log("Error persisting queue:",t);}}async loadPersistedQueue(){if(this.config.storage)try{let t=await this.config.storage.get(l.QUEUE);if(t){let e=JSON.parse(t);Array.isArray(e)&&(this.queue=e.filter(i=>i&&typeof i=="object"&&"event"in i&&"attempts"in i));}}catch(t){this.log("Error loading persisted queue:",t),this.queue=[];}}startFlushTimer(){this.flushTimer||(this.flushTimer=setInterval(()=>{this.queue.length>0&&(this.log("Flush timer triggered"),this.flush());},this.config.flushInterval));}log(...t){this.config.debug&&[...t];}},G=class{constructor(t,e=d.SESSION_TIMEOUT,i={},s=false){a(this,"sessionId",null),a(this,"sessionStartTime",0),a(this,"lastActivity",0),a(this,"timeout"),a(this,"storage"),a(this,"callbacks"),a(this,"debug"),this.storage=t,this.timeout=e,this.callbacks=i,this.debug=s;}async initialize(){if(!this.storage){this.log("No storage adapter, using in-memory session only");return}try{let t=await this.storage.get(l.SESSION_ID),e=await this.storage.get(l.SESSION_LAST_ACTIVITY);if(t&&e){let i=parseInt(e,10);Date.now()-i<this.timeout?(this.sessionId=t,this.lastActivity=i,this.sessionStartTime=i,this.log("Restored session from storage:",this.sessionId)):(this.log("Stored session expired, will start new session"),await this.clearStoredSession());}}catch(t){this.log("Error loading session from storage:",t);}}getSessionId(){let t=Date.now();return this.sessionId&&t-this.lastActivity>this.timeout&&this.endSession(),this.sessionId||this.startSession(),this.lastActivity=t,this.persistLastActivity(),this.sessionId}touch(){this.sessionId&&(this.lastActivity=Date.now(),this.persistLastActivity());}hasActiveSession(){return this.sessionId?Date.now()-this.lastActivity<this.timeout:false}getCurrentSessionId(){return this.sessionId}getSessionDuration(){return this.sessionId?Date.now()-this.sessionStartTime:0}startSession(){let t=Date.now();this.sessionId=y(),this.sessionStartTime=t,this.lastActivity=t,this.log("Started new session:",this.sessionId),this.persistSession(),this.callbacks.onSessionStart?.(this.sessionId);}endSession(){if(!this.sessionId)return;let t=this.getSessionDuration(),e=this.sessionId;this.log("Ended session:",e,"duration:",t),this.sessionId=null,this.sessionStartTime=0,this.lastActivity=0,this.clearStoredSession(),this.callbacks.onSessionEnd?.(e,t);}forceEndSession(){this.sessionId&&this.endSession();}setDebug(t){this.debug=t;}setTimeout(t){this.timeout=t;}async persistSession(){if(!(!this.storage||!this.sessionId))try{await Promise.all([this.storage.set(l.SESSION_ID,this.sessionId),this.storage.set(l.SESSION_LAST_ACTIVITY,String(this.lastActivity))]);}catch(t){this.log("Error persisting session:",t);}}async persistLastActivity(){if(this.storage)try{await this.storage.set(l.SESSION_LAST_ACTIVITY,String(this.lastActivity));}catch(t){this.log("Error persisting last activity:",t);}}async clearStoredSession(){if(this.storage)try{await Promise.all([this.storage.remove(l.SESSION_ID),this.storage.remove(l.SESSION_LAST_ACTIVITY)]);}catch(t){this.log("Error clearing stored session:",t);}}log(...t){this.debug&&[...t];}},Y={name:"pih-sdk-core",version:"0.4.1"},J=class{constructor(t){a(this,"apiUrl"),a(this,"apiKey"),a(this,"tenantId"),a(this,"debug"),a(this,"sdkMeta"),this.apiUrl=t.apiUrl.replace(/\/$/,""),this.apiKey=t.apiKey,this.tenantId=t.tenantId,this.debug=t.debug??false,this.sdkMeta=t.sdkMeta??Y;}async sendEvents(t,e){let i=`${this.apiUrl}/v1/track`,s=t.map(o=>this.toTrackApiPayload(o)),n=JSON.stringify({events:s});this.log("Sending",t.length,"events to",i);try{let o=await this.fetch(i,{method:"POST",headers:this.getHeaders(),body:n,keepalive:e?.useBeacon});if(!o.ok)throw o.status===429?c.rateLimited(`Rate limited: ${o.statusText}`,{status:o.status}):c.networkError(`HTTP ${o.status}: ${o.statusText}`,{status:o.status});let h=await o.json();return this.log("Track response:",h),h}catch(o){throw o instanceof c?o:c.networkError(`Failed to send events: ${o instanceof Error?o.message:String(o)}`,o)}}async sendIdentify(t){let e=`${this.apiUrl}/v1/identify`,i=JSON.stringify(this.toIdentifyApiPayload(t));this.log("Sending identify to",e,t);try{let s=await this.fetch(e,{method:"POST",headers:this.getHeaders(),body:i});if(!s.ok)throw s.status===429?c.rateLimited(`Rate limited: ${s.statusText}`,{status:s.status}):c.networkError(`HTTP ${s.status}: ${s.statusText}`,{status:s.status});let n=await s.json();return this.log("Identify response:",n),n}catch(s){throw s instanceof c?s:c.networkError(`Failed to send identify: ${s instanceof Error?s.message:String(s)}`,s)}}async fetchFlags(){let t=`${this.apiUrl}/v1/flags`;this.log("Fetching flags from",t);try{let e=await this.fetch(t,{method:"GET",headers:this.getHeaders()});if(!e.ok)throw c.networkError(`HTTP ${e.status}: ${e.statusText}`);let i=await e.json();return this.log("Flags response:",i),i.flags}catch(e){throw e instanceof c?e:c.networkError(`Failed to fetch flags: ${e instanceof Error?e.message:String(e)}`)}}async evaluateFlags(t){let e=`${this.apiUrl}/v1/flags/evaluate`,i=JSON.stringify({userId:t.userId??void 0,anonymousId:t.anonymousId,userTraits:t.userTraits});this.log("Evaluating flags at",e);try{let s=await this.fetch(e,{method:"POST",headers:this.getHeaders(),body:i});if(!s.ok)throw c.networkError(`HTTP ${s.status}: ${s.statusText}`);let n=await s.json();return this.log("Evaluate flags response:",n),n.flags}catch(s){throw s instanceof c?s:c.networkError(`Failed to evaluate flags: ${s instanceof Error?s.message:String(s)}`)}}setTenantId(t){this.tenantId=t;}setDebug(t){this.debug=t;}setSDKMeta(t){this.sdkMeta=t;}getHeaders(){let t={"Content-Type":"application/json",Authorization:`Bearer ${this.apiKey}`,"X-SDK-Name":this.sdkMeta.name,"X-SDK-Version":this.sdkMeta.version};return this.tenantId&&(t["X-Tenant-Id"]=this.tenantId),t}toTrackApiPayload(t){return {eventId:t.event_id,eventName:t.event_name,timestamp:t.timestamp,anonymousId:t.anonymous_id,userId:t.user_id??void 0,sessionId:t.session_id??void 0,properties:t.properties,userTraits:t.user_traits,context:{platform:t.platform,appVersion:t.app_version??void 0,...t.context??{}}}}toIdentifyApiPayload(t){return {userId:t.user_id,anonymousId:t.anonymous_id,traits:t.traits,timestamp:t.timestamp}}async fetch(t,e){if(typeof fetch<"u")return fetch(t,e);throw c.networkError("fetch is not available in this environment")}log(...t){this.debug&&[...t];}},x=class{constructor(t,e=null){a(this,"config"),a(this,"storage",null),a(this,"identity"),a(this,"session"),a(this,"transport"),a(this,"queue"),a(this,"initialized",false),a(this,"tenantId"),a(this,"sessionNumber",0),a(this,"eventIndex",0),a(this,"lastSessionId",null),a(this,"flags",{}),a(this,"flagRefreshTimer",null),this.validateConfig(t);let i=Object.fromEntries(Object.entries(t).filter(([,s])=>s!==void 0));this.config={apiUrl:d.API_URL,debug:false,flushInterval:d.FLUSH_INTERVAL,flushAt:d.FLUSH_AT,maxQueueSize:d.MAX_QUEUE_SIZE,sessionTimeout:d.SESSION_TIMEOUT,...i},this.storage=e,this.tenantId=t.tenantId??"",this.identity=new Q(e,this.config.debug),this.session=new G(e,this.config.sessionTimeout,{onSessionStart:s=>{this.log("Session started:",s),this.trackInternal("session_started",{session_id:s});},onSessionEnd:(s,n)=>{this.log("Session ended:",s,"duration:",n),this.trackInternal("session_ended",{session_id:s,duration_ms:n});}},this.config.debug),this.transport=new J({apiUrl:this.config.apiUrl,apiKey:this.config.apiKey,tenantId:this.tenantId,debug:this.config.debug}),this.queue=new X({flushAt:this.config.flushAt,flushInterval:this.config.flushInterval,maxQueueSize:this.config.maxQueueSize,storage:e,transport:this.transport,onError:(s,n)=>{this.config.onError?.(s),this.log("Queue error:",s.message,"events:",n.length);},debug:this.config.debug});}async initialize(){if(!this.initialized&&(await Promise.all([this.identity.initialize(),this.session.initialize(),this.queue.initialize()]),await this.loadSessionEnrichment(),await this.loadCachedFlags(),this.initialized=true,this.log("Client initialized"),this.config.featureFlags?.autoFetch!==false)){this.refreshFlags().catch(e=>{this.log("Auto-fetch flags error:",e);});let t=this.config.featureFlags?.refreshInterval??3e5;t>0&&(this.flagRefreshTimer=setInterval(()=>{this.refreshFlags().catch(e=>{this.log("Flag refresh error:",e);});},t));}}setTenant(t){this.tenantId=t,this.transport.setTenantId(t),this.log("Tenant set:",t);}async identify(t,e){await this.ensureInitialized(),await this.identity.setUserId(t),e&&await this.identity.setUserTraits(e);try{await this.transport.sendIdentify({project_id:this.config.projectId,environment:this.config.environment,tenant_id:this.tenantId,anonymous_id:this.identity.getAnonymousId(),user_id:t,traits:e,timestamp:H()});}catch(i){let s=c.fromUnknown(i);this.config.onError?.(s),this.log("Identify error:",s.message);}this.config.featureFlags?.autoFetch!==false&&this.refreshFlags().catch(i=>{this.log("Post-identify flag refresh error:",i);});}async setUserTraits(t){await this.ensureInitialized(),await this.identity.setUserTraits(t);}async track(t,e,i){await this.ensureInitialized();let s=this.createEvent(t,e,i);if(this.session.touch(),i?.immediate)try{await this.transport.sendEvents([s]);}catch(n){let o=c.fromUnknown(n);this.config.onError?.(o),this.log("Immediate track error:",o.message);}else await this.queue.enqueue(s);}async flush(t){await this.ensureInitialized(),await this.queue.flush(t);}async reset(){await this.ensureInitialized(),this.session.forceEndSession(),await this.identity.reset(),this.log("Client reset");}setDebug(t){this.config.debug=t,this.identity.setDebug(t),this.session.setDebug(t),this.transport.setDebug(t),this.queue.setDebug(t);}getAnonymousId(){return this.identity.getAnonymousId()}getUserId(){return this.identity.getUserId()}getSessionId(){return this.session.getCurrentSessionId()}getQueueLength(){return this.queue.getQueueLength()}isFeatureEnabled(t,e=false){return this.flags[t]??e}getFeatureFlag(t,e=false){return this.flags[t]??e}getFeatureFlags(){return {...this.flags}}async refreshFlags(){try{let t=await this.transport.evaluateFlags({userId:this.identity.getUserId(),anonymousId:this.identity.getAnonymousId(),userTraits:this.identity.getUserTraits()});this.flags=t,this.persistFlags(),this.config.featureFlags?.onFlagsUpdated?.(t),this.log("Flags refreshed:",Object.keys(t).length,"flags");}catch(t){this.log("Flag refresh error:",t);}}destroy(){this.flagRefreshTimer&&(clearInterval(this.flagRefreshTimer),this.flagRefreshTimer=null),this.queue.destroy(),this.log("Client destroyed");}trackInternal(t,e){let i=this.createEvent(t,e);this.queue.enqueue(i).catch(s=>{this.log("Internal track error:",s);});}getContext(){return {}}createEvent(t,e,i){let s=i?.timestamp?z(i.timestamp):H(),n=this.session.getSessionId();this.updateSessionEnrichment();let o={...this.getContext(),sessionNumber:this.sessionNumber,eventIndex:this.eventIndex};return {event_id:y(),timestamp:s,project_id:this.config.projectId,environment:this.config.environment,tenant_id:this.tenantId,event_name:t,anonymous_id:this.identity.getAnonymousId(),user_id:this.identity.getUserId(),session_id:n,platform:this.config.platform,app_version:this.config.appVersion??null,properties:e??{},user_traits:this.identity.getUserTraits(),context:o}}async ensureInitialized(){this.initialized||await this.initialize();}validateConfig(t){if(!t.apiKey)throw c.invalidConfig("apiKey is required");if(!t.projectId)throw c.invalidConfig("projectId is required");if(!t.environment)throw c.invalidConfig("environment is required");if(!t.platform)throw c.invalidConfig("platform is required")}async loadSessionEnrichment(){if(this.storage)try{let t=await this.storage.get(l.SESSION_NUMBER),e=await this.storage.get(l.EVENT_INDEX);t&&(this.sessionNumber=parseInt(t,10)||0),e&&(this.eventIndex=parseInt(e,10)||0);}catch(t){this.log("Error loading session enrichment:",t);}}updateSessionEnrichment(){let t=this.session.getCurrentSessionId();t&&t!==this.lastSessionId&&(this.sessionNumber++,this.eventIndex=0,this.lastSessionId=t,this.persistSessionEnrichment()),this.eventIndex++,this.persistEventIndex();}persistSessionEnrichment(){this.storage&&this.storage.set(l.SESSION_NUMBER,String(this.sessionNumber)).catch(t=>{this.log("Error persisting session number:",t);});}persistEventIndex(){this.storage&&this.storage.set(l.EVENT_INDEX,String(this.eventIndex)).catch(t=>{this.log("Error persisting event index:",t);});}async loadCachedFlags(){if(this.storage)try{let t=await this.storage.get(l.FLAGS);t&&(this.flags=JSON.parse(t));}catch(t){this.log("Error loading cached flags:",t);}}persistFlags(){this.storage&&this.storage.set(l.FLAGS,JSON.stringify(this.flags)).catch(t=>{this.log("Error persisting flags:",t);});}log(...t){this.config.debug&&[...t];}};var F="pih_utm_params",Z=["utm_source","utm_medium","utm_campaign","utm_term","utm_content"],tt="data-pih-",v=class{constructor(e,i={}){r(this,"client");r(this,"config");r(this,"attributePrefix");r(this,"cachedUtmParams",null);r(this,"originalPushState",null);r(this,"originalReplaceState",null);r(this,"boundHandleClick");r(this,"boundHandlePopState");r(this,"boundHandleSubmit");this.client=e,this.config={pageViews:i.pageViews!==false,clicks:i.clicks??false,clickSelector:i.clickSelector??"[data-track]",forms:i.forms??false},this.attributePrefix=i.attributePrefix??tt,this.boundHandleClick=this.handleClick.bind(this),this.boundHandlePopState=this.handlePopState.bind(this),this.boundHandleSubmit=this.handleSubmit.bind(this);}start(){typeof window>"u"||typeof document>"u"||(this.loadCachedUtmParams(),this.config.pageViews&&(this.setupPageViews(),this.trackPageView()),this.setupClickTracking(),this.config.forms&&this.setupFormTracking());}stop(){this.originalPushState&&(history.pushState=this.originalPushState,this.originalPushState=null),this.originalReplaceState&&(history.replaceState=this.originalReplaceState,this.originalReplaceState=null),typeof window<"u"&&window.removeEventListener("popstate",this.boundHandlePopState),typeof document<"u"&&(document.removeEventListener("click",this.boundHandleClick,true),document.removeEventListener("submit",this.boundHandleSubmit,true));}trackPageView(){let e=new URL(window.location.href),i=this.extractUtmParams(e);Object.keys(i).length>0&&!this.cachedUtmParams&&(this.cachedUtmParams=i,this.persistUtmParams(i)),this.client.track("page_viewed",{path:e.pathname,search:e.search,hash:e.hash,referrer:document.referrer||null,title:document.title,url:window.location.href,...this.getUtmParams(e)});}setupPageViews(){this.originalPushState=history.pushState,this.originalReplaceState=history.replaceState;let e=this.originalPushState,i=this.originalReplaceState,s=()=>this.trackPageView();history.pushState=function(...n){e.apply(history,n),setTimeout(s,0);},history.replaceState=function(...n){i.apply(history,n),setTimeout(s,0);},window.addEventListener("popstate",this.boundHandlePopState);}handlePopState(){this.trackPageView();}setupClickTracking(){document.addEventListener("click",this.boundHandleClick,true);}collectActionAttributes(e){let i={},s=e.attributes;for(let n=0;n<s.length;n++){let o=s[n];if(o.name.startsWith(this.attributePrefix)&&o.name!==`${this.attributePrefix}action`){let h=o.name.slice(this.attributePrefix.length).replace(/-/g,"_");i[h]=o.value;}}return i}findActionElement(e){let i=`${this.attributePrefix}action`,s=e;for(;s;){if(s.hasAttribute(i))return s;s=s.parentElement;}return null}handleClick(e){let i=e.target;if(!i)return;i.closest("[data-track]")&&console.warn("[PIH] data-track is deprecated. Use data-pih-action instead.");let n=this.findActionElement(i);if(n){let V=n.getAttribute(`${this.attributePrefix}action`),B=this.collectActionAttributes(n);this.client.track("$action",{action_name:V,...B,path:window.location.pathname});return}if(!this.config.clicks)return;let o=i.tagName.toLowerCase(),h=(i.textContent||"").trim().slice(0,100),g=i.id||null,m=i.className||null,M=i instanceof HTMLAnchorElement?i.href:null;this.client.track("$click",{element_tag:o,element_text:h,element_id:g,element_classes:m,href:M,path:window.location.pathname});}setupFormTracking(){document.addEventListener("submit",this.boundHandleSubmit,true);}handleSubmit(e){let i=e.target;!i||!(i instanceof HTMLFormElement)||this.client.track("form_submitted",{form_id:i.id||void 0,form_action:i.action||void 0,form_method:(i.method||"get").toUpperCase(),field_count:i.elements.length});}extractUtmParams(e){let i={};for(let s of Z){let n=e.searchParams.get(s);n&&(i[s]=n);}return i}getUtmParams(e){let i=this.extractUtmParams(e);return Object.keys(i).length>0?i:this.cachedUtmParams??{}}loadCachedUtmParams(){try{let e=localStorage.getItem(F);e&&(this.cachedUtmParams=JSON.parse(e));}catch{}}persistUtmParams(e){try{localStorage.setItem(F,JSON.stringify(e));}catch{}}};var S=class{constructor(e){r(this,"client");r(this,"boundHandleVisibilityChange");r(this,"boundHandleBeforeUnload");this.client=e,this.boundHandleVisibilityChange=this.handleVisibilityChange.bind(this),this.boundHandleBeforeUnload=this.handleBeforeUnload.bind(this);}start(){typeof document<"u"&&document.addEventListener("visibilitychange",this.boundHandleVisibilityChange),typeof window<"u"&&(window.addEventListener("beforeunload",this.boundHandleBeforeUnload),window.addEventListener("pagehide",this.boundHandleBeforeUnload));}stop(){typeof document<"u"&&document.removeEventListener("visibilitychange",this.boundHandleVisibilityChange),typeof window<"u"&&(window.removeEventListener("beforeunload",this.boundHandleBeforeUnload),window.removeEventListener("pagehide",this.boundHandleBeforeUnload));}handleVisibilityChange(){document.visibilityState==="hidden"&&this.flushWithBeacon();}handleBeforeUnload(){this.flushWithBeacon();}flushWithBeacon(){this.client.flush({useBeacon:true}).catch(()=>{});}};var C="pih_outbound",I=class{constructor(e,i={}){r(this,"client");r(this,"thresholdMs");r(this,"boundHandleClick");this.client=e,this.thresholdMs=(i.thresholdSeconds??30)*1e3,this.boundHandleClick=this.handleClick.bind(this);}start(){typeof window>"u"||typeof document>"u"||(this.checkBounceBack(),document.addEventListener("click",this.boundHandleClick,true));}stop(){typeof document<"u"&&document.removeEventListener("click",this.boundHandleClick,true);}handleClick(e){let i=e.target;if(!i)return;let s=i.closest("a");if(!(!s||!s.href))try{if(new URL(s.href).origin===window.location.origin)return;let o={};for(let g of Array.from(s.attributes))if(g.name.startsWith("data-pih-")){let m=g.name.slice(9).replace(/-/g,"_");o[m]=g.value;}let h={url:s.href,timestamp:Date.now(),pihAttributes:o};try{sessionStorage.setItem(C,JSON.stringify(h));}catch{}}catch{}}checkBounceBack(){try{let e=sessionStorage.getItem(C);if(!e)return;sessionStorage.removeItem(C);let i=JSON.parse(e),s=Date.now()-i.timestamp;s<=this.thresholdMs&&this.client.track("$bounce_back",{original_click_url:i.url,time_away_seconds:Math.round(s/1e3),page_returned_to:window.location.href,...i.pihAttributes});}catch{}}};function et(){if(typeof navigator>"u"||typeof window>"u")return "unknown";let t=navigator.userAgent.toLowerCase();if(/ipad|tablet|playbook|silk/.test(t))return "tablet";if(/mobile|iphone|ipod|android.*mobile|windows phone|blackberry|opera mini|opera mobi/.test(t))return "mobile";let e=window.screen?.width??0;return e>0&&e<768?"mobile":e>=768&&e<1024?"tablet":"desktop"}function it(){return typeof navigator>"u"?void 0:navigator.connection?.effectiveType??void 0}function L(){if(typeof window>"u"||typeof document>"u")return {};let t={screenWidth:window.screen?.width??void 0,screenHeight:window.screen?.height??void 0,viewportWidth:window.innerWidth??void 0,viewportHeight:window.innerHeight??void 0,locale:navigator?.language??void 0,timezone:st(),deviceType:et(),pageUrl:window.location.href,pagePath:window.location.pathname,pageTitle:document.title||void 0,referrer:document.referrer||void 0},e=it();return e&&(t.connectionType=e),Object.fromEntries(Object.entries(t).filter(([,i])=>i!==void 0))}function st(){try{return Intl.DateTimeFormat().resolvedOptions().timeZone}catch{return}}var nt=[25,50,75,100],w=class{constructor(){r(this,"client",null);r(this,"started",false);r(this,"visibleStartTime",0);r(this,"totalVisibleTime",0);r(this,"reachedThresholds",new Set);r(this,"boundHandleVisibilityChange");r(this,"boundHandleScroll");this.boundHandleVisibilityChange=this.handleVisibilityChange.bind(this),this.boundHandleScroll=this.handleScroll.bind(this);}start(e){this.started||typeof window>"u"||typeof document>"u"||(this.client=e,this.started=true,this.visibleStartTime=Date.now(),this.totalVisibleTime=0,this.reachedThresholds.clear(),document.visibilityState==="visible"&&(this.visibleStartTime=Date.now()),document.addEventListener("visibilitychange",this.boundHandleVisibilityChange),window.addEventListener("scroll",this.boundHandleScroll,{passive:true}));}stop(){this.started&&(this.accumulateVisibleTime(),this.totalVisibleTime>0&&this.client&&this.client.track("page_engaged",{visible_duration_ms:this.totalVisibleTime,page_path:typeof window<"u"?window.location.pathname:void 0}),typeof document<"u"&&document.removeEventListener("visibilitychange",this.boundHandleVisibilityChange),typeof window<"u"&&window.removeEventListener("scroll",this.boundHandleScroll),this.started=false,this.client=null);}handleVisibilityChange(){!this.started||!this.client||(document.visibilityState==="hidden"?(this.accumulateVisibleTime(),this.totalVisibleTime>0&&(this.client.track("page_engaged",{visible_duration_ms:this.totalVisibleTime,page_path:window.location.pathname}),this.totalVisibleTime=0)):this.visibleStartTime=Date.now());}accumulateVisibleTime(){this.visibleStartTime>0&&(this.totalVisibleTime+=Date.now()-this.visibleStartTime,this.visibleStartTime=Date.now());}handleScroll(){if(!this.started||!this.client)return;let e=this.getScrollPercent();for(let i of nt)e>=i&&!this.reachedThresholds.has(i)&&(this.reachedThresholds.add(i),this.client.track("scroll_depth_reached",{depth_percent:i,page_path:window.location.pathname}));}getScrollPercent(){let e=Math.max(document.body.scrollHeight,document.documentElement.scrollHeight,document.body.offsetHeight,document.documentElement.offsetHeight,document.body.clientHeight,document.documentElement.clientHeight),i=window.innerHeight,s=window.scrollY||document.documentElement.scrollTop,n=e-i;return n<=0?100:Math.min(100,Math.round(s/n*100))}};var E=class{constructor(){r(this,"client",null);r(this,"started",false);r(this,"errorCount",0);r(this,"boundHandleError");r(this,"boundHandleRejection");this.boundHandleError=this.handleError.bind(this),this.boundHandleRejection=this.handleRejection.bind(this);}start(e){this.started||typeof window>"u"||(this.client=e,this.started=true,this.errorCount=0,window.addEventListener("error",this.boundHandleError),window.addEventListener("unhandledrejection",this.boundHandleRejection));}stop(){this.started&&(typeof window<"u"&&(window.removeEventListener("error",this.boundHandleError),window.removeEventListener("unhandledrejection",this.boundHandleRejection)),this.started=false,this.client=null);}handleError(e){!this.started||!this.client||this.canReport()&&(this.errorCount++,this.client.track("js_error",{message:e.message||"Unknown error",filename:e.filename||void 0,lineno:e.lineno||void 0,colno:e.colno||void 0,stack:e.error?.stack||void 0}));}handleRejection(e){if(!this.started||!this.client||!this.canReport())return;this.errorCount++;let i=e.reason,s=i instanceof Error?i.message:typeof i=="string"?i:"Unhandled promise rejection",n=i instanceof Error?i.stack:void 0;this.client.track("js_error",{message:s,filename:void 0,lineno:void 0,colno:void 0,stack:n});}canReport(){return this.errorCount<10}};var T=class{constructor(){r(this,"client",null);r(this,"started",false);}start(e){this.started||(this.client=e,this.started=true,this.initWebVitals());}stop(){this.started=false,this.client=null;}async initWebVitals(){try{let i=await import('web-vitals'),s=n=>{!this.started||!this.client||this.client.track("web_vital",{name:n.name,value:n.value,rating:n.rating});};i.onLCP&&i.onLCP(s),i.onFID&&i.onFID(s),i.onCLS&&i.onCLS(s),i.onINP&&i.onINP(s),i.onTTFB&&i.onTTFB(s);}catch{}}};var k=class{constructor(e,i,s,n=false){r(this,"client");r(this,"apiUrl");r(this,"apiKey");r(this,"config",null);r(this,"buffer",[]);r(this,"sequence",0);r(this,"flushTimer",null);r(this,"durationTimer",null);r(this,"rrwebStop",null);r(this,"sessionId",null);r(this,"recording",false);r(this,"rageClickState",{clicks:[]});r(this,"debug");r(this,"matchedRule",null);r(this,"boundHandleClick",null);this.client=e,this.apiUrl=i.replace(/\/$/,""),this.apiKey=s,this.debug=n;}async start(){try{if(await this.fetchConfig(),!this.config||!this.config.enabled){this.log("Recording not enabled");return}let e=this.evaluateRules(this.config.rules);if(!e){this.log("No recording rule matched");return}if(this.matchedRule=e.type,Math.random()>e.sampleRate){this.log("Sampled out by rule",e.type);return}await this.startRecording();}catch(e){this.log("Failed to start recording:",e);}}stop(){this.rrwebStop&&(this.rrwebStop(),this.rrwebStop=null),this.flushTimer&&(clearInterval(this.flushTimer),this.flushTimer=null),this.durationTimer&&(clearTimeout(this.durationTimer),this.durationTimer=null),this.buffer.length>0&&this.flush().catch(e=>this.log("Final flush error:",e)),this.recording=false,this.removeRageClickListener(),this.log("Recording stopped");}isRecording(){return this.recording}async fetchConfig(){let e=`${this.apiUrl}/v1/recording-config`,i=await fetch(e,{method:"GET",headers:{Authorization:`Bearer ${this.apiKey}`,"Content-Type":"application/json"}});if(!i.ok)throw new Error(`Failed to fetch recording config: ${i.status}`);let s=await i.json();this.config=s.config,this.log("Fetched recording config:",this.config);}evaluateRules(e){let i=window.location.href;for(let s of e)switch(s.type){case "all_sessions":return s;case "url_match":if(s.pattern)try{if(new RegExp(s.pattern).test(i))return s}catch{this.log("Invalid URL regex pattern:",s.pattern);}break;case "event_match":break;case "rage_click":return s}return null}async startRecording(){if(!this.recording){if(this.sessionId=this.client.getSessionId(),!this.sessionId){this.log("No session ID available, skipping recording");return}try{let e=await import('rrweb'),i=this.config?.maskingConfig??{maskAllInputs:!0,maskAllText:!1,maskSelectors:[],unmaskSelectors:[],blockSelectors:[]};this.rrwebStop=e.record({emit:n=>{this.onEvent(n);},maskAllInputs:i.maskAllInputs,maskTextSelector:i.maskAllText?"*":void 0,maskInputOptions:{password:!0,email:i.maskAllInputs},blockSelector:i.blockSelectors.length>0?i.blockSelectors.join(", "):void 0}),this.recording=!0,this.flushTimer=setInterval(()=>{this.buffer.length>0&&this.flush().catch(n=>this.log("Flush error:",n));},5e3);let s=(this.config?.maxDurationSeconds??600)*1e3;this.durationTimer=setTimeout(()=>{this.log("Duration cap reached, stopping recording"),this.stop();},s),this.setupRageClickDetection(),this.log("Recording started for session:",this.sessionId);}catch(e){this.log("Failed to import rrweb or start recording:",e);}}}onEvent(e){this.buffer.push(e),this.buffer.length>=50&&this.flush().catch(i=>this.log("Buffer flush error:",i));}async flush(){if(this.buffer.length===0||!this.sessionId)return;let e=[...this.buffer];this.buffer=[];let i=this.sequence++;try{let s=`${this.apiUrl}/v1/recordings`,n=await fetch(s,{method:"POST",headers:{Authorization:`Bearer ${this.apiKey}`,"Content-Type":"application/json"},body:JSON.stringify({sessionId:this.sessionId,sequence:i,events:e,pageUrl:window.location.href,triggerRule:this.matchedRule??void 0})});n.ok?this.log("Uploaded chunk",i,"with",e.length,"events"):(this.log("Failed to upload recording chunk:",n.status),this.buffer.unshift(...e));}catch(s){this.log("Failed to upload recording chunk:",s),this.buffer.unshift(...e);}}setupRageClickDetection(){this.boundHandleClick=this.handleClickForRageDetection.bind(this),document.addEventListener("click",this.boundHandleClick,true);}removeRageClickListener(){this.boundHandleClick&&(document.removeEventListener("click",this.boundHandleClick,true),this.boundHandleClick=null);}handleClickForRageDetection(e){let i=Date.now(),{clientX:s,clientY:n}=e;if(this.rageClickState.clicks=this.rageClickState.clicks.filter(o=>i-o.timestamp<500),this.rageClickState.clicks.push({x:s,y:n,timestamp:i}),this.rageClickState.clicks.length>=3){let o=this.rageClickState.clicks,h=o[0];o.every(m=>Math.abs(m.x-h.x)<=30&&Math.abs(m.y-h.y)<=30)&&(this.client.track("rage_click",{x:h.x,y:h.y,click_count:o.length,page_url:window.location.href}),this.rageClickState.clicks=[]);}}log(...e){this.debug&&[...e];}};var b=class{constructor(){r(this,"data",new Map);}async get(e){return this.data.get(e)??null}async set(e,i){this.data.set(e,i);}async remove(e){this.data.delete(e);}};function rt(){try{let t="__pih_test__";return localStorage.setItem(t,t),localStorage.removeItem(t),!0}catch{return false}}var A=class{constructor(){r(this,"fallback",null);r(this,"useLocalStorage");this.useLocalStorage=rt(),this.useLocalStorage||(this.fallback=new b,console.warn("[PIH] localStorage unavailable, using in-memory storage. Data will not persist across page loads."));}async get(e){if(this.fallback)return this.fallback.get(e);try{return localStorage.getItem(e)}catch{return this.fallback||(this.fallback=new b),this.fallback.get(e)}}async set(e,i){if(this.fallback)return this.fallback.set(e,i);try{localStorage.setItem(e,i);}catch(s){return this.fallback||(this.fallback=new b,console.warn("[PIH] localStorage write failed, falling back to in-memory storage:",s)),this.fallback.set(e,i)}}async remove(e){if(this.fallback)return this.fallback.remove(e);try{localStorage.removeItem(e);}catch{}}};function P(){return new A}P();var U=new Set;function N(t){return U.add(t),()=>U.delete(t)}function ot(){for(let t of U)t();}function at(t,e=false){let i=react.useCallback(()=>_()?.isFeatureEnabled(t,e)??e,[t,e]);return react.useSyncExternalStore(N,i,()=>e)}function lt(){let t=react.useCallback(()=>_()?.getFeatureFlags()??{},[]);return react.useSyncExternalStore(N,t,()=>({}))}var R=class extends x{constructor(i,s){let n={...i,platform:"web"};super(n,s??P());r(this,"autocapture",null);r(this,"beacon");r(this,"performanceTracker",null);r(this,"engagementTracker",null);r(this,"errorTracker",null);r(this,"bounceBackDetector",null);r(this,"recordingManager",null);this.transport.setSDKMeta({name:"pih-sdk-web",version:"0.4.0"}),this.beacon=new S(this);}getContext(){return L()}async initialize(){await super.initialize(),this.beacon.start(),this.config.autocapture&&(this.autocapture=new v(this,this.config.autocapture),this.autocapture.start(),this.config.autocapture.performance&&(this.performanceTracker=new T,this.performanceTracker.start(this)),this.config.autocapture.engagement&&(this.engagementTracker=new w,this.engagementTracker.start(this)),this.config.autocapture.errorTracking&&(this.errorTracker=new E,this.errorTracker.start(this))),this.config.bounceBack?.enabled&&(this.bounceBackDetector=new I(this,this.config.bounceBack),this.bounceBackDetector.start()),this.config.recording?.enabled&&(this.recordingManager=new k(this,this.config.apiUrl??"https://repoingest-production.up.railway.app",this.config.apiKey,this.config.debug),this.recordingManager.start().catch(i=>{this.log("Failed to start recording:",i);})),this.log("Web client initialized");}enableAutocapture(i){this.autocapture&&this.autocapture.stop(),this.autocapture=new v(this,i),this.autocapture.start();}disableAutocapture(){this.autocapture&&(this.autocapture.stop(),this.autocapture=null);}trackPageView(){if(this.autocapture)this.autocapture.trackPageView();else {let i=new URL(window.location.href);this.track("page_viewed",{path:i.pathname,search:i.search,hash:i.hash,referrer:document.referrer||null,title:document.title,url:window.location.href});}}destroy(){this.beacon.stop(),this.autocapture&&this.autocapture.stop(),this.performanceTracker&&(this.performanceTracker.stop(),this.performanceTracker=null),this.engagementTracker&&(this.engagementTracker.stop(),this.engagementTracker=null),this.errorTracker&&(this.errorTracker.stop(),this.errorTracker=null),this.bounceBackDetector&&(this.bounceBackDetector.stop(),this.bounceBackDetector=null),this.recordingManager&&(this.recordingManager.stop(),this.recordingManager=null),super.destroy();}log(...i){this.config.debug&&[...i];}},p=null;function ct(t){return p?(console.warn("[PIH] SDK already initialized. Returning existing instance."),p):(p=new R(t),p.initialize().catch(e=>{console.error("[PIH] Failed to initialize:",e);}),p)}function _(){return p}function ht(){p&&(p.destroy(),p=null);}var ut={init:ct,getInstance:_,resetInstance:ht,WebPIHClient:R},Gt=ut;
2
+ exports.WebPIHClient=R;exports.default=Gt;exports.getInstance=_;exports.init=ct;exports.notifyFlagListeners=ot;exports.resetInstance=ht;exports.useFeatureFlag=at;exports.useFeatureFlags=lt;//# sourceMappingURL=index.cjs.map
3
3
  //# sourceMappingURL=index.cjs.map