getu-attribution-v2-sdk 0.2.1 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +180 -1
- package/dist/core/AttributionSDK.d.ts +22 -0
- package/dist/core/AttributionSDK.d.ts.map +1 -1
- package/dist/core/AttributionSDK.js +86 -3
- package/dist/getuai-attribution.min.js +1 -1
- package/dist/index.d.ts +50 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.esm.js +321 -4
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +321 -4
- package/dist/storage/index.d.ts +8 -0
- package/dist/storage/index.d.ts.map +1 -1
- package/dist/storage/index.js +121 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/index.d.ts.map +1 -1
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -8,6 +8,7 @@ A JavaScript SDK for tracking user attribution and conversion events in web appl
|
|
|
8
8
|
- [Installation](#installation)
|
|
9
9
|
- [Quick Start](#quick-start)
|
|
10
10
|
- [Configuration](#configuration)
|
|
11
|
+
- [User ID Management](#user-id-management)
|
|
11
12
|
- [Event Tracking](#event-tracking)
|
|
12
13
|
- [Auto-Tracking](#auto-tracking)
|
|
13
14
|
- [SPA Support](#spa-support)
|
|
@@ -104,6 +105,7 @@ await trackPageView({ category: "homepage" });
|
|
|
104
105
|
| `data-batch-size` | number | `100` | Number of events per batch |
|
|
105
106
|
| `data-batch-interval` | number | `2000` | Batch interval in milliseconds |
|
|
106
107
|
| `data-auto-clean-utm` | boolean | `true` | Remove UTM params from URL after capture |
|
|
108
|
+
| `data-user-id` | string | - | Initial user ID |
|
|
107
109
|
|
|
108
110
|
### Configuration Object
|
|
109
111
|
|
|
@@ -139,6 +141,9 @@ interface SDKConfig {
|
|
|
139
141
|
// Page View
|
|
140
142
|
pageViewDebounceInterval?: number; // Default: 5000 (ms) - Debounce interval for same page
|
|
141
143
|
|
|
144
|
+
// User ID
|
|
145
|
+
userId?: string; // Initial user ID to set on initialization
|
|
146
|
+
|
|
142
147
|
// Debug
|
|
143
148
|
enableDebug?: boolean; // Default: false
|
|
144
149
|
}
|
|
@@ -162,12 +167,83 @@ await init({
|
|
|
162
167
|
excludeDomains: ["google.com", "facebook.com"],
|
|
163
168
|
autoCleanUTM: true,
|
|
164
169
|
pageViewDebounceInterval: 3000,
|
|
170
|
+
userId: "user_123", // Optional: Set initial user ID
|
|
165
171
|
enableDebug: true,
|
|
166
172
|
});
|
|
167
173
|
```
|
|
168
174
|
|
|
169
175
|
---
|
|
170
176
|
|
|
177
|
+
## User ID Management
|
|
178
|
+
|
|
179
|
+
The SDK automatically manages user IDs and includes them in all tracked events.
|
|
180
|
+
|
|
181
|
+
### Setting User ID
|
|
182
|
+
|
|
183
|
+
#### During Initialization
|
|
184
|
+
|
|
185
|
+
You can set the user ID when initializing the SDK:
|
|
186
|
+
|
|
187
|
+
```javascript
|
|
188
|
+
// Via script tag
|
|
189
|
+
<script
|
|
190
|
+
src="https://unpkg.com/getu-attribution-v2-sdk/dist/getuai-attribution.min.js"
|
|
191
|
+
data-api-key="your_api_key_here"
|
|
192
|
+
data-user-id="user_123"
|
|
193
|
+
></script>;
|
|
194
|
+
|
|
195
|
+
// Via configuration object
|
|
196
|
+
await init({
|
|
197
|
+
apiKey: "your_api_key_here",
|
|
198
|
+
userId: "user_123",
|
|
199
|
+
});
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
#### Programmatically Set User ID
|
|
203
|
+
|
|
204
|
+
You can set or update the user ID at any time:
|
|
205
|
+
|
|
206
|
+
```javascript
|
|
207
|
+
import { setUserId } from "getu-attribution-v2-sdk";
|
|
208
|
+
|
|
209
|
+
// Set user ID (e.g., after user login)
|
|
210
|
+
setUserId("user_123");
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
### Getting User ID
|
|
214
|
+
|
|
215
|
+
```javascript
|
|
216
|
+
import { getUserId } from "getu-attribution-v2-sdk";
|
|
217
|
+
|
|
218
|
+
const userId = getUserId();
|
|
219
|
+
console.log("Current user ID:", userId); // "user_123" or null
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
### Removing User ID
|
|
223
|
+
|
|
224
|
+
```javascript
|
|
225
|
+
import { removeUserId } from "getu-attribution-v2-sdk";
|
|
226
|
+
|
|
227
|
+
// Remove user ID (e.g., after user logout)
|
|
228
|
+
removeUserId();
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
### Automatic Inclusion in Events
|
|
232
|
+
|
|
233
|
+
Once a user ID is set, it will **automatically be included** in all tracked events, even if you don't explicitly pass it:
|
|
234
|
+
|
|
235
|
+
```javascript
|
|
236
|
+
// User ID is already set via setUserId("user_123")
|
|
237
|
+
|
|
238
|
+
// This will automatically include user_123 in the event
|
|
239
|
+
await trackPageView({ category: "homepage" });
|
|
240
|
+
|
|
241
|
+
// Explicit user ID parameter will override the stored one
|
|
242
|
+
await trackPageView({ category: "homepage" }, "different_user_456");
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
---
|
|
246
|
+
|
|
171
247
|
## Event Tracking
|
|
172
248
|
|
|
173
249
|
### Event Types
|
|
@@ -239,9 +315,11 @@ await trackPageClick("user_123", {
|
|
|
239
315
|
|
|
240
316
|
### Track Purchase
|
|
241
317
|
|
|
318
|
+
**Method 1: Traditional format (user_id required)**
|
|
319
|
+
|
|
242
320
|
```javascript
|
|
243
321
|
await trackPurchase(
|
|
244
|
-
"user_123", // tracking_user_id
|
|
322
|
+
"user_123", // tracking_user_id (required)
|
|
245
323
|
99.99, // revenue
|
|
246
324
|
Currency.USD, // currency (optional, default: USD)
|
|
247
325
|
{
|
|
@@ -253,8 +331,33 @@ await trackPurchase(
|
|
|
253
331
|
);
|
|
254
332
|
```
|
|
255
333
|
|
|
334
|
+
**Method 2: Auto user ID format (object parameter, user_id optional)**
|
|
335
|
+
|
|
336
|
+
```javascript
|
|
337
|
+
// Auto-use stored user ID
|
|
338
|
+
await trackPurchaseAuto({
|
|
339
|
+
revenue: 99.99,
|
|
340
|
+
currency: Currency.USD, // optional
|
|
341
|
+
purchaseData: {
|
|
342
|
+
product_id: "prod_123",
|
|
343
|
+
product_name: "Premium Plan",
|
|
344
|
+
quantity: 1,
|
|
345
|
+
},
|
|
346
|
+
// tracking_user_id is optional - will use stored user ID if not provided
|
|
347
|
+
});
|
|
348
|
+
|
|
349
|
+
// Or explicitly provide user ID
|
|
350
|
+
await trackPurchaseAuto({
|
|
351
|
+
revenue: 99.99,
|
|
352
|
+
tracking_user_id: "user_123", // optional, will use stored if not provided
|
|
353
|
+
purchaseData: { product_id: "prod_123" },
|
|
354
|
+
});
|
|
355
|
+
```
|
|
356
|
+
|
|
256
357
|
### Track Login
|
|
257
358
|
|
|
359
|
+
**Method 1: Traditional format (user_id required)**
|
|
360
|
+
|
|
258
361
|
```javascript
|
|
259
362
|
await trackLogin("user_123", {
|
|
260
363
|
method: "email",
|
|
@@ -262,8 +365,29 @@ await trackLogin("user_123", {
|
|
|
262
365
|
});
|
|
263
366
|
```
|
|
264
367
|
|
|
368
|
+
**Method 2: Auto user ID format (object parameter, user_id optional)**
|
|
369
|
+
|
|
370
|
+
```javascript
|
|
371
|
+
// Auto-use stored user ID
|
|
372
|
+
await trackLoginAuto({
|
|
373
|
+
loginData: {
|
|
374
|
+
method: "email",
|
|
375
|
+
provider: "google",
|
|
376
|
+
},
|
|
377
|
+
// tracking_user_id is optional - will use stored user ID if not provided
|
|
378
|
+
});
|
|
379
|
+
|
|
380
|
+
// Or explicitly provide user ID
|
|
381
|
+
await trackLoginAuto({
|
|
382
|
+
tracking_user_id: "user_123", // optional
|
|
383
|
+
loginData: { method: "email" },
|
|
384
|
+
});
|
|
385
|
+
```
|
|
386
|
+
|
|
265
387
|
### Track Signup
|
|
266
388
|
|
|
389
|
+
**Method 1: Traditional format (user_id required)**
|
|
390
|
+
|
|
267
391
|
```javascript
|
|
268
392
|
await trackSignup("user_123", {
|
|
269
393
|
method: "email",
|
|
@@ -271,6 +395,25 @@ await trackSignup("user_123", {
|
|
|
271
395
|
});
|
|
272
396
|
```
|
|
273
397
|
|
|
398
|
+
**Method 2: Auto user ID format (object parameter, user_id optional)**
|
|
399
|
+
|
|
400
|
+
```javascript
|
|
401
|
+
// Auto-use stored user ID
|
|
402
|
+
await trackSignupAuto({
|
|
403
|
+
signupData: {
|
|
404
|
+
method: "email",
|
|
405
|
+
referral_code: "REF123",
|
|
406
|
+
},
|
|
407
|
+
// tracking_user_id is optional - will use stored user ID if not provided
|
|
408
|
+
});
|
|
409
|
+
|
|
410
|
+
// Or explicitly provide user ID
|
|
411
|
+
await trackSignupAuto({
|
|
412
|
+
tracking_user_id: "user_123", // optional
|
|
413
|
+
signupData: { method: "email" },
|
|
414
|
+
});
|
|
415
|
+
```
|
|
416
|
+
|
|
274
417
|
### Track Form Submit
|
|
275
418
|
|
|
276
419
|
```javascript
|
|
@@ -300,6 +443,8 @@ await trackVideoPlay("user_123", {
|
|
|
300
443
|
|
|
301
444
|
### Track Email Verification
|
|
302
445
|
|
|
446
|
+
**Method 1: Traditional format (user_id required)**
|
|
447
|
+
|
|
303
448
|
```javascript
|
|
304
449
|
await trackEmailVerification("user_123", {
|
|
305
450
|
email: "user@example.com",
|
|
@@ -307,6 +452,40 @@ await trackEmailVerification("user_123", {
|
|
|
307
452
|
});
|
|
308
453
|
```
|
|
309
454
|
|
|
455
|
+
**Method 2: Auto user ID format (object parameter, user_id optional)**
|
|
456
|
+
|
|
457
|
+
```javascript
|
|
458
|
+
// Auto-use stored user ID
|
|
459
|
+
await trackEmailVerificationAuto({
|
|
460
|
+
verificationData: {
|
|
461
|
+
email: "user@example.com",
|
|
462
|
+
verification_method: "email_link",
|
|
463
|
+
},
|
|
464
|
+
// tracking_user_id is optional - will use stored user ID if not provided
|
|
465
|
+
});
|
|
466
|
+
|
|
467
|
+
// Or explicitly provide user ID
|
|
468
|
+
await trackEmailVerificationAuto({
|
|
469
|
+
tracking_user_id: "user_123", // optional
|
|
470
|
+
verificationData: { email: "user@example.com" },
|
|
471
|
+
});
|
|
472
|
+
```
|
|
473
|
+
|
|
474
|
+
### Auto User ID Functions Summary
|
|
475
|
+
|
|
476
|
+
The following functions support auto user ID with object parameter format:
|
|
477
|
+
|
|
478
|
+
- `trackPurchaseAuto(options)` - Purchase tracking with optional user ID
|
|
479
|
+
- `trackLoginAuto(options)` - Login tracking with optional user ID
|
|
480
|
+
- `trackSignupAuto(options)` - Signup tracking with optional user ID
|
|
481
|
+
- `trackEmailVerificationAuto(options)` - Email verification tracking with optional user ID
|
|
482
|
+
|
|
483
|
+
**Important Notes:**
|
|
484
|
+
|
|
485
|
+
- If `tracking_user_id` is not provided in options and no user ID is stored, these functions will log an error and return without tracking the event
|
|
486
|
+
- Always set user ID using `setUserId()` before calling these functions, or provide `tracking_user_id` in the options
|
|
487
|
+
- The traditional functions (`trackPurchase`, `trackLogin`, etc.) still require `tracking_user_id` as the first parameter
|
|
488
|
+
|
|
310
489
|
### Track Product View
|
|
311
490
|
|
|
312
491
|
```javascript
|
|
@@ -26,6 +26,24 @@ export declare class AttributionSDK {
|
|
|
26
26
|
trackFormSubmit(tracking_user_id?: string, formData?: Record<string, any>): Promise<void>;
|
|
27
27
|
trackVideoPlay(tracking_user_id?: string, videoData?: Record<string, any>): Promise<void>;
|
|
28
28
|
trackEmailVerification(tracking_user_id: string, verificationData?: Record<string, any>): Promise<void>;
|
|
29
|
+
trackPurchaseAuto(options: {
|
|
30
|
+
revenue: number;
|
|
31
|
+
currency?: Currency;
|
|
32
|
+
purchaseData?: Record<string, any>;
|
|
33
|
+
tracking_user_id?: string;
|
|
34
|
+
}): Promise<void>;
|
|
35
|
+
trackLoginAuto(options: {
|
|
36
|
+
loginData?: Record<string, any>;
|
|
37
|
+
tracking_user_id?: string;
|
|
38
|
+
}): Promise<void>;
|
|
39
|
+
trackSignupAuto(options: {
|
|
40
|
+
signupData?: Record<string, any>;
|
|
41
|
+
tracking_user_id?: string;
|
|
42
|
+
}): Promise<void>;
|
|
43
|
+
trackEmailVerificationAuto(options: {
|
|
44
|
+
verificationData?: Record<string, any>;
|
|
45
|
+
tracking_user_id?: string;
|
|
46
|
+
}): Promise<void>;
|
|
29
47
|
trackProductView(tracking_user_id?: string, productData?: Record<string, any>): Promise<void>;
|
|
30
48
|
trackAddToCart(tracking_user_id?: string, cartData?: Record<string, any>): Promise<void>;
|
|
31
49
|
trackPageClick(tracking_user_id?: string, clickData?: Record<string, any>): Promise<void>;
|
|
@@ -33,6 +51,10 @@ export declare class AttributionSDK {
|
|
|
33
51
|
addUTMToURL(url: string): string;
|
|
34
52
|
getCurrentUTMParams(): Record<string, string>;
|
|
35
53
|
private getUTMParams;
|
|
54
|
+
private initializeUserId;
|
|
55
|
+
setUserId(userId: string): void;
|
|
56
|
+
getUserId(): string | null;
|
|
57
|
+
removeUserId(): void;
|
|
36
58
|
private initializeSession;
|
|
37
59
|
private extractAndStoreUTMData;
|
|
38
60
|
private setupAutoTracking;
|
|
@@ -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,EAGZ,MAAM,UAAU,CAAC;AAuBlB,qBAAa,cAAc;IACzB,OAAO,CAAC,MAAM,CAAY;IAC1B,OAAO,CAAC,MAAM,CAAgB;IAC9B,OAAO,CAAC,OAAO,CAA4B;IAC3C,OAAO,CAAC,UAAU,CAAkB;IACpC,OAAO,CAAC,KAAK,CAAoB;IACjC,OAAO,CAAC,OAAO,CAA4B;IAC3C,OAAO,CAAC,WAAW,CAAkB;IACrC,OAAO,CAAC,gBAAgB,CAAkB;IAC1C,OAAO,CAAC,kBAAkB,CAAkC;IAE5D,OAAO,CAAC,kBAAkB,CAAkB;IAC5C,OAAO,CAAC,eAAe,CAAc;IACrC,OAAO,CAAC,iBAAiB,CAAyC;IAClE,OAAO,CAAC,oBAAoB,CAA4C;IACxE,OAAO,CAAC,eAAe,CAAiD;gBAE5D,MAAM,EAAE,SAAS;IA+CvB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;
|
|
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,EAGZ,MAAM,UAAU,CAAC;AAuBlB,qBAAa,cAAc;IACzB,OAAO,CAAC,MAAM,CAAY;IAC1B,OAAO,CAAC,MAAM,CAAgB;IAC9B,OAAO,CAAC,OAAO,CAA4B;IAC3C,OAAO,CAAC,UAAU,CAAkB;IACpC,OAAO,CAAC,KAAK,CAAoB;IACjC,OAAO,CAAC,OAAO,CAA4B;IAC3C,OAAO,CAAC,WAAW,CAAkB;IACrC,OAAO,CAAC,gBAAgB,CAAkB;IAC1C,OAAO,CAAC,kBAAkB,CAAkC;IAE5D,OAAO,CAAC,kBAAkB,CAAkB;IAC5C,OAAO,CAAC,eAAe,CAAc;IACrC,OAAO,CAAC,iBAAiB,CAAyC;IAClE,OAAO,CAAC,oBAAoB,CAA4C;IACxE,OAAO,CAAC,eAAe,CAAiD;gBAE5D,MAAM,EAAE,SAAS;IA+CvB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAgFrB,UAAU,CACd,SAAS,EAAE,SAAS,EACpB,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAC/B,gBAAgB,CAAC,EAAE,MAAM,EACzB,OAAO,CAAC,EAAE,MAAM,EAChB,QAAQ,GAAE,QAAuB,GAChC,OAAO,CAAC,IAAI,CAAC;IAyDV,aAAa,CACjB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAC9B,gBAAgB,CAAC,EAAE,MAAM,GACxB,OAAO,CAAC,IAAI,CAAC;IAoChB,OAAO,CAAC,yBAAyB;YAUnB,aAAa;IAsBrB,aAAa,CACjB,gBAAgB,EAAE,MAAM,EACxB,OAAO,EAAE,MAAM,EACf,QAAQ,GAAE,QAAuB,EACjC,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GACjC,OAAO,CAAC,IAAI,CAAC;IAWV,UAAU,CACd,gBAAgB,EAAE,MAAM,EACxB,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAC9B,OAAO,CAAC,IAAI,CAAC;IAKV,WAAW,CACf,gBAAgB,EAAE,MAAM,EACxB,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAC/B,OAAO,CAAC,IAAI,CAAC;IAKV,eAAe,CACnB,gBAAgB,CAAC,EAAE,MAAM,EACzB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAC7B,OAAO,CAAC,IAAI,CAAC;IAKV,cAAc,CAClB,gBAAgB,CAAC,EAAE,MAAM,EACzB,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAC9B,OAAO,CAAC,IAAI,CAAC;IAKV,sBAAsB,CAC1B,gBAAgB,EAAE,MAAM,EACxB,gBAAgB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GACrC,OAAO,CAAC,IAAI,CAAC;IASV,iBAAiB,CAAC,OAAO,EAAE;QAC/B,OAAO,EAAE,MAAM,CAAC;QAChB,QAAQ,CAAC,EAAE,QAAQ,CAAC;QACpB,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;QACnC,gBAAgB,CAAC,EAAE,MAAM,CAAC;KAC3B,GAAG,OAAO,CAAC,IAAI,CAAC;IAkBX,cAAc,CAAC,OAAO,EAAE;QAC5B,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;QAChC,gBAAgB,CAAC,EAAE,MAAM,CAAC;KAC3B,GAAG,OAAO,CAAC,IAAI,CAAC;IAYX,eAAe,CAAC,OAAO,EAAE;QAC7B,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;QACjC,gBAAgB,CAAC,EAAE,MAAM,CAAC;KAC3B,GAAG,OAAO,CAAC,IAAI,CAAC;IAYX,0BAA0B,CAAC,OAAO,EAAE;QACxC,gBAAgB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;QACvC,gBAAgB,CAAC,EAAE,MAAM,CAAC;KAC3B,GAAG,OAAO,CAAC,IAAI,CAAC;IAgBX,gBAAgB,CACpB,gBAAgB,CAAC,EAAE,MAAM,EACzB,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAChC,OAAO,CAAC,IAAI,CAAC;IASV,cAAc,CAClB,gBAAgB,CAAC,EAAE,MAAM,EACzB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAC7B,OAAO,CAAC,IAAI,CAAC;IAKV,cAAc,CAClB,gBAAgB,CAAC,EAAE,MAAM,EACzB,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAC9B,OAAO,CAAC,IAAI,CAAC;IAKhB,kBAAkB,IAAI,eAAe,GAAG,IAAI;IAK5C,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM;IAmBhC,mBAAmB,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC;IAa7C,OAAO,CAAC,YAAY;IAgDpB,OAAO,CAAC,gBAAgB;IAqBxB,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAW/B,SAAS,IAAI,MAAM,GAAG,IAAI;IAK1B,YAAY,IAAI,IAAI;IAMpB,OAAO,CAAC,iBAAiB;IA4BzB,OAAO,CAAC,sBAAsB;IAgD9B,OAAO,CAAC,iBAAiB;IAazB,OAAO,CAAC,iBAAiB;IAuBzB,OAAO,CAAC,mBAAmB;IAiJ3B,OAAO,CAAC,kBAAkB;IAY1B,OAAO,CAAC,iBAAiB;IAkBzB,OAAO,CAAC,oBAAoB;IAqD5B,OAAO,CAAC,oBAAoB;IAY5B,OAAO,CAAC,uBAAuB;IAS/B,OAAO,CAAC,wBAAwB;IAQhC,OAAO,CAAC,cAAc;IAMtB,OAAO,CAAC,gBAAgB;IA+ExB,OAAO,CAAC,kBAAkB;IAwB1B,OAAO,CAAC,qBAAqB;IAQvB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAK5B,SAAS,IAAI;QACX,WAAW,EAAE,OAAO,CAAC;QACrB,OAAO,EAAE,WAAW,GAAG,IAAI,CAAC;QAC5B,SAAS,EAAE,MAAM,CAAC;QAClB,MAAM,EAAE,OAAO,CAAC;QAChB,cAAc,EAAE;YACd,OAAO,EAAE,OAAO,CAAC;YACjB,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;SACvC,CAAC;QACF,WAAW,EAAE;YACX,OAAO,EAAE,OAAO,CAAC;YACjB,WAAW,EAAE,MAAM,CAAC;SACrB,CAAC;KACH;IAkBD,OAAO,IAAI,IAAI;CAOhB"}
|
|
@@ -49,8 +49,17 @@ export class AttributionSDK {
|
|
|
49
49
|
}
|
|
50
50
|
try {
|
|
51
51
|
this.logger.info("Initializing GetuAI Attribution SDK");
|
|
52
|
-
// Initialize storage
|
|
53
|
-
|
|
52
|
+
// Initialize storage (IndexedDB failure should not block SDK initialization)
|
|
53
|
+
try {
|
|
54
|
+
await this.storage.init();
|
|
55
|
+
}
|
|
56
|
+
catch (storageError) {
|
|
57
|
+
this.logger.warn("Storage initialization failed (IndexedDB may be unavailable), continuing with basic features:", storageError);
|
|
58
|
+
// Continue initialization even if IndexedDB fails
|
|
59
|
+
// User ID and other features using cookie/localStorage will still work
|
|
60
|
+
}
|
|
61
|
+
// Initialize user ID (from config or existing storage)
|
|
62
|
+
this.initializeUserId();
|
|
54
63
|
// Initialize session
|
|
55
64
|
this.initializeSession();
|
|
56
65
|
// Extract and store UTM data from current URL
|
|
@@ -123,10 +132,12 @@ export class AttributionSDK {
|
|
|
123
132
|
last_activity: this.session?.lastActivity,
|
|
124
133
|
page_views: this.session?.pageViews,
|
|
125
134
|
};
|
|
135
|
+
// Use provided tracking_user_id or fallback to stored user ID
|
|
136
|
+
const finalUserId = tracking_user_id || this.getUserId();
|
|
126
137
|
const event = {
|
|
127
138
|
event_id: generateId(),
|
|
128
139
|
event_type: eventType,
|
|
129
|
-
tracking_user_id:
|
|
140
|
+
tracking_user_id: finalUserId || undefined,
|
|
130
141
|
timestamp: getTimestamp(),
|
|
131
142
|
event_data: eventData,
|
|
132
143
|
context: { page: pageContext, session: sessionContext },
|
|
@@ -223,6 +234,42 @@ export class AttributionSDK {
|
|
|
223
234
|
async trackEmailVerification(tracking_user_id, verificationData) {
|
|
224
235
|
await this.trackEvent(EventType.EMAIL_VERIFICATION, verificationData, tracking_user_id);
|
|
225
236
|
}
|
|
237
|
+
// Track purchase with auto user ID (object parameter format)
|
|
238
|
+
async trackPurchaseAuto(options) {
|
|
239
|
+
const userId = options.tracking_user_id || this.getUserId();
|
|
240
|
+
if (!userId) {
|
|
241
|
+
this.logger.error("❌ trackPurchaseAuto requires tracking_user_id. Please set user ID using setUserId() or pass it in options.tracking_user_id.");
|
|
242
|
+
return;
|
|
243
|
+
}
|
|
244
|
+
await this.trackEvent(EventType.PURCHASE, options.purchaseData, userId, options.revenue, options.currency || Currency.USD);
|
|
245
|
+
}
|
|
246
|
+
// Track login with auto user ID (object parameter format)
|
|
247
|
+
async trackLoginAuto(options) {
|
|
248
|
+
const userId = options.tracking_user_id || this.getUserId();
|
|
249
|
+
if (!userId) {
|
|
250
|
+
this.logger.error("❌ trackLoginAuto requires tracking_user_id. Please set user ID using setUserId() or pass it in options.tracking_user_id.");
|
|
251
|
+
return;
|
|
252
|
+
}
|
|
253
|
+
await this.trackEvent(EventType.LOGIN, options.loginData, userId);
|
|
254
|
+
}
|
|
255
|
+
// Track signup with auto user ID (object parameter format)
|
|
256
|
+
async trackSignupAuto(options) {
|
|
257
|
+
const userId = options.tracking_user_id || this.getUserId();
|
|
258
|
+
if (!userId) {
|
|
259
|
+
this.logger.error("❌ trackSignupAuto requires tracking_user_id. Please set user ID using setUserId() or pass it in options.tracking_user_id.");
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
262
|
+
await this.trackEvent(EventType.SIGNUP, options.signupData, userId);
|
|
263
|
+
}
|
|
264
|
+
// Track email verification with auto user ID (object parameter format)
|
|
265
|
+
async trackEmailVerificationAuto(options) {
|
|
266
|
+
const userId = options.tracking_user_id || this.getUserId();
|
|
267
|
+
if (!userId) {
|
|
268
|
+
this.logger.error("❌ trackEmailVerificationAuto requires tracking_user_id. Please set user ID using setUserId() or pass it in options.tracking_user_id.");
|
|
269
|
+
return;
|
|
270
|
+
}
|
|
271
|
+
await this.trackEvent(EventType.EMAIL_VERIFICATION, options.verificationData, userId);
|
|
272
|
+
}
|
|
226
273
|
// Track product view
|
|
227
274
|
async trackProductView(tracking_user_id, productData) {
|
|
228
275
|
await this.trackEvent(EventType.PRODUCT_VIEW, productData, tracking_user_id);
|
|
@@ -308,6 +355,42 @@ export class AttributionSDK {
|
|
|
308
355
|
this.logger.debug("UTM params for event:", filteredParams);
|
|
309
356
|
return filteredParams;
|
|
310
357
|
}
|
|
358
|
+
// Initialize user ID
|
|
359
|
+
initializeUserId() {
|
|
360
|
+
// If userId is provided in config, set it (will override existing)
|
|
361
|
+
if (this.config.userId) {
|
|
362
|
+
this.setUserId(this.config.userId);
|
|
363
|
+
this.logger.info(`👤 User ID initialized from config: ${this.config.userId}`);
|
|
364
|
+
}
|
|
365
|
+
else {
|
|
366
|
+
// Check if user ID already exists in storage
|
|
367
|
+
const existingUserId = this.getUserId();
|
|
368
|
+
if (existingUserId) {
|
|
369
|
+
this.logger.debug(`👤 Existing user ID found: ${existingUserId}`);
|
|
370
|
+
}
|
|
371
|
+
else {
|
|
372
|
+
this.logger.debug("👤 No user ID found, will be set when user identifies");
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
// Set user ID
|
|
377
|
+
setUserId(userId) {
|
|
378
|
+
if (!userId || userId.trim() === "") {
|
|
379
|
+
this.logger.warn("Cannot set empty user ID");
|
|
380
|
+
return;
|
|
381
|
+
}
|
|
382
|
+
this.storage.setUserId(userId);
|
|
383
|
+
this.logger.info(`👤 User ID set: ${userId}`);
|
|
384
|
+
}
|
|
385
|
+
// Get user ID
|
|
386
|
+
getUserId() {
|
|
387
|
+
return this.storage.getUserId();
|
|
388
|
+
}
|
|
389
|
+
// Remove user ID
|
|
390
|
+
removeUserId() {
|
|
391
|
+
this.storage.removeUserId();
|
|
392
|
+
this.logger.info("👤 User ID removed");
|
|
393
|
+
}
|
|
311
394
|
// Initialize user session
|
|
312
395
|
initializeSession() {
|
|
313
396
|
const existingSession = this.storage.getSession();
|
|
@@ -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:()=>$}),function(t){t.PAGE_VIEW="page_view",t.PAGE_CLICK="page_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||(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];function s(){return Math.floor(Date.now()/1e3)}function o(){try{const t="__localStorage_test__";return localStorage.setItem(t,t),localStorage.removeItem(t),!0}catch{return!1}}class c{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 u(){return document.referrer||""}function l(){return window.location.href}function h(){return document.title||""}function d(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 g(t,e=["utm_source","utm_medium","utm_campaign"]){const i={};return e.forEach(e=>{t[e]&&(i[e]=t[e])}),i}function m(t){try{const e=t||window.location.href;return new URL(e).search}catch(t){return""}}function p(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 w{constructor(t){this.logger=t}get(t){try{if(!o())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(!o())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{o()&&localStorage.removeItem(t)}catch(t){this.logger.error("Error removing from localStorage:",t)}}clear(){try{o()&&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 f{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 y{constructor(t){this.UTM_STORAGE_KEY="attribution_utm_data",this.SESSION_STORAGE_KEY="attribution_session",this.logger=t,this.localStorage=new w(t),this.indexedDB=new f(t)}async init(){await this.indexedDB.init()}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()}}class S{constructor(t,e,i,r=100,n=2e3,a=3,s=1e3,o){this.queue=[],this.processing=!1,this.batchTimer=null,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){this.queue.push(t),this.logger.debug(`Event added to queue: ${t.event_type}`),a.includes(t.event_type)?(this.logger.debug(`Immediate event detected: ${t.event_type}, processing immediately`),this.processImmediate(t)):this.scheduleBatchProcessing()}async process(){if(!this.processing&&0!==this.queue.length){this.processing=!0;try{const t=this.queue.splice(0,this.batchSize);this.logger.debug(`Processing ${t.length} events from queue`),await this.sendEvents(t),this.logger.info(`Successfully processed ${t.length} events`)}catch(t){this.logger.error("Failed to process events:",t);const e=this.queue.splice(0,this.batchSize);return this.queue.unshift(...e),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.logger.info(`Immediate event processed successfully: ${t.event_type}`)}catch(e){this.logger.error(`Failed to process immediate event: ${t.event_type}`,e),this.queue.unshift(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()}}class b{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||s()})),sdk_version:"2.0.0"};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 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 T{constructor(t){this.session=null,this.initialized=!1,this.autoTrackEnabled=!1,this.pageViewTrackTimes=new Map,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 c(this.config.enableDebug),this.storage=new y(this.logger),this.httpClient=new b(this.logger,this.config.apiKey,this.config.apiEndpoint||n,this.config.maxRetries,this.config.retryDelay),this.queue=new S(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"),await this.storage.init(),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.config.autoTrackPageView&&(this.logger.info("📄 Auto track page view enabled (including SPA route tracking)"),this.lastTrackedPath=this.getCurrentPath(),this.setupSPATracking(),setTimeout(()=>{this.trackPageView().then(()=>{this.logger.info("✅ Auto track page view completed")}).catch(t=>this.logger.error("❌ Auto track page view failed:",t))},100))}catch(t){throw this.logger.error("Failed to initialize SDK:",t),t}}async trackEvent(t,i,r,n,a=e.USD){if(this.initialized)try{const e=l();let o=null;try{o=await this.fetchPublicIP()}catch(t){}const c={domain:"undefined"!=typeof window?window.location.hostname:null,path:"undefined"!=typeof window?window.location.pathname:null,title:h(),referrer:u(),url:e.split("?")[0],querystring:m(e),query_params:p(e),ip_address:o},d={session_id:this.session?.sessionId,start_time:this.session?.startTime,last_activity:this.session?.lastActivity,page_views:this.session?.pageViews},g={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:t,tracking_user_id:r,timestamp:s(),event_data:i,context:{page:c,session:d},revenue:n,currency:a,...this.getUTMParams()};this.logger.debug(`Tracking event: ${t}`,g),this.queue.add(g)}catch(e){this.logger.error(`Failed to track event ${t}:`,e)}else this.logger.warn("SDK not initialized, event not tracked")}async trackPageView(e,i){const r=l(),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:h(),referrer:u(),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)}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 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)}getAttributionData(){return this.storage.getUTMData()}addUTMToURL(t){if(!this.config.enableCrossDomainUTM)return t;const e=this.getAttributionData();return e?d(t,g(e.lastTouch,this.config.crossDomainUTMParams)):t}getCurrentUTMParams(){const t=this.getAttributionData();return t?g(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}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=l(),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);if(this.logger.debug("Extracting UTM params from URL:",t),this.logger.debug("Found UTM params:",e),0===Object.keys(e).length)return void this.logger.debug("No UTM parameters found in URL");const i={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||"",timestamp:Date.now()},r=this.getAttributionData(),n={firstTouch:r?.firstTouch||i,lastTouch:i,touchpoints:r?.touchpoints||[],expiresAt:Date.now()+2592e6};r&&r.lastTouch.utm_source===i.utm_source&&r.lastTouch.utm_campaign===i.utm_campaign||n.touchpoints.push(i),this.storage.storeUTMData(n),this.logger.info("UTM data extracted and stored successfully:",i),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);this.trackEvent(t.FORM_SUBMIT,{...r,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(const t of r){const e=t.name;e&&!t.disabled&&(n.has(e)||n.set(e,[]),n.get(e).push(t))}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}serializeFormValue(t){return t instanceof File?{file_name:t.name,file_size:t.size,file_type:t.type}:t}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=g(r.lastTouch,this.config.crossDomainUTMParams);if(0===Object.keys(n).length)return void this.logger.debug("No UTM parameters to pass");const a=d(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",()=>{"visible"===document.visibilityState&&this.updateSessionActivity()})}setupBeforeUnloadHandler(){window.addEventListener("beforeunload",()=>{this.updateSessionActivity(),this.queue.flush()})}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.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 v=null;async function k(t){if(v)return console.warn("GetuAI SDK: Already initialized"),v;try{if(v=new T(t),await v.init(),t.enableDebug&&(window.GetuAISDK=v),console.log("GetuAI Attribution SDK initialized successfully"),"undefined"!=typeof window){const t=new CustomEvent("getuaiSDKReady",{detail:{sdk:v}});window.dispatchEvent(t)}return v}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 _(){return v}function E(){return new Promise((t,e)=>{if(v)return void t(v);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 D(t,i,r,n,a=e.USD){const s=_();s?await s.trackEvent(t,i,r,n,a):console.warn("GetuAI SDK: Not initialized. Call init() first.")}async function A(t,e){const i=_();i?await i.trackPageView(t,e):console.warn("GetuAI SDK: Not initialized. Call init() first.")}async function P(t,i,r=e.USD,n){const a=_();a?await a.trackPurchase(t,i,r,n):console.warn("GetuAI SDK: Not initialized. Call init() first.")}async function I(t,e){const i=_();i?await i.trackLogin(t,e):console.warn("GetuAI SDK: Not initialized. Call init() first.")}async function U(t,e){const i=_();i?await i.trackSignup(t,e):console.warn("GetuAI SDK: Not initialized. Call init() first.")}async function x(t,e){const i=_();i?await i.trackFormSubmit(t,e):console.warn("GetuAI SDK: Not initialized. Call init() first.")}async function C(t,e){const i=_();i?await i.trackVideoPlay(t,e):console.warn("GetuAI SDK: Not initialized. Call init() first.")}async function M(t,e){const i=_();i?await i.trackEmailVerification(t,e):console.warn("GetuAI SDK: Not initialized. Call init() first.")}async function K(t,e){const i=_();i?await i.trackProductView(t,e):console.warn("GetuAI SDK: Not initialized. Call init() first.")}async function z(t,e){const i=_();i?await i.trackAddToCart(t,e):console.warn("GetuAI SDK: Not initialized. Call init() first.")}async function O(t,e){const i=_();i?await i.trackPageClick(t,e):console.warn("GetuAI SDK: Not initialized. Call init() first.")}function L(){const t=_();return t?t.getAttributionData():null}async function N(){const t=_();t?await t.flush():console.warn("GetuAI SDK: Not initialized. Call init() first.")}function R(){const t=_();return t?t.getStatus():null}function V(t){const e=_();return e?e.addUTMToURL(t):(console.warn("GetuAI SDK: Not initialized. Call init() first."),t)}function F(){const t=_();return t?t.getCurrentUTMParams():{}}function G(){v&&(v.destroy(),v=null,console.log("GetuAI SDK destroyed"))}class q extends T{static async init(t){return await k(t)}static async trackEvent(t,i,r,n,a=e.USD){return await D(t,i,r,n,a)}static async trackPageView(t,e){return await A(t,e)}static async trackPurchase(t,i,r=e.USD,n){return await P(t,i,r,n)}static async trackLogin(t,e){return await I(t,e)}static async trackSignup(t,e){return await U(t,e)}static async trackFormSubmit(t,e){return await x(t,e)}static async trackVideoPlay(t,e){return await C(t,e)}static async trackEmailVerification(t,e){return await M(t,e)}static async trackProductView(t,e){return await K(t,e)}static async trackAddToCart(t,e){return await z(t,e)}static async trackPageClick(t,e){return await O(t,e)}static getAttributionData(){return L()}static async flush(){return await N()}static getStatus(){return R()}static addUTMToURL(t){return V(t)}static getCurrentUTMParams(){return F()}static destroy(){G()}static getSDK(){return _()}static waitForSDK(){return E()}}"undefined"!=typeof document&&function(){if(v)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?k({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")}):console.warn("GetuAI SDK: No API key provided. Please add data-api-key attribute to script tag.")}(),"undefined"!=typeof window&&(window.getuaiSDK={init:k,getSDK:_,waitForSDK:E,trackEvent:D,trackPageView:A,trackPageClick:O,trackPurchase:P,trackLogin:I,trackSignup:U,trackFormSubmit:x,trackVideoPlay:C,trackEmailVerification:M,trackProductView:K,trackAddToCart:z,getAttributionData:L,flush:N,getStatus:R,addUTMToURL:V,getCurrentUTMParams:F,destroy:G,EventType:t,Currency:e,AttributionSDK:q},window.init=k,window.waitForSDK=E,window.trackEvent=D,window.trackPageView=A,window.trackPageClick=O,window.trackPurchase=P,window.trackLogin=I,window.trackSignup=U,window.trackFormSubmit=x,window.trackVideoPlay=C,window.trackEmailVerification=M,window.trackProductView=K,window.trackAddToCart=z,window.getAttributionData=L,window.flush=N,window.getStatus=R,window.addUTMToURL=V,window.getCurrentUTMParams=F,window.destroy=G,window.AttributionSDK=q);const $={init:k,getSDK:_,waitForSDK:E,trackEvent:D,trackPageView:A,trackPageClick:O,trackPurchase:P,trackLogin:I,trackSignup:U,trackFormSubmit:x,trackVideoPlay:C,trackEmailVerification:M,trackProductView:K,trackAddToCart:z,getAttributionData:L,flush:N,getStatus:R,addUTMToURL:V,getCurrentUTMParams:F,destroy:G,EventType:t,Currency:e,AttributionSDK:q};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:()=>J}),function(t){t.PAGE_VIEW="page_view",t.PAGE_CLICK="page_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||(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];function s(){return Math.floor(Date.now()/1e3)}function o(){try{const t="__localStorage_test__";return localStorage.setItem(t,t),localStorage.removeItem(t),!0}catch{return!1}}class c{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 u(){return document.referrer||""}function l(){return window.location.href}function d(){return document.title||""}function h(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 g(t,e=["utm_source","utm_medium","utm_campaign"]){const i={};return e.forEach(e=>{t[e]&&(i[e]=t[e])}),i}function m(t){try{const e=t||window.location.href;return new URL(e).search}catch(t){return""}}function p(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 f{constructor(t){this.logger=t}get(t){try{if(!o())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(!o())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{o()&&localStorage.removeItem(t)}catch(t){this.logger.error("Error removing from localStorage:",t)}}clear(){try{o()&&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 w{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 y{constructor(t){this.UTM_STORAGE_KEY="attribution_utm_data",this.SESSION_STORAGE_KEY="attribution_session",this.USER_ID_KEY="getuai_user_id",this.USER_ID_COOKIE_EXPIRES=365,this.logger=t,this.localStorage=new f(t),this.indexedDB=new w(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()}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()}}class S{constructor(t,e,i,r=100,n=2e3,a=3,s=1e3,o){this.queue=[],this.processing=!1,this.batchTimer=null,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){this.queue.push(t),this.logger.debug(`Event added to queue: ${t.event_type}`),a.includes(t.event_type)?(this.logger.debug(`Immediate event detected: ${t.event_type}, processing immediately`),this.processImmediate(t)):this.scheduleBatchProcessing()}async process(){if(!this.processing&&0!==this.queue.length){this.processing=!0;try{const t=this.queue.splice(0,this.batchSize);this.logger.debug(`Processing ${t.length} events from queue`),await this.sendEvents(t),this.logger.info(`Successfully processed ${t.length} events`)}catch(t){this.logger.error("Failed to process events:",t);const e=this.queue.splice(0,this.batchSize);return this.queue.unshift(...e),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.logger.info(`Immediate event processed successfully: ${t.event_type}`)}catch(e){this.logger.error(`Failed to process immediate event: ${t.event_type}`,e),this.queue.unshift(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()}}class k{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||s()})),sdk_version:"2.0.0"};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 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 _{constructor(t){this.session=null,this.initialized=!1,this.autoTrackEnabled=!1,this.pageViewTrackTimes=new Map,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 c(this.config.enableDebug),this.storage=new y(this.logger),this.httpClient=new k(this.logger,this.config.apiKey,this.config.apiEndpoint||n,this.config.maxRetries,this.config.retryDelay),this.queue=new S(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");try{await 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.config.autoTrackPageView&&(this.logger.info("📄 Auto track page view enabled (including SPA route tracking)"),this.lastTrackedPath=this.getCurrentPath(),this.setupSPATracking(),setTimeout(()=>{this.trackPageView().then(()=>{this.logger.info("✅ Auto track page view completed")}).catch(t=>this.logger.error("❌ Auto track page view failed:",t))},100))}catch(t){throw this.logger.error("Failed to initialize SDK:",t),t}}async trackEvent(t,i,r,n,a=e.USD){if(this.initialized)try{const e=l();let o=null;try{o=await this.fetchPublicIP()}catch(t){}const c={domain:"undefined"!=typeof window?window.location.hostname:null,path:"undefined"!=typeof window?window.location.pathname:null,title:d(),referrer:u(),url:e.split("?")[0],querystring:m(e),query_params:p(e),ip_address:o},h={session_id:this.session?.sessionId,start_time:this.session?.startTime,last_activity:this.session?.lastActivity,page_views:this.session?.pageViews},g=r||this.getUserId(),f={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:t,tracking_user_id:g||void 0,timestamp:s(),event_data:i,context:{page:c,session:h},revenue:n,currency:a,...this.getUTMParams()};this.logger.debug(`Tracking event: ${t}`,f),this.queue.add(f)}catch(e){this.logger.error(`Failed to track event ${t}:`,e)}else this.logger.warn("SDK not initialized, event not tracked")}async trackPageView(e,i){const r=l(),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:d(),referrer:u(),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)}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 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)}getAttributionData(){return this.storage.getUTMData()}addUTMToURL(t){if(!this.config.enableCrossDomainUTM)return t;const e=this.getAttributionData();return e?h(t,g(e.lastTouch,this.config.crossDomainUTMParams)):t}getCurrentUTMParams(){const t=this.getAttributionData();return t?g(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}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")}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=l(),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);if(this.logger.debug("Extracting UTM params from URL:",t),this.logger.debug("Found UTM params:",e),0===Object.keys(e).length)return void this.logger.debug("No UTM parameters found in URL");const i={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||"",timestamp:Date.now()},r=this.getAttributionData(),n={firstTouch:r?.firstTouch||i,lastTouch:i,touchpoints:r?.touchpoints||[],expiresAt:Date.now()+2592e6};r&&r.lastTouch.utm_source===i.utm_source&&r.lastTouch.utm_campaign===i.utm_campaign||n.touchpoints.push(i),this.storage.storeUTMData(n),this.logger.info("UTM data extracted and stored successfully:",i),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);this.trackEvent(t.FORM_SUBMIT,{...r,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(const t of r){const e=t.name;e&&!t.disabled&&(n.has(e)||n.set(e,[]),n.get(e).push(t))}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}serializeFormValue(t){return t instanceof File?{file_name:t.name,file_size:t.size,file_type:t.type}:t}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=g(r.lastTouch,this.config.crossDomainUTMParams);if(0===Object.keys(n).length)return void this.logger.debug("No UTM parameters to pass");const a=h(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",()=>{"visible"===document.visibilityState&&this.updateSessionActivity()})}setupBeforeUnloadHandler(){window.addEventListener("beforeunload",()=>{this.updateSessionActivity(),this.queue.flush()})}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.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 b=null;async function v(t){if(b)return console.warn("GetuAI SDK: Already initialized"),b;try{if(b=new _(t),await b.init(),t.enableDebug&&(window.GetuAISDK=b),console.log("GetuAI Attribution SDK initialized successfully"),"undefined"!=typeof window){const t=new CustomEvent("getuaiSDKReady",{detail:{sdk:b}});window.dispatchEvent(t)}return b}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 I(){return b}function E(){return new Promise((t,e)=>{if(b)return void t(b);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 T(t,i,r,n,a=e.USD){const s=I();s?await s.trackEvent(t,i,r,n,a):console.warn("GetuAI SDK: Not initialized. Call init() first.")}async function D(t,e){const i=I();i?await i.trackPageView(t,e):console.warn("GetuAI SDK: Not initialized. Call init() first.")}async function A(t,i,r=e.USD,n){const a=I();a?await a.trackPurchase(t,i,r,n):console.warn("GetuAI SDK: Not initialized. Call init() first.")}async function P(t,e){const i=I();i?await i.trackLogin(t,e):console.warn("GetuAI SDK: Not initialized. Call init() first.")}async function U(t,e){const i=I();i?await i.trackSignup(t,e):console.warn("GetuAI SDK: Not initialized. Call init() first.")}async function C(t,e){const i=I();i?await i.trackFormSubmit(t,e):console.warn("GetuAI SDK: Not initialized. Call init() first.")}async function x(t,e){const i=I();i?await i.trackVideoPlay(t,e):console.warn("GetuAI SDK: Not initialized. Call init() first.")}async function K(t,e){const i=I();i?await i.trackEmailVerification(t,e):console.warn("GetuAI SDK: Not initialized. Call init() first.")}async function R(t){const e=I();e?await e.trackPurchaseAuto(t):console.warn("GetuAI SDK: Not initialized. Call init() first.")}async function M(t){const e=I();e?await e.trackLoginAuto(t):console.warn("GetuAI SDK: Not initialized. Call init() first.")}async function z(t){const e=I();e?await e.trackSignupAuto(t):console.warn("GetuAI SDK: Not initialized. Call init() first.")}async function N(t){const e=I();e?await e.trackEmailVerificationAuto(t):console.warn("GetuAI SDK: Not initialized. Call init() first.")}async function L(t,e){const i=I();i?await i.trackProductView(t,e):console.warn("GetuAI SDK: Not initialized. Call init() first.")}async function O(t,e){const i=I();i?await i.trackAddToCart(t,e):console.warn("GetuAI SDK: Not initialized. Call init() first.")}async function V(t,e){const i=I();i?await i.trackPageClick(t,e):console.warn("GetuAI SDK: Not initialized. Call init() first.")}function F(){const t=I();return t?t.getAttributionData():null}async function G(){const t=I();t?await t.flush():console.warn("GetuAI SDK: Not initialized. Call init() first.")}function q(){const t=I();return t?t.getStatus():null}function $(t){const e=I();return e?e.addUTMToURL(t):(console.warn("GetuAI SDK: Not initialized. Call init() first."),t)}function B(){const t=I();return t?t.getCurrentUTMParams():{}}function j(t){const e=I();e?e.setUserId(t):console.warn("GetuAI SDK: Not initialized. Call init() first.")}function H(){const t=I();return t?t.getUserId():(console.warn("GetuAI SDK: Not initialized. Call init() first."),null)}function Y(){const t=I();t?t.removeUserId():console.warn("GetuAI SDK: Not initialized. Call init() first.")}function W(){b&&(b.destroy(),b=null,console.log("GetuAI SDK destroyed"))}class Q extends _{static async init(t){return await v(t)}static async trackEvent(t,i,r,n,a=e.USD){return await T(t,i,r,n,a)}static async trackPageView(t,e){return await D(t,e)}static async trackPurchase(t,i,r=e.USD,n){return await A(t,i,r,n)}static async trackLogin(t,e){return await P(t,e)}static async trackSignup(t,e){return await U(t,e)}static async trackFormSubmit(t,e){return await C(t,e)}static async trackVideoPlay(t,e){return await x(t,e)}static async trackEmailVerification(t,e){return await K(t,e)}static async trackPurchaseAuto(t){return await R(t)}static async trackLoginAuto(t){return await M(t)}static async trackSignupAuto(t){return await z(t)}static async trackEmailVerificationAuto(t){return await N(t)}static async trackProductView(t,e){return await L(t,e)}static async trackAddToCart(t,e){return await O(t,e)}static async trackPageClick(t,e){return await V(t,e)}static getAttributionData(){return F()}static async flush(){return await G()}static getStatus(){return q()}static addUTMToURL(t){return $(t)}static getCurrentUTMParams(){return B()}static setUserId(t){j(t)}static getUserId(){return H()}static removeUserId(){Y()}static destroy(){W()}static getSDK(){return I()}static waitForSDK(){return E()}}"undefined"!=typeof document&&function(){if(b)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?v({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:v,getSDK:I,waitForSDK:E,trackEvent:T,trackPageView:D,trackPageClick:V,trackPurchase:A,trackLogin:P,trackSignup:U,trackFormSubmit:C,trackVideoPlay:x,trackEmailVerification:K,trackProductView:L,trackAddToCart:O,trackPurchaseAuto:R,trackLoginAuto:M,trackSignupAuto:z,trackEmailVerificationAuto:N,getAttributionData:F,flush:G,getStatus:q,addUTMToURL:$,getCurrentUTMParams:B,setUserId:j,getUserId:H,removeUserId:Y,destroy:W,EventType:t,Currency:e,AttributionSDK:Q},window.init=v,window.waitForSDK=E,window.trackEvent=T,window.trackPageView=D,window.trackPageClick=V,window.trackPurchase=A,window.trackLogin=P,window.trackSignup=U,window.trackFormSubmit=C,window.trackVideoPlay=x,window.trackEmailVerification=K,window.trackProductView=L,window.trackAddToCart=O,window.trackPurchaseAuto=R,window.trackLoginAuto=M,window.trackSignupAuto=z,window.trackEmailVerificationAuto=N,window.getAttributionData=F,window.flush=G,window.getStatus=q,window.addUTMToURL=$,window.getCurrentUTMParams=B,window.setUserId=j,window.getUserId=H,window.removeUserId=Y,window.destroy=W,window.AttributionSDK=Q);const J={init:v,getSDK:I,waitForSDK:E,trackEvent:T,trackPageView:D,trackPageClick:V,trackPurchase:A,trackLogin:P,trackSignup:U,trackFormSubmit:C,trackVideoPlay:x,trackEmailVerification:K,trackProductView:L,trackAddToCart:O,trackPurchaseAuto:R,trackLoginAuto:M,trackSignupAuto:z,trackEmailVerificationAuto:N,getAttributionData:F,flush:G,getStatus:q,addUTMToURL:$,getCurrentUTMParams:B,setUserId:j,getUserId:H,removeUserId:Y,destroy:W,EventType:t,Currency:e,AttributionSDK:Q};return r.default})());
|