@tokenoftrust/storefront-runner 1.3.2
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 +42 -0
- package/apps/storefront/astro.config.mjs +66 -0
- package/apps/storefront/dev-plugins/dev-loop-state.mjs +147 -0
- package/apps/storefront/dev-plugins/tenant-hot-reload.mjs +238 -0
- package/apps/storefront/env.d.ts +162 -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 +42 -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/public/shared/commerce-chrome.css +369 -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 +55 -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/admin/AdminAIWorkflowsTab.astro +531 -0
- package/apps/storefront/src/components/admin/AdminCustomersTab.astro +597 -0
- package/apps/storefront/src/components/admin/AdminFoxyCartSettingsPanel.astro +227 -0
- package/apps/storefront/src/components/admin/AdminFulfillmentTab.astro +465 -0
- package/apps/storefront/src/components/admin/AdminProductsTab.astro +901 -0
- package/apps/storefront/src/components/admin/AdminReportingTab.astro +435 -0
- package/apps/storefront/src/components/admin/AdminSettingsTab.astro +782 -0
- package/apps/storefront/src/components/admin/orders/OrdersWorkspace.astro +1223 -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 +201 -0
- package/apps/storefront/src/components/chrome/SiteFooter.astro +201 -0
- package/apps/storefront/src/components/chrome/SiteHeader.astro +234 -0
- package/apps/storefront/src/components/chrome/UtilityNav.astro +79 -0
- package/apps/storefront/src/components/commerce/ArticleCard.astro +242 -0
- package/apps/storefront/src/components/commerce/BrandGrid.astro +175 -0
- package/apps/storefront/src/components/commerce/FoxyLoader.astro +23 -0
- package/apps/storefront/src/components/commerce/FoxySidecartStyles.astro +61 -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/commerce/ReviewList.astro +54 -0
- package/apps/storefront/src/components/commerce/ReviewSummary.astro +44 -0
- package/apps/storefront/src/components/commerce/TagFilterCloud.astro +149 -0
- package/apps/storefront/src/components/compliance/AdultSignatureNotice.astro +37 -0
- package/apps/storefront/src/components/compliance/ComplianceNotice.astro +99 -0
- package/apps/storefront/src/components/compliance/ExciseTaxNotice.astro +45 -0
- package/apps/storefront/src/components/compliance/NicotineWarning.astro +33 -0
- package/apps/storefront/src/components/compliance/PactActNotice.astro +35 -0
- package/apps/storefront/src/components/compliance/Prop65Warning.astro +80 -0
- package/apps/storefront/src/components/compliance/PurchaseLimitNotice.astro +38 -0
- package/apps/storefront/src/components/compliance/ShippingRestrictionNotice.astro +35 -0
- package/apps/storefront/src/components/compliance/StateEligibilityNotice.astro +53 -0
- package/apps/storefront/src/components/home/Hero.astro +164 -0
- package/apps/storefront/src/components/islands/AgeGate.tsx +109 -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 +375 -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/membership/IllustrativeLabel.astro +56 -0
- package/apps/storefront/src/components/membership/TierCards.astro +164 -0
- package/apps/storefront/src/components/membership/TierComparisonTable.astro +148 -0
- package/apps/storefront/src/components/plp/ActiveFilters.astro +79 -0
- package/apps/storefront/src/components/plp/FacetSidebar.astro +199 -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/components/style-guide/StyleGuideNav.astro +95 -0
- package/apps/storefront/src/components/subscription/HowItWorksSteps.astro +67 -0
- package/apps/storefront/src/components/subscription/ManageSubscriptionEntry.astro +67 -0
- package/apps/storefront/src/components/subscription/SavingsExplainer.astro +101 -0
- package/apps/storefront/src/config/adminTargetTenant.ts +28 -0
- package/apps/storefront/src/config/devTenantSeed.ts +78 -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 +102 -0
- package/apps/storefront/src/config/tenants.ts +87 -0
- package/apps/storefront/src/layouts/Layout.astro +368 -0
- package/apps/storefront/src/lib/admin/README.md +122 -0
- package/apps/storefront/src/lib/admin/ai/approvalMode.ts +21 -0
- package/apps/storefront/src/lib/admin/ai/askEnvelope.ts +48 -0
- package/apps/storefront/src/lib/admin/ai/audit.ts +14 -0
- package/apps/storefront/src/lib/admin/ai/index.ts +6 -0
- package/apps/storefront/src/lib/admin/ai/operationalRuns.ts +155 -0
- package/apps/storefront/src/lib/admin/ai/recommendation.ts +20 -0
- package/apps/storefront/src/lib/admin/checkoutStyleInput.ts +172 -0
- package/apps/storefront/src/lib/admin/checkoutStyleTarget.ts +65 -0
- package/apps/storefront/src/lib/admin/envelope.ts +10 -0
- package/apps/storefront/src/lib/admin/evidence/evidenceReference.ts +32 -0
- package/apps/storefront/src/lib/admin/memoizeByEnvelope.ts +27 -0
- package/apps/storefront/src/lib/admin/orderClient.ts +146 -0
- package/apps/storefront/src/lib/admin/seed.ts +54 -0
- package/apps/storefront/src/lib/admin/services/mock/aiWorkflows.ts +59 -0
- package/apps/storefront/src/lib/admin/services/mock/customers.ts +86 -0
- package/apps/storefront/src/lib/admin/services/mock/evidence.ts +99 -0
- package/apps/storefront/src/lib/admin/services/mock/foxyCartSettings.ts +78 -0
- package/apps/storefront/src/lib/admin/services/mock/fulfillment.ts +62 -0
- package/apps/storefront/src/lib/admin/services/mock/index.ts +12 -0
- package/apps/storefront/src/lib/admin/services/mock/orders.ts +184 -0
- package/apps/storefront/src/lib/admin/services/mock/products.ts +105 -0
- package/apps/storefront/src/lib/admin/services/mock/reporting.ts +51 -0
- package/apps/storefront/src/lib/admin/services/mock/settings.ts +70 -0
- package/apps/storefront/src/lib/admin/services/mock/tenants.ts +29 -0
- package/apps/storefront/src/lib/admin/services/orders.ts +300 -0
- package/apps/storefront/src/lib/admin/viewmodels/aiWorkflows.ts +67 -0
- package/apps/storefront/src/lib/admin/viewmodels/customers.ts +56 -0
- package/apps/storefront/src/lib/admin/viewmodels/fulfillment.ts +49 -0
- package/apps/storefront/src/lib/admin/viewmodels/orders.ts +92 -0
- package/apps/storefront/src/lib/admin/viewmodels/products.ts +74 -0
- package/apps/storefront/src/lib/admin/viewmodels/reporting.ts +34 -0
- package/apps/storefront/src/lib/admin/viewmodels/settings.ts +64 -0
- package/apps/storefront/src/lib/adminTenantEnvelope.ts +90 -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 +119 -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 +244 -0
- package/apps/storefront/src/lib/chrome/parseConfig.ts +34 -0
- package/apps/storefront/src/lib/colors.ts +97 -0
- package/apps/storefront/src/lib/commerce/foxyCartSettings.ts +253 -0
- package/apps/storefront/src/lib/content-edit/applyDiff.ts +91 -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 +366 -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/activityIngestToken.ts +78 -0
- package/apps/storefront/src/lib/dev/activityStore.ts +72 -0
- package/apps/storefront/src/lib/dev/ai-widget-client.js +308 -0
- package/apps/storefront/src/lib/dev/apiBase.ts +14 -0
- package/apps/storefront/src/lib/dev/cliSignInCode.ts +155 -0
- package/apps/storefront/src/lib/dev/devDraft.ts +133 -0
- package/apps/storefront/src/lib/dev/hostedCockpit.ts +169 -0
- package/apps/storefront/src/lib/dev/livePreview.ts +77 -0
- package/apps/storefront/src/lib/dev/runtimeStore.ts +120 -0
- package/apps/storefront/src/lib/dev/storeStatus.ts +142 -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 +38 -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 +166 -0
- package/apps/storefront/src/lib/maintenance.ts +71 -0
- package/apps/storefront/src/lib/membership/model.ts +97 -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/priceBuckets.ts +33 -0
- package/apps/storefront/src/lib/rawChrome.ts +505 -0
- package/apps/storefront/src/lib/rawMarketingHtml.ts +136 -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 +607 -0
- package/apps/storefront/src/lib/subscription/model.ts +68 -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 +249 -0
- package/apps/storefront/src/lib/tot/totClientInterface.ts +30 -0
- package/apps/storefront/src/lib/url.ts +142 -0
- package/apps/storefront/src/lib/wishlist.ts +63 -0
- package/apps/storefront/src/middleware/index.ts +455 -0
- package/apps/storefront/src/pages/404.astro +61 -0
- package/apps/storefront/src/pages/[...slug].astro +90 -0
- package/apps/storefront/src/pages/admin.astro +1675 -0
- package/apps/storefront/src/pages/api/admin/orders/[id].ts +53 -0
- package/apps/storefront/src/pages/api/admin/orders.ts +71 -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/checkout-style.css.ts +43 -0
- package/apps/storefront/src/pages/api/cli/policy.ts +70 -0
- package/apps/storefront/src/pages/api/content-edit/ai-edit.ts +164 -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/activity.ts +124 -0
- package/apps/storefront/src/pages/api/dev/cli-signin-code.ts +73 -0
- package/apps/storefront/src/pages/api/dev/diff.ts +68 -0
- package/apps/storefront/src/pages/api/dev/file.ts +57 -0
- package/apps/storefront/src/pages/api/dev/locate.ts +125 -0
- package/apps/storefront/src/pages/api/dev/publish.ts +128 -0
- package/apps/storefront/src/pages/api/dev/tree.ts +70 -0
- package/apps/storefront/src/pages/api/dev/write.ts +116 -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 +177 -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/index.astro +290 -0
- package/apps/storefront/src/pages/llms.txt.ts +92 -0
- package/apps/storefront/src/pages/products/[handle].astro +402 -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 +1280 -0
- package/apps/storefront/src/pages/style-guide/[tenant]/chrome/[theme].astro +229 -0
- package/apps/storefront/src/pages/style-guide/[tenant]/guide/[theme].astro +144 -0
- package/apps/storefront/src/pages/style-guide/[tenant]/index.astro +116 -0
- package/apps/storefront/src/pages/style-guide/index.astro +98 -0
- package/apps/storefront/src/pages/tenants/[id]/[...path].ts +57 -0
- package/apps/storefront/src/styles/global.css +292 -0
- package/apps/storefront/src/themes/reference.ts +68 -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/package.json +26 -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/checkout-style-publish.ts +200 -0
- package/packages/public-runtime/src/checkout-style.ts +375 -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-orders.ts +136 -0
- package/packages/public-runtime/src/foxy.ts +196 -0
- package/packages/public-runtime/src/hot-standby-readiness.ts +140 -0
- package/packages/public-runtime/src/index.ts +34 -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 +384 -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/dev/ai-edit.mjs +183 -0
- package/scripts/dev/checkout-watch.mjs +116 -0
- package/scripts/dev/file-browser.mjs +159 -0
- package/scripts/dev/file-writer.mjs +141 -0
- package/scripts/dev/git-status.mjs +171 -0
- package/scripts/dev/locate-handler.mjs +163 -0
- package/scripts/dev/locate.mjs +112 -0
- package/scripts/dev/port-check.mjs +48 -0
- package/scripts/dev/safe-path.mjs +109 -0
- package/scripts/dev/shot.mjs +46 -0
- package/scripts/dev/tenant-source-root.mjs +76 -0
- package/scripts/dev/unified-diff.mjs +81 -0
- package/scripts/tot-dev.mjs +376 -0
- package/tenants/README.md +14 -0
- package/tenants/home/content/chrome.json +7 -0
- package/tenants/home/content/home.html +58 -0
- package/tenants/home/content/home.json +11 -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/tsconfig.base.json +20 -0
|
@@ -0,0 +1,376 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* `tot dev` — the developer's on-save local preview loop (the magic).
|
|
4
|
+
*
|
|
5
|
+
* Boots the storefront's `astro dev` (which already gives Vite HMR / live-reload
|
|
6
|
+
* on save) and points the browser at the developer's tenant via the DNS-free
|
|
7
|
+
* path-prefix route (`/<appDomain>/`). Edit a file under `content/<tenant>/`
|
|
8
|
+
* (home.html, pages, chrome) or the tenant theme → save → the browser reloads.
|
|
9
|
+
* Private, on-save, NO publish: product data renders from local fixtures; nothing
|
|
10
|
+
* is written to KV or shared. ("Publish" — to test/live — stays a separate,
|
|
11
|
+
* deliberate, compliance-gated step.)
|
|
12
|
+
*
|
|
13
|
+
* TWO MODES
|
|
14
|
+
* 1. MONOREPO (default): `node scripts/tot-dev.mjs <tenant>` — runs astro dev
|
|
15
|
+
* over the in-tree colocated `tenants/<tenant>/` layout (content/, public/,
|
|
16
|
+
* theme.json).
|
|
17
|
+
* 2. STANDALONE: `node scripts/tot-dev.mjs --workspace <checkout-path>` — the
|
|
18
|
+
* developer has cloned their tenant's Gitea repo (a FLAT, tenant-centric
|
|
19
|
+
* layout: `content/`, `public/`, `theme.json`, `.tot/config.json`). This mode
|
|
20
|
+
* reads the checkout's `.tot/config.json` `mappings` and grafts each workspace
|
|
21
|
+
* prefix into the storefront paths the app globs, then boots the same astro dev.
|
|
22
|
+
* Edits in the CHECKOUT hot-reload via Vite. On exit the graft (and any
|
|
23
|
+
* backed-up in-tree tenant content) is restored — the tree is left clean.
|
|
24
|
+
* Phase-0 bridge: the platform still comes from THIS storefront checkout; the
|
|
25
|
+
* containerized "platform baked in, tenant clone mounted" version is deferred.
|
|
26
|
+
*
|
|
27
|
+
* WHY COPY+WATCH, NOT SYMLINK: a symlink from the in-tree path to the checkout
|
|
28
|
+
* resolves (in Vite) to the checkout's REAL path, which is OUTSIDE the app root,
|
|
29
|
+
* so Vite's `server.fs.allow` denies the `?raw`/glob import ("Denied ID …"). So
|
|
30
|
+
* we COPY the checkout's files into the in-tree paths (inside the app root, which
|
|
31
|
+
* Vite trusts) and run a recursive `fs.watch` on the checkout that re-syncs each
|
|
32
|
+
* changed file back into the in-tree copy on save — which is what fires Vite HMR.
|
|
33
|
+
*
|
|
34
|
+
* Two things learned the hard way (see the two-liners below):
|
|
35
|
+
* - Tenant is selected by the URL path-prefix (`/<appDomain>/`), NOT by
|
|
36
|
+
* DEFAULT_TENANT_ID — the @astrojs/cloudflare dev runtime (workerd) reads env
|
|
37
|
+
* from .dev.vars/bindings, so a spawned process.env var never reaches it, and
|
|
38
|
+
* a bare `/` falls back to the first static tenant (pilot).
|
|
39
|
+
* - `--host` is passed so the server binds all interfaces (0.0.0.0 + ::).
|
|
40
|
+
* Without it astro/vite may bind IPv6 `::1` only, and browsers that resolve
|
|
41
|
+
* `localhost` to IPv4 `127.0.0.1` get "unable to connect".
|
|
42
|
+
*
|
|
43
|
+
* Usage:
|
|
44
|
+
* node scripts/tot-dev.mjs <tenant> [--port 4321]
|
|
45
|
+
* node scripts/tot-dev.mjs sample-store.example
|
|
46
|
+
* node scripts/tot-dev.mjs --workspace ~/tot/site [--port 4321] (standalone)
|
|
47
|
+
* (from a workspace dir with .tot/config.json) node <repo>/scripts/tot-dev.mjs
|
|
48
|
+
*
|
|
49
|
+
* Ctrl-C stops the server. Exit 1 = can't resolve tenant/domain/workspace; 2 = bad usage.
|
|
50
|
+
*/
|
|
51
|
+
import { spawn, spawnSync } from "node:child_process";
|
|
52
|
+
import {
|
|
53
|
+
readFileSync,
|
|
54
|
+
existsSync,
|
|
55
|
+
lstatSync,
|
|
56
|
+
mkdirSync,
|
|
57
|
+
renameSync,
|
|
58
|
+
rmSync,
|
|
59
|
+
cpSync,
|
|
60
|
+
} from "node:fs";
|
|
61
|
+
import { dirname, resolve, join } from "node:path";
|
|
62
|
+
import { fileURLToPath } from "node:url";
|
|
63
|
+
import { resolveDevSourceRoot } from "./dev/tenant-source-root.mjs";
|
|
64
|
+
import { firstFreePort } from "./dev/port-check.mjs";
|
|
65
|
+
import { resync, startWatch } from "./dev/checkout-watch.mjs";
|
|
66
|
+
|
|
67
|
+
const here = dirname(fileURLToPath(import.meta.url));
|
|
68
|
+
const repoRoot = resolve(here, ".."); // scripts/ -> repo root
|
|
69
|
+
|
|
70
|
+
const argv = process.argv.slice(2);
|
|
71
|
+
const flag = (name, def) => {
|
|
72
|
+
const i = argv.indexOf(`--${name}`);
|
|
73
|
+
return i !== -1 && argv[i + 1] && !argv[i + 1].startsWith("--") ? argv[i + 1] : def;
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
// `--version` / `-v` / `version` → print the runner's PUBLISHED version and exit.
|
|
77
|
+
// Reads the emitted root package.json (build-runner.mjs stamps RUNNER_VERSION in
|
|
78
|
+
// PUBLISH mode). This is the version the 2026-07-14 cache-staleness bug hid — the
|
|
79
|
+
// runner kept being served from a stale public-0.1.0 dir with no way to see it —
|
|
80
|
+
// so making it directly checkable (npm bin `storefront-runner`) is part of the fix.
|
|
81
|
+
if (argv[0] === "--version" || argv[0] === "-v" || argv[0] === "version") {
|
|
82
|
+
let v = "0.0.0";
|
|
83
|
+
try {
|
|
84
|
+
v = JSON.parse(readFileSync(join(repoRoot, "package.json"), "utf8")).version || v;
|
|
85
|
+
} catch {
|
|
86
|
+
/* fall back to 0.0.0 */
|
|
87
|
+
}
|
|
88
|
+
console.log(v);
|
|
89
|
+
process.exit(0);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// --- workspace (standalone) mode? --------------------------------------------
|
|
93
|
+
// `--workspace <path>` points at a developer's cloned tenant repo (flat layout +
|
|
94
|
+
// .tot/config.json). Absent → classic monorepo mode.
|
|
95
|
+
const workspacePath = flag("workspace", null);
|
|
96
|
+
|
|
97
|
+
// --- resolve the tenant + its appDomain --------------------------------------
|
|
98
|
+
// In monorepo mode the workspace config (if any) is read from CWD, as before.
|
|
99
|
+
// In standalone mode it comes from the checkout's .tot/config.json.
|
|
100
|
+
const cfgPath = workspacePath
|
|
101
|
+
? join(resolve(workspacePath), ".tot", "config.json")
|
|
102
|
+
: join(process.cwd(), ".tot", "config.json");
|
|
103
|
+
let cfg = null;
|
|
104
|
+
if (existsSync(cfgPath)) {
|
|
105
|
+
try {
|
|
106
|
+
cfg = JSON.parse(readFileSync(cfgPath, "utf8"));
|
|
107
|
+
} catch {
|
|
108
|
+
/* ignore */
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if (workspacePath && !cfg) {
|
|
113
|
+
console.error(
|
|
114
|
+
`✗ standalone mode: no readable .tot/config.json under ${resolve(workspacePath)}\n` +
|
|
115
|
+
` Is this a materialized tenant checkout (content/, public/, theme.json, .tot/config.json)?`,
|
|
116
|
+
);
|
|
117
|
+
process.exit(1);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
let tenant = argv[0] && !argv[0].startsWith("--") ? argv[0] : null;
|
|
121
|
+
tenant = tenant ?? cfg?.tenant ?? null;
|
|
122
|
+
|
|
123
|
+
if (!tenant) {
|
|
124
|
+
console.error(
|
|
125
|
+
"usage: node scripts/tot-dev.mjs <tenant> [--port 4321]\n" +
|
|
126
|
+
" node scripts/tot-dev.mjs --workspace <checkout-path> [--port 4321]\n" +
|
|
127
|
+
" (or run from a workspace dir containing .tot/config.json)",
|
|
128
|
+
);
|
|
129
|
+
process.exit(2);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/** The path-prefix segment that selects this tenant: its registered appDomain. */
|
|
133
|
+
function resolveDomain(id) {
|
|
134
|
+
// A workspace marker's `scope` carries the appDomain — but only trust it when
|
|
135
|
+
// it looks like a hostname (has a dot), since the path-prefix route only
|
|
136
|
+
// matches a domain that strictly claims the tenant (see tenantRouting.ts).
|
|
137
|
+
if (cfg?.scope && cfg.scope.includes(".")) return cfg.scope;
|
|
138
|
+
try {
|
|
139
|
+
const src = readFileSync(
|
|
140
|
+
resolve(repoRoot, "apps/storefront/src/config/tenants.ts"),
|
|
141
|
+
"utf8",
|
|
142
|
+
);
|
|
143
|
+
// Each tenant block declares tenant_id then appDomain; pair them up.
|
|
144
|
+
const re = /tenant_id:\s*"([^"]+)"[\s\S]*?appDomain:\s*"([^"]+)"/g;
|
|
145
|
+
for (let m; (m = re.exec(src)); ) if (m[1] === id) return m[2];
|
|
146
|
+
} catch {
|
|
147
|
+
/* ignore */
|
|
148
|
+
}
|
|
149
|
+
return null;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
const domain = resolveDomain(tenant);
|
|
153
|
+
if (!domain) {
|
|
154
|
+
console.error(
|
|
155
|
+
`✗ couldn't resolve a domain for "${tenant}" — is it registered in ` +
|
|
156
|
+
`apps/storefront/src/config/tenants.ts (or set as a hostname in .tot/config.json scope)?`,
|
|
157
|
+
);
|
|
158
|
+
process.exit(1);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// --- standalone: graft the checkout into the storefront's globbed paths -------
|
|
162
|
+
// COPY the checkout's files into the in-tree paths (so Vite's fs.allow trusts them)
|
|
163
|
+
// + fs.watch the checkout to re-sync each save into the in-tree copy (fires HMR).
|
|
164
|
+
// Records let us undo everything on exit (leave the tree exactly as found).
|
|
165
|
+
/** @type {{ target: string, backup: string|null }[]} */
|
|
166
|
+
const grafts = [];
|
|
167
|
+
/** @type {import("node:fs").FSWatcher[]} */
|
|
168
|
+
const watchers = [];
|
|
169
|
+
let cleanedUp = false;
|
|
170
|
+
|
|
171
|
+
function isSymlink(p) {
|
|
172
|
+
try {
|
|
173
|
+
return lstatSync(p).isSymbolicLink();
|
|
174
|
+
} catch {
|
|
175
|
+
return false;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Move `from` → `to`, with a cross-device fallback. In the container the baked-in
|
|
181
|
+
* `tenants/<id>/` lives in a read-only overlay layer, and overlayfs can't rename a
|
|
182
|
+
* directory across the layer boundary — `renameSync` throws EXDEV. Fall back to a
|
|
183
|
+
* recursive copy + remove so the graft's move-aside/restore still works there. On
|
|
184
|
+
* macOS (same-fs) the fast rename is taken.
|
|
185
|
+
*/
|
|
186
|
+
function movePath(from, to) {
|
|
187
|
+
try {
|
|
188
|
+
renameSync(from, to);
|
|
189
|
+
} catch (e) {
|
|
190
|
+
if (e?.code !== "EXDEV") throw e;
|
|
191
|
+
cpSync(from, to, { recursive: true });
|
|
192
|
+
rmSync(from, { recursive: true, force: true });
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
function cleanupGrafts() {
|
|
197
|
+
if (cleanedUp) return;
|
|
198
|
+
cleanedUp = true;
|
|
199
|
+
for (const w of watchers) {
|
|
200
|
+
try {
|
|
201
|
+
w.close();
|
|
202
|
+
} catch {
|
|
203
|
+
/* best effort */
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
// Undo in reverse: remove our copied-in files, then restore any backed-up content.
|
|
207
|
+
for (const { target, backup } of grafts.reverse()) {
|
|
208
|
+
try {
|
|
209
|
+
if (existsSync(target) || isSymlink(target)) rmSync(target, { recursive: true, force: true });
|
|
210
|
+
} catch {
|
|
211
|
+
/* best effort */
|
|
212
|
+
}
|
|
213
|
+
if (backup) {
|
|
214
|
+
try {
|
|
215
|
+
movePath(backup, target);
|
|
216
|
+
} catch {
|
|
217
|
+
/* best effort */
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// resync() + startWatch() moved to scripts/dev/checkout-watch.mjs (testable +
|
|
224
|
+
// ships in the runner via copyMjsFlat). startWatch now polls by default for
|
|
225
|
+
// reliable save detection — native fs.watch dropped editor atomic-saves on macOS.
|
|
226
|
+
|
|
227
|
+
if (workspacePath) {
|
|
228
|
+
const wsRoot = resolve(workspacePath);
|
|
229
|
+
const mappings = Array.isArray(cfg.mappings) ? cfg.mappings : [];
|
|
230
|
+
if (mappings.length === 0) {
|
|
231
|
+
console.error(`✗ .tot/config.json has no "mappings" — cannot graft the checkout.`);
|
|
232
|
+
process.exit(1);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
console.log(`\n tot dev — standalone (${tenant})`);
|
|
236
|
+
console.log(` checkout: ${wsRoot}`);
|
|
237
|
+
|
|
238
|
+
for (const map of mappings) {
|
|
239
|
+
// `workspace` is the checkout-relative prefix (e.g. "content/", "theme.json").
|
|
240
|
+
// COLOCATION: a tenant IS the repo-root dir `tenants/<id>/`, and the checkout
|
|
241
|
+
// (a `git subtree split --prefix=tenants/<id>` of that dir) has the SAME layout
|
|
242
|
+
// at its root — so the graft is an identity map: content/ -> tenants/<id>/content/,
|
|
243
|
+
// public/ -> tenants/<id>/public/, theme.json -> tenants/<id>/theme.json. We map
|
|
244
|
+
// structurally (ignoring any legacy `repo` field in the checkout's .tot/config.json).
|
|
245
|
+
const wsRel = String(map.workspace ?? "").replace(/\/+$/, "");
|
|
246
|
+
if (!wsRel) continue;
|
|
247
|
+
const repoRel = join("tenants", tenant, wsRel);
|
|
248
|
+
|
|
249
|
+
const source = resolve(wsRoot, wsRel); // real files in the checkout
|
|
250
|
+
const target = resolve(repoRoot, repoRel); // where the app globs them
|
|
251
|
+
const isDir = map.kind !== "file";
|
|
252
|
+
|
|
253
|
+
if (!existsSync(source)) {
|
|
254
|
+
console.warn(` ⚠ checkout has no ${wsRel} — skipping (${map.kind ?? "?"})`);
|
|
255
|
+
continue;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// Ensure the destination's parent exists (e.g. content/ or themes/tenants/).
|
|
259
|
+
mkdirSync(dirname(target), { recursive: true });
|
|
260
|
+
|
|
261
|
+
// If real in-tree content already occupies the destination, move it aside so
|
|
262
|
+
// we can restore it verbatim on exit (never destroy tracked tenant content).
|
|
263
|
+
let backup = null;
|
|
264
|
+
if (existsSync(target) || isSymlink(target)) {
|
|
265
|
+
backup = `${target}.__totdev_bak_${process.pid}`;
|
|
266
|
+
movePath(target, backup);
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
// COPY the checkout's files into the in-tree path (inside Vite's trusted root).
|
|
270
|
+
cpSync(source, target, { recursive: true });
|
|
271
|
+
grafts.push({ target, backup });
|
|
272
|
+
|
|
273
|
+
// WATCH the checkout for saves → re-sync into the in-tree copy → Vite HMR.
|
|
274
|
+
// `startWatch` polls by default (reliable — native fs.watch dropped editor
|
|
275
|
+
// atomic-saves on macOS); TOT_DEV_NATIVE_WATCH=1 opts into event-driven watch.
|
|
276
|
+
try {
|
|
277
|
+
const w = startWatch(source, isDir, (rel) => resync(source, target, rel));
|
|
278
|
+
watchers.push(w);
|
|
279
|
+
} catch (e) {
|
|
280
|
+
console.warn(` ⚠ could not watch ${wsRel} (edits won't hot-reload): ${e?.message}`);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
console.log(` ↪ ${repoRel} ⇐ ${wsRel}${backup ? " (in-tree content backed up)" : ""}`);
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
if (grafts.length === 0) {
|
|
287
|
+
console.error(`✗ nothing to graft — the checkout had none of the mapped prefixes.`);
|
|
288
|
+
cleanupGrafts();
|
|
289
|
+
process.exit(1);
|
|
290
|
+
}
|
|
291
|
+
} else if (!existsSync(resolve(repoRoot, "tenants", tenant, "content"))) {
|
|
292
|
+
console.warn(`⚠ no tenants/${tenant}/content/ found — the tenant may not be registered yet.`);
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
// Resolve the port BEFORE printing a URL or booting astro. Vite's `strictPort`
|
|
296
|
+
// defaults off, so a taken port makes astro SILENTLY drift to the next free one —
|
|
297
|
+
// which then mismatches the URL we (and the CLI's liveness poll) advertise, and a
|
|
298
|
+
// stale server on the original port produced a false "You're live" (2026-07-14).
|
|
299
|
+
// Instead we pick the first free port ourselves and PRINT it, so a busy default
|
|
300
|
+
// port "just works" (better UX than failing). See port-check.mjs.
|
|
301
|
+
const requestedPort = Number(flag("port", "4321"));
|
|
302
|
+
const resolvedPort = await firstFreePort(requestedPort);
|
|
303
|
+
const port = String(resolvedPort);
|
|
304
|
+
if (resolvedPort !== requestedPort) {
|
|
305
|
+
console.log(` ⚠ port ${requestedPort} was busy — starting on ${resolvedPort} instead.`);
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
// Land the developer on their store ROOT (plain save→reload HMR against their own
|
|
309
|
+
// site) — not the /dev cockpit, which is a hosted-only surface excluded from the
|
|
310
|
+
// local runner build (see build-runner.mjs DROP + stripCockpitPlugins).
|
|
311
|
+
const url = `http://localhost:${port}/${domain}/`;
|
|
312
|
+
|
|
313
|
+
console.log(`\n tot dev — ${tenant}`);
|
|
314
|
+
console.log(` → ${url}`);
|
|
315
|
+
if (workspacePath) {
|
|
316
|
+
console.log(` Edit files in your CHECKOUT (content/*.html, theme.json, public/…) + save → the browser reloads.`);
|
|
317
|
+
} else {
|
|
318
|
+
console.log(` Edit tenants/${tenant}/content/*.html (or the theme) + save → the browser reloads.`);
|
|
319
|
+
}
|
|
320
|
+
console.log(` Private local preview — nothing is published. Server binds all interfaces (LAN-visible). Ctrl-C to stop.\n`);
|
|
321
|
+
|
|
322
|
+
// --- mirror colocated tenant public/ assets into the storefront public/ dir ----
|
|
323
|
+
// astro dev is exec'd directly below (bypassing the `predev` npm lifecycle), so
|
|
324
|
+
// run the copy here: tenants/<id>/public/** -> apps/storefront/public/tenants/<id>/.
|
|
325
|
+
// Must run AFTER any standalone graft (so a grafted checkout's public/ is mirrored).
|
|
326
|
+
{
|
|
327
|
+
const r = spawnSync(
|
|
328
|
+
"node",
|
|
329
|
+
[resolve(repoRoot, "scripts/build/copy-tenant-assets.mjs")],
|
|
330
|
+
{ cwd: repoRoot, stdio: "inherit" },
|
|
331
|
+
);
|
|
332
|
+
if (r.status !== 0) {
|
|
333
|
+
console.warn(" ⚠ copy-tenant-assets failed — static tenant assets may 404.");
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
// --- boot astro dev (Vite HMR); --host so IPv4 `localhost`/127.0.0.1 both work -
|
|
338
|
+
// In standalone mode we MUST keep astro in the FOREGROUND, attached to this
|
|
339
|
+
// process: Astro 7 auto-detects an agent-run terminal and forks the dev server to
|
|
340
|
+
// a detached daemon (`astro dev stop/status`), which would make THIS process exit
|
|
341
|
+
// immediately — firing our cleanup and tearing down the graft symlinks while the
|
|
342
|
+
// daemon keeps serving the (restored) in-tree content. Setting ASTRO_DEV_BACKGROUND
|
|
343
|
+
// tells Astro "you are already the managed child → run foreground" (see astro's
|
|
344
|
+
// cli/dev/{index,background}.js), so our SIGINT/exit lifecycle and symlink cleanup
|
|
345
|
+
// stay correct. Monorepo mode is left untouched (current behavior).
|
|
346
|
+
// TOT_DEV_TENANT tells the inline-AI edit loop (WS5 G1 — astro.config.mjs's
|
|
347
|
+
// inlineAiEditPlugin, scripts/dev/ai-edit.mjs) which tenant's content/theme to
|
|
348
|
+
// read+write: one `tot dev <tenant>` run always serves exactly one tenant, so
|
|
349
|
+
// there's no per-request domain lookup to do.
|
|
350
|
+
// TOT_DEV_SOURCE_ROOT tells the /dev file tools (C3 tree/viewer, C4 diff, C5
|
|
351
|
+
// writer — scripts/dev/tenant-source-root.mjs) WHERE the developer's editable
|
|
352
|
+
// source lives. STANDALONE: the developer's REAL checkout (wsRoot) — the tools
|
|
353
|
+
// MUST read+write it, NOT the in-tree graft, or browser edits are silently lost
|
|
354
|
+
// on the next resync. MONOREPO: the in-tree tenants/<id>/ dir.
|
|
355
|
+
const devSourceRoot = resolveDevSourceRoot(workspacePath, repoRoot, tenant);
|
|
356
|
+
const spawnEnv = workspacePath
|
|
357
|
+
? { ...process.env, ASTRO_DEV_BACKGROUND: "1", TOT_DEV_TENANT: tenant, TOT_DEV_SOURCE_ROOT: devSourceRoot }
|
|
358
|
+
: { ...process.env, TOT_DEV_TENANT: tenant, TOT_DEV_SOURCE_ROOT: devSourceRoot };
|
|
359
|
+
const child = spawn(
|
|
360
|
+
"pnpm",
|
|
361
|
+
["--filter", "@tot/storefront-runner", "exec", "astro", "dev", "--port", port, "--host"],
|
|
362
|
+
{ cwd: repoRoot, stdio: "inherit", env: spawnEnv },
|
|
363
|
+
);
|
|
364
|
+
|
|
365
|
+
// Clean up grafts on every exit path so we never leave symlinks in the tree.
|
|
366
|
+
function shutdown(signal) {
|
|
367
|
+
child.kill(signal);
|
|
368
|
+
}
|
|
369
|
+
child.on("exit", (code) => {
|
|
370
|
+
cleanupGrafts();
|
|
371
|
+
process.exit(code ?? 0);
|
|
372
|
+
});
|
|
373
|
+
process.on("SIGINT", () => shutdown("SIGINT"));
|
|
374
|
+
process.on("SIGTERM", () => shutdown("SIGTERM"));
|
|
375
|
+
// Belt-and-suspenders: if the parent dies without the child exit firing.
|
|
376
|
+
process.on("exit", cleanupGrafts);
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# tenants/
|
|
2
|
+
|
|
3
|
+
This directory is the **runtime graft target** for the local dev loop — it ships
|
|
4
|
+
**empty** on purpose.
|
|
5
|
+
|
|
6
|
+
The storefront runner is a moat-free LOCAL dev harness. It bundles **no** tenant
|
|
7
|
+
content. When you run `tot dev`, your OWN tenant checkout (content, theme, public
|
|
8
|
+
assets) is grafted into `tenants/<your-id>/` here, and the renderer's
|
|
9
|
+
checkout-glob resolver discovers and serves it at `/<your-domain>/`. On exit the
|
|
10
|
+
graft is removed and this directory is left empty again.
|
|
11
|
+
|
|
12
|
+
Do not commit real merchant content here — the runner's build-time clean-guard
|
|
13
|
+
(`scripts/build/check-runner-clean.mjs`) fails the build if anything but this
|
|
14
|
+
README appears under `tenants/`.
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>Storefront runner — local dev</title>
|
|
7
|
+
<meta name="description" content="You're running the Token of Trust storefront runner locally.">
|
|
8
|
+
<meta name="robots" content="noindex,nofollow">
|
|
9
|
+
<style>
|
|
10
|
+
:root { color-scheme: light dark; }
|
|
11
|
+
* { box-sizing: border-box; margin: 0; }
|
|
12
|
+
body {
|
|
13
|
+
font-family: system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
|
|
14
|
+
line-height: 1.6; color: #16243d; background: #f5f7fa;
|
|
15
|
+
min-height: 100vh; display: grid; place-items: center; padding: 2rem;
|
|
16
|
+
}
|
|
17
|
+
.card {
|
|
18
|
+
max-width: 640px; width: 100%; background: #fff; border: 1px solid #e3e8ef;
|
|
19
|
+
border-radius: 16px; padding: 2.5rem; box-shadow: 0 6px 24px rgba(15,33,56,.08);
|
|
20
|
+
}
|
|
21
|
+
.badge {
|
|
22
|
+
display: inline-block; font-size: .78rem; font-weight: 600; letter-spacing: .04em;
|
|
23
|
+
text-transform: uppercase; color: #1b3b64; background: #eaeef4;
|
|
24
|
+
padding: .3rem .7rem; border-radius: 999px; margin-bottom: 1.1rem;
|
|
25
|
+
}
|
|
26
|
+
h1 { font-size: 1.7rem; line-height: 1.2; margin-bottom: .6rem; color: #0f2138; }
|
|
27
|
+
p { color: #5b6a82; margin-bottom: 1rem; }
|
|
28
|
+
code {
|
|
29
|
+
font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace; font-size: .92em;
|
|
30
|
+
background: #f5f7fa; border: 1px solid #e3e8ef; border-radius: 6px; padding: .1rem .4rem;
|
|
31
|
+
}
|
|
32
|
+
ul { margin: 0 0 1rem 1.2rem; color: #5b6a82; }
|
|
33
|
+
li { margin-bottom: .4rem; }
|
|
34
|
+
.muted { font-size: .88rem; color: #8a95a8; margin-bottom: 0; }
|
|
35
|
+
@media (prefers-color-scheme: dark) {
|
|
36
|
+
body { color: #e6ebf3; background: #0f1622; }
|
|
37
|
+
.card { background: #16202e; border-color: #26313f; box-shadow: none; }
|
|
38
|
+
h1 { color: #f2f5f9; }
|
|
39
|
+
p, ul { color: #9aa6b8; }
|
|
40
|
+
.badge { color: #cdd8e6; background: #22303f; }
|
|
41
|
+
code { background: #0f1622; border-color: #26313f; }
|
|
42
|
+
}
|
|
43
|
+
</style>
|
|
44
|
+
</head>
|
|
45
|
+
<body>
|
|
46
|
+
<main class="card">
|
|
47
|
+
<span class="badge">Local dev</span>
|
|
48
|
+
<h1>You're running the Token of Trust storefront runner locally.</h1>
|
|
49
|
+
<p>This is the default home page. To make it yours, edit
|
|
50
|
+
<code>tenants/home/content/home.html</code> and save — this page reloads automatically.</p>
|
|
51
|
+
<ul>
|
|
52
|
+
<li>Your real store renders here once you check one out with the Token of Trust CLI.</li>
|
|
53
|
+
<li>Visit <code>/sample-store.example/</code> to see the bundled sample store, including the 21+ age-gate and nicotine-warning compliance affordances.</li>
|
|
54
|
+
</ul>
|
|
55
|
+
<p class="muted">Serving from the storefront runner. No account or network required.</p>
|
|
56
|
+
</main>
|
|
57
|
+
</body>
|
|
58
|
+
</html>
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
{
|
|
2
|
+
"seoTitle": "Storefront runner — local dev",
|
|
3
|
+
"seoDescription": "You're running the Token of Trust storefront runner locally.",
|
|
4
|
+
"blocks": [
|
|
5
|
+
{
|
|
6
|
+
"component": "hero",
|
|
7
|
+
"headline": "Running locally",
|
|
8
|
+
"subhead": "You're running the Token of Trust storefront runner. Edit tenants/home/content/home.html and save to change this page."
|
|
9
|
+
}
|
|
10
|
+
]
|
|
11
|
+
}
|