@fayz-ai/storefront 0.84.2 → 0.84.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/CheckoutPage-4MFKLBEI.js +12 -0
- package/dist/{CheckoutPage-DDY4WLFJ.js.map → CheckoutPage-4MFKLBEI.js.map} +1 -1
- package/dist/MyPurchasesPage-F25MIFXT.js +11 -0
- package/dist/{MyPurchasesPage-IHCB5QRR.js.map → MyPurchasesPage-F25MIFXT.js.map} +1 -1
- package/dist/OrderConfirmationPage-SOQDD767.js +10 -0
- package/dist/{OrderConfirmationPage-SAKAGOCC.js.map → OrderConfirmationPage-SOQDD767.js.map} +1 -1
- package/dist/{chunk-6NUBVJJY.js → chunk-BR3XHNVM.js} +5 -5
- package/dist/{chunk-6NUBVJJY.js.map → chunk-BR3XHNVM.js.map} +1 -1
- package/dist/{chunk-K27LYMSF.js → chunk-C4XATNI2.js} +3 -3
- package/dist/{chunk-K27LYMSF.js.map → chunk-C4XATNI2.js.map} +1 -1
- package/dist/{chunk-45R7CIEA.js → chunk-CE4U65TZ.js} +4 -4
- package/dist/{chunk-45R7CIEA.js.map → chunk-CE4U65TZ.js.map} +1 -1
- package/dist/{chunk-PATS4DIF.js → chunk-EFHQ5MAJ.js} +57 -17
- package/dist/chunk-EFHQ5MAJ.js.map +1 -0
- package/dist/{chunk-JLCF7CPP.js → chunk-ELW7QWDJ.js} +13 -12
- package/dist/chunk-ELW7QWDJ.js.map +1 -0
- package/dist/{chunk-GQ3TCJVX.js → chunk-IUP5SDTX.js} +5 -5
- package/dist/{chunk-GQ3TCJVX.js.map → chunk-IUP5SDTX.js.map} +1 -1
- package/dist/{chunk-WPWNYBNG.js → chunk-NS6UPIJ3.js} +3 -3
- package/dist/{chunk-WPWNYBNG.js.map → chunk-NS6UPIJ3.js.map} +1 -1
- package/dist/{chunk-MFAU3AWK.js → chunk-OOQRYH3N.js} +6 -6
- package/dist/{chunk-MFAU3AWK.js.map → chunk-OOQRYH3N.js.map} +1 -1
- package/dist/{chunk-GVZ2SAOI.js → chunk-PSDR5TZD.js} +3 -2
- package/dist/chunk-PSDR5TZD.js.map +1 -0
- package/dist/{chunk-D5C5U2SG.js → chunk-RJUEUMR6.js} +4 -4
- package/dist/{chunk-D5C5U2SG.js.map → chunk-RJUEUMR6.js.map} +1 -1
- package/dist/{chunk-BKO2AQ3Q.js → chunk-SLJ4BRXD.js} +3 -3
- package/dist/{chunk-BKO2AQ3Q.js.map → chunk-SLJ4BRXD.js.map} +1 -1
- package/dist/{chunk-DRMDBXMT.js → chunk-YCAWAON6.js} +7 -7
- package/dist/{chunk-DRMDBXMT.js.map → chunk-YCAWAON6.js.map} +1 -1
- package/dist/{chunk-OHWX6BEY.js → chunk-YDZU2SZL.js} +4 -4
- package/dist/{chunk-OHWX6BEY.js.map → chunk-YDZU2SZL.js.map} +1 -1
- package/dist/{chunk-CV6RYJIC.js → chunk-Z5JT7T4I.js} +8 -8
- package/dist/{chunk-CV6RYJIC.js.map → chunk-Z5JT7T4I.js.map} +1 -1
- package/dist/components/delivery/StoreCover.d.ts.map +1 -1
- package/dist/data/auth.d.ts +7 -0
- package/dist/data/auth.d.ts.map +1 -1
- package/dist/document/config.d.ts +8 -0
- package/dist/document/config.d.ts.map +1 -1
- package/dist/host.js +4 -4
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +22 -22
- package/dist/index.js.map +1 -1
- package/dist/render.js +9 -9
- package/dist/tanstack.js +8 -8
- package/package.json +3 -3
- package/dist/CheckoutPage-DDY4WLFJ.js +0 -12
- package/dist/MyPurchasesPage-IHCB5QRR.js +0 -11
- package/dist/OrderConfirmationPage-SAKAGOCC.js +0 -10
- package/dist/chunk-GVZ2SAOI.js.map +0 -1
- package/dist/chunk-JLCF7CPP.js.map +0 -1
- package/dist/chunk-PATS4DIF.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/blocks/page-data.tsx","../src/blocks/page-blocks.ts"],"names":["entry","getBlockMeta"],"mappings":"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","file":"chunk-BKO2AQ3Q.js","sourcesContent":["import { errorMessage } from '@fayz-ai/core'\nimport React, { createContext, useContext, useEffect, useMemo, useState } from 'react'\nimport {\n dataNeedKey,\n defaultPropsFromSettings,\n getBlockErrorVisibility,\n getBlockMeta,\n isLoaderParamRef,\n isLoaderPropRef,\n resolveLoader,\n} from '@fayz-ai/core'\nimport type { BlockDataNeed, BlockNode } from '@fayz-ai/core'\nimport { getShopProvider } from '@fayz-ai/shop/runtime'\nimport { currentStaticRenderPass } from '../app/static-render'\n\n// ---------------------------------------------------------------------------\n// Page data — declared needs, resolved once per page (spine M3, I11).\n//\n// A block never fetches for itself. Its meta (or its document instance)\n// declares named needs against registered loaders; the page walks its repaired\n// tree, collects them, DEDUPES identical (loader, params) pairs, resolves them\n// concurrently through the ShopProvider port, and hands the results down by\n// context. Two product rails asking the same question cost one provider call —\n// and, because the needs are known before render, a server can resolve the\n// same set ahead of time, which is the SSR prerequisite L4 calls out.\n//\n// Failure semantics follow the M2 error-boundary contract (I7): one failing\n// loader marks ONE entry as errored and never touches the rest of the page.\n// A block whose data errored renders null for shoppers and an error card in\n// dev/admin (BlockDataError), exactly like a block that crashed.\n// ---------------------------------------------------------------------------\n\nexport type PageDataStatus = 'idle' | 'loading' | 'ready' | 'error'\n\nexport interface PageDataEntry {\n status: 'loading' | 'ready' | 'error'\n data?: unknown\n error?: string\n}\n\n/** One collected, binding-resolved, deduped need. */\nexport interface PageDataNeed {\n key: string\n loader: string\n params: Record<string, unknown>\n}\n\n// --- Binding resolution -------------------------------------------------------\n\n/**\n * Resolve a declared need's param bindings against the page's route params and\n * the declaring block's props (setting defaults filled in, so the declaration\n * and the rendered block agree on unset props). A binding that resolves to\n * `undefined` drops its param — dataNeedKey drops undefined anyway, so the\n * declared need and a hook asking with explicit `undefined`s share a key.\n */\nexport function resolveDataParams(\n need: BlockDataNeed,\n context: { params: Record<string, string>; props: Record<string, unknown> },\n): Record<string, unknown> {\n const out: Record<string, unknown> = {}\n for (const [name, value] of Object.entries(need.params ?? {})) {\n let resolved: unknown = value\n if (isLoaderParamRef(value)) resolved = context.params[value.$param]\n else if (isLoaderPropRef(value)) resolved = context.props[value.$prop]\n if (resolved !== undefined) out[name] = resolved\n }\n return out\n}\n\n/** A block's effective needs: the meta declaration with the document\n * instance's `data` merged over it by need name. */\nfunction needsForNode(node: BlockNode): Record<string, BlockDataNeed> {\n const meta = getBlockMeta(node.type)\n return { ...meta?.data, ...node.data }\n}\n\n/**\n * Walk a block tree (children included) and collect every declared need,\n * bindings resolved, deduped by canonical (loader, params) key.\n */\nexport function collectDataNeeds(\n nodes: readonly BlockNode[],\n routeParams: Record<string, string> = {},\n): PageDataNeed[] {\n const found = new Map<string, PageDataNeed>()\n const visit = (node: BlockNode): void => {\n const meta = getBlockMeta(node.type)\n const needs = needsForNode(node)\n if (Object.keys(needs).length > 0) {\n const props = { ...defaultPropsFromSettings(meta?.settings), ...node.props }\n for (const need of Object.values(needs)) {\n if (!need || typeof need.loader !== 'string' || !need.loader) continue\n const params = resolveDataParams(need, { params: routeParams, props })\n const key = dataNeedKey(need.loader, params)\n if (!found.has(key)) found.set(key, { key, loader: need.loader, params })\n }\n }\n for (const child of node.children ?? []) visit(child)\n }\n for (const node of nodes) visit(node)\n return [...found.values()]\n}\n\n// --- Resolution ---------------------------------------------------------------\n\n/** Resolve ONE need through the provider port. Never throws: an unknown\n * loader or a failing read becomes an 'error' entry for that need alone. */\nexport async function resolvePageDataNeed(need: Pick<PageDataNeed, 'loader' | 'params'>): Promise<PageDataEntry> {\n try {\n const impl = resolveLoader(need.loader)\n const data = await impl(need.params, { shop: getShopProvider() })\n return { status: 'ready', data }\n } catch (err) {\n const message = errorMessage(err)\n // Always log — a store hitting this in production is better off with the\n // reason in the console than without it (same policy as BlockErrorBoundary).\n console.error(`[storefront] loader \"${need.loader}\" failed:`, err)\n return { status: 'error', error: message }\n }\n}\n\n/** Resolve a set of needs concurrently, isolated per need. Test seam and the\n * future server-side entry point — the scope component uses the same calls. */\nexport async function resolvePageData(\n needs: readonly PageDataNeed[],\n): Promise<Record<string, PageDataEntry>> {\n const settled = await Promise.all(\n needs.map(async (need) => [need.key, await resolvePageDataNeed(need)] as const),\n )\n return Object.fromEntries(settled)\n}\n\n// --- The scope ----------------------------------------------------------------\n\ninterface PageDataContextValue {\n entries: Record<string, PageDataEntry>\n /** The route params this scope resolved `$param` bindings against — the same\n * ones a consuming block must use, or its key would diverge. */\n params: Record<string, string>\n}\n\nconst PageDataContext = createContext<PageDataContextValue | null>(null)\n\nexport interface PageDataScopeProps {\n nodes: readonly BlockNode[]\n /** Route params the tree's `$param` bindings resolve against. */\n params?: Record<string, string>\n /**\n * Pre-resolved entries, keyed by dataNeedKey — the server-side/static-render\n * seam (L4): `resolvePageData(collectDataNeeds(nodes))` ahead of render,\n * then hand the results in. Keys already 'ready' here are never re-fetched;\n * anything missing resolves client-side as usual.\n *\n * Live, not mount-only: a host whose shell outlives the page (every real\n * one — the shell must not remount on navigation) hands in a NEW object\n * for each page it loads, and a child scope mounting in that same commit\n * sees it. Pass a stable reference per page (the loader's result), not a\n * literal built in render.\n */\n initial?: Record<string, PageDataEntry>\n children: React.ReactNode\n}\n\n/**\n * Collects and resolves the data needs of one block tree, and provides the\n * results. Scopes nest (a slot inside a page): a child scope reuses every\n * entry an ancestor already resolved and only fetches its own remainder, so\n * dedupe holds across the whole rendered page, not per tree.\n */\nexport function PageDataScope({ nodes, params, initial, children }: PageDataScopeProps) {\n const parent = useContext(PageDataContext)\n const routeParams = useMemo(() => params ?? parent?.params ?? {}, [params, parent])\n const needs = useMemo(() => collectDataNeeds(nodes, routeParams), [nodes, routeParams])\n const own = useMemo(\n () => needs.filter((need) => !parent || !(need.key in parent.entries)),\n [needs, parent],\n )\n // A static render (renderStore) has no effects, so this scope reports what it\n // renders and what it would fetch, and reads back anything an earlier pass\n // already resolved. Null in the browser.\n const staticPass = currentStaticRenderPass()\n if (staticPass) {\n staticPass.tree(nodes)\n staticPass.needs(own)\n }\n\n // Seeded synchronously so the first render already shows 'loading' (or the\n // pre-resolved entry) for every owned key — a consuming block must never see\n // a gap and start its own fetch.\n const [entries, setEntries] = useState<Record<string, PageDataEntry>>(() => ({\n // Everything handed in is published, not only the needs THIS scope\n // collected. That is what lets a host framework resolve a page's data in\n // its own loader and wrap the shell in a seeding scope: the page's own\n // scope then finds those keys already answered on its parent and fetches\n // nothing. Without it, seeding only works when the seeding scope happens to\n // render the same block tree.\n ...initial,\n ...Object.fromEntries(\n own.map((need) => [\n need.key,\n initial?.[need.key] ?? staticPass?.entries[need.key] ?? ({ status: 'loading' } as PageDataEntry),\n ]),\n ),\n }))\n\n const signature = own.map((need) => need.key).join('\\n')\n useEffect(() => {\n // A key handed in ready stays authoritative; everything else resolves here.\n const pending = own.filter((need) => initial?.[need.key]?.status !== 'ready')\n if (pending.length === 0) return\n let cancelled = false\n setEntries((prev) => {\n const next = { ...prev }\n for (const need of pending) {\n if (next[need.key]?.status !== 'ready') next[need.key] = { status: 'loading' }\n }\n return next\n })\n for (const need of pending) {\n void resolvePageDataNeed(need).then((entry) => {\n if (!cancelled) setEntries((prev) => ({ ...prev, [need.key]: entry }))\n })\n }\n return () => {\n cancelled = true\n }\n // `own` is captured by its key signature: same keys = same needs.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [signature])\n\n // `initial` is overlaid at RENDER time, on top of what this scope resolved,\n // rather than folded into state once at mount. The difference is one\n // commit, and it is the whole point: after a client-side navigation the\n // host hands in the next page's entries and the next page's own scope\n // mounts in the same commit — it decides which needs are unanswered by\n // reading this value as it mounts. State would only carry the new entries\n // from the following commit, by which time the child has already started\n // fetching everything the host had just resolved, and the shopper watches\n // content that was ready pop in a second time.\n const value = useMemo<PageDataContextValue>(\n () => ({ entries: { ...parent?.entries, ...entries, ...initial }, params: routeParams }),\n [parent, entries, initial, routeParams],\n )\n return <PageDataContext.Provider value={value}>{children}</PageDataContext.Provider>\n}\n\n// --- Consumption --------------------------------------------------------------\n\nexport interface UseLoaderResult<T> {\n status: PageDataStatus\n data: T | null\n error: string | null\n /** Re-run the read (bypasses the page-resolved entry from then on). */\n reload: () => void\n}\n\n/**\n * A block's (or page's) read: `useLoader('products.list', { filter, limit })`.\n *\n * When a PageDataScope above already resolved the same (loader, params) pair —\n * because this block, or ANY block on the page, declared it — the resolved\n * entry is returned and no request is made. Otherwise the hook resolves the\n * loader itself, through the same provider port, so a component used outside a\n * composed page (a store's hand-written route) keeps working identically.\n * Pass `null` params to disable the read.\n */\nexport function useLoader<T>(\n loader: string,\n params: Record<string, unknown> | null,\n): UseLoaderResult<T> {\n const scope = useContext(PageDataContext)\n const key = params === null ? null : dataNeedKey(loader, params)\n const [nonce, setNonce] = useState(0)\n const scoped = nonce === 0 && key !== null && scope ? scope.entries[key] : undefined\n const [local, setLocal] = useState<{ key: string; entry: PageDataEntry } | null>(null)\n\n // Undeclared reads — a hand-written route calling the hook directly — still\n // resolve under a static render: the pass collects the key on the first pass\n // and serves it on the next. Without this, exactly the reads that no block\n // declared would be the ones missing from a headless render.\n const staticPass = currentStaticRenderPass()\n const staticEntry = staticPass && key !== null && scoped === undefined ? staticPass.entries[key] : undefined\n if (staticPass && key !== null && scoped === undefined && staticEntry === undefined) {\n staticPass.needs([{ key, loader, params: params! }])\n }\n\n const paramsKey = params === null ? null : JSON.stringify(params)\n useEffect(() => {\n if (key === null || scoped !== undefined) return\n let cancelled = false\n setLocal({ key, entry: { status: 'loading' } })\n void resolvePageDataNeed({ loader, params: params! }).then((entry) => {\n if (!cancelled) setLocal({ key, entry })\n })\n return () => {\n cancelled = true\n }\n // params identity travels through paramsKey; scoped presence through a boolean.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [loader, paramsKey, nonce, scoped !== undefined])\n\n const entry =\n scoped ??\n staticEntry ??\n (local && local.key === key ? local.entry : key !== null ? { status: 'loading' as const } : null)\n\n return {\n status: entry?.status ?? 'idle',\n data: entry?.status === 'ready' ? (entry.data as T) : null,\n error: entry?.status === 'error' ? entry.error ?? 'unknown error' : null,\n reload: () => setNonce((n) => n + 1),\n }\n}\n\n/**\n * A BLOCK's named data need: `useBlockData(node, 'products', fallback)`.\n *\n * Reads the block's effective declaration — meta `data` with the document\n * instance's `data` merged over it — and resolves its bindings with the SAME\n * inputs the page resolver used (scope route params, node props over setting\n * defaults), so the two always compute the same key. That is what makes a\n * document able to rebind a param (`category: { $param: 'slug' }`) and have\n * the rendered block follow: the block asks for its declared need, not for a\n * hardcoded one.\n *\n * `fallback` covers the component being used directly as JSX (a store's\n * hand-written route), where there is no node and no declaration to read.\n */\nexport function useBlockData<T>(\n node: BlockNode | undefined,\n name: string,\n fallback: { loader: string; params: Record<string, unknown> },\n): UseLoaderResult<T> & { loader: string } {\n const scope = useContext(PageDataContext)\n let loader = fallback.loader\n let params = fallback.params\n if (node) {\n const need = needsForNode(node)[name]\n if (need && typeof need.loader === 'string' && need.loader) {\n const meta = getBlockMeta(node.type)\n loader = need.loader\n params = resolveDataParams(need, {\n params: scope?.params ?? {},\n props: { ...defaultPropsFromSettings(meta?.settings), ...node.props },\n })\n }\n }\n const result = useLoader<T>(loader, params)\n return { ...result, loader }\n}\n\n// --- Error surface ------------------------------------------------------------\n\n/**\n * What a block renders when its declared data failed: nothing for shoppers, a\n * visible card for builders — the same visibility rule as a crashed block\n * (M2's per-block error boundary, WooCommerce's `CURRENT_USER_IS_ADMIN ?\n * onError : () => null`).\n */\nexport function BlockDataError({ loader, message }: { loader: string; message?: string | null }) {\n if (getBlockErrorVisibility() === 'silent') return null\n return (\n <div\n data-block-error={`loader:${loader}`}\n style={{\n border: '1px dashed #dc2626',\n color: '#b91c1c',\n padding: '8px 12px',\n font: '12px ui-monospace, monospace',\n borderRadius: 6,\n }}\n >\n {`Data for this block failed (loader \"${loader}\")`}\n {message ? <div style={{ opacity: 0.8, marginTop: 4 }}>{message}</div> : null}\n </div>\n )\n}\n","import { checkBlockConstraints, checkBlockContext, getBlockMeta, isBlockEnabledOn } from '@fayz-ai/core'\nimport type { BlockDataNeeds, BlockNode } from '@fayz-ai/core'\nimport type { StorefrontSection } from './sections'\nimport type { StorefrontTheme } from '../lib/theme'\nimport { matchPath } from '../app/router'\n\n// ---------------------------------------------------------------------------\n// Pages as block trees.\n//\n// The home page has been a block tree since the registry landed. Every other\n// route was hand-written JSX, which is why a store that wanted a banner above\n// the catalog grid had only one move available: reimplement the catalog page.\n// One store did exactly that for three routes and stopped receiving SDK updates\n// for all three.\n//\n// What the benchmarks do, and what this adopts:\n//\n// Shopify templates/product.json lists sections, one of which is `main` —\n// the product form itself. A theme adds sections around `main`\n// without owning it.\n// VTEX IO store.product is a block array containing `product-details`.\n// Same idea, same reason.\n//\n// So the native page body becomes a block (`catalogBody`, `productBody`), and a\n// store composes around it. The body keeps being maintained by the SDK.\n//\n// One deliberate shape change: the general block form nests its props under\n// `props`, matching Shopify's `settings` and VTEX's `props`. The flat\n// StorefrontSection form stays supported — five of seven live stores write it —\n// but it is the outlier, and it is the reason the two stores with custom blocks\n// both had to write `sections: mySections as never`: a closed union cannot\n// describe a block the SDK has never heard of. Nesting fixes that without a cast.\n// ---------------------------------------------------------------------------\n\n/** Pages that are composed from blocks, and the `enabledOn` vocabulary.\n * `content` is the editorial kind: a titled page that is nothing but blocks —\n * no commerce body, no catalog assumptions. */\nexport type StorefrontPageKind = 'home' | 'catalog' | 'product' | 'collection' | 'content'\n\n/**\n * A block instance in a page tree — the general form.\n *\n * `id` matters more than it looks: two product rails on one page are\n * indistinguishable without it, so an external editor cannot target one of them\n * and an AI-generated diff cannot say which it changed. VTEX solved the same\n * problem with `rich-text#hero`; this is the same idea spelled as a field.\n */\nexport interface StorefrontBlockRef {\n /** Registered block id — an SDK section, a plugin block, or an app's own. */\n type: string\n id?: string\n props?: Record<string, unknown>\n children?: readonly StorefrontBlockRef[]\n /**\n * Instance-level data needs, merged over the block meta's `data` declaration\n * by need name — how a document points a block's loader at a route param\n * (`{ products: { loader: 'products.list', params: { category: { $param:\n * 'slug' } } } }`). Resolved once per page by the page-data scope.\n */\n data?: BlockDataNeeds\n}\n\n/**\n * What a page's block list accepts.\n *\n * The typed union keeps checking SDK sections (a bad `variant` is still a type\n * error); the open ref form is what lets an app place its own registered block\n * without casting.\n */\nexport type StorefrontBlockLike = StorefrontSection | StorefrontBlockRef\n\nexport interface StorefrontPageConfig {\n blocks: readonly StorefrontBlockLike[]\n}\n\n/**\n * An editorial page, expressible entirely in the document: a slug, a title, a\n * route, and blocks. This is the page kind that absorbs the \"brand site half\"\n * of a store — biography, philosophy, press, collections — which today lives\n * as hand-written custom routes (cristinarotondaro carries ~240 lines of them).\n *\n * The store chrome (header, footer, cart) still frames the page; what a\n * content page does NOT get is a commerce body block or any catalog\n * assumption. It is blocks all the way down.\n */\nexport interface StorefrontContentPageConfig extends StorefrontPageConfig {\n /** Document/page title (`<title>` gets \"<title> — <store>\"). Defaults to the slug. */\n title?: string\n /**\n * Route path. Defaults to `/<slug>`. May declare params (`/categoria/:slug`)\n * — one page then serves every match, and its blocks read the params through\n * `$param` loader bindings (M3). validateStore checks that every binding\n * names a param the path declares.\n */\n path?: string\n /**\n * Theme overlay applied ONLY inside this page, merged over the store theme.\n *\n * This is what makes a campaign page — a licensed line, a seasonal drop, a\n * collab — expressible as a content page instead of a microsite. Stores\n * routinely build those elsewhere and lose the funnel with them: the \"buy\"\n * CTA leaves for another domain and the cart, the session and the\n * attribution go with it.\n *\n * The overlay is emitted scoped to `[data-page=\"<slug>\"]`, so a hot-pink\n * summer drop cannot repaint the header, the cart drawer or any other route.\n * It merges key by key, so a page that sets only `colors.primary` keeps the\n * store's typography and every other token.\n */\n theme?: Partial<StorefrontTheme>\n}\n\n/**\n * Merge a page's theme overlay over the store theme.\n *\n * Nested groups merge rather than replace: a page declaring one colour must not\n * silently drop the store's fonts, radii and brand tokens back to SDK defaults.\n */\nexport function mergePageTheme(\n base: StorefrontTheme | undefined,\n overlay: Partial<StorefrontTheme> | undefined,\n): StorefrontTheme | undefined {\n if (!base && !overlay) return undefined\n if (!overlay) return base\n const name = overlay.name ?? (base ? `${base.name}-page` : 'page')\n const font = base?.font ? { ...base.font, ...overlay.font } : (overlay.font as StorefrontTheme['font'])\n return {\n ...base,\n ...overlay,\n name,\n colors: { ...base?.colors, ...overlay.colors },\n font,\n tokens: { ...base?.tokens, ...overlay.tokens },\n header: { ...base?.header, ...overlay.header },\n productCard: { ...base?.productCard, ...overlay.productCard },\n }\n}\n\n/** The wrapper attribute + selector a page's scoped theme is emitted under. */\nexport function pageThemeSelector(slug: string): string {\n return `[data-page=\"${slug}\"]`\n}\n\n/** The params a route path pattern declares ('/categoria/:slug' → ['slug']). */\nexport function routePathParams(path: string): string[] {\n return path\n .split('/')\n .filter((segment) => segment.startsWith(':'))\n .map((segment) => segment.slice(1))\n}\n\nexport interface StorefrontPagesConfig {\n home?: StorefrontPageConfig\n catalog?: StorefrontPageConfig\n product?: StorefrontPageConfig\n collection?: StorefrontPageConfig\n /** Editorial pages, keyed by slug. */\n content?: Record<string, StorefrontContentPageConfig>\n}\n\n/** Route path of one content page (explicit `path`, or `/<slug>`). */\nexport function contentPagePath(slug: string, page: StorefrontContentPageConfig): string {\n return page.path ?? `/${slug}`\n}\n\nexport interface ContentPageMatch {\n slug: string\n page: StorefrontContentPageConfig\n /** Params extracted from the page's path pattern, when it declares any. */\n params: Record<string, string>\n}\n\n/** The content page a path resolves to, if the store declared one. Paths are\n * patterns (`/categoria/:slug`), so one declared page can serve a family of\n * URLs and hand the matched params to its blocks' `$param` bindings. */\nexport function matchContentPage(\n pages: StorefrontPagesConfig | undefined,\n path: string,\n): ContentPageMatch | null {\n for (const [slug, page] of Object.entries(pages?.content ?? {})) {\n const params = matchPath(contentPagePath(slug, page), path)\n if (params) return { slug, page, params }\n }\n return null\n}\n\n/** Block ids for the native page bodies — Shopify's `main` section, by another name. */\nexport const CATALOG_BODY_BLOCK = 'catalogBody'\nexport const PRODUCT_BODY_BLOCK = 'productBody'\n\n/** The block that renders the page's own reason for existing, per page kind. */\nconst BODY_BLOCK_BY_KIND: Partial<Record<StorefrontPageKind, string>> = {\n catalog: CATALOG_BODY_BLOCK,\n product: PRODUCT_BODY_BLOCK,\n}\n\n/**\n * Keys that are block identity rather than props, in the nested form.\n *\n * Exported for the same reason `blockInstanceId` is: it must be ONE rule.\n * Whoever asks \"where do this block's settings live\" — the renderer here, the\n * editor via `blockAtPath`, the overlay's settings/structure guard — has to get\n * the same answer, or an edit an editor accepts is one the renderer ignores.\n */\nexport const BLOCK_STRUCTURAL_KEYS: ReadonlySet<string> = new Set([\n 'type',\n 'id',\n 'props',\n 'children',\n 'data',\n])\n\nconst STRUCTURAL_KEYS = BLOCK_STRUCTURAL_KEYS\n\n/**\n * Does this entry keep its settings under `props` (nested form) or inline on\n * the entry itself (flat form)?\n *\n * Both forms are first-class and they MIX inside one array — artorious's home\n * page is 3 nested and 5 flat — so nothing may assume the shape from the path\n * it took to get here.\n */\nexport function isNestedBlockRef(entry: unknown): boolean {\n return isNestedRef(entry as StorefrontBlockLike)\n}\n\nfunction isNestedRef(entry: StorefrontBlockLike): boolean {\n const record = entry as Record<string, unknown>\n // An explicit `props` settles it. Otherwise: an entry carrying nothing but\n // structural keys is a ref with no props (`{ type: 'seals' }`), which the flat\n // reading would also produce — so either branch is correct for that case.\n if ('props' in record) return true\n return Object.keys(record).every((key) => STRUCTURAL_KEYS.has(key))\n}\n\n/**\n * The id a block instance is known by: the authored `id`, or `<type>-<index>`\n * when it has none.\n *\n * Exported because it must be ONE rule. A block with no authored id still\n * reaches the outside world under its synthesized id — `renderStore` reports\n * it, an editor targets it, a generated diff names it — so `applyChange` has\n * to resolve `blocks[#banner-0]` to the same entry the renderer called\n * `banner-0`. Two implementations of this rule would make the id an agent is\n * handed by one ability unusable by another.\n */\nexport function blockInstanceId(entry: unknown, index: number): string {\n const record = entry as Record<string, unknown> | null\n if (record && typeof record.id === 'string' && record.id) return record.id\n return `${String(record?.type)}-${index}`\n}\n\n/**\n * Normalize one authored entry into a `BlockNode`.\n *\n * Both authoring forms collapse to the same node, so everything downstream —\n * the renderer, the editor, `describeBlocks` — sees one shape.\n */\nfunction toBlockNode(entry: StorefrontBlockLike, index: number): BlockNode {\n const record = entry as Record<string, unknown>\n const type = String(record.type)\n const id = blockInstanceId(entry, index)\n\n const data = record.data as BlockDataNeeds | undefined\n\n if (isNestedRef(entry)) {\n const children = (record.children as readonly StorefrontBlockRef[] | undefined) ?? []\n return {\n type,\n id,\n props: (record.props as Record<string, unknown> | undefined) ?? {},\n ...(children.length ? { children: children.map((child, i) => toBlockNode(child, i)) } : {}),\n ...(data ? { data } : {}),\n }\n }\n\n // Flat form: everything that is not identity is a prop.\n const props: Record<string, unknown> = {}\n for (const [key, value] of Object.entries(record)) {\n if (!STRUCTURAL_KEYS.has(key)) props[key] = value\n }\n return { type, id, props, ...(data ? { data } : {}) }\n}\n\n/** Normalize an authored block list into the block tree the renderer consumes. */\nexport function toBlockNodes(blocks: readonly StorefrontBlockLike[]): BlockNode[] {\n return blocks.map(toBlockNode)\n}\n\n/**\n * The default tree for a page kind — what the SDK renders when a store has not\n * composed the page. For catalog and product that is the native body alone,\n * which is why adopting `pages` is a no-op until a store adds something.\n */\nexport function defaultPageBlocks(kind: StorefrontPageKind): BlockNode[] {\n const body = BODY_BLOCK_BY_KIND[kind]\n return body ? [{ type: body, id: body, props: {} }] : []\n}\n\nexport interface PageBlockDiagnostic {\n level: 'error' | 'warning'\n rule: string\n detail: string\n}\n\n/**\n * Diagnostics for one composed page.\n *\n * These are reported, not enforced. A store in production must not go blank\n * because a block was placed on a page its author did not anticipate — so the\n * hard stop belongs in the editor (which reads the same `enabledOn` through\n * `describeBlocks`), and this is the development-time equivalent.\n */\nexport function checkPageBlocks(kind: StorefrontPageKind, nodes: readonly BlockNode[]): PageBlockDiagnostic[] {\n const found: PageBlockDiagnostic[] = []\n const seenIds = new Set<string>()\n const expectedBody = BODY_BLOCK_BY_KIND[kind]\n let hasBody = false\n\n for (const node of nodes) {\n if (node.type === expectedBody) hasBody = true\n\n if (node.id) {\n if (seenIds.has(node.id)) {\n found.push({\n level: 'warning',\n rule: 'duplicate-block-id',\n detail: `two blocks on the ${kind} page share the id \"${node.id}\" — an editor or a generated diff cannot tell them apart`,\n })\n }\n seenIds.add(node.id)\n }\n\n const meta = getBlockMeta(node.type)\n if (!meta) {\n found.push({\n level: 'error',\n rule: 'unknown-block',\n detail: `the ${kind} page references block \"${node.type}\", which is not registered — register it before the storefront mounts`,\n })\n continue\n }\n if (!isBlockEnabledOn({ enabledOn: meta.enabledOn ?? [] }, kind)) {\n found.push({\n level: 'warning',\n rule: 'block-not-enabled-on-page',\n detail: `block \"${node.type}\" declares enabledOn [${(meta.enabledOn ?? []).join(', ')}] and is placed on the ${kind} page`,\n })\n }\n }\n\n if (expectedBody && !hasBody) {\n // Shopify hard-requires the `main` section in a template for this reason. We\n // warn instead of inserting it, because silently adding a block the config\n // does not mention makes the config a lie — and a fully bespoke catalog is a\n // legitimate thing to build.\n found.push({\n level: 'warning',\n rule: 'missing-page-body',\n detail: `the ${kind} page tree omits \"${expectedBody}\", so the page renders no ${kind === 'product' ? 'product detail' : 'product list'} — add it back unless the replacement is deliberate`,\n })\n }\n\n // Composition constraints, for pages composed in code: a document goes\n // through validateStore, but a TS config never does, so the same structural\n // rules are reported here. Errors, because the renderer will keep these\n // blocks off the shopper's page (I7) — the composition already lies.\n for (const violation of checkBlockConstraints(nodes)) {\n found.push({\n level: 'error',\n rule: violation.rule,\n detail: `${kind} page ${violation.path}: ${violation.detail}`,\n })\n }\n\n // Context is advisory: the key may arrive through a runtime channel a static\n // tree cannot see (a slot area, a future loader), so the honest verdict is\n // \"nothing declares this\", not \"this is broken\".\n for (const finding of checkBlockContext(nodes)) {\n found.push({\n level: 'warning',\n rule: 'context-not-provided',\n detail: `block \"${finding.type}\" at ${kind} page ${finding.path} uses context [${finding.missing.join(', ')}] that no ancestor provides`,\n })\n }\n\n return found\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/blocks/page-data.tsx","../src/blocks/page-blocks.ts"],"names":["entry","getBlockMeta"],"mappings":"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","file":"chunk-SLJ4BRXD.js","sourcesContent":["import { errorMessage } from '@fayz-ai/core'\nimport React, { createContext, useContext, useEffect, useMemo, useState } from 'react'\nimport {\n dataNeedKey,\n defaultPropsFromSettings,\n getBlockErrorVisibility,\n getBlockMeta,\n isLoaderParamRef,\n isLoaderPropRef,\n resolveLoader,\n} from '@fayz-ai/core'\nimport type { BlockDataNeed, BlockNode } from '@fayz-ai/core'\nimport { getShopProvider } from '@fayz-ai/shop/runtime'\nimport { currentStaticRenderPass } from '../app/static-render'\n\n// ---------------------------------------------------------------------------\n// Page data — declared needs, resolved once per page (spine M3, I11).\n//\n// A block never fetches for itself. Its meta (or its document instance)\n// declares named needs against registered loaders; the page walks its repaired\n// tree, collects them, DEDUPES identical (loader, params) pairs, resolves them\n// concurrently through the ShopProvider port, and hands the results down by\n// context. Two product rails asking the same question cost one provider call —\n// and, because the needs are known before render, a server can resolve the\n// same set ahead of time, which is the SSR prerequisite L4 calls out.\n//\n// Failure semantics follow the M2 error-boundary contract (I7): one failing\n// loader marks ONE entry as errored and never touches the rest of the page.\n// A block whose data errored renders null for shoppers and an error card in\n// dev/admin (BlockDataError), exactly like a block that crashed.\n// ---------------------------------------------------------------------------\n\nexport type PageDataStatus = 'idle' | 'loading' | 'ready' | 'error'\n\nexport interface PageDataEntry {\n status: 'loading' | 'ready' | 'error'\n data?: unknown\n error?: string\n}\n\n/** One collected, binding-resolved, deduped need. */\nexport interface PageDataNeed {\n key: string\n loader: string\n params: Record<string, unknown>\n}\n\n// --- Binding resolution -------------------------------------------------------\n\n/**\n * Resolve a declared need's param bindings against the page's route params and\n * the declaring block's props (setting defaults filled in, so the declaration\n * and the rendered block agree on unset props). A binding that resolves to\n * `undefined` drops its param — dataNeedKey drops undefined anyway, so the\n * declared need and a hook asking with explicit `undefined`s share a key.\n */\nexport function resolveDataParams(\n need: BlockDataNeed,\n context: { params: Record<string, string>; props: Record<string, unknown> },\n): Record<string, unknown> {\n const out: Record<string, unknown> = {}\n for (const [name, value] of Object.entries(need.params ?? {})) {\n let resolved: unknown = value\n if (isLoaderParamRef(value)) resolved = context.params[value.$param]\n else if (isLoaderPropRef(value)) resolved = context.props[value.$prop]\n if (resolved !== undefined) out[name] = resolved\n }\n return out\n}\n\n/** A block's effective needs: the meta declaration with the document\n * instance's `data` merged over it by need name. */\nfunction needsForNode(node: BlockNode): Record<string, BlockDataNeed> {\n const meta = getBlockMeta(node.type)\n return { ...meta?.data, ...node.data }\n}\n\n/**\n * Walk a block tree (children included) and collect every declared need,\n * bindings resolved, deduped by canonical (loader, params) key.\n */\nexport function collectDataNeeds(\n nodes: readonly BlockNode[],\n routeParams: Record<string, string> = {},\n): PageDataNeed[] {\n const found = new Map<string, PageDataNeed>()\n const visit = (node: BlockNode): void => {\n const meta = getBlockMeta(node.type)\n const needs = needsForNode(node)\n if (Object.keys(needs).length > 0) {\n const props = { ...defaultPropsFromSettings(meta?.settings), ...node.props }\n for (const need of Object.values(needs)) {\n if (!need || typeof need.loader !== 'string' || !need.loader) continue\n const params = resolveDataParams(need, { params: routeParams, props })\n const key = dataNeedKey(need.loader, params)\n if (!found.has(key)) found.set(key, { key, loader: need.loader, params })\n }\n }\n for (const child of node.children ?? []) visit(child)\n }\n for (const node of nodes) visit(node)\n return [...found.values()]\n}\n\n// --- Resolution ---------------------------------------------------------------\n\n/** Resolve ONE need through the provider port. Never throws: an unknown\n * loader or a failing read becomes an 'error' entry for that need alone. */\nexport async function resolvePageDataNeed(need: Pick<PageDataNeed, 'loader' | 'params'>): Promise<PageDataEntry> {\n try {\n const impl = resolveLoader(need.loader)\n const data = await impl(need.params, { shop: getShopProvider() })\n return { status: 'ready', data }\n } catch (err) {\n const message = errorMessage(err)\n // Always log — a store hitting this in production is better off with the\n // reason in the console than without it (same policy as BlockErrorBoundary).\n console.error(`[storefront] loader \"${need.loader}\" failed:`, err)\n return { status: 'error', error: message }\n }\n}\n\n/** Resolve a set of needs concurrently, isolated per need. Test seam and the\n * future server-side entry point — the scope component uses the same calls. */\nexport async function resolvePageData(\n needs: readonly PageDataNeed[],\n): Promise<Record<string, PageDataEntry>> {\n const settled = await Promise.all(\n needs.map(async (need) => [need.key, await resolvePageDataNeed(need)] as const),\n )\n return Object.fromEntries(settled)\n}\n\n// --- The scope ----------------------------------------------------------------\n\ninterface PageDataContextValue {\n entries: Record<string, PageDataEntry>\n /** The route params this scope resolved `$param` bindings against — the same\n * ones a consuming block must use, or its key would diverge. */\n params: Record<string, string>\n}\n\nconst PageDataContext = createContext<PageDataContextValue | null>(null)\n\nexport interface PageDataScopeProps {\n nodes: readonly BlockNode[]\n /** Route params the tree's `$param` bindings resolve against. */\n params?: Record<string, string>\n /**\n * Pre-resolved entries, keyed by dataNeedKey — the server-side/static-render\n * seam (L4): `resolvePageData(collectDataNeeds(nodes))` ahead of render,\n * then hand the results in. Keys already 'ready' here are never re-fetched;\n * anything missing resolves client-side as usual.\n *\n * Live, not mount-only: a host whose shell outlives the page (every real\n * one — the shell must not remount on navigation) hands in a NEW object\n * for each page it loads, and a child scope mounting in that same commit\n * sees it. Pass a stable reference per page (the loader's result), not a\n * literal built in render.\n */\n initial?: Record<string, PageDataEntry>\n children: React.ReactNode\n}\n\n/**\n * Collects and resolves the data needs of one block tree, and provides the\n * results. Scopes nest (a slot inside a page): a child scope reuses every\n * entry an ancestor already resolved and only fetches its own remainder, so\n * dedupe holds across the whole rendered page, not per tree.\n */\nexport function PageDataScope({ nodes, params, initial, children }: PageDataScopeProps) {\n const parent = useContext(PageDataContext)\n const routeParams = useMemo(() => params ?? parent?.params ?? {}, [params, parent])\n const needs = useMemo(() => collectDataNeeds(nodes, routeParams), [nodes, routeParams])\n const own = useMemo(\n () => needs.filter((need) => !parent || !(need.key in parent.entries)),\n [needs, parent],\n )\n // A static render (renderStore) has no effects, so this scope reports what it\n // renders and what it would fetch, and reads back anything an earlier pass\n // already resolved. Null in the browser.\n const staticPass = currentStaticRenderPass()\n if (staticPass) {\n staticPass.tree(nodes)\n staticPass.needs(own)\n }\n\n // Seeded synchronously so the first render already shows 'loading' (or the\n // pre-resolved entry) for every owned key — a consuming block must never see\n // a gap and start its own fetch.\n const [entries, setEntries] = useState<Record<string, PageDataEntry>>(() => ({\n // Everything handed in is published, not only the needs THIS scope\n // collected. That is what lets a host framework resolve a page's data in\n // its own loader and wrap the shell in a seeding scope: the page's own\n // scope then finds those keys already answered on its parent and fetches\n // nothing. Without it, seeding only works when the seeding scope happens to\n // render the same block tree.\n ...initial,\n ...Object.fromEntries(\n own.map((need) => [\n need.key,\n initial?.[need.key] ?? staticPass?.entries[need.key] ?? ({ status: 'loading' } as PageDataEntry),\n ]),\n ),\n }))\n\n const signature = own.map((need) => need.key).join('\\n')\n useEffect(() => {\n // A key handed in ready stays authoritative; everything else resolves here.\n const pending = own.filter((need) => initial?.[need.key]?.status !== 'ready')\n if (pending.length === 0) return\n let cancelled = false\n setEntries((prev) => {\n const next = { ...prev }\n for (const need of pending) {\n if (next[need.key]?.status !== 'ready') next[need.key] = { status: 'loading' }\n }\n return next\n })\n for (const need of pending) {\n void resolvePageDataNeed(need).then((entry) => {\n if (!cancelled) setEntries((prev) => ({ ...prev, [need.key]: entry }))\n })\n }\n return () => {\n cancelled = true\n }\n // `own` is captured by its key signature: same keys = same needs.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [signature])\n\n // `initial` is overlaid at RENDER time, on top of what this scope resolved,\n // rather than folded into state once at mount. The difference is one\n // commit, and it is the whole point: after a client-side navigation the\n // host hands in the next page's entries and the next page's own scope\n // mounts in the same commit — it decides which needs are unanswered by\n // reading this value as it mounts. State would only carry the new entries\n // from the following commit, by which time the child has already started\n // fetching everything the host had just resolved, and the shopper watches\n // content that was ready pop in a second time.\n const value = useMemo<PageDataContextValue>(\n () => ({ entries: { ...parent?.entries, ...entries, ...initial }, params: routeParams }),\n [parent, entries, initial, routeParams],\n )\n return <PageDataContext.Provider value={value}>{children}</PageDataContext.Provider>\n}\n\n// --- Consumption --------------------------------------------------------------\n\nexport interface UseLoaderResult<T> {\n status: PageDataStatus\n data: T | null\n error: string | null\n /** Re-run the read (bypasses the page-resolved entry from then on). */\n reload: () => void\n}\n\n/**\n * A block's (or page's) read: `useLoader('products.list', { filter, limit })`.\n *\n * When a PageDataScope above already resolved the same (loader, params) pair —\n * because this block, or ANY block on the page, declared it — the resolved\n * entry is returned and no request is made. Otherwise the hook resolves the\n * loader itself, through the same provider port, so a component used outside a\n * composed page (a store's hand-written route) keeps working identically.\n * Pass `null` params to disable the read.\n */\nexport function useLoader<T>(\n loader: string,\n params: Record<string, unknown> | null,\n): UseLoaderResult<T> {\n const scope = useContext(PageDataContext)\n const key = params === null ? null : dataNeedKey(loader, params)\n const [nonce, setNonce] = useState(0)\n const scoped = nonce === 0 && key !== null && scope ? scope.entries[key] : undefined\n const [local, setLocal] = useState<{ key: string; entry: PageDataEntry } | null>(null)\n\n // Undeclared reads — a hand-written route calling the hook directly — still\n // resolve under a static render: the pass collects the key on the first pass\n // and serves it on the next. Without this, exactly the reads that no block\n // declared would be the ones missing from a headless render.\n const staticPass = currentStaticRenderPass()\n const staticEntry = staticPass && key !== null && scoped === undefined ? staticPass.entries[key] : undefined\n if (staticPass && key !== null && scoped === undefined && staticEntry === undefined) {\n staticPass.needs([{ key, loader, params: params! }])\n }\n\n const paramsKey = params === null ? null : JSON.stringify(params)\n useEffect(() => {\n if (key === null || scoped !== undefined) return\n let cancelled = false\n setLocal({ key, entry: { status: 'loading' } })\n void resolvePageDataNeed({ loader, params: params! }).then((entry) => {\n if (!cancelled) setLocal({ key, entry })\n })\n return () => {\n cancelled = true\n }\n // params identity travels through paramsKey; scoped presence through a boolean.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [loader, paramsKey, nonce, scoped !== undefined])\n\n const entry =\n scoped ??\n staticEntry ??\n (local && local.key === key ? local.entry : key !== null ? { status: 'loading' as const } : null)\n\n return {\n status: entry?.status ?? 'idle',\n data: entry?.status === 'ready' ? (entry.data as T) : null,\n error: entry?.status === 'error' ? entry.error ?? 'unknown error' : null,\n reload: () => setNonce((n) => n + 1),\n }\n}\n\n/**\n * A BLOCK's named data need: `useBlockData(node, 'products', fallback)`.\n *\n * Reads the block's effective declaration — meta `data` with the document\n * instance's `data` merged over it — and resolves its bindings with the SAME\n * inputs the page resolver used (scope route params, node props over setting\n * defaults), so the two always compute the same key. That is what makes a\n * document able to rebind a param (`category: { $param: 'slug' }`) and have\n * the rendered block follow: the block asks for its declared need, not for a\n * hardcoded one.\n *\n * `fallback` covers the component being used directly as JSX (a store's\n * hand-written route), where there is no node and no declaration to read.\n */\nexport function useBlockData<T>(\n node: BlockNode | undefined,\n name: string,\n fallback: { loader: string; params: Record<string, unknown> },\n): UseLoaderResult<T> & { loader: string } {\n const scope = useContext(PageDataContext)\n let loader = fallback.loader\n let params = fallback.params\n if (node) {\n const need = needsForNode(node)[name]\n if (need && typeof need.loader === 'string' && need.loader) {\n const meta = getBlockMeta(node.type)\n loader = need.loader\n params = resolveDataParams(need, {\n params: scope?.params ?? {},\n props: { ...defaultPropsFromSettings(meta?.settings), ...node.props },\n })\n }\n }\n const result = useLoader<T>(loader, params)\n return { ...result, loader }\n}\n\n// --- Error surface ------------------------------------------------------------\n\n/**\n * What a block renders when its declared data failed: nothing for shoppers, a\n * visible card for builders — the same visibility rule as a crashed block\n * (M2's per-block error boundary, WooCommerce's `CURRENT_USER_IS_ADMIN ?\n * onError : () => null`).\n */\nexport function BlockDataError({ loader, message }: { loader: string; message?: string | null }) {\n if (getBlockErrorVisibility() === 'silent') return null\n return (\n <div\n data-block-error={`loader:${loader}`}\n style={{\n border: '1px dashed #dc2626',\n color: '#b91c1c',\n padding: '8px 12px',\n font: '12px ui-monospace, monospace',\n borderRadius: 6,\n }}\n >\n {`Data for this block failed (loader \"${loader}\")`}\n {message ? <div style={{ opacity: 0.8, marginTop: 4 }}>{message}</div> : null}\n </div>\n )\n}\n","import { checkBlockConstraints, checkBlockContext, getBlockMeta, isBlockEnabledOn } from '@fayz-ai/core'\nimport type { BlockDataNeeds, BlockNode } from '@fayz-ai/core'\nimport type { StorefrontSection } from './sections'\nimport type { StorefrontTheme } from '../lib/theme'\nimport { matchPath } from '../app/router'\n\n// ---------------------------------------------------------------------------\n// Pages as block trees.\n//\n// The home page has been a block tree since the registry landed. Every other\n// route was hand-written JSX, which is why a store that wanted a banner above\n// the catalog grid had only one move available: reimplement the catalog page.\n// One store did exactly that for three routes and stopped receiving SDK updates\n// for all three.\n//\n// What the benchmarks do, and what this adopts:\n//\n// Shopify templates/product.json lists sections, one of which is `main` —\n// the product form itself. A theme adds sections around `main`\n// without owning it.\n// VTEX IO store.product is a block array containing `product-details`.\n// Same idea, same reason.\n//\n// So the native page body becomes a block (`catalogBody`, `productBody`), and a\n// store composes around it. The body keeps being maintained by the SDK.\n//\n// One deliberate shape change: the general block form nests its props under\n// `props`, matching Shopify's `settings` and VTEX's `props`. The flat\n// StorefrontSection form stays supported — five of seven live stores write it —\n// but it is the outlier, and it is the reason the two stores with custom blocks\n// both had to write `sections: mySections as never`: a closed union cannot\n// describe a block the SDK has never heard of. Nesting fixes that without a cast.\n// ---------------------------------------------------------------------------\n\n/** Pages that are composed from blocks, and the `enabledOn` vocabulary.\n * `content` is the editorial kind: a titled page that is nothing but blocks —\n * no commerce body, no catalog assumptions. */\nexport type StorefrontPageKind = 'home' | 'catalog' | 'product' | 'collection' | 'content'\n\n/**\n * A block instance in a page tree — the general form.\n *\n * `id` matters more than it looks: two product rails on one page are\n * indistinguishable without it, so an external editor cannot target one of them\n * and an AI-generated diff cannot say which it changed. VTEX solved the same\n * problem with `rich-text#hero`; this is the same idea spelled as a field.\n */\nexport interface StorefrontBlockRef {\n /** Registered block id — an SDK section, a plugin block, or an app's own. */\n type: string\n id?: string\n props?: Record<string, unknown>\n children?: readonly StorefrontBlockRef[]\n /**\n * Instance-level data needs, merged over the block meta's `data` declaration\n * by need name — how a document points a block's loader at a route param\n * (`{ products: { loader: 'products.list', params: { category: { $param:\n * 'slug' } } } }`). Resolved once per page by the page-data scope.\n */\n data?: BlockDataNeeds\n}\n\n/**\n * What a page's block list accepts.\n *\n * The typed union keeps checking SDK sections (a bad `variant` is still a type\n * error); the open ref form is what lets an app place its own registered block\n * without casting.\n */\nexport type StorefrontBlockLike = StorefrontSection | StorefrontBlockRef\n\nexport interface StorefrontPageConfig {\n blocks: readonly StorefrontBlockLike[]\n}\n\n/**\n * An editorial page, expressible entirely in the document: a slug, a title, a\n * route, and blocks. This is the page kind that absorbs the \"brand site half\"\n * of a store — biography, philosophy, press, collections — which today lives\n * as hand-written custom routes (cristinarotondaro carries ~240 lines of them).\n *\n * The store chrome (header, footer, cart) still frames the page; what a\n * content page does NOT get is a commerce body block or any catalog\n * assumption. It is blocks all the way down.\n */\nexport interface StorefrontContentPageConfig extends StorefrontPageConfig {\n /** Document/page title (`<title>` gets \"<title> — <store>\"). Defaults to the slug. */\n title?: string\n /**\n * Route path. Defaults to `/<slug>`. May declare params (`/categoria/:slug`)\n * — one page then serves every match, and its blocks read the params through\n * `$param` loader bindings (M3). validateStore checks that every binding\n * names a param the path declares.\n */\n path?: string\n /**\n * Theme overlay applied ONLY inside this page, merged over the store theme.\n *\n * This is what makes a campaign page — a licensed line, a seasonal drop, a\n * collab — expressible as a content page instead of a microsite. Stores\n * routinely build those elsewhere and lose the funnel with them: the \"buy\"\n * CTA leaves for another domain and the cart, the session and the\n * attribution go with it.\n *\n * The overlay is emitted scoped to `[data-page=\"<slug>\"]`, so a hot-pink\n * summer drop cannot repaint the header, the cart drawer or any other route.\n * It merges key by key, so a page that sets only `colors.primary` keeps the\n * store's typography and every other token.\n */\n theme?: Partial<StorefrontTheme>\n}\n\n/**\n * Merge a page's theme overlay over the store theme.\n *\n * Nested groups merge rather than replace: a page declaring one colour must not\n * silently drop the store's fonts, radii and brand tokens back to SDK defaults.\n */\nexport function mergePageTheme(\n base: StorefrontTheme | undefined,\n overlay: Partial<StorefrontTheme> | undefined,\n): StorefrontTheme | undefined {\n if (!base && !overlay) return undefined\n if (!overlay) return base\n const name = overlay.name ?? (base ? `${base.name}-page` : 'page')\n const font = base?.font ? { ...base.font, ...overlay.font } : (overlay.font as StorefrontTheme['font'])\n return {\n ...base,\n ...overlay,\n name,\n colors: { ...base?.colors, ...overlay.colors },\n font,\n tokens: { ...base?.tokens, ...overlay.tokens },\n header: { ...base?.header, ...overlay.header },\n productCard: { ...base?.productCard, ...overlay.productCard },\n }\n}\n\n/** The wrapper attribute + selector a page's scoped theme is emitted under. */\nexport function pageThemeSelector(slug: string): string {\n return `[data-page=\"${slug}\"]`\n}\n\n/** The params a route path pattern declares ('/categoria/:slug' → ['slug']). */\nexport function routePathParams(path: string): string[] {\n return path\n .split('/')\n .filter((segment) => segment.startsWith(':'))\n .map((segment) => segment.slice(1))\n}\n\nexport interface StorefrontPagesConfig {\n home?: StorefrontPageConfig\n catalog?: StorefrontPageConfig\n product?: StorefrontPageConfig\n collection?: StorefrontPageConfig\n /** Editorial pages, keyed by slug. */\n content?: Record<string, StorefrontContentPageConfig>\n}\n\n/** Route path of one content page (explicit `path`, or `/<slug>`). */\nexport function contentPagePath(slug: string, page: StorefrontContentPageConfig): string {\n return page.path ?? `/${slug}`\n}\n\nexport interface ContentPageMatch {\n slug: string\n page: StorefrontContentPageConfig\n /** Params extracted from the page's path pattern, when it declares any. */\n params: Record<string, string>\n}\n\n/** The content page a path resolves to, if the store declared one. Paths are\n * patterns (`/categoria/:slug`), so one declared page can serve a family of\n * URLs and hand the matched params to its blocks' `$param` bindings. */\nexport function matchContentPage(\n pages: StorefrontPagesConfig | undefined,\n path: string,\n): ContentPageMatch | null {\n for (const [slug, page] of Object.entries(pages?.content ?? {})) {\n const params = matchPath(contentPagePath(slug, page), path)\n if (params) return { slug, page, params }\n }\n return null\n}\n\n/** Block ids for the native page bodies — Shopify's `main` section, by another name. */\nexport const CATALOG_BODY_BLOCK = 'catalogBody'\nexport const PRODUCT_BODY_BLOCK = 'productBody'\n\n/** The block that renders the page's own reason for existing, per page kind. */\nconst BODY_BLOCK_BY_KIND: Partial<Record<StorefrontPageKind, string>> = {\n catalog: CATALOG_BODY_BLOCK,\n product: PRODUCT_BODY_BLOCK,\n}\n\n/**\n * Keys that are block identity rather than props, in the nested form.\n *\n * Exported for the same reason `blockInstanceId` is: it must be ONE rule.\n * Whoever asks \"where do this block's settings live\" — the renderer here, the\n * editor via `blockAtPath`, the overlay's settings/structure guard — has to get\n * the same answer, or an edit an editor accepts is one the renderer ignores.\n */\nexport const BLOCK_STRUCTURAL_KEYS: ReadonlySet<string> = new Set([\n 'type',\n 'id',\n 'props',\n 'children',\n 'data',\n])\n\nconst STRUCTURAL_KEYS = BLOCK_STRUCTURAL_KEYS\n\n/**\n * Does this entry keep its settings under `props` (nested form) or inline on\n * the entry itself (flat form)?\n *\n * Both forms are first-class and they MIX inside one array — artorious's home\n * page is 3 nested and 5 flat — so nothing may assume the shape from the path\n * it took to get here.\n */\nexport function isNestedBlockRef(entry: unknown): boolean {\n return isNestedRef(entry as StorefrontBlockLike)\n}\n\nfunction isNestedRef(entry: StorefrontBlockLike): boolean {\n const record = entry as Record<string, unknown>\n // An explicit `props` settles it. Otherwise: an entry carrying nothing but\n // structural keys is a ref with no props (`{ type: 'seals' }`), which the flat\n // reading would also produce — so either branch is correct for that case.\n if ('props' in record) return true\n return Object.keys(record).every((key) => STRUCTURAL_KEYS.has(key))\n}\n\n/**\n * The id a block instance is known by: the authored `id`, or `<type>-<index>`\n * when it has none.\n *\n * Exported because it must be ONE rule. A block with no authored id still\n * reaches the outside world under its synthesized id — `renderStore` reports\n * it, an editor targets it, a generated diff names it — so `applyChange` has\n * to resolve `blocks[#banner-0]` to the same entry the renderer called\n * `banner-0`. Two implementations of this rule would make the id an agent is\n * handed by one ability unusable by another.\n */\nexport function blockInstanceId(entry: unknown, index: number): string {\n const record = entry as Record<string, unknown> | null\n if (record && typeof record.id === 'string' && record.id) return record.id\n return `${String(record?.type)}-${index}`\n}\n\n/**\n * Normalize one authored entry into a `BlockNode`.\n *\n * Both authoring forms collapse to the same node, so everything downstream —\n * the renderer, the editor, `describeBlocks` — sees one shape.\n */\nfunction toBlockNode(entry: StorefrontBlockLike, index: number): BlockNode {\n const record = entry as Record<string, unknown>\n const type = String(record.type)\n const id = blockInstanceId(entry, index)\n\n const data = record.data as BlockDataNeeds | undefined\n\n if (isNestedRef(entry)) {\n const children = (record.children as readonly StorefrontBlockRef[] | undefined) ?? []\n return {\n type,\n id,\n props: (record.props as Record<string, unknown> | undefined) ?? {},\n ...(children.length ? { children: children.map((child, i) => toBlockNode(child, i)) } : {}),\n ...(data ? { data } : {}),\n }\n }\n\n // Flat form: everything that is not identity is a prop.\n const props: Record<string, unknown> = {}\n for (const [key, value] of Object.entries(record)) {\n if (!STRUCTURAL_KEYS.has(key)) props[key] = value\n }\n return { type, id, props, ...(data ? { data } : {}) }\n}\n\n/** Normalize an authored block list into the block tree the renderer consumes. */\nexport function toBlockNodes(blocks: readonly StorefrontBlockLike[]): BlockNode[] {\n return blocks.map(toBlockNode)\n}\n\n/**\n * The default tree for a page kind — what the SDK renders when a store has not\n * composed the page. For catalog and product that is the native body alone,\n * which is why adopting `pages` is a no-op until a store adds something.\n */\nexport function defaultPageBlocks(kind: StorefrontPageKind): BlockNode[] {\n const body = BODY_BLOCK_BY_KIND[kind]\n return body ? [{ type: body, id: body, props: {} }] : []\n}\n\nexport interface PageBlockDiagnostic {\n level: 'error' | 'warning'\n rule: string\n detail: string\n}\n\n/**\n * Diagnostics for one composed page.\n *\n * These are reported, not enforced. A store in production must not go blank\n * because a block was placed on a page its author did not anticipate — so the\n * hard stop belongs in the editor (which reads the same `enabledOn` through\n * `describeBlocks`), and this is the development-time equivalent.\n */\nexport function checkPageBlocks(kind: StorefrontPageKind, nodes: readonly BlockNode[]): PageBlockDiagnostic[] {\n const found: PageBlockDiagnostic[] = []\n const seenIds = new Set<string>()\n const expectedBody = BODY_BLOCK_BY_KIND[kind]\n let hasBody = false\n\n for (const node of nodes) {\n if (node.type === expectedBody) hasBody = true\n\n if (node.id) {\n if (seenIds.has(node.id)) {\n found.push({\n level: 'warning',\n rule: 'duplicate-block-id',\n detail: `two blocks on the ${kind} page share the id \"${node.id}\" — an editor or a generated diff cannot tell them apart`,\n })\n }\n seenIds.add(node.id)\n }\n\n const meta = getBlockMeta(node.type)\n if (!meta) {\n found.push({\n level: 'error',\n rule: 'unknown-block',\n detail: `the ${kind} page references block \"${node.type}\", which is not registered — register it before the storefront mounts`,\n })\n continue\n }\n if (!isBlockEnabledOn({ enabledOn: meta.enabledOn ?? [] }, kind)) {\n found.push({\n level: 'warning',\n rule: 'block-not-enabled-on-page',\n detail: `block \"${node.type}\" declares enabledOn [${(meta.enabledOn ?? []).join(', ')}] and is placed on the ${kind} page`,\n })\n }\n }\n\n if (expectedBody && !hasBody) {\n // Shopify hard-requires the `main` section in a template for this reason. We\n // warn instead of inserting it, because silently adding a block the config\n // does not mention makes the config a lie — and a fully bespoke catalog is a\n // legitimate thing to build.\n found.push({\n level: 'warning',\n rule: 'missing-page-body',\n detail: `the ${kind} page tree omits \"${expectedBody}\", so the page renders no ${kind === 'product' ? 'product detail' : 'product list'} — add it back unless the replacement is deliberate`,\n })\n }\n\n // Composition constraints, for pages composed in code: a document goes\n // through validateStore, but a TS config never does, so the same structural\n // rules are reported here. Errors, because the renderer will keep these\n // blocks off the shopper's page (I7) — the composition already lies.\n for (const violation of checkBlockConstraints(nodes)) {\n found.push({\n level: 'error',\n rule: violation.rule,\n detail: `${kind} page ${violation.path}: ${violation.detail}`,\n })\n }\n\n // Context is advisory: the key may arrive through a runtime channel a static\n // tree cannot see (a slot area, a future loader), so the honest verdict is\n // \"nothing declares this\", not \"this is broken\".\n for (const finding of checkBlockContext(nodes)) {\n found.push({\n level: 'warning',\n rule: 'context-not-provided',\n detail: `block \"${finding.type}\" at ${kind} page ${finding.path} uses context [${finding.missing.join(', ')}] that no ancestor provides`,\n })\n }\n\n return found\n}\n"]}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { SuccessMark } from './chunk-WDQFNHYO.js';
|
|
2
|
-
import { rememberLocalOrder, useMyOrders } from './chunk-
|
|
2
|
+
import { rememberLocalOrder, useMyOrders } from './chunk-NS6UPIJ3.js';
|
|
3
3
|
import { SkeletonGroup, Skeleton, PulseDot } from './chunk-FCPOPWG3.js';
|
|
4
|
-
import { runBeforePlaceOrderValve, collectOrderMetadata, useT, useBuyerCountry, useDiscountValidator, trackStorefront, identifyStorefrontBuyer, Slot, SignInModal, returnPath, toast } from './chunk-
|
|
4
|
+
import { runBeforePlaceOrderValve, collectOrderMetadata, useT, useBuyerCountry, useDiscountValidator, trackStorefront, identifyStorefrontBuyer, Slot, SignInModal, returnPath, toast } from './chunk-OOQRYH3N.js';
|
|
5
5
|
import { prefersReducedMotion } from './chunk-WXXYJZE6.js';
|
|
6
|
-
import { selectShipping, useCartStore, useDeliveryStore, selectSubtotal, selectDiscountTotal, selectTotal, selectRequiresShipping } from './chunk-
|
|
7
|
-
import { TID, establishCustomerSession, useSessionStore, useStorefrontHead, signOutCustomer, SmoothImage, formatMoney, hasCustomerAuth, relinkCustomer } from './chunk-
|
|
8
|
-
import { useStorefrontConfig, navigateTo, Link } from './chunk-
|
|
6
|
+
import { selectShipping, useCartStore, useDeliveryStore, selectSubtotal, selectDiscountTotal, selectTotal, selectRequiresShipping } from './chunk-C4XATNI2.js';
|
|
7
|
+
import { TID, establishCustomerSession, useSessionStore, useStorefrontHead, signOutCustomer, SmoothImage, formatMoney, hasCustomerAuth, relinkCustomer } from './chunk-EFHQ5MAJ.js';
|
|
8
|
+
import { useStorefrontConfig, navigateTo, Link } from './chunk-PSDR5TZD.js';
|
|
9
9
|
import React4, { useState, useEffect, useRef, useCallback } from 'react';
|
|
10
10
|
import { MapPin, Lock, CreditCard, ShieldCheck, Check, QrCode, Copy } from 'lucide-react';
|
|
11
11
|
import { getShopProvider } from '@fayz-ai/shop/runtime';
|
|
@@ -2119,5 +2119,5 @@ function CheckoutPage() {
|
|
|
2119
2119
|
}
|
|
2120
2120
|
|
|
2121
2121
|
export { AddressForm, CheckoutPage, CountrySelect, DeliverToPicker, placeStorefrontOrder };
|
|
2122
|
-
//# sourceMappingURL=chunk-
|
|
2123
|
-
//# sourceMappingURL=chunk-
|
|
2122
|
+
//# sourceMappingURL=chunk-YCAWAON6.js.map
|
|
2123
|
+
//# sourceMappingURL=chunk-YCAWAON6.js.map
|