@tokenoftrust/storefront-runner 4.0.1 → 4.1.1

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.
@@ -32,6 +32,7 @@ import {
32
32
  withShippingPolicy,
33
33
  withReturnUrl,
34
34
  trustedCartReturnUrl,
35
+ CHECKOUT_NAVIGATION_REFERRER_POLICY,
35
36
  SHIPPING_UNAVAILABLE_CART_MESSAGE,
36
37
  ShippingPolicyUnavailableError,
37
38
  type CartDenialCode,
@@ -183,6 +184,7 @@ async function cartFormPost(context: APIContext): Promise<Response> {
183
184
  headers: {
184
185
  location,
185
186
  "cache-control": "no-store",
187
+ "referrer-policy": CHECKOUT_NAVIGATION_REFERRER_POLICY,
186
188
  "x-tot-payment-authorized": "false",
187
189
  },
188
190
  });
@@ -233,7 +235,7 @@ function sampleResponse(body: string | null, status: number, location?: string):
233
235
  return new Response(body, { status, headers: {
234
236
  "content-type": "application/json; charset=utf-8",
235
237
  "cache-control": "private, no-store",
236
- "referrer-policy": "no-referrer",
238
+ "referrer-policy": location ? CHECKOUT_NAVIGATION_REFERRER_POLICY : "no-referrer",
237
239
  "x-robots-tag": "noindex, nofollow",
238
240
  "x-tot-payment-authorized": "false",
239
241
  ...(location ? { location } : {}),
@@ -38,6 +38,7 @@
38
38
  * published (CloudFront/S3) output — both render Layout.astro. The session id
39
39
  * is not part of that HTML.
40
40
  */
41
+ import { CHECKOUT_NAVIGATION_REFERRER_POLICY } from "@tot/public-runtime";
41
42
  import { CART_UNAVAILABLE_MESSAGE } from "@/lib/checkout/commerce/cartSession";
42
43
 
43
44
  export const LAZY_CHECKOUT_LOADER_IDLE_MS = 8000;
@@ -77,6 +78,7 @@ export function lazyCheckoutLoaderJs(options: LazyCheckoutLoaderOptions): string
77
78
  var cartOrigin = ${literal(options.cartOrigin)};
78
79
  var sessionHref = ${literal(options.sessionHref)};
79
80
  var cookiePath = ${literal(options.cookiePath)};
81
+ var referrerPolicy = ${literal(CHECKOUT_NAVIGATION_REFERRER_POLICY)};
80
82
  var storeNotPublic = ${options.storeNotPublic ? "true" : "false"};
81
83
  var events = ${literal(LAZY_CHECKOUT_LOADER_EVENTS)};
82
84
  var state = 'idle';
@@ -87,6 +89,46 @@ export function lazyCheckoutLoaderJs(options: LazyCheckoutLoaderOptions): string
87
89
  if (!url) return false;
88
90
  try { return new URL(url, document.baseURI).origin === cartOrigin; } catch (e) { return false; }
89
91
  }
92
+ // Foxy copies the Referer onto cart_cancel_and_continue_link and ignores a
93
+ // continue parameter. The document policy stays strict-origin. A cart script
94
+ // snapshots the Referer while its src is assigned, so that assignment uses the
95
+ // store-home URL and restores the page before the turn ends. A cart navigation
96
+ // sends the full document URL through the element's referrerPolicy. That URL
97
+ // already includes the store origin and tenant path, and the history entry
98
+ // stays the page that opened the cart.
99
+ function storeHome() {
100
+ return location.origin + cookiePath;
101
+ }
102
+ function revealStoreHome() {
103
+ var home = storeHome();
104
+ if (location.href === home) return;
105
+ window.history.replaceState(window.history.state, "", home);
106
+ }
107
+ function restoreHref(current) {
108
+ if (location.href !== current) window.history.replaceState(window.history.state, "", current);
109
+ }
110
+ var origCreate = document.createElement;
111
+ document.createElement = function (name) {
112
+ var el = origCreate.apply(document, arguments);
113
+ if (String(name).toLowerCase() !== "script") return el;
114
+ var proto = window.HTMLScriptElement && window.HTMLScriptElement.prototype;
115
+ var srcDesc = proto && Object.getOwnPropertyDescriptor(proto, "src");
116
+ if (!srcDesc || !srcDesc.get || !srcDesc.set) return el;
117
+ Object.defineProperty(el, "src", {
118
+ configurable: true,
119
+ enumerable: true,
120
+ get: function () { return srcDesc.get.call(this); },
121
+ set: function (value) {
122
+ if (!targetsCart(value)) return srcDesc.set.call(this, value);
123
+ this.referrerPolicy = referrerPolicy;
124
+ var current = location.href;
125
+ revealStoreHome();
126
+ try { return srcDesc.set.call(this, value); }
127
+ finally { restoreHref(current); }
128
+ }
129
+ });
130
+ return el;
131
+ };
90
132
  function commerceReachable() {
91
133
  var nodes = document.querySelectorAll(${managedCart} + ', a[href], form[action]');
92
134
  for (var i = 0; i < nodes.length; i++) {
@@ -209,6 +251,7 @@ export function lazyCheckoutLoaderJs(options: LazyCheckoutLoaderOptions): string
209
251
  if (event.button !== 0 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) return;
210
252
  var anchor = event.target && event.target.closest ? event.target.closest('a[href]') : null;
211
253
  if (!anchor || anchor.target === '_blank' || !targetsCart(anchor.href)) return;
254
+ anchor.referrerPolicy = referrerPolicy;
212
255
  if (state === 'ready') {
213
256
  stampAnchor(anchor);
214
257
  return;
@@ -268,7 +311,12 @@ export function lazyCheckoutLoaderJs(options: LazyCheckoutLoaderOptions): string
268
311
  var cartForm = targetsCart(form.getAttribute('action'));
269
312
  if (!managed && !cartForm) return;
270
313
  if (form.dataset.cartSession === '1') {
271
- if (!cartForm || state === 'ready') return;
314
+ if (!cartForm || state === 'ready') {
315
+ if (!event.defaultPrevented && state === 'ready') {
316
+ form.referrerPolicy = referrerPolicy;
317
+ }
318
+ return;
319
+ }
272
320
  event.preventDefault();
273
321
  whenReady(function () { stampForm(form); resubmit(form); });
274
322
  return;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tokenoftrust/storefront-runner",
3
- "version": "4.0.1",
3
+ "version": "4.1.1",
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",
@@ -212,6 +212,22 @@ export const INTERNAL_CART_FIELDS = ["currency", "tenant", "verification", "ship
212
212
  /** The signed cart option the checkout store's Continue Shopping link reads (`withReturnUrl`). */
213
213
  export const RETURN_URL_CART_FIELD = "return_url";
214
214
 
215
+ /**
216
+ * Referrer policy for a request the browser makes to the managed checkout (a cart
217
+ * script, a cart navigation, or the hop that follows a 303 onto the cart).
218
+ *
219
+ * Measured 2026-10-24 against tot-preview: Foxy sets `cart_cancel_and_continue_link`
220
+ * from the Referer and ignores a `continue` parameter. An origin-only Referer
221
+ * overwrites a fuller one; the first request with no Referer falls back to the
222
+ * store's configured store URL (the host root). `strict-origin-when-cross-origin`
223
+ * sends only that origin on a cross-origin checkout request, so empty-cart and
224
+ * receipt links drop the tenant path. This value sends the full URL on https and
225
+ * nothing on a downgrade. It is not the document policy: a redirect response's
226
+ * Referrer-Policy replaces the element's, and `no-referrer` on that response
227
+ * clears the path. The site-wide header stays strict-origin.
228
+ */
229
+ export const CHECKOUT_NAVIGATION_REFERRER_POLICY = "no-referrer-when-downgrade";
230
+
215
231
  /** The signed cart option the checkout store reads the shipping charge from. */
216
232
  export const SHIPPING_POLICY_CART_FIELD = "shipping_policy";
217
233