@tokenoftrust/storefront-runner 1.4.1 → 2.0.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 (474) hide show
  1. package/apps/storefront/astro.config.mjs +17 -1
  2. package/apps/storefront/env.d.ts +54 -5
  3. package/apps/storefront/integrations/materialize-guard.mjs +54 -0
  4. package/apps/storefront/migrations-apps/0002_good_switch.sql +26 -0
  5. package/apps/storefront/migrations-apps/0003_mute_marauders.sql +12 -0
  6. package/apps/storefront/migrations-apps/0004_deep_morph.sql +36 -0
  7. package/apps/storefront/migrations-apps/0005_common_mystique.sql +23 -0
  8. package/apps/storefront/migrations-apps/0006_broad_microchip.sql +16 -0
  9. package/apps/storefront/migrations-apps/0007_brave_outlaw_kid.sql +15 -0
  10. package/apps/storefront/migrations-apps/0008_blue_dazzler.sql +11 -0
  11. package/apps/storefront/migrations-apps/0009_bored_shinobi_shaw.sql +15 -0
  12. package/apps/storefront/migrations-apps/0010_ws4_verified_provider_measurement.sql +35 -0
  13. package/apps/storefront/migrations-apps/meta/0002_snapshot.json +1160 -0
  14. package/apps/storefront/migrations-apps/meta/0003_snapshot.json +1235 -0
  15. package/apps/storefront/migrations-apps/meta/0004_snapshot.json +1474 -0
  16. package/apps/storefront/migrations-apps/meta/0005_snapshot.json +1617 -0
  17. package/apps/storefront/migrations-apps/meta/0006_snapshot.json +1716 -0
  18. package/apps/storefront/migrations-apps/meta/0007_snapshot.json +1812 -0
  19. package/apps/storefront/migrations-apps/meta/0008_snapshot.json +1881 -0
  20. package/apps/storefront/migrations-apps/meta/0009_snapshot.json +1979 -0
  21. package/apps/storefront/migrations-apps/meta/_journal.json +56 -0
  22. package/apps/storefront/public/apps/tot.demo.analytics/0.1.0/entry.js +42 -0
  23. package/apps/storefront/public/js/dashboard-team.js +38 -2
  24. package/apps/storefront/public/platform/amplitude-http/v1/entry.mjs +19 -0
  25. package/apps/storefront/public/platform/google-measurement/v1/entry.mjs +19 -0
  26. package/apps/storefront/public/shared/commerce-chrome.css +20 -2
  27. package/apps/storefront/scripts/build-v84-clean-clone-standalone.mjs +68 -0
  28. package/apps/storefront/scripts/deny-v84-clean-clone-standalone-capabilities.mjs +111 -0
  29. package/apps/storefront/scripts/deny-v84-dogfood-capabilities.mjs +2 -0
  30. package/apps/storefront/scripts/deny-v84-lifecycle-capabilities.mjs +247 -0
  31. package/apps/storefront/scripts/dogfood-v84-nonportable-routes.ts +73 -0
  32. package/apps/storefront/scripts/fixed-v84-native-install-producer.ts +79 -0
  33. package/apps/storefront/scripts/generated/v84-clean-clone-replay.bundle.mjs +10982 -0
  34. package/apps/storefront/scripts/import-v84-through-ws2.ts +38 -0
  35. package/apps/storefront/scripts/initialize-v84-lifecycle-tooling-dependency.ts +2 -0
  36. package/apps/storefront/scripts/initialize-v84-lifecycle-tooling.ts +4 -0
  37. package/apps/storefront/scripts/install-v84-native-through-ws1.ts +185 -0
  38. package/apps/storefront/scripts/prove-clone-isolation.ts +506 -0
  39. package/apps/storefront/scripts/replay-v84-clean-clone-pass.ts +148 -0
  40. package/apps/storefront/scripts/replay-v84-clean-clone-standalone.ts +35 -0
  41. package/apps/storefront/scripts/replay-v84-clean-clone.ts +214 -0
  42. package/apps/storefront/scripts/run-fixed-v84-native-install-producer.mjs +18 -0
  43. package/apps/storefront/scripts/run-v84-clone-isolation-proof.mjs +18 -0
  44. package/apps/storefront/scripts/run-v84-clone-lifecycle-proof.mjs +18 -0
  45. package/apps/storefront/scripts/run-v84-import-through-ws2-proof.mjs +17 -0
  46. package/apps/storefront/scripts/run-v84-nonportable-route-dogfood.mjs +15 -0
  47. package/apps/storefront/scripts/sanitize-v84-proof-environment.mjs +21 -0
  48. package/apps/storefront/scripts/v84CleanCloneGraphPin.ts +42 -0
  49. package/apps/storefront/scripts/v84CleanCloneSourceGraph.ts +145 -0
  50. package/apps/storefront/scripts/validate-v84-clone-lifecycle.ts +18 -0
  51. package/apps/storefront/src/components/Img.astro +1 -1
  52. package/apps/storefront/src/components/Seo.astro +65 -1
  53. package/apps/storefront/src/components/admin/AdminBlogDetailRail.astro +878 -0
  54. package/apps/storefront/src/components/admin/AdminBlogTab.astro +1435 -0
  55. package/apps/storefront/src/components/admin/AdminCustomersTab.astro +3 -7
  56. package/apps/storefront/src/components/admin/AdminIntegrationsTab.astro +105 -0
  57. package/apps/storefront/src/components/admin/AdminPublishTab.astro +1980 -2564
  58. package/apps/storefront/src/components/admin/AdminSettingsTab.astro +2 -2
  59. package/apps/storefront/src/components/admin/AdminSubscriptionsTab.astro +1 -3
  60. package/apps/storefront/src/components/admin/orders/OrdersWorkspace.astro +5 -5
  61. package/apps/storefront/src/components/blog/BlogPaywall.astro +87 -0
  62. package/apps/storefront/src/components/blog/PostAccessBadge.astro +43 -0
  63. package/apps/storefront/src/components/blog/PostGrid.astro +57 -0
  64. package/apps/storefront/src/components/blog/ReactionBar.astro +214 -0
  65. package/apps/storefront/src/components/chrome/NavDropdown.astro +12 -0
  66. package/apps/storefront/src/components/chrome/SiteFooter.astro +7 -2
  67. package/apps/storefront/src/components/chrome/SiteHeader.astro +46 -10
  68. package/apps/storefront/src/components/compliance/AdultSignatureNotice.astro +1 -1
  69. package/apps/storefront/src/components/compliance/ComplianceNotice.astro +1 -2
  70. package/apps/storefront/src/components/compliance/ExciseTaxNotice.astro +1 -2
  71. package/apps/storefront/src/components/compliance/PactActNotice.astro +1 -1
  72. package/apps/storefront/src/components/compliance/Prop65Warning.astro +1 -2
  73. package/apps/storefront/src/components/compliance/PurchaseLimitNotice.astro +2 -3
  74. package/apps/storefront/src/components/compliance/ShippingRestrictionNotice.astro +2 -2
  75. package/apps/storefront/src/components/compliance/StateEligibilityNotice.astro +2 -3
  76. package/apps/storefront/src/components/content/TrustStrip.astro +4 -4
  77. package/apps/storefront/src/components/islands/MobileNav.tsx +26 -1
  78. package/apps/storefront/src/components/islands/VariantSelector.tsx +27 -2
  79. package/apps/storefront/src/components/islands/useCheckoutHandoffCorrelation.ts +75 -0
  80. package/apps/storefront/src/components/membership/IllustrativeLabel.astro +1 -2
  81. package/apps/storefront/src/components/membership/TierCards.astro +2 -2
  82. package/apps/storefront/src/components/membership/TierComparisonTable.astro +2 -2
  83. package/apps/storefront/src/components/subscription/SavingsExplainer.astro +2 -2
  84. package/apps/storefront/src/config/adminTenantLookup.ts +35 -0
  85. package/apps/storefront/src/config/devTenantSeed.ts +9 -9
  86. package/apps/storefront/src/config/resolver.ts +4 -4
  87. package/apps/storefront/src/layouts/Layout.astro +146 -21
  88. package/apps/storefront/src/lib/activity/alerts.ts +17 -19
  89. package/apps/storefront/src/lib/activity/candidateSubmitLease.ts +87 -0
  90. package/apps/storefront/src/lib/activity/changeActorAttribution.ts +89 -9
  91. package/apps/storefront/src/lib/activity/deployVersion.ts +20 -27
  92. package/apps/storefront/src/lib/activity/ingest.ts +8 -8
  93. package/apps/storefront/src/lib/activity/ingestAuth.ts +1 -1
  94. package/apps/storefront/src/lib/activity/killSwitch.ts +6 -6
  95. package/apps/storefront/src/lib/activity/query.ts +9 -9
  96. package/apps/storefront/src/lib/activity/recordActivity.ts +11 -15
  97. package/apps/storefront/src/lib/activity/store.ts +4 -6
  98. package/apps/storefront/src/lib/activity/uiActor.ts +12 -12
  99. package/apps/storefront/src/lib/activity/workerCommit.ts +6 -6
  100. package/apps/storefront/src/lib/admin/adminShell.ts +292 -0
  101. package/apps/storefront/src/lib/admin/ai/approvalMode.ts +2 -3
  102. package/apps/storefront/src/lib/admin/ai/audit.ts +3 -5
  103. package/apps/storefront/src/lib/admin/checkoutStyleInput.ts +7 -7
  104. package/apps/storefront/src/lib/admin/checkoutStyleTarget.ts +3 -3
  105. package/apps/storefront/src/lib/admin/envelope.ts +1 -2
  106. package/apps/storefront/src/lib/admin/orderClient.ts +3 -5
  107. package/apps/storefront/src/lib/admin/services/orders.ts +17 -19
  108. package/apps/storefront/src/lib/admin/viewmodels/aiWorkflows.ts +4 -6
  109. package/apps/storefront/src/lib/admin/viewmodels/settings.ts +1 -1
  110. package/apps/storefront/src/lib/apps/adminService.ts +14 -4
  111. package/apps/storefront/src/lib/apps/adminSession.ts +54 -45
  112. package/apps/storefront/src/lib/apps/apiAuth.ts +2 -2
  113. package/apps/storefront/src/lib/apps/apiRoute.ts +2 -2
  114. package/apps/storefront/src/lib/apps/appVersionStore.ts +281 -0
  115. package/apps/storefront/src/lib/apps/catalogMapper.ts +1 -1
  116. package/apps/storefront/src/lib/apps/consentConfigStore.ts +228 -0
  117. package/apps/storefront/src/lib/apps/credentials.ts +2 -2
  118. package/apps/storefront/src/lib/apps/gatewayKeys.ts +4 -4
  119. package/apps/storefront/src/lib/apps/gtmMigrationInstall.ts +447 -0
  120. package/apps/storefront/src/lib/apps/gtmMigrationReport.ts +367 -0
  121. package/apps/storefront/src/lib/apps/gtmMigrationTab.ts +315 -0
  122. package/apps/storefront/src/lib/apps/healthAggregate.ts +5 -5
  123. package/apps/storefront/src/lib/apps/integrationLifecycle.ts +614 -0
  124. package/apps/storefront/src/lib/apps/integrationLifecycleController.ts +125 -0
  125. package/apps/storefront/src/lib/apps/integrationReadModel.ts +563 -0
  126. package/apps/storefront/src/lib/apps/integrationsTab.ts +547 -0
  127. package/apps/storefront/src/lib/apps/integrationsView.ts +106 -0
  128. package/apps/storefront/src/lib/apps/managedAnalyticsService.ts +599 -0
  129. package/apps/storefront/src/lib/apps/orders/attributionService.ts +2 -2
  130. package/apps/storefront/src/lib/apps/orders/customerHash.ts +1 -1
  131. package/apps/storefront/src/lib/apps/orders/foxyOrderClient.ts +12 -12
  132. package/apps/storefront/src/lib/apps/orders/idempotency.ts +7 -7
  133. package/apps/storefront/src/lib/apps/orders/orderConversionD1.ts +150 -0
  134. package/apps/storefront/src/lib/apps/orders/orderForwardReceiver.ts +9 -1
  135. package/apps/storefront/src/lib/apps/orders/orderMapper.ts +2 -2
  136. package/apps/storefront/src/lib/apps/orders/ordersStore.ts +7 -7
  137. package/apps/storefront/src/lib/apps/orders/verifiedProviderMeasurement.ts +117 -0
  138. package/apps/storefront/src/lib/apps/registryService.ts +270 -16
  139. package/apps/storefront/src/lib/apps/scopes.ts +2 -2
  140. package/apps/storefront/src/lib/apps/tenantIntegrationMaterialization.ts +170 -0
  141. package/apps/storefront/src/lib/apps/tokenIssuer.ts +4 -4
  142. package/apps/storefront/src/lib/apps/tokenVerifier.ts +6 -6
  143. package/apps/storefront/src/lib/apps/v84CleanCloneReplayContext.ts +16 -0
  144. package/apps/storefront/src/lib/apps/v84CloneLifecycleCacheEvidence.ts +105 -0
  145. package/apps/storefront/src/lib/apps/v84CloneLifecycleEvidence.ts +177 -0
  146. package/apps/storefront/src/lib/apps/v84CloneLifecycleMaterialization.ts +251 -0
  147. package/apps/storefront/src/lib/apps/v84CloneLifecycleSemantics.ts +241 -0
  148. package/apps/storefront/src/lib/apps/v84CloneMigrationEvidence.ts +211 -0
  149. package/apps/storefront/src/lib/apps/v84CloneNativeInstallEvidence.ts +409 -0
  150. package/apps/storefront/src/lib/apps/v84NonportableCodeCoaching.ts +368 -0
  151. package/apps/storefront/src/lib/apps/v84NonportableRouteContract.ts +188 -0
  152. package/apps/storefront/src/lib/apps/v84NonportableRouteDogfood.ts +291 -0
  153. package/apps/storefront/src/lib/apps/widgets/eligibility.ts +2 -2
  154. package/apps/storefront/src/lib/apps/widgets/frameProps.ts +3 -3
  155. package/apps/storefront/src/lib/apps/widgets/launchToken.ts +4 -4
  156. package/apps/storefront/src/lib/apps/widgets/placements.ts +2 -2
  157. package/apps/storefront/src/lib/apps/widgets/renderSlot.ts +1 -1
  158. package/apps/storefront/src/lib/apps/ws1ManagedAnalyticsClient.ts +95 -0
  159. package/apps/storefront/src/lib/auth/adminApiGuard.ts +66 -0
  160. package/apps/storefront/src/lib/auth/adminEntry.ts +11 -11
  161. package/apps/storefront/src/lib/auth/adminLanding.ts +88 -0
  162. package/apps/storefront/src/lib/auth/identityToken.ts +6 -14
  163. package/apps/storefront/src/lib/auth/loginGate.ts +75 -13
  164. package/apps/storefront/src/lib/auth/mcpClientAssertion.ts +2 -4
  165. package/apps/storefront/src/lib/auth/operatorSession.ts +113 -0
  166. package/apps/storefront/src/lib/auth/session.ts +7 -8
  167. package/apps/storefront/src/lib/auth/totAccessClient.ts +8 -18
  168. package/apps/storefront/src/lib/basePath.ts +9 -2
  169. package/apps/storefront/src/lib/blog/access.ts +29 -0
  170. package/apps/storefront/src/lib/blog/collection.ts +400 -0
  171. package/apps/storefront/src/lib/blog/markdown.ts +174 -0
  172. package/apps/storefront/src/lib/blog/provider.ts +62 -3
  173. package/apps/storefront/src/lib/blog/reactions.ts +87 -0
  174. package/apps/storefront/src/lib/blog/rss.ts +92 -0
  175. package/apps/storefront/src/lib/blog/teaser.ts +83 -0
  176. package/apps/storefront/src/lib/blog/types.ts +34 -0
  177. package/apps/storefront/src/lib/blog/viewCounts.ts +243 -0
  178. package/apps/storefront/src/lib/chrome/examples/tokenoftrust.chrome-assignments.example.json +6 -0
  179. package/apps/storefront/src/lib/chrome/examples/tokenoftrust.chrome.example.json +79 -0
  180. package/apps/storefront/src/lib/chrome/model.ts +48 -253
  181. package/apps/storefront/src/lib/chrome/navCurrent.ts +29 -0
  182. package/apps/storefront/src/lib/chrome/parseConfig.ts +18 -32
  183. package/apps/storefront/src/lib/chrome/variant.ts +39 -0
  184. package/apps/storefront/src/lib/commerce/checkoutHandoffCorrelation.ts +105 -0
  185. package/apps/storefront/src/lib/compliance/tamper-sentinel-boot.ts +152 -0
  186. package/apps/storefront/src/lib/compliance/tamper-sentinel.ts +327 -0
  187. package/apps/storefront/src/lib/content/index.ts +5 -5
  188. package/apps/storefront/src/lib/content-edit/applyDiff.ts +1 -1
  189. package/apps/storefront/src/lib/content-edit/route.ts +1 -1
  190. package/apps/storefront/src/lib/d1/catalog.ts +14 -2
  191. package/apps/storefront/src/lib/d1/schema-apps.ts +372 -17
  192. package/apps/storefront/src/lib/dev/activityIngestToken.ts +2 -4
  193. package/apps/storefront/src/lib/dev/activityStore.ts +3 -5
  194. package/apps/storefront/src/lib/dev/apiBase.ts +1 -4
  195. package/apps/storefront/src/lib/dev/cockpit/ask.ts +49 -0
  196. package/apps/storefront/src/lib/dev/cockpit/clickToSource.ts +71 -0
  197. package/apps/storefront/src/lib/dev/cockpit/copyCommand.ts +227 -0
  198. package/apps/storefront/src/lib/dev/cockpit/files.ts +249 -0
  199. package/apps/storefront/src/lib/dev/cockpit/hostedActivity.ts +500 -0
  200. package/apps/storefront/src/lib/dev/cockpit/infoPopover.ts +12 -0
  201. package/apps/storefront/src/lib/dev/cockpit/mcpCopy.ts +34 -0
  202. package/apps/storefront/src/lib/dev/cockpit/monitor.ts +107 -0
  203. package/apps/storefront/src/lib/dev/cockpit/stepper.ts +116 -0
  204. package/apps/storefront/src/lib/dev/cockpit/themeToggle.ts +23 -0
  205. package/apps/storefront/src/lib/dev/cockpitStore.ts +1 -9
  206. package/apps/storefront/src/lib/dev/devDraft.ts +1 -1
  207. package/apps/storefront/src/lib/dev/previewStatus.ts +9 -21
  208. package/apps/storefront/src/lib/dev/rendezvousBroker.ts +9 -24
  209. package/apps/storefront/src/lib/dev/runtimeStore.ts +1 -1
  210. package/apps/storefront/src/lib/edgeCache.ts +34 -5
  211. package/apps/storefront/src/lib/email/candidateConflictEmail.ts +80 -0
  212. package/apps/storefront/src/lib/http/fetchTransport.ts +63 -0
  213. package/apps/storefront/src/lib/i18n.ts +25 -0
  214. package/apps/storefront/src/lib/integration/browserEnvironment.ts +137 -0
  215. package/apps/storefront/src/lib/integration/managedInstallDeclarations.ts +72 -0
  216. package/apps/storefront/src/lib/integration/nonceStamp.ts +49 -0
  217. package/apps/storefront/src/lib/integration/tenantDeclarations.ts +116 -0
  218. package/apps/storefront/src/lib/jsonld.ts +118 -0
  219. package/apps/storefront/src/lib/membership/eligibility.ts +1 -1
  220. package/apps/storefront/src/lib/membership/model.ts +1 -2
  221. package/apps/storefront/src/lib/monitoring/manifest.ts +11 -11
  222. package/apps/storefront/src/lib/newsletter/confirmationEmail.ts +30 -0
  223. package/apps/storefront/src/lib/newsletter/consent.ts +23 -0
  224. package/apps/storefront/src/lib/newsletter/digest.ts +117 -0
  225. package/apps/storefront/src/lib/newsletter/resendClient.ts +79 -0
  226. package/apps/storefront/src/lib/newsletter/send.ts +265 -0
  227. package/apps/storefront/src/lib/newsletter/subscribers.ts +156 -0
  228. package/apps/storefront/src/lib/previewReload.ts +57 -1
  229. package/apps/storefront/src/lib/publish/adminPublishTab.ts +4625 -0
  230. package/apps/storefront/src/lib/publish/apex-readiness.ts +41 -18
  231. package/apps/storefront/src/lib/publish/buildStream.ts +269 -0
  232. package/apps/storefront/src/lib/publish/collapsePersistence.ts +72 -0
  233. package/apps/storefront/src/lib/publish/dispatchHealth.ts +5 -7
  234. package/apps/storefront/src/lib/publish/domainState.ts +22 -13
  235. package/apps/storefront/src/lib/publish/lifecycleState.ts +369 -0
  236. package/apps/storefront/src/lib/publish/liveReleaseSummary.ts +29 -0
  237. package/apps/storefront/src/lib/publish/mergeDoctorOnFailure.ts +78 -0
  238. package/apps/storefront/src/lib/publish/mergeHealthBanner.ts +62 -0
  239. package/apps/storefront/src/lib/publish/mergeRowFindings.ts +124 -0
  240. package/apps/storefront/src/lib/publish/mergeSupportEscalation.ts +75 -0
  241. package/apps/storefront/src/lib/publish/panelPolling.ts +78 -0
  242. package/apps/storefront/src/lib/publish/publish-guard.ts +88 -0
  243. package/apps/storefront/src/lib/publish/publishFormat.ts +165 -0
  244. package/apps/storefront/src/lib/publish/publishHero.ts +177 -0
  245. package/apps/storefront/src/lib/publish/reviewRelease/changeState.ts +66 -0
  246. package/apps/storefront/src/lib/publish/reviewRelease/copy.ts +141 -0
  247. package/apps/storefront/src/lib/publish/reviewRelease/index.ts +41 -0
  248. package/apps/storefront/src/lib/publish/reviewRelease/permissions.ts +47 -0
  249. package/apps/storefront/src/lib/publish/reviewRelease/releaseState.ts +42 -0
  250. package/apps/storefront/src/lib/publish/reviewRelease/types.ts +59 -0
  251. package/apps/storefront/src/lib/publish/reviewRelease/viewModel.ts +440 -0
  252. package/apps/storefront/src/lib/publish/reviewReleaseWorkspace/actions.ts +161 -0
  253. package/apps/storefront/src/lib/publish/reviewReleaseWorkspace/controller.ts +325 -0
  254. package/apps/storefront/src/lib/publish/reviewReleaseWorkspace/render.ts +241 -0
  255. package/apps/storefront/src/lib/publish/shipWorkspace.ts +121 -51
  256. package/apps/storefront/src/lib/publish/sseRoute.ts +119 -0
  257. package/apps/storefront/src/lib/publish/stageBundleUpload.ts +215 -0
  258. package/apps/storefront/src/lib/publish/streamLogConsole.ts +142 -0
  259. package/apps/storefront/src/lib/publish/versionNumber.ts +52 -0
  260. package/apps/storefront/src/lib/rate/kvWindow.ts +53 -0
  261. package/apps/storefront/src/lib/rawChrome.ts +125 -47
  262. package/apps/storefront/src/lib/rawMarketingHtml.ts +74 -3
  263. package/apps/storefront/src/lib/search/index.ts +2 -2
  264. package/apps/storefront/src/lib/seo/alternates.ts +42 -0
  265. package/apps/storefront/src/lib/seo/meta.ts +65 -0
  266. package/apps/storefront/src/lib/social/golive.ts +212 -0
  267. package/apps/storefront/src/lib/social/notify.ts +32 -0
  268. package/apps/storefront/src/lib/social/tokenStore.ts +171 -0
  269. package/apps/storefront/src/lib/storyblok/content-model.ts +7 -33
  270. package/apps/storefront/src/lib/storyblok/provider.ts +168 -60
  271. package/apps/storefront/src/lib/subscription/model.ts +4 -4
  272. package/apps/storefront/src/lib/the-build/provider.ts +32 -0
  273. package/apps/storefront/src/lib/the-build/readingTime.ts +35 -0
  274. package/apps/storefront/src/lib/the-build/toc.ts +95 -0
  275. package/apps/storefront/src/lib/tot/ToTClient.ts +1 -1
  276. package/apps/storefront/src/lib/tot/d1Client.ts +2 -2
  277. package/apps/storefront/src/lib/tot/totClientInterface.ts +3 -4
  278. package/apps/storefront/src/lib/webhooks/deliveryStore.ts +16 -17
  279. package/apps/storefront/src/middleware/index.ts +385 -52
  280. package/apps/storefront/src/pages/[...slug].astro +9 -4
  281. package/apps/storefront/src/pages/admin/review-release.astro +265 -0
  282. package/apps/storefront/src/pages/admin/select.astro +96 -0
  283. package/apps/storefront/src/pages/admin.astro +25 -1313
  284. package/apps/storefront/src/pages/api/activity.ts +8 -8
  285. package/apps/storefront/src/pages/api/admin/activity-alerts.ts +5 -20
  286. package/apps/storefront/src/pages/api/admin/orders/[id].ts +7 -2
  287. package/apps/storefront/src/pages/api/admin/orders.ts +8 -2
  288. package/apps/storefront/src/pages/api/apps/admin/gtm-migration-install.ts +174 -0
  289. package/apps/storefront/src/pages/api/apps/admin/gtm-migration.ts +96 -0
  290. package/apps/storefront/src/pages/api/apps/admin/integration-lifecycle.ts +118 -0
  291. package/apps/storefront/src/pages/api/apps/admin/integrations.ts +89 -0
  292. package/apps/storefront/src/pages/api/apps/admin/platform-adapters.ts +85 -0
  293. package/apps/storefront/src/pages/api/apps/internal/order-forward.ts +74 -2
  294. package/apps/storefront/src/pages/api/blog/[collection]/[slug]/react.ts +104 -0
  295. package/apps/storefront/src/pages/api/commerce/checkout-handoff.ts +77 -0
  296. package/apps/storefront/src/pages/api/compliance/tamper.ts +88 -0
  297. package/apps/storefront/src/pages/api/integrations/v1/[operation].ts +108 -0
  298. package/apps/storefront/src/pages/api/newsletter/[collection]/subscribe.ts +146 -0
  299. package/apps/storefront/src/pages/api/newsletter/confirm.ts +75 -0
  300. package/apps/storefront/src/pages/api/newsletter/unsubscribe.ts +90 -0
  301. package/apps/storefront/src/pages/api/publish/stage-bundle.ts +70 -0
  302. package/apps/storefront/src/pages/api/rum/vitals.ts +20 -2
  303. package/apps/storefront/src/pages/blog/[slug].astro +99 -19
  304. package/apps/storefront/src/pages/blog/author/[author].astro +140 -0
  305. package/apps/storefront/src/pages/blog/category/[category].astro +67 -0
  306. package/apps/storefront/src/pages/blog/index.astro +4 -2
  307. package/apps/storefront/src/pages/blog/tag/[tag].astro +65 -0
  308. package/apps/storefront/src/pages/cockpit.astro +77 -1353
  309. package/apps/storefront/src/pages/dashboard/[appDomain]/team.astro +15 -11
  310. package/apps/storefront/src/pages/index.astro +10 -5
  311. package/apps/storefront/src/pages/llms.txt.ts +1 -1
  312. package/apps/storefront/src/pages/products/[handle].astro +7 -6
  313. package/apps/storefront/src/pages/search.astro +1 -1
  314. package/apps/storefront/src/pages/sitemap.xml.ts +21 -1
  315. package/apps/storefront/src/pages/style-guide/[tenant]/[theme].astro +31 -27
  316. package/apps/storefront/src/pages/style-guide/[tenant]/chrome/[theme].astro +1 -1
  317. package/apps/storefront/src/pages/tenants/[id]/[...path].ts +38 -2
  318. package/apps/storefront/src/pages/the-build/[slug].astro +129 -0
  319. package/apps/storefront/src/pages/the-build/index.astro +146 -0
  320. package/apps/storefront/src/pages/the-build/rss.xml.ts +33 -0
  321. package/apps/storefront/src/styles/admin.css +1106 -0
  322. package/apps/storefront/tsconfig.json +6 -1
  323. package/package.json +1 -1
  324. package/packages/public-runtime/package.json +3 -1
  325. package/packages/public-runtime/schemas/managed-analytics/v1/adapter-definition.schema.json +427 -0
  326. package/packages/public-runtime/schemas/managed-analytics/v1/create-integration-install-request.schema.json +161 -0
  327. package/packages/public-runtime/schemas/managed-analytics/v1/create-migration-draft-request.schema.json +157 -0
  328. package/packages/public-runtime/schemas/managed-analytics/v1/delete-integration-install-request.schema.json +84 -0
  329. package/packages/public-runtime/schemas/managed-analytics/v1/delete-migration-draft-request.schema.json +84 -0
  330. package/packages/public-runtime/schemas/managed-analytics/v1/error-envelope.schema.json +180 -0
  331. package/packages/public-runtime/schemas/managed-analytics/v1/get-adapter-definition-request.schema.json +52 -0
  332. package/packages/public-runtime/schemas/managed-analytics/v1/get-integration-install-request.schema.json +52 -0
  333. package/packages/public-runtime/schemas/managed-analytics/v1/get-integration-revision-request.schema.json +79 -0
  334. package/packages/public-runtime/schemas/managed-analytics/v1/get-migration-draft-request.schema.json +52 -0
  335. package/packages/public-runtime/schemas/managed-analytics/v1/integration-install.schema.json +167 -0
  336. package/packages/public-runtime/schemas/managed-analytics/v1/integration-revision.schema.json +208 -0
  337. package/packages/public-runtime/schemas/managed-analytics/v1/list-adapter-definitions-request.schema.json +53 -0
  338. package/packages/public-runtime/schemas/managed-analytics/v1/list-integration-installs-request.schema.json +53 -0
  339. package/packages/public-runtime/schemas/managed-analytics/v1/list-integration-revisions-request.schema.json +82 -0
  340. package/packages/public-runtime/schemas/managed-analytics/v1/migration-draft.schema.json +218 -0
  341. package/packages/public-runtime/schemas/managed-analytics/v1/mutation-request.schema.json +52 -0
  342. package/packages/public-runtime/schemas/managed-analytics/v1/mutation-success-envelope.schema.json +49 -0
  343. package/packages/public-runtime/schemas/managed-analytics/v1/read-request.schema.json +20 -0
  344. package/packages/public-runtime/schemas/managed-analytics/v1/read-success-envelope.schema.json +45 -0
  345. package/packages/public-runtime/schemas/managed-analytics/v1/revise-integration-install-request.schema.json +156 -0
  346. package/packages/public-runtime/schemas/managed-analytics/v1/set-integration-install-status-request.schema.json +92 -0
  347. package/packages/public-runtime/schemas/managed-analytics/v1/update-migration-draft-request.schema.json +184 -0
  348. package/packages/public-runtime/scripts/generate-managed-analytics-api-v1-schemas.mjs +13 -0
  349. package/packages/public-runtime/src/activity/catalog.ts +89 -33
  350. package/packages/public-runtime/src/activity/event.ts +6 -6
  351. package/packages/public-runtime/src/activity/index.ts +2 -2
  352. package/packages/public-runtime/src/activity/redaction.ts +4 -4
  353. package/packages/public-runtime/src/candidate-index.ts +42 -8
  354. package/packages/public-runtime/src/catalog-d1.ts +1 -1
  355. package/packages/public-runtime/src/checkout-orders.ts +2 -2
  356. package/packages/public-runtime/src/checkout-style-publish.ts +2 -2
  357. package/packages/public-runtime/src/checkout-style.ts +7 -7
  358. package/packages/public-runtime/src/checkout.ts +3 -3
  359. package/packages/public-runtime/src/chrome.ts +777 -0
  360. package/packages/public-runtime/src/compliance-obligations.ts +310 -0
  361. package/packages/public-runtime/src/csp.ts +99 -0
  362. package/packages/public-runtime/src/customization-preview.ts +25 -2
  363. package/packages/public-runtime/src/customization-runtime.ts +156 -3
  364. package/packages/public-runtime/src/customization-versioning.ts +10 -0
  365. package/packages/public-runtime/src/destination-webhook/contract.ts +353 -0
  366. package/packages/public-runtime/src/embed-catalog.d.mts +9 -0
  367. package/packages/public-runtime/src/embed-catalog.mjs +40 -0
  368. package/packages/public-runtime/src/evidence.ts +288 -0
  369. package/packages/public-runtime/src/gtm-migration/bounds.ts +138 -0
  370. package/packages/public-runtime/src/gtm-migration/disposition-rules.ts +361 -0
  371. package/packages/public-runtime/src/gtm-migration/dispositions.ts +181 -0
  372. package/packages/public-runtime/src/gtm-migration/draft-generator.ts +400 -0
  373. package/packages/public-runtime/src/gtm-migration/graph.ts +468 -0
  374. package/packages/public-runtime/src/gtm-migration/index.ts +95 -0
  375. package/packages/public-runtime/src/gtm-migration/parse.ts +410 -0
  376. package/packages/public-runtime/src/gtm-migration/references.ts +266 -0
  377. package/packages/public-runtime/src/gtm-migration/types.ts +86 -0
  378. package/packages/public-runtime/src/index.ts +17 -1
  379. package/packages/public-runtime/src/integration/assurance-decision.ts +259 -0
  380. package/packages/public-runtime/src/integration/assurance.ts +228 -0
  381. package/packages/public-runtime/src/integration/candidate-evidence.ts +377 -0
  382. package/packages/public-runtime/src/integration/commerce-broker.ts +497 -0
  383. package/packages/public-runtime/src/integration/consent-broker.ts +337 -0
  384. package/packages/public-runtime/src/integration/consent-cmp-demand.ts +115 -0
  385. package/packages/public-runtime/src/integration/consent-config.ts +260 -0
  386. package/packages/public-runtime/src/integration/consent-preview.ts +125 -0
  387. package/packages/public-runtime/src/integration/correlation-token.ts +444 -0
  388. package/packages/public-runtime/src/integration/csp-compiler.ts +267 -0
  389. package/packages/public-runtime/src/integration/csp.ts +105 -0
  390. package/packages/public-runtime/src/integration/digest.ts +43 -0
  391. package/packages/public-runtime/src/integration/environment.ts +228 -0
  392. package/packages/public-runtime/src/integration/events-catalog.ts +186 -0
  393. package/packages/public-runtime/src/integration/events.ts +701 -0
  394. package/packages/public-runtime/src/integration/g2-action-boundary.ts +106 -0
  395. package/packages/public-runtime/src/integration/index.ts +90 -0
  396. package/packages/public-runtime/src/integration/interim-bridge.ts +138 -0
  397. package/packages/public-runtime/src/integration/lifecycle-boot.ts +317 -0
  398. package/packages/public-runtime/src/integration/lifecycle-runtime.ts +393 -0
  399. package/packages/public-runtime/src/integration/lifecycle.ts +94 -0
  400. package/packages/public-runtime/src/integration/managed-analytics-api-v1-safety.d.mts +2 -0
  401. package/packages/public-runtime/src/integration/managed-analytics-api-v1-safety.mjs +10 -0
  402. package/packages/public-runtime/src/integration/managed-analytics-api-v1.schemas.ts +382 -0
  403. package/packages/public-runtime/src/integration/managed-analytics-api-v1.ts +684 -0
  404. package/packages/public-runtime/src/integration/managed-analytics-bridge.ts +383 -0
  405. package/packages/public-runtime/src/integration/managed-analytics-value-safety.ts +14 -0
  406. package/packages/public-runtime/src/integration/managed-analytics.ts +277 -0
  407. package/packages/public-runtime/src/integration/manifest.ts +470 -0
  408. package/packages/public-runtime/src/integration/native-amplitude.ts +362 -0
  409. package/packages/public-runtime/src/integration/native-ga4.ts +352 -0
  410. package/packages/public-runtime/src/integration/observability.ts +451 -0
  411. package/packages/public-runtime/src/integration/order-conversion.ts +0 -0
  412. package/packages/public-runtime/src/integration/phases.ts +182 -0
  413. package/packages/public-runtime/src/integration/plan.ts +112 -0
  414. package/packages/public-runtime/src/integration/platform-adapter-runtime.ts +146 -0
  415. package/packages/public-runtime/src/integration/platform-adapters.ts +671 -0
  416. package/packages/public-runtime/src/integration/promotion-gate.ts +451 -0
  417. package/packages/public-runtime/src/integration/provider-checkout-extension.ts +425 -0
  418. package/packages/public-runtime/src/integration/receipt-measurement.ts +370 -0
  419. package/packages/public-runtime/src/integration/regulated-parent-cert.ts +311 -0
  420. package/packages/public-runtime/src/integration/render-phases.ts +267 -0
  421. package/packages/public-runtime/src/integration/resolve.ts +465 -0
  422. package/packages/public-runtime/src/integration/result.ts +239 -0
  423. package/packages/public-runtime/src/integration/secrets.ts +92 -0
  424. package/packages/public-runtime/src/integration/tamper-evidence.ts +205 -0
  425. package/packages/public-runtime/src/integration/tamper-recovery.ts +127 -0
  426. package/packages/public-runtime/src/integration/version.ts +92 -0
  427. package/packages/public-runtime/src/integration-capabilities.ts +520 -0
  428. package/packages/public-runtime/src/membership.ts +2 -2
  429. package/packages/public-runtime/src/raw-html-policy.ts +2 -4
  430. package/packages/public-runtime/src/regulated-commerce/broker/consent-dedupe.ts +144 -0
  431. package/packages/public-runtime/src/regulated-commerce/correlation/contract.ts +177 -0
  432. package/packages/public-runtime/src/regulated-commerce/diagnostics/local-dispatch.ts +24 -0
  433. package/packages/public-runtime/src/regulated-commerce/diagnostics/read-model.ts +69 -0
  434. package/packages/public-runtime/src/regulated-commerce/dispatch/core.ts +134 -0
  435. package/packages/public-runtime/src/regulated-commerce/events/contract.ts +113 -0
  436. package/packages/public-runtime/src/regulated-commerce/ledger/core.ts +81 -0
  437. package/packages/public-runtime/src/regulated-commerce/mappings/ga4-ads.ts +87 -0
  438. package/packages/public-runtime/src/regulated-commerce/provider-order/bridge.ts +52 -0
  439. package/packages/public-runtime/src/regulated-commerce/purchase-truth/contract.ts +252 -0
  440. package/packages/public-runtime/src/regulated-commerce/sources/accepted-browser-operation.ts +67 -0
  441. package/packages/public-runtime/src/regulated-commerce/testing/fake-destination.ts +93 -0
  442. package/packages/public-runtime/src/static-bundle.ts +357 -0
  443. package/packages/public-runtime/src/tenant-assets.ts +143 -4
  444. package/packages/public-runtime/src/tenant.ts +81 -17
  445. package/packages/public-runtime/tests/fixtures/integration/browser-environment.example.json +8 -0
  446. package/packages/public-runtime/tests/fixtures/integration/commerce-event.example.json +44 -0
  447. package/packages/public-runtime/tests/fixtures/integration/commerce-order-event.example.json +44 -0
  448. package/packages/public-runtime/tests/fixtures/integration/compliance-assurance-record.example.json +50 -0
  449. package/packages/public-runtime/tests/fixtures/integration/integration-manifest-v2.example.json +120 -0
  450. package/packages/public-runtime/tests/fixtures/integration/managed-analytics-v1/fake-adapter-catalog.json +35 -0
  451. package/packages/public-runtime/tests/fixtures/integration/tenant-action-registry.example.json +11 -0
  452. package/packages/public-runtime/tests/fixtures/integration/tenant-local-example/README.md +22 -0
  453. package/packages/public-runtime/tests/fixtures/integration/tenant-local-example/entry.js +30 -0
  454. package/packages/public-runtime/tests/fixtures/integration/tenant-local-example/manifest.json +75 -0
  455. package/packages/public-runtime/tests/fixtures/integration/tenant-local-example/public-config.json +3 -0
  456. package/packages/public-runtime/tests/fixtures/regulated-commerce/fake-destinations.ts +38 -0
  457. package/packages/public-runtime/tests/gtm-migration/fixtures/gtm-sample-container-v84.provenance.json +29 -0
  458. package/packages/public-runtime/tests/gtm-migration/fixtures/gtm-sample-container-v84.redacted.json +3397 -0
  459. package/scripts/build/copy-tenant-assets.mjs +14 -4
  460. package/scripts/dev/ai-edit.mjs +1 -1
  461. package/scripts/dev/checkout-watch.mjs +2 -2
  462. package/scripts/dev/file-browser.mjs +4 -6
  463. package/scripts/dev/file-writer.mjs +3 -3
  464. package/scripts/dev/git-status.mjs +2 -2
  465. package/scripts/dev/integration-guardrails.mjs +498 -0
  466. package/scripts/dev/locate-handler.mjs +3 -3
  467. package/scripts/dev/port-check.mjs +3 -3
  468. package/scripts/dev/publish.mjs +5 -9
  469. package/scripts/dev/safe-path.mjs +2 -2
  470. package/scripts/dev/shot.mjs +1 -1
  471. package/scripts/dev/tenant-source-root.mjs +1 -1
  472. package/scripts/dev/unified-diff.mjs +8 -3
  473. package/scripts/tot-dev.mjs +8 -11
  474. package/apps/storefront/src/lib/dev/vcBinding.ts +0 -80
@@ -6,7 +6,7 @@ import { withBase } from "@/lib/basePath";
6
6
  const { envelope } = Astro.props;
7
7
  const settings = getSettingsViewModel(envelope);
8
8
 
9
- // Checkout styling editor (U12). Renders the current CheckoutStyle and saves it
9
+ // Checkout styling editor. Renders the current CheckoutStyle and saves it
10
10
  // through the versioned publish spine (POST /api/preview/checkout-style —
11
11
  // colocated with reconcile/promote in the control-plane publish cluster). The
12
12
  // endpoint is base-path-prefixed so it stays inside a path-routed tenant, and
@@ -607,7 +607,7 @@ function formatChangeSummary(change: string): string {
607
607
  }
608
608
  }
609
609
 
610
- /* Checkout styling editor (U12) */
610
+ /* Checkout styling editor */
611
611
  .checkout-style-editor {
612
612
  display: grid;
613
613
  gap: 14px;
@@ -4,6 +4,7 @@
4
4
  * truth; lifecycle actions require a later subscription API and audit gates.
5
5
  */
6
6
  import { getSubscriptionsViewModel } from "@/lib/admin/services/mock";
7
+ import { formatDate } from "@/lib/format";
7
8
  import type { AdminTenantEnvelope } from "@/lib/admin/envelope";
8
9
  import type {
9
10
  SubscriptionAttentionRow,
@@ -64,9 +65,6 @@ function formatMoney(value: number): string {
64
65
  return `$${value.toFixed(2)}`;
65
66
  }
66
67
 
67
- function formatDate(iso: string): string {
68
- return new Date(iso).toLocaleDateString([], { month: "short", day: "numeric", year: "numeric" });
69
- }
70
68
  ---
71
69
 
72
70
  <section
@@ -38,13 +38,13 @@ interface Props {
38
38
  const { envelope } = Astro.props;
39
39
  const cspNonce = Astro.locals.cspNonce;
40
40
 
41
- // Real orders (U3): fetch this tenant's checkout orders via U2's client
41
+ // Real orders: fetch this tenant's checkout orders via the checkout order client
42
42
  // and map to the view model. The per-tenant bearer is a server-side Worker secret;
43
43
  // absent (or any upstream error) → an empty-but-valid model so the tab still
44
- // renders (rich empty/error UX is U5). Live data is gated on the ToT-core orders
45
- // endpoint (see U1/U2) — until then this resolves empty on qa.
44
+ // renders. Live data is gated on the ToT-core orders
45
+ // endpoint — until then this resolves empty on qa.
46
46
  const orderBearer = await readEnv("FOXY_ORDER_API_KEY");
47
- // Server-backed filters + pagination (U5): the queue filter + cursor come from
47
+ // Server-backed filters + pagination: the queue filter + cursor come from
48
48
  // the URL and are applied by the order API server-side; payment/search remain
49
49
  // client-side refinements on the returned page.
50
50
  const orderFilters = parseOrderFilters(Astro.url.searchParams);
@@ -444,7 +444,7 @@ const holdCount = vm.rows.filter((r) => r.shipState === "hold_review_required").
444
444
  const setLoading = () => root.setAttribute("data-orders-loading", "");
445
445
 
446
446
  // Queue filter + Refresh + pagination are SERVER-backed: they navigate so the
447
- // order API re-applies the filter/cursor and returns a fresh page (U5). Any
447
+ // order API re-applies the filter/cursor and returns a fresh page. Any
448
448
  // filter change resets pagination to the first page (drops the cursor).
449
449
  const navTo = (mutate) => {
450
450
  const url = new URL(window.location.href);
@@ -0,0 +1,87 @@
1
+ ---
2
+ // Paywall card shown under the teaser of a gated post the viewer can't read in
3
+ // full. Mirrors the "Coming soon" host gate (`src/lib/auth/gatePage.ts`):
4
+ // badge + headline + one line of value + a single CTA — restyled with the site's
5
+ // design tokens so it reads as native article chrome, and preceded by a fade that
6
+ // dissolves the truncated body into the card.
7
+ import type { BlogAccess } from "@/lib/blog/types.js";
8
+
9
+ interface Props {
10
+ access: BlogAccess;
11
+ next: string;
12
+ signedIn: boolean;
13
+ loginHref: string;
14
+ }
15
+
16
+ const { next, signedIn, loginHref } = Astro.props;
17
+ ---
18
+
19
+ <div class="paywall-fade" aria-hidden="true"></div>
20
+ <aside class="paywall" data-next={next} role="note">
21
+ <span class="paywall-badge">Members only</span>
22
+ {signedIn ? (
23
+ <>
24
+ <h2>You're signed in</h2>
25
+ <p>Membership tiers for this story are coming soon.</p>
26
+ {/* Unreachable in v1 (authenticated ⇒ member) — coded per the B4.0 spike so
27
+ a future paid tier only has to point this CTA at a membership route. */}
28
+ <a class="paywall-cta" href={loginHref}>Become a member</a>
29
+ </>
30
+ ) : (
31
+ <>
32
+ <h2>This story is for members</h2>
33
+ <p>Sign in with Token of Trust to keep reading — it's free.</p>
34
+ <a class="paywall-cta" href={loginHref}>Sign in to keep reading</a>
35
+ </>
36
+ )}
37
+ </aside>
38
+
39
+ <style>
40
+ .paywall-fade {
41
+ height: 7rem;
42
+ margin-top: -7rem;
43
+ position: relative;
44
+ pointer-events: none;
45
+ background: linear-gradient(to bottom, transparent, var(--color-bg));
46
+ }
47
+ .paywall {
48
+ display: flex;
49
+ flex-direction: column;
50
+ align-items: center;
51
+ text-align: center;
52
+ gap: .9rem;
53
+ max-width: 34rem;
54
+ margin: 0 auto;
55
+ padding: clamp(1.75rem, 4vw, 2.5rem);
56
+ border: 1px solid var(--color-border);
57
+ border-radius: var(--radius-lg);
58
+ background: var(--color-surface);
59
+ box-shadow: var(--shadow-md);
60
+ }
61
+ .paywall-badge {
62
+ display: inline-block;
63
+ letter-spacing: .18em;
64
+ text-transform: uppercase;
65
+ font-size: .72rem;
66
+ font-weight: 700;
67
+ color: var(--color-muted);
68
+ border: 1px solid var(--color-border);
69
+ border-radius: var(--radius-full);
70
+ padding: .35rem .8rem;
71
+ }
72
+ .paywall h2 { margin: 0; font-size: clamp(1.5rem, 3vw, 2rem); line-height: 1.15; }
73
+ .paywall p { margin: 0; max-width: 28rem; color: var(--color-muted); font-size: 1.02rem; line-height: 1.6; }
74
+ .paywall-cta {
75
+ margin-top: .4rem;
76
+ display: inline-block;
77
+ text-decoration: none;
78
+ font-weight: 700;
79
+ font-size: 1rem;
80
+ color: var(--color-primary-contrast);
81
+ background: var(--color-primary);
82
+ border-radius: var(--radius-md);
83
+ padding: .8rem 1.6rem;
84
+ transition: background .15s ease;
85
+ }
86
+ .paywall-cta:hover { background: var(--color-primary-hover); }
87
+ </style>
@@ -0,0 +1,43 @@
1
+ ---
2
+ // Visual-only lock badge for a gated post on listing card grids. Reads
3
+ // `post.access` and renders nothing for public posts. No gating logic lives
4
+ // here — entitlement is decided in the route via `viewerHasBlogAccess`; this
5
+ // is purely a "this story is members-only" indicator on listings and archives.
6
+ import type { BlogAccess } from "@/lib/blog/types.js";
7
+
8
+ interface Props {
9
+ access?: BlogAccess;
10
+ }
11
+
12
+ const { access } = Astro.props;
13
+ const gated = (access ?? "public") !== "public";
14
+ ---
15
+
16
+ {gated && (
17
+ <p class="access-badge">
18
+ <svg class="lock" width="12" height="12" viewBox="0 0 24 24" aria-hidden="true" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
19
+ <rect x="4" y="11" width="16" height="9" rx="2"></rect>
20
+ <path d="M8 11V7a4 4 0 0 1 8 0v4"></path>
21
+ </svg>
22
+ Members
23
+ </p>
24
+ )}
25
+
26
+ <style>
27
+ .access-badge {
28
+ display: inline-flex;
29
+ gap: .3rem;
30
+ align-items: center;
31
+ align-self: flex-start;
32
+ margin: 0 0 .55rem;
33
+ padding: .2rem .55rem;
34
+ border: 1px solid color-mix(in srgb, var(--color-accent) 45%, var(--color-border));
35
+ border-radius: var(--radius-full);
36
+ background: color-mix(in srgb, var(--color-accent) 10%, var(--color-surface));
37
+ color: var(--color-accent-ink);
38
+ font: 700 .68rem/1 var(--font-mono);
39
+ letter-spacing: .06em;
40
+ text-transform: uppercase;
41
+ }
42
+ .lock { flex: 0 0 auto; }
43
+ </style>
@@ -0,0 +1,57 @@
1
+ ---
2
+ // Reader-facing card grid for a list of blog posts, shared by the collection's
3
+ // category and tag index pages. `base` is the collection route base (e.g.
4
+ // "/blog"); `basePath` is the tenant/preview path prefix from Astro.locals.
5
+ import type { BlogPostSummary } from "@/lib/blog/types.js";
6
+ import PostAccessBadge from "@/components/blog/PostAccessBadge.astro";
7
+
8
+ interface Props {
9
+ posts: BlogPostSummary[];
10
+ basePath: string;
11
+ base: string;
12
+ }
13
+
14
+ const { posts, basePath, base } = Astro.props;
15
+ const href = (path: string) => `${basePath}${path}` || "/";
16
+ ---
17
+
18
+ <section class="post-grid" aria-label="Posts">
19
+ {posts.map((post, index) => (
20
+ <article class="post-card">
21
+ <a href={href(`${base}/${post.slug}/`)} aria-label={`Read ${post.title}`}>
22
+ {post.featuredImage ? (
23
+ <img
24
+ src={post.featuredImage.src}
25
+ alt={post.featuredImage.alt}
26
+ width={post.featuredImage.width}
27
+ height={post.featuredImage.height}
28
+ loading={index < 3 ? "eager" : "lazy"}
29
+ decoding="async"
30
+ />
31
+ ) : <div class="image-placeholder" aria-hidden="true"></div>}
32
+ <div class="post-card-copy">
33
+ <p class="post-meta"><time datetime={post.publishedAt}>{new Date(post.publishedAt).toLocaleDateString("en-US", { year: "numeric", month: "short", day: "numeric" })}</time>{post.tags[0] && <> · {post.tags[0]}</>}</p>
34
+ <PostAccessBadge access={post.access} />
35
+ <h2>{post.title}</h2>
36
+ <p>{post.excerpt}</p>
37
+ <span>Read article <span aria-hidden="true">→</span></span>
38
+ </div>
39
+ </a>
40
+ </article>
41
+ ))}
42
+ </section>
43
+
44
+ <style>
45
+ .post-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 1.35rem; align-items: start; }
46
+ .post-card { height: 100%; overflow: hidden; border: 1px solid var(--color-border); border-radius: var(--radius-lg); background: var(--color-surface); box-shadow: var(--shadow-sm); transition: transform .2s ease, box-shadow .2s ease; }
47
+ .post-card:hover { transform: translateY(-4px); box-shadow: var(--shadow-md); }
48
+ .post-card a { display: flex; height: 100%; flex-direction: column; }
49
+ .post-card img, .image-placeholder { width: 100%; aspect-ratio: 16 / 9; object-fit: cover; background: linear-gradient(135deg, color-mix(in srgb, var(--color-primary) 12%, white), color-mix(in srgb, var(--color-accent) 24%, white)); }
50
+ .post-card-copy { display: flex; flex: 1; flex-direction: column; padding: 1.25rem; }
51
+ .post-meta { margin: 0 0 .7rem; color: var(--color-muted); font: 600 .72rem/1.45 var(--font-mono); text-transform: uppercase; letter-spacing: .05em; }
52
+ .post-card h2 { font-size: 1.18rem; line-height: 1.25; }
53
+ .post-card-copy > p:not(.post-meta) { display: -webkit-box; margin: .75rem 0 1rem; overflow: hidden; color: var(--color-muted); font-size: .92rem; -webkit-line-clamp: 3; -webkit-box-orient: vertical; }
54
+ .post-card-copy > span { margin-top: auto; color: var(--color-accent-ink); font-weight: 700; font-size: .88rem; }
55
+ @media (max-width: 980px) { .post-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
56
+ @media (max-width: 520px) { .post-grid { grid-template-columns: 1fr; } }
57
+ </style>
@@ -0,0 +1,214 @@
1
+ ---
2
+ /**
3
+ * E1.2 — reader reaction bar. Server-renders the post's current reaction tallies
4
+ * (from the E1.1 KV counters) and offers the fixed vocabulary as clickable
5
+ * buttons; a client script POSTs the chosen reaction to the public react endpoint
6
+ * and updates the displayed count optimistically, then reconciles against the
7
+ * authoritative tallies the endpoint returns. Anonymous, no identity, no
8
+ * moderation — an emoji tally, the first reader-sentiment signal (complements
9
+ * view counts). Tenant/collection-generic: mounts on any post route.
10
+ */
11
+ import { getReactionCounts, REACTIONS, type Reaction } from "@/lib/blog/reactions.js";
12
+ import { readKv } from "@/lib/env";
13
+ import { withBase } from "@/lib/basePath";
14
+
15
+ interface Props {
16
+ /** Collection id the post lives in (e.g. "blog", "the-build"). */
17
+ collection: string;
18
+ /** Post slug. */
19
+ slug: string;
20
+ }
21
+
22
+ const { collection, slug } = Astro.props;
23
+ const { tenant, basePath, cspNonce } = Astro.locals;
24
+
25
+ const META: Record<Reaction, { emoji: string; label: string }> = {
26
+ like: { emoji: "👍", label: "Like" },
27
+ insightful: { emoji: "💡", label: "Insightful" },
28
+ celebrate: { emoji: "🎉", label: "Celebrate" },
29
+ };
30
+
31
+ const kv = await readKv("TENANT_CACHE");
32
+ const counts = await getReactionCounts(kv, tenant.tenant_id, collection, slug);
33
+
34
+ // The react endpoint is base-path-scoped like every other same-origin POST
35
+ // (mirrors the newsletter form action); host-routed tenants get an empty prefix.
36
+ const endpoint = withBase(
37
+ basePath,
38
+ `/api/blog/${encodeURIComponent(collection)}/${encodeURIComponent(slug)}/react`,
39
+ );
40
+ ---
41
+
42
+ <section class="reaction-bar" aria-label="React to this article" data-reaction-bar data-endpoint={endpoint}>
43
+ <span class="reaction-bar__prompt">React</span>
44
+ <div class="reaction-bar__buttons">
45
+ {
46
+ REACTIONS.map((reaction) => (
47
+ <button
48
+ type="button"
49
+ class="reaction-btn"
50
+ data-reaction={reaction}
51
+ aria-pressed="false"
52
+ aria-label={META[reaction].label}
53
+ >
54
+ <span class="reaction-btn__emoji" aria-hidden="true">
55
+ {META[reaction].emoji}
56
+ </span>
57
+ <span class="reaction-btn__label">{META[reaction].label}</span>
58
+ <span class="reaction-btn__count" data-reaction-count>
59
+ {counts[reaction]}
60
+ </span>
61
+ </button>
62
+ ))
63
+ }
64
+ </div>
65
+ </section>
66
+
67
+ <style>
68
+ .reaction-bar {
69
+ display: flex;
70
+ flex-wrap: wrap;
71
+ align-items: center;
72
+ gap: 0.75rem 1rem;
73
+ width: min(720px, 100%);
74
+ margin: clamp(2.5rem, 6vw, 4rem) auto 0;
75
+ padding-top: clamp(1.5rem, 4vw, 2.25rem);
76
+ border-top: 1px solid var(--color-border);
77
+ }
78
+ .reaction-bar__prompt {
79
+ color: var(--color-muted);
80
+ font-size: 0.8rem;
81
+ font-weight: 700;
82
+ text-transform: uppercase;
83
+ letter-spacing: 0.04em;
84
+ }
85
+ .reaction-bar__buttons {
86
+ display: flex;
87
+ flex-wrap: wrap;
88
+ gap: 0.6rem;
89
+ }
90
+ .reaction-btn {
91
+ display: inline-flex;
92
+ align-items: center;
93
+ gap: 0.45rem;
94
+ padding: 0.5rem 0.85rem;
95
+ border: 1px solid var(--color-border);
96
+ border-radius: var(--radius-full);
97
+ background: var(--color-surface);
98
+ color: var(--color-ink, inherit);
99
+ font: inherit;
100
+ font-size: 0.9rem;
101
+ line-height: 1;
102
+ cursor: pointer;
103
+ transition: border-color 0.15s ease, background 0.15s ease, transform 0.08s ease;
104
+ }
105
+ .reaction-btn:hover {
106
+ border-color: var(--color-accent);
107
+ }
108
+ .reaction-btn:active {
109
+ transform: translateY(1px);
110
+ }
111
+ .reaction-btn[aria-pressed="true"] {
112
+ border-color: var(--color-accent);
113
+ background: color-mix(in srgb, var(--color-accent) 12%, var(--color-surface));
114
+ }
115
+ .reaction-btn__emoji {
116
+ font-size: 1.1rem;
117
+ }
118
+ .reaction-btn__label {
119
+ font-weight: 700;
120
+ }
121
+ .reaction-btn__count {
122
+ min-width: 1.25ch;
123
+ padding-left: 0.15rem;
124
+ color: var(--color-muted);
125
+ font-variant-numeric: tabular-nums;
126
+ font-weight: 700;
127
+ }
128
+ .reaction-btn[aria-pressed="true"] .reaction-btn__count {
129
+ color: var(--color-accent-ink);
130
+ }
131
+ </style>
132
+
133
+ <script is:inline nonce={cspNonce}>
134
+ (() => {
135
+ // One article (and one reaction bar) per page.
136
+ const root = document.querySelector("[data-reaction-bar]");
137
+ if (!root) return;
138
+
139
+ const endpoint = root.dataset.endpoint;
140
+ if (!endpoint) return;
141
+
142
+ // Remember this browser's reactions so a reload shows the pressed state and
143
+ // the same reader can't trivially re-inflate a tally by re-clicking.
144
+ const storeKey = "blogreact:" + endpoint;
145
+ let reacted = {};
146
+ try {
147
+ reacted = JSON.parse(localStorage.getItem(storeKey) || "{}") || {};
148
+ } catch {
149
+ reacted = {};
150
+ }
151
+ const persist = () => {
152
+ try {
153
+ localStorage.setItem(storeKey, JSON.stringify(reacted));
154
+ } catch {
155
+ /* storage disabled — pressed state is best-effort */
156
+ }
157
+ };
158
+
159
+ const buttons = Array.from(root.querySelectorAll("[data-reaction]"));
160
+ const countEl = (btn) => btn.querySelector("[data-reaction-count]");
161
+ const readCount = (btn) => {
162
+ const n = parseInt((countEl(btn)?.textContent || "0").trim(), 10);
163
+ return Number.isFinite(n) && n >= 0 ? n : 0;
164
+ };
165
+ const setCount = (btn, n) => {
166
+ const el = countEl(btn);
167
+ if (el) el.textContent = String(n);
168
+ };
169
+
170
+ for (const btn of buttons) {
171
+ if (reacted[btn.dataset.reaction]) btn.setAttribute("aria-pressed", "true");
172
+ }
173
+
174
+ const applyServerCounts = (counts) => {
175
+ if (!counts || typeof counts !== "object") return;
176
+ for (const btn of buttons) {
177
+ const v = counts[btn.dataset.reaction];
178
+ if (typeof v === "number" && v >= 0) setCount(btn, v);
179
+ }
180
+ };
181
+
182
+ for (const btn of buttons) {
183
+ btn.addEventListener("click", async () => {
184
+ const reaction = btn.dataset.reaction;
185
+ if (!reaction || reacted[reaction]) return;
186
+
187
+ // Optimistic: bump immediately, mark pressed.
188
+ const before = readCount(btn);
189
+ setCount(btn, before + 1);
190
+ btn.setAttribute("aria-pressed", "true");
191
+ reacted[reaction] = true;
192
+ persist();
193
+
194
+ try {
195
+ const res = await fetch(endpoint, {
196
+ method: "POST",
197
+ headers: { "content-type": "application/json" },
198
+ body: JSON.stringify({ reaction }),
199
+ });
200
+ const data = await res.json().catch(() => null);
201
+ // The endpoint always returns the authoritative tallies (even on
202
+ // rate-limit / invalid) — reconcile the display to them.
203
+ if (data && data.counts) applyServerCounts(data.counts);
204
+ } catch {
205
+ // Network fault: revert the optimistic bump and pressed state.
206
+ setCount(btn, before);
207
+ btn.setAttribute("aria-pressed", "false");
208
+ delete reacted[reaction];
209
+ persist();
210
+ }
211
+ });
212
+ }
213
+ })();
214
+ </script>
@@ -17,6 +17,7 @@
17
17
  */
18
18
  import type { ChromeNavItem, ChromeLink } from "@/lib/chrome/model";
19
19
  import { withBase } from "@/lib/basePath";
20
+ import { isCurrentNavHref } from "@/lib/chrome/navCurrent";
20
21
 
21
22
  interface Props {
22
23
  item: ChromeNavItem;
@@ -32,6 +33,11 @@ const { basePath } = Astro.locals;
32
33
 
33
34
  const itemHref = withBase(basePath, item.href);
34
35
  const hrefOf = (l: ChromeLink) => (l.external ? l.href : withBase(basePath, l.href));
36
+ // Exact-path match only — a parent item's own href never gets aria-current
37
+ // just because the visitor is on a page nested under it. See navCurrent.ts.
38
+ const currentPath = Astro.url.pathname;
39
+ const itemCurrent = isCurrentNavHref(currentPath, itemHref);
40
+ const isChildCurrent = (l: ChromeLink) => isCurrentNavHref(currentPath, hrefOf(l), l.external);
35
41
  const hasMenu =
36
42
  (item.columns && item.columns.length > 0) ||
37
43
  (item.children && item.children.length > 0);
@@ -77,6 +83,7 @@ const groups = isMega
77
83
  <li>
78
84
  <a
79
85
  href={hrefOf(child)}
86
+ aria-current={isChildCurrent(child) ? "page" : undefined}
80
87
  class="flex items-center gap-1.5 rounded-sm px-2 py-1.5 text-sm text-muted transition-colors hover:bg-bg hover:text-primary"
81
88
  {...(child.external ? { rel: "noopener noreferrer" } : { "data-astro-prefetch": true })}
82
89
  >
@@ -100,6 +107,7 @@ const groups = isMega
100
107
  <a
101
108
  href={itemHref}
102
109
  data-astro-prefetch
110
+ aria-current={itemCurrent ? "page" : undefined}
103
111
  class:list={[
104
112
  "flex items-center gap-1.5 border-b border-border py-2.5 text-sm font-medium text-text transition-colors last:border-b-0 hover:text-accent",
105
113
  item.flag && "text-accent",
@@ -119,6 +127,7 @@ const groups = isMega
119
127
  <a
120
128
  href={itemHref}
121
129
  data-astro-prefetch
130
+ aria-current={itemCurrent ? "page" : undefined}
122
131
  class:list={[
123
132
  "inline-flex items-center gap-1 py-2 text-sm transition-colors hover:text-accent group-focus-within:text-accent",
124
133
  item.flag && "font-semibold text-accent",
@@ -149,6 +158,7 @@ const groups = isMega
149
158
  <li>
150
159
  <a
151
160
  href={hrefOf(child)}
161
+ aria-current={isChildCurrent(child) ? "page" : undefined}
152
162
  class="flex items-center gap-1.5 rounded-sm px-2 py-1 text-sm text-muted transition-colors hover:bg-bg hover:text-primary"
153
163
  {...(child.external ? { rel: "noopener noreferrer" } : { "data-astro-prefetch": true })}
154
164
  >
@@ -171,6 +181,7 @@ const groups = isMega
171
181
  <li>
172
182
  <a
173
183
  href={hrefOf(child)}
184
+ aria-current={isChildCurrent(child) ? "page" : undefined}
174
185
  class="block rounded-sm px-3 py-1.5 text-sm text-muted transition-colors hover:bg-bg hover:text-primary"
175
186
  {...(child.external ? { rel: "noopener noreferrer" } : { "data-astro-prefetch": true })}
176
187
  >
@@ -187,6 +198,7 @@ const groups = isMega
187
198
  <a
188
199
  href={itemHref}
189
200
  data-astro-prefetch
201
+ aria-current={itemCurrent ? "page" : undefined}
190
202
  class:list={[
191
203
  "inline-flex items-center gap-1 py-2 text-sm transition-colors hover:text-accent",
192
204
  item.flag && "font-semibold text-accent",
@@ -7,12 +7,16 @@
7
7
  * The newsletter is a plain POST form (progressive); it does not embed any cart
8
8
  * or checkout logic (commerce boundary).
9
9
  */
10
- import type { ChromeLink, FooterColumn, FooterNewsletter, SocialLink, ChromeLogo } from "@/lib/chrome/model";
10
+ import type { ChromeLink, FooterColumn, FooterNewsletter, SocialLink, ChromeLogo, FooterVariant } from "@/lib/chrome/model";
11
11
  import { withBase } from "@/lib/basePath";
12
12
 
13
13
  interface Props {
14
14
  columns: FooterColumn[];
15
15
  brand?: { logo?: ChromeLogo; wordmark?: string; contact?: string[] };
16
+ /** Content-shape variant (lib/chrome/model.ts FooterVariant). Defaults to
17
+ * "default" — the only member today; threaded through so a future variant
18
+ * is a drop-in without touching every caller. */
19
+ variant?: FooterVariant;
16
20
  newsletter?: FooterNewsletter;
17
21
  /** Payment method labels rendered as chips (e.g. "Visa", "PayPal"). */
18
22
  payments?: string[];
@@ -29,6 +33,7 @@ interface Props {
29
33
  const {
30
34
  columns,
31
35
  brand,
36
+ variant = "default",
32
37
  newsletter,
33
38
  payments = [],
34
39
  social = [],
@@ -43,7 +48,7 @@ const hrefOf = (l: ChromeLink) => (l.external ? l.href : withBase(basePath, l.hr
43
48
  const showLogo = !!brand?.logo && /^(https?:|\/)/.test(brand.logo.src);
44
49
  ---
45
50
 
46
- <footer class:list={["border-t border-border bg-surface text-text", className]}>
51
+ <footer class:list={["border-t border-border bg-surface text-text", className]} data-variant={variant}>
47
52
  <div class="container-prose py-12">
48
53
  <div class="grid gap-10 md:grid-cols-[1.4fr_repeat(3,1fr)]">
49
54
  <!-- Brand -->