@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,203 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Signed-in viewer session for the demo access gate (Track: "Sign in with ToT").
|
|
3
|
+
*
|
|
4
|
+
* After a visitor verifies a one-time email code (tot20 `verifyAccessCode`), we
|
|
5
|
+
* hold a server-side session so the gate can let them back in without re-auth.
|
|
6
|
+
* The session is **KV-backed** (revocable + no signing-key handling): the cookie
|
|
7
|
+
* carries only an opaque random id; the record (email, resource, roles) lives in
|
|
8
|
+
* the `SESSION` KV namespace with a TTL. Deleting the KV entry (or letting it
|
|
9
|
+
* expire) revokes access — a static bearer password can't do that.
|
|
10
|
+
*
|
|
11
|
+
* Renderer-agnostic and runtime-portable (WebCrypto + a small store seam). The
|
|
12
|
+
* store is an interface so routes/gate use `KvSessionStore` in the worker and an
|
|
13
|
+
* in-memory impl in tests. Nothing here reads env or touches cookies — the
|
|
14
|
+
* routes own cookie I/O (via Astro `context.cookies`) and the gate owns the
|
|
15
|
+
* host↔resource check.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
/** The cookie the browser carries; value is an opaque session id (not the record). */
|
|
19
|
+
export const SESSION_COOKIE = "tot_session";
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* A ToT-staff viewer's ACTIVE vendor selection — the two-phase-admission marker
|
|
23
|
+
* (#8425 staff cross-vendor access). This is a DISTINCT access path from
|
|
24
|
+
* `tenants[]`: a staff user isn't a member of a bounded set of vendors, so we
|
|
25
|
+
* NEVER synthesize a `tenants[]` entry for them. Instead, after the staff viewer
|
|
26
|
+
* explicitly searches + enters a vendor and tot20's `staffVendorAccess` writes
|
|
27
|
+
* the server-side audit row, we stamp THIS marker on the session (KV) scoped to
|
|
28
|
+
* the returned staffRoles `capability` and a short `exp`. The dashboard guard and
|
|
29
|
+
* login gate consult it as a separate branch from membership.
|
|
30
|
+
*/
|
|
31
|
+
export interface StaffSelection {
|
|
32
|
+
/** The vendor appDomain the staff viewer was admitted to (exact match only). */
|
|
33
|
+
resource: string;
|
|
34
|
+
/** staffRoles-scoped capability tot20 returned for this admission. */
|
|
35
|
+
capability: string;
|
|
36
|
+
/** Expiry (epoch seconds) — short-lived; re-checked on every gate/guard read. */
|
|
37
|
+
exp: number;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** Default session lifetime — a demo viewing session, revocable via KV. */
|
|
41
|
+
export const DEFAULT_SESSION_TTL_SECONDS = 7 * 24 * 3600;
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* What we know about a signed-in viewer.
|
|
45
|
+
*
|
|
46
|
+
* Phase 3: `email`/`resource`/`roles`/`capability` are now derived from tot's
|
|
47
|
+
* OFFLINE-VERIFIED `id_token` claims (see `@/lib/auth/identityToken`
|
|
48
|
+
* `sessionFromClaims`), not from trusting tot's flat `/verify` JSON. `capability`
|
|
49
|
+
* is the coarse standing on `resource` (owner|admin|member). `tenants`/`staff`
|
|
50
|
+
* are the multi-tenant claims carried in the verified token (present when the
|
|
51
|
+
* session was minted via the Phase-3 path; optional for backward compatibility).
|
|
52
|
+
*/
|
|
53
|
+
export interface SessionRecord {
|
|
54
|
+
/** Verified email (lowercased) — the principal that passed the grant check. */
|
|
55
|
+
email: string;
|
|
56
|
+
/** The resource (appDomain/tenant, e.g. "sample-store.example") this session authorizes. */
|
|
57
|
+
resource: string;
|
|
58
|
+
/** Display roles for `resource` (Phase 3: the coarse role from verified claims). */
|
|
59
|
+
roles: string[];
|
|
60
|
+
/** Coarse capability on `resource` (owner|admin|member; legacy: developer|inviter|staff|none). */
|
|
61
|
+
capability: string;
|
|
62
|
+
/** All tenant memberships from the verified assertion (policy input). Phase-3 path only. */
|
|
63
|
+
tenants?: { resource: string; role: string }[];
|
|
64
|
+
/** ToT-staff scope from the verified assertion (sandboxed; grants nothing today). Phase-3 path only. */
|
|
65
|
+
staff?: string[];
|
|
66
|
+
/**
|
|
67
|
+
* A staff viewer's active, explicitly-entered vendor selection (the two-phase
|
|
68
|
+
* admission marker). Present ONLY after `/api/dashboard/enter-vendor` succeeds;
|
|
69
|
+
* DISTINCT from `tenants[]` — never a synthesized membership. See {@link StaffSelection}.
|
|
70
|
+
*/
|
|
71
|
+
staffSelection?: StaffSelection;
|
|
72
|
+
/** Issued-at, epoch seconds. */
|
|
73
|
+
createdAt: number;
|
|
74
|
+
/** Expiry, epoch seconds. The store TTL mirrors this; we re-check on read. */
|
|
75
|
+
exp: number;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/** Minimal KV-ish seam so this unit-tests without a live KVNamespace. */
|
|
79
|
+
export interface SessionStore {
|
|
80
|
+
put(id: string, value: string, ttlSeconds: number): Promise<void>;
|
|
81
|
+
get(id: string): Promise<string | null>;
|
|
82
|
+
delete(id: string): Promise<void>;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/** Namespacing so session ids never collide with other SESSION-KV keys. */
|
|
86
|
+
const KEY_PREFIX = "authsession:";
|
|
87
|
+
|
|
88
|
+
/** A `SessionStore` over a Cloudflare `KVNamespace` (the `SESSION` binding). */
|
|
89
|
+
export class KvSessionStore implements SessionStore {
|
|
90
|
+
constructor(private readonly kv: KVNamespace) {}
|
|
91
|
+
async put(id: string, value: string, ttlSeconds: number): Promise<void> {
|
|
92
|
+
// KV requires expirationTtl >= 60; clamp so short TTLs in tests/dev don't throw.
|
|
93
|
+
await this.kv.put(KEY_PREFIX + id, value, {
|
|
94
|
+
expirationTtl: Math.max(60, Math.floor(ttlSeconds)),
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
async get(id: string): Promise<string | null> {
|
|
98
|
+
return this.kv.get(KEY_PREFIX + id);
|
|
99
|
+
}
|
|
100
|
+
async delete(id: string): Promise<void> {
|
|
101
|
+
await this.kv.delete(KEY_PREFIX + id);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/** A cryptographically-random, URL-safe session id (128 bits). */
|
|
106
|
+
export function newSessionId(): string {
|
|
107
|
+
const bytes = new Uint8Array(16);
|
|
108
|
+
crypto.getRandomValues(bytes);
|
|
109
|
+
let bin = "";
|
|
110
|
+
for (const b of bytes) bin += String.fromCharCode(b);
|
|
111
|
+
return btoa(bin).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Create a session for a verified viewer: mint an id, persist the record with a
|
|
116
|
+
* matching TTL, return the id (the caller sets it as the cookie). `nowSeconds`
|
|
117
|
+
* is injected so this stays pure/testable.
|
|
118
|
+
*/
|
|
119
|
+
export async function createSession(
|
|
120
|
+
store: SessionStore,
|
|
121
|
+
fields: Omit<SessionRecord, "createdAt" | "exp">,
|
|
122
|
+
ttlSeconds: number,
|
|
123
|
+
nowSeconds: number,
|
|
124
|
+
): Promise<{ id: string; record: SessionRecord }> {
|
|
125
|
+
const record: SessionRecord = {
|
|
126
|
+
...fields,
|
|
127
|
+
email: fields.email.toLowerCase(),
|
|
128
|
+
createdAt: nowSeconds,
|
|
129
|
+
exp: nowSeconds + ttlSeconds,
|
|
130
|
+
};
|
|
131
|
+
const id = newSessionId();
|
|
132
|
+
await store.put(id, JSON.stringify(record), ttlSeconds);
|
|
133
|
+
return { id, record };
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Read + validate a session by id. Returns the record only when it exists, is
|
|
138
|
+
* well-formed, has not expired, and (when `resource` is given) authorizes THAT
|
|
139
|
+
* resource — so a session minted for tenant A can never open tenant B's gate.
|
|
140
|
+
* A malformed or expired record is best-effort deleted.
|
|
141
|
+
*/
|
|
142
|
+
export async function readSession(
|
|
143
|
+
store: SessionStore,
|
|
144
|
+
id: string | null | undefined,
|
|
145
|
+
nowSeconds: number,
|
|
146
|
+
resource?: string,
|
|
147
|
+
): Promise<SessionRecord | null> {
|
|
148
|
+
if (!id) return null;
|
|
149
|
+
const raw = await store.get(id);
|
|
150
|
+
if (!raw) return null;
|
|
151
|
+
let record: SessionRecord;
|
|
152
|
+
try {
|
|
153
|
+
record = JSON.parse(raw) as SessionRecord;
|
|
154
|
+
} catch {
|
|
155
|
+
await store.delete(id);
|
|
156
|
+
return null;
|
|
157
|
+
}
|
|
158
|
+
if (
|
|
159
|
+
!record ||
|
|
160
|
+
typeof record.email !== "string" ||
|
|
161
|
+
typeof record.resource !== "string" ||
|
|
162
|
+
typeof record.exp !== "number"
|
|
163
|
+
) {
|
|
164
|
+
await store.delete(id);
|
|
165
|
+
return null;
|
|
166
|
+
}
|
|
167
|
+
if (record.exp <= nowSeconds) {
|
|
168
|
+
await store.delete(id);
|
|
169
|
+
return null;
|
|
170
|
+
}
|
|
171
|
+
if (resource && record.resource !== resource) return null;
|
|
172
|
+
return record;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Read a session, apply `mutate`, and re-persist it under the SAME id with the
|
|
177
|
+
* record's REMAINING lifetime (`exp - now`) as the store TTL — so stamping a
|
|
178
|
+
* staff selection (or any field) never extends the session past its original
|
|
179
|
+
* expiry. Returns the updated record, or null when there's no valid session to
|
|
180
|
+
* mutate (expired/missing/malformed are handled by {@link readSession}). The
|
|
181
|
+
* caller must NOT change `exp` in `mutate` unless it also intends to change TTL.
|
|
182
|
+
*/
|
|
183
|
+
export async function updateSession(
|
|
184
|
+
store: SessionStore,
|
|
185
|
+
id: string | null | undefined,
|
|
186
|
+
mutate: (record: SessionRecord) => SessionRecord,
|
|
187
|
+
nowSeconds: number,
|
|
188
|
+
): Promise<SessionRecord | null> {
|
|
189
|
+
const current = await readSession(store, id, nowSeconds);
|
|
190
|
+
if (!current || !id) return null;
|
|
191
|
+
const next = mutate({ ...current });
|
|
192
|
+
const ttl = Math.max(1, next.exp - nowSeconds);
|
|
193
|
+
await store.put(id, JSON.stringify(next), ttl);
|
|
194
|
+
return next;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/** Revoke a session (logout / admin kill). Idempotent. */
|
|
198
|
+
export async function revokeSession(
|
|
199
|
+
store: SessionStore,
|
|
200
|
+
id: string | null | undefined,
|
|
201
|
+
): Promise<void> {
|
|
202
|
+
if (id) await store.delete(id);
|
|
203
|
+
}
|