@tokenoftrust/storefront-runner 1.4.2-rc.0 → 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 (363) hide show
  1. package/apps/storefront/astro.config.mjs +3 -3
  2. package/apps/storefront/env.d.ts +54 -5
  3. package/apps/storefront/integrations/materialize-guard.mjs +12 -14
  4. package/apps/storefront/migrations-apps/0004_deep_morph.sql +36 -0
  5. package/apps/storefront/migrations-apps/0005_common_mystique.sql +23 -0
  6. package/apps/storefront/migrations-apps/0006_broad_microchip.sql +16 -0
  7. package/apps/storefront/migrations-apps/0007_brave_outlaw_kid.sql +15 -0
  8. package/apps/storefront/migrations-apps/0008_blue_dazzler.sql +11 -0
  9. package/apps/storefront/migrations-apps/0009_bored_shinobi_shaw.sql +15 -0
  10. package/apps/storefront/migrations-apps/0010_ws4_verified_provider_measurement.sql +35 -0
  11. package/apps/storefront/migrations-apps/meta/0004_snapshot.json +1474 -0
  12. package/apps/storefront/migrations-apps/meta/0005_snapshot.json +1617 -0
  13. package/apps/storefront/migrations-apps/meta/0006_snapshot.json +1716 -0
  14. package/apps/storefront/migrations-apps/meta/0007_snapshot.json +1812 -0
  15. package/apps/storefront/migrations-apps/meta/0008_snapshot.json +1881 -0
  16. package/apps/storefront/migrations-apps/meta/0009_snapshot.json +1979 -0
  17. package/apps/storefront/migrations-apps/meta/_journal.json +42 -0
  18. package/apps/storefront/public/apps/tot.demo.analytics/0.1.0/entry.js +42 -0
  19. package/apps/storefront/public/platform/amplitude-http/v1/entry.mjs +19 -0
  20. package/apps/storefront/public/platform/google-measurement/v1/entry.mjs +19 -0
  21. package/apps/storefront/public/shared/commerce-chrome.css +20 -2
  22. package/apps/storefront/scripts/build-v84-clean-clone-standalone.mjs +68 -0
  23. package/apps/storefront/scripts/deny-v84-clean-clone-standalone-capabilities.mjs +111 -0
  24. package/apps/storefront/scripts/deny-v84-dogfood-capabilities.mjs +2 -0
  25. package/apps/storefront/scripts/deny-v84-lifecycle-capabilities.mjs +247 -0
  26. package/apps/storefront/scripts/dogfood-v84-nonportable-routes.ts +73 -0
  27. package/apps/storefront/scripts/fixed-v84-native-install-producer.ts +79 -0
  28. package/apps/storefront/scripts/generated/v84-clean-clone-replay.bundle.mjs +10982 -0
  29. package/apps/storefront/scripts/import-v84-through-ws2.ts +38 -0
  30. package/apps/storefront/scripts/initialize-v84-lifecycle-tooling-dependency.ts +2 -0
  31. package/apps/storefront/scripts/initialize-v84-lifecycle-tooling.ts +4 -0
  32. package/apps/storefront/scripts/install-v84-native-through-ws1.ts +185 -0
  33. package/apps/storefront/scripts/prove-clone-isolation.ts +506 -0
  34. package/apps/storefront/scripts/replay-v84-clean-clone-pass.ts +148 -0
  35. package/apps/storefront/scripts/replay-v84-clean-clone-standalone.ts +35 -0
  36. package/apps/storefront/scripts/replay-v84-clean-clone.ts +214 -0
  37. package/apps/storefront/scripts/run-fixed-v84-native-install-producer.mjs +18 -0
  38. package/apps/storefront/scripts/run-v84-clone-isolation-proof.mjs +18 -0
  39. package/apps/storefront/scripts/run-v84-clone-lifecycle-proof.mjs +18 -0
  40. package/apps/storefront/scripts/run-v84-import-through-ws2-proof.mjs +17 -0
  41. package/apps/storefront/scripts/run-v84-nonportable-route-dogfood.mjs +15 -0
  42. package/apps/storefront/scripts/sanitize-v84-proof-environment.mjs +21 -0
  43. package/apps/storefront/scripts/v84CleanCloneGraphPin.ts +42 -0
  44. package/apps/storefront/scripts/v84CleanCloneSourceGraph.ts +145 -0
  45. package/apps/storefront/scripts/validate-v84-clone-lifecycle.ts +18 -0
  46. package/apps/storefront/src/components/Img.astro +1 -1
  47. package/apps/storefront/src/components/admin/AdminBlogTab.astro +19 -16
  48. package/apps/storefront/src/components/admin/AdminIntegrationsTab.astro +105 -0
  49. package/apps/storefront/src/components/admin/AdminPublishTab.astro +1057 -412
  50. package/apps/storefront/src/components/admin/AdminSettingsTab.astro +2 -2
  51. package/apps/storefront/src/components/admin/orders/OrdersWorkspace.astro +5 -5
  52. package/apps/storefront/src/components/chrome/NavDropdown.astro +12 -0
  53. package/apps/storefront/src/components/chrome/SiteFooter.astro +7 -2
  54. package/apps/storefront/src/components/chrome/SiteHeader.astro +46 -10
  55. package/apps/storefront/src/components/compliance/AdultSignatureNotice.astro +1 -1
  56. package/apps/storefront/src/components/compliance/ComplianceNotice.astro +1 -2
  57. package/apps/storefront/src/components/compliance/ExciseTaxNotice.astro +1 -2
  58. package/apps/storefront/src/components/compliance/PactActNotice.astro +1 -1
  59. package/apps/storefront/src/components/compliance/Prop65Warning.astro +1 -2
  60. package/apps/storefront/src/components/compliance/PurchaseLimitNotice.astro +2 -3
  61. package/apps/storefront/src/components/compliance/ShippingRestrictionNotice.astro +2 -2
  62. package/apps/storefront/src/components/compliance/StateEligibilityNotice.astro +2 -3
  63. package/apps/storefront/src/components/content/TrustStrip.astro +4 -4
  64. package/apps/storefront/src/components/islands/MobileNav.tsx +26 -1
  65. package/apps/storefront/src/components/islands/VariantSelector.tsx +27 -2
  66. package/apps/storefront/src/components/islands/useCheckoutHandoffCorrelation.ts +75 -0
  67. package/apps/storefront/src/components/membership/IllustrativeLabel.astro +1 -2
  68. package/apps/storefront/src/components/membership/TierCards.astro +2 -2
  69. package/apps/storefront/src/components/membership/TierComparisonTable.astro +2 -2
  70. package/apps/storefront/src/components/subscription/SavingsExplainer.astro +2 -2
  71. package/apps/storefront/src/config/adminTenantLookup.ts +35 -0
  72. package/apps/storefront/src/config/devTenantSeed.ts +9 -9
  73. package/apps/storefront/src/config/resolver.ts +4 -4
  74. package/apps/storefront/src/layouts/Layout.astro +100 -20
  75. package/apps/storefront/src/lib/activity/candidateSubmitLease.ts +87 -0
  76. package/apps/storefront/src/lib/activity/changeActorAttribution.ts +86 -6
  77. package/apps/storefront/src/lib/activity/deployVersion.ts +1 -1
  78. package/apps/storefront/src/lib/admin/adminShell.ts +2 -0
  79. package/apps/storefront/src/lib/admin/envelope.ts +1 -2
  80. package/apps/storefront/src/lib/apps/adminService.ts +10 -0
  81. package/apps/storefront/src/lib/apps/adminSession.ts +1 -1
  82. package/apps/storefront/src/lib/apps/appVersionStore.ts +281 -0
  83. package/apps/storefront/src/lib/apps/consentConfigStore.ts +228 -0
  84. package/apps/storefront/src/lib/apps/gtmMigrationInstall.ts +447 -0
  85. package/apps/storefront/src/lib/apps/gtmMigrationReport.ts +367 -0
  86. package/apps/storefront/src/lib/apps/gtmMigrationTab.ts +315 -0
  87. package/apps/storefront/src/lib/apps/integrationLifecycle.ts +614 -0
  88. package/apps/storefront/src/lib/apps/integrationLifecycleController.ts +125 -0
  89. package/apps/storefront/src/lib/apps/integrationReadModel.ts +563 -0
  90. package/apps/storefront/src/lib/apps/integrationsTab.ts +547 -0
  91. package/apps/storefront/src/lib/apps/integrationsView.ts +106 -0
  92. package/apps/storefront/src/lib/apps/managedAnalyticsService.ts +599 -0
  93. package/apps/storefront/src/lib/apps/orders/orderConversionD1.ts +150 -0
  94. package/apps/storefront/src/lib/apps/orders/orderForwardReceiver.ts +8 -0
  95. package/apps/storefront/src/lib/apps/orders/ordersStore.ts +1 -1
  96. package/apps/storefront/src/lib/apps/orders/verifiedProviderMeasurement.ts +117 -0
  97. package/apps/storefront/src/lib/apps/registryService.ts +264 -10
  98. package/apps/storefront/src/lib/apps/tenantIntegrationMaterialization.ts +170 -0
  99. package/apps/storefront/src/lib/apps/v84CleanCloneReplayContext.ts +16 -0
  100. package/apps/storefront/src/lib/apps/v84CloneLifecycleCacheEvidence.ts +105 -0
  101. package/apps/storefront/src/lib/apps/v84CloneLifecycleEvidence.ts +177 -0
  102. package/apps/storefront/src/lib/apps/v84CloneLifecycleMaterialization.ts +251 -0
  103. package/apps/storefront/src/lib/apps/v84CloneLifecycleSemantics.ts +241 -0
  104. package/apps/storefront/src/lib/apps/v84CloneMigrationEvidence.ts +211 -0
  105. package/apps/storefront/src/lib/apps/v84CloneNativeInstallEvidence.ts +409 -0
  106. package/apps/storefront/src/lib/apps/v84NonportableCodeCoaching.ts +368 -0
  107. package/apps/storefront/src/lib/apps/v84NonportableRouteContract.ts +188 -0
  108. package/apps/storefront/src/lib/apps/v84NonportableRouteDogfood.ts +291 -0
  109. package/apps/storefront/src/lib/apps/ws1ManagedAnalyticsClient.ts +95 -0
  110. package/apps/storefront/src/lib/auth/adminApiGuard.ts +66 -0
  111. package/apps/storefront/src/lib/auth/adminEntry.ts +2 -2
  112. package/apps/storefront/src/lib/auth/adminLanding.ts +88 -0
  113. package/apps/storefront/src/lib/blog/collection.ts +97 -10
  114. package/apps/storefront/src/lib/blog/markdown.ts +1 -1
  115. package/apps/storefront/src/lib/blog/provider.ts +57 -3
  116. package/apps/storefront/src/lib/blog/types.ts +9 -9
  117. package/apps/storefront/src/lib/chrome/examples/tokenoftrust.chrome-assignments.example.json +6 -0
  118. package/apps/storefront/src/lib/chrome/examples/tokenoftrust.chrome.example.json +79 -0
  119. package/apps/storefront/src/lib/chrome/model.ts +48 -253
  120. package/apps/storefront/src/lib/chrome/navCurrent.ts +29 -0
  121. package/apps/storefront/src/lib/chrome/parseConfig.ts +18 -32
  122. package/apps/storefront/src/lib/chrome/variant.ts +39 -0
  123. package/apps/storefront/src/lib/commerce/checkoutHandoffCorrelation.ts +105 -0
  124. package/apps/storefront/src/lib/compliance/tamper-sentinel-boot.ts +152 -0
  125. package/apps/storefront/src/lib/compliance/tamper-sentinel.ts +327 -0
  126. package/apps/storefront/src/lib/content/index.ts +5 -5
  127. package/apps/storefront/src/lib/content-edit/applyDiff.ts +1 -1
  128. package/apps/storefront/src/lib/content-edit/route.ts +1 -1
  129. package/apps/storefront/src/lib/d1/catalog.ts +12 -0
  130. package/apps/storefront/src/lib/d1/schema-apps.ts +276 -0
  131. package/apps/storefront/src/lib/dev/cockpit/ask.ts +49 -0
  132. package/apps/storefront/src/lib/dev/cockpit/clickToSource.ts +71 -0
  133. package/apps/storefront/src/lib/dev/cockpit/copyCommand.ts +227 -0
  134. package/apps/storefront/src/lib/dev/cockpit/files.ts +249 -0
  135. package/apps/storefront/src/lib/dev/cockpit/hostedActivity.ts +500 -0
  136. package/apps/storefront/src/lib/dev/cockpit/infoPopover.ts +12 -0
  137. package/apps/storefront/src/lib/dev/cockpit/mcpCopy.ts +34 -0
  138. package/apps/storefront/src/lib/dev/cockpit/monitor.ts +107 -0
  139. package/apps/storefront/src/lib/dev/cockpit/stepper.ts +116 -0
  140. package/apps/storefront/src/lib/dev/cockpit/themeToggle.ts +23 -0
  141. package/apps/storefront/src/lib/dev/runtimeStore.ts +1 -1
  142. package/apps/storefront/src/lib/edgeCache.ts +18 -6
  143. package/apps/storefront/src/lib/email/candidateConflictEmail.ts +80 -0
  144. package/apps/storefront/src/lib/integration/browserEnvironment.ts +137 -0
  145. package/apps/storefront/src/lib/integration/managedInstallDeclarations.ts +72 -0
  146. package/apps/storefront/src/lib/integration/nonceStamp.ts +49 -0
  147. package/apps/storefront/src/lib/integration/tenantDeclarations.ts +116 -0
  148. package/apps/storefront/src/lib/membership/eligibility.ts +1 -1
  149. package/apps/storefront/src/lib/membership/model.ts +1 -2
  150. package/apps/storefront/src/lib/newsletter/confirmationEmail.ts +2 -2
  151. package/apps/storefront/src/lib/newsletter/consent.ts +2 -2
  152. package/apps/storefront/src/lib/newsletter/send.ts +3 -3
  153. package/apps/storefront/src/lib/previewReload.ts +57 -1
  154. package/apps/storefront/src/lib/publish/adminPublishTab.ts +1775 -669
  155. package/apps/storefront/src/lib/publish/buildStream.ts +269 -0
  156. package/apps/storefront/src/lib/publish/collapsePersistence.ts +72 -0
  157. package/apps/storefront/src/lib/publish/lifecycleState.ts +369 -0
  158. package/apps/storefront/src/lib/publish/liveReleaseSummary.ts +29 -0
  159. package/apps/storefront/src/lib/publish/mergeDoctorOnFailure.ts +78 -0
  160. package/apps/storefront/src/lib/publish/mergeHealthBanner.ts +62 -0
  161. package/apps/storefront/src/lib/publish/mergeRowFindings.ts +124 -0
  162. package/apps/storefront/src/lib/publish/mergeSupportEscalation.ts +75 -0
  163. package/apps/storefront/src/lib/publish/publish-guard.ts +88 -0
  164. package/apps/storefront/src/lib/publish/publishFormat.ts +165 -0
  165. package/apps/storefront/src/lib/publish/publishHero.ts +177 -0
  166. package/apps/storefront/src/lib/publish/reviewRelease/changeState.ts +66 -0
  167. package/apps/storefront/src/lib/publish/reviewRelease/copy.ts +141 -0
  168. package/apps/storefront/src/lib/publish/reviewRelease/index.ts +41 -0
  169. package/apps/storefront/src/lib/publish/reviewRelease/permissions.ts +47 -0
  170. package/apps/storefront/src/lib/publish/reviewRelease/releaseState.ts +42 -0
  171. package/apps/storefront/src/lib/publish/reviewRelease/types.ts +59 -0
  172. package/apps/storefront/src/lib/publish/reviewRelease/viewModel.ts +440 -0
  173. package/apps/storefront/src/lib/publish/reviewReleaseWorkspace/actions.ts +161 -0
  174. package/apps/storefront/src/lib/publish/reviewReleaseWorkspace/controller.ts +325 -0
  175. package/apps/storefront/src/lib/publish/reviewReleaseWorkspace/render.ts +241 -0
  176. package/apps/storefront/src/lib/publish/shipWorkspace.ts +22 -3
  177. package/apps/storefront/src/lib/publish/sseRoute.ts +119 -0
  178. package/apps/storefront/src/lib/publish/streamLogConsole.ts +142 -0
  179. package/apps/storefront/src/lib/publish/versionNumber.ts +52 -0
  180. package/apps/storefront/src/lib/rawChrome.ts +125 -47
  181. package/apps/storefront/src/lib/rawMarketingHtml.ts +74 -3
  182. package/apps/storefront/src/lib/seo/alternates.ts +1 -1
  183. package/apps/storefront/src/lib/social/notify.ts +4 -4
  184. package/apps/storefront/src/lib/storyblok/content-model.ts +7 -33
  185. package/apps/storefront/src/lib/storyblok/provider.ts +168 -60
  186. package/apps/storefront/src/lib/subscription/model.ts +4 -4
  187. package/apps/storefront/src/lib/the-build/provider.ts +9 -4
  188. package/apps/storefront/src/middleware/index.ts +273 -51
  189. package/apps/storefront/src/pages/[...slug].astro +9 -4
  190. package/apps/storefront/src/pages/admin/review-release.astro +265 -0
  191. package/apps/storefront/src/pages/admin/select.astro +96 -0
  192. package/apps/storefront/src/pages/admin.astro +16 -2
  193. package/apps/storefront/src/pages/api/activity.ts +8 -8
  194. package/apps/storefront/src/pages/api/admin/activity-alerts.ts +5 -20
  195. package/apps/storefront/src/pages/api/admin/orders/[id].ts +7 -2
  196. package/apps/storefront/src/pages/api/admin/orders.ts +8 -2
  197. package/apps/storefront/src/pages/api/apps/admin/gtm-migration-install.ts +174 -0
  198. package/apps/storefront/src/pages/api/apps/admin/gtm-migration.ts +96 -0
  199. package/apps/storefront/src/pages/api/apps/admin/integration-lifecycle.ts +118 -0
  200. package/apps/storefront/src/pages/api/apps/admin/integrations.ts +89 -0
  201. package/apps/storefront/src/pages/api/apps/admin/platform-adapters.ts +85 -0
  202. package/apps/storefront/src/pages/api/apps/internal/order-forward.ts +74 -2
  203. package/apps/storefront/src/pages/api/commerce/checkout-handoff.ts +77 -0
  204. package/apps/storefront/src/pages/api/compliance/tamper.ts +88 -0
  205. package/apps/storefront/src/pages/api/integrations/v1/[operation].ts +108 -0
  206. package/apps/storefront/src/pages/api/rum/vitals.ts +1 -2
  207. package/apps/storefront/src/pages/blog/[slug].astro +5 -5
  208. package/apps/storefront/src/pages/blog/author/[author].astro +3 -3
  209. package/apps/storefront/src/pages/blog/category/[category].astro +2 -2
  210. package/apps/storefront/src/pages/blog/index.astro +2 -2
  211. package/apps/storefront/src/pages/blog/tag/[tag].astro +2 -2
  212. package/apps/storefront/src/pages/cockpit.astro +61 -1362
  213. package/apps/storefront/src/pages/index.astro +10 -5
  214. package/apps/storefront/src/pages/llms.txt.ts +1 -1
  215. package/apps/storefront/src/pages/products/[handle].astro +7 -6
  216. package/apps/storefront/src/pages/sitemap.xml.ts +2 -2
  217. package/apps/storefront/src/pages/style-guide/[tenant]/[theme].astro +31 -27
  218. package/apps/storefront/src/pages/style-guide/[tenant]/chrome/[theme].astro +1 -1
  219. package/apps/storefront/src/pages/tenants/[id]/[...path].ts +38 -2
  220. package/apps/storefront/src/pages/the-build/[slug].astro +4 -4
  221. package/apps/storefront/src/pages/the-build/index.astro +2 -2
  222. package/apps/storefront/src/pages/the-build/rss.xml.ts +2 -2
  223. package/apps/storefront/src/styles/admin.css +27 -1
  224. package/apps/storefront/tsconfig.json +1 -1
  225. package/package.json +1 -1
  226. package/packages/public-runtime/package.json +2 -1
  227. package/packages/public-runtime/schemas/managed-analytics/v1/adapter-definition.schema.json +427 -0
  228. package/packages/public-runtime/schemas/managed-analytics/v1/create-integration-install-request.schema.json +161 -0
  229. package/packages/public-runtime/schemas/managed-analytics/v1/create-migration-draft-request.schema.json +157 -0
  230. package/packages/public-runtime/schemas/managed-analytics/v1/delete-integration-install-request.schema.json +84 -0
  231. package/packages/public-runtime/schemas/managed-analytics/v1/delete-migration-draft-request.schema.json +84 -0
  232. package/packages/public-runtime/schemas/managed-analytics/v1/error-envelope.schema.json +180 -0
  233. package/packages/public-runtime/schemas/managed-analytics/v1/get-adapter-definition-request.schema.json +52 -0
  234. package/packages/public-runtime/schemas/managed-analytics/v1/get-integration-install-request.schema.json +52 -0
  235. package/packages/public-runtime/schemas/managed-analytics/v1/get-integration-revision-request.schema.json +79 -0
  236. package/packages/public-runtime/schemas/managed-analytics/v1/get-migration-draft-request.schema.json +52 -0
  237. package/packages/public-runtime/schemas/managed-analytics/v1/integration-install.schema.json +167 -0
  238. package/packages/public-runtime/schemas/managed-analytics/v1/integration-revision.schema.json +208 -0
  239. package/packages/public-runtime/schemas/managed-analytics/v1/list-adapter-definitions-request.schema.json +53 -0
  240. package/packages/public-runtime/schemas/managed-analytics/v1/list-integration-installs-request.schema.json +53 -0
  241. package/packages/public-runtime/schemas/managed-analytics/v1/list-integration-revisions-request.schema.json +82 -0
  242. package/packages/public-runtime/schemas/managed-analytics/v1/migration-draft.schema.json +218 -0
  243. package/packages/public-runtime/schemas/managed-analytics/v1/mutation-request.schema.json +52 -0
  244. package/packages/public-runtime/schemas/managed-analytics/v1/mutation-success-envelope.schema.json +49 -0
  245. package/packages/public-runtime/schemas/managed-analytics/v1/read-request.schema.json +20 -0
  246. package/packages/public-runtime/schemas/managed-analytics/v1/read-success-envelope.schema.json +45 -0
  247. package/packages/public-runtime/schemas/managed-analytics/v1/revise-integration-install-request.schema.json +156 -0
  248. package/packages/public-runtime/schemas/managed-analytics/v1/set-integration-install-status-request.schema.json +92 -0
  249. package/packages/public-runtime/schemas/managed-analytics/v1/update-migration-draft-request.schema.json +184 -0
  250. package/packages/public-runtime/scripts/generate-managed-analytics-api-v1-schemas.mjs +13 -0
  251. package/packages/public-runtime/src/activity/catalog.ts +41 -37
  252. package/packages/public-runtime/src/activity/event.ts +6 -6
  253. package/packages/public-runtime/src/activity/index.ts +2 -2
  254. package/packages/public-runtime/src/activity/redaction.ts +4 -4
  255. package/packages/public-runtime/src/candidate-index.ts +42 -8
  256. package/packages/public-runtime/src/checkout-orders.ts +2 -2
  257. package/packages/public-runtime/src/checkout-style-publish.ts +2 -2
  258. package/packages/public-runtime/src/checkout-style.ts +7 -7
  259. package/packages/public-runtime/src/checkout.ts +3 -3
  260. package/packages/public-runtime/src/chrome.ts +777 -0
  261. package/packages/public-runtime/src/compliance-obligations.ts +310 -0
  262. package/packages/public-runtime/src/csp.ts +99 -0
  263. package/packages/public-runtime/src/customization-runtime.ts +32 -1
  264. package/packages/public-runtime/src/destination-webhook/contract.ts +353 -0
  265. package/packages/public-runtime/src/embed-catalog.d.mts +9 -0
  266. package/packages/public-runtime/src/embed-catalog.mjs +40 -0
  267. package/packages/public-runtime/src/evidence.ts +288 -0
  268. package/packages/public-runtime/src/gtm-migration/bounds.ts +138 -0
  269. package/packages/public-runtime/src/gtm-migration/disposition-rules.ts +361 -0
  270. package/packages/public-runtime/src/gtm-migration/dispositions.ts +181 -0
  271. package/packages/public-runtime/src/gtm-migration/draft-generator.ts +400 -0
  272. package/packages/public-runtime/src/gtm-migration/graph.ts +468 -0
  273. package/packages/public-runtime/src/gtm-migration/index.ts +95 -0
  274. package/packages/public-runtime/src/gtm-migration/parse.ts +410 -0
  275. package/packages/public-runtime/src/gtm-migration/references.ts +266 -0
  276. package/packages/public-runtime/src/gtm-migration/types.ts +86 -0
  277. package/packages/public-runtime/src/index.ts +16 -1
  278. package/packages/public-runtime/src/integration/assurance-decision.ts +259 -0
  279. package/packages/public-runtime/src/integration/assurance.ts +228 -0
  280. package/packages/public-runtime/src/integration/candidate-evidence.ts +377 -0
  281. package/packages/public-runtime/src/integration/commerce-broker.ts +497 -0
  282. package/packages/public-runtime/src/integration/consent-broker.ts +337 -0
  283. package/packages/public-runtime/src/integration/consent-cmp-demand.ts +115 -0
  284. package/packages/public-runtime/src/integration/consent-config.ts +260 -0
  285. package/packages/public-runtime/src/integration/consent-preview.ts +125 -0
  286. package/packages/public-runtime/src/integration/correlation-token.ts +444 -0
  287. package/packages/public-runtime/src/integration/csp-compiler.ts +267 -0
  288. package/packages/public-runtime/src/integration/csp.ts +105 -0
  289. package/packages/public-runtime/src/integration/digest.ts +43 -0
  290. package/packages/public-runtime/src/integration/environment.ts +228 -0
  291. package/packages/public-runtime/src/integration/events-catalog.ts +186 -0
  292. package/packages/public-runtime/src/integration/events.ts +701 -0
  293. package/packages/public-runtime/src/integration/g2-action-boundary.ts +106 -0
  294. package/packages/public-runtime/src/integration/index.ts +90 -0
  295. package/packages/public-runtime/src/integration/interim-bridge.ts +138 -0
  296. package/packages/public-runtime/src/integration/lifecycle-boot.ts +317 -0
  297. package/packages/public-runtime/src/integration/lifecycle-runtime.ts +393 -0
  298. package/packages/public-runtime/src/integration/lifecycle.ts +94 -0
  299. package/packages/public-runtime/src/integration/managed-analytics-api-v1-safety.d.mts +2 -0
  300. package/packages/public-runtime/src/integration/managed-analytics-api-v1-safety.mjs +10 -0
  301. package/packages/public-runtime/src/integration/managed-analytics-api-v1.schemas.ts +382 -0
  302. package/packages/public-runtime/src/integration/managed-analytics-api-v1.ts +684 -0
  303. package/packages/public-runtime/src/integration/managed-analytics-bridge.ts +383 -0
  304. package/packages/public-runtime/src/integration/managed-analytics-value-safety.ts +14 -0
  305. package/packages/public-runtime/src/integration/managed-analytics.ts +277 -0
  306. package/packages/public-runtime/src/integration/manifest.ts +470 -0
  307. package/packages/public-runtime/src/integration/native-amplitude.ts +362 -0
  308. package/packages/public-runtime/src/integration/native-ga4.ts +352 -0
  309. package/packages/public-runtime/src/integration/observability.ts +451 -0
  310. package/packages/public-runtime/src/integration/order-conversion.ts +0 -0
  311. package/packages/public-runtime/src/integration/phases.ts +182 -0
  312. package/packages/public-runtime/src/integration/plan.ts +112 -0
  313. package/packages/public-runtime/src/integration/platform-adapter-runtime.ts +146 -0
  314. package/packages/public-runtime/src/integration/platform-adapters.ts +671 -0
  315. package/packages/public-runtime/src/integration/promotion-gate.ts +451 -0
  316. package/packages/public-runtime/src/integration/provider-checkout-extension.ts +425 -0
  317. package/packages/public-runtime/src/integration/receipt-measurement.ts +370 -0
  318. package/packages/public-runtime/src/integration/regulated-parent-cert.ts +311 -0
  319. package/packages/public-runtime/src/integration/render-phases.ts +267 -0
  320. package/packages/public-runtime/src/integration/resolve.ts +465 -0
  321. package/packages/public-runtime/src/integration/result.ts +239 -0
  322. package/packages/public-runtime/src/integration/secrets.ts +92 -0
  323. package/packages/public-runtime/src/integration/tamper-evidence.ts +205 -0
  324. package/packages/public-runtime/src/integration/tamper-recovery.ts +127 -0
  325. package/packages/public-runtime/src/integration/version.ts +92 -0
  326. package/packages/public-runtime/src/integration-capabilities.ts +520 -0
  327. package/packages/public-runtime/src/membership.ts +2 -2
  328. package/packages/public-runtime/src/raw-html-policy.ts +2 -4
  329. package/packages/public-runtime/src/regulated-commerce/broker/consent-dedupe.ts +144 -0
  330. package/packages/public-runtime/src/regulated-commerce/correlation/contract.ts +177 -0
  331. package/packages/public-runtime/src/regulated-commerce/diagnostics/local-dispatch.ts +24 -0
  332. package/packages/public-runtime/src/regulated-commerce/diagnostics/read-model.ts +69 -0
  333. package/packages/public-runtime/src/regulated-commerce/dispatch/core.ts +134 -0
  334. package/packages/public-runtime/src/regulated-commerce/events/contract.ts +113 -0
  335. package/packages/public-runtime/src/regulated-commerce/ledger/core.ts +81 -0
  336. package/packages/public-runtime/src/regulated-commerce/mappings/ga4-ads.ts +87 -0
  337. package/packages/public-runtime/src/regulated-commerce/provider-order/bridge.ts +52 -0
  338. package/packages/public-runtime/src/regulated-commerce/purchase-truth/contract.ts +252 -0
  339. package/packages/public-runtime/src/regulated-commerce/sources/accepted-browser-operation.ts +67 -0
  340. package/packages/public-runtime/src/regulated-commerce/testing/fake-destination.ts +93 -0
  341. package/packages/public-runtime/src/static-bundle.ts +5 -5
  342. package/packages/public-runtime/src/tenant-assets.ts +143 -4
  343. package/packages/public-runtime/src/tenant.ts +45 -9
  344. package/packages/public-runtime/tests/fixtures/integration/browser-environment.example.json +8 -0
  345. package/packages/public-runtime/tests/fixtures/integration/commerce-event.example.json +44 -0
  346. package/packages/public-runtime/tests/fixtures/integration/commerce-order-event.example.json +44 -0
  347. package/packages/public-runtime/tests/fixtures/integration/compliance-assurance-record.example.json +50 -0
  348. package/packages/public-runtime/tests/fixtures/integration/integration-manifest-v2.example.json +120 -0
  349. package/packages/public-runtime/tests/fixtures/integration/managed-analytics-v1/fake-adapter-catalog.json +35 -0
  350. package/packages/public-runtime/tests/fixtures/integration/tenant-action-registry.example.json +11 -0
  351. package/packages/public-runtime/tests/fixtures/integration/tenant-local-example/README.md +22 -0
  352. package/packages/public-runtime/tests/fixtures/integration/tenant-local-example/entry.js +30 -0
  353. package/packages/public-runtime/tests/fixtures/integration/tenant-local-example/manifest.json +75 -0
  354. package/packages/public-runtime/tests/fixtures/integration/tenant-local-example/public-config.json +3 -0
  355. package/packages/public-runtime/tests/fixtures/regulated-commerce/fake-destinations.ts +38 -0
  356. package/packages/public-runtime/tests/gtm-migration/fixtures/gtm-sample-container-v84.provenance.json +29 -0
  357. package/packages/public-runtime/tests/gtm-migration/fixtures/gtm-sample-container-v84.redacted.json +3397 -0
  358. package/scripts/dev/file-browser.mjs +2 -2
  359. package/scripts/dev/file-writer.mjs +2 -2
  360. package/scripts/dev/integration-guardrails.mjs +498 -0
  361. package/scripts/dev/safe-path.mjs +2 -2
  362. package/scripts/dev/tenant-source-root.mjs +1 -1
  363. package/scripts/tot-dev.mjs +2 -2
@@ -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;
@@ -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);
@@ -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 -->
@@ -3,7 +3,7 @@
3
3
  * <SiteHeader> — logo/wordmark + search island + account/auth + cart-count
4
4
  * affordance, with the primary <MegaMenu> nav below and a mobile drawer (the
5
5
  * <MobileNav> island). Tenant-agnostic; theme-token skinned. This is the
6
- * CANONICAL site header (unit I4) — the former `components/nav/Header.astro`
6
+ * CANONICAL site header — the former `components/nav/Header.astro`
7
7
  * app-Layout implementation is retired; every feature it had (search typeahead,
8
8
  * mobile drawer with focus-trap/scroll-lock, the session-aware auth entry point,
9
9
  * the saved-items/wishlist cue, marketing header CTAs, a dark header variant)
@@ -13,8 +13,9 @@
13
13
  * COMMERCE BOUNDARY: the cart is a LINK only — no cart state, no add-to-cart.
14
14
  * The count is a static affordance the managed cart can hydrate later.
15
15
  */
16
- import type { ChromeNavItem, ChromeLogo, ChromeCta } from "@/lib/chrome/model";
16
+ import type { ChromeNavItem, ChromeLogo, ChromeCta, HeaderVariant } from "@/lib/chrome/model";
17
17
  import MegaMenu from "@/components/chrome/MegaMenu.astro";
18
+ import NavDropdown from "@/components/chrome/NavDropdown.astro";
18
19
  import SearchTypeahead from "@/components/islands/SearchTypeahead.tsx";
19
20
  import MobileNav from "@/components/islands/MobileNav.tsx";
20
21
  import { withBase } from "@/lib/basePath";
@@ -23,6 +24,11 @@ interface Props {
23
24
  logo?: ChromeLogo;
24
25
  /** Fallback wordmark text when no logo image. */
25
26
  wordmark?: string;
27
+ /**
28
+ * Home/brand link target (lib/chrome/model.ts ChromeBrand.href). Run through
29
+ * `withBase` same as every other same-origin href. Omit to default to "/".
30
+ */
31
+ brandHref?: string;
26
32
  nav?: ChromeNavItem[];
27
33
  /** Search form action (GET). Omit to hide search. Mounts the live SearchTypeahead island. */
28
34
  searchAction?: string;
@@ -41,6 +47,15 @@ interface Props {
41
47
  cartCount?: number;
42
48
  /** A brand-fixed "dark" header bar (e.g. a navy bar with a light logo) vs. the default light bar. */
43
49
  variant?: "light" | "dark";
50
+ /**
51
+ * Content-shape variant (lib/chrome/model.ts HeaderVariant) — independent of
52
+ * `variant`'s light/dark TONE above. "minimal" (e.g. checkout/policy pages)
53
+ * renders brand + a reduced action set only: no search, no marketing CTAs,
54
+ * no saved-items affordance, no primary-nav bar, no mobile-menu toggle.
55
+ * Defaults to "primary" (the full header) so every existing caller is
56
+ * unaffected.
57
+ */
58
+ layout?: HeaderVariant;
44
59
  sticky?: boolean;
45
60
  class?: string;
46
61
  }
@@ -48,6 +63,7 @@ interface Props {
48
63
  const {
49
64
  logo,
50
65
  wordmark,
66
+ brandHref,
51
67
  nav = [],
52
68
  searchAction,
53
69
  announcement,
@@ -58,14 +74,16 @@ const {
58
74
  cartHref,
59
75
  cartCount = 0,
60
76
  variant = "light",
77
+ layout = "primary",
61
78
  sticky = true,
62
79
  class: className,
63
80
  } = Astro.props;
64
81
 
65
82
  const { basePath } = Astro.locals;
66
- const homeHref = withBase(basePath, "/");
83
+ const homeHref = withBase(basePath, brandHref ?? "/");
67
84
  const showLogo = !!logo && /^(https?:|\/)/.test(logo.src);
68
85
  const dark = variant === "dark";
86
+ const minimal = layout === "minimal";
69
87
 
70
88
  const ctaClass = (v?: string) =>
71
89
  v === "secondary" || v === "ghost"
@@ -86,6 +104,11 @@ const mobileNav = nav.map((item) => ({
86
104
  href: withBase(basePath, child.href),
87
105
  })),
88
106
  }));
107
+
108
+ // Current-page path, for aria-current="page" — threaded to the MobileNav
109
+ // island (which can't read Astro.url itself) so it marks the same link the
110
+ // server-rendered nav (NavDropdown, via Astro.url internally) marks.
111
+ const currentPath = Astro.url.pathname;
89
112
  ---
90
113
 
91
114
  <header class:list={[sticky && "sticky top-0 z-40", className]}>
@@ -133,7 +156,7 @@ const mobileNav = nav.map((item) => ({
133
156
 
134
157
  <!-- Search (live typeahead island) -->
135
158
  {
136
- searchAction && (
159
+ !minimal && searchAction && (
137
160
  <div class="hidden flex-1 md:flex">
138
161
  <SearchTypeahead client:idle action={searchAction} />
139
162
  </div>
@@ -143,7 +166,7 @@ const mobileNav = nav.map((item) => ({
143
166
  <!-- Actions: saved items + marketing CTAs + auth + account + cart (link only) + mobile menu -->
144
167
  <div class="ml-auto flex items-center gap-1">
145
168
  {
146
- savedItemsHref && (
169
+ !minimal && savedItemsHref && (
147
170
  <a
148
171
  href={withBase(basePath, savedItemsHref)}
149
172
  aria-label="Saved items"
@@ -161,7 +184,7 @@ const mobileNav = nav.map((item) => ({
161
184
  )
162
185
  }
163
186
  {
164
- headerCtas.length > 0 && (
187
+ !minimal && headerCtas.length > 0 && (
165
188
  <div class="hidden items-center gap-2 sm:flex">
166
189
  {headerCtas.map((cta) => (
167
190
  <a href={withBase(basePath, cta.href)} class={ctaClass(cta.variant)}>
@@ -220,9 +243,9 @@ const mobileNav = nav.map((item) => ({
220
243
 
221
244
  <!-- Mobile menu: the MobileNav island (focus-trap, scroll-lock, Escape-to-close) -->
222
245
  {
223
- nav.length > 0 && (
246
+ !minimal && nav.length > 0 && (
224
247
  <div class="md:hidden">
225
- <MobileNav client:idle nav={mobileNav} announcement={announcement} />
248
+ <MobileNav client:idle nav={mobileNav} announcement={announcement} currentPath={currentPath} />
226
249
  </div>
227
250
  )
228
251
  }
@@ -230,12 +253,25 @@ const mobileNav = nav.map((item) => ({
230
253
  </div>
231
254
  </div>
232
255
 
233
- <!-- Primary nav bar -->
256
+ <!-- Primary nav bar: desktop MegaMenu (hidden md:block) + a no-JS-safe mobile
257
+ fallback (md:hidden) below it. The fallback renders the SAME nav data as
258
+ real, crawlable <a href> links via a native <details>/<summary> accordion
259
+ (NavDropdown mode="accordion") — it works with zero JavaScript, since it
260
+ is below the `md` breakpoint the ONLY nav affordance a no-JS visitor has
261
+ (the MobileNav drawer above is a client:idle island and renders nothing
262
+ without JS). Once MobileNav hydrates it hides this fallback (see its
263
+ [data-mobile-nav-fallback] effect) so JS-enabled visitors get the drawer
264
+ as the primary/enhanced experience; this is additive, not a replacement. -->
234
265
  {
235
- nav.length > 0 && (
266
+ !minimal && nav.length > 0 && (
236
267
  <div class="border-b border-border bg-surface">
237
268
  <div class="container-prose">
238
269
  <MegaMenu items={nav} />
270
+ <nav aria-label="Mobile" data-mobile-nav-fallback class="md:hidden">
271
+ <ul class="grid">
272
+ {nav.map((item) => <NavDropdown item={item} mode="accordion" />)}
273
+ </ul>
274
+ </nav>
239
275
  </div>
240
276
  </div>
241
277
  )
@@ -4,7 +4,7 @@
4
4
  * require an of-age adult to sign; the wording is fixed here and the age
5
5
  * reflects tenant.compliance.minAge (defaults to 21). Renders NOTHING when
6
6
  * `enabled` is false. Driven by tenant.compliance.adultSignature — never hand-
7
- * authored on a marketing page (decision compliance-widgets-platform-owned).
7
+ * authored on a marketing page.
8
8
  */
9
9
  import ComplianceNotice from "./ComplianceNotice.astro";
10
10
 
@@ -5,8 +5,7 @@
5
5
  * FDA NicotineWarning follows). It owns ONLY the chrome: the fixed legal text
6
6
  * is supplied by each sibling widget via <slot />, and every sibling renders it
7
7
  * ONLY when its tenant.compliance data is present. This component adds no copy
8
- * of its own — it never fabricates legal wording (decision
9
- * compliance-widgets-platform-owned).
8
+ * of its own — it never fabricates legal wording.
10
9
  *
11
10
  * Variants — one visual treatment per placement, token-styled so they re-skin
12
11
  * with the theme and never carry a hardcoded brand color:
@@ -3,8 +3,7 @@
3
3
  * Excise-tax DISPLAY cue. Presentational only — it surfaces that applicable
4
4
  * excise taxes apply (and, optionally, a per-jurisdiction breakdown for shopper
5
5
  * transparency); it NEVER computes tax. The actual excise-tax CALCULATION is
6
- * product/commerce-owned (decision compliance-widgets-platform-owned). Renders
7
- * NOTHING when no note is supplied.
6
+ * product/commerce-owned. Renders NOTHING when no note is supplied.
8
7
  *
9
8
  * The optional jurisdiction breakdown is rendered as inline "label detail"
10
9
  * spans (valid inside the primitive's paragraph) so it re-skins with the theme
@@ -6,7 +6,7 @@
6
6
  * Postal Service carriage; only whether it renders (and the age) comes from
7
7
  * tenant.compliance.pactAct / .minAge. Renders NOTHING when `enabled` is false,
8
8
  * so absent data never shows placeholder legal text. Never hand-author this on a
9
- * marketing page (decision compliance-widgets-platform-owned).
9
+ * marketing page.
10
10
  */
11
11
  import ComplianceNotice from "./ComplianceNotice.astro";
12
12
 
@@ -5,8 +5,7 @@
5
5
  * ("WARNING: … known to the State of California to cause …"), the ⚠ symbol, and
6
6
  * the official warnings URL. Only the variable slots — harm endpoint(s) and the
7
7
  * chemical name(s) named in the long form — come from tenant.compliance.prop65.
8
- * Renders NOTHING when the config is absent, so it never fabricates a warning
9
- * (decision compliance-widgets-platform-owned).
8
+ * Renders NOTHING when the config is absent, so it never fabricates a warning.
10
9
  *
11
10
  * `true` → short cancer + reproductive-harm warning. A Prop65Config selects the
12
11
  * harm endpoint(s); providing `chemicals` switches to the long form.
@@ -3,9 +3,8 @@
3
3
  * Purchase-quantity-limit DISPLAY. Surfaces a shopper-facing limit (e.g. state
4
4
  * or PACT-Act order caps). Display surface ONLY — the limit text comes from
5
5
  * tenant.compliance.purchaseLimit and the actual enforcement stays in the
6
- * product/commerce layer, never hand-built here (decision
7
- * compliance-widgets-platform-owned). Renders NOTHING when the config (or its
8
- * text) is absent.
6
+ * product/commerce layer, never hand-built here. Renders NOTHING when the
7
+ * config (or its text) is absent.
9
8
  */
10
9
  import type { PurchaseLimitConfig } from "@tot/public-runtime";
11
10
  import ComplianceNotice from "./ComplianceNotice.astro";
@@ -3,8 +3,8 @@
3
3
  * Shipping-restriction DISPLAY notice (e.g. PACT Act no-ship states, adult
4
4
  * signature, carrier limits). Presentational only — it renders the tenant's
5
5
  * configured `compliance.shippingRestriction` text; the actual ship/no-ship
6
- * DECISION for an address stays in the product/commerce layer (decision
7
- * compliance-widgets-platform-owned). Renders NOTHING when `text` is absent.
6
+ * DECISION for an address stays in the product/commerce layer. Renders
7
+ * NOTHING when `text` is absent.
8
8
  *
9
9
  * This is the platform component behind the footer shipping band the app Layout
10
10
  * previously inlined — same data, same footer placement, now token-styled via
@@ -3,9 +3,8 @@
3
3
  * State/jurisdiction sale-eligibility DISPLAY notice. Presentational only — it
4
4
  * SHOWS the tenant-configured eligibility messaging (which states a product is /
5
5
  * isn't available in); it does NOT decide whether a given cart/address is
6
- * eligible — that computation stays product-owned (decision
7
- * compliance-widgets-platform-owned). Renders NOTHING when no eligibility data
8
- * is supplied, so absent config never shows a placeholder.
6
+ * eligible — that computation stays product-owned. Renders NOTHING when no
7
+ * eligibility data is supplied, so absent config never shows a placeholder.
9
8
  *
10
9
  * Message precedence: an explicit `note` wins; otherwise a sentence is composed
11
10
  * from `restrictedStates` ("Not available for sale in: …") or, failing that,
@@ -1,11 +1,11 @@
1
1
  ---
2
2
  /**
3
3
  * <TrustStrip> — the quiet row of proof tokens a store repeats across pages
4
- * ("Ships from USA", "Lab-tested", a rating). REAL-OR-ABSENT by construction
5
- * (f09): sanitizeTrustStrip() drops any claim that is scraped review-widget
6
- * markup and any rating that isn't a real positive aggregate, reusing f09's
4
+ * ("Ships from USA", "Lab-tested", a rating). REAL-OR-ABSENT by construction:
5
+ * sanitizeTrustStrip() drops any claim that is scraped review-widget
6
+ * markup and any rating that isn't a real positive aggregate, reusing the same
7
7
  * detector. If nothing real is left the strip renders NOTHING — never a
8
- * fabricated badge. Plain text + one rating glyph; styled from f06 tokens.
8
+ * fabricated badge. Plain text + one rating glyph; styled from design tokens.
9
9
  * data-widget="trust-strip".
10
10
  */
11
11
  import type { TrustStripItem } from "@/lib/content/trustStrip";
@@ -11,6 +11,7 @@
11
11
  * prefers-reduced-motion.
12
12
  */
13
13
  import { useCallback, useEffect, useRef, useState } from "preact/hooks";
14
+ import { isCurrentNavHref } from "@/lib/chrome/navCurrent";
14
15
 
15
16
  interface NavChild {
16
17
  label: string;
@@ -25,9 +26,13 @@ interface NavItem {
25
26
  interface Props {
26
27
  nav: NavItem[];
27
28
  announcement?: string;
29
+ /** Current request path (Astro.url.pathname), for aria-current="page" — see navCurrent.ts. */
30
+ currentPath?: string;
28
31
  }
29
32
 
30
33
  const DRAWER_ID = "mobile-drawer";
34
+ /** Matches SiteHeader.astro's no-JS fallback <nav data-mobile-nav-fallback>. */
35
+ const FALLBACK_NAV_SELECTOR = "[data-mobile-nav-fallback]";
31
36
 
32
37
  function prefersReducedMotion(): boolean {
33
38
  return (
@@ -37,7 +42,7 @@ function prefersReducedMotion(): boolean {
37
42
  );
38
43
  }
39
44
 
40
- export default function MobileNav({ nav, announcement }: Props) {
45
+ export default function MobileNav({ nav, announcement, currentPath }: Props) {
41
46
  const [open, setOpen] = useState(false);
42
47
  const buttonRef = useRef<HTMLButtonElement>(null);
43
48
  const panelRef = useRef<HTMLDivElement>(null);
@@ -45,6 +50,17 @@ export default function MobileNav({ nav, announcement }: Props) {
45
50
 
46
51
  const close = useCallback(() => setOpen(false), []);
47
52
 
53
+ // The no-JS fallback nav is only needed when JS never runs. Since this
54
+ // component mounting IS JS running, hide it in favor of the drawer above —
55
+ // additive progressive enhancement, not a replacement of either layer.
56
+ useEffect(() => {
57
+ document
58
+ .querySelectorAll<HTMLElement>(FALLBACK_NAV_SELECTOR)
59
+ .forEach((el) => {
60
+ el.hidden = true;
61
+ });
62
+ }, []);
63
+
48
64
  // Body scroll lock while open.
49
65
  useEffect(() => {
50
66
  if (!open) return;
@@ -202,6 +218,9 @@ export default function MobileNav({ nav, announcement }: Props) {
202
218
  <div class="px-3 py-2">
203
219
  <a
204
220
  href={item.href}
221
+ aria-current={
222
+ currentPath && isCurrentNavHref(currentPath, item.href) ? "page" : undefined
223
+ }
205
224
  class="block font-display text-base font-medium text-text hover:text-primary"
206
225
  >
207
226
  {item.label}
@@ -211,6 +230,9 @@ export default function MobileNav({ nav, announcement }: Props) {
211
230
  <li key={child.href}>
212
231
  <a
213
232
  href={child.href}
233
+ aria-current={
234
+ currentPath && isCurrentNavHref(currentPath, child.href) ? "page" : undefined
235
+ }
214
236
  class="block rounded px-2 py-1.5 font-body text-sm text-muted hover:bg-border/40 hover:text-text"
215
237
  >
216
238
  {child.label}
@@ -222,6 +244,9 @@ export default function MobileNav({ nav, announcement }: Props) {
222
244
  ) : (
223
245
  <a
224
246
  href={item.href}
247
+ aria-current={
248
+ currentPath && isCurrentNavHref(currentPath, item.href) ? "page" : undefined
249
+ }
225
250
  class="block rounded px-3 py-2.5 font-display text-base font-medium text-text hover:bg-border/40 hover:text-primary"
226
251
  >
227
252
  {item.label}
@@ -27,6 +27,7 @@ import type {
27
27
  } from "@tot/public-runtime";
28
28
  import { priceForPurchaseOption } from "@tot/public-runtime";
29
29
  import { formatPrice } from "@/lib/format";
30
+ import { useCheckoutHandoffCorrelation } from "@/components/islands/useCheckoutHandoffCorrelation";
30
31
 
31
32
  interface OptionDef {
32
33
  name: string;
@@ -55,7 +56,7 @@ interface Props {
55
56
  byVariantId: Record<string, Record<string, VariantCartPayload>>;
56
57
  };
57
58
  /**
58
- * Membership pricing (F10). Present only when an eligible member is resolved
59
+ * Membership pricing. Present only when an eligible member is resolved
59
60
  * for this request (Phase 2 shopper login). Undefined → list pricing only,
60
61
  * which is the Phase-1 anonymous behavior.
61
62
  */
@@ -63,6 +64,13 @@ interface Props {
63
64
  membershipRule: MembershipTierRule;
64
65
  stacking: MembershipStackingPolicy;
65
66
  };
67
+ /**
68
+ * Base-path-aware URL of the checkout-correlation mint endpoint
69
+ * (ext-commerce-10). When a store is wired the island posts the shopper's live
70
+ * consent snapshot here and, if the server attributes, carries the returned
71
+ * passthrough field on the managed-cart form. Undefined → no correlation.
72
+ */
73
+ handoffUrl?: string;
66
74
  }
67
75
 
68
76
  const LOW_STOCK_THRESHOLD = 5;
@@ -89,7 +97,14 @@ export default function VariantSelector({
89
97
  productTitle,
90
98
  checkoutCart,
91
99
  pricing,
100
+ handoffUrl,
92
101
  }: Props) {
102
+ // Checkout-correlation handoff mint (ext-commerce-10): resolved once the server
103
+ // attributes this shopper (analytics consent granted + signing secret present),
104
+ // else null → the buy form carries no correlation and the order is unattributed.
105
+ const correlationField = useCheckoutHandoffCorrelation(
106
+ checkoutCart ? handoffUrl : undefined,
107
+ );
93
108
  const sortedOptions = useMemo(
94
109
  () => [...options].sort((a, b) => a.position - b.position),
95
110
  [options],
@@ -219,7 +234,7 @@ export default function VariantSelector({
219
234
  }
220
235
  }
221
236
 
222
- // Compose member + subscription pricing (F10). The pricing context carries the
237
+ // Compose member + subscription pricing. The pricing context carries the
223
238
  // resolved member tier only when an eligible member was resolved server-side;
224
239
  // otherwise it is a non-member and list pricing stands.
225
240
  const pricingCtx = pricing
@@ -474,6 +489,16 @@ export default function VariantSelector({
474
489
  value={String(quantity)}
475
490
  />
476
491
  )}
492
+ {/* Checkout-correlation passthrough field (ext-commerce-10). Rides the
493
+ handoff as a never-signed hidden field the provider echoes back on
494
+ the order webhook; absent when unattributed. */}
495
+ {canBuy && correlationField && (
496
+ <input
497
+ type="hidden"
498
+ name={correlationField.name}
499
+ value={correlationField.value}
500
+ />
501
+ )}
477
502
  <button
478
503
  type="submit"
479
504
  disabled={!canBuy}
@@ -0,0 +1,75 @@
1
+ /**
2
+ * Client hook for the checkout-correlation handoff mint (ext-commerce-10).
3
+ *
4
+ * The shopper's analytics consent is a live CLIENT-side value, but the correlation
5
+ * token is minted under the SERVER-only signing secret. So at the handoff this hook
6
+ * posts the current consent snapshot (the renderer-owned `__TOT_INTEGRATION_CONSENT__`
7
+ * global) to `POST /api/commerce/checkout-handoff` and, when the server attributes,
8
+ * returns the opaque passthrough cart field for the managed-cart form to carry.
9
+ *
10
+ * Fail-safe by construction: no handoff URL, a network error, or an unattributed
11
+ * response yields `null` — the buy form renders and submits with NO correlation field,
12
+ * so the order is simply unattributed and checkout is never blocked.
13
+ */
14
+ import { useEffect, useState } from "preact/hooks";
15
+
16
+ /** The renderer-owned frozen consent-snapshot global (`render-phases.ts`). */
17
+ const INTEGRATION_CONSENT_GLOBAL = "__TOT_INTEGRATION_CONSENT__";
18
+
19
+ /** The opaque passthrough cart field to attach to the managed-cart form. */
20
+ export interface HandoffCartField {
21
+ name: string;
22
+ value: string;
23
+ }
24
+
25
+ /**
26
+ * Read the `{ attributed, cartField }` handoff response fail-closed into a cart field,
27
+ * or `null` for any non-attributed / malformed shape. Pure — unit-tested directly.
28
+ */
29
+ export function parseHandoffCartField(data: unknown): HandoffCartField | null {
30
+ if (!data || typeof data !== "object") return null;
31
+ const d = data as Record<string, unknown>;
32
+ if (d.attributed !== true) return null;
33
+ const field = d.cartField;
34
+ if (!field || typeof field !== "object") return null;
35
+ const { name, value } = field as Record<string, unknown>;
36
+ return typeof name === "string" && name.length > 0 && typeof value === "string" && value.length > 0
37
+ ? { name, value }
38
+ : null;
39
+ }
40
+
41
+ /** The live consent snapshot the renderer inlined + the CMP/native banner updates. */
42
+ function readClientConsent(): unknown {
43
+ return (globalThis as Record<string, unknown>)[INTEGRATION_CONSENT_GLOBAL] ?? null;
44
+ }
45
+
46
+ /**
47
+ * Fetch the handoff correlation for this page. Returns the cart field to attach once
48
+ * the server attributes, else `null`. `handoffUrl` absent (no store / marketing
49
+ * tenant) is a no-op.
50
+ */
51
+ export function useCheckoutHandoffCorrelation(handoffUrl?: string): HandoffCartField | null {
52
+ const [field, setField] = useState<HandoffCartField | null>(null);
53
+
54
+ useEffect(() => {
55
+ if (!handoffUrl) return;
56
+ let cancelled = false;
57
+ fetch(handoffUrl, {
58
+ method: "POST",
59
+ headers: { "content-type": "application/json" },
60
+ body: JSON.stringify({ consent: readClientConsent() }),
61
+ })
62
+ .then((res) => res.json())
63
+ .then((data) => {
64
+ if (!cancelled) setField(parseHandoffCartField(data));
65
+ })
66
+ .catch(() => {
67
+ // Fail-safe: leave the order unattributed; never disturb the buy form.
68
+ });
69
+ return () => {
70
+ cancelled = true;
71
+ };
72
+ }, [handoffUrl]);
73
+
74
+ return field;
75
+ }
@@ -4,8 +4,7 @@
4
4
  * offer" affordance shared by <TierCards>, <TierComparisonTable>, and the
5
5
  * subscription <SavingsExplainer>. When a group shows illustrative figures,
6
6
  * the widgets render this so an example price / savings number is never
7
- * mistaken for a live offer (decision compliance-widgets-platform-owned
8
- * education only, no pricing mechanics).
7
+ * mistaken for a live offer education only, no pricing mechanics.
9
8
  *
10
9
  * Two forms:
11
10
  * - "badge" a compact pill placed at the group header (the visible marker)
@@ -6,8 +6,8 @@
6
6
  * checkout, or subscription mechanics — each tier's CTA is a plain handoff link
7
7
  * into the tenant's own (product-owned) subscribe/learn flow.
8
8
  *
9
- * Illustrative-figures affordance (decision compliance-widgets-platform-owned):
10
- * a tier's figure is treated as an EXAMPLE by default (isIllustrative). When any
9
+ * Illustrative-figures affordance: a tier's figure is treated as an EXAMPLE
10
+ * by default (isIllustrative). When any
11
11
  * shown tier is illustrative the group header carries a first-class
12
12
  * <IllustrativeLabel> badge, each such figure is prefixed with an "e.g."
13
13
  * affordance + data-illustrative hook, and a footnote spells it out — so an