@tokenoftrust/storefront-runner 4.6.3 → 4.6.4

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.
@@ -165,7 +165,13 @@ export function lazyCheckoutLoaderJs(options: LazyCheckoutLoaderOptions): string
165
165
  document.addEventListener("visibilitychange", function () {
166
166
  if (document.visibilityState === "hidden") endCartNavigation();
167
167
  });
168
+ // A shopper action belongs to the page lifecycle it was taken in. Leaving the page
169
+ // starts a new one, so an action whose async work (session, gate, drawer) settles
170
+ // after a back-forward-cache restore is abandoned rather than replayed.
171
+ var lifecycle = 0;
172
+ function current(gen) { return gen === lifecycle; }
168
173
  window.addEventListener("pagehide", function () {
174
+ lifecycle++;
169
175
  endCartNavigation();
170
176
  dropHeld();
171
177
  });
@@ -389,7 +395,9 @@ export function lazyCheckoutLoaderJs(options: LazyCheckoutLoaderOptions): string
389
395
  }
390
396
  if (event.defaultPrevented) return;
391
397
  event.preventDefault();
398
+ var gen = lifecycle;
392
399
  whenReady(anchor, function () {
400
+ if (!current(gen)) return;
393
401
  stampAnchor(anchor);
394
402
  replay(anchor, function () { anchor.click(); });
395
403
  });
@@ -427,8 +435,9 @@ export function lazyCheckoutLoaderJs(options: LazyCheckoutLoaderOptions): string
427
435
  note.textContent = ${literal(CART_UNAVAILABLE_MESSAGE)};
428
436
  form.insertAdjacentElement('afterend', note);
429
437
  }
430
- function openSessionThen(form, fn) {
438
+ function openSessionThen(form, gen, fn) {
431
439
  ensureSession().then(function (id) {
440
+ if (!current(gen)) return;
432
441
  if (!id) { showCartUnavailable(form); return; }
433
442
  form.dataset.cartSession = '1';
434
443
  fn();
@@ -441,6 +450,10 @@ export function lazyCheckoutLoaderJs(options: LazyCheckoutLoaderOptions): string
441
450
  var managed = form.matches(${managedCart});
442
451
  var cartForm = targetsCart(form.getAttribute('action'));
443
452
  if (!managed && !cartForm) return;
453
+ var gen = lifecycle;
454
+ function resubmitLive() {
455
+ if (current(gen)) replay(form, function () { resubmit(form); });
456
+ }
444
457
  if (form.dataset.cartSession === '1') {
445
458
  if (!cartForm || settled()) {
446
459
  if (!event.defaultPrevented && settled()) {
@@ -455,18 +468,23 @@ export function lazyCheckoutLoaderJs(options: LazyCheckoutLoaderOptions): string
455
468
  return;
456
469
  }
457
470
  event.preventDefault();
458
- whenReady(form, function () { stampForm(form); replay(form, function () { resubmit(form); }); });
471
+ whenReady(form, function () {
472
+ if (!current(gen)) return;
473
+ stampForm(form);
474
+ resubmitLive();
475
+ });
459
476
  return;
460
477
  }
461
478
  event.preventDefault();
462
479
  function afterGate() {
463
- openSessionThen(form, function () {
480
+ if (!current(gen)) return;
481
+ openSessionThen(form, gen, function () {
464
482
  if (cartForm) {
465
483
  stampForm(form);
466
- whenReady(form, function () { replay(form, function () { resubmit(form); }); });
484
+ whenReady(form, resubmitLive);
467
485
  return;
468
486
  }
469
- resubmit(form);
487
+ if (current(gen)) resubmit(form);
470
488
  });
471
489
  }
472
490
  if (storeNotPublic && managed && form.dataset.checkoutReachable !== '1') {
@@ -481,7 +499,7 @@ export function lazyCheckoutLoaderJs(options: LazyCheckoutLoaderOptions): string
481
499
  }).then(function (res) {
482
500
  if (res.status === 401 && res.headers.get('x-tot-gate') === 'store-not-public') {
483
501
  return res.json().then(function (body) { return body && body.signIn; }, function () { return null; })
484
- .then(function (signIn) { showUnavailable(form, signIn); });
502
+ .then(function (signIn) { if (current(gen)) showUnavailable(form, signIn); });
485
503
  }
486
504
  form.dataset.checkoutReachable = '1';
487
505
  afterGate();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tokenoftrust/storefront-runner",
3
- "version": "4.6.3",
3
+ "version": "4.6.4",
4
4
  "license": "SEE LICENSE IN LICENSE",
5
5
  "description": "World-shareable storefront runner: multi-tenant renderer on Astro/Cloudflare. No control plane.",
6
6
  "packageManager": "pnpm@11.9.0",
@@ -86,6 +86,10 @@ export function createContactSalesActionSource(
86
86
  subscribe(listener) {
87
87
  if (!document || typeof document.querySelectorAll !== "function") return () => {};
88
88
  const removals: Array<() => void> = [];
89
+ // A deferred navigation belongs to this subscription. Teardown (pagehide) abandons
90
+ // it, so a back-forward-cache restore never resumes a navigation the shopper left.
91
+ let live = true;
92
+ removals.push(() => { live = false; });
89
93
  const bound = new Set<ContactSalesActionElement>();
90
94
  for (const source of CONTACT_SALES_ACTION_SOURCES) {
91
95
  const nodes = document.querySelectorAll(source.selector);
@@ -110,7 +114,7 @@ export function createContactSalesActionSource(
110
114
  // A full-document navigation destroys the isolated GA4 iframe. Give its
111
115
  // MessagePort one bounded turn to receive the reviewed action first.
112
116
  if (navigationHref) {
113
- schedule(() => options.navigate?.(navigationHref), CONTACT_SALES_NAVIGATION_DEFER_MS);
117
+ schedule(() => { if (live) options.navigate?.(navigationHref); }, CONTACT_SALES_NAVIGATION_DEFER_MS);
114
118
  }
115
119
  };
116
120
  node.addEventListener("click", emit);