@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
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Operator-session resolution for the Private Apps admin API
3
- * (`/api/apps/admin/*`, PrivateApps epic, D2 Chunk C). Adapted from
3
+ * (`/api/apps/admin/*`). Mirrors
4
4
  * `../grants/session.ts`'s owner-session resolution — same two paths, same
5
5
  * fail-closed shape — but resolves `{ tenantId, actor }` instead of `{ owner }`:
6
6
  *
@@ -27,7 +27,7 @@
27
27
  * DEDICATED `APPS_ADMIN_SECRET` only — unlike grants' session, this does
28
28
  * NOT fall back to the shared `PREVIEW_RECONCILE_SECRET`, since that
29
29
  * secret must not double as a cross-tenant app-registry master key
30
- * (decision d2-admin-auth-dedicated-secret). Unconfigured `APPS_ADMIN_SECRET`
30
+ * Unconfigured `APPS_ADMIN_SECRET`
31
31
  * means this path is unavailable — fails closed rather than trusting
32
32
  * another deployment's secret. The tenant is resolved server-side from
33
33
  * `APPS_ADMIN_TENANT` env or the `X-Tot-Tenant` header (honoured only
@@ -38,7 +38,7 @@
38
38
  * Routes depend only on the `{ ok, tenantId, actor }` result. `authorizeAdmin`
39
39
  * is the pure core of path 2 (unit-testable, no I/O).
40
40
  *
41
- * D8 ADDITION — console admin session. The two paths above are host-bound:
41
+ * Console admin session. The two paths above are host-bound:
42
42
  * they resolve exactly one tenant (`context.locals.tenant`). The
43
43
  * `/api/apps/admin/*` surface is also reachable from the multi-tenant vendor
44
44
  * console, where a signed-in viewer may hold memberships on several vendors —
@@ -61,6 +61,11 @@ import { sha256Hex } from "@tot/public-runtime";
61
61
  import { readEnv } from "@/lib/env";
62
62
  import { readViewerSession } from "@/lib/auth/route.js";
63
63
  import { viewerCanAccess, membershipFor } from "@/lib/dashboard/tenantSelection.js";
64
+ import {
65
+ authorizeBearerOperator,
66
+ resolveHostBoundSession,
67
+ type SessionFailure,
68
+ } from "@/lib/auth/operatorSession.js";
64
69
 
65
70
  /** Capabilities that make a signed-in viewer an app-registry operator for their tenant. */
66
71
  const ADMIN_CAPABILITIES: ReadonlySet<string> = new Set(["owner", "admin"]);
@@ -88,33 +93,30 @@ export interface AdminAuthInput {
88
93
 
89
94
  export type AdminAuthResult =
90
95
  | { ok: true; tenantId: string; actor: string; role?: string }
91
- | { ok: false; status: number; message: string };
96
+ | SessionFailure;
97
+
98
+ /** The one authorized-success shape both host-bound and Bearer admin paths return. */
99
+ type AdminAuthOk = Extract<AdminAuthResult, { ok: true }>;
92
100
 
93
101
  /**
94
102
  * Decide the authenticated tenant from env + request, with NO I/O —
95
103
  * unit-testable. Fail-closed: unconfigured gate → 503, bad/absent Bearer →
96
- * 401, no resolvable tenant → 400.
104
+ * 401, no resolvable tenant → 400. A thin adapter over the shared
105
+ * Bearer-operator core that maps its resolved `principal` to this surface's
106
+ * `tenantId` and stamps the fixed `BEARER_ACTOR` (no per-caller identity here).
97
107
  */
98
108
  export function authorizeAdmin(input: AdminAuthInput): AdminAuthResult {
99
- if (!input.gateSecret) {
100
- return {
101
- ok: false,
102
- status: 503,
103
- message: "apps admin operator secret not configured",
104
- };
105
- }
106
- if (input.authHeader !== `Bearer ${input.gateSecret}`) {
107
- return { ok: false, status: 401, message: "Unauthorized" };
108
- }
109
- const tenantId = (input.configuredTenant ?? input.requestedTenant ?? "").trim();
110
- if (!tenantId) {
111
- return {
112
- ok: false,
113
- status: 400,
114
- message: "tenant unresolved (set APPS_ADMIN_TENANT or X-Tot-Tenant)",
115
- };
116
- }
117
- return { ok: true, tenantId, actor: BEARER_ACTOR };
109
+ const res = authorizeBearerOperator({
110
+ authHeader: input.authHeader,
111
+ gateSecret: input.gateSecret,
112
+ configuredPrincipal: input.configuredTenant,
113
+ requestedPrincipal: input.requestedTenant,
114
+ unconfiguredMessage: "apps admin operator secret not configured",
115
+ unresolvedMessage: "tenant unresolved (set APPS_ADMIN_TENANT or X-Tot-Tenant)",
116
+ });
117
+ return res.ok
118
+ ? { ok: true, tenantId: res.principal, actor: BEARER_ACTOR }
119
+ : res;
118
120
  }
119
121
 
120
122
  /** One-way, stable pseudonym for an audit `actor` — never the email itself. */
@@ -128,30 +130,37 @@ async function actorFromEmail(email: string): Promise<string> {
128
130
  * from the body.
129
131
  */
130
132
  export async function resolveAdminSession(context: APIContext): Promise<AdminAuthResult> {
131
- // Path 1: a signed-in tenant-operator session. The tenant IS the resolved
132
- // host's appDomain — never from the request body.
133
- const viewer = context.locals?.viewer;
134
- const tenantId = context.locals?.tenant?.appDomain;
135
- if (viewer && tenantId) {
136
- if (!ADMIN_CAPABILITIES.has(viewer.capability)) {
137
- return {
133
+ return resolveHostBoundSession<AdminAuthOk>(context, {
134
+ capabilities: ADMIN_CAPABILITIES,
135
+ // A resolved-but-insufficient viewer fails closed with 403 and does NOT
136
+ // fall through to the Bearer gate — an authenticated-but-under-privileged
137
+ // caller must not be silently admitted by a secret it also presents.
138
+ onInsufficientCapability: {
139
+ mode: "failClosed",
140
+ failure: {
138
141
  ok: false,
139
142
  status: 403,
140
143
  message: "not authorized to manage apps for this tenant",
141
- };
142
- }
143
- return { ok: true, tenantId, actor: await actorFromEmail(viewer.email) };
144
- }
145
-
146
- // Path 2 (fallback): the headless-console Bearer operator secret. Dedicated
147
- // secret only — no fallback to another deployment's secret.
148
- const gateSecret = await readEnv("APPS_ADMIN_SECRET");
149
- const configuredTenant = await readEnv("APPS_ADMIN_TENANT");
150
- return authorizeAdmin({
151
- authHeader: context.request.headers.get("authorization"),
152
- gateSecret,
153
- configuredTenant,
154
- requestedTenant: context.request.headers.get("x-tot-tenant"),
144
+ },
145
+ },
146
+ // Path 1: the tenant IS the resolved host's appDomain — never the body.
147
+ resolveViewer: async (viewer, tenantId) => ({
148
+ ok: true,
149
+ tenantId,
150
+ actor: await actorFromEmail(viewer.email),
151
+ }),
152
+ // Path 2 (fallback): the headless-console Bearer operator secret. Dedicated
153
+ // secret only — no fallback to another deployment's secret.
154
+ resolveBearer: async (ctx) => {
155
+ const gateSecret = await readEnv("APPS_ADMIN_SECRET");
156
+ const configuredTenant = await readEnv("APPS_ADMIN_TENANT");
157
+ return authorizeAdmin({
158
+ authHeader: ctx.request.headers.get("authorization"),
159
+ gateSecret,
160
+ configuredTenant,
161
+ requestedTenant: ctx.request.headers.get("x-tot-tenant"),
162
+ });
163
+ },
155
164
  });
156
165
  }
157
166
 
@@ -1,7 +1,7 @@
1
1
  /**
2
- * The D3 scoped-API authorization boundary (PrivateApps epic) — every
2
+ * The scoped-API authorization boundary — every
3
3
  * `/api/apps/v1/*` route calls `authorizeAppRequest` FIRST. Verifies the
4
- * app-gateway bearer JWT (D2's `AppTokenVerifier`), resolves the calling
4
+ * app-gateway bearer JWT (`AppTokenVerifier`), resolves the calling
5
5
  * install from the registry, and enforces the single scope the route
6
6
  * requires per `docs/private-apps/contract/openapi.yaml`'s `x-requiredScope`.
7
7
  *
@@ -1,6 +1,6 @@
1
1
  /**
2
- * Response helpers shared by every `/api/apps/v1/*` route (PrivateApps epic,
3
- * D3) — a no-store JSON responder (mirrors `../grants/route.ts` /
2
+ * Response helpers shared by every `/api/apps/v1/*` route a no-store JSON
3
+ * responder (mirrors `../grants/route.ts` /
4
4
  * `./adminService.ts`'s pattern) plus the `tot-request-id` / `traceparent`
5
5
  * response headers `docs/private-apps/contract/openapi.yaml` documents on
6
6
  * every operation.
@@ -0,0 +1,281 @@
1
+ /**
2
+ * The managed-app version-retention + atomic-rollback spine (ext-apps-02) — the
3
+ * MANAGED-APP analog of the tenant integration lifecycle's known-good/rollback
4
+ * (`integrationLifecycle.ts`). Where that module keeps a tenant's own resolved
5
+ * integration revisions and repoints `live` to a known-good one, this persists
6
+ * every activated managed-app VERSION's COMPLETE restorable facet set and rolls an
7
+ * installed app back to a prior version's whole facet set in one atomic step.
8
+ *
9
+ * Two acceptance properties are schema-level, not conventions:
10
+ * - RETAINED — every activation is a new immutable `app_version_snapshots` row
11
+ * carrying the app's FULL restorable facet set (code ref/digest + entry module,
12
+ * public config, contributions, granted scopes, consent declaration, CSP
13
+ * origins) as one JSON blob. Nothing is mutated in place; the row list IS the
14
+ * retention history and audit trail (actor + time per activation).
15
+ * - ATOMIC ROLLBACK — a rollback restores a retained version's COMPLETE facet set
16
+ * in a SINGLE-ROW insert that copies that version's `facets` blob verbatim. You
17
+ * get the whole prior facet set or nothing — never a partial/mixed activation,
18
+ * never a silently-defaulted facet. It is forward-only (records `restoredFromSeq`
19
+ * and appends a new activation), so history is never rewritten and the rollback
20
+ * is itself an audited row.
21
+ *
22
+ * Fail-closed, mirroring the tenant lifecycle's discipline: a retained/restored
23
+ * facet set is scanned for secret material ({@link findForbiddenFields}) BEFORE any
24
+ * write, and a rollback to a version that was never retained is REFUSED with the
25
+ * store left completely untouched (no new row, current version unchanged).
26
+ *
27
+ * Pure over the `Queryable` seam (same shape `ConsentConfigStore`/`AppRegistryService`
28
+ * use), so it runs unchanged against real D1 in the Worker
29
+ * (`fromD1(env.STOREFRONT_APPS_DB)`) and an in-memory `node:sqlite` in tests. The
30
+ * current active version is `MAX(seq)` per install — the same "the latest row is
31
+ * current" model as `consent_config_revisions`.
32
+ */
33
+ import type {
34
+ ExecutionTier,
35
+ PlanIntegrationEntry,
36
+ PlanIntegrationConsent,
37
+ NormalizedContribution,
38
+ } from "@tot/public-runtime";
39
+ import { findForbiddenFields } from "@tot/public-runtime";
40
+ import type { Queryable, QueryStatement } from "../d1/catalog.js";
41
+
42
+ /**
43
+ * One managed-app version's COMPLETE restorable facet set — everything a rollback
44
+ * must put back to reconstitute the prior version with no missing or defaulted
45
+ * facet. All values are public (no secret material); the store proves it with a
46
+ * secret scan before persisting.
47
+ */
48
+ export interface AppVersionFacets {
49
+ /** The app package version this activation serves. */
50
+ appVersion: string;
51
+ /** The code identity — content digest of the pinned package. */
52
+ digest: string;
53
+ /** Where this version's code runs (elevated vs boxed). */
54
+ executionTier: ExecutionTier;
55
+ /** The exact-version entry module the lifecycle runtime imports. */
56
+ entry: PlanIntegrationEntry;
57
+ /** Resolved per-version public config; carries no secret material. */
58
+ publicConfig: Record<string, unknown>;
59
+ /** The normalized contributions this version registers. */
60
+ contributions: NormalizedContribution[];
61
+ /** The scopes granted to this version. */
62
+ scopes: string[];
63
+ /** The consent declaration the entry loader gates on. */
64
+ consent: PlanIntegrationConsent;
65
+ /** Sorted, deduped exact CSP origins this version needs. */
66
+ cspOrigins: string[];
67
+ }
68
+
69
+ /** One retained activation — an immutable version-retention/audit-trail entry. */
70
+ export interface AppVersionSnapshot {
71
+ id: string;
72
+ installId: string;
73
+ tenantId: string;
74
+ /** Monotonic per install; the largest is the currently active version. */
75
+ seq: number;
76
+ /** Mirror of `facets.appVersion` for cheap listing/filtering. */
77
+ appVersion: string;
78
+ /** The complete restorable facet set. */
79
+ facets: AppVersionFacets;
80
+ /** Opaque actor id — never PII. */
81
+ actor: string;
82
+ note: string | null;
83
+ /** The source `seq` when this activation is a rollback, else null. */
84
+ restoredFromSeq: number | null;
85
+ createdAt: string;
86
+ }
87
+
88
+ /** Retain (activate) a new managed-app version, persisting its full facet set. */
89
+ export interface RetainVersionInput {
90
+ installId: string;
91
+ tenantId: string;
92
+ facets: AppVersionFacets;
93
+ actor: string;
94
+ note?: string | null;
95
+ }
96
+
97
+ /** Roll an install back to a retained version, restoring its complete facet set. */
98
+ export interface RollbackVersionInput {
99
+ installId: string;
100
+ /** The retained activation's `seq` — its stable version handle — to restore. */
101
+ toSeq: number;
102
+ actor: string;
103
+ note?: string | null;
104
+ }
105
+
106
+ export interface PlannedVersionWrite {
107
+ snapshot: AppVersionSnapshot;
108
+ statement: QueryStatement;
109
+ }
110
+
111
+ export type AppVersionErrorCode = "unknown_target" | "integrity";
112
+
113
+ /** Thrown by every fail-closed refusal here — callers branch on `.code`. */
114
+ export class AppVersionError extends Error {
115
+ constructor(
116
+ message: string,
117
+ readonly code: AppVersionErrorCode,
118
+ readonly detail?: string[],
119
+ ) {
120
+ super(message);
121
+ this.name = "AppVersionError";
122
+ }
123
+ }
124
+
125
+ interface SnapshotRow {
126
+ id: string;
127
+ install_id: string;
128
+ tenant_id: string;
129
+ seq: number;
130
+ app_version: string;
131
+ facets: string;
132
+ actor: string;
133
+ note: string | null;
134
+ restored_from_seq: number | null;
135
+ created_at: string;
136
+ }
137
+
138
+ function toSnapshot(row: SnapshotRow): AppVersionSnapshot {
139
+ return {
140
+ id: row.id,
141
+ installId: row.install_id,
142
+ tenantId: row.tenant_id,
143
+ seq: row.seq,
144
+ appVersion: row.app_version,
145
+ facets: JSON.parse(row.facets) as AppVersionFacets,
146
+ actor: row.actor,
147
+ note: row.note,
148
+ restoredFromSeq: row.restored_from_seq,
149
+ createdAt: row.created_at,
150
+ };
151
+ }
152
+
153
+ export class AppVersionStore {
154
+ constructor(
155
+ private readonly db: Queryable,
156
+ private readonly now: () => string = () => new Date().toISOString(),
157
+ private readonly newId: () => string = () => crypto.randomUUID(),
158
+ ) {}
159
+
160
+ /**
161
+ * Retain (activate) a managed-app version by appending a new immutable snapshot
162
+ * carrying its COMPLETE restorable facet set. The `seq` is assigned as
163
+ * `MAX(seq)+1` for that install; the UNIQUE `(install_id, seq)` index makes a
164
+ * concurrent double-assignment fail loudly rather than overwrite an earlier
165
+ * activation. Fail-closed: a facet set naming secret material is REFUSED before
166
+ * any write (defense in depth over the plan/evidence contracts that produced it).
167
+ */
168
+ async retainVersion(input: RetainVersionInput): Promise<AppVersionSnapshot> {
169
+ const planned = await this.planRetainVersion(input);
170
+ await this.db.run(planned.statement.sql, ...(planned.statement.params ?? []));
171
+ return planned.snapshot;
172
+ }
173
+
174
+ /** Validate and plan retention without writing, for a caller-owned D1 batch. */
175
+ async planRetainVersion(input: RetainVersionInput): Promise<PlannedVersionWrite> {
176
+ return this.planAppend(input.installId, input.tenantId, input.facets, input.actor, input.note ?? null, null);
177
+ }
178
+
179
+ /**
180
+ * Roll an install back to a retained version, restoring its COMPLETE facet set in
181
+ * one atomic single-row insert that copies the target activation's `facets` blob
182
+ * verbatim (forward-only: records `restoredFromSeq`). No partial/mixed state is
183
+ * reachable — the whole prior facet set is restored or nothing is. Fail-closed: a
184
+ * `toSeq` that was never retained is REFUSED (`unknown_target`) and the store is
185
+ * left completely untouched — no new row, the current version unchanged.
186
+ */
187
+ async rollbackTo(input: RollbackVersionInput): Promise<AppVersionSnapshot> {
188
+ const planned = await this.planRollbackTo(input);
189
+ await this.db.run(planned.statement.sql, ...(planned.statement.params ?? []));
190
+ return planned.snapshot;
191
+ }
192
+
193
+ /** Plan a forward rollback without advancing MAX(seq) until the batch commits. */
194
+ async planRollbackTo(input: RollbackVersionInput): Promise<PlannedVersionWrite> {
195
+ const target = await this.getSnapshot(input.installId, input.toSeq);
196
+ if (!target) {
197
+ throw new AppVersionError(
198
+ `Cannot roll back install "${input.installId}" to version seq ${input.toSeq}: no such retained version.`,
199
+ "unknown_target",
200
+ );
201
+ }
202
+ const note = input.note ?? `Rollback to version ${target.appVersion} (seq ${input.toSeq}).`;
203
+ return this.planAppend(
204
+ input.installId,
205
+ target.tenantId,
206
+ target.facets,
207
+ input.actor,
208
+ note,
209
+ input.toSeq,
210
+ );
211
+ }
212
+
213
+ /** The current (active) version of an install — `MAX(seq)` — or null if none retained. */
214
+ async currentVersion(installId: string): Promise<AppVersionSnapshot | null> {
215
+ const row = await this.db.first<SnapshotRow>(
216
+ `SELECT * FROM app_version_snapshots WHERE install_id = ? ORDER BY seq DESC LIMIT 1`,
217
+ installId,
218
+ );
219
+ return row ? toSnapshot(row) : null;
220
+ }
221
+
222
+ /** A specific retained activation by its `seq`, or null if absent. */
223
+ async getSnapshot(installId: string, seq: number): Promise<AppVersionSnapshot | null> {
224
+ const row = await this.db.first<SnapshotRow>(
225
+ `SELECT * FROM app_version_snapshots WHERE install_id = ? AND seq = ?`,
226
+ installId,
227
+ seq,
228
+ );
229
+ return row ? toSnapshot(row) : null;
230
+ }
231
+
232
+ /** An install's full retention history, newest first — the version audit trail. */
233
+ async listVersions(installId: string): Promise<AppVersionSnapshot[]> {
234
+ const rows = await this.db.all<SnapshotRow>(
235
+ `SELECT * FROM app_version_snapshots WHERE install_id = ? ORDER BY seq DESC`,
236
+ installId,
237
+ );
238
+ return rows.map(toSnapshot);
239
+ }
240
+
241
+ /**
242
+ * Append a new activation. Shared by retain (source `null`) and rollback (source
243
+ * `restoredFromSeq`). Scans the facet set for secret material BEFORE the insert,
244
+ * then writes exactly one row — the atomicity boundary: the whole facet set lands
245
+ * or nothing does.
246
+ */
247
+ private async planAppend(
248
+ installId: string,
249
+ tenantId: string,
250
+ facets: AppVersionFacets,
251
+ actor: string,
252
+ note: string | null,
253
+ restoredFromSeq: number | null,
254
+ ): Promise<PlannedVersionWrite> {
255
+ const forbidden = findForbiddenFields(facets, "facets");
256
+ if (forbidden.length > 0) {
257
+ throw new AppVersionError(
258
+ `Refusing to persist version facets naming secret material: ${forbidden.map((v) => v.path).join(", ")}.`,
259
+ "integrity",
260
+ forbidden.map((v) => v.path),
261
+ );
262
+ }
263
+ const maxRow = await this.db.first<{ max_seq: number | null }>(
264
+ `SELECT MAX(seq) AS max_seq FROM app_version_snapshots WHERE install_id = ?`,
265
+ installId,
266
+ );
267
+ const seq = (maxRow?.max_seq ?? 0) + 1;
268
+ const createdAt = this.now();
269
+ const id = `avs_${this.newId()}`;
270
+ const statement: QueryStatement = {
271
+ sql: `INSERT INTO app_version_snapshots
272
+ (id, install_id, tenant_id, seq, app_version, facets, actor, note, restored_from_seq, created_at)
273
+ VALUES (?,?,?,?,?,?,?,?,?,?)`,
274
+ params: [id, installId, tenantId, seq, facets.appVersion, JSON.stringify(facets), actor, note, restoredFromSeq, createdAt],
275
+ };
276
+ return {
277
+ statement,
278
+ snapshot: { id, installId, tenantId, seq, appVersion: facets.appVersion, facets, actor, note, restoredFromSeq, createdAt },
279
+ };
280
+ }
281
+ }
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Maps the internal `CatalogProduct` read model to the D3 contract's
2
+ * Maps the internal `CatalogProduct` read model to the scoped-API contract's
3
3
  * `Product` shape (`docs/private-apps/contract/openapi.yaml`, schema
4
4
  * `Product` — `additionalProperties: false`). Drops every internal field
5
5
  * the contract doesn't declare (variants, images, metafields, source, …) so
@@ -0,0 +1,228 @@
1
+ /**
2
+ * The merchant consent configuration store — the durable spine behind ext-admin-03.
3
+ * It persists a tenant's consent configuration as an APPEND-ONLY revision history and
4
+ * the demonstrated-CMP-demand ledger, and reads back exactly the shapes the pure
5
+ * ext-policy-03 layer consumes.
6
+ *
7
+ * Two acceptance properties are schema-level, not conventions:
8
+ * - AUDITED — every save is a new immutable revision that names its actor and time;
9
+ * nothing is mutated in place, so the revision list IS the audit trail.
10
+ * - REVERSIBLE — a revert is a forward-only new revision that copies a prior config
11
+ * and records `revertedFrom`; history is never rewritten, and rolling back leaves
12
+ * its own audited entry.
13
+ *
14
+ * Pure over the `Queryable` seam (same shape `CandidateStore`/`OrdersStore` use), so it
15
+ * runs unchanged against real D1 in the Worker (`fromD1(env.STOREFRONT_APPS_DB)`) and an
16
+ * in-memory `node:sqlite` in tests. This is the DATA MODEL + repository API only — the
17
+ * validation (`validateConsentConfig`) and the broker materialization
18
+ * (`toTenantConsentPolicy`/`buildTenantConsentBroker`) live in `@tot/public-runtime`;
19
+ * this store persists already-validated JSON and never re-derives policy.
20
+ */
21
+ import type { Queryable } from "../d1/catalog.js";
22
+ import type { CmpDemandLedger, TenantConsentConfig } from "@tot/public-runtime";
23
+
24
+ /** One persisted consent-config revision — an immutable audit-trail entry. */
25
+ export interface ConsentConfigRevision {
26
+ id: string;
27
+ tenantId: string;
28
+ /** Monotonic per tenant; the largest is the current config. */
29
+ revision: number;
30
+ config: TenantConsentConfig;
31
+ /** Opaque actor id — never PII. */
32
+ actor: string;
33
+ note: string | null;
34
+ /** The source revision when this one is a revert, else null. */
35
+ revertedFrom: number | null;
36
+ createdAt: string;
37
+ }
38
+
39
+ /** Save a new revision of a tenant's (already-validated) consent configuration. */
40
+ export interface SaveConsentConfigInput {
41
+ tenantId: string;
42
+ config: TenantConsentConfig;
43
+ actor: string;
44
+ note?: string | null;
45
+ }
46
+
47
+ /** Revert a tenant's consent configuration to a prior revision (forward-only). */
48
+ export interface RevertConsentConfigInput {
49
+ tenantId: string;
50
+ /** The revision whose config to restore as a NEW revision. */
51
+ toRevision: number;
52
+ actor: string;
53
+ note?: string | null;
54
+ }
55
+
56
+ /** Record demonstrated demand for a CMP adapter from one tenant. */
57
+ export interface RecordCmpDemandInput {
58
+ tenantId: string;
59
+ adapterId: string;
60
+ source?: string | null;
61
+ }
62
+
63
+ interface RevisionRow {
64
+ id: string;
65
+ tenant_id: string;
66
+ revision: number;
67
+ config: string;
68
+ actor: string;
69
+ note: string | null;
70
+ reverted_from: number | null;
71
+ created_at: string;
72
+ }
73
+
74
+ interface DemandRow {
75
+ id: string;
76
+ tenant_id: string;
77
+ adapter_id: string;
78
+ source: string | null;
79
+ at: string;
80
+ }
81
+
82
+ function toRevision(row: RevisionRow): ConsentConfigRevision {
83
+ return {
84
+ id: row.id,
85
+ tenantId: row.tenant_id,
86
+ revision: row.revision,
87
+ config: JSON.parse(row.config) as TenantConsentConfig,
88
+ actor: row.actor,
89
+ note: row.note,
90
+ revertedFrom: row.reverted_from,
91
+ createdAt: row.created_at,
92
+ };
93
+ }
94
+
95
+ export class ConsentConfigStore {
96
+ constructor(
97
+ private readonly db: Queryable,
98
+ private readonly now: () => string = () => new Date().toISOString(),
99
+ private readonly newId: () => string = () => crypto.randomUUID(),
100
+ ) {}
101
+
102
+ /**
103
+ * Append a new revision of a tenant's consent config. The revision number is
104
+ * assigned as `MAX(revision)+1` for that tenant; the UNIQUE `(tenant_id, revision)`
105
+ * index makes a concurrent double-assignment fail loudly rather than overwrite an
106
+ * earlier entry. The prior revisions are never touched — the history is the audit.
107
+ */
108
+ async saveRevision(input: SaveConsentConfigInput): Promise<ConsentConfigRevision> {
109
+ return this.appendRevision(input.tenantId, input.config, input.actor, input.note ?? null, null);
110
+ }
111
+
112
+ /**
113
+ * Restore a prior revision as a NEW revision (forward-only revert). Reads the target
114
+ * revision's config and appends it again with `revertedFrom` set — so a rollback is
115
+ * itself an audited entry and no history is rewritten. Throws if the target is absent.
116
+ */
117
+ async revertTo(input: RevertConsentConfigInput): Promise<ConsentConfigRevision> {
118
+ const target = await this.getRevision(input.tenantId, input.toRevision);
119
+ if (!target) {
120
+ throw new Error(
121
+ `Cannot revert tenant "${input.tenantId}" to revision ${input.toRevision}: no such revision.`,
122
+ );
123
+ }
124
+ const note = input.note ?? `Revert to revision ${input.toRevision}.`;
125
+ return this.appendRevision(input.tenantId, target.config, input.actor, note, input.toRevision);
126
+ }
127
+
128
+ private async appendRevision(
129
+ tenantId: string,
130
+ config: TenantConsentConfig,
131
+ actor: string,
132
+ note: string | null,
133
+ revertedFrom: number | null,
134
+ ): Promise<ConsentConfigRevision> {
135
+ const maxRow = await this.db.first<{ max_revision: number | null }>(
136
+ `SELECT MAX(revision) AS max_revision FROM consent_config_revisions WHERE tenant_id = ?`,
137
+ tenantId,
138
+ );
139
+ const revision = (maxRow?.max_revision ?? 0) + 1;
140
+ const createdAt = this.now();
141
+ await this.db.run(
142
+ `INSERT INTO consent_config_revisions
143
+ (id, tenant_id, revision, config, actor, note, reverted_from, created_at)
144
+ VALUES (?,?,?,?,?,?,?,?)`,
145
+ `ccr_${this.newId()}`,
146
+ tenantId,
147
+ revision,
148
+ JSON.stringify(config),
149
+ actor,
150
+ note,
151
+ revertedFrom,
152
+ createdAt,
153
+ );
154
+ const row = await this.db.first<RevisionRow>(
155
+ `SELECT * FROM consent_config_revisions WHERE tenant_id = ? AND revision = ?`,
156
+ tenantId,
157
+ revision,
158
+ );
159
+ return toRevision(row!);
160
+ }
161
+
162
+ /** The current (latest) revision of a tenant's consent config, or null if none saved. */
163
+ async currentRevision(tenantId: string): Promise<ConsentConfigRevision | null> {
164
+ const row = await this.db.first<RevisionRow>(
165
+ `SELECT * FROM consent_config_revisions WHERE tenant_id = ?
166
+ ORDER BY revision DESC LIMIT 1`,
167
+ tenantId,
168
+ );
169
+ return row ? toRevision(row) : null;
170
+ }
171
+
172
+ /** A specific revision, or null if absent. */
173
+ async getRevision(tenantId: string, revision: number): Promise<ConsentConfigRevision | null> {
174
+ const row = await this.db.first<RevisionRow>(
175
+ `SELECT * FROM consent_config_revisions WHERE tenant_id = ? AND revision = ?`,
176
+ tenantId,
177
+ revision,
178
+ );
179
+ return row ? toRevision(row) : null;
180
+ }
181
+
182
+ /** A tenant's full revision history, newest first — the audit trail. */
183
+ async listRevisions(tenantId: string): Promise<ConsentConfigRevision[]> {
184
+ const rows = await this.db.all<RevisionRow>(
185
+ `SELECT * FROM consent_config_revisions WHERE tenant_id = ? ORDER BY revision DESC`,
186
+ tenantId,
187
+ );
188
+ return rows.map(toRevision);
189
+ }
190
+
191
+ /**
192
+ * Record demonstrated demand for a CMP adapter from one tenant. Idempotent per
193
+ * `(tenant, adapter)` — a tenant re-asking updates the recorded time/source on the
194
+ * SAME row and never inflates the distinct-tenant demand count.
195
+ */
196
+ async recordCmpDemand(input: RecordCmpDemandInput): Promise<void> {
197
+ await this.db.run(
198
+ `INSERT INTO consent_cmp_demand (id, tenant_id, adapter_id, source, at)
199
+ VALUES (?,?,?,?,?)
200
+ ON CONFLICT(tenant_id, adapter_id) DO UPDATE SET
201
+ source = excluded.source,
202
+ at = excluded.at`,
203
+ `cmd_${this.newId()}`,
204
+ input.tenantId,
205
+ input.adapterId,
206
+ input.source ?? null,
207
+ this.now(),
208
+ );
209
+ }
210
+
211
+ /**
212
+ * The whole demand ledger, in the `@tot/public-runtime` shape — passed straight into
213
+ * `validateConsentConfig` so a CMP choice is admitted only from demonstrated demand.
214
+ */
215
+ async demandLedger(): Promise<CmpDemandLedger> {
216
+ const rows = await this.db.all<DemandRow>(
217
+ `SELECT * FROM consent_cmp_demand ORDER BY adapter_id ASC, tenant_id ASC`,
218
+ );
219
+ return {
220
+ signals: rows.map((r) => ({
221
+ adapterId: r.adapter_id,
222
+ tenantPublicId: r.tenant_id,
223
+ at: r.at,
224
+ source: r.source ?? undefined,
225
+ })),
226
+ };
227
+ }
228
+ }