@fayz-ai/storefront 0.85.2 → 0.86.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/analytics/index.d.ts +1 -0
- package/dist/analytics/index.d.ts.map +1 -1
- package/dist/analytics/meta-pageview.d.ts +23 -0
- package/dist/analytics/meta-pageview.d.ts.map +1 -0
- package/dist/analytics/meta-pixel.d.ts +11 -5
- package/dist/analytics/meta-pixel.d.ts.map +1 -1
- package/dist/{chunk-BR3XHNVM.js → chunk-2JMPPAXL.js} +3 -3
- package/dist/{chunk-BR3XHNVM.js.map → chunk-2JMPPAXL.js.map} +1 -1
- package/dist/{chunk-ELW7QWDJ.js → chunk-G4WCU526.js} +4 -3
- package/dist/chunk-G4WCU526.js.map +1 -0
- package/dist/{chunk-CE4U65TZ.js → chunk-Z2NKBC42.js} +41 -3
- package/dist/chunk-Z2NKBC42.js.map +1 -0
- package/dist/edge.js +5 -1
- package/dist/edge.js.map +1 -1
- package/dist/host/edge-handler.d.ts.map +1 -1
- package/dist/host/store-host.d.ts +9 -0
- package/dist/host/store-host.d.ts.map +1 -1
- package/dist/host.js +39 -9
- package/dist/host.js.map +1 -1
- package/dist/index.js +5 -5
- package/dist/render.js +3 -3
- package/dist/tanstack.js +2 -2
- package/package.json +4 -4
- package/dist/chunk-CE4U65TZ.js.map +0 -1
- package/dist/chunk-ELW7QWDJ.js.map +0 -1
|
@@ -229,6 +229,44 @@ function menuDataNeeds() {
|
|
|
229
229
|
return Object.values(MENU_DATA_NEEDS).map(({ loader, params }) => ({ key: dataNeedKey(loader, params), loader, params }));
|
|
230
230
|
}
|
|
231
231
|
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
232
|
+
// src/analytics/meta-pageview.ts
|
|
233
|
+
var META_PAGEVIEW_BOT_UA = "bot|crawl|spider|slurp|facebookexternalhit|bingpreview|whatsapp|telegram";
|
|
234
|
+
function metaPageViewInlineScript(pixelId) {
|
|
235
|
+
const id = pixelId.trim();
|
|
236
|
+
if (!/^[0-9]{10,20}$/.test(id)) return "";
|
|
237
|
+
return [
|
|
238
|
+
"(function(w,d,id){",
|
|
239
|
+
"if(w.__fzMetaPV)return;",
|
|
240
|
+
`if(new RegExp(${JSON.stringify(META_PAGEVIEW_BOT_UA)},'i').test(navigator.userAgent))return;`,
|
|
241
|
+
'function ck(n){var m=d.cookie.match(new RegExp("(?:^|; )"+n+"=([^;]*)"));return m?decodeURIComponent(m[1]):""}',
|
|
242
|
+
// The registrable domain, found the way fbevents.js finds it: the shortest
|
|
243
|
+
// suffix a cookie can be set on. Its label count minus one is the
|
|
244
|
+
// "subdomain index" Meta encodes in both ids (fb.2.… for a .com.br site).
|
|
245
|
+
'function dom(){var h=location.hostname.split(".");if(h.length<2||/^[0-9]+$/.test(h[h.length-1]))return null;',
|
|
246
|
+
'for(var i=h.length-2;i>=0;i--){var c=h.slice(i).join(".");d.cookie="_fz_ck=1;domain=."+c+";path=/";',
|
|
247
|
+
'if(ck("_fz_ck")){d.cookie="_fz_ck=;domain=."+c+";path=/;max-age=0";return{name:c,idx:h.length-i-1}}}return null}',
|
|
248
|
+
'function set(n,v,dm){d.cookie=n+"="+v+";path=/;max-age=7776000;SameSite=Lax"+(dm?";domain=."+dm.name:"")+(location.protocol==="https:"?";Secure":"")}',
|
|
249
|
+
"var dm=dom(),ix=dm?dm.idx:0,now=Date.now();",
|
|
250
|
+
'if(!ck("_fbp"))set("_fbp","fb."+ix+"."+now+"."+String(Math.random()).slice(2,11)+String(Math.random()).slice(2,11),dm);',
|
|
251
|
+
"var clid=(location.search.match(/[?&]fbclid=([^&#]*)/)||[])[1];",
|
|
252
|
+
'if(clid){clid=decodeURIComponent(clid);var fbc=ck("_fbc");if(!fbc||fbc.slice(fbc.lastIndexOf(".")+1)!==clid)set("_fbc","fb."+ix+"."+now+"."+clid,dm)}',
|
|
253
|
+
'w.__fzMetaPV=function(){var eid="pv."+Date.now()+"."+Math.random().toString(36).slice(2,10);',
|
|
254
|
+
'var p={id:id,ev:"PageView",dl:location.href,rl:d.referrer,"if":w.top!==w?"true":"false",ts:Date.now(),',
|
|
255
|
+
'sw:screen.width,sh:screen.height,fbp:ck("_fbp"),fbc:ck("_fbc"),eid:eid,rqm:"GET"},s=[];',
|
|
256
|
+
'for(var k in p)if(p[k]!==""&&p[k]!=null)s.push(encodeURIComponent(k)+"="+encodeURIComponent(p[k]));',
|
|
257
|
+
'var i=new Image();i.src="https://www.facebook.com/tr/?"+s.join("&");',
|
|
258
|
+
"w.__fzMetaPVLast=location.pathname+location.search;return eid};",
|
|
259
|
+
"w.__fzMetaPV()",
|
|
260
|
+
`})(window,document,${JSON.stringify(id)})`
|
|
261
|
+
].join("");
|
|
262
|
+
}
|
|
263
|
+
function sendMetaPageView() {
|
|
264
|
+
if (typeof window === "undefined" || typeof window.__fzMetaPV !== "function") return false;
|
|
265
|
+
const here = window.location.pathname + window.location.search;
|
|
266
|
+
if (window.__fzMetaPVLast !== here) window.__fzMetaPV();
|
|
267
|
+
return true;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
export { MENU_DATA_NEEDS, isKnownPath, isPublicPath, menuDataNeeds, menuJsonLd, menuSeo, metaPageViewInlineScript, pageSeo, primaryImageUrl, productJsonLd, productSeo, resolveStorefrontRoute, sendMetaPageView, storefrontPaths };
|
|
271
|
+
//# sourceMappingURL=chunk-Z2NKBC42.js.map
|
|
272
|
+
//# sourceMappingURL=chunk-Z2NKBC42.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/lib/seo.ts","../src/app/serving.ts","../src/data/menu-needs.ts","../src/analytics/meta-pageview.ts"],"names":[],"mappings":"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","file":"chunk-Z2NKBC42.js","sourcesContent":["import { breadcrumbJsonLd, seoOrigin } from '@fayz-ai/core'\nimport { currentStaticRenderPass } from '../app/static-render'\nimport type { Category, Product } from '@fayz-ai/shop/types'\nimport { imageVariantUrl } from '@fayz-ai/shop'\nimport type { ResolvedStorefrontConfig } from '../document/config'\nimport type { StaticHeadInput } from '../app/static-render'\n\n// ---------------------------------------------------------------------------\n// What a storefront route should tell a crawler about itself.\n//\n// Pure builders returning a head declaration, so the page that knows what it is\n// rendering also declares its own SEO — rather than a renderer re-deriving the\n// routing table on the server to guess. Same reason `useStorefrontHead` reports\n// the title instead of letting `renderStore` infer it.\n//\n// Absolute URLs need an origin the store does not know at build time. Callers\n// pass one explicitly (a server render knows the request host); in a browser it\n// falls back to the live origin. Without either, canonical and OG URLs are\n// omitted rather than emitted relative, because a relative canonical is ignored\n// by search engines and a relative og:image renders as a broken share.\n// ---------------------------------------------------------------------------\n\nexport interface StorefrontSeoContext {\n config: ResolvedStorefrontConfig\n /** In-app path, e.g. '/product/pork-ribs'. */\n path: string\n /** Absolute origin, e.g. 'https://loja.example.com'. */\n origin?: string\n}\n\nfunction absolute(ctx: StorefrontSeoContext, path = ctx.path): string | undefined {\n // Explicit wins, then the origin the static render was told about, then the\n // live browser origin. A page never has to know which of the three it is in.\n const origin = (ctx.origin ?? currentStaticRenderPass()?.origin ?? seoOrigin()).replace(/\\/$/, '')\n if (!origin) return undefined\n return `${origin}${path.startsWith('/') ? path : `/${path}`}`\n}\n\n/** Primary image, falling back to the first — how product cards pick one. */\nexport function primaryImageUrl(product: Product): string | undefined {\n return (product.images?.find((i) => i.isPrimary) ?? product.images?.[0])?.url\n}\n\n/**\n * The primary image as a share card wants it: 1200 px wide when the store\n * serves variants, so a WhatsApp/OG scraper does not pull the 4000 px\n * original — and the same URL for every scraper, so it is cached once.\n */\nfunction shareImageUrl(product: Product, ctx: StorefrontSeoContext): string | undefined {\n const url = primaryImageUrl(product)\n if (!url) return undefined\n return ctx.config.imageLoading.transform === 'supabase' ? imageVariantUrl(url, { width: 1200, quality: 82 }) : url\n}\n\n/**\n * schema.org Product. Availability is derived from inventory rather than\n * declared, because a rich result advertising an in-stock product that is not\n * is worse than no rich result.\n */\nexport function productJsonLd(\n product: Product,\n ctx: StorefrontSeoContext,\n): Record<string, unknown> {\n const url = absolute(ctx)\n const image = shareImageUrl(product, ctx)\n return {\n '@context': 'https://schema.org',\n '@type': 'Product',\n name: product.name,\n ...(product.description ? { description: product.description } : {}),\n ...(product.sku ? { sku: product.sku } : {}),\n ...(image ? { image: [image] } : {}),\n offers: {\n '@type': 'Offer',\n ...(url ? { url } : {}),\n price: product.price,\n priceCurrency: product.currency ?? ctx.config.currency,\n availability:\n (product.inventoryCount ?? 0) > 0\n ? 'https://schema.org/InStock'\n : 'https://schema.org/OutOfStock',\n },\n }\n}\n\n/** The head declaration for a product page. */\nexport function productSeo(product: Product, ctx: StorefrontSeoContext): StaticHeadInput {\n const url = absolute(ctx)\n const home = absolute(ctx, '/')\n const catalog = absolute(ctx, ctx.config.catalogPath)\n return {\n title: `${product.name} — ${ctx.config.name}`,\n description: product.description?.slice(0, 160),\n canonical: url,\n image: shareImageUrl(product, ctx),\n type: 'product',\n jsonLd: [\n productJsonLd(product, ctx),\n ...(home && catalog && url\n ? [\n breadcrumbJsonLd([\n { name: ctx.config.name, url: home },\n { name: 'Loja', url: catalog },\n { name: product.name, url },\n ]),\n ]\n : []),\n ],\n }\n}\n\n/** The head declaration for any non-product route: canonical only. */\nexport function pageSeo(\n ctx: StorefrontSeoContext,\n input: { title: string; description?: string | null },\n): StaticHeadInput {\n return { ...input, canonical: absolute(ctx), type: 'website' }\n}\n\n/**\n * Who serves the menu, as the store knows itself. Everything optional: a\n * field the store has not filled is left out of the JSON-LD rather than\n * guessed, because a wrong phone number in a rich result is worse than none.\n */\nexport interface MenuBusiness {\n /** schema.org type. 'Restaurant' unless the house is better described by\n * another FoodEstablishment subtype (e.g. 'FastFoodRestaurant', 'Bakery'). */\n type?: string\n description?: string\n telephone?: string\n email?: string\n /** Absolute URL. */\n logo?: string\n /** Absolute URL; the cover photo. */\n image?: string\n servesCuisine?: string | string[]\n priceRange?: string\n address?: { streetAddress?: string; locality?: string; region?: string; postalCode?: string; country?: string }\n /** Cities or neighbourhoods delivered to, by name. */\n areaServed?: string[]\n /** schema.org openingHours, e.g. ['Mo-Fr 11:00-22:00']. */\n openingHours?: string[]\n sameAs?: string[]\n}\n\n/** The menu as the shopper sees it: category order, each dish under its\n * category, the uncategorised ones last (same grouping as `MenuPage`). */\nfunction menuSections(products: Product[], categories: Category[]) {\n const byCategory = new Map<string, Product[]>()\n const loose: Product[] = []\n for (const p of products) {\n const key = p.categoryId ?? p.categoryName ?? ''\n if (!key) { loose.push(p); continue }\n byCategory.set(key, [...(byCategory.get(key) ?? []), p])\n }\n const sections = categories\n .map((c) => ({ name: c.name, description: c.description, products: byCategory.get(c.id) ?? byCategory.get(c.name) ?? [] }))\n .filter((s) => s.products.length > 0)\n if (loose.length) sections.push({ name: 'Outros', description: null, products: loose })\n if (!sections.length && products.length) return [{ name: 'Cardápio', description: null, products }]\n return sections\n}\n\n/**\n * schema.org Restaurant with its Menu — what lets a search engine answer\n * \"delivery de costela defumada\" with this house, and read every dish and\n * price without running the page's JavaScript.\n *\n * Each dish links to its own product page, where the full Product entity\n * lives; here it is a MenuItem, which is what a dish on a menu is.\n */\nexport function menuJsonLd(\n input: { products: Product[]; categories: Category[]; business?: MenuBusiness },\n ctx: StorefrontSeoContext,\n): Record<string, unknown> {\n const url = absolute(ctx)\n const home = absolute(ctx, '/')\n const b = input.business ?? {}\n const currency = ctx.config.currency\n const address = b.address\n ? {\n '@type': 'PostalAddress',\n ...(b.address.streetAddress ? { streetAddress: b.address.streetAddress } : {}),\n ...(b.address.locality ? { addressLocality: b.address.locality } : {}),\n ...(b.address.region ? { addressRegion: b.address.region } : {}),\n ...(b.address.postalCode ? { postalCode: b.address.postalCode } : {}),\n addressCountry: b.address.country ?? 'BR',\n }\n : undefined\n return {\n '@context': 'https://schema.org',\n '@type': b.type ?? 'Restaurant',\n ...(home ? { '@id': `${home.replace(/\\/$/, '')}/#business` } : {}),\n name: ctx.config.name,\n ...(home ? { url: home } : {}),\n ...(b.description ? { description: b.description } : {}),\n ...(b.image ? { image: [b.image] } : {}),\n ...(b.logo ? { logo: b.logo } : {}),\n ...(b.telephone ? { telephone: b.telephone } : {}),\n ...(b.email ? { email: b.email } : {}),\n ...(b.servesCuisine ? { servesCuisine: b.servesCuisine } : {}),\n ...(b.priceRange ? { priceRange: b.priceRange } : {}),\n ...(address ? { address } : {}),\n ...(b.areaServed?.length ? { areaServed: b.areaServed.map((name) => ({ '@type': 'Place', name })) } : {}),\n ...(b.openingHours?.length ? { openingHours: b.openingHours } : {}),\n ...(b.sameAs?.length ? { sameAs: b.sameAs } : {}),\n ...(url ? { hasMenu: url } : {}),\n ...(url\n ? {\n potentialAction: {\n '@type': 'OrderAction',\n target: { '@type': 'EntryPoint', urlTemplate: url, actionPlatform: ['https://schema.org/DesktopWebPlatform', 'https://schema.org/MobileWebPlatform'] },\n deliveryMethod: 'http://purl.org/goodrelations/v1#DeliveryModeOwnFleet',\n },\n }\n : {}),\n menu: {\n '@type': 'Menu',\n name: `Cardápio — ${ctx.config.name}`,\n ...(url ? { url } : {}),\n inLanguage: 'pt-BR',\n hasMenuSection: menuSections(input.products, input.categories).map((section) => ({\n '@type': 'MenuSection',\n name: section.name,\n ...(section.description ? { description: section.description } : {}),\n hasMenuItem: section.products.map((p) => {\n const image = shareImageUrl(p, ctx)\n const itemUrl = absolute(ctx, `/product/${p.slug}`)\n return {\n '@type': 'MenuItem',\n name: p.name,\n ...(p.description ? { description: p.description.slice(0, 300) } : {}),\n ...(image ? { image } : {}),\n ...(itemUrl ? { url: itemUrl } : {}),\n offers: {\n '@type': 'Offer',\n price: p.price,\n priceCurrency: p.currency ?? currency,\n availability: (p.inventoryCount ?? 0) > 0 ? 'https://schema.org/InStock' : 'https://schema.org/OutOfStock',\n },\n }\n }),\n })),\n },\n }\n}\n\n/**\n * The head declaration for the delivery menu: its own title and description\n * (the store passes copy written for the query it should answer), the\n * Restaurant + Menu entity, and a breadcrumb back to the home page.\n */\nexport function menuSeo(\n input: {\n products: Product[]\n categories: Category[]\n title?: string\n description?: string | null\n business?: MenuBusiness\n },\n ctx: StorefrontSeoContext,\n): StaticHeadInput {\n const url = absolute(ctx)\n const home = absolute(ctx, '/')\n const names = input.products.slice(0, 4).map((p) => p.name).join(', ')\n return {\n title: input.title ?? `Delivery ${ctx.config.name} — peça online`,\n description:\n (input.description ?? (names ? `Peça online: ${names} e mais. Cardápio completo com preços.` : undefined))?.slice(0, 160),\n canonical: url,\n image: input.business?.image,\n type: 'website',\n jsonLd: [\n menuJsonLd(input, ctx),\n ...(home && url\n ? [breadcrumbJsonLd([{ name: ctx.config.name, url: home }, { name: 'Delivery', url }])]\n : []),\n ],\n }\n}\n","import type { ShopProvider } from '@fayz-ai/shop/provider'\nimport { matchPath } from './router'\nimport { matchContentPage } from '../blocks/page-blocks'\nimport { resolveConfig } from '../document/config'\nimport type { StorefrontConfig, ResolvedStorefrontConfig } from '../document/config'\n\n// ---------------------------------------------------------------------------\n// How a store is served, once its pages have URLs of their own.\n//\n// Two questions a host has to answer that the shell cannot: which paths exist\n// at all (what to prerender, what to put in a sitemap), and which of them are\n// the same for every visitor (what a shared cache may hold). Both are\n// properties of the route, so they are decided here rather than by each host\n// re-deriving them — and the second one has a failure mode a CDN makes\n// invisible: cache a cart or an order confirmation once and it is served to the\n// next shopper.\n// ---------------------------------------------------------------------------\n\nexport type StorefrontRouteMatch =\n | { kind: 'custom'; route: NonNullable<ResolvedStorefrontConfig['routes']>[number]; params: Record<string, string> }\n | { kind: 'product'; params: Record<string, string> }\n | { kind: 'order'; params: Record<string, string> }\n | { kind: 'checkout' }\n | { kind: 'account' }\n | { kind: 'catalog' }\n | { kind: 'menu' }\n | { kind: 'searchResults' }\n | { kind: 'policy'; policy: 'privacy' | 'terms' | 'returns' }\n | { kind: 'content'; slug: string; params: Record<string, string> }\n | { kind: 'home' }\n | { kind: 'notFound' }\n\n/**\n * What a path means, resolved once.\n *\n * The shell renders from this, cacheability is derived from it, and a host\n * framework asks it whether a URL exists at all before choosing a status code.\n * Those three answering separately is how a page ends up rendered as one thing\n * and cached or statused as another.\n *\n * The query string is stripped first: it addresses state WITHIN a page, so\n * `/?utm_source=instagram` is the home page — the single most common shape of\n * inbound marketing link, and a 404 for it is a silent, expensive bug.\n */\nexport function resolveStorefrontRoute(\n config: StorefrontConfig | ResolvedStorefrontConfig,\n rawPath: string,\n): StorefrontRouteMatch {\n const resolved = ensureResolved(config)\n const path = rawPath.split('?')[0] || '/'\n\n for (const route of resolved.routes ?? []) {\n const params = matchPath(route.path, path)\n if (params) return { kind: 'custom', route, params }\n }\n\n const product = matchPath('/product/:slug', path)\n if (product?.slug) return { kind: 'product', params: product }\n\n const order = matchPath('/order/:id', path)\n if (order?.id) return { kind: 'order', params: order }\n\n // Matched regardless of the feature flag: a disabled checkout still owns its\n // URL rather than falling through to an unrelated page.\n if (matchPath('/checkout', path)) return { kind: 'checkout' }\n if (matchPath('/account', path)) return { kind: 'account' }\n if (matchPath('/catalog', path)) return { kind: 'catalog' }\n // Só existe no modo delivery, e por isso é testado contra `resolved.delivery`\n // e não contra o literal '/menu': fora do modo, /menu é um 404 de verdade em\n // vez de uma página vazia — que é a diferença entre uma rota que não existe e\n // uma rota quebrada.\n if (resolved.delivery && matchPath(resolved.delivery.menuPath, path)) return { kind: 'menu' }\n if (resolved.search.mode === 'overlay' && matchPath(resolved.search.resultsPath, path)) {\n return { kind: 'searchResults' }\n }\n if (matchPath('/privacy', path)) return { kind: 'policy', policy: 'privacy' }\n if (matchPath('/terms', path)) return { kind: 'policy', policy: 'terms' }\n if (matchPath('/refunds', path)) return { kind: 'policy', policy: 'returns' }\n\n const content = matchContentPage(resolved.pages, path)\n if (content) return { kind: 'content', slug: content.slug, params: content.params }\n\n if (path === '/' || path === '') return { kind: 'home' }\n return { kind: 'notFound' }\n}\n\n/** Whether a URL exists at all — what a host needs before choosing a status. */\nexport function isKnownPath(\n config: StorefrontConfig | ResolvedStorefrontConfig,\n path: string,\n): boolean {\n return resolveStorefrontRoute(config, path).kind !== 'notFound'\n}\n\n/** Paths whose HTML is per-shopper and must never enter a shared cache. */\nconst PRIVATE_PATTERNS = ['/checkout', '/account', '/order/:id'] as const\n\n/** Paths the SDK serves that exist without knowing any product. The delivery\n * menu is added from the store's own `delivery.menuPath` — see `storefrontPaths`. */\nconst STATIC_PATTERNS = ['/', '/catalog', '/privacy', '/terms', '/refunds'] as const\n\nfunction ensureResolved(config: StorefrontConfig | ResolvedStorefrontConfig): ResolvedStorefrontConfig {\n return 'features' in config && 'pages' in config\n ? (config as ResolvedStorefrontConfig)\n : resolveConfig(config)\n}\n\n/**\n * Whether `path`'s HTML is identical for every visitor.\n *\n * Conservative by construction: a store's own route is private unless it says\n * otherwise, because guessing \"public\" is the direction that leaks one\n * shopper's page to another.\n */\nexport function isPublicPath(\n config: StorefrontConfig | ResolvedStorefrontConfig,\n path: string,\n): boolean {\n const match = resolveStorefrontRoute(config, path)\n switch (match.kind) {\n case 'checkout':\n case 'account':\n case 'order':\n return false\n case 'custom':\n return (\n match.route.kind !== 'checkout' &&\n match.route.kind !== 'account' &&\n match.route.chrome !== 'focused'\n )\n default:\n return true\n }\n}\n\n/**\n * Every path worth prerendering, and the same list a sitemap should carry.\n *\n * Product URLs come from the catalogue rather than the route table, so the\n * provider is asked rather than guessed at. Custom routes with parameters are\n * skipped: the SDK cannot know what values a store's own route accepts, and\n * emitting `/colecao/:slug` into a sitemap is worse than omitting it.\n */\nexport async function storefrontPaths(input: {\n config: StorefrontConfig | ResolvedStorefrontConfig\n provider: ShopProvider\n /** Include per-shopper routes. Off by default — they are not prerenderable. */\n includePrivate?: boolean\n}): Promise<string[]> {\n const resolved = ensureResolved(input.config)\n const paths = [...STATIC_PATTERNS] as string[]\n // The menu is the same for every visitor — what changes per shopper (address,\n // cart, open right now) resolves in the browser after hydration. It is listed\n // at the store's own path: a store whose menu lives at /delivery had /menu in\n // its sitemap and its real ordering page in none.\n if (resolved.delivery) paths.push(resolved.delivery.menuPath)\n\n for (const route of resolved.routes ?? []) {\n if (!route.path.includes(':')) paths.push(route.path)\n }\n\n const products = await input.provider.listProducts({ status: 'active' })\n for (const product of products) paths.push(`/product/${product.slug}`)\n\n const unique = Array.from(new Set(paths))\n return input.includePrivate ? unique : unique.filter((p) => isPublicPath(resolved, p))\n}\n","import { dataNeedKey } from '@fayz-ai/core'\n\n/**\n * The reads the delivery menu makes, named once so a server host can resolve\n * them before render. The menu is not a block tree, so nothing collects these\n * for it — a host that does not seed them sends a menu with no dishes in the\n * HTML, which is the page a crawler indexes.\n *\n * Its own module, free of React, because a host's route loader imports it on\n * every request.\n */\nexport const MENU_DATA_NEEDS = {\n products: { loader: 'products.list', params: {} },\n categories: { loader: 'categories.list', params: {} },\n featured: { loader: 'products.featured', params: {} },\n} as const\n\n/** The menu's data needs, keyed exactly as its `useLoader` calls will ask. */\nexport function menuDataNeeds(): Array<{ key: string; loader: string; params: Record<string, unknown> }> {\n return Object.values(MENU_DATA_NEEDS).map(({ loader, params }) => ({ key: dataNeedKey(loader, params), loader, params }))\n}\n","// ---------------------------------------------------------------------------\n// Meta's PageView, sent while the page is still being parsed.\n//\n// fbevents.js (~210 KB with its config, ~360 ms of main-thread work on a\n// mid-range phone) is deferred to the visitor's first interaction or 3 s after\n// load, so it stays out of the window PageSpeed measures. That left a hole: a\n// paid visitor who left within 3 s without touching the page never reached\n// Meta, and those are exactly the visits a campaign is optimised on.\n//\n// This closes it without bringing the script back: a few hundred bytes inlined\n// in the <head> that do what fbevents.js would do for a page view —\n// 1. adopt or create the first-party `_fbp` (browser id) and, when the URL\n// carries `fbclid`, the `_fbc` (click id) cookie, in Meta's own format and\n// on the same cookie domain fbevents.js picks, so the script adopts them\n// when it loads instead of minting a second id;\n// 2. send the PageView to /tr as the same GET image request fbevents.js\n// makes (id, ev, dl, rl, if, ts, sw, sh, fbp, fbc), with an event id that\n// a future Conversions API sender can deduplicate against.\n// It runs once per document and is exposed as `window.__fzMetaPV` so the SPA\n// can report each later navigation the same way. fbevents.js itself never\n// sends a PageView (see meta-pixel.ts): one view, one event.\n//\n// Plain ES5 text, not a bundled function: inline scripts are not transpiled,\n// and a minifier's helpers would not exist on the page.\n// ---------------------------------------------------------------------------\n\ndeclare global {\n interface Window {\n /** Sends a PageView for the current URL (inline boot script). Returns its event id. */\n __fzMetaPV?: () => string\n /** The URL (path + query) the last PageView was sent for. */\n __fzMetaPVLast?: string\n }\n}\n\n/** Crawlers and unfurlers are not visits. Real browsers, PageSpeed included, are. */\nexport const META_PAGEVIEW_BOT_UA = 'bot|crawl|spider|slurp|facebookexternalhit|bingpreview|whatsapp|telegram'\n\n/**\n * The inline <head> script for `pixelId` (digits only — anything else returns\n * an empty string, so a bad id can never become script text).\n */\nexport function metaPageViewInlineScript(pixelId: string): string {\n const id = pixelId.trim()\n if (!/^[0-9]{10,20}$/.test(id)) return ''\n return [\n '(function(w,d,id){',\n 'if(w.__fzMetaPV)return;',\n `if(new RegExp(${JSON.stringify(META_PAGEVIEW_BOT_UA)},'i').test(navigator.userAgent))return;`,\n 'function ck(n){var m=d.cookie.match(new RegExp(\"(?:^|; )\"+n+\"=([^;]*)\"));return m?decodeURIComponent(m[1]):\"\"}',\n // The registrable domain, found the way fbevents.js finds it: the shortest\n // suffix a cookie can be set on. Its label count minus one is the\n // \"subdomain index\" Meta encodes in both ids (fb.2.… for a .com.br site).\n 'function dom(){var h=location.hostname.split(\".\");if(h.length<2||/^[0-9]+$/.test(h[h.length-1]))return null;',\n 'for(var i=h.length-2;i>=0;i--){var c=h.slice(i).join(\".\");d.cookie=\"_fz_ck=1;domain=.\"+c+\";path=/\";',\n 'if(ck(\"_fz_ck\")){d.cookie=\"_fz_ck=;domain=.\"+c+\";path=/;max-age=0\";return{name:c,idx:h.length-i-1}}}return null}',\n 'function set(n,v,dm){d.cookie=n+\"=\"+v+\";path=/;max-age=7776000;SameSite=Lax\"+(dm?\";domain=.\"+dm.name:\"\")+(location.protocol===\"https:\"?\";Secure\":\"\")}',\n 'var dm=dom(),ix=dm?dm.idx:0,now=Date.now();',\n 'if(!ck(\"_fbp\"))set(\"_fbp\",\"fb.\"+ix+\".\"+now+\".\"+String(Math.random()).slice(2,11)+String(Math.random()).slice(2,11),dm);',\n 'var clid=(location.search.match(/[?&]fbclid=([^&#]*)/)||[])[1];',\n 'if(clid){clid=decodeURIComponent(clid);var fbc=ck(\"_fbc\");if(!fbc||fbc.slice(fbc.lastIndexOf(\".\")+1)!==clid)set(\"_fbc\",\"fb.\"+ix+\".\"+now+\".\"+clid,dm)}',\n 'w.__fzMetaPV=function(){var eid=\"pv.\"+Date.now()+\".\"+Math.random().toString(36).slice(2,10);',\n 'var p={id:id,ev:\"PageView\",dl:location.href,rl:d.referrer,\"if\":w.top!==w?\"true\":\"false\",ts:Date.now(),',\n 'sw:screen.width,sh:screen.height,fbp:ck(\"_fbp\"),fbc:ck(\"_fbc\"),eid:eid,rqm:\"GET\"},s=[];',\n 'for(var k in p)if(p[k]!==\"\"&&p[k]!=null)s.push(encodeURIComponent(k)+\"=\"+encodeURIComponent(p[k]));',\n 'var i=new Image();i.src=\"https://www.facebook.com/tr/?\"+s.join(\"&\");',\n 'w.__fzMetaPVLast=location.pathname+location.search;return eid};',\n 'w.__fzMetaPV()',\n `})(window,document,${JSON.stringify(id)})`,\n ].join('')\n}\n\n/**\n * Report a page view for the URL the visitor is on now. Returns false when the\n * boot script is not on the page (a host that does not inline it), so the\n * caller can fall back to fbevents.js. A second call for the URL the last view\n * was sent for is a no-op: hydration re-running the route effect is not a view.\n */\nexport function sendMetaPageView(): boolean {\n if (typeof window === 'undefined' || typeof window.__fzMetaPV !== 'function') return false\n const here = window.location.pathname + window.location.search\n if (window.__fzMetaPVLast !== here) window.__fzMetaPV()\n return true\n}\n"]}
|
package/dist/edge.js
CHANGED
|
@@ -154,7 +154,11 @@ function deferredEntryScript(src) {
|
|
|
154
154
|
function hydrateAfterFirstPaint(response) {
|
|
155
155
|
const Rewriter = globalThis.HTMLRewriter;
|
|
156
156
|
if (!Rewriter || !(response.headers.get("content-type") ?? "").includes("text/html")) return response;
|
|
157
|
-
return new Rewriter().on('
|
|
157
|
+
return new Rewriter().on('link[rel="modulepreload"]', {
|
|
158
|
+
element(el) {
|
|
159
|
+
el.remove();
|
|
160
|
+
}
|
|
161
|
+
}).on('script[type="module"][src]', {
|
|
158
162
|
element(el) {
|
|
159
163
|
const src = el.getAttribute("src");
|
|
160
164
|
if (!src || !el.hasAttribute("async") || src.includes("<")) return;
|
package/dist/edge.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/analytics/edge.ts","../src/host/edge-handler.ts"],"names":["response"],"mappings":"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","file":"edge.js","sourcesContent":["/**\n * The store's own door to PostHog, for a Worker (or any fetch-style) host.\n *\n * Events sent to the store's domain under `/ingest` are not dropped by ad\n * blockers the way requests to a known analytics host are, and the store needs\n * no PostHog setting of its own: every store forwards to the same project.\n *\n * if (url.pathname.startsWith('/ingest')) return posthogProxy(request)\n */\nexport interface PostHogProxyOptions {\n /** The path the storefront's analytics client sends to. Default `/ingest`. */\n prefix?: string\n /** PostHog's region. Default `us`. */\n region?: 'us' | 'eu'\n}\n\nexport function isPostHogProxyPath(pathname: string, prefix = '/ingest'): boolean {\n return pathname === prefix || pathname.startsWith(`${prefix}/`)\n}\n\nexport async function posthogProxy(request: Request, options: PostHogProxyOptions = {}): Promise<Response> {\n const prefix = options.prefix ?? '/ingest'\n const region = options.region ?? 'us'\n const url = new URL(request.url)\n const path = url.pathname.slice(prefix.length) || '/'\n // The SDK's script and its remote config live on the assets host.\n const host = path.startsWith('/static/') || path.startsWith('/array/')\n ? `${region}-assets.i.posthog.com`\n : `${region}.i.posthog.com`\n const target = new URL(`https://${host}${path}${url.search}`)\n\n const headers = new Headers(request.headers)\n headers.delete('cookie')\n headers.set('host', host)\n // PostHog geolocates from the visitor, not from the Worker.\n const ip = request.headers.get('cf-connecting-ip') ?? request.headers.get('x-forwarded-for')\n if (ip) headers.set('x-forwarded-for', ip)\n\n const init: RequestInit = { method: request.method, headers, redirect: 'manual' }\n if (request.method !== 'GET' && request.method !== 'HEAD') init.body = await request.arrayBuffer()\n const response = await fetch(target, init)\n const out = new Response(response.body, response)\n out.headers.delete('set-cookie')\n return out\n}\n","import { isPostHogProxyPath, posthogProxy } from '../analytics/edge'\nimport {\n EXPERIMENT_HEADER,\n NO_ASSIGNMENTS,\n assignExperiments,\n encodeExperimentHeader,\n experimentCacheSuffix,\n runningExperiments,\n isBotUserAgent,\n readVisitorId,\n readPinnedVisitorId,\n isPinnableVisitorId,\n visitorCookie,\n type ExperimentAssignments,\n type StorefrontExperiment,\n} from '../experiments/assign'\n\n// ---------------------------------------------------------------------------\n// The Worker in front of a server-rendered store.\n//\n// Five jobs a store's request handler has before the framework renders\n// anything, first written for artorious-shop and the same for every store:\n//\n// 1. Analytics through the store's own domain (`/ingest` → PostHog), so ad\n// blockers that drop requests to a known analytics host do not drop it.\n// 2. The store's photos from the store's own origin. The bytes live in\n// Supabase Storage; a photo addressed at the storage project is a second\n// origin the browser pays DNS + TLS for (measured 156–241 ms TTFB against\n// 112–141 ms same-origin). Only public objects of the `shop-images`\n// bucket are forwarded — this is not a general proxy.\n// 3. A real edge cache. Public pages already leave with `Cache-Control:\n// public, s-maxage=…`, but on workers.dev nothing in front honours it, so\n// every view paid the full render. The Workers Cache API makes the header\n// mean what it says. Marketing parameters (utm_*, fbclid…) are dropped\n// from the key: ad traffic hits the same entry as everyone else.\n// 4. `X-Robots-Tag: noindex` on any host that is not the store's address, so\n// the deployment URL never competes with the domain.\n// 5. A/B assignment. With a running experiment, every page request is\n// assigned its variants HERE, before render, locally with PostHog's\n// bucketing (experiments/assign.ts — no network), and the edge cache is\n// keyed by them: one entry per combination of variants, so a cached page\n// is never one visitor's variant served to another.\n// ---------------------------------------------------------------------------\n\n/** `/storage/v1/object/public/shop-images/…` or the `render/image` variant of it. */\nconst STORAGE_PATH = /^\\/storage\\/v1\\/(object|render\\/image)\\/public\\/(shop-images\\/[^?#]+)$/\n\n/** Query parameters the renderer understands. Anything else is dropped rather\n * than forwarded, so a crawler cannot mint unlimited distinct variants. */\nconst RENDER_PARAMS = new Set(['width', 'height', 'quality', 'format', 'resize'])\n\n/** A year: the object path carries a timestamp and is never overwritten. */\nconst IMMUTABLE = 'public, max-age=31536000, s-maxage=31536000, immutable'\n\nconst TRACKING_PARAM = /^(utm_[a-z0-9_]+|fbclid|gclid|gbraid|wbraid|msclkid|ttclid|igshid|mc_cid|mc_eid|_ga|ref)$/i\n\nexport function isStorageRequest(url: URL): boolean {\n return STORAGE_PATH.test(url.pathname)\n}\n\n/**\n * Which format this visitor gets — the only part of the request, besides the\n * URL, that changes the bytes. The renderer answers WebP or the original\n * depending on `accept` and declares no `Vary`, so every cache in front of it\n * has to put the answer in the key itself.\n */\nexport function imageFormatKey(request: Request): 'webp' | 'orig' {\n return /image\\/webp/.test(request.headers.get('accept') ?? '') ? 'webp' : 'orig'\n}\n\n/** Answer a storage request from Supabase, on the store's own origin. */\nexport async function serveStorageImage(request: Request, url: URL, supabaseUrl: string | undefined): Promise<Response> {\n const match = STORAGE_PATH.exec(url.pathname)\n if (!match) return new Response('Not found', { status: 404 })\n const base = supabaseUrl?.replace(/\\/$/, '')\n if (!base) return new Response('Storage is not configured', { status: 502 })\n\n const [, kind, objectPath] = match\n const upstream = new URL(`${base}/storage/v1/${kind}/public/${objectPath}`)\n for (const [key, value] of url.searchParams) {\n if (RENDER_PARAMS.has(key)) upstream.searchParams.set(key, value)\n }\n // A canonical `accept`, not the visitor's verbatim: two browsers that both\n // take WebP must produce the same upstream request.\n const webp = imageFormatKey(request) === 'webp'\n const response = await fetch(upstream.toString(), { headers: { accept: webp ? 'image/webp,image/*;q=0.8' : 'image/*' } })\n if (!response.ok) {\n // Upstream's status is passed through (a deleted photo must not look like\n // a 200), never its cache directives.\n return new Response(response.status === 404 ? 'Not found' : 'Upstream error', {\n status: response.status === 404 ? 404 : 502,\n headers: { 'cache-control': 'public, max-age=60' },\n })\n }\n const headers = new Headers()\n headers.set('content-type', response.headers.get('content-type') ?? 'image/jpeg')\n headers.set('cache-control', IMMUTABLE)\n headers.set('x-image-origin', 'supabase')\n headers.set('vary', 'Accept')\n return new Response(response.body, { status: 200, headers })\n}\n\n/** The cache key for a request, or null when it is not cacheable. `variant`\n * is the experiment part (`experimentCacheSuffix`); empty means control. */\nexport function edgeCacheKey(request: Request, buildId: string, variant = ''): Request | null {\n if (request.method !== 'GET') return null\n const url = new URL(request.url)\n for (const key of [...url.searchParams.keys()]) {\n if (TRACKING_PARAM.test(key)) url.searchParams.delete(key)\n }\n url.hash = ''\n if (isStorageRequest(url)) url.searchParams.set('_fmt', imageFormatKey(request))\n // Cached HTML names this build's hashed chunks, and a deploy deletes them.\n // Keyed by build, old pages stop matching the moment the new Worker serves.\n url.searchParams.set('_build', buildId)\n if (variant) url.searchParams.set('_exp', variant)\n return new Request(url.toString(), { method: 'GET' })\n}\n\n/** Only what the response itself declares shareable and fresh for a while. */\nexport function edgeCacheable(response: Response): boolean {\n if (response.status !== 200) return false\n const cc = response.headers.get('cache-control') ?? ''\n return /\\bpublic\\b/.test(cc) && /\\bs-maxage=[1-9]\\d*/.test(cc) && !/\\b(no-store|private)\\b/.test(cc)\n}\n\nfunction withHeader(response: Response, name: string, value: string): Response {\n const headers = new Headers(response.headers)\n headers.set(name, value)\n return new Response(response.body, { status: response.status, statusText: response.statusText, headers })\n}\n\n/** Adds cookies without touching the ones the page already sets. */\nfunction withCookies(response: Response, cookies: readonly string[]): Response {\n if (!cookies.length) return response\n const headers = new Headers(response.headers)\n for (const cookie of cookies) headers.append('set-cookie', cookie)\n return new Response(response.body, { status: response.status, statusText: response.statusText, headers })\n}\n\nexport interface StorefrontEdgeOptions {\n /** The framework's own handler (TanStack Start's `handler.fetch`). */\n render(request: Request): Response | Promise<Response>\n /** From the store host: `host.isCanonicalHost`. */\n isCanonicalHost(url: URL): boolean\n /** One id per build, for the cache key. */\n buildId: string\n /** The Supabase project the photos live in (`PUBLIC_SUPABASE_URL`). */\n supabaseUrl?: string\n /** Keeps the Worker alive past the response (`waitUntil` from 'cloudflare:workers'). */\n waitUntil(promise: Promise<unknown>): void\n /** The Workers cache (`caches.default`). Omitted: no edge cache. */\n cache?: Cache\n /**\n * Development: NEVER cache. The Cloudflare vite plugin runs the Worker in\n * workerd locally too, and an entry cannot be purged, only outlived — a\n * price edited and reloaded shows the old one with nothing in the browser\n * able to help.\n */\n dev?: boolean\n /**\n * A/B tests: the store's declared experiments (`config.experiments`).\n * Nothing running: no assignment, no cookie, the cache key is the page's\n * alone — exactly the handler without tests.\n */\n experiments?: StorefrontEdgeExperiments\n /**\n * Start the client bundle after the first frame is painted (see\n * `hydrateAfterFirstPaint`). Off by default; a server-rendered store whose\n * HTML is complete without JavaScript turns it on.\n */\n hydrateAfterFirstPaint?: boolean\n}\n\nexport interface StorefrontEdgeExperiments {\n declared: Record<string, StorefrontExperiment> | undefined\n /**\n * The PostHog project token (`phc_…`), the same the storefront sends with —\n * used only to read PostHog's own cookie, so a returning visitor is bucketed\n * by the id PostHog already knows them by. Optional: without it, `fz_vid`.\n */\n posthogKey?: string\n}\n\n/** A page a visitor navigates to — not an asset, a photo, a feed or a POST. */\nfunction isPageRequest(request: Request, url: URL): boolean {\n if (request.method !== 'GET') return false\n if (isStorageRequest(url) || isPostHogProxyPath(url.pathname)) return false\n // Hashed bundles, fonts, images, the sitemap: anything with an extension.\n if (/\\.[a-z0-9]{1,8}$/i.test(url.pathname)) return false\n // TanStack's server functions and the router's data requests.\n if (url.pathname.startsWith('/_serverFn') || url.pathname.startsWith('/_server')) return false\n return true\n}\n\ninterface PageAssignment {\n assignments: ExperimentAssignments\n /** The cache-key part; empty for control. */\n variant: string\n /** Cookies for THIS visitor's response: a new id. */\n cookies: string[]\n}\n\n/**\n * Decide this request's variants — locally, with PostHog's bucketing, no\n * network. Never throws.\n */\nexport async function assignForRequest(request: Request, options: StorefrontEdgeExperiments): Promise<PageAssignment> {\n const running = runningExperiments(options.declared)\n const keys = Object.keys(running)\n const none: PageAssignment = { assignments: NO_ASSIGNMENTS, variant: '', cookies: [] }\n // Nothing running: no id, no cookie, the page's plain cache entry.\n if (!keys.length) return none\n // Crawlers and unfurlers see control and get no id: what is indexed and\n // shared must not depend on a coin flip, and a bot is not an exposure.\n if (isBotUserAgent(request.headers.get('user-agent'))) return none\n const cookieHeader = request.headers.get('cookie')\n // The bucket id is pinned in `fz_vid` the first time and read from there\n // ever after. A returning visitor without one is pinned to the anonymous id\n // PostHog already knows them by; an identified id (an e-mail) is never\n // stored — they get a fresh anonymous one.\n const pinned = readPinnedVisitorId(cookieHeader)\n const known = pinned ?? readVisitorId(cookieHeader, options.posthogKey ?? '')\n const distinctId = known && isPinnableVisitorId(known) ? known : crypto.randomUUID()\n const cookies = pinned ? [] : [visitorCookie(distinctId, new URL(request.url).protocol === 'https:')]\n try {\n const assignments = await assignExperiments({ experiments: running, distinctId })\n return { assignments, variant: experimentCacheSuffix(assignments, keys), cookies }\n } catch {\n // No SubtleCrypto (not a Worker, not a modern runtime): everyone control.\n return { ...none, cookies }\n }\n}\n\n/**\n * The request the renderer sees: the same, plus the variants — and ONLY the\n * variants. The visitor id stays out of the header on purpose: the rendered\n * HTML is cached and shared by every visitor in the same variant bucket, and\n * an id in it would hand one visitor's identity to the next. The browser\n * reads its own id from the cookie (`readVisitorId(document.cookie, …)`).\n */\nfunction withAssignment(request: Request, assignments: ExperimentAssignments): Request {\n const headers = new Headers(request.headers)\n headers.delete(EXPERIMENT_HEADER) // never trust one the visitor sent\n if (Object.keys(assignments.flags).length) {\n headers.set(EXPERIMENT_HEADER, encodeExperimentHeader({ flags: assignments.flags }))\n }\n return new Request(request, { headers })\n}\n\n/** The Worker's `fetch`, from the store's pieces. */\nexport function createStorefrontEdge(options: StorefrontEdgeOptions): (request: Request) => Promise<Response> {\n async function respond(request: Request): Promise<Response> {\n const url = new URL(request.url)\n if (isStorageRequest(url)) return serveStorageImage(request, url, options.supabaseUrl)\n const rendered = await options.render(request)\n const response = options.hydrateAfterFirstPaint ? hydrateAfterFirstPaint(rendered) : rendered\n if (options.isCanonicalHost(url)) return response\n return withHeader(response, 'x-robots-tag', 'noindex, nofollow')\n }\n\n return async (incoming) => {\n const url = new URL(incoming.url)\n // Analytics leaves through the store's own domain, never cached.\n if (isPostHogProxyPath(url.pathname)) return posthogProxy(incoming)\n\n // Variants first: they change the HTML, so they are part of the cache key.\n const page = options.experiments && isPageRequest(incoming, url)\n ? await assignForRequest(incoming, options.experiments)\n : null\n const request = page ? withAssignment(incoming, page.assignments) : incoming\n // Cookies are added to what THIS visitor receives, never to the cached\n // copy (a shared entry carrying one visitor's id would hand it to the\n // next), and appended, so a cookie the page itself sets survives.\n const finish = (response: Response) => withCookies(response, page?.cookies ?? [])\n\n if (options.dev || !options.cache) return finish(await respond(request))\n\n const key = edgeCacheKey(request, options.buildId, page?.variant ?? '')\n if (!key) return finish(await respond(request))\n const hit = await options.cache.match(key)\n if (hit) return finish(withHeader(hit, 'x-edge-cache', 'HIT'))\n\n const response = await respond(request)\n if (!edgeCacheable(response)) return finish(withHeader(response, 'x-edge-cache', 'BYPASS'))\n options.waitUntil(options.cache.put(key, response.clone()))\n return finish(withHeader(response, 'x-edge-cache', 'MISS'))\n }\n}\n\n/** Minimal shape of the Workers runtime's streaming HTML rewriter. */\ninterface HtmlRewriterLike {\n on(selector: string, handlers: { element(el: HtmlElementLike): void }): HtmlRewriterLike\n transform(response: Response): Response\n}\ninterface HtmlElementLike {\n getAttribute(name: string): string | null\n hasAttribute(name: string): boolean\n removeAttribute(name: string): void\n setInnerContent(content: string, options?: { html?: boolean }): void\n}\n\n/** The inline loader that replaces the entry tag: import once a frame is painted. */\nexport function deferredEntryScript(src: string): string {\n // rAF runs before the next paint; the timeout queued from it runs after.\n return `requestAnimationFrame(function(){setTimeout(function(){import(${JSON.stringify(src)})},0)})`\n}\n\n/**\n * Let the server-rendered page paint before the client bundle runs.\n *\n * TanStack Start emits its entry as `<script type=\"module\" async src>`, which\n * executes the moment it arrives. On a fast connection that is BEFORE the\n * browser's first paint, so a page that was complete as HTML sat blank while\n * ~1 MB of JavaScript was evaluated and hydrated — and Lighthouse, seeing the\n * script run before the paint, counted the whole bundle as render-blocking\n * (FCP 1.0 s one run, 3.3 s the next). The entry is rewritten into an inline\n * module that imports the same URL after the first frame; its\n * `<link rel=\"modulepreload\">` still downloads it in parallel, so nothing\n * arrives later — it only runs later, after the shopper sees the page.\n *\n * Only HTML responses, only an `async` module entry with a `src`; everything\n * else passes through. Without the Workers `HTMLRewriter` (tests, Node) it is a\n * no-op.\n */\nexport function hydrateAfterFirstPaint(response: Response): Response {\n const Rewriter = (globalThis as { HTMLRewriter?: new () => HtmlRewriterLike }).HTMLRewriter\n if (!Rewriter || !(response.headers.get('content-type') ?? '').includes('text/html')) return response\n return new Rewriter()\n .on('script[type=\"module\"][src]', {\n element(el) {\n const src = el.getAttribute('src')\n if (!src || !el.hasAttribute('async') || src.includes('<')) return\n el.removeAttribute('src')\n el.removeAttribute('async')\n el.setInnerContent(deferredEntryScript(src), { html: true })\n },\n })\n .transform(response)\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/analytics/edge.ts","../src/host/edge-handler.ts"],"names":["response"],"mappings":";;;;AAgBO,SAAS,kBAAA,CAAmB,QAAA,EAAkB,MAAA,GAAS,SAAA,EAAoB;AAChF,EAAA,OAAO,aAAa,MAAA,IAAU,QAAA,CAAS,UAAA,CAAW,CAAA,EAAG,MAAM,CAAA,CAAA,CAAG,CAAA;AAChE;AAEA,eAAsB,YAAA,CAAa,OAAA,EAAkB,OAAA,GAA+B,EAAC,EAAsB;AACzG,EAAA,MAAM,MAAA,GAAS,QAAQ,MAAA,IAAU,SAAA;AACjC,EAAA,MAAM,MAAA,GAAS,QAAQ,MAAA,IAAU,IAAA;AACjC,EAAA,MAAM,GAAA,GAAM,IAAI,GAAA,CAAI,OAAA,CAAQ,GAAG,CAAA;AAC/B,EAAA,MAAM,OAAO,GAAA,CAAI,QAAA,CAAS,KAAA,CAAM,MAAA,CAAO,MAAM,CAAA,IAAK,GAAA;AAElD,EAAA,MAAM,IAAA,GAAO,IAAA,CAAK,UAAA,CAAW,UAAU,CAAA,IAAK,IAAA,CAAK,UAAA,CAAW,SAAS,CAAA,GACjE,CAAA,EAAG,MAAM,CAAA,qBAAA,CAAA,GACT,GAAG,MAAM,CAAA,cAAA,CAAA;AACb,EAAA,MAAM,MAAA,GAAS,IAAI,GAAA,CAAI,CAAA,QAAA,EAAW,IAAI,GAAG,IAAI,CAAA,EAAG,GAAA,CAAI,MAAM,CAAA,CAAE,CAAA;AAE5D,EAAA,MAAM,OAAA,GAAU,IAAI,OAAA,CAAQ,OAAA,CAAQ,OAAO,CAAA;AAC3C,EAAA,OAAA,CAAQ,OAAO,QAAQ,CAAA;AACvB,EAAA,OAAA,CAAQ,GAAA,CAAI,QAAQ,IAAI,CAAA;AAExB,EAAA,MAAM,EAAA,GAAK,QAAQ,OAAA,CAAQ,GAAA,CAAI,kBAAkB,CAAA,IAAK,OAAA,CAAQ,OAAA,CAAQ,GAAA,CAAI,iBAAiB,CAAA;AAC3F,EAAA,IAAI,EAAA,EAAI,OAAA,CAAQ,GAAA,CAAI,iBAAA,EAAmB,EAAE,CAAA;AAEzC,EAAA,MAAM,OAAoB,EAAE,MAAA,EAAQ,QAAQ,MAAA,EAAQ,OAAA,EAAS,UAAU,QAAA,EAAS;AAChF,EAAA,IAAI,OAAA,CAAQ,MAAA,KAAW,KAAA,IAAS,OAAA,CAAQ,MAAA,KAAW,QAAQ,IAAA,CAAK,IAAA,GAAO,MAAM,OAAA,CAAQ,WAAA,EAAY;AACjG,EAAA,MAAM,QAAA,GAAW,MAAM,KAAA,CAAM,MAAA,EAAQ,IAAI,CAAA;AACzC,EAAA,MAAM,GAAA,GAAM,IAAI,QAAA,CAAS,QAAA,CAAS,MAAM,QAAQ,CAAA;AAChD,EAAA,GAAA,CAAI,OAAA,CAAQ,OAAO,YAAY,CAAA;AAC/B,EAAA,OAAO,GAAA;AACT;;;ACCA,IAAM,YAAA,GAAe,wEAAA;AAIrB,IAAM,aAAA,uBAAoB,GAAA,CAAI,CAAC,SAAS,QAAA,EAAU,SAAA,EAAW,QAAA,EAAU,QAAQ,CAAC,CAAA;AAGhF,IAAM,SAAA,GAAY,wDAAA;AAElB,IAAM,cAAA,GAAiB,4FAAA;AAEhB,SAAS,iBAAiB,GAAA,EAAmB;AAClD,EAAA,OAAO,YAAA,CAAa,IAAA,CAAK,GAAA,CAAI,QAAQ,CAAA;AACvC;AAQO,SAAS,eAAe,OAAA,EAAmC;AAChE,EAAA,OAAO,aAAA,CAAc,KAAK,OAAA,CAAQ,OAAA,CAAQ,IAAI,QAAQ,CAAA,IAAK,EAAE,CAAA,GAAI,MAAA,GAAS,MAAA;AAC5E;AAGA,eAAsB,iBAAA,CAAkB,OAAA,EAAkB,GAAA,EAAU,WAAA,EAAoD;AACtH,EAAA,MAAM,KAAA,GAAQ,YAAA,CAAa,IAAA,CAAK,GAAA,CAAI,QAAQ,CAAA;AAC5C,EAAA,IAAI,CAAC,OAAO,OAAO,IAAI,SAAS,WAAA,EAAa,EAAE,MAAA,EAAQ,GAAA,EAAK,CAAA;AAC5D,EAAA,MAAM,IAAA,GAAO,WAAA,EAAa,OAAA,CAAQ,KAAA,EAAO,EAAE,CAAA;AAC3C,EAAA,IAAI,CAAC,MAAM,OAAO,IAAI,SAAS,2BAAA,EAA6B,EAAE,MAAA,EAAQ,GAAA,EAAK,CAAA;AAE3E,EAAA,MAAM,GAAG,IAAA,EAAM,UAAU,CAAA,GAAI,KAAA;AAC7B,EAAA,MAAM,QAAA,GAAW,IAAI,GAAA,CAAI,CAAA,EAAG,IAAI,CAAA,YAAA,EAAe,IAAI,CAAA,QAAA,EAAW,UAAU,CAAA,CAAE,CAAA;AAC1E,EAAA,KAAA,MAAW,CAAC,GAAA,EAAK,KAAK,CAAA,IAAK,IAAI,YAAA,EAAc;AAC3C,IAAA,IAAI,aAAA,CAAc,IAAI,GAAG,CAAA,WAAY,YAAA,CAAa,GAAA,CAAI,KAAK,KAAK,CAAA;AAAA,EAClE;AAGA,EAAA,MAAM,IAAA,GAAO,cAAA,CAAe,OAAO,CAAA,KAAM,MAAA;AACzC,EAAA,MAAM,QAAA,GAAW,MAAM,KAAA,CAAM,QAAA,CAAS,UAAS,EAAG,EAAE,OAAA,EAAS,EAAE,MAAA,EAAQ,IAAA,GAAO,0BAAA,GAA6B,SAAA,IAAa,CAAA;AACxH,EAAA,IAAI,CAAC,SAAS,EAAA,EAAI;AAGhB,IAAA,OAAO,IAAI,QAAA,CAAS,QAAA,CAAS,MAAA,KAAW,GAAA,GAAM,cAAc,gBAAA,EAAkB;AAAA,MAC5E,MAAA,EAAQ,QAAA,CAAS,MAAA,KAAW,GAAA,GAAM,GAAA,GAAM,GAAA;AAAA,MACxC,OAAA,EAAS,EAAE,eAAA,EAAiB,oBAAA;AAAqB,KAClD,CAAA;AAAA,EACH;AACA,EAAA,MAAM,OAAA,GAAU,IAAI,OAAA,EAAQ;AAC5B,EAAA,OAAA,CAAQ,IAAI,cAAA,EAAgB,QAAA,CAAS,QAAQ,GAAA,CAAI,cAAc,KAAK,YAAY,CAAA;AAChF,EAAA,OAAA,CAAQ,GAAA,CAAI,iBAAiB,SAAS,CAAA;AACtC,EAAA,OAAA,CAAQ,GAAA,CAAI,kBAAkB,UAAU,CAAA;AACxC,EAAA,OAAA,CAAQ,GAAA,CAAI,QAAQ,QAAQ,CAAA;AAC5B,EAAA,OAAO,IAAI,SAAS,QAAA,CAAS,IAAA,EAAM,EAAE,MAAA,EAAQ,GAAA,EAAK,SAAS,CAAA;AAC7D;AAIO,SAAS,YAAA,CAAa,OAAA,EAAkB,OAAA,EAAiB,OAAA,GAAU,EAAA,EAAoB;AAC5F,EAAA,IAAI,OAAA,CAAQ,MAAA,KAAW,KAAA,EAAO,OAAO,IAAA;AACrC,EAAA,MAAM,GAAA,GAAM,IAAI,GAAA,CAAI,OAAA,CAAQ,GAAG,CAAA;AAC/B,EAAA,KAAA,MAAW,OAAO,CAAC,GAAG,IAAI,YAAA,CAAa,IAAA,EAAM,CAAA,EAAG;AAC9C,IAAA,IAAI,eAAe,IAAA,CAAK,GAAG,GAAG,GAAA,CAAI,YAAA,CAAa,OAAO,GAAG,CAAA;AAAA,EAC3D;AACA,EAAA,GAAA,CAAI,IAAA,GAAO,EAAA;AACX,EAAA,IAAI,gBAAA,CAAiB,GAAG,CAAA,EAAG,GAAA,CAAI,aAAa,GAAA,CAAI,MAAA,EAAQ,cAAA,CAAe,OAAO,CAAC,CAAA;AAG/E,EAAA,GAAA,CAAI,YAAA,CAAa,GAAA,CAAI,QAAA,EAAU,OAAO,CAAA;AACtC,EAAA,IAAI,OAAA,EAAS,GAAA,CAAI,YAAA,CAAa,GAAA,CAAI,QAAQ,OAAO,CAAA;AACjD,EAAA,OAAO,IAAI,QAAQ,GAAA,CAAI,QAAA,IAAY,EAAE,MAAA,EAAQ,OAAO,CAAA;AACtD;AAGO,SAAS,cAAc,QAAA,EAA6B;AACzD,EAAA,IAAI,QAAA,CAAS,MAAA,KAAW,GAAA,EAAK,OAAO,KAAA;AACpC,EAAA,MAAM,EAAA,GAAK,QAAA,CAAS,OAAA,CAAQ,GAAA,CAAI,eAAe,CAAA,IAAK,EAAA;AACpD,EAAA,OAAO,YAAA,CAAa,IAAA,CAAK,EAAE,CAAA,IAAK,qBAAA,CAAsB,IAAA,CAAK,EAAE,CAAA,IAAK,CAAC,wBAAA,CAAyB,IAAA,CAAK,EAAE,CAAA;AACrG;AAEA,SAAS,UAAA,CAAW,QAAA,EAAoB,IAAA,EAAc,KAAA,EAAyB;AAC7E,EAAA,MAAM,OAAA,GAAU,IAAI,OAAA,CAAQ,QAAA,CAAS,OAAO,CAAA;AAC5C,EAAA,OAAA,CAAQ,GAAA,CAAI,MAAM,KAAK,CAAA;AACvB,EAAA,OAAO,IAAI,QAAA,CAAS,QAAA,CAAS,IAAA,EAAM,EAAE,MAAA,EAAQ,QAAA,CAAS,MAAA,EAAQ,UAAA,EAAY,QAAA,CAAS,UAAA,EAAY,OAAA,EAAS,CAAA;AAC1G;AAGA,SAAS,WAAA,CAAY,UAAoB,OAAA,EAAsC;AAC7E,EAAA,IAAI,CAAC,OAAA,CAAQ,MAAA,EAAQ,OAAO,QAAA;AAC5B,EAAA,MAAM,OAAA,GAAU,IAAI,OAAA,CAAQ,QAAA,CAAS,OAAO,CAAA;AAC5C,EAAA,KAAA,MAAW,MAAA,IAAU,OAAA,EAAS,OAAA,CAAQ,MAAA,CAAO,cAAc,MAAM,CAAA;AACjE,EAAA,OAAO,IAAI,QAAA,CAAS,QAAA,CAAS,IAAA,EAAM,EAAE,MAAA,EAAQ,QAAA,CAAS,MAAA,EAAQ,UAAA,EAAY,QAAA,CAAS,UAAA,EAAY,OAAA,EAAS,CAAA;AAC1G;AA+CA,SAAS,aAAA,CAAc,SAAkB,GAAA,EAAmB;AAC1D,EAAA,IAAI,OAAA,CAAQ,MAAA,KAAW,KAAA,EAAO,OAAO,KAAA;AACrC,EAAA,IAAI,iBAAiB,GAAG,CAAA,IAAK,mBAAmB,GAAA,CAAI,QAAQ,GAAG,OAAO,KAAA;AAEtE,EAAA,IAAI,mBAAA,CAAoB,IAAA,CAAK,GAAA,CAAI,QAAQ,GAAG,OAAO,KAAA;AAEnD,EAAA,IAAI,GAAA,CAAI,QAAA,CAAS,UAAA,CAAW,YAAY,CAAA,IAAK,IAAI,QAAA,CAAS,UAAA,CAAW,UAAU,CAAA,EAAG,OAAO,KAAA;AACzF,EAAA,OAAO,IAAA;AACT;AAcA,eAAsB,gBAAA,CAAiB,SAAkB,OAAA,EAA6D;AACpH,EAAA,MAAM,OAAA,GAAU,kBAAA,CAAmB,OAAA,CAAQ,QAAQ,CAAA;AACnD,EAAA,MAAM,IAAA,GAAO,MAAA,CAAO,IAAA,CAAK,OAAO,CAAA;AAChC,EAAA,MAAM,IAAA,GAAuB,EAAE,WAAA,EAAa,cAAA,EAAgB,SAAS,EAAA,EAAI,OAAA,EAAS,EAAC,EAAE;AAErF,EAAA,IAAI,CAAC,IAAA,CAAK,MAAA,EAAQ,OAAO,IAAA;AAGzB,EAAA,IAAI,eAAe,OAAA,CAAQ,OAAA,CAAQ,IAAI,YAAY,CAAC,GAAG,OAAO,IAAA;AAC9D,EAAA,MAAM,YAAA,GAAe,OAAA,CAAQ,OAAA,CAAQ,GAAA,CAAI,QAAQ,CAAA;AAKjD,EAAA,MAAM,MAAA,GAAS,oBAAoB,YAAY,CAAA;AAC/C,EAAA,MAAM,QAAQ,MAAA,IAAU,aAAA,CAAc,YAAA,EAAc,OAAA,CAAQ,cAAc,EAAE,CAAA;AAC5E,EAAA,MAAM,aAAa,KAAA,IAAS,mBAAA,CAAoB,KAAK,CAAA,GAAI,KAAA,GAAQ,OAAO,UAAA,EAAW;AACnF,EAAA,MAAM,OAAA,GAAU,MAAA,GAAS,EAAC,GAAI,CAAC,aAAA,CAAc,UAAA,EAAY,IAAI,GAAA,CAAI,OAAA,CAAQ,GAAG,CAAA,CAAE,QAAA,KAAa,QAAQ,CAAC,CAAA;AACpG,EAAA,IAAI;AACF,IAAA,MAAM,cAAc,MAAM,iBAAA,CAAkB,EAAE,WAAA,EAAa,OAAA,EAAS,YAAY,CAAA;AAChF,IAAA,OAAO,EAAE,WAAA,EAAa,OAAA,EAAS,sBAAsB,WAAA,EAAa,IAAI,GAAG,OAAA,EAAQ;AAAA,EACnF,CAAA,CAAA,MAAQ;AAEN,IAAA,OAAO,EAAE,GAAG,IAAA,EAAM,OAAA,EAAQ;AAAA,EAC5B;AACF;AASA,SAAS,cAAA,CAAe,SAAkB,WAAA,EAA6C;AACrF,EAAA,MAAM,OAAA,GAAU,IAAI,OAAA,CAAQ,OAAA,CAAQ,OAAO,CAAA;AAC3C,EAAA,OAAA,CAAQ,OAAO,iBAAiB,CAAA;AAChC,EAAA,IAAI,MAAA,CAAO,IAAA,CAAK,WAAA,CAAY,KAAK,EAAE,MAAA,EAAQ;AACzC,IAAA,OAAA,CAAQ,GAAA,CAAI,mBAAmB,sBAAA,CAAuB,EAAE,OAAO,WAAA,CAAY,KAAA,EAAO,CAAC,CAAA;AAAA,EACrF;AACA,EAAA,OAAO,IAAI,OAAA,CAAQ,OAAA,EAAS,EAAE,SAAS,CAAA;AACzC;AAGO,SAAS,qBAAqB,OAAA,EAAyE;AAC5G,EAAA,eAAe,QAAQ,OAAA,EAAqC;AAC1D,IAAA,MAAM,GAAA,GAAM,IAAI,GAAA,CAAI,OAAA,CAAQ,GAAG,CAAA;AAC/B,IAAA,IAAI,gBAAA,CAAiB,GAAG,CAAA,EAAG,OAAO,kBAAkB,OAAA,EAAS,GAAA,EAAK,QAAQ,WAAW,CAAA;AACrF,IAAA,MAAM,QAAA,GAAW,MAAM,OAAA,CAAQ,MAAA,CAAO,OAAO,CAAA;AAC7C,IAAA,MAAM,QAAA,GAAW,OAAA,CAAQ,sBAAA,GAAyB,sBAAA,CAAuB,QAAQ,CAAA,GAAI,QAAA;AACrF,IAAA,IAAI,OAAA,CAAQ,eAAA,CAAgB,GAAG,CAAA,EAAG,OAAO,QAAA;AACzC,IAAA,OAAO,UAAA,CAAW,QAAA,EAAU,cAAA,EAAgB,mBAAmB,CAAA;AAAA,EACjE;AAEA,EAAA,OAAO,OAAO,QAAA,KAAa;AACzB,IAAA,MAAM,GAAA,GAAM,IAAI,GAAA,CAAI,QAAA,CAAS,GAAG,CAAA;AAEhC,IAAA,IAAI,mBAAmB,GAAA,CAAI,QAAQ,CAAA,EAAG,OAAO,aAAa,QAAQ,CAAA;AAGlE,IAAA,MAAM,IAAA,GAAO,OAAA,CAAQ,WAAA,IAAe,aAAA,CAAc,QAAA,EAAU,GAAG,CAAA,GAC3D,MAAM,gBAAA,CAAiB,QAAA,EAAU,OAAA,CAAQ,WAAW,CAAA,GACpD,IAAA;AACJ,IAAA,MAAM,UAAU,IAAA,GAAO,cAAA,CAAe,QAAA,EAAU,IAAA,CAAK,WAAW,CAAA,GAAI,QAAA;AAIpE,IAAA,MAAM,MAAA,GAAS,CAACA,SAAAA,KAAuB,WAAA,CAAYA,WAAU,IAAA,EAAM,OAAA,IAAW,EAAE,CAAA;AAEhF,IAAA,IAAI,OAAA,CAAQ,GAAA,IAAO,CAAC,OAAA,CAAQ,KAAA,SAAc,MAAA,CAAO,MAAM,OAAA,CAAQ,OAAO,CAAC,CAAA;AAEvE,IAAA,MAAM,MAAM,YAAA,CAAa,OAAA,EAAS,QAAQ,OAAA,EAAS,IAAA,EAAM,WAAW,EAAE,CAAA;AACtE,IAAA,IAAI,CAAC,GAAA,EAAK,OAAO,OAAO,MAAM,OAAA,CAAQ,OAAO,CAAC,CAAA;AAC9C,IAAA,MAAM,GAAA,GAAM,MAAM,OAAA,CAAQ,KAAA,CAAM,MAAM,GAAG,CAAA;AACzC,IAAA,IAAI,KAAK,OAAO,MAAA,CAAO,WAAW,GAAA,EAAK,cAAA,EAAgB,KAAK,CAAC,CAAA;AAE7D,IAAA,MAAM,QAAA,GAAW,MAAM,OAAA,CAAQ,OAAO,CAAA;AACtC,IAAA,IAAI,CAAC,aAAA,CAAc,QAAQ,CAAA,EAAG,OAAO,OAAO,UAAA,CAAW,QAAA,EAAU,cAAA,EAAgB,QAAQ,CAAC,CAAA;AAC1F,IAAA,OAAA,CAAQ,SAAA,CAAU,QAAQ,KAAA,CAAM,GAAA,CAAI,KAAK,QAAA,CAAS,KAAA,EAAO,CAAC,CAAA;AAC1D,IAAA,OAAO,MAAA,CAAO,UAAA,CAAW,QAAA,EAAU,cAAA,EAAgB,MAAM,CAAC,CAAA;AAAA,EAC5D,CAAA;AACF;AAgBO,SAAS,oBAAoB,GAAA,EAAqB;AAEvD,EAAA,OAAO,CAAA,8DAAA,EAAiE,IAAA,CAAK,SAAA,CAAU,GAAG,CAAC,CAAA,OAAA,CAAA;AAC7F;AAmBO,SAAS,uBAAuB,QAAA,EAA8B;AACnE,EAAA,MAAM,WAAY,UAAA,CAA6D,YAAA;AAC/E,EAAA,IAAI,CAAC,QAAA,IAAY,CAAA,CAAE,QAAA,CAAS,OAAA,CAAQ,GAAA,CAAI,cAAc,CAAA,IAAK,EAAA,EAAI,QAAA,CAAS,WAAW,CAAA,EAAG,OAAO,QAAA;AAC7F,EAAA,OAAO,IAAI,QAAA,EAAS,CAQjB,EAAA,CAAG,2BAAA,EAA6B;AAAA,IAC/B,QAAQ,EAAA,EAAI;AACV,MAAA,EAAA,CAAG,MAAA,EAAO;AAAA,IACZ;AAAA,GACD,CAAA,CACA,EAAA,CAAG,4BAAA,EAA8B;AAAA,IAChC,QAAQ,EAAA,EAAI;AACV,MAAA,MAAM,GAAA,GAAM,EAAA,CAAG,YAAA,CAAa,KAAK,CAAA;AACjC,MAAA,IAAI,CAAC,GAAA,IAAO,CAAC,EAAA,CAAG,YAAA,CAAa,OAAO,CAAA,IAAK,GAAA,CAAI,QAAA,CAAS,GAAG,CAAA,EAAG;AAC5D,MAAA,EAAA,CAAG,gBAAgB,KAAK,CAAA;AACxB,MAAA,EAAA,CAAG,gBAAgB,OAAO,CAAA;AAC1B,MAAA,EAAA,CAAG,gBAAgB,mBAAA,CAAoB,GAAG,GAAG,EAAE,IAAA,EAAM,MAAM,CAAA;AAAA,IAC7D;AAAA,GACD,CAAA,CACA,SAAA,CAAU,QAAQ,CAAA;AACvB","file":"edge.js","sourcesContent":["/**\n * The store's own door to PostHog, for a Worker (or any fetch-style) host.\n *\n * Events sent to the store's domain under `/ingest` are not dropped by ad\n * blockers the way requests to a known analytics host are, and the store needs\n * no PostHog setting of its own: every store forwards to the same project.\n *\n * if (url.pathname.startsWith('/ingest')) return posthogProxy(request)\n */\nexport interface PostHogProxyOptions {\n /** The path the storefront's analytics client sends to. Default `/ingest`. */\n prefix?: string\n /** PostHog's region. Default `us`. */\n region?: 'us' | 'eu'\n}\n\nexport function isPostHogProxyPath(pathname: string, prefix = '/ingest'): boolean {\n return pathname === prefix || pathname.startsWith(`${prefix}/`)\n}\n\nexport async function posthogProxy(request: Request, options: PostHogProxyOptions = {}): Promise<Response> {\n const prefix = options.prefix ?? '/ingest'\n const region = options.region ?? 'us'\n const url = new URL(request.url)\n const path = url.pathname.slice(prefix.length) || '/'\n // The SDK's script and its remote config live on the assets host.\n const host = path.startsWith('/static/') || path.startsWith('/array/')\n ? `${region}-assets.i.posthog.com`\n : `${region}.i.posthog.com`\n const target = new URL(`https://${host}${path}${url.search}`)\n\n const headers = new Headers(request.headers)\n headers.delete('cookie')\n headers.set('host', host)\n // PostHog geolocates from the visitor, not from the Worker.\n const ip = request.headers.get('cf-connecting-ip') ?? request.headers.get('x-forwarded-for')\n if (ip) headers.set('x-forwarded-for', ip)\n\n const init: RequestInit = { method: request.method, headers, redirect: 'manual' }\n if (request.method !== 'GET' && request.method !== 'HEAD') init.body = await request.arrayBuffer()\n const response = await fetch(target, init)\n const out = new Response(response.body, response)\n out.headers.delete('set-cookie')\n return out\n}\n","import { isPostHogProxyPath, posthogProxy } from '../analytics/edge'\nimport {\n EXPERIMENT_HEADER,\n NO_ASSIGNMENTS,\n assignExperiments,\n encodeExperimentHeader,\n experimentCacheSuffix,\n runningExperiments,\n isBotUserAgent,\n readVisitorId,\n readPinnedVisitorId,\n isPinnableVisitorId,\n visitorCookie,\n type ExperimentAssignments,\n type StorefrontExperiment,\n} from '../experiments/assign'\n\n// ---------------------------------------------------------------------------\n// The Worker in front of a server-rendered store.\n//\n// Five jobs a store's request handler has before the framework renders\n// anything, first written for artorious-shop and the same for every store:\n//\n// 1. Analytics through the store's own domain (`/ingest` → PostHog), so ad\n// blockers that drop requests to a known analytics host do not drop it.\n// 2. The store's photos from the store's own origin. The bytes live in\n// Supabase Storage; a photo addressed at the storage project is a second\n// origin the browser pays DNS + TLS for (measured 156–241 ms TTFB against\n// 112–141 ms same-origin). Only public objects of the `shop-images`\n// bucket are forwarded — this is not a general proxy.\n// 3. A real edge cache. Public pages already leave with `Cache-Control:\n// public, s-maxage=…`, but on workers.dev nothing in front honours it, so\n// every view paid the full render. The Workers Cache API makes the header\n// mean what it says. Marketing parameters (utm_*, fbclid…) are dropped\n// from the key: ad traffic hits the same entry as everyone else.\n// 4. `X-Robots-Tag: noindex` on any host that is not the store's address, so\n// the deployment URL never competes with the domain.\n// 5. A/B assignment. With a running experiment, every page request is\n// assigned its variants HERE, before render, locally with PostHog's\n// bucketing (experiments/assign.ts — no network), and the edge cache is\n// keyed by them: one entry per combination of variants, so a cached page\n// is never one visitor's variant served to another.\n// ---------------------------------------------------------------------------\n\n/** `/storage/v1/object/public/shop-images/…` or the `render/image` variant of it. */\nconst STORAGE_PATH = /^\\/storage\\/v1\\/(object|render\\/image)\\/public\\/(shop-images\\/[^?#]+)$/\n\n/** Query parameters the renderer understands. Anything else is dropped rather\n * than forwarded, so a crawler cannot mint unlimited distinct variants. */\nconst RENDER_PARAMS = new Set(['width', 'height', 'quality', 'format', 'resize'])\n\n/** A year: the object path carries a timestamp and is never overwritten. */\nconst IMMUTABLE = 'public, max-age=31536000, s-maxage=31536000, immutable'\n\nconst TRACKING_PARAM = /^(utm_[a-z0-9_]+|fbclid|gclid|gbraid|wbraid|msclkid|ttclid|igshid|mc_cid|mc_eid|_ga|ref)$/i\n\nexport function isStorageRequest(url: URL): boolean {\n return STORAGE_PATH.test(url.pathname)\n}\n\n/**\n * Which format this visitor gets — the only part of the request, besides the\n * URL, that changes the bytes. The renderer answers WebP or the original\n * depending on `accept` and declares no `Vary`, so every cache in front of it\n * has to put the answer in the key itself.\n */\nexport function imageFormatKey(request: Request): 'webp' | 'orig' {\n return /image\\/webp/.test(request.headers.get('accept') ?? '') ? 'webp' : 'orig'\n}\n\n/** Answer a storage request from Supabase, on the store's own origin. */\nexport async function serveStorageImage(request: Request, url: URL, supabaseUrl: string | undefined): Promise<Response> {\n const match = STORAGE_PATH.exec(url.pathname)\n if (!match) return new Response('Not found', { status: 404 })\n const base = supabaseUrl?.replace(/\\/$/, '')\n if (!base) return new Response('Storage is not configured', { status: 502 })\n\n const [, kind, objectPath] = match\n const upstream = new URL(`${base}/storage/v1/${kind}/public/${objectPath}`)\n for (const [key, value] of url.searchParams) {\n if (RENDER_PARAMS.has(key)) upstream.searchParams.set(key, value)\n }\n // A canonical `accept`, not the visitor's verbatim: two browsers that both\n // take WebP must produce the same upstream request.\n const webp = imageFormatKey(request) === 'webp'\n const response = await fetch(upstream.toString(), { headers: { accept: webp ? 'image/webp,image/*;q=0.8' : 'image/*' } })\n if (!response.ok) {\n // Upstream's status is passed through (a deleted photo must not look like\n // a 200), never its cache directives.\n return new Response(response.status === 404 ? 'Not found' : 'Upstream error', {\n status: response.status === 404 ? 404 : 502,\n headers: { 'cache-control': 'public, max-age=60' },\n })\n }\n const headers = new Headers()\n headers.set('content-type', response.headers.get('content-type') ?? 'image/jpeg')\n headers.set('cache-control', IMMUTABLE)\n headers.set('x-image-origin', 'supabase')\n headers.set('vary', 'Accept')\n return new Response(response.body, { status: 200, headers })\n}\n\n/** The cache key for a request, or null when it is not cacheable. `variant`\n * is the experiment part (`experimentCacheSuffix`); empty means control. */\nexport function edgeCacheKey(request: Request, buildId: string, variant = ''): Request | null {\n if (request.method !== 'GET') return null\n const url = new URL(request.url)\n for (const key of [...url.searchParams.keys()]) {\n if (TRACKING_PARAM.test(key)) url.searchParams.delete(key)\n }\n url.hash = ''\n if (isStorageRequest(url)) url.searchParams.set('_fmt', imageFormatKey(request))\n // Cached HTML names this build's hashed chunks, and a deploy deletes them.\n // Keyed by build, old pages stop matching the moment the new Worker serves.\n url.searchParams.set('_build', buildId)\n if (variant) url.searchParams.set('_exp', variant)\n return new Request(url.toString(), { method: 'GET' })\n}\n\n/** Only what the response itself declares shareable and fresh for a while. */\nexport function edgeCacheable(response: Response): boolean {\n if (response.status !== 200) return false\n const cc = response.headers.get('cache-control') ?? ''\n return /\\bpublic\\b/.test(cc) && /\\bs-maxage=[1-9]\\d*/.test(cc) && !/\\b(no-store|private)\\b/.test(cc)\n}\n\nfunction withHeader(response: Response, name: string, value: string): Response {\n const headers = new Headers(response.headers)\n headers.set(name, value)\n return new Response(response.body, { status: response.status, statusText: response.statusText, headers })\n}\n\n/** Adds cookies without touching the ones the page already sets. */\nfunction withCookies(response: Response, cookies: readonly string[]): Response {\n if (!cookies.length) return response\n const headers = new Headers(response.headers)\n for (const cookie of cookies) headers.append('set-cookie', cookie)\n return new Response(response.body, { status: response.status, statusText: response.statusText, headers })\n}\n\nexport interface StorefrontEdgeOptions {\n /** The framework's own handler (TanStack Start's `handler.fetch`). */\n render(request: Request): Response | Promise<Response>\n /** From the store host: `host.isCanonicalHost`. */\n isCanonicalHost(url: URL): boolean\n /** One id per build, for the cache key. */\n buildId: string\n /** The Supabase project the photos live in (`PUBLIC_SUPABASE_URL`). */\n supabaseUrl?: string\n /** Keeps the Worker alive past the response (`waitUntil` from 'cloudflare:workers'). */\n waitUntil(promise: Promise<unknown>): void\n /** The Workers cache (`caches.default`). Omitted: no edge cache. */\n cache?: Cache\n /**\n * Development: NEVER cache. The Cloudflare vite plugin runs the Worker in\n * workerd locally too, and an entry cannot be purged, only outlived — a\n * price edited and reloaded shows the old one with nothing in the browser\n * able to help.\n */\n dev?: boolean\n /**\n * A/B tests: the store's declared experiments (`config.experiments`).\n * Nothing running: no assignment, no cookie, the cache key is the page's\n * alone — exactly the handler without tests.\n */\n experiments?: StorefrontEdgeExperiments\n /**\n * Start the client bundle after the first frame is painted (see\n * `hydrateAfterFirstPaint`). Off by default; a server-rendered store whose\n * HTML is complete without JavaScript turns it on.\n */\n hydrateAfterFirstPaint?: boolean\n}\n\nexport interface StorefrontEdgeExperiments {\n declared: Record<string, StorefrontExperiment> | undefined\n /**\n * The PostHog project token (`phc_…`), the same the storefront sends with —\n * used only to read PostHog's own cookie, so a returning visitor is bucketed\n * by the id PostHog already knows them by. Optional: without it, `fz_vid`.\n */\n posthogKey?: string\n}\n\n/** A page a visitor navigates to — not an asset, a photo, a feed or a POST. */\nfunction isPageRequest(request: Request, url: URL): boolean {\n if (request.method !== 'GET') return false\n if (isStorageRequest(url) || isPostHogProxyPath(url.pathname)) return false\n // Hashed bundles, fonts, images, the sitemap: anything with an extension.\n if (/\\.[a-z0-9]{1,8}$/i.test(url.pathname)) return false\n // TanStack's server functions and the router's data requests.\n if (url.pathname.startsWith('/_serverFn') || url.pathname.startsWith('/_server')) return false\n return true\n}\n\ninterface PageAssignment {\n assignments: ExperimentAssignments\n /** The cache-key part; empty for control. */\n variant: string\n /** Cookies for THIS visitor's response: a new id. */\n cookies: string[]\n}\n\n/**\n * Decide this request's variants — locally, with PostHog's bucketing, no\n * network. Never throws.\n */\nexport async function assignForRequest(request: Request, options: StorefrontEdgeExperiments): Promise<PageAssignment> {\n const running = runningExperiments(options.declared)\n const keys = Object.keys(running)\n const none: PageAssignment = { assignments: NO_ASSIGNMENTS, variant: '', cookies: [] }\n // Nothing running: no id, no cookie, the page's plain cache entry.\n if (!keys.length) return none\n // Crawlers and unfurlers see control and get no id: what is indexed and\n // shared must not depend on a coin flip, and a bot is not an exposure.\n if (isBotUserAgent(request.headers.get('user-agent'))) return none\n const cookieHeader = request.headers.get('cookie')\n // The bucket id is pinned in `fz_vid` the first time and read from there\n // ever after. A returning visitor without one is pinned to the anonymous id\n // PostHog already knows them by; an identified id (an e-mail) is never\n // stored — they get a fresh anonymous one.\n const pinned = readPinnedVisitorId(cookieHeader)\n const known = pinned ?? readVisitorId(cookieHeader, options.posthogKey ?? '')\n const distinctId = known && isPinnableVisitorId(known) ? known : crypto.randomUUID()\n const cookies = pinned ? [] : [visitorCookie(distinctId, new URL(request.url).protocol === 'https:')]\n try {\n const assignments = await assignExperiments({ experiments: running, distinctId })\n return { assignments, variant: experimentCacheSuffix(assignments, keys), cookies }\n } catch {\n // No SubtleCrypto (not a Worker, not a modern runtime): everyone control.\n return { ...none, cookies }\n }\n}\n\n/**\n * The request the renderer sees: the same, plus the variants — and ONLY the\n * variants. The visitor id stays out of the header on purpose: the rendered\n * HTML is cached and shared by every visitor in the same variant bucket, and\n * an id in it would hand one visitor's identity to the next. The browser\n * reads its own id from the cookie (`readVisitorId(document.cookie, …)`).\n */\nfunction withAssignment(request: Request, assignments: ExperimentAssignments): Request {\n const headers = new Headers(request.headers)\n headers.delete(EXPERIMENT_HEADER) // never trust one the visitor sent\n if (Object.keys(assignments.flags).length) {\n headers.set(EXPERIMENT_HEADER, encodeExperimentHeader({ flags: assignments.flags }))\n }\n return new Request(request, { headers })\n}\n\n/** The Worker's `fetch`, from the store's pieces. */\nexport function createStorefrontEdge(options: StorefrontEdgeOptions): (request: Request) => Promise<Response> {\n async function respond(request: Request): Promise<Response> {\n const url = new URL(request.url)\n if (isStorageRequest(url)) return serveStorageImage(request, url, options.supabaseUrl)\n const rendered = await options.render(request)\n const response = options.hydrateAfterFirstPaint ? hydrateAfterFirstPaint(rendered) : rendered\n if (options.isCanonicalHost(url)) return response\n return withHeader(response, 'x-robots-tag', 'noindex, nofollow')\n }\n\n return async (incoming) => {\n const url = new URL(incoming.url)\n // Analytics leaves through the store's own domain, never cached.\n if (isPostHogProxyPath(url.pathname)) return posthogProxy(incoming)\n\n // Variants first: they change the HTML, so they are part of the cache key.\n const page = options.experiments && isPageRequest(incoming, url)\n ? await assignForRequest(incoming, options.experiments)\n : null\n const request = page ? withAssignment(incoming, page.assignments) : incoming\n // Cookies are added to what THIS visitor receives, never to the cached\n // copy (a shared entry carrying one visitor's id would hand it to the\n // next), and appended, so a cookie the page itself sets survives.\n const finish = (response: Response) => withCookies(response, page?.cookies ?? [])\n\n if (options.dev || !options.cache) return finish(await respond(request))\n\n const key = edgeCacheKey(request, options.buildId, page?.variant ?? '')\n if (!key) return finish(await respond(request))\n const hit = await options.cache.match(key)\n if (hit) return finish(withHeader(hit, 'x-edge-cache', 'HIT'))\n\n const response = await respond(request)\n if (!edgeCacheable(response)) return finish(withHeader(response, 'x-edge-cache', 'BYPASS'))\n options.waitUntil(options.cache.put(key, response.clone()))\n return finish(withHeader(response, 'x-edge-cache', 'MISS'))\n }\n}\n\n/** Minimal shape of the Workers runtime's streaming HTML rewriter. */\ninterface HtmlRewriterLike {\n on(selector: string, handlers: { element(el: HtmlElementLike): void }): HtmlRewriterLike\n transform(response: Response): Response\n}\ninterface HtmlElementLike {\n getAttribute(name: string): string | null\n remove(): void\n hasAttribute(name: string): boolean\n removeAttribute(name: string): void\n setInnerContent(content: string, options?: { html?: boolean }): void\n}\n\n/** The inline loader that replaces the entry tag: import once a frame is painted. */\nexport function deferredEntryScript(src: string): string {\n // rAF runs before the next paint; the timeout queued from it runs after.\n return `requestAnimationFrame(function(){setTimeout(function(){import(${JSON.stringify(src)})},0)})`\n}\n\n/**\n * Let the server-rendered page paint before the client bundle runs.\n *\n * TanStack Start emits its entry as `<script type=\"module\" async src>`, which\n * executes the moment it arrives. On a fast connection that is BEFORE the\n * browser's first paint, so a page that was complete as HTML sat blank while\n * ~1 MB of JavaScript was evaluated and hydrated — and Lighthouse, seeing the\n * script run before the paint, counted the whole bundle as render-blocking\n * (FCP 1.0 s one run, 3.3 s the next). The entry is rewritten into an inline\n * module that imports the same URL after the first frame; its\n * `<link rel=\"modulepreload\">` still downloads it in parallel, so nothing\n * arrives later — it only runs later, after the shopper sees the page.\n *\n * Only HTML responses, only an `async` module entry with a `src`; everything\n * else passes through. Without the Workers `HTMLRewriter` (tests, Node) it is a\n * no-op.\n */\nexport function hydrateAfterFirstPaint(response: Response): Response {\n const Rewriter = (globalThis as { HTMLRewriter?: new () => HtmlRewriterLike }).HTMLRewriter\n if (!Rewriter || !(response.headers.get('content-type') ?? '').includes('text/html')) return response\n return new Rewriter()\n // The entry's modulepreloads go too. A modulepreload makes the browser\n // compile the module the moment it lands — before the first paint on a\n // fast connection, the very work this rewrite moves after it — and on a\n // slow one it competes with the page's LCP image for the same pipe. The\n // after-paint import() fetches the bundle (and its chunks) itself, once.\n // (A plain rel=preload is NOT an alternative: Chrome does not reuse it for\n // a module import, and the bundle downloads twice — measured, 0.85.1.)\n .on('link[rel=\"modulepreload\"]', {\n element(el) {\n el.remove()\n },\n })\n .on('script[type=\"module\"][src]', {\n element(el) {\n const src = el.getAttribute('src')\n if (!src || !el.hasAttribute('async') || src.includes('<')) return\n el.removeAttribute('src')\n el.removeAttribute('async')\n el.setInnerContent(deferredEntryScript(src), { html: true })\n },\n })\n .transform(response)\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"edge-handler.d.ts","sourceRoot":"","sources":["../../src/host/edge-handler.ts"],"names":[],"mappings":"AACA,OAAO,EAYL,KAAK,qBAAqB,EAC1B,KAAK,oBAAoB,EAC1B,MAAM,uBAAuB,CAAA;AAyC9B,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,GAAG,GAAG,OAAO,CAElD;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,GAAG,MAAM,CAEhE;AAED,yEAAyE;AACzE,wBAAsB,iBAAiB,CAAC,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE,GAAG,EAAE,WAAW,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC,QAAQ,CAAC,CA6BtH;AAED;6EAC6E;AAC7E,wBAAgB,YAAY,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,SAAK,GAAG,OAAO,GAAG,IAAI,CAa5F;AAED,8EAA8E;AAC9E,wBAAgB,aAAa,CAAC,QAAQ,EAAE,QAAQ,GAAG,OAAO,CAIzD;AAgBD,MAAM,WAAW,qBAAqB;IACpC,sEAAsE;IACtE,MAAM,CAAC,OAAO,EAAE,OAAO,GAAG,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAA;IACtD,mDAAmD;IACnD,eAAe,CAAC,GAAG,EAAE,GAAG,GAAG,OAAO,CAAA;IAClC,2CAA2C;IAC3C,OAAO,EAAE,MAAM,CAAA;IACf,uEAAuE;IACvE,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,wFAAwF;IACxF,SAAS,CAAC,OAAO,EAAE,OAAO,CAAC,OAAO,CAAC,GAAG,IAAI,CAAA;IAC1C,oEAAoE;IACpE,KAAK,CAAC,EAAE,KAAK,CAAA;IACb;;;;;OAKG;IACH,GAAG,CAAC,EAAE,OAAO,CAAA;IACb;;;;OAIG;IACH,WAAW,CAAC,EAAE,yBAAyB,CAAA;IACvC;;;;OAIG;IACH,sBAAsB,CAAC,EAAE,OAAO,CAAA;CACjC;AAED,MAAM,WAAW,yBAAyB;IACxC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,oBAAoB,CAAC,GAAG,SAAS,CAAA;IAC1D;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB;AAaD,UAAU,cAAc;IACtB,WAAW,EAAE,qBAAqB,CAAA;IAClC,6CAA6C;IAC7C,OAAO,EAAE,MAAM,CAAA;IACf,qDAAqD;IACrD,OAAO,EAAE,MAAM,EAAE,CAAA;CAClB;AAED;;;GAGG;AACH,wBAAsB,gBAAgB,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,yBAAyB,GAAG,OAAO,CAAC,cAAc,CAAC,CAyBpH;AAkBD,qDAAqD;AACrD,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,qBAAqB,GAAG,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,CAAC,CAqC5G;
|
|
1
|
+
{"version":3,"file":"edge-handler.d.ts","sourceRoot":"","sources":["../../src/host/edge-handler.ts"],"names":[],"mappings":"AACA,OAAO,EAYL,KAAK,qBAAqB,EAC1B,KAAK,oBAAoB,EAC1B,MAAM,uBAAuB,CAAA;AAyC9B,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,GAAG,GAAG,OAAO,CAElD;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,GAAG,MAAM,CAEhE;AAED,yEAAyE;AACzE,wBAAsB,iBAAiB,CAAC,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE,GAAG,EAAE,WAAW,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC,QAAQ,CAAC,CA6BtH;AAED;6EAC6E;AAC7E,wBAAgB,YAAY,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,SAAK,GAAG,OAAO,GAAG,IAAI,CAa5F;AAED,8EAA8E;AAC9E,wBAAgB,aAAa,CAAC,QAAQ,EAAE,QAAQ,GAAG,OAAO,CAIzD;AAgBD,MAAM,WAAW,qBAAqB;IACpC,sEAAsE;IACtE,MAAM,CAAC,OAAO,EAAE,OAAO,GAAG,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAA;IACtD,mDAAmD;IACnD,eAAe,CAAC,GAAG,EAAE,GAAG,GAAG,OAAO,CAAA;IAClC,2CAA2C;IAC3C,OAAO,EAAE,MAAM,CAAA;IACf,uEAAuE;IACvE,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,wFAAwF;IACxF,SAAS,CAAC,OAAO,EAAE,OAAO,CAAC,OAAO,CAAC,GAAG,IAAI,CAAA;IAC1C,oEAAoE;IACpE,KAAK,CAAC,EAAE,KAAK,CAAA;IACb;;;;;OAKG;IACH,GAAG,CAAC,EAAE,OAAO,CAAA;IACb;;;;OAIG;IACH,WAAW,CAAC,EAAE,yBAAyB,CAAA;IACvC;;;;OAIG;IACH,sBAAsB,CAAC,EAAE,OAAO,CAAA;CACjC;AAED,MAAM,WAAW,yBAAyB;IACxC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,oBAAoB,CAAC,GAAG,SAAS,CAAA;IAC1D;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB;AAaD,UAAU,cAAc;IACtB,WAAW,EAAE,qBAAqB,CAAA;IAClC,6CAA6C;IAC7C,OAAO,EAAE,MAAM,CAAA;IACf,qDAAqD;IACrD,OAAO,EAAE,MAAM,EAAE,CAAA;CAClB;AAED;;;GAGG;AACH,wBAAsB,gBAAgB,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,yBAAyB,GAAG,OAAO,CAAC,cAAc,CAAC,CAyBpH;AAkBD,qDAAqD;AACrD,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,qBAAqB,GAAG,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,CAAC,CAqC5G;AAeD,qFAAqF;AACrF,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAGvD;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,QAAQ,GAAG,QAAQ,CA0BnE"}
|
|
@@ -60,6 +60,13 @@ export interface StoreHostOptions<Extra = unknown> {
|
|
|
60
60
|
redirects?: Record<string, string>;
|
|
61
61
|
/** The store's own routes. */
|
|
62
62
|
extend?: StoreHostExtension<Extra>;
|
|
63
|
+
/**
|
|
64
|
+
* Send Meta's PageView from an inline <head> script, at first paint, for
|
|
65
|
+
* every visit (see analytics/meta-pageview.ts). Default on whenever the
|
|
66
|
+
* store has a Meta Pixel — `analytics.metaPixel` in code, else the id the
|
|
67
|
+
* merchant saved (shop 0148), read on the server. `false` turns it off.
|
|
68
|
+
*/
|
|
69
|
+
metaPageView?: boolean;
|
|
63
70
|
}
|
|
64
71
|
/** What a route loader returns, serialised to the browser with the page. */
|
|
65
72
|
export interface StoreRouteData<Extra = unknown> {
|
|
@@ -83,6 +90,8 @@ export interface StoreRouteData<Extra = unknown> {
|
|
|
83
90
|
* edge caches per variant bucket. Absent: every experiment is control.
|
|
84
91
|
*/
|
|
85
92
|
experiments?: ExperimentAssignments;
|
|
93
|
+
/** The Meta Pixel the head's PageView boot script reports to. Absent: no script. */
|
|
94
|
+
metaPixelId?: string;
|
|
86
95
|
}
|
|
87
96
|
export interface StoreHeadContext {
|
|
88
97
|
config: ResolvedStorefrontConfig;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"store-host.d.ts","sourceRoot":"","sources":["../../src/host/store-host.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgC,KAAK,SAAS,EAAE,KAAK,UAAU,EAAE,MAAM,eAAe,CAAA;AAE7F,OAAO,KAAK,EAAY,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAA;AAC1E,OAAO,EAAiB,KAAK,wBAAwB,EAAE,KAAK,gBAAgB,EAAE,MAAM,oBAAoB,CAAA;AAGxG,OAAO,EAAyC,KAAK,aAAa,EAAE,MAAM,qBAAqB,CAAA;AAG/F,OAAO,EAAgC,KAAK,YAAY,EAAE,MAAM,YAAY,CAAA;AAE5E,OAAO,EAAuB,KAAK,qBAAqB,EAAE,MAAM,uBAAuB,CAAA;
|
|
1
|
+
{"version":3,"file":"store-host.d.ts","sourceRoot":"","sources":["../../src/host/store-host.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgC,KAAK,SAAS,EAAE,KAAK,UAAU,EAAE,MAAM,eAAe,CAAA;AAE7F,OAAO,KAAK,EAAY,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAA;AAC1E,OAAO,EAAiB,KAAK,wBAAwB,EAAE,KAAK,gBAAgB,EAAE,MAAM,oBAAoB,CAAA;AAGxG,OAAO,EAAyC,KAAK,aAAa,EAAE,MAAM,qBAAqB,CAAA;AAG/F,OAAO,EAAgC,KAAK,YAAY,EAAE,MAAM,YAAY,CAAA;AAE5E,OAAO,EAAuB,KAAK,qBAAqB,EAAE,MAAM,uBAAuB,CAAA;AAkBvF,oEAAoE;AACpE,MAAM,WAAW,YAAY;IAC3B,GAAG,EAAE,MAAM,CAAA;IACX,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAChC;AAED,mEAAmE;AACnE,MAAM,WAAW,kBAAkB,CAAC,KAAK,GAAG,OAAO;IACjD,oFAAoF;IACpF,KAAK,CAAC,CAAC,IAAI,EAAE,MAAM,GAAG,YAAY,EAAE,CAAA;IACpC,sFAAsF;IACtF,IAAI,CAAC,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,GAAG,IAAI,CAAC,CAAA;IAC1C,sEAAsE;IACtE,OAAO,CAAC,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,GAAG,IAAI,GAAG,OAAO,CAAA;IACpD,uEAAuE;IACvE,IAAI,CAAC,CAAC,IAAI,EAAE,cAAc,CAAC,KAAK,CAAC,EAAE,GAAG,EAAE,gBAAgB,GAAG,SAAS,GAAG,IAAI,CAAA;IAC3E,yDAAyD;IACzD,MAAM,CAAC,CAAC,IAAI,EAAE,cAAc,CAAC,KAAK,CAAC,EAAE,GAAG,EAAE,gBAAgB,GAAG,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAA;IAC3F,qEAAqE;IACrE,YAAY,CAAC,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC,CAAA;CACnC;AAED,MAAM,WAAW,gBAAgB,CAAC,KAAK,GAAG,OAAO;IAC/C,MAAM,EAAE,gBAAgB,GAAG,wBAAwB,CAAA;IACnD;;;;;OAKG;IACH,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB;;;;;OAKG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,WAAW,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;IAC/D,2EAA2E;IAC3E,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC,YAAY,CAAC,CAAA;IAChC;;;;OAIG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAClC,8BAA8B;IAC9B,MAAM,CAAC,EAAE,kBAAkB,CAAC,KAAK,CAAC,CAAA;IAClC;;;;;OAKG;IACH,YAAY,CAAC,EAAE,OAAO,CAAA;CACvB;AAED,4EAA4E;AAC5E,MAAM,WAAW,cAAc,CAAC,KAAK,GAAG,OAAO;IAC7C,IAAI,EAAE,MAAM,CAAA;IACZ,gDAAgD;IAChD,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAAA;IACtC,oEAAoE;IACpE,OAAO,EAAE,OAAO,GAAG,IAAI,CAAA;IACvB,4DAA4D;IAC5D,KAAK,EAAE,KAAK,GAAG,IAAI,CAAA;IACnB,wFAAwF;IACxF,KAAK,EAAE,YAAY,EAAE,CAAA;IACrB,kBAAkB;IAClB,OAAO,EAAE,OAAO,CAAA;IAChB,sEAAsE;IACtE,MAAM,EAAE,MAAM,CAAA;IACd;;;;;OAKG;IACH,WAAW,CAAC,EAAE,qBAAqB,CAAA;IACnC,oFAAoF;IACpF,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB;AAED,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,wBAAwB,CAAA;IAChC,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,MAAM,CAAA;CACf;AAED,sEAAsE;AACtE,MAAM,WAAW,SAAS;IACxB,qGAAqG;IACrG,IAAI,EAAE,KAAK,CAAC;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC,CAAA;IACtG,KAAK,EAAE,UAAU,EAAE,CAAA;IACnB,OAAO,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;CACnD;AAED,MAAM,WAAW,SAAS,CAAC,KAAK,GAAG,OAAO;IACxC,MAAM,EAAE,wBAAwB,CAAA;IAChC,uCAAuC;IACvC,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAA;IACxC,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,WAAW,CAAC,EAAE,qBAAqB,GAAG,IAAI,CAAA;KAAE,GAAG,OAAO,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC,CAAA;IACjI,OAAO,CAAC,IAAI,EAAE,cAAc,CAAC,KAAK,CAAC,GAAG,SAAS,GAAG,SAAS,CAAA;IAC3D,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAA;IACrC,+EAA+E;IAC/E,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAA;IACvC,eAAe,CAAC,GAAG,EAAE,GAAG,GAAG,OAAO,CAAA;IAClC,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAA;IAC3C,SAAS,CAAC,GAAG,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAAA;CAC5C;AAKD,eAAO,MAAM,YAAY,iEAAiE,CAAA;AAC1F,eAAO,MAAM,aAAa,uCAAuC,CAAA;AAIjE,iFAAiF;AACjF,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,SAAS,YAAY,EAAE,GAAG,MAAM,EAAE,CAIzE;AAUD;;;;GAIG;AACH,wBAAgB,YAAY,CAC1B,MAAM,EAAE,wBAAwB,EAChC,KAAK,EAAE;IAAE,KAAK,EAAE,SAAS,YAAY,EAAE,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,OAAO,CAAC,YAAY,CAAC,CAAA;CAAE,GAChH,YAAY,CAoBd;AAED,4EAA4E;AAC5E,wBAAgB,UAAU,CACxB,MAAM,EAAE,wBAAwB,EAChC,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,YAAY,GACrB,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAsBhC;AAED,wBAAgB,eAAe,CAAC,KAAK,GAAG,OAAO,EAAE,OAAO,EAAE,gBAAgB,CAAC,KAAK,CAAC,GAAG,SAAS,CAAC,KAAK,CAAC,CAiMnG"}
|
package/dist/host.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { buildRobotsTxt, buildSitemapXml } from './chunk-WCAQRATH.js';
|
|
2
2
|
export { buildRobotsTxt, buildSitemapXml } from './chunk-WCAQRATH.js';
|
|
3
|
-
import { storefrontPaths, isPublicPath, menuDataNeeds, isKnownPath, productSeo, menuSeo, pageSeo } from './chunk-
|
|
4
|
-
export { menuDataNeeds } from './chunk-
|
|
3
|
+
import { storefrontPaths, isPublicPath, menuDataNeeds, isKnownPath, productSeo, menuSeo, pageSeo, metaPageViewInlineScript } from './chunk-Z2NKBC42.js';
|
|
4
|
+
export { menuDataNeeds } from './chunk-Z2NKBC42.js';
|
|
5
5
|
import { collectDataNeeds, resolvePageDataNeed, toBlockNodes, defaultPageBlocks } from './chunk-SLJ4BRXD.js';
|
|
6
6
|
import { resolveConfig, matchPath } from './chunk-PSDR5TZD.js';
|
|
7
7
|
import { validateExperiments } from './chunk-4REZOGGW.js';
|
|
@@ -96,6 +96,20 @@ function createStoreHost(options) {
|
|
|
96
96
|
return [];
|
|
97
97
|
}
|
|
98
98
|
}
|
|
99
|
+
let savedPixel = null;
|
|
100
|
+
async function metaPixelIdFor() {
|
|
101
|
+
if (options.metaPageView === false) return void 0;
|
|
102
|
+
const declared = config.analytics?.metaPixel?.pixelId;
|
|
103
|
+
if (declared) return declared;
|
|
104
|
+
if (savedPixel && Date.now() - savedPixel.at < 3e5) return savedPixel.id ?? void 0;
|
|
105
|
+
try {
|
|
106
|
+
const settings = await getShopProvider().getPublicStoreSettings?.();
|
|
107
|
+
savedPixel = { id: settings?.metaPixelId ?? null, at: Date.now() };
|
|
108
|
+
} catch {
|
|
109
|
+
savedPixel = { id: null, at: Date.now() };
|
|
110
|
+
}
|
|
111
|
+
return savedPixel.id ?? void 0;
|
|
112
|
+
}
|
|
99
113
|
async function loadRoute(path, origin, request) {
|
|
100
114
|
const params = matchPath("/product/:slug", path) ?? {};
|
|
101
115
|
const needs = [
|
|
@@ -104,7 +118,7 @@ function createStoreHost(options) {
|
|
|
104
118
|
...extend.needs?.(path) ?? []
|
|
105
119
|
];
|
|
106
120
|
const entries = {};
|
|
107
|
-
const [, product, extra, hours] = await Promise.all([
|
|
121
|
+
const [, product, extra, hours, metaPixelId] = await Promise.all([
|
|
108
122
|
// Failures are isolated per need: an unseeded need falls back to fetching
|
|
109
123
|
// in the browser, which is the pre-SSR behaviour, not a new failure mode.
|
|
110
124
|
Promise.all(needs.map(async (need) => {
|
|
@@ -112,11 +126,22 @@ function createStoreHost(options) {
|
|
|
112
126
|
})),
|
|
113
127
|
productFor(path),
|
|
114
128
|
extend.load ? extend.load(path) : Promise.resolve(null),
|
|
115
|
-
hoursFor(path)
|
|
129
|
+
hoursFor(path),
|
|
130
|
+
metaPixelIdFor()
|
|
116
131
|
]);
|
|
117
132
|
const missing = !isKnownPath(config, path) || Boolean(matchPath("/product/:slug", path)) && product === null || Boolean(extend.missing?.(path, extra));
|
|
118
133
|
const flags = request?.experiments?.flags;
|
|
119
|
-
return {
|
|
134
|
+
return {
|
|
135
|
+
path,
|
|
136
|
+
entries,
|
|
137
|
+
product,
|
|
138
|
+
extra,
|
|
139
|
+
hours,
|
|
140
|
+
missing,
|
|
141
|
+
origin,
|
|
142
|
+
...flags && Object.keys(flags).length ? { experiments: { flags } } : {},
|
|
143
|
+
...metaPixelId ? { metaPixelId } : {}
|
|
144
|
+
};
|
|
120
145
|
}
|
|
121
146
|
function menuFrom(data) {
|
|
122
147
|
const read = (loader) => {
|
|
@@ -157,10 +182,15 @@ function createStoreHost(options) {
|
|
|
157
182
|
...isPublicPath(config, path) ? [] : [{ name: "robots", content: "noindex,nofollow" }]
|
|
158
183
|
],
|
|
159
184
|
links: tags.links,
|
|
160
|
-
scripts:
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
185
|
+
scripts: [
|
|
186
|
+
// Meta's PageView at first paint (meta-pageview.ts): inline, so it
|
|
187
|
+
// does not wait for the client bundle or for fbevents.js.
|
|
188
|
+
...data?.metaPixelId && metaPageViewInlineScript(data.metaPixelId) ? [{ type: "text/javascript", children: metaPageViewInlineScript(data.metaPixelId) }] : [],
|
|
189
|
+
...tags.jsonLd.map((obj) => ({
|
|
190
|
+
type: "application/ld+json",
|
|
191
|
+
children: JSON.stringify(obj).replace(/</g, "\\u003c")
|
|
192
|
+
}))
|
|
193
|
+
]
|
|
164
194
|
};
|
|
165
195
|
}
|
|
166
196
|
return {
|