@product-intelligence-hub/sdk-web 0.2.0 → 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 +102 -5
- package/dist/hooks.cjs +2 -0
- package/dist/hooks.cjs.map +1 -0
- package/dist/hooks.d.cts +32 -0
- package/dist/hooks.d.ts +32 -0
- package/dist/hooks.js +2 -0
- package/dist/hooks.js.map +1 -0
- package/dist/index.cjs +2 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +40 -2
- package/dist/index.d.ts +40 -2
- package/dist/index.global.js +36 -2
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/package.json +25 -2
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
|
-
|
|
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,
|
|
149
|
-
clicks: true,
|
|
150
|
-
|
|
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"]}
|
package/dist/hooks.d.cts
ADDED
|
@@ -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.d.ts
ADDED
|
@@ -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 P=Object.defineProperty;var k=(t,e,i)=>e in t?P(t,e,{enumerable:true,configurable:true,writable:true,value:i}):t[e]=i;var o=(t,e,i)=>k(t,typeof e!="symbol"?e+"":e,i);var R=Object.defineProperty,x=(t,e,i)=>e in t?R(t,e,{enumerable:true,configurable:true,writable:true,value:i}):t[e]=i,r=(t,e,i)=>x(t,typeof e!="symbol"?e+"":e,i),h=class u extends Error{constructor(e,i,s){super(e),r(this,"code"),r(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 I(){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 b(){return new Date().toISOString()}function C(t){return t.toISOString()}function O(t,e=1e3,i=16e3){let s=e*Math.pow(2,t);return Math.min(s,i)}var p="pih_",l={QUEUE:`${p}queue`,ANONYMOUS_ID:`${p}anonymous_id`,USER_ID:`${p}user_id`,USER_TRAITS:`${p}user_traits`,SESSION_ID:`${p}session_id`,SESSION_LAST_ACTIVITY:`${p}session_last_activity`},c={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},H=class{constructor(t,e=false){r(this,"anonymousId"),r(this,"userId",null),r(this,"userTraits",{}),r(this,"storage"),r(this,"debug"),this.storage=t,this.debug=e,this.anonymousId=I();}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=I(),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];}},L=class{constructor(t){r(this,"queue",[]),r(this,"isFlushing",false),r(this,"flushTimer",null),r(this,"retryTimer",null),r(this,"config"),r(this,"initialized",false),this.config={flushAt:t.flushAt??c.FLUSH_AT,flushInterval:t.flushInterval??c.FLUSH_INTERVAL,maxQueueSize:t.maxQueueSize??c.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,c.MAX_BATCH_SIZE),i=e.map(s=>s.event);try{let s=await this.config.transport.sendEvents(i,t);for(let a of s.rejected)if(a.reason==="rate_limited"){let n=e.find(d=>d.event.event_id===a.event_id);n&&n.attempts<c.MAX_RETRIES&&(n.attempts++,this.queue.unshift(n),this.log("Rate limited event re-queued:",a.event_id,"attempt:",n.attempts));}else this.log("Event permanently rejected:",a.event_id,a.reason,a.details);this.log("Flush complete. Accepted:",s.stats.accepted,"Rejected:",s.stats.rejected);}catch(s){let a=h.fromUnknown(s);for(let f of e)f.attempts<c.MAX_RETRIES?(f.attempts++,this.queue.unshift(f)):this.log("Event dropped after max retries:",f.event.event_id);let n=e[0]?.attempts??1,d=O(n);this.log("Network error, scheduling retry in",d,"ms"),this.retryTimer=setTimeout(()=>{this.retryTimer=null,this.flush(t);},d),this.config.onError?.(a,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];}},N=class{constructor(t,e=c.SESSION_TIMEOUT,i={},s=false){r(this,"sessionId",null),r(this,"sessionStartTime",0),r(this,"lastActivity",0),r(this,"timeout"),r(this,"storage"),r(this,"callbacks"),r(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=I(),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];}},D=class{constructor(t){r(this,"apiUrl"),r(this,"apiKey"),r(this,"tenantId"),r(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`,s=t.map(n=>this.toTrackApiPayload(n)),a=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,a))return this.log("Events sent via sendBeacon"),{accepted:t.map(n=>({event_id:n.event_id,timestamp:n.timestamp})),rejected:[],stats:{received:t.length,accepted:t.length,rejected:0,processing_time_ms:0}};try{let n=await this.fetch(i,{method:"POST",headers:this.getHeaders(),body:a,keepalive:e?.useBeacon});if(!n.ok)throw n.status===429?h.rateLimited(`Rate limited: ${n.statusText}`,{status:n.status}):h.networkError(`HTTP ${n.status}: ${n.statusText}`,{status:n.status});let d=await n.json();return this.log("Track response:",d),d}catch(n){throw n instanceof h?n:h.networkError(`Failed to send events: ${n instanceof Error?n.message:String(n)}`,n)}}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 a=await s.json();return this.log("Identify response:",a),a}catch(s){throw s instanceof h?s:h.networkError(`Failed to send identify: ${s instanceof Error?s.message:String(s)}`,s)}}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 h.networkError("fetch is not available in this environment")}log(...t){this.debug&&[...t];}},T=class{constructor(t,e=null){r(this,"config"),r(this,"storage",null),r(this,"identity"),r(this,"session"),r(this,"transport"),r(this,"queue"),r(this,"initialized",false),r(this,"tenantId"),this.validateConfig(t),this.config={apiUrl:c.API_URL,debug:false,flushInterval:c.FLUSH_INTERVAL,flushAt:c.FLUSH_AT,maxQueueSize:c.MAX_QUEUE_SIZE,sessionTimeout:c.SESSION_TIMEOUT,...t},this.storage=e,this.tenantId=t.tenantId??"",this.identity=new H(e,this.config.debug),this.session=new N(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 D({apiUrl:this.config.apiUrl,apiKey:this.config.apiKey,tenantId:this.tenantId,debug:this.config.debug}),this.queue=new L({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(){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:b()});}catch(i){let s=h.fromUnknown(i);this.config.onError?.(s),this.log("Identify error:",s.message);}}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(a){let n=h.fromUnknown(a);this.config.onError?.(n),this.log("Immediate track error:",n.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()}destroy(){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);});}createEvent(t,e,i){let s=i?.timestamp?C(i.timestamp):b();return {event_id:I(),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: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 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")}log(...t){this.config.debug&&[...t];}};var _="pih_utm_params",q=["utm_source","utm_medium","utm_campaign","utm_term","utm_content"],m=class{constructor(e,i={}){o(this,"client");o(this,"config");o(this,"cachedUtmParams",null);o(this,"originalPushState",null);o(this,"originalReplaceState",null);o(this,"boundHandleClick");o(this,"boundHandlePopState");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);}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 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(...a){e.apply(history,a),setTimeout(s,0);},history.replaceState=function(...a){i.apply(history,a),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 a=s.tagName.toLowerCase(),n=(s.textContent||"").trim().slice(0,100),d=s.id||null,f=s.className||null,A=s.getAttribute("data-track"),U=s instanceof HTMLAnchorElement?s.href:null;this.client.track("element_clicked",{element_tag:a,element_text:n,element_id:d,element_classes:f,data_track:A,href:U,path:window.location.pathname});}extractUtmParams(e){let i={};for(let s of q){let a=e.searchParams.get(s);a&&(i[s]=a);}return i}getUtmParams(e){let i=this.extractUtmParams(e);return Object.keys(i).length>0?i:this.cachedUtmParams??{}}loadCachedUtmParams(){try{let e=localStorage.getItem(_);e&&(this.cachedUtmParams=JSON.parse(e));}catch{}}persistUtmParams(e){try{localStorage.setItem(_,JSON.stringify(e));}catch{}}};var S=class{constructor(e){o(this,"client");o(this,"boundHandleVisibilityChange");o(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 y=class{constructor(){o(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 w=class{constructor(){o(this,"fallback",null);o(this,"useLocalStorage");this.useLocalStorage=z(),this.useLocalStorage||(this.fallback=new y,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 y),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 y,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 E(){return new w}E();var v=class extends T{constructor(i,s){let a={...i,platform:"web"};super(a,s??E());o(this,"autocapture",null);o(this,"beacon");this.beacon=new S(this);}async initialize(){await super.initialize(),this.beacon.start(),this.config.autocapture&&(this.autocapture=new m(this,this.config.autocapture),this.autocapture.start()),this.log("Web client initialized");}enableAutocapture(i){this.autocapture&&this.autocapture.stop(),this.autocapture=new m(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(),super.destroy();}log(...i){this.config.debug&&[...i];}},g=null;function V(t){return g?(console.warn("[PIH] SDK already initialized. Returning existing instance."),g):(g=new v(t),g.initialize().catch(e=>{console.error("[PIH] Failed to initialize:",e);}),g)}function B(){return g}function Q(){g&&(g.destroy(),g=null);}var F={init:V,getInstance:B,resetInstance:Q,WebPIHClient:v},lt=F;
|
|
2
|
-
exports.WebPIHClient=
|
|
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
|