@tokenoftrust/storefront-runner 2.4.39 → 2.4.40
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/apps/storefront/env.d.ts +11 -0
- package/apps/storefront/src/components/chrome/SiteFooter.astro +2 -2
- package/apps/storefront/src/middleware/renderChain.ts +6 -0
- package/apps/storefront/src/pages/index.astro +2 -2
- package/apps/storefront/src/pages/products/[handle].astro +3 -3
- package/package.json +1 -1
package/apps/storefront/env.d.ts
CHANGED
|
@@ -141,6 +141,17 @@ declare global {
|
|
|
141
141
|
* inside this prefix on the client.
|
|
142
142
|
*/
|
|
143
143
|
basePath: string;
|
|
144
|
+
/**
|
|
145
|
+
* The tenant's REAL routed prefix, always — never overridden to a candidate/
|
|
146
|
+
* revision-pinned render's own prefix the way {@link basePath} is. A pinned
|
|
147
|
+
* preview (`/preview/render/<tenant>/<changeId>`, `/preview/rev/<sha>`,
|
|
148
|
+
* `/preview/staged/<sha>`) has no API mirror under its prefix (the catch-all
|
|
149
|
+
* preview route only renders pages), so any same-origin fetch to a real
|
|
150
|
+
* backend endpoint (`/api/...`) must be built from THIS, not `basePath`, or
|
|
151
|
+
* it 404s under preview. Nav hrefs still want `basePath` so they stay inside
|
|
152
|
+
* the pinned render.
|
|
153
|
+
*/
|
|
154
|
+
apiBasePath: string;
|
|
144
155
|
/**
|
|
145
156
|
* Which customization environment to serve. "live" on production
|
|
146
157
|
* hosts; "test" only behind a valid, tenant-bound preview token on a
|
|
@@ -50,7 +50,7 @@ const {
|
|
|
50
50
|
class: className,
|
|
51
51
|
} = Astro.props;
|
|
52
52
|
|
|
53
|
-
const { basePath } = Astro.locals;
|
|
53
|
+
const { basePath, apiBasePath } = Astro.locals;
|
|
54
54
|
const hrefOf = (l: ChromeLink) => (l.external ? l.href : withBase(basePath, l.href));
|
|
55
55
|
const showLogo = !!brand?.logo && /^(https?:|\/)/.test(brand.logo.src);
|
|
56
56
|
const columnLinkStamp = (l: ChromeLink, colIndex: number, index: number) =>
|
|
@@ -184,7 +184,7 @@ const collapsible = variant === "light-collapsible";
|
|
|
184
184
|
{newsletter.title && <h3 class="font-display text-base font-semibold">{newsletter.title}</h3>}
|
|
185
185
|
{newsletter.body && <p class="mt-1 text-sm text-muted">{newsletter.body}</p>}
|
|
186
186
|
<form
|
|
187
|
-
action={withBase(
|
|
187
|
+
action={withBase(apiBasePath, newsletter.action ?? "/api/newsletter")}
|
|
188
188
|
method="post"
|
|
189
189
|
class="mt-4 flex max-w-md flex-col gap-3 sm:flex-row sm:flex-wrap"
|
|
190
190
|
>
|
|
@@ -668,6 +668,12 @@ export async function runRenderChain(input: RenderChainInput): Promise<Response>
|
|
|
668
668
|
// `/preview/render/<tenant>/<changeId>`) so a pinned page's OWN generated
|
|
669
669
|
// links stay inside that candidate instead of escaping to the live host.
|
|
670
670
|
locals.basePath = candidatePin ? candidatePin.basePath : basePath;
|
|
671
|
+
// ALWAYS the real tenant-routed prefix, even under a candidate/revision pin —
|
|
672
|
+
// fb-1791905696516-ubw8a3: a pinned preview's catch-all route only knows how to
|
|
673
|
+
// RENDER a page, so same-origin fetches to a real `/api/...` backend endpoint
|
|
674
|
+
// (checkout handoff, compliance preflight, newsletter) must stay off `basePath`
|
|
675
|
+
// once it's pin-overridden, or they 404 under preview.
|
|
676
|
+
locals.apiBasePath = basePath;
|
|
671
677
|
// Renderer-owned four-stage browser environment (ADR 0014 E1 / 0017), computed
|
|
672
678
|
// ONCE here from authoritative request state and serialized before app code by
|
|
673
679
|
// both document mounts (Layout.astro + rawHtmlResponse). The candidate render
|
|
@@ -43,7 +43,7 @@ import {
|
|
|
43
43
|
type CommerceStaticResolved,
|
|
44
44
|
} from "@/lib/homeVisualParity";
|
|
45
45
|
|
|
46
|
-
const { tenant, tot, content, basePath, canonicalBase, features, capabilities } = Astro.locals;
|
|
46
|
+
const { tenant, tot, content, basePath, apiBasePath, canonicalBase, features, capabilities } = Astro.locals;
|
|
47
47
|
const marketing = !features.catalog;
|
|
48
48
|
|
|
49
49
|
// Raw-HTML home (vendor-injected): serve the tenant's own self-contained
|
|
@@ -296,7 +296,7 @@ const homeSectionWidgets = marketing
|
|
|
296
296
|
</h2>
|
|
297
297
|
{b.body && <p class="mt-3 text-[var(--color-muted)]">{b.body}</p>}
|
|
298
298
|
<form
|
|
299
|
-
action={withBase(
|
|
299
|
+
action={withBase(apiBasePath, "/api/newsletter")}
|
|
300
300
|
method="post"
|
|
301
301
|
class="mx-auto mt-7 flex max-w-md flex-col gap-3 sm:flex-row"
|
|
302
302
|
>
|
|
@@ -49,7 +49,7 @@ import { getRegistryService } from "@/lib/apps/registryAccess.js";
|
|
|
49
49
|
import { renderWidgetSlot } from "@/lib/apps/widgets/renderSlot.js";
|
|
50
50
|
|
|
51
51
|
const { handle } = Astro.params;
|
|
52
|
-
const { tenant, tot, canonicalBase, basePath, commerce } = Astro.locals;
|
|
52
|
+
const { tenant, tot, canonicalBase, basePath, apiBasePath, commerce } = Astro.locals;
|
|
53
53
|
// Commerce-only route: marketing tenants have no product detail pages.
|
|
54
54
|
if (!Astro.locals.features.catalog) return new Response(null, { status: 404 });
|
|
55
55
|
const compliance = tenant.compliance;
|
|
@@ -380,7 +380,7 @@ const jsonLd = [productJsonLd, breadcrumbLd(crumbs, canonicalBase)];
|
|
|
380
380
|
productTitle={product.title}
|
|
381
381
|
checkoutCart={checkoutCart}
|
|
382
382
|
pricing={pricing}
|
|
383
|
-
handoffUrl={withBase(
|
|
383
|
+
handoffUrl={withBase(apiBasePath, "/api/commerce/checkout-handoff")}
|
|
384
384
|
/>
|
|
385
385
|
{/* Regulated-tenant checkout ENFORCEMENT gate. Intercepts the
|
|
386
386
|
managed-cart handoff to clear age + ship-to compliance server-side
|
|
@@ -389,7 +389,7 @@ const jsonLd = [productJsonLd, breadcrumbLd(crumbs, canonicalBase)];
|
|
|
389
389
|
{checkoutCart && isRegulatedTenant(tenant) && compliance?.minAge && (
|
|
390
390
|
<CheckoutComplianceGate
|
|
391
391
|
client:load
|
|
392
|
-
preflightUrl={withBase(
|
|
392
|
+
preflightUrl={withBase(apiBasePath, "/api/compliance/preflight")}
|
|
393
393
|
minAge={compliance.minAge}
|
|
394
394
|
/>
|
|
395
395
|
)}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tokenoftrust/storefront-runner",
|
|
3
|
-
"version": "2.4.
|
|
3
|
+
"version": "2.4.40",
|
|
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",
|