getu-attribution-v2-sdk 0.3.7 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +85 -0
- package/dist/core/AttributionSDK.d.ts +1 -0
- package/dist/core/AttributionSDK.d.ts.map +1 -1
- package/dist/core/AttributionSDK.js +69 -6
- package/dist/getuai-attribution.min.js +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.esm.js +381 -39
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +383 -39
- package/dist/storage/index.d.ts +20 -0
- package/dist/storage/index.d.ts.map +1 -1
- package/dist/storage/index.js +227 -32
- package/dist/types/index.d.ts +2 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/utils/rootDomain.d.ts +14 -0
- package/dist/utils/rootDomain.d.ts.map +1 -0
- package/dist/utils/rootDomain.js +83 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +4 -2
package/README.md
CHANGED
|
@@ -13,6 +13,7 @@ A JavaScript SDK for tracking user attribution and conversion events in web appl
|
|
|
13
13
|
- [Auto-Tracking](#auto-tracking)
|
|
14
14
|
- [SPA Support](#spa-support)
|
|
15
15
|
- [Cross-Domain UTM](#cross-domain-utm)
|
|
16
|
+
- [Cross-Subdomain Tracking](#cross-subdomain-tracking)
|
|
16
17
|
- [Attribution Data](#attribution-data)
|
|
17
18
|
- [Storage](#storage)
|
|
18
19
|
- [Offline Support](#offline-support)
|
|
@@ -106,6 +107,7 @@ await trackPageView({ category: "homepage" });
|
|
|
106
107
|
| `data-batch-interval` | number | `2000` | Batch interval in milliseconds |
|
|
107
108
|
| `data-auto-clean-utm` | boolean | `true` | Remove UTM params from URL after capture |
|
|
108
109
|
| `data-user-id` | string | - | Initial user ID |
|
|
110
|
+
| `data-domain` | string | auto-detected | Cookie domain for cross-subdomain sharing (e.g. `.example.com`). See [Cross-Subdomain Tracking](#cross-subdomain-tracking) |
|
|
109
111
|
|
|
110
112
|
### Configuration Object
|
|
111
113
|
|
|
@@ -144,6 +146,9 @@ interface SDKConfig {
|
|
|
144
146
|
// User ID
|
|
145
147
|
userId?: string; // Initial user ID to set on initialization
|
|
146
148
|
|
|
149
|
+
// Cross-Subdomain (see Cross-Subdomain Tracking section)
|
|
150
|
+
domain?: string; // Cookie domain override, e.g. ".example.com". Default: auto-detected eTLD+1
|
|
151
|
+
|
|
147
152
|
// Debug
|
|
148
153
|
enableDebug?: boolean; // Default: false
|
|
149
154
|
}
|
|
@@ -726,6 +731,86 @@ const utmParams = getCurrentUTMParams();
|
|
|
726
731
|
|
|
727
732
|
---
|
|
728
733
|
|
|
734
|
+
## Cross-Subdomain Tracking
|
|
735
|
+
|
|
736
|
+
When a visitor moves between subdomains on the same root (e.g. `example.com` → `app.example.com` for signup), the SDK keeps `session_id`, `user_id`, and first-touch attribution continuous. Users are never split into two journeys.
|
|
737
|
+
|
|
738
|
+
### How It Works
|
|
739
|
+
|
|
740
|
+
On initialization, the SDK auto-detects the broadest cookie domain it can write to by probing parent-most hostnames. Once resolved:
|
|
741
|
+
|
|
742
|
+
- Session, user ID, and attribution data are stored in cookies scoped to `domain=.example.com` (the detected root).
|
|
743
|
+
- All subdomains read back the same values — session stays alive across navigation.
|
|
744
|
+
- Legacy `localStorage` data from earlier SDK versions is automatically migrated to cookies on first load.
|
|
745
|
+
|
|
746
|
+
**No code change required** for standard deployments (`example.com`, `example.com`, `shop.acme.co`, etc.).
|
|
747
|
+
|
|
748
|
+
### When You Need Explicit `domain` Config
|
|
749
|
+
|
|
750
|
+
Auto-detection works by asking the browser to accept a probe cookie at each candidate domain. Some deployments require you to set `domain` yourself:
|
|
751
|
+
|
|
752
|
+
| Scenario | Why auto-detect can't handle it | What to set |
|
|
753
|
+
| ------------------------------------------- | ------------------------------------------------------------------------------------------------------- | -------------------------------------------- |
|
|
754
|
+
| Hosted on a public suffix (`*.github.io`, `*.vercel.app`, `*.netlify.app`, `*.pages.dev`) | These are registered as public suffixes — the browser rejects `domain=.github.io`. Each subdomain is isolated by design. | Leave unset — each subdomain is its own scope. If you have your own domain, point it at the app and use that instead. |
|
|
755
|
+
| Multi-tenant platform sharing a parent domain (e.g. `tenant-a.app.com` + `tenant-b.app.com`, tenants must NOT share state) | Auto-detect would pick `.app.com` and leak sessions between tenants. | Set `domain` to the tenant hostname, e.g. `tenant-a.app.com`. |
|
|
756
|
+
| Staging vs prod on the same root (`staging.example.com` + `shop.example.com`, should stay separate) | Auto-detect picks `.example.com` and bridges envs. | Set `domain` to the environment hostname. |
|
|
757
|
+
| Custom enterprise setup where auto-detect returns `null` (air-gapped, odd DNS, cookie-restricted environments) | No candidate domain accepted. | Provide the exact domain you want. |
|
|
758
|
+
|
|
759
|
+
### Usage
|
|
760
|
+
|
|
761
|
+
**Via script tag:**
|
|
762
|
+
|
|
763
|
+
```html
|
|
764
|
+
<script
|
|
765
|
+
src="https://unpkg.com/getu-attribution-v2-sdk/dist/getuai-attribution.min.js"
|
|
766
|
+
data-api-key="your_api_key"
|
|
767
|
+
data-domain=".example.com"
|
|
768
|
+
></script>
|
|
769
|
+
```
|
|
770
|
+
|
|
771
|
+
**Via config object:**
|
|
772
|
+
|
|
773
|
+
```javascript
|
|
774
|
+
await init({
|
|
775
|
+
apiKey: "your_api_key",
|
|
776
|
+
domain: ".example.com", // leading dot optional — ".example.com" and "example.com" behave the same
|
|
777
|
+
});
|
|
778
|
+
```
|
|
779
|
+
|
|
780
|
+
### Verification
|
|
781
|
+
|
|
782
|
+
Open DevTools → Application → Cookies on any page after SDK init. You should see:
|
|
783
|
+
|
|
784
|
+
| Cookie | Domain | Purpose |
|
|
785
|
+
| ------------------ | ------------- | ----------------------------- |
|
|
786
|
+
| `_getuai_session` | `.example.com` | Session id + last activity |
|
|
787
|
+
| `_getuai_attrib` | `.example.com` | First/last-touch UTM data |
|
|
788
|
+
| `getuai_user_id` | `.example.com` | User identifier (after `setUserId`) |
|
|
789
|
+
|
|
790
|
+
If the Domain column shows the full hostname instead of the dotted parent (e.g. `shop.example.com` rather than `.example.com`), cross-subdomain sharing is NOT active — check the [When You Need Explicit `domain` Config](#when-you-need-explicit-domain-config) table and pass `domain` explicitly.
|
|
791
|
+
|
|
792
|
+
### Fallback Behavior
|
|
793
|
+
|
|
794
|
+
- **Cookie write blocked** (incognito mode with cookies disabled, sandboxed iframe): SDK falls back to `localStorage`. Single-site tracking still works; cross-subdomain continuity does not.
|
|
795
|
+
- **`localhost` / IP literals**: Cookie domain detection returns `null`; cookies are written as host-only. Use [nip.io](https://nip.io) subdomains for local cross-subdomain testing (e.g. `a.127.0.0.1.nip.io`).
|
|
796
|
+
|
|
797
|
+
### Session Ownership & Rotation
|
|
798
|
+
|
|
799
|
+
The SDK binds each `session_id` to the user who identified during it, so two different users on a shared device never share one session.
|
|
800
|
+
|
|
801
|
+
| Transition | Session behavior | Reason |
|
|
802
|
+
| ------------------------------------------------ | ------------------------ | ---------------------------------------------------------------------- |
|
|
803
|
+
| Anonymous → `setUserId("A")` | **Keep same session_id** | Backend backfill attaches prior anonymous events to user A |
|
|
804
|
+
| `setUserId("A")` → `setUserId("A")` again | **Keep same session_id** | No-op |
|
|
805
|
+
| `setUserId("A")` → `setUserId("B")` | **New session_id** | Different user — distinct journey |
|
|
806
|
+
| `setUserId("A")` → `removeUserId()` → `setUserId("B")` | **New session_id** | Logout alone doesn't rotate; the subsequent user change does |
|
|
807
|
+
| `setUserId("A")` → `removeUserId()` → `setUserId("A")` | **Keep same session_id** | Same user re-authenticated |
|
|
808
|
+
| `sessionTimeout` elapsed since last activity | **New session_id** | Normal inactivity-based rotation (default 30 min) |
|
|
809
|
+
|
|
810
|
+
Note: `removeUserId()` (logout) by itself does **not** rotate the session. The session still "remembers" its last owner, so a subsequent `setUserId()` can detect a mismatch and rotate correctly. This preserves the common "log out then log back in as yourself" case.
|
|
811
|
+
|
|
812
|
+
---
|
|
813
|
+
|
|
729
814
|
## Attribution Data
|
|
730
815
|
|
|
731
816
|
### Data Structure
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AttributionSDK.d.ts","sourceRoot":"","sources":["../../src/core/AttributionSDK.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,SAAS,EAET,SAAS,EACT,eAAe,EAEf,QAAQ,EACR,WAAW,EAGX,UAAU,EAEX,MAAM,UAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"AttributionSDK.d.ts","sourceRoot":"","sources":["../../src/core/AttributionSDK.ts"],"names":[],"mappings":"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"}
|
|
@@ -2,6 +2,7 @@ import { EventType, Currency, defaultEndpoint, DEFAULT_FIELD_MAPPINGS, } from ".
|
|
|
2
2
|
import { AttributionStorageManager } from "../storage";
|
|
3
3
|
import { EventQueueManager, EventHttpClient } from "../queue";
|
|
4
4
|
import { generateId, getTimestamp, extractUTMParams, extractAdParams, detectDevice, getCurrentUrl, getReferrer, getUserAgent, getPageTitle, generateSessionId, isOnline, ConsoleLogger, addUTMToURL, shouldExcludeDomain, isExternalURL, filterUTMParams, cleanURL, getQueryString, getQueryParams, } from "../utils";
|
|
5
|
+
import { detectCookieDomain, normaliseDomain, } from "../utils/rootDomain";
|
|
5
6
|
export class AttributionSDK {
|
|
6
7
|
constructor(config) {
|
|
7
8
|
this.session = null;
|
|
@@ -52,10 +53,24 @@ export class AttributionSDK {
|
|
|
52
53
|
}
|
|
53
54
|
try {
|
|
54
55
|
this.logger.info("Initializing GetuAI Attribution SDK");
|
|
56
|
+
// Resolve cross-subdomain cookie domain BEFORE any storage read/write
|
|
57
|
+
// so session/user_id/attribution cookies land on the right scope from
|
|
58
|
+
// the very first call. Explicit config wins; otherwise probe-detect.
|
|
59
|
+
const resolvedDomain = normaliseDomain(this.config.domain) ??
|
|
60
|
+
detectCookieDomain();
|
|
61
|
+
this.storage.setCookieDomain(resolvedDomain);
|
|
55
62
|
// Initialize storage in background (IndexedDB failure should not block SDK initialization)
|
|
56
63
|
void this.storage.init().catch((storageError) => {
|
|
57
64
|
this.logger.warn("Storage initialization failed (IndexedDB may be unavailable), continuing with basic features:", storageError);
|
|
58
65
|
});
|
|
66
|
+
// Best-effort: upgrade any legacy single-origin state to the new
|
|
67
|
+
// cross-subdomain cookies so upgraded sites don't lose their session.
|
|
68
|
+
try {
|
|
69
|
+
this.storage.migrateLegacyStorage();
|
|
70
|
+
}
|
|
71
|
+
catch (migrationError) {
|
|
72
|
+
this.logger.debug("Legacy storage migration skipped:", migrationError);
|
|
73
|
+
}
|
|
59
74
|
// Initialize user ID (from config or existing storage)
|
|
60
75
|
this.initializeUserId();
|
|
61
76
|
// Initialize session
|
|
@@ -436,8 +451,42 @@ export class AttributionSDK {
|
|
|
436
451
|
this.logger.warn("Cannot set empty user ID");
|
|
437
452
|
return;
|
|
438
453
|
}
|
|
439
|
-
|
|
440
|
-
this.
|
|
454
|
+
const trimmed = userId.trim();
|
|
455
|
+
this.storage.setUserId(trimmed);
|
|
456
|
+
// Bind this user to the current session. If the session was already
|
|
457
|
+
// owned by a *different* non-empty user (e.g. shared device: A logs
|
|
458
|
+
// out, B logs in), force a rotation — distinct users' activity must
|
|
459
|
+
// not share the same session_id.
|
|
460
|
+
//
|
|
461
|
+
// Note: during SDK init, setUserId() runs before initializeSession(),
|
|
462
|
+
// so this.session may be null here — that's fine, initializeSession()
|
|
463
|
+
// does its own reconciliation against storage.getUserId().
|
|
464
|
+
if (this.session) {
|
|
465
|
+
const prevUserId = this.session.userId;
|
|
466
|
+
if (prevUserId && prevUserId !== trimmed) {
|
|
467
|
+
this.logger.info(`🔄 User changed (${prevUserId} → ${trimmed}) — rotating session`);
|
|
468
|
+
this.rotateSession(trimmed);
|
|
469
|
+
}
|
|
470
|
+
else if (prevUserId !== trimmed) {
|
|
471
|
+
// Anonymous session learning the user's id — attach, no rotation.
|
|
472
|
+
this.session = { ...this.session, userId: trimmed };
|
|
473
|
+
this.storage.storeSession(this.session);
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
this.logger.info(`👤 User ID set: ${trimmed}`);
|
|
477
|
+
}
|
|
478
|
+
// Start a fresh session; used when the current session's owner user_id
|
|
479
|
+
// changes. Preserves no state from the previous session on purpose.
|
|
480
|
+
rotateSession(userId) {
|
|
481
|
+
const now = Date.now();
|
|
482
|
+
this.session = {
|
|
483
|
+
sessionId: generateSessionId(),
|
|
484
|
+
startTime: now,
|
|
485
|
+
lastActivity: now,
|
|
486
|
+
pageViews: 0,
|
|
487
|
+
userId,
|
|
488
|
+
};
|
|
489
|
+
this.storage.storeSession(this.session);
|
|
441
490
|
}
|
|
442
491
|
// Get user ID
|
|
443
492
|
getUserId() {
|
|
@@ -501,21 +550,35 @@ export class AttributionSDK {
|
|
|
501
550
|
initializeSession() {
|
|
502
551
|
const existingSession = this.storage.getSession();
|
|
503
552
|
const now = Date.now();
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
553
|
+
const currentUserId = this.storage.getUserId() ?? undefined;
|
|
554
|
+
const withinTimeout = existingSession &&
|
|
555
|
+
now - existingSession.lastActivity < this.config.sessionTimeout;
|
|
556
|
+
// If the existing session was bound to a *different* non-empty user
|
|
557
|
+
// than the currently active one, it belongs to someone else — don't
|
|
558
|
+
// extend it. Anonymous → known is still fine (existingSession.userId
|
|
559
|
+
// unset), so backend backfill keeps working.
|
|
560
|
+
const ownerMismatch = !!(existingSession?.userId &&
|
|
561
|
+
currentUserId &&
|
|
562
|
+
existingSession.userId !== currentUserId);
|
|
563
|
+
if (withinTimeout && !ownerMismatch) {
|
|
564
|
+
// Extend existing session; attach current user id if the session
|
|
565
|
+
// was anonymous until now.
|
|
507
566
|
this.session = {
|
|
508
567
|
...existingSession,
|
|
509
568
|
lastActivity: now,
|
|
569
|
+
userId: existingSession.userId ?? currentUserId,
|
|
510
570
|
};
|
|
511
571
|
}
|
|
512
572
|
else {
|
|
513
|
-
|
|
573
|
+
if (ownerMismatch) {
|
|
574
|
+
this.logger.info(`🔄 Session owner changed (${existingSession?.userId} → ${currentUserId}) — starting new session`);
|
|
575
|
+
}
|
|
514
576
|
this.session = {
|
|
515
577
|
sessionId: generateSessionId(),
|
|
516
578
|
startTime: now,
|
|
517
579
|
lastActivity: now,
|
|
518
580
|
pageViews: 0,
|
|
581
|
+
userId: currentUserId,
|
|
519
582
|
};
|
|
520
583
|
}
|
|
521
584
|
this.storage.storeSession(this.session);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
!function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.GetuAIAttribution=e():t.GetuAIAttribution=e()}(this,()=>(()=>{"use strict";var t,e,i={d:(t,e)=>{for(var r in e)i.o(e,r)&&!i.o(t,r)&&Object.defineProperty(t,r,{enumerable:!0,get:e[r]})},o:(t,e)=>Object.prototype.hasOwnProperty.call(t,e)},r={};i.d(r,{default:()=>ct}),function(t){t.PAGE_VIEW="page_view",t.PAGE_CLICK="page_click",t.BUTTON_CLICK="button_click",t.VIDEO_PLAY="video_play",t.FORM_SUBMIT="form_submit",t.EMAIL_VERIFICATION="email_verification",t.LOGIN="login",t.SIGNUP="signup",t.PRODUCT_VIEW="product_view",t.ADD_TO_CART="add_to_cart",t.PURCHASE="purchase",t.AUDIT_APPROVED="audit_approved",t.CUSTOM="custom"}(t||(t={})),function(t){t.USD="USD"}(e||(e={}));const n="https://attribution.getu.ai/attribution/api",a=[t.PURCHASE,t.LOGIN,t.SIGNUP,t.FORM_SUBMIT,t.EMAIL_VERIFICATION,t.AUDIT_APPROVED],s={email:["email","e-mail","mail","user_email","email_address"],name:["name","full_name","fullname","username","display_name"],first_name:["first_name","firstname","fname","given_name"],last_name:["last_name","lastname","lname","surname","family_name"],phone:["phone","telephone","mobile","phone_number","tel"],company_name:["company","company_name","organization","org","business"],title:["title","job_title","position","role","job"]};function o(){return Math.floor(Date.now()/1e3)}const c=["adgroup_id","ad_group_id","adgroupid","adset_id","tt_adgroup_id","adgroup"],u=["ad_id","adid","creative_id","creative","tt_ad_id"],l=["gclid","gbraid","wbraid","fbclid","ttclid","msclkid","twclid","li_fat_id"],d=/^\d{7,15}$/;function g(t,e){const i=e.toLowerCase();for(const e of Object.keys(t))if(e.toLowerCase()===i){const i=t[e];for(const t of i)if(t&&""!==t.trim())return t.trim()}}function h(){const t="undefined"!=typeof navigator&&navigator.userAgent||"";return/iPad|Tablet|PlayBook|Silk/i.test(t)?"tablet":/Mobi|Android|iPhone|iPod/i.test(t)?"mobile":"desktop"}function m(){try{const t="__localStorage_test__";return localStorage.setItem(t,t),localStorage.removeItem(t),!0}catch{return!1}}class p{constructor(t=!0){this.enabled=t}debug(t,...e){this.enabled&&console.debug&&console.debug(`[GetuAI Debug] ${t}`,...e)}info(t,...e){this.enabled&&console.info&&console.info(`[GetuAI Info] ${t}`,...e)}warn(t,...e){this.enabled&&console.warn&&console.warn(`[GetuAI Warn] ${t}`,...e)}error(t,...e){this.enabled&&console.error&&console.error(`[GetuAI Error] ${t}`,...e)}}function f(){return document.referrer||""}function _(){return window.location.href}function w(){return document.title||""}function y(t,e){try{const i=new URL(t);return Object.entries(e).forEach(([t,e])=>{e&&!i.searchParams.has(t)&&i.searchParams.set(t,e)}),i.toString()}catch(e){return t}}function v(t,e=["utm_source","utm_medium","utm_campaign"]){const i={};return e.forEach(e=>{t[e]&&(i[e]=t[e])}),i}function k(t){try{const e=t||window.location.href;return new URL(e).search}catch(t){return""}}function S(t){try{const e=t||window.location.href,i=new URL(e),r={};return i.searchParams.forEach((t,e)=>{r[e]=t}),r}catch(t){return{}}}class b{constructor(t){this.logger=t}get(t){try{if(!m())return this.logger.warn("LocalStorage not supported"),null;const e=localStorage.getItem(t);return null===e?null:JSON.parse(e)}catch(t){return this.logger.error("Error reading from localStorage:",t),null}}set(t,e){try{if(!m())return void this.logger.warn("LocalStorage not supported");localStorage.setItem(t,JSON.stringify(e))}catch(t){this.logger.error("Error writing to localStorage:",t),this.handleQuotaExceeded()}}remove(t){try{m()&&localStorage.removeItem(t)}catch(t){this.logger.error("Error removing from localStorage:",t)}}clear(){try{m()&&localStorage.clear()}catch(t){this.logger.error("Error clearing localStorage:",t)}}handleQuotaExceeded(){try{const t=Object.keys(localStorage).filter(t=>t.startsWith("attribution_"));if(t.length>0){t.sort((t,e)=>{const i=this.get(t),r=this.get(e);return(i?.expiresAt||0)-(r?.expiresAt||0)});const e=Math.ceil(.2*t.length);t.slice(0,e).forEach(t=>{this.remove(t)}),this.logger.info(`Cleaned up ${e} old attribution records`)}}catch(t){this.logger.error("Error during quota cleanup:",t)}}}class E{constructor(t){this.dbName="attribution_events",this.dbVersion=1,this.storeName="events",this.db=null,this.logger=t}async init(){if("indexedDB"in window)return new Promise((t,e)=>{const i=indexedDB.open(this.dbName,this.dbVersion);i.onerror=()=>{this.logger.error("Failed to open IndexedDB:",i.error),e(i.error)},i.onsuccess=()=>{this.db=i.result,this.logger.info("IndexedDB initialized successfully"),t()},i.onupgradeneeded=t=>{const e=t.target.result;if(!e.objectStoreNames.contains(this.storeName)){const t=e.createObjectStore(this.storeName,{keyPath:"id",autoIncrement:!0});t.createIndex("timestamp","timestamp",{unique:!1}),t.createIndex("sent","sent",{unique:!1}),t.createIndex("queued_at","queued_at",{unique:!1})}}});this.logger.warn("IndexedDB not supported")}async addEvent(t){if(!this.db)throw new Error("IndexedDB not initialized");return new Promise((e,i)=>{const r=this.db.transaction([this.storeName],"readwrite").objectStore(this.storeName),n={...t,queued_at:Date.now(),sent:!1},a=r.add(n);a.onsuccess=()=>{this.logger.debug("Event added to IndexedDB queue"),e()},a.onerror=()=>{this.logger.error("Failed to add event to IndexedDB:",a.error),i(a.error)}})}async getUnsentEvents(t=100){return this.db?new Promise((e,i)=>{const r=this.db.transaction([this.storeName],"readonly").objectStore(this.storeName).index("sent").getAll(IDBKeyRange.only(!1),t);r.onsuccess=()=>{const t=r.result.map(t=>{const{queued_at:e,sent:i,...r}=t;return r});e(t)},r.onerror=()=>{this.logger.error("Failed to get unsent events:",r.error),i(r.error)}}):[]}async markEventsAsSent(t){if(this.db&&0!==t.length)return new Promise((e,i)=>{const r=this.db.transaction([this.storeName],"readwrite").objectStore(this.storeName);let n=0,a=!1;t.forEach(s=>{const o=r.get(s);o.onsuccess=()=>{if(o.result){const s={...o.result,sent:!0},c=r.put(s);c.onsuccess=()=>{n++,n!==t.length||a||e()},c.onerror=()=>{a=!0,this.logger.error("Failed to mark event as sent:",c.error),i(c.error)}}else n++,n!==t.length||a||e()},o.onerror=()=>{a=!0,this.logger.error("Failed to get event for marking as sent:",o.error),i(o.error)}})})}async cleanupOldEvents(t=6048e5){if(!this.db)return;const e=Date.now()-t;return new Promise((t,i)=>{const r=this.db.transaction([this.storeName],"readwrite").objectStore(this.storeName).index("queued_at").openCursor(IDBKeyRange.upperBound(e));r.onsuccess=()=>{const e=r.result;e?(e.delete(),e.continue()):(this.logger.info("Old events cleanup completed"),t())},r.onerror=()=>{this.logger.error("Failed to cleanup old events:",r.error),i(r.error)}})}async getQueueSize(){return this.db?new Promise((t,e)=>{const i=this.db.transaction([this.storeName],"readonly").objectStore(this.storeName).count();i.onsuccess=()=>{t(i.result)},i.onerror=()=>{this.logger.error("Failed to get queue size:",i.error),e(i.error)}}):0}async clear(){if(this.db)return new Promise((t,e)=>{const i=this.db.transaction([this.storeName],"readwrite").objectStore(this.storeName).clear();i.onsuccess=()=>{this.logger.info("IndexedDB queue cleared"),t()},i.onerror=()=>{this.logger.error("Failed to clear IndexedDB queue:",i.error),e(i.error)}})}}class T{constructor(t){this.UTM_STORAGE_KEY="attribution_utm_data",this.SESSION_STORAGE_KEY="attribution_session",this.USER_ID_KEY="getuai_user_id",this.PENDING_EVENTS_KEY="getuai_pending_events_v1",this.USER_TRAITS_KEY="getuai_user_traits",this.USER_ID_COOKIE_EXPIRES=365,this.logger=t,this.localStorage=new b(t),this.indexedDB=new E(t)}setUserId(t){if(!t||""===t.trim())return void this.logger.warn("Cannot set empty user ID");const e=t.trim();try{this.setCookie(this.USER_ID_KEY,e,this.USER_ID_COOKIE_EXPIRES),this.logger.debug(`👤 User ID stored in cookie: ${e}`)}catch(t){this.logger.error("Failed to store user ID in cookie:",t)}try{"undefined"!=typeof localStorage&&(localStorage.setItem(this.USER_ID_KEY,e),this.logger.debug(`👤 User ID stored in localStorage: ${e}`))}catch(t){this.logger.error("Failed to store user ID in localStorage:",t)}}getUserId(){try{if("undefined"!=typeof localStorage){const t=localStorage.getItem(this.USER_ID_KEY);if(t)return t}}catch(t){this.logger.debug("Failed to get user ID from localStorage:",t)}try{const t=this.getCookie(this.USER_ID_KEY);if(t){try{"undefined"!=typeof localStorage&&localStorage.setItem(this.USER_ID_KEY,t)}catch(t){}return t}}catch(t){this.logger.debug("Failed to get user ID from cookie:",t)}return null}removeUserId(){try{this.deleteCookie(this.USER_ID_KEY),this.logger.debug("👤 User ID removed from cookie")}catch(t){this.logger.error("Failed to remove user ID from cookie:",t)}try{"undefined"!=typeof localStorage&&(localStorage.removeItem(this.USER_ID_KEY),this.logger.debug("👤 User ID removed from localStorage"))}catch(t){this.logger.error("Failed to remove user ID from localStorage:",t)}}setCookie(t,e,i){try{const r=new Date;r.setTime(r.getTime()+24*i*60*60*1e3);const n=`${t}=${encodeURIComponent(e)};expires=${r.toUTCString()};path=/;SameSite=Lax`;document.cookie=n}catch(t){this.logger.error("Failed to set cookie:",t)}}getCookie(t){try{const e=t+"=",i=document.cookie.split(";");for(let t=0;t<i.length;t++){let r=i[t];for(;" "===r.charAt(0);)r=r.substring(1,r.length);if(0===r.indexOf(e))return decodeURIComponent(r.substring(e.length,r.length))}return null}catch(t){return this.logger.error("Failed to get cookie:",t),null}}deleteCookie(t){try{document.cookie=`${t}=;expires=Thu, 01 Jan 1970 00:00:00 UTC;path=/;`}catch(t){this.logger.error("Failed to delete cookie:",t)}}async init(){await this.indexedDB.init()}getPendingEvents(){try{const t=this.localStorage.get(this.PENDING_EVENTS_KEY);return Array.isArray(t)?t:[]}catch(t){return this.logger.debug("Failed to read pending events:",t),[]}}appendPendingEvents(t){if(t&&0!==t.length)try{const e=[...this.getPendingEvents(),...t].slice(-500);this.localStorage.set(this.PENDING_EVENTS_KEY,e)}catch(t){this.logger.debug("Failed to append pending events:",t)}}removePendingEventsByIds(t){if(t&&0!==t.length)try{const e=this.getPendingEvents();if(0===e.length)return;const i=new Set(t),r=e.filter(t=>!i.has(t.event_id));0===r.length?this.localStorage.remove(this.PENDING_EVENTS_KEY):this.localStorage.set(this.PENDING_EVENTS_KEY,r)}catch(t){this.logger.debug("Failed to remove pending events:",t)}}clearPendingEvents(){try{this.localStorage.remove(this.PENDING_EVENTS_KEY)}catch(t){this.logger.debug("Failed to clear pending events:",t)}}storeUTMData(t){try{const e=this.getUTMData(),i={utm_source:"",utm_medium:"",utm_campaign:"",utm_term:"",utm_content:"",timestamp:Date.now()},r={firstTouch:e?.firstTouch||i,lastTouch:e?.lastTouch||i,touchpoints:e?.touchpoints||[],...t,expiresAt:Date.now()+2592e6};this.localStorage.set(this.UTM_STORAGE_KEY,r),this.logger.debug("UTM data stored successfully:",{firstTouch:r.firstTouch,lastTouch:r.lastTouch,touchpointsCount:r.touchpoints.length,expiresAt:new Date(r.expiresAt).toISOString()})}catch(t){this.logger.error("Failed to store UTM data:",t)}}getUTMData(){const t=this.localStorage.get(this.UTM_STORAGE_KEY);return t&&t.expiresAt&&t.expiresAt>Date.now()?t:(t&&this.localStorage.remove(this.UTM_STORAGE_KEY),null)}storeSession(t){this.localStorage.set(this.SESSION_STORAGE_KEY,t)}getSession(){return this.localStorage.get(this.SESSION_STORAGE_KEY)}async queueEvent(t){try{await this.indexedDB.addEvent(t)}catch(t){throw this.logger.error("Failed to queue event:",t),t}}async getUnsentEvents(t=100){return await this.indexedDB.getUnsentEvents(t)}async markEventsAsSent(t){await this.indexedDB.markEventsAsSent(t)}async getQueueSize(){return await this.indexedDB.getQueueSize()}async cleanupOldEvents(){await this.indexedDB.cleanupOldEvents()}async clearQueue(){await this.indexedDB.clear()}cleanupExpiredData(){this.getUTMData()}setUserTraits(t){try{const e={...this.getUserTraits()||{},...t};"undefined"!=typeof localStorage&&(localStorage.setItem(this.USER_TRAITS_KEY,JSON.stringify(e)),this.logger.debug("User traits stored:",e))}catch(t){this.logger.error("Failed to store user traits:",t)}}getUserTraits(){try{if("undefined"!=typeof localStorage){const t=localStorage.getItem(this.USER_TRAITS_KEY);return t?JSON.parse(t):null}return null}catch(t){return this.logger.error("Failed to get user traits:",t),null}}clearUserTraits(){try{"undefined"!=typeof localStorage&&(localStorage.removeItem(this.USER_TRAITS_KEY),this.logger.debug("User traits cleared"))}catch(t){this.logger.error("Failed to clear user traits:",t)}}}const I="0.3.7";class P{constructor(t,e,i,r=100,n=2e3,a=3,s=1e3,o){this.queue=[],this.processing=!1,this.batchTimer=null,this.retryCounts=new Map,this.logger=t,this.apiKey=e,this.apiEndpoint=i,this.batchSize=r,this.batchInterval=n,this.maxRetries=a,this.retryDelay=s,this.sendEvents=o,this.debouncedProcess=function(t){let e;return(...i)=>{clearTimeout(e),e=setTimeout(()=>t(...i),100)}}(this.process.bind(this))}add(t){if(a.includes(t.event_type))return this.logger.debug(`Immediate event detected: ${t.event_type}, sending immediately`),void this.processImmediate(t);this.queue.push(t),this.logger.debug(`Event added to queue: ${t.event_type}`),this.scheduleBatchProcessing()}async process(){if(this.processing||0===this.queue.length)return;this.processing=!0;const t=this.queue.splice(0,this.batchSize);try{this.logger.debug(`Processing ${t.length} events from queue`),await this.sendEvents(t),this.resetRetryCounts(t),this.logger.info(`Successfully processed ${t.length} events`)}catch(e){return this.logger.error("Failed to process events:",e),this.enqueueBatchForRetry(t),void setTimeout(()=>{this.processing=!1,this.debouncedProcess()},this.retryDelay)}this.processing=!1,this.queue.length>0&&this.debouncedProcess()}async processImmediate(t){try{this.logger.debug(`Processing immediate event: ${t.event_type}`),await this.sendEvents([t]),this.resetRetryCounts([t]),this.logger.info(`Immediate event processed successfully: ${t.event_type}`)}catch(e){this.logger.error(`Failed to process immediate event: ${t.event_type}`,e),this.enqueueBatchForRetry([t])}}scheduleBatchProcessing(){this.batchTimer&&clearTimeout(this.batchTimer),this.batchTimer=setTimeout(()=>{this.debouncedProcess()},this.batchInterval)}clear(){this.queue=[],this.batchTimer&&(clearTimeout(this.batchTimer),this.batchTimer=null),this.processing=!1,this.logger.info("Event queue cleared")}size(){return this.queue.length}getStats(){return{size:this.queue.length,processing:this.processing}}async flush(){for(this.logger.info("Flushing event queue");this.queue.length>0;)await this.process()}drainAll(){const t=this.queue.splice(0,this.queue.length);return this.batchTimer&&(clearTimeout(this.batchTimer),this.batchTimer=null),this.processing=!1,t}enqueueBatchForRetry(t){const e=[];for(const i of t){const t=(this.retryCounts.get(i.event_id)||0)+1;this.retryCounts.set(i.event_id,t),t>this.maxRetries?(this.logger.warn(`Dropping event after max retries (${this.maxRetries}): ${i.event_type} (${i.event_id})`),this.retryCounts.delete(i.event_id)):e.push(i)}e.length>0&&this.queue.unshift(...e)}resetRetryCounts(t){for(const e of t)this.retryCounts.delete(e.event_id)}}class A{constructor(t,e,i,r=3,n=1e3){this.logger=t,this.apiKey=e,this.apiEndpoint=i,this.maxRetries=r,this.retryDelay=n}async sendEvents(t){if(0===t.length)return;const e={events:t.map(t=>({event_id:t.event_id,event_type:t.event_type,tracking_user_id:t.tracking_user_id,utm_source:t.utm_source||null,utm_medium:t.utm_medium||null,utm_campaign:t.utm_campaign||null,utm_term:t.utm_term||null,utm_content:t.utm_content||null,revenue:t.revenue||null,currency:t.currency||null,event_data:t.event_data||null,context:t.context||null,timestamp:t.timestamp||o(),custom_event_name:t.custom_event_name||void 0})),sdk_version:I};await async function(t,e=3,i=1e3){let r;for(let n=0;n<=e;n++)try{return await t()}catch(t){if(r=t,n===e)throw r;const a=i*Math.pow(2,n);await new Promise(t=>setTimeout(t,a))}throw r}(async()=>{const i=await fetch(`${this.apiEndpoint}/attribution/events`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${this.apiKey}`},body:JSON.stringify(e)});if(!i.ok){const t=await i.text();throw new Error(`HTTP ${i.status}: ${t}`)}const r=await i.json();return this.logger.debug("Events sent successfully:",r),{result:r,sentEvents:t}},this.maxRetries,this.retryDelay)}async sendEventsKeepalive(t){if(0===t.length)return;const e={events:t.map(t=>({event_id:t.event_id,event_type:t.event_type,tracking_user_id:t.tracking_user_id,utm_source:t.utm_source||null,utm_medium:t.utm_medium||null,utm_campaign:t.utm_campaign||null,utm_term:t.utm_term||null,utm_content:t.utm_content||null,revenue:t.revenue||null,currency:t.currency||null,event_data:t.event_data||null,context:t.context||null,timestamp:t.timestamp||o(),custom_event_name:t.custom_event_name||void 0})),sdk_version:I},i=await fetch(`${this.apiEndpoint}/attribution/events`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${this.apiKey}`},body:JSON.stringify(e),keepalive:!0});if(!i.ok)throw new Error(`HTTP ${i.status}`)}sendEventsBeacon(t){if(0===t.length)return!0;if("undefined"==typeof navigator||"function"!=typeof navigator.sendBeacon)return!1;const e={events:t.map(t=>({event_id:t.event_id,event_type:t.event_type,tracking_user_id:t.tracking_user_id,utm_source:t.utm_source||null,utm_medium:t.utm_medium||null,utm_campaign:t.utm_campaign||null,utm_term:t.utm_term||null,utm_content:t.utm_content||null,revenue:t.revenue||null,currency:t.currency||null,event_data:t.event_data||null,context:t.context||null,timestamp:t.timestamp||o(),custom_event_name:t.custom_event_name||void 0})),sdk_version:I},i=`${this.apiEndpoint}/attribution/events_beacon?api_key=${encodeURIComponent(this.apiKey)}`,r=new Blob([JSON.stringify(e)],{type:"text/plain"});return navigator.sendBeacon(i,r)}async sendSingleEvent(t){await this.sendEvents([t])}async testConnection(){try{return(await fetch(`${this.apiEndpoint}/health`,{method:"GET",headers:{Authorization:`Bearer ${this.apiKey}`}})).ok}catch(t){return this.logger.error("Connection test failed:",t),!1}}}class D{constructor(t){this.session=null,this.initialized=!1,this.autoTrackEnabled=!1,this.pageViewTrackTimes=new Map,this.cachedPublicIP=null,this.publicIPFetchPromise=null,this.initialPageViewTriggered=!1,this.spaTrackingEnabled=!1,this.lastTrackedPath="",this.originalPushState=null,this.originalReplaceState=null,this.popstateHandler=null,this.config={apiEndpoint:n,batchSize:100,batchInterval:2e3,maxRetries:3,retryDelay:1e3,enableDebug:!1,autoTrack:!1,autoTrackPageView:!1,sessionTimeout:18e5,enableCrossDomainUTM:!0,crossDomainUTMParams:["utm_source","utm_medium","utm_campaign","utm_term","utm_content"],excludeDomains:[],autoCleanUTM:!0,pageViewDebounceInterval:5e3,...t},this.logger=new p(this.config.enableDebug),this.storage=new T(this.logger),this.httpClient=new A(this.logger,this.config.apiKey,this.config.apiEndpoint||n,this.config.maxRetries,this.config.retryDelay),this.queue=new P(this.logger,this.config.apiKey,this.config.apiEndpoint||n,this.config.batchSize,this.config.batchInterval,this.config.maxRetries,this.config.retryDelay,t=>this.httpClient.sendEvents(t))}async init(){if(this.initialized)this.logger.warn("SDK already initialized");else try{this.logger.info("Initializing GetuAI Attribution SDK"),this.storage.init().catch(t=>{this.logger.warn("Storage initialization failed (IndexedDB may be unavailable), continuing with basic features:",t)}),this.initializeUserId(),this.initializeSession(),this.extractAndStoreUTMData(),this.config.autoTrack&&this.setupAutoTracking(),this.setupNetworkHandlers(),this.setupVisibilityHandlers(),this.setupBeforeUnloadHandler(),this.initialized=!0,this.logger.info("🚀 GetuAI Attribution SDK initialized successfully"),this.logger.info("📄 Auto track page view = "+this.config.autoTrackPageView),this.retryPendingEvents().catch(t=>{this.logger.warn("⚠️ Pending events retry failed",t)}),this.config.autoTrackPageView&&(this.logger.info("📄 Auto track page view enabled (including SPA route tracking)"),this.lastTrackedPath=this.getCurrentPath(),this.setupSPATracking(),this.initialPageViewTriggered||(this.initialPageViewTriggered=!0,Promise.resolve().then(()=>this.trackPageView()).then(()=>this.queue.process()).then(()=>{this.logger.info("✅ Auto track page view completed")}).catch(t=>this.logger.error("❌ Auto track page view failed:",t))))}catch(t){throw this.logger.error("Failed to initialize SDK:",t),t}}async trackEvent(i,r,n,a,s=e.USD,c){if(this.initialized)try{const e=_(),u=this.cachedPublicIP;this.ensurePublicIPFetched();const l={domain:"undefined"!=typeof window?window.location.hostname:null,path:"undefined"!=typeof window?window.location.pathname:null,title:w(),referrer:f(),url:e.split("?")[0],querystring:k(e),query_params:S(e),ip_address:u},d={session_id:this.session?.sessionId,start_time:this.session?.startTime,last_activity:this.session?.lastActivity,page_views:this.session?.pageViews},g=n||this.getUserId(),m={event_id:"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,function(t){const e=16*Math.random()|0;return("x"===t?e:3&e|8).toString(16)}),event_type:i,tracking_user_id:g||void 0,timestamp:o(),event_data:r,context:{page:l,session:d},revenue:a,currency:s,...i===t.CUSTOM&&c?{custom_event_name:c}:{},...this.getUTMParams(),device:h(),...this.getAdParams()};this.logger.debug(`Tracking event: ${i}`,m),this.queue.add(m)}catch(t){this.logger.error(`Failed to track event ${i}:`,t)}else this.logger.warn("SDK not initialized, event not tracked")}async trackCustomEvent(i,r,n,a,s=e.USD){i&&""!==i.trim()?await this.trackEvent(t.CUSTOM,r,n,a,s,i.trim()):this.logger.error("trackCustomEvent requires a non-empty customEventName")}async trackPageView(e,i){const r=_(),n=r.split("?")[0],a=Date.now(),s=this.config.pageViewDebounceInterval||5e3,o=this.pageViewTrackTimes.get(n);if(o&&a-o<s)return void this.logger.debug(`Page view debounced: ${n} (last tracked ${a-o}ms ago)`);const c={url:r,title:w(),referrer:f(),user_agent:navigator.userAgent||"",...e};await this.trackEvent(t.PAGE_VIEW,c,i),this.pageViewTrackTimes.set(n,a),this.cleanupPageViewTrackTimes()}cleanupPageViewTrackTimes(){const t=Date.now()-36e5;for(const[e,i]of this.pageViewTrackTimes.entries())i<t&&this.pageViewTrackTimes.delete(e)}ensurePublicIPFetched(){this.cachedPublicIP||this.publicIPFetchPromise||(this.publicIPFetchPromise=this.fetchPublicIP().then(t=>(t&&(this.cachedPublicIP=t),t)).catch(()=>null).finally(()=>{this.publicIPFetchPromise=null}))}async fetchPublicIP(){try{const t=new AbortController,e=setTimeout(()=>t.abort(),2e3),i=await fetch("https://api.ipify.org?format=json",{signal:t.signal,headers:{Accept:"application/json"}});if(clearTimeout(e),!i.ok)return null;const r=await i.json();return"string"==typeof r?.ip?r.ip:null}catch(t){return this.logger.debug("Public IP fetch failed",t),null}}async trackPurchase(i,r,n=e.USD,a){await this.trackEvent(t.PURCHASE,a,i,r,n)}async trackLogin(e,i){await this.trackEvent(t.LOGIN,i,e)}async trackSignup(e,i){await this.trackEvent(t.SIGNUP,i,e)}async trackFormSubmit(e,i){await this.trackEvent(t.FORM_SUBMIT,i,e)}async trackVideoPlay(e,i){await this.trackEvent(t.VIDEO_PLAY,i,e)}async trackEmailVerification(e,i){await this.trackEvent(t.EMAIL_VERIFICATION,i,e)}async trackAuditApproved(e,i){await this.trackEvent(t.AUDIT_APPROVED,i,e)}async trackPurchaseAuto(i){const r=i.tracking_user_id||this.getUserId();r?await this.trackEvent(t.PURCHASE,i.purchaseData,r,i.revenue,i.currency||e.USD):this.logger.error("❌ trackPurchaseAuto requires tracking_user_id. Please set user ID using setUserId() or pass it in options.tracking_user_id.")}async trackLoginAuto(e){const i=e.tracking_user_id||this.getUserId();i?await this.trackEvent(t.LOGIN,e.loginData,i):this.logger.error("❌ trackLoginAuto requires tracking_user_id. Please set user ID using setUserId() or pass it in options.tracking_user_id.")}async trackSignupAuto(e){const i=e.tracking_user_id||this.getUserId();i?await this.trackEvent(t.SIGNUP,e.signupData,i):this.logger.error("❌ trackSignupAuto requires tracking_user_id. Please set user ID using setUserId() or pass it in options.tracking_user_id.")}async trackEmailVerificationAuto(e){const i=e.tracking_user_id||this.getUserId();i?await this.trackEvent(t.EMAIL_VERIFICATION,e.verificationData,i):this.logger.error("❌ trackEmailVerificationAuto requires tracking_user_id. Please set user ID using setUserId() or pass it in options.tracking_user_id.")}async trackProductView(e,i){await this.trackEvent(t.PRODUCT_VIEW,i,e)}async trackAddToCart(e,i){await this.trackEvent(t.ADD_TO_CART,i,e)}async trackPageClick(e,i){await this.trackEvent(t.PAGE_CLICK,i,e)}async trackButtonClick(e,i,r){await this.trackEvent(t.BUTTON_CLICK,{button_name:e,...r},i)}getAttributionData(){return this.storage.getUTMData()}addUTMToURL(t){if(!this.config.enableCrossDomainUTM)return t;const e=this.getAttributionData();return e?y(t,v(e.lastTouch,this.config.crossDomainUTMParams)):t}getCurrentUTMParams(){const t=this.getAttributionData();return t?v(t.lastTouch,this.config.crossDomainUTMParams):{}}getUTMParams(){const t=this.getAttributionData();if(!t)return this.logger.debug("No attribution data available for UTM params"),{};const e={utm_source:t.lastTouch.utm_source||null,utm_medium:t.lastTouch.utm_medium||null,utm_campaign:t.lastTouch.utm_campaign||null,utm_term:t.lastTouch.utm_term||null,utm_content:t.lastTouch.utm_content||null},i={};return e.utm_source&&""!==e.utm_source.trim()?i.utm_source=e.utm_source:i.utm_source=null,e.utm_medium&&""!==e.utm_medium.trim()?i.utm_medium=e.utm_medium:i.utm_medium=null,e.utm_campaign&&""!==e.utm_campaign.trim()?i.utm_campaign=e.utm_campaign:i.utm_campaign=null,e.utm_term&&""!==e.utm_term.trim()?i.utm_term=e.utm_term:i.utm_term=null,e.utm_content&&""!==e.utm_content.trim()?i.utm_content=e.utm_content:i.utm_content=null,this.logger.debug("UTM params for event:",i),i}getAdParams(){const t=this.getAttributionData();if(!t)return{};const e=t.lastTouch,i=t=>t&&"string"==typeof t&&""!==t.trim()?t:null;return{adgroup_id:i(e.adgroup_id),ad_id:i(e.ad_id),adgroup_id_source:i(e.adgroup_id_source),ad_id_source:i(e.ad_id_source),click_id:i(e.click_id),click_id_source:i(e.click_id_source)}}initializeUserId(){if(this.config.userId)this.setUserId(this.config.userId),this.logger.info(`👤 User ID initialized from config: ${this.config.userId}`);else{const t=this.getUserId();t?this.logger.debug(`👤 Existing user ID found: ${t}`):this.logger.debug("👤 No user ID found, will be set when user identifies")}}setUserId(t){t&&""!==t.trim()?(this.storage.setUserId(t),this.logger.info(`👤 User ID set: ${t}`)):this.logger.warn("Cannot set empty user ID")}getUserId(){return this.storage.getUserId()}removeUserId(){this.storage.removeUserId(),this.logger.info("👤 User ID removed")}async identify(e,i){this.initialized?e&&""!==e.trim()?(this.setUserId(e),i&&(this.storage.setUserTraits(i),this.logger.info(`User identified: ${e}`,i)),await this.trackEvent(t.FORM_SUBMIT,{_identify:!0,_user_traits:i,tracking_user_id:e},e)):this.logger.warn("Cannot identify with empty user ID"):this.logger.warn("SDK not initialized, identify call queued")}getUserTraits(){return this.storage.getUserTraits()}getSessionId(){return this.session?.sessionId||null}initializeSession(){const t=this.storage.getSession(),e=Date.now();t&&e-t.lastActivity<this.config.sessionTimeout?this.session={...t,lastActivity:e}:this.session={sessionId:`session_${Date.now()}_${Math.random().toString(36).substr(2,9)}`,startTime:e,lastActivity:e,pageViews:0},this.storage.storeSession(this.session),this.logger.debug("Session initialized:",this.session)}extractAndStoreUTMData(){const t=_(),e=function(t){const e=function(t){const e={};try{new URL(t).searchParams.forEach((t,i)=>{e[i]=t})}catch(t){}return e}(t),i={};return["utm_source","utm_medium","utm_campaign","utm_term","utm_content"].forEach(t=>{e[t]&&""!==e[t].trim()&&(i[t]=e[t].trim())}),i}(t),i=function(t){const e=function(t){const e={};try{const i=new URL(t),r=new Set;i.searchParams.forEach((t,e)=>{r.add(e)}),r.forEach(t=>{const r=i.searchParams.getAll(t);e[t]=r.filter(t=>null!=t&&""!==t)})}catch(t){}return e}(t),i={};for(const t of c){const r=g(e,t);if(void 0!==r){/^\{.+\}$/.test(r)?(i.adgroup_id=r,i.adgroup_id_source="valuetrack"):(i.adgroup_id=r,i.adgroup_id_source="explicit");break}}if(!i.adgroup_id){const t=g(e,"utm_term")?e[Object.keys(e).find(t=>"utm_term"===t.toLowerCase())]:[];for(const e of t){const t=(e||"").trim();if(d.test(t)){i.adgroup_id=t,i.adgroup_id_source="utm_term_numeric";break}}}for(const t of u){const r=g(e,t);if(void 0!==r){/^\{.+\}$/.test(r)?(i.ad_id=r,i.ad_id_source="valuetrack"):(i.ad_id=r,i.ad_id_source="explicit");break}}if(!i.ad_id){const t=g(e,"utm_content")?e[Object.keys(e).find(t=>"utm_content"===t.toLowerCase())]:[];for(const e of t){const t=(e||"").trim();if(d.test(t)){i.ad_id=t,i.ad_id_source="utm_content_numeric";break}}}for(const t of l){const r=g(e,t);if(r&&""!==r.trim()){i.click_id=r.trim(),i.click_id_source=t;break}}return i}(t);if(this.logger.debug("Extracting UTM params from URL:",t),this.logger.debug("Found UTM params:",e),this.logger.debug("Found ad params:",i),0===Object.keys(e).length&&!i.adgroup_id&&!i.ad_id&&!i.click_id)return void this.logger.debug("No UTM, ad, or click parameters found in URL");const r={utm_source:e.utm_source||"",utm_medium:e.utm_medium||"",utm_campaign:e.utm_campaign||"",utm_term:e.utm_term||"",utm_content:e.utm_content||"",adgroup_id:i.adgroup_id||"",ad_id:i.ad_id||"",adgroup_id_source:i.adgroup_id_source||"",ad_id_source:i.ad_id_source||"",click_id:i.click_id||"",click_id_source:i.click_id_source||"",timestamp:Date.now()},n=this.getAttributionData(),a={firstTouch:n?.firstTouch||r,lastTouch:r,touchpoints:n?.touchpoints||[],expiresAt:Date.now()+2592e6};n&&n.lastTouch.utm_source===r.utm_source&&n.lastTouch.utm_campaign===r.utm_campaign||a.touchpoints.push(r),this.storage.storeUTMData(a),this.logger.info("UTM data extracted and stored successfully:",r),this.config.autoCleanUTM&&function(){try{const t=new URL(window.location.href);let e=!1;["utm_source","utm_medium","utm_campaign","utm_term","utm_content"].forEach(i=>{t.searchParams.has(i)&&(t.searchParams.delete(i),e=!0)}),e&&window.history.replaceState({},document.title,t.toString())}catch(t){console.warn("Failed to clean URL:",t)}}()}setupAutoTracking(){this.autoTrackEnabled=!0,this.setupFormTracking(),this.setupLinkTracking(),this.logger.info("Auto-tracking enabled")}setupFormTracking(){document.addEventListener("submit",e=>{try{const i=e.target;if(!i)return;const r=this.serializeFormFields(i),n=this.extractLeadFields(r);n.email&&(this.storage.setUserTraits(n),this.logger.debug("Lead fields extracted from form:",n)),this.trackEvent(t.FORM_SUBMIT,{...r,_lead_fields:n,form_id:i.id||i.name,form_action:i.action,form_method:i.method})}catch(t){this.logger.error("Failed to auto-track form submit:",t)}})}serializeFormFields(t){const e={};try{const i=new FormData(t);for(const[t,r]of i.entries()){const i=this.serializeFormValue(r);if(Object.prototype.hasOwnProperty.call(e,t)){const r=e[t];Array.isArray(r)?(r.push(i),e[t]=r):e[t]=[r,i]}else e[t]=i}const r=Array.from(t.elements),n=new Map;for(let t=0;t<r.length;t++){const e=r[t];if(e.disabled)continue;const i=e.tagName.toLowerCase(),a=e.type?.toLowerCase();if("button"===i||"submit"===a||"reset"===a)continue;const s=this.getFormFieldKey(e,t);s&&(n.has(s)||n.set(s,[]),n.get(s).push(e))}n.forEach((t,i)=>{const r=t.some(t=>"radio"===t.type),n=t.some(t=>"checkbox"===t.type),a=t.some(t=>"file"===t.type),s=t.some(t=>"SELECT"===t.tagName&&t.multiple),o=t.some(t=>"password"===t.type);if(n){const r=t.filter(t=>"checkbox"===t.type).filter(t=>t.checked).map(t=>t.value||"on");return void(Object.prototype.hasOwnProperty.call(e,i)?Array.isArray(e[i])||(e[i]=[e[i],...r]):e[i]=r)}if(r){const r=t.filter(t=>"radio"===t.type).find(t=>t.checked);return void(Object.prototype.hasOwnProperty.call(e,i)||(e[i]=r?r.value:null))}if(s){const r=t.find(t=>"SELECT"===t.tagName&&t.multiple);if(r){const t=Array.from(r.selectedOptions).map(t=>t.value);return void(Object.prototype.hasOwnProperty.call(e,i)||(e[i]=t))}}if(a){const r=t.find(t=>"file"===t.type);if(r){const t=r.files?Array.from(r.files):[];return void(Object.prototype.hasOwnProperty.call(e,i)||(e[i]=t.map(t=>this.serializeFormValue(t))))}}if(o)Object.prototype.hasOwnProperty.call(e,i)?e[i]="string"==typeof e[i]?"*****":e[i]:e[i]="*****";else if(!Object.prototype.hasOwnProperty.call(e,i)){const r=t[0];if("SELECT"===r.tagName){const t=r;e[i]=t.multiple?Array.from(t.selectedOptions).map(t=>t.value):t.value}else r.type,e[i]=r.value??""}})}catch(t){this.logger.error("Failed to serialize form fields:",t)}return e}getFormFieldKey(t,e){const i=t.name;if(i)return i;if(t.id)return`id_${t.id}`;const r=t.getAttribute("aria-label");if(r&&r.trim())return`aria_${this.normalizeFormFieldKey(r)}`;const n=t instanceof HTMLInputElement||t instanceof HTMLTextAreaElement?t.placeholder:"";return n&&n.trim()?`ph_${this.normalizeFormFieldKey(n)}_${e}`:`field_${e}`}normalizeFormFieldKey(t){return t.trim().toLowerCase().replace(/\s+/g,"_").replace(/[^a-z0-9_]/g,"").slice(0,40)}serializeFormValue(t){return t instanceof File?{file_name:t.name,file_size:t.size,file_type:t.type}:t}extractLeadFields(t){const e={};for(const[i,r]of Object.entries(s))if(!e[i])for(const n of r||[]){const r=n.toLowerCase();for(const[n,a]of Object.entries(t))if((n.toLowerCase()===r||n.toLowerCase().includes(r))&&a&&"string"==typeof a&&a.trim()){e[i]=a.trim();break}if(e[i])break}return e.name||!e.first_name&&!e.last_name||(e.name=[e.first_name,e.last_name].filter(Boolean).join(" ").trim()),e}setupLinkTracking(){document.addEventListener("click",t=>{const e=t.target.closest("a");e&&function(t){try{const e=new URL(t),i=window.location.hostname;return e.hostname!==i}catch(t){return!1}}(e.href)&&this.handleCrossDomainUTM(e,t)})}handleCrossDomainUTM(t,e){if(!this.config.enableCrossDomainUTM)return;const i=t.href;if(function(t,e=[]){try{const i=new URL(t).hostname.toLowerCase();return e.some(t=>{const e=t.toLowerCase();return i===e||i.endsWith(`.${e}`)})}catch(t){return!1}}(i,this.config.excludeDomains))return void this.logger.debug(`Domain excluded from UTM passing: ${i}`);const r=this.getAttributionData();if(!r)return void this.logger.debug("No UTM data available for cross-domain passing");const n=v(r.lastTouch,this.config.crossDomainUTMParams);if(0===Object.keys(n).length)return void this.logger.debug("No UTM parameters to pass");const a=y(i,n);a!==i&&(t.href=a,this.logger.debug("UTM parameters added to external link:",{original:i,enhanced:a,utmParams:n}),this.logger.debug("Cross-domain UTM passed:",{link_url:a,original_url:i,utm_params_passed:n}))}setupNetworkHandlers(){window.addEventListener("online",()=>{this.logger.info("Network connection restored"),this.queue.flush()}),window.addEventListener("offline",()=>{this.logger.warn("Network connection lost")})}setupVisibilityHandlers(){document.addEventListener("visibilitychange",()=>{"hidden"!==document.visibilityState?"visible"===document.visibilityState&&this.updateSessionActivity():this.flushOnUnload("visibilitychange:hidden")})}setupBeforeUnloadHandler(){window.addEventListener("beforeunload",()=>{this.flushOnUnload("beforeunload")}),window.addEventListener("pagehide",()=>{this.flushOnUnload("pagehide")})}flushOnUnload(t){try{this.updateSessionActivity();const e=this.queue.drainAll();if(0===e.length)return;if(this.storage.appendPendingEvents(e),this.logger.debug(`🚚 Unload flush triggered: ${t}`,{count:e.length}),this.httpClient.sendEventsBeacon(e))return void this.storage.removePendingEventsByIds(e.map(t=>t.event_id));const i=20;for(let t=0;t<e.length;t+=i){const r=e.slice(t,t+i);this.httpClient.sendEventsKeepalive(r).then(()=>{this.storage.removePendingEventsByIds(r.map(t=>t.event_id))}).catch(t=>{this.logger.debug("Unload keepalive send failed",t)})}}catch(t){this.logger.debug("flushOnUnload failed",t)}}async retryPendingEvents(){const t=this.storage.getPendingEvents();if(0!==t.length){this.logger.info(`📦 Retrying pending events: ${t.length}`);try{await this.httpClient.sendEvents(t),this.storage.removePendingEventsByIds(t.map(t=>t.event_id)),this.logger.info(`✅ Pending events sent: ${t.length}`)}catch(t){this.logger.warn("⚠️ Pending events retry failed",t)}}}getCurrentPath(){return"undefined"==typeof window?"":window.location.pathname+window.location.search}setupSPATracking(){if("undefined"==typeof window||"undefined"==typeof history)return void this.logger.warn("⚠️ SPA tracking not available in this environment");if(this.spaTrackingEnabled)return void this.logger.warn("⚠️ SPA tracking already enabled");this.spaTrackingEnabled=!0,this.lastTrackedPath=this.getCurrentPath(),this.originalPushState=history.pushState.bind(history),this.originalReplaceState=history.replaceState.bind(history);const t=t=>{const e=this.getCurrentPath();e!==this.lastTrackedPath?(this.logger.debug(`🔄 [SPA] Route change detected (${t}): ${this.lastTrackedPath} -> ${e}`),this.lastTrackedPath=e,setTimeout(()=>{this.trackPageView().then(()=>this.queue.process()).then(()=>{this.logger.debug(`✅ [SPA] Page view tracked for: ${e}`)}).catch(t=>{this.logger.error("❌ [SPA] Failed to track page view:",t)})},100)):this.logger.debug(`🔄 [SPA] Route change detected (${t}) but path unchanged: ${e}`)};history.pushState=(e,i,r)=>{const n=this.originalPushState(e,i,r);return t("pushState"),n},history.replaceState=(e,i,r)=>{const n=this.originalReplaceState(e,i,r);return t("replaceState"),n},this.popstateHandler=()=>{t("popstate")},window.addEventListener("popstate",this.popstateHandler),this.logger.info("🔄 SPA tracking setup completed")}cleanupSPATracking(){this.spaTrackingEnabled&&(this.originalPushState&&(history.pushState=this.originalPushState,this.originalPushState=null),this.originalReplaceState&&(history.replaceState=this.originalReplaceState,this.originalReplaceState=null),this.popstateHandler&&(window.removeEventListener("popstate",this.popstateHandler),this.popstateHandler=null),this.spaTrackingEnabled=!1,this.logger.info("🔄 SPA tracking cleaned up"))}updateSessionActivity(){this.session&&(this.session.lastActivity=Date.now(),this.storage.storeSession(this.session))}async flush(){await this.queue.flush()}getStatus(){return{initialized:this.initialized,session:this.session,queueSize:this.queue.size(),online:navigator.onLine,crossDomainUTM:{enabled:this.config.enableCrossDomainUTM||!0,currentParams:this.getCurrentUTMParams()},spaTracking:{enabled:this.spaTrackingEnabled,currentPath:this.lastTrackedPath}}}destroy(){this.queue.clear(),this.autoTrackEnabled=!1,this.cleanupSPATracking(),this.initialized=!1,this.logger.info("🗑️ SDK destroyed")}}let U=null;async function C(t){if(U)return console.warn("GetuAI SDK: Already initialized"),U;try{if(U=new D(t),await U.init(),t.enableDebug&&(window.GetuAISDK=U),console.log("GetuAI Attribution SDK initialized successfully"),"undefined"!=typeof window){const t=new CustomEvent("getuaiSDKReady",{detail:{sdk:U}});window.dispatchEvent(t)}return U}catch(t){if(console.error("GetuAI SDK: Failed to initialize:",t),"undefined"!=typeof window){const e=new CustomEvent("getuaiSDKError",{detail:{error:t}});window.dispatchEvent(e)}throw t}}function x(){return U}function K(){return new Promise((t,e)=>{if(U)return void t(U);const i=e=>{window.removeEventListener("getuaiSDKReady",i),window.removeEventListener("getuaiSDKError",r),t(e.detail.sdk)},r=t=>{window.removeEventListener("getuaiSDKReady",i),window.removeEventListener("getuaiSDKError",r),e(t.detail.error)};window.addEventListener("getuaiSDKReady",i),window.addEventListener("getuaiSDKError",r),setTimeout(()=>{window.removeEventListener("getuaiSDKReady",i),window.removeEventListener("getuaiSDKError",r),e(new Error("SDK initialization timeout"))},1e4)})}async function N(t,i,r,n,a=e.USD){const s=x();s?await s.trackEvent(t,i,r,n,a):console.warn("GetuAI SDK: Not initialized. Call init() first.")}async function R(t,e){const i=x();i?await i.trackPageView(t,e):console.warn("GetuAI SDK: Not initialized. Call init() first.")}async function O(t,i,r=e.USD,n){const a=x();a?await a.trackPurchase(t,i,r,n):console.warn("GetuAI SDK: Not initialized. Call init() first.")}async function F(t,e){const i=x();i?await i.trackLogin(t,e):console.warn("GetuAI SDK: Not initialized. Call init() first.")}async function L(t,e){const i=x();i?await i.trackSignup(t,e):console.warn("GetuAI SDK: Not initialized. Call init() first.")}async function M(t,e){const i=x();i?await i.trackFormSubmit(t,e):console.warn("GetuAI SDK: Not initialized. Call init() first.")}async function z(t,e){const i=x();i?await i.trackVideoPlay(t,e):console.warn("GetuAI SDK: Not initialized. Call init() first.")}async function V(t,e){const i=x();i?await i.trackEmailVerification(t,e):console.warn("GetuAI SDK: Not initialized. Call init() first.")}async function G(t,e){const i=x();i?await i.trackAuditApproved(t,e):console.warn("GetuAI SDK: Not initialized. Call init() first.")}async function $(t){const e=x();e?await e.trackPurchaseAuto(t):console.warn("GetuAI SDK: Not initialized. Call init() first.")}async function B(t){const e=x();e?await e.trackLoginAuto(t):console.warn("GetuAI SDK: Not initialized. Call init() first.")}async function q(t){const e=x();e?await e.trackSignupAuto(t):console.warn("GetuAI SDK: Not initialized. Call init() first.")}async function j(t){const e=x();e?await e.trackEmailVerificationAuto(t):console.warn("GetuAI SDK: Not initialized. Call init() first.")}async function Y(t,i,r,n,a=e.USD){const s=x();s?await s.trackCustomEvent(t,i,r,n,a):console.warn("GetuAI SDK: Not initialized. Call init() first.")}async function H(t,e){const i=x();i?await i.trackProductView(t,e):console.warn("GetuAI SDK: Not initialized. Call init() first.")}async function J(t,e){const i=x();i?await i.trackAddToCart(t,e):console.warn("GetuAI SDK: Not initialized. Call init() first.")}async function W(t,e){const i=x();i?await i.trackPageClick(t,e):console.warn("GetuAI SDK: Not initialized. Call init() first.")}async function Q(t,e,i){const r=x();r?await r.trackButtonClick(t,e,i):console.warn("GetuAI SDK: Not initialized. Call init() first.")}function X(){const t=x();return t?t.getAttributionData():null}async function Z(){const t=x();t?await t.flush():console.warn("GetuAI SDK: Not initialized. Call init() first.")}function tt(){const t=x();return t?t.getStatus():null}function et(t){const e=x();return e?e.addUTMToURL(t):(console.warn("GetuAI SDK: Not initialized. Call init() first."),t)}function it(){const t=x();return t?t.getCurrentUTMParams():{}}function rt(t){const e=x();e?e.setUserId(t):console.warn("GetuAI SDK: Not initialized. Call init() first.")}function nt(){const t=x();return t?t.getUserId():(console.warn("GetuAI SDK: Not initialized. Call init() first."),null)}function at(){const t=x();t?t.removeUserId():console.warn("GetuAI SDK: Not initialized. Call init() first.")}function st(){U&&(U.destroy(),U=null,console.log("GetuAI SDK destroyed"))}class ot extends D{static async init(t){return await C(t)}static async trackEvent(t,i,r,n,a=e.USD){return await N(t,i,r,n,a)}static async trackPageView(t,e){return await R(t,e)}static async trackPurchase(t,i,r=e.USD,n){return await O(t,i,r,n)}static async trackLogin(t,e){return await F(t,e)}static async trackSignup(t,e){return await L(t,e)}static async trackFormSubmit(t,e){return await M(t,e)}static async trackVideoPlay(t,e){return await z(t,e)}static async trackEmailVerification(t,e){return await V(t,e)}static async trackPurchaseAuto(t){return await $(t)}static async trackLoginAuto(t){return await B(t)}static async trackSignupAuto(t){return await q(t)}static async trackEmailVerificationAuto(t){return await j(t)}static async trackCustomEvent(t,i,r,n,a=e.USD){return await Y(t,i,r,n,a)}static async trackProductView(t,e){return await H(t,e)}static async trackAddToCart(t,e){return await J(t,e)}static async trackPageClick(t,e){return await W(t,e)}static async trackButtonClick(t,e,i){return await Q(t,e,i)}static getAttributionData(){return X()}static async flush(){return await Z()}static getStatus(){return tt()}static addUTMToURL(t){return et(t)}static getCurrentUTMParams(){return it()}static setUserId(t){rt(t)}static getUserId(){return nt()}static removeUserId(){at()}static destroy(){st()}static getSDK(){return x()}static waitForSDK(){return K()}}"undefined"!=typeof document&&function(){if(U)return;const t=document.currentScript;if(!t)return void console.warn("GetuAI SDK: Could not find script tag for auto-initialization");const e=t.getAttribute("data-api-key");e?C({apiKey:e,apiEndpoint:t.getAttribute("data-api-endpoint")||n,enableDebug:"true"===t.getAttribute("data-debug"),autoTrack:"true"===t.getAttribute("data-auto-track"),autoTrackPageView:"true"===t.getAttribute("data-auto-track-page-view"),autoCleanUTM:"false"!==t.getAttribute("data-auto-clean-utm"),batchSize:parseInt(t.getAttribute("data-batch-size")||"100"),batchInterval:parseInt(t.getAttribute("data-batch-interval")||"2000"),userId:t.getAttribute("data-user-id")||void 0}):console.warn("GetuAI SDK: No API key provided. Please add data-api-key attribute to script tag.")}(),"undefined"!=typeof window&&(window.getuaiSDK={init:C,getSDK:x,waitForSDK:K,trackEvent:N,trackPageView:R,trackPageClick:W,trackButtonClick:Q,trackPurchase:O,trackLogin:F,trackSignup:L,trackFormSubmit:M,trackVideoPlay:z,trackEmailVerification:V,trackAuditApproved:G,trackCustomEvent:Y,trackProductView:H,trackAddToCart:J,trackPurchaseAuto:$,trackLoginAuto:B,trackSignupAuto:q,trackEmailVerificationAuto:j,getAttributionData:X,flush:Z,getStatus:tt,addUTMToURL:et,getCurrentUTMParams:it,setUserId:rt,getUserId:nt,removeUserId:at,destroy:st,EventType:t,Currency:e,AttributionSDK:ot},window.init=C,window.waitForSDK=K,window.trackEvent=N,window.trackPageView=R,window.trackPageClick=W,window.trackButtonClick=Q,window.trackPurchase=O,window.trackLogin=F,window.trackSignup=L,window.trackFormSubmit=M,window.trackVideoPlay=z,window.trackEmailVerification=V,window.trackProductView=H,window.trackAddToCart=J,window.trackPurchaseAuto=$,window.trackLoginAuto=B,window.trackSignupAuto=q,window.trackEmailVerificationAuto=j,window.getAttributionData=X,window.flush=Z,window.getStatus=tt,window.addUTMToURL=et,window.getCurrentUTMParams=it,window.setUserId=rt,window.getUserId=nt,window.removeUserId=at,window.destroy=st,window.AttributionSDK=ot);const ct={init:C,getSDK:x,waitForSDK:K,trackEvent:N,trackPageView:R,trackPageClick:W,trackButtonClick:Q,trackPurchase:O,trackLogin:F,trackSignup:L,trackFormSubmit:M,trackVideoPlay:z,trackEmailVerification:V,trackAuditApproved:G,trackCustomEvent:Y,trackProductView:H,trackAddToCart:J,trackPurchaseAuto:$,trackLoginAuto:B,trackSignupAuto:q,trackEmailVerificationAuto:j,getAttributionData:X,flush:Z,getStatus:tt,addUTMToURL:et,getCurrentUTMParams:it,setUserId:rt,getUserId:nt,removeUserId:at,destroy:st,EventType:t,Currency:e,AttributionSDK:ot};return r.default})());
|
|
1
|
+
!function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.GetuAIAttribution=e():t.GetuAIAttribution=e()}(this,()=>(()=>{"use strict";var t,e,i={d:(t,e)=>{for(var r in e)i.o(e,r)&&!i.o(t,r)&&Object.defineProperty(t,r,{enumerable:!0,get:e[r]})},o:(t,e)=>Object.prototype.hasOwnProperty.call(t,e)},r={};i.d(r,{default:()=>dt}),function(t){t.PAGE_VIEW="page_view",t.PAGE_CLICK="page_click",t.BUTTON_CLICK="button_click",t.VIDEO_PLAY="video_play",t.FORM_SUBMIT="form_submit",t.EMAIL_VERIFICATION="email_verification",t.LOGIN="login",t.SIGNUP="signup",t.PRODUCT_VIEW="product_view",t.ADD_TO_CART="add_to_cart",t.PURCHASE="purchase",t.AUDIT_APPROVED="audit_approved",t.CUSTOM="custom"}(t||(t={})),function(t){t.USD="USD"}(e||(e={}));const n="https://attribution.getu.ai/attribution/api",s=[t.PURCHASE,t.LOGIN,t.SIGNUP,t.FORM_SUBMIT,t.EMAIL_VERIFICATION,t.AUDIT_APPROVED],o={email:["email","e-mail","mail","user_email","email_address"],name:["name","full_name","fullname","username","display_name"],first_name:["first_name","firstname","fname","given_name"],last_name:["last_name","lastname","lname","surname","family_name"],phone:["phone","telephone","mobile","phone_number","tel"],company_name:["company","company_name","organization","org","business"],title:["title","job_title","position","role","job"]};function a(){return Math.floor(Date.now()/1e3)}const c=["adgroup_id","ad_group_id","adgroupid","adset_id","tt_adgroup_id","adgroup"],u=["ad_id","adid","creative_id","creative","tt_ad_id"],l=["gclid","gbraid","wbraid","fbclid","ttclid","msclkid","twclid","li_fat_id"],d=/^\d{7,15}$/;function h(t,e){const i=e.toLowerCase();for(const e of Object.keys(t))if(e.toLowerCase()===i){const i=t[e];for(const t of i)if(t&&""!==t.trim())return t.trim()}}function g(){const t="undefined"!=typeof navigator&&navigator.userAgent||"";return/iPad|Tablet|PlayBook|Silk/i.test(t)?"tablet":/Mobi|Android|iPhone|iPod/i.test(t)?"mobile":"desktop"}function m(){try{const t="__localStorage_test__";return localStorage.setItem(t,t),localStorage.removeItem(t),!0}catch{return!1}}class p{constructor(t=!0){this.enabled=t}debug(t,...e){this.enabled&&console.debug&&console.debug(`[GetuAI Debug] ${t}`,...e)}info(t,...e){this.enabled&&console.info&&console.info(`[GetuAI Info] ${t}`,...e)}warn(t,...e){this.enabled&&console.warn&&console.warn(`[GetuAI Warn] ${t}`,...e)}error(t,...e){this.enabled&&console.error&&console.error(`[GetuAI Error] ${t}`,...e)}}function f(){return document.referrer||""}function _(){return window.location.href}function y(){return document.title||""}function w(){return`session_${Date.now()}_${Math.random().toString(36).substr(2,9)}`}function S(t,e){try{const i=new URL(t);return Object.entries(e).forEach(([t,e])=>{e&&!i.searchParams.has(t)&&i.searchParams.set(t,e)}),i.toString()}catch(e){return t}}function k(t,e=["utm_source","utm_medium","utm_campaign"]){const i={};return e.forEach(e=>{t[e]&&(i[e]=t[e])}),i}function E(t){try{const e=t||window.location.href;return new URL(e).search}catch(t){return""}}function v(t){try{const e=t||window.location.href,i=new URL(e),r={};return i.searchParams.forEach((t,e)=>{r[e]=t}),r}catch(t){return{}}}class b{constructor(t){this.logger=t}get(t){try{if(!m())return this.logger.warn("LocalStorage not supported"),null;const e=localStorage.getItem(t);return null===e?null:JSON.parse(e)}catch(t){return this.logger.error("Error reading from localStorage:",t),null}}set(t,e){try{if(!m())return void this.logger.warn("LocalStorage not supported");localStorage.setItem(t,JSON.stringify(e))}catch(t){this.logger.error("Error writing to localStorage:",t),this.handleQuotaExceeded()}}remove(t){try{m()&&localStorage.removeItem(t)}catch(t){this.logger.error("Error removing from localStorage:",t)}}clear(){try{m()&&localStorage.clear()}catch(t){this.logger.error("Error clearing localStorage:",t)}}handleQuotaExceeded(){try{const t=Object.keys(localStorage).filter(t=>t.startsWith("attribution_"));if(t.length>0){t.sort((t,e)=>{const i=this.get(t),r=this.get(e);return(i?.expiresAt||0)-(r?.expiresAt||0)});const e=Math.ceil(.2*t.length);t.slice(0,e).forEach(t=>{this.remove(t)}),this.logger.info(`Cleaned up ${e} old attribution records`)}}catch(t){this.logger.error("Error during quota cleanup:",t)}}}class I{constructor(t){this.dbName="attribution_events",this.dbVersion=1,this.storeName="events",this.db=null,this.logger=t}async init(){if("indexedDB"in window)return new Promise((t,e)=>{const i=indexedDB.open(this.dbName,this.dbVersion);i.onerror=()=>{this.logger.error("Failed to open IndexedDB:",i.error),e(i.error)},i.onsuccess=()=>{this.db=i.result,this.logger.info("IndexedDB initialized successfully"),t()},i.onupgradeneeded=t=>{const e=t.target.result;if(!e.objectStoreNames.contains(this.storeName)){const t=e.createObjectStore(this.storeName,{keyPath:"id",autoIncrement:!0});t.createIndex("timestamp","timestamp",{unique:!1}),t.createIndex("sent","sent",{unique:!1}),t.createIndex("queued_at","queued_at",{unique:!1})}}});this.logger.warn("IndexedDB not supported")}async addEvent(t){if(!this.db)throw new Error("IndexedDB not initialized");return new Promise((e,i)=>{const r=this.db.transaction([this.storeName],"readwrite").objectStore(this.storeName),n={...t,queued_at:Date.now(),sent:!1},s=r.add(n);s.onsuccess=()=>{this.logger.debug("Event added to IndexedDB queue"),e()},s.onerror=()=>{this.logger.error("Failed to add event to IndexedDB:",s.error),i(s.error)}})}async getUnsentEvents(t=100){return this.db?new Promise((e,i)=>{const r=this.db.transaction([this.storeName],"readonly").objectStore(this.storeName).index("sent").getAll(IDBKeyRange.only(!1),t);r.onsuccess=()=>{const t=r.result.map(t=>{const{queued_at:e,sent:i,...r}=t;return r});e(t)},r.onerror=()=>{this.logger.error("Failed to get unsent events:",r.error),i(r.error)}}):[]}async markEventsAsSent(t){if(this.db&&0!==t.length)return new Promise((e,i)=>{const r=this.db.transaction([this.storeName],"readwrite").objectStore(this.storeName);let n=0,s=!1;t.forEach(o=>{const a=r.get(o);a.onsuccess=()=>{if(a.result){const o={...a.result,sent:!0},c=r.put(o);c.onsuccess=()=>{n++,n!==t.length||s||e()},c.onerror=()=>{s=!0,this.logger.error("Failed to mark event as sent:",c.error),i(c.error)}}else n++,n!==t.length||s||e()},a.onerror=()=>{s=!0,this.logger.error("Failed to get event for marking as sent:",a.error),i(a.error)}})})}async cleanupOldEvents(t=6048e5){if(!this.db)return;const e=Date.now()-t;return new Promise((t,i)=>{const r=this.db.transaction([this.storeName],"readwrite").objectStore(this.storeName).index("queued_at").openCursor(IDBKeyRange.upperBound(e));r.onsuccess=()=>{const e=r.result;e?(e.delete(),e.continue()):(this.logger.info("Old events cleanup completed"),t())},r.onerror=()=>{this.logger.error("Failed to cleanup old events:",r.error),i(r.error)}})}async getQueueSize(){return this.db?new Promise((t,e)=>{const i=this.db.transaction([this.storeName],"readonly").objectStore(this.storeName).count();i.onsuccess=()=>{t(i.result)},i.onerror=()=>{this.logger.error("Failed to get queue size:",i.error),e(i.error)}}):0}async clear(){if(this.db)return new Promise((t,e)=>{const i=this.db.transaction([this.storeName],"readwrite").objectStore(this.storeName).clear();i.onsuccess=()=>{this.logger.info("IndexedDB queue cleared"),t()},i.onerror=()=>{this.logger.error("Failed to clear IndexedDB queue:",i.error),e(i.error)}})}}class T{constructor(t){this.UTM_STORAGE_KEY="attribution_utm_data",this.SESSION_STORAGE_KEY="attribution_session",this.SESSION_COOKIE_KEY="_getuai_session",this.ATTRIB_COOKIE_KEY="_getuai_attrib",this.USER_ID_KEY="getuai_user_id",this.PENDING_EVENTS_KEY="getuai_pending_events_v1",this.USER_TRAITS_KEY="getuai_user_traits",this.USER_ID_COOKIE_EXPIRES=365,this.ATTRIB_COOKIE_EXPIRES=30,this.SESSION_COOKIE_EXPIRES=1,this.cookieDomain=null,this.logger=t,this.localStorage=new b(t),this.indexedDB=new I(t)}setCookieDomain(t){this.cookieDomain=t,this.logger.debug(t?`🍪 Cookie domain set to ${t} (cross-subdomain sharing enabled)`:"🍪 Cookie domain not set — using host-only cookies")}setUserId(t){if(!t||""===t.trim())return void this.logger.warn("Cannot set empty user ID");const e=t.trim();try{this.setCookie(this.USER_ID_KEY,e,{days:this.USER_ID_COOKIE_EXPIRES,domain:this.cookieDomain}),this.logger.debug(`👤 User ID stored in cookie: ${e}`)}catch(t){this.logger.error("Failed to store user ID in cookie:",t)}try{"undefined"!=typeof localStorage&&(localStorage.setItem(this.USER_ID_KEY,e),this.logger.debug(`👤 User ID stored in localStorage: ${e}`))}catch(t){this.logger.error("Failed to store user ID in localStorage:",t)}}getUserId(){let t=null;try{t=this.getCookie(this.USER_ID_KEY)}catch(t){this.logger.debug("Failed to get user ID from cookie:",t)}if(t){try{"undefined"!=typeof localStorage&&localStorage.getItem(this.USER_ID_KEY)!==t&&localStorage.setItem(this.USER_ID_KEY,t)}catch(t){}return t}try{if("undefined"!=typeof localStorage){const t=localStorage.getItem(this.USER_ID_KEY);if(t)return t}}catch(t){this.logger.debug("Failed to get user ID from localStorage:",t)}return null}removeUserId(){try{this.deleteCookie(this.USER_ID_KEY),this.logger.debug("👤 User ID removed from cookie")}catch(t){this.logger.error("Failed to remove user ID from cookie:",t)}try{"undefined"!=typeof localStorage&&(localStorage.removeItem(this.USER_ID_KEY),this.logger.debug("👤 User ID removed from localStorage"))}catch(t){this.logger.error("Failed to remove user ID from localStorage:",t)}}setCookie(t,e,i={}){try{const r="number"==typeof i?{days:i}:i,n=r.days??this.USER_ID_COOKIE_EXPIRES,s=new Date;s.setTime(s.getTime()+24*n*60*60*1e3);const o=r.secure??("undefined"!=typeof location&&"https:"===location.protocol),a=r.domain??this.cookieDomain,c=[`${t}=${encodeURIComponent(e)}`,`expires=${s.toUTCString()}`,"path=/","SameSite=Lax"];return a&&c.push(`domain=${a}`),o&&c.push("Secure"),document.cookie=c.join(";"),!0}catch(t){return this.logger.error("Failed to set cookie:",t),!1}}getCookie(t){try{const e=t+"=",i=document.cookie.split(";");for(let t=0;t<i.length;t++){let r=i[t];for(;" "===r.charAt(0);)r=r.substring(1,r.length);if(0===r.indexOf(e))return decodeURIComponent(r.substring(e.length,r.length))}return null}catch(t){return this.logger.error("Failed to get cookie:",t),null}}deleteCookie(t){try{document.cookie=`${t}=;expires=Thu, 01 Jan 1970 00:00:00 UTC;path=/;`,this.cookieDomain&&(document.cookie=`${t}=;expires=Thu, 01 Jan 1970 00:00:00 UTC;path=/;domain=${this.cookieDomain};`)}catch(t){this.logger.error("Failed to delete cookie:",t)}}async init(){await this.indexedDB.init()}getPendingEvents(){try{const t=this.localStorage.get(this.PENDING_EVENTS_KEY);return Array.isArray(t)?t:[]}catch(t){return this.logger.debug("Failed to read pending events:",t),[]}}appendPendingEvents(t){if(t&&0!==t.length)try{const e=[...this.getPendingEvents(),...t].slice(-500);this.localStorage.set(this.PENDING_EVENTS_KEY,e)}catch(t){this.logger.debug("Failed to append pending events:",t)}}removePendingEventsByIds(t){if(t&&0!==t.length)try{const e=this.getPendingEvents();if(0===e.length)return;const i=new Set(t),r=e.filter(t=>!i.has(t.event_id));0===r.length?this.localStorage.remove(this.PENDING_EVENTS_KEY):this.localStorage.set(this.PENDING_EVENTS_KEY,r)}catch(t){this.logger.debug("Failed to remove pending events:",t)}}clearPendingEvents(){try{this.localStorage.remove(this.PENDING_EVENTS_KEY)}catch(t){this.logger.debug("Failed to clear pending events:",t)}}storeUTMData(t){try{const e=this.getUTMData(),i={utm_source:"",utm_medium:"",utm_campaign:"",utm_term:"",utm_content:"",timestamp:Date.now()},r={firstTouch:e?.firstTouch||i,lastTouch:e?.lastTouch||i,touchpoints:e?.touchpoints||[],...t,expiresAt:Date.now()+2592e6};this.writeAttribution(r),this.logger.debug("UTM data stored successfully:",{firstTouch:r.firstTouch,lastTouch:r.lastTouch,touchpointsCount:r.touchpoints.length,expiresAt:new Date(r.expiresAt).toISOString()})}catch(t){this.logger.error("Failed to store UTM data:",t)}}getUTMData(){const t=this.readAttributionFromCookie();if(t&&t.expiresAt&&t.expiresAt>Date.now())return t;t&&this.deleteCookie(this.ATTRIB_COOKIE_KEY);const e=this.localStorage.get(this.UTM_STORAGE_KEY);return e&&e.expiresAt&&e.expiresAt>Date.now()?e:(e&&this.localStorage.remove(this.UTM_STORAGE_KEY),null)}storeSession(t){try{const e=JSON.stringify(t);this.setCookie(this.SESSION_COOKIE_KEY,e,{days:this.SESSION_COOKIE_EXPIRES,domain:this.cookieDomain})}catch(t){this.logger.debug("Failed to store session in cookie:",t)}this.localStorage.set(this.SESSION_STORAGE_KEY,t)}getSession(){try{const t=this.getCookie(this.SESSION_COOKIE_KEY);if(t)return JSON.parse(t)}catch(t){this.logger.debug("Failed to parse session cookie:",t)}return this.localStorage.get(this.SESSION_STORAGE_KEY)}migrateLegacyStorage(){try{if(!this.getCookie(this.SESSION_COOKIE_KEY)){const t=this.localStorage.get(this.SESSION_STORAGE_KEY);t&&(this.setCookie(this.SESSION_COOKIE_KEY,JSON.stringify(t),{days:this.SESSION_COOKIE_EXPIRES,domain:this.cookieDomain}),this.logger.debug("↗️ Migrated legacy session to cookie"))}}catch(t){this.logger.debug("Session migration skipped:",t)}try{if(!this.getCookie(this.ATTRIB_COOKIE_KEY)){const t=this.localStorage.get(this.UTM_STORAGE_KEY);t&&t.expiresAt&&t.expiresAt>Date.now()&&(this.writeAttribution(t),this.logger.debug("↗️ Migrated legacy attribution data to cookie"))}}catch(t){this.logger.debug("Attribution migration skipped:",t)}try{if(this.cookieDomain){const t=this.getCookie(this.USER_ID_KEY),e="undefined"!=typeof localStorage?localStorage.getItem(this.USER_ID_KEY):null;!t&&e&&(this.setCookie(this.USER_ID_KEY,e,{days:this.USER_ID_COOKIE_EXPIRES,domain:this.cookieDomain}),this.logger.debug("↗️ Migrated legacy user ID to cross-subdomain cookie"))}}catch(t){this.logger.debug("User ID migration skipped:",t)}}writeAttribution(t){this.localStorage.set(this.UTM_STORAGE_KEY,t);const e=Math.max(0,t.expiresAt-Date.now()),i=Math.max(1,Math.ceil(e/864e5));let r=encodeURIComponent(JSON.stringify(t));if(r.length>3800){const e={...t,touchpoints:(t.touchpoints||[]).slice(-20)};if(r=encodeURIComponent(JSON.stringify(e)),r.length>3800){const t={...e,touchpoints:[]};r=encodeURIComponent(JSON.stringify(t))}return this.logger.debug("Attribution cookie payload trimmed to fit size limit"),void this.setCookie(this.ATTRIB_COOKIE_KEY,decodeURIComponent(r),{days:i,domain:this.cookieDomain})}this.setCookie(this.ATTRIB_COOKIE_KEY,JSON.stringify(t),{days:i,domain:this.cookieDomain})}readAttributionFromCookie(){try{const t=this.getCookie(this.ATTRIB_COOKIE_KEY);return t?JSON.parse(t):null}catch(t){return this.logger.debug("Failed to parse attribution cookie:",t),null}}async queueEvent(t){try{await this.indexedDB.addEvent(t)}catch(t){throw this.logger.error("Failed to queue event:",t),t}}async getUnsentEvents(t=100){return await this.indexedDB.getUnsentEvents(t)}async markEventsAsSent(t){await this.indexedDB.markEventsAsSent(t)}async getQueueSize(){return await this.indexedDB.getQueueSize()}async cleanupOldEvents(){await this.indexedDB.cleanupOldEvents()}async clearQueue(){await this.indexedDB.clear()}cleanupExpiredData(){this.getUTMData()}setUserTraits(t){try{const e={...this.getUserTraits()||{},...t};"undefined"!=typeof localStorage&&(localStorage.setItem(this.USER_TRAITS_KEY,JSON.stringify(e)),this.logger.debug("User traits stored:",e))}catch(t){this.logger.error("Failed to store user traits:",t)}}getUserTraits(){try{if("undefined"!=typeof localStorage){const t=localStorage.getItem(this.USER_TRAITS_KEY);return t?JSON.parse(t):null}return null}catch(t){return this.logger.error("Failed to get user traits:",t),null}}clearUserTraits(){try{"undefined"!=typeof localStorage&&(localStorage.removeItem(this.USER_TRAITS_KEY),this.logger.debug("User traits cleared"))}catch(t){this.logger.error("Failed to clear user traits:",t)}}}const A="0.4.1";class D{constructor(t,e,i,r=100,n=2e3,s=3,o=1e3,a){this.queue=[],this.processing=!1,this.batchTimer=null,this.retryCounts=new Map,this.logger=t,this.apiKey=e,this.apiEndpoint=i,this.batchSize=r,this.batchInterval=n,this.maxRetries=s,this.retryDelay=o,this.sendEvents=a,this.debouncedProcess=function(t){let e;return(...i)=>{clearTimeout(e),e=setTimeout(()=>t(...i),100)}}(this.process.bind(this))}add(t){if(s.includes(t.event_type))return this.logger.debug(`Immediate event detected: ${t.event_type}, sending immediately`),void this.processImmediate(t);this.queue.push(t),this.logger.debug(`Event added to queue: ${t.event_type}`),this.scheduleBatchProcessing()}async process(){if(this.processing||0===this.queue.length)return;this.processing=!0;const t=this.queue.splice(0,this.batchSize);try{this.logger.debug(`Processing ${t.length} events from queue`),await this.sendEvents(t),this.resetRetryCounts(t),this.logger.info(`Successfully processed ${t.length} events`)}catch(e){return this.logger.error("Failed to process events:",e),this.enqueueBatchForRetry(t),void setTimeout(()=>{this.processing=!1,this.debouncedProcess()},this.retryDelay)}this.processing=!1,this.queue.length>0&&this.debouncedProcess()}async processImmediate(t){try{this.logger.debug(`Processing immediate event: ${t.event_type}`),await this.sendEvents([t]),this.resetRetryCounts([t]),this.logger.info(`Immediate event processed successfully: ${t.event_type}`)}catch(e){this.logger.error(`Failed to process immediate event: ${t.event_type}`,e),this.enqueueBatchForRetry([t])}}scheduleBatchProcessing(){this.batchTimer&&clearTimeout(this.batchTimer),this.batchTimer=setTimeout(()=>{this.debouncedProcess()},this.batchInterval)}clear(){this.queue=[],this.batchTimer&&(clearTimeout(this.batchTimer),this.batchTimer=null),this.processing=!1,this.logger.info("Event queue cleared")}size(){return this.queue.length}getStats(){return{size:this.queue.length,processing:this.processing}}async flush(){for(this.logger.info("Flushing event queue");this.queue.length>0;)await this.process()}drainAll(){const t=this.queue.splice(0,this.queue.length);return this.batchTimer&&(clearTimeout(this.batchTimer),this.batchTimer=null),this.processing=!1,t}enqueueBatchForRetry(t){const e=[];for(const i of t){const t=(this.retryCounts.get(i.event_id)||0)+1;this.retryCounts.set(i.event_id,t),t>this.maxRetries?(this.logger.warn(`Dropping event after max retries (${this.maxRetries}): ${i.event_type} (${i.event_id})`),this.retryCounts.delete(i.event_id)):e.push(i)}e.length>0&&this.queue.unshift(...e)}resetRetryCounts(t){for(const e of t)this.retryCounts.delete(e.event_id)}}class P{constructor(t,e,i,r=3,n=1e3){this.logger=t,this.apiKey=e,this.apiEndpoint=i,this.maxRetries=r,this.retryDelay=n}async sendEvents(t){if(0===t.length)return;const e={events:t.map(t=>({event_id:t.event_id,event_type:t.event_type,tracking_user_id:t.tracking_user_id,utm_source:t.utm_source||null,utm_medium:t.utm_medium||null,utm_campaign:t.utm_campaign||null,utm_term:t.utm_term||null,utm_content:t.utm_content||null,revenue:t.revenue||null,currency:t.currency||null,event_data:t.event_data||null,context:t.context||null,timestamp:t.timestamp||a(),custom_event_name:t.custom_event_name||void 0})),sdk_version:A};await async function(t,e=3,i=1e3){let r;for(let n=0;n<=e;n++)try{return await t()}catch(t){if(r=t,n===e)throw r;const s=i*Math.pow(2,n);await new Promise(t=>setTimeout(t,s))}throw r}(async()=>{const i=await fetch(`${this.apiEndpoint}/attribution/events`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${this.apiKey}`},body:JSON.stringify(e)});if(!i.ok){const t=await i.text();throw new Error(`HTTP ${i.status}: ${t}`)}const r=await i.json();return this.logger.debug("Events sent successfully:",r),{result:r,sentEvents:t}},this.maxRetries,this.retryDelay)}async sendEventsKeepalive(t){if(0===t.length)return;const e={events:t.map(t=>({event_id:t.event_id,event_type:t.event_type,tracking_user_id:t.tracking_user_id,utm_source:t.utm_source||null,utm_medium:t.utm_medium||null,utm_campaign:t.utm_campaign||null,utm_term:t.utm_term||null,utm_content:t.utm_content||null,revenue:t.revenue||null,currency:t.currency||null,event_data:t.event_data||null,context:t.context||null,timestamp:t.timestamp||a(),custom_event_name:t.custom_event_name||void 0})),sdk_version:A},i=await fetch(`${this.apiEndpoint}/attribution/events`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${this.apiKey}`},body:JSON.stringify(e),keepalive:!0});if(!i.ok)throw new Error(`HTTP ${i.status}`)}sendEventsBeacon(t){if(0===t.length)return!0;if("undefined"==typeof navigator||"function"!=typeof navigator.sendBeacon)return!1;const e={events:t.map(t=>({event_id:t.event_id,event_type:t.event_type,tracking_user_id:t.tracking_user_id,utm_source:t.utm_source||null,utm_medium:t.utm_medium||null,utm_campaign:t.utm_campaign||null,utm_term:t.utm_term||null,utm_content:t.utm_content||null,revenue:t.revenue||null,currency:t.currency||null,event_data:t.event_data||null,context:t.context||null,timestamp:t.timestamp||a(),custom_event_name:t.custom_event_name||void 0})),sdk_version:A},i=`${this.apiEndpoint}/attribution/events_beacon?api_key=${encodeURIComponent(this.apiKey)}`,r=new Blob([JSON.stringify(e)],{type:"text/plain"});return navigator.sendBeacon(i,r)}async sendSingleEvent(t){await this.sendEvents([t])}async testConnection(){try{return(await fetch(`${this.apiEndpoint}/health`,{method:"GET",headers:{Authorization:`Bearer ${this.apiKey}`}})).ok}catch(t){return this.logger.error("Connection test failed:",t),!1}}}const U="__getuai_probe";function C(t){try{document.cookie=`${U}=1;domain=${t};path=/;SameSite=Lax`;const e=document.cookie.split(";").some(t=>t.trim().startsWith(`${U}=`));return e&&(document.cookie=`${U}=;domain=${t};path=/;expires=Thu, 01 Jan 1970 00:00:00 GMT`),e}catch{return!1}}class O{constructor(t){this.session=null,this.initialized=!1,this.autoTrackEnabled=!1,this.pageViewTrackTimes=new Map,this.cachedPublicIP=null,this.publicIPFetchPromise=null,this.initialPageViewTriggered=!1,this.spaTrackingEnabled=!1,this.lastTrackedPath="",this.originalPushState=null,this.originalReplaceState=null,this.popstateHandler=null,this.config={apiEndpoint:n,batchSize:100,batchInterval:2e3,maxRetries:3,retryDelay:1e3,enableDebug:!1,autoTrack:!1,autoTrackPageView:!1,sessionTimeout:18e5,enableCrossDomainUTM:!0,crossDomainUTMParams:["utm_source","utm_medium","utm_campaign","utm_term","utm_content"],excludeDomains:[],autoCleanUTM:!0,pageViewDebounceInterval:5e3,...t},this.logger=new p(this.config.enableDebug),this.storage=new T(this.logger),this.httpClient=new P(this.logger,this.config.apiKey,this.config.apiEndpoint||n,this.config.maxRetries,this.config.retryDelay),this.queue=new D(this.logger,this.config.apiKey,this.config.apiEndpoint||n,this.config.batchSize,this.config.batchInterval,this.config.maxRetries,this.config.retryDelay,t=>this.httpClient.sendEvents(t))}async init(){if(this.initialized)this.logger.warn("SDK already initialized");else try{this.logger.info("Initializing GetuAI Attribution SDK");const t=function(t){if(!t)return null;const e=t.trim();return e?e.startsWith(".")?e:"."+e:null}(this.config.domain)??function(){if(!function(){try{return"undefined"!=typeof document&&"string"==typeof document.cookie}catch{return!1}}())return null;const t="undefined"!=typeof location&&location.hostname||"";if(!t)return null;if("localhost"===t)return null;if(function(t){return!!/^\d+\.\d+\.\d+\.\d+$/.test(t)||!!t.includes(":")}(t))return null;const e=t.split(".").filter(Boolean);if(e.length<2)return null;for(let t=e.length-2;t>=0;t--){const i="."+e.slice(t).join(".");if(C(i))return i}return null}();this.storage.setCookieDomain(t),this.storage.init().catch(t=>{this.logger.warn("Storage initialization failed (IndexedDB may be unavailable), continuing with basic features:",t)});try{this.storage.migrateLegacyStorage()}catch(t){this.logger.debug("Legacy storage migration skipped:",t)}this.initializeUserId(),this.initializeSession(),this.extractAndStoreUTMData(),this.config.autoTrack&&this.setupAutoTracking(),this.setupNetworkHandlers(),this.setupVisibilityHandlers(),this.setupBeforeUnloadHandler(),this.initialized=!0,this.logger.info("🚀 GetuAI Attribution SDK initialized successfully"),this.logger.info("📄 Auto track page view = "+this.config.autoTrackPageView),this.retryPendingEvents().catch(t=>{this.logger.warn("⚠️ Pending events retry failed",t)}),this.config.autoTrackPageView&&(this.logger.info("📄 Auto track page view enabled (including SPA route tracking)"),this.lastTrackedPath=this.getCurrentPath(),this.setupSPATracking(),this.initialPageViewTriggered||(this.initialPageViewTriggered=!0,Promise.resolve().then(()=>this.trackPageView()).then(()=>this.queue.process()).then(()=>{this.logger.info("✅ Auto track page view completed")}).catch(t=>this.logger.error("❌ Auto track page view failed:",t))))}catch(t){throw this.logger.error("Failed to initialize SDK:",t),t}}async trackEvent(i,r,n,s,o=e.USD,c){if(this.initialized)try{const e=_(),u=this.cachedPublicIP;this.ensurePublicIPFetched();const l={domain:"undefined"!=typeof window?window.location.hostname:null,path:"undefined"!=typeof window?window.location.pathname:null,title:y(),referrer:f(),url:e.split("?")[0],querystring:E(e),query_params:v(e),ip_address:u},d={session_id:this.session?.sessionId,start_time:this.session?.startTime,last_activity:this.session?.lastActivity,page_views:this.session?.pageViews},h=n||this.getUserId(),m={event_id:"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,function(t){const e=16*Math.random()|0;return("x"===t?e:3&e|8).toString(16)}),event_type:i,tracking_user_id:h||void 0,timestamp:a(),event_data:r,context:{page:l,session:d},revenue:s,currency:o,...i===t.CUSTOM&&c?{custom_event_name:c}:{},...this.getUTMParams(),device:g(),...this.getAdParams()};this.logger.debug(`Tracking event: ${i}`,m),this.queue.add(m)}catch(t){this.logger.error(`Failed to track event ${i}:`,t)}else this.logger.warn("SDK not initialized, event not tracked")}async trackCustomEvent(i,r,n,s,o=e.USD){i&&""!==i.trim()?await this.trackEvent(t.CUSTOM,r,n,s,o,i.trim()):this.logger.error("trackCustomEvent requires a non-empty customEventName")}async trackPageView(e,i){const r=_(),n=r.split("?")[0],s=Date.now(),o=this.config.pageViewDebounceInterval||5e3,a=this.pageViewTrackTimes.get(n);if(a&&s-a<o)return void this.logger.debug(`Page view debounced: ${n} (last tracked ${s-a}ms ago)`);const c={url:r,title:y(),referrer:f(),user_agent:navigator.userAgent||"",...e};await this.trackEvent(t.PAGE_VIEW,c,i),this.pageViewTrackTimes.set(n,s),this.cleanupPageViewTrackTimes()}cleanupPageViewTrackTimes(){const t=Date.now()-36e5;for(const[e,i]of this.pageViewTrackTimes.entries())i<t&&this.pageViewTrackTimes.delete(e)}ensurePublicIPFetched(){this.cachedPublicIP||this.publicIPFetchPromise||(this.publicIPFetchPromise=this.fetchPublicIP().then(t=>(t&&(this.cachedPublicIP=t),t)).catch(()=>null).finally(()=>{this.publicIPFetchPromise=null}))}async fetchPublicIP(){try{const t=new AbortController,e=setTimeout(()=>t.abort(),2e3),i=await fetch("https://api.ipify.org?format=json",{signal:t.signal,headers:{Accept:"application/json"}});if(clearTimeout(e),!i.ok)return null;const r=await i.json();return"string"==typeof r?.ip?r.ip:null}catch(t){return this.logger.debug("Public IP fetch failed",t),null}}async trackPurchase(i,r,n=e.USD,s){await this.trackEvent(t.PURCHASE,s,i,r,n)}async trackLogin(e,i){await this.trackEvent(t.LOGIN,i,e)}async trackSignup(e,i){await this.trackEvent(t.SIGNUP,i,e)}async trackFormSubmit(e,i){await this.trackEvent(t.FORM_SUBMIT,i,e)}async trackVideoPlay(e,i){await this.trackEvent(t.VIDEO_PLAY,i,e)}async trackEmailVerification(e,i){await this.trackEvent(t.EMAIL_VERIFICATION,i,e)}async trackAuditApproved(e,i){await this.trackEvent(t.AUDIT_APPROVED,i,e)}async trackPurchaseAuto(i){const r=i.tracking_user_id||this.getUserId();r?await this.trackEvent(t.PURCHASE,i.purchaseData,r,i.revenue,i.currency||e.USD):this.logger.error("❌ trackPurchaseAuto requires tracking_user_id. Please set user ID using setUserId() or pass it in options.tracking_user_id.")}async trackLoginAuto(e){const i=e.tracking_user_id||this.getUserId();i?await this.trackEvent(t.LOGIN,e.loginData,i):this.logger.error("❌ trackLoginAuto requires tracking_user_id. Please set user ID using setUserId() or pass it in options.tracking_user_id.")}async trackSignupAuto(e){const i=e.tracking_user_id||this.getUserId();i?await this.trackEvent(t.SIGNUP,e.signupData,i):this.logger.error("❌ trackSignupAuto requires tracking_user_id. Please set user ID using setUserId() or pass it in options.tracking_user_id.")}async trackEmailVerificationAuto(e){const i=e.tracking_user_id||this.getUserId();i?await this.trackEvent(t.EMAIL_VERIFICATION,e.verificationData,i):this.logger.error("❌ trackEmailVerificationAuto requires tracking_user_id. Please set user ID using setUserId() or pass it in options.tracking_user_id.")}async trackProductView(e,i){await this.trackEvent(t.PRODUCT_VIEW,i,e)}async trackAddToCart(e,i){await this.trackEvent(t.ADD_TO_CART,i,e)}async trackPageClick(e,i){await this.trackEvent(t.PAGE_CLICK,i,e)}async trackButtonClick(e,i,r){await this.trackEvent(t.BUTTON_CLICK,{button_name:e,...r},i)}getAttributionData(){return this.storage.getUTMData()}addUTMToURL(t){if(!this.config.enableCrossDomainUTM)return t;const e=this.getAttributionData();return e?S(t,k(e.lastTouch,this.config.crossDomainUTMParams)):t}getCurrentUTMParams(){const t=this.getAttributionData();return t?k(t.lastTouch,this.config.crossDomainUTMParams):{}}getUTMParams(){const t=this.getAttributionData();if(!t)return this.logger.debug("No attribution data available for UTM params"),{};const e={utm_source:t.lastTouch.utm_source||null,utm_medium:t.lastTouch.utm_medium||null,utm_campaign:t.lastTouch.utm_campaign||null,utm_term:t.lastTouch.utm_term||null,utm_content:t.lastTouch.utm_content||null},i={};return e.utm_source&&""!==e.utm_source.trim()?i.utm_source=e.utm_source:i.utm_source=null,e.utm_medium&&""!==e.utm_medium.trim()?i.utm_medium=e.utm_medium:i.utm_medium=null,e.utm_campaign&&""!==e.utm_campaign.trim()?i.utm_campaign=e.utm_campaign:i.utm_campaign=null,e.utm_term&&""!==e.utm_term.trim()?i.utm_term=e.utm_term:i.utm_term=null,e.utm_content&&""!==e.utm_content.trim()?i.utm_content=e.utm_content:i.utm_content=null,this.logger.debug("UTM params for event:",i),i}getAdParams(){const t=this.getAttributionData();if(!t)return{};const e=t.lastTouch,i=t=>t&&"string"==typeof t&&""!==t.trim()?t:null;return{adgroup_id:i(e.adgroup_id),ad_id:i(e.ad_id),adgroup_id_source:i(e.adgroup_id_source),ad_id_source:i(e.ad_id_source),click_id:i(e.click_id),click_id_source:i(e.click_id_source)}}initializeUserId(){if(this.config.userId)this.setUserId(this.config.userId),this.logger.info(`👤 User ID initialized from config: ${this.config.userId}`);else{const t=this.getUserId();t?this.logger.debug(`👤 Existing user ID found: ${t}`):this.logger.debug("👤 No user ID found, will be set when user identifies")}}setUserId(t){if(!t||""===t.trim())return void this.logger.warn("Cannot set empty user ID");const e=t.trim();if(this.storage.setUserId(e),this.session){const t=this.session.userId;t&&t!==e?(this.logger.info(`🔄 User changed (${t} → ${e}) — rotating session`),this.rotateSession(e)):t!==e&&(this.session={...this.session,userId:e},this.storage.storeSession(this.session))}this.logger.info(`👤 User ID set: ${e}`)}rotateSession(t){const e=Date.now();this.session={sessionId:w(),startTime:e,lastActivity:e,pageViews:0,userId:t},this.storage.storeSession(this.session)}getUserId(){return this.storage.getUserId()}removeUserId(){this.storage.removeUserId(),this.logger.info("👤 User ID removed")}async identify(e,i){this.initialized?e&&""!==e.trim()?(this.setUserId(e),i&&(this.storage.setUserTraits(i),this.logger.info(`User identified: ${e}`,i)),await this.trackEvent(t.FORM_SUBMIT,{_identify:!0,_user_traits:i,tracking_user_id:e},e)):this.logger.warn("Cannot identify with empty user ID"):this.logger.warn("SDK not initialized, identify call queued")}getUserTraits(){return this.storage.getUserTraits()}getSessionId(){return this.session?.sessionId||null}initializeSession(){const t=this.storage.getSession(),e=Date.now(),i=this.storage.getUserId()??void 0,r=t&&e-t.lastActivity<this.config.sessionTimeout,n=!(!t?.userId||!i||t.userId===i);r&&!n?this.session={...t,lastActivity:e,userId:t.userId??i}:(n&&this.logger.info(`🔄 Session owner changed (${t?.userId} → ${i}) — starting new session`),this.session={sessionId:w(),startTime:e,lastActivity:e,pageViews:0,userId:i}),this.storage.storeSession(this.session),this.logger.debug("Session initialized:",this.session)}extractAndStoreUTMData(){const t=_(),e=function(t){const e=function(t){const e={};try{new URL(t).searchParams.forEach((t,i)=>{e[i]=t})}catch(t){}return e}(t),i={};return["utm_source","utm_medium","utm_campaign","utm_term","utm_content"].forEach(t=>{e[t]&&""!==e[t].trim()&&(i[t]=e[t].trim())}),i}(t),i=function(t){const e=function(t){const e={};try{const i=new URL(t),r=new Set;i.searchParams.forEach((t,e)=>{r.add(e)}),r.forEach(t=>{const r=i.searchParams.getAll(t);e[t]=r.filter(t=>null!=t&&""!==t)})}catch(t){}return e}(t),i={};for(const t of c){const r=h(e,t);if(void 0!==r){/^\{.+\}$/.test(r)?(i.adgroup_id=r,i.adgroup_id_source="valuetrack"):(i.adgroup_id=r,i.adgroup_id_source="explicit");break}}if(!i.adgroup_id){const t=h(e,"utm_term")?e[Object.keys(e).find(t=>"utm_term"===t.toLowerCase())]:[];for(const e of t){const t=(e||"").trim();if(d.test(t)){i.adgroup_id=t,i.adgroup_id_source="utm_term_numeric";break}}}for(const t of u){const r=h(e,t);if(void 0!==r){/^\{.+\}$/.test(r)?(i.ad_id=r,i.ad_id_source="valuetrack"):(i.ad_id=r,i.ad_id_source="explicit");break}}if(!i.ad_id){const t=h(e,"utm_content")?e[Object.keys(e).find(t=>"utm_content"===t.toLowerCase())]:[];for(const e of t){const t=(e||"").trim();if(d.test(t)){i.ad_id=t,i.ad_id_source="utm_content_numeric";break}}}for(const t of l){const r=h(e,t);if(r&&""!==r.trim()){i.click_id=r.trim(),i.click_id_source=t;break}}return i}(t);if(this.logger.debug("Extracting UTM params from URL:",t),this.logger.debug("Found UTM params:",e),this.logger.debug("Found ad params:",i),0===Object.keys(e).length&&!i.adgroup_id&&!i.ad_id&&!i.click_id)return void this.logger.debug("No UTM, ad, or click parameters found in URL");const r={utm_source:e.utm_source||"",utm_medium:e.utm_medium||"",utm_campaign:e.utm_campaign||"",utm_term:e.utm_term||"",utm_content:e.utm_content||"",adgroup_id:i.adgroup_id||"",ad_id:i.ad_id||"",adgroup_id_source:i.adgroup_id_source||"",ad_id_source:i.ad_id_source||"",click_id:i.click_id||"",click_id_source:i.click_id_source||"",timestamp:Date.now()},n=this.getAttributionData(),s={firstTouch:n?.firstTouch||r,lastTouch:r,touchpoints:n?.touchpoints||[],expiresAt:Date.now()+2592e6};n&&n.lastTouch.utm_source===r.utm_source&&n.lastTouch.utm_campaign===r.utm_campaign||s.touchpoints.push(r),this.storage.storeUTMData(s),this.logger.info("UTM data extracted and stored successfully:",r),this.config.autoCleanUTM&&function(){try{const t=new URL(window.location.href);let e=!1;["utm_source","utm_medium","utm_campaign","utm_term","utm_content"].forEach(i=>{t.searchParams.has(i)&&(t.searchParams.delete(i),e=!0)}),e&&window.history.replaceState({},document.title,t.toString())}catch(t){console.warn("Failed to clean URL:",t)}}()}setupAutoTracking(){this.autoTrackEnabled=!0,this.setupFormTracking(),this.setupLinkTracking(),this.logger.info("Auto-tracking enabled")}setupFormTracking(){document.addEventListener("submit",e=>{try{const i=e.target;if(!i)return;const r=this.serializeFormFields(i),n=this.extractLeadFields(r);n.email&&(this.storage.setUserTraits(n),this.logger.debug("Lead fields extracted from form:",n)),this.trackEvent(t.FORM_SUBMIT,{...r,_lead_fields:n,form_id:i.id||i.name,form_action:i.action,form_method:i.method})}catch(t){this.logger.error("Failed to auto-track form submit:",t)}})}serializeFormFields(t){const e={};try{const i=new FormData(t);for(const[t,r]of i.entries()){const i=this.serializeFormValue(r);if(Object.prototype.hasOwnProperty.call(e,t)){const r=e[t];Array.isArray(r)?(r.push(i),e[t]=r):e[t]=[r,i]}else e[t]=i}const r=Array.from(t.elements),n=new Map;for(let t=0;t<r.length;t++){const e=r[t];if(e.disabled)continue;const i=e.tagName.toLowerCase(),s=e.type?.toLowerCase();if("button"===i||"submit"===s||"reset"===s)continue;const o=this.getFormFieldKey(e,t);o&&(n.has(o)||n.set(o,[]),n.get(o).push(e))}n.forEach((t,i)=>{const r=t.some(t=>"radio"===t.type),n=t.some(t=>"checkbox"===t.type),s=t.some(t=>"file"===t.type),o=t.some(t=>"SELECT"===t.tagName&&t.multiple),a=t.some(t=>"password"===t.type);if(n){const r=t.filter(t=>"checkbox"===t.type).filter(t=>t.checked).map(t=>t.value||"on");return void(Object.prototype.hasOwnProperty.call(e,i)?Array.isArray(e[i])||(e[i]=[e[i],...r]):e[i]=r)}if(r){const r=t.filter(t=>"radio"===t.type).find(t=>t.checked);return void(Object.prototype.hasOwnProperty.call(e,i)||(e[i]=r?r.value:null))}if(o){const r=t.find(t=>"SELECT"===t.tagName&&t.multiple);if(r){const t=Array.from(r.selectedOptions).map(t=>t.value);return void(Object.prototype.hasOwnProperty.call(e,i)||(e[i]=t))}}if(s){const r=t.find(t=>"file"===t.type);if(r){const t=r.files?Array.from(r.files):[];return void(Object.prototype.hasOwnProperty.call(e,i)||(e[i]=t.map(t=>this.serializeFormValue(t))))}}if(a)Object.prototype.hasOwnProperty.call(e,i)?e[i]="string"==typeof e[i]?"*****":e[i]:e[i]="*****";else if(!Object.prototype.hasOwnProperty.call(e,i)){const r=t[0];if("SELECT"===r.tagName){const t=r;e[i]=t.multiple?Array.from(t.selectedOptions).map(t=>t.value):t.value}else r.type,e[i]=r.value??""}})}catch(t){this.logger.error("Failed to serialize form fields:",t)}return e}getFormFieldKey(t,e){const i=t.name;if(i)return i;if(t.id)return`id_${t.id}`;const r=t.getAttribute("aria-label");if(r&&r.trim())return`aria_${this.normalizeFormFieldKey(r)}`;const n=t instanceof HTMLInputElement||t instanceof HTMLTextAreaElement?t.placeholder:"";return n&&n.trim()?`ph_${this.normalizeFormFieldKey(n)}_${e}`:`field_${e}`}normalizeFormFieldKey(t){return t.trim().toLowerCase().replace(/\s+/g,"_").replace(/[^a-z0-9_]/g,"").slice(0,40)}serializeFormValue(t){return t instanceof File?{file_name:t.name,file_size:t.size,file_type:t.type}:t}extractLeadFields(t){const e={};for(const[i,r]of Object.entries(o))if(!e[i])for(const n of r||[]){const r=n.toLowerCase();for(const[n,s]of Object.entries(t))if((n.toLowerCase()===r||n.toLowerCase().includes(r))&&s&&"string"==typeof s&&s.trim()){e[i]=s.trim();break}if(e[i])break}return e.name||!e.first_name&&!e.last_name||(e.name=[e.first_name,e.last_name].filter(Boolean).join(" ").trim()),e}setupLinkTracking(){document.addEventListener("click",t=>{const e=t.target.closest("a");e&&function(t){try{const e=new URL(t),i=window.location.hostname;return e.hostname!==i}catch(t){return!1}}(e.href)&&this.handleCrossDomainUTM(e,t)})}handleCrossDomainUTM(t,e){if(!this.config.enableCrossDomainUTM)return;const i=t.href;if(function(t,e=[]){try{const i=new URL(t).hostname.toLowerCase();return e.some(t=>{const e=t.toLowerCase();return i===e||i.endsWith(`.${e}`)})}catch(t){return!1}}(i,this.config.excludeDomains))return void this.logger.debug(`Domain excluded from UTM passing: ${i}`);const r=this.getAttributionData();if(!r)return void this.logger.debug("No UTM data available for cross-domain passing");const n=k(r.lastTouch,this.config.crossDomainUTMParams);if(0===Object.keys(n).length)return void this.logger.debug("No UTM parameters to pass");const s=S(i,n);s!==i&&(t.href=s,this.logger.debug("UTM parameters added to external link:",{original:i,enhanced:s,utmParams:n}),this.logger.debug("Cross-domain UTM passed:",{link_url:s,original_url:i,utm_params_passed:n}))}setupNetworkHandlers(){window.addEventListener("online",()=>{this.logger.info("Network connection restored"),this.queue.flush()}),window.addEventListener("offline",()=>{this.logger.warn("Network connection lost")})}setupVisibilityHandlers(){document.addEventListener("visibilitychange",()=>{"hidden"!==document.visibilityState?"visible"===document.visibilityState&&this.updateSessionActivity():this.flushOnUnload("visibilitychange:hidden")})}setupBeforeUnloadHandler(){window.addEventListener("beforeunload",()=>{this.flushOnUnload("beforeunload")}),window.addEventListener("pagehide",()=>{this.flushOnUnload("pagehide")})}flushOnUnload(t){try{this.updateSessionActivity();const e=this.queue.drainAll();if(0===e.length)return;if(this.storage.appendPendingEvents(e),this.logger.debug(`🚚 Unload flush triggered: ${t}`,{count:e.length}),this.httpClient.sendEventsBeacon(e))return void this.storage.removePendingEventsByIds(e.map(t=>t.event_id));const i=20;for(let t=0;t<e.length;t+=i){const r=e.slice(t,t+i);this.httpClient.sendEventsKeepalive(r).then(()=>{this.storage.removePendingEventsByIds(r.map(t=>t.event_id))}).catch(t=>{this.logger.debug("Unload keepalive send failed",t)})}}catch(t){this.logger.debug("flushOnUnload failed",t)}}async retryPendingEvents(){const t=this.storage.getPendingEvents();if(0!==t.length){this.logger.info(`📦 Retrying pending events: ${t.length}`);try{await this.httpClient.sendEvents(t),this.storage.removePendingEventsByIds(t.map(t=>t.event_id)),this.logger.info(`✅ Pending events sent: ${t.length}`)}catch(t){this.logger.warn("⚠️ Pending events retry failed",t)}}}getCurrentPath(){return"undefined"==typeof window?"":window.location.pathname+window.location.search}setupSPATracking(){if("undefined"==typeof window||"undefined"==typeof history)return void this.logger.warn("⚠️ SPA tracking not available in this environment");if(this.spaTrackingEnabled)return void this.logger.warn("⚠️ SPA tracking already enabled");this.spaTrackingEnabled=!0,this.lastTrackedPath=this.getCurrentPath(),this.originalPushState=history.pushState.bind(history),this.originalReplaceState=history.replaceState.bind(history);const t=t=>{const e=this.getCurrentPath();e!==this.lastTrackedPath?(this.logger.debug(`🔄 [SPA] Route change detected (${t}): ${this.lastTrackedPath} -> ${e}`),this.lastTrackedPath=e,setTimeout(()=>{this.trackPageView().then(()=>this.queue.process()).then(()=>{this.logger.debug(`✅ [SPA] Page view tracked for: ${e}`)}).catch(t=>{this.logger.error("❌ [SPA] Failed to track page view:",t)})},100)):this.logger.debug(`🔄 [SPA] Route change detected (${t}) but path unchanged: ${e}`)};history.pushState=(e,i,r)=>{const n=this.originalPushState(e,i,r);return t("pushState"),n},history.replaceState=(e,i,r)=>{const n=this.originalReplaceState(e,i,r);return t("replaceState"),n},this.popstateHandler=()=>{t("popstate")},window.addEventListener("popstate",this.popstateHandler),this.logger.info("🔄 SPA tracking setup completed")}cleanupSPATracking(){this.spaTrackingEnabled&&(this.originalPushState&&(history.pushState=this.originalPushState,this.originalPushState=null),this.originalReplaceState&&(history.replaceState=this.originalReplaceState,this.originalReplaceState=null),this.popstateHandler&&(window.removeEventListener("popstate",this.popstateHandler),this.popstateHandler=null),this.spaTrackingEnabled=!1,this.logger.info("🔄 SPA tracking cleaned up"))}updateSessionActivity(){this.session&&(this.session.lastActivity=Date.now(),this.storage.storeSession(this.session))}async flush(){await this.queue.flush()}getStatus(){return{initialized:this.initialized,session:this.session,queueSize:this.queue.size(),online:navigator.onLine,crossDomainUTM:{enabled:this.config.enableCrossDomainUTM||!0,currentParams:this.getCurrentUTMParams()},spaTracking:{enabled:this.spaTrackingEnabled,currentPath:this.lastTrackedPath}}}destroy(){this.queue.clear(),this.autoTrackEnabled=!1,this.cleanupSPATracking(),this.initialized=!1,this.logger.info("🗑️ SDK destroyed")}}let K=null;async function R(t){if(K)return console.warn("GetuAI SDK: Already initialized"),K;try{if(K=new O(t),await K.init(),t.enableDebug&&(window.GetuAISDK=K),console.log("GetuAI Attribution SDK initialized successfully"),"undefined"!=typeof window){const t=new CustomEvent("getuaiSDKReady",{detail:{sdk:K}});window.dispatchEvent(t)}return K}catch(t){if(console.error("GetuAI SDK: Failed to initialize:",t),"undefined"!=typeof window){const e=new CustomEvent("getuaiSDKError",{detail:{error:t}});window.dispatchEvent(e)}throw t}}function x(){return K}function N(){return new Promise((t,e)=>{if(K)return void t(K);const i=e=>{window.removeEventListener("getuaiSDKReady",i),window.removeEventListener("getuaiSDKError",r),t(e.detail.sdk)},r=t=>{window.removeEventListener("getuaiSDKReady",i),window.removeEventListener("getuaiSDKError",r),e(t.detail.error)};window.addEventListener("getuaiSDKReady",i),window.addEventListener("getuaiSDKError",r),setTimeout(()=>{window.removeEventListener("getuaiSDKReady",i),window.removeEventListener("getuaiSDKError",r),e(new Error("SDK initialization timeout"))},1e4)})}async function F(t,i,r,n,s=e.USD){const o=x();o?await o.trackEvent(t,i,r,n,s):console.warn("GetuAI SDK: Not initialized. Call init() first.")}async function L(t,e){const i=x();i?await i.trackPageView(t,e):console.warn("GetuAI SDK: Not initialized. Call init() first.")}async function M(t,i,r=e.USD,n){const s=x();s?await s.trackPurchase(t,i,r,n):console.warn("GetuAI SDK: Not initialized. Call init() first.")}async function z(t,e){const i=x();i?await i.trackLogin(t,e):console.warn("GetuAI SDK: Not initialized. Call init() first.")}async function V(t,e){const i=x();i?await i.trackSignup(t,e):console.warn("GetuAI SDK: Not initialized. Call init() first.")}async function $(t,e){const i=x();i?await i.trackFormSubmit(t,e):console.warn("GetuAI SDK: Not initialized. Call init() first.")}async function G(t,e){const i=x();i?await i.trackVideoPlay(t,e):console.warn("GetuAI SDK: Not initialized. Call init() first.")}async function B(t,e){const i=x();i?await i.trackEmailVerification(t,e):console.warn("GetuAI SDK: Not initialized. Call init() first.")}async function q(t,e){const i=x();i?await i.trackAuditApproved(t,e):console.warn("GetuAI SDK: Not initialized. Call init() first.")}async function Y(t){const e=x();e?await e.trackPurchaseAuto(t):console.warn("GetuAI SDK: Not initialized. Call init() first.")}async function j(t){const e=x();e?await e.trackLoginAuto(t):console.warn("GetuAI SDK: Not initialized. Call init() first.")}async function H(t){const e=x();e?await e.trackSignupAuto(t):console.warn("GetuAI SDK: Not initialized. Call init() first.")}async function J(t){const e=x();e?await e.trackEmailVerificationAuto(t):console.warn("GetuAI SDK: Not initialized. Call init() first.")}async function W(t,i,r,n,s=e.USD){const o=x();o?await o.trackCustomEvent(t,i,r,n,s):console.warn("GetuAI SDK: Not initialized. Call init() first.")}async function X(t,e){const i=x();i?await i.trackProductView(t,e):console.warn("GetuAI SDK: Not initialized. Call init() first.")}async function Q(t,e){const i=x();i?await i.trackAddToCart(t,e):console.warn("GetuAI SDK: Not initialized. Call init() first.")}async function Z(t,e){const i=x();i?await i.trackPageClick(t,e):console.warn("GetuAI SDK: Not initialized. Call init() first.")}async function tt(t,e,i){const r=x();r?await r.trackButtonClick(t,e,i):console.warn("GetuAI SDK: Not initialized. Call init() first.")}function et(){const t=x();return t?t.getAttributionData():null}async function it(){const t=x();t?await t.flush():console.warn("GetuAI SDK: Not initialized. Call init() first.")}function rt(){const t=x();return t?t.getStatus():null}function nt(t){const e=x();return e?e.addUTMToURL(t):(console.warn("GetuAI SDK: Not initialized. Call init() first."),t)}function st(){const t=x();return t?t.getCurrentUTMParams():{}}function ot(t){const e=x();e?e.setUserId(t):console.warn("GetuAI SDK: Not initialized. Call init() first.")}function at(){const t=x();return t?t.getUserId():(console.warn("GetuAI SDK: Not initialized. Call init() first."),null)}function ct(){const t=x();t?t.removeUserId():console.warn("GetuAI SDK: Not initialized. Call init() first.")}function ut(){K&&(K.destroy(),K=null,console.log("GetuAI SDK destroyed"))}class lt extends O{static async init(t){return await R(t)}static async trackEvent(t,i,r,n,s=e.USD){return await F(t,i,r,n,s)}static async trackPageView(t,e){return await L(t,e)}static async trackPurchase(t,i,r=e.USD,n){return await M(t,i,r,n)}static async trackLogin(t,e){return await z(t,e)}static async trackSignup(t,e){return await V(t,e)}static async trackFormSubmit(t,e){return await $(t,e)}static async trackVideoPlay(t,e){return await G(t,e)}static async trackEmailVerification(t,e){return await B(t,e)}static async trackPurchaseAuto(t){return await Y(t)}static async trackLoginAuto(t){return await j(t)}static async trackSignupAuto(t){return await H(t)}static async trackEmailVerificationAuto(t){return await J(t)}static async trackCustomEvent(t,i,r,n,s=e.USD){return await W(t,i,r,n,s)}static async trackProductView(t,e){return await X(t,e)}static async trackAddToCart(t,e){return await Q(t,e)}static async trackPageClick(t,e){return await Z(t,e)}static async trackButtonClick(t,e,i){return await tt(t,e,i)}static getAttributionData(){return et()}static async flush(){return await it()}static getStatus(){return rt()}static addUTMToURL(t){return nt(t)}static getCurrentUTMParams(){return st()}static setUserId(t){ot(t)}static getUserId(){return at()}static removeUserId(){ct()}static destroy(){ut()}static getSDK(){return x()}static waitForSDK(){return N()}}"undefined"!=typeof document&&function(){if(K)return;const t=document.currentScript;if(!t)return void console.warn("GetuAI SDK: Could not find script tag for auto-initialization");const e=t.getAttribute("data-api-key");e?R({apiKey:e,apiEndpoint:t.getAttribute("data-api-endpoint")||n,enableDebug:"true"===t.getAttribute("data-debug"),autoTrack:"true"===t.getAttribute("data-auto-track"),autoTrackPageView:"true"===t.getAttribute("data-auto-track-page-view"),autoCleanUTM:"false"!==t.getAttribute("data-auto-clean-utm"),batchSize:parseInt(t.getAttribute("data-batch-size")||"100"),batchInterval:parseInt(t.getAttribute("data-batch-interval")||"2000"),userId:t.getAttribute("data-user-id")||void 0,domain:t.getAttribute("data-domain")||void 0}):console.warn("GetuAI SDK: No API key provided. Please add data-api-key attribute to script tag.")}(),"undefined"!=typeof window&&(window.getuaiSDK={init:R,getSDK:x,waitForSDK:N,trackEvent:F,trackPageView:L,trackPageClick:Z,trackButtonClick:tt,trackPurchase:M,trackLogin:z,trackSignup:V,trackFormSubmit:$,trackVideoPlay:G,trackEmailVerification:B,trackAuditApproved:q,trackCustomEvent:W,trackProductView:X,trackAddToCart:Q,trackPurchaseAuto:Y,trackLoginAuto:j,trackSignupAuto:H,trackEmailVerificationAuto:J,getAttributionData:et,flush:it,getStatus:rt,addUTMToURL:nt,getCurrentUTMParams:st,setUserId:ot,getUserId:at,removeUserId:ct,destroy:ut,EventType:t,Currency:e,AttributionSDK:lt},window.init=R,window.waitForSDK=N,window.trackEvent=F,window.trackPageView=L,window.trackPageClick=Z,window.trackButtonClick=tt,window.trackPurchase=M,window.trackLogin=z,window.trackSignup=V,window.trackFormSubmit=$,window.trackVideoPlay=G,window.trackEmailVerification=B,window.trackProductView=X,window.trackAddToCart=Q,window.trackPurchaseAuto=Y,window.trackLoginAuto=j,window.trackSignupAuto=H,window.trackEmailVerificationAuto=J,window.getAttributionData=et,window.flush=it,window.getStatus=rt,window.addUTMToURL=nt,window.getCurrentUTMParams=st,window.setUserId=ot,window.getUserId=at,window.removeUserId=ct,window.destroy=ut,window.AttributionSDK=lt);const dt={init:R,getSDK:x,waitForSDK:N,trackEvent:F,trackPageView:L,trackPageClick:Z,trackButtonClick:tt,trackPurchase:M,trackLogin:z,trackSignup:V,trackFormSubmit:$,trackVideoPlay:G,trackEmailVerification:B,trackAuditApproved:q,trackCustomEvent:W,trackProductView:X,trackAddToCart:Q,trackPurchaseAuto:Y,trackLoginAuto:j,trackSignupAuto:H,trackEmailVerificationAuto:J,getAttributionData:et,flush:it,getStatus:rt,addUTMToURL:nt,getCurrentUTMParams:st,setUserId:ot,getUserId:at,removeUserId:ct,destroy:ut,EventType:t,Currency:e,AttributionSDK:lt};return r.default})());
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EACL,SAAS,EACT,SAAS,EACT,QAAQ,EACR,SAAS,EACT,eAAe,EAEhB,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|