@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
package/LICENSE
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
Token of Trust Source-Available License — Local Development Only
|
|
2
|
+
Version 1.0
|
|
3
|
+
|
|
4
|
+
Copyright (c) 2026 Token of Trust, Inc. All rights reserved.
|
|
5
|
+
|
|
6
|
+
1. DEFINITIONS
|
|
7
|
+
"Software" means this source code and any accompanying files in this
|
|
8
|
+
repository or distribution, including the storefront renderer runtime, the
|
|
9
|
+
`@tot/public-runtime` package, the storefront-runner distributable, and any
|
|
10
|
+
derivative works.
|
|
11
|
+
"Local Development" means running, building, and modifying the Software on a
|
|
12
|
+
machine you control, for the sole purpose of evaluating it or developing and
|
|
13
|
+
previewing your own tenant content against it. Serving the Software to any
|
|
14
|
+
third party, or over any public or shared network, is NOT Local Development.
|
|
15
|
+
"Token of Trust" means Token of Trust, Inc.
|
|
16
|
+
|
|
17
|
+
2. GRANT (LOCAL DEVELOPMENT ONLY)
|
|
18
|
+
Subject to the terms below, Token of Trust grants you a personal,
|
|
19
|
+
non-exclusive, non-transferable, revocable license to use, copy, build, and
|
|
20
|
+
modify the Software SOLELY for Local Development.
|
|
21
|
+
|
|
22
|
+
3. PERMISSION REQUIRED FOR EVERYTHING ELSE
|
|
23
|
+
Any use of the Software beyond Local Development requires the PRIOR WRITTEN
|
|
24
|
+
PERMISSION of Token of Trust. Without such permission you may NOT, in whole or
|
|
25
|
+
in part:
|
|
26
|
+
(a) deploy, host, serve, or otherwise run the Software in production or for
|
|
27
|
+
any third party, or on any public, staging, or shared/network-accessible
|
|
28
|
+
environment;
|
|
29
|
+
(b) use the Software for any commercial purpose or to provide a service to
|
|
30
|
+
others;
|
|
31
|
+
(c) distribute, publish, sublicense, sell, rent, lease, or otherwise make the
|
|
32
|
+
Software (or a derivative of it) available to any third party; or
|
|
33
|
+
(d) remove or obscure this license or the copyright notices.
|
|
34
|
+
To request permission, contact Token of Trust at legal@tokenoftrust.com.
|
|
35
|
+
|
|
36
|
+
4. NO TRADEMARK LICENSE
|
|
37
|
+
This license grants no rights to the Token of Trust name, logos, or
|
|
38
|
+
trademarks.
|
|
39
|
+
|
|
40
|
+
5. RESERVATION OF RIGHTS
|
|
41
|
+
All rights not expressly granted are reserved by Token of Trust. This is a
|
|
42
|
+
source-available license, not an open-source license; the Software is not
|
|
43
|
+
licensed under any OSI-approved license.
|
|
44
|
+
|
|
45
|
+
6. TERMINATION
|
|
46
|
+
This license terminates automatically if you breach it. On termination you
|
|
47
|
+
must stop using the Software and destroy all copies in your possession.
|
|
48
|
+
|
|
49
|
+
7. DISCLAIMER OF WARRANTY
|
|
50
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
51
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
52
|
+
FITNESS FOR A PARTICULAR PURPOSE, AND NONINFRINGEMENT.
|
|
53
|
+
|
|
54
|
+
8. LIMITATION OF LIABILITY
|
|
55
|
+
IN NO EVENT SHALL TOKEN OF TRUST BE LIABLE FOR ANY CLAIM, DAMAGES, OR OTHER
|
|
56
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT, OR OTHERWISE, ARISING FROM,
|
|
57
|
+
OUT OF, OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
58
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# Storefront Runner
|
|
2
|
+
|
|
3
|
+
A world-shareable, multi-tenant **renderer runtime** for the Token of Trust
|
|
4
|
+
storefront. It serves N tenants (or differently-named variants of one tenant) at
|
|
5
|
+
path-prefix URLs — the same thing `astro dev` does for the full platform — with
|
|
6
|
+
the control-plane services (content authoring, compliance, and the publish/deploy
|
|
7
|
+
control plane) kept private and **physically carved out**.
|
|
8
|
+
|
|
9
|
+
## Run
|
|
10
|
+
|
|
11
|
+
```sh
|
|
12
|
+
pnpm install
|
|
13
|
+
pnpm dev # astro dev on http://localhost:4321
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
Tenants resolve by `Host` header first, then by a `/<domain>/…` path prefix
|
|
17
|
+
(DNS-free, for localhost). Try:
|
|
18
|
+
|
|
19
|
+
- http://localhost:4321/tokenoftrust.com/
|
|
20
|
+
- http://localhost:4321/tokenoftrust-preview.com/ (renamed variant of the same tenant)
|
|
21
|
+
- http://localhost:4321/bigdvapor.net/
|
|
22
|
+
|
|
23
|
+
## Add a tenant
|
|
24
|
+
|
|
25
|
+
Adding a tenant — or a renamed variant of an existing one — is a one-object edit
|
|
26
|
+
in `apps/storefront/src/config/tenants.ts` (see `TOKENOFTRUST_PREVIEW_TENANT`),
|
|
27
|
+
plus a `tenants/<id>/` directory for its content/theme/public assets.
|
|
28
|
+
|
|
29
|
+
## IP boundary
|
|
30
|
+
|
|
31
|
+
This distributable depends only on `@tot/public-runtime` (the renderer-safe
|
|
32
|
+
model). The private control-plane package is absent, so a control-plane import
|
|
33
|
+
cannot resolve here.
|
|
34
|
+
|
|
35
|
+
## License
|
|
36
|
+
|
|
37
|
+
Source-available, **local-development only** (see `LICENSE`). You may run, build,
|
|
38
|
+
and modify this for Local Development. Any other use — deploying, hosting, or
|
|
39
|
+
serving it to third parties, or any production/commercial use — requires the
|
|
40
|
+
prior written permission of Token of Trust, Inc.
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# Copy to `.dev.vars` for local dev (gitignored). With NO values set, the app
|
|
2
|
+
# falls back to FixtureToTClient reading packages/migration/out/catalog/*.json,
|
|
3
|
+
# so the site renders fully offline. Set the TOT_* block to hit live tot20.
|
|
4
|
+
|
|
5
|
+
# --- ToT catalog API (leave blank to use fixtures) ---------------------------
|
|
6
|
+
# TOT_CATALOG_BASE_URL=https://api.tokenoftrust.com/api/catalog
|
|
7
|
+
# Either a bearer token...
|
|
8
|
+
# TOT_ACCESS_TOKEN=
|
|
9
|
+
# ...or an API key/secret pair:
|
|
10
|
+
# TOT_API_KEY=
|
|
11
|
+
# TOT_SECRET_KEY=
|
|
12
|
+
|
|
13
|
+
# --- Storyblok (leave blank to use local fallback content) -------------------
|
|
14
|
+
# Per-tenant delivery token; with none, LocalContentProvider reads
|
|
15
|
+
# src/content/pilot/*.json. The pilot tenant's token also lives in
|
|
16
|
+
# src/config/tenants.ts (placeholder there).
|
|
17
|
+
# STORYBLOK_DELIVERY_TOKEN=
|
|
18
|
+
|
|
19
|
+
# --- Analytics ---------------------------------------------------------------
|
|
20
|
+
# PUBLIC_CF_ANALYTICS_TOKEN=
|
|
21
|
+
|
|
22
|
+
# --- Staff broker assertion (#8453 staff-invite-proof-via-broker) ------------
|
|
23
|
+
# The storefront-signed RS256 staff proof presented to tot20 in the
|
|
24
|
+
# X-ToT-Broker-Assertion header (proves the signed-in ToT-staff actor server-side;
|
|
25
|
+
# replaces the spoofable plaintext staff-email field). Server-side ONLY — the
|
|
26
|
+
# private key must NEVER reach the client bundle. With this UNSET, the staff
|
|
27
|
+
# invite / vendor-search / enter-vendor routes fail closed (503).
|
|
28
|
+
# STOREFRONT_BROKER_PRIVATE_KEY_B64 = base64 of the PKCS8 PEM RS256 private key.
|
|
29
|
+
# tot20 verifies with the matching PUBLIC key registered under STOREFRONT_BROKER_KID.
|
|
30
|
+
# STOREFRONT_BROKER_PRIVATE_KEY_B64=
|
|
31
|
+
# STOREFRONT_BROKER_KID=storefront-qa-1
|
|
32
|
+
# STOREFRONT_BROKER_ISSUER=storefront
|
|
33
|
+
|
|
34
|
+
# --- Local dev auth (localhost only; never reaches prod) ----------------------
|
|
35
|
+
# tot20 sends invite/sign-in emails, so on localhost no email goes out. Set
|
|
36
|
+
# DEV_AUTH=1 to instead PRINT the one-time sign-in code to the SERVER CONSOLE
|
|
37
|
+
# (POST /api/auth/send) so you can complete sign-in locally without email. The
|
|
38
|
+
# whole path is behind `import.meta.env.DEV` and is tree-shaken from prod builds.
|
|
39
|
+
# (Accepting the printed code to mint a local session is being wired next — see
|
|
40
|
+
# handoff 2026-07-09-storefront-local-dev-auth-mode.md.)
|
|
41
|
+
# DEV_AUTH=1
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# Storefront environment variables (copy to .env for local dev).
|
|
2
|
+
# All are OPTIONAL — with none set, the storefront runs fully offline:
|
|
3
|
+
# catalog from the local fixture store, content from src/content/<tenant>/*.json.
|
|
4
|
+
|
|
5
|
+
# --- Catalog backend (ToT) ---------------------------------------------------
|
|
6
|
+
# When BASE_URL + (ACCESS_TOKEN | API_KEY+SECRET_KEY) are set, the storefront
|
|
7
|
+
# reads the live ToT catalog API instead of the local fixtures.
|
|
8
|
+
TOT_CATALOG_BASE_URL=
|
|
9
|
+
TOT_API_KEY=
|
|
10
|
+
TOT_SECRET_KEY=
|
|
11
|
+
TOT_ACCESS_TOKEN=
|
|
12
|
+
|
|
13
|
+
# --- Storyblok (editorial CMS) -----------------------------------------------
|
|
14
|
+
# Set a real delivery token to switch the pilot tenant from LocalContentProvider
|
|
15
|
+
# to the live StoryblokContentProvider. Model your space's components to match
|
|
16
|
+
# src/lib/storyblok/content-model.ts (home, chrome, pages/*).
|
|
17
|
+
STORYBLOK_PILOT_TOKEN=
|
|
18
|
+
# Space region: eu (default) | us | ap | ca
|
|
19
|
+
STORYBLOK_REGION=eu
|
|
20
|
+
# Content version: published (default) | draft (use draft for previewing)
|
|
21
|
+
STORYBLOK_VERSION=published
|
|
22
|
+
|
|
23
|
+
# --- Analytics ---------------------------------------------------------------
|
|
24
|
+
PUBLIC_CF_ANALYTICS_TOKEN=
|
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
import { defineConfig } from "astro/config";
|
|
3
|
+
import cloudflare from "@astrojs/cloudflare";
|
|
4
|
+
import preact from "@astrojs/preact";
|
|
5
|
+
import tailwindcss from "@tailwindcss/vite";
|
|
6
|
+
import { fileURLToPath } from "node:url";
|
|
7
|
+
|
|
8
|
+
// Repo-root tenants/ dir (colocated tenant layout) + the generated static dest
|
|
9
|
+
// the predev copy step mirrors public/ into. astro.config.mjs lives at
|
|
10
|
+
// apps/storefront/, so tenants/ is two levels up. Forward-slash normalized to
|
|
11
|
+
// match Vite's ctx.file (Vite always reports posix-style paths).
|
|
12
|
+
const TENANTS_DIR = fileURLToPath(new URL("../../tenants", import.meta.url)).replace(/\\/g, "/");
|
|
13
|
+
const PUBLIC_TENANTS_DEST = fileURLToPath(new URL("./public/tenants", import.meta.url));
|
|
14
|
+
|
|
15
|
+
// Dev-only default posture: save a colocated tenant file → the browser reloads.
|
|
16
|
+
// The `tenants/<id>/` tree sits ABOVE the Astro root and reaches the app only
|
|
17
|
+
// through a lazy `?raw` glob (HTML, bypassed by the dev disk-read) or the predev
|
|
18
|
+
// public/ copy — never Vite's own module graph — so Vite doesn't watch it and a
|
|
19
|
+
// save would otherwise do nothing. We watch it explicitly and reload on change:
|
|
20
|
+
// - content/**.html — read fresh from disk per request
|
|
21
|
+
// (LocalContentProvider.readRawDevFile), so just reload. (The Vite HMR client
|
|
22
|
+
// is injected into these otherwise-pipeline-bypassing pages in dev; see
|
|
23
|
+
// rawHtmlResponse in src/lib/rawMarketingHtml.ts.)
|
|
24
|
+
// - public/** — re-mirror the one changed asset into public/tenants/<id>/
|
|
25
|
+
// (skipping R2-versioned tenants, like copy-tenant-assets.mjs), then reload.
|
|
26
|
+
// content/*.json, themes/*.json and theme.json are ordinary glob imports, so Vite
|
|
27
|
+
// already invalidates + reloads them — left to default handling. Returning []
|
|
28
|
+
// suppresses the pointless glob HMR that would otherwise fire for the raw files.
|
|
29
|
+
function tenantHotReload() {
|
|
30
|
+
// Typed `any`: `vite` isn't a direct dependency of this package (it's pulled in
|
|
31
|
+
// transitively via astro), so `import('vite').Plugin` can't be resolved here and
|
|
32
|
+
// importing from "vite" would even fail at runtime. `any` keeps the hook params
|
|
33
|
+
// untyped without that reference.
|
|
34
|
+
/** @type {any} */
|
|
35
|
+
const plugin = {
|
|
36
|
+
name: "tot:tenant-hot-reload",
|
|
37
|
+
apply: "serve",
|
|
38
|
+
/** @param {any} server */
|
|
39
|
+
configureServer(server) {
|
|
40
|
+
server.watcher.add(TENANTS_DIR);
|
|
41
|
+
},
|
|
42
|
+
/** @param {any} ctx */
|
|
43
|
+
async handleHotUpdate(ctx) {
|
|
44
|
+
const file = ctx.file.replace(/\\/g, "/");
|
|
45
|
+
if (!file.startsWith(TENANTS_DIR + "/")) return undefined;
|
|
46
|
+
const rel = file.slice(TENANTS_DIR.length + 1); // "<id>/<...>"
|
|
47
|
+
const slash = rel.indexOf("/");
|
|
48
|
+
if (slash < 0) return undefined;
|
|
49
|
+
const id = rel.slice(0, slash);
|
|
50
|
+
const rest = rel.slice(slash + 1);
|
|
51
|
+
|
|
52
|
+
// Content HTML: the change invalidates the SSR module graph, so the
|
|
53
|
+
// Cloudflare Vite plugin reloads the worker and — once it is ready — emits
|
|
54
|
+
// its own correctly-timed full-reload (which the injected Vite HMR client
|
|
55
|
+
// acts on). We must NOT send our own reload: firing on the raw file-change
|
|
56
|
+
// event races ahead of the worker reload, so the browser re-navigates while
|
|
57
|
+
// miniflare is mid-teardown → "page not loaded" + an error overlay. Just let
|
|
58
|
+
// default handling run.
|
|
59
|
+
if (rest.startsWith("content/") && rest.endsWith(".html")) {
|
|
60
|
+
return undefined;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Public assets (CSS, images) aren't in the module graph — they're mirrored
|
|
64
|
+
// into public/tenants/<id>/ at predev, and a programmatic write there does
|
|
65
|
+
// NOT trip the platform's own reload. So: re-mirror the one changed file
|
|
66
|
+
// (await it so the served copy is current), THEN send the reload ourselves.
|
|
67
|
+
// Unlike content HTML this reloads no worker, so there's no teardown race to
|
|
68
|
+
// fire ahead of. Skip R2-versioned tenants, like copy-tenant-assets.mjs.
|
|
69
|
+
if (rest.startsWith("public/")) {
|
|
70
|
+
// fs via dynamic import so a config-load issue could never break startup.
|
|
71
|
+
const [{ cp, mkdir }, { existsSync }, { dirname, join }] = await Promise.all([
|
|
72
|
+
import("node:fs/promises"),
|
|
73
|
+
import("node:fs"),
|
|
74
|
+
import("node:path"),
|
|
75
|
+
]);
|
|
76
|
+
const versioned = existsSync(join(TENANTS_DIR, id, ".tot", "assets-versioned"));
|
|
77
|
+
if (!versioned) {
|
|
78
|
+
const target = join(PUBLIC_TENANTS_DEST, id, rest.slice("public/".length));
|
|
79
|
+
try {
|
|
80
|
+
await mkdir(dirname(target), { recursive: true });
|
|
81
|
+
await cp(ctx.file, target);
|
|
82
|
+
} catch {
|
|
83
|
+
/* best-effort; still reload below */
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
ctx.server.ws.send({ type: "full-reload" });
|
|
87
|
+
return [];
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return undefined;
|
|
91
|
+
},
|
|
92
|
+
};
|
|
93
|
+
return plugin;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Repo root, two levels up (astro.config.mjs lives at apps/storefront/).
|
|
97
|
+
const REPO_ROOT = fileURLToPath(new URL("../../", import.meta.url));
|
|
98
|
+
const WIDGET_CLIENT_PATH = fileURLToPath(
|
|
99
|
+
new URL("./src/lib/dev/ai-widget-client.js", import.meta.url),
|
|
100
|
+
);
|
|
101
|
+
|
|
102
|
+
/** @param {any} req */
|
|
103
|
+
async function readJsonBody(req) {
|
|
104
|
+
const chunks = [];
|
|
105
|
+
for await (const chunk of req) chunks.push(chunk);
|
|
106
|
+
const raw = Buffer.concat(chunks).toString("utf8");
|
|
107
|
+
return raw ? JSON.parse(raw) : {};
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/** @param {any} res @param {number} status @param {any} body */
|
|
111
|
+
function sendJson(res, status, body) {
|
|
112
|
+
res.statusCode = status;
|
|
113
|
+
res.setHeader("content-type", "application/json");
|
|
114
|
+
res.end(JSON.stringify(body));
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/** @param {unknown} e */
|
|
118
|
+
function errorMessage(e) {
|
|
119
|
+
return e instanceof Error ? e.message : String(e);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// The inline-AI edit loop (WS5 G1): the floating "✦ ask" box + its dev-server
|
|
123
|
+
// endpoints. Dev-only (`apply: "serve"`) — never runs in `astro build`, so it
|
|
124
|
+
// can never reach the production Cloudflare Worker. The widget itself is
|
|
125
|
+
// unconditional in dev (matching the Vite HMR client injection in
|
|
126
|
+
// rawMarketingHtml.ts) — it's the /__tot/ai-* ENDPOINTS below that gate on
|
|
127
|
+
// TOT_DEV_TENANT (set by scripts/tot-dev.mjs), since that's a Node-process env
|
|
128
|
+
// var these Vite-layer handlers see reliably (unlike SSR code, which under the
|
|
129
|
+
// Cloudflare adapter runs inside workerd and does NOT see it — see tot-dev.mjs's
|
|
130
|
+
// own note on this exact env-var-doesn't-reach-workerd gap). A bare `astro dev`
|
|
131
|
+
// (no active tenant) still shows the widget, but "Ask" reports `no_tenant`
|
|
132
|
+
// rather than erroring silently.
|
|
133
|
+
function inlineAiEditPlugin() {
|
|
134
|
+
/** @type {any} */
|
|
135
|
+
const plugin = {
|
|
136
|
+
name: "tot:inline-ai-edit",
|
|
137
|
+
apply: "serve",
|
|
138
|
+
/** @param {any} server */
|
|
139
|
+
configureServer(server) {
|
|
140
|
+
server.middlewares.use(async (
|
|
141
|
+
/** @type {any} */ req,
|
|
142
|
+
/** @type {any} */ res,
|
|
143
|
+
/** @type {any} */ next,
|
|
144
|
+
) => {
|
|
145
|
+
if (req.url === "/__tot/ai-widget-client.js") {
|
|
146
|
+
const { readFileSync } = await import("node:fs");
|
|
147
|
+
res.setHeader("content-type", "text/javascript");
|
|
148
|
+
res.end(readFileSync(WIDGET_CLIENT_PATH, "utf8"));
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
if (req.method === "POST" && req.url === "/__tot/ai-edit") {
|
|
152
|
+
const tenantId = process.env.TOT_DEV_TENANT;
|
|
153
|
+
if (!tenantId) {
|
|
154
|
+
sendJson(res, 200, { ok: false, status: "no_tenant", message: "This `astro dev` wasn't started via `tot dev` — no active tenant." });
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
const { handleAiEdit } = await import("../../scripts/dev/ai-edit.mjs");
|
|
158
|
+
try {
|
|
159
|
+
const { prompt } = await readJsonBody(req);
|
|
160
|
+
const result = await handleAiEdit({
|
|
161
|
+
repoRoot: REPO_ROOT, tenantId, prompt, mcpBaseUrl: undefined,
|
|
162
|
+
});
|
|
163
|
+
sendJson(res, 200, result);
|
|
164
|
+
} catch (e) {
|
|
165
|
+
sendJson(res, 200, { ok: false, status: "error", message: errorMessage(e) });
|
|
166
|
+
}
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
if (req.method === "POST" && req.url === "/__tot/ai-undo") {
|
|
170
|
+
const tenantId = process.env.TOT_DEV_TENANT;
|
|
171
|
+
if (!tenantId) {
|
|
172
|
+
sendJson(res, 200, { ok: false, status: "no_tenant", message: "This `astro dev` wasn't started via `tot dev` — no active tenant." });
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
const { handleAiUndo } = await import("../../scripts/dev/ai-edit.mjs");
|
|
176
|
+
try {
|
|
177
|
+
sendJson(res, 200, handleAiUndo({ repoRoot: REPO_ROOT, tenantId }));
|
|
178
|
+
} catch (e) {
|
|
179
|
+
sendJson(res, 200, { ok: false, status: "error", message: errorMessage(e) });
|
|
180
|
+
}
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
next();
|
|
184
|
+
});
|
|
185
|
+
},
|
|
186
|
+
// Covers pages that DO go through Astro/Vite's own render pipeline (e.g.
|
|
187
|
+
// Layout.astro commerce pages). Raw marketing pages (content/home.html)
|
|
188
|
+
// bypass that pipeline entirely — they inject the same script tag
|
|
189
|
+
// themselves in rawMarketingHtml.ts, right next to the Vite HMR client
|
|
190
|
+
// that function already injects for the same reason.
|
|
191
|
+
/** @param {string} html */
|
|
192
|
+
transformIndexHtml(html) {
|
|
193
|
+
const tag = '<script type="module" src="/__tot/ai-widget-client.js"></script>';
|
|
194
|
+
return html.includes("</body>") ? html.replace("</body>", `${tag}</body>`) : `${html}${tag}`;
|
|
195
|
+
},
|
|
196
|
+
};
|
|
197
|
+
return plugin;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// SSR on Cloudflare Workers. `output: 'server'` because every response is
|
|
201
|
+
// tenant-resolved at the edge from the Host header (see src/middleware).
|
|
202
|
+
// Preact powers the handful of interactive islands (variant selector, gallery,
|
|
203
|
+
// facets, search typeahead, mobile drawer) — near-zero JS everywhere else.
|
|
204
|
+
export default defineConfig({
|
|
205
|
+
output: "server",
|
|
206
|
+
// Externalize all CSS (no auto-inlined <style>) so a strict CSP needs no
|
|
207
|
+
// inline-style allowance for elements — every stylesheet is a 'self' <link>.
|
|
208
|
+
// Small first-paint tradeoff, offset by cross-page caching (see LIMITATIONS).
|
|
209
|
+
build: { inlineStylesheets: "never" },
|
|
210
|
+
adapter: cloudflare({
|
|
211
|
+
// Local image/asset placeholders are served from /public; real product
|
|
212
|
+
// images get rehosted to R2 in Phase 1 ETL (see DECISIONS.md).
|
|
213
|
+
imageService: "compile",
|
|
214
|
+
}),
|
|
215
|
+
integrations: [preact({ compat: true })],
|
|
216
|
+
vite: {
|
|
217
|
+
plugins: [tailwindcss(), tenantHotReload(), inlineAiEditPlugin()],
|
|
218
|
+
// Dev-only: let the dev server accept real tenant Host headers so host-based
|
|
219
|
+
// tenant routing can be tested locally (e.g. curl -H 'Host: giantvapes.com').
|
|
220
|
+
// Prod runs on Cloudflare Workers, which has no such allowlist.
|
|
221
|
+
server: {
|
|
222
|
+
allowedHosts: [
|
|
223
|
+
"giantvapes.com",
|
|
224
|
+
"www.giantvapes.com",
|
|
225
|
+
"pilot.tokenoftrust.com",
|
|
226
|
+
],
|
|
227
|
+
// Containerized `tot dev` (scripts/dev): a bind-mount often doesn't deliver
|
|
228
|
+
// inotify events, so Vite's file watcher must POLL for save→HMR to fire.
|
|
229
|
+
// Gated on CHOKIDAR_USEPOLLING (set by the dev image/compose) so native/local
|
|
230
|
+
// dev keeps instant, zero-overhead event-based watching. Explicit rather than
|
|
231
|
+
// relying on Vite auto-reading the env, so it's version-proof.
|
|
232
|
+
...(process.env.CHOKIDAR_USEPOLLING
|
|
233
|
+
? {
|
|
234
|
+
watch: {
|
|
235
|
+
usePolling: true,
|
|
236
|
+
interval: Number(process.env.CHOKIDAR_INTERVAL) || 300,
|
|
237
|
+
},
|
|
238
|
+
}
|
|
239
|
+
: {}),
|
|
240
|
+
},
|
|
241
|
+
},
|
|
242
|
+
// Canonical/sitemap host comes from the resolved tenant, not here.
|
|
243
|
+
// `site` is a fallback only used when no tenant context is available.
|
|
244
|
+
site: "https://pilot.tokenoftrust.com",
|
|
245
|
+
});
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
/// <reference path="./.astro/types.d.ts" />
|
|
2
|
+
/// <reference types="astro/client" />
|
|
3
|
+
|
|
4
|
+
import type {
|
|
5
|
+
TenantConfig,
|
|
6
|
+
CustomizationEnv,
|
|
7
|
+
TenantFeatures,
|
|
8
|
+
ResolvedFoxyCommerce,
|
|
9
|
+
CapabilityConfig,
|
|
10
|
+
} from "@tot/public-runtime";
|
|
11
|
+
import type { ToTClient } from "@/lib/tot/ToTClient";
|
|
12
|
+
import type { ContentProvider } from "@/lib/storyblok/provider";
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Minimal binding shapes. We intentionally do NOT depend on
|
|
16
|
+
* @cloudflare/workers-types (not installed); these cover what Phase 1 touches.
|
|
17
|
+
* Widen them when real KV/R2 calls are added.
|
|
18
|
+
*/
|
|
19
|
+
interface KVNamespaceLike {
|
|
20
|
+
get(key: string, opts?: { cacheTtl?: number }): Promise<string | null>;
|
|
21
|
+
put(key: string, value: string, opts?: Record<string, unknown>): Promise<void>;
|
|
22
|
+
list(opts?: { prefix?: string; cursor?: string; limit?: number }): Promise<{
|
|
23
|
+
keys: { name: string }[];
|
|
24
|
+
list_complete: boolean;
|
|
25
|
+
cursor?: string;
|
|
26
|
+
}>;
|
|
27
|
+
delete(key: string): Promise<void>;
|
|
28
|
+
}
|
|
29
|
+
interface R2BucketLike {
|
|
30
|
+
get(key: string): Promise<unknown>;
|
|
31
|
+
put(key: string, value: unknown): Promise<unknown>;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
interface CloudflareEnv {
|
|
35
|
+
/** Astro session store (adapter default). */
|
|
36
|
+
SESSION?: KVNamespaceLike;
|
|
37
|
+
/** Tenant config + catalog cache KV. */
|
|
38
|
+
TENANT_CACHE?: KVNamespaceLike;
|
|
39
|
+
/** Rehosted product images. */
|
|
40
|
+
PRODUCT_IMAGES?: R2BucketLike;
|
|
41
|
+
/**
|
|
42
|
+
* Versioned tenant static assets (`public/**`), keyed
|
|
43
|
+
* `tenants/<id>/<versionId>/<rel>`. Served by src/pages/tenants/[id]/[...path].
|
|
44
|
+
*/
|
|
45
|
+
TENANT_ASSETS?: R2BucketLike;
|
|
46
|
+
// Secrets / vars surfaced at runtime.
|
|
47
|
+
TOT_CATALOG_BASE_URL?: string;
|
|
48
|
+
TOT_API_KEY?: string;
|
|
49
|
+
TOT_SECRET_KEY?: string;
|
|
50
|
+
TOT_ACCESS_TOKEN?: string;
|
|
51
|
+
PUBLIC_CF_ANALYTICS_TOKEN?: string;
|
|
52
|
+
/** HMAC secret for signing/verifying customization preview tokens (D4). */
|
|
53
|
+
CUSTOMIZATION_PREVIEW_SECRET?: string;
|
|
54
|
+
/**
|
|
55
|
+
* FoxyCart (Track A). `FOXY_STORE_SUBDOMAIN` overrides the tenant's default
|
|
56
|
+
* demo store subdomain (swap the demo store with zero code change).
|
|
57
|
+
* `FOXY_CART_SECRET` is the store's cart-validation secret for signed mode —
|
|
58
|
+
* absent in the unsigned demo (validation OFF), present on preview/live.
|
|
59
|
+
*/
|
|
60
|
+
FOXY_STORE_SUBDOMAIN?: string;
|
|
61
|
+
/** Branded custom checkout domain override (e.g. checkout.preview.tokenoftrust.com). */
|
|
62
|
+
FOXY_STORE_DOMAIN?: string;
|
|
63
|
+
FOXY_CART_SECRET?: string;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
declare global {
|
|
67
|
+
// Alias used elsewhere.
|
|
68
|
+
type KVNamespace = KVNamespaceLike;
|
|
69
|
+
type R2Bucket = R2BucketLike;
|
|
70
|
+
|
|
71
|
+
namespace App {
|
|
72
|
+
interface Locals {
|
|
73
|
+
/** Tenant resolved from the request Host by src/middleware. */
|
|
74
|
+
tenant: TenantConfig;
|
|
75
|
+
/**
|
|
76
|
+
* Effective capability flags for the resolved tenant (commerce defaults
|
|
77
|
+
* unless siteType:"marketing"/explicit features). Chrome, layout islands,
|
|
78
|
+
* sitemap, and commerce routes gate on these — no tenant-identity checks.
|
|
79
|
+
*/
|
|
80
|
+
features: TenantFeatures;
|
|
81
|
+
/** Catalog client bound to the resolved tenant's data scope. */
|
|
82
|
+
tot: ToTClient;
|
|
83
|
+
/**
|
|
84
|
+
* Content provider for this request — overlays any published "Own Your
|
|
85
|
+
* Look" content for the resolved customizationEnv over Storyblok/local.
|
|
86
|
+
*/
|
|
87
|
+
content: ContentProvider;
|
|
88
|
+
/** Inline CSS-variable theme block already serialized for <head>. */
|
|
89
|
+
themeStyle: string;
|
|
90
|
+
/** Absolute canonical base for this request. */
|
|
91
|
+
canonicalBase: string;
|
|
92
|
+
/**
|
|
93
|
+
* Path prefix the request was routed under: "" for host-routed requests,
|
|
94
|
+
* or "/<domain>" when a `/<domain>/*` path segment selected the tenant
|
|
95
|
+
* (DNS-free local/preview routing). The Layout keeps root-absolute links
|
|
96
|
+
* inside this prefix on the client.
|
|
97
|
+
*/
|
|
98
|
+
basePath: string;
|
|
99
|
+
/**
|
|
100
|
+
* Which customization environment to serve (D4). "live" on production
|
|
101
|
+
* hosts; "test" only behind a valid, tenant-bound preview token on a
|
|
102
|
+
* preview host. Downstream theme/content selection reads this.
|
|
103
|
+
*/
|
|
104
|
+
customizationEnv: CustomizationEnv;
|
|
105
|
+
/**
|
|
106
|
+
* Per-request CSP nonce (D2). Placed on the platform's own inline
|
|
107
|
+
* <style>/<script is:inline> tags so a strict CSP can allow them without
|
|
108
|
+
* `unsafe-inline`.
|
|
109
|
+
*/
|
|
110
|
+
cspNonce: string;
|
|
111
|
+
/**
|
|
112
|
+
* Resolved FoxyCart cart/checkout facts for this request (loader src, cart
|
|
113
|
+
* URL, mode). Present only for commerce tenants with a FoxyCart store;
|
|
114
|
+
* undefined for marketing tenants or when no store is configured.
|
|
115
|
+
*/
|
|
116
|
+
commerce?: ResolvedFoxyCommerce;
|
|
117
|
+
/**
|
|
118
|
+
* Effective, per-environment capability switches (cart/checkout, age
|
|
119
|
+
* verification, excise tax) for this request. Resolved from the tenant's
|
|
120
|
+
* build-time default overridden by the published capabilities artifact for
|
|
121
|
+
* the current customizationEnv — so the value reflects exactly what a PR
|
|
122
|
+
* promoted to THIS environment.
|
|
123
|
+
*/
|
|
124
|
+
capabilities: CapabilityConfig;
|
|
125
|
+
/**
|
|
126
|
+
* Signed-in viewer for a login-gated preview host (set by the demo login
|
|
127
|
+
* gate once a valid, resource-bound `tot_session` cookie is present). Carries
|
|
128
|
+
* the email + the role(s)/capability tot20 resolved on this tenant, so chrome
|
|
129
|
+
* can show "signed in as … — owner/developer/staff". Absent on open or
|
|
130
|
+
* password-gated hosts.
|
|
131
|
+
*/
|
|
132
|
+
viewer?: {
|
|
133
|
+
email: string;
|
|
134
|
+
roles: string[];
|
|
135
|
+
capability: string;
|
|
136
|
+
};
|
|
137
|
+
/** Cloudflare runtime (env bindings) — provided by the adapter on Workers. */
|
|
138
|
+
runtime?: { env: CloudflareEnv };
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
interface ImportMetaEnv {
|
|
144
|
+
readonly TOT_CATALOG_BASE_URL?: string;
|
|
145
|
+
readonly TOT_API_KEY?: string;
|
|
146
|
+
readonly TOT_SECRET_KEY?: string;
|
|
147
|
+
readonly TOT_ACCESS_TOKEN?: string;
|
|
148
|
+
readonly PUBLIC_CF_ANALYTICS_TOKEN?: string;
|
|
149
|
+
readonly CUSTOMIZATION_PREVIEW_SECRET?: string;
|
|
150
|
+
}
|
|
151
|
+
interface ImportMeta {
|
|
152
|
+
readonly env: ImportMetaEnv;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
export {};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# ensure-preview-reconcile-secret.sh — resolve-or-MINT PREVIEW_RECONCILE_SECRET.
|
|
2
|
+
#
|
|
3
|
+
# ONE secret shared across ALL environments (preview/test/production) — they all
|
|
4
|
+
# point at the same Gitea instance (git.tokenoftrust.com), so there is exactly one
|
|
5
|
+
# webhook-signing secret to keep in sync, not one per tier. SOURCED (not executed)
|
|
6
|
+
# by deploy.sh; idempotent.
|
|
7
|
+
#
|
|
8
|
+
# env wins; else SSM SecureString at /tot/app/storefront/preview-reconcile-secret;
|
|
9
|
+
# else MINT a fresh random value and store it to that SAME SSM param — the first
|
|
10
|
+
# deploy that ever needs it creates it, every deploy after (any environment)
|
|
11
|
+
# resolves the identical value. Silent no-op if aws/openssl are unavailable (the
|
|
12
|
+
# caller then just skips whatever needs this secret, same fail-open shape as the
|
|
13
|
+
# other resolvers in this dir).
|
|
14
|
+
if [ -z "${PREVIEW_RECONCILE_SECRET:-}" ] && command -v aws >/dev/null 2>&1; then
|
|
15
|
+
_prs_ssm_name="/tot/app/storefront/preview-reconcile-secret"
|
|
16
|
+
_prs_region="${AWS_REGION:-us-east-1}"
|
|
17
|
+
_prs_val="$(aws ssm get-parameter --name "$_prs_ssm_name" --with-decryption --region "$_prs_region" --query Parameter.Value --output text 2>/dev/null || true)"
|
|
18
|
+
if [ -n "$_prs_val" ] && [ "$_prs_val" != "None" ]; then
|
|
19
|
+
export PREVIEW_RECONCILE_SECRET="$_prs_val"
|
|
20
|
+
echo "Loaded PREVIEW_RECONCILE_SECRET from SSM ($_prs_ssm_name)"
|
|
21
|
+
elif command -v openssl >/dev/null 2>&1; then
|
|
22
|
+
_prs_val="$(openssl rand -hex 32)"
|
|
23
|
+
if aws ssm put-parameter --name "$_prs_ssm_name" --type SecureString --value "$_prs_val" --region "$_prs_region" --query Version --output text >/dev/null 2>&1; then
|
|
24
|
+
export PREVIEW_RECONCILE_SECRET="$_prs_val"
|
|
25
|
+
echo "Minted a new PREVIEW_RECONCILE_SECRET and stored it to SSM ($_prs_ssm_name) — shared across all environments."
|
|
26
|
+
else
|
|
27
|
+
echo "Failed to mint/store PREVIEW_RECONCILE_SECRET to SSM ($_prs_ssm_name) — leaving it unset." >&2
|
|
28
|
+
fi
|
|
29
|
+
fi
|
|
30
|
+
unset _prs_val _prs_ssm_name _prs_region
|
|
31
|
+
fi
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# resolve-cf-token.sh — resolve CLOUDFLARE_API_TOKEN into the environment for
|
|
2
|
+
# Cloudflare CLI/API work. SOURCED (not executed) by provision.sh + deploy.sh so it
|
|
3
|
+
# exports into the caller. Single source of truth for the token + its fallback chain.
|
|
4
|
+
#
|
|
5
|
+
# Precedence: an already-set CLOUDFLARE_API_TOKEN in the env ALWAYS wins (CI injects
|
|
6
|
+
# it that way). Otherwise try each SSM SecureString param named in
|
|
7
|
+
# CLOUDFLARE_API_TOKEN_SSM (space-separated, PRIORITY ORDER), exporting the first
|
|
8
|
+
# that resolves:
|
|
9
|
+
# 1. /tot/cloudflare/provision-token broad control-plane — R2 + DNS + SSL/Certs
|
|
10
|
+
# + Workers Routes/Scripts + Zone:Read.
|
|
11
|
+
# 2. /tot/cloudflare/storefront-r2-token FALLBACK — narrower R2 + DNS. Keeps the R2
|
|
12
|
+
# and DNS steps working before the broad
|
|
13
|
+
# token is provisioned. NOTE: under-scoped
|
|
14
|
+
# for SSL-for-SaaS (Step 6) / Workers Routes,
|
|
15
|
+
# so those steps may still skip or warn.
|
|
16
|
+
#
|
|
17
|
+
# Set CLOUDFLARE_API_TOKEN_SSM="" to disable the SSM lookup entirely (e.g. to force
|
|
18
|
+
# wrangler's OAuth). Silent no-op if the token is already set, the aws CLI is absent,
|
|
19
|
+
# or nothing resolves — callers then just skip the CF-dependent steps.
|
|
20
|
+
#
|
|
21
|
+
# Secrets Manager works too: swap the fetch for
|
|
22
|
+
# aws secretsmanager get-secret-value --secret-id "$_cf_name" --query SecretString --output text
|
|
23
|
+
# Each candidate is VERIFIED with Cloudflare before it's accepted, so a
|
|
24
|
+
# present-but-invalid param (expired/revoked/broken — not just a missing one) falls
|
|
25
|
+
# through to the next in the chain instead of poisoning the run. `/user/tokens/verify`
|
|
26
|
+
# works for any token to check itself. If curl is unavailable, verification is
|
|
27
|
+
# skipped and the first resolvable token is taken as-is.
|
|
28
|
+
_cf_verify() {
|
|
29
|
+
command -v curl >/dev/null 2>&1 || return 0 # can't verify → accept
|
|
30
|
+
curl -fsS -H "Authorization: Bearer $1" \
|
|
31
|
+
https://api.cloudflare.com/client/v4/user/tokens/verify 2>/dev/null \
|
|
32
|
+
| grep -q '"status":"active"'
|
|
33
|
+
}
|
|
34
|
+
CLOUDFLARE_API_TOKEN_SSM="${CLOUDFLARE_API_TOKEN_SSM-/tot/cloudflare/provision-token /tot/cloudflare/storefront-r2-token}"
|
|
35
|
+
if [ -z "${CLOUDFLARE_API_TOKEN:-}" ] && [ -n "$CLOUDFLARE_API_TOKEN_SSM" ] && command -v aws >/dev/null 2>&1; then
|
|
36
|
+
for _cf_name in $CLOUDFLARE_API_TOKEN_SSM; do
|
|
37
|
+
_cf_tok="$(aws ssm get-parameter --name "$_cf_name" --with-decryption --query Parameter.Value --output text 2>/dev/null || true)"
|
|
38
|
+
[ -n "$_cf_tok" ] && [ "$_cf_tok" != "None" ] || continue
|
|
39
|
+
if ! _cf_verify "$_cf_tok"; then
|
|
40
|
+
echo "Skipping $_cf_name — token failed Cloudflare verify (expired/revoked/invalid)"
|
|
41
|
+
continue
|
|
42
|
+
fi
|
|
43
|
+
export CLOUDFLARE_API_TOKEN="$_cf_tok"
|
|
44
|
+
echo "Loaded CLOUDFLARE_API_TOKEN from SSM ($_cf_name)"
|
|
45
|
+
break
|
|
46
|
+
done
|
|
47
|
+
unset _cf_tok _cf_name
|
|
48
|
+
fi
|