@datalyr/web 1.6.5 → 1.7.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.
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @datalyr/web v1.6.5
2
+ * @datalyr/web v1.7.0
3
3
  * Datalyr Web SDK - Modern attribution tracking for web applications
4
4
  * (c) 2026 Datalyr Inc.
5
5
  * Released under the MIT License
@@ -2400,9 +2400,10 @@ class ContainerManager {
2400
2400
  throw new Error(`Failed to fetch container scripts: ${response.status}`);
2401
2401
  }
2402
2402
  const data = yield response.json();
2403
- // Store scripts and pixels
2403
+ // Store scripts, pixels, and the SDK runtime config envelope.
2404
2404
  this.scripts = data.scripts || [];
2405
2405
  this.pixels = data.pixels || null;
2406
+ this.remoteConfig = (data.config && typeof data.config === 'object') ? data.config : undefined;
2406
2407
  // Initialize pixels if configured. Awaited so advanced-matching hashes
2407
2408
  // are resolved before the first dl.track() flushes through trackToPixels
2408
2409
  // (otherwise fbq('init') would lag fbq('track') in fast-path tracks).
@@ -2432,6 +2433,13 @@ class ContainerManager {
2432
2433
  }
2433
2434
  });
2434
2435
  }
2436
+ /**
2437
+ * The SDK runtime config delivered by /container-scripts, or undefined if the
2438
+ * response omitted it. The SDK merges this under explicit init() options.
2439
+ */
2440
+ getRemoteConfig() {
2441
+ return this.remoteConfig;
2442
+ }
2435
2443
  /**
2436
2444
  * Load scripts by trigger type
2437
2445
  */
@@ -3089,7 +3097,7 @@ class AutoIdentifyManager {
3089
3097
  this.config = {
3090
3098
  enabled: config.enabled !== false,
3091
3099
  captureFromForms: config.captureFromForms !== false,
3092
- captureFromAPI: config.captureFromAPI !== false,
3100
+ captureFromAPI: config.captureFromAPI === true, // default OFF: same-origin response-scan can mis-identify (e.g. admin views)
3093
3101
  captureFromShopify: config.captureFromShopify !== false,
3094
3102
  trustedDomains: config.trustedDomains || [],
3095
3103
  debug: config.debug || false
@@ -3498,12 +3506,57 @@ class AutoIdentifyManager {
3498
3506
  }
3499
3507
  }
3500
3508
 
3509
+ /** Keys the remote config is allowed to fill on DatalyrConfig. */
3510
+ const REMOTE_KEYS = [
3511
+ 'autoIdentify',
3512
+ 'autoIdentifyForms',
3513
+ 'autoIdentifyAPI',
3514
+ 'autoIdentifyShopify',
3515
+ 'shopifyCartAttributes',
3516
+ 'checkoutChampDomains',
3517
+ 'respectGlobalPrivacyControl',
3518
+ 'respectDoNotTrack',
3519
+ 'privacyMode',
3520
+ ];
3521
+ /**
3522
+ * Fold `remote` into `config` IN PLACE. Precedence per key:
3523
+ * explicit init() value > remote (dashboard) > built-in default
3524
+ *
3525
+ * `explicitKeys` = the keys the CALLER passed to init() (before built-in
3526
+ * defaults were merged in). For any remote key NOT in that set, remote
3527
+ * OVERRIDES the built-in default. This is essential because some keys
3528
+ * (respectDoNotTrack, respectGlobalPrivacyControl, privacyMode) get a built-in
3529
+ * default at init() and are therefore never `undefined` — a naive
3530
+ * "fill-if-undefined" would silently ignore the dashboard for them.
3531
+ *
3532
+ * No-op when there's no remote config (older worker / container disabled /
3533
+ * failed fetch) — built-in defaults then stand.
3534
+ */
3535
+ function applyRemoteConfig(config, remote, explicitKeys) {
3536
+ if (!remote)
3537
+ return;
3538
+ const target = config;
3539
+ for (const key of REMOTE_KEYS) {
3540
+ const remoteVal = remote[key];
3541
+ if (remoteVal === undefined || remoteVal === null)
3542
+ continue;
3543
+ // Explicit init() always wins; otherwise remote overrides the built-in default.
3544
+ if (explicitKeys === null || explicitKeys === void 0 ? void 0 : explicitKeys.has(key))
3545
+ continue;
3546
+ target[key] = remoteVal;
3547
+ }
3548
+ }
3549
+
3501
3550
  /**
3502
3551
  * Datalyr Web SDK
3503
3552
  * Modern attribution tracking for web applications
3504
3553
  */
3505
3554
  class Datalyr {
3506
3555
  constructor() {
3556
+ // Keys the caller passed to init() (before built-in defaults were merged).
3557
+ // Lets the remote-config merge override built-in defaults while always
3558
+ // deferring to an explicit init() value. See applyRemoteConfig.
3559
+ this.explicitConfigKeys = new Set();
3507
3560
  this.superProperties = {};
3508
3561
  this.userProperties = {};
3509
3562
  this.optedOut = false;
@@ -3526,6 +3579,10 @@ class Datalyr {
3526
3579
  if (!config.workspaceId) {
3527
3580
  throw new Error('[Datalyr] workspaceId is required');
3528
3581
  }
3582
+ // Snapshot the keys the caller explicitly set, BEFORE built-in defaults are
3583
+ // merged in — so the remote-config merge can override built-in defaults
3584
+ // while still deferring to anything the caller passed explicitly.
3585
+ this.explicitConfigKeys = new Set(Object.keys(config));
3529
3586
  // Set default config values
3530
3587
  this.config = Object.assign({ endpoint: 'https://ingest.datalyr.com', debug: false, batchSize: 10, flushInterval: 5000, flushAt: 10, criticalEvents: undefined, highPriorityEvents: undefined, sessionTimeout: 60 * 60 * 1000, trackSessions: true, attributionWindow: 90 * 24 * 60 * 60 * 1000, trackedParams: [], respectDoNotTrack: false, respectGlobalPrivacyControl: true, privacyMode: 'standard', cookieDomain: 'auto', cookieExpires: 365, secureCookie: 'auto', sameSite: 'Lax', cookiePrefix: '__dl_', enablePerformanceTracking: true, enableFingerprinting: true, maxRetries: 5, retryDelay: 1000, maxOfflineQueueSize: 100, trackSPA: true, trackPageViews: true, fallbackEndpoints: [], plugins: [] }, config);
3531
3588
  // platform:'shopify' implies cart-attribute sync: stamp visitor_id + Meta
@@ -3602,6 +3659,7 @@ class Datalyr {
3602
3659
  return this.initializationPromise;
3603
3660
  }
3604
3661
  this.initializationPromise = (() => __awaiter(this, void 0, void 0, function* () {
3662
+ var _a;
3605
3663
  try {
3606
3664
  // SEC-03 Fix: Initialize encryption for PII data
3607
3665
  const deviceId = this.identity.getAnonymousId();
@@ -3647,6 +3705,16 @@ class Datalyr {
3647
3705
  this.fireCheckoutChampPurchasePixel();
3648
3706
  }
3649
3707
  }
3708
+ // Fold the remote /container-scripts config UNDER explicit init()
3709
+ // overrides (precedence: defaults <- remote <- explicit). No-op if the
3710
+ // container is disabled or the worker didn't send `config`. This is what
3711
+ // makes the dashboard toggles take effect with no snippet edits.
3712
+ applyRemoteConfig(this.config, (_a = this.container) === null || _a === void 0 ? void 0 : _a.getRemoteConfig(), this.explicitConfigKeys);
3713
+ // Privacy-strict turns auto-identify (email capture) OFF regardless of
3714
+ // the dashboard/remote value — privacy wins over the bridge.
3715
+ if (this.config.privacyMode === 'strict') {
3716
+ this.config.autoIdentify = false;
3717
+ }
3650
3718
  // autoIdentify default for the CC bridge:
3651
3719
  // - platform === 'checkoutchamp' (CC funnel pages) OR
3652
3720
  // - checkoutChampDomains set (Shopify storefronts feeding a CC funnel)
@@ -3657,8 +3725,10 @@ class Datalyr {
3657
3725
  if (ccBridgeActive && this.config.autoIdentify === undefined) {
3658
3726
  this.config.autoIdentify = true;
3659
3727
  }
3660
- // Initialize auto-identify if explicitly enabled (opt-in)
3661
- if (this.config.autoIdentify === true) {
3728
+ // Initialize auto-identify when enabled (explicit or remote) AND
3729
+ // tracking is allowed. The shouldTrack() gate keeps capture from even
3730
+ // setting up its form/API interceptors for opted-out / DNT / GPC users.
3731
+ if (this.config.autoIdentify === true && this.shouldTrack()) {
3662
3732
  this.autoIdentify = new AutoIdentifyManager({
3663
3733
  enabled: true,
3664
3734
  captureFromForms: this.config.autoIdentifyForms,
@@ -3684,7 +3754,7 @@ class Datalyr {
3684
3754
  // Lets server-side order webhooks recover the browser visitor + Meta click
3685
3755
  // signals (the postback webhook reads these as note_attributes). Inert unless
3686
3756
  // enabled; best-effort and never blocks init.
3687
- if (this.config.shopifyCartAttributes === true) {
3757
+ if (this.config.shopifyCartAttributes === true && this.shouldTrack()) {
3688
3758
  this.syncShopifyCartAttributes().catch((error) => {
3689
3759
  this.log('Shopify cart attribute sync failed:', error);
3690
3760
  });