@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.
Files changed (337) hide show
  1. package/LICENSE +58 -0
  2. package/README.md +40 -0
  3. package/apps/storefront/.dev.vars.example +41 -0
  4. package/apps/storefront/.env.example +24 -0
  5. package/apps/storefront/astro.config.mjs +245 -0
  6. package/apps/storefront/env.d.ts +155 -0
  7. package/apps/storefront/lib/ensure-preview-reconcile-secret.sh +31 -0
  8. package/apps/storefront/lib/resolve-cf-token.sh +48 -0
  9. package/apps/storefront/lib/resolve-tot-credentials.sh +69 -0
  10. package/apps/storefront/migrations/0000_baseline.sql +54 -0
  11. package/apps/storefront/migrations/0001_products_fts.sql +9 -0
  12. package/apps/storefront/migrations/README.md +54 -0
  13. package/apps/storefront/migrations/meta/0000_snapshot.json +347 -0
  14. package/apps/storefront/migrations/meta/0001_snapshot.json +347 -0
  15. package/apps/storefront/migrations/meta/_journal.json +20 -0
  16. package/apps/storefront/package.json +41 -0
  17. package/apps/storefront/public/brand/northwind-logo-dark.svg +4 -0
  18. package/apps/storefront/public/brand/northwind-logo-light.svg +4 -0
  19. package/apps/storefront/public/brand/northwind-og.svg +13 -0
  20. package/apps/storefront/public/favicon.svg +5 -0
  21. package/apps/storefront/public/js/dashboard-team.js +147 -0
  22. package/apps/storefront/public/js/dashboard-vendor-search.js +115 -0
  23. package/apps/storefront/src/components/Badge.astro +34 -0
  24. package/apps/storefront/src/components/Breadcrumbs.astro +45 -0
  25. package/apps/storefront/src/components/CollectionCard.astro +56 -0
  26. package/apps/storefront/src/components/EmptyState.astro +35 -0
  27. package/apps/storefront/src/components/Img.astro +68 -0
  28. package/apps/storefront/src/components/ProductCard.astro +228 -0
  29. package/apps/storefront/src/components/Section.astro +39 -0
  30. package/apps/storefront/src/components/Seo.astro +70 -0
  31. package/apps/storefront/src/components/WidgetFrame.astro +34 -0
  32. package/apps/storefront/src/components/auth/AuthBadge.astro +44 -0
  33. package/apps/storefront/src/components/chrome/AnnouncementBar.astro +108 -0
  34. package/apps/storefront/src/components/chrome/MegaMenu.astro +26 -0
  35. package/apps/storefront/src/components/chrome/NavDropdown.astro +116 -0
  36. package/apps/storefront/src/components/chrome/SiteFooter.astro +181 -0
  37. package/apps/storefront/src/components/chrome/SiteHeader.astro +181 -0
  38. package/apps/storefront/src/components/chrome/UtilityNav.astro +79 -0
  39. package/apps/storefront/src/components/commerce/FoxyLoader.astro +23 -0
  40. package/apps/storefront/src/components/commerce/PriceDisplay.astro +91 -0
  41. package/apps/storefront/src/components/commerce/ProductCarousel.astro +197 -0
  42. package/apps/storefront/src/components/commerce/ProductGrid.astro +75 -0
  43. package/apps/storefront/src/components/commerce/RatingStars.astro +58 -0
  44. package/apps/storefront/src/components/compliance/NicotineWarning.astro +33 -0
  45. package/apps/storefront/src/components/home/Hero.astro +164 -0
  46. package/apps/storefront/src/components/islands/AgeGate.tsx +105 -0
  47. package/apps/storefront/src/components/islands/ImageGallery.tsx +237 -0
  48. package/apps/storefront/src/components/islands/IsolatedAgeGate.tsx +73 -0
  49. package/apps/storefront/src/components/islands/MobileNav.tsx +239 -0
  50. package/apps/storefront/src/components/islands/QuickView.tsx +138 -0
  51. package/apps/storefront/src/components/islands/RecentlyViewed.tsx +122 -0
  52. package/apps/storefront/src/components/islands/SavedList.tsx +99 -0
  53. package/apps/storefront/src/components/islands/SearchTypeahead.tsx +248 -0
  54. package/apps/storefront/src/components/islands/VariantSelector.tsx +378 -0
  55. package/apps/storefront/src/components/marketing/CardGrid.astro +72 -0
  56. package/apps/storefront/src/components/marketing/CodeSample.astro +20 -0
  57. package/apps/storefront/src/components/marketing/CtaBand.astro +24 -0
  58. package/apps/storefront/src/components/marketing/Faq.astro +37 -0
  59. package/apps/storefront/src/components/marketing/Integrations.astro +63 -0
  60. package/apps/storefront/src/components/marketing/MarketingBlocks.astro +53 -0
  61. package/apps/storefront/src/components/marketing/MarketingCtas.astro +48 -0
  62. package/apps/storefront/src/components/marketing/MarketingHero.astro +72 -0
  63. package/apps/storefront/src/components/marketing/ProofStrip.astro +37 -0
  64. package/apps/storefront/src/components/marketing/SplitCompare.astro +53 -0
  65. package/apps/storefront/src/components/marketing/Steps.astro +36 -0
  66. package/apps/storefront/src/components/marketing/Testimonials.astro +38 -0
  67. package/apps/storefront/src/components/marketing/TrustBar.astro +22 -0
  68. package/apps/storefront/src/components/nav/Footer.astro +148 -0
  69. package/apps/storefront/src/components/nav/Header.astro +190 -0
  70. package/apps/storefront/src/components/plp/ActiveFilters.astro +79 -0
  71. package/apps/storefront/src/components/plp/FacetSidebar.astro +146 -0
  72. package/apps/storefront/src/components/plp/Pagination.astro +99 -0
  73. package/apps/storefront/src/components/plp/SortSelect.astro +64 -0
  74. package/apps/storefront/src/config/devTenants.ts +16 -0
  75. package/apps/storefront/src/config/kvTenantResolver.ts +87 -0
  76. package/apps/storefront/src/config/resolver.ts +46 -0
  77. package/apps/storefront/src/config/tenants.ts +193 -0
  78. package/apps/storefront/src/layouts/Layout.astro +303 -0
  79. package/apps/storefront/src/lib/analytics/index.ts +47 -0
  80. package/apps/storefront/src/lib/auth/brokerAssertion.ts +136 -0
  81. package/apps/storefront/src/lib/auth/devAuth.ts +90 -0
  82. package/apps/storefront/src/lib/auth/gatePage.ts +81 -0
  83. package/apps/storefront/src/lib/auth/identityToken.ts +341 -0
  84. package/apps/storefront/src/lib/auth/loginGate.ts +107 -0
  85. package/apps/storefront/src/lib/auth/route.ts +146 -0
  86. package/apps/storefront/src/lib/auth/session.ts +203 -0
  87. package/apps/storefront/src/lib/auth/totAccessClient.ts +503 -0
  88. package/apps/storefront/src/lib/basePath.ts +41 -0
  89. package/apps/storefront/src/lib/cfImage.ts +55 -0
  90. package/apps/storefront/src/lib/chrome/model.ts +65 -0
  91. package/apps/storefront/src/lib/colors.ts +97 -0
  92. package/apps/storefront/src/lib/content-edit/client.ts +230 -0
  93. package/apps/storefront/src/lib/content-edit/index.ts +14 -0
  94. package/apps/storefront/src/lib/content-edit/route.ts +25 -0
  95. package/apps/storefront/src/lib/content-edit/types.ts +19 -0
  96. package/apps/storefront/src/lib/cspStaticHashes.ts +24 -0
  97. package/apps/storefront/src/lib/d1/catalog.ts +289 -0
  98. package/apps/storefront/src/lib/dashboard/staffAdmission.ts +43 -0
  99. package/apps/storefront/src/lib/dashboard/staffVendorAccess.ts +449 -0
  100. package/apps/storefront/src/lib/dashboard/tenantSelection.ts +136 -0
  101. package/apps/storefront/src/lib/dashboard/vendorQuery.ts +61 -0
  102. package/apps/storefront/src/lib/demoGate.ts +113 -0
  103. package/apps/storefront/src/lib/dev/ai-widget-client.js +127 -0
  104. package/apps/storefront/src/lib/dev/cliSignInCode.ts +132 -0
  105. package/apps/storefront/src/lib/diag/redact.ts +20 -0
  106. package/apps/storefront/src/lib/edgeCache.ts +128 -0
  107. package/apps/storefront/src/lib/email/magicLinkInviteEmail.ts +62 -0
  108. package/apps/storefront/src/lib/env.ts +60 -0
  109. package/apps/storefront/src/lib/fixtures/sampleProducts.ts +130 -0
  110. package/apps/storefront/src/lib/format.ts +29 -0
  111. package/apps/storefront/src/lib/foxyCommerce.ts +49 -0
  112. package/apps/storefront/src/lib/imageAttrs.ts +55 -0
  113. package/apps/storefront/src/lib/jsonld.ts +136 -0
  114. package/apps/storefront/src/lib/maintenance.ts +71 -0
  115. package/apps/storefront/src/lib/memoryKv.ts +32 -0
  116. package/apps/storefront/src/lib/messaging/messagesClient.ts +143 -0
  117. package/apps/storefront/src/lib/placeholder.ts +99 -0
  118. package/apps/storefront/src/lib/previewReload.ts +125 -0
  119. package/apps/storefront/src/lib/rawMarketingHtml.ts +133 -0
  120. package/apps/storefront/src/lib/search/index.ts +74 -0
  121. package/apps/storefront/src/lib/sections.ts +129 -0
  122. package/apps/storefront/src/lib/securityHeaders.ts +31 -0
  123. package/apps/storefront/src/lib/storyblok/content-model.ts +289 -0
  124. package/apps/storefront/src/lib/storyblok/provider.ts +524 -0
  125. package/apps/storefront/src/lib/tenantRouting.ts +91 -0
  126. package/apps/storefront/src/lib/tot/ToTClient.ts +177 -0
  127. package/apps/storefront/src/lib/tot/d1Client.ts +62 -0
  128. package/apps/storefront/src/lib/tot/query.ts +187 -0
  129. package/apps/storefront/src/lib/tot/totClientInterface.ts +30 -0
  130. package/apps/storefront/src/lib/url.ts +119 -0
  131. package/apps/storefront/src/lib/wishlist.ts +63 -0
  132. package/apps/storefront/src/middleware/index.ts +413 -0
  133. package/apps/storefront/src/pages/404.astro +61 -0
  134. package/apps/storefront/src/pages/[...slug].astro +90 -0
  135. package/apps/storefront/src/pages/api/auth/logout.ts +21 -0
  136. package/apps/storefront/src/pages/api/auth/magic-exchange.ts +107 -0
  137. package/apps/storefront/src/pages/api/auth/send.ts +85 -0
  138. package/apps/storefront/src/pages/api/auth/verify.ts +135 -0
  139. package/apps/storefront/src/pages/api/cache-purge.ts +44 -0
  140. package/apps/storefront/src/pages/api/content-edit/ai-edit.ts +64 -0
  141. package/apps/storefront/src/pages/api/csp-report.ts +25 -0
  142. package/apps/storefront/src/pages/api/dashboard/enter-vendor.ts +124 -0
  143. package/apps/storefront/src/pages/api/dashboard/vendor-search.ts +120 -0
  144. package/apps/storefront/src/pages/api/dev/cli-signin-code.ts +44 -0
  145. package/apps/storefront/src/pages/api/newsletter.ts +104 -0
  146. package/apps/storefront/src/pages/api/request-access.ts +172 -0
  147. package/apps/storefront/src/pages/auth/login.astro +149 -0
  148. package/apps/storefront/src/pages/auth/magic.astro +105 -0
  149. package/apps/storefront/src/pages/capabilities.astro +292 -0
  150. package/apps/storefront/src/pages/collections/[handle].astro +154 -0
  151. package/apps/storefront/src/pages/collections/index.astro +59 -0
  152. package/apps/storefront/src/pages/dashboard/[appDomain]/index.astro +100 -0
  153. package/apps/storefront/src/pages/dashboard/[appDomain]/team.astro +154 -0
  154. package/apps/storefront/src/pages/dashboard/index.astro +160 -0
  155. package/apps/storefront/src/pages/dev.astro +653 -0
  156. package/apps/storefront/src/pages/index.astro +290 -0
  157. package/apps/storefront/src/pages/llms.txt.ts +92 -0
  158. package/apps/storefront/src/pages/products/[handle].astro +443 -0
  159. package/apps/storefront/src/pages/robots.txt.ts +24 -0
  160. package/apps/storefront/src/pages/saved.astro +35 -0
  161. package/apps/storefront/src/pages/search.astro +150 -0
  162. package/apps/storefront/src/pages/sitemap.xml.ts +78 -0
  163. package/apps/storefront/src/pages/style-guide/[tenant]/[theme].astro +646 -0
  164. package/apps/storefront/src/pages/style-guide/[tenant]/index.astro +85 -0
  165. package/apps/storefront/src/pages/style-guide/index.astro +94 -0
  166. package/apps/storefront/src/pages/tenants/[id]/[...path].ts +57 -0
  167. package/apps/storefront/src/styles/global.css +277 -0
  168. package/apps/storefront/src/themes/reference.ts +61 -0
  169. package/apps/storefront/src/themes/schema.ts +111 -0
  170. package/apps/storefront/src/themes/tenants/index.ts +51 -0
  171. package/apps/storefront/tsconfig.json +16 -0
  172. package/apps/storefront/vitest.config.ts +22 -0
  173. package/apps/storefront/wrangler.toml +175 -0
  174. package/package.json +23 -0
  175. package/packages/public-runtime/LICENSE +58 -0
  176. package/packages/public-runtime/package.json +22 -0
  177. package/packages/public-runtime/src/binary-path.ts +68 -0
  178. package/packages/public-runtime/src/capabilities.ts +89 -0
  179. package/packages/public-runtime/src/catalog-api.ts +81 -0
  180. package/packages/public-runtime/src/catalog-d1.ts +192 -0
  181. package/packages/public-runtime/src/csp.ts +109 -0
  182. package/packages/public-runtime/src/customization-preview.ts +197 -0
  183. package/packages/public-runtime/src/customization-reconcile.ts +63 -0
  184. package/packages/public-runtime/src/customization-runtime.ts +157 -0
  185. package/packages/public-runtime/src/customization-scripts.ts +64 -0
  186. package/packages/public-runtime/src/customization-versioning.ts +95 -0
  187. package/packages/public-runtime/src/foxy.ts +184 -0
  188. package/packages/public-runtime/src/index.ts +30 -0
  189. package/packages/public-runtime/src/product.ts +159 -0
  190. package/packages/public-runtime/src/search.ts +45 -0
  191. package/packages/public-runtime/src/tenant-assets.ts +444 -0
  192. package/packages/public-runtime/src/tenant.ts +243 -0
  193. package/packages/public-runtime/tsconfig.json +8 -0
  194. package/packages/public-runtime/vitest.config.ts +8 -0
  195. package/pnpm-workspace.yaml +7 -0
  196. package/scripts/build/copy-tenant-assets.mjs +69 -0
  197. package/scripts/tot-dev.mjs +439 -0
  198. package/tenants/bigdvapor/content/blog.json +52 -0
  199. package/tenants/bigdvapor/content/chrome.html +149 -0
  200. package/tenants/bigdvapor/content/chrome.json +47 -0
  201. package/tenants/bigdvapor/content/home.html +463 -0
  202. package/tenants/bigdvapor/content/home.json +73 -0
  203. package/tenants/bigdvapor/content/pages/about.json +5 -0
  204. package/tenants/bigdvapor/content/pages/privacy.json +6 -0
  205. package/tenants/bigdvapor/content/pages/shipping-returns.json +6 -0
  206. package/tenants/bigdvapor/content/pages-html/blogs/news.html +86 -0
  207. package/tenants/bigdvapor/content/pages-html/pages/about-us.html +106 -0
  208. package/tenants/bigdvapor/content/pages-html/pages/contact-us.html +61 -0
  209. package/tenants/bigdvapor/content/pages-html/pages/privacy-policy.html +72 -0
  210. package/tenants/bigdvapor/content/pages-html/pages/shipping-returns.html +172 -0
  211. package/tenants/bigdvapor/content/themes/bigd-navy.json +109 -0
  212. package/tenants/bigdvapor/public/img/brands/adjust-vape-disposable.png +0 -0
  213. package/tenants/bigdvapor/public/img/brands/again-vape-disposable.png +0 -0
  214. package/tenants/bigdvapor/public/img/brands/al-fakher-vape-disposable.png +0 -0
  215. package/tenants/bigdvapor/public/img/brands/arro-vape-disposable.png +0 -0
  216. package/tenants/bigdvapor/public/img/brands/cookies-vape-disposable.png +0 -0
  217. package/tenants/bigdvapor/public/img/brands/core-vape-disposable.png +0 -0
  218. package/tenants/bigdvapor/public/img/brands/cyclone-vape-disposable.png +0 -0
  219. package/tenants/bigdvapor/public/img/brands/dinner-lady-vape-disposable.png +0 -0
  220. package/tenants/bigdvapor/public/img/brands/extre-bar-vape-disposable.png +0 -0
  221. package/tenants/bigdvapor/public/img/brands/fifty-bar-vape-disposable.jpg +0 -0
  222. package/tenants/bigdvapor/public/img/brands/firerose-vape-disposable.jpg +0 -0
  223. package/tenants/bigdvapor/public/img/brands/flavor-beast-vape-disposable.jpg +0 -0
  224. package/tenants/bigdvapor/public/img/brands/foger-vape-disposable.png +0 -0
  225. package/tenants/bigdvapor/public/img/brands/kk-energy-vape-disposable.png +0 -0
  226. package/tenants/bigdvapor/public/img/brands/kumi-vape-disposable.png +0 -0
  227. package/tenants/bigdvapor/public/img/brands/lost-vape-dispoable.png +0 -0
  228. package/tenants/bigdvapor/public/img/brands/melo-labs-vape-disposable.png +0 -0
  229. package/tenants/bigdvapor/public/img/brands/mixo-vape-disposable.webp +0 -0
  230. package/tenants/bigdvapor/public/img/brands/one-tank-vape-disposable.jpg +0 -0
  231. package/tenants/bigdvapor/public/img/brands/oxbar-vape-disposable.webp +0 -0
  232. package/tenants/bigdvapor/public/img/brands/pillow-talk-vape-disposable.jpg +0 -0
  233. package/tenants/bigdvapor/public/img/brands/pod-king-vape-disposable.jpg +0 -0
  234. package/tenants/bigdvapor/public/img/brands/raz-vape-disposable.png +0 -0
  235. package/tenants/bigdvapor/public/img/hero-texas.jpg +0 -0
  236. package/tenants/bigdvapor/public/img/og.jpg +0 -0
  237. package/tenants/bigdvapor/public/img/pages/AdobeStock_202106312_web.jpg +0 -0
  238. package/tenants/bigdvapor/public/img/pages/CREW_web.jpg +0 -0
  239. package/tenants/bigdvapor/public/img/pages/Hero-Image.jpg +0 -0
  240. package/tenants/bigdvapor/public/img/tiles/adjust.png +0 -0
  241. package/tenants/bigdvapor/public/img/tiles/again.png +0 -0
  242. package/tenants/bigdvapor/public/img/tiles/al-fakher.png +0 -0
  243. package/tenants/bigdvapor/public/img/tiles/arro.png +0 -0
  244. package/tenants/bigdvapor/public/img/tiles/cookies.png +0 -0
  245. package/tenants/bigdvapor/public/img/tiles/core.png +0 -0
  246. package/tenants/bigdvapor/public/img/tiles/cyclone.png +0 -0
  247. package/tenants/bigdvapor/public/img/tiles/disposables.svg +23 -0
  248. package/tenants/bigdvapor/public/logo-wordmark.png +0 -0
  249. package/tenants/bigdvapor/public/pages/home.css +118 -0
  250. package/tenants/bigdvapor/public/pages/mkt.css +185 -0
  251. package/tenants/bigdvapor/public/pages/page.css +148 -0
  252. package/tenants/bigdvapor/public/themes/bigd-navy.css +73 -0
  253. package/tenants/bigdvapor/theme.json +12 -0
  254. package/tenants/giantvapes/content/chrome.html +152 -0
  255. package/tenants/giantvapes/content/chrome.json +94 -0
  256. package/tenants/giantvapes/content/home.html +194 -0
  257. package/tenants/giantvapes/content/home.json +50 -0
  258. package/tenants/giantvapes/content/pages/about.json +10 -0
  259. package/tenants/giantvapes/content/pages/privacy.json +6 -0
  260. package/tenants/giantvapes/content/pages/shipping-returns.json +6 -0
  261. package/tenants/giantvapes/content/pages-html/blogs/news.html +68 -0
  262. package/tenants/giantvapes/content/pages-html/pages/about-us.html +95 -0
  263. package/tenants/giantvapes/content/pages-html/pages/contact-us.html +68 -0
  264. package/tenants/giantvapes/content/pages-html/pages/privacy-policy.html +65 -0
  265. package/tenants/giantvapes/content/pages-html/pages/shipping-returns.html +77 -0
  266. package/tenants/giantvapes/content/themes/giant-navy.json +73 -0
  267. package/tenants/giantvapes/public/img/hero-suicide-bunny.jpg +0 -0
  268. package/tenants/giantvapes/public/img/hero.webp +0 -0
  269. package/tenants/giantvapes/public/img/og.jpg +0 -0
  270. package/tenants/giantvapes/public/logo-wordmark.png +0 -0
  271. package/tenants/giantvapes/public/pages/home.css +120 -0
  272. package/tenants/giantvapes/public/pages/mkt.css +185 -0
  273. package/tenants/giantvapes/public/pages/page.css +155 -0
  274. package/tenants/giantvapes/public/themes/giant-navy.css +76 -0
  275. package/tenants/giantvapes/theme.json +39 -0
  276. package/tenants/home/content/chrome.json +11 -0
  277. package/tenants/home/content/home.html +304 -0
  278. package/tenants/home/content/home.json +13 -0
  279. package/tenants/home/public/js/storefront.js +210 -0
  280. package/tenants/home/public/logo-wordmark.png +0 -0
  281. package/tenants/home/public/pages/storefront.css +212 -0
  282. package/tenants/home/theme.json +6 -0
  283. package/tenants/tokenoftrust/.tot/config.json +9 -0
  284. package/tenants/tokenoftrust/content/chrome.json +55 -0
  285. package/tenants/tokenoftrust/content/home.html +723 -0
  286. package/tenants/tokenoftrust/content/home.json +233 -0
  287. package/tenants/tokenoftrust/content/pages-html/careers.html +231 -0
  288. package/tenants/tokenoftrust/content/pages-html/company.html +275 -0
  289. package/tenants/tokenoftrust/content/pages-html/contact/contact-sales.html +188 -0
  290. package/tenants/tokenoftrust/content/pages-html/contact/startup-apply.html +206 -0
  291. package/tenants/tokenoftrust/content/pages-html/contact.html +178 -0
  292. package/tenants/tokenoftrust/content/pages-html/industries/adult-content-age-verification.html +286 -0
  293. package/tenants/tokenoftrust/content/pages-html/industries/cannabis.html +277 -0
  294. package/tenants/tokenoftrust/content/pages-html/pact-act-pricing.html +289 -0
  295. package/tenants/tokenoftrust/content/pages-html/pricing.html +483 -0
  296. package/tenants/tokenoftrust/content/pages-html/product/age-assurance/age-estimation.html +278 -0
  297. package/tenants/tokenoftrust/content/pages-html/product/age-assurance/age-gate.html +279 -0
  298. package/tenants/tokenoftrust/content/pages-html/product/age-assurance/age-verification.html +265 -0
  299. package/tenants/tokenoftrust/content/pages-html/product/age-assurance.html +173 -0
  300. package/tenants/tokenoftrust/content/pages-html/product/compliance/aml.html +214 -0
  301. package/tenants/tokenoftrust/content/pages-html/product/compliance/pact-act.html +226 -0
  302. package/tenants/tokenoftrust/content/pages-html/product/fraud-prevention.html +254 -0
  303. package/tenants/tokenoftrust/content/pages-html/product/social-profile-verification.html +212 -0
  304. package/tenants/tokenoftrust/content/pages-html/product/tax/excise-tax.html +213 -0
  305. package/tenants/tokenoftrust/content/pages-html/product/verification/aml.html +259 -0
  306. package/tenants/tokenoftrust/content/pages-html/product/verification/biometric-selfie.html +305 -0
  307. package/tenants/tokenoftrust/content/pages-html/product/verification/coverage.html +254 -0
  308. package/tenants/tokenoftrust/content/pages-html/product/verification/document-verification.html +306 -0
  309. package/tenants/tokenoftrust/content/pages-html/product/verification/electronic-identity-verification.html +304 -0
  310. package/tenants/tokenoftrust/content/pages-html/product/verification/government-id-verification.html +308 -0
  311. package/tenants/tokenoftrust/content/pages-html/product/verification.html +183 -0
  312. package/tenants/tokenoftrust/content/pages-html/product.html +228 -0
  313. package/tenants/tokenoftrust/content/pages-html/resources/integrations/bigcommerce-integration.html +220 -0
  314. package/tenants/tokenoftrust/content/pages-html/resources/integrations/shopify-integration.html +221 -0
  315. package/tenants/tokenoftrust/content/pages-html/resources/integrations/wordpress.html +220 -0
  316. package/tenants/tokenoftrust/content/pages-html/resources/integrations.html +172 -0
  317. package/tenants/tokenoftrust/content/pages-html/resources/refer-a-friend-program.html +317 -0
  318. package/tenants/tokenoftrust/content/pages-html/resources.html +177 -0
  319. package/tenants/tokenoftrust/content/pages-html/reviews.html +273 -0
  320. package/tenants/tokenoftrust/content/pages-html/solutions.html +292 -0
  321. package/tenants/tokenoftrust/content/pages-html/vlp-alcohol.html +289 -0
  322. package/tenants/tokenoftrust/content/pages-html/vlp-cigars-and-tobacco.html +286 -0
  323. package/tenants/tokenoftrust/content/pages-html/vlp-firearm-accessories.html +267 -0
  324. package/tenants/tokenoftrust/content/pages-html/vlp-peptides.html +273 -0
  325. package/tenants/tokenoftrust/content/themes/tot-navy.json +88 -0
  326. package/tenants/tokenoftrust/public/favicon.png +0 -0
  327. package/tenants/tokenoftrust/public/logo-icon.png +0 -0
  328. package/tenants/tokenoftrust/public/logo-wordmark.png +0 -0
  329. package/tenants/tokenoftrust/public/pages/company.css +108 -0
  330. package/tenants/tokenoftrust/public/pages/home.css +457 -0
  331. package/tenants/tokenoftrust/public/pages/mkt.css +482 -0
  332. package/tenants/tokenoftrust/public/pages/pricing.css +214 -0
  333. package/tenants/tokenoftrust/public/pages/solutions.css +66 -0
  334. package/tenants/tokenoftrust/public/themes/tot-navy-1.0.0.css +136 -0
  335. package/tenants/tokenoftrust/public/themes/tot-navy.css +358 -0
  336. package/tenants/tokenoftrust/theme.json +31 -0
  337. package/tsconfig.base.json +20 -0
@@ -0,0 +1,289 @@
1
+ /**
2
+ * D1 catalog read-model queries (the one-store target — epics/edge-read-replica.md).
3
+ *
4
+ * Queries run against a small `Queryable` seam so the SAME code serves D1 in the
5
+ * Worker (via `fromD1`) and an in-memory `node:sqlite` in tests. Covers FTS5
6
+ * (trigram) search, getProductByHandle, collections, and faceted `listProducts`
7
+ * (filter + sort + paginate + facet counts in SQL GROUP BY) — the full read path,
8
+ * verified end-to-end against the real D1 engine (workerd).
9
+ */
10
+ import {
11
+ CATALOG_D1_SCHEMA,
12
+ toProductRow,
13
+ toCollectionRow,
14
+ facetRows,
15
+ collectionRows,
16
+ type CatalogCollection,
17
+ type CatalogProduct,
18
+ type Facet,
19
+ type FacetValue,
20
+ type ListProductsQuery,
21
+ type ListProductsResponse,
22
+ } from "@tot/public-runtime";
23
+
24
+ /** Minimal async query seam — implemented by D1 and by the node:sqlite test shim. */
25
+ export interface Queryable {
26
+ all<T = Record<string, unknown>>(sql: string, ...params: unknown[]): Promise<T[]>;
27
+ first<T = Record<string, unknown>>(sql: string, ...params: unknown[]): Promise<T | null>;
28
+ run(sql: string, ...params: unknown[]): Promise<void>;
29
+ exec(sql: string): Promise<void>;
30
+ }
31
+
32
+ /** The slice of the Cloudflare D1Database we use (kept local to avoid a types dep). */
33
+ export interface D1Like {
34
+ prepare(sql: string): {
35
+ bind(...vals: unknown[]): {
36
+ all(): Promise<{ results: unknown[] }>;
37
+ first(): Promise<unknown>;
38
+ run(): Promise<unknown>;
39
+ };
40
+ };
41
+ exec(sql: string): Promise<unknown>;
42
+ }
43
+
44
+ /** Adapt a Cloudflare D1Database to the Queryable seam. */
45
+ export function fromD1(db: D1Like): Queryable {
46
+ return {
47
+ all: async <T>(sql: string, ...params: unknown[]) =>
48
+ (await db.prepare(sql).bind(...params).all()).results as T[],
49
+ first: async <T>(sql: string, ...params: unknown[]) =>
50
+ ((await db.prepare(sql).bind(...params).first()) ?? null) as T | null,
51
+ run: async (sql: string, ...params: unknown[]) => {
52
+ await db.prepare(sql).bind(...params).run();
53
+ },
54
+ exec: async (sql: string) => {
55
+ await db.exec(sql);
56
+ },
57
+ };
58
+ }
59
+
60
+ /** Create the catalog schema (idempotent). Used for local/test seeding. */
61
+ export async function initSchema(db: Queryable): Promise<void> {
62
+ await db.exec(CATALOG_D1_SCHEMA);
63
+ }
64
+
65
+ /** Insert one product into all read-model tables (products, fts, facets, collections). */
66
+ export async function insertProduct(db: Queryable, p: CatalogProduct, position = 0): Promise<void> {
67
+ const r = toProductRow(p, position);
68
+ await db.run(
69
+ `INSERT OR REPLACE INTO products
70
+ (handle,title,vendor,product_type,tags,description_text,price_min,price_max,currency,image,available,status,position,created_at,data)
71
+ VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)`,
72
+ r.handle, r.title, r.vendor, r.product_type, r.tags, r.description_text,
73
+ r.price_min, r.price_max, r.currency, r.image, r.available, r.status,
74
+ r.position, r.created_at, r.data,
75
+ );
76
+ await db.run(
77
+ `INSERT INTO products_fts (handle,title,vendor,product_type,tags,description_text)
78
+ VALUES (?,?,?,?,?,?)`,
79
+ p.handle, p.title, p.vendor ?? "", p.product_type ?? "", p.tags.join(" "), p.description_text,
80
+ );
81
+ for (const f of facetRows(p)) {
82
+ await db.run(`INSERT INTO product_facets (handle,name,value) VALUES (?,?,?)`, p.handle, f.name, f.value);
83
+ }
84
+ for (const c of collectionRows(p)) {
85
+ await db.run(`INSERT INTO product_collections (handle,collection) VALUES (?,?)`, p.handle, c.collection);
86
+ }
87
+ }
88
+
89
+ /** Insert one collection row. */
90
+ export async function insertCollection(db: Queryable, c: CatalogCollection): Promise<void> {
91
+ const r = toCollectionRow(c);
92
+ await db.run(
93
+ `INSERT OR REPLACE INTO collections (handle,title,position,data) VALUES (?,?,?,?)`,
94
+ r.handle, r.title, r.position, r.data,
95
+ );
96
+ }
97
+
98
+ export async function listCollections(db: Queryable): Promise<CatalogCollection[]> {
99
+ const rows = await db.all<{ data: string }>(`SELECT data FROM collections ORDER BY position ASC`);
100
+ return rows.map((r) => JSON.parse(r.data) as CatalogCollection);
101
+ }
102
+
103
+ export async function getCollectionByHandle(
104
+ db: Queryable,
105
+ handle: string,
106
+ ): Promise<CatalogCollection | null> {
107
+ const row = await db.first<{ data: string }>(
108
+ `SELECT data FROM collections WHERE handle = ?`,
109
+ handle,
110
+ );
111
+ return row ? (JSON.parse(row.data) as CatalogCollection) : null;
112
+ }
113
+
114
+ /** Build an FTS5 query expression: implicit-AND of quoted terms (trigram substring). */
115
+ function ftsExpr(q: string): string | null {
116
+ const terms = q
117
+ .trim()
118
+ .split(/\s+/)
119
+ .filter((t) => t.length >= 3) // FTS5 trigram tokens are 3 chars; shorter can't match
120
+ .map((t) => `"${t.replace(/"/g, '""')}"`);
121
+ return terms.length ? terms.join(" ") : null;
122
+ }
123
+
124
+ /** Typo-tolerant search over the active catalog, ranked by FTS5 relevance. */
125
+ export async function searchProducts(
126
+ db: Queryable,
127
+ q: string,
128
+ limit = 24,
129
+ ): Promise<CatalogProduct[]> {
130
+ const expr = ftsExpr(q);
131
+ if (!expr) return [];
132
+ const rows = await db.all<{ data: string }>(
133
+ `SELECT p.data AS data
134
+ FROM products_fts f
135
+ JOIN products p ON p.handle = f.handle
136
+ WHERE products_fts MATCH ? AND p.status = 'active'
137
+ ORDER BY rank
138
+ LIMIT ?`,
139
+ expr, limit,
140
+ );
141
+ return rows.map((r) => JSON.parse(r.data) as CatalogProduct);
142
+ }
143
+
144
+ /** Full product by handle (resolves drafts too — callers gate visibility). */
145
+ export async function getProductByHandle(
146
+ db: Queryable,
147
+ handle: string,
148
+ ): Promise<CatalogProduct | null> {
149
+ const row = await db.first<{ data: string }>(
150
+ `SELECT data FROM products WHERE handle = ?`,
151
+ handle,
152
+ );
153
+ return row ? (JSON.parse(row.data) as CatalogProduct) : null;
154
+ }
155
+
156
+ function sortClause(sort?: ListProductsQuery["sort"]): string {
157
+ switch (sort) {
158
+ case "price-asc":
159
+ return "ORDER BY p.price_min ASC, p.title COLLATE NOCASE ASC";
160
+ case "price-desc":
161
+ return "ORDER BY p.price_min DESC, p.title COLLATE NOCASE ASC";
162
+ case "newest":
163
+ return "ORDER BY p.created_at DESC";
164
+ case "title-asc":
165
+ return "ORDER BY p.title COLLATE NOCASE ASC";
166
+ case "featured":
167
+ default:
168
+ // Bestsellers float up, then the load order (position) — mirrors the
169
+ // in-Worker featured sort.
170
+ return `ORDER BY (CASE WHEN EXISTS (
171
+ SELECT 1 FROM product_facets f
172
+ WHERE f.handle = p.handle AND f.name = 'tags' AND f.value = 'bestseller'
173
+ ) THEN 0 ELSE 1 END), p.position ASC`;
174
+ }
175
+ }
176
+
177
+ /** Facet counts over the collection-scoped pool (BEFORE option/tag/price filters). */
178
+ async function buildFacetsSql(
179
+ db: Queryable,
180
+ poolSql: string,
181
+ poolParams: unknown[],
182
+ ): Promise<Facet[]> {
183
+ const rows = await db.all<{ name: string; value: string; n: number }>(
184
+ `SELECT pf.name AS name, pf.value AS value, COUNT(DISTINCT pf.handle) AS n
185
+ FROM product_facets pf
186
+ WHERE pf.handle IN (SELECT p.handle ${poolSql})
187
+ GROUP BY pf.name, pf.value
188
+ ORDER BY pf.name ASC, n DESC, pf.value ASC`,
189
+ ...poolParams,
190
+ );
191
+ const byName = new Map<string, FacetValue[]>();
192
+ for (const r of rows) {
193
+ const values = byName.get(r.name) ?? [];
194
+ values.push({ value: r.value, count: r.n });
195
+ byName.set(r.name, values);
196
+ }
197
+ const facets: Facet[] = [...byName.entries()].map(([name, values]) => ({ name, values }));
198
+
199
+ const avail = await db.first<{ instock: number | null; total: number }>(
200
+ `SELECT SUM(CASE WHEN p.available = 1 THEN 1 ELSE 0 END) AS instock, COUNT(*) AS total ${poolSql}`,
201
+ ...poolParams,
202
+ );
203
+ facets.push({
204
+ name: "availability",
205
+ values: [
206
+ { value: "in-stock", count: avail?.instock ?? 0 },
207
+ { value: "all", count: avail?.total ?? 0 },
208
+ ],
209
+ });
210
+ return facets;
211
+ }
212
+
213
+ /**
214
+ * Faceted listing — filter, sort, paginate, and count facets in SQL, replacing
215
+ * the in-Worker O(products×options×variants) scan. Option/tag filters are
216
+ * intra-group OR, inter-group AND (a product matches a group if it has any
217
+ * variant value in it). Facets are computed over the collection-scoped pool,
218
+ * BEFORE the option/tag/price filters, to match the storefront's facet UI.
219
+ */
220
+ export async function listProducts(
221
+ db: Queryable,
222
+ query: ListProductsQuery = {},
223
+ ): Promise<ListProductsResponse> {
224
+ const pageSize = Math.max(1, query.pageSize ?? 12);
225
+
226
+ // Pool: active products, optionally scoped to a collection.
227
+ const poolJoin = query.collection
228
+ ? "JOIN product_collections pc ON pc.handle = p.handle AND pc.collection = ?"
229
+ : "";
230
+ const poolParams: unknown[] = query.collection ? [query.collection] : [];
231
+ const poolSql = `FROM products p ${poolJoin} WHERE p.status = 'active'`;
232
+
233
+ // Result filters layered on the pool.
234
+ const filterSql: string[] = [];
235
+ const filterParams: unknown[] = [];
236
+
237
+ for (const [name, values] of Object.entries(query.filters ?? {})) {
238
+ if (name === "availability" || name === "price" || !values.length) continue;
239
+ const placeholders = values.map(() => "?").join(",");
240
+ filterSql.push(
241
+ `p.handle IN (SELECT handle FROM product_facets WHERE name = ? AND value IN (${placeholders}))`,
242
+ );
243
+ filterParams.push(name, ...values);
244
+ }
245
+ if (query.tags?.length) {
246
+ const placeholders = query.tags.map(() => "?").join(",");
247
+ filterSql.push(
248
+ `p.handle IN (SELECT handle FROM product_facets WHERE name = 'tags' AND value IN (${placeholders}))`,
249
+ );
250
+ filterParams.push(...query.tags);
251
+ }
252
+ if (query.availability === "in-stock") filterSql.push("p.available = 1");
253
+ if (query.priceMin != null) {
254
+ filterSql.push("p.price_max >= ?");
255
+ filterParams.push(query.priceMin);
256
+ }
257
+ if (query.priceMax != null) {
258
+ filterSql.push("p.price_min <= ?");
259
+ filterParams.push(query.priceMax);
260
+ }
261
+ if (query.q?.trim()) {
262
+ const like = `%${query.q.trim().toLowerCase()}%`;
263
+ filterSql.push(
264
+ "(lower(p.title) LIKE ? OR lower(p.vendor) LIKE ? OR lower(p.product_type) LIKE ? OR lower(p.description_text) LIKE ? OR lower(p.tags) LIKE ?)",
265
+ );
266
+ filterParams.push(like, like, like, like, like);
267
+ }
268
+
269
+ const filterClause = filterSql.length ? ` AND ${filterSql.join(" AND ")}` : "";
270
+ const resultParams = [...poolParams, ...filterParams];
271
+
272
+ const totalRow = await db.first<{ n: number }>(
273
+ `SELECT COUNT(*) AS n ${poolSql}${filterClause}`,
274
+ ...resultParams,
275
+ );
276
+ const total = totalRow?.n ?? 0;
277
+ const pageCount = Math.max(1, Math.ceil(total / pageSize));
278
+ const page = Math.min(Math.max(1, query.page ?? 1), pageCount);
279
+ const offset = (page - 1) * pageSize;
280
+
281
+ const rows = await db.all<{ data: string }>(
282
+ `SELECT p.data AS data ${poolSql}${filterClause} ${sortClause(query.sort)} LIMIT ? OFFSET ?`,
283
+ ...resultParams, pageSize, offset,
284
+ );
285
+ const products = rows.map((r) => JSON.parse(r.data) as CatalogProduct);
286
+
287
+ const facets = await buildFacetsSql(db, poolSql, poolParams);
288
+ return { products, page, pageSize, total, facets };
289
+ }
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Pure helpers for the ToT-staff two-phase admission (#8425 staff access), kept
3
+ * out of the Astro route files so the gate + selection-builder are unit-testable.
4
+ *
5
+ * PHASE 1 (admission gate): only a session that actually carries ToT-staff scope
6
+ * (`staff[]` non-empty) may search vendors or enter one. This is the near-side
7
+ * gate on the storefront's staff endpoints; tot20 re-checks staff scope + writes
8
+ * the audit row on the far side (`staffVendorAccess`), so this is defence-in-depth,
9
+ * not the authority.
10
+ *
11
+ * PHASE 2 (scope): after tot20 admits, we build a short-lived {@link StaffSelection}
12
+ * scoped to the returned staffRoles capability. It is stamped on the session as a
13
+ * DISTINCT marker from `tenants[]` — never a synthesized membership.
14
+ */
15
+ import type { SessionRecord, StaffSelection } from "@/lib/auth/session";
16
+
17
+ /**
18
+ * Staff selection lifetime. Deliberately short: staff cross-vendor access is a
19
+ * deliberate, audited act, not a standing grant — re-entering re-audits.
20
+ */
21
+ export const STAFF_SELECTION_TTL_SECONDS = 30 * 60;
22
+
23
+ /** True when the session carries ToT-staff scope (the admission-gate predicate). */
24
+ export function isStaffSession(
25
+ session: Pick<SessionRecord, "staff"> | null | undefined,
26
+ ): boolean {
27
+ return !!session && Array.isArray(session.staff) && session.staff.length > 0;
28
+ }
29
+
30
+ /** Build the short-lived staff selection marker to stamp on the session. */
31
+ export function buildStaffSelection(input: {
32
+ resource: string;
33
+ capability: string;
34
+ nowSeconds: number;
35
+ ttlSeconds?: number;
36
+ }): StaffSelection {
37
+ const ttl = input.ttlSeconds && input.ttlSeconds > 0 ? input.ttlSeconds : STAFF_SELECTION_TTL_SECONDS;
38
+ return {
39
+ resource: input.resource.trim(),
40
+ capability: input.capability,
41
+ exp: input.nowSeconds + ttl,
42
+ };
43
+ }