@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
@@ -3,8 +3,8 @@
3
3
  *
4
4
  * Used by FixtureToTClient.search (no D1 binding): typo-tolerant, relevance-ranked
5
5
  * search over the bundled catalog snapshot, zero infra. When a D1 binding is wired
6
- * (D1ToTClient), search instead goes through FTS5 — see lib/d1/catalog.ts and
7
- * epics/edge-read-replica.md. This module is the portable floor an extracted
6
+ * (D1ToTClient), search instead goes through FTS5 — see lib/d1/catalog.ts.
7
+ * This module is the portable floor an extracted
8
8
  * merchant gets out of the box (docs/extraction-runbook.md).
9
9
  */
10
10
  import Fuse, { type IFuseOptions } from "fuse.js";
@@ -0,0 +1,42 @@
1
+ /**
2
+ * hreflang alternates for a translated article. Two pure helpers so the
3
+ * article route and <Seo> stay thin and the "singleton emits nothing" guard is
4
+ * unit-testable without an Astro render harness.
5
+ */
6
+ export interface Alternate {
7
+ hreflang: string;
8
+ href: string;
9
+ }
10
+
11
+ /**
12
+ * Build the per-variant alternate set for an article's translation group. Returns
13
+ * `[]` for a singleton (≤1 variant) so a non-translated post emits no `hreflang`
14
+ * links and renders byte-identically to today. Each variant maps to its own
15
+ * localized canonical via `hrefForSlug`; `inLanguage` is the per-variant language
16
+ * key (defaulting "en", matching the field's repo-wide default).
17
+ */
18
+ export function buildAlternates(
19
+ variants: { slug: string; inLanguage?: string }[],
20
+ hrefForSlug: (slug: string) => string,
21
+ ): Alternate[] {
22
+ if (variants.length <= 1) return [];
23
+ return variants.map((v) => ({
24
+ hreflang: v.inLanguage ?? "en",
25
+ href: hrefForSlug(v.slug),
26
+ }));
27
+ }
28
+
29
+ /**
30
+ * Append the `x-default` entry to a per-variant alternate set. It points at the
31
+ * English variant (`en` or an `en-*` region variant); with no English variant it
32
+ * falls back to the first entry — a deterministic primary rather than none, so
33
+ * search engines always have a language-agnostic default. Returns `[]` unchanged
34
+ * for an empty input (nothing to default to).
35
+ */
36
+ export function withXDefault(alternates: Alternate[]): Alternate[] {
37
+ if (alternates.length === 0) return [];
38
+ const primary =
39
+ alternates.find((a) => a.hreflang === "en" || a.hreflang.startsWith("en-")) ??
40
+ alternates[0]!;
41
+ return [...alternates, { hreflang: "x-default", href: primary.href }];
42
+ }
@@ -0,0 +1,65 @@
1
+ /**
2
+ * Pure builders for the extra <meta> tags `<Seo>` emits on article pages
3
+ * (B1.6). Kept as a pure function so the "no article meta leaks onto
4
+ * website/product pages" invariant is unit-testable without rendering Astro.
5
+ */
6
+
7
+ /** Open Graph `article:*` fields — only meaningful when `ogType === "article"`. */
8
+ export interface ArticleMeta {
9
+ publishedTime?: string;
10
+ modifiedTime?: string;
11
+ /** Author name or author-page URL → article:author. */
12
+ author?: string;
13
+ /** The category/pillar → article:section. */
14
+ section?: string;
15
+ /** One <meta property="article:tag"> per entry. */
16
+ tags?: string[];
17
+ }
18
+
19
+ /** A meta tag is keyed by either `property` (OG) or `name` (Twitter/std). */
20
+ export interface MetaTag {
21
+ property?: string;
22
+ name?: string;
23
+ content: string;
24
+ }
25
+
26
+ export interface ArticleSeoInput {
27
+ ogType?: "website" | "product" | "article";
28
+ article?: ArticleMeta;
29
+ /** og:image:alt — the accessible description of the OG image. */
30
+ imageAlt?: string;
31
+ imageWidth?: number;
32
+ imageHeight?: number;
33
+ twitterSite?: string;
34
+ twitterCreator?: string;
35
+ }
36
+
37
+ /**
38
+ * The article-only OG/Twitter meta tags. Returns `[]` for any non-article page
39
+ * so `article:*`, `og:image:alt`, and `twitter:site`/`creator` can never leak
40
+ * onto website/product pages (guarded here, asserted in meta.test.ts).
41
+ */
42
+ export function articleSeoMetaTags(input: ArticleSeoInput): MetaTag[] {
43
+ if (input.ogType !== "article") return [];
44
+ const tags: MetaTag[] = [];
45
+ const a = input.article;
46
+ if (a?.publishedTime)
47
+ tags.push({ property: "article:published_time", content: a.publishedTime });
48
+ if (a?.modifiedTime)
49
+ tags.push({ property: "article:modified_time", content: a.modifiedTime });
50
+ if (a?.author) tags.push({ property: "article:author", content: a.author });
51
+ if (a?.section) tags.push({ property: "article:section", content: a.section });
52
+ for (const tag of a?.tags ?? [])
53
+ tags.push({ property: "article:tag", content: tag });
54
+ if (input.imageAlt)
55
+ tags.push({ property: "og:image:alt", content: input.imageAlt });
56
+ if (input.imageWidth)
57
+ tags.push({ property: "og:image:width", content: String(input.imageWidth) });
58
+ if (input.imageHeight)
59
+ tags.push({ property: "og:image:height", content: String(input.imageHeight) });
60
+ if (input.twitterSite)
61
+ tags.push({ name: "twitter:site", content: input.twitterSite });
62
+ if (input.twitterCreator)
63
+ tags.push({ name: "twitter:creator", content: input.twitterCreator });
64
+ return tags;
65
+ }
@@ -0,0 +1,212 @@
1
+ /**
2
+ * S1 — the social go-live hook + idempotency.
3
+ *
4
+ * WHERE IT FIRES: at the true go-live edge — after `AggregateShipOrchestrator`
5
+ * flips a tenant's `live` channel to a freshly-shipped version and records the
6
+ * release ledger. NOT on `POST /api/admin/blog`'s `publish` (which only opens a
7
+ * candidate PR a human still promotes) and NOT on accept-to-preview
8
+ * (`/api/changes/accept` → `integrate`, which only advances the `preview`
9
+ * aggregate). Go-live is where a post's file actually becomes public, so it is the
10
+ * only truthful "it went live" edge.
11
+ *
12
+ * WHAT IT DETECTS: a blog post that transitioned INTO a live-visible status — was
13
+ * absent or not-live in the version live before this ship (`fromVersionId`), and
14
+ * is live-visible in the version just promoted (`toVersionId`). Visibility is the
15
+ * ONE chokepoint `isVisibleOnLive` (published / unlisted / due-scheduled), reused
16
+ * so this never drifts from the render path.
17
+ *
18
+ * DISCIPLINE (mirrors `webhooks/emit.ts`): BEST-EFFORT and fire-and-forget — every
19
+ * failure is logged and swallowed. A social outage must NEVER fail or roll back a
20
+ * completed go-live.
21
+ *
22
+ * IDEMPOTENCY: a check-and-set posted-state key per `(tenant, collection, slug,
23
+ * network)` fires each post AT MOST ONCE, so a re-ship / re-run never re-posts. The
24
+ * transition guard also marks already-live posts as "primed" (key set, not fired)
25
+ * so the first ship after this hook deploys does NOT retro-announce the tenant's
26
+ * whole back-catalog.
27
+ */
28
+ import { isVisibleOnLive } from "../blog/collection.js";
29
+ import type { BlogPost, PostStatus } from "../blog/types.js";
30
+ import { notifyPostPublished } from "./notify.js";
31
+
32
+ /**
33
+ * The minimal version-read surface this hook needs — a structural subset of the
34
+ * control-plane `SiteVersionStore`. Declared locally (not imported) so `lib/social`
35
+ * stays inside the source-available renderer carve and never edges onto
36
+ * `@tot/private-controlplane`; the caller (the control-plane-cluster orchestrator)
37
+ * passes its real store in.
38
+ */
39
+ export interface VersionReadStore {
40
+ getSiteVersion(
41
+ tenantId: string,
42
+ versionId: string,
43
+ ): Promise<{ artifacts: Record<string, string> } | null>;
44
+ getVersionContent(
45
+ tenantId: string,
46
+ path: string,
47
+ versionId: string,
48
+ ): Promise<string | Uint8Array | null>;
49
+ }
50
+
51
+ /**
52
+ * The v1 idempotency network token — a single whole-post gate. S3/S4 replace this
53
+ * with the real per-network id (`x` / `linkedin` / …) and iterate networks; the key
54
+ * shape is ALREADY per-network so that extension needs no rework here.
55
+ */
56
+ export const SOCIAL_NETWORK = "social";
57
+
58
+ /** The posted-once key for one post on one network. */
59
+ export function socialPostedKey(
60
+ tenantId: string,
61
+ collection: string,
62
+ slug: string,
63
+ network: string,
64
+ ): string {
65
+ return `social:posted:${tenantId}:${collection}:${slug}:${network}`;
66
+ }
67
+
68
+ /** The minimal KV surface the idempotency store needs (TENANT_CACHE in prod). */
69
+ export interface SocialIdempotencyKv {
70
+ get(key: string): Promise<string | null>;
71
+ put(key: string, value: string): Promise<void>;
72
+ }
73
+
74
+ // `.../content/<collection>/posts/<slug>.json` — capture collection + slug from a
75
+ // version-manifest artifact path (robust to the leading `tenants/<id>/` prefix).
76
+ const POST_PATH_RE = /(?:^|\/)content\/([a-z0-9][a-z0-9._-]*)\/posts\/([a-z0-9][a-z0-9-]*)\.json$/;
77
+
78
+ function logDiag(fields: Record<string, unknown>): void {
79
+ try {
80
+ console.warn("[diag:social:golive]", JSON.stringify(fields));
81
+ } catch {
82
+ /* diagnostics must never break the caller */
83
+ }
84
+ }
85
+
86
+ // Legacy/seeded post JSON may omit `status`; default it to "published" on load, the
87
+ // same normalization `blog/collection.ts` applies so visibility matches the render path.
88
+ function normalizeStatus(raw: unknown): PostStatus {
89
+ return raw === "draft" || raw === "unlisted" || raw === "published" || raw === "scheduled"
90
+ ? raw
91
+ : "published";
92
+ }
93
+
94
+ // `contentVersionId` is the artifact's content hash (`manifest.artifacts[path]`) —
95
+ // the version store keys blob content by that hash, not by the site version id.
96
+ async function readPost(
97
+ store: VersionReadStore,
98
+ tenantId: string,
99
+ artifactPath: string,
100
+ contentVersionId: string,
101
+ ): Promise<BlogPost | null> {
102
+ const raw = await store.getVersionContent(tenantId, artifactPath, contentVersionId);
103
+ if (typeof raw !== "string") return null;
104
+ let parsed: unknown;
105
+ try {
106
+ parsed = JSON.parse(raw);
107
+ } catch {
108
+ return null;
109
+ }
110
+ if (parsed === null || typeof parsed !== "object") return null;
111
+ const post = parsed as BlogPost;
112
+ return { ...post, status: normalizeStatus((post as { status?: unknown }).status) };
113
+ }
114
+
115
+ export interface NotifyPostsWentLiveDeps {
116
+ /** The version store the shipped tenant publishes to (a control-plane `SiteVersionStore`). */
117
+ store: VersionReadStore;
118
+ /** Idempotency store — TENANT_CACHE in prod. */
119
+ kv: SocialIdempotencyKv;
120
+ now?: () => string;
121
+ /** Override the publish seam (tests + S3/S4 injection). Defaults to {@link notifyPostPublished}. */
122
+ notify?: (tenantId: string, collection: string, slug: string, post: BlogPost) => Promise<void>;
123
+ }
124
+
125
+ export interface NotifyPostsWentLiveInput {
126
+ tenantId: string;
127
+ /** The version just promoted to `live`. */
128
+ toVersionId: string;
129
+ /** The version live immediately BEFORE this ship, or null on the first-ever ship. */
130
+ fromVersionId: string | null;
131
+ }
132
+
133
+ /**
134
+ * For each blog post that went live in `toVersionId`, fire {@link notifyPostPublished}
135
+ * at most once. NEVER throws — the whole body is guarded so a completed go-live is
136
+ * never failed or rolled back by this hook.
137
+ */
138
+ export async function notifyPostsWentLive(
139
+ deps: NotifyPostsWentLiveDeps,
140
+ input: NotifyPostsWentLiveInput,
141
+ ): Promise<void> {
142
+ try {
143
+ const { store, kv } = deps;
144
+ const now = (deps.now ?? (() => new Date().toISOString()))();
145
+ const notify = deps.notify ?? notifyPostPublished;
146
+
147
+ const toManifest = await store.getSiteVersion(input.tenantId, input.toVersionId);
148
+ if (!toManifest) return;
149
+ const fromManifest = input.fromVersionId
150
+ ? await store.getSiteVersion(input.tenantId, input.fromVersionId)
151
+ : null;
152
+
153
+ for (const [artifactPath, contentHash] of Object.entries(toManifest.artifacts)) {
154
+ const match = POST_PATH_RE.exec(artifactPath);
155
+ if (!match) continue;
156
+ const collection = match[1]!;
157
+ const slug = match[2]!;
158
+ const key = socialPostedKey(input.tenantId, collection, slug, SOCIAL_NETWORK);
159
+
160
+ // Already posted (or primed) — skip before any content read (cheap steady state).
161
+ let already: string | null = null;
162
+ try {
163
+ already = await kv.get(key);
164
+ } catch {
165
+ already = null;
166
+ }
167
+ if (already) continue;
168
+
169
+ const post = await readPost(store, input.tenantId, artifactPath, contentHash);
170
+ // Not live now (draft / not-yet-due scheduled) — do NOT set the key, so it is
171
+ // re-checked on the ship that eventually takes it live.
172
+ if (!post || !isVisibleOnLive(post, now)) continue;
173
+
174
+ // Transition guard: if the post was ALREADY live in the prior version, this ship
175
+ // did not take it live — PRIME the key (so we never re-read it) but do NOT notify.
176
+ const prevHash = fromManifest?.artifacts[artifactPath];
177
+ if (prevHash) {
178
+ const prev = await readPost(store, input.tenantId, artifactPath, prevHash);
179
+ if (prev && isVisibleOnLive(prev, now)) {
180
+ try {
181
+ await kv.put(key, now);
182
+ } catch {
183
+ /* best-effort prime */
184
+ }
185
+ continue;
186
+ }
187
+ }
188
+
189
+ // Genuine went-live transition — fire once, THEN set the posted key. If notify
190
+ // throws, the key is left unset so the next ship retries (at-least-check, at-most-post).
191
+ try {
192
+ await notify(input.tenantId, collection, slug, post);
193
+ await kv.put(key, now);
194
+ } catch (err) {
195
+ logDiag({
196
+ event: "notify_failed",
197
+ tenantId: input.tenantId,
198
+ collection,
199
+ slug,
200
+ error: (err as Error).message,
201
+ });
202
+ }
203
+ }
204
+ } catch (err) {
205
+ logDiag({
206
+ event: "hook_failed",
207
+ tenantId: input.tenantId,
208
+ toVersionId: input.toVersionId,
209
+ error: (err as Error).message,
210
+ });
211
+ }
212
+ }
@@ -0,0 +1,32 @@
1
+ import type { BlogPost } from "../blog/types.js";
2
+
3
+ /** Swallow-and-log a soft failure — logging itself must never throw (mirrors `webhooks/emit.ts`). */
4
+ function logDiag(fields: Record<string, unknown>): void {
5
+ try {
6
+ console.warn("[diag:social:notify]", JSON.stringify(fields));
7
+ } catch {
8
+ /* diagnostics must never break the caller */
9
+ }
10
+ }
11
+
12
+ /**
13
+ * The seam — the single call the go-live hook makes when a blog post
14
+ * transitions INTO a live-visible state (see {@link notifyPostsWentLive}). This
15
+ * ships the HOOK + the at-most-once idempotency key, NOT the network
16
+ * last-mile: v1 is a no-op announcement (logs only).
17
+ *
18
+ * A `SocialPublisher` interface + X adapter, and per-`(tenant,network)`
19
+ * config + encrypted token store, replace THIS body to compose a per-network
20
+ * payload — title, canonical URL, excerpt, featured image, `categories[0]` →
21
+ * hashtags — and POST it to each configured network's publish API. The signature
22
+ * is deliberately per-POST; per-network fan-out and the per-network idempotency
23
+ * key live one layer up in the hook, so extending to X + LinkedIn is additive.
24
+ */
25
+ export async function notifyPostPublished(
26
+ tenantId: string,
27
+ collection: string,
28
+ slug: string,
29
+ post: BlogPost,
30
+ ): Promise<void> {
31
+ logDiag({ event: "post_published", tenantId, collection, slug, title: post.title });
32
+ }
@@ -0,0 +1,171 @@
1
+ /**
2
+ * Encrypted per-`(tenant, network)` outbound social OAuth token store
3
+ * (social-autopost epic, S2). The one RETRIEVABLE secret store in the
4
+ * storefront: unlike `app_credentials` (one-way `sha256Hex` for INBOUND auth),
5
+ * these are access/refresh tokens we must REPLAY to X/LinkedIn, so they are
6
+ * encrypted-at-rest and decrypted at post time — never hashed.
7
+ *
8
+ * Envelope encryption, Workers-native Web Crypto `AES-GCM` (no Node `crypto`):
9
+ * - The data-encrypting step runs per write with a fresh random 96-bit IV.
10
+ * - The key-encrypting key (KEK) is a Worker env binding (`SOCIAL_TOKEN_KEK`,
11
+ * a base64-encoded 256-bit key), imported to a non-extractable `CryptoKey`.
12
+ * It is NEVER a column — mirroring how every other real secret here is an
13
+ * env binding, not row data (see the `social_tokens` note in schema-apps.ts).
14
+ *
15
+ * PURE over injected seams (a `Queryable` for D1, a `CryptoKey` for the KEK) so
16
+ * the same code serves the real Worker bindings and an in-memory test — the
17
+ * activity/store.ts convention. `resolveSocialTokenStore()` is the composition
18
+ * root that wires the real bindings (mirrors `getDispatcher()`); it returns
19
+ * `null` when either binding is absent so callers degrade rather than throw.
20
+ */
21
+ import type { Queryable } from "../d1/catalog.js";
22
+ import { readD1, readEnv } from "../env.js";
23
+
24
+ /** The decrypted token material we persist as one JSON blob under AES-GCM. */
25
+ export interface SocialTokenPayload {
26
+ accessToken: string;
27
+ /** OAuth refresh token, when the network issues one (X app-passwords don't). */
28
+ refreshToken?: string;
29
+ /** Token expiry, ISO-8601. Absent for long-lived manual creds. */
30
+ expiresAt?: string;
31
+ }
32
+
33
+ /** Injected seams a store operation runs over. */
34
+ export interface SocialTokenStoreDeps {
35
+ db: Queryable;
36
+ /** AES-GCM key imported from the `SOCIAL_TOKEN_KEK` binding via {@link importKek}. */
37
+ kek: CryptoKey;
38
+ /** Clock seam (ISO-8601), overridable in tests. Defaults to `Date.now`. */
39
+ now?: () => string;
40
+ }
41
+
42
+ const IV_BYTES = 12; // AES-GCM standard 96-bit nonce.
43
+
44
+ function nowIso(deps: SocialTokenStoreDeps): string {
45
+ return (deps.now ?? (() => new Date().toISOString()))();
46
+ }
47
+
48
+ function toBase64(bytes: Uint8Array): string {
49
+ let s = "";
50
+ for (const b of bytes) s += String.fromCharCode(b);
51
+ return btoa(s);
52
+ }
53
+
54
+ function fromBase64(b64: string): Uint8Array<ArrayBuffer> {
55
+ const s = atob(b64);
56
+ const bytes = new Uint8Array(s.length);
57
+ for (let i = 0; i < s.length; i++) bytes[i] = s.charCodeAt(i);
58
+ return bytes;
59
+ }
60
+
61
+ /**
62
+ * Import the raw `SOCIAL_TOKEN_KEK` secret (base64 of a 256-bit key) into a
63
+ * non-extractable AES-GCM `CryptoKey`. Throws if the decoded key isn't 32 bytes
64
+ * — a misprovisioned KEK must fail loudly at the composition root, not silently
65
+ * produce unopenable ciphertext.
66
+ */
67
+ export async function importKek(rawBase64Key: string): Promise<CryptoKey> {
68
+ const raw = fromBase64(rawBase64Key.trim());
69
+ if (raw.length !== 32) {
70
+ throw new Error(`SOCIAL_TOKEN_KEK must decode to 32 bytes (got ${raw.length})`);
71
+ }
72
+ return crypto.subtle.importKey("raw", raw, { name: "AES-GCM" }, false, ["encrypt", "decrypt"]);
73
+ }
74
+
75
+ /** Encrypt a token payload → `{ ciphertext, iv }` (both base64). Fresh IV each call. */
76
+ export async function encryptTokenPayload(
77
+ kek: CryptoKey,
78
+ payload: SocialTokenPayload,
79
+ ): Promise<{ ciphertext: string; iv: string }> {
80
+ const iv = crypto.getRandomValues(new Uint8Array(IV_BYTES));
81
+ const plaintext = new TextEncoder().encode(JSON.stringify(payload));
82
+ const ct = await crypto.subtle.encrypt({ name: "AES-GCM", iv }, kek, plaintext);
83
+ return { ciphertext: toBase64(new Uint8Array(ct)), iv: toBase64(iv) };
84
+ }
85
+
86
+ /** Decrypt a stored `{ ciphertext, iv }` back to the token payload. */
87
+ export async function decryptTokenPayload(
88
+ kek: CryptoKey,
89
+ ciphertext: string,
90
+ iv: string,
91
+ ): Promise<SocialTokenPayload> {
92
+ const pt = await crypto.subtle.decrypt(
93
+ { name: "AES-GCM", iv: fromBase64(iv) },
94
+ kek,
95
+ fromBase64(ciphertext),
96
+ );
97
+ return JSON.parse(new TextDecoder().decode(pt)) as SocialTokenPayload;
98
+ }
99
+
100
+ /**
101
+ * Store (or overwrite) the token for `(tenantId, network)`. `INSERT OR REPLACE`
102
+ * on the composite PK, so this is an idempotent upsert; `created_at` follows the
103
+ * row's replacement (the natural key is the identity, not a birth timestamp).
104
+ */
105
+ export async function putSocialToken(
106
+ deps: SocialTokenStoreDeps,
107
+ tenantId: string,
108
+ network: string,
109
+ payload: SocialTokenPayload,
110
+ ): Promise<void> {
111
+ const { ciphertext, iv } = await encryptTokenPayload(deps.kek, payload);
112
+ const at = nowIso(deps);
113
+ await deps.db.run(
114
+ `INSERT OR REPLACE INTO social_tokens
115
+ (tenant_id, network, ciphertext, iv, expires_at, created_at, updated_at)
116
+ VALUES (?,?,?,?,?,?,?)`,
117
+ tenantId,
118
+ network,
119
+ ciphertext,
120
+ iv,
121
+ payload.expiresAt ?? null,
122
+ at,
123
+ at,
124
+ );
125
+ }
126
+
127
+ /** Decrypt and return the token for `(tenantId, network)`, or `null` if none stored. */
128
+ export async function getSocialToken(
129
+ deps: SocialTokenStoreDeps,
130
+ tenantId: string,
131
+ network: string,
132
+ ): Promise<SocialTokenPayload | null> {
133
+ const row = await deps.db.first<{ ciphertext: string; iv: string }>(
134
+ `SELECT ciphertext, iv FROM social_tokens WHERE tenant_id = ? AND network = ?`,
135
+ tenantId,
136
+ network,
137
+ );
138
+ if (!row) return null;
139
+ return decryptTokenPayload(deps.kek, row.ciphertext, row.iv);
140
+ }
141
+
142
+ /**
143
+ * Overwrite the token for `(tenantId, network)` with rotated material — the
144
+ * explicit name for the refresh-token-rotation path. Same write as
145
+ * {@link putSocialToken}; distinct export so a caller's intent reads at the seam.
146
+ */
147
+ export async function rotateSocialToken(
148
+ deps: SocialTokenStoreDeps,
149
+ tenantId: string,
150
+ network: string,
151
+ newPayload: SocialTokenPayload,
152
+ ): Promise<void> {
153
+ await putSocialToken(deps, tenantId, network, newPayload);
154
+ }
155
+
156
+ /**
157
+ * Composition root: wire the real Worker bindings into a `SocialTokenStoreDeps`.
158
+ * Returns `null` when the D1 binding or the `SOCIAL_TOKEN_KEK` secret is absent
159
+ * (unprovisioned runtime / dev), so a caller degrades to "social posting
160
+ * unavailable" rather than throwing — the KEK is a runtime secret that must be
161
+ * provisioned out of band (`wrangler secret put SOCIAL_TOKEN_KEK`).
162
+ */
163
+ export async function resolveSocialTokenStore(): Promise<SocialTokenStoreDeps | null> {
164
+ const [{ fromD1 }, d1, rawKek] = await Promise.all([
165
+ import("../d1/catalog.js"),
166
+ readD1("STOREFRONT_APPS_DB"),
167
+ readEnv("SOCIAL_TOKEN_KEK"),
168
+ ]);
169
+ if (!d1 || !rawKek) return null;
170
+ return { db: fromD1(d1), kek: await importKek(rawKek) };
171
+ }
@@ -64,7 +64,7 @@ export interface FeaturedProductsBlock {
64
64
 
65
65
  /**
66
66
  * Membership "band" on the commerce home — the Guest / Member / Autopilot tier
67
- * comparison, driven by the platform <TierCards> (F4) widget from tenant data.
67
+ * comparison, driven by the platform <TierCards> widget from tenant data.
68
68
  * The widget owns the illustrative-figures affordance (a tier is an EXAMPLE
69
69
  * unless it asserts `real: true`), so a tenant surfaces its membership model on
70
70
  * the home page without copying tier-card HTML. The block carries the same
@@ -271,11 +271,11 @@ export interface EditorialPage {
271
271
  /**
272
272
  * Sanitized HTML body (legacy / raw path). Optional: a page authored with
273
273
  * structured {@link ProseSection}s omits it and drives the body from `sections`
274
- * instead (spec §F4). The renderer prefers `sections` when present.
274
+ * instead. The renderer prefers `sections` when present.
275
275
  */
276
276
  body_html?: string;
277
277
  /**
278
- * Structured policy/prose body (spec §F4) — headings, paragraphs, lists,
278
+ * Structured policy/prose body — headings, paragraphs, lists,
279
279
  * callouts, a contact block, needs-review flags. When present, the page route
280
280
  * renders <ProseSections> from this instead of `set:html(body_html)`, so a
281
281
  * tenant drives a full policy page from JSON with no HTML/CSS to copy.
@@ -290,33 +290,7 @@ export interface EditorialPage {
290
290
  isPolicy?: boolean;
291
291
  }
292
292
 
293
- export interface SiteNavItem {
294
- label: string;
295
- href: string;
296
- /** Mega-menu children, e.g. collection links grouped under a category. */
297
- children?: { label: string; href: string }[];
298
- }
299
-
300
- export interface SiteChrome {
301
- nav: SiteNavItem[];
302
- /**
303
- * Header conversion buttons (marketing tenants). Rendered in the header's
304
- * right cluster in place of the commerce search/saved controls. Commerce
305
- * tenants omit this and keep the search + saved affordances.
306
- */
307
- headerCtas?: MarketingCta[];
308
- footer: {
309
- columns: { title: string; links: { label: string; href: string }[] }[];
310
- social: { label: string; href: string }[];
311
- tagline?: string;
312
- /** Bottom-bar legal/compliance line (marketing tenants). Plain text. */
313
- legal?: string;
314
- };
315
- announcement?: string;
316
- /** Default legal/safety disclaimer HTML (fallback for unknown categories). */
317
- disclaimer?: string;
318
- /** Per-product-category disclaimer HTML, keyed by product_type (or
319
- * compliance.product_category) — the source store renders a different legal
320
- * block per type (sub-ohm on salts, battery on devices). */
321
- disclaimers?: Record<string, string>;
322
- }
293
+ // Site chrome (header/footer) is the ONE consolidated schema in
294
+ // lib/chrome/model.ts (`ChromeConfig`) — see that file's header comment for
295
+ // why the old `SiteChrome`/`SiteNavItem` pair that used to live here was
296
+ // retired.