aurea-tracking-sdk 1.1.1 → 1.2.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.mjs CHANGED
@@ -17,6 +17,13 @@ var AureaSDK = class {
17
17
  fcp: false,
18
18
  ttfb: false
19
19
  };
20
+ // NEW: Funnel tracking
21
+ this.currentStage = "awareness";
22
+ this.stageHistory = [];
23
+ this.microConversions = [];
24
+ this.isInCheckout = false;
25
+ // NEW: Event categorization tracking
26
+ this.eventCategoryStats = /* @__PURE__ */ new Map();
20
27
  this.config = {
21
28
  apiUrl: "http://localhost:3000/api",
22
29
  debug: false,
@@ -49,9 +56,9 @@ var AureaSDK = class {
49
56
  console.warn("[Aurea SDK] Already initialized");
50
57
  return;
51
58
  }
52
- if (this.config.respectDoNotTrack && typeof navigator !== "undefined" && (navigator.doNotTrack === "1" || navigator.msDoNotTrack === "1")) {
59
+ if (this.config.respectDoNotTrack && typeof navigator !== "undefined" && (navigator.doNotTrack === "1" || navigator.msDoNotTrack === "1" || navigator.globalPrivacyControl === true)) {
53
60
  if (this.config.debug) {
54
- console.log("[Aurea SDK] Do Not Track enabled, skipping initialization");
61
+ console.log("[Aurea SDK] Do Not Track or Global Privacy Control enabled, skipping initialization");
55
62
  }
56
63
  return;
57
64
  }
@@ -70,11 +77,13 @@ var AureaSDK = class {
70
77
  this.startPurchasePolling();
71
78
  this.trackWebVitals();
72
79
  this.trackSessionTiming();
80
+ this.initializeFunnelTracking();
73
81
  if (this.config.debug) {
74
82
  console.log("[Aurea SDK] Initialized", {
75
83
  sessionId: this.sessionId,
76
84
  anonymousId: this.anonymousId,
77
- userId: this.userId
85
+ userId: this.userId,
86
+ currentStage: this.currentStage
78
87
  });
79
88
  }
80
89
  }
@@ -152,6 +161,360 @@ var AureaSDK = class {
152
161
  console.log("[Aurea SDK] Conversion tracked:", data);
153
162
  }
154
163
  }
164
+ /**
165
+ * Register custom event categories
166
+ * Allows users to define their own event categorization and auto-stage progression
167
+ *
168
+ * @example
169
+ * aurea.registerEventCategories({
170
+ * 'video_started': {
171
+ * category: 'engagement',
172
+ * advanceTo: 'interest',
173
+ * value: 30,
174
+ * description: 'User started watching sales video'
175
+ * },
176
+ * 'pricing_viewed': {
177
+ * category: 'intent',
178
+ * advanceTo: 'desire',
179
+ * value: 60
180
+ * },
181
+ * 'buy_button_clicked': {
182
+ * category: 'conversion',
183
+ * value: 90
184
+ * }
185
+ * })
186
+ */
187
+ registerEventCategories(categories) {
188
+ if (!this.config.eventCategories) {
189
+ this.config.eventCategories = {};
190
+ }
191
+ this.config.eventCategories = {
192
+ ...this.config.eventCategories,
193
+ ...categories
194
+ };
195
+ if (this.config.debug) {
196
+ console.log("[Aurea SDK] Event categories registered:", categories);
197
+ }
198
+ }
199
+ /**
200
+ * Track a categorized event with automatic stage progression
201
+ * This is the recommended way to track events - user defines their own event names
202
+ *
203
+ * @param eventName - User-defined event name (e.g., 'video_started', 'pricing_clicked')
204
+ * @param properties - Additional event properties
205
+ * @param options - Override category/value/stage for this specific event
206
+ *
207
+ * @example
208
+ * // Using pre-registered category
209
+ * aurea.trackEvent('video_started', { duration: 120 })
210
+ *
211
+ * // One-off event with inline category
212
+ * aurea.trackEvent('custom_action', { foo: 'bar' }, {
213
+ * category: 'engagement',
214
+ * value: 40,
215
+ * advanceTo: 'interest'
216
+ * })
217
+ */
218
+ trackEvent(eventName, properties, options) {
219
+ const categoryConfig = this.config.eventCategories?.[eventName];
220
+ const category = options?.category || categoryConfig?.category || "custom";
221
+ const value = options?.value ?? categoryConfig?.value ?? 50;
222
+ const advanceTo = options?.advanceTo || categoryConfig?.advanceTo;
223
+ const description = options?.description || categoryConfig?.description;
224
+ this.eventCategoryStats.set(
225
+ category,
226
+ (this.eventCategoryStats.get(category) || 0) + 1
227
+ );
228
+ const autoAdvance = this.config.autoAdvanceStages !== false;
229
+ if (autoAdvance && advanceTo && advanceTo !== this.currentStage) {
230
+ this.enterStage(advanceTo);
231
+ }
232
+ this.track(eventName, {
233
+ ...properties,
234
+ // Add categorization metadata
235
+ _category: category,
236
+ _value: value,
237
+ _description: description,
238
+ _currentStage: this.currentStage,
239
+ _categoryStats: Object.fromEntries(this.eventCategoryStats)
240
+ });
241
+ if (this.config.debug) {
242
+ console.log(`[Aurea SDK] Event tracked: ${eventName} [${category}] (value: ${value}, stage: ${this.currentStage})`);
243
+ }
244
+ }
245
+ /**
246
+ * Get current event category statistics
247
+ */
248
+ getCategoryStats() {
249
+ return Object.fromEntries(this.eventCategoryStats);
250
+ }
251
+ /**
252
+ * Get current funnel stage
253
+ */
254
+ getCurrentStage() {
255
+ return this.currentStage;
256
+ }
257
+ /**
258
+ * Get stage history
259
+ */
260
+ getStageHistory() {
261
+ return this.stageHistory.map((entry, index) => {
262
+ const nextEntry = this.stageHistory[index + 1];
263
+ const durationMs = nextEntry ? nextEntry.enteredAt - entry.enteredAt : Date.now() - entry.enteredAt;
264
+ return {
265
+ stage: entry.stage,
266
+ enteredAt: entry.enteredAt,
267
+ durationMs
268
+ };
269
+ });
270
+ }
271
+ /**
272
+ * Enter a new funnel stage
273
+ */
274
+ enterStage(stage) {
275
+ const previousStage = this.currentStage;
276
+ this.currentStage = stage;
277
+ this.stageHistory.push({
278
+ stage,
279
+ enteredAt: Date.now()
280
+ });
281
+ this.track("funnel_stage_entered", {
282
+ stage,
283
+ previousStage,
284
+ stageHistory: this.stageHistory,
285
+ timeInPreviousStage: this.getTimeInCurrentStage(previousStage)
286
+ });
287
+ if (this.config.debug) {
288
+ console.log(`[Aurea SDK] Entered funnel stage: ${previousStage} \u2192 ${stage}`);
289
+ }
290
+ }
291
+ /**
292
+ * Track micro-conversion (engagement signals)
293
+ *
294
+ * @deprecated Use trackEvent() instead for more flexibility
295
+ * This method is kept for backward compatibility
296
+ *
297
+ * @param type - Type of micro-conversion (e.g., 'video_played', 'faq_opened')
298
+ * @param value - Impact score 0-100 (how strong this signal is)
299
+ * @param properties - Additional metadata
300
+ * @param autoAdvanceStage - Whether to automatically advance funnel stage (default: true)
301
+ */
302
+ trackMicroConversion(type, value = 50, properties, autoAdvanceStage = true) {
303
+ const microConversion = {
304
+ type,
305
+ value,
306
+ properties
307
+ };
308
+ this.microConversions.push(microConversion);
309
+ const categoryConfig = this.config.eventCategories?.[type];
310
+ if (autoAdvanceStage) {
311
+ if (categoryConfig?.advanceTo && categoryConfig.advanceTo !== this.currentStage) {
312
+ this.enterStage(categoryConfig.advanceTo);
313
+ } else {
314
+ const suggestedStage = this.getSuggestedStageFromMicroConversion(type, value);
315
+ if (suggestedStage && suggestedStage !== this.currentStage) {
316
+ this.enterStage(suggestedStage);
317
+ }
318
+ }
319
+ }
320
+ this.track("micro_conversion", {
321
+ microConversionType: type,
322
+ value,
323
+ category: categoryConfig?.category || "custom",
324
+ currentStage: this.currentStage,
325
+ totalMicroConversions: this.microConversions.length,
326
+ ...properties
327
+ });
328
+ if (this.config.debug) {
329
+ console.log(`[Aurea SDK] Micro-conversion: ${type} (value: ${value}, stage: ${this.currentStage})`);
330
+ }
331
+ }
332
+ /**
333
+ * Determine suggested funnel stage based on micro-conversion type
334
+ * This uses common patterns to automatically progress users through the funnel
335
+ */
336
+ getSuggestedStageFromMicroConversion(type, value) {
337
+ const currentStageIndex = this.getFunnelStageOrder().indexOf(this.currentStage);
338
+ const interestSignals = [
339
+ "video_played",
340
+ "video_started",
341
+ "video_25_percent",
342
+ "video_50_percent",
343
+ "scroll_depth_25",
344
+ "scroll_depth_50",
345
+ "page_section_viewed",
346
+ "testimonial_viewed",
347
+ "feature_section_viewed"
348
+ ];
349
+ const desireSignals = [
350
+ "video_75_percent",
351
+ "video_completed",
352
+ "faq_opened",
353
+ "pricing_viewed",
354
+ "cta_section_viewed",
355
+ "scroll_depth_75",
356
+ "comparison_table_viewed",
357
+ "guarantee_section_viewed",
358
+ "bonus_section_viewed"
359
+ ];
360
+ const checkoutIntentSignals = [
361
+ "buy_button_hovered",
362
+ "checkout_button_clicked",
363
+ "add_to_cart",
364
+ "pricing_tier_selected"
365
+ ];
366
+ if (interestSignals.includes(type) && currentStageIndex < 1) {
367
+ return "interest";
368
+ }
369
+ if (desireSignals.includes(type) && currentStageIndex < 2) {
370
+ return "desire";
371
+ }
372
+ if (checkoutIntentSignals.includes(type) && currentStageIndex < 3) {
373
+ if (this.config.debug) {
374
+ console.log(`[Aurea SDK] High checkout intent detected: ${type}`);
375
+ }
376
+ }
377
+ if (value >= 80 && currentStageIndex < 2) {
378
+ return "desire";
379
+ }
380
+ return null;
381
+ }
382
+ /**
383
+ * Get funnel stage order for comparison
384
+ */
385
+ getFunnelStageOrder() {
386
+ return ["awareness", "interest", "desire", "checkout", "purchase", "abandoned"];
387
+ }
388
+ /**
389
+ * Track checkout started (user clicked buy button)
390
+ * This DOES NOT end the session - preserves context for when user returns
391
+ */
392
+ checkoutStarted(product) {
393
+ this.isInCheckout = true;
394
+ this.checkoutStartedAt = Date.now();
395
+ this.enterStage("checkout");
396
+ const checkoutContext = {
397
+ originalSessionId: this.sessionId,
398
+ anonymousId: this.anonymousId,
399
+ checkoutStartedAt: this.checkoutStartedAt,
400
+ product,
401
+ currentStage: this.currentStage,
402
+ priorStages: this.stageHistory.map((h) => h.stage),
403
+ utmSource: this.getUTMParam("utm_source"),
404
+ utmCampaign: this.getUTMParam("utm_campaign"),
405
+ utmMedium: this.getUTMParam("utm_medium")
406
+ };
407
+ if (typeof localStorage !== "undefined") {
408
+ localStorage.setItem("aurea_checkout_context", JSON.stringify(checkoutContext));
409
+ }
410
+ this.track("checkout_started", {
411
+ productId: product.productId,
412
+ productName: product.productName,
413
+ price: product.price,
414
+ currency: product.currency || "USD",
415
+ quantity: product.quantity || 1,
416
+ variant: product.variant,
417
+ sessionDurationSoFar: Math.floor((Date.now() - this.sessionStartTime) / 1e3),
418
+ microConversionsCount: this.microConversions.length,
419
+ priorStages: this.stageHistory.map((h) => h.stage)
420
+ });
421
+ if (this.config.debug) {
422
+ console.log("[Aurea SDK] Checkout started:", product);
423
+ console.log("[Aurea SDK] Session context preserved for return journey");
424
+ }
425
+ }
426
+ /**
427
+ * Track checkout completed (purchase successful)
428
+ * Links back to original session if user returned from external checkout
429
+ */
430
+ checkoutCompleted(data) {
431
+ let checkoutContext = null;
432
+ let checkoutDuration;
433
+ if (typeof localStorage !== "undefined") {
434
+ const stored = localStorage.getItem("aurea_checkout_context");
435
+ if (stored) {
436
+ try {
437
+ checkoutContext = JSON.parse(stored);
438
+ checkoutDuration = Math.floor((Date.now() - checkoutContext.checkoutStartedAt) / 1e3);
439
+ localStorage.removeItem("aurea_checkout_context");
440
+ } catch (e) {
441
+ console.error("[Aurea SDK] Failed to parse checkout context:", e);
442
+ }
443
+ }
444
+ }
445
+ this.enterStage("purchase");
446
+ this.track("checkout_completed", {
447
+ orderId: data.orderId,
448
+ revenue: data.revenue,
449
+ currency: data.currency || "USD",
450
+ paymentMethod: data.paymentMethod,
451
+ products: data.products,
452
+ // Link to original session if available
453
+ originalSessionId: checkoutContext?.originalSessionId,
454
+ checkoutDuration,
455
+ totalSessionDuration: Math.floor((Date.now() - this.sessionStartTime) / 1e3),
456
+ // Attribution data from original session
457
+ originalUtmSource: checkoutContext?.utmSource,
458
+ originalUtmCampaign: checkoutContext?.utmCampaign,
459
+ originalUtmMedium: checkoutContext?.utmMedium,
460
+ priorStages: checkoutContext?.priorStages || this.stageHistory.map((h) => h.stage),
461
+ // Engagement data
462
+ microConversionsCount: this.microConversions.length,
463
+ microConversions: this.microConversions
464
+ });
465
+ this.conversion({
466
+ type: "purchase",
467
+ revenue: data.revenue,
468
+ currency: data.currency,
469
+ orderId: data.orderId
470
+ });
471
+ if (this.config.debug) {
472
+ console.log("[Aurea SDK] Checkout completed:", data);
473
+ if (checkoutContext) {
474
+ console.log("[Aurea SDK] Session bridged - linked to original session:", checkoutContext.originalSessionId);
475
+ }
476
+ }
477
+ }
478
+ /**
479
+ * Track checkout abandoned (user left without completing)
480
+ */
481
+ checkoutAbandoned(reason) {
482
+ if (!this.isInCheckout) {
483
+ return;
484
+ }
485
+ const checkoutDuration = this.checkoutStartedAt ? Math.floor((Date.now() - this.checkoutStartedAt) / 1e3) : void 0;
486
+ this.enterStage("abandoned");
487
+ this.track("checkout_abandoned", {
488
+ reason: reason || "unknown",
489
+ checkoutDuration,
490
+ sessionDuration: Math.floor((Date.now() - this.sessionStartTime) / 1e3),
491
+ microConversionsCount: this.microConversions.length,
492
+ priorStages: this.stageHistory.map((h) => h.stage)
493
+ });
494
+ this.isInCheckout = false;
495
+ if (this.config.debug) {
496
+ console.log("[Aurea SDK] Checkout abandoned:", reason);
497
+ }
498
+ }
499
+ /**
500
+ * Get time spent in a specific stage (in seconds)
501
+ */
502
+ getTimeInCurrentStage(stage) {
503
+ const stageEntries = this.stageHistory.filter((h) => h.stage === stage);
504
+ if (stageEntries.length === 0) return 0;
505
+ const lastEntry = stageEntries[stageEntries.length - 1];
506
+ const nextEntry = this.stageHistory.find((h) => h.enteredAt > lastEntry.enteredAt);
507
+ const endTime = nextEntry ? nextEntry.enteredAt : Date.now();
508
+ return Math.floor((endTime - lastEntry.enteredAt) / 1e3);
509
+ }
510
+ /**
511
+ * Get UTM parameter from current URL
512
+ */
513
+ getUTMParam(param) {
514
+ if (typeof window === "undefined") return void 0;
515
+ const urlParams = new URLSearchParams(window.location.search);
516
+ return urlParams.get(param) || void 0;
517
+ }
155
518
  /**
156
519
  * Get or create session ID
157
520
  */
@@ -553,23 +916,102 @@ var AureaSDK = class {
553
916
  window.addEventListener("touchstart", resetInactivityTimer, { passive: true });
554
917
  window.addEventListener("beforeunload", () => {
555
918
  const now = Date.now();
919
+ if (this.isInCheckout && this.currentStage === "checkout") {
920
+ this.checkoutAbandoned("page_close");
921
+ }
556
922
  if (this.isPageVisible) {
557
923
  this.activeTime += now - this.lastActiveTimestamp;
558
924
  }
559
925
  const totalDuration = Math.floor((now - this.sessionStartTime) / 1e3);
560
926
  const activeTimeSeconds = Math.floor(this.activeTime / 1e3);
561
- this.track("session_end", {
562
- duration: totalDuration,
563
- activeTime: activeTimeSeconds,
564
- idleTime: totalDuration - activeTimeSeconds,
565
- engagementRate: totalDuration > 0 ? activeTimeSeconds / totalDuration * 100 : 0
927
+ const sessionEndEvent = {
928
+ eventId: this.generateEventId(),
929
+ eventName: "session_end",
930
+ properties: {
931
+ duration: totalDuration,
932
+ activeTime: activeTimeSeconds,
933
+ idleTime: totalDuration - activeTimeSeconds,
934
+ engagementRate: totalDuration > 0 ? activeTimeSeconds / totalDuration * 100 : 0
935
+ },
936
+ context: this.buildContext(),
937
+ timestamp: Date.now()
938
+ };
939
+ const url = `${this.config.apiUrl}/track/events`;
940
+ const payload = JSON.stringify({
941
+ events: [sessionEndEvent],
942
+ batch: false
566
943
  });
944
+ try {
945
+ fetch(url, {
946
+ method: "POST",
947
+ headers: {
948
+ "Content-Type": "application/json",
949
+ "X-Aurea-API-Key": this.config.apiKey,
950
+ "X-Aurea-Funnel-ID": this.config.funnelId
951
+ },
952
+ body: payload,
953
+ keepalive: true
954
+ // Critical: ensures request completes even if page unloads
955
+ }).catch(() => {
956
+ });
957
+ if (this.config.debug) {
958
+ console.log("[Aurea SDK] Session end sent with keepalive");
959
+ }
960
+ } catch (error) {
961
+ }
567
962
  this.flushEvents();
568
963
  });
569
964
  if (this.config.debug) {
570
965
  console.log("[Aurea SDK] Session timing tracking enabled");
571
966
  }
572
967
  }
968
+ /**
969
+ * Initialize funnel tracking
970
+ * Checks for returning checkout users and sets initial stage
971
+ */
972
+ initializeFunnelTracking() {
973
+ if (typeof window === "undefined") return;
974
+ if (typeof localStorage !== "undefined") {
975
+ const checkoutContext = localStorage.getItem("aurea_checkout_context");
976
+ if (checkoutContext) {
977
+ try {
978
+ const context = JSON.parse(checkoutContext);
979
+ const urlParams = new URLSearchParams(window.location.search);
980
+ const purchased = urlParams.get("purchased") === "true";
981
+ if (purchased) {
982
+ if (this.config.debug) {
983
+ console.log(
984
+ "[Aurea SDK] Returning from successful checkout - awaiting checkoutCompleted() call"
985
+ );
986
+ }
987
+ } else {
988
+ const timeSinceCheckout = Date.now() - context.checkoutStartedAt;
989
+ const fifteenMinutes = 15 * 60 * 1e3;
990
+ if (timeSinceCheckout > fifteenMinutes) {
991
+ if (this.config.debug) {
992
+ console.log(
993
+ "[Aurea SDK] Checkout abandoned (15+ min elapsed)"
994
+ );
995
+ }
996
+ }
997
+ }
998
+ } catch (err) {
999
+ console.error(
1000
+ "[Aurea SDK] Failed to parse checkout context:",
1001
+ err
1002
+ );
1003
+ localStorage.removeItem("aurea_checkout_context");
1004
+ }
1005
+ }
1006
+ }
1007
+ this.stageHistory.push({
1008
+ stage: "awareness",
1009
+ enteredAt: Date.now()
1010
+ });
1011
+ if (this.config.debug) {
1012
+ console.log("[Aurea SDK] Funnel tracking initialized - Stage: awareness");
1013
+ }
1014
+ }
573
1015
  /**
574
1016
  * Generate unique ID
575
1017
  */
@@ -632,6 +1074,7 @@ function trackPage(name, properties) {
632
1074
  }
633
1075
  }
634
1076
  export {
1077
+ AureaSDK,
635
1078
  getAurea,
636
1079
  identifyUser,
637
1080
  initAurea,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aurea-tracking-sdk",
3
- "version": "1.1.1",
3
+ "version": "1.2.0",
4
4
  "description": "Standalone tracking SDK for Aurea CRM external funnels",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",