@mparticle/web-sdk 3.16.1 → 3.17.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/mparticle.js CHANGED
@@ -204,7 +204,7 @@ var mParticle = (function () {
204
204
  Base64: Base64$1
205
205
  };
206
206
 
207
- var version = "3.16.1";
207
+ var version = "3.17.0";
208
208
 
209
209
  var Constants = {
210
210
  sdkVersion: version,
@@ -379,7 +379,8 @@ var mParticle = (function () {
379
379
  // - 'roktonly' → capture only Rokt-related IDs
380
380
  CaptureIntegrationSpecificIdsV2: 'captureIntegrationSpecificIds.V2',
381
381
  AstBackgroundEvents: 'astBackgroundEvents',
382
- AutoLogPageView: 'autoLogPageView'
382
+ AutoLogPageView: 'autoLogPageView',
383
+ CheckoutIntent: 'checkoutIntent'
383
384
  },
384
385
  DefaultInstance: 'default_instance',
385
386
  CCPAPurpose: 'data_sale_opt_out',
@@ -5788,7 +5789,8 @@ var mParticle = (function () {
5788
5789
  CaptureIntegrationSpecificIds = _a.CaptureIntegrationSpecificIds,
5789
5790
  CaptureIntegrationSpecificIdsV2 = _a.CaptureIntegrationSpecificIdsV2,
5790
5791
  AstBackgroundEvents = _a.AstBackgroundEvents,
5791
- AutoLogPageView = _a.AutoLogPageView;
5792
+ AutoLogPageView = _a.AutoLogPageView,
5793
+ CheckoutIntent = _a.CheckoutIntent;
5792
5794
  if (!config.flags) {
5793
5795
  return {};
5794
5796
  }
@@ -5805,6 +5807,7 @@ var mParticle = (function () {
5805
5807
  flags[CaptureIntegrationSpecificIdsV2] = config.flags[CaptureIntegrationSpecificIdsV2] || 'none';
5806
5808
  flags[AstBackgroundEvents] = config.flags[AstBackgroundEvents] === 'True';
5807
5809
  flags[AutoLogPageView] = config.flags[AutoLogPageView] === 'True';
5810
+ flags[CheckoutIntent] = config.flags[CheckoutIntent] === 'True';
5808
5811
  return flags;
5809
5812
  }
5810
5813
  function processBaseUrls(config, flags, apiKey) {
@@ -10624,6 +10627,8 @@ var mParticle = (function () {
10624
10627
  }
10625
10628
 
10626
10629
  var PASSBACK_CONVERSION_TRACKING_ID = 'passbackconversiontrackingid';
10630
+ var CHECKOUT_INTENT_BUFFER_LIMIT = 4;
10631
+ var CHECKOUT_INTENT_MAX_AGE_MS = 30000;
10627
10632
  var ON_SHOPPABLE_ADS_READY_METHOD = 'onShoppableAdsReady';
10628
10633
  // The purpose of this class is to create a link between the Core mParticle SDK and the
10629
10634
  // Rokt Web SDK via a Web Kit.
@@ -10649,6 +10654,8 @@ var mParticle = (function () {
10649
10654
  this.initialized = false;
10650
10655
  this.isShoppableAdsLoaded = false;
10651
10656
  this.stopRouteChangeWatch = null;
10657
+ // Checkout intent raised before the kit attached: latest per kind.
10658
+ this.pendingCheckoutIntents = [];
10652
10659
  }
10653
10660
  /**
10654
10661
  * Sets a callback to be invoked when RoktManager becomes ready
@@ -10727,6 +10734,7 @@ var mParticle = (function () {
10727
10734
  var _a, _b, _c, _d;
10728
10735
  this.kit = kit;
10729
10736
  this.watchRouteChanges();
10737
+ this.flushCheckoutIntents();
10730
10738
  if ((_a = kit.settings) === null || _a === void 0 ? void 0 : _a.accountId) {
10731
10739
  this.store.setRoktAccountId(kit.settings.accountId);
10732
10740
  }
@@ -10772,6 +10780,70 @@ var mParticle = (function () {
10772
10780
  (_c = this.logger) === null || _c === void 0 ? void 0 : _c.error("RoktManager: Error in onRouteChange: ".concat(getErrorMessage(e)));
10773
10781
  }
10774
10782
  };
10783
+ // Checkout intent is off unless the flag is on, and under opt-out, any Rokt
10784
+ // privacy launcher option, or Global Privacy Control.
10785
+ RoktManager.prototype.isCheckoutIntentSuppressed = function () {
10786
+ var _a, _b;
10787
+ var flags = (_b = (_a = this.store) === null || _a === void 0 ? void 0 : _a.SDKConfig) === null || _b === void 0 ? void 0 : _b.flags;
10788
+ if (!flags || flags[Constants.FeatureFlags.CheckoutIntent] !== true || !this.store.isEnabled) {
10789
+ return true;
10790
+ }
10791
+ var options = this.launcherOptions || {};
10792
+ if (options.noFunctional || options.noTargeting || options.doNotShareOrSell) {
10793
+ return true;
10794
+ }
10795
+ var nav = typeof navigator === 'undefined' ? undefined : navigator;
10796
+ return !!nav && nav.globalPrivacyControl === true;
10797
+ };
10798
+ // Reads `this.kit` at call time; before a kit attaches the signal is buffered
10799
+ // with the path it was raised on.
10800
+ RoktManager.prototype.notifyCheckoutIntent = function (signal, pathname) {
10801
+ var _a;
10802
+ if (pathname === void 0) {
10803
+ pathname = window.location.pathname;
10804
+ }
10805
+ if (this.isCheckoutIntentSuppressed()) {
10806
+ return;
10807
+ }
10808
+ var kit = this.kit;
10809
+ if (!kit) {
10810
+ this.pendingCheckoutIntents = this.pendingCheckoutIntents.filter(function (pending) {
10811
+ return pending.signal.kind !== signal.kind;
10812
+ }).concat({
10813
+ signal: signal,
10814
+ pathname: pathname
10815
+ }).slice(-CHECKOUT_INTENT_BUFFER_LIMIT);
10816
+ return;
10817
+ }
10818
+ try {
10819
+ if (isFunction(kit.onCheckoutIntent)) {
10820
+ kit.onCheckoutIntent(signal);
10821
+ }
10822
+ } catch (e) {
10823
+ (_a = this.logger) === null || _a === void 0 ? void 0 : _a.error("RoktManager: Error in onCheckoutIntent: ".concat(getErrorMessage(e)));
10824
+ }
10825
+ };
10826
+ RoktManager.prototype.discardCheckoutIntents = function () {
10827
+ this.pendingCheckoutIntents = [];
10828
+ };
10829
+ // Replays recent buffered signals raised on the current page, oldest first.
10830
+ RoktManager.prototype.flushCheckoutIntents = function () {
10831
+ var _this = this;
10832
+ var pending = this.pendingCheckoutIntents;
10833
+ this.pendingCheckoutIntents = [];
10834
+ var now = Date.now();
10835
+ var pathname = window.location.pathname;
10836
+ pending.filter(function (_a) {
10837
+ var signal = _a.signal,
10838
+ signalPathname = _a.pathname;
10839
+ return now - signal.t < CHECKOUT_INTENT_MAX_AGE_MS && signalPathname === pathname;
10840
+ }).sort(function (a, b) {
10841
+ return a.signal.t - b.signal.t;
10842
+ }).forEach(function (_a) {
10843
+ var signal = _a.signal;
10844
+ return _this.notifyCheckoutIntent(signal);
10845
+ });
10846
+ };
10775
10847
  RoktManager.prototype.isAutoLogPageViewEnabled = function () {
10776
10848
  var _a, _b, _c;
10777
10849
  return ((_c = (_b = (_a = this.store) === null || _a === void 0 ? void 0 : _a.SDKConfig) === null || _b === void 0 ? void 0 : _b.flags) === null || _c === void 0 ? void 0 : _c[Constants.FeatureFlags.AutoLogPageView]) === true;
@@ -11387,6 +11459,335 @@ var mParticle = (function () {
11387
11459
  return LoggingDispatcher;
11388
11460
  }();
11389
11461
 
11462
+ var checkoutPath = /(?:^|\/)(?:checkouts?|payment|order|kasse|paiement|commander?|pago)(?:\/|$)/i;
11463
+ var exclusions = new RegExp(['coupon|promo|newsletter|sign in|add to cart|gift card|pay later|pay in|payment plan|learn more', 'gutschein|anmelden|warenkorb|bon cadeau|connexion|panier|cupón|iniciar sesión|carrito', 'carrello|codice sconto|accedi|buono regalo|winkelwagen|kortingscode|inloggen|cadeaubon|achteraf betalen', 'カート|クーポン|ログイン|会員登録|ギフト|後払い|メルマガ'].join('|'));
11464
+ var commits = new RegExp('^(?:' + ['(?:place|submit|complete|confirm|finali[sz]e)(?!(?: [^ ]+){0,2} (?:another|new|next)(?: |$))(?: [^ ]+){0,2} (?:order|purchase|booking|reservation|payment|checkout)', 'pay(?: securely)?(?: now)?|confirm and pay|order and pay|order with obligation to pay', '(?:jetzt )?(?:zahlungs|kosten)pflichtig (?:bestellen|buchen)|(?:jetzt )?bezahlen|kauf abschließen|bestellung abschließen', 'commander et payer|payer(?: maintenant)?|(?:confirmer|finaliser) (?:ma |la )?commande', "valider et payer|confirmer et payer|commande avec obligation de paiement|finaliser l'achat", '(?:realizar|confirmar|completar) (?:el |mi |la )?(?:pedido|compra|reserva)|finalizar (?:el |mi )?pedido|pagar(?: ahora)?', 'pedido con obligación de pago', "(?:effettua|conferma|concludi|completa) (?:l'|il )?(?:ordine|acquisto)|ordine con obbligo di pagamento", 'paga(?: ora| adesso)?|ordina e paga', '(?:nu )?betalen|bestellen en betalen|bestelling plaatsen|plaats bestelling|bestelling afronden', 'bestelling met betalingsverplichting', '(?:この内容で|上記の?内容で)?ご?(?:注文|購入|予約|決済|お?支払い?)を?(?:確定|完了)(?:する)?', '(?:この内容で|上記の?内容で)ご?(?:注文|購入|予約)する|ご?注文する|決済する|(?:今すぐ|を)?支払う'].join('|') + ')$');
11465
+ var routeGatedCommits = new RegExp('^(?:buy now|purchase|jetzt kaufen|kaufen|commander|(?:passer|valider) (?:la |ma )?commande|finalizar (?:la |mi )?compra|' + 'comprar ahora|acquista ora|nu kopen|今すぐ購入|購入する|この内容で申し込む)$');
11466
+ var steps = new RegExp('^(?:' + ['checkout|check out|check-out|continue to payment|proceed to checkout', 'zur kasse|zur kasse gehen|auschecken|weiter zur zahlung', 'passer à la caisse|continuer vers le paiement|procéder au paiement', 'ir a caja|continuar al pago|tramitar pedido', "procedi al checkout|procedi all'acquisto|procedi al pagamento|vai alla cassa", 'afrekenen|verder naar betalen|doorgaan naar betalen', 'レジに進む|ご?(?:購入|注文)手続き(?:へ|に)(?:進む)?|お?支払い(?:へ|に)進む|(?:ご注文内容の)?確認画面へ(?:進む)?'].join('|') + ')$');
11467
+ var wallets = /^(?:apple pay|google pay|paypal|venmo|amazon pay|shop pay|klarna|afterpay|affirm)$/;
11468
+ // Exact HTTPS hosts of hosted wallet and payment frames.
11469
+ var frameHosts = new Set(['www.paypal.com', 'www.sandbox.paypal.com', 'pay.google.com', 'applepay.cdn-apple.com', 'js.stripe.com', 'checkoutshopper-live.adyen.com', 'payments.amazon.com', 'pay.shopify.com']);
11470
+ var CONTROL_SELECTOR = 'button, input[type="submit"], input[type="button"], input[type="image"], [role="button"]';
11471
+ function getIntentRouteClass(pathname) {
11472
+ return checkoutPath.test(pathname) ? 'checkout_path' : 'other';
11473
+ }
11474
+ // Rokt's own placements and frames never count as partner checkout controls.
11475
+ function isRoktElement(element) {
11476
+ if (element.closest('[data-core-type], #rokt-controller-frame')) {
11477
+ return true;
11478
+ }
11479
+ var src = element.getAttribute('src');
11480
+ if (src) {
11481
+ try {
11482
+ if (new URL(src, 'https://example.test').pathname.indexOf('/wsdk/') !== -1) {
11483
+ return true;
11484
+ }
11485
+ } catch (e) {
11486
+ // An unparseable src is not a Rokt element.
11487
+ }
11488
+ }
11489
+ return false;
11490
+ }
11491
+ function isWalletElement(element) {
11492
+ return element.matches('apple-pay-button, shop-pay-button, .gpay-button, #AmazonPayButton, [data-amazon-pay-button]');
11493
+ }
11494
+ function normalize(text) {
11495
+ if (!text || text.length > 128) {
11496
+ return undefined;
11497
+ }
11498
+ return text.toLowerCase().replace(/[’‘]/g, "'").replace(/[0-90-9$€£¥¥₹円.,,]/g, '').replace(/\s+/g, ' ').trim();
11499
+ }
11500
+ // Labels come from aria-label then visible text. Only submit and button inputs
11501
+ // have their value read, so a typed field value is never touched.
11502
+ function labels(element) {
11503
+ if (element.children.length > 4 || element.querySelector('input, select, textarea')) {
11504
+ return [];
11505
+ }
11506
+ var text = null;
11507
+ if (element.tagName.toLowerCase() === 'input') {
11508
+ var typeAttr = element.getAttribute('type');
11509
+ var type = typeAttr ? typeAttr.toLowerCase() : typeAttr;
11510
+ if (type === 'submit' || type === 'button') {
11511
+ text = element.value;
11512
+ } else if (type === 'image') {
11513
+ text = element.getAttribute('alt');
11514
+ }
11515
+ } else {
11516
+ text = element.textContent;
11517
+ }
11518
+ return [normalize(element.getAttribute('aria-label')), normalize(text)].filter(function (label) {
11519
+ return !!label;
11520
+ });
11521
+ }
11522
+ function classifyLabel(text, button, pathname) {
11523
+ if (commits.test(text) || routeGatedCommits.test(text) && getIntentRouteClass(pathname) === 'checkout_path') {
11524
+ return 'commit';
11525
+ }
11526
+ if (button && wallets.test(text)) {
11527
+ return 'wallet';
11528
+ }
11529
+ if (steps.test(text)) {
11530
+ return 'checkout_step';
11531
+ }
11532
+ return undefined;
11533
+ }
11534
+ // Walks at most 12 entries of an event's composed path for the nearest control.
11535
+ function classifyIntentControl(path, pathname) {
11536
+ var control;
11537
+ for (var _i = 0, _a = path.slice(0, 12); _i < _a.length; _i++) {
11538
+ var target = _a[_i];
11539
+ if (!(target instanceof Element)) {
11540
+ continue;
11541
+ }
11542
+ if (isRoktElement(target)) {
11543
+ return undefined;
11544
+ }
11545
+ if (!control && (target.matches(CONTROL_SELECTOR + ', a[href]') || isWalletElement(target))) {
11546
+ control = target;
11547
+ }
11548
+ }
11549
+ if (!control || isRoktElement(control)) {
11550
+ return undefined;
11551
+ }
11552
+ var texts = labels(control);
11553
+ if (texts.some(function (text) {
11554
+ return exclusions.test(text);
11555
+ })) {
11556
+ return undefined;
11557
+ }
11558
+ var button = false;
11559
+ if (!control.matches('a[href]')) {
11560
+ button = control.matches(CONTROL_SELECTOR) || isWalletElement(control);
11561
+ }
11562
+ if (button && isWalletElement(control)) {
11563
+ return 'wallet';
11564
+ }
11565
+ for (var _b = 0, texts_1 = texts; _b < texts_1.length; _b++) {
11566
+ var text = texts_1[_b];
11567
+ var kind = classifyLabel(text, button, pathname);
11568
+ if (kind) {
11569
+ return kind;
11570
+ }
11571
+ }
11572
+ return undefined;
11573
+ }
11574
+ // Focus moving into a visible hosted wallet or payment frame.
11575
+ function classifyIntentFrame(target, win) {
11576
+ if (win.document.visibilityState !== 'visible' || !(target instanceof HTMLIFrameElement)) {
11577
+ return undefined;
11578
+ }
11579
+ if (isRoktElement(target)) {
11580
+ return undefined;
11581
+ }
11582
+ try {
11583
+ var url = new URL(target.src);
11584
+ if (url.protocol === 'https:' && frameHosts.has(url.hostname)) {
11585
+ return 'frame';
11586
+ }
11587
+ } catch (e) {
11588
+ // An unparseable frame src is not a wallet frame.
11589
+ }
11590
+ return undefined;
11591
+ }
11592
+
11593
+ // State lives on `window`, like APV, so a re-executed bundle never doubles the
11594
+ // listeners. Inspect `window.__mpCit__` in a console to see if one is live.
11595
+ var WIN_CIT_KEY = '__mpCit__';
11596
+ var THROTTLE_MS = 2000;
11597
+ var SUBMIT_AFTER_COMMIT_CLICK_MS = 1000;
11598
+ var PATH_LIMIT = 12;
11599
+ var citWindow = function citWindow() {
11600
+ return typeof window === 'undefined' ? null : window;
11601
+ };
11602
+ var getActiveCheckoutIntentTracker = function getActiveCheckoutIntentTracker() {
11603
+ var win = citWindow();
11604
+ var state = win ? win[WIN_CIT_KEY] : undefined;
11605
+ return state ? state.tracker : undefined;
11606
+ };
11607
+ // Stops whichever tracker is active, including one from an earlier bundle load.
11608
+ var resetCheckoutIntentTracking = function resetCheckoutIntentTracking() {
11609
+ var win = citWindow();
11610
+ if (!win) {
11611
+ return;
11612
+ }
11613
+ var active = getActiveCheckoutIntentTracker();
11614
+ if (active) {
11615
+ active.teardown();
11616
+ }
11617
+ win[WIN_CIT_KEY] = {};
11618
+ };
11619
+ var CAPTURE_PASSIVE = {
11620
+ capture: true,
11621
+ passive: true
11622
+ };
11623
+ var PASSIVE = {
11624
+ passive: true
11625
+ };
11626
+ // Listens for checkout clicks, submits and wallet frame focus, and hands a
11627
+ // frozen {kind, t} to the Rokt kit. No storage, no network, no event logging.
11628
+ var CheckoutIntentTracker = /** @class */function () {
11629
+ function CheckoutIntentTracker(mpInstance) {
11630
+ var _this = this;
11631
+ this.active = false;
11632
+ // Bumped on reset so classification queued before it is dropped.
11633
+ this.generation = 0;
11634
+ this.lastCommitClick = -Infinity;
11635
+ this.lastSent = {};
11636
+ this.onPageShow = function (event) {
11637
+ // A page restored from the back-forward cache starts fresh.
11638
+ if (event.persisted) {
11639
+ _this.reset();
11640
+ }
11641
+ };
11642
+ this.onEvent = function (event) {
11643
+ try {
11644
+ _this.observe(event);
11645
+ } catch (e) {
11646
+ _this.log('[error] event handler threw: ' + e);
11647
+ }
11648
+ };
11649
+ this.mpInstance = mpInstance;
11650
+ }
11651
+ Object.defineProperty(CheckoutIntentTracker.prototype, "isActive", {
11652
+ get: function get() {
11653
+ return this.active;
11654
+ },
11655
+ enumerable: false,
11656
+ configurable: true
11657
+ });
11658
+ CheckoutIntentTracker.prototype.init = function () {
11659
+ var win = citWindow();
11660
+ if (!win) {
11661
+ return;
11662
+ }
11663
+ var previous = getActiveCheckoutIntentTracker();
11664
+ if (previous) {
11665
+ previous.teardown();
11666
+ }
11667
+ if (this.active) {
11668
+ this.teardown();
11669
+ }
11670
+ if (this.isSuppressed()) {
11671
+ this.log('privacy settings suppress checkout intent, not starting');
11672
+ return;
11673
+ }
11674
+ win.document.addEventListener('click', this.onEvent, CAPTURE_PASSIVE);
11675
+ win.document.addEventListener('submit', this.onEvent, CAPTURE_PASSIVE);
11676
+ win.addEventListener('blur', this.onEvent, PASSIVE);
11677
+ win.addEventListener('pageshow', this.onPageShow, PASSIVE);
11678
+ this.active = true;
11679
+ if (!win[WIN_CIT_KEY]) {
11680
+ win[WIN_CIT_KEY] = {};
11681
+ }
11682
+ win[WIN_CIT_KEY].tracker = this;
11683
+ this.log('listening');
11684
+ };
11685
+ CheckoutIntentTracker.prototype.teardown = function () {
11686
+ var win = citWindow();
11687
+ if (win) {
11688
+ win.document.removeEventListener('click', this.onEvent, true);
11689
+ win.document.removeEventListener('submit', this.onEvent, true);
11690
+ win.removeEventListener('blur', this.onEvent);
11691
+ win.removeEventListener('pageshow', this.onPageShow);
11692
+ var state = win[WIN_CIT_KEY];
11693
+ if (state && state.tracker === this) {
11694
+ state.tracker = undefined;
11695
+ }
11696
+ }
11697
+ this.active = false;
11698
+ // Signals not yet handed to a kit go with the tracker.
11699
+ this.clearPending();
11700
+ };
11701
+ // Drops queued classification, commit timing and buffered signals, so
11702
+ // nothing captured before an opt-out is delivered after an opt-in.
11703
+ CheckoutIntentTracker.prototype.clearPending = function () {
11704
+ this.reset();
11705
+ var roktManager = this.mpInstance._RoktManager;
11706
+ if (roktManager) {
11707
+ roktManager.discardCheckoutIntents();
11708
+ }
11709
+ };
11710
+ CheckoutIntentTracker.prototype.reset = function () {
11711
+ this.generation++;
11712
+ this.lastCommitClick = -Infinity;
11713
+ this.lastSent = {};
11714
+ };
11715
+ CheckoutIntentTracker.prototype.observe = function (event) {
11716
+ var _this = this;
11717
+ if (!this.active || !event.isTrusted || this.isSuppressed()) {
11718
+ return;
11719
+ }
11720
+ var t = Date.now();
11721
+ var pathname = window.location.pathname;
11722
+ var layer = event.type;
11723
+ // The composed path is only populated during dispatch, so copy it now.
11724
+ var path = layer === 'click' && typeof event.composedPath === 'function' ? event.composedPath().slice(0, PATH_LIMIT) : [];
11725
+ var target;
11726
+ if (layer === 'submit') {
11727
+ target = event.submitter;
11728
+ } else if (layer === 'blur') {
11729
+ // Read after the blur settles: some browsers have not moved focus
11730
+ // into the frame yet when the window blurs.
11731
+ target = null;
11732
+ } else {
11733
+ target = event.target;
11734
+ }
11735
+ var generation = this.generation;
11736
+ setTimeout(function () {
11737
+ if (generation !== _this.generation) {
11738
+ return;
11739
+ }
11740
+ try {
11741
+ var kind = void 0;
11742
+ if (layer === 'blur') {
11743
+ kind = classifyIntentFrame(document.activeElement, window);
11744
+ } else if (layer === 'submit' && target === null) {
11745
+ // A form submitted without a submitter only counts right
11746
+ // after a commit click.
11747
+ kind = t - _this.lastCommitClick <= SUBMIT_AFTER_COMMIT_CLICK_MS ? 'commit' : undefined;
11748
+ } else {
11749
+ kind = classifyIntentControl(layer === 'submit' ? [target] : path, pathname);
11750
+ }
11751
+ if (kind) {
11752
+ _this.emit(kind, layer, t, pathname);
11753
+ }
11754
+ } catch (e) {
11755
+ _this.log('[error] classification threw: ' + e);
11756
+ }
11757
+ }, 0);
11758
+ };
11759
+ CheckoutIntentTracker.prototype.emit = function (kind, layer, t, pathname) {
11760
+ if (this.isSuppressed()) {
11761
+ return;
11762
+ }
11763
+ if (kind === 'commit' && layer === 'click') {
11764
+ this.lastCommitClick = t;
11765
+ }
11766
+ var last = this.lastSent[kind];
11767
+ if (last && last.pathname === pathname && t - last.t < THROTTLE_MS) {
11768
+ return;
11769
+ }
11770
+ this.lastSent[kind] = {
11771
+ t: t,
11772
+ pathname: pathname
11773
+ };
11774
+ this.mpInstance._RoktManager.notifyCheckoutIntent(Object.freeze({
11775
+ kind: kind,
11776
+ t: t
11777
+ }), pathname);
11778
+ };
11779
+ // The Rokt manager owns the privacy gates: flag, opt-out, launcher options
11780
+ // and Global Privacy Control.
11781
+ CheckoutIntentTracker.prototype.isSuppressed = function () {
11782
+ var roktManager = this.mpInstance._RoktManager;
11783
+ return !roktManager || roktManager.isCheckoutIntentSuppressed();
11784
+ };
11785
+ CheckoutIntentTracker.prototype.log = function (message) {
11786
+ this.mpInstance.Logger.verbose('mParticle checkout intent: ' + message);
11787
+ };
11788
+ return CheckoutIntentTracker;
11789
+ }();
11790
+
11390
11791
  var Messages = Constants.Messages,
11391
11792
  HTTPCodes = Constants.HTTPCodes,
11392
11793
  FeatureFlags = Constants.FeatureFlags,
@@ -11520,6 +11921,10 @@ var mParticle = (function () {
11520
11921
  this.reset = function (instance) {
11521
11922
  try {
11522
11923
  instance._Persistence.resetPersistence();
11924
+ // The Rokt manager keeps its own store reference, so stop checkout
11925
+ // intent here rather than relying on the deleted store.
11926
+ resetCheckoutIntentTracking();
11927
+ instance._CheckoutIntentTracker = undefined;
11523
11928
  if (instance._Store) {
11524
11929
  delete instance._Store;
11525
11930
  }
@@ -11541,6 +11946,8 @@ var mParticle = (function () {
11541
11946
  instance._Events.stopTracking();
11542
11947
  resetPageViewTracking();
11543
11948
  instance._PageViewTracker = undefined;
11949
+ resetCheckoutIntentTracking();
11950
+ instance._CheckoutIntentTracker = undefined;
11544
11951
  if (!keepPersistence) {
11545
11952
  instance._Persistence.resetPersistence();
11546
11953
  }
@@ -12139,6 +12546,12 @@ var mParticle = (function () {
12139
12546
  if (queued) return;
12140
12547
  self._SessionManager.resetSessionTimer();
12141
12548
  self._Store.isEnabled = !isOptingOut;
12549
+ if (isOptingOut) {
12550
+ if (self._CheckoutIntentTracker) {
12551
+ self._CheckoutIntentTracker.clearPending();
12552
+ }
12553
+ self._RoktManager.discardCheckoutIntents();
12554
+ }
12142
12555
  self._Events.logOptOut();
12143
12556
  self._Persistence.update();
12144
12557
  if (self._Store.activeForwarders.length) {
@@ -12305,7 +12718,9 @@ var mParticle = (function () {
12305
12718
  var _a, _b, _c, _d;
12306
12719
  var kitBlocker = createKitBlocker(config, mpInstance);
12307
12720
  var getFeatureFlag = mpInstance._Helpers.getFeatureFlag;
12308
- var AutoLogPageView = Constants.FeatureFlags.AutoLogPageView;
12721
+ var _e = Constants.FeatureFlags,
12722
+ AutoLogPageView = _e.AutoLogPageView,
12723
+ CheckoutIntent = _e.CheckoutIntent;
12309
12724
  // Destroy previous batch uploader to prevent leaked timers and event listeners
12310
12725
  if ((_a = mpInstance._APIClient) === null || _a === void 0 ? void 0 : _a.uploader) {
12311
12726
  mpInstance._APIClient.uploader.destroy();
@@ -12321,6 +12736,8 @@ var mParticle = (function () {
12321
12736
  // persistence or Identity directly.
12322
12737
  if (mpInstance._Store.webviewBridgeEnabled) {
12323
12738
  mpInstance._NativeSdkHelpers.initializeSessionAttributes(apiKey);
12739
+ // Checkout intent never runs as a native passthrough.
12740
+ resetCheckoutIntentTracking();
12324
12741
  } else {
12325
12742
  // Main SDK initialization flow
12326
12743
  // Load any settings/identities/attributes from cookie or localStorage
@@ -12400,6 +12817,18 @@ var mParticle = (function () {
12400
12817
  // so it stops listening. Re-created if the flag is re-enabled later.
12401
12818
  mpInstance._PageViewTracker.teardown();
12402
12819
  }
12820
+ // Checkout intent only feeds the Rokt kit, so it listens only when the
12821
+ // workspace opts in and this init configures a Rokt kit.
12822
+ if (getFeatureFlag(CheckoutIntent) && roktConfig) {
12823
+ if (!mpInstance._CheckoutIntentTracker) {
12824
+ mpInstance._CheckoutIntentTracker = new CheckoutIntentTracker(mpInstance);
12825
+ }
12826
+ mpInstance._CheckoutIntentTracker.init();
12827
+ } else {
12828
+ // Also retires a tracker left by an earlier bundle load. Like APV,
12829
+ // this assumes a single active SDK instance.
12830
+ resetCheckoutIntentTracking();
12831
+ }
12403
12832
  processIdentityCallback(mpInstance, currentUser, currentUserMPID, currentUserIdentities);
12404
12833
  }
12405
12834
  // We will continue to clear out the ready queue as part of the initial init flow
@@ -12493,6 +12922,8 @@ var mParticle = (function () {
12493
12922
  mpInstance._LoggingDispatcher.logger = mpInstance.Logger;
12494
12923
  mpInstance._Store = new Store(config, mpInstance, apiKey);
12495
12924
  window.mParticle.Store = mpInstance._Store;
12925
+ // Checkout intent stops until this init's config decides whether it runs.
12926
+ resetCheckoutIntentTracking();
12496
12927
  mpInstance.Logger.verbose(StartingInitialization);
12497
12928
  // Initialize CookieConsentManager with privacy flags from launcherOptions
12498
12929
  var _a = normalizeRoktLauncherOptions(config === null || config === void 0 ? void 0 : config.launcherOptions),
@@ -0,0 +1,4 @@
1
+ export type CheckoutIntentKind = 'commit' | 'wallet' | 'checkout_step' | 'frame';
2
+ export declare function getIntentRouteClass(pathname: string): 'checkout_path' | 'other';
3
+ export declare function classifyIntentControl(path: Array<EventTarget | null>, pathname: string): CheckoutIntentKind | undefined;
4
+ export declare function classifyIntentFrame(target: EventTarget | null, win: Window): 'frame' | undefined;
@@ -0,0 +1,23 @@
1
+ import { IMParticleWebSDKInstance } from './mp-instance';
2
+ export declare const WIN_CIT_KEY = "__mpCit__";
3
+ export declare const getActiveCheckoutIntentTracker: () => CheckoutIntentTracker | undefined;
4
+ export declare const resetCheckoutIntentTracking: () => void;
5
+ export declare class CheckoutIntentTracker {
6
+ private readonly mpInstance;
7
+ private active;
8
+ private generation;
9
+ private lastCommitClick;
10
+ private lastSent;
11
+ constructor(mpInstance: IMParticleWebSDKInstance);
12
+ get isActive(): boolean;
13
+ init(): void;
14
+ teardown(): void;
15
+ clearPending(): void;
16
+ private reset;
17
+ private readonly onPageShow;
18
+ private readonly onEvent;
19
+ private observe;
20
+ private emit;
21
+ private isSuppressed;
22
+ private log;
23
+ }
@@ -162,6 +162,7 @@ declare const Constants: {
162
162
  readonly CaptureIntegrationSpecificIdsV2: "captureIntegrationSpecificIds.V2";
163
163
  readonly AstBackgroundEvents: "astBackgroundEvents";
164
164
  readonly AutoLogPageView: "autoLogPageView";
165
+ readonly CheckoutIntent: "checkoutIntent";
165
166
  };
166
167
  readonly DefaultInstance: "default_instance";
167
168
  readonly CCPAPurpose: "data_sale_opt_out";
@@ -14,7 +14,7 @@
14
14
  */
15
15
  export * from './public-types';
16
16
  export type { KitInterface, } from './forwarders.interfaces';
17
- export type { RoktAttributeValue, RoktAttributeValueType, RoktAttributeValueArray, RoktAttributes, IRoktPartnerExtensionData, IRoktSelectPlacementsOptions, IRoktSelection, IRoktLauncher, IRoktMessage, RoktKitFilterSettings, IRoktKitSettings, IRoktKit, IRoktOptions, IRoktLauncherOptions, } from './roktManager';
17
+ export type { RoktAttributeValue, RoktAttributeValueType, RoktAttributeValueArray, RoktAttributes, IRoktPartnerExtensionData, IRoktSelectPlacementsOptions, IRoktSelection, IRoktLauncher, IRoktMessage, RoktKitFilterSettings, IRoktKitSettings, IRoktKit, ICheckoutIntentSignal, IRoktOptions, IRoktLauncherOptions, } from './roktManager';
18
18
  export type { ErrorCodes, WSDKErrorSeverity, } from './reporting/types';
19
19
  export type { ISDKError, ISDKLogEntry, IErrorReportingService, ILoggingService, } from './reporting/types';
20
20
  export type { SDKEvent, SDKProductAction, SDKProductActionType, SDKPromotionAction, SDKGeoLocation, SDKDataPlan, SDKLoggerApi, SDKConfigApi, IMParticleInstanceManager, } from './sdkRuntimeModels';
@@ -22,6 +22,7 @@ import { ICookieConsentManager } from './cookieConsentManager';
22
22
  import { ErrorReportingDispatcher } from './reporting/errorReportingDispatcher';
23
23
  import { LoggingDispatcher } from './reporting/loggingDispatcher';
24
24
  import { PageViewTracker } from './pageViewTracker';
25
+ import { CheckoutIntentTracker } from './checkoutIntentTracker';
25
26
  export interface IErrorLogMessage {
26
27
  message?: string;
27
28
  name?: string;
@@ -47,6 +48,7 @@ export interface IMParticleWebSDKInstance extends MParticleWebSDK {
47
48
  _IntegrationCapture: IntegrationCapture;
48
49
  _NativeSdkHelpers: INativeSdkHelpers;
49
50
  _PageViewTracker?: PageViewTracker;
51
+ _CheckoutIntentTracker?: CheckoutIntentTracker;
50
52
  _Persistence: IPersistence;
51
53
  _CookieConsentManager: ICookieConsentManager;
52
54
  _ErrorReportingDispatcher: ErrorReportingDispatcher;
@@ -8,6 +8,7 @@ import { IStore, LocalSessionAttributes } from "./store";
8
8
  import { IErrorReportingService, ILoggingService } from "./reporting/types";
9
9
  import { IRoktLauncherOptions } from "./roktLauncherOptions";
10
10
  import IntegrationCapture from "./integrationCapture";
11
+ import type { CheckoutIntentKind } from "./checkoutIntentClassifier";
11
12
  export type RoktAttributeValueArray = Array<string | number | boolean>;
12
13
  export type RoktAttributeValueType = string | number | boolean | undefined | null;
13
14
  export type RoktAttributeValue = RoktAttributeValueType | RoktAttributeValueArray;
@@ -42,6 +43,10 @@ export interface RoktKitFilterSettings {
42
43
  filterUserAttributes?: (userAttributes: Dictionary<string>, filterList: number[]) => Dictionary<string>;
43
44
  filteredUser?: IMParticleUser | null;
44
45
  }
46
+ export interface ICheckoutIntentSignal {
47
+ kind: CheckoutIntentKind;
48
+ t: number;
49
+ }
45
50
  export interface IRoktKitSettings {
46
51
  accountId?: string;
47
52
  placementAttributesMapping?: string;
@@ -59,6 +64,7 @@ export interface IRoktKit {
59
64
  onShoppableAdsReady(callback: () => void): void;
60
65
  terminate?: () => Promise<void>;
61
66
  onRouteChange?: () => void;
67
+ onCheckoutIntent?: (signal: ICheckoutIntentSignal) => void;
62
68
  launcherOptions?: Dictionary<any>;
63
69
  settings?: IRoktKitSettings;
64
70
  integrationName?: string;
@@ -91,6 +97,7 @@ export default class RoktManager {
91
97
  private initialized;
92
98
  private isShoppableAdsLoaded;
93
99
  private stopRouteChangeWatch;
100
+ private pendingCheckoutIntents;
94
101
  constructor(instanceName?: string);
95
102
  /**
96
103
  * Sets a callback to be invoked when RoktManager becomes ready
@@ -115,6 +122,10 @@ export default class RoktManager {
115
122
  attachKit(kit: IRoktKit): void;
116
123
  private watchRouteChanges;
117
124
  private notifyRouteChange;
125
+ isCheckoutIntentSuppressed(): boolean;
126
+ notifyCheckoutIntent(signal: ICheckoutIntentSignal, pathname?: string): void;
127
+ discardCheckoutIntents(): void;
128
+ private flushCheckoutIntents;
118
129
  private isAutoLogPageViewEnabled;
119
130
  /**
120
131
  * Renders ads based on the options provided
@@ -79,6 +79,7 @@ export interface IFeatureFlags {
79
79
  'captureIntegrationSpecificIds.V2'?: string;
80
80
  astBackgroundEvents?: boolean;
81
81
  autoLogPageView?: boolean;
82
+ checkoutIntent?: boolean;
82
83
  }
83
84
  export interface IStore {
84
85
  isEnabled: boolean;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mparticle/web-sdk",
3
- "version": "3.16.1",
3
+ "version": "3.17.0",
4
4
  "description": "mParticle core SDK for web applications",
5
5
  "license": "Apache-2.0",
6
6
  "keywords": [