@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,8 +1,8 @@
1
1
  /**
2
- * App registry service — PrivateApps epic, D2 (Chunk A). Owns the
2
+ * App registry service. Owns the
3
3
  * `app_installs` / `app_audit_log` rows described in `schema.ts` and
4
4
  * orchestrates credential lifecycle through the {@link CredentialPort} seam
5
- * (Chunk B implements it; Chunk A only depends on the interface, mirroring
5
+ * (the credential store implements it; this service depends only on the interface, mirroring
6
6
  * how `D1VersionStore` implements `@tot/private-controlplane`'s `VersionStore`
7
7
  * contract without either side importing the other's internals).
8
8
  *
@@ -34,9 +34,42 @@
34
34
  * (no detail that would let a caller distinguish "wrong tenant" from
35
35
  * "never existed"); a suspended/uninstalled install rejects further
36
36
  * mutation with `invalid_state`.
37
+ *
38
+ * Version retention + the single source-of-truth (ext-apps-02b): every
39
+ * activation — `install`, `update`, and `rollback` — retains the version's
40
+ * COMPLETE restorable facet set through {@link AppVersionStore} (its
41
+ * `app_version_snapshots` table over the SAME `Queryable` seam this service
42
+ * uses). The authority for "which version is active" is the version store's
43
+ * `MAX(seq)` current snapshot; `app_installs.app_version` is a DERIVED MIRROR
44
+ * that this service re-points from the store's post-write current version in
45
+ * the same operation ({@link AppRegistryService.syncActiveVersionMirror}). The
46
+ * two can never diverge because the mirror is only ever written from the
47
+ * store's current snapshot — you change the active version by retaining/rolling
48
+ * one, never by writing the mirror directly. `rollback` restores a prior
49
+ * version's whole facet set atomically (store-side) and re-syncs the mirror, so
50
+ * the registry's active version always agrees with the restored snapshot.
37
51
  */
38
52
  import { validateScopes, type ScopeDenial } from "./scopes.js";
39
53
  import { validateWebhookEndpoint } from "../webhooks/endpointGuard.js";
54
+ import {
55
+ AppVersionStore,
56
+ AppVersionError,
57
+ type AppVersionFacets,
58
+ type AppVersionSnapshot,
59
+ } from "./appVersionStore.js";
60
+ import {
61
+ DEFAULT_EXECUTION_TIER,
62
+ EXECUTION_TIERS,
63
+ ENTRY_FORMATS,
64
+ VENDOR_ADDRESSABLE_PHASES,
65
+ CONSENT_LOAD_WHEN,
66
+ type ExecutionTier,
67
+ type EntryFormat,
68
+ type ReservedPhase,
69
+ type ConsentLoadWhen,
70
+ type PlanIntegrationEntry,
71
+ type PlanIntegrationConsent,
72
+ } from "@tot/public-runtime";
40
73
  import type { Queryable } from "../d1/catalog.js";
41
74
 
42
75
  export type AppInstallStatus = "active" | "suspended" | "uninstalled";
@@ -65,7 +98,8 @@ export type AppAuditEvent =
65
98
  | "suspended"
66
99
  | "resumed"
67
100
  | "uninstalled"
68
- | "credentials_rotated";
101
+ | "credentials_rotated"
102
+ | "rolled_back";
69
103
 
70
104
  /** The auth-derived context every install is scoped to — never caller input. */
71
105
  export interface InstallActorContext {
@@ -81,6 +115,14 @@ export interface InstallInput {
81
115
  manifest: unknown;
82
116
  webhookEndpoint?: string | null;
83
117
  widgetPlacements?: unknown[];
118
+ /**
119
+ * The fully plan-resolved facet set to retain for this version. Optional: when
120
+ * a caller has already run the plan resolver (ext-kernel-03) it passes the
121
+ * resolved set and it is snapshotted verbatim; when absent the registry
122
+ * projects a restorable facet set from the captured manifest + granted scopes
123
+ * ({@link resolveVersionFacets}). Either way `install` ALWAYS retains a v1.
124
+ */
125
+ versionFacets?: AppVersionFacets;
84
126
  }
85
127
 
86
128
  export interface AppInstallPatch {
@@ -89,6 +131,8 @@ export interface AppInstallPatch {
89
131
  manifest?: unknown;
90
132
  webhookEndpoint?: string | null;
91
133
  widgetPlacements?: unknown[];
134
+ /** As {@link InstallInput.versionFacets}: the resolved set to retain for this activation. */
135
+ versionFacets?: AppVersionFacets;
92
136
  }
93
137
 
94
138
  export type RegistryErrorCode =
@@ -97,7 +141,9 @@ export type RegistryErrorCode =
97
141
  | "already_installed"
98
142
  | "invalid_state"
99
143
  | "invalid_webhook_endpoint"
100
- | "invalid_widget_placement";
144
+ | "invalid_widget_placement"
145
+ | "unknown_version"
146
+ | "invalid_version_facets";
101
147
 
102
148
  /**
103
149
  * `install()`'s return: the install row PLUS the one-time client credential
@@ -131,7 +177,7 @@ export interface IssuedCredential {
131
177
 
132
178
  /**
133
179
  * `IssuedCredential` plus the one-time plaintext secret. `issue`/`rotate` mint
134
- * the secret, hash it for storage (Chunk B: `sha256Hex`), and hand the
180
+ * the secret, hash it for storage (`sha256Hex`), and hand the
135
181
  * plaintext back exactly once here — there is no "show secret again" path by
136
182
  * design, so a caller that discards this return has lost it for good (see
137
183
  * `AppRegistryService.install`'s `InstallResult`, which is why this seam
@@ -140,10 +186,10 @@ export interface IssuedCredential {
140
186
  export type IssuedCredentialSecret = IssuedCredential & { secret: string };
141
187
 
142
188
  /**
143
- * Minimal seam for the credential lifecycle a later chunk (D2 Chunk B)
189
+ * Minimal seam for the credential lifecycle the credential store
144
190
  * implements against `app_credentials`. `AppRegistryService` orchestrates
145
191
  * through this interface only — it never touches credential storage directly,
146
- * so Chunk A can ship and be tested (with a fake) without Chunk B existing.
192
+ * so it can be tested with a fake.
147
193
  */
148
194
  export interface CredentialPort {
149
195
  /** Issue a fresh client-credential set for a newly installed app. */
@@ -155,7 +201,7 @@ export interface CredentialPort {
155
201
  }
156
202
 
157
203
  /**
158
- * The 3 platform-owned widget slots (D5 Chunk A). Mirrors the `widgets[].placement`
204
+ * The 3 platform-owned widget slots. Mirrors the `widgets[].placement`
159
205
  * enum in `docs/private-apps/contract/tot-app.schema.json` — widgets render in a
160
206
  * platform-managed sandboxed iframe at one of these fixed slots, never as
161
207
  * merchant-pasted script at an app-chosen location.
@@ -257,12 +303,100 @@ function toDomain(row: AppInstallRow): AppInstall {
257
303
  };
258
304
  }
259
305
 
306
+ /**
307
+ * Project the version's restorable facet set from what the registry
308
+ * authoritatively holds. A pre-resolved `override` (produced upstream by the
309
+ * plan resolver) is snapshotted verbatim EXCEPT for the two facets the registry
310
+ * — not the resolver — is the authority for: the version handle (`appVersion`)
311
+ * and the granted `scopes`. With no override, the set is projected from the
312
+ * captured `manifest`'s declared browser facets (tier / digest / entry /
313
+ * consent) with fail-closed defaults for a partial declaration; the
314
+ * cross-cutting `contributions` / `cspOrigins` / tenant `publicConfig` a full
315
+ * plan resolution computes are left empty here and only ever arrive through the
316
+ * `override`. Pure — no I/O, never throws (the version store re-scans the result
317
+ * for secret material before it is persisted).
318
+ */
319
+ export function resolveVersionFacets(args: {
320
+ appVersion: string;
321
+ scopes: string[];
322
+ manifest: unknown;
323
+ override?: AppVersionFacets;
324
+ }): AppVersionFacets {
325
+ const { appVersion, scopes, manifest, override } = args;
326
+ if (override) return { ...override, appVersion, scopes };
327
+
328
+ const browser = asObject(asObject(manifest)?.["browser"]);
329
+ return {
330
+ appVersion,
331
+ digest: readString(asObject(asObject(manifest)?.["app"])?.["digest"]) ?? "",
332
+ executionTier: readExecutionTier(browser?.["executionTier"]),
333
+ entry: readEntry(asObject(browser?.["entry"])),
334
+ publicConfig: {},
335
+ contributions: [],
336
+ scopes,
337
+ consent: readConsent(asObject(browser?.["consent"])),
338
+ cspOrigins: [],
339
+ };
340
+ }
341
+
342
+ function asObject(value: unknown): Record<string, unknown> | undefined {
343
+ return value !== null && typeof value === "object" && !Array.isArray(value)
344
+ ? (value as Record<string, unknown>)
345
+ : undefined;
346
+ }
347
+
348
+ function readString(value: unknown): string | undefined {
349
+ return typeof value === "string" ? value : undefined;
350
+ }
351
+
352
+ function readExecutionTier(value: unknown): ExecutionTier {
353
+ return typeof value === "string" && (EXECUTION_TIERS as readonly string[]).includes(value)
354
+ ? (value as ExecutionTier)
355
+ : DEFAULT_EXECUTION_TIER;
356
+ }
357
+
358
+ function readEntry(entry: Record<string, unknown> | undefined): PlanIntegrationEntry {
359
+ const url = readString(entry?.["asset"]) ?? "";
360
+ const format =
361
+ typeof entry?.["format"] === "string" && (ENTRY_FORMATS as readonly string[]).includes(entry["format"] as string)
362
+ ? (entry["format"] as EntryFormat)
363
+ : "module";
364
+ const phase =
365
+ typeof entry?.["phase"] === "string" &&
366
+ (VENDOR_ADDRESSABLE_PHASES as readonly string[]).includes(entry["phase"] as string)
367
+ ? (entry["phase"] as ReservedPhase)
368
+ : "document.deferred";
369
+ return { url, format, phase };
370
+ }
371
+
372
+ function readConsent(consent: Record<string, unknown> | undefined): PlanIntegrationConsent {
373
+ const purposes = Array.isArray(consent?.["purposes"])
374
+ ? (consent["purposes"] as unknown[]).filter((p): p is string => typeof p === "string")
375
+ : [];
376
+ const loadWhen: ConsentLoadWhen =
377
+ typeof consent?.["loadWhen"] === "string" &&
378
+ (CONSENT_LOAD_WHEN as readonly string[]).includes(consent["loadWhen"] as string)
379
+ ? (consent["loadWhen"] as ConsentLoadWhen)
380
+ : "essential-only";
381
+ return { purposes, loadWhen };
382
+ }
383
+
260
384
  export class AppRegistryService {
385
+ private readonly versionStore: AppVersionStore;
386
+
261
387
  constructor(
262
388
  private readonly db: Queryable,
263
389
  private readonly credentials: CredentialPort,
264
390
  private readonly now: () => string = () => new Date().toISOString(),
265
- ) {}
391
+ /**
392
+ * The version-retention spine. Defaults to one over the SAME `Queryable`
393
+ * seam + clock, so retention writes share this service's transaction shape;
394
+ * injectable for tests.
395
+ */
396
+ versionStore: AppVersionStore = new AppVersionStore(db, now),
397
+ ) {
398
+ this.versionStore = versionStore;
399
+ }
266
400
 
267
401
  /**
268
402
  * Install an app for one (tenant, env). Validates scopes BEFORE writing
@@ -291,6 +425,16 @@ export class AppRegistryService {
291
425
  );
292
426
  }
293
427
 
428
+ // The active version is whatever we retain below; `app_installs.app_version`
429
+ // is a mirror of it. Resolve the facet set FIRST so the row's `app_version`
430
+ // is the same version handle the retained v1 snapshot carries.
431
+ const facets = resolveVersionFacets({
432
+ appVersion: input.appVersion,
433
+ scopes: validation.scopes,
434
+ manifest: input.manifest,
435
+ override: input.versionFacets,
436
+ });
437
+
294
438
  const installId = crypto.randomUUID();
295
439
  const now = this.now();
296
440
  await this.db.run(
@@ -300,7 +444,7 @@ export class AppRegistryService {
300
444
  VALUES (?,?,?,?,?,'active',?,?,?,?,?,?,?)`,
301
445
  installId,
302
446
  input.appId,
303
- input.appVersion,
447
+ facets.appVersion,
304
448
  ctx.tenantId,
305
449
  input.env,
306
450
  JSON.stringify(validation.scopes),
@@ -323,12 +467,30 @@ export class AppRegistryService {
323
467
  await this.db.run(`DELETE FROM app_installs WHERE install_id = ?`, installId);
324
468
  throw cause;
325
469
  }
470
+
471
+ // Retain v1's complete facet set. Fail-closed: a facet set naming secret
472
+ // material (or any retention failure) unwinds the whole install — clear the
473
+ // just-issued credential and delete the row — so a refused install leaves no
474
+ // trace, exactly like the credential-failure path above.
475
+ try {
476
+ await this.versionStore.retainVersion({
477
+ installId,
478
+ tenantId: ctx.tenantId,
479
+ facets,
480
+ actor: ctx.actor,
481
+ });
482
+ } catch (cause) {
483
+ await this.credentials.clear(installId).catch(() => {});
484
+ await this.db.run(`DELETE FROM app_installs WHERE install_id = ?`, installId);
485
+ throw this.asRegistryError(cause);
486
+ }
487
+
326
488
  await this.appendAudit({
327
489
  installId,
328
490
  tenantId: ctx.tenantId,
329
491
  event: "installed",
330
492
  actor: ctx.actor,
331
- detail: { appId: input.appId, appVersion: input.appVersion, env: input.env },
493
+ detail: { appId: input.appId, appVersion: facets.appVersion, env: input.env },
332
494
  });
333
495
 
334
496
  return {
@@ -340,7 +502,7 @@ export class AppRegistryService {
340
502
  /** Read one install by id, or `null` if it doesn't exist. */
341
503
  async get(installId: string): Promise<AppInstall | null> {
342
504
  const row = await this.db.first<AppInstallRow>(
343
- `SELECT * FROM app_installs WHERE install_id = ?`,
505
+ `SELECT * FROM app_installs WHERE install_id = ? AND managed_kind IS NULL`,
344
506
  installId,
345
507
  );
346
508
  return row ? toDomain(row) : null;
@@ -349,7 +511,7 @@ export class AppRegistryService {
349
511
  /** All installs for a tenant, newest first. */
350
512
  async listByTenant(tenantId: string): Promise<AppInstall[]> {
351
513
  const rows = await this.db.all<AppInstallRow>(
352
- `SELECT * FROM app_installs WHERE tenant_id = ? ORDER BY created_at DESC`,
514
+ `SELECT * FROM app_installs WHERE tenant_id = ? AND managed_kind IS NULL ORDER BY created_at DESC`,
353
515
  tenantId,
354
516
  );
355
517
  return rows.map(toDomain);
@@ -396,12 +558,34 @@ export class AppRegistryService {
396
558
  const widgetPlacementsJson =
397
559
  widgetPlacements !== undefined ? JSON.stringify(widgetPlacements) : row.widget_placements;
398
560
 
561
+ // Retain this update as a new activation BEFORE the metadata write — after
562
+ // every fail-closed validation above, so a bad patch retains nothing. This
563
+ // captures the whole facet set at this version and, being the last write
564
+ // gate that can refuse (a secret-bearing facet set), keeps `update` all-or-
565
+ // nothing. `app_version` is NOT written here: it is the mirror, re-pointed
566
+ // below from the store's post-retain current version.
567
+ const facets = resolveVersionFacets({
568
+ appVersion,
569
+ scopes: effectiveScopes,
570
+ manifest: patch.manifest !== undefined ? patch.manifest : (JSON.parse(row.manifest) as unknown),
571
+ override: patch.versionFacets,
572
+ });
573
+ try {
574
+ await this.versionStore.retainVersion({
575
+ installId,
576
+ tenantId: row.tenant_id,
577
+ facets,
578
+ actor,
579
+ });
580
+ } catch (cause) {
581
+ throw this.asRegistryError(cause);
582
+ }
583
+
399
584
  await this.db.run(
400
585
  `UPDATE app_installs
401
- SET app_version = ?, scopes = ?, manifest = ?, webhook_endpoint = ?,
586
+ SET scopes = ?, manifest = ?, webhook_endpoint = ?,
402
587
  widget_placements = ?, updated_at = ?
403
588
  WHERE install_id = ?`,
404
- appVersion,
405
589
  scopesJson,
406
590
  manifestJson,
407
591
  webhookEndpoint,
@@ -409,6 +593,7 @@ export class AppRegistryService {
409
593
  now,
410
594
  installId,
411
595
  );
596
+ await this.syncActiveVersionMirror(installId);
412
597
 
413
598
  await this.appendAudit({
414
599
  installId,
@@ -421,6 +606,37 @@ export class AppRegistryService {
421
606
  return this.getOrThrow(installId);
422
607
  }
423
608
 
609
+ /**
610
+ * Roll an install back to a retained version, restoring its COMPLETE facet set
611
+ * atomically through {@link AppVersionStore.rollbackTo} (forward-only: a new
612
+ * activation copying the target's facets verbatim), then re-point the
613
+ * `app_installs.app_version` mirror to the restored version so the registry's
614
+ * active version agrees with the store's current snapshot. Rejects an
615
+ * uninstalled install (a dead install can't be mutated) and an unretained
616
+ * `toSeq` (`unknown_version`), leaving the store untouched in both cases.
617
+ */
618
+ async rollback(installId: string, toSeq: number, actor: string): Promise<AppInstall> {
619
+ const row = await this.mustFindRow(installId);
620
+ if (row.status === "uninstalled") {
621
+ throw new RegistryError(`Cannot roll back an uninstalled app.`, "invalid_state");
622
+ }
623
+ let restored: AppVersionSnapshot;
624
+ try {
625
+ restored = await this.versionStore.rollbackTo({ installId, toSeq, actor });
626
+ } catch (cause) {
627
+ throw this.asRegistryError(cause);
628
+ }
629
+ await this.syncActiveVersionMirror(installId);
630
+ await this.appendAudit({
631
+ installId,
632
+ tenantId: row.tenant_id,
633
+ event: "rolled_back",
634
+ actor,
635
+ detail: { toSeq, restoredVersion: restored.appVersion, activationSeq: restored.seq },
636
+ });
637
+ return this.getOrThrow(installId);
638
+ }
639
+
424
640
  /** Suspend an active install: disables API/webhook/widget access. */
425
641
  async suspend(installId: string, actor: string): Promise<AppInstall> {
426
642
  const row = await this.mustFindRow(installId);
@@ -519,6 +735,40 @@ export class AppRegistryService {
519
735
  return validation.placements;
520
736
  }
521
737
 
738
+ /**
739
+ * Re-point `app_installs.app_version` (the derived mirror) to the version
740
+ * store's current (`MAX(seq)`) snapshot — the single reconciliation step that
741
+ * keeps the mirror and the source-of-truth authority from diverging. Called
742
+ * after every store write that can change the active version (`update`,
743
+ * `rollback`); `install` sets the mirror from the same resolved facet set at
744
+ * INSERT time, so it is already consistent there.
745
+ */
746
+ private async syncActiveVersionMirror(installId: string): Promise<void> {
747
+ const current = await this.versionStore.currentVersion(installId);
748
+ if (!current) return;
749
+ await this.db.run(
750
+ `UPDATE app_installs SET app_version = ?, updated_at = ? WHERE install_id = ?`,
751
+ current.appVersion,
752
+ this.now(),
753
+ installId,
754
+ );
755
+ }
756
+
757
+ /**
758
+ * Translate a version-store {@link AppVersionError} into this service's
759
+ * `RegistryError` vocabulary so callers branch on one `.code` space: an
760
+ * unretained rollback target → `unknown_version`, a secret-bearing/invalid
761
+ * facet set → `invalid_version_facets`. Anything else rethrows unchanged.
762
+ */
763
+ private asRegistryError(cause: unknown): unknown {
764
+ if (cause instanceof AppVersionError) {
765
+ const code: RegistryErrorCode =
766
+ cause.code === "unknown_target" ? "unknown_version" : "invalid_version_facets";
767
+ return new RegistryError(cause.message, code);
768
+ }
769
+ return cause;
770
+ }
771
+
522
772
  private async setStatus(installId: string, status: AppInstallStatus): Promise<void> {
523
773
  await this.db.run(
524
774
  `UPDATE app_installs SET status = ?, updated_at = ? WHERE install_id = ?`,
@@ -544,7 +794,11 @@ export class AppRegistryService {
544
794
  /** Fetch a row or throw the generic fail-closed `not_found` — no enumeration detail. */
545
795
  private async mustFindRow(installId: string): Promise<AppInstallRow> {
546
796
  const row = await this.db.first<AppInstallRow>(
547
- `SELECT * FROM app_installs WHERE install_id = ?`,
797
+ // Managed-analytics installs have a reviewed-first lifecycle and are owned
798
+ // exclusively by ManagedAnalyticsApiV1Facade. Hiding them here fences the
799
+ // legacy registry's update/status/rollback/uninstall methods from racing
800
+ // the managed CAS or appending an activation behind its revision counter.
801
+ `SELECT * FROM app_installs WHERE install_id = ? AND managed_kind IS NULL`,
548
802
  installId,
549
803
  );
550
804
  if (!row) throw new RegistryError(`App install not found.`, "not_found");
@@ -1,6 +1,6 @@
1
1
  /**
2
- * Scope validation — the compliance-floor gate for Storefront Private Apps
3
- * (PrivateApps epic, D2). See `docs/private-apps/feature-brief.md` §"The
2
+ * Scope validation — the compliance-floor gate for Storefront Private Apps.
3
+ * See `docs/private-apps/feature-brief.md` §"The
4
4
  * compliance floor" and the machine-readable contract:
5
5
  * - `docs/private-apps/contract/scopes.json` — the 8-scope V1 catalog.
6
6
  * - `docs/private-apps/contract/forbidden-scopes.json` — the 8 ids that map
@@ -0,0 +1,170 @@
1
+ /**
2
+ * Control-plane materialization of a tenant's OWN (`tenant-local`) integration
3
+ * declarations for the admin seams (ext-kernel-09).
4
+ *
5
+ * The middleware materializes these same declarations on the render hot path; this
6
+ * module materializes them for the ADMIN plane — the ext-admin-02 read model and the
7
+ * ext-admin-07 lifecycle route — so a declared integration shows its REAL effective
8
+ * state (its resolved plan, compiled CSP, consent needs) instead of a null /
9
+ * `not_materialized` placeholder. Resolution runs through the SAME interim marketing
10
+ * bridge the render path uses, so a regulated tenant is refused identically (no
11
+ * effective plan → the declaration surfaces honestly as unmaterialized) and an
12
+ * eligible unregulated one resolves the exact plan it serves.
13
+ *
14
+ * A tenant-local declaration is a live-by-config integration (on-by-default), not a
15
+ * candidate under review: its effective state is what the read model shows and what
16
+ * the lifecycle route loads. It has no separate promotion gate — publishing an
17
+ * on-by-default declaration would change nothing — so the lifecycle route surfaces
18
+ * its live state and refuses a preview/publish with no candidate rather than
19
+ * fabricating one.
20
+ */
21
+ import { loadTenantIntegrationDeclarations } from "@/lib/integration/tenantDeclarations";
22
+ import {
23
+ resolveInterimBridgePlan,
24
+ compileIntegrationCspFromPlan,
25
+ collectDeclaredCspProfiles,
26
+ buildCandidateEvidence,
27
+ defaultConsentSnapshot,
28
+ ENVIRONMENT_SCHEMA_VERSION,
29
+ type TenantConfig,
30
+ type BrowserEnvironment,
31
+ type IntegrationEnvironment,
32
+ type IntegrationSiteSurface,
33
+ type EffectivePlan,
34
+ type CompiledIntegrationCsp,
35
+ type ConsentSnapshot,
36
+ type AssetVerificationResult,
37
+ } from "@tot/public-runtime";
38
+ import type { IntegrationReadModelInputItem } from "@/lib/apps/integrationReadModel.js";
39
+ import {
40
+ facetsFromEvidence,
41
+ initialLifecycleState,
42
+ type TenantIntegrationLifecycleState,
43
+ } from "@/lib/apps/integrationLifecycle.js";
44
+
45
+ /** Stable, synthetic install id for a tenant-local declaration (no registry install backs it). */
46
+ export function tenantLocalInstallId(appId: string): string {
47
+ return `tenant-local:${appId}`;
48
+ }
49
+
50
+ /** A same-origin (public/-committed) entry declares no R2 app assets — a clean zero verify. */
51
+ const NO_R2_APP_ASSETS: AssetVerificationResult = { expected: 0, verified: 0, missing: [] };
52
+
53
+ /** The admin-plane browser environment for a lifecycle stage — the `admin` surface. */
54
+ function adminEnvironment(tenant: TenantConfig, stage: IntegrationEnvironment): BrowserEnvironment {
55
+ const surface: IntegrationSiteSurface = "admin";
56
+ return {
57
+ schemaVersion: ENVIRONMENT_SCHEMA_VERSION,
58
+ siteSurface: surface,
59
+ environment: stage,
60
+ canonicalHost: tenant.canonicalDomain || tenant.tenant_id,
61
+ revision: `admin-${stage}`,
62
+ };
63
+ }
64
+
65
+ /** The resolved plan + compiled CSP + consent snapshot for a tenant's tenant-local declarations. */
66
+ export interface MaterializedTenantLocalPlan {
67
+ plan: EffectivePlan;
68
+ compiledCsp: CompiledIntegrationCsp;
69
+ consentSnapshot: ConsentSnapshot;
70
+ }
71
+
72
+ /**
73
+ * Resolve a tenant's tenant-local declarations into the effective plan + compiled CSP
74
+ * for `stage`, through the interim bridge. Null when the tenant declares none, the
75
+ * bridge refuses (regulated), or the plan/CSP fail closed — every "no effective state"
76
+ * case an honest null, never a partial plan.
77
+ */
78
+ export function materializeTenantLocalPlan(
79
+ tenant: TenantConfig,
80
+ stage: IntegrationEnvironment,
81
+ ): MaterializedTenantLocalPlan | null {
82
+ const declarations = loadTenantIntegrationDeclarations(tenant);
83
+ if (declarations.length === 0) return null;
84
+ const bridge = resolveInterimBridgePlan({
85
+ tenantPublicId: tenant.tenant_id,
86
+ tenant,
87
+ environment: adminEnvironment(tenant, stage),
88
+ declarations,
89
+ });
90
+ if (!bridge.ok) return null;
91
+ const compiled = compileIntegrationCspFromPlan(
92
+ bridge.plan,
93
+ collectDeclaredCspProfiles(declarations),
94
+ );
95
+ if (!compiled.ok) return null;
96
+ return { plan: bridge.plan, compiledCsp: compiled.value, consentSnapshot: defaultConsentSnapshot() };
97
+ }
98
+
99
+ /**
100
+ * Build the ext-admin-02 read-model input items for a tenant's tenant-local
101
+ * declarations. Each carries the resolved effective plan + compiled CSP so the
102
+ * assembler surfaces REAL effective state; a refused/failed resolution yields a null
103
+ * effective plan, so the declaration surfaces as honestly unmaterialized (never a
104
+ * fabricated live state).
105
+ */
106
+ export function tenantLocalReadModelItems(
107
+ tenant: TenantConfig,
108
+ stage: IntegrationEnvironment,
109
+ ): IntegrationReadModelInputItem[] {
110
+ const declarations = loadTenantIntegrationDeclarations(tenant);
111
+ if (declarations.length === 0) return [];
112
+ const materialized = materializeTenantLocalPlan(tenant, stage);
113
+ return declarations.map((decl) => {
114
+ const { app } = decl.manifest;
115
+ return {
116
+ install: {
117
+ installId: tenantLocalInstallId(app.id),
118
+ appId: app.id,
119
+ appVersion: app.version,
120
+ tenantId: tenant.tenant_id,
121
+ status: "active",
122
+ },
123
+ purpose: app.name,
124
+ desiredConfig: decl.publicConfig,
125
+ effectivePlan: materialized?.plan ?? null,
126
+ effectiveCsp: materialized?.compiledCsp ?? null,
127
+ consentSnapshot: materialized?.consentSnapshot ?? null,
128
+ };
129
+ });
130
+ }
131
+
132
+ /**
133
+ * Load the real lifecycle state for a tenant-local declared install — a live-by-config
134
+ * integration whose effective plan occupies the resolved stage slot. Null when the
135
+ * install id names no materialized tenant-local declaration for the tenant (so the
136
+ * lifecycle route answers a fail-closed 409, never a fabricated state).
137
+ */
138
+ export function loadTenantLocalLifecycleState(
139
+ tenant: TenantConfig,
140
+ installId: string,
141
+ stage: IntegrationEnvironment,
142
+ ): TenantIntegrationLifecycleState | null {
143
+ const declarations = loadTenantIntegrationDeclarations(tenant);
144
+ const decl = declarations.find((d) => tenantLocalInstallId(d.manifest.app.id) === installId);
145
+ if (!decl) return null;
146
+ const materialized = materializeTenantLocalPlan(tenant, stage);
147
+ if (!materialized) return null;
148
+
149
+ const appId = decl.manifest.app.id;
150
+ const evidence = buildCandidateEvidence({
151
+ plan: materialized.plan,
152
+ compiledCsp: materialized.compiledCsp,
153
+ assets: NO_R2_APP_ASSETS,
154
+ });
155
+ if (!evidence.ok) return null;
156
+ const facets = facetsFromEvidence(evidence.value, appId);
157
+
158
+ const state = initialLifecycleState({
159
+ tenantPublicId: tenant.tenant_id,
160
+ installId,
161
+ appId,
162
+ });
163
+ // A tenant-local declaration is live-by-config: its resolved plan IS the serving
164
+ // (live) facet, and it is its own known-good rollback target.
165
+ return {
166
+ ...state,
167
+ stages: { ...state.stages, live: facets },
168
+ knownGood: [facets],
169
+ };
170
+ }
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * OAuth2 client-credentials token issuer — the Storefront App Gateway's
3
- * `POST /oauth/token` grant (PrivateApps epic, D2 Chunk B). Mints the
4
- * short-lived RS256 JWT a private app presents as a bearer token to the D3
3
+ * `POST /oauth/token` grant. Mints the
4
+ * short-lived RS256 JWT a private app presents as a bearer token to the
5
5
  * scoped APIs.
6
6
  *
7
7
  * `tenant` and `env` are supplied by the CALLER (the route, from
@@ -97,8 +97,8 @@ export class AppTokenIssuer {
97
97
  tenant: install.tenantId,
98
98
  env: install.env,
99
99
  scopes: install.scopes,
100
- // Discriminates this bearer token from a widget launch token (D5 Chunk
101
- // B) so the two can never be replayed for each other even though both
100
+ // Discriminates this bearer token from a widget launch token so the two
101
+ // can never be replayed for each other even though both
102
102
  // are RS256 JWTs signed by the same gateway key — see tokenVerifier.ts.
103
103
  token_use: "access",
104
104
  })
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Offline verification of the Storefront App Gateway's own OAuth2
3
- * client-credentials JWT (PrivateApps epic, D2 Chunk B) — this is what D3
4
- * imports at the scoped-API boundary to authorize an app's request.
3
+ * client-credentials JWT — this is what the scoped-API boundary
4
+ * imports to authorize an app's request.
5
5
  *
6
6
  * Modeled directly on `../auth/identityToken.ts`'s `IdentityAssertionVerifier`
7
7
  * (same `jwtVerify` + JWKS-by-`kid` shape, `aud`/`iss` pinning, alg allowlist),
@@ -25,7 +25,7 @@ import {
25
25
  } from "./gatewayKeys.js";
26
26
  import { readEnv } from "@/lib/env";
27
27
 
28
- /** The verified app-token claims (D3's authorization input). */
28
+ /** The verified app-token claims (the scoped API's authorization input). */
29
29
  export interface AppTokenClaims {
30
30
  iss: string;
31
31
  sub: string; // install id
@@ -40,8 +40,8 @@ export interface AppTokenClaims {
40
40
 
41
41
  /** Normalize a jose JWT payload into typed {@link AppTokenClaims}, throwing on any missing claim. */
42
42
  function toAppTokenClaims(payload: Record<string, unknown>): AppTokenClaims {
43
- // The security-critical discriminator: a widget launch token (D5 Chunk B,
44
- // `token_use: "widget_launch"`) is signed by this SAME gateway key, so
43
+ // The security-critical discriminator: a widget launch token
44
+ // (`token_use: "widget_launch"`) is signed by this SAME gateway key, so
45
45
  // signature/aud/iss/exp checks alone can't tell it apart from a bearer
46
46
  // token. Requiring `token_use === "access"` here is what stops a launch
47
47
  // token — minted for a sandboxed iframe, short-TTL, no scopes checked —
@@ -128,7 +128,7 @@ export class AppTokenVerifier {
128
128
  /**
129
129
  * Get (or build) the verifier for one tenant, from the gateway's own
130
130
  * locally-derived JWKS. Returns `null` when the signing key isn't configured
131
- * — callers (D3 routes) fail closed with 503, mirroring `getIdentityVerifier`.
131
+ * — callers (the scoped-API routes) fail closed with 503, mirroring `getIdentityVerifier`.
132
132
  * Not cached across calls (unlike `identityToken.ts`'s remote-fetch verifier):
133
133
  * deriving the local JWKS is cheap (`gatewayKeys.ts` caches the export itself).
134
134
  */