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

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/README.md CHANGED
@@ -12,6 +12,13 @@ yarn add @product-intelligence-hub/sdk-web
12
12
  pnpm add @product-intelligence-hub/sdk-web
13
13
  ```
14
14
 
15
+ ### Optional Peer Dependencies
16
+
17
+ For Web Vitals tracking (`performance: true`):
18
+ ```bash
19
+ npm install web-vitals
20
+ ```
21
+
15
22
  ## Quick Start
16
23
 
17
24
  ```typescript
@@ -61,7 +68,10 @@ PIH.init({
61
68
  autocapture?: {
62
69
  pageViews?: boolean; // Track page views (default: true)
63
70
  clicks?: boolean; // Track clicks (default: false)
64
- formSubmissions?: boolean; // Track form submissions (default: false)
71
+ forms?: boolean; // Track form submissions (default: false)
72
+ performance?: boolean; // Web Vitals tracking (default: false)
73
+ engagement?: boolean; // Time-on-page + scroll depth (default: false)
74
+ errorTracking?: boolean; // JS error capture (default: false)
65
75
  };
66
76
 
67
77
  // Callbacks
@@ -129,6 +139,36 @@ Force flush the event queue.
129
139
  await pih.flush();
130
140
  ```
131
141
 
142
+ ### `pih.isFeatureEnabled(key, defaultValue?)`
143
+
144
+ Check if a feature flag is enabled.
145
+
146
+ ```typescript
147
+ if (pih.isFeatureEnabled("new_checkout_flow")) {
148
+ showNewCheckout();
149
+ }
150
+
151
+ // With default value
152
+ const enabled = pih.isFeatureEnabled("beta_feature", false);
153
+ ```
154
+
155
+ ### `pih.getFeatureFlags()`
156
+
157
+ Get all feature flag values.
158
+
159
+ ```typescript
160
+ const flags = pih.getFeatureFlags();
161
+ // { new_checkout_flow: true, beta_feature: false }
162
+ ```
163
+
164
+ ### `pih.refreshFlags()`
165
+
166
+ Force refresh feature flags from the server.
167
+
168
+ ```typescript
169
+ await pih.refreshFlags();
170
+ ```
171
+
132
172
  ### `pih.reset()`
133
173
 
134
174
  Reset identity (for logout).
@@ -145,13 +185,28 @@ Enable automatic event capture:
145
185
  PIH.init({
146
186
  // ...
147
187
  autocapture: {
148
- pageViews: true, // Track page_viewed on navigation
149
- clicks: true, // Track element_clicked
150
- formSubmissions: true // Track form_submitted
188
+ pageViews: true, // Track page_viewed on navigation
189
+ clicks: true, // Track element_clicked
190
+ forms: true, // Track form_submitted
191
+ performance: true, // Track web_vital (requires web-vitals)
192
+ engagement: true, // Track page_engaged + scroll_depth_reached
193
+ errorTracking: true, // Track js_error
151
194
  }
152
195
  });
153
196
  ```
154
197
 
198
+ ### Auto-Tracked Events
199
+
200
+ | Event | Trigger | Key Properties |
201
+ |-------|---------|----------------|
202
+ | `page_viewed` | `pageViews: true` (default) | path, title, referrer, UTM params |
203
+ | `element_clicked` | `clicks: true` | element_tag, element_text, data_track |
204
+ | `form_submitted` | `forms: true` | form_id, form_action, form_method, field_count |
205
+ | `web_vital` | `performance: true` | metric_name (LCP/FID/CLS/INP/TTFB), value, rating |
206
+ | `page_engaged` | `engagement: true` | duration_ms (fired on visibility change) |
207
+ | `scroll_depth_reached` | `engagement: true` | depth_percent (25, 50, 75, 100) |
208
+ | `js_error` | `errorTracking: true` | message, filename, lineno, type (max 10/page) |
209
+
155
210
  ### Enabling/Disabling at Runtime
156
211
 
157
212
  ```typescript
@@ -162,12 +217,54 @@ pih.enableAutocapture({ clicks: true });
162
217
  pih.disableAutocapture();
163
218
  ```
164
219
 
220
+ ## Browser Context
221
+
222
+ The SDK automatically collects browser context on every event:
223
+
224
+ - `screenWidth` / `screenHeight` -- physical screen resolution
225
+ - `viewportWidth` / `viewportHeight` -- browser viewport size
226
+ - `locale` -- browser locale (e.g. `en-US`)
227
+ - `timezone` -- IANA timezone (e.g. `America/New_York`)
228
+ - `connectionType` -- network connection type
229
+ - `deviceType` -- `desktop`, `tablet`, or `mobile`
230
+ - `pageUrl`, `pagePath`, `pageTitle`, `referrer`
231
+
232
+ These fields are included in the `context` property of each event.
233
+
234
+ ## Session Enrichment
235
+
236
+ Each event includes session-level metadata:
237
+
238
+ - `sessionNumber` -- incrementing counter of sessions (persists across sessions)
239
+ - `eventIndex` -- zero-based position of the event within the current session
240
+
241
+ ## Feature Flag Hooks (React)
242
+
243
+ ```typescript
244
+ import { useFeatureFlag, useFeatureFlags } from "@product-intelligence-hub/sdk-web/hooks";
245
+
246
+ function MyComponent() {
247
+ // Single flag with default value
248
+ const isNewUI = useFeatureFlag("new_ui", false);
249
+
250
+ // All flags
251
+ const allFlags = useFeatureFlags();
252
+
253
+ if (isNewUI) {
254
+ return <NewUIComponent />;
255
+ }
256
+ return <LegacyComponent />;
257
+ }
258
+ ```
259
+
260
+ Hooks automatically re-render when flag values change (e.g. after `refreshFlags()` or `identify()`).
261
+
165
262
  ## TypeScript Support
166
263
 
167
264
  Full TypeScript support included. Types are exported from the package:
168
265
 
169
266
  ```typescript
170
- import type { PIHConfig, TrackEvent, TrackOptions } from "@product-intelligence-hub/sdk-web";
267
+ import type { PIHConfig, TrackEvent, TrackOptions, FeatureFlags } from "@product-intelligence-hub/sdk-web";
171
268
  ```
172
269
 
173
270
  ## Build Outputs
package/dist/hooks.cjs ADDED
@@ -0,0 +1,2 @@
1
+ 'use strict';var react=require('react');var r=new Set;function o(t){return r.add(t),()=>r.delete(t)}function T(){for(let t of r)t();}function v(t,s=false){let h=react.useCallback(()=>s,[t,s]);return react.useSyncExternalStore(o,h,()=>s)}function w(){let t=react.useCallback(()=>({}),[]);return react.useSyncExternalStore(o,t,()=>({}))}exports.notifyFlagListeners=T;exports.useFeatureFlag=v;exports.useFeatureFlags=w;//# sourceMappingURL=hooks.cjs.map
2
+ //# sourceMappingURL=hooks.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/hooks.ts"],"names":["listeners","subscribe","listener","notifyFlagListeners","useFeatureFlag","key","defaultValue","getSnapshot","useCallback","useSyncExternalStore","useFeatureFlags"],"mappings":"wCAOA,IAAMA,CAAAA,CAAY,IAAI,GAAA,CAEtB,SAASC,CAAAA,CAAUC,CAAAA,CAAgC,CACjD,OAAAF,EAAU,GAAA,CAAIE,CAAQ,CAAA,CACf,IAAMF,CAAAA,CAAU,MAAA,CAAOE,CAAQ,CACxC,CAMO,SAASC,CAAAA,EAA4B,CAC1C,IAAA,IAAWD,CAAAA,IAAYF,CAAAA,CACrBE,CAAAA,GAEJ,CAcO,SAASE,CAAAA,CAAeC,CAAAA,CAAaC,CAAAA,CAAe,KAAA,CAAgB,CACzE,IAAMC,EAAcC,iBAAAA,CAAY,IAEwBF,EACrD,CAACD,CAAAA,CAAKC,CAAY,CAAC,CAAA,CAEtB,OAAOG,0BAAAA,CAAqBR,CAAAA,CAAWM,EAAa,IAAMD,CAAY,CACxE,CAaO,SAASI,CAAAA,EAA2C,CACzD,IAAMH,EAAcC,iBAAAA,CAAY,KAEM,GAAC,CACpC,EAAE,CAAA,CAGL,OAAOC,0BAAAA,CACLR,CAAAA,CACAM,CAAAA,CACA,KAAO,GACT,CACF","file":"hooks.cjs","sourcesContent":["import { useSyncExternalStore, useCallback } from \"react\";\n\nimport { getInstance } from \"./index.js\";\n\ntype Listener = () => void;\n\n// Simple pub/sub for flag changes\nconst listeners = new Set<Listener>();\n\nfunction subscribe(listener: Listener): () => void {\n listeners.add(listener);\n return () => listeners.delete(listener);\n}\n\n/**\n * Notify all subscribers of flag changes.\n * Call this after refreshFlags() completes.\n */\nexport function notifyFlagListeners(): void {\n for (const listener of listeners) {\n listener();\n }\n}\n\n/**\n * React hook to check if a feature flag is enabled.\n *\n * @example\n * ```tsx\n * function MyComponent() {\n * const isEnabled = useFeatureFlag(\"enable_ai_chat\");\n * if (!isEnabled) return null;\n * return <AIChatWidget />;\n * }\n * ```\n */\nexport function useFeatureFlag(key: string, defaultValue = false): boolean {\n const getSnapshot = useCallback(() => {\n const client = getInstance();\n return client?.isFeatureEnabled(key, defaultValue) ?? defaultValue;\n }, [key, defaultValue]);\n\n return useSyncExternalStore(subscribe, getSnapshot, () => defaultValue);\n}\n\n/**\n * React hook to get all feature flags.\n *\n * @example\n * ```tsx\n * function FlagDebugPanel() {\n * const flags = useFeatureFlags();\n * return <pre>{JSON.stringify(flags, null, 2)}</pre>;\n * }\n * ```\n */\nexport function useFeatureFlags(): Record<string, boolean> {\n const getSnapshot = useCallback(() => {\n const client = getInstance();\n return client?.getFeatureFlags() ?? {};\n }, []);\n\n // Return stable reference via JSON comparison\n return useSyncExternalStore(\n subscribe,\n getSnapshot,\n () => ({})\n );\n}\n"]}
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Notify all subscribers of flag changes.
3
+ * Call this after refreshFlags() completes.
4
+ */
5
+ declare function notifyFlagListeners(): void;
6
+ /**
7
+ * React hook to check if a feature flag is enabled.
8
+ *
9
+ * @example
10
+ * ```tsx
11
+ * function MyComponent() {
12
+ * const isEnabled = useFeatureFlag("enable_ai_chat");
13
+ * if (!isEnabled) return null;
14
+ * return <AIChatWidget />;
15
+ * }
16
+ * ```
17
+ */
18
+ declare function useFeatureFlag(key: string, defaultValue?: boolean): boolean;
19
+ /**
20
+ * React hook to get all feature flags.
21
+ *
22
+ * @example
23
+ * ```tsx
24
+ * function FlagDebugPanel() {
25
+ * const flags = useFeatureFlags();
26
+ * return <pre>{JSON.stringify(flags, null, 2)}</pre>;
27
+ * }
28
+ * ```
29
+ */
30
+ declare function useFeatureFlags(): Record<string, boolean>;
31
+
32
+ export { notifyFlagListeners, useFeatureFlag, useFeatureFlags };
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Notify all subscribers of flag changes.
3
+ * Call this after refreshFlags() completes.
4
+ */
5
+ declare function notifyFlagListeners(): void;
6
+ /**
7
+ * React hook to check if a feature flag is enabled.
8
+ *
9
+ * @example
10
+ * ```tsx
11
+ * function MyComponent() {
12
+ * const isEnabled = useFeatureFlag("enable_ai_chat");
13
+ * if (!isEnabled) return null;
14
+ * return <AIChatWidget />;
15
+ * }
16
+ * ```
17
+ */
18
+ declare function useFeatureFlag(key: string, defaultValue?: boolean): boolean;
19
+ /**
20
+ * React hook to get all feature flags.
21
+ *
22
+ * @example
23
+ * ```tsx
24
+ * function FlagDebugPanel() {
25
+ * const flags = useFeatureFlags();
26
+ * return <pre>{JSON.stringify(flags, null, 2)}</pre>;
27
+ * }
28
+ * ```
29
+ */
30
+ declare function useFeatureFlags(): Record<string, boolean>;
31
+
32
+ export { notifyFlagListeners, useFeatureFlag, useFeatureFlags };
package/dist/hooks.js ADDED
@@ -0,0 +1,2 @@
1
+ import {useCallback,useSyncExternalStore}from'react';var r=new Set;function o(t){return r.add(t),()=>r.delete(t)}function T(){for(let t of r)t();}function v(t,s=false){let h=useCallback(()=>s,[t,s]);return useSyncExternalStore(o,h,()=>s)}function w(){let t=useCallback(()=>({}),[]);return useSyncExternalStore(o,t,()=>({}))}export{T as notifyFlagListeners,v as useFeatureFlag,w as useFeatureFlags};//# sourceMappingURL=hooks.js.map
2
+ //# sourceMappingURL=hooks.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/hooks.ts"],"names":["listeners","subscribe","listener","notifyFlagListeners","useFeatureFlag","key","defaultValue","getSnapshot","useCallback","useSyncExternalStore","useFeatureFlags"],"mappings":"qDAOA,IAAMA,CAAAA,CAAY,IAAI,GAAA,CAEtB,SAASC,CAAAA,CAAUC,CAAAA,CAAgC,CACjD,OAAAF,EAAU,GAAA,CAAIE,CAAQ,CAAA,CACf,IAAMF,CAAAA,CAAU,MAAA,CAAOE,CAAQ,CACxC,CAMO,SAASC,CAAAA,EAA4B,CAC1C,IAAA,IAAWD,CAAAA,IAAYF,CAAAA,CACrBE,CAAAA,GAEJ,CAcO,SAASE,CAAAA,CAAeC,CAAAA,CAAaC,CAAAA,CAAe,KAAA,CAAgB,CACzE,IAAMC,EAAcC,WAAAA,CAAY,IAEwBF,EACrD,CAACD,CAAAA,CAAKC,CAAY,CAAC,CAAA,CAEtB,OAAOG,oBAAAA,CAAqBR,CAAAA,CAAWM,EAAa,IAAMD,CAAY,CACxE,CAaO,SAASI,CAAAA,EAA2C,CACzD,IAAMH,EAAcC,WAAAA,CAAY,KAEM,GAAC,CACpC,EAAE,CAAA,CAGL,OAAOC,oBAAAA,CACLR,CAAAA,CACAM,CAAAA,CACA,KAAO,GACT,CACF","file":"hooks.js","sourcesContent":["import { useSyncExternalStore, useCallback } from \"react\";\n\nimport { getInstance } from \"./index.js\";\n\ntype Listener = () => void;\n\n// Simple pub/sub for flag changes\nconst listeners = new Set<Listener>();\n\nfunction subscribe(listener: Listener): () => void {\n listeners.add(listener);\n return () => listeners.delete(listener);\n}\n\n/**\n * Notify all subscribers of flag changes.\n * Call this after refreshFlags() completes.\n */\nexport function notifyFlagListeners(): void {\n for (const listener of listeners) {\n listener();\n }\n}\n\n/**\n * React hook to check if a feature flag is enabled.\n *\n * @example\n * ```tsx\n * function MyComponent() {\n * const isEnabled = useFeatureFlag(\"enable_ai_chat\");\n * if (!isEnabled) return null;\n * return <AIChatWidget />;\n * }\n * ```\n */\nexport function useFeatureFlag(key: string, defaultValue = false): boolean {\n const getSnapshot = useCallback(() => {\n const client = getInstance();\n return client?.isFeatureEnabled(key, defaultValue) ?? defaultValue;\n }, [key, defaultValue]);\n\n return useSyncExternalStore(subscribe, getSnapshot, () => defaultValue);\n}\n\n/**\n * React hook to get all feature flags.\n *\n * @example\n * ```tsx\n * function FlagDebugPanel() {\n * const flags = useFeatureFlags();\n * return <pre>{JSON.stringify(flags, null, 2)}</pre>;\n * }\n * ```\n */\nexport function useFeatureFlags(): Record<string, boolean> {\n const getSnapshot = useCallback(() => {\n const client = getInstance();\n return client?.getFeatureFlags() ?? {};\n }, []);\n\n // Return stable reference via JSON comparison\n return useSyncExternalStore(\n subscribe,\n getSnapshot,\n () => ({})\n );\n}\n"]}
package/dist/index.cjs CHANGED
@@ -1,3 +1,3 @@
1
- 'use strict';Object.defineProperty(exports,'__esModule',{value:true});var O=Object.defineProperty;var C=(n,t,e)=>t in n?O(n,t,{enumerable:true,configurable:true,writable:true,value:e}):n[t]=e;var s=(n,t,e)=>C(n,typeof t!="symbol"?t+"":t,e);var a=class n extends Error{constructor(e,i,r){super(e);s(this,"code");s(this,"details");this.name="PIHError",this.code=i,this.details=r,Error.captureStackTrace&&Error.captureStackTrace(this,n);}static networkError(e,i){return new n(e,"NETWORK_ERROR",i)}static invalidConfig(e,i){return new n(e,"INVALID_CONFIG",i)}static queueFull(e,i){return new n(e,"QUEUE_FULL",i)}static rateLimited(e,i){return new n(e,"RATE_LIMITED",i)}static invalidPayload(e,i){return new n(e,"INVALID_PAYLOAD",i)}static storageError(e,i){return new n(e,"STORAGE_ERROR",i)}static sessionError(e,i){return new n(e,"SESSION_ERROR",i)}static fromUnknown(e){return e instanceof n?e:e instanceof Error?new n(e.message,"UNKNOWN_ERROR",e):new n(String(e),"UNKNOWN_ERROR",e)}};function g(){if(typeof crypto<"u"&&typeof crypto.randomUUID=="function")return crypto.randomUUID();if(typeof crypto<"u"&&typeof crypto.getRandomValues=="function"){let n=new Uint8Array(16);crypto.getRandomValues(n),n[6]=n[6]&15|64,n[8]=n[8]&63|128;let t=Array.from(n,e=>e.toString(16).padStart(2,"0")).join("");return `${t.slice(0,8)}-${t.slice(8,12)}-${t.slice(12,16)}-${t.slice(16,20)}-${t.slice(20)}`}return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,n=>{let t=Math.random()*16|0;return (n==="x"?t:t&3|8).toString(16)})}function T(){return new Date().toISOString()}function A(n){return n.toISOString()}function U(n,t=1e3,e=16e3){let i=t*Math.pow(2,n);return Math.min(i,e)}var f="pih_",u={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`},h={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};var m=class{constructor(t,e=false){s(this,"anonymousId");s(this,"userId",null);s(this,"userTraits",{});s(this,"storage");s(this,"debug");this.storage=t,this.debug=e,this.anonymousId=g();}async initialize(){if(!this.storage){this.log("No storage adapter, using in-memory identity only");return}try{let t=await this.storage.get(u.ANONYMOUS_ID);t?(this.anonymousId=t,this.log("Loaded anonymous ID from storage:",this.anonymousId)):(await this.storage.set(u.ANONYMOUS_ID,this.anonymousId),this.log("Generated new anonymous ID:",this.anonymousId));let e=await this.storage.get(u.USER_ID);e&&(this.userId=e,this.log("Loaded user ID from storage:",this.userId));let i=await this.storage.get(u.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(u.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(u.USER_TRAITS,JSON.stringify(this.userTraits));}catch(e){this.log("Error persisting user traits:",e);}}async reset(){if(this.anonymousId=g(),this.userId=null,this.userTraits={},this.log("Identity reset, new anonymous ID:",this.anonymousId),this.storage)try{await Promise.all([this.storage.set(u.ANONYMOUS_ID,this.anonymousId),this.storage.remove(u.USER_ID),this.storage.remove(u.USER_TRAITS)]);}catch(t){this.log("Error resetting identity in storage:",t);}}setDebug(t){this.debug=t;}log(...t){this.debug&&[...t];}};var y=class{constructor(t,e=h.SESSION_TIMEOUT,i={},r=false){s(this,"sessionId",null);s(this,"sessionStartTime",0);s(this,"lastActivity",0);s(this,"timeout");s(this,"storage");s(this,"callbacks");s(this,"debug");this.storage=t,this.timeout=e,this.callbacks=i,this.debug=r;}async initialize(){if(!this.storage){this.log("No storage adapter, using in-memory session only");return}try{let t=await this.storage.get(u.SESSION_ID),e=await this.storage.get(u.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=g(),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(u.SESSION_ID,this.sessionId),this.storage.set(u.SESSION_LAST_ACTIVITY,String(this.lastActivity))]);}catch(t){this.log("Error persisting session:",t);}}async persistLastActivity(){if(this.storage)try{await this.storage.set(u.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(u.SESSION_ID),this.storage.remove(u.SESSION_LAST_ACTIVITY)]);}catch(t){this.log("Error clearing stored session:",t);}}log(...t){this.debug&&[...t];}};var I=class{constructor(t){s(this,"apiUrl");s(this,"apiKey");s(this,"tenantId");s(this,"debug");this.apiUrl=t.apiUrl.replace(/\/$/,""),this.apiKey=t.apiKey,this.tenantId=t.tenantId,this.debug=t.debug??false;}async sendEvents(t,e){let i=`${this.apiUrl}/v1/track`,r=t.map(o=>this.toTrackApiPayload(o)),l=JSON.stringify({events:r});if(this.log("Sending",t.length,"events to",i),e?.useBeacon&&typeof navigator<"u"&&typeof navigator.sendBeacon=="function"&&this.sendViaBeacon(i,l))return this.log("Events sent via sendBeacon"),{accepted:t.map(c=>({event_id:c.event_id,timestamp:c.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:l,keepalive:e?.useBeacon});if(!o.ok)throw o.status===429?a.rateLimited(`Rate limited: ${o.statusText}`,{status:o.status}):a.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 a?o:a.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 r=await this.fetch(e,{method:"POST",headers:this.getHeaders(),body:i});if(!r.ok)throw r.status===429?a.rateLimited(`Rate limited: ${r.statusText}`,{status:r.status}):a.networkError(`HTTP ${r.status}: ${r.statusText}`,{status:r.status});let l=await r.json();return this.log("Identify response:",l),l}catch(r){throw r instanceof a?r:a.networkError(`Failed to send identify: ${r instanceof Error?r.message:String(r)}`,r)}}setTenantId(t){this.tenantId=t;}setDebug(t){this.debug=t;}getHeaders(){let t={"Content-Type":"application/json",Authorization:`Bearer ${this.apiKey}`};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}}}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 a.networkError("fetch is not available in this environment")}log(...t){this.debug&&[...t];}};var S=class{constructor(t){s(this,"queue",[]);s(this,"isFlushing",false);s(this,"flushTimer",null);s(this,"retryTimer",null);s(this,"config");s(this,"initialized",false);this.config={flushAt:t.flushAt??h.FLUSH_AT,flushInterval:t.flushInterval??h.FLUSH_INTERVAL,maxQueueSize:t.maxQueueSize??h.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?.(a.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,h.MAX_BATCH_SIZE),i=e.map(r=>r.event);try{let r=await this.config.transport.sendEvents(i,t);for(let l of r.rejected)if(l.reason==="rate_limited"){let o=e.find(c=>c.event.event_id===l.event_id);o&&o.attempts<h.MAX_RETRIES&&(o.attempts++,this.queue.unshift(o),this.log("Rate limited event re-queued:",l.event_id,"attempt:",o.attempts));}else this.log("Event permanently rejected:",l.event_id,l.reason,l.details);this.log("Flush complete. Accepted:",r.stats.accepted,"Rejected:",r.stats.rejected);}catch(r){let l=a.fromUnknown(r);for(let p of e)p.attempts<h.MAX_RETRIES?(p.attempts++,this.queue.unshift(p)):this.log("Event dropped after max retries:",p.event.event_id);let o=e[0]?.attempts??1,c=U(o);this.log("Network error, scheduling retry in",c,"ms"),this.retryTimer=setTimeout(()=>{this.retryTimer=null,this.flush(t);},c),this.config.onError?.(l,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(u.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(u.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];}};var v=class{constructor(t,e=null){s(this,"config");s(this,"storage",null);s(this,"identity");s(this,"session");s(this,"transport");s(this,"queue");s(this,"initialized",false);s(this,"tenantId");this.validateConfig(t),this.config={apiUrl:h.API_URL,debug:false,flushInterval:h.FLUSH_INTERVAL,flushAt:h.FLUSH_AT,maxQueueSize:h.MAX_QUEUE_SIZE,sessionTimeout:h.SESSION_TIMEOUT,...t},this.storage=e,this.tenantId=t.tenantId??"",this.identity=new m(e,this.config.debug),this.session=new y(e,this.config.sessionTimeout,{onSessionStart:i=>{this.log("Session started:",i),this.trackInternal("session_started",{session_id:i});},onSessionEnd:(i,r)=>{this.log("Session ended:",i,"duration:",r),this.trackInternal("session_ended",{session_id:i,duration_ms:r});}},this.config.debug),this.transport=new I({apiUrl:this.config.apiUrl,apiKey:this.config.apiKey,tenantId:this.tenantId,debug:this.config.debug}),this.queue=new S({flushAt:this.config.flushAt,flushInterval:this.config.flushInterval,maxQueueSize:this.config.maxQueueSize,storage:e,transport:this.transport,onError:(i,r)=>{this.config.onError?.(i),this.log("Queue error:",i.message,"events:",r.length);},debug:this.config.debug});}async initialize(){this.initialized||(await Promise.all([this.identity.initialize(),this.session.initialize(),this.queue.initialize()]),this.initialized=true,this.log("Client initialized"));}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:T()});}catch(i){let r=a.fromUnknown(i);this.config.onError?.(r),this.log("Identify error:",r.message);}}async setUserTraits(t){await this.ensureInitialized(),await this.identity.setUserTraits(t);}async track(t,e,i){await this.ensureInitialized();let r=this.createEvent(t,e,i);if(this.session.touch(),i?.immediate)try{await this.transport.sendEvents([r]);}catch(l){let o=a.fromUnknown(l);this.config.onError?.(o),this.log("Immediate track error:",o.message);}else await this.queue.enqueue(r);}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()}destroy(){this.queue.destroy(),this.log("Client destroyed");}trackInternal(t,e){let i=this.createEvent(t,e);this.queue.enqueue(i).catch(r=>{this.log("Internal track error:",r);});}createEvent(t,e,i){let r=i?.timestamp?A(i.timestamp):T();return {event_id:g(),timestamp:r,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:this.session.getSessionId(),platform:this.config.platform,app_version:this.config.appVersion??null,properties:e??{},user_traits:this.identity.getUserTraits()}}async ensureInitialized(){this.initialized||await this.initialize();}validateConfig(t){if(!t.apiKey)throw a.invalidConfig("apiKey is required");if(!t.projectId)throw a.invalidConfig("projectId is required");if(!t.environment)throw a.invalidConfig("environment is required");if(!t.platform)throw a.invalidConfig("platform is required")}log(...t){this.config.debug&&[...t];}};var w=class{constructor(){s(this,"data",new Map);}async get(t){return this.data.get(t)??null}async set(t,e){this.data.set(t,e);}async remove(t){this.data.delete(t);}};function H(){try{let n="__pih_test__";return localStorage.setItem(n,n),localStorage.removeItem(n),!0}catch{return false}}var P=class{constructor(){s(this,"fallback",null);s(this,"useLocalStorage");this.useLocalStorage=H(),this.useLocalStorage||(this.fallback=new w,console.warn("[PIH] localStorage unavailable, using in-memory storage. Data will not persist across page loads."));}async get(t){if(this.fallback)return this.fallback.get(t);try{return localStorage.getItem(t)}catch{return this.fallback||(this.fallback=new w),this.fallback.get(t)}}async set(t,e){if(this.fallback)return this.fallback.set(t,e);try{localStorage.setItem(t,e);}catch(i){return this.fallback||(this.fallback=new w,console.warn("[PIH] localStorage write failed, falling back to in-memory storage:",i)),this.fallback.set(t,e)}}async remove(t){if(this.fallback)return this.fallback.remove(t);try{localStorage.removeItem(t);}catch{}}};function x(){return new P}x();var k="pih_utm_params",L=["utm_source","utm_medium","utm_campaign","utm_term","utm_content"],E=class{constructor(t,e={}){s(this,"client");s(this,"config");s(this,"cachedUtmParams",null);s(this,"originalPushState",null);s(this,"originalReplaceState",null);s(this,"boundHandleClick");s(this,"boundHandlePopState");this.client=t,this.config={pageViews:e.pageViews!==false,clicks:e.clicks??false,clickSelector:e.clickSelector??"[data-track]",forms:e.forms??false},this.boundHandleClick=this.handleClick.bind(this),this.boundHandlePopState=this.handlePopState.bind(this);}start(){typeof window>"u"||typeof document>"u"||(this.loadCachedUtmParams(),this.config.pageViews&&(this.setupPageViews(),this.trackPageView()),this.config.clicks&&this.setupClickTracking());}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);}trackPageView(){let t=new URL(window.location.href),e=this.extractUtmParams(t);Object.keys(e).length>0&&!this.cachedUtmParams&&(this.cachedUtmParams=e,this.persistUtmParams(e)),this.client.track("page_viewed",{path:t.pathname,search:t.search,hash:t.hash,referrer:document.referrer||null,title:document.title,url:window.location.href,...this.getUtmParams(t)});}setupPageViews(){this.originalPushState=history.pushState,this.originalReplaceState=history.replaceState;let t=this;history.pushState=function(...e){t.originalPushState.apply(history,e),setTimeout(()=>t.trackPageView(),0);},history.replaceState=function(...e){t.originalReplaceState.apply(history,e),setTimeout(()=>t.trackPageView(),0);},window.addEventListener("popstate",this.boundHandlePopState);}handlePopState(){this.trackPageView();}setupClickTracking(){document.addEventListener("click",this.boundHandleClick,true);}handleClick(t){let e=t.target;if(!e)return;let i=e.closest(this.config.clickSelector);if(!i)return;let r=i.tagName.toLowerCase(),l=(i.textContent||"").trim().slice(0,100),o=i.id||null,c=i.className||null,p=i.getAttribute("data-track"),R=i instanceof HTMLAnchorElement?i.href:null;this.client.track("element_clicked",{element_tag:r,element_text:l,element_id:o,element_classes:c,data_track:p,href:R,path:window.location.pathname});}extractUtmParams(t){let e={};for(let i of L){let r=t.searchParams.get(i);r&&(e[i]=r);}return e}getUtmParams(t){let e=this.extractUtmParams(t);return Object.keys(e).length>0?e:this.cachedUtmParams??{}}loadCachedUtmParams(){try{let t=localStorage.getItem(k);t&&(this.cachedUtmParams=JSON.parse(t));}catch{}}persistUtmParams(t){try{localStorage.setItem(k,JSON.stringify(t));}catch{}}};var b=class{constructor(t){s(this,"client");s(this,"boundHandleVisibilityChange");s(this,"boundHandleBeforeUnload");this.client=t,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 _=class extends v{constructor(e,i){let r={...e,platform:"web"};super(r,i??x());s(this,"autocapture",null);s(this,"beacon");this.beacon=new b(this);}async initialize(){await super.initialize(),this.beacon.start(),this.config.autocapture&&(this.autocapture=new E(this,this.config.autocapture),this.autocapture.start()),this.log("Web client initialized");}enableAutocapture(e){this.autocapture&&this.autocapture.stop(),this.autocapture=new E(this,e),this.autocapture.start();}disableAutocapture(){this.autocapture&&(this.autocapture.stop(),this.autocapture=null);}trackPageView(){if(this.autocapture)this.autocapture.trackPageView();else {let e=new URL(window.location.href);this.track("page_viewed",{path:e.pathname,search:e.search,hash:e.hash,referrer:document.referrer||null,title:document.title,url:window.location.href});}}destroy(){this.beacon.stop(),this.autocapture&&this.autocapture.stop(),super.destroy();}log(...e){this.config.debug&&[...e];}},d=null;function D(n){return d?(console.warn("[PIH] SDK already initialized. Returning existing instance."),d):(d=new _(n),d.initialize().catch(t=>{console.error("[PIH] Failed to initialize:",t);}),d)}function N(){return d}function q(){d&&(d.destroy(),d=null);}var z={init:D,getInstance:N,resetInstance:q,WebPIHClient:_},Ht=z;
2
- exports.WebPIHClient=_;exports.default=Ht;exports.getInstance=N;exports.init=D;exports.resetInstance=q;//# sourceMappingURL=index.cjs.map
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
3
3
  //# sourceMappingURL=index.cjs.map