@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,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-vendor theme registry.
|
|
3
|
+
*
|
|
4
|
+
* A tenant's theme override lives at the colocated repo-root path
|
|
5
|
+
* `tenants/<tenant_id>/theme.json` (a DeepPartial<ThemeTokens> — only the tokens
|
|
6
|
+
* you want to override; the reference theme fills the rest) and it's
|
|
7
|
+
* auto-discovered at build time. This is the seam the brand adopter targets: its
|
|
8
|
+
* `out/branding/<tenant>.theme.json` output is written to `tenants/<id>/theme.json`.
|
|
9
|
+
*
|
|
10
|
+
* Files may include documentation keys (`_vendor`, `_note`, `$schema`); those are
|
|
11
|
+
* stripped so only real tokens reach the theme merge.
|
|
12
|
+
*
|
|
13
|
+
* Depth: from src/themes/tenants/ the repo root is five levels up
|
|
14
|
+
* (tenants→themes→src→storefront→apps→root).
|
|
15
|
+
*/
|
|
16
|
+
import type { DeepPartial, ThemeTokens } from "@tot/public-runtime";
|
|
17
|
+
|
|
18
|
+
const modules = import.meta.glob<Record<string, unknown>>(
|
|
19
|
+
"../../../../../tenants/*/theme.json",
|
|
20
|
+
{
|
|
21
|
+
eager: true,
|
|
22
|
+
import: "default",
|
|
23
|
+
},
|
|
24
|
+
);
|
|
25
|
+
|
|
26
|
+
function stripMeta(obj: Record<string, unknown>): DeepPartial<ThemeTokens> {
|
|
27
|
+
const out: Record<string, unknown> = {};
|
|
28
|
+
for (const [k, v] of Object.entries(obj)) {
|
|
29
|
+
if (k.startsWith("_") || k.startsWith("$")) continue;
|
|
30
|
+
out[k] = v;
|
|
31
|
+
}
|
|
32
|
+
return out as DeepPartial<ThemeTokens>;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const THEMES: Record<string, DeepPartial<ThemeTokens>> = {};
|
|
36
|
+
for (const [path, mod] of Object.entries(modules)) {
|
|
37
|
+
// Path looks like ".../tenants/<id>/theme.json" — the id is the dir segment.
|
|
38
|
+
const id = /\/tenants\/([^/]+)\/theme\.json$/.exec(path)?.[1];
|
|
39
|
+
if (!id) continue;
|
|
40
|
+
THEMES[id] = stripMeta(mod as Record<string, unknown>);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** Theme-token overrides for a tenant, or {} if the vendor has no theme file. */
|
|
44
|
+
export function getTenantTheme(tenantId: string): DeepPartial<ThemeTokens> {
|
|
45
|
+
return THEMES[tenantId] ?? {};
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** Tenant ids that ship a theme file (for diagnostics / listing). */
|
|
49
|
+
export function tenantsWithThemes(): string[] {
|
|
50
|
+
return Object.keys(THEMES);
|
|
51
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"extends": "astro/tsconfigs/strict",
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
"strict": true,
|
|
5
|
+
"noUncheckedIndexedAccess": true,
|
|
6
|
+
"verbatimModuleSyntax": false,
|
|
7
|
+
"jsx": "react-jsx",
|
|
8
|
+
"jsxImportSource": "preact",
|
|
9
|
+
"baseUrl": ".",
|
|
10
|
+
"paths": {
|
|
11
|
+
"@/*": ["src/*"]
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"include": [".astro/types.d.ts", "src/**/*", "*.config.mjs", "env.d.ts"],
|
|
15
|
+
"exclude": ["dist", "node_modules"]
|
|
16
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { defineConfig } from "vitest/config";
|
|
2
|
+
import { fileURLToPath } from "node:url";
|
|
3
|
+
|
|
4
|
+
// Storefront unit tests — pure TS logic (tenant routing, search, query engine,
|
|
5
|
+
// image-attr resolution, edge cache, etc.). We deliberately do NOT render .astro
|
|
6
|
+
// components here: the Astro Container API needs Astro's Vite pipeline, whose
|
|
7
|
+
// vite-plugin-head crashes under vitest@2 + astro@7 (configureServer reads an
|
|
8
|
+
// undefined `ssr`). Render *logic* is extracted into testable modules instead
|
|
9
|
+
// (e.g. lib/imageAttrs.ts), and rendered output is verified on a live preview.
|
|
10
|
+
export default defineConfig({
|
|
11
|
+
// Mirror the `@` -> src alias from tsconfig so tests can import aliased modules
|
|
12
|
+
// (e.g. src/config/tenants.ts, which pulls the theme registry).
|
|
13
|
+
resolve: {
|
|
14
|
+
alias: {
|
|
15
|
+
"@": fileURLToPath(new URL("./src", import.meta.url)),
|
|
16
|
+
},
|
|
17
|
+
},
|
|
18
|
+
test: {
|
|
19
|
+
include: ["src/**/*.test.ts"],
|
|
20
|
+
environment: "node",
|
|
21
|
+
},
|
|
22
|
+
});
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
# Cloudflare Workers config for the ToT storefront (Phase 1 scaffold).
|
|
2
|
+
# Account/zone IDs and namespace IDs are PLACEHOLDERS — a human wires real
|
|
3
|
+
# values before deploy (see README.md "Pointing at live infra").
|
|
4
|
+
#
|
|
5
|
+
# NOTE: the @astrojs/cloudflare v14 adapter (via @cloudflare/vite-plugin) OWNS
|
|
6
|
+
# the worker entry ("main") — it generates dist/_worker.js at build time. We do
|
|
7
|
+
# NOT set `main` here; doing so makes the vite plugin validate a path that does
|
|
8
|
+
# not exist until after build. Bindings below are surfaced to the worker.
|
|
9
|
+
|
|
10
|
+
name = "tot-storefront"
|
|
11
|
+
compatibility_date = "2024-11-01"
|
|
12
|
+
compatibility_flags = ["nodejs_compat"]
|
|
13
|
+
account_id = "3e84256b846e0c0c8ef408d80126daac"
|
|
14
|
+
workers_dev = true
|
|
15
|
+
|
|
16
|
+
# --- Sessions -----------------------------------------------------------------
|
|
17
|
+
# The adapter enables Astro sessions backed by Cloudflare KV via a "SESSION"
|
|
18
|
+
# binding by default. Phase 1 does not use sessions (no cart/accounts), but the
|
|
19
|
+
# binding must exist for build/deploy. Placeholder id.
|
|
20
|
+
[[kv_namespaces]]
|
|
21
|
+
binding = "SESSION"
|
|
22
|
+
id = "REPLACE_WITH_SESSION_KV_NAMESPACE_ID"
|
|
23
|
+
preview_id = "REPLACE_WITH_SESSION_KV_PREVIEW_ID"
|
|
24
|
+
|
|
25
|
+
# --- Tenant config + catalog edge cache --------------------------------------
|
|
26
|
+
# Phase 1 resolves tenants from a static map (src/config/tenants.ts). This KV
|
|
27
|
+
# namespace is the migration path: per-tenant config + cached catalog reads
|
|
28
|
+
# (stale-while-revalidate) live here once we go many-tenant.
|
|
29
|
+
[[kv_namespaces]]
|
|
30
|
+
binding = "TENANT_CACHE"
|
|
31
|
+
id = "REPLACE_WITH_KV_NAMESPACE_ID"
|
|
32
|
+
preview_id = "REPLACE_WITH_KV_PREVIEW_ID"
|
|
33
|
+
|
|
34
|
+
# --- Rehosted product images -------------------------------------------------
|
|
35
|
+
# ETL rehosts Shopify-CDN images here; the <Img> component swaps placeholders
|
|
36
|
+
# for these URLs when CatalogImage.rehosted === true.
|
|
37
|
+
[[r2_buckets]]
|
|
38
|
+
binding = "PRODUCT_IMAGES"
|
|
39
|
+
bucket_name = "tot-storefront-product-images"
|
|
40
|
+
preview_bucket_name = "tot-storefront-product-images-preview"
|
|
41
|
+
|
|
42
|
+
# --- Versioned tenant static assets ------------------------------------------
|
|
43
|
+
# Tenant `public/**` (logo, CSS, binaries) served version-scoped from R2, keyed
|
|
44
|
+
# `tenants/<id>/<versionId>/<rel>` — replaces the build-time copy-tenant-assets
|
|
45
|
+
# baking for extracted/versioned tenants (decision `assets-r2-versioned`).
|
|
46
|
+
# Served by src/pages/tenants/[id]/[...path].ts. Local `wrangler dev` binds a
|
|
47
|
+
# miniflare R2; the real buckets are provisioned behind a human gate (no deploy
|
|
48
|
+
# creates them here). preview_bucket_name backs `wrangler dev --remote=false`.
|
|
49
|
+
[[r2_buckets]]
|
|
50
|
+
binding = "TENANT_ASSETS"
|
|
51
|
+
bucket_name = "tot-storefront-tenant-assets"
|
|
52
|
+
preview_bucket_name = "tot-storefront-tenant-assets-preview"
|
|
53
|
+
|
|
54
|
+
# --- Catalog edge read replica (D1) ------------------------------------------
|
|
55
|
+
# The ETL emits catalog.sql (schema + rows + FTS5); load it with
|
|
56
|
+
# `wrangler d1 execute CATALOG_DB --file out/catalog/<scope>/catalog.sql`. Reads
|
|
57
|
+
# (search/facets/PDP) are served from D1 when USE_D1_CATALOG=1 — see
|
|
58
|
+
# epics/edge-read-replica.md. Off by default; placeholder id until provisioned.
|
|
59
|
+
[[d1_databases]]
|
|
60
|
+
binding = "CATALOG_DB"
|
|
61
|
+
database_name = "tot-storefront-catalog"
|
|
62
|
+
database_id = "REPLACE_WITH_D1_DATABASE_ID"
|
|
63
|
+
|
|
64
|
+
# Cloudflare for SaaS custom hostnames are configured at the zone level, not
|
|
65
|
+
# here — Phase 1 ships one pilot subdomain. See DECISIONS.md.
|
|
66
|
+
|
|
67
|
+
[observability]
|
|
68
|
+
enabled = true
|
|
69
|
+
|
|
70
|
+
[vars]
|
|
71
|
+
# Non-secret defaults. Secrets (TOT_API_KEY etc.) come from `wrangler secret`
|
|
72
|
+
# or .dev.vars locally — never commit them.
|
|
73
|
+
PUBLIC_CF_ANALYTICS_TOKEN = "REPLACE_WITH_CF_WEB_ANALYTICS_TOKEN"
|
|
74
|
+
|
|
75
|
+
# --- Preview environment (the live storefront.<subdomain>.workers.dev) --------
|
|
76
|
+
# CI deploys this on merge to main (deploy.sh --env=preview). Resource ids are
|
|
77
|
+
# wired from provision.sh output — ids are identifiers, not secrets; the only
|
|
78
|
+
# secret (CLOUDFLARE_API_TOKEN) lives in GitHub Actions secrets. Named
|
|
79
|
+
# environments do NOT inherit top-level bindings/vars, so KV/D1/vars are repeated
|
|
80
|
+
# here; account_id, compatibility_*, observability, and workers_dev ARE inherited.
|
|
81
|
+
# Mirrors the live `storefront` worker exactly: name override so it updates that
|
|
82
|
+
# worker (not `tot-storefront-preview`), USE_D1_CATALOG=1 (edge-native read path),
|
|
83
|
+
# and NO R2 binding (the live worker has none). Runtime secrets are set via
|
|
84
|
+
# `wrangler secret` and persist across deploys — not declared here. Production is
|
|
85
|
+
# deferred: wire [env.production] from production.generated.json when ready.
|
|
86
|
+
[env.preview]
|
|
87
|
+
name = "storefront"
|
|
88
|
+
|
|
89
|
+
[[env.preview.kv_namespaces]]
|
|
90
|
+
binding = "SESSION"
|
|
91
|
+
id = "85a27218438b436db8b291c431afd0b2"
|
|
92
|
+
|
|
93
|
+
[[env.preview.kv_namespaces]]
|
|
94
|
+
binding = "TENANT_CACHE"
|
|
95
|
+
id = "d1e2d38f32d44d0bba65f59d1c3ae908"
|
|
96
|
+
|
|
97
|
+
[[env.preview.d1_databases]]
|
|
98
|
+
binding = "CATALOG_DB"
|
|
99
|
+
database_name = "tot-storefront-t-catalog"
|
|
100
|
+
database_id = "a7bb6565-d23d-4581-a50a-ace5c12b4cba"
|
|
101
|
+
|
|
102
|
+
# Versioned tenant static-asset bucket (named envs do NOT inherit the top-level
|
|
103
|
+
# binding). Required for src/pages/tenants/[id]/[...path].ts to serve from R2 in
|
|
104
|
+
# the live preview worker. Bucket provisioning is a human gate — CI does not
|
|
105
|
+
# create R2 buckets; wire the real bucket_name before enabling versioned tenants.
|
|
106
|
+
[[env.preview.r2_buckets]]
|
|
107
|
+
binding = "TENANT_ASSETS"
|
|
108
|
+
bucket_name = "tot-storefront-tenant-assets"
|
|
109
|
+
preview_bucket_name = "tot-storefront-tenant-assets-preview"
|
|
110
|
+
|
|
111
|
+
[env.preview.vars]
|
|
112
|
+
PUBLIC_CF_ANALYTICS_TOKEN = "REPLACE_WITH_CF_WEB_ANALYTICS_TOKEN"
|
|
113
|
+
# Site-wide maintenance kill-switch (src/lib/maintenance.ts). "1" serves a static
|
|
114
|
+
# "Coming Soon" holding page for EVERY host + path, before tenant resolution or
|
|
115
|
+
# any gate — the fail-safe lever to take the whole storefront offline. Back
|
|
116
|
+
# online: set to "0" (or remove) and redeploy.
|
|
117
|
+
MAINTENANCE_MODE = "0"
|
|
118
|
+
# D1 edge catalog is opt-in but the preview D1 isn't seeded yet (CI doesn't seed
|
|
119
|
+
# D1), so products 404 against it. Serve the bundled fixture snapshot (same source
|
|
120
|
+
# localhost uses) for the demo; flip back to "1" once the D1 catalog is seeded.
|
|
121
|
+
USE_D1_CATALOG = "0"
|
|
122
|
+
# Demo access gate for named hosts. Two modes (src/lib/auth/loginGate.ts):
|
|
123
|
+
# DEMO_GATE_MODE = "password" (default) → Basic-Auth shared secret DEMO_GATE_PASSWORD
|
|
124
|
+
# DEMO_GATE_MODE = "login" → real "Sign in with ToT" (email code → signed
|
|
125
|
+
# id_token, verified offline via JWKS)
|
|
126
|
+
# CUTOVER (this branch): flip to "login". PRECONDITIONS before deploy, or the gate
|
|
127
|
+
# fails CLOSED and locks out the demo:
|
|
128
|
+
# 1. TOT_CORE_URL points at a live tot IdP serving /api/internal/access-code/{send,verify}
|
|
129
|
+
# + /api/.well-known/jwks.json (proposed: qa.tokenoftrust.com — CONFIRM).
|
|
130
|
+
# 2. Secrets set: wrangler secret put TOT_API_KEY --env preview
|
|
131
|
+
# wrangler secret put TOT_SECRET_KEY --env preview (requireSecretKey endpoints)
|
|
132
|
+
# 3. tot20 has a grant roster for resource "bigdvapor.net" (else nobody can sign in).
|
|
133
|
+
# Wildcard gate: EVERY tenant subdomain on these zones (present + future) plus
|
|
134
|
+
# the shared path-prefix host and workers.dev host require login — no per-host
|
|
135
|
+
# maintenance. `*.tokenoftrust.store` covers bigdvapor.tokenoftrust.store,
|
|
136
|
+
# storefront.tokenoftrust.store (path-prefix), preview.*, etc. The image CDN
|
|
137
|
+
# (cdn.tokenoftrust.store) is a no-worker carve-out route, so it never reaches
|
|
138
|
+
# this middleware and is unaffected.
|
|
139
|
+
# NB: the bare apex `tokenoftrust.store` is listed EXPLICITLY — the `*.` wildcard
|
|
140
|
+
# matches subdomains only, so without this a path-prefix route like
|
|
141
|
+
# `tokenoftrust.store/<tenant>/` would bypass the gate. The apex `/` still resolves
|
|
142
|
+
# to the exempt home tenant (public); only its non-home tenant paths are gated.
|
|
143
|
+
DEMO_GATE_HOSTS = "*.tokenoftrust.store,tokenoftrust.store,*.storefront.store,storefront.tokenoftrust.workers.dev"
|
|
144
|
+
DEMO_GATE_MODE = "login"
|
|
145
|
+
DEMO_GATE_REALM = "bigdvapor demo — enter the shared password"
|
|
146
|
+
# tot IdP base for access-code + JWKS (HOST-ONLY; the code appends /api/... itself
|
|
147
|
+
# — /api/internal/access-code/{federated-send,federated-verify}, /api/.well-known/
|
|
148
|
+
# jwks.json, /api/clients/developerInvites). Do NOT add a /api suffix or every URL
|
|
149
|
+
# doubles to /api/api/... (#8425 federated sign-in + dev-invite depend on this).
|
|
150
|
+
TOT_CORE_URL = "https://qa.tokenoftrust.com"
|
|
151
|
+
# Storefront's OWN app identity — the `aud` of tot20's federated multi-tenant
|
|
152
|
+
# id_token (getFederatedVerifier pins aud to this). NON-secret. (#8425)
|
|
153
|
+
STOREFRONT_APP_DOMAIN = "storefront.tokenoftrust.com"
|
|
154
|
+
# Option-B MCP subject-capability writer — the FULL endpoint URL (used as-is by
|
|
155
|
+
# recordDeveloperAppBinding, not a base). NON-secret. Its auth is the SECRET
|
|
156
|
+
# MCP_SERVICE_SHARED_SECRET (wrangler secret; must match the tot-mcp side). (#8425)
|
|
157
|
+
MCP_APP_BINDING_URL = "https://mcp.qa.tokenoftrust.com/internal/dev-access/subject-capability"
|
|
158
|
+
# Forge/ShipLoop JSON-RPC endpoint for the storefront forge client. createForgeClient()'s
|
|
159
|
+
# FetchTransport POSTs here DIRECTLY, so this is the FULL `POST /mcp` endpoint INCLUDING the
|
|
160
|
+
# `/mcp` suffix (unlike TOT_CORE_URL, which is host-only and appends /api itself). Without it
|
|
161
|
+
# createForgeClient() throws ForgeConfigError and the reconcile's evidence push-to-MCP
|
|
162
|
+
# (reportPreviewEvidence, ADR 0010) silently no-ops → change_ready never sees a report.
|
|
163
|
+
# NON-secret.
|
|
164
|
+
MCP_BASE_URL = "https://mcp.qa.tokenoftrust.com/mcp"
|
|
165
|
+
# FoxyCart cart/checkout (Track A). The store defaults to "tot-preview" (from the
|
|
166
|
+
# tenant config) — the SHARED, ToT-owned test store (tot-preview.foxycart.com,
|
|
167
|
+
# test mode, cart validation OFF for the unsigned demo). No override needed here
|
|
168
|
+
# unless a different store is used per environment. Browse worker now lives on
|
|
169
|
+
# tokenoftrust.store (native CF zone), so the FoxyCart branded checkout host is a
|
|
170
|
+
# sibling under it — e.g. live = secure.tokenoftrust.store (CNAME → the live
|
|
171
|
+
# FoxyCart store, DNS-only). Preview test checkout stays UNBRANDED on
|
|
172
|
+
# tot-preview.foxycart.com. Merchant live = secure.<merchant>.com → their store.
|
|
173
|
+
# Loader still loads from cdn.foxycart.com/<store>/.
|
|
174
|
+
# FOXY_STORE_SUBDOMAIN = "tot-preview"
|
|
175
|
+
# FOXY_STORE_DOMAIN = "secure.tokenoftrust.store"
|
package/package.json
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@tokenoftrust/storefront-runner",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"license": "SEE LICENSE IN LICENSE",
|
|
5
|
+
"description": "World-shareable storefront runner: multi-tenant renderer on Astro/Cloudflare. No control plane.",
|
|
6
|
+
"packageManager": "pnpm@11.9.0",
|
|
7
|
+
"engines": {
|
|
8
|
+
"node": ">=24"
|
|
9
|
+
},
|
|
10
|
+
"scripts": {
|
|
11
|
+
"dev": "pnpm --filter @tot/storefront-runner dev",
|
|
12
|
+
"build": "pnpm --filter @tot/storefront-runner build",
|
|
13
|
+
"typecheck": "pnpm -r typecheck",
|
|
14
|
+
"depcruise": "pnpm --filter @tot/storefront-runner depcruise"
|
|
15
|
+
},
|
|
16
|
+
"devDependencies": {
|
|
17
|
+
"typescript": "^5.6.3"
|
|
18
|
+
},
|
|
19
|
+
"publishConfig": {
|
|
20
|
+
"access": "public",
|
|
21
|
+
"registry": "https://registry.npmjs.org"
|
|
22
|
+
}
|
|
23
|
+
}
|
|
@@ -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.
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@tot/public-runtime",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"private": true,
|
|
5
|
+
"license": "SEE LICENSE IN LICENSE",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"main": "./src/index.ts",
|
|
8
|
+
"types": "./src/index.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": "./src/index.ts"
|
|
11
|
+
},
|
|
12
|
+
"scripts": {
|
|
13
|
+
"typecheck": "tsc --noEmit",
|
|
14
|
+
"build": "tsc",
|
|
15
|
+
"test": "vitest run",
|
|
16
|
+
"test:watch": "vitest"
|
|
17
|
+
},
|
|
18
|
+
"devDependencies": {
|
|
19
|
+
"typescript": "^5.6.3",
|
|
20
|
+
"vitest": "^2.1.8"
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Binary-vs-text path classification — the SINGLE source of truth shared by the
|
|
3
|
+
* write side (seed's `buildSeedFiles`) and the read side (the Gitea read seam).
|
|
4
|
+
*
|
|
5
|
+
* The extraction/preview loop moves a tenant's files through two seams that MUST
|
|
6
|
+
* agree on what is binary, or a file's bytes round-trip differently on each side
|
|
7
|
+
* and binaries corrupt:
|
|
8
|
+
*
|
|
9
|
+
* - WRITE: `customization-seed.ts#buildSeedFiles` base64-encodes binaries when
|
|
10
|
+
* it materializes the colocated subtree into a flat Gitea-repo seed.
|
|
11
|
+
* - READ: `gitea-source.ts#createGiteaHttpSource().readContent` base64-encodes
|
|
12
|
+
* binaries when it reads them back at a commit for the reconcile/assets sink.
|
|
13
|
+
*
|
|
14
|
+
* Keeping the extension set here (and importing it in both) guarantees the two
|
|
15
|
+
* seams classify identically. Runtime-portable (no `node:*`) so it is safe in the
|
|
16
|
+
* renderer, the Worker, the publish pipeline, and unit tests alike.
|
|
17
|
+
*
|
|
18
|
+
* SVG is intentionally TEXT (it is XML the verbatim-serve path can nonce-inject);
|
|
19
|
+
* CSS/JSON/HTML/JS/TXT/XML are text. Everything in the set below is opaque bytes.
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* File extensions treated as BINARY (base64-encoded so raw bytes round-trip).
|
|
24
|
+
* Every other extension is emitted/read as UTF-8 text.
|
|
25
|
+
*/
|
|
26
|
+
export const BINARY_EXTENSIONS: ReadonlySet<string> = new Set([
|
|
27
|
+
// raster images
|
|
28
|
+
"png",
|
|
29
|
+
"jpg",
|
|
30
|
+
"jpeg",
|
|
31
|
+
"gif",
|
|
32
|
+
"webp",
|
|
33
|
+
"avif",
|
|
34
|
+
"ico",
|
|
35
|
+
"bmp",
|
|
36
|
+
"tiff",
|
|
37
|
+
"tif",
|
|
38
|
+
// fonts
|
|
39
|
+
"woff",
|
|
40
|
+
"woff2",
|
|
41
|
+
"ttf",
|
|
42
|
+
"otf",
|
|
43
|
+
"eot",
|
|
44
|
+
// media
|
|
45
|
+
"mp4",
|
|
46
|
+
"webm",
|
|
47
|
+
"mov",
|
|
48
|
+
"mp3",
|
|
49
|
+
"wav",
|
|
50
|
+
"ogg",
|
|
51
|
+
// docs / archives
|
|
52
|
+
"pdf",
|
|
53
|
+
"zip",
|
|
54
|
+
"gz",
|
|
55
|
+
]);
|
|
56
|
+
|
|
57
|
+
/** The lowercased extension of `path` (no leading dot), or "" when it has none. */
|
|
58
|
+
export function extensionOf(path: string): string {
|
|
59
|
+
const dot = path.lastIndexOf(".");
|
|
60
|
+
const slash = path.lastIndexOf("/");
|
|
61
|
+
if (dot === -1 || dot < slash) return "";
|
|
62
|
+
return path.slice(dot + 1).toLowerCase();
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/** True when `path` names a binary asset (base64), false for text. */
|
|
66
|
+
export function isBinaryPath(path: string): boolean {
|
|
67
|
+
return BINARY_EXTENSIONS.has(extensionOf(path));
|
|
68
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tenant capability switches — config that travels with the PR (Track A/B).
|
|
3
|
+
*
|
|
4
|
+
* Three independently-toggleable capabilities:
|
|
5
|
+
* - cartCheckout — FoxyCart cart/checkout ("Powered by FoxyCart", Track A)
|
|
6
|
+
* - ageVerification — Token of Trust age/identity gate (21+ regulated)
|
|
7
|
+
* - exciseTax — excise tax collection at checkout (Track B, tot-foxycart)
|
|
8
|
+
*
|
|
9
|
+
* The source of truth is the tenant repo's `.tot/config.json` `capabilities`
|
|
10
|
+
* block — so a change is a code change that travels with a PR. Resolution is
|
|
11
|
+
* per-environment on the SAME published-artifact spine as theme/content:
|
|
12
|
+
* localhost → working-tree config (ungated, HMR)
|
|
13
|
+
* preview → PR-head config reconciled to the `test` pointer
|
|
14
|
+
* live → the promoted config on the `live` pointer (evidence-gated promote)
|
|
15
|
+
* so each environment's capability state is independently pinned and a shared-env
|
|
16
|
+
* change is PR-gated by construction. `readPublishedArtifact(env, capabilities
|
|
17
|
+
* ArtifactPath)` overrides the build-time `TenantConfig.capabilities` default.
|
|
18
|
+
*/
|
|
19
|
+
import type { TenantConfig } from "./tenant.js";
|
|
20
|
+
|
|
21
|
+
export interface CapabilityState {
|
|
22
|
+
enabled: boolean;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface CapabilityConfig {
|
|
26
|
+
/** FoxyCart cart/checkout: loader + signed add-to-cart. */
|
|
27
|
+
cartCheckout: CapabilityState;
|
|
28
|
+
/** Token of Trust age/identity verification (soft 21+ gate now; full ToT later). */
|
|
29
|
+
ageVerification: CapabilityState;
|
|
30
|
+
/** Excise tax collection at checkout (materialized in the FoxyCart cart). */
|
|
31
|
+
exciseTax: CapabilityState;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export type CapabilityKey = keyof CapabilityConfig;
|
|
35
|
+
|
|
36
|
+
/** Stable capability order + labels for any surface that lists them. */
|
|
37
|
+
export const CAPABILITY_KEYS: CapabilityKey[] = [
|
|
38
|
+
"cartCheckout",
|
|
39
|
+
"ageVerification",
|
|
40
|
+
"exciseTax",
|
|
41
|
+
];
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Canonical published-artifact path for a tenant's capabilities. Same colocated
|
|
45
|
+
* `tenants/<id>/…` contract as theme/content, so the reconcile pipeline keys the
|
|
46
|
+
* KV artifact by it and the storefront reads it back per environment.
|
|
47
|
+
*/
|
|
48
|
+
export function capabilitiesArtifactPath(tenantId: string): string {
|
|
49
|
+
return `tenants/${tenantId}/capabilities.json`;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Resolve a tenant's effective capabilities: build-time `TenantConfig.capabilities`
|
|
54
|
+
* default (itself defaulted from siteType/compliance), then any per-environment
|
|
55
|
+
* published `override` layered on top. Undefined entries fall through, so a PR can
|
|
56
|
+
* flip ONE capability without restating the rest.
|
|
57
|
+
*/
|
|
58
|
+
export function resolveCapabilities(
|
|
59
|
+
tenant: Pick<TenantConfig, "capabilities" | "compliance" | "siteType">,
|
|
60
|
+
override?: Partial<CapabilityConfig> | null,
|
|
61
|
+
): CapabilityConfig {
|
|
62
|
+
const c = tenant.capabilities;
|
|
63
|
+
const base: CapabilityConfig = {
|
|
64
|
+
// Commerce tenants get a cart by default; marketing tenants don't.
|
|
65
|
+
cartCheckout: {
|
|
66
|
+
enabled: c?.cartCheckout?.enabled ?? tenant.siteType !== "marketing",
|
|
67
|
+
},
|
|
68
|
+
// Regulated tenants (an age minimum) verify by default.
|
|
69
|
+
ageVerification: {
|
|
70
|
+
enabled: c?.ageVerification?.enabled ?? !!tenant.compliance?.minAge,
|
|
71
|
+
},
|
|
72
|
+
// Nicotine/vape tenants collect excise by default.
|
|
73
|
+
exciseTax: {
|
|
74
|
+
enabled: c?.exciseTax?.enabled ?? !!tenant.compliance?.nicotineWarning,
|
|
75
|
+
},
|
|
76
|
+
};
|
|
77
|
+
if (!override) return base;
|
|
78
|
+
return {
|
|
79
|
+
cartCheckout: {
|
|
80
|
+
enabled: override.cartCheckout?.enabled ?? base.cartCheckout.enabled,
|
|
81
|
+
},
|
|
82
|
+
ageVerification: {
|
|
83
|
+
enabled: override.ageVerification?.enabled ?? base.ageVerification.enabled,
|
|
84
|
+
},
|
|
85
|
+
exciseTax: {
|
|
86
|
+
enabled: override.exciseTax?.enabled ?? base.exciseTax.enabled,
|
|
87
|
+
},
|
|
88
|
+
};
|
|
89
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Canonical catalog data contract (shared types). The tot20 HTTP catalog API has
|
|
3
|
+
* been retired — the storefront reads from a Cloudflare D1 edge replica — so these
|
|
4
|
+
* are now the in-process shapes shared across the catalog path:
|
|
5
|
+
* - packages/migration — the LOAD step's bulk-upsert result shape (counts/rows).
|
|
6
|
+
* - apps/storefront — the read clients' (D1 / fixture) list-query, facet,
|
|
7
|
+
* and list-response shapes.
|
|
8
|
+
*
|
|
9
|
+
* Tenant scoping is by `appDomain` (== TenantConfig.tot_data_scope).
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import type { CatalogCollection, CatalogProduct } from "./product.js";
|
|
13
|
+
|
|
14
|
+
/** POST /api/catalog/products:bulkUpsert */
|
|
15
|
+
export interface BulkUpsertProductsRequest {
|
|
16
|
+
appDomain?: string;
|
|
17
|
+
totApiKey?: string;
|
|
18
|
+
totSecretKey?: string;
|
|
19
|
+
products: CatalogProduct[];
|
|
20
|
+
/** When true, server skips records whose source_checksum is unchanged. */
|
|
21
|
+
incremental?: boolean;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface UpsertResultRow {
|
|
25
|
+
source_id: string;
|
|
26
|
+
handle: string;
|
|
27
|
+
action: "inserted" | "updated" | "skipped" | "error";
|
|
28
|
+
message?: string;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface BulkUpsertProductsResponse {
|
|
32
|
+
results: UpsertResultRow[];
|
|
33
|
+
counts: { inserted: number; updated: number; skipped: number; error: number };
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** POST /api/catalog/collections:bulkUpsert */
|
|
37
|
+
export interface BulkUpsertCollectionsRequest {
|
|
38
|
+
appDomain?: string;
|
|
39
|
+
totApiKey?: string;
|
|
40
|
+
totSecretKey?: string;
|
|
41
|
+
collections: CatalogCollection[];
|
|
42
|
+
incremental?: boolean;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export interface BulkUpsertCollectionsResponse {
|
|
46
|
+
results: UpsertResultRow[];
|
|
47
|
+
counts: { inserted: number; updated: number; skipped: number; error: number };
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** GET /api/catalog/products (list + facet, querystring-driven) */
|
|
51
|
+
export interface ListProductsQuery {
|
|
52
|
+
collection?: string; // collection handle
|
|
53
|
+
sort?: "featured" | "price-asc" | "price-desc" | "newest" | "title-asc";
|
|
54
|
+
/** Facet filters: option name -> selected values. Encoded as repeated query params. */
|
|
55
|
+
filters?: Record<string, string[]>;
|
|
56
|
+
priceMin?: number;
|
|
57
|
+
priceMax?: number;
|
|
58
|
+
availability?: "in-stock" | "all";
|
|
59
|
+
tags?: string[];
|
|
60
|
+
page?: number;
|
|
61
|
+
pageSize?: number;
|
|
62
|
+
q?: string; // free-text search
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export interface FacetValue {
|
|
66
|
+
value: string;
|
|
67
|
+
count: number;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export interface Facet {
|
|
71
|
+
name: string; // option name or "price"/"availability"/"tags"
|
|
72
|
+
values: FacetValue[];
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export interface ListProductsResponse {
|
|
76
|
+
products: CatalogProduct[];
|
|
77
|
+
page: number;
|
|
78
|
+
pageSize: number;
|
|
79
|
+
total: number;
|
|
80
|
+
facets: Facet[];
|
|
81
|
+
}
|