aurea-tracking-sdk 1.1.2 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -19,6 +19,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
19
19
  // src/index.ts
20
20
  var index_exports = {};
21
21
  __export(index_exports, {
22
+ AureaSDK: () => AureaSDK,
22
23
  getAurea: () => getAurea,
23
24
  identifyUser: () => identifyUser,
24
25
  initAurea: () => initAurea,
@@ -45,6 +46,15 @@ var AureaSDK = class {
45
46
  fcp: false,
46
47
  ttfb: false
47
48
  };
49
+ // NEW: Funnel tracking
50
+ this.currentStage = "awareness";
51
+ this.stageHistory = [];
52
+ this.microConversions = [];
53
+ this.isInCheckout = false;
54
+ // NEW: Event categorization tracking
55
+ this.eventCategoryStats = /* @__PURE__ */ new Map();
56
+ // NEW: Track which events have been fired (for trackOnce behavior)
57
+ this.trackedOnceEvents = /* @__PURE__ */ new Set();
48
58
  this.config = {
49
59
  apiUrl: "http://localhost:3000/api",
50
60
  debug: false,
@@ -98,11 +108,13 @@ var AureaSDK = class {
98
108
  this.startPurchasePolling();
99
109
  this.trackWebVitals();
100
110
  this.trackSessionTiming();
111
+ this.initializeFunnelTracking();
101
112
  if (this.config.debug) {
102
113
  console.log("[Aurea SDK] Initialized", {
103
114
  sessionId: this.sessionId,
104
115
  anonymousId: this.anonymousId,
105
- userId: this.userId
116
+ userId: this.userId,
117
+ currentStage: this.currentStage
106
118
  });
107
119
  }
108
120
  }
@@ -180,6 +192,372 @@ var AureaSDK = class {
180
192
  console.log("[Aurea SDK] Conversion tracked:", data);
181
193
  }
182
194
  }
195
+ /**
196
+ * Register custom event categories
197
+ * Allows users to define their own event categorization and auto-stage progression
198
+ *
199
+ * @example
200
+ * aurea.registerEventCategories({
201
+ * 'video_started': {
202
+ * category: 'engagement',
203
+ * advanceTo: 'interest',
204
+ * value: 30,
205
+ * description: 'User started watching sales video'
206
+ * },
207
+ * 'pricing_viewed': {
208
+ * category: 'intent',
209
+ * advanceTo: 'desire',
210
+ * value: 60
211
+ * },
212
+ * 'buy_button_clicked': {
213
+ * category: 'conversion',
214
+ * value: 90
215
+ * }
216
+ * })
217
+ */
218
+ registerEventCategories(categories) {
219
+ if (!this.config.eventCategories) {
220
+ this.config.eventCategories = {};
221
+ }
222
+ this.config.eventCategories = {
223
+ ...this.config.eventCategories,
224
+ ...categories
225
+ };
226
+ if (this.config.debug) {
227
+ console.log("[Aurea SDK] Event categories registered:", categories);
228
+ }
229
+ }
230
+ /**
231
+ * Track a categorized event with automatic stage progression
232
+ * This is the recommended way to track events - user defines their own event names
233
+ *
234
+ * @param eventName - User-defined event name (e.g., 'video_started', 'pricing_clicked')
235
+ * @param properties - Additional event properties
236
+ * @param options - Override category/value/stage for this specific event
237
+ *
238
+ * @example
239
+ * // Using pre-registered category
240
+ * aurea.trackEvent('video_started', { duration: 120 })
241
+ *
242
+ * // One-off event with inline category
243
+ * aurea.trackEvent('custom_action', { foo: 'bar' }, {
244
+ * category: 'engagement',
245
+ * value: 40,
246
+ * advanceTo: 'interest'
247
+ * })
248
+ */
249
+ trackEvent(eventName, properties, options) {
250
+ const categoryConfig = this.config.eventCategories?.[eventName];
251
+ const category = options?.category || categoryConfig?.category || "custom";
252
+ const value = options?.value ?? categoryConfig?.value ?? 50;
253
+ const advanceTo = options?.advanceTo || categoryConfig?.advanceTo;
254
+ const description = options?.description || categoryConfig?.description;
255
+ const trackOnce = categoryConfig?.trackOnce ?? false;
256
+ if (trackOnce) {
257
+ if (this.trackedOnceEvents.has(eventName)) {
258
+ if (this.config.debug) {
259
+ console.log(`[Aurea SDK] Event skipped (already tracked once): ${eventName}`);
260
+ }
261
+ return;
262
+ }
263
+ this.trackedOnceEvents.add(eventName);
264
+ }
265
+ this.eventCategoryStats.set(
266
+ category,
267
+ (this.eventCategoryStats.get(category) || 0) + 1
268
+ );
269
+ const autoAdvance = this.config.autoAdvanceStages !== false;
270
+ if (autoAdvance && advanceTo && advanceTo !== this.currentStage) {
271
+ this.enterStage(advanceTo);
272
+ }
273
+ this.track(eventName, {
274
+ ...properties,
275
+ // Add categorization metadata
276
+ _category: category,
277
+ _value: value,
278
+ _description: description,
279
+ _currentStage: this.currentStage,
280
+ _categoryStats: Object.fromEntries(this.eventCategoryStats),
281
+ _trackedOnce: trackOnce
282
+ // Include for backend reference
283
+ });
284
+ if (this.config.debug) {
285
+ console.log(`[Aurea SDK] Event tracked: ${eventName} [${category}] (value: ${value}, stage: ${this.currentStage}${trackOnce ? ", once-only" : ""})`);
286
+ }
287
+ }
288
+ /**
289
+ * Get current event category statistics
290
+ */
291
+ getCategoryStats() {
292
+ return Object.fromEntries(this.eventCategoryStats);
293
+ }
294
+ /**
295
+ * Get current funnel stage
296
+ */
297
+ getCurrentStage() {
298
+ return this.currentStage;
299
+ }
300
+ /**
301
+ * Get stage history
302
+ */
303
+ getStageHistory() {
304
+ return this.stageHistory.map((entry, index) => {
305
+ const nextEntry = this.stageHistory[index + 1];
306
+ const durationMs = nextEntry ? nextEntry.enteredAt - entry.enteredAt : Date.now() - entry.enteredAt;
307
+ return {
308
+ stage: entry.stage,
309
+ enteredAt: entry.enteredAt,
310
+ durationMs
311
+ };
312
+ });
313
+ }
314
+ /**
315
+ * Enter a new funnel stage
316
+ */
317
+ enterStage(stage) {
318
+ const previousStage = this.currentStage;
319
+ this.currentStage = stage;
320
+ this.stageHistory.push({
321
+ stage,
322
+ enteredAt: Date.now()
323
+ });
324
+ this.track("funnel_stage_entered", {
325
+ stage,
326
+ previousStage,
327
+ stageHistory: this.stageHistory,
328
+ timeInPreviousStage: this.getTimeInCurrentStage(previousStage)
329
+ });
330
+ if (this.config.debug) {
331
+ console.log(`[Aurea SDK] Entered funnel stage: ${previousStage} \u2192 ${stage}`);
332
+ }
333
+ }
334
+ /**
335
+ * Track micro-conversion (engagement signals)
336
+ *
337
+ * @deprecated Use trackEvent() instead for more flexibility
338
+ * This method is kept for backward compatibility
339
+ *
340
+ * @param type - Type of micro-conversion (e.g., 'video_played', 'faq_opened')
341
+ * @param value - Impact score 0-100 (how strong this signal is)
342
+ * @param properties - Additional metadata
343
+ * @param autoAdvanceStage - Whether to automatically advance funnel stage (default: true)
344
+ */
345
+ trackMicroConversion(type, value = 50, properties, autoAdvanceStage = true) {
346
+ const microConversion = {
347
+ type,
348
+ value,
349
+ properties
350
+ };
351
+ this.microConversions.push(microConversion);
352
+ const categoryConfig = this.config.eventCategories?.[type];
353
+ if (autoAdvanceStage) {
354
+ if (categoryConfig?.advanceTo && categoryConfig.advanceTo !== this.currentStage) {
355
+ this.enterStage(categoryConfig.advanceTo);
356
+ } else {
357
+ const suggestedStage = this.getSuggestedStageFromMicroConversion(type, value);
358
+ if (suggestedStage && suggestedStage !== this.currentStage) {
359
+ this.enterStage(suggestedStage);
360
+ }
361
+ }
362
+ }
363
+ this.track("micro_conversion", {
364
+ microConversionType: type,
365
+ value,
366
+ category: categoryConfig?.category || "custom",
367
+ currentStage: this.currentStage,
368
+ totalMicroConversions: this.microConversions.length,
369
+ ...properties
370
+ });
371
+ if (this.config.debug) {
372
+ console.log(`[Aurea SDK] Micro-conversion: ${type} (value: ${value}, stage: ${this.currentStage})`);
373
+ }
374
+ }
375
+ /**
376
+ * Determine suggested funnel stage based on micro-conversion type
377
+ * This uses common patterns to automatically progress users through the funnel
378
+ */
379
+ getSuggestedStageFromMicroConversion(type, value) {
380
+ const currentStageIndex = this.getFunnelStageOrder().indexOf(this.currentStage);
381
+ const interestSignals = [
382
+ "video_played",
383
+ "video_started",
384
+ "video_25_percent",
385
+ "video_50_percent",
386
+ "scroll_depth_25",
387
+ "scroll_depth_50",
388
+ "page_section_viewed",
389
+ "testimonial_viewed",
390
+ "feature_section_viewed"
391
+ ];
392
+ const desireSignals = [
393
+ "video_75_percent",
394
+ "video_completed",
395
+ "faq_opened",
396
+ "pricing_viewed",
397
+ "cta_section_viewed",
398
+ "scroll_depth_75",
399
+ "comparison_table_viewed",
400
+ "guarantee_section_viewed",
401
+ "bonus_section_viewed"
402
+ ];
403
+ const checkoutIntentSignals = [
404
+ "buy_button_hovered",
405
+ "checkout_button_clicked",
406
+ "add_to_cart",
407
+ "pricing_tier_selected"
408
+ ];
409
+ if (interestSignals.includes(type) && currentStageIndex < 1) {
410
+ return "interest";
411
+ }
412
+ if (desireSignals.includes(type) && currentStageIndex < 2) {
413
+ return "desire";
414
+ }
415
+ if (checkoutIntentSignals.includes(type) && currentStageIndex < 3) {
416
+ if (this.config.debug) {
417
+ console.log(`[Aurea SDK] High checkout intent detected: ${type}`);
418
+ }
419
+ }
420
+ if (value >= 80 && currentStageIndex < 2) {
421
+ return "desire";
422
+ }
423
+ return null;
424
+ }
425
+ /**
426
+ * Get funnel stage order for comparison
427
+ */
428
+ getFunnelStageOrder() {
429
+ return ["awareness", "interest", "desire", "checkout", "purchase", "abandoned"];
430
+ }
431
+ /**
432
+ * Track checkout started (user clicked buy button)
433
+ * This DOES NOT end the session - preserves context for when user returns
434
+ */
435
+ checkoutStarted(product) {
436
+ this.isInCheckout = true;
437
+ this.checkoutStartedAt = Date.now();
438
+ this.enterStage("checkout");
439
+ const checkoutContext = {
440
+ originalSessionId: this.sessionId,
441
+ anonymousId: this.anonymousId,
442
+ checkoutStartedAt: this.checkoutStartedAt,
443
+ product,
444
+ currentStage: this.currentStage,
445
+ priorStages: this.stageHistory.map((h) => h.stage),
446
+ utmSource: this.getUTMParam("utm_source"),
447
+ utmCampaign: this.getUTMParam("utm_campaign"),
448
+ utmMedium: this.getUTMParam("utm_medium")
449
+ };
450
+ if (typeof localStorage !== "undefined") {
451
+ localStorage.setItem("aurea_checkout_context", JSON.stringify(checkoutContext));
452
+ }
453
+ this.track("checkout_started", {
454
+ productId: product.productId,
455
+ productName: product.productName,
456
+ price: product.price,
457
+ currency: product.currency || "USD",
458
+ quantity: product.quantity || 1,
459
+ variant: product.variant,
460
+ sessionDurationSoFar: Math.floor((Date.now() - this.sessionStartTime) / 1e3),
461
+ microConversionsCount: this.microConversions.length,
462
+ priorStages: this.stageHistory.map((h) => h.stage)
463
+ });
464
+ if (this.config.debug) {
465
+ console.log("[Aurea SDK] Checkout started:", product);
466
+ console.log("[Aurea SDK] Session context preserved for return journey");
467
+ }
468
+ }
469
+ /**
470
+ * Track checkout completed (purchase successful)
471
+ * Links back to original session if user returned from external checkout
472
+ */
473
+ checkoutCompleted(data) {
474
+ let checkoutContext = null;
475
+ let checkoutDuration;
476
+ if (typeof localStorage !== "undefined") {
477
+ const stored = localStorage.getItem("aurea_checkout_context");
478
+ if (stored) {
479
+ try {
480
+ checkoutContext = JSON.parse(stored);
481
+ checkoutDuration = Math.floor((Date.now() - checkoutContext.checkoutStartedAt) / 1e3);
482
+ localStorage.removeItem("aurea_checkout_context");
483
+ } catch (e) {
484
+ console.error("[Aurea SDK] Failed to parse checkout context:", e);
485
+ }
486
+ }
487
+ }
488
+ this.enterStage("purchase");
489
+ this.track("checkout_completed", {
490
+ orderId: data.orderId,
491
+ revenue: data.revenue,
492
+ currency: data.currency || "USD",
493
+ paymentMethod: data.paymentMethod,
494
+ products: data.products,
495
+ // Link to original session if available
496
+ originalSessionId: checkoutContext?.originalSessionId,
497
+ checkoutDuration,
498
+ totalSessionDuration: Math.floor((Date.now() - this.sessionStartTime) / 1e3),
499
+ // Attribution data from original session
500
+ originalUtmSource: checkoutContext?.utmSource,
501
+ originalUtmCampaign: checkoutContext?.utmCampaign,
502
+ originalUtmMedium: checkoutContext?.utmMedium,
503
+ priorStages: checkoutContext?.priorStages || this.stageHistory.map((h) => h.stage),
504
+ // Engagement data
505
+ microConversionsCount: this.microConversions.length,
506
+ microConversions: this.microConversions
507
+ });
508
+ this.conversion({
509
+ type: "purchase",
510
+ revenue: data.revenue,
511
+ currency: data.currency,
512
+ orderId: data.orderId
513
+ });
514
+ if (this.config.debug) {
515
+ console.log("[Aurea SDK] Checkout completed:", data);
516
+ if (checkoutContext) {
517
+ console.log("[Aurea SDK] Session bridged - linked to original session:", checkoutContext.originalSessionId);
518
+ }
519
+ }
520
+ }
521
+ /**
522
+ * Track checkout abandoned (user left without completing)
523
+ */
524
+ checkoutAbandoned(reason) {
525
+ if (!this.isInCheckout) {
526
+ return;
527
+ }
528
+ const checkoutDuration = this.checkoutStartedAt ? Math.floor((Date.now() - this.checkoutStartedAt) / 1e3) : void 0;
529
+ this.enterStage("abandoned");
530
+ this.track("checkout_abandoned", {
531
+ reason: reason || "unknown",
532
+ checkoutDuration,
533
+ sessionDuration: Math.floor((Date.now() - this.sessionStartTime) / 1e3),
534
+ microConversionsCount: this.microConversions.length,
535
+ priorStages: this.stageHistory.map((h) => h.stage)
536
+ });
537
+ this.isInCheckout = false;
538
+ if (this.config.debug) {
539
+ console.log("[Aurea SDK] Checkout abandoned:", reason);
540
+ }
541
+ }
542
+ /**
543
+ * Get time spent in a specific stage (in seconds)
544
+ */
545
+ getTimeInCurrentStage(stage) {
546
+ const stageEntries = this.stageHistory.filter((h) => h.stage === stage);
547
+ if (stageEntries.length === 0) return 0;
548
+ const lastEntry = stageEntries[stageEntries.length - 1];
549
+ const nextEntry = this.stageHistory.find((h) => h.enteredAt > lastEntry.enteredAt);
550
+ const endTime = nextEntry ? nextEntry.enteredAt : Date.now();
551
+ return Math.floor((endTime - lastEntry.enteredAt) / 1e3);
552
+ }
553
+ /**
554
+ * Get UTM parameter from current URL
555
+ */
556
+ getUTMParam(param) {
557
+ if (typeof window === "undefined") return void 0;
558
+ const urlParams = new URLSearchParams(window.location.search);
559
+ return urlParams.get(param) || void 0;
560
+ }
183
561
  /**
184
562
  * Get or create session ID
185
563
  */
@@ -581,6 +959,9 @@ var AureaSDK = class {
581
959
  window.addEventListener("touchstart", resetInactivityTimer, { passive: true });
582
960
  window.addEventListener("beforeunload", () => {
583
961
  const now = Date.now();
962
+ if (this.isInCheckout && this.currentStage === "checkout") {
963
+ this.checkoutAbandoned("page_close");
964
+ }
584
965
  if (this.isPageVisible) {
585
966
  this.activeTime += now - this.lastActiveTimestamp;
586
967
  }
@@ -627,6 +1008,53 @@ var AureaSDK = class {
627
1008
  console.log("[Aurea SDK] Session timing tracking enabled");
628
1009
  }
629
1010
  }
1011
+ /**
1012
+ * Initialize funnel tracking
1013
+ * Checks for returning checkout users and sets initial stage
1014
+ */
1015
+ initializeFunnelTracking() {
1016
+ if (typeof window === "undefined") return;
1017
+ if (typeof localStorage !== "undefined") {
1018
+ const checkoutContext = localStorage.getItem("aurea_checkout_context");
1019
+ if (checkoutContext) {
1020
+ try {
1021
+ const context = JSON.parse(checkoutContext);
1022
+ const urlParams = new URLSearchParams(window.location.search);
1023
+ const purchased = urlParams.get("purchased") === "true";
1024
+ if (purchased) {
1025
+ if (this.config.debug) {
1026
+ console.log(
1027
+ "[Aurea SDK] Returning from successful checkout - awaiting checkoutCompleted() call"
1028
+ );
1029
+ }
1030
+ } else {
1031
+ const timeSinceCheckout = Date.now() - context.checkoutStartedAt;
1032
+ const fifteenMinutes = 15 * 60 * 1e3;
1033
+ if (timeSinceCheckout > fifteenMinutes) {
1034
+ if (this.config.debug) {
1035
+ console.log(
1036
+ "[Aurea SDK] Checkout abandoned (15+ min elapsed)"
1037
+ );
1038
+ }
1039
+ }
1040
+ }
1041
+ } catch (err) {
1042
+ console.error(
1043
+ "[Aurea SDK] Failed to parse checkout context:",
1044
+ err
1045
+ );
1046
+ localStorage.removeItem("aurea_checkout_context");
1047
+ }
1048
+ }
1049
+ }
1050
+ this.stageHistory.push({
1051
+ stage: "awareness",
1052
+ enteredAt: Date.now()
1053
+ });
1054
+ if (this.config.debug) {
1055
+ console.log("[Aurea SDK] Funnel tracking initialized - Stage: awareness");
1056
+ }
1057
+ }
630
1058
  /**
631
1059
  * Generate unique ID
632
1060
  */
@@ -690,6 +1118,7 @@ function trackPage(name, properties) {
690
1118
  }
691
1119
  // Annotate the CommonJS export names for ESM import in node:
692
1120
  0 && (module.exports = {
1121
+ AureaSDK,
693
1122
  getAurea,
694
1123
  identifyUser,
695
1124
  initAurea,