@fayz-ai/storefront 0.85.3 → 0.87.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.
@@ -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
- export { MENU_DATA_NEEDS, isKnownPath, isPublicPath, menuDataNeeds, menuJsonLd, menuSeo, pageSeo, primaryImageUrl, productJsonLd, productSeo, resolveStorefrontRoute, storefrontPaths };
233
- //# sourceMappingURL=chunk-CE4U65TZ.js.map
234
- //# sourceMappingURL=chunk-CE4U65TZ.js.map
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"]}
@@ -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":"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"}
1
+ {"version":3,"file":"store-host.d.ts","sourceRoot":"","sources":["../../src/host/store-host.ts"],"names":[],"mappings":"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"}
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-CE4U65TZ.js';
4
- export { menuDataNeeds } from './chunk-CE4U65TZ.js';
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 { path, entries, product, extra, hours, missing, origin, ...flags && Object.keys(flags).length ? { experiments: { flags } } : {} };
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: tags.jsonLd.map((obj) => ({
161
- type: "application/ld+json",
162
- children: JSON.stringify(obj).replace(/</g, "\\u003c")
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 {
package/dist/host.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/host/store-host.ts","../src/host/image-origin.ts"],"names":["getShopProvider"],"mappings":"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","file":"host.js","sourcesContent":["import { buildSeoTags, type BlockNode, type SeoConfig, type SeoLinkTag } from '@fayz-ai/core'\nimport { getShopProvider } from '@fayz-ai/shop/runtime'\nimport type { Category, Product, ServiceHours } from '@fayz-ai/shop/types'\nimport { resolveConfig, type ResolvedStorefrontConfig, type StorefrontConfig } from '../document/config'\nimport { isKnownPath, isPublicPath, storefrontPaths } from '../app/serving'\nimport { matchPath } from '../app/router'\nimport { collectDataNeeds, resolvePageDataNeed, type PageDataEntry } from '../blocks/page-data'\nimport { defaultPageBlocks, toBlockNodes } from '../blocks/page-blocks'\nimport { menuDataNeeds } from '../data/menu-needs'\nimport { menuSeo, pageSeo, productSeo, type MenuBusiness } from '../lib/seo'\nimport { buildRobotsTxt, buildSitemapXml } from '../app/sitemap'\nimport { validateExperiments, type ExperimentAssignments } from '../experiments/assign'\n\n// ---------------------------------------------------------------------------\n// A store served from a server, described once.\n//\n// Everything a server-rendered store has to decide per request that is NOT a\n// framework concern: which page a path is, what it loads before render, how it\n// is titled and described to a crawler, whether a shared cache may hold it,\n// where an old URL now lives, and what goes in sitemap.xml and robots.txt.\n//\n// It was written first as one store's host file (artorious-shop), and every\n// new store was going to copy it. Here it takes the store's config and returns\n// those answers; a framework adapter (`@fayz-ai/storefront/tanstack`) and the\n// store's route files call them. Framework-free: no React, nothing that only\n// runs in a browser or only on a server.\n// ---------------------------------------------------------------------------\n\n/** One read the page declares, keyed as `useLoader` asks for it. */\nexport interface HostDataNeed {\n key: string\n loader: string\n params: Record<string, unknown>\n}\n\n/** What the store's own routes add to a page, beyond the SDK's. */\nexport interface StoreHostExtension<Extra = unknown> {\n /** Reads an app-owned page makes (recipes, landing pages), seeded before render. */\n needs?(path: string): HostDataNeed[]\n /** The record an app-owned page is about, for its head and its 404. `null` = none. */\n load?(path: string): Promise<Extra | null>\n /** True when an app-owned URL has nothing behind it (answers 404). */\n missing?(path: string, extra: Extra | null): boolean\n /** The head for an app-owned page. Returning null leaves the SDK's. */\n head?(data: StoreRouteData<Extra>, ctx: StoreHeadContext): SeoConfig | null\n /** More JSON-LD for any page — added after the SDK's. */\n jsonLd?(data: StoreRouteData<Extra>, ctx: StoreHeadContext): Array<Record<string, unknown>>\n /** Parameterised paths the SDK cannot enumerate (a blog's posts). */\n sitemapPaths?(): Promise<string[]>\n}\n\nexport interface StoreHostOptions<Extra = unknown> {\n config: StorefrontConfig | ResolvedStorefrontConfig\n /**\n * The one address the store is indexed under ('https://loja.com.br'). A\n * request to any other public host — the workers.dev deployment URL, a\n * preview — is told not to be indexed, so one store does not compete with\n * itself under two hostnames. Unset: every host counts as canonical.\n */\n canonicalOrigin?: string\n /**\n * Title and description for the routes the SDK has no copy for, by path.\n * Write each for the query it should answer, not for the brand: a home page,\n * the delivery menu and three legal pages sharing one <title> is a\n * duplicate-title report and a coin flip over which one is shown.\n */\n pages?: Record<string, { title: string; description?: string }>\n /** The store's one-liner: the home page's description and the fallback. */\n description?: string\n /**\n * The house as the menu's Restaurant entity states it. Merged OVER what the\n * store's own data says (contact, social links, delivery locality, opening\n * hours read live from the back office) — set only what that data lacks.\n */\n business?: Partial<MenuBusiness>\n /**\n * Paths that answer 301 elsewhere. In delivery mode `/catalog` and `/menu`\n * redirect to the menu by default, so old links and results land where\n * people order; pass `{}` to turn that off.\n */\n redirects?: Record<string, string>\n /** The store's own routes. */\n extend?: StoreHostExtension<Extra>\n}\n\n/** What a route loader returns, serialised to the browser with the page. */\nexport interface StoreRouteData<Extra = unknown> {\n path: string\n /** Seeded page data, keyed by `dataNeedKey`. */\n entries: Record<string, PageDataEntry>\n /** The product a product URL is about, for its head and its 404. */\n product: Product | null\n /** The app-owned record (see `StoreHostExtension.load`). */\n extra: Extra | null\n /** Opening hours read for the Restaurant entity; empty when the store declares none. */\n hours: ServiceHours[]\n /** Answer 404. */\n missing: boolean\n /** Public origin of the request, for canonical and OpenGraph URLs. */\n origin: string\n /**\n * The variants the edge assigned this request (`createStorefrontEdge`'s\n * `experiments`), read from the `x-fayz-experiments` header by the route.\n * Variants only — never the visitor id: this is serialised into HTML the\n * edge caches per variant bucket. Absent: every experiment is control.\n */\n experiments?: ExperimentAssignments\n}\n\nexport interface StoreHeadContext {\n config: ResolvedStorefrontConfig\n path: string\n origin: string\n}\n\n/** The head as a framework wants it: meta, links, JSON-LD scripts. */\nexport interface StoreHead {\n /** `title` and `charSet` entries alongside name/property tags, as TanStack's head API takes them. */\n meta: Array<{ name?: string; property?: string; content?: string; title?: string; charSet?: 'utf-8' }>\n links: SeoLinkTag[]\n scripts: Array<{ type: string; children: string }>\n}\n\nexport interface StoreHost<Extra = unknown> {\n config: ResolvedStorefrontConfig\n /** Where a path now lives, or null. */\n redirectFor(path: string): string | null\n loadRoute(path: string, origin: string, request?: { experiments?: ExperimentAssignments | null }): Promise<StoreRouteData<Extra>>\n headFor(data: StoreRouteData<Extra> | undefined): StoreHead\n cacheControlFor(path: string): string\n /** Public pages render on the server; per-shopper ones only in the browser. */\n renderedOnServer(path: string): boolean\n isCanonicalHost(url: URL): boolean\n sitemapXml(origin: string): Promise<string>\n robotsTxt(url: URL, origin: string): string\n}\n\n// s-maxage is the edge TTL. Five minutes on purpose: an edge cache entry cannot\n// be purged globally from a workers.dev URL, so this bounds how stale a price\n// can be until the store's own zone and a purge hook exist.\nexport const PUBLIC_CACHE = 'public, max-age=0, s-maxage=300, stale-while-revalidate=3600'\nexport const PRIVATE_CACHE = 'private, no-store, must-revalidate'\n\nconst DAY = ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa']\n\n/** Back-office opening hours as schema.org `openingHours` ('Mo 11:00-22:00'). */\nexport function openingHoursFrom(hours: readonly ServiceHours[]): string[] {\n return hours\n .filter((h) => h.weekday >= 0 && h.weekday <= 6)\n .map((h) => `${DAY[h.weekday]} ${h.opensAt.slice(0, 5)}-${h.closesAt.slice(0, 5)}`)\n}\n\n/** A site-relative path made absolute; absolute URLs pass. */\nfunction absoluteUrl(url: string | undefined, origin: string): string | undefined {\n if (!url) return undefined\n if (/^https?:\\/\\//.test(url)) return url\n if (!origin) return undefined\n return `${origin}${url.startsWith('/') ? url : `/${url}`}`\n}\n\n/**\n * The business facts the store's own data already holds, with the explicit\n * ones on top. Nothing is guessed: a fact neither source has stays out of the\n * JSON-LD.\n */\nexport function businessFrom(\n config: ResolvedStorefrontConfig,\n input: { hours: readonly ServiceHours[]; origin: string; description?: string; override?: Partial<MenuBusiness> },\n): MenuBusiness {\n const contact = config.footer?.contact\n const social = (config.footer?.social ?? []).map((s) => s.href).filter((href) => /^https?:\\/\\//.test(href))\n const locality = config.delivery?.locality\n const opening = openingHoursFrom(input.hours)\n const base: MenuBusiness = {\n ...(input.description ? { description: input.description } : {}),\n ...(contact?.phone ? { telephone: contact.phone } : {}),\n ...(contact?.email ? { email: contact.email } : {}),\n ...(contact?.address ? { address: { streetAddress: contact.address } } : {}),\n ...(locality ? { areaServed: [locality] } : {}),\n ...(opening.length ? { openingHours: opening } : {}),\n ...(social.length ? { sameAs: social } : {}),\n }\n const cover = absoluteUrl(config.delivery?.coverImage, input.origin)\n if (cover) base.image = cover\n const merged = { ...base, ...input.override }\n if (merged.image) merged.image = absoluteUrl(merged.image, input.origin)\n if (merged.logo) merged.logo = absoluteUrl(merged.logo, input.origin)\n return merged\n}\n\n/** The house on the home page: Organization (knowledge panel) + WebSite. */\nexport function homeJsonLd(\n config: ResolvedStorefrontConfig,\n origin: string,\n business: MenuBusiness,\n): Array<Record<string, unknown>> {\n if (!origin) return []\n return [\n {\n '@context': 'https://schema.org',\n '@type': 'Organization',\n name: config.name,\n url: origin,\n ...(business.logo ? { logo: business.logo } : {}),\n ...(business.description ? { description: business.description } : {}),\n ...(business.email ? { email: business.email } : {}),\n ...(business.telephone ? { telephone: business.telephone } : {}),\n ...(business.sameAs?.length ? { sameAs: business.sameAs } : {}),\n },\n {\n '@context': 'https://schema.org',\n '@type': 'WebSite',\n name: config.name,\n url: origin,\n inLanguage: config.locale ?? 'pt-BR',\n },\n ]\n}\n\nexport function createStoreHost<Extra = unknown>(options: StoreHostOptions<Extra>): StoreHost<Extra> {\n const config = 'features' in options.config && 'pages' in options.config\n ? (options.config as ResolvedStorefrontConfig)\n : resolveConfig(options.config as StorefrontConfig)\n const menuPath = config.delivery?.menuPath\n const redirects: Record<string, string> =\n options.redirects ??\n (menuPath\n ? Object.fromEntries(['/catalog', '/menu'].filter((p) => p !== menuPath).map((p) => [p, menuPath]))\n : {})\n const extend = options.extend ?? {}\n // A typo in an experiment fails the build, not the experiment: a page that\n // silently renders no variant would run for weeks measuring nothing.\n const experimentIssues = validateExperiments(config.experiments)\n if (experimentIssues.length) throw new Error(`Invalid storefront experiments:\\n- ${experimentIssues.join('\\n- ')}`)\n const canonicalHost = options.canonicalOrigin ? new URL(options.canonicalOrigin).hostname : null\n\n const isMenu = (path: string) => Boolean(menuPath && matchPath(menuPath, path))\n\n /** The block tree the SDK renders for `path` — the source of its data needs. */\n function treeFor(path: string): BlockNode[] {\n const pages = config.pages\n if (matchPath('/product/:slug', path)) return toBlockNodes(pages.product?.blocks ?? defaultPageBlocks('product')) as BlockNode[]\n if (matchPath('/catalog', path)) return toBlockNodes(pages.catalog?.blocks ?? defaultPageBlocks('catalog')) as BlockNode[]\n if (path === '/' || path === '') return toBlockNodes(pages.home?.blocks ?? defaultPageBlocks('home')) as BlockNode[]\n return []\n }\n\n async function productFor(path: string): Promise<Product | null> {\n const slug = matchPath('/product/:slug', path)?.slug\n if (!slug) return null\n const found = await getShopProvider().listProducts({ slug, status: 'active', limit: 1 })\n return found[0] ?? null\n }\n\n /** Only where the hours are said: the menu and the home page. */\n async function hoursFor(path: string): Promise<ServiceHours[]> {\n if (!config.delivery || !(isMenu(path) || path === '/')) return []\n try {\n return (await getShopProvider().listServiceHours?.('delivery')) ?? []\n } catch {\n return []\n }\n }\n\n async function loadRoute(\n path: string,\n origin: string,\n request?: { experiments?: ExperimentAssignments | null },\n ): Promise<StoreRouteData<Extra>> {\n const params = matchPath('/product/:slug', path) ?? {}\n const needs: HostDataNeed[] = [\n ...collectDataNeeds(treeFor(path), params),\n ...(isMenu(path) ? menuDataNeeds() : []),\n ...(extend.needs?.(path) ?? []),\n ]\n const entries: Record<string, PageDataEntry> = {}\n const [, product, extra, hours] = await Promise.all([\n // Failures are isolated per need: an unseeded need falls back to fetching\n // in the browser, which is the pre-SSR behaviour, not a new failure mode.\n Promise.all(needs.map(async (need) => { entries[need.key] = await resolvePageDataNeed(need) })),\n productFor(path),\n extend.load ? extend.load(path) : Promise.resolve(null),\n hoursFor(path),\n ])\n // Two ways a URL has nothing behind it: the route table does not know it,\n // or it is a well-formed product URL whose product is gone. Both answer 404\n // — a soft 404 gets indexed as real content.\n const missing =\n !isKnownPath(config, path) ||\n (Boolean(matchPath('/product/:slug', path)) && product === null) ||\n Boolean(extend.missing?.(path, extra))\n // Variants only (see StoreRouteData.experiments).\n const flags = request?.experiments?.flags\n return { path, entries, product, extra, hours, missing, origin, ...(flags && Object.keys(flags).length ? { experiments: { flags } } : {}) }\n }\n\n function menuFrom(data: StoreRouteData<Extra>): { products: Product[]; categories: Category[] } {\n const read = <T,>(loader: string): T[] => {\n const need = menuDataNeeds().find((n) => n.loader === loader)\n const entry = need ? data.entries[need.key] : undefined\n return entry?.status === 'ready' && Array.isArray(entry.data) ? (entry.data as T[]) : []\n }\n return { products: read<Product>('products.list'), categories: read<Category>('categories.list') }\n }\n\n function headFor(data: StoreRouteData<Extra> | undefined): StoreHead {\n const path = data?.path ?? '/'\n const origin = (data?.origin ?? '').replace(/\\/$/, '')\n const ctx = { config, path, origin }\n const page = options.pages?.[path.split('?')[0] || '/']\n const business = businessFrom(config, {\n hours: data?.hours ?? [],\n origin,\n description: options.description,\n override: options.business,\n })\n\n const own = data ? extend.head?.(data, ctx) : null\n const base: SeoConfig =\n own ??\n (data?.product\n ? (productSeo(data.product, ctx) as SeoConfig)\n : data && isMenu(path)\n ? (menuSeo({ ...menuFrom(data), ...page, business }, ctx) as SeoConfig)\n : (pageSeo(ctx, page ?? { title: config.name, description: options.description }) as SeoConfig))\n\n const jsonLd = [\n ...(base.jsonLd ?? []),\n ...(path === '/' ? homeJsonLd(config, origin, business) : []),\n ...(data ? (extend.jsonLd?.(data, ctx) ?? []) : []),\n ]\n // A relative og:image renders as a broken share card; the store's cover is\n // the fallback for a page that has no photo of its own.\n const image = absoluteUrl(base.image ?? business.image, origin)\n const tags = buildSeoTags({ ...base, jsonLd, ...(image ? { image } : {}) })\n return {\n meta: [\n { charSet: 'utf-8' as const },\n { name: 'viewport', content: 'width=device-width, initial-scale=1' },\n { title: tags.title },\n ...tags.meta,\n // Checkout, account and order pages are one shopper's state at a URL —\n // never indexed. Same line the cache header is drawn on.\n ...(isPublicPath(config, path) ? [] : [{ name: 'robots', content: 'noindex,nofollow' }]),\n ],\n links: tags.links,\n scripts: tags.jsonLd.map((obj) => ({\n type: 'application/ld+json',\n children: JSON.stringify(obj).replace(/</g, '\\\\u003c'),\n })),\n }\n }\n\n return {\n config,\n redirectFor: (path) => redirects[path.split('?')[0] || '/'] ?? null,\n loadRoute,\n headFor,\n cacheControlFor: (path) => (isPublicPath(config, path) ? PUBLIC_CACHE : PRIVATE_CACHE),\n renderedOnServer: (path) => isPublicPath(config, path),\n isCanonicalHost(url) {\n if (!canonicalHost) return true\n const host = url.hostname\n // Local and preview hosts are never reachable by a crawler, and treating\n // them as foreign would put noindex in front of every e2e run.\n return host === canonicalHost || host === 'localhost' || host === '127.0.0.1' || host.endsWith('.local')\n },\n async sitemapXml(origin) {\n const [paths, extra] = await Promise.all([\n storefrontPaths({ config, provider: getShopProvider() }),\n extend.sitemapPaths?.() ?? Promise.resolve([]),\n ])\n // A URL that answers 301 does not belong in a sitemap: Search Console\n // reports it as an error, and the page it points to is listed already.\n const all = [...paths, ...extra].filter((p, i, list) => !redirects[p] && list.indexOf(p) === i)\n return buildSitemapXml(origin, all)\n },\n robotsTxt(url, origin) {\n if (this.isCanonicalHost(url)) return buildRobotsTxt(origin)\n return `# Not the store's address. The store is at ${options.canonicalOrigin}.\\nUser-agent: *\\nDisallow: /\\n`\n },\n }\n}\n","import { getShopProvider, setShopProvider } from '@fayz-ai/shop/runtime'\n\n// ---------------------------------------------------------------------------\n// Photos addressed at the store, not at the storage project.\n//\n// Catalogue rows hold absolute Supabase URLs, and the SDK builds every image\n// variant from whatever URL it is given — its matcher reads the PATH, not the\n// host. So serving photos from the store's own origin (see ./edge-handler) is\n// a matter of swapping the host on the way out of the provider.\n//\n// The origin is read per call, never captured: on the server it is the request\n// being answered, in the browser the page's own origin, and those two agree —\n// which keeps hydration quiet. With no origin, the row's URL is left alone and\n// photos come from Supabase: degraded, never broken.\n// ---------------------------------------------------------------------------\n\nconst PUBLIC_OBJECT = /^https?:\\/\\/[^/]+(\\/storage\\/v1\\/(?:object|render\\/image)\\/public\\/shop-images\\/[^\"'\\s]*)$/\n\n/** Swap the host of a storage URL for `origin`; anything else passes through. */\nexport function onOrigin(value: string, origin: string): string {\n const match = PUBLIC_OBJECT.exec(value)\n return match ? `${origin}${match[1]}` : value\n}\n\nfunction rewrite<T>(value: T, origin: string, depth = 0): T {\n if (depth > 6) return value\n if (typeof value === 'string') return onOrigin(value, origin) as T\n if (Array.isArray(value)) return value.map((v) => rewrite(v, origin, depth + 1)) as T\n if (value && typeof value === 'object' && (value as object).constructor === Object) {\n const out: Record<string, unknown> = {}\n for (const [k, v] of Object.entries(value as Record<string, unknown>)) out[k] = rewrite(v, origin, depth + 1)\n return out as T\n }\n return value\n}\n\n/**\n * Wrap any data provider so every storage URL it returns addresses `originOf()`.\n * A proxy rather than a list of methods: providers grow, and a photo can come\n * back from a product, a cart line, an order or a blog post.\n */\nexport function withImagesOnOrigin<T extends object>(provider: T, originOf: () => string): T {\n return new Proxy(provider, {\n get(target, prop) {\n // `target` as the receiver: a provider is a class instance, and a getter\n // handed the proxy as `this` throws on its private fields.\n const value = Reflect.get(target, prop, target)\n if (typeof value !== 'function') return value\n return (...args: unknown[]) => {\n const out = (value as (...a: unknown[]) => unknown).apply(target, args)\n const origin = originOf()\n if (!origin) return out\n return out instanceof Promise ? out.then((v) => rewrite(v, origin)) : rewrite(out, origin)\n }\n },\n }) as T\n}\n\n/**\n * Point every catalogue photo at the store's own origin. Call once, after\n * `initStorefrontRuntime` — it is the provider that call installs that gets\n * wrapped. Other providers serving the same bucket (a blog's) wrap themselves\n * with `withImagesOnOrigin`.\n */\nexport function serveShopImagesFromOrigin(originOf: () => string): void {\n setShopProvider(withImagesOnOrigin(getShopProvider(), originOf))\n}\n"]}
1
+ {"version":3,"sources":["../src/host/store-host.ts","../src/host/image-origin.ts"],"names":["getShopProvider"],"mappings":"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","file":"host.js","sourcesContent":["import { buildSeoTags, type BlockNode, type SeoConfig, type SeoLinkTag } from '@fayz-ai/core'\nimport { getShopProvider } from '@fayz-ai/shop/runtime'\nimport type { Category, Product, ServiceHours } from '@fayz-ai/shop/types'\nimport { resolveConfig, type ResolvedStorefrontConfig, type StorefrontConfig } from '../document/config'\nimport { isKnownPath, isPublicPath, storefrontPaths } from '../app/serving'\nimport { matchPath } from '../app/router'\nimport { collectDataNeeds, resolvePageDataNeed, type PageDataEntry } from '../blocks/page-data'\nimport { defaultPageBlocks, toBlockNodes } from '../blocks/page-blocks'\nimport { menuDataNeeds } from '../data/menu-needs'\nimport { menuSeo, pageSeo, productSeo, type MenuBusiness } from '../lib/seo'\nimport { buildRobotsTxt, buildSitemapXml } from '../app/sitemap'\nimport { validateExperiments, type ExperimentAssignments } from '../experiments/assign'\nimport { metaPageViewInlineScript } from '../analytics/meta-pageview'\n\n// ---------------------------------------------------------------------------\n// A store served from a server, described once.\n//\n// Everything a server-rendered store has to decide per request that is NOT a\n// framework concern: which page a path is, what it loads before render, how it\n// is titled and described to a crawler, whether a shared cache may hold it,\n// where an old URL now lives, and what goes in sitemap.xml and robots.txt.\n//\n// It was written first as one store's host file (artorious-shop), and every\n// new store was going to copy it. Here it takes the store's config and returns\n// those answers; a framework adapter (`@fayz-ai/storefront/tanstack`) and the\n// store's route files call them. Framework-free: no React, nothing that only\n// runs in a browser or only on a server.\n// ---------------------------------------------------------------------------\n\n/** One read the page declares, keyed as `useLoader` asks for it. */\nexport interface HostDataNeed {\n key: string\n loader: string\n params: Record<string, unknown>\n}\n\n/** What the store's own routes add to a page, beyond the SDK's. */\nexport interface StoreHostExtension<Extra = unknown> {\n /** Reads an app-owned page makes (recipes, landing pages), seeded before render. */\n needs?(path: string): HostDataNeed[]\n /** The record an app-owned page is about, for its head and its 404. `null` = none. */\n load?(path: string): Promise<Extra | null>\n /** True when an app-owned URL has nothing behind it (answers 404). */\n missing?(path: string, extra: Extra | null): boolean\n /** The head for an app-owned page. Returning null leaves the SDK's. */\n head?(data: StoreRouteData<Extra>, ctx: StoreHeadContext): SeoConfig | null\n /** More JSON-LD for any page — added after the SDK's. */\n jsonLd?(data: StoreRouteData<Extra>, ctx: StoreHeadContext): Array<Record<string, unknown>>\n /** Parameterised paths the SDK cannot enumerate (a blog's posts). */\n sitemapPaths?(): Promise<string[]>\n}\n\nexport interface StoreHostOptions<Extra = unknown> {\n config: StorefrontConfig | ResolvedStorefrontConfig\n /**\n * The one address the store is indexed under ('https://loja.com.br'). A\n * request to any other public host — the workers.dev deployment URL, a\n * preview — is told not to be indexed, so one store does not compete with\n * itself under two hostnames. Unset: every host counts as canonical.\n */\n canonicalOrigin?: string\n /**\n * Title and description for the routes the SDK has no copy for, by path.\n * Write each for the query it should answer, not for the brand: a home page,\n * the delivery menu and three legal pages sharing one <title> is a\n * duplicate-title report and a coin flip over which one is shown.\n */\n pages?: Record<string, { title: string; description?: string }>\n /** The store's one-liner: the home page's description and the fallback. */\n description?: string\n /**\n * The house as the menu's Restaurant entity states it. Merged OVER what the\n * store's own data says (contact, social links, delivery locality, opening\n * hours read live from the back office) — set only what that data lacks.\n */\n business?: Partial<MenuBusiness>\n /**\n * Paths that answer 301 elsewhere. In delivery mode `/catalog` and `/menu`\n * redirect to the menu by default, so old links and results land where\n * people order; pass `{}` to turn that off.\n */\n redirects?: Record<string, string>\n /** The store's own routes. */\n extend?: StoreHostExtension<Extra>\n /**\n * Send Meta's PageView from an inline <head> script, at first paint, for\n * every visit (see analytics/meta-pageview.ts). Default on whenever the\n * store has a Meta Pixel — `analytics.metaPixel` in code, else the id the\n * merchant saved (shop 0148), read on the server. `false` turns it off.\n */\n metaPageView?: boolean\n}\n\n/** What a route loader returns, serialised to the browser with the page. */\nexport interface StoreRouteData<Extra = unknown> {\n path: string\n /** Seeded page data, keyed by `dataNeedKey`. */\n entries: Record<string, PageDataEntry>\n /** The product a product URL is about, for its head and its 404. */\n product: Product | null\n /** The app-owned record (see `StoreHostExtension.load`). */\n extra: Extra | null\n /** Opening hours read for the Restaurant entity; empty when the store declares none. */\n hours: ServiceHours[]\n /** Answer 404. */\n missing: boolean\n /** Public origin of the request, for canonical and OpenGraph URLs. */\n origin: string\n /**\n * The variants the edge assigned this request (`createStorefrontEdge`'s\n * `experiments`), read from the `x-fayz-experiments` header by the route.\n * Variants only — never the visitor id: this is serialised into HTML the\n * edge caches per variant bucket. Absent: every experiment is control.\n */\n experiments?: ExperimentAssignments\n /** The Meta Pixel the head's PageView boot script reports to. Absent: no script. */\n metaPixelId?: string\n}\n\nexport interface StoreHeadContext {\n config: ResolvedStorefrontConfig\n path: string\n origin: string\n}\n\n/** The head as a framework wants it: meta, links, JSON-LD scripts. */\nexport interface StoreHead {\n /** `title` and `charSet` entries alongside name/property tags, as TanStack's head API takes them. */\n meta: Array<{ name?: string; property?: string; content?: string; title?: string; charSet?: 'utf-8' }>\n links: SeoLinkTag[]\n scripts: Array<{ type: string; children: string }>\n}\n\nexport interface StoreHost<Extra = unknown> {\n config: ResolvedStorefrontConfig\n /** Where a path now lives, or null. */\n redirectFor(path: string): string | null\n loadRoute(path: string, origin: string, request?: { experiments?: ExperimentAssignments | null }): Promise<StoreRouteData<Extra>>\n headFor(data: StoreRouteData<Extra> | undefined): StoreHead\n cacheControlFor(path: string): string\n /** Public pages render on the server; per-shopper ones only in the browser. */\n renderedOnServer(path: string): boolean\n isCanonicalHost(url: URL): boolean\n sitemapXml(origin: string): Promise<string>\n robotsTxt(url: URL, origin: string): string\n}\n\n// s-maxage is the edge TTL. Five minutes on purpose: an edge cache entry cannot\n// be purged globally from a workers.dev URL, so this bounds how stale a price\n// can be until the store's own zone and a purge hook exist.\nexport const PUBLIC_CACHE = 'public, max-age=0, s-maxage=300, stale-while-revalidate=3600'\nexport const PRIVATE_CACHE = 'private, no-store, must-revalidate'\n\nconst DAY = ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa']\n\n/** Back-office opening hours as schema.org `openingHours` ('Mo 11:00-22:00'). */\nexport function openingHoursFrom(hours: readonly ServiceHours[]): string[] {\n return hours\n .filter((h) => h.weekday >= 0 && h.weekday <= 6)\n .map((h) => `${DAY[h.weekday]} ${h.opensAt.slice(0, 5)}-${h.closesAt.slice(0, 5)}`)\n}\n\n/** A site-relative path made absolute; absolute URLs pass. */\nfunction absoluteUrl(url: string | undefined, origin: string): string | undefined {\n if (!url) return undefined\n if (/^https?:\\/\\//.test(url)) return url\n if (!origin) return undefined\n return `${origin}${url.startsWith('/') ? url : `/${url}`}`\n}\n\n/**\n * The business facts the store's own data already holds, with the explicit\n * ones on top. Nothing is guessed: a fact neither source has stays out of the\n * JSON-LD.\n */\nexport function businessFrom(\n config: ResolvedStorefrontConfig,\n input: { hours: readonly ServiceHours[]; origin: string; description?: string; override?: Partial<MenuBusiness> },\n): MenuBusiness {\n const contact = config.footer?.contact\n const social = (config.footer?.social ?? []).map((s) => s.href).filter((href) => /^https?:\\/\\//.test(href))\n const locality = config.delivery?.locality\n const opening = openingHoursFrom(input.hours)\n const base: MenuBusiness = {\n ...(input.description ? { description: input.description } : {}),\n ...(contact?.phone ? { telephone: contact.phone } : {}),\n ...(contact?.email ? { email: contact.email } : {}),\n ...(contact?.address ? { address: { streetAddress: contact.address } } : {}),\n ...(locality ? { areaServed: [locality] } : {}),\n ...(opening.length ? { openingHours: opening } : {}),\n ...(social.length ? { sameAs: social } : {}),\n }\n const cover = absoluteUrl(config.delivery?.coverImage, input.origin)\n if (cover) base.image = cover\n const merged = { ...base, ...input.override }\n if (merged.image) merged.image = absoluteUrl(merged.image, input.origin)\n if (merged.logo) merged.logo = absoluteUrl(merged.logo, input.origin)\n return merged\n}\n\n/** The house on the home page: Organization (knowledge panel) + WebSite. */\nexport function homeJsonLd(\n config: ResolvedStorefrontConfig,\n origin: string,\n business: MenuBusiness,\n): Array<Record<string, unknown>> {\n if (!origin) return []\n return [\n {\n '@context': 'https://schema.org',\n '@type': 'Organization',\n name: config.name,\n url: origin,\n ...(business.logo ? { logo: business.logo } : {}),\n ...(business.description ? { description: business.description } : {}),\n ...(business.email ? { email: business.email } : {}),\n ...(business.telephone ? { telephone: business.telephone } : {}),\n ...(business.sameAs?.length ? { sameAs: business.sameAs } : {}),\n },\n {\n '@context': 'https://schema.org',\n '@type': 'WebSite',\n name: config.name,\n url: origin,\n inLanguage: config.locale ?? 'pt-BR',\n },\n ]\n}\n\nexport function createStoreHost<Extra = unknown>(options: StoreHostOptions<Extra>): StoreHost<Extra> {\n const config = 'features' in options.config && 'pages' in options.config\n ? (options.config as ResolvedStorefrontConfig)\n : resolveConfig(options.config as StorefrontConfig)\n const menuPath = config.delivery?.menuPath\n const redirects: Record<string, string> =\n options.redirects ??\n (menuPath\n ? Object.fromEntries(['/catalog', '/menu'].filter((p) => p !== menuPath).map((p) => [p, menuPath]))\n : {})\n const extend = options.extend ?? {}\n // A typo in an experiment fails the build, not the experiment: a page that\n // silently renders no variant would run for weeks measuring nothing.\n const experimentIssues = validateExperiments(config.experiments)\n if (experimentIssues.length) throw new Error(`Invalid storefront experiments:\\n- ${experimentIssues.join('\\n- ')}`)\n const canonicalHost = options.canonicalOrigin ? new URL(options.canonicalOrigin).hostname : null\n\n const isMenu = (path: string) => Boolean(menuPath && matchPath(menuPath, path))\n\n /** The block tree the SDK renders for `path` — the source of its data needs. */\n function treeFor(path: string): BlockNode[] {\n const pages = config.pages\n if (matchPath('/product/:slug', path)) return toBlockNodes(pages.product?.blocks ?? defaultPageBlocks('product')) as BlockNode[]\n if (matchPath('/catalog', path)) return toBlockNodes(pages.catalog?.blocks ?? defaultPageBlocks('catalog')) as BlockNode[]\n if (path === '/' || path === '') return toBlockNodes(pages.home?.blocks ?? defaultPageBlocks('home')) as BlockNode[]\n return []\n }\n\n async function productFor(path: string): Promise<Product | null> {\n const slug = matchPath('/product/:slug', path)?.slug\n if (!slug) return null\n const found = await getShopProvider().listProducts({ slug, status: 'active', limit: 1 })\n return found[0] ?? null\n }\n\n /** Only where the hours are said: the menu and the home page. */\n async function hoursFor(path: string): Promise<ServiceHours[]> {\n if (!config.delivery || !(isMenu(path) || path === '/')) return []\n try {\n return (await getShopProvider().listServiceHours?.('delivery')) ?? []\n } catch {\n return []\n }\n }\n\n // The merchant's saved pixel id, read on the server for the head's boot\n // script. Cached per isolate for five minutes — the same staleness the edge\n // cache already allows the page itself — and never a reason to fail a page.\n let savedPixel: { id: string | null; at: number } | null = null\n async function metaPixelIdFor(): Promise<string | undefined> {\n if (options.metaPageView === false) return undefined\n const declared = config.analytics?.metaPixel?.pixelId\n if (declared) return declared\n if (savedPixel && Date.now() - savedPixel.at < 300_000) return savedPixel.id ?? undefined\n try {\n const settings = await getShopProvider().getPublicStoreSettings?.()\n savedPixel = { id: settings?.metaPixelId ?? null, at: Date.now() }\n } catch {\n savedPixel = { id: null, at: Date.now() }\n }\n return savedPixel.id ?? undefined\n }\n\n async function loadRoute(\n path: string,\n origin: string,\n request?: { experiments?: ExperimentAssignments | null },\n ): Promise<StoreRouteData<Extra>> {\n const params = matchPath('/product/:slug', path) ?? {}\n const needs: HostDataNeed[] = [\n ...collectDataNeeds(treeFor(path), params),\n ...(isMenu(path) ? menuDataNeeds() : []),\n ...(extend.needs?.(path) ?? []),\n ]\n const entries: Record<string, PageDataEntry> = {}\n const [, product, extra, hours, metaPixelId] = await Promise.all([\n // Failures are isolated per need: an unseeded need falls back to fetching\n // in the browser, which is the pre-SSR behaviour, not a new failure mode.\n Promise.all(needs.map(async (need) => { entries[need.key] = await resolvePageDataNeed(need) })),\n productFor(path),\n extend.load ? extend.load(path) : Promise.resolve(null),\n hoursFor(path),\n metaPixelIdFor(),\n ])\n // Two ways a URL has nothing behind it: the route table does not know it,\n // or it is a well-formed product URL whose product is gone. Both answer 404\n // — a soft 404 gets indexed as real content.\n const missing =\n !isKnownPath(config, path) ||\n (Boolean(matchPath('/product/:slug', path)) && product === null) ||\n Boolean(extend.missing?.(path, extra))\n // Variants only (see StoreRouteData.experiments).\n const flags = request?.experiments?.flags\n return {\n path, entries, product, extra, hours, missing, origin,\n ...(flags && Object.keys(flags).length ? { experiments: { flags } } : {}),\n ...(metaPixelId ? { metaPixelId } : {}),\n }\n }\n\n function menuFrom(data: StoreRouteData<Extra>): { products: Product[]; categories: Category[] } {\n const read = <T,>(loader: string): T[] => {\n const need = menuDataNeeds().find((n) => n.loader === loader)\n const entry = need ? data.entries[need.key] : undefined\n return entry?.status === 'ready' && Array.isArray(entry.data) ? (entry.data as T[]) : []\n }\n return { products: read<Product>('products.list'), categories: read<Category>('categories.list') }\n }\n\n function headFor(data: StoreRouteData<Extra> | undefined): StoreHead {\n const path = data?.path ?? '/'\n const origin = (data?.origin ?? '').replace(/\\/$/, '')\n const ctx = { config, path, origin }\n const page = options.pages?.[path.split('?')[0] || '/']\n const business = businessFrom(config, {\n hours: data?.hours ?? [],\n origin,\n description: options.description,\n override: options.business,\n })\n\n const own = data ? extend.head?.(data, ctx) : null\n const base: SeoConfig =\n own ??\n (data?.product\n ? (productSeo(data.product, ctx) as SeoConfig)\n : data && isMenu(path)\n ? (menuSeo({ ...menuFrom(data), ...page, business }, ctx) as SeoConfig)\n : (pageSeo(ctx, page ?? { title: config.name, description: options.description }) as SeoConfig))\n\n const jsonLd = [\n ...(base.jsonLd ?? []),\n ...(path === '/' ? homeJsonLd(config, origin, business) : []),\n ...(data ? (extend.jsonLd?.(data, ctx) ?? []) : []),\n ]\n // A relative og:image renders as a broken share card; the store's cover is\n // the fallback for a page that has no photo of its own.\n const image = absoluteUrl(base.image ?? business.image, origin)\n const tags = buildSeoTags({ ...base, jsonLd, ...(image ? { image } : {}) })\n return {\n meta: [\n { charSet: 'utf-8' as const },\n { name: 'viewport', content: 'width=device-width, initial-scale=1' },\n { title: tags.title },\n ...tags.meta,\n // Checkout, account and order pages are one shopper's state at a URL —\n // never indexed. Same line the cache header is drawn on.\n ...(isPublicPath(config, path) ? [] : [{ name: 'robots', content: 'noindex,nofollow' }]),\n ],\n links: tags.links,\n scripts: [\n // Meta's PageView at first paint (meta-pageview.ts): inline, so it\n // does not wait for the client bundle or for fbevents.js.\n ...(data?.metaPixelId && metaPageViewInlineScript(data.metaPixelId)\n ? [{ type: 'text/javascript', children: metaPageViewInlineScript(data.metaPixelId) }]\n : []),\n ...tags.jsonLd.map((obj) => ({\n type: 'application/ld+json',\n children: JSON.stringify(obj).replace(/</g, '\\\\u003c'),\n })),\n ],\n }\n }\n\n return {\n config,\n redirectFor: (path) => redirects[path.split('?')[0] || '/'] ?? null,\n loadRoute,\n headFor,\n cacheControlFor: (path) => (isPublicPath(config, path) ? PUBLIC_CACHE : PRIVATE_CACHE),\n renderedOnServer: (path) => isPublicPath(config, path),\n isCanonicalHost(url) {\n if (!canonicalHost) return true\n const host = url.hostname\n // Local and preview hosts are never reachable by a crawler, and treating\n // them as foreign would put noindex in front of every e2e run.\n return host === canonicalHost || host === 'localhost' || host === '127.0.0.1' || host.endsWith('.local')\n },\n async sitemapXml(origin) {\n const [paths, extra] = await Promise.all([\n storefrontPaths({ config, provider: getShopProvider() }),\n extend.sitemapPaths?.() ?? Promise.resolve([]),\n ])\n // A URL that answers 301 does not belong in a sitemap: Search Console\n // reports it as an error, and the page it points to is listed already.\n const all = [...paths, ...extra].filter((p, i, list) => !redirects[p] && list.indexOf(p) === i)\n return buildSitemapXml(origin, all)\n },\n robotsTxt(url, origin) {\n if (this.isCanonicalHost(url)) return buildRobotsTxt(origin)\n return `# Not the store's address. The store is at ${options.canonicalOrigin}.\\nUser-agent: *\\nDisallow: /\\n`\n },\n }\n}\n","import { getShopProvider, setShopProvider } from '@fayz-ai/shop/runtime'\n\n// ---------------------------------------------------------------------------\n// Photos addressed at the store, not at the storage project.\n//\n// Catalogue rows hold absolute Supabase URLs, and the SDK builds every image\n// variant from whatever URL it is given — its matcher reads the PATH, not the\n// host. So serving photos from the store's own origin (see ./edge-handler) is\n// a matter of swapping the host on the way out of the provider.\n//\n// The origin is read per call, never captured: on the server it is the request\n// being answered, in the browser the page's own origin, and those two agree —\n// which keeps hydration quiet. With no origin, the row's URL is left alone and\n// photos come from Supabase: degraded, never broken.\n// ---------------------------------------------------------------------------\n\nconst PUBLIC_OBJECT = /^https?:\\/\\/[^/]+(\\/storage\\/v1\\/(?:object|render\\/image)\\/public\\/shop-images\\/[^\"'\\s]*)$/\n\n/** Swap the host of a storage URL for `origin`; anything else passes through. */\nexport function onOrigin(value: string, origin: string): string {\n const match = PUBLIC_OBJECT.exec(value)\n return match ? `${origin}${match[1]}` : value\n}\n\nfunction rewrite<T>(value: T, origin: string, depth = 0): T {\n if (depth > 6) return value\n if (typeof value === 'string') return onOrigin(value, origin) as T\n if (Array.isArray(value)) return value.map((v) => rewrite(v, origin, depth + 1)) as T\n if (value && typeof value === 'object' && (value as object).constructor === Object) {\n const out: Record<string, unknown> = {}\n for (const [k, v] of Object.entries(value as Record<string, unknown>)) out[k] = rewrite(v, origin, depth + 1)\n return out as T\n }\n return value\n}\n\n/**\n * Wrap any data provider so every storage URL it returns addresses `originOf()`.\n * A proxy rather than a list of methods: providers grow, and a photo can come\n * back from a product, a cart line, an order or a blog post.\n */\nexport function withImagesOnOrigin<T extends object>(provider: T, originOf: () => string): T {\n return new Proxy(provider, {\n get(target, prop) {\n // `target` as the receiver: a provider is a class instance, and a getter\n // handed the proxy as `this` throws on its private fields.\n const value = Reflect.get(target, prop, target)\n if (typeof value !== 'function') return value\n return (...args: unknown[]) => {\n const out = (value as (...a: unknown[]) => unknown).apply(target, args)\n const origin = originOf()\n if (!origin) return out\n return out instanceof Promise ? out.then((v) => rewrite(v, origin)) : rewrite(out, origin)\n }\n },\n }) as T\n}\n\n/**\n * Point every catalogue photo at the store's own origin. Call once, after\n * `initStorefrontRuntime` — it is the provider that call installs that gets\n * wrapped. Other providers serving the same bucket (a blog's) wrap themselves\n * with `withImagesOnOrigin`.\n */\nexport function serveShopImagesFromOrigin(originOf: () => string): void {\n setShopProvider(withImagesOnOrigin(getShopProvider(), originOf))\n}\n"]}
package/dist/index.js CHANGED
@@ -7,10 +7,10 @@ export { MyPurchasesPage } from './chunk-Z5JT7T4I.js';
7
7
  export { useMyOrders } from './chunk-NS6UPIJ3.js';
8
8
  export { OrderTrackingTimeline } from './chunk-RJUEUMR6.js';
9
9
  import './chunk-FCPOPWG3.js';
10
- import { STORE_SECTION_NAMES, isRecord, STORE_SECTIONS, slugifyStoreName, exportStore, validateStore, isJsonValue, sectionToJsonSchema, STOREFRONT_PAGE_KINDS, STOREFRONT_COMPONENT_KEYS, hydrateStoreDocument } from './chunk-BR3XHNVM.js';
11
- export { STOREFRONT_COMPONENT_KEYS, STOREFRONT_PAGE_KINDS, STORE_BACKEND_PROVIDERS, STORE_COMMERCE_MODES, STORE_DOCUMENT_KIND, STORE_PAYMENT_METHOD_KINDS, STORE_PAYMENT_MODES, STORE_ROUTE_CHROMES, STORE_ROUTE_KINDS, STORE_SECTIONS, STORE_SECTION_NAMES, exportStore, hydrateStoreDocument, sectionToJsonSchema, slugifyStoreName, validateStore } from './chunk-BR3XHNVM.js';
12
- import { initStorefrontRuntime, StorefrontConfigHost, StorefrontShell, registerStorefrontBlocks, useCatalogStore } from './chunk-ELW7QWDJ.js';
13
- export { AddressStrip, BenefitsRow, CartDrawer, CatalogPage, CategoryShowcase, ClosedStoreDialog, CollectionCta, CollectionHero, ContentPage, CountdownBand, EntityListBlock, ExperimentBlock, ExperimentProvider, ExperimentVariantBlock, FaqSection, FiltersPanel, FormBlock, HeroSection, ImageTiles, ItemSheet, ManifestoBlock, MediaCarousel, MenuItemRow, MenuPage, MenuToolbar, MotifStrip, NewsletterBand, PLACEHOLDER_ASPECTS, PaymentTerms, Price, ProductCard, ProductDetailPage, ProductEnquiryForm, ProductGallery, ProductGrid, ProductOptionSelector, ProductRail, ProductReviews, ProductSlider, ProductSpecs, ProductSpotlight, PromoBanner, QuantityInput, ScheduleSheet, SealsBand, SearchOverlay, ServiceHoursModal, StepsSection, StoreCover, StorefrontAnalytics, StorefrontConfigHost, StorefrontFooter, StorefrontHeader, StorefrontShell, StorefrontThemeStyle, StoryQuote, THEME_SCALE_FIELDS, THEME_SCALE_VAR_PREFIX, Testimonials, TierCards, applyDeliveryPolicy, applyStoreSettings, bannerPlaceholder, buildEnquiryFromForm, createStorefront, createStorefrontApp, entityListBlockMeta, initStorefrontRuntime, isExperimentBinding, matchesFacets, mergeCollectionFacet, nextMilestone, orderGalleryImages, overrideComponent, overrideProps, pickProductCardImage, productCardComponentContract, productPlaceholder, registerStorefrontBlocks, registerStorefrontEntities, registerStorefrontLoaders, resolveExperimental, resolveOverride, resolvePaymentTerms, resolveStorefrontProvider, sectionsToBlocks, selectIsOpen, selectShowsHours, startStorefrontPostHog, storeBuyerId, storefrontComponentContracts, themeToCss, toFacetMap, useCatalogStore, useCategories, useEnquiry, useExperiment, useExperimentValue, useMarketProduct, useProduct, useProducts, useServiceStore, useStorefrontActions, withFacet } from './chunk-ELW7QWDJ.js';
10
+ import { STORE_SECTION_NAMES, isRecord, STORE_SECTIONS, slugifyStoreName, exportStore, validateStore, isJsonValue, sectionToJsonSchema, STOREFRONT_PAGE_KINDS, STOREFRONT_COMPONENT_KEYS, hydrateStoreDocument } from './chunk-2JMPPAXL.js';
11
+ export { STOREFRONT_COMPONENT_KEYS, STOREFRONT_PAGE_KINDS, STORE_BACKEND_PROVIDERS, STORE_COMMERCE_MODES, STORE_DOCUMENT_KIND, STORE_PAYMENT_METHOD_KINDS, STORE_PAYMENT_MODES, STORE_ROUTE_CHROMES, STORE_ROUTE_KINDS, STORE_SECTIONS, STORE_SECTION_NAMES, exportStore, hydrateStoreDocument, sectionToJsonSchema, slugifyStoreName, validateStore } from './chunk-2JMPPAXL.js';
12
+ import { initStorefrontRuntime, StorefrontConfigHost, StorefrontShell, registerStorefrontBlocks, useCatalogStore } from './chunk-G4WCU526.js';
13
+ export { AddressStrip, BenefitsRow, CartDrawer, CatalogPage, CategoryShowcase, ClosedStoreDialog, CollectionCta, CollectionHero, ContentPage, CountdownBand, EntityListBlock, ExperimentBlock, ExperimentProvider, ExperimentVariantBlock, FaqSection, FiltersPanel, FormBlock, HeroSection, ImageTiles, ItemSheet, ManifestoBlock, MediaCarousel, MenuItemRow, MenuPage, MenuToolbar, MotifStrip, NewsletterBand, PLACEHOLDER_ASPECTS, PaymentTerms, Price, ProductCard, ProductDetailPage, ProductEnquiryForm, ProductGallery, ProductGrid, ProductOptionSelector, ProductRail, ProductReviews, ProductSlider, ProductSpecs, ProductSpotlight, PromoBanner, QuantityInput, ScheduleSheet, SealsBand, SearchOverlay, ServiceHoursModal, StepsSection, StoreCover, StorefrontAnalytics, StorefrontConfigHost, StorefrontFooter, StorefrontHeader, StorefrontShell, StorefrontThemeStyle, StoryQuote, THEME_SCALE_FIELDS, THEME_SCALE_VAR_PREFIX, Testimonials, TierCards, applyDeliveryPolicy, applyStoreSettings, bannerPlaceholder, buildEnquiryFromForm, createStorefront, createStorefrontApp, entityListBlockMeta, initStorefrontRuntime, isExperimentBinding, matchesFacets, mergeCollectionFacet, nextMilestone, orderGalleryImages, overrideComponent, overrideProps, pickProductCardImage, productCardComponentContract, productPlaceholder, registerStorefrontBlocks, registerStorefrontEntities, registerStorefrontLoaders, resolveExperimental, resolveOverride, resolvePaymentTerms, resolveStorefrontProvider, sectionsToBlocks, selectIsOpen, selectShowsHours, startStorefrontPostHog, storeBuyerId, storefrontComponentContracts, themeToCss, toFacetMap, useCatalogStore, useCategories, useEnquiry, useExperiment, useExperimentValue, useMarketProduct, useProduct, useProducts, useServiceStore, useStorefrontActions, withFacet } from './chunk-G4WCU526.js';
14
14
  import { registerStorefrontExtensionPoints, STOREFRONT_SLOTS, useBuyerCountry, StorefrontPage } from './chunk-OOQRYH3N.js';
15
15
  export { BEFORE_PLACE_ORDER_VALVE, BUYER_COUNTRY_COOKIE, CART_LINE_ANNOTATION_POINT, CHECKOUT_FIELD_POINT, ORDER_METADATA_POINT, PAGE_DATA_SCRIPT_ID, PRODUCT_CARD_BADGE_POINT, STOREFRONT_SLOTS, Slot, StorefrontPage, collectOrderMetadata, getCartLineAnnotations, getCheckoutFieldContributions, getProductCardBadges, hydratedEntriesFor, identifyStorefrontBuyer, listSlots, onStorefrontEvent, readHydratedPageData, registerStorefrontExtensionPoints, resetPageDiagnostics, resetStorefrontExtensionPoints, runBeforePlaceOrderValve, serializePageData, trackStorefront, useBuyerCountry, useBuyerStore, useDiscountValidator, useSlotContext, useSlotFilled, useStorefrontPage, useStorefrontPageOptional, useT, usedSlots } from './chunk-OOQRYH3N.js';
16
16
  export { Reveal, prefersReducedMotion, useInView, usePopOnChange, useScrolled } from './chunk-WXXYJZE6.js';
@@ -20,7 +20,7 @@ export { formatProductOptionSelection, getProductOptionGroups, marketForCountry,
20
20
  import './chunk-DVQHKNNX.js';
21
21
  import { formatMoney, useSessionStore } from './chunk-EFHQ5MAJ.js';
22
22
  export { EmailConfirmationRequiredError, SmoothImage, TID, createLazyAuthAdapter, establishCustomerSession, formatMoney, getCustomerAuthAdapter, resolveAuthAdapter, roundCents, signInByEmail, signOutCustomer, signUpCustomer, useSessionStore, useStorefrontHead } from './chunk-EFHQ5MAJ.js';
23
- export { isKnownPath, isPublicPath, menuDataNeeds, menuJsonLd, menuSeo, pageSeo, primaryImageUrl, productJsonLd, productSeo, resolveStorefrontRoute, storefrontPaths } from './chunk-CE4U65TZ.js';
23
+ export { isKnownPath, isPublicPath, menuDataNeeds, menuJsonLd, menuSeo, pageSeo, primaryImageUrl, productJsonLd, productSeo, resolveStorefrontRoute, storefrontPaths } from './chunk-Z2NKBC42.js';
24
24
  import { isNestedBlockRef, BLOCK_STRUCTURAL_KEYS, blockInstanceId } from './chunk-SLJ4BRXD.js';
25
25
  export { BlockDataError, CATALOG_BODY_BLOCK, PRODUCT_BODY_BLOCK, PageDataScope, checkPageBlocks, collectDataNeeds, contentPagePath, defaultPageBlocks, matchContentPage, mergePageTheme, pageThemeSelector, resolveDataParams, resolvePageData, resolvePageDataNeed, routePathParams, toBlockNodes, useBlockData, useLoader } from './chunk-SLJ4BRXD.js';
26
26
  import { resolveConfig, useStorefrontConfig } from './chunk-PSDR5TZD.js';
package/dist/render.js CHANGED
@@ -1,14 +1,14 @@
1
- import { validateStore, hydrateStoreDocument } from './chunk-BR3XHNVM.js';
1
+ import { validateStore, hydrateStoreDocument } from './chunk-2JMPPAXL.js';
2
2
  import { buildRobotsTxt, buildSitemapXml } from './chunk-WCAQRATH.js';
3
3
  export { buildRobotsTxt, buildSitemapXml } from './chunk-WCAQRATH.js';
4
- import { registerStorefrontBlocks, registerStorefrontEntities, StorefrontShell, themeToCss, resolveStorefrontProvider } from './chunk-ELW7QWDJ.js';
4
+ import { registerStorefrontBlocks, registerStorefrontEntities, StorefrontShell, themeToCss, resolveStorefrontProvider } from './chunk-G4WCU526.js';
5
5
  import { resetPageDiagnostics, serializePageData } from './chunk-OOQRYH3N.js';
6
6
  import './chunk-WXXYJZE6.js';
7
7
  import './chunk-YDZU2SZL.js';
8
8
  import './chunk-C4XATNI2.js';
9
9
  import './chunk-DVQHKNNX.js';
10
10
  import './chunk-EFHQ5MAJ.js';
11
- import { storefrontPaths, isPublicPath } from './chunk-CE4U65TZ.js';
11
+ import { storefrontPaths, isPublicPath } from './chunk-Z2NKBC42.js';
12
12
  import { resolvePageData } from './chunk-SLJ4BRXD.js';
13
13
  import { resolveConfig, withStaticRenderPass, StorefrontConfigProvider } from './chunk-PSDR5TZD.js';
14
14
  import './chunk-4REZOGGW.js';
package/dist/tanstack.js CHANGED
@@ -1,11 +1,11 @@
1
- import { StorefrontConfigHost, ExperimentProvider, StorefrontShell } from './chunk-ELW7QWDJ.js';
1
+ import { StorefrontConfigHost, ExperimentProvider, StorefrontShell } from './chunk-G4WCU526.js';
2
2
  import './chunk-OOQRYH3N.js';
3
3
  import './chunk-WXXYJZE6.js';
4
4
  import './chunk-YDZU2SZL.js';
5
5
  import './chunk-C4XATNI2.js';
6
6
  import './chunk-DVQHKNNX.js';
7
7
  import './chunk-EFHQ5MAJ.js';
8
- import './chunk-CE4U65TZ.js';
8
+ import './chunk-Z2NKBC42.js';
9
9
  import { PageDataScope } from './chunk-SLJ4BRXD.js';
10
10
  import { StorefrontRouterProvider } from './chunk-PSDR5TZD.js';
11
11
  import './chunk-4REZOGGW.js';
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "fayz": {
4
4
  "status": "beta"
5
5
  },
6
- "version": "0.85.3",
6
+ "version": "0.87.0",
7
7
  "description": "Fayz SDK storefront kit — Shopify-style customer-facing ecommerce template components",
8
8
  "type": "module",
9
9
  "sideEffects": false,
@@ -55,9 +55,9 @@
55
55
  "posthog-js": "^1.444.0",
56
56
  "@fayz-ai/core": "^0.79.0",
57
57
  "@fayz-ai/sdk": "^0.79.0",
58
- "@fayz-ai/shop": "^0.79.0",
59
- "@fayz-ai/ui": "^0.79.0",
60
- "@fayz-ai/auth": "^0.79.0"
58
+ "@fayz-ai/auth": "^0.79.0",
59
+ "@fayz-ai/shop": "^0.87.0",
60
+ "@fayz-ai/ui": "^0.79.0"
61
61
  },
62
62
  "peerDependencies": {
63
63
  "@supabase/supabase-js": "^2.45.0",