@tracelog/lib 3.2.0 → 3.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.
@@ -393,6 +393,20 @@ interface Config {
393
393
  projectId: string;
394
394
  /** Enable Shopify cart attribute linking for webhook revenue attribution. */
395
395
  shopify?: boolean;
396
+ /**
397
+ * Opt into "Accuracy mode": send events through the merchant's own first-party
398
+ * subdomain (`https://{projectId}.{rootDomain}/collect`, a CNAME → middleware)
399
+ * instead of the default hosted endpoint (`https://ingest.tracelog.io/p/{projectId}/collect`).
400
+ *
401
+ * Leave unset (the default) and events post to the hosted endpoint with zero DNS
402
+ * setup — the snippet works the moment it is pasted. Only enable this once the
403
+ * CNAME (and the domain-ownership TXT record) are verified; the dashboard surfaces
404
+ * this flag in the snippet only after verification, so the lib never points at an
405
+ * unresolved subdomain. First-party transport recovers ~10–30% of visits that
406
+ * ad-blockers strip from third-party hosts.
407
+ * @default false
408
+ */
409
+ firstParty?: boolean;
396
410
  /**
397
411
  * Emit a low-frequency diagnostic "health beacon" when ingest is rejected at the domain
398
412
  * gate (HTTP 403) so the dashboard can tell the merchant their snippet is alive but their
@@ -1474,6 +1488,8 @@ declare class PerformanceHandler extends StateManager {
1474
1488
  private readonly observers;
1475
1489
  private vitalThresholds;
1476
1490
  private navigationCounter;
1491
+ private currentNavBase;
1492
+ private currentNavId;
1477
1493
  constructor(eventManager: EventManager);
1478
1494
  /**
1479
1495
  * Starts tracking Web Vitals and performance metrics.
@@ -1505,21 +1521,23 @@ declare class PerformanceHandler extends StateManager {
1505
1521
  private sendVital;
1506
1522
  private trackWebVital;
1507
1523
  /**
1508
- * Generates a unique navigation identifier for deduplication.
1509
- *
1510
- * **Purpose**: Creates deterministic IDs to prevent duplicate Web Vitals reporting
1511
- * across multiple metrics for the same navigation event.
1512
- *
1513
- * **ID Format**: `{timestamp}_{pathname}` or `{timestamp}_{pathname}_{counter}`
1514
- *
1515
- * **Edge Case Handling**:
1516
- * - If multiple navigations occur to the same pathname in the same millisecond,
1517
- * a counter suffix is appended (e.g., `1234.56_/home_2`)
1518
- * - Counter only added when > 1 to minimize ID length for common case
1519
- *
1520
- * **Why Deterministic**:
1521
- * - Previous implementation used random string duplicate metrics on page reload
1522
- * - Now: Same navigation = same ID = proper deduplication via reportedByNav Map
1524
+ * Generates a deterministic navigation identifier for deduplication.
1525
+ *
1526
+ * **Purpose**: Every call within the same navigation must return the SAME id,
1527
+ * so `reportedByNav` can collapse duplicate Web Vitals (one emission per
1528
+ * metric type per navigation — critical for the fallback observers, which
1529
+ * fire per entry batch).
1530
+ *
1531
+ * **ID Format**: `{startTime}_{pathname}` or `{startTime}_{pathname}_{counter}`
1532
+ *
1533
+ * **Determinism**:
1534
+ * - Base id is derived only from the navigation entry's `startTime` (0 by spec
1535
+ * for the document navigation — no `performance.now()` fallback, which made
1536
+ * every call unique) and the current pathname.
1537
+ * - The id is cached per navigation; the counter suffix is appended ONLY on a
1538
+ * real collision: a new navigation whose base id was already reported
1539
+ * (SPA revisit to the same path, e.g. A→B→A), so the revisit's vitals are
1540
+ * not suppressed by the first visit's dedup entries.
1523
1541
  *
1524
1542
  * @returns Navigation ID string or null if navigation timing unavailable
1525
1543
  *
@@ -393,6 +393,20 @@ interface Config {
393
393
  projectId: string;
394
394
  /** Enable Shopify cart attribute linking for webhook revenue attribution. */
395
395
  shopify?: boolean;
396
+ /**
397
+ * Opt into "Accuracy mode": send events through the merchant's own first-party
398
+ * subdomain (`https://{projectId}.{rootDomain}/collect`, a CNAME → middleware)
399
+ * instead of the default hosted endpoint (`https://ingest.tracelog.io/p/{projectId}/collect`).
400
+ *
401
+ * Leave unset (the default) and events post to the hosted endpoint with zero DNS
402
+ * setup — the snippet works the moment it is pasted. Only enable this once the
403
+ * CNAME (and the domain-ownership TXT record) are verified; the dashboard surfaces
404
+ * this flag in the snippet only after verification, so the lib never points at an
405
+ * unresolved subdomain. First-party transport recovers ~10–30% of visits that
406
+ * ad-blockers strip from third-party hosts.
407
+ * @default false
408
+ */
409
+ firstParty?: boolean;
396
410
  /**
397
411
  * Emit a low-frequency diagnostic "health beacon" when ingest is rejected at the domain
398
412
  * gate (HTTP 403) so the dashboard can tell the merchant their snippet is alive but their
@@ -1474,6 +1488,8 @@ declare class PerformanceHandler extends StateManager {
1474
1488
  private readonly observers;
1475
1489
  private vitalThresholds;
1476
1490
  private navigationCounter;
1491
+ private currentNavBase;
1492
+ private currentNavId;
1477
1493
  constructor(eventManager: EventManager);
1478
1494
  /**
1479
1495
  * Starts tracking Web Vitals and performance metrics.
@@ -1505,21 +1521,23 @@ declare class PerformanceHandler extends StateManager {
1505
1521
  private sendVital;
1506
1522
  private trackWebVital;
1507
1523
  /**
1508
- * Generates a unique navigation identifier for deduplication.
1509
- *
1510
- * **Purpose**: Creates deterministic IDs to prevent duplicate Web Vitals reporting
1511
- * across multiple metrics for the same navigation event.
1512
- *
1513
- * **ID Format**: `{timestamp}_{pathname}` or `{timestamp}_{pathname}_{counter}`
1514
- *
1515
- * **Edge Case Handling**:
1516
- * - If multiple navigations occur to the same pathname in the same millisecond,
1517
- * a counter suffix is appended (e.g., `1234.56_/home_2`)
1518
- * - Counter only added when > 1 to minimize ID length for common case
1519
- *
1520
- * **Why Deterministic**:
1521
- * - Previous implementation used random string duplicate metrics on page reload
1522
- * - Now: Same navigation = same ID = proper deduplication via reportedByNav Map
1524
+ * Generates a deterministic navigation identifier for deduplication.
1525
+ *
1526
+ * **Purpose**: Every call within the same navigation must return the SAME id,
1527
+ * so `reportedByNav` can collapse duplicate Web Vitals (one emission per
1528
+ * metric type per navigation — critical for the fallback observers, which
1529
+ * fire per entry batch).
1530
+ *
1531
+ * **ID Format**: `{startTime}_{pathname}` or `{startTime}_{pathname}_{counter}`
1532
+ *
1533
+ * **Determinism**:
1534
+ * - Base id is derived only from the navigation entry's `startTime` (0 by spec
1535
+ * for the document navigation — no `performance.now()` fallback, which made
1536
+ * every call unique) and the current pathname.
1537
+ * - The id is cached per navigation; the counter suffix is appended ONLY on a
1538
+ * real collision: a new navigation whose base id was already reported
1539
+ * (SPA revisit to the same path, e.g. A→B→A), so the revisit's vitals are
1540
+ * not suppressed by the first visit's dedup entries.
1523
1541
  *
1524
1542
  * @returns Navigation ID string or null if navigation timing unavailable
1525
1543
  *