@tokenoftrust/storefront-runner 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +58 -0
- package/README.md +40 -0
- package/apps/storefront/.dev.vars.example +41 -0
- package/apps/storefront/.env.example +24 -0
- package/apps/storefront/astro.config.mjs +245 -0
- package/apps/storefront/env.d.ts +155 -0
- package/apps/storefront/lib/ensure-preview-reconcile-secret.sh +31 -0
- package/apps/storefront/lib/resolve-cf-token.sh +48 -0
- package/apps/storefront/lib/resolve-tot-credentials.sh +69 -0
- package/apps/storefront/migrations/0000_baseline.sql +54 -0
- package/apps/storefront/migrations/0001_products_fts.sql +9 -0
- package/apps/storefront/migrations/README.md +54 -0
- package/apps/storefront/migrations/meta/0000_snapshot.json +347 -0
- package/apps/storefront/migrations/meta/0001_snapshot.json +347 -0
- package/apps/storefront/migrations/meta/_journal.json +20 -0
- package/apps/storefront/package.json +41 -0
- package/apps/storefront/public/brand/northwind-logo-dark.svg +4 -0
- package/apps/storefront/public/brand/northwind-logo-light.svg +4 -0
- package/apps/storefront/public/brand/northwind-og.svg +13 -0
- package/apps/storefront/public/favicon.svg +5 -0
- package/apps/storefront/public/js/dashboard-team.js +147 -0
- package/apps/storefront/public/js/dashboard-vendor-search.js +115 -0
- package/apps/storefront/src/components/Badge.astro +34 -0
- package/apps/storefront/src/components/Breadcrumbs.astro +45 -0
- package/apps/storefront/src/components/CollectionCard.astro +56 -0
- package/apps/storefront/src/components/EmptyState.astro +35 -0
- package/apps/storefront/src/components/Img.astro +68 -0
- package/apps/storefront/src/components/ProductCard.astro +228 -0
- package/apps/storefront/src/components/Section.astro +39 -0
- package/apps/storefront/src/components/Seo.astro +70 -0
- package/apps/storefront/src/components/WidgetFrame.astro +34 -0
- package/apps/storefront/src/components/auth/AuthBadge.astro +44 -0
- package/apps/storefront/src/components/chrome/AnnouncementBar.astro +108 -0
- package/apps/storefront/src/components/chrome/MegaMenu.astro +26 -0
- package/apps/storefront/src/components/chrome/NavDropdown.astro +116 -0
- package/apps/storefront/src/components/chrome/SiteFooter.astro +181 -0
- package/apps/storefront/src/components/chrome/SiteHeader.astro +181 -0
- package/apps/storefront/src/components/chrome/UtilityNav.astro +79 -0
- package/apps/storefront/src/components/commerce/FoxyLoader.astro +23 -0
- package/apps/storefront/src/components/commerce/PriceDisplay.astro +91 -0
- package/apps/storefront/src/components/commerce/ProductCarousel.astro +197 -0
- package/apps/storefront/src/components/commerce/ProductGrid.astro +75 -0
- package/apps/storefront/src/components/commerce/RatingStars.astro +58 -0
- package/apps/storefront/src/components/compliance/NicotineWarning.astro +33 -0
- package/apps/storefront/src/components/home/Hero.astro +164 -0
- package/apps/storefront/src/components/islands/AgeGate.tsx +105 -0
- package/apps/storefront/src/components/islands/ImageGallery.tsx +237 -0
- package/apps/storefront/src/components/islands/IsolatedAgeGate.tsx +73 -0
- package/apps/storefront/src/components/islands/MobileNav.tsx +239 -0
- package/apps/storefront/src/components/islands/QuickView.tsx +138 -0
- package/apps/storefront/src/components/islands/RecentlyViewed.tsx +122 -0
- package/apps/storefront/src/components/islands/SavedList.tsx +99 -0
- package/apps/storefront/src/components/islands/SearchTypeahead.tsx +248 -0
- package/apps/storefront/src/components/islands/VariantSelector.tsx +378 -0
- package/apps/storefront/src/components/marketing/CardGrid.astro +72 -0
- package/apps/storefront/src/components/marketing/CodeSample.astro +20 -0
- package/apps/storefront/src/components/marketing/CtaBand.astro +24 -0
- package/apps/storefront/src/components/marketing/Faq.astro +37 -0
- package/apps/storefront/src/components/marketing/Integrations.astro +63 -0
- package/apps/storefront/src/components/marketing/MarketingBlocks.astro +53 -0
- package/apps/storefront/src/components/marketing/MarketingCtas.astro +48 -0
- package/apps/storefront/src/components/marketing/MarketingHero.astro +72 -0
- package/apps/storefront/src/components/marketing/ProofStrip.astro +37 -0
- package/apps/storefront/src/components/marketing/SplitCompare.astro +53 -0
- package/apps/storefront/src/components/marketing/Steps.astro +36 -0
- package/apps/storefront/src/components/marketing/Testimonials.astro +38 -0
- package/apps/storefront/src/components/marketing/TrustBar.astro +22 -0
- package/apps/storefront/src/components/nav/Footer.astro +148 -0
- package/apps/storefront/src/components/nav/Header.astro +190 -0
- package/apps/storefront/src/components/plp/ActiveFilters.astro +79 -0
- package/apps/storefront/src/components/plp/FacetSidebar.astro +146 -0
- package/apps/storefront/src/components/plp/Pagination.astro +99 -0
- package/apps/storefront/src/components/plp/SortSelect.astro +64 -0
- package/apps/storefront/src/config/devTenants.ts +16 -0
- package/apps/storefront/src/config/kvTenantResolver.ts +87 -0
- package/apps/storefront/src/config/resolver.ts +46 -0
- package/apps/storefront/src/config/tenants.ts +193 -0
- package/apps/storefront/src/layouts/Layout.astro +303 -0
- package/apps/storefront/src/lib/analytics/index.ts +47 -0
- package/apps/storefront/src/lib/auth/brokerAssertion.ts +136 -0
- package/apps/storefront/src/lib/auth/devAuth.ts +90 -0
- package/apps/storefront/src/lib/auth/gatePage.ts +81 -0
- package/apps/storefront/src/lib/auth/identityToken.ts +341 -0
- package/apps/storefront/src/lib/auth/loginGate.ts +107 -0
- package/apps/storefront/src/lib/auth/route.ts +146 -0
- package/apps/storefront/src/lib/auth/session.ts +203 -0
- package/apps/storefront/src/lib/auth/totAccessClient.ts +503 -0
- package/apps/storefront/src/lib/basePath.ts +41 -0
- package/apps/storefront/src/lib/cfImage.ts +55 -0
- package/apps/storefront/src/lib/chrome/model.ts +65 -0
- package/apps/storefront/src/lib/colors.ts +97 -0
- package/apps/storefront/src/lib/content-edit/client.ts +230 -0
- package/apps/storefront/src/lib/content-edit/index.ts +14 -0
- package/apps/storefront/src/lib/content-edit/route.ts +25 -0
- package/apps/storefront/src/lib/content-edit/types.ts +19 -0
- package/apps/storefront/src/lib/cspStaticHashes.ts +24 -0
- package/apps/storefront/src/lib/d1/catalog.ts +289 -0
- package/apps/storefront/src/lib/dashboard/staffAdmission.ts +43 -0
- package/apps/storefront/src/lib/dashboard/staffVendorAccess.ts +449 -0
- package/apps/storefront/src/lib/dashboard/tenantSelection.ts +136 -0
- package/apps/storefront/src/lib/dashboard/vendorQuery.ts +61 -0
- package/apps/storefront/src/lib/demoGate.ts +113 -0
- package/apps/storefront/src/lib/dev/ai-widget-client.js +127 -0
- package/apps/storefront/src/lib/dev/cliSignInCode.ts +132 -0
- package/apps/storefront/src/lib/diag/redact.ts +20 -0
- package/apps/storefront/src/lib/edgeCache.ts +128 -0
- package/apps/storefront/src/lib/email/magicLinkInviteEmail.ts +62 -0
- package/apps/storefront/src/lib/env.ts +60 -0
- package/apps/storefront/src/lib/fixtures/sampleProducts.ts +130 -0
- package/apps/storefront/src/lib/format.ts +29 -0
- package/apps/storefront/src/lib/foxyCommerce.ts +49 -0
- package/apps/storefront/src/lib/imageAttrs.ts +55 -0
- package/apps/storefront/src/lib/jsonld.ts +136 -0
- package/apps/storefront/src/lib/maintenance.ts +71 -0
- package/apps/storefront/src/lib/memoryKv.ts +32 -0
- package/apps/storefront/src/lib/messaging/messagesClient.ts +143 -0
- package/apps/storefront/src/lib/placeholder.ts +99 -0
- package/apps/storefront/src/lib/previewReload.ts +125 -0
- package/apps/storefront/src/lib/rawMarketingHtml.ts +133 -0
- package/apps/storefront/src/lib/search/index.ts +74 -0
- package/apps/storefront/src/lib/sections.ts +129 -0
- package/apps/storefront/src/lib/securityHeaders.ts +31 -0
- package/apps/storefront/src/lib/storyblok/content-model.ts +289 -0
- package/apps/storefront/src/lib/storyblok/provider.ts +524 -0
- package/apps/storefront/src/lib/tenantRouting.ts +91 -0
- package/apps/storefront/src/lib/tot/ToTClient.ts +177 -0
- package/apps/storefront/src/lib/tot/d1Client.ts +62 -0
- package/apps/storefront/src/lib/tot/query.ts +187 -0
- package/apps/storefront/src/lib/tot/totClientInterface.ts +30 -0
- package/apps/storefront/src/lib/url.ts +119 -0
- package/apps/storefront/src/lib/wishlist.ts +63 -0
- package/apps/storefront/src/middleware/index.ts +413 -0
- package/apps/storefront/src/pages/404.astro +61 -0
- package/apps/storefront/src/pages/[...slug].astro +90 -0
- package/apps/storefront/src/pages/api/auth/logout.ts +21 -0
- package/apps/storefront/src/pages/api/auth/magic-exchange.ts +107 -0
- package/apps/storefront/src/pages/api/auth/send.ts +85 -0
- package/apps/storefront/src/pages/api/auth/verify.ts +135 -0
- package/apps/storefront/src/pages/api/cache-purge.ts +44 -0
- package/apps/storefront/src/pages/api/content-edit/ai-edit.ts +64 -0
- package/apps/storefront/src/pages/api/csp-report.ts +25 -0
- package/apps/storefront/src/pages/api/dashboard/enter-vendor.ts +124 -0
- package/apps/storefront/src/pages/api/dashboard/vendor-search.ts +120 -0
- package/apps/storefront/src/pages/api/dev/cli-signin-code.ts +44 -0
- package/apps/storefront/src/pages/api/newsletter.ts +104 -0
- package/apps/storefront/src/pages/api/request-access.ts +172 -0
- package/apps/storefront/src/pages/auth/login.astro +149 -0
- package/apps/storefront/src/pages/auth/magic.astro +105 -0
- package/apps/storefront/src/pages/capabilities.astro +292 -0
- package/apps/storefront/src/pages/collections/[handle].astro +154 -0
- package/apps/storefront/src/pages/collections/index.astro +59 -0
- package/apps/storefront/src/pages/dashboard/[appDomain]/index.astro +100 -0
- package/apps/storefront/src/pages/dashboard/[appDomain]/team.astro +154 -0
- package/apps/storefront/src/pages/dashboard/index.astro +160 -0
- package/apps/storefront/src/pages/dev.astro +653 -0
- package/apps/storefront/src/pages/index.astro +290 -0
- package/apps/storefront/src/pages/llms.txt.ts +92 -0
- package/apps/storefront/src/pages/products/[handle].astro +443 -0
- package/apps/storefront/src/pages/robots.txt.ts +24 -0
- package/apps/storefront/src/pages/saved.astro +35 -0
- package/apps/storefront/src/pages/search.astro +150 -0
- package/apps/storefront/src/pages/sitemap.xml.ts +78 -0
- package/apps/storefront/src/pages/style-guide/[tenant]/[theme].astro +646 -0
- package/apps/storefront/src/pages/style-guide/[tenant]/index.astro +85 -0
- package/apps/storefront/src/pages/style-guide/index.astro +94 -0
- package/apps/storefront/src/pages/tenants/[id]/[...path].ts +57 -0
- package/apps/storefront/src/styles/global.css +277 -0
- package/apps/storefront/src/themes/reference.ts +61 -0
- package/apps/storefront/src/themes/schema.ts +111 -0
- package/apps/storefront/src/themes/tenants/index.ts +51 -0
- package/apps/storefront/tsconfig.json +16 -0
- package/apps/storefront/vitest.config.ts +22 -0
- package/apps/storefront/wrangler.toml +175 -0
- package/package.json +23 -0
- package/packages/public-runtime/LICENSE +58 -0
- package/packages/public-runtime/package.json +22 -0
- package/packages/public-runtime/src/binary-path.ts +68 -0
- package/packages/public-runtime/src/capabilities.ts +89 -0
- package/packages/public-runtime/src/catalog-api.ts +81 -0
- package/packages/public-runtime/src/catalog-d1.ts +192 -0
- package/packages/public-runtime/src/csp.ts +109 -0
- package/packages/public-runtime/src/customization-preview.ts +197 -0
- package/packages/public-runtime/src/customization-reconcile.ts +63 -0
- package/packages/public-runtime/src/customization-runtime.ts +157 -0
- package/packages/public-runtime/src/customization-scripts.ts +64 -0
- package/packages/public-runtime/src/customization-versioning.ts +95 -0
- package/packages/public-runtime/src/foxy.ts +184 -0
- package/packages/public-runtime/src/index.ts +30 -0
- package/packages/public-runtime/src/product.ts +159 -0
- package/packages/public-runtime/src/search.ts +45 -0
- package/packages/public-runtime/src/tenant-assets.ts +444 -0
- package/packages/public-runtime/src/tenant.ts +243 -0
- package/packages/public-runtime/tsconfig.json +8 -0
- package/packages/public-runtime/vitest.config.ts +8 -0
- package/pnpm-workspace.yaml +7 -0
- package/scripts/build/copy-tenant-assets.mjs +69 -0
- package/scripts/tot-dev.mjs +439 -0
- package/tenants/bigdvapor/content/blog.json +52 -0
- package/tenants/bigdvapor/content/chrome.html +149 -0
- package/tenants/bigdvapor/content/chrome.json +47 -0
- package/tenants/bigdvapor/content/home.html +463 -0
- package/tenants/bigdvapor/content/home.json +73 -0
- package/tenants/bigdvapor/content/pages/about.json +5 -0
- package/tenants/bigdvapor/content/pages/privacy.json +6 -0
- package/tenants/bigdvapor/content/pages/shipping-returns.json +6 -0
- package/tenants/bigdvapor/content/pages-html/blogs/news.html +86 -0
- package/tenants/bigdvapor/content/pages-html/pages/about-us.html +106 -0
- package/tenants/bigdvapor/content/pages-html/pages/contact-us.html +61 -0
- package/tenants/bigdvapor/content/pages-html/pages/privacy-policy.html +72 -0
- package/tenants/bigdvapor/content/pages-html/pages/shipping-returns.html +172 -0
- package/tenants/bigdvapor/content/themes/bigd-navy.json +109 -0
- package/tenants/bigdvapor/public/img/brands/adjust-vape-disposable.png +0 -0
- package/tenants/bigdvapor/public/img/brands/again-vape-disposable.png +0 -0
- package/tenants/bigdvapor/public/img/brands/al-fakher-vape-disposable.png +0 -0
- package/tenants/bigdvapor/public/img/brands/arro-vape-disposable.png +0 -0
- package/tenants/bigdvapor/public/img/brands/cookies-vape-disposable.png +0 -0
- package/tenants/bigdvapor/public/img/brands/core-vape-disposable.png +0 -0
- package/tenants/bigdvapor/public/img/brands/cyclone-vape-disposable.png +0 -0
- package/tenants/bigdvapor/public/img/brands/dinner-lady-vape-disposable.png +0 -0
- package/tenants/bigdvapor/public/img/brands/extre-bar-vape-disposable.png +0 -0
- package/tenants/bigdvapor/public/img/brands/fifty-bar-vape-disposable.jpg +0 -0
- package/tenants/bigdvapor/public/img/brands/firerose-vape-disposable.jpg +0 -0
- package/tenants/bigdvapor/public/img/brands/flavor-beast-vape-disposable.jpg +0 -0
- package/tenants/bigdvapor/public/img/brands/foger-vape-disposable.png +0 -0
- package/tenants/bigdvapor/public/img/brands/kk-energy-vape-disposable.png +0 -0
- package/tenants/bigdvapor/public/img/brands/kumi-vape-disposable.png +0 -0
- package/tenants/bigdvapor/public/img/brands/lost-vape-dispoable.png +0 -0
- package/tenants/bigdvapor/public/img/brands/melo-labs-vape-disposable.png +0 -0
- package/tenants/bigdvapor/public/img/brands/mixo-vape-disposable.webp +0 -0
- package/tenants/bigdvapor/public/img/brands/one-tank-vape-disposable.jpg +0 -0
- package/tenants/bigdvapor/public/img/brands/oxbar-vape-disposable.webp +0 -0
- package/tenants/bigdvapor/public/img/brands/pillow-talk-vape-disposable.jpg +0 -0
- package/tenants/bigdvapor/public/img/brands/pod-king-vape-disposable.jpg +0 -0
- package/tenants/bigdvapor/public/img/brands/raz-vape-disposable.png +0 -0
- package/tenants/bigdvapor/public/img/hero-texas.jpg +0 -0
- package/tenants/bigdvapor/public/img/og.jpg +0 -0
- package/tenants/bigdvapor/public/img/pages/AdobeStock_202106312_web.jpg +0 -0
- package/tenants/bigdvapor/public/img/pages/CREW_web.jpg +0 -0
- package/tenants/bigdvapor/public/img/pages/Hero-Image.jpg +0 -0
- package/tenants/bigdvapor/public/img/tiles/adjust.png +0 -0
- package/tenants/bigdvapor/public/img/tiles/again.png +0 -0
- package/tenants/bigdvapor/public/img/tiles/al-fakher.png +0 -0
- package/tenants/bigdvapor/public/img/tiles/arro.png +0 -0
- package/tenants/bigdvapor/public/img/tiles/cookies.png +0 -0
- package/tenants/bigdvapor/public/img/tiles/core.png +0 -0
- package/tenants/bigdvapor/public/img/tiles/cyclone.png +0 -0
- package/tenants/bigdvapor/public/img/tiles/disposables.svg +23 -0
- package/tenants/bigdvapor/public/logo-wordmark.png +0 -0
- package/tenants/bigdvapor/public/pages/home.css +118 -0
- package/tenants/bigdvapor/public/pages/mkt.css +185 -0
- package/tenants/bigdvapor/public/pages/page.css +148 -0
- package/tenants/bigdvapor/public/themes/bigd-navy.css +73 -0
- package/tenants/bigdvapor/theme.json +12 -0
- package/tenants/giantvapes/content/chrome.html +152 -0
- package/tenants/giantvapes/content/chrome.json +94 -0
- package/tenants/giantvapes/content/home.html +194 -0
- package/tenants/giantvapes/content/home.json +50 -0
- package/tenants/giantvapes/content/pages/about.json +10 -0
- package/tenants/giantvapes/content/pages/privacy.json +6 -0
- package/tenants/giantvapes/content/pages/shipping-returns.json +6 -0
- package/tenants/giantvapes/content/pages-html/blogs/news.html +68 -0
- package/tenants/giantvapes/content/pages-html/pages/about-us.html +95 -0
- package/tenants/giantvapes/content/pages-html/pages/contact-us.html +68 -0
- package/tenants/giantvapes/content/pages-html/pages/privacy-policy.html +65 -0
- package/tenants/giantvapes/content/pages-html/pages/shipping-returns.html +77 -0
- package/tenants/giantvapes/content/themes/giant-navy.json +73 -0
- package/tenants/giantvapes/public/img/hero-suicide-bunny.jpg +0 -0
- package/tenants/giantvapes/public/img/hero.webp +0 -0
- package/tenants/giantvapes/public/img/og.jpg +0 -0
- package/tenants/giantvapes/public/logo-wordmark.png +0 -0
- package/tenants/giantvapes/public/pages/home.css +120 -0
- package/tenants/giantvapes/public/pages/mkt.css +185 -0
- package/tenants/giantvapes/public/pages/page.css +155 -0
- package/tenants/giantvapes/public/themes/giant-navy.css +76 -0
- package/tenants/giantvapes/theme.json +39 -0
- package/tenants/home/content/chrome.json +11 -0
- package/tenants/home/content/home.html +304 -0
- package/tenants/home/content/home.json +13 -0
- package/tenants/home/public/js/storefront.js +210 -0
- package/tenants/home/public/logo-wordmark.png +0 -0
- package/tenants/home/public/pages/storefront.css +212 -0
- package/tenants/home/theme.json +6 -0
- package/tenants/tokenoftrust/.tot/config.json +9 -0
- package/tenants/tokenoftrust/content/chrome.json +55 -0
- package/tenants/tokenoftrust/content/home.html +723 -0
- package/tenants/tokenoftrust/content/home.json +233 -0
- package/tenants/tokenoftrust/content/pages-html/careers.html +231 -0
- package/tenants/tokenoftrust/content/pages-html/company.html +275 -0
- package/tenants/tokenoftrust/content/pages-html/contact/contact-sales.html +188 -0
- package/tenants/tokenoftrust/content/pages-html/contact/startup-apply.html +206 -0
- package/tenants/tokenoftrust/content/pages-html/contact.html +178 -0
- package/tenants/tokenoftrust/content/pages-html/industries/adult-content-age-verification.html +286 -0
- package/tenants/tokenoftrust/content/pages-html/industries/cannabis.html +277 -0
- package/tenants/tokenoftrust/content/pages-html/pact-act-pricing.html +289 -0
- package/tenants/tokenoftrust/content/pages-html/pricing.html +483 -0
- package/tenants/tokenoftrust/content/pages-html/product/age-assurance/age-estimation.html +278 -0
- package/tenants/tokenoftrust/content/pages-html/product/age-assurance/age-gate.html +279 -0
- package/tenants/tokenoftrust/content/pages-html/product/age-assurance/age-verification.html +265 -0
- package/tenants/tokenoftrust/content/pages-html/product/age-assurance.html +173 -0
- package/tenants/tokenoftrust/content/pages-html/product/compliance/aml.html +214 -0
- package/tenants/tokenoftrust/content/pages-html/product/compliance/pact-act.html +226 -0
- package/tenants/tokenoftrust/content/pages-html/product/fraud-prevention.html +254 -0
- package/tenants/tokenoftrust/content/pages-html/product/social-profile-verification.html +212 -0
- package/tenants/tokenoftrust/content/pages-html/product/tax/excise-tax.html +213 -0
- package/tenants/tokenoftrust/content/pages-html/product/verification/aml.html +259 -0
- package/tenants/tokenoftrust/content/pages-html/product/verification/biometric-selfie.html +305 -0
- package/tenants/tokenoftrust/content/pages-html/product/verification/coverage.html +254 -0
- package/tenants/tokenoftrust/content/pages-html/product/verification/document-verification.html +306 -0
- package/tenants/tokenoftrust/content/pages-html/product/verification/electronic-identity-verification.html +304 -0
- package/tenants/tokenoftrust/content/pages-html/product/verification/government-id-verification.html +308 -0
- package/tenants/tokenoftrust/content/pages-html/product/verification.html +183 -0
- package/tenants/tokenoftrust/content/pages-html/product.html +228 -0
- package/tenants/tokenoftrust/content/pages-html/resources/integrations/bigcommerce-integration.html +220 -0
- package/tenants/tokenoftrust/content/pages-html/resources/integrations/shopify-integration.html +221 -0
- package/tenants/tokenoftrust/content/pages-html/resources/integrations/wordpress.html +220 -0
- package/tenants/tokenoftrust/content/pages-html/resources/integrations.html +172 -0
- package/tenants/tokenoftrust/content/pages-html/resources/refer-a-friend-program.html +317 -0
- package/tenants/tokenoftrust/content/pages-html/resources.html +177 -0
- package/tenants/tokenoftrust/content/pages-html/reviews.html +273 -0
- package/tenants/tokenoftrust/content/pages-html/solutions.html +292 -0
- package/tenants/tokenoftrust/content/pages-html/vlp-alcohol.html +289 -0
- package/tenants/tokenoftrust/content/pages-html/vlp-cigars-and-tobacco.html +286 -0
- package/tenants/tokenoftrust/content/pages-html/vlp-firearm-accessories.html +267 -0
- package/tenants/tokenoftrust/content/pages-html/vlp-peptides.html +273 -0
- package/tenants/tokenoftrust/content/themes/tot-navy.json +88 -0
- package/tenants/tokenoftrust/public/favicon.png +0 -0
- package/tenants/tokenoftrust/public/logo-icon.png +0 -0
- package/tenants/tokenoftrust/public/logo-wordmark.png +0 -0
- package/tenants/tokenoftrust/public/pages/company.css +108 -0
- package/tenants/tokenoftrust/public/pages/home.css +457 -0
- package/tenants/tokenoftrust/public/pages/mkt.css +482 -0
- package/tenants/tokenoftrust/public/pages/pricing.css +214 -0
- package/tenants/tokenoftrust/public/pages/solutions.css +66 -0
- package/tenants/tokenoftrust/public/themes/tot-navy-1.0.0.css +136 -0
- package/tenants/tokenoftrust/public/themes/tot-navy.css +358 -0
- package/tenants/tokenoftrust/theme.json +31 -0
- package/tsconfig.base.json +20 -0
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
---
|
|
2
|
+
/**
|
|
3
|
+
* Homepage — composed from Storyblok blocks (local fallback in Phase 1).
|
|
4
|
+
* Editorial composition comes from the ContentProvider; product/collection data
|
|
5
|
+
* is resolved from the ToTClient. All async data resolution happens HERE in the
|
|
6
|
+
* frontmatter (Astro templates can't host multi-statement async arrows), then
|
|
7
|
+
* the template renders simple typed view-models.
|
|
8
|
+
*/
|
|
9
|
+
import Layout from "@/layouts/Layout.astro";
|
|
10
|
+
import Section from "@/components/Section.astro";
|
|
11
|
+
import Hero from "@/components/home/Hero.astro";
|
|
12
|
+
import ProductCard from "@/components/ProductCard.astro";
|
|
13
|
+
import CollectionCard from "@/components/CollectionCard.astro";
|
|
14
|
+
import Img from "@/components/Img.astro";
|
|
15
|
+
import MarketingBlocks from "@/components/marketing/MarketingBlocks.astro";
|
|
16
|
+
import { withBase } from "@/lib/basePath";
|
|
17
|
+
import { rawHtmlResponse } from "@/lib/rawMarketingHtml";
|
|
18
|
+
import type { CatalogCollection, CatalogProduct } from "@tot/public-runtime";
|
|
19
|
+
import {
|
|
20
|
+
isMarketingBlock,
|
|
21
|
+
type HeroBlock,
|
|
22
|
+
type EditorialBlock,
|
|
23
|
+
type NewsletterBlock,
|
|
24
|
+
type PromoTilesBlock,
|
|
25
|
+
type MarketingBlock,
|
|
26
|
+
} from "@/lib/storyblok/content-model";
|
|
27
|
+
|
|
28
|
+
const { tenant, tot, content, basePath, features } = Astro.locals;
|
|
29
|
+
const marketing = !features.catalog;
|
|
30
|
+
|
|
31
|
+
// Raw-HTML home (vendor-injected): serve the tenant's own self-contained
|
|
32
|
+
// document VERBATIM, bypassing the block Layout entirely. Its inline
|
|
33
|
+
// <style>/<script> get this request's CSP nonce so the strict policy still
|
|
34
|
+
// allows them. This route stays fully inside Astro — middleware already applied
|
|
35
|
+
// tenant resolution + canonical host, and on the way out applies security
|
|
36
|
+
// headers, the CSP, and (in prod) the edge page cache. SEO title/meta/canonical/
|
|
37
|
+
// OpenGraph/JSON-LD come from the document's own <head>. The homepage `/` is in
|
|
38
|
+
// the marketing sitemap (sitemap.xml.ts).
|
|
39
|
+
//
|
|
40
|
+
// Served regardless of siteType: a tenant that ships home.html gets its raw
|
|
41
|
+
// marketing home whether or not commerce is on. This is the HYBRID tenant —
|
|
42
|
+
// pixel-perfect marketing chrome at `/` surrounding a live commerce core at
|
|
43
|
+
// /collections + /products/* (see playbook/site-migration/commerce-handoff.md §1
|
|
44
|
+
// and product-boundaries.md). Commerce tenants with no home.html fall through to
|
|
45
|
+
// the block-composed home below, unchanged. Mirrors the raw-subpage path in
|
|
46
|
+
// [...slug].astro (getPageHtml is already siteType-agnostic).
|
|
47
|
+
{
|
|
48
|
+
const rawHtml = await content.getHomeHtml();
|
|
49
|
+
if (rawHtml) {
|
|
50
|
+
// Shared chrome: if the tenant ships a chrome.html wrapper AND this home is a
|
|
51
|
+
// body-only fragment, rawHtmlResponse splices it in; a full document serves
|
|
52
|
+
// verbatim (backward-compatible).
|
|
53
|
+
const chromeWrapper = await content.getChromeHtml();
|
|
54
|
+
return rawHtmlResponse(rawHtml, { nonce: Astro.locals.cspNonce, basePath, chromeWrapper });
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const home = await content.getHome();
|
|
59
|
+
|
|
60
|
+
// Marketing tenants (no catalog) compose the homepage from marketing blocks and
|
|
61
|
+
// skip ALL product/collection resolution below.
|
|
62
|
+
const marketingBlocks: MarketingBlock[] = marketing
|
|
63
|
+
? home.blocks.filter(isMarketingBlock)
|
|
64
|
+
: [];
|
|
65
|
+
|
|
66
|
+
const collections = marketing ? [] : await tot.listCollections();
|
|
67
|
+
|
|
68
|
+
// --- Resolve each block into a typed, render-ready view-model -----------------
|
|
69
|
+
type Resolved =
|
|
70
|
+
| { kind: "hero"; block: HeroBlock; product: CatalogProduct | null }
|
|
71
|
+
| { kind: "promo_tiles"; title?: string; tiles: PromoTilesBlock["tiles"] }
|
|
72
|
+
| { kind: "collections"; title?: string; cols: CatalogCollection[] }
|
|
73
|
+
| { kind: "products"; title?: string; products: CatalogProduct[] }
|
|
74
|
+
| { kind: "editorial"; block: EditorialBlock }
|
|
75
|
+
| { kind: "newsletter"; block: NewsletterBlock };
|
|
76
|
+
|
|
77
|
+
async function resolveProducts(
|
|
78
|
+
source: "featured" | "newest",
|
|
79
|
+
handles: string[] | undefined,
|
|
80
|
+
limit: number,
|
|
81
|
+
): Promise<CatalogProduct[]> {
|
|
82
|
+
if (handles?.length) {
|
|
83
|
+
const all = await Promise.all(handles.map((h) => tot.getProductByHandle(h)));
|
|
84
|
+
return all
|
|
85
|
+
.filter((p): p is CatalogProduct => !!p && p.status === "active")
|
|
86
|
+
.slice(0, limit);
|
|
87
|
+
}
|
|
88
|
+
if (source === "newest") {
|
|
89
|
+
const { products } = await tot.listProducts({ sort: "newest", pageSize: limit });
|
|
90
|
+
return products;
|
|
91
|
+
}
|
|
92
|
+
return tot.getFeatured(limit);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const resolved: Resolved[] = [];
|
|
96
|
+
for (const block of marketing ? [] : home.blocks) {
|
|
97
|
+
switch (block.component) {
|
|
98
|
+
case "hero": {
|
|
99
|
+
let product: CatalogProduct | null = null;
|
|
100
|
+
if (block.featureCollection) {
|
|
101
|
+
const col = collections.find((c) => c.handle === block.featureCollection);
|
|
102
|
+
const firstHandle = col?.product_handles[0];
|
|
103
|
+
if (firstHandle) product = await tot.getProductByHandle(firstHandle);
|
|
104
|
+
}
|
|
105
|
+
resolved.push({ kind: "hero", block, product });
|
|
106
|
+
break;
|
|
107
|
+
}
|
|
108
|
+
case "promo_tiles": {
|
|
109
|
+
if (block.tiles?.length) {
|
|
110
|
+
resolved.push({ kind: "promo_tiles", title: block.title, tiles: block.tiles });
|
|
111
|
+
}
|
|
112
|
+
break;
|
|
113
|
+
}
|
|
114
|
+
case "featured_collections": {
|
|
115
|
+
const handles = block.handles?.length
|
|
116
|
+
? block.handles
|
|
117
|
+
: collections.map((c) => c.handle);
|
|
118
|
+
const cols = handles
|
|
119
|
+
.map((h) => collections.find((c) => c.handle === h))
|
|
120
|
+
.filter((c): c is CatalogCollection => !!c);
|
|
121
|
+
resolved.push({ kind: "collections", title: block.title, cols });
|
|
122
|
+
break;
|
|
123
|
+
}
|
|
124
|
+
case "featured_products": {
|
|
125
|
+
const products = await resolveProducts(
|
|
126
|
+
block.source,
|
|
127
|
+
block.handles,
|
|
128
|
+
block.limit ?? 4,
|
|
129
|
+
);
|
|
130
|
+
resolved.push({ kind: "products", title: block.title, products });
|
|
131
|
+
break;
|
|
132
|
+
}
|
|
133
|
+
case "editorial":
|
|
134
|
+
resolved.push({ kind: "editorial", block });
|
|
135
|
+
break;
|
|
136
|
+
case "newsletter":
|
|
137
|
+
resolved.push({ kind: "newsletter", block });
|
|
138
|
+
break;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
---
|
|
142
|
+
|
|
143
|
+
<Layout
|
|
144
|
+
title={home.seoTitle ?? tenant.displayName}
|
|
145
|
+
description={home.seoDescription}
|
|
146
|
+
path="/"
|
|
147
|
+
ogType="website"
|
|
148
|
+
fullBleed={marketing}
|
|
149
|
+
>
|
|
150
|
+
{marketing && <MarketingBlocks blocks={marketingBlocks} basePath={basePath} />}
|
|
151
|
+
{
|
|
152
|
+
!marketing && resolved.map((r) => {
|
|
153
|
+
if (r.kind === "hero") {
|
|
154
|
+
return <Hero block={r.block} featureProduct={r.product} />;
|
|
155
|
+
}
|
|
156
|
+
if (r.kind === "promo_tiles") {
|
|
157
|
+
return (
|
|
158
|
+
<Section title={r.title} eyebrow="Featured" tone="bg">
|
|
159
|
+
<div
|
|
160
|
+
class:list={[
|
|
161
|
+
"grid gap-5",
|
|
162
|
+
r.tiles.length >= 3 ? "sm:grid-cols-3" : "sm:grid-cols-2",
|
|
163
|
+
]}
|
|
164
|
+
>
|
|
165
|
+
{r.tiles.map((t) => (
|
|
166
|
+
<a
|
|
167
|
+
href={withBase(basePath, t.href)}
|
|
168
|
+
class="group relative block aspect-[16/9] overflow-hidden rounded-[var(--radius-lg)] shadow-[var(--shadow-sm)]"
|
|
169
|
+
>
|
|
170
|
+
{t.image && (
|
|
171
|
+
<img
|
|
172
|
+
src={t.image}
|
|
173
|
+
alt={t.label}
|
|
174
|
+
loading="lazy"
|
|
175
|
+
class="absolute inset-0 h-full w-full object-cover transition-transform duration-300 group-hover:scale-105"
|
|
176
|
+
/>
|
|
177
|
+
)}
|
|
178
|
+
<span class="absolute inset-x-0 bottom-0 bg-gradient-to-t from-black/70 to-transparent p-4 font-display text-lg font-semibold text-white">
|
|
179
|
+
{t.label}
|
|
180
|
+
</span>
|
|
181
|
+
</a>
|
|
182
|
+
))}
|
|
183
|
+
</div>
|
|
184
|
+
</Section>
|
|
185
|
+
);
|
|
186
|
+
}
|
|
187
|
+
if (r.kind === "collections") {
|
|
188
|
+
return (
|
|
189
|
+
<Section title={r.title} eyebrow="Collections" tone="bg">
|
|
190
|
+
<div class="grid gap-5 sm:grid-cols-2 lg:grid-cols-3">
|
|
191
|
+
{r.cols.map((c) => (
|
|
192
|
+
<CollectionCard collection={c} productCount={c.product_handles.length} />
|
|
193
|
+
))}
|
|
194
|
+
</div>
|
|
195
|
+
</Section>
|
|
196
|
+
);
|
|
197
|
+
}
|
|
198
|
+
if (r.kind === "products") {
|
|
199
|
+
return (
|
|
200
|
+
<Section title={r.title} eyebrow="Shop" tone="surface">
|
|
201
|
+
<div class="grid grid-cols-2 gap-5 lg:grid-cols-4">
|
|
202
|
+
{r.products.map((p, i) => (
|
|
203
|
+
<ProductCard product={p} position={i} list={r.title} />
|
|
204
|
+
))}
|
|
205
|
+
</div>
|
|
206
|
+
</Section>
|
|
207
|
+
);
|
|
208
|
+
}
|
|
209
|
+
if (r.kind === "editorial") {
|
|
210
|
+
const b = r.block;
|
|
211
|
+
const right = b.align !== "left";
|
|
212
|
+
return (
|
|
213
|
+
<Section tone="bg">
|
|
214
|
+
<div
|
|
215
|
+
class:list={[
|
|
216
|
+
"grid items-center gap-10 md:grid-cols-2",
|
|
217
|
+
right ? "" : "md:[&>*:first-child]:order-2",
|
|
218
|
+
]}
|
|
219
|
+
>
|
|
220
|
+
<div data-reveal>
|
|
221
|
+
{b.eyebrow && <p class="eyebrow eyebrow--tick mb-4">{b.eyebrow}</p>}
|
|
222
|
+
<h2 class="display-md">
|
|
223
|
+
{b.title}
|
|
224
|
+
</h2>
|
|
225
|
+
<div class="rte mt-5 text-[var(--color-muted)]" set:html={b.body_html} />
|
|
226
|
+
{b.ctaLabel && b.ctaHref && (
|
|
227
|
+
<a
|
|
228
|
+
href={withBase(basePath, b.ctaHref)}
|
|
229
|
+
class="link-accent mt-7 inline-block pb-0.5 font-display text-sm font-medium"
|
|
230
|
+
>
|
|
231
|
+
{b.ctaLabel} →
|
|
232
|
+
</a>
|
|
233
|
+
)}
|
|
234
|
+
</div>
|
|
235
|
+
<div data-reveal>
|
|
236
|
+
<div class="overflow-hidden rounded-[var(--radius-lg)] shadow-[var(--shadow-md)]">
|
|
237
|
+
<Img
|
|
238
|
+
seed={b.imageSeed ?? b.title}
|
|
239
|
+
label={b.title}
|
|
240
|
+
alt=""
|
|
241
|
+
width={1000}
|
|
242
|
+
height={750}
|
|
243
|
+
sizes="(min-width:768px) 48vw, 100vw"
|
|
244
|
+
class="h-full w-full object-cover"
|
|
245
|
+
/>
|
|
246
|
+
</div>
|
|
247
|
+
</div>
|
|
248
|
+
</div>
|
|
249
|
+
</Section>
|
|
250
|
+
);
|
|
251
|
+
}
|
|
252
|
+
if (r.kind === "newsletter") {
|
|
253
|
+
const b = r.block;
|
|
254
|
+
return (
|
|
255
|
+
<Section tone="surface" id="newsletter">
|
|
256
|
+
<div class="mx-auto max-w-xl text-center" data-reveal>
|
|
257
|
+
<h2 class="display-md">
|
|
258
|
+
{b.title}
|
|
259
|
+
</h2>
|
|
260
|
+
{b.body && <p class="mt-3 text-[var(--color-muted)]">{b.body}</p>}
|
|
261
|
+
<form
|
|
262
|
+
action={withBase(basePath, "/api/newsletter")}
|
|
263
|
+
method="post"
|
|
264
|
+
class="mx-auto mt-7 flex max-w-md flex-col gap-3 sm:flex-row"
|
|
265
|
+
>
|
|
266
|
+
<input type="hidden" name="source" value="home" />
|
|
267
|
+
<label class="sr-only" for="home-news-email">Email address</label>
|
|
268
|
+
<input
|
|
269
|
+
id="home-news-email"
|
|
270
|
+
type="email"
|
|
271
|
+
name="email"
|
|
272
|
+
required
|
|
273
|
+
placeholder={b.placeholder ?? "you@example.com"}
|
|
274
|
+
class="flex-1 rounded-full border border-[var(--color-border)] bg-[var(--color-bg)] px-5 py-3 text-[var(--color-text)] placeholder:text-[var(--color-muted)]"
|
|
275
|
+
/>
|
|
276
|
+
<button
|
|
277
|
+
type="submit"
|
|
278
|
+
class="rounded-full bg-[var(--color-primary)] px-6 py-3 font-display text-sm font-medium text-[var(--color-primary-contrast)] transition-transform hover:-translate-y-0.5"
|
|
279
|
+
>
|
|
280
|
+
{b.ctaLabel ?? "Subscribe"}
|
|
281
|
+
</button>
|
|
282
|
+
</form>
|
|
283
|
+
</div>
|
|
284
|
+
</Section>
|
|
285
|
+
);
|
|
286
|
+
}
|
|
287
|
+
return null;
|
|
288
|
+
})
|
|
289
|
+
}
|
|
290
|
+
</Layout>
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-tenant llms.txt — the emerging /llms.txt convention: a concise,
|
|
3
|
+
* machine-readable guide that helps LLM answer engines (ChatGPT, Perplexity,
|
|
4
|
+
* Google AI Overviews, etc.) understand what this store is and where its key
|
|
5
|
+
* content lives. Mirrors robots.txt / sitemap.xml: built per request from the
|
|
6
|
+
* resolved tenant's catalog + content, canonical base from the Host.
|
|
7
|
+
*
|
|
8
|
+
* Every line traces to REAL migrated data or tenant config — no fabricated copy
|
|
9
|
+
* (playbook/codebase-health.md T7). Summary/descriptions are the merchant's own
|
|
10
|
+
* SEO text; compliance facts come from tenant.compliance.
|
|
11
|
+
*/
|
|
12
|
+
import type { APIContext } from "astro";
|
|
13
|
+
import { createContentProvider } from "@/lib/storyblok/provider";
|
|
14
|
+
|
|
15
|
+
/** Collapse HTML/whitespace to a single trimmed line for a link description. */
|
|
16
|
+
function oneLine(s: string | undefined, max = 140): string | undefined {
|
|
17
|
+
if (!s) return undefined;
|
|
18
|
+
const t = s.replace(/<[^>]*>/g, " ").replace(/\s+/g, " ").trim();
|
|
19
|
+
if (!t) return undefined;
|
|
20
|
+
return t.length > max ? `${t.slice(0, max - 1)}…` : t;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export async function GET(context: APIContext): Promise<Response> {
|
|
24
|
+
const { tot, tenant, canonicalBase } = context.locals;
|
|
25
|
+
const base = canonicalBase.replace(/\/$/, "");
|
|
26
|
+
const provider = createContentProvider(tenant);
|
|
27
|
+
|
|
28
|
+
const [collections, pages, featured, home] = await Promise.all([
|
|
29
|
+
tot.listCollections(),
|
|
30
|
+
provider.listPages(),
|
|
31
|
+
tot.getFeatured(12),
|
|
32
|
+
provider.getHome().catch(() => null),
|
|
33
|
+
]);
|
|
34
|
+
|
|
35
|
+
const lines: string[] = [`# ${tenant.displayName}`, ""];
|
|
36
|
+
|
|
37
|
+
const summary = oneLine(home?.seoDescription, 280);
|
|
38
|
+
if (summary) lines.push(`> ${summary}`, "");
|
|
39
|
+
|
|
40
|
+
// Compliance facts — sourced from tenant config, never invented.
|
|
41
|
+
const c = tenant.compliance;
|
|
42
|
+
if (c && (c.minAge || c.nicotineWarning || c.shippingRestriction)) {
|
|
43
|
+
const facts: string[] = [];
|
|
44
|
+
if (c.minAge) facts.push(`age-restricted — customers must be ${c.minAge}+`);
|
|
45
|
+
if (c.nicotineWarning) facts.push("products contain nicotine (FDA warning applies)");
|
|
46
|
+
if (facts.length) lines.push(`**Compliance:** ${facts.join("; ")}.`);
|
|
47
|
+
const ship = oneLine(c.shippingRestriction, 240);
|
|
48
|
+
if (ship) lines.push(`**Shipping:** ${ship}`);
|
|
49
|
+
lines.push("");
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if (collections.length) {
|
|
53
|
+
lines.push("## Collections");
|
|
54
|
+
for (const col of collections) {
|
|
55
|
+
const desc = oneLine(col.description_text ?? col.seo.description);
|
|
56
|
+
lines.push(`- [${col.title}](${base}/collections/${col.handle})${desc ? `: ${desc}` : ""}`);
|
|
57
|
+
}
|
|
58
|
+
lines.push("");
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (featured.length) {
|
|
62
|
+
lines.push("## Featured products");
|
|
63
|
+
for (const p of featured) {
|
|
64
|
+
const desc = oneLine(p.seo.description ?? p.description_text);
|
|
65
|
+
lines.push(`- [${p.title}](${base}/products/${p.handle})${desc ? `: ${desc}` : ""}`);
|
|
66
|
+
}
|
|
67
|
+
lines.push("");
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if (pages.length) {
|
|
71
|
+
lines.push("## Pages");
|
|
72
|
+
for (const pg of pages) {
|
|
73
|
+
lines.push(`- [${pg.title}](${base}/${pg.slug})`);
|
|
74
|
+
}
|
|
75
|
+
lines.push("");
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
lines.push("## Optional");
|
|
79
|
+
lines.push(`- [Full product catalog (XML sitemap)](${base}/sitemap.xml)`);
|
|
80
|
+
lines.push(`- [Search](${base}/search)`);
|
|
81
|
+
lines.push(
|
|
82
|
+
"- Every page ships Schema.org JSON-LD (Product, Offer, AggregateRating, Breadcrumb, Organization) for structured extraction.",
|
|
83
|
+
);
|
|
84
|
+
lines.push("");
|
|
85
|
+
|
|
86
|
+
return new Response(lines.join("\n"), {
|
|
87
|
+
headers: {
|
|
88
|
+
"content-type": "text/plain; charset=utf-8",
|
|
89
|
+
"cache-control": "public, max-age=300, s-maxage=3600",
|
|
90
|
+
},
|
|
91
|
+
});
|
|
92
|
+
}
|